mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
7764 lines
1.3 MiB
Plaintext
Executable File
7764 lines
1.3 MiB
Plaintext
Executable File
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="9370aa76e493dcb8caff0772a51dc5b38b8c7c48b58f80c8f357a091b26c7ef4";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?"<svg>":3===t?"<math>":"",a=F;for(let t=0;t<i;t++){const i=e[t];let r,l,c=-1,h=0;for(;h<i.length&&(a.lastIndex=h,l=a.exec(i),null!==l);)h=a.lastIndex,a===F?"!--"===l[1]?a=N:void 0!==l[1]?a=E:void 0!==l[2]?(B.test(l[2])&&(n=RegExp("</"+l[2],"g")),a=O):void 0!==l[3]&&(a=O):a===O?">"===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?"</svg>":3===t?"</math>":"")),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.length<a;){if(1===s.nodeType){if(s.hasAttributes())for(const e of s.getAttributeNames())if(e.endsWith(M)){const t=c[o++],i=s.getAttribute(e).split(C),a=/([.?@])?(.*)/.exec(t);r.push({type:1,index:n,name:a[2],strings:i,ctor:"."===a[1]?ie:"?"===a[1]?se:"@"===a[1]?ne:te}),s.removeAttribute(e)}else e.startsWith(C)&&(r.push({type:6,index:n}),s.removeAttribute(e));if(B.test(s.tagName)){const e=s.textContent.split(C),t=e.length-1;if(t>0){s.textContent=$?$.emptyScript:"";for(let i=0;i<t;i++)s.append(e[i],I()),K.nextNode(),r.push({type:2,index:++n});s.append(e[t],I())}}}else if(8===s.nodeType)if(s.data===D)r.push({type:2,index:n});else{let e=-1;for(;-1!==(e=s.data.indexOf(C,e+1));)r.push({type:7,index:n}),e+=C.length-1}n++}}static createElement(e,t){const i=T.createElement("template");return i.innerHTML=e,i}}function Z(e,t,i=e,s){if(t===U)return t;let n=void 0!==s?i._$Co?.[s]:i._$Cl;const o=R(t)?void 0:t._$litDirective$;return n?.constructor!==o&&(n?._$AO?.(!1),void 0===o?n=void 0:(n=new o(e),n._$AT(e,i,s)),void 0!==s?(i._$Co??=[])[s]=n:i._$Cl=n),void 0!==n&&(t=Z(e,n._$AS(e,t.values),n,s)),t}class Q{constructor(e,t){this._$AV=[],this._$AN=void 0,this._$AD=e,this._$AM=t}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(e){const{el:{content:t},parts:i}=this._$AD,s=(e?.creationScope??T).importNode(t,!0);K.currentNode=s;let n=K.nextNode(),o=0,a=0,r=i[0];for(;void 0!==r;){if(o===r.index){let t;2===r.type?t=new ee(n,n.nextSibling,this,e):1===r.type?t=new r.ctor(n,r.name,r.strings,this,e):6===r.type&&(t=new oe(n,this,e)),this._$AV.push(t),r=i[++a]}o!==r?.index&&(n=K.nextNode(),o++)}return K.currentNode=T,s}p(e){let t=0;for(const i of this._$AV)void 0!==i&&(void 0!==i.strings?(i._$AI(e,i,t),t+=i.strings.length-2):i._$AI(e[t])),t++}}class ee{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(e,t,i,s){this.type=2,this._$AH=G,this._$AN=void 0,this._$AA=e,this._$AB=t,this._$AM=i,this.options=s,this._$Cv=s?.isConnected??!0}get parentNode(){let e=this._$AA.parentNode;const t=this._$AM;return void 0!==t&&11===e?.nodeType&&(e=t.parentNode),e}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(e,t=this){e=Z(this,e,t),R(e)?e===G||null==e||""===e?(this._$AH!==G&&this._$AR(),this._$AH=G):e!==this._$AH&&e!==U&&this._(e):void 0!==e._$litType$?this.$(e):void 0!==e.nodeType?this.T(e):(e=>A(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++;s<t.length&&(this._$AR(i&&i._$AB.nextSibling,s),t.length=s)}_$AR(e=this._$AA.nextSibling,t){for(this._$AP?.(!1,!0,t);e!==this._$AB;){const t=x(e).nextSibling;x(e).remove(),e=t}}setConnected(e){void 0===this._$AM&&(this._$Cv=e,this._$AP?.(e))}}class te{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(e,t,i,s,n){this.type=1,this._$AH=G,this._$AN=void 0,this.element=e,this.name=t,this._$AM=s,this.options=n,i.length>2||""!==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<n.length-1;a++)r=Z(this,s[i+a],t,a),r===U&&(r=this._$AH[a]),o||=!R(r)||r!==this._$AH[a],r===G?e=G:e!==G&&(e+=(r??"")+n[a+1]),this._$AH[a]=r}o&&!s&&this.j(e)}j(e){e===G?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,e??"")}}class ie extends te{constructor(){super(...arguments),this.type=3}j(e){this.element[this.name]=e===G?void 0:e}}class se extends te{constructor(){super(...arguments),this.type=4}j(e){this.element.toggleAttribute(this.name,!!e&&e!==G)}}class ne extends te{constructor(e,t,i,s,n){super(e,t,i,s,n),this.type=5}_$AI(e,t=this){if((e=Z(this,e,t,0)??G)===U)return;const i=this._$AH,s=e===G&&i!==G||e.capture!==i.capture||e.once!==i.once||e.passive!==i.passive,n=e!==G&&(i===G||s);s&&this.element.removeEventListener(this.name,this,i),n&&this.element.addEventListener(this.name,this,e),this._$AH=e}handleEvent(e){"function"==typeof this._$AH?this._$AH.call(this.options?.host??this.element,e):this._$AH.handleEvent(e)}}class oe{constructor(e,t,i){this.element=e,this.type=6,this._$AN=void 0,this._$AM=t,this.options=i}get _$AU(){return this._$AM._$AU}_$AI(e){Z(this,e)}}const ae={I:ee},re=k.litHtmlPolyfillSupport;re?.(X,ee),(k.litHtmlVersions??=[]).push("3.3.3");const le=(e,t,i)=>{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`<span class="title" id=${this._titleId}>
|
||
${this.icon?q`<ha-icon icon=${this.icon}></ha-icon>`:G}
|
||
<span class="title-text">${this.title}</span>
|
||
</span>`;return this._useHaDialog?q`<ha-dialog
|
||
.hass=${this.hass}
|
||
.open=${!0}
|
||
width=${this.wide?"medium":"small"}
|
||
.preventScrimClose=${!this.dismissOnScrim}
|
||
.ariaLabelledBy=${this._titleId}
|
||
@opened=${this._focusInitial}
|
||
@closed=${this._requestClose}
|
||
>
|
||
<span class="header-title-slot" slot="headerTitle">${e}</span>
|
||
<slot></slot>
|
||
<span class="footer" slot="footer"><slot name="footer"></slot></span>
|
||
</ha-dialog><div class="overlay-portal"></div>`:q`<dialog
|
||
role="dialog"
|
||
aria-modal="true"
|
||
aria-labelledby=${this._titleId}
|
||
@cancel=${this._onFallbackCancel}
|
||
@click=${this._onFallbackClick}
|
||
>
|
||
<section class="surface" tabindex="-1">
|
||
<header class="header">
|
||
${e}
|
||
<button class="close" type="button"
|
||
aria-label=${this.hass?.localize?.("ui.common.close")||"Close"}
|
||
@click=${this._requestClose}>
|
||
<ha-icon icon="mdi:close"></ha-icon>
|
||
</button>
|
||
</header>
|
||
<div class="content"><slot></slot></div>
|
||
<div class="footer"><slot name="footer"></slot></div>
|
||
</section>
|
||
<div class="overlay-portal"></div>
|
||
</dialog>`}}Pe.properties={title:{type:String},icon:{type:String},wide:{type:Boolean,reflect:!0},dismissOnScrim:{type:Boolean,attribute:"dismiss-on-scrim"},hass:{attribute:!1}},Pe.styles=o`
|
||
:host {
|
||
display: contents;
|
||
color: var(--primary-text-color, #e6e7eb);
|
||
font: inherit;
|
||
}
|
||
|
||
ha-dialog {
|
||
--dialog-content-padding: 0;
|
||
--dialog-surface-background: var(--card-background-color, var(--hp-bg, #202126));
|
||
--ha-dialog-border-radius: var(--rad-l, 18px);
|
||
/* HA's ha-dialog-header defaults this custom property to a one-line
|
||
fixed height. Our localized slot is intentionally allowed to wrap,
|
||
so leaving that default in place clips every line after the first at
|
||
the bottom of the header. auto is HA's public sizing hook and also
|
||
stays harmless on older ha-dialog implementations that do not consume
|
||
it. */
|
||
--ha-dialog-header-title-height: auto;
|
||
color: inherit;
|
||
}
|
||
|
||
ha-dialog::part(dialog) {
|
||
border: 1px solid var(--hp-accent, #d89300);
|
||
box-shadow: var(--shadow-3, 0 18px 48px rgb(0 0 0 / 0.34));
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* The HA header slot is a flex item with a constrained inline size. The
|
||
old inline-flex title kept its min-content width, so HA clipped the last
|
||
word instead of giving it a second line. Keep every wrapper shrinkable
|
||
and let the text wrap; this applies to every hp-dialog, including long
|
||
device names and translated titles. */
|
||
.header-title-slot {
|
||
display: block;
|
||
flex: 1 1 auto;
|
||
width: 100%;
|
||
max-width: 100%;
|
||
min-width: 0;
|
||
box-sizing: border-box;
|
||
white-space: normal;
|
||
}
|
||
|
||
.title {
|
||
display: flex;
|
||
flex: 1 1 auto;
|
||
align-items: center;
|
||
gap: var(--sp-4, 12px);
|
||
width: 100%;
|
||
max-width: 100%;
|
||
min-width: 0;
|
||
font-weight: 600;
|
||
line-height: 1.25;
|
||
white-space: normal;
|
||
}
|
||
|
||
.title ha-icon {
|
||
flex: none;
|
||
color: var(--hp-accent, #d89300);
|
||
}
|
||
|
||
.title-text {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
max-width: 100%;
|
||
white-space: normal;
|
||
overflow-wrap: anywhere;
|
||
word-break: normal;
|
||
}
|
||
|
||
.footer {
|
||
/* ha-dialog lays out its footer slot as a flex row. display: contents
|
||
exposed the consumer's action row as a shrink-to-fit flex item, so a
|
||
wide device dialog got a half-width divider and its Hide action slid
|
||
toward the centre. Keep one full-width slot item in both HA and the
|
||
native fallback. */
|
||
display: block;
|
||
width: 100%;
|
||
min-width: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
::slotted([slot='footer']) {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
dialog {
|
||
width: auto;
|
||
max-width: none;
|
||
max-height: none;
|
||
margin: auto;
|
||
padding: 0;
|
||
border: 0;
|
||
overflow: visible;
|
||
color: inherit;
|
||
background: transparent;
|
||
}
|
||
|
||
dialog::backdrop {
|
||
background: rgb(0 0 0 / 0.45);
|
||
}
|
||
|
||
.surface {
|
||
width: min(360px, 92vw);
|
||
max-height: 92vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
box-sizing: border-box;
|
||
color: inherit;
|
||
background: var(--card-background-color, var(--hp-bg, #202126));
|
||
border: 1px solid var(--hp-accent, #d89300);
|
||
border-radius: var(--rad-l, 18px);
|
||
box-shadow: var(--shadow-3, 0 18px 48px rgb(0 0 0 / 0.34));
|
||
}
|
||
|
||
:host([wide]) .surface {
|
||
width: min(var(--hp-dialog-wide-width, 500px), 94vw);
|
||
}
|
||
|
||
.header {
|
||
min-height: 56px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-4, 12px);
|
||
padding: var(--sp-4, 12px) var(--sp-5, 16px);
|
||
box-sizing: border-box;
|
||
border-bottom: 1px solid var(--hp-line, rgb(255 255 255 / 0.12));
|
||
}
|
||
|
||
.header .title {
|
||
flex: 1;
|
||
}
|
||
|
||
.close {
|
||
width: 40px;
|
||
height: 40px;
|
||
flex: none;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0;
|
||
color: inherit;
|
||
background: transparent;
|
||
border: 0;
|
||
border-radius: 50%;
|
||
cursor: pointer;
|
||
}
|
||
|
||
:host([data-pointer-hover]) .close:hover,
|
||
.close:focus-visible {
|
||
background: rgb(127 127 127 / 0.16);
|
||
}
|
||
|
||
.content {
|
||
min-height: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.overlay-portal {
|
||
position: fixed;
|
||
z-index: 2147483647;
|
||
inset: 0;
|
||
overflow: visible;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.overlay-portal:empty {
|
||
display: none;
|
||
}
|
||
|
||
.overlay-portal > * {
|
||
pointer-events: auto;
|
||
}
|
||
`,customElements.get("hp-dialog")||customElements.define("hp-dialog",Pe);const Te=/^#[0-9a-fA-F]{6}$/;function Ie(e,t){return"string"==typeof e&&Te.test(e)?e:t}const Re="(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])",Ae=new RegExp(`^rgb\\(${Re}, ${Re}, ${Re}\\)$`);function ze(e){if(!Array.isArray(e)||e.length<3)return null;const t=e.slice(0,3);if(!t.every(e=>"number"==typeof e&&Number.isFinite(e)))return null;const[i,s,n]=t.map(e=>Math.round(Math.min(255,Math.max(0,e))));return`rgb(${i}, ${s}, ${n})`}const Fe=(e,t,i)=>Math.min(i,Math.max(t,Number.isFinite(e)?e:t));function Ne(e){return Number.isFinite(e)?(e%360+360)%360:0}function Ee(e){if("string"!=typeof e)return null;const t=e.trim().replace(/^#/,""),i=/^[0-9a-fA-F]{3}$/.test(t)?t.split("").map(e=>`${e}${e}`).join(""):t;return/^[0-9a-fA-F]{6}$/.test(i)?`#${i.toLowerCase()}`:null}function Oe(e){const t=Ee(e);return t?{r:Number.parseInt(t.slice(1,3),16),g:Number.parseInt(t.slice(3,5),16),b:Number.parseInt(t.slice(5,7),16)}:null}function He(e){const t=Fe(e.r,0,255)/255,i=Fe(e.g,0,255)/255,s=Fe(e.b,0,255)/255,n=Math.max(t,i,s),o=n-Math.min(t,i,s);let a=0;return o>0&&(a=n===t?(i-s)/o%6*60:n===i?60*((s-t)/o+2):60*((t-i)/o+4)),{h:Ne(a),s:0===n?0:o/n*100,v:100*n}}function Le(e){return function(e){const t=e=>Math.round(Fe(e,0,255)).toString(16).padStart(2,"0");return`#${t(e.r)}${t(e.g)}${t(e.b)}`}(function(e){const t=Ne(e.h),i=Fe(e.s,0,100)/100,s=Fe(e.v,0,100)/100,n=s*i,o=n*(1-Math.abs(t/60%2-1)),a=s-n;let r;return r=t<60?[n,o,0]:t<120?[o,n,0]:t<180?[0,n,o]:t<240?[0,o,n]:t<300?[o,0,n]:[n,0,o],{r:Math.round(255*(r[0]+a)),g:Math.round(255*(r[1]+a)),b:Math.round(255*(r[2]+a))}}(e))}function Be(e){const t=e.visualViewport;return t&&Number.isFinite(t.width)&&Number.isFinite(t.height)?{left:Number(t.offsetLeft)||0,top:Number(t.offsetTop)||0,width:Math.max(0,Number(t.width)||0),height:Math.max(0,Number(t.height)||0)}:{left:0,top:0,width:e.innerWidth,height:e.innerHeight}}function je(e,t,i,s=7,n=8){const o=i.left+n,a=i.top+n,r=Math.max(o,i.left+i.width-n),l=Math.max(a,i.top+i.height-n),c=Math.max(0,r-o),h=Math.max(0,l-a),d=Math.min(Math.max(0,t.width),c),p=Math.min(Math.max(0,t.height),h);let u=e.left;u+d>r&&(u=e.right-d),u=Math.min(Math.max(o,u),Math.max(o,r-d));const _=e.bottom+s,m=e.top-s-p;let g="bottom",f=_;return _+p>l&&m>=a?(g="top",f=Math.min(Math.max(a,m),Math.max(a,l-p))):f=Math.min(Math.max(a,_),Math.max(a,l-p)),{left:Math.round(u),top:Math.round(f),side:g,maxWidth:Math.round(c),maxHeight:Math.round(h)}}class qe{constructor(e,t,i){this.owner=e,this.name=t,this.keydown=i,this.host=null,this.root=null}window(){return this.owner.ownerDocument.defaultView}dialog(){return this.owner.closest("hp-dialog")}usesPopover(e=!1){const t=this.window();return!e&&!!t&&function(e){const t=e.HTMLElement?.prototype;return"function"==typeof t?.showPopover&&"function"==typeof t?.hidePopover}(t)}get hasFallback(){return!!this.host}containsPath(e){return e.includes(this.owner)||!!this.host&&e.includes(this.host)}ownsActiveElement(){let e=this.owner.ownerDocument.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return!(!e||e!==this.owner&&!this.owner.shadowRoot?.contains(e)&&!this.root?.contains(e))}renderFallback(e,t){const i=this.ensureFallback();return i?(le(q`<style>${t}</style>${e}`,i),i):null}surface(e,t){return t?this.owner.shadowRoot?.querySelector(e)||null:this.root?.querySelector(e)||null}destroy(){this.root&&le(G,this.root),this.host?.remove(),this.host=null,this.root=null}ensureFallback(){if(this.root?.isConnected)return this.root;const e=this.dialog()?.overlayPortal()||this.owner.ownerDocument.body;if(!e)return null;const t=this.owner.ownerDocument.createElement("div");return t.dataset.hpOverlay=this.name,t.style.display="contents",this.keydown&&t.addEventListener("keydown",this.keydown,!0),e.append(t),this.host=t,this.root=t.attachShadow({mode:"open"}),this.root}}const We={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class Ue extends he{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=We,this._open=!1,this._pickerRaf=0,this._emitRaf=0,this._forceFallback=!1,this._overlayDispose=null,this._hue=0,this._saturation=0,this._value=0,this._hexDraft="#607d8b",this._hexInvalid=!1,this._lastValidColor="#607d8b",this._activePointerId=null,this._outsidePointerDown=e=>{if(!this._open)return;const t=e.composedPath();this._floating.containsPath(t)||this._closePicker(!1,"outside")},this._keyDown=e=>{this._open&&"Escape"===e.key&&(e.preventDefault(),e.stopImmediatePropagation(),this._closePicker(this._floating.ownsActiveElement(),"escape"))},this._floating=new qe(this,"color-opacity",this._keyDown),this._queuePickerPosition=()=>{if(!this._open)return;this._pickerRaf&&cancelAnimationFrame(this._pickerRaf);const e=this._window();e&&(this._pickerRaf=e.requestAnimationFrame(()=>{this._pickerRaf=0,this._positionPicker()||this._closePicker()}))}}connectedCallback(){super.connectedCallback(),this._syncFromColor(this.color,!1),this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("scroll",this._queuePickerPosition,!0);const e=this.ownerDocument.defaultView;e?.addEventListener("resize",this._queuePickerPosition),e?.addEventListener("orientationchange",this._queuePickerPosition),e?.visualViewport?.addEventListener("resize",this._queuePickerPosition),e?.visualViewport?.addEventListener("scroll",this._queuePickerPosition),this.addEventListener("keydown",this._keyDown,!0)}disconnectedCallback(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("scroll",this._queuePickerPosition,!0);const e=this.ownerDocument.defaultView;e?.removeEventListener("resize",this._queuePickerPosition),e?.removeEventListener("orientationchange",this._queuePickerPosition),e?.visualViewport?.removeEventListener("resize",this._queuePickerPosition),e?.visualViewport?.removeEventListener("scroll",this._queuePickerPosition),this.removeEventListener("keydown",this._keyDown,!0),this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._pickerRaf=0,this._emitRaf=0,this._closePicker(!1,"disconnect"),super.disconnectedCallback()}updated(e){if(e.has("color")){const e=Ee(this.color);e&&e!==this._lastValidColor&&this._syncFromColor(e,this._open)}this.disabled&&this._open?this._closePicker():this._open&&(this._supportsPopover()||this._renderFallback(),(e.has("color")||e.has("opacity")||e.has("showOpacity")||e.has("label")||e.has("opacityLabel")||e.has("pickerLabels"))&&this._queuePickerPosition())}_window(){return this._floating.window()}_supportsPopover(){return this._floating.usesPopover(this._forceFallback)}_dialog(){return this._floating.dialog()}async _toggle(){this.disabled||(this._open?this._closePicker():(this._forceFallback=!1,this._syncFromColor(this.color,!1),this._hexInvalid=!1,this._activePointerId=null,this._open=!0,await this.updateComplete,this._open&&(this._supportsPopover()||this._renderFallback(),this._positionPicker()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:e=>this._closePicker("escape"===e&&this._floating.ownsActiveElement(),e)})||null:this._closePicker())))}_closePicker(e=!1,t="exclusive"){if(!this._open&&!this._floating.hasFallback)return;const i=this._overlayDispose;this._overlayDispose=null,i?.();const s=this.renderRoot.querySelector(".picker");if(s?.hidePopover)try{s.matches(":popover-open")&&s.hidePopover()}catch{}this._open=!1,this._cancelQueuedEmit(),this._activePointerId=null,this._floating.destroy(),e&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_surface(){return this._floating.surface(".picker",this._supportsPopover())}_renderFallback(){const e=Ue.styles.cssText;this._floating.renderFallback(this._pickerTemplate(!1),e)}_positionPicker(){if(!this._open)return!1;const e=this._window(),t=this.renderRoot.querySelector(".trigger");let i=this._surface();if(!e||!t?.isConnected||!i?.isConnected)return!1;const s=Be(e);if(i.style.maxWidth=`${Math.max(0,s.width-16)}px`,i.style.maxHeight=`${Math.max(0,s.height-16)}px`,i.style.visibility="hidden",this._supportsPopover()&&i.showPopover)try{i.matches(":popover-open")||i.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),i=this._surface(),!i?.isConnected)return!1;i.style.maxWidth=`${Math.max(0,s.width-16)}px`,i.style.maxHeight=`${Math.max(0,s.height-16)}px`,i.style.visibility="hidden"}const n=t.getBoundingClientRect(),o=i.getBoundingClientRect();if(!(n.width&&n.height&&o.width&&o.height))return!1;const a=je(n,o,s);return i.style.left=`${a.left}px`,i.style.top=`${a.top}px`,i.dataset.side=a.side,i.style.visibility="",!0}_labels(){const e=this.pickerLabels||We;return{title:e.title||We.title,hue:e.hue||We.hue,saturation:e.saturation||We.saturation,value:e.value||We.value,hex:e.hex||We.hex,invalidHex:e.invalidHex||We.invalidHex}}_syncFromColor(e,t){const i=Ee(Ie(e,"#607d8b"))||"#607d8b",s=Oe(i);if(!s)return;const n=He(s);(!t||n.s>1e-4)&&(this._hue=n.h),this._saturation=n.s,this._value=n.v,this._hexDraft=i,this._hexInvalid=!1,this._lastValidColor=i}_cancelQueuedEmit(e=!1){this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._emitRaf=0,e&&this._syncFromColor(this._lastValidColor,!0)}_queueHsvEmit(){if(this._emitRaf)return;const e=this._window();e?this._emitRaf=e.requestAnimationFrame(()=>{this._emitRaf=0,this._emitHsv()}):this._emitHsv()}_emitHsv(){this._emit(Le({h:this._hue,s:this._saturation,v:this._value}),this.opacity)}_setHue(e){this._hue=Math.min(359,Math.max(0,Number.isFinite(e)?e:0)),this._emitHsv()}_setSaturation(e){this._saturation=Math.min(100,Math.max(0,Number.isFinite(e)?e:0)),this._emitHsv()}_setValue(e){this._value=Math.min(100,Math.max(0,Number.isFinite(e)?e:0)),this._emitHsv()}_setOpacity(e){this._emit(this._lastValidColor,e)}_shiftRangeKey(e,t,i,s,n){if(!e.shiftKey)return;const o="ArrowRight"===e.key||"ArrowUp"===e.key?10:"ArrowLeft"===e.key||"ArrowDown"===e.key?-10:0;o&&(e.preventDefault(),e.stopPropagation(),n(Math.min(s,Math.max(i,t+o))))}_setSvFromPointer(e,t){const i=e.currentTarget.getBoundingClientRect();i.width&&i.height&&(this._saturation=Math.min(100,Math.max(0,(e.clientX-i.left)/i.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(e.clientY-i.top)/i.height))),t?(this._cancelQueuedEmit(),this._emitHsv()):this._queueHsvEmit())}_svPointerDown(e){if(e.preventDefault(),e.stopPropagation(),null===this._activePointerId){this._activePointerId=e.pointerId;try{e.currentTarget.setPointerCapture(e.pointerId)}catch{}this._setSvFromPointer(e,!0)}}_svPointerMove(e){e.pointerId===this._activePointerId&&(e.preventDefault(),e.stopPropagation(),this._setSvFromPointer(e,!1))}_svPointerUp(e){if(e.pointerId===this._activePointerId){e.preventDefault(),e.stopPropagation(),this._setSvFromPointer(e,!0),this._activePointerId=null;try{e.currentTarget.releasePointerCapture(e.pointerId)}catch{}}}_svPointerCancel(e){e.pointerId===this._activePointerId&&(e.preventDefault(),e.stopPropagation(),this._activePointerId=null,this._cancelQueuedEmit(!0))}_svLostPointerCapture(e){e.pointerId===this._activePointerId&&(this._activePointerId=null,this._cancelQueuedEmit(!0))}_hexInput(e){const t=e.target.value;this._hexDraft=t,this._hexInvalid=!1;const i=Ee(t),s=i?Oe(i):null;if(!i||!s)return;const n=He(s);n.s>1e-4&&(this._hue=n.h),this._saturation=n.s,this._value=n.v,this._emit(i,this.opacity,!0)}_commitHex(){const e=Ee(this._hexDraft);if(!e)return this._hexDraft=this._lastValidColor,void(this._hexInvalid=!0);this._hexDraft=e,this._hexInvalid=!1,e!==this._lastValidColor&&this._emit(e,this.opacity)}_hexKeyDown(e){"Enter"===e.key&&(e.preventDefault(),e.stopPropagation(),this._commitHex())}_emit(e,t,i=!1){const s=Ee(e)||this._lastValidColor,n=Number.isFinite(t)?t:Number(this.opacity)||0,o=Math.min(1,Math.max(0,n));this.color=s,this.opacity=o,this._lastValidColor=s,i||(this._hexDraft=s),this._hexInvalid=!1,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:s,opacity:o},bubbles:!0,composed:!0}))}_pickerTemplate(e){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),i=Le({h:this._hue,s:this._saturation,v:this._value}),s=Le({h:this._hue,s:100,v:100}),n=this._labels(),o=Math.round(this._hue),a=Math.round(this._saturation),r=Math.round(this._value);return q`
|
||
<div class="picker" popover=${e?"manual":G} role="dialog" aria-label=${n.title}>
|
||
<div class="picker-head">
|
||
<span class="caption">${this.label||n.title}</span>
|
||
<span class="preview" aria-hidden="true"><span style=${`background:${i};opacity:${t/100}`}></span></span>
|
||
</div>
|
||
<div class="sv-field" aria-hidden="true"
|
||
style=${`--hp-picker-hue:${s};--hp-picker-saturation:${this._saturation}%;--hp-picker-value:${100-this._value}%`}
|
||
@pointerdown=${e=>this._svPointerDown(e)}
|
||
@pointermove=${e=>this._svPointerMove(e)}
|
||
@pointerup=${e=>this._svPointerUp(e)}
|
||
@pointercancel=${e=>this._svPointerCancel(e)}
|
||
@lostpointercapture=${e=>this._svLostPointerCapture(e)}>
|
||
<span class="sv-thumb"></span>
|
||
</div>
|
||
<label class="control">
|
||
<span class="control-head"><span class="caption">${n.hue}</span><span class="control-value">${o}°</span></span>
|
||
<input class="hue-range" type="range" min="0" max="359" step="1" .value=${String(o)}
|
||
aria-label=${n.hue} aria-valuetext=${`${o}°`} style=${`--hp-picker-hue:${s}`}
|
||
@input=${e=>this._setHue(Number(e.target.value))}
|
||
@keydown=${e=>this._shiftRangeKey(e,o,0,359,e=>this._setHue(e))} />
|
||
</label>
|
||
<label class="control">
|
||
<span class="control-head"><span class="caption">${n.saturation}</span><span class="control-value">${a}%</span></span>
|
||
<input type="range" min="0" max="100" step="1" .value=${String(a)}
|
||
aria-label=${n.saturation} aria-valuetext=${`${a}%`}
|
||
@input=${e=>this._setSaturation(Number(e.target.value))}
|
||
@keydown=${e=>this._shiftRangeKey(e,a,0,100,e=>this._setSaturation(e))} />
|
||
</label>
|
||
<label class="control">
|
||
<span class="control-head"><span class="caption">${n.value}</span><span class="control-value">${r}%</span></span>
|
||
<input type="range" min="0" max="100" step="1" .value=${String(r)}
|
||
aria-label=${n.value} aria-valuetext=${`${r}%`}
|
||
@input=${e=>this._setValue(Number(e.target.value))}
|
||
@keydown=${e=>this._shiftRangeKey(e,r,0,100,e=>this._setValue(e))} />
|
||
</label>
|
||
<label class="control">
|
||
<span class="caption">${n.hex}</span>
|
||
<input type="text" inputmode="text" autocomplete="off" spellcheck="false"
|
||
.value=${this._hexDraft} aria-label=${n.hex}
|
||
aria-invalid=${this._hexInvalid?"true":"false"}
|
||
aria-describedby=${this._hexInvalid?"hex-error":G}
|
||
@input=${e=>this._hexInput(e)}
|
||
@blur=${()=>this._commitHex()}
|
||
@keydown=${e=>this._hexKeyDown(e)} />
|
||
${this._hexInvalid?q`<span id="hex-error" class="error">${n.invalidHex}</span>`:G}
|
||
</label>
|
||
${this.showOpacity?q`<div class="row">
|
||
<span class="caption">${this.opacityLabel}</span>
|
||
<input type="range" min="0" max="100" step="1" .value=${String(t)}
|
||
aria-label=${this.opacityLabel} aria-valuetext=${`${t}%`}
|
||
@input=${e=>this._setOpacity(Number(e.target.value)/100)}
|
||
@keydown=${e=>this._shiftRangeKey(e,t,0,100,e=>this._setOpacity(e/100))} />
|
||
<input type="number" min="0" max="100" step="1" .value=${String(t)}
|
||
aria-label=${`${this.opacityLabel}, %`}
|
||
@change=${e=>this._setOpacity(Number(e.target.value)/100)} />
|
||
<span class="pct">%</span>
|
||
</div>`:G}
|
||
</div>`}render(){const e=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),t=Ie(this.color,"#607d8b"),i=this.showOpacity?`${this.label||"Color"}: ${t}, ${e}%`:`${this.label||"Color"}: ${t}`;return q`
|
||
${this.label?q`<span class="label">${this.label}</span>`:G}
|
||
<button class="trigger" type="button" .disabled=${this.disabled}
|
||
aria-label=${i} aria-haspopup="dialog" aria-expanded=${this._open?"true":"false"}
|
||
title=${i} @click=${this._toggle}>
|
||
<span class="swatch" style=${`background:${t};opacity:${this.showOpacity?e/100:1}`}></span>
|
||
</button>
|
||
${this._open&&!this.disabled&&this._supportsPopover()?this._pickerTemplate(!0):G}
|
||
`}}Ue.properties={label:{type:String},opacityLabel:{type:String,attribute:"opacity-label"},color:{type:String},opacity:{type:Number},disabled:{type:Boolean,reflect:!0},showOpacity:{type:Boolean,attribute:"show-opacity"},pickerLabels:{attribute:!1},_open:{state:!0},_hue:{state:!0},_saturation:{state:!0},_value:{state:!0},_hexDraft:{state:!0},_hexInvalid:{state:!0}},Ue.styles=o`
|
||
:host {
|
||
position: relative;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
min-width: 0;
|
||
}
|
||
.label {
|
||
font-size: 12px;
|
||
color: var(--secondary-text-color, #9aa4ad);
|
||
white-space: nowrap;
|
||
}
|
||
.trigger {
|
||
width: 40px;
|
||
height: 40px;
|
||
flex: none;
|
||
box-sizing: border-box;
|
||
padding: 3px;
|
||
border: 1px solid var(--divider-color, #666);
|
||
border-radius: 7px;
|
||
background:
|
||
linear-gradient(45deg, #b8b8b8 25%, transparent 25%) 0 0 / 10px 10px,
|
||
linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 0 0 / 10px 10px,
|
||
linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 5px -5px / 10px 10px,
|
||
linear-gradient(45deg, #b8b8b8 25%, #eee 25%) 5px 5px / 10px 10px;
|
||
cursor: pointer;
|
||
}
|
||
:host([data-pointer-hover]) .trigger:hover {
|
||
border-color: var(--primary-color, #03a9f4);
|
||
}
|
||
.trigger[aria-expanded='true'] {
|
||
border-color: var(--primary-color, #03a9f4);
|
||
outline: none;
|
||
box-shadow: 0 0 0 1px var(--primary-color, #03a9f4);
|
||
}
|
||
.trigger:focus-visible {
|
||
border-color: var(--primary-color, #03a9f4);
|
||
outline: 2px solid var(--primary-color, #03a9f4);
|
||
outline-offset: 2px;
|
||
}
|
||
.swatch {
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 3px;
|
||
box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.18);
|
||
pointer-events: none;
|
||
}
|
||
.picker {
|
||
/* The popover attribute promotes this surface into the browser top layer, outside
|
||
hp-dialog's scrolling/clipping body. JS supplies viewport coordinates. */
|
||
position: fixed;
|
||
z-index: 2147483647;
|
||
inset: auto;
|
||
top: 0;
|
||
left: 0;
|
||
width: min(292px, calc(100vw - 16px));
|
||
max-height: calc(100vh - 16px);
|
||
margin: 0;
|
||
box-sizing: border-box;
|
||
display: grid;
|
||
gap: 10px;
|
||
padding: 12px;
|
||
overflow: auto;
|
||
color: var(--primary-text-color, #fff);
|
||
background: var(--card-background-color, #202126);
|
||
border: 1px solid var(--primary-color, #03a9f4);
|
||
border-radius: 10px;
|
||
box-shadow: 0 10px 28px rgb(0 0 0 / 0.34);
|
||
}
|
||
.picker[popover]:not(:popover-open) {
|
||
/* Keep the author-level display:grid from exposing the surface for one
|
||
frame before showPopover() promotes it into the top layer. */
|
||
display: none;
|
||
}
|
||
.picker-head,
|
||
.row,
|
||
.control-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
min-width: 0;
|
||
}
|
||
.picker-head {
|
||
justify-content: space-between;
|
||
}
|
||
.caption {
|
||
flex: 1;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
color: var(--secondary-text-color, #9aa4ad);
|
||
font-size: 12px;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.preview {
|
||
width: 52px;
|
||
height: 30px;
|
||
flex: none;
|
||
box-sizing: border-box;
|
||
padding: 3px;
|
||
border: 1px solid var(--divider-color, #666);
|
||
border-radius: 7px;
|
||
background:
|
||
linear-gradient(45deg, #b8b8b8 25%, transparent 25%) 0 0 / 10px 10px,
|
||
linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 0 0 / 10px 10px,
|
||
linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 5px -5px / 10px 10px,
|
||
linear-gradient(45deg, #b8b8b8 25%, #eee 25%) 5px 5px / 10px 10px;
|
||
}
|
||
.preview > span {
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 3px;
|
||
}
|
||
.sv-field {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 132px;
|
||
overflow: hidden;
|
||
box-sizing: border-box;
|
||
border: 1px solid var(--divider-color, #666);
|
||
border-radius: 8px;
|
||
background:
|
||
linear-gradient(to top, #000, transparent),
|
||
linear-gradient(to right, #fff, var(--hp-picker-hue, #f00));
|
||
cursor: crosshair;
|
||
touch-action: none;
|
||
user-select: none;
|
||
}
|
||
.sv-thumb {
|
||
position: absolute;
|
||
left: var(--hp-picker-saturation, 0%);
|
||
top: var(--hp-picker-value, 100%);
|
||
width: 14px;
|
||
height: 14px;
|
||
box-sizing: border-box;
|
||
border: 2px solid #fff;
|
||
border-radius: 50%;
|
||
box-shadow: 0 0 0 1px rgb(0 0 0 / .65), 0 1px 3px rgb(0 0 0 / .5);
|
||
transform: translate(-50%, -50%);
|
||
pointer-events: none;
|
||
}
|
||
.control {
|
||
display: grid;
|
||
gap: 2px;
|
||
min-width: 0;
|
||
}
|
||
.control-value {
|
||
flex: none;
|
||
color: var(--secondary-text-color, #9aa4ad);
|
||
font-size: 12px;
|
||
}
|
||
input[type='range'] {
|
||
width: auto;
|
||
height: 40px;
|
||
min-width: 0;
|
||
flex: 1;
|
||
margin: -5px 0;
|
||
accent-color: var(--primary-color, #03a9f4);
|
||
touch-action: none;
|
||
}
|
||
.hue-range {
|
||
accent-color: var(--hp-picker-hue, #f00);
|
||
--hp-picker-hue-track: linear-gradient(to right,
|
||
#f00 0%, #ff0 16.667%, #0f0 33.333%, #0ff 50%,
|
||
#00f 66.667%, #f0f 83.333%, #f00 100%);
|
||
--hp-picker-hue-thumb-shadow:
|
||
0 0 0 2px var(--card-background-color, #202126),
|
||
0 0 0 3px var(--primary-text-color, #fff);
|
||
}
|
||
.hue-range::-webkit-slider-runnable-track {
|
||
height: 10px;
|
||
box-sizing: border-box;
|
||
border: 1px solid var(--divider-color, #666);
|
||
border-radius: 999px;
|
||
background: var(--hp-picker-hue-track);
|
||
}
|
||
.hue-range::-moz-range-track {
|
||
height: 10px;
|
||
box-sizing: border-box;
|
||
border: 1px solid var(--divider-color, #666);
|
||
border-radius: 999px;
|
||
background: var(--hp-picker-hue-track);
|
||
}
|
||
.hue-range::-moz-range-progress {
|
||
height: 10px;
|
||
border: 0;
|
||
border-radius: 999px;
|
||
background: transparent;
|
||
}
|
||
.hue-range::-webkit-slider-thumb {
|
||
border-radius: 50%;
|
||
box-shadow: var(--hp-picker-hue-thumb-shadow);
|
||
}
|
||
.hue-range::-moz-range-thumb {
|
||
border-radius: 50%;
|
||
box-shadow: var(--hp-picker-hue-thumb-shadow);
|
||
}
|
||
input[type='text'],
|
||
input[type='number'] {
|
||
min-height: 40px;
|
||
box-sizing: border-box;
|
||
border: 1px solid var(--divider-color, #666);
|
||
border-radius: 6px;
|
||
padding: 6px 8px;
|
||
color: var(--primary-text-color, #fff);
|
||
background: var(--input-fill-color, transparent);
|
||
font: inherit;
|
||
}
|
||
input[type='text'] {
|
||
width: 100%;
|
||
}
|
||
input[type='number'] {
|
||
width: 50px;
|
||
flex: none;
|
||
}
|
||
input:focus-visible,
|
||
.sv-field:focus-visible {
|
||
outline: 2px solid var(--primary-color, #03a9f4);
|
||
outline-offset: 1px;
|
||
}
|
||
input[aria-invalid='true'] {
|
||
border-color: var(--error-color, #db4437);
|
||
}
|
||
.error {
|
||
color: var(--error-color, #db4437);
|
||
font-size: 11px;
|
||
line-height: 1.25;
|
||
}
|
||
.pct {
|
||
color: var(--secondary-text-color, #9aa4ad);
|
||
font-size: 12px;
|
||
}
|
||
:host([disabled]) {
|
||
opacity: .5;
|
||
pointer-events: none;
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.picker { transition: none !important; }
|
||
}
|
||
@media (forced-colors: active) {
|
||
.hue-range::-webkit-slider-runnable-track {
|
||
border-color: ButtonText;
|
||
background: Canvas;
|
||
}
|
||
.hue-range::-moz-range-track {
|
||
border-color: ButtonText;
|
||
background: Canvas;
|
||
}
|
||
.hue-range::-webkit-slider-thumb {
|
||
box-shadow: none;
|
||
}
|
||
.hue-range::-moz-range-thumb {
|
||
box-shadow: none;
|
||
}
|
||
}
|
||
`,customElements.get("hp-color-opacity")||customElements.define("hp-color-opacity",Ue);let Ge=0;class Ve extends he{constructor(){super(...arguments),this.text="",this.ariaLabel="",this.pointerHover=!1,this._open=!1,this._openTimer=0,this._closeTimer=0,this._positionRaf=0,this._forceFallback=!1,this._hoverOwned=!1,this._overlayDispose=null,this._scrollDialog=null,this._descriptionId="hp-help-description-"+ ++Ge,this._surfacePointerEnter=()=>{const e=this._window();this._closeTimer&&e?.clearTimeout(this._closeTimer),this._closeTimer=0},this._surfacePointerLeave=()=>{this._scheduleClose()},this._outsidePointerDown=e=>{if(!this._open)return;const t=e.composedPath();this._floating.containsPath(t)||this._closeHelp(!1,"outside")},this._dialogScroll=e=>{const t=this._floating.containsPath(e.composedPath()),i=e.target,s=this._dialog(),n=i instanceof Node&&(i===s||i instanceof Element&&i.contains(this));var o;this._open&&(o=n,!t&&o)&&this._closeHelp(!1,"scroll")},this._keyDown=e=>{if(!this._open||e.defaultPrevented)return;if("Escape"===e.key)return e.preventDefault(),e.stopImmediatePropagation(),void this._closeHelp(this._floating.ownsActiveElement(),"escape");const t=this.renderRoot.querySelector(".trigger");if(!t||!e.composedPath().includes(t))return;const i=this._surface();if(!i||i.scrollHeight<=i.clientHeight)return;const s=Math.max(40,.8*i.clientHeight),n={ArrowDown:40,ArrowUp:-40,PageDown:s,PageUp:-s};if("Home"===e.key)i.scrollTop=0;else if("End"===e.key)i.scrollTop=i.scrollHeight;else{if(!(e.key in n))return;i.scrollBy({top:n[e.key],behavior:"auto"})}e.preventDefault(),e.stopImmediatePropagation()},this._floating=new qe(this,"help",this._keyDown),this._queuePosition=()=>{if(!this._open||this._positionRaf)return;const e=this._window();e&&(this._positionRaf=e.requestAnimationFrame(()=>{this._positionRaf=0,this._position()||this._closeHelp()}))}}connectedCallback(){super.connectedCallback(),this.addEventListener("keydown",this._keyDown,!0)}willUpdate(){this.toggleAttribute("data-has-content",this._hasContent())}disconnectedCallback(){const e=this.ownerDocument.defaultView;this._unsubscribeOpenListeners(),this.removeEventListener("keydown",this._keyDown,!0),this._clearTimers(),this._positionRaf&&e?.cancelAnimationFrame(this._positionRaf),this._positionRaf=0,this._closeHelp(!1,"disconnect"),super.disconnectedCallback()}updated(e){if(e.has("pointerHover")&&!this.pointerHover){const e=this._window();this._openTimer&&e?.clearTimeout(this._openTimer),this._openTimer=0,this._hoverOwned&&this._closeHelp(!1,"outside")}this._hasContent()||!this._open?this._open&&e.has("text")&&(this._usesPopover()||this._renderFallback(),this._queuePosition()):this._closeHelp()}_window(){return this._floating.window()}_dialog(){return this._floating.dialog()}_usesPopover(){return this._floating.usesPopover(this._forceFallback)}_hasContent(){return e=this.text,t=this.ariaLabel,"string"==typeof e&&e.trim().length>0&&"string"==typeof t&&t.trim().length>0;var e,t}_clearTimers(){const e=this._window();this._openTimer&&e?.clearTimeout(this._openTimer),this._closeTimer&&e?.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=0}_scheduleOpen(){const e=this._window();this.pointerHover&&e&&!this._open&&!this._openTimer&&(this._closeTimer&&e.clearTimeout(this._closeTimer),this._closeTimer=0,this._openTimer=e.setTimeout(()=>{this._openTimer=0,this.isConnected&&(this._hoverOwned=!0,this._openHelp())},300))}_scheduleClose(){const e=this._window(),t=this.renderRoot.querySelector(".trigger");e&&!t?.matches(":focus-visible")&&(this._openTimer&&e.clearTimeout(this._openTimer),this._closeTimer&&e.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=e.setTimeout(()=>{this._closeTimer=0,this.isConnected&&this._closeHelp()},150))}_triggerPointerEnter(e){this.pointerHover&&"mouse"===e.pointerType&&this._scheduleOpen()}_triggerPointerLeave(e){"mouse"===e.pointerType&&this._scheduleClose()}_triggerFocus(){queueMicrotask(()=>{const e=this.renderRoot.querySelector(".trigger");e?.matches(":focus-visible")&&(this._hoverOwned=!1,this._openHelp())})}_triggerBlur(){this._scheduleClose()}_triggerClick(){this._open?this._closeHelp():(this._hoverOwned=!1,this._openHelp())}_subscribeOpenListeners(){this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("keydown",this._keyDown,!0);const e=this._window();e?.addEventListener("resize",this._queuePosition),e?.addEventListener("orientationchange",this._queuePosition),e?.visualViewport?.addEventListener("resize",this._queuePosition),e?.visualViewport?.addEventListener("scroll",this._queuePosition),this._scrollDialog=this._dialog(),this._scrollDialog?.addEventListener("scroll",this._dialogScroll,!0)}_unsubscribeOpenListeners(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("keydown",this._keyDown,!0);const e=this._window();e?.removeEventListener("resize",this._queuePosition),e?.removeEventListener("orientationchange",this._queuePosition),e?.visualViewport?.removeEventListener("resize",this._queuePosition),e?.visualViewport?.removeEventListener("scroll",this._queuePosition),this._scrollDialog?.removeEventListener("scroll",this._dialogScroll,!0),this._scrollDialog=null}async _openHelp(){!this._open&&this._hasContent()&&(this._clearTimers(),this._forceFallback=!1,this._open=!0,this._subscribeOpenListeners(),await this.updateComplete,this._open&&(this._usesPopover()||this._renderFallback(),this._position()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:e=>this._closeHelp("escape"===e&&this._floating.ownsActiveElement(),e)})||null:this._closeHelp()))}_closeHelp(e=!1,t="exclusive"){if(!this._open&&!this._floating.hasFallback)return;this._clearTimers();const i=this._overlayDispose;this._overlayDispose=null,i?.();const s=this.renderRoot.querySelector(".tooltip");if(s?.hidePopover)try{s.matches(":popover-open")&&s.hidePopover()}catch{}this._open=!1,this._hoverOwned=!1,this._unsubscribeOpenListeners(),this._floating.destroy(),e&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_tooltipTemplate(e){return q`<div class="tooltip" data-side="bottom" popover=${e?"manual":G}
|
||
role="tooltip" aria-hidden="true" tabindex="-1"
|
||
@pointerenter=${this._surfacePointerEnter} @pointerleave=${this._surfacePointerLeave}>${this.text}</div>`}_renderFallback(){const e=Ve.styles.cssText;this._floating.renderFallback(this._tooltipTemplate(!1),e)}_surface(){return this._floating.surface(".tooltip",this._usesPopover())}_position(){if(!this._open)return!1;const e=this._window(),t=this.renderRoot.querySelector(".trigger");let i=this._surface();if(!e||!t?.isConnected||!i?.isConnected)return!1;const s=Be(e);if(i.style.maxWidth=`${Math.max(0,Math.min(320,s.width-16))}px`,i.style.maxHeight=`${Math.max(0,s.height-16)}px`,i.style.visibility="hidden",this._usesPopover()&&i.showPopover)try{i.matches(":popover-open")||i.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),i=this._surface(),!i?.isConnected)return!1;i.style.visibility="hidden"}const n=t.getBoundingClientRect(),o=i.getBoundingClientRect();if(!(n.width&&n.height&&o.width&&o.height))return!1;const a=je(n,o,s);return i.style.left=`${a.left}px`,i.style.top=`${a.top}px`,i.dataset.side=a.side,i.style.visibility="",!0}render(){if(!this._hasContent())return G;const e=this.ariaLabel.trim();return q`
|
||
<span id=${this._descriptionId} class="sr-only" role="tooltip"
|
||
aria-hidden=${this._open?"false":"true"}>${this.text}</span>
|
||
<button class="trigger" type="button" aria-label=${e}
|
||
aria-describedby=${this._open?this._descriptionId:G}
|
||
aria-expanded=${this._open?"true":"false"}
|
||
@pointerenter=${this._triggerPointerEnter} @pointerleave=${this._triggerPointerLeave}
|
||
@focus=${this._triggerFocus} @blur=${this._triggerBlur} @click=${this._triggerClick}>
|
||
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
||
<path d=${"M11,18H13V16H11V18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,6A4,4 0 0,0 8,10H10A2,2 0 0,1 12,8A2,2 0 0,1 14,10C14,12 11,11.75 11,15H13C13,12.75 16,12.5 16,10A4,4 0 0,0 12,6Z"}></path>
|
||
</svg>
|
||
</button>
|
||
${this._usesPopover()?this._tooltipTemplate(!0):G}
|
||
`}}Ve.properties={text:{type:String},ariaLabel:{type:String,attribute:"aria-label"},pointerHover:{type:Boolean,attribute:"data-pointer-hover",reflect:!0},_open:{state:!0},_forceFallback:{state:!0}},Ve.styles=o`
|
||
:host {
|
||
display: none;
|
||
flex: none;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
:host([data-has-content]) {
|
||
display: inline-flex;
|
||
}
|
||
|
||
.trigger {
|
||
width: 32px;
|
||
height: 32px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex: none;
|
||
box-sizing: border-box;
|
||
padding: 0;
|
||
color: var(--secondary-text-color, #9aa4ad);
|
||
background: transparent;
|
||
border: 0;
|
||
border-radius: 50%;
|
||
cursor: help;
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
|
||
.trigger svg {
|
||
display: block;
|
||
width: 18px;
|
||
height: 18px;
|
||
fill: currentColor;
|
||
}
|
||
|
||
:host([data-pointer-hover]) .trigger:hover,
|
||
.trigger:focus-visible,
|
||
.trigger[aria-expanded='true'] {
|
||
color: var(--primary-text-color, #fff);
|
||
background: rgb(127 127 127 / 0.18);
|
||
outline: none;
|
||
}
|
||
|
||
.trigger:focus-visible {
|
||
box-shadow: 0 0 0 2px var(--primary-color, #03a9f4);
|
||
}
|
||
|
||
.tooltip {
|
||
position: fixed;
|
||
z-index: 2147483647;
|
||
inset: auto;
|
||
top: 0;
|
||
left: 0;
|
||
width: max-content;
|
||
max-width: min(320px, calc(100vw - 16px));
|
||
max-height: calc(100vh - 16px);
|
||
margin: 0;
|
||
box-sizing: border-box;
|
||
padding: 9px 11px;
|
||
overflow: auto;
|
||
color: var(--primary-text-color, #fff);
|
||
background: var(--card-background-color, #202126);
|
||
border: 1px solid var(--divider-color, rgb(255 255 255 / 0.2));
|
||
border-radius: 9px;
|
||
box-shadow: 0 8px 24px rgb(0 0 0 / 0.32);
|
||
font: 400 13px/1.4 system-ui, sans-serif;
|
||
text-align: start;
|
||
white-space: normal;
|
||
overflow-wrap: anywhere;
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
transition: opacity 120ms ease, transform 120ms ease;
|
||
}
|
||
|
||
.tooltip[data-side='top'] {
|
||
transform-origin: bottom center;
|
||
}
|
||
|
||
.tooltip[data-side='bottom'] {
|
||
transform-origin: top center;
|
||
}
|
||
|
||
.tooltip[popover]:not(:popover-open) {
|
||
display: none;
|
||
}
|
||
|
||
@starting-style {
|
||
.tooltip {
|
||
opacity: 0;
|
||
transform: translateY(3px);
|
||
}
|
||
}
|
||
|
||
.sr-only {
|
||
/* Keep the accessibility-only description outside every dialog scroll
|
||
container's overflow geometry. Toggling the hidden attribute on the former
|
||
absolutely-positioned node made some browsers add a vertical
|
||
scrollbar while help was open, even though the visible tooltip was
|
||
already in the Popover top layer / fixed fallback portal. */
|
||
position: fixed;
|
||
inset: 0 auto auto 0;
|
||
width: 1px;
|
||
height: 1px;
|
||
padding: 0;
|
||
margin: 0;
|
||
overflow: hidden;
|
||
clip: rect(0, 0, 0, 0);
|
||
clip-path: inset(50%);
|
||
white-space: nowrap;
|
||
border: 0;
|
||
}
|
||
|
||
@media (pointer: coarse) {
|
||
.trigger {
|
||
width: 40px;
|
||
height: 40px;
|
||
}
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.tooltip {
|
||
transition: none;
|
||
}
|
||
}
|
||
`,customElements.get("hp-help")||customElements.define("hp-help",Ve);const Ke=[o`
|
||
:host {
|
||
--hp-bg: var(--card-background-color, #16212e);
|
||
--hp-line: var(--divider-color, #2b3d4f);
|
||
--hp-txt: var(--primary-text-color, #e6edf3);
|
||
--hp-muted: var(--secondary-text-color, #8aa0b3);
|
||
--hp-accent: var(--primary-color, #3ea6ff);
|
||
--hp-on: #ffd45c;
|
||
--hp-open: #ff9f43;
|
||
/* design tokens (UI chrome only). The icon/plan scale math stays on
|
||
--icon-size/--dev-size cqw units and never uses these. */
|
||
/* spacing scale, fact-based; stray 3/5/7/9/13/14px values are unified
|
||
onto the nearest step (max +-2px, the whole point of the pass) */
|
||
--sp-1: 2px;
|
||
--sp-2: 4px;
|
||
--sp-3: 6px;
|
||
--sp-4: 8px;
|
||
--sp-5: 12px;
|
||
--sp-6: 16px;
|
||
/* px radii of dialogs/buttons/plates (the 22% badge radius is scale math) */
|
||
--rad-s: 6px;
|
||
--rad-m: 8px;
|
||
--rad-l: 12px;
|
||
/* font tiers: fine print+labels / body+buttons / title */
|
||
--fs-s: 12px;
|
||
--fs-m: 13px;
|
||
--fs-l: 15px;
|
||
/* elevation: badge / floating panel (menu, tip, toast) / dialog */
|
||
--shadow-1: 0 1px 3px rgba(0, 0, 0, 0.45);
|
||
--shadow-2: 0 6px 22px rgba(0, 0, 0, 0.45);
|
||
--shadow-3: 0 8px 30px rgba(0, 0, 0, 0.5);
|
||
}
|
||
ha-card {
|
||
overflow: visible; /* overflow:hidden breaks position:sticky on the header */
|
||
}
|
||
.empty {
|
||
padding: 40px 24px;
|
||
color: var(--hp-txt);
|
||
text-align: center;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: var(--sp-4);
|
||
}
|
||
.empty .big {
|
||
--mdc-icon-size: 56px;
|
||
color: var(--hp-accent);
|
||
opacity: 0.7;
|
||
}
|
||
.empty .muted {
|
||
color: var(--hp-muted);
|
||
font-size: var(--fs-m);
|
||
margin: 0;
|
||
}
|
||
.empty .btn {
|
||
margin-top: var(--sp-4);
|
||
}
|
||
@keyframes fixedfloor-spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
.spacer {
|
||
flex: 1;
|
||
}
|
||
.bootveil {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 40;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: var(--hp-bg, #16212e);
|
||
opacity: 1;
|
||
transition: opacity 0.15s ease;
|
||
pointer-events: none;
|
||
}
|
||
.bootveil.off {
|
||
opacity: 0;
|
||
}
|
||
.bootveil .boothouse {
|
||
position: static; /* the main .zoomwrap SVG is pinned to inset:0 — not this icon */
|
||
width: 56px;
|
||
height: 56px;
|
||
fill: var(--hp-accent);
|
||
opacity: 0.85;
|
||
animation: hp-boot-pulse 1.3s ease-in-out infinite;
|
||
}
|
||
@keyframes hp-boot-pulse {
|
||
0%, 100% { opacity: 0.3; transform: scale(0.94); }
|
||
50% { opacity: 0.9; transform: scale(1); }
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.bootveil .boothouse {
|
||
animation: none;
|
||
opacity: 0.7;
|
||
}
|
||
}
|
||
@keyframes hp-pulse-short {
|
||
0% { transform: scale(1); opacity: 0.55; }
|
||
70% { opacity: 0.22; }
|
||
100% { transform: scale(var(--ripple-scale, 1.5)); opacity: 0; }
|
||
}
|
||
/* Alternate identity: a rapid retrigger restarts the browser timeline. */
|
||
@keyframes hp-pulse-short-b {
|
||
0% { transform: scale(1); opacity: 0.55; }
|
||
70% { opacity: 0.22; }
|
||
100% { transform: scale(var(--ripple-scale, 1.5)); opacity: 0; }
|
||
}
|
||
@keyframes hp-pulse-continuous {
|
||
0% { transform: scale(1); opacity: 0.55; }
|
||
65% { opacity: 0.18; }
|
||
100% { transform: scale(var(--ripple-scale, 1.5)); opacity: 0; }
|
||
}
|
||
@keyframes hp-pulse-alarm {
|
||
0% { transform: scale(1); opacity: 0.72; }
|
||
100% { transform: scale(1.5); opacity: 0; }
|
||
}
|
||
/* Space changes keep their direction, but travel only a few percent: the
|
||
motion explains what changed without making the whole plan fly around. */
|
||
@keyframes hp-slide-left {
|
||
0% { transform: translateX(4%); opacity: 0.72; }
|
||
100% { transform: translateX(0); opacity: 1; }
|
||
}
|
||
@keyframes hp-slide-right {
|
||
0% { transform: translateX(-4%); opacity: 0.72; }
|
||
100% { transform: translateX(0); opacity: 1; }
|
||
}
|
||
@keyframes hp-sunfade-in {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
@keyframes hp-sunfade-out {
|
||
from { opacity: 1; }
|
||
to { opacity: 0; }
|
||
}
|
||
.sr-only {
|
||
position: absolute !important;
|
||
width: 1px !important;
|
||
height: 1px !important;
|
||
padding: 0 !important;
|
||
margin: -1px !important;
|
||
overflow: hidden !important;
|
||
clip: rect(0, 0, 0, 0) !important;
|
||
white-space: nowrap !important;
|
||
border: 0 !important;
|
||
}
|
||
.toast {
|
||
position: fixed;
|
||
pointer-events: none;
|
||
left: 50%;
|
||
bottom: 22px;
|
||
transform: translateX(-50%);
|
||
background: var(--hp-bg);
|
||
border: 1px solid var(--hp-accent);
|
||
color: var(--hp-txt);
|
||
padding: var(--sp-4) var(--sp-6);
|
||
border-radius: var(--rad-l);
|
||
font-size: var(--fs-m);
|
||
box-shadow: var(--shadow-2);
|
||
z-index: 120;
|
||
max-width: 90vw;
|
||
}
|
||
`,o`
|
||
.fixedfloor-loading {
|
||
animation: fixedfloor-spin 1.1s linear infinite;
|
||
}
|
||
.fixedfloor-error p {
|
||
max-width: 42rem;
|
||
margin: 0;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.fixedfloor-loading { animation: none; }
|
||
}
|
||
.stage.noplan {
|
||
background: #ffffff;
|
||
}
|
||
/* Editors always keep the white drawing sheet — even when a backdrop
|
||
image is loaded. Without this, a picture plan painted the theme card
|
||
colour under the grid (owner 2026-08-05). View keeps the historical
|
||
theme / .noplan split. */
|
||
.stage.mode-plan,
|
||
.stage.mode-devices,
|
||
.stage.mode-decor {
|
||
background: #ffffff;
|
||
}
|
||
.stage {
|
||
position: relative;
|
||
width: 100%;
|
||
container-type: inline-size;
|
||
overflow: hidden;
|
||
touch-action: none; /* custom pinch/pan gestures */
|
||
background: var(--ha-card-background, var(--card-background-color, #111));
|
||
}
|
||
.zoomwrap {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 1;
|
||
}
|
||
.stage.mode-transition,
|
||
.stage.mode-transition .zoomwrap {
|
||
transition: none !important;
|
||
}
|
||
.stage.mode-transition .hp-view-only-layer,
|
||
.stage.mode-transition .hp-editor-only-layer {
|
||
pointer-events: none;
|
||
will-change: opacity;
|
||
}
|
||
.stage.mode-transition .hp-paper {
|
||
fill: var(--hp-mode-paper) !important;
|
||
}
|
||
/* Four-phase environment (#146): constant layers cross-fade because CSS
|
||
gradients themselves are not reliably interpolated. Everything stays
|
||
behind the plan; the plan tree is never dimmed, tinted or faded. */
|
||
.hp-day-cycle-env {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 0;
|
||
overflow: hidden;
|
||
pointer-events: none;
|
||
isolation: isolate;
|
||
}
|
||
.hp-day-cycle-bg {
|
||
position: absolute;
|
||
inset: 0;
|
||
opacity: 0;
|
||
overflow: hidden;
|
||
transition: opacity 1100ms cubic-bezier(.22, .61, .36, 1);
|
||
}
|
||
.hp-day-cycle-bg.active { opacity: 1; }
|
||
.hp-day-cycle-sun {
|
||
position: absolute;
|
||
left: var(--hp-day-cycle-sun-x);
|
||
top: var(--hp-day-cycle-sun-y);
|
||
width: clamp(140px, 25cqw, 250px);
|
||
height: clamp(140px, 25cqw, 250px);
|
||
transform: translate(-50%, -50%);
|
||
border-radius: 50%;
|
||
filter: blur(10px);
|
||
opacity: var(--hp-day-cycle-sun-opacity);
|
||
transition:
|
||
left 1100ms cubic-bezier(.22, .61, .36, 1),
|
||
top 1100ms cubic-bezier(.22, .61, .36, 1),
|
||
opacity 1100ms cubic-bezier(.22, .61, .36, 1);
|
||
}
|
||
.stage.mode-transition .hp-day-cycle-env { transition: none; }
|
||
.stage.daycycle .hp-paperg,
|
||
.hp-static-stage.daycycle .hp-paperg {
|
||
filter:
|
||
drop-shadow(0 0 1px var(--hp-day-cycle-outline-near))
|
||
drop-shadow(0 0 5px var(--hp-day-cycle-outline-mid))
|
||
drop-shadow(0 0 10px var(--hp-day-cycle-outline-far));
|
||
transition: filter 1100ms cubic-bezier(.22, .61, .36, 1);
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.hp-day-cycle-bg,
|
||
.hp-day-cycle-sun,
|
||
.stage.daycycle .hp-paperg,
|
||
.hp-static-stage.daycycle .hp-paperg {
|
||
transition: none;
|
||
}
|
||
}
|
||
.sunlayer {
|
||
pointer-events: none;
|
||
}
|
||
.compass {
|
||
width: 120px;
|
||
height: 120px;
|
||
flex: none;
|
||
touch-action: none;
|
||
cursor: grab;
|
||
user-select: none;
|
||
}
|
||
.compass:active {
|
||
cursor: grabbing;
|
||
}
|
||
.compass .cring {
|
||
fill: rgba(255, 255, 255, 0.04);
|
||
stroke: var(--divider-color, #444);
|
||
stroke-width: 2;
|
||
}
|
||
.compass .ctick {
|
||
stroke: var(--secondary-text-color, #9aa4ad);
|
||
stroke-width: 2;
|
||
}
|
||
.compass .ctick.minor {
|
||
stroke-width: 1;
|
||
opacity: 0.6;
|
||
}
|
||
.compass .cneedle line {
|
||
stroke: var(--primary-color, #3ea6ff);
|
||
stroke-width: 2.5;
|
||
stroke-linecap: round;
|
||
}
|
||
.compass .cneedle path {
|
||
fill: var(--primary-color, #3ea6ff);
|
||
}
|
||
.compass .cneedle text {
|
||
fill: var(--text-primary-color, #fff);
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
}
|
||
.compass .cdeg {
|
||
fill: var(--secondary-text-color, #9aa4ad);
|
||
font-size: 13px;
|
||
}
|
||
.compass.unset .cneedle {
|
||
opacity: 0.35;
|
||
}
|
||
/* HP-1552: first-open boot veil — the plan hides until the stage height settles */
|
||
.stage.hpboot .zoomwrap,
|
||
.stage.hpboot .zoombadge {
|
||
visibility: hidden;
|
||
}
|
||
/* AUD-1552-02: post-veil grace — HA chrome landing after the cap moves
|
||
the stage height smoothly; the viewport ResizeObserver refits the plan
|
||
along the transition, so a late panel glides instead of jumping. */
|
||
.stage.hpsettle {
|
||
transition: height 0.25s ease;
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.stage.hpsettle {
|
||
transition: none;
|
||
}
|
||
}
|
||
.zoomctl {
|
||
display: inline-flex;
|
||
gap: var(--sp-1);
|
||
background: rgba(127, 127, 127, 0.12);
|
||
border-radius: var(--rad-m);
|
||
padding: var(--sp-1);
|
||
}
|
||
.zoomctl .zb {
|
||
border: none;
|
||
padding: var(--sp-3) var(--sp-4);
|
||
}
|
||
.zoomctl .zb[disabled] {
|
||
opacity: 0.4;
|
||
pointer-events: none;
|
||
}
|
||
.projection-toggle {
|
||
min-width: 44px;
|
||
min-height: 44px;
|
||
justify-content: center;
|
||
padding: var(--sp-3);
|
||
}
|
||
/* docs/CANVAS.md §5: the plane has no edges, so you can pan until nothing
|
||
is on screen. One pointer home, one click back. */
|
||
.homearrow {
|
||
position: absolute;
|
||
z-index: 12;
|
||
transform: translate(-50%, -50%);
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
background: var(--card-background-color, var(--hp-bg));
|
||
color: var(--hp-accent);
|
||
border: 1px solid var(--hp-accent);
|
||
opacity: 0.9;
|
||
padding: 0;
|
||
}
|
||
.homearrow ha-icon {
|
||
--mdc-icon-size: 22px;
|
||
line-height: 1;
|
||
}
|
||
.zoombadge {
|
||
position: absolute;
|
||
z-index: 12;
|
||
left: var(--sp-4);
|
||
bottom: var(--sp-4);
|
||
background: var(--card-background-color, var(--hp-bg));
|
||
opacity: 0.92;
|
||
color: var(--hp-txt);
|
||
border: 1px solid var(--hp-accent);
|
||
border-radius: var(--rad-m);
|
||
padding: var(--sp-1) var(--sp-4);
|
||
font-size: var(--fs-s);
|
||
font-weight: 600;
|
||
pointer-events: none;
|
||
}
|
||
/* Only the plan canvas owns the whole zoom wrapper. Context trays now live
|
||
inside .stage as well, and may contain small SVG previews (furniture in
|
||
particular); the old descendant-wide stage-SVG selector stretched every
|
||
preview over the tray and stacked them into one blocking artefact. The
|
||
vacuum trail/fit overlays have their own explicit geometry below. */
|
||
.zoomwrap > svg {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
display: block;
|
||
}
|
||
.plan-svg { z-index: 1; }
|
||
.iso-underlay-svg { z-index: 0; overflow: visible; }
|
||
.iso-shadows-svg { z-index: 3; overflow: visible; }
|
||
.iso-walls-svg {
|
||
z-index: 4;
|
||
overflow: visible;
|
||
}
|
||
.iso-underlay-svg,
|
||
.iso-shadows-svg,
|
||
.iso-walls-svg,
|
||
.iso-underlay,
|
||
.iso-shadows,
|
||
.iso-walls,
|
||
.iso-openings {
|
||
pointer-events: none;
|
||
}
|
||
.iso-side-hi { stop-color: #b9bdbe; }
|
||
.iso-side-lo { stop-color: #969c9f; }
|
||
.iso-top-hi { stop-color: #fafaf7; }
|
||
.iso-top-lo { stop-color: #e2e4e2; }
|
||
.iso-wall-side {
|
||
fill: url(#hp-iso-wall-side) #a8acae;
|
||
stroke: #92989b;
|
||
stroke-width: 0.7;
|
||
vector-effect: non-scaling-stroke;
|
||
}
|
||
.iso-wall-top {
|
||
fill: url(#hp-iso-wall-top) #f3f3f1;
|
||
stroke: #d7d9d8;
|
||
stroke-width: 0.8;
|
||
vector-effect: non-scaling-stroke;
|
||
}
|
||
.iso-floor-side {
|
||
fill: #858b8d;
|
||
stroke: #71787b;
|
||
stroke-width: 0.7;
|
||
vector-effect: non-scaling-stroke;
|
||
}
|
||
.iso-opening-panel {
|
||
fill: #d7d9d7;
|
||
fill-opacity: 0.96;
|
||
stroke: #7f878b;
|
||
stroke-width: 0.9;
|
||
vector-effect: non-scaling-stroke;
|
||
}
|
||
.iso-opening-panel.iso-window {
|
||
fill: #dfeff4;
|
||
fill-opacity: 0.72;
|
||
stroke: #8aa7b1;
|
||
}
|
||
.iso-ambient-shadow {
|
||
fill: rgba(15, 21, 25, 0.22);
|
||
filter: url(#hp-iso-ambient-shadow);
|
||
}
|
||
.iso-contact-shadow {
|
||
fill: none;
|
||
stroke: rgba(22, 28, 31, 0.25);
|
||
stroke-width: 3;
|
||
filter: url(#hp-iso-contact-shadow);
|
||
vector-effect: non-scaling-stroke;
|
||
}
|
||
.iso-leaf-shadow {
|
||
fill: none;
|
||
stroke: rgba(18, 23, 27, 0.24);
|
||
stroke-width: 4;
|
||
filter: url(#hp-iso-leaf-shadow);
|
||
vector-effect: non-scaling-stroke;
|
||
}
|
||
@media (prefers-color-scheme: dark) {
|
||
.iso-side-hi { stop-color: #4c555a; }
|
||
.iso-side-lo { stop-color: #343c40; }
|
||
.iso-top-hi { stop-color: #687176; }
|
||
.iso-top-lo { stop-color: #50585d; }
|
||
.iso-wall-side { stroke: #30373b; }
|
||
.iso-wall-top { stroke: #7b858a; }
|
||
.iso-floor-side { fill: #2d3438; stroke: #20272a; }
|
||
.iso-opening-panel { fill: #626b70; stroke: #899399; }
|
||
.iso-opening-panel.iso-window { fill: #75919b; stroke: #abc6ce; }
|
||
.iso-ambient-shadow { fill: rgba(0, 0, 0, 0.34); }
|
||
.iso-contact-shadow, .iso-leaf-shadow { stroke: rgba(0, 0, 0, 0.38); }
|
||
}
|
||
@media (forced-colors: active) {
|
||
.iso-wall-side, .iso-wall-top, .iso-floor-side, .iso-opening-panel {
|
||
fill: Canvas;
|
||
stroke: CanvasText;
|
||
forced-color-adjust: auto;
|
||
}
|
||
.iso-ambient-shadow, .iso-contact-shadow, .iso-leaf-shadow { display: none; }
|
||
}
|
||
@supports not (filter: blur(1px)) {
|
||
.iso-ambient-shadow, .iso-contact-shadow, .iso-leaf-shadow { display: none; }
|
||
}
|
||
/* Opaque plan paper (owner 2026-08-03): the scene bg_color / daynight sky
|
||
shows ONLY around the plan, never through it. The colour is the
|
||
pre-bg_color canvas — the theme card background under an image plan,
|
||
plain white for a hand-drawn one (.stage.noplan). On drawn plans the
|
||
paper is per-room shapes following the room contours — fill only,
|
||
never a stroke, so the paper cannot poke past a wall. Night dimming
|
||
comes from the .zoomwrap brightness filter, never from alpha
|
||
(docs/SUN.md). */
|
||
.hp-paper {
|
||
fill: var(--ha-card-background, var(--card-background-color, #111));
|
||
stroke: none;
|
||
}
|
||
.stage.noplan .hp-paper,
|
||
.stage.mode-plan .hp-paper,
|
||
.stage.mode-devices .hp-paper,
|
||
.stage.mode-decor .hp-paper {
|
||
fill: #ffffff;
|
||
}
|
||
/* Owner 2026-08-04: «углы границ комнат всё ещё с зубцами». A miter join
|
||
on a 30-45° corner shoots a spike far past the wall (and flips to an
|
||
ugly bevel once past the miter limit) — the same defect the decor lines
|
||
had before they got round caps. Every room border, in EVERY renderer
|
||
that reuses these styles (plan view, plan editor, static space-card),
|
||
joins its walls with a ROUND join instead: the corner reads as the
|
||
stroke's own radius, never as a tooth. The linecap matters only for the
|
||
open outlines below, but it costs nothing to state it here. */
|
||
.room {
|
||
transition: 0.12s;
|
||
cursor: default; /* v1.40.1: rooms are not clickable — the label's link icon is */
|
||
stroke-linejoin: round;
|
||
stroke-linecap: round;
|
||
}
|
||
.room.overlay {
|
||
fill: transparent;
|
||
stroke: transparent;
|
||
stroke-width: calc(2px * var(--hp-cell-visual-scale, 1));
|
||
}
|
||
:host([data-pointer-hover]) .stage.mode-view .room.overlay:not(.styled):hover {
|
||
stroke: var(--hp-accent);
|
||
stroke-opacity: 1;
|
||
}
|
||
.room.yard {
|
||
fill: rgba(75, 140, 90, 0.14);
|
||
stroke: #4b8c5a;
|
||
stroke-width: calc(2px * var(--hp-cell-visual-scale, 1));
|
||
}
|
||
:host([data-pointer-hover]) .stage.mode-view .room.yard:not(.styled):hover {
|
||
stroke: var(--hp-accent);
|
||
stroke-opacity: 1;
|
||
}
|
||
.room.styled {
|
||
stroke: var(--room-stroke, transparent);
|
||
stroke-opacity: var(--room-stroke-op, 0);
|
||
stroke-width: calc(2.5px * var(--hp-cell-visual-scale, 1));
|
||
fill: var(--room-fill, transparent);
|
||
fill-opacity: var(--room-fill-op, 0);
|
||
}
|
||
.glow-base-layer,
|
||
.glow-base-tunnels,
|
||
.glow-pools-frame,
|
||
.glow-pools,
|
||
.glow-spot {
|
||
pointer-events: none;
|
||
}
|
||
/* The parent isolates all source spots from the room data fill, Glow base,
|
||
paper and backdrop. A spot is one circle clipped to the floor its lamp
|
||
can see, so the whole spot screen-blends as a single primitive — there is
|
||
no mask left to be dropped on a promoted layer. Per-stop alpha already
|
||
contains the shared 0.7 ceiling; the spot opacity below only animates
|
||
between 0 and 1 and is never another persistent alpha ceiling. */
|
||
.glow-pools-frame,
|
||
.glow-pools,
|
||
.glow-spot {
|
||
isolation: isolate;
|
||
}
|
||
.glow-pools.blend-screen .glow-spot {
|
||
mix-blend-mode: screen;
|
||
}
|
||
.glow-spot {
|
||
opacity: 1;
|
||
transition: opacity 500ms ease;
|
||
}
|
||
.glow-spot.is-entering,
|
||
.glow-spot.is-leaving {
|
||
opacity: 0;
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.glow-spot {
|
||
transition: none;
|
||
}
|
||
}
|
||
/* The explicit late room-hover layer owns the wash and halo. Keeping CSS
|
||
filters off room paths prevents Chromium from recompositing the sibling
|
||
screen-blended Glow layer for one bright frame on every hover. */
|
||
:host([data-pointer-hover]) .stage.mode-view .room.styled:hover {
|
||
stroke: var(--hp-accent);
|
||
stroke-opacity: 1;
|
||
}
|
||
/* doors, windows & gates */
|
||
.op-leaf {
|
||
transition: transform 0.6s ease;
|
||
}
|
||
.op-arc {
|
||
stroke-width: calc(1.5px * var(--hp-cell-visual-scale, 1));
|
||
transition: stroke-dashoffset 0.6s ease;
|
||
}
|
||
/* hover affordance: a rounded outline hugging the wall strip + a grab cursor */
|
||
.op-outline {
|
||
fill: none;
|
||
stroke: var(--hp-accent);
|
||
stroke-width: calc(1.5px * var(--hp-cell-visual-scale, 1));
|
||
opacity: 0;
|
||
transition: opacity 0.15s;
|
||
pointer-events: none;
|
||
}
|
||
:host([data-pointer-hover]) .stage.markup g.opening:hover .op-outline {
|
||
opacity: 0.9;
|
||
}
|
||
/* openings are pure status graphics outside Plan mode: no cursor, no hover,
|
||
no hit target — View must not interact with them at all */
|
||
.op-hit {
|
||
fill: transparent;
|
||
pointer-events: none;
|
||
cursor: default;
|
||
}
|
||
.stage.markup .op-hit {
|
||
pointer-events: auto;
|
||
cursor: grab;
|
||
touch-action: none; /* drags, not scrolls, on touch */
|
||
}
|
||
.stage.markup .op-hit:active {
|
||
cursor: grabbing;
|
||
}
|
||
.stage.markup .opening.orphan {
|
||
pointer-events: auto;
|
||
cursor: pointer;
|
||
color: var(--error-color, #db4437);
|
||
}
|
||
.stage.markup .opening.orphan circle {
|
||
fill: var(--hp-bg);
|
||
stroke: currentColor;
|
||
stroke-width: calc(2px * var(--hp-cell-visual-scale, 1));
|
||
}
|
||
.stage.markup .opening.orphan text {
|
||
fill: currentColor;
|
||
font-weight: 800;
|
||
font-size: calc(12px * var(--hp-cell-visual-scale, 1));
|
||
pointer-events: none;
|
||
}
|
||
/* HP-1550-04: in the resize tool the wall handles own the hit test — the
|
||
transparent .op-hit of a door at the midpoint of a wall used to sit ON
|
||
TOP of the handle and made that wall ungrabbable for both rooms.
|
||
Openings are not editable in this tool (they ride along with the wall),
|
||
so their hit area goes fully inert; every other Plan tool is untouched. */
|
||
.stage.markup.tool-resize .op-hit {
|
||
pointer-events: none;
|
||
cursor: default;
|
||
}
|
||
.stage.mode-view .oplock {
|
||
pointer-events: auto;
|
||
cursor: pointer;
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.op-leaf, .op-arc { transition: none; }
|
||
}
|
||
.roomlabel {
|
||
pointer-events: none; /* draggable only in plan mode (rule below) */
|
||
position: absolute;
|
||
transform: translate(-50%, -50%);
|
||
font-size: calc(var(--rl-icon-size, var(--icon-size, 2.5cqw)) * 0.5 * var(--rl-scale, 1) * var(--rl-font, 1) * var(--rl-space, 1));
|
||
font-weight: 700;
|
||
letter-spacing: 0.04em;
|
||
white-space: nowrap;
|
||
cursor: grab;
|
||
user-select: none;
|
||
z-index: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 0.15em;
|
||
text-align: center;
|
||
}
|
||
.rlname {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.25em;
|
||
font-size: calc(1em * var(--rl-name, 1));
|
||
}
|
||
.zoomwrap.slide-left { animation: hp-slide-left 0.18s cubic-bezier(0.2, 0.7, 0.2, 1); }
|
||
.zoomwrap.slide-right { animation: hp-slide-right 0.18s cubic-bezier(0.2, 0.7, 0.2, 1); }
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.zoomwrap.slide-left,
|
||
.zoomwrap.slide-right { animation: none; }
|
||
}
|
||
/* The name is the anchor: the label box is centred on the room point, so
|
||
anything that takes part in its layout SHIFTS THE NAME. The gear button
|
||
and the metrics hang below as absolutes — the name renders in exactly
|
||
the same place in view mode and in the plan editor (owner's request),
|
||
and the button sits at the very bottom of the card. */
|
||
/* Standalone, centred on the room, sized from the device icon: icon-size
|
||
already rescales with the view, so the button zooms WITH the plan
|
||
instead of keeping a constant screen size (owner's spec). */
|
||
.rlgearbtn {
|
||
--gear-h: calc(var(--icon-size, 2.5cqw) * 0.77); /* owner: half the previous size */
|
||
position: absolute;
|
||
/* dead-centred on the room, both axes (owner's spec) */
|
||
transform: translate(-50%, -50%);
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.35em;
|
||
height: var(--gear-h);
|
||
padding: 0 calc(var(--gear-h) * 0.38);
|
||
border: 0;
|
||
border-radius: 999px;
|
||
background: var(--hp-accent);
|
||
color: var(--text-primary-color, #fff);
|
||
font: inherit;
|
||
font-size: calc(var(--gear-h) * 0.42);
|
||
font-weight: 600;
|
||
line-height: 1;
|
||
white-space: nowrap;
|
||
cursor: pointer;
|
||
pointer-events: auto;
|
||
opacity: 0.92;
|
||
box-shadow: var(--shadow-1);
|
||
z-index: 2;
|
||
}
|
||
.rlgearbtn { transition: opacity 0.15s, filter 0.15s; }
|
||
:host([data-pointer-hover]) .rlgearbtn:hover { opacity: 1; filter: brightness(1.18); }
|
||
.rlgearbtn ha-icon { --mdc-icon-size: calc(var(--gear-h) * 0.55); display: inline-flex; }
|
||
.rlgear {
|
||
--mdc-icon-size: 0.9em;
|
||
display: inline-flex;
|
||
margin-right: 0.2em;
|
||
opacity: 0.6;
|
||
cursor: pointer;
|
||
pointer-events: auto;
|
||
}
|
||
:host([data-pointer-hover]) .rlgear:hover { opacity: 1; }
|
||
.rlgo {
|
||
--mdc-icon-size: 0.85em;
|
||
display: inline-flex;
|
||
opacity: 0.55;
|
||
}
|
||
.stage.mode-view .rlgo {
|
||
pointer-events: auto;
|
||
cursor: pointer;
|
||
}
|
||
:host([data-pointer-hover]) .stage.mode-view .rlgo:hover { opacity: 1; }
|
||
.roomlabel .rlmetrics {
|
||
position: absolute; /* below the name, outside the centring math */
|
||
top: calc(100% + 0.15em);
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
white-space: nowrap;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.55em;
|
||
font-size: calc(0.75em * var(--rl-meta, 1)); /* feedback: 0.62 was unreadable on a tablet */
|
||
font-weight: 600;
|
||
letter-spacing: 0.02em;
|
||
opacity: 0.9;
|
||
}
|
||
.roomlabel .rlm {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.12em;
|
||
}
|
||
.roomlabel .rlm ha-icon {
|
||
--mdc-icon-size: 1.05em;
|
||
display: inline-flex;
|
||
}
|
||
.roomlabel .rlm.lit { opacity: 1; }
|
||
.rlhandle {
|
||
display: none;
|
||
position: absolute;
|
||
width: 9px;
|
||
height: 9px;
|
||
border-radius: 2px;
|
||
background: var(--hp-accent);
|
||
border: 1px solid var(--card-background-color, #fff);
|
||
z-index: 2;
|
||
}
|
||
.rlhandle.tl { left: -6px; top: -6px; cursor: nwse-resize; }
|
||
.rlhandle.br { right: -6px; bottom: -6px; cursor: nwse-resize; }
|
||
.rlhandle.tr { right: -6px; top: -6px; cursor: nesw-resize; }
|
||
.rlhandle.bl { left: -6px; bottom: -6px; cursor: nesw-resize; }
|
||
:host([data-pointer-hover]) .stage.markup .roomlabel:hover .rlhandle { display: block; }
|
||
.stage.markup .roomlabel { pointer-events: auto; }
|
||
.roomlabel:active { cursor: grabbing; }
|
||
.measurelayer {
|
||
position: absolute;
|
||
inset: 0;
|
||
pointer-events: none;
|
||
}
|
||
.alignline {
|
||
stroke: var(--hp-accent);
|
||
stroke-width: calc(1.2px * var(--hp-cell-visual-scale, 1));
|
||
stroke-dasharray:
|
||
calc(4px * var(--hp-cell-visual-scale, 1))
|
||
calc(4px * var(--hp-cell-visual-scale, 1));
|
||
pointer-events: none;
|
||
opacity: 0.9;
|
||
}
|
||
.aligndot {
|
||
fill: var(--hp-accent);
|
||
pointer-events: none;
|
||
}
|
||
.measurelabel.on45 {
|
||
color: #4bd28f;
|
||
border-color: #4bd28f;
|
||
}
|
||
.measurelabel {
|
||
position: absolute;
|
||
transform: translate(12px, -150%);
|
||
font-size: var(--fs-s);
|
||
font-weight: 600;
|
||
padding: 1px var(--sp-3);
|
||
border-radius: var(--rad-s);
|
||
background: rgba(0, 0, 0, 0.72);
|
||
color: #fff;
|
||
white-space: nowrap;
|
||
pointer-events: none;
|
||
user-select: none;
|
||
z-index: 3;
|
||
}
|
||
.measurelabel.opdimension {
|
||
transform: translate(
|
||
calc(-50% + var(--op-label-shift-x, 0px)),
|
||
calc(-50% + var(--op-label-shift-y, -12px))
|
||
);
|
||
border: 1px solid var(--hp-open, #ff9800);
|
||
}
|
||
/* decor (background) layer */
|
||
.decorlayer .dshape { pointer-events: none; }
|
||
.stage.mode-decor .decorlayer .dshape {
|
||
pointer-events: visiblePainted;
|
||
cursor: pointer;
|
||
}
|
||
.stage.mode-decor.dtool-select .decorlayer .dshape { cursor: move; }
|
||
/* …but a DRAWING tool owns the canvas: existing shapes stop being targets
|
||
entirely, so a new line can start exactly on the end of an old one
|
||
instead of grabbing it (owner, 2026-08-04). Same for the picture tool —
|
||
a shape lying over the plan must not block the picture's own drag.
|
||
Only select (move) and erase (delete) keep the shapes clickable. */
|
||
.stage.mode-decor.dtool-line .decorlayer .dshape,
|
||
.stage.mode-decor.dtool-rect .decorlayer .dshape,
|
||
.stage.mode-decor.dtool-ellipse .decorlayer .dshape,
|
||
.stage.mode-decor.dtool-text .decorlayer .dshape,
|
||
.stage.mode-decor.dtool-furniture .decorlayer .dshape,
|
||
.stage.mode-decor.dtool-backdrop .decorlayer .dshape { pointer-events: none; }
|
||
/* the furniture tool is a stamp: the press must reach the stage even when
|
||
it lands on a sofa that is already there (docs/FURNITURE.md §4) */
|
||
.stage.mode-decor.dtool-furniture { cursor: copy; }
|
||
/* ONE exception (owner, 2026-08-04): under the TEXT tool an existing LABEL
|
||
is a target again — pressing it opens its editor instead of starting a
|
||
new label on top of the old one. Only labels: a line or a rectangle
|
||
under the text tool stays inert, so the press reaches the stage and a
|
||
new label is created there. */
|
||
.stage.mode-decor.dtool-text .decorlayer .dshape.dtext,
|
||
.stage.mode-decor.dtool-select .decorlayer .dshape.dtext,
|
||
.stage.mode-decor.dtool-erase .decorlayer .dshape.dtext {
|
||
/* A label is one logical decor object. SVG's visiblePainted would hit
|
||
only the ink of individual glyphs, so spaces, counters and the area
|
||
inside its selection glow behaved like empty canvas. */
|
||
pointer-events: visiblePainted;
|
||
pointer-events: bounding-box;
|
||
}
|
||
.stage.mode-decor.dtool-text .decorlayer .dshape.dtext {
|
||
cursor: text;
|
||
}
|
||
/* Erasing a hairline must not require pixel-perfect aim. The duplicate
|
||
geometry is invisible and exists only while Erase is active. A
|
||
non-scaling stroke keeps the target comfortably wide at every zoom. */
|
||
.decorlayer .derasehit {
|
||
fill: none;
|
||
stroke: transparent;
|
||
stroke-width: 16px;
|
||
stroke-linecap: round;
|
||
stroke-linejoin: round;
|
||
vector-effect: non-scaling-stroke;
|
||
pointer-events: none;
|
||
}
|
||
.stage.mode-decor.dtool-erase .decorlayer .derasehit {
|
||
pointer-events: stroke;
|
||
}
|
||
/* A dashed line must remain selectable across its gaps. This proxy also
|
||
makes the only entry to line-style properties — double click in Select —
|
||
practical for hairlines at every zoom. */
|
||
.decorlayer .dselecthit {
|
||
fill: none;
|
||
stroke: transparent;
|
||
stroke-width: 16px;
|
||
stroke-linecap: round;
|
||
vector-effect: non-scaling-stroke;
|
||
pointer-events: none;
|
||
}
|
||
.stage.mode-decor.dtool-select .decorlayer .dselecthit {
|
||
pointer-events: stroke;
|
||
cursor: move;
|
||
}
|
||
.decorlayer .dsel {
|
||
filter: drop-shadow(0 0 3px var(--hp-accent));
|
||
}
|
||
.decorlayer .ddraft {
|
||
stroke-dasharray:
|
||
calc(6px * var(--hp-cell-visual-scale, 1))
|
||
calc(5px * var(--hp-cell-visual-scale, 1));
|
||
pointer-events: none;
|
||
}
|
||
.decorlayer text {
|
||
font-weight: 600;
|
||
user-select: none;
|
||
dominant-baseline: middle;
|
||
text-anchor: middle;
|
||
}
|
||
.stage.mode-decor {
|
||
outline: 2px solid #26a69a;
|
||
outline-offset: -2px;
|
||
}
|
||
/* backdrop transform frame (docs/BACKDROP.md §2). Editor chrome: the
|
||
outline never takes a pointer, the four corner handles do — and they are
|
||
finger-sized (r = 2 % of the visible view), because this is dragged on a
|
||
tablet as often as with a mouse. */
|
||
.bdframe .bdbox {
|
||
fill: none;
|
||
stroke: var(--hp-accent);
|
||
stroke-width: 2;
|
||
stroke-dasharray: 10 7;
|
||
vector-effect: non-scaling-stroke;
|
||
pointer-events: none;
|
||
opacity: 0.9;
|
||
}
|
||
/* the HIT circle: invisible, finger-sized, the only thing that takes a
|
||
pointer. The visible bead is .bdknob, a quarter of its radius — the same
|
||
visual/hit split .dthandle + .dtknob and .rszhandle + .rszicon use. */
|
||
.bdframe .bdhandle {
|
||
fill: transparent;
|
||
stroke: none;
|
||
pointer-events: all;
|
||
touch-action: none;
|
||
}
|
||
.bdframe .bdknob {
|
||
fill: var(--hp-accent);
|
||
stroke: #fff;
|
||
stroke-width: 1;
|
||
vector-effect: non-scaling-stroke;
|
||
pointer-events: none;
|
||
}
|
||
:host([data-pointer-hover]) .bdframe .bdhandle:hover + .bdknob {
|
||
fill: #fff;
|
||
stroke: var(--hp-accent);
|
||
}
|
||
.bdframe .bd-nwse { cursor: nwse-resize; }
|
||
.bdframe .bd-nesw { cursor: nesw-resize; }
|
||
/* the picture itself is the drag target for a move (grab, then grabbing) */
|
||
.stage.mode-decor.bdgrab { cursor: grab; }
|
||
.stage.mode-decor.bdgrabbing,
|
||
.stage.mode-decor.bdgrabbing .bdframe .bdhandle { cursor: grabbing; }
|
||
.measurelabel.bdmeasure {
|
||
transform: translate(-50%, -50%);
|
||
border: 1px solid var(--hp-accent);
|
||
}
|
||
/* the selected decor object's frame — same chrome rules as the backdrop's:
|
||
the outline never takes a pointer, the handles always do, and they are
|
||
finger-sized because this is dragged on a tablet too */
|
||
.dtframe .dtbox {
|
||
fill: none;
|
||
stroke: var(--hp-accent);
|
||
stroke-width: 1.5;
|
||
stroke-dasharray: 7 5;
|
||
vector-effect: non-scaling-stroke;
|
||
pointer-events: none;
|
||
opacity: 0.85;
|
||
}
|
||
.dtframe .dtstem,
|
||
.bdframe .dtstem {
|
||
stroke: var(--hp-accent);
|
||
stroke-width: 1.5;
|
||
vector-effect: non-scaling-stroke;
|
||
pointer-events: none;
|
||
opacity: 0.85;
|
||
}
|
||
/* the HIT circle: invisible, finger-sized, the only thing that takes a
|
||
pointer. The visible bead is .dtknob, a quarter of its radius — the
|
||
same visual/hit split .rszhandle + .rszicon use. */
|
||
.dtframe .dthandle {
|
||
fill: transparent;
|
||
stroke: none;
|
||
pointer-events: all;
|
||
touch-action: none;
|
||
}
|
||
.dtframe .dtknob {
|
||
fill: var(--hp-accent);
|
||
stroke: #fff;
|
||
stroke-width: 1;
|
||
vector-effect: non-scaling-stroke;
|
||
pointer-events: none;
|
||
}
|
||
:host([data-pointer-hover]) .dtframe .dthandle:hover + .dtknob {
|
||
fill: #fff;
|
||
stroke: var(--hp-accent);
|
||
}
|
||
.dtframe .dt-nwse { cursor: nwse-resize; }
|
||
.dtframe .dt-nesw { cursor: nesw-resize; }
|
||
.dtframe .dtrot { cursor: grab; }
|
||
.dtframe .dtendpoint { cursor: crosshair; }
|
||
.bdframe .dtrot { cursor: grab; }
|
||
.dtarea {
|
||
resize: vertical;
|
||
min-height: 3.4em;
|
||
font: inherit;
|
||
line-height: 1.35;
|
||
}
|
||
.stage.mode-decor.dtool-line, .stage.mode-decor.dtool-rect,
|
||
.stage.mode-decor.dtool-ellipse, .stage.mode-decor.dtool-text {
|
||
cursor: crosshair;
|
||
}
|
||
.stage.mode-decor.dtool-erase,
|
||
.stage.mode-decor.dtool-erase .decorlayer .dshape {
|
||
cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cg transform='rotate(-45 12 12)'%3E%3Crect x='7' y='2' width='10' height='18' rx='2' fill='%23fff' stroke='%23111' stroke-width='1.5'/%3E%3Cpath d='M7 13h10v5a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2z' fill='%23ff9f43' stroke='%23111' stroke-width='1.5'/%3E%3C/g%3E%3C/svg%3E") 5 22, pointer;
|
||
}
|
||
.stage.mode-decor .room, .stage.mode-decor .devlayer { pointer-events: none; }
|
||
.stage.mode-decor .oplock { pointer-events: none; }
|
||
/* Backdrop-editor de-emphasis is a shared mode-transition coordinate.
|
||
It multiplies whole presentation groups and never changes Glow source
|
||
alpha, additive blending, or the underlying resolved state. */
|
||
.stage .room,
|
||
.stage .devlayer,
|
||
.stage .opening,
|
||
.stage .room-outline,
|
||
.stage .wallbodies,
|
||
.stage .opening-tunnels,
|
||
.stage .glow-base-layer,
|
||
.stage .glow-pools-frame,
|
||
.stage .zero-walls {
|
||
opacity: var(--hp-mode-architecture-opacity, 1);
|
||
}
|
||
.opening-preview {
|
||
opacity: 0.5;
|
||
pointer-events: none;
|
||
}
|
||
.opening-preview[data-kind="passage"] {
|
||
opacity: 1;
|
||
}
|
||
.passage-preview-cut {
|
||
fill: var(--wall-fill, #ffffff);
|
||
fill-opacity: 0.35;
|
||
stroke: none;
|
||
pointer-events: none;
|
||
}
|
||
.passage-preview-boundary {
|
||
stroke: var(--hp-open, #ff9800);
|
||
stroke-width: calc(2.5px * var(--hp-cell-visual-scale, 1));
|
||
pointer-events: none;
|
||
}
|
||
.opening-preview .op-leaf,
|
||
.opening-preview .op-arc {
|
||
transition: none;
|
||
}
|
||
.opening-preview-dot {
|
||
fill: var(--hp-open, #ff9800);
|
||
pointer-events: none;
|
||
}
|
||
.opening-dimensions,
|
||
.opening-dimension {
|
||
pointer-events: none;
|
||
}
|
||
.opening-dimension-line,
|
||
.opening-dimension-tick {
|
||
fill: none;
|
||
stroke: var(--hp-open, #ff9800);
|
||
stroke-width: 1.5;
|
||
vector-effect: non-scaling-stroke;
|
||
pointer-events: none;
|
||
}
|
||
.opening-dimension-tick {
|
||
stroke-width: 2;
|
||
}
|
||
.stage.edit .room {
|
||
pointer-events: none;
|
||
}
|
||
.stage.markup {
|
||
cursor: crosshair;
|
||
}
|
||
/* room-picking stages: merge (both clicks) and split before a room is chosen */
|
||
.stage.markup.tool-merge,
|
||
.stage.markup.tool-split.pickstage,
|
||
.stage.markup.tool-delroom {
|
||
cursor: pointer;
|
||
}
|
||
.stage.markup.tool-wallthick { cursor: default; }
|
||
.stage.markup.tool-wallthick.wallhot { cursor: pointer; }
|
||
/* Solid wall colour sits under the hatch (owner: both, not either/or). */
|
||
.wallbody-fill {
|
||
fill: var(--wall-fill, #ffffff);
|
||
fill-opacity: var(--wall-fill-op, 1);
|
||
fill-rule: evenodd;
|
||
stroke: none;
|
||
pointer-events: none;
|
||
}
|
||
.wallbody {
|
||
fill: url(#hp-wall-hatch);
|
||
fill-rule: evenodd;
|
||
stroke: var(--room-stroke, var(--hp-muted));
|
||
stroke-width: calc(0.6px * var(--hp-cell-visual-scale, 1));
|
||
pointer-events: none;
|
||
}
|
||
/* Thin-on-screen: hatch collapses into noise — fill alone, stroke from fill path's sibling. */
|
||
.wallbody.solid {
|
||
fill: none;
|
||
}
|
||
.wallthick-hover {
|
||
fill: var(--accent-color, #03a9f4);
|
||
fill-opacity: 0.38;
|
||
stroke: var(--accent-color, #03a9f4);
|
||
stroke-width: calc(2.5px * var(--hp-cell-visual-scale, 1));
|
||
stroke-linejoin: round;
|
||
stroke-linecap: round;
|
||
pointer-events: none;
|
||
}
|
||
.wallthick-hover.isopen {
|
||
fill: var(--error-color, #f44336);
|
||
stroke: var(--error-color, #f44336);
|
||
}
|
||
.zero-wall {
|
||
stroke: var(--zero-wall-stroke, var(--hp-muted));
|
||
stroke-width: calc(2.5px * var(--hp-cell-visual-scale, 1));
|
||
stroke-dasharray:
|
||
calc(7px * var(--hp-cell-visual-scale, 1))
|
||
calc(7px * var(--hp-cell-visual-scale, 1));
|
||
stroke-linecap: butt;
|
||
pointer-events: none;
|
||
opacity: 0.9;
|
||
}
|
||
.zero-walls.solid .zero-wall {
|
||
stroke-dasharray: none;
|
||
}
|
||
/* Rooms with zero/thick stretches: the polygon's own stroke is fully off.
|
||
The trimmed .room-outline draws normal walls; View hover gets its own
|
||
top overlay after the wall bodies. */
|
||
.room.noedge {
|
||
stroke-opacity: 0 !important;
|
||
}
|
||
/* rooms with open boundaries draw their walls as separate M..L subpaths,
|
||
so a corner between two of them is two stroke ENDS meeting: round caps
|
||
fill it in the same way a round join fills a closed contour's corner
|
||
(owner 2026-08-04 — no teeth anywhere on a room border). */
|
||
.room-outline {
|
||
fill: none;
|
||
stroke-width: calc(2.5px * var(--hp-cell-visual-scale, 1));
|
||
stroke-linejoin: round;
|
||
stroke-linecap: round;
|
||
pointer-events: none;
|
||
}
|
||
/* Plan editor: trimmed outlines use the markup blue */
|
||
.room-outline.outlined {
|
||
stroke: rgba(62, 166, 255, 0.55);
|
||
stroke-opacity: 1;
|
||
}
|
||
/* The wash is rendered directly above the resolved room fill but below
|
||
tunnels, Glow, sun and walls. Black at 22% reproduces the old
|
||
brightness(.78) contract without filtering/promoting an SVG ancestor or
|
||
changing the room's hue. The halo/outline are rendered again late, above
|
||
the wall bodies. */
|
||
.room-hover-fill {
|
||
fill: #000;
|
||
fill-opacity: 0.22;
|
||
pointer-events: none;
|
||
}
|
||
.room-hover-halo {
|
||
fill: none;
|
||
stroke: var(--hp-accent);
|
||
stroke-opacity: 0.28;
|
||
stroke-width: calc(8px * var(--hp-cell-visual-scale, 1));
|
||
stroke-linejoin: round;
|
||
stroke-linecap: round;
|
||
pointer-events: none;
|
||
}
|
||
.room-hover-outline {
|
||
fill: none;
|
||
stroke: var(--hp-accent);
|
||
stroke-width: calc(3px * var(--hp-cell-visual-scale, 1));
|
||
stroke-linejoin: round;
|
||
stroke-linecap: round;
|
||
pointer-events: none;
|
||
}
|
||
.stage.markup .room {
|
||
pointer-events: none;
|
||
}
|
||
.stage.markup .devlayer .dev {
|
||
display: none; /* in plan mode the icons do not get in the way; labels stay */
|
||
}
|
||
/* mode frames: the edit modes are visible at a glance */
|
||
.stage.mode-plan {
|
||
outline: 2px solid #ffc14d;
|
||
outline-offset: -2px;
|
||
}
|
||
.stage.mode-devices {
|
||
outline: 2px solid var(--hp-accent);
|
||
outline-offset: -2px;
|
||
}
|
||
.room.outlined {
|
||
stroke: rgba(62, 166, 255, 0.55);
|
||
fill: rgba(62, 166, 255, 0.06);
|
||
}
|
||
/* AFTER .outlined: same specificity — source order decides (gotcha x4) */
|
||
.room.picked {
|
||
stroke: #ffc14d;
|
||
stroke-width: calc(3px * var(--hp-cell-visual-scale, 1));
|
||
fill: rgba(255, 193, 77, 0.25);
|
||
}
|
||
/* Owner 2026-08-04: the grid is a HINT, not content — at full strength the
|
||
dots argued with the plan on white paper. Both levels are muted, the
|
||
hierarchy is kept (major still denser than fine). */
|
||
.griddot {
|
||
fill: var(--hp-accent);
|
||
opacity: 0.35;
|
||
stroke: rgba(0, 0, 0, 0.35);
|
||
stroke-width: 0.4;
|
||
}
|
||
/* docs/CANVAS.md §7: every coarse node (5x/10x the live step) keeps a
|
||
bigger, more opaque dot, so zoomed far out the grid still reads as a
|
||
grid instead of a grey wash. */
|
||
.griddot.major {
|
||
opacity: 0.5;
|
||
stroke-width: 0;
|
||
}
|
||
/* the contour being drawn in the Plan editor: each wall is its own <line>,
|
||
so the round cap IS the corner (matches the finished .room border) */
|
||
.seg {
|
||
stroke: var(--hp-accent);
|
||
stroke-width: calc(2.5px * var(--hp-cell-visual-scale, 1));
|
||
stroke-linecap: round;
|
||
stroke-linejoin: round;
|
||
}
|
||
.pathline {
|
||
stroke: #ffc14d;
|
||
stroke-width: calc(3px * var(--hp-cell-visual-scale, 1));
|
||
fill: none;
|
||
stroke-linecap: round;
|
||
stroke-linejoin: round;
|
||
}
|
||
.preview {
|
||
stroke: #ffc14d;
|
||
stroke-width: calc(2px * var(--hp-cell-visual-scale, 1));
|
||
stroke-dasharray:
|
||
calc(6px * var(--hp-cell-visual-scale, 1))
|
||
calc(5px * var(--hp-cell-visual-scale, 1));
|
||
opacity: 0.7;
|
||
}
|
||
.active-axis {
|
||
stroke: #ffc14d;
|
||
stroke-width: 2;
|
||
stroke-linecap: round;
|
||
vector-effect: non-scaling-stroke;
|
||
pointer-events: none;
|
||
}
|
||
.active-vertex {
|
||
fill: #ffc14d;
|
||
stroke: #171006;
|
||
stroke-width: 2;
|
||
vector-effect: non-scaling-stroke;
|
||
pointer-events: none;
|
||
}
|
||
.wall-repair-preview {
|
||
stroke: #d93025;
|
||
stroke-width: 4;
|
||
stroke-linecap: round;
|
||
stroke-dasharray: 5 4;
|
||
vector-effect: non-scaling-stroke;
|
||
pointer-events: none;
|
||
}
|
||
.drawwall-preview-fill {
|
||
fill: var(--wall-fill, #ffffff);
|
||
fill-opacity: calc(var(--wall-fill-op, 1) * 0.55);
|
||
fill-rule: evenodd;
|
||
stroke: none;
|
||
pointer-events: none;
|
||
}
|
||
.drawwall-preview {
|
||
fill: url(#hp-wall-hatch);
|
||
fill-opacity: 0.55;
|
||
fill-rule: evenodd;
|
||
stroke: var(--accent-color, #03a9f4);
|
||
stroke-width: calc(0.5px * var(--hp-cell-visual-scale, 1));
|
||
stroke-opacity: 0.7;
|
||
pointer-events: none;
|
||
}
|
||
.drawwall-zero-preview {
|
||
fill: none;
|
||
stroke: var(--accent-color, #03a9f4);
|
||
stroke-width: calc(3px * var(--hp-cell-visual-scale, 1));
|
||
stroke-linecap: butt;
|
||
stroke-linejoin: round;
|
||
opacity: 0.72;
|
||
pointer-events: none;
|
||
}
|
||
.drawwall-zero-preview.dashed {
|
||
stroke-dasharray:
|
||
calc(7px * var(--hp-cell-visual-scale, 1))
|
||
calc(7px * var(--hp-cell-visual-scale, 1));
|
||
}
|
||
.vertex {
|
||
fill: #ffc14d;
|
||
stroke: #4a2800;
|
||
stroke-width: calc(1px * var(--hp-cell-visual-scale, 1));
|
||
}
|
||
.vertex.first {
|
||
fill: #4bd28f;
|
||
stroke: #04121f;
|
||
}
|
||
.plan-snap-overlay,
|
||
.plan-snap-overlay *,
|
||
.hidden-wall-diagnostic,
|
||
.hidden-wall-diagnostic * {
|
||
pointer-events: none;
|
||
}
|
||
.plan-snap-line,
|
||
.hidden-wall-line {
|
||
fill: none;
|
||
stroke: color-mix(in srgb, var(--hp-accent) 82%, white 18%);
|
||
/* Explicit non-scaling-stroke in the SVG keeps this one screen pixel. */
|
||
stroke-width: 1;
|
||
stroke-linecap: round;
|
||
opacity: 0.92;
|
||
}
|
||
.plan-snap-node,
|
||
.hidden-wall-node {
|
||
fill: var(--ha-card-background, var(--card-background-color, #fff));
|
||
stroke: color-mix(in srgb, var(--hp-accent) 88%, #07131c 12%);
|
||
stroke-width: 1;
|
||
vector-effect: non-scaling-stroke;
|
||
}
|
||
.plan-snap-node.active {
|
||
fill: #ffc14d;
|
||
stroke: #171006;
|
||
stroke-width: 2;
|
||
}
|
||
.plan-snap-node.active.dynamic {
|
||
fill: #4bd28f;
|
||
stroke: #04121f;
|
||
}
|
||
.plan-snap-node.conflict {
|
||
fill: #fff;
|
||
stroke: #d93025;
|
||
stroke-width: 3;
|
||
}
|
||
@media (prefers-color-scheme: dark) {
|
||
.plan-snap-line {
|
||
stroke: color-mix(in srgb, var(--hp-accent) 72%, white 28%);
|
||
}
|
||
.hidden-wall-line {
|
||
stroke: color-mix(in srgb, var(--hp-accent) 72%, white 28%);
|
||
}
|
||
.plan-snap-node,
|
||
.hidden-wall-node {
|
||
fill: #17242c;
|
||
stroke: #9bdcf5;
|
||
}
|
||
.plan-snap-node.active {
|
||
fill: #ffc14d;
|
||
stroke: #fff4d6;
|
||
}
|
||
.plan-snap-node.active.dynamic {
|
||
fill: #4bd28f;
|
||
stroke: #eafff4;
|
||
}
|
||
}
|
||
@media (forced-colors: active) {
|
||
.plan-snap-line,
|
||
.hidden-wall-line {
|
||
stroke: CanvasText;
|
||
opacity: 1;
|
||
forced-color-adjust: auto;
|
||
}
|
||
.plan-snap-node,
|
||
.hidden-wall-node {
|
||
fill: Canvas;
|
||
stroke: CanvasText;
|
||
forced-color-adjust: auto;
|
||
}
|
||
.plan-snap-node.active,
|
||
.plan-snap-node.active.dynamic {
|
||
fill: Highlight;
|
||
stroke: HighlightText;
|
||
forced-color-adjust: auto;
|
||
}
|
||
}
|
||
/* RGB lights: the bulb takes the light's actual color */
|
||
/* v1.52.0: the RGB tint of the icon/border is gone — a lamp's colour
|
||
lives ONLY in its glow spot (owner's rule). The ripple-color fallback
|
||
keeps using the light colour; that is set inline via --ripple-color. */
|
||
/* Sun wedges (docs/SUN.md). The layer is present ONLY above the 3°
|
||
threshold; crossing it fades the whole layer in or out over EXACTLY
|
||
2 s (owner 2026-08-03 — RAY_FADE_MS in src/sun.ts must match). The
|
||
geometry is untouched: this is a plain opacity animation on the group,
|
||
so overlapping wedges keep their own blending while it plays. */
|
||
.sunlayer {
|
||
animation: hp-sunfade-in 2s linear both;
|
||
}
|
||
.sunlayer.out {
|
||
animation: hp-sunfade-out 2s linear both;
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
/* no fade at all: the rays are simply there or simply gone */
|
||
.sunlayer, .sunlayer.out { animation: none; }
|
||
.sunlayer.out { opacity: 0; }
|
||
}
|
||
.devlayer {
|
||
position: absolute;
|
||
inset: 0;
|
||
pointer-events: none;
|
||
z-index: 6;
|
||
}
|
||
.stage.mode-devices .dev { cursor: grab; }
|
||
.stage.mode-devices .dev:active { cursor: grabbing; }
|
||
.physical-hit {
|
||
fill: transparent;
|
||
stroke: transparent;
|
||
pointer-events: none;
|
||
cursor: grab;
|
||
}
|
||
.stage.tool-select .physical-hit { pointer-events: all; }
|
||
.physical-hit:active { cursor: grabbing; }
|
||
line.physical-hit { cursor: pointer; }
|
||
.physical-hit.selected {
|
||
fill: rgba(255, 193, 77, 0.24);
|
||
stroke: transparent;
|
||
}
|
||
line.physical-hit.selected {
|
||
stroke: transparent;
|
||
}
|
||
.physical-drag {
|
||
fill: rgba(255, 193, 77, 0.38);
|
||
stroke: #ffc14d;
|
||
stroke-width: 2.5;
|
||
vector-effect: non-scaling-stroke;
|
||
pointer-events: none;
|
||
}
|
||
.physical-chrome { pointer-events: none; }
|
||
.physical-chrome .frame,
|
||
.physical-chrome .stem {
|
||
fill: none;
|
||
stroke: #ffc14d;
|
||
stroke-width: 2;
|
||
vector-effect: non-scaling-stroke;
|
||
}
|
||
.physical-chrome .frame { fill: rgba(255, 193, 77, 0.22); }
|
||
.physical-chrome polyline.frame { fill: none; }
|
||
.physical-chrome .move-dot {
|
||
fill: #ffc14d;
|
||
stroke: #24262d;
|
||
stroke-width: 1.5;
|
||
vector-effect: non-scaling-stroke;
|
||
}
|
||
.physical-chrome .rotate-handle {
|
||
fill: #24262d;
|
||
stroke: #ffc14d;
|
||
stroke-width: 2.5;
|
||
vector-effect: non-scaling-stroke;
|
||
pointer-events: all;
|
||
cursor: crosshair;
|
||
}
|
||
.drawwall.invalid input { border-color: var(--error-color, #db4437); }
|
||
.drawwall .rangehint { margin-inline-start: 4px; font-size: 0.78em; opacity: 0.72; }
|
||
.drawwall.invalid .rangehint { color: var(--error-color, #db4437); opacity: 1; }
|
||
.stage.markup.tool-partition,
|
||
.stage.markup.tool-column { cursor: crosshair; }
|
||
/* room resize tool (docs/RESIZE.md) */
|
||
/* wall handle: invisible finger-sized hit circle (HP-1550-04 hit priority
|
||
kept), the visible glyph lives in the sibling .rszicon */
|
||
.rszhandle {
|
||
fill: transparent;
|
||
stroke: none;
|
||
pointer-events: all;
|
||
cursor: grab;
|
||
touch-action: none;
|
||
}
|
||
.rszhandle:active { cursor: grabbing; }
|
||
.rszhandle.disabled,
|
||
.rszhandle.disabled:active {
|
||
cursor: not-allowed;
|
||
}
|
||
/* wall-with-arrows glyph: accent ink over a bg halo, readable on any plan */
|
||
.rszicon { pointer-events: none; }
|
||
.rszicon path {
|
||
fill: none;
|
||
stroke-linecap: round;
|
||
stroke-linejoin: round;
|
||
vector-effect: non-scaling-stroke;
|
||
}
|
||
.rszhalo { stroke: var(--hp-bg); stroke-width: 6; }
|
||
.rszink { stroke: var(--hp-accent); stroke-width: 2; }
|
||
:host([data-pointer-hover]) .rszhandle:hover + .rszicon .rszink { stroke-width: 3; }
|
||
.rszicon.disabled { opacity: 0.38; }
|
||
:host([data-pointer-hover]) .rszhandle.disabled:hover + .rszicon .rszink { stroke-width: 2; }
|
||
.rszmeasurelayer,
|
||
.rszmeasurelayer * { pointer-events: none; }
|
||
.rszmeasurehalo,
|
||
.rszmeasureink,
|
||
.rszleader {
|
||
fill: none;
|
||
stroke-linecap: round;
|
||
vector-effect: non-scaling-stroke;
|
||
}
|
||
.rszmeasurehalo {
|
||
stroke: var(--hp-bg);
|
||
stroke-width: 7px;
|
||
opacity: 0.9;
|
||
}
|
||
.rszmeasureink {
|
||
stroke: var(--hp-accent);
|
||
stroke-width: 3px;
|
||
}
|
||
.rszleader {
|
||
stroke: var(--hp-accent);
|
||
stroke-width: 2px;
|
||
opacity: 0.95;
|
||
}
|
||
/* the decor draft badge rides the MIDDLE of the shape, so it is centred
|
||
horizontally and lifted clear of the line instead of trailing the
|
||
cursor the way a wall badge does (owner 2026-08-04) */
|
||
.measurelabel.dmeasure {
|
||
transform: translate(-50%, -160%);
|
||
}
|
||
.measurelabel.rszarea {
|
||
transform: translate(
|
||
calc(-50% + var(--rsz-label-x, 0px)),
|
||
calc(-50% + var(--rsz-label-y, 0px))
|
||
);
|
||
background: rgba(0, 0, 0, 0.6);
|
||
border: 1px solid var(--hp-accent);
|
||
}
|
||
/* width and depth of a piece of furniture while its corner is dragged —
|
||
centred on the edge they measure (docs/FURNITURE.md §6) */
|
||
.measurelabel.furnmeasure {
|
||
transform: translate(-50%, -50%);
|
||
border: 1px solid var(--hp-accent);
|
||
}
|
||
.alignmsg { margin: 0 0 8px; font-size: 13px; line-height: 1.45; }
|
||
`,o`
|
||
/* Unified device activity: alarm, short event and continuous state all
|
||
share this one renderer. No static rings are allowed. */
|
||
.dev ha-icon {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
.device-pulse {
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 50%;
|
||
width: var(--dev-size);
|
||
height: var(--dev-size);
|
||
transform: translate(-50%, -50%);
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
}
|
||
.device-pulse i {
|
||
position: absolute;
|
||
inset: 0;
|
||
border-radius: 50%;
|
||
border: 2px solid var(--ripple-color, var(--hp-accent));
|
||
opacity: 0;
|
||
}
|
||
/* A witnessed edge: exactly three waves over the 3.3 s runtime window. */
|
||
.device-pulse.short i {
|
||
animation: hp-pulse-short 1.1s cubic-bezier(.22,.61,.36,1) 1 forwards;
|
||
}
|
||
.device-pulse.short i:nth-child(2) { animation-delay: 1.1s; }
|
||
.device-pulse.short i:nth-child(3) { animation-delay: 2.2s; }
|
||
.device-pulse.short.gen2 i { animation-name: hp-pulse-short-b; }
|
||
.device-pulse.continuous i:nth-child(n + 2),
|
||
.device-pulse.alarm i:nth-child(n + 3) { display: none; }
|
||
.device-pulse.continuous i:first-child {
|
||
animation: hp-pulse-continuous 3.6s cubic-bezier(.45,.05,.55,.95) infinite;
|
||
}
|
||
.device-pulse.alarm i {
|
||
border-width: 3px;
|
||
border-color: #F0410C;
|
||
animation: hp-pulse-alarm 2.4s cubic-bezier(.22,.61,.36,1) infinite;
|
||
}
|
||
.device-pulse.alarm i:nth-child(2) { animation-delay: 1.2s; }
|
||
.activity-dot {
|
||
position: absolute;
|
||
right: 8%;
|
||
bottom: 8%;
|
||
width: 18%;
|
||
height: 18%;
|
||
min-width: 4px;
|
||
min-height: 4px;
|
||
border-radius: 50%;
|
||
background: var(--ripple-color, var(--hp-accent));
|
||
border: 1px solid color-mix(in srgb, var(--hp-bg) 82%, transparent);
|
||
pointer-events: none;
|
||
z-index: 3;
|
||
}
|
||
.kioskdots {
|
||
position: absolute;
|
||
left: 50%;
|
||
bottom: var(--sp-5);
|
||
transform: translateX(-50%);
|
||
display: flex;
|
||
gap: var(--sp-4);
|
||
z-index: 5;
|
||
pointer-events: none;
|
||
}
|
||
.kdot {
|
||
width: 9px;
|
||
height: 9px;
|
||
border-radius: 50%;
|
||
background: var(--hp-muted);
|
||
opacity: 0.55;
|
||
}
|
||
.kdot.on { background: var(--hp-accent); opacity: 1; }
|
||
.dev.valonly {
|
||
/* Saved coordinates remain the centre of a Text shell. The shell may
|
||
expand, while the invisible anchor stays one core diameter wide. */
|
||
width: var(--dev-size, var(--icon-size, 2.5cqw));
|
||
}
|
||
.dev.valonly .device-core {
|
||
width: max-content;
|
||
min-width: var(--dev-size, var(--icon-size, 2.5cqw));
|
||
padding-inline: calc(var(--dev-size, var(--icon-size, 2.5cqw)) * 0.16);
|
||
border-radius: calc(var(--dev-size, var(--icon-size, 2.5cqw)) / 2);
|
||
}
|
||
.dev .valtext {
|
||
overflow: visible;
|
||
font-size: calc(var(--dev-size, var(--icon-size, 2.5cqw)) * var(--value-font-scale, .45));
|
||
font-weight: 600;
|
||
white-space: nowrap;
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.device-pulse { display: none; }
|
||
}
|
||
.dev .newdot {
|
||
position: absolute;
|
||
top: calc(var(--dev-size, var(--icon-size, 2.5cqw)) * -0.12);
|
||
right: calc(var(--dev-size, var(--icon-size, 2.5cqw)) * -0.12);
|
||
width: calc(var(--dev-size, var(--icon-size, 2.5cqw)) * 0.34);
|
||
height: calc(var(--dev-size, var(--icon-size, 2.5cqw)) * 0.34);
|
||
border-radius: 50%;
|
||
background: #f0301f;
|
||
border: 2px solid var(--card-background-color, var(--hp-bg));
|
||
pointer-events: none;
|
||
z-index: 2;
|
||
}
|
||
.dev {
|
||
position: absolute;
|
||
/* The surface resolves compatibility icon_size units once. The shared
|
||
face receives an effective base and only applies the per-device size. */
|
||
--dev-size: calc(var(--device-base-size, 2.25cqw) * var(--dev-scale, 1));
|
||
/* 101.5/80 is the package shell/core ratio, including its stroke. */
|
||
--device-shell-size: calc(var(--dev-size) * 1.26875);
|
||
--device-shell-inset: calc(var(--dev-size) * 0.134375);
|
||
--device-shell-stroke-ratio: 0.01875;
|
||
--device-shell-border-width: max(1px, calc(var(--dev-size) * var(--device-shell-stroke-ratio)));
|
||
--device-core-bg: var(--card-background-color, #fff);
|
||
--device-core-fg: var(--primary-text-color, #252525);
|
||
--device-core-bg: light-dark(#fff, #252525);
|
||
--device-core-fg: light-dark(#252525, #fff);
|
||
--device-face-bg: var(--device-core-bg);
|
||
--device-face-fg: var(--device-core-fg);
|
||
--device-shell-base-stroke: light-dark(#BCBCBC, rgb(37 37 37 / 75%));
|
||
--device-shell-stroke: var(--device-shell-base-stroke);
|
||
--device-shell-shadow:
|
||
0 calc(var(--dev-size) * .025) calc(var(--dev-size) * .05) rgb(37 40 45 / 12%),
|
||
0 calc(var(--dev-size) * .1) calc(var(--dev-size) * .175)
|
||
calc(var(--dev-size) * -.025) rgb(37 40 45 / 18%);
|
||
--device-core-inset-shadow: 0 0 0 0 transparent;
|
||
--device-ring-color: transparent;
|
||
--device-ring-width: 0px;
|
||
/* The saved point is the exact centre of the icon core. */
|
||
width: var(--dev-size);
|
||
height: var(--dev-size);
|
||
margin: calc(var(--dev-size) / -2) 0 0 calc(var(--dev-size) / -2);
|
||
border: 0;
|
||
background: transparent;
|
||
display: block;
|
||
color: var(--device-core-fg);
|
||
cursor: pointer;
|
||
pointer-events: auto;
|
||
transition: opacity 0.2s;
|
||
box-shadow: none;
|
||
outline: none;
|
||
z-index: 2;
|
||
}
|
||
.dev.theme-light {
|
||
--device-core-bg: #fff;
|
||
--device-core-fg: #000;
|
||
--device-shell-base-stroke: #BCBCBC;
|
||
--device-shell-stroke: var(--device-shell-base-stroke);
|
||
--device-shell-shadow:
|
||
0 calc(var(--dev-size) * .025) calc(var(--dev-size) * .05) rgb(37 40 45 / 12%),
|
||
0 calc(var(--dev-size) * .1) calc(var(--dev-size) * .175)
|
||
calc(var(--dev-size) * -.025) rgb(37 40 45 / 18%);
|
||
--device-core-inset-shadow: 0 0 0 0 transparent;
|
||
}
|
||
.dev.theme-dark {
|
||
--device-core-bg: #252525;
|
||
--device-core-fg: #fff;
|
||
--device-shell-base-stroke: rgb(37 37 37 / 75%);
|
||
--device-shell-stroke: var(--device-shell-base-stroke);
|
||
--device-shell-shadow:
|
||
0 calc(var(--dev-size) * .025) calc(var(--dev-size) * .0375) rgb(37 40 45 / 12%),
|
||
0 calc(var(--dev-size) * .1) calc(var(--dev-size) * .175)
|
||
calc(var(--dev-size) * -.025) rgb(37 40 45 / 18%);
|
||
--device-core-inset-shadow:
|
||
inset 0 calc(var(--dev-size) * 0.0125) calc(var(--dev-size) * 0.0125) rgb(255 255 255 / 70%);
|
||
}
|
||
.dev::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 50%;
|
||
width: max(44px, var(--device-shell-size));
|
||
height: max(44px, var(--device-shell-size));
|
||
transform: translate(-50%, -50%);
|
||
border-radius: 50%;
|
||
pointer-events: auto;
|
||
z-index: 3;
|
||
}
|
||
.device-shell {
|
||
position: absolute;
|
||
box-sizing: border-box;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: calc(var(--dev-size) * 0.1);
|
||
padding: 0;
|
||
min-width: var(--dev-size);
|
||
min-height: var(--dev-size);
|
||
border: 0;
|
||
background: transparent;
|
||
box-shadow: none;
|
||
transition: opacity .2s;
|
||
pointer-events: none;
|
||
}
|
||
.device-shell-frame {
|
||
position: absolute;
|
||
z-index: 0;
|
||
box-sizing: border-box;
|
||
inset: calc(var(--device-shell-inset) / -1);
|
||
border: var(--device-shell-border-width) solid var(--device-shell-stroke);
|
||
/* A saturating radius is resolved from the final border box. Using a
|
||
second fractional length here lets border-box and radius quantise on
|
||
different sides of a device pixel at some zoom/DPR combinations. */
|
||
border-radius: 9999px;
|
||
background: transparent;
|
||
box-shadow: var(--device-shell-shadow);
|
||
transition: border-color .15s, box-shadow .15s, opacity .2s;
|
||
pointer-events: auto;
|
||
/* Normative production fallback: never add a per-marker backdrop blur. */
|
||
backdrop-filter: none;
|
||
}
|
||
.device-shell:not(.with-values) {
|
||
left: 0;
|
||
top: 0;
|
||
}
|
||
.device-shell:not(.with-values):not(.text-shell) .device-shell-frame {
|
||
border-radius: 50%;
|
||
}
|
||
.device-shell.with-values.pos-right {
|
||
left: 0;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
}
|
||
.device-shell.with-values.pos-left {
|
||
right: 0;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
flex-direction: row-reverse;
|
||
}
|
||
.device-shell.with-values.pos-bottom {
|
||
left: 50%;
|
||
top: 0;
|
||
transform: translateX(-50%);
|
||
flex-direction: column;
|
||
}
|
||
.device-shell.with-values.pos-top {
|
||
left: 50%;
|
||
bottom: 0;
|
||
transform: translateX(-50%);
|
||
flex-direction: column-reverse;
|
||
}
|
||
.device-core {
|
||
position: relative;
|
||
z-index: 1;
|
||
box-sizing: border-box;
|
||
flex: 0 0 auto;
|
||
width: var(--dev-size);
|
||
height: var(--dev-size);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 50%;
|
||
background: var(--device-face-bg);
|
||
color: var(--device-face-fg);
|
||
box-shadow:
|
||
var(--device-core-inset-shadow),
|
||
0 0 0 var(--device-ring-width) var(--device-ring-color);
|
||
line-height: 0;
|
||
transition: background .15s, color .15s, box-shadow .15s, opacity .2s;
|
||
pointer-events: none;
|
||
}
|
||
.device-sections {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
justify-content: center;
|
||
gap: calc(var(--dev-size) * .08);
|
||
min-width: 0;
|
||
pointer-events: none;
|
||
}
|
||
.device-shell.pos-top .device-sections,
|
||
.device-shell.pos-bottom .device-sections {
|
||
flex-direction: row;
|
||
}
|
||
.dev ha-icon {
|
||
/* from --dev-size, NOT --icon-size: the per-device size multiplier must
|
||
scale the GLYPH with its badge. Pinned to the base size, "make this
|
||
icon bigger" grew an empty box around a default-size glyph (user
|
||
report via the owner, 2026-07-29). */
|
||
--mdc-icon-size: calc(var(--dev-size, var(--device-base-size, 2.25cqw)) * 0.55);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
line-height: 0;
|
||
}
|
||
.dev.on {
|
||
--device-face-bg: #F0A00C;
|
||
--device-face-fg: light-dark(#fff, #252525);
|
||
--device-shell-stroke: #F0A00C;
|
||
}
|
||
.dev.theme-light.on { --device-face-fg: #fff; }
|
||
.dev.theme-dark.on {
|
||
--device-face-fg: #252525;
|
||
--device-shell-stroke-ratio: .0375;
|
||
}
|
||
.dev.open {
|
||
--device-face-bg: var(--hp-open);
|
||
--device-face-fg: light-dark(#fff, #252525);
|
||
}
|
||
.dev.theme-light.open { --device-face-fg: #fff; }
|
||
.dev.theme-dark.open { --device-face-fg: #252525; }
|
||
.dev.lock-locked {
|
||
--device-face-bg: #66D17A;
|
||
--device-face-fg: light-dark(#fff, #252525);
|
||
--device-shell-stroke: #66D17A;
|
||
}
|
||
.dev.theme-light.lock-locked { --device-face-fg: #fff; }
|
||
.dev.theme-dark.lock-locked {
|
||
--device-face-fg: #252525;
|
||
--device-shell-stroke-ratio: .025;
|
||
}
|
||
.dev.lock-unlocked {
|
||
--device-face-bg: #F0410C;
|
||
--device-face-fg: light-dark(#fff, #252525);
|
||
--device-shell-stroke: #F0410C;
|
||
}
|
||
.dev.theme-light.lock-unlocked { --device-face-fg: #fff; }
|
||
.dev.theme-dark.lock-unlocked {
|
||
--device-face-fg: #252525;
|
||
--device-shell-stroke-ratio: .025;
|
||
}
|
||
/* Interaction wins ordinary state colours. Alarm keeps priority through
|
||
the more-specific rule below. Unavailable has no visual hover. */
|
||
:host([data-pointer-hover]) .dev:not(.unavail):hover {
|
||
--device-face-bg: #0C82F0;
|
||
--device-face-fg: light-dark(#fff, #252525);
|
||
--device-shell-stroke: var(--device-shell-base-stroke);
|
||
}
|
||
:host([data-pointer-hover]) .dev.theme-light:not(.unavail):hover { --device-face-fg: #fff; }
|
||
:host([data-pointer-hover]) .dev.theme-dark:not(.unavail):hover { --device-face-fg: #252525; }
|
||
:host([data-pointer-hover]) .dev:hover,
|
||
.dev:focus-visible { z-index: 5; }
|
||
.dev.unavail {
|
||
opacity: 0.35;
|
||
--device-face-bg: #B5BAC1;
|
||
--device-shell-stroke: var(--device-shell-base-stroke);
|
||
}
|
||
.dev.virtual .device-shell-frame {
|
||
border-style: dashed;
|
||
}
|
||
/* "hide from plan" flag, shown only in the device editor with the
|
||
"show hidden devices" toggle on (docs/FILTERING.md). BLUE, so a hidden
|
||
device cannot be mistaken for an unavailable one (translucent dark) —
|
||
and no live-state paint at all: a ghost is configuration, not status
|
||
(owner's request). */
|
||
.dev.ghost {
|
||
opacity: 0.6;
|
||
color: var(--hp-accent);
|
||
}
|
||
.dev.ghost .device-shell-frame {
|
||
border-style: dashed;
|
||
border-color: var(--hp-accent);
|
||
box-shadow: none;
|
||
}
|
||
.dev.ghost .device-core {
|
||
background: rgba(62, 166, 255, 0.22); /* fallback for old WebViews */
|
||
background: color-mix(in srgb, var(--hp-accent) 30%, var(--card-background-color, #1c2530));
|
||
color: var(--hp-accent);
|
||
}
|
||
/* HA-disabled is not a user hide: neutral grey + a power-off badge keeps
|
||
the two ghosts distinguishable without relying on colour alone. */
|
||
.dev.ghost.ha-disabled {
|
||
opacity: 0.62;
|
||
color: var(--secondary-text-color, #9aa0aa);
|
||
}
|
||
.dev.ghost.ha-disabled .device-shell-frame {
|
||
border-color: var(--secondary-text-color, #9aa0aa);
|
||
}
|
||
.dev.ghost.ha-disabled .device-core {
|
||
background: rgba(120, 124, 134, 0.2);
|
||
background: color-mix(in srgb, var(--secondary-text-color, #9aa0aa) 24%, var(--card-background-color, #1c2530));
|
||
color: var(--secondary-text-color, #9aa0aa);
|
||
}
|
||
.dev .habadge {
|
||
position: absolute;
|
||
right: -18%;
|
||
bottom: -18%;
|
||
display: grid;
|
||
place-items: center;
|
||
width: 46%;
|
||
height: 46%;
|
||
min-width: 12px;
|
||
min-height: 12px;
|
||
border-radius: 50%;
|
||
background: var(--card-background-color, #242832);
|
||
border: 1px solid currentColor;
|
||
}
|
||
.dev .habadge ha-icon { width: 72%; height: 72%; }
|
||
.dev.sel {
|
||
--device-ring-color: #F0A00C;
|
||
--device-ring-width: calc(var(--dev-size) * 0.0375);
|
||
}
|
||
.dev:focus-visible {
|
||
--device-ring-color: #0C82F0;
|
||
--device-ring-width: calc(var(--dev-size) * 0.0375);
|
||
}
|
||
.dev:not(.on):not(.open):not(.alarm):not(.lock-locked):not(.lock-unlocked):not(.unavail):focus-visible {
|
||
--device-face-fg: #0C82F0;
|
||
}
|
||
/* Alert stays above focus, selection, hover and ordinary semantic paint. */
|
||
.dev.alarm,
|
||
:host([data-pointer-hover]) .dev.alarm:hover,
|
||
.dev.alarm:focus-visible {
|
||
--device-face-bg: #F0410C;
|
||
--device-face-fg: light-dark(#fff, #252525);
|
||
--device-shell-stroke: #F0410C;
|
||
}
|
||
.dev.theme-light.alarm { --device-face-fg: #fff; }
|
||
.dev.theme-dark.alarm {
|
||
--device-face-fg: #252525;
|
||
--device-shell-stroke-ratio: .025;
|
||
}
|
||
.dev .value-badge {
|
||
position: relative;
|
||
z-index: 2;
|
||
box-sizing: border-box;
|
||
width: max-content;
|
||
min-width: calc(var(--dev-size, var(--icon-size, 2.5cqw)) * .7875);
|
||
height: calc(var(--dev-size, var(--icon-size, 2.5cqw)) * .7875);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
overflow: visible;
|
||
background: var(--device-core-bg);
|
||
border: 0;
|
||
border-radius: calc(var(--dev-size, var(--icon-size, 2.5cqw)) * 0.39375);
|
||
padding: 0 calc(var(--dev-size, var(--icon-size, 2.5cqw)) * 0.14);
|
||
font-size: calc(var(--dev-size, var(--icon-size, 2.5cqw)) * var(--value-font-scale, .45));
|
||
font-weight: 600;
|
||
line-height: 1;
|
||
color: var(--device-core-fg);
|
||
white-space: nowrap;
|
||
pointer-events: none;
|
||
}
|
||
.dev .value-badge.unavailable,
|
||
.dev .value-badge.missing { opacity: 0.66; }
|
||
.dev .lqi {
|
||
position: absolute;
|
||
top: calc(50% + var(--device-shell-size) / 2 + var(--dev-size) * .05);
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
margin-top: calc(var(--dev-size, var(--icon-size, 2.5cqw)) * 0.05);
|
||
font-size: calc(var(--dev-size, var(--icon-size, 2.5cqw)) * 0.38);
|
||
font-weight: 600;
|
||
line-height: 1;
|
||
text-shadow: 0 0 3px rgba(0, 0, 0, 0.9), 0 0 2px rgba(0, 0, 0, 0.9);
|
||
white-space: nowrap;
|
||
pointer-events: none;
|
||
}
|
||
.dev .lqi.below-value-badge {
|
||
margin-top: calc(var(--dev-size, var(--icon-size, 2.5cqw)) * 0.8875);
|
||
}
|
||
.temprange {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
margin-left: auto;
|
||
color: var(--hp-muted);
|
||
font-size: var(--fs-s);
|
||
}
|
||
/* live vacuum: a round puck, no badge plate, soft pulse (docs/VACUUM.md) */
|
||
.vacpuck {
|
||
position: absolute;
|
||
/* the base badge, but round and 20% smaller — the owner's wording:
|
||
«иконка похожа на иконку базы, только круглая и чуть меньше» */
|
||
--puck-size: calc(var(--device-base-size, 2.25cqw) * 0.8);
|
||
width: var(--puck-size);
|
||
height: var(--puck-size);
|
||
border-radius: 50%;
|
||
transform: translate(-50%, -50%);
|
||
--vac-core-bg: light-dark(#fff, #252525);
|
||
--vac-core-fg: light-dark(#252525, #fff);
|
||
background: var(--vac-core-bg);
|
||
border: 1px solid #BCBCBC;
|
||
box-shadow:
|
||
0 1px 2px rgb(37 40 45 / 12%),
|
||
0 4px 8px -1.07px rgb(37 40 45 / 18%);
|
||
color: var(--vac-core-fg);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
z-index: 6;
|
||
/* glide between sparse position updates; .jump disables it so the robot
|
||
never appears to drive through walls after a data gap */
|
||
transition: left 1.2s linear, top 1.2s linear;
|
||
animation: vacpulse 2.2s ease-out infinite;
|
||
}
|
||
.vacpuck.theme-light { --vac-core-bg: #fff; --vac-core-fg: #252525; }
|
||
.vacpuck.theme-dark { --vac-core-bg: #252525; --vac-core-fg: #fff; }
|
||
.vacpuck.jump { transition: none; }
|
||
.vacpuck.stale { opacity: 0.45; animation: none; }
|
||
.vacpuck ha-icon {
|
||
--mdc-icon-size: calc(var(--puck-size) * 0.68);
|
||
color: var(--vac-core-fg);
|
||
/* same centering recipe as .dev ha-icon: without flex + line-height 0
|
||
the glyph sits on its text baseline and appears to float around the
|
||
circle (owner report 2026-07-31) */
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
line-height: 0;
|
||
width: var(--mdc-icon-size);
|
||
height: var(--mdc-icon-size);
|
||
}
|
||
@keyframes vacpulse {
|
||
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--hp-accent) 45%, transparent); }
|
||
70% { box-shadow: 0 0 0 12px transparent; }
|
||
100% { box-shadow: 0 0 0 0 transparent; }
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.vacpuck { animation: none; }
|
||
}
|
||
.vactrail {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
pointer-events: none;
|
||
z-index: 5;
|
||
overflow: visible;
|
||
}
|
||
.vactrail polyline,
|
||
.vactrail path {
|
||
fill: none;
|
||
stroke-linejoin: round;
|
||
stroke-linecap: round;
|
||
vector-effect: non-scaling-stroke;
|
||
}
|
||
/* dark halo + light core: neutral, and one of the two always contrasts
|
||
with whatever fill is underneath */
|
||
.vactrail g.prev { opacity: 0.4; }
|
||
.vactrail .case {
|
||
stroke: rgba(0, 0, 0, 0.4);
|
||
stroke-width: 2.25;
|
||
}
|
||
.vactrail .core {
|
||
stroke: rgba(255, 255, 255, 0.82);
|
||
stroke-width: 0.9;
|
||
}
|
||
.devicepreview-empty {
|
||
min-height: 82px;
|
||
margin: 4px 0 14px;
|
||
padding: var(--sp-4);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: var(--sp-3);
|
||
border: 1px dashed var(--hp-line);
|
||
border-radius: var(--rad-m);
|
||
color: var(--hp-muted);
|
||
text-align: center;
|
||
}
|
||
`,o`
|
||
.hdr {
|
||
position: sticky;
|
||
top: var(--header-height, 56px);
|
||
z-index: 20;
|
||
background: var(--card-background-color, var(--hp-bg));
|
||
border-radius: var(--ha-card-border-radius, 12px) var(--ha-card-border-radius, 12px) 0 0;
|
||
}
|
||
.tabs {
|
||
display: flex;
|
||
gap: var(--sp-2);
|
||
background: rgba(127, 127, 127, 0.12);
|
||
padding: var(--sp-2);
|
||
border-radius: var(--rad-l);
|
||
flex-wrap: wrap;
|
||
}
|
||
.tab {
|
||
border: 0;
|
||
background: transparent;
|
||
color: var(--hp-muted);
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: var(--sp-3) var(--sp-5);
|
||
border-radius: var(--rad-m);
|
||
font-size: var(--fs-m);
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: 0.15s;
|
||
font-family: inherit;
|
||
}
|
||
:host([data-pointer-hover]) .tab:hover {
|
||
color: var(--hp-txt);
|
||
}
|
||
.tab.active {
|
||
background: var(--hp-accent);
|
||
color: var(--text-primary-color, #fff);
|
||
}
|
||
/* docs/CANVAS.md §4.1: objects an order of magnitude away from the plan
|
||
do not decide the opening view — one quiet chip says so and offers to
|
||
take them in. Never a modal (owner). */
|
||
.farhint {
|
||
position: absolute;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
bottom: var(--sp-4);
|
||
z-index: 12;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
max-width: calc(100% - var(--sp-8));
|
||
background: var(--card-background-color, var(--hp-bg));
|
||
opacity: 0.94;
|
||
color: var(--hp-txt);
|
||
border: 1px solid var(--divider-color, #33404d);
|
||
border-radius: var(--rad-m);
|
||
padding: var(--sp-1) var(--sp-3);
|
||
font-size: var(--fs-s);
|
||
}
|
||
.farhint ha-icon {
|
||
--mdc-icon-size: 18px;
|
||
color: var(--hp-warn, #e2a03f);
|
||
flex: none;
|
||
}
|
||
.farhint span {
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.dropbtn {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-4);
|
||
width: 100%;
|
||
text-align: left;
|
||
border: 1px solid var(--hp-muted);
|
||
border-radius: var(--rad-m);
|
||
background: transparent;
|
||
color: var(--hp-txt);
|
||
padding: var(--sp-4) 10px;
|
||
cursor: pointer;
|
||
font-family: inherit;
|
||
font-size: var(--fs-m);
|
||
margin-top: var(--sp-3);
|
||
}
|
||
.dropbtn .ref { color: var(--hp-muted); font-size: var(--fs-s); margin-left: auto; }
|
||
.dropbtn ha-icon { --mdc-icon-size: 18px; margin-left: var(--sp-2); }
|
||
.dropbtn.open { border-color: var(--hp-accent); }
|
||
.droppanel {
|
||
border: 1px solid var(--hp-accent);
|
||
border-top: none;
|
||
border-radius: 0 0 var(--rad-m) var(--rad-m);
|
||
padding: var(--sp-3);
|
||
margin-top: -4px;
|
||
}
|
||
.hdr.kioskhide { display: none; }
|
||
.decorbar .dcolor {
|
||
width: 30px; height: 26px; padding: 0; border: none; background: none; cursor: pointer;
|
||
}
|
||
.decorbar .dwidth {
|
||
font-family: inherit; font-size: var(--fs-s); border-radius: var(--rad-s);
|
||
background: var(--hp-bg2, transparent); color: var(--hp-txt); border: 1px solid var(--hp-muted);
|
||
padding: var(--sp-2) var(--sp-3);
|
||
}
|
||
.decorbar .bdhint {
|
||
font-size: var(--fs-s);
|
||
color: var(--hp-muted);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.decorbar .dfill {
|
||
display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-s); cursor: pointer;
|
||
}
|
||
.decorbar .dfill input[type="checkbox"] {
|
||
width: 16px;
|
||
height: 16px;
|
||
flex: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
.decorbar hp-color-opacity { flex: 0 0 auto; }
|
||
.modes {
|
||
display: inline-flex;
|
||
gap: var(--sp-1);
|
||
background: rgba(127, 127, 127, 0.12);
|
||
border-radius: var(--rad-l);
|
||
padding: var(--sp-2);
|
||
}
|
||
.modetab {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
border: 0;
|
||
background: transparent;
|
||
color: var(--hp-muted);
|
||
padding: var(--sp-3) 10px; /* 10px h-padding kept: +2px would wrap the header modes row */
|
||
border-radius: var(--rad-m);
|
||
font-size: var(--fs-m);
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
font-family: inherit;
|
||
transition: background-color 0.14s ease, color 0.14s ease, transform 0.14s ease;
|
||
}
|
||
.modetab:active { transform: scale(0.97); }
|
||
.modetab ha-icon { --mdc-icon-size: 15px; }
|
||
/* issue #220: a tab can be dragged to a new position in the editors */
|
||
.tab[data-reorderable] { cursor: grab; }
|
||
.tab.dragging { cursor: grabbing; opacity: 0.55; }
|
||
.tab.drop-before { box-shadow: inset 2px 0 0 0 var(--primary-color, #03a9f4); }
|
||
.tab.drop-after { box-shadow: inset -2px 0 0 0 var(--primary-color, #03a9f4); }
|
||
.modetab .closex {
|
||
--mdc-icon-size: 13px;
|
||
box-sizing: border-box;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 24px;
|
||
height: 24px;
|
||
min-width: 24px;
|
||
min-height: 24px;
|
||
/* Keep the old 13 × 13 flex footprint (+2 px left margin) while the
|
||
pointer target grows around it. Header width, height and wrapping do
|
||
not move; only near-misses that used to hit the active tab reach X. */
|
||
margin: -5.5px -5.5px -5.5px -3.5px;
|
||
opacity: 0.75;
|
||
cursor: pointer;
|
||
border-radius: var(--rad-s);
|
||
}
|
||
:host([data-pointer-hover]) .modetab .closex:hover { opacity: 1; }
|
||
.editbar .barclose {
|
||
box-sizing: border-box;
|
||
width: 40px;
|
||
height: 40px;
|
||
padding: 0;
|
||
margin: 0;
|
||
min-width: 40px;
|
||
min-height: 40px;
|
||
justify-content: center;
|
||
gap: 0;
|
||
line-height: 0;
|
||
}
|
||
.editbar .barclose ha-icon { flex: none; margin: 0; }
|
||
.modetab.active {
|
||
background: var(--hp-accent);
|
||
color: var(--text-primary-color, #fff);
|
||
}
|
||
@media (max-width: 720px) {
|
||
.modetab .ml { display: none; }
|
||
}
|
||
.editbar {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) auto;
|
||
align-items: stretch;
|
||
border-bottom: 1px solid var(--hp-line);
|
||
font-size: var(--fs-m);
|
||
}
|
||
.editbar-tools {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: var(--sp-4) var(--sp-5);
|
||
flex-wrap: wrap;
|
||
min-width: 0;
|
||
outline: none;
|
||
}
|
||
.editbar-end {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: var(--sp-4) var(--sp-5);
|
||
border-inline-start: 1px solid var(--hp-line);
|
||
background: var(--card-background-color, var(--hp-bg));
|
||
position: relative;
|
||
z-index: 2;
|
||
}
|
||
.tab .tabedit {
|
||
--mdc-icon-size: 13px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
margin-left: var(--sp-3);
|
||
opacity: 0.4;
|
||
}
|
||
:host([data-pointer-hover]) .tab:hover .tabedit {
|
||
opacity: 0.9;
|
||
}
|
||
.tab.tabadd {
|
||
padding: var(--sp-3) var(--sp-4);
|
||
}
|
||
.tab.tabadd ha-icon {
|
||
--mdc-icon-size: 15px;
|
||
}
|
||
.rhint {
|
||
font-size: var(--fs-s);
|
||
color: var(--hp-muted);
|
||
margin-bottom: var(--sp-3);
|
||
}
|
||
.togglehint {
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.togglehint > div + div {
|
||
margin-top: var(--sp-1);
|
||
}
|
||
/* Keep the last editor bar mounted while the row collapses. This makes
|
||
both entering and leaving an editor change the card geometry gradually;
|
||
the header ResizeObserver keeps the stage fitted throughout. */
|
||
.editorchrome {
|
||
display: block;
|
||
height: 0;
|
||
opacity: 0;
|
||
visibility: hidden;
|
||
overflow: hidden;
|
||
}
|
||
.editorchrome:not(.open) {
|
||
pointer-events: none;
|
||
}
|
||
.editorchrome.open {
|
||
height: auto;
|
||
opacity: 1;
|
||
visibility: visible;
|
||
overflow: visible;
|
||
}
|
||
.editorchrome.transitioning {
|
||
overflow: hidden;
|
||
will-change: height;
|
||
pointer-events: none;
|
||
}
|
||
/* The toolbar is already visible while its height is interpolating. Its
|
||
explicit navigation control must remain usable even though all editing
|
||
tools stay frozen until the transition settles. */
|
||
.editorchrome.transitioning .barclose {
|
||
pointer-events: auto;
|
||
}
|
||
.editorchrome-inner {
|
||
min-height: 0;
|
||
transform-origin: top center;
|
||
}
|
||
.editorchrome.transitioning .editorchrome-inner {
|
||
will-change: opacity;
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.modetab { transition: none; }
|
||
}
|
||
.editbar .warn {
|
||
color: #ffc14d;
|
||
}
|
||
.editbar .sname {
|
||
font-weight: 600;
|
||
}
|
||
.editbar input {
|
||
width: 74px;
|
||
background: transparent;
|
||
border: 1px solid var(--hp-line);
|
||
color: var(--hp-txt);
|
||
border-radius: var(--rad-s);
|
||
padding: var(--sp-3) var(--sp-4);
|
||
font-size: var(--fs-m);
|
||
}
|
||
.editbar label,
|
||
.editbar .hint {
|
||
color: var(--hp-muted);
|
||
font-size: var(--fs-s);
|
||
}
|
||
.editbar .drawwall {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
white-space: nowrap;
|
||
}
|
||
.editbar .wallsgroup {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
white-space: nowrap;
|
||
}
|
||
.editbar .drawwall input {
|
||
width: 4.2em;
|
||
}
|
||
.editbar .drawwall .opl {
|
||
color: var(--hp-muted);
|
||
font-size: var(--fs-s);
|
||
}
|
||
.menuwrap {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 80;
|
||
}
|
||
.menu {
|
||
position: fixed;
|
||
background: var(--hp-bg);
|
||
border: 1px solid var(--hp-accent);
|
||
border-radius: var(--rad-l);
|
||
box-shadow: var(--shadow-2);
|
||
min-width: 210px;
|
||
max-width: 300px;
|
||
overflow: hidden;
|
||
transform: translate(0, 8px);
|
||
}
|
||
.menu .hd {
|
||
padding: var(--sp-4) var(--sp-5);
|
||
font-weight: 600;
|
||
font-size: var(--fs-m);
|
||
border-bottom: 1px solid var(--hp-line);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
}
|
||
.menu .hd ha-icon,
|
||
.menu .it.all ha-icon {
|
||
color: var(--hp-accent);
|
||
--mdc-icon-size: 16px;
|
||
}
|
||
.menu .it {
|
||
padding: var(--sp-4) var(--sp-5);
|
||
font-size: var(--fs-m);
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-4);
|
||
}
|
||
.menu .it ha-icon {
|
||
--mdc-icon-size: 16px;
|
||
color: var(--hp-muted);
|
||
}
|
||
:host([data-pointer-hover]) .menu .it:hover {
|
||
background: rgba(127, 127, 127, 0.15);
|
||
}
|
||
.menu .it.all {
|
||
color: var(--hp-accent);
|
||
font-weight: 600;
|
||
}
|
||
.tip {
|
||
position: fixed;
|
||
pointer-events: none;
|
||
background: var(--hp-bg);
|
||
border: 1px solid var(--hp-accent);
|
||
color: var(--hp-txt);
|
||
padding: var(--sp-3) 10px;
|
||
border-radius: var(--rad-m);
|
||
font-size: var(--fs-m);
|
||
box-shadow: var(--shadow-2);
|
||
z-index: 60;
|
||
max-width: 260px;
|
||
}
|
||
.tip .m {
|
||
color: var(--hp-muted);
|
||
font-size: var(--fs-s);
|
||
display: block;
|
||
}
|
||
`,o`
|
||
.head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 10px var(--sp-5);
|
||
border-bottom: 1px solid var(--hp-line);
|
||
flex-wrap: wrap;
|
||
}
|
||
.title {
|
||
font-size: var(--fs-l);
|
||
font-weight: 600;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
white-space: nowrap;
|
||
}
|
||
.title ha-icon {
|
||
color: var(--hp-accent);
|
||
--mdc-icon-size: 18px;
|
||
}
|
||
@media (max-width: 620px) {
|
||
.head { gap: var(--sp-3); padding: var(--sp-4) 10px; }
|
||
.head .count { display: none; }
|
||
.head .title { font-size: var(--fs-m); }
|
||
}
|
||
.count {
|
||
font-size: var(--fs-s);
|
||
color: var(--hp-muted);
|
||
}
|
||
.btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
border: 1px solid var(--hp-line);
|
||
background: transparent;
|
||
color: var(--hp-txt);
|
||
padding: var(--sp-3) 10px;
|
||
border-radius: var(--rad-m);
|
||
cursor: pointer;
|
||
transition: 0.15s;
|
||
font-family: inherit;
|
||
font-size: var(--fs-m);
|
||
}
|
||
.btn ha-icon {
|
||
--mdc-icon-size: 17px;
|
||
}
|
||
:host([data-pointer-hover]) .btn:hover {
|
||
border-color: var(--hp-accent);
|
||
}
|
||
.btn.on {
|
||
background: var(--hp-accent);
|
||
color: var(--text-primary-color, #fff);
|
||
border-color: var(--hp-accent);
|
||
}
|
||
.btn.ghost {
|
||
border: none;
|
||
}
|
||
.btn[disabled] {
|
||
opacity: 0.5;
|
||
pointer-events: none;
|
||
}
|
||
/* the compass dial in the general settings (docs/SUN.md) */
|
||
.sunrow {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-5);
|
||
margin: var(--sp-3) 0;
|
||
}
|
||
.suncol {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--sp-3);
|
||
min-width: 0;
|
||
}
|
||
.recoveryoverlay {
|
||
position: absolute;
|
||
inset: 0;
|
||
/* Above the contextual editor tray (70): recovery is the one state in
|
||
which every stage-editing surface must be inert. Dialogs live outside
|
||
the stage and retain their own higher card-level stacking context. */
|
||
z-index: 75;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: var(--sp-4);
|
||
padding: var(--sp-6);
|
||
box-sizing: border-box;
|
||
/* The final solid layer guarantees an opaque recovery surface even when
|
||
a custom HA theme exposes its card colour as rgba(). */
|
||
background:
|
||
linear-gradient(var(--ha-card-background, var(--card-background-color, #111)),
|
||
var(--ha-card-background, var(--card-background-color, #111))),
|
||
#111;
|
||
color: var(--primary-text-color, #fff);
|
||
text-align: center;
|
||
pointer-events: auto;
|
||
transition: opacity 0.15s ease;
|
||
}
|
||
.recoveryoverlay.phase-entering,
|
||
.recoveryoverlay.phase-leaving {
|
||
opacity: 0;
|
||
}
|
||
.recoveryoverlay.phase-fading-in,
|
||
.recoveryoverlay.phase-opaque {
|
||
opacity: 1;
|
||
}
|
||
.recoveryoverlay ha-icon {
|
||
--mdc-icon-size: 44px;
|
||
color: var(--hp-accent);
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.recoveryoverlay {
|
||
transition: none;
|
||
}
|
||
}
|
||
.oplock {
|
||
--oplock-size: calc(var(--icon-size, 2.5cqw) * 0.62);
|
||
--oplock-core-size: calc(var(--oplock-size) / 1.26875);
|
||
--oplock-stroke-ratio: 0.01875;
|
||
--oplock-stroke-width: max(1px, calc(var(--oplock-core-size) * var(--oplock-stroke-ratio)));
|
||
--oplock-core-bg: light-dark(#fff, #252525);
|
||
--oplock-core-fg: light-dark(#252525, #fff);
|
||
--oplock-shell-stroke: light-dark(#BCBCBC, rgb(37 37 37 / 75%));
|
||
--oplock-shell-shadow:
|
||
0 calc(var(--oplock-core-size) * .025) calc(var(--oplock-core-size) * .05) rgb(37 40 45 / 12%),
|
||
0 calc(var(--oplock-core-size) * .1) calc(var(--oplock-core-size) * .175)
|
||
calc(var(--oplock-core-size) * -.025) rgb(37 40 45 / 18%);
|
||
--oplock-core-shadow: 0 0 0 0 transparent;
|
||
pointer-events: none; /* inert while editing; clickable in View (rule below) */
|
||
position: absolute;
|
||
transform: translate(-50%, -50%);
|
||
width: var(--oplock-size);
|
||
height: var(--oplock-size);
|
||
display: grid;
|
||
place-items: center;
|
||
border: 0;
|
||
background: transparent;
|
||
z-index: 1;
|
||
}
|
||
.oplock.theme-light {
|
||
--oplock-core-bg: #fff;
|
||
--oplock-core-fg: #252525;
|
||
--oplock-shell-stroke: #BCBCBC;
|
||
}
|
||
.oplock.theme-dark {
|
||
--oplock-core-bg: #252525;
|
||
--oplock-core-fg: #fff;
|
||
--oplock-shell-stroke: rgb(37 37 37 / 75%);
|
||
--oplock-core-shadow:
|
||
inset 0 calc(var(--oplock-core-size) * .0125)
|
||
calc(var(--oplock-core-size) * .0125) rgb(255 255 255 / 70%);
|
||
}
|
||
.oplock-shell {
|
||
box-sizing: border-box;
|
||
width: 100%;
|
||
height: 100%;
|
||
border: var(--oplock-stroke-width) solid var(--oplock-shell-stroke);
|
||
border-radius: 50%;
|
||
background: transparent;
|
||
box-shadow: var(--oplock-shell-shadow);
|
||
display: grid;
|
||
place-items: center;
|
||
pointer-events: none;
|
||
backdrop-filter: none;
|
||
}
|
||
.oplock-core {
|
||
width: var(--oplock-core-size);
|
||
height: var(--oplock-core-size);
|
||
border-radius: 50%;
|
||
background: var(--oplock-core-bg);
|
||
color: var(--oplock-core-fg);
|
||
box-shadow: var(--oplock-core-shadow);
|
||
display: grid;
|
||
place-items: center;
|
||
pointer-events: none;
|
||
}
|
||
.oplock ha-icon {
|
||
--mdc-icon-size: calc(var(--oplock-core-size) * 0.55);
|
||
display: flex;
|
||
line-height: 0;
|
||
}
|
||
.oplock.locked {
|
||
--oplock-core-bg: #66D17A;
|
||
--oplock-core-fg: light-dark(#fff, #252525);
|
||
--oplock-shell-stroke: #66D17A;
|
||
}
|
||
.oplock.theme-light.locked { --oplock-core-fg: #fff; }
|
||
.oplock.theme-dark.locked {
|
||
--oplock-core-fg: #252525;
|
||
--oplock-stroke-ratio: .025;
|
||
}
|
||
.oplock.unlocked {
|
||
--oplock-core-bg: #F0410C;
|
||
--oplock-core-fg: light-dark(#fff, #252525);
|
||
--oplock-shell-stroke: #F0410C;
|
||
}
|
||
.oplock.theme-light.unlocked { --oplock-core-fg: #fff; }
|
||
.oplock.theme-dark.unlocked {
|
||
--oplock-core-fg: #252525;
|
||
--oplock-stroke-ratio: .025;
|
||
}
|
||
.oplock.unknown { --oplock-core-fg: var(--hp-muted); }
|
||
.btn.lockact {
|
||
width: 100%;
|
||
justify-content: center;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
margin-top: var(--sp-4);
|
||
}
|
||
.btn.lockact.warn {
|
||
color: var(--error-color, #d33);
|
||
border-color: var(--error-color, #d33);
|
||
}
|
||
.oprow {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-4);
|
||
padding: var(--sp-3) 0;
|
||
}
|
||
.oprow b { margin-left: auto; }
|
||
.oprow.ok b { color: #66d17a; }
|
||
.oprow.warn b { color: var(--hp-open); }
|
||
.bindharow {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-5);
|
||
flex-wrap: wrap;
|
||
}
|
||
.bindharow .entcheck { opacity: 0.9; }
|
||
.ctrlchips { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin: var(--sp-2) 0; }
|
||
.ctrlchip {
|
||
display: inline-flex; align-items: center; gap: var(--sp-2);
|
||
background: var(--hp-accent); color: var(--text-primary-color, #fff);
|
||
border-radius: var(--rad-l); padding: var(--sp-2) var(--sp-4); font-size: var(--fs-s);
|
||
}
|
||
.ctrlchip ha-icon { --mdc-icon-size: 14px; cursor: pointer; }
|
||
.ctrllist { display: flex; flex-direction: column; gap: var(--sp-1); margin-top: var(--sp-2); }
|
||
.ctrlopt {
|
||
display: flex; align-items: center; gap: var(--sp-4); text-align: left;
|
||
border: 0; background: transparent; color: var(--hp-txt);
|
||
padding: var(--sp-3) var(--sp-4); border-radius: var(--rad-s); cursor: pointer; font-family: inherit; font-size: var(--fs-m);
|
||
}
|
||
:host([data-pointer-hover]) .ctrlopt:hover {
|
||
background: var(--secondary-background-color, rgba(128,128,128,0.15));
|
||
}
|
||
.ctrlopt .sub { color: var(--hp-muted); font-size: var(--fs-s); margin-left: auto; }
|
||
.ctrlopt ha-icon { --mdc-icon-size: 16px; }
|
||
.ctrlstates { display: flex; flex-direction: column; gap: var(--sp-2); }
|
||
.ctrlstate { display: inline-flex; align-items: center; gap: var(--sp-3); color: var(--hp-muted); }
|
||
.ctrlstate.on { color: var(--hp-txt); }
|
||
.ctrlstate ha-icon { --mdc-icon-size: 15px; }
|
||
.cardpreview {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
margin: var(--sp-4) 0 var(--sp-1);
|
||
padding: 10px;
|
||
border: 1px dashed var(--hp-muted);
|
||
border-radius: var(--rad-m);
|
||
}
|
||
.cardpreview .cpname { font-weight: 700; letter-spacing: 0.04em; }
|
||
.cardpreview .cpmeta {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.3em;
|
||
font-weight: 600;
|
||
opacity: 0.85;
|
||
}
|
||
.cardpreview .cpmeta ha-icon { --mdc-icon-size: 1.05em; }
|
||
.iconauto {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
font-size: var(--fs-s);
|
||
margin: var(--sp-2) 0 0;
|
||
}
|
||
.iconauto ha-icon { --mdc-icon-size: 18px; }
|
||
.iconauto span { flex: 1; }
|
||
.iconauto .btn { min-height: 32px; padding: 0 var(--sp-3); }
|
||
hp-dialog .dfill {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
cursor: pointer;
|
||
}
|
||
.wallthick-dlg {
|
||
position: absolute;
|
||
z-index: 40;
|
||
min-width: 200px;
|
||
transform: translate(-50%, 8px);
|
||
padding: 10px 12px;
|
||
border-radius: 10px;
|
||
background: var(--card-background-color, #fff);
|
||
box-shadow: 0 8px 28px rgba(0,0,0,.22);
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
pointer-events: auto;
|
||
}
|
||
.wallthick-dlg .row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
.wallthick-dlg input[type="number"] {
|
||
width: 5.5em;
|
||
padding: 4px 6px;
|
||
border: 1px solid var(--divider-color, #ccc);
|
||
border-radius: 6px;
|
||
background: var(--input-fill, transparent);
|
||
color: var(--primary-text-color);
|
||
}
|
||
.areasel,
|
||
.namein {
|
||
background: var(--hp-bg);
|
||
border: 1px solid var(--hp-line);
|
||
color: var(--hp-txt);
|
||
border-radius: var(--rad-s);
|
||
padding: var(--sp-3) var(--sp-4);
|
||
font-size: var(--fs-m);
|
||
font-family: inherit;
|
||
}
|
||
.namein {
|
||
width: 130px;
|
||
}
|
||
.habindingbanner {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
padding: var(--sp-3) var(--sp-4);
|
||
margin-bottom: var(--sp-4);
|
||
border: 1px solid var(--warning-color, #ff9800);
|
||
border-radius: var(--rad-m);
|
||
background: color-mix(in srgb, var(--warning-color, #ff9800) 12%, transparent);
|
||
color: var(--primary-text-color, #f1f3f6);
|
||
}
|
||
.habindingbanner > span { flex: 1; min-width: 0; }
|
||
.habindingbanner > ha-icon { color: var(--warning-color, #ff9800); flex: 0 0 auto; }
|
||
.habindingbanner.limited {
|
||
border-color: var(--secondary-text-color, #9aa0aa);
|
||
background: color-mix(in srgb, var(--secondary-text-color, #9aa0aa) 10%, transparent);
|
||
}
|
||
.habindingbanner.limited > ha-icon { color: var(--secondary-text-color, #9aa0aa); }
|
||
.srcrow {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-4);
|
||
font-size: var(--fs-m);
|
||
cursor: pointer;
|
||
padding: var(--sp-1) 0;
|
||
}
|
||
.dispsection {
|
||
margin-top: var(--sp-5) !important;
|
||
padding-top: var(--sp-4);
|
||
border-top: 1px solid var(--hp-line);
|
||
font-weight: 600;
|
||
color: var(--hp-txt) !important;
|
||
}
|
||
.colorrow {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-4);
|
||
}
|
||
.gsrow > hp-color-opacity {
|
||
min-width: min(100%, 210px);
|
||
justify-content: space-between;
|
||
}
|
||
.ripple-colorrow > hp-color-opacity {
|
||
width: 100%;
|
||
justify-content: space-between;
|
||
}
|
||
.ripple-sizerow > .opl {
|
||
min-width: 0;
|
||
}
|
||
.colorrow input[type='range'] { flex: 1; }
|
||
.colorrow .tempin { width: 70px; flex: none; }
|
||
/* beat the generic hp-dialog .body .namein { width:100% } rule */
|
||
hp-dialog .body .temprange .tempin { width: 56px; flex: none; padding: var(--sp-2) var(--sp-3); }
|
||
hp-dialog .body .colorrow .tempin { width: 72px; flex: none; }
|
||
.srcrow { flex-wrap: nowrap; }
|
||
/* native HA controls (rendered only when the HA frontend defines them;
|
||
old HA and the smoke env keep the plain inputs). ha-switch is taller
|
||
than a checkbox - cap its footprint so .srcrow keeps its rhythm. */
|
||
.srcrow ha-switch { flex: none; }
|
||
.colorrow ha-slider { flex: 1; min-width: 0; }
|
||
.srcrow > span:first-of-type { white-space: nowrap; }
|
||
.colorrow .opl { color: var(--hp-muted); font-size: var(--fs-s); }
|
||
.colorrow .opv { font-size: var(--fs-s); min-width: 34px; text-align: right; }
|
||
.markerlightgroup {
|
||
min-width: 0;
|
||
margin: var(--sp-5) 0 0;
|
||
padding: var(--sp-4);
|
||
border: 1px solid var(--hp-line);
|
||
border-radius: var(--rad-m);
|
||
}
|
||
.markerlightgroup legend {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: var(--sp-1);
|
||
padding: 0 var(--sp-2);
|
||
color: var(--hp-txt);
|
||
font-weight: 600;
|
||
}
|
||
.markerlightgroup legend > span { min-width: 0; overflow-wrap: anywhere; }
|
||
.markerlightgroup[disabled] > :not(legend) { opacity: .62; }
|
||
.markerhelpfield { margin-top: var(--sp-4); }
|
||
.markerhelplabel {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: var(--sp-1);
|
||
margin-bottom: var(--sp-1);
|
||
}
|
||
.markerhelplabel > label { min-width: 0; overflow-wrap: anywhere; }
|
||
.markerradios { display: grid; gap: var(--sp-1); min-width: 0; }
|
||
.markerlightgroup .srcrow > span:first-of-type {
|
||
min-width: 0;
|
||
white-space: normal;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.markerglowvalue { margin: var(--sp-3) 0; flex-wrap: wrap; }
|
||
.markerglowvalue hp-color-opacity { flex: none; }
|
||
.markerlightdisabled {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
margin-top: var(--sp-2) !important;
|
||
}
|
||
.markerlightdisabled ha-icon { --mdc-icon-size: 18px; flex: none; }
|
||
.markerbadgetechnical {
|
||
min-width: 0;
|
||
margin: var(--sp-1) 0 var(--sp-2) !important;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.markerbadgetechnical code { white-space: normal; }
|
||
.planrow {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
/* the "already uploaded" picker: a plan is never deleted for being
|
||
unreferenced, so it has to be findable again */
|
||
.savedplans {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--sp-3);
|
||
max-height: 240px;
|
||
overflow: auto;
|
||
margin: var(--sp-3) 0 var(--sp-1);
|
||
padding: var(--sp-3);
|
||
border: 1px solid var(--hp-line);
|
||
border-radius: var(--rad-m);
|
||
background: var(--hp-bg2, rgba(255, 255, 255, 0.03));
|
||
/* The same collapse that ate .candlist (v1.53.1): a scroll box is a
|
||
flex item whose automatic minimum size is ZERO (overflow != visible),
|
||
so inside hp-dialog .body — a flex column taller than its 66vh cap —
|
||
it shrank to a 14px sliver: the rows were in the DOM, the owner saw
|
||
a thin rounded stripe under the "Already uploaded" button. Don't
|
||
shrink, and keep a floor even when the box is empty or loading. */
|
||
flex: 0 0 auto;
|
||
min-height: 2.6em;
|
||
}
|
||
.savedplan {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
.savedplan.cur { outline: 1px solid var(--hp-accent); border-radius: var(--rad-s); }
|
||
.savedplan img {
|
||
width: 56px;
|
||
height: 40px;
|
||
object-fit: contain;
|
||
border: 1px solid var(--hp-line);
|
||
border-radius: var(--rad-s);
|
||
background: #fff;
|
||
flex: none;
|
||
}
|
||
.savedmeta { display: flex; flex-direction: column; min-width: 0; flex: 1; }
|
||
.savedmeta b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||
.savedmeta .muted { font-size: var(--fs-s); }
|
||
.savedplan .btn.danger ha-icon { color: #f25a4a; }
|
||
.savedplan .btn[disabled] { opacity: 0.4; pointer-events: none; }
|
||
.planprev {
|
||
max-width: 120px;
|
||
max-height: 70px;
|
||
border: 1px solid var(--hp-line);
|
||
border-radius: var(--rad-s);
|
||
background: #fff;
|
||
}
|
||
.planname {
|
||
font-size: var(--fs-m);
|
||
max-width: 150px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.planname.muted {
|
||
color: var(--hp-muted);
|
||
}
|
||
.filebtn {
|
||
cursor: pointer;
|
||
}
|
||
.fileupload { display: inline-flex; min-width: 0; }
|
||
.fileupload > input { display: none; }
|
||
.btn.danger {
|
||
border-color: #b3402a;
|
||
color: #ff7a5c;
|
||
}
|
||
hp-dialog .row .spacer {
|
||
flex: 1;
|
||
}
|
||
hp-dialog .body {
|
||
max-height: 66vh;
|
||
overflow-y: auto;
|
||
}
|
||
.descin {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
background: var(--hp-bg);
|
||
border: 1px solid var(--hp-line);
|
||
color: var(--hp-txt);
|
||
border-radius: var(--rad-s);
|
||
padding: var(--sp-3) var(--sp-4);
|
||
font-size: var(--fs-m);
|
||
font-family: inherit;
|
||
resize: vertical;
|
||
/* flex column of the dialog body squeezes textareas — keep a usable height */
|
||
min-height: 92px;
|
||
flex-shrink: 0;
|
||
line-height: 1.35;
|
||
}
|
||
.bindsel {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--sp-3);
|
||
border: 1px solid var(--hp-line);
|
||
border-radius: var(--rad-m);
|
||
padding: var(--sp-4);
|
||
}
|
||
.bindsel .opt {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
border: 1px solid var(--hp-line);
|
||
background: transparent;
|
||
color: var(--hp-txt);
|
||
border-radius: var(--rad-s);
|
||
padding: var(--sp-3) var(--sp-4);
|
||
cursor: pointer;
|
||
font-size: var(--fs-m);
|
||
font-family: inherit;
|
||
}
|
||
.bindsel .opt.on {
|
||
background: var(--hp-accent);
|
||
color: var(--text-primary-color, #fff);
|
||
border-color: var(--hp-accent);
|
||
}
|
||
.curbind {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
font-size: var(--fs-m);
|
||
color: var(--hp-txt);
|
||
flex-wrap: wrap;
|
||
}
|
||
.curbind .ref {
|
||
color: var(--hp-muted);
|
||
font-size: var(--fs-s);
|
||
}
|
||
.vacbox .vacbtns { display: flex; gap: var(--sp-4); margin: var(--sp-3) 0; flex-wrap: wrap; }
|
||
.vacdiag { display: grid; gap: 4px; margin-bottom: var(--sp-3); }
|
||
.vacdiag > div { display: flex; justify-content: space-between; gap: var(--sp-5); }
|
||
.vacdiag > div > span { color: var(--secondary-text-color); }
|
||
.vacdiag > div > b { text-align: right; overflow-wrap: anywhere; }
|
||
.vacpicker { margin: var(--sp-3) 0; }
|
||
.vacsource-warning { display: grid; gap: 8px; }
|
||
.vacsource-warning .btn { justify-self: start; }
|
||
.vacpicker > summary { display: inline-flex; width: auto; list-style: none; cursor: pointer; }
|
||
.vacpicker > summary::-webkit-details-marker { display: none; }
|
||
.vacsource-list { display: grid; gap: 6px; margin-top: 8px; }
|
||
.vacsource-list details { padding: 6px 0 0; }
|
||
.vacsource-list details > summary { cursor: pointer; font-weight: 600; }
|
||
.vacsource { display: flex; align-items: center; justify-content: space-between; gap: 12px;
|
||
width: 100%; min-width: 0; padding: 9px 10px; border: 1px solid var(--divider-color);
|
||
border-radius: 10px; color: var(--primary-text-color); background: var(--secondary-background-color);
|
||
text-align: left; cursor: pointer; }
|
||
.vacsource.on { border-color: var(--accent-color); box-shadow: inset 3px 0 var(--accent-color); }
|
||
.vacsource > span:first-child { min-width: 0; display: grid; gap: 2px; }
|
||
.vacsource small { color: var(--secondary-text-color); overflow-wrap: anywhere; }
|
||
.vacsource-meta { color: var(--secondary-text-color); text-align: right; font-size: 0.82em; }
|
||
.vacxcme pre { margin: 8px 0 0; white-space: pre-wrap; user-select: text; }
|
||
.vacfit {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: 12;
|
||
overflow: visible;
|
||
touch-action: none;
|
||
cursor: grab;
|
||
/* the devlayer is pointer-events: none and every child opts back in —
|
||
without this line real clicks flew straight through the overlay
|
||
(owner: «уголки не кликабельны»; synthetic smoke events bypass
|
||
hit-testing, which is why they lied) */
|
||
pointer-events: auto;
|
||
}
|
||
.vacfit:active { cursor: grabbing; }
|
||
.vacfit polygon {
|
||
fill: color-mix(in srgb, var(--hp-accent) 16%, transparent);
|
||
stroke: var(--hp-accent);
|
||
stroke-width: 2;
|
||
vector-effect: non-scaling-stroke;
|
||
stroke-dasharray: 6 4;
|
||
}
|
||
.vacfit text {
|
||
fill: var(--hp-accent);
|
||
font-size: 26px;
|
||
text-anchor: middle;
|
||
dominant-baseline: middle;
|
||
pointer-events: none;
|
||
user-select: none;
|
||
}
|
||
/* ---- the furniture palette (docs/FURNITURE.md §3) ------------------- */
|
||
.furnpalette {
|
||
display: flex;
|
||
flex-direction: column;
|
||
max-height: 38vh;
|
||
border-top: 1px solid var(--hp-border, rgba(255, 255, 255, 0.12));
|
||
background: var(--card-background-color, var(--hp-bg));
|
||
font-size: 0.85em;
|
||
}
|
||
.furnhd {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 4px 8px;
|
||
font-weight: 600;
|
||
opacity: 0.9;
|
||
}
|
||
.furnhd .spacer { flex: 1; }
|
||
.furnbody {
|
||
overflow: auto;
|
||
padding: 0 8px 6px;
|
||
}
|
||
.furngroup {
|
||
margin: 6px 0 3px;
|
||
font-size: 0.85em;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.04em;
|
||
opacity: 0.6;
|
||
}
|
||
.furnrow {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 4px;
|
||
}
|
||
.furnitem {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 2px;
|
||
width: 74px;
|
||
padding: 4px 2px;
|
||
border: 1px solid transparent;
|
||
border-radius: 8px;
|
||
background: rgba(127, 127, 127, 0.08);
|
||
color: inherit;
|
||
font: inherit;
|
||
font-size: 0.8em;
|
||
line-height: 1.15;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
}
|
||
:host([data-pointer-hover]) .furnitem:hover { background: rgba(127, 127, 127, 0.18); }
|
||
.furnitem.on {
|
||
border-color: var(--hp-accent);
|
||
background: rgba(38, 166, 154, 0.18);
|
||
}
|
||
.furnprev {
|
||
width: 40px;
|
||
height: 40px;
|
||
color: var(--primary-text-color, currentColor);
|
||
}
|
||
.furnsize {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 6px;
|
||
padding: 6px 8px;
|
||
border-top: 1px solid var(--hp-border, rgba(255, 255, 255, 0.12));
|
||
}
|
||
.furnsize label {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 3px;
|
||
opacity: 0.8;
|
||
}
|
||
.furnsize .furnunit { opacity: 0.6; font-size: 0.85em; }
|
||
.furnsize input {
|
||
width: 5.5em;
|
||
padding: 3px 5px;
|
||
}
|
||
.furnhint { opacity: 0.6; }
|
||
.vacfitdot { fill: var(--hp-accent); pointer-events: none; }
|
||
/* hit target: invisible and finger-sized; .vacfitknob is the visible bead */
|
||
.vacfithandle {
|
||
fill: transparent;
|
||
stroke: none;
|
||
cursor: nwse-resize;
|
||
}
|
||
.vacfitknob {
|
||
fill: var(--hp-bg);
|
||
stroke: var(--hp-accent);
|
||
stroke-width: 2;
|
||
vector-effect: non-scaling-stroke;
|
||
pointer-events: none;
|
||
}
|
||
.vaccalbar {
|
||
position: fixed;
|
||
left: 50%;
|
||
bottom: 24px;
|
||
transform: translateX(-50%);
|
||
display: flex;
|
||
gap: var(--sp-5);
|
||
align-items: center;
|
||
background: var(--hp-bg);
|
||
color: var(--hp-txt);
|
||
border: 1px solid var(--hp-accent);
|
||
border-radius: var(--rad-l);
|
||
padding: 10px var(--sp-5);
|
||
z-index: 60;
|
||
box-shadow: var(--shadow-2);
|
||
}
|
||
.candlist {
|
||
max-height: 160px;
|
||
overflow-y: auto;
|
||
border-top: 1px solid var(--hp-line);
|
||
/* A scrollable box is a flex item that HAPPILY collapses: inside the
|
||
dialog body (a flex column) this list rendered its rows into a 1px
|
||
sliver — the DOM had 26 candidates and the user saw nothing. In the
|
||
binding dropdown it sits inside .droppanel (block context) and never
|
||
showed the bug. Field report, 2026-07-30. */
|
||
flex: 0 0 auto;
|
||
min-height: 2.6em;
|
||
}
|
||
.cand {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: var(--sp-4);
|
||
padding: var(--sp-3) var(--sp-4);
|
||
cursor: pointer;
|
||
border-radius: var(--rad-s);
|
||
font-size: var(--fs-m);
|
||
}
|
||
:host([data-pointer-hover]) .cand:hover {
|
||
background: rgba(127, 127, 127, 0.15);
|
||
}
|
||
.cand.sel {
|
||
background: var(--hp-accent);
|
||
color: var(--text-primary-color, #fff);
|
||
}
|
||
.cand .cs {
|
||
color: var(--hp-muted);
|
||
font-size: var(--fs-s);
|
||
white-space: nowrap;
|
||
}
|
||
.opening-entity-candidate {
|
||
width: 100%;
|
||
border: 0;
|
||
background: transparent;
|
||
color: inherit;
|
||
font: inherit;
|
||
text-align: left;
|
||
}
|
||
.opening-entity-candidate.sel {
|
||
background: var(--hp-accent);
|
||
color: var(--text-primary-color, #fff);
|
||
}
|
||
.opening-entity-empty { cursor: default; }
|
||
.cand.sel .cs {
|
||
color: var(--text-primary-color, #fff);
|
||
opacity: 0.85;
|
||
}
|
||
.cand.muted {
|
||
color: var(--hp-muted);
|
||
cursor: default;
|
||
}
|
||
.pdfedit {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: var(--sp-3);
|
||
align-items: center;
|
||
}
|
||
.pdftag {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
border: 1px solid var(--hp-line);
|
||
border-radius: var(--rad-s);
|
||
padding: var(--sp-2) var(--sp-3);
|
||
font-size: var(--fs-s);
|
||
}
|
||
.pdftag a {
|
||
color: var(--hp-txt);
|
||
text-decoration: none;
|
||
max-width: 150px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.pdftag .x {
|
||
--mdc-icon-size: 15px;
|
||
cursor: pointer;
|
||
color: var(--hp-muted);
|
||
}
|
||
:host([data-pointer-hover]) .pdftag .x:hover {
|
||
color: #ff7a5c;
|
||
}
|
||
.entlist {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--sp-2);
|
||
margin-bottom: 10px;
|
||
}
|
||
.entrow {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-4);
|
||
padding: var(--sp-3) var(--sp-4);
|
||
border-radius: var(--rad-m);
|
||
background: var(--secondary-background-color, rgba(128, 128, 128, 0.12));
|
||
}
|
||
.entrow ha-icon { --mdc-icon-size: 20px; color: var(--hp-muted); }
|
||
.entrow.on ha-icon { color: var(--hp-accent); }
|
||
.entrow .en { flex: 1; font-size: var(--fs-m); }
|
||
.entrow .ev { font-size: var(--fs-m); color: var(--hp-muted); }
|
||
.entbtn {
|
||
min-width: 74px;
|
||
min-height: 32px;
|
||
padding: var(--sp-2) var(--sp-5);
|
||
border: 1px solid var(--hp-muted);
|
||
border-radius: 999px;
|
||
background: transparent;
|
||
color: var(--hp-txt);
|
||
font: inherit;
|
||
font-size: var(--fs-m);
|
||
cursor: pointer;
|
||
}
|
||
.entbtn.on {
|
||
background: var(--hp-accent);
|
||
border-color: var(--hp-accent);
|
||
color: var(--text-primary-color, #fff);
|
||
font-weight: 600;
|
||
}
|
||
.inforow {
|
||
display: flex;
|
||
gap: 10px;
|
||
font-size: var(--fs-m);
|
||
margin: var(--sp-2) 0;
|
||
}
|
||
.inforow .k {
|
||
color: var(--hp-muted);
|
||
min-width: 84px;
|
||
}
|
||
.inforow a {
|
||
color: var(--hp-accent);
|
||
word-break: break-all;
|
||
}
|
||
.infodesc {
|
||
font-size: var(--fs-m);
|
||
white-space: pre-wrap;
|
||
margin-top: var(--sp-3);
|
||
}
|
||
.infodesc.muted {
|
||
color: var(--hp-muted);
|
||
}
|
||
.pdflist {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--sp-2);
|
||
}
|
||
.pdf {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
color: var(--hp-accent);
|
||
text-decoration: none;
|
||
}
|
||
ha-icon-picker {
|
||
display: block;
|
||
}
|
||
.floorrow {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-4);
|
||
padding: var(--sp-3) var(--sp-2);
|
||
font-size: var(--fs-m);
|
||
cursor: pointer;
|
||
}
|
||
.floorrow .floorlvl {
|
||
color: var(--hp-muted);
|
||
font-size: var(--fs-s);
|
||
border: 1px solid var(--hp-line);
|
||
border-radius: var(--rad-s);
|
||
padding: 0 var(--sp-3);
|
||
}
|
||
.rtest {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-4);
|
||
margin-bottom: var(--sp-4);
|
||
}
|
||
.rtest .namein { flex: 1; }
|
||
.rtest ha-icon { color: var(--hp-accent); }
|
||
.rtesticon { font-size: var(--fs-s); color: var(--hp-muted); }
|
||
.rrow {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
margin: var(--sp-1) 0;
|
||
}
|
||
.rrow .rpat { flex: 2; }
|
||
.rrow .ricon { flex: 1.4; }
|
||
.rrow .rpat.bad { border-color: #ff7a5c; }
|
||
.rrow .rprev { --mdc-icon-size: 18px; color: var(--hp-txt); min-width: 18px; }
|
||
.rrow .ract {
|
||
--mdc-icon-size: 16px;
|
||
color: var(--hp-muted);
|
||
cursor: pointer;
|
||
}
|
||
:host([data-pointer-hover]) .rrow .ract:hover { color: var(--hp-txt); }
|
||
:host([data-pointer-hover]) .rrow .ract.del:hover { color: #ff7a5c; }
|
||
.gsrow .gsl {
|
||
min-width: 150px;
|
||
font-size: var(--fs-m);
|
||
color: var(--hp-muted);
|
||
}
|
||
.optimize-live {
|
||
display: grid;
|
||
justify-items: start;
|
||
gap: var(--sp-2);
|
||
margin-bottom: var(--sp-3);
|
||
}
|
||
.optimize-live .alignmsg, .optimize-live .rhint { margin-bottom: 0; }
|
||
.optimize-cleanup { min-height: 44px; }
|
||
.optimize-selected { color: var(--hp-txt); }
|
||
.optimize-details {
|
||
margin-top: var(--sp-3);
|
||
color: var(--hp-muted);
|
||
font-size: var(--fs-s);
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.optimize-details > summary {
|
||
width: fit-content;
|
||
color: var(--hp-txt);
|
||
cursor: pointer;
|
||
font-weight: 600;
|
||
}
|
||
.optimize-details > summary:focus-visible {
|
||
outline: 2px solid var(--hp-accent);
|
||
outline-offset: 3px;
|
||
border-radius: var(--rad-s);
|
||
}
|
||
.optimize-details ul { margin: var(--sp-3) 0; padding-inline-start: 22px; }
|
||
.optimize-details li + li { margin-top: var(--sp-1); }
|
||
.btn.alignall { width: 100%; justify-content: center; }
|
||
.backupactions {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
gap: var(--sp-3);
|
||
}
|
||
.backupactions .btn { justify-content: center; min-width: 0; }
|
||
.backupupload { display: inline-flex; min-width: 0; }
|
||
.backupupload > .btn { width: 100%; justify-content: center; }
|
||
.backupupload input { display: none; }
|
||
.backupbody { min-width: 0; }
|
||
.backupplanonly { margin-inline-start: var(--sp-4) !important; align-items: flex-start !important; }
|
||
.backupplanonly > span:first-of-type { display: grid; gap: 2px; white-space: normal; }
|
||
.backupplanonly small { color: var(--secondary-text-color); line-height: 1.35; }
|
||
.backupplanonlystatus { color: var(--hp-accent) !important; font-weight: 700; }
|
||
.backupfile, .backupsummary, .backupcontent {
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-width: 0;
|
||
gap: var(--sp-1);
|
||
}
|
||
.backupfile b, .backupcontent span {
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.backupfile span, .backupsummary span, .backupcontent span {
|
||
color: var(--hp-muted);
|
||
font-size: var(--fs-s);
|
||
}
|
||
.backupcounts {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
gap: var(--sp-2) var(--sp-4);
|
||
font-size: var(--fs-s);
|
||
}
|
||
.backupwarn, .backuperror {
|
||
border-radius: var(--rad-s);
|
||
padding: var(--sp-3);
|
||
font-size: var(--fs-s);
|
||
line-height: 1.45;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.backupwarn { background: color-mix(in srgb, var(--hp-accent) 12%, transparent); }
|
||
.backuperror { background: rgba(179, 64, 42, .16); color: #ff7a5c; }
|
||
.backupdetails {
|
||
border: 1px solid var(--hp-line);
|
||
border-radius: var(--rad-s);
|
||
padding: var(--sp-2) var(--sp-3);
|
||
font-size: var(--fs-s);
|
||
}
|
||
.backupdetails summary { cursor: pointer; font-weight: 700; }
|
||
.backupdetails > div { display: grid; gap: var(--sp-1); padding-block-start: var(--sp-2); }
|
||
.backupdetails code { overflow-wrap: anywhere; color: var(--hp-muted); }
|
||
.backupchoices {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--sp-2);
|
||
border: 1px solid var(--hp-line);
|
||
border-radius: var(--rad-s);
|
||
min-width: 0;
|
||
}
|
||
.backupchoices label { margin: 0 !important; display: flex; gap: var(--sp-2); }
|
||
.backupconfirm { align-items: flex-start !important; }
|
||
.backupconfirm > span:first-of-type {
|
||
min-width: 0;
|
||
white-space: normal;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
@media (max-width: 520px) {
|
||
.backupactions, .backupcounts { grid-template-columns: 1fr; }
|
||
}
|
||
.aboutver {
|
||
font-size: var(--fs-s);
|
||
color: var(--hp-muted);
|
||
margin: var(--sp-2) 0 var(--sp-3);
|
||
}
|
||
.aboutlink {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
width: fit-content;
|
||
color: var(--hp-accent);
|
||
text-decoration: none;
|
||
font-size: var(--fs-m);
|
||
padding: var(--sp-1) 0;
|
||
}
|
||
:host([data-pointer-hover]) .aboutlink:hover { text-decoration: underline; }
|
||
.aboutlink ha-icon { --mdc-icon-size: 18px; line-height: 1; }
|
||
hp-dialog .body {
|
||
padding: var(--sp-5) var(--sp-6);
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--sp-3);
|
||
}
|
||
hp-dialog .tapconfirm-body {
|
||
min-width: 0;
|
||
overflow-x: hidden;
|
||
}
|
||
hp-dialog .tapconfirm-body p {
|
||
max-width: 100%;
|
||
min-width: 0;
|
||
margin: 0;
|
||
overflow-wrap: anywhere;
|
||
white-space: normal;
|
||
}
|
||
hp-dialog .tapconfirm-line {
|
||
color: var(--hp-muted);
|
||
}
|
||
hp-dialog .body label {
|
||
font-size: var(--fs-s);
|
||
color: var(--hp-muted);
|
||
margin-top: var(--sp-3);
|
||
}
|
||
hp-dialog .body .namein,
|
||
hp-dialog .body .areasel {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
/* Room settings contains long radio labels, two source pickers and live
|
||
previews, so it deliberately uses hp-dialog's medium width. Keep its
|
||
content shrinkable as well: the generic srcrow rule is nowrap because
|
||
compact switch rows need it, but here that created a horizontal scroll
|
||
box even on a wide desktop viewport. */
|
||
hp-dialog.roomdialog .body {
|
||
min-width: 0;
|
||
overflow-x: hidden;
|
||
}
|
||
hp-dialog.roomdialog .body > * {
|
||
min-width: 0;
|
||
max-width: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
hp-dialog.roomdialog .srcrow {
|
||
min-width: 0;
|
||
align-items: flex-start;
|
||
}
|
||
hp-dialog.roomdialog .srcrow > span:first-of-type {
|
||
min-width: 0;
|
||
white-space: normal;
|
||
overflow-wrap: anywhere;
|
||
line-height: 1.35;
|
||
}
|
||
hp-dialog.roomdialog .dropbtn > b,
|
||
hp-dialog.roomdialog .dropbtn > .ref {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
hp-dialog .row {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: var(--sp-4);
|
||
width: 100%;
|
||
min-width: 0;
|
||
box-sizing: border-box;
|
||
padding: var(--sp-5) var(--sp-6);
|
||
border-top: 1px solid var(--hp-line);
|
||
}
|
||
/* Stable destructive/commit footer contract. A flex spacer cannot react
|
||
when translated labels no longer fit: justify-content then overflows
|
||
the destructive button through the left inset. Two real groups wrap
|
||
as units instead — destructive actions stay left, while Cancel/Save
|
||
move together to a right-aligned second row when necessary. */
|
||
hp-dialog .row.dialog-action-footer {
|
||
align-items: center;
|
||
justify-content: flex-start;
|
||
flex-wrap: wrap;
|
||
row-gap: var(--sp-4);
|
||
}
|
||
hp-dialog .dialog-action-group {
|
||
display: flex;
|
||
flex: 0 1 auto;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: var(--sp-4);
|
||
max-width: 100%;
|
||
min-width: 0;
|
||
}
|
||
hp-dialog .dialog-action-group .btn {
|
||
flex: 0 0 auto;
|
||
min-height: 44px;
|
||
}
|
||
hp-dialog .dialog-action-danger {
|
||
margin-right: auto;
|
||
}
|
||
hp-dialog .dialog-action-commit {
|
||
margin-left: auto;
|
||
justify-content: flex-end;
|
||
}
|
||
hp-dialog .row.markerfooter {
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
hp-dialog .row.roomfooter {
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
/* Device info can have Edit + Open in HA + Close. It uses a wide dialog;
|
||
wrapping remains as a phone fallback, but without a flex spacer (which
|
||
used to strand Edit alone on the first line). */
|
||
hp-dialog .row.infofooter {
|
||
align-items: center;
|
||
justify-content: flex-start;
|
||
flex-wrap: wrap;
|
||
gap: var(--sp-3);
|
||
}
|
||
hp-dialog .row.infofooter .btn {
|
||
flex-shrink: 0;
|
||
}
|
||
hp-dialog .row.infofooter .infofooter-close {
|
||
margin-left: auto;
|
||
}
|
||
@media (max-width: 480px) {
|
||
hp-dialog .row.infofooter {
|
||
padding: var(--sp-4) var(--sp-5);
|
||
}
|
||
}
|
||
.markeractions,
|
||
.markersaveactions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-4);
|
||
}
|
||
.markeractions:empty { display: none; }
|
||
.markersaveactions { margin-left: auto; }
|
||
.device-inbox {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--sp-4);
|
||
padding: var(--sp-5) var(--sp-6);
|
||
min-width: 0;
|
||
}
|
||
.device-inbox-dialog { --hp-dialog-wide-width: 920px; }
|
||
.device-inbox-head {
|
||
display: grid;
|
||
grid-template-columns: minmax(180px, 1fr) auto;
|
||
align-items: center;
|
||
gap: var(--sp-4);
|
||
}
|
||
.device-inbox-search {
|
||
width: 100%;
|
||
min-width: 0;
|
||
box-sizing: border-box;
|
||
border: 1px solid var(--hp-line);
|
||
border-radius: var(--rad-m);
|
||
background: transparent;
|
||
color: var(--hp-txt);
|
||
font: inherit;
|
||
padding: 11px 14px;
|
||
}
|
||
.device-inbox-tabs {
|
||
display: flex;
|
||
gap: var(--sp-2);
|
||
overflow-x: auto;
|
||
scrollbar-width: thin;
|
||
padding-bottom: var(--sp-1);
|
||
}
|
||
.device-inbox-tabs button {
|
||
flex: 0 0 auto;
|
||
border: 1px solid var(--hp-line);
|
||
border-radius: 999px;
|
||
background: transparent;
|
||
color: var(--hp-txt);
|
||
font: inherit;
|
||
padding: 8px 12px;
|
||
cursor: pointer;
|
||
}
|
||
.device-inbox-tabs button.on {
|
||
border-color: var(--hp-accent);
|
||
background: color-mix(in srgb, var(--hp-accent) 18%, transparent);
|
||
}
|
||
.device-inbox-tabs button span { color: var(--hp-muted); margin-inline-start: 4px; }
|
||
.device-inbox-filters {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: var(--sp-4) var(--sp-6);
|
||
color: var(--hp-muted);
|
||
}
|
||
.device-inbox-filters label {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
cursor: pointer;
|
||
}
|
||
.device-inbox-results { display: grid; gap: var(--sp-3); min-width: 0; }
|
||
.device-inbox-row {
|
||
display: grid;
|
||
grid-template-columns: 42px minmax(180px, 1fr) minmax(180px, auto);
|
||
align-items: center;
|
||
gap: var(--sp-4);
|
||
min-width: 0;
|
||
border: 1px solid var(--hp-line);
|
||
border-radius: var(--rad-m);
|
||
padding: var(--sp-4);
|
||
background: color-mix(in srgb, var(--hp-txt) 3%, transparent);
|
||
}
|
||
.device-inbox-icon { --mdc-icon-size: 28px; color: var(--hp-txt); justify-self: center; }
|
||
.device-inbox-copy { min-width: 0; }
|
||
.device-inbox-name { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2); }
|
||
.device-inbox-new {
|
||
border-radius: 999px;
|
||
background: var(--hp-accent);
|
||
color: var(--text-primary-color, #fff);
|
||
font-size: var(--fs-s);
|
||
padding: 2px 7px;
|
||
}
|
||
.device-inbox-meta,
|
||
.device-inbox-reason,
|
||
.device-inbox-copy code {
|
||
display: block;
|
||
color: var(--hp-muted);
|
||
font-size: var(--fs-s);
|
||
overflow-wrap: anywhere;
|
||
white-space: normal;
|
||
}
|
||
.device-inbox-status { color: var(--error-color, #db4437); margin-inline-start: var(--sp-2); }
|
||
.device-inbox-actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: var(--sp-2);
|
||
}
|
||
.device-inbox-actions .btn { min-height: 36px; padding: 7px 10px; }
|
||
.device-inbox-menu { position: relative; }
|
||
.device-inbox-menu summary { list-style: none; cursor: pointer; }
|
||
.device-inbox-menu summary::-webkit-details-marker { display: none; }
|
||
.device-inbox-menu-items {
|
||
position: absolute;
|
||
z-index: 2;
|
||
inset-inline-end: 0;
|
||
top: calc(100% + var(--sp-1));
|
||
display: grid;
|
||
gap: var(--sp-1);
|
||
min-width: 180px;
|
||
padding: var(--sp-2);
|
||
border: 1px solid var(--hp-line);
|
||
border-radius: var(--rad-m);
|
||
background: var(--hp-panel, var(--card-background-color, #fff));
|
||
box-shadow: 0 8px 24px rgba(0, 0, 0, .22);
|
||
}
|
||
.device-inbox-menu-items .btn { justify-content: flex-start; width: 100%; }
|
||
.device-inbox-empty { color: var(--hp-muted); text-align: center; padding: var(--sp-8); }
|
||
.device-inbox-more { align-self: center; }
|
||
@media (max-width: 680px) {
|
||
.device-inbox { padding: var(--sp-4); }
|
||
.device-inbox-head { grid-template-columns: minmax(0, 1fr); }
|
||
.device-inbox-head .btn { justify-self: stretch; }
|
||
.device-inbox-tabs {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
overflow-x: visible;
|
||
}
|
||
.device-inbox-tabs button {
|
||
min-width: 0;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.device-inbox-row { grid-template-columns: 36px minmax(0, 1fr); }
|
||
.device-inbox-actions { grid-column: 1 / -1; justify-content: flex-start; }
|
||
}
|
||
`],Ye=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),Je=[{pattern:"протечк|leak|water sensor",icon:"mdi:water-alert"},{pattern:"клапан|valve",icon:"mdi:pipe-valve"},{pattern:"дым|smoke",icon:"mdi:smoke-detector"},{pattern:"термоголов|trv|radiator",icon:"mdi:radiator"},{pattern:"чайник|kettle|термопот",icon:"mdi:kettle"},{pattern:"сауна|sauna|harvia|парная|парилк",icon:"mdi:hot-tub"},{pattern:"температ|temperature|thermometer|climate sensor",icon:"mdi:thermometer"},{pattern:"qingping|air monitor|молекул|air quality",icon:"mdi:air-filter"},{pattern:"штор|curtain|blind|shade",icon:"mdi:roller-shade"},{pattern:"розетк|plug|socket|outlet",icon:"mdi:power-socket-de"},{pattern:"выключат|switch",icon:"mdi:light-switch"},{pattern:"лампа|лампочк|bulb|gx53|светильник|rgb|lamp|light strip",icon:"mdi:lightbulb"},{pattern:"камер|camera",icon:"mdi:cctv"},{pattern:"замок|ttlock|lock|sn609|sn9161",icon:"mdi:lock"},{pattern:"ворота|garage|gate",icon:"mdi:garage-variant"},{pattern:"калитк|door|открыт|contact",icon:"mdi:door"},{pattern:"счётчик|счетчик|kws|meter",icon:"mdi:meter-electric"},{pattern:"вводный автомат|breaker|wifimcbn",icon:"mdi:electric-switch"},{pattern:"myheat|котёл|котел|boiler|отоплен|heating",icon:"mdi:water-boiler"},{pattern:"холодильник|fridge",icon:"mdi:fridge"},{pattern:"стиральн|washer|washing",icon:"mdi:washing-machine"},{pattern:"сушилк|dryer",icon:"mdi:tumble-dryer"},{pattern:"пылесос|vacuum|dreame|roborock",icon:"mdi:robot-vacuum"},{pattern:"soundbar",icon:"mdi:soundbar"},{pattern:"колонк|станц|speaker|яндекс|yandex|алиса|alice",icon:"mdi:speaker"},{pattern:"tv|телевизор|hyundaitv|mitv|television",icon:"mdi:television"},{pattern:"keenetic|роутер|router|mesh|access point",icon:"mdi:router-wireless"},{pattern:"ибп|ups|kirpich",icon:"mdi:battery-charging-high"},{pattern:"slzb|координат|zigbee|coordinator",icon:"mdi:zigbee"},{pattern:"motion|движен|presence|присутств",icon:"mdi:motion-sensor"},{pattern:"humidity|влажн",icon:"mdi:water-percent"}];function Xe(e){const t=[];for(const i of e)if(i&&"string"==typeof i.pattern&&i.icon)try{t.push({re:new RegExp(i.pattern,"i"),icon:i.icon})}catch{}return t}const Ze=Xe(Je),Qe={temperature:"mdi:thermometer",humidity:"mdi:water-percent",motion:"mdi:motion-sensor",occupancy:"mdi:motion-sensor",presence:"mdi:motion-sensor",door:"mdi:door",window:"mdi:window-closed",garage_door:"mdi:garage-variant",smoke:"mdi:smoke-detector",moisture:"mdi:water-alert",gas:"mdi:gas-cylinder",power:"mdi:meter-electric",energy:"mdi:meter-electric",illuminance:"mdi:brightness-5",co2:"mdi:molecule-co2",pm25:"mdi:air-filter",battery:"mdi:battery"},et="mdi:chip";function tt(e,t,i){const s=((e||"")+" "+(t||"")).toLowerCase();for(const{re:e,icon:t}of i??Ze)if(e.test(s))return t;return et}const it=["light","switch","cover","valve","lock","climate","fan","media_player","camera","vacuum","humidifier","water_heater","alarm_control_panel","sensor","binary_sensor","event","button","number","select","update"];var st=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,nt=Math.ceil,ot=Math.floor,at="[BigNumber Error] ",rt=at+"Number primitive has more than 15 significant digits: ",lt=1e14,ct=14,ht=9007199254740991,dt=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],pt=1e7,ut=1e9;function _t(e){var t=0|e;return e>0||e===t?t:t-1}function mt(e){for(var t,i,s=1,n=e.length,o=e[0]+"";s<n;){for(t=e[s++]+"",i=ct-t.length;i--;t="0"+t);o+=t}for(n=o.length;48===o.charCodeAt(--n););return o.slice(0,n+1||1)}function gt(e,t){var i,s,n=e.c,o=t.c,a=e.s,r=t.s,l=e.e,c=t.e;if(!a||!r)return null;if(i=n&&!n[0],s=o&&!o[0],i||s)return i?s?0:-r:a;if(a!=r)return a;if(i=a<0,s=l==c,!n||!o)return s?0:!n^i?1:-1;if(!s)return l>c^i?1:-1;for(r=(l=n.length)<(c=o.length)?l:c,a=0;a<r;a++)if(n[a]!=o[a])return n[a]>o[a]^i?1:-1;return l==c?0:l>c^i?1:-1}function ft(e,t,i,s){if(e<t||e>i||e!==ot(e))throw Error(at+(s||"Argument")+("number"==typeof e?e<t||e>i?" out of range: ":" not an integer: ":" not a primitive number: ")+String(e))}function vt(e){var t=e.c.length-1;return _t(e.e/ct)==t&&e.c[t]%2!=0}function bt(e,t){return(e.length>1?e.charAt(0)+"."+e.slice(1):e)+(t<0?"e":"e+")+t}function yt(e,t,i){var s,n;if(t<0){for(n=i+".";++t;n+=i);e=n+e}else if(++t>(s=e.length)){for(n=i,t-=s;--t;n+=i);e+=n}else t<s&&(e=e.slice(0,t)+"."+e.slice(t));return e}var wt=function e(t){var i,s,n,o,a,r,l,c,h,d,p=M.prototype={constructor:M,toString:null,valueOf:null},u=new M(1),_=20,m=4,g=-7,f=21,v=-1e7,b=1e7,y=!1,w=1,k=0,x={prefix:"",groupSize:3,secondaryGroupSize:0,groupSeparator:",",decimalSeparator:".",fractionGroupSize:0,fractionGroupSeparator:" ",suffix:""},$="0123456789abcdefghijklmnopqrstuvwxyz",S=!0;function M(e,t){var i,o,a,r,l,c,h,d,p=this;if(!(p instanceof M))return new M(e,t);if(null==t){if(e&&!0===e._isBigNumber)return p.s=e.s,void(!e.c||e.e>b?p.c=p.e=null:e.e<v?p.c=[p.e=0]:(p.e=e.e,p.c=e.c.slice()));if((c="number"==typeof e)&&0*e==0){if(p.s=1/e<0?(e=-e,-1):1,e===~~e){for(r=0,l=e;l>=10;l/=10,r++);return void(r>b?p.c=p.e=null:(p.e=r,p.c=[e]))}d=String(e)}else{if(!st.test(d=String(e)))return n(p,d,c);p.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(r=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(r<0&&(r=l),r+=+d.slice(l+1),d=d.substring(0,l)):r<0&&(r=d.length)}else{if(ft(t,2,$.length,"Base"),10==t&&S)return T(p=new M(e),_+p.e+1,m);if(d=String(e),c="number"==typeof e){if(0*e!=0)return n(p,d,c,t);if(p.s=1/e<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(rt+e)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=$.slice(0,t),r=l=0,h=d.length;l<h;l++)if(i.indexOf(o=d.charAt(l))<0){if("."==o){if(l>r){r=h;continue}}else if(!a&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){a=!0,l=-1,r=0;continue}return n(p,String(e),c,t)}c=!1,(r=(d=s(d,t,10,p.s)).indexOf("."))>-1?d=d.replace(".",""):r=d.length}for(l=0;48===d.charCodeAt(l);l++);for(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(e>ht||e!==ot(e)))throw Error(rt+p.s*e);if((r=r-l-1)>b)p.c=p.e=null;else if(r<v)p.c=[p.e=0];else{if(p.e=r,p.c=[],l=(r+1)%ct,r<0&&(l+=ct),l<h){for(l&&p.c.push(+d.slice(0,l)),h-=ct;l<h;)p.c.push(+d.slice(l,l+=ct));l=ct-(d=d.slice(l)).length}else l-=h;for(;l--;d+="0");p.c.push(+d)}}else p.c=[p.e=0]}function C(e,t,i,s){var n,o,a,r,l;if(null==i?i=m:ft(i,0,8),!e.c)return e.toString();if(n=e.c[0],a=e.e,null==t)l=mt(e.c),l=1==s||2==s&&(a<=g||a>=f)?bt(l,a):yt(l,a,"0");else if(o=(e=T(new M(e),t,i)).e,r=(l=mt(e.c)).length,1==s||2==s&&(t<=o||o<=g)){for(;r<t;l+="0",r++);l=bt(l,o)}else if(t-=a+(2===s&&o>a),l=yt(l,o,"0"),o+1>r){if(--t>0)for(l+=".";t--;l+="0");}else if((t+=o-r)>0)for(o+1==r&&(l+=".");t--;l+="0");return e.s<0&&n?"-"+l:l}function D(e,t){for(var i,s,n=1,o=new M(e[0]);n<e.length;n++)(!(s=new M(e[n])).s||(i=gt(o,s))===t||0===i&&o.s===t)&&(o=s);return o}function P(e,t,i){for(var s=1,n=t.length;!t[--n];t.pop());for(n=t[0];n>=10;n/=10,s++);return(i=s+i*ct-1)>b?e.c=e.e=null:i<v?e.c=[e.e=0]:(e.e=i,e.c=t),e}function T(e,t,i,s){var n,o,a,r,l,c,h,d=e.c,p=dt;if(d){e:{for(n=1,r=d[0];r>=10;r/=10,n++);if((o=t-n)<0)o+=ct,a=t,l=d[c=0],h=ot(l/p[n-a-1]%10);else if((c=nt((o+1)/ct))>=d.length){if(!s)break e;for(;d.length<=c;d.push(0));l=h=0,n=1,a=(o%=ct)-ct+1}else{for(l=r=d[c],n=1;r>=10;r/=10,n++);h=(a=(o%=ct)-ct+n)<0?0:ot(l/p[n-a-1]%10)}if(s=s||t<0||null!=d[c+1]||(a<0?l:l%p[n-a-1]),s=i<4?(h||s)&&(0==i||i==(e.s<0?3:2)):h>5||5==h&&(4==i||s||6==i&&(o>0?a>0?l/p[n-a]:0:d[c-1])%10&1||i==(e.s<0?8:7)),t<1||!d[0])return d.length=0,s?(t-=e.e+1,d[0]=p[(ct-t%ct)%ct],e.e=-t||0):d[0]=e.e=0,e;if(0==o?(d.length=c,r=1,c--):(d.length=c+1,r=p[ct-o],d[c]=a>0?ot(l/p[n-a]%p[a])*r:0),s)for(;;){if(0==c){for(o=1,a=d[0];a>=10;a/=10,o++);for(a=d[0]+=r,r=1;a>=10;a/=10,r++);o!=r&&(e.e++,d[0]==lt&&(d[0]=1));break}if(d[c]+=r,d[c]!=lt)break;d[c--]=0,r=1}for(o=d.length;0===d[--o];d.pop());}e.e>b?e.c=e.e=null:e.e<v&&(e.c=[e.e=0])}return e}function I(e){var t,i=e.e;return null===i?e.toString():(t=mt(e.c),t=i<=g||i>=f?bt(t,i):yt(t,i,"0"),e.s<0?"-"+t:t)}return M.clone=e,M.ROUND_UP=0,M.ROUND_DOWN=1,M.ROUND_CEIL=2,M.ROUND_FLOOR=3,M.ROUND_HALF_UP=4,M.ROUND_HALF_DOWN=5,M.ROUND_HALF_EVEN=6,M.ROUND_HALF_CEIL=7,M.ROUND_HALF_FLOOR=8,M.EUCLID=9,M.config=M.set=function(e){var t,i;if(null!=e){if("object"!=typeof e)throw Error(at+"Object expected: "+e);if(e.hasOwnProperty(t="DECIMAL_PLACES")&&(ft(i=e[t],0,ut,t),_=i),e.hasOwnProperty(t="ROUNDING_MODE")&&(ft(i=e[t],0,8,t),m=i),e.hasOwnProperty(t="EXPONENTIAL_AT")&&((i=e[t])&&i.pop?(ft(i[0],-ut,0,t),ft(i[1],0,ut,t),g=i[0],f=i[1]):(ft(i,-ut,ut,t),g=-(f=i<0?-i:i))),e.hasOwnProperty(t="RANGE"))if((i=e[t])&&i.pop)ft(i[0],-ut,-1,t),ft(i[1],1,ut,t),v=i[0],b=i[1];else{if(ft(i,-ut,ut,t),!i)throw Error(at+t+" cannot be zero: "+i);v=-(b=i<0?-i:i)}if(e.hasOwnProperty(t="CRYPTO")){if((i=e[t])!==!!i)throw Error(at+t+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw y=!i,Error(at+"crypto unavailable");y=i}else y=i}if(e.hasOwnProperty(t="MODULO_MODE")&&(ft(i=e[t],0,9,t),w=i),e.hasOwnProperty(t="POW_PRECISION")&&(ft(i=e[t],0,ut,t),k=i),e.hasOwnProperty(t="FORMAT")){if("object"!=typeof(i=e[t]))throw Error(at+t+" not an object: "+i);x=i}if(e.hasOwnProperty(t="ALPHABET")){if("string"!=typeof(i=e[t])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(at+t+" invalid: "+i);S="0123456789"==i.slice(0,10),$=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,b],CRYPTO:y,MODULO_MODE:w,POW_PRECISION:k,FORMAT:x,ALPHABET:$}},M.isBigNumber=function(e){if(!e||!0!==e._isBigNumber)return!1;if(!M.DEBUG)return!0;var t,i,s=e.c,n=e.e,o=e.s;e:if("[object Array]"=={}.toString.call(s)){if((1===o||-1===o)&&n>=-ut&&n<=ut&&n===ot(n)){if(0===s[0]){if(0===n&&1===s.length)return!0;break e}if((t=(n+1)%ct)<1&&(t+=ct),String(s[0]).length==t){for(t=0;t<s.length;t++)if((i=s[t])<0||i>=lt||i!==ot(i))break e;if(0!==i)return!0}}}else if(null===s&&null===n&&(null===o||1===o||-1===o))return!0;throw Error(at+"Invalid BigNumber: "+e)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(o=9007199254740992,a=Math.random()*o&2097151?function(){return ot(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(e){var t,i,s,n,o,r=0,l=[],c=new M(u);if(null==e?e=_:ft(e,0,ut),n=nt(e/ct),y)if(crypto.getRandomValues){for(t=crypto.getRandomValues(new Uint32Array(n*=2));r<n;)(o=131072*t[r]+(t[r+1]>>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),t[r]=i[0],t[r+1]=i[1]):(l.push(o%1e14),r+=2);r=n/2}else{if(!crypto.randomBytes)throw y=!1,Error(at+"crypto unavailable");for(t=crypto.randomBytes(n*=7);r<n;)(o=281474976710656*(31&t[r])+1099511627776*t[r+1]+4294967296*t[r+2]+16777216*t[r+3]+(t[r+4]<<16)+(t[r+5]<<8)+t[r+6])>=9e15?crypto.randomBytes(7).copy(t,r):(l.push(o%1e14),r+=7);r=n/7}if(!y)for(;r<n;)(o=a())<9e15&&(l[r++]=o%1e14);for(n=l[--r],e%=ct,n&&e&&(o=dt[ct-e],l[r]=ot(n/o)*o);0===l[r];l.pop(),r--);if(r<0)l=[s=0];else{for(s=-1;0===l[0];l.splice(0,1),s-=ct);for(r=1,o=l[0];o>=10;o/=10,r++);r<ct&&(s-=ct-r)}return c.e=s,c.c=l,c}),M.sum=function(){for(var e=1,t=arguments,i=new M(t[0]);e<t.length;)i=i.plus(t[e++]);return i},s=function(){var e="0123456789";function t(e,t,i,s){for(var n,o,a=[0],r=0,l=e.length;r<l;){for(o=a.length;o--;a[o]*=t);for(a[0]+=s.indexOf(e.charAt(r++)),n=0;n<a.length;n++)a[n]>i-1&&(null==a[n+1]&&(a[n+1]=0),a[n+1]+=a[n]/i|0,a[n]%=i)}return a.reverse()}return function(s,n,o,a,r){var l,c,h,d,p,u,g,f,v=s.indexOf("."),b=_,y=m;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(n)).pow(s.length-v),k=d,f.c=t(yt(mt(u.c),u.e,"0"),10,o,e),f.e=f.c.length),h=d=(g=t(s,n,o,r?(l=$,e):(l=e,$))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--h:(u.c=g,u.e=h,u.s=a,g=(u=i(u,f,b,y,o)).c,p=u.r,h=u.e),v=g[c=h+b+1],d=o/2,p=p||c<0||null!=g[c+1],p=y<4?(null!=v||p)&&(0==y||y==(u.s<0?3:2)):v>d||v==d&&(4==y||p||6==y&&1&g[c-1]||y==(u.s<0?8:7)),c<1||!g[0])s=p?yt(l.charAt(1),-b,l.charAt(0)):l.charAt(0);else{if(g.length=c,p)for(--o;++g[--c]>o;)g[c]=0,c||(++h,g=[1].concat(g));for(d=g.length;!g[--d];);for(v=0,s="";v<=d;s+=l.charAt(g[v++]));s=yt(s,h,l.charAt(0))}return s}}(),i=function(){function e(e,t,i){var s,n,o,a,r=0,l=e.length,c=t%pt,h=t/pt|0;for(e=e.slice();l--;)r=((n=c*(o=e[l]%pt)+(s=h*o+(a=e[l]/pt|0)*c)%pt*pt+r)/i|0)+(s/pt|0)+h*a,e[l]=n%i;return r&&(e=[r].concat(e)),e}function t(e,t,i,s){var n,o;if(i!=s)o=i>s?1:-1;else for(n=o=0;n<i;n++)if(e[n]!=t[n]){o=e[n]>t[n]?1:-1;break}return o}function i(e,t,i,s){for(var n=0;i--;)e[i]-=n,n=e[i]<t[i]?1:0,e[i]=n*s+e[i]-t[i];for(;!e[0]&&e.length>1;e.splice(0,1));}return function(s,n,o,a,r){var l,c,h,d,p,u,_,m,g,f,v,b,y,w,k,x,$,S=s.s==n.s?1:-1,C=s.c,D=n.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&n.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(g=(m=new M(S)).c=[],S=o+(c=s.e-n.e)+1,r||(r=lt,c=_t(s.e/ct)-_t(n.e/ct),S=S/ct|0),h=0;D[h]==(C[h]||0);h++);if(D[h]>(C[h]||0)&&c--,S<0)g.push(1),d=!0;else{for(w=C.length,x=D.length,h=0,S+=2,(p=ot(r/(D[0]+1)))>1&&(D=e(D,p,r),C=e(C,p,r),x=D.length,w=C.length),y=x,v=(f=C.slice(0,x)).length;v<x;f[v++]=0);$=D.slice(),$=[0].concat($),k=D[0],D[1]>=r/2&&k++;do{if(p=0,(l=t(D,f,x,v))<0){if(b=f[0],x!=v&&(b=b*r+(f[1]||0)),(p=ot(b/k))>1)for(p>=r&&(p=r-1),_=(u=e(D,p,r)).length,v=f.length;1==t(u,f,_,v);)p--,i(u,x<_?$:D,_,r),_=u.length,l=1;else 0==p&&(l=p=1),_=(u=D.slice()).length;if(_<v&&(u=[0].concat(u)),i(f,u,v,r),v=f.length,-1==l)for(;t(D,f,x,v)<1;)p++,i(f,x<v?$:D,v,r),v=f.length}else 0===l&&(p++,f=[0]);g[h++]=p,f[0]?f[v++]=C[y]||0:(f=[C[y]],v=1)}while((y++<w||null!=f[0])&&S--);d=null!=f[0],g[0]||g.splice(0,1)}if(r==lt){for(h=1,S=g[0];S>=10;S/=10,h++);T(m,o+(m.e=h+c*ct-1)+1,a,d)}else m.e=c,m.r=+d;return m}}(),r=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,n=function(e,t,i,s){var n,o=i?t:t.replace(d,"");if(h.test(o))e.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(r,function(e,t,i){return n="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=n?e:t}),s&&(n=s,o=o.replace(l,"$1").replace(c,"0.$1")),t!=o))return new M(o,n);if(M.DEBUG)throw Error(at+"Not a"+(s?" base "+s:"")+" number: "+t);e.s=null}e.c=e.e=null},p.absoluteValue=p.abs=function(){var e=new M(this);return e.s<0&&(e.s=1),e},p.comparedTo=function(e,t){return gt(this,new M(e,t))},p.decimalPlaces=p.dp=function(e,t){var i,s,n,o=this;if(null!=e)return ft(e,0,ut),null==t?t=m:ft(t,0,8),T(new M(o),e+o.e+1,t);if(!(i=o.c))return null;if(s=((n=i.length-1)-_t(this.e/ct))*ct,n=i[n])for(;n%10==0;n/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(e,t){return i(this,new M(e,t),_,m)},p.dividedToIntegerBy=p.idiv=function(e,t){return i(this,new M(e,t),0,1)},p.exponentiatedBy=p.pow=function(e,t){var i,s,n,o,a,r,l,c,h=this;if((e=new M(e)).c&&!e.isInteger())throw Error(at+"Exponent not an integer: "+I(e));if(null!=t&&(t=new M(t)),a=e.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!e.c||!e.c[0])return c=new M(Math.pow(+I(h),a?e.s*(2-vt(e)):+I(e))),t?c.mod(t):c;if(r=e.s<0,t){if(t.c?!t.c[0]:!t.s)return new M(NaN);(s=!r&&h.isInteger()&&t.isInteger())&&(h=h.mod(t))}else{if(e.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||a&&h.c[1]>=24e7:h.c[0]<8e13||a&&h.c[0]<=9999975e7)))return o=h.s<0&&vt(e)?-0:0,h.e>-1&&(o=1/o),new M(r?1/o:o);k&&(o=nt(k/ct+2))}for(a?(i=new M(.5),r&&(e.s=1),l=vt(e)):l=(n=Math.abs(+I(e)))%2,c=new M(u);;){if(l){if(!(c=c.times(h)).c)break;o?c.c.length>o&&(c.c.length=o):s&&(c=c.mod(t))}if(n){if(0===(n=ot(n/2)))break;l=n%2}else if(T(e=e.times(i),e.e+1,1),e.e>14)l=vt(e);else{if(0===(n=+I(e)))break;l=n%2}h=h.times(h),o?h.c&&h.c.length>o&&(h.c.length=o):s&&(h=h.mod(t))}return s?c:(r&&(c=u.div(c)),t?c.mod(t):o?T(c,k,m,void 0):c)},p.integerValue=function(e){var t=new M(this);return null==e?e=m:ft(e,0,8),T(t,t.e+1,e)},p.isEqualTo=p.eq=function(e,t){return 0===gt(this,new M(e,t))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(e,t){return gt(this,new M(e,t))>0},p.isGreaterThanOrEqualTo=p.gte=function(e,t){return 1===(t=gt(this,new M(e,t)))||0===t},p.isInteger=function(){return!!this.c&&_t(this.e/ct)>this.c.length-2},p.isLessThan=p.lt=function(e,t){return gt(this,new M(e,t))<0},p.isLessThanOrEqualTo=p.lte=function(e,t){return-1===(t=gt(this,new M(e,t)))||0===t},p.isNaN=function(){return!this.s},p.isNegative=function(){return this.s<0},p.isPositive=function(){return this.s>0},p.isZero=function(){return!!this.c&&0==this.c[0]},p.minus=function(e,t){var i,s,n,o,a=this,r=a.s;if(t=(e=new M(e,t)).s,!r||!t)return new M(NaN);if(r!=t)return e.s=-t,a.plus(e);var l=a.e/ct,c=e.e/ct,h=a.c,d=e.c;if(!l||!c){if(!h||!d)return h?(e.s=-t,e):new M(d?a:NaN);if(!h[0]||!d[0])return d[0]?(e.s=-t,e):new M(h[0]?a:3==m?-0:0)}if(l=_t(l),c=_t(c),h=h.slice(),r=l-c){for((o=r<0)?(r=-r,n=h):(c=l,n=d),n.reverse(),t=r;t--;n.push(0));n.reverse()}else for(s=(o=(r=h.length)<(t=d.length))?r:t,r=t=0;t<s;t++)if(h[t]!=d[t]){o=h[t]<d[t];break}if(o&&(n=h,h=d,d=n,e.s=-e.s),(t=(s=d.length)-(i=h.length))>0)for(;t--;h[i++]=0);for(t=lt-1;s>r;){if(h[--s]<d[s]){for(i=s;i&&!h[--i];h[i]=t);--h[i],h[s]+=lt}h[s]-=d[s]}for(;0==h[0];h.splice(0,1),--c);return h[0]?P(e,h,c):(e.s=3==m?-1:1,e.c=[e.e=0],e)},p.modulo=p.mod=function(e,t){var s,n,o=this;return e=new M(e,t),!o.c||!e.s||e.c&&!e.c[0]?new M(NaN):!e.c||o.c&&!o.c[0]?new M(o):(9==w?(n=e.s,e.s=1,s=i(o,e,0,3),e.s=n,s.s*=n):s=i(o,e,0,w),(e=o.minus(s.times(e))).c[0]||1!=w||(e.s=o.s),e)},p.multipliedBy=p.times=function(e,t){var i,s,n,o,a,r,l,c,h,d,p,u,_,m,g,f=this,v=f.c,b=(e=new M(e,t)).c;if(!(v&&b&&v[0]&&b[0]))return!f.s||!e.s||v&&!v[0]&&!b||b&&!b[0]&&!v?e.c=e.e=e.s=null:(e.s*=f.s,v&&b?(e.c=[0],e.e=0):e.c=e.e=null),e;for(s=_t(f.e/ct)+_t(e.e/ct),e.s*=f.s,(l=v.length)<(d=b.length)&&(_=v,v=b,b=_,n=l,l=d,d=n),n=l+d,_=[];n--;_.push(0));for(m=lt,g=pt,n=d;--n>=0;){for(i=0,p=b[n]%g,u=b[n]/g|0,o=n+(a=l);o>n;)i=((c=p*(c=v[--a]%g)+(r=u*c+(h=v[a]/g|0)*p)%g*g+_[o]+i)/m|0)+(r/g|0)+u*h,_[o--]=c%m;_[o]=i}return i?++s:_.splice(0,1),P(e,_,s)},p.negated=function(){var e=new M(this);return e.s=-e.s||null,e},p.plus=function(e,t){var i,s=this,n=s.s;if(t=(e=new M(e,t)).s,!n||!t)return new M(NaN);if(n!=t)return e.s=-t,s.minus(e);var o=s.e/ct,a=e.e/ct,r=s.c,l=e.c;if(!o||!a){if(!r||!l)return new M(n/0);if(!r[0]||!l[0])return l[0]?e:new M(r[0]?s:0*n)}if(o=_t(o),a=_t(a),r=r.slice(),n=o-a){for(n>0?(a=o,i=l):(n=-n,i=r),i.reverse();n--;i.push(0));i.reverse()}for((n=r.length)-(t=l.length)<0&&(i=l,l=r,r=i,t=n),n=0;t;)n=(r[--t]=r[t]+l[t]+n)/lt|0,r[t]=lt===r[t]?0:r[t]%lt;return n&&(r=[n].concat(r),++a),P(e,r,a)},p.precision=p.sd=function(e,t){var i,s,n,o=this;if(null!=e&&e!==!!e)return ft(e,1,ut),null==t?t=m:ft(t,0,8),T(new M(o),e,t);if(!(i=o.c))return null;if(s=(n=i.length-1)*ct+1,n=i[n]){for(;n%10==0;n/=10,s--);for(n=i[0];n>=10;n/=10,s++);}return e&&o.e+1>s&&(s=o.e+1),s},p.shiftedBy=function(e){return ft(e,-9007199254740991,ht),this.times("1e"+e)},p.squareRoot=p.sqrt=function(){var e,t,s,n,o,a=this,r=a.c,l=a.s,c=a.e,h=_+4,d=new M("0.5");if(1!==l||!r||!r[0])return new M(!l||l<0&&(!r||r[0])?NaN:r?a:1/0);if(0==(l=Math.sqrt(+I(a)))||l==1/0?(((t=mt(r)).length+c)%2==0&&(t+="0"),l=Math.sqrt(+t),c=_t((c+1)/2)-(c<0||c%2),s=new M(t=l==1/0?"5e"+c:(t=l.toExponential()).slice(0,t.indexOf("e")+1)+c)):s=new M(l+""),s.c[0])for((l=(c=s.e)+h)<3&&(l=0);;)if(o=s,s=d.times(o.plus(i(a,o,h,1))),mt(o.c).slice(0,l)===(t=mt(s.c)).slice(0,l)){if(s.e<c&&--l,"9999"!=(t=t.slice(l-3,l+1))&&(n||"4999"!=t)){+t&&(+t.slice(1)||"5"!=t.charAt(0))||(T(s,s.e+_+2,1),e=!s.times(s).eq(a));break}if(!n&&(T(o,o.e+_+2,0),o.times(o).eq(a))){s=o;break}h+=4,l+=4,n=1}return T(s,s.e+_+1,m,e)},p.toExponential=function(e,t){return null!=e&&(ft(e,0,ut),e++),C(this,e,t,1)},p.toFixed=function(e,t){return null!=e&&(ft(e,0,ut),e=e+this.e+1),C(this,e,t)},p.toFormat=function(e,t,i){var s,n=this;if(null==i)null!=e&&t&&"object"==typeof t?(i=t,t=null):e&&"object"==typeof e?(i=e,e=t=null):i=x;else if("object"!=typeof i)throw Error(at+"Argument not an object: "+i);if(s=n.toFixed(e,t),n.c){var o,a=s.split("."),r=+i.groupSize,l=+i.secondaryGroupSize,c=i.groupSeparator||"",h=a[0],d=a[1],p=n.s<0,u=p?h.slice(1):h,_=u.length;if(l&&(o=r,r=l,l=o,_-=o),r>0&&_>0){for(o=_%r||r,h=u.substr(0,o);o<_;o+=r)h+=c+u.substr(o,r);l>0&&(h+=c+u.slice(o)),p&&(h="-"+h)}s=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(e){var t,s,n,o,a,r,l,c,h,d,p,_,g=this,f=g.c;if(null!=e&&(!(l=new M(e)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(at+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+I(l));if(!f)return new M(g);for(t=new M(u),h=s=new M(u),n=c=new M(u),_=mt(f),a=t.e=_.length-g.e-1,t.c[0]=dt[(r=a%ct)<0?ct+r:r],e=!e||l.comparedTo(t)>0?a>0?t:h:l,r=b,b=1/0,l=new M(_),c.c[0]=0;d=i(l,t,0,1),1!=(o=s.plus(d.times(n))).comparedTo(e);)s=n,n=o,h=c.plus(d.times(o=h)),c=o,t=l.minus(d.times(o=t)),l=o;return o=i(e.minus(s),n,0,1),c=c.plus(o.times(h)),s=s.plus(o.times(n)),c.s=h.s=g.s,p=i(h,n,a*=2,m).minus(g).abs().comparedTo(i(c,s,a,m).minus(g).abs())<1?[h,n]:[c,s],b=r,p},p.toNumber=function(){return+I(this)},p.toPrecision=function(e,t){return null!=e&&ft(e,1,ut),C(this,e,t,2)},p.toString=function(e){var t,i=this,n=i.s,o=i.e;return null===o?n?(t="Infinity",n<0&&(t="-"+t)):t="NaN":(null==e?t=o<=g||o>=f?bt(mt(i.c),o):yt(mt(i.c),o,"0"):10===e&&S?t=yt(mt((i=T(new M(i),_+o+1,m)).c),i.e,"0"):(ft(e,2,$.length,"Base"),t=s(yt(mt(i.c),o,"0"),10,e,n,!0)),n<0&&i.c[0]&&(t="-"+t)),t},p.valueOf=p.toJSON=function(){return I(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=t&&M.set(t),M}(),kt=class{key;left=null;right=null;constructor(e){this.key=e}},xt=class extends kt{constructor(e){super(e)}},$t=class{size=0;modificationCount=0;splayCount=0;splay(e){const t=this.root;if(null==t)return this.compare(e,e),-1;let i=null,s=null,n=null,o=null,a=t;const r=this.compare;let l;for(;;)if(l=r(a.key,e),l>0){let t=a.left;if(null==t)break;if(l=r(t.key,e),l>0&&(a.left=t.right,t.right=a,a=t,t=a.left,null==t))break;null==i?s=a:i.left=a,i=a,a=t}else{if(!(l<0))break;{let t=a.right;if(null==t)break;if(l=r(t.key,e),l<0&&(a.right=t.left,t.left=a,a=t,t=a.right,null==t))break;null==n?o=a:n.right=a,n=a,a=t}}return null!=n&&(n.right=a.left,a.left=o),null!=i&&(i.left=a.right,a.right=s),this.root!==a&&(this.root=a,this.splayCount++),l}splayMin(e){let t=e,i=t.left;for(;null!=i;){const e=i;t.left=e.right,e.right=t,t=e,i=t.left}return t}splayMax(e){let t=e,i=t.right;for(;null!=i;){const e=i;t.right=e.left,e.left=t,t=e,i=t.right}return t}_delete(e){if(null==this.root)return null;if(0!=this.splay(e))return null;let t=this.root;const i=t,s=t.left;if(this.size--,null==s)this.root=t.right;else{const e=t.right;t=this.splayMax(s),t.right=e,this.root=t}return this.modificationCount++,i}addNewRoot(e,t){this.size++,this.modificationCount++;const i=this.root;null!=i?(t<0?(e.left=i,e.right=i.right,i.right=null):(e.right=i,e.left=i.left,i.left=null),this.root=e):this.root=e}_first(){const e=this.root;return null==e?null:(this.root=this.splayMin(e),this.root)}_last(){const e=this.root;return null==e?null:(this.root=this.splayMax(e),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(e){return this.validKey(e)&&0==this.splay(e)}defaultCompare(){return(e,t)=>e<t?-1:e>t?1:0}wrap(){return{getRoot:()=>this.root,setRoot:e=>{this.root=e},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:e=>{this.splayCount=e},splay:e=>this.splay(e),has:e=>this.has(e)}}},St=class e extends $t{root=null;compare;validKey;constructor(e,t){super(),this.compare=e??this.defaultCompare(),this.validKey=t??(e=>null!=e&&null!=e)}delete(e){return!!this.validKey(e)&&null!=this._delete(e)}deleteAll(e){for(const t of e)this.delete(t)}forEach(e){const t=this[Symbol.iterator]();let i;for(;i=t.next(),!i.done;)e(i.value,i.value,this)}add(e){const t=this.splay(e);return 0!=t&&this.addNewRoot(new xt(e),t),this}addAndReturn(e){const t=this.splay(e);return 0!=t&&this.addNewRoot(new xt(e),t),this.root.key}addAll(e){for(const t of e)this.add(t)}isEmpty(){return null==this.root}isNotEmpty(){return null!=this.root}single(){if(0==this.size)throw"Bad state: No element";if(this.size>1)throw"Bad state: Too many element";return this.root.key}first(){if(0==this.size)throw"Bad state: No element";return this._first().key}last(){if(0==this.size)throw"Bad state: No element";return this._last().key}lastBefore(e){if(null==e)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(e)<0)return this.root.key;let t=this.root.left;if(null==t)return null;let i=t.right;for(;null!=i;)t=i,i=t.right;return t.key}firstAfter(e){if(null==e)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(e)>0)return this.root.key;let t=this.root.right;if(null==t)return null;let i=t.left;for(;null!=i;)t=i,i=t.left;return t.key}retainAll(t){const i=new e(this.compare,this.validKey),s=this.modificationCount;for(const e of t){if(s!=this.modificationCount)throw"Concurrent modification during iteration.";this.validKey(e)&&0==this.splay(e)&&i.add(this.root.key)}i.size!=this.size&&(this.root=i.root,this.size=i.size,this.modificationCount++)}lookup(e){if(!this.validKey(e))return null;return 0!=this.splay(e)?null:this.root.key}intersection(t){const i=new e(this.compare,this.validKey);for(const e of this)t.has(e)&&i.add(e);return i}difference(t){const i=new e(this.compare,this.validKey);for(const e of this)t.has(e)||i.add(e);return i}union(e){const t=this.clone();return t.addAll(e),t}clone(){const t=new e(this.compare,this.validKey);return t.size=this.size,t.root=this.copyNode(this.root),t}copyNode(e){if(null==e)return null;const t=new xt(e.key);return function e(t,i){let s,n;do{if(s=t.left,n=t.right,null!=s){const t=new xt(s.key);i.left=t,e(s,t)}if(null!=n){const e=new xt(n.key);i.right=e,t=n,i=e}}while(null!=n)}(e,t),t}toSet(){return this.clone()}entries(){return new Dt(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Ct(this.wrap())}[Symbol.toStringTag]="[object Set]"},Mt=class{tree;path=new Array;modificationCount=null;splayCount;constructor(e){this.tree=e,this.splayCount=e.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const e=this.path[this.path.length-1];return this.getValue(e)}rebuildPath(e){this.path.splice(0,this.path.length),this.tree.splay(e),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(e){for(;null!=e;)this.path.push(e),e=e.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let e=this.tree.getRoot();for(;null!=e;)this.path.push(e),e=e.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let e=this.path[this.path.length-1],t=e.right;if(null!=t){for(;null!=t;)this.path.push(t),t=t.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===e;)e=this.path.pop();return this.path.length>0}},Ct=class extends Mt{getValue(e){return e.key}},Dt=class extends Mt{getValue(e){return[e.key,e.key]}},Pt=e=>()=>e,Tt=e=>{const t=e?(t,i)=>i.minus(t).abs().isLessThanOrEqualTo(e):Pt(!1);return(e,i)=>t(e,i)?0:e.comparedTo(i)};function It(e){const t=e?(t,i,s,n,o)=>t.exponentiatedBy(2).isLessThanOrEqualTo(n.minus(i).exponentiatedBy(2).plus(o.minus(s).exponentiatedBy(2)).times(e)):Pt(!1);return(e,i,s)=>{const n=e.x,o=e.y,a=s.x,r=s.y,l=o.minus(r).times(i.x.minus(a)).minus(n.minus(a).times(i.y.minus(r)));return t(l,n,o,a,r)?0:l.comparedTo(0)}}var Rt=e=>e,At=e=>{if(e){const t=new St(Tt(e)),i=new St(Tt(e)),s=(e,t)=>t.addAndReturn(e),n=e=>({x:s(e.x,t),y:s(e.y,i)});return n({x:new wt(0),y:new wt(0)}),n}return Rt},zt=e=>({set:e=>{Ft=zt(e)},reset:()=>zt(e),compare:Tt(e),snap:At(e),orient:It(e)}),Ft=zt(),Nt=(e,t)=>e.ll.x.isLessThanOrEqualTo(t.x)&&t.x.isLessThanOrEqualTo(e.ur.x)&&e.ll.y.isLessThanOrEqualTo(t.y)&&t.y.isLessThanOrEqualTo(e.ur.y),Et=(e,t)=>{if(t.ur.x.isLessThan(e.ll.x)||e.ur.x.isLessThan(t.ll.x)||t.ur.y.isLessThan(e.ll.y)||e.ur.y.isLessThan(t.ll.y))return null;const i=e.ll.x.isLessThan(t.ll.x)?t.ll.x:e.ll.x,s=e.ur.x.isLessThan(t.ur.x)?e.ur.x:t.ur.x;return{ll:{x:i,y:e.ll.y.isLessThan(t.ll.y)?t.ll.y:e.ll.y},ur:{x:s,y:e.ur.y.isLessThan(t.ur.y)?e.ur.y:t.ur.y}}},Ot=(e,t)=>e.x.times(t.y).minus(e.y.times(t.x)),Ht=(e,t)=>e.x.times(t.x).plus(e.y.times(t.y)),Lt=e=>Ht(e,e).sqrt(),Bt=(e,t,i)=>{const s={x:t.x.minus(e.x),y:t.y.minus(e.y)},n={x:i.x.minus(e.x),y:i.y.minus(e.y)};return Ot(n,s).div(Lt(n)).div(Lt(s))},jt=(e,t,i)=>{const s={x:t.x.minus(e.x),y:t.y.minus(e.y)},n={x:i.x.minus(e.x),y:i.y.minus(e.y)};return Ht(n,s).div(Lt(n)).div(Lt(s))},qt=(e,t,i)=>t.y.isZero()?null:{x:e.x.plus(t.x.div(t.y).times(i.minus(e.y))),y:i},Wt=(e,t,i)=>t.x.isZero()?null:{x:i,y:e.y.plus(t.y.div(t.x).times(i.minus(e.x)))},Ut=class e{point;isLeft;segment;otherSE;consumedBy;static compare(t,i){const s=e.comparePoints(t.point,i.point);return 0!==s?s:(t.point!==i.point&&t.link(i),t.isLeft!==i.isLeft?t.isLeft?1:-1:Qt.compare(t.segment,i.segment))}static comparePoints(e,t){return e.x.isLessThan(t.x)?-1:e.x.isGreaterThan(t.x)?1:e.y.isLessThan(t.y)?-1:e.y.isGreaterThan(t.y)?1:0}constructor(e,t){void 0===e.events?e.events=[this]:e.events.push(this),this.point=e,this.isLeft=t}link(e){if(e.point===this.point)throw new Error("Tried to link already linked events");const t=e.point.events;for(let e=0,i=t.length;e<i;e++){const i=t[e];this.point.events.push(i),i.point=this.point}this.checkForConsuming()}checkForConsuming(){const e=this.point.events.length;for(let t=0;t<e;t++){const i=this.point.events[t];if(void 0===i.segment.consumedBy)for(let s=t+1;s<e;s++){const e=this.point.events[s];void 0===e.consumedBy&&(i.otherSE.point.events===e.otherSE.point.events&&i.segment.consume(e.segment))}}}getAvailableLinkedEvents(){const e=[];for(let t=0,i=this.point.events.length;t<i;t++){const i=this.point.events[t];i!==this&&!i.segment.ringOut&&i.segment.isInResult()&&e.push(i)}return e}getLeftmostComparator(e){const t=new Map,i=i=>{const s=i.otherSE;t.set(i,{sine:Bt(this.point,e.point,s.point),cosine:jt(this.point,e.point,s.point)})};return(e,s)=>{t.has(e)||i(e),t.has(s)||i(s);const{sine:n,cosine:o}=t.get(e),{sine:a,cosine:r}=t.get(s);return n.isGreaterThanOrEqualTo(0)&&a.isGreaterThanOrEqualTo(0)?o.isLessThan(r)?1:o.isGreaterThan(r)?-1:0:n.isLessThan(0)&&a.isLessThan(0)?o.isLessThan(r)?-1:o.isGreaterThan(r)?1:0:a.isLessThan(n)?-1:a.isGreaterThan(n)?1:0}}},Gt=class e{events;poly;_isExteriorRing;_enclosingRing;static factory(t){const i=[];for(let s=0,n=t.length;s<n;s++){const n=t[s];if(!n.isInResult()||n.ringOut)continue;let o=null,a=n.leftSE,r=n.rightSE;const l=[a],c=a.point,h=[];for(;o=a,a=r,l.push(a),a.point!==c;)for(;;){const t=a.getAvailableLinkedEvents();if(0===t.length){const e=l[0].point,t=l[l.length-1].point;throw new Error(`Unable to complete output ring starting at [${e.x}, ${e.y}]. Last matching segment found ends at [${t.x}, ${t.y}].`)}if(1===t.length){r=t[0].otherSE;break}let s=null;for(let e=0,t=h.length;e<t;e++)if(h[e].point===a.point){s=e;break}if(null!==s){const t=h.splice(s)[0],n=l.splice(t.index);n.unshift(n[0].otherSE),i.push(new e(n.reverse()));continue}h.push({index:l.length,point:a.point});const n=a.getLeftmostComparator(o);r=t.sort(n)[0].otherSE;break}i.push(new e(l))}return i}constructor(e){this.events=e;for(let t=0,i=e.length;t<i;t++)e[t].segment.ringOut=this;this.poly=null}getGeom(){let e=this.events[0].point;const t=[e];for(let i=1,s=this.events.length-1;i<s;i++){const s=this.events[i].point,n=this.events[i+1].point;0!==Ft.orient(s,e,n)&&(t.push(s),e=s)}if(1===t.length)return null;const i=t[0],s=t[1];0===Ft.orient(i,e,s)&&t.shift(),t.push(t[0]);const n=this.isExteriorRing()?1:-1,o=this.isExteriorRing()?0:t.length-1,a=this.isExteriorRing()?t.length:-1,r=[];for(let e=o;e!=a;e+=n)r.push([t[e].x.toNumber(),t[e].y.toNumber()]);return r}isExteriorRing(){if(void 0===this._isExteriorRing){const e=this.enclosingRing();this._isExteriorRing=!e||!e.isExteriorRing()}return this._isExteriorRing}enclosingRing(){return void 0===this._enclosingRing&&(this._enclosingRing=this._calcEnclosingRing()),this._enclosingRing}_calcEnclosingRing(){let e=this.events[0];for(let t=1,i=this.events.length;t<i;t++){const i=this.events[t];Ut.compare(e,i)>0&&(e=i)}let t=e.segment.prevInResult(),i=t?t.prevInResult():null;for(;;){if(!t)return null;if(!i)return t.ringOut;if(i.ringOut!==t.ringOut)return i.ringOut?.enclosingRing()!==t.ringOut?t.ringOut:t.ringOut?.enclosingRing();t=i.prevInResult(),i=t?t.prevInResult():null}}},Vt=class{exteriorRing;interiorRings;constructor(e){this.exteriorRing=e,e.poly=this,this.interiorRings=[]}addInterior(e){this.interiorRings.push(e),e.poly=this}getGeom(){const e=this.exteriorRing.getGeom();if(null===e)return null;const t=[e];for(let e=0,i=this.interiorRings.length;e<i;e++){const i=this.interiorRings[e].getGeom();null!==i&&t.push(i)}return t}},Kt=class{rings;polys;constructor(e){this.rings=e,this.polys=this._composePolys(e)}getGeom(){const e=[];for(let t=0,i=this.polys.length;t<i;t++){const i=this.polys[t].getGeom();null!==i&&e.push(i)}return e}_composePolys(e){const t=[];for(let i=0,s=e.length;i<s;i++){const s=e[i];if(!s.poly)if(s.isExteriorRing())t.push(new Vt(s));else{const e=s.enclosingRing();e?.poly||t.push(new Vt(e)),e?.poly?.addInterior(s)}}return t}},Yt=class{queue;tree;segments;constructor(e,t=Qt.compare){this.queue=e,this.tree=new St(t),this.segments=[]}process(e){const t=e.segment,i=[];if(e.consumedBy)return e.isLeft?this.queue.delete(e.otherSE):this.tree.delete(t),i;e.isLeft&&this.tree.add(t);let s=t,n=t;do{s=this.tree.lastBefore(s)}while(null!=s&&null!=s.consumedBy);do{n=this.tree.firstAfter(n)}while(null!=n&&null!=n.consumedBy);if(e.isLeft){let o=null;if(s){const e=s.getIntersection(t);if(null!==e&&(t.isAnEndpoint(e)||(o=e),!s.isAnEndpoint(e))){const t=this._splitSafely(s,e);for(let e=0,s=t.length;e<s;e++)i.push(t[e])}}let a=null;if(n){const e=n.getIntersection(t);if(null!==e&&(t.isAnEndpoint(e)||(a=e),!n.isAnEndpoint(e))){const t=this._splitSafely(n,e);for(let e=0,s=t.length;e<s;e++)i.push(t[e])}}if(null!==o||null!==a){let e=null;if(null===o)e=a;else if(null===a)e=o;else{e=Ut.comparePoints(o,a)<=0?o:a}this.queue.delete(t.rightSE),i.push(t.rightSE);const s=t.split(e);for(let e=0,t=s.length;e<t;e++)i.push(s[e])}i.length>0?(this.tree.delete(t),i.push(e)):(this.segments.push(t),t.prev=s)}else{if(s&&n){const e=s.getIntersection(n);if(null!==e){if(!s.isAnEndpoint(e)){const t=this._splitSafely(s,e);for(let e=0,s=t.length;e<s;e++)i.push(t[e])}if(!n.isAnEndpoint(e)){const t=this._splitSafely(n,e);for(let e=0,s=t.length;e<s;e++)i.push(t[e])}}}this.tree.delete(t)}return i}_splitSafely(e,t){this.tree.delete(e);const i=e.rightSE;this.queue.delete(i);const s=e.split(t);return s.push(i),void 0===e.consumedBy&&this.tree.add(e),s}},Jt=new class{type;numMultiPolys;run(e,t,i){Jt.type=e;const s=[new ii(t,!0)];for(let e=0,t=i.length;e<t;e++)s.push(new ii(i[e],!1));if(Jt.numMultiPolys=s.length,"difference"===Jt.type){const e=s[0];let t=1;for(;t<s.length;)null!==Et(s[t].bbox,e.bbox)?t++:s.splice(t,1)}if("intersection"===Jt.type)for(let e=0,t=s.length;e<t;e++){const t=s[e];for(let i=e+1,n=s.length;i<n;i++)if(null===Et(t.bbox,s[i].bbox))return[]}const n=new St(Ut.compare);for(let e=0,t=s.length;e<t;e++){const t=s[e].getSweepEvents();for(let e=0,i=t.length;e<i;e++)n.add(t[e])}const o=new Yt(n);let a=null;for(0!=n.size&&(a=n.first(),n.delete(a));a;){const e=o.process(a);for(let t=0,i=e.length;t<i;t++){const i=e[t];void 0===i.consumedBy&&n.add(i)}0!=n.size?(a=n.first(),n.delete(a)):a=null}Ft.reset();const r=Gt.factory(o.segments);return new Kt(r).getGeom()}},Xt=Jt,Zt=0,Qt=class e{id;leftSE;rightSE;rings;windings;ringOut;consumedBy;prev;_prevInResult;_beforeState;_afterState;_isInResult;static compare(e,t){const i=e.leftSE.point.x,s=t.leftSE.point.x,n=e.rightSE.point.x,o=t.rightSE.point.x;if(o.isLessThan(i))return 1;if(n.isLessThan(s))return-1;const a=e.leftSE.point.y,r=t.leftSE.point.y,l=e.rightSE.point.y,c=t.rightSE.point.y;if(i.isLessThan(s)){if(r.isLessThan(a)&&r.isLessThan(l))return 1;if(r.isGreaterThan(a)&&r.isGreaterThan(l))return-1;const i=e.comparePoint(t.leftSE.point);if(i<0)return 1;if(i>0)return-1;const s=t.comparePoint(e.rightSE.point);return 0!==s?s:-1}if(i.isGreaterThan(s)){if(a.isLessThan(r)&&a.isLessThan(c))return-1;if(a.isGreaterThan(r)&&a.isGreaterThan(c))return 1;const i=t.comparePoint(e.leftSE.point);if(0!==i)return i;const s=e.comparePoint(t.rightSE.point);return s<0?1:s>0?-1:1}if(a.isLessThan(r))return-1;if(a.isGreaterThan(r))return 1;if(n.isLessThan(o)){const i=t.comparePoint(e.rightSE.point);if(0!==i)return i}if(n.isGreaterThan(o)){const i=e.comparePoint(t.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!n.eq(o)){const e=l.minus(a),t=n.minus(i),h=c.minus(r),d=o.minus(s);if(e.isGreaterThan(t)&&h.isLessThan(d))return 1;if(e.isLessThan(t)&&h.isGreaterThan(d))return-1}return n.isGreaterThan(o)?1:n.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:e.id<t.id?-1:e.id>t.id?1:0}constructor(e,t,i,s){this.id=++Zt,this.leftSE=e,e.segment=this,e.otherSE=t,this.rightSE=t,t.segment=this,t.otherSE=e,this.rings=i,this.windings=s}static fromRing(t,i,s){let n,o,a;const r=Ut.comparePoints(t,i);if(r<0)n=t,o=i,a=1;else{if(!(r>0))throw new Error(`Tried to create degenerate segment at [${t.x}, ${t.y}]`);n=i,o=t,a=-1}const l=new Ut(n,!0),c=new Ut(o,!1);return new e(l,c,[s],[a])}replaceRightSE(e){this.rightSE=e,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const e=this.leftSE.point.y,t=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:e.isLessThan(t)?e:t},ur:{x:this.rightSE.point.x,y:e.isGreaterThan(t)?e:t}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(e){return e.x.eq(this.leftSE.point.x)&&e.y.eq(this.leftSE.point.y)||e.x.eq(this.rightSE.point.x)&&e.y.eq(this.rightSE.point.y)}comparePoint(e){return Ft.orient(this.leftSE.point,e,this.rightSE.point)}getIntersection(e){const t=this.bbox(),i=e.bbox(),s=Et(t,i);if(null===s)return null;const n=this.leftSE.point,o=this.rightSE.point,a=e.leftSE.point,r=e.rightSE.point,l=Nt(t,a)&&0===this.comparePoint(a),c=Nt(i,n)&&0===e.comparePoint(n),h=Nt(t,r)&&0===this.comparePoint(r),d=Nt(i,o)&&0===e.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?r:null;if(c)return h&&n.x.eq(r.x)&&n.y.eq(r.y)?null:n;if(l)return d&&o.x.eq(a.x)&&o.y.eq(a.y)?null:a;if(d&&h)return null;if(d)return o;if(h)return r;const p=((e,t,i,s)=>{if(t.x.isZero())return Wt(i,s,e.x);if(s.x.isZero())return Wt(e,t,i.x);if(t.y.isZero())return qt(i,s,e.y);if(s.y.isZero())return qt(e,t,i.y);const n=Ot(t,s);if(n.isZero())return null;const o={x:i.x.minus(e.x),y:i.y.minus(e.y)},a=Ot(o,t).div(n),r=Ot(o,s).div(n),l=e.x.plus(r.times(t.x)),c=i.x.plus(a.times(s.x)),h=e.y.plus(r.times(t.y)),d=i.y.plus(a.times(s.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(n,this.vector(),a,e.vector());return null===p?null:Nt(s,p)?Ft.snap(p):null}split(t){const i=[],s=void 0!==t.events,n=new Ut(t,!0),o=new Ut(t,!1),a=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(n);const r=new e(n,a,this.rings.slice(),this.windings.slice());return Ut.comparePoints(r.leftSE.point,r.rightSE.point)>0&&r.swapEvents(),Ut.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(n.checkForConsuming(),o.checkForConsuming()),i}swapEvents(){const e=this.rightSE;this.rightSE=this.leftSE,this.leftSE=e,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let e=0,t=this.windings.length;e<t;e++)this.windings[e]*=-1}consume(t){let i=this,s=t;for(;i.consumedBy;)i=i.consumedBy;for(;s.consumedBy;)s=s.consumedBy;const n=e.compare(i,s);if(0!==n){if(n>0){const e=i;i=s,s=e}if(i.prev===s){const e=i;i=s,s=e}for(let e=0,t=s.rings.length;e<t;e++){const t=s.rings[e],n=s.windings[e],o=i.rings.indexOf(t);-1===o?(i.rings.push(t),i.windings.push(n)):i.windings[o]+=n}s.rings=null,s.windings=null,s.consumedBy=i,s.leftSE.consumedBy=i.leftSE,s.rightSE.consumedBy=i.rightSE}}prevInResult(){return void 0!==this._prevInResult||(this.prev?this.prev.isInResult()?this._prevInResult=this.prev:this._prevInResult=this.prev.prevInResult():this._prevInResult=null),this._prevInResult}beforeState(){if(void 0!==this._beforeState)return this._beforeState;if(this.prev){const e=this.prev.consumedBy||this.prev;this._beforeState=e.afterState()}else this._beforeState={rings:[],windings:[],multiPolys:[]};return this._beforeState}afterState(){if(void 0!==this._afterState)return this._afterState;const e=this.beforeState();this._afterState={rings:e.rings.slice(0),windings:e.windings.slice(0),multiPolys:[]};const t=this._afterState.rings,i=this._afterState.windings,s=this._afterState.multiPolys;for(let e=0,s=this.rings.length;e<s;e++){const s=this.rings[e],n=this.windings[e],o=t.indexOf(s);-1===o?(t.push(s),i.push(n)):i[o]+=n}const n=[],o=[];for(let e=0,s=t.length;e<s;e++){if(0===i[e])continue;const s=t[e],a=s.poly;if(-1===o.indexOf(a))if(s.isExterior)n.push(a);else{-1===o.indexOf(a)&&o.push(a);const e=n.indexOf(s.poly);-1!==e&&n.splice(e,1)}}for(let e=0,t=n.length;e<t;e++){const t=n[e].multiPoly;-1===s.indexOf(t)&&s.push(t)}return this._afterState}isInResult(){if(this.consumedBy)return!1;if(void 0!==this._isInResult)return this._isInResult;const e=this.beforeState().multiPolys,t=this.afterState().multiPolys;switch(Xt.type){case"union":{const i=0===e.length,s=0===t.length;this._isInResult=i!==s;break}case"intersection":{let i,s;e.length<t.length?(i=e.length,s=t.length):(i=t.length,s=e.length),this._isInResult=s===Xt.numMultiPolys&&i<s;break}case"xor":{const i=Math.abs(e.length-t.length);this._isInResult=i%2==1;break}case"difference":{const i=e=>1===e.length&&e[0].isSubject;this._isInResult=i(e)!==i(t);break}}return this._isInResult}},ei=class{poly;isExterior;segments;bbox;constructor(e,t,i){if(!Array.isArray(e)||0===e.length)throw new Error("Input geometry is not a valid Polygon or MultiPolygon");if(this.poly=t,this.isExterior=i,this.segments=[],"number"!=typeof e[0][0]||"number"!=typeof e[0][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const s=Ft.snap({x:new wt(e[0][0]),y:new wt(e[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let n=s;for(let t=1,i=e.length;t<i;t++){if("number"!=typeof e[t][0]||"number"!=typeof e[t][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const i=Ft.snap({x:new wt(e[t][0]),y:new wt(e[t][1])});i.x.eq(n.x)&&i.y.eq(n.y)||(this.segments.push(Qt.fromRing(n,i,this)),i.x.isLessThan(this.bbox.ll.x)&&(this.bbox.ll.x=i.x),i.y.isLessThan(this.bbox.ll.y)&&(this.bbox.ll.y=i.y),i.x.isGreaterThan(this.bbox.ur.x)&&(this.bbox.ur.x=i.x),i.y.isGreaterThan(this.bbox.ur.y)&&(this.bbox.ur.y=i.y),n=i)}s.x.eq(n.x)&&s.y.eq(n.y)||this.segments.push(Qt.fromRing(n,s,this))}getSweepEvents(){const e=[];for(let t=0,i=this.segments.length;t<i;t++){const i=this.segments[t];e.push(i.leftSE),e.push(i.rightSE)}return e}},ti=class{multiPoly;exteriorRing;interiorRings;bbox;constructor(e,t){if(!Array.isArray(e))throw new Error("Input geometry is not a valid Polygon or MultiPolygon");this.exteriorRing=new ei(e[0],this,!0),this.bbox={ll:{x:this.exteriorRing.bbox.ll.x,y:this.exteriorRing.bbox.ll.y},ur:{x:this.exteriorRing.bbox.ur.x,y:this.exteriorRing.bbox.ur.y}},this.interiorRings=[];for(let t=1,i=e.length;t<i;t++){const i=new ei(e[t],this,!1);i.bbox.ll.x.isLessThan(this.bbox.ll.x)&&(this.bbox.ll.x=i.bbox.ll.x),i.bbox.ll.y.isLessThan(this.bbox.ll.y)&&(this.bbox.ll.y=i.bbox.ll.y),i.bbox.ur.x.isGreaterThan(this.bbox.ur.x)&&(this.bbox.ur.x=i.bbox.ur.x),i.bbox.ur.y.isGreaterThan(this.bbox.ur.y)&&(this.bbox.ur.y=i.bbox.ur.y),this.interiorRings.push(i)}this.multiPoly=t}getSweepEvents(){const e=this.exteriorRing.getSweepEvents();for(let t=0,i=this.interiorRings.length;t<i;t++){const i=this.interiorRings[t].getSweepEvents();for(let t=0,s=i.length;t<s;t++)e.push(i[t])}return e}},ii=class{isSubject;polys;bbox;constructor(e,t){if(!Array.isArray(e))throw new Error("Input geometry is not a valid Polygon or MultiPolygon");try{"number"==typeof e[0][0][0]&&(e=[e])}catch(e){}this.polys=[],this.bbox={ll:{x:new wt(Number.POSITIVE_INFINITY),y:new wt(Number.POSITIVE_INFINITY)},ur:{x:new wt(Number.NEGATIVE_INFINITY),y:new wt(Number.NEGATIVE_INFINITY)}};for(let t=0,i=e.length;t<i;t++){const i=new ti(e[t],this);i.bbox.ll.x.isLessThan(this.bbox.ll.x)&&(this.bbox.ll.x=i.bbox.ll.x),i.bbox.ll.y.isLessThan(this.bbox.ll.y)&&(this.bbox.ll.y=i.bbox.ll.y),i.bbox.ur.x.isGreaterThan(this.bbox.ur.x)&&(this.bbox.ur.x=i.bbox.ur.x),i.bbox.ur.y.isGreaterThan(this.bbox.ur.y)&&(this.bbox.ur.y=i.bbox.ur.y),this.polys.push(i)}this.isSubject=t}getSweepEvents(){const e=[];for(let t=0,i=this.polys.length;t<i;t++){const i=this.polys[t].getSweepEvents();for(let t=0,s=i.length;t<s;t++)e.push(i[t])}return e}},si=(e,...t)=>Xt.run("union",e,t),ni=(e,...t)=>Xt.run("intersection",e,t),oi=(e,...t)=>Xt.run("difference",e,t);function ai(e){const t=Math.max(0,Math.min(120,(e-40)/140*120));return`hsl(${Math.round(t)}, 85%, 55%)`}function ri(e,t){if(!(Number.isFinite(e)&&t>0))return e;const i=Math.round(e/t)*t;return Math.abs(i-e)<=1e-9*t?e:i}function li(e,t){if(t){const t=e/2.54;let i=Math.floor(t/12),s=Math.round(t-12*i);return 12===s&&(i+=1,s=0),`${i}′ ${s}″`}return`${(e/100).toFixed(2)} m`}function ci(e,t,i=1){const s=e[0].toFixed(i),n=e[1].toFixed(i),o=t[0].toFixed(i),a=t[1].toFixed(i),r=s<o||s===o&&n<=a,[l,c,h,d]=r?[s,n,o,a]:[o,a,s,n];return`${l},${c}-${h},${d}`}function hi(e){return e?.poly?.length>=3?e.poly:e&&null!=e.x&&null!=e.y&&null!=e.w&&null!=e.h?[[e.x,e.y],[e.x+e.w,e.y],[e.x+e.w,e.y+e.h],[e.x,e.y+e.h]]:null}function di(e){const t=[];for(const i of e||[])i?.poly?.length>=3?t.push({poly:i.poly.map(e=>e.join(",")).join(" ")}):i&&null!=i.x&&null!=i.y&&null!=i.w&&null!=i.h&&t.push({rect:{x:i.x,y:i.y,w:i.w,h:i.h,rx:.03*Math.min(i.w,i.h)}});return t}function pi(e){const t=[],i=new Set;for(const s of e||[]){const e=hi(s);if(e)for(let s=0;s<e.length;s++){const n=e[s],o=e[(s+1)%e.length],a=ci(n,o,5);i.has(a)||(i.add(a),t.push([n[0],n[1],o[0],o[1]]))}}return t}function ui(e,t,i,s={}){let n=null,o=i;for(const i of pi(t)){const[t,a,r,l]=i,c=r-t,h=l-a,d=c*c+h*h;if(!d)continue;let p=((e[0]-t)*c+(e[1]-a)*h)/d;p=Math.max(0,Math.min(1,p));const u=[t+p*c,a+p*h],_=Math.hypot(e[0]-u[0],e[1]-u[1]);if(_<o){o=_;let e=180*Math.atan2(h,c)/Math.PI;if(e>=90?e-=180:e<-90&&(e+=180),s.step&&s.step>0){const i=Math.sqrt(d),o=Math.min(Math.max(s.length||0,0)/2,i/2);let r=Math.round(p*i/s.step)*s.step;Math.abs(p*i-i/2)<=s.step/2&&(r=i/2),r=Math.max(o,Math.min(i-o,r));const l=r/i;n={x:t+l*c,y:a+l*h,angle:e}}else n={x:u[0],y:u[1],angle:e}}}return n}function _i(e,t,i,s,n,o=1){const a=t*Math.PI/180,r=[Math.cos(a),Math.sin(a)];let l=null,c=o;for(const t of pi(s)){const i=[[t[0],t[1]],[t[2],t[3]]],s=t=>Math.abs(r[0]*(t[1]-e[1])-r[1]*(t[0]-e[0]));if(s(i[0])>o||s(i[1])>o)continue;const n=(i[0][0]-e[0])*r[0]+(i[0][1]-e[1])*r[1],a=(i[1][0]-e[0])*r[0]+(i[1][1]-e[1])*r[1],h=Math.min(n,a),d=Math.max(n,a),p=h>0?h:d<0?-d:0;p<c&&(c=p,l=[h,d])}if(!l)return null;const[h,d]=l,p=i/2,u=Math.max(0,-p-h),_=Math.max(0,d-p),m=t=>[e[0]+r[0]*t,e[1]+r[1]*t],g=(h+d)/2;return{wallA:m(h),wallB:m(d),sideA:u,sideB:_,midA:m((h-p)/2),midB:m((p+d)/2),wallCenter:m(g),centered:Math.abs(g)<=n}}function mi(e,t,i=!1){if("passage"===e)return 1;if(null==t||"unavailable"===t||"unknown"===t)return"window"===e?0:1;const s=function(e){return["on","open","home","detected","playing","cleaning"].includes(String(e))}(t)!==!!i;return s?1:0}function gi(e){return"passage"===e.type?[]:[e.contact,e.lock].filter(e=>"string"==typeof e&&e.length>0)}function fi(e,t,i=.001){return Math.abs(e[0]-t[0])<i&&Math.abs(e[1]-t[1])<i}function vi(e,t){let i=!1;for(let s=0,n=t.length-1;s<t.length;n=s++){const[o,a]=t[s],[r,l]=t[n];a>e[1]!=l>e[1]&&e[0]<(r-o)*(e[1]-a)/(l-a)+o&&(i=!i)}return i}function bi(e,t,i){const s=i[0]-t[0],n=i[1]-t[1],o=s*s+n*n;let a=o?((e[0]-t[0])*s+(e[1]-t[1])*n)/o:0;return a=Math.max(0,Math.min(1,a)),Math.hypot(e[0]-(t[0]+a*s),e[1]-(t[1]+a*n))}function yi(e,t,i=1e-6){if(!t||t.length<2)return!1;for(let s=0;s<t.length;s++)if(bi(e,t[s],t[(s+1)%t.length])<=i)return!0;return!1}function wi(e,t,i=1e-6){return!(!t||t.length<3)&&(!yi(e,t,i)&&vi(e,t))}function ki(e,t,i){return(t[0]-e[0])*(i[1]-e[1])-(t[1]-e[1])*(i[0]-e[0])}function xi(e,t,i,s,n=1e-9){const o=ki(i,s,e),a=ki(i,s,t),r=ki(e,t,i),l=ki(e,t,s);return(o>n&&a<-n||o<-n&&a>n)&&(r>n&&l<-n||r<-n&&l>n)}function $i(e,t=24){const i=e.map(e=>e[0]),s=e.map(e=>e[1]),n=Math.min(...i),o=Math.max(...i),a=Math.min(...s),r=Math.max(...s),l=Math.max(o-n,r-a)||1;let c=0,h=0,d=0;for(let t=0;t<e.length;t++){const i=e[t],s=e[(t+1)%e.length],n=i[0]*s[1]-s[0]*i[1];c+=n,h+=(i[0]+s[0])*n,d+=(i[1]+s[1])*n}const p=Math.abs(c)>1e-9?[h/(3*c),d/(3*c)]:[(n+o)/2,(a+r)/2],u=(t,i)=>{const s=((t,i)=>{if(!vi([t,i],e))return-1/0;let s=1/0;for(let n=0;n<e.length;n++){const o=e[n],a=e[(n+1)%e.length];s=Math.min(s,Hs([t,i],[o[0],o[1],a[0],a[1]]))}return s})(t,i);return s===-1/0?s:s-.08*Math.hypot(t-p[0],i-p[1])-1e-4*l};let _=null,m=-1/0;for(let e=1;e<t;e++)for(let i=1;i<t;i++){const s=n+(o-n)*e/t,l=a+(r-a)*i/t,c=u(s,l);c>m&&(m=c,_=[s,l])}if(_){const[e,i]=_,s=(o-n)/t,l=(r-a)/t;for(let t=-4;t<=4;t++)for(let n=-4;n<=4;n++){const o=e+s*t/4,a=i+l*n/4,r=u(o,a);r>m&&(m=r,_=[o,a])}}return _||Si(e)||e[0]}function Si(e,t=1e-6){if(!e||e.length<3)return null;const i=e.length,s=[e.reduce((e,t)=>e+t[0],0)/i,e.reduce((e,t)=>e+t[1],0)/i];if(wi(s,e,t))return s;for(let s=0;s<i;s++){const n=e[(s-1+i)%i],o=e[s],a=e[(s+1)%i],r=[(n[0]+o[0]+a[0])/3,(n[1]+o[1]+a[1])/3];if(wi(r,e,t))return r}for(let s=0;s<i;s++)for(let n=s+2;n<i;n++){const i=[(e[s][0]+e[n][0])/2,(e[s][1]+e[n][1])/2];if(wi(i,e,t))return i}return null}function Mi(e,t,i){let s=!0;for(const n of e){if(wi(n,t,i))return!0;yi(n,t,i)||(s=!1)}if(s){const s=Si(e,i);return!!s&&wi(s,t,i)}return!1}function Ci(e,t,i=1e-6){if(!e||!t||e.length<3||t.length<3)return!1;for(let i=0;i<t.length;i++)for(let s=0;s<e.length;s++)if(xi(t[i],t[(i+1)%t.length],e[s],e[(s+1)%e.length]))return!1;for(const s of t)if(!wi(s,e,i)&&!yi(s,e,i))return!1;const s=Si(t,i);return!!s&&wi(s,e,i)&&Ti(t)<Ti(e)-i}function Di(e,t,i=1e-6){if(!e||!t||e.length<3||t.length<3)return!1;for(let i=0;i<e.length;i++)for(let s=0;s<t.length;s++)if(xi(e[i],e[(i+1)%e.length],t[s],t[(s+1)%t.length]))return!0;return!Ci(e,t,i)&&!Ci(t,e,i)&&(Mi(e,t,i)||Mi(t,e,i))}function Pi(e,t,i=1e-6){const s=t.filter(t=>Ci(e,t,i));return s.filter(e=>!s.some(t=>t!==e&&Ci(t,e,i)))}function Ti(e){if(!e||e.length<3)return 0;let t=0;for(let i=0;i<e.length;i++){const s=e[i],n=e[(i+1)%e.length];t+=s[0]*n[1]-n[0]*s[1]}return Math.abs(t)/2}function Ii(e){return[[...e.map(e=>[e[0],e[1]]),[e[0][0],e[0][1]]]]}function Ri(e,t,i){for(let s=0;s<e.length;s++)if(bi(t,e[s],e[(s+1)%e.length])<=i)return s;return-1}function Ai(e,t){const i=[];for(const s of e)i.length&&fi(i[i.length-1],s,t)||i.push(s);return i.length>1&&fi(i[0],i[i.length-1],t)&&i.pop(),i}function zi(e,t,i=1e-6){if(!e||e.length<3||!t||t.length<2)return null;const s=t[0],n=t[t.length-1];if(fi(s,n,i))return null;const o=Ri(e,s,i),a=Ri(e,n,i);if(o<0||a<0)return null;const r=t.slice(1,-1);for(const t of r)if(!wi(t,e,i))return null;for(let i=0;i<t.length-1;i++)for(let s=0;s<e.length;s++)if(xi(t[i],t[i+1],e[s],e[(s+1)%e.length]))return null;for(let e=0;e<t.length-1;e++)for(let i=e+2;i<t.length-1;i++)if(xi(t[e],t[e+1],t[i],t[i+1]))return null;if(2===t.length&&!wi([(s[0]+n[0])/2,(s[1]+n[1])/2],e,i))return null;const l=(t,s,n,o)=>{const a=[t];let r=(s+1)%e.length;for(let t=0;t<=e.length&&(a.push(e[r]),r!==o);t++)r=(r+1)%e.length;return a.push(n),Ai(a,i)};let c,h;if(o===a){const a=Ai([...t],i);if(a.length<3||Ti(a)<=i)return null;const r=[];for(let i=0;i<e.length;i++)if(r.push(e[i]),i===o){const i=(e[(o+1)%e.length][0]-e[o][0])*(n[0]-s[0])+(e[(o+1)%e.length][1]-e[o][1])*(n[1]-s[1])>=0?t:[...t].reverse();for(const e of i)r.push(e)}c=Ai(r,i),h=a}else c=Ai([...l(s,o,n,a),...[...r].reverse()],i),h=Ai([...l(n,a,s,o),...r],i);return c.length<3||h.length<3||Ti(c)<=i||Ti(h)<=i||Math.abs(Ti(c)+Ti(h)-Ti(e))>Math.max(i,1e-6*Ti(e))?null:[c,h]}function Fi(e,t,i){const[s,n]=e.split(":");return"device"===s?n:"entity"===s?"lg_"+n:t&&t.startsWith("v_")?t:i()}function Ni(e){return e.length?Math.round(e.reduce((e,t)=>e+t,0)/e.length):null}function Ei(e,t){if(t>e[2]/e[3]){const i=e[3],s=e[3]*t;return{x:e[0]-(s-e[2])/2,y:e[1],w:s,h:i}}const i=e[2],s=e[2]/t;return{x:e[0],y:e[1]-(s-e[3])/2,w:i,h:s}}function Oi(e,t,i,s){if(e.length<2)return;const n=t.x+s,o=t.x+t.w-s,a=t.y+s,r=t.y+t.h-s;for(let t=0;t<60;t++){let t=!1;for(let s=0;s<e.length;s++)for(let n=s+1;n<e.length;n++){const o=e[n].x-e[s].x,a=e[n].y-e[s].y,r=Math.hypot(o,a)||.001;if(r<i){const l=(i-r)/2,c=o/r,h=a/r;e[s].x-=c*l,e[s].y-=h*l,e[n].x+=c*l,e[n].y+=h*l,t=!0}}for(const t of e)t.x=Math.max(n,Math.min(o,t.x)),t.y=Math.max(a,Math.min(r,t.y));if(!t)break}}function Hi(e){if(!e)return null;const t=e.trim();return/^(https?:)?\/\//i.test(t)||t.startsWith("/")||/^[\w./#?=&%~-]+$/i.test(t)?/^[a-z][\w+.-]*:/i.test(t)&&!/^https?:/i.test(t)?null:t:null}Ft.set;const Li=["badge","icon_ripple","value","static_icon"];function Bi(e){return"ripple"===e?"icon_ripple":Li.includes(e)?e:"badge"}const ji=["info","more-info","toggle","run"],qi=["custom","lqi","light","temp"],Wi=["none","lqi","light","temp","custom"],Ui=new Set(["garage","door","gate"]),Gi=["automation","script","scene"];const Vi="—",Ki="{}";function Yi(e){const t=String(e??"").trim();if(!t)return null;let i=t,s="";const n=t.indexOf(":");if(n>=0)i=t.slice(0,n).trim(),s=t.slice(n+1).trim();else{const e=t.split(".");e.length>2&&(i=e.slice(0,2).join("."),s=e.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?n>=0&&!s||s&&!/^[a-zA-Z0-9_.-]+$/.test(s)?null:s?{entity:i,attr:s}:{entity:i}:null}function Ji(e,t){const i=String(e??"").trim(),s=String(t??"").trim(),n=Yi(s?`${i}:${s}`:i);return n?`{${n.entity}${n.attr?`:${n.attr}`:""}}`:""}function Xi(e,t,i){const s=String(t??"").trim();if(!s)return null;const n=e?.states?.[s];if(!n)return null;const o=String(i??"").trim(),a=e=>e.slice(0,60);if(o){const t=function(e){if(null==e)return null;if(Array.isArray(e)){const t=e.map(e=>null==e?"":String(e)).join(", ");return t?t.slice(0,60):null}if("object"==typeof e)return null;const t=String(e);return""===t?null:t.slice(0,60)}(n.attributes?.[o]);if(null===t)return null;const i=e?.formatEntityAttributeValue;if("function"==typeof i)try{const t=i.call(e,n,o);if("string"==typeof t&&""!==t)return{text:a(t),formatted:!0}}catch{}return{text:t,formatted:!1}}const r=n.state;if(null==r||""===r)return null;const l=e?.formatEntityState;if("function"==typeof l)try{const t=l.call(e,n);if("string"==typeof t&&""!==t)return{text:a(t),formatted:!0}}catch{}return{text:a(String(r)),formatted:!1}}function Zi(e,t,i){const s=String(t??"").trim(),n=String(i??"").trim()||s;if(!n)return e.text;const o=e.formatted&&s?function(e,t){if(!t)return e;const i=e.replace(/\s+$/,"");return i.endsWith(t)?i.slice(0,i.length-t.length).replace(/\s+$/,""):e}(e.text,s):e.text;return`${o} ${n}`}function Qi(e,t){const i=(t?.entity||"").trim();if(!i)return"";const s=e?.states?.[i],n=s?.state;if(!s||null==n||""===n||"unavailable"===n||"unknown"===n)return Vi;const o=(t?.attr||"").trim(),a=Xi(e,i,o||null);if(null===a)return Vi;return Zi(a,o?"":String(s.attributes?.unit_of_measurement??"").trim(),t?.unit)}function es(e,t,i,s=()=>!0){const n=e??"";let o=!1;const a=n.replace(/\{([^{}\r\n]+)\}/g,(e,t)=>{const n=Yi(t);return n?(o=!0,s(n.entity||"")?Qi(i,n):Vi):e});if(o)return a;const r=(t?.entity||"").trim();if(!r)return n;if(!s(r)){const e=n.indexOf(Ki);return e>=0?n.slice(0,e)+Vi+n.slice(e+2):n?`${n} ${Vi}`:Vi}const l=Qi(i,t),c=n.indexOf(Ki);return c>=0?n.slice(0,c)+l+n.slice(c+2):n?`${n} ${l}`:l}const ts=20;function is(e){const t=Number(e?.scale);if(Number.isFinite(t)&&t>0)return Math.min(20,Math.max(.15,t));return{s:.7,m:1,l:1.5}[String(e?.size??"")]??1}function ss(e,t){if(!t)return e;let i=e;for(const[e,s]of Object.entries(t))i=i.split("{"+e+"}").join(String(s));return i}const ns="#55606c",os=.55,as={c:"#607d8b",a:.18};function rs(e,t=as){const i=e&&"object"==typeof e?e:null,s=i?.a;return{c:Ie(i?.c,t.c),a:"number"==typeof s&&Number.isFinite(s)?Math.min(1,Math.max(0,s)):t.a}}function ls(e,t){const i=rs(e),s=t?.settings?.custom_fill;return s&&"object"==typeof s?rs(s,i):i}function cs(e){const t=e?.settings||{},i=!e?.plan_url,s="glow"===t.fill_mode;return{showBorders:t.show_borders??i,showNames:t.show_names??i,color:Ie(t.room_color,ns),opacity:"number"==typeof t.room_opacity?Math.min(1,Math.max(0,t.room_opacity)):os,fill:["lqi","light","temp","custom"].includes(t.fill_mode)?t.fill_mode:"none",customFill:rs(t.custom_fill),glow:"boolean"==typeof t.glow_enabled?t.glow_enabled:s,tempMin:"number"==typeof t.temp_min?t.temp_min:20,tempMax:"number"==typeof t.temp_max?t.temp_max:25,showLqi:"boolean"==typeof t.show_lqi?t.show_lqi:null,cardFontScale:"number"==typeof t.card_font_scale&&t.card_font_scale>0?Math.min(3,Math.max(.5,t.card_font_scale)):1,labelTemp:!0===t.label_temp,labelHum:!0===t.label_hum,labelLqi:!0===t.label_lqi,labelLight:!0===t.label_light,bgColor:Ie(t.bg_color,null),hideDecor:!0===t.hide_decor,hideOpenings:!0===t.hide_openings}}function hs(e,t){if(t.bgColor)return t.bgColor;const i=e?.bg_color;return Ie(i,"")}const ds={light_on:{c:"#ffd45c",a:.18},light_off:{c:"#9aa0a6",a:.14},light_none:{c:"#6b7480",a:0},temp_cold:{c:"#4fc3f7",a:.18},temp_ok:{c:"#66d17a",a:.18},temp_hot:{c:"#ffd45c",a:.18},lqi_low:{c:"#f25a4a",a:.18},lqi_high:{c:"#4bd28f",a:.18},glow_base:{c:"#0d1b2a",a:.5},glow_light:{c:"#ffd9a0",a:.85},wall_fill:{c:"#ffffff",a:1}};function ps(e){const t={},i=e?.fill_colors||{};for(const e of Object.keys(ds)){const s=ds[e],n=i[e];t[e]={c:Ie(n?.c,s.c),a:n&&"number"==typeof n.a?Math.min(1,Math.max(0,n.a)):s.a}}return t}function us(e,t,i){const s=Math.min(1,Math.max(0,i)),n=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),o=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),a=n.map((e,t)=>Math.round(e+(o[t]-e)*s));return"#"+a.map(e=>e.toString(16).padStart(2,"0")).join("")}function _s(e,t,i,s,n,o,a,r=as){const l=function(e,t,i,s,n,o,a,r=as){if("custom"===e)return rs(r);if("lqi"===e){if(null==t)return null;const e=(t-40)/140;return{c:us(a.lqi_low.c,a.lqi_high.c,e),a:a.lqi_low.a+(a.lqi_high.a-a.lqi_low.a)*Math.min(1,Math.max(0,e))}}if("light"===e)return"none"===i?a.light_none.a>0?a.light_none:null:"on"===i?a.light_on:a.light_off;if("temp"===e){if(null==s)return null;const e=Math.min(n,o),t=Math.max(n,o);return s<e?a.temp_cold:s>t?a.temp_hot:a.temp_ok}return null}(e,t,i,s,n,o,a,r);return l?{color:l.c,opacity:l.a,mode:e}:null}const ms={blind:["mdi:blinds","mdi:blinds-open"],shade:["mdi:blinds","mdi:blinds-open"],shutter:["mdi:window-shutter","mdi:window-shutter-open"],curtain:["mdi:curtains-closed","mdi:curtains"],window:["mdi:window-closed","mdi:window-open"],awning:["mdi:awning-outline","mdi:awning"],door:["mdi:door-closed","mdi:door-open"],garage:["mdi:garage","mdi:garage-open"],gate:["mdi:gate","mdi:gate-open"],damper:["mdi:circle-slice-8","mdi:circle-outline"]},gs=[["mdi:roller-shade-closed","mdi:roller-shade"],["mdi:blinds-horizontal-closed","mdi:blinds-horizontal"],["mdi:garage-variant","mdi:garage-open-variant"],["mdi:door","mdi:door-open"]];function fs(e){for(const t of[...Object.values(ms),...gs])if(e===t[0]||e===t[1])return t;return null}function vs(e,t,i,s,n){if(!s||"unavailable"===s||"unknown"===s)return e;if(n){const i="cover"===t?fs(e):null;return i?"closed"===s?i[0]:i[1]:e}if("binary_sensor"===t){if("door"===i)return"on"===s?"mdi:door-open":"mdi:door-closed";if("window"===i)return"on"===s?"mdi:window-open":"mdi:window-closed";if("garage_door"===i)return"on"===s?"mdi:garage-open-variant":"mdi:garage-variant"}if("cover"===t){const t=ms[String(i||"")];if(t)return"closed"===s?t[0]:t[1];const n=fs(e);return n?"closed"===s?n[0]:n[1]:e}return"lock"===t?"locked"===s?"mdi:lock":"mdi:lock-open-variant":"light"===t&&"mdi:lightbulb"===e&&"on"===s?"mdi:lightbulb-on":e}function bs(e){const t=Math.min(4e4,Math.max(1e3,e))/100,i=t<=66?255:329.698727446*Math.pow(t-60,-.1332047592),s=t<=66?99.4708025861*Math.log(t)-161.1195681661:288.1221695283*Math.pow(t-60,-.0755148492),n=t>=66?255:t<=19?0:138.5177312231*Math.log(t-10)-305.0447927307,o=e=>Math.round(Math.min(255,Math.max(0,e)));return[o(i),o(s),o(n)]}const ys=1/2.2;function ws(e){const t=/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/.exec(e);return t?"#"+t.slice(1).map(e=>Math.min(255,Number(e)).toString(16).padStart(2,"0")).join(""):null}function ks(e){if(!e||"object"!=typeof e||Array.isArray(e))return null;const t=e;if(Object.keys(t).some(e=>"c"!==e&&"bri"!==e))return null;const i=Ie(t.c,null);return i?void 0===t.bri||null===t.bri?{c:i}:"number"!=typeof t.bri||!Number.isFinite(t.bri)||t.bri<.01||t.bri>1?null:{c:i,bri:t.bri}:null}function xs(e,t,i){const s=ks(t),n=e?.attributes||{},o=s?.bri??function(e){const t=e?.attributes?.brightness,i="number"==typeof t?t:"string"==typeof t&&""!==t.trim()?Number(t):Number.NaN;return Number.isFinite(i)?Math.max(0,Math.min(1,i/255)):1}(e);if(s)return{c:s.c,bri:o};const a=ze(n.rgb_color);if(a)return{c:ws(a)||Ie(i,"#ffd9a0"),bri:o};const r=Number(n.color_temp_kelvin)||(Number(n.color_temp)>0?1e6/Number(n.color_temp):NaN);return Number.isFinite(r)&&r>0?{c:(l=bs(r),"#"+l.slice(0,3).map(e=>Math.min(255,Math.max(0,Math.round(Number(e)||0))).toString(16).padStart(2,"0")).join("")),bri:o}:{c:Ie(i,"#ffd9a0"),bri:o};var l}function $s(e,t,i){return"on"===e?.state?xs(e,t,i):null}function Ss(e,t=1){const i=Math.max(0,Math.min(1,Number.isFinite(e)?e:1)),s=.7*Math.max(0,Math.min(1,Number.isFinite(t)?t:1))*(.4+.6*Math.pow(i,ys));return Math.max(0,Math.min(1,s))}function Ms(e){return e.startsWith("light.")||e.startsWith("switch.")}function Cs(e,t,i=1e-6){const s=[];if(!e||!t||e.length<3||t.length<3)return s;for(let n=0;n<e.length;n++){const o=e[n],a=e[(n+1)%e.length],r=a[0]-o[0],l=a[1]-o[1],c=Math.hypot(r,l);if(c<i)continue;const h=r/c,d=l/c;for(let e=0;e<t.length;e++){const n=t[e],a=t[(e+1)%t.length],r=Math.abs((n[0]-o[0])*d-(n[1]-o[1])*h),l=Math.abs((a[0]-o[0])*d-(a[1]-o[1])*h),p=Math.max(i,1e-6*c);if(r>p||l>p)continue;const u=(n[0]-o[0])*h+(n[1]-o[1])*d,_=(a[0]-o[0])*h+(a[1]-o[1])*d,m=Math.max(0,Math.min(u,_)),g=Math.min(c,Math.max(u,_));g-m>i&&s.push([o[0]+h*m,o[1]+d*m,o[0]+h*g,o[1]+d*g])}}return s}function Ds(e,t,i=1e-6){const s=[];for(const n of e){const e=[n[0],n[1]],o=[n[2],n[3]],a=o[0]-e[0],r=o[1]-e[1],l=Math.hypot(a,r);if(l<i)continue;const c=a/l,h=r/l,d=[];for(const s of t){const t=Math.abs((s[0]-e[0])*h-(s[1]-e[1])*c),n=Math.abs((s[2]-e[0])*h-(s[3]-e[1])*c),o=Math.max(i,1e-6*l);if(t>o||n>o)continue;const a=(s[0]-e[0])*c+(s[1]-e[1])*h,r=(s[2]-e[0])*c+(s[3]-e[1])*h,p=Math.max(0,Math.min(a,r)),u=Math.min(l,Math.max(a,r));u-p>i&&d.push([p,u])}if(!d.length){s.push([e[0],e[1],o[0],o[1]]);continue}d.sort((e,t)=>e[0]-t[0]);let p=0;for(const[t,n]of d)t-p>i&&s.push([e[0]+c*p,e[1]+h*p,e[0]+c*t,e[1]+h*t]),p=Math.max(p,n);l-p>i&&s.push([e[0]+c*p,e[1]+h*p,o[0],o[1]])}return s}function Ps(e,t,i=1e-6){const s=[];for(let t=0;t<e.length;t++){const i=e[t],n=e[(t+1)%e.length];s.push([i[0],i[1],n[0],n[1]])}return Ds(s,t,i)}const Ts=864e5,Is=576e5;function Rs(e){const t=new Set,i=e=>{if("string"!=typeof e||!e)return;const i=As(e);i.startsWith("/api/houseplan/content/")&&t.add(i)};for(const t of e?.spaces||[]){i(t?.plan_url);for(const e of t?.markers||[])for(const t of e?.pdfs||[])i(t?.url)}for(const t of e?.markers||[])for(const e of t?.pdfs||[])i(e?.url);return t}function As(e){return e?e.startsWith("/houseplan_files/plans/")?"/api/houseplan/content/plans/_/"+e.slice(23):e.startsWith("/houseplan_files/files/")?"/api/houseplan/content/files/"+e.slice(23):e:""}function zs(e,t){const i=t?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i||"custom"===i?i:e}function Fs(e,t){const i=t?.settings;return"boolean"==typeof i?.glow?i.glow:"glow"===i?.fill_mode||e}function Ns(e,t=1){const i=Number(e);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):t}function Es(e,t){let i=180*Math.atan2(t[1]-e[1],t[0]-e[0])/Math.PI;return i<0&&(i+=360),i}function Os(e,t=.5){const i=(e%45+45)%45;return i<=t||45-i<=t}function Hs(e,t){const i=t[2]-t[0],s=t[3]-t[1],n=i*i+s*s;if(!n)return Math.hypot(e[0]-t[0],e[1]-t[1]);let o=((e[0]-t[0])*i+(e[1]-t[1])*s)/n;return o=Math.max(0,Math.min(1,o)),Math.hypot(e[0]-(t[0]+o*i),e[1]-(t[1]+o*s))}const Ls=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);function Bs(e,t){return"alarm_control_panel"===e||"siren"===e||"binary_sensor"===e&&!!t&&Ls.has(t)}function js(e){if(!e)return null;const t=e.indexOf("#");if(t<=0)return null;const i=e.slice(0,t),s=e.slice(t+1);if(!s)return null;if(s.startsWith("@")){const e=s.slice(1);return e?{space:i,area:null,roomId:e}:null}return{space:i,area:s,roomId:null}}const qs={availability:"available",status:"alarm",activity:"none"},Ws=new Set(["motion","vibration","sound"]),Us=new Set(["occupancy","presence"]),Gs=new Set(["door","window","garage_door","opening"]),Vs=new Set(["running","power"]),Ks=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Ys=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),Js=new Set([...Ys,"start","started","run","active","in_progress","wash","rinse","spin","dry"]),Xs=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping","stop","end","done","inactive"]),Zs=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),Qs=e=>""===e||"unknown"===e||"unavailable"===e||"__missing__"===e,en=e=>String(e??"").trim().toLowerCase(),tn=e=>en(e).replace(/[\s-]+/g,"_").replace(/_+/g,"_").replace(/^_|_$/g,""),sn=new Map([["run_state",0],["job_state",0],["operation_state",0],["activity_state",0],["machine_state",1],["running_state",1],["status",2],["device_status",2],["machine_status",2]]),nn=new Set(["wifi","connection","signal","battery"]);function on(e,t){const i=e?.entities?.[t]||{};if("config"===i.entity_category)return null;const s=String(t||"").split(".").slice(1).join("."),n=[i.translation_key,i.original_name,i.name],o=[...n,s];n.some(e=>en(e))||o.push(e?.states?.[t]?.attributes?.friendly_name);const a=o.map(tn).filter(Boolean);if(a.some(e=>e.split("_").some(e=>nn.has(e))))return null;let r=null;for(const e of a)for(const[t,i]of sn)(e===t||e.endsWith(`_${t}`))&&(r=null==r?i:Math.min(r,i));return r}const an=(e,t)=>null!=on(e,t);function rn(e,t){if(!t.startsWith("switch."))return!1;const i=e?.entities?.[t]||{},s=e?.states?.[t],n=t.slice(7).toLowerCase();if(/(?:^|_)(?:main_)?power$/.test(n))return!0;if([i.translation_key,i.original_name,i.name].map(en).some(e=>["power","main power","power switch","питание"].includes(e)))return!0;const o=en(s?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(o)||/(?:^|[\s._-])питание$/.test(o)}function ln(e){for(const t of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=en(e?.[t]);if(Ys.has(i)||Xs.has(i))return i}return""}function cn(e,t){const i=e?.states?.[t],s=i?en(i.state):"__missing__",n=String(t||"").split(".")[0],o=en(i?.attributes?.device_class),a={eid:t,state:s,availability:Qs(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===a.availability)return a;if(function(e,t,i){return"alarm_control_panel"===e?"triggered"===i:"on"===i&&Bs(e,t)}(n,o,s))return{...a,status:"alarm"};if("binary_sensor"===n)return Ws.has(o)?{...a,edge:"rising"}:Us.has(o)?{...a,activity:"on"===s?"presence":"none"}:Gs.has(o)?{...a,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===o?{...a,activity:"on"===s?"transition":"none"}:Vs.has(o)&&"on"===s?{...a,status:"working",activity:"running"}:a;if("cover"===n)return{...a,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===n)return{...a,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===n)return{...a,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===n){const e=ln(i.attributes),t=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(en):[],n=!Xs.has(s)&&(Zs.has(s)||Ys.has(s)||t.includes(s));return(e?Ys.has(e):n)?{...a,status:"working",activity:"running"}:a}if(["light","switch","fan","humidifier"].includes(n))return"on"===s?{...a,status:"working",activity:"running"}:a;if("media_player"===n)return"off"===s?{...a,availability:"unavailable"}:a;if("vacuum"===n)return"cleaning"===s?{...a,status:"working",activity:"running"}:"returning"===s?{...a,status:"working",activity:"transition"}:a;if("script"===n)return"on"===s?{...a,status:"working",activity:"running"}:a;if("automation"===n)return a;if("button"===n||"event"===n)return{...a,edge:"change"};const r=ln(i.attributes);return Ys.has(r)||Ys.has(s)&&!Xs.has(s)?{...a,status:"working",activity:"running"}:a}function hn(e,t,i){const s=i.filter(t=>t.startsWith("switch.")&&!e?.entities?.[t]?.entity_category),n=t.find(t=>rn(e,t));if(!(s.length>1&&!!n))return t.map(t=>cn(e,t));const o=t.find(t=>t!==n&&an(e,t)),a=cn(e,n);return"unavailable"===a.availability||"off"===a.state?t.map(t=>({eid:t,state:e?.states?.[t]?en(e.states[t].state):"__missing__",availability:"unavailable",status:"neutral",activity:"none",edge:"none"})):t.map(t=>{if(t===o)return function(e,t){const i=e?.states?.[t],s=i?en(i.state):"__missing__",n={eid:t,state:s,availability:Qs(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===n.availability)return n;const o=ln(i?.attributes);return Ys.has(o)||Js.has(s)&&!Xs.has(s)?{...n,status:"working",activity:"running"}:n}(e,t);return{...t===n?a:cn(e,t),status:"neutral",activity:"none",edge:"none"}})}function dn(e){if(!e.length)return{availability:"available",status:"neutral",activity:"none"};const t=e.filter(e=>"available"===e.availability);if(!t.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(t.some(e=>"alarm"===e.status))return qs;const i=t.some(e=>"working"===e.status)?"working":t.some(e=>"open"===e.status)?"open":"neutral",s=t.some(e=>"transition"===e.activity)?"transition":t.some(e=>"presence"===e.activity)?"presence":t.some(e=>"running"===e.activity)?"running":"none";return{availability:"available",status:i,activity:s}}function pn(e,t){if(!(e=>!!e&&!Qs(e))(e)||"unavailable"===t.availability||e===t.state)return null;if("rising"===t.edge)return"off"===e&&"on"===t.state?"event":null;if("change"===t.edge)return"event";if("terminal_transition"===t.edge){const i=new Set([e,t.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}const un=(e,t=0)=>{const i=Number(e);return Number.isInteger(i)&&i>=0?i:t},_n=(e,t)=>{const i={rev:0,configRev:un(t),off:new Set};if(null==e)return{snapshot:i,valid:!1};const s=e.off;return!Number.isInteger(e.rev)||Number(e.rev)<0||!Number.isInteger(e.config_rev)||Number(e.config_rev)<0||!Array.isArray(s)||s.some(e=>"string"!=typeof e||!e)?{snapshot:i,valid:!1}:{snapshot:{rev:Number(e.rev),configRev:Number(e.config_rev),off:new Set(s)},valid:!0}};function mn(e,t=0){return _n(e,t).snapshot}function gn(e,t,i,s){const n=_n(t,i);return s&&n.valid&&n.snapshot.configRev===i?e.configRev===i&&n.snapshot.rev<e.rev?e:n.snapshot:mn(null,i)}function fn(e){return{rev:e.rev,config_rev:e.configRev,off:[...e.off].sort()}}function vn(e){return e?`${e.configRev}:${e.rev}:${[...e.off].sort().join(",")}`:"0:0:"}function bn(e){return!!e&&"string"==typeof e.id&&!!e.id&&"virtual"===e.binding&&!0===e.is_light&&"toggle"===e.tap_action&&!0!==e.removed}function yn(e,t){return!bn(e)||!t?.off.has(e.id)}function wn(e,t){const i="string"==typeof t?.marker_id?t.marker_id:"",s=un(t?.rev,-1);if(!i||"boolean"!=typeof t?.on||s<=e.rev)return e;const n=new Set(e.off);return t.on?n.delete(i):n.add(i),{...e,rev:s,off:n}}const kn=new WeakMap,xn=new WeakMap;let $n=1;const Sn="houseplan.ha-binding-status.v1";let Mn=null;function Cn(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=kn.get(t);return i||(i={revision:0,authoritative:!1,access:"pending",devices:{},entities:{},lastSuccess:0,listeners:new Set,refs:0},kn.set(t,i)),i}function Dn(e,t){const i=Array.isArray(e)?e:e&&"object"==typeof e&&Array.isArray(e.entries)?e.entries:null;if(!i)return null;const s={};for(const e of i){const i=e?.[t];"string"==typeof i&&i&&(s[i]=e)}return s}async function Pn(e,t){return t.loading||!e?.callWS||(t.loading=(async()=>{try{const[i,s]=await Promise.all([e.callWS({type:"config/device_registry/list"}),e.callWS({type:"config/entity_registry/list"})]),n=Dn(i,"id"),o=Dn(s,"entity_id");if(!n||!o)throw new Error("invalid_registry_response");t.devices=n,t.entities=o,t.authoritative=!0,t.access="full",t.lastSuccess=Date.now(),t.error=void 0}catch(e){t.authoritative=!1,t.access="limited",t.error=function(e){if(e&&"object"==typeof e){const t=e;return String(t.message||t.code||t.error||"registry_unavailable")}return String(e||"registry_unavailable")}(e)}finally{t.revision++,t.loading=void 0,function(e){for(const t of[...e.listeners])try{t()}catch{}}(t)}})()),t.loading}function Tn(e,t){void 0===t.reloadTimer&&(t.reloadTimer=globalThis.setTimeout(()=>{t.reloadTimer=void 0,Pn(e,t)},80))}async function In(e,t){if(t.subscribing||t.unsubDevice&&t.unsubEntity)return t.subscribing;const i=e?.connection?.subscribeEvents;return"function"==typeof i?(t.subscribing=(async()=>{try{t.unsubDevice||(t.unsubDevice=await i.call(e.connection,()=>Tn(e,t),"device_registry_updated")),t.unsubEntity||(t.unsubEntity=await i.call(e.connection,()=>Tn(e,t),"entity_registry_updated"))}catch{}finally{0===t.refs&&(t.unsubDevice?.(),t.unsubEntity?.(),t.unsubDevice=void 0,t.unsubEntity=void 0),t.subscribing=void 0}})(),t.subscribing):void 0}function Rn(e,t){const i=Cn(e);if(!i)return()=>{};const s=0===i.refs;i.refs++,i.listeners.add(t),("pending"===i.access||s)&&Pn(e,i),In(e,i);let n=!1;return()=>{n||(n=!0,i.listeners.delete(t),i.refs=Math.max(0,i.refs-1),i.refs>0||(i.unsubDevice?.(),i.unsubEntity?.(),i.unsubDevice=void 0,i.unsubEntity=void 0,void 0!==i.reloadTimer&&globalThis.clearTimeout(i.reloadTimer),i.reloadTimer=void 0))}}function An(e){const t=Cn(e);if(!t)return{revision:0,authoritative:!1,access:"limited",devices:e?.devices||{},entities:e?.entities||{},lastSuccess:0,error:"registry_unavailable"};let i=e?.devices||{},s=e?.entities||{};if(t.authoritative){const n=t.liveDevices!==i||t.liveEntities!==s,o=void 0!==t.liveDevices||void 0!==t.liveEntities;if(n&&(t.liveDevices=i,t.liveEntities=s,t.projectedRevision=void 0,o&&Tn(e,t)),t.projectedRevision!==t.revision||!t.projectedDevices||!t.projectedEntities){const e={...t.devices},n={...t.entities};for(const[t,s]of Object.entries(i))Object.prototype.hasOwnProperty.call(e,t)||(e[t]=s);for(const[e,t]of Object.entries(s))Object.prototype.hasOwnProperty.call(n,e)||(n[e]=t);t.projectedDevices=e,t.projectedEntities=n,t.projectedRevision=t.revision}i=t.projectedDevices,s=t.projectedEntities}return{revision:t.revision,authoritative:t.authoritative,access:t.access,devices:i,entities:s,lastSuccess:t.lastSuccess,error:t.error}}function zn(e){if(!e||"object"!=typeof e&&"function"!=typeof e)return 0;let t=xn.get(e);return t||(t=$n++,xn.set(e,t)),t}function Fn(e,t=An(e)){return[t.revision,t.access,zn(t.devices),zn(t.entities)].join(":")}function Nn(e){return!!e&&null==e.disabled_by}function En(e,t=An(e)){const i={},s={},n={};for(const[e,s]of Object.entries(t.devices||{}))Nn(s)&&(i[e]=s);for(const[e,i]of Object.entries(t.entities||{})){if(!Nn(i))continue;const n=i.device_id?t.devices?.[i.device_id]:null;t.authoritative&&i.device_id&&!n||(n&&!Nn(n)||(s[e]=i))}for(const[i,s]of Object.entries(e?.states||{})){const e=t.entities?.[i];if(e&&!Nn(e))continue;const o=e?.device_id?t.devices?.[e.device_id]:null;t.authoritative&&e?.device_id&&!o||(o&&!Nn(o)||(n[i]=s))}return{...e,devices:i,entities:s,states:n}}function On(e,t=An(e)){return{...e,devices:t.devices||{},entities:t.entities||{}}}function Hn(e,t){const i=[];for(const[s,n]of Object.entries(e||{}))n?.device_id===t&&i.push(s);return i}function Ln(){if(Mn)return Mn;if("undefined"==typeof localStorage)return{};try{const e=JSON.parse(localStorage.getItem(Sn)||"{}");if(!e||"object"!=typeof e||Array.isArray(e))return{};const t=Date.now(),i={};for(const[s,n]of Object.entries(e))!n||"active"!==n.kind&&"ha_disabled"!==n.kind||!Number.isFinite(n.ts)||t-n.ts>7776e6||(i[s]=n);return Mn=i,Mn}catch{return{}}}function Bn(e,t,i=An(e)){if(!t||"virtual"===t)return{kind:"active",enabledEntityIds:[],allEntityIds:[]};const s=t.indexOf(":");if(s<1)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const n=t.slice(0,s),o=t.slice(s+1);if("device"!==n&&"entity"!==n||!o)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const a=i.devices||{},r=i.entities||{};if(i.authoritative){if("device"===n){const e=a[o];if(!e)return{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[]};const t=Hn(r,o);if(!Nn(e))return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:t};const i=t.filter(e=>Nn(r[e]));return t.length&&!i.length?{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:t}:{kind:"active",enabledEntityIds:i,allEntityIds:t}}const t=r[o];return t&&!Nn(t)?{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]}:t?.device_id&&!a[t.device_id]?{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[o]}:t?.device_id&&!Nn(a[t.device_id])?{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]}:t||e?.states?.[o]?{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}:{kind:"orphaned",reason:"entity_missing",enabledEntityIds:[],allEntityIds:[]}}const l=function(e){return Ln()[e]||null}(t);if("device"===n){const t=a[o],i=Hn(r,o);if(null!=t?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:i};const s=i.filter(e=>{const t=r[e];return null==t?.disabled_by&&(!t.device_id||null==a[t.device_id]?.disabled_by)});if(i.length&&!s.length&&i.every(e=>null!=r[e]?.disabled_by))return{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:i};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"device",enabledEntityIds:[],allEntityIds:i};if(t||s.some(t=>!!e?.states?.[t]))return{kind:"active",enabledEntityIds:s,allEntityIds:i}}else{const t=r[o];if(null!=t?.disabled_by)return{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]};if(t?.device_id&&null!=a[t.device_id]?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"entity",enabledEntityIds:[],allEntityIds:[o]};if(t||e?.states?.[o])return{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}}return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]}}function jn(e){const t={};for(const[i,s]of Object.entries(e.entities)){if(!s?.device_id||!Nn(s))continue;const n=e.devices?.[s.device_id];n&&!Nn(n)||(t[s.device_id]=t[s.device_id]||[]).push(i)}return t}function qn(e,t){const i=new Map;for(const s of e){if(!0===s?.removed)continue;const e=s?.binding||"";if(!e.startsWith("entity:"))continue;const n=e.slice(7);if(!n)continue;const o=t?.entities?.[n]?.device_id;if(!o)continue;const a=i.get(o)||new Set;a.add(n),i.set(o,a)}return{byDevice:i}}function Wn(e,t,i,s){const n=s.byDevice.get(t);return n?.size?{partial:!0,entityIds:i.filter(t=>!n.has(t)&&!e?.entities?.[t]?.hidden)}:{partial:!1,entityIds:[...i]}}function Un(e,t,i){if(t.identifiers?.[0]?.[0])return t.identifiers[0][0];for(const t of i){const i=e.entities[t]?.platform;if(i)return i}return""}function Gn(e,t){if(/_device_temperature$/.test(t))return!1;if(e.entities?.[t]?.entity_category)return!1;const i=e.states[t];if(!i)return/_temperature$/.test(t);const s=i.attributes||{};return"temperature"===s.device_class||/°C|°F/.test(s.unit_of_measurement||"")||/_temperature$/.test(t)}const Vn=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],Kn=e=>[...e.filter(e=>!e.reg?.hidden),...e.filter(e=>!!e.reg?.hidden)];function Yn(e,t){const i=t.map(t=>({eid:t,reg:e?.entities?.[t]})).filter(e=>!!e.reg);if(!i.length)return[];const s=i.filter(e=>!e.reg.entity_category),n=s.length?s:i;for(const e of Vn){const t=n.filter(t=>t.eid.startsWith(e+"."));if(t.length)return Kn(t).map(e=>e.eid)}const o=n.filter(t=>function(e,t){if(!t.startsWith("binary_sensor."))return!1;const i=en(e?.states?.[t]?.attributes?.device_class||e?.entities?.[t]?.device_class||e?.entities?.[t]?.original_device_class);return Ws.has(i)||Us.has(i)||Gs.has(i)||Vs.has(i)||"moving"===i||Ks.has(i)}(e,t.eid));if(o.length)return Kn(o).map(e=>e.eid);const a=n.filter(e=>e.eid.startsWith("switch."));if(a.length){const t=Kn(a),s=t.find(t=>rn(e,t.eid));if(a.length>1&&s){const t=i.filter(t=>t.eid!==s.eid&&"config"!==t.reg?.entity_category&&null!=on(e,t.eid)),n=t.filter(e=>!e.reg?.entity_category),o=n.length?n:t;for(const t of[0,1,2]){const i=Kn(o.filter(i=>on(e,i.eid)===t));if(i.length)return[i[0].eid,s.eid]}}return[(s||t[0]).eid]}const r=[];for(const e of it)r.push(...Kn(n.filter(t=>t.eid.startsWith(e+"."))));return r.push(...Kn(n.filter(e=>!it.includes(e.eid.split(".")[0])))),r.map(e=>e.eid)}function Jn(e,t,i){const s=t.map(t=>({eid:t,reg:e.entities[t],st:e.states[t]})).filter(e=>e.reg),n=[s.filter(e=>!e.reg.hidden&&!e.reg.entity_category),s.filter(e=>!e.reg.entity_category),s.filter(e=>!e.reg.hidden),s];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const t of n){const i=t.find(t=>Gn(e,t.eid));if(i)return i.eid}const o=Yn(e,t);return o.find(Ms)||o[0]}function Xn(e,t,i=[]){return[...new Set(Zn(e,t,i))].filter(e=>Ms(e))}function Zn(e,t,i=[]){const s=e?.startsWith("entity:")?new Set([e.slice(7)]):new Set(i);return(t||[]).filter(e=>"string"==typeof e&&!s.has(e))}function Qn(e,t){if("string"==typeof t)return e.area===t;const i=e.marker?.room_id;return i?!!t.id&&i===t.id:!!t.area&&e.area===t.area}function eo(e){const t=e.marker?.binding?.startsWith("entity:")?e.marker.binding.slice(7):null,i=e.entities.filter(Ms),s=e.primary&&Ms(e.primary)?e.primary:null;return[...new Set([t,s,...i].filter(e=>!!e&&Ms(e)))]}function to(e){const t=eo(e),i=e.marker?.light_entity;return i&&t.includes(i)?i:t[0]||null}function io(e,t){if(!1===t.marker?.is_light)return[];if(!0===t.marker?.is_light){return[{eid:to(t),via:"forced"}]}const i=t.primary||Yn(e,t.entities)[0];return i&&!i.startsWith("light.")?[]:t.entities.filter(t=>t.startsWith("light.")&&!!e.states?.[t]).map(e=>({eid:e,via:"light"}))}const so=new WeakMap,no=new WeakMap;function oo(e){return e.map(e=>[e.id||"",!0===e.hidden?1:0,e.primary||"",[...e.entities].join(","),void 0===e.controls?"<runtime-undefined>":[...e.controls].join(","),e.marker?.id||"",e.marker?.binding||"",e.marker?.is_light,e.marker?.tap_action||"",!0===e.marker?.removed?1:0,e.marker?.light_entity||"",null==e.marker?.controls?"<persisted-null>":[...e.marker.controls].join(",")].join("")).join("")}function ao(e){const t=oo(e),i=so.get(e);if(i?.fingerprint===t)return i;const s=e.filter(e=>!e.hidden),n=new Map,o=new Map;for(const t of e){o.set(t,Zn(t.marker?.binding,t.marker?.controls??t.controls,t.entities));const e=t.hidden||!0!==t.marker?.is_light?null:t.marker?.id||t.id;e&&n.set(String(e),t)}const a=new Map;for(const t of e){const e=o.get(t)||[],i=new Set(void 0===t.controls?e.filter(Ms):t.controls.filter(Ms)),s=to(t),r=i.size?[...i]:s?[s]:[];for(const i of e){if(!i.startsWith("marker:"))continue;const e=i.slice(7);if(!e||e===String(t.marker?.id||t.id||""))continue;if(!n.has(e))continue;const s=a.get(e)||{markerId:e,controllers:[],driverEids:[]};s.controllers.push({device:t,driverEids:[...r]}),s.driverEids=[...new Set([...s.driverEids,...r])],a.set(e,s)}}const r={fingerprint:t,visible:s,markerById:n,persistedByDevice:o,incomingByMarker:a};return so.set(e,r),r}function ro(e){return ao(e).incomingByMarker}function lo(e,t){const i=Xn(t.marker?.binding,t.controls??t.marker?.controls,t.entities),s=Zn(t.marker?.binding,t.marker?.controls??t.controls,t.entities);return(null!=t.marker?.is_light||!i.length&&!s.some(e=>e.startsWith("marker:")))&&io(e,t).length>0}function co(e,t){const i=Xn(t.marker?.binding,t.controls??t.marker?.controls,t.entities).map(e=>({eid:e,via:"controls"})),s=lo(e,t)?io(e,t):[];for(const e of s)e.eid&&i.some(t=>t.eid===e.eid)||i.push(e);return i}function ho(e,t,i,s){if(null!=i)return ho(e,t,null,s).filter(e=>Qn(e.device,i));const n=oo(t),o=function(e,t){const i=new Set;for(const e of t){for(const t of e.entities)i.add(t);for(const t of e.controls||e.marker?.controls||[])"string"!=typeof t||t.startsWith("marker:")||i.add(t)}return[...i].sort().map(t=>`${t}:${e?.states?.[t]?.state??"<missing>"}`).join("|")}(e,t),a=vn(s),r=no.get(t);if(r?.graphFingerprint===n&&r.stateFingerprint===o&&r.virtualLightFingerprint===a&&r.registry===e?.entities)return r.sources;const{visible:l,markerById:c,persistedByDevice:h,incomingByMarker:d}=ao(t),p=null==i?l:l.filter(e=>Qn(e,i)),u=new Map,_=new Map;for(const t of p){const i=co(e,t).filter(e=>"controls"!==e.via);for(const s of i){const i=!0===t.marker?.is_light?String(t.marker?.id||t.id||""):"",n=i?`marker:${i}`:`entity:${s.eid}`,o=s.eid?[s.eid]:[],a={key:n,eid:s.eid||"",stateEids:o,serviceEids:o,device:t,via:s.via,castsGlow:!0,passive:!s.eid,on:!s.eid||"on"===e.states?.[s.eid]?.state};u.set(t,[...u.get(t)||[],a]),s.eid&&_.set(s.eid,a)}}const m=new Map;for(const e of t){const t=h.get(e)||[],i=new Set(void 0===e.controls?t.filter(Ms):e.controls);m.set(e,i)}for(const t of u.values())for(const i of t){if(!i.passive)continue;const t=i.key.slice(7),n=d.get(t);i.on=!n||n.driverEids.some(t=>"on"===e.states?.[t]?.state),!n&&bn(i.device.marker)&&(i.on=yn(i.device.marker,s))}const g=new Map,f=e=>{const t=g.get(e.key);(!t||e.castsGlow&&!t.castsGlow)&&g.set(e.key,e)};for(const t of l){const s=h.get(t)||[],n=null==i||Qn(t,i);for(const i of s){if(i.startsWith("marker:")){const e=c.get(i.slice(7)),t=e?u.get(e):null;if(t)for(const e of t)f(e);continue}if(!Ms(i)||!m.get(t)?.has(i))continue;const s=_.get(i);s?f(s):n&&f({key:`entity:${i}`,eid:i,stateEids:[i],serviceEids:[i],device:t,via:"controls",castsGlow:!1,passive:!1,on:"on"===e.states?.[i]?.state})}for(const e of u.get(t)||[])f(e)}const v=[...g.values()];return no.set(t,{graphFingerprint:n,stateFingerprint:o,virtualLightFingerprint:a,registry:e?.entities,sources:v}),v}function po(e){const t=e.filter(e=>e.castsGlow);return t.find(e=>e.on)||t[0]||null}function uo(e){return e.length?e.some(e=>e.on)?"on":"off":"none"}function _o(e,t){const i=[];for(const s of t){const t=e.states[s];if(!t)continue;const n=(t.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===n){const e=parseFloat(t.state);isNaN(e)||i.push(e);continue}const o=t.attributes?.linkquality??t.attributes?.lqi;if(null!=o){const e=parseFloat(o);isNaN(e)||i.push(e)}}return Ni(i)}function mo(e,t){for(const i of t){if(!Gn(e,i))continue;const t=e.states[i];if(!t)continue;const s=parseFloat(t.state);if(!isNaN(s))return Math.round(10*s)/10}return null}function go(e,t){for(const i of t){if(!i.startsWith("climate."))continue;const t=e.states[i];if(!t||"unavailable"===t.state||"unknown"===t.state)continue;const s=parseFloat(t.attributes?.current_temperature);if(Number.isFinite(s))return Math.round(10*s)/10}return null}function fo(e,t){if(e.entities?.[t]?.entity_category)return!1;const i=e.states[t];if(!i)return/_humidity$/.test(t);const s=i.attributes||{};return"humidity"===s.device_class||"%"===s.unit_of_measurement&&/_humidity$/.test(t)||/_humidity$/.test(t)}function vo(e,t){for(const i of t){if(!fo(e,i))continue;const t=e.states[i];if(!t)continue;const s=parseFloat(t.state);if(!isNaN(s))return Math.round(s)}return null}function bo(e,t){if(!t)return[];const i=[];for(const[t,s]of Object.entries(e.entities)){if(!t.startsWith("light.")||s.hidden||!Nn(s))continue;let n=null;if("group"===s.platform)n=s.area_id||null;else{if(!s.device_id)continue;{const t=e.devices[s.device_id];if(!Nn(t))continue;if("Group"!==t?.model)continue;n=t.area_id||s.area_id||null}}if(!n)continue;const o=e.states[t];i.push({eid:t,name:s.name||o?.attributes?.friendly_name||t,area:n})}return i}function yo(e,t,i,s,n){const o=tt(t,i,n);if(o!==et)return o;const a=[];for(const t of s){const i=e.states[t]?.attributes?.device_class;i&&a.push(i)}return function(e){for(const t of e){const e=Qe[t];if(e)return e}return null}(a)??et}function wo(e){const t=new Set,i=new Set,s=new Set;for(const n of e||[]){const e=String(n.binding||"").indexOf(":");if(e<1)continue;const o=n.binding.slice(0,e),a=n.binding.slice(e+1);a&&(!0===n?.removed?"device"===o?t.add(a):"entity"===o&&i.add(a):"entity"===o&&s.add(a))}return{devices:t,entities:i,liveEntities:s}}function ko(e,t,i){if(i.liveEntities.has(t))return!1;if(i.entities.has(t))return!0;const s=e?.entities?.[t]?.device_id;return!!s&&i.devices.has(s)}function xo(e,t,i,s,n){const o=Xn(t.binding,t.controls,e.entities).filter(e=>!ko(i,e,s)).filter(e=>"active"===Bn(i,"entity:"+e,n).kind);e.marker=t,e.controls=o,e.userHidden=!0===t.hidden,e.hidden=e.userHidden||"ha_disabled"===e.bindingStatus?.kind,t.name&&(e.name=t.name),t.icon&&(e.icon=t.icon),null!=t.model&&(e.model=t.model),e.link=t.link??null,e.description=t.description??null,e.pdfs=t.pdfs||[],e.tapAction=t.tap_action??null}function $o(e,t,i,s){if("string"==typeof e.room_id&&e.room_id.length>0&&null===e.area)return{area:"",space:e.space||s};const n=e.area||t||"";return{area:n,space:n&&i[n]||e.space||s}}function So(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,showAll:c,firstSpaceId:h,loc:d,iconRules:p}=e,u=!1!==r.group_lights,_=wo(a),m=bo(s,u).filter(e=>!ko(s,e.eid,_)),g=new Set(m.map(e=>e.area)),f=jn(s),v=function(e){const t={};for(const[i,s]of Object.entries(e.entities||{}))s?.device_id&&(t[s.device_id]=t[s.device_id]||[]).push(i);return t}(n),b=qn(a,n),y=new Set;for(const e of a){const[t,i]=e.binding.split(":");"device"!==t&&"entity"!==t||!i||y.add(e.binding)}const w=(e,t)=>a.find(i=>i.binding===e+":"+t),k={},x=[];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(y.has("device:"+e.id))continue;const a=Bn(t,"device:"+e.id,i);if("active"!==a.kind)continue;const h=w("device",e.id);if(h&&h.hidden&&!r.filter_seeded)continue;const _=Wn(s,e.id,f[e.id]||[],b);if(_.partial&&!_.entityIds.length)continue;const m=_.entityIds,v=_.partial?{kind:"active",enabledEntityIds:m,allEntityIds:m}:a,$=Un(s,e,m),S=!r.filter_seeded;if(S&&!c){if(l.has($))continue;if("Group"===e.model)continue;if(/scene/i.test(e.model||""))continue;if(/bridge/i.test((e.model||"")+(e.name||"")))continue;if("myheat"===$&&e.via_device_id)continue}const M=(e.name_by_user||e.name||d("device.unnamed")).trim(),C=M+"|"+n;let D=yo(s,M,e.model,m,p);if(m.some(e=>e.startsWith("lock."))&&(D="mdi:lock"),S&&!c&&u&&"mdi:lightbulb"===D&&g.has(n))continue;k[C]=(k[C]||0)+1;const P=k[C]>1?M+" "+k[C]:M,T={id:e.id,name:P,model:e.model||"",area:n,space:o[n],icon:D,entities:m,allEntities:v.allEntityIds,bindingStatus:v,bindingKind:"device",bindingRef:e.id,pdfs:[]};T.primary=Jn(s,m,D),"mdi:thermometer"!==D&&"mdi:air-filter"!==D||(T.temp=mo(s,m)),T.primary&&fo(s,T.primary)&&(T.hum=vo(s,m)),x.push(T)}for(const e of m)o[e.area]&&(y.has("entity:"+e.eid)||x.push({id:"lg_"+e.eid,name:e.name,model:d("device.light_group"),area:e.area,space:o[e.area],icon:"mdi:lightbulb-group",entities:[e.eid],allEntities:[e.eid],bindingStatus:{kind:"active",enabledEntityIds:[e.eid],allEntityIds:[e.eid]},primary:e.eid,bindingKind:"entity",bindingRef:e.eid,pdfs:[]}));for(const e of a){if(e.removed)continue;const[a,l]=e.binding.split(":"),c="device"===a||"entity"===a?Bn(t,e.binding,i):null;if(!e.hidden||r.filter_seeded||"ha_disabled"===c?.kind)if("device"===a){const t=c;if("unverified"===t.kind)continue;const a=n.devices[l],{area:r,space:u}=$o(e,a?.area_id,o,h),m="active"===t.kind?t.enabledEntityIds:[];let g=a?yo(s,a.name_by_user||a.name||"",a.model,m,p):"mdi:help-circle";m.some(e=>e.startsWith("lock."))&&(g="mdi:lock");const f={id:e.id,name:a?.name_by_user||a?.name||d("device.fallback"),model:a?.model||"",area:r,space:u,icon:g,entities:m,allEntities:t.allEntityIds.length?t.allEntityIds:a&&v[a.id]||[],bindingStatus:t,bindingKind:"device",bindingRef:l};f.primary=Jn(s,m,g),"mdi:thermometer"!==g&&"mdi:air-filter"!==g||(f.temp=mo(s,m)),f.primary&&fo(s,f.primary)&&(f.hum=vo(s,m)),xo(f,e,n,_,i),x.push(f)}else if("entity"===a){if(ko(n,l,_))continue;const t=c;if("unverified"===t.kind)continue;const a=n.entities[l],r=a?.area_id||a?.device_id&&n.devices[a.device_id]?.area_id||"",{area:d,space:u}=$o(e,r,o,h),m=s.states[l],g=a?.name||m?.attributes?.friendly_name||l;let f=yo(s,g,"",[l],p);l.startsWith("lock.")&&(f="mdi:lock");const v={id:e.id,name:g,model:"",area:d,space:u,icon:f,entities:"active"===t.kind?[l]:[],allEntities:[l],bindingStatus:t,primary:"active"===t.kind?l:void 0,bindingKind:"entity",bindingRef:l};"mdi:thermometer"!==f&&"mdi:air-filter"!==f||!v.entities.length||(v.temp=mo(s,v.entities)),v.entities.length&&fo(s,l)&&(v.hum=vo(s,v.entities)),xo(v,e,n,_,i),x.push(v)}else{const t=e.area||"",s=e.space||t&&o[t]||h,a={id:e.id,name:e.name||d("device.virtual"),model:e.model||"",area:t,space:s,icon:e.icon||"mdi:map-marker",entities:[],allEntities:[],bindingStatus:{kind:"active",enabledEntityIds:[],allEntityIds:[]},bindingKind:"virtual",virtual:!0};xo(a,e,n,_,i),x.push(a)}}return x}function Mo(e,t,i,s){if(!t)return null;if(function(e,t,i){if(!t)return!1;const s=t.indexOf(":");if(s<1)return!1;const n=t.slice(0,s),o=t.slice(s+1),a=wo(i);return"device"===n?a.devices.has(o):"entity"===n&&ko(e,o,a)}(e,t,s))return null;const n=t.indexOf(":");if(n<0)return null;const o=t.slice(0,n),a=t.slice(n+1);if(!a)return null;if("entity"===o){const t=parseFloat(e.states[a]?.state);return Number.isFinite(t)?"temp"===i?Math.round(10*t)/10:Math.round(t):null}if("device"===o){const t=Object.entries(e.entities).filter(([,e])=>e.device_id===a).map(([e])=>e);return"temp"===i?mo(e,t):vo(e,t)}return null}const Co=new RegExp(["water","voda","coolant","flow_?temp","return_?temp","target","setpoint","chip","cpu","processor","board","core_temp","device_temp","batter","akkum","freezer","fridge","oven","kettle","boiler"].join("|"),"i");function Do(e,t,i){const s=[];for(const n of t){if(n.area!==i||n.virtual)continue;const t=_o(e,n.entities);null!=t&&s.push(t)}return Ni(s)}function Po(e,t,i){const s=[];for(const n of t){if(n.area!==i)continue;if("mdi:thermometer"!==n.icon&&"mdi:air-filter"!==n.icon)continue;const t=mo(e,n.entities);null!=t&&s.push(t)}return s.length?Math.round(s.reduce((e,t)=>e+t,0)/s.length*10)/10:null}const To={climate:["current_temperature","temperature","current_humidity","humidity"],water_heater:["current_temperature","temperature"],cover:["current_position"],valve:["current_position"],fan:["percentage"],humidifier:["current_humidity","humidity"],light:["brightness"],media_player:["volume_level"],vacuum:["battery_level","fan_speed"],lawn_mower:["battery_level","fan_speed"]};function Io(e){return e?e.sourceLabel?`${e.sourceLabel}: ${e.fullText}`:e.fullText:""}const Ro=new WeakMap;function Ao(e){return e?"entity_state"===e.kind?`state:${e.entity_id}`:"entity_attribute"===e.kind?`attr:${e.entity_id}:${e.attribute}`:"derived_marker_state"===e.kind?`marker:${e.ref}`:"derived:lqi":""}function zo(e){if("derived:lqi"===e)return{kind:"derived_lqi"};if(e.startsWith("state:"))return{kind:"entity_state",entity_id:e.slice(6)};if(e.startsWith("attr:")){const t=e.slice(5),i=t.lastIndexOf(":");if(i>0)return{kind:"entity_attribute",entity_id:t.slice(0,i),attribute:t.slice(i+1)}}return e.startsWith("marker:marker:")?{kind:"derived_marker_state",ref:e.slice(7)}:null}function Fo(e,t){const i=e?.entities?.[t],s=e?.states?.[t];return String(i?.name||i?.original_name||s?.attributes?.friendly_name||t)}function No(e){return e.replaceAll("_"," ")}function Eo(e,t=1){const i=Number(e);return Number.isFinite(i)?`${Math.round(i*t)} %`:null}function Oo(e){if("derived_lqi"===e.kind)return"lqi";if("entity_attribute"===e.kind){if(e.attribute.includes("temperature"))return"temperature";if(e.attribute.includes("humidity"))return"humidity"}return"default"}function Ho(e,t,i,s=[]){let n=null,o="",a="available";if("entity_state"===i.kind){o=Fo(e,i.entity_id);const t=e?.states?.[i.entity_id];if(t)if(Bo(e,i.entity_id))if(["unknown","unavailable"].includes(String(t.state).toLowerCase()))a="unavailable";else{const s=Xi(e,i.entity_id);n=s?Zi(s,String(t.attributes?.unit_of_measurement||"")):null,n||(a="unavailable")}else a="unavailable";else a="missing"}else if("entity_attribute"===i.kind){o=`${No(i.attribute)} · ${Fo(e,i.entity_id)}`;const t=e?.states?.[i.entity_id];t?Bo(e,i.entity_id)?(n=function(e,t,i){const s=e?.states?.[t];if(!s||!(i in(s.attributes||{})))return null;const n=Xi(e,t,i);if(n?.formatted)return n.text;const o=s.attributes?.[i];if("brightness"===i)return Eo(o,100/255);if("volume_level"===i)return Eo(o,100);if(["current_position","percentage","current_humidity","humidity","battery_level"].includes(i))return Eo(o);if("current_temperature"===i||"temperature"===i){const t=Number(o);if(!Number.isFinite(t))return null;const i=String(e?.config?.unit_system?.temperature||"°C");return`${Math.round(10*t)/10} ${i}`}return n?.text||(["string","number","boolean"].includes(typeof o)?String(o):null)}(e,i.entity_id,i.attribute),null==n&&(a="unavailable")):a="unavailable":a="missing"}else if("derived_lqi"===i.kind){o="LQI";const i=_o(e,t.entities);null==i?a="unavailable":n=String(i)}else{o="Light state";const t=s.find(e=>e.ref===i.ref);if(t){const i=t.on?"on":"off",s=e?.localize?.(`state.default.${i}`);n="string"==typeof s&&s?s:i,o=t.name||o}else a="missing"}const r=e?.localize?.("state.default.unavailable")||"Unavailable";return{source:i,sourceLabel:o,text:n??"—",fullText:n??r,availability:a,isLqi:"derived_lqi"===i.kind,tone:Oo(i)}}function Lo(e,t){if(!0===t.marker?.use_climate_temp){const i=t.entities.find(t=>t.startsWith("climate.")&&Number.isFinite(Number(e?.states?.[t]?.attributes?.current_temperature)));return i?{kind:"entity_attribute",entity_id:i,attribute:"current_temperature"}:null}if("mdi:thermometer"===t.icon||"mdi:air-filter"===t.icon){const i=t.entities.find(t=>Gn(e,t)&&Number.isFinite(Number(e?.states?.[t]?.state)));if(i)return{kind:"entity_state",entity_id:i}}return t.primary&&fo(e,t.primary)&&Number.isFinite(Number(e?.states?.[t.primary]?.state))?{kind:"entity_state",entity_id:t.primary}:null}function Bo(e,t){const i=e?.entities?.[t];if(i&&!Nn(i))return!1;const s=i?.device_id?e?.devices?.[i.device_id]:null;return!s||Nn(s)}function jo(e,t){return Bo(e,t)&&!!e?.states?.[t]}function qo(e,t,i=[t]){const s=function(e,t){const i=e=>[e.id,e.primary,e.hidden?1:0,e.userHidden?1:0,e.entities.join(","),(e.controls||[]).join(","),e.marker?.binding||"",!0===e.marker?.is_light?1:0,e.marker?.light_entity||"",(e.marker?.controls||[]).join(",")].join("|");return`${i(e)}\n${t.map(i).join("\n")}`}(t,i),n=Ro.get(t);if(n&&n.states===(e?.states||null)&&n.entities===(e?.entities||null)&&n.devices===(e?.devices||null)&&n.signature===s)return n.result;const o=new Set(t.entities);for(const e of t.controls||[])e.startsWith("marker:")||o.add(e);const a=[],r=ho(e,i),l=r.filter(e=>e.key.startsWith("marker:")).map(e=>({ref:e.key,on:e.on,name:e.device?.name||e.key})),c=i=>{const s=Ao(i);if(a.some(e=>e.key===s))return;const n=Ho(e,t,i,l),o="entity_attribute"===i.kind?`${i.entity_id} · ${i.attribute}`:"entity_state"===i.kind?i.entity_id:"derived_marker_state"===i.kind?i.ref:"LQI";a.push({key:s,source:i,label:n.sourceLabel,technical:o,value:n.text,available:"available"===n.availability})};for(const t of[...o].sort()){if(!jo(e,t))continue;const i=t.split(".")[0],s=e?.entities?.[t];if("button"===i||"event"===i||"config"===s?.entity_category)continue;c({kind:"entity_state",entity_id:t});const n=e?.states?.[t];for(const e of To[i]||[])e in(n?.attributes||{})&&c({kind:"entity_attribute",entity_id:t,attribute:e})}const h=new Set((t.marker?.controls||t.controls||[]).filter(e=>e.startsWith("marker:")));!0===t.marker?.is_light&&t.marker.id&&h.add(`marker:${t.marker.id}`);const d=new Set(r.map(e=>e.key));for(const e of[...h].sort())d.has(e)&&c({kind:"derived_marker_state",ref:e});return t.virtual||null==_o(e,t.entities)||c({kind:"derived_lqi"}),Ro.set(t,{states:e?.states||null,entities:e?.entities||null,devices:e?.devices||null,signature:s,result:a}),a}function Wo(e,t,i){const s=Lo(e,t);if(s&&i.some(e=>e.key===Ao(s)))return s;const n=t.primary&&i.find(e=>e.key===`state:${t.primary}`);if(n)return n.source;const o=[e=>e.technical.includes("temperature"),e=>e.technical.includes("humidity"),e=>e.technical.includes("battery")];for(const e of o){const t=i.find(e);if(t)return t.source}return i.find(e=>"derived_lqi"!==e.source.kind)?.source||i[0]?.source||null}function Uo(e){return"boolean"==typeof e?.themes?.darkMode?e.themes.darkMode?"theme-dark":"theme-light":""}function Go(e){const t=[];if(1!==e.scale&&t.push(`--dev-scale:${e.scale}`),"none"!==e.pulse.kind){t.push(`--ripple-scale:${e.pulse.diameterScale}`);const n=(i=e.pulse.color,s=null,"string"==typeof i&&(Te.test(i)||Ae.test(i))?i:s);n&&t.push(`--ripple-color:${n}`)}var i,s;return t}function Vo(e){const t=[...String(e)].reduce((e,t)=>/\s/.test(t)?e+.35:e+(t.codePointAt(0)>255?1:.62),0);return t<=8?.45:Math.max(.25,Math.round(3.6/t*1e3)/1e3)}function Ko(e,t){const i=e.pulse,s=i.generation%2==0,n=function(e){const t=e.valueBadge;if(!t||!1!==t.configured)return[];const i=[];return null!=e.tempText&&"temperature"!==t.tone&&i.push({kind:"temperature",text:e.tempText,suffix:"°"}),null!=e.humText&&"humidity"!==t.tone&&i.push({kind:"humidity",text:e.humText,suffix:"%"}),i}(e),o=e.valueBadge,a=o?.position||"right",r=!!o||n.length>0,l=["device-shell",null!=e.valueText?"text-shell":"",r?`with-values pos-${a}`:"",n.length?"with-legacy":""].filter(Boolean).join(" ");return q`
|
||
${"none"!==i.kind&&"dot"!==i.reducedMotionIndicator?q`<span class="device-pulse activity-ring ${i.kind} ${i.reason} reason-${i.reason} ${s?"gen2":""}"
|
||
aria-hidden="true"><i></i><i></i><i></i></span>`:G}
|
||
${"dot"===i.reducedMotionIndicator?q`<span class="activity-dot" aria-hidden="true"></span>`:G}
|
||
${t.newDevice?q`<span class="newdot" title=${t.newDeviceTitle||""} aria-hidden="true"></span>`:G}
|
||
${e.haDisabled?q`<span class="habadge" title=${t.disabledTitle||""} aria-hidden="true"><ha-icon icon="mdi:power-plug-off-outline"></ha-icon></span>`:G}
|
||
<span class=${l} aria-hidden="true">
|
||
<span class="device-shell-frame"></span>
|
||
<span class="device-core">
|
||
${null!=e.valueText?q`<span class="valtext" title=${e.valueFullText||e.valueText}
|
||
style=${`--value-font-scale:${Vo(e.valueFullText||e.valueText)}`}
|
||
>${e.valueText}</span>`:q`<ha-icon icon=${e.icon}
|
||
style=${e.angle?`transform:rotate(${e.angle}deg)`:G}></ha-icon>`}
|
||
</span>
|
||
${r?q`<span class="device-sections">
|
||
${o?q`<span
|
||
class=${function(e){return`value-badge pos-${e.position} ${e.availability} tone-${e.tone}`}(o)}
|
||
title=${Io(o)}
|
||
style=${`--value-font-scale:${Vo(o.fullText||o.text)}`}
|
||
>${o.text}</span>`:G}
|
||
${n.map(e=>q`<span
|
||
class="value-badge legacy-secondary available tone-${e.kind}"
|
||
title=${e.text+e.suffix}
|
||
style=${`--value-font-scale:${Vo(e.text+e.suffix)}`}
|
||
>${e.text}${e.suffix}</span>`)}
|
||
</span>`:G}
|
||
</span>
|
||
${null!=e.lqiText?q`<span class="${function(e){return"lqi"+("bottom"===e?.position?" below-value-badge":"")}(e.valueBadge)}${e.lqiBand?` band-${e.lqiBand}`:""}"
|
||
style=${e.lqiColor?`color:${e.lqiColor}`:G}>${e.lqiText}</span>`: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=[{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;function la(e){return"string"==typeof e?e.trim().replaceAll("_","-").toLowerCase():""}const ca=new Map(aa.map(e=>[la(e.code),e]));function ha(e){return ca.get(la(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=>[la(e),e])),o=n.get(la(e));if(o)return o;const a=la(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`<section class="devicepreview">
|
||
<div class="previewhead">
|
||
<strong>${this._t("marker.preview.title")}</strong>
|
||
<span class="previewbadge ${t?"example":""}">
|
||
${this._t(t?"marker.preview.example":"marker.preview.actual")}
|
||
</span>
|
||
</div>
|
||
<div class="previewgrid">
|
||
<div class="previewstage" role="img" aria-label=${g}>
|
||
<div class="previewfit" style=${p}>
|
||
<div class=${u} style=${_}>
|
||
${Ko(i,{disabledTitle:this._reason("ha_disabled")})}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="previewfacts" aria-live="polite">
|
||
<div><span>${this._t("marker.preview.integration")}</span><b>${this._providerSummary(s)}</b></div>
|
||
<div><span>${this._t("marker.preview.source")}</span><b>${this._sourceSummary(e)}</b></div>
|
||
<div><span>${this._t("marker.preview.current_state")}</span><b>${this._stateSummary(e)}</b></div>
|
||
<div><span>${this._t("marker.preview.result")}</span><b>${m}</b></div>
|
||
${e.fallbackReason?q`<p class="previewnotice">${this._reason(e.fallbackReason)}</p>`:G}
|
||
${e.explanation.notices.map(e=>q`<p class="previewnotice">${this._reason(e)}</p>`)}
|
||
${h<1?q`<p class="previewnotice">${this._t("marker.preview.scaled",{n:d})}</p>`:G}
|
||
${t&&this._reducedMotion?q`<p class="previewnotice">${this._t("marker.preview.reduced_motion")}</p>`:G}
|
||
</div>
|
||
</div>
|
||
${e.visualSources.length||e.criticalSources.length?q`<details class="previewdetails">
|
||
<summary>${this._t("marker.preview.details")}</summary>
|
||
<div class="previewtable">
|
||
${[...e.visualSources,...e.criticalSources].map(e=>q`
|
||
<div class="previewsource">
|
||
<b>${e.name}</b><code>${e.eid}</code>
|
||
<span>${e.stateText||e.state}</span>
|
||
<span>${this._sourceProvider(e.eid)}</span>
|
||
</div>`)}
|
||
</div>
|
||
</details>`:G}
|
||
${"icon_ripple"===e.display?q`<div class="previewdemos">
|
||
<button class="previewdemo" type="button"
|
||
?disabled=${n||"continuous"===this._demoKind}
|
||
title=${n?this._t("marker.preview.demo_already_visible"):""}
|
||
@click=${this._startShortDemo}>
|
||
<ha-icon icon="mdi:motion-play-outline"></ha-icon>
|
||
${this._t("marker.preview.demo_short")}
|
||
</button>
|
||
<button class="previewdemo" type="button"
|
||
?disabled=${n}
|
||
title=${n?this._t("marker.preview.demo_already_visible"):""}
|
||
@click=${this._toggleContinuousDemo}>
|
||
<ha-icon icon=${"continuous"===this._demoKind?"mdi:stop-circle-outline":"mdi:repeat"}></ha-icon>
|
||
${this._t("continuous"===this._demoKind?"marker.preview.stop_continuous":"marker.preview.demo_continuous")}
|
||
</button>
|
||
</div>`:G}
|
||
</section>`}}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;
|
||
padding: 14px;
|
||
border: 1px solid var(--divider-color, rgba(127, 127, 127, 0.28));
|
||
border-radius: 14px;
|
||
background: color-mix(in srgb, var(--card-background-color, #20232b) 92%, var(--primary-color, #03a9f4));
|
||
color: var(--primary-text-color, #fff);
|
||
overflow: hidden;
|
||
}
|
||
.previewhead { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
|
||
.previewbadge {
|
||
padding: 3px 9px;
|
||
border-radius: 999px;
|
||
color: var(--secondary-text-color, #9aa0aa);
|
||
background: color-mix(in srgb, var(--secondary-text-color, #9aa0aa) 12%, transparent);
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
}
|
||
.previewbadge.example { color: var(--warning-color, #ffb300); }
|
||
.previewgrid { display: grid; grid-template-columns: minmax(170px, 0.8fr) minmax(240px, 1.2fr); gap: 16px; align-items: stretch; }
|
||
.previewstage {
|
||
position: relative;
|
||
min-height: 168px;
|
||
border-radius: 12px;
|
||
background: var(--secondary-background-color, rgba(0, 0, 0, 0.16));
|
||
overflow: hidden;
|
||
pointer-events: none;
|
||
container-type: inline-size;
|
||
--icon-size: 54px;
|
||
--device-base-size: 48.6px;
|
||
}
|
||
.previewfit { position: absolute; left: 50%; top: 50%; transform-origin: center; }
|
||
.previewstage .dev { left: 0; top: 0; cursor: default; }
|
||
:host([data-pointer-hover]) .previewstage .dev:hover { z-index: 2; }
|
||
.previewfacts { min-width: 0; display: grid; align-content: start; gap: 8px; }
|
||
.previewfacts > div { display: grid; grid-template-columns: minmax(115px, 0.8fr) minmax(0, 1.3fr); gap: 8px; }
|
||
.previewfacts span { color: var(--secondary-text-color, #9aa0aa); }
|
||
.previewfacts b { min-width: 0; overflow-wrap: anywhere; }
|
||
.previewnotice { margin: 2px 0 0; color: var(--secondary-text-color, #9aa0aa); font-size: 13px; }
|
||
.previewdetails { margin-top: 12px; }
|
||
.previewdetails summary { cursor: pointer; color: var(--secondary-text-color, #9aa0aa); }
|
||
.previewtable { display: grid; gap: 8px; margin-top: 9px; }
|
||
.previewsource { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 3px 10px; font-size: 13px; }
|
||
.previewsource > * { min-width: 0; overflow-wrap: anywhere; }
|
||
.previewsource code { color: var(--secondary-text-color, #9aa0aa); }
|
||
.previewdemo {
|
||
min-height: 40px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 8px 12px;
|
||
border: 1px solid var(--divider-color, rgba(127, 127, 127, 0.35));
|
||
border-radius: 10px;
|
||
background: transparent;
|
||
color: inherit;
|
||
cursor: pointer;
|
||
}
|
||
.previewdemos { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
|
||
.previewdemo:focus-visible { outline: 2px solid var(--primary-color, #03a9f4); outline-offset: 2px; }
|
||
.previewdemo:disabled { opacity: 0.5; cursor: not-allowed; }
|
||
@media (max-width: 640px) {
|
||
.previewgrid { grid-template-columns: minmax(0, 1fr); }
|
||
.previewstage { min-height: 142px; }
|
||
.previewfacts > div { grid-template-columns: minmax(0, 1fr); gap: 2px; }
|
||
}
|
||
`],customElements.get("hp-device-preview")||customElements.define("hp-device-preview",ma);const ga=Math.tan(.25*Math.PI/180);function fa(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>ga?null:{axis:n,major:o,minor:a,angleDegrees:180*Math.atan2(a,o)/Math.PI}}function va(e,t){const i=fa(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 ba=e=>`${e[0]},${e[1]}`,ya=(e,t)=>e[0]===t[0]&&e[1]===t[1],wa=(e,t)=>{const i=ba(e),s=ba(t);return i<s?`${i}|${s}`:`${s}|${i}`},ka=e=>{let t=0;for(let i=0;i<e.length;i++){const s=e[i],n=e[(i+1)%e.length];t+=s[0]*n[1]-n[0]*s[1]}return t/2},xa=e=>{if(e.length<3)return!1;for(let t=0;t<e.length;t++){if(ya(e[t],e[(t+1)%e.length]))return!1;for(let i=t+1;i<e.length;i++)if(i!==t+1&&(0!==t||i!==e.length-1)&&xi(e[t],e[(t+1)%e.length],e[i],e[(i+1)%e.length]))return!1}return Ti(e)>1e-12},$a=e=>{for(let t=0;t+1<e.length;t++){if(ya(e[t],e[t+1]))return!1;for(let i=t+2;i+1<e.length;i++)if(xi(e[t],e[t+1],e[i],e[i+1]))return!1}return!0},Sa=(e,t)=>ya(e,t.from)?[...t.to]:[...e],Ma=(e,t)=>e.map(e=>Array.isArray(e?.poly)?{...e,poly:e.poly.map(e=>Sa(e,t))}:e),Ca=(e,t,i)=>{const s=e.flatMap((e,t)=>hi(e)?.some(e=>ya(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(!xa(n))return!1;const o=Math.sign(ka(s)),a=Math.sign(ka(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<t.length;s++){if(e===s)continue;const o=e<s?`${e}:${s}`:`${s}:${e}`;if(n.has(o))continue;n.add(o);const a=hi(t[s]);if(a&&Di(i,a))return!1}}return!0},Da=(e,t)=>{let i=0;for(const s of e){const e=hi(s);if(e)for(let s=0;s<e.length;s++){const n=e[s],o=e[(s+1)%e.length];(ya(n,t)||ya(o,t))&&(n[0]!==o[0]&&n[1]!==o[1]||i++)}}return i},Pa=(e,t)=>{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:Da(t,i)},{move:a,preservedDegree:Da(t,s)}].sort((e,t)=>t.preservedDegree-e.preservedDegree||ba(e.move.to).localeCompare(ba(t.move.to))||ba(e.move.from).localeCompare(ba(t.move.from))).map(e=>e.move)};const Ta=(e,t)=>[e[0]-t[0],e[1]-t[1]],Ia=(e,t)=>[e[0]+t[0],e[1]+t[1]],Ra=(e,t)=>e[0]*t[0]+e[1]*t[1],Aa=e=>Math.hypot(e[0],e[1]);function za(e){let t=0;for(let i=0;i<e.length;i++){const s=e[i],n=e[(i+1)%e.length];t+=s[0]*n[1]-n[0]*s[1]}return t/2}function Fa(e,t,i){const s=Ta(i,t),n=Ra(s,s);if(n<1e-12)return Aa(Ta(e,t));let o=Ra(Ta(e,t),s)/n;return o=Math.max(0,Math.min(1,o)),Aa(Ta(e,[t[0]+s[0]*o,t[1]+s[1]*o]))}function Na(e,t){const i=e[t],s=e[(t+1)%e.length],n=Ta(s,i),o=Aa(n)||1;let a=[n[1]/o,-n[0]/o];const r=[(i[0]+s[0])/2,(i[1]+s[1])/2],l=Math.max(.01*o,1e-4);return function(e,t){let i=!1;for(let s=0,n=t.length-1;s<t.length;n=s++){const o=t[s][0],a=t[s][1],r=t[n][0],l=t[n][1];a>e[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 Ea(e){const t=e.length;if(t<3)return!1;for(let i=0;i<t;i++)for(let s=i+1;s<t;s++)if(s!==i&&(s+1)%t!==i&&(i+1)%t!==s&&xi(e[i],e[(i+1)%t],e[s],e[(s+1)%t]))return!1;return!0}function Oa(e,t,i=1e-6){let s=1/0;for(const[n,o]of t){const t=Ta(o,n),a=Aa(t);if(a<i)continue;const r=[t[0]/a,t[1]/a],l=e=>(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||e<s&&(s=e)}for(let t=0;t<e.length;t++){const n=e[t],o=e[(t+1)%e.length],a=l(n),r=l(o);if(Math.abs(a)<=i||Math.abs(r)<=i)continue;const p=c(n),u=c(o),_=Math.max(h,Math.min(p,u)),m=Math.min(d,Math.max(p,u));if(m-_<=i)continue;const g=u-p;if(Math.abs(g)<i){s=Math.min(s,Math.abs(a),Math.abs(r));continue}const f=e=>Math.abs(a+(e-p)/g*(r-a));s=Math.min(s,f(_),f(m))}}return s}function Ha(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 La(e,t,i){for(const s of t)for(let t=0;t<s.length;t++){const n=s[t],o=s[(t+1)%s.length],a=Ta(o,n),r=Aa(a);if(r<i)continue;const l=[a[0]/r,a[1]/r];if(Math.abs((e.x-n[0])*l[1]-(e.y-n[1])*l[0])>i)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 Ba(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 ja(e,t,i){return Math.abs(e[0]-t[0])<=i&&Math.abs(e[1]-t[1])<=i}function qa(e,t,i,s,n){return ja(e,i,n)&&ja(t,s,n)||ja(e,s,n)&&ja(t,i,n)}function Wa(e,t,i,s,n){const o=Ba(e,t,n);return o&&Ba(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 Ua(e,t,i,s){const n=e.length,o=Ba(e[(t-1+n)%n],e[t],s),a=Ba(e[(t+1)%n],e[(t+2)%n],s),r="h"===i?"v":"h";return o===r&&a===r}function Ga(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 Va(e,t,i){const s=Ga(e),n=Ga(t);return s[2]<n[0]-i||n[2]<s[0]-i||s[3]<n[1]-i||n[3]<s[1]-i}function Ka(e,t,i,s){const n=s.eps;return"circle"===e.kind?Fa(e.center,t,i)<e.radius+(s.movingHalf||0)-n:Wa(t,i,e.a,e.b,n)>n}const Ya=(e,t)=>"h"===t?e[0]:e[1],Ja=(e,t)=>"h"===t?e[1]:e[0];function Xa(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=Ba(a,r,n);if(!l)return null;const c=0===s?r:a,h=0===s?a:r,d=Ya(c,l),p=Ya(h,l),u=Ja(c,l),_=function(e,t,i,s){const n=[];for(const o of e)for(let e=0;e<o.poly.length;e++){const a=o.poly[e],r=o.poly[(e+1)%o.poly.length];if(Ba(a,r,s)!==t||Math.abs(Ja(a,t)-i)>s||Math.abs(Ja(r,t)-i)>s)continue;const l=Ya(a,t),c=Ya(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+1<o.length;t++){const i=o[t],n=o[t+1];if(n-i<=s)continue;const r=(i+n)/2,l=[...new Set(e.filter(e=>r>e.lo-s&&r<e.hi+s).map(e=>e.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 Za(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(Ba(a,r,i)!==e.axis||Math.abs(Ja(a,e.axis)-e.line)>i||Math.abs(Ja(r,e.axis)-e.line)>i)continue;const l=Ya(a,e.axis),c=Ya(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 Qa(e,t,i){return new Set(e.filter(e=>t>e.lo-i&&t<e.hi+i).map(e=>e.roomId)).size}function er(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(Ba(n,o,i)!==s.axis)return!1;const a=Ya(0===s.movedEndpoint?n:o,s.axis),r=Za(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+1<p.length;e++){const t=p[e],n=p[e+1];if(n-t<=i)continue;const o=(t+n)/2,a=o>u-i&&o<_+i,c=o>m-i&&o<g+i;if(!a&&!c)continue;const h=Qa(s.intervals,o,i),d=Qa(r,o,i);if(h>2||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 tr(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?Ia(e,n):[...e]),o.movedSpans[t]=[[Ia(s.poly[a],n),Ia(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 ir(e,t,i,s,n,o,a,r){if(!function(e,t,i,s){if(Fa([e.x,e.y],t,i)>2*s)return!1;const n=Aa(Ta(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(Fa([e.x,e.y],s,n)>2*r)return!1;const l=0===o?s:n;return Aa(Ta([e.x,e.y],l))>=e.length/2+a-r}function sr(e,t,i,s){const n=s.movingHalf||0;return"circle"===e.kind?Fa(e.center,t,i)<e.radius+n-s.eps:function(e,t,i,s){return xi(e,t,i,s)?0:Math.min(Fa(e,i,s),Fa(t,i,s),Fa(i,e,t),Fa(s,e,t))}(t,i,e.a,e.b)<(e.half||0)+n-s.eps}function nr(e,t,i,s,n){const{eps:o,minDim:a}=n;if(!Number.isFinite(s)||i.roomIds.length<1||i.roomIds.length>2)return!1;const r=tr(e,t,i,s);if(Object.keys(r.polys).length!==i.roomIds.length)return!1;if(!er(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||!Ea(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(fa(c[e],c[(e+1)%d]))return!1;const _=za(l.poly),m=za(c);if(Math.abs(m)<o||_*m<=0)return!1;const g=[l.poly[h],l.poly[(h+1)%l.poly.length]],f=r.movedSpans[s],v=Oa(l.poly,[g],o);if(Oa(c,f,o)<Math.min(a,v)-o)return!1;const b=[l.poly[p],l.poly[h]],y=[c[p],c[h]],w=[l.poly[(h+1)%d],l.poly[(h+2)%d]],k=[c[(h+1)%d],c[(h+2)%d]],x=Ba(c[h],c[(h+1)%d],o),$="h"===x?"v":"v"===x?"h":null;if(!$||Ba(...y,o)!==$||Ba(...k,o)!==$)return!1;for(const e of t)if(!i.movingOpeningIds.includes(e.id)){if(!ir(e,...b,...y,1,n.movingHalf||0,o))return!1;if(!ir(e,...w,...k,0,n.movingHalf||0,o))return!1}}if(2===i.roomIds.length){const[e,t]=i.roomIds,s=r.polys[e],n=r.polys[t],a=i.edgeByRoom[e],l=i.edgeByRoom[t];if(!qa(s[a],s[(a+1)%s.length],n[l],n[(l+1)%n.length],o))return!1}for(const t of i.roomIds){const i=e.find(e=>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(!Va(i.poly,n.poly,o)||!Va(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)||Ha(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(sr(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(!La({...e,x:t[0],y:t[1]},[h],2*o))return!1}return!0}const or=new WeakMap;function ar(e,t,i){const s=i/t;return Ti(e)*s*s/1e4}function rr(e,t){return t?`${Math.round(10.7639*e)} ft²`:`${(Math.round(10*e)/10).toFixed(1)} m²`}const lr=e=>"number"==typeof e&&Number.isFinite(e),cr=(e,t)=>{const i=new Map;for(const s of e||[])!lr(s?.cm)||!t&&s.cm<=0||i.set(s.cm,(i.get(s.cm)||0)+1);return i},hr=e=>[String(e),"см"].join(" ");function dr(e,t,i={}){if(i.allowClear)return[];const s=!0===i.exactMultiplicity,n=cr(e,s),o=cr(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:hr(e),reference:`было ${t}`,detail:"записи этой толщины исчезли целиком"}):s&&i!==t&&a.push({invariant:"wall_records",kind:"count",owner:hr(e),reference:`было ${t}, стало ${i}`,detail:"изменилось число записей этой толщины"})}if(s)for(const[e,t]of o)n.has(e)||a.push({invariant:"wall_records",kind:"count",owner:hr(e),reference:`было 0, стало ${t}`,detail:"появились записи новой толщины"});return a}class pr{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=or.get(i);c&&c.opts===o||(c={opts:o,values:new Map},or.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=nr(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=tr(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(dr(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(nr(t.rooms,t.openings,t.plan,t.delta,t.options))if(dr(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 ur=(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 _r(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<e.length;i++){const s=e[i],n=e[(i+1)%e.length];t+=s[0]*n[1]-n[0]*s[1]}return t/2})(e)>=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=ur(r,n),p=ur(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)<n+l+8&&2*Math.abs(s-r)<o+c+8;var i,s,n,o,a,r,l,c};let v=0;if(f(0)){const e=(Math.abs(h[0])>=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 mr(e){const t=e%360;return t<0?t+360:t}function gr(e,t){const i=function(e,t){return mr(e+t)}(e,t)*Math.PI/180;return[Math.sin(i),-Math.cos(i)]}const fr=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)"})}),vr=e=>"number"==typeof e&&Number.isFinite(e),br=(e,t,i)=>Math.min(i,Math.max(t,e));function yr(e){return e.elevation<=-6?"night":e.elevation>=6?"day":e.rising?"dawn":"dusk"}function wr(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 kr(e){const t=yr(e),i=e.azimuth*Math.PI/180;return{sunX:50-42*Math.sin(i),sunY:78-br(e.elevation,0,90)/90*64,sunOpacity:"night"===t?0:br((e.elevation+6)/12,0,1)}}function xr(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 $r(e,t=new Date){const i=function(e){const t=e?.states?.["sun.sun"]?.attributes;return t&&vr(t.azimuth)&&vr(t.elevation)&&"boolean"==typeof t.rising?{azimuth:mr(t.azimuth),elevation:t.elevation,rising:t.rising}:null}(e);if(i)return{phase:yr(i),source:"sun",...kr(i)};const s="number"==typeof t?t:function(e=new Date){return 60*e.getHours()+e.getMinutes()}(t);return{phase:wr(s),source:"clock",...xr(s)}}function Sr(e){return`${e.source}|${e.phase}|${e.sunX.toFixed(2)}|${e.sunY.toFixed(2)}|`+e.sunOpacity.toFixed(3)}const Mr=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],Cr=e=>Math.min(1,Math.max(0,e));function Dr(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 Pr(e,t,i){return i>0&&e[0]*t[0]+e[1]*t[1]>.05}function Tr(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 Ir(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 Rr(e,t,i,s,n,o,a){if(!(s>0))return[];const r=gr(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=Dr(i,e);if(!t||!Pr(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=Ir(Tr(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 Ar=e=>"number"==typeof e&&Number.isInteger(e)&&e>=0&&e<=359?e:null;function zr(e,t){const i=Ar(t?.north_deg);return null!==i?i:Ar(e?.north_deg)}function Fr(e,t){const i=e=>"static"===e||"daynight"===e?e:null;return i(t?.bg_mode)??i(e?.bg_mode)??"static"}function Nr(e,t){const i=t?.sun_rays;return"boolean"==typeof i?i:!0===e?.sun_rays}function Er(e){const t=e?.states?.["sun.sun"]?.attributes,i=Number(t?.azimuth),s=Number(t?.elevation);return Number.isFinite(i)&&Number.isFinite(s)?{azimuth:i,elevation:s}:null}const Or=["dawn","day","dusk","night"];function Hr(e){if(!e)return"";const t=fr[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 Lr(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`<div class="hp-day-cycle-env" aria-hidden="true"
|
||
data-day-cycle-phase=${e.phase} data-day-cycle-source=${e.source}
|
||
style=${i}>
|
||
${Or.map(t=>{const i=fr[t],s=`background:radial-gradient(ellipse at 50% 88%, ${i.horizon} 0%, transparent 54%),linear-gradient(180deg, ${i.top} 0%, ${i.bottom} 100%);box-shadow:inset 0 0 90px ${i.vignette}`;return q`<div class="hp-day-cycle-bg phase-${t} ${t===e.phase?"active":""}"
|
||
data-day-cycle-layer=${t} style=${s}>
|
||
<div class="hp-day-cycle-sun" style="background:radial-gradient(circle, ${i.sun} 0%, transparent 67%)"></div>
|
||
</div>`})}
|
||
</div>`}const Br={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},jr=(e,t=1)=>{const i=Number(e);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):t},qr=e=>{let t=Number(e);return Number.isFinite(t)?(t=(t%360+360)%360,t>180?t-360:t):0};function Wr(e,t,i){return qr(t)?[e.x,e.y]:i([e.x,e.y])}const Ur=(e,t,i)=>{const s=Number(e),n=Number.isFinite(t)&&t>0?t:5;return Number.isFinite(s)&&s>0?s/n*i:0},Gr=(e,t,i)=>{const s=Number(e),n=Number.isFinite(t)&&t>0?t:5;return Number.isFinite(s)&&i>0?s/i*n:0},Vr=(e,t,i,s=Br.widthCm)=>{const n=Number(e?.width_cm);if(Number.isFinite(n)&&n>0)return n;const o=Number(e?.width);return Number.isFinite(o)&&o>0?Gr(o,t,i):s};function Kr(e,t){return{color:e.color,opacity:jr(e.opacity),width_cm:Math.max(.1,Math.min(100,Number(e.widthCm)||.1)),...t?{fill:e.fill,fill_color:e.fillColor,fill_opacity:jr(e.fillOpacity,.25)}:{}}}function Yr(e){const t=e.x+e.w/2,i=e.y+e.h/2,s=qr(e.angle)*Math.PI/180,n=Math.cos(s),o=Math.sin(s),a=(e,s)=>{const a=e-t,r=s-i;return[t+a*n-r*o,i+a*o+r*n]};return[a(e.x,e.y),a(e.x+e.w,e.y),a(e.x+e.w,e.y+e.h),a(e.x,e.y+e.h)]}function Jr(e){const t=Yr(e),i=(e,t)=>[(e[0]+t[0])/2,(e[1]+t[1])/2],s=[(t[0][0]+t[2][0])/2,(t[0][1]+t[2][1])/2];return{points:[...t,i(t[0],t[1]),i(t[1],t[2]),i(t[2],t[3]),i(t[3],t[0]),s],segments:t.map((e,i)=>({a:e,b:t[(i+1)%4]}))}}function Xr(e,t,i,s,n,o,a,r){const l=qr(e.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l),d=-Math.sin(l),p=Math.cos(l),u=e.x+e.w/2,_=e.y+e.h/2,m=t>0?-e.w/2:e.w/2,g=i>0?-e.h/2:e.h/2,f=u+m*c+g*d,v=_+m*h+g*p,b=s-f,y=n-v;let w=(b*c+y*h)*(t>0?1:-1),k=(b*d+y*p)*(i>0?1:-1),x=null;if(o){const t=w/Math.max(e.w,r),i=k/Math.max(e.h,r);x=Math.max(r/Math.max(e.w,r),r/Math.max(e.h,r),t,i),w=e.w*x,k=e.h*x}if(a>0)if(o){const t=Math.max(1,Math.round(e.w/a)),i=Math.max(1,Math.round(e.h/a));if(Math.abs(e.w-t*a)<1e-6&&Math.abs(e.h-i*a)<1e-6){const s=(e,t)=>{let i=Math.abs(e),s=Math.abs(t);for(;s;)[i,s]=[s,i%s];return Math.max(1,i)},n=1/s(t,i),o=Math.max(r/e.w,r/e.h),a=Math.max(o,Math.round((x??1)/n)*n);w=e.w*a,k=e.h*a}else{e.w>=e.h?(w=Math.round(w/a)*a,k=w*(e.h/Math.max(e.w,r))):(k=Math.round(k/a)*a,w=k*(e.w/Math.max(e.h,r)))}}else w=Math.round(w/a)*a,k=Math.round(k/a)*a;w=Math.max(r,w),k=Math.max(r,k);return{x:f+(t>0?w/2:-w/2)*c+(i>0?k/2:-k/2)*d-w/2,y:v+(t>0?w/2:-w/2)*h+(i>0?k/2:-k/2)*p-k/2,w:w,h:k,angle:qr(e.angle)||void 0}}const Zr=(e,t,i)=>{const s=i[0]-t[0],n=i[1]-t[1],o=s*s+n*n;if(o<1e-12)return[...t];const a=Math.max(0,Math.min(1,((e[0]-t[0])*s+(e[1]-t[1])*n)/o));return[t[0]+s*a,t[1]+n*a]};const Qr=10**9,el=240,tl=1e-4;function il(e){if(Array.isArray(e))return e.map(e=>il(e));if(null!==e&&"object"==typeof e){const t={};for(const[i,s]of Object.entries(e))t[i]=il(s);return t}return e}function sl(e){if("number"!=typeof e||!Number.isFinite(e))return e;const t=(e<0||Object.is(e,-0)?-1:1)*(Math.floor(Math.abs(e)*Qr+.5)/Qr);return 0===t?0:t}function nl(e){if("number"!=typeof e||!Number.isFinite(e))return e;const t=e*el,i=Math.round(t);if(Math.abs(t-i)<tl){const e=i/el;return 0===e?0:e}return sl(e)}function ol(e){if(!Number.isFinite(e)||0===e)return"0";return Math.abs(e)<.001?e.toExponential(2):String(Number(e.toPrecision(3)))}function al(e){return null===e||"object"!=typeof e||Array.isArray(e)?null:e}function rl(e){return Array.isArray(e)?e.filter(e=>null!==al(e)):[]}function ll(e,t){for(const i of t)Object.prototype.hasOwnProperty.call(e,i)&&(e[i]=sl(e[i]))}function cl(e,t){for(const i of t)Object.prototype.hasOwnProperty.call(e,i)&&(e[i]=nl(e[i]))}function hl(e){if(Array.isArray(e))for(let t=0;t<Math.min(2,e.length);t++)e[t]=nl(e[t])}function dl(e){if(Array.isArray(e))for(const t of e)hl(t)}function pl(e,t,i){const s=al(e);if(s)for(const e of rl(s.spaces)){const t=(t,s)=>{for(const n of s){const s=t[n];"number"==typeof s&&Number.isFinite(s)&&i(s,e)}},s=t=>{if(Array.isArray(t))for(let s=0;s<Math.min(2,t.length);s++){const n=t[s];"number"==typeof n&&Number.isFinite(n)&&i(n,e)}},n=e=>{if(Array.isArray(e))for(const t of e)s(t)};for(const i of rl(e.rooms))t(i,["x","y","w","h"]),n(i.poly);for(const t of rl(e.walls))s(t.a),s(t.b);for(const t of rl(e.wall_segments))s(t.a),s(t.b);for(const i of rl(e.openings))t(i,["x","y"]);for(const i of rl(e.decor))"line"===i.kind?t(i,["x1","y1","x2","y2"]):"rect"===i.kind||"ellipse"===i.kind||"furniture"===i.kind?t(i,["x","y","w","h"]):"text"===i.kind&&t(i,["x","y"]);for(const t of rl(e.room_drafts))n(t.points);for(const t of rl(e.partitions))s(t.a),s(t.b);for(const t of rl(e.wall_columns))s(t.center);for(const t of rl(e.open_spans))s(t.a),s(t.b)}const n=al(t);if(!n)return;const o=new Map;if(s)for(const e of rl(s.spaces))null!=e.id&&o.set(String(e.id),e);for(const e of Object.values(n)){const t=al(e);if(!t)continue;const s=null!=t.s&&o.get(String(t.s))||null;for(const e of["x","y"]){const n=t[e];"number"==typeof n&&Number.isFinite(n)&&i(n,s)}}}function ul(e){const t=Number(e?.cell_cm);return t>0?t:5}function _l(e,t={}){const i={canonicalized:0,far:0,maxShift:0,maxShiftCm:0,spaces:[],bySpace:new Map},s=rl(al(e)?.spaces);let n=5;for(const e of s)n=Math.max(n,ul(e));pl(e,t,(e,t)=>{const s=e*el,o=Math.abs(s-Math.round(s)),a=nl(e),r=o>0&&o<tl;if(!r&&!(o>=tl))return;const l=r&&"number"==typeof a?Math.abs(a-e):0;if(r?i.canonicalized++:i.far++,i.maxShift=Math.max(i.maxShift,l),i.maxShiftCm=Math.max(i.maxShiftCm,l*el*(t?ul(t):n)),!t?.id||!r)return;const c=String(t.id);let h=i.bySpace.get(c);h||(h={spaceId:c,space:String(t.title||c),canonicalized:0,far:0,maxShift:0,maxShiftCm:0},i.bySpace.set(c,h)),h.canonicalized++,h.maxShift=Math.max(h.maxShift,l),h.maxShiftCm=Math.max(h.maxShiftCm,l*el*ul(t))}),i.bySpace.size&&pl(e,t,(e,t)=>{if(!t?.id)return;const s=i.bySpace.get(String(t.id));if(!s)return;const n=e*el;Math.abs(n-Math.round(n))>=tl&&s.far++}),i.spaces=[...i.bySpace.values()];const{bySpace:o,...a}=i;return a}function ml(e){return function(e){const t=e,i=al(t);i&&cl(i,["x","y"]);return t}(il(e))}function gl(e){const t=e,i=al(t);if(!i)return t;for(const e of Object.values(i)){const t=al(e);t&&cl(t,["x","y"])}return t}function fl(e){const t=e,i=al(t);if(!i)return t;for(const e of rl(i.spaces)){ll(e,["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"]);for(const t of rl(e.rooms))cl(t,["x","y","w","h"]),dl(t.poly);for(const t of rl(e.walls))hl(t.a),hl(t.b);for(const t of rl(e.wall_segments))hl(t.a),hl(t.b);for(const t of rl(e.openings)){cl(t,["x","y"]),ll(t,["angle","length"]);const e=al(t.host);e&&ll(e,["t"])}for(const t of rl(e.decor))"line"===t.kind?cl(t,["x1","y1","x2","y2"]):"rect"===t.kind||"ellipse"===t.kind||"furniture"===t.kind?(cl(t,["x","y","w","h"]),ll(t,["angle"])):"text"===t.kind&&(cl(t,["x","y"]),ll(t,["scale","angle"]));for(const t of rl(e.room_drafts))dl(t.points);for(const t of rl(e.partitions))hl(t.a),hl(t.b);for(const t of rl(e.wall_columns))hl(t.center),"square"===t.shape&&ll(t,["angle"]);for(const t of rl(e.open_spans))hl(t.a),hl(t.b)}for(const e of rl(i.markers))ll(e,["angle"]);return t}const vl=15;function bl(e,t,i,s,n){const o=i[0]-e[0],a=i[1]-e[1],r=Math.hypot(o,a);if(!(r>0)||r<=n+1e-9)return null;const l=o/r,c=a/r,h=t=>{const s=(t[0]-e[0])*l+(t[1]-e[1])*c,o=(n-s)/(r-s);return!Number.isFinite(o)||o<-1e-9||o>1+1e-9?null:[t[0]+(i[0]-t[0])*o,t[1]+(i[1]-t[1])*o]},d=h(t),p=h(s);return d&&p?[[e[0],e[1]],t,d,p,s]:null}const yl=Math.sin(.25*Math.PI/180);function wl(e,t){return Number.isFinite(e)&&e>0&&Number.isFinite(t)&&t>0&&e*t<3}function kl(e){const t=Number(e)>0?Number(e):5;if(5===t)return 8;const i=5/t*8;return Math.min(80,Math.max(.5,i))}function xl(e,t){return Number.isFinite(e)&&e>0&&Number.isFinite(t)&&t>0&&e*t<2}function $l(e){return Number.isFinite(e)?Math.max(1,Math.min(100,e)):1}function Sl(e,t){return!Number.isFinite(e)||e<0?"":String(t?Math.round(e/2.54*100)/100:Math.round(100*e)/100)}function Ml(e,t,i){if(!Number.isFinite(e)||e<=0)return 0;const s=Number(t)>0?Number(t):5;return $l(e)/s*i}function Cl(e,t){return t>0&&Number.isFinite(e)?Math.round(e/t)*t:e}function Dl(e){return Math.max(1e-6*Math.abs(e),1e-9)}function Pl(e,t){if(!(t>0&&Number.isFinite(e)))return e;const i=Cl(e,t);return Math.abs(i-e)<=Dl(t)?i:e}function Tl(e,t){let i=t[0]-e[0],s=t[1]-e[1];const n=Math.hypot(i,s);return n<1e-12?[1,0]:(i/=n,s/=n,(i<-1e-12||Math.abs(i)<=1e-12&&s<0)&&(i=-i,s=-s),[i,s])}function Il(e,t,i){const s=[Pl(e[0],i),Pl(e[1],i)],n=[Pl(t[0],i),Pl(t[1],i)],o=Cl((s[0]+n[0])/2,i),a=Cl((s[1]+n[1])/2,i),[r,l]=Tl(s,n);let c=Math.atan2(l,r);c<0&&(c+=Math.PI);const h=Math.round(1800*c)/1800,d=i>0&&i<.01?6:i<1?4:2;return`${o.toFixed(d)},${a.toFixed(d)}@${h.toFixed(4)}`}function Rl(e,t,i,s){return 1===s?Il(e,t,i):Il([e[0]/s,e[1]/s],[t[0]/s,t[1]/s],i)}function Al(e,t){if(!Array.isArray(e.a)||!Array.isArray(e.b)||e.a.length<2||e.b.length<2)return null;const i=[Number(e.a[0]),Number(e.a[1]),Number(e.b[0]),Number(e.b[1])];if(!i.every(Number.isFinite))return null;const s=t>0?t:1;return[[i[0]*s,i[1]*s],[i[2]*s,i[3]*s]]}function zl(e,t,i,s,n){const o=n>0?n:1;return{key:Rl(e,t,s,o),cm:$l(i),a:[e[0]/o,e[1]/o],b:[t[0]/o,t[1]/o]}}function Fl(e,t){const i=t>0?t:1,s=[];for(const t of e){const e=t.key.lastIndexOf("@");if(e<0)continue;const[n,o]=t.key.slice(0,e).split(",").map(Number),a=Number(t.key.slice(e+1));[n,o,a].every(Number.isFinite)&&s.push({w:t,x:n*i,y:o*i,ang:a})}return s}function Nl(e,t){const[i,s]=Tl(e,t);let n=Math.atan2(s,i);return n<0&&(n+=Math.PI),n}function El(e,t){let i=Math.abs(e-t);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function Ol(e,t,i,s,n=1){if(!e?.length)return null;const o=Rl(t,i,s,n),a=e.find(e=>e.key===o);if(a)return a;const r=n>0?n:1,l=Dl(s)*r,c=(e,t)=>Math.abs(e[0]-t[0])<=l&&Math.abs(e[1]-t[1])<=l;for(const s of e){const e=Al(s,r);if(e&&(c(e[0],t)&&c(e[1],i)||c(e[0],i)&&c(e[1],t)))return s}const h=(t[0]+i[0])/2,d=(t[1]+i[1])/2,p=Nl(t,i),u=Math.max(.5*s,1e-9)*r;for(const t of Fl(e,r))if(El(t.ang,p)&&Math.hypot(t.x-h,t.y-d)<=u)return t.w;return null}function Hl(e,t,i,s,n=1){const o=Ol(e,t,i,s,n);if(o&&o.cm>0)return $l(o.cm);const a=function(e,t,i,s,n){if(!e?.length)return null;const o=n>0?n:1,a=Math.hypot(i[0]-t[0],i[1]-t[1]);if(a<1e-12)return null;const r=Nl(t,i),l=Math.max(.5*s,1e-9)*o;let c=null;for(const s of e){if(!(s.cm>0))continue;const e=Al(s,o);if(!e)continue;const n=Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1]);if(n<1e-12||!El(Nl(e[0],e[1]),r))continue;if(Sc(t[0],t[1],e[0][0],e[0][1],e[1][0],e[1][1])>l||Sc(i[0],i[1],e[0][0],e[0][1],e[1][0],e[1][1])>l)continue;if(n+l<a)continue;const h=Math.max(0,n-a),d=`${s.key}|${$l(s.cm)}|${e.flat().join(",")}`;(!c||h<c.extra-1e-12||Math.abs(h-c.extra)<=1e-12&&d<c.stable)&&(c={wall:s,extra:h,stable:d})}return c?.wall||null}(e,t,i,s,n);return a?$l(a.cm):0}function Ll(e,t,i,s=1,n=[]){if(!e?.length)return[];const o=new Set,a=pi(t);for(const e of a)o.add(Rl([e[0],e[1]],[e[2],e[3]],i,s));const r=t||[],l=Math.max(i*s*.02,1e-9);for(let e=0;e<r.length;e++){const t=hi(r[e]);if(t)for(let n=e+1;n<r.length;n++){const e=hi(r[n]);if(e)for(const n of Cs(t,e,l))o.add(Rl([n[0],n[1]],[n[2],n[3]],i,s))}}for(const t of r){if(!t?.id)continue;const a=dc(r,t.id,n,i,s,e);if(a)for(let e=0;e<a.poly.length;e++)o.add(Rl(a.poly[e],a.poly[(e+1)%a.poly.length],i,s))}return e.filter(e=>(o.has(e.key)||(e=>{const t=Al(e,s);if(!t)return!1;const[i,o]=t,r=o[0]-i[0],c=o[1]-i[1],h=Math.hypot(r,c);if(h<=l)return!1;if(!a.some(e=>{const t=[e[0],e[1]],s=[e[2],e[3]];return El(Nl(i,o),Nl(t,s))&&Sc(i[0],i[1],t[0],t[1],s[0],s[1])<=l&&Sc(o[0],o[1],t[0],t[1],s[0],s[1])<=l}))return!1;const d=(n||[]).some(e=>{const t=[e[0],e[1]],s=[e[2],e[3]];if(!El(Nl(i,o),Nl(t,s)))return!1;const n=e=>Math.abs((e[0]-i[0])*c-(e[1]-i[1])*r)/h;if(n(t)>l||n(s)>l)return!1;const a=h*h,d=((t[0]-i[0])*r+(t[1]-i[1])*c)/a,p=((s[0]-i[0])*r+(s[1]-i[1])*c)/a;return Math.min(1,Math.max(d,p))-Math.max(0,Math.min(d,p))>l/h});return!d})(e))&&e.cm>=1&&e.cm<=100)}function Bl(e,t,i,s=8){const[n,o]=Tl(e,t);let a=Math.atan2(o,n);a<0&&(a+=Math.PI);let r=i*Math.PI/180%Math.PI;r<0&&(r+=Math.PI);let l=Math.abs(a-r);return l>Math.PI/2&&(l=Math.PI-l),l<=s*Math.PI/180}function jl(e,t,i,s,n=1,o="affine",a){if(!e?.length)return[];if(t.length!==i.length)return"fixed-topology"===o&&a?.(),e.slice();const r=n>0?n:1,l=Math.max(.5*s,1e-9)*r,c=Math.max(s*r*1e-6,1e-9),h=[],d=new Map,p=new Map,u=(e,t,i)=>{const s=e.get(t)||new Set;s.add(i),e.set(t,s)};for(let e=0;e<t.length;e++){const[o,a]=t[e],[r,l]=i[e];if(![o?.[0],o?.[1],a?.[0],a?.[1],r?.[0],r?.[1],l?.[0],l?.[1]].every(Number.isFinite))continue;const _=a[0]-o[0],m=a[1]-o[1],g=_*_+m*m;if(g<1e-18)continue;if(Math.max(Math.hypot(r[0]-o[0],r[1]-o[1]),Math.hypot(l[0]-a[0],l[1]-a[1]))<=c)continue;h.push({oa:o,ob:a,na:r,nb:l,dx:_,dy:m,len2:g});const f=Rl(o,a,s,n),v=Rl(r,l,s,n);u(p,f,v),u(p,Rl(o,a,s,1),Rl(r,l,s,1)),f!==v&&u(d,f,v)}if(!h.length)return e.slice();const _=(e,t,i)=>[e[0]+(t[0]-e[0])*i,e[1]+(t[1]-e[1])*i],m=(e,t)=>Math.hypot(e[0]-t[0],e[1]-t[1])<=c,g=(e,t)=>{if("fixed-topology"===o){const i=t.na[0]-t.oa[0],s=t.na[1]-t.oa[1],n=t.nb[0]-t.ob[0],o=t.nb[1]-t.ob[1];return Math.hypot(i-n,s-o)<=c?[e[0]+i,e[1]+s]:m(e,t.oa)?[...t.na]:m(e,t.ob)?[...t.nb]:[...e]}const i=Math.max(0,Math.min(1,((e[0]-t.oa[0])*t.dx+(e[1]-t.oa[1])*t.dy)/t.len2));return _(t.na,t.nb,i)},f=(e,t)=>{const[i,s]=Tl(e,t);return(t[0]-e[0])*i+(t[1]-e[1])*s>=0?[[...e],[...t]]:[[...t],[...e]]},v=[],b=[],y=(e,t,i)=>{if(Math.hypot(t[0]-e[0],t[1]-e[1])<=c)return;const[n,o]=f(e,t),a=$l(i);if(b.some(e=>e.entry.cm===a&&m(e.span[0],n)&&m(e.span[1],o)))return;const l=zl(n,o,a,s,r);v.push(l),b.push({entry:l,span:[n,o]})};for(const t of e){const e=Al(t,r);if(e){const[i,s]=f(e[0],e[1]),n=s[0]-i[0],a=s[1]-i[1],r=n*n+a*a,d=Math.sqrt(r);if(d<=c){v.push({...t,cm:$l(t.cm)});continue}const p=[];for(const e of h){if(!El(Nl(i,s),Nl(e.oa,e.ob)))continue;const t=e=>Math.abs((e[0]-i[0])*a-(e[1]-i[1])*n)/d;if(t(e.oa)>l||t(e.ob)>l)continue;const o=((e.oa[0]-i[0])*n+(e.oa[1]-i[1])*a)/r,h=((e.ob[0]-i[0])*n+(e.ob[1]-i[1])*a)/r,u=Math.max(0,Math.min(o,h)),_=Math.min(1,Math.max(o,h));(_-u)*d>c&&p.push({lo:u,hi:_,move:e})}if(!p.length){"fixed-topology"===o?v.push({...t}):y(i,s,t.cm);continue}const u=[0,1,...p.flatMap(({lo:e,hi:t})=>[e,t])].sort((e,t)=>e-t).filter((e,t,i)=>0===t||Math.abs(e-i[t-1])*d>c),b=[];for(let e=0;e+1<u.length;e++){const t=u[e],n=u[e+1];if((n-t)*d<=c)continue;const o=_(i,s,t),a=_(i,s,n),r=(t+n)/2,l=p.filter(e=>r>=e.lo-1e-12&&r<=e.hi+1e-12);if(!l.length){b.push([o,a]);continue}const h=[g(o,l[0].move),g(a,l[0].move)],f=l.slice(1).some(e=>{const t=g(o,e.move),i=g(a,e.move);return!m(h[0],t)||!m(h[1],i)});b.push(f?[o,a]:h)}const w=(e,t)=>{const i=e[1][0]-e[0][0],s=e[1][1]-e[0][1],n=t[1][0]-t[0][0],o=t[1][1]-t[0][1],a=Math.hypot(i,s);return!(a<=c||i*n+s*o<=0)&&Math.abs(i*o-s*n)/a<=c},k=[];for(const e of b){const t=k[k.length-1];t&&m(t[1],e[0])&&w(t,e)?t[1]=e[1]:k.push([[...e[0]],[...e[1]]])}if("fixed-topology"===o&&1===k.length){const[i,s]=k[0];if(m(i,e[0])&&m(s,e[1])||m(i,e[1])&&m(s,e[0])){v.push({...t});continue}}for(const[e,i]of k)y(e,i,t.cm);continue}if("fixed-topology"===o){const e=p.get(t.key);if(1===e?.size){const i=[...e][0];v.push(i===t.key?{...t}:{...t,key:i});continue}const i=[Fl([t],r)[0]];1!==r&&i.push(Fl([t],1)[0]);const s=i.filter(Boolean).some(e=>h.some(t=>{if(!El(e.ang,Nl(t.oa,t.ob)))return!1;const i=((e.x-t.oa[0])*t.dx+(e.y-t.oa[1])*t.dy)/t.len2;return i>=-1e-6&&i<=1.000001&&Sc(e.x,e.y,t.oa[0],t.oa[1],t.ob[0],t.ob[1])<=l}));((e?.size||0)>1||s)&&a?.(),v.push({...t});continue}let i="";const n=d.get(t.key);if(1===n?.size&&(i=[...n][0]),!i){const e=Fl([t],r)[0];if(e){const t=new Set;for(const i of h){if(!El(e.ang,Nl(i.oa,i.ob)))continue;const n=((e.x-i.oa[0])*i.dx+(e.y-i.oa[1])*i.dy)/i.len2;if(n<-1e-6||n>1.000001)continue;if(Sc(e.x,e.y,i.oa[0],i.oa[1],i.ob[0],i.ob[1])>l)continue;const o=g([e.x,e.y],i),[a,c]=Tl(i.na,i.nb),h=Math.max(s*r,1e-6);t.add(Rl([o[0]-a*h,o[1]-c*h],[o[0]+a*h,o[1]+c*h],s,r))}1===t.size&&(i=[...t][0])}}v.push({...t,key:i||t.key,cm:$l(t.cm)})}return v}function ql(e,t,i,s,n=1,o="affine"){return jl(e,t,i,s,n,o)}function Wl(e,t,i,s=1,n=[]){const o=s>0?s:1,a=Math.abs(i),r=Math.max(a*o*tl,1e-9),l=(n||[]).flatMap(e=>{const t=Al(e,o);return t?t.map(e=>[...e]):[]}),c=e=>e.every((e,t)=>(e=>{if(!(a>0))return!0;const t=e/o/a;return Math.abs(t-Math.round(t))<tl})(e)||l.some(i=>Math.abs(e-i[t])<=r)),h=[],d=e=>JSON.stringify([e.key,e.cm,e.a,e.b]);for(const i of e||[]){const e=Al(i,o);if(!e)continue;const[s,n]=e;if(![s[0],s[1],n[0],n[1]].every(Number.isFinite)||!c(s)||!c(n)){h.push(d(i));continue}const a=n[0]-s[0],l=n[1]-s[1],p=Math.hypot(a,l);if(p<=r){h.push(d(i));continue}const u=a/p,_=l/p,m=[];for(const e of t){const[t,i]=e;if(![t?.[0],t?.[1],i?.[0],i?.[1]].every(Number.isFinite))continue;const n=e=>Math.abs((e[0]-s[0])*_-(e[1]-s[1])*u);if(n(t)>r||n(i)>r)continue;const o=(t[0]-s[0])*u+(t[1]-s[1])*_,a=(i[0]-s[0])*u+(i[1]-s[1])*_,l=Math.max(0,Math.min(o,a)),c=Math.min(p,Math.max(o,a));c-l>r&&m.push([l,c])}m.sort((e,t)=>e[0]-t[0]||e[1]-t[1]);let g=0;for(const[e,t]of m){if(e>g+r)break;if(g=Math.max(g,t),g>=p-r)break}g<p-r&&h.push(d(i))}return h}function Ul(e,t,i,s,n,o=1){const a=Rl(t,i,n,o),r=(e||[]).filter(e=>e.key!==a);return null==s||s<1?r:[...r,zl(t,i,s,n,o)]}function Gl(e,t,i,s,n,o=[],a=1){let r=e?e.slice():[];for(const e of function(e,t,i,s,n=1,o=[]){const a=dc(e,t,i,s,n,o);if(!a)return[];const r=[];for(let e=0;e<a.poly.length;e++){const t=a.poly[e],o=a.poly[(e+1)%a.poly.length];ic(t,o,i,s,n)||r.push({a:t,b:o})}return r}(t,i,o,n,a,r))r=Ul(r,e.a,e.b,s,n,a);return r}function Vl(e,t,i,s,n,o=[],a=1){if(null==s||s<1)return e?e.slice():[];const r=dc(t,i,o,n,a,e);if(!r)return e?e.slice():[];const l=pc(e,r,n,a);let c=e?e.slice():[];for(let e=0;e<r.poly.length;e++){const t=r.poly[e],i=r.poly[(e+1)%r.poly.length];ic(t,i,o,n,a)||(l[e]>0||(c=Ul(c,t,i,s,n,a)))}return c}function Kl(e){const{a:t,b:i,halfDepth:s}=e;if(!Array.isArray(t)||!Array.isArray(i)||t.length<2||i.length<2||![t[0],t[1],i[0],i[1]].every(Number.isFinite))return null;const n=i[0]-t[0],o=i[1]-t[1],a=Math.hypot(n,o);if(!(a>1e-9&&s>0&&Number.isFinite(s)))return null;const r=-o/a*s,l=n/a*s;return[[t[0]+r,t[1]+l],[i[0]+r,i[1]+l],[i[0]-r,i[1]-l],[t[0]-r,t[1]-l]]}function Yl(e,t,i){return Math.hypot(e[0]-t[0],e[1]-t[1])<=i}function Jl(e,t,i){const s=t.b[0]-t.a[0],n=t.b[1]-t.a[1],o=s*s+n*n;if(!(o>i*i))return!1;const a=((e[0]-t.a[0])*s+(e[1]-t.a[1])*n)/o;if(!(a>0&&a<1))return!1;const r=[t.a[0]+s*a,t.a[1]+n*a];return Math.hypot(e[0]-r[0],e[1]-r[1])<=i}function Xl(e,t,i,s){const n=Math.hypot(t,i);if(!(n>1e-9&&s>0))return;const o=[t/n,i/n],a=e.find(e=>Math.abs(e.u[0]*o[1]-e.u[1]*o[0])<1e-9&&e.u[0]*o[0]+e.u[1]*o[1]>1-1e-9);a?a.halfDepth=Math.max(a.halfDepth,s):e.push({u:o,halfDepth:s})}function Zl(e,t){try{const i=oi(Dc(e),Dc(t));let s=null,n=0;for(const e of i||[]){const t=e?.[0]||[],i=Math.abs(nc(t));t.length>=4&&i>n&&(n=i,s=t.slice(0,-1).map(e=>[e[0],e[1]]))}return s}catch{return null}}function Ql(e,t=1e-6){const i=(e||[]).map((e,t)=>({segment:e,index:t})).filter(({segment:e})=>e&&Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2&&e.a.every(Number.isFinite)&&e.b.every(Number.isFinite)&&Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>1e-9);if(i.length<2)return[];const s=Math.max(Number.isFinite(t)?t:0,1e-9),n=i.flatMap(({segment:e})=>[e.a,e.b]).map(e=>[e[0],e[1]]).sort((e,t)=>e[0]-t[0]||e[1]-t[1]),o=[];for(const e of n)o.some(t=>Yl(t,e,s))||o.push(e);const a=[];for(const e of o){const t=[];let n=!1;for(const{segment:o,index:a}of i){const i=Math.hypot(o.b[0]-o.a[0],o.b[1]-o.a[1]);Yl(e,o.a,s)?t.push({u:[(o.b[0]-o.a[0])/i,(o.b[1]-o.a[1])/i],halfDepth:o.halfDepth,length:i,index:a}):Yl(e,o.b,s)?t.push({u:[(o.a[0]-o.b[0])/i,(o.a[1]-o.b[1])/i],halfDepth:o.halfDepth,length:i,index:a}):Jl(e,o,s)&&(n=!0)}if(n||2!==t.length)continue;const[o,r]=t,l=o.u[0]*r.u[1]-o.u[1]*r.u[0];if(Math.abs(l)<1e-9)continue;const c=l<0?1:-1,h=[-o.u[1],o.u[0]],d=[-r.u[1],r.u[0]],p=[e[0]+h[0]*o.halfDepth*c,e[1]+h[1]*o.halfDepth*c],u=[e[0]-d[0]*r.halfDepth*c,e[1]-d[1]*r.halfDepth*c];if(!rc(p,o.u,u,r.u))continue;const _=[[o,r,u,[d[0]*-c,d[1]*-c]],[r,o,p,[h[0]*c,h[1]*c]]];for(const[t,,i,n]of _){const o=Math.min(2*t.halfDepth,t.length),r=[-t.u[1]*t.halfDepth,t.u[0]*t.halfDepth],l=[[e[0]+r[0],e[1]+r[1]],[e[0]+t.u[0]*o+r[0],e[1]+t.u[1]*o+r[1]],[e[0]+t.u[0]*o-r[0],e[1]+t.u[1]*o-r[1]],[e[0]-r[0],e[1]-r[1]]],c=e=>(e[0]-i[0])*n[0]+(e[1]-i[1])*n[1],h=[];for(let e=0;e<l.length;e++){const t=l[e],i=l[(e+1)%l.length],s=c(t),n=c(i);if(s>=-1e-12&&h.push(t),s>1e-12&&n<-1e-12||s<-1e-12&&n>1e-12){const e=s/(s-n);h.push([t[0]+(i[0]-t[0])*e,t[1]+(i[1]-t[1])*e])}}h.length>=3&&Math.abs(nc(h))>s*s&&a.push({segmentIndex:t.index,wedge:h})}}return a}function ec(e,t=1e-6){const i=(e||[]).filter(e=>e&&Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2&&e.a.every(Number.isFinite)&&e.b.every(Number.isFinite)&&Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>1e-9);if(i.length<2)return[];const s=Math.max(Number.isFinite(t)?t:0,1e-9),n=i.flatMap(e=>[e.a,e.b]).map(e=>[e[0],e[1]]).sort((e,t)=>e[0]-t[0]||e[1]-t[1]),o=[];for(const e of n)o.some(t=>Yl(t,e,s))||o.push(e);const a=[],r=i.map((e,t)=>({roomId:"",a:[e.a[0],e.a[1]],b:[e.b[0],e.b[1]],key:`join-${t}`,kind:"outer",cm:0,open:!1,half:e.halfDepth})),l=gc(r,s);for(const e of fc(l).fans)a.push(e);const c=e=>!!vc(l,e);for(const e of o){if(c(e))continue;const t=[];for(const n of i)Yl(e,n.a,s)?Xl(t,n.b[0]-n.a[0],n.b[1]-n.a[1],n.halfDepth):Yl(e,n.b,s)?Xl(t,n.a[0]-n.b[0],n.a[1]-n.b[1],n.halfDepth):Jl(e,n,s)&&(Xl(t,n.a[0]-e[0],n.a[1]-e[1],n.halfDepth),Xl(t,n.b[0]-e[0],n.b[1]-e[1],n.halfDepth));if(!(t.length<2)){t.sort((e,t)=>Math.atan2(e.u[1],e.u[0])-Math.atan2(t.u[1],t.u[0])||e.halfDepth-t.halfDepth);for(let i=0;i<t.length;i++)for(let n=i+1;n<t.length;n++){const o=t[i],r=t[n],l=o.u[0]*r.u[1]-o.u[1]*r.u[0];if(Math.abs(l)<1e-9)continue;const c=[-o.u[1],o.u[0]],h=[-r.u[1],r.u[0]],d=l<0?1:-1,p=[e[0]+c[0]*o.halfDepth*d,e[1]+c[1]*o.halfDepth*d],u=[e[0]-h[0]*r.halfDepth*d,e[1]-h[1]*r.halfDepth*d],_=rc(p,o.u,u,r.u),m=_?[e.slice(),p,_,u]:[e.slice(),p,u];Math.abs(nc(m))>s*s&&a.push(m)}}}return a}function tc(e,t,i,s){if(!(t>0)||!e||e.length<2)return"";if(i&&e.length>=3){let i=e;const n=e[e.length-1];if(e.length>=4&&Math.hypot(e[0][0]-n[0],e[0][1]-n[1])<1e-9&&(i=e.slice(0,-1)),i.length>=3){const e=i.map((e,i)=>s?.[i]||t),n=Vc(i,e),o=lc(i,e);if(n&&o)return`${cc(n)} ${cc(hc(o))}`}}const n=[];for(let i=0;i<e.length-1;i++){const o=e[i],a=e[i+1],r=s?.[i]||t;Math.hypot(a[0]-o[0],a[1]-o[1])>=1e-9&&r>0&&n.push({a:o,b:a,halfDepth:r})}const o=n.map(Kl);for(const{segmentIndex:e,wedge:t}of Ql(n)){const i=o[e];if(!i)continue;const s=Zl(i,t);s&&(o[e]=s)}const a=[...o.filter(e=>!!e),...ec(n)],r=function(e){let t=null;try{for(const i of e){if(i.length<3)continue;const e=Dc(i);t=t?si(t,e):[e]}return t}catch{return null}}(a);return r?Lc(r):a.map(e=>cc(e)).join(" ")}function ic(e,t,i,s,n=1){if(!i.length)return!1;const o=sc(s,n),a=(e[0]+t[0])/2,r=(e[1]+t[1])/2,[l,c]=Tl(e,t);for(const e of i){const[t,i]=Tl([e[0],e[1]],[e[2],e[3]]);if(!(Math.abs(l*i-c*t)>.05)&&Sc(a,r,e[0],e[1],e[2],e[3])<=o)return!0}return!1}function sc(e,t){return Math.max(e*(t>0?t:1)*.04,1e-9)}function nc(e){let t=0;for(let i=0;i<e.length;i++){const s=e[i],n=e[(i+1)%e.length];t+=s[0]*n[1]-n[0]*s[1]}return t/2}function oc(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;let r=-o/a,l=n/a;const c=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(e,t){let i=!1;for(let s=0,n=t.length-1;s<t.length;n=s++){const o=t[s][0],a=t[s][1],r=t[n][0],l=t[n][1];a>e[1]!=l>e[1]&&e[0]<(r-o)*(e[1]-a)/(l-a+0)+o&&(i=!i)}return i}([c[0]+.001*r,c[1]+.001*l],e)||(r=-r,l=-l),[r,l]}function ac(e,t){const i=e[0]*t[1]-e[1]*t[0],s=e[0]*t[0]+e[1]*t[1];return Math.abs(i)<1e-9&&s>0}function rc(e,t,i,s){const n=t[0]*s[1]-t[1]*s[0];if(Math.abs(n)<1e-12)return null;const o=[i[0]-e[0],i[1]-e[1]],a=(o[0]*s[1]-o[1]*s[0])/n;return[e[0]+a*t[0],e[1]+a*t[1]]}function lc(e,t,i){const s=e?.length||0;if(s<3||t.length!==s)return null;if(t.every(e=>!(e>0)))return e.map(e=>[e[0],e[1]]);const n=[];for(let o=0;o<s;o++){const a=(o-1+s)%s,r=e[a],l=e[o],c=e[o],h=e[(o+1)%s],d=Math.max(0,t[a]),p=Math.max(0,t[o]),[u,_]=oc(e,a),[m,g]=oc(e,o),f=[l[0]-r[0],l[1]-r[1]],v=[h[0]-c[0],h[1]-c[1]],b=Math.hypot(f[0],f[1])||1,y=Math.hypot(v[0],v[1])||1,w=[f[0]/b,f[1]/b],k=[v[0]/y,v[1]/y],x=[r[0]+u*d,r[1]+_*d],$=[c[0]+m*p,c[1]+g*p];if(!(d>0||p>0)){n.push([e[o][0],e[o][1]]);continue}if(d>0!=p>0){const t=e[o],i=d>0?[t[0]+u*d,t[1]+_*d]:[t[0],t[1]],s=p>0?[t[0]+m*p,t[1]+g*p]:[t[0],t[1]];n.push(i),Math.hypot(s[0]-i[0],s[1]-i[1])>1e-9&&n.push(s);continue}if(ac(w,k)){const t=e[o],i=[t[0]+u*d,t[1]+_*d],s=[t[0]+m*p,t[1]+g*p];n.push(i),Math.hypot(s[0]-i[0],s[1]-i[1])>1e-9&&n.push(s);continue}const S=rc(x,w,$,k),M=Math.max(d,p,1e-9),C=vc(i,e[o])?.limit??4*M;if(S){const i=Math.hypot(S[0]-e[o][0],S[1]-e[o][1]);if(Number.isFinite(i)&&i<=C){n.push(S);continue}if(jc(e,t,o)){n.push(S);continue}}d>0&&n.push([e[o][0]+u*d,e[o][1]+_*d]),p>0&&n.push([e[o][0]+m*p,e[o][1]+g*p]),d>0||p>0||n.push([e[o][0],e[o][1]])}return n.length>=3?n:null}function cc(e,t=!0){if(!e.length)return"";let i=`M ${e[0][0]} ${e[0][1]}`;for(let t=1;t<e.length;t++)i+=` L ${e[t][0]} ${e[t][1]}`;return t&&(i+=" Z"),i}function hc(e){return e.slice().reverse()}function dc(e,t,i,s,n=1,o=[]){const a=(e||[]).find(e=>e?.id===t),r=hi(a);if(!r||r.length<3)return null;const l=sc(s,n),c=[];for(const i of e||[]){if(!i||i.id===t)continue;const e=hi(i);if(e)for(const t of Cs(r,e,l))c.push([t[0],t[1]],[t[2],t[3]])}for(const e of i||[])c.push([e[0],e[1]],[e[2],e[3]]);for(const e of o||[]){const t=Al(e,n);t&&c.push(t[0],t[1])}const h=[],d=[];for(let e=0;e<r.length;e++){const t=r[e],i=r[(e+1)%r.length];h.push([t[0],t[1]]),d.push(e);const s=Math.hypot(i[0]-t[0],i[1]-t[1]);if(s<2*l||!c.length)continue;const n=Math.min(.499,2*l/s),o=[];for(const e of c){if(Sc(e[0],e[1],t[0],t[1],i[0],i[1])>l)continue;const a=((e[0]-t[0])*(i[0]-t[0])+(e[1]-t[1])*(i[1]-t[1]))/(s*s);a<=n||a>=1-n||(o.some(e=>Math.abs(e-a)*s<=2*l)||o.push(a))}o.sort((e,t)=>e-t);for(const s of o)h.push([t[0]+(i[0]-t[0])*s,t[1]+(i[1]-t[1])*s]),d.push(e)}return{poly:h,parent:d,orig:r}}function pc(e,t,i,s){const n=t.poly.length,o=new Array(n).fill(0);if(!e?.length)return o;const a=new Set,r=[];for(let l=0;l<n;l++){const c=Ol(e,t.poly[l],t.poly[(l+1)%n],i,s);c&&c.cm>0?(o[l]=$l(c.cm),a.add(c.key)):r.push(l)}if(!r.length)return o;const l=s>0?s:1,c=Math.max(.5*i,1e-9)*l,h=Fl(e,l).filter(e=>e.w.cm>0);for(let e=r.length-1;e>=0;e--){const i=r[e],s=t.poly[i],a=t.poly[(i+1)%n],d=Nl(s,a);let p=null;for(const e of h){const t=Al(e.w,l);if(!t||!El(Nl(t[0],t[1]),d))continue;if(Sc(s[0],s[1],t[0][0],t[0][1],t[1][0],t[1][1])>c||Sc(a[0],a[1],t[0][0],t[0][1],t[1][0],t[1][1])>c)continue;const i=Math.hypot(a[0]-s[0],a[1]-s[1]),n=Math.hypot(t[1][0]-t[0][0],t[1][1]-t[0][1]),o=Math.max(0,n-i);(!p||o<p.extra)&&(p={cm:$l(e.w.cm),extra:o})}p&&(o[i]=p.cm,r.splice(e,1))}const d=new Map;for(const e of r){const i=t.parent[e],s=d.get(i);s?s.push(e):d.set(i,[e])}for(const[e,i]of d){const s=t.orig[e],n=t.orig[(e+1)%t.orig.length],r=Nl(s,n),d=(s[0]+n[0])/2,p=(s[1]+n[1])/2;let u=null;const _=Math.hypot(n[0]-s[0],n[1]-s[1]);for(const e of h){if(a.has(e.w.key))continue;if(!El(e.ang,r))continue;const t=Al(e.w,l);let i=!1,o=0;if(t){if(!El(Nl(t[0],t[1]),r))continue;if(Sc(s[0],s[1],t[0][0],t[0][1],t[1][0],t[1][1])>c||Sc(n[0],n[1],t[0][0],t[0][1],t[1][0],t[1][1])>c)continue;i=!0,o=Math.max(0,Math.hypot(t[1][0]-t[0][0],t[1][1]-t[0][1])-_)}else{if(Sc(e.x,e.y,s[0],s[1],n[0],n[1])>c)continue;o=Math.hypot(e.x-d,e.y-p)}(!u||i&&!u.exact||i===u.exact&&o<u.d)&&(u={cm:$l(e.w.cm),d:o,exact:i})}if(u)for(const e of i)o[e]=u.cm}return o}function uc(e,t){return[Math.floor(e[0]/t),Math.floor(e[1]/t)]}function _c(e,t){return`${e},${t}`}function mc(e,t,i){const[s,n]=uc(t,i),o=[];for(let t=-1;t<=1;t++)for(let i=-1;i<=1;i++){const a=e.get(_c(s+t,n+i));a&&o.push(...a)}return o}function gc(e,t=1e-6,i=1){const s=Math.max(Number.isFinite(t)?t:0,1e-9),n=Number.isFinite(i)&&i>0?i:1,o=(e||[]).filter(e=>e&&!e.open&&null!==e.kind&&Number.isFinite(e.half)&&e.half>0&&Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2&&[e.a[0],e.a[1],e.b[0],e.b[1]].every(Number.isFinite)&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>s).sort((e,t)=>e.key.localeCompare(t.key)||e.a[0]-t.a[0]||e.a[1]-t.a[1]||e.b[0]-t.b[0]||e.b[1]-t.b[1]||e.half-t.half),a=new Map;for(const e of o){const t=a.get(e.key);t?e.half>t.half&&a.set(e.key,{...t,half:e.half}):a.set(e.key,e)}const r=[...a.values()].flatMap(e=>[{point:[e.a[0],e.a[1]],other:e.b,halfDepth:e.half,kind:e.kind,key:e.key},{point:[e.b[0],e.b[1]],other:e.a,halfDepth:e.half,kind:e.kind,key:e.key}]).sort((e,t)=>e.point[0]-t.point[0]||e.point[1]-t.point[1]||e.other[0]-t.other[0]||e.other[1]-t.other[1]||e.halfDepth-t.halfDepth),l=new Map;for(const e of r){const[t,i]=uc(e.point,s),n=_c(t,i),o=l.get(n)||[];o.push(e),l.set(n,o)}const c=[],h=new Map;for(const e of r){const t=mc(h,e.point,s).filter(t=>Math.hypot(t.point[0]-e.point[0],t.point[1]-e.point[1])<=s).sort((t,i)=>Math.hypot(t.point[0]-e.point[0],t.point[1]-e.point[1])-Math.hypot(i.point[0]-e.point[0],i.point[1]-e.point[1])||t.point[0]-i.point[0]||t.point[1]-i.point[1]);let i=t[0];if(!i){i={point:[...e.point],rays:[]},c.push(i);const[t,n]=uc(i.point,s),o=_c(t,n),a=h.get(o)||[];a.push(i),h.set(o,a)}const n=e.other[0]-e.point[0],o=e.other[1]-e.point[1],a=Math.hypot(n,o);if(!(a>s))continue;const r=[n/a,o/a];let l=Math.atan2(r[1],r[0]);l<0&&(l+=2*Math.PI),i.rays.push({u:r,halfDepth:e.halfDepth,length:a,angle:l})}const d=[],p=e=>{const t=1e-9*Math.max(1,n),i=e.filter(e=>Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Number.isFinite(e.length)&&e.length>s);return i.filter((e,s)=>!i.some((i,n)=>n!==s&&i.halfDepth>=e.halfDepth-t&&i.length>=e.length-t&&(i.halfDepth>e.halfDepth+t||i.length>e.length+t||n<s))).sort((e,t)=>e.length-t.length||e.halfDepth-t.halfDepth).map(e=>({...e}))};for(const e of c){const t=e.rays.sort((e,t)=>e.angle-t.angle||e.length-t.length||e.halfDepth-t.halfDepth),i=[];for(const e of t){const t=i[i.length-1];t&&Math.abs(e.angle-t.angle)<=1e-9?t.supports.push({halfDepth:e.halfDepth,length:e.length}):i.push({u:[...e.u],angle:e.angle,supports:[{halfDepth:e.halfDepth,length:e.length}]})}if(i.length>1&&2*Math.PI-i[i.length-1].angle+i[0].angle<=1e-9){const e=i.pop();i[0].supports.push(...e.supports)}if(i.length<3)continue;const n=i.map(t=>{const i=p(t.supports),n=new Set,o=[];for(const a of i){const i=[e.point[0]+t.u[0]*a.length,e.point[1]+t.u[1]*a.length];for(const t of mc(l,i,s)){if("shared"!==t.kind||Math.hypot(t.point[0]-i[0],t.point[1]-i[1])>s)continue;const a=t.other[0]-t.point[0],r=t.other[1]-t.point[1],l=Math.hypot(a,r);if(!(l>s))continue;const c=a/l,h=r/l;if(Math.hypot(t.other[0]-e.point[0],t.other[1]-e.point[1])<=s)continue;const d=`${t.key}|${t.point[0]}|${t.point[1]}|${t.other[0]}|${t.other[1]}|${t.halfDepth}`;n.has(d)||(n.add(d),o.push({start:[t.point[0],t.point[1]],u:[c,h],length:l,halfDepth:t.halfDepth}))}}return o.sort((e,t)=>e.start[0]-t.start[0]||e.start[1]-t.start[1]||e.u[0]-t.u[0]||e.u[1]-t.u[1]||e.length-t.length||e.halfDepth-t.halfDepth),{u:[...t.u],halfDepth:Math.max(...i.map(e=>e.halfDepth)),length:Math.max(...i.map(e=>e.length)),supports:i,continuations:o}}).filter(e=>Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Number.isFinite(e.length)&&e.length>s);if(n.length<3)continue;const o=Math.max(...n.map(e=>e.halfDepth));o>0&&Number.isFinite(o)&&d.push({point:[...e.point],rays:n,halfDepth:o,limit:1.25*o})}d.sort((e,t)=>e.point[0]-t.point[0]||e.point[1]-t.point[1]);const u=new Map;for(const e of d){const[t,i]=uc(e.point,s),n=_c(t,i),o=u.get(n)||[];o.push(e),u.set(n,o)}return{epsilon:s,coordinateScale:n,nodes:d,index:u}}function fc(e){const t={fans:[],supports:[]};if(!e?.nodes?.length)return t;const i=Math.max(e.epsilon,1e-9)**2;for(const s of e.nodes){const e=s.rays.filter(e=>Number.isFinite(e.halfDepth)&&e.halfDepth>0).map(e=>({...e,thickLength:Math.max(...e.supports.filter(t=>t.halfDepth>=e.halfDepth-1e-12).map(e=>e.length),0),angle:(()=>{const t=Math.atan2(e.u[1],e.u[0]);return t<0?t+2*Math.PI:t})()})).sort((e,t)=>e.angle-t.angle);if(e.length<2)continue;const n=s.point;for(const i of e)for(const e of i.supports){if(!(e.halfDepth>0&&e.length>0))continue;const s=-i.u[1]*e.halfDepth,o=i.u[0]*e.halfDepth,a=[n[0]+i.u[0]*e.length,n[1]+i.u[1]*e.length];t.supports.push([[n[0]+s,n[1]+o],[a[0]+s,a[1]+o],[a[0]-s,a[1]-o],[n[0]-s,n[1]-o]])}for(let s=0;s<e.length;s++){const o=e[s],a=e[(s+1)%e.length],r=(()=>{const e=a.angle-o.angle;return e>0?e:e+2*Math.PI})();if(r<1e-9)continue;const l=r>Math.PI+1e-9,c=4*Math.max(o.halfDepth,a.halfDepth),h=[n[0]-o.u[1]*o.halfDepth,n[1]+o.u[0]*o.halfDepth],d=[n[0]+a.u[1]*a.halfDepth,n[1]-a.u[0]*a.halfDepth],p=o.u[0]*a.u[1]-o.u[1]*a.u[0],u=e=>{let t=Math.atan2(e[1]-n[1],e[0]-n[0])-o.angle;for(;t<0;)t+=2*Math.PI;return t<=r+1e-9};let _=null;if(Math.abs(p)>1e-9){const e=((d[0]-h[0])*a.u[1]-(d[1]-h[1])*a.u[0])/p,t=((d[0]-h[0])*o.u[1]-(d[1]-h[1])*o.u[0])/p,i=[h[0]+o.u[0]*e,h[1]+o.u[1]*e];(l?e<=1e-9&&t<=1e-9:e>1e-9&&e<=o.thickLength&&t<=a.thickLength)&&Math.hypot(i[0]-n[0],i[1]-n[1])<=c&&u(i)&&(_=i)}const m=e=>{Math.abs(nc(e))>i&&t.fans.push(e)};if(_){const e=1.5*Math.max(o.halfDepth,a.halfDepth);m(bl([n[0],n[1]],h,_,d,e)??[[n[0],n[1]],h,_,d]);continue}if(l){m([[n[0],n[1]],h,d]);continue}const g=(e,t)=>Math.min(t,Math.sqrt(Math.max(c**2-e**2,0)),2*Math.max(o.halfDepth,a.halfDepth)),f=[h[0]+o.u[0]*g(o.halfDepth,o.thickLength),h[1]+o.u[1]*g(o.halfDepth,o.thickLength)],v=[d[0]+a.u[0]*g(a.halfDepth,a.thickLength),d[1]+a.u[1]*g(a.halfDepth,a.thickLength)];m([[n[0],n[1]],h,f,v,d])}}return t}function vc(e,t){return!e||!Array.isArray(t)||t.length<2||!t.slice(0,2).every(Number.isFinite)?null:mc(e.index,t,e.epsilon).filter(i=>Math.hypot(i.point[0]-t[0],i.point[1]-t[1])<=e.epsilon).sort((e,i)=>Math.hypot(e.point[0]-t[0],e.point[1]-t[1])-Math.hypot(i.point[0]-t[0],i.point[1]-t[1])||e.point[0]-i.point[0]||e.point[1]-i.point[1])[0]||null}function bc(e,t,i,s,n,o,a){return gc(wc(e,t,i,s,n,o,a),4*sc(s,a),a)}function yc(e,t,i,s,n,o,a,r=1){const l=dc(e,t,s,n,r,i);if(!l)return null;const c=function(e,t,i){const s=(e||[]).find(e=>e?.id===t),n=hi(s);if(!n)return[];const o=[];for(const s of e||[]){if(!s||s.id===t)continue;const e=hi(s);if(e)for(const t of Cs(n,e,i))o.push(t)}return o}(e,t,sc(n,r)),h=function(e,t,i,s,n){const o=sc(s,n),a=[];for(let r=0;r<e.length;r++){const l=e[r],c=e[(r+1)%e.length];if(ic(l,c,i,s,n)){a.push(null);continue}const h=(l[0]+c[0])/2,d=(l[1]+c[1])/2,p=t.some(e=>Sc(h,d,e[0],e[1],e[2],e[3])<=o);a.push(p?"shared":"outer")}return a}(l.poly,c,s,n,r),d=pc(i,l,n,r),p=d.map((e,t)=>h[t]&&e>0?Ml(e,o,a)/2:0);return{...l,kinds:h,cms:d,offsets:p}}function wc(e,t,i,s,n,o,a=1){const r=[];for(const l of e||[]){if(!l?.id)continue;const c=yc(e,l.id,t,i,s,n,o,a);if(c)for(let e=0;e<c.poly.length;e++){const t=c.poly[e],i=c.poly[(e+1)%c.poly.length];r.push({roomId:l.id,a:[t[0],t[1]],b:[i[0],i[1]],key:Rl(t,i,s,a),kind:c.kinds[e],cm:c.kinds[e]?c.cms[e]:0,open:null===c.kinds[e],half:c.offsets[e]})}}return r}function kc(e,t,i,s,n,o,a=1){let r=[];const l=wc(e,t,i,s,n,o,a);for(const e of l)!e.open&&e.cm>0&&(r=Ul(r,e.a,e.b,e.cm,s,a));return r}function xc(e,t,i,s,n,o,a=1){if(!t?.length)return[];const r=wc(e,t,i,s,n,o,a),l=new Map;for(const e of r){if(e.open||!e.kind||!e.roomId)continue;const t=l.get(e.key)||new Set;t.add(e.roomId),l.set(e.key,t)}const c=e=>{const t=[...l.get(e)||[]].sort();return 1!==t.length&&2!==t.length?`ambiguous:${e}`:`${1===t.length?"outer":"shared"}:${t.join("|")}`},h=[],d=new Set;for(const e of r)!e.open&&e.cm>0&&!d.has(e.key)&&(d.add(e.key),h.push({...e,ownerSignature:c(e.key)}));const p=[];for(const r of e||[]){if(!r?.id)continue;const l=yc(e,r.id,t,i,s,n,o,a);if(l)for(let e=0;e<l.orig.length;e++){const t=[];for(let i=0;i<l.parent.length;i++)l.parent[i]===e&&t.push(i);if(t.length)for(let e=0;e<t.length;){const i=t[e],n=l.cms[i];if(!(n>0)||null===l.kinds[i]){e++;continue}const o=c(Rl(l.poly[i],l.poly[(i+1)%l.poly.length],s,a));let r=e;for(;r+1<t.length;){const e=t[r+1],i=Rl(l.poly[e],l.poly[(e+1)%l.poly.length],s,a);if(null===l.kinds[e]||l.cms[e]!==n||c(i)!==o)break;r++}const h=t[r],d=l.poly[i],u=l.poly[(h+1)%l.poly.length],_=Math.hypot(u[0]-d[0],u[1]-d[1]);_>0&&p.push({a:[d[0],d[1]],b:[u[0],u[1]],key:Rl(d,u,s,a),cm:n,len:_,ownerSignature:o}),e=r+1}}}p.sort((e,t)=>t.len-e.len||e.key.localeCompare(t.key));const u=[],_=new Set,m=new Set,g=4*sc(s,a);for(const e of p){const t=h.filter(t=>!m.has(t.key)&&t.cm===e.cm&&t.ownerSignature===e.ownerSignature&&El(Nl(t.a,t.b),Nl(e.a,e.b))&&Sc(t.a[0],t.a[1],e.a[0],e.a[1],e.b[0],e.b[1])<=g&&Sc(t.b[0],t.b[1],e.a[0],e.a[1],e.b[0],e.b[1])<=g);if(t.length){for(const e of t)m.add(e.key);_.has(e.key)||(_.add(e.key),u.push(zl(e.a,e.b,e.cm,s,a)))}}for(const e of h)m.has(e.key)||_.has(e.key)||(_.add(e.key),u.push(zl(e.a,e.b,e.cm,s,a)));return u}function $c(e,t,i,s,n,o,a,r=1){const l=sc(n,r),c=(s[0]+s[2])/2,h=(s[1]+s[3])/2,d=Nl([s[0],s[1]],[s[2],s[3]]);let p=null;for(const s of wc(e,t,i,n,o,a,r)){if(!El(Nl(s.a,s.b),d))continue;const e=Sc(c,h,s.a[0],s.a[1],s.b[0],s.b[1]);e>4*l||(!p||e<p.d)&&(p={cm:s.cm,d:e})}return p?.cm||0}function Sc(e,t,i,s,n,o){const a=n-i,r=o-s,l=a*a+r*r;if(l<1e-18)return Math.hypot(e-i,t-s);let c=((e-i)*a+(t-s)*r)/l;return c=Math.max(0,Math.min(1,c)),Math.hypot(e-(i+a*c),t-(s+r*c))}function Mc(e){let t=null,i=0;for(const s of e||[]){const e=s?.[0];if(!Array.isArray(e)||e.length<4)continue;const n=e.slice(0,-1).map(e=>[e[0],e[1]]),o=Math.abs(nc(n));n.length>=3&&o>i&&(t=n,i=o)}return t}function Cc(e,t,i,s,n,o,a,r=1,l,c){const h=(e||[]).find(e=>e?.id===t),d=hi(h);if(!d||d.length<3)return null;if(!i?.length)return d.map(e=>[e[0],e[1]]);const p=yc(e,t,i,s,n,o,a,r);if(!p||!p.offsets.some(e=>e>0))return d.map(e=>[e[0],e[1]]);const u=c||bc(e,i,s,n,o,a,r),_=lc(p.poly,p.offsets,u);if(!_)return d.map(e=>[e[0],e[1]]);if(!u.nodes.length)return _;const m=void 0===l?qc(e,i,s,[],n,o,a,r):null,g=l??("ok"===m?.status||"degraded-extra"===m?.status?m.roomGeom:void 0);if(g)try{const e=Mc(oi(Dc(p.poly),g));if(e)return e}catch{}return function(e,t){try{return Mc(ni(Dc(e),Dc(t)))}catch{return null}}(_,p.poly)||d.map(e=>[e[0],e[1]])}function Dc(e){const t=e.map(e=>[e[0],e[1]]);return t.push([e[0][0],e[0][1]]),[t]}function Pc(e){return!(!Array.isArray(e)||!e.length)&&e.every(e=>Array.isArray(e)&&e.length&&e.every(e=>Array.isArray(e)&&e.length>=4&&e.every(e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1])))&&Math.abs(Ti(e[0]))>1e-9)}function Tc(e,t,i,s){const n=s?new Set(s):null;let o=null;for(let s=0;s<e.rays.length;s++){if(n&&!n.has(s))continue;const a=e.rays[s],r=[-a.u[1],a.u[0]];for(const s of a.supports){const n=Math.min(i,s.length);if(!(n>t.epsilon))continue;const l=Fc([[e.point[0]+r[0]*s.halfDepth,e.point[1]+r[1]*s.halfDepth],[e.point[0]+a.u[0]*n+r[0]*s.halfDepth,e.point[1]+a.u[1]*n+r[1]*s.halfDepth],[e.point[0]+a.u[0]*n-r[0]*s.halfDepth,e.point[1]+a.u[1]*n-r[1]*s.halfDepth],[e.point[0]-r[0]*s.halfDepth,e.point[1]-r[1]*s.halfDepth]],t.coordinateScale);if(!l)continue;const c=Dc(l);o=o?si(o,c):c}}return o}function Ic(e,t,i){let s=null;for(const n of e.rays)for(const e of n.continuations){const n=[-e.u[1],e.u[0]],o=[e.start[0]+e.u[0]*e.length,e.start[1]+e.u[1]*e.length],a=Fc([[e.start[0]+n[0]*e.halfDepth,e.start[1]+n[1]*e.halfDepth],[o[0]+n[0]*e.halfDepth,o[1]+n[1]*e.halfDepth],[o[0]-n[0]*e.halfDepth,o[1]-n[1]*e.halfDepth],[e.start[0]-n[0]*e.halfDepth,e.start[1]-n[1]*e.halfDepth]],t.coordinateScale);if(!a)continue;const r=ni(Dc(a),i);Array.isArray(r)&&0!==r.length&&(s=s?si(s,r):r)}return s}function Rc(e,t,i=2*(4*e.halfDepth+2*t.epsilon)){const s=function(e,t=yl){const i=new Set,s=Number.isFinite(t)&&t>=0?t:yl;for(let t=0;t<e.rays.length;t++)for(let n=t+1;n<e.rays.length;n++){const o=e.rays[t].u,a=e.rays[n].u;Math.abs(o[0]*a[0]+o[1]*a[1])<=s&&(i.add(t),i.add(n))}return[...i].sort((e,t)=>e-t)}(e);return s.length?Tc(e,t,i,s):null}function Ac(e,t,i,s,n){const o=function(e){let t=null;for(const i of e){const e=Dc(i);try{t=t?si(t,e):e}catch{}}return t}(function(e,t,i=!1){if(!e)return[];const s=[];for(const n of e.nodes)for(let o=0;o<n.rays.length;o++){const a=n.rays[o],r=n.rays[(o+1)%n.rays.length],l=Math.atan2(a.u[1],a.u[0]);let c=Math.atan2(r.u[1],r.u[0]);for(;c<=l;)c+=2*Math.PI;const h=c-l;if(!(h>1e-9)||h>=Math.PI-1e-9)continue;const d=[-a.u[1],a.u[0]],p=[-r.u[1],r.u[0]],u=[n.point[0]+d[0]*a.halfDepth,n.point[1]+d[1]*a.halfDepth],_=[n.point[0]-p[0]*r.halfDepth,n.point[1]-p[1]*r.halfDepth],m=rc(u,a.u,_,r.u);if(!m)continue;const g=Math.hypot(m[0]-n.point[0],m[1]-n.point[1]);if(!Number.isFinite(g)||g<=n.limit)continue;const f=t?Math.sqrt(Math.max(0,n.limit*n.limit-a.halfDepth*a.halfDepth)):0,v=t?Math.sqrt(Math.max(0,n.limit*n.limit-r.halfDepth*r.halfDepth)):0,b=Fc([[u[0]+a.u[0]*f,u[1]+a.u[1]*f],[_[0]+r.u[0]*v,_[1]+r.u[1]*v],m],e.coordinateScale);if(b&&s.push(b),i){const t=m[0]-n.point[0],i=m[1]-n.point[1],o=Math.hypot(t,i),a=g-n.limit;if(o>e.epsilon&&a>e.epsilon){const r=Math.min(Math.max(8*e.epsilon,.05*n.halfDepth),.25*a),l=t/o,c=i/o,h=-c,d=l,p=Fc([[m[0]-l*r+h*r,m[1]-c*r+d*r],[m[0]-l*r-h*r,m[1]-c*r-d*r],[m[0]+l*r,m[1]+c*r]],e.coordinateScale);p&&s.push(p)}}}return s}({...t,nodes:[e]},i,s));return o&&n?oi(o,n):o}function zc(e,t,i,s){if(!e||!t.nodes.length)return e;let n=null;try{n=function(e){let t=null;for(const i of e.nodes){const s=Rc(i,e);s&&(t=t?si(t,s):s)}return t}(t)}catch{return e}let o=e;for(const e of t.nodes){const a=4*e.halfDepth+2*t.epsilon,r=2*a,l=[[e.point[0]-a,e.point[1]-a],[e.point[0]+a,e.point[1]-a],[e.point[0]+a,e.point[1]+a],[e.point[0]-a,e.point[1]+a]];try{let a=o;const c=Ac(e,t,!1,!0,n);c&&(a=oi(a,c));let h=Tc(e,t,r);const d=Ac(e,t,!0,!0,n);d&&(h=oi(h,d)),n&&(h=si(h,n));const p=.02*Math.min(...e.rays.map(e=>e.halfDepth));if(h=si(h,Dc([[e.point[0]-p,e.point[1]-p],[e.point[0]+p,e.point[1]-p],[e.point[0]+p,e.point[1]+p],[e.point[0]-p,e.point[1]+p]])),!h)continue;let u=ni(h,Dc(l));s?u=ni(u,s):i&&(u=ni(u,i));const _=Dc(l),m=oi(a,_),g=i?oi(ni(a,_),i):null,f=Ic(e,t,_);o=si(m,...g?[g]:[],...f?[f]:[],u)}catch{}}if(n)try{let e=n;s?e=ni(e,s):i&&(e=ni(e,i)),o=si(o,e)}catch{}return o}function Fc(e,t=1){if(!Array.isArray(e)||e.length<3)return null;const i=Number.isFinite(t)&&t>0?t:1,s=1e-12*Math.max(1,i),n=[];for(const t of e){if(!Array.isArray(t)||t.length<2)return null;const e=Number(t[0]),i=Number(t[1]);if(!Number.isFinite(e)||!Number.isFinite(i))return null;const o=Math.round(e/s)*s,a=Math.round(i/s)*s;if(!Number.isFinite(o)||!Number.isFinite(a))return null;n.push([Object.is(o,-0)?0:o,Object.is(a,-0)?0:a])}return Math.abs(nc(n))>s*s?n:null}function Nc(e,t,i,s){if(Sc(e[0],e[1],t[0],t[1],i[0],i[1])>s)return!1;const n=i[0]-t[0],o=i[1]-t[1],a=(e[0]-t[0])*n+(e[1]-t[1])*o,r=n*n+o*o,l=s*Math.sqrt(r);return a>=-l&&a<=r+l}function Ec(e,t,i){const s=[],n=[];for(let o=0;o<e.length;o++){const a=e[o],r=e[(o+1)%e.length],l=r[0]-a[0],c=r[1]-a[1],h=l*l+c*c;if(!(h>i*i))continue;const d=i/Math.sqrt(h),p=[0,1];for(const e of t)for(const t of[e.a,e.b]){if(!Nc(t,a,r,i))continue;const e=((t[0]-a[0])*l+(t[1]-a[1])*c)/h;e>d&&e<1-d&&p.push(e)}p.sort((e,t)=>e-t);const u=p.filter((e,t)=>0===t||Math.abs(e-p[t-1])>d);for(let e=0;e<u.length-1;e++){const o=u[e],r=u[e+1],h=[a[0]+l*o,a[1]+c*o],d=[a[0]+l*(o+r)/2,a[1]+c*(o+r)/2];let p=0;for(const e of t)Nc(d,e.a,e.b,i)&&(p=Math.max(p,e.half));s.push(h),n.push(p)}}return s.length>=3&&n.length===s.length?{poly:s,offsets:n}:null}function Oc(e,t,i,s,n,o,a,r){const l=(e||[]).map(hi).filter(e=>!!e&&e.length>=3);if(!l.length)return null;let c=si(Dc(l[0]));for(let e=1;e<l.length;e++)c=si(c,Dc(l[e]));const h=wc(e,t,i,s,n,o,a),d=h.filter(e=>"outer"===e.kind&&e.half>0),p=4*sc(s,a),u=r||gc(h,p,a);let _=null;for(const e of function(e){const t=[];for(const i of Array.isArray(e)?e:[])if(Array.isArray(i))for(const e of i){if(!Array.isArray(e)||e.length<4)continue;const i=e.slice(0,-1).map(e=>[e[0],e[1]]);i.length>=3&&t.push(i)}return t}(c)){const t=Ec(e,d,p);if(!t||!t.offsets.some(e=>e>0))continue;const i=Vc(t.poly,t.offsets,u),s=lc(t.poly,t.offsets,u);if(!i||!s)continue;const n=oi(Dc(i),Dc(s));_=_?si(_,n):n}return{centre:c,shell:_||[]}}function Hc(e,t,i,s,n,o,a=1){try{const r=Oc(e,t,i,s,n,o,a,bc(e,t,i,s,n,o,a));if(!r)return[];return r.shell?.length?si(r.centre,r.shell):r.centre}catch{return null}}function Lc(e){if(!e)return"";let t="";for(const i of e)if(Array.isArray(i))for(const e of i){if(!Array.isArray(e)||e.length<4)continue;const i=e.slice(0,e.length-1);i.length<3||(t+=(t?" ":"")+cc(i.map(e=>[e[0],e[1]])))}return t}const Bc=15;function jc(e,t,i){const s=e?.length||0;if(s<3||t?.length!==s)return!1;const n=e[(i-1+s)%s],o=e[i],a=e[(i+1)%s],r=[n[0]-o[0],n[1]-o[1]],l=[a[0]-o[0],a[1]-o[1]],c=Math.hypot(r[0],r[1]),h=Math.hypot(l[0],l[1]);if(!(c>1e-9&&h>1e-9))return!1;const d=Math.max(-1,Math.min(1,(r[0]*l[0]+r[1]*l[1])/(c*h))),p=Math.acos(d);if(!(p>1e-9))return!1;if(p>=Bc*Math.PI/180)return!1;const u=Math.max(Math.max(0,t[(i-1+s)%s]),Math.max(0,t[i]));if(!(u>0))return!1;const _=u/Math.tan(p/2);return Number.isFinite(_)&&_>0&&_<c-1e-9&&_<h-1e-9}function qc(e,t,i,s=[],n,o,a,r=1,l=[],c={}){if(!t?.length&&!l.length)return{status:"not-applicable",geom:[],components:[],roomGeom:[],paperGeom:[],roomComponents:[],depthUnits:0,openingIndex:null,multiWallNodes:null,degradedExtraCount:0};const h=[],d=bc(e,t,i,n,o,a,r);let p=0;for(const s of e||[]){if(!s?.id)continue;const l=yc(e,s.id,t,i,n,o,a,r);if(!l||l.poly.length<3||!l.offsets.some(e=>e>0))continue;for(const e of l.offsets)e>0&&(p=Math.max(p,2*e));const c=Vc(l.poly,l.offsets,d),u=lc(l.poly,l.offsets,d);c&&h.push({outset:c,inset:u})}for(const e of l){const t=e.map(e=>e[0]),i=e.map(e=>e[1]);if(t.length){const s=Math.min(Math.max(...t)-Math.min(...t),Math.max(...i)-Math.min(...i)),n=Math.min(...e.map((t,i)=>{const s=e[(i+1)%e.length];return Math.hypot(s[0]-t[0],s[1]-t[1])}));p=Math.max(p,e.length>16?s:n)}}const u=function(e,t,i,s,n,o,a,r){if(!t?.length||!i?.length)return[];const l=4*sc(s,a),c=new Map;for(const r of wc(e,t,i,s,n,o,a))!r.open&&r.half>0&&!c.has(r.key)&&c.set(r.key,r);const h=[...c.values()];if(h.length<2)return[];const d=r||gc(h,l),p=[];for(const e of i)for(const t of[[e[0],e[1]],[e[2],e[3]]])p.some(e=>Math.hypot(e[0]-t[0],e[1]-t[1])<=l)||p.push(t);const u=[],_=(e,t)=>{let i=0,s=0;if(Math.hypot(e.a[0]-t[0],e.a[1]-t[1])<=l)i=e.b[0]-e.a[0],s=e.b[1]-e.a[1];else{if(!(Math.hypot(e.b[0]-t[0],e.b[1]-t[1])<=l))return null;i=e.a[0]-e.b[0],s=e.a[1]-e.b[1]}const n=Math.hypot(i,s);return n>l?[i/n,s/n]:null};for(const e of p){const t=h.map(t=>({iv:t,u:_(t,e)})).filter(e=>!!e.u);for(let i=0;i<t.length;i++)for(let s=i+1;s<t.length;s++){const n=t[i],o=t[s],a=n.u[0]*o.u[1]-n.u[1]*o.u[0],r=Math.abs(a);if(r<.001)continue;const c=o.iv.half/r,h=n.iv.half/r,p=[e[0]-n.u[0]*c,e[1]-n.u[1]*c],_=[e[0]-o.u[0]*h,e[1]-o.u[1]*h],m=[p[0]+_[0]-e[0],p[1]+_[1]-e[1]],g=Math.max(n.iv.half,o.iv.half,1e-9),f=vc(d,e),v=f?.limit??4*g;let b;if(Math.hypot(m[0]-e[0],m[1]-e[1])<=v)b=a>0?[e.slice(),p,m,_]:[e.slice(),_,m,p];else{if(!f)continue;{const t=[-n.u[1],n.u[0]],i=[-o.u[1],o.u[0]],s=a<0?1:-1,r=[e[0]+t[0]*n.iv.half*s,e[1]+t[1]*n.iv.half*s],l=[e[0]-i[0]*o.iv.half*s,e[1]-i[1]*o.iv.half*s];b=a>0?[e.slice(),r,l]:[e.slice(),l,r]}}Math.abs(nc(b))>l*l&&u.push(b)}}return u}(e,t,i,n,o,a,r,d),_=s.length?Kc(e,t,i,n,o,a,r):null;let m="exterior";try{const v=Oc(e,t,i,n,o,a,r,d);m="paper";const b=v?v.shell?.length?si(v.centre,v.shell):v.centre:[],y=e=>{const t=Dc(e.outset);if(!e.inset)return t;const i=Dc(e.inset);return oi(t,i)};m="room-rings";let w=null;for(const e of h)try{const t=y(e);w=w?si(w,t):t}catch{}if(m="edge-bodies",v)for(const s of Gc(e,t,i,n,o,a,r))try{const e=ni(Dc(s.quad),v.centre);w=w?si(w,e):e}catch{}m="junctions",w=function(e,t,i=1,s=si){let n=e;for(const e of t||[]){const t=Fc(e,i);if(t)try{const e=Dc(t);n=n?s(n,e):e}catch{}}return n}(w,u,r),m="facade-clip",w&&v&&(w=ni(w,v.centre)),m="exterior-shell";const k=[];let x=0;if(v?.shell?.length)if(w)try{const e=si(w,v.shell);if(!Pc(e))throw new Error("invalid shell union");w=e}catch{if(!Pc(w)||!Pc(v.shell))throw new Error("invalid shell");k.push({id:"exterior-shell",geom:v.shell}),x++}else w=v.shell;if(m="multi-wall-trim",w&&d.nodes.length){const e=e=>e.rays.some(e=>e.supports.some(e=>e.length<2*e.halfDepth)),t=d.nodes.filter(e);if(t.length){w=zc(w,{...d,nodes:t},v?.centre,b)}}if(m="junction-corners",d.nodes.length){const s=fc(d),l=function(e,t,i,s,n,o,a,r){try{const l=Oc(e,t,i,s,n,o,a,{epsilon:r.epsilon,coordinateScale:r.coordinateScale,nodes:[],index:new Map});return l?l.shell?.length?si(l.centre,l.shell):l.centre:null}catch{return null}}(e,t,i,n,o,a,r,d);for(const e of s.fans)try{let t=[Dc(e)];if(l&&(t=ni(t,l)),!t?.length)continue;w=w?si(w,t):t}catch{}g=w,f=Math.max(d.epsilon,1e-9)**2,w=Array.isArray(g)?g.map(e=>{if(!Array.isArray(e)||!e.length)return e;const[t,...i]=e;return Math.abs(nc(t||[]))<=f?null:[t,...i.filter(e=>Math.abs(nc(e||[]))>f)]}).filter(e=>!!e):g}const $=w||[],S=[...Pc($)?[{id:"room-primary",geom:$}]:[],...k.map((e,t)=>({id:`room-isolated-${t}`,geom:e.geom}))];m="openings";for(const e of s){if(!(e.length>0))continue;const t=Xc(_,e,!0);if(!t.negative&&!t.positive)continue;const i=e.angle*Math.PI/180,s=Math.cos(i),o=Math.sin(i),a=-o,l=s,c=e.length/2,h=1.25*Math.max(p,n*r),d=[[e.x-s*c-a*h,e.y-o*c-l*h],[e.x+s*c-a*h,e.y+o*c-l*h],[e.x+s*c+a*h,e.y+o*c+l*h],[e.x-s*c+a*h,e.y-o*c+l*h]];w&&(w=oi(w,Dc(d)));for(const e of k)e.geom=oi(e.geom,Dc(d))}m="extras";const M=[];let C=0;const D=c.mergeExtra||((e,t)=>e?si(e,t):t);for(let e=0;e<l.length;e++){const t=l[e];if(t.length<3||!t.every(e=>e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]))||Math.abs(Ti(t))<=1e-9){C++;continue}const i=[Dc(t)];try{const t=D(w,i,e);if(!Pc(t))throw new Error("invalid extra union");w=t}catch{C++,Pc(i)&&M.push({id:`extra-${e}`,geom:i})}}M.push(...k),M.sort((e,t)=>Lc(e.geom).localeCompare(Lc(t.geom)));const P=w||[];return{status:C||x?"degraded-extra":"ok",geom:P,components:[...Pc(P)?[{id:"primary",geom:P}]:[],...M.map((e,t)=>({...e,id:`isolated-${t}`}))],roomGeom:$,roomComponents:S,paperGeom:b,depthUnits:p,openingPadUnits:1.25*Math.max(p,n*r),openingIndex:_,multiWallNodes:d,degradedExtraCount:C+x}}catch{return c.onCoreFailure?.(m),{status:"failed-core",geom:[],components:[],roomGeom:[],paperGeom:[],roomComponents:[],depthUnits:p,openingIndex:null,multiWallNodes:d,degradedExtraCount:0}}var g,f}function Wc(e,t,i,s=[],n,o,a,r=1,l=[],c={}){if(!t?.length&&!l.length)return null;return Uc(qc(e,t,i,s,n,o,a,r,l,c))}function Uc(e){if("failed-core"===e.status||"not-applicable"===e.status)return null;const t=e.components.map(e=>({id:e.id,d:Lc(e.geom),fillRule:"evenodd"})).filter(e=>!!e.d),i=t[0]?.d||"",s=Lc(e.paperGeom);if(t.length){const n={status:e.status,d:i,paths:t,components:e.components,roomGeom:e.roomGeom,multiWallNodes:e.multiWallNodes,paperD:s,depthUnits:e.depthUnits,fillRule:"evenodd"};return Object.defineProperties(n,{roomComponents:{value:e.roomComponents||[],enumerable:!1},openingIndex:{value:e.openingIndex,enumerable:!1},openingPadUnits:{value:e.openingPadUnits,enumerable:!1}}),n}return null}function Gc(e,t,i,s,n,o,a=1){if(!t?.length)return[];const r=new Set,l=[];for(const c of e||[]){if(!c?.id)continue;const h=yc(e,c.id,t,i,s,n,o,a);if(!h)continue;const d=h.poly;for(let e=0;e<d.length;e++){const t=d[e],i=d[(e+1)%d.length],c=h.kinds[e];if(!c)continue;const p=h.cms[e];if(!(p>0))continue;const u=Rl(t,i,s,a);if(r.has(u))continue;r.add(u);const _=Ml(p,n,o),[m,g]=oc(d,e),f=-m,v=-g,b=_/2,y=[[t[0]+f*b,t[1]+v*b],[i[0]+f*b,i[1]+v*b],[i[0]+m*b,i[1]+g*b],[t[0]+m*b,t[1]+g*b]];l.push({key:u,kind:c,cm:p,quad:y,a:[t[0],t[1]],b:[i[0],i[1]],depthUnits:_})}}return l}function Vc(e,t,i){const s=e?.length||0;if(s<3||t.length!==s)return null;if(t.every(e=>!(e>0)))return e.map(e=>[e[0],e[1]]);hc(e),t.slice().reverse();const n=[];for(let o=0;o<s;o++){const a=(o-1+s)%s,r=Math.max(0,t[a]),l=Math.max(0,t[o]),[c,h]=oc(e,a),[d,p]=oc(e,o),u=e[a],_=e[o],m=e[o],g=e[(o+1)%s],f=[_[0]-u[0],_[1]-u[1]],v=[g[0]-m[0],g[1]-m[1]],b=Math.hypot(f[0],f[1])||1,y=Math.hypot(v[0],v[1])||1,w=[f[0]/b,f[1]/b],k=[v[0]/y,v[1]/y],x=[u[0]-c*r,u[1]-h*r],$=[m[0]-d*l,m[1]-p*l];if(!(r>0||l>0)){n.push([e[o][0],e[o][1]]);continue}if(r>0!=l>0){const t=e[o],i=r>0?[t[0]-c*r,t[1]-h*r]:[t[0],t[1]],s=l>0?[t[0]-d*l,t[1]-p*l]:[t[0],t[1]];n.push(i),Math.hypot(s[0]-i[0],s[1]-i[1])>1e-9&&n.push(s);continue}if(ac(w,k)){const t=e[o],i=[t[0]-c*r,t[1]-h*r],s=[t[0]-d*l,t[1]-p*l];n.push(i),Math.hypot(s[0]-i[0],s[1]-i[1])>1e-9&&n.push(s);continue}const S=rc(x,w,$,k),M=Math.max(r,l,1e-9),C=vc(i,e[o])?.limit??4*M;if(S){const i=Math.hypot(S[0]-e[o][0],S[1]-e[o][1]);if(Number.isFinite(i)&&i<=C){n.push(S);continue}if(jc(e,t,o)){n.push([e[o][0],e[o][1]]);continue}}r>0&&n.push([e[o][0]-c*r,e[o][1]-h*r]),l>0&&n.push([e[o][0]-d*l,e[o][1]-p*l])}return n.length>=3?n:null}function Kc(e,t,i,s,n,o,a=1){const r=[];for(const l of e||[]){if(!l?.id)continue;const c=yc(e,l.id,t,i,s,n,o,a);if(!c)continue;const h=Math.abs(Ti(c.poly));for(let e=0;e<c.poly.length;e++){if(!c.kinds[e])continue;const t=c.poly[e],i=c.poly[(e+1)%c.poly.length];r.push({roomId:l.id,a:t,b:i,inward:oc(c.poly,e),cm:c.cms[e],half:c.offsets[e],area:h,key:Rl(t,i,s,a)})}}return{edges:r,adjacencyEps:sc(s,a)}}function Yc(e,t,i,s){const n=e.map(e=>[Math.max(t,e.x0),Math.min(i,e.x1)]).filter(e=>e[1]-e[0]>s).sort((e,t)=>e[0]-t[0]||e[1]-t[1]);if(!n.length)return{coverage:0,full:!1};let o=n[0][0],a=n[0][1],r=0,l=o<=t+s;for(let e=1;e<n.length;e++){const[t,i]=n[e];t<=a+s?a=Math.max(a,i):(r+=a-o,l=!1,o=t,a=i)}return r+=a-o,l=l&&a>=i-s,{coverage:r,full:l}}function Jc(e,t){return Number(t.full)-Number(e.full)||e.faceDistance-t.faceDistance||e.area-t.area||e.roomId.localeCompare(t.roomId)}function Xc(e,t,i=!1){const s=Number(t?.x),n=Number(t?.y),o=Number(t?.angle),a=Number(t?.length);if(!([s,n,o,a].every(Number.isFinite)&&a>0))return{negative:null,positive:null};const r=o*Math.PI/180,l=Math.cos(r),c=Math.sin(r),h=-c,d=l,p=a/2,u=Math.max(1e-9,e.adjacencyEps),_=new Map;let m=0;for(const t of e.edges){if(i&&!(t.half>0))continue;if(!Bl(t.a,t.b,o))continue;const[e,a]=Tl(t.a,t.b);if(Math.abs((s-t.a[0])*a-(n-t.a[1])*e)>u)continue;const r=(t.a[0]-s)*l+(t.a[1]-n)*c,g=(t.b[0]-s)*l+(t.b[1]-n)*c,f=Math.max(-p,Math.min(r,g)),v=Math.min(p,Math.max(r,g));if(v-f<=u)continue;const b=t.inward[0]*h+t.inward[1]*d>=0?1:-1,y=((t.a[0]+t.b[0])/2-s)*h+((t.a[1]+t.b[1])/2-n)*d,w=Math.abs(y+b*t.half),k=`${b}|${t.roomId}`,x={x0:f,x1:v,half:t.half,cm:t.cm,key:t.key,axis:[e,a]},$=_.get(k);$?($.pieces.push(x),$.faceDistance=Math.min($.faceDistance,w)):_.set(k,{roomId:t.roomId,side:b,order:m++,pieces:[x],faceDistance:w,area:t.area,coverage:0,full:!1})}for(const e of _.values()){const t=Yc(e.pieces,-p,p,u);e.coverage=t.coverage,e.full=t.full}const g=e=>{const t=[..._.values()].filter(t=>t.side===e&&t.coverage>u);return t.sort(Jc),t[0]||null};return{negative:g(-1),positive:g(1)}}function Zc(e,t){const i=Xc(e,t),s=[i.negative,i.positive].filter(e=>!!e);if(!s.length)return{ox:0,oy:0,cm:0,side:-1};const n=i.negative&&i.positive?-1:s[0].side,o=t.flip_v?-n:n,a=(-1===o?i.negative:i.positive)||s[0],r=[...a.pieces].sort((e,t)=>(e.x0<=0&&e.x1>=0?0:Math.min(Math.abs(e.x0),Math.abs(e.x1)))-(t.x0<=0&&t.x1>=0?0:Math.min(Math.abs(t.x0),Math.abs(t.x1)))||t.x1-t.x0-(e.x1-e.x0)||e.key.localeCompare(t.key))[0];if(!(r.half>0&&r.cm>0))return{ox:0,oy:0,cm:0,side:o};const l=t.angle*Math.PI/180,c=-Math.sin(l),h=Math.cos(l);return{ox:c*o*r.half,oy:h*o*r.half,cm:r.cm,side:o}}function Qc(e,t){const i=1e-9,s=t.filter(e=>Number.isFinite(e.x0)&&Number.isFinite(e.x1)&&Number.isFinite(e.half)&&e.x1>e.x0&&e.half>0);if(!s.length)return"";const n=s.flatMap(e=>[e.x0,e.x1]).sort((e,t)=>e-t),o=[];for(const e of n){const t=o[o.length-1];(void 0===t||e>t+i)&&o.push(e)}const a=[];for(let e=0;e+1<o.length;e++){const t=o[e],n=o[e+1];if(!(n>t+i))continue;const r=(t+n)/2,l=s.reduce((e,t)=>r>=t.x0-i&&r<=t.x1+i?Math.max(e,t.half):e,0);if(!(l>0))continue;const c=a[a.length-1];c&&t<=c.x1+i&&Math.abs(l-c.half)<=i?c.x1=n:a.push({x0:t,x1:n,half:l})}const r=[];for(const e of a){const t=r[r.length-1],s=t?.[t.length-1];s&&e.x0<=s.x1+i?(e.x0=s.x1,t.push(e)):r.push([e])}return r.map(t=>{const i=t[0],s=t[t.length-1],n=Math.min(.25*Math.min(...t.map(e=>e.half)),.75),o=-e*n,a=[];if(1===e){a.push(`M ${i.x0} ${o} L ${s.x1} ${o}`);for(let e=t.length-1;e>=0;e--){const i=t[e];a.push(`L ${i.x1} ${i.half} L ${i.x0} ${i.half}`)}}else{a.push(`M ${s.x1} ${o} L ${i.x0} ${o}`);for(const e of t)a.push(`L ${e.x0} ${-e.half} L ${e.x1} ${-e.half}`)}return a.push("Z"),a.join(" ")}).join(" ")}function eh(e,t,i,s){if(!s)return i;const n=e.angle*Math.PI/180,o=Math.cos(n),a=Math.sin(n),r=[],l=1e-9;for(const n of i){const[i,c]=n.axis,h=o*i+a*c;if(Math.abs(h)<=l)continue;const d=e.x*i+e.y*c,p=d+h*n.x0,u=d+h*n.x1,_=Math.min(p,u),m=Math.max(p,u),g=`${n.key}|${t}`,f=s.get(g)||[];let v=[[_,m]];for(const[e,t]of f){const i=[];for(const[s,n]of v)t<=s+l||e>=n-l?i.push([s,n]):(e>s+l&&i.push([s,Math.min(n,e)]),t<n-l&&i.push([Math.max(s,t),n]));if(v=i,!v.length)break}for(const[e,t]of v){const i=(e-d)/h,s=(t-d)/h;r.push({...n,x0:Math.min(i,s),x1:Math.max(i,s)})}const b=[...f,[_,m]].sort((e,t)=>e[0]-t[0]||e[1]-t[1]),y=[];for(const e of b){const t=y[y.length-1];t&&e[0]<=t[1]+l?t[1]=Math.max(t[1],e[1]):y.push([e[0],e[1]])}s.set(g,y)}return r}function th(e,t){const i=new Map;return t.map(t=>function(e,t,i){const s=Xc(e,t,!0),n=s.negative,o=s.positive;if(!n&&!o)return null;let a;if(n&&o)a=[{candidate:n,side:-1},{candidate:o,side:1}];else{const e=n||o;a=[{candidate:e,side:-1},{candidate:e,side:1}]}const r=a.map(({candidate:e,side:s})=>({candidate:e,side:s,pieces:eh(t,s,e.pieces,i)})),l=r.map(({candidate:e,side:t,pieces:i})=>({side:t,roomId:e.roomId,d:Qc(t,i)})),c=r.flatMap(({pieces:e})=>e);if(!c.length)return null;const h=Math.max(...c.map(e=>e.half));return{faces:l,minY:-h,maxY:h,wallKey:[...new Set(c.map(e=>e.key))].sort().join("|")}}(e,t,i))}const ih=150,sh=1e-6;function nh(e){return Number.isFinite(e)?Math.max(1,Math.min(ih,e)):1}function oh(e){return((Number.isFinite(Number(e))?Number(e):0)%90+90)%90}const ah=e=>{const t=e.map(e=>[e[0],e[1]]);return!t.length||t[0][0]===t[t.length-1][0]&&t[0][1]===t[t.length-1][1]||t.push([...t[0]]),[t]},rh=(e,t)=>e[0]===t[0]&&e[1]===t[1];function lh(e,t=1e-6){const i=Number.isFinite(t)&&t>0?t:sh,s=[];for(const t of e||[]){if(!Array.isArray(t)||t.length<2)return null;const e=Number(t[0]),n=Number(t[1]);if(!Number.isFinite(e)||!Number.isFinite(n))return null;const o=Math.round(e/i)*i,a=Math.round(n/i)*i;if(!Number.isFinite(o)||!Number.isFinite(a))return null;const r=[Object.is(o,-0)?0:o,Object.is(a,-0)?0:a];s.length&&rh(s[s.length-1],r)||s.push(r)}return s.length>1&&rh(s[0],s[s.length-1])&&s.pop(),s.length<3||new Set(s.map(e=>`${e[0]},${e[1]}`)).size<3?null:Ti(s)>i*i?s:null}function ch(e){const t=[];for(const i of e||[])for(const e of i||[]){const i=(e||[]).filter(e=>Array.isArray(e)&&e.length>=2);i.length<4||t.push(`M ${i.slice(0,-1).map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`)}return t.join(" ")}function hh(e,t,i,s,n){return Kl({a:e,b:t,halfDepth:Ml(i,s,n)/2})}function dh(e,t,i){const s=Number(t)>0?Number(t):5,n=nh(e.cm)/s*i,o=e.center[0],a=e.center[1];if("circle"===e.shape){const e=n/2;return Array.from({length:96},(t,i)=>{const s=i/96*Math.PI*2;return[o+Math.cos(s)*e,a+Math.sin(s)*e]})}const r=n/2,l=oh(e.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l);return[[-r,-r],[r,-r],[r,r],[-r,r]].map(([e,t])=>[o+e*c-t*h,a+e*h+t*c])}function ph(e,t,i=1e-9){if(!t.length)return[e];let s=[ah(e)];try{for(const e of t){const t=e.b[0]-e.a[0],n=e.b[1]-e.a[1],o=Math.hypot(t,n);if(!(o>i))continue;const a=t/o,r=n/o,l=-r,c=a,h=1.25*Math.max(Number(e.depth)||0,4*i),d=Math.max(2*i,1e-9*o),p=[[e.a[0]-a*d-l*h,e.a[1]-r*d-c*h],[e.b[0]+a*d-l*h,e.b[1]+r*d-c*h],[e.b[0]+a*d+l*h,e.b[1]+r*d+c*h],[e.a[0]-a*d+l*h,e.a[1]-r*d+c*h]];s=oi(s,ah(p))}return kh(s)}catch{return[e]}}function uh(e,t){try{const i=oi([[...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]]]]);let s=null,n=0;for(const e of i||[]){const t=e?.[0]||[],i=Math.abs(Ti(t));t.length>=4&&i>n&&(n=i,s=t.slice(0,-1).map(e=>[e[0],e[1]]))}return s}catch{return null}}function _h(e,t,i,s=Math.max(2e-4*i,1e-9),n=[]){const o=[],a=[],r=new Map;for(const e of n){const t=r.get(e.hostId)||[];t.push(e),r.set(e.hostId,t)}const l=[],c=[],h=[];for(const s of e.room_drafts||[])for(let e=0;e+1<s.points.length;e++){const n=Number(s.segments[e]?.cm),a=Ml(Number.isFinite(n)?n:15,t,i)/2;if(!(a>0))continue;const r={a:s.points[e],b:s.points[e+1],halfDepth:a},c=Kl(r);c&&(o.push(r),l.push(c))}const d=[];for(const s of e.partitions||[]){const e={a:s.a,b:s.b,halfDepth:Ml(s.cm,t,i)/2};if(!(e.halfDepth>0))continue;const n=Kl(e);n&&(a.push(e),c.push(n),d.push({id:s.id,body:n}))}const p=[...o,...a];for(const{segmentIndex:e,wedge:t}of Ql(p,s)){const i=e<o.length?{list:l,at:e}:{list:c,at:e-o.length},s=uh(i.list[i.at],t);s&&(i.list[i.at]=s,e>=o.length&&(d[i.at].body=s))}for(const e of d)h.push(...ph(e.body,r.get(e.id)||[],s));const u=(e.wall_columns||[]).map(e=>dh(e,t,i)),_=ec([...o,...a],s).flatMap(e=>ph(e,n,s));return{drafts:l,partitions:c,columns:u,patches:_,all:[...l,...h,..._,...u]}}function mh(e){try{const t=e.map(e=>lh(e)).filter(e=>!!e).map(e=>ah(e));return t.length?si(t[0],...t.slice(1)):null}catch{return null}}const gh=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`;function fh(e){const t=[];for(const i of e||[]){if(yh([i])<=1e-6)continue;const e=[];for(const t of i||[]){const i=(t||[]).filter(e=>Array.isArray(e)&&e.length>=2);i.length<4||e.push(gh(i.slice(0,-1)))}e.length&&t.push(e.join(" "))}return t}function vh(e,t,i={}){const s=mh(e.filter(e=>e.length>=3)),n=t.some(e=>!lh(e))?null:mh(t);if(!s)return[];if(n)try{return fh(ni(s,n))}catch{}return function(e,t,i){let s=null;for(let n=0;n<t.length;n++){const o=mh([t[n]]);if(!o){i.onBoundsFailure?.({boundIndex:n,phase:"bound-union"});continue}let a;try{a=ni(e,o)}catch{i.onBoundsFailure?.({boundIndex:n,phase:"bound-intersection"});continue}if(a?.length&&!(yh(a)<=1e-12))if(s)try{s=si(s,a)}catch{i.onBoundsFailure?.({boundIndex:n,phase:"result-union"})}else s=a}return s?fh(s):[]}(s,t,i)}function bh(e,t){if(!t.length)return[ah(e)];try{const i=mh(t);if(i)return oi(ah(e),i)}catch{}let i=[ah(e)];for(const e of t)if(!(e.length<3))try{i=oi(i,ah(e))}catch{}return i}function yh(e){let t=0;for(const i of e||[])if(i?.length){t+=Ti(i[0]||[]);for(let e=1;e<i.length;e++)t-=Ti(i[e]||[])}return Math.max(0,t)}function wh(e){const t=[];for(const i of e||[])for(const e of i||[])e?.length>=4&&t.push(e.slice(0,-1).map(e=>[e[0],e[1]]));return t}function kh(e){const t=[];for(const i of e||[]){const e=i?.[0];e?.length>=4&&t.push(e.slice(0,-1).map(e=>[e[0],e[1]]))}return t}function xh(e,t){let i=!1;for(let s=0,n=t.length-1;s<t.length;n=s++){const o=t[s][0],a=t[s][1],r=t[n][0],l=t[n][1];a>e[1]!=l>e[1]&&e[0]<(r-o)*(e[1]-a)/(l-a||1e-12)+o&&(i=!i)}return i}function $h(e,t,i){return function(e,t){for(const i of t||[]){const t=i?.[0];if(!t?.length||!xh(e,t))continue;let s=!1;for(let t=1;t<i.length;t++)if(i[t]?.length&&xh(e,i[t])){s=!0;break}if(!s)return!0}return!1}(e,t)||i.some(t=>xh(e,t))}function Sh(e,t,i){if(Math.hypot(e.center[0]-t.center[0],e.center[1]-t.center[1])>i)return!1;if(Math.abs(nh(e.cm)-nh(t.cm))>1e-6)return!1;if(e.shape!==t.shape)return!0;if("circle"===e.shape||"circle"===t.shape)return!0;const s=Math.abs(oh(e.angle)-oh(t.angle));return Math.min(s,90-s)<=1e-6}const Mh=2.54;function Ch(e){const t="number"==typeof e?e:NaN;return!Number.isFinite(t)||t<=0||5===t?1:5/t}function Dh(e,t){return e*Ch(t)}function Ph(e){return e?Mh:1}function Th(e,t){if(!t)return String(e);const i=e/Mh;return String(Math.round(1e6*i)/1e6)}const Ih=1e3;function Rh(e,t){const i=Number(e),s=Number.isFinite(i)&&i>0?i:1,n=s>=1?t:t*s,o=s>=1?t/s:t;return{x:(t-n)/2,y:(t-o)/2,w:n,h:o}}const Ah=.01,zh=100;function Fh(e,t=1e3){const i=Rh(e?.plan_aspect,t),s=Number(e?.plan_scale),n=Number.isFinite(s)&&s>0?Math.min(zh,Math.max(Ah,s)):1,o=Number(e?.plan_scale_x),a=Number(e?.plan_scale_y),r=Number.isFinite(o)&&o>0?Math.min(zh,Math.max(Ah,o)):n,l=Number.isFinite(a)&&a>0?Math.min(zh,Math.max(Ah,a)):n,c=Number(e?.plan_x),h=Number(e?.plan_y),d=qr(e?.plan_angle);return{x:i.x+(Number.isFinite(c)?Gh(c):0)*t,y:i.y+(Number.isFinite(h)?Gh(h):0)*t,w:i.w*r,h:i.h*l,...d?{angle:d}:{}}}function Nh(e){if(null==e.x||null==e.y)return{x:e.x,y:e.y,w:e.w,h:e.h};const t=Number(e.w)||0,i=Number(e.h)||0;return{x:t<0?e.x+t:e.x,y:i<0?e.y+i:e.y,w:Math.abs(t),h:Math.abs(i)}}function Eh(e){return e&&Array.isArray(e.spaces)?e.spaces.map(e=>{const t=Ih,i=function(e){return Array.isArray(e)&&4===e.length&&e.every(e=>Number.isFinite(e))&&e[2]>1e-6&&e[3]>1e-6?e:[0,0,1,1]}(e.view_box);return{id:e.id,title:e.title,cellCm:Number.isFinite(Number(e.cell_cm))&&Number(e.cell_cm)>0?Number(e.cell_cm):5,vb:[i[0]*Ih,i[1]*t,i[2]*Ih,i[3]*t],bg:e.plan_url?{href:As(e.plan_url),...Fh(e,Ih)}:null,rooms:(e.rooms||[]).map(e=>{const i={...e,...Nh(e)};return{id:i.id,name:i.name,area:i.area??null,open_to:i.open_to||void 0,settings:i.settings||void 0,x:null!=i.x?i.x*Ih:void 0,y:null!=i.y?i.y*t:void 0,w:null!=i.w?i.w*Ih:void 0,h:null!=i.h?i.h*t:void 0,poly:i.poly?i.poly.map(e=>[e[0]*Ih,e[1]*t]):void 0,wall_ids:Array.isArray(i.wall_ids)?[...i.wall_ids]:void 0}}),wall_segments:(e.wall_segments||[]).map(e=>({...e,id:String(e.id),a:[Number(e.a?.[0])*Ih,Number(e.a?.[1])*t],b:[Number(e.b?.[0])*Ih,Number(e.b?.[1])*t],cm:Number(e.cm)})),room_drafts:(e.room_drafts||[]).map(e=>({id:e.id,points:(e.points||[]).map(e=>[e[0]*Ih,e[1]*t]),segments:(e.segments||[]).map(e=>({..."string"==typeof e.id&&e.id?{id:e.id}:{},cm:Number(e.cm)}))})),partitions:(e.partitions||[]).map(e=>({id:e.id,a:[e.a[0]*Ih,e.a[1]*t],b:[e.b[0]*Ih,e.b[1]*t],cm:Number(e.cm)})),wall_columns:(e.wall_columns||[]).map(e=>({id:e.id,shape:"circle"===e.shape?"circle":"square",center:[e.center[0]*Ih,e.center[1]*t],cm:Number(e.cm),..."circle"===e.shape?{}:{angle:oh(e.angle)}}))}}):[]}const Oh=5e3,Hh=Oh*Ih,Lh=240,Bh=Ih/Lh,jh=1/Lh;function qh(e){if(!Number.isFinite(e))return e;const t=Math.round(e*Lh/Ih)*Ih/Lh;return Math.abs(t-e)<=1e-9*Bh?e:t}function Wh(e){return{x:qh(e.x),y:qh(e.y)}}function Uh(e){return Number.isFinite(e)?Math.min(Hh,Math.max(-Hh,e)):0}function Gh(e){return Number.isFinite(e)?Math.min(Oh,Math.max(-Oh,e)):0}const Vh=1/3,Kh=200;function Yh(e){let t=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of e){const e=Number(o[0]),a=Number(o[1]);Number.isFinite(e)&&Number.isFinite(a)&&(e<t&&(t=e),a<i&&(i=a),e>s&&(s=e),a>n&&(n=a))}return t>s?null:{minX:t,minY:i,maxX:s,maxY:n}}function Jh(e){return e.poly&&e.poly.length?Yh(e.poly):null==e.x||null==e.y?null:Yh([[e.x,e.y],[e.x+(e.w||0),e.y+(e.h||0)]])}function Xh(e,t){const i=[];for(const t of e.rooms||[]){const e=Jh(t);e&&i.push(e)}if(e.bg){const t=Yh(Yr(e.bg));t&&i.push(t)}for(const e of t||[])if(Array.isArray(e)){const t=Yh([e]);t&&i.push(t)}else i.push(e);return i}const Zh=e=>{if(!e.length)return 0;const t=[...e].sort((e,t)=>e-t),i=t.length>>1;return t.length%2?t[i]:(t[i-1]+t[i])/2},Qh=e=>{let t=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of e)o.minX<t&&(t=o.minX),o.minY<i&&(i=o.minY),o.maxX>s&&(s=o.maxX),o.maxY>n&&(n=o.maxY);return t>s||i>n?null:{x:t,y:i,w:s-t,h:n-i}};function ed(e,t){let{x:i,y:s,w:n,h:o}=e;n<30&&(i=i+n/2-100,n=Kh),o<30&&(s=s+o/2-100,o=Kh);const a=Math.max(n,o)*t;return{x:i-a,y:s-a,w:n+2*a,h:o+2*a}}function td(e,t={}){const i=t.pad??.05,s=t.k??10,n=t.minSpread??50,o=e.filter(e=>Number.isFinite(e.minX)&&Number.isFinite(e.minY)&&Number.isFinite(e.maxX)&&Number.isFinite(e.maxY)&&Math.abs(e.minX)<=Hh&&Math.abs(e.maxX)<=Hh&&Math.abs(e.minY)<=Hh&&Math.abs(e.maxY)<=Hh);if(!o.length)return{core:null,all:null,outliers:0};const a=Qh(o);if(o.length<4){const e=ed(a,i);return{core:e,all:e,outliers:0}}const r=o.map(e=>(e.minX+e.maxX)/2),l=o.map(e=>(e.minY+e.maxY)/2),c=Zh(r),h=Zh(l),d=o.map((e,t)=>Math.max(Math.abs(r[t]-c),Math.abs(l[t]-h))),p=Math.max(((e,t)=>e.length?e[Math.min(e.length-1,Math.max(0,Math.round(t*(e.length-1))))]:0)([...d].sort((e,t)=>e-t),.75),n),u=d.map(e=>e>s*p),_=u.filter(Boolean).length,m=_&&_<=o.length*Vh?o.filter((e,t)=>!u[t]):o;return{core:ed(Qh(m)||a,i),all:ed(a,i),outliers:m===o?0:_}}function id(e,t,i=.05){const s=td(Xh(e,t),{pad:i});if(s.core)return s.core;const n=e.vb&&4===e.vb.length&&e.vb[2]>0&&e.vb[3]>0?e.vb:[0,0,Ih,Ih];return{x:n[0],y:n[1],w:n[2],h:n[3]}}function sd(e){const t=id(e);return{x:t.x+t.w/2,y:t.y+t.h/2}}function nd(e){const t=[];for(const i of e.rooms||[]){const e=Jh(i);e&&t.push(e)}const i=td(t,{pad:0}).core,s=Ih*Ch(e.cellCm??5);return i?Math.max(s,Math.min(Hh,Math.max(i.w,i.h))):s}function od(e,t,i,s=1){const n=Number(i),o=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(n)||n<=0?e*o:e*nd(t)*o/n}const ad=[1,2,5,10,20,50,100,200,500,1e3];function rd(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}}function ld(e,t,i,s){const n=i["rl_"+(e.id||"")];if(n&&n.s===t)return{x:n.x*Ih,y:n.y*Ih};const o=function(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)]}(e);return Wh({x:o[0],y:o[1]})}const cd=["furniture","appliance","sanitary","other"],hd=[{id:"sofa",group:"furniture",w:220,h:90,g:[["r",0,0,1,1],["l",.09,.26,.91,.26],["l",.09,.26,.09,1],["l",.91,.26,.91,1],["l",.5,.26,.5,1]]},{id:"armchair",group:"furniture",w:90,h:85,g:[["r",0,0,1,1],["l",.14,.28,.86,.28],["l",.14,.28,.14,1],["l",.86,.28,.86,1]]},{id:"coffee_table",group:"furniture",w:110,h:60,g:[["r",0,0,1,1],["r",.08,.14,.84,.72]]},{id:"table_dining",group:"furniture",w:140,h:80,g:[["r",0,0,1,1],["r",.06,.11,.88,.78]]},{id:"table_round",group:"furniture",w:120,h:120,g:[["e",.5,.5,.5,.5],["e",.5,.5,.41,.41]]},{id:"chair",group:"furniture",w:45,h:45,g:[["r",0,0,1,.18],["r",.06,.18,.88,.8]]},{id:"desk",group:"furniture",w:120,h:60,g:[["r",0,0,1,1],["r",.63,.07,.31,.86],["l",.63,.5,.94,.5]]},{id:"bed_double",group:"furniture",w:160,h:200,g:[["r",0,0,1,1],["r",0,0,1,.07],["r",.06,.1,.4,.15],["r",.54,.1,.4,.15],["l",0,.33,1,.33]]},{id:"bed_single",group:"furniture",w:90,h:200,g:[["r",0,0,1,1],["r",0,0,1,.07],["r",.15,.1,.7,.15],["l",0,.33,1,.33]]},{id:"nightstand",group:"furniture",w:45,h:40,g:[["r",0,0,1,1],["r",.12,.14,.76,.33],["r",.12,.53,.76,.33]]},{id:"wardrobe",group:"furniture",w:100,h:60,g:[["r",0,0,1,1],["l",0,.72,1,.72],["l",.5,.72,.5,1]]},{id:"bookshelf",group:"furniture",w:80,h:30,g:[["r",0,0,1,1],["l",.34,0,.34,1],["l",.67,0,.67,1]]},{id:"fridge",group:"appliance",w:60,h:65,g:[["r",0,0,1,1],["l",0,.36,1,.36],["l",.83,.44,.83,.64]]},{id:"stove",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["e",.29,.31,.15,.15],["e",.71,.31,.15,.15],["e",.29,.71,.15,.15],["e",.71,.71,.15,.15]]},{id:"dishwasher",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["r",.1,.12,.8,.76],["e",.5,.5,.27,.27],["e",.5,.5,.13,.13]]},{id:"washer",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["l",.08,.17,.92,.17],["e",.5,.57,.3,.3],["e",.5,.57,.14,.14]]},{id:"dryer",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["l",.08,.17,.92,.17],["e",.5,.57,.3,.3],["p",.36,.5,.5,.64,.64,.5]]},{id:"tv",group:"appliance",w:120,h:30,g:[["r",0,0,1,.42],["l",.5,.42,.5,.72],["l",.3,.72,.7,.72]]},{id:"ac",group:"appliance",w:90,h:25,g:[["r",0,0,1,1],["l",.05,.55,.95,.55],["l",.05,.79,.95,.79]]},{id:"water_heater",group:"appliance",w:45,h:45,g:[["e",.5,.5,.5,.5],["e",.5,.5,.31,.31]]},{id:"toilet",group:"sanitary",w:40,h:70,g:[["r",.06,0,.88,.2],["e",.5,.58,.37,.35],["e",.5,.58,.22,.2]]},{id:"bathtub",group:"sanitary",w:170,h:75,g:[["r",0,0,1,1],["r",.05,.11,.77,.78],["e",.89,.5,.045,.1]]},{id:"shower",group:"sanitary",w:90,h:90,g:[["r",0,0,1,1],["l",0,0,1,1],["l",1,0,0,1],["e",.5,.5,.08,.08]]},{id:"sink",group:"sanitary",w:60,h:45,g:[["r",0,0,1,1],["e",.5,.6,.34,.3],["e",.5,.15,.07,.07]]},{id:"kitchen_sink",group:"sanitary",w:80,h:60,g:[["r",0,0,1,1],["r",.06,.24,.44,.64],["r",.54,.24,.4,.64],["e",.5,.12,.06,.06]]},{id:"bidet",group:"sanitary",w:40,h:55,g:[["e",.5,.5,.44,.5],["e",.5,.5,.26,.3]]},{id:"stairs",group:"other",w:100,h:280,g:[["r",0,0,1,1],["l",0,.111,1,.111],["l",0,.222,1,.222],["l",0,.333,1,.333],["l",0,.444,1,.444],["l",0,.556,1,.556],["l",0,.667,1,.667],["l",0,.778,1,.778],["l",0,.889,1,.889],["l",.5,.93,.5,.06],["p",.38,.16,.5,.06,.62,.16]]},{id:"fireplace",group:"other",w:120,h:40,g:[["r",0,0,1,1],["p",.22,1,.22,.42,.78,.42,.78,1]]},{id:"plant",group:"other",w:40,h:40,g:[["e",.5,.5,.22,.22],["l",.5,.28,.5,.02],["l",.5,.72,.5,.98],["l",.28,.5,.02,.5],["l",.72,.5,.98,.5],["l",.34,.34,.13,.13],["l",.66,.66,.87,.87],["l",.66,.34,.87,.13],["l",.34,.66,.13,.87]]},{id:"rug",group:"other",w:200,h:140,g:[["r",0,0,1,1],["r",.06,.09,.88,.82]]}],dd=new Map(hd.map(e=>[e.id,e]));function pd(e){return e&&dd.get(e)||null}function ud(e){return hd.filter(t=>t.group===e)}function _d(e,t,i=Bh,s=1e3){const n=Number(t)>0?Number(t):5;return(Number(e)||0)/n*i/s}const md=5e-4,gd=Oh;function fd(e){return Number.isFinite(e)?Math.max(md,Math.min(gd,e)):md}const vd=e=>{const t=Math.round(1e3*e)/1e3;return Object.is(t,-0)?"0":String(t)};function bd(e,t,i){const s=pd(e);if(!(s&&t>0&&i>0))return"";const n=e=>vd(e*t),o=e=>vd(e*i),a=[];for(const e of s.g)if("r"===e[0]){const[,t,i,s,r]=e;a.push(`M${n(t)} ${o(i)}H${n(t+s)}V${o(i+r)}H${n(t)}Z`)}else if("l"===e[0]){const[,t,i,s,r]=e;a.push(`M${n(t)} ${o(i)}L${n(s)} ${o(r)}`)}else if("e"===e[0]){const[,s,r,l,c]=e;a.push(`M${n(s-l)} ${o(r)}A${vd(l*t)} ${vd(c*i)} 0 0 1 ${n(s+l)} ${o(r)}A${vd(l*t)} ${vd(c*i)} 0 0 1 ${n(s-l)} ${o(r)}Z`)}else{const t=e.slice(1);if(t.length<4)continue;let i=`M${n(t[0])} ${o(t[1])}`;for(let e=2;e+1<t.length;e+=2)i+=`L${n(t[e])} ${o(t[e+1])}`;a.push(i)}return a.join("")}const yd=e=>{let t=(e%360+360)%360;return t>180&&(t-=360),t};function wd(e,t,i,s,n,o=0){let a=null,r=n;for(const n of s){const[s,l,c,h]=n,d=c-s,p=h-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let m=((e-s)*d+(t-l)*p)/u;m=Math.max(0,Math.min(1,m));let g=s+m*d,f=l+m*p;const v=Math.hypot(e-g,t-f);if(!(v<r))continue;r=v;let b=e-g,y=t-f;const w=Math.hypot(b,y);if(w<1e-9?(b=p/_,y=-d/_):(b/=w,y/=w),o>0){const e=Math.round(m*_/o)*o;g=s+e/_*d,f=l+e/_*p}a={cx:g+b*(i/2),cy:f+y*(i/2),angle:yd(180*Math.atan2(-b,y)/Math.PI),dist:v}}return a}const kd=1e-9,xd=e=>`${e[0].toFixed(6)},${e[1].toFixed(6)}`,$d=(e,t)=>Math.hypot(t[0]-e[0],t[1]-e[1]),Sd=(e,t)=>Math.atan2(t[1]-e[1],t[0]-e[0]),Md=e=>Array.isArray(e)&&e.length>=2&&e.every(e=>Number.isFinite(e)),Cd=e=>(e||[]).filter(e=>Md(e?.a)&&Md(e?.b)&&$d(e.a,e.b)>kd);function Dd(e,{minAngleDeg:t=15,maxValence:i=6}={}){const s=new Map;for(const t of Cd(e))for(const[e,i]of[[t.a,t.b],[t.b,t.a]]){const t=s.get(xd(e))||[];t.push(Sd(e,i)),s.set(xd(e),t)}const n=[];for(const[e,o]of s){if(o.length>i&&n.push({rule:"valence",subject:e,actual:o.length,limit:i}),o.length<2)continue;const s=[...o].sort((e,t)=>e-t);let a=1/0;for(let e=0;e<s.length;e++){let t=s[(e+1)%s.length]-s[e];e===s.length-1&&(t+=2*Math.PI);const i=180*t/Math.PI;i>kd&&i<a&&(a=i)}a<t-1e-9&&n.push({rule:"angle",subject:e,actual:a,limit:t})}return n}const Pd=e=>(180*Math.atan2(e.b[1]-e.a[1],e.b[0]-e.a[0])/Math.PI%180+180)%180;function Td(e,t){const i=Cd(t),s=new Map;for(const e of i)for(const t of[e.a,e.b])s.set(xd(t),[...s.get(xd(t))||[],e]);const n=new Set([e]);let o=$d(e.a,e.b);const a=(e,t)=>{const i=(s.get(xd(t))||[]).find(t=>!n.has(t)&&((e,t,i=1)=>{const s=Math.abs(Pd(e)-Pd(t));return Math.min(s,180-s)<=i})(t,e)&&Number(t.cm||0)===Number(e.cm||0));i&&(n.add(i),o+=$d(i.a,i.b),a(i,xd(i.a)===xd(t)?i.b:i.a))};return a(e,e.a),a(e,e.b),o}function Id(e,t,i,{minLengthCm:s=20}={}){const n=[],o=Cd(e);for(const e of o){const a=Td(e,o)/i*(t||1),r=Math.max(s,Number(e.cm)>0?Number(e.cm):0);a<r-1e-9&&n.push({rule:"length",subject:String(e.id||xd(e.a)),actual:a,limit:r})}return n}const Rd=(e,t,i)=>{const s=i[0]-t[0],n=i[1]-t[1],o=s*s+n*n,a=o<=kd?0: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))};function Ad(e,t,i,{minDistanceCm:s=5}={}){const n=Cd(e),o=new Map;for(const e of n)o.set(xd(e.a),e.a),o.set(xd(e.b),e.b);const a=((e,t,i)=>e/(t||1)*i)(s,t,i),r=[],l=[...o.entries()];for(let e=0;e<l.length;e++)for(let n=e+1;n<l.length;n++){const o=$d(l[e][1],l[n][1]);o<a-1e-9&&r.push({rule:"distance",subject:`${l[e][0]} ↔ ${l[n][0]}`,actual:o/i*(t||1),limit:s})}for(const[e,l]of o)for(const o of n){if(xd(o.a)===e||xd(o.b)===e)continue;const n=Rd(l,o.a,o.b);n<=1e-9||n<a-1e-9&&r.push({rule:"distance",subject:`${e} → ${String(o.id||xd(o.a))}`,actual:n/i*(t||1),limit:s})}return r}function zd(e,t,i,s,{minClearanceCm2:n=25}={}){const o=(t||[]).filter(Md),a=o.length<3?0:Math.abs(o.reduce((e,t,i)=>{const s=o[(i+1)%o.length];return e+(t[0]*s[1]-s[0]*t[1])},0))/2,r=(i||1)/s,l=a*r*r;return l<n-1e-9?[{rule:"clearance",subject:e,actual:l,limit:n}]:[]}function Fd(e,t,i){const s=i>0?i:1;return{a:[e[0]/s,e[1]/s],b:[t[0]/s,t[1]/s]}}function Nd(e,t){const i=t>0?t:1;return[e.a[0]*i,e.a[1]*i,e.b[0]*i,e.b[1]*i]}function Ed(e){return Array.isArray(e)&&e.length>=2&&Number.isFinite(Number(e[0]))&&Number.isFinite(Number(e[1]))}function Od(e){if(!Array.isArray(e))return[];const t=[];for(const i of e){if(!i||"object"!=typeof i)continue;const e=i;if(!Ed(e.a)||!Ed(e.b))continue;const s=[Number(e.a[0]),Number(e.a[1])],n=[Number(e.b[0]),Number(e.b[1])];Math.hypot(n[0]-s[0],n[1]-s[1])<.001||t.push({a:s,b:n})}return t}function Hd(e,t,i,s,n=!0){const o=(e||[]).filter(e=>e?.id),a=Od(t);if(a.length)return function(e,t,i,s){const n=Od(e);if(!n.length)return[];const o=function(e,t){const i=[],s=(e||[]).filter(e=>e?.id);for(let e=0;e<s.length;e++){const n=hi(s[e]);if(n)for(let o=e+1;o<s.length;o++){const a=hi(s[o]);if(a)for(const r of Cs(n,a,t))i.push({seg:r,pair:`${s[e].id}:${s[o].id}`,a:s[e],b:s[o]})}}return i}(t,s);if(!o.length)return[];const a=[],r=Math.max(4*s,1e-6);for(const e of n){const t=Nd(e,i),n=t[0],l=t[1],c=t[2]-n,h=t[3]-l,d=Math.hypot(c,h);if(d<r)continue;const p=c/d,u=h/d,_=new Map;for(const{seg:e,pair:t}of o){const i=Math.abs((e[0]-n)*u-(e[1]-l)*p),o=Math.abs((e[2]-n)*u-(e[3]-l)*p);if(i>4*s||o>4*s)continue;const a=(e[0]-n)*p+(e[1]-l)*u,c=(e[2]-n)*p+(e[3]-l)*u,h=Math.max(0,Math.min(a,c)),m=Math.min(d,Math.max(a,c));if(m-h<r)continue;const g=_.get(t)||[];g.push({lo:h,hi:m}),_.set(t,g)}for(const[e,t]of _){t.sort((e,t)=>e.lo-t.lo||e.hi-t.hi);const i=[];for(const e of t){const t=i[i.length-1];t&&e.lo<=t.hi+r?t.hi=Math.max(t.hi,e.hi):i.push({...e})}for(const t of i){const i=[n+p*t.lo,l+u*t.lo],s=[n+p*t.hi,l+u*t.hi];Math.hypot(s[0]-i[0],s[1]-i[1])<r||a.push({pair:e,seg:[i[0],i[1],s[0],s[1]]})}}}const l=[],c=Math.max(4*s,1e-6);for(const{pair:e,seg:t}of a){const i=t[2]-t[0],s=t[3]-t[1],n=Math.hypot(i,s);if(n<r)continue;let o=i/n,a=s/n;(o<-1e-12||Math.abs(o)<=1e-12&&a<0)&&(o=-o,a=-a);let h=l.find(i=>i.pair===e&&Math.abs(i.ux*a-i.uy*o)<=1e-6&&Math.abs((t[0]-i.origin[0])*i.uy-(t[1]-i.origin[1])*i.ux)<=c&&Math.abs((t[2]-i.origin[0])*i.uy-(t[3]-i.origin[1])*i.ux)<=c);h||(h={pair:e,origin:[t[0],t[1]],ux:o,uy:a,ranges:[]},l.push(h));const d=(t[0]-h.origin[0])*h.ux+(t[1]-h.origin[1])*h.uy,p=(t[2]-h.origin[0])*h.ux+(t[3]-h.origin[1])*h.uy;h.ranges.push({lo:Math.min(d,p),hi:Math.max(d,p)})}const h=[];for(const e of l){e.ranges.sort((e,t)=>e.lo-t.lo||e.hi-t.hi);const t=[];for(const i of e.ranges){const e=t[t.length-1];e&&i.lo<=e.hi+r?e.hi=Math.max(e.hi,i.hi):t.push({...i})}for(const s of t){const t=[e.origin[0]+e.ux*s.lo,e.origin[1]+e.uy*s.lo],n=[e.origin[0]+e.ux*s.hi,e.origin[1]+e.uy*s.hi];Math.hypot(n[0]-t[0],n[1]-t[1])>=r&&h.push(Fd(t,n,i))}}return h}(a,e,i,s).map(e=>Nd(e,i));if(!n)return[];const r=[],l=(e,t)=>(e.open_to||[]).includes(t.id)||(t.open_to||[]).includes(e.id);for(let e=0;e<o.length;e++)for(let t=e+1;t<o.length;t++){if(!l(o[e],o[t]))continue;const i=hi(o[e]),n=hi(o[t]);if(i&&n)for(const e of Cs(i,n,s))r.push(e)}return r}const Ld=15e3,Bd=2e3,jd=new WeakMap;function qd(e,t){let i=jd.get(e);if(!i){const t=new Set;i={hiddenAt:"hidden"===e.visibilityState?Date.now():0,token:0,subscribers:t,onVisibility:()=>{const t=jd.get(e);if(!t)return;const i=Date.now();if("hidden"===e.visibilityState){t.hiddenAt||(t.hiddenAt=i);const e={kind:"hidden",token:t.token,at:i,hiddenFor:0,long:!1};for(const i of[...t.subscribers])i(e);return}const s=t.hiddenAt?Math.max(0,i-t.hiddenAt):0;t.hiddenAt=0,t.token++;const n={kind:"visible",token:t.token,at:i,hiddenFor:s,long:s>=Ld};for(const e of[...t.subscribers])e(n)},onPageShow:t=>{const i=jd.get(e);if(!i)return;const s=Date.now();i.token++;const n={kind:"pageshow",token:i.token,at:s,hiddenFor:0,long:!!t.persisted,persisted:!!t.persisted};for(const e of[...i.subscribers])e(n)}},jd.set(e,i),e.addEventListener("visibilitychange",i.onVisibility),e.defaultView?.addEventListener("pageshow",i.onPageShow)}return i.subscribers.add(t),()=>{const i=jd.get(e);i&&(i.subscribers.delete(t),i.subscribers.size||(e.removeEventListener("visibilitychange",i.onVisibility),e.defaultView?.removeEventListener("pageshow",i.onPageShow),jd.delete(e)))}}const Wd={now:()=>Date.now(),setTimeout:(e,t)=>window.setTimeout(e,t),clearTimeout:e=>window.clearTimeout(e),requestAnimationFrame:e=>window.requestAnimationFrame(e),cancelAnimationFrame:e=>window.cancelAnimationFrame(e)};class Ud{constructor(e,t=Wd){this.onChange=e,this.clock=t,this._state="steady",this._token=0,this._frameFingerprint="",this._hasCompleteFrame=!1,this._overlayPhase="none",this._recoveryReason=null,this._overlayTimer=0,this._overlayRaf=0,this._overlayOpaqueAt=0,this._barrierRafs=new Set,this._trace=[],this._disposed=!1}get state(){return this._state}get token(){return this._token}get frameFingerprint(){return this._frameFingerprint}get hasCompleteFrame(){return this._hasCompleteFrame}get overlayPhase(){return this._overlayPhase}get overlayVisible(){return"none"!==this._overlayPhase}get overlayBlocksInteraction(){return this.overlayVisible}get recoveryReason(){return this._recoveryReason}get trace(){return this._trace.map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}note(e,t={}){this.record(e,void 0,t)}record(e,t,i={}){this._trace.push({at:this.clock.now(),token:this._token,event:e,state:this._state,...t?{reason:t}:{},...i}),this._trace.length>80&&this._trace.splice(0,this._trace.length-80)}changed(){this._disposed||this.onChange()}adoptCompleteFrame(e){e&&(this._hasCompleteFrame=!0,this._frameFingerprint=e,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-adopted"),this.changed())}markCompleteFrame(e){e&&(this._hasCompleteFrame=!0,this._frameFingerprint=e,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-complete"),this.changed())}refreshCompleteFrame(e){!this._disposed&&this._hasCompleteFrame&&"steady"===this._state&&e&&e!==this._frameFingerprint&&(this._frameFingerprint=e,this.record("frame-refreshed"))}visibility(e){return"hidden"===e.kind?(this.record("visibility-hidden"),this._token):e.long||"visible"!==e.kind&&"pageshow"!==e.kind?this.beginCandidate("pageshow"===e.kind?"pageshow":"long-resume"):(this.record("pageshow"===e.kind?"pageshow-noop":"visibility-visible-quick"),this._token)}beginCandidate(e,t="plan"){return this._token++,this._recoveryReason=t,this._hasCompleteFrame?(this._state="connection"===t?"offline-stale":"holding",this.clearOverlay()):this.overlayVisible?this._state="overlay-visible":(this._state="overlay-pending",this.scheduleOverlay(t)),this.record("candidate-start",e),this.changed(),this._token}connectionLost(){return this.beginCandidate("connection-lost","connection")}candidateReady(e){return!this._disposed&&e===this._token&&(this._state="candidate-ready",this.record("candidate-ready"),this.changed(),!0)}async commitAfterPaint(e,t){if(this._disposed||e!==this._token)return!1;let i=!0;const s=(async()=>{for(await t.updateComplete();i&&!this._disposed&&e===this._token;)if(t.stageValid()&&t.assetsReady()){if(await this.nextFrame(),e!==this._token||!t.stageValid()||!t.assetsReady())continue;if(await this.nextFrame(),e===this._token&&t.stageValid()&&t.assetsReady())return!0}else await this.nextFrame();return!1})();let n=0;const o=new Promise(e=>{n=this.clock.setTimeout(()=>e({ready:!1,timedOut:!0}),Bd)}),a=await Promise.race([s.then(e=>({ready:e,timedOut:!1})),o]);return i=!1,this.clock.clearTimeout(n),!this._disposed&&e===this._token&&(!(!a.ready&&!a.timedOut)&&(a.ready?(this._hasCompleteFrame=!0,this._frameFingerprint=t.frameFingerprint(),this.record("paint-barrier"),this.finishOverlayAfterCommit(),!0):(this.record("paint-barrier-timeout"),this._hasCompleteFrame?(this._state="connection"===this._recoveryReason?"offline-stale":"steady",this.clearOverlay()):(this._state="recovery-error","opaque"!==this._overlayPhase&&this.showOverlayNow()),this.changed(),!1)))}retry(e=this._recoveryReason||"plan"){return this.beginCandidate("retry",e)}nextFrame(){return new Promise(e=>{const t=this.clock.requestAnimationFrame(()=>{this._barrierRafs.delete(t),e()});this._barrierRafs.add(t)})}scheduleOverlay(e){this.clearOverlayTimer(),this._recoveryReason=e,this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._hasCompleteFrame||"overlay-pending"!==this._state&&"candidate-ready"!==this._state||(this._state="overlay-visible",this._overlayPhase="entering",this.record("overlay-enter"),this.changed(),this._overlayRaf=this.clock.requestAnimationFrame(()=>{this._overlayRaf=0,"entering"===this._overlayPhase&&(this._overlayPhase="fading-in",this.changed(),this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,"fading-in"===this._overlayPhase&&(this._overlayPhase="opaque",this._overlayOpaqueAt=this.clock.now(),this.record("overlay-opaque"),this.changed())},150))}))},150)}showOverlayNow(){this.clearOverlayTimer(),this._overlayPhase="opaque",this._overlayOpaqueAt=this.clock.now(),this.record("overlay-error")}finishOverlayAfterCommit(){if(this.clearOverlayTimer(),"none"===this._overlayPhase||"entering"===this._overlayPhase||"fading-in"===this._overlayPhase)return this.clearOverlay(),this._state="steady",this._recoveryReason=null,this.record("candidate-committed"),void this.changed();const e=Math.max(0,this._overlayOpaqueAt+250-this.clock.now());this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._overlayPhase="leaving",this.record("overlay-leave"),this.changed(),this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._overlayPhase="none",this._state="steady",this._recoveryReason=null,this.record("candidate-committed"),this.changed()},180)},e)}clearOverlayTimer(){this._overlayTimer&&this.clock.clearTimeout(this._overlayTimer),this._overlayTimer=0}clearOverlay(){this.clearOverlayTimer(),this._overlayRaf&&this.clock.cancelAnimationFrame(this._overlayRaf),this._overlayRaf=0,this._overlayPhase="none",this._overlayOpaqueAt=0}dispose(){this._disposed=!0,this._token++,this.clearOverlay();for(const e of this._barrierRafs)this.clock.cancelAnimationFrame(e);this._barrierRafs.clear()}}function Gd(e){let t=2166136261,i=2654435769;const s=new WeakSet,n=e=>{for(let s=0;s<e.length;s++){const n=e.charCodeAt(s);t^=n,t=Math.imul(t,16777619),i^=n+(i<<6>>>0)+(i>>>2)}},o=e=>{if(null===e)return void n("n");const t=typeof e;if("string"===t)return void n(`s${e.length}:${e}`);if("number"===t)return void n(`d${Number.isNaN(e)?"NaN":String(e)}`);if("boolean"===t)return void n(e?"t":"f");if("undefined"===t)return void n("u");if("object"!==t)return void n(`${t}:${String(e)}`);const i=e;if(s.has(i))n("[cycle]");else{if(s.add(i),Array.isArray(e)){n("[");for(const t of e)o(t);return n("]"),void s.delete(i)}n("{");for(const t of Object.keys(e).sort())n(t),o(e[t]);n("}"),s.delete(i)}};return o(e),`${(t>>>0).toString(16).padStart(8,"0")}${(i>>>0).toString(16).padStart(8,"0")}`}function Vd(e){return Gd(e)}const Kd=new WeakMap;class Yd{constructor(e,t=()=>Date.now()){this.onUpdate=e,this.now=t,this.fallbackAuthority={},this.referenceOwner={},this.referenced=new Set,this.queued=new Set,this.disposed=!1,this.sharedUpdate=()=>{this.disposed||this.onUpdate()}}bind(e){const t=((e,t)=>{const i=e?.connection||e?.callWS||t;return!i||"object"!=typeof i&&"function"!=typeof i?t:i})(e,this.fallbackAuthority);return this.shared&&this.authority===t||(this.shared&&(this.shared.listeners.delete(this.sharedUpdate),this.shared.references.delete(this.referenceOwner)),this.authority=t,this.shared=(e=>{let t=Kd.get(e);return t||(t={signed:{},queued:new Set,inFlight:new Map,retry:new Map,listeners:new Set,settlers:new Set,references:new Map},Kd.set(e,t)),t})(t),this.disposed||(this.shared.listeners.add(this.sharedUpdate),this.shared.references.set(this.referenceOwner,new Set(this.referenced)))),this.shared}start(e,t){this.disposed=!1,this.referenced=new Set(t());const i=this.bind(e());i.listeners.add(this.sharedUpdate),i.references.set(this.referenceOwner,new Set(this.referenced)),this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(e(),t()),288e5)}dispose(){if(this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.shared){this.shared.listeners.delete(this.sharedUpdate),this.shared.references.delete(this.referenceOwner);for(const e of this.queued)this.shared.queued.delete(e)}this.queued.clear()}invalidate(e){const t=this.bind(e);clearTimeout(this.batchTimer),this.queued.clear(),t.queued.clear(),t.inFlight.clear(),t.retry.clear(),t.signed={}}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(e,t){const i=As(t);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.bind(e);this.referenced.add(i),s.references.set(this.referenceOwner,new Set(this.referenced));const n=s.signed[i],o=n?this.now()-n.at:1/0;return o<Is?n.url:o<Ts?(n.pending||this.request(e,i),n.url):(n&&delete s.signed[i],this.request(e,i),"")}request(e,t){if(!e?.callWS||this.queued.has(t))return;const i=this.bind(e),s=this.now(),n=i.inFlight.get(t);if(void 0!==n&&s-n<15e3)return;if(i.queued.has(t))return;const o=i.retry.get(t);o&&s<o.notBefore||(this.queued.add(t),i.queued.add(t),clearTimeout(this.batchTimer),this.batchTimer=setTimeout(()=>{const t=[...this.queued];this.queued.clear();for(const e of t)i.queued.delete(e);this.sign(e,t)},30))}sign(e,t){if(!t.length||!e?.callWS)return;const i=this.bind(e);for(const s of function(e,t){const i=Math.max(1,Math.floor(t)),s=[];for(let t=0;t<e.length;t+=i)s.push(e.slice(t,t+i));return s}(t,200)){const t=this.now(),n=s.filter(e=>{const s=i.inFlight.get(e);return!(void 0!==s&&t-s<15e3)&&(i.inFlight.set(e,t),!0)});n.length&&e.callWS({type:"houseplan/content/sign",paths:n}).then(e=>{const t=this.now();let s=0,o=!1;for(const a of n){const n=e?.urls?.[a];if("string"==typeof n&&n){const e=i.signed[a];e?.loaded&&e.url!==n?(e.pending={url:n,at:t},this.preloadReplacement(i,a,n,t)):(i.signed[a]={url:n,at:t,loaded:e?.url===n&&!!e.loaded},o=!0),i.retry.delete(a),s++}else this.backOff(i,a)}if(s&&(this.trimShared(i),o))for(const e of[...i.listeners])e()}).catch(()=>{for(const e of n)this.backOff(i,e)}).finally(()=>{for(const e of n)i.inFlight.get(e)===t&&i.inFlight.delete(e);for(const e of[...i.settlers])e()})}}preloadReplacement(e,t,i,s){const n=()=>{const n=e.signed[t];if(n?.pending?.url===i){e.signed[t]={url:i,at:s,loaded:!0},e.retry.delete(t),this.trimShared(e);for(const t of[...e.listeners])t()}},o=()=>{const s=e.signed[t];s?.pending?.url===i&&(delete s.pending,this.backOff(e,t))};if("undefined"==typeof Image)return void n();const a=new Image;a.onload=()=>{("function"==typeof a.decode?a.decode():Promise.resolve()).then(n).catch(o)},a.onerror=o,a.src=i}preloadCurrentImage(e,t,i){if(i.loaded)return Promise.resolve(!0);if(i.preload)return i.preload;if("undefined"==typeof Image)return i.loaded=!0,Promise.resolve(!0);const s=i.url;return i.preload=new Promise(n=>{const o=new Image;let a=!1;const r=o=>{if(a)return;a=!0,clearTimeout(l);const r=e.signed[t];if(r===i&&delete r.preload,o&&r===i&&r.url===s){r.loaded=!0;for(const t of[...e.listeners])t();n(!0)}else o||r!==i||this.backOff(e,t),n(!1)};o.onload=()=>{("function"==typeof o.decode?o.decode():Promise.resolve()).then(()=>r(!0)).catch(()=>r(!1))},o.onerror=()=>r(!1);const l=setTimeout(()=>r(!1),Bd);o.src=s}),i.preload}prepareImage(e,t){const i=As(t);if(!i.startsWith("/api/houseplan/content/"))return Promise.resolve(!0);const s=this.bind(e),n=()=>{const t=s.signed[i];if(t&&this.now()-t.at<Ts)return this.preloadCurrentImage(s,i,t);this.display(e,i);const n=s.retry.get(i);return n&&n.notBefore>this.now()&&!s.inFlight.has(i)&&!s.queued.has(i)?Promise.resolve(!1):null},o=n();return o?(a=o,new Promise(e=>{let t=!1;const i=i=>{t||(t=!0,clearTimeout(s),e(i))},s=setTimeout(()=>i(!1),Bd);a.then(i).catch(()=>i(!1))})):new Promise(e=>{let t=!1,i=null;const o=i=>{t||(t=!0,clearTimeout(r),s.settlers.delete(a),e(i))},a=()=>{t||i||(i=n(),i&&i.then(o).catch(()=>o(!1)))},r=setTimeout(()=>o(!1),Bd);s.settlers.add(a),a()});var a}backOff(e,t){const i=e.retry.get(t)?.delay||0,s=Math.min(6e4,i?2*i:2e3);e.retry.set(t,{notBefore:this.now()+s,delay:s})}trimShared(e){const t=Object.entries(e.signed);if(t.length<=512)return;const i=new Set(e.inFlight.keys());for(const t of e.references.values())for(const e of t)i.add(e);for(const[e,s]of t)(s.pending||s.preload)&&i.add(e);t.sort((e,t)=>t[1].at-e[1].at);const s=t.filter(([e])=>i.has(e));for(const e of t){if(s.length>=512)break;i.has(e[0])||s.push(e)}e.signed=Object.fromEntries(s)}resign(e,t){const i=this.bind(e);this.referenced=new Set(t),i.references.set(this.referenceOwner,new Set(this.referenced));const s=this.now(),n=new Set;for(const e of i.references.values())for(const t of e)n.add(t);const o={};for(const[e,t]of Object.entries(i.signed))n.has(e)&&s-t.at<Ts&&(o[e]=t);i.signed=o,i.retry.clear(),this.sign(e,[...t].filter(e=>!!o[e]&&!o[e].pending))}markLoaded(e,t,i){const s=As(t);if(!s.startsWith("/api/houseplan/content/"))return;const n=this.bind(e).signed[s];if(n&&(!i||n.url===i)&&!n.loaded){n.loaded=!0;for(const t of[...this.bind(e).listeners])t()}}isReady(e,t){const i=As(t);if(!i.startsWith("/api/houseplan/content/"))return!0;const s=this.bind(e).signed[i];return!!s&&this.now()-s.at<Ts&&s.loaded}get entries(){const e={};for(const[t,i]of Object.entries(this.shared?.signed||{}))e[t]={url:i.url,at:i.at};return e}get inFlightUrls(){return[...this.shared?.inFlight.keys()||[]]}}function Jd(e,t){if(t)return e.find(e=>e.id===t)}function Xd(e="file"){const t="draw"===e;return{source:e,showBorders:t,showNames:t,displayTouched:!1}}function Zd(e,t){if(e.displayTouched)return{...e,source:t};const i="draw"===t;return{...e,source:t,showBorders:i,showNames:i}}function Qd(e,t,i){return{...e,[t]:i,displayTouched:!0}}function ep(e,t,i){return[e[0]*t+e[1]*i+e[2],e[3]*t+e[4]*i+e[5]]}function tp(e,t){let i=0;for(const[s,n]of t){const t=ep(e,s[0],s[1]);i=Math.max(i,Math.hypot(t[0]-n[0],t[1]-n[1]))}return i}const ip=e=>{if(null==e||""===e)return null;const t=Number(e);return Number.isFinite(t)?t:null},sp=e=>{const t=e,i=ip(Array.isArray(t)?t[0]:t?.x),s=ip(Array.isArray(t)?t[1]:t?.y);return null==i||null==s?null:[i,s]};function np(e){if(!Array.isArray(e))return null;const t=[];for(const i of e){const e=sp(i);if(!e)return null;t.push(e)}if(t.length>1){const e=t[0],i=t[t.length-1];e[0]===i[0]&&e[1]===i[1]&&t.pop()}if(t.length<3)return null;let i=0,s=0,n=0;for(let e=0;e<t.length;e++){const o=t[e],a=t[(e+1)%t.length],r=o[0]*a[1]-a[0]*o[1];i+=r,s+=(o[0]+a[0])*r,n+=(o[1]+a[1])*r}return Math.abs(i)<1e-12?[t.reduce((e,t)=>e+t[0],0)/t.length,t.reduce((e,t)=>e+t[1],0)/t.length]:[s/(3*i),n/(3*i)]}function op(e){if(!Array.isArray(e))return null;const t=[];for(const i of e){const e=sp(i);if(!e)return null;t.push(e)}if(t.length>1){const e=t[0],i=t[t.length-1];e[0]===i[0]&&e[1]===i[1]&&t.pop()}const i=np(t);if(!i)return null;const s=t.map(e=>e[0]),n=t.map(e=>e[1]);return{centroid:i,bbox:[Math.min(...s),Math.min(...n),Math.max(...s),Math.max(...n)]}}function ap(e){let t=e;if(t&&"object"==typeof t&&!Array.isArray(t)){const e=t;t=e.path??e.points??t}if(!Array.isArray(t)||!t.length)return[];let i=(t.some(e=>(e=>Array.isArray(e)?e.length>=2&&!Array.isArray(e[0])&&("object"!=typeof e[0]||null==e[0]):!!e&&"object"==typeof e&&("x"in e||"y"in e))(e))?[t]:t.filter(Array.isArray)).map(e=>e.map(sp).filter(e=>null!=e)).filter(e=>e.length>=2);i.length>64&&(i=i.slice(-64));const s=i.reduce((e,t)=>e+t.length,0);if(s<=4e3)return i.map(e=>e.slice());const n=4e3-2*i.length,o=i.map(e=>e.length-2),a=o.reduce((e,t)=>e+t,0),r=o.map(e=>n*e/a),l=r.map(Math.floor);let c=n-l.reduce((e,t)=>e+t,0);const h=r.map((e,t)=>({index:t,fraction:e-Math.floor(e)})).sort((e,t)=>t.fraction-e.fraction||e.index-t.index);for(let e=0;e<c;e++)l[h[e].index]++;return i.map((e,t)=>function(e,t){if(t>=e.length)return e.slice();if(t<=2)return[e[0],e[e.length-1]];const i=[];for(let s=0;s<t;s++){const n=Math.round(s*(e.length-1)/(t-1));i.push(e[n])}return i}(e,2+l[t]))}function rp(e,t,i){const s=ap(e&&"object"==typeof e&&!Array.isArray(e)&&"path"in e?e.path:e);if(s.length)return{path:s,source:"integration"};const n=ap(t&&"object"==typeof t&&!Array.isArray(t)&&"points"in t?t.points:t);if(n.length)return{path:n,source:"server"};const o=ap(i);return o.length?{path:o,source:"local"}:{path:[],source:"none"}}function lp(e){const t=e.map(e=>e.slice()),i=t[t.length-1];return i?(i.length>2?i.pop():t.pop(),t):[]}function cp(e){if(!e)return null;const t=e.vacuum_position||e.robot_position||null,i=t&&null!=ip(t.x)&&null!=ip(t.y)?{x:ip(t.x),y:ip(t.y),a:ip(t.a??t.angle??t.theta)}:null,s=ap(e.path?.path??e.path?.points??e.path),n=[],o=e.rooms,a=Array.isArray(o)?o.map((e,t)=>[String(e?.id??t),e]):o&&"object"==typeof o?Object.entries(o):[];for(const[e,t]of a){if(!t||"object"!=typeof t)continue;const i=String(t.name??t.label??"").trim(),s=[[ip(t.cx),ip(t.cy)],[ip(t.center?.x),ip(t.center?.y)],[ip(t.x),ip(t.y)]].find(([e,t])=>null!=e&&null!=t);let o=s?.[0]??null,a=s?.[1]??null;const r=op(t.outline),l=ip(t.x0),c=ip(t.y0),h=ip(t.x1),d=ip(t.y1),p=(null!=l&&null!=c&&null!=h&&null!=d?[Math.min(l,h),Math.min(c,d),Math.max(l,h),Math.max(c,d)]:null)||r?.bbox||null;if(null!=o&&null!=a||!r||([o,a]=r.centroid),null!=o&&null!=a||!p||(o=(p[0]+p[2])/2,a=(p[1]+p[3])/2),i&&null!=o&&null!=a){const t={id:e,name:i,cx:o,cy:a};p&&([t.x0,t.y0,t.x1,t.y1]=p),n.push(t)}}const r=function(e){return String(e.map_name??e.current_map??e.map_index??e.selected_map??"default")}(e);return i||n.length||s.length?{pos:i,path:s,rooms:n,mapId:r}:null}function hp(e,t,i){if(!e||!t)return null;const s=t.attributes||{},n=e.split(".")[0]||"",o=null!=i?.platform?String(i.platform):null,a=!!sp(s.vacuum_position??s.robot_position),r=!(!s.rooms||"object"!=typeof s.rooms),l=ap(s.path?.path??s.path?.points??s.path).length>0,c=[s.map_name,s.current_map,s.map_index,s.selected_map].some(e=>null!=e),h="xiaomi_cloud_map_extractor"===o,d=r||l||c;let p=null;if(a?p="compatible":h?p="known_xcme_incomplete":d?p="partial":"camera"===n&&(p="camera"),!p)return null;const u=a?"camera"===n?300:200:h?100:d?50:0;return{entityId:e,name:String(s.friendly_name||e),platform:o,category:p,hasPosition:a,hasRooms:r,hasPath:l,hasMapId:c,score:u}}const dp=e=>e.toLowerCase().replace(/[\s_\-.,]+/g,"");function pp(e,t){const i=new Map(t.map(e=>[dp(e.name),e])),s=[],n=[];for(const t of e){const e=i.get(dp(t.name));e&&(s.push([[t.cx,t.cy],[e.cx,e.cy]]),n.push(t.name))}if(s.length<3)return null;const o=function(e){if(e.length<3)return null;let t=0,i=0,s=0,n=0,o=0,a=0,r=0,l=0,c=0,h=0,d=0,p=0;for(const[[u,_],[m,g]]of e){if(![u,_,m,g].every(Number.isFinite))return null;t+=u*u,i+=u*_,s+=u,n+=_*_,o+=_,a+=1,r+=u*m,l+=_*m,c+=m,h+=u*g,d+=_*g,p+=g}const u=[t,i,s,i,n,o,s,o,a],_=e=>{const[t,i,s,n,o,a,r,l,c]=u,h=t*(o*c-a*l)-i*(n*c-a*r)+s*(n*l-o*r);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(o*c-a*l)/h,(s*l-i*c)/h,(i*a-s*o)/h,(a*r-n*c)/h,(t*c-s*r)/h,(s*n-t*a)/h,(n*l-o*r)/h,(i*r-t*l)/h,(t*o-i*n)/h];return[d[0]*e[0]+d[1]*e[1]+d[2]*e[2],d[3]*e[0]+d[4]*e[1]+d[5]*e[2],d[6]*e[0]+d[7]*e[1]+d[8]*e[2]]},m=_([r,l,c]),g=_([h,d,p]);if(!m||!g)return null;const f=[m[0],m[1],m[2],g[0],g[1],g[2]];return f.every(Number.isFinite)?f:null}(s);return o?{matrix:o,matched:n,residual:tp(o,s)}:null}function up(e,t,i){const s=e[e.length-1];if(s&&s[0]===t[0]&&s[1]===t[1])return e;if(e.push(t),e.length<=600)return e;let n=function(e,t){if(e.length<3)return e.slice();const i=new Uint8Array(e.length);i[0]=i[e.length-1]=1;const s=[[0,e.length-1]];for(;s.length;){const[n,o]=s.pop(),[a,r]=e[n],[l,c]=e[o],h=l-a,d=c-r,p=Math.hypot(h,d)||1e-9;let u=0,_=-1;for(let t=n+1;t<o;t++){const i=Math.abs((e[t][0]-a)*d-(e[t][1]-r)*h)/p;i>u&&(u=i,_=t)}_>0&&u>t&&(i[_]=1,s.push([n,_],[_,o]))}const n=[];for(let t=0;t<e.length;t++)i[t]&&n.push(e[t]);return n}(e,i);return n.length>600&&(n=n.filter((e,t)=>t%2==0||t===n.length-1)),n}function _p(e){return"cleaning"===e||"returning"===e||"on"===e}const mp={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function gp(e){const[t,i]=mp[e.rot]||[1,0],s=e.mir?-1:1;return[e.s*t*s,-e.s*i,e.ox,e.s*i*s,e.s*t,e.oy]}function fp(e){const t=e[0]*e[4]-e[1]*e[3];if(!Number.isFinite(t)||Math.abs(t)<1e-12)return null;const i=t<0,s=Math.sqrt(Math.abs(t));let n=180*Math.atan2(-e[1],e[4])/Math.PI;return n=(90*Math.round(n/90)%360+360)%360,{ox:e[2],oy:e[5],s:s,rot:n,mir:i}}function vp(e,t,i,s){const[n,o]=ep(gp(t),i,s),a=gp({...e,ox:0,oy:0}),[r,l]=ep(a,i,s);return{...e,ox:n-r,oy:o-l}}function bp(e){const t=e?.trail_mode;return"never"===t||"cleaning"===t||"always"===t?t:!1===e?.trail?"never":"cleaning"}const yp=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);function wp(e){const{hass:t,devices:i,markers:s,showEntities:n,currentBinding:o,currentDeviceId:a,labels:r}=e,l=wo(s),c=new Set(s.filter(e=>e.removed).map(e=>e.binding)),h=new Set;for(const e of i)e.id!==a&&("device"===e.bindingKind&&e.bindingRef&&h.add(`device:${e.bindingRef}`),"entity"===e.bindingKind&&e.bindingRef&&h.add(`entity:${e.bindingRef}`));const d=new Set;for(const e of i)"device"===e.bindingKind&&e.name&&d.add(`${e.name.trim()}|${e.area||""}`);const p=[];for(const e of Object.values(t?.devices||{})){if(!e||"service"===e.entry_type)continue;const t=`device:${e.id}`;if(h.has(t))continue;const i=String(e.name_by_user||e.name||e.id).trim();t!==o&&!c.has(t)&&d.has(`${i}|${e.area_id||""}`)||p.push({value:t,label:i,sub:(e.model||r.device)+("Group"===e.model?r.z2mGroup:""),kind:"device",ref:e.id,areaId:e.area_id||"",model:e.model||""})}for(const[e,i]of Object.entries(t?.entities||{})){const s=`entity:${e}`;if(h.has(s))continue;if(ko(t,e,l)&&!c.has(s))continue;const n=yp.has(i?.platform),o="group"===i?.platform;if(!n&&!o)continue;if(i?.hidden&&!c.has(s))continue;const a=t?.states?.[e];p.push({value:s,label:i?.name||a?.attributes?.friendly_name||e,sub:`${e.split(".")[0]} · ${o?r.group:r.helper}`,kind:"entity",ref:e,areaId:i?.area_id||i?.device_id&&t?.devices?.[i.device_id]?.area_id||"",model:"",parentDeviceId:i?.device_id||void 0})}if(n){const e=new Set(p.map(e=>e.value));for(const[i,s]of Object.entries(t?.entities||{})){const n=`entity:${i}`;if(h.has(n)||e.has(n)||s?.hidden&&!c.has(n))continue;const o=!!s?.device_id&&l.devices.has(s.device_id);if(ko(t,i,l)&&!c.has(n)&&!o)continue;const a=t?.states?.[i],d=s?.device_id?t?.devices?.[s.device_id]:null,u=d?String(d.name_by_user||d.name||""):"";p.push({value:n,label:s?.name||a?.attributes?.friendly_name||i,sub:`${i.split(".")[0]} · ${r.entity}${u?` · ${u}`:""}`,kind:"entity",ref:i,areaId:s?.area_id||d?.area_id||"",model:"",parentDeviceId:s?.device_id||void 0})}}return p.sort((e,t)=>e.label.localeCompare(t.label)||e.value.localeCompare(t.value))}const kp={kind:"active",enabledEntityIds:[],allEntityIds:[]};function xp(e){return e.startsWith("entity:")?"entity":"device"}function $p(e){const t=e.binding.indexOf(":"),i=t>=0?e.binding.slice(t+1):"",s=Object.keys(e).filter(e=>!["id","binding","hidden"].includes(e));return e.id===`h${i}`&&0===s.length}const Sp={offStates:["off"],unknownUsesToggle:!0},Mp={light:Sp,switch:Sp,fan:Sp,humidifier:Sp,input_boolean:Sp,automation:Sp,remote:Sp,group:Sp,climate:{...Sp,featureMasks:{turn_on:256,turn_off:128,toggle:384}},media_player:{...Sp,featureMasks:{turn_on:128,turn_off:256,toggle:384}},siren:{...Sp,featureMasks:{turn_on:1,turn_off:2,toggle:3}},vacuum:{...Sp,featureMasks:{turn_on:1,turn_off:2,toggle:3}},water_heater:{...Sp,featureMasks:{turn_on:8,turn_off:8,toggle:8}},camera:{...Sp,featureMasks:{turn_on:1,turn_off:1,toggle:1}}};function Cp(e){return e.slice(0,e.indexOf("."))}function Dp(e,t,i){if(!e?.services||"object"!=typeof e.services)return!1;const s=e.services?.[t];return!!s&&Object.prototype.hasOwnProperty.call(s,i)}function Pp(e,t,i){return Dp(e,t,i)?{domain:t,service:i}:Dp(e,"homeassistant",i)?{domain:"homeassistant",service:i}:null}function Tp(e,t){return e?.entities?.[t]||null}function Ip(e,t,i){const s=e?.states?.[i],n=Tp(t,i);return s?.attributes?.friendly_name||n?.name||n?.original_name||i}function Rp(e,t){const i=Tp(e,t);if(null!=i?.disabled_by)return!0;const s=i?.device_id?e?.devices?.[i.device_id]:null;return null!=s?.disabled_by}function Ap(e,t){const i=e?.attributes?.supported_features;if(null==i||""===i)return!1;const s=Number(i);return Number.isFinite(s)&&(s&t)===t}function zp(e,t,i){const s=Cp(i);if("lock"===s||"alarm_control_panel"===s)return!0;if("cover"!==s)return!1;const n=e?.states?.[i],o=Tp(t,i),a=String(n?.attributes?.device_class||o?.device_class||o?.original_device_class||"");return Ui.has(a)}function Fp(e,t,i,s,n){return{ref:i,entityId:s,name:s?Ip(e,t,s):null,reason:n}}function Np(e,t,i,s,n,o=null){return{target:null,skipped:Fp(e,t,i,s,n),semantics:o,nextEffect:null,command:null}}function Ep(e,t,i,s,n=i){const o=Cp(i);if("cover"!==o&&"valve"!==o)return function(e,t,i,s,n=i){const o=Cp(i),a=e?.states?.[i];if(Rp(t,i))return Np(e,t,n,i,"ha-disabled","power");if(zp(e,t,i))return Np(e,t,n,i,"secure");if(!a)return Np(e,t,n,i,"missing","power");if("unavailable"===a.state)return Np(e,t,n,i,"unavailable","power");const r=Mp[o];if(!r)return Np(e,t,n,i,"unsupported");let l,c;if("unknown"===a.state||""===a.state){if(!r.unknownUsesToggle)return Np(e,t,n,i,"unsupported","power");l="toggle",c="toggle"}else r.offStates.includes(String(a.state))?(l="turn-on",c="turn_on"):(l="turn-off",c="turn_off");const h=r.featureMasks?.[c];if(h&&!Ap(a,h))return Np(e,t,n,i,"unsupported","power");const d=Pp(e,o,c);return d?{target:{entityId:i,name:Ip(e,t,i),state:String(a.state||""),via:s},skipped:null,semantics:"power",nextEffect:l,command:{...d,data:{entity_id:i}}}:Np(e,t,n,i,"unsupported","power")}(e,t,i,s,n);if(Rp(t,i))return Np(e,t,n,i,"ha-disabled",o);if(zp(e,t,i))return Np(e,t,n,i,"secure",o);const a=e?.states?.[i];if(!a)return Np(e,t,n,i,"missing",o);if("unavailable"===a.state)return Np(e,t,n,i,"unavailable",o);const r=function(e,t,i){const s=String(i?.state||""),n="closed"===s?{service:`open_${t}`,effect:"open",feature:1}:"open"===s?{service:`close_${t}`,effect:"close",feature:2}:"opening"===s||"closing"===s?{service:`stop_${t}`,effect:"stop",feature:8}:null;return n&&Ap(i,n.feature)&&Dp(e,t,n.service)?n:Ap(i,3)&&Dp(e,t,"toggle")?{service:"toggle",effect:"toggle"}:null}(e,o,a);if(!r)return Np(e,t,n,i,"unsupported",o);return{target:{entityId:i,name:Ip(e,t,i),state:String(a.state||""),via:s},skipped:null,semantics:o,nextEffect:r.effect,command:{domain:o,service:r.service,data:{entity_id:i}}}}function Op(e){return"entity"===e.bindingKind?e.bindingRef&&Ms(e.bindingRef)?[e.bindingRef]:[]:"virtual"===e.bindingKind?[]:eo(e)}function Hp(e){const t=e.marker?.toggle_entity;return t&&Op(e).includes(t)?t:null}function Lp(e,t,i){const s=function(e,t){if("entity"===e.bindingKind&&e.bindingRef){const t=e.marker?.light_entity?to(e):null;return[...new Set([t,e.bindingRef].filter(e=>!!e))]}const i=e.entities.length?e.entities:e.allEntities||[],s=e.marker?.light_entity||e.primary&&Ms(e.primary)?to(e):null;return[...new Set([s,...Yn(t,i)].filter(e=>!!e))]}(i,t);if(!s.length)return null;const n="entity"===i.bindingKind?"binding":"device-role";if("binding"===n)return Ep(e,t,s[0],n);let o=null,a=null;for(const i of s){const s=Ep(e,t,i,n);if(s.command)return s;const r=s.skipped?.reason;if("missing"===r||"unavailable"===r||"secure"===r)return s;"ha-disabled"!==r?o||=s:a||=s}return o||a}function Bp(e){const t=e.skipped?.reason;return"missing"===t?"unavailable":t||"unsupported"}function jp(e,t){return{origin:e,kind:"none",semantics:null,targets:[],skippedTargets:[],noneReason:t,nextEffect:null,command:null}}function qp(e,t){return"cover"===e||"toggle"===e?"toggle":"more-info"===e||"run"===e||"info"===e?e:(null==e||""===e)&&"light"===t?"toggle":"info"}function Wp(e,t){return{origin:e,kind:"single",semantics:t.semantics,targets:t.target?[t.target]:[],skippedTargets:t.skipped?[t.skipped]:[],noneReason:t.command?null:Bp(t),nextEffect:t.nextEffect,command:t.command}}function Up(e,t,i,s=[]){const n=new Map,o=[],a=new Set,r=e=>{const t=`${e.ref}\n${e.entityId||""}\n${e.reason}`;a.has(t)||(a.add(t),o.push(e))};for(const e of s)r(e);for(const s of i){const i=Ep(e,t,s.entityId,s.via,s.ref);i.target?n.set(s.entityId,n.get(s.entityId)||i.target):i.skipped&&r(i.skipped)}const l=[...n.values()];if(!l.length)return{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:l,skippedTargets:o,noneReason:o.length&&o.every(e=>"secure"===e.reason)?"secure":"configured-targets-missing",nextEffect:null,command:null};const c=l.some(e=>"on"===e.state)?"turn_off":"turn_on",h=Pp(e,"homeassistant",c);return h?{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:l,skippedTargets:o,noneReason:null,nextEffect:"turn_on"===c?"turn-on":"turn-off",command:{...h,data:{entity_id:l.map(e=>e.entityId)}}}:{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:[],skippedTargets:[...o,...l.map(i=>Fp(e,t,i.entityId,i.entityId,"unsupported"))],noneReason:"unsupported",nextEffect:null,command:null}}function Gp(e){return String(e.marker?.id||e.id||"")}function Vp(e){const{hass:t,device:i}=e,s=e.registryHass||t,n=function(e){return"toggle"===e.tapAction?"explicit-toggle":"cover"===e.tapAction?"legacy-cover":!e.tapAction&&e.primary?.startsWith("light.")?"default-light":null}(i);if(!n)return null;const o=i.marker;if("explicit-toggle"===n&&bn(o)){const t=String(o?.id||i.id||""),s=ro(e.devices).get(t);if(s)return function(e,t){const i=e.registryHass||e.hass;return Up(e.hass,i,t.driverEids.map(e=>({entityId:e,via:"control-marker-driver",ref:`marker:${t.markerId}`})))}(e,s);const a=yn(o,e.virtualLights);return{origin:n,kind:"single",semantics:"power",targets:[{entityId:"",name:i.name,state:a?"on":"off",via:"virtual-light"}],skippedTargets:[],noneReason:null,nextEffect:a?"turn-off":"turn-on",command:null,operation:{kind:"virtual-light",markerId:o.id}}}if("explicit-toggle"===n){if(Zn(i.marker?.binding,i.marker?.controls??i.controls,i.entities).length)return function(e,t=null){const{hass:i,device:s,devices:n}=e,o=e.registryHass||i,a=Zn(s.marker?.binding,s.marker?.controls??s.controls,s.entities),r=e.lightSources||ho(i,n,null,e.virtualLights),l=new Map;for(const e of r){if(!e.key.startsWith("marker:"))continue;const t=l.get(e.key)||[];t.push(e),l.set(e.key,t)}const c=ro(n),h=new Map;for(const e of n){const t=String(e.marker?.id||e.id||"");t&&h.set(t,e)}const d=[],p=[];t&&d.push({entityId:t,via:"entity"===s.bindingKind?"binding":"device-role",ref:t});for(const e of a){if(!e.startsWith("marker:")){Ms(e)?d.push({entityId:e,via:"control-entity",ref:e}):p.push(Fp(i,o,e,e.includes(".")?e:null,"unsupported"));continue}const t=l.get(e)||[];if(!t.length){const t=h.get(e.slice(7));if("ha_disabled"===t?.bindingStatus?.kind){const s=t.bindingStatus.allEntityIds[0]||null;p.push({ref:e,entityId:s,name:t.name||(s?Ip(i,o,s):null),reason:"ha-disabled"})}else p.push(Fp(i,o,e,null,"missing"));continue}const n=[...new Set(t.flatMap(e=>e.serviceEids))];if(n.length){for(const t of n)d.push({entityId:t,via:"control-entity",ref:e});continue}const a=t.some(e=>e.passive),r=e.slice(7),u=Gp(s),_=c.get(r)?.controllers.find(e=>Gp(e.device)===u);if(a&&_?.driverEids.length)for(const t of _.driverEids)d.push({entityId:t,via:"control-marker-driver",ref:e});else p.push(Fp(i,o,e,null,a?"missing":"unsupported"))}return Up(i,o,d,p)}(e,Hp(i))}if("legacy-cover"===n){const e=i.entities.find(e=>e.startsWith("cover."))||i.allEntities?.find(e=>e.startsWith("cover."));return e?Wp(n,Ep(t,s,e,"device-role")):jp(n,"no-actionable-entity")}const a=Hp(i),r=a?Ep(t,s,a,"entity"===i.bindingKind?"binding":"device-role"):Lp(t,s,i);return r?Wp(n,r):jp(n,i.virtual||"virtual"===i.bindingKind?"no-actionable-entity":"no-binding")}function Kp(e){if(!e)return[];const t=Array.isArray(e.data.entity_id)?e.data.entity_id:[e.data.entity_id];return[...new Set(t)].sort()}function Yp(e){return e?e.operation?e.operation:e.command?{kind:"ha-service",command:e.command}:null:null}const Jp=new Set(["missing","ha-disabled","unavailable"]);function Xp(e,t){const i=Yp(e),s=Yp(t);return!(!i||!s||i.kind!==s.kind)&&("virtual-light"===i.kind&&"virtual-light"===s.kind?i.markerId===s.markerId:"ha-service"===i.kind&&"ha-service"===s.kind&&function(e,t){const i=Kp(e),s=Kp(t);return i.length===s.length&&i.every((e,t)=>e===s[t])}(i.command,s.command))}const Zp=e=>!!e&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]);function Qp(e,t=5,i=5){return Ml(e.cm,t,i)/2}function eu(e,t,i=1,s=5,n=5,o=0){const a=e.host;if(!a||"partition"!==a.kind||"string"!=typeof a.id||!a.id)return{resolved:null,reason:"invalid-host"};const r=t.find(e=>e.id===a.id);if(!r)return{resolved:null,reason:"missing-partition"};if(!Zp(r.a)||!Zp(r.b)||!Number.isFinite(a.t)||a.t<0||a.t>1)return{resolved:null,reason:"invalid-position"};const l=r.b[0]-r.a[0],c=r.b[1]-r.a[1],h=Math.hypot(l,c),d=Number(e.length)*i;if(!(h>1e-9&&d>0&&Number.isFinite(d)))return{resolved:null,reason:"invalid-length"};const p=a.t*h;if(p-d/2<o-1e-9||p+d/2>h-o+1e-9)return{resolved:null,reason:"does-not-fit"};const u=l/h,_=c/h;let m=180*Math.atan2(c,l)/Math.PI;return m>=90?m-=180:m<-90&&(m+=180),{reason:null,resolved:{opening:e,host:a,partition:r,center:[r.a[0]+l*a.t,r.a[1]+c*a.t],angle:m,length:d,depth:Ml(r.cm,s,n),t:a.t,axis:{a:[r.a[0],r.a[1]],b:[r.b[0],r.b[1]],ux:u,uy:_,length:h}}}}function tu(e,t,i=1,s=5,n=5){return eu(e,t,i,s,n,0)}function iu(e,t,i=1,s=5,n=5){const o=e.host,a="partition"===o?.kind?t.find(e=>e.id===o.id):void 0;if(!a)return tu(e,t,i,s,n);const r=eu(e,t,i,s,n,Qp(a,s,n));return"does-not-fit"===r.reason?{resolved:null,reason:"does-not-fit-jamb"}:r}function su(e,t){return!e||(e.length!==t.length||e.host?.kind!==t.host?.kind||e.host?.id!==t.host?.id||e.host?.t!==t.host?.t)}function nu(e){const{center:t,length:i,axis:s,host:n}=e,o=i/2;return{hostId:n.id,a:[t[0]-s.ux*o,t[1]-s.uy*o],b:[t[0]+s.ux*o,t[1]+s.uy*o],depth:e.depth}}function ou(e,t=!1){const i=t?1:-1,s=e.depth/2;return{ox:-e.axis.uy*i*s,oy:e.axis.ux*i*s,cm:e.partition.cm,side:i}}function au(e,t,i){return e.flatMap(e=>!Zp(e.a)||!Zp(e.b)||Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])<=1e-9?[]:[{roomId:"",a:[e.a[0],e.a[1]],b:[e.b[0],e.b[1]],key:`partition:${e.id}`,kind:"outer",cm:e.cm,open:!1,half:Ml(e.cm,t,i)/2,partitionHost:{kind:"partition",id:e.id}}])}function ru(e,t,i){const{center:s,length:n,axis:o}=e,a=n/2,r=t.flatMap(e=>{if(!e.kind||e.open)return[];const t=e.b[0]-e.a[0],n=e.b[1]-e.a[1],r=Math.hypot(t,n);if(!(r>i))return[];const l=t/r,c=n/r;if(Math.abs(o.ux*c-o.uy*l)>1e-6)return[];if(Math.abs((s[0]-e.a[0])*c-(s[1]-e.a[1])*l)>i)return[];const h=e=>(e[0]-s[0])*o.ux+(e[1]-s[1])*o.uy,d=h(e.a),p=h(e.b),u=Math.max(-a,Math.min(d,p)),_=Math.min(a,Math.max(d,p));return _>=u-i?[[u,_]]:[]});if(!r.length)return!1;r.sort((e,t)=>e[0]-t[0]||e[1]-t[1]);let l=-a;for(const[e,t]of r){if(e>l+i)return!1;if(l=Math.max(l,t),l>=a-i)return!0}return!1}function lu(e,t,i){return{...e,x:t.center[0]/i,y:t.center[1]/i,angle:t.angle}}const cu=.001;function hu(e){return!!e&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1])}function du(e){return`${e[0].toFixed(6)},${e[1].toFixed(6)}`}function pu(e,t,i){return Math.abs(e[0]-t[0])<i&&Math.abs(e[1]-t[1])<i}function uu(e,t){const i=function(e,t){return e[0]-t[0]||e[1]-t[1]}(e,t)<=0?e:t,s=i===e?t:e;return[[i[0],i[1]],[s[0],s[1]]]}function _u(e){const[t,i]=uu(e.a,e.b);return`${e.kind}|${e.id}|${du(t)}|${du(i)}`}function mu(e,t,i){const[s,n]=uu(t,i);return`${_u(e)}|${du(s)}|${du(n)}`}function gu(e){return"room"===e?0:"draft"===e?1:2}function fu(e,t,i){return pu(e,[t[0],t[1]],i)||pu(e,[t[2],t[3]],i)}function vu(e){const t=e.epsilon??cu,i=[];for(const[t,s]of pi(e.space.rooms).entries())s.length<4||i.push({a:[s[0],s[1]],b:[s[2],s[3]],kind:"room",id:`room-edge-${t}`,cuts:[]});for(const s of e.space.room_drafts||[])if(s.id!==e.activeDraftId)for(let e=0;e+1<s.points.length;e++){const n=s.points[e],o=s.points[e+1];hu(n)&&hu(o)&&!pu(n,o,t)&&i.push({a:[n[0],n[1]],b:[o[0],o[1]],kind:"draft",id:`${s.id}:${e}`,cuts:[]})}for(const s of e.space.partitions||[])hu(s.a)&&hu(s.b)&&!pu(s.a,s.b,t)&&i.push({a:[s.a[0],s.a[1]],b:[s.b[0],s.b[1]],kind:"partition",id:s.id,cuts:[]});const s=i.filter(e=>"room"!==e.kind&&i.some(i=>i!==e&&function(e,t,i){const s=e.b[0]-e.a[0],n=e.b[1]-e.a[1],o=Math.hypot(s,n);if(!(o>i))return!1;const a=s/o,r=n/o,l=t=>Math.abs((t[0]-e.a[0])*r-(t[1]-e.a[1])*a);if(l(t.a)>i||l(t.b)>i)return!1;const c=(t.a[0]-e.a[0])*a+(t.a[1]-e.a[1])*r,h=(t.b[0]-e.a[0])*a+(t.b[1]-e.a[1])*r;return Math.min(o,Math.max(c,h))-Math.max(0,Math.min(c,h))>i}(e,i,t))),n=s.map(e=>{const[t,i]=uu(e.a,e.b);return{a:t,b:i,key:`hidden|${mu(e,t,i)}`,sourceKind:e.kind,sourceId:e.id}}).sort((e,t)=>e.key.localeCompare(t.key)),o=s.flatMap(e=>[e.a,e.b].map((t,i)=>({point:[t[0],t[1]],key:`hidden|${_u(e)}|endpoint-${i}`,sourceKind:e.kind,sourceId:e.id}))).sort((e,t)=>e.key.localeCompare(t.key));return{segments:n,endpoints:o}}function bu(e){const t=e.epsilon??cu,i=e.roomCuts||[],s=new Map;for(const t of e.partitionCuts||[]){if("string"!=typeof t.hostId||!t.hostId||!hu(t.a)||!hu(t.b))continue;const e=s.get(t.hostId)||[];e.push([t.a[0],t.a[1],t.b[0],t.b[1]]),s.set(t.hostId,e)}const n=[];for(const[t,s]of pi(e.space.rooms).entries())s.length<4||n.push({a:[s[0],s[1]],b:[s[2],s[3]],kind:"room",id:`room-edge-${t}`,cuts:i});for(const t of e.space.room_drafts||[])if(t.id!==e.activeDraftId)for(let e=0;e+1<t.points.length;e++){const i=t.points[e],s=t.points[e+1];hu(i)&&hu(s)&&n.push({a:[i[0],i[1]],b:[s[0],s[1]],kind:"draft",id:`${t.id}:${e}`,cuts:[]})}for(const t of e.space.partitions||[])hu(t.a)&&hu(t.b)&&n.push({a:[t.a[0],t.a[1]],b:[t.b[0],t.b[1]],kind:"partition",id:t.id,cuts:s.get(t.id)||[]});const o=new Map,a=new Map;for(const e of n){const i=Ds([[e.a[0],e.a[1],e.b[0],e.b[1]]],e.cuts,t);if(i.length){for(const s of i){if(s.length<4)continue;const[i,n]=uu([s[0],s[1]],[s[2],s[3]]);if(pu(i,n,t))continue;const a=`${du(i)}|${du(n)}`,r={a:i,b:n,key:mu(e,i,n),sourceKind:e.kind,sourceId:e.id},l=o.get(a);(!l||gu(r.sourceKind)<gu(l.sourceKind)||gu(r.sourceKind)===gu(l.sourceKind)&&r.key.localeCompare(l.key)<0)&&o.set(a,r)}for(const s of[e.a,e.b]){if(!i.some(e=>fu(s,e,t)))continue;const e=du(s);a.has(e)||a.set(e,{point:[s[0],s[1]],key:e})}}}return{segments:[...o.values()].sort((e,t)=>e.key.localeCompare(t.key)),endpoints:[...a.values()].sort((e,t)=>e.key.localeCompare(t.key))}}function yu(e,t,i){return t.some(t=>hu(t)&&pu(e,t,i))}function wu(e,t,i){if(!i)return!0;const s=e-i.distance;return s<-1e-9||Math.abs(s)<=1e-9&&t.localeCompare(i.key)<0}function ku(e,t,i){const s=i.epsilon??cu,n=i.excludePoints||[],o=[...e.endpoints,...(i.extraEndpoints||[]).filter(e=>hu(e.point)).map(e=>({point:[e.point[0],e.point[1]],key:e.key}))],a=new Map;for(const e of o){if(yu(e.point,n,s))continue;const t=du(e.point),i=a.get(t);(!i||e.key.localeCompare(i.key)<0)&&a.set(t,e)}return[...a.values()].map(e=>({kind:"endpoint",point:[...e.point],key:e.key,distance:Math.hypot(t[0]-e.point[0],t[1]-e.point[1])})).filter(e=>e.distance<=i.tolerance).sort((e,t)=>e.distance-t.distance||e.key.localeCompare(t.key))}function xu(e,t){if(!e.length)return null;if(t>0){const i=new Set;for(let s=0;s<e.length;s++)for(let n=s+1;n<e.length;n++)Math.hypot(e[s].point[0]-e[n].point[0],e[s].point[1]-e[n].point[1])<t&&(i.add(e[s].key),i.add(e[n].key));if(i.size>1)return{kind:"ambiguous",candidate:null,conflicts:e.filter(e=>i.has(e.key)).map(e=>({point:[...e.point],key:e.key}))}}return{kind:"resolved",candidate:e[0],conflicts:[]}}function $u(e,t,i){const s=e.b[0]-e.a[0],n=e.b[1]-e.a[1],o=Math.hypot(s,n);if(!(o>0))return[...e.a];const a=s/o,r=n/o,l=Math.max(0,Math.min(o,(t[0]-e.a[0])*a+(t[1]-e.a[1])*r)),c=i>0?Math.max(0,Math.min(o,Math.round(l/i)*i)):l;return[e.a[0]+a*c,e.a[1]+r*c]}function Su(e,t,i){if(!(hu(t)&&hu(i.anchor)&&i.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const s=i.epsilon??cu,n=function(e,t){const i=t[0]-e[0],s=t[1]-e[1];if(!(Math.hypot(i,s)>Number.EPSILON))return null;const n=Math.round(Math.atan2(s,i)/(Math.PI/4))*(Math.PI/4);return[Math.cos(n),Math.sin(n)]}(i.anchor,t);if(!n)return{kind:"none",candidate:null,conflicts:[]};const o=ku(e,t,i).filter(e=>function(e,t,i,s){const n=e[0]-t[0],o=e[1]-t[1];return n*i[0]+o*i[1]>s&&Math.abs(n*i[1]-o*i[0])<=s*Math.max(Math.hypot(n,o),1)}(e.point,i.anchor,n,s)),a=xu(o,i.distinguishTolerance||0);if(a)return a;let r=null;for(const o of e.segments){const e=o.b[0]-o.a[0],a=o.b[1]-o.a[1],l=o.a[0]-i.anchor[0],c=o.a[1]-i.anchor[1],h=n[0]*a-n[1]*e;let d=null;if(Math.abs(h)<=s*Math.max(Math.hypot(e,a),1)){if(Math.abs(l*n[1]-c*n[0])>s*Math.max(Math.hypot(l,c),1))continue;const e=[o.a,o.b].map(e=>({point:e,along:(e[0]-i.anchor[0])*n[0]+(e[1]-i.anchor[1])*n[1]})).filter(e=>e.along>s).sort((e,t)=>e.along-t.along);e.length&&(d=[...e[0].point])}else{const t=(l*a-c*e)/h,o=(l*n[1]-c*n[0])/h;t>s&&o>=-s&&o<=1+s&&(d=[i.anchor[0]+n[0]*t,i.anchor[1]+n[1]*t])}if(!d)continue;const p=Math.hypot(t[0]-d[0],t[1]-d[1]);p>i.tolerance||yu(d,i.excludePoints||[],s)||!wu(p,o.key,r)||(r={kind:"line",point:d,key:o.key,distance:p,segment:o})}return r?{kind:"resolved",candidate:r,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}const Mu=.001;function Cu(e,t,i,s){const n=Number.isFinite(e)&&e>0?Math.floor(e):0,o=e=>"number"==typeof e&&Number.isFinite(e)&&e>=0?e:null,a=o(i),r=o(s)??0,l=[];let c=null;for(let e=0;e<n;e++){const i=o(t?.[e])??(e===n-1?a??c??r:c??a??r);l.push(i),c=i}return l}function Du(e,t){const i=[];for(let s=0;s+1<e.length;s++){const n=e[s],o=e[s+1];!Pu(n)||!Pu(o)||Math.hypot(o[0]-n[0],o[1]-n[1])<=Number.EPSILON||i.push({a:[n[0],n[1]],b:[o[0],o[1]],cm:t[s]})}return i}function Pu(e){return!!e&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1])}function Tu(e,t,i,s){return e*s-t*i}function Iu(e){let t=0;for(let i=0;i<e.length;i++){const s=e[i],n=e[(i+1)%e.length];t+=s[0]*n[1]-n[0]*s[1]}return t/2}function Ru(e,t){const i=Math.max(t,Number.EPSILON),s=Math.round(e[0]/i)*i,n=Math.round(e[1]/i)*i;return[Object.is(s,-0)?0:s,Object.is(n,-0)?0:n]}function Au(e,t){const i=Ru(e,t);return`${Math.round(i[0]/t)},${Math.round(i[1]/t)}`}function zu(e,t){return e.localeCompare(t)<=0?`${e}|${t}`:`${t}|${e}`}function Fu(e){if(!e.length)return"";const t=[];for(const i of[e,[...e].reverse()])for(let e=0;e<i.length;e++)t.push([...i.slice(e),...i.slice(0,e)].join(";"));return t.sort((e,t)=>e.localeCompare(t)),t[0]}function Nu(e,t,i){const s=[...e];for(let e=!0;e&&s.length>=3;){e=!1;for(let n=0;n<s.length;n++){const o=t.get(s[(n-1+s.length)%s.length]),a=t.get(s[n]),r=t.get(s[(n+1)%s.length]),l=a[0]-o[0],c=a[1]-o[1],h=r[0]-a[0],d=r[1]-a[1];if(Math.abs(Tu(l,c,h,d))<=i*Math.max(Math.hypot(l,c),Math.hypot(h,d),1)&&l*h+c*d>=0){s.splice(n,1),e=!0;break}}}return s}function Eu(e,t){const i=e.map(e=>Math.max(0,Math.min(1,e))).sort((e,t)=>e-t),s=[];for(const e of i)(!s.length||Math.abs(e-s[s.length-1])>t)&&s.push(e);return s}function Ou(e,t){return[e.a[0]+(e.b[0]-e.a[0])*t,e.a[1]+(e.b[1]-e.a[1])*t]}function Hu(e,t,i){const s=function(e,t){const i=t.b[0]-t.a[0],s=t.b[1]-t.a[1],n=i*i+s*s;return n>0?((e[0]-t.a[0])*i+(e[1]-t.a[1])*s)/n:0}(e,t);if(s<-i||s>1+i)return null;const n=Ou(t,s);return Math.hypot(n[0]-e[0],n[1]-e[1])<=i?Math.max(0,Math.min(1,s)):null}function Lu(e,t,i,s,n){const o=e.b[0]-e.a[0],a=e.b[1]-e.a[1],r=t.b[0]-t.a[0],l=t.b[1]-t.a[1],c=t.a[0]-e.a[0],h=t.a[1]-e.a[1],d=Tu(o,a,r,l),p=Math.max(Math.hypot(o,a),Math.hypot(r,l),1);if(Math.abs(d)>n*p){const e=Tu(c,h,r,l)/d,t=Tu(c,h,o,a)/d;return void(e>=-n&&e<=1+n&&t>=-n&&t<=1+n&&(i.push(Math.max(0,Math.min(1,e))),s.push(Math.max(0,Math.min(1,t)))))}if(!(Math.abs(Tu(c,h,o,a))>n*p)){for(const s of[t.a,t.b]){const t=Hu(s,e,n);null!=t&&i.push(t)}for(const i of[e.a,e.b]){const e=Hu(i,t,n);null!=e&&s.push(e)}}}function Bu(e){let t=e+1|0;return t^=t<<13,t^=t>>>17,t^=t<<5,t>>>0}function ju(e,t){return e.minY-t.minY||e.index-t.index}function qu(e){return e.subtreeMaxY=Math.max(e.item.maxY,e.left?.subtreeMaxY??-1/0,e.right?.subtreeMaxY??-1/0),e}function Wu(e){const t=e.right;return e.right=t.left,t.left=qu(e),qu(t)}function Uu(e){const t=e.left;return e.left=t.right,t.right=qu(e),qu(t)}function Gu(e,t){return e?(ju(t,e.item)<0?(e.left=Gu(e.left,t),e.left.priority<e.priority&&(e=Uu(e))):(e.right=Gu(e.right,t),e.right.priority<e.priority&&(e=Wu(e))),qu(e)):{item:t,priority:Bu(t.index),subtreeMaxY:t.maxY,left:null,right:null}}function Vu(e,t){if(!e)return null;const i=ju(t,e.item);if(i<0)e.left=Vu(e.left,t);else if(i>0)e.right=Vu(e.right,t);else{if(!e.left)return e.right;if(!e.right)return e.left;e.left.priority<e.right.priority?(e=Uu(e)).right=Vu(e.right,t):(e=Wu(e)).left=Vu(e.left,t)}return qu(e)}function Ku(e,t,i,s){!e||e.subtreeMaxY<t||(null!=e.left?.subtreeMaxY&&e.left.subtreeMaxY>=t&&Ku(e.left,t,i,s),e.item.minY<=i&&e.item.maxY>=t&&s.push(e.item),e.item.minY<=i&&Ku(e.right,t,i,s))}function Yu(e,t=.001){const i=Number.isFinite(t)&&t>0?t:Mu,s=e.filter(e=>Pu(e.a)&&Pu(e.b)&&"string"==typeof e.key&&e.key.length>0&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>i),n=s.map(()=>[0,1]);!function(e,t,i){const s=e.map((e,t)=>({index:t,minX:Math.min(e.a[0],e.b[0])-i,maxX:Math.max(e.a[0],e.b[0])+i,minY:Math.min(e.a[1],e.b[1])-i,maxY:Math.max(e.a[1],e.b[1])+i})),n=[...s].sort((e,t)=>e.minX-t.minX||e.minY-t.minY||e.index-t.index),o=[...s].sort((e,t)=>e.maxX-t.maxX||e.index-t.index),a=new Set;let r=null,l=0;for(const s of n){for(;l<o.length&&o[l].maxX<s.minX;){const e=o[l++];a.delete(e.index)&&(r=Vu(r,e))}const n=[];Ku(r,s.minY,s.maxY,n),n.sort((e,t)=>e.index-t.index);for(const o of n)Lu(e[o.index],e[s.index],t[o.index],t[s.index],i);a.add(s.index),r=Gu(r,s)}}(s,n,i);const o=new Map;for(let e=0;e<s.length;e++){const t=s[e],a=Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1]),r=Eu(n[e],i/Math.max(a,1));for(let e=0;e+1<r.length;e++){const s=Ru(Ou(t,r[e]),i),n=Ru(Ou(t,r[e+1]),i);if(Math.hypot(n[0]-s[0],n[1]-s[1])<=i)continue;const a=Au(s,i),l=Au(n,i),c=zu(a,l),h=o.get(c);h?h.sourceKeys.add(t.key):o.set(c,{a:a.localeCompare(l)<=0?s:n,b:a.localeCompare(l)<=0?n:s,sourceKeys:new Set([t.key])})}}return[...o.entries()].map(([e,t])=>({key:e,a:t.a,b:t.b,sourceKeys:[...t.sourceKeys].sort((e,t)=>e.localeCompare(t))})).sort((e,t)=>e.key.localeCompare(t.key))}function Ju(e,t=.001){const i=Number.isFinite(t)&&t>0?t:Mu,s=Yu(e,i),n=new Map,o=new Map,a=new Map(s.map(e=>[e.key,e]));for(const e of s){const t=Au(e.a,i),s=Au(e.b,i);n.set(t,e.a),n.set(s,e.b),o.has(t)||o.set(t,new Set),o.has(s)||o.set(s,new Set),o.get(t).add(s),o.get(s).add(t)}const r=new Map;for(const[e,t]of o){const i=n.get(e);r.set(e,[...t].sort((e,t)=>{const s=n.get(e),o=n.get(t);return Math.atan2(s[1]-i[1],s[0]-i[0])-Math.atan2(o[1]-i[1],o[0]-i[0])||e.localeCompare(t)}))}const l=new Set,c=new Map,h=(e,t)=>`${e}>${t}`;for(const e of s){const t=[Au(e.a,i),Au(e.b,i)];for(const[e,o]of[t,[t[1],t[0]]]){if(l.has(h(e,o)))continue;const t=[],d=[];let p=e,u=o,_=!1;for(let i=0;i<=2*s.length+2;i++){const i=h(p,u);if(l.has(i)){_=p===e&&u===o;break}l.add(i),t.push(p),d.push(zu(p,u));const s=r.get(u)||[],n=s.indexOf(p);if(n<0||!s.length)break;if(p=u,u=s[(n-1+s.length)%s.length],p===e&&u===o){_=!0;break}}if(!_||new Set(t).size<3||new Set(t).size!==t.length)continue;const m=t.map(e=>n.get(e)),g=Iu(m);if(!(g>i*i))continue;const f=Fu(Nu(t,n,i)),v=new Set;for(const e of d)for(const t of a.get(e)?.sourceKeys||[])v.add(t);const b={ring:m,key:f,area:g,atomKeys:[...d],sourceKeys:[...v].sort((e,t)=>e.localeCompare(t))};c.has(f)||c.set(f,b)}}return{atoms:s,faces:[...c.values()].sort((e,t)=>e.area-t.area||e.key.localeCompare(t.key))}}function Xu(e,t,i=.001){if(!Pu(t))return null;const s=e.faces.filter(e=>{if(e.ring.some((s,n)=>function(e,t,i,s){const n=i[0]-t[0],o=i[1]-t[1],a=n*n+o*o;if(!(a>0))return Math.hypot(e[0]-t[0],e[1]-t[1])<=s;const r=((e[0]-t[0])*n+(e[1]-t[1])*o)/a;return!(r<0||r>1)&&Math.hypot(t[0]+n*r-e[0],t[1]+o*r-e[1])<=s}(t,s,e.ring[(n+1)%e.ring.length],i)))return!1;let s=!1;for(let i=0,n=e.ring.length-1;i<e.ring.length;n=i++){const o=e.ring[i],a=e.ring[n];o[1]>t[1]!=a[1]>t[1]&&t[0]<(a[0]-o[0])*(t[1]-o[1])/(a[1]-o[1])+o[0]&&(s=!s)}return s});return[...s].sort((e,t)=>e.area-t.area||e.key.localeCompare(t.key))[0]||null}function Zu(e,t,i){const s=i[0]-t[0],n=i[1]-t[1],o=s*s+n*n,a=o>0?Math.max(0,Math.min(1,((e[0]-t[0])*s+(e[1]-t[1])*n)/o)):0;return Math.hypot(t[0]+s*a-e[0],t[1]+n*a-e[1])}function Qu(e,t,i=.5){let s=Math.abs(e-t)%180;return s>90&&(s=180-s),s<=i}function e_(e,t,i,s){const n=e.filter(e=>"outer"===e.kind&&!e.open&&e.cm>0).map(e=>({interval:e,reusePartitionId:t.find(t=>function(e,t,i){return!(Math.abs(e.cm-t.cm)>1e-6)&&(Qu(180*Math.atan2(e.b[1]-e.a[1],e.b[0]-e.a[0])/Math.PI,180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI,.001)&&Zu(t.a,e.a,e.b)<=i&&Zu(t.b,e.a,e.b)<=i)}(t,e,s))?.id||null})),o=new Map;for(const t of i){if("partition"===t.host?.kind)continue;const i=e.map(e=>{const i=180*Math.atan2(e.b[1]-e.a[1],e.b[0]-e.a[0])/Math.PI;return{interval:e,distance:Qu(t.angle,i)?Zu([t.x,t.y],e.a,e.b):1/0}}).filter(e=>e.distance<=s).sort((e,t)=>e.distance-t.distance||e.interval.key.localeCompare(t.interval.key)),n=i[0]?.interval;"outer"===n?.kind&&!n.open&&n.cm>0&&o.set(t.id,n.key)}return{materialize:n,openingIntervals:o,removeOpeningIds:[...o.keys()].sort((e,t)=>e.localeCompare(t))}}function t_(e,t){const i=t.b[0]-t.a[0],s=t.b[1]-t.a[1],n=i*i+s*s;return n>0?Math.max(0,Math.min(1,((e[0]-t.a[0])*i+(e[1]-t.a[1])*s)/n)):0}const i_=e=>!e.key.startsWith("static:room|");function s_(e,t){const i=t.b[0]-t.a[0],s=t.b[1]-t.a[1],n=i*i+s*s;if(!(n>0))return null;const o=((e[0]-t.a[0])*i+(e[1]-t.a[1])*s)/n,a=[t.a[0]+i*o,t.a[1]+s*o];return{point:a,t:o,distance:Math.hypot(a[0]-e[0],a[1]-e[1])}}function n_(e,t){return t>0?Math.hypot(e[0]-Math.round(e[0]/t)*t,e[1]-Math.round(e[1]/t)*t):0}function o_(e,t,i){if(i_(e.source)!==i_(t.source))return i_(e.source)?e:t;const s=n_(e.point,i),n=n_(t.point,i);return Math.abs(s-n)>1e-9?s>n?e:t:function(e,t){return e[0]-t[0]||e[1]-t[1]}(e.point,t.point)>0?e:t}function a_(e){return[e.from[0],e.from[1],e.to[0],e.to[1],e.targetKind].map(e=>"number"==typeof e?e.toFixed(6):e).join("|")}function r_(e,t){return e.map(e=>e.key!==t.sourceKey?e:{...e,[t.endpoint]:[...t.to]})}function l_(e,t){const i=t.epsilon??.001;if(!(t.maxDistance>=0)||!t.point&&!t.requiredSourceKey)return{kind:"none"};const s=new Map,n=e.flatMap(e=>[{source:e,endpoint:"a",point:e.a},{source:e,endpoint:"b",point:e.b}]);for(let e=0;e<n.length;e++)for(let o=e+1;o<n.length;o++){const a=n[e],r=n[o];if(a.source===r.source)continue;const l=Math.hypot(a.point[0]-r.point[0],a.point[1]-r.point[1]);if(!(l>i)||l>t.maxDistance)continue;const c=o_(a,r,t.gridStep||0),h=c===a?r:a;if(!i_(c.source))continue;const d={sourceKey:c.source.key,endpoint:c.endpoint,from:[c.point[0],c.point[1]],to:[h.point[0],h.point[1]],targetSourceKey:h.source.key,targetKind:"endpoint",distance:l};s.set(a_(d),d)}for(const o of n)if(i_(o.source))for(const n of e){if(n===o.source)continue;const e=s_(o.point,n);if(!e||e.t<=i||e.t>=1-i||!(e.distance>i)||e.distance>t.maxDistance)continue;const a={sourceKey:o.source.key,endpoint:o.endpoint,from:[o.point[0],o.point[1]],to:e.point,targetSourceKey:n.key,targetKind:"line",distance:e.distance};s.set(a_(a),a)}const o=[];for(const n of s.values()){const s=Ju(r_(e,n),i),a=t.point?Xu(s,t.point,i):s.faces.find(e=>!t.requiredSourceKey||e.sourceKeys.includes(t.requiredSourceKey))||null;a&&o.push({proposal:n,face:a})}if(o.sort((e,t)=>e.proposal.distance-t.proposal.distance||a_(e.proposal).localeCompare(a_(t.proposal))),!o.length)return{kind:"none"};return new Set(o.map(e=>`${a_(e.proposal)}|${e.face.key}`)).size>1?{kind:"ambiguous",proposals:o.map(e=>e.proposal)}:{kind:"repair",...o[0]}}const c_=1e-5;function h_(e){const t=e.map(()=>[]);for(let i=0;i<e.length;i++){const s=e[i],n=s[2]-s[0],o=s[3]-s[1];for(let a=i+1;a<e.length;a++){const r=e[a],l=n*(r[3]-r[1])-o*(r[2]-r[0]);if(Math.abs(l)<1e-12)continue;const c=r[0]-s[0],h=r[1]-s[1],d=(c*(r[3]-r[1])-h*(r[2]-r[0]))/l,p=(c*o-h*n)/l;d<=1e-9||d>=1-1e-9||p<=1e-9||p>=1-1e-9||(t[i].push(d),t[a].push(p))}}const i=[];for(let s=0;s<e.length;s++){const n=e[s];if(!t[s].length){i.push(n);continue}const o=[0,...t[s].sort((e,t)=>e-t),1];for(let e=1;e<o.length;e++)o[e]-o[e-1]<1e-9||i.push([n[0]+(n[2]-n[0])*o[e-1],n[1]+(n[3]-n[1])*o[e-1],n[0]+(n[2]-n[0])*o[e],n[1]+(n[3]-n[1])*o[e]])}return i}function d_(e){const t=[];for(let i=0;i<e.length;i++){const s=e[i],n=e[(i+1)%e.length];s&&n&&(Math.hypot(n[0]-s[0],n[1]-s[1])<1e-9||t.push([s[0],s[1],n[0],n[1]]))}return t}const p_=(e,t)=>{const i=t[2]-t[0],s=t[3]-t[1],n=i*i+s*s;if(!(n>0))return Math.hypot(e[0]-t[0],e[1]-t[1]);const o=Math.max(0,Math.min(1,((e[0]-t[0])*i+(e[1]-t[1])*s)/n));return Math.hypot(e[0]-(t[0]+o*i),e[1]-(t[1]+o*s))},u_=(e,t,i,s)=>{const n=s[2]-s[0],o=s[3]-s[1],a=t*o-i*n;if(Math.abs(a)<1e-12)return 1/0;const r=s[0]-e[0],l=s[1]-e[1],c=(r*o-l*n)/a;if(!(c>1e-9))return 1/0;const h=(r*i-l*t)/a;return h<-1e-9||h>1+1e-9?1/0:c};function __(e,t,i,s=96){if(!(t>0&&Number.isFinite(e[0])&&Number.isFinite(e[1])))return[];const n=[];for(const s of i){if(!s||s.length<4)continue;if(![s[0],s[1],s[2],s[3]].every(Number.isFinite))continue;const i=p_(e,s);if(i<1e-7)return[];i>t||n.push(s)}const o=[],a=Math.max(12,Math.round(s));for(let e=0;e<a;e++)o.push(e/a*Math.PI*2-Math.PI);for(const t of n)for(const i of[[t[0],t[1]],[t[2],t[3]]]){const t=Math.atan2(i[1]-e[1],i[0]-e[0]);o.push(t-c_,t,t+c_)}const r=2*Math.PI;for(let e=0;e<o.length;e++)o[e]=(o[e]%r+r)%r;o.sort((e,t)=>e-t);const l=[];let c=Number.NEGATIVE_INFINITY;for(const i of o){if(i-c<1e-9)continue;c=i;const s=Math.cos(i),o=Math.sin(i);let a=t;for(const t of n){const i=u_(e,s,o,t);i<a&&(a=i)}l.push([e[0]+s*a,e[1]+o*a])}return l.length>=3?l:[]}class m_ extends he{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return pa(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},t=this._floorToken;return t?e.floor=t:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],t=this._lang,i=[{value:"",label:ua(t,"editor.floor_none")}],s=this._floorToken;s&&i.push({value:s,label:ua(t,"editor.floor_index",{index:String(this._config?.floor)})});const n="string"==typeof this._config?.floor?this._config.floor:"";n&&!e.some(e=>e.value===n)&&i.push({value:n,label:n}),i.push(...e);const o="string"==typeof this._config?.default_floor?this._config.default_floor:"",a=[...e];return o&&!e.some(e=>e.value===o)&&a.unshift({value:o,label:o}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:i}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:a}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:da(ua(t,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const e=this._lang,t={title:ua(e,"editor.title"),floor:ua(e,"editor.floor"),default_floor:ua(e,"editor.default_floor"),language:ua(e,"editor.language"),icon_size:ua(e,"editor.icon_size"),show_temperature:ua(e,"editor.show_temperature"),live_states:ua(e,"editor.live_states"),show_signal:ua(e,"editor.show_signal"),kiosk:ua(e,"editor.kiosk"),cycle:ua(e,"editor.cycle")},i=this._schema,s=function(e,t,i){if(!i||null===t)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||t.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),n=e=>q`<ha-form
|
||
.hass=${this.hass}
|
||
.data=${this._formData}
|
||
.schema=${e}
|
||
.computeLabel=${e=>t[e.name]||e.name}
|
||
@value-changed=${this._valueChanged}
|
||
></ha-form>`;return q`
|
||
${n(i.slice(0,3))}
|
||
${s?q`<div class="default-floor-error" role="alert"
|
||
style="color:var(--error-color,#db4437);margin:-4px 0 12px;overflow-wrap:anywhere">
|
||
${ua(e,"editor.default_floor_missing",{id:s})}
|
||
</div>`:G}
|
||
${n(i.slice(3))}
|
||
`}_valueChanged(e){const t={...this._config,...e.detail.value};""===t.floor?delete t.floor:t.floor===this._floorToken&&(t.floor=this._config?.floor);const i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:t},this.dispatchEvent(i)}}function g_(e){const t=e.length/2,i=e.face.cm>0?e.face.cm/e.cellCm*e.gridPitch/2:Dh(4,e.cellCm),s="gate"===e.type?Math.sin(10*Math.PI/180)*t:0;return{half:t,jambHalf:i,gateDepth:s,outlineHalf:Math.max(Dh(16,e.cellCm),i+Dh(8,e.cellCm),s+Dh(8,e.cellCm)),hitHalf:Math.max(Dh(20,e.cellCm),i+Dh(10,e.cellCm),s+Dh(12,e.cellCm))}}function f_(e){if("passage"===e.type)return W``;const t=Math.max(0,Math.min(1,e.amount)),{half:i,jambHalf:s}=g_(e),n=Ch(e.cellCm),o=1.75*n,a=e.flipH?-1:1,r=e.flipV?-1:1,l="gate"===e.type?1:r;e.type,e.flipV,e.angle,e.face;let c;if("window"===e.type){const o=Math.PI/2*i,a=e.face.cm>0?W`<line class="op-glass" x1="0" y1="${-s}" x2="0" y2="${s}"
|
||
stroke="${e.tone}" stroke-width="${1.5*n}"></line>`:W``;c=W`
|
||
<g transform="translate(${0} ${0})">
|
||
<path class="op-arc" d="M 0 0 A ${i} ${i} 0 0 0 ${-i} ${-i}" fill="none"
|
||
stroke="${e.tone}" stroke-dasharray="${o}" stroke-dashoffset="${o*(1-t)}"></path>
|
||
<path class="op-arc" d="M 0 0 A ${i} ${i} 0 0 1 ${i} ${-i}" fill="none"
|
||
stroke="${e.tone}" stroke-dasharray="${o}" stroke-dashoffset="${o*(1-t)}"></path>
|
||
<g transform="translate(${-i} 0)">
|
||
<g class="op-leaf" style="transform:rotate(${-90*t}deg)">
|
||
<rect x="0" y="${-1.5*n}" width="${i}" height="${3*n}" fill="${e.tone}"></rect>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(${i} 0)">
|
||
<g class="op-leaf" style="transform:rotate(${90*t}deg)">
|
||
<rect x="${-i}" y="${-1.5*n}" width="${i}" height="${3*n}" fill="${e.tone}"></rect>
|
||
</g>
|
||
</g>
|
||
${a}
|
||
</g>`}else if("gate"===e.type){const s=10*e.face.side*t;c=W`
|
||
<g transform="translate(${0} ${0})">
|
||
<g transform="translate(${-i} 0)">
|
||
<g class="op-leaf" style="transform:rotate(${s}deg)">
|
||
<rect x="0" y="${-o}" width="${i}" height="${2*o}" fill="${e.tone}"></rect>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(${i} 0)">
|
||
<g class="op-leaf" style="transform:rotate(${-s}deg)">
|
||
<rect x="${-i}" y="${-o}" width="${i}" height="${2*o}" fill="${e.tone}"></rect>
|
||
</g>
|
||
</g>
|
||
</g>`}else{const s=Math.PI/2*e.length;c=W`
|
||
<g transform="translate(${0} ${0})">
|
||
<path class="op-arc" d="M ${i} 0 A ${e.length} ${e.length} 0 0 0 ${-i} ${-e.length}" fill="none"
|
||
stroke="${e.tone}" stroke-dasharray="${s}" stroke-dashoffset="${s*(1-t)}"></path>
|
||
<g transform="translate(${-i} 0)">
|
||
<g class="op-leaf" style="transform:rotate(${-90*t}deg)">
|
||
<rect x="0" y="${-o}" width="${e.length}" height="${2*o}" fill="${e.tone}"></rect>
|
||
</g>
|
||
</g>
|
||
</g>`}return W`<g transform="scale(${a} ${l})">
|
||
<line x1="${-i}" y1="${-s}" x2="${-i}" y2="${s}"
|
||
stroke="${e.base}" stroke-width="${2.5*n}"></line>
|
||
<line x1="${i}" y1="${-s}" x2="${i}" y2="${s}"
|
||
stroke="${e.base}" stroke-width="${2.5*n}"></line>
|
||
${c}
|
||
</g>`}m_.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",m_);const v_={availability:"available",status:"neutral",activity:"none"};function b_(e){return e<=40?"low":e<180?"mid":"high"}function y_(e){return ai(e)}function w_(e){return"static_icon"===e.display?"neutral":"alarm"===e.visual.status?"alarm":"unavailable"===e.visual.availability?"unavailable":e.lockState?e.lockState:"working"===e.visual.status?"working":"open"===e.visual.status?"open":"neutral"}const k_={sourceKind:"none",decisionIds:["source.skipped_static_fast_path"],visualSources:[],criticalSources:[],samples:[]};function x_(e,t){const i=e?.entities?.[t],s=e?.states?.[t];return String(i?.name||i?.original_name||s?.attributes?.friendly_name||t)}function $_(e,t){const i=e?.states?.[t];if(!i)return"";if("function"==typeof e?.formatEntityState)try{const t=e.formatEntityState(i);if("string"==typeof t&&t)return t}catch{}return String(i.state??"")}function S_(e,t){if(t.virtual||"virtual"===t.bindingKind||"virtual"===t.marker?.binding)return"available";return(t.entities||[]).some(t=>{const i=String(e?.states?.[t]?.state??"").trim().toLowerCase();return""!==i&&"unknown"!==i&&"unavailable"!==i})?"available":"unavailable"}function M_(e,t,i,s){return{eid:t,role:i,name:x_(e,t),state:String(e?.states?.[t]?.state??""),stateText:$_(e,t),integrationDomain:e?.entities?.[t]?.platform||null,sample:s||cn(e,t)}}function C_(e,t,i=[t],s,n=e){const o=t.hidden&&t.userHidden?{...t,hidden:!1}:t;let a="none",r=[];const l=[],c=[...o.entities,...o.allEntities||[]].some(e=>e.startsWith("cover."))?{...o,tapAction:"cover"}:o,h=(d=Vp({hass:e,devices:i,device:c,lightSources:s,registryHass:n}),"cover"!==d?.semantics?null:d.targets[0]?.entityId||d.skippedTargets[0]?.entityId||null);var d;const p=ho(e,[o]),u=Zn(o.marker?.binding,o.marker?.controls??o.controls,o.entities),_=new Set(u.filter(e=>e.startsWith("marker:"))),m=!0===o.marker?.is_light||_.size>0?s||ho(e,i):p,g=m.filter(e=>e.device.id===o.id&&"controls"!==e.via),f=p.filter(e=>"controls"!==e.via),v=g.length?g:f,b=bn(o.marker),y=b?[...v]:[...p.filter(e=>"controls"===e.via),...v];if(_.size)for(const e of m){if(!_.has(e.key))continue;e.stateEids.length>0&&y.some(t=>t.stateEids.some(t=>e.stateEids.includes(t)))||y.some(t=>t.key===e.key)||y.push({...e,via:"controls",castsGlow:!1})}const w=Yn(n,o.entities),k=!!h&&("cover"===o.tapAction||o.primary?.startsWith("cover.")||w.some(e=>e.startsWith("cover.")));if(h&&!k&&l.push("source.cover_capability_bypassed"),k)a="cover",l.push("source.cover"),r=[M_(e,h,"cover")];else if(y.length)a=!b&&y.some(e=>"controls"===e.via)?"controls":"light",b?l.push("source.manual_virtual_light"):l.push("controls"===a?"source.controls":"source.owned_light"),"controls"!==a||!o.virtual&&"virtual"!==o.bindingKind||l.push("source.virtual_controller"),r=y.map(i=>{const s="controls"===i.via?"control":"forced"===i.via?"forced_light":"light";if(!i.passive)return M_(e,i.eid,s);const n=i.on?"on":"off";return{eid:i.key,role:s,name:t.name,state:n,stateText:n,integrationDomain:null,sample:{eid:i.key,state:n,availability:"available",status:i.on?"working":"neutral",activity:"none",edge:"none"}}});else{const t=Yn(n,o.entities),i=t.length?t:o.entities.filter(t=>!!e?.states?.[t]);if(i.length){a="device_role",l.push("source.device_role");const t=hn(n===e?e:{...n,states:e?.states||{}},i,o.entities),s=i.some(e=>an(n,e));r=t.map(t=>{const i=s?rn(n,t.eid)?"power_gate":an(n,t.eid)?"lifecycle":"device_role":"device_role";return M_(e,t.eid,i,t)})}else o.primary?(a="primary",l.push("source.primary_fallback"),r=[M_(e,o.primary,"primary")]):l.push("source.none")}u.length>0&&"controls"!==a&&"light"!==a&&"cover"!==a&&l.push("source.filtered_saved_controls");const x=[];for(const t of o.entities||[]){const i=cn(e,t);"alarm"!==i.status||r.some(e=>e.eid===t)||x.push(M_(e,t,"critical",i))}return x.length&&l.push("source.critical_sibling"),{sourceKind:a,decisionIds:l,visualSources:r,criticalSources:x,samples:[...r,...x].map(e=>e.sample)}}function D_(e,t,i,s){if(t.virtual)return{source:null,text:null,fallback:"value_virtual"};if(s){const i=function(e,t){if(!0!==t.marker?.use_climate_temp)return null;for(const i of t.entities){if(!i.startsWith("climate."))continue;const t=Number(e?.states?.[i]?.attributes?.current_temperature);if(Number.isFinite(t))return{eid:i,text:Math.round(10*t)/10+"°"}}return null}(e,t);if(i)return{source:{kind:"temperature",eid:i.eid,attribute:"current_temperature",text:i.text},text:i.text,fallback:null};const s=function(e,t){if("mdi:thermometer"!==t.icon&&"mdi:air-filter"!==t.icon)return null;for(const i of t.entities){if(!Gn(e,i))continue;const t=Number(e?.states?.[i]?.state);if(Number.isFinite(t))return{eid:i,text:Math.round(10*t)/10+"°"}}return null}(e,t);if(s)return{source:{kind:"temperature",eid:s.eid,text:s.text},text:s.text,fallback:null};const n=function(e,t){if(!t.primary||!fo(e,t.primary))return null;const i=Number(e?.states?.[t.primary]?.state);return Number.isFinite(i)?{eid:t.primary,text:`${Math.round(i)}%`}:null}(e,t);if(n)return{source:{kind:"humidity",eid:n.eid,text:n.text},text:n.text,fallback:null}}const n=i.visualSources.filter(e=>!e.eid.startsWith("marker:"));let o=n.map(e=>e.eid);const a=n.find(e=>"power_gate"===e.role);if(a&&(o=[a.eid]),!o.length&&i.visualSources.some(e=>e.eid.startsWith("marker:"))&&(o=Yn(e,t.entities),!o.length&&t.primary&&e?.states?.[t.primary]&&(o=[t.primary])),o=[...new Set(o)],1!==o.length)return{source:null,text:null,fallback:o.length?"value_ambiguous_sources":"value_no_state"};const r=o[0],l=function(e,t){const i=e?.states?.[t];if(!i||null==i.state||""===String(i.state).trim())return{text:"",fallback:"value_no_state"};const s=i.state;if(!["string","number","boolean"].includes(typeof s))return{text:"",fallback:"value_non_scalar"};const n=String(s).trim().toLowerCase();if("unknown"===n||"unavailable"===n)return{text:"",fallback:"value_no_state"};const o=Xi(e,t);return o?{text:Zi(o,String(i.attributes?.unit_of_measurement||"")),fallback:null}:{text:"",fallback:"value_no_state"}}(e,r);return l.fallback?{source:null,text:null,fallback:l.fallback}:{source:{kind:"entity",eid:r,text:l.text},text:l.text,fallback:null}}function P_(e,t,i){const s=e.marker?.binding||(e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:e.virtual?"virtual":""),n=[...t.visualSources.map(e=>`${e.role}:${e.eid}`),...t.criticalSources.map(e=>`critical:${e.eid}`)].sort(),o=i?`${i.kind}:${i.eid}:${i.attribute||""}`:"none";return[s,t.sourceKind,...n,`value:${o}`].join("\n")}function T_(e,t,i){return P_(t,i||C_(e,t),null).replace(/\nvalue:none$/,"")}function I_(e){if(e.effectiveHidden)return[];if("static_icon"===e.display)return["static-icon"];const t=[],{visual:i}=e;return"alarm"===e.pulse.kind?t.push("alarm"):"unavailable"===i.availability?t.push("unavail"):"working"===i.status?t.push("on"):"open"===i.status&&t.push("open"),e.lockState&&t.push(`lock-${e.lockState}`),"none"!==e.pulse.reason&&"alarm"!==e.pulse.reason&&t.push("activity-"+e.pulse.reason),e.pulse.generation%2==0&&(t.push("pulse-gen2"),"short"===e.pulse.kind&&t.push("activity-gen2")),t}function R_(e,t,i){const s=Bi(t.marker?.display),n="static_icon"===s,o=n&&!1===i.sourceDetails?k_:C_(e,t,i.lightDevices||[t],i.lightSources,i.registryHass||e),a=t.bindingStatus,r="ha_disabled"===a?.kind,l="orphaned"===a?.kind,c=r?"ha_disabled":l?"orphaned":"active",h=!0===t.userHidden||!0===t.marker?.hidden,d=dn(o.samples),p=o.visualSources.find(e=>e.eid.startsWith("lock.")),u=p?["unlocked","open"].includes(p.state.toLowerCase())?"unlocked":"locked"===p.state.toLowerCase()?"locked":null:null,_=!bn(t.marker)&&Zn(t.marker?.binding,t.marker?.controls??t.controls,t.entities).length>0,m="controls"===o.sourceKind||_&&"light"!==o.sourceKind&&"cover"!==o.sourceKind,g=n&&!1===i.sourceDetails?{source:null,text:null,fallback:null}:D_(e,t,o,i.showTemperature),f=P_(t,o,g.source),v=T_(e,t,o),b=i.activityRuntime,y=i.now??Date.now(),w=b?.expiresAt||(b?.flashTs?b.flashTs+3300:0),k=b?.sources===v&&b.flashTs&&b.flashKind&&w>y?b.flashKind:null,x=function(e){const t=[],i="ha_disabled"===e.bindingLifecycle||"orphaned"===e.bindingLifecycle;let s=!1;"ha_disabled"===e.bindingLifecycle?(s=!0,t.push("lifecycle.ha_disabled_hidden")):e.userHidden&&!e.designPreview?(s=!0,t.push("lifecycle.user_hidden")):e.userHidden?t.push("lifecycle.user_hidden_preview"):"orphaned"===e.bindingLifecycle?t.push("lifecycle.orphaned_diagnostic"):t.push("lifecycle.active");const n="static_icon"===e.display;let o=e.controllerFace?{...e.sourceVisual,availability:e.controllerAvailability}:e.sourceVisual;t.push(e.controllerFace?"available"===e.controllerAvailability?"availability.controller_available":"availability.controller_unavailable":"availability.source"),s?(o=v_,t.push("face.hidden")):n?(o=v_,t.push("face.static")):"alarm"===e.sourceVisual.status||e.liveStates?t.push("face.dynamic"):(o=v_,t.push("face.live_states_disabled")),!n&&!s&&e.liveStates&&"alarm"!==e.sourceVisual.status&&e.shortActivity&&(o={...o,activity:e.shortActivity},t.push(`activity.short_${e.shortActivity}`)),"alarm"===o.status?t.push("status.alarm"):"unavailable"===o.availability?t.push("status.unavailable"):"working"===o.status?t.push("status.working"):"open"===o.status?t.push("status.open"):t.push("status.neutral");const a="value"===e.display&&!s&&e.valueAvailable?"value":"icon";t.push("value"===a?"content.value":"value"===e.display?"content.value_fallback_icon":"content.icon"),"value"===e.display&&!e.valueAvailable&&e.valueFallback&&t.push(`content.${e.valueFallback}`);const r=e.liveStates&&!n&&!s,l=!n&&!s,c=!s&&!i&&!n&&"available"===o.availability&&("alarm"===o.status||e.liveStates&&"icon_ripple"===e.display),h=l&&e.vacuumLiveRequested;return t.push(r?"diagnostics.dynamic_icon":"diagnostics.base_icon"),t.push(l?"diagnostics.metrics_enabled":"diagnostics.metrics_suppressed"),t.push(c?"activity.pulse_eligible":"activity.pulse_suppressed"),t.push(h?"diagnostics.vacuum_live":"diagnostics.vacuum_static"),{effectiveHidden:s,bindingUnavailable:i,visual:o,face:a,dynamicIcon:r,metrics:l,liveColor:r,pulseEligible:c,vacuumLive:h,decisionIds:t}}({bindingLifecycle:c,userHidden:h,designPreview:!0===i.designPreview,display:s,liveStates:i.liveStates,sourceVisual:d,controllerFace:m,controllerAvailability:m?S_(e,t):"available",shortActivity:k,valueAvailable:null!=g.text,valueFallback:g.fallback,vacuumLiveRequested:!0===t.marker?.vacuum?.live}),{effectiveHidden:$,visual:S}=x,M="icon_ripple"===s&&!$&&i.liveStates&&"alarm"!==S.status?S.activity:"none",C=x.metrics&&i.showTemperature?!0===t.marker?.use_climate_temp?go(e,t.entities):"mdi:thermometer"===t.icon||"mdi:air-filter"===t.icon?mo(e,t.entities):null:null,D=x.metrics&&i.showTemperature&&t.primary&&fo(e,t.primary)?vo(e,t.entities):null,P=x.metrics&&i.showSignal&&!t.virtual?_o(e,t.entities):null,T=i.lightSources||("derived_marker_state"===t.marker?.value_badge?.source?.kind?ho(e,i.lightDevices||[t]):[]),I=function(e,t,i){const s=null!=t.marker?.value_badge;if(i.effectiveHidden||"static_icon"===i.display)return null;const n=t.marker?.value_badge;if(s)return n?.enabled?n.source?{configured:!0,enabled:!0,position:n.position||"right",...Ho(e,t,n.source,i.markerStates)}:{configured:!0,enabled:!0,source:null,sourceLabel:"",text:"—",fullText:e?.localize?.("state.default.unavailable")||"Unavailable",position:n.position||"right",availability:"missing",isLqi:!1,tone:"default"}:null;if(!i.showTemperature||"value"===i.display)return null;const o=Lo(e,t);if(!o)return null;let a=null,r="default";if("entity_attribute"===o.kind&&"current_temperature"===o.attribute){const i=go(e,t.entities);null!=i&&(a=`${i}°`),r="temperature"}else if("mdi:thermometer"===t.icon||"mdi:air-filter"===t.icon){const i=mo(e,t.entities);null!=i&&(a=`${i}°`),r="temperature"}else{const i=vo(e,t.entities);null!=i&&(a=`${i}%`),r="humidity"}return null==a?null:{configured:!1,enabled:!0,source:o,position:"right",sourceLabel:"entity_attribute"===o.kind?`${No(o.attribute)} · ${Fo(e,o.entity_id)}`:"entity_state"===o.kind?Fo(e,o.entity_id):"",text:a,fullText:a,availability:"available",isLqi:!1,tone:r}}(e,t,{showTemperature:i.showTemperature,showSignal:i.showSignal,display:s,effectiveHidden:$,markerStates:T.filter(e=>e.key.startsWith("marker:")).map(e=>({ref:e.key,on:e.on,name:e.device.name}))}),R=o.visualSources.find(e=>!e.eid.startsWith("marker:")),A="cover"===o.sourceKind?R?.eid:t.primary||R?.eid,z=A?e?.states?.[A]:null,F=x.dynamicIcon?vs(t.icon,A?.split(".")[0],z?.attributes?.device_class,z?.state,!!t.marker?.icon):t.icon,N=x.liveColor&&o.visualSources.filter(e=>!e.eid.startsWith("marker:")).map(t=>function(e){return e&&"on"===e.state?ze(e.attributes?.rgb_color):null}(e?.states?.[t.eid])).find(e=>!!e)||null,E=Number(t.marker?.size)>0?Number(t.marker.size):1,O=Number(t.marker?.angle)||0,H=Number(t.marker?.ripple_size)>0?Number(t.marker.ripple_size):1.5,L=Ie(t.marker?.ripple_color,null),B=n?null:L||N||null,j=function(e){const{display:t,visual:i,semanticActivity:s,liveStates:n,effectiveHidden:o,bindingUnavailable:a=!1,reducedMotion:r=!1}=e,l=Math.max(1,Math.trunc(e.shortGeneration||1)),c=Number.isFinite(e.diameterScale)?Math.max(1,Number(e.diameterScale)):1.5,h=e.color||function(e,t){return"presence"===t?"#1DC21D":"running"===t||"working"===e.status||"open"===e.status?"#F0A00C":"#0C82F0"}(i,s);if(o||a||"unavailable"===i.availability||"static_icon"===t)return Yo(l,h,c);if("alarm"===i.status)return{kind:"alarm",reason:"alarm",generation:l,expiresAt:null,color:"#F0410C",diameterScale:1.5,animated:!r,reducedMotionIndicator:"none"};if(!n||"icon_ripple"!==t)return Yo(l,h,c);const d=e.now??Date.now(),p=e.shortExpiresAt||null;return e.shortReason&&null!=p&&p>d?{kind:"short",reason:e.shortReason,generation:l,expiresAt:p,color:h,diameterScale:r?1:c,animated:!r,reducedMotionIndicator:r?"dot":"none"}:"presence"===s||"transition"===s||"running"===s?{kind:"continuous",reason:s,generation:l,expiresAt:null,color:h,diameterScale:r?1:c,animated:!r,reducedMotionIndicator:r?"dot":"none"}:Yo(l,h,c)}({display:s,visual:S,semanticActivity:d.activity,shortReason:b?.sources===v?b?.flashKind:null,shortGeneration:b?.gen,shortExpiresAt:b?.sources===v?w:null,now:y,liveStates:i.liveStates,effectiveHidden:$,bindingUnavailable:x.bindingUnavailable,reducedMotion:i.reducedMotion,color:B,diameterScale:H}),q="value"===x.face?g.text:null,W="ha_disabled"===a?.kind?a.reason:null,U="ha_disabled"===(G={lifecycle:c,display:s,liveStates:i.liveStates,sourceKind:o.sourceKind,primaryDomain:(t.primary||"").split(".")[0],visual:S,activity:M}).lifecycle?"ha_disabled":"orphaned"===G.lifecycle?"orphaned":"static_icon"===G.display?"static_icon":"alarm"===G.visual.status?"alarm":G.liveStates?"unavailable"===G.visual.availability?"unavailable":"cover"===G.sourceKind?"cover_icon_state":"presence"===G.activity?"presence":"event"===G.activity?"event":"transition"===G.activity?"transition":"working"===G.visual.status?"icon_ripple"===G.display&&"none"!==G.activity?"working_activity":"working":"open"===G.visual.status?"open":"media_player"===G.primaryDomain?"media_neutral":"neutral":"live_states_disabled";var G;const V=[];i.designPreview&&h&&V.push("hidden_design_preview"),n||!0!==t.marker?.vacuum?.live||V.push("vacuum_live_plan_only");const K=o.visualSources.some(t=>"power_gate"===t.role||rn(e,t.eid)),Y=t.entities.filter(t=>t.startsWith("switch.")&&!e?.entities?.[t]?.entity_category).length;K&&Y>1&&V.push("composite_power_source"),"icon_ripple"!==s&&"static_icon"!==s&&"alarm"!==d.status&&"none"!==d.activity&&V.push("activity_display_disabled");const J={binding:t.marker?.binding||(t.bindingKind&&t.bindingRef?`${t.bindingKind}:${t.bindingRef}`:t.virtual?"virtual":""),sourceKind:o.sourceKind,visualSources:o.visualSources,criticalSources:o.criticalSources,valueSource:g.source,sourceSignature:f,decisionIds:[...o.decisionIds,...x.decisionIds,...I?["diagnostics.value_badge"]:[],...null==P?[]:[`diagnostics.lqi_${b_(P)}`],`pulse.${j.kind}_${j.reason}`],visual:S,lockState:u,display:s,icon:F,valueText:q,valueFullText:q,fallbackReason:"value"!==s||q?null:g.fallback,activity:M,activityGeneration:b?.gen||1,pulse:j,classes:[],tempText:null==C?null:String(C),humText:null==D?null:String(D),valueBadge:I,lqiText:null==P||I?.isLqi?null:String(P),lqiColor:null==P?null:y_(P),lqiBand:null==P?null:b_(P),lightColor:N,scale:E,angle:O,rippleScale:H,rippleColor:B,userHidden:h,effectiveHidden:$,haDisabled:r,disabledReason:W,orphaned:l,vacuumLive:x.vacuumLive,explanation:{reason:U,notices:V}};return{...J,classes:I_(J)}}function A_(e,t=new WeakMap){if(null===e||"object"!=typeof e)return e;const i=e,s=t.get(i);if(s)return s;if(Array.isArray(e)){const s=[];t.set(i,s);for(const i of e)s.push(A_(i,t));return Object.freeze(s)}const n={};t.set(i,n);for(const[i,s]of Object.entries(e))"function"!=typeof s&&(n[i]=A_(s,t));return Object.freeze(n)}function z_(e){const t=new Map(e);let i;return i=Object.freeze({get size(){return t.size},get:e=>t.get(e),has:e=>t.has(e),entries:()=>t.entries(),keys:()=>t.keys(),values:()=>t.values(),forEach:(e,s)=>t.forEach((t,n)=>e.call(s,t,n,i)),[Symbol.iterator]:()=>t[Symbol.iterator]()}),i}function F_(e){const t=new Set(e.entityIds||[]),i=new Set(e.deviceIds||[]),s=new Set(e.areaIds||[]);for(const s of e.devices){for(const e of s.entities||[])t.add(e);s.primary&&t.add(s.primary);for(const e of s.controls||[])t.add(e);s.marker?.vacuum?.source&&t.add(s.marker.vacuum.source),"device"===s.bindingKind&&s.bindingRef&&i.add(s.bindingRef),"entity"===s.bindingKind&&s.bindingRef&&t.add(s.bindingRef)}const n={},o={};for(const[o,a]of Object.entries(e.hass?.entities||{})){const r=a?.device_id?e.hass?.devices?.[a.device_id]:null;(t.has(o)||a?.device_id&&i.has(a.device_id)||s.has(a?.area_id)||s.has(r?.area_id))&&(t.add(o),n[o]=a,a?.device_id&&i.add(a.device_id))}for(const t of i){const i=e.hass?.devices?.[t];i&&(o[t]=i)}const a={};for(const i of t){const t=e.hass?.states?.[i];t&&(a[i]=t)}const r=Object.freeze({states:A_(a),entities:A_(n),devices:A_(o),config:A_(e.hass?.config),locale:A_(e.hass?.locale),themes:A_(e.hass?.themes)});return Object.freeze({sourceSequence:e.sourceSequence,capturedAt:e.capturedAt??Date.now(),hass:r,devices:A_([...e.devices]),positions:z_([...e.positions||[]].map(([e,t])=>[e,Object.freeze({x:t.x,y:t.y})])),presentations:z_([...e.presentations].map(([e,t])=>[e,A_(t)])),facts:z_([...e.facts||[]].map(([e,t])=>[e,A_(t)]))})}const N_=(e,t)=>`${e}:${t?1:0}`;function E_(e){return!Number.isFinite(e)||e<=0?2.25:e/2.5*2.25}function O_({openings:e,geometries:t,fillsByRoomId:i,idPrefix:s="data",groupClass:n="opening-tunnels",dataLayer:o="data"}){const a=e.map((e,n)=>{const o=t[n];if(!o)return G;const a=o.faces.find(e=>-1===e.side),r=o.faces.find(e=>1===e.side);if(!a||!r)return G;const l=i.get(a.roomId)||null,c=i.get(r.roomId)||null;if(!l&&!c)return G;const h=`${a.d} ${r.d}`,d=!!l&&!!c&&l.color===c.color&&l.opacity===c.opacity,p=`translate(${e.rx} ${e.ry}) rotate(${e.angle})`;if(d)return W`<path class="opening-tunnel" data-hp="opening-tunnel" data-id=${e.id} data-kind=${e.type}
|
||
data-wall-key=${o.wallKey} aria-hidden="true" pointer-events="none"
|
||
transform=${p} d=${h} fill=${l.color}
|
||
fill-opacity=${l.opacity} fill-rule="nonzero"></path>`;const u=o.maxY-o.minY;if(!(u>0))return G;const _=`${(100*Math.max(0,Math.min(1,-o.minY/u))).toFixed(6)}%`,m=l||{color:"#000000",opacity:0},g=c||{color:"#000000",opacity:0},f=`hp-opening-tunnel-${s.replace(/[^a-zA-Z0-9_-]/g,"-")}-${n}`;return W`<g class="opening-tunnel" data-hp="opening-tunnel" data-id=${e.id} data-kind=${e.type}
|
||
data-wall-key=${o.wallKey} aria-hidden="true" pointer-events="none"
|
||
transform=${p}>
|
||
<defs><linearGradient id=${f} gradientUnits="userSpaceOnUse"
|
||
x1="0" y1=${o.minY} x2="0" y2=${o.maxY}>
|
||
<stop offset="0%" stop-color=${m.color} stop-opacity=${m.opacity}></stop>
|
||
<stop offset=${_} stop-color=${m.color} stop-opacity=${m.opacity}></stop>
|
||
<stop offset=${_} stop-color=${g.color} stop-opacity=${g.opacity}></stop>
|
||
<stop offset="100%" stop-color=${g.color} stop-opacity=${g.opacity}></stop>
|
||
</linearGradient></defs>
|
||
<path d=${h} fill=${`url(#${f})`} fill-rule="nonzero"></path>
|
||
</g>`});return W`<g class=${n} data-layer=${o} aria-hidden="true" pointer-events="none">${a}</g>`}const H_=e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(Number(e[0]))&&Number.isFinite(Number(e[1])),L_=(e,t,i)=>[Number(e[0])*i,Number(e[1])*i,Number(t[0])*i,Number(t[1])*i],B_=e=>{const t=`${Number(e[0]).toFixed(9)},${Number(e[1]).toFixed(9)}`,i=`${Number(e[2]).toFixed(9)},${Number(e[3]).toFixed(9)}`;return t<=i?`${t}|${i}`:`${i}|${t}`},j_=e=>{const t=new Map;for(const i of e.flat())!Array.isArray(i)||i.length<4||!i.every(Number.isFinite)||Math.hypot(i[2]-i[0],i[3]-i[1])<=1e-9||t.set(B_(i),[...i]);return[...t.values()]};function q_(e){return"solid"===e?.zero_wall_style?"solid":"dashed"}function W_(e,t=1,i){const s=[];for(const n of Array.isArray(e?.wall_segments)?e.wall_segments:[])0===Number(n?.cm)&&H_(n?.a)&&H_(n?.b)&&(i&&!i.has(String(n.id||""))||s.push(L_(n.a,n.b,t)));return j_([s])}function U_(e,t=1){const i=[];for(const s of Array.isArray(e?.partitions)?e.partitions:[])0===Number(s?.cm)&&H_(s?.a)&&H_(s?.b)&&i.push(L_(s.a,s.b,t));for(const s of Array.isArray(e?.room_drafts)?e.room_drafts:[]){const e=Array.isArray(s?.points)?s.points:[];for(let n=0;n+1<e.length;n++)0===Number(s?.segments?.[n]?.cm)&&H_(e[n])&&H_(e[n+1])&&i.push(L_(e[n],e[n+1],t))}return j_([i])}function G_(e,t,i,s){if(!e)return[];const n=Od(e.open_spans);return Hd(t,n.length?n:null,i,s,!0)}function V_(e,t,i,s){const n=q_(e),o=new Set;for(const e of t.rooms||[])for(const t of Array.isArray(e.wall_ids)?e.wall_ids:[])"string"==typeof t&&t&&o.add(t);const a=j_([W_(t,1,o.size?o:void 0),G_(e,t.rooms,i,s)]),r=j_([a,U_(t,1)]);return{style:n,lines:r,contour:a,barriers:"solid"===n?r:[],transmissive:"dashed"===n?a:[]}}function K_(e,t){return(e||[]).some(e=>e?.host?.kind===t.kind&&e.host.id===t.id)}const Y_=new WeakMap,J_=new WeakMap;function X_(e){const t=Eh(e.cfg),i={};for(const t of e.cfg.spaces||[])for(const e of t.rooms||[])e.area&&(i[e.area]=t.id);const s=e.cfg.settings?.exclude_integrations?new Set(e.cfg.settings.exclude_integrations):Ye,n=Xe(e.cfg.settings?.icon_rules?.length?e.cfg.settings.icon_rules:Je);return So({hass:e.hass,registry:e.registry,areaToSpace:i,markers:e.cfg.markers||[],settings:e.cfg.settings||{},excluded:s,showAll:!!e.cfg.settings?.show_all,firstSpaceId:t[0]?.id||"",loc:t=>ua(e.lang,t),iconRules:n})}function Z_(e){const t=Eh(e.cfg).find(t=>t.id===e.spaceId);if(!t)return null;const i=cs(e.cfg.spaces.find(t=>t.id===e.spaceId)),s=ps(e.cfg.settings),n=e.iconSize??2.5,o=n>8?2.5:n,a=E_(o),r=e.registry?En(e.hass,e.registry):e.hass,l=e.registry?On(e.hass,e.registry):e.hass,c=(e.devices||X_(e)).filter(t=>t.space===e.spaceId),h=c.filter(e=>!e.hidden),d=function(e,t,i){const s={},n=i/100*nd(t)*1.3;for(const i of t.rooms){if(!i.area)continue;const t=e.filter(e=>e.area===i.area);if(!t.length)continue;const o=rd(i),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(t.length*r/Math.max(l,1)))),h=r/c,d=l/Math.max(Math.ceil(t.length/c),1),p=t.map((e,t)=>({x:o.x+a+h*(t%c+.5),y:o.y+a+d*(Math.floor(t/c)+.5)}));Oi(p,o,n,.5*a),t.forEach((e,t)=>s[e.id]=Wh(p[t]))}return s}(c,t,o),p=[];for(const t of h){const i=e.layout[t.id];if(i&&i.s===e.spaceId){const e=i.x*Ih,t=i.y*Ih;p.push({minX:e,minY:t,maxX:e,maxY:t})}}const u=e.cfg.spaces.find(t=>t.id===e.spaceId)||{},_=Array.isArray(u.walls)?u.walls:[],m=Number(u.cell_cm)>0?Number(u.cell_cm):5,g=V_(u,t,Ih,.02*Bh),f=(u.openings||[]).flatMap(e=>{if("partition"!==e.host?.kind)return[];const i=tu(e,t.partitions,Ih,m,Bh).resolved;return i?[i]:[]}),v=Gd({partitions:t.partitions,roomDrafts:t.room_drafts,columns:t.wall_columns,cellCm:m,hostedOpenings:f.map(e=>({id:e.opening.id,host:e.host,length:e.length,type:e.opening.type}))}),b=function(e,t,i,s){let n=J_.get(e);n||(n=new Map,J_.set(e,n));const o=n.get(t);if(o?.fingerprint===i)return o.value;const a=s();return n.set(t,{fingerprint:i,value:a}),a}(e.cfg,t.id,v,()=>_h(t,m,Bh,2e-4*Bh,f.map(nu)).all);for(const e of b){const t=e.map(e=>e[0]),i=e.map(e=>e[1]);t.length&&p.push({minX:Math.min(...t),minY:Math.min(...i),maxX:Math.max(...t),maxY:Math.max(...i)})}for(const e of g.lines)p.push({minX:Math.min(e[0],e[2]),minY:Math.min(e[1],e[3]),maxX:Math.max(e[0],e[2]),maxY:Math.max(e[1],e[3])});const y=id(t,p),w=[y.x,y.y,y.w,y.h],k=new Map(t.rooms.map(t=>{const n=zs(i.fill,t);return[t,_s(n,"lqi"===n&&t.area?Do(r,c,t.area):null,"light"===n?uo(ho(r,c,t,e.virtualLights)):"none","temp"===n&&t.area?Po(r,c,t.area):null,i.tempMin,i.tempMax,s,ls(i.customFill,t))]})),x=new Map;for(const e of t.rooms)e.id&&x.set(e.id,k.get(e)||null);const $=function(e,t=1e3){const i=[];for(const[s,n]of(e||[]).entries()){if("passage"!==n?.type)continue;const e={...n,id:String(n.id||`passage-${s}`),type:"passage",rx:Number(n.x)*t,ry:Number(n.y)*t,rlen:Number(n.length)*t};[e.rx,e.ry,e.rlen,e.angle].every(Number.isFinite)&&e.rlen>0&&i.push(e)}return i}((u.openings||[]).flatMap(e=>{if(!e.host||"wall"===e.host.kind)return[e];const t=f.find(t=>t.opening.id===e.id);return t?[lu(e,t,Ih)]:[]}),Ih),S=wc(t.rooms,_,g.contour,jh,m,Bh,Ih),M=f.filter(e=>ru(e,S,2e-4*Bh)).map(e=>({x:e.center[0],y:e.center[1],angle:e.angle,length:e.length})),C=t.rooms.filter(e=>e.area||i.showBorders||"none"!==zs(i.fill,e)).map(e=>{let s="room "+(t.bg?"overlay":"yard"),n="";const o=zs(i.fill,e);if(i.showBorders||"none"!==o){s+=" styled";const t=[`--room-stroke:${i.color}`,`--room-stroke-op:${i.showBorders&&!g.contour.length?i.opacity:0}`],o=k.get(e)||null;o?(s+=" filled",t.push(`--room-fill:${o.color}`,`--room-fill-op:${o.opacity.toFixed(3)}`)):t.push("--room-fill:transparent","--room-fill-op:0"),n=t.join(";")}const a=e.id||G,r=e.area||G,l=e.poly?W`<polygon class="${s}" style="${n}" data-hp="room" data-id=${a} data-area=${r}
|
||
points="${e.poly.map(e=>e.join(",")).join(" ")}"></polygon>`:W`<rect class="${s}" style="${n}" data-hp="room" data-id=${a} data-area=${r}
|
||
x="${e.x}" y="${e.y}" width="${e.w}" height="${e.h}" rx="${.03*Math.min(e.w,e.h)}"></rect>`;return l}),D=t.rooms.filter(e=>{const t=k.get(e);return Fs(i.glow,e)&&(!t||t.opacity<=0)}).map(e=>e.poly?W`<polygon class="glow-base" aria-hidden="true" pointer-events="none"
|
||
data-room-id=${e.id||G}
|
||
points="${e.poly.map(e=>e.join(",")).join(" ")}"
|
||
fill=${s.glow_base.c} fill-opacity=${s.glow_base.a}></polygon>`:W`<rect class="glow-base" aria-hidden="true" pointer-events="none"
|
||
data-room-id=${e.id||G}
|
||
x=${e.x} y=${e.y} width=${e.w} height=${e.h}
|
||
rx=${.03*Math.min(e.w,e.h)}
|
||
fill=${s.glow_base.c} fill-opacity=${s.glow_base.a}></rect>`),P=new Map;for(const e of t.rooms)if(e.id){const t=k.get(e)||null;P.set(e.id,Fs(i.glow,e)&&(!t||t.opacity<=0)?{color:s.glow_base.c,opacity:s.glow_base.a,mode:"glow"}:null)}const T=ho(r,h,null,e.virtualLights),I=h.map(s=>{const n=function(e,t,i,s,n){const o=t[e.id];return o&&o.s===e.space?{x:o.x*Ih,y:o.y*Ih}:s[e.id]?s[e.id]:Wh(sd(n))}(s,e.layout,e.cfg,d,t),o=(n.x-w[0])/w[2]*100,a=(n.y-w[1])/w[3]*100,c=i.showLqi??!1!==e.showSignal,p=e.presentations?.get(N_(s.id,c))||R_(r,s,{liveStates:!1!==e.liveStates,showTemperature:!1!==e.showTemperature,showSignal:c,activityRuntime:e.activityRuntime?.get(s.id),sourceDetails:!1,lightDevices:h,lightSources:T,registryHass:l,reducedMotion:e.reducedMotion}),u=[`left:${o}%`,`top:${a}%`,...Go(p)],_=w_(p),m=[s.name,ua(e.lang,`marker.state_a11y_${_}`),"none"!==p.pulse.kind?ua(e.lang,`marker.pulse_a11y_${p.pulse.reason}`):"",p.valueFullText||p.valueText||"",Io(p.valueBadge),null!=p.lqiText&&p.lqiBand?ua(e.lang,`marker.lqi_a11y_${p.lqiBand}`,{value:p.lqiText}):""].filter(Boolean).join(", ");return q`<div class="dev ${Uo(r)} ${p.classes.join(" ")} ${s.virtual?"virtual":""} ${null!=p.valueText?"valonly":""}"
|
||
data-hp="device" data-id="${s.id}" data-entity=${s.primary||G} data-area=${s.area||G}
|
||
role="img" aria-label=${m}
|
||
data-state=${_}
|
||
data-lqi-band=${null!=p.lqiText&&p.lqiBand||G}
|
||
data-binding-status=${"ha_disabled"===s.bindingStatus?.kind?"ha-disabled":s.bindingStatus?.kind||"active"}
|
||
data-disabled-reason=${p.disabledReason?p.disabledReason.replace("_","-"):G}
|
||
style="${u.join(";")}">
|
||
${Ko(p,{})}
|
||
</div>`}),R=i.showNames?t.rooms.filter(e=>e.name).map(s=>{const n=ld(s,t.id,e.layout,e.cfg),o=(n.x-w[0])/w[2]*100,a=(n.y-w[1])/w[3]*100,r=Math.min(1,i.opacity+.25);return q`<div class="roomlabel"
|
||
data-hp="room-label" data-id=${s.id||G} data-area=${s.area||G}
|
||
style="left:${o}%;top:${a}%;color:${i.color};opacity:${r}">${s.name}</div>`}):[],A=t.bg?e.displayUrl?e.displayUrl(t.bg.href):t.bg.href:"",z=e.cfg.spaces.find(t=>t.id===e.spaceId)?.settings||{},F="daynight"===Fr(e.cfg?.settings,z)?$r(r,e.dayCycleNow??new Date):null,N=hs(e.cfg?.settings,i),E=!!(_.length||b.length&&i.showBorders),O=E?Gd($.length?{rooms:t.rooms,walls:_,extras:b,cellCm:m,zero:g.contour,passages:$.map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),hostedCompositeOpenings:M}:{rooms:t.rooms,walls:_,extras:b,cellCm:m,zero:g.contour}):"",H=E?function(e,t,i,s){let n=Y_.get(e);n||(n=new Map,Y_.set(e,n));const o=n.get(t);if(o?.fingerprint===i)return o.value;const a=s();return n.set(t,{fingerprint:i,value:a}),a}(e.cfg,t.id,O,()=>Wc(t.rooms,_,g.contour,[...$.filter(e=>"partition"!==e.host?.kind).map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),...M],jh,m,Bh,Ih,b)):null,L=$.length&&_.length?function(e,t,i,s,n,o,a,r=1){return[n,o,a,r].every(Number.isFinite)&&o>0&&r>0&&i?.length?th(Kc(e,i,s,n,o,a,r),t):t.map(()=>null)}(t.rooms,$.map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),_,g.contour,jh,m,Bh,Ih):$.map(()=>null),B=$.length?O_({openings:$,geometries:L,fillsByRoomId:x,idPrefix:`${t.id}-static-data`,groupClass:"opening-tunnels static-opening-tunnels",dataLayer:"data"}):G,j=$.length?O_({openings:$,geometries:L,fillsByRoomId:P,idPrefix:`${t.id}-static-glow`,groupClass:"opening-tunnels glow-base-tunnels static-opening-tunnels",dataLayer:"glow-base"}):G,U=_.length&&H?.paperD?[{path:H.paperD}]:di(t.rooms),V=i.showBorders?H:null,K=e.stageWidth&&w[2]?e.stageWidth/w[2]:1,Y=kl(m),J=!!V&&(wl(V.depthUnits,K)||xl(Y,K)),X=i.color||"#607d8b",Z=i.hideOpenings?[]:f.map(e=>{const t=e.opening,i="passage"!==t.type&&t.contact?r.states?.[t.contact]?.state:null,s=mi(t.type,i,!!t.invert),n=s>0&&!!t.contact,o="gate"===t.type?!t.flip_v:!!t.flip_v,a={type:t.type,length:e.length,angle:e.angle,amount:s,flipH:!!t.flip_h,flipV:!!t.flip_v,base:X,tone:n?"var(--hp-open)":X,cellCm:m,gridPitch:Bh,face:ou(e,o)};return W`<g class="opening static-opening" data-hp="opening"
|
||
data-id=${t.id} data-kind=${t.type} pointer-events="none"
|
||
transform="translate(${e.center[0]} ${e.center[1]}) rotate(${e.angle})">
|
||
${f_(a)}
|
||
</g>`});return q`
|
||
<div class="hp-static-stage${F?` daycycle phase-${F.phase}`:""}"
|
||
?inert=${!!e.inert}
|
||
style="aspect-ratio:${w[2]}/${w[3]}${N?";background:"+N:""};--hp-cell-visual-scale:${Ch(m)};--wall-fill:${s.wall_fill.c};--wall-fill-op:${s.wall_fill.a}${F?`;${Hr(F)}`:""}">
|
||
${Lr(F)}
|
||
<svg viewBox="${w[0]} ${w[1]} ${w[2]} ${w[3]}" preserveAspectRatio="xMidYMid meet">
|
||
${V?W`<defs>
|
||
<pattern id="hp-wall-hatch" patternUnits="userSpaceOnUse"
|
||
width="${Y}" height="${Y}" patternTransform="rotate(45)">
|
||
<path d="M0 0 L0 ${Y}" stroke="${X}"
|
||
stroke-width="${Y/8*2}"></path>
|
||
</pattern>
|
||
</defs>`:G}
|
||
<g class="hp-paperg">${U.map(e=>"path"in e?W`<path class="hp-paper" d="${e.path}" fill-rule="evenodd"></path>`:"poly"in e?W`<polygon class="hp-paper" points="${e.poly}"></polygon>`:W`<rect class="hp-paper" x="${e.rect.x}" y="${e.rect.y}" width="${e.rect.w}" height="${e.rect.h}" rx="${e.rect.rx}"></rect>`)}</g>
|
||
${A?W`<image href="${A}" x="${t.bg.x}" y="${t.bg.y}" width="${t.bg.w}" height="${t.bg.h}"
|
||
@load=${()=>e.assetLoaded?.(t.bg.href,A)}
|
||
transform=${t.bg.angle?`rotate(${t.bg.angle} ${t.bg.x+t.bg.w/2} ${t.bg.y+t.bg.h/2})`:G}
|
||
preserveAspectRatio="none" />`:G}
|
||
${C}
|
||
${i.showBorders&&g.contour.length?W`<g class="room-outlines" aria-hidden="true" pointer-events="none">
|
||
${t.rooms.map(e=>{const t=hi(e);if(!t)return G;const s=Ps(t,g.contour,.02*Bh);return W`<path class="room-outline" fill="none" stroke=${i.color}
|
||
stroke-opacity=${i.opacity}
|
||
stroke-width=${Dh(2.5,m)}
|
||
d=${s.map(e=>`M ${e[0]} ${e[1]} L ${e[2]} ${e[3]}`).join(" ")}></path>`})}
|
||
</g>`:G}
|
||
${B}
|
||
${D.length?W`<g class="glow-base-layer" aria-hidden="true" pointer-events="none">${D}</g>`:G}
|
||
${j}
|
||
${V?W`<g class="wallbodies" style="--room-stroke:${X}">
|
||
${V.paths.map(e=>W`
|
||
<path class="wallbody-fill" data-component=${e.id} d="${e.d}"
|
||
fill="${s.wall_fill.c}" fill-opacity="${s.wall_fill.a}"
|
||
fill-rule=${e.fillRule} stroke="none" pointer-events="none"></path>
|
||
<path class="wallbody ${J?"solid":""}"
|
||
data-hp="wall" data-id="union" data-kind="union" data-component=${e.id}
|
||
d="${e.d}" fill="${J?"none":"url(#hp-wall-hatch)"}"
|
||
fill-rule=${e.fillRule} stroke="${X}"
|
||
stroke-width="${Dh(.6,m)}" pointer-events="none"></path>`)}
|
||
</g>`:G}
|
||
${i.showBorders&&g.lines.length?W`<g class="zero-walls ${g.style}"
|
||
data-zero-wall-style=${g.style} aria-hidden="true" pointer-events="none">
|
||
${g.lines.map(e=>W`<line class="zero-wall"
|
||
x1=${e[0]} y1=${e[1]} x2=${e[2]} y2=${e[3]}
|
||
stroke=${X} stroke-width=${Dh(2.5,m)}
|
||
stroke-dasharray=${"dashed"===g.style?`${Dh(7,m)} ${Dh(7,m)}`:G}></line>`)}
|
||
</g>`:G}
|
||
${Z}
|
||
</svg>
|
||
${""}
|
||
<div class="devlayer" style="--icon-size:${od(o,t,w[2]).toFixed(3)}cqw;--device-base-size:${od(a,t,w[2]).toFixed(3)}cqw">${I}${R}</div>
|
||
</div>
|
||
`}let Q_=null,em=null,tm=0,im=-1,sm=null,nm=[];const om=new Set,am=(e,t)=>{"function"==typeof e&&t.push(e)};function rm(){if(Q_)return Q_;try{const e=JSON.parse(localStorage.getItem("houseplan_card_cfg_v1")||"null");if(e&&e.config&&Array.isArray(e.config.spaces)){const t=e.layout||{};return{config:e.config,rev:e.rev||0,configFingerprint:e.config_fingerprint||Gd(e.config),layout:t,layoutRev:e.layout_rev||0,layoutFingerprint:e.layout_fingerprint||Gd(t),virtualLights:mn(e.virtual_lights,e.rev||0)}}}catch{}return null}function lm(e,t=!1){if(t&&(Q_=null,tm++,em))return em.catch(()=>null).then(()=>lm(e,!1));if(Q_)return Promise.resolve(Q_);if(em)return im!==tm?em.catch(()=>null).then(()=>lm(e,!1)):em;return im=tm,em=async function(e,t){const[i,s]=await Promise.all([e.callWS({type:"houseplan/config/get"}),e.callWS({type:"houseplan/layout/get"})]),n=i?.rev??0,o=Q_?gn(Q_.virtualLights,i?.virtual_lights,n,!!i&&"virtual_lights"in i):mn(i?.virtual_lights,n),a={config:i?.config??null,rev:n,configFingerprint:Gd(i?.config??null),layout:s?.layout??{},layoutRev:s?.rev??0,layoutFingerprint:Gd(s?.layout??{}),virtualLights:o};t===tm&&(Q_=a);const r=e.connection;if(r?.subscribeEvents&&sm!==r){for(const e of nm)e();nm=[],sm=r;const e=()=>{Q_=null,tm++,om.forEach(e=>e())},t=[];try{if(am(await r.subscribeEvents(e,"houseplan_config_updated"),t),am(await r.subscribeEvents(e,"houseplan_layout_updated"),t),am(await r.subscribeEvents(e=>{if(Q_){const t=wn(Q_.virtualLights,e?.data);if(t===Q_.virtualLights)return;Q_={...Q_,virtualLights:t}}else tm++;om.forEach(e=>e())},"houseplan_virtual_light_updated"),t),sm===r)nm=t;else for(const e of t)e()}catch{for(const e of t)e();sm===r&&(sm=null)}}return a}(e,tm).finally(()=>{em=null,im=-1}),em}const cm=3300;function hm(e,t){return{sources:e,last:Object.fromEntries(t.map(e=>[e.eid,e.state])),flashTs:0,flashKind:null,timer:0,gen:0,expiresAt:0,alarmActive:"alarm"===dn(t).status}}function dm(e,t,i,s){s(e.timer);const n=hm(t,i);Object.assign(e,n)}function pm(e,t,i,s,n){e.flashTs&&i-e.flashTs<cm&&"event"===e.flashKind&&"transition"===t||(e.flashTs=i,e.expiresAt=i+cm,e.flashKind=t,e.gen++,s(e.timer),e.timer=n(3360))}function um(e,t,i){"transition"===e.flashKind&&t.some(e=>"transition"===e.activity)&&(i(e.timer),e.flashTs=0,e.flashKind=null,e.expiresAt=0);if("alarm"===dn(t).status){e.alarmActive||(i(e.timer),e.flashTs=0,e.flashKind=null,e.expiresAt=0);for(const i of t)e.last[i.eid]=i.state;return e.alarmActive=!0,null}if(e.alarmActive){for(const i of t)e.last[i.eid]=i.state;return e.alarmActive=!1,null}let s=null;for(const i of t){const t=pn(e.last[i.eid],i);("event"===t||!s&&t)&&(s=t),e.last[i.eid]=i.state}return s}class _m{constructor(e,t=()=>{}){this._host=e,this._onGateChange=t,this._modality="unknown",this._gate=!1,this._media=null,this._connected=!1,this._onMediaChange=()=>this._syncGate()}get modality(){return this._modality}get hoverEnabled(){return this._gate}connect(e=globalThis.window){this._connected||(this._connected=!0,this._modality="unknown",this._media="function"==typeof e?.matchMedia?e.matchMedia("(any-hover: hover) and (any-pointer: fine)"):null,this._media?.addEventListener?.("change",this._onMediaChange),this._setGate(!1))}disconnect(){this._media?.removeEventListener?.("change",this._onMediaChange),this._media=null,this._connected=!1,this._modality="unknown",this._setGate(!1)}note(e){return this._modality=function(e,t){const i="mouse"===(s=t.pointerType)||"touch"===s||"pen"===s?s:null;var s;return i?"mouse"===i&&t.sourceCapabilities?.firesTouchEvents?e:i:e}(this._modality,e),this._syncGate(),this._modality}suspend(){this._setGate(!1)}_syncGate(){var e,t;this._setGate((e=this._modality,t=!!this._media?.matches,"mouse"===e&&t))}_setGate(e){this._gate!==e&&(this._gate=e,this._host.toggleAttribute("data-pointer-hover",e),this._onGateChange(e))}}class mm extends he{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return pa(this.hass,this._config?.language)}get _schema(){const e=this._spaces||[];return[e.length?{name:"space",selector:{select:{mode:"dropdown",options:e}}}:{name:"space",selector:{text:{}}},{name:"title",selector:{text:{}}},{name:"show_button",selector:{boolean:{}}},{name:"button_label",selector:{text:{}}},{name:"button_target",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const e=this._lang,t={space:ua(e,"editor.space"),title:ua(e,"editor.title"),show_button:ua(e,"editor.show_button"),button_label:ua(e,"editor.button_label"),button_target:ua(e,"editor.button_target"),icon_size:ua(e,"editor.icon_size"),show_temperature:ua(e,"editor.show_temperature"),live_states:ua(e,"editor.live_states"),show_signal:ua(e,"editor.show_signal")};return q`<ha-form
|
||
.hass=${this.hass}
|
||
.data=${this._config}
|
||
.schema=${this._schema}
|
||
.computeLabel=${e=>t[e.name]||e.name}
|
||
@value-changed=${this._valueChanged}
|
||
></ha-form>`}_valueChanged(e){const t={...this._config||{},...e.detail.value};delete t.aspect_ratio;const i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:t},this.dispatchEvent(i)}}mm.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",mm);const gm=e=>{history.pushState(null,"",e),((e,t,i)=>{const s=new Event(t,{bubbles:!0,composed:!0});s.detail=i??{},e.dispatchEvent(s)})(window,"location-changed",{replace:!1})};class fm extends he{constructor(){super(...arguments),this._snap=null,this._loading=!1,this._reloadQueued=!1,this._forceReloadQueued=!1,this._reloadRetryTimer=0,this._dayCycleTimer=0,this._dayCycleClockKey="",this._stageWidth=0,this._pendingStageWidth=0,this._stageWidthRaf=0,this._haRegistryConnection=null,this._haRegistryRevision=-1,this._devices=[],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._connHooked=null,this._connectionWasLost=!1,this._visibleDeviceSnapshot=null,this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._capturedSnapshotSequence=-1,this._capturedSnapshotDevices=null,this._capturedSnapshotActivity="",this._capturedSnapshotVirtual="",this._activityRuntime=new Map,this._reducedMotion=!1,this._pointerModality=new _m(this),this._onMotionChange=e=>{this._reducedMotion=e.matches,this._capturedSnapshotSequence=-1,this.requestUpdate()},this._onHaRegistryUpdate=()=>{const e=An(this.hass).revision;e!==this._haRegistryRevision&&(this._haRegistryRevision=e,this._refreshDevices(),this._capturedSnapshotSequence=-1,this.requestUpdate())},this._dayCycleTick=()=>{if(!this.isConnected||"hidden"===this.ownerDocument.visibilityState)return;const e=this._dayCycleState();if(!e)return this._dayCycleTimer&&(window.clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),void(this._dayCycleClockKey="");const t=Sr(e);t!==this._dayCycleClockKey&&(this._dayCycleClockKey=t,this.requestUpdate())},this._pageVisibility=e=>{if(this._continuity.visibility(e),this._dayCycleVisibility(e),"hidden"!==e.kind){if(!e.long){const e=Date.now();let t=!1;for(const i of this._activityRuntime.values())!i.flashKind||(i.expiresAt||i.flashTs+3300)>e||(i.flashTs=0,i.flashKind=null,i.expiresAt=0,t=!0);return void(t&&(this._capturedSnapshotSequence=-1,this.requestUpdate()))}Date.now()-this._renderSnapshotAt>1e3&&this._continuity.note("device-snapshot-stale"),this._continuityDataReady=!1,this._continuityPaintToken=-1,this._load(!0)}else this._pointerModality.suspend()},this._onConnLost=()=>{this._connectionWasLost=!0,this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.connectionLost()},this._onConnReady=()=>{!this._connectionWasLost&&this._continuity.hasCompleteFrame?this._beginContinuityCandidate("connection-ready",!1,"plan"):(this._continuityDataReady=!1,this._continuityPaintToken=-1),this._load(!0)},this._onAssetLoaded=(e,t)=>{this._signer.markLoaded(this.hass,e,t),this._continuity.note("asset-ready"),this._continuityPaintToken=-1,"steady"!==this._continuity.state&&this.requestUpdate()},this._loadedOnce=!1,this._signer=new Yd(()=>this.requestUpdate()),this._retryContinuity=()=>{this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.retry(this._continuity.recoveryReason||"plan"),this._load(!0)},this._goToSpace=()=>{const e=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");gm(`${e}#space=${encodeURIComponent(this._config.space)}`)}}_ensureHaRegistryAuthority(){const e=this.hass?.connection||null;e&&e!==this._haRegistryConnection&&(this._haRegistryRelease?.(),this._haRegistryConnection=e,this._haRegistryRevision=-1,this._haRegistryRelease=Rn(this.hass,this._onHaRegistryUpdate),this._onHaRegistryUpdate())}_newContinuityController(){return new Ud(()=>{this._continuityEpoch++,this.isConnected&&this.requestUpdate()})}_dayCycleState(e=new Date){const t=this._snap?.config;if(!t||!this._config)return null;const i=t.spaces?.find(e=>e.id===this._config?.space)?.settings||{};return"daynight"!==Fr(t.settings,i)?null:$r(this._renderDeviceSnapshot?.hass||this.hass,e)}_syncDayCycleClock(){const e=this._dayCycleState();this._dayCycleClockKey=e?Sr(e):"";const t="clock"===e?.source&&"hidden"!==this.ownerDocument.visibilityState&&this.isConnected;t&&!this._dayCycleTimer?this._dayCycleTimer=window.setInterval(this._dayCycleTick,3e4):!t&&this._dayCycleTimer&&(window.clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_dayCycleVisibility(e){"hidden"!==e.kind?(this._dayCycleTick(),this._syncDayCycleClock()):this._dayCycleTimer&&(window.clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_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)}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(e){const t=rm();return{type:"custom:houseplan-space-card",space:Eh(t?.config||null)[0]?.id||"",show_button:!0,live_states:!0,show_temperature:!0,show_signal:!0}}setConfig(e){if(!e||!e.space)throw new Error('houseplan-space-card: "space" is required');this._config={show_button:!0,button_target:"/plan-doma",live_states:!0,show_temperature:!0,show_signal:!0,...e},this._snap=this._snap||rm()}connectedCallback(){var e;this._continuityDisposed&&(this._continuity=this._newContinuityController(),this._continuityDisposed=!1,this._continuityPaintToken=-1),super.connectedCallback(),this._pointerModality.connect(this.ownerDocument.defaultView),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this.hass&&this._ensureHaRegistryAuthority(),this._continuityUnsub?.(),this._continuityUnsub=qd(this.ownerDocument,this._pageVisibility),this._unsub=(e=()=>{this._beginContinuityCandidate("config-event",!1),this._reloadQueued=!0,this._load()},om.add(e),()=>om.delete(e)),this._signer.start(()=>this.hass,()=>this._referenced())}disconnectedCallback(){this._pointerModality.disconnect(),this._continuityUnsub?.(),this._continuityUnsub=void 0,this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,this._unsub?.(),this._unsub=void 0,window.clearTimeout(this._reloadRetryTimer),this._reloadRetryTimer=0,this._dayCycleTimer&&(window.clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),this._dayCycleClockKey="",this._stageObserver?.disconnect(),this._stageObserver=void 0,this._observedStage=void 0,this._stageWidthRaf&&cancelAnimationFrame(this._stageWidthRaf),this._stageWidthRaf=0,this._pendingStageWidth=0,this._signer.dispose(),this._haRegistryRelease?.(),this._haRegistryRelease=void 0,this._haRegistryConnection=null,this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),this._connHooked=null;for(const e of this._activityRuntime.values())window.clearTimeout(e.timer);this._activityRuntime.clear(),this._continuityHistory=[...this._continuityHistory,...this._continuity.trace].slice(-80),this._continuity.dispose(),this._continuityDisposed=!0,super.disconnectedCallback()}willUpdate(e){e.has("hass")&&this.hass&&(this._hassSequence++,this._renderSnapshotAt=Date.now(),this._continuity.note("hass-snapshot"),this._ensureHaRegistryAuthority(),this._hookConnection()),!this.hass||this._loading||this._snap&&!e.has("hass")||this._snap&&this._loadedOnce||this._load(),(e.has("hass")||e.has("_snap")||e.has("_config")||!this._devices.length)&&this._refreshDevices(),this._captureRenderDeviceSnapshot(),this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._continuity.refreshCompleteFrame(this._frameFingerprint())}_stampActivity(e,t){pm(e,t,Date.now(),window.clearTimeout.bind(window),e=>window.setTimeout(()=>this.requestUpdate(),e))}_syncActivity(e,t,i=this.hass){if(!1===this._config?.live_states){for(const e of this._activityRuntime.values())window.clearTimeout(e.timer);return void this._activityRuntime.clear()}const s=new Set,n=ho(t,e,null,this._snap?.virtualLights);for(const o of e){if(o.hidden)continue;if("icon_ripple"!==Bi(o.marker?.display))continue;s.add(o.id);const a=C_(t,o,e,n,i),r=a.samples,l=T_(t,o,a);let c=this._activityRuntime.get(o.id);if(!c||c.sources!==l){c&&window.clearTimeout(c.timer),c=hm(l,r),this._activityRuntime.set(o.id,c);continue}const h=um(c,r,window.clearTimeout.bind(window));h&&this._stampActivity(c,h)}for(const[e,t]of this._activityRuntime)s.has(e)||(window.clearTimeout(t.timer),this._activityRuntime.delete(e))}_refreshDevices(){if(!this.hass||!this._snap?.config||!this._config)return;const e=An(this.hass),t=X_({hass:this.hass,registry:e,cfg:this._snap.config,lang:this._lang});this._syncActivity(t,En(this.hass,e),this.hass),this._devices=t}_captureRenderDeviceSnapshot(){if(!this.hass)return;const e=Date.now(),t=[...this._activityRuntime.entries()].map(([t,i])=>`${t}:${i.gen}:${i.flashTs}:`+(i.flashKind&&(i.expiresAt||i.flashTs+3300)>e?1:0)).join("|"),i=this._snap?.virtualLights?`${this._snap.virtualLights.configRev}:${this._snap.virtualLights.rev}`:"";if(this._capturedSnapshotSequence===this._hassSequence&&this._capturedSnapshotDevices===this._devices&&this._capturedSnapshotActivity===t&&this._capturedSnapshotVirtual===i)return;const s=En(this.hass,An(this.hass)),n=new Map,o=new Set(["sun.sun"]),a=new Set,r=new Set,l=e=>{if(!e)return;const t=e.indexOf(":");if(t<0)return void o.add(e);const i=e.slice(0,t),s=e.slice(t+1);"device"===i?a.add(s):"entity"===i&&o.add(s)},c=Eh(this._snap?.config||null).find(e=>e.id===this._config?.space);for(const e of c?.rooms||[])e.area&&r.add(e.area),l(e.settings?.temp_source),l(e.settings?.hum_source);const h=this._snap?.config?.spaces?.find(e=>e.id===this._config?.space);for(const e of h?.openings||[])for(const t of gi(e))o.add(t);const d=ho(s,this._devices,null,this._snap?.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._activityRuntime.get(e.id),sourceDetails:!1,lightDevices:this._devices,lightSources:d,registryHass:this.hass,reducedMotion:this._reducedMotion}));const p=F_({sourceSequence:this._hassSequence,hass:s,devices:this._devices,presentations:n,entityIds:o,deviceIds:a,areaIds:r});this._capturedSnapshotSequence=this._hassSequence,this._capturedSnapshotDevices=this._devices,this._capturedSnapshotActivity=t,this._capturedSnapshotVirtual=i,this._visibleDeviceSnapshot&&"steady"!==this._continuity.state?this._candidateDeviceSnapshot=p:(this._visibleDeviceSnapshot=p,this._candidateDeviceSnapshot=null)}get _renderDeviceSnapshot(){return this._stagedDeviceSnapshotToken===this._continuity.token?this._candidateDeviceSnapshot||this._visibleDeviceSnapshot:this._visibleDeviceSnapshot||this._candidateDeviceSnapshot}_beginContinuityCandidate(e,t,i="plan"){return this._continuityDataReady=t,this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._continuity.beginCandidate(e,i)}_backdropRaw(){return this._snap?.config&&this._config&&Eh(this._snap.config).find(e=>e.id===this._config.space)?.bg?.href||""}_candidateBackdrop(e){return e&&this._config&&Eh(e).find(e=>e.id===this._config.space)?.bg?.href||""}_assetsReady(){const e=this._backdropRaw();return!e||this._signer.isReady(this.hass,e)}_frameFingerprint(){const e=this._snap;return Vd([e?.rev||0,e?.configFingerprint||Gd(e?.config),e?.layoutRev||0,e?.layoutFingerprint||Gd(e?.layout),e?.virtualLights?`${e.virtualLights.configRev}:${e.virtualLights.rev}`:"",this._config?.space||"",this._stageWidth,this.hass?.themes?.darkMode??this.hass?.themes?.default_theme??""])}_stageValid(){const e=this._observedStage;return!!e&&e.clientWidth>0&&e.clientHeight>0}_settleContinuityFrame(){if(!this._stageValid())return;if(!this._continuity.hasCompleteFrame&&"steady"===this._continuity.state){if(this._assetsReady())return this._renderSnapshotAt=Date.now(),void this._continuity.markCompleteFrame(this._frameFingerprint());this._beginContinuityCandidate("asset-wait",!0,"asset")}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._stageValid(),assetsReady:()=>this._assetsReady(),frameFingerprint:()=>this._frameFingerprint()}).then(t=>{t&&e===this._continuity.token?(this._renderSnapshotAt=Date.now(),this._candidateDeviceSnapshot&&(this._visibleDeviceSnapshot=this._candidateDeviceSnapshot),this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1):e===this._continuity.token&&(this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._candidateDeviceSnapshot=null,this.requestUpdate())}))}updated(){const e=this.renderRoot.querySelector(".hp-static-stage")||void 0;if(e!==this._observedStage)if(this._stageObserver?.disconnect(),this._observedStage=e,e){const t=()=>{const i=e.clientWidth;if(!(i<=0||Math.abs(i-this._stageWidth)<=.5)){if(this._stageWidth<=0)return this._stageWidth=i,void this.requestUpdate();this._pendingStageWidth=i,this._stageWidthRaf||(this._stageWidthRaf=requestAnimationFrame(()=>{this._stageWidthRaf=0;const e=this._pendingStageWidth;this._pendingStageWidth=0,!e||!this._observedStage||this._observedStage.clientWidth<=0||(Math.abs(this._observedStage.clientWidth-e)>.5?t():Math.abs(e-this._stageWidth)<=.5||(this._continuity.hasCompleteFrame&&this._beginContinuityCandidate("stage-resize",!0,"stage-size"),this._stageWidth=e,this.requestUpdate()))}))}};this._stageObserver=new ResizeObserver(t),this._stageObserver.observe(e),t()}else this._stageObserver=void 0;this._syncDayCycleClock(),this._settleContinuityFrame()}async _load(e=!1){if(this.hass){if(this._loading)return this._reloadQueued=!0,void(this._forceReloadQueued||=e);this._loading=!0,this._reloadQueued=!1;try{const t=await lm(this.hass,e),i=!this._snap||this._snap.configFingerprint!==t.configFingerprint,s=!this._snap||this._snap.layoutFingerprint!==t.layoutFingerprint,n=!this._snap||this._snap.virtualLights!==t.virtualLights;if(i&&!await this._signer.prepareImage(this.hass,this._candidateBackdrop(t.config)))return this._continuity.note("asset-failed"),window.clearTimeout(this._reloadRetryTimer),void(this._reloadRetryTimer=window.setTimeout(()=>{this._load(!0)},1e3));window.clearTimeout(this._reloadRetryTimer),this._reloadRetryTimer=0,(i||s)&&this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._beginContinuityCandidate("structural-response",!0),i||s||n?this._snap=t:this._snap&&(this._snap.rev=t.rev,this._snap.layoutRev=t.layoutRev),i&&this._continuity.note("config-candidate",{configRev:t.rev}),s&&this._continuity.note("layout-candidate",{layoutRev:t.layoutRev}),n&&(this._capturedSnapshotSequence=-1),this._loadedOnce=!0,this._connectionWasLost=!1,this._continuityDataReady=!0,this._refreshDevices()}catch{}finally{if(this._loading=!1,this._continuityDataReady=!0,this.requestUpdate(),this._reloadQueued){const e=this._forceReloadQueued;this._reloadQueued=!1,this._forceReloadQueued=!1,this._load(e)}}}}get _lang(){return pa(this.hass,this._config?.language)}getCardSize(){const e=Eh(this._snap?.config||null).find(e=>e.id===this._config?.space);if(e){const t=e.vb[3]/e.vb[2];return Math.max(3,Math.round(8*t))+(!1===this._config?.show_button?0:1)}return 6}_errorCard(e){return q`<ha-card
|
||
data-continuity-state=${this._continuity.state}
|
||
data-continuity-token=${this._continuity.token}
|
||
data-frame-fingerprint=${this._continuity.frameFingerprint||G}
|
||
data-recovery-reason=${(this._continuity.overlayVisible||"recovery-error"===this._continuity.state)&&this._continuity.recoveryReason||G}>
|
||
<div class="hp-static-error">${e}</div>
|
||
</ha-card>`}_referenced(){return Rs(this._snap?.config)}_renderRecoveryOverlay(){if(!this._continuity.overlayVisible&&"recovery-error"!==this._continuity.state)return G;const e="connection"===this._continuity.recoveryReason;return q`<div class="recoveryoverlay phase-${this._continuity.overlayPhase}"
|
||
role="status" aria-live="polite" aria-atomic="true">
|
||
<ha-icon icon="mdi:home-sync-outline"></ha-icon>
|
||
<span>${ua(this._lang,e?"continuity.restore_connection":"continuity.restore_plan")}</span>
|
||
${"recovery-error"===this._continuity.state?q`<button class="btn on" @click=${this._retryContinuity}>${ua(this._lang,"continuity.retry")}</button>`:G}
|
||
</div>`}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`
|
||
<ha-card
|
||
data-continuity-state=${this._continuity.state}
|
||
data-continuity-token=${this._continuity.token}
|
||
data-frame-fingerprint=${this._continuity.frameFingerprint||G}
|
||
data-device-snapshot-sequence=${i?.sourceSequence??G}
|
||
data-recovery-reason=${l||G}
|
||
@pointerover=${e=>this._pointerModality.note(e)}
|
||
@pointerdown=${e=>this._pointerModality.note(e)}
|
||
@pointermove=${e=>this._pointerModality.note(e)}>
|
||
${o?q`<div class="hp-static-title">${o}</div>`:G}
|
||
<div class="hp-static-body">
|
||
${s}
|
||
${this._renderRecoveryOverlay()}
|
||
</div>
|
||
${a?q`<div class="hp-static-foot">
|
||
<button class="hp-static-btn" @click=${this._goToSpace}>${r}</button>
|
||
</div>`:G}
|
||
</ha-card>
|
||
`}}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;
|
||
font-size: 16px;
|
||
color: var(--primary-text-color);
|
||
}
|
||
.hp-static-body {
|
||
position: relative;
|
||
}
|
||
.hp-static-stage {
|
||
position: relative;
|
||
width: 100%;
|
||
container-type: inline-size;
|
||
overflow: hidden;
|
||
pointer-events: none; /* kill ALL interaction on the schematic (§4) */
|
||
background: var(--ha-card-background, var(--card-background-color, #111));
|
||
}
|
||
.hp-static-stage svg {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
display: block;
|
||
z-index: 1;
|
||
}
|
||
/* Opaque plan paper — the scene bg_color/day-cycle environment shows only AROUND
|
||
the plan (owner 2026-08-03). The static card keeps its historical
|
||
canvas colour: the theme card background. Drawn plans paper the room
|
||
contours (per-room shapes, fill only, no stroke) — same contract as
|
||
the full card. */
|
||
.hp-paper {
|
||
fill: var(--ha-card-background, var(--card-background-color, #111));
|
||
stroke: none;
|
||
}
|
||
.wallbody-fill {
|
||
fill: var(--wall-fill, #ffffff);
|
||
fill-opacity: var(--wall-fill-op, 1);
|
||
fill-rule: evenodd;
|
||
stroke: none;
|
||
pointer-events: none;
|
||
}
|
||
.wallbody {
|
||
fill: url(#hp-wall-hatch);
|
||
fill-rule: evenodd;
|
||
stroke: var(--room-stroke, var(--hp-muted, #607d8b));
|
||
stroke-width: calc(0.6px * var(--hp-cell-visual-scale, 1));
|
||
pointer-events: none;
|
||
}
|
||
.wallbody.solid {
|
||
fill: none;
|
||
}
|
||
.hp-static-stage .devlayer {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 2;
|
||
}
|
||
.hp-static-foot {
|
||
padding: 8px 12px 12px;
|
||
pointer-events: auto; /* the button stays clickable */
|
||
}
|
||
.hp-static-btn {
|
||
width: 100%;
|
||
padding: 9px 14px;
|
||
border: none;
|
||
border-radius: 10px;
|
||
background: var(--primary-color, #3ea6ff);
|
||
color: var(--text-primary-color, #fff);
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
}
|
||
:host([data-pointer-hover]) .hp-static-btn:hover {
|
||
filter: brightness(1.08);
|
||
}
|
||
.hp-static-error {
|
||
padding: 24px;
|
||
text-align: center;
|
||
color: var(--secondary-text-color, #9aa4ad);
|
||
}
|
||
`],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);
|
||
--hp-editor-tray-border: color-mix(in srgb, var(--hp-line) 82%, var(--hp-accent));
|
||
--hp-editor-tray-blur: 8px;
|
||
--hp-editor-tray-shadow: var(--shadow-2);
|
||
}
|
||
|
||
/* Contextual editor controls float over the stage. Their appearance must
|
||
never alter stage height, viewport fitting or the pinned close action. */
|
||
.editor-secondary-host {
|
||
position: absolute;
|
||
inset: var(--sp-2) var(--sp-4) auto;
|
||
z-index: 70;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
pointer-events: none;
|
||
}
|
||
.editor-secondary-host.kind-palette,
|
||
.editor-secondary-host.kind-group {
|
||
justify-content: flex-start;
|
||
}
|
||
.editor-secondary {
|
||
pointer-events: none;
|
||
max-width: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
padding: var(--sp-3) var(--sp-4);
|
||
color: var(--hp-txt);
|
||
background: var(--card-background-color, var(--hp-bg));
|
||
background: var(--hp-editor-tray-bg-fallback);
|
||
border: 1px solid var(--hp-line);
|
||
border-color: var(--hp-editor-tray-border);
|
||
border-radius: var(--rad-l);
|
||
box-shadow: var(--hp-editor-tray-shadow);
|
||
opacity: 0;
|
||
visibility: hidden;
|
||
transform: translateY(-4px);
|
||
transition:
|
||
opacity 0.1s ease,
|
||
transform 0.1s cubic-bezier(0.2, 0.7, 0.2, 1),
|
||
visibility 0s linear 0.1s;
|
||
touch-action: auto;
|
||
}
|
||
.editor-secondary-host.open .editor-secondary {
|
||
pointer-events: auto;
|
||
opacity: 1;
|
||
visibility: visible;
|
||
transform: translateY(0);
|
||
transition-duration: 0.14s, 0.14s, 0s;
|
||
transition-delay: 0s;
|
||
}
|
||
.editor-secondary-host.blocked .editor-secondary { pointer-events: none; }
|
||
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
|
||
.editor-secondary {
|
||
background: var(--hp-editor-tray-bg);
|
||
-webkit-backdrop-filter: blur(var(--hp-editor-tray-blur));
|
||
backdrop-filter: blur(var(--hp-editor-tray-blur));
|
||
}
|
||
}
|
||
.editor-secondary-content,
|
||
.editor-group-items {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
min-width: 0;
|
||
}
|
||
.editor-context-label {
|
||
max-width: 14rem;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
padding-inline-end: var(--sp-4);
|
||
border-inline-end: 1px solid var(--hp-line);
|
||
color: var(--hp-muted);
|
||
font-size: var(--fs-s);
|
||
}
|
||
.editor-secondary.kind-palette {
|
||
width: min(760px, 100%);
|
||
align-items: stretch;
|
||
padding: 0;
|
||
overflow: hidden;
|
||
}
|
||
.editor-secondary.kind-palette .editor-secondary-content,
|
||
.editor-secondary.kind-palette .furnpalette {
|
||
width: 100%;
|
||
}
|
||
.editor-secondary.kind-palette .furnpalette {
|
||
max-height: 38vh;
|
||
border: 0;
|
||
background: transparent;
|
||
}
|
||
.editor-group-launcher .group-chevron {
|
||
--mdc-icon-size: 16px;
|
||
transition: transform 0.14s ease;
|
||
}
|
||
.editor-group-launcher[aria-expanded="true"] .group-chevron { transform: rotate(180deg); }
|
||
|
||
.editor-secondary .bdhint {
|
||
font-size: var(--fs-s);
|
||
color: var(--hp-muted);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.editor-secondary .dfill {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
font-size: var(--fs-s);
|
||
cursor: pointer;
|
||
}
|
||
.editor-secondary .dfill input[type="checkbox"] {
|
||
width: 16px;
|
||
height: 16px;
|
||
flex: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
.editor-secondary hp-color-opacity { flex: 0 0 auto; }
|
||
.editor-secondary input {
|
||
width: 74px;
|
||
background: transparent;
|
||
border: 1px solid var(--hp-line);
|
||
color: var(--hp-txt);
|
||
border-radius: var(--rad-s);
|
||
padding: var(--sp-3) var(--sp-4);
|
||
font-size: var(--fs-m);
|
||
}
|
||
.editor-secondary label,
|
||
.editor-secondary .hint {
|
||
color: var(--hp-muted);
|
||
font-size: var(--fs-s);
|
||
}
|
||
.editor-secondary .drawwall {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
white-space: nowrap;
|
||
}
|
||
.editor-secondary .drawwall input { width: 4.2em; }
|
||
.editor-secondary .drawwall .opl {
|
||
color: var(--hp-muted);
|
||
font-size: var(--fs-s);
|
||
}
|
||
|
||
@container (min-width: 560px) and (max-width: 899px) {
|
||
.editor-secondary-content,
|
||
.editor-group-items {
|
||
flex-wrap: wrap;
|
||
justify-content: flex-end;
|
||
}
|
||
}
|
||
@container (max-width: 559px) {
|
||
.editor-secondary-host { inset-inline: var(--sp-4); }
|
||
.editor-secondary { max-width: 100%; }
|
||
.editor-secondary-content,
|
||
.editor-group-items {
|
||
overflow-x: auto;
|
||
scrollbar-width: thin;
|
||
}
|
||
.editor-secondary-content > *,
|
||
.editor-group-items > * { flex: none; }
|
||
.editor-context-label { display: none; }
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.editor-secondary { transition: none; }
|
||
.editor-group-launcher .group-chevron { transition: none; }
|
||
}
|
||
`,bm=(e,t)=>(e.composedPath?.()||[e.target]).some(e=>(e=>!!e&&"function"==typeof e.matches)(e)&&t(e));class ym{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`<button class="btn editor-group-launcher ${s||n?"on":""}"
|
||
data-editor-group=${e.id} aria-expanded=${s?"true":"false"}
|
||
aria-pressed=${n?"true":"false"}
|
||
aria-controls="hp-editor-secondary" @click=${()=>this.toggleGroup(t,e.id)}
|
||
@keydown=${i=>{"ArrowDown"===i.key&&(i.preventDefault(),s||this.toggleGroup(t,e.id),this.host.updateComplete().then(()=>this.host.root().querySelector(".editor-group-item:not([disabled])")?.focus()))}}
|
||
title=${n?i.groupActive(e.label,n.label):e.label}>
|
||
<ha-icon icon=${e.icon}></ha-icon><span class="ml">${e.label}</span>
|
||
<ha-icon class="group-chevron" icon="mdi:chevron-down"></ha-icon>
|
||
</button>`}renderGroupModel(e,t,i){const s=e.items.find(e=>!e.disabled)?.id,n=e.items.some(t=>t.id===e.activeItemId&&!t.disabled)?e.activeItemId:void 0;return{contextId:t,kind:"group",ariaLabel:i.openGroup(e.label),visibleLabel:e.label,content:q`<div class="editor-group-items"
|
||
@keydown=${e=>this._groupKeydown(e)}>
|
||
${e.items.map(t=>q`<button
|
||
class="btn editor-group-item ${n===t.id?"on":""}"
|
||
data-group-item=${t.id} ?disabled=${!!t.disabled}
|
||
tabindex=${t.id===(n||s)?"0":"-1"}
|
||
aria-label=${t.disabled&&t.disabledReason?i.disabledAction(t.label,t.disabledReason):t.label}
|
||
aria-pressed=${"tool"===t.role||"toggle"===t.role?n===t.id?"true":"false":G}
|
||
title=${t.disabled&&t.disabledReason||t.label}
|
||
@click=${i=>this._activateGroupItem(e,t.id,i)}>
|
||
<ha-icon icon=${t.icon}></ha-icon><span>${t.label}</span>
|
||
</button>`)}
|
||
</div>`}}runContext(e,t,i){e===t&&i()}render(e,t){this._currentModel=e,this._blocked=t;const i=e?.kind||"hidden";return q`<div
|
||
class="editor-secondary-host kind-${i} ${e?"open":"closed"} ${t?"blocked":""}"
|
||
aria-hidden=${e?"false":"true"}>
|
||
<div id="hp-editor-secondary" class="editor-secondary kind-${i}"
|
||
data-context-id=${e?.contextId||""} role="toolbar"
|
||
aria-label=${e?.ariaLabel||""} ?inert=${!e||t}
|
||
@focusin=${()=>this._focusOwned=!0}
|
||
@focusout=${e=>{const t=e.relatedTarget;t&&e.currentTarget.contains(t)||(this._focusOwned=!1)}}
|
||
@pointerenter=${()=>this.host.clearTip()}
|
||
@pointerdown=${wm}
|
||
@pointerup=${wm}
|
||
@pointercancel=${wm}
|
||
@click=${wm}
|
||
@dblclick=${wm}
|
||
@wheel=${wm}>
|
||
${e?.visibleLabel?q`<span class="editor-context-label">${e.visibleLabel}</span>`:G}
|
||
<div class="editor-secondary-content">${e?.content||G}</div>
|
||
</div>
|
||
</div>`}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<a.length;e++){const t=a[e+1],i=r[r.length-1];i&&$m(i[0],i[1],t[0],t[1])<=km||(r.push(t),l.push([n[e+1],t]),c.push({...s.segments?.[e]||{},cm:Number.isFinite(Number(s.segments?.[e]?.cm))?Math.max(0,Math.min(100,Number(s.segments[e].cm))):15}))}if(s.points=r,s.segments=c,r.length>=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;i<t.length;i++){const s=t[i],o=t[(i+1)%t.length];if(Tm(s)&&Tm(o)&&Rm(e,s,o)<=n)return!0}for(const t of s?.columns||[])if(Tm(t?.center)&&Im(e,t.center)<=n)return!0;for(const t of s?.draftEnds||[])if(Tm(t)&&Im(e,t)<=n)return!0;return!1}function zm(e,t,i,s){if(e.cm!==t.cm)return null;const n=[e.b[0]-e.a[0],e.b[1]-e.a[1]],o=[t.b[0]-t.a[0],t.b[1]-t.a[1]],a=Math.hypot(n[0],n[1]),r=Math.hypot(o[0],o[1]);if(!(a>0&&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;e<n.length+1;e++){let e=null;for(let s=0;s<n.length&&!e;s++)for(let a=s+1;a<n.length;a++){const r=zm(n[s],n[a],.02,i);if(r&&((!o||o.has(n[s].id)||o.has(n[a].id))&&!Am(r,n,new Set([n[s].id,n[a].id]),t.geometry,i))){e={i:s,j:a,at:r};break}}if(!e)break;const r=n[e.i],l=n[e.j],c=[r.a,r.b,l.a,l.b].filter(t=>Im(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]<h[0]||d[0]===h[0]&&d[1]<h[1]){const e=h;h=d,d=e}const p={...r,a:[h[0],h[1]],b:[d[0],d[1]]},u=Im(h,d),_=u>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)<Math.min(e.length/2,d+t.length/2)-i},Km=(e,t)=>({...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<e.length;i++)t^=e.charCodeAt(i),t=Math.imul(t,16777619)>>>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+1<e.points.length&&t;n++){const o=e.points[n],a=e.points[n+1],r=Number(e.segments[n]?.cm);if(!(Om(o)&&Om(a)&&r>0)){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+s<r)return[];const i=jm(o,a,e.a,e.b,s);return i?[i]:[]}).sort((e,t)=>e.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<l-s&&(t=!1)}t&&o.add(e.id)}return o})(l,t.room_drafts,_,h),g=l.filter(e=>!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+1<M.length;e++){const s=M[e],a=M[e+1];if(!(a-s>h))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+1<e.points.length&&!!jm(r,l,t,e.points[i+1],h))),$=b&&!k&&!x&&!Um(y,t.wall_columns,n,h),S=b?p[0].cm>0?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&&e<s.hi-h),a=P.filter(e=>qm(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;e<U.length;e++)for(let t=e+1;t<U.length;t++)Vm(U[e],U[t],h)&&(G=!0);const V=new Set(W.map(e=>e.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 i<s?`${i}|${s}`:`${s}|${i}`},hg=(e,t)=>lg(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<e.points.length;s++){const n=[Number(e.points[s][0]),Number(e.points[s][1])];if(fi(t[t.length-1],n))continue;t.push(n);const o=e.segments?.[s-1];i.push({...o&&"object"==typeof o?o:{},cm:Number.isFinite(Number(o?.cm))?Math.max(0,Math.min(100,Number(o.cm))):15})}return{points:t,segments:i}},ug=(e,t=[])=>{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<s;e+=64){for(let t=0;t<16;t++)l[t]=o.getUint32(e+4*t,!1);for(let e=16;e<64;e++){const t=l[e-15],i=l[e-2],s=c(t,7)^c(t,18)^t>>>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<r.length;e++)d.setUint32(4*e,r[e],!1);return h},vg=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<s.length;e++){const n="string"==typeof s[e]?t.get(s[e]):null;0===Number(n?.cm)&&o.push([i[e][0],i[e][1],i[(e+1)%i.length][0],i[(e+1)%i.length][1]])}}const a=[...o,...n],r=n.length?(Array.isArray(e.openings)?e.openings:[]).filter(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;e<p.length;e++){const i=t.get(_[e]),s=i?xg(p[e],p[(e+1)%p.length],i):null;if(!s||g&&(Math.abs(s[0]-g[0])>sg||Math.abs(s[1]-g[1])>sg)){f=!1;break}g=s}const v=[];for(let e=0;e<u.poly.length;e++){const i=u.poly[e],s=u.poly[(e+1)%u.poly.length];if(dg(i,s)<=sg)throw new ng("zero-length",r);const a=cg(i,s);let l=h.get(a);if(!l){const[e,t]=hg(i,s);l={key:a,a:e,b:t,owners:new Set,preferredIds:new Set,positionalIds:new Set,preferredCarriers:new Map,parentKeys:new Set,zeroWall:!1},h.set(a,l)}if(l.owners.add(r),l.owners.size>2)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.id<t.id?-1:e.id>t.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<n.length;o++){const a=n[o];if("string"==typeof a.id&&a.id){if(i.has(a.id))throw new ng("duplicate-id",a.id);i.add(a.id);continue}const r=s.points?.[o],l=s.points?.[o+1];if(!ag(r)||!ag(l))throw new ng("zero-length",s.id);if(t){const t=bg(String(e.id||""),r,l,[`draft:${String(s.id||"")}`]);let n=1;for(a.id=t;i.has(a.id);)a.id=`${t}-${++n}`}else a.id=wg(i);i.add(a.id)}}})(e,t),((e,t,i)=>{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<e.length?i(e[n],t):og(t)});return e.splice(0,e.length,...n),e}if(Tg(e)&&Tg(t)){const s=Object.entries(t).map(([t,s])=>[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 o<a?`${o}|${a}`:`${a}|${o}`};function Ng(e,t,i,s,n,o,a=1){if(!t?.length)return[];const r=a>0?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;e<u.orig.length;e++){const t=[];for(let i=0;i<u.parent.length;i++)u.parent[i]===e&&t.push(i);for(let e=1;e+1<t.length;e++){const i=t[e-1],s=t[e],n=t[e+1];if(null===u.kinds[i]||null===u.kinds[s]||null===u.kinds[n])continue;const a=u.cms[i],_=u.cms[s],m=u.cms[n];if(!(a>0&&_>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<e.length;i++)t.push([[e[i][0],e[i][1]],[e[(i+1)%e.length][0],e[(i+1)%e.length][1]]])}return t};function Og(e,t,i={},s={}){const n=s.reconcileCoincidentPartitions??Xm,o=function(e,t,i={}){const s=Zm(e||{spaces:[],markers:[],settings:{}}),n=Zm(t||{}),o=JSON.stringify(s),a=JSON.stringify(n),r=Array.isArray(s.spaces)?s.spaces:[],l=Array.isArray(s.markers)?s.markers:[],c=r.map(e=>"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;e<t.length;e++){const i=[...t[e]],n=[...t[(e+1)%t.length]],o=fa(i,n);if(!o)continue;const a=wa(i,n);s.has(a)||s.set(a,{key:a,a:i,b:n,axis:o.axis})}}let n=0,o=0,a=0;const r=new Map;for(const e of[...s.values()].sort((e,t)=>e.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;e<a.spaces.length;e++){const t=u[e],i=a.spaces[e],s=JSON.stringify({zero:(t.wall_segments||[]).filter(e=>0===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<s.length;e++)for(let i=e+1;i<s.length;i++){const a=s[e],r=s[i],l=hi(a),c=hi(r);if(!l||!c)continue;const h=Cs(l,c,n);if(!h.length)continue;const d=t.filter(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;i<s.length;i++){const c=s[i],h=Gg(c,i,n);let d;try{const t=Eh({...e,spaces:[c]})[0];if(!t)throw new Error("missing space model");d=o(c,t)}catch(e){l.push({...h,status:"failed",reason:"prepare-exception",detail:Lg(e)});continue}const p=d.walls.length>0||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=`<svg xmlns="http://www.w3.org/2000/svg" width="4" height="1" viewBox="0 0 4 1">\n <rect width="4" height="1" fill="rgb(${n.join(",")})"/>\n <g style="isolation:isolate">\n <rect x="0" width="2" height="1" fill="rgb(${i.join(",")})"/>\n <rect x="1" width="2" height="1" fill="rgb(${s.join(",")})" style="mix-blend-mode:screen"/>\n </g>\n </svg>`,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)<t?i=n:s=n;return tf(n,.7,1)})(i),n=ef(e.stageWidth,t.stageWidth,s),o=ef(e.stageHeight,t.stageHeight,s),a=Math.max(1e-9,e.viewport.pixelsPerUnit),r=Math.max(1e-9,t.viewport.pixelsPerUnit),l={centerX:ef(e.viewport.centerX,t.viewport.centerX,s),centerY:ef(e.viewport.centerY,t.viewport.centerY,s),pixelsPerUnit:Math.exp(ef(Math.log(a),Math.log(r),s))};return{presentedMode:s<.5?e.presentedMode:t.presentedMode,editorChromeHeight:ef(e.editorChromeHeight,t.editorChromeHeight,s),stageWidth:n,stageHeight:o,viewport:{...l,viewBox:nf(l,n,o)},stageColor:af(e.stageColor,t.stageColor,s),paperColor:af(e.paperColor,t.paperColor,s),sceneBrightness:ef(e.sceneBrightness,t.sceneBrightness,s),architectureOpacity:ef(e.architectureOpacity,t.architectureOpacity,s),backdropOpacity:ef(e.backdropOpacity,t.backdropOpacity,s),viewWeight:ef(e.viewWeight,t.viewWeight,s),editorWeight:ef(e.editorWeight,t.editorWeight,s),toolbarContentOpacity:ef(e.toolbarContentOpacity,t.toolbarContentOpacity,s)}}class lf{constructor(e,t=Zg){this._hooks=e,this._clock=t,this._token=0,this._raf=0,this._state=null}get state(){return this._state}get active(){return"running"===this._state?.phase||"preparing"===this._state?.phase}get presented(){return this._state?.presented||null}start(e,t,i,s=220){this.cancel(!1);const n=++this._token,o=this._clock.now();if(this._state={token:n,phase:s<=0?"settling":"running",from:e,to:t,presented:s<=0?t:e,startedAt:o,duration:Math.max(0,s),targetMode:i},s<=0)return this._hooks.frame(this._state),this._hooks.settled(this._state),this._state=null,n;this._hooks.frame(this._state);const a=e=>{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]<t[i]?-1:1;return 0}function uf(e){if(!/^[a-z][a-z0-9-]*$/.test(e.id)||!Number.isInteger(e.issue)||e.issue<=0||!e.summary.trim())return!1;const t=df(e.since),i=df(e.expires);return!!t&&!!i&&pf(t,i)<0}function _f(e){const t=new Set;for(const i of e){if(!uf(i)||t.has(i.id))return!1;t.add(i.id)}return!0}function mf(e){const t=String(e||"").replace(/^#/,"");return new URLSearchParams(t)}function gf(e){return mf(e).get("space")||""}function ff(e){return e.getAll("hp-labs").flatMap(e=>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;i<e.length;i++){const s=e[i],n=e[(i+1)%e.length];t+=s[0]*n[1]-n[0]*s[1]}return t/2}function Ff(e,t){const i=function(e){const t=(Array.isArray(e)?e:[]).filter(e=>Array.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;e<i.length;e++)(i[e][0]<i[s][0]||i[e][0]===i[s][0]&&i[e][1]<i[s][1])&&(s=e);return[...i.slice(s),...i.slice(0,s)]}(e,t>0)).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;e<s.length;e++){const r=s[e];for(const e of r)n.push(Nf(e,i,0));const l=r[0];a+=l.length;for(let s=0;s<l.length;s++){const n=l[s],a=l[(s+1)%l.length];if(Ef(n,a,i)<=1e-9)continue;const r=Pf(n,0,i),c=Pf(a,0,i),h=Pf(a,-t,i),d=Pf(n,-t,i);o.push({d:`M ${Af(r)} L ${Af(c)} L ${Af(h)} L ${Af(d)} Z`,depth:Math.max(d[1],h[1]),component:e,edge:s,planEdge:[n,a]})}}return o.sort((e,t)=>e.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.segmentKey<t.target.segmentKey?-1:e.target.segmentKey>t.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<e.length;i++){const s=e[i],n=e[(i+1)%e.length];t+=s[0]*n[1]-n[0]*s[1]}return t/2}(e)>=0?[-o/a,n/a]:[o/a,-n/a]}(e.profile.poly,t),a=tv(i.center,o,n),r=[];for(let t=0;t<e.inner.length;t++){const n=e.inner[t],o=e.inner[(t+1)%e.inner.length],l=ev(o,n),c=Math.hypot(l[0],l[1]);if(!(c>s&&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;n<t.length;o=n++){if(lv(e,t[o],t[n],i))return!0;const[a,r]=t[n],[l,c]=t[o];r>e[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;s<r.body.length;s++){const n=hv(e,t,r.body[s],r.body[(s+1)%r.body.length],i,o);null!=n&&(null==a||n<a)&&(a=n)}}return a}(e.from,i,s,t.boundaries,n,t.epsilon);if(null==o)return e;const a=tv(e.from,i,o);return{...e,to:a,label:[(e.from[0]+a[0])/2,(e.from[1]+a[1])/2],distance:o,source:"connected-face"}})}function pv(e,t){const i=function(e){const t=e.angle*Math.PI/180,i=[Math.cos(t),Math.sin(t)],s=[-i[1],i[0]],n=[e.x,e.y],o=[e.target.a,e.target.b].map(e=>Qf(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;e<s.poly.length;e++){if(!(s.kinds[e]&&s.offsets[e]>0))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||p<t.half-i)continue;const u=Math.max(0,d>0?d:p<0?-p:0);(!n||u<n.offset-i||Math.abs(u-n.offset)<=i&&e<n.edge)&&(n={edge:e,offset:u})}return n?.edge??null}(e,i,t.epsilon);return null==s?[]:[{room:e,edge:s}]});if(s.length>2)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)||n<a||n>o)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;i<t.length;i++){const o=t[i],r=t[(i+1)%t.length];e.partitions.push({id:a[i]?.id||`partition-${n}-${i}`,a:[o[0]/zv,o[1]/zv],b:[r[0]/zv,r[1]/zv],cm:s[i]})}this._activeDraftId&&Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(e=>e.id!==this._activeDraftId),e.room_drafts.length||delete e.room_drafts),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(e<r.left||e>r.right||t<r.top||t>r.bottom)continue;const l=s.indexOf(o);return l<0?null:{targetId:o,placement:l<n?"before":"after"}}return null}_tabPointerMove(e){const t=this._tabDrag;if(!t||t.pointerId!==e.pointerId)return;if(!(t.moved||(i=e.clientX-t.x,s=e.clientY-t.y,Math.hypot(i,s)>=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&&s<i?"right":"left")&&this._saveNav()}_cycleTick(){if(!this._hasFixedFloor&&this._kiosk&&Number(this._config?.cycle)>0&&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;e<t.length;e++)if(t[e]===this)return e;return-1}_warmRelease(){const e=this._warmSlot,t=this._warmKey;this._warmSlot=null,this._warmKey=null,e&&t&&(e.freed=Date.now(),e.owner===this._warmGen&&(e.live=!1),this._warmScheduleEvict(e,t))}_warmTrim(e){for(;e.length>4;){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`<div class="recoveryoverlay phase-${this._continuity.overlayPhase}"
|
||
role="status" aria-live="polite" aria-atomic="true"
|
||
@pointerdown=${e=>e.stopPropagation()}
|
||
@click=${e=>e.stopPropagation()}
|
||
@wheel=${e=>e.stopPropagation()}>
|
||
<ha-icon icon="mdi:home-sync-outline"></ha-icon>
|
||
<span>${this._t(e?"continuity.restore_connection":"continuity.restore_plan")}</span>
|
||
${"recovery-error"===this._continuity.state?q`<button class="btn on" @click=${this._retryContinuity}>${this._t("continuity.retry")}</button>`:G}
|
||
</div>`}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`<hp-help data-help-key=${e}
|
||
.text=${ua(i,e)} .ariaLabel=${ua(i,t)}></hp-help>`: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;s<c.length;s++){const a=c[s],l=c[(s+1)%c.length],h=Pf(a,0,t),d=Pf(l,0,t);if(o.push(`M ${Af(h)} L ${Af(d)}`),Ef(a,l,t)<=1e-9)continue;const p=Pf(l,i,t),u=Pf(a,i,t);n.push({d:`M ${Af(h)} L ${Af(d)} L ${Af(p)} L ${Af(u)} Z`,depth:Math.max(h[1],d[1]),polygon:r,ring:e,edge:s})}}}}return n.sort((e,t)=>e.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`<div class="farhint">
|
||
<ha-icon icon="mdi:map-marker-alert-outline"></ha-icon>
|
||
<span>${this._t("canvas.far_objects",{n:this._outliers})}</span>
|
||
<button class="btn ghostbtn" @click=${()=>this._fitFar()}>${this._t("canvas.show_far")}</button>
|
||
</div>`}_renderHomeArrow(){if(this._booting)return G;const e=this._view;if(!e||!e.w||!e.h)return G;const t=this._baseVb(),i=t[0],s=t[1],n=t[2],o=t[3];if(!(i+n<=e.x||i>=e.x+e.w||s+o<=e.y||s>=e.y+e.h))return G;const a=Math.atan2(s+o/2-(e.y+e.h/2),i+n/2-(e.x+e.w/2)),r=50+38*Math.cos(a),l=50+38*Math.sin(a);return q`<button class="homearrow" title=${this._t("canvas.home_tip")}
|
||
style="left:${r.toFixed(1)}%;top:${l.toFixed(1)}%"
|
||
@click=${e=>{e.stopPropagation(),this._fitAll()}}>
|
||
<ha-icon icon="mdi:arrow-right-thick" style="transform:rotate(${(180*a/Math.PI).toFixed(1)}deg)"></ha-icon>
|
||
</button>`}_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(qv.ZOOM_MAX,Math.max(qv.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},bv)}_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>=Ld?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(qv.ZOOM_MAX,Math.max(qv.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(Pv,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)<o||Math.abs(e)<1.5*Math.abs(t))return null;const a=s.indexOf(n);if(a<0)return null;const r=s.length;return e<0?s[(a+1)%r]:s[(a-1+r)%r]}(i,s,this._zoom,this._model.map(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&&Lv("/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},jv(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=Uh(this._drag.ox+a),c=Uh(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(Av))t.toggleAttribute("data-pointer-hover",e)}_syncPointerHoverSubtree(e){if(e.nodeType!==Node.ELEMENT_NODE)return;const t=e,i=this._pointerModality.hoverEnabled;t.matches(Av)&&t.toggleAttribute("data-pointer-hover",i);for(const e of t.querySelectorAll(Av))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 Bh}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,zv}get _segments(){const e=this._curSpaceCfg,t=this._spaceH;return pi(e?.rooms||[]).map(e=>[e[0]*zv,e[1]*t,e[2]*zv,e[3]*t])}_savedNav(){if(this._hasFixedFloor)return null;try{return JSON.parse(localStorage.getItem(Tv)||"null")}catch{return null}}_saveNav(){if(!this._hasFixedFloor)try{localStorage.setItem(Tv,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?Pf([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=Sl(vl,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?Sl(vl,this._imperial):this._drawWallField}get _drawWallCm(){const e=yv(this._drawWallFieldValue);if(null==e||e<0)return null;const t=this._imperial?2.54*e:e,i="column"===this._tool?ih:100;return t>=("column"===this._tool?1:0)&&t<=i?t:null}get _drawWallMaxCm(){return"column"===this._tool?ih:100}_showPhysicalRange(e=this._drawWallMaxCm,t=0){this._showToast(this._t("toast.physical_range",{min:Sl(t,this._imperial),max:Sl(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=Fm(i,{pitch:jh,seedIds:t,geometry:Em(e,{excludeDraftId:this._activeDraftId})});return s.merged?(e.partitions=s.partitions,Nm(e.openings,e.partitions,s.openingMoves,{coordScale:zv,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=Du(this._path,Cu(this._path.length-1,this._draftSegmentCms,this._drawWallCm,vl)),i=t.length;if((e.partitions||[]).length+i>Ov)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;s<i;s++){const i=t[s],a=r[s]?.id||`partition-${n}-${s}`;o.push(a),e.partitions.push({id:a,a:[i.a[0]/zv,i.a[1]/zv],b:[i.b[0]/zv,i.b[1]/zv],cm:i.cm})}return this._mergeSpacePartitions(e,o),this._activeDraftId&&Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(e=>e.id!==this._activeDraftId),e.room_drafts.length||delete e.room_drafts),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>=Ov:(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[Uh(ri(e[0],t)),Uh(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,Hh):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=Xc(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=bu({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=vu({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=bu({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?Su(i.value,e,{...o,anchor:s}):function(e,t,i){if(!(hu(t)&&i.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const s=i.epsilon??cu,n=i.excludePoints||[],o=xu(ku(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=$u(o,t,i.gridStep);!yu(l,n,s)&&wu(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?va(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))<=Oh);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:nh(Number(e.cm)||15),..."circle"===e.shape?{}:{angle:oh(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=pg(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=Jd(Eh({spaces:[e]}),e.id),i=t?V_(e,t,zv,.02*Bh).contour.map(e=>e.map(e=>e/zv)):Od(e.open_spans).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]]);e.walls=Ll(e.walls,e.rooms||[],jh,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=Gd(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=fl(il(this._serverCfg)),t=Gd(e),i=[...this._pendingPhysicalWrites.entries()];for(const[t,s]of i){const i=Bg(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!==Gd(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 ng?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+1<e.length;n++)t.push({id:String(s[n]?.id||`${i?.id||"draft"}-${n}`),a:e[n],b:e[n+1],cm:Number(s[n]?.cm)})}return t}_junctionLimitViolations(e,t){const i=(e?.spaces||[]).find(e=>e?.id===t);if(!i)return[];const s=Number(i.cell_cm)>0?Number(i.cell_cm):5,n=this._limitSegmentsOf(i),o=[...Dd(n),...Id(n,s,jh),...Ad(n,s,jh)];for(const e of i.rooms||[]){const t=String(e?.id||"");if(!t)continue;let n=null;try{n=Cc(i.rooms||[],t,i.walls||[],[],jh,s,jh,1)}catch{n=null}o.push(...zd(t,n,s,jh))}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=Pg(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||Bg(t)===Bg(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 ng("invalid-room",t.spaceId);const i=Pg(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 ng("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=Hl(n.walls,e.a,e.b,jh,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}=$g(i,kg(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<r.length;t++){const i=(e,t,i,s)=>{const n=[];for(let o=0;o<e.length;o++){const a=e[o],r=e[(o+1)%e.length];if(mg(a,i,s)>sg||mg(r,i,s)>sg)continue;const l=_g(a,i,s),c=_g(r,i,s);l<-sg||c>1+sg||c<=l+sg||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;e<n.length;e++){const t=s.get(o[e].id);if(t&&t!==n[e].id)throw new ng("duplicate-id",o[e].id);s.set(o[e].id,n[e].id)}}}return s})(r,t.rooms,c);a=Pg(o,{lineageHints:h,lineageSpaceId:t.spaceId}).config}else a=Pg(s).config}catch(e){return this._clearGeometryGesture(),this._restoreGeometryStateLocal(t),this._showWallModelMigrationBlocked(e),!1}let c=!1;try{const e=a.spaces.find(e=>e.id===t.spaceId),s=this._path.length>=2?this._path.map(e=>[e[0]/zv,e[1]/zv]):[];s.push(...i.map(e=>[e[0],e[1]])),c=ug(e)<=ug(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;Ig(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:Bg(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=Bg(i)!==Bg(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=Pg(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;Ig(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:Bg(a)})}else Ig(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(!Ea(e))return!0;const t=e.length,i=.001;for(let s=0;s<t;s++){const n=e[s],o=e[(s+1)%t];for(let a=s+1;a<t;a++){if(a===s+1||0===s&&a===t-1)continue;const r=e[a],l=e[(a+1)%t],c=[r[0],r[1],l[0],l[1]],h=[n[0],n[1],o[0],o[1]];if(Hs(n,c)<=i||Hs(o,c)<=i||Hs(r,h)<=i||Hs(l,h)<=i)return!0}}return!1}_canAppendRoomDraftPoint(){if(null==this._drawWallCm)return this._showPhysicalRange(100),!1;if(this._path.length>=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>Ov)||(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=va(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]/zv,e[1]/zv]),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=Cu(s,t,this._drawWallCm,vl);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])*zv,Number(e?.[1])*zv]):[],n=Array.isArray(t?.segments)?t.segments:[];return Cu(Math.max(0,e.length-1),i,this._drawWallCm,vl).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]/zv,e[1]/zv]),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+1<e.length;i++)t.push({a:e[i],b:e[i+1],key:this._activeWallSourceKey(i)});return t}_wallFaceGraph(e,t){const i=`${this._space}|${t}|${JSON.stringify(e)}`,s=this._wallFaceGraphCache.findIndex(e=>e.key===i);if(s>=0){const[e]=this._wallFaceGraphCache.splice(s,1);return this._wallFaceGraphCache.push(e),e.value}const n=Ju(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=l_(r,{requiredSourceKey:n,maxDistance:Ml(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=Xu(this._wallFaceGraph(this._wallGraphSources([]),t),e,t);if(!s){const n=l_(this._wallGraphSources([]),{point:e,maxDistance:Ml(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=l_(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(ih,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:nh(i),angle:0},o=(s.wall_columns||[]).find(e=>Sh(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]/zv,t[1]/zv]}),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:Sl(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:Sl(i.cm,this._imperial),shape:i.shape,angle:this._angleField("square"===i.shape?oh(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:Sl(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(),jv(e);const n=this._svgPoint(e),o=[];for(const e of[...s.wall_columns].reverse())xh(n,dh(e,this._cellCm,this._gridPitch))&&o.push({kind:"column",id:e.id});for(const e of[...s.partitions].reverse()){const t=hh(e.a,e.b,e.cm,this._cellCm,this._gridPitch);t&&xh(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(-Oh*zv-Math.min(...n),Math.min(Oh*zv-Math.max(...n),i[0])),Math.max(-Oh*zv-Math.min(...o),Math.min(Oh*zv-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]/zv,n.a[1]/zv],e.b=[n.b[0]/zv,n.b[1]/zv];for(const e of s.openings||[]){if("partition"!==e.host?.kind||e.host.id!==t.id)continue;const i=tu(e,[n],zv,this._cellCm,this._gridPitch).resolved;i&&Object.assign(e,lu(e,i,zv))}}}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&&Sh(e,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));e&&(e.center=[(n.center[0]+t.delta[0])/zv,(n.center[1]+t.delta[1])/zv])}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(),jv(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:oh(t.angle),angle:oh(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=oh(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&&Sh(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;e<t.length;e++){const n=t[(e-1+t.length)%t.length],o=t[e],a=t[(e+1)%t.length],r=Ta(o,n),l=Ta(a,o),c=Aa(r),h=Aa(l);if(c<=s||h<=s)continue;const d=Math.abs(r[0]*l[1]-r[1]*l[0]),p=r[0]*l[0]+r[1]*l[1];if(d<=s*(c+h)&&p>0){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:Ml(i.cm,this._cellCm,this._gridPitch)/2});for(const i of e.room_drafts||[])for(let e=0;e+1<i.points.length;e++)t.push({kind:"segment",a:[...i.points[e]],b:[...i.points[e+1]],half:Ml(Number.isFinite(Number(i.segments?.[e]?.cm))?Number(i.segments[e].cm):vl,this._cellCm,this._gridPitch)/2});for(const i of e.wall_columns||[]){const e=Ml(i.cm,this._cellCm,this._gridPitch)/2;t.push({kind:"circle",center:[...i.center],radius:"square"===i.shape?e*Math.SQRT2:e})}return t}_rszOptsFor(e,t){const i=Hl(this._spaceWalls,e,t,this._wallKeyPitch,zv),s=this._spaceWalls.filter(e=>Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2).map(e=>({wall:e,a:[e.a[0]*zv,e.a[1]*this._spaceH],b:[e.b[0]*zv,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?Ml(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||!Ea(a.poly))return{enabled:!1,reason:"invalid-geometry"};const r=a.poly[s],l=a.poly[(s+1)%a.poly.length],c=Ba(r,l,o);if(!c)return{enabled:!1,reason:"diagonal"};if(!Ua(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(Ka(e,r,l,n))return{enabled:!1,reason:"duplicate-physical-wall"};const h=Aa(Ta(l,r)),d=[],p=new Set;let u=!1,_=!1;for(const t of e)if(t.id!==i)for(let e=0;e<t.poly.length;e++){const i=t.poly[e],s=t.poly[(e+1)%t.poly.length],n=Wa(r,l,i,s,o);n<=o||(p.add(t.id),qa(r,l,i,s,o)?d.push({room:t,edge:e}):n<h-o?u=!0:_=!0)}if(u)return{enabled:!1,reason:"partial-shared"};if(_)return{enabled:!1,reason:"unequal-shared"};if(p.size>1||d.length>1)return{enabled:!1,reason:"multiple-rooms"};const m={[i]:s},g=[i];if(1===d.length){const e=d[0];if(!Ea(e.room.poly))return{enabled:!1,reason:"invalid-geometry"};if(!Ua(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(!(Fa([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=Xa(e,t,n,1,o),l=Xa(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:Na(a.poly,s),roomIds:g,edgeByRoom:m,topology:v,movingOpeningIds:f,sideOwnership:b};if(!nr(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=>nr(e,t,y,i,n))){if(!i.some(i=>er(tr(e,t,y,i),y,o)))return{enabled:!1,reason:"partial-shared"};const s={...n,obstacles:[]};return i.some(i=>nr(e,t,y,i,s))?{enabled:!1,reason:"duplicate-physical-wall"}:t.length&&i.some(t=>nr(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]/zv,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]/zv,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]*zv,e[1]*l]);if(t.poly.length!==s.length)continue;const o=Array.isArray(t.wall_ids)?t.wall_ids:[];for(let e=0;e<t.poly.length;e++){c.push([t.poly[e],t.poly[(e+1)%t.poly.length]]),h.push([s[e],s[(e+1)%s.length]]);const i=o[e],n="string"==typeof i?(r.wall_segments||[]).find(e=>e.id===i):null;n&&(n.a=[s[e][0]/zv,s[e][1]/zv],n.b=[s[(e+1)%s.length][0]/zv,s[(e+1)%s.length][1]/zv])}}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=jl(e,t,i,s,n,o,()=>{a=!0});return{walls:a?(e||[]).map(e=>({...e})):r,rejected:a}}(r.walls,c,h,this._wallKeyPitch,zv,"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;e<t.length;e++)d.push([[t[e][0]*zv,t[e][1]*zv],[t[(e+1)%t.length][0]*zv,t[(e+1)%t.length][1]*zv]])}const p=e=>JSON.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(Wl(_,d,this._wallKeyPitch,zv,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=Uc(t);if(!i)return;const s=`${this._space}|${this._cfgEpoch}|${e.sp.rooms.length}`;Object.defineProperty(i,"sourceFingerprint",{value:Gd([e.sp,this._cellCm,this._gridPitch]),enumerable:!1});const n={key:s,value:i};kv(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));jv(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};kv(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?od(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=rr(d.length?yh(bh(a,d))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:ar(a,this._gridPitch,this._cellCm),l),c=_r({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=yc(e,t,i,s,n,o,a,r);if(!h)return c.map(()=>0);const d=4*sc(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;e<h.poly.length;e++){const t=h.poly[e],i=h.poly[(e+1)%h.poly.length];if(Sc(s[0],s[1],t[0],t[1],i[0],i[1])<=d)return Math.max(0,h.offsets[e]||0)}return 0})}(s,e,n,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,zv);if(!o||o.length!==t.length)return null;const a=this._cellCm/this._gridPitch;return t.map((e,i)=>function(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=oc(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=oc(e,t),_=rc(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`<g class="rszmeasurelayer" aria-hidden="true" pointer-events="none">
|
||
${t.map((e,t)=>W`<g class="rszmeasuredge"
|
||
data-hp="resize-measured-edge" data-edge-index=${t}>
|
||
<line class="rszmeasurehalo" x1=${e.edge.a[0]} y1=${e.edge.a[1]}
|
||
x2=${e.edge.b[0]} y2=${e.edge.b[1]}></line>
|
||
<line class="rszmeasureink" x1=${e.edge.a[0]} y1=${e.edge.a[1]}
|
||
x2=${e.edge.b[0]} y2=${e.edge.b[1]}></line>
|
||
</g>`)}
|
||
${i.map(e=>W`<line class="rszleader" data-hp="resize-area-leader"
|
||
data-room=${e.roomId}
|
||
x1=${e.placement.leader.a[0]} y1=${e.placement.leader.a[1]}
|
||
x2=${e.placement.leader.b[0]} y2=${e.placement.leader.b[1]}></line>`)}
|
||
</g>`}_renderResizeLayer(e){const t=Math.max(.013*e.w,5),i=t/2,s=e=>e.toFixed(1),n=`M ${s(-.7*i)} 0 H ${s(.7*i)} M 0 ${s(-.22*i)} V ${s(-i)} M ${s(-.32*i)} ${s(-.6*i)} L 0 ${s(-i)} L ${s(.32*i)} ${s(-.6*i)} M 0 ${s(.22*i)} V ${s(i)} M ${s(-.32*i)} ${s(.6*i)} L 0 ${s(i)} L ${s(.32*i)} ${s(.6*i)}`,o=[],a=this._rszRooms(),r=this._resize.snapshotIdentity||this._rszSnapshot();for(const e of a)for(let i=0;i<e.poly.length;i++){const a=e.poly[i],l=e.poly[(i+1)%e.poly.length];if(Math.hypot(l[0]-a[0],l[1]-a[1])<this._gridPitch)continue;const c=s((a[0]+l[0])/2),h=s((a[1]+l[1])/2),d=s(180*Math.atan2(l[1]-a[1],l[0]-a[0])/Math.PI),p=this._rszResolution(e.id,i,r),u=!p.enabled,_=u?this._rszReasonText(p.reason):this._t("title.markup_resize");o.push(W`<circle class="rszhandle ${u?"disabled":""}"
|
||
cx="${c}" cy="${h}" r="${s(t)}" tabindex="0" role="button"
|
||
aria-disabled="${u?"true":"false"}" aria-label="${_}"
|
||
@pointerdown=${t=>this._rszEdgeDown(t,e.id,i)}
|
||
@pointermove=${e=>this._rszMove(e)}
|
||
@pointerup=${e=>this._rszUp(e)}
|
||
@pointercancel=${e=>this._rszPointerCancel(e)}
|
||
@lostpointercapture=${e=>this._rszPointerCancel(e)}
|
||
@click=${u?e=>this._rszDisabledActivate(e,p.reason):null}
|
||
@keydown=${u?e=>this._rszDisabledKey(e,p.reason):null}>
|
||
<title>${_}</title>
|
||
</circle>`),o.push(W`<g class="rszicon ${u?"disabled":""}" transform="translate(${c} ${h}) rotate(${d})"><path class="rszhalo" d="${n}"></path><path class="rszink" d="${n}"></path></g>`)}return W`${o}`}get _openingsR(){const e=this._curSpaceCfg,t=this._spaceH,i=this._spaceModel();return i?(e?.openings||[]).flatMap(e=>{const s={...e,rx:e.x*zv,ry:e.y*t,rlen:e.length*zv};if(!e.host||"wall"===e.host.kind)return[s];const n=tu(e,i.partitions,zv,this._cellCm,this._gridPitch);if(!n.resolved)return"plan"===this._mode?[{...s,orphanReason:n.reason||"invalid-host"}]:[];let[o,a]=n.resolved.center;const r=this._physicalDrag;return r?.moved&&"partition"===r.kind&&r.id===e.host.id&&(o+=r.delta[0],a+=r.delta[1]),[{...e,rx:o,ry:a,rlen:n.resolved.length,angle:n.resolved.angle,partitionHost:n.resolved}]}):[]}_partitionOpeningCuts(e=this._spaceModel(),t=()=>!0){if(!e)return[];return qg(jg(this._curSpaceCfg?.id===e.id?this._curSpaceCfg:null,e,this._cellCm,this._gridPitch,zv),t)}_roomWallOpeningInputs(e=this._openingsR,t=this._spaceModel()){if(!t)return[];const i=this._openCuts();return Wg(e,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,zv)}_openingFace(e,t,i){return e.partitionHost?ou(e.partitionHost,i):Zc(t,{x:e.rx,y:e.ry,angle:e.angle,length:e.rlen,flip_v:i})}_cmToUnits(e){return e/this._cellCm*this._gridPitch}get _decorList(){const e=this._curSpaceCfg;return Array.isArray(e?.decor)?e.decor:[]}get _decorH(){return zv}_decorResolvedStyle(e){return function(e,t,i,s=Br){const n="rect"===e?.kind||"ellipse"===e?.kind,o=e,a=Ie(o?.color,s.color);return{color:a,opacity:jr(o?.opacity,s.opacity),widthCm:Vr(e,t,i,s.widthCm),fill:!!n&&!0===o?.fill,fillColor:Ie(o?.fill_color,o?.fill?a:s.fillColor),fillOpacity:n&&o?.fill?jr(o?.fill_opacity,.25):s.fillOpacity}}(e,this._cellCm,this._gridPitch,Br)}_decorWidthUnits(e){return((e,t,i,s=Br.widthCm)=>{const n=Number(e?.width_cm);if(Number.isFinite(n)&&n>0)return Ur(n,t,i);const o=Number(e?.width);return Number.isFinite(o)&&o>0?o:Ur(s,t,i)})(e,this._cellCm,this._gridPitch,Br.widthCm)}_decorTextSizeCm(e){if("text"===e?.kind){const t=Number(e.size_cm);return Number.isFinite(t)&&t>0?t:Gr(ts*is(e),this._cellCm,this._gridPitch)}return Gr(ts,this._cellCm,this._gridPitch)}_decorTextUnits(e){if("text"!==e.kind)return ts;const t=Number(e.size_cm);return Number.isFinite(t)&&t>0?Ur(t,this._cellCm,this._gridPitch):ts*is(e)}_decorSmallField(e){return Math.round(100*(this._imperial?e/2.54:e))/100}_decorSmallCm(e){const t=this._imperial?2.54*e:e;return Number.isFinite(t)?Math.max(.1,Math.min(100,t)):.1}_decorTextCm(e){const t=this._imperial?2.54*e:e;return Number.isFinite(t)?Math.max(.1,Math.min(fv,t)):.1}_decorLargeField(e){return Math.round(100*(this._imperial?e/30.48:e/100))/100}_decorLargeCm(e){const t=this._imperial?30.48*e:100*e;return Number.isFinite(t)?Math.max(.1,Math.min(Oh*this._cellCm,t)):.1}_angleField(e){const t=Number(e);return Number.isFinite(t)?String(Number(t.toFixed(3))):"0"}_decorBoxOf(e){return"rect"!==e.kind&&"ellipse"!==e.kind&&"furniture"!==e.kind?null:{x:e.x*zv,y:e.y*this._decorH,w:e.w*zv,h:e.h*this._decorH,angle:qr(e.angle)||void 0}}_decorSnapGeometry(e){const t=this._spaceModel();if(!t)return{points:[],segments:[]};const i=e||"",s=this._decorSnapCache;if(s&&s.epoch===this._cfgEpoch&&s.space===this._space&&s.height===this._decorH&&s.exclude===i)return s.geometry;const n=[];for(const t of this._decorList)if(t.id!==e)if("line"===t.kind){const e=[t.x1*zv,t.y1*this._decorH],i=[t.x2*zv,t.y2*this._decorH];n.push({points:[e,i,[(e[0]+i[0])/2,(e[1]+i[1])/2]],segments:[{a:e,b:i}]})}else if("text"===t.kind)n.push({points:[[t.x*zv,t.y*this._decorH]],segments:[]});else{const e=this._decorBoxOf(t);e&&n.push(Jr(e))}for(const e of t.rooms){const t=hi(e);t?.length&&n.push({points:t.flatMap((e,i)=>{const s=t[(i+1)%t.length];return[e,[(e[0]+s[0])/2,(e[1]+s[1])/2]]}),segments:t.map((e,i)=>({a:e,b:t[(i+1)%t.length]}))})}const o=function(e){return{points:e.flatMap(e=>e.points),segments:e.flatMap(e=>e.segments)}}(n);return this._decorSnapCache={epoch:this._cfgEpoch,space:this._space,height:this._decorH,exclude:i,geometry:o},o}_decorSnap(e,t="mouse",i){const s=this._stageEl,n=this._viewOr(this._baseVb()),o="touch"===t||"pen"===t?14:8,a=s?n.w/Math.max(1,s.clientWidth)*o:this._gridPitch;return function(e,t,i,s){const n=s(e);let o=null;for(const n of t.points){const t=s(n),a=Math.hypot(t[0]-e[0],t[1]-e[1]);a<=i&&(!o||a<o.d)&&(o={p:t,d:a,kind:"point",target:n})}for(const n of t.segments){const t=Zr(e,n.a,n.b),a=s(t),r=Math.hypot(a[0]-e[0],a[1]-e[1]);r<=i&&(!o||r<o.d)&&(o={p:a,d:r,kind:"edge",target:t})}return o?{point:o.p,target:o.target,kind:o.kind}:{point:n,target:null,kind:"grid"}}(e,this._decorSnapGeometry(i),a,e=>this._snap(e)).point}_replaceDecor(e,t){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>i.id===e?{...i,...t}:i),this.requestUpdate())}_cancelDecorGesture(){const e=this._decorMove?.before||this._dtDrag?.before||this._bdDrag?.before,t=e&&this._serverCfg?.spaces.find(t=>t.id===e.spaceId);if(e&&t){const i=e=>JSON.parse(JSON.stringify(e));void 0!==e.decor?t.decor=i(e.decor):delete t.decor;for(const e of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete t[e];Object.assign(t,i(e.plan_transform||{})),this._cfgEpoch++}this._decorMove=null,this._dtDrag=null,this._bdDrag=null,this.requestUpdate()}_decorPointerDown(e){const t=this._decorTool;if("select"===t||"erase"===t?e.target.closest?.(".dshape"):null)return!0;if("line"===t||"rect"===t||"ellipse"===t){e.preventDefault();const i=this._decorSnap(this._svgPoint(e),e.pointerType);return this._decorDraft={kind:t,a:i,b:i,pid:e.pointerId},jv(e),!0}if("text"===t){const t=this._decorSnap(this._svgPoint(e),e.pointerType);return this._decorTextDialog={x:Gh(t[0]/zv),y:Gh(t[1]/this._decorH),text:"",color:this._decorStyle.color,opacity:this._decorStyle.opacity,angle:"0",sizeCm:Gr(ts,this._cellCm,this._gridPitch)},this._decorTextSelection={start:0,end:0},!0}if("furniture"===t)return!!this._furnPalette&&(e.preventDefault(),this._furnPlace(this._svgPoint(e),e.shiftKey),!0);if("select"===t&&(this._decorSel=null),this._bdMovable){const t=this._bdRect,i=this._svgPoint(e),s=t.x+t.w/2,n=t.y+t.h/2,o=-qr(t.angle)*Math.PI/180,a=i[0]-s,r=i[1]-n,l=[s+a*Math.cos(o)-r*Math.sin(o),n+a*Math.sin(o)+r*Math.cos(o)];if(l[0]>=t.x&&l[0]<=t.x+t.w&&l[1]>=t.y&&l[1]<=t.y+t.h)return e.preventDefault(),this._bdStart(e)}return!1}_decorCommitDraft(){const e=this._decorDraft;if(this._decorDraft=null,!e)return;const t=.5*this._gridPitch;if(!function(e,t,i,s){const n=Math.abs(i[0]-t[0]),o=Math.abs(i[1]-t[1]);return"line"===e?Math.hypot(n,o)>=s:n>=s&&o>=s}(e.kind,e.a,e.b,t))return;const i=zv,s=this._decorH,n=this._decorStyle,o=this._geometrySnapshot(),a="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),r=Gh;let l;if("line"===e.kind)l={id:a,kind:"line",x1:r(e.a[0]/i),y1:r(e.a[1]/s),x2:r(e.b[0]/i),y2:r(e.b[1]/s),...Kr(n,!1)};else{const t=r(Math.min(e.a[0],e.b[0])/i),o=r(Math.min(e.a[1],e.b[1])/s),c=Math.abs(e.b[0]-e.a[0])/i,h=Math.abs(e.b[1]-e.a[1])/s;l={id:a,kind:e.kind,x:t,y:o,w:c,h:h,...Kr(n,!0)}}this._curSpaceCfg.decor=[...this._decorList,l],this._decorSel=a,this._recordGeometry(this._t("history.decor_add"),o),this._saveConfig(),this.requestUpdate()}_decorShapeDown(e,t){if("decor"!==this._mode)return;const i=this._decorTool;if("text"===i){if("text"!==t.kind)return;return e.stopPropagation(),e.preventDefault(),void this._decorOpenText(t)}"select"!==i&&"erase"!==i||(e.stopPropagation(),e.preventDefault(),"erase"!==i?(this._decorSel=t.id,this._decorMove={id:t.id,start:this._svgPoint(e),orig:JSON.parse(JSON.stringify(t)),pid:e.pointerId,moved:!1,before:this._geometrySnapshot()},jv(e)):this._decorEraseConfirm={id:t.id,kind:t.kind})}_decorMoveUpdate(e){const t=this._decorMove;if("furniture"===t.orig?.kind)return void this._furnMoveUpdate(e);const i=this._svgPoint(e),s=t.orig,n=("line"===s.kind?s.x1:s.x)*zv,o=("line"===s.kind?s.y1:s.y)*this._decorH,a=this._decorSnap([n+(i[0]-t.start[0]),o+(i[1]-t.start[1])],e.pointerType,t.id);let r=(a[0]-n)/zv,l=(a[1]-o)/this._decorH;const c=t.orig,h="line"===c.kind?Math.min(c.x1,c.x2):c.x,d="line"===c.kind?Math.min(c.y1,c.y2):c.y,p="line"===c.kind?Math.abs(c.x2-c.x1):c.w||0,u="line"===c.kind?Math.abs(c.y2-c.y1):c.h||0,_=Oh;r=Math.max(-_-h,Math.min(_-h-p,r)),l=Math.max(-_-d,Math.min(_-d-u,l)),(r||l)&&(t.moved=!0);this._curSpaceCfg.decor=this._decorList.map(e=>{if(e.id!==t.id)return e;const i=t.orig;return"line"===e.kind?{...e,x1:i.x1+r,y1:i.y1+l,x2:i.x2+r,y2:i.y2+l}:{...e,x:i.x+r,y:i.y+l}}),this.requestUpdate()}_decorShapeDbl(e,t){"decor"===this._mode&&"select"===this._decorTool&&(e.preventDefault(),e.stopPropagation(),this._decorMove=null,this._decorSel=t.id,this._openDecorProperties(t))}_openDecorProperties(e){if("decor"!==this._mode||"select"!==this._decorTool)return;if("text"===e.kind)return void this._decorOpenText(e);if(!["line","rect","ellipse","furniture"].includes(e.kind))return;const t=this._decorResolvedStyle(e),i="line"===e.kind?e:null,s=this._decorBoxOf(e);this._decorShapeDialog={id:e.id,kind:e.kind,color:t.color,opacity:t.opacity,widthCm:t.widthCm,angle:this._angleField(qr(i?Es([i.x1*zv,i.y1*this._decorH],[i.x2*zv,i.y2*this._decorH]):e.angle)),...i?{lengthCm:Gr(Math.hypot((i.x2-i.x1)*zv,(i.y2-i.y1)*this._decorH),this._cellCm,this._gridPitch),lineStyle:"dashed"===i.line_style?"dashed":"solid"}:{},...s?{sizeWCm:Gr(s.w,this._cellCm,this._gridPitch),sizeHCm:Gr(s.h,this._cellCm,this._gridPitch)}:{},..."furniture"===e.kind?{symbol:e.symbol}:{},..."rect"===e.kind||"ellipse"===e.kind?{fill:t.fill,fillColor:t.fillColor,fillOpacity:t.fillOpacity}:{}}}_decorOpenText(e){if("text"!==e.kind)return;let t=String(e.text??"");const i=[...t.matchAll(/\{([^{}\r\n]+)\}/g)].some(e=>!!Yi(e[1])),s=String(e.unit??"").trim(),n="state"===String(e.attr??"").trim().toLowerCase()?null:e.attr,o=i||s?"":Ji(e.entity,n),a=!(!String(e.entity??"").trim()||i||o&&!s);if(o&&!a){const e=t.indexOf("{}");t=e>=0?t.slice(0,e)+o+t.slice(e+2):`${t}${t?" ":""}${o}`}this._decorTextDialog={id:e.id,x:e.x,y:e.y,text:t,color:Ie(e.color,this._decorStyle.color),opacity:jr(e.opacity,this._decorStyle.opacity),angle:this._angleField(e.angle),sizeCm:this._decorTextSizeCm(e),pickerEntity:e.entity||"",preserveLegacy:a||void 0},this._decorTextSelection={start:t.length,end:t.length}}_decorRememberTextSelection(e){this._decorTextSelection={start:e.selectionStart??e.value.length,end:e.selectionEnd??e.value.length}}_decorInsertLiveVariable(e){const t=this._decorTextDialog;if(!t)return;const i=Ji(t.pickerEntity,e);if(!i)return;const s=t.text,n=Math.max(0,Math.min(s.length,this._decorTextSelection.start)),o=Math.max(n,Math.min(s.length,this._decorTextSelection.end));if(s.length-(o-n)+i.length>200)return;const a=s.slice(0,n)+i+s.slice(o),r=n+i.length;this._decorTextSelection={start:r,end:r},this._decorTextDialog={...t,text:a,preserveLegacy:void 0},this.updateComplete.then(()=>{const e=this.renderRoot.querySelector("textarea.dtarea");e&&(e.focus(),e.setSelectionRange(r,r))})}_decorSaveText(){const e=this._decorTextDialog,t=String(e?.text??"").replace(/\r\n?/g,"\n").trim();if(!e||!t)return void(this._decorTextDialog=null);const i=this._geometrySnapshot(),s=this._curSpaceCfg,n={color:e.color,opacity:jr(e.opacity),size_cm:Number(Math.max(.1,Math.min(fv,e.sizeCm)).toFixed(4)),...qr(e.angle)?{angle:qr(e.angle)}:{}};if(e.id)s.decor=this._decorList.map(i=>{if(i.id!==e.id)return i;if("text"!==i.kind)return i;if(e.preserveLegacy){const{angle:e,size:s,scale:o,...a}=i;return{...a,text:t,...n}}const{entity:s,attr:o,unit:a,...r}=i,{angle:l,size:c,scale:h,...d}=r;return{...d,text:t,...n}});else{const i="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);s.decor=[...this._decorList,{id:i,kind:"text",x:e.x,y:e.y,text:t,...n}],this._decorSel=i}this._decorTextDialog=null,this._recordGeometry(this._t(e.id?"history.decor_edit":"history.decor_add"),i),this._saveConfig(),this.requestUpdate()}_decorSaveShape(){const e=this._decorShapeDialog;if(!e)return;const t=this._geometrySnapshot(),i={color:e.color,opacity:jr(e.opacity),widthCm:Math.max(.1,Math.min(100,Number(e.widthCm)||.1)),fill:!!e.fill,fillColor:e.fillColor||e.color,fillOpacity:jr(e.fillOpacity,.25)};this._curSpaceCfg.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const s="rect"===e.kind||"ellipse"===e.kind,n=Kr(i,s);if("line"===t.kind){const i=(t.x1+t.x2)/2*zv,s=(t.y1+t.y2)/2*this._decorH,o=Ur(Math.max(.1,Number(e.lengthCm)||.1),this._cellCm,this._gridPitch),a=qr(e.angle)*Math.PI/180,r=Math.cos(a)*o/2,l=Math.sin(a)*o/2,c=this._snap([i-r,s-l]),h=this._snap([i+r,s+l]),{width:d,line_style:p,...u}=t;return{...u,...n,x1:Gh(c[0]/zv),y1:Gh(c[1]/this._decorH),x2:Gh(h[0]/zv),y2:Gh(h[1]/this._decorH),..."dashed"===e.lineStyle?{line_style:"dashed"}:{}}}if("rect"===t.kind||"ellipse"===t.kind||"furniture"===t.kind){const i=t.w*zv,s=t.h*this._decorH,o=Math.max(this._gridPitch,ri(Ur(Number(e.sizeWCm),this._cellCm,this._gridPitch),this._gridPitch)),a=Math.max(this._gridPitch,ri(Ur(Number(e.sizeHCm),this._cellCm,this._gridPitch),this._gridPitch)),r=t.x*zv+i/2,l=t.y*this._decorH+s/2,c=qr(e.angle),h=Wr({x:r-o/2,y:l-a/2},c,e=>this._snap(e)),{width:d,angle:p,...u}=t;return{...u,...n,x:Gh(h[0]/zv),y:Gh(h[1]/this._decorH),w:o/zv,h:a/this._decorH,..."furniture"===t.kind&&e.symbol?{symbol:e.symbol}:{},...c?{angle:c}:{}}}return t}),this._decorStyle={...i,fill:"rect"===e.kind||"ellipse"===e.kind?i.fill:this._decorStyle.fill,fillColor:"rect"===e.kind||"ellipse"===e.kind?i.fillColor:this._decorStyle.fillColor,fillOpacity:"rect"===e.kind||"ellipse"===e.kind?i.fillOpacity:this._decorStyle.fillOpacity},this._decorShapeDialog=null,this._recordGeometry(this._t("history.decor_edit"),t),this._saveConfig(),this.requestUpdate()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(e=>e.id===this._decorSel)||null}_dtPivot(e){return"line"===e.kind?[(e.x1+e.x2)/2*zv,(e.y1+e.y2)/2*this._decorH]:"furniture"===e.kind||"rect"===e.kind||"ellipse"===e.kind?[(e.x+e.w/2)*zv,(e.y+e.h/2)*this._decorH]:[e.x*zv,e.y*this._decorH]}_dtApply(e,t){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>{if(i.id!==e)return i;const s={...i};"text"===i.kind&&void 0!==t.textSizeCm&&(delete s.size,delete s.scale);const n={...s};return void 0!==t.textSizeCm&&(n.size_cm=Number(Math.max(.1,Math.min(fv,t.textSizeCm)).toFixed(4))),void 0!==t.angle&&(t.angle?n.angle=Number(t.angle.toFixed(2)):delete n.angle),n}),this._cfgEpoch++,this.requestUpdate())}_dtStart(e,t,i,s){const n=this._dtSel;if(!n)return;e.stopPropagation(),e.preventDefault();const[o,a]=this._dtPivot(n),r=this._svgPoint(e),l=this._decorBoxOf(n);this._dtDrag={id:n.id,kind:t,pid:e.pointerId,ax:o,ay:a,r0:Math.hypot(r[0]-o,r[1]-a),a0:180*Math.atan2(r[1]-a,r[0]-o)/Math.PI,textSizeCm0:"text"===n.kind?this._decorTextSizeCm(n):1,angle0:"line"===n.kind?0:Number(n.angle)||0,sgx:i?.[0],sgy:i?.[1],orig:l||void 0,origShape:JSON.parse(JSON.stringify(n)),before:this._geometrySnapshot(),lineEnd:s,moved:!1},jv(e)}_dtMove(e){const t=this._dtDrag;if(!t)return;const i=this._svgPoint(e);if(void 0!==t.lineEnd&&"line"===t.origShape.kind){const s=this._decorSnap(i,e.pointerType,t.id),n=Gh(s[0]/zv),o=Gh(s[1]/this._decorH),a=t.origShape,r=0===t.lineEnd?a.x1:a.x2,l=0===t.lineEnd?a.y1:a.y2;return(Math.abs(n-r)>1e-9||Math.abs(o-l)>1e-9)&&(t.moved=!0),void this._replaceDecor(t.id,0===t.lineEnd?{x1:n,y1:o}:{x2:n,y2:o})}if("scale"===t.kind&&t.orig){const s=Xr(t.orig,t.sgx??1,t.sgy??1,i[0],i[1],!e.shiftKey,this._gridPitch,this._gridPitch),n=Math.abs(s.x-t.orig.x)>1e-6||Math.abs(s.y-t.orig.y)>1e-6||Math.abs(s.w-t.orig.w)>1e-6||Math.abs(s.h-t.orig.h)>1e-6;if(!n&&!t.moved)return;return t.moved||=n,void this._decorApplyBox(t.id,s)}if("scale"===t.kind){const e=Math.hypot(i[0]-t.ax,i[1]-t.ay);if(t.r0<1e-6)return;const s=Math.max(.1,Math.min(fv,t.textSizeCm0*(e/t.r0))),n=Math.abs(s-t.textSizeCm0)>1e-6;if(!n&&!t.moved)return;return t.moved||=n,void this._dtApply(t.id,{textSizeCm:s})}const s=180*Math.atan2(i[1]-t.ay,i[0]-t.ax)/Math.PI;let n=t.angle0+(s-t.a0);e.shiftKey||(n=5*Math.round(n/5)),n=(n%360+360)%360,n>180&&(n-=360);const o=Math.abs(n-t.angle0)>1e-6;(o||t.moved)&&(t.moved||=o,this._dtApply(t.id,{angle:n}))}_dtUp(){const e=this._dtDrag;this._dtDrag=null,e?.moved&&(this._recordGeometry(this._t("history.decor_transform"),e.before),this._saveConfig()),this.requestUpdate()}_dtMeasure(){const e=this._dtSel;if(!e)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let t;if("line"===e.kind){const i=e.x1*zv,s=e.y1*this._decorH,n=e.x2*zv,o=e.y2*this._decorH;t={id:e.id,x:Math.min(i,n),y:Math.min(s,o),w:Math.abs(n-i),h:Math.abs(o-s)}}else if("furniture"===e.kind||"rect"===e.kind||"ellipse"===e.kind)t={id:e.id,x:e.x*zv,y:e.y*this._decorH,w:e.w*zv,h:e.h*this._decorH};else{const i=this.renderRoot.querySelector(`text.dtext[data-id="${e.id}"]`);if(!i||"function"!=typeof i.getBBox)return;let s;try{s=i.getBBox()}catch{return}if(!s||!s.width&&!s.height)return;t={id:e.id,x:s.x,y:s.y,w:s.width,h:s.height}}const i=this._dtBox;i&&i.id===t.id&&Math.abs(i.x-t.x)<.01&&Math.abs(i.y-t.y)<.01&&Math.abs(i.w-t.w)<.01&&Math.abs(i.h-t.h)<.01||(this._dtBox=t,this.requestUpdate())}_deleteDecor(e){if(!this._decorList.some(t=>t.id===e))return;const t=this._geometrySnapshot();this._curSpaceCfg.decor=this._decorList.filter(t=>t.id!==e),this._decorSel===e&&(this._decorSel=null),this._recordGeometry(this._t("history.decor_delete"),t),this._saveConfig(),this.requestUpdate()}_decorDeleteSel(){this._decorSel&&this._deleteDecor(this._decorSel)}_confirmDecorErase(){const e=this._decorEraseConfirm;this._decorEraseConfirm=null,e&&this._deleteDecor(e.id)}get _furnWalls(){const e=this._rawPhysicalBodiesR().flatMap(e=>e.map((t,i)=>{const s=e[(i+1)%e.length];return[t[0],t[1],s[0],s[1]]}));return[...this._segments,...e]}get _furnWallReach(){return 6*this._gridPitch}_furnFieldValue(e){return Math.round(100*(this._imperial?e/30.48:e/100))/100}_furnFieldToCm(e){return t=this._imperial?30.48*e:100*e,Number.isFinite(t)?Math.max(1,Math.min(1e4,t)):1;var t}_furnPick(e){const t=function(e){const t=pd(e);return t?{w:t.w,h:t.h}:{w:60,h:60}}(e);this._furnPalette={symbol:e,w:t.w,h:t.h}}_furnPlace(e,t=!1){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const n=zv,o=this._decorH,a=fd(_d(i.w,this._cellCm,this._gridPitch,n)),r=fd(_d(i.h,this._cellCm,this._gridPitch,n)),l=this._geometrySnapshot(),c=this._decorSnap(e);let h=c[0],d=c[1],p=0;const u=t?null:wd(h,d,r*o,this._furnWalls,this._furnWallReach,this._gridPitch);u&&(h=u.cx,d=u.cy,p=u.angle);const _="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),m={id:_,kind:"furniture",symbol:i.symbol,x:Gh(h/n-a/2),y:Gh(d/o-r/2),w:a,h:r,...Kr(this._decorStyle,!1)};p&&(m.angle=Number(p.toFixed(2))),s.decor=[...this._decorList,m],this._decorSel=_,this._decorTool="select",this._furnPalette=null,this._recordGeometry(this._t("history.decor_add"),l),this._saveConfig(),this.requestUpdate()}_furnMoveUpdate(e){const t=this._decorMove;if("furniture"!==t.orig.kind)return;const i=t.orig,s=this._curSpaceCfg;if(!s)return;const n=zv,o=this._decorH,a=this._svgPoint(e),r=(i.x+i.w/2)*n+(a[0]-t.start[0]),l=(i.y+i.h/2)*o+(a[1]-t.start[1]);let c,h,d=Number(i.angle)||0;const p=e.shiftKey?null:wd(r,l,i.h*o,this._furnWalls,this._furnWallReach,this._gridPitch);if(p)c=p.cx/n-i.w/2,h=p.cy/o-i.h/2,d=p.angle;else{const s=this._decorSnap([r-i.w/2*n,l-i.h/2*o],e.pointerType,t.id);c=s[0]/n,h=s[1]/o}c=Gh(c),h=Gh(h),(Math.abs(c-i.x)>1e-9||Math.abs(h-i.y)>1e-9||Math.abs(d-(Number(i.angle)||0))>1e-9)&&(t.moved=!0),s.decor=this._decorList.map(e=>{if(e.id!==t.id)return e;const i={...e,x:c,y:h};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_decorApplyBox(e,t){const i=this._curSpaceCfg;if(!i)return;const s=zv,n=this._decorH;i.decor=this._decorList.map(i=>{if(i.id!==e)return i;const o=Wr(t,i.angle,e=>this._snap(e));return{...i,x:Gh(o[0]/s),y:Gh(o[1]/n),w:Math.max(this._gridPitch/s,Math.min(2*Oh,t.w/s)),h:Math.max(this._gridPitch/n,Math.min(2*Oh,t.h/n))}}),this._cfgEpoch++,this.requestUpdate()}get _furnLive(){const e=this._dtDrag;if(!e||"scale"!==e.kind||!e.orig)return null;const t=this._decorList.find(t=>t.id===e.id);if(!t||"furniture"!==t.kind)return null;const i=zv,s=this._decorH,n=t.w*i,o=t.h*s,a=function(e,t,i,s,n){const o=e+i/2,a=t+s/2,r=(Number(n)||0)*Math.PI/180,l=Math.cos(r),c=Math.sin(r),h=(e,t)=>{const i=e-o,s=t-a;return[o+i*l-s*c,a+i*c+s*l]};return[h(e,t),h(e+i,t),h(e+i,t+s),h(e,t+s)]}(t.x*i,t.y*s,n,o,Number(t.angle)||0),r=(e,t)=>[(e[0]+t[0])/2,(e[1]+t[1])/2],l=r(a[0],a[1]),c=r(a[0],a[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[n,0])},{x:c[0],y:c[1],text:this._fmtLen([0,0],[0,o])}]}_renderFurnPalette(){const e=this._furnPalette,t=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return q`<div class="furnpalette" @pointerdown=${e=>e.stopPropagation()}>
|
||
<div class="furnhd">
|
||
<ha-icon icon="mdi:sofa-outline"></ha-icon>${this._t("furn.title")}
|
||
<span class="spacer"></span>
|
||
<button class="btn furnclose" title=${this._t("btn.close")}
|
||
@click=${()=>{this._furnPalette=null,this._decorTool="select"}}>
|
||
<ha-icon icon="mdi:close"></ha-icon>
|
||
</button>
|
||
</div>
|
||
<div class="furnbody">
|
||
${cd.map(t=>q`
|
||
<div class="furngroup" data-group=${t}>${this._t(`furn.group_${t}`)}</div>
|
||
<div class="furnrow">
|
||
${ud(t).map(t=>q`<button
|
||
class="furnitem ${e?.symbol===t.id?"on":""}" data-symbol=${t.id}
|
||
title=${this._t(`furn.sym_${t.id}`)}
|
||
@click=${()=>this._furnPick(t.id)}>
|
||
${(e=>{const t=pd(e),i=36/Math.max(t.w,t.h),s=t.w*i,n=t.h*i;return W`<svg class="furnprev" viewBox="0 0 40 40" aria-hidden="true"><g
|
||
transform="translate(${(40-s)/2} ${(40-n)/2})"><path
|
||
d=${bd(e,s,n)} fill="none" stroke="currentColor"
|
||
stroke-width="1.2" stroke-linejoin="round"></path></g></svg>`})(t.id)}<span>${this._t(`furn.sym_${t.id}`)}</span>
|
||
</button>`)}
|
||
</div>`)}
|
||
</div>
|
||
${e?q`<div class="furnsize">
|
||
<label>${this._t("furn.width")}<span class="furnunit">${t}</span></label>
|
||
<input class="namein furnw" type="number" min="0.01" step="0.05"
|
||
.value=${String(this._furnFieldValue(e.w))}
|
||
@input=${t=>this._furnPalette={...e,w:this._furnFieldToCm(Number(t.target.value))}} />
|
||
<label>${this._t("furn.depth")}<span class="furnunit">${t}</span></label>
|
||
<input class="namein furnh" type="number" min="0.01" step="0.05"
|
||
.value=${String(this._furnFieldValue(e.h))}
|
||
@input=${t=>this._furnPalette={...e,h:this._furnFieldToCm(Number(t.target.value))}} />
|
||
<span class="furnhint">${this._t("furn.place_hint")}</span>
|
||
</div>`:q`<div class="furnsize"><span class="furnhint">${this._t("furn.pick_hint")}</span></div>`}
|
||
</div>`}get _bdBase(){const e=this._curSpaceCfg;return e?.plan_url?{...Rh(e.plan_aspect,zv)}:null}get _bdRect(){const e=this._curSpaceCfg;return e?.plan_url?Fh(e,zv):null}get _bdParams(){const e=this._curSpaceCfg,t=Number(e?.plan_x),i=Number(e?.plan_y),s=Number(e?.plan_scale),n=Number(e?.plan_scale_x),o=Number(e?.plan_scale_y),a=Number.isFinite(s)&&s>0?s:1;return{dx:Number.isFinite(t)?t:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(n)&&n>0?n:a,sy:Number.isFinite(o)&&o>0?o:a,angle:qr(e?.plan_angle)}}_openBackdropDialog(e){if(!this._bdMovable||!this._bdRect)return;e?.preventDefault(),e?.stopPropagation(),this._bdDrag=null;const t=this._bdRect;this._backdropDialog={widthCm:Gr(t.w,this._cellCm,this._gridPitch),heightCm:Gr(t.h,this._cellCm,this._gridPitch),angle:this._angleField(t.angle)}}_saveBackdropDialog(){const e=this._backdropDialog,t=this._bdBase,i=this._bdRect;if(!e||!t||!i)return;const s=qr(e.angle),n=this._geometrySnapshot(),o=Math.min(t.w*zh,Math.max(t.w*Ah,ri(Ur(e.widthCm,this._cellCm,this._gridPitch),this._gridPitch))),a=Math.min(t.h*zh,Math.max(t.h*Ah,ri(Ur(e.heightCm,this._cellCm,this._gridPitch),this._gridPitch))),r=Wr({x:i.x+i.w/2-o/2,y:i.y+i.h/2-a/2},s,e=>this._snap(e));this._bdApply((r[0]-t.x)/zv,(r[1]-t.y)/zv,o/t.w,a/t.h,s),this._backdropDialog=null,this._recordGeometry(this._t("history.backdrop_transform"),n),this._saveConfig()}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&"backdrop"===this._decorTool}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(e,t,i,s,n){const o=this._curSpaceCfg;if(!o)return;o.plan_x=Number(Gh(e).toFixed(6)),o.plan_y=Number(Gh(t).toFixed(6)),delete o.plan_scale,o.plan_scale_x=Number(Math.min(zh,Math.max(Ah,i)).toFixed(6)),o.plan_scale_y=Number(Math.min(zh,Math.max(Ah,s)).toFixed(6));const a=qr(n);a?o.plan_angle=Number(a.toFixed(2)):delete o.plan_angle,this._cfgEpoch++,this.requestUpdate()}_bdStart(e,t,i=!1){const s=this._bdBase,n=this._bdRect;if(!s||!n)return!1;const o=this._svgPoint(e),a=t?t[0]:0,r=t?t[1]:0,l=a>0?n.x:n.x+n.w,c=r>0?n.y:n.y+n.h;return this._bdDrag={kind:i?"rotate":t?"scale":"move",pid:e.pointerId,sx:o[0],sy:o[1],base:s,p0:this._bdParams,fx:l,fy:c,sgx:a,sgy:r,rect0:{x:n.x,y:n.y,w:n.w,h:n.h,angle:n.angle},before:this._geometrySnapshot(),moved:!1},jv(e),!0}_bdMove(e){const t=this._bdDrag;if(!t)return;const i=this._svgPoint(e),s=t.base;if("move"===t.kind){const e=t.rect0.x,n=t.rect0.y,o=this._snap([e+(i[0]-t.sx),n+(i[1]-t.sy)]),a=Math.abs(o[0]-e)>1e-9||Math.abs(o[1]-n)>1e-9;if(!a&&!t.moved)return;return t.moved||=a,void this._bdApply((o[0]-s.x)/zv,(o[1]-s.y)/zv,t.p0.sx,t.p0.sy,t.p0.angle)}if("rotate"===t.kind){const s=t.rect0.x+t.rect0.w/2,n=t.rect0.y+t.rect0.h/2,o=180*Math.atan2(t.sy-n,t.sx-s)/Math.PI;let a=t.p0.angle+(180*Math.atan2(i[1]-n,i[0]-s)/Math.PI-o);e.shiftKey||(a=5*Math.round(a/5)),a=qr(a);const r=Math.abs(a-t.p0.angle)>1e-9;if(!r&&!t.moved)return;return t.moved||=r,void this._bdApply(t.p0.dx,t.p0.dy,t.p0.sx,t.p0.sy,a)}const n=Xr(t.rect0,t.sgx,t.sgy,i[0],i[1],!e.shiftKey,this._gridPitch,Math.min(s.w,s.h)*Ah),o=n.w/Math.max(1e-9,s.w),a=n.h/Math.max(1e-9,s.h),r=Math.abs(o-t.p0.sx)>1e-9||Math.abs(a-t.p0.sy)>1e-9||Math.abs(n.x-t.rect0.x)>1e-9||Math.abs(n.y-t.rect0.y)>1e-9;if(!r&&!t.moved)return;t.moved||=r;const l=Wr(n,t.p0.angle,e=>this._snap(e));this._bdApply((l[0]-s.x)/zv,(l[1]-s.y)/zv,o,a,t.p0.angle)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const e=this._bdParams;return 0!==e.dx||0!==e.dy||1!==e.sx||1!==e.sy||0!==e.angle}_bdReset(){const e=this._curSpaceCfg;if(!e)return;const t=this._geometrySnapshot();delete e.plan_x,delete e.plan_y,delete e.plan_scale,delete e.plan_scale_x,delete e.plan_scale_y,delete e.plan_angle,this._bdDrag=null,this._recordGeometry(this._t("history.backdrop_transform"),t),this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate()}_bdUp(){const e=this._bdDrag;this._bdDrag=null,e?.moved&&(this._recordGeometry(this._t("history.backdrop_transform"),e.before),this._saveConfig()),this.requestUpdate()}get _bdLive(){if(!this._bdDrag)return null;const e=this._bdRect;return e?{x:e.x+e.w/2,y:e.y+e.h/2,text:`${this._fmtLen([0,0],[e.w,0])} × ${this._fmtLen([0,0],[0,e.h])}`}:null}_renderBackdropFrame(e){const t=this._bdRect;if(!this._bdActive||!t)return G;const i=.02*Math.max(e.w,e.h),s=i/4,n=t.x+t.w/2,o=t.y+t.h/2,a=qr(t.angle),r=2.2*i;return W`<g class="bdframe" transform=${a?`rotate(${a} ${n} ${o})`:G}>
|
||
<rect class="bdbox" x="${t.x}" y="${t.y}" width="${t.w}" height="${t.h}"></rect>
|
||
<line class="dtstem" x1="${n}" y1="${t.y}" x2="${n}" y2="${t.y-r}"></line>
|
||
<circle class="bdhandle dtrot" cx="${n}" cy="${t.y-r}" r="${i.toFixed(1)}"
|
||
@pointerdown=${e=>{e.stopPropagation(),e.preventDefault(),this._bdStart(e,void 0,!0)}}></circle>
|
||
<circle class="bdknob" cx="${n}" cy="${t.y-r}" r="${s.toFixed(2)}"></circle>
|
||
${[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]].map(([e,n,o])=>{const a=e<0?t.x:t.x+t.w,r=n<0?t.y:t.y+t.h;return W`<circle
|
||
class="bdhandle bd-${o}" data-corner="${e+","+n}"
|
||
cx="${a}" cy="${r}" r="${i.toFixed(1)}"
|
||
@pointerdown=${t=>{t.stopPropagation(),t.preventDefault(),this._bdStart(t,[e,n])}}></circle><circle class="bdknob" cx="${a}" cy="${r}" r="${s.toFixed(2)}"></circle>`})}
|
||
</g>`}_renderTextFrame(e){const t=this._dtSel,i=this._dtBox;if(!t||!i||i.id!==t.id)return G;const s=.018*Math.max(e.w,e.h),n=s/4;if("line"===t.kind){const e=[t.x1*zv,t.y1*this._decorH],i=[t.x2*zv,t.y2*this._decorH];return W`<g class="dtframe dtlineframe">
|
||
<line class="dtbox" x1="${e[0]}" y1="${e[1]}" x2="${i[0]}" y2="${i[1]}"></line>
|
||
${[e,i].map((e,t)=>W`<circle class="dthandle dtendpoint" cx="${e[0]}" cy="${e[1]}"
|
||
r="${s.toFixed(1)}" @pointerdown=${e=>this._dtStart(e,"scale",void 0,t)}></circle>
|
||
<circle class="dtknob" cx="${e[0]}" cy="${e[1]}" r="${n.toFixed(2)}"></circle>`)}
|
||
</g>`}const[o,a]=this._dtPivot(t),r=Number(t.angle)||0,l=2.2*s;return W`<g class="dtframe" transform=${r?`rotate(${r} ${o} ${a})`:G}>
|
||
<rect class="dtbox" x="${i.x}" y="${i.y}" width="${i.w}" height="${i.h}"></rect>
|
||
<line class="dtstem" x1="${i.x+i.w/2}" y1="${i.y}" x2="${i.x+i.w/2}" y2="${i.y-l}"></line>
|
||
<circle class="dthandle dtrot" cx="${i.x+i.w/2}" cy="${i.y-l}" r="${s.toFixed(1)}"
|
||
@pointerdown=${e=>this._dtStart(e,"rotate")}></circle>
|
||
<circle class="dtknob" cx="${i.x+i.w/2}" cy="${i.y-l}" r="${n.toFixed(2)}"></circle>
|
||
${[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]].map(([e,t,o])=>W`<circle class="dthandle dt-${o}"
|
||
cx="${e<0?i.x:i.x+i.w}" cy="${t<0?i.y:i.y+i.h}" r="${s.toFixed(1)}"
|
||
@pointerdown=${i=>this._dtStart(i,"scale",[e,t])}></circle><circle class="dtknob"
|
||
cx="${e<0?i.x:i.x+i.w}" cy="${t<0?i.y:i.y+i.h}" r="${n.toFixed(2)}"></circle>`)}
|
||
</g>`}_renderDecorLayer(){const e=zv,t=this._decorH,i="decor"===this._mode,s=i&&"erase"===this._decorTool,n=this._decorList.map(n=>{const o="dshape"+(i&&this._decorSel===n.id?" dsel":""),a=this._decorResolvedStyle(n),r=this._decorWidthUnits(n),l=e=>this._decorShapeDown(e,n),c=e=>this._decorShapeDbl(e,n);if("line"===n.kind)return W`<line class="${o}" data-hp="decor" data-id="${n.id}" data-kind="${n.kind}"
|
||
x1="${n.x1*e}" y1="${n.y1*t}" x2="${n.x2*e}" y2="${n.y2*t}"
|
||
stroke="${a.color}" stroke-opacity="${a.opacity}" stroke-width="${r}"
|
||
stroke-dasharray=${"dashed"===n.line_style?`${4*r} ${3*r}`:G}
|
||
stroke-linecap="round" stroke-linejoin="round"
|
||
@pointerdown=${l} @dblclick=${c}></line>
|
||
${i&&"select"===this._decorTool?W`<line class="dshape dselecthit"
|
||
data-hp="decor" data-id="${n.id}" data-kind="${n.kind}"
|
||
x1="${n.x1*e}" y1="${n.y1*t}" x2="${n.x2*e}" y2="${n.y2*t}"
|
||
@pointerdown=${l} @dblclick=${c}></line>`:G}
|
||
${s?W`<line class="dshape derasehit" data-hp="decor" data-id="${n.id}" data-kind="${n.kind}"
|
||
x1="${n.x1*e}" y1="${n.y1*t}" x2="${n.x2*e}" y2="${n.y2*t}"
|
||
@pointerdown=${l}></line>`:G}`;if("rect"===n.kind){const i=(n.x+n.w/2)*e,h=(n.y+n.h/2)*t,d=qr(n.angle);return W`<rect class="${o}" data-hp="decor" data-id="${n.id}" data-kind="${n.kind}"
|
||
x="${n.x*e}" y="${n.y*t}" width="${n.w*e}" height="${n.h*t}"
|
||
stroke="${a.color}" stroke-opacity="${a.opacity}" stroke-width="${r}"
|
||
fill="${a.fill?a.fillColor:"none"}" fill-opacity="${a.fill?a.fillOpacity:0}"
|
||
transform=${d?`rotate(${d} ${i} ${h})`:G}
|
||
@pointerdown=${l} @dblclick=${c}></rect>
|
||
${s?W`<rect class="dshape derasehit" data-hp="decor" data-id="${n.id}" data-kind="${n.kind}"
|
||
x="${n.x*e}" y="${n.y*t}" width="${n.w*e}" height="${n.h*t}"
|
||
transform=${d?`rotate(${d} ${i} ${h})`:G} @pointerdown=${l}></rect>`:G}`}if("ellipse"===n.kind){const i=(n.x+n.w/2)*e,h=(n.y+n.h/2)*t,d=qr(n.angle);return W`<ellipse class="${o}" data-hp="decor" data-id="${n.id}" data-kind="${n.kind}"
|
||
cx="${i}" cy="${h}"
|
||
rx="${n.w/2*e}" ry="${n.h/2*t}" stroke="${a.color}" stroke-opacity="${a.opacity}" stroke-width="${r}"
|
||
fill="${a.fill?a.fillColor:"none"}" fill-opacity="${a.fill?a.fillOpacity:0}"
|
||
transform=${d?`rotate(${d} ${i} ${h})`:G}
|
||
@pointerdown=${l} @dblclick=${c}></ellipse>
|
||
${s?W`<ellipse class="dshape derasehit" data-hp="decor" data-id="${n.id}" data-kind="${n.kind}"
|
||
cx="${i}" cy="${h}" rx="${n.w/2*e}" ry="${n.h/2*t}"
|
||
transform=${d?`rotate(${d} ${i} ${h})`:G} @pointerdown=${l}></ellipse>`:G}`}if("furniture"===n.kind){const i=n.w*e,h=n.h*t,d=bd(n.symbol,i,h);if(!d)return G;const p=Number(n.angle)||0,u=n.x*e+i/2,_=n.y*t+h/2,m=`${p?`rotate(${p} ${u} ${_}) `:""}translate(${n.x*e} ${n.y*t})`;return W`<path class="${o} dfurn" data-hp="decor" data-id="${n.id}"
|
||
data-kind="${n.kind}" data-symbol="${n.symbol}" d="${d}" transform=${m}
|
||
stroke="${a.color}" stroke-opacity="${a.opacity}" stroke-width="${r}" fill="none"
|
||
stroke-linecap="round" stroke-linejoin="round"
|
||
@pointerdown=${l} @dblclick=${c}></path>
|
||
${s?W`<path class="dshape derasehit" data-hp="decor" data-id="${n.id}"
|
||
data-kind="${n.kind}" data-symbol="${n.symbol}" d="${d}" transform=${m}
|
||
@pointerdown=${l}></path>`:G}`}if("text"===n.kind){const i=this._decorTextUnits(n),s=this._renderDeviceSnapshot?.facts.get(`decor:${this._space}:${n.id}`),r=function(e){return String(e??"").replace(/\r\n?/g,"\n").split("\n")}("string"==typeof s?s:es(n.text,n,this._renderPlanHass,e=>this._renderEntityAvailable(e))),h=n.x*e,d=n.y*t,p=Number(n.angle)||0,u=d-(r.length-1)*i*1.2/2;return W`<text class="${o} dtext" data-hp="decor" data-id="${n.id}" data-kind="${n.kind}"
|
||
x="${h}" y="${d}" fill="${a.color}" fill-opacity="${a.opacity}"
|
||
font-size="${i}" transform=${p?`rotate(${p} ${h} ${d})`:G}
|
||
@pointerdown=${l} @dblclick=${c}>${r.map((e,t)=>W`<tspan x="${h}" y="${u+t*i*1.2}">${e}</tspan>`)}</text>`}return G});let o=G;const a=this._decorDraft;if(a){const e=this._decorStyle,t=Ur(e.widthCm,this._cellCm,this._gridPitch);if("line"===a.kind)o=W`<line class="ddraft" x1="${a.a[0]}" y1="${a.a[1]}" x2="${a.b[0]}" y2="${a.b[1]}"
|
||
stroke="${e.color}" stroke-opacity="${e.opacity}" stroke-width="${t}" stroke-linecap="round" stroke-linejoin="round"></line>`;else{const i=Math.min(a.a[0],a.b[0]),s=Math.min(a.a[1],a.b[1]),n=Math.abs(a.b[0]-a.a[0]),r=Math.abs(a.b[1]-a.a[1]);o="rect"===a.kind?W`<rect class="ddraft" x="${i}" y="${s}" width="${n}" height="${r}" stroke="${e.color}"
|
||
stroke-opacity="${e.opacity}" stroke-width="${t}" fill="${e.fill?e.fillColor:"none"}" fill-opacity="${e.fill?e.fillOpacity:0}"></rect>`:W`<ellipse class="ddraft" cx="${i+n/2}" cy="${s+r/2}" rx="${n/2}" ry="${r/2}"
|
||
stroke="${e.color}" stroke-opacity="${e.opacity}" stroke-width="${t}" fill="${e.fill?e.fillColor:"none"}" fill-opacity="${e.fill?e.fillOpacity:0}"></ellipse>`}}return W`<g class="decorlayer">${n}${o}</g>`}get _editorToolbarGroups(){return"plan"!==this._mode?[]:[{id:"opening",label:this._t("markup.opening"),icon:"mdi:door",activeItemId:"opening"===this._tool?this._openingPreset?.type:void 0,items:[{id:"window",label:this._t("opening.window"),icon:"mdi:window-closed-variant",role:"tool",invoke:()=>this._activateOpeningPlacement("window")},{id:"door",label:this._t("opening.door"),icon:"mdi:door-open",role:"tool",invoke:()=>this._activateOpeningPlacement("door")},{id:"passage",label:this._t("opening.passage"),icon:"mdi:arch",role:"tool",invoke:()=>this._activateOpeningPlacement("passage")},{id:"gate",label:this._t("opening.gate"),icon:"mdi:gate",role:"tool",invoke:()=>this._activateOpeningPlacement("gate")}]}]}_renderEditorGroupLauncher(e){return this._editorSecondary.renderGroupLauncher(e,this._editorToolbarGroups,this._editorSecondaryCopy)}get _editorSecondaryContextId(){const e=`editor:${this._mode}:${this._space}:${this._cfgEpoch}`,t=this._editorSecondary.activeGroup(this._editorToolbarGroups);if(t)return`${e}:group:${t.id}:${this._editorSecondary.groupGeneration}`;if("plan"===this._mode){const t=this._physicalSel;return t?`${e}:selection:${t.kind}:${t.id}:${t.segment??""}`:`${e}:tool:${this._tool}:${this._path.length}`}return"decor"===this._mode?"furniture"===this._decorTool?`${e}:palette:furniture:${this._furnPalette?.symbol||"none"}`:"select"===this._decorTool&&this._decorSel?`${e}:selection:decor:${this._decorSel}`:`${e}:tool:${this._decorTool}:${this._bdMoved?1:0}:${this._bdDrag?1:0}`:`${e}:none`}_runEditorContext(e,t){this._editorSecondary.runContext(e,this._editorSecondaryContextId,t)}_renderEditorGroupModel(e){return this._editorSecondary.renderGroupModel(e,this._editorSecondaryContextId,this._editorSecondaryCopy)}_renderDrawWallControl(){const e="column"===this._tool?1:0;return q`<label class="drawwall ${null==this._drawWallCm?"invalid":""}">${this._t("wallthick.field")}
|
||
<input type="number" min=${Sl(e,this._imperial)}
|
||
max=${Sl(this._drawWallMaxCm,this._imperial)} step="any"
|
||
.value=${this._drawWallFieldValue}
|
||
@input=${e=>{this._drawWallField=e.target.value}}
|
||
title=${this._t("draw"===this._tool?"markup.draw_wall_title":"physical.column_size_title")} />
|
||
<span class="opl">${this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}</span>
|
||
<span class="rangehint">${this._t("physical.allowed_range",{min:Sl(e,this._imperial),max:Sl(this._drawWallMaxCm,this._imperial),unit:this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")})}</span>
|
||
</label>`}_renderPlanSecondary(){const e=this._editorSecondaryContextId,t=this._physicalSel;if(t){if(!("partition"===t.kind?!!this._curSpaceCfg.partitions?.some(e=>e.id===t.id):"column"===t.kind?!!this._curSpaceCfg.wall_columns?.some(e=>e.id===t.id):!!this._curSpaceCfg.room_drafts?.some(e=>e.id===t.id)))return null;const i="partition"===t.kind?this._t("markup.partition"):"column"===t.kind?this._t("markup.column"):this._t("markup.add");return{contextId:e,kind:"selection",ariaLabel:this._t("editor.context_actions",{object:i}),visibleLabel:i,content:q`
|
||
<button class="btn ghost" @click=${()=>this._runEditorContext(e,()=>{const e=this._physicalSel;e&&this._openPhysicalDialog(e.kind,e.id,e.segment)})}><ha-icon icon="mdi:tune"></ha-icon>${this._t("btn.properties")}</button>
|
||
<button class="btn danger" @click=${()=>this._runEditorContext(e,()=>this._deletePhysicalSelection())}>
|
||
<ha-icon icon="mdi:delete-outline"></ha-icon>${this._t("btn.delete")}
|
||
</button>`}}const i="draw"===this._tool||"column"===this._tool,s="column"===this._tool?"markup.hint_column":"resize"===this._tool?"markup.hint_resize":"wallthick"===this._tool?"markup.hint_wallthick":null,n="draw"===this._tool?this._t(this._path.length?"markup.hint_points":"markup.hint_start",this._path.length?{n:this._path.length}:void 0):"";if(!i&&!s&&!n)return null;const o="draw"===this._tool&&this._path.length>0;return{contextId:e,kind:o?"operation":"tool",ariaLabel:this._t("editor.tool_options",{tool:this._t("draw"===this._tool?"markup.add":"column"===this._tool?"markup.column":"resize"===this._tool?"markup.resize":"markup.wallthick")}),content:q`
|
||
${i?this._renderDrawWallControl():G}
|
||
${n?q`<span class="hint">${n}</span>`:G}
|
||
${s?q`<span class="hint">${this._t(s)}</span>`:G}
|
||
${"draw"===this._tool&&this._path.length?q`<button class="btn ghost" @click=${()=>this._runEditorContext(e,()=>this._cancelPath())}>
|
||
${this._t("btn.reset")}
|
||
</button>`:G}`}}_renderDecorSecondary(){const e=this._editorSecondaryContextId;if("furniture"===this._decorTool)return{contextId:e,kind:"palette",ariaLabel:this._t("editor.palette",{tool:this._t("decor.furniture")}),launcherId:"furniture",dismissPolicy:this._furnPalette?"stay-open-on-canvas":"outside",dismiss:()=>{"furniture"===this._decorTool&&(this._furnPalette=null,this._decorTool="select",this.requestUpdate())},content:this._renderFurnPalette()};const t=this._dtSel;if(t){const i=this._t(`decor.${t.kind}`);return{contextId:e,kind:"selection",ariaLabel:this._t("editor.context_actions",{object:i}),visibleLabel:i,content:q`
|
||
<button class="btn ghost" @click=${()=>this._runEditorContext(e,()=>{const e=this._dtSel;e&&this._openDecorProperties(e)})}><ha-icon icon="mdi:tune"></ha-icon>${this._t("btn.properties")}</button>
|
||
<button class="btn danger" @click=${()=>this._runEditorContext(e,()=>this._decorDeleteSel())}>
|
||
<ha-icon icon="mdi:delete-outline"></ha-icon>${this._t("btn.delete")}
|
||
</button>`}}const i="line"===this._decorTool||"rect"===this._decorTool||"ellipse"===this._decorTool,s="rect"===this._decorTool||"ellipse"===this._decorTool,n="backdrop"===this._decorTool&&!!this._bdRect;return i||n?{contextId:e,kind:"tool",ariaLabel:this._t("editor.tool_options",{tool:this._t(i?`decor.${this._decorTool}`:"decor.backdrop")}),content:q`
|
||
${i?q`
|
||
<hp-color-opacity .label=${this._t("decor.color")} .color=${this._decorStyle.color}
|
||
.opacity=${this._decorStyle.opacity} .opacityLabel=${this._t("space.opacity")}
|
||
.pickerLabels=${this._colorPickerLabels}
|
||
@hp-color-opacity-change=${e=>this._decorStyle={...this._decorStyle,...e.detail}}></hp-color-opacity>
|
||
<label class="drawwall">${this._t("decor.width")}
|
||
<input type="number" min=${this._decorSmallField(.1)}
|
||
max=${this._decorSmallField(100)} step="0.1"
|
||
.value=${String(this._decorSmallField(this._decorStyle.widthCm))}
|
||
@input=${e=>this._decorStyle={...this._decorStyle,widthCm:this._decorSmallCm(Number(e.target.value))}} />
|
||
<span class="opl">${this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}</span>
|
||
</label>
|
||
${s?q`<label class="dfill"><input type="checkbox" .checked=${this._decorStyle.fill}
|
||
@change=${e=>this._decorStyle={...this._decorStyle,fill:e.target.checked}} />${this._t("decor.fill")}</label>
|
||
<hp-color-opacity .label=${this._t("decor.fill_color")} .color=${this._decorStyle.fillColor}
|
||
.opacity=${this._decorStyle.fillOpacity} .opacityLabel=${this._t("space.opacity")}
|
||
.pickerLabels=${this._colorPickerLabels}
|
||
.disabled=${!this._decorStyle.fill}
|
||
@hp-color-opacity-change=${e=>this._decorStyle={...this._decorStyle,fillColor:e.detail.color,fillOpacity:e.detail.opacity}}></hp-color-opacity>`:G}
|
||
`:G}
|
||
${n?q`<span class="bdhint">${this._t("decor.backdrop_hint")}</span>`:G}`}:null}_withBackdropReset(e){if(!this._bdMoved||this._bdDrag||"palette"===e?.kind)return e;const t=this._editorSecondaryContextId,i=q`<button class="btn bdreset" title=${this._t("decor.backdrop_reset")}
|
||
@click=${()=>this._runEditorContext(t,()=>this._bdReset())}>
|
||
<ha-icon icon="mdi:image-refresh-outline"></ha-icon>${this._t("decor.backdrop_reset")}
|
||
</button>`;return e?{...e,kind:"selection"===e.kind?"mixed":e.kind,content:q`${e.content}${i}`}:{contextId:t,kind:"tool",ariaLabel:this._t("editor.tool_options",{tool:this._t("decor.backdrop")}),content:i}}get _editorSecondaryDialogBlocked(){return!!(this._tapConfirm||this._vacCalConfirm||this._roomDialog||this._mergeDialog||this._openingDialog||this._physicalDialog||this._openingInfo||this._decorTextDialog||this._decorShapeDialog||this._backdropDialog||this._decorEraseConfirm||this._spaceDialog||this._markerDialog||this._deviceInbox||this._infoCard||this._rulesDialog||this._settingsDialog||this._alignDialog||this._importDialog||this._kioskDialog||this._backupExportDialog||this._backupImportDialog||this._wallDialog)}_renderEditorSecondary(){if(!this._editing)return G;const e=this._editorSecondary.activeGroup(this._editorToolbarGroups),t=e?this._renderEditorGroupModel(e):"plan"===this._mode?this._renderPlanSecondary():"decor"===this._mode?this._withBackdropReset(this._renderDecorSecondary()):null;return this._editorSecondary.render(t,this._editorSecondaryDialogBlocked)}_renderDecorBar(){const e=[["select","mdi:cursor-default-outline","decor.select"],...this._bdRect?[["backdrop","mdi:image-move","decor.backdrop"]]:[],["line","mdi:vector-line","decor.line"],["rect","mdi:rectangle-outline","decor.rect"],["ellipse","mdi:ellipse-outline","decor.ellipse"],["text","mdi:format-text","decor.text"],["furniture","mdi:sofa-outline","decor.furniture"],["erase","mdi:eraser","decor.erase"]],t=this._geometryHistory.undoName,i=this._geometryHistory.redoName;return q`<div class="editbar decorbar">
|
||
<div class="editbar-tools" tabindex="-1" ?inert=${this._modeTransitionBusy}>
|
||
${e.map(([e,t,i])=>q`<button class="btn dtool ${this._decorTool===e?"on":""}"
|
||
data-editor-palette=${"furniture"===e?"furniture":G}
|
||
@click=${()=>{if("furniture"===e&&"furniture"===this._decorTool)return this._furnPalette=null,void(this._decorTool="select");"furniture"===e&&this._editorSecondary.openPalette(),this._decorTool=e,this._decorDraft=null,"furniture"!==e&&(this._furnPalette=null)}}
|
||
title=${this._t(i)}>
|
||
<ha-icon icon=${t}></ha-icon><span class="ml">${this._t(i)}</span>
|
||
</button>`)}
|
||
${this._editorToolbarGroups.map(e=>this._renderEditorGroupLauncher(e))}
|
||
<button class="btn ghost" @click=${this._undoGeometry} ?disabled=${!t}
|
||
title=${t?this._t("history.undo_named",{name:t}):this._t("history.undo_empty")}>
|
||
<ha-icon icon="mdi:undo-variant"></ha-icon>${this._t("history.undo")}
|
||
</button>
|
||
<button class="btn ghost" @click=${this._redoGeometry} ?disabled=${!i}
|
||
title=${i?this._t("history.redo_named",{name:i}):this._t("history.redo_empty")}>
|
||
<ha-icon icon="mdi:redo-variant"></ha-icon>${this._t("history.redo")}
|
||
</button>
|
||
</div>
|
||
<div class="editbar-end">
|
||
<button class="btn barclose" title=${this._t("title.close_editor")}
|
||
data-editor-navigation="view"
|
||
@click=${()=>this._setMode("view")}>
|
||
<ha-icon icon="mdi:close"></ha-icon>
|
||
</button>
|
||
</div>
|
||
</div>`}_renderDecorEraseConfirm(){const e=this._decorEraseConfirm,t=this._t(`decor.${e.kind}`);return q`<hp-dialog .hass=${this.hass} .title=${this._t("decor.erase_confirm_title")}
|
||
icon="mdi:eraser" dismiss-on-scrim @hp-close=${()=>this._decorEraseConfirm=null}>
|
||
<div class="body"><p>${this._t("confirm.erase_decor",{kind:t})}</p></div>
|
||
<div class="row" slot="footer">
|
||
<span class="spacer"></span>
|
||
<button class="btn ghost" @click=${()=>this._decorEraseConfirm=null}>
|
||
${this._t("btn.cancel")}
|
||
</button>
|
||
<button class="btn danger" @click=${this._confirmDecorErase}>
|
||
<ha-icon icon="mdi:eraser"></ha-icon>${this._t("decor.erase")}
|
||
</button>
|
||
</div>
|
||
</hp-dialog>`}_renderDecorTextDialog(){const e=this._decorTextDialog,t=(e.pickerEntity||"").trim(),i=t?this.hass?.states?.[t]:null;return q`<hp-dialog .hass=${this.hass} .title=${this._t("decor.text_title")}
|
||
icon="mdi:format-text" dismiss-on-scrim @hp-close=${()=>this._decorTextDialog=null}>
|
||
<div class="body">
|
||
<label>${this._t("decor.text_label")}</label>
|
||
${""}
|
||
<textarea class="namein dtarea" rows="3" maxlength="200" .value=${e.text} autofocus
|
||
@input=${t=>{const i=t.target;this._decorRememberTextSelection(i),this._decorTextDialog={...e,text:i.value}}}
|
||
@click=${e=>this._decorRememberTextSelection(e.target)}
|
||
@keyup=${e=>this._decorRememberTextSelection(e.target)}
|
||
@select=${e=>this._decorRememberTextSelection(e.target)}
|
||
@blur=${e=>this._decorRememberTextSelection(e.target)}
|
||
@keydown=${e=>{e.stopPropagation(),"Enter"===e.key&&(e.ctrlKey||e.metaKey)&&this._decorSaveText()}}></textarea>
|
||
<hp-color-opacity .label=${this._t("decor.color")} .color=${e.color} .opacity=${e.opacity}
|
||
.opacityLabel=${this._t("space.opacity")} .pickerLabels=${this._colorPickerLabels}
|
||
@hp-color-opacity-change=${t=>this._decorTextDialog={...e,...t.detail}}></hp-color-opacity>
|
||
<label>${this._t("decor.text_size")}</label>
|
||
<div class="colorrow"><input class="namein" type="number" min="0.1"
|
||
max=${this._decorSmallField(fv)} step="0.1"
|
||
.value=${String(this._decorSmallField(e.sizeCm))}
|
||
@input=${t=>this._decorTextDialog={...e,sizeCm:this._decorTextCm(Number(t.target.value))}} />
|
||
<span class="opl">${this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}</span></div>
|
||
<label>${this._t("decor.angle")}</label>
|
||
<input class="namein" type="number" min="-180" max="180" step="1" .value=${e.angle}
|
||
@input=${t=>this._decorTextDialog={...e,angle:t.target.value}} />
|
||
<label class="dispsection">${this._t("decor.live_group")}</label>
|
||
<label>${this._t("decor.live_entity")}</label>
|
||
<input class="namein" type="text" list="hp-dtext-ents" placeholder=${this._t("decor.live_entity_ph")}
|
||
.value=${e.pickerEntity||""}
|
||
@input=${t=>this._decorTextDialog={...e,pickerEntity:t.target.value}} />
|
||
<datalist id="hp-dtext-ents">
|
||
${Object.keys(this.hass?.states||{}).map(e=>q`<option value=${e}></option>`)}
|
||
</datalist>
|
||
${t?q`
|
||
<label>${this._t("decor.live_attr")}</label>
|
||
<select id="decor-live-attribute" class="namein" .value=${""}
|
||
@change=${e=>{const t=e.target.value;t&&this._decorInsertLiveVariable("__state__"===t?null:t)}}>
|
||
<option value="">${this._t("decor.live_attr_ph")}</option>
|
||
<option value="__state__">${this._t("decor.live_state")}</option>
|
||
${Object.keys(i?.attributes||{}).filter(e=>!!Ji(t,e)).map(e=>q`<option value=${e}>${e}</option>`)}
|
||
</select>
|
||
`:G}
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<span class="spacer"></span>
|
||
<button class="btn ghost" @click=${()=>this._decorTextDialog=null}>${this._t("btn.cancel")}</button>
|
||
<button class="btn primary" ?disabled=${!e.text.trim()} @click=${()=>this._decorSaveText()}>${this._t("btn.save")}</button>
|
||
</div>
|
||
</hp-dialog>`}_renderDecorShapeDialog(){const e=this._decorShapeDialog,t="rect"===e.kind||"ellipse"===e.kind,i=this._t("decor."+e.kind),s=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return q`<hp-dialog .hass=${this.hass}
|
||
.title=${this._t("decor.object_title",{kind:i})} icon="mdi:pencil-outline"
|
||
dismiss-on-scrim @hp-close=${()=>this._decorShapeDialog=null}>
|
||
<div class="body">
|
||
${"furniture"===e.kind?q`
|
||
<label>${this._t("furn.symbol")}</label>
|
||
<select class="namein"
|
||
@change=${t=>this._decorShapeDialog={...e,symbol:t.target.value}}>
|
||
${cd.map(t=>q`<optgroup label=${this._t(`furn.group_${t}`)}>
|
||
${ud(t).map(t=>q`<option value=${t.id}
|
||
?selected=${t.id===e.symbol}>
|
||
${this._t(`furn.sym_${t.id}`)}
|
||
</option>`)}
|
||
</optgroup>`)}
|
||
</select>`:G}
|
||
<hp-color-opacity .label=${this._t("decor.color")} .color=${e.color} .opacity=${e.opacity}
|
||
.opacityLabel=${this._t("space.opacity")} .pickerLabels=${this._colorPickerLabels}
|
||
@hp-color-opacity-change=${t=>this._decorShapeDialog={...e,...t.detail}}></hp-color-opacity>
|
||
<label>${this._t("decor.width")}</label>
|
||
<div class="colorrow"><input class="namein" type="number"
|
||
min=${this._decorSmallField(.1)} max=${this._decorSmallField(100)} step="0.1"
|
||
.value=${String(this._decorSmallField(e.widthCm))}
|
||
@input=${t=>this._decorShapeDialog={...e,widthCm:this._decorSmallCm(Number(t.target.value))}} /><span class="opl">${this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}</span></div>
|
||
${"line"===e.kind?q`
|
||
<label>${this._t("decor.line_style")}</label>
|
||
<div role="radiogroup" aria-label=${this._t("decor.line_style")}>
|
||
<label class="srcrow"><input type="radio" name="decor-line-style"
|
||
.checked=${"dashed"!==e.lineStyle}
|
||
@change=${()=>this._decorShapeDialog={...e,lineStyle:"solid"}} />
|
||
<span>${this._t("decor.line_style_solid")}</span></label>
|
||
<label class="srcrow"><input type="radio" name="decor-line-style"
|
||
.checked=${"dashed"===e.lineStyle}
|
||
@change=${()=>this._decorShapeDialog={...e,lineStyle:"dashed"}} />
|
||
<span>${this._t("decor.line_style_dashed")}</span></label>
|
||
</div>
|
||
<label>${this._t("decor.length")}</label>
|
||
<div class="colorrow"><input class="namein" type="number" min="0.01" step="0.01"
|
||
.value=${String(this._decorLargeField(e.lengthCm||0))}
|
||
@input=${t=>this._decorShapeDialog={...e,lengthCm:this._decorLargeCm(Number(t.target.value))}} />
|
||
<span class="opl">${s}</span></div>`:q`
|
||
<label>${this._t("decor.size")}</label>
|
||
<div class="colorrow"><input class="namein" type="number" min="0.01" step="0.01"
|
||
.value=${String(this._decorLargeField(e.sizeWCm||0))}
|
||
@input=${t=>this._decorShapeDialog={...e,sizeWCm:this._decorLargeCm(Number(t.target.value))}} />
|
||
<span>×</span><input class="namein" type="number" min="0.01" step="0.01"
|
||
.value=${String(this._decorLargeField(e.sizeHCm||0))}
|
||
@input=${t=>this._decorShapeDialog={...e,sizeHCm:this._decorLargeCm(Number(t.target.value))}} />
|
||
<span class="opl">${s}</span></div>`}
|
||
<label>${this._t("decor.angle")}</label>
|
||
<input class="namein" type="number" min="-180" max="180" step="1" .value=${e.angle}
|
||
@input=${t=>this._decorShapeDialog={...e,angle:t.target.value}} />
|
||
${t?q`<label class="dfill"><input type="checkbox" .checked=${!!e.fill}
|
||
@change=${t=>this._decorShapeDialog={...e,fill:t.target.checked}} />${this._t("decor.fill")}</label>
|
||
<hp-color-opacity .label=${this._t("decor.fill_color")}
|
||
.color=${e.fillColor||e.color} .opacity=${e.fillOpacity??.25}
|
||
.opacityLabel=${this._t("space.opacity")} .pickerLabels=${this._colorPickerLabels}
|
||
.disabled=${!e.fill}
|
||
@hp-color-opacity-change=${t=>this._decorShapeDialog={...e,fillColor:t.detail.color,fillOpacity:t.detail.opacity}}></hp-color-opacity>`:G}
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<span class="spacer"></span>
|
||
<button class="btn ghost" @click=${()=>this._decorShapeDialog=null}>${this._t("btn.cancel")}</button>
|
||
<button class="btn primary" @click=${()=>this._decorSaveShape()}>${this._t("btn.save")}</button>
|
||
</div>
|
||
</hp-dialog>`}_renderBackdropDialog(){const e=this._backdropDialog,t=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return q`<hp-dialog .hass=${this.hass} .title=${this._t("decor.backdrop_properties")}
|
||
icon="mdi:image-edit-outline" dismiss-on-scrim @hp-close=${()=>this._backdropDialog=null}>
|
||
<div class="body">
|
||
<label>${this._t("decor.size")}</label>
|
||
<div class="colorrow"><input class="namein" type="number" min="0.01" step="0.01"
|
||
.value=${String(this._decorLargeField(e.widthCm))}
|
||
@input=${t=>this._backdropDialog={...e,widthCm:this._decorLargeCm(Number(t.target.value))}} />
|
||
<span>×</span><input class="namein" type="number" min="0.01" step="0.01"
|
||
.value=${String(this._decorLargeField(e.heightCm))}
|
||
@input=${t=>this._backdropDialog={...e,heightCm:this._decorLargeCm(Number(t.target.value))}} />
|
||
<span class="opl">${t}</span></div>
|
||
<label>${this._t("decor.angle")}</label>
|
||
<input class="namein" type="number" min="-180" max="180" step="1" .value=${e.angle}
|
||
@input=${t=>this._backdropDialog={...e,angle:t.target.value}} />
|
||
</div>
|
||
<div class="row" slot="footer"><span class="spacer"></span>
|
||
<button class="btn ghost" @click=${()=>this._backdropDialog=null}>${this._t("btn.cancel")}</button>
|
||
<button class="btn primary" @click=${()=>this._saveBackdropDialog()}>${this._t("btn.save")}</button>
|
||
</div>
|
||
</hp-dialog>`}_cssPxToRender(e){const t=this._stageEl,i=this._viewOr(this._baseVb());return t?.clientWidth&&t.clientHeight?Math.max(i.w/t.clientWidth,i.h/t.clientHeight)*e:this._gridPitch/8*e}_renderZeroWalls(e){if(e&&!e.showBorders&&!this._editing)return W``;const t=this._zeroWalls();if(!t.lines.length)return W``;const i=e?.color||"var(--hp-muted)";return W`<g class="zero-walls ${t.style}"
|
||
data-zero-wall-style=${t.style} style="--zero-wall-stroke:${i}">
|
||
${t.lines.map(e=>W`<line class="zero-wall"
|
||
x1="${e[0]}" y1="${e[1]}" x2="${e[2]}" y2="${e[3]}"></line>`)}
|
||
</g>`}_zeroWalls(){const e=this._spaceModel();return e?V_(this._curSpaceCfg,e,zv,.02*this._gridPitch):{style:q_(this._curSpaceCfg),lines:[],contour:[],barriers:[],transmissive:[]}}_openCuts(){return this._zeroWalls().contour}_deleteRoomClick(e){const t=this._spaceModel();if(!t)return;const i=[...t.rooms].reverse().find(t=>this._pointInRoom(e,t));i?i.id&&(this._roomDeleteDialog={roomId:i.id,name:i.name}):this._showToast(this._t("toast.delete_room_pick"))}get _wallKeyPitch(){return jh}get _spaceWalls(){const e=this._curSpaceCfg?.walls;return Array.isArray(e)?e:[]}_cfgOpenCuts(){const e=this._curSpaceCfg,t=this._spaceModel();if(!e||!t)return[];const i=W_(e,1),s=G_(e,t.rooms,zv,.02*this._gridPitch).map(e=>e.map(e=>e/zv)),n=new Set;return[...i,...s].filter(e=>{const t=e.map(e=>Number(e).toFixed(9)).join(","),i=[e[2],e[3],e[0],e[1]].map(e=>Number(e).toFixed(9)).join(","),s=t<i?t:i;return!n.has(s)&&(n.add(s),!0)})}_intervalCm(e){const t=this._spaceModel();return t?$c(t.rooms,this._spaceWalls,this._openCuts(),e,this._wallKeyPitch,this._cellCm,this._gridPitch,zv):0}_normalizeWalls(e,t){const i=this._spaceModel();if(!i)return[];return Ll(xc(i.rooms,e,t,this._wallKeyPitch,this._cellCm,this._gridPitch,zv),this._curSpaceCfg?.rooms||[],jh,1,t.map(e=>[e[0]/zv,e[1]/zv,e[2]/zv,e[3]/zv]))}_paperShapes(e){if(!this._spaceWalls.length)return di(e);const t=this._wallUnionGeometry();return t?.paperD?[{path:t.paperD}]:di(e)}_wallUnionGeometry(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._physicalBodiesR();if(!t.length&&!i.length)return null;const s=`${this._space}|${this._cfgEpoch}|${e.rooms.length}`;if(!this._wallUnionCache||this._wallUnionCache.key!==s){const n=wv(this._wallUnionPool,s);if(n.hit)this._wallUnionCache=n.value;else{const n=this._openCuts(),o=this._roomWallOpeningInputs(),a=Wc(e.rooms,t,n,o,this._wallKeyPitch,this._cellCm,this._gridPitch,zv,i);a&&Object.defineProperty(a,"sourceFingerprint",{value:Gd([this._curSpaceCfg,this._cellCm,this._gridPitch]),enumerable:!1});const r={key:s,value:a};kv(this._wallUnionPool,s,r,8),this._wallUnionCache=r}}return this._wallUnionCache.value}_thickWallCuts(){const e=this._spaceModel();if(!e)return[];const t=this._spaceWalls;if(!t.length)return[];const i=this._openCuts();return Gc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,zv).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]])}_innerRoomContour(e,t,i=this._openCuts(),s=this._wallUnionGeometry()?.roomGeom,n=this._wallUnionGeometry()?.multiWallNodes){const o=i.map(e=>e.join(",")).join(";"),a=`${e.id}|${this._cfgEpoch}|${t}|${o}`,r=wv(this._innerContourCache,a);if(r.hit)return r.value;const l=Cc(e.rooms,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,zv,s,n);return kv(this._innerContourCache,a,l,600),l}_wallThickHit(e){const t=this._spaceModel();if(!t)return null;const i=6*this._gridPitch,s=this._openCuts();let n=null;const o=(e,t,s)=>{t>i||(!n||t<n.d-1e-9||s&&!n.independent&&t<=n.d+1e-9)&&(n={hit:e,d:t,independent:s})};for(const i of wc(t.rooms,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,zv))o({a:i.a,b:i.b,roomId:i.roomId,segs:[[i.a[0],i.a[1],i.b[0],i.b[1]]],open:i.open,cm:i.cm,source:{kind:"room"}},Hs(e,[i.a[0],i.a[1],i.b[0],i.b[1]]),!1);for(const i of t.partitions||[]){const t=[i.a[0],i.a[1]],s=[i.b[0],i.b[1]];o({a:t,b:s,roomId:"",segs:[[t[0],t[1],s[0],s[1]]],open:!1,cm:Number(i.cm)||0,source:{kind:"partition",id:i.id}},Hs(e,[t[0],t[1],s[0],s[1]]),!0)}for(const i of t.room_drafts||[])if(i.id!==this._activeDraftId)for(let t=0;t+1<i.points.length;t++){const s=[i.points[t][0],i.points[t][1]],n=[i.points[t+1][0],i.points[t+1][1]],a=Number(i.segments[t]?.cm);o({a:s,b:n,roomId:"",segs:[[s[0],s[1],n[0],n[1]]],open:!1,cm:Number.isFinite(a)?a:15,source:{kind:"draft",id:i.id,segment:t}},Hs(e,[s[0],s[1],n[0],n[1]]),!0)}return n?n.hit:null}get _wallThickHover(){if(!this._markup||"wallthick"!==this._tool||!this._cursorPt||this._wallDialog)return null;const e=this._wallThickHit(this._cursorPt);if(!e)return null;const t=function(e,t,i){const s=Number.isFinite(e)&&e>0?e:0,n=Number.isFinite(t)&&t>0?t:5,o=Number.isFinite(i)&&i>0?i:0;return s>0?Ml(s,n,o)/2:Dh(1.5*o,n)}(e.cm,this._cellCm,this._gridPitch);let i="";for(const s of e.segs)i+=(i?" ":"")+tc([[s[0],s[1]],[s[2],s[3]]],t,!1);return{segs:e.segs,open:e.open,d:i}}_wallThickClick(e){const t=this._wallThickHit(e);if(!t)return void this._showToast(this._t("toast.wallthick_pick"));const i=t.cm,s=this._viewOr(this._baseVb()),n=(t.a[0]+t.b[0])/2,o=(t.a[1]+t.b[1])/2;this._wallDialog={a:t.a,b:t.b,value:Sl(i,this._imperial),roomId:t.roomId,source:t.source,sx:(n-s.x)/s.w*100,sy:(o-s.y)/s.h*100}}_wallThickApply(e){const t=this._wallDialog;if(!t)return;const i=this._curSpaceCfg,s=this._spaceModel();if(!i||!s)return;const n=t.value.trim(),o=n?yv(n):null;if(null==o)return void this._showPhysicalRange(100);const a=this._imperial?2.54*o:o;if(!Number.isFinite(a)||a<0||a>100)return void this._showPhysicalRange(100);if("room"!==t.source.kind){const e=this._geometrySnapshot();if("partition"===t.source.kind){const e=(i.partitions||[]).find(e=>e.id===t.source.id);if(!e)return;if(0===a&&K_(i.openings,{kind:"partition",id:e.id}))return void this._showToast(this._t("toast.zero_wall_opening_conflict"));e.cm=a}else{const e=(i.room_drafts||[]).find(e=>e.id===t.source.id),s=e?.segments?.[t.source.segment];if(!s)return;s.cm=a}this._wallDialog=null;return this._commitPhysicalGeometry(this._t("history.wall_thickness"),e)&&this._showToast(this._t("toast.wallthick_set")),void this.requestUpdate()}const r=this._geometrySnapshot(),l=a>0?a:null;if(0===a){const s=.02*this._gridPitch,n=[t.a[0],t.a[1],t.b[0],t.b[1]],o=(i.rooms||[]).find(e=>e.id===t.roomId),a=new Set(e&&o&&Array.isArray(o.wall_ids)?o.wall_ids:(i.wall_segments||[]).filter(e=>{const t=[Number(e.a?.[0])*zv,Number(e.a?.[1])*zv],i=[Number(e.b?.[0])*zv,Number(e.b?.[1])*zv];return Hs(t,n)<=s&&Hs(i,n)<=s}).map(e=>String(e.id))),r=(i.openings||[]).some(e=>{if("partition"===e.host?.kind)return!1;if("wall"===e.host?.kind&&a.has(e.host.id))return!0;if(e.host)return!1;const t=[Number(e.x)*zv,Number(e.y)*zv];return t.every(Number.isFinite)&&Hs(t,n)<=s});if(r)return void this._showToast(this._t("toast.zero_wall_opening_conflict"))}let c,h=this._openCuts();if(a>0&&h.length){const n=[];if(e&&t.roomId){const e=(i.rooms||[]).find(e=>e.id===t.roomId),o=new Set(Array.isArray(e?.wall_ids)?e.wall_ids:[]);for(const e of i.wall_segments||[])o.has(e.id)&&n.push([Number(e.a?.[0])*zv,Number(e.a?.[1])*zv,Number(e.b?.[0])*zv,Number(e.b?.[1])*zv]);if(!n.length){const e=s.rooms.find(e=>e.id===t.roomId),i=e?hi(e):null;for(let e=0;i&&e<i.length;e++){const t=i[e],s=i[(e+1)%i.length];n.push([t[0],t[1],s[0],s[1]])}}}else n.push([t.a[0],t.a[1],t.b[0],t.b[1]]);const o=.02*this._gridPitch;h=h.filter(e=>!n.some(t=>Hs([e[0],e[1]],t)<=o&&Hs([e[2],e[3]],t)<=o&&Hs([t[0],t[1]],e)<=o&&Hs([t[2],t[3]],e)<=o))}c=e&&t.roomId?Gl(i.walls,s.rooms,t.roomId,l,this._wallKeyPitch,h,zv):Ul(i.walls,t.a,t.b,l,this._wallKeyPitch,zv),c=this._normalizeWalls(c,h);for(const e of i.wall_segments||[])e.cm=Hl(c,e.a,e.b,jh,1);c.length?i.walls=c:delete i.walls,this._wallDialog=null,this._commitPhysicalGeometry(this._t("history.wall_thickness"),r)&&this._showToast(this._t(0===a?"toast.wallthick_cleared":"toast.wallthick_set")),this.requestUpdate()}_wallHatchDefs(e){if(!this._spaceWalls.length&&!this._physicalBodiesR().length&&!this._markup)return W``;const t=kl(this._cellCm);return W`<defs>
|
||
<pattern id="hp-wall-hatch" patternUnits="userSpaceOnUse"
|
||
width="${t}" height="${t}" patternTransform="rotate(45)">
|
||
<path d="M0 0 L0 ${t}" stroke="${e||"#607d8b"}" stroke-width="${t/8*2}"></path>
|
||
</pattern>
|
||
</defs>`}_resolvedRoomFills(e,t){const i=new Map,s=new Map;for(const n of e.rooms){const e=this._roomDialog&&n.id===this._roomEditId?this._roomFill||t.fill:zs(t.fill,n),o=this._roomDialog&&n.id===this._roomEditId?this._roomCustomFill||t.customFill:ls(t.customFill,n),a=_s(e,"lqi"===e&&n.area?this._roomLqi(n.area):null,"light"===e?uo(ho(this._renderPlanHass,this._renderDevices,n,this._virtualLights)):"none","temp"===e?this._roomTemp(n):null,t.tempMin,t.tempMax,this._fillColors,o);i.set(n,a),n.id&&s.set(n.id,a)}return{byRoom:i,byId:s}}_spaceDisplayForRender(){const e=cs(this._curSpaceCfg),t=this._spaceDialog;return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,showBorders:t.showBorders,showNames:t.showNames,hideDecor:t.hideDecor,hideOpenings:t.hideOpenings,color:t.roomColor,opacity:t.roomOpacity,fill:t.fillMode,customFill:t.customFill?rs(t.customFill):as,glow:t.glowEnabled,tempMin:t.tempMin,tempMax:t.tempMax,showLqi:t.showLqi,cardFontScale:t.cardFontScale,labelTemp:t.labelTemp,labelHum:t.labelHum,labelLqi:t.labelLqi,labelLight:t.labelLight}:e}_openingWallIndexFor(e,t){const i=e.rooms.map(e=>`${e.id}:${e.poly?.map(e=>e.join(",")).join("/")||`${e.x},${e.y},${e.w},${e.h}`}`).join(";"),s=this._spaceWalls.map(e=>`${e.key}:${e.a?.join(",")||""}:${e.b?.join(",")||""}:${e.cm}`).join(";"),n=t.map(e=>e.join(",")).join(";"),o=[e.id,this._cfgEpoch,this._wallKeyPitch,this._cellCm,this._gridPitch,i,s,n].join("|");let a=this._openingWallIndexCache.get(o);return a||(a=Kc(e.rooms,this._spaceWalls,t,this._wallKeyPitch,this._cellCm,this._gridPitch,zv)),kv(this._openingWallIndexCache,o,a,4),{key:o,value:a}}_renderOpeningTunnelFills(e,t,i="data"){if(this._markup||!this._spaceWalls.length||!this._openingsR.length)return W``;if("glow-base"===i&&![...t.byRoom.values()].some(Boolean))return W``;const s=this._openCuts(),n=this._openingsR.map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=n.map(e=>`${e.x},${e.y},${e.angle},${e.length}`).join(";"),a=this._openingWallIndexFor(e,s),r=`${a.key}|${o}`;return this._openingTunnelCache&&this._openingTunnelCache.key===r||(this._openingTunnelCache={key:r,value:th(a.value,n)}),O_({openings:this._openingsR,geometries:this._openingTunnelCache.value,fillsByRoomId:t.byId,idPrefix:`${e.id}-${i}`,groupClass:"data"===i?"opening-tunnels":"opening-tunnels glow-base-tunnels",dataLayer:i})}_resolvedGlowBase(e,t,i){const s=new Map,n=new Map,o=this._fillColors.glow_base;for(const a of e.rooms){const e=i.byRoom.get(a),r=Fs(t.glow,a)&&(!e||e.opacity<=0)?{color:o.c,opacity:o.a,mode:"glow"}:null;s.set(a,r),a.id&&n.set(a.id,r)}return{byRoom:s,byId:n}}_renderGlowBaseRooms(e,t){if(this._markup||![...t.byRoom.values()].some(Boolean))return W``;const i=new Map(e.rooms.map(e=>[e,hi(e)])),s=this._openCuts(),n=this._wallUnionGeometry()?.roomGeom,o=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",a=e.rooms.map(a=>{const r=t.byRoom.get(a)||null,l=i.get(a)||null;if(!r||!l)return G;const c=this._spaceWalls.length&&a.id&&this._innerRoomContour(e,a.id,s,n)||l,h=Pi(c,e.rooms.filter(e=>e!==a).map(e=>i.get(e)).filter(e=>!!e)),d=this._cleanFloor(a,c,e).path;return d||h.length?W`<path class="glow-base" data-room-id=${a.id||G}
|
||
d="${[d||o(c),...h.map(o)].join(" ")}"
|
||
fill=${r.color} fill-opacity=${r.opacity} fill-rule="evenodd"
|
||
pointer-events="none"></path>`:a.poly||c!==l?W`<polygon class="glow-base" data-room-id=${a.id||G}
|
||
points="${c.map(e=>e.join(",")).join(" ")}"
|
||
fill=${r.color} fill-opacity=${r.opacity} pointer-events="none"></polygon>`:W`<rect class="glow-base" data-room-id=${a.id||G}
|
||
x=${a.x} y=${a.y} width=${a.w} height=${a.h}
|
||
rx=${.03*Math.min(a.w,a.h)}
|
||
fill=${r.color} fill-opacity=${r.opacity} pointer-events="none"></rect>`});return W`<g class="glow-base-layer" aria-hidden="true" pointer-events="none">${a}</g>`}_renderWallBodies(e){if("iso"===this._renderProjection)return W``;if(e&&!e.showBorders&&("view"===this._mode||"devices"===this._mode))return W``;const t=this._wallUnionGeometry();if(!t)return W``;const i=this._stageEl,s=this._viewOr(this._baseVb()),n=i&&i.clientWidth&&s.w?i.clientWidth/s.w:1,o=e?.color||"#607d8b",a=wl(t.depthUnits,n)||xl(kl(this._cellCm),n),r=this._fillColors.wall_fill;return W`<g class="wallbodies" style="--room-stroke:${o};--wall-fill:${r.c};--wall-fill-op:${r.a}">
|
||
${t.paths.map(e=>W`
|
||
<path class="wallbody-fill" data-component=${e.id} d="${e.d}"
|
||
fill="${r.c}" fill-opacity="${r.a}" fill-rule=${e.fillRule}
|
||
stroke="none" pointer-events="none"></path>
|
||
<path class="wallbody ${a?"solid":""}"
|
||
data-hp="wall" data-id="union" data-kind="union" data-component=${e.id}
|
||
d="${e.d}" fill="${a?"none":"url(#hp-wall-hatch)"}"
|
||
fill-rule=${e.fillRule}
|
||
stroke="${o}" stroke-width="${Dh(.6,this._cellCm)}"
|
||
pointer-events="none"></path>`)}
|
||
</g>`}_isoDecorationLayers(e){const t="undefined"==typeof CSS||"function"!=typeof CSS.supports||CSS.supports("filter","blur(1px)"),i="function"==typeof matchMedia&&matchMedia("(forced-colors: active)").matches;return function(e){const t=!!e.showBorders;return{structural:t,panels:t&&!e.hideOpenings,shadows:t&&e.filtersSupported&&!e.forcedColors,materialNuance:t&&!e.forcedColors,floorSymbols:!e.hideOpenings&&!t}}({showBorders:e.showBorders,hideOpenings:e.hideOpenings,filtersSupported:t,forcedColors:i})}_isoOpeningPanels(e){if(!e.panels)return[];const t=this._isoScene();if(!t)return[];return t.openings.flatMap(e=>{const t=this._openingsR[e.sourceIndex];return t?Wf(e,this._openingAmt(t)):[]}).sort((e,t)=>e.depth-t.depth||e.sourceIndex-t.sourceIndex||e.leaf-t.leaf)}_renderIsoDefs(e,t){const i=Ch(this._cellCm);return W`<defs>
|
||
${"walls"===t&&e.materialNuance?W`
|
||
<linearGradient id="hp-iso-wall-side" x1="0" y1="0" x2="0" y2="1">
|
||
<stop offset="0" class="iso-side-hi"></stop><stop offset="1" class="iso-side-lo"></stop>
|
||
</linearGradient>
|
||
<linearGradient id="hp-iso-wall-top" x1="0" y1="0" x2="1" y2="1">
|
||
<stop offset="0" class="iso-top-hi"></stop><stop offset="1" class="iso-top-lo"></stop>
|
||
</linearGradient>`:G}
|
||
${"underlay"===t&&e.shadows?W`
|
||
<filter id="hp-iso-ambient-shadow" x="-12%" y="-12%" width="124%" height="130%">
|
||
<feGaussianBlur stdDeviation="${7*i}"></feGaussianBlur>
|
||
</filter>`:G}
|
||
${"shadows"===t&&e.shadows?W`
|
||
<filter id="hp-iso-contact-shadow" x="-8%" y="-20%" width="116%" height="140%">
|
||
<feGaussianBlur stdDeviation="${2.5*i}"></feGaussianBlur>
|
||
</filter>
|
||
<filter id="hp-iso-leaf-shadow" x="-12%" y="-30%" width="124%" height="160%">
|
||
<feGaussianBlur stdDeviation="${2*i}"></feGaussianBlur>
|
||
</filter>`:G}
|
||
</defs>`}_renderIsoUnderlay(e){if(!e.structural)return W``;const t=this._isoScene()?.floor;return t?W`<g class="iso-underlay" data-hp="iso-underlay" aria-hidden="true" pointer-events="none">
|
||
${this._renderIsoDefs(e,"underlay")}
|
||
${e.shadows&&t.footprintPath?W`<path class="iso-ambient-shadow" d=${t.footprintPath}
|
||
transform="translate(0 ${Dh(8,this._cellCm)})"></path>`:G}
|
||
<g class="iso-floor-edge">${t.sides.map(e=>W`<path class="iso-floor-side" d=${e.d} data-component=${e.component}
|
||
data-edge=${e.edge}></path>`)}</g>
|
||
</g>`:W``}_renderIsoShadows(e,t){if(!e.shadows)return W``;const i=this._isoScene()?.geometry;return i?W`<g class="iso-shadows" data-hp="iso-shadows" aria-hidden="true" pointer-events="none">
|
||
${this._renderIsoDefs(e,"shadows")}
|
||
<path class="iso-contact-shadow" d=${i.contactPath}></path>
|
||
<g class="iso-leaf-shadows">${t.map(e=>W`<path class="iso-leaf-shadow" d=${e.shadowD}
|
||
data-id=${e.id} data-leaf=${e.leaf}></path>`)}</g>
|
||
</g>`:W``}_renderIsoWalls(e,t){if("iso"!==this._renderProjection)return W``;if(!e.structural)return W``;const i=this._isoScene();return i?W`<g class="iso-walls" data-hp="iso-walls" data-fingerprint=${i.key}>
|
||
${this._renderIsoDefs(e,"walls")}
|
||
<g class="iso-wall-sides">${i.geometry.sides.map(e=>W`<path class="iso-wall-side" d=${e.d} data-edge=${e.edge}></path>`)}</g>
|
||
<path class="iso-wall-top" d=${i.geometry.topPath} fill-rule="evenodd"></path>
|
||
${e.panels?W`<g class="iso-openings" data-hp="iso-openings"
|
||
aria-hidden="true" pointer-events="none">${t.map(e=>W`<path class="iso-opening-panel iso-${e.type}" d=${e.d}
|
||
data-id=${e.id} data-kind=${e.type} data-leaf=${e.leaf}></path>`)}</g>`:G}
|
||
</g>`:W``}_roomHoverPaths(e){const t=this._hoverRoom;if("view"!==this._mode||!t||t.space!==e.id)return null;const i=e.rooms.find(e=>e===t.room||!!e.id&&e.id===t.room.id);if(!i)return null;const s=hi(i);if(!s)return null;const n=e.rooms.filter(e=>e!==i).map(e=>({room:e,poly:hi(e)})).filter(e=>!!e.poly),o=Pi(s,n.map(e=>e.poly)),a=this._openCuts(),r=.02*this._gridPitch,l=a.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return s.some((e,i)=>Hs(t,[e[0],e[1],...s[(i+1)%s.length]])<=4*r)}),c=this._spaceWalls,h=this._wallUnionGeometry()?.roomGeom,d=c.length&&i.id&&this._innerRoomContour(e,i.id,a,h)||s,p=[{axis:s,face:d}];for(const t of o){const i=n.find(e=>e.poly===t)?.room;let s=t;if(c.length&&i?.id){const n=yc(e.rooms,i.id,c,a,this._wallKeyPitch,this._cellCm,this._gridPitch,zv);n&&(s=Vc(n.poly,n.offsets)||t)}p.push({axis:t,face:s})}const u=this._roomWallOpeningInputs(this._openingsR,e).map(e=>{const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,s=Math.sin(t)*e.length/2;return[e.x-i,e.y-s,e.x+i,e.y+s]}),_=l.concat(u),m=p.map(({axis:e,face:t})=>{const i=_.map(i=>((e,t,i)=>{const s=e[2]-e[0],n=e[3]-e[1],o=Math.hypot(s,n);if(o<r)return null;const a=s/o,l=n/o,c=(e[0]+e[2])/2,h=(e[1]+e[3])/2;let d=!1;for(let e=0;e<t.length;e++){const i=t[e],s=t[(e+1)%t.length],n=s[0]-i[0],o=s[1]-i[1],p=Math.hypot(n,o);if(!(p<r||Math.abs(a*(o/p)-l*(n/p))>.05)&&Hs([c,h],[i[0],i[1],s[0],s[1]])<=4*r){d=!0;break}}if(!d)return null;let p=null;for(let e=0;e<i.length;e++){const t=i[e],s=i[(e+1)%i.length],n=s[0]-t[0],o=s[1]-t[1],d=Math.hypot(n,o);if(d<r||Math.abs(a*(o/d)-l*(n/d))>.05)continue;const u=Hs([c,h],[t[0],t[1],s[0],s[1]]);(!p||u<p.d)&&(p={a:t,b:s,d:u})}if(!p)return null;const u=p.b[0]-p.a[0],_=p.b[1]-p.a[1],m=Math.hypot(u,_)||1,g=-_/m,f=u/m,v=(p.a[0]-c)*g+(p.a[1]-h)*f;return[e[0]+g*v,e[1]+f*v,e[2]+g*v,e[3]+f*v]})(i,e,t)).filter(e=>!!e);return i.length?Ps(t,i,r).map(e=>`M ${e[0]} ${e[1]} L ${e[2]} ${e[3]}`).join(" "):`M ${t.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`}).filter(Boolean).join(" ");if(!m)return null;const g=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`;return{fillD:[this._cleanFloor(i,d,e).path||g(d),...o.map(g)].join(" "),outlineD:m}}_renderRoomHoverFill(e){return e?W`<g class="room-hover room-hover-fill-layer" pointer-events="none">
|
||
<path class="room-hover-fill" d="${e.fillD}" fill-rule="evenodd"></path>
|
||
</g>`:W``}_renderRoomHoverOutline(e){return e?W`<g class="room-hover room-hover-outline-layer" pointer-events="none">
|
||
<path class="room-hover-halo" d="${e.outlineD}"></path>
|
||
<path class="room-hover-outline" d="${e.outlineD}"></path>
|
||
</g>`:W``}_renderWallThickUi(){const e=this._wallThickHover;return e&&e.d?W`<path class="wallthick-hover ${e.open?"isopen":""}"
|
||
d="${e.d}"></path>`:W``}_renderWallThickDialog(){const e=this._wallDialog;return e?q`<div class="wallthick-dlg" style="left:${e.sx.toFixed(2)}%;top:${e.sy.toFixed(2)}%"
|
||
@click=${e=>e.stopPropagation()}>
|
||
<div class="row">
|
||
<label>${this._t("wallthick.field")}</label>
|
||
<input type="number" min="0" max="100" step="any" .value=${e.value}
|
||
@input=${t=>{this._wallDialog={...e,value:t.target.value}}}
|
||
@keydown=${e=>{"Enter"===e.key&&(e.preventDefault(),this._wallThickApply(!1))}} />
|
||
<span class="opl">${this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}</span>
|
||
</div>
|
||
<div class="row">
|
||
${"room"===e.source.kind?q`<button class="btn ghost"
|
||
@click=${()=>this._wallThickApply(!0)}>
|
||
${this._t("wallthick.apply_room")}
|
||
</button>`:G}
|
||
<span class="spacer"></span>
|
||
<button class="btn on" @click=${()=>this._wallThickApply(!1)}>
|
||
<ha-icon icon="mdi:check"></ha-icon>
|
||
</button>
|
||
</div>
|
||
</div>`:q``}_openingAt(e){if(!this._openingsR.length)return null;const t=this._openingsR.flatMap(t=>{const i=t.angle*Math.PI/180,s=e[0]-t.rx,n=e[1]-t.ry,o=s*Math.cos(i)+n*Math.sin(i);return Math.abs(o)>t.rlen/2+Dh(12,this._cellCm)?[]:[{o:t,localY:-s*Math.sin(i)+n*Math.cos(i)}]});if(!t.length)return null;const i=this._spaceModel();if(!i)return null;const s=this._openCuts(),n=this._openingWallIndexFor(i,s).value;return t.find(({o:e,localY:t})=>{const i="gate"===e.type?!e.flip_v:e.flip_v,s=e.partitionHost||this._spaceWalls.length||"gate"===e.type?this._openingFace(e,n,!!i):{ox:0,oy:0,cm:0,side:-1},o=g_({type:e.type,length:e.rlen,angle:e.angle,amount:this._openingAmt(e),flipH:!!e.flip_h,flipV:!!e.flip_v,cellCm:this._cellCm,gridPitch:this._gridPitch,face:s});return Math.abs(t)<=o.hitHalf})?.o||null}_resolveOpeningPlacement(e){const t=this._openingPreset;if("opening"!==this._tool||!t)return null;const i=this._spaceModel();if(!i)return null;const s=this._openCuts(),n=this._openingWallIndexFor(i,s),o=`${n.key}|partitions:${this._cfgEpoch}`;this._openingPlacementIntervalsCache&&this._openingPlacementIntervalsCache.key===o||(this._openingPlacementIntervalsCache={key:o,value:[...wc(i.rooms,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,zv),...au(i.partitions,this._cellCm,this._gridPitch)]}),this._openingDimensionContextCache&&this._openingDimensionContextCache.key===o||(this._openingDimensionContextCache={key:o,value:ov({rooms:i.rooms,walls:this._spaceWalls,openCuts:s,partitions:i.partitions,roomOpenings:this._roomWallOpeningInputs(),partitionCuts:this._partitionOpeningCuts(i),pitch:this._wallKeyPitch,cellCm:this._cellCm,gridPitch:this._gridPitch,coordScale:zv,epsilon:2e-4*this._gridPitch})});const a=Jf({pointer:[e[0],e[1]],preset:t,geometryRevision:this._cfgEpoch,renderedLength:this._cmToUnits(t.lengthCm),intervals:this._openingPlacementIntervalsCache.value,baseTolerance:1.5*this._gridPitch,bodyPointerPadding:this._cssPxToRender("touch"===this._pointerModality.modality||"pen"===this._pointerModality.modality?10:6),gridStep:this._gridPitch});this._openingJambBlockCm=a.jambBlockedTarget?a.jambBlockedTarget.physicalHalfWidth/this._gridPitch*this._cellCm:null;const r=a.candidate;if(!r)return null;const l="gate"===r.type?!r.flipV:r.flipV;let c;if(r.host){const e=tu({id:"preview",type:r.type,x:r.x/zv,y:r.y/zv,angle:r.angle,length:r.renderedLength/zv,host:r.host},i.partitions,zv,this._cellCm,this._gridPitch).resolved;c=e?ou(e,l):{ox:0,oy:0,cm:0,side:-1}}else c=Zc(n.value,{x:r.x,y:r.y,angle:r.angle,length:r.renderedLength,flip_v:l});const h="mi"===this.hass?.config?.unit_system?.length,d=pv(r,this._openingDimensionContextCache.value).map(e=>({x:e.label[0],y:e.label[1],text:li(e.distance/this._gridPitch*this._cellCm,h),dimension:e}));return{...r,face:c,measure:{labels:d,guide:r.measure.guide}}}_activateOpeningPlacement(e){this._activateMarkupTool("opening"),"opening"===this._tool&&(this._openingPreset=function(e,t){return{type:e,lengthCm:Gf(e),flipH:!1,flipV:!1,revision:t}}(e,++this._openingPresetRevision),this._openingHoverCandidate=null,this._cursorPt=null)}_clearOpeningPlacement(e){this._openingHoverCandidate=null,this._openingJambBlockCm=null,e&&(this._openingPreset=null,this._openingRebindId=null)}_openingClick(e){const t=this._spaceModel();if(!t)return;const i=this._openingRebindId?null:this._openingAt(e);if(i)return void this._editOpening(i);const s=this._openingPreset;if(!s)return;const n=this._openingHoverCandidate,o=n&&Xf(n,[e[0],e[1]],s.revision,this._cfgEpoch)?n:this._resolveOpeningPlacement(e);if(!o){if(null!=this._openingJambBlockCm)return void this._showToast(this._t("opening.partition_jamb_margin",{distance:li(this._openingJambBlockCm,this._imperial)}));const i=1.5*this._gridPitch,s=ui(e,t.rooms,i);return s&&function(e,t,i,s,n){for(const o of s)if(!(Hs([e,t],o)>n)&&Bl([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(s.x,s.y,s.angle,this._openCuts(),i)?void this._showToast(this._t("toast.opening_on_zero_wall")):void this._showToast(this._t("toast.opening_no_wall"))}const a=this._openingRebindId?this._curSpaceCfg?.openings?.find(e=>e.id===this._openingRebindId):null;this._openingDialog={...a?{id:a.id,type:a.type,lengthCm:Math.round(a.length*zv/this._gridPitch*this._cellCm),lengthTouched:!1,contact:a.contact||"",lock:a.lock||"",invert:!!a.invert,flipH:!!a.flip_h,flipV:!!a.flip_v}:{type:o.type,lengthCm:o.lengthCm,contact:"",lock:"",invert:!1,flipH:o.flipH,flipV:o.flipV},...o.host?{host:o.host}:{},x:o.x,y:o.y,angle:o.angle},this._openingRebindId=null,this._openingHoverCandidate=null,this._cursorPt=null}_editOpening(e){this._openingDialog={id:e.id,type:e.type,lengthCm:Math.round(e.rlen/this._gridPitch*this._cellCm),lengthTouched:!1,contact:e.contact||"",lock:e.lock||"",invert:!!e.invert,flipH:!!e.flip_h,flipV:!!e.flip_v,..."partition"===e.host?.kind?{host:{...e.host}}:{},x:e.rx,y:e.ry,angle:e.angle}}_opPointerDown(e,t){if("plan"===this._mode&&this._spaceModel()&&"resize"!==this._tool){e.preventDefault(),e.stopPropagation();try{jv(e)}catch{}this._opDrag={id:t.id,moved:!1,sx:e.clientX,sy:e.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(e,t){if(!this._opDrag||this._opDrag.id!==t.id)return;if(Math.abs(e.clientX-this._opDrag.sx)+Math.abs(e.clientY-this._opDrag.sy)<=3)return;const i=this._spaceModel();if(!i)return;const s=this._svgPoint(e),n=this._curSpaceCfg,o=n?.openings?.find(e=>e.id===t.id);if(!o)return;if("partition"===o.host?.kind){const e=i.partitions.find(e=>e.id===o.host.id);if(!e)return;const t=e.b[0]-e.a[0],n=e.b[1]-e.a[1],a=Math.hypot(t,n);if(!(a>1e-9))return;const r=t/a,l=n/a;if(Math.abs((s[0]-e.a[0])*l-(s[1]-e.a[1])*r)>4*this._gridPitch)return;const c=o.length*zv/2+Qp(e,this._cellCm,this._gridPitch);let h=(s[0]-e.a[0])*r+(s[1]-e.a[1])*l;if(h=Math.round(h/this._gridPitch)*this._gridPitch,h=Math.max(c,Math.min(a-c,h)),a<2*c-1e-9)return;const d=h/a,p=(e.a[0]+r*h)/zv,u=(e.a[1]+l*h)/this._spaceH;let _=180*Math.atan2(n,t)/Math.PI;_>=90?_-=180:_<-90&&(_+=180);const m=o.host.t!==d||o.x!==p||o.y!==u||o.angle!==_;return this._opDrag.moved=!0,m&&(this._opDrag.dirty=!0),o.host={...o.host,t:d},o.x=p,o.y=u,o.angle=_,this._opMeasure=null,m&&this._cfgEpoch++,void this.requestUpdate()}const a=ui(s,i.rooms,4*this._gridPitch);if(!a)return;this._opDrag.moved=!0;const r=this._opRuler(a,o.length*zv);this._opMeasure=r.measure;const l=r.x/zv,c=r.y/this._spaceH,h=o.x!==l||o.y!==c||o.angle!==a.angle;if(h&&(this._opDrag.dirty=!0),o.x=l,o.y=c,o.angle=a.angle,Number(this._serverCfg?.model_version||0)>=9){const e=Mg(o,n?.wall_segments||[]);e?o.host=e:delete o.host}h&&this._cfgEpoch++,this.requestUpdate()}_opRuler(e,t){const i=this._spaceModel()?.rooms||[],s=this._gridPitch/2;let n=e.x,o=e.y,a=_i([n,o],e.angle,t,i,s);if(a&&a.centered&&(n!==a.wallCenter[0]||o!==a.wallCenter[1]))[n,o]=a.wallCenter,a=_i([n,o],e.angle,t,i,s);else if(a){const[r,l]=a.wallA,[c,h]=a.wallB,d=c-r,p=h-l,u=Math.hypot(d,p);if(u>0){const c=this._gridPitch,h=Math.min(t/2,u/2);let _=Math.round(((n-r)*d+(o-l)*p)/u/c)*c;_=Math.max(h,Math.min(u-h,_)),n=r+_/u*d,o=l+_/u*p,a=_i([n,o],e.angle,t,i,s)||a}}if(!a)return{x:n,y:o,angle:e.angle,measure:null};const r="mi"===this.hass?.config?.unit_system?.length,l=(e,t)=>({x:t[0],y:t[1],text:li(e/this._gridPitch*this._cellCm,r)});return{x:n,y:o,angle:e.angle,measure:{labels:[l(a.sideA,a.midA),l(a.sideB,a.midB)],guide:a.centered?{x:a.wallCenter[0],y:a.wallCenter[1],angle:e.angle}:null}}}_opPointerUp(e,t){if(!this._opDrag||this._opDrag.id!==t.id)return;const i=this._opDrag,s=i.moved;this._opMeasure=null,s&&i.dirty&&this._commitPhysicalGeometry(this._t("history.move_opening"),i.before),s?window.setTimeout(()=>this._opDrag=null,0):this._opDrag=null}_opClick(e,t){"plan"===this._mode&&"resize"===this._tool||(e.stopPropagation(),this._opDrag?.moved||"plan"===this._mode&&this._editOpening(t))}_saveOpening(){const e=this._openingDialog,t=this._curSpaceCfg,i=this._spaceModel();if(!e||!t||!i)return;const s=this._geometrySnapshot(),n=this._spaceH,o=(t.openings||[]).find(t=>t.id===e.id),a={...o||{},id:e.id||"o"+Date.now().toString(36),type:e.type,x:e.x/zv,y:e.y/n,angle:e.angle,length:o&&!e.lengthTouched?o.length:this._cmToUnits(Math.max(20,e.lengthCm))/zv,...e.host?{host:{...e.host}}:{}};if("partition"===a.host?.kind){const e=su(o,a)?iu(a,i.partitions,zv,this._cellCm,this._gridPitch):tu(a,i.partitions,zv,this._cellCm,this._gridPitch);if(!e.resolved){const t=i.partitions.find(e=>e.id===a.host.id);if("does-not-fit-jamb"===e.reason&&t){const e=Qp(t,this._cellCm,this._gridPitch);this._showToast(this._t("opening.partition_jamb_margin",{distance:li(e/this._gridPitch*this._cellCm,this._imperial)}))}else this._showToast(this._t("opening.partition_orphan"));return}const s=(t.openings||[]).flatMap(e=>{if(!e.host||e.id===a.id)return[];const t=tu(e,i.partitions,zv,this._cellCm,this._gridPitch).resolved;return t?[t]:[]});if(function(e,t,i=1e-9){const s=e.length/(2*e.axis.length),n=e.t-s,o=e.t+s;return t.some(t=>{if(t.host.id!==e.host.id||t.opening.id===e.opening.id)return!1;const s=t.length/(2*t.axis.length);return Math.max(n,t.t-s)<Math.min(o,t.t+s)-i})}(e.resolved,s))return void this._showToast(this._t("toast.opening_no_wall"));Object.assign(a,lu(a,e.resolved,zv))}else if(Number(this._serverCfg?.model_version||0)>=9){const e=Mg(a,t.wall_segments||[]);if(!e)return void this._showToast(this._t("toast.opening_no_wall"));a.host=e}else delete a.host;"passage"===e.type?(delete a.contact,delete a.lock,delete a.invert,delete a.flip_h,delete a.flip_v):(a.contact=e.contact||null,a.lock=("door"===e.type||"gate"===e.type)&&e.lock||null,a.invert=e.invert||void 0,a.flip_h="gate"!==e.type&&e.flipH||void 0,a.flip_v=e.flipV||void 0),t.openings=t.openings||[];const r=t.openings.findIndex(e=>e.id===a.id);r>=0?t.openings[r]=a:t.openings.push(a),this._commitPhysicalGeometry(this._t(e.id?"history.edit_opening":"history.add_opening"),s),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const e=this._openingDialog,t=this._curSpaceCfg;if(!e?.id||!t?.openings)return;const i=this._geometrySnapshot();t.openings=t.openings.filter(t=>t.id!==e.id),this._commitPhysicalGeometry(this._t("history.delete_opening"),i),this._openingDialog=null,this.requestUpdate()}_contactCandidates(){const e=[];for(const t of Object.keys(this.hass.states)){if(!this._openingEntityAvailable(t))continue;const i=t.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[t],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&e.push([t,s?.attributes?.friendly_name||t,n?0:1])}return e.sort((e,t)=>e[2]-t[2]||e[1].localeCompare(t[1])).map(([e,t])=>({value:e,label:t}))}_lockCandidates(){return Object.keys(this.hass.states).filter(e=>e.startsWith("lock.")&&this._openingEntityAvailable(e)).map(e=>({value:e,label:this.hass.states[e]?.attributes?.friendly_name||e})).sort((e,t)=>e.label.localeCompare(t.label))}_toggleOpeningEntityPicker(e){const t=this._openingDialog;if(!t)return;const i="contact"===e?!t.contactOpen:!t.lockOpen;this._openingDialog={...t,contactOpen:"contact"===e&&i,lockOpen:"lock"===e&&i}}_filterOpeningEntities(e,t){const i=this._openingDialog;i&&(this._openingDialog="contact"===e?{...i,contactFilter:t}:{...i,lockFilter:t})}_selectOpeningEntity(e,t){const i=this._openingDialog;i&&(this._openingDialog="contact"===e?{...i,contact:t,contactOpen:!1,contactFilter:""}:{...i,lock:t,lockOpen:!1,lockFilter:""})}_mergeClick(e){const t=this._spaceModel();if(!t)return;const i=t.rooms,s=[...i].reverse().find(t=>this._pointInRoom(e,t));if(!s?.id)return;const n=s.id;if(!this._mergeSel||this._mergeSel===n)return void(this._mergeSel=this._mergeSel===n?null:n);const o=i.find(e=>e.id===this._mergeSel),a=o?hi(o):null,r=hi(s),l=a&&r?function(e,t){if(!e||!t||e.length<3||t.length<3)return null;const i=si(Ii(e),Ii(t));if(1!==i.length)return null;if(1!==i[0].length)return null;const s=i[0][0].slice(0,-1).map(e=>[e[0],e[1]]);return s.length>=3?s:null}(a,r):null;if(!l)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:n,poly:l,pick:"a"},this._mergeSel=null}_commitMerge(){const e=this._mergeDialog,t=this._curSpaceCfg;if(!e||!t||!this._spaceModel())return;const i=this._geometrySnapshot(),s=this._spaceH,n="a"===e.pick?e.aId:e.bId,o="a"===e.pick?e.bId:e.aId,a=t.rooms.find(e=>e.id===n);if(!a)return void(this._mergeDialog=null);a.poly=e.poly.map(e=>[e[0]/zv,e[1]/s]),delete a.x,delete a.y,delete a.w,delete a.h,t.rooms=t.rooms.filter(e=>e.id!==o);const r=this._commitPhysicalGeometry(this._t("history.merge_rooms"),i);this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),r&&this._showToast(this._t("toast.rooms_merged",{name:a.name||""}))}_splitClick(e){const t=this._spaceModel();if(!t)return;const i=t.rooms;if(!this._splitSel){const t=[...i].reverse().find(t=>this._pointInRoom(e,t));if(!t?.id)return;return void(this._splitSel={roomId:t.id,pts:[]})}const s=i.find(e=>e.id===this._splitSel.roomId),n=s?hi(s):null;if(!s||!n)return void(this._splitSel=null);const o=.02*this._gridPitch,a=6*this._gridPitch,r=function(e,t){if(!t||t.length<2)return null;let i=null,s=1/0;for(let n=0;n<t.length;n++){const o=t[n],a=t[(n+1)%t.length],r=a[0]-o[0],l=a[1]-o[1],c=r*r+l*l;let h=c?((e[0]-o[0])*r+(e[1]-o[1])*l)/c:0;h=Math.max(0,Math.min(1,h));const d=[o[0]+h*r,o[1]+h*l],p=Math.hypot(e[0]-d[0],e[1]-d[1]);p<s&&(s=p,i=d)}return i}(e,n),l=r&&function(e,t,i){let s=null,n=1/0;for(let o=0;o<t.length;o++){const[a,r]=t[o],[l,c]=t[(o+1)%t.length],h=l-a,d=c-r,p=h*h+d*d;if(!p)continue;let u=((e[0]-a)*h+(e[1]-r)*d)/p;u=Math.max(0,Math.min(1,u));const _=Math.hypot(e[0]-(a+u*h),e[1]-(r+u*d));if(_>=n)continue;n=_;const m=Math.sqrt(p),g=(i>0?Math.max(0,Math.min(m,Math.round(u*m/i)*i)):u*m)/m;s=[a+g*h,r+g*d]}return s}(r,n,this._gridPitch)||r,c=r&&l&&Math.hypot(r[0]-e[0],r[1]-e[1])<=a?l:null,h=!!c&&yi(c,n,o),d=this._splitSel.pts;if(!d.length)return h?void(this._splitSel={...this._splitSel,pts:[c]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const t=this._snap(e);return wi(t,n,o)?void(this._splitSel={...this._splitSel,pts:[...d,t]}):void this._showToast(this._t("toast.split_pick_inside"))}const p=zi(n,[...d,c],o);if(!p)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,_]=p,m=Ti(u)>=Ti(_)?u:_,g=m===u?_:u;this._pendingSplit={roomId:s.id,mainPoly:m,newPoly:g},this._cursorPt=null,this._nameSel="",this._areaSel="",this._roomDialog=!0}get _contourClosed(){return this._path.length>=4&&this._samePt(this._path[0],this._path[this._path.length-1])&&(null!=this._closingWallCm||!this._activeDraftId)}_markupMove(e){if(!this._markup)return;if("column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(e)));if("opening"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(e));const t="draw"===this._tool&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;if(!t&&!i)return;const s=this._svgPoint(e);if(t){const t=this._resolvePlanDrawPoint(s,e.shiftKey);return this._planSnapHover={contextKey:t.contextKey,candidate:t.candidate,conflicts:t.conflicts},this._syncPlanSnapConflictMarkers(t.conflicts),this._path.length?void(this._cursorPt=t.point):void this._syncPlanSnapActiveMarker(t.candidate)}this._cursorPt=this._snap(s)}get _openingPreview(){const e=this._openingPreset;if("opening"!==this._tool||!e||!this._cursorPt)return null;const t=this._cursorPt,i=this._openingHoverCandidate;if(i&&Xf(i,[t[0],t[1]],e.revision,this._cfgEpoch))return i;if(this._openingAt(t))return this._openingHoverCandidate=null,null;const s=this._resolveOpeningPlacement(t);return this._openingHoverCandidate=s,s}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){(this._areaSel||this._nameSel.trim())&&(this._wallFaceBatch?this._decideWallFace(!0):this._commitRoom())}_decideWallFace(e){const t=this._wallFaceBatch;if(!t)return;const i=t.candidates[t.index];if(!i)return;if(!e&&(i.existing||i.repair)&&1===t.candidates.length)return this._wallFaceBatch=null,this._roomDialog=!1,this._nameSel="",this._areaSel="",void this.requestUpdate();const s=e?{candidate:i,create:!0,name:this._nameSel.trim(),area:this._areaSel||null,settings:this._roomSettingsFromDialog()?JSON.parse(JSON.stringify(this._roomSettingsFromDialog())):null}:{candidate:i,create:!1},n=[...t.decisions,s],o=t.index+1;if(o<t.candidates.length)return this._wallFaceBatch={...t,decisions:n,index:o},this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),void this.requestUpdate();this._wallFaceBatch={...t,decisions:n},this._applyWallFaceBatch()}_wallSourceCmAt(e,t,i){const s=e=>"number"==typeof e&&Number.isFinite(e)&&e>=0?e:null,n=.02*this._gridPitch,o=this._spaceModel();if(!o)return vl;for(const t of this._planSnapGeometrySnapshot().value.segments){if("room"===t.sourceKind||Hs(e,[t.a[0],t.a[1],t.b[0],t.b[1]])>n)continue;if("partition"===t.sourceKind)return s(o.partitions.find(e=>e.id===t.sourceId)?.cm)??vl;const i=t.sourceId.lastIndexOf(":"),a=i>=0?t.sourceId.slice(0,i):t.sourceId,r=i>=0?Number(t.sourceId.slice(i+1)):-1;return s(o.room_drafts.find(e=>e.id===a)?.segments[r]?.cm)??vl}const a=Cu(Math.max(0,t.length-1),i,this._drawWallCm,vl);for(let i=0;i+1<t.length;i++){const s=t[i],o=t[i+1];if(Hs(e,[s[0],s[1],o[0],o[1]])<=n)return a[i]??vl}return vl}_activePathWithRepair(e,t){const i=e.map(e=>[...e]);if(!t?.sourceKey.startsWith("active:"))return i;const s=/:(\d+)$/.exec(t.sourceKey),n=s?Number(s[1])+("b"===t.endpoint?1:0):-1;return n>=0&&n<i.length&&(i[n]=[...t.to]),i}_validateWallRepair(e,t){const i=2e-4*this._gridPitch,s=this._wallGraphSources(t),n=s.find(t=>t.key===e.sourceKey),o=s.find(t=>t.key===e.targetSourceKey);if(!n||!o)return!1;const a="a"===e.endpoint?n.a:n.b;return!(Math.hypot(a[0]-e.from[0],a[1]-e.from[1])>i)&&(!(Hs(e.to,[o.a[0],o.a[1],o.b[0],o.b[1]])>i)&&!function(e,t){if(!e.sourceKey.startsWith("static:partition|"))return!1;const i=e.sourceKey.slice(17).split("|")[0];return!!i&&t.some(e=>"partition"===e.host?.kind&&e.host.id===i)}(e,this._curSpaceCfg?.openings||[]))}_applyWallRepair(e,t){const i=this._curSpaceCfg;if(!i||!this._validateWallRepair(e,t.activePath))return!1;const s=t=>{t[0]=e.to[0]/zv,t[1]=e.to[1]/zv};if(e.sourceKey.startsWith("active:")){const n=/:(\d+)$/.exec(e.sourceKey),o=n?Number(n[1])+("b"===e.endpoint?1:0):-1;if(o<0||o>=t.activePath.length)return!1;t.activePath[o]=[...e.to];const a=(i.room_drafts||[]).find(e=>e.id===t.activeDraftId);return a?.points?.[o]&&s(a.points[o]),!0}if(!e.sourceKey.startsWith("static:"))return!1;const[n,o]=e.sourceKey.slice(7).split("|");if("partition"===n){const t=(i.partitions||[]).find(e=>e.id===o);if(!t)return!1;const n=[t.a[0]*zv,t.a[1]*zv],a=[t.b[0]*zv,t.b[1]*zv],r=Math.hypot(n[0]-e.from[0],n[1]-e.from[1])<=2e-4*this._gridPitch?t.a:Math.hypot(a[0]-e.from[0],a[1]-e.from[1])<=2e-4*this._gridPitch?t.b:null;return!!r&&(s(r),!0)}if("draft"===n){const t=o.lastIndexOf(":"),n=t>=0?o.slice(0,t):"",a=t>=0?Number(o.slice(t+1)):-1,r=(i.room_drafts||[]).find(e=>e.id===n);if(!r?.points?.[a]||!r?.points?.[a+1])return!1;const l=[a,a+1].find(t=>Math.hypot(r.points[t][0]*zv-e.from[0],r.points[t][1]*zv-e.from[1])<=2e-4*this._gridPitch);return null!=l&&(s(r.points[l]),!0)}return!1}_applyWallFaceBatch(){const e=this._wallFaceBatch,t=this._curSpaceCfg,i=this._spaceModel();if(!e||!t||!i)return;const s=(e,t)=>{this._showToast(this._t(e,t)),this._roomDialogCancel()},n=e.decisions.filter(e=>e.create),o=n.map(e=>e.candidate.ring),a=i.rooms;for(let e=0;e<o.length;e++){if(this._contourSelfIntersects(o[e])||Ti(o[e])<=1e-6)return void s("toast.contour_cannot_close");for(let t=e+1;t<o.length;t++)if(Di(o[e],o[t]))return void s("toast.contour_cannot_close");const t=n[e].candidate.split?.roomId,i=a.find(i=>i.id!==t&&!!hi(i)&&Di(o[e],hi(i)));if(i)return void s("toast.room_overlap",{name:i.name||""})}if((t.rooms||[]).length+n.length>400)return void s("toast.physical_limit");if(n.some(e=>e.candidate.split&&!t.rooms.some(t=>t.id===e.candidate.split.roomId)))return void s("toast.contour_cannot_close");const r=n.map(e=>e.candidate.repair).filter(e=>!!e);if(r.length>1||r[0]&&!this._validateWallRepair(r[0],e.activePath))return void s("toast.wall_repair_changed");const l=this._activePathWithRepair(e.activePath,r[0]),c=2e-4*this._gridPitch,h=new Map;for(let t=0;t<e.activeCms.length;t++)h.set(this._activeWallSourceKey(t),e.activeCms[t]);const d=[];if(n.length){const e=new Set(n.flatMap(e=>e.candidate.atomKeys)),t=n.some(e=>e.candidate.consumeAllActive);for(const i of Yu(this._wallGraphSources(l),c)){const s=i.sourceKeys.find(e=>h.has(e));!s||t||e.has(i.key)||d.push({a:i.a,b:i.b,cm:h.get(s)})}}else d.push(...Du(l,Cu(l.length-1,e.activeCms,this._drawWallCm,vl)));if((t.partitions||[]).length+d.length>Ov)return void s("toast.physical_limit");const p=n.map(e=>e.candidate.ring.map(()=>"")),u=e.activeDraftId?(t.room_drafts||[]).find(t=>t.id===e.activeDraftId):null,_=this._draftSegmentsForPath(e.activePath,u,e.activeCms),m=(e,t)=>{const i=`${e[0].toFixed(9)},${e[1].toFixed(9)}`,s=`${t[0].toFixed(9)},${t[1].toFixed(9)}`;return i<=s?`${i}|${s}`:`${s}|${i}`};for(let t=0;t<_.length;t++){const i=_[t]?.id;if(!i)continue;const s=e.activePath[t],o=e.activePath[t+1],a=[s[0],s[1],o[0],o[1]],r=[];n.forEach((e,t)=>e.candidate.ring.forEach((i,s)=>{const n=e.candidate.ring[(s+1)%e.candidate.ring.length];Hs(i,a)<=c&&Hs(n,a)<=c&&r.push({kind:"room",owner:t,edge:s,a:i,b:n})})),d.forEach((e,t)=>{Hs(e.a,a)<=c&&Hs(e.b,a)<=c&&r.push({kind:"partition",owner:t,edge:0,a:e.a,b:e.b})});const l=[(s[0]+o[0])/2,(s[1]+o[1])/2];r.sort((e,t)=>{const i=[e.a[0],e.a[1],e.b[0],e.b[1]],n=[t.a[0],t.a[1],t.b[0],t.b[1]];return(Hs(l,i)<=c?0:1)-(Hs(l,n)<=c?0:1)||(Hs(s,i)<=c?0:1)-(Hs(s,n)<=c?0:1)||m(e.a,e.b).localeCompare(m(t.a,t.b))});const h=r[0];if(!h)continue;const u=m(h.a,h.b);for(const e of r)m(e.a,e.b)===u&&("room"===e.kind?p[e.owner][e.edge]=i:d[e.owner].id=i)}const g=this._geometrySnapshot();if(r[0]&&!this._applyWallRepair(r[0],e))return void s("toast.wall_repair_changed");const f=n.some(e=>!!e.candidate.split),v=f?kc(i.rooms,t.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,zv):null,b=f&&Array.isArray(t.walls)?t.walls:null;for(const e of n){const i=e.candidate.split;if(!i)continue;const n=t.rooms.find(e=>e.id===i.roomId);if(!n)return void s("toast.contour_cannot_close");n.poly=i.mainPoly.map(e=>[e[0]/zv,e[1]/this._spaceH]),delete n.x,delete n.y,delete n.w,delete n.h}const y=Date.now().toString(36),w=[];if(n.forEach((e,i)=>{const s=e.area?this.hass.areas[e.area]?.name:"",n={id:`r${y}-${i}`,name:e.name||s||this._t("room.default_name"),area:e.area||null,poly:e.candidate.ring.map(e=>[e[0]/zv,e[1]/this._spaceH]),...p[i].some(Boolean)?{wall_ids:p[i]}:{},...e.settings?{settings:JSON.parse(JSON.stringify(e.settings))}:{}};t.rooms.push(n),w.push({room:n,decision:e})}),e.activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==e.activeDraftId),t.room_drafts.length||delete t.room_drafts),d.length&&(t.partitions||=[],d.forEach((e,i)=>t.partitions.push({id:e.id||`partition-${y}-${i}`,a:[e.a[0]/zv,e.a[1]/zv],b:[e.b[0]/zv,e.b[1]/zv],cm:e.cm}))),f){const e=this._normalizeWalls(v,this._openCuts());e.length?t.walls=e:b?.length?t.walls=b:delete t.walls}if(w.length){this._cfgEpoch++;const i=this._openCuts(),s=this._spaceModel();if(!s)return;let n=t.walls;for(const{room:t,decision:o}of w){const a=o.candidate.ring,r=this._wallSourceCmAt(a[0],e.activePath,e.activeCms);n=Vl(n,s.rooms,t.id,r,this._wallKeyPitch,i,zv);for(const o of wc(s.rooms,n,i,this._wallKeyPitch,this._cellCm,this._gridPitch,zv)){if(o.roomId!==t.id||"outer"!==o.kind)continue;const i=[(o.a[0]+o.b[0])/2,(o.a[1]+o.b[1])/2];n=Ul(n,o.a,o.b,this._wallSourceCmAt(i,e.activePath,e.activeCms),this._wallKeyPitch,zv)}}const o=this._normalizeWalls(n,i);o.length?t.walls=o:delete t.walls}if(!this._commitPhysicalGeometry(this._t("history.wall_face_batch"),g))return;delete this._resumeDraftBySpace[this._space],this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._wallFaceBatch=null,this._roomDialog=!1,this._nameSel="",this._areaSel="",this._regSignature="",this._maybeRebuildDevices();const k=new Set(n.map(e=>e.area).filter(e=>!!e));if(k.size){const e={...this._layout};for(const t of this._devices){if(!k.has(t.area||"")||t.space!==this._space)continue;if(this._layout[t.id])continue;const i=this._defPos[t.id];i&&(e[t.id]={s:this._space,x:i.x/zv,y:i.y/zv},this._dirtyPos.add(t.id))}this._layout=e,this._persistLayout()}this._showToast(this._t(n.length?"toast.wall_rooms_saved":"toast.wall_chain_saved",{n:n.length}))}_commitRoom(){const e=this._curSpaceCfg,t=this._spaceModel();if(!e||!t)return;const i=this._geometrySnapshot(),s=this._spaceH,n=!!this._pendingSplit,o=n?kc(t.rooms,e.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,zv):null,a=n&&Array.isArray(e.walls)?e.walls:null;let r;if(this._pendingSplit){const t=e.rooms.find(e=>e.id===this._pendingSplit.roomId);if(!t)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);t.poly=this._pendingSplit.mainPoly.map(e=>[e[0]/zv,e[1]/s]),delete t.x,delete t.y,delete t.w,delete t.h,r=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;r=this._path.slice(0,-1)}const l=this._areaSel?this.hass.areas[this._areaSel]?.name:"",c={id:"r"+Date.now().toString(36),name:this._nameSel||l||this._t("room.default_name"),area:this._areaSel||null,poly:r.map(e=>[e[0]/zv,e[1]/s]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(!n&&this._activeDraftId){const t=Array.isArray(e.room_drafts)?e.room_drafts.find(e=>e.id===this._activeDraftId):null,i=Cu(r.length,[...this._draftSegmentCms,this._closingWallCm??void 0],this._drawWallCm,vl),s=this._draftSegmentsForPath(this._path,t,i);s.length===r.length&&s.every(e=>!!e.id)&&(c.wall_ids=s.map(e=>e.id))}if(e.rooms.push(c),!n&&this._activeDraftId&&Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(e=>e.id!==this._activeDraftId),e.room_drafts.length||delete e.room_drafts),n){const t=this._normalizeWalls(o,this._openCuts());t.length?e.walls=t:a?.length?e.walls=a:delete e.walls}if(!n){const t=Cu(r.length,[...this._draftSegmentCms,this._closingWallCm??void 0],this._drawWallCm,vl),i=t[0];if(null!=i){this._cfgEpoch++;const s=this._openCuts(),n=this._spaceModel();if(!n)return;let o=Vl(e.walls,n.rooms,c.id,i,this._wallKeyPitch,s,zv);for(const e of wc(n.rooms,o,s,this._wallKeyPitch,this._cellCm,this._gridPitch,zv)){if(e.roomId!==c.id||"outer"!==e.kind)continue;const i=[(e.a[0]+e.b[0])/2,(e.a[1]+e.b[1])/2],s=r.findIndex((e,t)=>{const s=r[(t+1)%r.length];return Hs(i,[e[0],e[1],s[0],s[1]])<=.02*this._gridPitch});s>=0&&(o=Ul(o,e.a,e.b,t[s],this._wallKeyPitch,zv))}o=this._normalizeWalls(o,s),o.length?e.walls=o:delete e.walls}}const h=this._commitPhysicalGeometry(this._t(n?"history.split_room":"history.add_room"),i);this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._pendingSplit=null,this._splitSel=null;const d=this._areaSel;if(this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices(),!h)return;let p=0;if(d){const e=zv,t={...this._layout};for(const i of this._devices){if(i.area!==d||i.space!==this._space)continue;if(p++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(t[i.id]={s:this._space,x:s.x/zv,y:s.y/e},this._dirtyPos.add(i.id))}this._layout=t,this._persistLayout()}const u=this._model.find(e=>e.id===this._space)?.rooms.length||0;this._showToast(d?this._t("toast.room_saved",{n:u,added:p}):this._t("toast.room_saved_no_area",{n:u}))}_cancelPath(){this._wallRepairDiagnostic=null,this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._clearPlanSnapHover(),this._roomDialog=!1,this._pendingSplit=null,this._wallFaceBatch=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const e=this._resumeDraftBySpace[this._space];if(!e)return;const t=this._spaceModel()?.room_drafts.find(t=>t.id===e);t?(this._activeDraftId=e,this._path=t.points.map(e=>[...e]),this._draftSegmentCms=this._adoptDraftCms(this._path,t.segments.map(e=>e.cm),t.id),this._clearPlanSnapHover()):delete this._resumeDraftBySpace[this._space]}_roomDialogCancel(){if(this._roomDialog=!1,this._roomEditId)return this._roomEditId=null,this._nameSel="",void(this._areaSel="");if(this._wallFaceBatch){const e=this._wallFaceBatch;return this._wallFaceBatch=null,this._path=e.activePath.map(e=>[...e]),this._draftSegmentCms=[...e.activeCms],this._activeDraftId=e.activeDraftId,this._nameSel="",this._areaSel="",void this.requestUpdate()}if(this._pendingSplit)return this._pendingSplit=null,void(this._splitSel=null);this._undoPoint()}get _freeAreas(){const e=new Set;for(const t of this._serverCfg?.spaces||[])for(const i of t.rooms||[])i.area&&e.add(i.area);for(const t of this._wallFaceBatch?.decisions||[])t.create&&t.area&&e.add(t.area);return Object.values(this.hass?.areas||{}).filter(t=>!e.has(t.area_id)).sort((e,t)=>(e.name||"").localeCompare(t.name||""))}_openDeviceInbox(){this._deviceInboxReturn=null,this._deviceInbox=this._deviceInbox||{tab:"on_plan",search:"",showEntities:!1,onlyNew:!1,limit:100}}_closeMarkerDialog(){if(this._markerDialog=null,this._deviceInboxReturn){const e={...this._deviceInboxReturn};this._deviceInbox=e,this._deviceInboxReturn=null,e.anchor&&this.updateComplete.then(()=>requestAnimationFrame(()=>{const t=`.device-inbox-row[data-binding="${CSS.escape(e.anchor)}"]`;this.renderRoot.querySelector(t)?.scrollIntoView({block:"nearest"})}))}}_deviceInboxCandidates(e){return wp({hass:this._planHass,devices:this._devices,markers:this._markers,showEntities:e,labels:{device:this._t("marker.sub_device"),z2mGroup:this._t("marker.sub_z2m_group"),group:this._t("marker.sub_group"),helper:this._t("marker.sub_helper"),entity:this._t("marker.sub_entity")}})}_deviceInboxRows(){const e=this._deviceInbox||this._deviceInboxReturn,t=!!e?.showEntities,i=[this._haRegistry.revision,this._cfgRev,this._cfgEpoch,this._regSignature,this._newSyncKey,t?1:0,this._showAll?1:0,pa(this.hass,this._config?.language)].join("|");if(this._deviceInboxMemo?.key===i)return this._deviceInboxMemo.rows;const s=this._deviceInboxCandidates(t),n=new Set(s.map(e=>e.value));for(const e of this._markers)e.binding&&"virtual"!==e.binding&&n.add(e.binding);for(const e of this._devices)e.bindingKind&&"virtual"!==e.bindingKind&&e.bindingRef&&n.add(`${e.bindingKind}:${e.bindingRef}`);const o=new Map;for(const e of n)o.set(e,this._bindingStatus(e));const a=this._areaToSpace,r={};for(const[e,t]of Object.entries(this.hass?.areas||{}))r[e]=a[e]?.room?.name||t?.name||e;for(const[e,t]of Object.entries(a))r[e]=t.room.name||r[e]||e;const l=Object.fromEntries(this._model.map(e=>[e.id,e.title])),c=Object.fromEntries(Object.entries(a).map(([e,t])=>[e,t.space])),h={},d=new Map;for(const[e,t]of Object.entries(this._fullRegistryHass.entities||{})){const i=String(t?.platform||"").trim();if(i&&(h[`entity:${e}`]=i),i&&t?.device_id){const e=d.get(t.device_id)||new Set;e.add(i),d.set(t.device_id,e)}}for(const[e,t]of d)h[`device:${e}`]=[...t].sort().join(", ");const p={};for(const[e,t]of Object.entries(this._fullRegistryHass.devices||{})){const i=`device:${e}`,s=d.get(e)||new Set,n=Array.isArray(t?.identifiers?.[0])?String(t.identifiers[0][0]||""):"",o=[n,...s].some(e=>this._excluded.has(e));"service"===t?.entry_type?p[i]="service_entry":o?p[i]="excluded_integration":"Group"===t?.model?p[i]="grouped_light":/scene/i.test(t?.model||"")?p[i]="excluded_domain":(/bridge/i.test(`${t?.model||""}${t?.name||""}`)||"myheat"===n&&t?.via_device_id)&&(p[i]="represented_by_parent")}const u=function(e){const{devices:t,markers:i,candidates:s,statuses:n,newDeviceIds:o,showHiddenOnPlan:a,areaNames:r={},spaceNames:l={},spaceByArea:c={},integrationByBinding:h={},reasonByBinding:d={}}=e,p=new Map;for(const e of t)!e.virtual&&e.bindingKind&&"virtual"!==e.bindingKind&&e.bindingRef&&p.set(`${e.bindingKind}:${e.bindingRef}`,e);const u=new Map,_=new Map;for(const e of i)e?.binding&&"virtual"!==e.binding&&(e.removed?_.set(e.binding,e):u.set(e.binding,e));const m=new Map(s.map(e=>[e.value,e])),g=new Set([...p.keys(),...u.keys(),...m.keys(),...[..._.keys()].filter(e=>m.has(e))]),f=[];for(const e of g){const t=p.get(e),i=u.get(e),s=i?void 0:_.get(e),g=m.get(e);let v;if(s&&g)v="readd";else if(!0===i?.hidden)v="hidden";else if(t||i)v="on_plan";else{if(!g)continue;v="available"}const b=t?.bindingStatus||n.get(e)||kp,y=i||s,w=xp(e),k=t?.area||g?.areaId||y?.area||"",x=t?.space||y?.space||c[k]||"",$=t?.name||y?.name||g?.label||e,S=t?.model||y?.model||g?.model||"",M=!!g?.parentDeviceId&&p.has(`device:${g.parentDeviceId}`),C="readd"===v?"removed":"hidden"===v?i&&$p(i)?d[e]||"automatic_hidden":"manual_hidden":"on_plan"===v?i?"visible_explicit":"visible_auto":d[e]||(M?"represented_by_parent":k&&x?"available":"no_bound_room"),D=!!t&&(!t.hidden||a),P="active"===b.kind,T=D&&(P||"ha_disabled"===b.kind&&a),I=[$,S,h[e],r[k],l[x],e,g?.sub].filter(Boolean).join(" ").toLocaleLowerCase();f.push({key:e,binding:e,category:v,status:b,reason:C,deviceId:t?.id,markerId:y?.id,name:$,icon:t?.icon||y?.icon||("entity"===w?"mdi:code-braces":"mdi:devices"),model:S,integration:h[e]||"",areaId:k,areaName:r[k]||"",spaceId:x,spaceName:l[x]||"",kind:w,isNew:!!t&&o.has(t.id),searchText:I,canFind:T,canEdit:!!t||!!i,canHide:"on_plan"===v&&P,canShow:"hidden"===v&&P,canAdd:("available"===v||"readd"===v)&&P})}const v=e=>"active"===e.status.kind?1:0;return f.sort((e,t)=>e.category.localeCompare(t.category)||Number(t.isNew)-Number(e.isNew)||v(e)-v(t)||e.name.localeCompare(t.name)||e.binding.localeCompare(t.binding))}({devices:this._devices,markers:this._markers,candidates:s,statuses:o,newDeviceIds:this._newIds,showHiddenOnPlan:this._showAll,areaNames:r,spaceNames:l,spaceByArea:c,integrationByBinding:h,reasonByBinding:p});return this._deviceInboxMemo={key:i,rows:u},u}_deviceForInboxRow(e){const t=e.deviceId?this._devices.find(t=>t.id===e.deviceId):null;if(t)return t;const i=e.markerId?this._markers.find(t=>t.id===e.markerId):null;return!i||i.removed?null:{id:i.id,name:e.name,model:e.model,area:e.areaId,space:e.spaceId||this._space,hidden:!0===i.hidden||"ha_disabled"===e.status.kind,userHidden:!0===i.hidden,bindingStatus:e.status,icon:e.icon,entities:"active"===e.status.kind?e.status.enabledEntityIds:[],allEntities:e.status.allEntityIds,primary:"active"===e.status.kind?e.status.enabledEntityIds[0]:void 0,marker:i,bindingKind:e.kind,bindingRef:e.binding.slice(e.binding.indexOf(":")+1),pdfs:i.pdfs||[]}}_openInboxMarker(e,t=!1){const i=this._deviceInbox;if(i){if(this._deviceInboxReturn={...i,anchor:e.key},this._deviceInbox=null,!t){const t=this._deviceForInboxRow(e);return void(t?this._openMarkerDialog(t):this._closeMarkerDialog())}this._openMarkerDialog(),this._markerDialog?this._markerDialog={...this._markerDialog,bindingMode:"ha",binding:e.binding,bindingOpen:!1,showEntities:"entity"===e.kind,name:""}:this._closeMarkerDialog()}}async _setInboxHidden(e,t){const i=this._deviceInbox,s=this._serverCfg;if(!i||!s||i.busy||"active"!==e.status.kind)return;const n=s.markers||[],o=n.find(t=>!t.removed&&t.binding===e.binding);if(!t&&!o)return;const a=o?.id||Fi(e.binding,e.markerId,()=>`m_${Date.now().toString(36)}`),r=o?{...o,hidden:t}:{id:a,binding:e.binding,hidden:!0};s.markers=[...n.filter(t=>t.id!==a&&(t.binding!==e.binding||!0===t.removed)),r],this._deviceInbox={...i,busy:e.key,anchor:e.key};try{await this._saveConfigNow(),this._regSignature="",this._deviceInboxMemo=null,this._maybeRebuildDevices(),this._deviceInbox&&(this._deviceInbox={...this._deviceInbox,busy:void 0}),this._showToast(this._t("device_inbox.saved"))}catch(e){this._serverCfg===s&&(s.markers=n),this._deviceInbox&&(this._deviceInbox={...this._deviceInbox,busy:void 0}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}_findInboxDevice(e){if(!e.canFind)return;const t=this._deviceForInboxRow(e);if(!t)return;if(this._deviceInbox=null,t.space&&t.space!==this._space){if(!this._commitSpace(t.space))return;this._restoreZoom()}const i=()=>{const e=this._devices.find(e=>e.id===t.id)||t,i=this._pos(e);this._applyView(this._zoom,i.x,i.y),this._selId=e.id,this.requestUpdate(),window.setTimeout(()=>{this._selId===e.id&&(this._selId=null,this.requestUpdate())},1500)};requestAnimationFrame(()=>requestAnimationFrame(i))}_deviceInboxTabKey(e){if("ArrowLeft"!==e.key&&"ArrowRight"!==e.key)return;const t=this._deviceInbox;if(!t)return;const i=["on_plan","available","hidden","readd"],s="ArrowRight"===e.key?1:-1,n=(i.indexOf(t.tab)+s+i.length)%i.length;this._deviceInbox={...t,tab:i[n],limit:100,onlyNew:!1},e.preventDefault()}_openMarkerDialog(e){if(this._vacAllCamerasFor=null,this._vacAllCameraCache=null,e&&this._ackNewDevice(e.id),this._norm)if(e){const t=e.marker,i=Object.prototype.hasOwnProperty.call(t||{},"is_light"),s=Object.prototype.hasOwnProperty.call(t||{},"light_entity"),n=Object.prototype.hasOwnProperty.call(t||{},"glow_color"),o=Object.prototype.hasOwnProperty.call(t||{},"value_badge"),a=Object.prototype.hasOwnProperty.call(t||{},"tap_action"),r=Object.prototype.hasOwnProperty.call(t||{},"toggle_entity"),l=ks(t?.glow_color),c=this._devicePresentation(e,!0).valueBadge,h=qo(this._planHass,e,this._devices),d=Wo(this._planHass,e,h);this._markerDialog={devId:e.id,name:e.name,binding:"virtual"===e.bindingKind?"virtual":e.bindingKind+":"+e.bindingRef,bindingMode:"virtual"===e.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===e.bindingKind&&!!this._fullRegistryHass.entities[e.bindingRef||""]?.device_id,bindingFilter:"",icon:e.marker?.icon||"",autoIcon:e.icon||"",display:Bi(e.marker?.display),rippleColor:Ie(e.marker?.ripple_color,""),rippleSize:Number(e.marker?.ripple_size)>0?Number(e.marker.ripple_size):1.5,size:Number(e.marker?.size)>0?Number(e.marker.size):1,angle:Number(e.marker?.angle)||0,tapAction:qp(e.marker?.tap_action,e.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:a,originalTapAction:e.marker?.tap_action,tapHintAnnouncement:"",toggleEntity:t?.toggle_entity||"",toggleEntityTouched:!1,originalHasToggleEntity:r,originalToggleEntity:t?.toggle_entity,tapTarget:e.marker?.tap_target||"",tapConfirm:!0===e.marker?.tap_confirm,runFilter:"",controls:Zn(e.marker?.binding,e.marker?.controls,e.entities),controlsFilter:"",lightRole:!0===t?.is_light?"always":!1===t?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:i,originalIsLight:t?.is_light,lightEntity:t?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:s,originalLightEntity:t?.light_entity,glowMode:null!=l?.bri?"fixed":l?"color":"auto",glowColor:l?.c||this._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(l?.bri??1))),glowColorDrafted:!!l,glowBrightnessDrafted:null!=l?.bri,glowTouched:!1,originalHasGlowColor:n&&(!!l||null===t?.glow_color),originalGlowColor:l||(null===t?.glow_color?null:void 0),valueBadgeEnabled:o?!0===t?.value_badge?.enabled:!!c,valueBadgeSource:t?.value_badge?.source||c?.source||d,valueBadgePosition:t?.value_badge?.position||c?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:o,originalValueBadge:t?.value_badge,useClimateTemp:!0===e.marker?.use_climate_temp,glowRadius:Number(e.marker?.glow_radius_cm)>0?String(this._imperial?Math.round(Number(e.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(e.marker.glow_radius_cm))/100):"",model:e.model||"",link:e.link||"",description:e.description||"",pdfs:[...e.pdfs||[]],room:e.marker?.room_id?e.space+"#@"+e.marker.room_id:e.space&&e.area?e.space+"#"+e.area:"",hideFromPlan:!0===e.marker?.hidden,busy:!1}}else this._markerDialog={name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:1.5,size:1,angle:0,tapAction:"info",tapActionTouched:!1,originalHasTapAction:!1,originalTapAction:void 0,tapHintAnnouncement:"",toggleEntity:"",toggleEntityTouched:!1,originalHasToggleEntity:!1,originalToggleEntity:void 0,tapTarget:"",tapConfirm:!1,runFilter:"",controls:[],controlsFilter:"",lightRole:"auto",lightRoleTouched:!1,originalHasIsLight:!1,originalIsLight:void 0,lightEntity:"",lightEntityTouched:!1,originalHasLightEntity:!1,originalLightEntity:void 0,glowMode:"auto",glowColor:this._fillColors.glow_light.c,glowBrightness:100,glowColorDrafted:!1,glowBrightnessDrafted:!1,glowTouched:!1,originalHasGlowColor:!1,originalGlowColor:void 0,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgePosition:"right",valueBadgeTouched:!1,originalHasValueBadge:!1,originalValueBadge:void 0,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const e=[];for(const t of Gi)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(t+".")&&this._planEntityAvailable(i)&&e.push({value:i,label:s?.attributes?.friendly_name||i,sub:this._t("run."+t)});return e.sort((e,t)=>e.sub.localeCompare(t.sub)||e.label.localeCompare(t.label))}_bindingCandidates(){const e=wp({hass:this._planHass,devices:this._devices,markers:this._markers,showEntities:!!this._markerDialog?.showEntities,currentBinding:this._markerDialog?.binding,currentDeviceId:this._markerDialog?.devId,labels:{device:this._t("marker.sub_device"),z2mGroup:this._t("marker.sub_z2m_group"),group:this._t("marker.sub_group"),helper:this._t("marker.sub_helper"),entity:this._t("marker.sub_entity")}}),t=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),i=t?e.filter(e=>(e.label+" "+e.sub+" "+e.value).toLowerCase().includes(t)):e;return i.sort((e,t)=>e.label.localeCompare(t.label)),i.slice(0,200)}_physicalBodiesR(e=this._spaceModel()){if(!e)return[];const t=`${e.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===t)return this._physicalBodiesCache.all;const i=_h(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e));return this._physicalBodiesCache={key:t,...i},i.all}_rawPhysicalBodiesR(e=this._spaceModel()){if(!e)return[];this._physicalBodiesR(e);const t=this._physicalBodiesCache;return t?[...t.drafts,...t.partitions,...t.columns]:[]}_cleanFloor(e,t,i=this._spaceModel()){if(!i)return{floor:t,geom:null,path:"",area:yh([[[...t,t[0]]]])};const s=e.id||`#${i.rooms.indexOf(e)}`,n=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._resize.preview){const e=wv(this._cleanFloorCache,n);if(e.hit)return e.value}const o=t.map(e=>e[0]),a=t.map(e=>e[1]),r=[Math.min(...o),Math.min(...a),Math.max(...o),Math.max(...a)],l=this._physicalBodiesR(i).filter(e=>{const t=e.map(e=>e[0]),i=e.map(e=>e[1]);return Math.max(...t)>=r[0]&&Math.min(...t)<=r[2]&&Math.max(...i)>=r[1]&&Math.min(...i)<=r[3]}),c=l.length?bh(t,l):null,h={floor:t,geom:c,path:c?ch(c):"",area:yh(c||[[[...t,t[0]]]])};return this._resize.preview||kv(this._cleanFloorCache,n,h,600),h}_autoIconForBinding(e){if("virtual"===e)return"mdi:map-marker";const[t,i]=e.split(":");if(!i)return"";const s=this._fullRegistryHass,n=this._bindingStatus(e),o="active"===n.kind?n.enabledEntityIds:n.allEntityIds;if("device"===t){const e=s.devices?.[i];if(!e)return"mdi:help-circle";const t=o;return t.some(e=>e.startsWith("lock."))?"mdi:lock":yo(s,e.name_by_user||e.name||"",e.model,t,this._iconRules)}if("entity"===t){const e=s.entities?.[i],t=this.hass.states?.[i],n=e?.name||t?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":yo(s,n,"",[i],this._iconRules)}return""}_allRoomsFlat(){const e=[];for(const t of this._serverCfg?.spaces||[])for(const i of t.rooms||[])i.area?e.push({value:t.id+"#"+i.area,label:(t.title||t.id)+" · "+i.name}):i.id&&e.push({value:t.id+"#@"+i.id,label:(t.title||t.id)+" · "+i.name+" · "+this._t("marker.subarea")});return e}_errText(e){if(!e)return this._t("err.unknown");if("string"==typeof e)return e;if("invalid_passage_fields"===e.code){const t=String(e.message||e.error||"").match(/space=([^;]*);\s*opening=([^;]*);\s*fields=([^;]*)/);if(t){const e=this._serverCfg?.spaces?.find(e=>String(e.id)===t[1]),i={contact:"opening.contact_label",lock:"opening.lock_label",invert:"opening.invert",flip_h:"opening.flip_h",flip_v:"opening.flip_v"},s=t[3].split(",").filter(Boolean).map(e=>i[e]?this._t(i[e]):e).join(", ");return this._t("opening.invalid_passage_fields",{room:e?.title||t[1],fields:s})}}if("invalid_partition_opening_jamb_margin"===e.code){const t=String(e.message||e.error||"").match(/margin_cm=([^;]*)/),i=t?Number(t[1]):NaN;if(Number.isFinite(i))return this._t("opening.partition_jamb_margin",{distance:li(i,this._imperial)})}if(e.message)return e.message;if(e.error)return e.error;if(null!=e.code)return this._t("err.code",{code:e.code});try{return JSON.stringify(e)}catch{return String(e)}}_backupErrorText(e){const t=e?.code??e?.error;if("string"==typeof t){const e=`backup.error.${t}`,i=this._t(e);if(i!==e)return i}return this._errText(e)}async _pickMarkerFiles(e){const t=e.target,i=t.files?[...t.files]:[];if(t.value="",!i.length||!this._markerDialog)return;const s=this._markerDialog.uploadId||this._markerDialog.devId||"new",n=[];for(const e of i)try{const t=new FormData;t.append("marker_id",s),t.append("file",e,e.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:t}):await fetch("/api/houseplan/upload",{method:"POST",body:t,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const e={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(e[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||e.name,url:o.url})}catch(t){this._showToast(this._t("toast.file_failed",{name:e.name,err:this._errText(t)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(e){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(t=>t.url!==e)})}_markerLightFields(e){const t={};if(e.lightRoleTouched?"always"===e.lightRole?t.is_light=!0:"never"===e.lightRole&&(t.is_light=!1):e.originalHasIsLight&&(t.is_light=e.originalIsLight??null),e.lightEntityTouched?e.lightEntity&&(t.light_entity=e.lightEntity):e.originalHasLightEntity&&(t.light_entity=e.originalLightEntity??null),e.glowTouched){if("auto"!==e.glowMode){const i={c:Ie(e.glowColor,this._fillColors.glow_light.c)};"fixed"===e.glowMode&&(i.bri=Math.max(.01,Math.min(1,Math.round(e.glowBrightness)/100))),t.glow_color=i}}else e.originalHasGlowColor&&(t.glow_color=e.originalGlowColor??null);return t}_markerTapActionFields(e){return e.tapActionTouched?{tap_action:e.tapAction||null}:e.originalHasTapAction?{tap_action:e.originalTapAction??null}:{}}_markerToggleEntityFields(e){return(t={touched:e.toggleEntityTouched,originalHas:e.originalHasToggleEntity,original:e.originalToggleEntity,value:e.toggleEntity}).touched?t.value?{toggle_entity:t.value}:{}:t.originalHas?{toggle_entity:t.original??null}:{};var t}async _saveMarker(){const e=this._markerDialog;if(!e||e.busy)return;const t=this._effectiveMarkerTapAction(e);if("ha"===e.bindingMode&&(!e.binding||"virtual"===e.binding))return;if("virtual"===e.binding&&!e.name.trim())return void this._showToast(this._t("toast.virtual_name_required"));if("run"===t&&!e.tapTarget)return void this._showToast(this._t("toast.run_target_required"));if(e.valueBadgeTouched&&e.valueBadgeEnabled&&!e.valueBadgeSource)return void this._showToast(this._t("toast.value_badge_source_required"));if("ha"===e.bindingMode){const t=this._bindingStatus(e.binding),i=e.devId?this._markers.find(t=>t.id===e.devId):null,s=!i||i.binding!==e.binding;if("active"!==t.kind&&s)return void this._showToast(this._t("ha_disabled"===t.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===t.kind&&!0===i?.hidden&&!e.hideFromPlan)return this._markerDialog={...e,hideFromPlan:!0},void this._showToast(this._t("entity"===t.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}const i=this._serverCfg;if(!i)return;const s=i.markers||[],n=js(e.room);let o=n?.space||null;const a=n?.area||null,r=n?.roomId||null,l=Fi(e.binding,e.devId,()=>"v_"+Date.now().toString(36)),c=e.devId,h=c?this._devices.find(e=>e.id===c):null,d=n?.space||h?.space||null,p=d?this._spaceModelById(d):this._spaceModel();if(!p)return;const u=p.id;"virtual"!==e.binding||o||(o=u),this._markerDialog={...e,busy:!0};try{const n="virtual"===e.binding?[]:s.filter(t=>t.removed&&t.binding===e.binding).map(e=>e.id),d=n.length>0,_=Zn(e.binding,e.controls,this._bindingEntities(e.binding)),m=s.find(e=>e.id===l||e.id===c)?.vacuum||null,g={id:l,vacuum:m,binding:e.binding,name:e.name.trim()||null,icon:e.icon||null,display:"badge"!==e.display?e.display:null,ripple_color:"icon_ripple"===e.display&&e.rippleColor?e.rippleColor:null,ripple_size:"icon_ripple"===e.display&&1.5!==e.rippleSize?e.rippleSize:null,size:1!==e.size?e.size:null,angle:e.angle?e.angle:null,...this._markerTapActionFields(e),...this._markerToggleEntityFields(e),tap_target:"run"===t&&e.tapTarget||null,tap_confirm:!!e.tapConfirm||null,controls:_.length?_:null,...this._markerLightFields(e),...this._markerValueBadgeFields(e),use_climate_temp:!!e.useClimateTemp||null,glow_radius_cm:(()=>{const t=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};("virtual"===e.binding||e.room)&&(g.space=o,g.area=a,g.room_id=r);const f=h?.marker?.room_id??null,v=!!e.room&&null!=h&&(h.space!==o||h.area!==a||f!==r);let b=!1;const y=e.uploadId||c;if(y&&y!==l&&g.pdfs?.length)try{const e=await this.hass.callWS({type:"houseplan/files/migrate",from_id:y,to_id:l}),t=e?.mapping||{};g.pdfs=function(e,t,i,s){if(!t||!i||t===i)return e;const n="/files/"+t+"/",o="/files/"+i+"/";return e.map(e=>{if(!e.url.includes(n))return e;const t=e.url.split(n)[1]||"",[i,a]=[t.split("?")[0],t.includes("?")?"?"+t.split("?")[1]:""];if(s){const t=s[decodeURIComponent(i)]??s[i];return t?{...e,url:e.url.split(n+i)[0]+o+encodeURIComponent(t)+a}:e}return{...e,url:e.url.split(n).join(o)}})}(g.pdfs,y,l,t),b=Object.keys(t).length>0}catch(e){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(e)}))}i.markers=s,c&&c!==l&&(i.markers=function(e,t,i){if(!t||!i||t===i)return[...e];const s=`marker:${t}`,n=`marker:${i}`;return e.map(e=>{const t=Array.isArray(e.controls)?e.controls.map(e=>e===s?n:e):e.controls,i="derived_marker_state"===e.value_badge?.source?.kind&&e.value_badge.source.ref===s?{...e.value_badge,source:{...e.value_badge.source,ref:n}}:e.value_badge;return t===e.controls&&i===e.value_badge?e:{...e,controls:t,value_badge:i}})}(i.markers,c,l)),c&&c!==l&&"derived_marker_state"===g.value_badge?.source?.kind&&g.value_badge.source.ref===`marker:${c}`&&(g.value_badge.source={kind:"derived_marker_state",ref:`marker:${l}`}),i.markers=i.markers.filter(e=>e.id!==l&&e.id!==c&&("virtual"===g.binding||e.binding!==g.binding)),i.markers.push(g);let w=null;const k=c?this._layout[c]:null,x=k?{s:k.s||h?.space||this._space,x:k.x,y:k.y}:c&&h&&this._defPos[c]?this._normPos(h.space,this._defPos[c].x,this._defPos[c].y):null;if(!d&&x&&x.s===u)l===c&&this._layout[l]&&!v||(w={s:x.s,x:x.x,y:x.y},this._layout={...this._layout,[l]:w});else if(d||!this._layout[l]||v){let e=p.vb[0]+p.vb[2]/2,t=p.vb[1]+p.vb[3]/2;const i=r?p.rooms.find(e=>e.id===r):a?p.rooms.find(e=>e.area===a):void 0;i&&([e,t]=this._roomCenter(i)),w=this._normPos(u,e,t),this._layout={...this._layout,[l]:w}}if(await this._saveConfigNow(),w){const e=ml(w);this._layout={...this._layout,[l]:e},this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:l,pos:e}))}const $=new Set(n);c&&c!==l&&$.add(c),$.delete(l);for(const e of $)delete this._layout[e],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e}).then(e=>this._noteLayoutRev(e)).catch(()=>{});b&&y&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:y}).catch(()=>{}),this._closeMarkerDialog(),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_saved"))}catch(e){this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}async _deleteMarker(){const e=this._markerDialog;if(!e||e.busy||!e.devId)return;const t=e.devId?this._devices.find(t=>t.id===e.devId):null,i=this._markers.find(t=>t.id===e.devId);if(!t&&!i)return;const s=e.name||this._t("device.fallback");if(!confirm(this._t("confirm.remove_marker",{name:s})))return;const n=this._serverCfg;n.markers=n.markers||[];const o=n.markers,a=t?.id||i.id,r=t?"virtual"===t.bindingKind?"virtual":t.bindingKind&&t.bindingRef?`${t.bindingKind}:${t.bindingRef}`:"":i.binding;if(!r)return;const l=function(e,t,i,s){const n=new Set([t]),o=e.filter(e=>{const o=e.id===t||!s&&e.binding===i;return o&&n.add(e.id),!o});return{markers:s?o:[...o,{id:t,binding:i,removed:!0,hidden:!0}],cleanupIds:n}}(n.markers,a,r,"virtual"===r);var c,h;n.markers=(c=l.markers,h=l.cleanupIds,c.map(e=>{if(!Array.isArray(e.controls))return e;const t=e.controls.filter(e=>!("string"==typeof e&&e.startsWith("marker:")&&h.has(e.slice(7))));return t.length===e.controls.length?e:{...e,controls:t.length?t:null}}));const d=l.cleanupIds;this._markerDialog={...e,busy:!0};try{await this._saveConfigNow();for(const e of d){delete this._layout[e],delete this._defPos[e],this._dirtyPos.delete(e),this._sentPos.delete(e);const t=this._activityRt.get(e);t&&clearTimeout(t.timer),this._activityRt.delete(e),this._vacRt.delete(e),delete this._vacSrvTrails[e],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e}).then(e=>this._noteLayoutRev(e)).catch(()=>{}),await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:e}).catch(()=>{}),await this.hass.callWS({type:"houseplan/trail/delete",marker_id:e}).catch(()=>{})}this._deviceInboxReturn&&(this._deviceInboxReturn={...this._deviceInboxReturn,tab:"readd",anchor:r}),this._closeMarkerDialog(),this._infoCard?.id===a&&this._closeInfoCard(),this._selId===a&&(this._selId=null),this._drag?.id===a&&(this._drag=null),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(e){this._serverCfg===n&&(n.markers=o),this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}_normPos(e,t,i){return{s:e,x:t/zv,y:i/zv}}_openSpaceDialog(e,t){if(this._serverStorage&&this._serverCfg)if("edit"===e){const i=this._serverCfg.spaces.find(e=>e.id===t);if(!i)return;const s=cs(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?rs(i.settings.custom_fill):null,o="none"===s.fill?{...n||as,a:0}:n;this._spaceDialog={mode:e,spaceId:t,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:s.showBorders,showNames:s.showNames,zeroWallStyle:q_(i),displayTouched:!0,hideDecor:s.hideDecor,hideOpenings:s.hideOpenings,roomColor:s.color,roomOpacity:s.opacity,fillMode:"none"===s.fill?"custom":s.fill,customFill:o,glowEnabled:s.glow,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:zr({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:Th(Number(i.cell_cm)>0?Number(i.cell_cm):5,this._imperial),cellCmTouched:!1,busy:!1}}else this._spaceDialog={mode:e,title:"",planUrl:null,planFile:null,...Xd(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:ns,roomOpacity:os,fillMode:"custom",customFill:{...as,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:Ph(this._imperial),cellCmInput:Th(Ph(this._imperial),this._imperial),cellCmTouched:!1,busy:!1};else this._showToast(this._t("toast.integration_missing"))}async _pickPlanFile(e){const t=e.target,i=t.files?.[0];if(!i||!this._spaceDialog)return;const s={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[i.type]||(i.name.toLowerCase().endsWith(".svg")?"svg":"");if(!s)return void this._showToast(this._t("toast.plan_formats"));const n=new Uint8Array(await i.arrayBuffer());let o="";for(let e=0;e<n.length;e+=32768)o+=String.fromCharCode(...n.subarray(e,e+32768));const a=btoa(o),r=URL.createObjectURL(i),l=await new Promise(e=>{const t=new Image;t.onload=()=>e(t.naturalWidth&&t.naturalHeight?t.naturalWidth/t.naturalHeight:1.414),t.onerror=()=>e(1.414),t.src=r});URL.revokeObjectURL(r),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:a,aspect:l,name:i.name}}}_useServerPlan(e){const t=this._spaceDialog;t&&(this._spaceDialog={...t,planUrl:e,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(e))}async _readPlanAspect(e){for(let t=0;t<40;t++){const t=this._display(e);if(t){const i=await new Promise(e=>{const i=new Image;i.onload=()=>e(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>e(0),i.src=t}),s=this._spaceDialog;return s&&s.planUrl===e&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(e=>setTimeout(e,150)),this._spaceDialog?.planUrl!==e)return 0}return 0}async _deleteServerPlan(e){if(confirm(this._t("confirm.delete_plan",{name:e})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:e});const t=this._spaceDialog;t?.saved&&(this._spaceDialog={...t,saved:t.saved.filter(t=>t.name!==e)})}catch(e){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(e)}))}}_renderServerPlans(e){if(e.savedBusy)return q`<div class="savedplans muted">${this._t("space.loading")}</div>`;const t=e.saved||[];if(!t.length)return q`<div class="savedplans muted">${this._t("space.no_saved")}</div>`;return q`<div class="savedplans">
|
||
${t.map(t=>q`
|
||
<div class="savedplan ${t.url===e.planUrl?"cur":""}">
|
||
<img src=${this._display(t.url)} alt="" loading="lazy" decoding="async" />
|
||
<div class="savedmeta">
|
||
<b>${t.name}</b>
|
||
<span class="muted">${(e=>e>=1048576?(e/1048576).toFixed(1)+" MB":Math.round(e/1024)+" KB")(t.size)}${t.used_by.length?" · "+this._t("space.used_by",{list:t.used_by.join(", ")}):""}</span>
|
||
</div>
|
||
<button class="btn ghost" @click=${()=>this._useServerPlan(t.url)}
|
||
?disabled=${t.url===e.planUrl}>${this._t("btn.use")}</button>
|
||
<button class="btn ghost danger"
|
||
title=${t.used_by.length||t.url===e.planUrl?this._t("space.in_use"):this._t("btn.delete")}
|
||
?disabled=${t.used_by.length>0||t.url===e.planUrl}
|
||
@click=${()=>this._deleteServerPlan(t.name)}>
|
||
<ha-icon icon="mdi:trash-can-outline"></ha-icon>
|
||
</button>
|
||
</div>`)}
|
||
</div>`}async _saveSpaceDialog(){const e=this._spaceDialog;if(!e||e.busy||!e.title.trim())return;if("file"===e.source&&!e.planFile&&!e.planUrl)return void this._showToast(this._t("toast.plan_required"));const t="create"===e.mode&&0===(this._serverCfg?.spaces.length||0);this._spaceDialog={...e,busy:!0};try{const n="create"===e.mode?"s"+Date.now().toString(36):e.spaceId;let o=null;if("file"===e.source&&e.planFile){o={url:(await this.hass.callWS({type:"houseplan/plan/set",space_id:n,ext:e.planFile.ext,data:e.planFile.b64})).url,aspect:e.planFile.aspect}}let a=e.savedAspect||null;!o&&"file"===e.source&&e.planUrl&&!a&&this._aspectJob&&(a=await this._aspectJob||null);const r=this._serverCfg;let l;if("create"===e.mode)i=n,s=e.title.trim(),l={id:i,title:s,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]},r.spaces.push(l);else{if(l=r.spaces.find(e=>e.id===n),!l)throw new Error("space "+n+" is gone from the config");l.title=e.title.trim()}o?(l.plan_url=o.url,l.plan_aspect=o.aspect):"file"===e.source&&e.planUrl&&e.planUrl!==l.plan_url&&(l.plan_url=e.planUrl,l.plan_aspect=a),"draw"===e.source&&(l.plan_url=null,l.plan_aspect=null,delete l.plan_x,delete l.plan_y,delete l.plan_scale,delete l.plan_scale_x,delete l.plan_scale_y,delete l.plan_angle),l.settings={...l.settings||{},show_borders:e.showBorders,show_names:e.showNames,hide_decor:e.hideDecor||void 0,hide_openings:e.hideOpenings||void 0,room_color:e.roomColor,room_opacity:e.roomOpacity,bg_color:e.bgColor||void 0,bg_mode:e.bgMode||void 0,north_deg:e.northDeg??void 0,sun_rays:e.sunRays??void 0,fill_mode:e.fillMode,custom_fill:e.customFill||void 0,glow_enabled:e.glowEnabled,temp_min:Number.isFinite(e.tempMin)?Math.min(e.tempMin,e.tempMax):20,temp_max:Number.isFinite(e.tempMax)?Math.max(e.tempMin,e.tempMax):25,show_lqi:e.showLqi,card_font_scale:1!==e.cardFontScale?e.cardFontScale:void 0,label_temp:e.labelTemp,label_hum:e.labelHum,label_lqi:e.labelLqi,label_light:e.labelLight},l.zero_wall_style=e.zeroWallStyle,l.cell_cm=Number.isFinite(e.cellCm)&&e.cellCm>0?Math.max(.1,Math.min(vv,e.cellCm)):5,await this._saveConfigNow(),this._spaceDialog=null,"create"===e.mode&&this._commitSpace(l.id),this._regSignature="",this._maybeRebuildDevices(),this._importQueue.length?this._openNextImport():t||this._importTotal>0?(this._importTotal=0,this._commitSpace(this._serverCfg.spaces[0]?.id||this._space),this._setMode("plan"),this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._showToast(this._t(t&&!this._importTotal?"toast.space_added_onboard":"import.done"))):(this._showToast("create"===e.mode?this._t("toast.space_added"):this._t("toast.space_saved")),"create"===e.mode&&("plan"!==this._mode?this._setMode("plan"):(this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._saveNav())))}catch(e){"conflict"!==e?.code&&await this._reloadConfigOnly(!0),this._spaceDialog&&(this._spaceDialog={...this._spaceDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}var i,s}async _deleteSpace(){const e=this._spaceDialog;if(!e||"edit"!==e.mode)return;const t=this._serverCfg;if(!t)return;const i=t.spaces.find(t=>t.id===e.spaceId),s=Hg(t,this._layout||{},e.spaceId||""),n=1===t.spaces.length&&t.spaces[0]?.id===e.spaceId;if(!s.count||n){if(confirm(this._t("confirm.delete_space",{title:i.title}))){this._spaceDialog={...e,deleteBlockers:0,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._persistLayout.pending()&&this._persistLayout.flush(),await this._writeChain;const t=await this.hass.callWS({type:"houseplan/space/delete",space_id:e.spaceId,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev}),[i,s]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._adoptStructuralResponses(i,s),this._cfgRev=t?.config_rev??this._cfgRev,this._layoutRev=t?.layout_rev??this._layoutRev,this._spaceDialog=null,this._space===e.spaceId&&this._commitSpace(this._serverCfg.spaces[0]?.id||""),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.space_deleted"))}catch(t){"conflict"!==t?.code&&"space_in_use"!==t?.code||await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]);const i=this._serverCfg;if(this._spaceDialog&&i){const t=Hg(i,this._layout||{},e.spaceId||""),s=1===i.spaces.length&&i.spaces[0]?.id===e.spaceId;this._spaceDialog={...this._spaceDialog,busy:!1,deleteBlockers:s?0:t.count}}this._showToast(this._t("toast.delete_failed",{err:this._errText(t)}))}}}else this._spaceDialog={...e,deleteBlockers:s.count}}async _saveConfigNow(){this._cfgEpoch++;try{await this._writeConfig()}catch(e){throw e?.physicalGeometryRolledBack?await this._reloadRejectedPhysicalWrite():"conflict"===e?.code&&await this._reloadConfigOnly(),e}}_startImport(){const e=this._importDialog;if(!e)return;const t=e.floors.filter(e=>e.checked).map(e=>e.name);this._importDialog=null,t.length?(this._importQueue=t,this._importTotal=t.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const e=this._importQueue.shift();void 0!==e&&(this._spaceDialog={mode:"create",title:e,planUrl:null,planFile:null,...Xd(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:ns,roomOpacity:os,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:Ph(this._imperial),cellCmInput:Th(Ph(this._imperial),this._imperial),cellCmTouched:!1,busy:!1})}_skipImport(){this._spaceDialog=null,this._importQueue.length?this._openNextImport():this._importTotal>0&&this._model.length&&(this._importTotal=0,this._commitSpace(this._serverCfg.spaces[0]?.id||this._space),this._setMode("plan"),this._showToast(this._t("import.done")))}_renderImportDialog(){const e=this._importDialog,t=e.floors.filter(e=>e.checked).length;return q`<hp-dialog .hass=${this.hass} .title=${this._t("import.title")} icon="mdi:home-floor-1"
|
||
@hp-close=${()=>this._importDialog=null}>
|
||
<div class="body">
|
||
<div class="rhint">${this._t("import.hint")}</div>
|
||
${e.floors.map((t,i)=>q`<label class="floorrow">
|
||
<input type="checkbox" .checked=${t.checked}
|
||
@change=${s=>{const n=[...e.floors];n[i]={...t,checked:s.target.checked},this._importDialog={floors:n}}} />
|
||
<span>${t.name}</span>
|
||
${null!=t.level?q`<span class="floorlvl">L${t.level}</span>`:G}
|
||
</label>`)}
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<button class="btn ghost" @click=${()=>{this._importDialog=null,this._openSpaceDialog("create")}}>
|
||
${this._t("import.manual")}
|
||
</button>
|
||
<span class="spacer"></span>
|
||
<button class="btn on" @click=${()=>this._startImport()} ?disabled=${!t}>
|
||
<ha-icon icon="mdi:import"></ha-icon>${this._t("import.start",{n:t})}
|
||
</button>
|
||
</div>
|
||
</hp-dialog>`}_sunGlobal(){const e=this._settingsDialog;return e?{...this._settings,north_deg:e.northDeg??void 0,bg_mode:e.bgMode,sun_rays:e.sunRays}:this._settings}_sunSpace(){const e=this._spaceDialog,t=this._curSpaceCfg?.settings||{};return e&&"edit"===e.mode&&e.spaceId===this._space?{...t,north_deg:e.northDeg??void 0,bg_mode:e.bgMode??void 0,sun_rays:e.sunRays??void 0}:t}_effNorth(){return zr(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Fr(this._sunGlobal(),this._sunSpace())}_dayCycleState(e=new Date){return(this._modeTransitionVisual?.viewWeight??("view"===this._mode?1:0))<=0||"daynight"!==this._effBgMode()?null:$r(this._renderPlanHass,e)}_syncDayCycleClock(){const e=this._dayCycleState();this._dayCycleClockKey=e?Sr(e):"";const t="clock"===e?.source&&"hidden"!==this.ownerDocument.visibilityState&&this.isConnected;t&&!this._dayCycleTimer?this._dayCycleTimer=window.setInterval(this._dayCycleTick,3e4):!t&&this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_dayCycleVisibility(e){"hidden"!==e.kind?(this._dayCycleTick(),this._syncDayCycleClock()):this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_effSunRays(){return Nr(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Er(this._renderPlanHass):null}_renderSunRays(e){const t=W``,i=this._modeTransitionVisual?.viewWeight??0;if(this._editing&&i<=0||!this._effSunRays())return this._sunFadeReset(),t;const s=this._effNorth(),n=null!==s?Er(this._renderPlanHass):null;if(!n||n.elevation<=0)return this._sunFadeReset(),t;if(o=n.elevation,Number(o)>=3)this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunOut=!1,this._sunShown=!0;else{if(!this._sunShown)return t;this._sunOut||(this._sunOut=!0,this._sunOutTimer=window.setTimeout(()=>{this._sunOutTimer=0,this._sunShown=!1,this._sunOut=!1,this.requestUpdate()},2e3))}var o;const a=this._zeroWalls(),r=a.barriers.map(e=>e.join(",")).join(";"),l=`${e.id}|${n.azimuth}|${n.elevation}|${s}|${this._cfgEpoch}|${a.style}|${r}`;if(!this._sunRaysCache||this._sunRaysCache.key!==l){const t=e.rooms.map(e=>({id:e.id||"",poly:hi(e)})).filter(e=>!!e.id&&!!e.poly),i=this._openingsR.filter(e=>"window"===e.type&&"partition"!==e.host?.kind).map(e=>({id:e.id,x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=this._spaceWalls,r=this._openCuts(),c=this._openingWallIndexFor(e,r).value,h={},d={},p=this._wallUnionGeometry()?.roomGeom;if(o.length){for(const i of t){const t=this._innerRoomContour(e,i.id,r,p);t&&(h[i.id]=t)}for(const e of i){const t=Zc(c,{x:e.x,y:e.y,angle:e.angle,length:e.length});t.cm>0&&(d[e.id]=Ml(t.cm,this._cellCm,this._gridPitch))}}let u=Rr(t,i,n.azimuth,n.elevation,s,o.length?h:void 0,o.length?d:void 0);const _=[...this._physicalBodiesR(e),...a.barriers.map(e=>[[e[0],e[1]],[e[2],e[3]]])];_.length&&(u=u.map(e=>{const t=function(e,t,i){return i>0?e.map(e=>function(e){const t=[...e].sort((e,t)=>e[0]-t[0]||e[1]-t[1]);if(t.length<=2)return t;const i=(e,t,i)=>(t[0]-e[0])*(i[1]-e[1])-(t[1]-e[1])*(i[0]-e[0]),s=[];for(const e of t){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],e)<=0;)s.pop();s.push(e)}const n=[];for(let e=t.length-1;e>=0;e--){const s=t[e];for(;n.length>=2&&i(n[n.length-2],n[n.length-1],s)<=0;)n.pop();n.push(s)}return s.slice(0,-1).concat(n.slice(0,-1))}([...e,...e.map(e=>[e[0]+t[0]*i,e[1]+t[1]*i])])).filter(e=>e.length>=3):e}(_,e.dir,e.len),i=e.polys.map(e=>bh(e,t));return{...e,paths:i.map(ch).filter(Boolean),polys:i.flatMap(kh)}}).filter(e=>e.paths?.length||e.polys.length)),this._sunRaysCache={key:l,rays:u,rims:u.map(e=>function(e,t=1e-4){const[i,s]=e.dir,n=-s,o=i,a=[];for(const r of[e.a,e.b]){const l=[];for(const a of e.polys)for(let e=0;e<a.length;e++){const c=a[e],h=a[(e+1)%a.length];if(Math.abs((c[0]-r[0])*n+(c[1]-r[1])*o)>t)continue;if(Math.abs((h[0]-r[0])*n+(h[1]-r[1])*o)>t)continue;const d=(c[0]-r[0])*i+(c[1]-r[1])*s,p=(h[0]-r[0])*i+(h[1]-r[1])*s;Math.abs(p-d)<=t||l.push(d<p?[d,p]:[p,d])}l.sort((e,t)=>e[0]-t[0]);const c=[];for(const e of l){const i=c[c.length-1];i&&e[0]<=i[1]+t?i[1]=Math.max(i[1],e[1]):c.push([e[0],e[1]])}for(const[e,t]of c)a.push([[r[0]+i*e,r[1]+s*e],[r[0]+i*t,r[1]+s*t]])}return a}(e))}}const c=this._sunRaysCache.rays,h=this._sunRaysCache.rims;if(!c.length)return t;const d=(p=function(e){const t=Math.min(90,Math.max(-90,Number(e)||0));let i=Mr[Mr.length-1][1];for(let e=1;e<Mr.length;e++){const[s,n]=Mr[e-1],[o,a]=Mr[e];if(t<=o){i=us(n,a,(t-s)/(o-s));break}}return{bg:i,planDim:.1*Cr((10-t)/16),warmth:t<0?1:Cr(1-t/10)}}(n.elevation).warmth,us("#ffe9c2","#ff9a45",Cr(p)));var p;const u=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],_=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]];return W`<defs>
|
||
${c.map((e,t)=>{const i=(e.a[0]+e.b[0])/2,s=(e.a[1]+e.b[1])/2,n=i+e.normal[0]*e.depth,o=s+e.normal[1]*e.depth;return W`<linearGradient id="hp-sun-${t}" gradientUnits="userSpaceOnUse"
|
||
x1="${i}" y1="${s}" x2="${n}" y2="${o}">
|
||
${u.map(([e,t])=>W`<stop offset="${(100*e).toFixed(1)}%"
|
||
stop-color="${d}" stop-opacity="${(.3*t).toFixed(4)}"></stop>`)}
|
||
</linearGradient>
|
||
<linearGradient id="hp-sunrim-${t}" gradientUnits="userSpaceOnUse"
|
||
x1="${i}" y1="${s}" x2="${n}" y2="${o}">
|
||
${_.map(([e,t])=>W`<stop offset="${(100*e).toFixed(1)}%"
|
||
stop-color="${"#000000"}" stop-opacity="${(.42*t).toFixed(4)}"></stop>`)}
|
||
</linearGradient>`})}
|
||
</defs>
|
||
<g class="sunlayer hp-view-only-layer ${this._sunOut?"out":""}"
|
||
opacity="${this._modeTransitionVisual?.viewWeight??1}">
|
||
${c.map((e,t)=>e.paths?.length?e.paths.map(e=>W`<path d=${e} fill-rule="evenodd" fill="url(#hp-sun-${t})"></path>`):e.polys.map(e=>W`<polygon
|
||
points="${e.map(e=>e[0]+","+e[1]).join(" ")}" fill="url(#hp-sun-${t})"></polygon>`))}
|
||
${c.map((e,t)=>(h[t]||[]).map(e=>W`<line class="sunrim"
|
||
x1="${e[0][0]}" y1="${e[0][1]}" x2="${e[1][0]}" y2="${e[1][1]}"
|
||
stroke="url(#hp-sunrim-${t})" stroke-width="1"
|
||
vector-effect="non-scaling-stroke"></line>`))}
|
||
</g>`}_sunFadeReset(){this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunShown=!1,this._sunOut=!1}_compassPoint(e){const t=e.currentTarget.getBoundingClientRect(),i=e.clientX-(t.left+t.width/2),s=e.clientY-(t.top+t.height/2);if(Math.hypot(i,s)<5)return;let n=Math.round(180*Math.atan2(i,-s)/Math.PI);e.shiftKey&&(n=15*Math.round(n/15)),n=(n%360+360)%360,this._settingsDialog={...this._settingsDialog,northDeg:n}}_renderCompass(){const e=this._settingsDialog.northDeg;return q`<svg class="compass ${null===e?"unset":""}" viewBox="-60 -60 120 120"
|
||
@pointerdown=${e=>{e.currentTarget.setPointerCapture(e.pointerId),this._compassDrag=!0,this._compassPoint(e)}}
|
||
@pointermove=${e=>{this._compassDrag&&this._compassPoint(e)}}
|
||
@pointerup=${()=>this._compassDrag=!1}
|
||
@pointercancel=${()=>this._compassDrag=!1}>
|
||
<circle class="cring" r="50"></circle>
|
||
${[0,45,90,135,180,225,270,315].map(e=>W`<line class="ctick ${e%90?"minor":""}" x1="0" y1="-50" x2="0" y2="${e%90?-46:-43}"
|
||
transform="rotate(${e})"></line>`)}
|
||
<g class="cneedle" transform="rotate(${e??0})">
|
||
<line x1="0" y1="34" x2="0" y2="-28"></line>
|
||
<path d="M -7 -24 L 0 -42 L 7 -24 Z"></path>
|
||
<text x="0" y="-12" text-anchor="middle">${this._t("gs.north_letter")}</text>
|
||
</g>
|
||
<text class="cdeg" x="0" y="26" text-anchor="middle">${null===e?"—":e+"°"}</text>
|
||
</svg>`}_stageBg(e){const t=this._settingsDialog,i=this._spaceDialog,s=t?t.bgColor||"":hs(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":e.bgColor||"")||s}_stageBgHex(){const e=this._stageEl;if(e){const t=getComputedStyle(e).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(t)return"#"+t.slice(1,4).map(e=>(+e).toString(16).padStart(2,"0")).join("")}return"#111111"}_preflightDiagnostics(e,t){const i=new Map((t?.spaces||[]).map(e=>[String(e?.id||""),e]));return{kind:"houseplan-optimize-preflight",origin:"runtime",cardVersion:_v,checkedAt:(new Date).toISOString(),preflightFingerprint:e.fingerprint,failures:e.failures.map(e=>({spaceId:e.spaceId,displayName:e.displayName,reason:e.reason,detail:e.detail??null,spaceGeometryFingerprint:i.has(e.spaceId)?Bg(i.get(e.spaceId)):null}))}}_reportPreflightFailure(e,t){e.ok||e.fingerprint===this._reportedPreflightFingerprint||(this._reportedPreflightFingerprint=e.fingerprint,console.warn("[houseplan] optimize preflight failed",this._preflightDiagnostics(e,t)))}_preflightVersionsDiffer(){const e=this._haIntegrationVersion;return"string"==typeof e&&e.length>0&&e!==_v}async _copyPreflightDiagnostics(){const e=this._alignDialog?.preflight;if(!e||e.ok)return;const t=JSON.stringify(this._preflightDiagnostics(e,this._alignDialog?.config??null),null,2);try{await navigator.clipboard.writeText(t),this._preflightClipboardFallback=null,this._showToast(this._t("gs.preflight_copied"))}catch{this._preflightClipboardFallback=t}}_checkOptimizeGeometry(e){return Vg(e,{fallbackSpaceName:e=>this._t("gs.align_preflight_space",{n:String(e)})})}_checkSpacePhysicalGeometry(e,t,i){return function(e,t,i={}){const s=(Array.isArray(e?.spaces)?e.spaces:[]).find(e=>String(e?.id||"")===String(t||"")),n=Bg(s);if(!s)return{spaceId:String(t||""),displayName:"",status:"failed",reason:"prepare-exception",fingerprint:n,ok:!1};const o=Vg({...e,spaces:[s]},{...i,fingerprint:()=>n}).spaces[0]||{spaceId:String(t||""),displayName:"",status:"failed",reason:"prepare-exception"};return{...o,fingerprint:n,ok:"failed"!==o.status}}(e,t,{fallbackSpaceName:e=>this._t("gs.align_preflight_space",{n:String(e)}),captureWallGeometry:i?(e,t)=>i(t):void 0})}_optimizeReferenceContext(e){const t=this._haRegistry,i=this._fullRegistryHass,s={},n=(...e)=>{for(const t of e){const e=String(t||"").trim();if(e)return e}return""};for(const[e,i]of Object.entries(t.devices||{}))s[e]=n(i?.name_by_user,i?.name,i?.model);for(const[e,i]of Object.entries(t.entities||{}))s[`lg_${e}`]=n(this.hass?.states?.[e]?.attributes?.friendly_name,i?.name,i?.original_name);for(const e of this._devices)s[e.id]=n(e.name,s[e.id]);for(const e of this._serverCfg?.markers||[]){const t=String(e.binding||"").indexOf(":"),o=t>0?e.binding.slice(0,t):"",a=t>0?e.binding.slice(t+1):"",r="device"===o?i?.devices?.[a]:"entity"===o?i?.entities?.[a]:null;s[e.id]=n(e.name,"device"===o?r?.name_by_user:null,r?.name,"entity"===o?this.hass?.states?.[a]?.attributes?.friendly_name:null,s[e.id])}return{effectiveAreaByMarker:Object.fromEntries(this._devices.filter(e=>!e.virtual&&!!e.area).map(e=>[e.id,e.area])),ownerRoster:{authoritative:t.authoritative,deviceIds:Object.keys(t.devices||{}),entityIds:[...new Set([...Object.keys(t.entities||{}),...Object.keys(this.hass?.states||{})])],names:s},removeLiveMissingPositions:e}}_previewAlignDialog(e){if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[];let i;try{i=Og(this._serverCfg,this._layout||{},this._optimizeReferenceContext(e))}catch(e){return void this._showWallModelMigrationBlocked(e)}const s=i.changed?this._checkOptimizeGeometry(i.config):null;s&&this._reportPreflightFailure(s,i.config);const n=Math.ceil(10*i.report.maxShiftCm)/10,o=t.find(e=>null!=e?.id&&String(e.id)===i.report.maxSpace),a=t.length>1&&o?String(o.title||o.id):"";this._preflightClipboardFallback=null,this._alignDialog={report:i.report,config:i.config,layout:i.layout,cm:n,where:a,preflight:s,changed:i.changed,busy:!1,removeLiveMissingPositions:e}}async _runAlignToGrid(){let e=this._alignDialog;if(!e||e.busy||!this._serverCfg||!e.changed||!e.preflight?.ok)return;const t=Gd(e.config);if(e.preflight.fingerprint!==t){const t=this._checkOptimizeGeometry(e.config);if(this._reportPreflightFailure(t,e.config),e={...e,preflight:t},this._alignDialog=e,!t.ok)return}this._clearGeometryGesture(),this._alignDialog={...e,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain;const t=await this.hass.callWS({type:"houseplan/plan/optimize",config:e.config,layout:e.layout,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});this._serverCfg=e.config,this._layout=e.layout,this._geometryHistory.clear(),this._cfgRev=t?.config_rev??this._cfgRev+1,this._layoutRev=t?.layout_rev??this._layoutRev+1,this._canOptimizeUndo=!!t?.can_undo,this._undoKind=t?.can_undo?"optimize":null,this._dirtyPos.clear(),this._sentPos.clear(),this._cfgEpoch++,this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._cacheSnapshot(),this._alignDialog=null,this._preflightClipboardFallback=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(e.report.moved),m:String(e.report.migrated+e.report.canonicalized+e.report.coordsCanonicalized+e.report.latticeCoordinatesCanonicalized+e.report.wallsMerged+e.report.spansMerged+e.report.partitionsMerged+e.report.partitionsReconciled+e.report.openingsRehosted+e.report.wallsStraightened),r:String(e.report.spaceRefsRemapped+e.report.roomRefsRemapped+e.report.positionsRemapped+e.report.markersDetached+e.report.orphanRoomLabelsRemoved+e.report.orphanDevicePositionsRemoved+e.report.orphanGroupPositionsRemoved)}))}catch(e){if(this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),"wall_model_client_outdated"===e?.code)return void this._showToast(this._t("toast.wall_model_client_outdated"));"conflict"===e?.code&&await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}async _undoPlanOptimization(){if(!this._canOptimizeUndo||this._optimizeUndoBusy)return;const e=this._undoKind;this._clearGeometryGesture(),this._optimizeUndoBusy=!0,this.requestUpdate();try{await this.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});const[t,i]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._serverCfg=t?.config||this._serverCfg,this._cfgRev=t?.rev??this._cfgRev,this._layout=i?.layout||this._layout,this._geometryHistory.clear(),this._layoutRev=i?.rev??this._layoutRev,this._canOptimizeUndo=!1,this._undoKind=null,this._cfgEpoch++,this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._cacheSnapshot(),this.requestUpdate(),this._showToast(this._t("import"===e?"backup.import_undone":"gs.optimize_undone"))}catch(e){this._canOptimizeUndo=!1,this._undoKind=null,this._showToast(this._t("toast.error",{err:this._errText(e)}))}finally{this._optimizeUndoBusy=!1,this.requestUpdate()}}async _runBackupExport(){const e=this._backupExportDialog;if(e&&!e.busy){this._backupExportDialog={...e,busy:!0,error:""};try{const t=await this.hass.callWS({type:"houseplan/export/create",kind:e.kind,space_id:"space"===e.kind?this._space:void 0,..."space"===e.kind&&e.planOnly?{plan_only:!0}:{},card_version:_v}),i=new Blob([JSON.stringify(t.document,null,2)+"\n"],{type:"application/json;charset=utf-8"}),s=URL.createObjectURL(i),n=document.createElement("a");n.href=s,n.download=t.filename||`houseplan-${e.kind}.json`,n.style.display="none",document.body.append(n),n.click(),n.remove(),window.setTimeout(()=>URL.revokeObjectURL(s),0),this._backupExportDialog=null,this._showToast(this._t("backup.export_done"))}catch(e){this._backupExportDialog&&(this._backupExportDialog={...this._backupExportDialog,busy:!1,error:this._backupErrorText(e)})}}}async _pickBackupImport(e){const t=e.target,i=t.files?.[0];if(t.value="",i){this._settingsDialog=null,this._backupImportDialog={filename:i.name,size:i.size,token:"",preview:null,expectedConfigRev:this._cfgRev,expectedLayoutRev:this._layoutRev,duplicatePolicy:"skip",confirmMissing:!1,busy:!0,error:""};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain,this._persistLayout.pending()&&this._persistLayout.flush();const e="/api/houseplan/import/preview?duplicate_policy=skip",t={method:"POST",body:i,headers:{"content-type":"application/json"}},s=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth(e,t):await fetch(e,{...t,headers:{...t.headers,...this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}}),n=await s.json().catch(()=>({}));if(!s.ok||n.error)throw Object.assign(new Error(n.message||n.error||`HTTP ${s.status}`),{code:n.error});this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,token:n.token,preview:n.preview,expectedConfigRev:n.expected_config_rev,expectedLayoutRev:n.expected_layout_rev,busy:!1})}catch(e){this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,busy:!1,error:this._backupErrorText(e)})}}}async _setBackupDuplicatePolicy(e){const t=this._backupImportDialog;if(t&&!t.busy&&t.token&&e!==t.duplicatePolicy){this._backupImportDialog={...t,duplicatePolicy:e,busy:!0,error:""};try{const i=await this.hass.callWS({type:"houseplan/import/revalidate",token:t.token,duplicate_policy:e});this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,preview:{...t.preview,...i.preview},expectedConfigRev:i.expected_config_rev,expectedLayoutRev:i.expected_layout_rev,confirmMissing:!1,busy:!1})}catch(e){this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,duplicatePolicy:t.duplicatePolicy,preview:t.preview,confirmMissing:t.confirmMissing,busy:!1,error:this._backupErrorText(e)})}}}async _applyBackupImport(){const e=this._backupImportDialog;if(!e||e.busy||!e.token||!e.preview)return;if(e.preview.confirmation_required&&!e.confirmMissing)return;this._backupImportDialog={...e,busy:!0,error:""};const t=this._space;try{const i=await this.hass.callWS({type:"houseplan/import/apply",token:e.token,expected_config_rev:e.expectedConfigRev,expected_layout_rev:e.expectedLayoutRev,duplicate_policy:e.duplicatePolicy,confirm_missing_content:e.confirmMissing}),[s,n]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._adoptStructuralResponses(s,n),this._geometryHistory.clear(),this._dirtyPos.clear(),this._sentPos.clear(),this._defPos={},this._cfgEpoch++,this._modelCache=null,this._frame=null,this._visibleDeviceSnapshot=null,this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._capturedSnapshotConfigEpoch=-1,this._regSignature="",this._signer.invalidate(this.hass),this._resign(),this._maybeRebuildDevices();const o=this._serverCfg?.spaces||[],a="space"===i.kind&&i.space_id?i.space_id:o.some(e=>e.id===t)?t:o[0]?.id||this._space;this._hasFixedFloor?this._adoptInitialSpace(this._model,!0):this._commitSpace(a),this._backupImportDialog=null,this._cacheSnapshot(),this.requestUpdate();const r="space"===i.kind?e.preview.counts:i.counts;this._showToast(this._t("space"===i.kind?"backup.space_done":"backup.full_done",{spaces:String(r?.spaces||0),rooms:String(r?.rooms||0),markers:String(r?.markers||0),refs:String(i.repaired_target_refs||0)}))}catch(e){if("conflict"===e?.code&&this._backupImportDialog?.token)try{const e=this._backupImportDialog,t=await this.hass.callWS({type:"houseplan/import/revalidate",token:e.token,duplicate_policy:e.duplicatePolicy});return void(this._backupImportDialog={...e,preview:{...e.preview,...t.preview},expectedConfigRev:t.expected_config_rev,expectedLayoutRev:t.expected_layout_rev,confirmMissing:!1,busy:!1,error:this._t("backup.revalidated")})}catch(t){e=t}this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,busy:!1,error:this._backupErrorText(e)})}}_renderBackupExportDialog(){const e=this._backupExportDialog,t=(this._serverCfg?.spaces||[]).find(e=>e.id===this._space);return q`<hp-dialog .hass=${this.hass} .title=${this._t("backup.export_title")}
|
||
icon="mdi:download" dismiss-on-scrim @hp-close=${()=>this._backupExportDialog=null}>
|
||
<div class="body backupbody">
|
||
<div class="rhint">${this._t("backup.export_hint")}</div>
|
||
<label class="srcrow"><input type="radio" name="backup-kind" value="full"
|
||
.checked=${"full"===e.kind}
|
||
@change=${()=>this._backupExportDialog={...e,kind:"full",planOnly:!1}} />
|
||
<span>${this._t("backup.full")}</span></label>
|
||
<label class="srcrow"><input type="radio" name="backup-kind" value="space"
|
||
.checked=${"space"===e.kind} ?disabled=${!t}
|
||
@change=${()=>this._backupExportDialog={...e,kind:"space"}} />
|
||
<span>${t?this._t("backup.current_space_title",{title:t.title||t.id}):this._t("backup.no_current_space")}</span></label>
|
||
${"space"===e.kind&&t?q`<label class="srcrow backupplanonly">
|
||
<input type="checkbox" .checked=${e.planOnly}
|
||
@change=${t=>this._backupExportDialog={...e,planOnly:t.target.checked}} />
|
||
<span><b>${this._t("backup.plan_only")}</b><small>${this._t("backup.plan_only_hint")}</small></span>
|
||
</label>`:G}
|
||
<div class="backupwarn">${this._t("backup.privacy_warning")}</div>
|
||
${e.error?q`<div class="backuperror" role="alert">${e.error}</div>`:G}
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<button class="btn ghost" autofocus @click=${()=>this._backupExportDialog=null}>${this._t("btn.cancel")}</button>
|
||
<span class="spacer"></span>
|
||
<button class="btn on" ?disabled=${e.busy||"space"===e.kind&&!t}
|
||
@click=${this._runBackupExport}>
|
||
<ha-icon icon="mdi:download"></ha-icon>${e.busy?"…":this._t("backup.download")}
|
||
</button>
|
||
</div>
|
||
</hp-dialog>`}_renderBackupImportDialog(){const e=this._backupImportDialog,t=e.preview,i=t?.counts||{},s=t?.reference_report||{},n=e=>Object.values(e||{}).reduce((e,t)=>e+(Number(t)||0),0),o=[["incoming_remapped",n(s.remapped?.incoming)],["target_repaired",n(s.remapped?.target)],["preserved_unresolved",n(s.preservedUnresolved)],["collisions",n(s.collisions)],["dropped_links",n(s.droppedIncomingLinks)],["bounded_lineages",Number(s.boundedLineages)||0]].filter(([,e])=>e>0);return q`<hp-dialog .hass=${this.hass} .title=${this._t("backup.import_title")}
|
||
icon="mdi:upload" wide dismiss-on-scrim @hp-close=${()=>this._backupImportDialog=null}>
|
||
<div class="body backupbody" aria-busy=${e.busy?"true":"false"}>
|
||
<div class="backupfile"><b>${e.filename}</b><span>${(e.size/1024).toFixed(1)} KB</span></div>
|
||
${e.busy&&!t?q`<div class="rhint" role="status" aria-live="polite">${this._t("backup.reading")}</div>`:G}
|
||
${e.error?q`<div class="backuperror" role="alert">${e.error}</div>`:G}
|
||
${t?q`
|
||
<div class="backupsummary">
|
||
<b>${this._t("full"===t.kind?"backup.full":"backup.current_space")}</b>
|
||
${t.plan_only?q`<span class="backupplanonlystatus">${this._t("backup.plan_only_preview")}</span>`:G}
|
||
<span>${this._t("same"===t.source?"backup.same_source":"backup.foreign_source")}</span>
|
||
<span>${this._t("backup.created",{value:t.created_at||"—"})}</span>
|
||
<span>${this._t("backup.versions",{card:t.card_version||"—",integration:t.integration_version||"—",model:String(t.model_version??"—")})}</span>
|
||
</div>
|
||
<div class="backupcounts">
|
||
<span>${this._t("backup.count_spaces",{n:"full"===t.kind?`${t.current_counts?.spaces||0} → ${i.spaces||0}`:String(i.spaces||0)})}</span>
|
||
<span>${this._t("backup.count_rooms",{n:"full"===t.kind?`${t.current_counts?.rooms||0} → ${i.rooms||0}`:String(i.rooms||0)})}</span>
|
||
<span>${this._t("backup.count_walls",{n:"full"===t.kind?`${t.current_counts?.walls||0} → ${i.walls||0}`:String(i.walls||0)})}</span>
|
||
<span>${this._t("backup.count_openings",{n:"full"===t.kind?`${t.current_counts?.openings||0} → ${i.openings||0}`:String(i.openings||0)})}</span>
|
||
<span>${this._t("backup.count_decor",{n:"full"===t.kind?`${t.current_counts?.decor||0} → ${i.decor||0}`:String(i.decor||0)})}</span>
|
||
<span>${this._t("backup.count_markers",{n:"full"===t.kind?`${t.current_counts?.markers||0} → ${i.markers||0}`:String(i.markers||0)})}</span>
|
||
<span>${this._t("backup.count_layout",{n:"full"===t.kind?`${t.current_counts?.layout||0} → ${i.layout||0}`:String(i.layout||0)})}</span>
|
||
</div>
|
||
${t.bindings?q`<div class="rhint">${this._t("backup.bindings",{device:String(t.bindings.device||0),entity:String(t.bindings.entity||0),virtual:String(t.bindings.virtual||0),legacy:String(t.legacy_positions||0)})}</div><div class="rhint">${this._t("backup.binding_status",{active:String(t.bindings.active||0),disabled:String(t.bindings.disabled||0),missing:String(t.bindings.missing||0)})}</div>`:G}
|
||
${t.missing_areas?.length?q`<div class="backupwarn">${this._t("backup.missing_areas",{areas:t.missing_areas.join(", ")})}</div>`:G}
|
||
${t.dropped_marker_links?q`<div class="backupwarn">${this._t("backup.dropped_marker_links",{n:String(t.dropped_marker_links)})}</div>`:G}
|
||
${t.repaired_target_refs?q`<div class="rhint">${this._t("backup.repaired_target_refs",{n:String(t.repaired_target_refs)})}</div>`:G}
|
||
${t.preserved_unresolved_refs?q`
|
||
<div class="backupwarn">${this._t("backup.preserved_unresolved_refs",{n:String(t.preserved_unresolved_refs)})}<br />${this._t("backup.preserved_unresolved_hint")}</div>
|
||
`:G}
|
||
${o.length?q`<details class="backupdetails">
|
||
<summary>${this._t("backup.import_details")}</summary>
|
||
<div>
|
||
${o.map(([e,t])=>q`<span>${this._t(`backup.import_detail.${e}`,{n:String(t)})}</span>`)}
|
||
${(s.examples||[]).slice(0,8).map(e=>q`
|
||
<code>${e.owner} → ${e.reference}</code>
|
||
`)}
|
||
</div>
|
||
</details>`:G}
|
||
${"full"===t.kind?q`
|
||
<div class="backupwarn">${this._t("backup.replace_warning")}</div>
|
||
${"foreign"===t.source?q`<div class="rhint">${this._t("backup.foreign_bookkeeping")}</div>`:G}`:q`
|
||
<div class="backupsummary"><b>${this._t("backup.final_name")}</b><span>${t.space_title}</span></div>
|
||
<div class="rhint">${this._t("backup.target_settings")}</div>
|
||
${t.duplicates?q`<fieldset class="backupchoices"><legend>${this._t("backup.duplicates",{n:String(t.duplicates)})}</legend>
|
||
<label><input type="radio" name="duplicate-policy" .checked=${"skip"===e.duplicatePolicy}
|
||
@change=${()=>this._setBackupDuplicatePolicy("skip")} />${this._t("backup.skip")}</label>
|
||
<label><input type="radio" name="duplicate-policy" .checked=${"virtual"===e.duplicatePolicy}
|
||
@change=${()=>this._setBackupDuplicatePolicy("virtual")} />${this._t("backup.virtual_copy")}</label>
|
||
</fieldset>`:G}`}
|
||
${t.content?.length?q`<div class="backupcontent">
|
||
<b>${this._t("backup.content")}</b>
|
||
${t.content.map(e=>q`<span>${e.url} · ${this._t("available"===e.state?"backup.content_available":"external"===e.state?"backup.content_external":"backup.content_detach_required")}</span>`)}
|
||
</div>`:G}
|
||
${t.confirmation_required?q`<label class="srcrow backupconfirm">
|
||
<input type="checkbox" .checked=${e.confirmMissing}
|
||
@change=${t=>this._backupImportDialog={...e,confirmMissing:t.target.checked}} />
|
||
<span>${this._t("backup.confirm_detach")}</span>
|
||
</label>`:G}
|
||
`:G}
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<button class="btn ghost" autofocus @click=${()=>this._backupImportDialog=null}>${this._t("btn.cancel")}</button>
|
||
<span class="spacer"></span>
|
||
${t?q`<button class="btn ${"full"===t.kind?"danger":"on"}"
|
||
?disabled=${e.busy||t.confirmation_required&&!e.confirmMissing} @click=${this._applyBackupImport}>
|
||
<ha-icon icon=${"full"===t.kind?"mdi:database-import":"mdi:plus"}></ha-icon>
|
||
${e.busy?"…":this._t("full"===t.kind?"backup.replace":"backup.add")}
|
||
</button>`:G}
|
||
</div>
|
||
</hp-dialog>`}_setFillColor(e,t){const i=this._settingsDialog;this._settingsDialog={...i,colors:{...i.colors,[e]:{...i.colors[e],...t}}}}async _saveSettingsDialog(){const e=this._settingsDialog;if(e&&!e.busy){this._settingsDialog={...e,busy:!0};try{const t=this._serverCfg,i=JSON.stringify(e.colors)===JSON.stringify(ds),s={...t.settings};i?delete s.fill_colors:s.fill_colors=e.colors;const n=this._imperial?30.48*e.glowRadius:100*e.glowRadius;Number.isFinite(n)&&n>0&&300!==Math.round(n)?s.glow_radius_cm=Math.round(n):delete s.glow_radius_cm,e.bgColor?s.bg_color=e.bgColor:delete s.bg_color,null!==e.northDeg&&Number.isInteger(e.northDeg)&&e.northDeg>=0&&e.northDeg<=359?s.north_deg=e.northDeg:delete s.north_deg,s.bg_mode=e.bgMode,e.sunRays?s.sun_rays=!0:delete s.sun_rays,delete s.weather_entity,this._serverCfg={...t,settings:s},await this._saveConfigNow(),this._settingsDialog=null,this.requestUpdate(),this._showToast(this._t("gs.saved"))}catch(e){this._settingsDialog&&(this._settingsDialog={...this._settingsDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}}_boolInput(e,t,i=!1){const s=e=>t(!!e.target.checked);return customElements.get("ha-switch")?q`<ha-switch .checked=${e} .disabled=${i} @change=${s}></ha-switch>`:q`<input type="checkbox" .checked=${e} ?disabled=${i} @change=${s} />`}_rangeInput(e,t,i,s,n,o=!1,a){const r=e=>{const t=Number(e.target.value);Number.isFinite(t)&&n(t)};return customElements.get("ha-slider")?q`<ha-slider .min=${e} .max=${t} .step=${i} .value=${s}
|
||
.disabled=${o} aria-label=${a||G} @input=${r} @change=${r}></ha-slider>`:q`<input type="range" min=${e} max=${t} step=${i} .value=${String(s)}
|
||
?disabled=${o} aria-label=${a||G} @input=${r} />`}_renderColorRow(e,t){const i=this._settingsDialog.colors[e];return q`<div class="colorrow gsrow">
|
||
<hp-color-opacity .label=${this._t(t)}
|
||
.opacityLabel=${this._t("space.opacity")}
|
||
.pickerLabels=${this._colorPickerLabels}
|
||
.color=${i.c} .opacity=${i.a} .showOpacity=${!0}
|
||
@hp-color-opacity-change=${t=>{this._setFillColor(e,{c:t.detail.color,a:t.detail.opacity})}}></hp-color-opacity>
|
||
</div>`}get _glowRadiusCm(){const e=Number(this._settings.glow_radius_cm);return Number.isFinite(e)&&e>0?e:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const e=this._glowRadiusCm;return this._imperial?String(Math.round(e/30.48*10)/10):String(e/100)}_glowTransition(e,t){let i=this._glowRenderedSources.get(e);if(t){const t=this._glowFadeTimers.get(e);if(null!=t&&(clearTimeout(t),this._glowFadeTimers.delete(e)),null==i){this._suspendGlowFeatherForTransition(),i=++this._glowSourceSeq,this._glowRenderedSources.set(e,i),this._glowEnteringSources.add(e);const t=requestAnimationFrame(()=>{this._glowEnterRafs.get(e)===t&&(this._glowEnterRafs.delete(e),this._glowEnteringSources.delete(e),this.isConnected&&this.requestUpdate())});this._glowEnterRafs.set(e,t)}return{domId:i,entering:this._glowEnteringSources.has(e),leaving:!1}}if(null==i)return null;const s=this._glowEnterRafs.get(e);if(null!=s&&cancelAnimationFrame(s),this._glowEnterRafs.delete(e),this._glowEnteringSources.delete(e),!this._glowFadeTimers.has(e)){this._suspendGlowFeatherForTransition();const t=window.setTimeout(()=>{this._glowFadeTimers.get(e)===t&&(this._glowFadeTimers.delete(e),this._glowRenderedSources.delete(e),this._glowLastAppearance.delete(e),this.isConnected&&this.requestUpdate())},534);this._glowFadeTimers.set(e,t)}return{domId:i,entering:!1,leaving:!0}}_suspendGlowFeatherForTransition(){if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches)return;this._glowFeatherSuspendUntil=Math.max(this._glowFeatherSuspendUntil,Date.now()+500),clearTimeout(this._glowFeatherResumeTimer);const e=Math.max(0,this._glowFeatherSuspendUntil-Date.now())+17,t=()=>{this._glowFeatherResumeTimer=0,Date.now()<this._glowFeatherSuspendUntil?this._glowFeatherResumeTimer=window.setTimeout(t,this._glowFeatherSuspendUntil-Date.now()+17):(this._glowFeatherSuspendUntil=0,this.isConnected&&this.requestUpdate())};this._glowFeatherResumeTimer=window.setTimeout(t,e)}_forgetGlowSource(e){const t=this._glowFadeTimers.get(e);null!=t&&clearTimeout(t);const i=this._glowEnterRafs.get(e);null!=i&&cancelAnimationFrame(i),this._glowFadeTimers.delete(e),this._glowEnterRafs.delete(e),this._glowEnteringSources.delete(e),this._glowRenderedSources.delete(e),this._glowLastAppearance.delete(e)}_forgetGlowSpace(e){const t=`${e}|`;for(const e of this._glowRenderedSources.keys())e.startsWith(t)&&this._forgetGlowSource(e)}_warnGlowGeometryFallback(e,t,i,s){const n=`${e}|${t}|${i}`;if(!this._glowGeometryWarnings.has(n)){if(this._glowGeometryWarnings.size>=128){const e=this._glowGeometryWarnings.values().next().value;e&&this._glowGeometryWarnings.delete(e)}this._glowGeometryWarnings.add(n),console.warn(`HOUSEPLAN GLOW GEOMETRY FALLBACK: #218, space ${e}, room ${i}, phase ${s}`)}}_lightBarriers(e,t){const i=Gd([this._curSpaceCfg,this._cellCm,this._gridPitch]),s=`${e.id}|${i}`,n=wv(this._lightBarrierPool,s);if(n.hit)return this._lightBarrierCache=n.value,n.value.value;const o=this._zeroWalls(),a=this._openCuts(),r=[...o.transmissive],l=Math.max(this._cmToUnits(10),.5*this._gridPitch),c=e=>t.some(t=>this._pointInRoom(e,t.r)),h=this._openingsR.filter(e=>{if("door"!==(t=String(e.type))&&"gate"!==t&&"passage"!==t)return!1;var t;const i=e.angle*Math.PI/180,s=-Math.sin(i),n=Math.cos(i);return c([e.rx+s*l,e.ry+n*l])&&c([e.rx-s*l,e.ry-n*l])}),d=this._roomWallOpeningInputs(h,e);for(const e of d){const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,s=Math.sin(t)*e.length/2;r.push([e.x-i,e.y-s,e.x+i,e.y+s])}const p=new Set(h.filter(e=>e.partitionHost).map(e=>e.id)),u=[e.id,this._cfgEpoch,this._cellCm,this._gridPitch,[...p].sort().join(",")].join("|");this._lightPhysicalBodiesCache?.key!==u&&(this._lightPhysicalBodiesCache={key:u,all:_h(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e,e=>p.has(e.id))).all});const _=this._lightPhysicalBodiesCache.all,m=this._spaceWalls,g=.02*this._gridPitch,f=[],v=this._wallUnionGeometry(),b=v?.sourceFingerprint,y=v&&b===i?function(e,t=[],i=[],s={}){if("failed-core"===e.status||"not-applicable"===e.status)return null;const n=e.roomComponents?.length?e.roomComponents:Pc(e.roomGeom)?[{id:"room-primary",geom:e.roomGeom}]:[];let o=n[0]?.geom||null;const a=n.slice(1).map(e=>({...e}));try{for(const i of t){if(!(i.length>0&&e.openingIndex))continue;const t=Xc(e.openingIndex,i,!0);if(!t.negative&&!t.positive)continue;const s=i.angle*Math.PI/180,n=Math.cos(s),r=Math.sin(s),l=-r,c=n,h=i.length/2,d=e.openingPadUnits??1.25*Math.max(e.depthUnits,1),p=Dc([[i.x-n*h-l*d,i.y-r*h-c*d],[i.x+n*h-l*d,i.y+r*h-c*d],[i.x+n*h+l*d,i.y+r*h+c*d],[i.x-n*h+l*d,i.y-r*h+c*d]]);o&&(o=oi(o,p));for(const e of a)e.geom=oi(e.geom,p)}const n=[...a];let r=a.length;const l=s.mergeExtra||((e,t)=>e?si(e,t):t);for(let e=0;e<i.length;e++){const t=i[e];if(t.length<3||!t.every(e=>e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]))||Math.abs(Ti(t))<=1e-9){r++;continue}const s=[Dc(t)];try{const t=l(o,s,e);if(!Pc(t))throw new Error("invalid extra union");o=t}catch{r++,Pc(s)&&n.push({id:`policy-extra-${e}`,geom:s})}}n.sort((e,t)=>Lc(e.geom).localeCompare(Lc(t.geom)));const c=o||[];return{status:r?"degraded-extra":"ok",geom:c,components:[...Pc(c)?[{id:"primary",geom:c}]:[],...n.map((e,t)=>({...e,id:`isolated-${t}`}))]}}catch{return null}}(v,d,_):null,w=y||(m.length||_.length?qc(e.rooms,m,a,d,this._wallKeyPitch,this._cellCm,this._gridPitch,zv,_):null);if(!w||"ok"!==w.status&&"degraded-extra"!==w.status)for(const e of _)f.push(...d_(e));else for(const e of w.components)for(const t of wh(e.geom))f.push(...d_(t));for(const{poly:e}of t)for(const t of r.length?Ps(e,r,g):d_(e))f.push(t);for(const e of o.barriers)f.push(e);const k={occluders:h_(f),floor:t.map(e=>e.poly),fingerprint:i,masonryGeometry:!w||"ok"!==w.status&&"degraded-extra"!==w.status?[]:w.components.flatMap(e=>e.geom),opaqueBodies:_},x={key:s,value:k};return kv(this._lightBarrierPool,s,x,8),this._lightBarrierCache=x,k}_renderGlowLayer(e,t){const i=this._fillColors,s=this._glowRadiusCm/this._cellCm*this._gridPitch,n=e.rooms.map(e=>({r:e,poly:hi(e)})).filter(e=>!!e.poly),o=n.filter(({r:e})=>Fs(t.glow,e));if(!o.length)return this._forgetGlowSpace(e.id),W``;const{occluders:a,floor:r,fingerprint:l,masonryGeometry:c,opaqueBodies:h}=this._lightBarriers(e,n),d=ho(this._renderPlanHass,this._renderDevices,null,this._virtualLights).filter(t=>t.device.space===e.id),p=new Map;for(const e of d){if(!e.device.id)continue;const t=p.get(e.device.id)||[];t.push(e),p.set(e.device.id,t)}const u=[],_=new Set;for(const t of this._renderDevices){if(t.space!==e.id)continue;const o=po(p.get(t.id)||[]);if(!o)continue;const d=`${e.id}|${t.id}`;_.add(d);const m=$s(o.passive?{state:o.on?"on":"off",attributes:{}}:this._renderPlanHass.states[o.eid],t.marker?.glow_color,i.glow_light.c),g=Number(t.marker?.glow_radius_cm),f=Number.isFinite(g)&&g>0?g/this._cellCm*this._gridPitch:s,v=this._pos(t);if($h([v.x,v.y],c,h)){this._forgetGlowSource(d);continue}const b=this._glowTransition(d,!!m);if(!b)continue;m&&this._glowLastAppearance.set(d,{c:m.c,alpha:Ss(m.bri,i.glow_light.a)});const y=this._glowLastAppearance.get(d);if(!y)continue;let w=null;const k=`${e.id}|${l}|${v.x.toFixed(4)},${v.y.toFixed(4)}|${f.toFixed(4)}`,x=wv(this._glowClipCache,k);if(x.hit)w=x.value;else{const t=__([v.x,v.y],f,a,96);w={lit:t.length>=3?vh([t],r,{onBoundsFailure:({boundIndex:t,phase:i})=>{const s=n[t]?.r;this._warnGlowGeometryFallback(e.id,l,s?.id||`#${t}`,i)}}):[]},kv(this._glowClipCache,k,w,256)}u.push({key:d,sourceEid:o.eid,domId:b.domId,entering:b.entering,leaving:b.leaving,pos:v,c:y.c,alpha:y.alpha,geometry:w,r:f})}const m=`${e.id}|`;for(const e of this._glowRenderedSources.keys())e.startsWith(m)&&!_.has(e)&&this._forgetGlowSource(e);if(!u.length)return W``;const g=this._spaceWalls,f=o.length===n.length?[]:this._openCuts(),v=this._wallUnionGeometry()?.roomGeom,b=o.length===n.length?null:o.map(({r:t,poly:i})=>{const s=g.length&&t.id&&this._innerRoomContour(e,t.id,f,v)||i,o=this._cleanFloor(t,s,e).path,a=Pi(s,n.filter(e=>e.r!==t).map(e=>e.poly)),r=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z";return[o||r(s),...a.map(r)].join(" ")}),y=this._viewOr(this._baseVb()),w=this._stageEl?.clientWidth&&y.w?this._stageEl.clientWidth/y.w:1,k=1/(w>0?w:1),x=!this._pinchStart&&!this._panStart&&Date.now()>=this._glowFeatherSuspendUntil;(null==this._glowFeatherUnits||x)&&(this._glowFeatherUnits=k);const $=this._glowFeatherUnits??k,S=4*$,M=u.reduce((e,t)=>({x:Math.min(e.x,t.pos.x-t.r-S),y:Math.min(e.y,t.pos.y-t.r-S),maxX:Math.max(e.maxX,t.pos.x+t.r+S),maxY:Math.max(e.maxY,t.pos.y+t.r+S),w:0,h:0}),{x:1/0,y:1/0,maxX:-1/0,maxY:-1/0,w:0,h:0});return M.w=M.maxX-M.x,M.h=M.maxY-M.y,W`<defs>
|
||
${Me(u,e=>e.key,e=>{const t=e.domId,i=e.geometry;return W`
|
||
${""}
|
||
<radialGradient id="hp-glow-${t}" gradientUnits="userSpaceOnUse"
|
||
cx="${e.pos.x}" cy="${e.pos.y}" r="${e.r}">
|
||
${Fv.map(([t,i])=>W`
|
||
<stop offset="${t}%" stop-color="${e.c}"
|
||
stop-opacity="${(e.alpha*i).toFixed(4)}"></stop>`)}
|
||
</radialGradient>
|
||
${""}
|
||
${i?W`
|
||
<clipPath id="hp-glowclip-${t}">
|
||
<path class="glow-lit" d="${i.lit.join(" ")}"
|
||
clip-rule="evenodd" fill-rule="evenodd"></path>
|
||
</clipPath>`:G}`})}
|
||
${b?W`<clipPath id="hp-glow-enabled">${b.map(e=>W`<path d=${e} clip-rule="evenodd" fill-rule="evenodd"></path>`)}</clipPath>`:G}
|
||
${""}
|
||
<filter id="hp-glowfeather" filterUnits="userSpaceOnUse"
|
||
x="${M.x}" y="${M.y}"
|
||
width="${M.w}" height="${M.h}"
|
||
color-interpolation-filters="sRGB">
|
||
<feGaussianBlur stdDeviation="${$.toFixed(4)}" edgeMode="none"></feGaussianBlur>
|
||
</filter>
|
||
</defs>
|
||
${""}
|
||
<g class="glowlayer glow-pools-frame" pointer-events="none"
|
||
filter=${x?"url(#hp-glowfeather)":G}>
|
||
<g class="glow-pools ${this._glowScreenBlend?"blend-screen":"blend-normal"}"
|
||
data-blend=${this._glowScreenBlend?"screen":"normal"}
|
||
data-feather-px="${2}"
|
||
clip-path=${b?"url(#hp-glow-enabled)":G}>
|
||
${Me(u,e=>e.key,e=>{const t=e.domId;return W`
|
||
<g class="glow-spot ${e.entering?"is-entering":""} ${e.leaving?"is-leaving":""}"
|
||
data-glow-spot="${t}" data-glow-source="${e.sourceEid}">
|
||
<circle class="glow-pool"
|
||
cx="${e.pos.x}" cy="${e.pos.y}" r="${e.r}"
|
||
data-lit-parts="${e.geometry?.lit.length||0}"
|
||
data-feather-px="${2}"
|
||
fill="url(#hp-glow-${t})"
|
||
clip-path=${e.geometry?`url(#hp-glowclip-${t})`:G}></circle>
|
||
</g>`})}
|
||
</g>
|
||
</g>`}_renderAlignDialog(){const e=this._alignDialog,t=e.report,i=e.changed&&!e.preflight?.ok,s=e.preflight?.failures||[],n=s.slice(0,3).map(e=>e.displayName),o=n.length?n.join(", "):this._t("gs.align_preflight_space",{n:"1"}),a=Math.max(0,s.length-n.length),r=a?this._t("gs.align_preflight_more",{n:String(a)}):"",l=t.spaceRefsRemapped+t.roomRefsRemapped+t.positionsRemapped+t.markersDetached,c=t.migrated+t.canonicalized+t.coordsCanonicalized+t.wallSegmentsMigrated+t.wallsMerged+t.spansMerged+t.partitionsMerged+t.partitionsReconciled+t.openingsRehosted+t.redundantDraftsRemoved,h=t.moved+t.rotated+t.removedDrafts+t.coordsCanonicalized+t.wallsStraightened,d=Math.ceil(10*t.maxStraightenShiftCm)/10,p=(this._serverCfg?.spaces||[]).find(e=>String(e?.id||"")===t.maxStraightenSpace),u=(this._serverCfg?.spaces||[]).length>1&&p?String(p.title||p.id):"",_=t.orphanRoomLabelsRemoved+t.orphanDevicePositionsRemoved+t.orphanGroupPositionsRemoved,m=t.liveMissingPositions.map(e=>e.name).filter(Boolean),g=m.slice(0,3).join(", "),f=Math.max(0,m.length-3),v=g?this._t("gs.optimize_live_names",{names:g,more:f?this._t("gs.optimize_reference_more",{n:String(f)}):""}):"",b=t.unverifiedPositions.some(e=>"registry_unavailable"===e.reason),y=e=>t.removedPositions.some(t=>t.id===e.id)?this._t("gs.optimize_detail_removed"):t.liveMissingPositions.some(t=>t.id===e.id)?this._t("gs.optimize_detail_live"):this._t("gs.optimize_detail_unverified"),w=e=>this._t("room_label"===e?"gs.optimize_detail_room_label":"group"===e?"gs.optimize_detail_group":"device"===e?"gs.optimize_detail_device":"gs.optimize_detail_unknown"),k=[...t.removedPositions,...t.liveMissingPositions.filter(e=>!t.removedPositions.some(t=>t.id===e.id)),...t.unverifiedPositions],x=k.slice(0,10),$=Math.max(0,k.length-x.length);return q`<hp-dialog .hass=${this.hass} .title=${this._t("gs.align_title")} icon="mdi:broom"
|
||
dismiss-on-scrim @hp-close=${()=>{this._alignDialog=null,this._preflightClipboardFallback=null}}>
|
||
<div class="body">
|
||
${i?q`
|
||
<p class="alignmsg">${this._t("gs.align_preflight_failed",{spaces:o,more:r})}</p>
|
||
${s.slice(0,10).map(e=>q`<p class="alignmsg">
|
||
${e.displayName}: ${this._t(`gs.preflight_reason_${e.reason}`)}
|
||
</p>`)}
|
||
${s.length>10?q`<p class="alignmsg">
|
||
${this._t("gs.align_preflight_more",{n:String(s.length-10)})}
|
||
</p>`:G}
|
||
<div class="rhint">${this._t("gs.align_preflight_hint")}</div>
|
||
${this._preflightVersionsDiffer()?q`
|
||
<div class="rhint">${this._t("gs.preflight_update_hint")}</div>`:G}
|
||
<div class="row">
|
||
<button class="btn ghost" @click=${()=>this._copyPreflightDiagnostics()}>
|
||
<ha-icon icon="mdi:content-copy"></ha-icon>
|
||
${this._t("gs.preflight_copy")}
|
||
</button>
|
||
</div>
|
||
${this._preflightClipboardFallback?q`<details open>
|
||
<summary>${this._t("gs.preflight_copy")}</summary>
|
||
<pre style="user-select:text;white-space:pre-wrap">${this._preflightClipboardFallback}</pre>
|
||
</details>`:G}`:e.changed?q`
|
||
${t.moved?q`<p class="alignmsg">${this._t("gs.align_count",{n:String(t.moved),total:String(t.total),cm:String(e.cm)})}</p>`:G}
|
||
${t.latticeCoordinatesCanonicalized?q`
|
||
<p class="alignmsg">${this._t("gs.optimize_lattice_summary",{n:String(t.latticeCoordinatesCanonicalized),cm:ol(t.latticeMaxShiftCm)})}</p>
|
||
${t.latticeSpaces.map(e=>q`<p class="alignmsg">${this._t("gs.optimize_lattice_space",{space:e.space,n:String(e.canonicalized),far:String(e.far)})}</p>`)}
|
||
`:G}
|
||
${e.where?q`<p class="alignmsg">${this._t("gs.align_where",{s:e.where})}</p>`:G}
|
||
${t.rotated?q`<p class="alignmsg">${this._t("gs.align_turned",{n:String(t.rotated)})}</p>`:G}
|
||
${t.removedDrafts?q`<p class="alignmsg">${this._t("gs.align_removed_drafts",{n:String(t.removedDrafts)})}</p>`:G}
|
||
${t.redundantDraftsRemoved?q`<p class="alignmsg">${this._t("gs.optimize_redundant_drafts",{n:String(t.redundantDraftsRemoved)})}</p>`:G}
|
||
${t.wallSegmentsMigrated?q`<p class="alignmsg">${this._t("gs.wall_segments_migrated",{n:String(t.wallSegmentsMigrated)})}</p>`:G}
|
||
${t.legacyZeroWallsMigrated?q`<p class="alignmsg">${this._t("gs.zero_walls_migrated",{n:String(t.legacyZeroWallsMigrated)})}</p>`:G}
|
||
${c?q`<p class="alignmsg">${this._t("gs.optimize_changes",{m:String(t.migrated),c:String(t.canonicalized),p:String(t.coordsCanonicalized),w:String(t.wallsMerged),s:String(t.spansMerged),i:String(t.partitionsMerged)})}</p>`:G}
|
||
${t.partitionsReconciled?q`<p class="alignmsg">${this._t("gs.optimize_coincident_partitions",{n:String(t.partitionsReconciled)})}</p>`:G}
|
||
${t.openingsRehosted?q`<p class="alignmsg">${this._t("gs.optimize_openings_rehosted",{n:String(t.openingsRehosted)})}</p>`:G}
|
||
${t.wallsStraightened?q`<p class="alignmsg">${this._t("gs.optimize_walls_straightened",{n:String(t.wallsStraightened),cm:String(d)})}</p>`:G}
|
||
${u?q`<p class="alignmsg">${this._t("gs.optimize_walls_straightened_where",{s:u})}</p>`:G}
|
||
${t.glowSpacesMigrated||t.glowRoomsMigrated?q`<p class="alignmsg">${this._t("gs.optimize_glow_migration",{spaces:String(t.glowSpacesMigrated),rooms:String(t.glowRoomsMigrated)})}</p>`:G}
|
||
${h?q`<div class="rhint">${this._t("gs.align_warn")}</div>`:G}`:q`<p class="alignmsg">${this._t(t.liveMissingPositions.length||t.unverifiedPositions.length||t.nestedRefsUnresolved?"gs.optimize_no_automatic_changes":"gs.align_none")}</p>`}
|
||
${!i&&t.wallsStraightenSkipped?q`<p class="rhint">${this._t("gs.optimize_walls_straighten_skipped",{n:String(t.wallsStraightenSkipped)})}</p>`:G}
|
||
${l?q`<p class="alignmsg">${this._t("gs.optimize_references",{spaces:String(t.spaceRefsRemapped),rooms:String(t.roomRefsRemapped),positions:String(t.positionsRemapped),detached:String(t.markersDetached)})}</p>`:G}
|
||
${_?q`<p class="alignmsg">${this._t("gs.optimize_orphans_removed",{total:String(_),rooms:String(t.orphanRoomLabelsRemoved),devices:String(t.orphanDevicePositionsRemoved),groups:String(t.orphanGroupPositionsRemoved)})}</p>`:G}
|
||
${t.liveMissingPositions.length?q`<div class="optimize-live">
|
||
<p class="alignmsg">${this._t(e.removeLiveMissingPositions?"gs.optimize_live_positions_remove":"gs.optimize_live_positions",{n:String(t.liveMissingPositions.length),names:v})}</p>
|
||
<button class="btn ghost optimize-cleanup" type="button"
|
||
aria-pressed=${e.removeLiveMissingPositions?"true":"false"}
|
||
@click=${this._toggleOptimizeLivePositions} ?disabled=${e.busy}>
|
||
<ha-icon icon=${e.removeLiveMissingPositions?"mdi:undo":"mdi:map-marker-remove-outline"}></ha-icon>
|
||
${this._t(e.removeLiveMissingPositions?"gs.optimize_live_keep":"gs.optimize_live_remove")}
|
||
</button>
|
||
${e.removeLiveMissingPositions?q`<div class="rhint optimize-selected" role="status">
|
||
${this._t("gs.optimize_live_selected")}
|
||
</div>`:G}
|
||
</div>`:G}
|
||
${t.unverifiedPositions.length?q`<div class="rhint" role="alert">
|
||
${this._t("gs.optimize_unverified",{n:String(t.unverifiedPositions.length)})}
|
||
${b?` ${this._t("gs.optimize_registry_limited")}`:""}
|
||
</div>`:G}
|
||
${t.nestedRefsUnresolved?q`<div class="rhint" role="alert">${this._t("gs.optimize_vacuum_warning",{n:String(t.nestedRefsUnresolved)})}</div>`:G}
|
||
${k.length?q`<details class="optimize-details">
|
||
<summary>${this._t("gs.optimize_details")}</summary>
|
||
<ul>
|
||
${x.map(e=>q`<li>${this._t("gs.optimize_detail_item",{status:y(e),kind:w(e.kind),id:e.id,space:e.spaceId})}</li>`)}
|
||
</ul>
|
||
${$?q`<div class="rhint">${this._t("gs.optimize_details_more",{n:String($)})}</div>`:G}
|
||
</details>`:G}
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<span class="spacer"></span>
|
||
<button class="btn ghost" @click=${()=>{this._alignDialog=null,this._preflightClipboardFallback=null}}>${this._t("btn.cancel")}</button>
|
||
${e.changed&&e.preflight?.ok?q`
|
||
<button class="btn on" @click=${this._runAlignToGrid} ?disabled=${e.busy}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${e.busy?"…":this._t("gs.align_run")}
|
||
</button>`:G}
|
||
</div>
|
||
</hp-dialog>`}_renderSettingsDialog(){return q`<hp-dialog .hass=${this.hass} .title=${this._t("gs.title")} icon="mdi:cog-outline" wide
|
||
@hp-close=${()=>this._settingsDialog=null}>
|
||
<div class="body">
|
||
<div class="rhint">${this._t("gs.hint")}</div>
|
||
<label class="dispsection">${this._t("gs.light_group")}</label>
|
||
${this._renderColorRow("light_on","gs.light_on")}
|
||
${this._renderColorRow("light_off","gs.light_off")}
|
||
${this._renderColorRow("light_none","gs.light_none")}
|
||
<label class="dispsection">${this._t("gs.temp_group")}</label>
|
||
${this._renderColorRow("temp_cold","gs.temp_cold")}
|
||
${this._renderColorRow("temp_ok","gs.temp_ok")}
|
||
${this._renderColorRow("temp_hot","gs.temp_hot")}
|
||
<label class="dispsection">${this._t("gs.lqi_group")}</label>
|
||
${this._renderColorRow("lqi_low","gs.lqi_low")}
|
||
${this._renderColorRow("lqi_high","gs.lqi_high")}
|
||
<label class="dispsection">${this._t("gs.glow_group")}</label>
|
||
${this._renderColorRow("glow_base","gs.glow_base")}
|
||
${this._renderColorRow("glow_light","gs.glow_light")}
|
||
<div class="colorrow gsrow">
|
||
<span class="gsl">${this._t("gs.glow_radius")}</span>
|
||
<input type="number" class="tempin" min="0.5" step="0.5"
|
||
.value=${String(this._settingsDialog.glowRadius)}
|
||
@input=${e=>{const t=yv(e.target.value);null!=t&&t>0&&(this._settingsDialog={...this._settingsDialog,glowRadius:t})}} />
|
||
<span class="opl">${this._imperial?this._t("gs.unit_ft"):this._t("gs.unit_m")}</span>
|
||
</div>
|
||
<label class="dispsection">${this._t("gs.wall_group")}</label>
|
||
${this._renderColorRow("wall_fill","gs.wall_fill")}
|
||
<label class="dispsection">${this._t("gs.bg_group")}</label>
|
||
<div class="colorrow gsrow">
|
||
<span class="gsl">${this._t("gs.bg_mode")}</span>
|
||
<select class="areasel"
|
||
@change=${e=>this._settingsDialog={...this._settingsDialog,bgMode:"daynight"===e.target.value?"daynight":"static"}}>
|
||
<option value="static" ?selected=${"static"===this._settingsDialog.bgMode}>${this._t("gs.bg_static")}</option>
|
||
<option value="daynight" ?selected=${"daynight"===this._settingsDialog.bgMode}>${this._t("gs.bg_daynight")}</option>
|
||
</select>
|
||
</div>
|
||
${"static"===this._settingsDialog.bgMode?q`<div class="colorrow gsrow">
|
||
<hp-color-opacity .label=${this._t("gs.bg_color")}
|
||
.pickerLabels=${this._colorPickerLabels}
|
||
.color=${this._settingsDialog.bgColor||this._stageBgHex()}
|
||
.opacity=${1} .showOpacity=${!1}
|
||
@hp-color-opacity-change=${e=>{this._settingsDialog={...this._settingsDialog,bgColor:e.detail.color}}}></hp-color-opacity>
|
||
${this._settingsDialog.bgColor?q`<button class="btn ghost" @click=${()=>this._settingsDialog={...this._settingsDialog,bgColor:null}}>${this._t("gs.bg_default")}</button>`:q`<span class="opl">${this._t("gs.bg_theme")}</span>`}
|
||
</div>`:q`<div class="rhint">${this._t("gs.bg_daynight_hint")}</div>`}
|
||
<label class="dispsection">${this._t("gs.sun_group")}</label>
|
||
${Er(this.hass)?G:q`<div class="rhint">${this._t("gs.sun_missing")}</div>`}
|
||
<div class="sunrow">
|
||
${this._renderCompass()}
|
||
<div class="suncol">
|
||
<span class="gsl">${this._t("gs.north")}</span>
|
||
<div class="colorrow">
|
||
<input class="namein tempin" type="number" min="0" max="359" step="1"
|
||
placeholder=${this._t("gs.north_ph")}
|
||
.value=${null===this._settingsDialog.northDeg?"":String(this._settingsDialog.northDeg)}
|
||
@input=${e=>{const t=e.target.value.trim(),i=""===t?null:Math.round(Number(t));this._settingsDialog={...this._settingsDialog,northDeg:null!==i&&Number.isFinite(i)?Math.min(359,Math.max(0,i)):null}}} />
|
||
${null!==this._settingsDialog.northDeg?q`<button class="btn ghost" @click=${()=>this._settingsDialog={...this._settingsDialog,northDeg:null}}>${this._t("gs.north_clear")}</button>`:G}
|
||
</div>
|
||
${null===this._settingsDialog.northDeg?q`<div class="rhint">${this._t("gs.north_hint")}</div>`:G}
|
||
</div>
|
||
</div>
|
||
<label class="srcrow">
|
||
${this._boolInput(this._settingsDialog.sunRays,e=>this._settingsDialog={...this._settingsDialog,sunRays:e})}
|
||
<span>${this._t("gs.sun_rays")}</span>
|
||
</label>
|
||
${this._canEdit?q`
|
||
<label class="dispsection">${this._t("gs.backup_group")}</label>
|
||
<div class="rhint">${this._t("gs.backup_hint")}</div>
|
||
<div class="backupactions">
|
||
<button class="btn ghost" @click=${this._openBackupExport}>
|
||
<ha-icon icon="mdi:download"></ha-icon>${this._t("backup.export_open")}
|
||
</button>
|
||
<span class="backupupload">
|
||
<button class="btn ghost" type="button" @click=${e=>e.currentTarget.nextElementSibling?.click()}>
|
||
<ha-icon icon="mdi:upload"></ha-icon>${this._t("backup.import_open")}
|
||
</button>
|
||
<input type="file" accept="application/json,.json" @change=${this._pickBackupImport} />
|
||
</span>
|
||
${this._canOptimizeUndo&&"import"===this._undoKind?q`
|
||
<button class="btn ghost" @click=${this._undoPlanOptimization}
|
||
?disabled=${this._optimizeUndoBusy}>
|
||
<ha-icon icon="mdi:undo-variant"></ha-icon>${this._t("backup.undo_import")}
|
||
</button>`:G}
|
||
</div>`:G}
|
||
<label class="dispsection">${this._t("gs.grid_group")}</label>
|
||
<div class="rhint">${this._t("gs.grid_hint")}</div>
|
||
<div class="colorrow gsrow">
|
||
<button class="btn ghost alignall" @click=${this._openAlignDialog}>
|
||
<ha-icon icon="mdi:broom"></ha-icon>${this._t("gs.align_all")}
|
||
</button>
|
||
</div>
|
||
${this._canOptimizeUndo&&"import"!==this._undoKind?q`<div class="colorrow gsrow">
|
||
<button class="btn ghost alignall" @click=${this._undoPlanOptimization}
|
||
?disabled=${this._optimizeUndoBusy}>
|
||
<ha-icon icon="mdi:undo-variant"></ha-icon>${this._t("gs.optimize_undo")}
|
||
</button>
|
||
</div>`:G}
|
||
<label class="dispsection">${this._t("gs.about_group")}</label>
|
||
<div class="aboutver">${this._t("gs.about_version",{v:_v})}</div>
|
||
<a class="aboutlink" href="https://github.com/Matysh/houseplan-card" target="_blank" rel="noopener">
|
||
<ha-icon icon="mdi:github"></ha-icon>${this._t("gs.about_github")}</a>
|
||
<a class="aboutlink" href="https://t.me/ha_houseplan" target="_blank" rel="noopener">
|
||
<ha-icon icon="mdi:send"></ha-icon>${this._t("gs.about_telegram")}</a>
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<button class="btn ghost" @click=${()=>this._settingsDialog={...this._settingsDialog,colors:JSON.parse(JSON.stringify(ds)),glowRadius:this._imperial?9.8:3,bgColor:null,northDeg:null,bgMode:"daynight",sunRays:!1}}>
|
||
${this._t("gs.reset")}
|
||
</button>
|
||
<span class="spacer"></span>
|
||
<button class="btn ghost" @click=${()=>this._settingsDialog=null}>${this._t("btn.cancel")}</button>
|
||
<button class="btn on" @click=${this._saveSettingsDialog} ?disabled=${this._settingsDialog.busy}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${this._settingsDialog.busy?"…":this._t("btn.save")}
|
||
</button>
|
||
</div>
|
||
</hp-dialog>`}_rulesSet(e){this._rulesDialog={...this._rulesDialog,rules:e}}async _saveRules(){const e=this._rulesDialog;if(!e||e.busy)return;const t=e.rules.filter(e=>e.pattern.trim()&&e.icon.trim());this._rulesDialog={...e,busy:!0};try{const e=this._serverCfg,i=JSON.stringify(t)===JSON.stringify(Je),s={...e.settings};i?delete s.icon_rules:s.icon_rules=t,this._serverCfg={...e,settings:s},await this._saveConfigNow(),this._rulesDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("rules.saved"))}catch(e){this._rulesDialog&&(this._rulesDialog={...this._rulesDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}_renderRulesDialog(){const e=this._rulesDialog,t=Xe(e.rules),i=e.test.trim()?tt(e.test,"",t):null,s=(t,i)=>{const s=[...e.rules],n=t+i;n<0||n>=s.length||([s[t],s[n]]=[s[n],s[t]],this._rulesSet(s))};return q`<hp-dialog .hass=${this.hass} .title=${this._t("rules.title")}
|
||
icon="mdi:shape-plus-outline" wide @hp-close=${()=>this._rulesDialog=null}>
|
||
<div class="body">
|
||
<div class="rhint">${this._t("rules.hint")}</div>
|
||
<div class="rtest">
|
||
<input class="namein" type="text" placeholder=${this._t("rules.test_ph")}
|
||
.value=${e.test}
|
||
@input=${t=>this._rulesDialog={...e,test:t.target.value}} />
|
||
${i?q`<ha-icon icon=${i}></ha-icon><span class="rtesticon">${i}</span>`:G}
|
||
</div>
|
||
${e.rules.map((t,i)=>{const n=""!==t.pattern.trim()&&!function(e){try{return new RegExp(e,"i"),!0}catch{return!1}}(t.pattern);return q`<div class="rrow">
|
||
<input class="namein rpat ${n?"bad":""}" type="text"
|
||
placeholder=${this._t("rules.pattern_ph")}
|
||
title=${n?this._t("rules.invalid"):""}
|
||
.value=${t.pattern}
|
||
@input=${s=>{const n=[...e.rules];n[i]={...t,pattern:s.target.value},this._rulesSet(n)}} />
|
||
<input class="namein ricon" type="text" placeholder=${this._t("rules.icon_ph")}
|
||
.value=${t.icon}
|
||
@input=${s=>{const n=[...e.rules];n[i]={...t,icon:s.target.value},this._rulesSet(n)}} />
|
||
<ha-icon class="rprev" icon=${t.icon||"mdi:chip"}></ha-icon>
|
||
<ha-icon class="ract" icon="mdi:arrow-up" title=${this._t("btn.up")}
|
||
@click=${()=>s(i,-1)}></ha-icon>
|
||
<ha-icon class="ract" icon="mdi:arrow-down" title=${this._t("btn.down")}
|
||
@click=${()=>s(i,1)}></ha-icon>
|
||
<ha-icon class="ract del" icon="mdi:close" title=${this._t("btn.delete")}
|
||
@click=${()=>this._rulesSet(e.rules.filter((e,t)=>t!==i))}></ha-icon>
|
||
</div>`})}
|
||
<button class="btn ghost" @click=${()=>this._rulesSet([...e.rules,{pattern:"",icon:""}])}>
|
||
<ha-icon icon="mdi:plus"></ha-icon>${this._t("rules.add")}
|
||
</button>
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<button class="btn ghost" @click=${()=>this._rulesSet(Je.map(e=>({...e})))}>
|
||
${this._t("rules.reset")}
|
||
</button>
|
||
<span class="spacer"></span>
|
||
<button class="btn ghost" @click=${()=>this._rulesDialog=null}>${this._t("btn.cancel")}</button>
|
||
<button class="btn on" @click=${this._saveRules} ?disabled=${e.busy}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${e.busy?"…":this._t("btn.save")}
|
||
</button>
|
||
</div>
|
||
</hp-dialog>`}_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`<label>${i}</label>
|
||
<div class="colorrow">
|
||
${this._rangeInput(50,300,5,Math.round(100*e[t]),e=>this._saveKioskScale({[t]:e/100}))}
|
||
<span class="opv">${Math.round(100*e[t])}%</span>
|
||
</div>`;return q`<hp-dialog .hass=${this.hass} .title=${this._t("kiosk.title")} icon="mdi:tablet"
|
||
dismiss-on-scrim @hp-close=${()=>this._kioskDialog=!1}>
|
||
<div class="body">
|
||
<div class="rhint">${this._t("kiosk.hint")}</div>
|
||
${t("icon",this._t("kiosk.icon_scale"))}
|
||
${t("font",this._t("kiosk.font_scale"))}
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<button class="btn ghost" @click=${()=>this._saveKioskScale({icon:1,font:1})}>${this._t("gs.reset")}</button>
|
||
<span class="spacer"></span>
|
||
<button class="btn on" @click=${()=>this._kioskDialog=!1}>${this._t("btn.close")}</button>
|
||
</div>
|
||
</hp-dialog>`}_fixedFloorValue(e){if("string"==typeof e)return e||"''";try{const t=JSON.stringify(e);return(void 0===t?String(e):t).slice(0,160)}catch{return String(e).slice(0,160)}}render(){if(!this._config||!this.hass)return G;const e=this._model,t=this.houseplanDiagnostics(),i=this._fixedFloorState(e);if("pending"===i.kind)return q`<ha-card data-fixed-floor-state="pending">
|
||
<div class="head">
|
||
<div class="title"><ha-icon icon="mdi:home-city"></ha-icon>${this._config.title||this._t("card.title")}</div>
|
||
</div>
|
||
<div class="empty" role="status" aria-live="polite">
|
||
<ha-icon icon="mdi:loading" class="big fixedfloor-loading"></ha-icon>
|
||
<p>${this._t("fixed_floor.loading")}</p>
|
||
</div>
|
||
</ha-card>`;if("invalid"===i.kind)return q`<ha-card
|
||
data-fixed-floor-state="invalid"
|
||
data-fixed-floor-reason=${i.reason}>
|
||
<div class="head">
|
||
<div class="title"><ha-icon icon="mdi:home-city"></ha-icon>${this._config.title||this._t("card.title")}</div>
|
||
</div>
|
||
<div class="empty fixedfloor-error" role="alert" aria-live="assertive">
|
||
<ha-icon icon="mdi:alert-circle-outline" class="big"></ha-icon>
|
||
<p><b>${this._t("fixed_floor.invalid_title")}</b></p>
|
||
<p>${this._t("fixed_floor.invalid_body",{value:this._fixedFloorValue(i.value)})}</p>
|
||
</div>
|
||
</ha-card>`;if(!e.length)return q`<ha-card
|
||
data-continuity-state=${this._continuity.state}
|
||
data-continuity-token=${this._continuity.token}
|
||
data-frame-fingerprint=${this._continuity.frameFingerprint||G}
|
||
data-recovery-reason=${(this._continuity.overlayVisible||"recovery-error"===this._continuity.state)&&this._continuity.recoveryReason||G}
|
||
data-ha-registry-access=${t.registry.access}
|
||
data-ha-disabled-bindings=${t.bindings.ha_disabled}
|
||
data-ha-unverified-bindings=${t.bindings.unverified}>
|
||
<div class="head">
|
||
<div class="title"><ha-icon icon="mdi:home-city"></ha-icon>${this._config.title||this._t("card.title")}</div>
|
||
</div>
|
||
<div class="empty">
|
||
<ha-icon icon="mdi:floor-plan" class="big"></ha-icon>
|
||
<p>${this._t("empty.no_spaces")}</p>
|
||
${this._serverStorage?q`<p class="muted">${this._t("empty.add_first")}</p>
|
||
<button class="btn on" @click=${()=>this._openSpaceDialog("create")}>
|
||
<ha-icon icon="mdi:plus"></ha-icon>${this._t("btn.add_space")}
|
||
</button>`:q`<p class="muted">${this._t("empty.install")}</p>`}
|
||
</div>
|
||
${this._spaceDialog?this._renderSpaceDialog():G}
|
||
${this._importDialog?this._renderImportDialog():G}
|
||
${this._toast?q`<div class="toast" role="alert" aria-live="assertive">${this._toast}</div>`:G}
|
||
</ha-card>`;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`
|
||
<ha-card
|
||
data-continuity-state=${this._continuity.state}
|
||
data-continuity-token=${this._continuity.token}
|
||
data-frame-fingerprint=${this._continuity.frameFingerprint||G}
|
||
data-device-snapshot-sequence=${h?.sourceSequence??G}
|
||
data-recovery-reason=${T||G}
|
||
data-ha-registry-access=${t.registry.access}
|
||
data-ha-disabled-bindings=${t.bindings.ha_disabled}
|
||
data-ha-unverified-bindings=${t.bindings.unverified}
|
||
@pointerover=${e=>this._notePointer(e)}
|
||
@pointerdown=${this._touchGestureGuard}
|
||
@pointermove=${this._touchGestureGuard}
|
||
@pointerup=${this._touchGestureGuard}
|
||
@pointercancel=${this._touchGestureGuard}
|
||
@lostpointercapture=${this._touchGestureGuard}
|
||
@click=${this._touchGestureGuard}>
|
||
<div class="hdr ${this._kiosk?"kioskhide":""}">
|
||
<div class="head">
|
||
<div class="title">
|
||
<ha-icon icon="mdi:home-city"></ha-icon>
|
||
${this._config.title||this._t("card.title")}
|
||
</div>
|
||
<div class="tabs" @pointermove=${e=>this._tabPointerMove(e)}>
|
||
${n.map(e=>q`<button
|
||
data-hp="space-tab" data-id="${e.id}"
|
||
class="tab ${this._space===e.id?"active":""}${this._tabDrag?.moved&&this._tabDrag.id===e.id?" dragging":""}${this._tabDrag?.moved&&this._tabDrag.targetId===e.id?` drop-${this._tabDrag.placement}`:""}"
|
||
?data-reorderable=${this._canReorderTabs}
|
||
@pointerdown=${t=>this._tabPointerDown(t,e.id)}
|
||
@pointerup=${e=>this._tabPointerUp(e)}
|
||
@pointercancel=${()=>this._endTabDrag()}
|
||
@click=${()=>this._tabClick(e.id)}
|
||
>
|
||
${e.title}${this._norm&&this._canEdit?q`<ha-icon class="tabedit" icon="mdi:cog-outline"
|
||
title=${this._t("title.configure_space")}
|
||
@click=${t=>{t.stopPropagation(),this._openSpaceDialog("edit",e.id)}}></ha-icon>`:G}
|
||
</button>`)}
|
||
${""}
|
||
${!this._canEdit||this._kiosk||this._hasFixedFloor?G:q`<button class="tab tabadd" title=${this._t("title.add_space")}
|
||
@click=${()=>this._openSpaceDialog("create")}>
|
||
<ha-icon icon="mdi:plus"></ha-icon>
|
||
</button>`}
|
||
</div>
|
||
${this._canEdit?q`<div class="modes">
|
||
${[["plan","mdi:floor-plan"],["devices","mdi:tune-variant"],["decor","mdi:draw"]].map(([e,t])=>q`<button class="modetab ${this._mode===e?"active":""}"
|
||
data-editor-navigation=${e}
|
||
title=${this._t("mode."+e+"_tip")}
|
||
@click=${()=>this._setMode(e)}>
|
||
<ha-icon icon=${t}></ha-icon><span class="ml">${this._t("mode."+e)}</span>
|
||
${this._mode===e?q`<ha-icon class="closex" icon="mdi:close" title=${this._t("title.close_editor")}
|
||
data-editor-navigation="view"
|
||
@click=${e=>{e.stopPropagation(),this._setMode("view")}}></ha-icon>`:G}
|
||
</button>`)}
|
||
</div>`:G}
|
||
<span class="count">${this._t("count.devices",{n:c.filter(e=>!e.hidden).length})}</span>
|
||
<span class="spacer"></span>
|
||
${this._labsIso&&"view"===this._mode&&!this._kiosk?q`<button class="btn projection-toggle ${r?"on":""}"
|
||
data-hp="projection-toggle" aria-pressed=${r?"true":"false"}
|
||
aria-label=${this._t("view.volumetric")}
|
||
title=${this._t(r?"view.flat":"view.volumetric")}
|
||
@click=${()=>this._setProjection(r?"flat":"iso")}>
|
||
<ha-icon icon=${r?"mdi:view-grid-outline":"mdi:cube-outline"}></ha-icon>
|
||
</button>`:G}
|
||
<div class="zoomctl">
|
||
<button class="btn zb" @click=${()=>this._stepZoom(-1)} title=${this._t("title.zoom_out")}><ha-icon icon="mdi:minus"></ha-icon></button>
|
||
${""}
|
||
<button class="btn zb" @click=${()=>this._fitAll()}
|
||
title=${this._t("title.zoom_fit")}><ha-icon icon="mdi:fit-to-page-outline"></ha-icon></button>
|
||
<button class="btn zb" @click=${()=>this._stepZoom(1)} title=${this._t("title.zoom_in")}><ha-icon icon="mdi:plus"></ha-icon></button>
|
||
</div>
|
||
${this._norm&&this._canEdit?q`<button class="btn" @click=${this._openSettingsDialog} title=${this._t("title.general_settings")}>
|
||
<ha-icon icon="mdi:cog-outline"></ha-icon>
|
||
</button>`:G}
|
||
</div>
|
||
${this._canEdit&&!this._kiosk?q`<div class="editorchrome ${this._editing||this._modeTransitionBusy?"open":""}${this._modeTransitionBusy?" transitioning":""}"
|
||
style=${I?`height:${I.editorChromeHeight}px;opacity:${I.editorWeight}`:G}
|
||
aria-hidden=${this._editing?"false":"true"}
|
||
?inert=${!this._editing}>
|
||
<div class="editorchrome-inner"
|
||
style=${I?`opacity:${I.toolbarContentOpacity}`:G}>
|
||
${"plan"===C?this._renderMarkupBar():"devices"===C?this._renderDevicesBar():this._renderDecorBar()}
|
||
</div>
|
||
</div>`:G}
|
||
</div>
|
||
|
||
<div class="stage ${this._markup?"markup tool-"+this._tool+("split"!==this._tool||this._splitSel?"":" pickstage")+("wallthick"===this._tool&&this._wallThickHover?" wallhot":""):""} ${"decor"===this._mode?"dtool-"+this._decorTool:""} ${s.bg?"":"noplan"} mode-${this._mode}${this._bdMovable?" bdgrab":""}${this._bdDrag?" bdgrabbing":""}${R?` daycycle phase-${R.phase}`:""}${this._booting?" hpboot":""}${this._bootSoft?" hpsettle":""}${this._modeTransitionBusy?" mode-transition":""}"
|
||
?inert=${this._modeTransitionBusy}
|
||
style="height:${I?`${I.stageHeight}px`:this._kiosk?"100dvh":`calc(100dvh - ${this._hdrH}px)`}${N?`;background:${N}`:""};--hp-cell-visual-scale:${Ch(this._cellCm)};--wall-fill:${this._fillColors.wall_fill.c};--wall-fill-op:${this._fillColors.wall_fill.a};--hp-mode-architecture-opacity:${I?I.architectureOpacity:"decor"===this._mode?.35:1};--hp-mode-view-weight:${I?.viewWeight??("view"===this._mode?1:0)};--hp-mode-editor-weight:${I?.editorWeight??("view"===this._mode?0:1)}${I?`;--hp-mode-paper:${I.paperColor}`:""}${R?`;${Hr(R)}`:""}"
|
||
@click=${e=>this._markupClick(e)}
|
||
@wheel=${e=>this._onWheel(e)}
|
||
@pointerdown=${e=>{this._notePointer(e),this._stagePointerDown(e)}}
|
||
@pointermove=${e=>this._stagePointerMove(e)}
|
||
@pointerleave=${e=>this._stagePointerLeave(e)}
|
||
@pointerup=${e=>this._stagePointerUp(e)}
|
||
@pointercancel=${e=>this._stagePointerCancel(e)}>
|
||
${Lr(R,A)}
|
||
${this._renderEditorSecondary()}
|
||
<div class="zoomwrap ${this._slide?"slide-"+this._slide:""}"
|
||
?inert=${this._continuity.overlayBlocksInteraction||this._modeTransitionBusy}
|
||
style="${1!==E?`filter:brightness(${E.toFixed(3)})`:""}">
|
||
${r&&p?.structural?W`<svg class="iso-underlay-svg"
|
||
viewBox="${y.x} ${y.y} ${y.w} ${y.h}"
|
||
preserveAspectRatio="xMidYMid meet" aria-hidden="true" pointer-events="none">
|
||
${this._renderIsoUnderlay(p)}
|
||
</svg>`:G}
|
||
<svg class=${p?.structural?"plan-svg":G}
|
||
viewBox=${p?.structural?`${y.x} ${y.y} ${y.w} ${y.h}`:`${w.x} ${w.y} ${w.w} ${w.h}`}
|
||
preserveAspectRatio=${p?.structural||!r?"xMidYMid meet":"none"}>
|
||
<g class=${p?.structural?"iso-floor-scene":G}
|
||
transform=${p?.structural?If():G}>
|
||
${""}
|
||
${this._wallHatchDefs(d.color)}${W`<g class="hp-paperg">${this._paperShapes(s.rooms).map(e=>"path"in e?W`<path class="hp-paper" d="${e.path}" fill-rule="evenodd" pointer-events="none"></path>`:"poly"in e?W`<polygon class="hp-paper" points="${e.poly}" pointer-events="none"></polygon>`:W`<rect class="hp-paper" x="${e.rect.x}" y="${e.rect.y}" width="${e.rect.w}" height="${e.rect.h}" rx="${e.rect.rx}" pointer-events="none"></rect>`)}</g>`}
|
||
${this._editing?this._renderMarkupDefs(o):G}
|
||
${""}
|
||
${this._editing&&!this._markup&&this._gridLevels()?W`<rect x="${y.x}" y="${y.y}" width="${y.w}" height="${y.h}" fill="url(#hp-grid-major)" pointer-events="none"></rect>`:G}
|
||
${s.bg&&P?W`<image class="hp-backdrop" href="${P}" x="${s.bg.x}" y="${s.bg.y}" width="${s.bg.w}" height="${s.bg.h}"
|
||
opacity="${I?.backdropOpacity??("decor"===this._mode&&"backdrop"!==this._decorTool?.5:1)}"
|
||
@load=${()=>this._onBackdropLoaded(s.bg.href,P)}
|
||
transform=${s.bg.angle?`rotate(${s.bg.angle} ${s.bg.x+s.bg.w/2} ${s.bg.y+s.bg.h/2})`:G}
|
||
@dblclick=${e=>this._openBackdropDialog(e)}
|
||
preserveAspectRatio="none" />`:G}
|
||
${(()=>{const e=this._openCuts(),t=this._thickWallCuts(),i=this._wallUnionGeometry()?.roomGeom,n=new Map,o=e=>(n.has(e)||n.set(e,hi(e)),n.get(e));return s.rooms.filter(e=>e.area||"view"===this._mode||this._markup||d.showBorders).map(n=>{let a="room "+(s.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||n.id!==this._mergeSel&&n.id!==this._splitSel?.roomId||(a+=" picked");let r="";const l=zs(d.fill,n);if(!this._markup&&(d.showBorders||"none"!==l)){a+=" styled";const e=[];e.push(`--room-stroke:${d.color}`,`--room-stroke-op:${d.showBorders?d.opacity:0}`);const t=_.byRoom.get(n)||null;t?(a+=" filled",e.push(`--room-fill:${t.color}`,`--room-fill-op:${t.opacity}`)):e.push("--room-fill:transparent","--room-fill-op:0"),r=e.join(";")}let c;const h=e=>{"view"===this._mode&&(void 0===c&&(c=this._roomArea(n)),this._showTip(e,n.name||this._t("room.unnamed"),c?this._t("tip.area",{value:c}):"",g?this._roomLqi(n.area):null,this._roomTemp(n),this._roomHum(n)))},p=o(n),u=this._markup&&(n.id===this._mergeSel||n.id===this._splitSel?.roomId),m=p&&!u?e.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return p.some((e,i)=>Hs(t,[e[0],e[1],...p[(i+1)%p.length]])<=.08*this._gridPitch)}):[],f=u?[]:t,v=m.concat(f);v.length&&(a+=" noedge");const b=this._spaceWalls.length&&n.id&&p&&this._innerRoomContour(s,n.id,e,i)||p,y=b?Pi(b,(w=n,s.rooms.filter(e=>e!==w).map(o).filter(Boolean))):[];var w;const k=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",x=b?this._cleanFloor(n,b,s).path:"",$=n.id||G,S=n.area||G,M=x&&b?W`<path class="${a}" style="${r}" fill-rule="evenodd"
|
||
data-hp="room" data-id=${$} data-area=${S}
|
||
d="${[x,...y.map(k)].join(" ")}"
|
||
@pointerenter=${e=>{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}}
|
||
@pointermove=${h}
|
||
@pointerleave=${()=>this._clearTransientHover()}></path>`:y.length&&b?W`<path class="${a}" style="${r}" fill-rule="evenodd"
|
||
data-hp="room" data-id=${$} data-area=${S}
|
||
d="${[b,...y].map(k).join(" ")}"
|
||
@pointerenter=${e=>{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}}
|
||
@pointermove=${h}
|
||
@pointerleave=${()=>this._clearTransientHover()}></path>`:b&&b!==p?W`<polygon class="${a}" style="${r}" points="${b.map(e=>e.join(",")).join(" ")}"
|
||
data-hp="room" data-id=${$} data-area=${S}
|
||
@pointerenter=${e=>{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}}
|
||
@pointermove=${h}
|
||
@pointerleave=${()=>this._clearTransientHover()}></polygon>`:n.poly?W`<polygon class="${a}" style="${r}" points="${n.poly.map(e=>e.join(",")).join(" ")}"
|
||
data-hp="room" data-id=${$} data-area=${S}
|
||
@pointerenter=${e=>{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}}
|
||
@pointermove=${h}
|
||
@pointerleave=${()=>this._clearTransientHover()}></polygon>`:W`<rect class="${a}" style="${r}"
|
||
data-hp="room" data-id=${$} data-area=${S}
|
||
x="${n.x}" y="${n.y}" width="${n.w}" height="${n.h}" rx="${.03*Math.min(n.w,n.h)}"
|
||
@pointerenter=${e=>{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}}
|
||
@pointermove=${h}
|
||
@pointerleave=${()=>this._clearTransientHover()}></rect>`,C=v.length&&p?Ps(p,v,.02*this._gridPitch):null,D=C?W`<path class="room-outline ${this._markup?"outlined":""}"
|
||
d="${C.map(e=>`M ${e[0]} ${e[1]} L ${e[2]} ${e[3]}`).join(" ")}"
|
||
style=${this._markup?G:`stroke:${d.color};stroke-opacity:${d.showBorders?d.opacity:0}`}></path>`:G;return W`${M}${D}`})})()}
|
||
${this._renderRoomHoverFill(D)}
|
||
${this._renderOpeningTunnelFills(s,_)}
|
||
${this._renderGlowBaseRooms(s,m)}
|
||
${this._renderOpeningTunnelFills(s,m,"glow-base")}
|
||
${""}
|
||
${d.hideDecor&&"decor"!==this._mode?G:this._renderDecorLayer()}
|
||
${F?this._renderGlowLayer(s,d):G}
|
||
${this._renderSunRays(s)}
|
||
${this._editing?W`<g class="hp-editor-only-layer"
|
||
opacity="${I?.editorWeight??1}">${this._renderAlignGuides()}</g>`:G}
|
||
${this._markup?W`<g class="hp-editor-only-layer"
|
||
opacity="${I?.editorWeight??1}">${this._renderMarkupLayer(o)}</g>`:G}
|
||
${""}
|
||
${""}
|
||
${this._editing?G:this._renderZeroWalls(d)}
|
||
${this._renderWallBodies(d)}
|
||
${this._markup&&"resize"===this._tool?this._renderResizeMeasurements():G}
|
||
${this._renderRoomHoverOutline(D)}
|
||
${""}
|
||
${this._editing?this._renderZeroWalls(d):G}
|
||
${this._markup?W`<g class="hp-editor-only-layer"
|
||
opacity="${I?.editorWeight??1}">${this._renderHiddenWallDiagnosticOverlay()}</g>`:G}
|
||
${this._markup?W`<g class="hp-editor-only-layer"
|
||
opacity="${I?.editorWeight??1}">${this._renderOpeningPlacementPreview()}</g>`:G}
|
||
${x?this._renderOpeningDimensionGuides(x):G}
|
||
${x?.guide?this._renderOpeningCenterTick(x.guide):G}
|
||
${""}
|
||
${""}
|
||
${this._markup?W`<g class="hp-editor-only-layer"
|
||
opacity="${I?.editorWeight??1}">${this._renderActiveChainInk()}</g>`:G}
|
||
${this._markup?W`<g class="hp-editor-only-layer"
|
||
opacity="${I?.editorWeight??1}">${this._renderPlanSnapOverlay()}</g>`:G}
|
||
${d.hideOpenings&&!this._markup||p&&!p.floorSymbols?G:this._renderOpenings(d)}
|
||
${this._renderWallThickUi()}
|
||
${this._markup&&"resize"===this._tool?this._renderResizeLayer(y):G}
|
||
${""}
|
||
${this._renderBackdropFrame(y)}
|
||
${this._renderTextFrame(y)}
|
||
</g>
|
||
</svg>
|
||
${r&&p?.structural?W`<svg class="iso-shadows-svg"
|
||
viewBox="${y.x} ${y.y} ${y.w} ${y.h}"
|
||
preserveAspectRatio="xMidYMid meet" aria-hidden="true" pointer-events="none">
|
||
${this._renderIsoShadows(p,u)}
|
||
</svg>
|
||
<svg class="iso-walls-svg" viewBox="${y.x} ${y.y} ${y.w} ${y.h}"
|
||
preserveAspectRatio="xMidYMid meet" aria-hidden="true" pointer-events="none">
|
||
${this._renderIsoWalls(p,u)}
|
||
</svg>`:G}
|
||
${""}
|
||
<div class="devlayer" style="--icon-size:${od(v,s,y.w,this._kiosk?this._kioskScale.icon:1).toFixed(3)}cqw;--device-base-size:${od(b,s,y.w,this._kiosk?this._kioskScale.icon:1).toFixed(3)}cqw;--rl-icon-size:${od(v,s,this._roomLabelReferenceViewWidth(y),this._kiosk?this._kioskScale.icon:1).toFixed(3)}cqw;--rl-font:${this._kiosk?this._kioskScale.font:1}">
|
||
${c.map(e=>this._renderDevice(e,y,g))}
|
||
${this._renderVacuums(c,y)}
|
||
${this._renderVacFit(y)}
|
||
${this._renderOpeningLocks(y)}
|
||
${d.showNames||this._markup?s.rooms.map(e=>this._renderRoomLabel(e,s,y,d)):G}
|
||
${this._markup?s.rooms.map(e=>this._renderRoomGear(e,s,y)):G}
|
||
</div>
|
||
${this._measureAnchor?q`<div class="measurelayer">${this._renderMeasureLabel(y)}</div>`:G}
|
||
${this._resize.liveLabels?q`<div class="measurelayer">${this._resize.liveLabels?.map(e=>q`<div
|
||
class="measurelabel ${"area"===e.kind?"rszarea":"rszlength"}"
|
||
data-hp=${"area"===e.kind?"resize-area-label":"resize-length-label"}
|
||
data-room=${"area"===e.kind?e.roomId:G}
|
||
data-side=${"area"===e.kind?e.placement.side:G}
|
||
style="left:${((e.x-y.x)/y.w*100).toFixed(2)}%;top:${((e.y-y.y)/y.h*100).toFixed(2)}%;${"area"===e.kind?`--rsz-label-x:${e.placement.offsetXPx.toFixed(2)}px;--rsz-label-y:${e.placement.offsetYPx.toFixed(2)}px;--rsz-label-tangent:${e.placement.tangentOffsetPx.toFixed(2)}px`:""}">${e.text}</div>`)}</div>`:G}
|
||
${x?q`<div class="measurelayer">${x.labels.map(e=>q`<div
|
||
class="measurelabel opshoulder ${e.dimension?"opdimension":""}"
|
||
data-dimension-source=${e.dimension?.source||G}
|
||
data-dimension-room=${e.dimension?.roomId||G}
|
||
style="left:${((e.x-y.x)/y.w*100).toFixed(2)}%;top:${((e.y-y.y)/y.h*100).toFixed(2)}%;${e.dimension?`--op-label-shift-x:${(12*e.dimension.labelNormal[0]).toFixed(2)}px;--op-label-shift-y:${(12*e.dimension.labelNormal[1]).toFixed(2)}px`:""}">${e.text}</div>`)}</div>`:G}
|
||
${this._wallDialog?q`<div class="measurelayer">${this._renderWallThickDialog()}</div>`:G}
|
||
${$?q`<div class="measurelayer"><div
|
||
class="measurelabel dmeasure ${$.on45?"on45":""}"
|
||
style="left:${(($.x-y.x)/y.w*100).toFixed(2)}%;top:${(($.y-y.y)/y.h*100).toFixed(2)}%">${$.text}</div></div>`:G}
|
||
${M?q`<div class="measurelayer">${M.map(e=>q`<div
|
||
class="measurelabel furnmeasure"
|
||
style="left:${((e.x-y.x)/y.w*100).toFixed(2)}%;top:${((e.y-y.y)/y.h*100).toFixed(2)}%">${e.text}</div>`)}</div>`:G}
|
||
${S?q`<div class="measurelayer"><div
|
||
class="measurelabel bdmeasure"
|
||
style="left:${((S.x-y.x)/y.w*100).toFixed(2)}%;top:${((S.y-y.y)/y.h*100).toFixed(2)}%">${S.text}</div></div>`:G}
|
||
</div>
|
||
${this._zoom>1?q`<div class="zoombadge">${Math.round(100*this._zoom)}%</div>`:G}
|
||
${this._renderFarHint()}
|
||
${this._renderHomeArrow()}
|
||
${this._renderRecoveryOverlay()}
|
||
${this._booting||this._bootFading?q`<div class="bootveil ${this._booting?"":"off"}" aria-hidden="true">
|
||
<svg class="boothouse" viewBox="0 0 24 24"><path d="${"M10,2V4.26L12,5.59V4H22V19H17V21H24V2H10M7.5,5L0,10V21H15V10L7.5,5M14,6V6.93L15.61,8H16V6H14M18,6V8H20V6H18M7.5,7.5L13,11V19H10V13H5V19H2V11L7.5,7.5M18,10V12H20V10H18M18,14V16H20V14H18Z"}"></path></svg>
|
||
</div>`:G}
|
||
</div>
|
||
|
||
${this._roomDialog?this._renderRoomDialog():G}
|
||
${this._mergeDialog?this._renderMergeDialog():G}
|
||
${this._openingDialog?this._renderOpeningDialog():G}
|
||
${this._physicalDialog?this._renderPhysicalDialog():G}
|
||
${this._partitionDeleteDialog?this._renderPartitionDeleteDialog():G}
|
||
${this._roomDeleteDialog?this._renderRoomDeleteDialog():G}
|
||
${this._openingInfo?this._renderOpeningInfoCard():G}
|
||
${this._decorTextDialog?this._renderDecorTextDialog():G}
|
||
${this._decorShapeDialog?this._renderDecorShapeDialog():G}
|
||
${this._backdropDialog?this._renderBackdropDialog():G}
|
||
${this._decorEraseConfirm?this._renderDecorEraseConfirm():G}
|
||
${this._spaceDialog?this._renderSpaceDialog():G}
|
||
${this._deviceInbox?this._renderDeviceInbox():G}
|
||
${this._markerDialog?this._renderMarkerDialog():G}
|
||
${this._vacCalConfirm?q`<hp-dialog .hass=${this.hass}
|
||
.title=${this._t("vac.residual_title")} icon="mdi:map-marker-alert-outline"
|
||
dismiss-on-scrim @hp-close=${()=>this._vacCalConfirm=null}>
|
||
<div class="body">
|
||
<p>${this._t("vac.residual_message",{error:this._vacCalConfirm.error})}</p>
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<button class="btn ghost" @click=${()=>this._vacCalConfirm=null}>${this._t("btn.cancel")}</button>
|
||
<span class="spacer"></span>
|
||
<button class="btn ghost" @click=${()=>this._vacApplyCalibrationProposal(!0)}>${this._t("vac.fit")}</button>
|
||
<button class="btn on" @click=${()=>this._vacApplyCalibrationProposal(!1)}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${this._t("vac.apply_proposal")}
|
||
</button>
|
||
</div>
|
||
</hp-dialog>`:G}
|
||
${this._infoCard?this._renderInfoCard():G}
|
||
${this._rulesDialog?this._renderRulesDialog():G}
|
||
${this._settingsDialog?this._renderSettingsDialog():G}
|
||
${this._alignDialog?this._renderAlignDialog():G}
|
||
${this._backupExportDialog?this._renderBackupExportDialog():G}
|
||
${this._backupImportDialog?this._renderBackupImportDialog():G}
|
||
${this._importDialog?this._renderImportDialog():G}
|
||
${this._tip?q`<div class="tip" style="left:${this._tip.x+12}px;top:${this._tip.y+12}px">
|
||
<b>${this._tip.title}</b>${this._tip.meta?q`<span class="m">${this._tip.meta}</span>`:G}
|
||
${null!=this._tip.temp?q`<span class="m">${this._t("tip.temp_avg")} <b>${this._tip.temp}°</b></span>`:G}
|
||
${null!=this._tip.hum?q`<span class="m">${this._t("tip.hum_avg")} <b>${this._tip.hum}%</b></span>`:G}
|
||
${null!=this._tip.lqi?q`<span class="m">${this._t("tip.lqi")}
|
||
<b style="color:${ai(this._tip.lqi)}">${this._tip.lqi}</b></span>`:G}
|
||
</div>`:G}
|
||
${this._kiosk&&!this._hasFixedFloor&&this._kioskDots&&this._model.length>1?q`<div class="kioskdots">
|
||
${this._model.map(e=>q`<span class="kdot ${e.id===this._space?"on":""}"></span>`)}
|
||
</div>`:G}
|
||
${this._kioskDialog?this._renderKioskDialog():G}
|
||
${this._vacFit?q`<div class="vaccalbar">
|
||
<span>${this._t("vac.fit_hint")}</span>
|
||
<button class="btn ghostbtn" @click=${()=>this._vacFitTurn({rot:(this._vacFit.p.rot+90)%360})}>${this._t("vac.fit_rotate")}</button>
|
||
<button class="btn ghostbtn" @click=${()=>this._vacFitTurn({mir:!this._vacFit.p.mir})}>${this._t("vac.fit_mirror")}</button>
|
||
<button class="btn" @click=${()=>this._vacFitSave()}>${this._t("btn.save")}</button>
|
||
<button class="btn ghostbtn" @click=${()=>{this._vacFit=null}}>${this._t("btn.cancel")}</button>
|
||
</div>`:G}
|
||
${this._tapConfirm?q`<hp-dialog .hass=${this.hass}
|
||
.title=${"toggle"===this._tapConfirm.kind?this._tapConfirm.text:this._t("btn.run")}
|
||
icon="mdi:alert-outline"
|
||
dismiss-on-scrim @hp-close=${()=>this._tapConfirm=null}>
|
||
<div class="body ${"toggle"===this._tapConfirm.kind?"tapconfirm-body":""}">
|
||
${"run"===this._tapConfirm.kind?q`<p>${this._tapConfirm.text}</p>`:this._tapConfirm.lines.map((e,t)=>q`
|
||
<p class="tapconfirm-line" data-line=${t}>${e}</p>`)}
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<span class="spacer"></span>
|
||
<button class="btn ghost" @click=${()=>this._tapConfirm=null}>${this._t("btn.cancel")}</button>
|
||
<button class="btn on" @click=${()=>{const e=this._tapConfirm;this._tapConfirm=null,e.exec()}}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${this._t("btn.run")}
|
||
</button>
|
||
</div>
|
||
</hp-dialog>`:G}
|
||
${this._toast?q`<div class="toast" role="alert" aria-live="assertive">${this._toast}</div>`:G}
|
||
</ha-card>
|
||
`}_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`
|
||
<button type="button" class="vacsource ${e.entityId===o?"on":""}"
|
||
@click=${()=>u({source:e.entityId})}>
|
||
<span><b>${e.name}</b><small>${e.entityId}</small></span>
|
||
<span class="vacsource-meta">${e.platform||this._t("vac.platform_unknown")} · ${f(e)}</span>
|
||
</button>`,b=n.candidates.some(e=>_.has(e.entityId)&&"known_xcme_incomplete"===e.category)||!!n.pinned&&!!o?.startsWith("camera.")&&!a?.hasPosition;return q`
|
||
<label>${this._t("vac.section")}</label>
|
||
<div class="bindbox vacbox">
|
||
<div class="vacdiag" role="status">
|
||
<div><span>${this._t("vac.diag_source")}</span><b>${o||this._t("vac.source_none")}</b></div>
|
||
${a?.platform?q`<div><span>${this._t("vac.diag_platform")}</span><b>${a.platform}</b></div>`:G}
|
||
<div><span>${this._t("vac.diag_status")}</span><b>${this._t(`vac.source_status_${n.status}`)}</b></div>
|
||
<div><span>${this._t("vac.diag_position")}</span><b>${r?.pos?this._t("common.yes"):this._t("common.no")}</b></div>
|
||
<div><span>${this._t("vac.diag_rooms")}</span><b>${ss(this._t("vac.diag_rooms_value"),{total:String(r?.rooms.length||0),matched:String(h),readiness:this._t(h>=3?"vac.autocal_ready":"vac.autocal_not_ready")})}</b></div>
|
||
<div><span>${this._t("vac.diag_path")}</span><b>${r?.path.length?this._t("common.yes"):this._t("common.no")}</b></div>
|
||
<div><span>${this._t("vac.diag_map")}</span><b>${r?.mapId??"default"}</b></div>
|
||
</div>
|
||
${"missing"===n.status||"disabled"===n.status||"unverified"===n.status?q`<div class="warn vacsource-warning">
|
||
<span>${this._t(`vac.source_banner_${n.status}`)}</span>
|
||
<button type="button" class="btn ghostbtn" @click=${()=>{const e=[...this.renderRoot.querySelectorAll("details.vacpicker")].find(e=>e.dataset.devId===t.id);e&&(e.open=!0,e.querySelector("summary")?.focus())}}>${this._t("vac.choose_source")}</button>
|
||
</div>`:G}
|
||
${b?q`<div class="warn vacxcme">
|
||
<b>${this._t("vac.xcme_hint")}</b>
|
||
<pre>attributes:
|
||
- vacuum_position
|
||
- rooms
|
||
- path
|
||
- map_name</pre>
|
||
</div>`:G}
|
||
<details class="vacpicker" data-dev-id=${t.id}>
|
||
<summary class="btn ghostbtn">${this._t("vac.choose_source")}</summary>
|
||
<div class="vacsource-list">
|
||
<button type="button" class="vacsource ${n.pinned?"":"on"}"
|
||
@click=${()=>u({source:null})}>
|
||
<span><b>${this._t("vac.source_auto")}</b><small>${this._t("vac.source_auto_hint")}</small></span>
|
||
</button>
|
||
${m.map(v)}
|
||
<details ?open=${s}
|
||
@toggle=${e=>{e.currentTarget.open?this._vacOpenAllCameras(t):(this._vacAllCamerasFor=null,this._vacAllCameraCache=null)}}>
|
||
<summary>${this._t("vac.all_cameras")}</summary>
|
||
<div class="rhint">${this._t("vac.all_cameras_warn")}</div>
|
||
${s?g.length?g.map(v):q`<div class="rhint">${this._t("vac.all_cameras_empty")}</div>`:G}
|
||
</details>
|
||
</div>
|
||
</details>
|
||
<div class="vacbtns">
|
||
${d?q`<button class="btn" @click=${()=>this._vacAutoCalibrate(t)}>${this._t("vac.autocal")}</button>`:G}
|
||
${l?q`<button class="btn ghostbtn" @click=${()=>this._vacStartFit(t)}>${this._t("vac.fit")}</button>`:G}
|
||
<a class="btn ghostbtn" href="https://github.com/Matysh/houseplan-card/blob/main/docs/VACUUM.md"
|
||
target="_blank" rel="noopener">${this._t("vac.documentation")}</a>
|
||
</div>
|
||
${r?q`
|
||
<label class="srcrow">
|
||
${this._boolInput(!1!==i.live,e=>u({live:!!e&&null}))}
|
||
<span>${this._t("vac.live")}</span>
|
||
</label>
|
||
<label>${this._t("vac.trail")}</label>
|
||
<select class="areasel"
|
||
@change=${e=>u({trail_mode:e.target.value,trail:null})}>
|
||
${["never","cleaning","always"].map(e=>q`
|
||
<option value=${e} ?selected=${bp(i)===e}>${this._t("vac.trail_"+e)}</option>`)}
|
||
</select>
|
||
${p.length?q`<div class="rhint">${ss(this._t("vac.cal_maps"),{maps:p.join(", ")})}</div>`:G}
|
||
`:G}
|
||
</div>`}_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`<polygon points="${t.map(e=>e[0].toFixed(1)+","+e[1].toFixed(1)).join(" ")}"></polygon>
|
||
<text x="${i.toFixed(1)}" y="${r.toFixed(1)}">${e.name}</text>`)}let r=G;if(i.pos){const[t,n]=ep(s,i.pos.x,i.pos.y);r=W`<circle class="vacfitdot" cx="${t.toFixed(1)}" cy="${n.toFixed(1)}" r="${(.012*e.w).toFixed(1)}"></circle>`}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`<circle class="vacfithandle" data-corner="${i[0]+","+i[1]}"
|
||
cx="${e.toFixed(1)}" cy="${s.toFixed(1)}" r="${h.toFixed(1)}"></circle>
|
||
<circle class="vacfitknob" cx="${e.toFixed(1)}" cy="${s.toFixed(1)}" r="${d.toFixed(2)}"></circle>`)}}return q`<svg class="vacfit" viewBox="${e.x} ${e.y} ${e.w} ${e.h}"
|
||
preserveAspectRatio="none"
|
||
@pointerdown=${t=>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}</svg>`}_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`<g class="prev"><polyline class="case" points="${e}"></polyline><polyline class="core" points="${e}"></polyline></g>`)}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`<path class="case" d="${e}"></path><path class="core" d="${e}"></path>`);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`<line class="case tip" data-mid="${i.id}" x1="${r}" y1="${l}" x2="${r}" y2="${l}"></line><line class="core tip" data-mid="${i.id}" x1="${r}" y1="${l}" x2="${r}" y2="${l}"></line>`)}}}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`<div
|
||
data-mid="${i.id}"
|
||
class="vacpuck ${Uo(this._renderPlanHass)} ${h?.jump||s?"jump":""} ${k?"stale":""}"
|
||
style="left:${y}%;top:${w}%"
|
||
title=${i.name}
|
||
@click=${e=>{e.stopPropagation();const t=this._vacEntity(i);t&&this._openMoreInfo(t)}}>
|
||
<ha-icon .icon=${x}></ha-icon>
|
||
</div>`)}return this._vacLastView=t,n.length&&!this._vacRaf&&this._vacRafLoop(),n.length||o.length?q`
|
||
${o.length?W`<svg class="vactrail" viewBox="${t.x} ${t.y} ${t.w} ${t.h}" preserveAspectRatio="none">${o}</svg>`: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=w_(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`<div
|
||
${""}
|
||
data-hp="device"
|
||
data-id="${e.id}"
|
||
data-entity=${e.primary||G}
|
||
data-area=${e.area||G}
|
||
data-binding-status=${r.haDisabled?"ha-disabled":e.bindingStatus?.kind||"active"}
|
||
data-disabled-reason=${c?c.replace("_","-"):G}
|
||
data-state=${d}
|
||
data-lqi-band=${null!=r.lqiText&&r.lqiBand||G}
|
||
role=${p?"button":G}
|
||
tabindex=${p?"0":G}
|
||
aria-label=${u}
|
||
class="dev ${Uo(this._renderPlanHass)} ${r.classes.join(" ")} ${this._selId===e.id?"sel":""} ${e.virtual?"virtual":""} ${e.hidden?"ghost":""} ${r.haDisabled?"ha-disabled":""} ${null!=r.valueText?"valonly":""}"
|
||
style="${l.join(";")}"
|
||
@click=${t=>this._clickDevice(t,e)}
|
||
@keydown=${t=>this._keyDevice(t,e)}
|
||
@contextmenu=${t=>this._ctxDevice(t,e)}
|
||
@pointerover=${t=>this._showTip(t,e.name,r.haDisabled?h:_)}
|
||
@pointerleave=${()=>this._clearTransientHover()}
|
||
@pointerdown=${t=>this._pointerDown(t,e)}
|
||
@pointermove=${t=>{this._pointerMove(t,e),this._showTip(t,e.name,r.haDisabled?h:_)}}
|
||
@pointerup=${t=>this._pointerUp(t,e)}
|
||
@pointercancel=${t=>this._pointerUp(t,e)}
|
||
>
|
||
${Ko(r,{newDevice:this._newIds.has(e.id),newDeviceTitle:this._t("device.new"),disabledTitle:r.haDisabled?h:""})}
|
||
</div>`}_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 rr(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*zv,y:i.y*zv};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},jv(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=Uh(this._drag.ox+c),p=Uh(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},jv(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/zv,y:o.y/zv,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`<button class="rlgearbtn" data-hp="room-settings" data-room=${e.id}
|
||
style="left:${n}%;top:${o}%"
|
||
title=${this._t("room.settings_title")}
|
||
@pointerdown=${e=>e.stopPropagation()}
|
||
@click=${t=>{t.stopPropagation(),this._openRoomEdit(e)}}>
|
||
<ha-icon icon="mdi:cog-outline"></ha-icon>
|
||
<span class="rlgeartext">${this._t("room.settings_short")}</span>
|
||
</button>`}_renderRoomLabel(e,t,i,s){if(!e.name&&!this._markup)return G;const n=this._labelPos(e,t.id),o=this._scenePoint([n.x,n.y]),a=(o[0]-i.x)/i.w*100,r=(o[1]-i.y)/i.h*100,l=Math.min(1,s.opacity+.25),c=this._labelScale(e),h=[];if(e.area||e.settings?.temp_source||e.settings?.hum_source||s.labelLight){if(s.labelTemp){const t=this._roomTemp(e);null!=t&&h.push(q`<span class="rlm"><ha-icon icon="mdi:thermometer"></ha-icon>${t}°</span>`)}if(s.labelHum){const t=this._roomHum(e);null!=t&&h.push(q`<span class="rlm"><ha-icon icon="mdi:water-percent"></ha-icon>${t}%</span>`)}if(s.labelLqi&&e.area){const t=this._roomLqi(e.area);null!=t&&h.push(q`<span class="rlm"><ha-icon icon="mdi:zigbee"></ha-icon>${t}</span>`)}if(s.labelLight){const t=(d=ho(this._renderPlanHass,this._renderDevices,e,this._virtualLights)).length?{on:d.filter(e=>e.on).length,total:d.length}:null;if(t){const e=0===t.on?this._t("roomcard.light_off"):t.on===t.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:t.on,total:t.total});h.push(q`<span class="rlm ${t.on?"lit":""}"><ha-icon icon=${t.on?"mdi:lightbulb-on":"mdi:lightbulb-outline"}></ha-icon>${e}</span>`)}}}var d;const p=!!e.area,u=!this._markup;return q`<div class="roomlabel ${h.length?"card":""}"
|
||
data-hp="room-label" data-id=${e.id||G} data-area=${e.area||G}
|
||
style="left:${a}%;top:${r}%;color:${s.color};opacity:${l};--rl-scale:${c};--rl-space:${s.cardFontScale};--rl-name:${Ns(e.settings?.name_scale)};--rl-meta:${Ns(e.settings?.label_scale)}"
|
||
@pointerdown=${i=>this._labelDown(i,e,t.id)}
|
||
@pointermove=${i=>this._labelMove(i,e,t.id)}
|
||
@pointerup=${()=>this._labelUp(e)}
|
||
@pointercancel=${()=>this._labelUp(e)}
|
||
><span class="rlname">${e.name||(this._markup?this._t("room.unnamed"):"")}${p?q`<ha-icon class="rlgo" icon="mdi:open-in-new"
|
||
title=${u?this._t("room.open_area"):G}
|
||
@click=${u?t=>{t.stopPropagation(),this._clickRoom(e)}:G}
|
||
@pointerdown=${u?e=>e.stopPropagation():G}></ha-icon>`:G}</span>
|
||
${h.length?q`<span class="rlmetrics">${h}</span>`:G}
|
||
${"plan"===this._mode?["tl","tr","bl","br"].map(i=>q`<span class="rlhandle ${i}"
|
||
@pointerdown=${i=>this._rlResizeDown(i,e,t.id)}
|
||
@pointermove=${e=>this._rlResizeMove(e)}
|
||
@pointerup=${()=>this._rlResizeUp()}
|
||
@pointercancel=${()=>this._rlResizeUp()}></span>`):G}
|
||
</div>`}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`<div class="measurelabel ${r?"on45":""}" style="left:${s}%;top:${n}%">
|
||
${this._fmtLen(t,i)} · ${a}°</div>`}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||t<s.d)&&(s={d:t,c:o})}if(Math.abs(o[1]-e[1])<=i){const t=Math.abs(o[0]-e[0]);t>1e-6&&(!n||t<n.d)&&(n={d:t,c:o})}}const o=[];return s&&o.push({axis:"x",at:s.c[0],from:s.c}),n&&o.push({axis:"y",at:n.c[1],from:n.c}),o}(e,this._alignCandidates(),t);if(!i.length)return W``;const s=this._gridPitch,n=1.5*s;return W`<g class="alignguides">
|
||
${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`<line class="alignline" x1="${i}" y1="${o}" x2="${a}" y2="${r}"></line>
|
||
<circle class="aligndot" cx="${t.from[0]}" cy="${t.from[1]}"
|
||
r="${Dh(.18*s,this._cellCm)}"></circle>`})}
|
||
</g>`}_renderOpeningCenterTick(e){const t=(e.angle+90)*Math.PI/180,i=Dh(15,this._cellCm);return W`<line class="alignline opcentertick"
|
||
x1="${e.x-Math.cos(t)*i}" y1="${e.y-Math.sin(t)*i}"
|
||
x2="${e.x+Math.cos(t)*i}" y2="${e.y+Math.sin(t)*i}"></line>`}_renderOpeningDimensionGuides(e){const t=e.labels.flatMap(e=>e.dimension?[e.dimension]:[]);if(!t.length)return W``;const i=this._cssPxToRender(4);return W`<g class="opening-dimensions" aria-hidden="true" pointer-events="none">
|
||
${t.map(e=>{const t=-e.axis[1],s=e.axis[0],n=(e,n)=>W`<line
|
||
class="opening-dimension-tick" data-end=${n}
|
||
x1=${e[0]-t*i} y1=${e[1]-s*i}
|
||
x2=${e[0]+t*i} y2=${e[1]+s*i}></line>`;return W`<g class="opening-dimension" data-source=${e.source}
|
||
data-room=${e.roomId||G}>
|
||
<line class="opening-dimension-line"
|
||
x1=${e.from[0]} y1=${e.from[1]}
|
||
x2=${e.to[0]} y2=${e.to[1]}></line>
|
||
${n(e.from,"from")}${n(e.to,"to")}
|
||
</g>`})}
|
||
</g>`}_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`<g class="opening-preview" data-kind=${e.type}
|
||
aria-hidden="true" pointer-events="none"
|
||
transform="translate(${e.x} ${e.y}) rotate(${e.angle})">
|
||
${i?W`
|
||
<rect class="passage-preview-cut" pointer-events="none"
|
||
x=${i.rect.x} y=${i.rect.y}
|
||
width=${i.rect.width} height=${i.rect.height}></rect>
|
||
${i.boundaries.map(e=>W`
|
||
<line class="passage-preview-boundary" pointer-events="none"
|
||
x1=${e.x1} y1=${e.y1}
|
||
x2=${e.x2} y2=${e.y2}></line>`)}
|
||
`:f_(t)}
|
||
</g>
|
||
<circle class="opening-preview-dot opghost-dot" aria-hidden="true" pointer-events="none"
|
||
cx=${e.x} cy=${e.y}
|
||
r=${Dh(.18*this._gridPitch,this._cellCm)}></circle>`}_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`<g class="opening orphan" data-hp="opening-orphan"
|
||
data-id=${e.id} role="button" tabindex="0"
|
||
aria-label=${this._t("opening.partition_orphan")}
|
||
transform="translate(${e.rx} ${e.ry})"
|
||
@click=${t=>{t.stopPropagation(),this._editOpening(e)}}>
|
||
<circle r=${Dh(.55*this._gridPitch,this._cellCm)}></circle>
|
||
<text text-anchor="middle" dominant-baseline="central">!</text>
|
||
</g>`;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`<g class="opening" data-hp="opening" data-id="${e.id}" data-kind="${e.type}"
|
||
transform="translate(${e.rx} ${e.ry}) rotate(${e.angle})">
|
||
${f_(l)}
|
||
<rect class="op-outline" x="${-c-p}" y="${-h}"
|
||
width="${e.rlen+2*p}" height="${2*h}"
|
||
rx="${Dh(6,this._cellCm)}"></rect>
|
||
<rect class="op-hit" x="${-c-u}" y="${-d}"
|
||
width="${e.rlen+2*u}" height="${2*d}"
|
||
@click=${t=>this._opClick(t,e)}
|
||
@pointerdown=${t=>this._opPointerDown(t,e)}
|
||
@pointermove=${t=>this._opPointerMove(t,e)}
|
||
@pointerup=${t=>this._opPointerUp(t,e)}
|
||
@pointercancel=${t=>this._opPointerUp(t,e)}></rect>
|
||
</g>`})}`}_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`<div class="oplock ${Uo(this._renderPlanHass)} ${s?"locked":o?"unlocked":"unknown"}"
|
||
style="left:${u}%;top:${_}%"
|
||
@click=${e=>{e.stopPropagation(),"view"===this._mode&&(this._openingInfo=t)}}>
|
||
<span class="oplock-shell" aria-hidden="true">
|
||
<span class="oplock-core">
|
||
<ha-icon icon="${s?"mdi:lock":o?"mdi:lock-open-variant":"mdi:lock-question"}"></ha-icon>
|
||
</span>
|
||
</span>
|
||
</div>`})}`}_lockAction(e,t){if(this._openingEntityAvailable(e)){if("unlock"===t){const t=this.hass?.states?.[e]?.attributes?.friendly_name||e;if(!confirm(this._t("confirm.unlock",{name:t})))return}this.hass?.callService?.("lock",t,{entity_id:e})}}_renderOpeningInfoCard(){const e=this._openingInfo,t="passage"!==e.type&&e.contact&&this._openingEntityAvailable(e.contact)?e.contact:null,i=("door"===e.type||"gate"===e.type)&&e.lock&&this._openingEntityAvailable(e.lock)?e.lock:null,s=t?this.hass.states[t]?.state:null,n=this._openingAmt(e),o=i?this.hass.states[i]?.state:null,a="door"===e.type?"opening.door":"gate"===e.type?"opening.gate":"passage"===e.type?"opening.passage":"opening.window",r="door"===e.type?"mdi:door":"gate"===e.type?"mdi:gate":"passage"===e.type?"mdi:arch":"mdi:window-closed-variant",l="gate"===e.type?n>0?"mdi:gate-open":"mdi:gate":n>0?"mdi:door-open":"mdi:door-closed",c=(e,t,i,s="")=>q`<div class="oprow ${s}"><ha-icon icon=${e}></ha-icon><span>${t}</span><b>${i}</b></div>`;return q`<hp-dialog .hass=${this.hass}
|
||
.title=${this._t(a)} icon=${r} dismiss-on-scrim
|
||
@hp-close=${()=>this._openingInfo=null}>
|
||
<div class="body">
|
||
${t?c(l,this._t("opening.contact_label"),"unavailable"===s||null==s?this._t("opening.state_unknown"):this._t(n>0?"opening.open":"opening.closed"),n>0?"warn":"ok"):G}
|
||
${i?c("locked"===o?"mdi:lock":"mdi:lock-open-variant",this._t("opening.lock_label"),"locked"===o?this._t("opening.locked"):["unlocked","open"].includes(String(o))?this._t("opening.unlocked"):this._t("opening.state_unknown"),"locked"===o?"ok":"warn"):G}
|
||
${i&&("locked"===o||["unlocked","open"].includes(String(o)))?q`<button
|
||
class="btn lockact ${"locked"===o?"warn":""}"
|
||
@click=${()=>this._lockAction(i,"locked"===o?"unlock":"lock")}>
|
||
<ha-icon icon=${"locked"===o?"mdi:lock-open-variant":"mdi:lock"}></ha-icon>
|
||
${this._t("locked"===o?"opening.unlock_action":"opening.lock_action")}
|
||
</button>`:i&&["locking","unlocking"].includes(String(o))?q`<button class="btn lockact" disabled>
|
||
<ha-icon icon="mdi:timer-sand"></ha-icon>${this._t("opening.lock_pending")}
|
||
</button>`:G}
|
||
${t||i?G:q`<p class="muted">${this._t("opening.no_entities")}</p>`}
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<span class="spacer"></span>
|
||
<button class="btn ghost" @click=${()=>this._openingInfo=null}>${this._t("btn.close")}</button>
|
||
</div>
|
||
</hp-dialog>`}_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`
|
||
<button type="button" class="dropbtn opening-entity-drop ${n?"open":""}"
|
||
data-opening-picker=${t} aria-expanded=${n?"true":"false"}
|
||
@click=${()=>this._toggleOpeningEntityPicker(t)}>
|
||
${s?q`<b>${r}</b><span class="ref">${s}</span>`:q`<span class="muted">${this._t("opening.none")}</span>`}
|
||
<ha-icon icon=${n?"mdi:chevron-up":"mdi:chevron-down"}></ha-icon>
|
||
</button>
|
||
${n?q`<div class="droppanel opening-entity-panel" data-opening-panel=${t}>
|
||
<input class="namein opening-entity-search" type="text"
|
||
placeholder=${this._t("opening.search_ph")} .value=${o}
|
||
@input=${e=>this._filterOpeningEntities(t,e.target.value)} />
|
||
<div class="candlist">
|
||
<button type="button" class="cand opening-entity-candidate ${s?"":"sel"}"
|
||
data-opening-entity="" @click=${()=>this._selectOpeningEntity(t,"")}>
|
||
<span class="cl">${this._t("opening.none")}</span>
|
||
</button>
|
||
${l.map(e=>q`
|
||
<button type="button"
|
||
class="cand opening-entity-candidate ${e.value===s?"sel":""}"
|
||
data-opening-entity=${e.value}
|
||
@click=${()=>this._selectOpeningEntity(t,e.value)}>
|
||
<span class="cl">${e.label}</span>
|
||
<span class="cs">${e.value}</span>
|
||
</button>`)}
|
||
${l.length?G:q`<div class="cand muted opening-entity-empty">${this._t("marker.nothing_found")}</div>`}
|
||
</div>
|
||
</div>`:G}`};return q`<hp-dialog .hass=${this.hass} wide
|
||
.title=${e.id?this._t("opening.edit"):this._t("opening.new")} icon=${h}
|
||
@hp-close=${()=>this._openingDialog=null}>
|
||
<div class="body">
|
||
${e.host?q`<label>${this._t("opening.host_partition")}</label>
|
||
<div class=${c||a?"habindingbanner":"rhint"}
|
||
role=${c||a?"status":G}>
|
||
${c||a?q`<ha-icon icon="mdi:alert-outline"></ha-icon>`:G}
|
||
<span>${c?this._t("opening.partition_orphan"):a?this._t("opening.partition_jamb_margin",{distance:l}):this._t("opening.host_partition")}</span>
|
||
</div>`:G}
|
||
<label>${this._t("opening.type_label")}</label>
|
||
<label class="srcrow"><input type="radio" name="optype" .checked=${"window"===e.type}
|
||
@change=${()=>this._openingDialog={...e,type:"window",lengthCm:e.id?e.lengthCm:Gf("window"),contactOpen:!1,lockOpen:!1}} />
|
||
<span>${this._t("opening.window")}</span></label>
|
||
<label class="srcrow"><input type="radio" name="optype" .checked=${"door"===e.type}
|
||
@change=${()=>this._openingDialog={...e,type:"door",lengthCm:e.id?e.lengthCm:Gf("door"),contactOpen:!1,lockOpen:!1}} />
|
||
<span>${this._t("opening.door")}</span></label>
|
||
<label class="srcrow"><input type="radio" name="optype" .checked=${"passage"===e.type}
|
||
@change=${()=>this._openingDialog={...e,type:"passage",lengthCm:e.id?e.lengthCm:Gf("passage"),contactOpen:!1,lockOpen:!1}} />
|
||
<span>${this._t("opening.passage")}</span></label>
|
||
<label class="srcrow"><input type="radio" name="optype" .checked=${"gate"===e.type}
|
||
@change=${()=>this._openingDialog={...e,type:"gate",lengthCm:e.id?e.lengthCm:Gf("gate"),flipH:!1,contactOpen:!1,lockOpen:!1}} />
|
||
<span>${this._t("opening.gate")}</span></label>
|
||
|
||
<label>${this._t("opening.length_label")}</label>
|
||
<input class="namein tempin" type="number" min="20" max="600" step="5" .value=${String(e.lengthCm)}
|
||
@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`<div class="habindingbanner" role="status" aria-live="polite">
|
||
<ha-icon icon="mdi:alert-outline"></ha-icon>
|
||
<span>${this._t("opening.passage_binding_warning")}</span>
|
||
</div>`:G}
|
||
|
||
${"passage"!==e.type?q`<label>${this._t("opening.contact_label")}</label>
|
||
${d("contact",this._contactCandidates())}
|
||
${e.contact?q`<label class="srcrow">${this._boolInput(e.invert,t=>this._openingDialog={...e,invert:t})}
|
||
<span>${this._t("opening.invert")}</span></label>`:G}`:G}
|
||
|
||
${"door"===e.type||"gate"===e.type?q`<label>${this._t("opening.lock_label")}</label>
|
||
${d("lock",this._lockCandidates())}`:G}
|
||
|
||
${"gate"!==e.type&&"passage"!==e.type?q`<label class="srcrow">${this._boolInput(e.flipH,t=>this._openingDialog={...e,flipH:t})}
|
||
<span>${this._t("opening.flip_h")}</span></label>`:G}
|
||
${"passage"!==e.type?q`<label class="srcrow">${this._boolInput(e.flipV,t=>this._openingDialog={...e,flipV:t})}
|
||
<span>${this._t("opening.flip_v")}</span></label>`:G}
|
||
</div>
|
||
<div class="row dialog-action-footer" slot="footer">
|
||
${e.id?q`<div class="dialog-action-group dialog-action-danger">
|
||
<button class="btn danger" @click=${this._deleteOpening}>
|
||
<ha-icon icon="mdi:delete-outline"></ha-icon>${this._t("btn.delete")}
|
||
</button>
|
||
</div>`:G}
|
||
${c?q`<button class="btn ghost" @click=${this._rebindPartitionOpening}>
|
||
${this._t("opening.rebind_partition")}
|
||
</button>`:G}
|
||
<div class="dialog-action-group dialog-action-commit">
|
||
<button class="btn ghost" @click=${()=>this._openingDialog=null}>${this._t("btn.cancel")}</button>
|
||
<button class="btn on" @click=${this._saveOpening}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${this._t("btn.save")}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</hp-dialog>`}_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`<defs></defs>`;const i=this._gridPitch*t.fine,s=this._gridPitch*t.coarse,n=this._gridPitch*t.fine*.14;return W`<defs>
|
||
<pattern id="hp-grid" x="0" y="0" width="${i}" height="${i}" patternUnits="userSpaceOnUse">
|
||
<circle cx="0" cy="0" r="${n}" class="griddot"></circle>
|
||
<circle cx="${i}" cy="0" r="${n}" class="griddot"></circle>
|
||
<circle cx="0" cy="${i}" r="${n}" class="griddot"></circle>
|
||
<circle cx="${i}" cy="${i}" r="${n}" class="griddot"></circle>
|
||
</pattern>
|
||
<pattern id="hp-grid-major" x="0" y="0" width="${s}" height="${s}" patternUnits="userSpaceOnUse">
|
||
<rect width="${s}" height="${s}" fill="url(#hp-grid)"></rect>
|
||
<circle cx="0" cy="0" r="${2.1*n}" class="griddot major"></circle>
|
||
<circle cx="${s}" cy="0" r="${2.1*n}" class="griddot major"></circle>
|
||
<circle cx="0" cy="${s}" r="${2.1*n}" class="griddot major"></circle>
|
||
<circle cx="${s}" cy="${s}" r="${2.1*n}" class="griddot major"></circle>
|
||
</pattern>
|
||
</defs>`}_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`<line class="physical-hit ${l("draft",e.id)?"selected":""}"
|
||
data-hp="room-draft" data-kind="segment" data-id=${e.id} data-segment=${i}
|
||
x1=${t[0]} y1=${t[1]} x2=${s[0]} y2=${s[1]}
|
||
stroke-width=${Math.max(24,a/Math.max(n,1e-9))}
|
||
vector-effect="non-scaling-stroke"
|
||
@pointerdown=${t=>{this._physicalSel={kind:"draft",id:e.id,segment:i},this._registerPhysicalTap("draft",e.id,i)}}></line>`})),h=(e.partitions||[]).map(e=>{const t=hh(e.a,e.b,e.cm,this._cellCm,this._gridPitch);return t?W`<path class="physical-hit ${l("partition",e.id)?"selected":""}"
|
||
data-hp="partition" data-kind="partition" data-id=${e.id} d=${r(t)}
|
||
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)}></path>`:W`<line class="physical-hit ${l("partition",e.id)?"selected":""}"
|
||
data-hp="partition" data-kind="partition" data-id=${e.id}
|
||
x1=${e.a[0]} y1=${e.a[1]} x2=${e.b[0]} y2=${e.b[1]}
|
||
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)}></line>`}),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`<path class="physical-hit ${l("column",e.id)?"selected":""}"
|
||
data-hp="wall-column" data-kind=${e.shape} data-id=${e.id} d=${r(t)}
|
||
stroke-width=${24} vector-effect="non-scaling-stroke"
|
||
@pointerdown=${t=>this._physicalDown(t,"column",e.id)}
|
||
@pointermove=${e=>this._physicalMove(e)}
|
||
@pointerup=${e=>this._physicalUp(e)}></path>`}),p=this._physicalDrag,u=(()=>{if(!p?.moved)return G;if("partition"===p.kind&&0===Number(p.base.cm)){const e=p.base;return W`<line class="physical-drag zero"
|
||
x1=${e.a[0]} y1=${e.a[1]}
|
||
x2=${e.b[0]} y2=${e.b[1]}
|
||
transform="translate(${p.delta[0]} ${p.delta[1]})"></line>`}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`<path class="physical-drag" d=${r(e)}
|
||
transform="translate(${p.delta[0]} ${p.delta[1]})"></path>`: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`<g class="physical-chrome" data-kind="draft-selection">
|
||
<polyline class="frame" points=${t.points.map(e=>e.join(",")).join(" ")}></polyline>
|
||
${t.points.map(e=>W`<circle class="move-dot" cx=${e[0]} cy=${e[1]} r=${.55*o}></circle>`)}
|
||
</g>`: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`<g class="physical-chrome" data-kind="partition-selection">
|
||
${s?W`<path class="frame" d=${r(s)}></path>`:W`<line class="frame" x1=${t.a[0]} y1=${t.a[1]}
|
||
x2=${t.b[0]} y2=${t.b[1]}></line>`}
|
||
<circle class="move-dot" cx=${t.a[0]} cy=${t.a[1]} r=${.55*o}></circle>
|
||
<circle class="move-dot" cx=${t.b[0]} cy=${t.b[1]} r=${.55*o}></circle>
|
||
<circle class="move-dot" cx=${n} cy=${a} r=${o}></circle>
|
||
</g>`}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`<g class="physical-chrome" data-kind="circle-selection">
|
||
<path class="frame" d=${r(c)}></path>
|
||
<circle class="move-dot" cx=${l.center[0]} cy=${l.center[1]} r=${o}></circle>
|
||
</g>`;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`<g class="physical-chrome" data-kind="square-selection">
|
||
<path class="frame" d=${r(c)}></path>
|
||
<circle class="move-dot" cx=${l.center[0]} cy=${l.center[1]} r=${o}></circle>
|
||
<line class="stem" x1=${_[0]} y1=${_[1]} x2=${m[0]} y2=${m[1]}></line>
|
||
<circle class="rotate-handle" cx=${m[0]} cy=${m[1]} r=${a}
|
||
data-kind="rotate" @pointerdown=${e=>this._physicalRotateDown(e,s)}
|
||
@pointermove=${e=>this._physicalRotateMove(e)}
|
||
@pointerup=${e=>this._physicalRotateUp(e)}></circle>
|
||
</g>`})();return W`<g class="physical-editor">${c}${h}${d}${u}${_}</g>`}_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`<g class="hidden-wall-diagnostic" data-hp="hidden-wall-diagnostic"
|
||
data-segment-count=${e.segments.length}
|
||
data-endpoint-count=${e.endpoints.length}
|
||
aria-hidden="true" pointer-events="none">
|
||
${e.segments.map(e=>W`<line class="hidden-wall-line"
|
||
data-key=${e.key} data-source-kind=${e.sourceKind}
|
||
data-source-id=${e.sourceId}
|
||
x1=${e.a[0]} y1=${e.a[1]} x2=${e.b[0]} y2=${e.b[1]}
|
||
vector-effect="non-scaling-stroke" pointer-events="none"></line>`)}
|
||
${e.endpoints.map(e=>W`<circle class="hidden-wall-node"
|
||
data-key=${e.key} data-source-kind=${e.sourceKind}
|
||
data-source-id=${e.sourceId}
|
||
cx=${e.point[0]} cy=${e.point[1]} r=${t}
|
||
pointer-events="none"></circle>`)}
|
||
</g>`}_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`<g class="plan-snap-overlay" data-hp="plan-snap-overlay"
|
||
data-segment-count=${e.segments.length}
|
||
data-endpoint-count=${e.endpoints.length}
|
||
aria-hidden="true" pointer-events="none">
|
||
${ge([e,s,[...i].sort().join("|")],()=>W`
|
||
${e.segments.map(e=>W`<line class="plan-snap-line"
|
||
data-key=${e.key} data-source-kind=${e.sourceKind}
|
||
x1=${e.a[0]} y1=${e.a[1]} x2=${e.b[0]} y2=${e.b[1]}
|
||
vector-effect="non-scaling-stroke" pointer-events="none"></line>`)}
|
||
${e.endpoints.map(e=>W`<circle class="plan-snap-node ${i.has(e.key)?"conflict":""}"
|
||
data-kind="endpoint" data-key=${e.key} data-active="false"
|
||
cx=${e.point[0]} cy=${e.point[1]} r=${s}
|
||
pointer-events="none"></circle>`)}
|
||
`)}
|
||
<circle class="plan-snap-node ${t?"active":""} ${"line"===t?.kind?"dynamic":""}"
|
||
data-hp="plan-snap-active-marker"
|
||
data-kind=${t?.kind??G} data-key=${t?.key??G}
|
||
data-active=${t?"true":"false"}
|
||
cx=${t?.point[0]??0} cy=${t?.point[1]??0} r=${n}
|
||
visibility=${t?"visible":"hidden"} pointer-events="none"></circle>
|
||
</g>`}_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+1<e.length;s++)t[s]>0&&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`<rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" fill="url(#hp-grid-major)" pointer-events="none"></rect>`:G}
|
||
${n.map(e=>W`<line class="seg" x1="${e[0]}" y1="${e[1]}" x2="${e[2]}" y2="${e[3]}"></line>`)}
|
||
${this._renderPhysicalEditorLayer()}
|
||
${"column"===this._tool&&this._cursorPt&&this._drawWallCm?W`<path class="physical-drag" d=${(()=>`M ${dh({shape:"square",center:this._cursorPt,cm:this._drawWallCm},this._cellCm,this._gridPitch).map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`)()}></path>`:G}
|
||
${d?W`<path class="drawwall-preview-fill" d="${d}"></path>
|
||
<path class="drawwall-preview" d="${d}"></path>`:G}
|
||
${c&&0===l?W`<polyline class="drawwall-zero-preview ${q_(this._curSpaceCfg)}"
|
||
points=${c.map(e=>e.join(",")).join(" ")}></polyline>`:G}
|
||
${p?W`<path class="drawwall-preview-fill" d="${p}"></path>
|
||
<path class="drawwall-preview" style="stroke:none" d="${p}"></path>`:G}
|
||
${u?W`<line class="wall-repair-preview"
|
||
x1=${u.from[0]} y1=${u.from[1]}
|
||
x2=${u.to[0]} y2=${u.to[1]}
|
||
aria-hidden="true" pointer-events="none"></line>`:G}
|
||
${"split"===this._tool&&this._splitSel?.pts?.length?W`${this._splitSel.pts.length>1?W`<polyline class="pathline" points="${this._splitSel.pts.map(e=>e.join(",")).join(" ")}"></polyline>`:G}
|
||
${this._splitSel.pts.map((e,t)=>W`<circle class="vertex ${0===t?"first":""}"
|
||
cx="${e[0]}" cy="${e[1]}" r="${Dh(.22*a,this._cellCm)}"></circle>`)}
|
||
${this._cursorPt?W`<line class="preview" x1="${this._splitSel.pts[this._splitSel.pts.length-1][0]}" y1="${this._splitSel.pts[this._splitSel.pts.length-1][1]}"
|
||
x2="${this._cursorPt[0]}" y2="${this._cursorPt[1]}"></line>`:G}`:G}
|
||
`}_renderActiveChainInk(){const e=this._path,t=this._gridPitch;return W`
|
||
${e.length>1?W`<polyline class="pathline" points="${e.map(e=>e.join(",")).join(" ")}"></polyline>`:G}
|
||
${e.length&&this._cursorPt&&"draw"===this._tool&&!this._contourClosed?W`<line class="active-axis" x1="${e[e.length-1][0]}" y1="${e[e.length-1][1]}"
|
||
x2="${this._cursorPt[0]}" y2="${this._cursorPt[1]}" aria-hidden="true"></line>
|
||
${this._activePlanSnapCandidate||this._activePlanSnapConflicts.length?G:W`<circle class="active-vertex" cx="${this._cursorPt[0]}" cy="${this._cursorPt[1]}"
|
||
r="${Dh(.22*t,this._cellCm)}" aria-hidden="true"></circle>`}`:G}
|
||
${e.map((e,i)=>W`<circle class="vertex ${0===i?"first":""}"
|
||
cx="${e[0]}" cy="${e[1]}" r="${Dh(.22*t,this._cellCm)}"></circle>`)}
|
||
`}_renderPartitionDeleteDialog(){const e=this._partitionDeleteDialog,t="mi"===this.hass?.config?.unit_system?.length;return q`<hp-dialog .hass=${this.hass}
|
||
.title=${this._t("confirm.delete_partition_openings_title")}
|
||
icon="mdi:wall" dismiss-on-scrim
|
||
@hp-close=${()=>this._partitionDeleteDialog=null}>
|
||
<div class="body">
|
||
<p>${this._t("confirm.delete_partition_openings_body",{count:e.openings.length})}</p>
|
||
<ul aria-label=${this._t("confirm.delete_partition_openings_title")}>
|
||
${e.openings.map(e=>q`<li>${this._t("confirm.delete_partition_openings_item",{type:this._t(`opening.${e.type}`),length:li(e.length*zv/this._gridPitch*this._cellCm,t)})}</li>`)}
|
||
</ul>
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<button class="btn ghost" @click=${()=>this._partitionDeleteDialog=null}>
|
||
${this._t("btn.cancel")}
|
||
</button>
|
||
<span class="spacer"></span>
|
||
<button class="btn danger" @click=${this._confirmPartitionDelete}>
|
||
<ha-icon icon="mdi:delete-outline"></ha-icon>${this._t("btn.delete")}
|
||
</button>
|
||
</div>
|
||
</hp-dialog>`}_renderRoomDeleteDialog(){const e=this._roomDeleteDialog;return q`<hp-dialog .hass=${this.hass}
|
||
.title=${this._t("confirm.delete_room_title",{name:e.name})}
|
||
icon="mdi:floor-plan" dismiss-on-scrim
|
||
@hp-close=${()=>this._roomDeleteDialog=null}>
|
||
<div class="body">
|
||
<p>${this._t("confirm.delete_room_body")}</p>
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<button class="btn ghost" @click=${()=>this._roomDeleteDialog=null}>
|
||
${this._t("btn.cancel")}
|
||
</button>
|
||
<span class="spacer"></span>
|
||
<button class="btn" @click=${()=>this._confirmRoomDelete(!0)}>
|
||
<ha-icon icon="mdi:wall"></ha-icon>${this._t("btn.delete_room_keep_walls")}
|
||
</button>
|
||
<button class="btn danger" @click=${()=>this._confirmRoomDelete(!1)}>
|
||
<ha-icon icon="mdi:delete-outline"></ha-icon>${this._t("btn.delete_room_with_walls")}
|
||
</button>
|
||
</div>
|
||
</hp-dialog>`}_renderPhysicalDialog(){const e=this._physicalDialog,t="column"===e.kind;return q`<hp-dialog .hass=${this.hass} wide
|
||
.title=${this._t(t?"physical.column_properties":"partition"===e.kind?"physical.partition_properties":"physical.draft_properties")}
|
||
icon=${t?"mdi:vector-square":"mdi:wall"}
|
||
@hp-close=${()=>this._physicalDialog=null}>
|
||
<div class="body">
|
||
${t?q`<label>${this._t("physical.shape")}</label>
|
||
<select class="areasel" @change=${t=>{const i=t.target.value;this._physicalDialog={...e,shape:i}}}>
|
||
<option value="square" ?selected=${"square"===e.shape}>${this._t("physical.square")}</option>
|
||
<option value="circle" ?selected=${"circle"===e.shape}>${this._t("physical.circle")}</option>
|
||
</select>`:G}
|
||
<label>${this._t(t?"circle"===e.shape?"physical.diameter":"physical.side":"wallthick.field")}</label>
|
||
<div class="row"><input class="namein tempin" type="number"
|
||
min=${Sl(t?1:0,this._imperial)}
|
||
max=${Sl(t?150:100,this._imperial)} step="any" .value=${e.cm}
|
||
@input=${t=>this._physicalDialog={...e,cm:t.target.value}} />
|
||
<span class="opl">${this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}</span></div>
|
||
${t&&"square"===e.shape?q`
|
||
<label>${this._t("physical.rotation")}</label>
|
||
<input class="namein tempin" type="number" min="0" max="89.999" step="5"
|
||
.value=${e.angle||"0"}
|
||
@input=${t=>this._physicalDialog={...e,angle:t.target.value}} />`:G}
|
||
${e.length?q`<div class="muted">${this._t("physical.length")}: ${e.length}</div>`:G}
|
||
</div>
|
||
<div class="row dialog-action-footer physicalfooter" slot="footer">
|
||
<div class="dialog-action-group dialog-action-danger">
|
||
${"draft"===e.kind?q`
|
||
<button class="btn danger" @click=${this._deleteDraftSegment}>
|
||
<ha-icon icon="mdi:vector-line"></ha-icon>${this._t("physical.delete_segment")}
|
||
</button>
|
||
<button class="btn danger" @click=${this._deleteDraftWhole}>
|
||
<ha-icon icon="mdi:delete-outline"></ha-icon>${this._t("physical.delete_draft")}
|
||
</button>`:q`
|
||
<button class="btn danger" @click=${this._deletePhysicalSelection}>
|
||
<ha-icon icon="mdi:delete-outline"></ha-icon>${this._t("btn.delete")}
|
||
</button>`}
|
||
</div>
|
||
<div class="dialog-action-group dialog-action-commit">
|
||
<button class="btn ghost" @click=${()=>this._physicalDialog=null}>${this._t("btn.cancel")}</button>
|
||
<button class="btn on" @click=${this._savePhysicalDialog}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${this._t("btn.save")}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</hp-dialog>`}_renderMarkupBar(){const e=this._geometryHistory.undoName,t=this._geometryHistory.redoName,i=e?this._t("history.undo_named",{name:e}):this._t("history.undo_empty"),s=t?this._t("history.redo_named",{name:t}):this._t("history.redo_empty");return q`<div class="editbar planbar">
|
||
<div class="editbar-tools" tabindex="-1" ?inert=${this._modeTransitionBusy}>
|
||
<ha-icon icon="mdi:vector-square-edit" class="warn"></ha-icon>
|
||
<span class="wallsgroup">
|
||
<button class="btn ${"select"===this._tool?"on":""}"
|
||
@click=${()=>this._activateMarkupTool("select")}
|
||
title=${this._t("title.markup_select")}>
|
||
<ha-icon icon="mdi:cursor-default-outline"></ha-icon>${this._t("markup.select")}
|
||
</button>
|
||
<button class="btn ${"draw"===this._tool?"on":""}"
|
||
aria-pressed=${"draw"===this._tool?"true":"false"}
|
||
@click=${()=>this._activateMarkupTool("draw")}
|
||
title=${this._t("title.markup_add")}>
|
||
<ha-icon icon="mdi:vector-polyline-plus"></ha-icon>${this._t("markup.add")}
|
||
</button>
|
||
<button class="btn ${"column"===this._tool?"on":""}"
|
||
@click=${()=>this._activateMarkupTool("column")}
|
||
title=${this._t("title.markup_column")}>
|
||
<ha-icon icon="mdi:vector-square"></ha-icon>${this._t("markup.column")}
|
||
</button>
|
||
</span>
|
||
<button class="btn ${"merge"===this._tool?"on":""}"
|
||
@click=${()=>this._activateMarkupTool("merge")}
|
||
title=${this._t("title.markup_merge")}>
|
||
<ha-icon icon="mdi:vector-union"></ha-icon>${this._t("markup.merge")}
|
||
</button>
|
||
<button class="btn ${"split"===this._tool?"on":""}"
|
||
@click=${()=>this._activateMarkupTool("split")}
|
||
title=${this._t("title.markup_split")}>
|
||
<ha-icon icon="mdi:vector-polyline-remove"></ha-icon>${this._t("markup.split")}
|
||
</button>
|
||
<button class="btn ${"resize"===this._tool?"on":""}"
|
||
@click=${()=>this._activateMarkupTool("resize")}
|
||
title=${this._t("title.markup_resize")}>
|
||
<ha-icon icon="mdi:arrow-expand-all"></ha-icon>${this._t("markup.resize")}
|
||
</button>
|
||
${this._editorToolbarGroups.map(e=>this._renderEditorGroupLauncher(e))}
|
||
<button class="btn ${"wallthick"===this._tool?"on":""}"
|
||
@click=${()=>this._activateMarkupTool("wallthick")}
|
||
title=${this._t("title.markup_wallthick")}>
|
||
<ha-icon icon="mdi:wall"></ha-icon>${this._t("markup.wallthick")}
|
||
</button>
|
||
<button class="btn ${"delroom"===this._tool?"on":""}"
|
||
@click=${()=>this._activateMarkupTool("delroom")}
|
||
title=${this._t("title.markup_delroom")}>
|
||
<ha-icon icon="mdi:delete-outline"></ha-icon>${this._t("markup.delete_room")}
|
||
</button>
|
||
<button class="btn ghost" @click=${this._undoGeometry}
|
||
?disabled=${!e}
|
||
title=${i} aria-label=${i}>
|
||
<ha-icon icon="mdi:undo-variant" aria-hidden="true"></ha-icon>
|
||
</button>
|
||
<button class="btn ghost" @click=${this._redoGeometry}
|
||
?disabled=${!t}
|
||
title=${s} aria-label=${s}>
|
||
<ha-icon icon="mdi:redo-variant" aria-hidden="true"></ha-icon>
|
||
</button>
|
||
</div>
|
||
<div class="editbar-end">
|
||
<button class="btn barclose" title=${this._t("title.close_editor")}
|
||
data-editor-navigation="view"
|
||
@click=${()=>this._setMode("view")}>
|
||
<ha-icon icon="mdi:close"></ha-icon>
|
||
</button>
|
||
</div>
|
||
</div>`}_renderDevicesBar(){return q`<div class="editbar devbar">
|
||
<div class="editbar-tools" tabindex="-1" ?inert=${this._modeTransitionBusy}>
|
||
<ha-icon icon="mdi:tune-variant" class="warn"></ha-icon>
|
||
<button class="btn ${this._showAll?"on":""}" @click=${this._openDeviceInbox}
|
||
title=${this._t("device_inbox.title")}>
|
||
<ha-icon icon="mdi:devices"></ha-icon>${this._t("device_inbox.button")}
|
||
</button>
|
||
<button class="btn" @click=${this._openRulesDialog} title=${this._t("title.icon_rules")}>
|
||
<ha-icon icon="mdi:shape-plus-outline"></ha-icon>${this._t("devbar.rules")}
|
||
</button>
|
||
${this._editorToolbarGroups.map(e=>this._renderEditorGroupLauncher(e))}
|
||
</div>
|
||
<div class="editbar-end">
|
||
<button class="btn barclose" title=${this._t("title.close_editor")}
|
||
data-editor-navigation="view"
|
||
@click=${()=>this._setMode("view")}>
|
||
<ha-icon icon="mdi:close"></ha-icon>
|
||
</button>
|
||
</div>
|
||
</div>`}_renderDeviceInbox(){const e=this._deviceInbox,t=this._deviceInboxRows(),i=Object.fromEntries(["on_plan","available","hidden","readd"].map(e=>[e,t.filter(t=>t.category===e).length])),s=function(e,t,i,s=!1){const n=i.trim().toLocaleLowerCase();return e.filter(e=>e.category===t&&(!s||e.isNew)&&(!n||e.searchText.includes(n)))}(t,e.tab,e.search,"on_plan"===e.tab&&e.onlyNew),n=s.slice(0,e.limit),o=e=>this._t(`device_inbox.tab_${e}`),a=`device_inbox.empty_${e.tab}`;return q`<hp-dialog class="device-inbox-dialog" .hass=${this.hass}
|
||
.title=${this._t("device_inbox.title")} icon="mdi:devices" wide
|
||
@hp-close=${()=>this._deviceInbox=null}>
|
||
<div class="device-inbox" ?inert=${!!e.busy}>
|
||
<div class="device-inbox-head">
|
||
<input class="device-inbox-search" type="search" autofocus
|
||
placeholder=${this._t("device_inbox.search")} .value=${e.search}
|
||
@input=${t=>this._deviceInbox={...e,search:t.target.value,limit:100}} />
|
||
<button type="button" class="btn" @click=${()=>{this._deviceInboxReturn={...e},this._deviceInbox=null,this._openMarkerDialog(),this._markerDialog||this._closeMarkerDialog()}}>
|
||
<ha-icon icon="mdi:map-marker-plus-outline"></ha-icon>
|
||
${this._t("device_inbox.add_virtual")}
|
||
</button>
|
||
</div>
|
||
<div class="device-inbox-tabs" role="tablist" @keydown=${this._deviceInboxTabKey}>
|
||
${["on_plan","available","hidden","readd"].map(t=>q`
|
||
<button type="button" role="tab" aria-selected=${e.tab===t?"true":"false"}
|
||
class=${e.tab===t?"on":""}
|
||
@click=${()=>this._deviceInbox={...e,tab:t,limit:100,onlyNew:!1}}>
|
||
${o(t)} <span>${i[t]}</span>
|
||
</button>`)}
|
||
</div>
|
||
<div class="device-inbox-filters">
|
||
${"on_plan"===e.tab?q`<label>
|
||
<input type="checkbox" .checked=${e.onlyNew}
|
||
@change=${t=>this._deviceInbox={...e,onlyNew:t.target.checked,limit:100}} />${this._t("device_inbox.only_new")}
|
||
</label>`:G}
|
||
${"available"===e.tab?q`<label>
|
||
<input type="checkbox" .checked=${e.showEntities}
|
||
@change=${t=>{this._deviceInboxMemo=null,this._deviceInbox={...e,showEntities:t.target.checked,limit:100}}} />${this._t("device_inbox.show_entities")}
|
||
</label>`:G}
|
||
<label>
|
||
<input type="checkbox" .checked=${this._showAll}
|
||
@change=${e=>{this._showHidden=e.target.checked,this._deviceInboxMemo=null,this.requestUpdate()}} />${this._t("device_inbox.show_hidden")}
|
||
</label>
|
||
</div>
|
||
<div class="device-inbox-results" aria-live="polite">
|
||
${n.length?n.map(e=>{const t="on_plan"===e.category?e.canFind?q`<button type="button" class="btn" @click=${()=>this._findInboxDevice(e)}>
|
||
<ha-icon icon="mdi:crosshairs-gps"></ha-icon>${this._t("device_inbox.find")}</button>`:q`<button type="button" class="btn" @click=${()=>this._openInboxMarker(e)}
|
||
?disabled=${!e.canEdit}>${this._t("device_inbox.edit")}</button>`:"hidden"===e.category?q`<button type="button" class="btn" @click=${()=>this._setInboxHidden(e,!1)}
|
||
title=${e.canShow?"":this._t("device_inbox.show_disabled")}
|
||
?disabled=${!e.canShow}>${this._t("device_inbox.show")}</button>`:q`<button type="button" class="btn" @click=${()=>this._openInboxMarker(e,!0)}
|
||
?disabled=${!e.canAdd}>${this._t("readd"===e.category?"device_inbox.readd":"device_inbox.add")}</button>`,i="active"===e.status.kind?"":this._t(`device_inbox.status_${e.status.kind}`);return q`<article class="device-inbox-row" data-binding=${e.binding}
|
||
data-category=${e.category} data-status=${e.status.kind}>
|
||
<ha-icon class="device-inbox-icon" .icon=${e.icon}></ha-icon>
|
||
<div class="device-inbox-copy">
|
||
<div class="device-inbox-name"><b>${e.name}</b>
|
||
${e.isNew?q`<span class="device-inbox-new">${this._t("device_inbox.new")}</span>`:G}
|
||
</div>
|
||
<div class="device-inbox-meta">
|
||
${[e.model,e.integration,e.spaceName,e.areaName].filter(Boolean).join(" · ")}
|
||
</div>
|
||
<div class="device-inbox-reason">
|
||
${this._t(`device_inbox.reason_${e.reason}`)}
|
||
${i?q`<span class="device-inbox-status">${i}</span>`:G}
|
||
</div>
|
||
<code>${e.binding}</code>
|
||
</div>
|
||
<div class="device-inbox-actions">
|
||
${t}
|
||
${e.canEdit||e.canHide||"available"===e.category||"hidden"===e.category||this._bindingHasHaPage(e.binding)?q`
|
||
<details class="device-inbox-menu">
|
||
<summary class="btn ghost" aria-label=${this._t("device_inbox.more_actions")}
|
||
title=${this._t("device_inbox.more_actions")}>
|
||
<ha-icon icon="mdi:dots-vertical"></ha-icon>
|
||
</summary>
|
||
<div class="device-inbox-menu-items">
|
||
${e.canEdit&&("on_plan"!==e.category||e.canFind)?q`<button type="button" class="btn ghost" @click=${()=>this._openInboxMarker(e)}>
|
||
${this._t("device_inbox.edit")}</button>`:G}
|
||
${e.canHide?q`<button type="button" class="btn ghost"
|
||
@click=${()=>this._setInboxHidden(e,!0)}>${this._t("device_inbox.hide")}</button>`:G}
|
||
${"available"===e.category?q`<button type="button" class="btn ghost"
|
||
@click=${()=>this._setInboxHidden(e,!0)}>${this._t("device_inbox.hide_available")}</button>`:G}
|
||
${"hidden"===e.category?q`<button type="button" class="btn ghost"
|
||
title=${e.canFind?"":this._t("device_inbox.find_hidden_hint")}
|
||
?disabled=${!e.canFind} @click=${()=>this._findInboxDevice(e)}>
|
||
<ha-icon icon="mdi:crosshairs-gps"></ha-icon>${this._t("device_inbox.find")}</button>`:G}
|
||
${this._bindingHasHaPage(e.binding)?q`<button type="button" class="btn ghost"
|
||
@click=${()=>this._openBindingInHa(e.binding)}>${this._t("btn.open_in_ha")}</button>`:G}
|
||
</div>
|
||
</details>`:G}
|
||
</div>
|
||
</article>`}):q`<div class="device-inbox-empty">${this._t(a)}</div>`}
|
||
</div>
|
||
${s.length>n.length?q`<button type="button" class="btn device-inbox-more"
|
||
@click=${()=>this._deviceInbox={...e,limit:e.limit+100}}>
|
||
${this._t("device_inbox.show_more")} (${s.length-n.length})
|
||
</button>`:G}
|
||
</div>
|
||
<div slot="footer" class="row">
|
||
<button type="button" class="btn ghost" @click=${()=>this._deviceInbox=null}>
|
||
${this._t("btn.close")}</button>
|
||
</div>
|
||
</hp-dialog>`}_cardEntities(e){const t=this._planHass,i=[],s=new Set,n=e=>{if(!e||s.has(e)||!t.states[e])return;const n=t.entities[e];if("config"===n?.entity_category||"diagnostic"===n?.entity_category)return;s.add(e);const o=e.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(o)?i.push({eid:e,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(o)?i.push({eid:e,kind:"open"}):["sensor","binary_sensor","number","select"].includes(o)&&i.push({eid:e,kind:"value"})};for(const i of ho(t,this._devices,null,this._virtualLights))if(i.device.id===e.id)for(const e of[...i.serviceEids,...i.stateEids])n(e);e.primary&&n(e.primary);for(const t of e.entities)n(t);return i.slice(0,12)}_cardToggle(e){const t=e.split(".")[0];"lock"!==t&&"alarm_control_panel"!==t&&this._planEntityAvailable(e)&&this.hass.callService("homeassistant","toggle",{entity_id:e}).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))}_renderInfoCard(){const e=this._infoCard,t=e.primary?this.hass.states[e.primary]:void 0,i=t?Xi(this.hass,e.primary)?.text??t.state:null,s=(e.controls??e.marker?.controls??[]).filter(Ms).filter(e=>this._planEntityAvailable(e));return q`<hp-dialog .hass=${this.hass} .title=${e.name} .icon=${e.icon} wide
|
||
dismiss-on-scrim @hp-close=${this._closeInfoCard}>
|
||
<div class="body">
|
||
${(()=>{const t=this._cardEntities(e);return t.length?q`<div class="entlist">
|
||
${t.map(({eid:e,kind:t})=>{const i=this.hass.states[e],s=this.hass.entities[e]?.name||i?.attributes?.friendly_name||e,n=i?Xi(this.hass,e)?.text??i.state:"",o="on"===i?.state||["open","unlocked","playing","cleaning"].includes(i?.state);return q`<div class="entrow ${o?"on":""}">
|
||
<ha-icon icon=${vs(tt(s,"",this._iconRules),e.split(".")[0],i?.attributes?.device_class,i?.state,!1)}></ha-icon>
|
||
<span class="en">${s}</span>
|
||
${"toggle"===t?q`<button class="entbtn ${o?"on":""}"
|
||
@click=${()=>this._cardToggle(e)}>${n}</button>`:"open"===t?q`<button class="entbtn"
|
||
@click=${()=>{this._closeInfoCard(),this._openMoreInfo(e)}}>${n}</button>`:q`<span class="ev">${n}</span>`}
|
||
</div>`})}
|
||
</div>`:G})()}
|
||
${e.model?q`<div class="inforow"><span class="k">${this._t("info.model")}</span><span>${e.model}</span></div>`:G}
|
||
${i&&!this._cardEntities(e).length?q`<div class="inforow"><span class="k">${this._t("info.state")}</span><span>${i}</span></div>`:G}
|
||
${Hi(e.link)?q`<div class="inforow"><span class="k">${this._t("info.link")}</span>
|
||
<a href="${Hi(e.link)}" target="_blank" rel="noreferrer noopener">${e.link}</a></div>`:G}
|
||
${e.description?q`<div class="infodesc">${e.description}</div>`:G}
|
||
${e.pdfs&&e.pdfs.length?q`<div class="inforow"><span class="k">${this._t("info.manuals")}</span><span class="pdflist">
|
||
${e.pdfs.map(e=>q`<a class="pdf" href="${Hi(this._display(e.url))||"#"}" target="_blank" rel="noreferrer noopener">
|
||
<ha-icon icon="mdi:file-pdf-box"></ha-icon>${e.name}</a>`)}</span></div>`:G}
|
||
${s.length?q`<div class="inforow"><span class="k">${this._t("info.controls")}</span>
|
||
<span class="ctrlstates">
|
||
${s.map(e=>{const t=this.hass.states[e],i="on"===t?.state;return q`<span class="ctrlstate ${i?"on":""}">
|
||
<ha-icon icon=${i?"mdi:lightbulb-on":"mdi:lightbulb-outline"}></ha-icon>
|
||
${t?.attributes?.friendly_name||e}</span>`})}
|
||
</span></div>`:G}
|
||
${e.model||i||e.link||e.description||e.pdfs&&e.pdfs.length||s.length?G:q`<div class="infodesc muted">${this._t("info.none")}</div>`}
|
||
</div>
|
||
<div class="row infofooter" slot="footer">
|
||
<button class="btn" @click=${()=>{const t=e;this._closeInfoCard(),this._openMarkerDialog(t)}}>
|
||
<ha-icon icon="mdi:pencil"></ha-icon>${this._t("btn.edit")}
|
||
</button>
|
||
${e.primary?q`<button class="btn" @click=${()=>{const t=e.primary;this._closeInfoCard(),this._openMoreInfo(t)}}>
|
||
<ha-icon icon="mdi:open-in-new"></ha-icon>${this._t("btn.open_in_ha")}
|
||
</button>`:G}
|
||
<button class="btn ghost infofooter-close" @click=${this._closeInfoCard}>${this._t("btn.close")}</button>
|
||
</div>
|
||
</hp-dialog>`}_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`<hp-dialog .hass=${this.hass}
|
||
.title=${e.devId?this._t("info.device_header"):this._t("marker.new_device")}
|
||
icon="mdi:shape-plus" wide @hp-close=${this._closeMarkerDialog}>
|
||
<div class="body">
|
||
${"ha_disabled"===s?.kind?q`<div class="habindingbanner" role="status">
|
||
<ha-icon icon="mdi:power-plug-off-outline"></ha-icon>
|
||
<span>${this._t(`marker.ha_disabled_${s.reason}`)}</span>
|
||
${n?q`<button class="btn ghost" type="button" @click=${()=>this._openBindingInHa(e.binding)}>
|
||
<ha-icon icon="mdi:open-in-new"></ha-icon>${this._t("btn.open_in_ha")}
|
||
</button>`:G}
|
||
</div>`:"unverified"===s?.kind&&e.binding?q`<div class="habindingbanner limited" role="status">
|
||
<ha-icon icon="mdi:shield-alert-outline"></ha-icon>
|
||
<span>${this._t("marker.ha_registry_limited")}</span>
|
||
</div>`:G}
|
||
<label>${this._t("marker.name_label")}</label>
|
||
<input class="namein" type="text" placeholder=${this._t("marker.name_ph")}
|
||
.value=${e.name}
|
||
@input=${t=>this._markerDialog={...e,name:t.target.value}} />
|
||
|
||
<label>${this._t("marker.binding_label")}</label>
|
||
<div class="bindsel">
|
||
<label class="srcrow">
|
||
<input type="radio" name="bmode" .checked=${"virtual"===e.bindingMode}
|
||
@change=${()=>{const t={...e,bindingMode:"virtual",binding:"virtual",bindingOpen:!1,controls:Zn("virtual",e.controls),autoIcon:this._autoIconForBinding("virtual")};this._markerDialog=this._announceToggleDraft({...t,...this._valueBadgeForBinding(t,"virtual")})}} />
|
||
<span>${this._t("marker.virtual_option")}</span>
|
||
</label>
|
||
<div class="bindharow">
|
||
<label class="srcrow">
|
||
<input type="radio" name="bmode" .checked=${"ha"===e.bindingMode}
|
||
@change=${()=>this._markerDialog=this._announceToggleDraft({...e,bindingMode:"ha",binding:"virtual"===e.binding?"":e.binding,bindingOpen:"virtual"===e.binding||!e.binding})} />
|
||
<span>${this._t("marker.from_ha_option")}</span>
|
||
</label>
|
||
<label class="srcrow inline entcheck" title=${this._t("marker.show_entities_tip")}>
|
||
${this._boolInput(e.showEntities,t=>this._markerDialog={...e,showEntities:t},"ha"!==e.bindingMode)}
|
||
<span>${this._t("marker.show_entities")}</span>
|
||
</label>
|
||
</div>
|
||
${"ha"===e.bindingMode?q`<button class="dropbtn ${e.bindingOpen?"open":""}"
|
||
@click=${()=>this._markerDialog={...e,bindingOpen:!e.bindingOpen}}>
|
||
${N?q`<b>${N}</b><span class="ref">${e.binding}${"ha_disabled"===s?.kind?` · ${this._t("marker.binding_disabled")}`:""}</span>`:q`<span class="muted">${this._t("marker.pick_ph")}</span>`}
|
||
<ha-icon icon=${e.bindingOpen?"mdi:chevron-up":"mdi:chevron-down"}></ha-icon>
|
||
</button>
|
||
${e.bindingOpen?q`<div class="droppanel">
|
||
<input class="namein" type="text" placeholder=${this._t("marker.search_ph")}
|
||
.value=${e.bindingFilter}
|
||
@input=${t=>this._markerDialog={...e,bindingFilter:t.target.value}} />
|
||
<div class="candlist">
|
||
${i.map(t=>q`<div class="cand ${t.value===e.binding?"sel":""}"
|
||
@click=${()=>{const i={...e,binding:t.value,bindingOpen:!1,controls:Zn(t.value,e.controls,this._bindingEntities(t.value)),autoIcon:this._autoIconForBinding(t.value)};this._markerDialog=this._announceToggleDraft({...i,...this._valueBadgeForBinding(i,t.value)})}}>
|
||
<span class="cl">${t.label}</span><span class="cs">${t.sub}</span>
|
||
</div>`)}
|
||
${i.length?G:q`<div class="cand muted">${this._t("marker.nothing_found")}</div>`}
|
||
</div>
|
||
</div>`:G}`:G}
|
||
</div>
|
||
|
||
<label for="marker-room">${this._t("marker.room_label")}${t?"":this._t("marker.room_override")}</label>
|
||
<select id="marker-room" class="areasel"
|
||
@change=${t=>this._markerDialog={...e,room:t.target.value}}>
|
||
<option value="" ?selected=${!e.room}>
|
||
${t?this._t("marker.room_choose"):this._t("marker.room_auto")}
|
||
</option>
|
||
${this._allRoomsFlat().map(t=>q`<option value=${t.value} ?selected=${t.value===e.room}>${t.label}</option>`)}
|
||
</select>
|
||
|
||
${this._renderVacSection(e)}
|
||
|
||
<label>${this._t("marker.tap_label")}</label>
|
||
<select id="marker-tap-action" class="areasel"
|
||
aria-describedby=${"toggle"===a?"marker-toggle-hint":G}
|
||
@change=${t=>{const i={...e,tapAction:t.target.value,tapActionTouched:!0};this._markerDialog=this._announceToggleDraft(i)}}>
|
||
${ji.map(e=>[e,"tap."+e.replace("-","_")]).map(([e,t])=>q`<option value=${e} ?selected=${e===a}>
|
||
${this._t(t)}
|
||
</option>`)}
|
||
</select>
|
||
${"toggle"===a&&(R.length>1||A)?q`<div class="markerhelpfield markertoggleentity">
|
||
<div class="markerhelplabel">
|
||
<label for="marker-toggle-entity">${this._t("marker.toggle_entity_label")}</label>
|
||
${this._help("marker.toggle_entity.help")}
|
||
</div>
|
||
<select id="marker-toggle-entity" class="areasel"
|
||
@change=${t=>{const i={...e,toggleEntity:t.target.value,toggleEntityTouched:!0};this._markerDialog=this._announceToggleDraft(i)}}>
|
||
<option value="" ?selected=${A||!e.toggleEntity}>
|
||
${this._t("marker.toggle_entity_auto",{entity:F||this._t("marker.toggle_entity_none")})}
|
||
</option>
|
||
${R.map(t=>q`<option value=${t}
|
||
?selected=${!A&&t===e.toggleEntity}>
|
||
${this.hass.states[t]?.attributes?.friendly_name||this._fullRegistryHass.entities[t]?.name||t} · ${t}
|
||
</option>`)}
|
||
</select>
|
||
${A?q`<p class="muted markerlightwarning" role="status">
|
||
<ha-icon icon="mdi:alert-outline"></ha-icon>
|
||
${this._t("marker.toggle_entity_missing",{entity:e.toggleEntity,fallback:F||this._t("marker.toggle_entity_none")})}
|
||
</p>`:G}
|
||
</div>`:G}
|
||
${"toggle"===a?q`<div id="marker-toggle-hint" class="rhint togglehint">
|
||
${h.map(e=>q`<div>${e}</div>`)}
|
||
</div>
|
||
<div class="sr-only" role="status" aria-live="polite">${e.tapHintAnnouncement}</div>`:G}
|
||
${"run"===a?(()=>{const t=e.runFilter.trim().toLowerCase(),i=this._runCandidates().filter(e=>!t||e.label.toLowerCase().includes(t)||e.value.includes(t)),s=e.tapTarget?this._runCandidates().find(t=>t.value===e.tapTarget):null;return q`
|
||
<label>${this._t("marker.run_target_label")}</label>
|
||
${e.tapTarget&&!s?q`<div class="rhint">${this._t("marker.run_target_gone",{id:e.tapTarget})}</div>`:G}
|
||
<input class="namein" type="text" placeholder=${this._t("marker.run_search_ph")}
|
||
.value=${s?s.label:e.runFilter}
|
||
@focus=${e=>{e.target.select()}}
|
||
@input=${t=>this._markerDialog={...e,runFilter:t.target.value,tapTarget:""}} />
|
||
${s?G:q`<div class="candlist">
|
||
${i.slice(0,40).map(t=>q`<div class="cand ${t.value===e.tapTarget?"sel":""}"
|
||
@click=${()=>this._markerDialog={...e,tapTarget:t.value,runFilter:""}}>
|
||
<span class="cl">${t.label}</span><span class="cs">${t.sub}</span>
|
||
</div>`)}
|
||
${i.length?G:q`<div class="cand muted">${this._t("marker.nothing_found")}</div>`}
|
||
</div>`}`})():G}
|
||
${"run"===a||"toggle"===a?q`<label class="srcrow" title=${this._t("marker.tap_confirm_tip")}>
|
||
${this._boolInput(e.tapConfirm,t=>this._markerDialog={...e,tapConfirm:t})}
|
||
<span>${this._t("marker.tap_confirm")}</span>
|
||
</label>`:G}
|
||
|
||
<label>${this._t("marker.controls_label")}</label>
|
||
<div class="rhint">${this._t("marker.controls_hint")}</div>
|
||
${e.controls.length?q`<div class="ctrlchips">
|
||
${e.controls.map(t=>{const i=this._controlRefInfo(t);return q`<span class="ctrlchip ${i.warning?"warning":""}" title=${i.sub}>
|
||
<ha-icon icon=${i.icon}></ha-icon>${i.label}
|
||
<ha-icon icon="mdi:close" @click=${()=>this._markerDialog=this._announceToggleDraft({...e,controls:e.controls.filter(e=>e!==t)})}></ha-icon>
|
||
</span>`})}
|
||
</div>`:G}
|
||
<input class="namein" type="text" placeholder=${this._t("marker.controls_filter")}
|
||
.value=${e.controlsFilter}
|
||
@input=${t=>this._markerDialog={...e,controlsFilter:t.target.value}} />
|
||
${e.controlsFilter.trim()?q`<div class="ctrllist">
|
||
${this._controlCandidates(e).map(t=>q`<button class="ctrlopt"
|
||
@click=${()=>this._addControlRef(e,t.value)}>
|
||
<ha-icon icon=${t.icon}></ha-icon>
|
||
${t.label}
|
||
<span class="sub">${t.sub}</span>
|
||
</button>`)}
|
||
</div>`:G}
|
||
|
||
${this._bindingHasClimate(e.binding)?q`<label class="srcrow climrow" title=${this._t("marker.use_climate_temp_tip")}>
|
||
${this._boolInput(e.useClimateTemp,t=>this._markerDialog={...e,useClimateTemp:t})}
|
||
<span>${this._t("marker.use_climate_temp")}</span>
|
||
</label>`:G}
|
||
<fieldset class="markerlightgroup">
|
||
<legend><span>${this._t("marker.light_role_label")}</span>${this._help("marker.light_role.help")}</legend>
|
||
<div class="markerradios" role="radiogroup" aria-label=${this._t("marker.light_role_label")}>
|
||
<label class="srcrow"><input type="radio" name="marker-light-role" value="auto"
|
||
.checked=${"auto"===e.lightRole} @change=${()=>this._setMarkerLightRole("auto")} />
|
||
<span>${this._t(w?"marker.light_role_auto_yes":"marker.light_role_auto_no")}</span></label>
|
||
<label class="srcrow"><input type="radio" name="marker-light-role" value="always"
|
||
.checked=${"always"===e.lightRole} @change=${()=>this._setMarkerLightRole("always")} />
|
||
<span>${this._t("marker.light_role_always")}</span></label>
|
||
<label class="srcrow"><input type="radio" name="marker-light-role" value="never"
|
||
.checked=${"never"===e.lightRole} @change=${()=>this._setMarkerLightRole("never")} />
|
||
<span>${this._t("marker.light_role_never")}</span></label>
|
||
</div>
|
||
</fieldset>
|
||
|
||
${"always"===e.lightRole&&(P.length>1||I)?q`<div class="markerhelpfield markerleadingentity">
|
||
<div class="markerhelplabel">
|
||
<label for="marker-light-entity">${this._t("marker.light_entity_label")}</label>
|
||
${this._help("marker.light_entity.help")}
|
||
</div>
|
||
<select id="marker-light-entity" class="areasel"
|
||
@change=${t=>this._markerDialog={...e,lightEntity:t.target.value,lightEntityTouched:!0}}>
|
||
<option value="" ?selected=${I||!e.lightEntity}>
|
||
${this._t("marker.light_entity_auto",{entity:T||this._t("marker.light_entity_none")})}
|
||
</option>
|
||
${P.map(t=>q`<option value=${t}
|
||
?selected=${!I&&t===e.lightEntity}>
|
||
${this.hass.states[t]?.attributes?.friendly_name||this._fullRegistryHass.entities[t]?.name||t} · ${t}
|
||
</option>`)}
|
||
</select>
|
||
${I?q`<p class="muted markerlightwarning" role="status">
|
||
<ha-icon icon="mdi:alert-outline"></ha-icon>
|
||
${this._t("marker.light_entity_missing",{entity:e.lightEntity,fallback:T||"—"})}
|
||
</p>`:G}
|
||
</div>`:G}
|
||
|
||
<fieldset class="markerlightgroup" ?disabled=${$}>
|
||
<legend><span>${this._t("marker.glow_color_label")}</span>${this._help("marker.glow_mode.help")}</legend>
|
||
<div class="markerradios" role="radiogroup" aria-label=${this._t("marker.glow_color_label")}>
|
||
<label class="srcrow"><input type="radio" name="marker-glow-mode" value="auto"
|
||
.checked=${"auto"===C} ?disabled=${S}
|
||
aria-describedby=${S?"marker-glow-disabled-hint":G}
|
||
@change=${()=>this._setMarkerGlowMode("auto")} />
|
||
<span>${this._t("marker.glow_mode_auto")}</span></label>
|
||
<label class="srcrow"><input type="radio" name="marker-glow-mode" value="color"
|
||
.checked=${"color"===C} ?disabled=${$}
|
||
aria-describedby=${$?"marker-glow-disabled-hint":G}
|
||
@change=${()=>this._setMarkerGlowMode("color")} />
|
||
<span>${this._t("marker.glow_mode_color")}</span></label>
|
||
<label class="srcrow"><input type="radio" name="marker-glow-mode" value="fixed"
|
||
.checked=${"fixed"===C} ?disabled=${$}
|
||
aria-describedby=${$?"marker-glow-disabled-hint":G}
|
||
@change=${()=>this._setMarkerGlowMode("fixed")} />
|
||
<span>${this._t("marker.glow_mode_fixed")}</span></label>
|
||
</div>
|
||
${"auto"!==C?q`<div class="colorrow markerglowvalue">
|
||
<hp-color-opacity .label=${this._t("marker.glow_color")}
|
||
.color=${e.glowColor} .opacity=${1} .showOpacity=${!1}
|
||
.pickerLabels=${this._colorPickerLabels}
|
||
.disabled=${$}
|
||
@hp-color-opacity-change=${t=>{this._markerDialog={...e,glowMode:C,glowColor:t.detail.color,glowColorDrafted:!0,glowTouched:!0}}}></hp-color-opacity>
|
||
${"fixed"===C?q`
|
||
<span class="opl">${this._t("marker.glow_brightness")}</span>
|
||
${this._rangeInput(1,100,1,e.glowBrightness,t=>{this._markerDialog={...e,glowMode:C,glowBrightness:t,glowBrightnessDrafted:!0,glowTouched:!0}},$,this._t("marker.glow_brightness"))}
|
||
<span class="opv">${Math.round(e.glowBrightness)}%</span>`:G}
|
||
</div>`:G}
|
||
</fieldset>
|
||
<div class="markerhelpfield">
|
||
<div class="markerhelplabel">
|
||
<label for="marker-glow-radius">${this._t("marker.glow_radius_label")}</label>
|
||
${this._help("marker.glow_radius.help")}
|
||
</div>
|
||
<div class="colorrow">
|
||
<input id="marker-glow-radius" class="tempin" type="number" min="0.5" step="0.5"
|
||
placeholder=${this._glowRadiusPlaceholder} ?disabled=${$}
|
||
aria-describedby=${$||M?"marker-glow-disabled-hint":G}
|
||
.value=${e.glowRadius}
|
||
@input=${t=>this._markerDialog={...e,glowRadius:t.target.value}} />
|
||
<span class="opl">${this._imperial?this._t("gs.unit_ft"):this._t("gs.unit_m")}</span>
|
||
</div>
|
||
</div>
|
||
${$||M?q`<p id="marker-glow-disabled-hint" class="muted markerlightdisabled" role="note">
|
||
<ha-icon icon="mdi:information-outline"></ha-icon>${D}
|
||
</p>`:G}
|
||
|
||
<label>${this._t("marker.icon_label")}</label>
|
||
${customElements.get("ha-icon-picker")?q`<ha-icon-picker .hass=${this.hass} .value=${e.icon||e.autoIcon}
|
||
.placeholder=${e.autoIcon||void 0}
|
||
.fallbackPath=${void 0}
|
||
@value-changed=${t=>{const i=t.detail.value||"";(e.icon||i!==e.autoIcon)&&(this._markerDialog={...e,icon:i})}}></ha-icon-picker>`:q`<input class="namein" type="text"
|
||
placeholder=${e.autoIcon||this._t("marker.icon_ph")}
|
||
.value=${e.icon}
|
||
@input=${t=>this._markerDialog={...e,icon:t.target.value}} />`}
|
||
${!e.icon&&e.autoIcon?q`<p class="muted iconauto"><ha-icon icon=${e.autoIcon}></ha-icon>
|
||
<span>${this._t("marker.icon_auto",{icon:e.autoIcon})}</span>
|
||
<button class="btn ghost" type="button"
|
||
@click=${()=>this._markerDialog={...e,icon:e.autoIcon}}>
|
||
${this._t("marker.icon_pin_auto")}
|
||
</button></p>`:G}
|
||
|
||
<label for="marker-display">${this._t("marker.display_label")}</label>
|
||
<select id="marker-display" class="areasel"
|
||
@change=${t=>this._markerDialog={...e,display:Bi(t.target.value)}}>
|
||
${Li.map(t=>q`<option value=${t} ?selected=${t===e.display}>
|
||
${this._t(mv[t])}
|
||
</option>`)}
|
||
</select>
|
||
<p class="muted">${this._t(gv[e.display])}</p>
|
||
${"static_icon"===e.display&&this._bindingHasAlarm(e.binding)?q`<div class="habindingbanner" role="note">
|
||
<ha-icon icon="mdi:alert-outline"></ha-icon>
|
||
<span>${this._t("marker.static_alarm_warning")}</span>
|
||
</div>`:G}
|
||
<fieldset class="markerlightgroup markerbadgegroup">
|
||
<legend><span>${this._t("marker.value_badge_title")}</span>${this._help("marker.value_badge.help")}</legend>
|
||
<label class="srcrow">
|
||
${this._boolInput(_,t=>{const i=m||u;this._markerDialog={...e,valueBadgeEnabled:t&&!!i,valueBadgeSource:i,valueBadgeTouched:!0}},"static_icon"===e.display||!p.length&&!e.valueBadgeSource)}
|
||
<span>${this._t("marker.value_badge_enabled")}</span>
|
||
</label>
|
||
${"static_icon"===e.display?q`<p class="muted markerlightdisabled" role="note">
|
||
<ha-icon icon="mdi:information-outline"></ha-icon>${this._t("marker.value_badge_static")}
|
||
</p>`:p.length||e.valueBadgeSource?G:q`<p class="muted markerlightdisabled" role="note">
|
||
<ha-icon icon="mdi:information-outline"></ha-icon>${this._t("marker.value_badge_empty")}
|
||
</p>`}
|
||
${_?q`
|
||
<div class="markerhelplabel">
|
||
<label for="marker-value-badge-source">${this._t("marker.value_badge_source")}</label>
|
||
${this._help("marker.value_badge_source.help")}
|
||
</div>
|
||
<select id="marker-value-badge-source" class="areasel"
|
||
@change=${t=>this._markerDialog={...e,valueBadgeSource:zo(t.target.value),valueBadgeEnabled:!0,valueBadgeTouched:!0}}>
|
||
${v?q`<option value=${f} selected>
|
||
${this._t("marker.value_badge_missing")}
|
||
</option>`:G}
|
||
${p.map(e=>q`<option value=${e.key}
|
||
?selected=${e.key===f}
|
||
title=${e.technical}>
|
||
${this._valueBadgeCandidateLabel(e)} · ${e.value}
|
||
</option>`)}
|
||
</select>
|
||
${b?q`<p class="muted markerbadgetechnical"><code>${b.technical}</code></p>`:G}
|
||
${v?q`<p class="muted markerlightwarning" role="status">
|
||
<ha-icon icon="mdi:alert-outline"></ha-icon>${this._t("marker.value_badge_missing_hint")}
|
||
</p>`:G}
|
||
${"value"===e.display&&f===y?q`<p class="muted markerlightwarning" role="note">
|
||
<ha-icon icon="mdi:information-outline"></ha-icon>${this._t("marker.value_badge_duplicate")}
|
||
</p>`:G}
|
||
<div class="markerhelplabel">
|
||
<label for="marker-value-badge-position">${this._t("marker.value_badge_position")}</label>
|
||
${this._help("marker.value_badge_position.help")}
|
||
</div>
|
||
<select id="marker-value-badge-position" class="areasel"
|
||
@change=${t=>this._markerDialog={...e,valueBadgeEnabled:_,valueBadgeSource:m,valueBadgePosition:t.target.value,valueBadgeTouched:!0}}>
|
||
${["right","bottom","left","top"].map(e=>q`
|
||
<option value=${e} ?selected=${e===g}>
|
||
${this._t(`marker.value_badge_${e}`)}
|
||
</option>`)}
|
||
</select>
|
||
`:G}
|
||
</fieldset>
|
||
${d?q`<hp-device-preview
|
||
.hass=${this.hass}
|
||
.presentation=${d}
|
||
.registry=${this._haRegistry}
|
||
.deviceName=${e.name.trim()||o?.name||N||""}>
|
||
</hp-device-preview>`:q`<div class="devicepreview-empty">
|
||
<ha-icon icon="mdi:eye-outline"></ha-icon>
|
||
<span>${this._t("marker.preview.select_source")}</span>
|
||
</div>`}
|
||
${"icon_ripple"===e.display?q`<div class="colorrow ripple-colorrow">
|
||
<hp-color-opacity .label=${this._t("marker.activity_color")}
|
||
.pickerLabels=${this._colorPickerLabels}
|
||
.color=${e.rippleColor||"#3ea6ff"} .opacity=${1} .showOpacity=${!1}
|
||
@hp-color-opacity-change=${t=>{this._markerDialog={...e,rippleColor:t.detail.color}}}></hp-color-opacity>
|
||
</div>
|
||
<div class="colorrow ripple-sizerow">
|
||
<span class="opl">${this._t("marker.ripple_size")}</span>
|
||
${this._rangeInput(1,8,.5,e.rippleSize,t=>this._markerDialog={...e,rippleSize:t})}
|
||
<span class="opv">×${e.rippleSize}</span>
|
||
</div>
|
||
<p class="muted" role="note">${this._t("marker.activity_alarm_note")}</p>`:G}
|
||
|
||
<label>${this._t("marker.size_label")}</label>
|
||
<div class="colorrow">
|
||
${this._rangeInput(.5,3,.1,e.size,t=>this._markerDialog={...e,size:t})}
|
||
<span class="opv">×${e.size.toFixed(1)}</span>
|
||
<span class="opl">${this._t("marker.angle_label")}</span>
|
||
${""}
|
||
${this._rangeInput(0,355,5,e.angle,t=>this._markerDialog={...e,angle:t})}
|
||
<span class="opv">${e.angle}°</span>
|
||
</div>
|
||
|
||
<label>${this._t("marker.model_label")}</label>
|
||
<input class="namein" type="text" placeholder=${this._t("marker.model_ph")}
|
||
.value=${e.model}
|
||
@input=${t=>this._markerDialog={...e,model:t.target.value}} />
|
||
|
||
<label>${this._t("marker.link_label")}</label>
|
||
<input class="namein" type="url" placeholder="https://…"
|
||
.value=${e.link}
|
||
@input=${t=>this._markerDialog={...e,link:t.target.value}} />
|
||
|
||
<label>${this._t("marker.desc_label")}</label>
|
||
<textarea class="descin" rows="4" placeholder=${this._t("marker.desc_ph")}
|
||
.value=${e.description}
|
||
@input=${t=>this._markerDialog={...e,description:t.target.value}}></textarea>
|
||
|
||
<label>${this._t("marker.manuals_label")}</label>
|
||
<div class="pdfedit">
|
||
${e.pdfs.map(e=>q`<span class="pdftag"><ha-icon icon="mdi:file-pdf-box"></ha-icon>
|
||
<a href="${Hi(this._display(e.url))||"#"}" target="_blank" rel="noreferrer noopener">${e.name}</a>
|
||
<ha-icon class="x" icon="mdi:close" @click=${()=>this._removeMarkerPdf(e.url)}></ha-icon></span>`)}
|
||
<span class="fileupload">
|
||
<button class="btn filebtn" type="button" @click=${e=>e.currentTarget.nextElementSibling?.click()}>
|
||
<ha-icon icon="mdi:paperclip"></ha-icon>${this._t("btn.attach")}
|
||
</button>
|
||
<input type="file" hidden multiple accept=".pdf,.png,.jpg,.jpeg,.webp,.txt,application/pdf"
|
||
@change=${e=>this._pickMarkerFiles(e)} />
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="row markerfooter" slot="footer">
|
||
<div class="markeractions">
|
||
${e.devId?q`<button class="btn" type="button"
|
||
?disabled=${e.busy}
|
||
aria-pressed=${e.hideFromPlan||"ha_disabled"===s?.kind?"true":"false"}
|
||
title=${this._t(e.hideFromPlan||"ha_disabled"===s?.kind?"marker.show_tip":"marker.hide_tip")}
|
||
@click=${this._toggleMarkerDialogVisibility}>
|
||
<ha-icon icon=${e.hideFromPlan||"ha_disabled"===s?.kind?"mdi:eye-outline":"mdi:eye-off-outline"}></ha-icon>
|
||
${this._t(e.hideFromPlan||"ha_disabled"===s?.kind?"marker.show":"marker.hide")}
|
||
</button>`:G}
|
||
${e.devId?q`<button class="btn danger" type="button" ?disabled=${e.busy}
|
||
title=${this._t("marker.delete_tip")} @click=${this._deleteMarker}>
|
||
<ha-icon icon="mdi:delete-outline"></ha-icon>${this._t("btn.delete")}
|
||
</button>`:G}
|
||
</div>
|
||
<div class="markersaveactions">
|
||
<button class="btn ghost" ?disabled=${e.busy}
|
||
@click=${this._closeMarkerDialog}>${this._t("btn.cancel")}</button>
|
||
<button class="btn on" @click=${this._saveMarker}
|
||
?disabled=${e.busy||"ha"===e.bindingMode&&(!e.binding||"virtual"===e.binding||!e.devId&&"active"!==s?.kind)}
|
||
title=${"ha"!==e.bindingMode||e.binding&&"virtual"!==e.binding?"":this._t("marker.pick_ph")}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${e.busy?"…":this._t("btn.save")}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</hp-dialog>`}_renderSpaceDialog(){const e=this._spaceDialog,t=this._importTotal>0&&"create"===e.mode?this._t("import.progress",{i:this._importTotal-this._importQueue.length,n:this._importTotal}):"",i=()=>{this._spaceDialog=null,this._importQueue=[],this._importTotal=0};return q`<hp-dialog .hass=${this.hass}
|
||
.title=${`${"create"===e.mode?this._t("space.new"):this._t("space.header")}${t?` · ${t}`:""}`}
|
||
icon="mdi:floor-plan" wide @hp-close=${i}>
|
||
<div class="body">
|
||
<label>${this._t("space.title_label")}</label>
|
||
<input class="namein" type="text" placeholder=${this._t("space.title_ph")}
|
||
.value=${e.title}
|
||
@input=${t=>this._spaceDialog={...e,title:t.target.value}} />
|
||
<label>${this._t("space.plan_label")}</label>
|
||
<label class="srcrow">
|
||
<input type="radio" name="plansrc" .checked=${"file"===e.source}
|
||
@change=${()=>this._spaceDialog=Zd(e,"file")} />
|
||
<span>${this._t("space.source_file")}</span>
|
||
</label>
|
||
${"file"===e.source?q`<div class="planrow">
|
||
${e.planFile?q`<span class="planname">${e.planFile.name}</span>`:e.planUrl?q`<img class="planprev" src=${this._display(e.planUrl)} alt=${this._t("space.plan_alt")} />`:q`<span class="planname muted">${this._t("space.no_plan")}</span>`}
|
||
<span class="fileupload">
|
||
<button class="btn filebtn" type="button" @click=${e=>e.currentTarget.nextElementSibling?.click()}>
|
||
<ha-icon icon="mdi:upload"></ha-icon>${e.planUrl||e.planFile?this._t("btn.replace"):this._t("btn.upload")}
|
||
</button>
|
||
<input type="file" hidden accept=".svg,.png,.jpg,.jpeg,.webp,image/svg+xml,image/png,image/jpeg,image/webp"
|
||
@change=${e=>this._pickPlanFile(e)} />
|
||
</span>
|
||
<button class="btn ghost" @click=${this._toggleServerPlans}
|
||
title=${this._t("space.pick_saved_hint")}>
|
||
<ha-icon icon="mdi:folder-image"></ha-icon>${this._t("space.pick_saved")}
|
||
</button>
|
||
</div>
|
||
${e.pickSaved?this._renderServerPlans(e):G}`:G}
|
||
<label class="srcrow">
|
||
<input type="radio" name="plansrc" .checked=${"draw"===e.source}
|
||
@change=${()=>this._spaceDialog=Zd(e,"draw")} />
|
||
<span>${this._t("space.source_draw")}</span>
|
||
</label>
|
||
|
||
<label>${this._t("space.scale_label")}</label>
|
||
<div class="colorrow">
|
||
<input class="namein tempin" type="number"
|
||
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}}} />
|
||
<span class="opl">${this._t(this._imperial?"space.scale_unit_imperial":"space.scale_unit")}</span>
|
||
</div>
|
||
|
||
<label class="dispsection">${this._t("space.display_section")}</label>
|
||
<label class="srcrow">
|
||
${this._boolInput(e.showBorders,t=>this._spaceDialog=Qd(e,"showBorders",t))}
|
||
<span>${this._t("space.show_borders")}</span>
|
||
</label>
|
||
<label>${this._t("space.zero_wall_style")}</label>
|
||
<select class="areasel"
|
||
@change=${t=>{const i=t.target.value;this._spaceDialog={...e,zeroWallStyle:"solid"===i?"solid":"dashed"}}}>
|
||
<option value="dashed" ?selected=${"dashed"===e.zeroWallStyle}>
|
||
${this._t("space.zero_wall_dashed")}
|
||
</option>
|
||
<option value="solid" ?selected=${"solid"===e.zeroWallStyle}>
|
||
${this._t("space.zero_wall_solid")}
|
||
</option>
|
||
</select>
|
||
<div class="rhint">${this._t("space.zero_wall_help")}</div>
|
||
<label class="srcrow">
|
||
${this._boolInput(e.showNames,t=>this._spaceDialog=Qd(e,"showNames",t))}
|
||
<span>${this._t("space.show_names")}</span>
|
||
</label>
|
||
<label class="srcrow">
|
||
${this._boolInput(e.showLqi,t=>this._spaceDialog={...e,showLqi:t})}
|
||
<span>${this._t("space.show_lqi")}</span>
|
||
</label>
|
||
${""}
|
||
<label class="srcrow">
|
||
${this._boolInput(e.hideDecor,t=>this._spaceDialog={...e,hideDecor:t})}
|
||
<span>${this._t("space.hide_decor")}</span>
|
||
</label>
|
||
<div class="rhint">${this._t("space.hide_decor_tip")}</div>
|
||
<label class="srcrow">
|
||
${this._boolInput(e.hideOpenings,t=>this._spaceDialog={...e,hideOpenings:t})}
|
||
<span>${this._t("space.hide_openings")}</span>
|
||
</label>
|
||
<div class="rhint">${this._t("space.hide_openings_tip")}</div>
|
||
<label class="dispsection">${this._t("space.roomcard_section")}</label>
|
||
${[["labelTemp","space.label_temp"],["labelHum","space.label_hum"],["labelLqi","space.label_lqi"],["labelLight","space.label_light"]].map(([t,i])=>q`<label class="srcrow">
|
||
${this._boolInput(e[t],i=>this._spaceDialog={...e,[t]:i})}
|
||
<span>${this._t(i)}</span>
|
||
</label>`)}
|
||
<label>${this._t("space.card_font")}</label>
|
||
<div class="colorrow gsrow">
|
||
${this._rangeInput(50,300,5,Math.round(100*e.cardFontScale),t=>this._spaceDialog={...e,cardFontScale:t/100})}
|
||
<span class="opv">${Math.round(100*e.cardFontScale)}%</span>
|
||
</div>
|
||
${this._renderCardPreview(e.cardFontScale,1,1)}
|
||
<div class="colorrow">
|
||
<hp-color-opacity .label=${this._t("space.room_color")}
|
||
.opacityLabel=${this._t("space.opacity")}
|
||
.pickerLabels=${this._colorPickerLabels}
|
||
.color=${e.roomColor} .opacity=${e.roomOpacity} .showOpacity=${!0}
|
||
@hp-color-opacity-change=${t=>{this._spaceDialog={...e,roomColor:t.detail.color,roomOpacity:t.detail.opacity}}}></hp-color-opacity>
|
||
</div>
|
||
<label>${this._t("space.bg_mode")}</label>
|
||
<select class="areasel"
|
||
@change=${t=>{const i=t.target.value;this._spaceDialog={...e,bgMode:"static"===i||"daynight"===i?i:null}}}>
|
||
<option value="" ?selected=${null===e.bgMode}>${this._t("space.sun_inherit")}</option>
|
||
<option value="static" ?selected=${"static"===e.bgMode}>${this._t("gs.bg_static")}</option>
|
||
<option value="daynight" ?selected=${"daynight"===e.bgMode}>${this._t("gs.bg_daynight")}</option>
|
||
</select>
|
||
${"static"===(e.bgMode??Fr(this._settings,{}))?q`<div class="colorrow">
|
||
<hp-color-opacity .label=${this._t("space.bg_color")}
|
||
.pickerLabels=${this._colorPickerLabels}
|
||
.color=${e.bgColor||hs(this._settings,{bgColor:null})||this._stageBgHex()}
|
||
.opacity=${1} .showOpacity=${!1}
|
||
@hp-color-opacity-change=${t=>{this._spaceDialog={...e,bgColor:t.detail.color}}}></hp-color-opacity>
|
||
${e.bgColor?q`<button class="btn ghost" @click=${()=>this._spaceDialog={...e,bgColor:null}}>
|
||
${this._t("space.bg_inherit")}</button>`:q`<span class="opl">${this._t("space.bg_inherited")}</span>`}
|
||
</div>`:G}
|
||
<label>${this._t("space.north")}</label>
|
||
<div class="colorrow">
|
||
<input class="namein tempin" type="number" min="0" max="359" step="1"
|
||
placeholder=${this._t("space.sun_inherit")}
|
||
.value=${null===e.northDeg?"":String(e.northDeg)}
|
||
@input=${t=>{const i=t.target.value.trim(),s=""===i?null:Math.round(Number(i));this._spaceDialog={...e,northDeg:null!==s&&Number.isFinite(s)?Math.min(359,Math.max(0,s)):null}}} />
|
||
<span class="opl">${null===e.northDeg?this._t("space.north_inherited",{v:null===zr(this._settings,{})?"—":String(zr(this._settings,{}))+"°"}):"°"}</span>
|
||
</div>
|
||
<label>${this._t("space.sun_rays")}</label>
|
||
<select class="areasel"
|
||
@change=${t=>{const i=t.target.value;this._spaceDialog={...e,sunRays:""===i?null:"1"===i}}}>
|
||
<option value="" ?selected=${null===e.sunRays}>${this._t("space.sun_inherit")}</option>
|
||
<option value="1" ?selected=${!0===e.sunRays}>${this._t("space.sun_on")}</option>
|
||
<option value="0" ?selected=${!1===e.sunRays}>${this._t("space.sun_off")}</option>
|
||
</select>
|
||
<label>${this._t("space.fill_label")}</label>
|
||
${qi.map(e=>[e,"fill."+e]).map(([t,i])=>q`<label class="srcrow">
|
||
<input type="radio" name="fillmode" .checked=${e.fillMode===t}
|
||
@change=${()=>this._spaceDialog={...e,fillMode:t}} />
|
||
<span>${this._t(i)}</span>
|
||
${"temp"===t&&"temp"===e.fillMode?q`<span class="temprange">
|
||
<input class="namein tempin" type="number" step="0.5" .value=${String(e.tempMin)}
|
||
@input=${t=>{const i=yv(t.target.value);null!=i&&(this._spaceDialog={...e,tempMin:i})}} />
|
||
–
|
||
<input class="namein tempin" type="number" step="0.5" .value=${String(e.tempMax)}
|
||
@input=${t=>{const i=yv(t.target.value);null!=i&&(this._spaceDialog={...e,tempMax:i})}} />
|
||
°C
|
||
</span>`:G}
|
||
</label>
|
||
${"custom"===t&&"custom"===e.fillMode?q`<div class="colorrow gsrow">
|
||
<span class="gsl">${this._t("space.custom_fill")}</span>
|
||
<hp-color-opacity
|
||
.label=${this._t("space.custom_fill")}
|
||
.opacityLabel=${this._t("space.opacity")}
|
||
.pickerLabels=${this._colorPickerLabels}
|
||
.color=${(e.customFill||as).c}
|
||
.opacity=${(e.customFill||as).a}
|
||
@hp-color-opacity-change=${t=>{this._spaceDialog={...e,customFill:{c:t.detail.color,a:t.detail.opacity}}}}></hp-color-opacity>
|
||
${e.customFill?q`<button class="btn ghost" type="button"
|
||
@click=${()=>this._spaceDialog={...e,customFill:null}}>
|
||
${this._t("btn.reset")}</button>`:G}
|
||
</div>`:G}`)}
|
||
<label class="srcrow">
|
||
${this._boolInput(e.glowEnabled,t=>{this._spaceDialog={...e,glowEnabled:t}})}
|
||
<span>${this._t("space.glow_enabled")}</span>
|
||
</label>
|
||
${e.deleteBlockers?q`<div class="backuperror" role="alert">${this._t("space.delete_blocked",{n:String(e.deleteBlockers)})}</div>`:G}
|
||
</div>
|
||
<div class="row dialog-action-footer" slot="footer">
|
||
${"edit"===e.mode?q`<div class="dialog-action-group dialog-action-danger">
|
||
<button class="btn danger" @click=${this._deleteSpace} ?disabled=${e.busy}>
|
||
<ha-icon icon="mdi:delete-outline"></ha-icon>${this._t("btn.delete")}
|
||
</button>
|
||
</div>`:G}
|
||
<div class="dialog-action-group dialog-action-commit">
|
||
${this._importTotal>0&&"create"===e.mode?q`<button class="btn ghost" @click=${()=>this._skipImport()}>${this._t("btn.skip")}</button>`:G}
|
||
<button class="btn ghost" @click=${i}>${this._t("btn.cancel")}</button>
|
||
<button class="btn on" @click=${this._saveSpaceDialog}
|
||
?disabled=${!e.title.trim()||"file"===e.source&&!(e.planFile||e.planUrl)||e.busy}
|
||
title=${"file"!==e.source||e.planFile||e.planUrl?"":this._t("title.need_plan")}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${e.busy?"…":this._t("btn.save")}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</hp-dialog>`}_renderMergeDialog(){const e=this._mergeDialog,t=this._spaceModel()?.rooms||[],i=(i,s)=>{const n=t.find(e=>e.id===i),o=n?.area?this.hass.areas[n.area]?.name:null;return q`<label class="srcrow">
|
||
<input type="radio" name="mergekeep" .checked=${e.pick===s}
|
||
@change=${()=>this._mergeDialog={...e,pick:s}} />
|
||
<span>${n?.name||""} <span class="muted">· ${o||this._t("merge.no_area")}</span></span>
|
||
</label>`};return q`<hp-dialog .hass=${this.hass} .title=${this._t("merge.header")} icon="mdi:vector-union"
|
||
@hp-close=${()=>this._mergeDialog=null}>
|
||
<div class="body">
|
||
<p class="muted">${this._t("merge.hint")}</p>
|
||
<label>${this._t("merge.keep")}</label>
|
||
${i(e.aId,"a")}
|
||
${i(e.bId,"b")}
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<span class="spacer"></span>
|
||
<button class="btn ghost" @click=${()=>this._mergeDialog=null}>${this._t("btn.cancel")}</button>
|
||
<button class="btn on" @click=${this._commitMerge}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${this._t("btn.save")}
|
||
</button>
|
||
</div>
|
||
</hp-dialog>`}_renderCardPreview(e,t,i){const s=18*e;return q`<div class="cardpreview">
|
||
<span class="cpname" style="font-size:${(s*t).toFixed(1)}px">
|
||
${this._t("preview.room_name")}</span>
|
||
<span class="cpmeta" style="font-size:${(.62*s*i).toFixed(1)}px">
|
||
<ha-icon icon="mdi:thermometer"></ha-icon>22.4° ·
|
||
<ha-icon icon="mdi:water-percent"></ha-icon>45% ·
|
||
<ha-icon icon="mdi:lightbulb-on"></ha-icon>${this._t("roomcard.light_partial",{on:1,total:3})}
|
||
</span>
|
||
</div>`}_renderRoomSource(e){const t="temp"===e?this._roomTempSrc:this._roomHumSrc,i=t=>{"temp"===e?this._roomTempSrc=t:this._roomHumSrc=t,this.requestUpdate()},s=this._roomSrcOpen===e;return q`
|
||
<label>${this._t("temp"===e?"room.temp_src_label":"room.hum_src_label")}</label>
|
||
<label class="srcrow">
|
||
<input type="radio" name="rsrc-${e}" .checked=${!t}
|
||
@change=${()=>{i(""),this._roomSrcOpen=null}} />
|
||
<span>${this._t("room.src_average")}</span>
|
||
</label>
|
||
<label class="srcrow">
|
||
<input type="radio" name="rsrc-${e}" .checked=${!!t}
|
||
@change=${()=>{this._roomSrcOpen=e,this._roomSrcFilter="",this.requestUpdate()}} />
|
||
<span>${this._t("room.src_pick")}</span>
|
||
</label>
|
||
${t||s?q`<button class="dropbtn ${s?"open":""}"
|
||
@click=${()=>{this._roomSrcOpen=s?null:e,this._roomSrcFilter=""}}>
|
||
${t?q`<b>${this._roomSrcLabel(t)}</b><span class="ref">${t}</span>`:q`<span class="muted">${this._t("room.src_ph")}</span>`}
|
||
<ha-icon icon=${s?"mdi:chevron-up":"mdi:chevron-down"}></ha-icon>
|
||
</button>
|
||
${s?q`<div class="droppanel">
|
||
<input class="namein" type="text" placeholder=${this._t("marker.search_ph")}
|
||
.value=${this._roomSrcFilter}
|
||
@input=${e=>{this._roomSrcFilter=e.target.value,this.requestUpdate()}} />
|
||
<div class="candlist">
|
||
${this._roomSrcCandidates().map(e=>q`<div class="cand ${e.value===t?"sel":""}"
|
||
@click=${()=>{i(e.value),this._roomSrcOpen=null}}>
|
||
<span class="cl">${e.label}</span><span class="cs">${e.sub}</span>
|
||
</div>`)}
|
||
</div>
|
||
</div>`:G}`:G}`}_renderRoomDialog(){const e=!!this._roomEditId,t=e?null:this._wallFaceBatch,i=t&&t.candidates.length>1?this._t("room.queue_progress",{current:t.index+1,total:t.candidates.length}):"",s=!!this._areaSel||!!this._nameSel.trim(),n=cs(this._curSpaceCfg),o=this._roomFill||n.fill,a=this._roomCustomFill||n.customFill,r=[...this._freeAreas];if(e&&this._areaSel&&!r.some(e=>e.area_id===this._areaSel)){const e=this.hass.areas[this._areaSel];e&&r.unshift(e)}return q`<hp-dialog class="roomdialog" .hass=${this.hass} wide
|
||
.title=${e?this._t("room.settings_title"):i||this._t("room.new")}
|
||
icon=${e?"mdi:cog-outline":"mdi:floor-plan"} @hp-close=${this._roomDialogCancel}>
|
||
<div class="body">
|
||
${i?q`<p class="muted" role="status" aria-live="polite">
|
||
${i}
|
||
</p>`:G}
|
||
<label>${this._t("room.name_label")}</label>
|
||
<input class="namein" type="text" placeholder=${this._t("room.name_ph")}
|
||
.value=${this._nameSel}
|
||
@input=${e=>this._nameSel=e.target.value} />
|
||
<label>${this._t("room.area_label")}</label>
|
||
<select class="areasel"
|
||
@change=${e=>{this._areaSel=e.target.value,!this._nameSel&&this._areaSel&&(this._nameSel=this.hass.areas[this._areaSel]?.name||""),this.requestUpdate()}}>
|
||
<option value="">${this._t("room.no_area_option")}</option>
|
||
${r.map(e=>q`<option value=${e.area_id} ?selected=${e.area_id===this._areaSel}>${e.name}</option>`)}
|
||
</select>
|
||
|
||
<label class="dispsection">${this._t("room.settings_section")}</label>
|
||
<label>${this._t("room.fill_label")}</label>
|
||
${[["","fill.inherit"],...Wi.map(e=>[e,"fill."+e])].map(([e,t])=>q`<label class="srcrow inline">
|
||
<input type="radio" name="rfill" .checked=${this._roomFill===e}
|
||
@change=${()=>{this._roomFill=e,this.requestUpdate()}} />
|
||
<span>${this._t(t)}</span>
|
||
</label>`)}
|
||
${"custom"===o?q`<div class="colorrow gsrow">
|
||
<span class="gsl">${this._roomCustomFill?this._t("room.custom_fill_own"):this._t("room.custom_fill_space")}</span>
|
||
<hp-color-opacity
|
||
.label=${this._roomCustomFill?this._t("room.custom_fill_own"):this._t("room.custom_fill_space")}
|
||
.opacityLabel=${this._t("space.opacity")}
|
||
.pickerLabels=${this._colorPickerLabels}
|
||
.color=${a.c}
|
||
.opacity=${a.a}
|
||
@hp-color-opacity-change=${e=>{this._roomCustomFill={c:e.detail.color,a:e.detail.opacity}}}></hp-color-opacity>
|
||
${this._roomCustomFill?q`<button class="btn ghost" type="button" @click=${()=>{this._roomCustomFill=null}}>${this._t("btn.reset")}</button>`:G}
|
||
</div>`:G}
|
||
${this._renderRoomSource("temp")}
|
||
${this._renderRoomSource("hum")}
|
||
|
||
<label class="dispsection">${this._t("room.sizes_section")}</label>
|
||
<label>${this._t("room.name_scale")}</label>
|
||
<div class="colorrow gsrow">
|
||
${this._rangeInput(50,300,5,Math.round(100*this._roomNameScale),e=>{this._roomNameScale=e/100,this.requestUpdate()})}
|
||
<span class="opv">${Math.round(100*this._roomNameScale)}%</span>
|
||
</div>
|
||
<label>${this._t("room.label_scale")}</label>
|
||
<div class="colorrow gsrow">
|
||
${this._rangeInput(50,300,5,Math.round(100*this._roomLabelScale),e=>{this._roomLabelScale=e/100,this.requestUpdate()})}
|
||
<span class="opv">${Math.round(100*this._roomLabelScale)}%</span>
|
||
</div>
|
||
${this._renderCardPreview(cs(this._curSpaceCfg).cardFontScale,this._roomNameScale,this._roomLabelScale)}
|
||
</div>
|
||
<div class="row roomfooter" slot="footer">
|
||
<button class="btn ghost" @click=${this._roomDialogCancel}>${this._t("btn.cancel")}</button>
|
||
<span class="spacer"></span>
|
||
${e?q`<button class="btn on" @click=${()=>this._saveRoomEdit()} ?disabled=${!this._nameSel.trim()}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${this._t("btn.save")}
|
||
</button>`:q`${this._pendingSplit?G:q`<button class="btn ghost" @click=${this._keepClosedAsPartitions}>
|
||
<ha-icon icon="mdi:wall"></ha-icon>${this._t("btn.keep_as_walls")}
|
||
</button>`}
|
||
<button class="btn on room-save" @click=${this._saveRoom} ?disabled=${!s}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${this._t("btn.save")}
|
||
</button>`}
|
||
</div>
|
||
</hp-dialog>`}}qv.properties={_tabDrag:{state:!0},_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_continuityEpoch:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_partitionDeleteDialog:{state:!0},_roomDeleteDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingPreset:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_vacAllCamerasFor:{state:!0},_vacCalConfirm:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomCustomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_deviceInbox:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_preflightClipboardFallback:{state:!0},_backupExportDialog:{state:!0},_backupImportDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},qv.ZOOM_MAX=8,qv.ZOOM_MIN=1/3,qv.styles=[Ke,vm],customElements.get("houseplan-card")||customElements.define("houseplan-card",qv),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-card"===e.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${_v} `,"background:#3ea6ff;color:#04121f;font-weight:700","");
|