diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 54c224a6..4bb6a5f0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,6 +9,35 @@ User-visible changes go into **both** changelogs in the same commit: `docs/CHANGELOG.md` (English) and `docs/CHANGELOG.ru.md` (Russian). Entries older than v1.42.0 exist only in the English file — no need to backfill them. +## Translations + +A shipped UI language has three matching parts: + +1. `src/i18n/.json` for the card; +2. `custom_components/houseplan/translations/.json` for the Home Assistant + integration; +3. one static entry (dictionary import, code and native label) in + `src/i18n/registry.ts`. + +Use the canonical Home Assistant/BCP 47 language tag as `` (for example, +`fr` or `pt-BR`) and use that exact spelling for both JSON filenames. Lookup is +case-insensitive and also accepts `_` from legacy locale sources. + +The registry drives language resolution, the visual-editor selector and parity +tests. The tests reject missing or extra locale files; frontend dictionaries +also fail on mismatched keys, empty values and changed placeholders. +Placeholders such as `{name}` and `{n}` are a contract: do not translate, add +or remove them. + +The current `subst()` helper does not implement plural rules. Phrase strings so +their grammar does not depend on the numeric value (for example, use a neutral +label followed by `{n}` rather than an English singular/plural pair). + +Adding a UI locale does not automatically create another full documentation +set; maintain the existing English and Russian documentation according to the +project's normal rules. Right-to-left layout is a separate product project, +because the plan canvas and editors cannot be mirrored by translations alone. + ## Where to ask Not sure whether something is a bug, or just want to discuss an idea before @@ -45,8 +74,8 @@ every push — locally they are skipped when `homeassistant` is not importable. - **Docs in the same commit**: CHANGELOG entry for user-visible changes; `docs/STATUS.md` for state changes; `docs/DEVELOPMENT.md` for new gotchas. -- Every UI string goes through `src/i18n/.json` (tests enforce en/ru key parity). - Adding a language = adding one JSON file + registering it in `src/i18n.ts`. +- Every UI string goes through `src/i18n/.json`; follow the + [Translations](#translations) flow for registry and backend parity. - The built card must be committed in sync: `cp dist/houseplan-card.js custom_components/houseplan/frontend/` (CI compares them byte-for-byte). - Tap actions have a security model (locks/alarms never toggle from the plan) — diff --git a/custom_components/houseplan/frontend/houseplan-card.js b/custom_components/houseplan/frontend/houseplan-card.js index 8b02af02..ebf4642f 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__="30d20d6f6822065e2d5226b18abbb05d8d26bc9ddfff42a327dc483b2446800c";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}},b=(e,t)=>!r(e,t),y={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:b};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=y){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)??y}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??b)(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,N=/-->/g,E=/>/g,O=RegExp(`>|${z}(?:([^\\s"'>=/]+)(${z}*=${z}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,B=/^(?:script|style|textarea|title)$/i,j=e=>(t,...i)=>({_$litType$:e,strings:t,values:i}),q=j(1),W=j(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===N||a===E?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,be=()=>document.createComment(""),ye=(e,t,i)=>{const s=e._$AA.parentNode,n=void 0===t?e._$AB:t._$AA;if(void 0===i){const t=s.insertBefore(be(),n),o=s.insertBefore(be(),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[_]),ye(e,l[_+1],n[d]),d++,_--;else if(r[p]===a[u])l[u]=we(n[p],o[u]),ye(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=ye(e,n[d]);we(t,o[u]),l[u]=t}else l[u]=we(i,o[u]),ye(e,n[d],i),n[t]=null;u++}else $e(n[p]),p--;else $e(n[d]),d++;for(;u<=_;){const t=ye(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=q` +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="0241463fe185294ee49595feb903f78fd0c09aeee71e050d3d9f7f51a0b99e60";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}},b=(e,t)=>!r(e,t),y={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:b};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=y){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)??y}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??b)(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,N=/-->/g,E=/>/g,O=RegExp(`>|${z}(?:([^\\s"'>=/]+)(${z}*=${z}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,B=/^(?:script|style|textarea|title)$/i,j=e=>(t,...i)=>({_$litType$:e,strings:t,values:i}),q=j(1),W=j(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===N||a===E?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,be=()=>document.createComment(""),ye=(e,t,i)=>{const s=e._$AA.parentNode,n=void 0===t?e._$AB:t._$AA;if(void 0===i){const t=s.insertBefore(be(),n),o=s.insertBefore(be(),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[_]),ye(e,l[_+1],n[d]),d++,_--;else if(r[p]===a[u])l[u]=we(n[p],o[u]),ye(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=ye(e,n[d]);we(t,o[u]),l[u]=t}else l[u]=we(i,o[u]),ye(e,n[d],i),n[t]=null;u++}else $e(n[p]),p--;else $e(n[d]),d++;for(;u<=_;){const t=ye(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=q` ${this.icon?q``:G} ${this.title} `;return this._useHaDialog?q`${e.lqiText}`:G} - `}function Yo(e,t,i){return{kind:"none",reason:"none",generation:e,expiresAt:null,color:t,diameterScale:i,animated:!1,reducedMotionIndicator:"none"}}const Jo=new WeakMap;function Xo(e){const t=function(e){const t=e?.connection||e;return!t||"object"!=typeof t&&"function"!=typeof t?null:t}(e);if(!t)return null;let i=Jo.get(t);return i||(i={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},Jo.set(t,i)),i}function Zo(e){e.revision++;for(const t of[...e.listeners])try{t()}catch{}}function Qo(e,t){const i=Xo(e);if(!i)return()=>{};i.refs++,i.listeners.add(t),async function(e,t){t.loading||"function"!=typeof e?.callWS?t.loading:t.loaded&&t.loadedAt&&Date.now()-t.loadedAt<3e5||(t.loading=(async()=>{const[i,s]=await Promise.allSettled([e.callWS({type:"config_entries/get"}),e.callWS({type:"manifest/list"})]);"fulfilled"===i.status&&Array.isArray(i.value)&&(t.configEntries=Object.fromEntries(i.value.filter(e=>"string"==typeof e?.entry_id).map(e=>[e.entry_id,e]))),"fulfilled"===s.status&&Array.isArray(s.value)&&(t.manifests=Object.fromEntries(s.value.filter(e=>"string"==typeof e?.domain).map(e=>[e.domain,e]))),t.loaded=!0,t.loadedAt=Date.now(),t.loading=void 0,Zo(t)})().catch(()=>{t.loaded=!0,t.loadedAt=Date.now(),t.loading=void 0,Zo(t)}),t.loading)}(e,i),async function(e,t){if(t.unsubscribe||t.subscribing)return t.subscribing;const i=e?.connection?.subscribeMessage;"function"==typeof i&&(t.subscribing=(async()=>{try{t.unsubscribe=await i.call(e.connection,e=>{const i=Array.isArray(e)?e:Array.isArray(e?.entries)?e.entries:e?[e]:[];let s=!1;const n={...t.configEntries};for(const e of i){const t=e?.entry||e?.data?.entry||("string"==typeof e?.entry_id?e:null),i=t?.entry_id;if("string"!=typeof i)continue;const o=e?.type||e?.action||e?.data?.action;"removed"===o||"remove"===o?delete n[i]:n[i]=t,s=!0}s&&(t.configEntries=n,Zo(t))},{type:"config_entries/subscribe"})}catch{}finally{0===t.refs&&(t.unsubscribe?.(),t.unsubscribe=void 0),t.subscribing=void 0}})(),t.subscribing)}(e,i);let s=!1;return()=>{s||(s=!0,i.listeners.delete(t),i.refs=Math.max(0,i.refs-1),i.refs||(i.unsubscribe?.(),i.unsubscribe=void 0))}}function ea(e){const t=Xo(e);return t?{revision:t.revision,loaded:t.loaded,configEntries:t.configEntries,manifests:t.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function ta(e,t,i=ea(e),s){const n=i.manifests[t];return function(e,t){if("function"!=typeof e?.localize)return"";try{const i=`component.${t}.title`,s=e.localize(i);return"string"==typeof s&&s!==i?s:""}catch{return""}}(e,t)||String(n?.name||"")||String(s||"")||t.replace(/_/g," ")}function ia(e,t,i,s,n){const o=String(t||"").trim();if(!o)return null;const a=n?s.configEntries[n]:null;return{domain:o,label:ta(e,o,s,a?.title),configEntryId:n,configEntryTitle:a?.title,confidence:i}}function sa(e){const t={"registry-owner":0,"entity-platform":1,"identifier-fallback":2},i=new Set;return e.filter(e=>!!e).sort((e,i)=>t[e.confidence]-t[i.confidence]||e.label.localeCompare(i.label)||e.domain.localeCompare(i.domain)).filter(e=>{const t=`${e.domain}\n${e.configEntryId||""}`;return!i.has(t)&&(i.add(t),!0)})}function na(e,t,i,s=ea(e)){const n=i.entities?.[t]||e?.entities?.[t];if(!n)return null;const o=n.config_entry_id||void 0,a=o?s.configEntries[o]?.domain:"";return ia(e,n.platform||a,"entity-platform",s,o)}var oa={"editor.context_actions":"Actions: {object}","editor.tool_options":"Tool options: {tool}","editor.palette":"Palette: {tool}","editor.open_group":"Tool group: {group}","editor.group_active":"{group} — active: {item}","editor.disabled_action":"{action} is unavailable: {reason}","color_picker.title":"Color picker","color_picker.hue":"Hue","color_picker.saturation":"Saturation","color_picker.value":"Brightness","color_picker.hex":"Hex color","color_picker.invalid_hex":"Enter a 3- or 6-digit hex color","btn.properties":"Properties","btn.keep_as_walls":"Keep as walls","btn.delete_room_keep_walls":"Delete room, keep walls","btn.delete_room_with_walls":"Delete room and walls","title.markup_select":"Select and edit walls, columns and saved outlines","title.markup_column":"Column: click a grid point to place a square column","markup.partition":"Partition","markup.column":"Column","markup.hint_column":"click a grid point to place a square column","history.draft_segment":"Add room-draft segment","history.draft_merge":"Join unfinished room outlines","history.draft_segment_delete":"Remove room-draft segment","history.partition_add":"Add partition","history.column_add":"Add column","history.physical_edit":"Change physical object","history.physical_delete":"Delete physical object","history.physical_move":"Move physical object","history.contour_to_partitions":"Convert contour to closed walls","history.wall_chain_finish":"Finish wall chain","history.wall_face_batch":"Create rooms from walls","toast.column_duplicate":"A column with the same centre and outer size already exists","confirm.delete_draft":"Delete the whole unfinished room contour?","confirm.delete_draft_segment":"Delete this draft segment? The remaining contour may split in two.","physical.partition_properties":"Partition properties","physical.column_properties":"Column properties","physical.draft_properties":"Draft segment properties","physical.shape":"Shape","physical.square":"Square","physical.circle":"Circle","physical.diameter":"Diameter","physical.side":"Side","physical.rotation":"Rotation angle","physical.length":"Length","physical.allowed_range":"Allowed: {min}–{max} {unit}","physical.delete_segment":"Delete segment","physical.delete_draft":"Delete entire outline","physical.column_size_title":"Outer side of the square column placed by the click (1–150 cm).","card.title":"House plan","count.devices":"{n} dev.","empty.no_spaces":"No spaces yet.","empty.add_first":"Add the first space and upload a floor plan.","empty.install":'Install the House Plan integration and add it in "Devices & services".',"btn.add_space":"Add space","btn.cancel":"Cancel","btn.save":"Save","btn.close":"Close","btn.delete":"Delete","btn.edit":"Edit","btn.open_in_ha":"Open in HA","btn.reset":"Reset","btn.attach":"Attach…","btn.upload":"Upload…","btn.replace":"Replace…","title.zoom_in":"Zoom in","title.zoom_out":"Zoom out","title.zoom_fit":"Fit all","title.add_device":"Add a device to the plan","title.show_all":"Show hidden and disabled devices as service markers (this tab only)","title.markup":"Room markup: grid, lines, outlines","title.configure_space":"Configure space","title.add_space":"Add space","title.markup_add":"Walls: draw a continuous chain; Shift locks to 45°. Changing tool or leaving the editor finishes it as independent walls","title.markup_merge":"Merge: click one room, then the neighbour it shares a wall with","title.markup_split":"Split a room: click the room, then two points on its walls","title.markup_delroom":"Delete room: click inside a room and confirm","title.need_plan":"Upload a floor-plan image","markup.add":"Walls","markup.merge":"Merge","markup.split":"Split","markup.resize":"Resize","title.markup_resize":"Resize rooms: drag an available horizontal or vertical wall handle","markup.hint_resize":"drag an available wall handle · the wall stops at the first unsafe position · Esc cancels · Ctrl+Z — undo","resize.disabled.diagonal":"This wall is angled; Resize supports only horizontal and vertical walls","resize.disabled.side-angle":"An adjoining wall is angled; Resize requires both adjoining walls to meet this wall at right angles","resize.disabled.duplicate-physical-wall":"A separate partition, unfinished outline, or column overlaps this wall; remove or move it before resizing","resize.disabled.partial-shared":"A neighbouring room uses only part of this wall, so it cannot be moved safely as one shared wall","resize.disabled.unequal-shared":"The neighbouring room uses different endpoints or wall length, so the walls cannot move together safely","resize.disabled.multiple-rooms":"Moving this wall would affect more than two rooms","resize.disabled.thickness-conflict":"The wall thickness cannot be preserved safely","resize.disabled.opening-conflict":"An opening on this wall cannot be moved safely","resize.disabled.invalid-geometry":"This wall cannot be resized without changing the plan structure","resize.limit_stopped":"Wall stopped: the next step would break a junction limit","resize.commit_failed":"Resize was cancelled because the final plan did not pass the geometry check","resize.preview_failed":"Resize stopped at the last safe position because the plan geometry could not be preserved","markup.opening":"Opening","markup.delete_room":"Delete room","history.undo":"Undo","history.redo":"Redo","history.undo_named":"Undo: {name} (Ctrl+Z)","history.redo_named":"Redo: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Nothing to undo","history.redo_empty":"Nothing to redo","history.undone":"Undone: {name}","history.redone":"Redone: {name}","history.add_room":"Create room","history.split_room":"Split room","history.merge_rooms":"Merge rooms","history.resize_room":"Resize room","history.wall_thickness":"Change wall thickness","history.add_opening":"Add door or window opening","history.edit_opening":"Edit opening","history.move_opening":"Move opening","history.delete_opening":"Delete opening","history.delete_room":"Delete room","history.delete_room_keep_walls":"Delete room, keep walls","history.delete_room_with_walls":"Delete room and walls","history.decor_add":"Add decor object","history.decor_edit":"Edit decor object","history.decor_move":"Move decor object","history.decor_transform":"Transform decor object","history.decor_delete":"Delete decor object","history.backdrop_transform":"Transform plan backdrop","title.markup_opening":"Openings: click a wall to place, click an opening to edit","opening.new":"New opening","opening.edit":"Opening","opening.door":"Door","opening.window":"Window","opening.gate":"Gate","opening.passage":"Open passage","opening.passage_binding_warning":"Saving will remove the open/close sensor and lock.","opening.invalid_passage_fields":"The open passage on plan “{room}” has unsupported settings: {fields}.","opening.type_label":"Type","opening.length_label":"Length, cm","opening.contact_label":"Open/close sensor","opening.lock_label":"Lock","opening.none":"— none —","opening.search_ph":"Search: name or entity id…","opening.invert":"Invert open/closed","opening.flip_h":"Hinge on the other jamb","opening.flip_v":"Opens to the other side","opening.open":"Open","opening.closed":"Closed","opening.locked":"Locked","opening.unlocked":"Unlocked","opening.state_unknown":"unavailable","opening.no_entities":"No sensors bound — a static symbol on the plan.","toast.opening_no_wall":"Click next to a room wall or independent wall","opening.host_partition":"Independent wall","opening.partition_orphan":"The independent wall for this opening no longer exists","opening.partition_jamb_margin":"Leave at least {distance} between the opening and the end of the wall","opening.rebind_partition":"Attach to another independent wall","confirm.delete_partition_openings_title":"Delete wall and openings?","confirm.delete_partition_openings_body":"This wall contains {count} opening(s). They will be deleted together.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Delete","markup.hint_points":"points: {n} · Shift — 45° steps · Esc — finish chain · Ctrl+Z — undo a point · closing an area offers a room","markup.hint_start":"click a grid dot to start a wall chain","tip.lqi":"average zigbee signal:","tip.area":"area: {value}","info.device_header":"Device on the plan","info.model":"Model","info.state":"State","info.link":"Link","info.manuals":"Manuals","info.none":"No additional information","marker.new_device":"New device","marker.name_label":"Name (shown on the plan)","marker.name_ph":"Name","marker.binding_label":"Bind to an HA device","marker.binding_disabled":"disabled in Home Assistant","marker.virtual_option":"Virtual device (no binding)","marker.search_ph":"Search device / group…","marker.nothing_found":"nothing found","marker.room_label":"Room","marker.room_override":" (override placement)","marker.room_choose":"— select a room —","marker.room_auto":"— by device area (auto) —","marker.icon_label":"Icon","marker.icon_ph":"mdi:… (empty = auto)","marker.display_label":"Display","display.badge":"Icon + state","display.icon_ripple":"Icon + state and activity","display.static_icon":"Always static icon","marker.display_hint":"Icon + state changes the plate for work, open, alarm and unavailable states. Icon + state and activity additionally shows a short pulse for events and a continuous pulse for ongoing work, movement or presence. Value + state replaces the icon with one unambiguous HA value. A static icon never reacts to device states.","marker.display_hint_badge":"The icon and dynamic plate show device state without the ordinary activity pulse. Red alarms remain visible.","marker.display_hint_icon":"The icon and dynamic plate show device state without the ordinary activity pulse. Red alarms remain visible.","marker.display_hint_icon_ripple":"The icon, dynamic plate and pulse show a short pulse for events and a continuous pulse for work, motion or presence. Red alarms have separate priority.","marker.display_hint_value":"One unambiguous Home Assistant value replaces the icon while the plate continues to show state. Red alarms remain visible.","marker.display_hint_static_icon":"The theme-aware shell and icon always stay the same. State, activity, unavailability and alarms do not change the face.","marker.static_alarm_warning":"This device can report alarms. Static display hides the marker's visual alarm indication.","marker.preview.title":"Display preview","marker.preview.actual":"Now","marker.preview.example":"Example","marker.preview.integration":"Provided by","marker.preview.source":"Display source","marker.preview.current_state":"Current state","marker.preview.result":"On the plan","marker.preview.details":"Source details","marker.preview.select_source":"Choose a Home Assistant device or entity to see its actual display.","marker.preview.unknown_provider":"Unknown integration","marker.preview.virtual_provider":"House Plan · virtual device","marker.preview.no_source":"No active source","marker.preview.no_state":"No current state","marker.preview.mixed_states":"Several different states","marker.preview.multiple_sources":"{n} sources","marker.preview.more_sources":"+{n} more","marker.preview.scaled":"The preview is scaled to {n}% to fit. The saved size is unchanged.","marker.preview.demo_activity":"Show activity example","marker.preview.demo_notice":"Temporary activity example; the actual device state is unchanged.","marker.preview.demo_short":"Show short activity","marker.preview.demo_continuous":"Show continuous activity","marker.preview.stop_continuous":"Stop continuous activity","marker.preview.demo_short_notice":"Short activity example; the actual device state is unchanged.","marker.preview.demo_continuous_notice":"Continuous activity example; the actual device state is unchanged.","marker.preview.demo_already_visible":"The current state already shows a real activity or alarm effect.","marker.preview.reduced_motion":"System reduced-motion is enabled, so ordinary activity is shown as a dot.","marker.preview.reason.neutral":"Neutral dark plate","marker.preview.reason.working":"Yellow plate: the device is working now","marker.preview.reason.working_activity":"Yellow plate and activity effect: the device is working now","marker.preview.reason.open":"Orange plate: physically open or unlocked","marker.preview.reason.cover_icon_state":"Neutral plate; the cover entity controls the displayed state and icon","marker.preview.reason.presence":"Activity effect while presence is detected","marker.preview.reason.event":"Short activity effect after a detected event","marker.preview.reason.transition":"Activity effect while the device is moving or changing state","marker.preview.reason.media_neutral":"Media devices stay on a neutral dark plate while available","marker.preview.reason.unavailable":"Subdued neutral plate: unavailable or off","marker.preview.reason.alarm":"Red alarm plate; alarm indication is always shown","marker.preview.reason.live_states_disabled":"Live-state styling is disabled for this card","marker.preview.reason.value_no_state":"No usable state value; the icon is shown instead","marker.preview.reason.value_ambiguous_sources":"Several possible value sources; the icon is shown instead","marker.preview.reason.value_non_scalar":"The source did not return a simple value; the icon is shown instead","marker.preview.reason.value_virtual":"A virtual device has no Home Assistant value; the icon is shown instead","marker.preview.reason.vacuum_live_plan_only":"Live vacuum position and trail are available only on the full plan","marker.preview.reason.hidden_design_preview":"The device is hidden on the plan; this design preview remains visible","marker.preview.reason.composite_power_source":"State comes from the device Power entity; auxiliary switches are ignored","marker.preview.reason.activity_display_disabled":"The selected display mode does not show ordinary activity effects","marker.preview.reason.ha_disabled":"The Home Assistant binding is disabled and will be hidden on the plan","marker.preview.reason.orphaned":"The saved Home Assistant binding can no longer be found","marker.preview.reason.static_icon":"Static mode: device state does not change the icon","marker.activity_color":"Activity pulse color","marker.ripple_size":"Activity pulse size","marker.activity_alarm_note":"Color and size affect ordinary activity only and do not change red alarms.","marker.pulse_a11y_alarm":"Alarm","marker.pulse_a11y_event":"Recent event","marker.pulse_a11y_presence":"Presence detected","marker.pulse_a11y_transition":"Changing state","marker.pulse_a11y_running":"Working","marker.state_a11y_neutral":"Default state","marker.state_a11y_working":"Working","marker.state_a11y_open":"Open","marker.state_a11y_locked":"Locked","marker.state_a11y_unlocked":"Unlocked","marker.state_a11y_alarm":"Alarm","marker.state_a11y_unavailable":"Unavailable","marker.lqi_a11y_low":"LQI {value}, low signal","marker.lqi_a11y_mid":"LQI {value}, medium signal","marker.lqi_a11y_high":"LQI {value}, high signal","marker.size_label":"Icon size / rotation","marker.angle_label":"Rotate","marker.model_label":"Model","marker.model_ph":"e.g. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Description","marker.desc_ph":"Notes, specs…","marker.manuals_label":"Manuals (PDF etc.)","marker.sub_device":"device","marker.sub_z2m_group":" · Z2M group","marker.sub_group":"group","marker.sub_helper":"helper","space.new":"New space","space.header":"Space","space.title_label":"Title","space.title_ph":"e.g. Garage","space.plan_label":"Floor plan (background)","space.no_plan":"no plan image","space.plan_alt":"plan","room.new":"New room","room.name_label":"Display name","room.name_ph":"e.g. Terrace","room.area_label":"Home Assistant area (unassigned)","room.no_area_option":"— no area —","room.default_name":"Room","device.unnamed":"unnamed","device.light_group":"light group","device.fallback":"device","device.virtual":"virtual device","confirm.delete_room":'Delete room "{name}"?',"confirm.delete_room_title":'Delete room "{name}"?',"confirm.delete_room_body":"Choose whether the room's exclusive physical walls should remain. Walls shared with another room are always kept.","confirm.merge_rooms":'Merge rooms "{a}" and "{b}"?',"confirm.remove_marker":'Delete "{name}" from the plan? The device will disappear completely and stop contributing to plan data. You can add it again later.',"confirm.erase_decor":"Erase the {kind} object? You can undo this action from the editor history.","confirm.delete_space":'Delete space "{title}" with all its rooms and markup?',"space.delete_blocked":"This space is still used by {n} device(s). Move them to another space or delete them first.","toast.pos_save_failed":"Failed to save position: {err}","toast.no_entity":"The device has no suitable entity","toast.ha_disabled_action":"A Home Assistant object that is disabled cannot be used on the plan.","toast.ha_disabled_show_device":"A device disabled in Home Assistant cannot be shown on the plan. Enable it in Home Assistant first.","toast.ha_disabled_show_entity":"An entity disabled in Home Assistant cannot be shown on the plan. Enable it in Home Assistant first.","toast.ha_disabled_add":"A disabled Home Assistant object cannot be added to the plan. Enable it in Home Assistant first.","toast.ha_binding_unverified":"The object status could not be verified through the Home Assistant registry. Display and actions are temporarily unavailable.","toast.markup_needs_server":"Markup is available after the config is moved to the server","toast.space_order_changed":"Order changed. If any card pins its floor by number, check those panels.","toast.conflict":"Config was changed in another window — data refreshed, repeat your last action","toast.cfg_save_failed":"Failed to save config: {err}","toast.room_overlap":"The outline overlaps room “{name}” — rooms must not overlap","toast.contour_min_edges":"Draw at least two edges before closing the outline","toast.contour_cannot_close":"The outline cannot close because it is degenerate or intersects itself","toast.merge_not_adjacent":"Only rooms that share a wall can be merged","toast.rooms_merged":"Rooms merged into “{name}”","toast.split_pick_wall":"Start the cut on the room’s wall","toast.split_bad_cut":"The cut must run wall to wall inside the room, without crossing walls or itself","merge.header":"Merge rooms","merge.hint":"The merged room keeps one name and one area. The other area is released — its devices leave the plan until another room claims it.","merge.keep":"Keep","merge.no_area":"no area","room.split_header":"New room from the split","toast.room_saved":"Room saved ({n}). Devices added: {added}. Outline the next one or exit markup.","toast.room_saved_no_area":"Room saved ({n}, no area). Outline the next one or exit markup.","toast.marker_needs_server":"Device editing is available after the config is moved to the server","toast.virtual_name_required":"Enter a name for the virtual device","toast.marker_saved":"Device saved","toast.marker_removed":"Device deleted from the plan","toast.integration_missing":"The House Plan integration is not installed — management unavailable","toast.plan_formats":"Supported formats: SVG, PNG, JPG, WebP","toast.plan_required":"Upload a floor plan — it is required","toast.space_added_onboard":"Space added. Outline the rooms: click grid dots and close the contour.","toast.space_added":"Space added","toast.space_saved":"Space saved","toast.space_deleted":"Space deleted","toast.delete_failed":"Delete failed: {err}","toast.error":"Error: {err}","toast.toggle_target_unavailable":"Target “{name}” is unavailable — no action was performed","toast.toggle_targets_unavailable":"Targets are unavailable: {names}. No action was performed","toast.file_failed":'File "{name}" was not uploaded: {err}',"toast.files_attached":"Files attached: {n}","err.unknown":"unknown error","err.code":"code {code}","err.too_large":"file larger than {mb} MB","err.bad_ext":"unsupported type (PDF/image expected)","err.unauthorized":"administrator rights required","editor.title":"Title","editor.default_floor":"Initial space","editor.default_floor_missing":"Initial space “{id}” no longer exists. Choose another space.","editor.floor":"Fixed space","editor.floor_none":"— not fixed —","editor.floor_index":"YAML index {index} (preserved)","fixed_floor.loading":"Loading the fixed space…","fixed_floor.invalid_title":"The fixed space is unavailable","fixed_floor.invalid_body":"Check floor in the card configuration. The configured value is: {value}","editor.icon_size":"Icon size, % of plan width","editor.show_temperature":"Show temperature","editor.live_states":"Live states (on/off, open…)","editor.show_signal":"Show zigbee signal (LQI)","editor.language":"Interface language","editor.lang_auto":"Auto (HA profile)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Icon rules: which MDI icon devices get by name","rules.title":"Icon rules","rules.hint":"Rules are checked top-down against “device name + model” (case-insensitive regex); the first match wins. When nothing matches, the entity device class decides, then the generic chip icon.","rules.pattern_ph":"regex, e.g. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Add rule","rules.reset":"Reset to defaults","rules.test_ph":"Try a device name…","rules.invalid":"invalid regex","rules.saved":"Icon rules saved","btn.up":"Up","btn.down":"Down","tap.info":"Device card","tap.more_info":"HA more-info dialog","tap.toggle":"Toggle state","marker.tap_label":"Tap action for this device","tap.toggle_note":"The resolved state is shown below. Secure devices are never toggled from the plan.","import.title":"Create spaces from HA floors","import.hint":"Your Home Assistant already knows these floors. Pick the ones to turn into plan spaces — you will upload a floor-plan image for each one next. Rooms are then outlined by hand on the plan.","import.start":"Create {n} space(s)","import.manual":"Start from scratch","import.progress":"Floor {i} of {n}","import.done":"Spaces created. Outline the rooms: click grid dots and close the contour.","btn.skip":"Skip","space.scale_label":"Scale (grid cell size)","space.scale_unit":"cm per cell","space.scale_unit_imperial":"in per cell","space.display_section":"Display","space.show_borders":"Always show room borders","space.zero_wall_style":"Zero-thickness walls","space.zero_wall_dashed":"Dashed","space.zero_wall_solid":"Solid","space.zero_wall_help":"Dashed walls let light through. Solid walls block light even at zero thickness.","space.show_names":"Show room names (drag to move)","space.room_color":"Border & name color","space.opacity":"Opacity","space.fill_label":"Room fill","fill.none":"None","fill.lqi":"Zigbee signal","fill.light":"Lights","fill.custom":"Custom color","space.custom_fill":"Fill color","space.source_file":"I have a floor-plan image","space.source_draw":"No image — I'll outline rooms by hand","space.orientation":"Canvas","orient.landscape":"Landscape","orient.portrait":"Portrait","orient.square":"Square","fill.temp":"Temperature","space.temp_min":"Comfort from","space.temp_max":"to","tip.temp_avg":"average temperature:","tip.hum_avg":"average humidity:","space_card.button":"Open the space plan","space_card.not_found":"Space “{id}” not found","space_card.loading":"Loading…","continuity.restore_plan":"Restoring floor plan…","continuity.restore_connection":"Restoring device connection…","continuity.retry":"Try again","editor.space":"Space","editor.show_button":"Show button","editor.button_label":"Button label","editor.button_target":"Target dashboard path","marker.sub_entity":"entity","title.general_settings":"General settings","gs.title":"General settings","gs.hint":"Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.","gs.light_group":"Fill: lights","gs.light_on":"Lights on","gs.light_off":"All lights off","gs.temp_group":"Fill: temperature","gs.temp_cold":"Cold","gs.temp_ok":"Comfortable","gs.temp_hot":"Hot","gs.lqi_group":"Fill: zigbee signal","gs.lqi_low":"Weak signal","gs.lqi_high":"Strong signal","gs.reset":"Reset to defaults","gs.saved":"General settings saved","space.show_lqi":"Show zigbee signal (LQI) next to devices","space.hide_decor":"Hide the decorative layer","space.hide_decor_tip":"Lines, shapes, labels and furniture stay where they are — visible in the backdrop editor, not on the plan.","space.hide_openings":"Hide openings","space.hide_openings_tip":"Door, window and gate symbols are not drawn, but the openings keep working: light passes through, the sun comes in at a window, contact sensors still open them. The plan editor always shows them.","gs.light_none":"No light sources","mode.plan":"Plan editor","mode.devices":"Device editor","display.value":"Value + state","marker.subarea":"no area, manual","device.new":"New device — open its editor to dismiss","opening.unlock_action":"Unlock","opening.lock_action":"Lock","opening.lock_pending":"Working…","title.close_editor":"Close editor (back to view)","devbar.add":"Add","devbar.show_all":"Hidden and disabled","devbar.rules":"Icon rules","device_inbox.button":"Devices","device_inbox.title":"Devices on the plan","device_inbox.search":"Search devices, entities and integrations…","device_inbox.add_virtual":"Add virtual device","device_inbox.tab_on_plan":"On plan","device_inbox.tab_available":"Available","device_inbox.tab_hidden":"Hidden","device_inbox.tab_readd":"Available again","device_inbox.only_new":"New only","device_inbox.show_entities":"Show entities","device_inbox.show_hidden":"Show hidden on plan","device_inbox.new":"New","device_inbox.find":"Find on plan","device_inbox.edit":"Edit","device_inbox.hide":"Hide","device_inbox.show":"Show","device_inbox.add":"Add","device_inbox.readd":"Add again","device_inbox.hide_available":"Hide from list","device_inbox.show_more":"Show more","device_inbox.more_actions":"More actions","device_inbox.find_hidden_hint":"Enable “Show hidden on plan” first","device_inbox.show_disabled":"Activate this binding in Home Assistant before showing it","device_inbox.empty_on_plan":"No matching devices are on the plan yet.","device_inbox.empty_available":"No devices are available to add.","device_inbox.empty_hidden":"There are no hidden devices.","device_inbox.empty_readd":"No removed devices are available to add again.","device_inbox.reason_visible_auto":"Discovered automatically","device_inbox.reason_visible_explicit":"Added explicitly","device_inbox.reason_manual_hidden":"Hidden by user","device_inbox.reason_automatic_hidden":"Hidden automatically","device_inbox.reason_service_entry":"Service-only registry entry","device_inbox.reason_excluded_integration":"Integration excluded by device filters","device_inbox.reason_excluded_domain":"Non-spatial entity type","device_inbox.reason_grouped_light":"Represented by a room light group","device_inbox.reason_represented_by_parent":"Represented by its parent device","device_inbox.reason_removed":"Previously removed from the plan","device_inbox.reason_available":"Available to add to the plan","device_inbox.reason_no_bound_room":"HA area is not linked to a plan room","device_inbox.status_ha_disabled":"Disabled in Home Assistant","device_inbox.status_orphaned":"Binding is no longer present in Home Assistant","device_inbox.status_unverified":"Home Assistant registry is temporarily unavailable","device_inbox.saved":"Device list updated","space.roomcard_section":"Room card shows:","space.label_temp":"Temperature","space.label_hum":"Humidity","space.label_lqi":"Average Zigbee signal","space.label_light":"Lights on/off","roomcard.light_on":"On","roomcard.light_off":"Off","roomcard.light_partial":"{on} of {total}","toast.split_pick_inside":"Intermediate cut points must be inside the room","mode.decor":"Background editor","decor.select":"Select","decor.line":"Line","decor.rect":"Rectangle","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Erase","decor.erase_confirm_title":"Erase object?","decor.color":"Color","decor.width":"Line width","decor.line_style":"Line style","decor.line_style_solid":"Solid","decor.line_style_dashed":"Dashed","decor.fill":"Fill","decor.fill_color":"Fill color","decor.length":"Length","decor.size":"Size","decor.angle":"Rotation angle, °","decor.text_size":"Text size","decor.backdrop_properties":"Plan backdrop properties","decor.text_title":"Text label","decor.object_title":"Edit {kind}","decor.text_label":"Text","decor.live_group":"Insert HA variable","decor.live_entity":"Entity","decor.live_entity_ph":"choose an entity","decor.live_attr":"Value","decor.live_attr_ph":"choose state or attribute","decor.live_state":"State","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag to move; pull a corner to resize; use the top handle to rotate. Shift changes proportions or frees the angle.","decor.backdrop_reset":"Reset the picture","decor.backdrop_reset_done":"The picture is back at its original place and size","marker.icon_auto":"Auto: {icon} (by icon rules; pick one to override)","marker.icon_pin_auto":"Pin","mode.plan_tip":"Plan editor — the geometry of the home: draw and split/merge rooms, bind them to HA areas, place doors, windows and gates, move room cards, set the scale","mode.devices_tip":"Device editor — everything about icons: drag to position, click to edit binding/icon/display, add virtual devices, icon rules","mode.decor_tip":"Background editor — purely visual decor under the plan: lines, rectangles, ovals and text labels that never react to clicks","space.glow_enabled":"Light-source glow","gs.glow_group":"Light-source glow","gs.glow_base":"House darkness","gs.glow_light":"Default light color / intensity","gs.wall_group":"Walls","gs.wall_fill":"Wall fill","gs.glow_radius":"Glow radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Controls other light sources","marker.controls_hint":"With tap action “Toggle”, a click flips every source added here. The marker’s own bound entity is controlled directly and cannot be added; use the switch below to classify it as a light.","marker.controls_filter":"Search lights and switches…","info.controls":"Controls","marker.glow_radius_label":"Glow radius","marker.glow_radius.help":"Sets the glow radius in metres or feet; an empty value uses the radius from general settings.","marker.glow_radius.help.aria":"Help: glow radius","markup.wallthick":"Thickness","markup.select":"Select","title.markup_wallthick":"Thickness — click a wall to set its thickness from 0 to 100 cm.","markup.hint_wallthick":"click a wall · Esc closes without applying","wallthick.field":"Thickness","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Apply to all walls of this room","markup.draw_wall_title":"Thickness of each new wall segment (0–100 cm). Shared walls keep the existing value.","room.queue_progress":"Room {current} of {total}","toast.wall_rooms_saved":"Created rooms: {n}","toast.wall_chain_saved":"Wall chain saved","toast.wallthick_pick":"Click a wall","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from {min} to {max} {unit}","toast.zero_wall_opening_conflict":"Remove the opening on this wall segment first.","toast.zero_wall_ambiguous":"The wall segment is ambiguous. Simplify or adjust the junction.","toast.zero_wall_migration_blocked":"The space was not converted: {reason}. No data was changed.","toast.physical_angle":"Enter a rotation angle from 0° up to, but not including, 90°","toast.physical_limit":"The space has reached the limit for this type of geometry","toast.geometry_unsafe":"Change canceled: wall geometry could not be built safely.","junction.limit_angle":"The angle between walls is too sharp: {actual}°, minimum {limit}°.","junction.limit_valence":"Too many walls meet in one node: {actual}, maximum {limit}.","junction.limit_length":"The wall segment is too short: {actual} cm, minimum {limit} cm.","junction.limit_distance":"Walls and nodes are too close: {actual} cm, minimum {limit} cm.","junction.limit_clearance":"No room interior is left: {actual} cm², minimum {limit} cm².","toast.wall_model_migration_blocked":"The wall model could not be updated: {reason}. The plan was not changed. Run “Optimize plans”; if the error repeats, fix the conflicting wall geometry.","toast.wall_model_client_outdated":"Update the card and reload the page before editing the plan.","wall_model.reason.invalid-room":"invalid room contour","wall_model.reason.zero-length":"zero-length wall segment","wall_model.reason.third-owner":"wall shared by more than two rooms","wall_model.reason.duplicate-id":"conflicting wall identifiers","wall_model.reason.thickness-conflict":"conflicting wall thickness values","wall_model.reason.opening-host":"opening cannot be assigned to one wall","toast.delete_room_pick":"Click inside the room you want to delete","toast.plan_snap_ambiguous":"Zoom in to choose the wall node","toast.wall_repair_ambiguous":"The room outline has more than one possible connection. Zoom in and connect it explicitly.","toast.wall_repair_too_large":"The gap is larger than 2 cm. Connect the walls before creating the room.","toast.wall_repair_changed":"The wall geometry changed. Try creating the room again.","toast.opening_on_zero_wall":"Openings cannot sit on a zero-thickness wall","marker.from_ha_option":"Pick from the HA list","marker.show_entities":"Show entities","marker.show_entities_tip":"Adds not only devices to the list, but all their entities too","marker.pick_ph":"Choose a device…","room.open_area":"Open the HA area","view.volumetric":"Volumetric view","view.flat":"Flat view","kiosk.title":"This screen's sizes","kiosk.hint":"Stored on this device only — every wall tablet or TV can have its own comfortable sizes.","kiosk.icon_scale":"Device icon size","kiosk.font_scale":"Room card text size","editor.kiosk":"Wall device (kiosk) mode","editor.cycle":"Auto-switch spaces every N seconds (kiosk, 0 = off)","room.settings_title":"Room settings","room.settings_section":"Room settings (override the space)","room.fill_label":"Fill in THIS room","fill.inherit":"As the space","room.custom_fill_space":"Space color","room.custom_fill_own":"Room color","room.temp_src_label":"Temperature source","room.hum_src_label":"Humidity source","room.src_average":"Average over the room's sensors (default)","room.src_pick":"A specific HA device or entity","room.src_ph":"Choose a source…","toast.room_updated":"Room updated","space.card_font":"Room-card font size (whole space)","room.sizes_section":"Font sizes","room.name_scale":"Room name size","room.label_scale":"Metrics size","preview.room_name":"Living room","toast.cfg_reload_failed":"Could not reload the plan from the server: {err}","room.settings_short":"Room settings","room.unnamed":"Unnamed room","marker.use_climate_temp":"Include the device temperature in the room","marker.use_climate_temp_tip":"Adds an air conditioner or thermostat current_temperature to the room average. Configure the external value badge separately below.","marker.value_badge_title":"Value badge","marker.value_badge.help":"Shows one selected value next to the icon. It does not affect room metrics, light, Glow or the tap action.","marker.value_badge.help.aria":"Help: device value badge","marker.value_badge_enabled":"Show a value badge","marker.value_badge_source":"Value","marker.value_badge_source.help":"Choose a specific state, supported attribute, or derived value of this device.","marker.value_badge_source.help.aria":"Help: value badge source","marker.value_badge_position":"Position","marker.value_badge_position.help":"The chosen side stays fixed and does not flip automatically at a plan edge.","marker.value_badge_position.help.aria":"Help: value badge position","marker.value_badge_right":"Right","marker.value_badge_bottom":"Below","marker.value_badge_left":"Left","marker.value_badge_top":"Above","marker.value_badge_empty":"This device has no available values","marker.value_badge_static":"A static icon does not show live values. The setting is preserved.","marker.value_badge_missing":"Source unavailable","marker.value_badge_missing_hint":"The saved source is currently unavailable. Keep it, disable the badge, or choose another value.","marker.value_badge_duplicate":"The same value is already shown inside the icon.","marker.value_badge_state":"State · {name}","marker.value_badge_marker_state":"Light-source state · {name}","marker.value_badge_lqi":"Average Zigbee signal quality","marker.value_badge_attr_current_temperature":"Current temperature · {name}","marker.value_badge_attr_temperature":"Target temperature · {name}","marker.value_badge_attr_current_humidity":"Current humidity · {name}","marker.value_badge_attr_humidity":"Humidity · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Speed · {name}","marker.value_badge_attr_brightness":"Brightness · {name}","marker.value_badge_attr_volume_level":"Volume · {name}","marker.value_badge_attr_battery_level":"Battery · {name}","marker.value_badge_attr_fan_speed":"Fan speed · {name}","marker.light_role_label":"Is this device a light source?","marker.light_role.help":"Controls whether this marker itself is a spatial light source. Linked lights above remain independent room-light controls.","marker.light_role.help.aria":"Help: whether this device is a light source","marker.light_role_auto_yes":"Auto (light source)","marker.light_role_auto_no":"Auto (not a light source)","marker.light_role_always":"Always a light source","marker.light_role_never":"Never a light source","marker.light_entity_label":"Leading light entity","marker.light_entity.help":"For a composite device, choose the entity whose state, colour and brightness represent this plan source. The automatic fallback remains compatible with older plans.","marker.light_entity.help.aria":"Help: leading light entity","marker.light_entity_auto":"Automatic ({entity})","marker.light_entity_none":"no controllable entity","marker.light_entity_missing":"The saved entity {entity} is unavailable. House Plan temporarily uses {fallback}; the saved choice will be restored if it returns.","marker.toggle_entity_label":"Entity to toggle","marker.toggle_entity.help":"For a composite device, choose the exact own light or switch operated by a tap. Automatic keeps the previous target rules and is independent from the leading light entity.","marker.toggle_entity.help.aria":"Help: entity to toggle","marker.toggle_entity_auto":"Automatic ({entity})","marker.toggle_entity_none":"no own controllable entity","marker.toggle_entity_missing":"The saved entity {entity} is no longer among this marker’s selectable channels. House Plan temporarily uses {fallback}; the saved choice will be restored if it returns.","marker.glow_color_label":"Glow colour and brightness","marker.glow_mode.help":"Use live source values, override only its colour, or fix both colour and brightness. The minimum is 1%; to disable the source, choose “Never”.","marker.glow_mode.help.aria":"Help: glow colour and brightness","marker.glow_mode_auto":"From source","marker.glow_mode_color":"Set colour","marker.glow_mode_fixed":"Set colour and brightness","marker.glow_color":"Glow colour","marker.glow_brightness":"Brightness","marker.glow_disabled_never":"Glow settings are unavailable because this marker is explicitly not a light source.","marker.glow_disabled_auto":"Auto mode found no spatial light source for this marker.","marker.glow_disabled_no_entity":"No active controllable Home Assistant entity is available for this spatial source.","marker.glow_passive_hint":"This source has no own Home Assistant data. Set its colour and brightness manually; its radius remains available.","marker.control_broken":"Saved source is missing or no longer marked as a light","marker.control_missing_label":"Missing plan light","marker.control_passive":"passive source","toast.marker_control_cycle":"This link would create a circular chain of light controls.","toast.marker_binding_required":"Choose a Home Assistant device before linking another light source.","confirm.unlock":"Unlock “{name}”?","toast.files_migrate_failed":"Attachments could not be moved to the new binding, links keep pointing at the old files: {err}","space.pick_saved":"Already uploaded","space.pick_saved_hint":"Plans stored on the server, including ones you detached earlier","space.no_saved":"No plans stored on the server yet.","space.loading":"Loading…","space.used_by":"in use: {list}","space.in_use":"A space still uses this plan — detach it first","btn.use":"Use","confirm.delete_plan":'Delete the plan file "{name}" from the server? This cannot be undone.',"toast.plans_list_failed":"Could not list the stored plans: {err}","toast.plan_delete_failed":"Could not delete the plan: {err}","marker.hide":"Hide","marker.hide_tip":'The device will disappear from the plan after saving but will still count toward the room signal. Restore it through the "Devices" catalog in the device editor.',"marker.show":"Show","marker.show_tip":"The device will appear on the plan again after saving.","marker.hidden_ghost":"Device hidden by the user","marker.ha_disabled_device":"The device is disabled in Home Assistant and hidden from the plan.","marker.ha_disabled_entity":"The entity is disabled in Home Assistant and hidden from the plan.","marker.ha_disabled_all_entities":"The device has no active Home Assistant entities, so it is hidden from the plan.","marker.ha_registry_limited":"The full Home Assistant registry is unavailable to this user. The unverified object cannot be shown or used for now.","marker.delete_tip":"Completely delete the device from the plan and every aggregate. You can add it again later.","tap.run":"Run automation/script/scene","tap.cover":"Open/close (curtains/blinds)","marker.run_target_label":"What to run","marker.run_search_ph":"Search: automation, script or scene…","marker.run_target_gone":"Target {id} not found — pick again","marker.tap_confirm":"Ask for confirmation","marker.tap_confirm_tip":"Show a confirmation dialog before acting — a guard against accidental taps.","marker.toggle_hint_single":"Target: {name} ({id}).","marker.virtual_light_target":"Virtual light: {name}.","marker.virtual_light_current":"Manual state: {state} → {effect}.","marker.virtual_light_state_on":"on","marker.virtual_light_state_off":"off","marker.toggle_hint_group":"Will toggle {count} source(s): {names}.","marker.toggle_hint_current":"Now: {state} → {effect}.","marker.toggle_hint_group_current":"Currently on: {on} of {count} → {effect}.","marker.toggle_hint_skipped":"Will be skipped ({count}): {targets}.","marker.toggle_effect_turn_on":"will turn on","marker.toggle_effect_turn_off":"will turn off","marker.toggle_effect_open":"will open","marker.toggle_effect_close":"will close","marker.toggle_effect_stop":"will stop","marker.toggle_effect_toggle":"state will toggle","marker.toggle_skip_missing":"not found","marker.toggle_skip_ha_disabled":"disabled in HA","marker.toggle_skip_unavailable":"unavailable","marker.toggle_skip_unsupported":"toggle is unsupported","marker.toggle_skip_secure":"blocked for security","marker.toggle_none_no_binding":"This device has no binding, so there is no state to toggle. Tapping will do nothing.","marker.toggle_none_no_actionable_entity":"This device has no state that can be toggled. Tapping will do nothing.","marker.toggle_none_configured_targets_missing":"The configured targets are unavailable. The device's own entity will not be substituted for them.","marker.toggle_none_ha_disabled":"The target is disabled in Home Assistant and cannot be used on the plan.","marker.toggle_none_unavailable":"The target is currently unavailable. Tapping will do nothing.","marker.toggle_none_unsupported":"Home Assistant exposes no safe toggle operation for this entity. Tapping will do nothing.","marker.toggle_none_secure":"Toggling locks, alarms and secure gates from the plan is blocked for security.","run.automation":"automation","run.script":"script","run.scene":"scene","confirm.tap_run":'Run "{name}"?',"confirm.tap_toggle":'Toggle "{name}"?',"confirm.tap_cover":'Open/close "{name}"?',"confirm.current_state":"Current state: {state}","confirm.expected_state":"After switching: {state}","confirm.group_current":"on {on} of {total}","confirm.group_all_on":"all are on","confirm.group_all_off":"all are off","confirm.unavailable_targets":"Unavailable: {count}","confirm.expected_by_ha":"Home Assistant will determine the state","confirm.state_on":"On","confirm.state_off":"Off","confirm.state_open":"Open","confirm.state_closed":"Closed","confirm.state_opening":"Opening","confirm.state_closing":"Closing","confirm.state_stopped":"Stopped","confirm.state_unknown":"Unknown","toast.run_started":"Started: {name}","toast.run_target_missing":"Run target not found — check the device settings","toast.run_target_required":"Pick an automation, script or scene","toast.tap_target_changed":"The action target changed. Try again.","toast.virtual_light_toggle_failed":"Could not toggle the virtual light: {err}","toast.value_badge_source_required":"Choose a value for the badge","btn.run":"Run","vac.section":"Robot vacuum: live position","vac.autocal":"Set up automatically","vac.live":"Live position on the plan","vac.trail":"Show the robot's path","vac.cal_maps":"Calibrated maps: {maps}","vac.autocal_no_rooms":"The integration reports no room list — open “Fit manually”","vac.autocal_no_match":"Room names did not match (need ≥3 in common) — open “Fit manually”","vac.autocal_done":"Done: bound via {rooms} rooms. Start a cleanup and check","vac.cal_need_pos":"The robot is not reporting coordinates — start a cleanup and pause it","vac.cal_done":"Calibration saved. Start a cleanup and check","vac.cal_cancelled":"Calibration cancelled","vac.fit":"Fit manually","vac.fit_hint":"Drag the robot map into place, stretch by the corners","vac.fit_rotate":"Rotate 90°","vac.fit_mirror":"Mirror","vac.trail_never":"Never","vac.trail_cleaning":"While cleaning","vac.trail_always":"Always","gs.bg_group":"Stage background","gs.bg_color":"Background around the plan","gs.bg_default":"Theme default","gs.bg_theme":"theme default","gs.bg_mode":"Plan background","gs.bg_static":"Static color","gs.bg_daynight":"Follows the Sun","gs.bg_daynight_hint":"The background moves through dawn, day, dusk and night using sun.sun, or your local clock when sun data is unavailable. No compass is needed for the background.","gs.sun_group":"Sun","gs.sun_missing":"The sun.sun entity was not found — the background follows your local clock; window rays are unavailable.","gs.north":"North on the plan","gs.north_ph":"not set","gs.north_hint":"Set north only for window rays (1° steps, 15° with Shift); the Sun background works without it.","gs.north_clear":"Clear","gs.north_letter":"N","gs.sun_rays":"Sunlight through windows","gs.about_group":"About","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · docs & issues","gs.about_telegram":"Telegram chat","space.bg_color":"Background around the plan","space.bg_inherit":"Inherit general","space.bg_inherited":"inherits general settings","space.bg_mode":"Plan background","space.north":"North on the plan (override)","space.north_inherited":"inherited: {v}","space.sun_rays":"Sunlight through windows","space.sun_inherit":"Inherit general","space.sun_on":"On","space.sun_off":"Off","canvas.far_objects":"{n} object(s) far from the plan","canvas.show_far":"Show","canvas.home_tip":"The plan is over there — click to fit it","gs.grid_group":"Plan maintenance","gs.grid_hint":"Updates data models, aligns plan elements to the grid and merges redundant wall fragments. An exact report is shown before anything is stored.","gs.align_all":"Optimize plans","gs.align_title":"Optimize plans","gs.align_none":"All plans already use the current optimized data model.","gs.optimize_no_automatic_changes":"There are no automatic changes to apply. Review the items below.","gs.align_count":"{n} of {total} elements will move, by at most {cm} cm.","gs.align_where":"The largest shift is in “{s}”.","gs.align_turned":"Openings whose angle is corrected: {n}.","gs.align_removed_drafts":"Invalid outlines collapsed by the grid and removed: {n}.","gs.optimize_redundant_drafts":"Saved wall chains hidden by solid room walls and removed: {n}.","gs.align_preflight_failed":"Could not safely verify the geometry of the following spaces: {spaces}{more}.","gs.align_preflight_hint":"Plans were not changed. Copy the diagnostics with the button below and attach them to the bug report together with a space export.","gs.preflight_reason_prepare-exception":"Could not prepare the space geometry (exception while building the model)","gs.preflight_reason_wall-null":"The wall body did not build (the union came back empty)","gs.preflight_reason_wall-degraded-extra":"The wall body degraded with extra geometry","gs.preflight_reason_wall-failed-core":"The wall body core failed to assemble","gs.preflight_reason_wall-exception":"Wall construction threw an exception","gs.preflight_reason_floor-null":"The floor outline did not build","gs.preflight_reason_floor-exception":"Floor construction threw an exception","gs.preflight_copy":"Copy diagnostics","gs.preflight_copied":"Diagnostics copied","gs.preflight_update_hint":"The card and integration versions differ — update House Plan and retry.","gs.align_preflight_space":"Space {n}","gs.align_preflight_more":", and {n} more","gs.optimize_changes":"Model migrations: {m}; spaces updated: {c}; noisy coordinate values removed: {p}; merged real-wall fragments: {w}; merged zero-thickness wall fragments: {s}; independent walls: {i}.","gs.zero_walls_migrated":"Virtual wall spans converted: {n}.","gs.wall_segments_migrated":"Wall segments stabilised: {n}.","gs.optimize_lattice_summary":"Noisy coordinate values canonicalized: {n}; maximum movement: {cm} cm.","gs.optimize_lattice_space":"{space}: coordinate values canonicalized: {n}; off-grid values left unchanged: {far}.","gs.optimize_coincident_partitions":"Hidden independent wall sections absorbed into room walls: {n}.","gs.optimize_openings_rehosted":"Openings reattached to room walls: {n}.","gs.optimize_walls_straightened":"Walls straightened: {n}; maximum movement: {cm} cm.","gs.optimize_walls_straightened_where":"Largest wall correction: {s}.","gs.optimize_walls_straighten_skipped":"Near-axis walls left unchanged because they could not be repaired safely: {n}.","gs.optimize_glow_migration":"Legacy Glow: {spaces} spaces → no data fill + independent Glow; {rooms} rooms → inherited data fill + independent Glow.","gs.optimize_references":"References repaired: spaces — {spaces}; rooms — {rooms}; positions — {positions}; devices detached from missing spaces — {detached}.","gs.optimize_reference_more":", and {n} more","gs.optimize_orphans_removed":"Forgotten records removed: {total} — room labels: {rooms}; devices: {devices}; group markers: {groups}. They belonged to spaces deleted earlier.","gs.optimize_live_positions":"Old positions in deleted spaces belong to existing objects: {n}{names}. They will be kept.","gs.optimize_live_positions_remove":"Old positions in deleted spaces belong to existing objects: {n}{names}. They are selected for removal.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Remove old positions","gs.optimize_live_keep":"Keep old positions","gs.optimize_live_selected":"Old positions will be removed after Optimize is applied.","gs.optimize_unverified":"Could not safely verify positions: {n}. They were left unchanged.","gs.optimize_registry_limited":"Full administrator access to the Home Assistant registries is required for a safe check.","gs.optimize_vacuum_warning":"Vacuum room mappings that still need review: {n}.","gs.optimize_details":"Details","gs.optimize_details_more":"And {n} more records.","gs.optimize_detail_removed":"will be removed","gs.optimize_detail_live":"will be kept","gs.optimize_detail_unverified":"not verified","gs.optimize_detail_room_label":"room label","gs.optimize_detail_device":"device","gs.optimize_detail_group":"group marker","gs.optimize_detail_unknown":"unknown owner","gs.optimize_detail_item":"{status}: {kind} {id}; old space {space}","gs.align_warn":"Elements deliberately placed between grid nodes will move. One undo is available after the operation, only until the next plan edit.","gs.align_run":"Optimize","gs.align_done":"Plans optimized: {n} elements moved, {m} records maintained, {r} references repaired","gs.optimize_undo":"Undo last optimization","gs.optimize_undone":"The last optimization was undone","decor.furniture":"Furniture","furn.title":"Furniture library","furn.symbol":"Symbol","furn.group_furniture":"Furniture","furn.group_appliance":"Appliances","furn.group_sanitary":"Plumbing","furn.group_other":"Other","furn.width":"Width","furn.depth":"Depth","furn.pick_hint":"Pick a symbol, then click on the plan.","furn.place_hint":"Click on the plan — the piece lands against the nearest wall. Shift places it free.","furn.sym_sofa":"Sofa","furn.sym_armchair":"Armchair","furn.sym_coffee_table":"Coffee table","furn.sym_table_dining":"Dining table","furn.sym_table_round":"Round table","furn.sym_chair":"Chair","furn.sym_desk":"Desk","furn.sym_bed_double":"Double bed","furn.sym_bed_single":"Single bed","furn.sym_nightstand":"Nightstand","furn.sym_wardrobe":"Wardrobe","furn.sym_bookshelf":"Bookshelf","furn.sym_fridge":"Fridge","furn.sym_stove":"Cooker","furn.sym_dishwasher":"Dishwasher","furn.sym_washer":"Washing machine","furn.sym_dryer":"Tumble dryer","furn.sym_tv":"TV","furn.sym_ac":"Air conditioner","furn.sym_water_heater":"Water heater","furn.sym_toilet":"Toilet","furn.sym_bathtub":"Bathtub","furn.sym_shower":"Shower","furn.sym_sink":"Washbasin","furn.sym_kitchen_sink":"Kitchen sink","furn.sym_bidet":"Bidet","furn.sym_stairs":"Stairs","furn.sym_fireplace":"Fireplace","furn.sym_plant":"Plant","furn.sym_rug":"Rug","common.yes":"Yes","common.no":"No","vac.diag_source":"Source","vac.diag_platform":"Integration","vac.diag_status":"Status","vac.diag_position":"Position","vac.diag_rooms":"Rooms","vac.diag_rooms_value":"{total} · {matched} names match · {readiness}","vac.autocal_ready":"auto-calibration available","vac.autocal_not_ready":"need 3 matching names","vac.diag_path":"Integration path","vac.diag_map":"Map ID","vac.source_none":"not selected","vac.source_status_ok":"Ready","vac.source_status_missing":"Missing","vac.source_status_disabled":"Disabled in Home Assistant","vac.source_status_unavailable":"Unavailable","vac.source_status_unverified":"Cannot verify with current permissions","vac.source_status_unsupported":"No position data","vac.source_status_none":"No source","vac.source_banner_missing":"The saved source no longer exists. It was not replaced automatically; choose another source or restore it in Home Assistant.","vac.source_banner_disabled":"The saved source is disabled in Home Assistant. Enable it there or choose another source.","vac.source_banner_unverified":"Current Home Assistant permissions cannot verify this saved source. It remains pinned and will not be replaced automatically.","vac.choose_source":"Choose source","vac.source_auto":"Automatic","vac.source_auto_hint":"Use a compatible entity from this device","vac.all_cameras":"All cameras","vac.all_cameras_warn":"A camera may not provide robot data. Choose one only if it is your vacuum map.","vac.all_cameras_empty":"No other camera entities found.","vac.platform_unknown":"unknown integration","vac.cap_position":"position","vac.cap_rooms_short":"rooms","vac.cap_path":"path","vac.cap_map":"map ID","vac.cap_none":"no robot data detected","vac.xcme_hint":"Enable these Xiaomi Cloud Map Extractor attributes:","vac.documentation":"Documentation","vac.residual_title":"Check automatic calibration","vac.residual_message":"The matched rooms disagree by up to {error}. Apply this approximate calibration, refine it manually, or cancel without changing the saved setup.","vac.apply_proposal":"Apply","gs.backup_group":"Backup and transfer","gs.backup_hint":"Download a portable JSON backup or preview and import a backup made by House Plan.","backup.export_open":"Export","backup.import_open":"Import","backup.export_title":"Export House Plan","backup.import_title":"Import House Plan","backup.export_hint":"Choose whether the backup should contain the whole House Plan configuration or only the current space.","backup.full":"Full backup","backup.current_space":"Current space","backup.current_space_title":"Current space: {title}","backup.no_current_space":"No current space","backup.plan_only":"Plan only","backup.plan_only_hint":"Keep rooms, walls, openings, decor and room-label positions without devices or Home Assistant bindings.","backup.plan_only_preview":"This file contains the plan only","backup.privacy_warning":"The archive keeps names, Home Assistant identifiers and exact coordinates. Internal plans and attachments are referenced, not embedded; runtime states and vacuum trails are not included.","backup.download":"Download JSON","backup.export_done":"Backup downloaded","backup.reading":"Checking the backup…","backup.revalidated":"The plan changed after this preview. The summary was refreshed; review it and confirm again.","backup.error.unauthorized":"You do not have permission to export or import this plan.","backup.error.not_ready":"House Plan is not ready yet. Try again in a moment.","backup.error.too_large":"The backup exceeds the 8 MiB limit.","backup.error.invalid_json":"The selected file is not valid JSON.","backup.error.invalid_format":"The selected file is not a House Plan backup.","backup.error.unsupported_export_version":"This backup format is not supported by the installed version.","backup.error.future_model":"The backup was created by a newer House Plan data model.","backup.error.invalid_config":"The backup contains an invalid House Plan configuration.","backup.error.wall_model_migration_blocked":"The backup wall model could not be upgraded safely. Optimize the source plan first, then export it again.","backup.error.invalid_layout":"The backup contains invalid object positions.","backup.error.invalid_content":"The backup contains invalid or inconsistent content references.","backup.error.space_not_found":"The selected space no longer exists.","backup.error.capacity_exceeded":"Adding this backup would exceed the plan limits.","backup.error.preview_expired":"The preview expired. Select the backup file again.","backup.error.preview_owner_mismatch":"This preview belongs to another Home Assistant user.","backup.error.conflict":"The plan changed after the preview. Review the refreshed summary.","backup.error.content_confirmation_required":"Confirm that unavailable local content may be detached.","backup.error.commit_failed":"The backup could not be applied safely. The previous plan was restored or is pending recovery.","backup.error.missing_plan":"A referenced plan file disappeared after the preview. Review the backup again.","backup.error.missing_content":"A referenced local attachment disappeared after the preview. Review the backup again.","backup.error.marker_control_missing":"A linked plan light is missing from the backup.","backup.error.marker_control_not_light":"A linked plan target is no longer marked as a light source.","backup.error.marker_control_self":"A light source cannot control itself.","backup.error.marker_control_cycle":"The backup contains a circular chain of light controls.","backup.error.duplicate_marker_control":"The backup contains a duplicate plan-light link.","backup.error.no_backup":"There is no import or optimization snapshot to restore.","backup.same_source":"Created on this Home Assistant instance","backup.foreign_source":"Created on another Home Assistant instance","backup.created":"Created: {value}","backup.versions":"Card {card}; integration {integration}; data model {model}","backup.count_spaces":"Spaces: {n}","backup.count_rooms":"Rooms: {n}","backup.count_walls":"Walls: {n}","backup.count_openings":"Openings: {n}","backup.count_decor":"Decor objects: {n}","backup.count_markers":"Devices: {n}","backup.count_layout":"Positions: {n}","backup.bindings":"Bindings — devices: {device}, entities: {entity}, virtual: {virtual}; positions without a space: {legacy}","backup.binding_status":"Target status — active: {active}, disabled: {disabled}, missing: {missing}","backup.missing_areas":"Areas missing on the target: {areas}","backup.dropped_marker_links":"Plan-light links outside this transferred space were omitted: {n}.","backup.repaired_target_refs":"Existing references restored by this space import: {n}.","backup.preserved_unresolved_refs":"References that could not be restored unambiguously were preserved: {n}.","backup.preserved_unresolved_hint":"No data was guessed or deleted. After the import, run Optimize plans to inspect the remaining references.","backup.import_details":"Import reference details","backup.import_detail.incoming_remapped":"References updated inside the imported copy: {n}","backup.import_detail.target_repaired":"Existing references restored: {n}","backup.import_detail.preserved_unresolved":"Unresolved references preserved: {n}","backup.import_detail.collisions":"Destination conflicts preserved safely: {n}","backup.import_detail.dropped_links":"Incoming links omitted by transfer rules: {n}","backup.import_detail.bounded_lineages":"Overly nested identifiers left bounded: {n}","backup.replace_warning":"This replaces the current configuration and layout. Uploaded files are never deleted. One undo remains available until the next plan edit.","backup.foreign_bookkeeping":"Instance-specific known/new-device bookkeeping will not be imported.","backup.final_name":"New space name","backup.target_settings":"The target instance's global settings remain unchanged.","backup.duplicates":"Bindings already present: {n}","backup.skip":"Skip duplicate devices","backup.virtual_copy":"Add safe static virtual copies","backup.content":"Referenced content","backup.content_available":"available locally","backup.content_external":"external link","backup.content_detach_required":"will be detached","backup.confirm_detach":"I understand that unavailable internal plans and attachments will be detached from the imported configuration.","backup.replace":"Replace","backup.add":"Add space","backup.space_done":"Space imported: {rooms} rooms, {markers} devices, {refs} existing references restored","backup.full_done":"Backup restored: {spaces} spaces, {rooms} rooms, {markers} devices","backup.undo_import":"Undo last full import","backup.import_undone":"Full import undone"};const aa={en:oa,ru:{"editor.context_actions":"Действия: {object}","editor.tool_options":"Параметры инструмента: {tool}","editor.palette":"Палитра: {tool}","editor.open_group":"Группа инструментов: {group}","editor.group_active":"{group} — активно: {item}","editor.disabled_action":"{action} недоступно: {reason}","color_picker.title":"Выбор цвета","color_picker.hue":"Оттенок","color_picker.saturation":"Насыщенность","color_picker.value":"Яркость","color_picker.hex":"Цвет HEX","color_picker.invalid_hex":"Введите цвет HEX из 3 или 6 цифр","btn.properties":"Свойства","btn.keep_as_walls":"Оставить стенами","btn.delete_room_keep_walls":"Удалить комнату, оставить стены","btn.delete_room_with_walls":"Удалить комнату и стены","title.markup_select":"Выбор и редактирование стен, колонн и сохранённых контуров","title.markup_column":"Колонна: кликните точку сетки для квадратной колонны","markup.partition":"Перегородка","markup.column":"Колонна","markup.hint_column":"кликните точку сетки, чтобы поставить квадратную колонну","history.draft_segment":"Добавление сегмента черновика комнаты","history.draft_merge":"Соединение незавершённых контуров комнаты","history.draft_segment_delete":"Удаление сегмента черновика комнаты","history.partition_add":"Добавление перегородки","history.column_add":"Добавление колонны","history.physical_edit":"Изменение физического объекта","history.physical_delete":"Удаление физического объекта","history.physical_move":"Перемещение физического объекта","history.contour_to_partitions":"Преобразование контура в замкнутые стены","history.wall_chain_finish":"Завершение цепочки стен","history.wall_face_batch":"Создание комнат из стен","toast.column_duplicate":"Колонна с тем же центром и внешним размером уже существует","confirm.delete_draft":"Удалить весь незавершённый контур комнаты?","confirm.delete_draft_segment":"Удалить этот сегмент черновика? Оставшийся контур может разделиться на два.","physical.partition_properties":"Свойства перегородки","physical.column_properties":"Свойства колонны","physical.draft_properties":"Свойства сегмента черновика","physical.shape":"Форма","physical.square":"Квадрат","physical.circle":"Круг","physical.diameter":"Диаметр","physical.side":"Сторона","physical.rotation":"Угол поворота","physical.length":"Длина","physical.allowed_range":"Допустимо: {min}–{max} {unit}","physical.delete_segment":"Удалить сегмент","physical.delete_draft":"Удалить весь контур","physical.column_size_title":"Внешняя сторона квадратной колонны, создаваемой кликом (1–150 см).","card.title":"План дома","count.devices":"{n} устр.","empty.no_spaces":"Пространств пока нет.","empty.add_first":"Добавьте первое пространство и загрузите план этажа.","empty.install":"Установите интеграцию House Plan и добавьте запись в «Устройства и службы».","btn.add_space":"Добавить пространство","btn.cancel":"Отмена","btn.save":"Сохранить","btn.close":"Закрыть","btn.delete":"Удалить","btn.edit":"Редактировать","btn.open_in_ha":"Открыть в HA","btn.reset":"Сброс","btn.attach":"Прикрепить…","btn.upload":"Загрузить…","btn.replace":"Заменить…","title.zoom_in":"Приблизить","title.zoom_out":"Отдалить","title.zoom_fit":"Вписать всё","title.add_device":"Добавить устройство на план","title.show_all":"Показать скрытые и деактивированные устройства служебными маркерами (только в этой вкладке)","title.markup":"Разметка комнат: сетка, линии, контуры","title.configure_space":"Настроить пространство","title.add_space":"Добавить пространство","title.markup_add":"Стены: рисуйте непрерывную цепочку; Shift фиксирует 45°. Смена инструмента или выход завершает её как независимые стены","title.markup_merge":"Объединить: клик по одной комнате, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить комнату: кликните внутри комнаты и подтвердите удаление","title.need_plan":"Загрузите подложку (план этажа)","markup.add":"Стены","markup.merge":"Объединить","markup.split":"Разделить","markup.resize":"Размер","title.markup_resize":"Изменение размера комнат: тяните доступную ручку горизонтальной или вертикальной стены","markup.hint_resize":"тяните доступную ручку стены · стена упирается в первую небезопасную позицию · Esc — отмена · Ctrl+Z — отмена шага","resize.disabled.diagonal":"Стена расположена под углом: Resize поддерживает только горизонтальные и вертикальные стены","resize.disabled.side-angle":"Примыкающая стена расположена под углом: для Resize обе примыкающие стены должны образовывать прямой угол","resize.disabled.duplicate-physical-wall":"Стену перекрывает отдельная перегородка, незавершённый контур или колонна; удалите или переместите её перед изменением размера","resize.disabled.partial-shared":"Соседняя комната использует только часть этой стены, поэтому её нельзя безопасно двигать как одну общую стену","resize.disabled.unequal-shared":"У соседней комнаты другие конечные точки или длина стены, поэтому стены нельзя безопасно двигать вместе","resize.disabled.multiple-rooms":"Перемещение затронуло бы больше двух комнат","resize.disabled.thickness-conflict":"Толщину стены нельзя безопасно сохранить","resize.disabled.opening-conflict":"Проём на этой стене нельзя безопасно переместить","resize.disabled.invalid-geometry":"Эту стену нельзя переместить без изменения структуры плана","resize.limit_stopped":"Стена остановлена: дальше нарушается ограничение стыков","resize.commit_failed":"Изменение размера отменено: итоговый план не прошёл проверку геометрии","resize.preview_failed":"Стена остановлена в последней безопасной позиции: геометрию плана нельзя сохранить без потерь","markup.opening":"Проём","markup.delete_room":"Удалить комнату","history.undo":"Отменить","history.redo":"Повторить","history.undo_named":"Отменить: {name} (Ctrl+Z)","history.redo_named":"Повторить: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Нет операций для отмены","history.redo_empty":"Нет операций для повтора","history.undone":"Отменено: {name}","history.redone":"Повторено: {name}","history.add_room":"Создание комнаты","history.split_room":"Разделение комнаты","history.merge_rooms":"Объединение комнат","history.resize_room":"Изменение размера комнаты","history.wall_thickness":"Изменение толщины стены","history.add_opening":"Добавление дверного или оконного проёма","history.edit_opening":"Изменение проёма","history.move_opening":"Перемещение проёма","history.delete_opening":"Удаление проёма","history.delete_room":"Удаление комнаты","history.delete_room_keep_walls":"Удаление комнаты с сохранением стен","history.delete_room_with_walls":"Удаление комнаты и её стен","history.decor_add":"Добавление объекта декора","history.decor_edit":"Изменение объекта декора","history.decor_move":"Перемещение объекта декора","history.decor_transform":"Трансформация объекта декора","history.decor_delete":"Удаление объекта декора","history.backdrop_transform":"Трансформация подложки плана","title.markup_opening":"Проёмы: клик по стене — добавить, клик по проёму — редактировать","opening.new":"Новый проём","opening.edit":"Проём","opening.door":"Дверь","opening.window":"Окно","opening.gate":"Ворота","opening.passage":"Открытый проём","opening.passage_binding_warning":"При сохранении датчик открытия и замок будут удалены.","opening.invalid_passage_fields":"У открытого проёма на плане «{room}» есть недопустимые параметры: {fields}.","opening.type_label":"Тип","opening.length_label":"Длина, см","opening.contact_label":"Датчик открытия","opening.lock_label":"Замок","opening.none":"— нет —","opening.search_ph":"Поиск: имя или entity_id…","opening.invert":"Инвертировать открыто/закрыто","opening.flip_h":"Петли с другой стороны","opening.flip_v":"Открывается в другую сторону","opening.open":"Открыто","opening.closed":"Закрыто","opening.locked":"Заперто","opening.unlocked":"Не заперто","opening.state_unknown":"недоступно","opening.no_entities":"Датчики не привязаны — статичный символ на плане.","toast.opening_no_wall":"Кликните рядом со стеной комнаты или независимой стеной","opening.host_partition":"Независимая стена","opening.partition_orphan":"Независимая стена этого проёма больше не существует","opening.partition_jamb_margin":"Оставьте от края проёма до торца стены минимум {distance}","opening.rebind_partition":"Привязать к другой независимой стене","confirm.delete_partition_openings_title":"Удалить стену и проёмы?","confirm.delete_partition_openings_body":"В стене есть проёмы: {count}. Они будут удалены вместе со стеной.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Удалить","markup.hint_points":"точек: {n} · Shift — шаг 45° · Esc — завершить цепочку · Ctrl+Z — убрать точку · при замыкании будет предложена комната","markup.hint_start":"кликните точку сетки, чтобы начать цепочку стен","tip.lqi":"средний сигнал zigbee:","tip.area":"площадь: {value}","info.device_header":"Устройство на плане","info.model":"Модель","info.state":"Состояние","info.link":"Ссылка","info.manuals":"Инструкции","info.none":"Нет дополнительной информации","marker.new_device":"Новое устройство","marker.name_label":"Имя (отображается на плане)","marker.name_ph":"Название","marker.binding_label":"Привязка к устройству HA","marker.binding_disabled":"деактивировано в Home Assistant","marker.virtual_option":"Виртуальное устройство (без привязки)","marker.search_ph":"Поиск устройства / группы…","marker.nothing_found":"ничего не найдено","marker.room_label":"Комната","marker.room_override":" (переопределить размещение)","marker.room_choose":"— выберите комнату —","marker.room_auto":"— по зоне устройства (авто) —","marker.icon_label":"Иконка","marker.icon_ph":"mdi:… (пусто = авто)","marker.display_label":"Отображение","display.badge":"Значок + состояние","display.icon_ripple":"Значок + состояние и активность","display.static_icon":"Всегда статичный значок","marker.display_hint":"Значок + состояние меняет подложку при работе, открытии, тревоге и недоступности. Значок + состояние и активность дополнительно показывает короткую пульсацию для событий и постоянную — для работы, движения или присутствия. Значение + состояние заменяет значок одним однозначным значением HA. Статичный значок не реагирует на состояния устройства.","marker.display_hint_badge":"Значок и динамическая подложка показывают состояние устройства без обычной пульсации активности. Красная тревога сохраняется.","marker.display_hint_icon":"Значок и динамическая подложка показывают состояние устройства без обычной пульсации активности. Красная тревога сохраняется.","marker.display_hint_icon_ripple":"Значок, динамическая подложка и пульсация: короткая — для событий, постоянная — для работы, движения или присутствия. Красная тревога имеет отдельный приоритет.","marker.display_hint_value":"Значок заменяется одним однозначным значением Home Assistant; подложка продолжает показывать состояние. Красная тревога сохраняется.","marker.display_hint_static_icon":"Подложка в цветах темы и значок всегда остаются одинаковыми. Состояния, активность, недоступность и тревоги не меняют отображение.","marker.static_alarm_warning":"Это устройство может сообщать о тревогах. Статичный режим скрывает визуальную тревогу маркера.","marker.preview.title":"Предпросмотр отображения","marker.preview.actual":"Сейчас","marker.preview.example":"Пример","marker.preview.integration":"Интеграция","marker.preview.source":"Источник отображения","marker.preview.current_state":"Текущее состояние","marker.preview.result":"На плане","marker.preview.details":"Подробнее об источниках","marker.preview.select_source":"Выберите устройство или сущность Home Assistant, чтобы увидеть фактическое отображение.","marker.preview.unknown_provider":"Интеграция неизвестна","marker.preview.virtual_provider":"House Plan · виртуальное устройство","marker.preview.no_source":"Нет активного источника","marker.preview.no_state":"Нет текущего состояния","marker.preview.mixed_states":"Несколько разных состояний","marker.preview.multiple_sources":"Источников: {n}","marker.preview.more_sources":"ещё {n}","marker.preview.scaled":"Предпросмотр уменьшен до {n}%, чтобы поместиться. Сохранённый размер не изменится.","marker.preview.demo_activity":"Показать пример активности","marker.preview.demo_notice":"Временный пример активности; фактическое состояние устройства не меняется.","marker.preview.demo_short":"Показать краткую активность","marker.preview.demo_continuous":"Показать постоянную активность","marker.preview.stop_continuous":"Остановить постоянную активность","marker.preview.demo_short_notice":"Пример краткой активности; фактическое состояние устройства не меняется.","marker.preview.demo_continuous_notice":"Пример постоянной активности; фактическое состояние устройства не меняется.","marker.preview.demo_already_visible":"Текущее состояние уже показывает реальную активность или тревогу.","marker.preview.reduced_motion":"В системе включено уменьшение движения, поэтому обычная активность показана точкой.","marker.preview.reason.neutral":"Нейтральная тёмная подложка","marker.preview.reason.working":"Жёлтая подложка: устройство сейчас работает","marker.preview.reason.working_activity":"Жёлтая подложка и эффект активности: устройство сейчас работает","marker.preview.reason.open":"Оранжевая подложка: физически открыто или разблокировано","marker.preview.reason.cover_icon_state":"Нейтральная подложка; состояние и значок определяет сущность шторы или заслонки","marker.preview.reason.presence":"Эффект активности, пока обнаружено присутствие","marker.preview.reason.event":"Короткий эффект активности после события","marker.preview.reason.transition":"Эффект активности во время движения или смены состояния","marker.preview.reason.media_neutral":"Медиаустройство остаётся на нейтральной тёмной подложке, пока доступно","marker.preview.reason.unavailable":"Приглушённая нейтральная подложка: отключено или недоступно","marker.preview.reason.alarm":"Красная тревожная подложка; тревога отображается всегда","marker.preview.reason.live_states_disabled":"Оформление по живым состояниям отключено в настройках карточки","marker.preview.reason.value_no_state":"Нет подходящего значения состояния — вместо него показан значок","marker.preview.reason.value_ambiguous_sources":"Подходит несколько источников значения — вместо него показан значок","marker.preview.reason.value_non_scalar":"Источник вернул не простое значение — вместо него показан значок","marker.preview.reason.value_virtual":"У виртуального устройства нет значения HA — вместо него показан значок","marker.preview.reason.vacuum_live_plan_only":"Живая позиция и след пылесоса доступны только на полном плане","marker.preview.reason.hidden_design_preview":"Устройство скрыто на плане, но его оформление видно в предпросмотре","marker.preview.reason.composite_power_source":"Состояние берётся из сущности питания устройства; вспомогательные переключатели не учитываются","marker.preview.reason.activity_display_disabled":"Выбранный режим отображения не показывает обычные эффекты активности","marker.preview.reason.ha_disabled":"Привязка отключена в Home Assistant, поэтому устройство будет скрыто на плане","marker.preview.reason.orphaned":"Сохранённая привязка Home Assistant больше не найдена","marker.preview.reason.static_icon":"Статичный режим: состояние устройства не меняет значок","marker.activity_color":"Цвет пульсации активности","marker.ripple_size":"Размер пульсации активности","marker.activity_alarm_note":"Цвет и размер относятся только к обычной активности и не влияют на красную тревогу.","marker.pulse_a11y_alarm":"Тревога","marker.pulse_a11y_event":"Недавнее событие","marker.pulse_a11y_presence":"Обнаружено присутствие","marker.pulse_a11y_transition":"Изменение состояния","marker.pulse_a11y_running":"Работает","marker.state_a11y_neutral":"Обычное состояние","marker.state_a11y_working":"Работает","marker.state_a11y_open":"Открыто","marker.state_a11y_locked":"Заблокировано","marker.state_a11y_unlocked":"Разблокировано","marker.state_a11y_alarm":"Тревога","marker.state_a11y_unavailable":"Недоступно","marker.lqi_a11y_low":"LQI {value}, слабый сигнал","marker.lqi_a11y_mid":"LQI {value}, средний сигнал","marker.lqi_a11y_high":"LQI {value}, сильный сигнал","marker.size_label":"Размер / поворот значка","marker.angle_label":"Поворот","marker.model_label":"Модель","marker.model_ph":"напр. Aqara T&H","marker.link_label":"Ссылка","marker.desc_label":"Описание","marker.desc_ph":"Заметки, характеристики…","marker.manuals_label":"Инструкции (PDF и т.п.)","marker.sub_device":"устройство","marker.sub_z2m_group":" · Z2M-группа","marker.sub_group":"группа","marker.sub_helper":"хелпер","space.new":"Новое пространство","space.header":"Пространство","space.title_label":"Название","space.title_ph":"Например: Гараж","space.plan_label":"Подложка (план)","space.no_plan":"нет подложки","space.plan_alt":"план","room.new":"Новая комната","room.name_label":"Отображаемое имя","room.name_ph":"Например: Терраса","room.area_label":"Зона Home Assistant (свободные)","room.no_area_option":"— без зоны —","room.default_name":"Комната","device.unnamed":"без имени","device.light_group":"группа света","device.fallback":"устройство","device.virtual":"виртуальное устройство","confirm.delete_room":"Удалить комнату «{name}»?","confirm.delete_room_title":"Удалить комнату «{name}»?","confirm.delete_room_body":"Выберите, нужно ли оставить физические стены, принадлежащие только этой комнате. Общие с другой комнатой стены сохраняются всегда.","confirm.merge_rooms":"Слить комнаты «{a}» и «{b}»?","confirm.remove_marker":"Удалить «{name}» с плана? Устройство исчезнет полностью и перестанет участвовать в данных плана. Позже его можно будет добавить заново.","confirm.erase_decor":"Стереть объект «{kind}»? Действие можно отменить из истории редактора.","confirm.delete_space":"Удалить пространство «{title}» со всеми комнатами и разметкой?","space.delete_blocked":"Это пространство всё ещё используется устройствами: {n}. Сначала перенесите их в другое пространство или удалите.","toast.pos_save_failed":"Не удалось сохранить позицию: {err}","toast.no_entity":"У устройства нет подходящей сущности","toast.ha_disabled_action":"Деактивированный объект Home Assistant нельзя использовать на плане.","toast.ha_disabled_show_device":"Деактивированное в Home Assistant устройство нельзя показать на плане. Сначала активируйте его в Home Assistant.","toast.ha_disabled_show_entity":"Деактивированную в Home Assistant сущность нельзя показать на плане. Сначала активируйте её в Home Assistant.","toast.ha_disabled_add":"Деактивированный объект Home Assistant нельзя добавить на план. Сначала активируйте его в Home Assistant.","toast.ha_binding_unverified":"Статус объекта не удалось подтвердить по реестру Home Assistant. Отображение и действия временно недоступны.","toast.markup_needs_server":"Разметка доступна после переноса конфига на сервер","toast.space_order_changed":"Порядок изменён. Если где-то этаж карточки задан номером, проверьте такие панели.","toast.conflict":"Конфиг изменён в другом окне — данные обновлены, повторите последнее действие","toast.cfg_save_failed":"Не удалось сохранить конфиг: {err}","toast.room_overlap":"Контур накладывается на комнату «{name}» — комнаты не должны накладываться","toast.contour_min_edges":"Чтобы замкнуть контур, сначала нарисуйте минимум две грани","toast.contour_cannot_close":"Контур нельзя замкнуть: он вырожден или пересекает сам себя","toast.merge_not_adjacent":"Объединять можно только комнаты с общей стеной","toast.rooms_merged":"Комнаты объединены в «{name}»","toast.split_pick_wall":"Начните разрез на стене комнаты","toast.split_bad_cut":"Разрез — от стены до стены внутри комнаты, без пересечения стен и самого себя","merge.header":"Объединение комнат","merge.hint":"У объединённой комнаты одно имя и одна зона. Вторая зона освобождается — её устройства уйдут с плана, пока их не заберёт другая комната.","merge.keep":"Оставить","merge.no_area":"без зоны","room.split_header":"Новая комната после разделения","toast.room_saved":"Комната сохранена ({n}). Устройств добавлено: {added}. Обведите следующую или выйдите из разметки.","toast.room_saved_no_area":"Комната сохранена ({n}, без зоны). Обведите следующую или выйдите из разметки.","toast.marker_needs_server":"Редактирование устройств доступно после переноса конфига на сервер","toast.virtual_name_required":"Укажите имя виртуального устройства","toast.marker_saved":"Устройство сохранено","toast.marker_removed":"Устройство удалено с плана","toast.integration_missing":"Интеграция House Plan не установлена — управление недоступно","toast.plan_formats":"Поддерживаются SVG, PNG, JPG, WebP","toast.plan_required":"Загрузите подложку — план этажа обязателен","toast.space_added_onboard":"Пространство добавлено. Обведите комнаты: кликайте по точкам сетки и замкните контур.","toast.space_added":"Пространство добавлено","toast.space_saved":"Пространство сохранено","toast.space_deleted":"Пространство удалено","toast.delete_failed":"Ошибка удаления: {err}","toast.error":"Ошибка: {err}","toast.toggle_target_unavailable":"Цель «{name}» недоступна — действие не выполнено","toast.toggle_targets_unavailable":"Цели недоступны: {names}. Действие не выполнено","toast.file_failed":"Файл «{name}» не загружен: {err}","toast.files_attached":"Прикреплено файлов: {n}","err.unknown":"неизвестная ошибка","err.code":"код {code}","err.too_large":"файл больше {mb} МБ","err.bad_ext":"недопустимый тип (нужен PDF/изображение)","err.unauthorized":"нужны права администратора","editor.title":"Заголовок","editor.default_floor":"Стартовое пространство","editor.default_floor_missing":"Стартовое пространство «{id}» больше не существует. Выберите другое.","editor.floor":"Закреплённое пространство","editor.floor_none":"— не закреплять —","editor.floor_index":"YAML-индекс {index} (сохранён)","fixed_floor.loading":"Загружаем закреплённое пространство…","fixed_floor.invalid_title":"Закреплённое пространство недоступно","fixed_floor.invalid_body":"Проверьте floor в конфигурации карточки. Настроенное значение: {value}","editor.icon_size":"Размер иконок, % ширины плана","editor.show_temperature":"Показывать температуру","editor.live_states":"Живые состояния (вкл/выкл, открыто…)","editor.show_signal":"Показывать сигнал zigbee (LQI)","editor.language":"Язык интерфейса","editor.lang_auto":"Авто (профиль HA)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Правила иконок: какая MDI-иконка достаётся устройству по имени","rules.title":"Правила иконок","rules.hint":"Правила проверяются сверху вниз по строке «имя устройства + модель» (regex без учёта регистра); срабатывает первое совпадение. Если ничего не подошло — решает device class сущности, затем — иконка-заглушка.","rules.pattern_ph":"regex, напр. розетк|plug","rules.icon_ph":"mdi:power-socket-de","rules.add":"Добавить правило","rules.reset":"Сбросить к умолчаниям","rules.test_ph":"Проверьте имя устройства…","rules.invalid":"некорректный regex","rules.saved":"Правила иконок сохранены","btn.up":"Вверх","btn.down":"Вниз","tap.info":"Карточка устройства","tap.more_info":"Диалог HA (more-info)","tap.toggle":"Переключить состояние","marker.tap_label":"Действие по нажатию для этого устройства","tap.toggle_note":"Под селектором показано, какое состояние изменится. Защищённые устройства не переключаются с плана.","import.title":"Создать пространства из этажей HA","import.hint":"Home Assistant уже знает эти этажи. Отметьте, какие превратить в пространства плана — далее для каждого попросим картинку плана. Комнаты затем обводятся вручную по плану.","import.start":"Создать: {n}","import.manual":"Начать с нуля","import.progress":"Этаж {i} из {n}","import.done":"Пространства созданы. Обведите комнаты: кликайте по точкам сетки и замкните контур.","btn.skip":"Пропустить","space.scale_label":"Масштаб (размер клетки сетки)","space.scale_unit":"см на клетку","space.scale_unit_imperial":"дюйм на клетку","space.display_section":"Отображение","space.show_borders":"Всегда отображать границы комнат","space.zero_wall_style":"Стены нулевой толщины","space.zero_wall_dashed":"Пунктирные","space.zero_wall_solid":"Сплошные","space.zero_wall_help":"Пунктирные стены пропускают свет. Сплошные стены блокируют свет, даже при нулевой толщине.","space.show_names":"Отображать названия комнат (перетаскиваются)","space.room_color":"Цвет границ и названий","space.opacity":"Прозрачность","space.fill_label":"Заливка комнат","fill.none":"Нет","fill.lqi":"По силе зигби-сигнала","fill.light":"По освещению","fill.custom":"Свой цвет","space.custom_fill":"Цвет заливки","space.source_file":"У меня есть картинка плана","space.source_draw":"Нет подложки — нарисую комнаты вручную","space.orientation":"Холст","orient.landscape":"Альбомный","orient.portrait":"Портретный","orient.square":"Квадрат","fill.temp":"По температуре","space.temp_min":"Комфорт от","space.temp_max":"до","tip.temp_avg":"средняя температура:","tip.hum_avg":"средняя влажность:","space_card.button":"Перейти к пространству","space_card.not_found":"Пространство «{id}» не найдено","space_card.loading":"Загрузка…","continuity.restore_plan":"Восстанавливаем план…","continuity.restore_connection":"Восстанавливаем подключение к устройствам…","continuity.retry":"Повторить","editor.space":"Пространство","editor.show_button":"Показывать кнопку","editor.button_label":"Текст кнопки","editor.button_target":"Путь дашборда (куда вести)","marker.sub_entity":"сущность","title.general_settings":"Общие настройки","gs.title":"Общие настройки","gs.hint":"Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.","gs.light_group":"Заливка: освещение","gs.light_on":"Свет включён","gs.light_off":"Весь свет выключен","gs.temp_group":"Заливка: температура","gs.temp_cold":"Холодно","gs.temp_ok":"Комфорт","gs.temp_hot":"Жарко","gs.lqi_group":"Заливка: зигби-сигнал","gs.lqi_low":"Слабый сигнал","gs.lqi_high":"Сильный сигнал","gs.reset":"Сбросить к умолчаниям","gs.saved":"Общие настройки сохранены","space.show_lqi":"Показывать зигби-сигнал (LQI) у устройств","space.hide_decor":"Скрыть декоративный слой","space.hide_decor_tip":"Линии, фигуры, надписи и мебель остаются на месте — их видно в редакторе подложки, но не на плане.","space.hide_openings":"Скрыть проёмы","space.hide_openings_tip":"Двери, окна и ворота не рисуются, но продолжают работать: свет проходит, солнце светит в окна, датчики открытия срабатывают. В редакторе плана проёмы видно всегда.","gs.light_none":"Нет источников света","mode.plan":"Редактор плана","mode.devices":"Редактор устройств","display.value":"Значение + состояние","marker.subarea":"без зоны, вручную","device.new":"Новое устройство — откройте его редактор, чтобы снять отметку","opening.unlock_action":"Открыть замок","opening.lock_action":"Закрыть замок","opening.lock_pending":"Выполняется…","title.close_editor":"Закрыть редактор (вернуться к просмотру)","devbar.add":"Добавить","devbar.show_all":"Скрытые и деактивированные","devbar.rules":"Правила иконок","device_inbox.button":"Устройства","device_inbox.title":"Устройства на плане","device_inbox.search":"Поиск по устройствам, сущностям и интеграциям…","device_inbox.add_virtual":"Добавить виртуальное устройство","device_inbox.tab_on_plan":"На плане","device_inbox.tab_available":"Доступны","device_inbox.tab_hidden":"Скрытые","device_inbox.tab_readd":"Доступны снова","device_inbox.only_new":"Только новые","device_inbox.show_entities":"Показывать сущности","device_inbox.show_hidden":"Показывать скрытые на плане","device_inbox.new":"Новое","device_inbox.find":"Найти на плане","device_inbox.edit":"Настроить","device_inbox.hide":"Скрыть","device_inbox.show":"Показать","device_inbox.add":"Добавить","device_inbox.readd":"Добавить заново","device_inbox.hide_available":"Скрыть из списка","device_inbox.show_more":"Показать ещё","device_inbox.more_actions":"Другие действия","device_inbox.find_hidden_hint":"Сначала включите «Показывать скрытые на плане»","device_inbox.show_disabled":"Перед показом активируйте эту привязку в Home Assistant","device_inbox.empty_on_plan":"На плане пока нет подходящих устройств.","device_inbox.empty_available":"Нет доступных устройств для добавления.","device_inbox.empty_hidden":"Скрытых устройств нет.","device_inbox.empty_readd":"Нет удалённых устройств, доступных для повторного добавления.","device_inbox.reason_visible_auto":"Найдено автоматически","device_inbox.reason_visible_explicit":"Добавлено явно","device_inbox.reason_manual_hidden":"Скрыто пользователем","device_inbox.reason_automatic_hidden":"Скрыто автоматически","device_inbox.reason_service_entry":"Служебная запись реестра","device_inbox.reason_excluded_integration":"Интеграция исключена фильтрами устройств","device_inbox.reason_excluded_domain":"Непространственный тип сущности","device_inbox.reason_grouped_light":"Представлено группой света комнаты","device_inbox.reason_represented_by_parent":"Представлено родительским устройством","device_inbox.reason_removed":"Ранее удалено с плана","device_inbox.reason_available":"Можно добавить на план","device_inbox.reason_no_bound_room":"Зона HA не связана с комнатой плана","device_inbox.status_ha_disabled":"Отключено в Home Assistant","device_inbox.status_orphaned":"Привязка больше не найдена в Home Assistant","device_inbox.status_unverified":"Реестр Home Assistant временно недоступен","device_inbox.saved":"Список устройств обновлён","space.roomcard_section":"В карточке комнаты:","space.label_temp":"Температура","space.label_hum":"Влажность","space.label_lqi":"Средний Zigbee-сигнал","space.label_light":"Свет вкл/выкл","roomcard.light_on":"Вкл","roomcard.light_off":"Выкл","roomcard.light_partial":"{on} из {total}","toast.split_pick_inside":"Промежуточные точки разреза — внутри комнаты","mode.decor":"Редактор подложки","decor.select":"Выбрать","decor.line":"Линия","decor.rect":"Прямоугольник","decor.ellipse":"Овал","decor.text":"Надпись","decor.erase":"Стереть","decor.erase_confirm_title":"Стереть объект?","decor.color":"Цвет","decor.width":"Толщина линии","decor.line_style":"Стиль линии","decor.line_style_solid":"Сплошная","decor.line_style_dashed":"Пунктирная","decor.fill":"Заливка","decor.fill_color":"Цвет заливки","decor.length":"Длина","decor.size":"Размер","decor.angle":"Угол поворота, °","decor.text_size":"Размер текста","decor.backdrop_properties":"Свойства картинки-подложки","decor.text_title":"Надпись","decor.object_title":"Редактирование: {kind}","decor.text_label":"Текст","decor.live_group":"Вставить переменную HA","decor.live_entity":"Сущность","decor.live_entity_ph":"выберите сущность","decor.live_attr":"Значение","decor.live_attr_ph":"выберите состояние или атрибут","decor.live_state":"Состояние","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — перемещение; угол — размер; верхнюю ручку — поворот. Shift меняет пропорции или снимает шаг угла.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","marker.icon_pin_auto":"Закрепить","mode.plan_tip":"Редактор плана — геометрия дома: рисование и объединение/разделение комнат, привязка к зонам HA, двери, окна и ворота, карточки комнат, масштаб","mode.devices_tip":"Редактор устройств — всё про значки: перетаскивание, клик — настройка привязки/иконки/отображения, виртуальные устройства, правила иконок","mode.decor_tip":"Редактор подложки — чисто визуальный декор под планом: линии, прямоугольники, овалы и надписи, не реагирующие на клики","space.glow_enabled":"Свечение источников света","gs.glow_group":"Свечение источников света","gs.glow_base":"Темнота дома","gs.glow_light":"Цвет света по умолчанию / интенсивность","gs.wall_group":"Стены","gs.wall_fill":"Цвет заливки стен","gs.glow_radius":"Радиус свечения","gs.unit_m":"м","gs.unit_ft":"фут","marker.controls_label":"Управляет другими источниками света","marker.controls_hint":"При действии «Переключить» клик разом переключает все добавленные здесь источники. Собственная сущность маркера управляется напрямую и сюда не добавляется; чтобы считать её светом, включите флаг ниже.","marker.controls_filter":"Поиск ламп и выключателей…","info.controls":"Управляет","marker.glow_radius_label":"Радиус свечения","marker.glow_radius.help":"Задаёт радиус свечения в метрах или футах; пустое значение использует радиус из общих настроек.","marker.glow_radius.help.aria":"Подсказка: радиус свечения","markup.wallthick":"Толщина","markup.select":"Выбрать","title.markup_wallthick":"Толщина — клик по стене задаёт толщину от 0 до 100 см.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены (0–100 см). Общие стены сохраняют существующее значение.","room.queue_progress":"Комната {current} из {total}","toast.wall_rooms_saved":"Создано комнат: {n}","toast.wall_chain_saved":"Цепочка стен сохранена","toast.wallthick_pick":"Кликните по стене","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от {min} до {max} {unit}","toast.zero_wall_opening_conflict":"Сначала удалите проём на этом участке стены.","toast.zero_wall_ambiguous":"Не удалось однозначно выбрать участок стены. Уточните геометрию узла.","toast.zero_wall_migration_blocked":"Пространство не преобразовано: {reason}. Данные не изменены.","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","toast.geometry_unsafe":"Изменение отменено: геометрию стен нельзя безопасно построить.","junction.limit_angle":"Слишком острый угол между стенами: {actual}°, минимум {limit}°.","junction.limit_valence":"Слишком много стен в одном узле: {actual}, максимум {limit}.","junction.limit_length":"Слишком короткий участок стены: {actual} см, минимум {limit} см.","junction.limit_distance":"Стены и узлы слишком близко: {actual} см, минимум {limit} см.","junction.limit_clearance":"Внутри комнаты не остаётся места: {actual} см², минимум {limit} см².","toast.wall_model_migration_blocked":"Не удалось обновить модель стен: {reason}. План не изменён. Запустите «Оптимизировать планы»; если ошибка повторится, исправьте конфликтующую геометрию стен.","toast.wall_model_client_outdated":"Обновите карточку и перезагрузите страницу перед редактированием плана.","wall_model.reason.invalid-room":"некорректный контур комнаты","wall_model.reason.zero-length":"отрезок стены нулевой длины","wall_model.reason.third-owner":"стена принадлежит более чем двум комнатам","wall_model.reason.duplicate-id":"конфликт идентификаторов стен","wall_model.reason.thickness-conflict":"конфликт значений толщины стены","wall_model.reason.opening-host":"проём нельзя однозначно привязать к стене","toast.delete_room_pick":"Кликните внутри комнаты, которую нужно удалить","toast.plan_snap_ambiguous":"Увеличьте масштаб, чтобы выбрать узел стены","toast.wall_repair_ambiguous":"У контура несколько возможных соединений. Увеличьте масштаб и соедините стены явно.","toast.wall_repair_too_large":"Разрыв больше 2 см. Соедините стены перед созданием комнаты.","toast.wall_repair_changed":"Геометрия стен изменилась. Попробуйте создать комнату ещё раз.","toast.opening_on_zero_wall":"Проёмы на стене нулевой толщины запрещены","marker.from_ha_option":"Выбрать из списка HA","marker.show_entities":"Показывать сущности","marker.show_entities_tip":"Добавляет в список не только устройства, но и все их сущности","marker.pick_ph":"Выберите устройство…","room.open_area":"Открыть зону в HA","view.volumetric":"Объёмный вид","view.flat":"Плоский вид","kiosk.title":"Размеры на этом экране","kiosk.hint":"Хранится только на этом устройстве — у каждого настенного планшета или ТВ свои удобные размеры.","kiosk.icon_scale":"Размер значков устройств","kiosk.font_scale":"Размер текста карточек комнат","editor.kiosk":"Режим настенного устройства (киоск)","editor.cycle":"Автосмена пространств каждые N секунд (киоск, 0 = выкл)","room.settings_title":"Настройки комнаты","room.settings_section":"Настройки комнаты (переопределяют пространство)","room.fill_label":"Заливка в ЭТОЙ комнате","fill.inherit":"Как у пространства","room.custom_fill_space":"Цвет пространства","room.custom_fill_own":"Цвет комнаты","room.temp_src_label":"Источник температуры","room.hum_src_label":"Источник влажности","room.src_average":"Средняя по датчикам комнаты (по умолчанию)","room.src_pick":"Конкретное устройство или сущность HA","room.src_ph":"Выберите источник…","toast.room_updated":"Комната обновлена","space.card_font":"Размер шрифта карточек комнат (всё пространство)","room.sizes_section":"Размеры шрифтов","room.name_scale":"Размер названия","room.label_scale":"Размер подписей","preview.room_name":"Гостиная","toast.cfg_reload_failed":"Не удалось перечитать план с сервера: {err}","room.settings_short":"Настройки комнаты","room.unnamed":"Комната без имени","marker.use_climate_temp":"Учитывать температуру устройства в комнате","marker.use_climate_temp_tip":"Добавляет current_temperature кондиционера или термостата в среднюю температуру комнаты. Внешний бейдж со значением настраивается отдельно ниже.","marker.value_badge_title":"Бейдж со значением","marker.value_badge.help":"Показывает одно выбранное значение рядом со значком. Не влияет на комнатные показатели, свет, Glow и действие по нажатию.","marker.value_badge.help.aria":"Подсказка: бейдж со значением устройства","marker.value_badge_enabled":"Отображать бейдж со значением","marker.value_badge_source":"Значение","marker.value_badge_source.help":"Выберите конкретное состояние, поддерживаемый атрибут или производное значение этого устройства.","marker.value_badge_source.help.aria":"Подсказка: источник значения бейджа","marker.value_badge_position":"Расположение","marker.value_badge_position.help":"Выбранная сторона сохраняется постоянно и не переворачивается автоматически у края плана.","marker.value_badge_position.help.aria":"Подсказка: расположение бейджа","marker.value_badge_right":"Справа","marker.value_badge_bottom":"Снизу","marker.value_badge_left":"Слева","marker.value_badge_top":"Сверху","marker.value_badge_empty":"У этого устройства нет доступных значений","marker.value_badge_static":"Статичный значок не показывает живые значения. Настройка сохранится.","marker.value_badge_missing":"Источник недоступен","marker.value_badge_missing_hint":"Сохранённый источник сейчас недоступен. Можно сохранить его, отключить бейдж или выбрать другой.","marker.value_badge_duplicate":"То же значение уже показано внутри значка.","marker.value_badge_state":"Состояние · {name}","marker.value_badge_marker_state":"Состояние источника света · {name}","marker.value_badge_lqi":"Качество Zigbee-сигнала (среднее)","marker.value_badge_attr_current_temperature":"Текущая температура · {name}","marker.value_badge_attr_temperature":"Целевая температура · {name}","marker.value_badge_attr_current_humidity":"Текущая влажность · {name}","marker.value_badge_attr_humidity":"Влажность · {name}","marker.value_badge_attr_current_position":"Положение · {name}","marker.value_badge_attr_percentage":"Скорость · {name}","marker.value_badge_attr_brightness":"Яркость · {name}","marker.value_badge_attr_volume_level":"Громкость · {name}","marker.value_badge_attr_battery_level":"Заряд батареи · {name}","marker.value_badge_attr_fan_speed":"Скорость вентилятора · {name}","marker.light_role_label":"Является источником света","marker.light_role.help":"Определяет, является ли сам маркер пространственным источником света. Привязанные выше лампы остаются независимыми источниками для комнаты.","marker.light_role.help.aria":"Подсказка: является ли устройство источником света","marker.light_role_auto_yes":"Автоматически (источник света)","marker.light_role_auto_no":"Автоматически (не источник света)","marker.light_role_always":"Всегда источник света","marker.light_role_never":"Никогда не источник света","marker.light_entity_label":"Ведущая сущность света","marker.light_entity.help":"Для составного устройства выберите сущность, состояние, цвет и яркость которой представляют этот источник на плане. Автоматический вариант сохраняет совместимость со старыми планами.","marker.light_entity.help.aria":"Подсказка: ведущая сущность света","marker.light_entity_auto":"Автоматически ({entity})","marker.light_entity_none":"нет управляемой сущности","marker.light_entity_missing":"Сохранённая сущность {entity} недоступна. Временно используется {fallback}; выбор восстановится, если сущность вернётся.","marker.toggle_entity_label":"Переключаемая сущность","marker.toggle_entity.help":"Для составного устройства выберите собственную лампу или переключатель, которой управляет нажатие. Автоматический вариант сохраняет прежние правила цели и не зависит от ведущей сущности света.","marker.toggle_entity.help.aria":"Подсказка: переключаемая сущность","marker.toggle_entity_auto":"Автоматически ({entity})","marker.toggle_entity_none":"нет собственной управляемой сущности","marker.toggle_entity_missing":"Сохранённой сущности {entity} больше нет среди выбираемых каналов этого маркера. Временно используется {fallback}; выбор восстановится, если сущность вернётся.","marker.glow_color_label":"Цвет и яркость свечения","marker.glow_mode.help":"Используйте данные источника, задайте только цвет или зафиксируйте цвет и яркость. Минимум — 1%; чтобы отключить источник, выберите «Никогда».","marker.glow_mode.help.aria":"Подсказка: цвет и яркость свечения","marker.glow_mode_auto":"Из источника","marker.glow_mode_color":"Задать цвет","marker.glow_mode_fixed":"Задать цвет и яркость","marker.glow_color":"Цвет свечения","marker.glow_brightness":"Яркость","marker.glow_disabled_never":"Настройки свечения недоступны: маркер явно исключён из источников света.","marker.glow_disabled_auto":"В автоматическом режиме у этого маркера не найден пространственный источник света.","marker.glow_disabled_no_entity":"Для пространственного источника нет активной управляемой сущности Home Assistant.","marker.glow_passive_hint":"У источника нет собственных данных Home Assistant. Задайте цвет и яркость вручную; радиус остаётся доступным.","marker.control_broken":"Сохранённый источник отсутствует или больше не отмечен как источник света","marker.control_missing_label":"Отсутствующий источник на плане","marker.control_passive":"пассивный источник","toast.marker_control_cycle":"Такая связь создаст циклическую цепочку управления светом.","toast.marker_binding_required":"Сначала выберите устройство Home Assistant, затем добавьте управляемый источник света.","confirm.unlock":"Открыть замок «{name}»?","toast.files_migrate_failed":"Не удалось перенести вложения к новой привязке, ссылки остались на старые файлы: {err}","space.pick_saved":"Уже загруженные","space.pick_saved_hint":"Планы, сохранённые на сервере, включая отцеплённые ранее","space.no_saved":"На сервере пока нет сохранённых планов.","space.loading":"Загрузка…","space.used_by":"используется: {list}","space.in_use":"План используется пространством — сначала отцепите его","btn.use":"Выбрать","confirm.delete_plan":"Удалить файл плана «{name}» с сервера? Действие необратимо.","toast.plans_list_failed":"Не удалось получить список планов: {err}","toast.plan_delete_failed":"Не удалось удалить план: {err}","marker.hide":"Скрыть","marker.hide_tip":"Устройство исчезнет с плана после сохранения, но продолжит участвовать в расчёте сигнала комнаты. Вернуть его можно через каталог «Устройства» в редакторе устройств.","marker.show":"Показать","marker.show_tip":"Устройство снова появится на плане после сохранения.","marker.hidden_ghost":"Устройство скрыто пользователем","marker.ha_disabled_device":"Устройство деактивировано в Home Assistant и скрыто с плана.","marker.ha_disabled_entity":"Сущность деактивирована в Home Assistant и скрыта с плана.","marker.ha_disabled_all_entities":"У устройства нет активных сущностей Home Assistant, поэтому оно скрыто с плана.","marker.ha_registry_limited":"Полный реестр Home Assistant недоступен этому пользователю. Неподтверждённый объект временно нельзя показывать или использовать.","marker.delete_tip":"Полностью удалить устройство с плана и из всех расчётов. Позже его можно добавить заново.","tap.run":"Запустить автоматизацию/скрипт/сцену","tap.cover":"Открыть/закрыть (шторы/жалюзи)","marker.run_target_label":"Что запускать","marker.run_search_ph":"Поиск: автоматизация, скрипт или сцена…","marker.run_target_gone":"Цель {id} не найдена — выберите заново","marker.tap_confirm":"Спрашивать подтверждение","marker.tap_confirm_tip":"Перед выполнением показать диалог подтверждения — защита от случайных нажатий.","marker.toggle_hint_single":"Цель: {name} ({id}).","marker.virtual_light_target":"Виртуальный свет: {name}.","marker.virtual_light_current":"Ручное состояние: {state} → {effect}.","marker.virtual_light_state_on":"включён","marker.virtual_light_state_off":"выключен","marker.toggle_hint_group":"Переключатся источники ({count}): {names}.","marker.toggle_hint_current":"Сейчас: {state} → {effect}.","marker.toggle_hint_group_current":"Сейчас включено {on} из {count} → {effect}.","marker.toggle_hint_skipped":"Не будут затронуты ({count}): {targets}.","marker.toggle_effect_turn_on":"включится","marker.toggle_effect_turn_off":"выключится","marker.toggle_effect_open":"откроется","marker.toggle_effect_close":"закроется","marker.toggle_effect_stop":"остановится","marker.toggle_effect_toggle":"состояние переключится","marker.toggle_skip_missing":"не найдено","marker.toggle_skip_ha_disabled":"деактивировано в HA","marker.toggle_skip_unavailable":"недоступно","marker.toggle_skip_unsupported":"переключение не поддерживается","marker.toggle_skip_secure":"заблокировано из соображений безопасности","marker.toggle_none_no_binding":"У устройства нет привязки, состояние переключать нечем. По нажатию ничего не произойдёт.","marker.toggle_none_no_actionable_entity":"У устройства нет состояния, которое можно переключить. По нажатию ничего не произойдёт.","marker.toggle_none_configured_targets_missing":"Настроенные цели сейчас недоступны. Собственная сущность устройства не будет подставлена вместо них.","marker.toggle_none_ha_disabled":"Цель деактивирована в Home Assistant и не может использоваться на плане.","marker.toggle_none_unavailable":"Цель сейчас недоступна. По нажатию ничего не произойдёт.","marker.toggle_none_unsupported":"Для этой сущности Home Assistant не предоставляет безопасного переключения. По нажатию ничего не произойдёт.","marker.toggle_none_secure":"Переключение замков, сигнализации и защитных ворот с плана заблокировано из соображений безопасности.","run.automation":"автоматизация","run.script":"скрипт","run.scene":"сцена","confirm.tap_run":"Запустить «{name}»?","confirm.tap_toggle":"Переключить «{name}»?","confirm.tap_cover":"Открыть/закрыть «{name}»?","confirm.current_state":"Текущее состояние: {state}","confirm.expected_state":"После переключения: {state}","confirm.group_current":"включено {on} из {total}","confirm.group_all_on":"все включены","confirm.group_all_off":"все выключены","confirm.unavailable_targets":"Недоступно: {count}","confirm.expected_by_ha":"Состояние определит Home Assistant","confirm.state_on":"Включено","confirm.state_off":"Выключено","confirm.state_open":"Открыто","confirm.state_closed":"Закрыто","confirm.state_opening":"Открывается","confirm.state_closing":"Закрывается","confirm.state_stopped":"Остановлено","confirm.state_unknown":"Неизвестно","toast.run_started":"Запущено: {name}","toast.run_target_missing":"Цель запуска не найдена — проверьте настройки устройства","toast.run_target_required":"Выберите автоматизацию, скрипт или сцену","toast.tap_target_changed":"Цель действия изменилась. Повторите попытку.","toast.virtual_light_toggle_failed":"Не удалось переключить виртуальный свет: {err}","toast.value_badge_source_required":"Выберите значение для бейджа","btn.run":"Выполнить","vac.section":"Робот-пылесос: живая позиция","vac.autocal":"Настроить автоматически","vac.live":"Живая позиция на плане","vac.trail":"Показывать путь робота","vac.cal_maps":"Откалиброваны карты: {maps}","vac.autocal_no_rooms":"Интеграция не отдаёт список комнат — откройте «Подогнать вручную»","vac.autocal_no_match":"Не совпали имена комнат (нужно ≥3 общих) — откройте «Подогнать вручную»","vac.autocal_done":"Готово: привязка по {rooms} комнатам. Запустите уборку и проверьте","vac.cal_need_pos":"Робот сейчас не отдаёт координаты — запустите уборку и поставьте на паузу","vac.cal_done":"Калибровка сохранена. Запустите уборку и проверьте","vac.cal_cancelled":"Калибровка отменена","vac.fit":"Подогнать вручную","vac.fit_hint":"Перетащите карту робота на место, растяните за уголки","vac.fit_rotate":"Повернуть 90°","vac.fit_mirror":"Отразить","vac.trail_never":"Не показывать никогда","vac.trail_cleaning":"Во время уборки","vac.trail_always":"Показывать всегда","gs.bg_group":"Фон сцены","gs.bg_color":"Цвет фона вокруг плана","gs.bg_default":"Как в теме","gs.bg_theme":"по умолчанию — из темы","gs.bg_mode":"Фон плана","gs.bg_static":"Статичный цвет","gs.bg_daynight":"Следует за Солнцем","gs.bg_daynight_hint":"Фон проходит рассвет, день, закат и ночь по данным sun.sun, а при их недоступности — по локальным часам. Компас для фона не нужен.","gs.sun_group":"Солнце","gs.sun_missing":"Сущность sun.sun не найдена — фон следует локальным часам, а оконные лучи недоступны.","gs.north":"Север на плане","gs.north_ph":"не задан","gs.north_hint":"Север нужен только для оконных лучей (шаг 1°, с Shift 15°); солнечный фон работает без него.","gs.north_clear":"Сбросить","gs.north_letter":"С","gs.sun_rays":"Солнце в окнах","gs.about_group":"О карточке","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · документация и issues","gs.about_telegram":"Чат в Telegram","space.bg_color":"Цвет фона вокруг плана","space.bg_inherit":"Наследовать общий","space.bg_inherited":"наследуется из общих настроек","space.bg_mode":"Фон плана","space.north":"Север на плане (переопределение)","space.north_inherited":"наследуется: {v}","space.sun_rays":"Солнце в окнах","space.sun_inherit":"Наследовать общий","space.sun_on":"Вкл","space.sun_off":"Выкл","canvas.far_objects":"Объектов далеко от плана: {n}","canvas.show_far":"Показать","canvas.home_tip":"План там — нажмите, чтобы вписать","gs.grid_group":"Обслуживание планов","gs.grid_hint":"Обновляет модели данных, выравнивает элементы по сетке и объединяет лишние фрагменты стен. Перед записью будет показан точный отчёт.","gs.align_all":"Оптимизировать планы","gs.align_title":"Оптимизировать планы","gs.align_none":"Все планы уже используют актуальную и оптимальную модель данных.","gs.optimize_no_automatic_changes":"Автоматических изменений нет. Проверьте пункты ниже.","gs.align_count":"Сдвинется элементов: {n} из {total}, максимум на {cm} см.","gs.align_where":"Наибольший сдвиг — в пространстве «{s}».","gs.align_turned":"Проёмов с исправлением угла: {n}.","gs.align_removed_drafts":"Схлопнувшиеся на сетке некорректные контуры удалены: {n}.","gs.optimize_redundant_drafts":"Сохранённые цепочки стен, полностью скрытые стенами комнат, удалены: {n}.","gs.align_preflight_failed":"Не удалось безопасно проверить геометрию следующих пространств: {spaces}{more}.","gs.align_preflight_hint":"Планы не изменены. Скопируйте диагностику кнопкой ниже и приложите её к отчёту об ошибке вместе с экспортом пространства.","gs.preflight_reason_prepare-exception":"Не удалось подготовить геометрию пространства (исключение при сборке модели)","gs.preflight_reason_wall-null":"Тело стен не построилось (объединение вернуло пустоту)","gs.preflight_reason_wall-degraded-extra":"Тело стен деградировало с лишней геометрией","gs.preflight_reason_wall-failed-core":"Ядро тела стен не собралось","gs.preflight_reason_wall-exception":"Построение стен упало с исключением","gs.preflight_reason_floor-null":"Контур пола не построился","gs.preflight_reason_floor-exception":"Построение пола упало с исключением","gs.preflight_copy":"Скопировать диагностику","gs.preflight_copied":"Диагностика скопирована","gs.preflight_update_hint":"Версии карточки и интеграции различаются — обновите House Plan и повторите.","gs.align_preflight_space":"Пространство {n}","gs.align_preflight_more":" и ещё {n}","gs.optimize_changes":"Миграций модели: {m}; обновлено пространств: {c}; устранён шум координат: {p}; объединено отрезков реальных стен: {w}; объединено отрезков стен нулевой толщины: {s}; независимых: {i}.","gs.zero_walls_migrated":"Преобразовано виртуальных участков: {n}.","gs.wall_segments_migrated":"Стабилизировано сегментов стен: {n}.","gs.optimize_lattice_summary":"Канонизировано шумовых значений координат: {n}; максимальный сдвиг: {cm} см.","gs.optimize_lattice_space":"{space}: канонизировано значений координат: {n}; оставлено значений вне сетки: {far}.","gs.optimize_coincident_partitions":"Скрытые участки независимых стен поглощены стенами комнат: {n}.","gs.optimize_openings_rehosted":"Проёмы перепривязаны к стенам комнат: {n}.","gs.optimize_walls_straightened":"Выпрямлено стен: {n}; максимальное перемещение: {cm} см.","gs.optimize_walls_straightened_where":"Максимальная правка стены: {s}.","gs.optimize_walls_straighten_skipped":"Оставлено почти осевых стен, которые нельзя безопасно выпрямить: {n}.","gs.optimize_glow_migration":"Старый Glow: пространств — {spaces} → без заливки данных + независимый Glow; комнат — {rooms} → наследуемая заливка + независимый Glow.","gs.optimize_references":"Исправлено ссылок: пространства — {spaces}; комнаты — {rooms}; позиции — {positions}; устройств отвязано от отсутствующих пространств — {detached}.","gs.optimize_reference_more":", и ещё {n}","gs.optimize_orphans_removed":"Убрано забытых записей: {total} — подписи комнат: {rooms}; устройства: {devices}; групповые метки: {groups}. Все они принадлежали пространствам, удалённым ранее.","gs.optimize_live_positions":"Старые позиции в удалённых пространствах принадлежат существующим объектам: {n}{names}. Они будут сохранены.","gs.optimize_live_positions_remove":"Старые позиции в удалённых пространствах принадлежат существующим объектам: {n}{names}. Они выбраны для удаления.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Убрать старые позиции","gs.optimize_live_keep":"Сохранить старые позиции","gs.optimize_live_selected":"Старые позиции будут убраны после применения оптимизации.","gs.optimize_unverified":"Не удалось безопасно проверить позиций: {n}. Они оставлены без изменений.","gs.optimize_registry_limited":"Для безопасной проверки нужен полный доступ администратора к реестрам Home Assistant.","gs.optimize_vacuum_warning":"Сопоставления комнат пылесоса требуют проверки: {n}.","gs.optimize_details":"Подробности","gs.optimize_details_more":"И ещё записей: {n}.","gs.optimize_detail_removed":"будет удалено","gs.optimize_detail_live":"будет сохранено","gs.optimize_detail_unverified":"не проверено","gs.optimize_detail_room_label":"подпись комнаты","gs.optimize_detail_device":"устройство","gs.optimize_detail_group":"групповая метка","gs.optimize_detail_unknown":"неизвестный владелец","gs.optimize_detail_item":"{status}: {kind} {id}; прежнее пространство {space}","gs.align_warn":"Элементы, намеренно поставленные между узлами, будут сдвинуты. После операции доступна одна отмена — только до следующего изменения плана.","gs.align_run":"Оптимизировать","gs.align_done":"Планы оптимизированы: сдвинуто элементов — {n}, обслужено записей — {m}, исправлено ссылок — {r}","gs.optimize_undo":"Отменить последнюю оптимизацию","gs.optimize_undone":"Последняя оптимизация отменена","decor.furniture":"Мебель","furn.title":"Библиотека мебели","furn.symbol":"Символ","furn.group_furniture":"Мебель","furn.group_appliance":"Техника","furn.group_sanitary":"Сантехника","furn.group_other":"Прочее","furn.width":"Ширина","furn.depth":"Глубина","furn.pick_hint":"Выберите символ и кликните по плану.","furn.place_hint":"Кликните по плану — предмет встанет к ближайшей стене. Shift — свободно.","furn.sym_sofa":"Диван","furn.sym_armchair":"Кресло","furn.sym_coffee_table":"Журнальный столик","furn.sym_table_dining":"Обеденный стол","furn.sym_table_round":"Круглый стол","furn.sym_chair":"Стул","furn.sym_desk":"Письменный стол","furn.sym_bed_double":"Двуспальная кровать","furn.sym_bed_single":"Односпальная кровать","furn.sym_nightstand":"Тумбочка","furn.sym_wardrobe":"Шкаф","furn.sym_bookshelf":"Стеллаж","furn.sym_fridge":"Холодильник","furn.sym_stove":"Плита","furn.sym_dishwasher":"Посудомоечная машина","furn.sym_washer":"Стиральная машина","furn.sym_dryer":"Сушильная машина","furn.sym_tv":"Телевизор","furn.sym_ac":"Кондиционер","furn.sym_water_heater":"Бойлер","furn.sym_toilet":"Унитаз","furn.sym_bathtub":"Ванна","furn.sym_shower":"Душ","furn.sym_sink":"Раковина","furn.sym_kitchen_sink":"Кухонная мойка","furn.sym_bidet":"Биде","furn.sym_stairs":"Лестница","furn.sym_fireplace":"Камин","furn.sym_plant":"Растение","furn.sym_rug":"Ковёр","common.yes":"Да","common.no":"Нет","vac.diag_source":"Источник","vac.diag_platform":"Интеграция","vac.diag_status":"Статус","vac.diag_position":"Позиция","vac.diag_rooms":"Комнаты","vac.diag_rooms_value":"{total} · совпало имён: {matched} · {readiness}","vac.autocal_ready":"автокалибровка доступна","vac.autocal_not_ready":"нужно 3 совпавших имени","vac.diag_path":"Путь интеграции","vac.diag_map":"ID карты","vac.source_none":"не выбран","vac.source_status_ok":"Готов","vac.source_status_missing":"Не найден","vac.source_status_disabled":"Деактивирован в Home Assistant","vac.source_status_unavailable":"Недоступен","vac.source_status_unverified":"Нельзя проверить с текущими правами","vac.source_status_unsupported":"Нет данных позиции","vac.source_status_none":"Нет источника","vac.source_banner_missing":"Сохранённый источник больше не существует. Он не заменён автоматически: выберите другой или восстановите его в Home Assistant.","vac.source_banner_disabled":"Сохранённый источник деактивирован в Home Assistant. Активируйте его там или выберите другой.","vac.source_banner_unverified":"Текущих прав Home Assistant недостаточно для проверки источника. Привязка сохранена и не будет заменена автоматически.","vac.choose_source":"Выбрать источник","vac.source_auto":"Автоматически","vac.source_auto_hint":"Использовать совместимую сущность этого устройства","vac.all_cameras":"Все камеры","vac.all_cameras_warn":"Камера может не отдавать данные робота. Выбирайте её только если это карта вашего пылесоса.","vac.all_cameras_empty":"Других сущностей камеры не найдено.","vac.platform_unknown":"интеграция неизвестна","vac.cap_position":"позиция","vac.cap_rooms_short":"комнаты","vac.cap_path":"путь","vac.cap_map":"ID карты","vac.cap_none":"данные робота не обнаружены","vac.xcme_hint":"Включите атрибуты Xiaomi Cloud Map Extractor:","vac.documentation":"Документация","vac.residual_title":"Проверьте автокалибровку","vac.residual_message":"Совпавшие комнаты расходятся максимум на {error}. Примените приблизительную калибровку, подгоните её вручную либо отмените без изменения сохранённых настроек.","vac.apply_proposal":"Применить","gs.backup_group":"Резервная копия и перенос","gs.backup_hint":"Скачайте переносимую JSON-копию или проверьте и импортируйте копию, созданную House Plan.","backup.export_open":"Экспорт","backup.import_open":"Импорт","backup.export_title":"Экспорт House Plan","backup.import_title":"Импорт House Plan","backup.export_hint":"Выберите, должна ли копия содержать всю конфигурацию House Plan или только текущее пространство.","backup.full":"Полная копия","backup.current_space":"Текущее пространство","backup.current_space_title":"Текущее пространство: {title}","backup.no_current_space":"Нет текущего пространства","backup.plan_only":"Только планировка","backup.plan_only_hint":"Сохранить комнаты, стены, проёмы, декор и позиции подписей комнат без устройств и привязок Home Assistant.","backup.plan_only_preview":"Файл содержит только планировку","backup.privacy_warning":"Архив сохраняет названия, идентификаторы Home Assistant и точные координаты. Внутренние планы и вложения указываются ссылками, но не вкладываются; текущие состояния и маршруты пылесосов не включаются.","backup.download":"Скачать JSON","backup.export_done":"Резервная копия скачана","backup.reading":"Проверяем резервную копию…","backup.revalidated":"После предпросмотра план изменился. Сводка обновлена — проверьте её и подтвердите ещё раз.","backup.error.unauthorized":"У вас нет прав на экспорт или импорт этого плана.","backup.error.not_ready":"House Plan ещё не готов. Повторите попытку через несколько секунд.","backup.error.too_large":"Размер резервной копии превышает 8 МиБ.","backup.error.invalid_json":"Выбранный файл не является корректным JSON.","backup.error.invalid_format":"Выбранный файл не является резервной копией House Plan.","backup.error.unsupported_export_version":"Эта версия формата резервной копии не поддерживается установленной версией.","backup.error.future_model":"Резервная копия создана в более новой версии модели данных House Plan.","backup.error.invalid_config":"Резервная копия содержит некорректную конфигурацию House Plan.","backup.error.wall_model_migration_blocked":"Не удалось безопасно обновить модель стен из резервной копии. Сначала оптимизируйте исходный план и повторите экспорт.","backup.error.invalid_layout":"Резервная копия содержит некорректные позиции объектов.","backup.error.invalid_content":"Резервная копия содержит некорректные или несогласованные ссылки на файлы.","backup.error.space_not_found":"Выбранное пространство больше не существует.","backup.error.capacity_exceeded":"Добавление этой копии превысит ограничения размера плана.","backup.error.preview_expired":"Время предпросмотра истекло. Выберите файл резервной копии заново.","backup.error.preview_owner_mismatch":"Этот предпросмотр принадлежит другому пользователю Home Assistant.","backup.error.conflict":"После предпросмотра план изменился. Проверьте обновлённую сводку.","backup.error.content_confirmation_required":"Подтвердите отсоединение недоступных локальных файлов.","backup.error.commit_failed":"Не удалось безопасно применить копию. Предыдущий план восстановлен или ожидает восстановления.","backup.error.missing_plan":"После предпросмотра исчез связанный файл плана. Проверьте копию ещё раз.","backup.error.missing_content":"После предпросмотра исчез связанный локальный файл. Проверьте копию ещё раз.","backup.error.marker_control_missing":"В резервной копии отсутствует связанный источник света на плане.","backup.error.marker_control_not_light":"Связанная цель больше не отмечена как источник света.","backup.error.marker_control_self":"Источник света не может управлять самим собой.","backup.error.marker_control_cycle":"Резервная копия содержит циклическую цепочку управления светом.","backup.error.duplicate_marker_control":"Резервная копия содержит повторяющуюся связь с источником света.","backup.error.no_backup":"Нет снимка импорта или оптимизации, который можно восстановить.","backup.same_source":"Создано на этом экземпляре Home Assistant","backup.foreign_source":"Создано на другом экземпляре Home Assistant","backup.created":"Создано: {value}","backup.versions":"Карточка {card}; интеграция {integration}; модель данных {model}","backup.count_spaces":"Пространств: {n}","backup.count_rooms":"Комнат: {n}","backup.count_walls":"Стен: {n}","backup.count_openings":"Проёмов: {n}","backup.count_decor":"Объектов декора: {n}","backup.count_markers":"Устройств: {n}","backup.count_layout":"Позиций: {n}","backup.bindings":"Привязки — устройства: {device}, сущности: {entity}, виртуальные: {virtual}; позиций без пространства: {legacy}","backup.binding_status":"Статус на целевом экземпляре — активных: {active}, деактивированных: {disabled}, отсутствующих: {missing}","backup.missing_areas":"На целевом экземпляре отсутствуют зоны: {areas}","backup.dropped_marker_links":"Связи с источниками света вне переносимого пространства пропущены: {n}.","backup.repaired_target_refs":"Существующих ссылок восстановлено этим импортом: {n}.","backup.preserved_unresolved_refs":"Неоднозначные ссылки сохранены без изменений: {n}.","backup.preserved_unresolved_hint":"House Plan ничего не угадывал и не удалял. После импорта запустите «Оптимизировать планы», чтобы проверить оставшиеся ссылки.","backup.import_details":"Подробности восстановления ссылок","backup.import_detail.incoming_remapped":"Ссылок обновлено внутри импортируемой копии: {n}","backup.import_detail.target_repaired":"Существующих ссылок восстановлено: {n}","backup.import_detail.preserved_unresolved":"Неразрешимых ссылок сохранено: {n}","backup.import_detail.collisions":"Конфликтов назначения безопасно сохранено: {n}","backup.import_detail.dropped_links":"Входящих связей пропущено по правилам переноса: {n}","backup.import_detail.bounded_lineages":"Чрезмерно вложенных идентификаторов ограничено: {n}","backup.replace_warning":"Текущая конфигурация и расположение будут заменены. Загруженные файлы не удаляются. Одну отмену можно выполнить до следующего изменения плана.","backup.foreign_bookkeeping":"Служебные списки известных и новых устройств другого экземпляра импортированы не будут.","backup.final_name":"Название нового пространства","backup.target_settings":"Глобальные настройки целевого экземпляра не изменятся.","backup.duplicates":"Уже существующих привязок: {n}","backup.skip":"Пропустить повторяющиеся устройства","backup.virtual_copy":"Добавить безопасные статичные виртуальные копии","backup.content":"Связанное содержимое","backup.content_available":"доступно локально","backup.content_external":"внешняя ссылка","backup.content_detach_required":"будет отвязано","backup.confirm_detach":"Я понимаю, что недоступные внутренние планы и вложения будут отвязаны от импортируемой конфигурации.","backup.replace":"Заменить","backup.add":"Добавить пространство","backup.space_done":"Пространство импортировано: комнат — {rooms}, устройств — {markers}, существующих ссылок восстановлено — {refs}","backup.full_done":"Копия восстановлена: пространств — {spaces}, комнат — {rooms}, устройств — {markers}","backup.undo_import":"Отменить последний полный импорт","backup.import_undone":"Полный импорт отменён"}};function ra(e,t){if(t&&t in aa)return t;return(e?.locale?.language||e?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function la(e,t,i){return ss(aa[e][t]??oa[t]??t,i)}function ca(e,t){const i=aa[e][t]??aa.en[t];return"string"==typeof i&&i.trim().length>0}class ha extends he{constructor(){super(...arguments),this.deviceName="",this._metadataConnection=null,this._demoUntil=0,this._demoTimer=0,this._demoGeneration=1,this._demoKind=null,this._reducedMotion=!1,this._onMotionChange=e=>{this._reducedMotion=e.matches,this.requestUpdate()},this._startShortDemo=()=>{const e=this.presentation;e&&"icon_ripple"===e.display&&"none"===e.pulse.kind&&(this._demoGeneration++,this._demoKind="short",this._demoUntil=Date.now()+3300,window.clearTimeout(this._demoTimer),this._demoTimer=window.setTimeout(()=>{this._demoUntil=0,this._demoTimer=0,this._demoKind=null,this.requestUpdate()},3330),this.requestUpdate())},this._toggleContinuousDemo=()=>{const e=this.presentation;e&&"icon_ripple"===e.display&&"none"===e.pulse.kind&&("continuous"===this._demoKind?this._clearDemo():(this._clearDemo(),this._demoGeneration++,this._demoKind="continuous",this._demoUntil=Number.POSITIVE_INFINITY),this.requestUpdate())}}connectedCallback(){super.connectedCallback(),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this._ensureMetadata()}disconnectedCallback(){this._metadataRelease?.(),this._metadataRelease=void 0,this._metadataConnection=null,this._clearDemo(),this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,super.disconnectedCallback()}willUpdate(e){e.has("hass")&&this._ensureMetadata();const t=this.presentation,i=e.get("presentation");e.has("presentation")&&i?.binding!==t?.binding&&this._clearDemo(),t&&this._demoKind&&("icon_ripple"!==t.display||"none"!==t.pulse.kind)&&this._clearDemo()}_ensureMetadata(){const e=this.hass?.connection||null;e&&e!==this._metadataConnection&&(this._metadataRelease?.(),this._metadataConnection=e,this._metadataRelease=Qo(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0,this._demoKind=null}get _lang(){return ra(this.hass)}_t(e,t){return la(this._lang,e,t)}_providerText(e){return"houseplan"===e.domain?this._t("marker.preview.virtual_provider"):e.label.toLowerCase()===e.domain.toLowerCase()?e.domain:`${e.label} (${e.domain})`}_providers(){return this.presentation&&this.registry?function(e,t,i,s=ea(e)){if("virtual"===t)return[{domain:"houseplan",label:"House Plan",confidence:"registry-owner"}];const n=t.indexOf(":");if(n<1)return[];const o=t.slice(0,n),a=t.slice(n+1);if("entity"===o){const t=na(e,a,i,s);return t?[t]:[]}if("device"!==o)return[];const r=i.devices?.[a]||e?.devices?.[a];if(!r)return[];const l=[r.config_entry_id,...Array.isArray(r.config_entries)?r.config_entries:[]].filter(e=>"string"==typeof e&&!!e).map(t=>{const i=s.configEntries[t];return ia(e,i?.domain,"registry-owner",s,t)});if(l.some(Boolean))return sa(l);const c=[];for(const[t,n]of Object.entries(i.entities||{}))n?.device_id===a&&null==n?.disabled_by&&c.push(na(e,t,i,s));return c.some(Boolean)?sa(c):sa((Array.isArray(r.identifiers)?r.identifiers:[]).map(t=>{const i=Array.isArray(t)?t[0]:null;return ia(e,i,"identifier-fallback",s)}))}(this.hass,this.presentation.binding,this.registry,ea(this.hass)):[]}_providerSummary(e){if(!e.length)return this._t("marker.preview.unknown_provider");const t=e.slice(0,2).map(e=>this._providerText(e)),i=e.length-t.length;return t.join(", ")+(i>0?` · ${this._t("marker.preview.more_sources",{n:i})}`:"")}_sourceSummary(e){const t=e.visualSources;return t.length?1===t.length?`${t[0].name} · ${t[0].eid} · ${this._sourceProvider(t[0].eid)}`:2===t.length?t.map(e=>e.name).join(" · "):this._t("marker.preview.multiple_sources",{n:t.length}):this._t("marker.preview.no_source")}_stateSummary(e){if(!e.visualSources.length)return this._t("marker.preview.no_state");if(1===e.visualSources.length)return e.visualSources[0].stateText||this._t("marker.preview.no_state");const t=[...new Set(e.visualSources.map(e=>e.stateText).filter(Boolean))];return 1===t.length?t[0]:this._t("marker.preview.mixed_states")}_reason(e){return this._t(`marker.preview.reason.${e}`)}_sourceProvider(e){if(!this.registry)return this._t("marker.preview.unknown_provider");const t=na(this.hass,e,this.registry,ea(this.hass));return t?this._providerText(t):this._t("marker.preview.unknown_provider")}render(){const e=this.presentation;if(!e)return G;const t=!!this._demoKind&&this._demoUntil>Date.now()&&"icon_ripple"===e.display&&"none"===e.pulse.kind,i=t?(()=>{const t=function(e,t,i,s,n=null){return{kind:t,reason:"short"===t?"event":"running",generation:Math.max(1,Math.trunc(i)),expiresAt:"short"===t?n:null,color:e.color,diameterScale:s?1:e.diameterScale,animated:!s,reducedMotionIndicator:s?"dot":"none"}}(e.pulse,this._demoKind,this._demoGeneration,this._reducedMotion,"short"===this._demoKind?this._demoUntil:null);return{...e,pulse:t,classes:[...e.classes.filter(e=>!e.startsWith("pulse-")),`pulse-${t.kind}`,...t.generation%2==0?["pulse-gen2"]:[]]}})():e,s=this._providers(),n="none"!==e.pulse.kind,o=i.scale*(i.pulse.animated?i.pulse.diameterScale:1);let a=-o/2,r=o/2,l=-o/2,c=o/2;if(i.valueBadge){const e=i.scale*Math.min(4,Math.max(.9,.29*i.valueBadge.text.length+.28)),t=.7*i.scale,s=.6*i.scale;"right"===i.valueBadge.position&&(r=Math.max(r,s+e)),"left"===i.valueBadge.position&&(a=Math.min(a,-s-e)),"top"===i.valueBadge.position&&(l=Math.min(l,-s-t)),"bottom"===i.valueBadge.position&&(c=Math.max(c,s+t))}if(null!=i.lqiText){const e=i.scale*("bottom"===i.valueBadge?.position?1.55:.95);c=Math.max(c,e)}const h=Math.min(1,2.35/Math.max(1,r-a),2.35/Math.max(1,c-l)),d=Math.round(100*h),p=[`left:calc(50% - ${54*((a+r)/2)*h}px)`,`top:calc(50% - ${54*((l+c)/2)*h}px)`,h<1?`transform:scale(${h})`:""].filter(Boolean).join(";"),u=["dev",Uo(this.hass),...i.classes,"virtual"===i.binding?"virtual":"",i.haDisabled?"ghost ha-disabled":"",null!=i.valueText?"valonly":""].filter(Boolean).join(" "),_=Go(i).join(";"),m=t?this._t("continuous"===this._demoKind?"marker.preview.demo_continuous_notice":"marker.preview.demo_short_notice"):this._reason(e.explanation.reason),g=[this.deviceName,this._providerSummary(s),this._stateSummary(e),m,e.valueFullText||"",Io(e.valueBadge),null!=e.lqiText&&e.lqiBand?this._t(`marker.lqi_a11y_${e.lqiBand}`,{value:e.lqiText}):""].filter(Boolean).join(". ");return q`
+ `}function Yo(e,t,i){return{kind:"none",reason:"none",generation:e,expiresAt:null,color:t,diameterScale:i,animated:!1,reducedMotionIndicator:"none"}}const Jo=new WeakMap;function Xo(e){const t=function(e){const t=e?.connection||e;return!t||"object"!=typeof t&&"function"!=typeof t?null:t}(e);if(!t)return null;let i=Jo.get(t);return i||(i={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},Jo.set(t,i)),i}function Zo(e){e.revision++;for(const t of[...e.listeners])try{t()}catch{}}function Qo(e,t){const i=Xo(e);if(!i)return()=>{};i.refs++,i.listeners.add(t),async function(e,t){t.loading||"function"!=typeof e?.callWS?t.loading:t.loaded&&t.loadedAt&&Date.now()-t.loadedAt<3e5||(t.loading=(async()=>{const[i,s]=await Promise.allSettled([e.callWS({type:"config_entries/get"}),e.callWS({type:"manifest/list"})]);"fulfilled"===i.status&&Array.isArray(i.value)&&(t.configEntries=Object.fromEntries(i.value.filter(e=>"string"==typeof e?.entry_id).map(e=>[e.entry_id,e]))),"fulfilled"===s.status&&Array.isArray(s.value)&&(t.manifests=Object.fromEntries(s.value.filter(e=>"string"==typeof e?.domain).map(e=>[e.domain,e]))),t.loaded=!0,t.loadedAt=Date.now(),t.loading=void 0,Zo(t)})().catch(()=>{t.loaded=!0,t.loadedAt=Date.now(),t.loading=void 0,Zo(t)}),t.loading)}(e,i),async function(e,t){if(t.unsubscribe||t.subscribing)return t.subscribing;const i=e?.connection?.subscribeMessage;"function"==typeof i&&(t.subscribing=(async()=>{try{t.unsubscribe=await i.call(e.connection,e=>{const i=Array.isArray(e)?e:Array.isArray(e?.entries)?e.entries:e?[e]:[];let s=!1;const n={...t.configEntries};for(const e of i){const t=e?.entry||e?.data?.entry||("string"==typeof e?.entry_id?e:null),i=t?.entry_id;if("string"!=typeof i)continue;const o=e?.type||e?.action||e?.data?.action;"removed"===o||"remove"===o?delete n[i]:n[i]=t,s=!0}s&&(t.configEntries=n,Zo(t))},{type:"config_entries/subscribe"})}catch{}finally{0===t.refs&&(t.unsubscribe?.(),t.unsubscribe=void 0),t.subscribing=void 0}})(),t.subscribing)}(e,i);let s=!1;return()=>{s||(s=!0,i.listeners.delete(t),i.refs=Math.max(0,i.refs-1),i.refs||(i.unsubscribe?.(),i.unsubscribe=void 0))}}function ea(e){const t=Xo(e);return t?{revision:t.revision,loaded:t.loaded,configEntries:t.configEntries,manifests:t.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function ta(e,t,i=ea(e),s){const n=i.manifests[t];return function(e,t){if("function"!=typeof e?.localize)return"";try{const i=`component.${t}.title`,s=e.localize(i);return"string"==typeof s&&s!==i?s:""}catch{return""}}(e,t)||String(n?.name||"")||String(s||"")||t.replace(/_/g," ")}function ia(e,t,i,s,n){const o=String(t||"").trim();if(!o)return null;const a=n?s.configEntries[n]:null;return{domain:o,label:ta(e,o,s,a?.title),configEntryId:n,configEntryTitle:a?.title,confidence:i}}function sa(e){const t={"registry-owner":0,"entity-platform":1,"identifier-fallback":2},i=new Set;return e.filter(e=>!!e).sort((e,i)=>t[e.confidence]-t[i.confidence]||e.label.localeCompare(i.label)||e.domain.localeCompare(i.domain)).filter(e=>{const t=`${e.domain}\n${e.configEntryId||""}`;return!i.has(t)&&(i.add(t),!0)})}function na(e,t,i,s=ea(e)){const n=i.entities?.[t]||e?.entities?.[t];if(!n)return null;const o=n.config_entry_id||void 0,a=o?s.configEntries[o]?.domain:"";return ia(e,n.platform||a,"entity-platform",s,o)}var oa={"editor.context_actions":"Actions: {object}","editor.tool_options":"Tool options: {tool}","editor.palette":"Palette: {tool}","editor.open_group":"Tool group: {group}","editor.group_active":"{group} — active: {item}","editor.disabled_action":"{action} is unavailable: {reason}","color_picker.title":"Color picker","color_picker.hue":"Hue","color_picker.saturation":"Saturation","color_picker.value":"Brightness","color_picker.hex":"Hex color","color_picker.invalid_hex":"Enter a 3- or 6-digit hex color","btn.properties":"Properties","btn.keep_as_walls":"Keep as walls","btn.delete_room_keep_walls":"Delete room, keep walls","btn.delete_room_with_walls":"Delete room and walls","title.markup_select":"Select and edit walls, columns and saved outlines","title.markup_column":"Column: click a grid point to place a square column","markup.partition":"Partition","markup.column":"Column","markup.hint_column":"click a grid point to place a square column","history.draft_segment":"Add room-draft segment","history.draft_merge":"Join unfinished room outlines","history.draft_segment_delete":"Remove room-draft segment","history.partition_add":"Add partition","history.column_add":"Add column","history.physical_edit":"Change physical object","history.physical_delete":"Delete physical object","history.physical_move":"Move physical object","history.contour_to_partitions":"Convert contour to closed walls","history.wall_chain_finish":"Finish wall chain","history.wall_face_batch":"Create rooms from walls","toast.column_duplicate":"A column with the same centre and outer size already exists","confirm.delete_draft":"Delete the whole unfinished room contour?","confirm.delete_draft_segment":"Delete this draft segment? The remaining contour may split in two.","physical.partition_properties":"Partition properties","physical.column_properties":"Column properties","physical.draft_properties":"Draft segment properties","physical.shape":"Shape","physical.square":"Square","physical.circle":"Circle","physical.diameter":"Diameter","physical.side":"Side","physical.rotation":"Rotation angle","physical.length":"Length","physical.allowed_range":"Allowed: {min}–{max} {unit}","physical.delete_segment":"Delete segment","physical.delete_draft":"Delete entire outline","physical.column_size_title":"Outer side of the square column placed by the click (1–150 cm).","card.title":"House plan","count.devices":"{n} dev.","empty.no_spaces":"No spaces yet.","empty.add_first":"Add the first space and upload a floor plan.","empty.install":'Install the House Plan integration and add it in "Devices & services".',"btn.add_space":"Add space","btn.cancel":"Cancel","btn.save":"Save","btn.close":"Close","btn.delete":"Delete","btn.edit":"Edit","btn.open_in_ha":"Open in HA","btn.reset":"Reset","btn.attach":"Attach…","btn.upload":"Upload…","btn.replace":"Replace…","title.zoom_in":"Zoom in","title.zoom_out":"Zoom out","title.zoom_fit":"Fit all","title.add_device":"Add a device to the plan","title.show_all":"Show hidden and disabled devices as service markers (this tab only)","title.markup":"Room markup: grid, lines, outlines","title.configure_space":"Configure space","title.add_space":"Add space","title.markup_add":"Walls: draw a continuous chain; Shift locks to 45°. Changing tool or leaving the editor finishes it as independent walls","title.markup_merge":"Merge: click one room, then the neighbour it shares a wall with","title.markup_split":"Split a room: click the room, then two points on its walls","title.markup_delroom":"Delete room: click inside a room and confirm","title.need_plan":"Upload a floor-plan image","markup.add":"Walls","markup.merge":"Merge","markup.split":"Split","markup.resize":"Resize","title.markup_resize":"Resize rooms: drag an available horizontal or vertical wall handle","markup.hint_resize":"drag an available wall handle · the wall stops at the first unsafe position · Esc cancels · Ctrl+Z — undo","resize.disabled.diagonal":"This wall is angled; Resize supports only horizontal and vertical walls","resize.disabled.side-angle":"An adjoining wall is angled; Resize requires both adjoining walls to meet this wall at right angles","resize.disabled.duplicate-physical-wall":"A separate partition, unfinished outline, or column overlaps this wall; remove or move it before resizing","resize.disabled.partial-shared":"A neighbouring room uses only part of this wall, so it cannot be moved safely as one shared wall","resize.disabled.unequal-shared":"The neighbouring room uses different endpoints or wall length, so the walls cannot move together safely","resize.disabled.multiple-rooms":"Moving this wall would affect more than two rooms","resize.disabled.thickness-conflict":"The wall thickness cannot be preserved safely","resize.disabled.opening-conflict":"An opening on this wall cannot be moved safely","resize.disabled.invalid-geometry":"This wall cannot be resized without changing the plan structure","resize.limit_stopped":"Wall stopped: the next step would break a junction limit","resize.commit_failed":"Resize was cancelled because the final plan did not pass the geometry check","resize.preview_failed":"Resize stopped at the last safe position because the plan geometry could not be preserved","markup.opening":"Opening","markup.delete_room":"Delete room","history.undo":"Undo","history.redo":"Redo","history.undo_named":"Undo: {name} (Ctrl+Z)","history.redo_named":"Redo: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Nothing to undo","history.redo_empty":"Nothing to redo","history.undone":"Undone: {name}","history.redone":"Redone: {name}","history.add_room":"Create room","history.split_room":"Split room","history.merge_rooms":"Merge rooms","history.resize_room":"Resize room","history.wall_thickness":"Change wall thickness","history.add_opening":"Add door or window opening","history.edit_opening":"Edit opening","history.move_opening":"Move opening","history.delete_opening":"Delete opening","history.delete_room":"Delete room","history.delete_room_keep_walls":"Delete room, keep walls","history.delete_room_with_walls":"Delete room and walls","history.decor_add":"Add decor object","history.decor_edit":"Edit decor object","history.decor_move":"Move decor object","history.decor_transform":"Transform decor object","history.decor_delete":"Delete decor object","history.backdrop_transform":"Transform plan backdrop","title.markup_opening":"Openings: click a wall to place, click an opening to edit","opening.new":"New opening","opening.edit":"Opening","opening.door":"Door","opening.window":"Window","opening.gate":"Gate","opening.passage":"Open passage","opening.passage_binding_warning":"Saving will remove the open/close sensor and lock.","opening.invalid_passage_fields":"The open passage on plan “{room}” has unsupported settings: {fields}.","opening.type_label":"Type","opening.length_label":"Length, cm","opening.contact_label":"Open/close sensor","opening.lock_label":"Lock","opening.none":"— none —","opening.search_ph":"Search: name or entity id…","opening.invert":"Invert open/closed","opening.flip_h":"Hinge on the other jamb","opening.flip_v":"Opens to the other side","opening.open":"Open","opening.closed":"Closed","opening.locked":"Locked","opening.unlocked":"Unlocked","opening.state_unknown":"unavailable","opening.no_entities":"No sensors bound — a static symbol on the plan.","toast.opening_no_wall":"Click next to a room wall or independent wall","opening.host_partition":"Independent wall","opening.partition_orphan":"The independent wall for this opening no longer exists","opening.partition_jamb_margin":"Leave at least {distance} between the opening and the end of the wall","opening.rebind_partition":"Attach to another independent wall","confirm.delete_partition_openings_title":"Delete wall and openings?","confirm.delete_partition_openings_body":"This wall contains {count} opening(s). They will be deleted together.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Delete","markup.hint_points":"points: {n} · Shift — 45° steps · Esc — finish chain · Ctrl+Z — undo a point · closing an area offers a room","markup.hint_start":"click a grid dot to start a wall chain","tip.lqi":"average zigbee signal:","tip.area":"area: {value}","info.device_header":"Device on the plan","info.model":"Model","info.state":"State","info.link":"Link","info.manuals":"Manuals","info.none":"No additional information","marker.new_device":"New device","marker.name_label":"Name (shown on the plan)","marker.name_ph":"Name","marker.binding_label":"Bind to an HA device","marker.binding_disabled":"disabled in Home Assistant","marker.virtual_option":"Virtual device (no binding)","marker.search_ph":"Search device / group…","marker.nothing_found":"nothing found","marker.room_label":"Room","marker.room_override":" (override placement)","marker.room_choose":"— select a room —","marker.room_auto":"— by device area (auto) —","marker.icon_label":"Icon","marker.icon_ph":"mdi:… (empty = auto)","marker.display_label":"Display","display.badge":"Icon + state","display.icon_ripple":"Icon + state and activity","display.static_icon":"Always static icon","marker.display_hint":"Icon + state changes the plate for work, open, alarm and unavailable states. Icon + state and activity additionally shows a short pulse for events and a continuous pulse for ongoing work, movement or presence. Value + state replaces the icon with one unambiguous HA value. A static icon never reacts to device states.","marker.display_hint_badge":"The icon and dynamic plate show device state without the ordinary activity pulse. Red alarms remain visible.","marker.display_hint_icon":"The icon and dynamic plate show device state without the ordinary activity pulse. Red alarms remain visible.","marker.display_hint_icon_ripple":"The icon, dynamic plate and pulse show a short pulse for events and a continuous pulse for work, motion or presence. Red alarms have separate priority.","marker.display_hint_value":"One unambiguous Home Assistant value replaces the icon while the plate continues to show state. Red alarms remain visible.","marker.display_hint_static_icon":"The theme-aware shell and icon always stay the same. State, activity, unavailability and alarms do not change the face.","marker.static_alarm_warning":"This device can report alarms. Static display hides the marker's visual alarm indication.","marker.preview.title":"Display preview","marker.preview.actual":"Now","marker.preview.example":"Example","marker.preview.integration":"Provided by","marker.preview.source":"Display source","marker.preview.current_state":"Current state","marker.preview.result":"On the plan","marker.preview.details":"Source details","marker.preview.select_source":"Choose a Home Assistant device or entity to see its actual display.","marker.preview.unknown_provider":"Unknown integration","marker.preview.virtual_provider":"House Plan · virtual device","marker.preview.no_source":"No active source","marker.preview.no_state":"No current state","marker.preview.mixed_states":"Several different states","marker.preview.multiple_sources":"{n} sources","marker.preview.more_sources":"+{n} more","marker.preview.scaled":"The preview is scaled to {n}% to fit. The saved size is unchanged.","marker.preview.demo_activity":"Show activity example","marker.preview.demo_notice":"Temporary activity example; the actual device state is unchanged.","marker.preview.demo_short":"Show short activity","marker.preview.demo_continuous":"Show continuous activity","marker.preview.stop_continuous":"Stop continuous activity","marker.preview.demo_short_notice":"Short activity example; the actual device state is unchanged.","marker.preview.demo_continuous_notice":"Continuous activity example; the actual device state is unchanged.","marker.preview.demo_already_visible":"The current state already shows a real activity or alarm effect.","marker.preview.reduced_motion":"System reduced-motion is enabled, so ordinary activity is shown as a dot.","marker.preview.reason.neutral":"Neutral dark plate","marker.preview.reason.working":"Yellow plate: the device is working now","marker.preview.reason.working_activity":"Yellow plate and activity effect: the device is working now","marker.preview.reason.open":"Orange plate: physically open or unlocked","marker.preview.reason.cover_icon_state":"Neutral plate; the cover entity controls the displayed state and icon","marker.preview.reason.presence":"Activity effect while presence is detected","marker.preview.reason.event":"Short activity effect after a detected event","marker.preview.reason.transition":"Activity effect while the device is moving or changing state","marker.preview.reason.media_neutral":"Media devices stay on a neutral dark plate while available","marker.preview.reason.unavailable":"Subdued neutral plate: unavailable or off","marker.preview.reason.alarm":"Red alarm plate; alarm indication is always shown","marker.preview.reason.live_states_disabled":"Live-state styling is disabled for this card","marker.preview.reason.value_no_state":"No usable state value; the icon is shown instead","marker.preview.reason.value_ambiguous_sources":"Several possible value sources; the icon is shown instead","marker.preview.reason.value_non_scalar":"The source did not return a simple value; the icon is shown instead","marker.preview.reason.value_virtual":"A virtual device has no Home Assistant value; the icon is shown instead","marker.preview.reason.vacuum_live_plan_only":"Live vacuum position and trail are available only on the full plan","marker.preview.reason.hidden_design_preview":"The device is hidden on the plan; this design preview remains visible","marker.preview.reason.composite_power_source":"State comes from the device Power entity; auxiliary switches are ignored","marker.preview.reason.activity_display_disabled":"The selected display mode does not show ordinary activity effects","marker.preview.reason.ha_disabled":"The Home Assistant binding is disabled and will be hidden on the plan","marker.preview.reason.orphaned":"The saved Home Assistant binding can no longer be found","marker.preview.reason.static_icon":"Static mode: device state does not change the icon","marker.activity_color":"Activity pulse color","marker.ripple_size":"Activity pulse size","marker.activity_alarm_note":"Color and size affect ordinary activity only and do not change red alarms.","marker.pulse_a11y_alarm":"Alarm","marker.pulse_a11y_event":"Recent event","marker.pulse_a11y_presence":"Presence detected","marker.pulse_a11y_transition":"Changing state","marker.pulse_a11y_running":"Working","marker.state_a11y_neutral":"Default state","marker.state_a11y_working":"Working","marker.state_a11y_open":"Open","marker.state_a11y_locked":"Locked","marker.state_a11y_unlocked":"Unlocked","marker.state_a11y_alarm":"Alarm","marker.state_a11y_unavailable":"Unavailable","marker.lqi_a11y_low":"LQI {value}, low signal","marker.lqi_a11y_mid":"LQI {value}, medium signal","marker.lqi_a11y_high":"LQI {value}, high signal","marker.size_label":"Icon size / rotation","marker.angle_label":"Rotate","marker.model_label":"Model","marker.model_ph":"e.g. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Description","marker.desc_ph":"Notes, specs…","marker.manuals_label":"Manuals (PDF etc.)","marker.sub_device":"device","marker.sub_z2m_group":" · Z2M group","marker.sub_group":"group","marker.sub_helper":"helper","space.new":"New space","space.header":"Space","space.title_label":"Title","space.title_ph":"e.g. Garage","space.plan_label":"Floor plan (background)","space.no_plan":"no plan image","space.plan_alt":"plan","room.new":"New room","room.name_label":"Display name","room.name_ph":"e.g. Terrace","room.area_label":"Home Assistant area (unassigned)","room.no_area_option":"— no area —","room.default_name":"Room","device.unnamed":"unnamed","device.light_group":"light group","device.fallback":"device","device.virtual":"virtual device","confirm.delete_room":'Delete room "{name}"?',"confirm.delete_room_title":'Delete room "{name}"?',"confirm.delete_room_body":"Choose whether the room's exclusive physical walls should remain. Walls shared with another room are always kept.","confirm.merge_rooms":'Merge rooms "{a}" and "{b}"?',"confirm.remove_marker":'Delete "{name}" from the plan? The device will disappear completely and stop contributing to plan data. You can add it again later.',"confirm.erase_decor":"Erase the {kind} object? You can undo this action from the editor history.","confirm.delete_space":'Delete space "{title}" with all its rooms and markup?',"space.delete_blocked":"This space is still used by {n} device(s). Move them to another space or delete them first.","toast.pos_save_failed":"Failed to save position: {err}","toast.no_entity":"The device has no suitable entity","toast.ha_disabled_action":"A Home Assistant object that is disabled cannot be used on the plan.","toast.ha_disabled_show_device":"A device disabled in Home Assistant cannot be shown on the plan. Enable it in Home Assistant first.","toast.ha_disabled_show_entity":"An entity disabled in Home Assistant cannot be shown on the plan. Enable it in Home Assistant first.","toast.ha_disabled_add":"A disabled Home Assistant object cannot be added to the plan. Enable it in Home Assistant first.","toast.ha_binding_unverified":"The object status could not be verified through the Home Assistant registry. Display and actions are temporarily unavailable.","toast.markup_needs_server":"Markup is available after the config is moved to the server","toast.space_order_changed":"Order changed. If any card pins its floor by number, check those panels.","toast.conflict":"Config was changed in another window — data refreshed, repeat your last action","toast.cfg_save_failed":"Failed to save config: {err}","toast.room_overlap":"The outline overlaps room “{name}” — rooms must not overlap","toast.contour_min_edges":"Draw at least two edges before closing the outline","toast.contour_cannot_close":"The outline cannot close because it is degenerate or intersects itself","toast.merge_not_adjacent":"Only rooms that share a wall can be merged","toast.rooms_merged":"Rooms merged into “{name}”","toast.split_pick_wall":"Start the cut on the room’s wall","toast.split_bad_cut":"The cut must run wall to wall inside the room, without crossing walls or itself","merge.header":"Merge rooms","merge.hint":"The merged room keeps one name and one area. The other area is released — its devices leave the plan until another room claims it.","merge.keep":"Keep","merge.no_area":"no area","room.split_header":"New room from the split","toast.room_saved":"Room saved ({n}). Devices added: {added}. Outline the next one or exit markup.","toast.room_saved_no_area":"Room saved ({n}, no area). Outline the next one or exit markup.","toast.marker_needs_server":"Device editing is available after the config is moved to the server","toast.virtual_name_required":"Enter a name for the virtual device","toast.marker_saved":"Device saved","toast.marker_removed":"Device deleted from the plan","toast.integration_missing":"The House Plan integration is not installed — management unavailable","toast.plan_formats":"Supported formats: SVG, PNG, JPG, WebP","toast.plan_required":"Upload a floor plan — it is required","toast.space_added_onboard":"Space added. Outline the rooms: click grid dots and close the contour.","toast.space_added":"Space added","toast.space_saved":"Space saved","toast.space_deleted":"Space deleted","toast.delete_failed":"Delete failed: {err}","toast.error":"Error: {err}","toast.toggle_target_unavailable":"Target “{name}” is unavailable — no action was performed","toast.toggle_targets_unavailable":"Targets are unavailable: {names}. No action was performed","toast.file_failed":'File "{name}" was not uploaded: {err}',"toast.files_attached":"Files attached: {n}","err.unknown":"unknown error","err.code":"code {code}","err.too_large":"file larger than {mb} MB","err.bad_ext":"unsupported type (PDF/image expected)","err.unauthorized":"administrator rights required","editor.title":"Title","editor.default_floor":"Initial space","editor.default_floor_missing":"Initial space “{id}” no longer exists. Choose another space.","editor.floor":"Fixed space","editor.floor_none":"— not fixed —","editor.floor_index":"YAML index {index} (preserved)","fixed_floor.loading":"Loading the fixed space…","fixed_floor.invalid_title":"The fixed space is unavailable","fixed_floor.invalid_body":"Check floor in the card configuration. The configured value is: {value}","editor.icon_size":"Icon size, % of plan width","editor.show_temperature":"Show temperature","editor.live_states":"Live states (on/off, open…)","editor.show_signal":"Show zigbee signal (LQI)","editor.language":"Interface language","editor.lang_auto":"Auto (HA profile)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Icon rules: which MDI icon devices get by name","rules.title":"Icon rules","rules.hint":"Rules are checked top-down against “device name + model” (case-insensitive regex); the first match wins. When nothing matches, the entity device class decides, then the generic chip icon.","rules.pattern_ph":"regex, e.g. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Add rule","rules.reset":"Reset to defaults","rules.test_ph":"Try a device name…","rules.invalid":"invalid regex","rules.saved":"Icon rules saved","btn.up":"Up","btn.down":"Down","tap.info":"Device card","tap.more_info":"HA more-info dialog","tap.toggle":"Toggle state","marker.tap_label":"Tap action for this device","tap.toggle_note":"The resolved state is shown below. Secure devices are never toggled from the plan.","import.title":"Create spaces from HA floors","import.hint":"Your Home Assistant already knows these floors. Pick the ones to turn into plan spaces — you will upload a floor-plan image for each one next. Rooms are then outlined by hand on the plan.","import.start":"Create {n} space(s)","import.manual":"Start from scratch","import.progress":"Floor {i} of {n}","import.done":"Spaces created. Outline the rooms: click grid dots and close the contour.","btn.skip":"Skip","space.scale_label":"Scale (grid cell size)","space.scale_unit":"cm per cell","space.scale_unit_imperial":"in per cell","space.display_section":"Display","space.show_borders":"Always show room borders","space.zero_wall_style":"Zero-thickness walls","space.zero_wall_dashed":"Dashed","space.zero_wall_solid":"Solid","space.zero_wall_help":"Dashed walls let light through. Solid walls block light even at zero thickness.","space.show_names":"Show room names (drag to move)","space.room_color":"Border & name color","space.opacity":"Opacity","space.fill_label":"Room fill","fill.none":"None","fill.lqi":"Zigbee signal","fill.light":"Lights","fill.custom":"Custom color","space.custom_fill":"Fill color","space.source_file":"I have a floor-plan image","space.source_draw":"No image — I'll outline rooms by hand","space.orientation":"Canvas","orient.landscape":"Landscape","orient.portrait":"Portrait","orient.square":"Square","fill.temp":"Temperature","space.temp_min":"Comfort from","space.temp_max":"to","tip.temp_avg":"average temperature:","tip.hum_avg":"average humidity:","space_card.button":"Open the space plan","space_card.not_found":"Space “{id}” not found","space_card.loading":"Loading…","continuity.restore_plan":"Restoring floor plan…","continuity.restore_connection":"Restoring device connection…","continuity.retry":"Try again","editor.space":"Space","editor.show_button":"Show button","editor.button_label":"Button label","editor.button_target":"Target dashboard path","marker.sub_entity":"entity","title.general_settings":"General settings","gs.title":"General settings","gs.hint":"Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.","gs.light_group":"Fill: lights","gs.light_on":"Lights on","gs.light_off":"All lights off","gs.temp_group":"Fill: temperature","gs.temp_cold":"Cold","gs.temp_ok":"Comfortable","gs.temp_hot":"Hot","gs.lqi_group":"Fill: zigbee signal","gs.lqi_low":"Weak signal","gs.lqi_high":"Strong signal","gs.reset":"Reset to defaults","gs.saved":"General settings saved","space.show_lqi":"Show zigbee signal (LQI) next to devices","space.hide_decor":"Hide the decorative layer","space.hide_decor_tip":"Lines, shapes, labels and furniture stay where they are — visible in the backdrop editor, not on the plan.","space.hide_openings":"Hide openings","space.hide_openings_tip":"Door, window and gate symbols are not drawn, but the openings keep working: light passes through, the sun comes in at a window, contact sensors still open them. The plan editor always shows them.","gs.light_none":"No light sources","mode.plan":"Plan editor","mode.devices":"Device editor","display.value":"Value + state","marker.subarea":"no area, manual","device.new":"New device — open its editor to dismiss","opening.unlock_action":"Unlock","opening.lock_action":"Lock","opening.lock_pending":"Working…","title.close_editor":"Close editor (back to view)","devbar.add":"Add","devbar.show_all":"Hidden and disabled","devbar.rules":"Icon rules","device_inbox.button":"Devices","device_inbox.title":"Devices on the plan","device_inbox.search":"Search devices, entities and integrations…","device_inbox.add_virtual":"Add virtual device","device_inbox.tab_on_plan":"On plan","device_inbox.tab_available":"Available","device_inbox.tab_hidden":"Hidden","device_inbox.tab_readd":"Available again","device_inbox.only_new":"New only","device_inbox.show_entities":"Show entities","device_inbox.show_hidden":"Show hidden on plan","device_inbox.new":"New","device_inbox.find":"Find on plan","device_inbox.edit":"Edit","device_inbox.hide":"Hide","device_inbox.show":"Show","device_inbox.add":"Add","device_inbox.readd":"Add again","device_inbox.hide_available":"Hide from list","device_inbox.show_more":"Show more","device_inbox.more_actions":"More actions","device_inbox.find_hidden_hint":"Enable “Show hidden on plan” first","device_inbox.show_disabled":"Activate this binding in Home Assistant before showing it","device_inbox.empty_on_plan":"No matching devices are on the plan yet.","device_inbox.empty_available":"No devices are available to add.","device_inbox.empty_hidden":"There are no hidden devices.","device_inbox.empty_readd":"No removed devices are available to add again.","device_inbox.reason_visible_auto":"Discovered automatically","device_inbox.reason_visible_explicit":"Added explicitly","device_inbox.reason_manual_hidden":"Hidden by user","device_inbox.reason_automatic_hidden":"Hidden automatically","device_inbox.reason_service_entry":"Service-only registry entry","device_inbox.reason_excluded_integration":"Integration excluded by device filters","device_inbox.reason_excluded_domain":"Non-spatial entity type","device_inbox.reason_grouped_light":"Represented by a room light group","device_inbox.reason_represented_by_parent":"Represented by its parent device","device_inbox.reason_removed":"Previously removed from the plan","device_inbox.reason_available":"Available to add to the plan","device_inbox.reason_no_bound_room":"HA area is not linked to a plan room","device_inbox.status_ha_disabled":"Disabled in Home Assistant","device_inbox.status_orphaned":"Binding is no longer present in Home Assistant","device_inbox.status_unverified":"Home Assistant registry is temporarily unavailable","device_inbox.saved":"Device list updated","space.roomcard_section":"Room card shows:","space.label_temp":"Temperature","space.label_hum":"Humidity","space.label_lqi":"Average Zigbee signal","space.label_light":"Lights on/off","roomcard.light_on":"On","roomcard.light_off":"Off","roomcard.light_partial":"{on} of {total}","toast.split_pick_inside":"Intermediate cut points must be inside the room","mode.decor":"Background editor","decor.select":"Select","decor.line":"Line","decor.rect":"Rectangle","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Erase","decor.erase_confirm_title":"Erase object?","decor.color":"Color","decor.width":"Line width","decor.line_style":"Line style","decor.line_style_solid":"Solid","decor.line_style_dashed":"Dashed","decor.fill":"Fill","decor.fill_color":"Fill color","decor.length":"Length","decor.size":"Size","decor.angle":"Rotation angle, °","decor.text_size":"Text size","decor.backdrop_properties":"Plan backdrop properties","decor.text_title":"Text label","decor.object_title":"Edit {kind}","decor.text_label":"Text","decor.live_group":"Insert HA variable","decor.live_entity":"Entity","decor.live_entity_ph":"choose an entity","decor.live_attr":"Value","decor.live_attr_ph":"choose state or attribute","decor.live_state":"State","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag to move; pull a corner to resize; use the top handle to rotate. Shift changes proportions or frees the angle.","decor.backdrop_reset":"Reset the picture","decor.backdrop_reset_done":"The picture is back at its original place and size","marker.icon_auto":"Auto: {icon} (by icon rules; pick one to override)","marker.icon_pin_auto":"Pin","mode.plan_tip":"Plan editor — the geometry of the home: draw and split/merge rooms, bind them to HA areas, place doors, windows and gates, move room cards, set the scale","mode.devices_tip":"Device editor — everything about icons: drag to position, click to edit binding/icon/display, add virtual devices, icon rules","mode.decor_tip":"Background editor — purely visual decor under the plan: lines, rectangles, ovals and text labels that never react to clicks","space.glow_enabled":"Light-source glow","gs.glow_group":"Light-source glow","gs.glow_base":"House darkness","gs.glow_light":"Default light color / intensity","gs.wall_group":"Walls","gs.wall_fill":"Wall fill","gs.glow_radius":"Glow radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Controls other light sources","marker.controls_hint":"With tap action “Toggle”, a click flips every source added here. The marker’s own bound entity is controlled directly and cannot be added; use the switch below to classify it as a light.","marker.controls_filter":"Search lights and switches…","info.controls":"Controls","marker.glow_radius_label":"Glow radius","marker.glow_radius.help":"Sets the glow radius in metres or feet; an empty value uses the radius from general settings.","marker.glow_radius.help.aria":"Help: glow radius","markup.wallthick":"Thickness","markup.select":"Select","title.markup_wallthick":"Thickness — click a wall to set its thickness from 0 to 100 cm.","markup.hint_wallthick":"click a wall · Esc closes without applying","wallthick.field":"Thickness","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Apply to all walls of this room","markup.draw_wall_title":"Thickness of each new wall segment (0–100 cm). Shared walls keep the existing value.","room.queue_progress":"Room {current} of {total}","toast.wall_rooms_saved":"Created rooms: {n}","toast.wall_chain_saved":"Wall chain saved","toast.wallthick_pick":"Click a wall","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from {min} to {max} {unit}","toast.zero_wall_opening_conflict":"Remove the opening on this wall segment first.","toast.zero_wall_ambiguous":"The wall segment is ambiguous. Simplify or adjust the junction.","toast.zero_wall_migration_blocked":"The space was not converted: {reason}. No data was changed.","toast.physical_angle":"Enter a rotation angle from 0° up to, but not including, 90°","toast.physical_limit":"The space has reached the limit for this type of geometry","toast.geometry_unsafe":"Change canceled: wall geometry could not be built safely.","junction.limit_angle":"The angle between walls is too sharp: {actual}°, minimum {limit}°.","junction.limit_valence":"Too many walls meet in one node: {actual}, maximum {limit}.","junction.limit_length":"The wall segment is too short: {actual} cm, minimum {limit} cm.","junction.limit_distance":"Walls and nodes are too close: {actual} cm, minimum {limit} cm.","junction.limit_clearance":"No room interior is left: {actual} cm², minimum {limit} cm².","toast.wall_model_migration_blocked":"The wall model could not be updated: {reason}. The plan was not changed. Run “Optimize plans”; if the error repeats, fix the conflicting wall geometry.","toast.wall_model_client_outdated":"Update the card and reload the page before editing the plan.","wall_model.reason.invalid-room":"invalid room contour","wall_model.reason.zero-length":"zero-length wall segment","wall_model.reason.third-owner":"wall shared by more than two rooms","wall_model.reason.duplicate-id":"conflicting wall identifiers","wall_model.reason.thickness-conflict":"conflicting wall thickness values","wall_model.reason.opening-host":"opening cannot be assigned to one wall","toast.delete_room_pick":"Click inside the room you want to delete","toast.plan_snap_ambiguous":"Zoom in to choose the wall node","toast.wall_repair_ambiguous":"The room outline has more than one possible connection. Zoom in and connect it explicitly.","toast.wall_repair_too_large":"The gap is larger than 2 cm. Connect the walls before creating the room.","toast.wall_repair_changed":"The wall geometry changed. Try creating the room again.","toast.opening_on_zero_wall":"Openings cannot sit on a zero-thickness wall","marker.from_ha_option":"Pick from the HA list","marker.show_entities":"Show entities","marker.show_entities_tip":"Adds not only devices to the list, but all their entities too","marker.pick_ph":"Choose a device…","room.open_area":"Open the HA area","view.volumetric":"Volumetric view","view.flat":"Flat view","kiosk.title":"This screen's sizes","kiosk.hint":"Stored on this device only — every wall tablet or TV can have its own comfortable sizes.","kiosk.icon_scale":"Device icon size","kiosk.font_scale":"Room card text size","editor.kiosk":"Wall device (kiosk) mode","editor.cycle":"Auto-switch spaces every N seconds (kiosk, 0 = off)","room.settings_title":"Room settings","room.settings_section":"Room settings (override the space)","room.fill_label":"Fill in THIS room","fill.inherit":"As the space","room.custom_fill_space":"Space color","room.custom_fill_own":"Room color","room.temp_src_label":"Temperature source","room.hum_src_label":"Humidity source","room.src_average":"Average over the room's sensors (default)","room.src_pick":"A specific HA device or entity","room.src_ph":"Choose a source…","toast.room_updated":"Room updated","space.card_font":"Room-card font size (whole space)","room.sizes_section":"Font sizes","room.name_scale":"Room name size","room.label_scale":"Metrics size","preview.room_name":"Living room","toast.cfg_reload_failed":"Could not reload the plan from the server: {err}","room.settings_short":"Room settings","room.unnamed":"Unnamed room","marker.use_climate_temp":"Include the device temperature in the room","marker.use_climate_temp_tip":"Adds an air conditioner or thermostat current_temperature to the room average. Configure the external value badge separately below.","marker.value_badge_title":"Value badge","marker.value_badge.help":"Shows one selected value next to the icon. It does not affect room metrics, light, Glow or the tap action.","marker.value_badge.help.aria":"Help: device value badge","marker.value_badge_enabled":"Show a value badge","marker.value_badge_source":"Value","marker.value_badge_source.help":"Choose a specific state, supported attribute, or derived value of this device.","marker.value_badge_source.help.aria":"Help: value badge source","marker.value_badge_position":"Position","marker.value_badge_position.help":"The chosen side stays fixed and does not flip automatically at a plan edge.","marker.value_badge_position.help.aria":"Help: value badge position","marker.value_badge_right":"Right","marker.value_badge_bottom":"Below","marker.value_badge_left":"Left","marker.value_badge_top":"Above","marker.value_badge_empty":"This device has no available values","marker.value_badge_static":"A static icon does not show live values. The setting is preserved.","marker.value_badge_missing":"Source unavailable","marker.value_badge_missing_hint":"The saved source is currently unavailable. Keep it, disable the badge, or choose another value.","marker.value_badge_duplicate":"The same value is already shown inside the icon.","marker.value_badge_state":"State · {name}","marker.value_badge_marker_state":"Light-source state · {name}","marker.value_badge_lqi":"Average Zigbee signal quality","marker.value_badge_attr_current_temperature":"Current temperature · {name}","marker.value_badge_attr_temperature":"Target temperature · {name}","marker.value_badge_attr_current_humidity":"Current humidity · {name}","marker.value_badge_attr_humidity":"Humidity · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Speed · {name}","marker.value_badge_attr_brightness":"Brightness · {name}","marker.value_badge_attr_volume_level":"Volume · {name}","marker.value_badge_attr_battery_level":"Battery · {name}","marker.value_badge_attr_fan_speed":"Fan speed · {name}","marker.light_role_label":"Is this device a light source?","marker.light_role.help":"Controls whether this marker itself is a spatial light source. Linked lights above remain independent room-light controls.","marker.light_role.help.aria":"Help: whether this device is a light source","marker.light_role_auto_yes":"Auto (light source)","marker.light_role_auto_no":"Auto (not a light source)","marker.light_role_always":"Always a light source","marker.light_role_never":"Never a light source","marker.light_entity_label":"Leading light entity","marker.light_entity.help":"For a composite device, choose the entity whose state, colour and brightness represent this plan source. The automatic fallback remains compatible with older plans.","marker.light_entity.help.aria":"Help: leading light entity","marker.light_entity_auto":"Automatic ({entity})","marker.light_entity_none":"no controllable entity","marker.light_entity_missing":"The saved entity {entity} is unavailable. House Plan temporarily uses {fallback}; the saved choice will be restored if it returns.","marker.toggle_entity_label":"Entity to toggle","marker.toggle_entity.help":"For a composite device, choose the exact own light or switch operated by a tap. Automatic keeps the previous target rules and is independent from the leading light entity.","marker.toggle_entity.help.aria":"Help: entity to toggle","marker.toggle_entity_auto":"Automatic ({entity})","marker.toggle_entity_none":"no own controllable entity","marker.toggle_entity_missing":"The saved entity {entity} is no longer among this marker’s selectable channels. House Plan temporarily uses {fallback}; the saved choice will be restored if it returns.","marker.glow_color_label":"Glow colour and brightness","marker.glow_mode.help":"Use live source values, override only its colour, or fix both colour and brightness. The minimum is 1%; to disable the source, choose “Never”.","marker.glow_mode.help.aria":"Help: glow colour and brightness","marker.glow_mode_auto":"From source","marker.glow_mode_color":"Set colour","marker.glow_mode_fixed":"Set colour and brightness","marker.glow_color":"Glow colour","marker.glow_brightness":"Brightness","marker.glow_disabled_never":"Glow settings are unavailable because this marker is explicitly not a light source.","marker.glow_disabled_auto":"Auto mode found no spatial light source for this marker.","marker.glow_disabled_no_entity":"No active controllable Home Assistant entity is available for this spatial source.","marker.glow_passive_hint":"This source has no own Home Assistant data. Set its colour and brightness manually; its radius remains available.","marker.control_broken":"Saved source is missing or no longer marked as a light","marker.control_missing_label":"Missing plan light","marker.control_passive":"passive source","toast.marker_control_cycle":"This link would create a circular chain of light controls.","toast.marker_binding_required":"Choose a Home Assistant device before linking another light source.","confirm.unlock":"Unlock “{name}”?","toast.files_migrate_failed":"Attachments could not be moved to the new binding, links keep pointing at the old files: {err}","space.pick_saved":"Already uploaded","space.pick_saved_hint":"Plans stored on the server, including ones you detached earlier","space.no_saved":"No plans stored on the server yet.","space.loading":"Loading…","space.used_by":"in use: {list}","space.in_use":"A space still uses this plan — detach it first","btn.use":"Use","confirm.delete_plan":'Delete the plan file "{name}" from the server? This cannot be undone.',"toast.plans_list_failed":"Could not list the stored plans: {err}","toast.plan_delete_failed":"Could not delete the plan: {err}","marker.hide":"Hide","marker.hide_tip":'The device will disappear from the plan after saving but will still count toward the room signal. Restore it through the "Devices" catalog in the device editor.',"marker.show":"Show","marker.show_tip":"The device will appear on the plan again after saving.","marker.hidden_ghost":"Device hidden by the user","marker.ha_disabled_device":"The device is disabled in Home Assistant and hidden from the plan.","marker.ha_disabled_entity":"The entity is disabled in Home Assistant and hidden from the plan.","marker.ha_disabled_all_entities":"The device has no active Home Assistant entities, so it is hidden from the plan.","marker.ha_registry_limited":"The full Home Assistant registry is unavailable to this user. The unverified object cannot be shown or used for now.","marker.delete_tip":"Completely delete the device from the plan and every aggregate. You can add it again later.","tap.run":"Run automation/script/scene","tap.cover":"Open/close (curtains/blinds)","marker.run_target_label":"What to run","marker.run_search_ph":"Search: automation, script or scene…","marker.run_target_gone":"Target {id} not found — pick again","marker.tap_confirm":"Ask for confirmation","marker.tap_confirm_tip":"Show a confirmation dialog before acting — a guard against accidental taps.","marker.toggle_hint_single":"Target: {name} ({id}).","marker.virtual_light_target":"Virtual light: {name}.","marker.virtual_light_current":"Manual state: {state} → {effect}.","marker.virtual_light_state_on":"on","marker.virtual_light_state_off":"off","marker.toggle_hint_group":"Will toggle {count} source(s): {names}.","marker.toggle_hint_current":"Now: {state} → {effect}.","marker.toggle_hint_group_current":"Currently on: {on} of {count} → {effect}.","marker.toggle_hint_skipped":"Will be skipped ({count}): {targets}.","marker.toggle_effect_turn_on":"will turn on","marker.toggle_effect_turn_off":"will turn off","marker.toggle_effect_open":"will open","marker.toggle_effect_close":"will close","marker.toggle_effect_stop":"will stop","marker.toggle_effect_toggle":"state will toggle","marker.toggle_skip_missing":"not found","marker.toggle_skip_ha_disabled":"disabled in HA","marker.toggle_skip_unavailable":"unavailable","marker.toggle_skip_unsupported":"toggle is unsupported","marker.toggle_skip_secure":"blocked for security","marker.toggle_none_no_binding":"This device has no binding, so there is no state to toggle. Tapping will do nothing.","marker.toggle_none_no_actionable_entity":"This device has no state that can be toggled. Tapping will do nothing.","marker.toggle_none_configured_targets_missing":"The configured targets are unavailable. The device's own entity will not be substituted for them.","marker.toggle_none_ha_disabled":"The target is disabled in Home Assistant and cannot be used on the plan.","marker.toggle_none_unavailable":"The target is currently unavailable. Tapping will do nothing.","marker.toggle_none_unsupported":"Home Assistant exposes no safe toggle operation for this entity. Tapping will do nothing.","marker.toggle_none_secure":"Toggling locks, alarms and secure gates from the plan is blocked for security.","run.automation":"automation","run.script":"script","run.scene":"scene","confirm.tap_run":'Run "{name}"?',"confirm.tap_toggle":'Toggle "{name}"?',"confirm.tap_cover":'Open/close "{name}"?',"confirm.current_state":"Current state: {state}","confirm.expected_state":"After switching: {state}","confirm.group_current":"on {on} of {total}","confirm.group_all_on":"all are on","confirm.group_all_off":"all are off","confirm.unavailable_targets":"Unavailable: {count}","confirm.expected_by_ha":"Home Assistant will determine the state","confirm.state_on":"On","confirm.state_off":"Off","confirm.state_open":"Open","confirm.state_closed":"Closed","confirm.state_opening":"Opening","confirm.state_closing":"Closing","confirm.state_stopped":"Stopped","confirm.state_unknown":"Unknown","toast.run_started":"Started: {name}","toast.run_target_missing":"Run target not found — check the device settings","toast.run_target_required":"Pick an automation, script or scene","toast.tap_target_changed":"The action target changed. Try again.","toast.virtual_light_toggle_failed":"Could not toggle the virtual light: {err}","toast.value_badge_source_required":"Choose a value for the badge","btn.run":"Run","vac.section":"Robot vacuum: live position","vac.autocal":"Set up automatically","vac.live":"Live position on the plan","vac.trail":"Show the robot's path","vac.cal_maps":"Calibrated maps: {maps}","vac.autocal_no_rooms":"The integration reports no room list — open “Fit manually”","vac.autocal_no_match":"Room names did not match (need ≥3 in common) — open “Fit manually”","vac.autocal_done":"Done: bound via {rooms} rooms. Start a cleanup and check","vac.cal_need_pos":"The robot is not reporting coordinates — start a cleanup and pause it","vac.cal_done":"Calibration saved. Start a cleanup and check","vac.cal_cancelled":"Calibration cancelled","vac.fit":"Fit manually","vac.fit_hint":"Drag the robot map into place, stretch by the corners","vac.fit_rotate":"Rotate 90°","vac.fit_mirror":"Mirror","vac.trail_never":"Never","vac.trail_cleaning":"While cleaning","vac.trail_always":"Always","gs.bg_group":"Stage background","gs.bg_color":"Background around the plan","gs.bg_default":"Theme default","gs.bg_theme":"theme default","gs.bg_mode":"Plan background","gs.bg_static":"Static color","gs.bg_daynight":"Follows the Sun","gs.bg_daynight_hint":"The background moves through dawn, day, dusk and night using sun.sun, or your local clock when sun data is unavailable. No compass is needed for the background.","gs.sun_group":"Sun","gs.sun_missing":"The sun.sun entity was not found — the background follows your local clock; window rays are unavailable.","gs.north":"North on the plan","gs.north_ph":"not set","gs.north_hint":"Set north only for window rays (1° steps, 15° with Shift); the Sun background works without it.","gs.north_clear":"Clear","gs.north_letter":"N","gs.sun_rays":"Sunlight through windows","gs.about_group":"About","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · docs & issues","gs.about_telegram":"Telegram chat","space.bg_color":"Background around the plan","space.bg_inherit":"Inherit general","space.bg_inherited":"inherits general settings","space.bg_mode":"Plan background","space.north":"North on the plan (override)","space.north_inherited":"inherited: {v}","space.sun_rays":"Sunlight through windows","space.sun_inherit":"Inherit general","space.sun_on":"On","space.sun_off":"Off","canvas.far_objects":"{n} object(s) far from the plan","canvas.show_far":"Show","canvas.home_tip":"The plan is over there — click to fit it","gs.grid_group":"Plan maintenance","gs.grid_hint":"Updates data models, aligns plan elements to the grid and merges redundant wall fragments. An exact report is shown before anything is stored.","gs.align_all":"Optimize plans","gs.align_title":"Optimize plans","gs.align_none":"All plans already use the current optimized data model.","gs.optimize_no_automatic_changes":"There are no automatic changes to apply. Review the items below.","gs.align_count":"{n} of {total} elements will move, by at most {cm} cm.","gs.align_where":"The largest shift is in “{s}”.","gs.align_turned":"Openings whose angle is corrected: {n}.","gs.align_removed_drafts":"Invalid outlines collapsed by the grid and removed: {n}.","gs.optimize_redundant_drafts":"Saved wall chains hidden by solid room walls and removed: {n}.","gs.align_preflight_failed":"Could not safely verify the geometry of the following spaces: {spaces}{more}.","gs.align_preflight_hint":"Plans were not changed. Copy the diagnostics with the button below and attach them to the bug report together with a space export.","gs.preflight_reason_prepare-exception":"Could not prepare the space geometry (exception while building the model)","gs.preflight_reason_wall-null":"The wall body did not build (the union came back empty)","gs.preflight_reason_wall-degraded-extra":"The wall body degraded with extra geometry","gs.preflight_reason_wall-failed-core":"The wall body core failed to assemble","gs.preflight_reason_wall-exception":"Wall construction threw an exception","gs.preflight_reason_floor-null":"The floor outline did not build","gs.preflight_reason_floor-exception":"Floor construction threw an exception","gs.preflight_copy":"Copy diagnostics","gs.preflight_copied":"Diagnostics copied","gs.preflight_update_hint":"The card and integration versions differ — update House Plan and retry.","gs.align_preflight_space":"Space {n}","gs.align_preflight_more":", and {n} more","gs.optimize_changes":"Model migrations: {m}; spaces updated: {c}; noisy coordinate values removed: {p}; merged real-wall fragments: {w}; merged zero-thickness wall fragments: {s}; independent walls: {i}.","gs.zero_walls_migrated":"Virtual wall spans converted: {n}.","gs.wall_segments_migrated":"Wall segments stabilised: {n}.","gs.optimize_lattice_summary":"Noisy coordinate values canonicalized: {n}; maximum movement: {cm} cm.","gs.optimize_lattice_space":"{space}: coordinate values canonicalized: {n}; off-grid values left unchanged: {far}.","gs.optimize_coincident_partitions":"Hidden independent wall sections absorbed into room walls: {n}.","gs.optimize_openings_rehosted":"Openings reattached to room walls: {n}.","gs.optimize_walls_straightened":"Walls straightened: {n}; maximum movement: {cm} cm.","gs.optimize_walls_straightened_where":"Largest wall correction: {s}.","gs.optimize_walls_straighten_skipped":"Near-axis walls left unchanged because they could not be repaired safely: {n}.","gs.optimize_glow_migration":"Legacy Glow: {spaces} spaces → no data fill + independent Glow; {rooms} rooms → inherited data fill + independent Glow.","gs.optimize_references":"References repaired: spaces — {spaces}; rooms — {rooms}; positions — {positions}; devices detached from missing spaces — {detached}.","gs.optimize_reference_more":", and {n} more","gs.optimize_orphans_removed":"Forgotten records removed: {total} — room labels: {rooms}; devices: {devices}; group markers: {groups}. They belonged to spaces deleted earlier.","gs.optimize_live_positions":"Old positions in deleted spaces belong to existing objects: {n}{names}. They will be kept.","gs.optimize_live_positions_remove":"Old positions in deleted spaces belong to existing objects: {n}{names}. They are selected for removal.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Remove old positions","gs.optimize_live_keep":"Keep old positions","gs.optimize_live_selected":"Old positions will be removed after Optimize is applied.","gs.optimize_unverified":"Could not safely verify positions: {n}. They were left unchanged.","gs.optimize_registry_limited":"Full administrator access to the Home Assistant registries is required for a safe check.","gs.optimize_vacuum_warning":"Vacuum room mappings that still need review: {n}.","gs.optimize_details":"Details","gs.optimize_details_more":"And {n} more records.","gs.optimize_detail_removed":"will be removed","gs.optimize_detail_live":"will be kept","gs.optimize_detail_unverified":"not verified","gs.optimize_detail_room_label":"room label","gs.optimize_detail_device":"device","gs.optimize_detail_group":"group marker","gs.optimize_detail_unknown":"unknown owner","gs.optimize_detail_item":"{status}: {kind} {id}; old space {space}","gs.align_warn":"Elements deliberately placed between grid nodes will move. One undo is available after the operation, only until the next plan edit.","gs.align_run":"Optimize","gs.align_done":"Plans optimized: {n} elements moved, {m} records maintained, {r} references repaired","gs.optimize_undo":"Undo last optimization","gs.optimize_undone":"The last optimization was undone","decor.furniture":"Furniture","furn.title":"Furniture library","furn.symbol":"Symbol","furn.group_furniture":"Furniture","furn.group_appliance":"Appliances","furn.group_sanitary":"Plumbing","furn.group_other":"Other","furn.width":"Width","furn.depth":"Depth","furn.pick_hint":"Pick a symbol, then click on the plan.","furn.place_hint":"Click on the plan — the piece lands against the nearest wall. Shift places it free.","furn.sym_sofa":"Sofa","furn.sym_armchair":"Armchair","furn.sym_coffee_table":"Coffee table","furn.sym_table_dining":"Dining table","furn.sym_table_round":"Round table","furn.sym_chair":"Chair","furn.sym_desk":"Desk","furn.sym_bed_double":"Double bed","furn.sym_bed_single":"Single bed","furn.sym_nightstand":"Nightstand","furn.sym_wardrobe":"Wardrobe","furn.sym_bookshelf":"Bookshelf","furn.sym_fridge":"Fridge","furn.sym_stove":"Cooker","furn.sym_dishwasher":"Dishwasher","furn.sym_washer":"Washing machine","furn.sym_dryer":"Tumble dryer","furn.sym_tv":"TV","furn.sym_ac":"Air conditioner","furn.sym_water_heater":"Water heater","furn.sym_toilet":"Toilet","furn.sym_bathtub":"Bathtub","furn.sym_shower":"Shower","furn.sym_sink":"Washbasin","furn.sym_kitchen_sink":"Kitchen sink","furn.sym_bidet":"Bidet","furn.sym_stairs":"Stairs","furn.sym_fireplace":"Fireplace","furn.sym_plant":"Plant","furn.sym_rug":"Rug","common.yes":"Yes","common.no":"No","vac.diag_source":"Source","vac.diag_platform":"Integration","vac.diag_status":"Status","vac.diag_position":"Position","vac.diag_rooms":"Rooms","vac.diag_rooms_value":"{total} · {matched} names match · {readiness}","vac.autocal_ready":"auto-calibration available","vac.autocal_not_ready":"need 3 matching names","vac.diag_path":"Integration path","vac.diag_map":"Map ID","vac.source_none":"not selected","vac.source_status_ok":"Ready","vac.source_status_missing":"Missing","vac.source_status_disabled":"Disabled in Home Assistant","vac.source_status_unavailable":"Unavailable","vac.source_status_unverified":"Cannot verify with current permissions","vac.source_status_unsupported":"No position data","vac.source_status_none":"No source","vac.source_banner_missing":"The saved source no longer exists. It was not replaced automatically; choose another source or restore it in Home Assistant.","vac.source_banner_disabled":"The saved source is disabled in Home Assistant. Enable it there or choose another source.","vac.source_banner_unverified":"Current Home Assistant permissions cannot verify this saved source. It remains pinned and will not be replaced automatically.","vac.choose_source":"Choose source","vac.source_auto":"Automatic","vac.source_auto_hint":"Use a compatible entity from this device","vac.all_cameras":"All cameras","vac.all_cameras_warn":"A camera may not provide robot data. Choose one only if it is your vacuum map.","vac.all_cameras_empty":"No other camera entities found.","vac.platform_unknown":"unknown integration","vac.cap_position":"position","vac.cap_rooms_short":"rooms","vac.cap_path":"path","vac.cap_map":"map ID","vac.cap_none":"no robot data detected","vac.xcme_hint":"Enable these Xiaomi Cloud Map Extractor attributes:","vac.documentation":"Documentation","vac.residual_title":"Check automatic calibration","vac.residual_message":"The matched rooms disagree by up to {error}. Apply this approximate calibration, refine it manually, or cancel without changing the saved setup.","vac.apply_proposal":"Apply","gs.backup_group":"Backup and transfer","gs.backup_hint":"Download a portable JSON backup or preview and import a backup made by House Plan.","backup.export_open":"Export","backup.import_open":"Import","backup.export_title":"Export House Plan","backup.import_title":"Import House Plan","backup.export_hint":"Choose whether the backup should contain the whole House Plan configuration or only the current space.","backup.full":"Full backup","backup.current_space":"Current space","backup.current_space_title":"Current space: {title}","backup.no_current_space":"No current space","backup.plan_only":"Plan only","backup.plan_only_hint":"Keep rooms, walls, openings, decor and room-label positions without devices or Home Assistant bindings.","backup.plan_only_preview":"This file contains the plan only","backup.privacy_warning":"The archive keeps names, Home Assistant identifiers and exact coordinates. Internal plans and attachments are referenced, not embedded; runtime states and vacuum trails are not included.","backup.download":"Download JSON","backup.export_done":"Backup downloaded","backup.reading":"Checking the backup…","backup.revalidated":"The plan changed after this preview. The summary was refreshed; review it and confirm again.","backup.error.unauthorized":"You do not have permission to export or import this plan.","backup.error.not_ready":"House Plan is not ready yet. Try again in a moment.","backup.error.too_large":"The backup exceeds the 8 MiB limit.","backup.error.invalid_json":"The selected file is not valid JSON.","backup.error.invalid_format":"The selected file is not a House Plan backup.","backup.error.unsupported_export_version":"This backup format is not supported by the installed version.","backup.error.future_model":"The backup was created by a newer House Plan data model.","backup.error.invalid_config":"The backup contains an invalid House Plan configuration.","backup.error.wall_model_migration_blocked":"The backup wall model could not be upgraded safely. Optimize the source plan first, then export it again.","backup.error.invalid_layout":"The backup contains invalid object positions.","backup.error.invalid_content":"The backup contains invalid or inconsistent content references.","backup.error.space_not_found":"The selected space no longer exists.","backup.error.capacity_exceeded":"Adding this backup would exceed the plan limits.","backup.error.preview_expired":"The preview expired. Select the backup file again.","backup.error.preview_owner_mismatch":"This preview belongs to another Home Assistant user.","backup.error.conflict":"The plan changed after the preview. Review the refreshed summary.","backup.error.content_confirmation_required":"Confirm that unavailable local content may be detached.","backup.error.commit_failed":"The backup could not be applied safely. The previous plan was restored or is pending recovery.","backup.error.missing_plan":"A referenced plan file disappeared after the preview. Review the backup again.","backup.error.missing_content":"A referenced local attachment disappeared after the preview. Review the backup again.","backup.error.marker_control_missing":"A linked plan light is missing from the backup.","backup.error.marker_control_not_light":"A linked plan target is no longer marked as a light source.","backup.error.marker_control_self":"A light source cannot control itself.","backup.error.marker_control_cycle":"The backup contains a circular chain of light controls.","backup.error.duplicate_marker_control":"The backup contains a duplicate plan-light link.","backup.error.no_backup":"There is no import or optimization snapshot to restore.","backup.same_source":"Created on this Home Assistant instance","backup.foreign_source":"Created on another Home Assistant instance","backup.created":"Created: {value}","backup.versions":"Card {card}; integration {integration}; data model {model}","backup.count_spaces":"Spaces: {n}","backup.count_rooms":"Rooms: {n}","backup.count_walls":"Walls: {n}","backup.count_openings":"Openings: {n}","backup.count_decor":"Decor objects: {n}","backup.count_markers":"Devices: {n}","backup.count_layout":"Positions: {n}","backup.bindings":"Bindings — devices: {device}, entities: {entity}, virtual: {virtual}; positions without a space: {legacy}","backup.binding_status":"Target status — active: {active}, disabled: {disabled}, missing: {missing}","backup.missing_areas":"Areas missing on the target: {areas}","backup.dropped_marker_links":"Plan-light links outside this transferred space were omitted: {n}.","backup.repaired_target_refs":"Existing references restored by this space import: {n}.","backup.preserved_unresolved_refs":"References that could not be restored unambiguously were preserved: {n}.","backup.preserved_unresolved_hint":"No data was guessed or deleted. After the import, run Optimize plans to inspect the remaining references.","backup.import_details":"Import reference details","backup.import_detail.incoming_remapped":"References updated inside the imported copy: {n}","backup.import_detail.target_repaired":"Existing references restored: {n}","backup.import_detail.preserved_unresolved":"Unresolved references preserved: {n}","backup.import_detail.collisions":"Destination conflicts preserved safely: {n}","backup.import_detail.dropped_links":"Incoming links omitted by transfer rules: {n}","backup.import_detail.bounded_lineages":"Overly nested identifiers left bounded: {n}","backup.replace_warning":"This replaces the current configuration and layout. Uploaded files are never deleted. One undo remains available until the next plan edit.","backup.foreign_bookkeeping":"Instance-specific known/new-device bookkeeping will not be imported.","backup.final_name":"New space name","backup.target_settings":"The target instance's global settings remain unchanged.","backup.duplicates":"Bindings already present: {n}","backup.skip":"Skip duplicate devices","backup.virtual_copy":"Add safe static virtual copies","backup.content":"Referenced content","backup.content_available":"available locally","backup.content_external":"external link","backup.content_detach_required":"will be detached","backup.confirm_detach":"I understand that unavailable internal plans and attachments will be detached from the imported configuration.","backup.replace":"Replace","backup.add":"Add space","backup.space_done":"Space imported: {rooms} rooms, {markers} devices, {refs} existing references restored","backup.full_done":"Backup restored: {spaces} spaces, {rooms} rooms, {markers} devices","backup.undo_import":"Undo last full import","backup.import_undone":"Full import undone"};const aa=[{code:"en",nativeLabel:"English",dictionary:oa},{code:"ru",nativeLabel:"Русский",dictionary:{"editor.context_actions":"Действия: {object}","editor.tool_options":"Параметры инструмента: {tool}","editor.palette":"Палитра: {tool}","editor.open_group":"Группа инструментов: {group}","editor.group_active":"{group} — активно: {item}","editor.disabled_action":"{action} недоступно: {reason}","color_picker.title":"Выбор цвета","color_picker.hue":"Оттенок","color_picker.saturation":"Насыщенность","color_picker.value":"Яркость","color_picker.hex":"Цвет HEX","color_picker.invalid_hex":"Введите цвет HEX из 3 или 6 цифр","btn.properties":"Свойства","btn.keep_as_walls":"Оставить стенами","btn.delete_room_keep_walls":"Удалить комнату, оставить стены","btn.delete_room_with_walls":"Удалить комнату и стены","title.markup_select":"Выбор и редактирование стен, колонн и сохранённых контуров","title.markup_column":"Колонна: кликните точку сетки для квадратной колонны","markup.partition":"Перегородка","markup.column":"Колонна","markup.hint_column":"кликните точку сетки, чтобы поставить квадратную колонну","history.draft_segment":"Добавление сегмента черновика комнаты","history.draft_merge":"Соединение незавершённых контуров комнаты","history.draft_segment_delete":"Удаление сегмента черновика комнаты","history.partition_add":"Добавление перегородки","history.column_add":"Добавление колонны","history.physical_edit":"Изменение физического объекта","history.physical_delete":"Удаление физического объекта","history.physical_move":"Перемещение физического объекта","history.contour_to_partitions":"Преобразование контура в замкнутые стены","history.wall_chain_finish":"Завершение цепочки стен","history.wall_face_batch":"Создание комнат из стен","toast.column_duplicate":"Колонна с тем же центром и внешним размером уже существует","confirm.delete_draft":"Удалить весь незавершённый контур комнаты?","confirm.delete_draft_segment":"Удалить этот сегмент черновика? Оставшийся контур может разделиться на два.","physical.partition_properties":"Свойства перегородки","physical.column_properties":"Свойства колонны","physical.draft_properties":"Свойства сегмента черновика","physical.shape":"Форма","physical.square":"Квадрат","physical.circle":"Круг","physical.diameter":"Диаметр","physical.side":"Сторона","physical.rotation":"Угол поворота","physical.length":"Длина","physical.allowed_range":"Допустимо: {min}–{max} {unit}","physical.delete_segment":"Удалить сегмент","physical.delete_draft":"Удалить весь контур","physical.column_size_title":"Внешняя сторона квадратной колонны, создаваемой кликом (1–150 см).","card.title":"План дома","count.devices":"{n} устр.","empty.no_spaces":"Пространств пока нет.","empty.add_first":"Добавьте первое пространство и загрузите план этажа.","empty.install":"Установите интеграцию House Plan и добавьте запись в «Устройства и службы».","btn.add_space":"Добавить пространство","btn.cancel":"Отмена","btn.save":"Сохранить","btn.close":"Закрыть","btn.delete":"Удалить","btn.edit":"Редактировать","btn.open_in_ha":"Открыть в HA","btn.reset":"Сброс","btn.attach":"Прикрепить…","btn.upload":"Загрузить…","btn.replace":"Заменить…","title.zoom_in":"Приблизить","title.zoom_out":"Отдалить","title.zoom_fit":"Вписать всё","title.add_device":"Добавить устройство на план","title.show_all":"Показать скрытые и деактивированные устройства служебными маркерами (только в этой вкладке)","title.markup":"Разметка комнат: сетка, линии, контуры","title.configure_space":"Настроить пространство","title.add_space":"Добавить пространство","title.markup_add":"Стены: рисуйте непрерывную цепочку; Shift фиксирует 45°. Смена инструмента или выход завершает её как независимые стены","title.markup_merge":"Объединить: клик по одной комнате, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить комнату: кликните внутри комнаты и подтвердите удаление","title.need_plan":"Загрузите подложку (план этажа)","markup.add":"Стены","markup.merge":"Объединить","markup.split":"Разделить","markup.resize":"Размер","title.markup_resize":"Изменение размера комнат: тяните доступную ручку горизонтальной или вертикальной стены","markup.hint_resize":"тяните доступную ручку стены · стена упирается в первую небезопасную позицию · Esc — отмена · Ctrl+Z — отмена шага","resize.disabled.diagonal":"Стена расположена под углом: Resize поддерживает только горизонтальные и вертикальные стены","resize.disabled.side-angle":"Примыкающая стена расположена под углом: для Resize обе примыкающие стены должны образовывать прямой угол","resize.disabled.duplicate-physical-wall":"Стену перекрывает отдельная перегородка, незавершённый контур или колонна; удалите или переместите её перед изменением размера","resize.disabled.partial-shared":"Соседняя комната использует только часть этой стены, поэтому её нельзя безопасно двигать как одну общую стену","resize.disabled.unequal-shared":"У соседней комнаты другие конечные точки или длина стены, поэтому стены нельзя безопасно двигать вместе","resize.disabled.multiple-rooms":"Перемещение затронуло бы больше двух комнат","resize.disabled.thickness-conflict":"Толщину стены нельзя безопасно сохранить","resize.disabled.opening-conflict":"Проём на этой стене нельзя безопасно переместить","resize.disabled.invalid-geometry":"Эту стену нельзя переместить без изменения структуры плана","resize.limit_stopped":"Стена остановлена: дальше нарушается ограничение стыков","resize.commit_failed":"Изменение размера отменено: итоговый план не прошёл проверку геометрии","resize.preview_failed":"Стена остановлена в последней безопасной позиции: геометрию плана нельзя сохранить без потерь","markup.opening":"Проём","markup.delete_room":"Удалить комнату","history.undo":"Отменить","history.redo":"Повторить","history.undo_named":"Отменить: {name} (Ctrl+Z)","history.redo_named":"Повторить: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Нет операций для отмены","history.redo_empty":"Нет операций для повтора","history.undone":"Отменено: {name}","history.redone":"Повторено: {name}","history.add_room":"Создание комнаты","history.split_room":"Разделение комнаты","history.merge_rooms":"Объединение комнат","history.resize_room":"Изменение размера комнаты","history.wall_thickness":"Изменение толщины стены","history.add_opening":"Добавление дверного или оконного проёма","history.edit_opening":"Изменение проёма","history.move_opening":"Перемещение проёма","history.delete_opening":"Удаление проёма","history.delete_room":"Удаление комнаты","history.delete_room_keep_walls":"Удаление комнаты с сохранением стен","history.delete_room_with_walls":"Удаление комнаты и её стен","history.decor_add":"Добавление объекта декора","history.decor_edit":"Изменение объекта декора","history.decor_move":"Перемещение объекта декора","history.decor_transform":"Трансформация объекта декора","history.decor_delete":"Удаление объекта декора","history.backdrop_transform":"Трансформация подложки плана","title.markup_opening":"Проёмы: клик по стене — добавить, клик по проёму — редактировать","opening.new":"Новый проём","opening.edit":"Проём","opening.door":"Дверь","opening.window":"Окно","opening.gate":"Ворота","opening.passage":"Открытый проём","opening.passage_binding_warning":"При сохранении датчик открытия и замок будут удалены.","opening.invalid_passage_fields":"У открытого проёма на плане «{room}» есть недопустимые параметры: {fields}.","opening.type_label":"Тип","opening.length_label":"Длина, см","opening.contact_label":"Датчик открытия","opening.lock_label":"Замок","opening.none":"— нет —","opening.search_ph":"Поиск: имя или entity_id…","opening.invert":"Инвертировать открыто/закрыто","opening.flip_h":"Петли с другой стороны","opening.flip_v":"Открывается в другую сторону","opening.open":"Открыто","opening.closed":"Закрыто","opening.locked":"Заперто","opening.unlocked":"Не заперто","opening.state_unknown":"недоступно","opening.no_entities":"Датчики не привязаны — статичный символ на плане.","toast.opening_no_wall":"Кликните рядом со стеной комнаты или независимой стеной","opening.host_partition":"Независимая стена","opening.partition_orphan":"Независимая стена этого проёма больше не существует","opening.partition_jamb_margin":"Оставьте от края проёма до торца стены минимум {distance}","opening.rebind_partition":"Привязать к другой независимой стене","confirm.delete_partition_openings_title":"Удалить стену и проёмы?","confirm.delete_partition_openings_body":"В стене есть проёмы: {count}. Они будут удалены вместе со стеной.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Удалить","markup.hint_points":"точек: {n} · Shift — шаг 45° · Esc — завершить цепочку · Ctrl+Z — убрать точку · при замыкании будет предложена комната","markup.hint_start":"кликните точку сетки, чтобы начать цепочку стен","tip.lqi":"средний сигнал zigbee:","tip.area":"площадь: {value}","info.device_header":"Устройство на плане","info.model":"Модель","info.state":"Состояние","info.link":"Ссылка","info.manuals":"Инструкции","info.none":"Нет дополнительной информации","marker.new_device":"Новое устройство","marker.name_label":"Имя (отображается на плане)","marker.name_ph":"Название","marker.binding_label":"Привязка к устройству HA","marker.binding_disabled":"деактивировано в Home Assistant","marker.virtual_option":"Виртуальное устройство (без привязки)","marker.search_ph":"Поиск устройства / группы…","marker.nothing_found":"ничего не найдено","marker.room_label":"Комната","marker.room_override":" (переопределить размещение)","marker.room_choose":"— выберите комнату —","marker.room_auto":"— по зоне устройства (авто) —","marker.icon_label":"Иконка","marker.icon_ph":"mdi:… (пусто = авто)","marker.display_label":"Отображение","display.badge":"Значок + состояние","display.icon_ripple":"Значок + состояние и активность","display.static_icon":"Всегда статичный значок","marker.display_hint":"Значок + состояние меняет подложку при работе, открытии, тревоге и недоступности. Значок + состояние и активность дополнительно показывает короткую пульсацию для событий и постоянную — для работы, движения или присутствия. Значение + состояние заменяет значок одним однозначным значением HA. Статичный значок не реагирует на состояния устройства.","marker.display_hint_badge":"Значок и динамическая подложка показывают состояние устройства без обычной пульсации активности. Красная тревога сохраняется.","marker.display_hint_icon":"Значок и динамическая подложка показывают состояние устройства без обычной пульсации активности. Красная тревога сохраняется.","marker.display_hint_icon_ripple":"Значок, динамическая подложка и пульсация: короткая — для событий, постоянная — для работы, движения или присутствия. Красная тревога имеет отдельный приоритет.","marker.display_hint_value":"Значок заменяется одним однозначным значением Home Assistant; подложка продолжает показывать состояние. Красная тревога сохраняется.","marker.display_hint_static_icon":"Подложка в цветах темы и значок всегда остаются одинаковыми. Состояния, активность, недоступность и тревоги не меняют отображение.","marker.static_alarm_warning":"Это устройство может сообщать о тревогах. Статичный режим скрывает визуальную тревогу маркера.","marker.preview.title":"Предпросмотр отображения","marker.preview.actual":"Сейчас","marker.preview.example":"Пример","marker.preview.integration":"Интеграция","marker.preview.source":"Источник отображения","marker.preview.current_state":"Текущее состояние","marker.preview.result":"На плане","marker.preview.details":"Подробнее об источниках","marker.preview.select_source":"Выберите устройство или сущность Home Assistant, чтобы увидеть фактическое отображение.","marker.preview.unknown_provider":"Интеграция неизвестна","marker.preview.virtual_provider":"House Plan · виртуальное устройство","marker.preview.no_source":"Нет активного источника","marker.preview.no_state":"Нет текущего состояния","marker.preview.mixed_states":"Несколько разных состояний","marker.preview.multiple_sources":"Источников: {n}","marker.preview.more_sources":"ещё {n}","marker.preview.scaled":"Предпросмотр уменьшен до {n}%, чтобы поместиться. Сохранённый размер не изменится.","marker.preview.demo_activity":"Показать пример активности","marker.preview.demo_notice":"Временный пример активности; фактическое состояние устройства не меняется.","marker.preview.demo_short":"Показать краткую активность","marker.preview.demo_continuous":"Показать постоянную активность","marker.preview.stop_continuous":"Остановить постоянную активность","marker.preview.demo_short_notice":"Пример краткой активности; фактическое состояние устройства не меняется.","marker.preview.demo_continuous_notice":"Пример постоянной активности; фактическое состояние устройства не меняется.","marker.preview.demo_already_visible":"Текущее состояние уже показывает реальную активность или тревогу.","marker.preview.reduced_motion":"В системе включено уменьшение движения, поэтому обычная активность показана точкой.","marker.preview.reason.neutral":"Нейтральная тёмная подложка","marker.preview.reason.working":"Жёлтая подложка: устройство сейчас работает","marker.preview.reason.working_activity":"Жёлтая подложка и эффект активности: устройство сейчас работает","marker.preview.reason.open":"Оранжевая подложка: физически открыто или разблокировано","marker.preview.reason.cover_icon_state":"Нейтральная подложка; состояние и значок определяет сущность шторы или заслонки","marker.preview.reason.presence":"Эффект активности, пока обнаружено присутствие","marker.preview.reason.event":"Короткий эффект активности после события","marker.preview.reason.transition":"Эффект активности во время движения или смены состояния","marker.preview.reason.media_neutral":"Медиаустройство остаётся на нейтральной тёмной подложке, пока доступно","marker.preview.reason.unavailable":"Приглушённая нейтральная подложка: отключено или недоступно","marker.preview.reason.alarm":"Красная тревожная подложка; тревога отображается всегда","marker.preview.reason.live_states_disabled":"Оформление по живым состояниям отключено в настройках карточки","marker.preview.reason.value_no_state":"Нет подходящего значения состояния — вместо него показан значок","marker.preview.reason.value_ambiguous_sources":"Подходит несколько источников значения — вместо него показан значок","marker.preview.reason.value_non_scalar":"Источник вернул не простое значение — вместо него показан значок","marker.preview.reason.value_virtual":"У виртуального устройства нет значения HA — вместо него показан значок","marker.preview.reason.vacuum_live_plan_only":"Живая позиция и след пылесоса доступны только на полном плане","marker.preview.reason.hidden_design_preview":"Устройство скрыто на плане, но его оформление видно в предпросмотре","marker.preview.reason.composite_power_source":"Состояние берётся из сущности питания устройства; вспомогательные переключатели не учитываются","marker.preview.reason.activity_display_disabled":"Выбранный режим отображения не показывает обычные эффекты активности","marker.preview.reason.ha_disabled":"Привязка отключена в Home Assistant, поэтому устройство будет скрыто на плане","marker.preview.reason.orphaned":"Сохранённая привязка Home Assistant больше не найдена","marker.preview.reason.static_icon":"Статичный режим: состояние устройства не меняет значок","marker.activity_color":"Цвет пульсации активности","marker.ripple_size":"Размер пульсации активности","marker.activity_alarm_note":"Цвет и размер относятся только к обычной активности и не влияют на красную тревогу.","marker.pulse_a11y_alarm":"Тревога","marker.pulse_a11y_event":"Недавнее событие","marker.pulse_a11y_presence":"Обнаружено присутствие","marker.pulse_a11y_transition":"Изменение состояния","marker.pulse_a11y_running":"Работает","marker.state_a11y_neutral":"Обычное состояние","marker.state_a11y_working":"Работает","marker.state_a11y_open":"Открыто","marker.state_a11y_locked":"Заблокировано","marker.state_a11y_unlocked":"Разблокировано","marker.state_a11y_alarm":"Тревога","marker.state_a11y_unavailable":"Недоступно","marker.lqi_a11y_low":"LQI {value}, слабый сигнал","marker.lqi_a11y_mid":"LQI {value}, средний сигнал","marker.lqi_a11y_high":"LQI {value}, сильный сигнал","marker.size_label":"Размер / поворот значка","marker.angle_label":"Поворот","marker.model_label":"Модель","marker.model_ph":"напр. Aqara T&H","marker.link_label":"Ссылка","marker.desc_label":"Описание","marker.desc_ph":"Заметки, характеристики…","marker.manuals_label":"Инструкции (PDF и т.п.)","marker.sub_device":"устройство","marker.sub_z2m_group":" · Z2M-группа","marker.sub_group":"группа","marker.sub_helper":"хелпер","space.new":"Новое пространство","space.header":"Пространство","space.title_label":"Название","space.title_ph":"Например: Гараж","space.plan_label":"Подложка (план)","space.no_plan":"нет подложки","space.plan_alt":"план","room.new":"Новая комната","room.name_label":"Отображаемое имя","room.name_ph":"Например: Терраса","room.area_label":"Зона Home Assistant (свободные)","room.no_area_option":"— без зоны —","room.default_name":"Комната","device.unnamed":"без имени","device.light_group":"группа света","device.fallback":"устройство","device.virtual":"виртуальное устройство","confirm.delete_room":"Удалить комнату «{name}»?","confirm.delete_room_title":"Удалить комнату «{name}»?","confirm.delete_room_body":"Выберите, нужно ли оставить физические стены, принадлежащие только этой комнате. Общие с другой комнатой стены сохраняются всегда.","confirm.merge_rooms":"Слить комнаты «{a}» и «{b}»?","confirm.remove_marker":"Удалить «{name}» с плана? Устройство исчезнет полностью и перестанет участвовать в данных плана. Позже его можно будет добавить заново.","confirm.erase_decor":"Стереть объект «{kind}»? Действие можно отменить из истории редактора.","confirm.delete_space":"Удалить пространство «{title}» со всеми комнатами и разметкой?","space.delete_blocked":"Это пространство всё ещё используется устройствами: {n}. Сначала перенесите их в другое пространство или удалите.","toast.pos_save_failed":"Не удалось сохранить позицию: {err}","toast.no_entity":"У устройства нет подходящей сущности","toast.ha_disabled_action":"Деактивированный объект Home Assistant нельзя использовать на плане.","toast.ha_disabled_show_device":"Деактивированное в Home Assistant устройство нельзя показать на плане. Сначала активируйте его в Home Assistant.","toast.ha_disabled_show_entity":"Деактивированную в Home Assistant сущность нельзя показать на плане. Сначала активируйте её в Home Assistant.","toast.ha_disabled_add":"Деактивированный объект Home Assistant нельзя добавить на план. Сначала активируйте его в Home Assistant.","toast.ha_binding_unverified":"Статус объекта не удалось подтвердить по реестру Home Assistant. Отображение и действия временно недоступны.","toast.markup_needs_server":"Разметка доступна после переноса конфига на сервер","toast.space_order_changed":"Порядок изменён. Если где-то этаж карточки задан номером, проверьте такие панели.","toast.conflict":"Конфиг изменён в другом окне — данные обновлены, повторите последнее действие","toast.cfg_save_failed":"Не удалось сохранить конфиг: {err}","toast.room_overlap":"Контур накладывается на комнату «{name}» — комнаты не должны накладываться","toast.contour_min_edges":"Чтобы замкнуть контур, сначала нарисуйте минимум две грани","toast.contour_cannot_close":"Контур нельзя замкнуть: он вырожден или пересекает сам себя","toast.merge_not_adjacent":"Объединять можно только комнаты с общей стеной","toast.rooms_merged":"Комнаты объединены в «{name}»","toast.split_pick_wall":"Начните разрез на стене комнаты","toast.split_bad_cut":"Разрез — от стены до стены внутри комнаты, без пересечения стен и самого себя","merge.header":"Объединение комнат","merge.hint":"У объединённой комнаты одно имя и одна зона. Вторая зона освобождается — её устройства уйдут с плана, пока их не заберёт другая комната.","merge.keep":"Оставить","merge.no_area":"без зоны","room.split_header":"Новая комната после разделения","toast.room_saved":"Комната сохранена ({n}). Устройств добавлено: {added}. Обведите следующую или выйдите из разметки.","toast.room_saved_no_area":"Комната сохранена ({n}, без зоны). Обведите следующую или выйдите из разметки.","toast.marker_needs_server":"Редактирование устройств доступно после переноса конфига на сервер","toast.virtual_name_required":"Укажите имя виртуального устройства","toast.marker_saved":"Устройство сохранено","toast.marker_removed":"Устройство удалено с плана","toast.integration_missing":"Интеграция House Plan не установлена — управление недоступно","toast.plan_formats":"Поддерживаются SVG, PNG, JPG, WebP","toast.plan_required":"Загрузите подложку — план этажа обязателен","toast.space_added_onboard":"Пространство добавлено. Обведите комнаты: кликайте по точкам сетки и замкните контур.","toast.space_added":"Пространство добавлено","toast.space_saved":"Пространство сохранено","toast.space_deleted":"Пространство удалено","toast.delete_failed":"Ошибка удаления: {err}","toast.error":"Ошибка: {err}","toast.toggle_target_unavailable":"Цель «{name}» недоступна — действие не выполнено","toast.toggle_targets_unavailable":"Цели недоступны: {names}. Действие не выполнено","toast.file_failed":"Файл «{name}» не загружен: {err}","toast.files_attached":"Прикреплено файлов: {n}","err.unknown":"неизвестная ошибка","err.code":"код {code}","err.too_large":"файл больше {mb} МБ","err.bad_ext":"недопустимый тип (нужен PDF/изображение)","err.unauthorized":"нужны права администратора","editor.title":"Заголовок","editor.default_floor":"Стартовое пространство","editor.default_floor_missing":"Стартовое пространство «{id}» больше не существует. Выберите другое.","editor.floor":"Закреплённое пространство","editor.floor_none":"— не закреплять —","editor.floor_index":"YAML-индекс {index} (сохранён)","fixed_floor.loading":"Загружаем закреплённое пространство…","fixed_floor.invalid_title":"Закреплённое пространство недоступно","fixed_floor.invalid_body":"Проверьте floor в конфигурации карточки. Настроенное значение: {value}","editor.icon_size":"Размер иконок, % ширины плана","editor.show_temperature":"Показывать температуру","editor.live_states":"Живые состояния (вкл/выкл, открыто…)","editor.show_signal":"Показывать сигнал zigbee (LQI)","editor.language":"Язык интерфейса","editor.lang_auto":"Авто (профиль HA)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Правила иконок: какая MDI-иконка достаётся устройству по имени","rules.title":"Правила иконок","rules.hint":"Правила проверяются сверху вниз по строке «имя устройства + модель» (regex без учёта регистра); срабатывает первое совпадение. Если ничего не подошло — решает device class сущности, затем — иконка-заглушка.","rules.pattern_ph":"regex, напр. розетк|plug","rules.icon_ph":"mdi:power-socket-de","rules.add":"Добавить правило","rules.reset":"Сбросить к умолчаниям","rules.test_ph":"Проверьте имя устройства…","rules.invalid":"некорректный regex","rules.saved":"Правила иконок сохранены","btn.up":"Вверх","btn.down":"Вниз","tap.info":"Карточка устройства","tap.more_info":"Диалог HA (more-info)","tap.toggle":"Переключить состояние","marker.tap_label":"Действие по нажатию для этого устройства","tap.toggle_note":"Под селектором показано, какое состояние изменится. Защищённые устройства не переключаются с плана.","import.title":"Создать пространства из этажей HA","import.hint":"Home Assistant уже знает эти этажи. Отметьте, какие превратить в пространства плана — далее для каждого попросим картинку плана. Комнаты затем обводятся вручную по плану.","import.start":"Создать: {n}","import.manual":"Начать с нуля","import.progress":"Этаж {i} из {n}","import.done":"Пространства созданы. Обведите комнаты: кликайте по точкам сетки и замкните контур.","btn.skip":"Пропустить","space.scale_label":"Масштаб (размер клетки сетки)","space.scale_unit":"см на клетку","space.scale_unit_imperial":"дюйм на клетку","space.display_section":"Отображение","space.show_borders":"Всегда отображать границы комнат","space.zero_wall_style":"Стены нулевой толщины","space.zero_wall_dashed":"Пунктирные","space.zero_wall_solid":"Сплошные","space.zero_wall_help":"Пунктирные стены пропускают свет. Сплошные стены блокируют свет, даже при нулевой толщине.","space.show_names":"Отображать названия комнат (перетаскиваются)","space.room_color":"Цвет границ и названий","space.opacity":"Прозрачность","space.fill_label":"Заливка комнат","fill.none":"Нет","fill.lqi":"По силе зигби-сигнала","fill.light":"По освещению","fill.custom":"Свой цвет","space.custom_fill":"Цвет заливки","space.source_file":"У меня есть картинка плана","space.source_draw":"Нет подложки — нарисую комнаты вручную","space.orientation":"Холст","orient.landscape":"Альбомный","orient.portrait":"Портретный","orient.square":"Квадрат","fill.temp":"По температуре","space.temp_min":"Комфорт от","space.temp_max":"до","tip.temp_avg":"средняя температура:","tip.hum_avg":"средняя влажность:","space_card.button":"Перейти к пространству","space_card.not_found":"Пространство «{id}» не найдено","space_card.loading":"Загрузка…","continuity.restore_plan":"Восстанавливаем план…","continuity.restore_connection":"Восстанавливаем подключение к устройствам…","continuity.retry":"Повторить","editor.space":"Пространство","editor.show_button":"Показывать кнопку","editor.button_label":"Текст кнопки","editor.button_target":"Путь дашборда (куда вести)","marker.sub_entity":"сущность","title.general_settings":"Общие настройки","gs.title":"Общие настройки","gs.hint":"Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.","gs.light_group":"Заливка: освещение","gs.light_on":"Свет включён","gs.light_off":"Весь свет выключен","gs.temp_group":"Заливка: температура","gs.temp_cold":"Холодно","gs.temp_ok":"Комфорт","gs.temp_hot":"Жарко","gs.lqi_group":"Заливка: зигби-сигнал","gs.lqi_low":"Слабый сигнал","gs.lqi_high":"Сильный сигнал","gs.reset":"Сбросить к умолчаниям","gs.saved":"Общие настройки сохранены","space.show_lqi":"Показывать зигби-сигнал (LQI) у устройств","space.hide_decor":"Скрыть декоративный слой","space.hide_decor_tip":"Линии, фигуры, надписи и мебель остаются на месте — их видно в редакторе подложки, но не на плане.","space.hide_openings":"Скрыть проёмы","space.hide_openings_tip":"Двери, окна и ворота не рисуются, но продолжают работать: свет проходит, солнце светит в окна, датчики открытия срабатывают. В редакторе плана проёмы видно всегда.","gs.light_none":"Нет источников света","mode.plan":"Редактор плана","mode.devices":"Редактор устройств","display.value":"Значение + состояние","marker.subarea":"без зоны, вручную","device.new":"Новое устройство — откройте его редактор, чтобы снять отметку","opening.unlock_action":"Открыть замок","opening.lock_action":"Закрыть замок","opening.lock_pending":"Выполняется…","title.close_editor":"Закрыть редактор (вернуться к просмотру)","devbar.add":"Добавить","devbar.show_all":"Скрытые и деактивированные","devbar.rules":"Правила иконок","device_inbox.button":"Устройства","device_inbox.title":"Устройства на плане","device_inbox.search":"Поиск по устройствам, сущностям и интеграциям…","device_inbox.add_virtual":"Добавить виртуальное устройство","device_inbox.tab_on_plan":"На плане","device_inbox.tab_available":"Доступны","device_inbox.tab_hidden":"Скрытые","device_inbox.tab_readd":"Доступны снова","device_inbox.only_new":"Только новые","device_inbox.show_entities":"Показывать сущности","device_inbox.show_hidden":"Показывать скрытые на плане","device_inbox.new":"Новое","device_inbox.find":"Найти на плане","device_inbox.edit":"Настроить","device_inbox.hide":"Скрыть","device_inbox.show":"Показать","device_inbox.add":"Добавить","device_inbox.readd":"Добавить заново","device_inbox.hide_available":"Скрыть из списка","device_inbox.show_more":"Показать ещё","device_inbox.more_actions":"Другие действия","device_inbox.find_hidden_hint":"Сначала включите «Показывать скрытые на плане»","device_inbox.show_disabled":"Перед показом активируйте эту привязку в Home Assistant","device_inbox.empty_on_plan":"На плане пока нет подходящих устройств.","device_inbox.empty_available":"Нет доступных устройств для добавления.","device_inbox.empty_hidden":"Скрытых устройств нет.","device_inbox.empty_readd":"Нет удалённых устройств, доступных для повторного добавления.","device_inbox.reason_visible_auto":"Найдено автоматически","device_inbox.reason_visible_explicit":"Добавлено явно","device_inbox.reason_manual_hidden":"Скрыто пользователем","device_inbox.reason_automatic_hidden":"Скрыто автоматически","device_inbox.reason_service_entry":"Служебная запись реестра","device_inbox.reason_excluded_integration":"Интеграция исключена фильтрами устройств","device_inbox.reason_excluded_domain":"Непространственный тип сущности","device_inbox.reason_grouped_light":"Представлено группой света комнаты","device_inbox.reason_represented_by_parent":"Представлено родительским устройством","device_inbox.reason_removed":"Ранее удалено с плана","device_inbox.reason_available":"Можно добавить на план","device_inbox.reason_no_bound_room":"Зона HA не связана с комнатой плана","device_inbox.status_ha_disabled":"Отключено в Home Assistant","device_inbox.status_orphaned":"Привязка больше не найдена в Home Assistant","device_inbox.status_unverified":"Реестр Home Assistant временно недоступен","device_inbox.saved":"Список устройств обновлён","space.roomcard_section":"В карточке комнаты:","space.label_temp":"Температура","space.label_hum":"Влажность","space.label_lqi":"Средний Zigbee-сигнал","space.label_light":"Свет вкл/выкл","roomcard.light_on":"Вкл","roomcard.light_off":"Выкл","roomcard.light_partial":"{on} из {total}","toast.split_pick_inside":"Промежуточные точки разреза — внутри комнаты","mode.decor":"Редактор подложки","decor.select":"Выбрать","decor.line":"Линия","decor.rect":"Прямоугольник","decor.ellipse":"Овал","decor.text":"Надпись","decor.erase":"Стереть","decor.erase_confirm_title":"Стереть объект?","decor.color":"Цвет","decor.width":"Толщина линии","decor.line_style":"Стиль линии","decor.line_style_solid":"Сплошная","decor.line_style_dashed":"Пунктирная","decor.fill":"Заливка","decor.fill_color":"Цвет заливки","decor.length":"Длина","decor.size":"Размер","decor.angle":"Угол поворота, °","decor.text_size":"Размер текста","decor.backdrop_properties":"Свойства картинки-подложки","decor.text_title":"Надпись","decor.object_title":"Редактирование: {kind}","decor.text_label":"Текст","decor.live_group":"Вставить переменную HA","decor.live_entity":"Сущность","decor.live_entity_ph":"выберите сущность","decor.live_attr":"Значение","decor.live_attr_ph":"выберите состояние или атрибут","decor.live_state":"Состояние","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — перемещение; угол — размер; верхнюю ручку — поворот. Shift меняет пропорции или снимает шаг угла.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","marker.icon_pin_auto":"Закрепить","mode.plan_tip":"Редактор плана — геометрия дома: рисование и объединение/разделение комнат, привязка к зонам HA, двери, окна и ворота, карточки комнат, масштаб","mode.devices_tip":"Редактор устройств — всё про значки: перетаскивание, клик — настройка привязки/иконки/отображения, виртуальные устройства, правила иконок","mode.decor_tip":"Редактор подложки — чисто визуальный декор под планом: линии, прямоугольники, овалы и надписи, не реагирующие на клики","space.glow_enabled":"Свечение источников света","gs.glow_group":"Свечение источников света","gs.glow_base":"Темнота дома","gs.glow_light":"Цвет света по умолчанию / интенсивность","gs.wall_group":"Стены","gs.wall_fill":"Цвет заливки стен","gs.glow_radius":"Радиус свечения","gs.unit_m":"м","gs.unit_ft":"фут","marker.controls_label":"Управляет другими источниками света","marker.controls_hint":"При действии «Переключить» клик разом переключает все добавленные здесь источники. Собственная сущность маркера управляется напрямую и сюда не добавляется; чтобы считать её светом, включите флаг ниже.","marker.controls_filter":"Поиск ламп и выключателей…","info.controls":"Управляет","marker.glow_radius_label":"Радиус свечения","marker.glow_radius.help":"Задаёт радиус свечения в метрах или футах; пустое значение использует радиус из общих настроек.","marker.glow_radius.help.aria":"Подсказка: радиус свечения","markup.wallthick":"Толщина","markup.select":"Выбрать","title.markup_wallthick":"Толщина — клик по стене задаёт толщину от 0 до 100 см.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены (0–100 см). Общие стены сохраняют существующее значение.","room.queue_progress":"Комната {current} из {total}","toast.wall_rooms_saved":"Создано комнат: {n}","toast.wall_chain_saved":"Цепочка стен сохранена","toast.wallthick_pick":"Кликните по стене","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от {min} до {max} {unit}","toast.zero_wall_opening_conflict":"Сначала удалите проём на этом участке стены.","toast.zero_wall_ambiguous":"Не удалось однозначно выбрать участок стены. Уточните геометрию узла.","toast.zero_wall_migration_blocked":"Пространство не преобразовано: {reason}. Данные не изменены.","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","toast.geometry_unsafe":"Изменение отменено: геометрию стен нельзя безопасно построить.","junction.limit_angle":"Слишком острый угол между стенами: {actual}°, минимум {limit}°.","junction.limit_valence":"Слишком много стен в одном узле: {actual}, максимум {limit}.","junction.limit_length":"Слишком короткий участок стены: {actual} см, минимум {limit} см.","junction.limit_distance":"Стены и узлы слишком близко: {actual} см, минимум {limit} см.","junction.limit_clearance":"Внутри комнаты не остаётся места: {actual} см², минимум {limit} см².","toast.wall_model_migration_blocked":"Не удалось обновить модель стен: {reason}. План не изменён. Запустите «Оптимизировать планы»; если ошибка повторится, исправьте конфликтующую геометрию стен.","toast.wall_model_client_outdated":"Обновите карточку и перезагрузите страницу перед редактированием плана.","wall_model.reason.invalid-room":"некорректный контур комнаты","wall_model.reason.zero-length":"отрезок стены нулевой длины","wall_model.reason.third-owner":"стена принадлежит более чем двум комнатам","wall_model.reason.duplicate-id":"конфликт идентификаторов стен","wall_model.reason.thickness-conflict":"конфликт значений толщины стены","wall_model.reason.opening-host":"проём нельзя однозначно привязать к стене","toast.delete_room_pick":"Кликните внутри комнаты, которую нужно удалить","toast.plan_snap_ambiguous":"Увеличьте масштаб, чтобы выбрать узел стены","toast.wall_repair_ambiguous":"У контура несколько возможных соединений. Увеличьте масштаб и соедините стены явно.","toast.wall_repair_too_large":"Разрыв больше 2 см. Соедините стены перед созданием комнаты.","toast.wall_repair_changed":"Геометрия стен изменилась. Попробуйте создать комнату ещё раз.","toast.opening_on_zero_wall":"Проёмы на стене нулевой толщины запрещены","marker.from_ha_option":"Выбрать из списка HA","marker.show_entities":"Показывать сущности","marker.show_entities_tip":"Добавляет в список не только устройства, но и все их сущности","marker.pick_ph":"Выберите устройство…","room.open_area":"Открыть зону в HA","view.volumetric":"Объёмный вид","view.flat":"Плоский вид","kiosk.title":"Размеры на этом экране","kiosk.hint":"Хранится только на этом устройстве — у каждого настенного планшета или ТВ свои удобные размеры.","kiosk.icon_scale":"Размер значков устройств","kiosk.font_scale":"Размер текста карточек комнат","editor.kiosk":"Режим настенного устройства (киоск)","editor.cycle":"Автосмена пространств каждые N секунд (киоск, 0 = выкл)","room.settings_title":"Настройки комнаты","room.settings_section":"Настройки комнаты (переопределяют пространство)","room.fill_label":"Заливка в ЭТОЙ комнате","fill.inherit":"Как у пространства","room.custom_fill_space":"Цвет пространства","room.custom_fill_own":"Цвет комнаты","room.temp_src_label":"Источник температуры","room.hum_src_label":"Источник влажности","room.src_average":"Средняя по датчикам комнаты (по умолчанию)","room.src_pick":"Конкретное устройство или сущность HA","room.src_ph":"Выберите источник…","toast.room_updated":"Комната обновлена","space.card_font":"Размер шрифта карточек комнат (всё пространство)","room.sizes_section":"Размеры шрифтов","room.name_scale":"Размер названия","room.label_scale":"Размер подписей","preview.room_name":"Гостиная","toast.cfg_reload_failed":"Не удалось перечитать план с сервера: {err}","room.settings_short":"Настройки комнаты","room.unnamed":"Комната без имени","marker.use_climate_temp":"Учитывать температуру устройства в комнате","marker.use_climate_temp_tip":"Добавляет current_temperature кондиционера или термостата в среднюю температуру комнаты. Внешний бейдж со значением настраивается отдельно ниже.","marker.value_badge_title":"Бейдж со значением","marker.value_badge.help":"Показывает одно выбранное значение рядом со значком. Не влияет на комнатные показатели, свет, Glow и действие по нажатию.","marker.value_badge.help.aria":"Подсказка: бейдж со значением устройства","marker.value_badge_enabled":"Отображать бейдж со значением","marker.value_badge_source":"Значение","marker.value_badge_source.help":"Выберите конкретное состояние, поддерживаемый атрибут или производное значение этого устройства.","marker.value_badge_source.help.aria":"Подсказка: источник значения бейджа","marker.value_badge_position":"Расположение","marker.value_badge_position.help":"Выбранная сторона сохраняется постоянно и не переворачивается автоматически у края плана.","marker.value_badge_position.help.aria":"Подсказка: расположение бейджа","marker.value_badge_right":"Справа","marker.value_badge_bottom":"Снизу","marker.value_badge_left":"Слева","marker.value_badge_top":"Сверху","marker.value_badge_empty":"У этого устройства нет доступных значений","marker.value_badge_static":"Статичный значок не показывает живые значения. Настройка сохранится.","marker.value_badge_missing":"Источник недоступен","marker.value_badge_missing_hint":"Сохранённый источник сейчас недоступен. Можно сохранить его, отключить бейдж или выбрать другой.","marker.value_badge_duplicate":"То же значение уже показано внутри значка.","marker.value_badge_state":"Состояние · {name}","marker.value_badge_marker_state":"Состояние источника света · {name}","marker.value_badge_lqi":"Качество Zigbee-сигнала (среднее)","marker.value_badge_attr_current_temperature":"Текущая температура · {name}","marker.value_badge_attr_temperature":"Целевая температура · {name}","marker.value_badge_attr_current_humidity":"Текущая влажность · {name}","marker.value_badge_attr_humidity":"Влажность · {name}","marker.value_badge_attr_current_position":"Положение · {name}","marker.value_badge_attr_percentage":"Скорость · {name}","marker.value_badge_attr_brightness":"Яркость · {name}","marker.value_badge_attr_volume_level":"Громкость · {name}","marker.value_badge_attr_battery_level":"Заряд батареи · {name}","marker.value_badge_attr_fan_speed":"Скорость вентилятора · {name}","marker.light_role_label":"Является источником света","marker.light_role.help":"Определяет, является ли сам маркер пространственным источником света. Привязанные выше лампы остаются независимыми источниками для комнаты.","marker.light_role.help.aria":"Подсказка: является ли устройство источником света","marker.light_role_auto_yes":"Автоматически (источник света)","marker.light_role_auto_no":"Автоматически (не источник света)","marker.light_role_always":"Всегда источник света","marker.light_role_never":"Никогда не источник света","marker.light_entity_label":"Ведущая сущность света","marker.light_entity.help":"Для составного устройства выберите сущность, состояние, цвет и яркость которой представляют этот источник на плане. Автоматический вариант сохраняет совместимость со старыми планами.","marker.light_entity.help.aria":"Подсказка: ведущая сущность света","marker.light_entity_auto":"Автоматически ({entity})","marker.light_entity_none":"нет управляемой сущности","marker.light_entity_missing":"Сохранённая сущность {entity} недоступна. Временно используется {fallback}; выбор восстановится, если сущность вернётся.","marker.toggle_entity_label":"Переключаемая сущность","marker.toggle_entity.help":"Для составного устройства выберите собственную лампу или переключатель, которой управляет нажатие. Автоматический вариант сохраняет прежние правила цели и не зависит от ведущей сущности света.","marker.toggle_entity.help.aria":"Подсказка: переключаемая сущность","marker.toggle_entity_auto":"Автоматически ({entity})","marker.toggle_entity_none":"нет собственной управляемой сущности","marker.toggle_entity_missing":"Сохранённой сущности {entity} больше нет среди выбираемых каналов этого маркера. Временно используется {fallback}; выбор восстановится, если сущность вернётся.","marker.glow_color_label":"Цвет и яркость свечения","marker.glow_mode.help":"Используйте данные источника, задайте только цвет или зафиксируйте цвет и яркость. Минимум — 1%; чтобы отключить источник, выберите «Никогда».","marker.glow_mode.help.aria":"Подсказка: цвет и яркость свечения","marker.glow_mode_auto":"Из источника","marker.glow_mode_color":"Задать цвет","marker.glow_mode_fixed":"Задать цвет и яркость","marker.glow_color":"Цвет свечения","marker.glow_brightness":"Яркость","marker.glow_disabled_never":"Настройки свечения недоступны: маркер явно исключён из источников света.","marker.glow_disabled_auto":"В автоматическом режиме у этого маркера не найден пространственный источник света.","marker.glow_disabled_no_entity":"Для пространственного источника нет активной управляемой сущности Home Assistant.","marker.glow_passive_hint":"У источника нет собственных данных Home Assistant. Задайте цвет и яркость вручную; радиус остаётся доступным.","marker.control_broken":"Сохранённый источник отсутствует или больше не отмечен как источник света","marker.control_missing_label":"Отсутствующий источник на плане","marker.control_passive":"пассивный источник","toast.marker_control_cycle":"Такая связь создаст циклическую цепочку управления светом.","toast.marker_binding_required":"Сначала выберите устройство Home Assistant, затем добавьте управляемый источник света.","confirm.unlock":"Открыть замок «{name}»?","toast.files_migrate_failed":"Не удалось перенести вложения к новой привязке, ссылки остались на старые файлы: {err}","space.pick_saved":"Уже загруженные","space.pick_saved_hint":"Планы, сохранённые на сервере, включая отцеплённые ранее","space.no_saved":"На сервере пока нет сохранённых планов.","space.loading":"Загрузка…","space.used_by":"используется: {list}","space.in_use":"План используется пространством — сначала отцепите его","btn.use":"Выбрать","confirm.delete_plan":"Удалить файл плана «{name}» с сервера? Действие необратимо.","toast.plans_list_failed":"Не удалось получить список планов: {err}","toast.plan_delete_failed":"Не удалось удалить план: {err}","marker.hide":"Скрыть","marker.hide_tip":"Устройство исчезнет с плана после сохранения, но продолжит участвовать в расчёте сигнала комнаты. Вернуть его можно через каталог «Устройства» в редакторе устройств.","marker.show":"Показать","marker.show_tip":"Устройство снова появится на плане после сохранения.","marker.hidden_ghost":"Устройство скрыто пользователем","marker.ha_disabled_device":"Устройство деактивировано в Home Assistant и скрыто с плана.","marker.ha_disabled_entity":"Сущность деактивирована в Home Assistant и скрыта с плана.","marker.ha_disabled_all_entities":"У устройства нет активных сущностей Home Assistant, поэтому оно скрыто с плана.","marker.ha_registry_limited":"Полный реестр Home Assistant недоступен этому пользователю. Неподтверждённый объект временно нельзя показывать или использовать.","marker.delete_tip":"Полностью удалить устройство с плана и из всех расчётов. Позже его можно добавить заново.","tap.run":"Запустить автоматизацию/скрипт/сцену","tap.cover":"Открыть/закрыть (шторы/жалюзи)","marker.run_target_label":"Что запускать","marker.run_search_ph":"Поиск: автоматизация, скрипт или сцена…","marker.run_target_gone":"Цель {id} не найдена — выберите заново","marker.tap_confirm":"Спрашивать подтверждение","marker.tap_confirm_tip":"Перед выполнением показать диалог подтверждения — защита от случайных нажатий.","marker.toggle_hint_single":"Цель: {name} ({id}).","marker.virtual_light_target":"Виртуальный свет: {name}.","marker.virtual_light_current":"Ручное состояние: {state} → {effect}.","marker.virtual_light_state_on":"включён","marker.virtual_light_state_off":"выключен","marker.toggle_hint_group":"Переключатся источники ({count}): {names}.","marker.toggle_hint_current":"Сейчас: {state} → {effect}.","marker.toggle_hint_group_current":"Сейчас включено {on} из {count} → {effect}.","marker.toggle_hint_skipped":"Не будут затронуты ({count}): {targets}.","marker.toggle_effect_turn_on":"включится","marker.toggle_effect_turn_off":"выключится","marker.toggle_effect_open":"откроется","marker.toggle_effect_close":"закроется","marker.toggle_effect_stop":"остановится","marker.toggle_effect_toggle":"состояние переключится","marker.toggle_skip_missing":"не найдено","marker.toggle_skip_ha_disabled":"деактивировано в HA","marker.toggle_skip_unavailable":"недоступно","marker.toggle_skip_unsupported":"переключение не поддерживается","marker.toggle_skip_secure":"заблокировано из соображений безопасности","marker.toggle_none_no_binding":"У устройства нет привязки, состояние переключать нечем. По нажатию ничего не произойдёт.","marker.toggle_none_no_actionable_entity":"У устройства нет состояния, которое можно переключить. По нажатию ничего не произойдёт.","marker.toggle_none_configured_targets_missing":"Настроенные цели сейчас недоступны. Собственная сущность устройства не будет подставлена вместо них.","marker.toggle_none_ha_disabled":"Цель деактивирована в Home Assistant и не может использоваться на плане.","marker.toggle_none_unavailable":"Цель сейчас недоступна. По нажатию ничего не произойдёт.","marker.toggle_none_unsupported":"Для этой сущности Home Assistant не предоставляет безопасного переключения. По нажатию ничего не произойдёт.","marker.toggle_none_secure":"Переключение замков, сигнализации и защитных ворот с плана заблокировано из соображений безопасности.","run.automation":"автоматизация","run.script":"скрипт","run.scene":"сцена","confirm.tap_run":"Запустить «{name}»?","confirm.tap_toggle":"Переключить «{name}»?","confirm.tap_cover":"Открыть/закрыть «{name}»?","confirm.current_state":"Текущее состояние: {state}","confirm.expected_state":"После переключения: {state}","confirm.group_current":"включено {on} из {total}","confirm.group_all_on":"все включены","confirm.group_all_off":"все выключены","confirm.unavailable_targets":"Недоступно: {count}","confirm.expected_by_ha":"Состояние определит Home Assistant","confirm.state_on":"Включено","confirm.state_off":"Выключено","confirm.state_open":"Открыто","confirm.state_closed":"Закрыто","confirm.state_opening":"Открывается","confirm.state_closing":"Закрывается","confirm.state_stopped":"Остановлено","confirm.state_unknown":"Неизвестно","toast.run_started":"Запущено: {name}","toast.run_target_missing":"Цель запуска не найдена — проверьте настройки устройства","toast.run_target_required":"Выберите автоматизацию, скрипт или сцену","toast.tap_target_changed":"Цель действия изменилась. Повторите попытку.","toast.virtual_light_toggle_failed":"Не удалось переключить виртуальный свет: {err}","toast.value_badge_source_required":"Выберите значение для бейджа","btn.run":"Выполнить","vac.section":"Робот-пылесос: живая позиция","vac.autocal":"Настроить автоматически","vac.live":"Живая позиция на плане","vac.trail":"Показывать путь робота","vac.cal_maps":"Откалиброваны карты: {maps}","vac.autocal_no_rooms":"Интеграция не отдаёт список комнат — откройте «Подогнать вручную»","vac.autocal_no_match":"Не совпали имена комнат (нужно ≥3 общих) — откройте «Подогнать вручную»","vac.autocal_done":"Готово: привязка по {rooms} комнатам. Запустите уборку и проверьте","vac.cal_need_pos":"Робот сейчас не отдаёт координаты — запустите уборку и поставьте на паузу","vac.cal_done":"Калибровка сохранена. Запустите уборку и проверьте","vac.cal_cancelled":"Калибровка отменена","vac.fit":"Подогнать вручную","vac.fit_hint":"Перетащите карту робота на место, растяните за уголки","vac.fit_rotate":"Повернуть 90°","vac.fit_mirror":"Отразить","vac.trail_never":"Не показывать никогда","vac.trail_cleaning":"Во время уборки","vac.trail_always":"Показывать всегда","gs.bg_group":"Фон сцены","gs.bg_color":"Цвет фона вокруг плана","gs.bg_default":"Как в теме","gs.bg_theme":"по умолчанию — из темы","gs.bg_mode":"Фон плана","gs.bg_static":"Статичный цвет","gs.bg_daynight":"Следует за Солнцем","gs.bg_daynight_hint":"Фон проходит рассвет, день, закат и ночь по данным sun.sun, а при их недоступности — по локальным часам. Компас для фона не нужен.","gs.sun_group":"Солнце","gs.sun_missing":"Сущность sun.sun не найдена — фон следует локальным часам, а оконные лучи недоступны.","gs.north":"Север на плане","gs.north_ph":"не задан","gs.north_hint":"Север нужен только для оконных лучей (шаг 1°, с Shift 15°); солнечный фон работает без него.","gs.north_clear":"Сбросить","gs.north_letter":"С","gs.sun_rays":"Солнце в окнах","gs.about_group":"О карточке","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · документация и issues","gs.about_telegram":"Чат в Telegram","space.bg_color":"Цвет фона вокруг плана","space.bg_inherit":"Наследовать общий","space.bg_inherited":"наследуется из общих настроек","space.bg_mode":"Фон плана","space.north":"Север на плане (переопределение)","space.north_inherited":"наследуется: {v}","space.sun_rays":"Солнце в окнах","space.sun_inherit":"Наследовать общий","space.sun_on":"Вкл","space.sun_off":"Выкл","canvas.far_objects":"Объектов далеко от плана: {n}","canvas.show_far":"Показать","canvas.home_tip":"План там — нажмите, чтобы вписать","gs.grid_group":"Обслуживание планов","gs.grid_hint":"Обновляет модели данных, выравнивает элементы по сетке и объединяет лишние фрагменты стен. Перед записью будет показан точный отчёт.","gs.align_all":"Оптимизировать планы","gs.align_title":"Оптимизировать планы","gs.align_none":"Все планы уже используют актуальную и оптимальную модель данных.","gs.optimize_no_automatic_changes":"Автоматических изменений нет. Проверьте пункты ниже.","gs.align_count":"Сдвинется элементов: {n} из {total}, максимум на {cm} см.","gs.align_where":"Наибольший сдвиг — в пространстве «{s}».","gs.align_turned":"Проёмов с исправлением угла: {n}.","gs.align_removed_drafts":"Схлопнувшиеся на сетке некорректные контуры удалены: {n}.","gs.optimize_redundant_drafts":"Сохранённые цепочки стен, полностью скрытые стенами комнат, удалены: {n}.","gs.align_preflight_failed":"Не удалось безопасно проверить геометрию следующих пространств: {spaces}{more}.","gs.align_preflight_hint":"Планы не изменены. Скопируйте диагностику кнопкой ниже и приложите её к отчёту об ошибке вместе с экспортом пространства.","gs.preflight_reason_prepare-exception":"Не удалось подготовить геометрию пространства (исключение при сборке модели)","gs.preflight_reason_wall-null":"Тело стен не построилось (объединение вернуло пустоту)","gs.preflight_reason_wall-degraded-extra":"Тело стен деградировало с лишней геометрией","gs.preflight_reason_wall-failed-core":"Ядро тела стен не собралось","gs.preflight_reason_wall-exception":"Построение стен упало с исключением","gs.preflight_reason_floor-null":"Контур пола не построился","gs.preflight_reason_floor-exception":"Построение пола упало с исключением","gs.preflight_copy":"Скопировать диагностику","gs.preflight_copied":"Диагностика скопирована","gs.preflight_update_hint":"Версии карточки и интеграции различаются — обновите House Plan и повторите.","gs.align_preflight_space":"Пространство {n}","gs.align_preflight_more":" и ещё {n}","gs.optimize_changes":"Миграций модели: {m}; обновлено пространств: {c}; устранён шум координат: {p}; объединено отрезков реальных стен: {w}; объединено отрезков стен нулевой толщины: {s}; независимых: {i}.","gs.zero_walls_migrated":"Преобразовано виртуальных участков: {n}.","gs.wall_segments_migrated":"Стабилизировано сегментов стен: {n}.","gs.optimize_lattice_summary":"Канонизировано шумовых значений координат: {n}; максимальный сдвиг: {cm} см.","gs.optimize_lattice_space":"{space}: канонизировано значений координат: {n}; оставлено значений вне сетки: {far}.","gs.optimize_coincident_partitions":"Скрытые участки независимых стен поглощены стенами комнат: {n}.","gs.optimize_openings_rehosted":"Проёмы перепривязаны к стенам комнат: {n}.","gs.optimize_walls_straightened":"Выпрямлено стен: {n}; максимальное перемещение: {cm} см.","gs.optimize_walls_straightened_where":"Максимальная правка стены: {s}.","gs.optimize_walls_straighten_skipped":"Оставлено почти осевых стен, которые нельзя безопасно выпрямить: {n}.","gs.optimize_glow_migration":"Старый Glow: пространств — {spaces} → без заливки данных + независимый Glow; комнат — {rooms} → наследуемая заливка + независимый Glow.","gs.optimize_references":"Исправлено ссылок: пространства — {spaces}; комнаты — {rooms}; позиции — {positions}; устройств отвязано от отсутствующих пространств — {detached}.","gs.optimize_reference_more":", и ещё {n}","gs.optimize_orphans_removed":"Убрано забытых записей: {total} — подписи комнат: {rooms}; устройства: {devices}; групповые метки: {groups}. Все они принадлежали пространствам, удалённым ранее.","gs.optimize_live_positions":"Старые позиции в удалённых пространствах принадлежат существующим объектам: {n}{names}. Они будут сохранены.","gs.optimize_live_positions_remove":"Старые позиции в удалённых пространствах принадлежат существующим объектам: {n}{names}. Они выбраны для удаления.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Убрать старые позиции","gs.optimize_live_keep":"Сохранить старые позиции","gs.optimize_live_selected":"Старые позиции будут убраны после применения оптимизации.","gs.optimize_unverified":"Не удалось безопасно проверить позиций: {n}. Они оставлены без изменений.","gs.optimize_registry_limited":"Для безопасной проверки нужен полный доступ администратора к реестрам Home Assistant.","gs.optimize_vacuum_warning":"Сопоставления комнат пылесоса требуют проверки: {n}.","gs.optimize_details":"Подробности","gs.optimize_details_more":"И ещё записей: {n}.","gs.optimize_detail_removed":"будет удалено","gs.optimize_detail_live":"будет сохранено","gs.optimize_detail_unverified":"не проверено","gs.optimize_detail_room_label":"подпись комнаты","gs.optimize_detail_device":"устройство","gs.optimize_detail_group":"групповая метка","gs.optimize_detail_unknown":"неизвестный владелец","gs.optimize_detail_item":"{status}: {kind} {id}; прежнее пространство {space}","gs.align_warn":"Элементы, намеренно поставленные между узлами, будут сдвинуты. После операции доступна одна отмена — только до следующего изменения плана.","gs.align_run":"Оптимизировать","gs.align_done":"Планы оптимизированы: сдвинуто элементов — {n}, обслужено записей — {m}, исправлено ссылок — {r}","gs.optimize_undo":"Отменить последнюю оптимизацию","gs.optimize_undone":"Последняя оптимизация отменена","decor.furniture":"Мебель","furn.title":"Библиотека мебели","furn.symbol":"Символ","furn.group_furniture":"Мебель","furn.group_appliance":"Техника","furn.group_sanitary":"Сантехника","furn.group_other":"Прочее","furn.width":"Ширина","furn.depth":"Глубина","furn.pick_hint":"Выберите символ и кликните по плану.","furn.place_hint":"Кликните по плану — предмет встанет к ближайшей стене. Shift — свободно.","furn.sym_sofa":"Диван","furn.sym_armchair":"Кресло","furn.sym_coffee_table":"Журнальный столик","furn.sym_table_dining":"Обеденный стол","furn.sym_table_round":"Круглый стол","furn.sym_chair":"Стул","furn.sym_desk":"Письменный стол","furn.sym_bed_double":"Двуспальная кровать","furn.sym_bed_single":"Односпальная кровать","furn.sym_nightstand":"Тумбочка","furn.sym_wardrobe":"Шкаф","furn.sym_bookshelf":"Стеллаж","furn.sym_fridge":"Холодильник","furn.sym_stove":"Плита","furn.sym_dishwasher":"Посудомоечная машина","furn.sym_washer":"Стиральная машина","furn.sym_dryer":"Сушильная машина","furn.sym_tv":"Телевизор","furn.sym_ac":"Кондиционер","furn.sym_water_heater":"Бойлер","furn.sym_toilet":"Унитаз","furn.sym_bathtub":"Ванна","furn.sym_shower":"Душ","furn.sym_sink":"Раковина","furn.sym_kitchen_sink":"Кухонная мойка","furn.sym_bidet":"Биде","furn.sym_stairs":"Лестница","furn.sym_fireplace":"Камин","furn.sym_plant":"Растение","furn.sym_rug":"Ковёр","common.yes":"Да","common.no":"Нет","vac.diag_source":"Источник","vac.diag_platform":"Интеграция","vac.diag_status":"Статус","vac.diag_position":"Позиция","vac.diag_rooms":"Комнаты","vac.diag_rooms_value":"{total} · совпало имён: {matched} · {readiness}","vac.autocal_ready":"автокалибровка доступна","vac.autocal_not_ready":"нужно 3 совпавших имени","vac.diag_path":"Путь интеграции","vac.diag_map":"ID карты","vac.source_none":"не выбран","vac.source_status_ok":"Готов","vac.source_status_missing":"Не найден","vac.source_status_disabled":"Деактивирован в Home Assistant","vac.source_status_unavailable":"Недоступен","vac.source_status_unverified":"Нельзя проверить с текущими правами","vac.source_status_unsupported":"Нет данных позиции","vac.source_status_none":"Нет источника","vac.source_banner_missing":"Сохранённый источник больше не существует. Он не заменён автоматически: выберите другой или восстановите его в Home Assistant.","vac.source_banner_disabled":"Сохранённый источник деактивирован в Home Assistant. Активируйте его там или выберите другой.","vac.source_banner_unverified":"Текущих прав Home Assistant недостаточно для проверки источника. Привязка сохранена и не будет заменена автоматически.","vac.choose_source":"Выбрать источник","vac.source_auto":"Автоматически","vac.source_auto_hint":"Использовать совместимую сущность этого устройства","vac.all_cameras":"Все камеры","vac.all_cameras_warn":"Камера может не отдавать данные робота. Выбирайте её только если это карта вашего пылесоса.","vac.all_cameras_empty":"Других сущностей камеры не найдено.","vac.platform_unknown":"интеграция неизвестна","vac.cap_position":"позиция","vac.cap_rooms_short":"комнаты","vac.cap_path":"путь","vac.cap_map":"ID карты","vac.cap_none":"данные робота не обнаружены","vac.xcme_hint":"Включите атрибуты Xiaomi Cloud Map Extractor:","vac.documentation":"Документация","vac.residual_title":"Проверьте автокалибровку","vac.residual_message":"Совпавшие комнаты расходятся максимум на {error}. Примените приблизительную калибровку, подгоните её вручную либо отмените без изменения сохранённых настроек.","vac.apply_proposal":"Применить","gs.backup_group":"Резервная копия и перенос","gs.backup_hint":"Скачайте переносимую JSON-копию или проверьте и импортируйте копию, созданную House Plan.","backup.export_open":"Экспорт","backup.import_open":"Импорт","backup.export_title":"Экспорт House Plan","backup.import_title":"Импорт House Plan","backup.export_hint":"Выберите, должна ли копия содержать всю конфигурацию House Plan или только текущее пространство.","backup.full":"Полная копия","backup.current_space":"Текущее пространство","backup.current_space_title":"Текущее пространство: {title}","backup.no_current_space":"Нет текущего пространства","backup.plan_only":"Только планировка","backup.plan_only_hint":"Сохранить комнаты, стены, проёмы, декор и позиции подписей комнат без устройств и привязок Home Assistant.","backup.plan_only_preview":"Файл содержит только планировку","backup.privacy_warning":"Архив сохраняет названия, идентификаторы Home Assistant и точные координаты. Внутренние планы и вложения указываются ссылками, но не вкладываются; текущие состояния и маршруты пылесосов не включаются.","backup.download":"Скачать JSON","backup.export_done":"Резервная копия скачана","backup.reading":"Проверяем резервную копию…","backup.revalidated":"После предпросмотра план изменился. Сводка обновлена — проверьте её и подтвердите ещё раз.","backup.error.unauthorized":"У вас нет прав на экспорт или импорт этого плана.","backup.error.not_ready":"House Plan ещё не готов. Повторите попытку через несколько секунд.","backup.error.too_large":"Размер резервной копии превышает 8 МиБ.","backup.error.invalid_json":"Выбранный файл не является корректным JSON.","backup.error.invalid_format":"Выбранный файл не является резервной копией House Plan.","backup.error.unsupported_export_version":"Эта версия формата резервной копии не поддерживается установленной версией.","backup.error.future_model":"Резервная копия создана в более новой версии модели данных House Plan.","backup.error.invalid_config":"Резервная копия содержит некорректную конфигурацию House Plan.","backup.error.wall_model_migration_blocked":"Не удалось безопасно обновить модель стен из резервной копии. Сначала оптимизируйте исходный план и повторите экспорт.","backup.error.invalid_layout":"Резервная копия содержит некорректные позиции объектов.","backup.error.invalid_content":"Резервная копия содержит некорректные или несогласованные ссылки на файлы.","backup.error.space_not_found":"Выбранное пространство больше не существует.","backup.error.capacity_exceeded":"Добавление этой копии превысит ограничения размера плана.","backup.error.preview_expired":"Время предпросмотра истекло. Выберите файл резервной копии заново.","backup.error.preview_owner_mismatch":"Этот предпросмотр принадлежит другому пользователю Home Assistant.","backup.error.conflict":"После предпросмотра план изменился. Проверьте обновлённую сводку.","backup.error.content_confirmation_required":"Подтвердите отсоединение недоступных локальных файлов.","backup.error.commit_failed":"Не удалось безопасно применить копию. Предыдущий план восстановлен или ожидает восстановления.","backup.error.missing_plan":"После предпросмотра исчез связанный файл плана. Проверьте копию ещё раз.","backup.error.missing_content":"После предпросмотра исчез связанный локальный файл. Проверьте копию ещё раз.","backup.error.marker_control_missing":"В резервной копии отсутствует связанный источник света на плане.","backup.error.marker_control_not_light":"Связанная цель больше не отмечена как источник света.","backup.error.marker_control_self":"Источник света не может управлять самим собой.","backup.error.marker_control_cycle":"Резервная копия содержит циклическую цепочку управления светом.","backup.error.duplicate_marker_control":"Резервная копия содержит повторяющуюся связь с источником света.","backup.error.no_backup":"Нет снимка импорта или оптимизации, который можно восстановить.","backup.same_source":"Создано на этом экземпляре Home Assistant","backup.foreign_source":"Создано на другом экземпляре Home Assistant","backup.created":"Создано: {value}","backup.versions":"Карточка {card}; интеграция {integration}; модель данных {model}","backup.count_spaces":"Пространств: {n}","backup.count_rooms":"Комнат: {n}","backup.count_walls":"Стен: {n}","backup.count_openings":"Проёмов: {n}","backup.count_decor":"Объектов декора: {n}","backup.count_markers":"Устройств: {n}","backup.count_layout":"Позиций: {n}","backup.bindings":"Привязки — устройства: {device}, сущности: {entity}, виртуальные: {virtual}; позиций без пространства: {legacy}","backup.binding_status":"Статус на целевом экземпляре — активных: {active}, деактивированных: {disabled}, отсутствующих: {missing}","backup.missing_areas":"На целевом экземпляре отсутствуют зоны: {areas}","backup.dropped_marker_links":"Связи с источниками света вне переносимого пространства пропущены: {n}.","backup.repaired_target_refs":"Существующих ссылок восстановлено этим импортом: {n}.","backup.preserved_unresolved_refs":"Неоднозначные ссылки сохранены без изменений: {n}.","backup.preserved_unresolved_hint":"House Plan ничего не угадывал и не удалял. После импорта запустите «Оптимизировать планы», чтобы проверить оставшиеся ссылки.","backup.import_details":"Подробности восстановления ссылок","backup.import_detail.incoming_remapped":"Ссылок обновлено внутри импортируемой копии: {n}","backup.import_detail.target_repaired":"Существующих ссылок восстановлено: {n}","backup.import_detail.preserved_unresolved":"Неразрешимых ссылок сохранено: {n}","backup.import_detail.collisions":"Конфликтов назначения безопасно сохранено: {n}","backup.import_detail.dropped_links":"Входящих связей пропущено по правилам переноса: {n}","backup.import_detail.bounded_lineages":"Чрезмерно вложенных идентификаторов ограничено: {n}","backup.replace_warning":"Текущая конфигурация и расположение будут заменены. Загруженные файлы не удаляются. Одну отмену можно выполнить до следующего изменения плана.","backup.foreign_bookkeeping":"Служебные списки известных и новых устройств другого экземпляра импортированы не будут.","backup.final_name":"Название нового пространства","backup.target_settings":"Глобальные настройки целевого экземпляра не изменятся.","backup.duplicates":"Уже существующих привязок: {n}","backup.skip":"Пропустить повторяющиеся устройства","backup.virtual_copy":"Добавить безопасные статичные виртуальные копии","backup.content":"Связанное содержимое","backup.content_available":"доступно локально","backup.content_external":"внешняя ссылка","backup.content_detach_required":"будет отвязано","backup.confirm_detach":"Я понимаю, что недоступные внутренние планы и вложения будут отвязаны от импортируемой конфигурации.","backup.replace":"Заменить","backup.add":"Добавить пространство","backup.space_done":"Пространство импортировано: комнат — {rooms}, устройств — {markers}, существующих ссылок восстановлено — {refs}","backup.full_done":"Копия восстановлена: пространств — {spaces}, комнат — {rooms}, устройств — {markers}","backup.undo_import":"Отменить последний полный импорт","backup.import_undone":"Полный импорт отменён"}}],ra=oa,la=new Map(aa.map(e=>[e.code,e]));function ca(e){return"string"==typeof e?e.trim().replaceAll("_","-").toLowerCase():""}function ha(e){return la.get(ca(e))}function da(e,t){const i=[{value:"",label:e},...aa.map(({code:e,nativeLabel:t})=>({value:e,label:t}))],s="string"==typeof t?t:"";return s&&!i.some(e=>e.value===s)&&i.push({value:s,label:ha(s)?.nativeLabel??s}),i}function pa(e,t){return function(e,t,i,s){const n=new Map(i.map(e=>[ca(e),e])),o=n.get(ca(e));if(o)return o;const a=ca(t),r=n.get(a);return r||(n.get(a.split("-")[0]||"")??s)}(t,e?.locale?.language||e?.language,aa.map(({code:e})=>e),"en")}function ua(e,t,i){const s=ha(e)?.dictionary;return ss(s?.[t]??ra[t]??t,i)}function _a(e,t){const i=ha(e)?.dictionary[t]??ra[t];return"string"==typeof i&&i.trim().length>0}class ma extends he{constructor(){super(...arguments),this.deviceName="",this._metadataConnection=null,this._demoUntil=0,this._demoTimer=0,this._demoGeneration=1,this._demoKind=null,this._reducedMotion=!1,this._onMotionChange=e=>{this._reducedMotion=e.matches,this.requestUpdate()},this._startShortDemo=()=>{const e=this.presentation;e&&"icon_ripple"===e.display&&"none"===e.pulse.kind&&(this._demoGeneration++,this._demoKind="short",this._demoUntil=Date.now()+3300,window.clearTimeout(this._demoTimer),this._demoTimer=window.setTimeout(()=>{this._demoUntil=0,this._demoTimer=0,this._demoKind=null,this.requestUpdate()},3330),this.requestUpdate())},this._toggleContinuousDemo=()=>{const e=this.presentation;e&&"icon_ripple"===e.display&&"none"===e.pulse.kind&&("continuous"===this._demoKind?this._clearDemo():(this._clearDemo(),this._demoGeneration++,this._demoKind="continuous",this._demoUntil=Number.POSITIVE_INFINITY),this.requestUpdate())}}connectedCallback(){super.connectedCallback(),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this._ensureMetadata()}disconnectedCallback(){this._metadataRelease?.(),this._metadataRelease=void 0,this._metadataConnection=null,this._clearDemo(),this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,super.disconnectedCallback()}willUpdate(e){e.has("hass")&&this._ensureMetadata();const t=this.presentation,i=e.get("presentation");e.has("presentation")&&i?.binding!==t?.binding&&this._clearDemo(),t&&this._demoKind&&("icon_ripple"!==t.display||"none"!==t.pulse.kind)&&this._clearDemo()}_ensureMetadata(){const e=this.hass?.connection||null;e&&e!==this._metadataConnection&&(this._metadataRelease?.(),this._metadataConnection=e,this._metadataRelease=Qo(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0,this._demoKind=null}get _lang(){return pa(this.hass)}_t(e,t){return ua(this._lang,e,t)}_providerText(e){return"houseplan"===e.domain?this._t("marker.preview.virtual_provider"):e.label.toLowerCase()===e.domain.toLowerCase()?e.domain:`${e.label} (${e.domain})`}_providers(){return this.presentation&&this.registry?function(e,t,i,s=ea(e)){if("virtual"===t)return[{domain:"houseplan",label:"House Plan",confidence:"registry-owner"}];const n=t.indexOf(":");if(n<1)return[];const o=t.slice(0,n),a=t.slice(n+1);if("entity"===o){const t=na(e,a,i,s);return t?[t]:[]}if("device"!==o)return[];const r=i.devices?.[a]||e?.devices?.[a];if(!r)return[];const l=[r.config_entry_id,...Array.isArray(r.config_entries)?r.config_entries:[]].filter(e=>"string"==typeof e&&!!e).map(t=>{const i=s.configEntries[t];return ia(e,i?.domain,"registry-owner",s,t)});if(l.some(Boolean))return sa(l);const c=[];for(const[t,n]of Object.entries(i.entities||{}))n?.device_id===a&&null==n?.disabled_by&&c.push(na(e,t,i,s));return c.some(Boolean)?sa(c):sa((Array.isArray(r.identifiers)?r.identifiers:[]).map(t=>{const i=Array.isArray(t)?t[0]:null;return ia(e,i,"identifier-fallback",s)}))}(this.hass,this.presentation.binding,this.registry,ea(this.hass)):[]}_providerSummary(e){if(!e.length)return this._t("marker.preview.unknown_provider");const t=e.slice(0,2).map(e=>this._providerText(e)),i=e.length-t.length;return t.join(", ")+(i>0?` · ${this._t("marker.preview.more_sources",{n:i})}`:"")}_sourceSummary(e){const t=e.visualSources;return t.length?1===t.length?`${t[0].name} · ${t[0].eid} · ${this._sourceProvider(t[0].eid)}`:2===t.length?t.map(e=>e.name).join(" · "):this._t("marker.preview.multiple_sources",{n:t.length}):this._t("marker.preview.no_source")}_stateSummary(e){if(!e.visualSources.length)return this._t("marker.preview.no_state");if(1===e.visualSources.length)return e.visualSources[0].stateText||this._t("marker.preview.no_state");const t=[...new Set(e.visualSources.map(e=>e.stateText).filter(Boolean))];return 1===t.length?t[0]:this._t("marker.preview.mixed_states")}_reason(e){return this._t(`marker.preview.reason.${e}`)}_sourceProvider(e){if(!this.registry)return this._t("marker.preview.unknown_provider");const t=na(this.hass,e,this.registry,ea(this.hass));return t?this._providerText(t):this._t("marker.preview.unknown_provider")}render(){const e=this.presentation;if(!e)return G;const t=!!this._demoKind&&this._demoUntil>Date.now()&&"icon_ripple"===e.display&&"none"===e.pulse.kind,i=t?(()=>{const t=function(e,t,i,s,n=null){return{kind:t,reason:"short"===t?"event":"running",generation:Math.max(1,Math.trunc(i)),expiresAt:"short"===t?n:null,color:e.color,diameterScale:s?1:e.diameterScale,animated:!s,reducedMotionIndicator:s?"dot":"none"}}(e.pulse,this._demoKind,this._demoGeneration,this._reducedMotion,"short"===this._demoKind?this._demoUntil:null);return{...e,pulse:t,classes:[...e.classes.filter(e=>!e.startsWith("pulse-")),`pulse-${t.kind}`,...t.generation%2==0?["pulse-gen2"]:[]]}})():e,s=this._providers(),n="none"!==e.pulse.kind,o=i.scale*(i.pulse.animated?i.pulse.diameterScale:1);let a=-o/2,r=o/2,l=-o/2,c=o/2;if(i.valueBadge){const e=i.scale*Math.min(4,Math.max(.9,.29*i.valueBadge.text.length+.28)),t=.7*i.scale,s=.6*i.scale;"right"===i.valueBadge.position&&(r=Math.max(r,s+e)),"left"===i.valueBadge.position&&(a=Math.min(a,-s-e)),"top"===i.valueBadge.position&&(l=Math.min(l,-s-t)),"bottom"===i.valueBadge.position&&(c=Math.max(c,s+t))}if(null!=i.lqiText){const e=i.scale*("bottom"===i.valueBadge?.position?1.55:.95);c=Math.max(c,e)}const h=Math.min(1,2.35/Math.max(1,r-a),2.35/Math.max(1,c-l)),d=Math.round(100*h),p=[`left:calc(50% - ${54*((a+r)/2)*h}px)`,`top:calc(50% - ${54*((l+c)/2)*h}px)`,h<1?`transform:scale(${h})`:""].filter(Boolean).join(";"),u=["dev",Uo(this.hass),...i.classes,"virtual"===i.binding?"virtual":"",i.haDisabled?"ghost ha-disabled":"",null!=i.valueText?"valonly":""].filter(Boolean).join(" "),_=Go(i).join(";"),m=t?this._t("continuous"===this._demoKind?"marker.preview.demo_continuous_notice":"marker.preview.demo_short_notice"):this._reason(e.explanation.reason),g=[this.deviceName,this._providerSummary(s),this._stateSummary(e),m,e.valueFullText||"",Io(e.valueBadge),null!=e.lqiText&&e.lqiBand?this._t(`marker.lqi_a11y_${e.lqiBand}`,{value:e.lqiText}):""].filter(Boolean).join(". ");return q`
${this._t("marker.preview.title")} @@ -4555,7 +4555,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 ${this._t("continuous"===this._demoKind?"marker.preview.stop_continuous":"marker.preview.demo_continuous")}
`:G} -
`}}ha.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},ha.styles=[Ke,o` +
`}}ma.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},ma.styles=[Ke,o` :host { display: block; min-width: 0; } .devicepreview { margin: 4px 0 14px; @@ -4622,14 +4622,14 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 .previewstage { min-height: 142px; } .previewfacts > div { grid-template-columns: minmax(0, 1fr); gap: 2px; } } - `],customElements.get("hp-device-preview")||customElements.define("hp-device-preview",ha);const da=Math.tan(.25*Math.PI/180);function pa(e,t){const i=Math.abs(Number(t[0])-Number(e[0])),s=Math.abs(Number(t[1])-Number(e[1]));if(!([i,s].every(Number.isFinite)&&i>0&&s>0))return null;const n=i>=s?"horizontal":"vertical",o=Math.max(i,s),a=Math.min(i,s);return a/o>da?null:{axis:n,major:o,minor:a,angleDegrees:180*Math.atan2(a,o)/Math.PI}}function ua(e,t){const i=pa(e,t);return i?"horizontal"===i.axis?[Number(t[0]),Number(e[1])]:[Number(e[0]),Number(t[1])]:[Number(t[0]),Number(t[1])]}const _a=e=>`${e[0]},${e[1]}`,ma=(e,t)=>e[0]===t[0]&&e[1]===t[1],ga=(e,t)=>{const i=_a(e),s=_a(t);return i{let t=0;for(let i=0;i{if(e.length<3)return!1;for(let t=0;t1e-12},ba=e=>{for(let t=0;t+1ma(e,t.from)?[...t.to]:[...e],wa=(e,t)=>e.map(e=>Array.isArray(e?.poly)?{...e,poly:e.poly.map(e=>ya(e,t))}:e),ka=(e,t,i)=>{const s=e.flatMap((e,t)=>hi(e)?.some(e=>ma(e,i.from))?[t]:[]);if(!s.length)return!1;for(const i of s){const s=hi(e[i]),n=hi(t[i]);if(!s||!n)continue;if(!va(n))return!1;const o=Math.sign(fa(s)),a=Math.sign(fa(n));if(o&&a&&o!==a)return!1}const n=new Set;for(const e of s){const i=hi(t[e]);if(i)for(let s=0;s{let i=0;for(const s of e){const e=hi(s);if(e)for(let s=0;s{const{a:i,b:s,axis:n}=e,o="horizontal"===n?{from:s,to:[s[0],i[1]]}:{from:s,to:[i[0],s[1]]},a="horizontal"===n?{from:i,to:[i[0],s[1]]}:{from:i,to:[s[0],i[1]]};return[{move:o,preservedDegree:xa(t,i)},{move:a,preservedDegree:xa(t,s)}].sort((e,t)=>t.preservedDegree-e.preservedDegree||_a(e.move.to).localeCompare(_a(t.move.to))||_a(e.move.from).localeCompare(_a(t.move.from))).map(e=>e.move)};const Sa=(e,t)=>[e[0]-t[0],e[1]-t[1]],Ma=(e,t)=>[e[0]+t[0],e[1]+t[1]],Ca=(e,t)=>e[0]*t[0]+e[1]*t[1],Da=e=>Math.hypot(e[0],e[1]);function Pa(e){let t=0;for(let i=0;ie[1]!=l>e[1]&&e[0]<(r-o)*(e[1]-a)/(l-a)+o&&(i=!i)}return i}([r[0]+a[0]*l,r[1]+a[1]*l],e)&&(a=[-a[0],-a[1]]),a}function Ra(e){const t=e.length;if(t<3)return!1;for(let i=0;i(e[0]-n[0])*r[1]-(e[1]-n[1])*r[0],c=e=>(e[0]-n[0])*r[0]+(e[1]-n[1])*r[1],h=i,d=a-i;for(const t of e){const e=Math.abs(l(t));if(e<=i)continue;const n=c(t);n<=h||n>=d||eMath.abs(a+(e-p)/g*(r-a));s=Math.min(s,f(_),f(m))}}return s}function za(e,t,i){if(Di(e,t,i))return!0;if(Ci(e,t,i)||Ci(t,e,i))return!1;let s=0;try{const i=ni([[...e.map(e=>[e[0],e[1]]),[e[0][0],e[0][1]]]],[[...t.map(e=>[e[0],e[1]]),[t[0][0],t[0][1]]]]);for(const e of i)e?.[0]&&(s+=Ti(e[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function Fa(e,t,i){for(const s of t)for(let t=0;ti)continue;const c=(e.x-n[0])*l[0]+(e.y-n[1])*l[1];if(c-e.length/2>=-i&&c+e.length/2<=r+i)return!0}return!1}function Na(e,t,i){const s=Math.abs(t[0]-e[0]),n=Math.abs(t[1]-e[1]);return s<=i&&n>i?"v":n<=i&&s>i?"h":null}function Ea(e,t,i){return Math.abs(e[0]-t[0])<=i&&Math.abs(e[1]-t[1])<=i}function Oa(e,t,i,s,n){return Ea(e,i,n)&&Ea(t,s,n)||Ea(e,s,n)&&Ea(t,i,n)}function Ha(e,t,i,s,n){const o=Na(e,t,n);return o&&Na(i,s,n)===o?"h"===o?Math.abs(e[1]-i[1])>n||Math.abs(e[1]-s[1])>n?0:Math.max(0,Math.min(Math.max(e[0],t[0]),Math.max(i[0],s[0]))-Math.max(Math.min(e[0],t[0]),Math.min(i[0],s[0]))):Math.abs(e[0]-i[0])>n||Math.abs(e[0]-s[0])>n?0:Math.max(0,Math.min(Math.max(e[1],t[1]),Math.max(i[1],s[1]))-Math.max(Math.min(e[1],t[1]),Math.min(i[1],s[1]))):0}function La(e,t,i,s){const n=e.length,o=Na(e[(t-1+n)%n],e[t],s),a=Na(e[(t+1)%n],e[(t+2)%n],s),r="h"===i?"v":"h";return o===r&&a===r}function Ba(e){let t=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of e)t=Math.min(t,o[0]),i=Math.min(i,o[1]),s=Math.max(s,o[0]),n=Math.max(n,o[1]);return[t,i,s,n]}function ja(e,t,i){const s=Ba(e),n=Ba(t);return s[2]n}const Wa=(e,t)=>"h"===t?e[0]:e[1],Ua=(e,t)=>"h"===t?e[1]:e[0];function Ga(e,t,i,s,n){const o=e.find(e=>e.id===t);if(!o)return null;const a=o.poly[i],r=o.poly[(i+1)%o.poly.length],l=Na(a,r,n);if(!l)return null;const c=0===s?r:a,h=0===s?a:r,d=Wa(c,l),p=Wa(h,l),u=Ua(c,l),_=function(e,t,i,s){const n=[];for(const o of e)for(let e=0;es||Math.abs(Ua(r,t)-i)>s)continue;const l=Wa(a,t),c=Wa(r,t),h=Math.min(l,c),d=Math.max(l,c);d-h>s&&n.push({roomId:o.id,edge:e,lo:h,hi:d})}return n.sort((e,t)=>e.lo-t.lo||e.hi-t.hi||e.roomId.localeCompare(t.roomId)||e.edge-t.edge)}(e,l,u,n),m=function(e,t,i,s){const n=[t,i];for(const o of e){const e=Math.max(t,o.lo),a=Math.min(i,o.hi);a-e>s&&n.push(e,a)}const o=[...new Set(n)].sort((e,t)=>e-t),a=[];for(let t=0;t+1r>e.lo-s&&re.roomId))].sort();a.push({lo:i,hi:n,owners:l})}return a}(_,Math.min(d,p),Math.max(d,p),n);return!m.length||m.some(e=>e.owners.length<1||e.owners.length>2)?null:{roomId:t,edge:i,movedEndpoint:s,axis:l,line:u,fixed:d,moving:p,intervals:_,baseline:m}}function Va(e,t,i){const s=[];for(const n of e.intervals){const o=t.polys[n.roomId];if(!o){s.push(n);continue}if(n.edge<0||n.edge>=o.length)continue;const a=o[n.edge],r=o[(n.edge+1)%o.length];if(Na(a,r,i)!==e.axis||Math.abs(Ua(a,e.axis)-e.line)>i||Math.abs(Ua(r,e.axis)-e.line)>i)continue;const l=Wa(a,e.axis),c=Wa(r,e.axis),h=Math.min(l,c),d=Math.max(l,c);d-h>i&&s.push({...n,lo:h,hi:d})}return s}function Ka(e,t,i){return new Set(e.filter(e=>t>e.lo-i&&te.roomId)).size}function Ya(e,t,i){for(const s of t.sideOwnership){const t=e.polys[s.roomId];if(!t)return!1;const n=t[s.edge],o=t[(s.edge+1)%t.length];if(Na(n,o,i)!==s.axis)return!1;const a=Wa(0===s.movedEndpoint?n:o,s.axis),r=Va(s,e,i),l=(s.moving>=s.fixed?s.baseline[s.baseline.length-1]:s.baseline[0]).owners.length;if(l<1||l>2)return!1;const c=Math.min(s.fixed,s.moving,a),h=Math.max(s.fixed,s.moving,a),d=[c,h,s.fixed,s.moving,a];for(const e of s.intervals)d.push(e.lo,e.hi);for(const e of r)d.push(e.lo,e.hi);const p=[...new Set(d.filter(e=>e>=c&&e<=h))].sort((e,t)=>e-t),u=Math.min(s.fixed,s.moving),_=Math.max(s.fixed,s.moving),m=Math.min(s.fixed,a),g=Math.max(s.fixed,a);for(let e=0;e+1u-i&&o<_+i,c=o>m-i&&o2||d>2)return!1;if(a&&c){if(h!==d)return!1}else if(!a&&c){if(d!==l||h>0&&h!==d)return!1}else if(2===h?2!==d:d>1)return!1}}return!0}function Ja(e,t,i,s){const n=[i.n[0]*s,i.n[1]*s],o={polys:{},openings:{},movedSpans:{}};for(const t of i.roomIds){const s=e.find(e=>e.id===t),a=i.edgeByRoom[t];if(!s||null==a)continue;const r=(a+1)%s.poly.length;o.polys[t]=s.poly.map((e,t)=>t===a||t===r?Ma(e,n):[...e]),o.movedSpans[t]=[[Ma(s.poly[a],n),Ma(s.poly[r],n)]]}for(const e of t)i.movingOpeningIds.includes(e.id)&&(o.openings[e.id]=[e.x+n[0],e.y+n[1]]);return o}function Xa(e,t,i,s,n,o,a,r){if(!function(e,t,i,s){if(Ta([e.x,e.y],t,i)>2*s)return!1;const n=Da(Sa(i,t));if(n<=s)return!1;const o=[(i[0]-t[0])/n,(i[1]-t[1])/n],a=(e.x-t[0])*o[0]+(e.y-t[1])*o[1];return a>=-s&&a<=n+s}(e,t,i,r))return!0;if(Ta([e.x,e.y],s,n)>2*r)return!1;const l=0===o?s:n;return Da(Sa([e.x,e.y],l))>=e.length/2+a-r}function Za(e,t,i,s){const n=s.movingHalf||0;return"circle"===e.kind?Ta(e.center,t,i)2)return!1;const r=Ja(e,t,i,s);if(Object.keys(r.polys).length!==i.roomIds.length)return!1;if(!Ya(r,i,o))return!1;const l=new Set(i.roomIds),c=e=>r.polys[e.id]||e.poly;for(const s of i.roomIds){const l=e.find(e=>e.id===s),c=r.polys[s],h=i.edgeByRoom[s];if(!l||!c||c.length!==i.topology[s]||c.length!==l.poly.length||!Ra(c))return!1;const d=l.poly.length,p=(h-1+d)%d,u=[p,h,(h+1)%d];for(const e of u)if(pa(c[e],c[(e+1)%d]))return!1;const _=Pa(l.poly),m=Pa(c);if(Math.abs(m)e.id===t),s=r.polys[t];for(const n of e){if(n.id===t||l.has(n.id))continue;const e=c(n);if(!ja(i.poly,n.poly,o)||!ja(s,e,o))if(Ci(i.poly,n.poly,o)){if(!Ci(s,e,o))return!1}else if(Ci(n.poly,i.poly,o)){if(!Ci(e,s,o))return!1}else if(Ci(s,e,o)||Ci(e,s,o)||za(s,e,o))return!1}}e.find(e=>e.id===i.roomId);const h=r.polys[i.roomId],d=i.edgeByRoom[i.roomId],p=h[d],u=h[(d+1)%h.length];if(Math.abs(s)>o)for(const e of n.obstacles||[])if(Za(e,p,u,n))return!1;for(const e of t){if(!i.movingOpeningIds.includes(e.id))continue;const t=r.openings[e.id];if(!t||e.hosted)return!1;if(!Fa({...e,x:t[0],y:t[1]},[h],2*o))return!1}return!0}const er=new WeakMap;function tr(e,t,i){const s=i/t;return Ti(e)*s*s/1e4}function ir(e,t){return t?`${Math.round(10.7639*e)} ft²`:`${(Math.round(10*e)/10).toFixed(1)} m²`}const sr=e=>"number"==typeof e&&Number.isFinite(e),nr=(e,t)=>{const i=new Map;for(const s of e||[])!sr(s?.cm)||!t&&s.cm<=0||i.set(s.cm,(i.get(s.cm)||0)+1);return i},or=e=>[String(e),"см"].join(" ");function ar(e,t,i={}){if(i.allowClear)return[];const s=!0===i.exactMultiplicity,n=nr(e,s),o=nr(t,s),a=[];for(const[e,t]of n){const i=o.get(e)||0;0===i?a.push({invariant:"wall_records",kind:"lost",owner:or(e),reference:`было ${t}`,detail:"записи этой толщины исчезли целиком"}):s&&i!==t&&a.push({invariant:"wall_records",kind:"count",owner:or(e),reference:`было ${t}, стало ${i}`,detail:"изменилось число записей этой толщины"})}if(s)for(const[e,t]of o)n.has(e)||a.push({invariant:"wall_records",kind:"count",owner:or(e),reference:`было 0, стало ${t}`,detail:"появились записи новой толщины"});return a}class rr{constructor(){this._selectedRoomId=null,this._session=null,this._eligibility=null}get dragging(){return null!==this._session}ownsPointer(e){return this._session?.pointerId===e}get selectedRoomId(){return this._selectedRoomId}get snapshotIdentity(){return this._session?.snapshotIdentity||null}get activePointerId(){return this._session?.pointerId??null}get moved(){return!0===this._session?.moved}get delta(){return this._session?.delta||0}get rooms(){return this._session?.rooms||null}get openings(){return this._session?.openings||null}get plan(){return this._session?.plan||null}get preview(){return this._session?.accepted?.preview||null}get liveLabels(){return this._session?.accepted?.labels||null}selectRoom(e){this._selectedRoomId=e}restoreSelection(e){this._selectedRoomId=e}escapeIdle(){return this._selectedRoomId?(this._selectedRoomId=null,"selection-cleared"):"exit-tool"}resolve(e,t,i){this._eligibility&&this._eligibility.context===e||(this._eligibility={context:e,values:new Map});const s=this._eligibility.values.get(t);if(s)return s;const n=i();return this._eligibility.values.set(t,n),n}begin(e){return!this._session&&(this._session={pointerId:e.pointerId,start:[...e.start],roomId:e.roomId,plan:e.plan,options:e.options,rooms:e.rooms,openings:e.openings,snapshotIdentity:e.snapshotIdentity,before:e.before,moved:!1,delta:0,changedRoomIds:[...e.plan.roomIds],rejectionNotified:!1,wallUnionBefore:e.wallUnionBefore,accepted:null},!0)}move(e){const t=this._session;if(!t||t.pointerId!==e.pointerId)return{kind:"no-op"};const i=(s=t.start,n=e.point,o=t.plan.n,[s?.[0],s?.[1],n?.[0],n?.[1],o?.[0],o?.[1]].every(Number.isFinite)?(n[0]-s[0])*o[0]+(n[1]-s[1])*o[1]:0);var s,n,o;const a=e.snap([t.plan.a[0]+t.plan.n[0]*i,t.plan.a[1]+t.plan.n[1]*i]),r=(a[0]-t.plan.a[0])*t.plan.n[0]+(a[1]-t.plan.a[1])*t.plan.n[1],l=function(e,t,i,s,n,o){if(!Number.isFinite(s)||Math.abs(s)<1e-9)return 0;const a=Math.sign(s),r=Math.abs(s),l=Math.max(Math.abs(n),1e-6);let c=er.get(i);c&&c.opts===o||(c={opts:o,values:new Map},er.set(i,c));const h=c.values;let d=0;for(let s=Math.min(l,r),n=0;n<4096&&s<=r+1e-9;n++,s=Math.min(r,s+l)){const n=a*s,c=`${n.toFixed(9)}|${l.toFixed(9)}`;let p=h.get(c);if(void 0===p&&(p=Qa(e,t,i,n,o),h.size<4096&&h.set(c,p)),!p)break;if(d=n,Math.abs(s-r)<=1e-9)break}return d}(t.rooms,t.openings,t.plan,r,e.step,t.options);if(t.moved&&l===t.delta)return{kind:"no-op"};const c=Ja(t.rooms,t.openings,t.plan,l);let h;try{if(h=e.project(t.snapshotIdentity,c.polys,c.openings,t.changedRoomIds,t.rooms),!h.ok)return this._projectionRejected(t);if(ar(h.value.beforeWalls,h.value.afterWalls,{exactMultiplicity:!0}).length)return this._projectionRejected(t)}catch{return this._projectionRejected(t)}const d=t.accepted,p={preview:h.value.preview,labels:null,artifact:h.value.artifact,beforeWalls:h.value.beforeWalls,afterWalls:h.value.afterWalls};t.accepted=p;try{e.publish(p.preview,p.artifact),p.labels=e.measure(c,t.plan)}catch{t.accepted=d;try{e.publish(d?.preview||null,d?.artifact||null)}catch{}return this._projectionRejected(t)}return t.moved=!0,t.delta=l,{kind:"accepted",preview:h.value.preview,labels:p.labels,artifact:h.value.artifact}}finish(e){const t=this._session;if(!t||t.pointerId!==e.pointerId)return{kind:"no-op"};const i=t.accepted;let s;if(t.moved&&Math.abs(t.delta)>1e-9&&i)if(e.currentSnapshotIdentity!==t.snapshotIdentity)s={kind:"rejected",reason:"stale-snapshot"};else if(Qa(t.rooms,t.openings,t.plan,t.delta,t.options))if(ar(i.beforeWalls,i.afterWalls,{exactMultiplicity:!0}).length)s={kind:"rejected",reason:"wall-records"};else{let n=!1;try{n=e.validatePreview(i.preview)}catch{n=!1}s=n?{kind:"commit",preview:i.preview,before:t.before}:{kind:"rejected",reason:"invalid-candidate"}}else s={kind:"rejected",reason:"invalid-topology"};else s={kind:"no-op"};return this._session=null,"commit"===s.kind&&(this._eligibility=null),s}cancel(e,t){const i=this._session;return!i||void 0!==t&&i.pointerId!==t?{kind:"no-op"}:(this._session=null,this._eligibility=null,{kind:"cancelled",restoreWallUnion:e===i.snapshotIdentity?i.wallUnionBefore:null})}reset(){this._selectedRoomId=null,this._session=null,this._eligibility=null}_projectionRejected(e){const t=!e.rejectionNotified;return e.rejectionNotified=!0,{kind:"rejected",notify:t}}}const lr=(e,t)=>[(e[0]-t.x)/Math.max(1e-9,t.w)*t.stageWidth,(e[1]-t.y)/Math.max(1e-9,t.h)*t.stageHeight];function cr(e){const{poly:t,edge:i,text:s,view:n}=e,o=t[i],a=t[(i+1)%t.length],r=[(o[0]+a[0])/2,(o[1]+a[1])/2],l=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(e=>{let t=0;for(let i=0;i=0?[-o/a,n/a]:[o/a,-n/a]}(t,i),c=Math.hypot(a[0]-o[0],a[1]-o[1])||1,h=[(a[0]-o[0])/c,(a[1]-o[1])/c],d=lr(r,n),p=lr(e.gearCenter,n),u=Math.max(34,7.2*s.length+12),_=Math.abs(l[0])>=Math.abs(l[1])?Math.max(28,u/2+4):Math.max(28,13),m=d[0]+l[0]*_,g=d[1]+l[1]*_,f=t=>{return i=m+h[0]*t,s=g+h[1]*t,n=u,o=18,a=p[0],r=p[1],l=e.gearWidthPx,c=e.gearHeightPx,2*Math.abs(i-a)=Math.abs(h[1])?h[0]:h[1])>0?-1:1,t=Math.max(n.stageWidth,n.stageHeight,64);for(let i=4;i<=t;i+=4){const t=i*e;if(!f(t)){v=t;break}const s=-t;if(!f(s)){v=s;break}}}const b=l[0]*_+h[0]*v,y=l[1]*_+h[1]*v,w=Math.hypot(b,y)||1,k=y/w*12,x=[r[0]+b/w*12*n.w/Math.max(1,n.stageWidth),r[1]+k*n.h/Math.max(1,n.stageHeight)];return{anchor:r,offsetXPx:b,offsetYPx:y,tangentOffsetPx:v,side:Math.abs(l[0])>=Math.abs(l[1])?l[0]<0?"left":"right":l[1]<0?"above":"below",leader:{a:r,b:x}}}function hr(e){const t=e%360;return t<0?t+360:t}function dr(e,t){const i=function(e,t){return hr(e+t)}(e,t)*Math.PI/180;return[Math.sin(i),-Math.cos(i)]}const pr=Object.freeze({dawn:Object.freeze({top:"#aabdd1",bottom:"#e8c8b7",horizon:"rgba(255,201,156,.56)",sun:"rgba(255,188,125,.78)",vignette:"rgba(65,72,99,.21)",outlineNear:"rgba(74,57,61,.25)",outlineMid:"rgba(255,238,224,.40)",outlineFar:"rgba(255,224,202,.18)"}),day:Object.freeze({top:"#dce9ef",bottom:"#cbdce3",horizon:"rgba(255,245,220,.45)",sun:"rgba(255,239,190,.72)",vignette:"rgba(65,91,105,.16)",outlineNear:"rgba(45,62,71,.28)",outlineMid:"rgba(255,255,255,.42)",outlineFar:"rgba(255,255,255,.20)"}),dusk:Object.freeze({top:"#48536c",bottom:"#9a7380",horizon:"rgba(242,156,114,.34)",sun:"rgba(255,167,113,.55)",vignette:"rgba(20,26,44,.39)",outlineNear:"rgba(238,219,225,.40)",outlineMid:"rgba(229,207,218,.26)",outlineFar:"rgba(215,190,205,.12)"}),night:Object.freeze({top:"#111a27",bottom:"#1f2f3e",horizon:"rgba(79,120,151,.16)",sun:"rgba(169,208,231,0)",vignette:"rgba(3,8,14,.58)",outlineNear:"rgba(218,238,249,.56)",outlineMid:"rgba(174,215,238,.30)",outlineFar:"rgba(136,194,226,.14)"})}),ur=e=>"number"==typeof e&&Number.isFinite(e),_r=(e,t,i)=>Math.min(i,Math.max(t,e));function mr(e){return e.elevation<=-6?"night":e.elevation>=6?"day":e.rising?"dawn":"dusk"}function gr(e){const t=(Math.floor(Number(e)||0)%1440+1440)%1440;return t>=300&&t<480?"dawn":t>=480&&t<1080?"day":t>=1080&&t<1260?"dusk":"night"}function fr(e){const t=mr(e),i=e.azimuth*Math.PI/180;return{sunX:50-42*Math.sin(i),sunY:78-_r(e.elevation,0,90)/90*64,sunOpacity:"night"===t?0:_r((e.elevation+6)/12,0,1)}}function vr(e){const t=(Math.floor(Number(e)||0)%1440+1440)%1440;if(t<300||t>=1260)return{sunX:50,sunY:78,sunOpacity:0};const i=(t-300)/960,s=(t-300)/120,n=(1260-t)/120;return{sunX:8+84*i,sunY:78-64*Math.sin(i*Math.PI),sunOpacity:Math.max(.18,Math.min(s,n,1))}}function br(e,t=new Date){const i=function(e){const t=e?.states?.["sun.sun"]?.attributes;return t&&ur(t.azimuth)&&ur(t.elevation)&&"boolean"==typeof t.rising?{azimuth:hr(t.azimuth),elevation:t.elevation,rising:t.rising}:null}(e);if(i)return{phase:mr(i),source:"sun",...fr(i)};const s="number"==typeof t?t:function(e=new Date){return 60*e.getHours()+e.getMinutes()}(t);return{phase:gr(s),source:"clock",...vr(s)}}function yr(e){return`${e.source}|${e.phase}|${e.sunX.toFixed(2)}|${e.sunY.toFixed(2)}|`+e.sunOpacity.toFixed(3)}const wr=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],kr=e=>Math.min(1,Math.max(0,e));function xr(e,t,i=6){const s=e.angle*Math.PI/180,n=[Math.sin(s),-Math.cos(s)],o=s=>{const o=[e.x+n[0]*i*s,e.y+n[1]*i*s];return t.find(e=>e.poly.length>=3&&vi(o,e.poly))||null},a=o(1),r=o(-1);return a&&r?null:a||r?a?{normal:[-n[0],-n[1]],roomId:a.id}:{normal:n,roomId:r.id}:null}function $r(e,t,i){return i>0&&e[0]*t[0]+e[1]*t[1]>.05}function Sr(e,t,i,s){return[[e[0],e[1]],[t[0],t[1]],[t[0]+i[0]*s,t[1]+i[1]*s],[e[0]+i[0]*s,e[1]+i[1]*s]]}function Mr(e,t){try{const i=ni([[...e.map(e=>[e[0],e[1]]),[e[0][0],e[0][1]]]],[[...t.map(e=>[e[0],e[1]]),[t[0][0],t[0][1]]]]),s=[];for(const e of i){const t=e?.[0];!Array.isArray(t)||t.length<4||s.push(t.slice(0,t.length-1).map(e=>[e[0],e[1]]))}return s}catch{return[]}}function Cr(e,t,i,s,n,o,a){if(!(s>0))return[];const r=dr(i,n),l=[-r[0],-r[1]],c=function(e){const t=Math.min(90,Math.max(0,e));return.7*(.8+1.7*Math.pow(1-t/90,1.6))}(s),h=[];for(const i of t){if(!(i.length>0))continue;const t=xr(i,e);if(!t||!$r(t.normal,r,s))continue;const n=e.find(e=>e.id===t.roomId);if(!n)continue;const d=o&&o[t.roomId]||n.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-t.normal[0],-t.normal[1]],m=Math.max(0,a?.[i.id]||0),g=i.x+_[0]*m/2,f=i.y+_[1]*m/2,v=Math.cos(p)*u,b=Math.sin(p)*u,y=[g-v,f-b],w=[g+v,f+b],k=c*i.length,x=Mr(Sr(y,w,l,k),d);if(!x.length)continue;const $=l[0]*_[0]+l[1]*_[1];h.push({openingId:i.id,roomId:t.roomId,polys:x,a:y,b:w,dir:l,len:k,normal:_,depth:k*$})}return h}const Dr=e=>"number"==typeof e&&Number.isInteger(e)&&e>=0&&e<=359?e:null;function Pr(e,t){const i=Dr(t?.north_deg);return null!==i?i:Dr(e?.north_deg)}function Tr(e,t){const i=e=>"static"===e||"daynight"===e?e:null;return i(t?.bg_mode)??i(e?.bg_mode)??"static"}function Ir(e,t){const i=t?.sun_rays;return"boolean"==typeof i?i:!0===e?.sun_rays}function Rr(e){const t=e?.states?.["sun.sun"]?.attributes,i=Number(t?.azimuth),s=Number(t?.elevation);return Number.isFinite(i)&&Number.isFinite(s)?{azimuth:i,elevation:s}:null}const Ar=["dawn","day","dusk","night"];function zr(e){if(!e)return"";const t=pr[e.phase];return[`--hp-day-cycle-outline-near:${t.outlineNear}`,`--hp-day-cycle-outline-mid:${t.outlineMid}`,`--hp-day-cycle-outline-far:${t.outlineFar}`].join(";")}function Fr(e,t=1){if(!e)return G;const i=[`--hp-day-cycle-sun-x:${e.sunX.toFixed(2)}%`,`--hp-day-cycle-sun-y:${e.sunY.toFixed(2)}%`,`--hp-day-cycle-sun-opacity:${e.sunOpacity.toFixed(3)}`,`opacity:${Math.min(1,Math.max(0,t)).toFixed(4)}`].join(";");return q``}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(ua(this._lang,"space_card.loading"));const t=this._config.space,i=this._renderDeviceSnapshot,s=Z_({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(ua(this._lang,"space_card.not_found",{id:t}));const n=Eh(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||ua(this._lang,"space_card.button"),l=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null;return q` ${r} `:G} - `}}pm.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0},_continuityEpoch:{state:!0}},pm.styles=[Ke,o` + `}}fm.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0},_continuityEpoch:{state:!0}},fm.styles=[Ke,o` .hp-static-title { font-weight: 700; padding: 10px 14px 6px; @@ -4892,7 +4892,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 text-align: center; color: var(--secondary-text-color, #9aa4ad); } - `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",pm),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 um=o` + `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",fm),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 vm=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); @@ -5063,7 +5063,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 .editor-secondary { transition: none; } .editor-group-launcher .group-chevron { transition: none; } } -`,_m=(e,t)=>(e.composedPath?.()||[e.target]).some(e=>(e=>!!e&&"function"==typeof e.matches)(e)&&t(e));class mm{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 q``: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 Rh(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?Ud(this._model,this._space):(e=this._model,t=this._space,e.find(e=>e.id===t)??e[0]);var e,t}_spaceModelById(e){return Ud(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._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._physicalDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._roomDialog=!1,"edit"===this._spaceDialog?.mode&&(this._spaceDialog=null),this._editorSecondary.closeForNavigation(),this._saveConfigDebounced.cancel(),this._frame=null,this._planSnapGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._decorSnapCache=null,this._commitSpace("",!0)}}else this._emptySpaceStateActive=!1}get _areaToSpace(){const e={};for(const t of this._model)for(const i of t.rooms)i.area&&(e[i.area]={space:t.id,room:i});return e}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._showHidden||!this._settings.filter_seeded&&!!this._settings.show_all}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const e=this._serverCfg,t=function(e){const t=e.hass,i=e.registry||An(t),s=En(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=bo(s,h).filter(e=>!ko(s,e.eid,d)),u=new Set(p.map(e=>e.area)),_=jn(s),m=qn(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=Wn(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"===yo(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=Bd(n),a=o!==(this._cfgContentFingerprint||Bd(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=Bd(e);r=s!==(this._layoutContentFingerprint||Bd(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=Bd(n)!==(this._cfgContentFingerprint||Bd(this._serverCfg))||Bd(s?.layout??{})!==(this._layoutContentFingerprint||Bd(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(kv)||"{}")||{}}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(Bd(t)!==(this._cfgContentFingerprint||Bd(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=En(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+nm)>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(I_(e.id,t),C_(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?np(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=T_({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&&Fv("/config/devices/device/"+encodeURIComponent(e.device_id))}}else Fv("/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=Bd(i);s!==(this._layoutContentFingerprint||Bd(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)&&(zv(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 Ni(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*Qh(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]=Hh(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*Pv,y:i.y*Pv}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[e.id]?this._defPos[e.id]:t?Hh(Zh(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:Bh(n/Pv),y:Bh(o/Pv),...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 k_(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(I_(e.id,t));return s||C_(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,M_({...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,zv(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=Op(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(!Wp(e))return void this._showUnavailableToggleTargets(e);const t=e=>{const t=Wp(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&&!Wp(n)&&this._showUnavailableToggleTargets(n)||(n&&Gp(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||Wp(e)||"configured-targets-missing"!==e.noneReason)return[];if(!e.skippedTargets.length||e.skippedTargets.some(e=>!Up.has(e.reason)&&"secure"!==e.reason))return[];const t=[],i=new Set;for(const s of e.skippedTargets){if(!Up.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)?q``: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=Wh([[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=Wh("line"===e.kind?[[e.x1*Pv,e.y1*i],[e.x2*Pv,e.y2*i]]:"text"===e.kind?[[e.x*Pv,e.y*i]]:Wr({x:e.x*Pv,y:e.y*i,w:e.w*Pv,h:e.h*i,angle:e.angle}));s&&t.push(s)}for(const i of this._physicalBodiesR(e)){const e=Wh(i);e&&t.push(e)}}return Gh(e,t)}_frameOf(){const e=this._spaceModel();if(!e){this._frame=null;const e={x:0,y:0,w:Pv,h:Pv};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=Jh(this._contentItems(e));let n=s.all||Xh(e),o=this._showFar?n:s.core||Xh(e);return t&&t.id===e.id&&i&&t.grow&&(o=Iv(t.rect,o),n=Iv(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=xh(64,this._cellCm),o=xh(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=zf(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=zf(t,-1)}else t=zf(t,e?1:0);else for(const i of e)t=zf(t,i.codePointAt(0)||0);else t=zf(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:kf,wallHeight:n,floorEdgeHeight:o,algorithm:3})}`;return{key:a,build:()=>{const o=ch(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e)).all,a=this._roomWallOpeningInputs(this._openingsR,e),r=t.length||o.length?Oc(e.rooms,t,i,a,this._wallKeyPitch,this._cellCm,this._gridPitch,Pv,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,Pv);if(!l)throw new Error("floor boolean geometry failed");const c=r?.openingIndex||qc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Pv),h=s.map(e=>{const i=this._openingsR[e.sourceIndex],s="gate"===e.type?!e.flipV:e.flipV,o=i?.partitionHost?eu(i.partitionHost,s):t.length||"gate"===e.type?Vc(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(!(Ff([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=[Ef(e,0,[-i,0],[i,0],n,0,.88*t),Ef(e,1,[i,0],[-i,0],-n,0,.88*t)]}else s="window"===e.type?[Ef(e,0,[-i,0],[i,0],-90,.27*t,.78*t),Ef(e,1,[i,0],[-i,0],90,.27*t,.78*t)]:[Ef(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=xh(64,this._cellCm),o=xh(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 Ff([t,i,s,n])?{x:t,y:i,w:s-t,h:n-i}:null}(s.openings),r=Cf({rect:a?Iv(i,a):i,wallHeight:n,openingHeight:n,floorDepth:o}),l=function(e,t=kf,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=Tf(l[e],e>0);if(!(c.length<3||Math.abs(Pf(c))<1e-9)){s.push(If(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,kf,n),c={geometry:l,floor:Af(s.floor,o),openings:s.openings,frame:r};return fv(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?$f(e,0):e}_floorView(e){if("iso"!==this._renderProjection)return e;const t=Sf([e.x,e.y]),i=Sf([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=Cf({rect:this._frameOf().rect,wallHeight:xh(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:q`
+
`}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 Eh(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?Jd(this._model,this._space):(e=this._model,t=this._space,e.find(e=>e.id===t)??e[0]);var e,t}_spaceModelById(e){return Jd(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._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._physicalDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._roomDialog=!1,"edit"===this._spaceDialog?.mode&&(this._spaceDialog=null),this._editorSecondary.closeForNavigation(),this._saveConfigDebounced.cancel(),this._frame=null,this._planSnapGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._decorSnapCache=null,this._commitSpace("",!0)}}else this._emptySpaceStateActive=!1}get _areaToSpace(){const e={};for(const t of this._model)for(const i of t.rooms)i.area&&(e[i.area]={space:t.id,room:i});return e}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._showHidden||!this._settings.filter_seeded&&!!this._settings.show_all}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const e=this._serverCfg,t=function(e){const t=e.hass,i=e.registry||An(t),s=En(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=bo(s,h).filter(e=>!ko(s,e.eid,d)),u=new Set(p.map(e=>e.area)),_=jn(s),m=qn(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=Wn(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"===yo(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=Gd(n),a=o!==(this._cfgContentFingerprint||Gd(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=Gd(e);r=s!==(this._layoutContentFingerprint||Gd(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=Gd(n)!==(this._cfgContentFingerprint||Gd(this._serverCfg))||Gd(s?.layout??{})!==(this._layoutContentFingerprint||Gd(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(Cv)||"{}")||{}}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(Gd(t)!==(this._cfgContentFingerprint||Gd(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=En(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+cm)>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(N_(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?cp(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=F_({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&&Lv("/config/devices/device/"+encodeURIComponent(e.device_id))}}else Lv("/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=Gd(i);s!==(this._layoutContentFingerprint||Gd(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")+":"+pa(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)&&(Hv(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 Ni(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*nd(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]=Wh(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*zv,y:i.y*zv}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[e.id]?this._defPos[e.id]:t?Wh(sd(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:Gh(n/zv),y:Gh(o/zv),...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 C_(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(N_(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,I_({...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,Hv(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=qp(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(!Yp(e))return void this._showUnavailableToggleTargets(e);const t=e=>{const t=Yp(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&&!Yp(n)&&this._showUnavailableToggleTargets(n)||(n&&Xp(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||Yp(e)||"configured-targets-missing"!==e.noneReason)return[];if(!e.skippedTargets.length||e.skippedTargets.some(e=>!Jp.has(e.reason)&&"secure"!==e.reason))return[];const t=[],i=new Set;for(const s of e.skippedTargets){if(!Jp.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 ua(pa(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=pa(this.hass,this._config?.language);return _a(i,e)&&_a(i,t)?q``: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=Yh([[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=Yh("line"===e.kind?[[e.x1*zv,e.y1*i],[e.x2*zv,e.y2*i]]:"text"===e.kind?[[e.x*zv,e.y*i]]:Yr({x:e.x*zv,y:e.y*i,w:e.w*zv,h:e.h*i,angle:e.angle}));s&&t.push(s)}for(const i of this._physicalBodiesR(e)){const e=Yh(i);e&&t.push(e)}}return Xh(e,t)}_frameOf(){const e=this._spaceModel();if(!e){this._frame=null;const e={x:0,y:0,w:zv,h:zv};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=td(this._contentItems(e));let n=s.all||id(e),o=this._showFar?n:s.core||id(e);return t&&t.id===e.id&&i&&t.grow&&(o=Nv(t.rect,o),n=Nv(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=Dh(64,this._cellCm),o=Dh(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=Hf(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=Hf(t,-1)}else t=Hf(t,e?1:0);else for(const i of e)t=Hf(t,i.codePointAt(0)||0);else t=Hf(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:Cf,wallHeight:n,floorEdgeHeight:o,algorithm:3})}`;return{key:a,build:()=>{const o=_h(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e)).all,a=this._roomWallOpeningInputs(this._openingsR,e),r=t.length||o.length?qc(e.rooms,t,i,a,this._wallKeyPitch,this._cellCm,this._gridPitch,zv,o):null;if(r&&("failed-core"===r.status||"not-applicable"===r.status))throw new Error("wall boolean geometry failed");const l=r?.paperGeom??Hc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,zv);if(!l)throw new Error("floor boolean geometry failed");const c=r?.openingIndex||Kc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,zv),h=s.map(e=>{const i=this._openingsR[e.sourceIndex],s="gate"===e.type?!e.flipV:e.flipV,o=i?.partitionHost?ou(i.partitionHost,s):t.length||"gate"===e.type?Zc(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(!(Lf([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=[jf(e,0,[-i,0],[i,0],n,0,.88*t),jf(e,1,[i,0],[-i,0],-n,0,.88*t)]}else s="window"===e.type?[jf(e,0,[-i,0],[i,0],-90,.27*t,.78*t),jf(e,1,[i,0],[-i,0],90,.27*t,.78*t)]:[jf(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=Dh(64,this._cellCm),o=Dh(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 Lf([t,i,s,n])?{x:t,y:i,w:s-t,h:n-i}:null}(s.openings),r=Rf({rect:a?Nv(i,a):i,wallHeight:n,openingHeight:n,floorDepth:o}),l=function(e,t=Cf,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=Ff(l[e],e>0);if(!(c.length<3||Math.abs(zf(c))<1e-9)){s.push(Nf(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,Cf,n),c={geometry:l,floor:Of(s.floor,o),openings:s.openings,frame:r};return kv(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?Pf(e,0):e}_floorView(e){if("iso"!==this._renderProjection)return e;const t=Tf([e.x,e.y]),i=Tf([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:Dh(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:q`
${this._t("canvas.far_objects",{n:this._outliers})} @@ -5118,7 +5118,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 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:Ei(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=Ei(s,this._stageAspect()),o=Math.min(Ov.ZOOM_MAX,Math.max(Ov.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},_v)}_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>=Fd?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=Ei(this._baseVb(),this._stageAspect()),o=Math.min(Ov.ZOOM_MAX,Math.max(Ov.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=Ei(e,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem($v,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=Ei(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&&Fv("/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},Ev(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=Lh(this._drag.ox+a),c=Lh(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(Dv))t.toggleAttribute("data-pointer-hover",e)}_syncPointerHoverSubtree(e){if(e.nodeType!==Node.ELEMENT_NODE)return;const t=e,i=this._pointerModality.hoverEnabled;t.matches(Dv)&&t.toggleAttribute("data-pointer-hover",i);for(const e of t.querySelectorAll(Dv))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 Nh}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,Pv}get _segments(){const e=this._curSpaceCfg,t=this._spaceH;return pi(e?.rooms||[]).map(e=>[e[0]*Pv,e[1]*t,e[2]*Pv,e[3]*t])}_savedNav(){if(this._hasFixedFloor)return null;try{return JSON.parse(localStorage.getItem(Sv)||"null")}catch{return null}}_saveNav(){if(!this._hasFixedFloor)try{localStorage.setItem(Sv,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._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._openingDialog=null,this._physicalDialog=null,this._wallDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._mergeDialog=null,this._roomDialog=!1,this._spaceDialog=null,this._importDialog=null;const e=this._warmSlot;e?.owner===this._warmGen&&(e.vp=this._warmViewportState(),e.dlg=null,e.frameFingerprint="",clearTimeout(e.evict),e.evict=0),this._saveNav()}_setMode(e,t=!0){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,"devices"===s&&"devices"!==e&&(this._showHidden=!1,this._deviceInbox=null,this._deviceInboxReturn=null,this._deviceInboxMemo=null),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?$f([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=yl(ul,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?yl(ul,this._imperial):this._drawWallField}get _drawWallCm(){const e=mv(this._drawWallFieldValue);if(null==e||e<0)return null;const t=this._imperial?2.54*e:e,i="column"===this._tool?Xc:100;return t>=("column"===this._tool?1:0)&&t<=i?t:null}get _drawWallMaxCm(){return"column"===this._tool?Xc:100}_showPhysicalRange(e=this._drawWallMaxCm,t=0){this._showToast(this._t("toast.physical_range",{min:yl(t,this._imperial),max:yl(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=Tm(i,{pitch:Eh,seedIds:t,geometry:Rm(e,{excludeDraftId:this._activeDraftId})});return s.merged?(e.partitions=s.partitions,Im(e.openings,e.partitions,s.openingMoves,{coordScale:Pv,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=xu(this._path,ku(this._path.length-1,this._draftSegmentCms,this._drawWallCm,ul)),i=t.length;if((e.partitions||[]).length+i>Av)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>=Av:(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[Lh(ri(e[0],t)),Lh(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,zh):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=Gc(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=_u({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=uu({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=_u({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?yu(i.value,e,{...o,anchor:s}):function(e,t,i){if(!(ou(t)&&i.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const s=i.epsilon??nu,n=i.excludePoints||[],o=vu(fu(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=bu(o,t,i.gridStep);!mu(l,n,s)&&gu(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))<=Ah);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:Qc(Number(e.cm)||15),..."circle"===e.shape?{}:{angle:eh(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=rg(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=Ud(Rh({spaces:[e]}),e.id),i=t?j_(e,t,Pv,.02*Nh).contour.map(e=>e.map(e=>e/Pv)):Ad(e.open_spans).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]]);e.walls=Fl(e.walls,e.rooms||[],Eh,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=Bd(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=Bd(e),i=[...this._pendingPhysicalWrites.entries()];for(const[t,s]of i){const i=Ng(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!==Bd(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 Qm?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)}))}_limitSegmentsOf(e){const t=[];for(const i of e?.wall_segments||[])i?.a&&i?.b&&t.push({id:String(i.id||""),a:i.a,b:i.b,cm:Number(i.cm)});for(const i of e?.partitions||[])i?.a&&i?.b&&t.push({id:String(i.id||""),a:i.a,b:i.b,cm:Number(i.cm)});for(const i of e?.room_drafts||[]){const e=Array.isArray(i?.points)?i.points:[],s=Array.isArray(i?.segments)?i.segments:[];for(let n=0;n+1e?.id===t);if(!i)return[];const s=Number(i.cell_cm)>0?Number(i.cell_cm):5,n=this._limitSegmentsOf(i),o=[...xd(n),...Md(n,s,Eh),...Dd(n,s,Eh)];for(const e of i.rooms||[]){const t=String(e?.id||"");if(!t)continue;let n=null;try{n=kc(i.rooms||[],t,i.walls||[],[],Eh,s,Eh,1)}catch{n=null}o.push(...Pd(t,n,s,Eh))}return o}_junctionLimitLabel(e){const t=e=>String(Math.round(10*e)/10);return this._t(`junction.limit_${e.rule}`,{actual:t(e.actual),limit:t(e.limit)})}_junctionLimitsIntroduced(e,t,i){let s=[];try{const e=$g(t).config;s=this._junctionLimitViolations(e,i)}catch{return[]}let n=[];try{n=this._junctionLimitViolations(e,i)}catch{return[]}return function(e,t){const i=new Map;for(const e of t||[])i.set(e.rule,(i.get(e.rule)||0)+1);const s=new Map;for(const t of e||[])s.set(t.rule,[...s.get(t.rule)||[],t]);const n=[];for(const[e,t]of s){const s=t.length-(i.get(e)||0);s>0&&n.push(...t.slice(0,s))}return n}(n,s)}_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||Ng(t)===Ng(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 Qm("invalid-room",t.spaceId);const i=$g(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 Qm("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,Eh,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}=bg(i,fg(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;oZm||hg(r,i,s)>Zm)continue;const l=cg(a,i,s),c=cg(r,i,s);l<-Zm||c>1+Zm||c<=l+Zm||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]/Pv,e[1]/Pv]):[];s.push(...i.map(e=>[e[0],e[1]])),c=lg(e)<=lg(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;const h=JSON.parse(JSON.stringify(s));this._restoreGeometryStateInConfig(h,t);const d=this._junctionLimitsIntroduced(a,h,t.spaceId);if(d.length)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(t),this._showToast(this._junctionLimitLabel(d[0])),!1;Mg(s,a),this._recordGeometry(e,l);const p=s.spaces.find(e=>e.id===t.spaceId),u=this._pendingPhysicalWrites.get(t.spaceId);return this._pendingPhysicalWrites.set(t.spaceId,{before:u?.before||l,fingerprint:Ng(p)}),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=Ng(i)!==Ng(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=$g(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;Mg(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:Ng(a)})}else Mg(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>Av)||(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 b=this._geometrySnapshot(),y=this._activeDraftId||e.draft.id,w={...o||a,id:y,points:g.map(e=>[e[0]/Pv,e[1]/Pv]),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=y,this._resumeDraftBySpace[this._space]=y,this._path=g,this._draftSegmentCms=this._adoptDraftCms(g,f.map(e=>Number(e.cm)),y),this._physicalSel=null,this._commitPhysicalGeometry(this._t("history.draft_merge"),b),null!=d&&this._offerWallFaces(i,i.length-1,s)}_adoptDraftCms(e,t,i){const s=Math.max(0,e.length-1),n=ku(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])*Pv,Number(e?.[1])*Pv]):[],n=Array.isArray(t?.segments)?t.segments:[];return ku(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]/Pv,e[1]/Pv]),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=Uu(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=s_(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=Gu(this._wallFaceGraph(this._wallGraphSources([]),t),e,t);if(!s){const n=s_(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=s_(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(Xc,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:Qc(i),angle:0},o=(s.wall_columns||[]).find(e=>yh(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]/Pv,t[1]/Pv]}),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:yl(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:yl(i.cm,this._imperial),shape:i.shape,angle:this._angleField("square"===i.shape?eh(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:yl(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(),Ev(e);const n=this._svgPoint(e),o=[];for(const e of[...s.wall_columns].reverse())vh(n,ah(e,this._cellCm,this._gridPitch))&&o.push({kind:"column",id:e.id});for(const e of[...s.partitions].reverse()){const t=oh(e.a,e.b,e.cm,this._cellCm,this._gridPitch);t&&vh(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(-Ah*Pv-Math.min(...n),Math.min(Ah*Pv-Math.max(...n),i[0])),Math.max(-Ah*Pv-Math.min(...o),Math.min(Ah*Pv-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]/Pv,n.a[1]/Pv],e.b=[n.b[0]/Pv,n.b[1]/Pv];for(const e of s.openings||[]){if("partition"!==e.host?.kind||e.host.id!==t.id)continue;const i=Jp(e,[n],Pv,this._cellCm,this._gridPitch).resolved;i&&Object.assign(e,su(e,i,Pv))}}}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&&yh(e,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));e&&(e.center=[(n.center[0]+t.delta[0])/Pv,(n.center[1]+t.delta[1])/Pv])}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(),Ev(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:eh(t.angle),angle:eh(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=eh(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&&yh(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]*Pv,e.a[1]*this._spaceH],b:[e.b[0]*Pv,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=Na(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(qa(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])),b=[];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"};b.push(r,l)}const y={roomId:i,edge:s,a:[...r],b:[...l],n:Ia(a.poly,s),roomIds:g,edgeByRoom:m,topology:v,movingOpeningIds:f,sideOwnership:b};if(!Qa(e,t,y,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,y,i,n))){if(!i.some(i=>Ya(Ja(e,t,y,i),y,o)))return{enabled:!1,reason:"partial-shared"};const s={...n,obstacles:[]};return i.some(i=>Qa(e,t,y,i,s))?{enabled:!1,reason:"duplicate-physical-wall"}:t.length&&i.some(t=>Qa(e,[],y,t,n))?{enabled:!1,reason:"opening-conflict"}:{enabled:!1,reason:"invalid-geometry"}}}return{enabled:!0,plan:y}}(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){this._rszLimitViolation=null;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]/Pv,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]/Pv,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]*Pv,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]/Pv,s[e][1]/Pv],n.b=[s[(e+1)%s.length][0]/Pv,s[(e+1)%s.length][1]/Pv])}}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=El(e,t,i,s,n,o,()=>{a=!0});return{walls:a?(e||[]).map(e=>({...e})):r,rejected:a}}(r.walls,c,h,this._wallKeyPitch,Pv,"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,Pv,a.walls||[]).length)return{ok:!1,reason:"wall-metadata"};const m=this._rszSpaceCandidateGeometry(this._space,r);if(!m.ok)return{ok:!1,reason:"physical-geometry"};const g={...this._serverCfg,spaces:(this._serverCfg?.spaces||[]).map(e=>e?.id===this._space?r:e)},f=this._junctionLimitsIntroduced(g,this._serverCfg,this._space);return f.length?(this._rszLimitViolation=f[0],{ok:!1,reason:"junction-limit"}):{ok:!0,value:{preview:{space:this._space,sp:r},beforeWalls:a.walls||[],afterWalls:r.walls||[],artifact:m.wallGeometry}}}_rszAcceptPreview(e,t){if(this._cfgEpoch++,!e||!t)return;const i=Lc(t);if(!i)return;const s=`${this._space}|${this._cfgEpoch}|${e.sp.rooms.length}`;Object.defineProperty(i,"sourceFingerprint",{value:Bd([e.sp,this._cellCm,this._gridPitch]),enumerable:!1});const n={key:s,value:i};fv(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));Ev(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._rszLimitViolation?`${this._t("resize.limit_stopped")} — `+this._junctionLimitLabel(this._rszLimitViolation):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};fv(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,b=f?ed(v>8?2.5:v,f,u.w,this._kiosk?this._kioskScale.icon:1)*g.stageWidth/100:24,y=Math.max(10,.77*b),w=this._t("room.settings_short"),k=y*(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?mh(_h(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:y});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 W`
@@ -5887,7 +5887,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 ${this._settingsDialog.bgColor?q``:q`${this._t("gs.bg_theme")}`} `:q`
${this._t("gs.bg_daynight_hint")}
`} - ${Rr(this.hass)?G:q`
${this._t("gs.sun_missing")}
`} + ${Er(this.hass)?G:q`
${this._t("gs.sun_missing")}
`}
${this._renderCompass()}
@@ -5939,7 +5939,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26
`:G} -
${this._t("gs.about_version",{v:cv})}
+
${this._t("gs.about_version",{v:_v})}
${this._t("gs.about_github")} @@ -5996,7 +5996,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 ${e.busy?"…":this._t("btn.save")}
- `}_saveKioskScale(e){this._kioskScale={...this._kioskScale,...e};try{localStorage.setItem(Mv,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const e=this._kioskScale,t=(t,i)=>q` + `}_saveKioskScale(e){this._kioskScale={...this._kioskScale,...e};try{localStorage.setItem(Iv,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const e=this._kioskScale,t=(t,i)=>q`
${this._rangeInput(50,300,5,Math.round(100*e[t]),e=>this._saveKioskScale({[t]:e/100}))} ${Math.round(100*e[t])}% @@ -6053,7 +6053,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 ${this._spaceDialog?this._renderSpaceDialog():G} ${this._importDialog?this._renderImportDialog():G} ${this._toast?q``: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){mf||wf(e);const t=_f,i=t.active.join(",");if(!i||i===ff)return;ff=i;const s=new Map(of.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}`)}(cv);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,b=R_(v),y=this._viewOr(this._baseVb()),w=this._floorView(y),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),N=I?.stageColor||k,E=I?.sceneBrightness??1;return q` + `;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){yf||Mf(e);const t=bf,i=t.active.join(",");if(!i||i===kf)return;kf=i;const s=new Map(hf.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}`)}(_v);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,b=E_(v),y=this._viewOr(this._baseVb()),w=this._floorView(y),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),N=I?.stageColor||k,E=I?.sceneBrightness??1;return q` this._markupClick(e)} @wheel=${e=>this._onWheel(e)} @pointerdown=${e=>{this._notePointer(e),this._stagePointerDown(e)}} @@ -6148,7 +6148,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 @pointerleave=${e=>this._stagePointerLeave(e)} @pointerup=${e=>this._stagePointerUp(e)} @pointercancel=${e=>this._stagePointerCancel(e)}> - ${Fr(R,A)} + ${Lr(R,A)} ${this._renderEditorSecondary()}
+ transform=${p?.structural?If():G}> ${""} ${this._wallHatchDefs(d.color)}${W`${this._paperShapes(s.rooms).map(e=>"path"in e?W``:"poly"in e?W``:W``)}`} ${this._editing?this._renderMarkupDefs(o):G} @@ -6250,7 +6250,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 ${this._renderIsoWalls(p,u)} `:G} ${""} -
+
${c.map(e=>this._renderDevice(e,y,g))} ${this._renderVacuums(c,y)} ${this._renderVacFit(y)} @@ -6362,7 +6362,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 `:G} ${this._toast?q``: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=op(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=op(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=k_(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:S_(this._planHass,e,i)}}_stampActivity(e,t,i){let s=this._activityRt.get(e);s||(s=om(i||"",[]),this._activityRt.set(e,s)),null!=i&&(s.sources=i),rm(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&&am(r,a,o,window.clearTimeout.bind(window)):(r=om(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=lm(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=cp(this.hass.states[i||""]?.state),n=np(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"!==_p(e.marker?.vacuum)&&!n?.path.length;!s&&o.moving&&(o.endedTs=Date.now(),a&&o.lastPos&&(o.trail=lp(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=lp(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?np(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(ap).filter(Boolean)),s=new Set;for(const t of e){const e=ap(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=>q` + `}_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=hp(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=hp(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=C_(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:T_(this._planHass,e,i)}}_stampActivity(e,t,i){let s=this._activityRt.get(e);s||(s=hm(i||"",[]),this._activityRt.set(e,s)),null!=i&&(s.sources=i),pm(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&&dm(r,a,o,window.clearTimeout.bind(window)):(r=hm(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=um(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=_p(this.hass.states[i||""]?.state),n=cp(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"!==bp(e.marker?.vacuum)&&!n?.path.length;!s&&o.moving&&(o.endedTs=Date.now(),a&&o.lastPos&&(o.trail=up(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=up(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?cp(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(dp).filter(Boolean)),s=new Set;for(const t of e){const e=dp(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=>q`
`}_vacMapId(e,t,i=this._planHass){const s=this._vacEntity(e),n=s?i?.states?.[s]?.attributes?.selected_map:null;return o=t.mapId,a=n,"default"!==o?o:null!=a?String(a):"default";var o,a}_vacSaveMatrix(e,t,i,s){const n=this._devices.find(t=>t.id===e),o=n?this._vacEnsureMarker(n):this._serverCfg?.markers?.find(t=>t.id===e);if(!o)return!1;const a={...o.vacuum||{}};return a.source=t,a.calibration={...a.calibration||{},[i]:s.map(e=>Number(e.toFixed(6)))},o.vacuum=a,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_vacPlanRoomAnchors(e){return(this._spaceModelById(e)?.rooms||[]).map(e=>({room:e,poly:hi(e)})).filter(({room:e,poly:t})=>e.name&&t).map(({room:e,poly:t})=>{const i=Qd(t);return i?{name:String(e.name),cx:i[0],cy:i[1]}:null}).filter(Boolean)}_vacAutoCalibrate(e){const t=this._vacSource(e),i=t?np(this.hass?.states[t]?.attributes):null;if(!t||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._vacPlanRoomAnchors(e.space),n=rp(i.rooms,s);if(!n)return void this._showToast(this._t("vac.autocal_no_match"));const o=this._serverCfg?.spaces?.find(t=>t.id===e.space),a=Number(o?.cell_cm),r=Number.isFinite(a)&&a>0?a:5,l=function(e,t,i){return![e,t,i].every(Number.isFinite)||e<0||t<=0||i<=0?Number.POSITIVE_INFINITY:e/t*i}(n.residual,this._gridPitch,r),c=this._vacMapId(e,i);l>40?this._vacCalConfirm={markerId:e.id,source:t,mapId:c,matrix:n.matrix,rooms:n.matched.length,error:li(l,"mi"===this.hass?.config?.unit_system?.length)}:this._vacSaveMatrix(e.id,t,c,n.matrix)&&this._showToast(ss(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))}_vacApplyCalibrationProposal(e){const t=this._vacCalConfirm;if(t){if(this._vacCalConfirm=null,e){const e=this._devices.find(e=>e.id===t.markerId),i=pp(t.matrix);if(!e||!i)return;if(this._markerDialog=null,e.space!==this._space&&!this._commitSpace(e.space))return;return void(this._vacFit={markerId:t.markerId,source:t.source,mapId:t.mapId,p:i,drag:null})}this._vacSaveMatrix(t.markerId,t.source,t.mapId,t.matrix)&&this._showToast(ss(this._t("vac.autocal_done"),{rooms:String(t.rooms)}))}}_vacStartFit(e){const t=this._vacSource(e),i=t?np(this.hass?.states[t]?.attributes):null;if(!t||!i)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._vacMapId(e,i),n=e.marker?.vacuum?.calibration?.[s],o=this._spaceModelById(e.space);if(!o)return;const a=o.vb,r=n&&6===n.length&&pp(n)||function(e,t){const i=[],s=[];for(const t of e)null!=t.x0?(i.push(t.x0,t.x1),s.push(t.y0,t.y1)):(i.push(t.cx),s.push(t.cy));if(!i.length)return{ox:t[0]+t[2]/2,oy:t[1]+t[3]/2,s:t[2]/1e4,rot:0,mir:!0};const n=Math.min(...i),o=Math.max(...i),a=Math.min(...s),r=Math.max(...s),l=Math.max(o-n,r-a)||1,c={ox:0,oy:0,s:.6*Math.min(t[2],t[3])/l,rot:0,mir:!0},h=dp(c),[d,p]=Yd(h,(n+o)/2,(a+r)/2);return c.ox=t[0]+t[2]/2-d,c.oy=t[1]+t[3]/2-p,c}(i.rooms,a);this._markerDialog=null,(e.space===this._space||this._commitSpace(e.space))&&(this._vacFit={markerId:e.id,source:t,mapId:s,p:r,drag:null})}_vacFitSave(){const e=this._vacFit;if(!e)return;const t=this._vacSaveMatrix(e.markerId,e.source,e.mapId,dp(e.p));this._vacFit=null,t&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(e){const t=this._vacFit;if(!t)return;const i=np(this.hass?.states[t.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),n={...t.p,...e};this._vacFit={...t,p:up(n,t.p,s[0],s[1])}}_vacGhostCentre(e){const t=[],i=[];for(const s of e)t.push(s.x0??s.cx,s.x1??s.cx),i.push(s.y0??s.cy,s.y1??s.cy);return t.length?[(Math.min(...t)+Math.max(...t))/2,(Math.min(...i)+Math.max(...i))/2]:[0,0]}_vacDelta(e,t,i){const s=this._stageEl,n=s?.clientWidth||1,o=s?.clientHeight||1;return[t/n*e.w,i/o*e.h]}_vacFitPointer(e,t){const i=this._vacFit;if(!i)return;if(e.stopPropagation(),"pointerdown"===e.type){const t=e.target,s=t.getAttribute?.("data-corner");try{e.currentTarget.setPointerCapture?.(e.pointerId)}catch{}return void(this._vacFit={...i,drag:s?{kind:"scale",sx:e.clientX,sy:e.clientY,p0:{...i.p},fx:Number(s.split(",")[0]),fy:Number(s.split(",")[1])}:{kind:"move",sx:e.clientX,sy:e.clientY,p0:{...i.p},fx:0,fy:0}})}const s=i.drag;if(s){if("pointermove"===e.type){const[n,o]=this._vacDelta(t,e.clientX-s.sx,e.clientY-s.sy);if("move"===s.kind)this._vacFit={...i,p:{...s.p0,ox:s.p0.ox+n,oy:s.p0.oy+o}};else{const e=np(this.hass?.states[i.source]?.attributes),t=this._vacGhostCentre(e?.rooms||[]),a=dp(s.p0),[r,l]=Yd(a,t[0],t[1]),[c,h]=Yd(a,s.fx,s.fy),d=Math.hypot(r-c,l-h)||1,[p,u]=[2*r-c,2*l-h],_=Math.hypot(p+2*n-c,u+2*o-h)/2,m=Math.max(.05,_/d),g={...s.p0,s:s.p0.s*m};this._vacFit={...i,p:up(g,s.p0,s.fx,s.fy)}}return}"pointerup"!==e.type&&"pointercancel"!==e.type||(this._vacFit={...i,drag:null})}}_renderVacFit(e){const t=this._vacFit;if(!t)return G;const i=np(this._renderPlanHass?.states?.[t.source]?.attributes);if(!i)return G;const s=dp(t.p),n=[],o=[],a=[];for(const e of i.rooms){if(null==e.x0)continue;const t=[[e.x0,e.y0],[e.x1,e.y0],[e.x1,e.y1],[e.x0,e.y1]].map(([e,t])=>Yd(s,e,t));t.forEach(([e,t])=>{o.push(e),a.push(t)});const[i,r]=Yd(s,e.cx,e.cy);n.push(W` - ${e.name}`)}let r=G;if(i.pos){const[t,n]=Yd(s,i.pos.x,i.pos.y);r=W``}const l=[];if(o.length){const t=(()=>{const e=s[0]*s[4]-s[1]*s[3];return(t,i)=>[(s[4]*(t-s[2])-s[1]*(i-s[5]))/e,(-s[3]*(t-s[2])+s[0]*(i-s[5]))/e]})(),i=Math.min(...o),n=Math.max(...o),r=Math.min(...a),c=Math.max(...a),h=.022*e.w,d=h/4;for(const[e,s,o,a]of[[i,r,n,c],[n,r,i,c],[n,c,i,r],[i,c,n,r]]){const i=t(o,a);l.push(W``}_vacMapId(e,t,i=this._planHass){const s=this._vacEntity(e),n=s?i?.states?.[s]?.attributes?.selected_map:null;return o=t.mapId,a=n,"default"!==o?o:null!=a?String(a):"default";var o,a}_vacSaveMatrix(e,t,i,s){const n=this._devices.find(t=>t.id===e),o=n?this._vacEnsureMarker(n):this._serverCfg?.markers?.find(t=>t.id===e);if(!o)return!1;const a={...o.vacuum||{}};return a.source=t,a.calibration={...a.calibration||{},[i]:s.map(e=>Number(e.toFixed(6)))},o.vacuum=a,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_vacPlanRoomAnchors(e){return(this._spaceModelById(e)?.rooms||[]).map(e=>({room:e,poly:hi(e)})).filter(({room:e,poly:t})=>e.name&&t).map(({room:e,poly:t})=>{const i=np(t);return i?{name:String(e.name),cx:i[0],cy:i[1]}:null}).filter(Boolean)}_vacAutoCalibrate(e){const t=this._vacSource(e),i=t?cp(this.hass?.states[t]?.attributes):null;if(!t||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._vacPlanRoomAnchors(e.space),n=pp(i.rooms,s);if(!n)return void this._showToast(this._t("vac.autocal_no_match"));const o=this._serverCfg?.spaces?.find(t=>t.id===e.space),a=Number(o?.cell_cm),r=Number.isFinite(a)&&a>0?a:5,l=function(e,t,i){return![e,t,i].every(Number.isFinite)||e<0||t<=0||i<=0?Number.POSITIVE_INFINITY:e/t*i}(n.residual,this._gridPitch,r),c=this._vacMapId(e,i);l>40?this._vacCalConfirm={markerId:e.id,source:t,mapId:c,matrix:n.matrix,rooms:n.matched.length,error:li(l,"mi"===this.hass?.config?.unit_system?.length)}:this._vacSaveMatrix(e.id,t,c,n.matrix)&&this._showToast(ss(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))}_vacApplyCalibrationProposal(e){const t=this._vacCalConfirm;if(t){if(this._vacCalConfirm=null,e){const e=this._devices.find(e=>e.id===t.markerId),i=fp(t.matrix);if(!e||!i)return;if(this._markerDialog=null,e.space!==this._space&&!this._commitSpace(e.space))return;return void(this._vacFit={markerId:t.markerId,source:t.source,mapId:t.mapId,p:i,drag:null})}this._vacSaveMatrix(t.markerId,t.source,t.mapId,t.matrix)&&this._showToast(ss(this._t("vac.autocal_done"),{rooms:String(t.rooms)}))}}_vacStartFit(e){const t=this._vacSource(e),i=t?cp(this.hass?.states[t]?.attributes):null;if(!t||!i)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._vacMapId(e,i),n=e.marker?.vacuum?.calibration?.[s],o=this._spaceModelById(e.space);if(!o)return;const a=o.vb,r=n&&6===n.length&&fp(n)||function(e,t){const i=[],s=[];for(const t of e)null!=t.x0?(i.push(t.x0,t.x1),s.push(t.y0,t.y1)):(i.push(t.cx),s.push(t.cy));if(!i.length)return{ox:t[0]+t[2]/2,oy:t[1]+t[3]/2,s:t[2]/1e4,rot:0,mir:!0};const n=Math.min(...i),o=Math.max(...i),a=Math.min(...s),r=Math.max(...s),l=Math.max(o-n,r-a)||1,c={ox:0,oy:0,s:.6*Math.min(t[2],t[3])/l,rot:0,mir:!0},h=gp(c),[d,p]=ep(h,(n+o)/2,(a+r)/2);return c.ox=t[0]+t[2]/2-d,c.oy=t[1]+t[3]/2-p,c}(i.rooms,a);this._markerDialog=null,(e.space===this._space||this._commitSpace(e.space))&&(this._vacFit={markerId:e.id,source:t,mapId:s,p:r,drag:null})}_vacFitSave(){const e=this._vacFit;if(!e)return;const t=this._vacSaveMatrix(e.markerId,e.source,e.mapId,gp(e.p));this._vacFit=null,t&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(e){const t=this._vacFit;if(!t)return;const i=cp(this.hass?.states[t.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),n={...t.p,...e};this._vacFit={...t,p:vp(n,t.p,s[0],s[1])}}_vacGhostCentre(e){const t=[],i=[];for(const s of e)t.push(s.x0??s.cx,s.x1??s.cx),i.push(s.y0??s.cy,s.y1??s.cy);return t.length?[(Math.min(...t)+Math.max(...t))/2,(Math.min(...i)+Math.max(...i))/2]:[0,0]}_vacDelta(e,t,i){const s=this._stageEl,n=s?.clientWidth||1,o=s?.clientHeight||1;return[t/n*e.w,i/o*e.h]}_vacFitPointer(e,t){const i=this._vacFit;if(!i)return;if(e.stopPropagation(),"pointerdown"===e.type){const t=e.target,s=t.getAttribute?.("data-corner");try{e.currentTarget.setPointerCapture?.(e.pointerId)}catch{}return void(this._vacFit={...i,drag:s?{kind:"scale",sx:e.clientX,sy:e.clientY,p0:{...i.p},fx:Number(s.split(",")[0]),fy:Number(s.split(",")[1])}:{kind:"move",sx:e.clientX,sy:e.clientY,p0:{...i.p},fx:0,fy:0}})}const s=i.drag;if(s){if("pointermove"===e.type){const[n,o]=this._vacDelta(t,e.clientX-s.sx,e.clientY-s.sy);if("move"===s.kind)this._vacFit={...i,p:{...s.p0,ox:s.p0.ox+n,oy:s.p0.oy+o}};else{const e=cp(this.hass?.states[i.source]?.attributes),t=this._vacGhostCentre(e?.rooms||[]),a=gp(s.p0),[r,l]=ep(a,t[0],t[1]),[c,h]=ep(a,s.fx,s.fy),d=Math.hypot(r-c,l-h)||1,[p,u]=[2*r-c,2*l-h],_=Math.hypot(p+2*n-c,u+2*o-h)/2,m=Math.max(.05,_/d),g={...s.p0,s:s.p0.s*m};this._vacFit={...i,p:vp(g,s.p0,s.fx,s.fy)}}return}"pointerup"!==e.type&&"pointercancel"!==e.type||(this._vacFit={...i,drag:null})}}_renderVacFit(e){const t=this._vacFit;if(!t)return G;const i=cp(this._renderPlanHass?.states?.[t.source]?.attributes);if(!i)return G;const s=gp(t.p),n=[],o=[],a=[];for(const e of i.rooms){if(null==e.x0)continue;const t=[[e.x0,e.y0],[e.x1,e.y0],[e.x1,e.y1],[e.x0,e.y1]].map(([e,t])=>ep(s,e,t));t.forEach(([e,t])=>{o.push(e),a.push(t)});const[i,r]=ep(s,e.cx,e.cy);n.push(W` + ${e.name}`)}let r=G;if(i.pos){const[t,n]=ep(s,i.pos.x,i.pos.y);r=W``}const l=[];if(o.length){const t=(()=>{const e=s[0]*s[4]-s[1]*s[3];return(t,i)=>[(s[4]*(t-s[2])-s[1]*(i-s[5]))/e,(-s[3]*(t-s[2])+s[0]*(i-s[5]))/e]})(),i=Math.min(...o),n=Math.max(...o),r=Math.min(...a),c=Math.max(...a),h=.022*e.w,d=h/4;for(const[e,s,o,a]of[[i,r,n,c],[n,r,i,c],[n,c,i,r],[i,c,n,r]]){const i=t(o,a);l.push(W` `)}}return q`this._vacFitPointer(t,e)} @pointermove=${t=>this._vacFitPointer(t,e)} @pointerup=${t=>this._vacFitPointer(t,e)} - @pointercancel=${t=>this._vacFitPointer(t,e)}>${n}${r}${l}`}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const e=this.renderRoot,t=this._stageEl,i=this._vacLastView,s=e?.querySelectorAll?.(".vacpuck")||[];if(!t||!i||!s.length)return void(this._vacRaf=0);const n=t.getBoundingClientRect();for(const t of s){const s=t.getAttribute("data-mid"),o=t.getBoundingClientRect(),a=i.x+(o.left+o.width/2-n.left)/n.width*i.w,r=i.y+(o.top+o.height/2-n.top)/n.height*i.h;for(const t of e.querySelectorAll(`line.tip[data-mid="${s}"]`))t.setAttribute("x2",a.toFixed(1)),t.setAttribute("y2",r.toFixed(1))}this._vacRafLoop()})}_renderVacuums(e,t){if(this._markup||"decor"===this._mode)return G;const i=this._space+"|"+t.x+"|"+t.y+"|"+t.w+"|"+t.h,s=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const n=[],o=[];for(const i of e){if(i.hidden||!this._isVacDev(i))continue;if("static_icon"===Bi(i.marker?.display))continue;const e=this._renderDeviceSnapshot?.facts.get(`vacuum:${i.id}`),a=e?.source??this._vacSource(i,this._renderPlanHass);if(!a)continue;const r=e?.telemetry??np(this._renderPlanHass?.states[a]?.attributes);if(!r)continue;const l=String(e?.mapId??this._vacMapId(i,r,this._renderPlanHass)),c=i.marker?.vacuum?.calibration?.[l];if(!c||6!==c.length)continue;const h=e?.runtime??this._vacRt.get(i.id),d=h?.moving??!1,p=_p(i.marker?.vacuum),u="always"===p||"cleaning"===p&&d,_=e?.server??this._vacSrvTrails[i.id],m=_?.current?.map_id===l&&Array.isArray(_.current.points)?_.current:null,g=_?.previous?.map_id===l&&Array.isArray(_.previous.points)?_.previous:null;if("always"===p&&g&&g.points.length>1){const e=g.points.map(([e,t])=>{const[i,s]=Yd(c,e,t),n=this._scenePoint([i,s]);return n[0].toFixed(1)+","+n[1].toFixed(1)}).join(" ");o.push(W``)}if(u){const e=ip(r,m,h?.trail||[]),t=!d||"integration"!==e.source&&"server"!==e.source?e.path:sp(e.path);if(t.length){const e=t.map(e=>e.map(([e,t],i)=>{const[s,n]=Yd(c,e,t),o=this._scenePoint([s,n]);return`${i?"L":"M"} ${o[0].toFixed(1)} ${o[1].toFixed(1)}`}).join(" ")).join(" ");o.push(W``);const s=t[t.length-1];if(d&&s?.length>=2){const e=s[s.length-1],[t,n]=Yd(c,e[0],e[1]),a=this._scenePoint([t,n]),r=a[0].toFixed(1),l=a[1].toFixed(1);o.push(W``)}}}if(!d||!r.pos)continue;const[f,v]=Yd(c,r.pos.x,r.pos.y),b=this._scenePoint([f,v]),y=(b[0]-t.x)/t.w*100,w=(b[1]-t.y)/t.h*100,k=h&&h.lastTs>0&&Date.now()-h.lastTs>6e4,x=i.marker?.icon||i.icon||"mdi:robot-vacuum";n.push(q`
this._vacFitPointer(t,e)}>${n}${r}${l}`}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const e=this.renderRoot,t=this._stageEl,i=this._vacLastView,s=e?.querySelectorAll?.(".vacpuck")||[];if(!t||!i||!s.length)return void(this._vacRaf=0);const n=t.getBoundingClientRect();for(const t of s){const s=t.getAttribute("data-mid"),o=t.getBoundingClientRect(),a=i.x+(o.left+o.width/2-n.left)/n.width*i.w,r=i.y+(o.top+o.height/2-n.top)/n.height*i.h;for(const t of e.querySelectorAll(`line.tip[data-mid="${s}"]`))t.setAttribute("x2",a.toFixed(1)),t.setAttribute("y2",r.toFixed(1))}this._vacRafLoop()})}_renderVacuums(e,t){if(this._markup||"decor"===this._mode)return G;const i=this._space+"|"+t.x+"|"+t.y+"|"+t.w+"|"+t.h,s=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const n=[],o=[];for(const i of e){if(i.hidden||!this._isVacDev(i))continue;if("static_icon"===Bi(i.marker?.display))continue;const e=this._renderDeviceSnapshot?.facts.get(`vacuum:${i.id}`),a=e?.source??this._vacSource(i,this._renderPlanHass);if(!a)continue;const r=e?.telemetry??cp(this._renderPlanHass?.states[a]?.attributes);if(!r)continue;const l=String(e?.mapId??this._vacMapId(i,r,this._renderPlanHass)),c=i.marker?.vacuum?.calibration?.[l];if(!c||6!==c.length)continue;const h=e?.runtime??this._vacRt.get(i.id),d=h?.moving??!1,p=bp(i.marker?.vacuum),u="always"===p||"cleaning"===p&&d,_=e?.server??this._vacSrvTrails[i.id],m=_?.current?.map_id===l&&Array.isArray(_.current.points)?_.current:null,g=_?.previous?.map_id===l&&Array.isArray(_.previous.points)?_.previous:null;if("always"===p&&g&&g.points.length>1){const e=g.points.map(([e,t])=>{const[i,s]=ep(c,e,t),n=this._scenePoint([i,s]);return n[0].toFixed(1)+","+n[1].toFixed(1)}).join(" ");o.push(W``)}if(u){const e=rp(r,m,h?.trail||[]),t=!d||"integration"!==e.source&&"server"!==e.source?e.path:lp(e.path);if(t.length){const e=t.map(e=>e.map(([e,t],i)=>{const[s,n]=ep(c,e,t),o=this._scenePoint([s,n]);return`${i?"L":"M"} ${o[0].toFixed(1)} ${o[1].toFixed(1)}`}).join(" ")).join(" ");o.push(W``);const s=t[t.length-1];if(d&&s?.length>=2){const e=s[s.length-1],[t,n]=ep(c,e[0],e[1]),a=this._scenePoint([t,n]),r=a[0].toFixed(1),l=a[1].toFixed(1);o.push(W``)}}}if(!d||!r.pos)continue;const[f,v]=ep(c,r.pos.x,r.pos.y),b=this._scenePoint([f,v]),y=(b[0]-t.x)/t.w*100,w=(b[1]-t.y)/t.h*100,k=h&&h.lastTs>0&&Date.now()-h.lastTs>6e4,x=i.marker?.icon||i.icon||"mdi:robot-vacuum";n.push(q`
${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=g_(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 q`
`}_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=yo(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=Ns(e.settings?.name_scale),this._roomLabelScale=Ns(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*Pv,y:i.y*Pv};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},Ev(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=Lh(this._drag.ox+c),p=Lh(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},Ev(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/Pv,y:o.y/Pv,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 q`
`}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=Es(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 q`
- ${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=Es(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=Pv,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 W``;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=Es(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} · ${rr(Gr(Math.abs(s-t),this._cellCm,this._gridPitch)*Gr(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=zv,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 W``;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 W` `})} - `}_renderOpeningCenterTick(e){const t=(e.angle+90)*Math.PI/180,i=xh(15,this._cellCm);return W``})} + `}_renderOpeningCenterTick(e){const t=(e.angle+90)*Math.PI/180,i=Dh(15,this._cellCm);return W``}_renderOpeningDimensionGuides(e){const t=e.labels.flatMap(e=>e.dimension?[e.dimension]:[]);if(!t.length)return W``;const i=this._cssPxToRender(4);return W``}_roomCenter(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}_openingAmt(e){const t=e.contact&&this._renderOpeningEntityAvailable(e.contact)?this._renderPlanHass.states[e.contact]?.state:null;return mi(e.type,t,!!e.invert)}_planEntityAvailable(e){return!!e&&(!ko(this._fullRegistryHass,e,wo(this._markers))&&"active"===this._bindingStatus("entity:"+e).kind)}_renderEntityAvailable(e){return!!e&&(!ko(this._renderPlanHass,e,wo(this._markers))&&(!!this._renderPlanHass.entities?.[e]&&!!this._renderPlanHass.states?.[e]))}_openingEntityAvailable(e){return function(e,t,i=An(e)){return!!t&&"active"===Bn(e,`entity:${t}`,i).kind}(this.hass,e,this._haRegistry)}_renderOpeningEntityAvailable(e){return t=this._renderPlanHass,!!(i=e)&&!!t?.states?.[i];var t,i}_renderOpeningPlacementPreview(){const e=this._openingPreview;if(!e)return W``;const t={type:e.type,length:e.renderedLength,angle:e.angle,amount:mi(e.type,null),flipH:e.flipH,flipV:e.flipV,base:"var(--hp-open)",tone:"var(--hp-open)",cellCm:this._cellCm,gridPitch:this._gridPitch,face:e.face},i="passage"===e.type?function(e,t){const i=Math.max(0,e.renderedLength)/2,s=Math.max(0,e.target.physicalHalfWidth),n=s+.18*Math.max(0,t),o=e=>({x1:e,y1:-n,x2:e,y2:n});return{rect:{x:-i,y:s?-s:0,width:2*i,height:2*s},boundaries:[o(-i),o(i)]}}(e,xh(this._gridPitch,this._cellCm)):null;return W``}_roomCenter(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}_openingAmt(e){const t=e.contact&&this._renderOpeningEntityAvailable(e.contact)?this._renderPlanHass.states[e.contact]?.state:null;return mi(e.type,t,!!e.invert)}_planEntityAvailable(e){return!!e&&(!ko(this._fullRegistryHass,e,wo(this._markers))&&"active"===this._bindingStatus("entity:"+e).kind)}_renderEntityAvailable(e){return!!e&&(!ko(this._renderPlanHass,e,wo(this._markers))&&(!!this._renderPlanHass.entities?.[e]&&!!this._renderPlanHass.states?.[e]))}_openingEntityAvailable(e){return function(e,t,i=An(e)){return!!t&&"active"===Bn(e,`entity:${t}`,i).kind}(this.hass,e,this._haRegistry)}_renderOpeningEntityAvailable(e){return t=this._renderPlanHass,!!(i=e)&&!!t?.states?.[i];var t,i}_renderOpeningPlacementPreview(){const e=this._openingPreview;if(!e)return W``;const t={type:e.type,length:e.renderedLength,angle:e.angle,amount:mi(e.type,null),flipH:e.flipH,flipV:e.flipV,base:"var(--hp-open)",tone:"var(--hp-open)",cellCm:this._cellCm,gridPitch:this._gridPitch,face:e.face},i="passage"===e.type?function(e,t){const i=Math.max(0,e.renderedLength)/2,s=Math.max(0,e.target.physicalHalfWidth),n=s+.18*Math.max(0,t),o=e=>({x1:e,y1:-n,x2:e,y2:n});return{rect:{x:-i,y:s?-s:0,width:2*i,height:2*s},boundaries:[o(-i),o(i)]}}(e,Dh(this._gridPitch,this._cellCm)):null;return W` `}_renderOpenings(e){const t=this._openingsR;if(!t.length)return W``;const i=this._spaceModel();if(!i)return W``;const s=e.color,n=this._spaceWalls,o=this._openCuts(),a=this._openingWallIndexFor(i,o).value;return W`${t.map(e=>{if(e.orphanReason)return W``}_renderOpenings(e){const t=this._openingsR;if(!t.length)return W``;const i=this._spaceModel();if(!i)return W``;const s=e.color,n=this._spaceWalls,o=this._openCuts(),a=this._openingWallIndexFor(i,o).value;return W`${t.map(e=>{if(e.orphanReason)return W`{t.stopPropagation(),this._editOpening(e)}}> - + ! - `;const t=this._openingAmt(e),i=t>0&&!!e.contact&&this._renderOpeningEntityAvailable(e.contact)?"var(--hp-open)":s,o="gate"===e.type?!e.flip_v:e.flip_v,r=e.partitionHost||n.length||"gate"===e.type?this._openingFace(e,a,!!o):{ox:0,oy:0,cm:0,side:-1},l={type:e.type,length:e.rlen,angle:e.angle,amount:t,flipH:!!e.flip_h,flipV:!!e.flip_v,base:s,tone:i,cellCm:this._cellCm,gridPitch:this._gridPitch,face:r},{half:c,outlineHalf:h,hitHalf:d}=d_(l),p=xh(10,this._cellCm),u=xh(12,this._cellCm);return W``;const t=this._openingAmt(e),i=t>0&&!!e.contact&&this._renderOpeningEntityAvailable(e.contact)?"var(--hp-open)":s,o="gate"===e.type?!e.flip_v:e.flip_v,r=e.partitionHost||n.length||"gate"===e.type?this._openingFace(e,a,!!o):{ox:0,oy:0,cm:0,side:-1},l={type:e.type,length:e.rlen,angle:e.angle,amount:t,flipH:!!e.flip_h,flipV:!!e.flip_v,base:s,tone:i,cellCm:this._cellCm,gridPitch:this._gridPitch,face:r},{half:c,outlineHalf:h,hitHalf:d}=g_(l),p=Dh(10,this._cellCm),u=Dh(12,this._cellCm);return W` - ${p_(l)} + ${f_(l)} + rx="${Dh(6,this._cellCm)}"> this._opClick(t,e)} @@ -6546,7 +6546,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 @pointermove=${t=>this._opPointerMove(t,e)} @pointerup=${t=>this._opPointerUp(t,e)} @pointercancel=${t=>this._opPointerUp(t,e)}> - `})}`}_renderOpeningLocks(e){const t=this._openingsR.filter(e=>!e.orphanReason&&("door"===e.type||"gate"===e.type)&&e.lock&&this._renderOpeningEntityAvailable(e.lock));if(!t.length)return q``;const i=this._spaceModel();if(!i)return q``;const s=this._openCuts(),n=this._openingWallIndexFor(i,s).value;return q`${t.map(t=>{const i=this._renderPlanHass.states[t.lock]?.state,s="locked"===i,o=s||["unlocked","open","opening","unlocking","locking"].includes(String(i)),a=(t.angle+90)*Math.PI/180,r="gate"===t.type?this._openingFace(t,n,!t.flip_v):null,l=xh(16,this._cellCm),c=r?-l*r.side:l*(t.flip_v?-1:1),h=t.rx+Math.cos(a)*c,d=t.ry+Math.sin(a)*c,p=this._scenePoint([h,d]),u=(p[0]-e.x)/e.w*100,_=(p[1]-e.y)/e.h*100;return q`
`})}`}_renderOpeningLocks(e){const t=this._openingsR.filter(e=>!e.orphanReason&&("door"===e.type||"gate"===e.type)&&e.lock&&this._renderOpeningEntityAvailable(e.lock));if(!t.length)return q``;const i=this._spaceModel();if(!i)return q``;const s=this._openCuts(),n=this._openingWallIndexFor(i,s).value;return q`${t.map(t=>{const i=this._renderPlanHass.states[t.lock]?.state,s="locked"===i,o=s||["unlocked","open","opening","unlocking","locking"].includes(String(i)),a=(t.angle+90)*Math.PI/180,r="gate"===t.type?this._openingFace(t,n,!t.flip_v):null,l=Dh(16,this._cellCm),c=r?-l*r.side:l*(t.flip_v?-1:1),h=t.rx+Math.cos(a)*c,d=t.ry+Math.sin(a)*c,p=this._scenePoint([h,d]),u=(p[0]-e.x)/e.w*100,_=(p[1]-e.y)/e.h*100;return q`
{e.stopPropagation(),"view"===this._mode&&(this._openingInfo=t)}}>
- `}_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/Pv,y:e.y/this._spaceH,angle:e.angle,length:i&&!e.lengthTouched?i.length:this._cmToUnits(Math.max(20,e.lengthCm))/Pv,...e.host?{host:e.host}:{}},n=Zp(i,s),o=e.host?n?Xp(s,t,Pv,this._cellCm,this._gridPitch):Jp(s,t,Pv,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 q` + `}_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/zv,y:e.y/this._spaceH,angle:e.angle,length:i&&!e.lengthTouched?i.length:this._cmToUnits(Math.max(20,e.lengthCm))/zv,...e.host?{host:e.host}:{}},n=su(i,s),o=e.host?n?iu(s,t,zv,this._cellCm,this._gridPitch):tu(s,t,zv,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 q`
`:G} {const i=mv(t.target.value);null!=i&&(this._openingDialog={...e,lengthCm:i,lengthTouched:!0})}} /> + @input=${t=>{const i=yv(t.target.value);null!=i&&(this._openingDialog={...e,lengthCm:i,lengthTouched:!0})}} /> ${"passage"===e.type&&(e.contact||e.lock)?q`
@@ -6662,7 +6662,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26
- `}_gridLevels(){const e=this._stageEl,t=this._viewOr(this._baseVb()),i=e&&e.clientWidth&&t.w?e.clientWidth/t.w:1;return function(e,t,i=7){if(!(e>0&&t>0&&Number.isFinite(t)))return null;const s=td.find(s=>e*s*t>=i);if(void 0===s)return null;const n=td.find(e=>e>=5*s)??5*s;return{fine:s,coarse:n}}(this._gridPitch,i)}_renderMarkupDefs(e){const t=this._gridLevels();if(!t)return W``;const i=this._gridPitch*t.fine,s=this._gridPitch*t.coarse,n=this._gridPitch*t.fine*.14;return W` + `}_gridLevels(){const e=this._stageEl,t=this._viewOr(this._baseVb()),i=e&&e.clientWidth&&t.w?e.clientWidth/t.w:1;return function(e,t,i=7){if(!(e>0&&t>0&&Number.isFinite(t)))return null;const s=ad.find(s=>e*s*t>=i);if(void 0===s)return null;const n=ad.find(e=>e>=5*s)??5*s;return{fine:s,coarse:n}}(this._gridPitch,i)}_renderMarkupDefs(e){const t=this._gridLevels();if(!t)return W``;const i=this._gridPitch*t.fine,s=this._gridPitch*t.coarse,n=this._gridPitch*t.fine*.14;return W` @@ -6676,12 +6676,12 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 - `}_renderPhysicalEditorLayer(){const e=this._spaceModel();if(!e)return W``;const t=this._gridPitch,i=this._viewOr(this._baseVb()),s=this._stageEl,n=s?.clientWidth?i.w/s.clientWidth:t/8,o=Math.max(.22*t,8*n),a=Math.max(o,12*n),r=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`,l=(e,t)=>this._physicalSel?.kind===e&&this._physicalSel.id===t||"column"===e&&this._duplicateColumnId===t,c=(e.room_drafts||[]).flatMap(e=>e.points.slice(0,-1).map((t,i)=>{const s=e.points[i+1],o=Number(e.segments[i]?.cm),a=wl(Number.isFinite(o)?o:15,this._cellCm,this._gridPitch);return W``}_renderPhysicalEditorLayer(){const e=this._spaceModel();if(!e)return W``;const t=this._gridPitch,i=this._viewOr(this._baseVb()),s=this._stageEl,n=s?.clientWidth?i.w/s.clientWidth:t/8,o=Math.max(.22*t,8*n),a=Math.max(o,12*n),r=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`,l=(e,t)=>this._physicalSel?.kind===e&&this._physicalSel.id===t||"column"===e&&this._duplicateColumnId===t,c=(e.room_drafts||[]).flatMap(e=>e.points.slice(0,-1).map((t,i)=>{const s=e.points[i+1],o=Number(e.segments[i]?.cm),a=Ml(Number.isFinite(o)?o:15,this._cellCm,this._gridPitch);return W`{this._physicalSel={kind:"draft",id:e.id,segment:i},this._registerPhysicalTap("draft",e.id,i)}}>`})),h=(e.partitions||[]).map(e=>{const t=oh(e.a,e.b,e.cm,this._cellCm,this._gridPitch);return t?W`{this._physicalSel={kind:"draft",id:e.id,segment:i},this._registerPhysicalTap("draft",e.id,i)}}>`})),h=(e.partitions||[]).map(e=>{const t=hh(e.a,e.b,e.cm,this._cellCm,this._gridPitch);return t?W`this._physicalDown(t,"partition",e.id)} @@ -6692,7 +6692,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 stroke-width=${24} vector-effect="non-scaling-stroke" @pointerdown=${t=>this._physicalDown(t,"partition",e.id)} @pointermove=${e=>this._physicalMove(e)} - @pointerup=${e=>this._physicalUp(e)}>`}),d=(e.wall_columns||[]).map(e=>{const t=ah(this._physicalRotate?.id===e.id?{...e,angle:this._physicalRotate.angle}:e,this._cellCm,this._gridPitch);return W`this._physicalUp(e)}>`}),d=(e.wall_columns||[]).map(e=>{const t=dh(this._physicalRotate?.id===e.id?{...e,angle:this._physicalRotate.angle}:e,this._cellCm,this._gridPitch);return W`this._physicalDown(t,"column",e.id)} @@ -6700,20 +6700,20 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 @pointerup=${e=>this._physicalUp(e)}>`}),p=this._physicalDrag,u=(()=>{if(!p?.moved)return G;if("partition"===p.kind&&0===Number(p.base.cm)){const e=p.base;return W``}const e="partition"===p.kind?oh(p.base.a,p.base.b,p.base.cm,this._cellCm,this._gridPitch):ah(p.base,this._cellCm,this._gridPitch);return e?W``}const e="partition"===p.kind?hh(p.base.a,p.base.b,p.base.cm,this._cellCm,this._gridPitch):dh(p.base,this._cellCm,this._gridPitch);return e?W``:G})(),_=(()=>{const i=this._physicalSel;if(!i)return G;if("draft"===i.kind){const t=e.room_drafts.find(e=>e.id===i.id);return t?W` e.join(",")).join(" ")}> ${t.points.map(e=>W``)} - `:G}if("partition"===i.kind){const t=e.partitions.find(e=>e.id===i.id);if(!t)return G;const s=oh(t.a,t.b,t.cm,this._cellCm,this._gridPitch),n=(t.a[0]+t.b[0])/2,a=(t.a[1]+t.b[1])/2;return W` + `:G}if("partition"===i.kind){const t=e.partitions.find(e=>e.id===i.id);if(!t)return G;const s=hh(t.a,t.b,t.cm,this._cellCm,this._gridPitch),n=(t.a[0]+t.b[0])/2,a=(t.a[1]+t.b[1])/2;return W` ${s?W``:W``} - `}const s=e.wall_columns.find(e=>e.id===i.id);if(!s)return G;const l=this._physicalRotate?.id===s.id?{...s,angle:this._physicalRotate.angle}:s,c=ah(l,this._cellCm,this._gridPitch);if("square"!==l.shape)return W` + `}const s=e.wall_columns.find(e=>e.id===i.id);if(!s)return G;const l=this._physicalRotate?.id===s.id?{...s,angle:this._physicalRotate.angle}:s,c=dh(l,this._cellCm,this._gridPitch);if("square"!==l.shape)return W` - `;const h=wl(l.cm,this._cellCm,this._gridPitch),d=eh(l.angle)*Math.PI/180,p=Math.sin(d),u=-Math.cos(d),_=[l.center[0]+p*h/2,l.center[1]+u*h/2],m=[_[0]+p*Math.max(t,24*n),_[1]+u*Math.max(t,24*n)];return W` + `;const h=Ml(l.cm,this._cellCm,this._gridPitch),d=oh(l.angle)*Math.PI/180,p=Math.sin(d),u=-Math.cos(d),_=[l.center[0]+p*h/2,l.center[1]+u*h/2],m=[_[0]+p*Math.max(t,24*n),_[1]+u*Math.max(t,24*n)];return W` @@ -6721,7 +6721,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 data-kind="rotate" @pointerdown=${e=>this._physicalRotateDown(e,s)} @pointermove=${e=>this._physicalRotateMove(e)} @pointerup=${e=>this._physicalRotateUp(e)}>
- `})();return W`${c}${h}${d}${u}${_}`}_renderHiddenWallDiagnosticOverlay(){if(!this._markup)return W``;const e=this._hiddenWallDiagnosticSnapshot().value;if(!e.segments.length)return W``;const t=wl(5,this._cellCm,this._gridPitch);return W``})();return W`${c}${h}${d}${u}${_}`}_renderHiddenWallDiagnosticOverlay(){if(!this._markup)return W``;const e=this._hiddenWallDiagnosticSnapshot().value;if(!e.segments.length)return W``;const t=Ml(5,this._cellCm,this._gridPitch);return W``}_renderPlanSnapOverlay(){if(!this._markup)return W``;const e=this._planSnapGeometrySnapshot().value,t=this._activePlanSnapCandidate,i=new Set(this._activePlanSnapConflicts.map(e=>e.key)),s=wl(5,this._cellCm,this._gridPitch),n=wl(10,this._cellCm,this._gridPitch);return W``}_renderPlanSnapOverlay(){if(!this._markup)return W``;const e=this._planSnapGeometrySnapshot().value,t=this._activePlanSnapCandidate,i=new Set(this._activePlanSnapConflicts.map(e=>e.key)),s=Ml(5,this._cellCm,this._gridPitch),n=Ml(10,this._cellCm,this._gridPitch);return W``}_syncPlanSnapActiveMarker(e){const t=this.renderRoot?.querySelector('[data-hp="plan-snap-active-marker"]');if(t){if(t.setAttribute("class",`plan-snap-node${e?" active":""}${"line"===e?.kind?" dynamic":""}`),t.setAttribute("data-active",e?"true":"false"),t.setAttribute("visibility",e?"visible":"hidden"),!e)return t.removeAttribute("data-kind"),void t.removeAttribute("data-key");t.setAttribute("data-kind",e.kind),t.setAttribute("data-key",e.key),t.setAttribute("cx",String(e.point[0])),t.setAttribute("cy",String(e.point[1])),t.setAttribute("r",String(wl(10,this._cellCm,this._gridPitch)))}}_syncPlanSnapConflictMarkers(e){const t=new Set(e.map(e=>e.key));for(const e of this.renderRoot?.querySelectorAll('.plan-snap-node[data-kind="endpoint"]')||[])e.setAttribute("class","plan-snap-node"+(t.has(e.dataset.key||"")?" conflict":""))}_planSnapPhysicalSegment(e){let t=0;const i=this._spaceModel();if(!i)return null;if("partition"===e.sourceKind)t=Number(i.partitions.find(t=>t.id===e.sourceId)?.cm)||0;else if("draft"===e.sourceKind){const s=/^(.*):(\d+)$/.exec(e.sourceId),n=s?i.room_drafts.find(e=>e.id===s[1]):null;t=n&&s&&Number(n.segments[Number(s[2])]?.cm)||0}else t=bc(i.rooms,this._spaceWalls,this._openCuts(),[e.a[0],e.a[1],e.b[0],e.b[1]],this._wallKeyPitch,this._cellCm,this._gridPitch,Pv);return t>0?{a:[...e.a],b:[...e.b],halfDepth:wl(t,this._cellCm,this._gridPitch)/2}:null}_drawPreviewJoinPatchD(e,t){if(e.length<2)return"";const i=[];for(let s=0;s+10&&i.push({a:e[s],b:e[s+1],halfDepth:t[s]});if(!i.length)return"";const s=2e-4*this._gridPitch,n=this._planSnapGeometrySnapshot().value.segments.filter(t=>e.some(e=>Hs(e,[t.a[0],t.a[1],t.b[0],t.b[1]])<=s)).map(e=>this._planSnapPhysicalSegment(e)).filter(e=>!!e);return Yl([...i,...n],s).map(e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`).join(" ")}_renderMarkupLayer(e){const t=this._openCuts(),i=this._thickWallCuts(),s=t.concat(i),n=s.length?Ds(this._segments,s,.02*this._gridPitch):this._segments,o=this._path,a=this._gridPitch,r=this._viewOr(this._baseVb()),l="draw"===this._tool?this._drawWallCm:null,c=(()=>"draw"===this._tool&&o.length&&null!=l?this._contourClosed?o:this._cursorPt?[...o,this._cursorPt]:o.length>=2?o:null:null)(),h=c?ku(c.length-1,this._contourClosed?[...this._draftSegmentCms,this._closingWallCm??void 0]:this._draftSegmentCms,l,ul).map(e=>wl(e,this._cellCm,this._gridPitch)/2):[],d=c?Jl(c,wl(l,this._cellCm,this._gridPitch)/2,this._contourClosed,h):"",p=c?this._drawPreviewJoinPatchD(c,h):"",u=this._wallFaceBatch?.candidates[this._wallFaceBatch.index]?.repair||this._wallRepairDiagnostic;return W` + `}_syncPlanSnapActiveMarker(e){const t=this.renderRoot?.querySelector('[data-hp="plan-snap-active-marker"]');if(t){if(t.setAttribute("class",`plan-snap-node${e?" active":""}${"line"===e?.kind?" dynamic":""}`),t.setAttribute("data-active",e?"true":"false"),t.setAttribute("visibility",e?"visible":"hidden"),!e)return t.removeAttribute("data-kind"),void t.removeAttribute("data-key");t.setAttribute("data-kind",e.kind),t.setAttribute("data-key",e.key),t.setAttribute("cx",String(e.point[0])),t.setAttribute("cy",String(e.point[1])),t.setAttribute("r",String(Ml(10,this._cellCm,this._gridPitch)))}}_syncPlanSnapConflictMarkers(e){const t=new Set(e.map(e=>e.key));for(const e of this.renderRoot?.querySelectorAll('.plan-snap-node[data-kind="endpoint"]')||[])e.setAttribute("class","plan-snap-node"+(t.has(e.dataset.key||"")?" conflict":""))}_planSnapPhysicalSegment(e){let t=0;const i=this._spaceModel();if(!i)return null;if("partition"===e.sourceKind)t=Number(i.partitions.find(t=>t.id===e.sourceId)?.cm)||0;else if("draft"===e.sourceKind){const s=/^(.*):(\d+)$/.exec(e.sourceId),n=s?i.room_drafts.find(e=>e.id===s[1]):null;t=n&&s&&Number(n.segments[Number(s[2])]?.cm)||0}else t=$c(i.rooms,this._spaceWalls,this._openCuts(),[e.a[0],e.a[1],e.b[0],e.b[1]],this._wallKeyPitch,this._cellCm,this._gridPitch,zv);return t>0?{a:[...e.a],b:[...e.b],halfDepth:Ml(t,this._cellCm,this._gridPitch)/2}:null}_drawPreviewJoinPatchD(e,t){if(e.length<2)return"";const i=[];for(let s=0;s+10&&i.push({a:e[s],b:e[s+1],halfDepth:t[s]});if(!i.length)return"";const s=2e-4*this._gridPitch,n=this._planSnapGeometrySnapshot().value.segments.filter(t=>e.some(e=>Hs(e,[t.a[0],t.a[1],t.b[0],t.b[1]])<=s)).map(e=>this._planSnapPhysicalSegment(e)).filter(e=>!!e);return ec([...i,...n],s).map(e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`).join(" ")}_renderMarkupLayer(e){const t=this._openCuts(),i=this._thickWallCuts(),s=t.concat(i),n=s.length?Ds(this._segments,s,.02*this._gridPitch):this._segments,o=this._path,a=this._gridPitch,r=this._viewOr(this._baseVb()),l="draw"===this._tool?this._drawWallCm:null,c=(()=>"draw"===this._tool&&o.length&&null!=l?this._contourClosed?o:this._cursorPt?[...o,this._cursorPt]:o.length>=2?o:null:null)(),h=c?Cu(c.length-1,this._contourClosed?[...this._draftSegmentCms,this._closingWallCm??void 0]:this._draftSegmentCms,l,vl).map(e=>Ml(e,this._cellCm,this._gridPitch)/2):[],d=c?tc(c,Ml(l,this._cellCm,this._gridPitch)/2,this._contourClosed,h):"",p=c?this._drawPreviewJoinPatchD(c,h):"",u=this._wallFaceBatch?.candidates[this._wallFaceBatch.index]?.repair||this._wallRepairDiagnostic;return W` ${this._gridLevels()?W``:G} ${n.map(e=>W``)} ${this._renderPhysicalEditorLayer()} - ${"column"===this._tool&&this._cursorPt&&this._drawWallCm?W``M ${ah({shape:"square",center:this._cursorPt,cm:this._drawWallCm},this._cellCm,this._gridPitch).map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`)()}>`:G} + ${"column"===this._tool&&this._cursorPt&&this._drawWallCm?W``M ${dh({shape:"square",center:this._cursorPt,cm:this._drawWallCm},this._cellCm,this._gridPitch).map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`)()}>`:G} ${d?W` `:G} - ${c&&0===l?W`e.join(",")).join(" ")}>`:G} ${p?W` `:G} @@ -6772,7 +6772,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 aria-hidden="true" pointer-events="none">`:G} ${"split"===this._tool&&this._splitSel?.pts?.length?W`${this._splitSel.pts.length>1?W``:G} ${this._splitSel.pts.map((e,t)=>W``)} + cx="${e[0]}" cy="${e[1]}" r="${Dh(.22*a,this._cellCm)}">`)} ${this._cursorPt?W``:G}`:G} `}_renderActiveChainInk(){const e=this._path,t=this._gridPitch;return W` @@ -6780,9 +6780,9 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 ${e.length&&this._cursorPt&&"draw"===this._tool&&!this._contourClosed?W` ${this._activePlanSnapCandidate||this._activePlanSnapConflicts.length?G:W``}`:G} + r="${Dh(.22*t,this._cellCm)}" aria-hidden="true">`}`:G} ${e.map((e,i)=>W``)} + cx="${e[0]}" cy="${e[1]}" r="${Dh(.22*t,this._cellCm)}">`)} `}_renderPartitionDeleteDialog(){const e=this._partitionDeleteDialog,t="mi"===this.hass?.config?.unit_system?.length;return q`

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

    - ${e.openings.map(e=>q`
  • ${this._t("confirm.delete_partition_openings_item",{type:this._t(`opening.${e.type}`),length:li(e.length*Pv/this._gridPitch*this._cellCm,t)})}
  • `)} + ${e.openings.map(e=>q`
  • ${this._t("confirm.delete_partition_openings_item",{type:this._t(`opening.${e.type}`),length:li(e.length*zv/this._gridPitch*this._cellCm,t)})}
  • `)}
@@ -6833,8 +6833,8 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 `:G}
this._physicalDialog={...e,cm:t.target.value}} /> ${this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}
${t&&"square"===e.shape?q` @@ -7077,7 +7077,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 `: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=js(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=mv(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${Bd(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 jp({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(!Wp(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:Op(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=qo(this._planHass,s,n),a=Wo(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?C_(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?qo(this._planHass,o,l):[],u=o?Wo(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),b=p.find(e=>e.key===f),y=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?Ap(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(", "):"",N=(()=>{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 q``}_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=js(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=yv(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${Gd(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 Vp({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(!Yp(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:qp(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=qo(this._planHass,s,n),a=Wo(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?qo(this._planHass,o,l):[],u=o?Wo(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),b=p.find(e=>e.key===f),y=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?Op(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(", "):"",N=(()=>{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 q`
@@ -7323,10 +7323,10 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 -

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

+

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

${"static_icon"===e.display&&this._bindingHasAlarm(e.binding)?q`
${this._t("marker.static_alarm_warning")} @@ -7475,7 +7475,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 ${"file"===e.source?q`
@@ -7495,23 +7495,23 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 ${e.pickSaved?this._renderServerPlans(e):G}`:G}
{const i=t.target.value,s=mv(i),n=null==s?null:(o=s,this._imperial?o*wh:o);var o;this._spaceDialog={...e,cellCmInput:i,cellCmTouched:!0,cellCm:null!=n&&n>0?Math.max(.1,Math.min(uv,n)):e.cellCm}}} /> + min=${Th(.1,this._imperial)} + max=${Th(vv,this._imperial)} + step="0.1" .value=${e.cellCmInput??Th(e.cellCm,this._imperial)} + @input=${t=>{const i=t.target.value,s=yv(i),n=null==s?null:(o=s,this._imperial?o*Mh:o);var o;this._spaceDialog={...e,cellCmInput:i,cellCmTouched:!0,cellCm:null!=n&&n>0?Math.max(.1,Math.min(vv,n)):e.cellCm}}} /> ${this._t(this._imperial?"space.scale_unit_imperial":"space.scale_unit")}
@@ -7526,7 +7526,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26
${this._t("space.zero_wall_help")}
`:G} - `}}pm.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0},_continuityEpoch:{state:!0}},pm.styles=[Ke,o` + `}}fm.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0},_continuityEpoch:{state:!0}},fm.styles=[Ke,o` .hp-static-title { font-weight: 700; padding: 10px 14px 6px; @@ -4892,7 +4892,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 text-align: center; color: var(--secondary-text-color, #9aa4ad); } - `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",pm),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 um=o` + `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",fm),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 vm=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); @@ -5063,7 +5063,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 .editor-secondary { transition: none; } .editor-group-launcher .group-chevron { transition: none; } } -`,_m=(e,t)=>(e.composedPath?.()||[e.target]).some(e=>(e=>!!e&&"function"==typeof e.matches)(e)&&t(e));class mm{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 q`
-
`}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(_m(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=_m(e,e=>e.classList.contains("editor-secondary")),s=_m(e,e=>!!e.dataset.editorGroup),n=_m(e,e=>!!e.dataset.editorPalette),o=_m(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 gm=e=>e.stopPropagation(),fm=4.166666666666667e-9;function vm(e){return Number.isFinite(e)?Math.round(e/Eh)*Eh:e}const bm=(e,t,i,s)=>Math.hypot(i-e,s-t),ym=(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)))),wm=(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(bm(e+c,t+h,n+d,o+p),bm(e-c,t-h,n-d,o-p)),_=Math.max(bm(e+c,t+h,n-d,o-p),bm(e-c,t-h,n+d,o+p));return Math.min(u,_)},km=e=>{const t=Number(e?.cell_cm);return t>0?t:5};function xm(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)<=fm&&h++},u=(e,t)=>{p(e[0],t[0]),p(e[1],t[1])},_={};let m=5;for(const e of i){const t=km(e);null!=e?.id&&(_[String(e.id)]=t),t>m&&(m=t)}const g=(e,t,i,s=!1)=>{if(!(e>fm||s))return;n++,e>a&&(a=e);const o=e*Fh*t;o>r&&(r=o,l=i)};for(const e of i){const t=km(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=[vm(t[0]),vm(t[1])];return e=Math.max(e,bm(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=vm(t+n),r=vm(i+o),l=vm(t),c=vm(i),h=Math.max(Eh,a-l),d=Math.max(Eh,r-c);e=ym(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=[vm(t[0]),vm(t[1])];return e=Math.max(e,bm(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=[vm(n[0]),vm(n[1])],l=[vm(a[0]),vm(a[1])];let c=Math.max(bm(n[0],n[1],r[0],r[1]),bm(a[0],a[1],l[0],l[1]));const h=bm(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>=-fm&&s+t/2<=h+fm});h>fm&&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=[vm(e[0]),vm(e[1])],a=bm(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=[vm(t[0]),vm(t[1])],o=[vm(i[0]),vm(i[1])];e=Math.max(bm(t[0],t[1],n[0],n[1]),bm(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=vm(t),r=vm(i);if(null!=s.w&&null!=s.h){const l=vm(t+n),c=vm(i+o),h=Math.max(Eh,l-a),d=Math.max(Eh,c-r);e=ym(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=bm(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)<=fm)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),_=wm(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:Eh,length:Number(s.length)||0});if(!n)continue;const a=Number(s.angle),r=!(Number.isFinite(a)&&a===n.angle),l=wm(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=vm(i.x),a=vm(i.y),r=bm(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 $m(e,t){const i=t.base+e*t.span;return Math.min(1,Math.max(0,i))}const Sm=e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]),Mm=(e,t)=>Math.hypot(e[0]-t[0],e[1]-t[1]);function Cm(e,t,i){const s=i[0]-t[0],n=i[1]-t[1],o=s*s+n*n;if(o<=0)return Mm(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 Dm(e,t,i,s,n){for(const s of t)if(s&&!i.has(s.id)&&Sm(s.a)&&Sm(s.b)&&(Mm(e,s.a)<=n||Mm(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(Mm(i,n)<=s)return[(i[0]+n[0])/2,(i[1]+n[1])/2];return null}function Tm(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&&Sm(e.a)&&Sm(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;eMm(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 Im(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:$m(e.t,a)},o++;const r=Jp(i,t,s.coordScale,s.cellCm,s.gridPitch).resolved;r&&Object.assign(i,su(i,r,s.coordScale))}return o}function Rm(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 Am=e=>!!e&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]),zm=(e,t)=>e[0]-t[0]||e[1]-t[1],Fm=(e,t,i,s)=>[e[0]+t*s,e[1]+i*s],Nm=(e,t,i,s)=>(e[0]-t[0])*i+(e[1]-t[1])*s,Em=(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=Nm(i,e,l,c),p=Nm(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},Om=(e,t,i)=>Math.min(e.hi,t.hi)-Math.max(e.lo,t.lo)>i,Hm=e=>{const t=new Set(["id","a","b","cm"]);return Object.keys(e||{}).every(e=>t.has(e))},Lm=(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(!Am(n))return!0;const o=Nm(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=Jp(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},jm=(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]}),Wm=(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},Um=(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 Gm(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 b=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(!Hm(e))continue;const i=b.get(e.id);if(!(i&&Am(i.a)&&Am(i.b)&&Number.isFinite(i.cm)&&i.cm>0))continue;const[o,a]=zm(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,y=gc(t.rooms,d,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale),w=y.flatMap(e=>{if(e.open||"outer"!==e.kind&&"shared"!==e.kind)return[];const t=Em(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:Jp(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=Nm(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=>qm(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=Fm(o,_,g,s),l=Fm(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(),b=1===u.size&&1===m.size&&("outer"===f&&1===v.length||"shared"===f&&2===v.length),y={id:i.id,a:r,b:l,cm:i.cm},k=C.some(e=>!!Em(r,l,e.a,e.b,h)),x=D.some(e=>e.points.some((t,i)=>i+10?p[0].cm:ul:0,T=b?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&&eOm(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&&iu(t,y,h)){T.set(e.id,{resolved:t,signature:[...r][0]});continue}const l=Kp(i,n.cellCm,n.gridPitch),c={lo:s.lo-l,hi:s.hi+l};for(const e of P)Om(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=>Om(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=Kp(i,n.cellCm,n.gridPitch),r={lo:s.lo-a,hi:s.hi+a};for(const e of P)Om(e,r,h)&&(e.safe=!1,e.signature="");I=!0}}const R=Um(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]=zm(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 N=new Set(p.map(e=>e.id));N.delete(i.id);const E=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:Wm(i.id,s,o,N);return N.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,...E);const L=H.map(e=>qm(e,n.coordScale)),B=new Map;let j=!0;for(const{opening:e,resolved:t}of k){const i=T.get(e.id);if(i){const t=su(e,i.resolved,n.coordScale),{host:s,...o}=t;B.set(e.id,o);continue}const s=S.get(e.id),o=E[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){j=!1;break}const r=a.b[0]-a.a[0],l=a.b[1]-a.a[1],c=Math.hypot(r,l),d=Nm(t.center,a.a,r/c,l/c),p={...su(e,t,n.coordScale),host:{kind:"partition",id:o.id,t:d/c}};if(!Xp(p,L,n.coordScale,n.cellCm,n.gridPitch).resolved){j=!1;break}B.set(e.id,p)}if(!j)continue;const q=u.map(e=>B.get(e.id)||e),W=[...T.keys()].map(e=>B.get(e)),U=W.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 q){if(V.has(e.id))continue;const t=Bm(e,L,n);t&&U.some(e=>jm(e,t,h))&&(G=!0)}if(G)continue;const K=qc(t.rooms,F,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale);[...T.entries()].every(([e,t])=>{const i=B.get(e),s=Gc(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=q,f+=A.length,v+=T.size)}return{walls:d,partitions:p,openings:u,roomDrafts:g,partitionsReconciled:f,openingsRehosted:v,removedDrafts:m.size}}const Vm=e=>JSON.parse(JSON.stringify(e)),Km=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),Ym=(e,t)=>t.length>0&&t.length<=35&&("space"===e?/^[a-z0-9_-]+$/.test(t):/^[A-Za-z0-9_-]+$/.test(t));function Jm(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 Xm=(e,t,i)=>{const s=e.get(t)||[];s.push(i),e.set(t,s)};const Zm=1e-9;class Qm extends Error{constructor(e,t=""){super(t?`${e}: ${t}`:e),this.reason=e,this.name="WallSegmentModelError"}}const eg=e=>JSON.parse(JSON.stringify(e)),tg=e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(Number(e[0]))&&Number.isFinite(Number(e[1])),ig=e=>[Number(e[0]),Number(e[1])],sg=e=>`${Number(e[0]).toFixed(12)},${Number(e[1]).toFixed(12)}`,ng=(e,t)=>{const i=sg(e),s=sg(t);return isg(e)<=sg(t)?[ig(e),ig(t)]:[ig(t),ig(e)],ag=(e,t)=>Math.hypot(t[0]-e[0],t[1]-e[1]),rg=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/Eh;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},cg=(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},hg=(e,t,i)=>{const s=Math.max(0,Math.min(1,cg(e,t,i)));return Math.hypot(e[0]-(t[0]+(i[0]-t[0])*s),e[1]-(t[1]+(i[1]-t[1])*s))},dg=(e,t,i,s,n=Zm)=>{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=cg(i,e,t),h=cg(s,e,t);return Math.max(0,Math.min(1,Math.max(c,h))-Math.max(0,Math.min(c,h)))*r},pg=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},_g=(e,t,i,s,n="")=>{const[o,a]=og(t,i),r=`${e}|${sg(o)}|${sg(a)}|${[...s].sort().join(",")}${n}`;return`wall-${ug(pg(r)).slice(0,20)}`};let mg=0;const gg=e=>{for(let t=0;t<1e3;t++){const t=globalThis.crypto?.randomUUID?.(),i=`wall-${t||ug(pg(`${Date.now()}|${mg++}|${Math.random()}`)).slice(0,26)}`;if(!e.has(i))return i}throw new Qm("duplicate-id","id factory exhausted")},fg=e=>{const t=new Map;for(const i of Array.isArray(e?.wall_segments)?e.wall_segments:[])if(i&&"string"==typeof i.id&&tg(i.a)&&tg(i.b)){if(t.has(i.id))throw new Qm("duplicate-id",i.id);t.set(i.id,i)}return t},vg=(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)<=Zm&&Math.abs(t[1]-s[1]-o)<=Zm?[n,o]:null};return s(i.a,i.b)||s(i.b,i.a)},bg=(e,t)=>{const i=Array.isArray(e.rooms)?e.rooms:[],s=Ad(e.open_spans).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]]),n=s.length?s:zd(i,null,1,.04*Eh,!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=>hg([Number(e.x),Number(e.y)],[t[0],t[1]],[t[2],t[3]])<=.04*Eh)):[],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(!Nl(e,t,Number(i.angle)))return!1;if(hg(s,e,t)>.02*Eh)return!1;const n=ag(e,t),o=cg(s,e,t)*n,a=Number(i.length)/2;return o+a>=-Zm&&o-a<=n+Zm}),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 Qm("invalid-room",r);const u=ac(i,r,a,Eh,1,[...e.walls||[],...l]);if(!u||u.poly.length<3)throw new Qm("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;eZm||Math.abs(s[1]-g[1])>Zm)){f=!1;break}g=s}const v=[];for(let e=0;e2)throw new Qm("third-owner",a);const d=m?_[u.parent[e]]:void 0;if("string"==typeof d&&d){const n=t.get(d);f||!n||dg(i,s,n.a,n.b)>Zm?l.preferredIds.add(d):l.positionalIds.add(d),l.preferredCarriers.set(d,{a:ig(p[u.parent[e]]),b:ig(p[(u.parent[e]+1)%p.length])})}const g=u.parent[e];l.parentKeys.add(Ml(p[g],p[(g+1)%p.length],Eh));const b=[(i[0]+s[0])/2,(i[1]+s[1])/2],y=e=>e.some(e=>hg(b,[e[0],e[1]],[e[2],e[3]])<=.04*Eh);l.zeroWall||=y(o)||y(n)&&!c(i,s),v.push(a)}const b={...s};delete b.wall_ids,d.push({...b,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}},yg=(e,t,i,s,n)=>{const o=new Map([...i.values()].map(e=>[ng(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 Qm("duplicate-id",t);r.set(e,s);continue}if(e.preferredIds.size>1)throw new Qm("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=>dg(e.a,e.b,t.a,t.b)>Zm),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)||ag(t.a,t.b)-ag(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 Qm("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=hg(n,e.a,e.b)<=Zm?0:1,o=hg(n,t.a,t.b)<=Zm?0:1;if(i!==o)return i-o;return(hg(s.a,e.a,e.b)<=Zm?0:1)-(hg(s.a,t.a,t.b)<=Zm?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 Qm("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]=og(t.a,t.b),n=`${String(e.id||"")}|${sg(i)}|${sg(s)}|${[...t.owners].sort().join(",")}`,o=ug(pg(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 Qm("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=gg(c),c.add(e.id)},wg=(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=cg(i,t.a,t.b),n=ag(t.a,t.b),o=Number(e.length)/2;return s>=-Zm&&s<=1+Zm&&hg(i,t.a,t.b)<=.02*Eh&&Nl(t.a,t.b,Number(e.angle))&&Number.isFinite(o)&&o>=0&&s*n-o>=-Zm&&s*n+o<=n+Zm},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,cg(i,a.a,a.b)))}},kg=(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=cg(i,t.a,t.b),o=ag(t.a,t.b);return n>=-Zm&&n<=1+Zm&&hg(i,t.a,t.b)<=.02*Eh&&Nl(t.a,t.b,Number(e.angle))&&Number.isFinite(s)&&s>=0&&n*o-s>=-Zm&&n*o+s<=o+Zm}),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)=>hg(i,e.a,e.b)-hg(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,cg(i,o.a,o.b)))}:null},xg=(e,t,i)=>{const s=fg(e),{atoms:n,rooms:o}=bg(e,s);yg(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,Eh),o=s.filter(e=>Number(e.cm)>0&&(!(e.key!==n&&!t.parentKeys.has(e.key))||!(!tg(e.a)||!tg(e.b))&&dg(t.a,t.b,e.a,e.b)>=ag(t.a,t.b)-Zm)).map(e=>Math.max(1,Math.min(100,Number(e.cm))));if(new Set(o.map(e=>e.toFixed(9))).size>1)throw new Qm("thickness-conflict",t.key);if(o.length)return o[0];const a=zl(s,t.a,t.b,Eh,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,Eh),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=wg(e,t);i&&(e.host=i);continue}const s=wg(e,t)??(i?kg(e,t):null);if(s)e.host=s;else{if(!i)throw new Qm("opening-host",e.id);delete e.host}}})(e,a,t),a.reduce((e,t)=>e+(s.has(t.id)?0:1),0)};function $g(e,t={}){const i=JSON.stringify(e),s=eg(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+=xg(e,o,i)}return s.model_version=9,pl(s),{config:s,changed:i!==JSON.stringify(s),migratedSegments:n}}const Sg=e=>!!e&&"object"==typeof e&&!Array.isArray(e);function Mg(e,t){const i=(e,t)=>{if(Array.isArray(e)&&Array.isArray(t)){const s=new Map;for(const t of e)Sg(t)&&"string"==typeof t.id&&t.id&&s.set(t.id,t);const n=t.map((t,n)=>{if(Sg(t)&&"string"==typeof t.id&&t.id){const e=s.get(t.id);return e?i(e,t):eg(t)}return n[t,t in e?i(e[t],s):eg(s)]);for(const t of Object.keys(e))delete e[t];for(const[t,i]of s)e[t]=i;return e}return eg(t)};return i(e,t)}const Cg=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),Dg=(e,t,i)=>Math.min(i,Math.max(t,e)),Pg=e=>Rh({spaces:[e]})[0],Tg=(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 b=Tg(g,f,r),y=p.get(b);y?y.targets.add(a):p.set(b,{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)=>Tg(e.a,e.b,r).localeCompare(Tg(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 Rg=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=Jm("space",e);t.layers>0&&Ym("space",t.root)&&Xm(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=Jm("room",e);n.layers>0&&Ym("room",n.root)&&Xm(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 b={spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,positionsUnresolved:0,nestedRefsUnresolved:0,deadSpaceIds:[],orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0,removedPositions:[],liveMissingPositions:[],unverifiedPositions:[]},y=new Set,w=e=>h.has(e)?null:(()=>{const t=Jm("space",e).root;if(!Ym("space",t))return null;const i=d.get(t)||[];return 1===i.length?i[0]:null})(),k=(e,t)=>{const i=Jm("room",e).root;if(!Ym("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,b.roomRefsRemapped++):i?(e.room_id=i.roomId,b.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,b.roomRefsRemapped++):b.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,b.spaceRefsRemapped++):l||"virtual"===e.binding?l||(delete e.space,c=null,b.markersDetached++):(d=x(t),d?(e.space=d.spaceId,c=d.spaceId,b.spaceRefsRemapped++):(delete e.space,c=null,b.markersDetached++))}l&&!p||$(e,c,d),r&&p&&a===i&&c&&(n[t]={...o,s:c},b.positionsRemapped++,y.add(t))}for(const e of Object.keys(n)){if(y.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}`;Km(n,i)||(n[i]={...t,s:s}),delete n[e],b.roomRefsRemapped++,b.positionsRemapped++;continue}}n[e]={...t,s:s},b.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?b.orphanRoomLabelsRemoved++:"group"===e?b.orphanGroupPositionsRemoved++:b.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(b.liveMissingPositions.push(o),!0===i.removeLiveMissingPositions){delete n[e],b.liveMissingPositionsRemoved++,R(o.kind),b.removedPositions.push(o);continue}}else b.unverifiedPositions.push(o);b.positionsUnresolved++,A.add(s)}else delete n[e],R(o.kind),b.removedPositions.push(o)}return b.deadSpaceIds=[...A].sort((e,t)=>e.localeCompare(t)),{config:s,layout:n,report:b,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&&Cg(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(Cg(n,"segments")&&(delete n.segments,t++),Cg(n,"plan_scale")){const e=Number(n.plan_scale);Number.isFinite(e)&&e>=Dh&&e<=Ph&&(Cg(n,"plan_scale_x")||(n.plan_scale_x=e),Cg(n,"plan_scale_y")||(n.plan_scale_y=e),delete n.plan_scale,t++)}const o=Number(n.cell_cm);Cg(n,"cell_cm")&&(!Number.isFinite(o)||o<.1||o>1e3)&&(n.cell_cm=Number.isFinite(o)&&o>0?Dg(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)Cg(e,"angle")&&(delete e.angle,t++);else if("square"===e.shape&&Cg(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(Cg(e,"width")){const i=Number(e.width);(Cg(e,"width_cm")||Number.isFinite(i))&&(Cg(e,"width_cm")||(e.width_cm=Number(Dg(i/Nh*r,.1,100).toFixed(6))),delete e.width,t++)}if("rect"!==e?.kind&&"ellipse"!==e?.kind||!0!==e.fill||(Cg(e,"fill_color")||(e.fill_color=e.color||"#607d8b",t++),Cg(e,"fill_opacity")||(e.fill_opacity=.25,t++)),"text"!==e?.kind)continue;void 0===e.size_cm?(e.size_cm=Number(Dg(ts*is(e)/Nh*r,.1,2e3).toFixed(6)),delete e.scale,delete e.size,t++):(Cg(e,"scale")||Cg(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(!(Cg(e,"entity")||Cg(e,"attr")||Cg(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=xm(a.spaces||[],o.layout);let g=0,f=0,v=0,b="";const y=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=>ya(e,i)),n=e.length>=4&&ma(e[0],e[e.length-1]),o=n?e.slice(0,-1):e;if(n?va(o):ba(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=ya(e.a,i),n=ya(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/Eh*i;return s>v&&(v=s,b=String(e?.id||"")),t.space}),w=g?xm(y,m.layout):{...m,spaces:y};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=Pg(t),r=Pg(i);if(!o||!r)continue;const l=Rg(o.rooms),c=Rg(r.rooms),h=.02*Nh,d=(t.wall_segments||[]).filter(e=>0===Number(e.cm)).map(e=>({...e,key:Ml(e.a,e.b,Eh),cm:0})),p=new Set(d.map(e=>e.key)),_=B_(t,o.rooms,Mh,h);$+=_.length;for(const e of _){const t=[e[0]/Mh,e[1]/Mh],i=[e[2]/Mh,e[3]/Mh],s=Ml(t,i,Eh);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,Eh,Mh),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*Mh),i=e.b.map(e=>e*Mh);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 b=(i.wall_segments||[]).filter(e=>Number(e.cm)>0);i.wall_segments=[...b,...v.map(({key:e,...t})=>t)];const y=v.map(e=>[e.a[0]*Mh,e.a[1]*Mh,e.b[0]*Mh,e.b[1]*Mh]),w=Array.isArray(t.walls)?t.walls.length:0;let k=Ol(t.walls,l,c,Eh,Mh);k=Ig(r.rooms,k,y,Eh,Number(i.cell_cm)>0?Number(i.cell_cm):5,Nh,Mh),k=vc(r.rooms,k,y,Eh,Number(i.cell_cm)>0?Number(i.cell_cm):5,Nh,Mh),k=Fl(k,i.rooms||[],Eh,1,y.map(e=>[e[0]/Mh,e[1]/Mh,e[2]/Mh,e[3]/Mh])),x+=Math.max(0,w-k.length),k.length?i.walls=k:delete i.walls;const I=Tm(i.partitions||[],{pitch:Eh,geometry:Rm(i)});I.merged&&(M+=I.merged,i.partitions=I.partitions,Im(i.openings,i.partitions,I.openingMoves,{coordScale:Mh,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,gridPitch:Nh}));const R=Pg(i);if(R){const e=n(i,R,i.walls||[],y,{pitch:Eh,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,gridPitch:Nh,coordScale:Mh});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=$g(e);return Mg(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,N=Number.isInteger(Number(R.model_version))?Number(R.model_version):h,E=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:{...E,modelFrom:h,modelTo:N,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?b:"",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 zg(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 Fg(e){return e instanceof Error?e.name||"Error":typeof e}function Ng(e){return Bd({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 Eg(e,t,i,s=Nh,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=Jp(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 Og(e,t=()=>!0){return e.flatMap(e=>e.host&&e.partitionHost&&t(e)?[Qp(e.partitionHost)]:[])}function Hg(e,t,i,s,n,o,a=Nh,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&&iu(e.partitionHost,l,2e-4*a)?[t]:[]:[t]})}function Lg(e,t){const i=Eh,s=Nh,n=Mh,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=Nh,s=1e3){return zd(t.rooms,e?.open_spans,s,.02*i)}(e,t,s,n),l=function(e,t,i=Nh){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=Eg(e,t,o,s,n),h=Og(c),d=ch(t,o,s,2e-4*s,h).all;return{space:t,walls:a,openCuts:l,openings:c,roomOpenings:Hg(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 jg(e,t={}){const i=(t.fingerprint||Bd)(e),s=Array.isArray(e?.spaces)?e.spaces:[],n=t.fallbackSpaceName||(e=>`Space ${e}`),o=t.prepareSpace||Lg,a=t.wallPass||Oc,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:Fg(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:Fg(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 qg{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 Wg=new WeakMap;const Ug=(e,t,i)=>255===e[i+3]&&t.every((t,s)=>Math.abs(e[i+s]-t)<=2);async function Gg(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 Ug(c,i,0)&&Ug(c,function(e,t){return[0,1,2].map(i=>Math.round(255-(255-e[i])*(255-t[i])/255))}(i,s),4)&&Ug(c,s,8)&&Ug(c,n,12)}catch{return!1}finally{t.URL.revokeObjectURL(a)}}const Vg={now:()=>performance.now(),requestFrame:e=>requestAnimationFrame(e),cancelFrame:e=>cancelAnimationFrame(e)},Kg=e=>Math.max(0,Math.min(1,e)),Yg=(e,t,i)=>e+(t-e)*i,Jg=(e,t,i)=>{const s=1-e;return 3*s*s*e*t+3*s*e*e*i+e*e*e};function Xg(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 Zg(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 Qg(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 ef(e,t,i){const s=Qg(e),n=Qg(t);if(!s||!n)return i<.5?e:t;const o=Kg(i);return`rgba(${Math.round(Yg(s[0],n[0],o))}, ${Math.round(Yg(s[1],n[1],o))}, ${Math.round(Yg(s[2],n[2],o))}, ${Yg(s[3],n[3],o).toFixed(3)})`}function tf(e,t,i){const s=(e=>{const t=Kg(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,Jg(n,.2,.2){const t=this._state;if(!t||t.token!==n)return;const i=Kg((e-t.startedAt)/t.duration);t.presented=i>=1?t.to:tf(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 nf="houseplan_card_labs_v1",of=Object.freeze([Object.freeze({id:"iso",issue:89,since:"1.62.0",expires:"1.65.0",summary:"Volumetric plan renderer"})]);function af(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 rf(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 uf(e,t,i,s=of){const n=function(e,t=of){const i=af(e),s=new Map;if(!i||!cf(t))return s;for(const e of t){const t=af(e.since),n=af(e.expires);rf(i,t)>=0&&rf(i,n)<0&&s.set(e.id,e)}return s}(i,s),o=new Set(cf(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 pf(new URLSearchParams(String(e.search||"").replace(/^\?/,""))))l(t);for(const t of pf(hf(e.hash)))l(t);const c=Object.freeze([...a].sort());return{active:c,space:df(e.hash),persist:r?JSON.stringify(c):void 0,knownUrlOperation:r}}let _f={active:Object.freeze([]),space:""},mf="",gf=!1,ff="";const vf=new Set;function bf(){if("undefined"==typeof window)return _f;const e=uf(window.location,function(){try{return window.localStorage.getItem(nf)}catch{return null}}(),mf);void 0!==e.persist&&function(e){try{window.localStorage.setItem(nf,e)}catch{}}(e.persist),_f={active:e.active,space:e.space},window.__hpLabs=e.active;for(const e of vf)e(_f);return _f}function yf(){bf()}function wf(e){return mf=e,"undefined"==typeof window?_f:(gf||(gf=!0,window.addEventListener("hashchange",yf),window.addEventListener("popstate",yf)),bf())}const kf=Object.freeze({rotDeg:0,tiltDeg:20,xyScale:1,zScale:1,origin:Object.freeze([500,500])});function xf(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 $f(e,t,i=kf){if(!(xf(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 Sf(e,t=kf){if(!xf(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 Mf(e=kf){return`matrix(${function(e=kf){if(!xf(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 Cf(e,t=kf){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=>[$f(e,-o,t),$f(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 Df=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function Pf(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 Pf(i)>0===s?i:[...i].reverse()}function If(e,t,i){return e.length?`M ${e.map(e=>Df($f(e,i,t))).join(" L ")} Z`:""}function Rf(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 Af(e,t,i=kf){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=Tf(e,t);if(!i.length)return i;let s=0;for(let e=1;e0)).filter(e=>e.length>=3&&Math.abs(Pf(e))>=1e-9)).filter(e=>e.length&&Pf(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(Pf(t[0]))-Math.abs(Pf(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 zf(e,t){return e^=Math.round(64*(Number.isFinite(t)?t:0)),Math.imul(e,16777619)>>>0}const Ff=e=>e.every(Number.isFinite);function Nf(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 Ef(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],Nf(i,e.angle,r,l));return{leaf:t,hinge:h,closedVector:Nf(s,e.angle,r,l),quarterVector:Nf([-s[1],s[0]],e.angle,r,l),turnDeg:n,bottom:o,top:a}}const Of=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function Hf(e,t,i=kf){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=$f(t.hinge,t.bottom,i),a=$f(n,t.bottom,i),r=$f(n,t.top,i),l=$f(t.hinge,t.top,i);return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaf:t.leaf,d:`M ${Of(o)} L ${Of(a)} L ${Of(r)} L ${Of(l)} Z`,shadowD:`M ${Of($f(t.hinge,0,i))} L ${Of($f(n,0,i))}`,depth:Math.max(o[1],a[1])}})}const Lf={window:120,door:90,passage:90,gate:300};function Bf(e){return Lf[e]}function jf(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 qf(e){const t=new Map;return e.forEach((e,i)=>{if(!e.kind||e.open)return;const s=jf(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 Wf(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 Uf(e){const t=qf(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=jf(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(Wf),s=t.filter(t=>!t.target.partitionHost||e.renderedLength+2*t.target.physicalHalfWidth<=t.length+1e-9).sort(Wf);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,b=r.a[1]+p*m,y=Math.max(0,e.renderedLength)/2,w=m-y,k=m+y,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:b,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:b,angle:C}:null}},jambBlockedTarget:null}}function Gf(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 Vf=(e,t)=>e[0]*t[1]-e[1]*t[0],Kf=(e,t)=>e[0]*t[0]+e[1]*t[1],Yf=(e,t)=>[e[0]-t[0],e[1]-t[1]],Jf=(e,t,i)=>[e[0]+t[0]*i,e[1]+t[1]*i];function Xf(e,t,i){return Math.abs(Vf(e,t))<=i}function Zf(e,t,i){return Math.abs(Vf(Yf(e,t),i))}function Qf(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&&Xf(l,[s/a,o/a],1e-6)))return[];if(Zf(t.a,e,l)>1.1*Math.max(i,n)||Zf(t.b,e,l)>1.1*Math.max(i,n))return[];return[[Kf(Yf(t.a,e),l),Kf(Yf(t.b,e),l)]]}),n).flatMap(([t,s])=>{const n=ql({a:Jf(e,l,t),b:Jf(e,l,s),halfDepth:i});return n?[n]:[]})}function ev(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:Jf([e.x,e.y],i,-s),b:Jf([e.x,e.y],i,s)}}),o=[];for(const s of Bc(e.rooms,e.walls,e.openCuts,e.pitch,e.cellCm,e.gridPitch,t))for(const e of Qf(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 Qf(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 tv(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=Jf(i.center,o,n),r=[];for(let t=0;ts&&Xf(i.axis,[l[0]/c,l[1]/c],1e-6)))continue;if(Zf(n,a,i.axis)>s||Zf(o,a,i.axis)>s)continue;const h=Kf(Yf(n,a),i.axis),d=Kf(Yf(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=Jf(a,i.axis,-i.half),p=Jf(a,i.axis,i.half),u=Math.max(0,-i.half-c),_=Math.max(0,h-i.half),m=u>0?Jf(a,i.axis,c):d,g=_>0?Jf(a,i.axis,h):p,f=Kf(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 iv(e){const t=Jf(e.center,e.axis,-e.half),i=Jf(e.center,e.axis,e.half),s=Jf(e.center,e.axis,Math.min(-e.half,e.targetLo)),n=Jf(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 sv(e,t,i,s){const n=Yf(i,t),o=Kf(n,n);if(!(o>s*s))return Math.hypot(...Yf(e,t))<=s;const a=Kf(Yf(e,t),n)/o;if(a<-s||a>1+s)return!1;const r=Jf(t,n,Math.max(0,Math.min(1,a)));return Math.hypot(...Yf(e,r))<=s}function nv(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 ov(e,t,i,s,n,o){const a=Yf(s,i),r=Vf(t,a);if(Math.abs(r)<=o)return null;const l=Yf(i,e),c=Vf(l,a)/r,h=Vf(l,t)/r;return c<-o||c>n+o||h<-o||h>1+o?null:Math.max(0,Math.min(n,c))}function av(e,t,i){const s=iv(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(nv(e,r.body,o))return 0;for(let s=0;sKf(Yf(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=Yf(a,o),l=Math.hypot(r[0],r[1]);if(!(l>i&&Xf(t.axis,[r[0]/l,r[1]/l],1e-6)))continue;if(Zf(o,t.center,t.axis)>i||Zf(a,t.center,t.axis)>i)continue;const c=Kf(Yf(o,t.center),t.axis),h=Kf(Yf(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 iv(i);if(s.length){const e=s.map(({room:e,edge:s})=>tv(e,s,i,t.epsilon));if(e.some(e=>!e))return iv(i);const n=e;return[...n.map(e=>e.left),...n.map(e=>e.right)]}return av(e,t,i)}function lv(e){return!(!e.canEdit||e.kiosk||e.fixedFloor)&&("view"!==e.mode&&("mouse"===e.pointerType&&e.spaceCount>1))}const cv="1.68.1",hv={badge:"display.badge",icon_ripple:"display.icon_ripple",value:"display.value",static_icon:"display.static_icon"},dv={badge:"marker.display_hint_badge",icon_ripple:"marker.display_hint_icon_ripple",value:"marker.display_hint_value",static_icon:"marker.display_hint_static_icon"},pv=2e3,uv=1e3,_v=1500,mv=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},gv=(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}},fv=(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)}},vv=new Map;let bv=0;const yv=e=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(e??{})}`;let wv=1e4;const kv="houseplan_card_layout_v1",xv="houseplan_card_cfg_v1",$v="houseplan_card_zoom_v1",Sv="houseplan_card_nav_v1",Mv="houseplan_card_kiosk_v1",Cv="houseplan_card_view_v1",Dv="hp-dialog, hp-help, hp-color-opacity, hp-device-preview",Pv=1e3,Tv=[[0,1],[45,.88],[70,.62],[86,.32],[100,0]],Iv=(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}},Rv=new Set(["select","draw","column","merge","split","resize","opening","wallthick","delroom"]),Av=2e3,zv=(e,t,i)=>{const s=new Event(t,{bubbles:!0,composed:!0});s.detail=i??{},e.dispatchEvent(s)},Fv=e=>{history.pushState(null,"",e),zv(window,"location-changed",{replace:!1})},Nv=(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},Ev=e=>{try{e.target?.setPointerCapture?.(e.pointerId)}catch{}};class Ov 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 cm(this,()=>this._syncPointerHoverTargets()),this._devicePressAnimations=new Map,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...Nr},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 sf({frame:e=>this._applyModeTransitionFrame(e),settled:e=>this._settleModeTransition(e)}),this._editorSecondary=new mm({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 qg(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._rszLimitViolation=null,this._path=[],this._cursorPt=null,this._planSnapHover=null,this._mergeSel=null,this._openingPreset=null,this._openingRebindId=null,this._openingPresetRevision=0,this._openingHoverCandidate=null,this._openingJambBlockCm=null,this._openingDialog=null,this._openingInfo=null,this._opDrag=null,this._opMeasure=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._wallFaceBatch=null,this._wallRepairDiagnostic=null,this._wallFaceGraphCache=[],this._areaSel="",this._nameSel="",this._roomDialog=!1,this._roomEditId=null,this._roomFill="",this._roomCustomFill=null,this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1,this._zoom=1,this._view=null,this._zoomBySpace={},this._viewModeSnap=null,this._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._deviceInbox=null,this._deviceInboxReturn=null,this._deviceInboxMemo=null,this._markerDialog=null,this._spaceDialog=null,this._keyHandler=e=>this._onKey(e),this._warmVp=null,this._warmVpArmed=!1,this._warmLongReturn=!1,this._warmRevivePending=!1,this._warmGen=++bv,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+nm)>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 Wd(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=Nv(()=>{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(kv,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=Nv(()=>{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=mv(e.cm);if(null==s)return void this._showPhysicalRange("column"===e.kind?Xc:100,"column"===e.kind?1:0);const n=this._imperial?2.54*s:s,o="column"===e.kind?Xc: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=mv(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&&yh(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&&q_(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=Qc(n),i.shape="circle"===e.shape?"circle":"square","square"===i.shape?i.angle=mv(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=Yu(gc(s.rooms,this._spaceWalls,o,this._wallKeyPitch,this._cellCm,this._gridPitch,Pv).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>Av)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,Pv);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])*Pv,Number(t.a?.[1])*Pv],s=[Number(t.b?.[0])*Pv,Number(t.b?.[1])*Pv];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]/Pv,o.interval.a[1]/Pv],b:[o.interval.b[0]/Pv,o.interval.b[1]/Pv],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]*Pv,s.a[1]*Pv],b:[s.b[0]*Pv,s.b[1]*Pv]};t.host={kind:"partition",id:s.id,t:Ju([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>Av)return void this._showToast(this._t("toast.physical_limit"));const i=this._geometrySnapshot(),s=ku(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=yr(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:Xg(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=Ei(this._baseVb(),s/Math.max(1,n)),a=Math.min(Ov.ZOOM_MAX,Math.max(Ov.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:Xg(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 lv({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:"mouse",spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})}_tabPointerDown(e,t){lv({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:e.pointerType,spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})&&(Ev(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 Ld(()=>{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(Cv,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?Sf(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?$f([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 df(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,Wg.get(t)?.resolved);var t;void 0!==e&&(this._glowScreenBlend=e),this._continuityUnsub?.(),this._continuityUnsub=Od(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=Wg.get(e);if(t)return t.promise;const i={promise:Promise.resolve(!1)};return i.promise=Gg(e).catch(()=>!1).then(e=>(i.resolved=e,e)),Wg.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){vf.add(t);const i=wf(e);return"undefined"==typeof window&&t(i),()=>{vf.delete(t)}}(cv,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},_v)),!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._closeMarkerDialog();if(this._deviceInbox)return void(this._deviceInbox=null);if(this._openingDialog)return void(this._openingDialog=null);if(this._physicalDialog)return void(this._physicalDialog=null);if(this._backdropDialog)return void(this._backdropDialog=null);if(this._decorShapeDialog)return void(this._decorShapeDialog=null);if(this._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0);if(this._editorSecondary.hasOpenGroup)return e.preventDefault(),void this._editorSecondary.closeGroup(!0)}const t=e.composedPath?.()||[e.target],i=t.some(e=>e?.matches?.('input, textarea, select, [contenteditable="true"]')),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]/Pv,e[1]/Pv]),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 vv.values())for(const t of e)clearTimeout(t.evict);vv.clear(),wv=e&&e>0?e:1e4}static _warmBootStats(){let e=0,t=0;const i=[];for(const s of vv.values())for(const n of s)e++,n.dlg&&(t++,i.push(n.dlg.kind));return{keys:vv.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($v)||"{}")||{}}catch{this._zoomBySpace={}}try{const e=JSON.parse(localStorage.getItem(Cv)||"{}")||{};this._viewPreference=Object.fromEntries(Object.entries(e).filter(e=>"flat"===e[1]||"iso"===e[1]))}catch{this._viewPreference={}}this._labs=wf(cv);try{const e=JSON.parse(localStorage.getItem(Mv)||"null");this._kioskScale={icon:Ns(e?.icon),font:Ns(e?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(xv)||"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||Bd(e.config),this._layout=e.layout||{},this._layoutRev=e.layout_rev||0,this._layoutContentFingerprint=e.layout_fingerprint||Bd(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=yv(this._config);if(this._warmKey===e&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const t=this.parentNode,i=this._warmIdx(t),s=vv.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>=Fd,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>=Fd,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},_v)),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=vv.get(t);if(!e.live&&o&&o.length>1){const t=o.indexOf(e);t>=0&&o.splice(t,1)}},wv+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?$f([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&&Rv.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=yv(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=vv.get(i)||[];for(s.push(this._warmSlot),vv.set(i,s),this._warmTrim(s);vv.size>8;){const e=vv.keys().next().value;if(void 0===e||e===i)break;for(const t of vv.get(e)||[])clearTimeout(t.evict);vv.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>wv)&&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=Bd(this._serverCfg),this._layoutContentFingerprint=Bd(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(bn).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(xv,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=Rh(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 jd([this._cfgRev,this._cfgContentFingerprint||Bd(this._serverCfg),this._layoutRev,this._layoutContentFingerprint||Bd(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 q`
`}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(bm(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=bm(e,e=>e.classList.contains("editor-secondary")),s=bm(e,e=>!!e.dataset.editorGroup),n=bm(e,e=>!!e.dataset.editorPalette),o=bm(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 wm=e=>e.stopPropagation(),km=4.166666666666667e-9;function xm(e){return Number.isFinite(e)?Math.round(e/jh)*jh:e}const $m=(e,t,i,s)=>Math.hypot(i-e,s-t),Sm=(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)))),Mm=(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($m(e+c,t+h,n+d,o+p),$m(e-c,t-h,n-d,o-p)),_=Math.max($m(e+c,t+h,n-d,o-p),$m(e-c,t-h,n+d,o+p));return Math.min(u,_)},Cm=e=>{const t=Number(e?.cell_cm);return t>0?t:5};function Dm(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)<=km&&h++},u=(e,t)=>{p(e[0],t[0]),p(e[1],t[1])},_={};let m=5;for(const e of i){const t=Cm(e);null!=e?.id&&(_[String(e.id)]=t),t>m&&(m=t)}const g=(e,t,i,s=!1)=>{if(!(e>km||s))return;n++,e>a&&(a=e);const o=e*Lh*t;o>r&&(r=o,l=i)};for(const e of i){const t=Cm(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=[xm(t[0]),xm(t[1])];return e=Math.max(e,$m(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=xm(t+n),r=xm(i+o),l=xm(t),c=xm(i),h=Math.max(jh,a-l),d=Math.max(jh,r-c);e=Sm(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=[xm(t[0]),xm(t[1])];return e=Math.max(e,$m(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=[xm(n[0]),xm(n[1])],l=[xm(a[0]),xm(a[1])];let c=Math.max($m(n[0],n[1],r[0],r[1]),$m(a[0],a[1],l[0],l[1]));const h=$m(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>=-km&&s+t/2<=h+km});h>km&&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=[xm(e[0]),xm(e[1])],a=$m(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=[xm(t[0]),xm(t[1])],o=[xm(i[0]),xm(i[1])];e=Math.max($m(t[0],t[1],n[0],n[1]),$m(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=xm(t),r=xm(i);if(null!=s.w&&null!=s.h){const l=xm(t+n),c=xm(i+o),h=Math.max(jh,l-a),d=Math.max(jh,c-r);e=Sm(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=$m(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)<=km)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),_=Mm(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:jh,length:Number(s.length)||0});if(!n)continue;const a=Number(s.angle),r=!(Number.isFinite(a)&&a===n.angle),l=Mm(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=xm(i.x),a=xm(i.y),r=$m(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 Pm(e,t){const i=t.base+e*t.span;return Math.min(1,Math.max(0,i))}const Tm=e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]),Im=(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 Im(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 Am(e,t,i,s,n){for(const s of t)if(s&&!i.has(s.id)&&Tm(s.a)&&Tm(s.b)&&(Im(e,s.a)<=n||Im(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(Im(i,n)<=s)return[(i[0]+n[0])/2,(i[1]+n[1])/2];return null}function Fm(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&&Tm(e.a)&&Tm(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;eIm(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 Nm(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:Pm(e.t,a)},o++;const r=tu(i,t,s.coordScale,s.cellCm,s.gridPitch).resolved;r&&Object.assign(i,lu(i,r,s.coordScale))}return o}function Em(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 Om=e=>!!e&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]),Hm=(e,t)=>e[0]-t[0]||e[1]-t[1],Lm=(e,t,i,s)=>[e[0]+t*s,e[1]+i*s],Bm=(e,t,i,s)=>(e[0]-t[0])*i+(e[1]-t[1])*s,jm=(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=Bm(i,e,l,c),p=Bm(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},qm=(e,t,i)=>Math.min(e.hi,t.hi)-Math.max(e.lo,t.lo)>i,Wm=e=>{const t=new Set(["id","a","b","cm"]);return Object.keys(e||{}).every(e=>t.has(e))},Um=(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=Ml(e.cm,i.cellCm,i.gridPitch)/2;return t.some(t=>{const n=t?.center;if(!Om(n))return!0;const o=Bm(n,e.a,r,l),h=Math.abs((n[0]-e.a[0])*l-(n[1]-e.a[1])*r),d=Ml(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})},Gm=(e,t,i)=>{if(e.host){const s=tu(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},Vm=(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]}),Ym=(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},Jm=(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 Xm(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 _=wc(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:vl;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 b=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(!Wm(e))continue;const i=b.get(e.id);if(!(i&&Om(i.a)&&Om(i.b)&&Number.isFinite(i.cm)&&i.cm>0))continue;const[o,a]=Hm(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,y=wc(t.rooms,d,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale),w=y.flatMap(e=>{if(e.open||"outer"!==e.kind&&"shared"!==e.kind)return[];const t=jm(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:tu(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=Bm(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=Lm(o,_,g,s),l=Lm(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:vl)),f=p[0]?.kind,v=[...d.keys()].sort(),b=1===u.size&&1===m.size&&("outer"===f&&1===v.length||"shared"===f&&2===v.length),y={id:i.id,a:r,b:l,cm:i.cm},k=C.some(e=>!!jm(r,l,e.a,e.b,h)),x=D.some(e=>e.points.some((t,i)=>i+10?p[0].cm:vl:0,T=b?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&&eqm(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&&ru(t,y,h)){T.set(e.id,{resolved:t,signature:[...r][0]});continue}const l=Qp(i,n.cellCm,n.gridPitch),c={lo:s.lo-l,hi:s.hi+l};for(const e of P)qm(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=>qm(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=Qp(i,n.cellCm,n.gridPitch),r={lo:s.lo-a,hi:s.hi+a};for(const e of P)qm(e,r,h)&&(e.safe=!1,e.signature="");I=!0}}const R=Jm(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]=Hm(i.a,i.b)<=0?[e.a,e.b]:[e.b,e.a];F=Ul(F,t,s,e.finalCm,n.pitch,n.coordScale)}if(F=xc(t.rooms,F,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale),F.length>500)continue;const N=new Set(p.map(e=>e.id));N.delete(i.id);const E=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:Ym(i.id,s,o,N);return N.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,...E);const L=H.map(e=>Km(e,n.coordScale)),B=new Map;let j=!0;for(const{opening:e,resolved:t}of k){const i=T.get(e.id);if(i){const t=lu(e,i.resolved,n.coordScale),{host:s,...o}=t;B.set(e.id,o);continue}const s=S.get(e.id),o=E[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){j=!1;break}const r=a.b[0]-a.a[0],l=a.b[1]-a.a[1],c=Math.hypot(r,l),d=Bm(t.center,a.a,r/c,l/c),p={...lu(e,t,n.coordScale),host:{kind:"partition",id:o.id,t:d/c}};if(!iu(p,L,n.coordScale,n.cellCm,n.gridPitch).resolved){j=!1;break}B.set(e.id,p)}if(!j)continue;const q=u.map(e=>B.get(e.id)||e),W=[...T.keys()].map(e=>B.get(e)),U=W.map(e=>Gm(e,L,n));if(U.some(e=>!e))continue;let G=!1;for(let e=0;ee.id));for(const e of q){if(V.has(e.id))continue;const t=Gm(e,L,n);t&&U.some(e=>Vm(e,t,h))&&(G=!0)}if(G)continue;const K=Kc(t.rooms,F,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale);[...T.entries()].every(([e,t])=>{const i=B.get(e),s=Xc(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=q,f+=A.length,v+=T.size)}return{walls:d,partitions:p,openings:u,roomDrafts:g,partitionsReconciled:f,openingsRehosted:v,removedDrafts:m.size}}const Zm=e=>JSON.parse(JSON.stringify(e)),Qm=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),eg=(e,t)=>t.length>0&&t.length<=35&&("space"===e?/^[a-z0-9_-]+$/.test(t):/^[A-Za-z0-9_-]+$/.test(t));function tg(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 ig=(e,t,i)=>{const s=e.get(t)||[];s.push(i),e.set(t,s)};const sg=1e-9;class ng extends Error{constructor(e,t=""){super(t?`${e}: ${t}`:e),this.reason=e,this.name="WallSegmentModelError"}}const og=e=>JSON.parse(JSON.stringify(e)),ag=e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(Number(e[0]))&&Number.isFinite(Number(e[1])),rg=e=>[Number(e[0]),Number(e[1])],lg=e=>`${Number(e[0]).toFixed(12)},${Number(e[1]).toFixed(12)}`,cg=(e,t)=>{const i=lg(e),s=lg(t);return ilg(e)<=lg(t)?[rg(e),rg(t)]:[rg(t),rg(e)],dg=(e,t)=>Math.hypot(t[0]-e[0],t[1]-e[1]),pg=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/jh;Math.abs(s-Math.round(s))>=tl&&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},_g=(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},mg=(e,t,i)=>{const s=Math.max(0,Math.min(1,_g(e,t,i)));return Math.hypot(e[0]-(t[0]+(i[0]-t[0])*s),e[1]-(t[1]+(i[1]-t[1])*s))},gg=(e,t,i,s,n=sg)=>{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=_g(i,e,t),h=_g(s,e,t);return Math.max(0,Math.min(1,Math.max(c,h))-Math.max(0,Math.min(c,h)))*r},fg=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},bg=(e,t,i,s,n="")=>{const[o,a]=hg(t,i),r=`${e}|${lg(o)}|${lg(a)}|${[...s].sort().join(",")}${n}`;return`wall-${vg(fg(r)).slice(0,20)}`};let yg=0;const wg=e=>{for(let t=0;t<1e3;t++){const t=globalThis.crypto?.randomUUID?.(),i=`wall-${t||vg(fg(`${Date.now()}|${yg++}|${Math.random()}`)).slice(0,26)}`;if(!e.has(i))return i}throw new ng("duplicate-id","id factory exhausted")},kg=e=>{const t=new Map;for(const i of Array.isArray(e?.wall_segments)?e.wall_segments:[])if(i&&"string"==typeof i.id&&ag(i.a)&&ag(i.b)){if(t.has(i.id))throw new ng("duplicate-id",i.id);t.set(i.id,i)}return t},xg=(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)<=sg&&Math.abs(t[1]-s[1]-o)<=sg?[n,o]:null};return s(i.a,i.b)||s(i.b,i.a)},$g=(e,t)=>{const i=Array.isArray(e.rooms)?e.rooms:[],s=Od(e.open_spans).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]]),n=s.length?s:Hd(i,null,1,.04*jh,!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=>mg([Number(e.x),Number(e.y)],[t[0],t[1]],[t[2],t[3]])<=.04*jh)):[],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(!Bl(e,t,Number(i.angle)))return!1;if(mg(s,e,t)>.02*jh)return!1;const n=dg(e,t),o=_g(s,e,t)*n,a=Number(i.length)/2;return o+a>=-sg&&o-a<=n+sg}),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 ng("invalid-room",r);const u=dc(i,r,a,jh,1,[...e.walls||[],...l]);if(!u||u.poly.length<3)throw new ng("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;esg||Math.abs(s[1]-g[1])>sg)){f=!1;break}g=s}const v=[];for(let e=0;e2)throw new ng("third-owner",a);const d=m?_[u.parent[e]]:void 0;if("string"==typeof d&&d){const n=t.get(d);f||!n||gg(i,s,n.a,n.b)>sg?l.preferredIds.add(d):l.positionalIds.add(d),l.preferredCarriers.set(d,{a:rg(p[u.parent[e]]),b:rg(p[(u.parent[e]+1)%p.length])})}const g=u.parent[e];l.parentKeys.add(Il(p[g],p[(g+1)%p.length],jh));const b=[(i[0]+s[0])/2,(i[1]+s[1])/2],y=e=>e.some(e=>mg(b,[e[0],e[1]],[e[2],e[3]])<=.04*jh);l.zeroWall||=y(o)||y(n)&&!c(i,s),v.push(a)}const b={...s};delete b.wall_ids,d.push({...b,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}},Sg=(e,t,i,s,n)=>{const o=new Map([...i.values()].map(e=>[cg(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 ng("duplicate-id",t);r.set(e,s);continue}if(e.preferredIds.size>1)throw new ng("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=>gg(e.a,e.b,t.a,t.b)>sg),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)||dg(t.a,t.b)-dg(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 ng("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=mg(n,e.a,e.b)<=sg?0:1,o=mg(n,t.a,t.b)<=sg?0:1;if(i!==o)return i-o;return(mg(s.a,e.a,e.b)<=sg?0:1)-(mg(s.a,t.a,t.b)<=sg?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 ng("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]=hg(t.a,t.b),n=`${String(e.id||"")}|${lg(i)}|${lg(s)}|${[...t.owners].sort().join(",")}`,o=vg(fg(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 ng("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=wg(c),c.add(e.id)},Mg=(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=_g(i,t.a,t.b),n=dg(t.a,t.b),o=Number(e.length)/2;return s>=-sg&&s<=1+sg&&mg(i,t.a,t.b)<=.02*jh&&Bl(t.a,t.b,Number(e.angle))&&Number.isFinite(o)&&o>=0&&s*n-o>=-sg&&s*n+o<=n+sg},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,_g(i,a.a,a.b)))}},Cg=(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=_g(i,t.a,t.b),o=dg(t.a,t.b);return n>=-sg&&n<=1+sg&&mg(i,t.a,t.b)<=.02*jh&&Bl(t.a,t.b,Number(e.angle))&&Number.isFinite(s)&&s>=0&&n*o-s>=-sg&&n*o+s<=o+sg}),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)=>mg(i,e.a,e.b)-mg(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,_g(i,o.a,o.b)))}:null},Dg=(e,t,i)=>{const s=kg(e),{atoms:n,rooms:o}=$g(e,s);Sg(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=Il(t.a,t.b,jh),o=s.filter(e=>Number(e.cm)>0&&(!(e.key!==n&&!t.parentKeys.has(e.key))||!(!ag(e.a)||!ag(e.b))&&gg(t.a,t.b,e.a,e.b)>=dg(t.a,t.b)-sg)).map(e=>Math.max(1,Math.min(100,Number(e.cm))));if(new Set(o.map(e=>e.toFixed(9))).size>1)throw new ng("thickness-conflict",t.key);if(o.length)return o[0];const a=Hl(s,t.a,t.b,jh,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:Il(e.a,e.b,jh),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=Mg(e,t);i&&(e.host=i);continue}const s=Mg(e,t)??(i?Cg(e,t):null);if(s)e.host=s;else{if(!i)throw new ng("opening-host",e.id);delete e.host}}})(e,a,t),a.reduce((e,t)=>e+(s.has(t.id)?0:1),0)};function Pg(e,t={}){const i=JSON.stringify(e),s=og(e);fl(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+=Dg(e,o,i)}return s.model_version=9,fl(s),{config:s,changed:i!==JSON.stringify(s),migratedSegments:n}}const Tg=e=>!!e&&"object"==typeof e&&!Array.isArray(e);function Ig(e,t){const i=(e,t)=>{if(Array.isArray(e)&&Array.isArray(t)){const s=new Map;for(const t of e)Tg(t)&&"string"==typeof t.id&&t.id&&s.set(t.id,t);const n=t.map((t,n)=>{if(Tg(t)&&"string"==typeof t.id&&t.id){const e=s.get(t.id);return e?i(e,t):og(t)}return n[t,t in e?i(e[t],s):og(s)]);for(const t of Object.keys(e))delete e[t];for(const[t,i]of s)e[t]=i;return e}return og(t)};return i(e,t)}const Rg=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),Ag=(e,t,i)=>Math.min(i,Math.max(t,e)),zg=e=>Eh({spaces:[e]})[0],Fg=(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=yc(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 b=Fg(g,f,r),y=p.get(b);y?y.targets.add(a):p.set(b,{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)=>Fg(e.a,e.b,r).localeCompare(Fg(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=Ul(u,e.a,e.b,t,s,r))}return u}const Eg=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=tg("space",e);t.layers>0&&eg("space",t.root)&&ig(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=tg("room",e);n.layers>0&&eg("room",n.root)&&ig(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 b={spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,positionsUnresolved:0,nestedRefsUnresolved:0,deadSpaceIds:[],orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0,removedPositions:[],liveMissingPositions:[],unverifiedPositions:[]},y=new Set,w=e=>h.has(e)?null:(()=>{const t=tg("space",e).root;if(!eg("space",t))return null;const i=d.get(t)||[];return 1===i.length?i[0]:null})(),k=(e,t)=>{const i=tg("room",e).root;if(!eg("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,b.roomRefsRemapped++):i?(e.room_id=i.roomId,b.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,b.roomRefsRemapped++):b.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,b.spaceRefsRemapped++):l||"virtual"===e.binding?l||(delete e.space,c=null,b.markersDetached++):(d=x(t),d?(e.space=d.spaceId,c=d.spaceId,b.spaceRefsRemapped++):(delete e.space,c=null,b.markersDetached++))}l&&!p||$(e,c,d),r&&p&&a===i&&c&&(n[t]={...o,s:c},b.positionsRemapped++,y.add(t))}for(const e of Object.keys(n)){if(y.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}`;Qm(n,i)||(n[i]={...t,s:s}),delete n[e],b.roomRefsRemapped++,b.positionsRemapped++;continue}}n[e]={...t,s:s},b.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?b.orphanRoomLabelsRemoved++:"group"===e?b.orphanGroupPositionsRemoved++:b.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(b.liveMissingPositions.push(o),!0===i.removeLiveMissingPositions){delete n[e],b.liveMissingPositionsRemoved++,R(o.kind),b.removedPositions.push(o);continue}}else b.unverifiedPositions.push(o);b.positionsUnresolved++,A.add(s)}else delete n[e],R(o.kind),b.removedPositions.push(o)}return b.deadSpaceIds=[...A].sort((e,t)=>e.localeCompare(t)),{config:s,layout:n,report:b,changed:JSON.stringify(s)!==o||JSON.stringify(n)!==a}}(e,t,i),a=o.config,r=_l(a,o.layout);fl(a),gl(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>=Ah&&e<=zh&&(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?Ag(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(Ag(i/Bh*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(Ag(ts*is(e)/Bh*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=Dm(a.spaces||[],o.layout);let g=0,f=0,v=0,b="";const y=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 Pa(e,i)){const e=r.get(ba(s.from));if(e&&e!==ba(s.to))continue;const n=Ma(i,s);if(Ca(i,n,s)){t=s,i=n;break}}t?(r.set(ba(t.from),ba(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=fa(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 Pa(e,[{poly:s}])){const e=s.map(e=>Sa(e,i)),n=e.length>=4&&ya(e[0],e[e.length-1]),o=n?e.slice(0,-1):e;if(n?xa(o):$a(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=fa(e.a,e.b);if(!i)continue;const s={key:String(e.id||wa(e.a,e.b)),a:[...e.a],b:[...e.b],axis:i.axis};let r=null;for(const i of Pa(s,[{poly:[e.a,e.b]}])){const s=Sa(e.a,i),n=Sa(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/jh*i;return s>v&&(v=s,b=String(e?.id||"")),t.space}),w=g?Dm(y,m.layout):{...m,spaces:y};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=zg(t),r=zg(i);if(!o||!r)continue;const l=Eg(o.rooms),c=Eg(r.rooms),h=.02*Bh,d=(t.wall_segments||[]).filter(e=>0===Number(e.cm)).map(e=>({...e,key:Il(e.a,e.b,jh),cm:0})),p=new Set(d.map(e=>e.key)),_=G_(t,o.rooms,Ih,h);$+=_.length;for(const e of _){const t=[e[0]/Ih,e[1]/Ih],i=[e[2]/Ih,e[3]/Ih],s=Il(t,i,jh);p.has(s)||(p.add(s),d.push({key:s,a:t,b:i,cm:0}))}const m=d.length,g=ql(d,l,c,jh,Ih),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*Ih),i=e.b.map(e=>e*Ih);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 b=(i.wall_segments||[]).filter(e=>Number(e.cm)>0);i.wall_segments=[...b,...v.map(({key:e,...t})=>t)];const y=v.map(e=>[e.a[0]*Ih,e.a[1]*Ih,e.b[0]*Ih,e.b[1]*Ih]),w=Array.isArray(t.walls)?t.walls.length:0;let k=ql(t.walls,l,c,jh,Ih);k=Ng(r.rooms,k,y,jh,Number(i.cell_cm)>0?Number(i.cell_cm):5,Bh,Ih),k=xc(r.rooms,k,y,jh,Number(i.cell_cm)>0?Number(i.cell_cm):5,Bh,Ih),k=Ll(k,i.rooms||[],jh,1,y.map(e=>[e[0]/Ih,e[1]/Ih,e[2]/Ih,e[3]/Ih])),x+=Math.max(0,w-k.length),k.length?i.walls=k:delete i.walls;const I=Fm(i.partitions||[],{pitch:jh,geometry:Em(i)});I.merged&&(M+=I.merged,i.partitions=I.partitions,Nm(i.openings,i.partitions,I.openingMoves,{coordScale:Ih,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,gridPitch:Bh}));const R=zg(i);if(R){const e=n(i,R,i.walls||[],y,{pitch:jh,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,gridPitch:Bh,coordScale:Ih});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=Pg(e);return Ig(e,t.config),{...t,config:e}}(a).migratedSegments);const R=fl(a),A=gl(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,N=Number.isInteger(Number(R.model_version))?Number(R.model_version):h,E=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:{...E,modelFrom:h,modelTo:N,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?b:"",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 Hg(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 Lg(e){return e instanceof Error?e.name||"Error":typeof e}function Bg(e){return Gd({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 jg(e,t,i,s=Bh,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=tu(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 qg(e,t=()=>!0){return e.flatMap(e=>e.host&&e.partitionHost&&t(e)?[nu(e.partitionHost)]:[])}function Wg(e,t,i,s,n,o,a=Bh,r=1e3){const l=wc(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&&ru(e.partitionHost,l,2e-4*a)?[t]:[]:[t]})}function Ug(e,t){const i=jh,s=Bh,n=Ih,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=Bh,s=1e3){return Hd(t.rooms,e?.open_spans,s,.02*i)}(e,t,s,n),l=function(e,t,i=Bh){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=jg(e,t,o,s,n),h=qg(c),d=_h(t,o,s,2e-4*s,h).all;return{space:t,walls:a,openCuts:l,openings:c,roomOpenings:Wg(c,t,a,l,i,o,s,n),partitionCuts:h,physicalBodies:d,wallKeyPitch:i,cellCm:o,gridPitch:s,coordScale:n}}function Gg(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 Vg(e,t={}){const i=(t.fingerprint||Gd)(e),s=Array.isArray(e?.spaces)?e.spaces:[],n=t.fallbackSpaceName||(e=>`Space ${e}`),o=t.prepareSpace||Ug,a=t.wallPass||qc,r=t.floorPass||Hc,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:Lg(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:Lg(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 Yg=new WeakMap;const Jg=(e,t,i)=>255===e[i+3]&&t.every((t,s)=>Math.abs(e[i+s]-t)<=2);async function Xg(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 Jg(c,i,0)&&Jg(c,function(e,t){return[0,1,2].map(i=>Math.round(255-(255-e[i])*(255-t[i])/255))}(i,s),4)&&Jg(c,s,8)&&Jg(c,n,12)}catch{return!1}finally{t.URL.revokeObjectURL(a)}}const Zg={now:()=>performance.now(),requestFrame:e=>requestAnimationFrame(e),cancelFrame:e=>cancelAnimationFrame(e)},Qg=e=>Math.max(0,Math.min(1,e)),ef=(e,t,i)=>e+(t-e)*i,tf=(e,t,i)=>{const s=1-e;return 3*s*s*e*t+3*s*e*e*i+e*e*e};function sf(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 nf(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 of(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 af(e,t,i){const s=of(e),n=of(t);if(!s||!n)return i<.5?e:t;const o=Qg(i);return`rgba(${Math.round(ef(s[0],n[0],o))}, ${Math.round(ef(s[1],n[1],o))}, ${Math.round(ef(s[2],n[2],o))}, ${ef(s[3],n[3],o).toFixed(3)})`}function rf(e,t,i){const s=(e=>{const t=Qg(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,tf(n,.2,.2){const t=this._state;if(!t||t.token!==n)return;const i=Qg((e-t.startedAt)/t.duration);t.presented=i>=1?t.to:rf(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 cf="houseplan_card_labs_v1",hf=Object.freeze([Object.freeze({id:"iso",issue:89,since:"1.62.0",expires:"1.65.0",summary:"Volumetric plan renderer"})]);function df(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 pf(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 vf(e,t,i,s=hf){const n=function(e,t=hf){const i=df(e),s=new Map;if(!i||!_f(t))return s;for(const e of t){const t=df(e.since),n=df(e.expires);pf(i,t)>=0&&pf(i,n)<0&&s.set(e.id,e)}return s}(i,s),o=new Set(_f(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 ff(new URLSearchParams(String(e.search||"").replace(/^\?/,""))))l(t);for(const t of ff(mf(e.hash)))l(t);const c=Object.freeze([...a].sort());return{active:c,space:gf(e.hash),persist:r?JSON.stringify(c):void 0,knownUrlOperation:r}}let bf={active:Object.freeze([]),space:""},yf="",wf=!1,kf="";const xf=new Set;function $f(){if("undefined"==typeof window)return bf;const e=vf(window.location,function(){try{return window.localStorage.getItem(cf)}catch{return null}}(),yf);void 0!==e.persist&&function(e){try{window.localStorage.setItem(cf,e)}catch{}}(e.persist),bf={active:e.active,space:e.space},window.__hpLabs=e.active;for(const e of xf)e(bf);return bf}function Sf(){$f()}function Mf(e){return yf=e,"undefined"==typeof window?bf:(wf||(wf=!0,window.addEventListener("hashchange",Sf),window.addEventListener("popstate",Sf)),$f())}const Cf=Object.freeze({rotDeg:0,tiltDeg:20,xyScale:1,zScale:1,origin:Object.freeze([500,500])});function Df(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 Pf(e,t,i=Cf){if(!(Df(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 Tf(e,t=Cf){if(!Df(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 If(e=Cf){return`matrix(${function(e=Cf){if(!Df(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=Cf){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=>[Pf(e,-o,t),Pf(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 Af=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function zf(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 zf(i)>0===s?i:[...i].reverse()}function Nf(e,t,i){return e.length?`M ${e.map(e=>Af(Pf(e,i,t))).join(" L ")} Z`:""}function Ef(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 Of(e,t,i=Cf){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=Ff(e,t);if(!i.length)return i;let s=0;for(let e=1;e0)).filter(e=>e.length>=3&&Math.abs(zf(e))>=1e-9)).filter(e=>e.length&&zf(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(zf(t[0]))-Math.abs(zf(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 Hf(e,t){return e^=Math.round(64*(Number.isFinite(t)?t:0)),Math.imul(e,16777619)>>>0}const Lf=e=>e.every(Number.isFinite);function Bf(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 jf(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],Bf(i,e.angle,r,l));return{leaf:t,hinge:h,closedVector:Bf(s,e.angle,r,l),quarterVector:Bf([-s[1],s[0]],e.angle,r,l),turnDeg:n,bottom:o,top:a}}const qf=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function Wf(e,t,i=Cf){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=Pf(t.hinge,t.bottom,i),a=Pf(n,t.bottom,i),r=Pf(n,t.top,i),l=Pf(t.hinge,t.top,i);return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaf:t.leaf,d:`M ${qf(o)} L ${qf(a)} L ${qf(r)} L ${qf(l)} Z`,shadowD:`M ${qf(Pf(t.hinge,0,i))} L ${qf(Pf(n,0,i))}`,depth:Math.max(o[1],a[1])}})}const Uf={window:120,door:90,passage:90,gate:300};function Gf(e){return Uf[e]}function Vf(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=Vf(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 Yf(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 Jf(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=Vf(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(Yf),s=t.filter(t=>!t.target.partitionHost||e.renderedLength+2*t.target.physicalHalfWidth<=t.length+1e-9).sort(Yf);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,b=r.a[1]+p*m,y=Math.max(0,e.renderedLength)/2,w=m-y,k=m+y,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:b,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:b,angle:C}:null}},jambBlockedTarget:null}}function Xf(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 Zf=(e,t)=>e[0]*t[1]-e[1]*t[0],Qf=(e,t)=>e[0]*t[0]+e[1]*t[1],ev=(e,t)=>[e[0]-t[0],e[1]-t[1]],tv=(e,t,i)=>[e[0]+t[0]*i,e[1]+t[1]*i];function iv(e,t,i){return Math.abs(Zf(e,t))<=i}function sv(e,t,i){return Math.abs(Zf(ev(e,t),i))}function nv(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&&iv(l,[s/a,o/a],1e-6)))return[];if(sv(t.a,e,l)>1.1*Math.max(i,n)||sv(t.b,e,l)>1.1*Math.max(i,n))return[];return[[Qf(ev(t.a,e),l),Qf(ev(t.b,e),l)]]}),n).flatMap(([t,s])=>{const n=Kl({a:tv(e,l,t),b:tv(e,l,s),halfDepth:i});return n?[n]:[]})}function ov(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=yc(e.rooms,i.id,e.walls,e.openCuts,e.pitch,e.cellCm,e.gridPitch,t);if(!n||n.poly.length<3)continue;const o=lc(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:tv([e.x,e.y],i,-s),b:tv([e.x,e.y],i,s)}}),o=[];for(const s of Gc(e.rooms,e.walls,e.openCuts,e.pitch,e.cellCm,e.gridPitch,t))for(const e of nv(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=Ml(t.cm,e.cellCm,e.gridPitch)/2;for(const e of nv(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 av(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=tv(i.center,o,n),r=[];for(let t=0;ts&&iv(i.axis,[l[0]/c,l[1]/c],1e-6)))continue;if(sv(n,a,i.axis)>s||sv(o,a,i.axis)>s)continue;const h=Qf(ev(n,a),i.axis),d=Qf(ev(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=tv(a,i.axis,-i.half),p=tv(a,i.axis,i.half),u=Math.max(0,-i.half-c),_=Math.max(0,h-i.half),m=u>0?tv(a,i.axis,c):d,g=_>0?tv(a,i.axis,h):p,f=Qf(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 rv(e){const t=tv(e.center,e.axis,-e.half),i=tv(e.center,e.axis,e.half),s=tv(e.center,e.axis,Math.min(-e.half,e.targetLo)),n=tv(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 lv(e,t,i,s){const n=ev(i,t),o=Qf(n,n);if(!(o>s*s))return Math.hypot(...ev(e,t))<=s;const a=Qf(ev(e,t),n)/o;if(a<-s||a>1+s)return!1;const r=tv(t,n,Math.max(0,Math.min(1,a)));return Math.hypot(...ev(e,r))<=s}function cv(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 hv(e,t,i,s,n,o){const a=ev(s,i),r=Zf(t,a);if(Math.abs(r)<=o)return null;const l=ev(i,e),c=Zf(l,a)/r,h=Zf(l,t)/r;return c<-o||c>n+o||h<-o||h>1+o?null:Math.max(0,Math.min(n,c))}function dv(e,t,i){const s=rv(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(cv(e,r.body,o))return 0;for(let s=0;sQf(ev(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=ev(a,o),l=Math.hypot(r[0],r[1]);if(!(l>i&&iv(t.axis,[r[0]/l,r[1]/l],1e-6)))continue;if(sv(o,t.center,t.axis)>i||sv(a,t.center,t.axis)>i)continue;const c=Qf(ev(o,t.center),t.axis),h=Qf(ev(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 rv(i);if(s.length){const e=s.map(({room:e,edge:s})=>av(e,s,i,t.epsilon));if(e.some(e=>!e))return rv(i);const n=e;return[...n.map(e=>e.left),...n.map(e=>e.right)]}return dv(e,t,i)}function uv(e){return!(!e.canEdit||e.kiosk||e.fixedFloor)&&("view"!==e.mode&&("mouse"===e.pointerType&&e.spaceCount>1))}const _v="1.68.1",mv={badge:"display.badge",icon_ripple:"display.icon_ripple",value:"display.value",static_icon:"display.static_icon"},gv={badge:"marker.display_hint_badge",icon_ripple:"marker.display_hint_icon_ripple",value:"marker.display_hint_value",static_icon:"marker.display_hint_static_icon"},fv=2e3,vv=1e3,bv=1500,yv=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},wv=(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}},kv=(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)}},xv=new Map;let $v=0;const Sv=e=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(e??{})}`;let Mv=1e4;const Cv="houseplan_card_layout_v1",Dv="houseplan_card_cfg_v1",Pv="houseplan_card_zoom_v1",Tv="houseplan_card_nav_v1",Iv="houseplan_card_kiosk_v1",Rv="houseplan_card_view_v1",Av="hp-dialog, hp-help, hp-color-opacity, hp-device-preview",zv=1e3,Fv=[[0,1],[45,.88],[70,.62],[86,.32],[100,0]],Nv=(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}},Ev=new Set(["select","draw","column","merge","split","resize","opening","wallthick","delroom"]),Ov=2e3,Hv=(e,t,i)=>{const s=new Event(t,{bubbles:!0,composed:!0});s.detail=i??{},e.dispatchEvent(s)},Lv=e=>{history.pushState(null,"",e),Hv(window,"location-changed",{replace:!1})},Bv=(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},jv=e=>{try{e.target?.setPointerCapture?.(e.pointerId)}catch{}};class qv 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 _m(this,()=>this._syncPointerHoverTargets()),this._devicePressAnimations=new Map,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...Br},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 lf({frame:e=>this._applyModeTransitionFrame(e),settled:e=>this._settleModeTransition(e)}),this._editorSecondary=new ym({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 pr,this._rszLimitViolation=null,this._path=[],this._cursorPt=null,this._planSnapHover=null,this._mergeSel=null,this._openingPreset=null,this._openingRebindId=null,this._openingPresetRevision=0,this._openingHoverCandidate=null,this._openingJambBlockCm=null,this._openingDialog=null,this._openingInfo=null,this._opDrag=null,this._opMeasure=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._wallFaceBatch=null,this._wallRepairDiagnostic=null,this._wallFaceGraphCache=[],this._areaSel="",this._nameSel="",this._roomDialog=!1,this._roomEditId=null,this._roomFill="",this._roomCustomFill=null,this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1,this._zoom=1,this._view=null,this._zoomBySpace={},this._viewModeSnap=null,this._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._deviceInbox=null,this._deviceInboxReturn=null,this._deviceInboxMemo=null,this._markerDialog=null,this._spaceDialog=null,this._keyHandler=e=>this._onKey(e),this._warmVp=null,this._warmVpArmed=!1,this._warmLongReturn=!1,this._warmRevivePending=!1,this._warmGen=++$v,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+cm)>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 Yd(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=Bv(()=>{if(this._serverStorage){const e=[...this._dirtyPos];this._dirtyPos.clear();for(const t of e){const e=ml(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=gl(il(this._layout)),localStorage.setItem(Cv,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=Bv(()=>{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=yv(e.cm);if(null==s)return void this._showPhysicalRange("column"===e.kind?ih:100,"column"===e.kind?1:0);const n=this._imperial?2.54*s:s,o="column"===e.kind?ih: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=yv(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&&Sh(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=nh(n),i.shape="circle"===e.shape?"circle":"square","square"===i.shape?i.angle=yv(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=e_(wc(s.rooms,this._spaceWalls,o,this._wallKeyPitch,this._cellCm,this._gridPitch,zv).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>Ov)return void this._showToast(this._t("toast.physical_limit"));const l=this._geometrySnapshot(),c=kc(s.rooms,this._spaceWalls,o,this._wallKeyPitch,this._cellCm,this._gridPitch,zv);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])*zv,Number(t.a?.[1])*zv],s=[Number(t.b?.[0])*zv,Number(t.b?.[1])*zv];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]/zv,o.interval.a[1]/zv],b:[o.interval.b[0]/zv,o.interval.b[1]/zv],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]*zv,s.a[1]*zv],b:[s.b[0]*zv,s.b[1]*zv]};t.host={kind:"partition",id:s.id,t:t_([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>Ov)return void this._showToast(this._t("toast.physical_limit"));const i=this._geometrySnapshot(),s=Cu(t.length,[...this._draftSegmentCms,this._closingWallCm??void 0],this._drawWallCm,vl);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=Sr(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:zr(this._settings,{}),bgMode:Fr(this._settings,{}),sunRays:Nr(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:sf(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=Ei(this._baseVb(),s/Math.max(1,n)),a=Math.min(qv.ZOOM_MAX,Math.max(qv.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:sf(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 uv({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:"mouse",spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})}_tabPointerDown(e,t){uv({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:e.pointerType,spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})&&(jv(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 Ud(()=>{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?Tf(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?Pf([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 gf(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,Yg.get(t)?.resolved);var t;void 0!==e&&(this._glowScreenBlend=e),this._continuityUnsub?.(),this._continuityUnsub=qd(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=Yg.get(e);if(t)return t.promise;const i={promise:Promise.resolve(!1)};return i.promise=Xg(e).catch(()=>!1).then(e=>(i.resolved=e,e)),Yg.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){xf.add(t);const i=Mf(e);return"undefined"==typeof window&&t(i),()=>{xf.delete(t)}}(_v,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},bv)),!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._closeMarkerDialog();if(this._deviceInbox)return void(this._deviceInbox=null);if(this._openingDialog)return void(this._openingDialog=null);if(this._physicalDialog)return void(this._physicalDialog=null);if(this._backdropDialog)return void(this._backdropDialog=null);if(this._decorShapeDialog)return void(this._decorShapeDialog=null);if(this._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0);if(this._editorSecondary.hasOpenGroup)return e.preventDefault(),void this._editorSecondary.closeGroup(!0)}const t=e.composedPath?.()||[e.target],i=t.some(e=>e?.matches?.('input, textarea, select, [contenteditable="true"]')),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]/zv,e[1]/zv]),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 xv.values())for(const t of e)clearTimeout(t.evict);xv.clear(),Mv=e&&e>0?e:1e4}static _warmBootStats(){let e=0,t=0;const i=[];for(const s of xv.values())for(const n of s)e++,n.dlg&&(t++,i.push(n.dlg.kind));return{keys:xv.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(Pv)||"{}")||{}}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=Mf(_v);try{const e=JSON.parse(localStorage.getItem(Iv)||"null");this._kioskScale={icon:Ns(e?.icon),font:Ns(e?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(Dv)||"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||Gd(e.config),this._layout=e.layout||{},this._layoutRev=e.layout_rev||0,this._layoutContentFingerprint=e.layout_fingerprint||Gd(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=Sv(this._config);if(this._warmKey===e&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const t=this.parentNode,i=this._warmIdx(t),s=xv.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>=Ld,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>=Ld,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},bv)),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=xv.get(t);if(!e.live&&o&&o.length>1){const t=o.indexOf(e);t>=0&&o.splice(t,1)}},Mv+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?Pf([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&&Ev.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=Sv(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=xv.get(i)||[];for(s.push(this._warmSlot),xv.set(i,s),this._warmTrim(s);xv.size>8;){const e=xv.keys().next().value;if(void 0===e||e===i)break;for(const t of xv.get(e)||[])clearTimeout(t.evict);xv.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>Mv)&&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=Gd(this._serverCfg),this._layoutContentFingerprint=Gd(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(bn).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(Dv,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=Eh(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 Vd([this._cfgRev,this._cfgContentFingerprint||Gd(this._serverCfg),this._layoutRev,this._layoutContentFingerprint||Gd(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 q`
e.stopPropagation()} @click=${e=>e.stopPropagation()} @@ -5109,8 +5109,8 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 ${this._t(e?"continuity.restore_connection":"continuity.restore_plan")} ${"recovery-error"===this._continuity.state?q``: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 Rh(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?Ud(this._model,this._space):(e=this._model,t=this._space,e.find(e=>e.id===t)??e[0]);var e,t}_spaceModelById(e){return Ud(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._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._physicalDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._roomDialog=!1,"edit"===this._spaceDialog?.mode&&(this._spaceDialog=null),this._editorSecondary.closeForNavigation(),this._saveConfigDebounced.cancel(),this._frame=null,this._planSnapGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._decorSnapCache=null,this._commitSpace("",!0)}}else this._emptySpaceStateActive=!1}get _areaToSpace(){const e={};for(const t of this._model)for(const i of t.rooms)i.area&&(e[i.area]={space:t.id,room:i});return e}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._showHidden||!this._settings.filter_seeded&&!!this._settings.show_all}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const e=this._serverCfg,t=function(e){const t=e.hass,i=e.registry||An(t),s=En(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=bo(s,h).filter(e=>!ko(s,e.eid,d)),u=new Set(p.map(e=>e.area)),_=jn(s),m=qn(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=Wn(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"===yo(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=Bd(n),a=o!==(this._cfgContentFingerprint||Bd(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=Bd(e);r=s!==(this._layoutContentFingerprint||Bd(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=Bd(n)!==(this._cfgContentFingerprint||Bd(this._serverCfg))||Bd(s?.layout??{})!==(this._layoutContentFingerprint||Bd(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(kv)||"{}")||{}}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(Bd(t)!==(this._cfgContentFingerprint||Bd(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=En(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+nm)>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(I_(e.id,t),C_(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?np(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=T_({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&&Fv("/config/devices/device/"+encodeURIComponent(e.device_id))}}else Fv("/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=Bd(i);s!==(this._layoutContentFingerprint||Bd(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)&&(zv(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 Ni(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*Qh(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]=Hh(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*Pv,y:i.y*Pv}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[e.id]?this._defPos[e.id]:t?Hh(Zh(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:Bh(n/Pv),y:Bh(o/Pv),...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 k_(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(I_(e.id,t));return s||C_(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,M_({...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,zv(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=Op(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(!Wp(e))return void this._showUnavailableToggleTargets(e);const t=e=>{const t=Wp(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&&!Wp(n)&&this._showUnavailableToggleTargets(n)||(n&&Gp(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||Wp(e)||"configured-targets-missing"!==e.noneReason)return[];if(!e.skippedTargets.length||e.skippedTargets.some(e=>!Up.has(e.reason)&&"secure"!==e.reason))return[];const t=[],i=new Set;for(const s of e.skippedTargets){if(!Up.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)?q``: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=Wh([[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=Wh("line"===e.kind?[[e.x1*Pv,e.y1*i],[e.x2*Pv,e.y2*i]]:"text"===e.kind?[[e.x*Pv,e.y*i]]:Wr({x:e.x*Pv,y:e.y*i,w:e.w*Pv,h:e.h*i,angle:e.angle}));s&&t.push(s)}for(const i of this._physicalBodiesR(e)){const e=Wh(i);e&&t.push(e)}}return Gh(e,t)}_frameOf(){const e=this._spaceModel();if(!e){this._frame=null;const e={x:0,y:0,w:Pv,h:Pv};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=Jh(this._contentItems(e));let n=s.all||Xh(e),o=this._showFar?n:s.core||Xh(e);return t&&t.id===e.id&&i&&t.grow&&(o=Iv(t.rect,o),n=Iv(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=xh(64,this._cellCm),o=xh(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=zf(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=zf(t,-1)}else t=zf(t,e?1:0);else for(const i of e)t=zf(t,i.codePointAt(0)||0);else t=zf(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:kf,wallHeight:n,floorEdgeHeight:o,algorithm:3})}`;return{key:a,build:()=>{const o=ch(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e)).all,a=this._roomWallOpeningInputs(this._openingsR,e),r=t.length||o.length?Oc(e.rooms,t,i,a,this._wallKeyPitch,this._cellCm,this._gridPitch,Pv,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,Pv);if(!l)throw new Error("floor boolean geometry failed");const c=r?.openingIndex||qc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Pv),h=s.map(e=>{const i=this._openingsR[e.sourceIndex],s="gate"===e.type?!e.flipV:e.flipV,o=i?.partitionHost?eu(i.partitionHost,s):t.length||"gate"===e.type?Vc(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(!(Ff([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=[Ef(e,0,[-i,0],[i,0],n,0,.88*t),Ef(e,1,[i,0],[-i,0],-n,0,.88*t)]}else s="window"===e.type?[Ef(e,0,[-i,0],[i,0],-90,.27*t,.78*t),Ef(e,1,[i,0],[-i,0],90,.27*t,.78*t)]:[Ef(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=xh(64,this._cellCm),o=xh(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 Ff([t,i,s,n])?{x:t,y:i,w:s-t,h:n-i}:null}(s.openings),r=Cf({rect:a?Iv(i,a):i,wallHeight:n,openingHeight:n,floorDepth:o}),l=function(e,t=kf,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=Tf(l[e],e>0);if(!(c.length<3||Math.abs(Pf(c))<1e-9)){s.push(If(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,kf,n),c={geometry:l,floor:Af(s.floor,o),openings:s.openings,frame:r};return fv(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?$f(e,0):e}_floorView(e){if("iso"!==this._renderProjection)return e;const t=Sf([e.x,e.y]),i=Sf([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=Cf({rect:this._frameOf().rect,wallHeight:xh(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:q`
+
`}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 Eh(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?Jd(this._model,this._space):(e=this._model,t=this._space,e.find(e=>e.id===t)??e[0]);var e,t}_spaceModelById(e){return Jd(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._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._physicalDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._roomDialog=!1,"edit"===this._spaceDialog?.mode&&(this._spaceDialog=null),this._editorSecondary.closeForNavigation(),this._saveConfigDebounced.cancel(),this._frame=null,this._planSnapGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._decorSnapCache=null,this._commitSpace("",!0)}}else this._emptySpaceStateActive=!1}get _areaToSpace(){const e={};for(const t of this._model)for(const i of t.rooms)i.area&&(e[i.area]={space:t.id,room:i});return e}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._showHidden||!this._settings.filter_seeded&&!!this._settings.show_all}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const e=this._serverCfg,t=function(e){const t=e.hass,i=e.registry||An(t),s=En(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=bo(s,h).filter(e=>!ko(s,e.eid,d)),u=new Set(p.map(e=>e.area)),_=jn(s),m=qn(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=Wn(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"===yo(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=Gd(n),a=o!==(this._cfgContentFingerprint||Gd(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=Gd(e);r=s!==(this._layoutContentFingerprint||Gd(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=Gd(n)!==(this._cfgContentFingerprint||Gd(this._serverCfg))||Gd(s?.layout??{})!==(this._layoutContentFingerprint||Gd(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(Cv)||"{}")||{}}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(Gd(t)!==(this._cfgContentFingerprint||Gd(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=En(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+cm)>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(N_(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?cp(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=F_({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&&Lv("/config/devices/device/"+encodeURIComponent(e.device_id))}}else Lv("/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=Gd(i);s!==(this._layoutContentFingerprint||Gd(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")+":"+pa(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)&&(Hv(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 Ni(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*nd(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]=Wh(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*zv,y:i.y*zv}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[e.id]?this._defPos[e.id]:t?Wh(sd(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:Gh(n/zv),y:Gh(o/zv),...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 C_(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(N_(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,I_({...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,Hv(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=qp(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(!Yp(e))return void this._showUnavailableToggleTargets(e);const t=e=>{const t=Yp(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&&!Yp(n)&&this._showUnavailableToggleTargets(n)||(n&&Xp(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||Yp(e)||"configured-targets-missing"!==e.noneReason)return[];if(!e.skippedTargets.length||e.skippedTargets.some(e=>!Jp.has(e.reason)&&"secure"!==e.reason))return[];const t=[],i=new Set;for(const s of e.skippedTargets){if(!Jp.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 ua(pa(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=pa(this.hass,this._config?.language);return _a(i,e)&&_a(i,t)?q``: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=Yh([[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=Yh("line"===e.kind?[[e.x1*zv,e.y1*i],[e.x2*zv,e.y2*i]]:"text"===e.kind?[[e.x*zv,e.y*i]]:Yr({x:e.x*zv,y:e.y*i,w:e.w*zv,h:e.h*i,angle:e.angle}));s&&t.push(s)}for(const i of this._physicalBodiesR(e)){const e=Yh(i);e&&t.push(e)}}return Xh(e,t)}_frameOf(){const e=this._spaceModel();if(!e){this._frame=null;const e={x:0,y:0,w:zv,h:zv};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=td(this._contentItems(e));let n=s.all||id(e),o=this._showFar?n:s.core||id(e);return t&&t.id===e.id&&i&&t.grow&&(o=Nv(t.rect,o),n=Nv(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=Dh(64,this._cellCm),o=Dh(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=Hf(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=Hf(t,-1)}else t=Hf(t,e?1:0);else for(const i of e)t=Hf(t,i.codePointAt(0)||0);else t=Hf(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:Cf,wallHeight:n,floorEdgeHeight:o,algorithm:3})}`;return{key:a,build:()=>{const o=_h(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e)).all,a=this._roomWallOpeningInputs(this._openingsR,e),r=t.length||o.length?qc(e.rooms,t,i,a,this._wallKeyPitch,this._cellCm,this._gridPitch,zv,o):null;if(r&&("failed-core"===r.status||"not-applicable"===r.status))throw new Error("wall boolean geometry failed");const l=r?.paperGeom??Hc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,zv);if(!l)throw new Error("floor boolean geometry failed");const c=r?.openingIndex||Kc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,zv),h=s.map(e=>{const i=this._openingsR[e.sourceIndex],s="gate"===e.type?!e.flipV:e.flipV,o=i?.partitionHost?ou(i.partitionHost,s):t.length||"gate"===e.type?Zc(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(!(Lf([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=[jf(e,0,[-i,0],[i,0],n,0,.88*t),jf(e,1,[i,0],[-i,0],-n,0,.88*t)]}else s="window"===e.type?[jf(e,0,[-i,0],[i,0],-90,.27*t,.78*t),jf(e,1,[i,0],[-i,0],90,.27*t,.78*t)]:[jf(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=Dh(64,this._cellCm),o=Dh(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 Lf([t,i,s,n])?{x:t,y:i,w:s-t,h:n-i}:null}(s.openings),r=Rf({rect:a?Nv(i,a):i,wallHeight:n,openingHeight:n,floorDepth:o}),l=function(e,t=Cf,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=Ff(l[e],e>0);if(!(c.length<3||Math.abs(zf(c))<1e-9)){s.push(Nf(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,Cf,n),c={geometry:l,floor:Of(s.floor,o),openings:s.openings,frame:r};return kv(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?Pf(e,0):e}_floorView(e){if("iso"!==this._renderProjection)return e;const t=Tf([e.x,e.y]),i=Tf([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:Dh(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:q`
${this._t("canvas.far_objects",{n:this._outliers})} @@ -5118,7 +5118,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 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:Ei(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=Ei(s,this._stageAspect()),o=Math.min(Ov.ZOOM_MAX,Math.max(Ov.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},_v)}_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>=Fd?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=Ei(this._baseVb(),this._stageAspect()),o=Math.min(Ov.ZOOM_MAX,Math.max(Ov.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=Ei(e,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem($v,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=Ei(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&&Fv("/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},Ev(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=Lh(this._drag.ox+a),c=Lh(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(Dv))t.toggleAttribute("data-pointer-hover",e)}_syncPointerHoverSubtree(e){if(e.nodeType!==Node.ELEMENT_NODE)return;const t=e,i=this._pointerModality.hoverEnabled;t.matches(Dv)&&t.toggleAttribute("data-pointer-hover",i);for(const e of t.querySelectorAll(Dv))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 Nh}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,Pv}get _segments(){const e=this._curSpaceCfg,t=this._spaceH;return pi(e?.rooms||[]).map(e=>[e[0]*Pv,e[1]*t,e[2]*Pv,e[3]*t])}_savedNav(){if(this._hasFixedFloor)return null;try{return JSON.parse(localStorage.getItem(Sv)||"null")}catch{return null}}_saveNav(){if(!this._hasFixedFloor)try{localStorage.setItem(Sv,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._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._openingDialog=null,this._physicalDialog=null,this._wallDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._mergeDialog=null,this._roomDialog=!1,this._spaceDialog=null,this._importDialog=null;const e=this._warmSlot;e?.owner===this._warmGen&&(e.vp=this._warmViewportState(),e.dlg=null,e.frameFingerprint="",clearTimeout(e.evict),e.evict=0),this._saveNav()}_setMode(e,t=!0){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,"devices"===s&&"devices"!==e&&(this._showHidden=!1,this._deviceInbox=null,this._deviceInboxReturn=null,this._deviceInboxMemo=null),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?$f([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=yl(ul,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?yl(ul,this._imperial):this._drawWallField}get _drawWallCm(){const e=mv(this._drawWallFieldValue);if(null==e||e<0)return null;const t=this._imperial?2.54*e:e,i="column"===this._tool?Xc:100;return t>=("column"===this._tool?1:0)&&t<=i?t:null}get _drawWallMaxCm(){return"column"===this._tool?Xc:100}_showPhysicalRange(e=this._drawWallMaxCm,t=0){this._showToast(this._t("toast.physical_range",{min:yl(t,this._imperial),max:yl(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=Tm(i,{pitch:Eh,seedIds:t,geometry:Rm(e,{excludeDraftId:this._activeDraftId})});return s.merged?(e.partitions=s.partitions,Im(e.openings,e.partitions,s.openingMoves,{coordScale:Pv,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=xu(this._path,ku(this._path.length-1,this._draftSegmentCms,this._drawWallCm,ul)),i=t.length;if((e.partitions||[]).length+i>Av)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>=Av:(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[Lh(ri(e[0],t)),Lh(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,zh):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=Gc(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=_u({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=uu({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=_u({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?yu(i.value,e,{...o,anchor:s}):function(e,t,i){if(!(ou(t)&&i.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const s=i.epsilon??nu,n=i.excludePoints||[],o=vu(fu(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=bu(o,t,i.gridStep);!mu(l,n,s)&&gu(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))<=Ah);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:Qc(Number(e.cm)||15),..."circle"===e.shape?{}:{angle:eh(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=rg(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=Ud(Rh({spaces:[e]}),e.id),i=t?j_(e,t,Pv,.02*Nh).contour.map(e=>e.map(e=>e/Pv)):Ad(e.open_spans).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]]);e.walls=Fl(e.walls,e.rooms||[],Eh,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=Bd(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=Bd(e),i=[...this._pendingPhysicalWrites.entries()];for(const[t,s]of i){const i=Ng(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!==Bd(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 Qm?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)}))}_limitSegmentsOf(e){const t=[];for(const i of e?.wall_segments||[])i?.a&&i?.b&&t.push({id:String(i.id||""),a:i.a,b:i.b,cm:Number(i.cm)});for(const i of e?.partitions||[])i?.a&&i?.b&&t.push({id:String(i.id||""),a:i.a,b:i.b,cm:Number(i.cm)});for(const i of e?.room_drafts||[]){const e=Array.isArray(i?.points)?i.points:[],s=Array.isArray(i?.segments)?i.segments:[];for(let n=0;n+1e?.id===t);if(!i)return[];const s=Number(i.cell_cm)>0?Number(i.cell_cm):5,n=this._limitSegmentsOf(i),o=[...xd(n),...Md(n,s,Eh),...Dd(n,s,Eh)];for(const e of i.rooms||[]){const t=String(e?.id||"");if(!t)continue;let n=null;try{n=kc(i.rooms||[],t,i.walls||[],[],Eh,s,Eh,1)}catch{n=null}o.push(...Pd(t,n,s,Eh))}return o}_junctionLimitLabel(e){const t=e=>String(Math.round(10*e)/10);return this._t(`junction.limit_${e.rule}`,{actual:t(e.actual),limit:t(e.limit)})}_junctionLimitsIntroduced(e,t,i){let s=[];try{const e=$g(t).config;s=this._junctionLimitViolations(e,i)}catch{return[]}let n=[];try{n=this._junctionLimitViolations(e,i)}catch{return[]}return function(e,t){const i=new Map;for(const e of t||[])i.set(e.rule,(i.get(e.rule)||0)+1);const s=new Map;for(const t of e||[])s.set(t.rule,[...s.get(t.rule)||[],t]);const n=[];for(const[e,t]of s){const s=t.length-(i.get(e)||0);s>0&&n.push(...t.slice(0,s))}return n}(n,s)}_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||Ng(t)===Ng(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 Qm("invalid-room",t.spaceId);const i=$g(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 Qm("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,Eh,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}=bg(i,fg(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;oZm||hg(r,i,s)>Zm)continue;const l=cg(a,i,s),c=cg(r,i,s);l<-Zm||c>1+Zm||c<=l+Zm||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]/Pv,e[1]/Pv]):[];s.push(...i.map(e=>[e[0],e[1]])),c=lg(e)<=lg(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;const h=JSON.parse(JSON.stringify(s));this._restoreGeometryStateInConfig(h,t);const d=this._junctionLimitsIntroduced(a,h,t.spaceId);if(d.length)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(t),this._showToast(this._junctionLimitLabel(d[0])),!1;Mg(s,a),this._recordGeometry(e,l);const p=s.spaces.find(e=>e.id===t.spaceId),u=this._pendingPhysicalWrites.get(t.spaceId);return this._pendingPhysicalWrites.set(t.spaceId,{before:u?.before||l,fingerprint:Ng(p)}),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=Ng(i)!==Ng(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=$g(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;Mg(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:Ng(a)})}else Mg(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>Av)||(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 b=this._geometrySnapshot(),y=this._activeDraftId||e.draft.id,w={...o||a,id:y,points:g.map(e=>[e[0]/Pv,e[1]/Pv]),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=y,this._resumeDraftBySpace[this._space]=y,this._path=g,this._draftSegmentCms=this._adoptDraftCms(g,f.map(e=>Number(e.cm)),y),this._physicalSel=null,this._commitPhysicalGeometry(this._t("history.draft_merge"),b),null!=d&&this._offerWallFaces(i,i.length-1,s)}_adoptDraftCms(e,t,i){const s=Math.max(0,e.length-1),n=ku(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])*Pv,Number(e?.[1])*Pv]):[],n=Array.isArray(t?.segments)?t.segments:[];return ku(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]/Pv,e[1]/Pv]),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=Uu(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=s_(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=Gu(this._wallFaceGraph(this._wallGraphSources([]),t),e,t);if(!s){const n=s_(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=s_(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(Xc,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:Qc(i),angle:0},o=(s.wall_columns||[]).find(e=>yh(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]/Pv,t[1]/Pv]}),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:yl(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:yl(i.cm,this._imperial),shape:i.shape,angle:this._angleField("square"===i.shape?eh(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:yl(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(),Ev(e);const n=this._svgPoint(e),o=[];for(const e of[...s.wall_columns].reverse())vh(n,ah(e,this._cellCm,this._gridPitch))&&o.push({kind:"column",id:e.id});for(const e of[...s.partitions].reverse()){const t=oh(e.a,e.b,e.cm,this._cellCm,this._gridPitch);t&&vh(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(-Ah*Pv-Math.min(...n),Math.min(Ah*Pv-Math.max(...n),i[0])),Math.max(-Ah*Pv-Math.min(...o),Math.min(Ah*Pv-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]/Pv,n.a[1]/Pv],e.b=[n.b[0]/Pv,n.b[1]/Pv];for(const e of s.openings||[]){if("partition"!==e.host?.kind||e.host.id!==t.id)continue;const i=Jp(e,[n],Pv,this._cellCm,this._gridPitch).resolved;i&&Object.assign(e,su(e,i,Pv))}}}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&&yh(e,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));e&&(e.center=[(n.center[0]+t.delta[0])/Pv,(n.center[1]+t.delta[1])/Pv])}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(),Ev(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:eh(t.angle),angle:eh(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=eh(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&&yh(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]*Pv,e.a[1]*this._spaceH],b:[e.b[0]*Pv,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=Na(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(qa(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])),b=[];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"};b.push(r,l)}const y={roomId:i,edge:s,a:[...r],b:[...l],n:Ia(a.poly,s),roomIds:g,edgeByRoom:m,topology:v,movingOpeningIds:f,sideOwnership:b};if(!Qa(e,t,y,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,y,i,n))){if(!i.some(i=>Ya(Ja(e,t,y,i),y,o)))return{enabled:!1,reason:"partial-shared"};const s={...n,obstacles:[]};return i.some(i=>Qa(e,t,y,i,s))?{enabled:!1,reason:"duplicate-physical-wall"}:t.length&&i.some(t=>Qa(e,[],y,t,n))?{enabled:!1,reason:"opening-conflict"}:{enabled:!1,reason:"invalid-geometry"}}}return{enabled:!0,plan:y}}(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){this._rszLimitViolation=null;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]/Pv,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]/Pv,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]*Pv,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]/Pv,s[e][1]/Pv],n.b=[s[(e+1)%s.length][0]/Pv,s[(e+1)%s.length][1]/Pv])}}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=El(e,t,i,s,n,o,()=>{a=!0});return{walls:a?(e||[]).map(e=>({...e})):r,rejected:a}}(r.walls,c,h,this._wallKeyPitch,Pv,"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,Pv,a.walls||[]).length)return{ok:!1,reason:"wall-metadata"};const m=this._rszSpaceCandidateGeometry(this._space,r);if(!m.ok)return{ok:!1,reason:"physical-geometry"};const g={...this._serverCfg,spaces:(this._serverCfg?.spaces||[]).map(e=>e?.id===this._space?r:e)},f=this._junctionLimitsIntroduced(g,this._serverCfg,this._space);return f.length?(this._rszLimitViolation=f[0],{ok:!1,reason:"junction-limit"}):{ok:!0,value:{preview:{space:this._space,sp:r},beforeWalls:a.walls||[],afterWalls:r.walls||[],artifact:m.wallGeometry}}}_rszAcceptPreview(e,t){if(this._cfgEpoch++,!e||!t)return;const i=Lc(t);if(!i)return;const s=`${this._space}|${this._cfgEpoch}|${e.sp.rooms.length}`;Object.defineProperty(i,"sourceFingerprint",{value:Bd([e.sp,this._cellCm,this._gridPitch]),enumerable:!1});const n={key:s,value:i};fv(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));Ev(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._rszLimitViolation?`${this._t("resize.limit_stopped")} — `+this._junctionLimitLabel(this._rszLimitViolation):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};fv(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,b=f?ed(v>8?2.5:v,f,u.w,this._kiosk?this._kioskScale.icon:1)*g.stageWidth/100:24,y=Math.max(10,.77*b),w=this._t("room.settings_short"),k=y*(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?mh(_h(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:y});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 W`
@@ -5887,7 +5887,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 ${this._settingsDialog.bgColor?q``:q`${this._t("gs.bg_theme")}`}
`:q`
${this._t("gs.bg_daynight_hint")}
`} - ${Rr(this.hass)?G:q`
${this._t("gs.sun_missing")}
`} + ${Er(this.hass)?G:q`
${this._t("gs.sun_missing")}
`}
- `}_saveKioskScale(e){this._kioskScale={...this._kioskScale,...e};try{localStorage.setItem(Mv,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const e=this._kioskScale,t=(t,i)=>q` + `}_saveKioskScale(e){this._kioskScale={...this._kioskScale,...e};try{localStorage.setItem(Iv,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const e=this._kioskScale,t=(t,i)=>q`
${this._rangeInput(50,300,5,Math.round(100*e[t]),e=>this._saveKioskScale({[t]:e/100}))} ${Math.round(100*e[t])}% @@ -6053,7 +6053,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 ${this._spaceDialog?this._renderSpaceDialog():G} ${this._importDialog?this._renderImportDialog():G} ${this._toast?q``: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){mf||wf(e);const t=_f,i=t.active.join(",");if(!i||i===ff)return;ff=i;const s=new Map(of.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}`)}(cv);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,b=R_(v),y=this._viewOr(this._baseVb()),w=this._floorView(y),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),N=I?.stageColor||k,E=I?.sceneBrightness??1;return q` + `;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){yf||Mf(e);const t=bf,i=t.active.join(",");if(!i||i===kf)return;kf=i;const s=new Map(hf.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}`)}(_v);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,b=E_(v),y=this._viewOr(this._baseVb()),w=this._floorView(y),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),N=I?.stageColor||k,E=I?.sceneBrightness??1;return q` this._markupClick(e)} @wheel=${e=>this._onWheel(e)} @pointerdown=${e=>{this._notePointer(e),this._stagePointerDown(e)}} @@ -6148,7 +6148,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 @pointerleave=${e=>this._stagePointerLeave(e)} @pointerup=${e=>this._stagePointerUp(e)} @pointercancel=${e=>this._stagePointerCancel(e)}> - ${Fr(R,A)} + ${Lr(R,A)} ${this._renderEditorSecondary()}
+ transform=${p?.structural?If():G}> ${""} ${this._wallHatchDefs(d.color)}${W`${this._paperShapes(s.rooms).map(e=>"path"in e?W``:"poly"in e?W``:W``)}`} ${this._editing?this._renderMarkupDefs(o):G} @@ -6250,7 +6250,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 ${this._renderIsoWalls(p,u)} `:G} ${""} -
+
${c.map(e=>this._renderDevice(e,y,g))} ${this._renderVacuums(c,y)} ${this._renderVacFit(y)} @@ -6362,7 +6362,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 `:G} ${this._toast?q``: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=op(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=op(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=k_(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:S_(this._planHass,e,i)}}_stampActivity(e,t,i){let s=this._activityRt.get(e);s||(s=om(i||"",[]),this._activityRt.set(e,s)),null!=i&&(s.sources=i),rm(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&&am(r,a,o,window.clearTimeout.bind(window)):(r=om(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=lm(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=cp(this.hass.states[i||""]?.state),n=np(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"!==_p(e.marker?.vacuum)&&!n?.path.length;!s&&o.moving&&(o.endedTs=Date.now(),a&&o.lastPos&&(o.trail=lp(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=lp(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?np(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(ap).filter(Boolean)),s=new Set;for(const t of e){const e=ap(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=>q` + `}_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=hp(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=hp(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=C_(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:T_(this._planHass,e,i)}}_stampActivity(e,t,i){let s=this._activityRt.get(e);s||(s=hm(i||"",[]),this._activityRt.set(e,s)),null!=i&&(s.sources=i),pm(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&&dm(r,a,o,window.clearTimeout.bind(window)):(r=hm(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=um(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=_p(this.hass.states[i||""]?.state),n=cp(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"!==bp(e.marker?.vacuum)&&!n?.path.length;!s&&o.moving&&(o.endedTs=Date.now(),a&&o.lastPos&&(o.trail=up(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=up(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?cp(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(dp).filter(Boolean)),s=new Set;for(const t of e){const e=dp(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=>q`
`}_vacMapId(e,t,i=this._planHass){const s=this._vacEntity(e),n=s?i?.states?.[s]?.attributes?.selected_map:null;return o=t.mapId,a=n,"default"!==o?o:null!=a?String(a):"default";var o,a}_vacSaveMatrix(e,t,i,s){const n=this._devices.find(t=>t.id===e),o=n?this._vacEnsureMarker(n):this._serverCfg?.markers?.find(t=>t.id===e);if(!o)return!1;const a={...o.vacuum||{}};return a.source=t,a.calibration={...a.calibration||{},[i]:s.map(e=>Number(e.toFixed(6)))},o.vacuum=a,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_vacPlanRoomAnchors(e){return(this._spaceModelById(e)?.rooms||[]).map(e=>({room:e,poly:hi(e)})).filter(({room:e,poly:t})=>e.name&&t).map(({room:e,poly:t})=>{const i=Qd(t);return i?{name:String(e.name),cx:i[0],cy:i[1]}:null}).filter(Boolean)}_vacAutoCalibrate(e){const t=this._vacSource(e),i=t?np(this.hass?.states[t]?.attributes):null;if(!t||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._vacPlanRoomAnchors(e.space),n=rp(i.rooms,s);if(!n)return void this._showToast(this._t("vac.autocal_no_match"));const o=this._serverCfg?.spaces?.find(t=>t.id===e.space),a=Number(o?.cell_cm),r=Number.isFinite(a)&&a>0?a:5,l=function(e,t,i){return![e,t,i].every(Number.isFinite)||e<0||t<=0||i<=0?Number.POSITIVE_INFINITY:e/t*i}(n.residual,this._gridPitch,r),c=this._vacMapId(e,i);l>40?this._vacCalConfirm={markerId:e.id,source:t,mapId:c,matrix:n.matrix,rooms:n.matched.length,error:li(l,"mi"===this.hass?.config?.unit_system?.length)}:this._vacSaveMatrix(e.id,t,c,n.matrix)&&this._showToast(ss(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))}_vacApplyCalibrationProposal(e){const t=this._vacCalConfirm;if(t){if(this._vacCalConfirm=null,e){const e=this._devices.find(e=>e.id===t.markerId),i=pp(t.matrix);if(!e||!i)return;if(this._markerDialog=null,e.space!==this._space&&!this._commitSpace(e.space))return;return void(this._vacFit={markerId:t.markerId,source:t.source,mapId:t.mapId,p:i,drag:null})}this._vacSaveMatrix(t.markerId,t.source,t.mapId,t.matrix)&&this._showToast(ss(this._t("vac.autocal_done"),{rooms:String(t.rooms)}))}}_vacStartFit(e){const t=this._vacSource(e),i=t?np(this.hass?.states[t]?.attributes):null;if(!t||!i)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._vacMapId(e,i),n=e.marker?.vacuum?.calibration?.[s],o=this._spaceModelById(e.space);if(!o)return;const a=o.vb,r=n&&6===n.length&&pp(n)||function(e,t){const i=[],s=[];for(const t of e)null!=t.x0?(i.push(t.x0,t.x1),s.push(t.y0,t.y1)):(i.push(t.cx),s.push(t.cy));if(!i.length)return{ox:t[0]+t[2]/2,oy:t[1]+t[3]/2,s:t[2]/1e4,rot:0,mir:!0};const n=Math.min(...i),o=Math.max(...i),a=Math.min(...s),r=Math.max(...s),l=Math.max(o-n,r-a)||1,c={ox:0,oy:0,s:.6*Math.min(t[2],t[3])/l,rot:0,mir:!0},h=dp(c),[d,p]=Yd(h,(n+o)/2,(a+r)/2);return c.ox=t[0]+t[2]/2-d,c.oy=t[1]+t[3]/2-p,c}(i.rooms,a);this._markerDialog=null,(e.space===this._space||this._commitSpace(e.space))&&(this._vacFit={markerId:e.id,source:t,mapId:s,p:r,drag:null})}_vacFitSave(){const e=this._vacFit;if(!e)return;const t=this._vacSaveMatrix(e.markerId,e.source,e.mapId,dp(e.p));this._vacFit=null,t&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(e){const t=this._vacFit;if(!t)return;const i=np(this.hass?.states[t.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),n={...t.p,...e};this._vacFit={...t,p:up(n,t.p,s[0],s[1])}}_vacGhostCentre(e){const t=[],i=[];for(const s of e)t.push(s.x0??s.cx,s.x1??s.cx),i.push(s.y0??s.cy,s.y1??s.cy);return t.length?[(Math.min(...t)+Math.max(...t))/2,(Math.min(...i)+Math.max(...i))/2]:[0,0]}_vacDelta(e,t,i){const s=this._stageEl,n=s?.clientWidth||1,o=s?.clientHeight||1;return[t/n*e.w,i/o*e.h]}_vacFitPointer(e,t){const i=this._vacFit;if(!i)return;if(e.stopPropagation(),"pointerdown"===e.type){const t=e.target,s=t.getAttribute?.("data-corner");try{e.currentTarget.setPointerCapture?.(e.pointerId)}catch{}return void(this._vacFit={...i,drag:s?{kind:"scale",sx:e.clientX,sy:e.clientY,p0:{...i.p},fx:Number(s.split(",")[0]),fy:Number(s.split(",")[1])}:{kind:"move",sx:e.clientX,sy:e.clientY,p0:{...i.p},fx:0,fy:0}})}const s=i.drag;if(s){if("pointermove"===e.type){const[n,o]=this._vacDelta(t,e.clientX-s.sx,e.clientY-s.sy);if("move"===s.kind)this._vacFit={...i,p:{...s.p0,ox:s.p0.ox+n,oy:s.p0.oy+o}};else{const e=np(this.hass?.states[i.source]?.attributes),t=this._vacGhostCentre(e?.rooms||[]),a=dp(s.p0),[r,l]=Yd(a,t[0],t[1]),[c,h]=Yd(a,s.fx,s.fy),d=Math.hypot(r-c,l-h)||1,[p,u]=[2*r-c,2*l-h],_=Math.hypot(p+2*n-c,u+2*o-h)/2,m=Math.max(.05,_/d),g={...s.p0,s:s.p0.s*m};this._vacFit={...i,p:up(g,s.p0,s.fx,s.fy)}}return}"pointerup"!==e.type&&"pointercancel"!==e.type||(this._vacFit={...i,drag:null})}}_renderVacFit(e){const t=this._vacFit;if(!t)return G;const i=np(this._renderPlanHass?.states?.[t.source]?.attributes);if(!i)return G;const s=dp(t.p),n=[],o=[],a=[];for(const e of i.rooms){if(null==e.x0)continue;const t=[[e.x0,e.y0],[e.x1,e.y0],[e.x1,e.y1],[e.x0,e.y1]].map(([e,t])=>Yd(s,e,t));t.forEach(([e,t])=>{o.push(e),a.push(t)});const[i,r]=Yd(s,e.cx,e.cy);n.push(W` - ${e.name}`)}let r=G;if(i.pos){const[t,n]=Yd(s,i.pos.x,i.pos.y);r=W``}const l=[];if(o.length){const t=(()=>{const e=s[0]*s[4]-s[1]*s[3];return(t,i)=>[(s[4]*(t-s[2])-s[1]*(i-s[5]))/e,(-s[3]*(t-s[2])+s[0]*(i-s[5]))/e]})(),i=Math.min(...o),n=Math.max(...o),r=Math.min(...a),c=Math.max(...a),h=.022*e.w,d=h/4;for(const[e,s,o,a]of[[i,r,n,c],[n,r,i,c],[n,c,i,r],[i,c,n,r]]){const i=t(o,a);l.push(W``}_vacMapId(e,t,i=this._planHass){const s=this._vacEntity(e),n=s?i?.states?.[s]?.attributes?.selected_map:null;return o=t.mapId,a=n,"default"!==o?o:null!=a?String(a):"default";var o,a}_vacSaveMatrix(e,t,i,s){const n=this._devices.find(t=>t.id===e),o=n?this._vacEnsureMarker(n):this._serverCfg?.markers?.find(t=>t.id===e);if(!o)return!1;const a={...o.vacuum||{}};return a.source=t,a.calibration={...a.calibration||{},[i]:s.map(e=>Number(e.toFixed(6)))},o.vacuum=a,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_vacPlanRoomAnchors(e){return(this._spaceModelById(e)?.rooms||[]).map(e=>({room:e,poly:hi(e)})).filter(({room:e,poly:t})=>e.name&&t).map(({room:e,poly:t})=>{const i=np(t);return i?{name:String(e.name),cx:i[0],cy:i[1]}:null}).filter(Boolean)}_vacAutoCalibrate(e){const t=this._vacSource(e),i=t?cp(this.hass?.states[t]?.attributes):null;if(!t||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._vacPlanRoomAnchors(e.space),n=pp(i.rooms,s);if(!n)return void this._showToast(this._t("vac.autocal_no_match"));const o=this._serverCfg?.spaces?.find(t=>t.id===e.space),a=Number(o?.cell_cm),r=Number.isFinite(a)&&a>0?a:5,l=function(e,t,i){return![e,t,i].every(Number.isFinite)||e<0||t<=0||i<=0?Number.POSITIVE_INFINITY:e/t*i}(n.residual,this._gridPitch,r),c=this._vacMapId(e,i);l>40?this._vacCalConfirm={markerId:e.id,source:t,mapId:c,matrix:n.matrix,rooms:n.matched.length,error:li(l,"mi"===this.hass?.config?.unit_system?.length)}:this._vacSaveMatrix(e.id,t,c,n.matrix)&&this._showToast(ss(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))}_vacApplyCalibrationProposal(e){const t=this._vacCalConfirm;if(t){if(this._vacCalConfirm=null,e){const e=this._devices.find(e=>e.id===t.markerId),i=fp(t.matrix);if(!e||!i)return;if(this._markerDialog=null,e.space!==this._space&&!this._commitSpace(e.space))return;return void(this._vacFit={markerId:t.markerId,source:t.source,mapId:t.mapId,p:i,drag:null})}this._vacSaveMatrix(t.markerId,t.source,t.mapId,t.matrix)&&this._showToast(ss(this._t("vac.autocal_done"),{rooms:String(t.rooms)}))}}_vacStartFit(e){const t=this._vacSource(e),i=t?cp(this.hass?.states[t]?.attributes):null;if(!t||!i)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._vacMapId(e,i),n=e.marker?.vacuum?.calibration?.[s],o=this._spaceModelById(e.space);if(!o)return;const a=o.vb,r=n&&6===n.length&&fp(n)||function(e,t){const i=[],s=[];for(const t of e)null!=t.x0?(i.push(t.x0,t.x1),s.push(t.y0,t.y1)):(i.push(t.cx),s.push(t.cy));if(!i.length)return{ox:t[0]+t[2]/2,oy:t[1]+t[3]/2,s:t[2]/1e4,rot:0,mir:!0};const n=Math.min(...i),o=Math.max(...i),a=Math.min(...s),r=Math.max(...s),l=Math.max(o-n,r-a)||1,c={ox:0,oy:0,s:.6*Math.min(t[2],t[3])/l,rot:0,mir:!0},h=gp(c),[d,p]=ep(h,(n+o)/2,(a+r)/2);return c.ox=t[0]+t[2]/2-d,c.oy=t[1]+t[3]/2-p,c}(i.rooms,a);this._markerDialog=null,(e.space===this._space||this._commitSpace(e.space))&&(this._vacFit={markerId:e.id,source:t,mapId:s,p:r,drag:null})}_vacFitSave(){const e=this._vacFit;if(!e)return;const t=this._vacSaveMatrix(e.markerId,e.source,e.mapId,gp(e.p));this._vacFit=null,t&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(e){const t=this._vacFit;if(!t)return;const i=cp(this.hass?.states[t.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),n={...t.p,...e};this._vacFit={...t,p:vp(n,t.p,s[0],s[1])}}_vacGhostCentre(e){const t=[],i=[];for(const s of e)t.push(s.x0??s.cx,s.x1??s.cx),i.push(s.y0??s.cy,s.y1??s.cy);return t.length?[(Math.min(...t)+Math.max(...t))/2,(Math.min(...i)+Math.max(...i))/2]:[0,0]}_vacDelta(e,t,i){const s=this._stageEl,n=s?.clientWidth||1,o=s?.clientHeight||1;return[t/n*e.w,i/o*e.h]}_vacFitPointer(e,t){const i=this._vacFit;if(!i)return;if(e.stopPropagation(),"pointerdown"===e.type){const t=e.target,s=t.getAttribute?.("data-corner");try{e.currentTarget.setPointerCapture?.(e.pointerId)}catch{}return void(this._vacFit={...i,drag:s?{kind:"scale",sx:e.clientX,sy:e.clientY,p0:{...i.p},fx:Number(s.split(",")[0]),fy:Number(s.split(",")[1])}:{kind:"move",sx:e.clientX,sy:e.clientY,p0:{...i.p},fx:0,fy:0}})}const s=i.drag;if(s){if("pointermove"===e.type){const[n,o]=this._vacDelta(t,e.clientX-s.sx,e.clientY-s.sy);if("move"===s.kind)this._vacFit={...i,p:{...s.p0,ox:s.p0.ox+n,oy:s.p0.oy+o}};else{const e=cp(this.hass?.states[i.source]?.attributes),t=this._vacGhostCentre(e?.rooms||[]),a=gp(s.p0),[r,l]=ep(a,t[0],t[1]),[c,h]=ep(a,s.fx,s.fy),d=Math.hypot(r-c,l-h)||1,[p,u]=[2*r-c,2*l-h],_=Math.hypot(p+2*n-c,u+2*o-h)/2,m=Math.max(.05,_/d),g={...s.p0,s:s.p0.s*m};this._vacFit={...i,p:vp(g,s.p0,s.fx,s.fy)}}return}"pointerup"!==e.type&&"pointercancel"!==e.type||(this._vacFit={...i,drag:null})}}_renderVacFit(e){const t=this._vacFit;if(!t)return G;const i=cp(this._renderPlanHass?.states?.[t.source]?.attributes);if(!i)return G;const s=gp(t.p),n=[],o=[],a=[];for(const e of i.rooms){if(null==e.x0)continue;const t=[[e.x0,e.y0],[e.x1,e.y0],[e.x1,e.y1],[e.x0,e.y1]].map(([e,t])=>ep(s,e,t));t.forEach(([e,t])=>{o.push(e),a.push(t)});const[i,r]=ep(s,e.cx,e.cy);n.push(W` + ${e.name}`)}let r=G;if(i.pos){const[t,n]=ep(s,i.pos.x,i.pos.y);r=W``}const l=[];if(o.length){const t=(()=>{const e=s[0]*s[4]-s[1]*s[3];return(t,i)=>[(s[4]*(t-s[2])-s[1]*(i-s[5]))/e,(-s[3]*(t-s[2])+s[0]*(i-s[5]))/e]})(),i=Math.min(...o),n=Math.max(...o),r=Math.min(...a),c=Math.max(...a),h=.022*e.w,d=h/4;for(const[e,s,o,a]of[[i,r,n,c],[n,r,i,c],[n,c,i,r],[i,c,n,r]]){const i=t(o,a);l.push(W` `)}}return q`this._vacFitPointer(t,e)} @pointermove=${t=>this._vacFitPointer(t,e)} @pointerup=${t=>this._vacFitPointer(t,e)} - @pointercancel=${t=>this._vacFitPointer(t,e)}>${n}${r}${l}`}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const e=this.renderRoot,t=this._stageEl,i=this._vacLastView,s=e?.querySelectorAll?.(".vacpuck")||[];if(!t||!i||!s.length)return void(this._vacRaf=0);const n=t.getBoundingClientRect();for(const t of s){const s=t.getAttribute("data-mid"),o=t.getBoundingClientRect(),a=i.x+(o.left+o.width/2-n.left)/n.width*i.w,r=i.y+(o.top+o.height/2-n.top)/n.height*i.h;for(const t of e.querySelectorAll(`line.tip[data-mid="${s}"]`))t.setAttribute("x2",a.toFixed(1)),t.setAttribute("y2",r.toFixed(1))}this._vacRafLoop()})}_renderVacuums(e,t){if(this._markup||"decor"===this._mode)return G;const i=this._space+"|"+t.x+"|"+t.y+"|"+t.w+"|"+t.h,s=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const n=[],o=[];for(const i of e){if(i.hidden||!this._isVacDev(i))continue;if("static_icon"===Bi(i.marker?.display))continue;const e=this._renderDeviceSnapshot?.facts.get(`vacuum:${i.id}`),a=e?.source??this._vacSource(i,this._renderPlanHass);if(!a)continue;const r=e?.telemetry??np(this._renderPlanHass?.states[a]?.attributes);if(!r)continue;const l=String(e?.mapId??this._vacMapId(i,r,this._renderPlanHass)),c=i.marker?.vacuum?.calibration?.[l];if(!c||6!==c.length)continue;const h=e?.runtime??this._vacRt.get(i.id),d=h?.moving??!1,p=_p(i.marker?.vacuum),u="always"===p||"cleaning"===p&&d,_=e?.server??this._vacSrvTrails[i.id],m=_?.current?.map_id===l&&Array.isArray(_.current.points)?_.current:null,g=_?.previous?.map_id===l&&Array.isArray(_.previous.points)?_.previous:null;if("always"===p&&g&&g.points.length>1){const e=g.points.map(([e,t])=>{const[i,s]=Yd(c,e,t),n=this._scenePoint([i,s]);return n[0].toFixed(1)+","+n[1].toFixed(1)}).join(" ");o.push(W``)}if(u){const e=ip(r,m,h?.trail||[]),t=!d||"integration"!==e.source&&"server"!==e.source?e.path:sp(e.path);if(t.length){const e=t.map(e=>e.map(([e,t],i)=>{const[s,n]=Yd(c,e,t),o=this._scenePoint([s,n]);return`${i?"L":"M"} ${o[0].toFixed(1)} ${o[1].toFixed(1)}`}).join(" ")).join(" ");o.push(W``);const s=t[t.length-1];if(d&&s?.length>=2){const e=s[s.length-1],[t,n]=Yd(c,e[0],e[1]),a=this._scenePoint([t,n]),r=a[0].toFixed(1),l=a[1].toFixed(1);o.push(W``)}}}if(!d||!r.pos)continue;const[f,v]=Yd(c,r.pos.x,r.pos.y),b=this._scenePoint([f,v]),y=(b[0]-t.x)/t.w*100,w=(b[1]-t.y)/t.h*100,k=h&&h.lastTs>0&&Date.now()-h.lastTs>6e4,x=i.marker?.icon||i.icon||"mdi:robot-vacuum";n.push(q`
this._vacFitPointer(t,e)}>${n}${r}${l}`}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const e=this.renderRoot,t=this._stageEl,i=this._vacLastView,s=e?.querySelectorAll?.(".vacpuck")||[];if(!t||!i||!s.length)return void(this._vacRaf=0);const n=t.getBoundingClientRect();for(const t of s){const s=t.getAttribute("data-mid"),o=t.getBoundingClientRect(),a=i.x+(o.left+o.width/2-n.left)/n.width*i.w,r=i.y+(o.top+o.height/2-n.top)/n.height*i.h;for(const t of e.querySelectorAll(`line.tip[data-mid="${s}"]`))t.setAttribute("x2",a.toFixed(1)),t.setAttribute("y2",r.toFixed(1))}this._vacRafLoop()})}_renderVacuums(e,t){if(this._markup||"decor"===this._mode)return G;const i=this._space+"|"+t.x+"|"+t.y+"|"+t.w+"|"+t.h,s=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const n=[],o=[];for(const i of e){if(i.hidden||!this._isVacDev(i))continue;if("static_icon"===Bi(i.marker?.display))continue;const e=this._renderDeviceSnapshot?.facts.get(`vacuum:${i.id}`),a=e?.source??this._vacSource(i,this._renderPlanHass);if(!a)continue;const r=e?.telemetry??cp(this._renderPlanHass?.states[a]?.attributes);if(!r)continue;const l=String(e?.mapId??this._vacMapId(i,r,this._renderPlanHass)),c=i.marker?.vacuum?.calibration?.[l];if(!c||6!==c.length)continue;const h=e?.runtime??this._vacRt.get(i.id),d=h?.moving??!1,p=bp(i.marker?.vacuum),u="always"===p||"cleaning"===p&&d,_=e?.server??this._vacSrvTrails[i.id],m=_?.current?.map_id===l&&Array.isArray(_.current.points)?_.current:null,g=_?.previous?.map_id===l&&Array.isArray(_.previous.points)?_.previous:null;if("always"===p&&g&&g.points.length>1){const e=g.points.map(([e,t])=>{const[i,s]=ep(c,e,t),n=this._scenePoint([i,s]);return n[0].toFixed(1)+","+n[1].toFixed(1)}).join(" ");o.push(W``)}if(u){const e=rp(r,m,h?.trail||[]),t=!d||"integration"!==e.source&&"server"!==e.source?e.path:lp(e.path);if(t.length){const e=t.map(e=>e.map(([e,t],i)=>{const[s,n]=ep(c,e,t),o=this._scenePoint([s,n]);return`${i?"L":"M"} ${o[0].toFixed(1)} ${o[1].toFixed(1)}`}).join(" ")).join(" ");o.push(W``);const s=t[t.length-1];if(d&&s?.length>=2){const e=s[s.length-1],[t,n]=ep(c,e[0],e[1]),a=this._scenePoint([t,n]),r=a[0].toFixed(1),l=a[1].toFixed(1);o.push(W``)}}}if(!d||!r.pos)continue;const[f,v]=ep(c,r.pos.x,r.pos.y),b=this._scenePoint([f,v]),y=(b[0]-t.x)/t.w*100,w=(b[1]-t.y)/t.h*100,k=h&&h.lastTs>0&&Date.now()-h.lastTs>6e4,x=i.marker?.icon||i.icon||"mdi:robot-vacuum";n.push(q`
${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=g_(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 q`
`}_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=yo(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=Ns(e.settings?.name_scale),this._roomLabelScale=Ns(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*Pv,y:i.y*Pv};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},Ev(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=Lh(this._drag.ox+c),p=Lh(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},Ev(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/Pv,y:o.y/Pv,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 q`
`}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=Es(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 q`
- ${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=Es(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=Pv,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 W``;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=Es(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} · ${rr(Gr(Math.abs(s-t),this._cellCm,this._gridPitch)*Gr(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=zv,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 W``;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 W` `})} - `}_renderOpeningCenterTick(e){const t=(e.angle+90)*Math.PI/180,i=xh(15,this._cellCm);return W``})} + `}_renderOpeningCenterTick(e){const t=(e.angle+90)*Math.PI/180,i=Dh(15,this._cellCm);return W``}_renderOpeningDimensionGuides(e){const t=e.labels.flatMap(e=>e.dimension?[e.dimension]:[]);if(!t.length)return W``;const i=this._cssPxToRender(4);return W``}_roomCenter(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}_openingAmt(e){const t=e.contact&&this._renderOpeningEntityAvailable(e.contact)?this._renderPlanHass.states[e.contact]?.state:null;return mi(e.type,t,!!e.invert)}_planEntityAvailable(e){return!!e&&(!ko(this._fullRegistryHass,e,wo(this._markers))&&"active"===this._bindingStatus("entity:"+e).kind)}_renderEntityAvailable(e){return!!e&&(!ko(this._renderPlanHass,e,wo(this._markers))&&(!!this._renderPlanHass.entities?.[e]&&!!this._renderPlanHass.states?.[e]))}_openingEntityAvailable(e){return function(e,t,i=An(e)){return!!t&&"active"===Bn(e,`entity:${t}`,i).kind}(this.hass,e,this._haRegistry)}_renderOpeningEntityAvailable(e){return t=this._renderPlanHass,!!(i=e)&&!!t?.states?.[i];var t,i}_renderOpeningPlacementPreview(){const e=this._openingPreview;if(!e)return W``;const t={type:e.type,length:e.renderedLength,angle:e.angle,amount:mi(e.type,null),flipH:e.flipH,flipV:e.flipV,base:"var(--hp-open)",tone:"var(--hp-open)",cellCm:this._cellCm,gridPitch:this._gridPitch,face:e.face},i="passage"===e.type?function(e,t){const i=Math.max(0,e.renderedLength)/2,s=Math.max(0,e.target.physicalHalfWidth),n=s+.18*Math.max(0,t),o=e=>({x1:e,y1:-n,x2:e,y2:n});return{rect:{x:-i,y:s?-s:0,width:2*i,height:2*s},boundaries:[o(-i),o(i)]}}(e,xh(this._gridPitch,this._cellCm)):null;return W``}_roomCenter(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}_openingAmt(e){const t=e.contact&&this._renderOpeningEntityAvailable(e.contact)?this._renderPlanHass.states[e.contact]?.state:null;return mi(e.type,t,!!e.invert)}_planEntityAvailable(e){return!!e&&(!ko(this._fullRegistryHass,e,wo(this._markers))&&"active"===this._bindingStatus("entity:"+e).kind)}_renderEntityAvailable(e){return!!e&&(!ko(this._renderPlanHass,e,wo(this._markers))&&(!!this._renderPlanHass.entities?.[e]&&!!this._renderPlanHass.states?.[e]))}_openingEntityAvailable(e){return function(e,t,i=An(e)){return!!t&&"active"===Bn(e,`entity:${t}`,i).kind}(this.hass,e,this._haRegistry)}_renderOpeningEntityAvailable(e){return t=this._renderPlanHass,!!(i=e)&&!!t?.states?.[i];var t,i}_renderOpeningPlacementPreview(){const e=this._openingPreview;if(!e)return W``;const t={type:e.type,length:e.renderedLength,angle:e.angle,amount:mi(e.type,null),flipH:e.flipH,flipV:e.flipV,base:"var(--hp-open)",tone:"var(--hp-open)",cellCm:this._cellCm,gridPitch:this._gridPitch,face:e.face},i="passage"===e.type?function(e,t){const i=Math.max(0,e.renderedLength)/2,s=Math.max(0,e.target.physicalHalfWidth),n=s+.18*Math.max(0,t),o=e=>({x1:e,y1:-n,x2:e,y2:n});return{rect:{x:-i,y:s?-s:0,width:2*i,height:2*s},boundaries:[o(-i),o(i)]}}(e,Dh(this._gridPitch,this._cellCm)):null;return W` `}_renderOpenings(e){const t=this._openingsR;if(!t.length)return W``;const i=this._spaceModel();if(!i)return W``;const s=e.color,n=this._spaceWalls,o=this._openCuts(),a=this._openingWallIndexFor(i,o).value;return W`${t.map(e=>{if(e.orphanReason)return W``}_renderOpenings(e){const t=this._openingsR;if(!t.length)return W``;const i=this._spaceModel();if(!i)return W``;const s=e.color,n=this._spaceWalls,o=this._openCuts(),a=this._openingWallIndexFor(i,o).value;return W`${t.map(e=>{if(e.orphanReason)return W`{t.stopPropagation(),this._editOpening(e)}}> - + ! - `;const t=this._openingAmt(e),i=t>0&&!!e.contact&&this._renderOpeningEntityAvailable(e.contact)?"var(--hp-open)":s,o="gate"===e.type?!e.flip_v:e.flip_v,r=e.partitionHost||n.length||"gate"===e.type?this._openingFace(e,a,!!o):{ox:0,oy:0,cm:0,side:-1},l={type:e.type,length:e.rlen,angle:e.angle,amount:t,flipH:!!e.flip_h,flipV:!!e.flip_v,base:s,tone:i,cellCm:this._cellCm,gridPitch:this._gridPitch,face:r},{half:c,outlineHalf:h,hitHalf:d}=d_(l),p=xh(10,this._cellCm),u=xh(12,this._cellCm);return W``;const t=this._openingAmt(e),i=t>0&&!!e.contact&&this._renderOpeningEntityAvailable(e.contact)?"var(--hp-open)":s,o="gate"===e.type?!e.flip_v:e.flip_v,r=e.partitionHost||n.length||"gate"===e.type?this._openingFace(e,a,!!o):{ox:0,oy:0,cm:0,side:-1},l={type:e.type,length:e.rlen,angle:e.angle,amount:t,flipH:!!e.flip_h,flipV:!!e.flip_v,base:s,tone:i,cellCm:this._cellCm,gridPitch:this._gridPitch,face:r},{half:c,outlineHalf:h,hitHalf:d}=g_(l),p=Dh(10,this._cellCm),u=Dh(12,this._cellCm);return W` - ${p_(l)} + ${f_(l)} + rx="${Dh(6,this._cellCm)}"> this._opClick(t,e)} @@ -6546,7 +6546,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 @pointermove=${t=>this._opPointerMove(t,e)} @pointerup=${t=>this._opPointerUp(t,e)} @pointercancel=${t=>this._opPointerUp(t,e)}> - `})}`}_renderOpeningLocks(e){const t=this._openingsR.filter(e=>!e.orphanReason&&("door"===e.type||"gate"===e.type)&&e.lock&&this._renderOpeningEntityAvailable(e.lock));if(!t.length)return q``;const i=this._spaceModel();if(!i)return q``;const s=this._openCuts(),n=this._openingWallIndexFor(i,s).value;return q`${t.map(t=>{const i=this._renderPlanHass.states[t.lock]?.state,s="locked"===i,o=s||["unlocked","open","opening","unlocking","locking"].includes(String(i)),a=(t.angle+90)*Math.PI/180,r="gate"===t.type?this._openingFace(t,n,!t.flip_v):null,l=xh(16,this._cellCm),c=r?-l*r.side:l*(t.flip_v?-1:1),h=t.rx+Math.cos(a)*c,d=t.ry+Math.sin(a)*c,p=this._scenePoint([h,d]),u=(p[0]-e.x)/e.w*100,_=(p[1]-e.y)/e.h*100;return q`
`})}`}_renderOpeningLocks(e){const t=this._openingsR.filter(e=>!e.orphanReason&&("door"===e.type||"gate"===e.type)&&e.lock&&this._renderOpeningEntityAvailable(e.lock));if(!t.length)return q``;const i=this._spaceModel();if(!i)return q``;const s=this._openCuts(),n=this._openingWallIndexFor(i,s).value;return q`${t.map(t=>{const i=this._renderPlanHass.states[t.lock]?.state,s="locked"===i,o=s||["unlocked","open","opening","unlocking","locking"].includes(String(i)),a=(t.angle+90)*Math.PI/180,r="gate"===t.type?this._openingFace(t,n,!t.flip_v):null,l=Dh(16,this._cellCm),c=r?-l*r.side:l*(t.flip_v?-1:1),h=t.rx+Math.cos(a)*c,d=t.ry+Math.sin(a)*c,p=this._scenePoint([h,d]),u=(p[0]-e.x)/e.w*100,_=(p[1]-e.y)/e.h*100;return q`
{e.stopPropagation(),"view"===this._mode&&(this._openingInfo=t)}}>
- `}_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/Pv,y:e.y/this._spaceH,angle:e.angle,length:i&&!e.lengthTouched?i.length:this._cmToUnits(Math.max(20,e.lengthCm))/Pv,...e.host?{host:e.host}:{}},n=Zp(i,s),o=e.host?n?Xp(s,t,Pv,this._cellCm,this._gridPitch):Jp(s,t,Pv,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 q` + `}_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/zv,y:e.y/this._spaceH,angle:e.angle,length:i&&!e.lengthTouched?i.length:this._cmToUnits(Math.max(20,e.lengthCm))/zv,...e.host?{host:e.host}:{}},n=su(i,s),o=e.host?n?iu(s,t,zv,this._cellCm,this._gridPitch):tu(s,t,zv,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 q`
`:G} {const i=mv(t.target.value);null!=i&&(this._openingDialog={...e,lengthCm:i,lengthTouched:!0})}} /> + @input=${t=>{const i=yv(t.target.value);null!=i&&(this._openingDialog={...e,lengthCm:i,lengthTouched:!0})}} /> ${"passage"===e.type&&(e.contact||e.lock)?q`
@@ -6662,7 +6662,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26
- `}_gridLevels(){const e=this._stageEl,t=this._viewOr(this._baseVb()),i=e&&e.clientWidth&&t.w?e.clientWidth/t.w:1;return function(e,t,i=7){if(!(e>0&&t>0&&Number.isFinite(t)))return null;const s=td.find(s=>e*s*t>=i);if(void 0===s)return null;const n=td.find(e=>e>=5*s)??5*s;return{fine:s,coarse:n}}(this._gridPitch,i)}_renderMarkupDefs(e){const t=this._gridLevels();if(!t)return W``;const i=this._gridPitch*t.fine,s=this._gridPitch*t.coarse,n=this._gridPitch*t.fine*.14;return W` + `}_gridLevels(){const e=this._stageEl,t=this._viewOr(this._baseVb()),i=e&&e.clientWidth&&t.w?e.clientWidth/t.w:1;return function(e,t,i=7){if(!(e>0&&t>0&&Number.isFinite(t)))return null;const s=ad.find(s=>e*s*t>=i);if(void 0===s)return null;const n=ad.find(e=>e>=5*s)??5*s;return{fine:s,coarse:n}}(this._gridPitch,i)}_renderMarkupDefs(e){const t=this._gridLevels();if(!t)return W``;const i=this._gridPitch*t.fine,s=this._gridPitch*t.coarse,n=this._gridPitch*t.fine*.14;return W` @@ -6676,12 +6676,12 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 - `}_renderPhysicalEditorLayer(){const e=this._spaceModel();if(!e)return W``;const t=this._gridPitch,i=this._viewOr(this._baseVb()),s=this._stageEl,n=s?.clientWidth?i.w/s.clientWidth:t/8,o=Math.max(.22*t,8*n),a=Math.max(o,12*n),r=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`,l=(e,t)=>this._physicalSel?.kind===e&&this._physicalSel.id===t||"column"===e&&this._duplicateColumnId===t,c=(e.room_drafts||[]).flatMap(e=>e.points.slice(0,-1).map((t,i)=>{const s=e.points[i+1],o=Number(e.segments[i]?.cm),a=wl(Number.isFinite(o)?o:15,this._cellCm,this._gridPitch);return W``}_renderPhysicalEditorLayer(){const e=this._spaceModel();if(!e)return W``;const t=this._gridPitch,i=this._viewOr(this._baseVb()),s=this._stageEl,n=s?.clientWidth?i.w/s.clientWidth:t/8,o=Math.max(.22*t,8*n),a=Math.max(o,12*n),r=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`,l=(e,t)=>this._physicalSel?.kind===e&&this._physicalSel.id===t||"column"===e&&this._duplicateColumnId===t,c=(e.room_drafts||[]).flatMap(e=>e.points.slice(0,-1).map((t,i)=>{const s=e.points[i+1],o=Number(e.segments[i]?.cm),a=Ml(Number.isFinite(o)?o:15,this._cellCm,this._gridPitch);return W`{this._physicalSel={kind:"draft",id:e.id,segment:i},this._registerPhysicalTap("draft",e.id,i)}}>`})),h=(e.partitions||[]).map(e=>{const t=oh(e.a,e.b,e.cm,this._cellCm,this._gridPitch);return t?W`{this._physicalSel={kind:"draft",id:e.id,segment:i},this._registerPhysicalTap("draft",e.id,i)}}>`})),h=(e.partitions||[]).map(e=>{const t=hh(e.a,e.b,e.cm,this._cellCm,this._gridPitch);return t?W`this._physicalDown(t,"partition",e.id)} @@ -6692,7 +6692,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 stroke-width=${24} vector-effect="non-scaling-stroke" @pointerdown=${t=>this._physicalDown(t,"partition",e.id)} @pointermove=${e=>this._physicalMove(e)} - @pointerup=${e=>this._physicalUp(e)}>`}),d=(e.wall_columns||[]).map(e=>{const t=ah(this._physicalRotate?.id===e.id?{...e,angle:this._physicalRotate.angle}:e,this._cellCm,this._gridPitch);return W`this._physicalUp(e)}>`}),d=(e.wall_columns||[]).map(e=>{const t=dh(this._physicalRotate?.id===e.id?{...e,angle:this._physicalRotate.angle}:e,this._cellCm,this._gridPitch);return W`this._physicalDown(t,"column",e.id)} @@ -6700,20 +6700,20 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 @pointerup=${e=>this._physicalUp(e)}>`}),p=this._physicalDrag,u=(()=>{if(!p?.moved)return G;if("partition"===p.kind&&0===Number(p.base.cm)){const e=p.base;return W``}const e="partition"===p.kind?oh(p.base.a,p.base.b,p.base.cm,this._cellCm,this._gridPitch):ah(p.base,this._cellCm,this._gridPitch);return e?W``}const e="partition"===p.kind?hh(p.base.a,p.base.b,p.base.cm,this._cellCm,this._gridPitch):dh(p.base,this._cellCm,this._gridPitch);return e?W``:G})(),_=(()=>{const i=this._physicalSel;if(!i)return G;if("draft"===i.kind){const t=e.room_drafts.find(e=>e.id===i.id);return t?W` e.join(",")).join(" ")}> ${t.points.map(e=>W``)} - `:G}if("partition"===i.kind){const t=e.partitions.find(e=>e.id===i.id);if(!t)return G;const s=oh(t.a,t.b,t.cm,this._cellCm,this._gridPitch),n=(t.a[0]+t.b[0])/2,a=(t.a[1]+t.b[1])/2;return W` + `:G}if("partition"===i.kind){const t=e.partitions.find(e=>e.id===i.id);if(!t)return G;const s=hh(t.a,t.b,t.cm,this._cellCm,this._gridPitch),n=(t.a[0]+t.b[0])/2,a=(t.a[1]+t.b[1])/2;return W` ${s?W``:W``} - `}const s=e.wall_columns.find(e=>e.id===i.id);if(!s)return G;const l=this._physicalRotate?.id===s.id?{...s,angle:this._physicalRotate.angle}:s,c=ah(l,this._cellCm,this._gridPitch);if("square"!==l.shape)return W` + `}const s=e.wall_columns.find(e=>e.id===i.id);if(!s)return G;const l=this._physicalRotate?.id===s.id?{...s,angle:this._physicalRotate.angle}:s,c=dh(l,this._cellCm,this._gridPitch);if("square"!==l.shape)return W` - `;const h=wl(l.cm,this._cellCm,this._gridPitch),d=eh(l.angle)*Math.PI/180,p=Math.sin(d),u=-Math.cos(d),_=[l.center[0]+p*h/2,l.center[1]+u*h/2],m=[_[0]+p*Math.max(t,24*n),_[1]+u*Math.max(t,24*n)];return W` + `;const h=Ml(l.cm,this._cellCm,this._gridPitch),d=oh(l.angle)*Math.PI/180,p=Math.sin(d),u=-Math.cos(d),_=[l.center[0]+p*h/2,l.center[1]+u*h/2],m=[_[0]+p*Math.max(t,24*n),_[1]+u*Math.max(t,24*n)];return W` @@ -6721,7 +6721,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 data-kind="rotate" @pointerdown=${e=>this._physicalRotateDown(e,s)} @pointermove=${e=>this._physicalRotateMove(e)} @pointerup=${e=>this._physicalRotateUp(e)}>
- `})();return W`${c}${h}${d}${u}${_}`}_renderHiddenWallDiagnosticOverlay(){if(!this._markup)return W``;const e=this._hiddenWallDiagnosticSnapshot().value;if(!e.segments.length)return W``;const t=wl(5,this._cellCm,this._gridPitch);return W``})();return W`${c}${h}${d}${u}${_}`}_renderHiddenWallDiagnosticOverlay(){if(!this._markup)return W``;const e=this._hiddenWallDiagnosticSnapshot().value;if(!e.segments.length)return W``;const t=Ml(5,this._cellCm,this._gridPitch);return W``}_renderPlanSnapOverlay(){if(!this._markup)return W``;const e=this._planSnapGeometrySnapshot().value,t=this._activePlanSnapCandidate,i=new Set(this._activePlanSnapConflicts.map(e=>e.key)),s=wl(5,this._cellCm,this._gridPitch),n=wl(10,this._cellCm,this._gridPitch);return W``}_renderPlanSnapOverlay(){if(!this._markup)return W``;const e=this._planSnapGeometrySnapshot().value,t=this._activePlanSnapCandidate,i=new Set(this._activePlanSnapConflicts.map(e=>e.key)),s=Ml(5,this._cellCm,this._gridPitch),n=Ml(10,this._cellCm,this._gridPitch);return W``}_syncPlanSnapActiveMarker(e){const t=this.renderRoot?.querySelector('[data-hp="plan-snap-active-marker"]');if(t){if(t.setAttribute("class",`plan-snap-node${e?" active":""}${"line"===e?.kind?" dynamic":""}`),t.setAttribute("data-active",e?"true":"false"),t.setAttribute("visibility",e?"visible":"hidden"),!e)return t.removeAttribute("data-kind"),void t.removeAttribute("data-key");t.setAttribute("data-kind",e.kind),t.setAttribute("data-key",e.key),t.setAttribute("cx",String(e.point[0])),t.setAttribute("cy",String(e.point[1])),t.setAttribute("r",String(wl(10,this._cellCm,this._gridPitch)))}}_syncPlanSnapConflictMarkers(e){const t=new Set(e.map(e=>e.key));for(const e of this.renderRoot?.querySelectorAll('.plan-snap-node[data-kind="endpoint"]')||[])e.setAttribute("class","plan-snap-node"+(t.has(e.dataset.key||"")?" conflict":""))}_planSnapPhysicalSegment(e){let t=0;const i=this._spaceModel();if(!i)return null;if("partition"===e.sourceKind)t=Number(i.partitions.find(t=>t.id===e.sourceId)?.cm)||0;else if("draft"===e.sourceKind){const s=/^(.*):(\d+)$/.exec(e.sourceId),n=s?i.room_drafts.find(e=>e.id===s[1]):null;t=n&&s&&Number(n.segments[Number(s[2])]?.cm)||0}else t=bc(i.rooms,this._spaceWalls,this._openCuts(),[e.a[0],e.a[1],e.b[0],e.b[1]],this._wallKeyPitch,this._cellCm,this._gridPitch,Pv);return t>0?{a:[...e.a],b:[...e.b],halfDepth:wl(t,this._cellCm,this._gridPitch)/2}:null}_drawPreviewJoinPatchD(e,t){if(e.length<2)return"";const i=[];for(let s=0;s+10&&i.push({a:e[s],b:e[s+1],halfDepth:t[s]});if(!i.length)return"";const s=2e-4*this._gridPitch,n=this._planSnapGeometrySnapshot().value.segments.filter(t=>e.some(e=>Hs(e,[t.a[0],t.a[1],t.b[0],t.b[1]])<=s)).map(e=>this._planSnapPhysicalSegment(e)).filter(e=>!!e);return Yl([...i,...n],s).map(e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`).join(" ")}_renderMarkupLayer(e){const t=this._openCuts(),i=this._thickWallCuts(),s=t.concat(i),n=s.length?Ds(this._segments,s,.02*this._gridPitch):this._segments,o=this._path,a=this._gridPitch,r=this._viewOr(this._baseVb()),l="draw"===this._tool?this._drawWallCm:null,c=(()=>"draw"===this._tool&&o.length&&null!=l?this._contourClosed?o:this._cursorPt?[...o,this._cursorPt]:o.length>=2?o:null:null)(),h=c?ku(c.length-1,this._contourClosed?[...this._draftSegmentCms,this._closingWallCm??void 0]:this._draftSegmentCms,l,ul).map(e=>wl(e,this._cellCm,this._gridPitch)/2):[],d=c?Jl(c,wl(l,this._cellCm,this._gridPitch)/2,this._contourClosed,h):"",p=c?this._drawPreviewJoinPatchD(c,h):"",u=this._wallFaceBatch?.candidates[this._wallFaceBatch.index]?.repair||this._wallRepairDiagnostic;return W` + `}_syncPlanSnapActiveMarker(e){const t=this.renderRoot?.querySelector('[data-hp="plan-snap-active-marker"]');if(t){if(t.setAttribute("class",`plan-snap-node${e?" active":""}${"line"===e?.kind?" dynamic":""}`),t.setAttribute("data-active",e?"true":"false"),t.setAttribute("visibility",e?"visible":"hidden"),!e)return t.removeAttribute("data-kind"),void t.removeAttribute("data-key");t.setAttribute("data-kind",e.kind),t.setAttribute("data-key",e.key),t.setAttribute("cx",String(e.point[0])),t.setAttribute("cy",String(e.point[1])),t.setAttribute("r",String(Ml(10,this._cellCm,this._gridPitch)))}}_syncPlanSnapConflictMarkers(e){const t=new Set(e.map(e=>e.key));for(const e of this.renderRoot?.querySelectorAll('.plan-snap-node[data-kind="endpoint"]')||[])e.setAttribute("class","plan-snap-node"+(t.has(e.dataset.key||"")?" conflict":""))}_planSnapPhysicalSegment(e){let t=0;const i=this._spaceModel();if(!i)return null;if("partition"===e.sourceKind)t=Number(i.partitions.find(t=>t.id===e.sourceId)?.cm)||0;else if("draft"===e.sourceKind){const s=/^(.*):(\d+)$/.exec(e.sourceId),n=s?i.room_drafts.find(e=>e.id===s[1]):null;t=n&&s&&Number(n.segments[Number(s[2])]?.cm)||0}else t=$c(i.rooms,this._spaceWalls,this._openCuts(),[e.a[0],e.a[1],e.b[0],e.b[1]],this._wallKeyPitch,this._cellCm,this._gridPitch,zv);return t>0?{a:[...e.a],b:[...e.b],halfDepth:Ml(t,this._cellCm,this._gridPitch)/2}:null}_drawPreviewJoinPatchD(e,t){if(e.length<2)return"";const i=[];for(let s=0;s+10&&i.push({a:e[s],b:e[s+1],halfDepth:t[s]});if(!i.length)return"";const s=2e-4*this._gridPitch,n=this._planSnapGeometrySnapshot().value.segments.filter(t=>e.some(e=>Hs(e,[t.a[0],t.a[1],t.b[0],t.b[1]])<=s)).map(e=>this._planSnapPhysicalSegment(e)).filter(e=>!!e);return ec([...i,...n],s).map(e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`).join(" ")}_renderMarkupLayer(e){const t=this._openCuts(),i=this._thickWallCuts(),s=t.concat(i),n=s.length?Ds(this._segments,s,.02*this._gridPitch):this._segments,o=this._path,a=this._gridPitch,r=this._viewOr(this._baseVb()),l="draw"===this._tool?this._drawWallCm:null,c=(()=>"draw"===this._tool&&o.length&&null!=l?this._contourClosed?o:this._cursorPt?[...o,this._cursorPt]:o.length>=2?o:null:null)(),h=c?Cu(c.length-1,this._contourClosed?[...this._draftSegmentCms,this._closingWallCm??void 0]:this._draftSegmentCms,l,vl).map(e=>Ml(e,this._cellCm,this._gridPitch)/2):[],d=c?tc(c,Ml(l,this._cellCm,this._gridPitch)/2,this._contourClosed,h):"",p=c?this._drawPreviewJoinPatchD(c,h):"",u=this._wallFaceBatch?.candidates[this._wallFaceBatch.index]?.repair||this._wallRepairDiagnostic;return W` ${this._gridLevels()?W``:G} ${n.map(e=>W``)} ${this._renderPhysicalEditorLayer()} - ${"column"===this._tool&&this._cursorPt&&this._drawWallCm?W``M ${ah({shape:"square",center:this._cursorPt,cm:this._drawWallCm},this._cellCm,this._gridPitch).map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`)()}>`:G} + ${"column"===this._tool&&this._cursorPt&&this._drawWallCm?W``M ${dh({shape:"square",center:this._cursorPt,cm:this._drawWallCm},this._cellCm,this._gridPitch).map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`)()}>`:G} ${d?W` `:G} - ${c&&0===l?W`e.join(",")).join(" ")}>`:G} ${p?W` `:G} @@ -6772,7 +6772,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 aria-hidden="true" pointer-events="none">`:G} ${"split"===this._tool&&this._splitSel?.pts?.length?W`${this._splitSel.pts.length>1?W``:G} ${this._splitSel.pts.map((e,t)=>W``)} + cx="${e[0]}" cy="${e[1]}" r="${Dh(.22*a,this._cellCm)}">`)} ${this._cursorPt?W``:G}`:G} `}_renderActiveChainInk(){const e=this._path,t=this._gridPitch;return W` @@ -6780,9 +6780,9 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 ${e.length&&this._cursorPt&&"draw"===this._tool&&!this._contourClosed?W` ${this._activePlanSnapCandidate||this._activePlanSnapConflicts.length?G:W``}`:G} + r="${Dh(.22*t,this._cellCm)}" aria-hidden="true">`}`:G} ${e.map((e,i)=>W``)} + cx="${e[0]}" cy="${e[1]}" r="${Dh(.22*t,this._cellCm)}">`)} `}_renderPartitionDeleteDialog(){const e=this._partitionDeleteDialog,t="mi"===this.hass?.config?.unit_system?.length;return q`

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

    - ${e.openings.map(e=>q`
  • ${this._t("confirm.delete_partition_openings_item",{type:this._t(`opening.${e.type}`),length:li(e.length*Pv/this._gridPitch*this._cellCm,t)})}
  • `)} + ${e.openings.map(e=>q`
  • ${this._t("confirm.delete_partition_openings_item",{type:this._t(`opening.${e.type}`),length:li(e.length*zv/this._gridPitch*this._cellCm,t)})}
  • `)}
@@ -6833,8 +6833,8 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 `:G}
this._physicalDialog={...e,cm:t.target.value}} /> ${this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}
${t&&"square"===e.shape?q` @@ -7077,7 +7077,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 `: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=js(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=mv(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${Bd(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 jp({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(!Wp(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:Op(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=qo(this._planHass,s,n),a=Wo(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?C_(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?qo(this._planHass,o,l):[],u=o?Wo(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),b=p.find(e=>e.key===f),y=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?Ap(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(", "):"",N=(()=>{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 q``}_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=js(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=yv(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${Gd(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 Vp({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(!Yp(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:qp(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=qo(this._planHass,s,n),a=Wo(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?qo(this._planHass,o,l):[],u=o?Wo(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),b=p.find(e=>e.key===f),y=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?Op(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(", "):"",N=(()=>{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 q`
@@ -7323,10 +7323,10 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 -

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

+

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

${"static_icon"===e.display&&this._bindingHasAlarm(e.binding)?q`
${this._t("marker.static_alarm_warning")} @@ -7475,7 +7475,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 ${"file"===e.source?q`
@@ -7495,23 +7495,23 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26 ${e.pickSaved?this._renderServerPlans(e):G}`:G}
{const i=t.target.value,s=mv(i),n=null==s?null:(o=s,this._imperial?o*wh:o);var o;this._spaceDialog={...e,cellCmInput:i,cellCmTouched:!0,cellCm:null!=n&&n>0?Math.max(.1,Math.min(uv,n)):e.cellCm}}} /> + min=${Th(.1,this._imperial)} + max=${Th(vv,this._imperial)} + step="0.1" .value=${e.cellCmInput??Th(e.cellCm,this._imperial)} + @input=${t=>{const i=t.target.value,s=yv(i),n=null==s?null:(o=s,this._imperial?o*Mh:o);var o;this._spaceDialog={...e,cellCmInput:i,cellCmTouched:!0,cellCm:null!=n&&n>0?Math.max(.1,Math.min(vv,n)):e.cellCm}}} /> ${this._t(this._imperial?"space.scale_unit_imperial":"space.scale_unit")}
@@ -7526,7 +7526,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="30d20d6f6822065e2d5226b18abbb05d8d26
${this._t("space.zero_wall_help")}