mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
Владелец, осмотрев первые эталоны сета, отменил дневное решение о сохранении фаски: `junction-y-60-equal50` показывал вырез, `junction-acute30-mixed` — торчащие углы. По визуальному сравнению трёх вариантов принято: узлы смыкаются полным mitre, как обычное пересечение стен на чертеже. Итоговое правило веера (одно на все случаи): - mitre принимается, когда он В СЕКТОРЕ пары (вперёд по лучам для обычной пары, назад — для рефлексной: наружный угол между крайними лучами, где и жил вырез Y-60), в пределах классического `MITRE_LIMIT` и не дальше конца толстого саппорта (#271); - рефлекс без валидного mitre замыкается плоской хордой между гранями; - обычная пара без mitre — локальный бевел: ход по граням ограничен толстым саппортом, лимитом и двойной толщиной пары, чтобы хорда осталась деталью угла. Гигантские бевел-«бабочки» и mitre вне сектора — две реальные ошибки промежуточных версий, обе пойманы на сценах сета до пуша. Слой `bevelMultiWallBody` сохранён только как АДРЕСНЫЙ латеральный трим для узлов с вырожденно-коротким толстым саппортом (#271); все прочие узлы — чисто аддитивные, следы трима на них исчезли. `bevelMultiWallPaper` из бумаги удалён. Обе записи CHANGELOG приведены к финальному контракту. Тесты: юниты §302 усилены; площадь фикстуры #197 +0.6 юнита²; мутанты переякорены, краснота каждого проверена исполнением. Issue: #302 User-Visible: yes
7496 lines
1.2 MiB
Plaintext
Executable File
7496 lines
1.2 MiB
Plaintext
Executable File
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b866f4e85930183f5c63e7e049127d1ae7987a1da56f85dc77fe105809b936ec";const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},a=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:r,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!r(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),u.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=p(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(a(t))}else void 0!==t&&e.push(a(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.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(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??y)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};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=t=>t,$=k.trustedTypes,S=$?$.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,P=`<${D}>`,T=document,R=()=>T.createComment(""),I=t=>null===t||"object"!=typeof t&&"function"!=typeof t,A=Array.isArray,z="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,E=/-->/g,N=/>/g,O=RegExp(`>|${z}(?:([^\\s"'>=/]+)(${z}*=${z}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,B=/^(?:script|style|textarea|title)$/i,q=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=q(1),j=q(2),G=Symbol.for("lit-noChange"),U=Symbol.for("lit-nothing"),V=new WeakMap,K=T.createTreeWalker(T,129);function Y(t,e){if(!A(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"<svg>":3===e?"<math>":"",a=F;for(let e=0;e<i;e++){const i=t[e];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=E:void 0!==l[1]?a=N: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===E||a===N?a=F:(a=O,n=void 0);const d=a===O&&t[e+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?e:d)}return[Y(t,o+(t[i]||"<?>")+(2===e?"</svg>":3===e?"</math>":"")),s]};class X{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const a=t.length-1,r=this.parts,[l,c]=J(t,e);if(this.el=X.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&r.length<a;){if(1===s.nodeType){if(s.hasAttributes())for(const t of s.getAttributeNames())if(t.endsWith(M)){const e=c[o++],i=s.getAttribute(t).split(C),a=/([.?@])?(.*)/.exec(e);r.push({type:1,index:n,name:a[2],strings:i,ctor:"."===a[1]?it:"?"===a[1]?st:"@"===a[1]?nt:et}),s.removeAttribute(t)}else t.startsWith(C)&&(r.push({type:6,index:n}),s.removeAttribute(t));if(B.test(s.tagName)){const t=s.textContent.split(C),e=t.length-1;if(e>0){s.textContent=$?$.emptyScript:"";for(let i=0;i<e;i++)s.append(t[i],R()),K.nextNode(),r.push({type:2,index:++n});s.append(t[e],R())}}}else if(8===s.nodeType)if(s.data===D)r.push({type:2,index:n});else{let t=-1;for(;-1!==(t=s.data.indexOf(C,t+1));)r.push({type:7,index:n}),t+=C.length-1}n++}}static createElement(t,e){const i=T.createElement("template");return i.innerHTML=t,i}}function Z(t,e,i=t,s){if(e===G)return e;let n=void 0!==s?i._$Co?.[s]:i._$Cl;const o=I(e)?void 0:e._$litDirective$;return n?.constructor!==o&&(n?._$AO?.(!1),void 0===o?n=void 0:(n=new o(t),n._$AT(t,i,s)),void 0!==s?(i._$Co??=[])[s]=n:i._$Cl=n),void 0!==n&&(e=Z(t,n._$AS(t,e.values),n,s)),e}class Q{constructor(t,e){this._$AV=[],this._$AN=void 0,this._$AD=t,this._$AM=e}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(t){const{el:{content:e},parts:i}=this._$AD,s=(t?.creationScope??T).importNode(e,!0);K.currentNode=s;let n=K.nextNode(),o=0,a=0,r=i[0];for(;void 0!==r;){if(o===r.index){let e;2===r.type?e=new tt(n,n.nextSibling,this,t):1===r.type?e=new r.ctor(n,r.name,r.strings,this,t):6===r.type&&(e=new ot(n,this,t)),this._$AV.push(e),r=i[++a]}o!==r?.index&&(n=K.nextNode(),o++)}return K.currentNode=T,s}p(t){let e=0;for(const i of this._$AV)void 0!==i&&(void 0!==i.strings?(i._$AI(t,i,e),e+=i.strings.length-2):i._$AI(t[e])),e++}}class tt{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(t,e,i,s){this.type=2,this._$AH=U,this._$AN=void 0,this._$AA=t,this._$AB=e,this._$AM=i,this.options=s,this._$Cv=s?.isConnected??!0}get parentNode(){let t=this._$AA.parentNode;const e=this._$AM;return void 0!==e&&11===t?.nodeType&&(t=e.parentNode),t}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(t,e=this){t=Z(this,t,e),I(t)?t===U||null==t||""===t?(this._$AH!==U&&this._$AR(),this._$AH=U):t!==this._$AH&&t!==G&&this._(t):void 0!==t._$litType$?this.$(t):void 0!==t.nodeType?this.T(t):(t=>A(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==U&&I(this._$AH)?this._$AA.nextSibling.data=t:this.T(T.createTextNode(t)),this._$AH=t}$(t){const{values:e,_$litType$:i}=t,s="number"==typeof i?this._$AC(t):(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(e);else{const t=new Q(s,this),i=t.u(this.options);t.p(e),this.T(i),this._$AH=t}}_$AC(t){let e=V.get(t.strings);return void 0===e&&V.set(t.strings,e=new X(t)),e}k(t){A(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(R()),this.O(R()),this,this.options)):i=e[s],i._$AI(n),s++;s<e.length&&(this._$AR(i&&i._$AB.nextSibling,s),e.length=s)}_$AR(t=this._$AA.nextSibling,e){for(this._$AP?.(!1,!0,e);t!==this._$AB;){const e=x(t).nextSibling;x(t).remove(),t=e}}setConnected(t){void 0===this._$AM&&(this._$Cv=t,this._$AP?.(t))}}class et{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(t,e,i,s,n){this.type=1,this._$AH=U,this._$AN=void 0,this.element=t,this.name=e,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=U}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=Z(this,t,e,0),o=!I(t)||t!==this._$AH&&t!==G,o&&(this._$AH=t);else{const s=t;let a,r;for(t=n[0],a=0;a<n.length-1;a++)r=Z(this,s[i+a],e,a),r===G&&(r=this._$AH[a]),o||=!I(r)||r!==this._$AH[a],r===U?t=U:t!==U&&(t+=(r??"")+n[a+1]),this._$AH[a]=r}o&&!s&&this.j(t)}j(t){t===U?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,t??"")}}class it extends et{constructor(){super(...arguments),this.type=3}j(t){this.element[this.name]=t===U?void 0:t}}class st extends et{constructor(){super(...arguments),this.type=4}j(t){this.element.toggleAttribute(this.name,!!t&&t!==U)}}class nt extends et{constructor(t,e,i,s,n){super(t,e,i,s,n),this.type=5}_$AI(t,e=this){if((t=Z(this,t,e,0)??U)===G)return;const i=this._$AH,s=t===U&&i!==U||t.capture!==i.capture||t.once!==i.once||t.passive!==i.passive,n=t!==U&&(i===U||s);s&&this.element.removeEventListener(this.name,this,i),n&&this.element.addEventListener(this.name,this,t),this._$AH=t}handleEvent(t){"function"==typeof this._$AH?this._$AH.call(this.options?.host??this.element,t):this._$AH.handleEvent(t)}}class ot{constructor(t,e,i){this.element=t,this.type=6,this._$AN=void 0,this._$AM=e,this.options=i}get _$AU(){return this._$AM._$AU}_$AI(t){Z(this,t)}}const at={I:tt},rt=k.litHtmlPolyfillSupport;rt?.(X,tt),(k.litHtmlVersions??=[]).push("3.3.3");const lt=(t,e,i)=>{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return n._$AI(t),n},ct=globalThis;let ht=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=lt(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return G}};ht._$litElement$=!0,ht.finalized=!0,ct.litElementHydrateSupport?.({LitElement:ht});const dt=ct.litElementPolyfillSupport;dt?.({LitElement:ht}),(ct.litElementVersions??=[]).push("4.2.2");const pt=2,ut=t=>(...e)=>({_$litDirective$:t,values:e});let _t=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};const mt={},gt=ut(class extends _t{constructor(){super(...arguments),this.ot=mt}render(t,e){return e()}update(t,[e,i]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((t,e)=>t===this.ot[e]))return G}else if(this.ot===e)return G;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,i)}}),{I:ft}=at,vt=t=>t,yt=()=>document.createComment(""),bt=(t,e,i)=>{const s=t._$AA.parentNode,n=void 0===e?t._$AB:e._$AA;if(void 0===i){const e=s.insertBefore(yt(),n),o=s.insertBefore(yt(),n);i=new ft(e,o,t,t.options)}else{const e=i._$AB.nextSibling,o=i._$AM,a=o!==t;if(a){let e;i._$AQ?.(t),i._$AM=t,void 0!==i._$AP&&(e=t._$AU)!==o._$AU&&i._$AP(e)}if(e!==n||a){let t=i._$AA;for(;t!==e;){const e=vt(t).nextSibling;vt(s).insertBefore(t,n),t=e}}}return i},wt=(t,e,i=t)=>(t._$AI(e,i),t),kt={},xt=(t,e=kt)=>t._$AH=e,$t=t=>{t._$AR(),t._$AA.remove()},St=(t,e,i)=>{const s=new Map;for(let n=e;n<=i;n++)s.set(t[n],n);return s},Mt=ut(class extends _t{constructor(t){if(super(t),t.type!==pt)throw Error("repeat() can only be used in text expressions")}dt(t,e,i){let s;void 0===i?i=e:void 0!==e&&(s=e);const n=[],o=[];let a=0;for(const e of t)n[a]=s?s(e,a):a,o[a]=i(e,a),a++;return{values:o,keys:n}}render(t,e,i){return this.dt(t,e,i).values}update(t,[e,i,s]){const n=(t=>t._$AH)(t),{values:o,keys:a}=this.dt(e,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]=wt(n[d],o[u]),d++,u++;else if(r[p]===a[_])l[_]=wt(n[p],o[_]),p--,_--;else if(r[d]===a[_])l[_]=wt(n[d],o[_]),bt(t,l[_+1],n[d]),d++,_--;else if(r[p]===a[u])l[u]=wt(n[p],o[u]),bt(t,n[d],n[p]),p--,u++;else if(void 0===c&&(c=St(a,u,_),h=St(r,d,p)),c.has(r[d]))if(c.has(r[p])){const e=h.get(a[u]),i=void 0!==e?n[e]:null;if(null===i){const e=bt(t,n[d]);wt(e,o[u]),l[u]=e}else l[u]=wt(i,o[u]),bt(t,n[d],i),n[e]=null;u++}else $t(n[p]),p--;else $t(n[d]),d++;for(;u<=_;){const e=bt(t,l[_+1]);wt(e,o[u]),l[u++]=e}for(;d<=p;){const t=n[d++];null!==t&&$t(t)}return this.ut=a,xt(t,l),G}}),Ct=new WeakMap;let Dt=0;class Pt extends ht{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-"+ ++Dt,this._focusInitial=()=>{const t=this._focusableElements(),e=t.find(t=>t.hasAttribute("autofocus"))||t[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");e?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key){t.preventDefault(),t.stopImmediatePropagation(),this._pruneOverlays();const e=this._overlays[this._overlays.length-1];return e?void this._closeOverlay(e,"escape"):void this._requestClose()}if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!t.shiftKey||s!==n&&i.includes(s)?t.shiftKey||s!==o||(t.preventDefault(),n.focus()):(t.preventDefault(),o.focus())},this._onFallbackCancel=t=>{t.preventDefault(),this._requestClose()},this._onFallbackClick=t=>{this.dismissOnScrim&&t.target===t.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const t=this.getRootNode();this._focusRoot=t;const e=Ct.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),Ct.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=[...this._overlays];this._overlays=[];for(const e of t.reverse())e.close("disconnect");const e=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const s=e?Ct.get(e):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),e&&s&&requestAnimationFrame(()=>{const t=Ct.get(e);if(!t)return;if(!t.dialogs.size)return t.opener?.isConnected&&t.opener.focus({preventScroll:!0}),void Ct.delete(e);const s=i?.closest("hp-dialog");i?.isConnected&&s&&t.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[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(","),e=[],i=new Set,s=n=>{if(!i.has(n)){if(i.add(n),n instanceof HTMLElement){if(n.matches(t)&&e.push(n),n instanceof HTMLSlotElement){for(const t of n.assignedNodes({flatten:!0}))s(t);return}if(n.shadowRoot){for(const t of n.shadowRoot.childNodes)s(t);return}}for(const t of n.childNodes)s(t)}};for(const t of this.childNodes)s(t);const n=this.overlayPortal();return n&&s(n),e.filter(t=>{let e=t;for(;e;){const t=getComputedStyle(e);if(e.hidden||e.inert||"true"===e.getAttribute("aria-hidden")||"none"===t.display||"hidden"===t.visibility)return!1;if(e=e.assignedSlot||e.parentElement||(e.getRootNode()instanceof ShadowRoot?e.getRootNode().host:null),e===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(t=>t.owner.isConnected)}_closeOverlay(t,e){const i=this._overlays.findIndex(e=>e.token===t.token);i>=0&&this._overlays.splice(i,1),t.close(e)}registerOverlay(t){this._pruneOverlays();const e=this._overlays.find(e=>e.owner===t.owner);e&&this._overlays.splice(this._overlays.indexOf(e),1);const i=t.group||"transient";for(const t of[...this._overlays].reverse())t.group===i&&this._closeOverlay(t,"exclusive");const s={...t,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const t=this._overlays.findIndex(t=>t.token===s.token);t>=0&&this._overlays.splice(t,1)}}closeTransientOverlays(t="outside"){this._pruneOverlays();const e=[...this._overlays].filter(t=>"transient"===(t.group||"transient"));for(const i of e.reverse())this._closeOverlay(i,t);return e.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const t=W`<span class="title" id=${this._titleId}>
|
||
${this.icon?W`<ha-icon icon=${this.icon}></ha-icon>`:U}
|
||
<span class="title-text">${this.title}</span>
|
||
</span>`;return this._useHaDialog?W`<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">${t}</span>
|
||
<slot></slot>
|
||
<span class="footer" slot="footer"><slot name="footer"></slot></span>
|
||
</ha-dialog><div class="overlay-portal"></div>`:W`<dialog
|
||
role="dialog"
|
||
aria-modal="true"
|
||
aria-labelledby=${this._titleId}
|
||
@cancel=${this._onFallbackCancel}
|
||
@click=${this._onFallbackClick}
|
||
>
|
||
<section class="surface" tabindex="-1">
|
||
<header class="header">
|
||
${t}
|
||
<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>`}}Pt.properties={title:{type:String},icon:{type:String},wide:{type:Boolean,reflect:!0},dismissOnScrim:{type:Boolean,attribute:"dismiss-on-scrim"},hass:{attribute:!1}},Pt.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(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",Pt);const Tt=/^#[0-9a-fA-F]{6}$/;function Rt(t,e){return"string"==typeof t&&Tt.test(t)?t:e}const It="(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])",At=new RegExp(`^rgb\\(${It}, ${It}, ${It}\\)$`);function zt(t){if(!Array.isArray(t)||t.length<3)return null;const e=t.slice(0,3);if(!e.every(t=>"number"==typeof t&&Number.isFinite(t)))return null;const[i,s,n]=e.map(t=>Math.round(Math.min(255,Math.max(0,t))));return`rgb(${i}, ${s}, ${n})`}const Ft=(t,e,i)=>Math.min(i,Math.max(e,Number.isFinite(t)?t:e));function Et(t){return Number.isFinite(t)?(t%360+360)%360:0}function Nt(t){if("string"!=typeof t)return null;const e=t.trim().replace(/^#/,""),i=/^[0-9a-fA-F]{3}$/.test(e)?e.split("").map(t=>`${t}${t}`).join(""):e;return/^[0-9a-fA-F]{6}$/.test(i)?`#${i.toLowerCase()}`:null}function Ot(t){const e=Nt(t);return e?{r:Number.parseInt(e.slice(1,3),16),g:Number.parseInt(e.slice(3,5),16),b:Number.parseInt(e.slice(5,7),16)}:null}function Ht(t){const e=Ft(t.r,0,255)/255,i=Ft(t.g,0,255)/255,s=Ft(t.b,0,255)/255,n=Math.max(e,i,s),o=n-Math.min(e,i,s);let a=0;return o>0&&(a=n===e?(i-s)/o%6*60:n===i?60*((s-e)/o+2):60*((e-i)/o+4)),{h:Et(a),s:0===n?0:o/n*100,v:100*n}}function Lt(t){return function(t){const e=t=>Math.round(Ft(t,0,255)).toString(16).padStart(2,"0");return`#${e(t.r)}${e(t.g)}${e(t.b)}`}(function(t){const e=Et(t.h),i=Ft(t.s,0,100)/100,s=Ft(t.v,0,100)/100,n=s*i,o=n*(1-Math.abs(e/60%2-1)),a=s-n;let r;return r=e<60?[n,o,0]:e<120?[o,n,0]:e<180?[0,n,o]:e<240?[0,o,n]:e<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))}}(t))}function Bt(t){const e=t.visualViewport;return e&&Number.isFinite(e.width)&&Number.isFinite(e.height)?{left:Number(e.offsetLeft)||0,top:Number(e.offsetTop)||0,width:Math.max(0,Number(e.width)||0),height:Math.max(0,Number(e.height)||0)}:{left:0,top:0,width:t.innerWidth,height:t.innerHeight}}function qt(t,e,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,e.width),c),p=Math.min(Math.max(0,e.height),h);let u=t.left;u+d>r&&(u=t.right-d),u=Math.min(Math.max(o,u),Math.max(o,r-d));const _=t.bottom+s,m=t.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 Wt{constructor(t,e,i){this.owner=t,this.name=e,this.keydown=i,this.host=null,this.root=null}window(){return this.owner.ownerDocument.defaultView}dialog(){return this.owner.closest("hp-dialog")}usesPopover(t=!1){const e=this.window();return!t&&!!e&&function(t){const e=t.HTMLElement?.prototype;return"function"==typeof e?.showPopover&&"function"==typeof e?.hidePopover}(e)}get hasFallback(){return!!this.host}containsPath(t){return t.includes(this.owner)||!!this.host&&t.includes(this.host)}ownsActiveElement(){let t=this.owner.ownerDocument.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return!(!t||t!==this.owner&&!this.owner.shadowRoot?.contains(t)&&!this.root?.contains(t))}renderFallback(t,e){const i=this.ensureFallback();return i?(lt(W`<style>${e}</style>${t}`,i),i):null}surface(t,e){return e?this.owner.shadowRoot?.querySelector(t)||null:this.root?.querySelector(t)||null}destroy(){this.root&<(U,this.root),this.host?.remove(),this.host=null,this.root=null}ensureFallback(){if(this.root?.isConnected)return this.root;const t=this.dialog()?.overlayPortal()||this.owner.ownerDocument.body;if(!t)return null;const e=this.owner.ownerDocument.createElement("div");return e.dataset.hpOverlay=this.name,e.style.display="contents",this.keydown&&e.addEventListener("keydown",this.keydown,!0),t.append(e),this.host=e,this.root=e.attachShadow({mode:"open"}),this.root}}const jt={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class Gt extends ht{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=jt,this._open=!1,this._pickerRaf=0,this._emitRaf=0,this._forceFallback=!1,this._overlayDispose=null,this._hue=0,this._saturation=0,this._value=0,this._hexDraft="#607d8b",this._hexInvalid=!1,this._lastValidColor="#607d8b",this._activePointerId=null,this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closePicker(!1,"outside")},this._keyDown=t=>{this._open&&"Escape"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),this._closePicker(this._floating.ownsActiveElement(),"escape"))},this._floating=new Wt(this,"color-opacity",this._keyDown),this._queuePickerPosition=()=>{if(!this._open)return;this._pickerRaf&&cancelAnimationFrame(this._pickerRaf);const t=this._window();t&&(this._pickerRaf=t.requestAnimationFrame(()=>{this._pickerRaf=0,this._positionPicker()||this._closePicker()}))}}connectedCallback(){super.connectedCallback(),this._syncFromColor(this.color,!1),this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.addEventListener("resize",this._queuePickerPosition),t?.addEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.addEventListener("resize",this._queuePickerPosition),t?.visualViewport?.addEventListener("scroll",this._queuePickerPosition),this.addEventListener("keydown",this._keyDown,!0)}disconnectedCallback(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.removeEventListener("resize",this._queuePickerPosition),t?.removeEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.removeEventListener("resize",this._queuePickerPosition),t?.visualViewport?.removeEventListener("scroll",this._queuePickerPosition),this.removeEventListener("keydown",this._keyDown,!0),this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._pickerRaf=0,this._emitRaf=0,this._closePicker(!1,"disconnect"),super.disconnectedCallback()}updated(t){if(t.has("color")){const t=Nt(this.color);t&&t!==this._lastValidColor&&this._syncFromColor(t,this._open)}this.disabled&&this._open?this._closePicker():this._open&&(this._supportsPopover()||this._renderFallback(),(t.has("color")||t.has("opacity")||t.has("showOpacity")||t.has("label")||t.has("opacityLabel")||t.has("pickerLabels"))&&this._queuePickerPosition())}_window(){return this._floating.window()}_supportsPopover(){return this._floating.usesPopover(this._forceFallback)}_dialog(){return this._floating.dialog()}async _toggle(){this.disabled||(this._open?this._closePicker():(this._forceFallback=!1,this._syncFromColor(this.color,!1),this._hexInvalid=!1,this._activePointerId=null,this._open=!0,await this.updateComplete,this._open&&(this._supportsPopover()||this._renderFallback(),this._positionPicker()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:t=>this._closePicker("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closePicker())))}_closePicker(t=!1,e="exclusive"){if(!this._open&&!this._floating.hasFallback)return;const 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(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_surface(){return this._floating.surface(".picker",this._supportsPopover())}_renderFallback(){const t=Gt.styles.cssText;this._floating.renderFallback(this._pickerTemplate(!1),t)}_positionPicker(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let i=this._surface();if(!t||!e?.isConnected||!i?.isConnected)return!1;const s=Bt(t);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=e.getBoundingClientRect(),o=i.getBoundingClientRect();if(!(n.width&&n.height&&o.width&&o.height))return!1;const a=qt(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 t=this.pickerLabels||jt;return{title:t.title||jt.title,hue:t.hue||jt.hue,saturation:t.saturation||jt.saturation,value:t.value||jt.value,hex:t.hex||jt.hex,invalidHex:t.invalidHex||jt.invalidHex}}_syncFromColor(t,e){const i=Nt(Rt(t,"#607d8b"))||"#607d8b",s=Ot(i);if(!s)return;const n=Ht(s);(!e||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(t=!1){this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._emitRaf=0,t&&this._syncFromColor(this._lastValidColor,!0)}_queueHsvEmit(){if(this._emitRaf)return;const t=this._window();t?this._emitRaf=t.requestAnimationFrame(()=>{this._emitRaf=0,this._emitHsv()}):this._emitHsv()}_emitHsv(){this._emit(Lt({h:this._hue,s:this._saturation,v:this._value}),this.opacity)}_setHue(t){this._hue=Math.min(359,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setSaturation(t){this._saturation=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setValue(t){this._value=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setOpacity(t){this._emit(this._lastValidColor,t)}_shiftRangeKey(t,e,i,s,n){if(!t.shiftKey)return;const o="ArrowRight"===t.key||"ArrowUp"===t.key?10:"ArrowLeft"===t.key||"ArrowDown"===t.key?-10:0;o&&(t.preventDefault(),t.stopPropagation(),n(Math.min(s,Math.max(i,e+o))))}_setSvFromPointer(t,e){const i=t.currentTarget.getBoundingClientRect();i.width&&i.height&&(this._saturation=Math.min(100,Math.max(0,(t.clientX-i.left)/i.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(t.clientY-i.top)/i.height))),e?(this._cancelQueuedEmit(),this._emitHsv()):this._queueHsvEmit())}_svPointerDown(t){if(t.preventDefault(),t.stopPropagation(),null===this._activePointerId){this._activePointerId=t.pointerId;try{t.currentTarget.setPointerCapture(t.pointerId)}catch{}this._setSvFromPointer(t,!0)}}_svPointerMove(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!1))}_svPointerUp(t){if(t.pointerId===this._activePointerId){t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!0),this._activePointerId=null;try{t.currentTarget.releasePointerCapture(t.pointerId)}catch{}}}_svPointerCancel(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._activePointerId=null,this._cancelQueuedEmit(!0))}_svLostPointerCapture(t){t.pointerId===this._activePointerId&&(this._activePointerId=null,this._cancelQueuedEmit(!0))}_hexInput(t){const e=t.target.value;this._hexDraft=e,this._hexInvalid=!1;const i=Nt(e),s=i?Ot(i):null;if(!i||!s)return;const n=Ht(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 t=Nt(this._hexDraft);if(!t)return this._hexDraft=this._lastValidColor,void(this._hexInvalid=!0);this._hexDraft=t,this._hexInvalid=!1,t!==this._lastValidColor&&this._emit(t,this.opacity)}_hexKeyDown(t){"Enter"===t.key&&(t.preventDefault(),t.stopPropagation(),this._commitHex())}_emit(t,e,i=!1){const s=Nt(t)||this._lastValidColor,n=Number.isFinite(e)?e: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(t){const e=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),i=Lt({h:this._hue,s:this._saturation,v:this._value}),s=Lt({h:this._hue,s:100,v:100}),n=this._labels(),o=Math.round(this._hue),a=Math.round(this._saturation),r=Math.round(this._value);return W`
|
||
<div class="picker" popover=${t?"manual":U} 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:${e/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=${t=>this._svPointerDown(t)}
|
||
@pointermove=${t=>this._svPointerMove(t)}
|
||
@pointerup=${t=>this._svPointerUp(t)}
|
||
@pointercancel=${t=>this._svPointerCancel(t)}
|
||
@lostpointercapture=${t=>this._svLostPointerCapture(t)}>
|
||
<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=${t=>this._setHue(Number(t.target.value))}
|
||
@keydown=${t=>this._shiftRangeKey(t,o,0,359,t=>this._setHue(t))} />
|
||
</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=${t=>this._setSaturation(Number(t.target.value))}
|
||
@keydown=${t=>this._shiftRangeKey(t,a,0,100,t=>this._setSaturation(t))} />
|
||
</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=${t=>this._setValue(Number(t.target.value))}
|
||
@keydown=${t=>this._shiftRangeKey(t,r,0,100,t=>this._setValue(t))} />
|
||
</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":U}
|
||
@input=${t=>this._hexInput(t)}
|
||
@blur=${()=>this._commitHex()}
|
||
@keydown=${t=>this._hexKeyDown(t)} />
|
||
${this._hexInvalid?W`<span id="hex-error" class="error">${n.invalidHex}</span>`:U}
|
||
</label>
|
||
${this.showOpacity?W`<div class="row">
|
||
<span class="caption">${this.opacityLabel}</span>
|
||
<input type="range" min="0" max="100" step="1" .value=${String(e)}
|
||
aria-label=${this.opacityLabel} aria-valuetext=${`${e}%`}
|
||
@input=${t=>this._setOpacity(Number(t.target.value)/100)}
|
||
@keydown=${t=>this._shiftRangeKey(t,e,0,100,t=>this._setOpacity(t/100))} />
|
||
<input type="number" min="0" max="100" step="1" .value=${String(e)}
|
||
aria-label=${`${this.opacityLabel}, %`}
|
||
@change=${t=>this._setOpacity(Number(t.target.value)/100)} />
|
||
<span class="pct">%</span>
|
||
</div>`:U}
|
||
</div>`}render(){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),e=Rt(this.color,"#607d8b"),i=this.showOpacity?`${this.label||"Color"}: ${e}, ${t}%`:`${this.label||"Color"}: ${e}`;return W`
|
||
${this.label?W`<span class="label">${this.label}</span>`:U}
|
||
<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:${e};opacity:${this.showOpacity?t/100:1}`}></span>
|
||
</button>
|
||
${this._open&&!this.disabled&&this._supportsPopover()?this._pickerTemplate(!0):U}
|
||
`}}Gt.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}},Gt.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",Gt);let Ut=0;class Vt extends ht{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-"+ ++Ut,this._surfacePointerEnter=()=>{const t=this._window();this._closeTimer&&t?.clearTimeout(this._closeTimer),this._closeTimer=0},this._surfacePointerLeave=()=>{this._scheduleClose()},this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closeHelp(!1,"outside")},this._dialogScroll=t=>{const e=this._floating.containsPath(t.composedPath()),i=t.target,s=this._dialog(),n=i instanceof Node&&(i===s||i instanceof Element&&i.contains(this));var o;this._open&&(o=n,!e&&o)&&this._closeHelp(!1,"scroll")},this._keyDown=t=>{if(!this._open||t.defaultPrevented)return;if("Escape"===t.key)return t.preventDefault(),t.stopImmediatePropagation(),void this._closeHelp(this._floating.ownsActiveElement(),"escape");const e=this.renderRoot.querySelector(".trigger");if(!e||!t.composedPath().includes(e))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"===t.key)i.scrollTop=0;else if("End"===t.key)i.scrollTop=i.scrollHeight;else{if(!(t.key in n))return;i.scrollBy({top:n[t.key],behavior:"auto"})}t.preventDefault(),t.stopImmediatePropagation()},this._floating=new Wt(this,"help",this._keyDown),this._queuePosition=()=>{if(!this._open||this._positionRaf)return;const t=this._window();t&&(this._positionRaf=t.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 t=this.ownerDocument.defaultView;this._unsubscribeOpenListeners(),this.removeEventListener("keydown",this._keyDown,!0),this._clearTimers(),this._positionRaf&&t?.cancelAnimationFrame(this._positionRaf),this._positionRaf=0,this._closeHelp(!1,"disconnect"),super.disconnectedCallback()}updated(t){if(t.has("pointerHover")&&!this.pointerHover){const t=this._window();this._openTimer&&t?.clearTimeout(this._openTimer),this._openTimer=0,this._hoverOwned&&this._closeHelp(!1,"outside")}this._hasContent()||!this._open?this._open&&t.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 t=this.text,e=this.ariaLabel,"string"==typeof t&&t.trim().length>0&&"string"==typeof e&&e.trim().length>0;var t,e}_clearTimers(){const t=this._window();this._openTimer&&t?.clearTimeout(this._openTimer),this._closeTimer&&t?.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=0}_scheduleOpen(){const t=this._window();this.pointerHover&&t&&!this._open&&!this._openTimer&&(this._closeTimer&&t.clearTimeout(this._closeTimer),this._closeTimer=0,this._openTimer=t.setTimeout(()=>{this._openTimer=0,this.isConnected&&(this._hoverOwned=!0,this._openHelp())},300))}_scheduleClose(){const t=this._window(),e=this.renderRoot.querySelector(".trigger");t&&!e?.matches(":focus-visible")&&(this._openTimer&&t.clearTimeout(this._openTimer),this._closeTimer&&t.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=t.setTimeout(()=>{this._closeTimer=0,this.isConnected&&this._closeHelp()},150))}_triggerPointerEnter(t){this.pointerHover&&"mouse"===t.pointerType&&this._scheduleOpen()}_triggerPointerLeave(t){"mouse"===t.pointerType&&this._scheduleClose()}_triggerFocus(){queueMicrotask(()=>{const t=this.renderRoot.querySelector(".trigger");t?.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 t=this._window();t?.addEventListener("resize",this._queuePosition),t?.addEventListener("orientationchange",this._queuePosition),t?.visualViewport?.addEventListener("resize",this._queuePosition),t?.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 t=this._window();t?.removeEventListener("resize",this._queuePosition),t?.removeEventListener("orientationchange",this._queuePosition),t?.visualViewport?.removeEventListener("resize",this._queuePosition),t?.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:t=>this._closeHelp("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closeHelp()))}_closeHelp(t=!1,e="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(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_tooltipTemplate(t){return W`<div class="tooltip" data-side="bottom" popover=${t?"manual":U}
|
||
role="tooltip" aria-hidden="true" tabindex="-1"
|
||
@pointerenter=${this._surfacePointerEnter} @pointerleave=${this._surfacePointerLeave}>${this.text}</div>`}_renderFallback(){const t=Vt.styles.cssText;this._floating.renderFallback(this._tooltipTemplate(!1),t)}_surface(){return this._floating.surface(".tooltip",this._usesPopover())}_position(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let i=this._surface();if(!t||!e?.isConnected||!i?.isConnected)return!1;const s=Bt(t);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=e.getBoundingClientRect(),o=i.getBoundingClientRect();if(!(n.width&&n.height&&o.width&&o.height))return!1;const a=qt(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 U;const t=this.ariaLabel.trim();return W`
|
||
<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=${t}
|
||
aria-describedby=${this._open?this._descriptionId:U}
|
||
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):U}
|
||
`}}Vt.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}},Vt.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",Vt);const Kt=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);
|
||
}
|
||
.fixedfloor-loading {
|
||
animation: fixedfloor-spin 1.1s linear infinite;
|
||
}
|
||
.fixedfloor-error p {
|
||
max-width: 42rem;
|
||
margin: 0;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
@keyframes fixedfloor-spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.fixedfloor-loading { animation: none; }
|
||
}
|
||
.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;
|
||
}
|
||
.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;
|
||
}
|
||
.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;
|
||
}
|
||
@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); }
|
||
}
|
||
.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);
|
||
}
|
||
.count {
|
||
font-size: var(--fs-s);
|
||
color: var(--hp-muted);
|
||
}
|
||
.spacer {
|
||
flex: 1;
|
||
}
|
||
.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;
|
||
}
|
||
.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;
|
||
}
|
||
/* 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;
|
||
}
|
||
.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;
|
||
}
|
||
.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;
|
||
}
|
||
.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);
|
||
}
|
||
@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;
|
||
}
|
||
.recoveryoverlay {
|
||
transition: none;
|
||
}
|
||
.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 §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;
|
||
}
|
||
/* 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;
|
||
}
|
||
.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%);
|
||
}
|
||
.stage.mode-view .oplock {
|
||
pointer-events: auto;
|
||
cursor: pointer;
|
||
}
|
||
.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); }
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.op-leaf, .op-arc { transition: none; }
|
||
}
|
||
/* 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; }
|
||
@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; }
|
||
}
|
||
.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;
|
||
}
|
||
.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));
|
||
}
|
||
/* 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; }
|
||
}
|
||
.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; }
|
||
.bindharow {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-5);
|
||
flex-wrap: wrap;
|
||
}
|
||
.bindharow .entcheck { opacity: 0.9; }
|
||
.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;
|
||
}
|
||
.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); }
|
||
.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;
|
||
}
|
||
.hdr.kioskhide { display: none; }
|
||
.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; }
|
||
.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 .openwalls {
|
||
opacity: var(--hp-mode-architecture-opacity, 1);
|
||
}
|
||
.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; }
|
||
hp-dialog .dfill {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
cursor: pointer;
|
||
}
|
||
.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-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-hover.isopen {
|
||
fill: var(--error-color, #f44336);
|
||
stroke: var(--error-color, #f44336);
|
||
}
|
||
.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);
|
||
}
|
||
/* Boundary is neutral off-target, then advertises its resolved action. */
|
||
.stage.markup.tool-boundary { cursor: default; }
|
||
.stage.markup.tool-boundary.boundary-solid { cursor: crosshair; }
|
||
.stage.markup.tool-boundary.boundary-open { cursor: pointer; }
|
||
.stage.markup.tool-boundary.boundary-invalid { cursor: not-allowed; }
|
||
.openwall {
|
||
stroke: var(--ow-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;
|
||
}
|
||
/* Rooms with open/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;
|
||
}
|
||
.openwall-preview {
|
||
stroke: #ffc14d;
|
||
stroke-width: calc(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: round;
|
||
pointer-events: none;
|
||
opacity: 0.95;
|
||
}
|
||
.openwall-preview.boundary-range.invalid {
|
||
stroke: var(--error-color, #f44336);
|
||
}
|
||
.openwall-preview.boundary-restore {
|
||
fill: rgba(255, 193, 77, 0.28);
|
||
fill-rule: evenodd;
|
||
stroke: #ffc14d;
|
||
stroke-width: calc(2.5px * var(--hp-cell-visual-scale, 1));
|
||
stroke-dasharray: none;
|
||
stroke-linejoin: round;
|
||
}
|
||
.boundary-point {
|
||
fill: #ffc14d;
|
||
stroke: #24262d;
|
||
stroke-width: 1.5;
|
||
vector-effect: non-scaling-stroke;
|
||
pointer-events: none;
|
||
}
|
||
.boundary-point.invalid circle {
|
||
fill: var(--error-color, #f44336);
|
||
}
|
||
.boundary-point.invalid path {
|
||
fill: none;
|
||
stroke: #fff;
|
||
stroke-width: 1.7;
|
||
vector-effect: non-scaling-stroke;
|
||
}
|
||
.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;
|
||
}
|
||
.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; }
|
||
}
|
||
.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;
|
||
}
|
||
.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;
|
||
}
|
||
}
|
||
.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;
|
||
}
|
||
.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;
|
||
}
|
||
/* 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;
|
||
}
|
||
@keyframes hp-sunfade-in {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
@keyframes hp-sunfade-out {
|
||
from { opacity: 1; }
|
||
to { opacity: 0; }
|
||
}
|
||
@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; }
|
||
}
|
||
@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;
|
||
}
|
||
.devlayer {
|
||
position: absolute;
|
||
inset: 0;
|
||
pointer-events: none;
|
||
z-index: 6;
|
||
}
|
||
.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;
|
||
}
|
||
.stage.mode-devices .dev { cursor: grab; }
|
||
.stage.mode-devices .dev:active { cursor: grabbing; }
|
||
.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);
|
||
}
|
||
.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; }
|
||
.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%; }
|
||
.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); }
|
||
.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);
|
||
}
|
||
.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;
|
||
}
|
||
.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; }
|
||
.temprange {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
margin-left: auto;
|
||
color: var(--hp-muted);
|
||
font-size: var(--fs-s);
|
||
}
|
||
/* 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);
|
||
}
|
||
/* 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;
|
||
}
|
||
.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;
|
||
}
|
||
/* 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);
|
||
}
|
||
/* ---- 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);
|
||
}
|
||
.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);
|
||
}
|
||
.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;
|
||
}
|
||
.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);
|
||
}
|
||
.alignmsg { margin: 0 0 8px; font-size: 13px; line-height: 1.45; }
|
||
.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; }
|
||
.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;
|
||
}
|
||
.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;
|
||
}
|
||
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;
|
||
}
|
||
/* 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; }
|
||
}
|
||
/* 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; }
|
||
.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;
|
||
}
|
||
.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;
|
||
}
|
||
`,Yt=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),Jt=[{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 Xt(t){const e=[];for(const i of t)if(i&&"string"==typeof i.pattern&&i.icon)try{e.push({re:new RegExp(i.pattern,"i"),icon:i.icon})}catch{}return e}const Zt=Xt(Jt),Qt={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"},te="mdi:chip";function ee(t,e,i){const s=((t||"")+" "+(e||"")).toLowerCase();for(const{re:t,icon:e}of i??Zt)if(t.test(s))return e;return te}const ie=["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 se=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,ne=Math.ceil,oe=Math.floor,ae="[BigNumber Error] ",re=ae+"Number primitive has more than 15 significant digits: ",le=1e14,ce=14,he=9007199254740991,de=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],pe=1e7,ue=1e9;function _e(t){var e=0|t;return t>0||t===e?e:e-1}function me(t){for(var e,i,s=1,n=t.length,o=t[0]+"";s<n;){for(e=t[s++]+"",i=ce-e.length;i--;e="0"+e);o+=e}for(n=o.length;48===o.charCodeAt(--n););return o.slice(0,n+1||1)}function ge(t,e){var i,s,n=t.c,o=e.c,a=t.s,r=e.s,l=t.e,c=e.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 fe(t,e,i,s){if(t<e||t>i||t!==oe(t))throw Error(ae+(s||"Argument")+("number"==typeof t?t<e||t>i?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function ve(t){var e=t.c.length-1;return _e(t.e/ce)==e&&t.c[e]%2!=0}function ye(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function be(t,e,i){var s,n;if(e<0){for(n=i+".";++e;n+=i);t=n+t}else if(++e>(s=t.length)){for(n=i,e-=s;--e;n+=i);t+=n}else e<s&&(t=t.slice(0,e)+"."+t.slice(e));return t}var we=function t(e){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,y=1e7,b=!1,w=1,k=0,x={prefix:"",groupSize:3,secondaryGroupSize:0,groupSeparator:",",decimalSeparator:".",fractionGroupSize:0,fractionGroupSeparator:" ",suffix:""},$="0123456789abcdefghijklmnopqrstuvwxyz",S=!0;function M(t,e){var i,o,a,r,l,c,h,d,p=this;if(!(p instanceof M))return new M(t,e);if(null==e){if(t&&!0===t._isBigNumber)return p.s=t.s,void(!t.c||t.e>y?p.c=p.e=null:t.e<v?p.c=[p.e=0]:(p.e=t.e,p.c=t.c.slice()));if((c="number"==typeof t)&&0*t==0){if(p.s=1/t<0?(t=-t,-1):1,t===~~t){for(r=0,l=t;l>=10;l/=10,r++);return void(r>y?p.c=p.e=null:(p.e=r,p.c=[t]))}d=String(t)}else{if(!se.test(d=String(t)))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(fe(e,2,$.length,"Base"),10==e&&S)return T(p=new M(t),_+p.e+1,m);if(d=String(t),c="number"==typeof t){if(0*t!=0)return n(p,d,c,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(re+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=$.slice(0,e),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(t),c,e)}c=!1,(r=(d=s(d,e,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&&(t>he||t!==oe(t)))throw Error(re+p.s*t);if((r=r-l-1)>y)p.c=p.e=null;else if(r<v)p.c=[p.e=0];else{if(p.e=r,p.c=[],l=(r+1)%ce,r<0&&(l+=ce),l<h){for(l&&p.c.push(+d.slice(0,l)),h-=ce;l<h;)p.c.push(+d.slice(l,l+=ce));l=ce-(d=d.slice(l)).length}else l-=h;for(;l--;d+="0");p.c.push(+d)}}else p.c=[p.e=0]}function C(t,e,i,s){var n,o,a,r,l;if(null==i?i=m:fe(i,0,8),!t.c)return t.toString();if(n=t.c[0],a=t.e,null==e)l=me(t.c),l=1==s||2==s&&(a<=g||a>=f)?ye(l,a):be(l,a,"0");else if(o=(t=T(new M(t),e,i)).e,r=(l=me(t.c)).length,1==s||2==s&&(e<=o||o<=g)){for(;r<e;l+="0",r++);l=ye(l,o)}else if(e-=a+(2===s&&o>a),l=be(l,o,"0"),o+1>r){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=o-r)>0)for(o+1==r&&(l+=".");e--;l+="0");return t.s<0&&n?"-"+l:l}function D(t,e){for(var i,s,n=1,o=new M(t[0]);n<t.length;n++)(!(s=new M(t[n])).s||(i=ge(o,s))===e||0===i&&o.s===e)&&(o=s);return o}function P(t,e,i){for(var s=1,n=e.length;!e[--n];e.pop());for(n=e[0];n>=10;n/=10,s++);return(i=s+i*ce-1)>y?t.c=t.e=null:i<v?t.c=[t.e=0]:(t.e=i,t.c=e),t}function T(t,e,i,s){var n,o,a,r,l,c,h,d=t.c,p=de;if(d){t:{for(n=1,r=d[0];r>=10;r/=10,n++);if((o=e-n)<0)o+=ce,a=e,l=d[c=0],h=oe(l/p[n-a-1]%10);else if((c=ne((o+1)/ce))>=d.length){if(!s)break t;for(;d.length<=c;d.push(0));l=h=0,n=1,a=(o%=ce)-ce+1}else{for(l=r=d[c],n=1;r>=10;r/=10,n++);h=(a=(o%=ce)-ce+n)<0?0:oe(l/p[n-a-1]%10)}if(s=s||e<0||null!=d[c+1]||(a<0?l:l%p[n-a-1]),s=i<4?(h||s)&&(0==i||i==(t.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==(t.s<0?8:7)),e<1||!d[0])return d.length=0,s?(e-=t.e+1,d[0]=p[(ce-e%ce)%ce],t.e=-e||0):d[0]=t.e=0,t;if(0==o?(d.length=c,r=1,c--):(d.length=c+1,r=p[ce-o],d[c]=a>0?oe(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&&(t.e++,d[0]==le&&(d[0]=1));break}if(d[c]+=r,d[c]!=le)break;d[c--]=0,r=1}for(o=d.length;0===d[--o];d.pop());}t.e>y?t.c=t.e=null:t.e<v&&(t.c=[t.e=0])}return t}function R(t){var e,i=t.e;return null===i?t.toString():(e=me(t.c),e=i<=g||i>=f?ye(e,i):be(e,i,"0"),t.s<0?"-"+e:e)}return M.clone=t,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(t){var e,i;if(null!=t){if("object"!=typeof t)throw Error(ae+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(fe(i=t[e],0,ue,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(fe(i=t[e],0,8,e),m=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(fe(i[0],-ue,0,e),fe(i[1],0,ue,e),g=i[0],f=i[1]):(fe(i,-ue,ue,e),g=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)fe(i[0],-ue,-1,e),fe(i[1],1,ue,e),v=i[0],y=i[1];else{if(fe(i,-ue,ue,e),!i)throw Error(ae+e+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(ae+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(ae+"crypto unavailable");b=i}else b=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(fe(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(fe(i=t[e],0,ue,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(ae+e+" not an object: "+i);x=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(ae+e+" invalid: "+i);S="0123456789"==i.slice(0,10),$=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:k,FORMAT:x,ALPHABET:$}},M.isBigNumber=function(t){if(!t||!0!==t._isBigNumber)return!1;if(!M.DEBUG)return!0;var e,i,s=t.c,n=t.e,o=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===o||-1===o)&&n>=-ue&&n<=ue&&n===oe(n)){if(0===s[0]){if(0===n&&1===s.length)return!0;break t}if((e=(n+1)%ce)<1&&(e+=ce),String(s[0]).length==e){for(e=0;e<s.length;e++)if((i=s[e])<0||i>=le||i!==oe(i))break t;if(0!==i)return!0}}}else if(null===s&&null===n&&(null===o||1===o||-1===o))return!0;throw Error(ae+"Invalid BigNumber: "+t)},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 oe(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,n,o,r=0,l=[],c=new M(u);if(null==t?t=_:fe(t,0,ue),n=ne(t/ce),b)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(n*=2));r<n;)(o=131072*e[r]+(e[r+1]>>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[r]=i[0],e[r+1]=i[1]):(l.push(o%1e14),r+=2);r=n/2}else{if(!crypto.randomBytes)throw b=!1,Error(ae+"crypto unavailable");for(e=crypto.randomBytes(n*=7);r<n;)(o=281474976710656*(31&e[r])+1099511627776*e[r+1]+4294967296*e[r+2]+16777216*e[r+3]+(e[r+4]<<16)+(e[r+5]<<8)+e[r+6])>=9e15?crypto.randomBytes(7).copy(e,r):(l.push(o%1e14),r+=7);r=n/7}if(!b)for(;r<n;)(o=a())<9e15&&(l[r++]=o%1e14);for(n=l[--r],t%=ce,n&&t&&(o=de[ce-t],l[r]=oe(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-=ce);for(r=1,o=l[0];o>=10;o/=10,r++);r<ce&&(s-=ce-r)}return c.e=s,c.c=l,c}),M.sum=function(){for(var t=1,e=arguments,i=new M(e[0]);t<e.length;)i=i.plus(e[t++]);return i},s=function(){var t="0123456789";function e(t,e,i,s){for(var n,o,a=[0],r=0,l=t.length;r<l;){for(o=a.length;o--;a[o]*=e);for(a[0]+=s.indexOf(t.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("."),y=_,b=m;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(n)).pow(s.length-v),k=d,f.c=e(be(me(u.c),u.e,"0"),10,o,t),f.e=f.c.length),h=d=(g=e(s,n,o,r?(l=$,t):(l=t,$))).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,y,b,o)).c,p=u.r,h=u.e),v=g[c=h+y+1],d=o/2,p=p||c<0||null!=g[c+1],p=b<4?(null!=v||p)&&(0==b||b==(u.s<0?3:2)):v>d||v==d&&(4==b||p||6==b&&1&g[c-1]||b==(u.s<0?8:7)),c<1||!g[0])s=p?be(l.charAt(1),-y,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=be(s,h,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,n,o,a,r=0,l=t.length,c=e%pe,h=e/pe|0;for(t=t.slice();l--;)r=((n=c*(o=t[l]%pe)+(s=h*o+(a=t[l]/pe|0)*c)%pe*pe+r)/i|0)+(s/pe|0)+h*a,t[l]=n%i;return r&&(t=[r].concat(t)),t}function e(t,e,i,s){var n,o;if(i!=s)o=i>s?1:-1;else for(n=o=0;n<i;n++)if(t[n]!=e[n]){o=t[n]>e[n]?1:-1;break}return o}function i(t,e,i,s){for(var n=0;i--;)t[i]-=n,n=t[i]<e[i]?1:0,t[i]=n*s+t[i]-e[i];for(;!t[0]&&t.length>1;t.splice(0,1));}return function(s,n,o,a,r){var l,c,h,d,p,u,_,m,g,f,v,y,b,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=le,c=_e(s.e/ce)-_e(n.e/ce),S=S/ce|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=oe(r/(D[0]+1)))>1&&(D=t(D,p,r),C=t(C,p,r),x=D.length,w=C.length),b=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=e(D,f,x,v))<0){if(y=f[0],x!=v&&(y=y*r+(f[1]||0)),(p=oe(y/k))>1)for(p>=r&&(p=r-1),_=(u=t(D,p,r)).length,v=f.length;1==e(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(;e(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[b]||0:(f=[C[b]],v=1)}while((b++<w||null!=f[0])&&S--);d=null!=f[0],g[0]||g.splice(0,1)}if(r==le){for(h=1,S=g[0];S>=10;S/=10,h++);T(m,o+(m.e=h+c*ce-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(t,e,i,s){var n,o=i?e:e.replace(d,"");if(h.test(o))t.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(r,function(t,e,i){return n="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=n?t:e}),s&&(n=s,o=o.replace(l,"$1").replace(c,"0.$1")),e!=o))return new M(o,n);if(M.DEBUG)throw Error(ae+"Not a"+(s?" base "+s:"")+" number: "+e);t.s=null}t.c=t.e=null},p.absoluteValue=p.abs=function(){var t=new M(this);return t.s<0&&(t.s=1),t},p.comparedTo=function(t,e){return ge(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,n,o=this;if(null!=t)return fe(t,0,ue),null==e?e=m:fe(e,0,8),T(new M(o),t+o.e+1,e);if(!(i=o.c))return null;if(s=((n=i.length-1)-_e(this.e/ce))*ce,n=i[n])for(;n%10==0;n/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,m)},p.dividedToIntegerBy=p.idiv=function(t,e){return i(this,new M(t,e),0,1)},p.exponentiatedBy=p.pow=function(t,e){var i,s,n,o,a,r,l,c,h=this;if((t=new M(t)).c&&!t.isInteger())throw Error(ae+"Exponent not an integer: "+R(t));if(null!=e&&(e=new M(e)),a=t.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!t.c||!t.c[0])return c=new M(Math.pow(+R(h),a?t.s*(2-ve(t)):+R(t))),e?c.mod(e):c;if(r=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!r&&h.isInteger()&&e.isInteger())&&(h=h.mod(e))}else{if(t.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&&ve(t)?-0:0,h.e>-1&&(o=1/o),new M(r?1/o:o);k&&(o=ne(k/ce+2))}for(a?(i=new M(.5),r&&(t.s=1),l=ve(t)):l=(n=Math.abs(+R(t)))%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(e))}if(n){if(0===(n=oe(n/2)))break;l=n%2}else if(T(t=t.times(i),t.e+1,1),t.e>14)l=ve(t);else{if(0===(n=+R(t)))break;l=n%2}h=h.times(h),o?h.c&&h.c.length>o&&(h.c.length=o):s&&(h=h.mod(e))}return s?c:(r&&(c=u.div(c)),e?c.mod(e):o?T(c,k,m,void 0):c)},p.integerValue=function(t){var e=new M(this);return null==t?t=m:fe(t,0,8),T(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===ge(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return ge(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=ge(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&_e(this.e/ce)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return ge(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=ge(this,new M(t,e)))||0===e},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(t,e){var i,s,n,o,a=this,r=a.s;if(e=(t=new M(t,e)).s,!r||!e)return new M(NaN);if(r!=e)return t.s=-e,a.plus(t);var l=a.e/ce,c=t.e/ce,h=a.c,d=t.c;if(!l||!c){if(!h||!d)return h?(t.s=-e,t):new M(d?a:NaN);if(!h[0]||!d[0])return d[0]?(t.s=-e,t):new M(h[0]?a:3==m?-0:0)}if(l=_e(l),c=_e(c),h=h.slice(),r=l-c){for((o=r<0)?(r=-r,n=h):(c=l,n=d),n.reverse(),e=r;e--;n.push(0));n.reverse()}else for(s=(o=(r=h.length)<(e=d.length))?r:e,r=e=0;e<s;e++)if(h[e]!=d[e]){o=h[e]<d[e];break}if(o&&(n=h,h=d,d=n,t.s=-t.s),(e=(s=d.length)-(i=h.length))>0)for(;e--;h[i++]=0);for(e=le-1;s>r;){if(h[--s]<d[s]){for(i=s;i&&!h[--i];h[i]=e);--h[i],h[s]+=le}h[s]-=d[s]}for(;0==h[0];h.splice(0,1),--c);return h[0]?P(t,h,c):(t.s=3==m?-1:1,t.c=[t.e=0],t)},p.modulo=p.mod=function(t,e){var s,n,o=this;return t=new M(t,e),!o.c||!t.s||t.c&&!t.c[0]?new M(NaN):!t.c||o.c&&!o.c[0]?new M(o):(9==w?(n=t.s,t.s=1,s=i(o,t,0,3),t.s=n,s.s*=n):s=i(o,t,0,w),(t=o.minus(s.times(t))).c[0]||1!=w||(t.s=o.s),t)},p.multipliedBy=p.times=function(t,e){var i,s,n,o,a,r,l,c,h,d,p,u,_,m,g,f=this,v=f.c,y=(t=new M(t,e)).c;if(!(v&&y&&v[0]&&y[0]))return!f.s||!t.s||v&&!v[0]&&!y||y&&!y[0]&&!v?t.c=t.e=t.s=null:(t.s*=f.s,v&&y?(t.c=[0],t.e=0):t.c=t.e=null),t;for(s=_e(f.e/ce)+_e(t.e/ce),t.s*=f.s,(l=v.length)<(d=y.length)&&(_=v,v=y,y=_,n=l,l=d,d=n),n=l+d,_=[];n--;_.push(0));for(m=le,g=pe,n=d;--n>=0;){for(i=0,p=y[n]%g,u=y[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(t,_,s)},p.negated=function(){var t=new M(this);return t.s=-t.s||null,t},p.plus=function(t,e){var i,s=this,n=s.s;if(e=(t=new M(t,e)).s,!n||!e)return new M(NaN);if(n!=e)return t.s=-e,s.minus(t);var o=s.e/ce,a=t.e/ce,r=s.c,l=t.c;if(!o||!a){if(!r||!l)return new M(n/0);if(!r[0]||!l[0])return l[0]?t:new M(r[0]?s:0*n)}if(o=_e(o),a=_e(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)-(e=l.length)<0&&(i=l,l=r,r=i,e=n),n=0;e;)n=(r[--e]=r[e]+l[e]+n)/le|0,r[e]=le===r[e]?0:r[e]%le;return n&&(r=[n].concat(r),++a),P(t,r,a)},p.precision=p.sd=function(t,e){var i,s,n,o=this;if(null!=t&&t!==!!t)return fe(t,1,ue),null==e?e=m:fe(e,0,8),T(new M(o),t,e);if(!(i=o.c))return null;if(s=(n=i.length-1)*ce+1,n=i[n]){for(;n%10==0;n/=10,s--);for(n=i[0];n>=10;n/=10,s++);}return t&&o.e+1>s&&(s=o.e+1),s},p.shiftedBy=function(t){return fe(t,-9007199254740991,he),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,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(+R(a)))||l==1/0?(((e=me(r)).length+c)%2==0&&(e+="0"),l=Math.sqrt(+e),c=_e((c+1)/2)-(c<0||c%2),s=new M(e=l==1/0?"5e"+c:(e=l.toExponential()).slice(0,e.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))),me(o.c).slice(0,l)===(e=me(s.c)).slice(0,l)){if(s.e<c&&--l,"9999"!=(e=e.slice(l-3,l+1))&&(n||"4999"!=e)){+e&&(+e.slice(1)||"5"!=e.charAt(0))||(T(s,s.e+_+2,1),t=!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,t)},p.toExponential=function(t,e){return null!=t&&(fe(t,0,ue),t++),C(this,t,e,1)},p.toFixed=function(t,e){return null!=t&&(fe(t,0,ue),t=t+this.e+1),C(this,t,e)},p.toFormat=function(t,e,i){var s,n=this;if(null==i)null!=t&&e&&"object"==typeof e?(i=e,e=null):t&&"object"==typeof t?(i=t,t=e=null):i=x;else if("object"!=typeof i)throw Error(ae+"Argument not an object: "+i);if(s=n.toFixed(t,e),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(t){var e,s,n,o,a,r,l,c,h,d,p,_,g=this,f=g.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(ae+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+R(l));if(!f)return new M(g);for(e=new M(u),h=s=new M(u),n=c=new M(u),_=me(f),a=e.e=_.length-g.e-1,e.c[0]=de[(r=a%ce)<0?ce+r:r],t=!t||l.comparedTo(e)>0?a>0?e:h:l,r=y,y=1/0,l=new M(_),c.c[0]=0;d=i(l,e,0,1),1!=(o=s.plus(d.times(n))).comparedTo(t);)s=n,n=o,h=c.plus(d.times(o=h)),c=o,e=l.minus(d.times(o=e)),l=o;return o=i(t.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],y=r,p},p.toNumber=function(){return+R(this)},p.toPrecision=function(t,e){return null!=t&&fe(t,1,ue),C(this,t,e,2)},p.toString=function(t){var e,i=this,n=i.s,o=i.e;return null===o?n?(e="Infinity",n<0&&(e="-"+e)):e="NaN":(null==t?e=o<=g||o>=f?ye(me(i.c),o):be(me(i.c),o,"0"):10===t&&S?e=be(me((i=T(new M(i),_+o+1,m)).c),i.e,"0"):(fe(t,2,$.length,"Base"),e=s(be(me(i.c),o,"0"),10,t,n,!0)),n<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return R(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),ke=class{key;left=null;right=null;constructor(t){this.key=t}},xe=class extends ke{constructor(t){super(t)}},$e=class{size=0;modificationCount=0;splayCount=0;splay(t){const e=this.root;if(null==e)return this.compare(t,t),-1;let i=null,s=null,n=null,o=null,a=e;const r=this.compare;let l;for(;;)if(l=r(a.key,t),l>0){let e=a.left;if(null==e)break;if(l=r(e.key,t),l>0&&(a.left=e.right,e.right=a,a=e,e=a.left,null==e))break;null==i?s=a:i.left=a,i=a,a=e}else{if(!(l<0))break;{let e=a.right;if(null==e)break;if(l=r(e.key,t),l<0&&(a.right=e.left,e.left=a,a=e,e=a.right,null==e))break;null==n?o=a:n.right=a,n=a,a=e}}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(t){let e=t,i=e.left;for(;null!=i;){const t=i;e.left=t.right,t.right=e,e=t,i=e.left}return e}splayMax(t){let e=t,i=e.right;for(;null!=i;){const t=i;e.right=t.left,t.left=e,e=t,i=e.right}return e}_delete(t){if(null==this.root)return null;if(0!=this.splay(t))return null;let e=this.root;const i=e,s=e.left;if(this.size--,null==s)this.root=e.right;else{const t=e.right;e=this.splayMax(s),e.right=t,this.root=e}return this.modificationCount++,i}addNewRoot(t,e){this.size++,this.modificationCount++;const i=this.root;null!=i?(e<0?(t.left=i,t.right=i.right,i.right=null):(t.right=i,t.left=i.left,i.left=null),this.root=t):this.root=t}_first(){const t=this.root;return null==t?null:(this.root=this.splayMin(t),this.root)}_last(){const t=this.root;return null==t?null:(this.root=this.splayMax(t),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(t){return this.validKey(t)&&0==this.splay(t)}defaultCompare(){return(t,e)=>t<e?-1:t>e?1:0}wrap(){return{getRoot:()=>this.root,setRoot:t=>{this.root=t},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:t=>{this.splayCount=t},splay:t=>this.splay(t),has:t=>this.has(t)}}},Se=class t extends $e{root=null;compare;validKey;constructor(t,e){super(),this.compare=t??this.defaultCompare(),this.validKey=e??(t=>null!=t&&null!=t)}delete(t){return!!this.validKey(t)&&null!=this._delete(t)}deleteAll(t){for(const e of t)this.delete(e)}forEach(t){const e=this[Symbol.iterator]();let i;for(;i=e.next(),!i.done;)t(i.value,i.value,this)}add(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new xe(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new xe(t),e),this.root.key}addAll(t){for(const e of t)this.add(e)}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(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)<0)return this.root.key;let e=this.root.left;if(null==e)return null;let i=e.right;for(;null!=i;)e=i,i=e.right;return e.key}firstAfter(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)>0)return this.root.key;let e=this.root.right;if(null==e)return null;let i=e.left;for(;null!=i;)e=i,i=e.left;return e.key}retainAll(e){const i=new t(this.compare,this.validKey),s=this.modificationCount;for(const t of e){if(s!=this.modificationCount)throw"Concurrent modification during iteration.";this.validKey(t)&&0==this.splay(t)&&i.add(this.root.key)}i.size!=this.size&&(this.root=i.root,this.size=i.size,this.modificationCount++)}lookup(t){if(!this.validKey(t))return null;return 0!=this.splay(t)?null:this.root.key}intersection(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)&&i.add(t);return i}difference(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)||i.add(t);return i}union(t){const e=this.clone();return e.addAll(t),e}clone(){const e=new t(this.compare,this.validKey);return e.size=this.size,e.root=this.copyNode(this.root),e}copyNode(t){if(null==t)return null;const e=new xe(t.key);return function t(e,i){let s,n;do{if(s=e.left,n=e.right,null!=s){const e=new xe(s.key);i.left=e,t(s,e)}if(null!=n){const t=new xe(n.key);i.right=t,e=n,i=t}}while(null!=n)}(t,e),e}toSet(){return this.clone()}entries(){return new De(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Ce(this.wrap())}[Symbol.toStringTag]="[object Set]"},Me=class{tree;path=new Array;modificationCount=null;splayCount;constructor(t){this.tree=t,this.splayCount=t.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 t=this.path[this.path.length-1];return this.getValue(t)}rebuildPath(t){this.path.splice(0,this.path.length),this.tree.splay(t),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(t){for(;null!=t;)this.path.push(t),t=t.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let t=this.tree.getRoot();for(;null!=t;)this.path.push(t),t=t.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 t=this.path[this.path.length-1],e=t.right;if(null!=e){for(;null!=e;)this.path.push(e),e=e.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===t;)t=this.path.pop();return this.path.length>0}},Ce=class extends Me{getValue(t){return t.key}},De=class extends Me{getValue(t){return[t.key,t.key]}},Pe=t=>()=>t,Te=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):Pe(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function Re(t){const e=t?(e,i,s,n,o)=>e.exponentiatedBy(2).isLessThanOrEqualTo(n.minus(i).exponentiatedBy(2).plus(o.minus(s).exponentiatedBy(2)).times(t)):Pe(!1);return(t,i,s)=>{const n=t.x,o=t.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 e(l,n,o,a,r)?0:l.comparedTo(0)}}var Ie=t=>t,Ae=t=>{if(t){const e=new Se(Te(t)),i=new Se(Te(t)),s=(t,e)=>e.addAndReturn(t),n=t=>({x:s(t.x,e),y:s(t.y,i)});return n({x:new we(0),y:new we(0)}),n}return Ie},ze=t=>({set:t=>{Fe=ze(t)},reset:()=>ze(t),compare:Te(t),snap:Ae(t),orient:Re(t)}),Fe=ze(),Ee=(t,e)=>t.ll.x.isLessThanOrEqualTo(e.x)&&e.x.isLessThanOrEqualTo(t.ur.x)&&t.ll.y.isLessThanOrEqualTo(e.y)&&e.y.isLessThanOrEqualTo(t.ur.y),Ne=(t,e)=>{if(e.ur.x.isLessThan(t.ll.x)||t.ur.x.isLessThan(e.ll.x)||e.ur.y.isLessThan(t.ll.y)||t.ur.y.isLessThan(e.ll.y))return null;const i=t.ll.x.isLessThan(e.ll.x)?e.ll.x:t.ll.x,s=t.ur.x.isLessThan(e.ur.x)?t.ur.x:e.ur.x;return{ll:{x:i,y:t.ll.y.isLessThan(e.ll.y)?e.ll.y:t.ll.y},ur:{x:s,y:t.ur.y.isLessThan(e.ur.y)?t.ur.y:e.ur.y}}},Oe=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),He=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),Le=t=>He(t,t).sqrt(),Be=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return Oe(n,s).div(Le(n)).div(Le(s))},qe=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return He(n,s).div(Le(n)).div(Le(s))},We=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},je=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},Ge=class t{point;isLeft;segment;otherSE;consumedBy;static compare(e,i){const s=t.comparePoints(e.point,i.point);return 0!==s?s:(e.point!==i.point&&e.link(i),e.isLeft!==i.isLeft?e.isLeft?1:-1:Qe.compare(e.segment,i.segment))}static comparePoints(t,e){return t.x.isLessThan(e.x)?-1:t.x.isGreaterThan(e.x)?1:t.y.isLessThan(e.y)?-1:t.y.isGreaterThan(e.y)?1:0}constructor(t,e){void 0===t.events?t.events=[this]:t.events.push(this),this.point=t,this.isLeft=e}link(t){if(t.point===this.point)throw new Error("Tried to link already linked events");const e=t.point.events;for(let t=0,i=e.length;t<i;t++){const i=e[t];this.point.events.push(i),i.point=this.point}this.checkForConsuming()}checkForConsuming(){const t=this.point.events.length;for(let e=0;e<t;e++){const i=this.point.events[e];if(void 0===i.segment.consumedBy)for(let s=e+1;s<t;s++){const t=this.point.events[s];void 0===t.consumedBy&&(i.otherSE.point.events===t.otherSE.point.events&&i.segment.consume(t.segment))}}}getAvailableLinkedEvents(){const t=[];for(let e=0,i=this.point.events.length;e<i;e++){const i=this.point.events[e];i!==this&&!i.segment.ringOut&&i.segment.isInResult()&&t.push(i)}return t}getLeftmostComparator(t){const e=new Map,i=i=>{const s=i.otherSE;e.set(i,{sine:Be(this.point,t.point,s.point),cosine:qe(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:n,cosine:o}=e.get(t),{sine:a,cosine:r}=e.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}}},Ue=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,n=e.length;s<n;s++){const n=e[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 e=a.getAvailableLinkedEvents();if(0===e.length){const t=l[0].point,e=l[l.length-1].point;throw new Error(`Unable to complete output ring starting at [${t.x}, ${t.y}]. Last matching segment found ends at [${e.x}, ${e.y}].`)}if(1===e.length){r=e[0].otherSE;break}let s=null;for(let t=0,e=h.length;t<e;t++)if(h[t].point===a.point){s=t;break}if(null!==s){const e=h.splice(s)[0],n=l.splice(e.index);n.unshift(n[0].otherSE),i.push(new t(n.reverse()));continue}h.push({index:l.length,point:a.point});const n=a.getLeftmostComparator(o);r=e.sort(n)[0].otherSE;break}i.push(new t(l))}return i}constructor(t){this.events=t;for(let e=0,i=t.length;e<i;e++)t[e].segment.ringOut=this;this.poly=null}getGeom(){let t=this.events[0].point;const e=[t];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!==Fe.orient(s,t,n)&&(e.push(s),t=s)}if(1===e.length)return null;const i=e[0],s=e[1];0===Fe.orient(i,t,s)&&e.shift(),e.push(e[0]);const n=this.isExteriorRing()?1:-1,o=this.isExteriorRing()?0:e.length-1,a=this.isExteriorRing()?e.length:-1,r=[];for(let t=o;t!=a;t+=n)r.push([e[t].x.toNumber(),e[t].y.toNumber()]);return r}isExteriorRing(){if(void 0===this._isExteriorRing){const t=this.enclosingRing();this._isExteriorRing=!t||!t.isExteriorRing()}return this._isExteriorRing}enclosingRing(){return void 0===this._enclosingRing&&(this._enclosingRing=this._calcEnclosingRing()),this._enclosingRing}_calcEnclosingRing(){let t=this.events[0];for(let e=1,i=this.events.length;e<i;e++){const i=this.events[e];Ge.compare(t,i)>0&&(t=i)}let e=t.segment.prevInResult(),i=e?e.prevInResult():null;for(;;){if(!e)return null;if(!i)return e.ringOut;if(i.ringOut!==e.ringOut)return i.ringOut?.enclosingRing()!==e.ringOut?e.ringOut:e.ringOut?.enclosingRing();e=i.prevInResult(),i=e?e.prevInResult():null}}},Ve=class{exteriorRing;interiorRings;constructor(t){this.exteriorRing=t,t.poly=this,this.interiorRings=[]}addInterior(t){this.interiorRings.push(t),t.poly=this}getGeom(){const t=this.exteriorRing.getGeom();if(null===t)return null;const e=[t];for(let t=0,i=this.interiorRings.length;t<i;t++){const i=this.interiorRings[t].getGeom();null!==i&&e.push(i)}return e}},Ke=class{rings;polys;constructor(t){this.rings=t,this.polys=this._composePolys(t)}getGeom(){const t=[];for(let e=0,i=this.polys.length;e<i;e++){const i=this.polys[e].getGeom();null!==i&&t.push(i)}return t}_composePolys(t){const e=[];for(let i=0,s=t.length;i<s;i++){const s=t[i];if(!s.poly)if(s.isExteriorRing())e.push(new Ve(s));else{const t=s.enclosingRing();t?.poly||e.push(new Ve(t)),t?.poly?.addInterior(s)}}return e}},Ye=class{queue;tree;segments;constructor(t,e=Qe.compare){this.queue=t,this.tree=new Se(e),this.segments=[]}process(t){const e=t.segment,i=[];if(t.consumedBy)return t.isLeft?this.queue.delete(t.otherSE):this.tree.delete(e),i;t.isLeft&&this.tree.add(e);let s=e,n=e;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(t.isLeft){let o=null;if(s){const t=s.getIntersection(e);if(null!==t&&(e.isAnEndpoint(t)||(o=t),!s.isAnEndpoint(t))){const e=this._splitSafely(s,t);for(let t=0,s=e.length;t<s;t++)i.push(e[t])}}let a=null;if(n){const t=n.getIntersection(e);if(null!==t&&(e.isAnEndpoint(t)||(a=t),!n.isAnEndpoint(t))){const e=this._splitSafely(n,t);for(let t=0,s=e.length;t<s;t++)i.push(e[t])}}if(null!==o||null!==a){let t=null;if(null===o)t=a;else if(null===a)t=o;else{t=Ge.comparePoints(o,a)<=0?o:a}this.queue.delete(e.rightSE),i.push(e.rightSE);const s=e.split(t);for(let t=0,e=s.length;t<e;t++)i.push(s[t])}i.length>0?(this.tree.delete(e),i.push(t)):(this.segments.push(e),e.prev=s)}else{if(s&&n){const t=s.getIntersection(n);if(null!==t){if(!s.isAnEndpoint(t)){const e=this._splitSafely(s,t);for(let t=0,s=e.length;t<s;t++)i.push(e[t])}if(!n.isAnEndpoint(t)){const e=this._splitSafely(n,t);for(let t=0,s=e.length;t<s;t++)i.push(e[t])}}}this.tree.delete(e)}return i}_splitSafely(t,e){this.tree.delete(t);const i=t.rightSE;this.queue.delete(i);const s=t.split(e);return s.push(i),void 0===t.consumedBy&&this.tree.add(t),s}},Je=new class{type;numMultiPolys;run(t,e,i){Je.type=t;const s=[new ii(e,!0)];for(let t=0,e=i.length;t<e;t++)s.push(new ii(i[t],!1));if(Je.numMultiPolys=s.length,"difference"===Je.type){const t=s[0];let e=1;for(;e<s.length;)null!==Ne(s[e].bbox,t.bbox)?e++:s.splice(e,1)}if("intersection"===Je.type)for(let t=0,e=s.length;t<e;t++){const e=s[t];for(let i=t+1,n=s.length;i<n;i++)if(null===Ne(e.bbox,s[i].bbox))return[]}const n=new Se(Ge.compare);for(let t=0,e=s.length;t<e;t++){const e=s[t].getSweepEvents();for(let t=0,i=e.length;t<i;t++)n.add(e[t])}const o=new Ye(n);let a=null;for(0!=n.size&&(a=n.first(),n.delete(a));a;){const t=o.process(a);for(let e=0,i=t.length;e<i;e++){const i=t[e];void 0===i.consumedBy&&n.add(i)}0!=n.size?(a=n.first(),n.delete(a)):a=null}Fe.reset();const r=Ue.factory(o.segments);return new Ke(r).getGeom()}},Xe=Je,Ze=0,Qe=class t{id;leftSE;rightSE;rings;windings;ringOut;consumedBy;prev;_prevInResult;_beforeState;_afterState;_isInResult;static compare(t,e){const i=t.leftSE.point.x,s=e.leftSE.point.x,n=t.rightSE.point.x,o=e.rightSE.point.x;if(o.isLessThan(i))return 1;if(n.isLessThan(s))return-1;const a=t.leftSE.point.y,r=e.leftSE.point.y,l=t.rightSE.point.y,c=e.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=t.comparePoint(e.leftSE.point);if(i<0)return 1;if(i>0)return-1;const s=e.comparePoint(t.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=e.comparePoint(t.leftSE.point);if(0!==i)return i;const s=t.comparePoint(e.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=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(n.isGreaterThan(o)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!n.eq(o)){const t=l.minus(a),e=n.minus(i),h=c.minus(r),d=o.minus(s);if(t.isGreaterThan(e)&&h.isLessThan(d))return 1;if(t.isLessThan(e)&&h.isGreaterThan(d))return-1}return n.isGreaterThan(o)?1:n.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:t.id<e.id?-1:t.id>e.id?1:0}constructor(t,e,i,s){this.id=++Ze,this.leftSE=t,t.segment=this,t.otherSE=e,this.rightSE=e,e.segment=this,e.otherSE=t,this.rings=i,this.windings=s}static fromRing(e,i,s){let n,o,a;const r=Ge.comparePoints(e,i);if(r<0)n=e,o=i,a=1;else{if(!(r>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);n=i,o=e,a=-1}const l=new Ge(n,!0),c=new Ge(o,!1);return new t(l,c,[s],[a])}replaceRightSE(t){this.rightSE=t,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const t=this.leftSE.point.y,e=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:t.isLessThan(e)?t:e},ur:{x:this.rightSE.point.x,y:t.isGreaterThan(e)?t:e}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(t){return t.x.eq(this.leftSE.point.x)&&t.y.eq(this.leftSE.point.y)||t.x.eq(this.rightSE.point.x)&&t.y.eq(this.rightSE.point.y)}comparePoint(t){return Fe.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=Ne(e,i);if(null===s)return null;const n=this.leftSE.point,o=this.rightSE.point,a=t.leftSE.point,r=t.rightSE.point,l=Ee(e,a)&&0===this.comparePoint(a),c=Ee(i,n)&&0===t.comparePoint(n),h=Ee(e,r)&&0===this.comparePoint(r),d=Ee(i,o)&&0===t.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=((t,e,i,s)=>{if(e.x.isZero())return je(i,s,t.x);if(s.x.isZero())return je(t,e,i.x);if(e.y.isZero())return We(i,s,t.y);if(s.y.isZero())return We(t,e,i.y);const n=Oe(e,s);if(n.isZero())return null;const o={x:i.x.minus(t.x),y:i.y.minus(t.y)},a=Oe(o,e).div(n),r=Oe(o,s).div(n),l=t.x.plus(r.times(e.x)),c=i.x.plus(a.times(s.x)),h=t.y.plus(r.times(e.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,t.vector());return null===p?null:Ee(s,p)?Fe.snap(p):null}split(e){const i=[],s=void 0!==e.events,n=new Ge(e,!0),o=new Ge(e,!1),a=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(n);const r=new t(n,a,this.rings.slice(),this.windings.slice());return Ge.comparePoints(r.leftSE.point,r.rightSE.point)>0&&r.swapEvents(),Ge.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(n.checkForConsuming(),o.checkForConsuming()),i}swapEvents(){const t=this.rightSE;this.rightSE=this.leftSE,this.leftSE=t,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let t=0,e=this.windings.length;t<e;t++)this.windings[t]*=-1}consume(e){let i=this,s=e;for(;i.consumedBy;)i=i.consumedBy;for(;s.consumedBy;)s=s.consumedBy;const n=t.compare(i,s);if(0!==n){if(n>0){const t=i;i=s,s=t}if(i.prev===s){const t=i;i=s,s=t}for(let t=0,e=s.rings.length;t<e;t++){const e=s.rings[t],n=s.windings[t],o=i.rings.indexOf(e);-1===o?(i.rings.push(e),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 t=this.prev.consumedBy||this.prev;this._beforeState=t.afterState()}else this._beforeState={rings:[],windings:[],multiPolys:[]};return this._beforeState}afterState(){if(void 0!==this._afterState)return this._afterState;const t=this.beforeState();this._afterState={rings:t.rings.slice(0),windings:t.windings.slice(0),multiPolys:[]};const e=this._afterState.rings,i=this._afterState.windings,s=this._afterState.multiPolys;for(let t=0,s=this.rings.length;t<s;t++){const s=this.rings[t],n=this.windings[t],o=e.indexOf(s);-1===o?(e.push(s),i.push(n)):i[o]+=n}const n=[],o=[];for(let t=0,s=e.length;t<s;t++){if(0===i[t])continue;const s=e[t],a=s.poly;if(-1===o.indexOf(a))if(s.isExterior)n.push(a);else{-1===o.indexOf(a)&&o.push(a);const t=n.indexOf(s.poly);-1!==t&&n.splice(t,1)}}for(let t=0,e=n.length;t<e;t++){const e=n[t].multiPoly;-1===s.indexOf(e)&&s.push(e)}return this._afterState}isInResult(){if(this.consumedBy)return!1;if(void 0!==this._isInResult)return this._isInResult;const t=this.beforeState().multiPolys,e=this.afterState().multiPolys;switch(Xe.type){case"union":{const i=0===t.length,s=0===e.length;this._isInResult=i!==s;break}case"intersection":{let i,s;t.length<e.length?(i=t.length,s=e.length):(i=e.length,s=t.length),this._isInResult=s===Xe.numMultiPolys&&i<s;break}case"xor":{const i=Math.abs(t.length-e.length);this._isInResult=i%2==1;break}case"difference":{const i=t=>1===t.length&&t[0].isSubject;this._isInResult=i(t)!==i(e);break}}return this._isInResult}},ti=class{poly;isExterior;segments;bbox;constructor(t,e,i){if(!Array.isArray(t)||0===t.length)throw new Error("Input geometry is not a valid Polygon or MultiPolygon");if(this.poly=e,this.isExterior=i,this.segments=[],"number"!=typeof t[0][0]||"number"!=typeof t[0][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const s=Fe.snap({x:new we(t[0][0]),y:new we(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let n=s;for(let e=1,i=t.length;e<i;e++){if("number"!=typeof t[e][0]||"number"!=typeof t[e][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const i=Fe.snap({x:new we(t[e][0]),y:new we(t[e][1])});i.x.eq(n.x)&&i.y.eq(n.y)||(this.segments.push(Qe.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(Qe.fromRing(n,s,this))}getSweepEvents(){const t=[];for(let e=0,i=this.segments.length;e<i;e++){const i=this.segments[e];t.push(i.leftSE),t.push(i.rightSE)}return t}},ei=class{multiPoly;exteriorRing;interiorRings;bbox;constructor(t,e){if(!Array.isArray(t))throw new Error("Input geometry is not a valid Polygon or MultiPolygon");this.exteriorRing=new ti(t[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 e=1,i=t.length;e<i;e++){const i=new ti(t[e],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=e}getSweepEvents(){const t=this.exteriorRing.getSweepEvents();for(let e=0,i=this.interiorRings.length;e<i;e++){const i=this.interiorRings[e].getSweepEvents();for(let e=0,s=i.length;e<s;e++)t.push(i[e])}return t}},ii=class{isSubject;polys;bbox;constructor(t,e){if(!Array.isArray(t))throw new Error("Input geometry is not a valid Polygon or MultiPolygon");try{"number"==typeof t[0][0][0]&&(t=[t])}catch(t){}this.polys=[],this.bbox={ll:{x:new we(Number.POSITIVE_INFINITY),y:new we(Number.POSITIVE_INFINITY)},ur:{x:new we(Number.NEGATIVE_INFINITY),y:new we(Number.NEGATIVE_INFINITY)}};for(let e=0,i=t.length;e<i;e++){const i=new ei(t[e],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=e}getSweepEvents(){const t=[];for(let e=0,i=this.polys.length;e<i;e++){const i=this.polys[e].getSweepEvents();for(let e=0,s=i.length;e<s;e++)t.push(i[e])}return t}},si=(t,...e)=>Xe.run("union",t,e),ni=(t,...e)=>Xe.run("intersection",t,e),oi=(t,...e)=>Xe.run("difference",t,e);function ai(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function ri(t,e){if(!(Number.isFinite(t)&&e>0))return t;const i=Math.round(t/e)*e;return Math.abs(i-t)<=1e-9*e?t:i}function li(t,e){if(e){const e=t/2.54;let i=Math.floor(e/12),s=Math.round(e-12*i);return 12===s&&(i+=1,s=0),`${i}′ ${s}″`}return`${(t/100).toFixed(2)} m`}function ci(t,e,i=1){const s=t[0].toFixed(i),n=t[1].toFixed(i),o=e[0].toFixed(i),a=e[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(t){return t?.poly?.length>=3?t.poly:t&&null!=t.x&&null!=t.y&&null!=t.w&&null!=t.h?[[t.x,t.y],[t.x+t.w,t.y],[t.x+t.w,t.y+t.h],[t.x,t.y+t.h]]:null}function di(t){const e=[];for(const i of t||[])i?.poly?.length>=3?e.push({poly:i.poly.map(t=>t.join(",")).join(" ")}):i&&null!=i.x&&null!=i.y&&null!=i.w&&null!=i.h&&e.push({rect:{x:i.x,y:i.y,w:i.w,h:i.h,rx:.03*Math.min(i.w,i.h)}});return e}function pi(t){const e=[],i=new Set;for(const s of t||[]){const t=hi(s);if(t)for(let s=0;s<t.length;s++){const n=t[s],o=t[(s+1)%t.length],a=ci(n,o,5);i.has(a)||(i.add(a),e.push([n[0],n[1],o[0],o[1]]))}}return e}function ui(t,e,i,s={}){let n=null,o=i;for(const i of pi(e)){const[e,a,r,l]=i,c=r-e,h=l-a,d=c*c+h*h;if(!d)continue;let p=((t[0]-e)*c+(t[1]-a)*h)/d;p=Math.max(0,Math.min(1,p));const u=[e+p*c,a+p*h],_=Math.hypot(t[0]-u[0],t[1]-u[1]);if(_<o){o=_;let t=180*Math.atan2(h,c)/Math.PI;if(t>=90?t-=180:t<-90&&(t+=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:e+l*c,y:a+l*h,angle:t}}else n={x:u[0],y:u[1],angle:t}}}return n}function _i(t,e,i,s,n,o=1){const a=e*Math.PI/180,r=[Math.cos(a),Math.sin(a)];let l=null,c=o;for(const e of pi(s)){const i=[[e[0],e[1]],[e[2],e[3]]],s=e=>Math.abs(r[0]*(e[1]-t[1])-r[1]*(e[0]-t[0]));if(s(i[0])>o||s(i[1])>o)continue;const n=(i[0][0]-t[0])*r[0]+(i[0][1]-t[1])*r[1],a=(i[1][0]-t[0])*r[0]+(i[1][1]-t[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=e=>[t[0]+r[0]*e,t[1]+r[1]*e],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(t,e,i=!1){if("passage"===t)return 1;if(null==e||"unavailable"===e||"unknown"===e)return"window"===t?0:1;const s=function(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}(e)!==!!i;return s?1:0}function gi(t){return"passage"===t.type?[]:[t.contact,t.lock].filter(t=>"string"==typeof t&&t.length>0)}function fi(t,e,i=.001){return Math.abs(t[0]-e[0])<i&&Math.abs(t[1]-e[1])<i}function vi(t,e){let i=!1;for(let s=0,n=e.length-1;s<e.length;n=s++){const[o,a]=e[s],[r,l]=e[n];a>t[1]!=l>t[1]&&t[0]<(r-o)*(t[1]-a)/(l-a)+o&&(i=!i)}return i}function yi(t,e,i){const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;let a=o?((t[0]-e[0])*s+(t[1]-e[1])*n)/o:0;return a=Math.max(0,Math.min(1,a)),Math.hypot(t[0]-(e[0]+a*s),t[1]-(e[1]+a*n))}function bi(t,e,i=1e-6){if(!e||e.length<2)return!1;for(let s=0;s<e.length;s++)if(yi(t,e[s],e[(s+1)%e.length])<=i)return!0;return!1}function wi(t,e,i=1e-6){return!(!e||e.length<3)&&(!bi(t,e,i)&&vi(t,e))}function ki(t,e,i){return(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0])}function xi(t,e,i,s,n=1e-9){const o=ki(i,s,t),a=ki(i,s,e),r=ki(t,e,i),l=ki(t,e,s);return(o>n&&a<-n||o<-n&&a>n)&&(r>n&&l<-n||r<-n&&l>n)}function $i(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[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 e=0;e<t.length;e++){const i=t[e],s=t[(e+1)%t.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=(e,i)=>{const s=((e,i)=>{if(!vi([e,i],t))return-1/0;let s=1/0;for(let n=0;n<t.length;n++){const o=t[n],a=t[(n+1)%t.length];s=Math.min(s,Hs([e,i],[o[0],o[1],a[0],a[1]]))}return s})(e,i);return s===-1/0?s:s-.08*Math.hypot(e-p[0],i-p[1])-1e-4*l};let _=null,m=-1/0;for(let t=1;t<e;t++)for(let i=1;i<e;i++){const s=n+(o-n)*t/e,l=a+(r-a)*i/e,c=u(s,l);c>m&&(m=c,_=[s,l])}if(_){const[t,i]=_,s=(o-n)/e,l=(r-a)/e;for(let e=-4;e<=4;e++)for(let n=-4;n<=4;n++){const o=t+s*e/4,a=i+l*n/4,r=u(o,a);r>m&&(m=r,_=[o,a])}}return _||Si(t)||t[0]}function Si(t,e=1e-6){if(!t||t.length<3)return null;const i=t.length,s=[t.reduce((t,e)=>t+e[0],0)/i,t.reduce((t,e)=>t+e[1],0)/i];if(wi(s,t,e))return s;for(let s=0;s<i;s++){const n=t[(s-1+i)%i],o=t[s],a=t[(s+1)%i],r=[(n[0]+o[0]+a[0])/3,(n[1]+o[1]+a[1])/3];if(wi(r,t,e))return r}for(let s=0;s<i;s++)for(let n=s+2;n<i;n++){const i=[(t[s][0]+t[n][0])/2,(t[s][1]+t[n][1])/2];if(wi(i,t,e))return i}return null}function Mi(t,e,i){let s=!0;for(const n of t){if(wi(n,e,i))return!0;bi(n,e,i)||(s=!1)}if(s){const s=Si(t,i);return!!s&&wi(s,e,i)}return!1}function Ci(t,e,i=1e-6){if(!t||!e||t.length<3||e.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!1;for(const s of e)if(!wi(s,t,i)&&!bi(s,t,i))return!1;const s=Si(e,i);return!!s&&wi(s,t,i)&&Ti(e)<Ti(t)-i}function Di(t,e,i=1e-6){if(!t||!e||t.length<3||e.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!0;return!Ci(t,e,i)&&!Ci(e,t,i)&&(Mi(t,e,i)||Mi(e,t,i))}function Pi(t,e,i=1e-6){const s=e.filter(e=>Ci(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&Ci(e,t,i)))}function Ti(t){if(!t||t.length<3)return 0;let e=0;for(let i=0;i<t.length;i++){const s=t[i],n=t[(i+1)%t.length];e+=s[0]*n[1]-n[0]*s[1]}return Math.abs(e)/2}function Ri(t){return[[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]]}function Ii(t,e,i){for(let s=0;s<t.length;s++)if(yi(e,t[s],t[(s+1)%t.length])<=i)return s;return-1}function Ai(t,e){const i=[];for(const s of t)i.length&&fi(i[i.length-1],s,e)||i.push(s);return i.length>1&&fi(i[0],i[i.length-1],e)&&i.pop(),i}function zi(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(fi(s,n,i))return null;const o=Ii(t,s,i),a=Ii(t,n,i);if(o<0||a<0)return null;const r=e.slice(1,-1);for(const e of r)if(!wi(e,t,i))return null;for(let i=0;i<e.length-1;i++)for(let s=0;s<t.length;s++)if(xi(e[i],e[i+1],t[s],t[(s+1)%t.length]))return null;for(let t=0;t<e.length-1;t++)for(let i=t+2;i<e.length-1;i++)if(xi(e[t],e[t+1],e[i],e[i+1]))return null;if(2===e.length&&!wi([(s[0]+n[0])/2,(s[1]+n[1])/2],t,i))return null;const l=(e,s,n,o)=>{const a=[e];let r=(s+1)%t.length;for(let e=0;e<=t.length&&(a.push(t[r]),r!==o);e++)r=(r+1)%t.length;return a.push(n),Ai(a,i)};let c,h;if(o===a){const a=Ai([...e],i);if(a.length<3||Ti(a)<=i)return null;const r=[];for(let i=0;i<t.length;i++)if(r.push(t[i]),i===o){const i=(t[(o+1)%t.length][0]-t[o][0])*(n[0]-s[0])+(t[(o+1)%t.length][1]-t[o][1])*(n[1]-s[1])>=0?e:[...e].reverse();for(const t of i)r.push(t)}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(t))>Math.max(i,1e-6*Ti(t))?null:[c,h]}function Fi(t,e,i){const[s,n]=t.split(":");return"device"===s?n:"entity"===s?"lg_"+n:e&&e.startsWith("v_")?e:i()}function Ei(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function Ni(t,e){if(e>t[2]/t[3]){const i=t[3],s=t[3]*e;return{x:t[0]-(s-t[2])/2,y:t[1],w:s,h:i}}const i=t[2],s=t[2]/e;return{x:t[0],y:t[1]-(s-t[3])/2,w:i,h:s}}function Oi(t,e,i,s){if(t.length<2)return;const n=e.x+s,o=e.x+e.w-s,a=e.y+s,r=e.y+e.h-s;for(let e=0;e<60;e++){let e=!1;for(let s=0;s<t.length;s++)for(let n=s+1;n<t.length;n++){const o=t[n].x-t[s].x,a=t[n].y-t[s].y,r=Math.hypot(o,a)||.001;if(r<i){const l=(i-r)/2,c=o/r,h=a/r;t[s].x-=c*l,t[s].y-=h*l,t[n].x+=c*l,t[n].y+=h*l,e=!0}}for(const e of t)e.x=Math.max(n,Math.min(o,e.x)),e.y=Math.max(a,Math.min(r,e.y));if(!e)break}}function Hi(t){if(!t)return null;const e=t.trim();return/^(https?:)?\/\//i.test(e)||e.startsWith("/")||/^[\w./#?=&%~-]+$/i.test(e)?/^[a-z][\w+.-]*:/i.test(e)&&!/^https?:/i.test(e)?null:e:null}Fe.set;const Li=["badge","icon_ripple","value","static_icon"];function Bi(t){return"ripple"===t?"icon_ripple":Li.includes(t)?t:"badge"}const qi=["info","more-info","toggle","run"],Wi=["custom","lqi","light","temp"],ji=["none","lqi","light","temp","custom"],Gi=new Set(["garage","door","gate"]),Ui=["automation","script","scene"];const Vi="—",Ki="{}";function Yi(t){const e=String(t??"").trim();if(!e)return null;let i=e,s="";const n=e.indexOf(":");if(n>=0)i=e.slice(0,n).trim(),s=e.slice(n+1).trim();else{const t=e.split(".");t.length>2&&(i=t.slice(0,2).join("."),s=t.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(t,e){const i=String(t??"").trim(),s=String(e??"").trim(),n=Yi(s?`${i}:${s}`:i);return n?`{${n.entity}${n.attr?`:${n.attr}`:""}}`:""}function Xi(t,e,i){const s=String(e??"").trim();if(!s)return null;const n=t?.states?.[s];if(!n)return null;const o=String(i??"").trim(),a=t=>t.slice(0,60);if(o){const e=function(t){if(null==t)return null;if(Array.isArray(t)){const e=t.map(t=>null==t?"":String(t)).join(", ");return e?e.slice(0,60):null}if("object"==typeof t)return null;const e=String(t);return""===e?null:e.slice(0,60)}(n.attributes?.[o]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,n,o);if("string"==typeof e&&""!==e)return{text:a(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const r=n.state;if(null==r||""===r)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,n);if("string"==typeof e&&""!==e)return{text:a(e),formatted:!0}}catch{}return{text:a(String(r)),formatted:!1}}function Zi(t,e,i){const s=String(e??"").trim(),n=String(i??"").trim()||s;if(!n)return t.text;const o=t.formatted&&s?function(t,e){if(!e)return t;const i=t.replace(/\s+$/,"");return i.endsWith(e)?i.slice(0,i.length-e.length).replace(/\s+$/,""):t}(t.text,s):t.text;return`${o} ${n}`}function Qi(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],n=s?.state;if(!s||null==n||""===n||"unavailable"===n||"unknown"===n)return Vi;const o=(e?.attr||"").trim(),a=Xi(t,i,o||null);if(null===a)return Vi;return Zi(a,o?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}function ts(t,e,i,s=()=>!0){const n=t??"";let o=!1;const a=n.replace(/\{([^{}\r\n]+)\}/g,(t,e)=>{const n=Yi(e);return n?(o=!0,s(n.entity||"")?Qi(i,n):Vi):t});if(o)return a;const r=(e?.entity||"").trim();if(!r)return n;if(!s(r)){const t=n.indexOf(Ki);return t>=0?n.slice(0,t)+Vi+n.slice(t+2):n?`${n} ${Vi}`:Vi}const l=Qi(i,e),c=n.indexOf(Ki);return c>=0?n.slice(0,c)+l+n.slice(c+2):n?`${n} ${l}`:l}const es=20;function is(t){const e=Number(t?.scale);if(Number.isFinite(e)&&e>0)return Math.min(20,Math.max(.15,e));return{s:.7,m:1,l:1.5}[String(t?.size??"")]??1}function ss(t,e){if(!e)return t;let i=t;for(const[t,s]of Object.entries(e))i=i.split("{"+t+"}").join(String(s));return i}const ns="#55606c",os=.55,as={c:"#607d8b",a:.18};function rs(t,e=as){const i=t&&"object"==typeof t?t:null,s=i?.a;return{c:Rt(i?.c,e.c),a:"number"==typeof s&&Number.isFinite(s)?Math.min(1,Math.max(0,s)):e.a}}function ls(t,e){const i=rs(t),s=e?.settings?.custom_fill;return s&&"object"==typeof s?rs(s,i):i}function cs(t){const e=t?.settings||{},i=!t?.plan_url,s="glow"===e.fill_mode;return{showBorders:e.show_borders??i,showNames:e.show_names??i,color:Rt(e.room_color,ns),opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):os,fill:["lqi","light","temp","custom"].includes(e.fill_mode)?e.fill_mode:"none",customFill:rs(e.custom_fill),glow:"boolean"==typeof e.glow_enabled?e.glow_enabled:s,tempMin:"number"==typeof e.temp_min?e.temp_min:20,tempMax:"number"==typeof e.temp_max?e.temp_max:25,showLqi:"boolean"==typeof e.show_lqi?e.show_lqi:null,cardFontScale:"number"==typeof e.card_font_scale&&e.card_font_scale>0?Math.min(3,Math.max(.5,e.card_font_scale)):1,labelTemp:!0===e.label_temp,labelHum:!0===e.label_hum,labelLqi:!0===e.label_lqi,labelLight:!0===e.label_light,bgColor:Rt(e.bg_color,null),hideDecor:!0===e.hide_decor,hideOpenings:!0===e.hide_openings}}function hs(t,e){if(e.bgColor)return e.bgColor;const i=t?.bg_color;return Rt(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(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(ds)){const s=ds[t],n=i[t];e[t]={c:Rt(n?.c,s.c),a:n&&"number"==typeof n.a?Math.min(1,Math.max(0,n.a)):s.a}}return e}function us(t,e,i){const s=Math.min(1,Math.max(0,i)),n=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),o=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),a=n.map((t,e)=>Math.round(t+(o[e]-t)*s));return"#"+a.map(t=>t.toString(16).padStart(2,"0")).join("")}function _s(t,e,i,s,n,o,a,r=as){const l=function(t,e,i,s,n,o,a,r=as){if("custom"===t)return rs(r);if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:us(a.lqi_low.c,a.lqi_high.c,t),a:a.lqi_low.a+(a.lqi_high.a-a.lqi_low.a)*Math.min(1,Math.max(0,t))}}if("light"===t)return"none"===i?a.light_none.a>0?a.light_none:null:"on"===i?a.light_on:a.light_off;if("temp"===t){if(null==s)return null;const t=Math.min(n,o),e=Math.max(n,o);return s<t?a.temp_cold:s>e?a.temp_hot:a.temp_ok}return null}(t,e,i,s,n,o,a,r);return l?{color:l.c,opacity:l.a,mode:t}: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(t){for(const e of[...Object.values(ms),...gs])if(t===e[0]||t===e[1])return e;return null}function vs(t,e,i,s,n){if(!s||"unavailable"===s||"unknown"===s)return t;if(n){const i="cover"===e?fs(t):null;return i?"closed"===s?i[0]:i[1]:t}if("binary_sensor"===e){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"===e){const e=ms[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const n=fs(t);return n?"closed"===s?n[0]:n[1]:t}return"lock"===e?"locked"===s?"mdi:lock":"mdi:lock-open-variant":"light"===e&&"mdi:lightbulb"===t&&"on"===s?"mdi:lightbulb-on":t}function ys(t){const e=Math.min(4e4,Math.max(1e3,t))/100,i=e<=66?255:329.698727446*Math.pow(e-60,-.1332047592),s=e<=66?99.4708025861*Math.log(e)-161.1195681661:288.1221695283*Math.pow(e-60,-.0755148492),n=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,o=t=>Math.round(Math.min(255,Math.max(0,t)));return[o(i),o(s),o(n)]}const bs=1/2.2;function ws(t){const e=/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/.exec(t);return e?"#"+e.slice(1).map(t=>Math.min(255,Number(t)).toString(16).padStart(2,"0")).join(""):null}function ks(t){if(!t||"object"!=typeof t||Array.isArray(t))return null;const e=t;if(Object.keys(e).some(t=>"c"!==t&&"bri"!==t))return null;const i=Rt(e.c,null);return i?void 0===e.bri||null===e.bri?{c:i}:"number"!=typeof e.bri||!Number.isFinite(e.bri)||e.bri<.01||e.bri>1?null:{c:i,bri:e.bri}:null}function xs(t,e,i){const s=ks(e),n=t?.attributes||{},o=s?.bri??function(t){const e=t?.attributes?.brightness,i="number"==typeof e?e:"string"==typeof e&&""!==e.trim()?Number(e):Number.NaN;return Number.isFinite(i)?Math.max(0,Math.min(1,i/255)):1}(t);if(s)return{c:s.c,bri:o};const a=zt(n.rgb_color);if(a)return{c:ws(a)||Rt(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=ys(r),"#"+l.slice(0,3).map(t=>Math.min(255,Math.max(0,Math.round(Number(t)||0))).toString(16).padStart(2,"0")).join("")),bri:o}:{c:Rt(i,"#ffd9a0"),bri:o};var l}function $s(t,e,i){return"on"===t?.state?xs(t,e,i):null}function Ss(t,e=1){const i=Math.max(0,Math.min(1,Number.isFinite(t)?t:1)),s=.7*Math.max(0,Math.min(1,Number.isFinite(e)?e:1))*(.4+.6*Math.pow(i,bs));return Math.max(0,Math.min(1,s))}function Ms(t){return t.startsWith("light.")||t.startsWith("switch.")}function Cs(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;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=Math.hypot(r,l);if(c<i)continue;const h=r/c,d=l/c;for(let t=0;t<e.length;t++){const n=e[t],a=e[(t+1)%e.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(t,e,i=1e-6){const s=[];for(const n of t){const t=[n[0],n[1]],o=[n[2],n[3]],a=o[0]-t[0],r=o[1]-t[1],l=Math.hypot(a,r);if(l<i)continue;const c=a/l,h=r/l,d=[];for(const s of e){const e=Math.abs((s[0]-t[0])*h-(s[1]-t[1])*c),n=Math.abs((s[2]-t[0])*h-(s[3]-t[1])*c),o=Math.max(i,1e-6*l);if(e>o||n>o)continue;const a=(s[0]-t[0])*c+(s[1]-t[1])*h,r=(s[2]-t[0])*c+(s[3]-t[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([t[0],t[1],o[0],o[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,n]of d)e-p>i&&s.push([t[0]+c*p,t[1]+h*p,t[0]+c*e,t[1]+h*e]),p=Math.max(p,n);l-p>i&&s.push([t[0]+c*p,t[1]+h*p,o[0],o[1]])}return s}function Ps(t,e,i=1e-6){const s=[];for(let e=0;e<t.length;e++){const i=t[e],n=t[(e+1)%t.length];s.push([i[0],i[1],n[0],n[1]])}return Ds(s,e,i)}const Ts=864e5,Rs=576e5;function Is(t){const e=new Set,i=t=>{if("string"!=typeof t||!t)return;const i=As(t);i.startsWith("/api/houseplan/content/")&&e.add(i)};for(const e of t?.spaces||[]){i(e?.plan_url);for(const t of e?.markers||[])for(const e of t?.pdfs||[])i(e?.url)}for(const e of t?.markers||[])for(const t of e?.pdfs||[])i(t?.url);return e}function As(t){return t?t.startsWith("/houseplan_files/plans/")?"/api/houseplan/content/plans/_/"+t.slice(23):t.startsWith("/houseplan_files/files/")?"/api/houseplan/content/files/"+t.slice(23):t:""}function zs(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i||"custom"===i?i:t}function Fs(t,e){const i=e?.settings;return"boolean"==typeof i?.glow?i.glow:"glow"===i?.fill_mode||t}function Es(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Ns(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Os(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Hs(t,e){const i=e[2]-e[0],s=e[3]-e[1],n=i*i+s*s;if(!n)return Math.hypot(t[0]-e[0],t[1]-e[1]);let o=((t[0]-e[0])*i+(t[1]-e[1])*s)/n;return o=Math.max(0,Math.min(1,o)),Math.hypot(t[0]-(e[0]+o*i),t[1]-(e[1]+o*s))}const Ls=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);function Bs(t,e){return"alarm_control_panel"===t||"siren"===t||"binary_sensor"===t&&!!e&&Ls.has(e)}function qs(t){if(!t)return null;const e=t.indexOf("#");if(e<=0)return null;const i=t.slice(0,e),s=t.slice(e+1);if(!s)return null;if(s.startsWith("@")){const t=s.slice(1);return t?{space:i,area:null,roomId:t}:null}return{space:i,area:s,roomId:null}}const Ws={availability:"available",status:"alarm",activity:"none"},js=new Set(["motion","vibration","sound"]),Gs=new Set(["occupancy","presence"]),Us=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=t=>""===t||"unknown"===t||"unavailable"===t||"__missing__"===t,tn=t=>String(t??"").trim().toLowerCase(),en=t=>tn(t).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(t,e){const i=t?.entities?.[e]||{};if("config"===i.entity_category)return null;const s=String(e||"").split(".").slice(1).join("."),n=[i.translation_key,i.original_name,i.name],o=[...n,s];n.some(t=>tn(t))||o.push(t?.states?.[e]?.attributes?.friendly_name);const a=o.map(en).filter(Boolean);if(a.some(t=>t.split("_").some(t=>nn.has(t))))return null;let r=null;for(const t of a)for(const[e,i]of sn)(t===e||t.endsWith(`_${e}`))&&(r=null==r?i:Math.min(r,i));return r}const an=(t,e)=>null!=on(t,e);function rn(t,e){if(!e.startsWith("switch."))return!1;const i=t?.entities?.[e]||{},s=t?.states?.[e],n=e.slice(7).toLowerCase();if(/(?:^|_)(?:main_)?power$/.test(n))return!0;if([i.translation_key,i.original_name,i.name].map(tn).some(t=>["power","main power","power switch","питание"].includes(t)))return!0;const o=tn(s?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(o)||/(?:^|[\s._-])питание$/.test(o)}function ln(t){for(const e of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=tn(t?.[e]);if(Ys.has(i)||Xs.has(i))return i}return""}function cn(t,e){const i=t?.states?.[e],s=i?tn(i.state):"__missing__",n=String(e||"").split(".")[0],o=tn(i?.attributes?.device_class),a={eid:e,state:s,availability:Qs(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===a.availability)return a;if(function(t,e,i){return"alarm_control_panel"===t?"triggered"===i:"on"===i&&Bs(t,e)}(n,o,s))return{...a,status:"alarm"};if("binary_sensor"===n)return js.has(o)?{...a,edge:"rising"}:Gs.has(o)?{...a,activity:"on"===s?"presence":"none"}:Us.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 t=ln(i.attributes),e=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(tn):[],n=!Xs.has(s)&&(Zs.has(s)||Ys.has(s)||e.includes(s));return(t?Ys.has(t):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(t,e,i){const s=i.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category),n=e.find(e=>rn(t,e));if(!(s.length>1&&!!n))return e.map(e=>cn(t,e));const o=e.find(e=>e!==n&&an(t,e)),a=cn(t,n);return"unavailable"===a.availability||"off"===a.state?e.map(e=>({eid:e,state:t?.states?.[e]?tn(t.states[e].state):"__missing__",availability:"unavailable",status:"neutral",activity:"none",edge:"none"})):e.map(e=>{if(e===o)return function(t,e){const i=t?.states?.[e],s=i?tn(i.state):"__missing__",n={eid:e,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}(t,e);return{...e===n?a:cn(t,e),status:"neutral",activity:"none",edge:"none"}})}function dn(t){if(!t.length)return{availability:"available",status:"neutral",activity:"none"};const e=t.filter(t=>"available"===t.availability);if(!e.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(e.some(t=>"alarm"===t.status))return Ws;const i=e.some(t=>"working"===t.status)?"working":e.some(t=>"open"===t.status)?"open":"neutral",s=e.some(t=>"transition"===t.activity)?"transition":e.some(t=>"presence"===t.activity)?"presence":e.some(t=>"running"===t.activity)?"running":"none";return{availability:"available",status:i,activity:s}}function pn(t,e){if(!(t=>!!t&&!Qs(t))(t)||"unavailable"===e.availability||t===e.state)return null;if("rising"===e.edge)return"off"===t&&"on"===e.state?"event":null;if("change"===e.edge)return"event";if("terminal_transition"===e.edge){const i=new Set([t,e.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}const un=(t,e=0)=>{const i=Number(t);return Number.isInteger(i)&&i>=0?i:e},_n=(t,e)=>{const i={rev:0,configRev:un(e),off:new Set};if(null==t)return{snapshot:i,valid:!1};const s=t.off;return!Number.isInteger(t.rev)||Number(t.rev)<0||!Number.isInteger(t.config_rev)||Number(t.config_rev)<0||!Array.isArray(s)||s.some(t=>"string"!=typeof t||!t)?{snapshot:i,valid:!1}:{snapshot:{rev:Number(t.rev),configRev:Number(t.config_rev),off:new Set(s)},valid:!0}};function mn(t,e=0){return _n(t,e).snapshot}function gn(t,e,i,s){const n=_n(e,i);return s&&n.valid&&n.snapshot.configRev===i?t.configRev===i&&n.snapshot.rev<t.rev?t:n.snapshot:mn(null,i)}function fn(t){return{rev:t.rev,config_rev:t.configRev,off:[...t.off].sort()}}function vn(t){return t?`${t.configRev}:${t.rev}:${[...t.off].sort().join(",")}`:"0:0:"}function yn(t){return!!t&&"string"==typeof t.id&&!!t.id&&"virtual"===t.binding&&!0===t.is_light&&"toggle"===t.tap_action&&!0!==t.removed}function bn(t,e){return!yn(t)||!e?.off.has(t.id)}function wn(t,e){const i="string"==typeof e?.marker_id?e.marker_id:"",s=un(e?.rev,-1);if(!i||"boolean"!=typeof e?.on||s<=t.rev)return t;const n=new Set(t.off);return e.on?n.delete(i):n.add(i),{...t,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(t){const e=function(t){const e=t?.connection||t;return!e||"object"!=typeof e&&"function"!=typeof e?null:e}(t);if(!e)return null;let i=kn.get(e);return i||(i={revision:0,authoritative:!1,access:"pending",devices:{},entities:{},lastSuccess:0,listeners:new Set,refs:0},kn.set(e,i)),i}function Dn(t,e){const i=Array.isArray(t)?t:t&&"object"==typeof t&&Array.isArray(t.entries)?t.entries:null;if(!i)return null;const s={};for(const t of i){const i=t?.[e];"string"==typeof i&&i&&(s[i]=t)}return s}async function Pn(t,e){return e.loading||!t?.callWS||(e.loading=(async()=>{try{const[i,s]=await Promise.all([t.callWS({type:"config/device_registry/list"}),t.callWS({type:"config/entity_registry/list"})]),n=Dn(i,"id"),o=Dn(s,"entity_id");if(!n||!o)throw new Error("invalid_registry_response");e.devices=n,e.entities=o,e.authoritative=!0,e.access="full",e.lastSuccess=Date.now(),e.error=void 0}catch(t){e.authoritative=!1,e.access="limited",e.error=function(t){if(t&&"object"==typeof t){const e=t;return String(e.message||e.code||e.error||"registry_unavailable")}return String(t||"registry_unavailable")}(t)}finally{e.revision++,e.loading=void 0,function(t){for(const e of[...t.listeners])try{e()}catch{}}(e)}})()),e.loading}function Tn(t,e){void 0===e.reloadTimer&&(e.reloadTimer=globalThis.setTimeout(()=>{e.reloadTimer=void 0,Pn(t,e)},80))}async function Rn(t,e){if(e.subscribing||e.unsubDevice&&e.unsubEntity)return e.subscribing;const i=t?.connection?.subscribeEvents;return"function"==typeof i?(e.subscribing=(async()=>{try{e.unsubDevice||(e.unsubDevice=await i.call(t.connection,()=>Tn(t,e),"device_registry_updated")),e.unsubEntity||(e.unsubEntity=await i.call(t.connection,()=>Tn(t,e),"entity_registry_updated"))}catch{}finally{0===e.refs&&(e.unsubDevice?.(),e.unsubEntity?.(),e.unsubDevice=void 0,e.unsubEntity=void 0),e.subscribing=void 0}})(),e.subscribing):void 0}function In(t,e){const i=Cn(t);if(!i)return()=>{};const s=0===i.refs;i.refs++,i.listeners.add(e),("pending"===i.access||s)&&Pn(t,i),Rn(t,i);let n=!1;return()=>{n||(n=!0,i.listeners.delete(e),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(t){const e=Cn(t);if(!e)return{revision:0,authoritative:!1,access:"limited",devices:t?.devices||{},entities:t?.entities||{},lastSuccess:0,error:"registry_unavailable"};let i=t?.devices||{},s=t?.entities||{};if(e.authoritative){const n=e.liveDevices!==i||e.liveEntities!==s,o=void 0!==e.liveDevices||void 0!==e.liveEntities;if(n&&(e.liveDevices=i,e.liveEntities=s,e.projectedRevision=void 0,o&&Tn(t,e)),e.projectedRevision!==e.revision||!e.projectedDevices||!e.projectedEntities){const t={...e.devices},n={...e.entities};for(const[e,s]of Object.entries(i))Object.prototype.hasOwnProperty.call(t,e)||(t[e]=s);for(const[t,e]of Object.entries(s))Object.prototype.hasOwnProperty.call(n,t)||(n[t]=e);e.projectedDevices=t,e.projectedEntities=n,e.projectedRevision=e.revision}i=e.projectedDevices,s=e.projectedEntities}return{revision:e.revision,authoritative:e.authoritative,access:e.access,devices:i,entities:s,lastSuccess:e.lastSuccess,error:e.error}}function zn(t){if(!t||"object"!=typeof t&&"function"!=typeof t)return 0;let e=xn.get(t);return e||(e=$n++,xn.set(t,e)),e}function Fn(t,e=An(t)){return[e.revision,e.access,zn(e.devices),zn(e.entities)].join(":")}function En(t){return!!t&&null==t.disabled_by}function Nn(t,e=An(t)){const i={},s={},n={};for(const[t,s]of Object.entries(e.devices||{}))En(s)&&(i[t]=s);for(const[t,i]of Object.entries(e.entities||{})){if(!En(i))continue;const n=i.device_id?e.devices?.[i.device_id]:null;e.authoritative&&i.device_id&&!n||(n&&!En(n)||(s[t]=i))}for(const[i,s]of Object.entries(t?.states||{})){const t=e.entities?.[i];if(t&&!En(t))continue;const o=t?.device_id?e.devices?.[t.device_id]:null;e.authoritative&&t?.device_id&&!o||(o&&!En(o)||(n[i]=s))}return{...t,devices:i,entities:s,states:n}}function On(t,e=An(t)){return{...t,devices:e.devices||{},entities:e.entities||{}}}function Hn(t,e){const i=[];for(const[s,n]of Object.entries(t||{}))n?.device_id===e&&i.push(s);return i}function Ln(){if(Mn)return Mn;if("undefined"==typeof localStorage)return{};try{const t=JSON.parse(localStorage.getItem(Sn)||"{}");if(!t||"object"!=typeof t||Array.isArray(t))return{};const e=Date.now(),i={};for(const[s,n]of Object.entries(t))!n||"active"!==n.kind&&"ha_disabled"!==n.kind||!Number.isFinite(n.ts)||e-n.ts>7776e6||(i[s]=n);return Mn=i,Mn}catch{return{}}}function Bn(t,e,i=An(t)){if(!e||"virtual"===e)return{kind:"active",enabledEntityIds:[],allEntityIds:[]};const s=e.indexOf(":");if(s<1)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const n=e.slice(0,s),o=e.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 t=a[o];if(!t)return{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[]};const e=Hn(r,o);if(!En(t))return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:e};const i=e.filter(t=>En(r[t]));return e.length&&!i.length?{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:e}:{kind:"active",enabledEntityIds:i,allEntityIds:e}}const e=r[o];return e&&!En(e)?{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]}:e?.device_id&&!a[e.device_id]?{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[o]}:e?.device_id&&!En(a[e.device_id])?{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]}:e||t?.states?.[o]?{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}:{kind:"orphaned",reason:"entity_missing",enabledEntityIds:[],allEntityIds:[]}}const l=function(t){return Ln()[t]||null}(e);if("device"===n){const e=a[o],i=Hn(r,o);if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:i};const s=i.filter(t=>{const e=r[t];return null==e?.disabled_by&&(!e.device_id||null==a[e.device_id]?.disabled_by)});if(i.length&&!s.length&&i.every(t=>null!=r[t]?.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(e||s.some(e=>!!t?.states?.[e]))return{kind:"active",enabledEntityIds:s,allEntityIds:i}}else{const e=r[o];if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]};if(e?.device_id&&null!=a[e.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(e||t?.states?.[o])return{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}}return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]}}function qn(t){const e={};for(const[i,s]of Object.entries(t.entities)){if(!s?.device_id||!En(s))continue;const n=t.devices?.[s.device_id];n&&!En(n)||(e[s.device_id]=e[s.device_id]||[]).push(i)}return e}function Wn(t,e){const i=new Map;for(const s of t){if(!0===s?.removed)continue;const t=s?.binding||"";if(!t.startsWith("entity:"))continue;const n=t.slice(7);if(!n)continue;const o=e?.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 jn(t,e,i,s){const n=s.byDevice.get(e);return n?.size?{partial:!0,entityIds:i.filter(e=>!n.has(e)&&!t?.entities?.[e]?.hidden)}:{partial:!1,entityIds:[...i]}}function Gn(t,e,i){if(e.identifiers?.[0]?.[0])return e.identifiers[0][0];for(const e of i){const i=t.entities[e]?.platform;if(i)return i}return""}function Un(t,e){if(/_device_temperature$/.test(e))return!1;if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_temperature$/.test(e);const s=i.attributes||{};return"temperature"===s.device_class||/°C|°F/.test(s.unit_of_measurement||"")||/_temperature$/.test(e)}const Vn=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],Kn=t=>[...t.filter(t=>!t.reg?.hidden),...t.filter(t=>!!t.reg?.hidden)];function Yn(t,e){const i=e.map(e=>({eid:e,reg:t?.entities?.[e]})).filter(t=>!!t.reg);if(!i.length)return[];const s=i.filter(t=>!t.reg.entity_category),n=s.length?s:i;for(const t of Vn){const e=n.filter(e=>e.eid.startsWith(t+"."));if(e.length)return Kn(e).map(t=>t.eid)}const o=n.filter(e=>function(t,e){if(!e.startsWith("binary_sensor."))return!1;const i=tn(t?.states?.[e]?.attributes?.device_class||t?.entities?.[e]?.device_class||t?.entities?.[e]?.original_device_class);return js.has(i)||Gs.has(i)||Us.has(i)||Vs.has(i)||"moving"===i||Ks.has(i)}(t,e.eid));if(o.length)return Kn(o).map(t=>t.eid);const a=n.filter(t=>t.eid.startsWith("switch."));if(a.length){const e=Kn(a),s=e.find(e=>rn(t,e.eid));if(a.length>1&&s){const e=i.filter(e=>e.eid!==s.eid&&"config"!==e.reg?.entity_category&&null!=on(t,e.eid)),n=e.filter(t=>!t.reg?.entity_category),o=n.length?n:e;for(const e of[0,1,2]){const i=Kn(o.filter(i=>on(t,i.eid)===e));if(i.length)return[i[0].eid,s.eid]}}return[(s||e[0]).eid]}const r=[];for(const t of ie)r.push(...Kn(n.filter(e=>e.eid.startsWith(t+"."))));return r.push(...Kn(n.filter(t=>!ie.includes(t.eid.split(".")[0])))),r.map(t=>t.eid)}function Jn(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),n=[s.filter(t=>!t.reg.hidden&&!t.reg.entity_category),s.filter(t=>!t.reg.entity_category),s.filter(t=>!t.reg.hidden),s];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const e of n){const i=e.find(e=>Un(t,e.eid));if(i)return i.eid}const o=Yn(t,e);return o.find(Ms)||o[0]}function Xn(t,e,i=[]){return[...new Set(Zn(t,e,i))].filter(t=>Ms(t))}function Zn(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).filter(t=>"string"==typeof t&&!s.has(t))}function Qn(t,e){if("string"==typeof e)return t.area===e;const i=t.marker?.room_id;return i?!!e.id&&i===e.id:!!e.area&&t.area===e.area}function to(t){const e=t.marker?.binding?.startsWith("entity:")?t.marker.binding.slice(7):null,i=t.entities.filter(Ms),s=t.primary&&Ms(t.primary)?t.primary:null;return[...new Set([e,s,...i].filter(t=>!!t&&Ms(t)))]}function eo(t){const e=to(t),i=t.marker?.light_entity;return i&&e.includes(i)?i:e[0]||null}function io(t,e){if(!1===e.marker?.is_light)return[];if(!0===e.marker?.is_light){return[{eid:eo(e),via:"forced"}]}const i=e.primary||Yn(t,e.entities)[0];return i&&!i.startsWith("light.")?[]:e.entities.filter(e=>e.startsWith("light.")&&!!t.states?.[e]).map(t=>({eid:t,via:"light"}))}const so=new WeakMap,no=new WeakMap;function oo(t){return t.map(t=>[t.id||"",!0===t.hidden?1:0,t.primary||"",[...t.entities].join(","),void 0===t.controls?"<runtime-undefined>":[...t.controls].join(","),t.marker?.id||"",t.marker?.binding||"",t.marker?.is_light,t.marker?.tap_action||"",!0===t.marker?.removed?1:0,t.marker?.light_entity||"",null==t.marker?.controls?"<persisted-null>":[...t.marker.controls].join(",")].join("")).join("")}function ao(t){const e=oo(t),i=so.get(t);if(i?.fingerprint===e)return i;const s=t.filter(t=>!t.hidden),n=new Map,o=new Map;for(const e of t){o.set(e,Zn(e.marker?.binding,e.marker?.controls??e.controls,e.entities));const t=e.hidden||!0!==e.marker?.is_light?null:e.marker?.id||e.id;t&&n.set(String(t),e)}const a=new Map;for(const e of t){const t=o.get(e)||[],i=new Set(void 0===e.controls?t.filter(Ms):e.controls.filter(Ms)),s=eo(e),r=i.size?[...i]:s?[s]:[];for(const i of t){if(!i.startsWith("marker:"))continue;const t=i.slice(7);if(!t||t===String(e.marker?.id||e.id||""))continue;if(!n.has(t))continue;const s=a.get(t)||{markerId:t,controllers:[],driverEids:[]};s.controllers.push({device:e,driverEids:[...r]}),s.driverEids=[...new Set([...s.driverEids,...r])],a.set(t,s)}}const r={fingerprint:e,visible:s,markerById:n,persistedByDevice:o,incomingByMarker:a};return so.set(t,r),r}function ro(t){return ao(t).incomingByMarker}function lo(t,e){const i=Xn(e.marker?.binding,e.controls??e.marker?.controls,e.entities),s=Zn(e.marker?.binding,e.marker?.controls??e.controls,e.entities);return(null!=e.marker?.is_light||!i.length&&!s.some(t=>t.startsWith("marker:")))&&io(t,e).length>0}function co(t,e){const i=Xn(e.marker?.binding,e.controls??e.marker?.controls,e.entities).map(t=>({eid:t,via:"controls"})),s=lo(t,e)?io(t,e):[];for(const t of s)t.eid&&i.some(e=>e.eid===t.eid)||i.push(t);return i}function ho(t,e,i,s){if(null!=i)return ho(t,e,null,s).filter(t=>Qn(t.device,i));const n=oo(e),o=function(t,e){const i=new Set;for(const t of e){for(const e of t.entities)i.add(e);for(const e of t.controls||t.marker?.controls||[])"string"!=typeof e||e.startsWith("marker:")||i.add(e)}return[...i].sort().map(e=>`${e}:${t?.states?.[e]?.state??"<missing>"}`).join("|")}(t,e),a=vn(s),r=no.get(e);if(r?.graphFingerprint===n&&r.stateFingerprint===o&&r.virtualLightFingerprint===a&&r.registry===t?.entities)return r.sources;const{visible:l,markerById:c,persistedByDevice:h,incomingByMarker:d}=ao(e),p=null==i?l:l.filter(t=>Qn(t,i)),u=new Map,_=new Map;for(const e of p){const i=co(t,e).filter(t=>"controls"!==t.via);for(const s of i){const i=!0===e.marker?.is_light?String(e.marker?.id||e.id||""):"",n=i?`marker:${i}`:`entity:${s.eid}`,o=s.eid?[s.eid]:[],a={key:n,eid:s.eid||"",stateEids:o,serviceEids:o,device:e,via:s.via,castsGlow:!0,passive:!s.eid,on:!s.eid||"on"===t.states?.[s.eid]?.state};u.set(e,[...u.get(e)||[],a]),s.eid&&_.set(s.eid,a)}}const m=new Map;for(const t of e){const e=h.get(t)||[],i=new Set(void 0===t.controls?e.filter(Ms):t.controls);m.set(t,i)}for(const e of u.values())for(const i of e){if(!i.passive)continue;const e=i.key.slice(7),n=d.get(e);i.on=!n||n.driverEids.some(e=>"on"===t.states?.[e]?.state),!n&&yn(i.device.marker)&&(i.on=bn(i.device.marker,s))}const g=new Map,f=t=>{const e=g.get(t.key);(!e||t.castsGlow&&!e.castsGlow)&&g.set(t.key,t)};for(const e of l){const s=h.get(e)||[],n=null==i||Qn(e,i);for(const i of s){if(i.startsWith("marker:")){const t=c.get(i.slice(7)),e=t?u.get(t):null;if(e)for(const t of e)f(t);continue}if(!Ms(i)||!m.get(e)?.has(i))continue;const s=_.get(i);s?f(s):n&&f({key:`entity:${i}`,eid:i,stateEids:[i],serviceEids:[i],device:e,via:"controls",castsGlow:!1,passive:!1,on:"on"===t.states?.[i]?.state})}for(const t of u.get(e)||[])f(t)}const v=[...g.values()];return no.set(e,{graphFingerprint:n,stateFingerprint:o,virtualLightFingerprint:a,registry:t?.entities,sources:v}),v}function po(t){const e=t.filter(t=>t.castsGlow);return e.find(t=>t.on)||e[0]||null}function uo(t){return t.length?t.some(t=>t.on)?"on":"off":"none"}function _o(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const n=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===n){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const o=e.attributes?.linkquality??e.attributes?.lqi;if(null!=o){const t=parseFloat(o);isNaN(t)||i.push(t)}}return Ei(i)}function mo(t,e){for(const i of e){if(!Un(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(10*s)/10}return null}function go(t,e){for(const i of e){if(!i.startsWith("climate."))continue;const e=t.states[i];if(!e||"unavailable"===e.state||"unknown"===e.state)continue;const s=parseFloat(e.attributes?.current_temperature);if(Number.isFinite(s))return Math.round(10*s)/10}return null}function fo(t,e){if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_humidity$/.test(e);const s=i.attributes||{};return"humidity"===s.device_class||"%"===s.unit_of_measurement&&/_humidity$/.test(e)||/_humidity$/.test(e)}function vo(t,e){for(const i of e){if(!fo(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(s)}return null}function yo(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden||!En(s))continue;let n=null;if("group"===s.platform)n=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if(!En(e))continue;if("Group"!==e?.model)continue;n=e.area_id||s.area_id||null}}if(!n)continue;const o=t.states[e];i.push({eid:e,name:s.name||o?.attributes?.friendly_name||e,area:n})}return i}function bo(t,e,i,s,n){const o=ee(e,i,n);if(o!==te)return o;const a=[];for(const e of s){const i=t.states[e]?.attributes?.device_class;i&&a.push(i)}return function(t){for(const e of t){const t=Qt[e];if(t)return t}return null}(a)??te}function wo(t){const e=new Set,i=new Set,s=new Set;for(const n of t||[]){const t=String(n.binding||"").indexOf(":");if(t<1)continue;const o=n.binding.slice(0,t),a=n.binding.slice(t+1);a&&(!0===n?.removed?"device"===o?e.add(a):"entity"===o&&i.add(a):"entity"===o&&s.add(a))}return{devices:e,entities:i,liveEntities:s}}function ko(t,e,i){if(i.liveEntities.has(e))return!1;if(i.entities.has(e))return!0;const s=t?.entities?.[e]?.device_id;return!!s&&i.devices.has(s)}function xo(t,e,i,s,n){const o=Xn(e.binding,e.controls,t.entities).filter(t=>!ko(i,t,s)).filter(t=>"active"===Bn(i,"entity:"+t,n).kind);t.marker=e,t.controls=o,t.userHidden=!0===e.hidden,t.hidden=t.userHidden||"ha_disabled"===t.bindingStatus?.kind,e.name&&(t.name=e.name),e.icon&&(t.icon=e.icon),null!=e.model&&(t.model=e.model),t.link=e.link??null,t.description=e.description??null,t.pdfs=e.pdfs||[],t.tapAction=e.tap_action??null}function $o(t,e,i,s){if("string"==typeof t.room_id&&t.room_id.length>0&&null===t.area)return{area:"",space:t.space||s};const n=t.area||e||"";return{area:n,space:n&&i[n]||t.space||s}}function So(t){const e=t.hass,i=t.registry||An(e),s=Nn(e,i),n=On(e,i),{areaToSpace:o,markers:a,settings:r,excluded:l,showAll:c,firstSpaceId:h,loc:d,iconRules:p}=t,u=!1!==r.group_lights,_=wo(a),m=yo(s,u).filter(t=>!ko(s,t.eid,_)),g=new Set(m.map(t=>t.area)),f=qn(s),v=function(t){const e={};for(const[i,s]of Object.entries(t.entities||{}))s?.device_id&&(e[s.device_id]=e[s.device_id]||[]).push(i);return e}(n),y=Wn(a,n),b=new Set;for(const t of a){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||b.add(t.binding)}const w=(t,e)=>a.find(i=>i.binding===t+":"+e),k={},x=[];for(const t of Object.values(s.devices)){const n=t.area_id;if(!n||!o[n])continue;if("service"===t.entry_type)continue;if(b.has("device:"+t.id))continue;const a=Bn(e,"device:"+t.id,i);if("active"!==a.kind)continue;const h=w("device",t.id);if(h&&h.hidden&&!r.filter_seeded)continue;const _=jn(s,t.id,f[t.id]||[],y);if(_.partial&&!_.entityIds.length)continue;const m=_.entityIds,v=_.partial?{kind:"active",enabledEntityIds:m,allEntityIds:m}:a,$=Gn(s,t,m),S=!r.filter_seeded;if(S&&!c){if(l.has($))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===$&&t.via_device_id)continue}const M=(t.name_by_user||t.name||d("device.unnamed")).trim(),C=M+"|"+n;let D=bo(s,M,t.model,m,p);if(m.some(t=>t.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:t.id,name:P,model:t.model||"",area:n,space:o[n],icon:D,entities:m,allEntities:v.allEntityIds,bindingStatus:v,bindingKind:"device",bindingRef:t.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 t of m)o[t.area]&&(b.has("entity:"+t.eid)||x.push({id:"lg_"+t.eid,name:t.name,model:d("device.light_group"),area:t.area,space:o[t.area],icon:"mdi:lightbulb-group",entities:[t.eid],allEntities:[t.eid],bindingStatus:{kind:"active",enabledEntityIds:[t.eid],allEntityIds:[t.eid]},primary:t.eid,bindingKind:"entity",bindingRef:t.eid,pdfs:[]}));for(const t of a){if(t.removed)continue;const[a,l]=t.binding.split(":"),c="device"===a||"entity"===a?Bn(e,t.binding,i):null;if(!t.hidden||r.filter_seeded||"ha_disabled"===c?.kind)if("device"===a){const e=c;if("unverified"===e.kind)continue;const a=n.devices[l],{area:r,space:u}=$o(t,a?.area_id,o,h),m="active"===e.kind?e.enabledEntityIds:[];let g=a?bo(s,a.name_by_user||a.name||"",a.model,m,p):"mdi:help-circle";m.some(t=>t.startsWith("lock."))&&(g="mdi:lock");const f={id:t.id,name:a?.name_by_user||a?.name||d("device.fallback"),model:a?.model||"",area:r,space:u,icon:g,entities:m,allEntities:e.allEntityIds.length?e.allEntityIds:a&&v[a.id]||[],bindingStatus:e,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,t,n,_,i),x.push(f)}else if("entity"===a){if(ko(n,l,_))continue;const e=c;if("unverified"===e.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(t,r,o,h),m=s.states[l],g=a?.name||m?.attributes?.friendly_name||l;let f=bo(s,g,"",[l],p);l.startsWith("lock.")&&(f="mdi:lock");const v={id:t.id,name:g,model:"",area:d,space:u,icon:f,entities:"active"===e.kind?[l]:[],allEntities:[l],bindingStatus:e,primary:"active"===e.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,t,n,_,i),x.push(v)}else{const e=t.area||"",s=t.space||e&&o[e]||h,a={id:t.id,name:t.name||d("device.virtual"),model:t.model||"",area:e,space:s,icon:t.icon||"mdi:map-marker",entities:[],allEntities:[],bindingStatus:{kind:"active",enabledEntityIds:[],allEntityIds:[]},bindingKind:"virtual",virtual:!0};xo(a,t,n,_,i),x.push(a)}}return x}function Mo(t,e,i,s){if(!e)return null;if(function(t,e,i){if(!e)return!1;const s=e.indexOf(":");if(s<1)return!1;const n=e.slice(0,s),o=e.slice(s+1),a=wo(i);return"device"===n?a.devices.has(o):"entity"===n&&ko(t,o,a)}(t,e,s))return null;const n=e.indexOf(":");if(n<0)return null;const o=e.slice(0,n),a=e.slice(n+1);if(!a)return null;if("entity"===o){const e=parseFloat(t.states[a]?.state);return Number.isFinite(e)?"temp"===i?Math.round(10*e)/10:Math.round(e):null}if("device"===o){const e=Object.entries(t.entities).filter(([,t])=>t.device_id===a).map(([t])=>t);return"temp"===i?mo(t,e):vo(t,e)}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(t,e,i){const s=[];for(const n of e){if(n.area!==i||n.virtual)continue;const e=_o(t,n.entities);null!=e&&s.push(e)}return Ei(s)}function Po(t,e,i){const s=[];for(const n of e){if(n.area!==i)continue;if("mdi:thermometer"!==n.icon&&"mdi:air-filter"!==n.icon)continue;const e=mo(t,n.entities);null!=e&&s.push(e)}return s.length?Math.round(s.reduce((t,e)=>t+e,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 Ro(t){return t?t.sourceLabel?`${t.sourceLabel}: ${t.fullText}`:t.fullText:""}const Io=new WeakMap;function Ao(t){return t?"entity_state"===t.kind?`state:${t.entity_id}`:"entity_attribute"===t.kind?`attr:${t.entity_id}:${t.attribute}`:"derived_marker_state"===t.kind?`marker:${t.ref}`:"derived:lqi":""}function zo(t){if("derived:lqi"===t)return{kind:"derived_lqi"};if(t.startsWith("state:"))return{kind:"entity_state",entity_id:t.slice(6)};if(t.startsWith("attr:")){const e=t.slice(5),i=e.lastIndexOf(":");if(i>0)return{kind:"entity_attribute",entity_id:e.slice(0,i),attribute:e.slice(i+1)}}return t.startsWith("marker:marker:")?{kind:"derived_marker_state",ref:t.slice(7)}:null}function Fo(t,e){const i=t?.entities?.[e],s=t?.states?.[e];return String(i?.name||i?.original_name||s?.attributes?.friendly_name||e)}function Eo(t){return t.replaceAll("_"," ")}function No(t,e=1){const i=Number(t);return Number.isFinite(i)?`${Math.round(i*e)} %`:null}function Oo(t){if("derived_lqi"===t.kind)return"lqi";if("entity_attribute"===t.kind){if(t.attribute.includes("temperature"))return"temperature";if(t.attribute.includes("humidity"))return"humidity"}return"default"}function Ho(t,e,i,s=[]){let n=null,o="",a="available";if("entity_state"===i.kind){o=Fo(t,i.entity_id);const e=t?.states?.[i.entity_id];if(e)if(Bo(t,i.entity_id))if(["unknown","unavailable"].includes(String(e.state).toLowerCase()))a="unavailable";else{const s=Xi(t,i.entity_id);n=s?Zi(s,String(e.attributes?.unit_of_measurement||"")):null,n||(a="unavailable")}else a="unavailable";else a="missing"}else if("entity_attribute"===i.kind){o=`${Eo(i.attribute)} · ${Fo(t,i.entity_id)}`;const e=t?.states?.[i.entity_id];e?Bo(t,i.entity_id)?(n=function(t,e,i){const s=t?.states?.[e];if(!s||!(i in(s.attributes||{})))return null;const n=Xi(t,e,i);if(n?.formatted)return n.text;const o=s.attributes?.[i];if("brightness"===i)return No(o,100/255);if("volume_level"===i)return No(o,100);if(["current_position","percentage","current_humidity","humidity","battery_level"].includes(i))return No(o);if("current_temperature"===i||"temperature"===i){const e=Number(o);if(!Number.isFinite(e))return null;const i=String(t?.config?.unit_system?.temperature||"°C");return`${Math.round(10*e)/10} ${i}`}return n?.text||(["string","number","boolean"].includes(typeof o)?String(o):null)}(t,i.entity_id,i.attribute),null==n&&(a="unavailable")):a="unavailable":a="missing"}else if("derived_lqi"===i.kind){o="LQI";const i=_o(t,e.entities);null==i?a="unavailable":n=String(i)}else{o="Light state";const e=s.find(t=>t.ref===i.ref);if(e){const i=e.on?"on":"off",s=t?.localize?.(`state.default.${i}`);n="string"==typeof s&&s?s:i,o=e.name||o}else a="missing"}const r=t?.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(t,e){if(!0===e.marker?.use_climate_temp){const i=e.entities.find(e=>e.startsWith("climate.")&&Number.isFinite(Number(t?.states?.[e]?.attributes?.current_temperature)));return i?{kind:"entity_attribute",entity_id:i,attribute:"current_temperature"}:null}if("mdi:thermometer"===e.icon||"mdi:air-filter"===e.icon){const i=e.entities.find(e=>Un(t,e)&&Number.isFinite(Number(t?.states?.[e]?.state)));if(i)return{kind:"entity_state",entity_id:i}}return e.primary&&fo(t,e.primary)&&Number.isFinite(Number(t?.states?.[e.primary]?.state))?{kind:"entity_state",entity_id:e.primary}:null}function Bo(t,e){const i=t?.entities?.[e];if(i&&!En(i))return!1;const s=i?.device_id?t?.devices?.[i.device_id]:null;return!s||En(s)}function qo(t,e){return Bo(t,e)&&!!t?.states?.[e]}function Wo(t,e,i=[e]){const s=function(t,e){const i=t=>[t.id,t.primary,t.hidden?1:0,t.userHidden?1:0,t.entities.join(","),(t.controls||[]).join(","),t.marker?.binding||"",!0===t.marker?.is_light?1:0,t.marker?.light_entity||"",(t.marker?.controls||[]).join(",")].join("|");return`${i(t)}\n${e.map(i).join("\n")}`}(e,i),n=Io.get(e);if(n&&n.states===(t?.states||null)&&n.entities===(t?.entities||null)&&n.devices===(t?.devices||null)&&n.signature===s)return n.result;const o=new Set(e.entities);for(const t of e.controls||[])t.startsWith("marker:")||o.add(t);const a=[],r=ho(t,i),l=r.filter(t=>t.key.startsWith("marker:")).map(t=>({ref:t.key,on:t.on,name:t.device?.name||t.key})),c=i=>{const s=Ao(i);if(a.some(t=>t.key===s))return;const n=Ho(t,e,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 e of[...o].sort()){if(!qo(t,e))continue;const i=e.split(".")[0],s=t?.entities?.[e];if("button"===i||"event"===i||"config"===s?.entity_category)continue;c({kind:"entity_state",entity_id:e});const n=t?.states?.[e];for(const t of To[i]||[])t in(n?.attributes||{})&&c({kind:"entity_attribute",entity_id:e,attribute:t})}const h=new Set((e.marker?.controls||e.controls||[]).filter(t=>t.startsWith("marker:")));!0===e.marker?.is_light&&e.marker.id&&h.add(`marker:${e.marker.id}`);const d=new Set(r.map(t=>t.key));for(const t of[...h].sort())d.has(t)&&c({kind:"derived_marker_state",ref:t});return e.virtual||null==_o(t,e.entities)||c({kind:"derived_lqi"}),Io.set(e,{states:t?.states||null,entities:t?.entities||null,devices:t?.devices||null,signature:s,result:a}),a}function jo(t,e,i){const s=Lo(t,e);if(s&&i.some(t=>t.key===Ao(s)))return s;const n=e.primary&&i.find(t=>t.key===`state:${e.primary}`);if(n)return n.source;const o=[t=>t.technical.includes("temperature"),t=>t.technical.includes("humidity"),t=>t.technical.includes("battery")];for(const t of o){const e=i.find(t);if(e)return e.source}return i.find(t=>"derived_lqi"!==t.source.kind)?.source||i[0]?.source||null}function Go(t){return"boolean"==typeof t?.themes?.darkMode?t.themes.darkMode?"theme-dark":"theme-light":""}function Uo(t){const e=[];if(1!==t.scale&&e.push(`--dev-scale:${t.scale}`),"none"!==t.pulse.kind){e.push(`--ripple-scale:${t.pulse.diameterScale}`);const n=(i=t.pulse.color,s=null,"string"==typeof i&&(Tt.test(i)||At.test(i))?i:s);n&&e.push(`--ripple-color:${n}`)}var i,s;return e}function Vo(t){const e=[...String(t)].reduce((t,e)=>/\s/.test(e)?t+.35:t+(e.codePointAt(0)>255?1:.62),0);return e<=8?.45:Math.max(.25,Math.round(3.6/e*1e3)/1e3)}function Ko(t,e){const i=t.pulse,s=i.generation%2==0,n=function(t){const e=t.valueBadge;if(!e||!1!==e.configured)return[];const i=[];return null!=t.tempText&&"temperature"!==e.tone&&i.push({kind:"temperature",text:t.tempText,suffix:"°"}),null!=t.humText&&"humidity"!==e.tone&&i.push({kind:"humidity",text:t.humText,suffix:"%"}),i}(t),o=t.valueBadge,a=o?.position||"right",r=!!o||n.length>0,l=["device-shell",null!=t.valueText?"text-shell":"",r?`with-values pos-${a}`:"",n.length?"with-legacy":""].filter(Boolean).join(" ");return W`
|
||
${"none"!==i.kind&&"dot"!==i.reducedMotionIndicator?W`<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>`:U}
|
||
${"dot"===i.reducedMotionIndicator?W`<span class="activity-dot" aria-hidden="true"></span>`:U}
|
||
${e.newDevice?W`<span class="newdot" title=${e.newDeviceTitle||""} aria-hidden="true"></span>`:U}
|
||
${t.haDisabled?W`<span class="habadge" title=${e.disabledTitle||""} aria-hidden="true"><ha-icon icon="mdi:power-plug-off-outline"></ha-icon></span>`:U}
|
||
<span class=${l} aria-hidden="true">
|
||
<span class="device-shell-frame"></span>
|
||
<span class="device-core">
|
||
${null!=t.valueText?W`<span class="valtext" title=${t.valueFullText||t.valueText}
|
||
style=${`--value-font-scale:${Vo(t.valueFullText||t.valueText)}`}
|
||
>${t.valueText}</span>`:W`<ha-icon icon=${t.icon}
|
||
style=${t.angle?`transform:rotate(${t.angle}deg)`:U}></ha-icon>`}
|
||
</span>
|
||
${r?W`<span class="device-sections">
|
||
${o?W`<span
|
||
class=${function(t){return`value-badge pos-${t.position} ${t.availability} tone-${t.tone}`}(o)}
|
||
title=${Ro(o)}
|
||
style=${`--value-font-scale:${Vo(o.fullText||o.text)}`}
|
||
>${o.text}</span>`:U}
|
||
${n.map(t=>W`<span
|
||
class="value-badge legacy-secondary available tone-${t.kind}"
|
||
title=${t.text+t.suffix}
|
||
style=${`--value-font-scale:${Vo(t.text+t.suffix)}`}
|
||
>${t.text}${t.suffix}</span>`)}
|
||
</span>`:U}
|
||
</span>
|
||
${null!=t.lqiText?W`<span class="${function(t){return"lqi"+("bottom"===t?.position?" below-value-badge":"")}(t.valueBadge)}${t.lqiBand?` band-${t.lqiBand}`:""}"
|
||
style=${t.lqiColor?`color:${t.lqiColor}`:U}>${t.lqiText}</span>`:U}
|
||
`}function Yo(t,e,i){return{kind:"none",reason:"none",generation:t,expiresAt:null,color:e,diameterScale:i,animated:!1,reducedMotionIndicator:"none"}}const Jo=new WeakMap;function Xo(t){const e=function(t){const e=t?.connection||t;return!e||"object"!=typeof e&&"function"!=typeof e?null:e}(t);if(!e)return null;let i=Jo.get(e);return i||(i={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},Jo.set(e,i)),i}function Zo(t){t.revision++;for(const e of[...t.listeners])try{e()}catch{}}function Qo(t,e){const i=Xo(t);if(!i)return()=>{};i.refs++,i.listeners.add(e),async function(t,e){e.loading||"function"!=typeof t?.callWS?e.loading:e.loaded&&e.loadedAt&&Date.now()-e.loadedAt<3e5||(e.loading=(async()=>{const[i,s]=await Promise.allSettled([t.callWS({type:"config_entries/get"}),t.callWS({type:"manifest/list"})]);"fulfilled"===i.status&&Array.isArray(i.value)&&(e.configEntries=Object.fromEntries(i.value.filter(t=>"string"==typeof t?.entry_id).map(t=>[t.entry_id,t]))),"fulfilled"===s.status&&Array.isArray(s.value)&&(e.manifests=Object.fromEntries(s.value.filter(t=>"string"==typeof t?.domain).map(t=>[t.domain,t]))),e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Zo(e)})().catch(()=>{e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Zo(e)}),e.loading)}(t,i),async function(t,e){if(e.unsubscribe||e.subscribing)return e.subscribing;const i=t?.connection?.subscribeMessage;"function"==typeof i&&(e.subscribing=(async()=>{try{e.unsubscribe=await i.call(t.connection,t=>{const i=Array.isArray(t)?t:Array.isArray(t?.entries)?t.entries:t?[t]:[];let s=!1;const n={...e.configEntries};for(const t of i){const e=t?.entry||t?.data?.entry||("string"==typeof t?.entry_id?t:null),i=e?.entry_id;if("string"!=typeof i)continue;const o=t?.type||t?.action||t?.data?.action;"removed"===o||"remove"===o?delete n[i]:n[i]=e,s=!0}s&&(e.configEntries=n,Zo(e))},{type:"config_entries/subscribe"})}catch{}finally{0===e.refs&&(e.unsubscribe?.(),e.unsubscribe=void 0),e.subscribing=void 0}})(),e.subscribing)}(t,i);let s=!1;return()=>{s||(s=!0,i.listeners.delete(e),i.refs=Math.max(0,i.refs-1),i.refs||(i.unsubscribe?.(),i.unsubscribe=void 0))}}function ta(t){const e=Xo(t);return e?{revision:e.revision,loaded:e.loaded,configEntries:e.configEntries,manifests:e.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function ea(t,e,i=ta(t),s){const n=i.manifests[e];return function(t,e){if("function"!=typeof t?.localize)return"";try{const i=`component.${e}.title`,s=t.localize(i);return"string"==typeof s&&s!==i?s:""}catch{return""}}(t,e)||String(n?.name||"")||String(s||"")||e.replace(/_/g," ")}function ia(t,e,i,s,n){const o=String(e||"").trim();if(!o)return null;const a=n?s.configEntries[n]:null;return{domain:o,label:ea(t,o,s,a?.title),configEntryId:n,configEntryTitle:a?.title,confidence:i}}function sa(t){const e={"registry-owner":0,"entity-platform":1,"identifier-fallback":2},i=new Set;return t.filter(t=>!!t).sort((t,i)=>e[t.confidence]-e[i.confidence]||t.label.localeCompare(i.label)||t.domain.localeCompare(i.domain)).filter(t=>{const e=`${t.domain}\n${t.configEntryId||""}`;return!i.has(e)&&(i.add(e),!0)})}function na(t,e,i,s=ta(t)){const n=i.entities?.[e]||t?.entities?.[e];if(!n)return null;const o=n.config_entry_id||void 0,a=o?s.configEntries[o]?.domain:"";return ia(t,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: 1–{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.markup_boundary":"Boundary: click two points on a solid shared wall to make that stretch virtual; click a dashed stretch to restore it","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.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.boundary":"Boundary","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.open_boundary":"Open boundary","history.close_boundary":"Restore wall","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.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","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 how thick it is. Empty or zero removes the thickness.","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 (1–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 (not an open boundary)","toast.wallthick_open":"Open boundaries have no thickness","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from 1 to {max} {unit}","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.","markup.boundary_hint":"Select a shared room boundary or a dashed stretch","markup.boundary_hint_open":"Click the start of the open stretch","markup.boundary_hint_second":"Choose the end on the selected boundary · Esc to cancel","markup.boundary_hint_retry":"Choose the end on the selected boundary","markup.boundary_hint_restore":"Click to restore the wall","markup.boundary_hint_outer":"Only a boundary shared by two rooms can be opened","markup.boundary_hint_blocked":"a partition, column or unfinished contour is above this boundary","markup.boundary_hint_ambiguous":"Choose a stretch farther from the junction","markup.boundary_cancel":"Cancel the unfinished Boundary action","toast.openwall_pick":"This tool works with room boundaries","toast.openwall_shared_only":"Only a shared wall between two rooms can be opened","toast.openwall_short":"Stretch too short — pick a farther point","toast.boundary_opened":"Boundary stretch opened","toast.boundary_restored":"Wall restored on this stretch","toast.boundary_same_edge":"Choose the end point on the selected wall","toast.boundary_ambiguous":"Choose a stretch slightly farther from the junction","toast.boundary_blocked":"This boundary is covered by independent masonry. Move or delete it with Select first","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.openwall_openings_removed":"Openings on the virtual stretch were removed","toast.openwall_opened":"Boundary “{a}” ↔ “{b}” is now open","toast.openwall_closed":"Boundary “{a}” ↔ “{b}” is closed again","toast.opening_on_virtual":"Openings cannot sit on a virtual 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 "Hidden and disabled" 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. Update House Plan and try again. If the error persists, attach a space export to the bug report.","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}; virtual fragments: {s}; independent walls: {i}.","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.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.replace_warning":"This replaces the current configuration and layout. Uploaded files are never deleted. One undo remains available until the next plan edit.","backup.foreign_bookkeeping":"Instance-specific known/new-device bookkeeping will not be imported.","backup.final_name":"New space name","backup.target_settings":"The target instance's global settings remain unchanged.","backup.duplicates":"Bindings already present: {n}","backup.skip":"Skip duplicate devices","backup.virtual_copy":"Add safe static virtual copies","backup.content":"Referenced content","backup.content_available":"available locally","backup.content_external":"external link","backup.content_detach_required":"will be detached","backup.confirm_detach":"I understand that unavailable internal plans and attachments will be detached from the imported configuration.","backup.replace":"Replace","backup.add":"Add space","backup.space_done":"Space imported: {rooms} rooms, {markers} devices, {refs} existing references restored","backup.full_done":"Backup restored: {spaces} spaces, {rooms} rooms, {markers} devices","backup.undo_import":"Undo last full import","backup.import_undone":"Full import undone"};const aa={en:oa,ru:{"editor.context_actions":"Действия: {object}","editor.tool_options":"Параметры инструмента: {tool}","editor.palette":"Палитра: {tool}","editor.open_group":"Группа инструментов: {group}","editor.group_active":"{group} — активно: {item}","editor.disabled_action":"{action} недоступно: {reason}","color_picker.title":"Выбор цвета","color_picker.hue":"Оттенок","color_picker.saturation":"Насыщенность","color_picker.value":"Яркость","color_picker.hex":"Цвет HEX","color_picker.invalid_hex":"Введите цвет HEX из 3 или 6 цифр","btn.properties":"Свойства","btn.keep_as_walls":"Оставить стенами","btn.delete_room_keep_walls":"Удалить комнату, оставить стены","btn.delete_room_with_walls":"Удалить комнату и стены","title.markup_select":"Выбор и редактирование стен, колонн и сохранённых контуров","title.markup_column":"Колонна: кликните точку сетки для квадратной колонны","markup.partition":"Перегородка","markup.column":"Колонна","markup.hint_column":"кликните точку сетки, чтобы поставить квадратную колонну","history.draft_segment":"Добавление сегмента черновика комнаты","history.draft_merge":"Соединение незавершённых контуров комнаты","history.draft_segment_delete":"Удаление сегмента черновика комнаты","history.partition_add":"Добавление перегородки","history.column_add":"Добавление колонны","history.physical_edit":"Изменение физического объекта","history.physical_delete":"Удаление физического объекта","history.physical_move":"Перемещение физического объекта","history.contour_to_partitions":"Преобразование контура в замкнутые стены","history.wall_chain_finish":"Завершение цепочки стен","history.wall_face_batch":"Создание комнат из стен","toast.column_duplicate":"Колонна с тем же центром и внешним размером уже существует","confirm.delete_draft":"Удалить весь незавершённый контур комнаты?","confirm.delete_draft_segment":"Удалить этот сегмент черновика? Оставшийся контур может разделиться на два.","physical.partition_properties":"Свойства перегородки","physical.column_properties":"Свойства колонны","physical.draft_properties":"Свойства сегмента черновика","physical.shape":"Форма","physical.square":"Квадрат","physical.circle":"Круг","physical.diameter":"Диаметр","physical.side":"Сторона","physical.rotation":"Угол поворота","physical.length":"Длина","physical.allowed_range":"Допустимо: 1–{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.markup_boundary":"Граница: две точки на сплошной общей стене делают участок виртуальным; клик по пунктиру восстанавливает стену","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.commit_failed":"Изменение размера отменено: итоговый план не прошёл проверку геометрии","resize.preview_failed":"Стена остановлена в последней безопасной позиции: геометрию плана нельзя сохранить без потерь","markup.opening":"Проём","markup.boundary":"Граница","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.open_boundary":"Открытие границы","history.close_boundary":"Восстановление стены","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.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":"Правила иконок","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":"Толщина — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены (1–100 см). Общие стены сохраняют существующее значение.","room.queue_progress":"Комната {current} из {total}","toast.wall_rooms_saved":"Создано комнат: {n}","toast.wall_chain_saved":"Цепочка стен сохранена","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от 1 до {max} {unit}","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","toast.geometry_unsafe":"Изменение отменено: геометрию стен нельзя безопасно построить.","markup.boundary_hint":"Выберите общую границу комнат или пунктирный участок","markup.boundary_hint_open":"Кликните начало открытого участка","markup.boundary_hint_second":"Укажите конец на выбранной границе · Esc — отмена","markup.boundary_hint_retry":"Укажите конец на выбранной границе","markup.boundary_hint_restore":"Кликните, чтобы восстановить стену","markup.boundary_hint_outer":"Открыть можно только общую границу двух комнат","markup.boundary_hint_blocked":"над границей находится перегородка, колонна или незавершённый контур","markup.boundary_hint_ambiguous":"Выберите участок чуть дальше от стыка","markup.boundary_cancel":"Отменить незавершённое действие «Граница»","toast.openwall_pick":"Инструмент работает с границами комнат","toast.openwall_shared_only":"Открыть можно только общую стену двух комнат","toast.openwall_short":"Слишком короткий отрезок — кликните дальше","toast.boundary_opened":"Участок границы открыт","toast.boundary_restored":"Стена на участке восстановлена","toast.boundary_same_edge":"Выберите конечную точку на выбранной стене","toast.boundary_ambiguous":"Выберите участок чуть дальше от стыка","toast.boundary_blocked":"Граница перекрыта независимой стеной. Сначала переместите или удалите её инструментом «Выбрать»","toast.delete_room_pick":"Кликните внутри комнаты, которую нужно удалить","toast.plan_snap_ambiguous":"Увеличьте масштаб, чтобы выбрать узел стены","toast.wall_repair_ambiguous":"У контура несколько возможных соединений. Увеличьте масштаб и соедините стены явно.","toast.wall_repair_too_large":"Разрыв больше 2 см. Соедините стены перед созданием комнаты.","toast.wall_repair_changed":"Геометрия стен изменилась. Попробуйте создать комнату ещё раз.","toast.openwall_openings_removed":"Проёмы на виртуальном отрезке удалены","toast.openwall_opened":"Граница «{a}» ↔ «{b}» теперь открыта","toast.openwall_closed":"Граница «{a}» ↔ «{b}» снова закрыта","toast.opening_on_virtual":"Проёмы на виртуальной стене запрещены","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":"Планы не изменены. Обновите House Plan и повторите. Если ошибка останется, приложите экспорт пространства к отчёту об ошибке.","gs.align_preflight_space":"Пространство {n}","gs.align_preflight_more":" и ещё {n}","gs.optimize_changes":"Миграций модели: {m}; обновлено пространств: {c}; устранён шум координат: {p}; объединено отрезков реальных стен: {w}; виртуальных: {s}; независимых: {i}.","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.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.replace_warning":"Текущая конфигурация и расположение будут заменены. Загруженные файлы не удаляются. Одну отмену можно выполнить до следующего изменения плана.","backup.foreign_bookkeeping":"Служебные списки известных и новых устройств другого экземпляра импортированы не будут.","backup.final_name":"Название нового пространства","backup.target_settings":"Глобальные настройки целевого экземпляра не изменятся.","backup.duplicates":"Уже существующих привязок: {n}","backup.skip":"Пропустить повторяющиеся устройства","backup.virtual_copy":"Добавить безопасные статичные виртуальные копии","backup.content":"Связанное содержимое","backup.content_available":"доступно локально","backup.content_external":"внешняя ссылка","backup.content_detach_required":"будет отвязано","backup.confirm_detach":"Я понимаю, что недоступные внутренние планы и вложения будут отвязаны от импортируемой конфигурации.","backup.replace":"Заменить","backup.add":"Добавить пространство","backup.space_done":"Пространство импортировано: комнат — {rooms}, устройств — {markers}, существующих ссылок восстановлено — {refs}","backup.full_done":"Копия восстановлена: пространств — {spaces}, комнат — {rooms}, устройств — {markers}","backup.undo_import":"Отменить последний полный импорт","backup.import_undone":"Полный импорт отменён"}};function ra(t,e){if(e&&e in aa)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function la(t,e,i){return ss(aa[t][e]??oa[e]??e,i)}function ca(t,e){const i=aa[t][e]??aa.en[e];return"string"==typeof i&&i.trim().length>0}class ha extends ht{constructor(){super(...arguments),this.deviceName="",this._metadataConnection=null,this._demoUntil=0,this._demoTimer=0,this._demoGeneration=1,this._demoKind=null,this._reducedMotion=!1,this._onMotionChange=t=>{this._reducedMotion=t.matches,this.requestUpdate()},this._startShortDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&(this._demoGeneration++,this._demoKind="short",this._demoUntil=Date.now()+3300,window.clearTimeout(this._demoTimer),this._demoTimer=window.setTimeout(()=>{this._demoUntil=0,this._demoTimer=0,this._demoKind=null,this.requestUpdate()},3330),this.requestUpdate())},this._toggleContinuousDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&("continuous"===this._demoKind?this._clearDemo():(this._clearDemo(),this._demoGeneration++,this._demoKind="continuous",this._demoUntil=Number.POSITIVE_INFINITY),this.requestUpdate())}}connectedCallback(){super.connectedCallback(),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this._ensureMetadata()}disconnectedCallback(){this._metadataRelease?.(),this._metadataRelease=void 0,this._metadataConnection=null,this._clearDemo(),this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,super.disconnectedCallback()}willUpdate(t){t.has("hass")&&this._ensureMetadata();const e=this.presentation,i=t.get("presentation");t.has("presentation")&&i?.binding!==e?.binding&&this._clearDemo(),e&&this._demoKind&&("icon_ripple"!==e.display||"none"!==e.pulse.kind)&&this._clearDemo()}_ensureMetadata(){const t=this.hass?.connection||null;t&&t!==this._metadataConnection&&(this._metadataRelease?.(),this._metadataConnection=t,this._metadataRelease=Qo(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0,this._demoKind=null}get _lang(){return ra(this.hass)}_t(t,e){return la(this._lang,t,e)}_providerText(t){return"houseplan"===t.domain?this._t("marker.preview.virtual_provider"):t.label.toLowerCase()===t.domain.toLowerCase()?t.domain:`${t.label} (${t.domain})`}_providers(){return this.presentation&&this.registry?function(t,e,i,s=ta(t)){if("virtual"===e)return[{domain:"houseplan",label:"House Plan",confidence:"registry-owner"}];const n=e.indexOf(":");if(n<1)return[];const o=e.slice(0,n),a=e.slice(n+1);if("entity"===o){const e=na(t,a,i,s);return e?[e]:[]}if("device"!==o)return[];const r=i.devices?.[a]||t?.devices?.[a];if(!r)return[];const l=[r.config_entry_id,...Array.isArray(r.config_entries)?r.config_entries:[]].filter(t=>"string"==typeof t&&!!t).map(e=>{const i=s.configEntries[e];return ia(t,i?.domain,"registry-owner",s,e)});if(l.some(Boolean))return sa(l);const c=[];for(const[e,n]of Object.entries(i.entities||{}))n?.device_id===a&&null==n?.disabled_by&&c.push(na(t,e,i,s));return c.some(Boolean)?sa(c):sa((Array.isArray(r.identifiers)?r.identifiers:[]).map(e=>{const i=Array.isArray(e)?e[0]:null;return ia(t,i,"identifier-fallback",s)}))}(this.hass,this.presentation.binding,this.registry,ta(this.hass)):[]}_providerSummary(t){if(!t.length)return this._t("marker.preview.unknown_provider");const e=t.slice(0,2).map(t=>this._providerText(t)),i=t.length-e.length;return e.join(", ")+(i>0?` · ${this._t("marker.preview.more_sources",{n:i})}`:"")}_sourceSummary(t){const e=t.visualSources;return e.length?1===e.length?`${e[0].name} · ${e[0].eid} · ${this._sourceProvider(e[0].eid)}`:2===e.length?e.map(t=>t.name).join(" · "):this._t("marker.preview.multiple_sources",{n:e.length}):this._t("marker.preview.no_source")}_stateSummary(t){if(!t.visualSources.length)return this._t("marker.preview.no_state");if(1===t.visualSources.length)return t.visualSources[0].stateText||this._t("marker.preview.no_state");const e=[...new Set(t.visualSources.map(t=>t.stateText).filter(Boolean))];return 1===e.length?e[0]:this._t("marker.preview.mixed_states")}_reason(t){return this._t(`marker.preview.reason.${t}`)}_sourceProvider(t){if(!this.registry)return this._t("marker.preview.unknown_provider");const e=na(this.hass,t,this.registry,ta(this.hass));return e?this._providerText(e):this._t("marker.preview.unknown_provider")}render(){const t=this.presentation;if(!t)return U;const e=!!this._demoKind&&this._demoUntil>Date.now()&&"icon_ripple"===t.display&&"none"===t.pulse.kind,i=e?(()=>{const e=function(t,e,i,s,n=null){return{kind:e,reason:"short"===e?"event":"running",generation:Math.max(1,Math.trunc(i)),expiresAt:"short"===e?n:null,color:t.color,diameterScale:s?1:t.diameterScale,animated:!s,reducedMotionIndicator:s?"dot":"none"}}(t.pulse,this._demoKind,this._demoGeneration,this._reducedMotion,"short"===this._demoKind?this._demoUntil:null);return{...t,pulse:e,classes:[...t.classes.filter(t=>!t.startsWith("pulse-")),`pulse-${e.kind}`,...e.generation%2==0?["pulse-gen2"]:[]]}})():t,s=this._providers(),n="none"!==t.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 t=i.scale*Math.min(4,Math.max(.9,.29*i.valueBadge.text.length+.28)),e=.7*i.scale,s=.6*i.scale;"right"===i.valueBadge.position&&(r=Math.max(r,s+t)),"left"===i.valueBadge.position&&(a=Math.min(a,-s-t)),"top"===i.valueBadge.position&&(l=Math.min(l,-s-e)),"bottom"===i.valueBadge.position&&(c=Math.max(c,s+e))}if(null!=i.lqiText){const t=i.scale*("bottom"===i.valueBadge?.position?1.55:.95);c=Math.max(c,t)}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",Go(this.hass),...i.classes,"virtual"===i.binding?"virtual":"",i.haDisabled?"ghost ha-disabled":"",null!=i.valueText?"valonly":""].filter(Boolean).join(" "),_=Uo(i).join(";"),m=e?this._t("continuous"===this._demoKind?"marker.preview.demo_continuous_notice":"marker.preview.demo_short_notice"):this._reason(t.explanation.reason),g=[this.deviceName,this._providerSummary(s),this._stateSummary(t),m,t.valueFullText||"",Ro(t.valueBadge),null!=t.lqiText&&t.lqiBand?this._t(`marker.lqi_a11y_${t.lqiBand}`,{value:t.lqiText}):""].filter(Boolean).join(". ");return W`<section class="devicepreview">
|
||
<div class="previewhead">
|
||
<strong>${this._t("marker.preview.title")}</strong>
|
||
<span class="previewbadge ${e?"example":""}">
|
||
${this._t(e?"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(t)}</b></div>
|
||
<div><span>${this._t("marker.preview.current_state")}</span><b>${this._stateSummary(t)}</b></div>
|
||
<div><span>${this._t("marker.preview.result")}</span><b>${m}</b></div>
|
||
${t.fallbackReason?W`<p class="previewnotice">${this._reason(t.fallbackReason)}</p>`:U}
|
||
${t.explanation.notices.map(t=>W`<p class="previewnotice">${this._reason(t)}</p>`)}
|
||
${h<1?W`<p class="previewnotice">${this._t("marker.preview.scaled",{n:d})}</p>`:U}
|
||
${e&&this._reducedMotion?W`<p class="previewnotice">${this._t("marker.preview.reduced_motion")}</p>`:U}
|
||
</div>
|
||
</div>
|
||
${t.visualSources.length||t.criticalSources.length?W`<details class="previewdetails">
|
||
<summary>${this._t("marker.preview.details")}</summary>
|
||
<div class="previewtable">
|
||
${[...t.visualSources,...t.criticalSources].map(t=>W`
|
||
<div class="previewsource">
|
||
<b>${t.name}</b><code>${t.eid}</code>
|
||
<span>${t.stateText||t.state}</span>
|
||
<span>${this._sourceProvider(t.eid)}</span>
|
||
</div>`)}
|
||
</div>
|
||
</details>`:U}
|
||
${"icon_ripple"===t.display?W`<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>`:U}
|
||
</section>`}}ha.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},ha.styles=[Kt,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",ha);const da=Math.tan(.25*Math.PI/180);function pa(t,e){const i=Math.abs(Number(e[0])-Number(t[0])),s=Math.abs(Number(e[1])-Number(t[1]));if(!([i,s].every(Number.isFinite)&&i>0&&s>0))return null;const n=i>=s?"horizontal":"vertical",o=Math.max(i,s),a=Math.min(i,s);return a/o>da?null:{axis:n,major:o,minor:a,angleDegrees:180*Math.atan2(a,o)/Math.PI}}function ua(t,e){const i=pa(t,e);return i?"horizontal"===i.axis?[Number(e[0]),Number(t[1])]:[Number(t[0]),Number(e[1])]:[Number(e[0]),Number(e[1])]}const _a=t=>`${t[0]},${t[1]}`,ma=(t,e)=>t[0]===e[0]&&t[1]===e[1],ga=(t,e)=>{const i=_a(t),s=_a(e);return i<s?`${i}|${s}`:`${s}|${i}`},fa=t=>{let e=0;for(let i=0;i<t.length;i++){const s=t[i],n=t[(i+1)%t.length];e+=s[0]*n[1]-n[0]*s[1]}return e/2},va=t=>{if(t.length<3)return!1;for(let e=0;e<t.length;e++){if(ma(t[e],t[(e+1)%t.length]))return!1;for(let i=e+1;i<t.length;i++)if(i!==e+1&&(0!==e||i!==t.length-1)&&xi(t[e],t[(e+1)%t.length],t[i],t[(i+1)%t.length]))return!1}return Ti(t)>1e-12},ya=t=>{for(let e=0;e+1<t.length;e++){if(ma(t[e],t[e+1]))return!1;for(let i=e+2;i+1<t.length;i++)if(xi(t[e],t[e+1],t[i],t[i+1]))return!1}return!0},ba=(t,e)=>ma(t,e.from)?[...e.to]:[...t],wa=(t,e)=>t.map(t=>Array.isArray(t?.poly)?{...t,poly:t.poly.map(t=>ba(t,e))}:t),ka=(t,e,i)=>{const s=t.flatMap((t,e)=>hi(t)?.some(t=>ma(t,i.from))?[e]:[]);if(!s.length)return!1;for(const i of s){const s=hi(t[i]),n=hi(e[i]);if(!s||!n)continue;if(!va(n))return!1;const o=Math.sign(fa(s)),a=Math.sign(fa(n));if(o&&a&&o!==a)return!1}const n=new Set;for(const t of s){const i=hi(e[t]);if(i)for(let s=0;s<e.length;s++){if(t===s)continue;const o=t<s?`${t}:${s}`:`${s}:${t}`;if(n.has(o))continue;n.add(o);const a=hi(e[s]);if(a&&Di(i,a))return!1}}return!0},xa=(t,e)=>{let i=0;for(const s of t){const t=hi(s);if(t)for(let s=0;s<t.length;s++){const n=t[s],o=t[(s+1)%t.length];(ma(n,e)||ma(o,e))&&(n[0]!==o[0]&&n[1]!==o[1]||i++)}}return i},$a=(t,e)=>{const{a:i,b:s,axis:n}=t,o="horizontal"===n?{from:s,to:[s[0],i[1]]}:{from:s,to:[i[0],s[1]]},a="horizontal"===n?{from:i,to:[i[0],s[1]]}:{from:i,to:[s[0],i[1]]};return[{move:o,preservedDegree:xa(e,i)},{move:a,preservedDegree:xa(e,s)}].sort((t,e)=>e.preservedDegree-t.preservedDegree||_a(t.move.to).localeCompare(_a(e.move.to))||_a(t.move.from).localeCompare(_a(e.move.from))).map(t=>t.move)};const Sa=(t,e)=>[t[0]-e[0],t[1]-e[1]],Ma=(t,e)=>[t[0]+e[0],t[1]+e[1]],Ca=(t,e)=>t[0]*e[0]+t[1]*e[1],Da=t=>Math.hypot(t[0],t[1]);function Pa(t){let e=0;for(let i=0;i<t.length;i++){const s=t[i],n=t[(i+1)%t.length];e+=s[0]*n[1]-n[0]*s[1]}return e/2}function Ta(t,e,i){const s=Sa(i,e),n=Ca(s,s);if(n<1e-12)return Da(Sa(t,e));let o=Ca(Sa(t,e),s)/n;return o=Math.max(0,Math.min(1,o)),Da(Sa(t,[e[0]+s[0]*o,e[1]+s[1]*o]))}function Ra(t,e){const i=t[e],s=t[(e+1)%t.length],n=Sa(s,i),o=Da(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(t,e){let i=!1;for(let s=0,n=e.length-1;s<e.length;n=s++){const o=e[s][0],a=e[s][1],r=e[n][0],l=e[n][1];a>t[1]!=l>t[1]&&t[0]<(r-o)*(t[1]-a)/(l-a)+o&&(i=!i)}return i}([r[0]+a[0]*l,r[1]+a[1]*l],t)&&(a=[-a[0],-a[1]]),a}function Ia(t){const e=t.length;if(e<3)return!1;for(let i=0;i<e;i++)for(let s=i+1;s<e;s++)if(s!==i&&(s+1)%e!==i&&(i+1)%e!==s&&xi(t[i],t[(i+1)%e],t[s],t[(s+1)%e]))return!1;return!0}function Aa(t,e,i=1e-6){let s=1/0;for(const[n,o]of e){const e=Sa(o,n),a=Da(e);if(a<i)continue;const r=[e[0]/a,e[1]/a],l=t=>(t[0]-n[0])*r[1]-(t[1]-n[1])*r[0],c=t=>(t[0]-n[0])*r[0]+(t[1]-n[1])*r[1],h=i,d=a-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const n=c(e);n<=h||n>=d||t<s&&(s=t)}for(let e=0;e<t.length;e++){const n=t[e],o=t[(e+1)%t.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=t=>Math.abs(a+(t-p)/g*(r-a));s=Math.min(s,f(_),f(m))}}return s}function za(t,e,i){if(Di(t,e,i))return!0;if(Ci(t,e,i)||Ci(e,t,i))return!1;let s=0;try{const i=ni([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]);for(const t of i)t?.[0]&&(s+=Ti(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function Fa(t,e,i){for(const s of e)for(let e=0;e<s.length;e++){const n=s[e],o=s[(e+1)%s.length],a=Sa(o,n),r=Da(a);if(r<i)continue;const l=[a[0]/r,a[1]/r];if(Math.abs((t.x-n[0])*l[1]-(t.y-n[1])*l[0])>i)continue;const c=(t.x-n[0])*l[0]+(t.y-n[1])*l[1];if(c-t.length/2>=-i&&c+t.length/2<=r+i)return!0}return!1}function Ea(t,e,i){const s=Math.abs(e[0]-t[0]),n=Math.abs(e[1]-t[1]);return s<=i&&n>i?"v":n<=i&&s>i?"h":null}function Na(t,e,i){return Math.abs(t[0]-e[0])<=i&&Math.abs(t[1]-e[1])<=i}function Oa(t,e,i,s,n){return Na(t,i,n)&&Na(e,s,n)||Na(t,s,n)&&Na(e,i,n)}function Ha(t,e,i,s,n){const o=Ea(t,e,n);return o&&Ea(i,s,n)===o?"h"===o?Math.abs(t[1]-i[1])>n||Math.abs(t[1]-s[1])>n?0:Math.max(0,Math.min(Math.max(t[0],e[0]),Math.max(i[0],s[0]))-Math.max(Math.min(t[0],e[0]),Math.min(i[0],s[0]))):Math.abs(t[0]-i[0])>n||Math.abs(t[0]-s[0])>n?0:Math.max(0,Math.min(Math.max(t[1],e[1]),Math.max(i[1],s[1]))-Math.max(Math.min(t[1],e[1]),Math.min(i[1],s[1]))):0}function La(t,e,i,s){const n=t.length,o=Ea(t[(e-1+n)%n],t[e],s),a=Ea(t[(e+1)%n],t[(e+2)%n],s),r="h"===i?"v":"h";return o===r&&a===r}function Ba(t){let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t)e=Math.min(e,o[0]),i=Math.min(i,o[1]),s=Math.max(s,o[0]),n=Math.max(n,o[1]);return[e,i,s,n]}function qa(t,e,i){const s=Ba(t),n=Ba(e);return s[2]<n[0]-i||n[2]<s[0]-i||s[3]<n[1]-i||n[3]<s[1]-i}function Wa(t,e,i,s){const n=s.eps;return"circle"===t.kind?Ta(t.center,e,i)<t.radius+(s.movingHalf||0)-n:Ha(e,i,t.a,t.b,n)>n}const ja=(t,e)=>"h"===e?t[0]:t[1],Ga=(t,e)=>"h"===e?t[1]:t[0];function Ua(t,e,i,s,n){const o=t.find(t=>t.id===e);if(!o)return null;const a=o.poly[i],r=o.poly[(i+1)%o.poly.length],l=Ea(a,r,n);if(!l)return null;const c=0===s?r:a,h=0===s?a:r,d=ja(c,l),p=ja(h,l),u=Ga(c,l),_=function(t,e,i,s){const n=[];for(const o of t)for(let t=0;t<o.poly.length;t++){const a=o.poly[t],r=o.poly[(t+1)%o.poly.length];if(Ea(a,r,s)!==e||Math.abs(Ga(a,e)-i)>s||Math.abs(Ga(r,e)-i)>s)continue;const l=ja(a,e),c=ja(r,e),h=Math.min(l,c),d=Math.max(l,c);d-h>s&&n.push({roomId:o.id,edge:t,lo:h,hi:d})}return n.sort((t,e)=>t.lo-e.lo||t.hi-e.hi||t.roomId.localeCompare(e.roomId)||t.edge-e.edge)}(t,l,u,n),m=function(t,e,i,s){const n=[e,i];for(const o of t){const t=Math.max(e,o.lo),a=Math.min(i,o.hi);a-t>s&&n.push(t,a)}const o=[...new Set(n)].sort((t,e)=>t-e),a=[];for(let e=0;e+1<o.length;e++){const i=o[e],n=o[e+1];if(n-i<=s)continue;const r=(i+n)/2,l=[...new Set(t.filter(t=>r>t.lo-s&&r<t.hi+s).map(t=>t.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(t=>t.owners.length<1||t.owners.length>2)?null:{roomId:e,edge:i,movedEndpoint:s,axis:l,line:u,fixed:d,moving:p,intervals:_,baseline:m}}function Va(t,e,i){const s=[];for(const n of t.intervals){const o=e.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(Ea(a,r,i)!==t.axis||Math.abs(Ga(a,t.axis)-t.line)>i||Math.abs(Ga(r,t.axis)-t.line)>i)continue;const l=ja(a,t.axis),c=ja(r,t.axis),h=Math.min(l,c),d=Math.max(l,c);d-h>i&&s.push({...n,lo:h,hi:d})}return s}function Ka(t,e,i){return new Set(t.filter(t=>e>t.lo-i&&e<t.hi+i).map(t=>t.roomId)).size}function Ya(t,e,i){for(const s of e.sideOwnership){const e=t.polys[s.roomId];if(!e)return!1;const n=e[s.edge],o=e[(s.edge+1)%e.length];if(Ea(n,o,i)!==s.axis)return!1;const a=ja(0===s.movedEndpoint?n:o,s.axis),r=Va(s,t,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 t of s.intervals)d.push(t.lo,t.hi);for(const t of r)d.push(t.lo,t.hi);const p=[...new Set(d.filter(t=>t>=c&&t<=h))].sort((t,e)=>t-e),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 t=0;t+1<p.length;t++){const e=p[t],n=p[t+1];if(n-e<=i)continue;const o=(e+n)/2,a=o>u-i&&o<_+i,c=o>m-i&&o<g+i;if(!a&&!c)continue;const h=Ka(s.intervals,o,i),d=Ka(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 Ja(t,e,i,s){const n=[i.n[0]*s,i.n[1]*s],o={polys:{},openings:{},movedSpans:{}};for(const e of i.roomIds){const s=t.find(t=>t.id===e),a=i.edgeByRoom[e];if(!s||null==a)continue;const r=(a+1)%s.poly.length;o.polys[e]=s.poly.map((t,e)=>e===a||e===r?Ma(t,n):[...t]),o.movedSpans[e]=[[Ma(s.poly[a],n),Ma(s.poly[r],n)]]}for(const t of e)i.movingOpeningIds.includes(t.id)&&(o.openings[t.id]=[t.x+n[0],t.y+n[1]]);return o}function Xa(t,e,i,s,n,o,a,r){if(!function(t,e,i,s){if(Ta([t.x,t.y],e,i)>2*s)return!1;const n=Da(Sa(i,e));if(n<=s)return!1;const o=[(i[0]-e[0])/n,(i[1]-e[1])/n],a=(t.x-e[0])*o[0]+(t.y-e[1])*o[1];return a>=-s&&a<=n+s}(t,e,i,r))return!0;if(Ta([t.x,t.y],s,n)>2*r)return!1;const l=0===o?s:n;return Da(Sa([t.x,t.y],l))>=t.length/2+a-r}function Za(t,e,i,s){const n=s.movingHalf||0;return"circle"===t.kind?Ta(t.center,e,i)<t.radius+n-s.eps:function(t,e,i,s){return xi(t,e,i,s)?0:Math.min(Ta(t,i,s),Ta(e,i,s),Ta(i,t,e),Ta(s,t,e))}(e,i,t.a,t.b)<(t.half||0)+n-s.eps}function Qa(t,e,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=Ja(t,e,i,s);if(Object.keys(r.polys).length!==i.roomIds.length)return!1;if(!Ya(r,i,o))return!1;const l=new Set(i.roomIds),c=t=>r.polys[t.id]||t.poly;for(const s of i.roomIds){const l=t.find(t=>t.id===s),c=r.polys[s],h=i.edgeByRoom[s];if(!l||!c||c.length!==i.topology[s]||c.length!==l.poly.length||!Ia(c))return!1;const d=l.poly.length,p=(h-1+d)%d,u=[p,h,(h+1)%d];for(const t of u)if(pa(c[t],c[(t+1)%d]))return!1;const _=Pa(l.poly),m=Pa(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=Aa(l.poly,[g],o);if(Aa(c,f,o)<Math.min(a,v)-o)return!1;const y=[l.poly[p],l.poly[h]],b=[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=Ea(c[h],c[(h+1)%d],o),$="h"===x?"v":"v"===x?"h":null;if(!$||Ea(...b,o)!==$||Ea(...k,o)!==$)return!1;for(const t of e)if(!i.movingOpeningIds.includes(t.id)){if(!Xa(t,...y,...b,1,n.movingHalf||0,o))return!1;if(!Xa(t,...w,...k,0,n.movingHalf||0,o))return!1}}if(2===i.roomIds.length){const[t,e]=i.roomIds,s=r.polys[t],n=r.polys[e],a=i.edgeByRoom[t],l=i.edgeByRoom[e];if(!Oa(s[a],s[(a+1)%s.length],n[l],n[(l+1)%n.length],o))return!1}for(const e of i.roomIds){const i=t.find(t=>t.id===e),s=r.polys[e];for(const n of t){if(n.id===e||l.has(n.id))continue;const t=c(n);if(!qa(i.poly,n.poly,o)||!qa(s,t,o))if(Ci(i.poly,n.poly,o)){if(!Ci(s,t,o))return!1}else if(Ci(n.poly,i.poly,o)){if(!Ci(t,s,o))return!1}else if(Ci(s,t,o)||Ci(t,s,o)||za(s,t,o))return!1}}t.find(t=>t.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 t of n.obstacles||[])if(Za(t,p,u,n))return!1;for(const t of e){if(!i.movingOpeningIds.includes(t.id))continue;const e=r.openings[t.id];if(!e||t.hosted)return!1;if(!Fa({...t,x:e[0],y:e[1]},[h],2*o))return!1}return!0}const tr=new WeakMap;function er(t,e,i){const s=i/e;return Ti(t)*s*s/1e4}function ir(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const sr=(t,e)=>[(t[0]-e.x)/Math.max(1e-9,e.w)*e.stageWidth,(t[1]-e.y)/Math.max(1e-9,e.h)*e.stageHeight];function nr(t){const{poly:e,edge:i,text:s,view:n}=t,o=e[i],a=e[(i+1)%e.length],r=[(o[0]+a[0])/2,(o[1]+a[1])/2],l=function(t,e){const i=t[e],s=t[(e+1)%t.length],n=s[0]-i[0],o=s[1]-i[1],a=Math.hypot(n,o)||1;return(t=>{let e=0;for(let i=0;i<t.length;i++){const s=t[i],n=t[(i+1)%t.length];e+=s[0]*n[1]-n[0]*s[1]}return e/2})(t)>=0?[-o/a,n/a]:[o/a,-n/a]}(e,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=sr(r,n),p=sr(t.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=e=>{return i=m+h[0]*e,s=g+h[1]*e,n=u,o=18,a=p[0],r=p[1],l=t.gearWidthPx,c=t.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 t=(Math.abs(h[0])>=Math.abs(h[1])?h[0]:h[1])>0?-1:1,e=Math.max(n.stageWidth,n.stageHeight,64);for(let i=4;i<=e;i+=4){const e=i*t;if(!f(e)){v=e;break}const s=-e;if(!f(s)){v=s;break}}}const y=l[0]*_+h[0]*v,b=l[1]*_+h[1]*v,w=Math.hypot(y,b)||1,k=b/w*12,x=[r[0]+y/w*12*n.w/Math.max(1,n.stageWidth),r[1]+k*n.h/Math.max(1,n.stageHeight)];return{anchor:r,offsetXPx:y,offsetYPx:b,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 or(t){const e=t%360;return e<0?e+360:e}function ar(t,e){const i=function(t,e){return or(t+e)}(t,e)*Math.PI/180;return[Math.sin(i),-Math.cos(i)]}const rr=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)"})}),lr=t=>"number"==typeof t&&Number.isFinite(t),cr=(t,e,i)=>Math.min(i,Math.max(e,t));function hr(t){return t.elevation<=-6?"night":t.elevation>=6?"day":t.rising?"dawn":"dusk"}function dr(t){const e=(Math.floor(Number(t)||0)%1440+1440)%1440;return e>=300&&e<480?"dawn":e>=480&&e<1080?"day":e>=1080&&e<1260?"dusk":"night"}function pr(t){const e=hr(t),i=t.azimuth*Math.PI/180;return{sunX:50-42*Math.sin(i),sunY:78-cr(t.elevation,0,90)/90*64,sunOpacity:"night"===e?0:cr((t.elevation+6)/12,0,1)}}function ur(t){const e=(Math.floor(Number(t)||0)%1440+1440)%1440;if(e<300||e>=1260)return{sunX:50,sunY:78,sunOpacity:0};const i=(e-300)/960,s=(e-300)/120,n=(1260-e)/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(t,e=new Date){const i=function(t){const e=t?.states?.["sun.sun"]?.attributes;return e&&lr(e.azimuth)&&lr(e.elevation)&&"boolean"==typeof e.rising?{azimuth:or(e.azimuth),elevation:e.elevation,rising:e.rising}:null}(t);if(i)return{phase:hr(i),source:"sun",...pr(i)};const s="number"==typeof e?e:function(t=new Date){return 60*t.getHours()+t.getMinutes()}(e);return{phase:dr(s),source:"clock",...ur(s)}}function mr(t){return`${t.source}|${t.phase}|${t.sunX.toFixed(2)}|${t.sunY.toFixed(2)}|`+t.sunOpacity.toFixed(3)}const gr=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],fr=t=>Math.min(1,Math.max(0,t));function vr(t,e,i=6){const s=t.angle*Math.PI/180,n=[Math.sin(s),-Math.cos(s)],o=s=>{const o=[t.x+n[0]*i*s,t.y+n[1]*i*s];return e.find(t=>t.poly.length>=3&&vi(o,t.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 yr(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function br(t,e,i,s){return[[t[0],t[1]],[e[0],e[1]],[e[0]+i[0]*s,e[1]+i[1]*s],[t[0]+i[0]*s,t[1]+i[1]*s]]}function wr(t,e){try{const i=ni([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]),s=[];for(const t of i){const e=t?.[0];!Array.isArray(e)||e.length<4||s.push(e.slice(0,e.length-1).map(t=>[t[0],t[1]]))}return s}catch{return[]}}function kr(t,e,i,s,n,o,a){if(!(s>0))return[];const r=ar(i,n),l=[-r[0],-r[1]],c=function(t){const e=Math.min(90,Math.max(0,t));return.7*(.8+1.7*Math.pow(1-e/90,1.6))}(s),h=[];for(const i of e){if(!(i.length>0))continue;const e=vr(i,t);if(!e||!yr(e.normal,r,s))continue;const n=t.find(t=>t.id===e.roomId);if(!n)continue;const d=o&&o[e.roomId]||n.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.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,y=Math.sin(p)*u,b=[g-v,f-y],w=[g+v,f+y],k=c*i.length,x=wr(br(b,w,l,k),d);if(!x.length)continue;const $=l[0]*_[0]+l[1]*_[1];h.push({openingId:i.id,roomId:e.roomId,polys:x,a:b,b:w,dir:l,len:k,normal:_,depth:k*$})}return h}const xr=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function $r(t,e){const i=xr(e?.north_deg);return null!==i?i:xr(t?.north_deg)}function Sr(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function Mr(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Cr(t){const e=t?.states?.["sun.sun"]?.attributes,i=Number(e?.azimuth),s=Number(e?.elevation);return Number.isFinite(i)&&Number.isFinite(s)?{azimuth:i,elevation:s}:null}const Dr=["dawn","day","dusk","night"];function Pr(t){if(!t)return"";const e=rr[t.phase];return[`--hp-day-cycle-outline-near:${e.outlineNear}`,`--hp-day-cycle-outline-mid:${e.outlineMid}`,`--hp-day-cycle-outline-far:${e.outlineFar}`].join(";")}function Tr(t,e=1){if(!t)return U;const i=[`--hp-day-cycle-sun-x:${t.sunX.toFixed(2)}%`,`--hp-day-cycle-sun-y:${t.sunY.toFixed(2)}%`,`--hp-day-cycle-sun-opacity:${t.sunOpacity.toFixed(3)}`,`opacity:${Math.min(1,Math.max(0,e)).toFixed(4)}`].join(";");return W`<div class="hp-day-cycle-env" aria-hidden="true"
|
||
data-day-cycle-phase=${t.phase} data-day-cycle-source=${t.source}
|
||
style=${i}>
|
||
${Dr.map(e=>{const i=rr[e],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 W`<div class="hp-day-cycle-bg phase-${e} ${e===t.phase?"active":""}"
|
||
data-day-cycle-layer=${e} style=${s}>
|
||
<div class="hp-day-cycle-sun" style="background:radial-gradient(circle, ${i.sun} 0%, transparent 67%)"></div>
|
||
</div>`})}
|
||
</div>`}const Rr={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},Ir=(t,e=1)=>{const i=Number(t);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):e},Ar=t=>{let e=Number(t);return Number.isFinite(e)?(e=(e%360+360)%360,e>180?e-360:e):0};function zr(t,e,i){return Ar(e)?[t.x,t.y]:i([t.x,t.y])}const Fr=(t,e,i)=>{const s=Number(t),n=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&s>0?s/n*i:0},Er=(t,e,i)=>{const s=Number(t),n=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&i>0?s/i*n:0},Nr=(t,e,i,s=Rr.widthCm)=>{const n=Number(t?.width_cm);if(Number.isFinite(n)&&n>0)return n;const o=Number(t?.width);return Number.isFinite(o)&&o>0?Er(o,e,i):s};function Or(t,e){return{color:t.color,opacity:Ir(t.opacity),width_cm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),...e?{fill:t.fill,fill_color:t.fillColor,fill_opacity:Ir(t.fillOpacity,.25)}:{}}}function Hr(t){const e=t.x+t.w/2,i=t.y+t.h/2,s=Ar(t.angle)*Math.PI/180,n=Math.cos(s),o=Math.sin(s),a=(t,s)=>{const a=t-e,r=s-i;return[e+a*n-r*o,i+a*o+r*n]};return[a(t.x,t.y),a(t.x+t.w,t.y),a(t.x+t.w,t.y+t.h),a(t.x,t.y+t.h)]}function Lr(t){const e=Hr(t),i=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],s=[(e[0][0]+e[2][0])/2,(e[0][1]+e[2][1])/2];return{points:[...e,i(e[0],e[1]),i(e[1],e[2]),i(e[2],e[3]),i(e[3],e[0]),s],segments:e.map((t,i)=>({a:t,b:e[(i+1)%4]}))}}function Br(t,e,i,s,n,o,a,r){const l=Ar(t.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l),d=-Math.sin(l),p=Math.cos(l),u=t.x+t.w/2,_=t.y+t.h/2,m=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,f=u+m*c+g*d,v=_+m*h+g*p,y=s-f,b=n-v;let w=(y*c+b*h)*(e>0?1:-1),k=(y*d+b*p)*(i>0?1:-1),x=null;if(o){const e=w/Math.max(t.w,r),i=k/Math.max(t.h,r);x=Math.max(r/Math.max(t.w,r),r/Math.max(t.h,r),e,i),w=t.w*x,k=t.h*x}if(a>0)if(o){const e=Math.max(1,Math.round(t.w/a)),i=Math.max(1,Math.round(t.h/a));if(Math.abs(t.w-e*a)<1e-6&&Math.abs(t.h-i*a)<1e-6){const s=(t,e)=>{let i=Math.abs(t),s=Math.abs(e);for(;s;)[i,s]=[s,i%s];return Math.max(1,i)},n=1/s(e,i),o=Math.max(r/t.w,r/t.h),a=Math.max(o,Math.round((x??1)/n)*n);w=t.w*a,k=t.h*a}else{t.w>=t.h?(w=Math.round(w/a)*a,k=w*(t.h/Math.max(t.w,r))):(k=Math.round(k/a)*a,w=k*(t.w/Math.max(t.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+(e>0?w/2:-w/2)*c+(i>0?k/2:-k/2)*d-w/2,y:v+(e>0?w/2:-w/2)*h+(i>0?k/2:-k/2)*p-k/2,w:w,h:k,angle:Ar(t.angle)||void 0}}const qr=(t,e,i)=>{const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;if(o<1e-12)return[...e];const a=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*n)/o));return[e[0]+s*a,e[1]+n*a]};const Wr=10**9,jr=240,Gr=1e-4;function Ur(t){if(Array.isArray(t))return t.map(t=>Ur(t));if(null!==t&&"object"==typeof t){const e={};for(const[i,s]of Object.entries(t))e[i]=Ur(s);return e}return t}function Vr(t){if("number"!=typeof t||!Number.isFinite(t))return t;const e=(t<0||Object.is(t,-0)?-1:1)*(Math.floor(Math.abs(t)*Wr+.5)/Wr);return 0===e?0:e}function Kr(t){if("number"!=typeof t||!Number.isFinite(t))return t;const e=t*jr,i=Math.round(e);if(Math.abs(e-i)<Gr){const t=i/jr;return 0===t?0:t}return Vr(t)}function Yr(t){if(!Number.isFinite(t)||0===t)return"0";return Math.abs(t)<.001?t.toExponential(2):String(Number(t.toPrecision(3)))}function Jr(t){return null===t||"object"!=typeof t||Array.isArray(t)?null:t}function Xr(t){return Array.isArray(t)?t.filter(t=>null!==Jr(t)):[]}function Zr(t,e){for(const i of e)Object.prototype.hasOwnProperty.call(t,i)&&(t[i]=Vr(t[i]))}function Qr(t,e){for(const i of e)Object.prototype.hasOwnProperty.call(t,i)&&(t[i]=Kr(t[i]))}function tl(t){if(Array.isArray(t))for(let e=0;e<Math.min(2,t.length);e++)t[e]=Kr(t[e])}function el(t){if(Array.isArray(t))for(const e of t)tl(e)}function il(t,e,i){const s=Jr(t);if(s)for(const t of Xr(s.spaces)){const e=(e,s)=>{for(const n of s){const s=e[n];"number"==typeof s&&Number.isFinite(s)&&i(s,t)}},s=e=>{if(Array.isArray(e))for(let s=0;s<Math.min(2,e.length);s++){const n=e[s];"number"==typeof n&&Number.isFinite(n)&&i(n,t)}},n=t=>{if(Array.isArray(t))for(const e of t)s(e)};for(const i of Xr(t.rooms))e(i,["x","y","w","h"]),n(i.poly);for(const e of Xr(t.walls))s(e.a),s(e.b);for(const i of Xr(t.openings))e(i,["x","y"]);for(const i of Xr(t.decor))"line"===i.kind?e(i,["x1","y1","x2","y2"]):"rect"===i.kind||"ellipse"===i.kind||"furniture"===i.kind?e(i,["x","y","w","h"]):"text"===i.kind&&e(i,["x","y"]);for(const e of Xr(t.room_drafts))n(e.points);for(const e of Xr(t.partitions))s(e.a),s(e.b);for(const e of Xr(t.wall_columns))s(e.center);for(const e of Xr(t.open_spans))s(e.a),s(e.b)}const n=Jr(e);if(!n)return;const o=new Map;if(s)for(const t of Xr(s.spaces))null!=t.id&&o.set(String(t.id),t);for(const t of Object.values(n)){const e=Jr(t);if(!e)continue;const s=null!=e.s&&o.get(String(e.s))||null;for(const t of["x","y"]){const n=e[t];"number"==typeof n&&Number.isFinite(n)&&i(n,s)}}}function sl(t){const e=Number(t?.cell_cm);return e>0?e:5}function nl(t,e={}){const i={canonicalized:0,far:0,maxShift:0,maxShiftCm:0,spaces:[],bySpace:new Map},s=Xr(Jr(t)?.spaces);let n=5;for(const t of s)n=Math.max(n,sl(t));il(t,e,(t,e)=>{const s=t*jr,o=Math.abs(s-Math.round(s)),a=Kr(t),r=o>0&&o<Gr;if(!r&&!(o>=Gr))return;const l=r&&"number"==typeof a?Math.abs(a-t):0;if(r?i.canonicalized++:i.far++,i.maxShift=Math.max(i.maxShift,l),i.maxShiftCm=Math.max(i.maxShiftCm,l*jr*(e?sl(e):n)),!e?.id||!r)return;const c=String(e.id);let h=i.bySpace.get(c);h||(h={spaceId:c,space:String(e.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*jr*sl(e))}),i.bySpace.size&&il(t,e,(t,e)=>{if(!e?.id)return;const s=i.bySpace.get(String(e.id));if(!s)return;const n=t*jr;Math.abs(n-Math.round(n))>=Gr&&s.far++}),i.spaces=[...i.bySpace.values()];const{bySpace:o,...a}=i;return a}function ol(t){return function(t){const e=t,i=Jr(e);i&&Qr(i,["x","y"]);return e}(Ur(t))}function al(t){const e=t,i=Jr(e);if(!i)return e;for(const t of Object.values(i)){const e=Jr(t);e&&Qr(e,["x","y"])}return e}function rl(t){const e=t,i=Jr(e);if(!i)return e;for(const t of Xr(i.spaces)){Zr(t,["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"]);for(const e of Xr(t.rooms))Qr(e,["x","y","w","h"]),el(e.poly);for(const e of Xr(t.walls))tl(e.a),tl(e.b);for(const e of Xr(t.openings)){Qr(e,["x","y"]),Zr(e,["angle","length"]);const t=Jr(e.host);t&&Zr(t,["t"])}for(const e of Xr(t.decor))"line"===e.kind?Qr(e,["x1","y1","x2","y2"]):"rect"===e.kind||"ellipse"===e.kind||"furniture"===e.kind?(Qr(e,["x","y","w","h"]),Zr(e,["angle"])):"text"===e.kind&&(Qr(e,["x","y"]),Zr(e,["scale","angle"]));for(const e of Xr(t.room_drafts))el(e.points);for(const e of Xr(t.partitions))tl(e.a),tl(e.b);for(const e of Xr(t.wall_columns))tl(e.center),"square"===e.shape&&Zr(e,["angle"]);for(const e of Xr(t.open_spans))tl(e.a),tl(e.b)}for(const t of Xr(i.markers))Zr(t,["angle"]);return e}const ll=15,cl=Math.sin(.25*Math.PI/180);function hl(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function dl(t){const e=Number(t)>0?Number(t):5;if(5===e)return 8;const i=5/e*8;return Math.min(80,Math.max(.5,i))}function pl(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<2}function ul(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function _l(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function ml(t,e,i){const s=Number(e)>0?Number(e):5;return ul(t)/s*i}function gl(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function fl(t){return Math.max(1e-6*Math.abs(t),1e-9)}function vl(t,e){if(!(e>0&&Number.isFinite(t)))return t;const i=gl(t,e);return Math.abs(i-t)<=fl(e)?i:t}function yl(t,e){let i=e[0]-t[0],s=e[1]-t[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 bl(t,e,i){const s=[vl(t[0],i),vl(t[1],i)],n=[vl(e[0],i),vl(e[1],i)],o=gl((s[0]+n[0])/2,i),a=gl((s[1]+n[1])/2,i),[r,l]=yl(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 wl(t,e,i,s){return 1===s?bl(t,e,i):bl([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function kl(t,e){if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return null;const i=[Number(t.a[0]),Number(t.a[1]),Number(t.b[0]),Number(t.b[1])];if(!i.every(Number.isFinite))return null;const s=e>0?e:1;return[[i[0]*s,i[1]*s],[i[2]*s,i[3]*s]]}function xl(t,e,i,s,n){const o=n>0?n:1;return{key:wl(t,e,s,o),cm:ul(i),a:[t[0]/o,t[1]/o],b:[e[0]/o,e[1]/o]}}function $l(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[n,o]=e.key.slice(0,t).split(",").map(Number),a=Number(e.key.slice(t+1));[n,o,a].every(Number.isFinite)&&s.push({w:e,x:n*i,y:o*i,ang:a})}return s}function Sl(t,e){const[i,s]=yl(t,e);let n=Math.atan2(s,i);return n<0&&(n+=Math.PI),n}function Ml(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function Cl(t,e,i,s,n=1){if(!t?.length)return null;const o=wl(e,i,s,n),a=t.find(t=>t.key===o);if(a)return a;const r=n>0?n:1,l=fl(s)*r,c=(t,e)=>Math.abs(t[0]-e[0])<=l&&Math.abs(t[1]-e[1])<=l;for(const s of t){const t=kl(s,r);if(t&&(c(t[0],e)&&c(t[1],i)||c(t[0],i)&&c(t[1],e)))return s}const h=(e[0]+i[0])/2,d=(e[1]+i[1])/2,p=Sl(e,i),u=Math.max(.5*s,1e-9)*r;for(const e of $l(t,r))if(Ml(e.ang,p)&&Math.hypot(e.x-h,e.y-d)<=u)return e.w;return null}function Dl(t,e,i,s,n=1){const o=Cl(t,e,i,s,n);if(o&&o.cm>0)return ul(o.cm);const a=function(t,e,i,s,n){if(!t?.length)return null;const o=n>0?n:1,a=Math.hypot(i[0]-e[0],i[1]-e[1]);if(a<1e-12)return null;const r=Sl(e,i),l=Math.max(.5*s,1e-9)*o;let c=null;for(const s of t){if(!(s.cm>0))continue;const t=kl(s,o);if(!t)continue;const n=Math.hypot(t[1][0]-t[0][0],t[1][1]-t[0][1]);if(n<1e-12||!Ml(Sl(t[0],t[1]),r))continue;if(pc(e[0],e[1],t[0][0],t[0][1],t[1][0],t[1][1])>l||pc(i[0],i[1],t[0][0],t[0][1],t[1][0],t[1][1])>l)continue;if(n+l<a)continue;const h=Math.max(0,n-a),d=`${s.key}|${ul(s.cm)}|${t.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}(t,e,i,s,n);return a?ul(a.cm):0}function Pl(t,e,i,s=1,n=[]){if(!t?.length)return[];const o=new Set,a=pi(e);for(const t of a)o.add(wl([t[0],t[1]],[t[2],t[3]],i,s));const r=e||[],l=Math.max(i*s*.02,1e-9);for(let t=0;t<r.length;t++){const e=hi(r[t]);if(e)for(let n=t+1;n<r.length;n++){const t=hi(r[n]);if(t)for(const n of Cs(e,t,l))o.add(wl([n[0],n[1]],[n[2],n[3]],i,s))}}for(const e of r){if(!e?.id)continue;const a=Zl(r,e.id,n,i,s,t);if(a)for(let t=0;t<a.poly.length;t++)o.add(wl(a.poly[t],a.poly[(t+1)%a.poly.length],i,s))}return t.filter(t=>(o.has(t.key)||(t=>{const e=kl(t,s);if(!e)return!1;const[i,o]=e,r=o[0]-i[0],c=o[1]-i[1],h=Math.hypot(r,c);if(h<=l)return!1;if(!a.some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];return Ml(Sl(i,o),Sl(e,s))&&pc(i[0],i[1],e[0],e[1],s[0],s[1])<=l&&pc(o[0],o[1],e[0],e[1],s[0],s[1])<=l}))return!1;const d=(n||[]).some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];if(!Ml(Sl(i,o),Sl(e,s)))return!1;const n=t=>Math.abs((t[0]-i[0])*c-(t[1]-i[1])*r)/h;if(n(e)>l||n(s)>l)return!1;const a=h*h,d=((e[0]-i[0])*r+(e[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})(t))&&t.cm>=1&&t.cm<=100)}function Tl(t,e,i,s=8){const[n,o]=yl(t,e);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 Rl(t,e,i,s,n=1,o="affine",a){if(!t?.length)return[];if(e.length!==i.length)return"fixed-topology"===o&&a?.(),t.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=(t,e,i)=>{const s=t.get(e)||new Set;s.add(i),t.set(e,s)};for(let t=0;t<e.length;t++){const[o,a]=e[t],[r,l]=i[t];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=wl(o,a,s,n),v=wl(r,l,s,n);u(p,f,v),u(p,wl(o,a,s,1),wl(r,l,s,1)),f!==v&&u(d,f,v)}if(!h.length)return t.slice();const _=(t,e,i)=>[t[0]+(e[0]-t[0])*i,t[1]+(e[1]-t[1])*i],m=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1])<=c,g=(t,e)=>{if("fixed-topology"===o){const i=e.na[0]-e.oa[0],s=e.na[1]-e.oa[1],n=e.nb[0]-e.ob[0],o=e.nb[1]-e.ob[1];return Math.hypot(i-n,s-o)<=c?[t[0]+i,t[1]+s]:m(t,e.oa)?[...e.na]:m(t,e.ob)?[...e.nb]:[...t]}const i=Math.max(0,Math.min(1,((t[0]-e.oa[0])*e.dx+(t[1]-e.oa[1])*e.dy)/e.len2));return _(e.na,e.nb,i)},f=(t,e)=>{const[i,s]=yl(t,e);return(e[0]-t[0])*i+(e[1]-t[1])*s>=0?[[...t],[...e]]:[[...e],[...t]]},v=[],y=[],b=(t,e,i)=>{if(Math.hypot(e[0]-t[0],e[1]-t[1])<=c)return;const[n,o]=f(t,e),a=ul(i);if(y.some(t=>t.entry.cm===a&&m(t.span[0],n)&&m(t.span[1],o)))return;const l=xl(n,o,a,s,r);v.push(l),y.push({entry:l,span:[n,o]})};for(const e of t){const t=kl(e,r);if(t){const[i,s]=f(t[0],t[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({...e,cm:ul(e.cm)});continue}const p=[];for(const t of h){if(!Ml(Sl(i,s),Sl(t.oa,t.ob)))continue;const e=t=>Math.abs((t[0]-i[0])*a-(t[1]-i[1])*n)/d;if(e(t.oa)>l||e(t.ob)>l)continue;const o=((t.oa[0]-i[0])*n+(t.oa[1]-i[1])*a)/r,h=((t.ob[0]-i[0])*n+(t.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:t})}if(!p.length){"fixed-topology"===o?v.push({...e}):b(i,s,e.cm);continue}const u=[0,1,...p.flatMap(({lo:t,hi:e})=>[t,e])].sort((t,e)=>t-e).filter((t,e,i)=>0===e||Math.abs(t-i[e-1])*d>c),y=[];for(let t=0;t+1<u.length;t++){const e=u[t],n=u[t+1];if((n-e)*d<=c)continue;const o=_(i,s,e),a=_(i,s,n),r=(e+n)/2,l=p.filter(t=>r>=t.lo-1e-12&&r<=t.hi+1e-12);if(!l.length){y.push([o,a]);continue}const h=[g(o,l[0].move),g(a,l[0].move)],f=l.slice(1).some(t=>{const e=g(o,t.move),i=g(a,t.move);return!m(h[0],e)||!m(h[1],i)});y.push(f?[o,a]:h)}const w=(t,e)=>{const i=t[1][0]-t[0][0],s=t[1][1]-t[0][1],n=e[1][0]-e[0][0],o=e[1][1]-e[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 t of y){const e=k[k.length-1];e&&m(e[1],t[0])&&w(e,t)?e[1]=t[1]:k.push([[...t[0]],[...t[1]]])}if("fixed-topology"===o&&1===k.length){const[i,s]=k[0];if(m(i,t[0])&&m(s,t[1])||m(i,t[1])&&m(s,t[0])){v.push({...e});continue}}for(const[t,i]of k)b(t,i,e.cm);continue}if("fixed-topology"===o){const t=p.get(e.key);if(1===t?.size){const i=[...t][0];v.push(i===e.key?{...e}:{...e,key:i});continue}const i=[$l([e],r)[0]];1!==r&&i.push($l([e],1)[0]);const s=i.filter(Boolean).some(t=>h.some(e=>{if(!Ml(t.ang,Sl(e.oa,e.ob)))return!1;const i=((t.x-e.oa[0])*e.dx+(t.y-e.oa[1])*e.dy)/e.len2;return i>=-1e-6&&i<=1.000001&&pc(t.x,t.y,e.oa[0],e.oa[1],e.ob[0],e.ob[1])<=l}));((t?.size||0)>1||s)&&a?.(),v.push({...e});continue}let i="";const n=d.get(e.key);if(1===n?.size&&(i=[...n][0]),!i){const t=$l([e],r)[0];if(t){const e=new Set;for(const i of h){if(!Ml(t.ang,Sl(i.oa,i.ob)))continue;const n=((t.x-i.oa[0])*i.dx+(t.y-i.oa[1])*i.dy)/i.len2;if(n<-1e-6||n>1.000001)continue;if(pc(t.x,t.y,i.oa[0],i.oa[1],i.ob[0],i.ob[1])>l)continue;const o=g([t.x,t.y],i),[a,c]=yl(i.na,i.nb),h=Math.max(s*r,1e-6);e.add(wl([o[0]-a*h,o[1]-c*h],[o[0]+a*h,o[1]+c*h],s,r))}1===e.size&&(i=[...e][0])}}v.push({...e,key:i||e.key,cm:ul(e.cm)})}return v}function Il(t,e,i,s,n=1,o="affine"){return Rl(t,e,i,s,n,o)}function Al(t,e,i,s=1,n=[]){const o=s>0?s:1,a=Math.abs(i),r=Math.max(a*o*Gr,1e-9),l=(n||[]).flatMap(t=>{const e=kl(t,o);return e?e.map(t=>[...t]):[]}),c=t=>t.every((t,e)=>(t=>{if(!(a>0))return!0;const e=t/o/a;return Math.abs(e-Math.round(e))<Gr})(t)||l.some(i=>Math.abs(t-i[e])<=r)),h=[],d=t=>JSON.stringify([t.key,t.cm,t.a,t.b]);for(const i of t||[]){const t=kl(i,o);if(!t)continue;const[s,n]=t;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 t of e){const[e,i]=t;if(![e?.[0],e?.[1],i?.[0],i?.[1]].every(Number.isFinite))continue;const n=t=>Math.abs((t[0]-s[0])*_-(t[1]-s[1])*u);if(n(e)>r||n(i)>r)continue;const o=(e[0]-s[0])*u+(e[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((t,e)=>t[0]-e[0]||t[1]-e[1]);let g=0;for(const[t,e]of m){if(t>g+r)break;if(g=Math.max(g,e),g>=p-r)break}g<p-r&&h.push(d(i))}return h}function zl(t,e,i,s,n,o=1){const a=wl(e,i,n,o),r=(t||[]).filter(t=>t.key!==a);return null==s||s<1?r:[...r,xl(e,i,s,n,o)]}function Fl(t,e,i,s,n,o=[],a=1){let r=t?t.slice():[];for(const t of function(t,e,i,s,n=1,o=[]){const a=Zl(t,e,i,s,n,o);if(!a)return[];const r=[];for(let t=0;t<a.poly.length;t++){const e=a.poly[t],o=a.poly[(t+1)%a.poly.length];Wl(e,o,i,s,n)||r.push({a:e,b:o})}return r}(e,i,o,n,a,r))r=zl(r,t.a,t.b,s,n,a);return r}function El(t,e,i,s,n,o=[],a=1){if(null==s||s<1)return t?t.slice():[];const r=Zl(e,i,o,n,a,t);if(!r)return t?t.slice():[];const l=Ql(t,r,n,a);let c=t?t.slice():[];for(let t=0;t<r.poly.length;t++){const e=r.poly[t],i=r.poly[(t+1)%r.poly.length];Wl(e,i,o,n,a)||(l[t]>0||(c=zl(c,e,i,s,n,a)))}return c}function Nl(t){const{a:e,b:i,halfDepth:s}=t;if(!Array.isArray(e)||!Array.isArray(i)||e.length<2||i.length<2||![e[0],e[1],i[0],i[1]].every(Number.isFinite))return null;const n=i[0]-e[0],o=i[1]-e[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[[e[0]+r,e[1]+l],[i[0]+r,i[1]+l],[i[0]-r,i[1]-l],[e[0]-r,e[1]-l]]}function Ol(t,e,i){return Math.hypot(t[0]-e[0],t[1]-e[1])<=i}function Hl(t,e,i){const s=e.b[0]-e.a[0],n=e.b[1]-e.a[1],o=s*s+n*n;if(!(o>i*i))return!1;const a=((t[0]-e.a[0])*s+(t[1]-e.a[1])*n)/o;if(!(a>0&&a<1))return!1;const r=[e.a[0]+s*a,e.a[1]+n*a];return Math.hypot(t[0]-r[0],t[1]-r[1])<=i}function Ll(t,e,i,s){const n=Math.hypot(e,i);if(!(n>1e-9&&s>0))return;const o=[e/n,i/n],a=t.find(t=>Math.abs(t.u[0]*o[1]-t.u[1]*o[0])<1e-9&&t.u[0]*o[0]+t.u[1]*o[1]>1-1e-9);a?a.halfDepth=Math.max(a.halfDepth,s):t.push({u:o,halfDepth:s})}function Bl(t,e=1e-6){const i=(t||[]).filter(t=>t&&Array.isArray(t.a)&&Array.isArray(t.b)&&t.a.length>=2&&t.b.length>=2&&t.a.every(Number.isFinite)&&t.b.every(Number.isFinite)&&Number.isFinite(t.halfDepth)&&t.halfDepth>0&&Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])>1e-9);if(i.length<2)return[];const s=Math.max(Number.isFinite(e)?e:0,1e-9),n=i.flatMap(t=>[t.a,t.b]).map(t=>[t[0],t[1]]).sort((t,e)=>t[0]-e[0]||t[1]-e[1]),o=[];for(const t of n)o.some(e=>Ol(e,t,s))||o.push(t);const a=[];for(const t of o){const e=[];for(const n of i)Ol(t,n.a,s)?Ll(e,n.b[0]-n.a[0],n.b[1]-n.a[1],n.halfDepth):Ol(t,n.b,s)?Ll(e,n.a[0]-n.b[0],n.a[1]-n.b[1],n.halfDepth):Hl(t,n,s)&&(Ll(e,n.a[0]-t[0],n.a[1]-t[1],n.halfDepth),Ll(e,n.b[0]-t[0],n.b[1]-t[1],n.halfDepth));if(!(e.length<2)){e.sort((t,e)=>Math.atan2(t.u[1],t.u[0])-Math.atan2(e.u[1],e.u[0])||t.halfDepth-e.halfDepth);for(let i=0;i<e.length;i++)for(let n=i+1;n<e.length;n++){const o=e[i],r=e[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=[t[0]+c[0]*o.halfDepth*d,t[1]+c[1]*o.halfDepth*d],u=[t[0]-h[0]*r.halfDepth*d,t[1]-h[1]*r.halfDepth*d],_=Kl(p,o.u,u,r.u),m=4*Math.max(o.halfDepth,r.halfDepth),g=_&&Math.hypot(_[0]-t[0],_[1]-t[1])<=m?[t.slice(),p,_,u]:[t.slice(),p,u];Math.abs(Gl(g))>s*s&&a.push(g)}}}return a}function ql(t,e,i,s){if(!(e>0)||!t||t.length<2)return"";if(i&&t.length>=3){let i=t;const n=t[t.length-1];if(t.length>=4&&Math.hypot(t[0][0]-n[0],t[0][1]-n[1])<1e-9&&(i=t.slice(0,-1)),i.length>=3){const t=i.map((t,i)=>s?.[i]||e),n=zc(i,t),o=Yl(i,t);if(n&&o)return`${Jl(n)} ${Jl(Xl(o))}`}}const n=[];for(let i=0;i<t.length-1;i++){const o=t[i],a=t[i+1],r=s?.[i]||e;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(Nl).filter(t=>!!t),...Bl(n)],a=function(t){let e=null;try{for(const i of t){if(i.length<3)continue;const t=mc(i);e=e?si(e,t):[t]}return e}catch{return null}}(o);return a?Tc(a):o.map(t=>Jl(t)).join(" ")}function Wl(t,e,i,s,n=1){if(!i.length)return!1;const o=jl(s,n),a=(t[0]+e[0])/2,r=(t[1]+e[1])/2,[l,c]=yl(t,e);for(const t of i){const[e,i]=yl([t[0],t[1]],[t[2],t[3]]);if(!(Math.abs(l*i-c*e)>.05)&&pc(a,r,t[0],t[1],t[2],t[3])<=o)return!0}return!1}function jl(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function Gl(t){let e=0;for(let i=0;i<t.length;i++){const s=t[i],n=t[(i+1)%t.length];e+=s[0]*n[1]-n[0]*s[1]}return e/2}function Ul(t,e){const i=t[e],s=t[(e+1)%t.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(t,e){let i=!1;for(let s=0,n=e.length-1;s<e.length;n=s++){const o=e[s][0],a=e[s][1],r=e[n][0],l=e[n][1];a>t[1]!=l>t[1]&&t[0]<(r-o)*(t[1]-a)/(l-a+0)+o&&(i=!i)}return i}([c[0]+.001*r,c[1]+.001*l],t)||(r=-r,l=-l),[r,l]}function Vl(t,e){const i=t[0]*e[1]-t[1]*e[0],s=t[0]*e[0]+t[1]*e[1];return Math.abs(i)<1e-9&&s>0}function Kl(t,e,i,s){const n=e[0]*s[1]-e[1]*s[0];if(Math.abs(n)<1e-12)return null;const o=[i[0]-t[0],i[1]-t[1]],a=(o[0]*s[1]-o[1]*s[0])/n;return[t[0]+a*e[0],t[1]+a*e[1]]}function Yl(t,e,i){const s=t?.length||0;if(s<3||e.length!==s)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);const n=[];for(let o=0;o<s;o++){const a=(o-1+s)%s,r=t[a],l=t[o],c=t[o],h=t[(o+1)%s],d=Math.max(0,e[a]),p=Math.max(0,e[o]),[u,_]=Ul(t,a),[m,g]=Ul(t,o),f=[l[0]-r[0],l[1]-r[1]],v=[h[0]-c[0],h[1]-c[1]],y=Math.hypot(f[0],f[1])||1,b=Math.hypot(v[0],v[1])||1,w=[f[0]/y,f[1]/y],k=[v[0]/b,v[1]/b],x=[r[0]+u*d,r[1]+_*d],$=[c[0]+m*p,c[1]+g*p];if(!(d>0||p>0)){n.push([t[o][0],t[o][1]]);continue}if(d>0!=p>0){const e=t[o],i=d>0?[e[0]+u*d,e[1]+_*d]:[e[0],e[1]],s=p>0?[e[0]+m*p,e[1]+g*p]:[e[0],e[1]];n.push(i),Math.hypot(s[0]-i[0],s[1]-i[1])>1e-9&&n.push(s);continue}if(Vl(w,k)){const e=t[o],i=[e[0]+u*d,e[1]+_*d],s=[e[0]+m*p,e[1]+g*p];n.push(i),Math.hypot(s[0]-i[0],s[1]-i[1])>1e-9&&n.push(s);continue}const S=Kl(x,w,$,k),M=Math.max(d,p,1e-9),C=oc(i,t[o])?.limit??4*M;if(S){const e=Math.hypot(S[0]-t[o][0],S[1]-t[o][1]);if(Number.isFinite(e)&&e<=C){n.push(S);continue}}d>0&&n.push([t[o][0]+u*d,t[o][1]+_*d]),p>0&&n.push([t[o][0]+m*p,t[o][1]+g*p]),d>0||p>0||n.push([t[o][0],t[o][1]])}return n.length>=3?n:null}function Jl(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;e<t.length;e++)i+=` L ${t[e][0]} ${t[e][1]}`;return e&&(i+=" Z"),i}function Xl(t){return t.slice().reverse()}function Zl(t,e,i,s,n=1,o=[]){const a=(t||[]).find(t=>t?.id===e),r=hi(a);if(!r||r.length<3)return null;const l=jl(s,n),c=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=hi(i);if(t)for(const e of Cs(r,t,l))c.push([e[0],e[1]],[e[2],e[3]])}for(const t of i||[])c.push([t[0],t[1]],[t[2],t[3]]);for(const t of o||[]){const e=kl(t,n);e&&c.push(e[0],e[1])}const h=[],d=[];for(let t=0;t<r.length;t++){const e=r[t],i=r[(t+1)%r.length];h.push([e[0],e[1]]),d.push(t);const s=Math.hypot(i[0]-e[0],i[1]-e[1]);if(s<2*l||!c.length)continue;const n=Math.min(.499,2*l/s),o=[];for(const t of c){if(pc(t[0],t[1],e[0],e[1],i[0],i[1])>l)continue;const a=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);a<=n||a>=1-n||(o.some(t=>Math.abs(t-a)*s<=2*l)||o.push(a))}o.sort((t,e)=>t-e);for(const s of o)h.push([e[0]+(i[0]-e[0])*s,e[1]+(i[1]-e[1])*s]),d.push(t)}return{poly:h,parent:d,orig:r}}function Ql(t,e,i,s){const n=e.poly.length,o=new Array(n).fill(0);if(!t?.length)return o;const a=new Set,r=[];for(let l=0;l<n;l++){const c=Cl(t,e.poly[l],e.poly[(l+1)%n],i,s);c&&c.cm>0?(o[l]=ul(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=$l(t,l).filter(t=>t.w.cm>0);for(let t=r.length-1;t>=0;t--){const i=r[t],s=e.poly[i],a=e.poly[(i+1)%n],d=Sl(s,a);let p=null;for(const t of h){const e=kl(t.w,l);if(!e||!Ml(Sl(e[0],e[1]),d))continue;if(pc(s[0],s[1],e[0][0],e[0][1],e[1][0],e[1][1])>c||pc(a[0],a[1],e[0][0],e[0][1],e[1][0],e[1][1])>c)continue;const i=Math.hypot(a[0]-s[0],a[1]-s[1]),n=Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1]),o=Math.max(0,n-i);(!p||o<p.extra)&&(p={cm:ul(t.w.cm),extra:o})}p&&(o[i]=p.cm,r.splice(t,1))}const d=new Map;for(const t of r){const i=e.parent[t],s=d.get(i);s?s.push(t):d.set(i,[t])}for(const[t,i]of d){const s=e.orig[t],n=e.orig[(t+1)%e.orig.length],r=Sl(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 t of h){if(a.has(t.w.key))continue;if(!Ml(t.ang,r))continue;const e=kl(t.w,l);let i=!1,o=0;if(e){if(!Ml(Sl(e[0],e[1]),r))continue;if(pc(s[0],s[1],e[0][0],e[0][1],e[1][0],e[1][1])>c||pc(n[0],n[1],e[0][0],e[0][1],e[1][0],e[1][1])>c)continue;i=!0,o=Math.max(0,Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1])-_)}else{if(pc(t.x,t.y,s[0],s[1],n[0],n[1])>c)continue;o=Math.hypot(t.x-d,t.y-p)}(!u||i&&!u.exact||i===u.exact&&o<u.d)&&(u={cm:ul(t.w.cm),d:o,exact:i})}if(u)for(const t of i)o[t]=u.cm}return o}function tc(t,e){return[Math.floor(t[0]/e),Math.floor(t[1]/e)]}function ec(t,e){return`${t},${e}`}function ic(t,e,i){const[s,n]=tc(e,i),o=[];for(let e=-1;e<=1;e++)for(let i=-1;i<=1;i++){const a=t.get(ec(s+e,n+i));a&&o.push(...a)}return o}function sc(t,e=1e-6,i=1){const s=Math.max(Number.isFinite(e)?e:0,1e-9),n=Number.isFinite(i)&&i>0?i:1,o=(t||[]).filter(t=>t&&!t.open&&null!==t.kind&&Number.isFinite(t.half)&&t.half>0&&Array.isArray(t.a)&&Array.isArray(t.b)&&t.a.length>=2&&t.b.length>=2&&[t.a[0],t.a[1],t.b[0],t.b[1]].every(Number.isFinite)&&Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])>s).sort((t,e)=>t.key.localeCompare(e.key)||t.a[0]-e.a[0]||t.a[1]-e.a[1]||t.b[0]-e.b[0]||t.b[1]-e.b[1]||t.half-e.half),a=new Map;for(const t of o){const e=a.get(t.key);e?t.half>e.half&&a.set(t.key,{...e,half:t.half}):a.set(t.key,t)}const r=[...a.values()].flatMap(t=>[{point:[t.a[0],t.a[1]],other:t.b,halfDepth:t.half,kind:t.kind,key:t.key},{point:[t.b[0],t.b[1]],other:t.a,halfDepth:t.half,kind:t.kind,key:t.key}]).sort((t,e)=>t.point[0]-e.point[0]||t.point[1]-e.point[1]||t.other[0]-e.other[0]||t.other[1]-e.other[1]||t.halfDepth-e.halfDepth),l=new Map;for(const t of r){const[e,i]=tc(t.point,s),n=ec(e,i),o=l.get(n)||[];o.push(t),l.set(n,o)}const c=[],h=new Map;for(const t of r){const e=ic(h,t.point,s).filter(e=>Math.hypot(e.point[0]-t.point[0],e.point[1]-t.point[1])<=s).sort((e,i)=>Math.hypot(e.point[0]-t.point[0],e.point[1]-t.point[1])-Math.hypot(i.point[0]-t.point[0],i.point[1]-t.point[1])||e.point[0]-i.point[0]||e.point[1]-i.point[1]);let i=e[0];if(!i){i={point:[...t.point],rays:[]},c.push(i);const[e,n]=tc(i.point,s),o=ec(e,n),a=h.get(o)||[];a.push(i),h.set(o,a)}const n=t.other[0]-t.point[0],o=t.other[1]-t.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:t.halfDepth,length:a,angle:l})}const d=[],p=t=>{const e=1e-9*Math.max(1,n),i=t.filter(t=>Number.isFinite(t.halfDepth)&&t.halfDepth>0&&Number.isFinite(t.length)&&t.length>s);return i.filter((t,s)=>!i.some((i,n)=>n!==s&&i.halfDepth>=t.halfDepth-e&&i.length>=t.length-e&&(i.halfDepth>t.halfDepth+e||i.length>t.length+e||n<s))).sort((t,e)=>t.length-e.length||t.halfDepth-e.halfDepth).map(t=>({...t}))};for(const t of c){const e=t.rays.sort((t,e)=>t.angle-e.angle||t.length-e.length||t.halfDepth-e.halfDepth),i=[];for(const t of e){const e=i[i.length-1];e&&Math.abs(t.angle-e.angle)<=1e-9?e.supports.push({halfDepth:t.halfDepth,length:t.length}):i.push({u:[...t.u],angle:t.angle,supports:[{halfDepth:t.halfDepth,length:t.length}]})}if(i.length>1&&2*Math.PI-i[i.length-1].angle+i[0].angle<=1e-9){const t=i.pop();i[0].supports.push(...t.supports)}if(i.length<3)continue;const n=i.map(e=>{const i=p(e.supports),n=new Set,o=[];for(const a of i){const i=[t.point[0]+e.u[0]*a.length,t.point[1]+e.u[1]*a.length];for(const e of ic(l,i,s)){if("shared"!==e.kind||Math.hypot(e.point[0]-i[0],e.point[1]-i[1])>s)continue;const a=e.other[0]-e.point[0],r=e.other[1]-e.point[1],l=Math.hypot(a,r);if(!(l>s))continue;const c=a/l,h=r/l;if(Math.hypot(e.other[0]-t.point[0],e.other[1]-t.point[1])<=s)continue;const d=`${e.key}|${e.point[0]}|${e.point[1]}|${e.other[0]}|${e.other[1]}|${e.halfDepth}`;n.has(d)||(n.add(d),o.push({start:[e.point[0],e.point[1]],u:[c,h],length:l,halfDepth:e.halfDepth}))}}return o.sort((t,e)=>t.start[0]-e.start[0]||t.start[1]-e.start[1]||t.u[0]-e.u[0]||t.u[1]-e.u[1]||t.length-e.length||t.halfDepth-e.halfDepth),{u:[...e.u],halfDepth:Math.max(...i.map(t=>t.halfDepth)),length:Math.max(...i.map(t=>t.length)),supports:i,continuations:o}}).filter(t=>Number.isFinite(t.halfDepth)&&t.halfDepth>0&&Number.isFinite(t.length)&&t.length>s);if(n.length<3)continue;const o=Math.max(...n.map(t=>t.halfDepth));o>0&&Number.isFinite(o)&&d.push({point:[...t.point],rays:n,halfDepth:o,limit:1.25*o})}d.sort((t,e)=>t.point[0]-e.point[0]||t.point[1]-e.point[1]);const u=new Map;for(const t of d){const[e,i]=tc(t.point,s),n=ec(e,i),o=u.get(n)||[];o.push(t),u.set(n,o)}return{epsilon:s,coordinateScale:n,nodes:d,index:u}}function nc(t){const e={fans:[],supports:[]};if(!t?.nodes?.length)return e;const i=Math.max(t.epsilon,1e-9)**2;for(const s of t.nodes){const t=s.rays.filter(t=>Number.isFinite(t.halfDepth)&&t.halfDepth>0).map(t=>({...t,thickLength:Math.max(...t.supports.filter(e=>e.halfDepth>=t.halfDepth-1e-12).map(t=>t.length),0),angle:(()=>{const e=Math.atan2(t.u[1],t.u[0]);return e<0?e+2*Math.PI:e})()})).sort((t,e)=>t.angle-e.angle);if(t.length<2)continue;const n=s.point;for(const i of t)for(const t of i.supports){if(!(t.halfDepth>0&&t.length>0))continue;const s=-i.u[1]*t.halfDepth,o=i.u[0]*t.halfDepth,a=[n[0]+i.u[0]*t.length,n[1]+i.u[1]*t.length];e.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<t.length;s++){const o=t[s],a=t[(s+1)%t.length],r=(()=>{const t=a.angle-o.angle;return t>0?t:t+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=t=>{let e=Math.atan2(t[1]-n[1],t[0]-n[0])-o.angle;for(;e<0;)e+=2*Math.PI;return e<=r+1e-9};let _=null;if(Math.abs(p)>1e-9){const t=((d[0]-h[0])*a.u[1]-(d[1]-h[1])*a.u[0])/p,e=((d[0]-h[0])*o.u[1]-(d[1]-h[1])*o.u[0])/p,i=[h[0]+o.u[0]*t,h[1]+o.u[1]*t];(l?t<=1e-9&&e<=1e-9:t>1e-9&&t<=o.thickLength&&e<=a.thickLength)&&Math.hypot(i[0]-n[0],i[1]-n[1])<=c&&u(i)&&(_=i)}const m=t=>{Math.abs(Gl(t))>i&&e.fans.push(t)};if(_){m([[n[0],n[1]],h,_,d]);continue}if(l){m([[n[0],n[1]],h,d]);continue}const g=(t,e)=>Math.min(e,Math.sqrt(Math.max(c**2-t**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 e}function oc(t,e){return!t||!Array.isArray(e)||e.length<2||!e.slice(0,2).every(Number.isFinite)?null:ic(t.index,e,t.epsilon).filter(i=>Math.hypot(i.point[0]-e[0],i.point[1]-e[1])<=t.epsilon).sort((t,i)=>Math.hypot(t.point[0]-e[0],t.point[1]-e[1])-Math.hypot(i.point[0]-e[0],i.point[1]-e[1])||t.point[0]-i.point[0]||t.point[1]-i.point[1])[0]||null}function ac(t,e,i,s,n,o,a){return sc(lc(t,e,i,s,n,o,a),4*jl(s,a),a)}function rc(t,e,i,s,n,o,a,r=1){const l=Zl(t,e,s,n,r,i);if(!l)return null;const c=function(t,e,i){const s=(t||[]).find(t=>t?.id===e),n=hi(s);if(!n)return[];const o=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=hi(s);if(t)for(const e of Cs(n,t,i))o.push(e)}return o}(t,e,jl(n,r)),h=function(t,e,i,s,n){const o=jl(s,n),a=[];for(let r=0;r<t.length;r++){const l=t[r],c=t[(r+1)%t.length];if(Wl(l,c,i,s,n)){a.push(null);continue}const h=(l[0]+c[0])/2,d=(l[1]+c[1])/2,p=e.some(t=>pc(h,d,t[0],t[1],t[2],t[3])<=o);a.push(p?"shared":"outer")}return a}(l.poly,c,s,n,r),d=Ql(i,l,n,r),p=d.map((t,e)=>h[e]&&t>0?ml(t,o,a)/2:0);return{...l,kinds:h,cms:d,offsets:p}}function lc(t,e,i,s,n,o,a=1){const r=[];for(const l of t||[]){if(!l?.id)continue;const c=rc(t,l.id,e,i,s,n,o,a);if(c)for(let t=0;t<c.poly.length;t++){const e=c.poly[t],i=c.poly[(t+1)%c.poly.length];r.push({roomId:l.id,a:[e[0],e[1]],b:[i[0],i[1]],key:wl(e,i,s,a),kind:c.kinds[t],cm:c.kinds[t]?c.cms[t]:0,open:null===c.kinds[t],half:c.offsets[t]})}}return r}function cc(t,e,i,s,n,o,a=1){let r=[];const l=lc(t,e,i,s,n,o,a);for(const t of l)!t.open&&t.cm>0&&(r=zl(r,t.a,t.b,t.cm,s,a));return r}function hc(t,e,i,s,n,o,a=1){if(!e?.length)return[];const r=lc(t,e,i,s,n,o,a),l=new Map;for(const t of r){if(t.open||!t.kind||!t.roomId)continue;const e=l.get(t.key)||new Set;e.add(t.roomId),l.set(t.key,e)}const c=t=>{const e=[...l.get(t)||[]].sort();return 1!==e.length&&2!==e.length?`ambiguous:${t}`:`${1===e.length?"outer":"shared"}:${e.join("|")}`},h=[],d=new Set;for(const t of r)!t.open&&t.cm>0&&!d.has(t.key)&&(d.add(t.key),h.push({...t,ownerSignature:c(t.key)}));const p=[];for(const r of t||[]){if(!r?.id)continue;const l=rc(t,r.id,e,i,s,n,o,a);if(l)for(let t=0;t<l.orig.length;t++){const e=[];for(let i=0;i<l.parent.length;i++)l.parent[i]===t&&e.push(i);if(e.length)for(let t=0;t<e.length;){const i=e[t],n=l.cms[i];if(!(n>0)||null===l.kinds[i]){t++;continue}const o=c(wl(l.poly[i],l.poly[(i+1)%l.poly.length],s,a));let r=t;for(;r+1<e.length;){const t=e[r+1],i=wl(l.poly[t],l.poly[(t+1)%l.poly.length],s,a);if(null===l.kinds[t]||l.cms[t]!==n||c(i)!==o)break;r++}const h=e[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:wl(d,u,s,a),cm:n,len:_,ownerSignature:o}),t=r+1}}}p.sort((t,e)=>e.len-t.len||t.key.localeCompare(e.key));const u=[],_=new Set,m=new Set,g=4*jl(s,a);for(const t of p){const e=h.filter(e=>!m.has(e.key)&&e.cm===t.cm&&e.ownerSignature===t.ownerSignature&&Ml(Sl(e.a,e.b),Sl(t.a,t.b))&&pc(e.a[0],e.a[1],t.a[0],t.a[1],t.b[0],t.b[1])<=g&&pc(e.b[0],e.b[1],t.a[0],t.a[1],t.b[0],t.b[1])<=g);if(e.length){for(const t of e)m.add(t.key);_.has(t.key)||(_.add(t.key),u.push(xl(t.a,t.b,t.cm,s,a)))}}for(const t of h)m.has(t.key)||_.has(t.key)||(_.add(t.key),u.push(xl(t.a,t.b,t.cm,s,a)));return u}function dc(t,e,i,s,n,o,a,r=1){const l=jl(n,r),c=(s[0]+s[2])/2,h=(s[1]+s[3])/2,d=Sl([s[0],s[1]],[s[2],s[3]]);let p=null;for(const s of lc(t,e,i,n,o,a,r)){if(!Ml(Sl(s.a,s.b),d))continue;const t=pc(c,h,s.a[0],s.a[1],s.b[0],s.b[1]);t>4*l||(!p||t<p.d)&&(p={cm:s.cm,d:t})}return p?.cm||0}function pc(t,e,i,s,n,o){const a=n-i,r=o-s,l=a*a+r*r;if(l<1e-18)return Math.hypot(t-i,e-s);let c=((t-i)*a+(e-s)*r)/l;return c=Math.max(0,Math.min(1,c)),Math.hypot(t-(i+a*c),e-(s+r*c))}function uc(t){let e=null,i=0;for(const s of t||[]){const t=s?.[0];if(!Array.isArray(t)||t.length<4)continue;const n=t.slice(0,-1).map(t=>[t[0],t[1]]),o=Math.abs(Gl(n));n.length>=3&&o>i&&(e=n,i=o)}return e}function _c(t,e,i,s,n,o,a,r=1,l){const c=(t||[]).find(t=>t?.id===e),h=hi(c);if(!h||h.length<3)return null;if(!i?.length)return h.map(t=>[t[0],t[1]]);const d=rc(t,e,i,s,n,o,a,r);if(!d||!d.offsets.some(t=>t>0))return h.map(t=>[t[0],t[1]]);const p=ac(t,i,s,n,o,a,r),u=Yl(d.poly,d.offsets,p);if(!u)return h.map(t=>[t[0],t[1]]);if(!p.nodes.length)return u;const _=void 0===l?Rc(t,i,s,[],n,o,a,r):null,m=l??("ok"===_?.status||"degraded-extra"===_?.status?_.roomGeom:void 0);if(m)try{const t=uc(oi(mc(d.poly),m));if(t)return t}catch{}return function(t,e){try{return uc(ni(mc(t),mc(e)))}catch{return null}}(u,d.poly)||h.map(t=>[t[0],t[1]])}function mc(t){const e=t.map(t=>[t[0],t[1]]);return e.push([t[0][0],t[0][1]]),[e]}function gc(t){return!(!Array.isArray(t)||!t.length)&&t.every(t=>Array.isArray(t)&&t.length&&t.every(t=>Array.isArray(t)&&t.length>=4&&t.every(t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])))&&Math.abs(Ti(t[0]))>1e-9)}function fc(t,e,i,s){const n=s?new Set(s):null;let o=null;for(let s=0;s<t.rays.length;s++){if(n&&!n.has(s))continue;const a=t.rays[s],r=[-a.u[1],a.u[0]];for(const s of a.supports){const n=Math.min(i,s.length);if(!(n>e.epsilon))continue;const l=kc([[t.point[0]+r[0]*s.halfDepth,t.point[1]+r[1]*s.halfDepth],[t.point[0]+a.u[0]*n+r[0]*s.halfDepth,t.point[1]+a.u[1]*n+r[1]*s.halfDepth],[t.point[0]+a.u[0]*n-r[0]*s.halfDepth,t.point[1]+a.u[1]*n-r[1]*s.halfDepth],[t.point[0]-r[0]*s.halfDepth,t.point[1]-r[1]*s.halfDepth]],e.coordinateScale);if(!l)continue;const c=mc(l);o=o?si(o,c):c}}return o}function vc(t,e,i){let s=null;for(const n of t.rays)for(const t of n.continuations){const n=[-t.u[1],t.u[0]],o=[t.start[0]+t.u[0]*t.length,t.start[1]+t.u[1]*t.length],a=kc([[t.start[0]+n[0]*t.halfDepth,t.start[1]+n[1]*t.halfDepth],[o[0]+n[0]*t.halfDepth,o[1]+n[1]*t.halfDepth],[o[0]-n[0]*t.halfDepth,o[1]-n[1]*t.halfDepth],[t.start[0]-n[0]*t.halfDepth,t.start[1]-n[1]*t.halfDepth]],e.coordinateScale);if(!a)continue;const r=ni(mc(a),i);Array.isArray(r)&&0!==r.length&&(s=s?si(s,r):r)}return s}function yc(t,e,i=2*(4*t.halfDepth+2*e.epsilon)){const s=function(t,e=cl){const i=new Set,s=Number.isFinite(e)&&e>=0?e:cl;for(let e=0;e<t.rays.length;e++)for(let n=e+1;n<t.rays.length;n++){const o=t.rays[e].u,a=t.rays[n].u;Math.abs(o[0]*a[0]+o[1]*a[1])<=s&&(i.add(e),i.add(n))}return[...i].sort((t,e)=>t-e)}(t);return s.length?fc(t,e,i,s):null}function bc(t,e,i,s,n){const o=function(t){let e=null;for(const i of t){const t=mc(i);try{e=e?si(e,t):t}catch{}}return e}(function(t,e,i=!1){if(!t)return[];const s=[];for(const n of t.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=Kl(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=e?Math.sqrt(Math.max(0,n.limit*n.limit-a.halfDepth*a.halfDepth)):0,v=e?Math.sqrt(Math.max(0,n.limit*n.limit-r.halfDepth*r.halfDepth)):0,y=kc([[u[0]+a.u[0]*f,u[1]+a.u[1]*f],[_[0]+r.u[0]*v,_[1]+r.u[1]*v],m],t.coordinateScale);if(y&&s.push(y),i){const e=m[0]-n.point[0],i=m[1]-n.point[1],o=Math.hypot(e,i),a=g-n.limit;if(o>t.epsilon&&a>t.epsilon){const r=Math.min(Math.max(8*t.epsilon,.05*n.halfDepth),.25*a),l=e/o,c=i/o,h=-c,d=l,p=kc([[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]],t.coordinateScale);p&&s.push(p)}}}return s}({...e,nodes:[t]},i,s));return o&&n?oi(o,n):o}function wc(t,e,i,s){if(!t||!e.nodes.length)return t;let n=null;try{n=function(t){let e=null;for(const i of t.nodes){const s=yc(i,t);s&&(e=e?si(e,s):s)}return e}(e)}catch{return t}let o=t;for(const t of e.nodes){const a=4*t.halfDepth+2*e.epsilon,r=2*a,l=[[t.point[0]-a,t.point[1]-a],[t.point[0]+a,t.point[1]-a],[t.point[0]+a,t.point[1]+a],[t.point[0]-a,t.point[1]+a]];try{let a=o;const c=bc(t,e,!1,!0,n);c&&(a=oi(a,c));let h=fc(t,e,r);const d=bc(t,e,!0,!0,n);d&&(h=oi(h,d)),n&&(h=si(h,n));const p=.02*Math.min(...t.rays.map(t=>t.halfDepth));if(h=si(h,mc([[t.point[0]-p,t.point[1]-p],[t.point[0]+p,t.point[1]-p],[t.point[0]+p,t.point[1]+p],[t.point[0]-p,t.point[1]+p]])),!h)continue;let u=ni(h,mc(l));s?u=ni(u,s):i&&(u=ni(u,i));const _=mc(l),m=oi(a,_),g=i?oi(ni(a,_),i):null,f=vc(t,e,_);o=si(m,...g?[g]:[],...f?[f]:[],u)}catch{}}if(n)try{let t=n;s?t=ni(t,s):i&&(t=ni(t,i)),o=si(o,t)}catch{}return o}function kc(t,e=1){if(!Array.isArray(t)||t.length<3)return null;const i=Number.isFinite(e)&&e>0?e:1,s=1e-12*Math.max(1,i),n=[];for(const e of t){if(!Array.isArray(e)||e.length<2)return null;const t=Number(e[0]),i=Number(e[1]);if(!Number.isFinite(t)||!Number.isFinite(i))return null;const o=Math.round(t/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(Gl(n))>s*s?n:null}function xc(t,e,i,s){if(pc(t[0],t[1],e[0],e[1],i[0],i[1])>s)return!1;const n=i[0]-e[0],o=i[1]-e[1],a=(t[0]-e[0])*n+(t[1]-e[1])*o,r=n*n+o*o,l=s*Math.sqrt(r);return a>=-l&&a<=r+l}function $c(t,e,i){const s=[],n=[];for(let o=0;o<t.length;o++){const a=t[o],r=t[(o+1)%t.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 t of e)for(const e of[t.a,t.b]){if(!xc(e,a,r,i))continue;const t=((e[0]-a[0])*l+(e[1]-a[1])*c)/h;t>d&&t<1-d&&p.push(t)}p.sort((t,e)=>t-e);const u=p.filter((t,e)=>0===e||Math.abs(t-p[e-1])>d);for(let t=0;t<u.length-1;t++){const o=u[t],r=u[t+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 t of e)xc(d,t.a,t.b,i)&&(p=Math.max(p,t.half));s.push(h),n.push(p)}}return s.length>=3&&n.length===s.length?{poly:s,offsets:n}:null}function Sc(t,e,i,s,n,o,a,r){const l=(t||[]).map(hi).filter(t=>!!t&&t.length>=3);if(!l.length)return null;let c=si(mc(l[0]));for(let t=1;t<l.length;t++)c=si(c,mc(l[t]));const h=lc(t,e,i,s,n,o,a),d=h.filter(t=>"outer"===t.kind&&t.half>0),p=4*jl(s,a),u=r||sc(h,p,a);let _=null;for(const t of function(t){const e=[];for(const i of Array.isArray(t)?t:[])if(Array.isArray(i))for(const t of i){if(!Array.isArray(t)||t.length<4)continue;const i=t.slice(0,-1).map(t=>[t[0],t[1]]);i.length>=3&&e.push(i)}return e}(c)){const e=$c(t,d,p);if(!e||!e.offsets.some(t=>t>0))continue;const i=zc(e.poly,e.offsets,u),s=Yl(e.poly,e.offsets,u);if(!i||!s)continue;const n=oi(mc(i),mc(s));_=_?si(_,n):n}return{centre:c,shell:_||[]}}function Mc(t,e,i,s,n,o,a=1){try{const r=ac(t,e,i,s,n,o,a),l=Sc(t,e,i,s,n,o,a,r);if(!l)return[];const c=l.shell?.length?si(l.centre,l.shell):l.centre;return r.nodes.length?Pc(c,r,Dc(t,e,i,s,n,o,a,r)):c}catch{return null}}function Cc(t,e){if(!Array.isArray(t))return t;return t.map(t=>{if(!Array.isArray(t)||!t.length)return t;const[i,...s]=t;return Math.abs(Gl(i||[]))<=e?null:[i,...s.filter(t=>Math.abs(Gl(t||[]))>e)]}).filter(t=>!!t)}function Dc(t,e,i,s,n,o,a,r){try{const l=Sc(t,e,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}}function Pc(t,e,i){const s=nc(e);let n=t;for(const t of[...s.supports,...s.fans])try{let e=[mc(t)];i&&(e=ni(e,i)),e?.length&&(n=si(n,e))}catch{}return Cc(n,Math.max(e.epsilon,1e-9)**2)}function Tc(t){if(!t)return"";let e="";for(const i of t)if(Array.isArray(i))for(const t of i){if(!Array.isArray(t)||t.length<4)continue;const i=t.slice(0,t.length-1);i.length<3||(e+=(e?" ":"")+Jl(i.map(t=>[t[0],t[1]])))}return e}function Rc(t,e,i,s=[],n,o,a,r=1,l=[],c={}){if(!e?.length&&!l.length)return{status:"not-applicable",geom:[],components:[],roomGeom:[],paperGeom:[],depthUnits:0,openingIndex:null,degradedExtraCount:0};const h=[],d=ac(t,e,i,n,o,a,r);let p=0;for(const s of t||[]){if(!s?.id)continue;const l=rc(t,s.id,e,i,n,o,a,r);if(!l||l.poly.length<3||!l.offsets.some(t=>t>0))continue;for(const t of l.offsets)t>0&&(p=Math.max(p,2*t));const c=zc(l.poly,l.offsets,d),u=Yl(l.poly,l.offsets,d);c&&h.push({outset:c,inset:u})}for(const t of l){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);if(e.length){const s=Math.min(Math.max(...e)-Math.min(...e),Math.max(...i)-Math.min(...i)),n=Math.min(...t.map((e,i)=>{const s=t[(i+1)%t.length];return Math.hypot(s[0]-e[0],s[1]-e[1])}));p=Math.max(p,t.length>16?s:n)}}const u=function(t,e,i,s,n,o,a,r){if(!e?.length||!i?.length)return[];const l=4*jl(s,a),c=new Map;for(const r of lc(t,e,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||sc(h,l),p=[];for(const t of i)for(const e of[[t[0],t[1]],[t[2],t[3]]])p.some(t=>Math.hypot(t[0]-e[0],t[1]-e[1])<=l)||p.push(e);const u=[],_=(t,e)=>{let i=0,s=0;if(Math.hypot(t.a[0]-e[0],t.a[1]-e[1])<=l)i=t.b[0]-t.a[0],s=t.b[1]-t.a[1];else{if(!(Math.hypot(t.b[0]-e[0],t.b[1]-e[1])<=l))return null;i=t.a[0]-t.b[0],s=t.a[1]-t.b[1]}const n=Math.hypot(i,s);return n>l?[i/n,s/n]:null};for(const t of p){const e=h.map(e=>({iv:e,u:_(e,t)})).filter(t=>!!t.u);for(let i=0;i<e.length;i++)for(let s=i+1;s<e.length;s++){const n=e[i],o=e[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=[t[0]-n.u[0]*c,t[1]-n.u[1]*c],_=[t[0]-o.u[0]*h,t[1]-o.u[1]*h],m=[p[0]+_[0]-t[0],p[1]+_[1]-t[1]],g=Math.max(n.iv.half,o.iv.half,1e-9),f=oc(d,t),v=f?.limit??4*g;let y;if(Math.hypot(m[0]-t[0],m[1]-t[1])<=v)y=a>0?[t.slice(),p,m,_]:[t.slice(),_,m,p];else{if(!f)continue;{const e=[-n.u[1],n.u[0]],i=[-o.u[1],o.u[0]],s=a<0?1:-1,r=[t[0]+e[0]*n.iv.half*s,t[1]+e[1]*n.iv.half*s],l=[t[0]-i[0]*o.iv.half*s,t[1]-i[1]*o.iv.half*s];y=a>0?[t.slice(),r,l]:[t.slice(),l,r]}}Math.abs(Gl(y))>l*l&&u.push(y)}}return u}(t,e,i,n,o,a,r,d),_=s.length?Fc(t,e,i,n,o,a,r):null;let m="exterior";try{const g=Sc(t,e,i,n,o,a,r,d);m="paper";const f=g?g.shell?.length?si(g.centre,g.shell):g.centre:[],v=d.nodes.length?Pc(f,d,Dc(t,e,i,n,o,a,r,d)):f,y=t=>{const e=mc(t.outset);return t.inset?oi(e,mc(t.inset)):e};m="room-rings";let b=null;for(const t of h)try{const e=y(t);b=b?si(b,e):e}catch{}if(m="edge-bodies",g)for(const s of Ac(t,e,i,n,o,a,r))try{const t=ni(mc(s.quad),g.centre);b=b?si(b,t):t}catch{}m="junctions",b=function(t,e,i=1,s=si){let n=t;for(const t of e||[]){const e=kc(t,i);if(e)try{const t=mc(e);n=n?s(n,t):t}catch{}}return n}(b,u,r),m="facade-clip",b&&g&&(b=ni(b,g.centre)),m="exterior-shell";const w=[];let k=0;if(g?.shell?.length)if(b)try{const t=si(b,g.shell);if(!gc(t))throw new Error("invalid shell union");b=t}catch{if(!gc(b)||!gc(g.shell))throw new Error("invalid shell");w.push({id:"exterior-shell",geom:g.shell}),k++}else b=g.shell;if(m="multi-wall-trim",b&&d.nodes.length){const t=t=>t.rays.some(t=>t.supports.some(t=>t.length<2*t.halfDepth)),e=d.nodes.filter(t);if(e.length){b=wc(b,{...d,nodes:e},g?.centre,v)}}if(m="junction-corners",d.nodes.length){const s=nc(d),l=Dc(t,e,i,n,o,a,r,d);for(const t of[...s.supports,...s.fans])try{let e=[mc(t)];if(l&&(e=ni(e,l)),!e?.length)continue;b=b?si(b,e):e}catch{}b=Cc(b,Math.max(d.epsilon,1e-9)**2)}const x=b||[];m="openings";for(const t of s){if(!(t.length>0))continue;const e=Oc(_,t,!0);if(!e.negative&&!e.positive)continue;const i=t.angle*Math.PI/180,s=Math.cos(i),o=Math.sin(i),a=-o,l=s,c=t.length/2,h=1.25*Math.max(p,n*r),d=[[t.x-s*c-a*h,t.y-o*c-l*h],[t.x+s*c-a*h,t.y+o*c-l*h],[t.x+s*c+a*h,t.y+o*c+l*h],[t.x-s*c+a*h,t.y-o*c+l*h]];b&&(b=oi(b,mc(d)));for(const t of w)t.geom=oi(t.geom,mc(d))}m="extras";const $=[];let S=0;const M=c.mergeExtra||((t,e)=>t?si(t,e):e);for(let t=0;t<l.length;t++){const e=l[t];if(e.length<3||!e.every(t=>t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]))||Math.abs(Ti(e))<=1e-9){S++;continue}const i=[mc(e)];try{const e=M(b,i,t);if(!gc(e))throw new Error("invalid extra union");b=e}catch{S++,gc(i)&&$.push({id:`extra-${t}`,geom:i})}}$.push(...w),$.sort((t,e)=>Tc(t.geom).localeCompare(Tc(e.geom)));const C=b||[];return{status:S||k?"degraded-extra":"ok",geom:C,components:[...gc(C)?[{id:"primary",geom:C}]:[],...$.map((t,e)=>({...t,id:`isolated-${e}`}))],roomGeom:x,paperGeom:v,depthUnits:p,openingIndex:_,degradedExtraCount:S+k}}catch{return c.onCoreFailure?.(m),{status:"failed-core",geom:[],components:[],roomGeom:[],paperGeom:[],depthUnits:p,openingIndex:null,degradedExtraCount:0}}}function Ic(t,e,i,s=[],n,o,a,r=1,l=[],c={}){if(!e?.length&&!l.length)return null;const h=Rc(t,e,i,s,n,o,a,r,l,c);if("failed-core"===h.status||"not-applicable"===h.status)return null;const d=h.components.map(t=>({id:t.id,d:Tc(t.geom),fillRule:"evenodd"})).filter(t=>!!t.d),p=d[0]?.d||"",u=Tc(h.paperGeom);return d.length?{status:h.status,d:p,paths:d,components:h.components,roomGeom:h.roomGeom,paperD:u,depthUnits:h.depthUnits,fillRule:"evenodd"}:null}function Ac(t,e,i,s,n,o,a=1){if(!e?.length)return[];const r=new Set,l=[];for(const c of t||[]){if(!c?.id)continue;const h=rc(t,c.id,e,i,s,n,o,a);if(!h)continue;const d=h.poly;for(let t=0;t<d.length;t++){const e=d[t],i=d[(t+1)%d.length],c=h.kinds[t];if(!c)continue;const p=h.cms[t];if(!(p>0))continue;const u=wl(e,i,s,a);if(r.has(u))continue;r.add(u);const _=ml(p,n,o),[m,g]=Ul(d,t),f=-m,v=-g,y=_/2,b=[[e[0]+f*y,e[1]+v*y],[i[0]+f*y,i[1]+v*y],[i[0]+m*y,i[1]+g*y],[e[0]+m*y,e[1]+g*y]];l.push({key:u,kind:c,cm:p,quad:b,a:[e[0],e[1]],b:[i[0],i[1]],depthUnits:_})}}return l}function zc(t,e,i){const s=t?.length||0;if(s<3||e.length!==s)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);Xl(t),e.slice().reverse();const n=[];for(let o=0;o<s;o++){const a=(o-1+s)%s,r=Math.max(0,e[a]),l=Math.max(0,e[o]),[c,h]=Ul(t,a),[d,p]=Ul(t,o),u=t[a],_=t[o],m=t[o],g=t[(o+1)%s],f=[_[0]-u[0],_[1]-u[1]],v=[g[0]-m[0],g[1]-m[1]],y=Math.hypot(f[0],f[1])||1,b=Math.hypot(v[0],v[1])||1,w=[f[0]/y,f[1]/y],k=[v[0]/b,v[1]/b],x=[u[0]-c*r,u[1]-h*r],$=[m[0]-d*l,m[1]-p*l];if(!(r>0||l>0)){n.push([t[o][0],t[o][1]]);continue}if(r>0!=l>0){const e=t[o],i=r>0?[e[0]-c*r,e[1]-h*r]:[e[0],e[1]],s=l>0?[e[0]-d*l,e[1]-p*l]:[e[0],e[1]];n.push(i),Math.hypot(s[0]-i[0],s[1]-i[1])>1e-9&&n.push(s);continue}if(Vl(w,k)){const e=t[o],i=[e[0]-c*r,e[1]-h*r],s=[e[0]-d*l,e[1]-p*l];n.push(i),Math.hypot(s[0]-i[0],s[1]-i[1])>1e-9&&n.push(s);continue}const S=Kl(x,w,$,k),M=Math.max(r,l,1e-9),C=oc(i,t[o])?.limit??4*M;if(S){const e=Math.hypot(S[0]-t[o][0],S[1]-t[o][1]);if(Number.isFinite(e)&&e<=C){n.push(S);continue}}r>0&&n.push([t[o][0]-c*r,t[o][1]-h*r]),l>0&&n.push([t[o][0]-d*l,t[o][1]-p*l])}return n.length>=3?n:null}function Fc(t,e,i,s,n,o,a=1){const r=[];for(const l of t||[]){if(!l?.id)continue;const c=rc(t,l.id,e,i,s,n,o,a);if(!c)continue;const h=Math.abs(Ti(c.poly));for(let t=0;t<c.poly.length;t++){if(!c.kinds[t])continue;const e=c.poly[t],i=c.poly[(t+1)%c.poly.length];r.push({roomId:l.id,a:e,b:i,inward:Ul(c.poly,t),cm:c.cms[t],half:c.offsets[t],area:h,key:wl(e,i,s,a)})}}return{edges:r,adjacencyEps:jl(s,a)}}function Ec(t,e,i,s){const n=t.map(t=>[Math.max(e,t.x0),Math.min(i,t.x1)]).filter(t=>t[1]-t[0]>s).sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(!n.length)return{coverage:0,full:!1};let o=n[0][0],a=n[0][1],r=0,l=o<=e+s;for(let t=1;t<n.length;t++){const[e,i]=n[t];e<=a+s?a=Math.max(a,i):(r+=a-o,l=!1,o=e,a=i)}return r+=a-o,l=l&&a>=i-s,{coverage:r,full:l}}function Nc(t,e){return Number(e.full)-Number(t.full)||t.faceDistance-e.faceDistance||t.area-e.area||t.roomId.localeCompare(e.roomId)}function Oc(t,e,i=!1){const s=Number(e?.x),n=Number(e?.y),o=Number(e?.angle),a=Number(e?.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,t.adjacencyEps),_=new Map;let m=0;for(const e of t.edges){if(i&&!(e.half>0))continue;if(!Tl(e.a,e.b,o))continue;const[t,a]=yl(e.a,e.b);if(Math.abs((s-e.a[0])*a-(n-e.a[1])*t)>u)continue;const r=(e.a[0]-s)*l+(e.a[1]-n)*c,g=(e.b[0]-s)*l+(e.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 y=e.inward[0]*h+e.inward[1]*d>=0?1:-1,b=((e.a[0]+e.b[0])/2-s)*h+((e.a[1]+e.b[1])/2-n)*d,w=Math.abs(b+y*e.half),k=`${y}|${e.roomId}`,x={x0:f,x1:v,half:e.half,cm:e.cm,key:e.key,axis:[t,a]},$=_.get(k);$?($.pieces.push(x),$.faceDistance=Math.min($.faceDistance,w)):_.set(k,{roomId:e.roomId,side:y,order:m++,pieces:[x],faceDistance:w,area:e.area,coverage:0,full:!1})}for(const t of _.values()){const e=Ec(t.pieces,-p,p,u);t.coverage=e.coverage,t.full=e.full}const g=t=>{const e=[..._.values()].filter(e=>e.side===t&&e.coverage>u);return e.sort(Nc),e[0]||null};return{negative:g(-1),positive:g(1)}}function Hc(t,e){const i=Oc(t,e),s=[i.negative,i.positive].filter(t=>!!t);if(!s.length)return{ox:0,oy:0,cm:0,side:-1};const n=i.negative&&i.positive?-1:s[0].side,o=e.flip_v?-n:n,a=(-1===o?i.negative:i.positive)||s[0],r=[...a.pieces].sort((t,e)=>(t.x0<=0&&t.x1>=0?0:Math.min(Math.abs(t.x0),Math.abs(t.x1)))-(e.x0<=0&&e.x1>=0?0:Math.min(Math.abs(e.x0),Math.abs(e.x1)))||e.x1-e.x0-(t.x1-t.x0)||t.key.localeCompare(e.key))[0];if(!(r.half>0&&r.cm>0))return{ox:0,oy:0,cm:0,side:o};const l=e.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 Lc(t,e){const i=1e-9,s=e.filter(t=>Number.isFinite(t.x0)&&Number.isFinite(t.x1)&&Number.isFinite(t.half)&&t.x1>t.x0&&t.half>0);if(!s.length)return"";const n=s.flatMap(t=>[t.x0,t.x1]).sort((t,e)=>t-e),o=[];for(const t of n){const e=o[o.length-1];(void 0===e||t>e+i)&&o.push(t)}const a=[];for(let t=0;t+1<o.length;t++){const e=o[t],n=o[t+1];if(!(n>e+i))continue;const r=(e+n)/2,l=s.reduce((t,e)=>r>=e.x0-i&&r<=e.x1+i?Math.max(t,e.half):t,0);if(!(l>0))continue;const c=a[a.length-1];c&&e<=c.x1+i&&Math.abs(l-c.half)<=i?c.x1=n:a.push({x0:e,x1:n,half:l})}const r=[];for(const t of a){const e=r[r.length-1],s=e?.[e.length-1];s&&t.x0<=s.x1+i?(t.x0=s.x1,e.push(t)):r.push([t])}return r.map(e=>{const i=e[0],s=e[e.length-1],n=Math.min(.25*Math.min(...e.map(t=>t.half)),.75),o=-t*n,a=[];if(1===t){a.push(`M ${i.x0} ${o} L ${s.x1} ${o}`);for(let t=e.length-1;t>=0;t--){const i=e[t];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 t of e)a.push(`L ${t.x0} ${-t.half} L ${t.x1} ${-t.half}`)}return a.push("Z"),a.join(" ")}).join(" ")}function Bc(t,e,i,s){if(!s)return i;const n=t.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=t.x*i+t.y*c,p=d+h*n.x0,u=d+h*n.x1,_=Math.min(p,u),m=Math.max(p,u),g=`${n.key}|${e}`,f=s.get(g)||[];let v=[[_,m]];for(const[t,e]of f){const i=[];for(const[s,n]of v)e<=s+l||t>=n-l?i.push([s,n]):(t>s+l&&i.push([s,Math.min(n,t)]),e<n-l&&i.push([Math.max(s,e),n]));if(v=i,!v.length)break}for(const[t,e]of v){const i=(t-d)/h,s=(e-d)/h;r.push({...n,x0:Math.min(i,s),x1:Math.max(i,s)})}const y=[...f,[_,m]].sort((t,e)=>t[0]-e[0]||t[1]-e[1]),b=[];for(const t of y){const e=b[b.length-1];e&&t[0]<=e[1]+l?e[1]=Math.max(e[1],t[1]):b.push([t[0],t[1]])}s.set(g,b)}return r}function qc(t,e){const i=new Map;return e.map(e=>function(t,e,i){const s=Oc(t,e,!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 t=n||o;a=[{candidate:t,side:-1},{candidate:t,side:1}]}const r=a.map(({candidate:t,side:s})=>({candidate:t,side:s,pieces:Bc(e,s,t.pieces,i)})),l=r.map(({candidate:t,side:e,pieces:i})=>({side:e,roomId:t.roomId,d:Lc(e,i)})),c=r.flatMap(({pieces:t})=>t);if(!c.length)return null;const h=Math.max(...c.map(t=>t.half));return{faces:l,minY:-h,maxY:h,wallKey:[...new Set(c.map(t=>t.key))].sort().join("|")}}(t,e,i))}const Wc=150,jc=1e-6;function Gc(t){return Number.isFinite(t)?Math.max(1,Math.min(Wc,t)):1}function Uc(t){return((Number.isFinite(Number(t))?Number(t):0)%90+90)%90}const Vc=t=>{const e=t.map(t=>[t[0],t[1]]);return!e.length||e[0][0]===e[e.length-1][0]&&e[0][1]===e[e.length-1][1]||e.push([...e[0]]),[e]},Kc=(t,e)=>t[0]===e[0]&&t[1]===e[1];function Yc(t,e=1e-6){const i=Number.isFinite(e)&&e>0?e:jc,s=[];for(const e of t||[]){if(!Array.isArray(e)||e.length<2)return null;const t=Number(e[0]),n=Number(e[1]);if(!Number.isFinite(t)||!Number.isFinite(n))return null;const o=Math.round(t/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&&Kc(s[s.length-1],r)||s.push(r)}return s.length>1&&Kc(s[0],s[s.length-1])&&s.pop(),s.length<3||new Set(s.map(t=>`${t[0]},${t[1]}`)).size<3?null:Ti(s)>i*i?s:null}function Jc(t){const e=[];for(const i of t||[])for(const t of i||[]){const i=(t||[]).filter(t=>Array.isArray(t)&&t.length>=2);i.length<4||e.push(`M ${i.slice(0,-1).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`)}return e.join(" ")}function Xc(t,e,i,s,n){return Nl({a:t,b:e,halfDepth:ml(i,s,n)/2})}function Zc(t,e,i){const s=Number(e)>0?Number(e):5,n=Gc(t.cm)/s*i,o=t.center[0],a=t.center[1];if("circle"===t.shape){const t=n/2;return Array.from({length:96},(e,i)=>{const s=i/96*Math.PI*2;return[o+Math.cos(s)*t,a+Math.sin(s)*t]})}const r=n/2,l=Uc(t.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l);return[[-r,-r],[r,-r],[r,r],[-r,r]].map(([t,e])=>[o+t*c-e*h,a+t*h+e*c])}function Qc(t,e,i=1e-9){if(!e.length)return[t];let s=[Vc(t)];try{for(const t of e){const e=t.b[0]-t.a[0],n=t.b[1]-t.a[1],o=Math.hypot(e,n);if(!(o>i))continue;const a=e/o,r=n/o,l=-r,c=a,h=1.25*Math.max(Number(t.depth)||0,4*i),d=Math.max(2*i,1e-9*o),p=[[t.a[0]-a*d-l*h,t.a[1]-r*d-c*h],[t.b[0]+a*d-l*h,t.b[1]+r*d-c*h],[t.b[0]+a*d+l*h,t.b[1]+r*d+c*h],[t.a[0]-a*d+l*h,t.a[1]-r*d+c*h]];s=oi(s,Vc(p))}return lh(s)}catch{return[t]}}function th(t,e,i,s=Math.max(2e-4*i,1e-9),n=[]){const o=[],a=[],r=new Map;for(const t of n){const e=r.get(t.hostId)||[];e.push(t),r.set(t.hostId,e)}const l=[],c=[],h=[];for(const s of t.room_drafts||[])for(let t=0;t+1<s.points.length;t++){const n=ml(s.segments[t]?.cm||15,e,i)/2,a={a:s.points[t],b:s.points[t+1],halfDepth:n},r=Nl(a);r&&(o.push(a),l.push(r))}for(const n of t.partitions||[]){const t={a:n.a,b:n.b,halfDepth:ml(n.cm,e,i)/2},o=Nl(t);o&&(a.push(t),c.push(o),h.push(...Qc(o,r.get(n.id)||[],s)))}const d=(t.wall_columns||[]).map(t=>Zc(t,e,i)),p=Bl([...o,...a],s).flatMap(t=>Qc(t,n,s));return{drafts:l,partitions:c,columns:d,patches:p,all:[...l,...h,...p,...d]}}function eh(t){try{const e=t.map(t=>Yc(t)).filter(t=>!!t).map(t=>Vc(t));return e.length?si(e[0],...e.slice(1)):null}catch{return null}}const ih=t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`;function sh(t){const e=[];for(const i of t||[]){if(ah([i])<=1e-6)continue;const t=[];for(const e of i||[]){const i=(e||[]).filter(t=>Array.isArray(t)&&t.length>=2);i.length<4||t.push(ih(i.slice(0,-1)))}t.length&&e.push(t.join(" "))}return e}function nh(t,e,i={}){const s=eh(t.filter(t=>t.length>=3)),n=e.some(t=>!Yc(t))?null:eh(e);if(!s)return[];if(n)try{return sh(ni(s,n))}catch{}return function(t,e,i){let s=null;for(let n=0;n<e.length;n++){const o=eh([e[n]]);if(!o){i.onBoundsFailure?.({boundIndex:n,phase:"bound-union"});continue}let a;try{a=ni(t,o)}catch{i.onBoundsFailure?.({boundIndex:n,phase:"bound-intersection"});continue}if(a?.length&&!(ah(a)<=1e-12))if(s)try{s=si(s,a)}catch{i.onBoundsFailure?.({boundIndex:n,phase:"result-union"})}else s=a}return s?sh(s):[]}(s,e,i)}function oh(t,e){if(!e.length)return[Vc(t)];try{const i=eh(e);if(i)return oi(Vc(t),i)}catch{}let i=[Vc(t)];for(const t of e)if(!(t.length<3))try{i=oi(i,Vc(t))}catch{}return i}function ah(t){let e=0;for(const i of t||[])if(i?.length){e+=Ti(i[0]||[]);for(let t=1;t<i.length;t++)e-=Ti(i[t]||[])}return Math.max(0,e)}function rh(t){const e=[];for(const i of t||[])for(const t of i||[])t?.length>=4&&e.push(t.slice(0,-1).map(t=>[t[0],t[1]]));return e}function lh(t){const e=[];for(const i of t||[]){const t=i?.[0];t?.length>=4&&e.push(t.slice(0,-1).map(t=>[t[0],t[1]]))}return e}function ch(t,e){let i=!1;for(let s=0,n=e.length-1;s<e.length;n=s++){const o=e[s][0],a=e[s][1],r=e[n][0],l=e[n][1];a>t[1]!=l>t[1]&&t[0]<(r-o)*(t[1]-a)/(l-a||1e-12)+o&&(i=!i)}return i}function hh(t,e,i){return function(t,e){for(const i of e||[]){const e=i?.[0];if(!e?.length||!ch(t,e))continue;let s=!1;for(let e=1;e<i.length;e++)if(i[e]?.length&&ch(t,i[e])){s=!0;break}if(!s)return!0}return!1}(t,e)||i.some(e=>ch(t,e))}function dh(t,e,i){if(Math.hypot(t.center[0]-e.center[0],t.center[1]-e.center[1])>i)return!1;if(Math.abs(Gc(t.cm)-Gc(e.cm))>1e-6)return!1;if(t.shape!==e.shape)return!0;if("circle"===t.shape||"circle"===e.shape)return!0;const s=Math.abs(Uc(t.angle)-Uc(e.angle));return Math.min(s,90-s)<=1e-6}const ph=2.54;function uh(t){const e="number"==typeof t?t:NaN;return!Number.isFinite(e)||e<=0||5===e?1:5/e}function _h(t,e){return t*uh(e)}function mh(t){return t?ph:1}function gh(t,e){if(!e)return String(t);const i=t/ph;return String(Math.round(1e6*i)/1e6)}const fh=1e3;function vh(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,n=s>=1?e:e*s,o=s>=1?e/s:e;return{x:(e-n)/2,y:(e-o)/2,w:n,h:o}}const yh=.01,bh=100;function wh(t,e=1e3){const i=vh(t?.plan_aspect,e),s=Number(t?.plan_scale),n=Number.isFinite(s)&&s>0?Math.min(bh,Math.max(yh,s)):1,o=Number(t?.plan_scale_x),a=Number(t?.plan_scale_y),r=Number.isFinite(o)&&o>0?Math.min(bh,Math.max(yh,o)):n,l=Number.isFinite(a)&&a>0?Math.min(bh,Math.max(yh,a)):n,c=Number(t?.plan_x),h=Number(t?.plan_y),d=Ar(t?.plan_angle);return{x:i.x+(Number.isFinite(c)?Ih(c):0)*e,y:i.y+(Number.isFinite(h)?Ih(h):0)*e,w:i.w*r,h:i.h*l,...d?{angle:d}:{}}}function kh(t){if(null==t.x||null==t.y)return{x:t.x,y:t.y,w:t.w,h:t.h};const e=Number(t.w)||0,i=Number(t.h)||0;return{x:e<0?t.x+e:t.x,y:i<0?t.y+i:t.y,w:Math.abs(e),h:Math.abs(i)}}function xh(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=fh,i=function(t){return Array.isArray(t)&&4===t.length&&t.every(t=>Number.isFinite(t))&&t[2]>1e-6&&t[3]>1e-6?t:[0,0,1,1]}(t.view_box);return{id:t.id,title:t.title,cellCm:Number.isFinite(Number(t.cell_cm))&&Number(t.cell_cm)>0?Number(t.cell_cm):5,vb:[i[0]*fh,i[1]*e,i[2]*fh,i[3]*e],bg:t.plan_url?{href:As(t.plan_url),...wh(t,fh)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...kh(t)};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*fh:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*fh:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*fh,t[1]*e]):void 0}}),room_drafts:(t.room_drafts||[]).map(t=>({id:t.id,points:(t.points||[]).map(t=>[t[0]*fh,t[1]*e]),segments:(t.segments||[]).map(t=>({cm:Number(t.cm)}))})),partitions:(t.partitions||[]).map(t=>({id:t.id,a:[t.a[0]*fh,t.a[1]*e],b:[t.b[0]*fh,t.b[1]*e],cm:Number(t.cm)})),wall_columns:(t.wall_columns||[]).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[t.center[0]*fh,t.center[1]*e],cm:Number(t.cm),..."circle"===t.shape?{}:{angle:Uc(t.angle)}}))}}):[]}const $h=5e3,Sh=$h*fh,Mh=240,Ch=fh/Mh,Dh=1/Mh;function Ph(t){if(!Number.isFinite(t))return t;const e=Math.round(t*Mh/fh)*fh/Mh;return Math.abs(e-t)<=1e-9*Ch?t:e}function Th(t){return{x:Ph(t.x),y:Ph(t.y)}}function Rh(t){return Number.isFinite(t)?Math.min(Sh,Math.max(-Sh,t)):0}function Ih(t){return Number.isFinite(t)?Math.min($h,Math.max(-$h,t)):0}const Ah=1/3,zh=200;function Fh(t){let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t){const t=Number(o[0]),a=Number(o[1]);Number.isFinite(t)&&Number.isFinite(a)&&(t<e&&(e=t),a<i&&(i=a),t>s&&(s=t),a>n&&(n=a))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:n}}function Eh(t){return t.poly&&t.poly.length?Fh(t.poly):null==t.x||null==t.y?null:Fh([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function Nh(t,e){const i=[];for(const e of t.rooms||[]){const t=Eh(e);t&&i.push(t)}if(t.bg){const e=Fh(Hr(t.bg));e&&i.push(e)}for(const t of e||[])if(Array.isArray(t)){const e=Fh([t]);e&&i.push(e)}else i.push(t);return i}const Oh=t=>{if(!t.length)return 0;const e=[...t].sort((t,e)=>t-e),i=e.length>>1;return e.length%2?e[i]:(e[i-1]+e[i])/2},Hh=t=>{let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t)o.minX<e&&(e=o.minX),o.minY<i&&(i=o.minY),o.maxX>s&&(s=o.maxX),o.maxY>n&&(n=o.maxY);return e>s||i>n?null:{x:e,y:i,w:s-e,h:n-i}};function Lh(t,e){let{x:i,y:s,w:n,h:o}=t;n<30&&(i=i+n/2-100,n=zh),o<30&&(s=s+o/2-100,o=zh);const a=Math.max(n,o)*e;return{x:i-a,y:s-a,w:n+2*a,h:o+2*a}}function Bh(t,e={}){const i=e.pad??.05,s=e.k??10,n=e.minSpread??50,o=t.filter(t=>Number.isFinite(t.minX)&&Number.isFinite(t.minY)&&Number.isFinite(t.maxX)&&Number.isFinite(t.maxY)&&Math.abs(t.minX)<=Sh&&Math.abs(t.maxX)<=Sh&&Math.abs(t.minY)<=Sh&&Math.abs(t.maxY)<=Sh);if(!o.length)return{core:null,all:null,outliers:0};const a=Hh(o);if(o.length<4){const t=Lh(a,i);return{core:t,all:t,outliers:0}}const r=o.map(t=>(t.minX+t.maxX)/2),l=o.map(t=>(t.minY+t.maxY)/2),c=Oh(r),h=Oh(l),d=o.map((t,e)=>Math.max(Math.abs(r[e]-c),Math.abs(l[e]-h))),p=Math.max(((t,e)=>t.length?t[Math.min(t.length-1,Math.max(0,Math.round(e*(t.length-1))))]:0)([...d].sort((t,e)=>t-e),.75),n),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,m=_&&_<=o.length*Ah?o.filter((t,e)=>!u[e]):o;return{core:Lh(Hh(m)||a,i),all:Lh(a,i),outliers:m===o?0:_}}function qh(t,e,i=.05){const s=Bh(Nh(t,e),{pad:i});if(s.core)return s.core;const n=t.vb&&4===t.vb.length&&t.vb[2]>0&&t.vb[3]>0?t.vb:[0,0,fh,fh];return{x:n[0],y:n[1],w:n[2],h:n[3]}}function Wh(t){const e=qh(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function jh(t){const e=[];for(const i of t.rooms||[]){const t=Eh(i);t&&e.push(t)}const i=Bh(e,{pad:0}).core,s=fh*uh(t.cellCm??5);return i?Math.max(s,Math.min(Sh,Math.max(i.w,i.h))):s}function Gh(t,e,i,s=1){const n=Number(i),o=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(n)||n<=0?t*o:t*jh(e)*o/n}const Uh=[1,2,5,10,20,50,100,200,500,1e3];function Vh(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),n=Math.min(...i);return{x:s,y:n,w:Math.max(...e)-s,h:Math.max(...i)-n}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function Kh(t,e,i,s){const n=i["rl_"+(t.id||"")];if(n&&n.s===e)return{x:n.x*fh,y:n.y*fh};const o=function(t){if(t.poly){const e=t.poly.length;return[t.poly.reduce((t,e)=>t+e[0],0)/e,t.poly.reduce((t,e)=>t+e[1],0)/e]}return[t.x+t.w/2,t.y+.1*Math.min(t.w,t.h)]}(t);return Th({x:o[0],y:o[1]})}const Yh=["furniture","appliance","sanitary","other"],Jh=[{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]]}],Xh=new Map(Jh.map(t=>[t.id,t]));function Zh(t){return t&&Xh.get(t)||null}function Qh(t){return Jh.filter(e=>e.group===t)}function td(t,e,i=Ch,s=1e3){const n=Number(e)>0?Number(e):5;return(Number(t)||0)/n*i/s}const ed=5e-4,id=$h;function sd(t){return Number.isFinite(t)?Math.max(ed,Math.min(id,t)):ed}const nd=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function od(t,e,i){const s=Zh(t);if(!(s&&e>0&&i>0))return"";const n=t=>nd(t*e),o=t=>nd(t*i),a=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,r]=t;a.push(`M${n(e)} ${o(i)}H${n(e+s)}V${o(i+r)}H${n(e)}Z`)}else if("l"===t[0]){const[,e,i,s,r]=t;a.push(`M${n(e)} ${o(i)}L${n(s)} ${o(r)}`)}else if("e"===t[0]){const[,s,r,l,c]=t;a.push(`M${n(s-l)} ${o(r)}A${nd(l*e)} ${nd(c*i)} 0 0 1 ${n(s+l)} ${o(r)}A${nd(l*e)} ${nd(c*i)} 0 0 1 ${n(s-l)} ${o(r)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${n(e[0])} ${o(e[1])}`;for(let t=2;t+1<e.length;t+=2)i+=`L${n(e[t])} ${o(e[t+1])}`;a.push(i)}return a.join("")}const ad=t=>{let e=(t%360+360)%360;return e>180&&(e-=360),e};function rd(t,e,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=((t-s)*d+(e-l)*p)/u;m=Math.max(0,Math.min(1,m));let g=s+m*d,f=l+m*p;const v=Math.hypot(t-g,e-f);if(!(v<r))continue;r=v;let y=t-g,b=e-f;const w=Math.hypot(y,b);if(w<1e-9?(y=p/_,b=-d/_):(y/=w,b/=w),o>0){const t=Math.round(m*_/o)*o;g=s+t/_*d,f=l+t/_*p}a={cx:g+y*(i/2),cy:f+b*(i/2),angle:ad(180*Math.atan2(-y,b)/Math.PI),dist:v}}return a}function ld(t,e,i){const s=i>0?i:1;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function cd(t,e){const i=e>0?e:1;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function hd(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function dd(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!hd(t.a)||!hd(t.b))continue;const s=[Number(t.a[0]),Number(t.a[1])],n=[Number(t.b[0]),Number(t.b[1])];Math.hypot(n[0]-s[0],n[1]-s[1])<.001||e.push({a:s,b:n})}return e}function pd(t,e){const i=e[0],s=e[1],n=e[2]-i,o=e[3]-s,a=n*n+o*o;if(a<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let r=((t[0]-i)*n+(t[1]-s)*o)/a;r=Math.max(0,Math.min(1,r));const l=[i+r*n,s+r*o];return{q:l,t:r,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function ud(t,e){return pd(t,e).q}function _d(t,e,i,s,n){const o=pd(t,e).q;let a=o,r=1/0;for(const t of i){const e=Math.hypot(o[0]-t[0],o[1]-t[1]);e<=n&&e<r&&(r=e,a=[t[0],t[1]])}if(r<=n)return a;const l=e[0],c=e[1],h=e[2]-l,d=e[3]-c,p=Math.hypot(h,d)||1,u=((o[0]-l)*h+(o[1]-c)*d)/p,_=s>0?s:1,m=Math.round(u/_)*_,g=Math.max(0,Math.min(p,m))/p;return[l+h*g,c+d*g]}function md(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;t<s.length;t++){const n=hi(s[t]);if(n)for(let o=t+1;o<s.length;o++){const a=hi(s[o]);if(a)for(const r of Cs(n,a,e))i.push({seg:r,pair:`${s[t].id}:${s[o].id}`,a:s[t],b:s[o]})}}return i}function gd(t,e,i,s,n=!0){const o=(t||[]).filter(t=>t?.id),a=dd(e);if(a.length)return $d(a,t,i,s).map(t=>cd(t,i));if(!n)return[];const r=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;t<o.length;t++)for(let e=t+1;e<o.length;e++){if(!l(o[t],o[e]))continue;const i=hi(o[t]),n=hi(o[e]);if(i&&n)for(const t of Cs(i,n,s))r.push(t)}return r}function fd(t,e){return t.map(t=>ld([t[0],t[1]],[t[2],t[3]],e))}function vd(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const n=(e||[]).filter(t=>t?.id);new Map(n.map(t=>[t.id,t]));const o=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),a=(t,e)=>{const i=o.get(t),s=o.get(e);i&&s&&((i.open_to||[]).includes(e)||(i.open_to=[...i.open_to||[],e]),(s.open_to||[]).includes(t)||(s.open_to=[...s.open_to||[],t]))};for(let t=0;t<n.length;t++)for(let e=t+1;e<n.length;e++){const o=hi(n[t]),r=hi(n[e]);if(!o||!r)continue;const l=Cs(o,r,s);if(l.length)for(const o of i){const i=[(o[0]+o[2])/2,(o[1]+o[3])/2];if(l.some(t=>Hs(i,t)<4*s)){a(n[t].id,n[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}function yd(t,e,i,s=0){const n=e[2]-e[0],o=e[3]-e[1],a=Math.hypot(n,o);if(!(a>1e-9))return null;const r=n/a,l=o/a,c=(t[0]-e[0])*r+(t[1]-e[1])*l;if(c<-s||c>a+s)return null;return Math.abs((t[0]-e[0])*l-(t[1]-e[1])*r)>i?null:Hs(t,e)}function bd(t,e,i){const s=t[2]-t[0],n=t[3]-t[1],o=e[2]-e[0],a=e[3]-e[1],r=Math.hypot(s,n),l=Math.hypot(o,a);return r>1e-9&&l>1e-9&&(!(Math.abs(s/r*(a/l)-n/r*(o/l))>1e-6)&&(Hs([e[0],e[1]],t)<=4*i||Hs([t[0],t[1]],e)<=4*i))}function wd(t,e,i){if(!t.length)return null;t.sort((t,e)=>t.distance-e.distance);const s=t[0];for(let n=1;n<t.length;n++){const o=t[n];if(o.distance-s.distance>e)break;if(o.semantic!==s.semantic||!bd(s.seg,o.seg,i))return"ambiguous"}return s}function kd(t,e,i,s,n=1,o=15){const a=function(t,e,i,s,n=1,o=15){const[a,r]=yl([e[0],e[1]],[e[2],e[3]]);let l=0,c=1/0;const h=[(e[0]+e[2])/2,(e[1]+e[3])/2];for(const e of i){const[i,o]=yl([e[0],e[1]],[e[2],e[3]]);if(Math.abs(a*o-r*i)>.05)continue;const d=Dl(t,[e[0],e[1]],[e[2],e[3]],s,n);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-h[0],p[1]-h[1]);u<c&&(c=u,l=d)}return l>0?l:o}(t,e,i,s,n,o);return zl(t,[e[0],e[1]],[e[2],e[3]],a,s,n)}function xd(t,e,i){const s=[[t[0],t[1]],[t[2],t[3]]],n=[(t[0]+t[2])/2,(t[1]+t[3])/2];for(const o of e)if(!(Hs(n,o)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Hs([o[0],o[1]],t)>i))for(const e of[[o[0],o[1]],[o[2],o[3]]])Hs(e,t)<=2*i&&s.push(e);return s}function $d(t,e,i,s){const n=dd(t);if(!n.length)return[];const o=md(e,s);if(!o.length)return[];const a=[],r=Math.max(4*s,1e-6);for(const t of n){const e=cd(t,i),n=e[0],l=e[1],c=e[2]-n,h=e[3]-l,d=Math.hypot(c,h);if(d<r)continue;const p=c/d,u=h/d,_=new Map;for(const{seg:t,pair:e}of o){const i=Math.abs((t[0]-n)*u-(t[1]-l)*p),o=Math.abs((t[2]-n)*u-(t[3]-l)*p);if(i>4*s||o>4*s)continue;const a=(t[0]-n)*p+(t[1]-l)*u,c=(t[2]-n)*p+(t[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(e)||[];g.push({lo:h,hi:m}),_.set(e,g)}for(const[t,e]of _){e.sort((t,e)=>t.lo-e.lo||t.hi-e.hi);const i=[];for(const t of e){const e=i[i.length-1];e&&t.lo<=e.hi+r?e.hi=Math.max(e.hi,t.hi):i.push({...t})}for(const e of i){const i=[n+p*e.lo,l+u*e.lo],s=[n+p*e.hi,l+u*e.hi];Math.hypot(s[0]-i[0],s[1]-i[1])<r||a.push({pair:t,seg:[i[0],i[1],s[0],s[1]]})}}}const l=[],c=Math.max(4*s,1e-6);for(const{pair:t,seg:e}of a){const i=e[2]-e[0],s=e[3]-e[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===t&&Math.abs(i.ux*a-i.uy*o)<=1e-6&&Math.abs((e[0]-i.origin[0])*i.uy-(e[1]-i.origin[1])*i.ux)<=c&&Math.abs((e[2]-i.origin[0])*i.uy-(e[3]-i.origin[1])*i.ux)<=c);h||(h={pair:t,origin:[e[0],e[1]],ux:o,uy:a,ranges:[]},l.push(h));const d=(e[0]-h.origin[0])*h.ux+(e[1]-h.origin[1])*h.uy,p=(e[2]-h.origin[0])*h.ux+(e[3]-h.origin[1])*h.uy;h.ranges.push({lo:Math.min(d,p),hi:Math.max(d,p)})}const h=[];for(const t of l){t.ranges.sort((t,e)=>t.lo-e.lo||t.hi-e.hi);const e=[];for(const i of t.ranges){const t=e[e.length-1];t&&i.lo<=t.hi+r?t.hi=Math.max(t.hi,i.hi):e.push({...i})}for(const s of e){const e=[t.origin[0]+t.ux*s.lo,t.origin[1]+t.uy*s.lo],n=[t.origin[0]+t.ux*s.hi,t.origin[1]+t.uy*s.hi];Math.hypot(n[0]-e[0],n[1]-e[1])>=r&&h.push(ld(e,n,i))}}return h}function Sd(t,e,i,s){const n=dd(t);if(!n.length)return[];if(e.length!==i.length)return n;const o=[];for(const t of n){const n=cd(t,s),a=[n[0],n[1]],r=[n[2],n[3]];let l=a,c=r;for(let t=0;t<e.length;t++){const[s,n]=e[t],[o,h]=i[t],d=t=>{const e=pd(t,[s[0],s[1],n[0],n[1]]);if(e.d>.001)return t;const i=h[0]-o[0],a=h[1]-o[1];return[o[0]+i*e.t,o[1]+a*e.t]};if(pd([(a[0]+r[0])/2,(a[1]+r[1])/2],[s[0],s[1],n[0],n[1]]).d<.01){l=d(a),c=d(r);break}}o.push(ld(l,c,s))}return o}const Md=15e3,Cd=2e3,Dd=new WeakMap;function Pd(t,e){let i=Dd.get(t);if(!i){const e=new Set;i={hiddenAt:"hidden"===t.visibilityState?Date.now():0,token:0,subscribers:e,onVisibility:()=>{const e=Dd.get(t);if(!e)return;const i=Date.now();if("hidden"===t.visibilityState){e.hiddenAt||(e.hiddenAt=i);const t={kind:"hidden",token:e.token,at:i,hiddenFor:0,long:!1};for(const i of[...e.subscribers])i(t);return}const s=e.hiddenAt?Math.max(0,i-e.hiddenAt):0;e.hiddenAt=0,e.token++;const n={kind:"visible",token:e.token,at:i,hiddenFor:s,long:s>=Md};for(const t of[...e.subscribers])t(n)},onPageShow:e=>{const i=Dd.get(t);if(!i)return;const s=Date.now();i.token++;const n={kind:"pageshow",token:i.token,at:s,hiddenFor:0,long:!!e.persisted,persisted:!!e.persisted};for(const t of[...i.subscribers])t(n)}},Dd.set(t,i),t.addEventListener("visibilitychange",i.onVisibility),t.defaultView?.addEventListener("pageshow",i.onPageShow)}return i.subscribers.add(e),()=>{const i=Dd.get(t);i&&(i.subscribers.delete(e),i.subscribers.size||(t.removeEventListener("visibilitychange",i.onVisibility),t.defaultView?.removeEventListener("pageshow",i.onPageShow),Dd.delete(t)))}}const Td={now:()=>Date.now(),setTimeout:(t,e)=>window.setTimeout(t,e),clearTimeout:t=>window.clearTimeout(t),requestAnimationFrame:t=>window.requestAnimationFrame(t),cancelAnimationFrame:t=>window.cancelAnimationFrame(t)};class Rd{constructor(t,e=Td){this.onChange=t,this.clock=e,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(t=>({...t,...t.stage?{stage:[...t.stage]}:{}}))}note(t,e={}){this.record(t,void 0,e)}record(t,e,i={}){this._trace.push({at:this.clock.now(),token:this._token,event:t,state:this._state,...e?{reason:e}:{},...i}),this._trace.length>80&&this._trace.splice(0,this._trace.length-80)}changed(){this._disposed||this.onChange()}adoptCompleteFrame(t){t&&(this._hasCompleteFrame=!0,this._frameFingerprint=t,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-adopted"),this.changed())}markCompleteFrame(t){t&&(this._hasCompleteFrame=!0,this._frameFingerprint=t,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-complete"),this.changed())}refreshCompleteFrame(t){!this._disposed&&this._hasCompleteFrame&&"steady"===this._state&&t&&t!==this._frameFingerprint&&(this._frameFingerprint=t,this.record("frame-refreshed"))}visibility(t){return"hidden"===t.kind?(this.record("visibility-hidden"),this._token):t.long||"visible"!==t.kind&&"pageshow"!==t.kind?this.beginCandidate("pageshow"===t.kind?"pageshow":"long-resume"):(this.record("pageshow"===t.kind?"pageshow-noop":"visibility-visible-quick"),this._token)}beginCandidate(t,e="plan"){return this._token++,this._recoveryReason=e,this._hasCompleteFrame?(this._state="connection"===e?"offline-stale":"holding",this.clearOverlay()):this.overlayVisible?this._state="overlay-visible":(this._state="overlay-pending",this.scheduleOverlay(e)),this.record("candidate-start",t),this.changed(),this._token}connectionLost(){return this.beginCandidate("connection-lost","connection")}candidateReady(t){return!this._disposed&&t===this._token&&(this._state="candidate-ready",this.record("candidate-ready"),this.changed(),!0)}async commitAfterPaint(t,e){if(this._disposed||t!==this._token)return!1;let i=!0;const s=(async()=>{for(await e.updateComplete();i&&!this._disposed&&t===this._token;)if(e.stageValid()&&e.assetsReady()){if(await this.nextFrame(),t!==this._token||!e.stageValid()||!e.assetsReady())continue;if(await this.nextFrame(),t===this._token&&e.stageValid()&&e.assetsReady())return!0}else await this.nextFrame();return!1})();let n=0;const o=new Promise(t=>{n=this.clock.setTimeout(()=>t({ready:!1,timedOut:!0}),Cd)}),a=await Promise.race([s.then(t=>({ready:t,timedOut:!1})),o]);return i=!1,this.clock.clearTimeout(n),!this._disposed&&t===this._token&&(!(!a.ready&&!a.timedOut)&&(a.ready?(this._hasCompleteFrame=!0,this._frameFingerprint=e.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(t=this._recoveryReason||"plan"){return this.beginCandidate("retry",t)}nextFrame(){return new Promise(t=>{const e=this.clock.requestAnimationFrame(()=>{this._barrierRafs.delete(e),t()});this._barrierRafs.add(e)})}scheduleOverlay(t){this.clearOverlayTimer(),this._recoveryReason=t,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 t=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)},t)}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 t of this._barrierRafs)this.clock.cancelAnimationFrame(t);this._barrierRafs.clear()}}function Id(t){let e=2166136261,i=2654435769;const s=new WeakSet,n=t=>{for(let s=0;s<t.length;s++){const n=t.charCodeAt(s);e^=n,e=Math.imul(e,16777619),i^=n+(i<<6>>>0)+(i>>>2)}},o=t=>{if(null===t)return void n("n");const e=typeof t;if("string"===e)return void n(`s${t.length}:${t}`);if("number"===e)return void n(`d${Number.isNaN(t)?"NaN":String(t)}`);if("boolean"===e)return void n(t?"t":"f");if("undefined"===e)return void n("u");if("object"!==e)return void n(`${e}:${String(t)}`);const i=t;if(s.has(i))n("[cycle]");else{if(s.add(i),Array.isArray(t)){n("[");for(const e of t)o(e);return n("]"),void s.delete(i)}n("{");for(const e of Object.keys(t).sort())n(e),o(t[e]);n("}"),s.delete(i)}};return o(t),`${(e>>>0).toString(16).padStart(8,"0")}${(i>>>0).toString(16).padStart(8,"0")}`}function Ad(t){return Id(t)}const zd=new WeakMap;class Fd{constructor(t,e=()=>Date.now()){this.onUpdate=t,this.now=e,this.fallbackAuthority={},this.referenceOwner={},this.referenced=new Set,this.queued=new Set,this.disposed=!1,this.sharedUpdate=()=>{this.disposed||this.onUpdate()}}bind(t){const e=((t,e)=>{const i=t?.connection||t?.callWS||e;return!i||"object"!=typeof i&&"function"!=typeof i?e:i})(t,this.fallbackAuthority);return this.shared&&this.authority===e||(this.shared&&(this.shared.listeners.delete(this.sharedUpdate),this.shared.references.delete(this.referenceOwner)),this.authority=e,this.shared=(t=>{let e=zd.get(t);return e||(e={signed:{},queued:new Set,inFlight:new Map,retry:new Map,listeners:new Set,settlers:new Set,references:new Map},zd.set(t,e)),e})(e),this.disposed||(this.shared.listeners.add(this.sharedUpdate),this.shared.references.set(this.referenceOwner,new Set(this.referenced)))),this.shared}start(t,e){this.disposed=!1,this.referenced=new Set(e());const i=this.bind(t());i.listeners.add(this.sharedUpdate),i.references.set(this.referenceOwner,new Set(this.referenced)),this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(t(),e()),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 t of this.queued)this.shared.queued.delete(t)}this.queued.clear()}invalidate(t){const e=this.bind(t);clearTimeout(this.batchTimer),this.queued.clear(),e.queued.clear(),e.inFlight.clear(),e.retry.clear(),e.signed={}}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(t,e){const i=As(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.bind(t);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<Rs?n.url:o<Ts?(n.pending||this.request(t,i),n.url):(n&&delete s.signed[i],this.request(t,i),"")}request(t,e){if(!t?.callWS||this.queued.has(e))return;const i=this.bind(t),s=this.now(),n=i.inFlight.get(e);if(void 0!==n&&s-n<15e3)return;if(i.queued.has(e))return;const o=i.retry.get(e);o&&s<o.notBefore||(this.queued.add(e),i.queued.add(e),clearTimeout(this.batchTimer),this.batchTimer=setTimeout(()=>{const e=[...this.queued];this.queued.clear();for(const t of e)i.queued.delete(t);this.sign(t,e)},30))}sign(t,e){if(!e.length||!t?.callWS)return;const i=this.bind(t);for(const s of function(t,e){const i=Math.max(1,Math.floor(e)),s=[];for(let e=0;e<t.length;e+=i)s.push(t.slice(e,e+i));return s}(e,200)){const e=this.now(),n=s.filter(t=>{const s=i.inFlight.get(t);return!(void 0!==s&&e-s<15e3)&&(i.inFlight.set(t,e),!0)});n.length&&t.callWS({type:"houseplan/content/sign",paths:n}).then(t=>{const e=this.now();let s=0,o=!1;for(const a of n){const n=t?.urls?.[a];if("string"==typeof n&&n){const t=i.signed[a];t?.loaded&&t.url!==n?(t.pending={url:n,at:e},this.preloadReplacement(i,a,n,e)):(i.signed[a]={url:n,at:e,loaded:t?.url===n&&!!t.loaded},o=!0),i.retry.delete(a),s++}else this.backOff(i,a)}if(s&&(this.trimShared(i),o))for(const t of[...i.listeners])t()}).catch(()=>{for(const t of n)this.backOff(i,t)}).finally(()=>{for(const t of n)i.inFlight.get(t)===e&&i.inFlight.delete(t);for(const t of[...i.settlers])t()})}}preloadReplacement(t,e,i,s){const n=()=>{const n=t.signed[e];if(n?.pending?.url===i){t.signed[e]={url:i,at:s,loaded:!0},t.retry.delete(e),this.trimShared(t);for(const e of[...t.listeners])e()}},o=()=>{const s=t.signed[e];s?.pending?.url===i&&(delete s.pending,this.backOff(t,e))};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(t,e,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=t.signed[e];if(r===i&&delete r.preload,o&&r===i&&r.url===s){r.loaded=!0;for(const e of[...t.listeners])e();n(!0)}else o||r!==i||this.backOff(t,e),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),Cd);o.src=s}),i.preload}prepareImage(t,e){const i=As(e);if(!i.startsWith("/api/houseplan/content/"))return Promise.resolve(!0);const s=this.bind(t),n=()=>{const e=s.signed[i];if(e&&this.now()-e.at<Ts)return this.preloadCurrentImage(s,i,e);this.display(t,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(t=>{let e=!1;const i=i=>{e||(e=!0,clearTimeout(s),t(i))},s=setTimeout(()=>i(!1),Cd);a.then(i).catch(()=>i(!1))})):new Promise(t=>{let e=!1,i=null;const o=i=>{e||(e=!0,clearTimeout(r),s.settlers.delete(a),t(i))},a=()=>{e||i||(i=n(),i&&i.then(o).catch(()=>o(!1)))},r=setTimeout(()=>o(!1),Cd);s.settlers.add(a),a()});var a}backOff(t,e){const i=t.retry.get(e)?.delay||0,s=Math.min(6e4,i?2*i:2e3);t.retry.set(e,{notBefore:this.now()+s,delay:s})}trimShared(t){const e=Object.entries(t.signed);if(e.length<=512)return;const i=new Set(t.inFlight.keys());for(const e of t.references.values())for(const t of e)i.add(t);for(const[t,s]of e)(s.pending||s.preload)&&i.add(t);e.sort((t,e)=>e[1].at-t[1].at);const s=e.filter(([t])=>i.has(t));for(const t of e){if(s.length>=512)break;i.has(t[0])||s.push(t)}t.signed=Object.fromEntries(s)}resign(t,e){const i=this.bind(t);this.referenced=new Set(e),i.references.set(this.referenceOwner,new Set(this.referenced));const s=this.now(),n=new Set;for(const t of i.references.values())for(const e of t)n.add(e);const o={};for(const[t,e]of Object.entries(i.signed))n.has(t)&&s-e.at<Ts&&(o[t]=e);i.signed=o,i.retry.clear(),this.sign(t,[...e].filter(t=>!!o[t]&&!o[t].pending))}markLoaded(t,e,i){const s=As(e);if(!s.startsWith("/api/houseplan/content/"))return;const n=this.bind(t).signed[s];if(n&&(!i||n.url===i)&&!n.loaded){n.loaded=!0;for(const e of[...this.bind(t).listeners])e()}}isReady(t,e){const i=As(e);if(!i.startsWith("/api/houseplan/content/"))return!0;const s=this.bind(t).signed[i];return!!s&&this.now()-s.at<Ts&&s.loaded}get entries(){const t={};for(const[e,i]of Object.entries(this.shared?.signed||{}))t[e]={url:i.url,at:i.at};return t}get inFlightUrls(){return[...this.shared?.inFlight.keys()||[]]}}function Ed(t,e){if(e)return t.find(t=>t.id===e)}function Nd(t="file"){const e="draw"===t;return{source:t,showBorders:e,showNames:e,displayTouched:!1}}function Od(t,e){if(t.displayTouched)return{...t,source:e};const i="draw"===e;return{...t,source:e,showBorders:i,showNames:i}}function Hd(t,e,i){return{...t,[e]:i,displayTouched:!0}}function Ld(t,e,i){return[t[0]*e+t[1]*i+t[2],t[3]*e+t[4]*i+t[5]]}function Bd(t,e){let i=0;for(const[s,n]of e){const e=Ld(t,s[0],s[1]);i=Math.max(i,Math.hypot(e[0]-n[0],e[1]-n[1]))}return i}const qd=t=>{if(null==t||""===t)return null;const e=Number(t);return Number.isFinite(e)?e:null},Wd=t=>{const e=t,i=qd(Array.isArray(e)?e[0]:e?.x),s=qd(Array.isArray(e)?e[1]:e?.y);return null==i||null==s?null:[i,s]};function jd(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=Wd(i);if(!t)return null;e.push(t)}if(e.length>1){const t=e[0],i=e[e.length-1];t[0]===i[0]&&t[1]===i[1]&&e.pop()}if(e.length<3)return null;let i=0,s=0,n=0;for(let t=0;t<e.length;t++){const o=e[t],a=e[(t+1)%e.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?[e.reduce((t,e)=>t+e[0],0)/e.length,e.reduce((t,e)=>t+e[1],0)/e.length]:[s/(3*i),n/(3*i)]}function Gd(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=Wd(i);if(!t)return null;e.push(t)}if(e.length>1){const t=e[0],i=e[e.length-1];t[0]===i[0]&&t[1]===i[1]&&e.pop()}const i=jd(e);if(!i)return null;const s=e.map(t=>t[0]),n=e.map(t=>t[1]);return{centroid:i,bbox:[Math.min(...s),Math.min(...n),Math.max(...s),Math.max(...n)]}}function Ud(t){let e=t;if(e&&"object"==typeof e&&!Array.isArray(e)){const t=e;e=t.path??t.points??e}if(!Array.isArray(e)||!e.length)return[];let i=(e.some(t=>(t=>Array.isArray(t)?t.length>=2&&!Array.isArray(t[0])&&("object"!=typeof t[0]||null==t[0]):!!t&&"object"==typeof t&&("x"in t||"y"in t))(t))?[e]:e.filter(Array.isArray)).map(t=>t.map(Wd).filter(t=>null!=t)).filter(t=>t.length>=2);i.length>64&&(i=i.slice(-64));if(i.reduce((t,e)=>t+e.length,0)<=4e3)return i.map(t=>t.slice());const s=4e3-2*i.length,n=i.map(t=>t.length-2),o=n.reduce((t,e)=>t+e,0),a=n.map(t=>s*t/o),r=a.map(Math.floor);let l=s-r.reduce((t,e)=>t+e,0);const c=a.map((t,e)=>({index:e,fraction:t-Math.floor(t)})).sort((t,e)=>e.fraction-t.fraction||t.index-e.index);for(let t=0;t<l;t++)r[c[t].index]++;return i.map((t,e)=>function(t,e){if(e>=t.length)return t.slice();if(e<=2)return[t[0],t[t.length-1]];const i=[];for(let s=0;s<e;s++){const n=Math.round(s*(t.length-1)/(e-1));i.push(t[n])}return i}(t,2+r[e]))}function Vd(t,e,i){const s=Ud(t&&"object"==typeof t&&!Array.isArray(t)&&"path"in t?t.path:t);if(s.length)return{path:s,source:"integration"};const n=Ud(e&&"object"==typeof e&&!Array.isArray(e)&&"points"in e?e.points:e);if(n.length)return{path:n,source:"server"};const o=Ud(i);return o.length?{path:o,source:"local"}:{path:[],source:"none"}}function Kd(t){const e=t.map(t=>t.slice()),i=e[e.length-1];return i?(i.length>2?i.pop():e.pop(),e):[]}function Yd(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=qd(e.x)&&null!=qd(e.y)?{x:qd(e.x),y:qd(e.y),a:qd(e.a??e.angle??e.theta)}:null,s=Ud(t.path?.path??t.path?.points??t.path),n=[],o=t.rooms,a=Array.isArray(o)?o.map((t,e)=>[String(t?.id??e),t]):o&&"object"==typeof o?Object.entries(o):[];for(const[t,e]of a){if(!e||"object"!=typeof e)continue;const i=String(e.name??e.label??"").trim(),s=[[qd(e.cx),qd(e.cy)],[qd(e.center?.x),qd(e.center?.y)],[qd(e.x),qd(e.y)]].find(([t,e])=>null!=t&&null!=e);let o=s?.[0]??null,a=s?.[1]??null;const r=Gd(e.outline),l=qd(e.x0),c=qd(e.y0),h=qd(e.x1),d=qd(e.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 e={id:t,name:i,cx:o,cy:a};p&&([e.x0,e.y0,e.x1,e.y1]=p),n.push(e)}}const r=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||n.length||s.length?{pos:i,path:s,rooms:n,mapId:r}:null}function Jd(t,e,i){if(!t||!e)return null;const s=e.attributes||{},n=t.split(".")[0]||"",o=null!=i?.platform?String(i.platform):null,a=!!Wd(s.vacuum_position??s.robot_position),r=!(!s.rooms||"object"!=typeof s.rooms),l=Ud(s.path?.path??s.path?.points??s.path).length>0,c=[s.map_name,s.current_map,s.map_index,s.selected_map].some(t=>null!=t),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:t,name:String(s.friendly_name||t),platform:o,category:p,hasPosition:a,hasRooms:r,hasPath:l,hasMapId:c,score:u}}const Xd=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function Zd(t,e){const i=new Map(e.map(t=>[Xd(t.name),t])),s=[],n=[];for(const e of t){const t=i.get(Xd(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),n.push(e.name))}if(s.length<3)return null;const o=function(t){if(t.length<3)return null;let e=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 t){if(![u,_,m,g].every(Number.isFinite))return null;e+=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=[e,i,s,i,n,o,s,o,a],_=t=>{const[e,i,s,n,o,a,r,l,c]=u,h=e*(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,(e*c-s*r)/h,(s*n-e*a)/h,(n*l-o*r)/h,(i*r-e*l)/h,(e*o-i*n)/h];return[d[0]*t[0]+d[1]*t[1]+d[2]*t[2],d[3]*t[0]+d[4]*t[1]+d[5]*t[2],d[6]*t[0]+d[7]*t[1]+d[8]*t[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:Bd(o,s)}:null}function Qd(t,e,i){const s=t[t.length-1];if(s&&s[0]===e[0]&&s[1]===e[1])return t;if(t.push(e),t.length<=600)return t;let n=function(t,e){if(t.length<3)return t.slice();const i=new Uint8Array(t.length);i[0]=i[t.length-1]=1;const s=[[0,t.length-1]];for(;s.length;){const[n,o]=s.pop(),[a,r]=t[n],[l,c]=t[o],h=l-a,d=c-r,p=Math.hypot(h,d)||1e-9;let u=0,_=-1;for(let e=n+1;e<o;e++){const i=Math.abs((t[e][0]-a)*d-(t[e][1]-r)*h)/p;i>u&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([n,_],[_,o]))}const n=[];for(let e=0;e<t.length;e++)i[e]&&n.push(t[e]);return n}(t,i);return n.length>600&&(n=n.filter((t,e)=>e%2==0||e===n.length-1)),n}function tp(t){return"cleaning"===t||"returning"===t||"on"===t}const ep={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function ip(t){const[e,i]=ep[t.rot]||[1,0],s=t.mir?-1:1;return[t.s*e*s,-t.s*i,t.ox,t.s*i*s,t.s*e,t.oy]}function sp(t){const e=t[0]*t[4]-t[1]*t[3];if(!Number.isFinite(e)||Math.abs(e)<1e-12)return null;const i=e<0,s=Math.sqrt(Math.abs(e));let n=180*Math.atan2(-t[1],t[4])/Math.PI;return n=(90*Math.round(n/90)%360+360)%360,{ox:t[2],oy:t[5],s:s,rot:n,mir:i}}function np(t,e,i,s){const[n,o]=Ld(ip(e),i,s),a=ip({...t,ox:0,oy:0}),[r,l]=Ld(a,i,s);return{...t,ox:n-r,oy:o-l}}function op(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}const ap={offStates:["off"],unknownUsesToggle:!0},rp={light:ap,switch:ap,fan:ap,humidifier:ap,input_boolean:ap,automation:ap,remote:ap,group:ap,climate:{...ap,featureMasks:{turn_on:256,turn_off:128,toggle:384}},media_player:{...ap,featureMasks:{turn_on:128,turn_off:256,toggle:384}},siren:{...ap,featureMasks:{turn_on:1,turn_off:2,toggle:3}},vacuum:{...ap,featureMasks:{turn_on:1,turn_off:2,toggle:3}},water_heater:{...ap,featureMasks:{turn_on:8,turn_off:8,toggle:8}},camera:{...ap,featureMasks:{turn_on:1,turn_off:1,toggle:1}}};function lp(t){return t.slice(0,t.indexOf("."))}function cp(t,e,i){if(!t?.services||"object"!=typeof t.services)return!1;const s=t.services?.[e];return!!s&&Object.prototype.hasOwnProperty.call(s,i)}function hp(t,e,i){return cp(t,e,i)?{domain:e,service:i}:cp(t,"homeassistant",i)?{domain:"homeassistant",service:i}:null}function dp(t,e){return t?.entities?.[e]||null}function pp(t,e,i){const s=t?.states?.[i],n=dp(e,i);return s?.attributes?.friendly_name||n?.name||n?.original_name||i}function up(t,e){const i=dp(t,e);if(null!=i?.disabled_by)return!0;const s=i?.device_id?t?.devices?.[i.device_id]:null;return null!=s?.disabled_by}function _p(t,e){const i=t?.attributes?.supported_features;if(null==i||""===i)return!1;const s=Number(i);return Number.isFinite(s)&&(s&e)===e}function mp(t,e,i){const s=lp(i);if("lock"===s||"alarm_control_panel"===s)return!0;if("cover"!==s)return!1;const n=t?.states?.[i],o=dp(e,i),a=String(n?.attributes?.device_class||o?.device_class||o?.original_device_class||"");return Gi.has(a)}function gp(t,e,i,s,n){return{ref:i,entityId:s,name:s?pp(t,e,s):null,reason:n}}function fp(t,e,i,s,n,o=null){return{target:null,skipped:gp(t,e,i,s,n),semantics:o,nextEffect:null,command:null}}function vp(t,e,i,s,n=i){const o=lp(i);if("cover"!==o&&"valve"!==o)return function(t,e,i,s,n=i){const o=lp(i),a=t?.states?.[i];if(up(e,i))return fp(t,e,n,i,"ha-disabled","power");if(mp(t,e,i))return fp(t,e,n,i,"secure");if(!a)return fp(t,e,n,i,"missing","power");if("unavailable"===a.state)return fp(t,e,n,i,"unavailable","power");const r=rp[o];if(!r)return fp(t,e,n,i,"unsupported");let l,c;if("unknown"===a.state||""===a.state){if(!r.unknownUsesToggle)return fp(t,e,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&&!_p(a,h))return fp(t,e,n,i,"unsupported","power");const d=hp(t,o,c);return d?{target:{entityId:i,name:pp(t,e,i),state:String(a.state||""),via:s},skipped:null,semantics:"power",nextEffect:l,command:{...d,data:{entity_id:i}}}:fp(t,e,n,i,"unsupported","power")}(t,e,i,s,n);if(up(e,i))return fp(t,e,n,i,"ha-disabled",o);if(mp(t,e,i))return fp(t,e,n,i,"secure",o);const a=t?.states?.[i];if(!a)return fp(t,e,n,i,"missing",o);if("unavailable"===a.state)return fp(t,e,n,i,"unavailable",o);const r=function(t,e,i){const s=String(i?.state||""),n="closed"===s?{service:`open_${e}`,effect:"open",feature:1}:"open"===s?{service:`close_${e}`,effect:"close",feature:2}:"opening"===s||"closing"===s?{service:`stop_${e}`,effect:"stop",feature:8}:null;return n&&_p(i,n.feature)&&cp(t,e,n.service)?n:_p(i,3)&&cp(t,e,"toggle")?{service:"toggle",effect:"toggle"}:null}(t,o,a);if(!r)return fp(t,e,n,i,"unsupported",o);return{target:{entityId:i,name:pp(t,e,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 yp(t){return"entity"===t.bindingKind?t.bindingRef&&Ms(t.bindingRef)?[t.bindingRef]:[]:"virtual"===t.bindingKind?[]:to(t)}function bp(t){const e=t.marker?.toggle_entity;return e&&yp(t).includes(e)?e:null}function wp(t,e,i){const s=function(t,e){if("entity"===t.bindingKind&&t.bindingRef){const e=t.marker?.light_entity?eo(t):null;return[...new Set([e,t.bindingRef].filter(t=>!!t))]}const i=t.entities.length?t.entities:t.allEntities||[],s=t.marker?.light_entity||t.primary&&Ms(t.primary)?eo(t):null;return[...new Set([s,...Yn(e,i)].filter(t=>!!t))]}(i,e);if(!s.length)return null;const n="entity"===i.bindingKind?"binding":"device-role";if("binding"===n)return vp(t,e,s[0],n);let o=null,a=null;for(const i of s){const s=vp(t,e,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 kp(t){const e=t.skipped?.reason;return"missing"===e?"unavailable":e||"unsupported"}function xp(t,e){return{origin:t,kind:"none",semantics:null,targets:[],skippedTargets:[],noneReason:e,nextEffect:null,command:null}}function $p(t,e){return"cover"===t||"toggle"===t?"toggle":"more-info"===t||"run"===t||"info"===t?t:(null==t||""===t)&&"light"===e?"toggle":"info"}function Sp(t,e){return{origin:t,kind:"single",semantics:e.semantics,targets:e.target?[e.target]:[],skippedTargets:e.skipped?[e.skipped]:[],noneReason:e.command?null:kp(e),nextEffect:e.nextEffect,command:e.command}}function Mp(t,e,i,s=[]){const n=new Map,o=[],a=new Set,r=t=>{const e=`${t.ref}\n${t.entityId||""}\n${t.reason}`;a.has(e)||(a.add(e),o.push(t))};for(const t of s)r(t);for(const s of i){const i=vp(t,e,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(t=>"secure"===t.reason)?"secure":"configured-targets-missing",nextEffect:null,command:null};const c=l.some(t=>"on"===t.state)?"turn_off":"turn_on",h=hp(t,"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(t=>t.entityId)}}}:{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:[],skippedTargets:[...o,...l.map(i=>gp(t,e,i.entityId,i.entityId,"unsupported"))],noneReason:"unsupported",nextEffect:null,command:null}}function Cp(t){return String(t.marker?.id||t.id||"")}function Dp(t){const{hass:e,device:i}=t,s=t.registryHass||e,n=function(t){return"toggle"===t.tapAction?"explicit-toggle":"cover"===t.tapAction?"legacy-cover":!t.tapAction&&t.primary?.startsWith("light.")?"default-light":null}(i);if(!n)return null;const o=i.marker;if("explicit-toggle"===n&&yn(o)){const e=String(o?.id||i.id||""),s=ro(t.devices).get(e);if(s)return function(t,e){const i=t.registryHass||t.hass;return Mp(t.hass,i,e.driverEids.map(t=>({entityId:t,via:"control-marker-driver",ref:`marker:${e.markerId}`})))}(t,s);const a=bn(o,t.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(t,e=null){const{hass:i,device:s,devices:n}=t,o=t.registryHass||i,a=Zn(s.marker?.binding,s.marker?.controls??s.controls,s.entities),r=t.lightSources||ho(i,n,null,t.virtualLights),l=new Map;for(const t of r){if(!t.key.startsWith("marker:"))continue;const e=l.get(t.key)||[];e.push(t),l.set(t.key,e)}const c=ro(n),h=new Map;for(const t of n){const e=String(t.marker?.id||t.id||"");e&&h.set(e,t)}const d=[],p=[];e&&d.push({entityId:e,via:"entity"===s.bindingKind?"binding":"device-role",ref:e});for(const t of a){if(!t.startsWith("marker:")){Ms(t)?d.push({entityId:t,via:"control-entity",ref:t}):p.push(gp(i,o,t,t.includes(".")?t:null,"unsupported"));continue}const e=l.get(t)||[];if(!e.length){const e=h.get(t.slice(7));if("ha_disabled"===e?.bindingStatus?.kind){const s=e.bindingStatus.allEntityIds[0]||null;p.push({ref:t,entityId:s,name:e.name||(s?pp(i,o,s):null),reason:"ha-disabled"})}else p.push(gp(i,o,t,null,"missing"));continue}const n=[...new Set(e.flatMap(t=>t.serviceEids))];if(n.length){for(const e of n)d.push({entityId:e,via:"control-entity",ref:t});continue}const a=e.some(t=>t.passive),r=t.slice(7),u=Cp(s),_=c.get(r)?.controllers.find(t=>Cp(t.device)===u);if(a&&_?.driverEids.length)for(const e of _.driverEids)d.push({entityId:e,via:"control-marker-driver",ref:t});else p.push(gp(i,o,t,null,a?"missing":"unsupported"))}return Mp(i,o,d,p)}(t,bp(i))}if("legacy-cover"===n){const t=i.entities.find(t=>t.startsWith("cover."))||i.allEntities?.find(t=>t.startsWith("cover."));return t?Sp(n,vp(e,s,t,"device-role")):xp(n,"no-actionable-entity")}const a=bp(i),r=a?vp(e,s,a,"entity"===i.bindingKind?"binding":"device-role"):wp(e,s,i);return r?Sp(n,r):xp(n,i.virtual||"virtual"===i.bindingKind?"no-actionable-entity":"no-binding")}function Pp(t){if(!t)return[];const e=Array.isArray(t.data.entity_id)?t.data.entity_id:[t.data.entity_id];return[...new Set(e)].sort()}function Tp(t){return t?t.operation?t.operation:t.command?{kind:"ha-service",command:t.command}:null:null}const Rp=new Set(["missing","ha-disabled","unavailable"]);function Ip(t,e){const i=Tp(t),s=Tp(e);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(t,e){const i=Pp(t),s=Pp(e);return i.length===s.length&&i.every((t,e)=>t===s[e])}(i.command,s.command))}const Ap=t=>!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]);function zp(t,e=5,i=5){return ml(t.cm,e,i)/2}function Fp(t,e,i=1,s=5,n=5,o=0){const a=t.host;if(!a||"partition"!==a.kind||"string"!=typeof a.id||!a.id)return{resolved:null,reason:"invalid-host"};const r=e.find(t=>t.id===a.id);if(!r)return{resolved:null,reason:"missing-partition"};if(!Ap(r.a)||!Ap(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(t.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:t,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 Ep(t,e,i=1,s=5,n=5){return Fp(t,e,i,s,n,0)}function Np(t,e,i=1,s=5,n=5){const o=t.host,a="partition"===o?.kind?e.find(t=>t.id===o.id):void 0;if(!a)return Ep(t,e,i,s,n);const r=Fp(t,e,i,s,n,zp(a,s,n));return"does-not-fit"===r.reason?{resolved:null,reason:"does-not-fit-jamb"}:r}function Op(t,e){return!t||(t.length!==e.length||t.host?.kind!==e.host?.kind||t.host?.id!==e.host?.id||t.host?.t!==e.host?.t)}function Hp(t){const{center:e,length:i,axis:s,host:n}=t,o=i/2;return{hostId:n.id,a:[e[0]-s.ux*o,e[1]-s.uy*o],b:[e[0]+s.ux*o,e[1]+s.uy*o],depth:t.depth}}function Lp(t,e=!1){const i=e?1:-1,s=t.depth/2;return{ox:-t.axis.uy*i*s,oy:t.axis.ux*i*s,cm:t.partition.cm,side:i}}function Bp(t,e,i){return t.flatMap(t=>!Ap(t.a)||!Ap(t.b)||Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])<=1e-9?[]:[{roomId:"",a:[t.a[0],t.a[1]],b:[t.b[0],t.b[1]],key:`partition:${t.id}`,kind:"outer",cm:t.cm,open:!1,half:ml(t.cm,e,i)/2,partitionHost:{kind:"partition",id:t.id}}])}function qp(t,e,i){const{center:s,length:n,axis:o}=t,a=n/2,r=e.flatMap(t=>{if(!t.kind||t.open)return[];const e=t.b[0]-t.a[0],n=t.b[1]-t.a[1],r=Math.hypot(e,n);if(!(r>i))return[];const l=e/r,c=n/r;if(Math.abs(o.ux*c-o.uy*l)>1e-6)return[];if(Math.abs((s[0]-t.a[0])*c-(s[1]-t.a[1])*l)>i)return[];const h=t=>(t[0]-s[0])*o.ux+(t[1]-s[1])*o.uy,d=h(t.a),p=h(t.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((t,e)=>t[0]-e[0]||t[1]-e[1]);let l=-a;for(const[t,e]of r){if(t>l+i)return!1;if(l=Math.max(l,e),l>=a-i)return!0}return!1}function Wp(t,e,i){return{...t,x:e.center[0]/i,y:e.center[1]/i,angle:e.angle}}const jp=.001;function Gp(t){return!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function Up(t){return`${t[0].toFixed(6)},${t[1].toFixed(6)}`}function Vp(t,e,i){return Math.abs(t[0]-e[0])<i&&Math.abs(t[1]-e[1])<i}function Kp(t,e){const i=function(t,e){return t[0]-e[0]||t[1]-e[1]}(t,e)<=0?t:e,s=i===t?e:t;return[[i[0],i[1]],[s[0],s[1]]]}function Yp(t){const[e,i]=Kp(t.a,t.b);return`${t.kind}|${t.id}|${Up(e)}|${Up(i)}`}function Jp(t,e,i){const[s,n]=Kp(e,i);return`${Yp(t)}|${Up(s)}|${Up(n)}`}function Xp(t){return"room"===t?0:"draft"===t?1:2}function Zp(t,e,i){return Vp(t,[e[0],e[1]],i)||Vp(t,[e[2],e[3]],i)}function Qp(t){const e=t.epsilon??jp,i=[];for(const[e,s]of pi(t.space.rooms).entries())s.length<4||i.push({a:[s[0],s[1]],b:[s[2],s[3]],kind:"room",id:`room-edge-${e}`,cuts:[]});for(const s of t.space.room_drafts||[])if(s.id!==t.activeDraftId)for(let t=0;t+1<s.points.length;t++){const n=s.points[t],o=s.points[t+1];Gp(n)&&Gp(o)&&!Vp(n,o,e)&&i.push({a:[n[0],n[1]],b:[o[0],o[1]],kind:"draft",id:`${s.id}:${t}`,cuts:[]})}for(const s of t.space.partitions||[])Gp(s.a)&&Gp(s.b)&&!Vp(s.a,s.b,e)&&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(t=>"room"!==t.kind&&i.some(i=>i!==t&&function(t,e,i){const s=t.b[0]-t.a[0],n=t.b[1]-t.a[1],o=Math.hypot(s,n);if(!(o>i))return!1;const a=s/o,r=n/o,l=e=>Math.abs((e[0]-t.a[0])*r-(e[1]-t.a[1])*a);if(l(e.a)>i||l(e.b)>i)return!1;const c=(e.a[0]-t.a[0])*a+(e.a[1]-t.a[1])*r,h=(e.b[0]-t.a[0])*a+(e.b[1]-t.a[1])*r;return Math.min(o,Math.max(c,h))-Math.max(0,Math.min(c,h))>i}(t,i,e))),n=s.map(t=>{const[e,i]=Kp(t.a,t.b);return{a:e,b:i,key:`hidden|${Jp(t,e,i)}`,sourceKind:t.kind,sourceId:t.id}}).sort((t,e)=>t.key.localeCompare(e.key)),o=s.flatMap(t=>[t.a,t.b].map((e,i)=>({point:[e[0],e[1]],key:`hidden|${Yp(t)}|endpoint-${i}`,sourceKind:t.kind,sourceId:t.id}))).sort((t,e)=>t.key.localeCompare(e.key));return{segments:n,endpoints:o}}function tu(t){const e=t.epsilon??jp,i=t.roomCuts||[],s=new Map;for(const e of t.partitionCuts||[]){if("string"!=typeof e.hostId||!e.hostId||!Gp(e.a)||!Gp(e.b))continue;const t=s.get(e.hostId)||[];t.push([e.a[0],e.a[1],e.b[0],e.b[1]]),s.set(e.hostId,t)}const n=[];for(const[e,s]of pi(t.space.rooms).entries())s.length<4||n.push({a:[s[0],s[1]],b:[s[2],s[3]],kind:"room",id:`room-edge-${e}`,cuts:i});for(const e of t.space.room_drafts||[])if(e.id!==t.activeDraftId)for(let t=0;t+1<e.points.length;t++){const i=e.points[t],s=e.points[t+1];Gp(i)&&Gp(s)&&n.push({a:[i[0],i[1]],b:[s[0],s[1]],kind:"draft",id:`${e.id}:${t}`,cuts:[]})}for(const e of t.space.partitions||[])Gp(e.a)&&Gp(e.b)&&n.push({a:[e.a[0],e.a[1]],b:[e.b[0],e.b[1]],kind:"partition",id:e.id,cuts:s.get(e.id)||[]});const o=new Map,a=new Map;for(const t of n){const i=Ds([[t.a[0],t.a[1],t.b[0],t.b[1]]],t.cuts,e);if(i.length){for(const s of i){if(s.length<4)continue;const[i,n]=Kp([s[0],s[1]],[s[2],s[3]]);if(Vp(i,n,e))continue;const a=`${Up(i)}|${Up(n)}`,r={a:i,b:n,key:Jp(t,i,n),sourceKind:t.kind,sourceId:t.id},l=o.get(a);(!l||Xp(r.sourceKind)<Xp(l.sourceKind)||Xp(r.sourceKind)===Xp(l.sourceKind)&&r.key.localeCompare(l.key)<0)&&o.set(a,r)}for(const s of[t.a,t.b]){if(!i.some(t=>Zp(s,t,e)))continue;const t=Up(s);a.has(t)||a.set(t,{point:[s[0],s[1]],key:t})}}}return{segments:[...o.values()].sort((t,e)=>t.key.localeCompare(e.key)),endpoints:[...a.values()].sort((t,e)=>t.key.localeCompare(e.key))}}function eu(t,e,i){return e.some(e=>Gp(e)&&Vp(t,e,i))}function iu(t,e,i){if(!i)return!0;const s=t-i.distance;return s<-1e-9||Math.abs(s)<=1e-9&&e.localeCompare(i.key)<0}function su(t,e,i){const s=i.epsilon??jp,n=i.excludePoints||[],o=[...t.endpoints,...(i.extraEndpoints||[]).filter(t=>Gp(t.point)).map(t=>({point:[t.point[0],t.point[1]],key:t.key}))],a=new Map;for(const t of o){if(eu(t.point,n,s))continue;const e=Up(t.point),i=a.get(e);(!i||t.key.localeCompare(i.key)<0)&&a.set(e,t)}return[...a.values()].map(t=>({kind:"endpoint",point:[...t.point],key:t.key,distance:Math.hypot(e[0]-t.point[0],e[1]-t.point[1])})).filter(t=>t.distance<=i.tolerance).sort((t,e)=>t.distance-e.distance||t.key.localeCompare(e.key))}function nu(t,e){if(!t.length)return null;if(e>0){const i=new Set;for(let s=0;s<t.length;s++)for(let n=s+1;n<t.length;n++)Math.hypot(t[s].point[0]-t[n].point[0],t[s].point[1]-t[n].point[1])<e&&(i.add(t[s].key),i.add(t[n].key));if(i.size>1)return{kind:"ambiguous",candidate:null,conflicts:t.filter(t=>i.has(t.key)).map(t=>({point:[...t.point],key:t.key}))}}return{kind:"resolved",candidate:t[0],conflicts:[]}}function ou(t,e,i){const s=t.b[0]-t.a[0],n=t.b[1]-t.a[1],o=Math.hypot(s,n);if(!(o>0))return[...t.a];const a=s/o,r=n/o,l=Math.max(0,Math.min(o,(e[0]-t.a[0])*a+(e[1]-t.a[1])*r)),c=i>0?Math.max(0,Math.min(o,Math.round(l/i)*i)):l;return[t.a[0]+a*c,t.a[1]+r*c]}function au(t,e,i){if(!(Gp(e)&&Gp(i.anchor)&&i.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const s=i.epsilon??jp,n=function(t,e){const i=e[0]-t[0],s=e[1]-t[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,e);if(!n)return{kind:"none",candidate:null,conflicts:[]};const o=su(t,e,i).filter(t=>function(t,e,i,s){const n=t[0]-e[0],o=t[1]-e[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)}(t.point,i.anchor,n,s)),a=nu(o,i.distinguishTolerance||0);if(a)return a;let r=null;for(const o of t.segments){const t=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]*t;let d=null;if(Math.abs(h)<=s*Math.max(Math.hypot(t,a),1)){if(Math.abs(l*n[1]-c*n[0])>s*Math.max(Math.hypot(l,c),1))continue;const t=[o.a,o.b].map(t=>({point:t,along:(t[0]-i.anchor[0])*n[0]+(t[1]-i.anchor[1])*n[1]})).filter(t=>t.along>s).sort((t,e)=>t.along-e.along);t.length&&(d=[...t[0].point])}else{const e=(l*a-c*t)/h,o=(l*n[1]-c*n[0])/h;e>s&&o>=-s&&o<=1+s&&(d=[i.anchor[0]+n[0]*e,i.anchor[1]+n[1]*e])}if(!d)continue;const p=Math.hypot(e[0]-d[0],e[1]-d[1]);p>i.tolerance||eu(d,i.excludePoints||[],s)||!iu(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 ru=.001;function lu(t,e,i,s){const n=Number.isFinite(t)&&t>0?Math.floor(t):0,o=t=>"number"==typeof t&&Number.isFinite(t)&&t>0?t:null,a=o(i),r=o(s)??1,l=[];let c=null;for(let t=0;t<n;t++){const i=o(e?.[t])??(t===n-1?a??c??r:c??a??r);l.push(i),c=i}return l}function cu(t,e){const i=[];for(let s=0;s+1<t.length;s++){const n=t[s],o=t[s+1];!hu(n)||!hu(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:e[s]})}return i}function hu(t){return!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function du(t,e,i,s){return t*s-e*i}function pu(t){let e=0;for(let i=0;i<t.length;i++){const s=t[i],n=t[(i+1)%t.length];e+=s[0]*n[1]-n[0]*s[1]}return e/2}function uu(t,e){const i=Math.max(e,Number.EPSILON),s=Math.round(t[0]/i)*i,n=Math.round(t[1]/i)*i;return[Object.is(s,-0)?0:s,Object.is(n,-0)?0:n]}function _u(t,e){const i=uu(t,e);return`${Math.round(i[0]/e)},${Math.round(i[1]/e)}`}function mu(t,e){return t.localeCompare(e)<=0?`${t}|${e}`:`${e}|${t}`}function gu(t){if(!t.length)return"";const e=[];for(const i of[t,[...t].reverse()])for(let t=0;t<i.length;t++)e.push([...i.slice(t),...i.slice(0,t)].join(";"));return e.sort((t,e)=>t.localeCompare(e)),e[0]}function fu(t,e,i){const s=[...t];for(let t=!0;t&&s.length>=3;){t=!1;for(let n=0;n<s.length;n++){const o=e.get(s[(n-1+s.length)%s.length]),a=e.get(s[n]),r=e.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(du(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),t=!0;break}}}return s}function vu(t,e){const i=t.map(t=>Math.max(0,Math.min(1,t))).sort((t,e)=>t-e),s=[];for(const t of i)(!s.length||Math.abs(t-s[s.length-1])>e)&&s.push(t);return s}function yu(t,e){return[t.a[0]+(t.b[0]-t.a[0])*e,t.a[1]+(t.b[1]-t.a[1])*e]}function bu(t,e,i){const s=function(t,e){const i=e.b[0]-e.a[0],s=e.b[1]-e.a[1],n=i*i+s*s;return n>0?((t[0]-e.a[0])*i+(t[1]-e.a[1])*s)/n:0}(t,e);if(s<-i||s>1+i)return null;const n=yu(e,s);return Math.hypot(n[0]-t[0],n[1]-t[1])<=i?Math.max(0,Math.min(1,s)):null}function wu(t,e,i,s,n){const o=t.b[0]-t.a[0],a=t.b[1]-t.a[1],r=e.b[0]-e.a[0],l=e.b[1]-e.a[1],c=e.a[0]-t.a[0],h=e.a[1]-t.a[1],d=du(o,a,r,l),p=Math.max(Math.hypot(o,a),Math.hypot(r,l),1);if(Math.abs(d)>n*p){const t=du(c,h,r,l)/d,e=du(c,h,o,a)/d;return void(t>=-n&&t<=1+n&&e>=-n&&e<=1+n&&(i.push(Math.max(0,Math.min(1,t))),s.push(Math.max(0,Math.min(1,e)))))}if(!(Math.abs(du(c,h,o,a))>n*p)){for(const s of[e.a,e.b]){const e=bu(s,t,n);null!=e&&i.push(e)}for(const i of[t.a,t.b]){const t=bu(i,e,n);null!=t&&s.push(t)}}}function ku(t){let e=t+1|0;return e^=e<<13,e^=e>>>17,e^=e<<5,e>>>0}function xu(t,e){return t.minY-e.minY||t.index-e.index}function $u(t){return t.subtreeMaxY=Math.max(t.item.maxY,t.left?.subtreeMaxY??-1/0,t.right?.subtreeMaxY??-1/0),t}function Su(t){const e=t.right;return t.right=e.left,e.left=$u(t),$u(e)}function Mu(t){const e=t.left;return t.left=e.right,e.right=$u(t),$u(e)}function Cu(t,e){return t?(xu(e,t.item)<0?(t.left=Cu(t.left,e),t.left.priority<t.priority&&(t=Mu(t))):(t.right=Cu(t.right,e),t.right.priority<t.priority&&(t=Su(t))),$u(t)):{item:e,priority:ku(e.index),subtreeMaxY:e.maxY,left:null,right:null}}function Du(t,e){if(!t)return null;const i=xu(e,t.item);if(i<0)t.left=Du(t.left,e);else if(i>0)t.right=Du(t.right,e);else{if(!t.left)return t.right;if(!t.right)return t.left;t.left.priority<t.right.priority?(t=Mu(t)).right=Du(t.right,e):(t=Su(t)).left=Du(t.left,e)}return $u(t)}function Pu(t,e,i,s){!t||t.subtreeMaxY<e||(null!=t.left?.subtreeMaxY&&t.left.subtreeMaxY>=e&&Pu(t.left,e,i,s),t.item.minY<=i&&t.item.maxY>=e&&s.push(t.item),t.item.minY<=i&&Pu(t.right,e,i,s))}function Tu(t,e=.001){const i=Number.isFinite(e)&&e>0?e:ru,s=t.filter(t=>hu(t.a)&&hu(t.b)&&"string"==typeof t.key&&t.key.length>0&&Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])>i),n=s.map(()=>[0,1]);!function(t,e,i){const s=t.map((t,e)=>({index:e,minX:Math.min(t.a[0],t.b[0])-i,maxX:Math.max(t.a[0],t.b[0])+i,minY:Math.min(t.a[1],t.b[1])-i,maxY:Math.max(t.a[1],t.b[1])+i})),n=[...s].sort((t,e)=>t.minX-e.minX||t.minY-e.minY||t.index-e.index),o=[...s].sort((t,e)=>t.maxX-e.maxX||t.index-e.index),a=new Set;let r=null,l=0;for(const s of n){for(;l<o.length&&o[l].maxX<s.minX;){const t=o[l++];a.delete(t.index)&&(r=Du(r,t))}const n=[];Pu(r,s.minY,s.maxY,n),n.sort((t,e)=>t.index-e.index);for(const o of n)wu(t[o.index],t[s.index],e[o.index],e[s.index],i);a.add(s.index),r=Cu(r,s)}}(s,n,i);const o=new Map;for(let t=0;t<s.length;t++){const e=s[t],a=Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1]),r=vu(n[t],i/Math.max(a,1));for(let t=0;t+1<r.length;t++){const s=uu(yu(e,r[t]),i),n=uu(yu(e,r[t+1]),i);if(Math.hypot(n[0]-s[0],n[1]-s[1])<=i)continue;const a=_u(s,i),l=_u(n,i),c=mu(a,l),h=o.get(c);h?h.sourceKeys.add(e.key):o.set(c,{a:a.localeCompare(l)<=0?s:n,b:a.localeCompare(l)<=0?n:s,sourceKeys:new Set([e.key])})}}return[...o.entries()].map(([t,e])=>({key:t,a:e.a,b:e.b,sourceKeys:[...e.sourceKeys].sort((t,e)=>t.localeCompare(e))})).sort((t,e)=>t.key.localeCompare(e.key))}function Ru(t,e=.001){const i=Number.isFinite(e)&&e>0?e:ru,s=Tu(t,i),n=new Map,o=new Map,a=new Map(s.map(t=>[t.key,t]));for(const t of s){const e=_u(t.a,i),s=_u(t.b,i);n.set(e,t.a),n.set(s,t.b),o.has(e)||o.set(e,new Set),o.has(s)||o.set(s,new Set),o.get(e).add(s),o.get(s).add(e)}const r=new Map;for(const[t,e]of o){const i=n.get(t);r.set(t,[...e].sort((t,e)=>{const s=n.get(t),o=n.get(e);return Math.atan2(s[1]-i[1],s[0]-i[0])-Math.atan2(o[1]-i[1],o[0]-i[0])||t.localeCompare(e)}))}const l=new Set,c=new Map,h=(t,e)=>`${t}>${e}`;for(const t of s){const e=[_u(t.a,i),_u(t.b,i)];for(const[t,o]of[e,[e[1],e[0]]]){if(l.has(h(t,o)))continue;const e=[],d=[];let p=t,u=o,_=!1;for(let i=0;i<=2*s.length+2;i++){const i=h(p,u);if(l.has(i)){_=p===t&&u===o;break}l.add(i),e.push(p),d.push(mu(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===t&&u===o){_=!0;break}}if(!_||new Set(e).size<3||new Set(e).size!==e.length)continue;const m=e.map(t=>n.get(t)),g=pu(m);if(!(g>i*i))continue;const f=gu(fu(e,n,i)),v=new Set;for(const t of d)for(const e of a.get(t)?.sourceKeys||[])v.add(e);const y={ring:m,key:f,area:g,atomKeys:[...d],sourceKeys:[...v].sort((t,e)=>t.localeCompare(e))};c.has(f)||c.set(f,y)}}return{atoms:s,faces:[...c.values()].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key))}}function Iu(t,e,i=.001){if(!hu(e))return null;const s=t.faces.filter(t=>{if(t.ring.some((s,n)=>function(t,e,i,s){const n=i[0]-e[0],o=i[1]-e[1],a=n*n+o*o;if(!(a>0))return Math.hypot(t[0]-e[0],t[1]-e[1])<=s;const r=((t[0]-e[0])*n+(t[1]-e[1])*o)/a;return!(r<0||r>1)&&Math.hypot(e[0]+n*r-t[0],e[1]+o*r-t[1])<=s}(e,s,t.ring[(n+1)%t.ring.length],i)))return!1;let s=!1;for(let i=0,n=t.ring.length-1;i<t.ring.length;n=i++){const o=t.ring[i],a=t.ring[n];o[1]>e[1]!=a[1]>e[1]&&e[0]<(a[0]-o[0])*(e[1]-o[1])/(a[1]-o[1])+o[0]&&(s=!s)}return s});return[...s].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key))[0]||null}function Au(t,e,i){const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n,a=o>0?Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*n)/o)):0;return Math.hypot(e[0]+s*a-t[0],e[1]+n*a-t[1])}function zu(t,e,i=.5){let s=Math.abs(t-e)%180;return s>90&&(s=180-s),s<=i}function Fu(t,e,i,s){const n=t.filter(t=>"outer"===t.kind&&!t.open&&t.cm>0).map(t=>({interval:t,reusePartitionId:e.find(e=>function(t,e,i){return!(Math.abs(t.cm-e.cm)>1e-6)&&(zu(180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI,180*Math.atan2(e.b[1]-e.a[1],e.b[0]-e.a[0])/Math.PI,.001)&&Au(e.a,t.a,t.b)<=i&&Au(e.b,t.a,t.b)<=i)}(e,t,s))?.id||null})),o=new Map;for(const e of i){if("partition"===e.host?.kind)continue;const i=t.map(t=>{const i=180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI;return{interval:t,distance:zu(e.angle,i)?Au([e.x,e.y],t.a,t.b):1/0}}).filter(t=>t.distance<=s).sort((t,e)=>t.distance-e.distance||t.interval.key.localeCompare(e.interval.key)),n=i[0]?.interval;"outer"===n?.kind&&!n.open&&n.cm>0&&o.set(e.id,n.key)}return{materialize:n,openingIntervals:o,removeOpeningIds:[...o.keys()].sort((t,e)=>t.localeCompare(e))}}function Eu(t,e){const i=e.b[0]-e.a[0],s=e.b[1]-e.a[1],n=i*i+s*s;return n>0?Math.max(0,Math.min(1,((t[0]-e.a[0])*i+(t[1]-e.a[1])*s)/n)):0}const Nu=t=>!t.key.startsWith("static:room|");function Ou(t,e){const i=e.b[0]-e.a[0],s=e.b[1]-e.a[1],n=i*i+s*s;if(!(n>0))return null;const o=((t[0]-e.a[0])*i+(t[1]-e.a[1])*s)/n,a=[e.a[0]+i*o,e.a[1]+s*o];return{point:a,t:o,distance:Math.hypot(a[0]-t[0],a[1]-t[1])}}function Hu(t,e){return e>0?Math.hypot(t[0]-Math.round(t[0]/e)*e,t[1]-Math.round(t[1]/e)*e):0}function Lu(t,e,i){if(Nu(t.source)!==Nu(e.source))return Nu(t.source)?t:e;const s=Hu(t.point,i),n=Hu(e.point,i);return Math.abs(s-n)>1e-9?s>n?t:e:function(t,e){return t[0]-e[0]||t[1]-e[1]}(t.point,e.point)>0?t:e}function Bu(t){return[t.from[0],t.from[1],t.to[0],t.to[1],t.targetKind].map(t=>"number"==typeof t?t.toFixed(6):t).join("|")}function qu(t,e){return t.map(t=>t.key!==e.sourceKey?t:{...t,[e.endpoint]:[...e.to]})}function Wu(t,e){const i=e.epsilon??.001;if(!(e.maxDistance>=0)||!e.point&&!e.requiredSourceKey)return{kind:"none"};const s=new Map,n=t.flatMap(t=>[{source:t,endpoint:"a",point:t.a},{source:t,endpoint:"b",point:t.b}]);for(let t=0;t<n.length;t++)for(let o=t+1;o<n.length;o++){const a=n[t],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>e.maxDistance)continue;const c=Lu(a,r,e.gridStep||0),h=c===a?r:a;if(!Nu(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(Bu(d),d)}for(const o of n)if(Nu(o.source))for(const n of t){if(n===o.source)continue;const t=Ou(o.point,n);if(!t||t.t<=i||t.t>=1-i||!(t.distance>i)||t.distance>e.maxDistance)continue;const a={sourceKey:o.source.key,endpoint:o.endpoint,from:[o.point[0],o.point[1]],to:t.point,targetSourceKey:n.key,targetKind:"line",distance:t.distance};s.set(Bu(a),a)}const o=[];for(const n of s.values()){const s=Ru(qu(t,n),i),a=e.point?Iu(s,e.point,i):s.faces.find(t=>!e.requiredSourceKey||t.sourceKeys.includes(e.requiredSourceKey))||null;a&&o.push({proposal:n,face:a})}if(o.sort((t,e)=>t.proposal.distance-e.proposal.distance||Bu(t.proposal).localeCompare(Bu(e.proposal))),!o.length)return{kind:"none"};return new Set(o.map(t=>`${Bu(t.proposal)}|${t.face.key}`)).size>1?{kind:"ambiguous",proposals:o.map(t=>t.proposal)}:{kind:"repair",...o[0]}}const ju=1e-5;function Gu(t){const e=t.map(()=>[]);for(let i=0;i<t.length;i++){const s=t[i],n=s[2]-s[0],o=s[3]-s[1];for(let a=i+1;a<t.length;a++){const r=t[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||(e[i].push(d),e[a].push(p))}}const i=[];for(let s=0;s<t.length;s++){const n=t[s];if(!e[s].length){i.push(n);continue}const o=[0,...e[s].sort((t,e)=>t-e),1];for(let t=1;t<o.length;t++)o[t]-o[t-1]<1e-9||i.push([n[0]+(n[2]-n[0])*o[t-1],n[1]+(n[3]-n[1])*o[t-1],n[0]+(n[2]-n[0])*o[t],n[1]+(n[3]-n[1])*o[t]])}return i}function Uu(t){const e=[];for(let i=0;i<t.length;i++){const s=t[i],n=t[(i+1)%t.length];s&&n&&(Math.hypot(n[0]-s[0],n[1]-s[1])<1e-9||e.push([s[0],s[1],n[0],n[1]]))}return e}const Vu=(t,e)=>{const i=e[2]-e[0],s=e[3]-e[1],n=i*i+s*s;if(!(n>0))return Math.hypot(t[0]-e[0],t[1]-e[1]);const o=Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*s)/n));return Math.hypot(t[0]-(e[0]+o*i),t[1]-(e[1]+o*s))},Ku=(t,e,i,s)=>{const n=s[2]-s[0],o=s[3]-s[1],a=e*o-i*n;if(Math.abs(a)<1e-12)return 1/0;const r=s[0]-t[0],l=s[1]-t[1],c=(r*o-l*n)/a;if(!(c>1e-9))return 1/0;const h=(r*i-l*e)/a;return h<-1e-9||h>1+1e-9?1/0:c};function Yu(t,e,i,s=96){if(!(e>0&&Number.isFinite(t[0])&&Number.isFinite(t[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=Vu(t,s);if(i<1e-7)return[];i>e||n.push(s)}const o=[],a=Math.max(12,Math.round(s));for(let t=0;t<a;t++)o.push(t/a*Math.PI*2-Math.PI);for(const e of n)for(const i of[[e[0],e[1]],[e[2],e[3]]]){const e=Math.atan2(i[1]-t[1],i[0]-t[0]);o.push(e-ju,e,e+ju)}const r=2*Math.PI;for(let t=0;t<o.length;t++)o[t]=(o[t]%r+r)%r;o.sort((t,e)=>t-e);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=e;for(const e of n){const i=Ku(t,s,o,e);i<a&&(a=i)}l.push([t[0]+s*a,t[1]+o*a])}return l.length>=3?l:[]}class Ju extends ht{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return ra(this.hass,this._config?.language)}get _floorToken(){const t=this._config?.floor;return"number"==typeof t?`__houseplan_yaml_floor_index__:${String(t)}`:null}get _formData(){const t={...this._config},e=this._floorToken;return e?t.floor=e:Object.prototype.hasOwnProperty.call(t,"floor")||(t.floor=""),t}get _schema(){const t=this._spaces||[],e=this._lang,i=[{value:"",label:la(e,"editor.floor_none")}],s=this._floorToken;s&&i.push({value:s,label:la(e,"editor.floor_index",{index:String(this._config?.floor)})});const n="string"==typeof this._config?.floor?this._config.floor:"";n&&!t.some(t=>t.value===n)&&i.push({value:n,label:n}),i.push(...t);const o="string"==typeof this._config?.default_floor?this._config.default_floor:"",a=[...t];return o&&!t.some(t=>t.value===o)&&a.unshift({value:o,label:o}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:i}}},t.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:a}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:[{value:"",label:la(e,"editor.lang_auto")},{value:"en",label:la(e,"editor.lang_en")},{value:"ru",label:la(e,"editor.lang_ru")}]}}},{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 U;this._loadSpaces();const t=this._lang,e={title:la(t,"editor.title"),floor:la(t,"editor.floor"),default_floor:la(t,"editor.default_floor"),language:la(t,"editor.language"),icon_size:la(t,"editor.icon_size"),show_temperature:la(t,"editor.show_temperature"),live_states:la(t,"editor.live_states"),show_signal:la(t,"editor.show_signal"),kiosk:la(t,"editor.kiosk"),cycle:la(t,"editor.cycle")},i=this._schema,s=function(t,e,i){if(!i||null===e)return null;const s="string"==typeof t?.default_floor?t.default_floor:"";return!s||e.some(t=>t.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),n=t=>W`<ha-form
|
||
.hass=${this.hass}
|
||
.data=${this._formData}
|
||
.schema=${t}
|
||
.computeLabel=${t=>e[t.name]||t.name}
|
||
@value-changed=${this._valueChanged}
|
||
></ha-form>`;return W`
|
||
${n(i.slice(0,3))}
|
||
${s?W`<div class="default-floor-error" role="alert"
|
||
style="color:var(--error-color,#db4437);margin:-4px 0 12px;overflow-wrap:anywhere">
|
||
${la(t,"editor.default_floor_missing",{id:s})}
|
||
</div>`:U}
|
||
${n(i.slice(3))}
|
||
`}_valueChanged(t){const e={...this._config,...t.detail.value};""===e.floor?delete e.floor:e.floor===this._floorToken&&(e.floor=this._config?.floor);const i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:e},this.dispatchEvent(i)}}function Xu(t){const e=t.length/2,i=t.face.cm>0?t.face.cm/t.cellCm*t.gridPitch/2:_h(4,t.cellCm),s="gate"===t.type?Math.sin(10*Math.PI/180)*e:0;return{half:e,jambHalf:i,gateDepth:s,outlineHalf:Math.max(_h(16,t.cellCm),i+_h(8,t.cellCm),s+_h(8,t.cellCm)),hitHalf:Math.max(_h(20,t.cellCm),i+_h(10,t.cellCm),s+_h(12,t.cellCm))}}function Zu(t){if("passage"===t.type)return j``;const e=Math.max(0,Math.min(1,t.amount)),{half:i,jambHalf:s}=Xu(t),n=uh(t.cellCm),o=1.75*n,a=t.flipH?-1:1,r=t.flipV?-1:1,l="gate"===t.type?1:r;t.type,t.flipV,t.angle,t.face;let c;if("window"===t.type){const o=Math.PI/2*i,a=t.face.cm>0?j`<line class="op-glass" x1="0" y1="${-s}" x2="0" y2="${s}"
|
||
stroke="${t.tone}" stroke-width="${1.5*n}"></line>`:j``;c=j`
|
||
<g transform="translate(${0} ${0})">
|
||
<path class="op-arc" d="M 0 0 A ${i} ${i} 0 0 0 ${-i} ${-i}" fill="none"
|
||
stroke="${t.tone}" stroke-dasharray="${o}" stroke-dashoffset="${o*(1-e)}"></path>
|
||
<path class="op-arc" d="M 0 0 A ${i} ${i} 0 0 1 ${i} ${-i}" fill="none"
|
||
stroke="${t.tone}" stroke-dasharray="${o}" stroke-dashoffset="${o*(1-e)}"></path>
|
||
<g transform="translate(${-i} 0)">
|
||
<g class="op-leaf" style="transform:rotate(${-90*e}deg)">
|
||
<rect x="0" y="${-1.5*n}" width="${i}" height="${3*n}" fill="${t.tone}"></rect>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(${i} 0)">
|
||
<g class="op-leaf" style="transform:rotate(${90*e}deg)">
|
||
<rect x="${-i}" y="${-1.5*n}" width="${i}" height="${3*n}" fill="${t.tone}"></rect>
|
||
</g>
|
||
</g>
|
||
${a}
|
||
</g>`}else if("gate"===t.type){const s=10*t.face.side*e;c=j`
|
||
<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="${t.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="${t.tone}"></rect>
|
||
</g>
|
||
</g>
|
||
</g>`}else{const s=Math.PI/2*t.length;c=j`
|
||
<g transform="translate(${0} ${0})">
|
||
<path class="op-arc" d="M ${i} 0 A ${t.length} ${t.length} 0 0 0 ${-i} ${-t.length}" fill="none"
|
||
stroke="${t.tone}" stroke-dasharray="${s}" stroke-dashoffset="${s*(1-e)}"></path>
|
||
<g transform="translate(${-i} 0)">
|
||
<g class="op-leaf" style="transform:rotate(${-90*e}deg)">
|
||
<rect x="0" y="${-o}" width="${t.length}" height="${2*o}" fill="${t.tone}"></rect>
|
||
</g>
|
||
</g>
|
||
</g>`}return j`<g transform="scale(${a} ${l})">
|
||
<line x1="${-i}" y1="${-s}" x2="${-i}" y2="${s}"
|
||
stroke="${t.base}" stroke-width="${2.5*n}"></line>
|
||
<line x1="${i}" y1="${-s}" x2="${i}" y2="${s}"
|
||
stroke="${t.base}" stroke-width="${2.5*n}"></line>
|
||
${c}
|
||
</g>`}function Qu(t){return t<=40?"low":t<180?"mid":"high"}function t_(t){return ai(t)}function e_(t){return"static_icon"===t.display?"neutral":"alarm"===t.visual.status?"alarm":"unavailable"===t.visual.availability?"unavailable":t.lockState?t.lockState:"working"===t.visual.status?"working":"open"===t.visual.status?"open":"neutral"}Ju.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",Ju);const i_={sourceKind:"none",visualSources:[],criticalSources:[],samples:[]};function s_(t,e){const i=t?.entities?.[e],s=t?.states?.[e];return String(i?.name||i?.original_name||s?.attributes?.friendly_name||e)}function n_(t,e){const i=t?.states?.[e];if(!i)return"";if("function"==typeof t?.formatEntityState)try{const e=t.formatEntityState(i);if("string"==typeof e&&e)return e}catch{}return String(i.state??"")}function o_(t,e){if(e.virtual||"virtual"===e.bindingKind||"virtual"===e.marker?.binding)return"available";return(e.entities||[]).some(e=>{const i=String(t?.states?.[e]?.state??"").trim().toLowerCase();return""!==i&&"unknown"!==i&&"unavailable"!==i})?"available":"unavailable"}function a_(t,e,i,s){return{eid:e,role:i,name:s_(t,e),state:String(t?.states?.[e]?.state??""),stateText:n_(t,e),integrationDomain:t?.entities?.[e]?.platform||null,sample:s||cn(t,e)}}function r_(t,e,i=[e],s,n=t){const o=e.hidden&&e.userHidden?{...e,hidden:!1}:e;let a="none",r=[];const l=[...o.entities,...o.allEntities||[]].some(t=>t.startsWith("cover."))?{...o,tapAction:"cover"}:o,c=(h=Dp({hass:t,devices:i,device:l,lightSources:s,registryHass:n}),"cover"!==h?.semantics?null:h.targets[0]?.entityId||h.skippedTargets[0]?.entityId||null);var h;const d=ho(t,[o]),p=Zn(o.marker?.binding,o.marker?.controls??o.controls,o.entities),u=new Set(p.filter(t=>t.startsWith("marker:"))),_=!0===o.marker?.is_light||u.size>0?s||ho(t,i):d,m=_.filter(t=>t.device.id===o.id&&"controls"!==t.via),g=d.filter(t=>"controls"!==t.via),f=m.length?m:g,v=yn(o.marker),y=v?[...f]:[...d.filter(t=>"controls"===t.via),...f];if(u.size)for(const t of _){if(!u.has(t.key))continue;t.stateEids.length>0&&y.some(e=>e.stateEids.some(e=>t.stateEids.includes(e)))||y.some(e=>e.key===t.key)||y.push({...t,via:"controls",castsGlow:!1})}const b=Yn(n,o.entities);if(!!c&&("cover"===o.tapAction||o.primary?.startsWith("cover.")||b.some(t=>t.startsWith("cover."))))a="cover",r=[a_(t,c,"cover")];else if(y.length)a=!v&&y.some(t=>"controls"===t.via)?"controls":"light",r=y.map(i=>{const s="controls"===i.via?"control":"forced"===i.via?"forced_light":"light";if(!i.passive)return a_(t,i.eid,s);const n=i.on?"on":"off";return{eid:i.key,role:s,name:e.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 e=Yn(n,o.entities),i=e.length?e:o.entities.filter(e=>!!t?.states?.[e]);if(i.length){a="device_role";const e=hn(n===t?t:{...n,states:t?.states||{}},i,o.entities),s=i.some(t=>an(n,t));r=e.map(e=>{const i=s?rn(n,e.eid)?"power_gate":an(n,e.eid)?"lifecycle":"device_role":"device_role";return a_(t,e.eid,i,e)})}else o.primary&&(a="primary",r=[a_(t,o.primary,"primary")])}const w=[];for(const e of o.entities||[]){const i=cn(t,e);"alarm"!==i.status||r.some(t=>t.eid===e)||w.push(a_(t,e,"critical",i))}return{sourceKind:a,visualSources:r,criticalSources:w,samples:[...r,...w].map(t=>t.sample)}}function l_(t,e,i,s){if(e.virtual)return{source:null,text:null,fallback:"value_virtual"};if(s){const i=function(t,e){if(!0!==e.marker?.use_climate_temp)return null;for(const i of e.entities){if(!i.startsWith("climate."))continue;const e=Number(t?.states?.[i]?.attributes?.current_temperature);if(Number.isFinite(e))return{eid:i,text:Math.round(10*e)/10+"°"}}return null}(t,e);if(i)return{source:{kind:"temperature",eid:i.eid,attribute:"current_temperature",text:i.text},text:i.text,fallback:null};const s=function(t,e){if("mdi:thermometer"!==e.icon&&"mdi:air-filter"!==e.icon)return null;for(const i of e.entities){if(!Un(t,i))continue;const e=Number(t?.states?.[i]?.state);if(Number.isFinite(e))return{eid:i,text:Math.round(10*e)/10+"°"}}return null}(t,e);if(s)return{source:{kind:"temperature",eid:s.eid,text:s.text},text:s.text,fallback:null};const n=function(t,e){if(!e.primary||!fo(t,e.primary))return null;const i=Number(t?.states?.[e.primary]?.state);return Number.isFinite(i)?{eid:e.primary,text:`${Math.round(i)}%`}:null}(t,e);if(n)return{source:{kind:"humidity",eid:n.eid,text:n.text},text:n.text,fallback:null}}const n=i.visualSources.filter(t=>!t.eid.startsWith("marker:"));let o=n.map(t=>t.eid);const a=n.find(t=>"power_gate"===t.role);if(a&&(o=[a.eid]),!o.length&&i.visualSources.some(t=>t.eid.startsWith("marker:"))&&(o=Yn(t,e.entities),!o.length&&e.primary&&t?.states?.[e.primary]&&(o=[e.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(t,e){const i=t?.states?.[e];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(t,e);return o?{text:Zi(o,String(i.attributes?.unit_of_measurement||"")),fallback:null}:{text:"",fallback:"value_no_state"}}(t,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 c_(t,e,i){const s=t.marker?.binding||(t.bindingKind&&t.bindingRef?`${t.bindingKind}:${t.bindingRef}`:t.virtual?"virtual":""),n=[...e.visualSources.map(t=>`${t.role}:${t.eid}`),...e.criticalSources.map(t=>`critical:${t.eid}`)].sort(),o=i?`${i.kind}:${i.eid}:${i.attribute||""}`:"none";return[s,e.sourceKind,...n,`value:${o}`].join("\n")}function h_(t,e,i){return c_(e,i||r_(t,e),null).replace(/\nvalue:none$/,"")}function d_(t){if(t.effectiveHidden)return[];if("static_icon"===t.display)return["static-icon"];const e=[],{visual:i}=t;return"alarm"===t.pulse.kind?e.push("alarm"):"unavailable"===i.availability?e.push("unavail"):"working"===i.status?e.push("on"):"open"===i.status&&e.push("open"),t.lockState&&e.push(`lock-${t.lockState}`),"none"!==t.pulse.reason&&"alarm"!==t.pulse.reason&&e.push("activity-"+t.pulse.reason),t.pulse.generation%2==0&&(e.push("pulse-gen2"),"short"===t.pulse.kind&&e.push("activity-gen2")),e}function p_(t,e,i){const s=Bi(e.marker?.display),n="static_icon"===s,o=n&&!1===i.sourceDetails?i_:r_(t,e,i.lightDevices||[e],i.lightSources,i.registryHass||t),a=e.bindingStatus,r="ha_disabled"===a?.kind,l="orphaned"===a?.kind,c=!0===e.userHidden||!0===e.marker?.hidden,h=r||c&&!i.designPreview,d=dn(o.samples),p=o.visualSources.find(t=>t.eid.startsWith("lock.")),u=p?["unlocked","open"].includes(p.state.toLowerCase())?"unlocked":"locked"===p.state.toLowerCase()?"locked":null:null,_=!yn(e.marker)&&Zn(e.marker?.binding,e.marker?.controls??e.controls,e.entities).length>0;let m="controls"===o.sourceKind||_&&"light"!==o.sourceKind&&"cover"!==o.sourceKind?{...d,availability:o_(t,e)}:d;h||n?m={availability:"available",status:"neutral",activity:"none"}:"alarm"===d.status||i.liveStates||(m={availability:"available",status:"neutral",activity:"none"});const g=n&&!1===i.sourceDetails?{source:null,text:null,fallback:null}:l_(t,e,o,i.showTemperature),f=c_(e,o,g.source),v=h_(t,e,o),y=i.activityRuntime,b=i.now??Date.now(),w=y?.expiresAt||(y?.flashTs?y.flashTs+3300:0);!n&&!h&&i.liveStates&&"alarm"!==d.status&&y?.sources===v&&y.flashTs&&y.flashKind&&w>b&&(m={...m,activity:y.flashKind});const k="icon_ripple"===s&&!h&&i.liveStates&&"alarm"!==m.status?m.activity:"none",x=n||h||!i.showTemperature?null:!0===e.marker?.use_climate_temp?go(t,e.entities):"mdi:thermometer"===e.icon||"mdi:air-filter"===e.icon?mo(t,e.entities):null,$=!n&&!h&&i.showTemperature&&e.primary&&fo(t,e.primary)?vo(t,e.entities):null,S=n||h||!i.showSignal||e.virtual?null:_o(t,e.entities),M=i.lightSources||("derived_marker_state"===e.marker?.value_badge?.source?.kind?ho(t,i.lightDevices||[e]):[]),C=function(t,e,i){const s=null!=e.marker?.value_badge;if(i.effectiveHidden||"static_icon"===i.display)return null;const n=e.marker?.value_badge;if(s)return n?.enabled?n.source?{configured:!0,enabled:!0,position:n.position||"right",...Ho(t,e,n.source,i.markerStates)}:{configured:!0,enabled:!0,source:null,sourceLabel:"",text:"—",fullText:t?.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(t,e);if(!o)return null;let a=null,r="default";if("entity_attribute"===o.kind&&"current_temperature"===o.attribute){const i=go(t,e.entities);null!=i&&(a=`${i}°`),r="temperature"}else if("mdi:thermometer"===e.icon||"mdi:air-filter"===e.icon){const i=mo(t,e.entities);null!=i&&(a=`${i}°`),r="temperature"}else{const i=vo(t,e.entities);null!=i&&(a=`${i}%`),r="humidity"}return null==a?null:{configured:!1,enabled:!0,source:o,position:"right",sourceLabel:"entity_attribute"===o.kind?`${Eo(o.attribute)} · ${Fo(t,o.entity_id)}`:"entity_state"===o.kind?Fo(t,o.entity_id):"",text:a,fullText:a,availability:"available",isLqi:!1,tone:r}}(t,e,{showTemperature:i.showTemperature,showSignal:i.showSignal,display:s,effectiveHidden:h,markerStates:M.filter(t=>t.key.startsWith("marker:")).map(t=>({ref:t.key,on:t.on,name:t.device.name}))}),D=o.visualSources.find(t=>!t.eid.startsWith("marker:")),P="cover"===o.sourceKind?D?.eid:e.primary||D?.eid,T=P?t?.states?.[P]:null,R=!i.liveStates||n||h?e.icon:vs(e.icon,P?.split(".")[0],T?.attributes?.device_class,T?.state,!!e.marker?.icon),I=!i.liveStates||n||h?null:o.visualSources.filter(t=>!t.eid.startsWith("marker:")).map(e=>function(t){return t&&"on"===t.state?zt(t.attributes?.rgb_color):null}(t?.states?.[e.eid])).find(t=>!!t)||null,A=Number(e.marker?.size)>0?Number(e.marker.size):1,z=Number(e.marker?.angle)||0,F=Number(e.marker?.ripple_size)>0?Number(e.marker.ripple_size):1.5,E=Rt(e.marker?.ripple_color,null),N=n?null:E||I||null,O=function(t){const{display:e,visual:i,semanticActivity:s,liveStates:n,effectiveHidden:o,bindingUnavailable:a=!1,reducedMotion:r=!1}=t,l=Math.max(1,Math.trunc(t.shortGeneration||1)),c=Number.isFinite(t.diameterScale)?Math.max(1,Number(t.diameterScale)):1.5,h=t.color||function(t,e){return"presence"===e?"#1DC21D":"running"===e||"working"===t.status||"open"===t.status?"#F0A00C":"#0C82F0"}(i,s);if(o||a||"unavailable"===i.availability||"static_icon"===e)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"!==e)return Yo(l,h,c);const d=t.now??Date.now(),p=t.shortExpiresAt||null;return t.shortReason&&null!=p&&p>d?{kind:"short",reason:t.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:m,semanticActivity:d.activity,shortReason:y?.sources===v?y?.flashKind:null,shortGeneration:y?.gen,shortExpiresAt:y?.sources===v?w:null,now:b,liveStates:i.liveStates,effectiveHidden:h,bindingUnavailable:r||l,reducedMotion:i.reducedMotion,color:N,diameterScale:F}),H="value"!==s||h?null:g.text,L="ha_disabled"===a?.kind?a.reason:null,B=function(t,e,i,s,n,o,a,r){return o?"ha_disabled":a?"orphaned":"static_icon"===r?"static_icon":"alarm"===e.status?"alarm":n?"unavailable"===e.availability?"unavailable":"cover"===s?"cover_icon_state":"presence"===i?"presence":"event"===i?"event":"transition"===i?"transition":"working"===e.status?"icon_ripple"===r&&"none"!==i?"working_activity":"working":"open"===e.status?"open":(t.primary||"").startsWith("media_player.")?"media_neutral":"neutral":"live_states_disabled"}(e,m,k,o.sourceKind,i.liveStates,r,l,s),q=[];i.designPreview&&c&&q.push("hidden_design_preview"),n||!0!==e.marker?.vacuum?.live||q.push("vacuum_live_plan_only");const W=o.visualSources.some(e=>"power_gate"===e.role||rn(t,e.eid)),j=e.entities.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category).length;W&&j>1&&q.push("composite_power_source"),"icon_ripple"!==s&&"static_icon"!==s&&"alarm"!==d.status&&"none"!==d.activity&&q.push("activity_display_disabled");const G={binding:e.marker?.binding||(e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:e.virtual?"virtual":""),sourceKind:o.sourceKind,visualSources:o.visualSources,criticalSources:o.criticalSources,valueSource:g.source,sourceSignature:f,visual:m,lockState:u,display:s,icon:R,valueText:H,valueFullText:H,fallbackReason:"value"!==s||H?null:g.fallback,activity:k,activityGeneration:y?.gen||1,pulse:O,classes:[],tempText:null==x?null:String(x),humText:null==$?null:String($),valueBadge:C,lqiText:null==S||C?.isLqi?null:String(S),lqiColor:null==S?null:t_(S),lqiBand:null==S?null:Qu(S),lightColor:I,scale:A,angle:z,rippleScale:F,rippleColor:N,userHidden:c,effectiveHidden:h,haDisabled:r,disabledReason:L,orphaned:l,vacuumLive:!n&&!0===e.marker?.vacuum?.live,explanation:{reason:B,notices:q}};return{...G,classes:d_(G)}}function u_(t,e=new WeakMap){if(null===t||"object"!=typeof t)return t;const i=t,s=e.get(i);if(s)return s;if(Array.isArray(t)){const s=[];e.set(i,s);for(const i of t)s.push(u_(i,e));return Object.freeze(s)}const n={};e.set(i,n);for(const[i,s]of Object.entries(t))"function"!=typeof s&&(n[i]=u_(s,e));return Object.freeze(n)}function __(t){const e=new Map(t);let i;return i=Object.freeze({get size(){return e.size},get:t=>e.get(t),has:t=>e.has(t),entries:()=>e.entries(),keys:()=>e.keys(),values:()=>e.values(),forEach:(t,s)=>e.forEach((e,n)=>t.call(s,e,n,i)),[Symbol.iterator]:()=>e[Symbol.iterator]()}),i}function m_(t){const e=new Set(t.entityIds||[]),i=new Set(t.deviceIds||[]),s=new Set(t.areaIds||[]);for(const s of t.devices){for(const t of s.entities||[])e.add(t);s.primary&&e.add(s.primary);for(const t of s.controls||[])e.add(t);s.marker?.vacuum?.source&&e.add(s.marker.vacuum.source),"device"===s.bindingKind&&s.bindingRef&&i.add(s.bindingRef),"entity"===s.bindingKind&&s.bindingRef&&e.add(s.bindingRef)}const n={},o={};for(const[o,a]of Object.entries(t.hass?.entities||{})){const r=a?.device_id?t.hass?.devices?.[a.device_id]:null;(e.has(o)||a?.device_id&&i.has(a.device_id)||s.has(a?.area_id)||s.has(r?.area_id))&&(e.add(o),n[o]=a,a?.device_id&&i.add(a.device_id))}for(const e of i){const i=t.hass?.devices?.[e];i&&(o[e]=i)}const a={};for(const i of e){const e=t.hass?.states?.[i];e&&(a[i]=e)}const r=Object.freeze({states:u_(a),entities:u_(n),devices:u_(o),config:u_(t.hass?.config),locale:u_(t.hass?.locale),themes:u_(t.hass?.themes)});return Object.freeze({sourceSequence:t.sourceSequence,capturedAt:t.capturedAt??Date.now(),hass:r,devices:u_([...t.devices]),positions:__([...t.positions||[]].map(([t,e])=>[t,Object.freeze({x:e.x,y:e.y})])),presentations:__([...t.presentations].map(([t,e])=>[t,u_(e)])),facts:__([...t.facts||[]].map(([t,e])=>[t,u_(e)]))})}const g_=(t,e)=>`${t}:${e?1:0}`;function f_(t){return!Number.isFinite(t)||t<=0?2.25:t/2.5*2.25}function v_({openings:t,geometries:e,fillsByRoomId:i,idPrefix:s="data",groupClass:n="opening-tunnels",dataLayer:o="data"}){const a=t.map((t,n)=>{const o=e[n];if(!o)return U;const a=o.faces.find(t=>-1===t.side),r=o.faces.find(t=>1===t.side);if(!a||!r)return U;const l=i.get(a.roomId)||null,c=i.get(r.roomId)||null;if(!l&&!c)return U;const h=`${a.d} ${r.d}`,d=!!l&&!!c&&l.color===c.color&&l.opacity===c.opacity,p=`translate(${t.rx} ${t.ry}) rotate(${t.angle})`;if(d)return j`<path class="opening-tunnel" data-hp="opening-tunnel" data-id=${t.id} data-kind=${t.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 U;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 j`<g class="opening-tunnel" data-hp="opening-tunnel" data-id=${t.id} data-kind=${t.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 j`<g class=${n} data-layer=${o} aria-hidden="true" pointer-events="none">${a}</g>`}const y_=new WeakMap,b_=new WeakMap;function w_(t){const e=xh(t.cfg),i={};for(const e of t.cfg.spaces||[])for(const t of e.rooms||[])t.area&&(i[t.area]=e.id);const s=t.cfg.settings?.exclude_integrations?new Set(t.cfg.settings.exclude_integrations):Yt,n=Xt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:Jt);return So({hass:t.hass,registry:t.registry,areaToSpace:i,markers:t.cfg.markers||[],settings:t.cfg.settings||{},excluded:s,showAll:!!t.cfg.settings?.show_all,firstSpaceId:e[0]?.id||"",loc:e=>la(t.lang,e),iconRules:n})}function k_(t){const e=xh(t.cfg).find(e=>e.id===t.spaceId);if(!e)return null;const i=cs(t.cfg.spaces.find(e=>e.id===t.spaceId)),s=ps(t.cfg.settings),n=t.iconSize??2.5,o=n>8?2.5:n,a=f_(o),r=t.registry?Nn(t.hass,t.registry):t.hass,l=t.registry?On(t.hass,t.registry):t.hass,c=(t.devices||w_(t)).filter(e=>e.space===t.spaceId),h=c.filter(t=>!t.hidden),d=function(t,e,i){const s={},n=i/100*jh(e)*1.3;for(const i of e.rooms){if(!i.area)continue;const e=t.filter(t=>t.area===i.area);if(!e.length)continue;const o=Vh(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(e.length*r/Math.max(l,1)))),h=r/c,d=l/Math.max(Math.ceil(e.length/c),1),p=e.map((t,e)=>({x:o.x+a+h*(e%c+.5),y:o.y+a+d*(Math.floor(e/c)+.5)}));Oi(p,o,n,.5*a),e.forEach((t,e)=>s[t.id]=Th(p[e]))}return s}(c,e,o),p=[];for(const e of h){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*fh,e=i.y*fh;p.push({minX:t,minY:e,maxX:t,maxY:e})}}const u=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},_=Array.isArray(u.walls)?u.walls:[],m=Number(u.cell_cm)>0?Number(u.cell_cm):5,g=(u.openings||[]).flatMap(t=>{if(!t.host)return[];const i=Ep(t,e.partitions,fh,m,Ch).resolved;return i?[i]:[]}),f=Id({partitions:e.partitions,roomDrafts:e.room_drafts,columns:e.wall_columns,cellCm:m,hostedOpenings:g.map(t=>({id:t.opening.id,host:t.host,length:t.length,type:t.opening.type}))}),v=function(t,e,i,s){let n=b_.get(t);n||(n=new Map,b_.set(t,n));const o=n.get(e);if(o?.fingerprint===i)return o.value;const a=s();return n.set(e,{fingerprint:i,value:a}),a}(t.cfg,e.id,f,()=>th(e,m,Ch,2e-4*Ch,g.map(Hp)).all);for(const t of v){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);e.length&&p.push({minX:Math.min(...e),minY:Math.min(...i),maxX:Math.max(...e),maxY:Math.max(...i)})}const y=qh(e,p),b=[y.x,y.y,y.w,y.h],w=new Map(e.rooms.map(e=>{const n=zs(i.fill,e);return[e,_s(n,"lqi"===n&&e.area?Do(r,c,e.area):null,"light"===n?uo(ho(r,c,e,t.virtualLights)):"none","temp"===n&&e.area?Po(r,c,e.area):null,i.tempMin,i.tempMax,s,ls(i.customFill,e))]})),k=new Map;for(const t of e.rooms)t.id&&k.set(t.id,w.get(t)||null);const x=function(t,e=1e3){const i=[];for(const[s,n]of(t||[]).entries()){if("passage"!==n?.type)continue;const t={...n,id:String(n.id||`passage-${s}`),type:"passage",rx:Number(n.x)*e,ry:Number(n.y)*e,rlen:Number(n.length)*e};[t.rx,t.ry,t.rlen,t.angle].every(Number.isFinite)&&t.rlen>0&&i.push(t)}return i}((u.openings||[]).flatMap(t=>{if(!t.host)return[t];const e=g.find(e=>e.opening.id===t.id);return e?[Wp(t,e,fh)]:[]}),fh),$=lc(e.rooms,_,[],Dh,m,Ch,fh),S=g.filter(t=>qp(t,$,2e-4*Ch)).map(t=>({x:t.center[0],y:t.center[1],angle:t.angle,length:t.length})),M=e.rooms.filter(t=>t.area||i.showBorders||"none"!==zs(i.fill,t)).map(t=>{let s="room "+(e.bg?"overlay":"yard"),n="";const o=zs(i.fill,t);if(i.showBorders||"none"!==o){s+=" styled";const e=[`--room-stroke:${i.color}`,`--room-stroke-op:${i.showBorders?i.opacity:0}`],o=w.get(t)||null;o?(s+=" filled",e.push(`--room-fill:${o.color}`,`--room-fill-op:${o.opacity.toFixed(3)}`)):e.push("--room-fill:transparent","--room-fill-op:0"),n=e.join(";")}const a=t.id||U,r=t.area||U,l=t.poly?j`<polygon class="${s}" style="${n}" data-hp="room" data-id=${a} data-area=${r}
|
||
points="${t.poly.map(t=>t.join(",")).join(" ")}"></polygon>`:j`<rect class="${s}" style="${n}" data-hp="room" data-id=${a} data-area=${r}
|
||
x="${t.x}" y="${t.y}" width="${t.w}" height="${t.h}" rx="${.03*Math.min(t.w,t.h)}"></rect>`;return l}),C=e.rooms.filter(t=>{const e=w.get(t);return Fs(i.glow,t)&&(!e||e.opacity<=0)}).map(t=>t.poly?j`<polygon class="glow-base" aria-hidden="true" pointer-events="none"
|
||
data-room-id=${t.id||U}
|
||
points="${t.poly.map(t=>t.join(",")).join(" ")}"
|
||
fill=${s.glow_base.c} fill-opacity=${s.glow_base.a}></polygon>`:j`<rect class="glow-base" aria-hidden="true" pointer-events="none"
|
||
data-room-id=${t.id||U}
|
||
x=${t.x} y=${t.y} width=${t.w} height=${t.h}
|
||
rx=${.03*Math.min(t.w,t.h)}
|
||
fill=${s.glow_base.c} fill-opacity=${s.glow_base.a}></rect>`),D=new Map;for(const t of e.rooms)if(t.id){const e=w.get(t)||null;D.set(t.id,Fs(i.glow,t)&&(!e||e.opacity<=0)?{color:s.glow_base.c,opacity:s.glow_base.a,mode:"glow"}:null)}const P=ho(r,h,null,t.virtualLights),T=h.map(s=>{const n=function(t,e,i,s,n){const o=e[t.id];return o&&o.s===t.space?{x:o.x*fh,y:o.y*fh}:s[t.id]?s[t.id]:Th(Wh(n))}(s,t.layout,t.cfg,d,e),o=(n.x-b[0])/b[2]*100,a=(n.y-b[1])/b[3]*100,c=i.showLqi??!1!==t.showSignal,p=t.presentations?.get(g_(s.id,c))||p_(r,s,{liveStates:!1!==t.liveStates,showTemperature:!1!==t.showTemperature,showSignal:c,activityRuntime:t.activityRuntime?.get(s.id),sourceDetails:!1,lightDevices:h,lightSources:P,registryHass:l,reducedMotion:t.reducedMotion}),u=[`left:${o}%`,`top:${a}%`,...Uo(p)],_=e_(p),m=[s.name,la(t.lang,`marker.state_a11y_${_}`),"none"!==p.pulse.kind?la(t.lang,`marker.pulse_a11y_${p.pulse.reason}`):"",p.valueFullText||p.valueText||"",Ro(p.valueBadge),null!=p.lqiText&&p.lqiBand?la(t.lang,`marker.lqi_a11y_${p.lqiBand}`,{value:p.lqiText}):""].filter(Boolean).join(", ");return W`<div class="dev ${Go(r)} ${p.classes.join(" ")} ${s.virtual?"virtual":""} ${null!=p.valueText?"valonly":""}"
|
||
data-hp="device" data-id="${s.id}" data-entity=${s.primary||U} data-area=${s.area||U}
|
||
role="img" aria-label=${m}
|
||
data-state=${_}
|
||
data-lqi-band=${null!=p.lqiText&&p.lqiBand||U}
|
||
data-binding-status=${"ha_disabled"===s.bindingStatus?.kind?"ha-disabled":s.bindingStatus?.kind||"active"}
|
||
data-disabled-reason=${p.disabledReason?p.disabledReason.replace("_","-"):U}
|
||
style="${u.join(";")}">
|
||
${Ko(p,{})}
|
||
</div>`}),R=i.showNames?e.rooms.filter(t=>t.name).map(s=>{const n=Kh(s,e.id,t.layout,t.cfg),o=(n.x-b[0])/b[2]*100,a=(n.y-b[1])/b[3]*100,r=Math.min(1,i.opacity+.25);return W`<div class="roomlabel"
|
||
data-hp="room-label" data-id=${s.id||U} data-area=${s.area||U}
|
||
style="left:${o}%;top:${a}%;color:${i.color};opacity:${r}">${s.name}</div>`}):[],I=e.bg?t.displayUrl?t.displayUrl(e.bg.href):e.bg.href:"",A=t.cfg.spaces.find(e=>e.id===t.spaceId)?.settings||{},z="daynight"===Sr(t.cfg?.settings,A)?_r(r,t.dayCycleNow??new Date):null,F=hs(t.cfg?.settings,i),E=!!(_.length||v.length&&i.showBorders),N=E?Id(x.length?{rooms:e.rooms,walls:_,extras:v,cellCm:m,passages:x.map(t=>({x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),hostedCompositeOpenings:S}:{rooms:e.rooms,walls:_,extras:v,cellCm:m}):"",O=E?function(t,e,i,s){let n=y_.get(t);n||(n=new Map,y_.set(t,n));const o=n.get(e);if(o?.fingerprint===i)return o.value;const a=s();return n.set(e,{fingerprint:i,value:a}),a}(t.cfg,e.id,N,()=>Ic(e.rooms,_,[],[...x.filter(t=>!t.host).map(t=>({x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),...S],Dh,m,Ch,fh,v)):null,H=x.length&&_.length?function(t,e,i,s,n,o,a,r=1){return[n,o,a,r].every(Number.isFinite)&&o>0&&r>0&&i?.length?qc(Fc(t,i,s,n,o,a,r),e):e.map(()=>null)}(e.rooms,x.map(t=>({x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),_,[],Dh,m,Ch,fh):x.map(()=>null),L=x.length?v_({openings:x,geometries:H,fillsByRoomId:k,idPrefix:`${e.id}-static-data`,groupClass:"opening-tunnels static-opening-tunnels",dataLayer:"data"}):U,B=x.length?v_({openings:x,geometries:H,fillsByRoomId:D,idPrefix:`${e.id}-static-glow`,groupClass:"opening-tunnels glow-base-tunnels static-opening-tunnels",dataLayer:"glow-base"}):U,q=_.length&&O?.paperD?[{path:O.paperD}]:di(e.rooms),G=i.showBorders?O:null,V=t.stageWidth&&b[2]?t.stageWidth/b[2]:1,K=dl(m),Y=!!G&&(hl(G.depthUnits,V)||pl(K,V)),J=i.color||"#607d8b",X=i.hideOpenings?[]:g.map(t=>{const e=t.opening,i="passage"!==e.type&&e.contact?r.states?.[e.contact]?.state:null,s=mi(e.type,i,!!e.invert),n=s>0&&!!e.contact,o="gate"===e.type?!e.flip_v:!!e.flip_v,a={type:e.type,length:t.length,angle:t.angle,amount:s,flipH:!!e.flip_h,flipV:!!e.flip_v,base:J,tone:n?"var(--hp-open)":J,cellCm:m,gridPitch:Ch,face:Lp(t,o)};return j`<g class="opening static-opening" data-hp="opening"
|
||
data-id=${e.id} data-kind=${e.type} pointer-events="none"
|
||
transform="translate(${t.center[0]} ${t.center[1]}) rotate(${t.angle})">
|
||
${Zu(a)}
|
||
</g>`});return W`
|
||
<div class="hp-static-stage${z?` daycycle phase-${z.phase}`:""}"
|
||
?inert=${!!t.inert}
|
||
style="aspect-ratio:${b[2]}/${b[3]}${F?";background:"+F:""};--hp-cell-visual-scale:${uh(m)};--wall-fill:${s.wall_fill.c};--wall-fill-op:${s.wall_fill.a}${z?`;${Pr(z)}`:""}">
|
||
${Tr(z)}
|
||
<svg viewBox="${b[0]} ${b[1]} ${b[2]} ${b[3]}" preserveAspectRatio="xMidYMid meet">
|
||
${G?j`<defs>
|
||
<pattern id="hp-wall-hatch" patternUnits="userSpaceOnUse"
|
||
width="${K}" height="${K}" patternTransform="rotate(45)">
|
||
<path d="M0 0 L0 ${K}" stroke="${J}"
|
||
stroke-width="${K/8*2}"></path>
|
||
</pattern>
|
||
</defs>`:U}
|
||
<g class="hp-paperg">${q.map(t=>"path"in t?j`<path class="hp-paper" d="${t.path}" fill-rule="evenodd"></path>`:"poly"in t?j`<polygon class="hp-paper" points="${t.poly}"></polygon>`:j`<rect class="hp-paper" x="${t.rect.x}" y="${t.rect.y}" width="${t.rect.w}" height="${t.rect.h}" rx="${t.rect.rx}"></rect>`)}</g>
|
||
${I?j`<image href="${I}" x="${e.bg.x}" y="${e.bg.y}" width="${e.bg.w}" height="${e.bg.h}"
|
||
@load=${()=>t.assetLoaded?.(e.bg.href,I)}
|
||
transform=${e.bg.angle?`rotate(${e.bg.angle} ${e.bg.x+e.bg.w/2} ${e.bg.y+e.bg.h/2})`:U}
|
||
preserveAspectRatio="none" />`:U}
|
||
${M}
|
||
${L}
|
||
${C.length?j`<g class="glow-base-layer" aria-hidden="true" pointer-events="none">${C}</g>`:U}
|
||
${B}
|
||
${G?j`<g class="wallbodies" style="--room-stroke:${J}">
|
||
${G.paths.map(t=>j`
|
||
<path class="wallbody-fill" data-component=${t.id} d="${t.d}"
|
||
fill="${s.wall_fill.c}" fill-opacity="${s.wall_fill.a}"
|
||
fill-rule=${t.fillRule} stroke="none" pointer-events="none"></path>
|
||
<path class="wallbody ${Y?"solid":""}"
|
||
data-hp="wall" data-id="union" data-kind="union" data-component=${t.id}
|
||
d="${t.d}" fill="${Y?"none":"url(#hp-wall-hatch)"}"
|
||
fill-rule=${t.fillRule} stroke="${J}"
|
||
stroke-width="${_h(.6,m)}" pointer-events="none"></path>`)}
|
||
</g>`:U}
|
||
${X}
|
||
</svg>
|
||
${""}
|
||
<div class="devlayer" style="--icon-size:${Gh(o,e,b[2]).toFixed(3)}cqw;--device-base-size:${Gh(a,e,b[2]).toFixed(3)}cqw">${T}${R}</div>
|
||
</div>
|
||
`}let x_=null,$_=null,S_=0,M_=-1,C_=null,D_=[];const P_=new Set,T_=(t,e)=>{"function"==typeof t&&e.push(t)};function R_(){if(x_)return x_;try{const t=JSON.parse(localStorage.getItem("houseplan_card_cfg_v1")||"null");if(t&&t.config&&Array.isArray(t.config.spaces)){const e=t.layout||{};return{config:t.config,rev:t.rev||0,configFingerprint:t.config_fingerprint||Id(t.config),layout:e,layoutRev:t.layout_rev||0,layoutFingerprint:t.layout_fingerprint||Id(e),virtualLights:mn(t.virtual_lights,t.rev||0)}}}catch{}return null}function I_(t,e=!1){if(e&&(x_=null,S_++,$_))return $_.catch(()=>null).then(()=>I_(t,!1));if(x_)return Promise.resolve(x_);if($_)return M_!==S_?$_.catch(()=>null).then(()=>I_(t,!1)):$_;return M_=S_,$_=async function(t,e){const[i,s]=await Promise.all([t.callWS({type:"houseplan/config/get"}),t.callWS({type:"houseplan/layout/get"})]),n=i?.rev??0,o=x_?gn(x_.virtualLights,i?.virtual_lights,n,!!i&&"virtual_lights"in i):mn(i?.virtual_lights,n),a={config:i?.config??null,rev:n,configFingerprint:Id(i?.config??null),layout:s?.layout??{},layoutRev:s?.rev??0,layoutFingerprint:Id(s?.layout??{}),virtualLights:o};e===S_&&(x_=a);const r=t.connection;if(r?.subscribeEvents&&C_!==r){for(const t of D_)t();D_=[],C_=r;const t=()=>{x_=null,S_++,P_.forEach(t=>t())},e=[];try{if(T_(await r.subscribeEvents(t,"houseplan_config_updated"),e),T_(await r.subscribeEvents(t,"houseplan_layout_updated"),e),T_(await r.subscribeEvents(t=>{if(x_){const e=wn(x_.virtualLights,t?.data);if(e===x_.virtualLights)return;x_={...x_,virtualLights:e}}else S_++;P_.forEach(t=>t())},"houseplan_virtual_light_updated"),e),C_===r)D_=e;else for(const t of e)t()}catch{for(const t of e)t();C_===r&&(C_=null)}}return a}(t,S_).finally(()=>{$_=null,M_=-1}),$_}const A_=3300;function z_(t,e){return{sources:t,last:Object.fromEntries(e.map(t=>[t.eid,t.state])),flashTs:0,flashKind:null,timer:0,gen:0,expiresAt:0,alarmActive:"alarm"===dn(e).status}}function F_(t,e,i,s){s(t.timer);const n=z_(e,i);Object.assign(t,n)}function E_(t,e,i,s,n){t.flashTs&&i-t.flashTs<A_&&"event"===t.flashKind&&"transition"===e||(t.flashTs=i,t.expiresAt=i+A_,t.flashKind=e,t.gen++,s(t.timer),t.timer=n(3360))}function N_(t,e,i){"transition"===t.flashKind&&e.some(t=>"transition"===t.activity)&&(i(t.timer),t.flashTs=0,t.flashKind=null,t.expiresAt=0);if("alarm"===dn(e).status){t.alarmActive||(i(t.timer),t.flashTs=0,t.flashKind=null,t.expiresAt=0);for(const i of e)t.last[i.eid]=i.state;return t.alarmActive=!0,null}if(t.alarmActive){for(const i of e)t.last[i.eid]=i.state;return t.alarmActive=!1,null}let s=null;for(const i of e){const e=pn(t.last[i.eid],i);("event"===e||!s&&e)&&(s=e),t.last[i.eid]=i.state}return s}class O_{constructor(t,e=()=>{}){this._host=t,this._onGateChange=e,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(t=globalThis.window){this._connected||(this._connected=!0,this._modality="unknown",this._media="function"==typeof t?.matchMedia?t.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(t){return this._modality=function(t,e){const i="mouse"===(s=e.pointerType)||"touch"===s||"pen"===s?s:null;var s;return i?"mouse"===i&&e.sourceCapabilities?.firesTouchEvents?t:i:t}(this._modality,t),this._syncGate(),this._modality}suspend(){this._setGate(!1)}_syncGate(){var t,e;this._setGate((t=this._modality,e=!!this._media?.matches,"mouse"===t&&e))}_setGate(t){this._gate!==t&&(this._gate=t,this._host.toggleAttribute("data-pointer-hover",t),this._onGateChange(t))}}class H_ extends ht{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return ra(this.hass,this._config?.language)}get _schema(){const t=this._spaces||[];return[t.length?{name:"space",selector:{select:{mode:"dropdown",options:t}}}:{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 U;this._loadSpaces();const t=this._lang,e={space:la(t,"editor.space"),title:la(t,"editor.title"),show_button:la(t,"editor.show_button"),button_label:la(t,"editor.button_label"),button_target:la(t,"editor.button_target"),icon_size:la(t,"editor.icon_size"),show_temperature:la(t,"editor.show_temperature"),live_states:la(t,"editor.live_states"),show_signal:la(t,"editor.show_signal")};return W`<ha-form
|
||
.hass=${this.hass}
|
||
.data=${this._config}
|
||
.schema=${this._schema}
|
||
.computeLabel=${t=>e[t.name]||t.name}
|
||
@value-changed=${this._valueChanged}
|
||
></ha-form>`}_valueChanged(t){const e={...this._config||{},...t.detail.value};delete e.aspect_ratio;const i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:e},this.dispatchEvent(i)}}H_.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",H_);const L_=t=>{history.pushState(null,"",t),((t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)})(window,"location-changed",{replace:!1})};class B_ extends ht{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 O_(this),this._onMotionChange=t=>{this._reducedMotion=t.matches,this._capturedSnapshotSequence=-1,this.requestUpdate()},this._onHaRegistryUpdate=()=>{const t=An(this.hass).revision;t!==this._haRegistryRevision&&(this._haRegistryRevision=t,this._refreshDevices(),this._capturedSnapshotSequence=-1,this.requestUpdate())},this._dayCycleTick=()=>{if(!this.isConnected||"hidden"===this.ownerDocument.visibilityState)return;const t=this._dayCycleState();if(!t)return this._dayCycleTimer&&(window.clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),void(this._dayCycleClockKey="");const e=mr(t);e!==this._dayCycleClockKey&&(this._dayCycleClockKey=e,this.requestUpdate())},this._pageVisibility=t=>{if(this._continuity.visibility(t),this._dayCycleVisibility(t),"hidden"!==t.kind){if(!t.long){const t=Date.now();let e=!1;for(const i of this._activityRuntime.values())!i.flashKind||(i.expiresAt||i.flashTs+3300)>t||(i.flashTs=0,i.flashKind=null,i.expiresAt=0,e=!0);return void(e&&(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=(t,e)=>{this._signer.markLoaded(this.hass,t,e),this._continuity.note("asset-ready"),this._continuityPaintToken=-1,"steady"!==this._continuity.state&&this.requestUpdate()},this._loadedOnce=!1,this._signer=new Fd(()=>this.requestUpdate()),this._retryContinuity=()=>{this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.retry(this._continuity.recoveryReason||"plan"),this._load(!0)},this._goToSpace=()=>{const t=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");L_(`${t}#space=${encodeURIComponent(this._config.space)}`)}}_ensureHaRegistryAuthority(){const t=this.hass?.connection||null;t&&t!==this._haRegistryConnection&&(this._haRegistryRelease?.(),this._haRegistryConnection=t,this._haRegistryRevision=-1,this._haRegistryRelease=In(this.hass,this._onHaRegistryUpdate),this._onHaRegistryUpdate())}_newContinuityController(){return new Rd(()=>{this._continuityEpoch++,this.isConnected&&this.requestUpdate()})}_dayCycleState(t=new Date){const e=this._snap?.config;if(!e||!this._config)return null;const i=e.spaces?.find(t=>t.id===this._config?.space)?.settings||{};return"daynight"!==Sr(e.settings,i)?null:_r(this._renderDeviceSnapshot?.hass||this.hass,t)}_syncDayCycleClock(){const t=this._dayCycleState();this._dayCycleClockKey=t?mr(t):"";const e="clock"===t?.source&&"hidden"!==this.ownerDocument.visibilityState&&this.isConnected;e&&!this._dayCycleTimer?this._dayCycleTimer=window.setInterval(this._dayCycleTick,3e4):!e&&this._dayCycleTimer&&(window.clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_dayCycleVisibility(t){"hidden"!==t.kind?(this._dayCycleTick(),this._syncDayCycleClock()):this._dayCycleTimer&&(window.clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_hookConnection(){const t=this.hass?.connection;t&&t!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),t.addEventListener?.("ready",this._onConnReady),t.addEventListener?.("disconnected",this._onConnLost),t.addEventListener?.("reconnect-error",this._onConnLost),this._connHooked=t)}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(t){const e=R_();return{type:"custom:houseplan-space-card",space:xh(e?.config||null)[0]?.id||"",show_button:!0,live_states:!0,show_temperature:!0,show_signal:!0}}setConfig(t){if(!t||!t.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,...t},this._snap=this._snap||R_()}connectedCallback(){var t;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=Pd(this.ownerDocument,this._pageVisibility),this._unsub=(t=()=>{this._beginContinuityCandidate("config-event",!1),this._reloadQueued=!0,this._load()},P_.add(t),()=>P_.delete(t)),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 t of this._activityRuntime.values())window.clearTimeout(t.timer);this._activityRuntime.clear(),this._continuityHistory=[...this._continuityHistory,...this._continuity.trace].slice(-80),this._continuity.dispose(),this._continuityDisposed=!0,super.disconnectedCallback()}willUpdate(t){t.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&&!t.has("hass")||this._snap&&this._loadedOnce||this._load(),(t.has("hass")||t.has("_snap")||t.has("_config")||!this._devices.length)&&this._refreshDevices(),this._captureRenderDeviceSnapshot(),this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._continuity.refreshCompleteFrame(this._frameFingerprint())}_stampActivity(t,e){E_(t,e,Date.now(),window.clearTimeout.bind(window),t=>window.setTimeout(()=>this.requestUpdate(),t))}_syncActivity(t,e,i=this.hass){if(!1===this._config?.live_states){for(const t of this._activityRuntime.values())window.clearTimeout(t.timer);return void this._activityRuntime.clear()}const s=new Set,n=ho(e,t,null,this._snap?.virtualLights);for(const o of t){if(o.hidden)continue;if("icon_ripple"!==Bi(o.marker?.display))continue;s.add(o.id);const a=r_(e,o,t,n,i),r=a.samples,l=h_(e,o,a);let c=this._activityRuntime.get(o.id);if(!c||c.sources!==l){c&&window.clearTimeout(c.timer),c=z_(l,r),this._activityRuntime.set(o.id,c);continue}const h=N_(c,r,window.clearTimeout.bind(window));h&&this._stampActivity(c,h)}for(const[t,e]of this._activityRuntime)s.has(t)||(window.clearTimeout(e.timer),this._activityRuntime.delete(t))}_refreshDevices(){if(!this.hass||!this._snap?.config||!this._config)return;const t=An(this.hass),e=w_({hass:this.hass,registry:t,cfg:this._snap.config,lang:this._lang});this._syncActivity(e,Nn(this.hass,t),this.hass),this._devices=e}_captureRenderDeviceSnapshot(){if(!this.hass)return;const t=Date.now(),e=[...this._activityRuntime.entries()].map(([e,i])=>`${e}:${i.gen}:${i.flashTs}:`+(i.flashKind&&(i.expiresAt||i.flashTs+3300)>t?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===e&&this._capturedSnapshotVirtual===i)return;const s=Nn(this.hass,An(this.hass)),n=new Map,o=new Set(["sun.sun"]),a=new Set,r=new Set,l=t=>{if(!t)return;const e=t.indexOf(":");if(e<0)return void o.add(t);const i=t.slice(0,e),s=t.slice(e+1);"device"===i?a.add(s):"entity"===i&&o.add(s)},c=xh(this._snap?.config||null).find(t=>t.id===this._config?.space);for(const t of c?.rooms||[])t.area&&r.add(t.area),l(t.settings?.temp_source),l(t.settings?.hum_source);const h=this._snap?.config?.spaces?.find(t=>t.id===this._config?.space);for(const t of h?.openings||[])for(const e of gi(t))o.add(e);const d=ho(s,this._devices,null,this._snap?.virtualLights);for(const t of this._devices)for(const e of[!1,!0])n.set(g_(t.id,e),p_(s,t,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:e,activityRuntime:this._activityRuntime.get(t.id),sourceDetails:!1,lightDevices:this._devices,lightSources:d,registryHass:this.hass,reducedMotion:this._reducedMotion}));const p=m_({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=e,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(t,e,i="plan"){return this._continuityDataReady=e,this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._continuity.beginCandidate(t,i)}_backdropRaw(){return this._snap?.config&&this._config&&xh(this._snap.config).find(t=>t.id===this._config.space)?.bg?.href||""}_candidateBackdrop(t){return t&&this._config&&xh(t).find(t=>t.id===this._config.space)?.bg?.href||""}_assetsReady(){const t=this._backdropRaw();return!t||this._signer.isReady(this.hass,t)}_frameFingerprint(){const t=this._snap;return Ad([t?.rev||0,t?.configFingerprint||Id(t?.config),t?.layoutRev||0,t?.layoutFingerprint||Id(t?.layout),t?.virtualLights?`${t.virtualLights.configRev}:${t.virtualLights.rev}`:"",this._config?.space||"",this._stageWidth,this.hass?.themes?.darkMode??this.hass?.themes?.default_theme??""])}_stageValid(){const t=this._observedStage;return!!t&&t.clientWidth>0&&t.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 t=this._continuity.token;if(this._candidateDeviceSnapshot&&this._candidateDeviceSnapshot!==this._visibleDeviceSnapshot&&this._stagedDeviceSnapshotToken!==t)return this._stagedDeviceSnapshotToken=t,void this.requestUpdate();this._continuityPaintToken!==t&&(this._continuityPaintToken=t,this._continuity.candidateReady(t)&&this._continuity.commitAfterPaint(t,{updateComplete:()=>this.updateComplete,stageValid:()=>this.isConnected&&this._stageValid(),assetsReady:()=>this._assetsReady(),frameFingerprint:()=>this._frameFingerprint()}).then(e=>{e&&t===this._continuity.token?(this._renderSnapshotAt=Date.now(),this._candidateDeviceSnapshot&&(this._visibleDeviceSnapshot=this._candidateDeviceSnapshot),this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1):t===this._continuity.token&&(this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._candidateDeviceSnapshot=null,this.requestUpdate())}))}updated(){const t=this.renderRoot.querySelector(".hp-static-stage")||void 0;if(t!==this._observedStage)if(this._stageObserver?.disconnect(),this._observedStage=t,t){const e=()=>{const i=t.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 t=this._pendingStageWidth;this._pendingStageWidth=0,!t||!this._observedStage||this._observedStage.clientWidth<=0||(Math.abs(this._observedStage.clientWidth-t)>.5?e():Math.abs(t-this._stageWidth)<=.5||(this._continuity.hasCompleteFrame&&this._beginContinuityCandidate("stage-resize",!0,"stage-size"),this._stageWidth=t,this.requestUpdate()))}))}};this._stageObserver=new ResizeObserver(e),this._stageObserver.observe(t),e()}else this._stageObserver=void 0;this._syncDayCycleClock(),this._settleContinuityFrame()}async _load(t=!1){if(this.hass){if(this._loading)return this._reloadQueued=!0,void(this._forceReloadQueued||=t);this._loading=!0,this._reloadQueued=!1;try{const e=await I_(this.hass,t),i=!this._snap||this._snap.configFingerprint!==e.configFingerprint,s=!this._snap||this._snap.layoutFingerprint!==e.layoutFingerprint,n=!this._snap||this._snap.virtualLights!==e.virtualLights;if(i&&!await this._signer.prepareImage(this.hass,this._candidateBackdrop(e.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=e:this._snap&&(this._snap.rev=e.rev,this._snap.layoutRev=e.layoutRev),i&&this._continuity.note("config-candidate",{configRev:e.rev}),s&&this._continuity.note("layout-candidate",{layoutRev:e.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 t=this._forceReloadQueued;this._reloadQueued=!1,this._forceReloadQueued=!1,this._load(t)}}}}get _lang(){return ra(this.hass,this._config?.language)}getCardSize(){const t=xh(this._snap?.config||null).find(t=>t.id===this._config?.space);if(t){const e=t.vb[3]/t.vb[2];return Math.max(3,Math.round(8*e))+(!1===this._config?.show_button?0:1)}return 6}_errorCard(t){return W`<ha-card
|
||
data-continuity-state=${this._continuity.state}
|
||
data-continuity-token=${this._continuity.token}
|
||
data-frame-fingerprint=${this._continuity.frameFingerprint||U}
|
||
data-recovery-reason=${(this._continuity.overlayVisible||"recovery-error"===this._continuity.state)&&this._continuity.recoveryReason||U}>
|
||
<div class="hp-static-error">${t}</div>
|
||
</ha-card>`}_referenced(){return Is(this._snap?.config)}_renderRecoveryOverlay(){if(!this._continuity.overlayVisible&&"recovery-error"!==this._continuity.state)return U;const t="connection"===this._continuity.recoveryReason;return W`<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>${la(this._lang,t?"continuity.restore_connection":"continuity.restore_plan")}</span>
|
||
${"recovery-error"===this._continuity.state?W`<button class="btn on" @click=${this._retryContinuity}>${la(this._lang,"continuity.retry")}</button>`:U}
|
||
</div>`}houseplanContinuityTrace(){return[...this._continuityHistory,...this._continuity.trace].slice(-80).map(t=>({...t,...t.stage?{stage:[...t.stage]}:{}}))}render(){if(!this._config)return U;const t=this._snap?.config;if(!t)return this._errorCard(la(this._lang,"space_card.loading"));const e=this._config.space,i=this._renderDeviceSnapshot,s=k_({hass:i?.hass||this.hass,cfg:t,layout:this._snap?.layout||{},spaceId:e,iconSize:this._config.icon_size,stageWidth:this._stageWidth,lang:this._lang,displayUrl:t=>this._signer.display(this.hass,t),assetLoaded:this._onAssetLoaded,registry:i?{revision:i.sourceSequence,authoritative:!1,access:"limited",entities:{...i.hass.entities},devices:{...i.hass.devices},lastSuccess:i.capturedAt}:An(this.hass),devices:[...i?.devices||this._devices],presentations:i?.presentations,activityRuntime:this._activityRuntime,reducedMotion:this._reducedMotion,virtualLights:this._snap?.virtualLights,liveStates:!1!==this._config.live_states,showTemperature:!1!==this._config.show_temperature,showSignal:!1!==this._config.show_signal,inert:this._continuity.overlayBlocksInteraction});if(!s)return this._errorCard(la(this._lang,"space_card.not_found",{id:e}));const n=xh(t).find(t=>t.id===e),o=void 0!==this._config.title?this._config.title:n?.title||"",a=!1!==this._config.show_button,r=this._config.button_label||la(this._lang,"space_card.button"),l=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null;return W`
|
||
<ha-card
|
||
data-continuity-state=${this._continuity.state}
|
||
data-continuity-token=${this._continuity.token}
|
||
data-frame-fingerprint=${this._continuity.frameFingerprint||U}
|
||
data-device-snapshot-sequence=${i?.sourceSequence??U}
|
||
data-recovery-reason=${l||U}
|
||
@pointerover=${t=>this._pointerModality.note(t)}
|
||
@pointerdown=${t=>this._pointerModality.note(t)}
|
||
@pointermove=${t=>this._pointerModality.note(t)}>
|
||
${o?W`<div class="hp-static-title">${o}</div>`:U}
|
||
<div class="hp-static-body">
|
||
${s}
|
||
${this._renderRecoveryOverlay()}
|
||
</div>
|
||
${a?W`<div class="hp-static-foot">
|
||
<button class="hp-static-btn" @click=${this._goToSpace}>${r}</button>
|
||
</div>`:U}
|
||
</ha-card>
|
||
`}}B_.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0},_continuityEpoch:{state:!0}},B_.styles=[Kt,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",B_),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-space-card"===t.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 q_=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; }
|
||
}
|
||
`,W_=(t,e)=>(t.composedPath?.()||[t.target]).some(t=>(t=>!!t&&"function"==typeof t.matches)(t)&&e(t));class j_{constructor(t){this.host=t,this._openGroupId=null,this._groupGeneration=0,this._renderedContext="",this._focusOwned=!1,this._currentModel=null,this._blocked=!1,this._dismissPointerTarget=null,this._globalDismissListening=!1,this._globalDismissGuard=t=>{this.handleOutsideDismiss(t)}}get hasOpenGroup(){return null!==this._openGroupId}get groupGeneration(){return this._groupGeneration}activeGroup(t){return this._openGroupId&&t.find(t=>t.id===this._openGroupId)||null}reset(){this._openGroupId=null,this._contentAnimation?.cancel(),this._contentAnimation=void 0,this._renderedContext="",this._focusOwned=!1,this._currentModel=null,this._blocked=!1,this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}closeForNavigation(){const t="palette"===this._currentModel?.kind?this._currentModel.dismiss:void 0,e=!!this._openGroupId||!!t;this._openGroupId=null,t?.(),t&&(this._currentModel=null),this._clearDismissClick(),e&&this.host.requestUpdate(),this._syncGlobalDismissListener()}openPalette(){this._openGroupId&&this.closeGroup(!1)}toggleGroup(t,e){this._openGroupId!==e?t.some(t=>t.id===e)&&("palette"===this._currentModel?.kind&&this._currentModel.dismiss?.(),this._groupGeneration+=1,this._openGroupId=e,this.host.clearTip(),this.host.requestUpdate(),this._syncGlobalDismissListener()):this.closeGroup(!1)}closeGroup(t){const e=this._openGroupId;this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener(),t&&e&&this.host.updateComplete().then(()=>{const t=this.host.root(),i=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===e&&null!==t.offsetParent);(i||t.querySelector(".editbar-tools"))?.focus?.()})}renderGroupLauncher(t,e,i){const s=this._openGroupId===t.id,n=t.items.find(e=>e.id===t.activeItemId&&!e.disabled);return W`<button class="btn editor-group-launcher ${s||n?"on":""}"
|
||
data-editor-group=${t.id} aria-expanded=${s?"true":"false"}
|
||
aria-pressed=${n?"true":"false"}
|
||
aria-controls="hp-editor-secondary" @click=${()=>this.toggleGroup(e,t.id)}
|
||
@keydown=${i=>{"ArrowDown"===i.key&&(i.preventDefault(),s||this.toggleGroup(e,t.id),this.host.updateComplete().then(()=>this.host.root().querySelector(".editor-group-item:not([disabled])")?.focus()))}}
|
||
title=${n?i.groupActive(t.label,n.label):t.label}>
|
||
<ha-icon icon=${t.icon}></ha-icon><span class="ml">${t.label}</span>
|
||
<ha-icon class="group-chevron" icon="mdi:chevron-down"></ha-icon>
|
||
</button>`}renderGroupModel(t,e,i){const s=t.items.find(t=>!t.disabled)?.id,n=t.items.some(e=>e.id===t.activeItemId&&!e.disabled)?t.activeItemId:void 0;return{contextId:e,kind:"group",ariaLabel:i.openGroup(t.label),visibleLabel:t.label,content:W`<div class="editor-group-items"
|
||
@keydown=${t=>this._groupKeydown(t)}>
|
||
${t.items.map(e=>W`<button
|
||
class="btn editor-group-item ${n===e.id?"on":""}"
|
||
data-group-item=${e.id} ?disabled=${!!e.disabled}
|
||
tabindex=${e.id===(n||s)?"0":"-1"}
|
||
aria-label=${e.disabled&&e.disabledReason?i.disabledAction(e.label,e.disabledReason):e.label}
|
||
aria-pressed=${"tool"===e.role||"toggle"===e.role?n===e.id?"true":"false":U}
|
||
title=${e.disabled&&e.disabledReason||e.label}
|
||
@click=${i=>this._activateGroupItem(t,e.id,i)}>
|
||
<ha-icon icon=${e.icon}></ha-icon><span>${e.label}</span>
|
||
</button>`)}
|
||
</div>`}}runContext(t,e,i){t===e&&i()}render(t,e){this._currentModel=t,this._blocked=e;const i=t?.kind||"hidden";return W`<div
|
||
class="editor-secondary-host kind-${i} ${t?"open":"closed"} ${e?"blocked":""}"
|
||
aria-hidden=${t?"false":"true"}>
|
||
<div id="hp-editor-secondary" class="editor-secondary kind-${i}"
|
||
data-context-id=${t?.contextId||""} role="toolbar"
|
||
aria-label=${t?.ariaLabel||""} ?inert=${!t||e}
|
||
@focusin=${()=>this._focusOwned=!0}
|
||
@focusout=${t=>{const e=t.relatedTarget;e&&t.currentTarget.contains(e)||(this._focusOwned=!1)}}
|
||
@pointerenter=${()=>this.host.clearTip()}
|
||
@pointerdown=${G_}
|
||
@pointerup=${G_}
|
||
@pointercancel=${G_}
|
||
@click=${G_}
|
||
@dblclick=${G_}
|
||
@wheel=${G_}>
|
||
${t?.visibleLabel?W`<span class="editor-context-label">${t.visibleLabel}</span>`:U}
|
||
<div class="editor-secondary-content">${t?.content||U}</div>
|
||
</div>
|
||
</div>`}afterRender(){const t=this.host.root();this._blocked&&(this._openGroupId?this.closeGroup(!1):"palette"===this._currentModel?.kind&&this._currentModel.dismiss?.()),this._syncGlobalDismissListener();const e=t.querySelector(".editor-secondary"),i=this._renderedContext,s=e?.dataset.contextId||"";if(s&&i&&s!==i&&!window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches&&(this._contentAnimation?.cancel(),this._contentAnimation=e?.querySelector(".editor-secondary-content")?.animate([{opacity:.35,transform:"translateY(-4px)"},{opacity:1,transform:"translateY(0)"}],{duration:100,easing:"ease-out"})),this._renderedContext=s,i&&s!==i&&this._focusOwned){const i=s?e?.querySelector('[tabindex="0"], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])'):t.querySelector(".editbar-tools");i?.focus?.(),this._focusOwned=!!s}if("palette"===this._currentModel?.kind&&this._currentModel.launcherId){const e=[...t.querySelectorAll("[data-editor-palette]")].find(t=>t.dataset.editorPalette===this._currentModel?.launcherId);e&&null!==e.offsetParent||this._currentModel.dismiss?.()}if(!this._openGroupId)return;const n=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===this._openGroupId);n&&null!==n.offsetParent||this.closeGroup(!0)}handleOutsideDismiss(t){if(W_(t,t=>t.hasAttribute("data-editor-navigation")))return"pointerdown"===t.type?this.closeForNavigation():"click"===t.type&&this._dismissPointerTarget&&this._clearDismissClick(),!1;if("click"===t.type&&this._dismissPointerTarget){return!!(t.composedPath?.()||[t.target]).includes(this._dismissPointerTarget)&&(this._clearDismissClick(),t.preventDefault(),t.stopImmediatePropagation(),!0)}const e=this._openGroupId||this._blocked||"palette"!==this._currentModel?.kind?null:this._currentModel;if("pointerdown"===t.type&&(this._openGroupId||e)){const i=W_(t,t=>t.classList.contains("editor-secondary")),s=W_(t,t=>!!t.dataset.editorGroup),n=W_(t,t=>!!t.dataset.editorPalette),o=W_(t,t=>t.classList.contains("stage"));if(!(i||s||n||"stay-open-on-canvas"===e?.dismissPolicy&&o))return t.preventDefault(),t.stopImmediatePropagation(),this._dismissPointerTarget=t.target,clearTimeout(this._dismissTimer),this._dismissTimer=window.setTimeout(()=>{this._clearDismissClick()},800),this._openGroupId?this.closeGroup(!1):e?.dismiss?.(),this._syncGlobalDismissListener(),!0}return!1}_activateGroupItem(t,e,i){if(t.id!==this._openGroupId)return;const s=t.items.find(t=>t.id===e);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 e=[...this.host.root().querySelectorAll("[data-editor-group]")].find(e=>e.dataset.editorGroup===t.id&&null!==e.offsetParent);e?.focus()})}_groupKeydown(t){if(!["ArrowLeft","ArrowRight","Home","End"].includes(t.key))return;const e=[...t.currentTarget.querySelectorAll(".editor-group-item:not([disabled])")];if(!e.length)return;t.preventDefault();const i=e.indexOf(this.host.root().activeElement),s="Home"===t.key?0:"End"===t.key?e.length-1:"ArrowLeft"===t.key?i<=0?e.length-1:i-1:(i+1)%e.length;e[s]?.focus()}_clearDismissClick(){this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}_syncGlobalDismissListener(){const t=!this._blocked&&"palette"===this._currentModel?.kind,e=!!this._openGroupId||t||!!this._dismissPointerTarget;e!==this._globalDismissListening&&(this._globalDismissListening=e,e?(window.addEventListener("pointerdown",this._globalDismissGuard,!0),window.addEventListener("click",this._globalDismissGuard,!0)):(window.removeEventListener("pointerdown",this._globalDismissGuard,!0),window.removeEventListener("click",this._globalDismissGuard,!0)))}}const G_=t=>t.stopPropagation(),U_=4.166666666666667e-9;function V_(t){return Number.isFinite(t)?Math.round(t/Dh)*Dh:t}const K_=(t,e,i,s)=>Math.hypot(i-t,s-e),Y_=(t,e,i,s,n,o,a,r)=>Math.hypot(Math.max(Math.abs(n-t),Math.abs(n+a-(t+i))),Math.max(Math.abs(o-e),Math.abs(o+r-(e+s)))),J_=(t,e,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(K_(t+c,e+h,n+d,o+p),K_(t-c,e-h,n-d,o-p)),_=Math.max(K_(t+c,e+h,n-d,o-p),K_(t-c,e-h,n+d,o+p));return Math.min(u,_)},X_=t=>{const e=Number(t?.cell_cm);return e>0?e:5};function Z_(t,e){const i=JSON.parse(JSON.stringify(t||[])),s=JSON.parse(JSON.stringify(e||{}));let n=0,o=0,a=0,r=0,l="",c=0,h=0,d=0;const p=(t,e)=>{Number.isFinite(t)&&Number.isFinite(e)&&t!==e&&Math.abs(e-t)<=U_&&h++},u=(t,e)=>{p(t[0],e[0]),p(t[1],e[1])},_={};let m=5;for(const t of i){const e=X_(t);null!=t?.id&&(_[String(t.id)]=e),e>m&&(m=e)}const g=(t,e,i,s=!1)=>{if(!(t>U_||s))return;n++,t>a&&(a=t);const o=t*Mh*e;o>r&&(r=o,l=i)};for(const t of i){const e=X_(t),i=null!=t?.id?String(t.id):"";for(const s of t.rooms||[]){o++;let t=0;if(s.poly?.length)s.poly=s.poly.map(e=>{const i=[V_(e[0]),V_(e[1])];return t=Math.max(t,K_(e[0],e[1],i[0],i[1])),u(e,i),i});else if(null!=s.x&&null!=s.y){const e=s.x,i=s.y,n=s.w||0,o=s.h||0,a=V_(e+n),r=V_(i+o),l=V_(e),c=V_(i),h=Math.max(Dh,a-l),d=Math.max(Dh,r-c);t=Y_(e,i,n,o,l,c,h,d),s.x=l,s.y=c,s.w=h,s.h=d,p(e,l),p(i,c),p(e+n,l+h),p(i+o,c+d)}g(t,e,i)}for(const s of t.room_drafts||[]){o++;let t=0;const n=s.points||[],a=n.map(e=>{const i=[V_(e[0]),V_(e[1])];return t=Math.max(t,K_(e[0],e[1],i[0],i[1])),i}),r=a.length?[a[0]]:[],l=a.length?[[n[0],a[0]]]:[],c=[];for(let t=0;t+1<a.length;t++){const e=a[t+1],i=r[r.length-1];i&&K_(i[0],i[1],e[0],e[1])<=U_||(r.push(e),l.push([n[t+1],e]),c.push({...s.segments?.[t]||{},cm:Number(s.segments?.[t]?.cm)||15}))}if(s.points=r,s.segments=c,r.length>=2)for(const[t,e]of l)u(t,e);g(t,e,i)}if(Array.isArray(t.room_drafts)){const e=t.room_drafts.length;t.room_drafts=t.room_drafts.filter(t=>t.points?.length>=2),d+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const s of t.partitions||[]){o++;const n=[s.a[0],s.a[1]],a=[s.b[0],s.b[1]],r=[V_(n[0]),V_(n[1])],l=[V_(a[0]),V_(a[1])];let c=Math.max(K_(n[0],n[1],r[0],r[1]),K_(a[0],a[1],l[0],l[1]));const h=K_(r[0],r[1],l[0],l[1]),d=(t.openings||[]).filter(t=>"partition"===t.host?.kind&&t.host.id===s.id).every(t=>{const e=Number(t.length),i=Number(t.host.t),s=i*h;return Number.isFinite(e)&&e>0&&Number.isFinite(i)&&i>=0&&i<=1&&s-e/2>=-U_&&s+e/2<=h+U_});h>U_&&d?(s.a=r,s.b=l,u(n,r),u(a,l)):c=0,g(c,e,i)}for(const s of t.wall_columns||[]){o++;const t=[s.center[0],s.center[1]],n=[V_(t[0]),V_(t[1])],a=K_(t[0],t[1],n[0],n[1]);s.center=n,u(t,n),g(a,e,i)}for(const s of t.decor||[]){o++;let t=0;if("line"===s.kind){const e=[s.x1,s.y1],i=[s.x2,s.y2],n=[V_(e[0]),V_(e[1])],o=[V_(i[0]),V_(i[1])];t=Math.max(K_(e[0],e[1],n[0],n[1]),K_(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(e,n),u(i,o)}else{const e=s.x,i=s.y,n=s.w,o=s.h,a=V_(e),r=V_(i);if(null!=s.w&&null!=s.h){const l=V_(e+n),c=V_(i+o),h=Math.max(Dh,l-a),d=Math.max(Dh,c-r);t=Y_(e,i,n,o,a,r,h,d),s.w=h,s.h=d,p(e+n,a+h),p(i+o,r+d)}else t=K_(e,i,a,r);s.x=a,s.y=r,p(e,a),p(i,r)}g(t,e,i)}for(const s of t.openings||[]){if(o++,"partition"===s.host?.kind){const n=(t.partitions||[]).find(t=>t.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)<=U_)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),_=J_(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(_,e,i,u);continue}const n=ui([s.x,s.y],t.rooms||[],.025,{step:Dh,length:Number(s.length)||0});if(!n)continue;const a=Number(s.angle),r=!(Number.isFinite(a)&&a===n.angle),l=J_(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,e,i,r)}}for(const[t,e]of Object.entries(s)){if(!e||"object"!=typeof e)continue;const i=e;if("number"!=typeof i.x||"number"!=typeof i.y)continue;o++;const n=V_(i.x),a=V_(i.y),r=K_(i.x,i.y,n,a);s[t]={...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 Q_(t,e){const i=e.base+t*e.span;return Math.min(1,Math.max(0,i))}const tm=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),em=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1]);function im(t,e,i){const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;if(o<=0)return em(t,e);let a=((t[0]-e[0])*s+(t[1]-e[1])*n)/o;return a=Math.max(0,Math.min(1,a)),Math.hypot(t[0]-(e[0]+s*a),t[1]-(e[1]+n*a))}function sm(t,e,i,s,n){for(const s of e)if(s&&!i.has(s.id)&&tm(s.a)&&tm(s.b)&&(em(t,s.a)<=n||em(t,s.b)<=n))return!0;for(const e of s?.roomPolygons||[])for(let i=0;i<e.length;i++){const s=e[i],o=e[(i+1)%e.length];if(tm(s)&&tm(o)&&im(t,s,o)<=n)return!0}for(const e of s?.columns||[])if(tm(e?.center)&&em(t,e.center)<=n)return!0;for(const e of s?.draftEnds||[])if(tm(e)&&em(t,e)<=n)return!0;return!1}function nm(t,e,i,s){if(t.cm!==e.cm)return null;const n=[t.b[0]-t.a[0],t.b[1]-t.a[1]],o=[e.b[0]-e.a[0],e.b[1]-e.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[[t.a,e.a],[t.a,e.b],[t.b,e.a],[t.b,e.b]])if(em(i,n)<=s)return[(i[0]+n[0])/2,(i[1]+n[1])/2];return null}function om(t,e){const i=.05*(Number(e.pitch)>0?Number(e.pitch):1),s=new Map;let n=(t||[]).filter(t=>!!t&&"string"==typeof t.id&&tm(t.a)&&tm(t.b));if(n.length<2)return{partitions:[...t||[]],merged:0,openingMoves:[]};const o=e.seedIds&&e.seedIds.length?new Set(e.seedIds):null;let a=0;for(let t=0;t<n.length+1;t++){let t=null;for(let s=0;s<n.length&&!t;s++)for(let a=s+1;a<n.length;a++){const r=nm(n[s],n[a],.02,i);if(r&&((!o||o.has(n[s].id)||o.has(n[a].id))&&!sm(r,n,new Set([n[s].id,n[a].id]),e.geometry,i))){t={i:s,j:a,at:r};break}}if(!t)break;const r=n[t.i],l=n[t.j],c=[r.a,r.b,l.a,l.b].filter(e=>em(e,t.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 t=h;h=d,d=t}const p={...r,a:[h[0],h[1]],b:[d[0],d[1]]},u=em(h,d),_=u>0?(d[0]-h[0])/u:0,m=u>0?(d[1]-h[1])/u:0,g=t=>u>0?((t[0]-h[0])*_+(t[1]-h[1])*m)/u:0;for(const t of[r,l]){const e=g(t.a),i=g(t.b)-e;for(const[n,o]of s)o.toId===t.id&&s.set(n,{fromId:o.fromId,toId:p.id,base:e+o.base*i,span:o.span*i});s.set(t.id,{fromId:t.id,toId:p.id,base:e,span:i})}o&&o.add(p.id),n=n.filter((e,i)=>i!==t.i&&i!==t.j),n.splice(t.i,0,p),a++}return{partitions:n,merged:a,openingMoves:[...s.values()]}}function am(t,e,i,s){if(!t?.length||!i.length)return 0;const n=new Map(i.map(t=>[t.fromId,t]));let o=0;for(const i of t){const t=i?.host;if(!t||"partition"!==t.kind)continue;const a=n.get(t.id);if(!a)continue;i.host={...t,id:a.toId,t:Q_(t.t,a)},o++;const r=Ep(i,e,s.coordScale,s.cellCm,s.gridPitch).resolved;r&&Object.assign(i,Wp(i,r,s.coordScale))}return o}function rm(t,e){const i=e?.excludeDraftId||null;return{roomPolygons:(t?.rooms||[]).map(t=>hi(t)).filter(t=>!!t),columns:t?.wall_columns||[],draftEnds:(t?.room_drafts||[]).flatMap(t=>{if(i&&t?.id===i)return[];const e=t?.points||[];return e.length?[e[0],e[e.length-1]]:[]})}}const lm=t=>!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),cm=(t,e)=>t[0]-e[0]||t[1]-e[1],hm=(t,e,i,s)=>[t[0]+e*s,t[1]+i*s],dm=(t,e,i,s)=>(t[0]-e[0])*i+(t[1]-e[1])*s,pm=(t,e,i,s,n)=>{const o=e[0]-t[0],a=e[1]-t[1],r=Math.hypot(o,a);if(!(r>n))return null;const l=o/r,c=a/r,h=e=>Math.abs((e[0]-t[0])*c-(e[1]-t[1])*l);if(h(i)>n||h(s)>n)return null;const d=dm(i,t,l,c),p=dm(s,t,l,c),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));return _-u>n?{lo:u,hi:_}:null},um=(t,e,i)=>Math.min(t.hi,e.hi)-Math.max(t.lo,e.lo)>i,_m=t=>{const e=new Set(["id","a","b","cm"]);return Object.keys(t||{}).every(t=>e.has(t))},mm=(t,e,i,s)=>{const n=t.b[0]-t.a[0],o=t.b[1]-t.a[1],a=Math.hypot(n,o);if(!(a>s))return!0;const r=n/a,l=o/a,c=ml(t.cm,i.cellCm,i.gridPitch)/2;return e.some(e=>{const n=e?.center;if(!lm(n))return!0;const o=dm(n,t.a,r,l),h=Math.abs((n[0]-t.a[0])*l-(n[1]-t.a[1])*r),d=ml(Number(e.cm),i.cellCm,i.gridPitch)*("square"===e.shape?Math.SQRT1_2:.5);return!(Number.isFinite(d)&&d>0)||o>=-d-s&&o<=a+d+s&&h<=c+d+s})},gm=(t,e,i)=>{if(t.host){const s=Ep(t,e,i.coordScale,i.cellCm,i.gridPitch).resolved;return s?{center:s.center,angle:s.angle,length:s.length}:null}const s=Number(t.x)*i.coordScale,n=Number(t.y)*i.coordScale,o=Number(t.angle),a=Number(t.length)*i.coordScale;return[s,n,o,a].every(Number.isFinite)&&a>0?{center:[s,n],angle:o,length:a}:null},fm=(t,e,i)=>{const s=t.angle*Math.PI/180,n=Math.cos(s),o=Math.sin(s),a=e.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=e.center[0]-t.center[0],h=e.center[1]-t.center[1];if(Math.abs(c*o-h*n)>i)return!1;const d=c*n+h*o;return Math.max(-t.length/2,d-e.length/2)<Math.min(t.length/2,d+e.length/2)-i},vm=(t,e)=>({...t,a:[t.a[0]*e,t.a[1]*e],b:[t.b[0]*e,t.b[1]*e]}),ym=(t,e,i,s)=>{const n=`~r-${(t=>{let e=2166136261;for(let i=0;i<t.length;i++)e^=t.charCodeAt(i),e=Math.imul(e,16777619)>>>0;return e.toString(36)})(`${t}|${e[0].toFixed(9)},${e[1].toFixed(9)}|${i[0].toFixed(9)},${i[1].toFixed(9)}`)}`,o=`${t.slice(0,Math.max(1,64-n.length))}${n}`;let a=o;for(let t=2;s.has(a);t++){const e=`-${t}`;a=`${o.slice(0,64-e.length)}${e}`}return s.add(a),a},bm=(t,e)=>{const i=[];for(const s of t){const t=i[i.length-1];!(t&&Math.abs(t.hi-s.lo)<=e&&t.safe===s.safe)||s.safe&&t.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}):(t.hi=s.hi,t.b=s.b)}return i};function wm(t,e,i,s,n){const o=i||[],a=Array.isArray(t?.partitions)?t.partitions:[],r=Array.isArray(t?.openings)?t.openings:[],l=Array.isArray(t?.room_drafts)?t.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 _=lc(e.rooms,d,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale),m=((t,e,i,s)=>{const n=new Map(t.map(t=>[t.id,t])),o=new Set;for(const t of e){if(!n.get(t.id)||!Array.isArray(t.points)||t.points.length<2||t.segments.length!==t.points.length-1)continue;let e=!0;for(let n=0;n+1<t.points.length&&e;n++){const o=t.points[n],a=t.points[n+1],r=Number(t.segments[n]?.cm);if(!(lm(o)&&lm(a)&&r>0)){e=!1;break}const l=Math.hypot(a[0]-o[0],a[1]-o[1]);if(!(l>s)){e=!1;break}const c=i.flatMap(t=>{const e=t.cm>0?t.cm:ll;if(t.open||"outer"!==t.kind&&"shared"!==t.kind||e+s<r)return[];const i=pm(o,a,t.a,t.b,s);return i?[i]:[]}).sort((t,e)=>t.lo-e.lo||t.hi-e.hi);let h=0;for(const t of c){if(t.lo>h+s)break;if(h=Math.max(h,t.hi),h>=l-s)break}h<l-s&&(e=!1)}e&&o.add(t.id)}return o})(l,e.room_drafts,_,h),g=l.filter(t=>!m.has(t.id));let f=0,v=0;const y=new Map(e.partitions.map(t=>[t.id,t]));for(const t of[...a].sort((t,e)=>t.id.localeCompare(e.id))){if(!p.some(e=>e.id===t.id))continue;if(!_m(t))continue;const i=y.get(t.id);if(!(i&&lm(i.a)&&lm(i.b)&&Number.isFinite(i.cm)&&i.cm>0))continue;const[o,a]=cm(i.a,i.b)<=0?[[i.a[0],i.a[1]],[i.b[0],i.b[1]]]:[[i.b[0],i.b[1]],[i.a[0],i.a[1]]],r=a[0]-o[0],l=a[1]-o[1],c=Math.hypot(r,l);if(!(c>h))continue;const _=r/c,g=l/c,b=lc(e.rooms,d,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale),w=b.flatMap(t=>{if(t.open||"outer"!==t.kind&&"shared"!==t.kind)return[];const e=pm(o,a,t.a,t.b,h);return e?[{interval:t,range:e}]:[]});if(!w.length)continue;const k=u.filter(t=>"partition"===t.host?.kind&&t.host.id===i.id).map(t=>({opening:t,resolved:Ep(t,e.partitions,n.coordScale,n.cellCm,n.gridPitch).resolved}));if(k.some(t=>!t.resolved))continue;const x=[0,c];for(const{range:t}of w)x.push(t.lo,t.hi);const $=x.slice(),S=new Map;for(const{opening:t,resolved:e}of k){const i=dm(e.center,o,_,g),s={lo:i-e.length/2,hi:i+e.length/2};S.set(t.id,s),$.push(Math.max(0,s.lo),Math.min(c,s.hi))}const M=[...new Set($.filter(t=>Number.isFinite(t)&&t>=-h&&t<=c+h).map(t=>Math.max(0,Math.min(c,t)).toFixed(9)))].map(Number).sort((t,e)=>t-e),C=p.map(t=>vm(t,n.coordScale)).filter(t=>t.id!==i.id),D=e.room_drafts.filter(t=>!m.has(t.id)),P=[];for(let t=0;t+1<M.length;t++){const s=M[t],a=M[t+1];if(!(a-s>h))continue;const r=hm(o,_,g,s),l=hm(o,_,g,a),c=w.filter(({range:t})=>t.lo<=s+h&&t.hi>=a-h).map(({interval:t})=>t),d=new Map;for(const t of c)d.set(t.roomId,t);const p=[...d.values()],u=new Set(p.map(t=>t.kind)),m=new Set(p.map(t=>t.cm>0?t.cm:ll)),f=p[0]?.kind,v=[...d.keys()].sort(),y=1===u.size&&1===m.size&&("outer"===f&&1===v.length||"shared"===f&&2===v.length),b={id:i.id,a:r,b:l,cm:i.cm},k=C.some(t=>!!pm(r,l,t.a,t.b,h)),x=D.some(t=>t.points.some((e,i)=>i+1<t.points.length&&!!pm(r,l,e,t.points[i+1],h))),$=y&&!k&&!x&&!mm(b,e.wall_columns,n,h),S=y?p[0].cm>0?p[0].cm:ll:0,T=y?Math.max(S,i.cm):i.cm;P.push({lo:s,hi:a,a:r,b:l,safe:$,signature:$?`${f}|${v.join(",")}|${T}`:"",roomIds:v,roomCm:S,finalCm:T})}if(!P.some(t=>t.safe))continue;const T=new Map;for(const{opening:t,resolved:e}of k){const s=S.get(t.id),o=x.some(t=>t>s.lo+h&&t<s.hi-h),a=P.filter(t=>um(t,s,h)),r=new Set(a.filter(t=>t.safe).map(t=>t.signature));if(!o&&a.length>0&&a.every(t=>t.safe)&&1===r.size&&qp(e,b,h)){T.set(t.id,{resolved:e,signature:[...r][0]});continue}const l=zp(i,n.cellCm,n.gridPitch),c={lo:s.lo-l,hi:s.hi+l};for(const t of P)um(t,c,h)&&(t.safe=!1,t.signature="")}let R=!0;for(;R;){R=!1;for(const[t,e]of[...T.entries()]){const s=S.get(t),o=P.filter(t=>um(t,s,h));if(o.length>0&&o.every(t=>t.safe)&&o.every(t=>t.signature===e.signature))continue;T.delete(t);const a=zp(i,n.cellCm,n.gridPitch),r={lo:s.lo-a,hi:s.hi+a};for(const t of P)um(t,r,h)&&(t.safe=!1,t.signature="");R=!0}}const I=bm(P,h),A=I.filter(t=>t.safe),z=I.filter(t=>!t.safe);if(!A.length)continue;if(p.length-1+z.length>2e3)continue;let F=d;for(const t of A){const[e,s]=cm(i.a,i.b)<=0?[t.a,t.b]:[t.b,t.a];F=zl(F,e,s,t.finalCm,n.pitch,n.coordScale)}if(F=hc(e.rooms,F,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale),F.length>500)continue;const E=new Set(p.map(t=>t.id));E.delete(i.id);const N=z.map((t,e)=>{const s=[t.a[0]/n.coordScale,t.a[1]/n.coordScale],o=[t.b[0]/n.coordScale,t.b[1]/n.coordScale],a=0===e?i.id:ym(i.id,s,o,E);return E.add(a),{id:a,a:s,b:o,cm:i.cm}}),O=p.findIndex(t=>t.id===i.id),H=p.slice();H.splice(O,1,...N);const L=H.map(t=>vm(t,n.coordScale)),B=new Map;let q=!0;for(const{opening:t,resolved:e}of k){const i=T.get(t.id);if(i){const e=Wp(t,i.resolved,n.coordScale),{host:s,...o}=e;B.set(t.id,o);continue}const s=S.get(t.id),o=N[z.findIndex(t=>t.lo<=s.lo+h&&t.hi>=s.hi-h)],a=o&&L.find(t=>t.id===o.id);if(!o||!a){q=!1;break}const r=a.b[0]-a.a[0],l=a.b[1]-a.a[1],c=Math.hypot(r,l),d=dm(e.center,a.a,r/c,l/c),p={...Wp(t,e,n.coordScale),host:{kind:"partition",id:o.id,t:d/c}};if(!Np(p,L,n.coordScale,n.cellCm,n.gridPitch).resolved){q=!1;break}B.set(t.id,p)}if(!q)continue;const W=u.map(t=>B.get(t.id)||t),j=[...T.keys()].map(t=>B.get(t)),G=j.map(t=>gm(t,L,n));if(G.some(t=>!t))continue;let U=!1;for(let t=0;t<G.length;t++)for(let e=t+1;e<G.length;e++)fm(G[t],G[e],h)&&(U=!0);const V=new Set(j.map(t=>t.id));for(const t of W){if(V.has(t.id))continue;const e=gm(t,L,n);e&&G.some(t=>fm(t,e,h))&&(U=!0)}if(U)continue;const K=Fc(e.rooms,F,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale);[...T.entries()].every(([t,e])=>{const i=B.get(t),s=Oc(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(t=>!!t?.full),a=e.signature.split("|")[1].split(","),r=new Set(o.map(t=>t.roomId));return o.length===a.length&&r.size===a.length&&a.every(t=>r.has(t))})&&(d=F,p=H,u=W,f+=A.length,v+=T.size)}return{walls:d,partitions:p,openings:u,roomDrafts:g,partitionsReconciled:f,openingsRehosted:v,removedDrafts:m.size}}const km=t=>JSON.parse(JSON.stringify(t)),xm=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),$m=(t,e)=>e.length>0&&e.length<=35&&("space"===t?/^[a-z0-9_-]+$/.test(e):/^[A-Za-z0-9_-]+$/.test(e)),Sm=(t,e,i)=>{const s=t.get(e)||[];s.push(i),t.set(e,s)};const Mm=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Cm=(t,e,i)=>Math.min(i,Math.max(e,t)),Dm=t=>xh({spaces:[t]})[0],Pm=(t,e,i)=>{const s=i>0?i:1,n=t=>`${(t[0]/s).toFixed(12)},${(t[1]/s).toFixed(12)}`,o=n(t),a=n(e);return o<a?`${o}|${a}`:`${a}|${o}`};function Tm(t,e,i,s,n,o,a=1){if(!e?.length)return[];const r=a>0?a:1,l=Math.max(s*r*.02,1e-9),c=[];for(const e of t||[])for(const t of hi(e)||[])c.push([t[0],t[1]]);const h=[];for(const t of i||[])Array.isArray(t)&&t.length>=4&&t.slice(0,4).every(Number.isFinite)&&h.push([t[0],t[1]],[t[2],t[3]]);const d=(t,e)=>e.some(e=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*l),p=new Map;for(const a of t||[]){if(!a?.id)continue;const u=rc(t,a.id,e,i,s,n,o,r);if(u)for(let t=0;t<u.orig.length;t++){const e=[];for(let i=0;i<u.parent.length;i++)u.parent[i]===t&&e.push(i);for(let t=1;t+1<e.length;t++){const i=e[t-1],s=e[t],n=e[t+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 y=Pm(g,f,r),b=p.get(y);b?b.targets.add(a):p.set(y,{a:[g[0],g[1]],b:[f[0],f[1]],targets:new Set([a])})}}}let u=e.slice();const _=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*l,m=(t,e)=>{if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return!1;const i=[Number(t.a[0])*r,Number(t.a[1])*r],s=[Number(t.b[0])*r,Number(t.b[1])*r];return!![...i,...s].every(Number.isFinite)&&(_(i,e.a)&&_(s,e.b)||_(i,e.b)&&_(s,e.a))};for(const t of[...p.values()].filter(t=>1===t.targets.size).sort((t,e)=>Pm(t.a,t.b,r).localeCompare(Pm(e.a,e.b,r)))){const e=[...t.targets][0],i=u.filter(e=>m(e,t));if(new Set(i.map(t=>t.cm)).size>1)continue;let n=!1;u=u.map(i=>m(i,t)?(n=!0,i.cm===e?i:{...i,cm:e}):i),n||(u=zl(u,t.a,t.b,e,s,r))}return u}const Rm=t=>{const e=[];for(const i of t||[]){const t=hi(i);if(t?.length)for(let i=0;i<t.length;i++)e.push([[t[i][0],t[i][1]],[t[(i+1)%t.length][0],t[(i+1)%t.length][1]]])}return e};function Im(t,e,i={},s={}){const n=s.reconcileCoincidentPartitions??wm,o=function(t,e,i={}){const s=km(t||{spaces:[],markers:[],settings:{}}),n=km(e||{}),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(t=>"string"==typeof t?.id?t.id:"").filter(Boolean),h=new Set(c),d=new Map;for(const t of c){const e=/^space_(.+)_([0-9a-f]{8})$/.exec(t);e&&Sm(d,e[1],t)}const p=new Map,u=new Map,_=new Map,m=new Map;for(const t of r){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const i=new Map;for(const s of Array.isArray(t.rooms)?t.rooms:[]){const t="string"==typeof s?.id?s.id:"";if(!t)continue;const n=/^room_(.+)_([0-9a-f]{8})$/.exec(t);n&&Sm(i,n[1],t),u.has(t)||u.set(t,e),_.has(t)||_.set(t,String(s.name||""));const o="string"==typeof s.area?s.area:"";if(o){const i=m.get(o)||[];i.push({spaceId:e,roomId:t}),m.set(o,i)}}p.set(e,i)}const g=new Set(u.keys()),f=new Map,v=new Map;for(const t of l)"string"==typeof t?.id&&t.id&&(!0===t.removed?v.set(t.id,t):f.set(t.id,t));const y={spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,positionsUnresolved:0,nestedRefsUnresolved:0,deadSpaceIds:[],orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0,removedPositions:[],liveMissingPositions:[],unverifiedPositions:[]},b=new Set,w=t=>h.has(t)||!$m("space",t)?null:1===d.get(t)?.length?d.get(t)[0]:null,k=(t,e)=>{if(!$m("room",t))return null;const i=p.get(e)?.get(t)||[];return 1===i.length?i[0]:null},x=t=>{const e=i.effectiveAreaByMarker?.[t]||"",s=e&&m.get(e)||[];return 1===s.length?s[0]:null},$=(t,e,i)=>{if("string"==typeof t.room_id&&t.room_id&&!g.has(t.room_id)){const s=e?k(t.room_id,e):null;s?(t.room_id=s,y.roomRefsRemapped++):i?(t.room_id=i.roomId,y.roomRefsRemapped++):!0!==t.removed&&delete t.room_id}const s=t.vacuum?.segment_map;if(s&&"object"==typeof s&&!Array.isArray(s))for(const t of Object.keys(s)){const i=s[t];if("string"!=typeof i||!i||g.has(i))continue;const n=e?k(i,e):null;n?(s[t]=n,y.roomRefsRemapped++):y.nestedRefsUnresolved++}};for(const t of l){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const i="string"==typeof t.space?t.space:"",s=i&&!h.has(i),o=n[e],a="string"==typeof o?.s?o.s:"",r=a&&!h.has(a),l=!0===t.removed;let c=i&&h.has(i)?i:null,d=null,p=!1;if(s){const s=w(i);s?(t.space=s,c=s,p=!0,y.spaceRefsRemapped++):l||"virtual"===t.binding?l||(delete t.space,c=null,y.markersDetached++):(d=x(e),d?(t.space=d.spaceId,c=d.spaceId,y.spaceRefsRemapped++):(delete t.space,c=null,y.markersDetached++))}l&&!p||$(t,c,d),r&&p&&a===i&&c&&(n[e]={...o,s:c},y.positionsRemapped++,b.add(e))}for(const t of Object.keys(n)){if(b.has(t))continue;const e=n[t],i="string"==typeof e?.s?e.s:"";if(!i||h.has(i))continue;const s=w(i);if(s){if(t.startsWith("rl_")){const i=t.slice(3),o=k(i,s);if(o){const i=`rl_${o}`;xm(n,i)||(n[i]={...e,s:s}),delete n[t],y.roomRefsRemapped++,y.positionsRemapped++;continue}}n[t]={...e,s:s},y.positionsRemapped++}}const S=i.ownerRoster,M=!0===S?.authoritative,C=new Set(S?.deviceIds||[]),D=new Set(S?.entityIds||[]),P=new Set(Array.isArray(s.settings?.known_devices)?s.settings.known_devices.filter(t=>"string"==typeof t):[]),T=(t,e="")=>{const i=String(S?.names?.[t]||e||"").trim();return i&&i!==t?i:""},R=(t,e,i,s="",n)=>({id:t,spaceId:e,kind:i,name:T(t,s),...n?{reason:n}:{}}),I=t=>{"room_label"===t?y.orphanRoomLabelsRemoved++:"group"===t?y.orphanGroupPositionsRemoved++:y.orphanDevicePositionsRemoved++},A=new Set;for(const[t,e]of Object.entries(n)){const s="string"==typeof e?.s?e.s:"";if(!s||h.has(s))continue;let o,a;const r=f.get(t),l=v.get(t);if(r)o=R(t,s,"device",String(r.name||"")),a="live";else if(l)o=R(t,s,"device",String(l.name||"")),a="absent";else if(t.startsWith("rl_")){const e=t.slice(3);o=R(t,s,"room_label",_.get(e)||""),a=g.has(e)?"live":"absent"}else if(t.startsWith("lg_")){const e=t.slice(3);o=R(t,s,"group"),a=D.has(e)?"live":M?"absent":"unverified","unverified"===a&&(o.reason="registry_unavailable")}else C.has(t)?(o=R(t,s,"device"),a="live"):P.has(t)?(o=R(t,s,"device"),a=M?"absent":"unverified","unverified"===a&&(o.reason="registry_unavailable")):(o=R(t,s,"unknown","","unknown_owner"),a="unverified");if("absent"!==a){if("live"===a){if(y.liveMissingPositions.push(o),!0===i.removeLiveMissingPositions){delete n[t],y.liveMissingPositionsRemoved++,I(o.kind),y.removedPositions.push(o);continue}}else y.unverifiedPositions.push(o);y.positionsUnresolved++,A.add(s)}else delete n[t],I(o.kind),y.removedPositions.push(o)}return y.deadSpaceIds=[...A].sort((t,e)=>t.localeCompare(e)),{config:s,layout:n,report:y,changed:JSON.stringify(s)!==o||JSON.stringify(n)!==a}}(t,e,i),a=o.config,r=nl(a,o.layout);rl(a),al(o.layout);const l=JSON.stringify(t||{}),c=JSON.stringify(e||{}),h=Number.isInteger(Number(a.model_version))?Number(a.model_version):0,d=(t=>{let e=0,i=0,s=0;for(const i of t.markers||[]){if("ripple"===i.display&&(i.display="icon_ripple",e++),Array.isArray(i.controls)){const t="string"==typeof i.binding&&i.binding.startsWith("entity:")?i.binding.slice(7):"",s=t?i.controls.filter(e=>e!==t):i.controls;JSON.stringify(s)!==JSON.stringify(i.controls)&&(i.controls=s.length?s:null,e++)}const t=i.vacuum;t&&Mm(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const n of t.spaces||[]){const t=n.settings;"glow"===t?.fill_mode&&("boolean"!=typeof t.glow_enabled&&(t.glow_enabled=!0),t.fill_mode="none",e++,i++);for(const t of n.rooms||[]){const i=t.settings;"glow"===i?.fill_mode&&("boolean"!=typeof i.glow&&(i.glow=!0),delete i.fill_mode,e++,s++)}if(Mm(n,"segments")&&(delete n.segments,e++),Mm(n,"plan_scale")){const t=Number(n.plan_scale);Number.isFinite(t)&&t>=yh&&t<=bh&&(Mm(n,"plan_scale_x")||(n.plan_scale_x=t),Mm(n,"plan_scale_y")||(n.plan_scale_y=t),delete n.plan_scale,e++)}const o=Number(n.cell_cm);Mm(n,"cell_cm")&&(!Number.isFinite(o)||o<.1||o>1e3)&&(n.cell_cm=Number.isFinite(o)&&o>0?Cm(o,.1,1e3):5,e++);const a=Number(n.cell_cm),r=Number.isFinite(a)&&a>0?a:5;for(const t of n.wall_columns||[])if("circle"===t.shape)Mm(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&Mm(t,"angle")){const i=((Number(t.angle)||0)%90+90)%90;t.angle!==i&&(t.angle=i,e++)}for(const t of n.decor||[]){if(Mm(t,"width")){const i=Number(t.width);(Mm(t,"width_cm")||Number.isFinite(i))&&(Mm(t,"width_cm")||(t.width_cm=Number(Cm(i/Ch*r,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(Mm(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),Mm(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number(Cm(es*is(t)/Ch*r,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(Mm(t,"scale")||Mm(t,"size"))&&(delete t.scale,delete t.size,e++);let i=String(t.text??"");const s=[...i.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!Yi(t[1]));if(!(Mm(t,"entity")||Mm(t,"attr")||Mm(t,"unit")))continue;const n=String(t.unit??"").trim(),o="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,a=s||n?"":Ji(t.entity,o);if(s||a){if(a){const e=i.indexOf("{}");i=e>=0?i.slice(0,e)+a+i.slice(e+2):`${i}${i?" ":""}${a}`,t.text=i}delete t.entity,delete t.attr,delete t.unit,e++}}}return{total:e,glowSpaces:i,glowRooms:s}})(a);let p=d.total;for(const t of a.spaces||[]){const e=Dm(t);if(!e)continue;if(!dd(t.open_spans).length){const i=gd(e.rooms,null,fh,.02*Ch,!0);i.length&&(t.open_spans=fd(i,fh),p++)}}const u=(_=a.spaces||[],JSON.parse(JSON.stringify(_)));var _;const m=Z_(a.spaces||[],o.layout);let g=0,f=0,v=0,y="";const b=m.spaces.map(t=>{const e=function(t){const e=JSON.parse(JSON.stringify(t||{}));let i=Array.isArray(e.rooms)?e.rooms:[];const s=new Map;for(const t of i){const e=hi(t);if(e)for(let t=0;t<e.length;t++){const i=[...e[t]],n=[...e[(t+1)%e.length]],o=pa(i,n);if(!o)continue;const a=ga(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 t of[...s.values()].sort((t,e)=>t.key.localeCompare(e.key))){let e=null;for(const s of $a(t,i)){const t=r.get(_a(s.from));if(t&&t!==_a(s.to))continue;const n=wa(i,s);if(ka(i,n,s)){e=s,i=n;break}}e?(r.set(_a(e.from),_a(e.to)),n++,a=Math.max(a,Math.hypot(e.to[0]-e.from[0],e.to[1]-e.from[1]))):o++}e.rooms=i;for(const t of e.room_drafts||[]){if(!Array.isArray(t?.points)||t.points.length<2)continue;const e=t.points.map(t=>[...t]),i=e.slice(0,-1).flatMap((i,s)=>{const n=e[s+1],o=pa(i,n);return o?[{key:`${String(t.id||"")}:${s}`,a:[...i],b:[...n],axis:o.axis}]:[]});let s=e;for(const t of i){let e=null;for(const i of $a(t,[{poly:s}])){const t=s.map(t=>ba(t,i)),n=t.length>=4&&ma(t[0],t[t.length-1]),o=n?t.slice(0,-1):t;if(n?va(o):ya(o)){e=i,s=t;break}}e?(n++,a=Math.max(a,Math.hypot(e.to[0]-e.from[0],e.to[1]-e.from[1]))):o++}t.points=s}for(const t of e.partitions||[]){if(!Array.isArray(t?.a)||!Array.isArray(t?.b))continue;const i=pa(t.a,t.b);if(!i)continue;const s={key:String(t.id||ga(t.a,t.b)),a:[...t.a],b:[...t.b],axis:i.axis};let r=null;for(const i of $a(s,[{poly:[t.a,t.b]}])){const s=ba(t.a,i),n=ba(t.b,i),o=Math.hypot(n[0]-s[0],n[1]-s[1]);if(!(o>0))continue;const a=(e.openings||[]).filter(e=>"partition"===e?.host?.kind&&e.host.id===t.id).every(t=>{const e=Number(t.host.t),i=Number(t.length);return Number.isFinite(e)&&e>=0&&e<=1&&Number.isFinite(i)&&i>0&&e*o-i/2>=-1e-12&&e*o+i/2<=o+1e-12});if(a){r=i,t.a=s,t.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:e,report:{wallsStraightened:n,wallsStraightenSkipped:o,maxStraightenShift:a},changed:n>0}}(t);g+=e.report.wallsStraightened,f+=e.report.wallsStraightenSkipped;const i=Number(t?.cell_cm)>0?Number(t.cell_cm):5,s=e.report.maxStraightenShift/Dh*i;return s>v&&(v=s,y=String(t?.id||"")),e.space}),w=g?Z_(b,m.layout):{...m,spaces:b};a.spaces=w.spaces;const k=g?{...m.report,moved:m.report.moved+w.report.moved,coordsCanonicalized:m.report.coordsCanonicalized+w.report.coordsCanonicalized,maxShift:Math.max(m.report.maxShift,w.report.maxShift),maxShiftCm:Math.max(m.report.maxShiftCm,w.report.maxShiftCm),maxSpace:w.report.maxShiftCm>m.report.maxShiftCm?w.report.maxSpace:m.report.maxSpace,rotated:m.report.rotated+w.report.rotated,removedDrafts:m.report.removedDrafts+w.report.removedDrafts}:{...m.report};let x=0,$=0,S=0,M=0,C=0,D=0,P=0;for(let t=0;t<a.spaces.length;t++){const e=u[t],i=a.spaces[t],s=JSON.stringify({spans:e.open_spans||[],links:(e.rooms||[]).map(t=>[t.id,t.open_to||[]]),walls:e.walls||[]}),o=Dm(e),r=Dm(i);if(!o||!r)continue;const l=Rm(o.rooms),c=Rm(r.rooms),h=.02*Ch;let d=Sd(dd(e.open_spans),l,c,fh);const p=d.length;d=$d(d,r.rooms,fh,h),d=d.map(t=>{const e=cd(t,fh),s=[e[0],e[1]],n=[e[2],e[3]],o=[(e[0]+e[2])/2,(e[1]+e[3])/2],a=e[2]-e[0],r=e[3]-e[1],l=Math.hypot(a,r)||1,d=c.map(([t,e])=>[t[0],t[1],e[0],e[1]]).filter(t=>{const e=t[2]-t[0],i=t[3]-t[1],c=Math.hypot(e,i)||1;return Math.abs(a*i-r*e)/(l*c)<=1e-6&&pd(o,t).d<=4*h&&pd(s,t).d<=4*h&&pd(n,t).d<=4*h}).sort((t,e)=>Math.hypot(e[2]-e[0],e[3]-e[1])-Math.hypot(t[2]-t[0],t[3]-t[1]))[0];if(k.total++,!d)return t;const p=[[d[0],d[1]],[d[2],d[3]]],u=_d(s,d,p,Ch,2*h),_=_d(n,d,p,Ch,2*h);if(Math.hypot(_[0]-u[0],_[1]-u[1])<.5*Ch)return t;const m=Math.max(Math.hypot(u[0]-s[0],u[1]-s[1]),Math.hypot(_[0]-n[0],_[1]-n[1]));if(m>1e-6*Ch){k.moved++;const t=m/fh;t>k.maxShift&&(k.maxShift=t);const e=m/Ch*(Number(i.cell_cm)>0?Number(i.cell_cm):5);e>k.maxShiftCm&&(k.maxShiftCm=e,k.maxSpace=String(i.id||""))}return ld(u,_,fh)}),d=$d(d,r.rooms,fh,h),$+=Math.max(0,p-d.length);const _=d.map(t=>cd(t,fh));d.length?i.open_spans=d:delete i.open_spans,vd(i.rooms||[],r.rooms,_,h);const m=Array.isArray(e.walls)?e.walls.length:0;let g=Il(e.walls,l,c,Dh,fh);g=Tm(r.rooms,g,_,Dh,Number(i.cell_cm)>0?Number(i.cell_cm):5,Ch,fh),g=hc(r.rooms,g,_,Dh,Number(i.cell_cm)>0?Number(i.cell_cm):5,Ch,fh),g=Pl(g,i.rooms||[],Dh,1,_.map(t=>[t[0]/fh,t[1]/fh,t[2]/fh,t[3]/fh])),x+=Math.max(0,m-g.length),g.length?i.walls=g:delete i.walls;const f=om(i.partitions||[],{pitch:Dh,geometry:rm(i)});f.merged&&(S+=f.merged,i.partitions=f.partitions,am(i.openings,i.partitions,f.openingMoves,{coordScale:fh,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,gridPitch:Ch}));const v=Dm(i);if(v){const t=n(i,v,i.walls||[],_,{pitch:Dh,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,gridPitch:Ch,coordScale:fh});t.partitionsReconciled&&(M+=t.partitionsReconciled,C+=t.openingsRehosted,t.partitions.length?i.partitions=t.partitions:delete i.partitions,t.openings.length?i.openings=t.openings:delete i.openings,t.walls.length?i.walls=t.walls:delete i.walls),t.removedDrafts&&(D+=t.removedDrafts,t.roomDrafts.length?i.room_drafts=t.roomDrafts:delete i.room_drafts)}const y=JSON.stringify({spans:i.open_spans||[],links:(i.rooms||[]).map(t=>[t.id,t.open_to||[]]),walls:i.walls||[]});y!==s&&P++}const T=rl(a),R=al(w.layout),I=JSON.stringify(T)!==l||JSON.stringify(R)!==c;h<7&&I&&(T.model_version=7);const A=JSON.stringify(T)!==l||JSON.stringify(R)!==c,z=Number.isInteger(Number(T.model_version))?Number(T.model_version):h,F=A?k:{...k,moved:0,coordsCanonicalized:0,maxShift:0,maxShiftCm:0,maxSpace:"",rotated:0,removedDrafts:0},E=A?o.report:{...o.report,spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0};return{config:T,layout:R,report:{...F,modelFrom:h,modelTo:z,migrated:A?p:0,glowSpacesMigrated:A?d.glowSpaces:0,glowRoomsMigrated:A?d.glowRooms:0,canonicalized:A?P:0,wallsMerged:A?x:0,spansMerged:A?$:0,partitionsMerged:A?S:0,partitionsReconciled:A?M:0,openingsRehosted:A?C:0,redundantDraftsRemoved:A?D:0,wallsStraightened:A?g:0,wallsStraightenSkipped:f,maxStraightenShiftCm:A?v:0,maxStraightenSpace:A?y:"",latticeCoordinatesCanonicalized:A?r.canonicalized:0,latticeCoordinatesFar:A?r.far:0,latticeMaxShift:A?r.maxShift:0,latticeMaxShiftCm:A?r.maxShiftCm:0,latticeSpaces:A?r.spaces:[],...E},changed:A}}function Am(t,e,i){const s=(t?.spaces||[]).find(t=>t?.id===i),n=new Set((s?.rooms||[]).map(t=>String(t?.id||"")).filter(Boolean)),o=[...new Set((t?.markers||[]).filter(t=>!0!==t?.removed&&"string"==typeof t?.id).filter(t=>t.space===i||"string"==typeof t.room_id&&n.has(t.room_id)||e?.[t.id]?.s===i).map(t=>t.id))].sort((t,e)=>t.localeCompare(e));return{markerIds:o,count:o.length}}function zm(t){return Id({id:t?.id??"",cell_cm:t?.cell_cm,rooms:t?.rooms||[],walls:t?.walls||[],open_spans:t?.open_spans||[],openings:t?.openings||[],partitions:t?.partitions||[],room_drafts:t?.room_drafts||[],wall_columns:t?.wall_columns||[]})}function Fm(t,e,i=Ch,s=1e3){return gd(e.rooms,t?.open_spans,s,.02*i)}function Em(t,e,i=Ch){if(!e.length)return[];const s=t.filter(t=>t?.id),n=.02*i,o=[];for(let t=0;t<s.length;t++)for(let i=t+1;i<s.length;i++){const a=s[t],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=e.filter(t=>{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return h.some(t=>Hs(e,t)<4*n)}).map(t=>[...t]);d.length&&o.push({a:a,b:r,segs:d})}return o}function Nm(t,e,i,s=Ch,n=1e3){return(Array.isArray(t?.openings)?t.openings:[]).flatMap(t=>{const o={...t,rx:Number(t.x)*n,ry:Number(t.y)*n,rlen:Number(t.length)*n};if(!t.host)return[o];const a=Ep(t,e.partitions,n,i,s);return a.resolved?[{...t,rx:a.resolved.center[0],ry:a.resolved.center[1],rlen:a.resolved.length,angle:a.resolved.angle,partitionHost:a.resolved}]:[]})}function Om(t,e=()=>!0){return t.flatMap(t=>t.host&&t.partitionHost&&e(t)?[Hp(t.partitionHost)]:[])}function Hm(t,e,i,s,n,o,a=Ch,r=1e3){const l=lc(e.rooms,[...i],s,n,o,a,r);return t.flatMap(t=>{const e={x:t.rx,y:t.ry,angle:Number(t.angle)||0,length:t.rlen};return t.host?t.partitionHost&&qp(t.partitionHost,l,2e-4*a)?[e]:[]:[e]})}function Lm(t,e){const i=Dh,s=Ch,n=fh,o=Number.isFinite(Number(e.cellCm))&&Number(e.cellCm)>0?Number(e.cellCm):5,a=Array.isArray(t?.walls)?t.walls:[],r=Fm(t,e,s,n),l=Em(e.rooms,r,s).flatMap(t=>t.segs),c=Nm(t,e,o,s,n),h=Om(c),d=th(e,o,s,2e-4*s,h).all;return{space:e,walls:a,openCuts:l,openings:c,roomOpenings:Hm(c,e,a,l,i,o,s,n),partitionCuts:h,physicalBodies:d,wallKeyPitch:i,cellCm:o,gridPitch:s,coordScale:n}}function Bm(t,e,i){const s="string"==typeof t?.title?t.title.trim():"",n=null==t?.id?"":String(t.id).trim();return{spaceId:n,displayName:s||n||i(e+1)}}function qm(t,e={}){const i=(e.fingerprint||Id)(t),s=Array.isArray(t?.spaces)?t.spaces:[],n=e.fallbackSpaceName||(t=>`Space ${t}`),o=e.prepareSpace||Lm,a=e.wallPass||Rc,r=e.floorPass||Mc,l=[];for(let e=0;e<s.length;e++){const i=s[e],c=Bm(i,e,n);let h;try{const e=xh({...t,spaces:[i]})[0];if(!e)throw new Error("missing space model");h=o(i,e)}catch{l.push({...c,status:"failed",reason:"prepare-exception"});continue}const d=h.walls.length>0||h.physicalBodies.length>0;if(!h.space.rooms.length&&!d){l.push({...c,status:"not-applicable"});continue}let p=null;if(d){try{p=a(h.space.rooms,h.walls,h.openCuts,h.roomOpenings,h.wallKeyPitch,h.cellCm,h.gridPitch,h.coordScale,h.physicalBodies)}catch{l.push({...c,status:"failed",reason:"wall-exception"});continue}if(null==p){l.push({...c,status:"failed",reason:"wall-null"});continue}if("degraded-extra"===p.status){l.push({...c,status:"failed",reason:"wall-degraded-extra"});continue}if("failed-core"===p.status){l.push({...c,status:"failed",reason:"wall-failed-core"});continue}}if(h.space.rooms.length&&null==p?.paperGeom){let t;try{t=r(h.space.rooms,h.walls,h.openCuts,h.wallKeyPitch,h.cellCm,h.gridPitch,h.coordScale)}catch{l.push({...c,status:"failed",reason:"floor-exception"});continue}if(null==t){l.push({...c,status:"failed",reason:"floor-null"});continue}}l.push({...c,status:"ok"})}const c=l.filter(t=>"failed"===t.status);return{fingerprint:i,spaces:l,failures:c,ok:0===c.length}}class Wm{constructor(t=50){this._undo=[],this._redo=[],this._limit=Math.max(30,Math.floor(t))}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(t){this._undo.push(t),this._undo.length>this._limit&&this._undo.splice(0,this._undo.length-this._limit),this._redo=[]}undo(){const t=this._undo.pop()??null;return t&&this._redo.push(t),t}redo(){const t=this._redo.pop()??null;return t&&this._undo.push(t),t}clear(){this._undo=[],this._redo=[]}}const jm=new WeakMap;const Gm=(t,e,i)=>255===t[i+3]&&e.every((e,s)=>Math.abs(t[i+s]-e)<=2);async function Um(t){const e=t.defaultView;if(!e||!e.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=e.URL.createObjectURL(new Blob([o],{type:"image/svg+xml"}));try{const o=await new Promise((t,i)=>{const s=new e.Image,n=e.setTimeout(()=>i(new Error("SVG blend probe timeout")),1e3);s.onload=()=>{e.clearTimeout(n),t(s)},s.onerror=()=>{e.clearTimeout(n),i(new Error("SVG blend probe failed"))},s.src=a}),r=t.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 Gm(c,i,0)&&Gm(c,function(t,e){return[0,1,2].map(i=>Math.round(255-(255-t[i])*(255-e[i])/255))}(i,s),4)&&Gm(c,s,8)&&Gm(c,n,12)}catch{return!1}finally{e.URL.revokeObjectURL(a)}}const Vm={now:()=>performance.now(),requestFrame:t=>requestAnimationFrame(t),cancelFrame:t=>cancelAnimationFrame(t)},Km=t=>Math.max(0,Math.min(1,t)),Ym=(t,e,i)=>t+(e-t)*i,Jm=(t,e,i)=>{const s=1-t;return 3*s*s*t*e+3*s*t*t*i+t*t*t};function Xm(t,e){return{centerX:t.x+t.w/2,centerY:t.y+t.h/2,pixelsPerUnit:e>0&&t.w>0?e/t.w:1,viewBox:{...t}}}function Zm(t,e,i){const s=Math.max(1e-9,t.pixelsPerUnit),n=Math.max(1e-9,e/s),o=Math.max(1e-9,i/s);return{x:t.centerX-n/2,y:t.centerY-o/2,w:n,h:o}}function Qm(t){const e=t.trim(),i=/^rgba?\(\s*([\d.]+)[, ]+([\d.]+)[, ]+([\d.]+)(?:\s*[,/]\s*([\d.]+))?\s*\)$/i.exec(e);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(e)?.[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 tg(t,e,i){const s=Qm(t),n=Qm(e);if(!s||!n)return i<.5?t:e;const o=Km(i);return`rgba(${Math.round(Ym(s[0],n[0],o))}, ${Math.round(Ym(s[1],n[1],o))}, ${Math.round(Ym(s[2],n[2],o))}, ${Ym(s[3],n[3],o).toFixed(3)})`}function eg(t,e,i){const s=(t=>{const e=Km(t);if(0===e||1===e)return e;let i=0,s=1,n=e;for(let t=0;t<12;t++)n=(i+s)/2,Jm(n,.2,.2)<e?i=n:s=n;return Jm(n,.7,1)})(i),n=Ym(t.stageWidth,e.stageWidth,s),o=Ym(t.stageHeight,e.stageHeight,s),a=Math.max(1e-9,t.viewport.pixelsPerUnit),r=Math.max(1e-9,e.viewport.pixelsPerUnit),l={centerX:Ym(t.viewport.centerX,e.viewport.centerX,s),centerY:Ym(t.viewport.centerY,e.viewport.centerY,s),pixelsPerUnit:Math.exp(Ym(Math.log(a),Math.log(r),s))};return{presentedMode:s<.5?t.presentedMode:e.presentedMode,editorChromeHeight:Ym(t.editorChromeHeight,e.editorChromeHeight,s),stageWidth:n,stageHeight:o,viewport:{...l,viewBox:Zm(l,n,o)},stageColor:tg(t.stageColor,e.stageColor,s),paperColor:tg(t.paperColor,e.paperColor,s),sceneBrightness:Ym(t.sceneBrightness,e.sceneBrightness,s),architectureOpacity:Ym(t.architectureOpacity,e.architectureOpacity,s),backdropOpacity:Ym(t.backdropOpacity,e.backdropOpacity,s),viewWeight:Ym(t.viewWeight,e.viewWeight,s),editorWeight:Ym(t.editorWeight,e.editorWeight,s),toolbarContentOpacity:Ym(t.toolbarContentOpacity,e.toolbarContentOpacity,s)}}class ig{constructor(t,e=Vm){this._hooks=t,this._clock=e,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(t,e,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:t,to:e,presented:s<=0?e:t,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=t=>{const e=this._state;if(!e||e.token!==n)return;const i=Km((t-e.startedAt)/e.duration);e.presented=i>=1?e.to:eg(e.from,e.to,i),e.phase=i>=1?"settling":"running",this._hooks.frame(e),i<1?this._raf=this._clock.requestFrame(a):(this._raf=0,this._hooks.settled(e),this._state?.token===n&&(this._state=null))};return this._raf=this._clock.requestFrame(a),n}cancel(t=!0){this._raf&&this._clock.cancelFrame(this._raf),this._raf=0;const e=this._state;this._state=null,this._token++,e&&t&&(e.phase="settling",e.presented=e.to,this._hooks.frame(e),this._hooks.settled(e))}dispose(){this.cancel(!1)}}const sg="houseplan_card_labs_v1",ng=Object.freeze([Object.freeze({id:"iso",issue:89,since:"1.62.0",expires:"1.65.0",summary:"Volumetric plan renderer"})]);function og(t){const e=/^(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?$/.exec(String(t||"").trim());if(!e)return null;const i=e.slice(1).map(Number);return i.every(t=>Number.isSafeInteger(t))?i:null}function ag(t,e){for(let i=0;i<3;i++)if(t[i]!==e[i])return t[i]<e[i]?-1:1;return 0}function rg(t){if(!/^[a-z][a-z0-9-]*$/.test(t.id)||!Number.isInteger(t.issue)||t.issue<=0||!t.summary.trim())return!1;const e=og(t.since),i=og(t.expires);return!!e&&!!i&&ag(e,i)<0}function lg(t){const e=new Set;for(const i of t){if(!rg(i)||e.has(i.id))return!1;e.add(i.id)}return!0}function cg(t){const e=String(t||"").replace(/^#/,"");return new URLSearchParams(e)}function hg(t){return cg(t).get("space")||""}function dg(t){return t.getAll("hp-labs").flatMap(t=>t.split(",")).map(t=>t.trim()).filter(Boolean)}function pg(t,e,i,s=ng){const n=function(t,e=ng){const i=og(t),s=new Map;if(!i||!lg(e))return s;for(const t of e){const e=og(t.since),n=og(t.expires);ag(i,e)>=0&&ag(i,n)<0&&s.set(t.id,t)}return s}(i,s),o=new Set(lg(s)?s.map(t=>t.id):[]),a=new Set(function(t){if(!t)return[];try{const e=JSON.parse(t);return Array.isArray(e)?e.filter(t=>"string"==typeof t):[]}catch{return[]}}(e).filter(t=>n.has(t)));let r=!1;const l=t=>{if("off"===t)return a.clear(),void(r=!0);const e=t.startsWith("-"),i=e?t.slice(1):t;o.has(i)&&(r=!0,e||!n.has(i)?a.delete(i):a.add(i))};for(const e of dg(new URLSearchParams(String(t.search||"").replace(/^\?/,""))))l(e);for(const e of dg(cg(t.hash)))l(e);const c=Object.freeze([...a].sort());return{active:c,space:hg(t.hash),persist:r?JSON.stringify(c):void 0,knownUrlOperation:r}}let ug={active:Object.freeze([]),space:""},_g="",mg=!1,gg="";const fg=new Set;function vg(){if("undefined"==typeof window)return ug;const t=pg(window.location,function(){try{return window.localStorage.getItem(sg)}catch{return null}}(),_g);void 0!==t.persist&&function(t){try{window.localStorage.setItem(sg,t)}catch{}}(t.persist),ug={active:t.active,space:t.space},window.__hpLabs=t.active;for(const t of fg)t(ug);return ug}function yg(){vg()}function bg(t){return _g=t,"undefined"==typeof window?ug:(mg||(mg=!0,window.addEventListener("hashchange",yg),window.addEventListener("popstate",yg)),vg())}const wg=Object.freeze({rotDeg:0,tiltDeg:20,xyScale:1,zScale:1,origin:Object.freeze([500,500])});function kg(t){return[t.rotDeg,t.tiltDeg,t.xyScale,t.zScale,t.origin[0],t.origin[1]].every(Number.isFinite)&&Math.abs(t.xyScale)>1e-12&&Math.abs(Math.cos(t.tiltDeg*Math.PI/180))>1e-12}function xg(t,e,i=wg){if(!(kg(i)&&Number.isFinite(t[0])&&Number.isFinite(t[1])&&Number.isFinite(e)))throw new Error("invalid isometric projection input");const s=i.rotDeg*Math.PI/180,n=i.tiltDeg*Math.PI/180,o=t[0]-i.origin[0],a=t[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)-e*i.zScale*Math.sin(n)]}function $g(t,e=wg){if(!kg(e)||!Number.isFinite(t[0])||!Number.isFinite(t[1]))throw new Error("invalid isometric projection input");const i=e.rotDeg*Math.PI/180,s=e.tiltDeg*Math.PI/180,n=(t[0]-e.origin[0])/e.xyScale,o=(t[1]-e.origin[1])/(e.xyScale*Math.cos(s));return[e.origin[0]+n*Math.cos(i)+o*Math.sin(i),e.origin[1]-n*Math.sin(i)+o*Math.cos(i)]}function Sg(t=wg){return`matrix(${function(t=wg){if(!kg(t))throw new Error("invalid isometric camera");const e=t.rotDeg*Math.PI/180,i=t.tiltDeg*Math.PI/180,s=t.xyScale*Math.cos(e),n=-t.xyScale*Math.sin(e),o=t.xyScale*Math.sin(e)*Math.cos(i),a=t.xyScale*Math.cos(e)*Math.cos(i);return[s,o,n,a,t.origin[0]-s*t.origin[0]-n*t.origin[1],t.origin[1]-o*t.origin[0]-a*t.origin[1]]}(t).map(t=>Number(t.toFixed(12))).join(" ")})`}function Mg(t,e=wg){const{rect:i,wallHeight:s}=t,n=t.openingHeight??s,o=t.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(t=>[xg(t,-o,e),xg(t,r,e)]),c=l.map(t=>t[0]),h=l.map(t=>t[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 Cg=t=>`${Number(t[0].toFixed(4))} ${Number(t[1].toFixed(4))}`;function Dg(t){let e=0;for(let i=0;i<t.length;i++){const s=t[i],n=t[(i+1)%t.length];e+=s[0]*n[1]-n[0]*s[1]}return e/2}function Pg(t,e){const i=function(t){const e=(Array.isArray(t)?t:[]).filter(t=>Array.isArray(t)&&Number.isFinite(t[0])&&Number.isFinite(t[1])).map(t=>[Number(t[0]),Number(t[1])]),i=e[e.length-1];return e.length>1&&e[0][0]===i[0]&&e[0][1]===i[1]&&e.pop(),e}(t);if(i.length<3)return[];const s=!e;return Dg(i)>0===s?i:[...i].reverse()}function Tg(t,e,i){return t.length?`M ${t.map(t=>Cg(xg(t,i,e))).join(" L ")} Z`:""}function Rg(t,e,i){const s=e[1]-t[1],n=-(e[0]-t[0]),o=i.rotDeg*Math.PI/180;return s*Math.sin(o)+n*Math.cos(o)}function Ig(t,e,i=wg){if(!Number.isFinite(e)||e<0)throw new Error("invalid floor edge height");const s=(t||[]).map(t=>(t||[]).map((t,e)=>function(t,e){const i=Pg(t,e);if(!i.length)return i;let s=0;for(let t=1;t<i.length;t++)(i[t][0]<i[s][0]||i[t][0]===i[s][0]&&i[t][1]<i[s][1])&&(s=t);return[...i.slice(s),...i.slice(0,s)]}(t,e>0)).filter(t=>t.length>=3&&Math.abs(Dg(t))>=1e-9)).filter(t=>t.length&&Dg(t[0])>0);s.sort((t,e)=>{const i=t[0][0],s=e[0][0];return i[0]-s[0]||i[1]-s[1]||Math.abs(Dg(e[0]))-Math.abs(Dg(t[0]))});const n=[],o=[];let a=0;for(let t=0;t<s.length;t++){const r=s[t];for(const t of r)n.push(Tg(t,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(Rg(n,a,i)<=1e-9)continue;const r=xg(n,0,i),c=xg(a,0,i),h=xg(a,-e,i),d=xg(n,-e,i);o.push({d:`M ${Cg(r)} L ${Cg(c)} L ${Cg(h)} L ${Cg(d)} Z`,depth:Math.max(d[1],h[1]),component:t,edge:s,planEdge:[n,a]})}}return o.sort((t,e)=>t.depth-e.depth||t.component-e.component||t.edge-e.edge),{footprintPath:n.join(" "),sides:o,componentCount:s.length,edgeCount:a}}function Ag(t,e){return t^=Math.round(64*(Number.isFinite(e)?e:0)),Math.imul(t,16777619)>>>0}const zg=t=>t.every(Number.isFinite);function Fg(t,e,i,s){const n=t[0]*i,o=t[1]*s,a=e*Math.PI/180;return[n*Math.cos(a)-o*Math.sin(a),n*Math.sin(a)+o*Math.cos(a)]}function Eg(t,e,i,s,n,o,a){const r=t.flipH?-1:1,l="gate"===t.type?1:t.flipV?-1:1,c=(t.type,t.flipV,t.angle,t.face,{ox:0,oy:0}),h=function(t,e){return[t[0]+e[0],t[1]+e[1]]}([t.x+c.ox,t.y+c.oy],Fg(i,t.angle,r,l));return{leaf:e,hinge:h,closedVector:Fg(s,t.angle,r,l),quarterVector:Fg([-s[1],s[0]],t.angle,r,l),turnDeg:n,bottom:o,top:a}}const Ng=t=>`${Number(t[0].toFixed(4))} ${Number(t[1].toFixed(4))}`;function Og(t,e,i=wg){const s=Math.max(0,Math.min(1,Number.isFinite(e)?e:0));return t.leaves.map(e=>{const n=function(t,e){const i=t.turnDeg*e*Math.PI/180;return[t.hinge[0]+t.closedVector[0]*Math.cos(i)+t.quarterVector[0]*Math.sin(i),t.hinge[1]+t.closedVector[1]*Math.cos(i)+t.quarterVector[1]*Math.sin(i)]}(e,s),o=xg(e.hinge,e.bottom,i),a=xg(n,e.bottom,i),r=xg(n,e.top,i),l=xg(e.hinge,e.top,i);return{id:t.id,sourceIndex:t.sourceIndex,type:t.type,leaf:e.leaf,d:`M ${Ng(o)} L ${Ng(a)} L ${Ng(r)} L ${Ng(l)} Z`,shadowD:`M ${Ng(xg(e.hinge,0,i))} L ${Ng(xg(n,0,i))}`,depth:Math.max(o[1],a[1])}})}const Hg={window:120,door:90,passage:90,gate:300};function Lg(t){return Hg[t]}function Bg(t,e){const i=[t[0],t[1]],s=[e[0],e[1]];return function(t,e){return t[0]-e[0]||t[1]-e[1]}(i,s)<=0?{a:i,b:s}:{a:s,b:i}}function qg(t){const e=new Map;return t.forEach((t,i)=>{if(!t.kind||t.open)return;const s=Bg(t.a,t.b);if(Math.hypot(s.b[0]-s.a[0],s.b[1]-s.a[1])<=1e-9)return;const n=function(t,e){const i=t=>(Math.abs(t)<=5e-10?0:Math.round(1e6*t)/1e6).toFixed(6);return`${i(t[0])},${i(t[1])}>${i(e[0])},${i(e[1])}`}(s.a,s.b),o=e.get(n);if(o)return o.physicalHalfWidth=Math.max(o.physicalHalfWidth,t.half||0),o.sourceOrder=Math.min(o.sourceOrder,i),void(t.partitionHost&&(o.partitionHost&&o.partitionHost.id!==t.partitionHost.id?o.ambiguousPartitionHost=!0:o.partitionHost=t.partitionHost));e.set(n,{segmentKey:n,a:s.a,b:s.b,physicalHalfWidth:Math.max(0,t.half||0),sourceOrder:i,...t.partitionHost?{partitionHost:t.partitionHost}:{}})}),[...e.values()]}function Wg(t,e){if(Math.abs(t.distance-e.distance)>1e-9)return t.distance-e.distance;if(Math.abs(t.perpendicular-e.perpendicular)>1e-9)return t.perpendicular-e.perpendicular;return(t.target.segmentKey<e.target.segmentKey?-1:t.target.segmentKey>e.target.segmentKey?1:0)||t.target.sourceOrder-e.target.sourceOrder}function jg(t){const e=qg(t.intervals).map(e=>{const i=function(t,e){const i=e.b[0]-e.a[0],s=e.b[1]-e.a[1],n=Math.hypot(i,s),o=i/n,a=s/n,r=(t[0]-e.a[0])*o+(t[1]-e.a[1])*a,l=Math.max(0,Math.min(n,r)),c=e.a[0]+l*o,h=e.a[1]+l*a;return{along:l,length:n,x:c,y:h,distance:Math.hypot(t[0]-c,t[1]-h),perpendicular:Math.abs((t[0]-e.a[0])*a-(t[1]-e.a[1])*o)}}(t.pointer,e);return{target:e,...i,envelope:Math.max(t.baseTolerance,e.physicalHalfWidth+t.bodyPointerPadding)}}).filter(t=>t.distance<=t.envelope+1e-9).filter(e=>!function(t,e,i,s,n){const o=e.b[0]-e.a[0],a=e.b[1]-e.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=Bg(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]-e.a[0])*c-(i.a[1]-e.a[1])*l)>n)continue;const u=(t[0]-i.a[0])*d+(t[1]-i.a[1])*p,_=1e-9;if(!(u<=_||u>=h-_)&&Math.abs((t[0]-i.a[0])*p-(t[1]-i.a[1])*d)<=s+1e-9)return!0}return!1}(t.pointer,e.target,t.intervals,e.envelope,Math.max(1e-9,Math.min(t.baseTolerance,.04*t.gridStep)))),i=e.filter(e=>!!e.target.partitionHost&&t.renderedLength+2*e.target.physicalHalfWidth>e.length+1e-9).sort(Wg),s=e.filter(e=>!e.target.partitionHost||t.renderedLength+2*e.target.physicalHalfWidth<=e.length+1e-9).sort(Wg);let n=s[0];if(!n)return{candidate:null,jambBlockedTarget:i[0]?.target||null};const o=s.filter(t=>Math.abs(t.distance-n.distance)<=1e-9&&Math.abs(t.perpendicular-n.perpendicular)<=1e-9),a=o.filter(t=>t.target.partitionHost);if(a.length){if(1!==new Set(a.map(t=>t.target.partitionHost.id)).size||a.some(t=>t.target.ambiguousPartitionHost))return{candidate:null,jambBlockedTarget:null};const t=a[0],e=t.target.b[0]-t.target.a[0],i=t.target.b[1]-t.target.a[1],s=Math.hypot(e,i),r=e/s,l=i/s,c=o.every(e=>{const i=e.target.b[0]-e.target.a[0],s=e.target.b[1]-e.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((e.target.a[0]-t.target.a[0])*l-(e.target.a[1]-t.target.a[1])*r);return c&&h<=1e-9});if(!c)return{candidate:null,jambBlockedTarget:null};n=t}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,t.renderedLength)/2+u,l/2);let m=n.along;const g=Math.max(t.gridStep,1e-9),f=l/2;m=Math.abs(m-f)<=g/2?f:Math.round(m/g)*g,m=Math.max(_,Math.min(l-_,m));const v=r.a[0]+d*m,y=r.a[1]+p*m,b=Math.max(0,t.renderedLength)/2,w=m-b,k=m+b,x=Math.max(0,w),$=Math.max(0,l-k),S=[r.a[0]+d*(w-x/2),r.a[1]+p*(w-x/2)],M=[r.a[0]+d*(k+$/2),r.a[1]+p*(k+$/2)];let C=180*Math.atan2(h,c)/Math.PI;C>=90?C-=180:C<-90&&(C+=180);const D=Math.abs(m-f)<=1e-9;return{candidate:{presetRevision:t.preset.revision,geometryRevision:t.geometryRevision,pointer:[t.pointer[0],t.pointer[1]],type:t.preset.type,lengthCm:t.preset.lengthCm,flipH:t.preset.flipH,flipV:t.preset.flipV,x:v,y:y,angle:C,renderedLength:t.renderedLength,target:r,...r.partitionHost?{host:{...r.partitionHost,t:l>0?m/l:0}}:{},measure:{labels:[{distance:x,midpoint:S},{distance:$,midpoint:M}],guide:D?{x:v,y:y,angle:C}:null}},jambBlockedTarget:null}}function Gg(t,e,i,s,n=1e-6){return t.presetRevision===i&&t.geometryRevision===s&&Math.hypot(t.pointer[0]-e[0],t.pointer[1]-e[1])<=n}const Ug=(t,e)=>t[0]*e[1]-t[1]*e[0],Vg=(t,e)=>t[0]*e[0]+t[1]*e[1],Kg=(t,e)=>[t[0]-e[0],t[1]-e[1]],Yg=(t,e,i)=>[t[0]+e[0]*i,t[1]+e[1]*i];function Jg(t,e,i){return Math.abs(Ug(t,e))<=i}function Xg(t,e,i){return Math.abs(Ug(Kg(t,e),i))}function Zg(t,e,i,s,n){const o=e[0]-t[0],a=e[1]-t[1],r=Math.hypot(o,a);if(!(r>n&&i>0))return[];const l=[o/r,a/r];return function(t,e,i){const s=e.map(([e,i])=>[Math.max(0,Math.min(e,i)),Math.min(t,Math.max(e,i))]).filter(([t,e])=>e>t+i).sort((t,e)=>t[0]-e[0]||t[1]-e[1]),n=[];for(const t of s){const e=n[n.length-1];!e||t[0]>e[1]+i?n.push([...t]):e[1]=Math.max(e[1],t[1])}const o=[];let a=0;for(const[t,e]of n)t>a+i&&o.push([a,t]),a=Math.max(a,e);return t>a+i&&o.push([a,t]),o}(r,s.flatMap(e=>{const s=e.b[0]-e.a[0],o=e.b[1]-e.a[1],a=Math.hypot(s,o);if(!(a>n&&Jg(l,[s/a,o/a],1e-6)))return[];if(Xg(e.a,t,l)>1.1*Math.max(i,n)||Xg(e.b,t,l)>1.1*Math.max(i,n))return[];return[[Vg(Kg(e.a,t),l),Vg(Kg(e.b,t),l)]]}),n).flatMap(([e,s])=>{const n=Nl({a:Yg(t,l,e),b:Yg(t,l,s),halfDepth:i});return n?[n]:[]})}function Qg(t){const e=t.coordScale??1,i=Math.max(t.epsilon??2e-4*t.gridPitch,1e-9),s=[];for(const i of t.rooms||[]){if(!i?.id)continue;const n=rc(t.rooms,i.id,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e);if(!n||n.poly.length<3)continue;const o=Yl(n.poly,n.offsets);!o||o.length<3||s.push({roomId:i.id,profile:n,inner:o})}s.sort((t,e)=>t.roomId.localeCompare(e.roomId));const n=(t.roomOpenings||[]).map(t=>{const e=t.angle*Math.PI/180,i=[Math.cos(e),Math.sin(e)],s=Math.max(0,t.length)/2;return{a:Yg([t.x,t.y],i,-s),b:Yg([t.x,t.y],i,s)}}),o=[];for(const s of Ac(t.rooms,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e))for(const t of Zg(s.a,s.b,s.depthUnits/2,n,i))o.push({body:t});const a=new Map;for(const e of t.partitionCuts||[]){const t=a.get(e.hostId)||[];t.push(e),a.set(e.hostId,t)}for(const e of t.partitions||[]){const s=ml(e.cm,t.cellCm,t.gridPitch)/2;for(const t of Zg(e.a,e.b,s,a.get(e.id)||[],i))o.push({body:t,partitionId:e.id})}return{rooms:s,boundaries:o,epsilon:i}}function tf(t,e,i,s){const n=t.profile.offsets[e];if(!(n>0))return null;const o=function(t,e){const i=t[e],s=t[(e+1)%t.length],n=s[0]-i[0],o=s[1]-i[1],a=Math.hypot(n,o)||1;return function(t){let e=0;for(let i=0;i<t.length;i++){const s=t[i],n=t[(i+1)%t.length];e+=s[0]*n[1]-n[0]*s[1]}return e/2}(t)>=0?[-o/a,n/a]:[o/a,-n/a]}(t.profile.poly,e),a=Yg(i.center,o,n),r=[];for(let e=0;e<t.inner.length;e++){const n=t.inner[e],o=t.inner[(e+1)%t.inner.length],l=Kg(o,n),c=Math.hypot(l[0],l[1]);if(!(c>s&&Jg(i.axis,[l[0]/c,l[1]/c],1e-6)))continue;if(Xg(n,a,i.axis)>s||Xg(o,a,i.axis)>s)continue;const h=Vg(Kg(n,a),i.axis),d=Vg(Kg(o,a),i.axis);r.push([Math.min(h,d),Math.max(h,d)])}const l=function(t,e){t.sort((t,e)=>t[0]-e[0]||t[1]-e[1]);const i=[];for(const s of t){const t=i[i.length-1];!t||s[0]>t[1]+e?i.push([...s]):t[1]=Math.max(t[1],s[1])}return i}(r,s).find(([t,e])=>t<=s&&e>=-s);if(!l)return null;const[c,h]=l,d=Yg(a,i.axis,-i.half),p=Yg(a,i.axis,i.half),u=Math.max(0,-i.half-c),_=Math.max(0,h-i.half),m=u>0?Yg(a,i.axis,c):d,g=_>0?Yg(a,i.axis,h):p,f=Vg(o,i.normal)>=0?1:-1,v=(e,s,n)=>({from:e,to:s,label:[(e[0]+s[0])/2,(e[1]+s[1])/2],axis:i.axis,labelNormal:o,distance:n,roomId:t.roomId,roomSide:f,source:"room-face"});return{left:v(d,m,u),right:v(p,g,_)}}function ef(t){const e=Yg(t.center,t.axis,-t.half),i=Yg(t.center,t.axis,t.half),s=Yg(t.center,t.axis,Math.min(-t.half,t.targetLo)),n=Yg(t.center,t.axis,Math.max(t.half,t.targetHi)),o=(e,i)=>({from:e,to:i,label:[(e[0]+i[0])/2,(e[1]+i[1])/2],axis:t.axis,labelNormal:t.normal,distance:Math.hypot(i[0]-e[0],i[1]-e[1]),source:"host-end"});return[o(e,s),o(i,n)]}function sf(t,e,i,s){const n=Kg(i,e),o=Vg(n,n);if(!(o>s*s))return Math.hypot(...Kg(t,e))<=s;const a=Vg(Kg(t,e),n)/o;if(a<-s||a>1+s)return!1;const r=Yg(e,n,Math.max(0,Math.min(1,a)));return Math.hypot(...Kg(t,r))<=s}function nf(t,e,i){let s=!1;for(let n=0,o=e.length-1;n<e.length;o=n++){if(sf(t,e[o],e[n],i))return!0;const[a,r]=e[n],[l,c]=e[o];r>t[1]!=c>t[1]&&t[0]<(l-a)*(t[1]-r)/(c-r)+a&&(s=!s)}return s}function of(t,e,i,s,n,o){const a=Kg(s,i),r=Ug(e,a);if(Math.abs(r)<=o)return null;const l=Kg(i,t),c=Ug(l,a)/r,h=Ug(l,e)/r;return c<-o||c>n+o||h<-o||h>1+o?null:Math.max(0,Math.min(n,c))}function af(t,e,i){const s=ef(i),n="partition"===t.host?.kind?t.host.id:void 0;return[{base:s[0],direction:[-i.axis[0],-i.axis[1]]},{base:s[1],direction:i.axis}].map(({base:t,direction:i})=>{const s=t.distance,o=function(t,e,i,s,n,o){let a=null;for(const r of s)if(!n||r.partitionId!==n){if(nf(t,r.body,o))return 0;for(let s=0;s<r.body.length;s++){const n=of(t,e,r.body[s],r.body[(s+1)%r.body.length],i,o);null!=n&&(null==a||n<a)&&(a=n)}}return a}(t.from,i,s,e.boundaries,n,e.epsilon);if(null==o)return t;const a=Yg(t.from,i,o);return{...t,to:a,label:[(t.from[0]+a[0])/2,(t.from[1]+a[1])/2],distance:o,source:"connected-face"}})}function rf(t,e){const i=function(t){const e=t.angle*Math.PI/180,i=[Math.cos(e),Math.sin(e)],s=[-i[1],i[0]],n=[t.x,t.y],o=[t.target.a,t.target.b].map(t=>Vg(Kg(t,n),i));return{center:n,axis:i,normal:s,half:Math.max(0,t.renderedLength)/2,targetLo:Math.min(...o),targetHi:Math.max(...o)}}(t),s=e.rooms.flatMap(t=>{const s=function(t,e,i){const{profile:s}=t;let n=null;for(let t=0;t<s.poly.length;t++){if(!(s.kinds[t]&&s.offsets[t]>0))continue;const o=s.poly[t],a=s.poly[(t+1)%s.poly.length],r=Kg(a,o),l=Math.hypot(r[0],r[1]);if(!(l>i&&Jg(e.axis,[r[0]/l,r[1]/l],1e-6)))continue;if(Xg(o,e.center,e.axis)>i||Xg(a,e.center,e.axis)>i)continue;const c=Vg(Kg(o,e.center),e.axis),h=Vg(Kg(a,e.center),e.axis),d=Math.min(c,h),p=Math.max(c,h);if(d>-e.half+i||p<e.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&&t<n.edge)&&(n={edge:t,offset:u})}return n?.edge??null}(t,i,e.epsilon);return null==s?[]:[{room:t,edge:s}]});if(s.length>2)return ef(i);if(s.length){const t=s.map(({room:t,edge:s})=>tf(t,s,i,e.epsilon));if(t.some(t=>!t))return ef(i);const n=t;return[...n.map(t=>t.left),...n.map(t=>t.right)]}return af(t,e,i)}function lf(t){return!(!t.canEdit||t.kiosk||t.fixedFloor)&&("view"!==t.mode&&("mouse"===t.pointerType&&t.spaceCount>1))}const cf="1.67.0-rc.2",hf={badge:"display.badge",icon_ripple:"display.icon_ripple",value:"display.value",static_icon:"display.static_icon"},df={badge:"marker.display_hint_badge",icon_ripple:"marker.display_hint_icon_ripple",value:"marker.display_hint_value",static_icon:"marker.display_hint_static_icon"},pf=2e3,uf=1e3,_f=1500,mf=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const i=Number(e);return Number.isFinite(i)?i:null},gf=(t,e)=>{if(!t.has(e))return{hit:!1};const i=t.get(e);return t.delete(e),t.set(e,i),{hit:!0,value:i}},ff=(t,e,i,s)=>{for(t.delete(e),t.set(e,i);t.size>s;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}},vf=new Map;let yf=0;const bf=t=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(t??{})}`;let wf=1e4;const kf="houseplan_card_layout_v1",xf="houseplan_card_cfg_v1",$f="houseplan_card_zoom_v1",Sf="houseplan_card_nav_v1",Mf="houseplan_card_kiosk_v1",Cf="houseplan_card_view_v1",Df="hp-dialog, hp-help, hp-color-opacity, hp-device-preview",Pf=1e3,Tf=[[0,1],[45,.88],[70,.62],[86,.32],[100,0]],Rf=(t,e)=>{const i=Math.min(t.x,e.x),s=Math.min(t.y,e.y);return{x:i,y:s,w:Math.max(t.x+t.w,e.x+e.w)-i,h:Math.max(t.y+t.h,e.y+e.h)-s}},If=new Set(["select","draw","column","merge","split","resize","opening","boundary","wallthick","delroom"]),Af=2e3,zf=(t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)},Ff=t=>{history.pushState(null,"",t),zf(window,"location-changed",{replace:!1})},Ef=(t,e)=>{let i,s=null;const n=(...n)=>{clearTimeout(i),s=n,i=window.setTimeout(()=>{i=void 0;const e=s;s=null,e&&t(...e)},e)};return n.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const e=s;s=null,e&&t(...e)},n.cancel=()=>{clearTimeout(i),i=void 0,s=null},n.pending=()=>void 0!==i,n},Nf=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}};class Of extends ht{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 O_(this,()=>this._syncPointerHoverTargets()),this._devicePressAnimations=new Map,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...Rr},this._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=t=>{this._reducedMotion=t.matches,this._cancelDevicePressFeedback(),t.matches&&this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:t.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 ig({frame:t=>this._applyModeTransitionFrame(t),settled:t=>this._settleModeTransition(t)}),this._editorSecondary=new j_({root:()=>this.renderRoot,requestUpdate:()=>this.requestUpdate(),updateComplete:()=>this.updateComplete,clearTip:()=>{this._tip=null}}),this._editorSecondaryCopy={groupActive:(t,e)=>this._t("editor.group_active",{group:t,item:e}),openGroup:t=>this._t("editor.open_group",{group:t}),disabledAction:(t,e)=>this._t("editor.disabled_action",{action:t,reason:e})},this._tool="draw",this._geometryHistory=new Wm(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._wallUnionCache=null,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._glowClipCache=new Map,this._glowGeometryWarnings=new Set,this._lightBarrierCache=null,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._rszSel=null,this._rszDrag=null,this._rszEligibilityCache=null,this._rszPreview=null,this._rszLive=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._openWallAnchor=null,this._boundaryPointerType="mouse",this._boundaryRestoreGuard=null,this._boundaryTargetMemo=null,this._boundaryPreviewMemo=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:t=>this._guardTouchGesture(t)},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._alignDialog=null,this._settingsDialog=null,this._backupExportDialog=null,this._backupImportDialog=null,this._sunRaysCache=null,this._dayCycleTimer=0,this._dayCycleClockKey="",this._compassDrag=!1,this._importDialog=null,this._importQueue=[],this._importTotal=0,this._rulesCompiledSrc="",this._infoCard=null,this._nativeMoreInfoEntity=null,this._markerDialog=null,this._spaceDialog=null,this._keyHandler=t=>this._onKey(t),this._warmVp=null,this._warmVpArmed=!1,this._warmLongReturn=!1,this._warmRevivePending=!1,this._warmGen=++yf,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=t=>{if(this._continuity.visibility(t),this._dayCycleVisibility(t),"hidden"===t.kind)return this._clearTransientHover(!0),this._cancelDevicePressFeedback(),void(this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:this._cancelModeTransition(!0));if(this._vacJumpOnce=!0,!t.long){const t=Date.now();let e=!1;for(const i of this._activityRt.values())!i.flashKind||(i.expiresAt||i.flashTs+A_)>t||(i.flashTs=0,i.flashKind=null,i.expiresAt=0,e=!0);return void(e&&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=t=>{const e=this._effectiveProjection();this._labs=t;const i=this._effectiveProjection();this._convertProjectionView(e,i),this.requestUpdate()},this._onHashChange=()=>{if(this._hasFixedFloor)return;const t=this._hashSpace();if(t&&this._model.find(e=>e.id===t)&&t!==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(t),this._selId=null,this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._openWallAnchor=null,this._boundaryRestoreGuard=null,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 t=An(this.hass);t.revision===this._haRegistryRev&&this._devices.length||(this._haRegistryRev=t.revision,this._planHassMemo=null,this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate())},this._onConnReady=()=>{if(this._loadTries=0,clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,function(t){const e=Cn(t);e&&(Tn(t,e),Rn(t,e))}(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 Fd(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=Ef(()=>{if(this._serverStorage){const t=[...this._dirtyPos];this._dirtyPos.clear();for(const e of t){const t=ol(this._layout[e]);t&&(this._layout={...this._layout,[e]:t},this._sentPos.set(e,t),this.hass.callWS({type:"houseplan/layout/update",device_id:e,pos:t}).then(t=>this._noteLayoutRev(t)).catch(t=>this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))).finally(()=>{this._sentPos.get(e)===t&&this._sentPos.delete(e)}))}this._cacheSnapshot()}else this._layout=al(Ur(this._layout)),localStorage.setItem(kf,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._cancelBoundaryAnchor())return;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._rszDrag)return void this._rszCancelDrag();const t=this._geometryHistory.undo();t&&(this._applyGeometryState(t.before,!0)?this._showToast(this._t("history.undone",{name:t.name})):this._geometryHistory.clear())},this._redoGeometry=()=>{if(this._cancelBoundaryAnchor())return;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._rszDrag)return void this._rszCancelDrag();const t=this._geometryHistory.redo();t&&(this._applyGeometryState(t.after,!0)?this._showToast(this._t("history.redone",{name:t.name})):this._geometryHistory.clear())},this._saveConfigDebounced=Ef(()=>{this._serverCfg&&this._writeConfig().catch(t=>{"geometry-unsafe"!==t?.code&&("conflict"===t?.code?(this._showToast(this._t("toast.conflict")),this._cancelPath(),this._reloadConfigOnly(!0)):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(t)})))})},500),this._draftSegmentCms=[],this._closingWallCm=null,this._savePhysicalDialog=()=>{const t=this._physicalDialog,e=this._curSpaceCfg,i=this._spaceModel();if(!t||!e||!i)return;const s=mf(t.cm);if(null==s)return void this._showPhysicalRange("column"===t.kind?Wc:100);const n=this._imperial?2.54*s:s,o="column"===t.kind?Wc:100;if(!Number.isFinite(n)||n<1||n>o)return void this._showPhysicalRange(o);if("column"===t.kind){const e=i.wall_columns.find(e=>e.id===t.id);if(!e)return;const s=mf(t.angle||"0");if("circle"!==t.shape&&(null==s||s<0||s>=90))return void this._showToast(this._t("toast.physical_angle"));const o="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:n}:{id:t.id,shape:"square",center:e.center,cm:n,angle:s};if(i.wall_columns.some(e=>e.id!==t.id&&dh(e,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"))}const a=this._geometrySnapshot();if("partition"===t.kind){const i=(e.partitions||[]).find(e=>e.id===t.id);i&&(i.cm=n)}else if("column"===t.kind){const i=(e.wall_columns||[]).find(e=>e.id===t.id);i&&(i.cm=Gc(n),i.shape="circle"===t.shape?"circle":"square","square"===i.shape?i.angle=mf(t.angle||"0"):delete i.angle)}else{const i=(e.room_drafts||[]).find(e=>e.id===t.id);i?.segments?.[t.segment||0]&&(i.segments[t.segment||0].cm=n)}const r=this._commitPhysicalGeometry(this._t("history.physical_edit"),a);this._physicalDialog=null,r||this.requestUpdate()},this._deletePhysicalSelection=()=>{const t=this._physicalSel,e=this._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void this._deleteDraftWhole();const i=this._geometrySnapshot();if("partition"===t.kind){const i=(e.openings||[]).filter(e=>"partition"===e.host?.kind&&e.host.id===t.id).sort((t,e)=>(t.host?.t||0)-(e.host?.t||0));if(i.length)return void(this._partitionDeleteDialog={id:t.id,openings:i.map(t=>JSON.parse(JSON.stringify(t)))})}const s="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[s]=(e[s]||[]).filter(e=>e.id!==t.id),e[s].length||delete e[s],this._activeDraftId===t.id&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._commitPhysicalGeometry(this._t("history.physical_delete"),i)},this._confirmPartitionDelete=()=>{const t=this._partitionDeleteDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot();e.partitions=(e.partitions||[]).filter(e=>e.id!==t.id),e.partitions.length||delete e.partitions,e.openings=(e.openings||[]).filter(e=>"partition"!==e.host?.kind||e.host.id!==t.id),e.openings.length||delete e.openings,this._partitionDeleteDialog=null,this._physicalSel=null,this._physicalDialog=null,this._commitPhysicalGeometry(this._t("history.physical_delete"),i)},this._deleteDraftWhole=()=>{const t="draft"===this._physicalDialog?.kind?this._physicalDialog.id:"draft"===this._physicalSel?.kind?this._physicalSel.id:null,e=this._curSpaceCfg;if(!t||!e||!confirm(this._t("confirm.delete_draft")))return;const i=this._geometrySnapshot();e.room_drafts=(e.room_drafts||[]).filter(e=>e.id!==t),e.room_drafts.length||delete e.room_drafts,this._activeDraftId===t&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._commitPhysicalGeometry(this._t("history.physical_delete"),i)},this._deleteDraftSegment=()=>{const t=this._physicalDialog,e=this._curSpaceCfg;if(!t||"draft"!==t.kind||!e)return;if(!confirm(this._t("confirm.delete_draft_segment")))return;const i=(e.room_drafts||[]).findIndex(e=>e.id===t.id);if(i<0)return;const s=e.room_drafts[i],n=Math.max(0,Math.min(s.segments.length-1,t.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&&e.room_drafts.length>=200)return void this._showToast(this._t("toast.physical_limit"));const l=this._geometrySnapshot();e.room_drafts.splice(i,1,...o),e.room_drafts.length||delete e.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=t=>{const e=this._roomDeleteDialog,i=this._curSpaceCfg,s=this._spaceModel();if(!e||!i||!s)return;const n=s.rooms.find(t=>t.id===e.roomId);if(!n)return void(this._roomDeleteDialog=null);const o=this._openCuts(),a=Fu(lc(s.rooms,this._spaceWalls,o,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf).filter(t=>t.roomId===n.id),s.partitions,this._openingsR.map(t=>({...t,x:t.rx,y:t.ry})),.02*this._gridPitch),r=new Set(a.materialize.filter(t=>!t.reusePartitionId).map(t=>t.interval.key)).size;if(t&&(i.partitions||[]).length+r>Af)return void this._showToast(this._t("toast.physical_limit"));const l=this._geometrySnapshot(),c=cc(s.rooms,this._spaceWalls,o,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf);if(t){i.partitions||=[];const t=new Map,e=new Map,s=Date.now().toString(36);for(const n of a.materialize){let o=n.reusePartitionId?i.partitions.find(t=>t.id===n.reusePartitionId):e.get(n.interval.key);o||(o={id:`partition-room-${s}-${e.size}`,a:[n.interval.a[0]/Pf,n.interval.a[1]/Pf],b:[n.interval.b[0]/Pf,n.interval.b[1]/Pf],cm:n.interval.cm},i.partitions.push(o),e.set(n.interval.key,o)),t.set(n.interval.key,o)}for(const e of i.openings||[]){const i=a.openingIntervals.get(e.id),s=i?t.get(i):null,n=this._openingsR.find(t=>t.id===e.id);if(!s||!n)continue;const o={a:[s.a[0]*Pf,s.a[1]*Pf],b:[s.b[0]*Pf,s.b[1]*Pf]};e.host={kind:"partition",id:s.id,t:Eu([n.rx,n.ry],o)}}}else if(a.removeOpeningIds.length){const t=new Set(a.removeOpeningIds);i.openings=(i.openings||[]).filter(e=>!t.has(e.id)),i.openings.length||delete i.openings}i.rooms=i.rooms.filter(t=>t.id!==n.id),this._cfgEpoch++,this._commitOpenSpans();const h=this._normalizeWalls(c,this._openCuts());h.length?i.walls=h:delete i.walls,this._roomDeleteDialog=null,this._commitPhysicalGeometry(this._t(t?"history.delete_room_keep_walls":"history.delete_room_with_walls"),l),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()},this._rebindPartitionOpening=()=>{const t=this._openingDialog;if(!t?.id)return;const e=t.id;this._activateMarkupTool("opening"),"opening"===this._tool&&(this._openingRebindId=e,this._openingPreset={type:t.type,lengthCm:t.lengthCm,flipH:t.flipH,flipV:t.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 t=this._curSpaceCfg,e=this._path.slice(0,-1);if((t.partitions||[]).length+e.length>Af)return void this._showToast(this._t("toast.physical_limit"));const i=this._geometrySnapshot(),s=lu(e.length,[...this._draftSegmentCms,this._closingWallCm??void 0],this._drawWallCm,ll);t.partitions||=[];const n=Date.now().toString(36);for(let i=0;i<e.length;i++){const o=e[i],a=e[(i+1)%e.length];t.partitions.push({id:`partition-${n}-${i}`,a:[o[0]/Pf,o[1]/Pf],b:[a[0]/Pf,a[1]/Pf],cm:s[i]})}this._activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==this._activeDraftId),t.room_drafts.length||delete t.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 t=this._spaceDialog;if(t)if(t.pickSaved)this._spaceDialog={...t,pickSaved:!1};else{this._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.hass.callWS({type:"houseplan/plans/list"}),e=this._spaceDialog;e&&(this._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,saved:[],savedBusy:!1}),this._showToast(this._t("toast.plans_list_failed",{err:this._errText(t)}))}}},this._aspectJob=null,this._dayCycleTick=()=>{if(!this.isConnected||"hidden"===this.ownerDocument.visibilityState)return;const t=this._dayCycleState();if(!t)return this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),void(this._dayCycleClockKey="");const e=mr(t);e!==this._dayCycleClockKey&&(this._dayCycleClockKey=e,this.requestUpdate())},this._sunShown=!1,this._sunOut=!1,this._sunOutTimer=0,this._openSettingsDialog=()=>{if(!this._norm)return;const t=this._glowRadiusCm,e=this._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this._settingsDialog={colors:JSON.parse(JSON.stringify(this._fillColors)),glowRadius:e,bgColor:hs(this._settings,{bgColor:null})||null,northDeg:$r(this._settings,{}),bgMode:Sr(this._settings,{}),sunRays:Mr(this._settings,{}),busy:!1}},this._openAlignDialog=()=>this._previewAlignDialog(!1),this._toggleOptimizeLivePositions=()=>{const t=this._alignDialog;t&&!t.busy&&t.report.liveMissingPositions.length&&this._previewAlignDialog(!t.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 t=this._settings.icon_rules,e=(t&&t.length?t:Jt).map(t=>({...t}));this._rulesDialog={rules:e,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(t,e){const i=String(t||"").trim();if(!i)return e;const s=document.createElement("span");s.style.cssText=`position:absolute;visibility:hidden;color:${i}`,this.renderRoot.append(s);const n=getComputedStyle(s).color||e;return s.remove(),n}_currentModeVisual(t=this._mode){const e=this._modeTransition.presented||this._modeTransitionVisual;if(e)return{...e,viewport:{...e.viewport,viewBox:{...e.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:t,editorChromeHeight:"view"===t?0:s?.getBoundingClientRect().height||0,stageWidth:i.clientWidth,stageHeight:i.clientHeight,viewport:Xm(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"===t?.35:1,backdropOpacity:a&&Number(getComputedStyle(a).opacity)||1,viewWeight:"view"===t?1:0,editorWeight:"view"===t?0:1,toolbarContentOpacity:"view"===t?0:1}}_viewForModeTarget(t,e,i,s,n){const o=Ni(this._baseVb(),s/Math.max(1,n)),a=Math.min(Of.ZOOM_MAX,Math.max(Of.ZOOM_MIN,t)),r=o.w/a,l=o.h/a,c=e??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(t){return"view"!==t?"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(t){return"view"===t&&this._spaceModel()?.bg?this._cssColor("var(--ha-card-background, var(--card-background-color, #111))","rgb(17, 17, 17)"):"rgb(255, 255, 255)"}_targetBrightness(t){return 1}_applyModeTransitionFrame(t){t.targetMode===this._mode&&(this._modeTransitionVisual=t.presented,this._view={...t.presented.viewport.viewBox},this.requestUpdate())}_settleModeTransition(t){if(t.targetMode!==this._mode)return;const e=this._modeTransitionRequest;this._view={...t.to.viewport.viewBox},this._zoom=this._modeTransitionTargetZoom,this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._lastValidStageSize=[t.to.stageWidth,t.to.stageHeight],"view"===this._mode&&(this._saveZoom(),this._viewModeSnap=null,this._modeTransitionEditorCamera=null),this.requestUpdate(),this.updateComplete.then(()=>{if(!this.isConnected||e!==this._modeTransitionRequest||this._modeTransitionBusy||t.targetMode!==this._mode)return;const i=this._stageEl;if(i&&i.clientWidth>0&&i.clientHeight>0&&(Math.abs(i.clientWidth-t.to.stageWidth)>.5||Math.abs(i.clientHeight-t.to.stageHeight)>.5)){const t=this._view;this._lastValidStageSize=[i.clientWidth,i.clientHeight],this._applyView(this._zoom,t?t.x+t.w/2:void 0,t?t.y+t.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(t=!0){const e=!!this._modeTransition.state;this._modeTransitionRequest++,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransition.cancel(t),t&&e||(this._modeTransitionVisual=null)}_adoptMode(t){this._cancelModeTransition(!1),this._mode=t,"view"!==t&&(this._editorChromeMode=t)}_commitViewModeAtomic(t,e,i,s){if(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._modeTransitionForceAtomic=!1,this._zoom=e,t){const n=this._stageEl?.clientWidth||t.stageWidth,o=Math.max(1,t.stageHeight+t.editorChromeHeight);this._view=this._viewForModeTarget(e,i,s,n,o),this._lastValidStageSize=[n,o]}else this._view=null;this._viewModeSnap=null,this._modeTransitionEditorCamera=null,this._saveZoom(),this.requestUpdate()}_prepareModeTransition(t,e,i,s,n,o){this.updateComplete.then(()=>{if(!this.isConnected||t!==this._modeTransitionRequest||this._mode!==i)return void(t===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,e.stageHeight+e.editorChromeHeight),h=Math.max(1,c-l),d=this._stageEl?.clientWidth||e.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:Xm(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(e,u,i,this._reducedMotion||_?0:220)})}get _hasFixedFloor(){return!!this._config&&Object.prototype.hasOwnProperty.call(this._config,"floor")}_fixedFloorState(t=this._model,e=this._loadOk){const i=this._config?.floor,s=function(t){if(!t.hasFloor)return{kind:"absent"};const e=t.floor;if("string"==typeof e)return e.length?t.spaceIds.includes(e)?{kind:"valid",id:e,source:"id"}:{kind:"invalid",reason:"unknown-id",value:e}:{kind:"invalid",reason:"empty-id",value:e};if("number"==typeof e){if(!Number.isFinite(e))return{kind:"invalid",reason:"non-finite-index",value:e};if(!Number.isInteger(e))return{kind:"invalid",reason:"fractional-index",value:e};if(e<0)return{kind:"invalid",reason:"negative-index",value:e};const i=t.spaceIds[e];return void 0===i?{kind:"invalid",reason:"out-of-range-index",value:e}:{kind:"valid",id:i,source:"index"}}return{kind:"invalid",reason:"invalid-type",value:e}}({spaceIds:t.map(t=>t.id),hasFloor:this._hasFixedFloor,floor:i});return!this._hasFixedFloor||e?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(t,e=!1){if(e||!this._hasFixedFloor)return!0;const i=this._fixedFloorState();return"valid"===i.kind&&i.id===t}_commitSpace(t,e=!1){return!!this._canCommitSpace(t,e)&&(t!==this._space&&(this._clearTransientHover(!0),this._cancelDevicePressFeedback()),this._space=t,!0)}_slideTo(t,e){if(t===this._space)return!0;if(!this._canCommitSpace(t))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(t),this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._openWallAnchor=null,this._boundaryRestoreGuard=null,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=e,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slideTimer=void 0,this._slide="",this.requestUpdate()},190),this.requestUpdate()),!0}get _canReorderTabs(){return lf({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:"mouse",spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})}_tabPointerDown(t,e){lf({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:t.pointerType,spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})&&(Nf(t),this._tabDragRelease=t=>this._tabPointerUp(t),window.addEventListener("pointerup",this._tabDragRelease),window.addEventListener("pointercancel",this._tabDragRelease),this._tabDrag={id:e,pointerId:t.pointerId,x:t.clientX,y:t.clientY,moved:!1,targetId:null,placement:null})}_tabDropTargetAt(t,e,i){const s=this._model.map(t=>t.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(t<r.left||t>r.right||e<r.top||e>r.bottom)continue;const l=s.indexOf(o);return l<0?null:{targetId:o,placement:l<n?"before":"after"}}return null}_tabPointerMove(t){const e=this._tabDrag;if(!e||e.pointerId!==t.pointerId)return;if(!(e.moved||(i=t.clientX-e.x,s=t.clientY-e.y,Math.hypot(i,s)>=4)))return;var i,s;const n=this._tabDropTargetAt(t.clientX,t.clientY,e.id);e.moved&&e.targetId===n?.targetId&&e.placement===n?.placement||(this._tabDrag={...e,moved:!0,targetId:n?.targetId||null,placement:n?.placement||null})}_tabPointerUp(t){const e=this._tabDrag;if(e&&e.pointerId!==t.pointerId)return;const i="pointerup"===t.type&&e?.moved?this._tabDropTargetAt(t.clientX,t.clientY,e.id):null;"pointerup"===t.type&&e?.moved&&this._suppressNextTabClick(),this._endTabDrag(),e?.moved&&i&&this._commitTabOrder(e.id,i.targetId)}_endTabDrag(){this._tabDrag=null,this._tabDragRelease&&(window.removeEventListener("pointerup",this._tabDragRelease),window.removeEventListener("pointercancel",this._tabDragRelease),this._tabDragRelease=null)}_tabClick(t){if(this._tabSuppressClick)return this._tabSuppressClick=!1,clearTimeout(this._tabSuppressClickTimer),void(this._tabSuppressClickTimer=void 0);this._pickSpace(t)}_suppressNextTabClick(){this._tabSuppressClick=!0,clearTimeout(this._tabSuppressClickTimer),this._tabSuppressClickTimer=window.setTimeout(()=>{this._tabSuppressClick=!1,this._tabSuppressClickTimer=void 0},0)}_commitTabOrder(t,e){const i=this._serverCfg;if(!i||!this._canReorderTabs)return;const s=this._model.map(t=>t.id),n=function(t,e,i){const s=t.indexOf(e),n=t.indexOf(i);if(s<0||n<0||s===n)return t;const o=t.slice();return o.splice(s,1),o.splice(n,0,e),o}(s,t,e);if(n===s)return;const o=new Map(this._devices.map(t=>[String(t.id),String(t.area||"")])),a=function(t,e,i,s=()=>""){if(!i)return[];const n=[];for(const o of t){if(!o||!0===o.removed)continue;const t="string"==typeof o.id?o.id:"";if(!t)continue;if("string"==typeof o.space&&o.space)continue;const a=("string"==typeof o.area?o.area:"")||s(t)||"";a&&e[a]||n.push({id:t,space:i})}return n}(i.markers||[],Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),s[0]||"",t=>o.get(t)||"");if(a.length){const t=new Map(a.map(t=>[t.id,t.space]));for(const e of i.markers||[]){const i=t.get(String(e.id));i&&(e.space=i)}}i.spaces=function(t,e){const i=new Map(e.map((t,e)=>[t,e]));return t.map((t,e)=>({space:t,index:e})).sort((t,e)=>(i.get(String(t.space?.id))??Number.MAX_SAFE_INTEGER)-(i.get(String(e.space?.id))??Number.MAX_SAFE_INTEGER)||t.index-e.index).map(t=>t.space)}(i.spaces||[],n),this._saveConfig(),this._tabOrderWarned||(this._tabOrderWarned=!0,this._showToast(this._t("toast.space_order_changed")))}_pickSpace(t){if(this._endTabDrag(),t===this._space)return;const e=this._model.map(t=>t.id),i=e.indexOf(this._space),s=e.indexOf(t);this._navApplied=!0,this._showFar=!1,this._frame=null,this._slideTo(t,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 t=this._model.map(t=>t.id),e=t.indexOf(this._space);this._slideTo(t[(e+1)%t.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}_newContinuityController(){return new Rd(()=>{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(Cf,JSON.stringify(this._viewPreference))}catch{}}_logicalViewCenter(t){const e=this._view;if(!e)return null;const i=[e.x+e.w/2,e.y+e.h/2],s="iso"===t?$g(i):i;return{x:s[0],y:s[1]}}_convertProjectionView(t,e){if(t===e)return;const i=this._logicalViewCenter(t);this._view=null;const s=i?"iso"===e?xg([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(t){if(!this._labsIso||"view"!==this._mode)return;const e=this._effectiveProjection();if(this._viewPreference={...this._viewPreference,[this._space]:t},"iso"===t){const t=this._isoSceneKey();t&&this._isoFallback.delete(t)}this._saveViewPreference();const i=this._effectiveProjection();this._convertProjectionView(e,i)}_hashSpace(){return hg(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 t=(e=this.ownerDocument,jm.get(e)?.resolved);var e;void 0!==t&&(this._glowScreenBlend=t),this._continuityUnsub?.(),this._continuityUnsub=Pd(this.ownerDocument,this._pageVisibility),super.connectedCallback(),this._pointerModality.connect(this.ownerDocument.defaultView);const i=this.ownerDocument.defaultView?.MutationObserver;i&&(this._pointerHoverObserver=new i(t=>{for(const e of t)for(const t of e.addedNodes)this._syncPointerHoverSubtree(t)}),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(t){const e=jm.get(t);if(e)return e.promise;const i={promise:Promise.resolve(!1)};return i.promise=Um(t).catch(()=>!1).then(t=>(i.resolved=t,t)),jm.set(t,i),i.promise}(this.ownerDocument).then(t=>{t!==this._glowScreenBlend&&(this._glowScreenBlend=t,this.isConnected&&this.requestUpdate())}),this.hass&&this._ensureHaRegistryAuthority(),window.addEventListener("keydown",this._keyHandler),this._signer.start(()=>this.hass,()=>Is(this._serverCfg)),this._syncCycleTimer(),window.addEventListener("hashchange",this._onHashChange),this._labsUnsub?.(),this._labsUnsub=function(t,e){fg.add(e);const i=bg(t);return"undefined"==typeof window&&e(i),()=>{fg.delete(e)}}(cf,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},_f)),!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 t of this._activityRt.values())clearTimeout(t.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 t of this._glowFadeTimers.values())clearTimeout(t);for(const t of this._glowEnterRafs.values())cancelAnimationFrame(t);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._openWallAnchor=null,this._boundaryRestoreGuard=null,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(t){if("Escape"===t.key&&this._vacFit)return this._vacFit=null,this._showToast(this._t("vac.cal_cancelled")),void t.stopPropagation();if("Escape"===t.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 void(this._alignDialog=null);if(this._backupImportDialog)return void(this._backupImportDialog=null);if(this._backupExportDialog)return void(this._backupExportDialog=null);if(this._settingsDialog)return void(this._settingsDialog=null);if(this._markerDialog)return void(this._markerDialog=null);if(this._openingDialog)return void(this._openingDialog=null);if(this._physicalDialog)return void(this._physicalDialog=null);if(this._backdropDialog)return void(this._backdropDialog=null);if(this._decorShapeDialog)return void(this._decorShapeDialog=null);if(this._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0);if(this._editorSecondary.hasOpenGroup)return t.preventDefault(),void this._editorSecondary.closeGroup(!0)}const e=t.composedPath?.()||[t.target],i=e.some(t=>t?.matches?.('input, textarea, select, [contenteditable="true"]')),s=e.some(t=>t?.classList?.contains?.("editor-secondary")),n=t.ctrlKey||t.metaKey,o=t.key.toLowerCase(),a=/^[a-z]$/.test(o),r="z"===o||!a&&"KeyZ"===t.code,l="y"===o||!a&&"KeyY"===t.code,c=n&&(r&&t.shiftKey||l),h=n&&r&&!t.shiftKey;if("decor"===this._mode){if((h||c)&&i)return;return c?(t.preventDefault(),void this._redoGeometry()):h?(t.preventDefault(),this._decorDraft?void(this._decorDraft=null):this._decorMove||this._dtDrag||this._bdDrag?void this._cancelDecorGesture():void this._undoGeometry()):"Delete"!==t.key&&"Backspace"!==t.key||!this._decorSel||i||s?void("Escape"===t.key&&(t.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"))):(t.preventDefault(),void this._decorDeleteSel())}if(this._markup&&(!h&&!c||!i)){if(("Delete"===t.key||"Backspace"===t.key)&&this._physicalSel&&!i&&!s)return t.preventDefault(),void this._deletePhysicalSelection();if(c)return t.preventDefault(),void this._redoGeometry();if(h)return t.preventDefault(),this._rszDrag?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"===t.key)return this._physicalDrag||this._physicalRotate?(t.preventDefault(),void this._cancelPhysicalGesture()):this._roomDialog?(t.preventDefault(),void this._roomDialogCancel()):"draw"===this._tool&&this._path.length?(t.preventDefault(),void this._finishWallChain()):this._physicalSel?(t.preventDefault(),void(this._physicalSel=null)):"resize"===this._tool?(t.preventDefault(),this._rszDrag?void this._rszCancelDrag():void(this._rszSel?this._rszSel=null:this._tool="draw")):"split"===this._tool?(t.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?(t.preventDefault(),void(this._mergeSel?this._mergeSel=null:this._tool="draw")):this._wallDialog?(t.preventDefault(),void(this._wallDialog=null)):"boundary"===this._tool?(t.preventDefault(),void(this._openWallAnchor?this._cancelBoundaryAnchor():this._tool="draw")):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool&&"column"!==this._tool||(t.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 t=this._geometrySnapshot();this._path=this._path.slice(0,-1),this._draftSegmentCms=this._draftSegmentCms.slice(0,-1);const e=this._curSpaceCfg,i=(e.room_drafts||[]).findIndex(t=>t.id===this._activeDraftId);return void(i>=0&&(this._path.length<2?(e.room_drafts.splice(i,1),e.room_drafts.length||delete e.room_drafts,this._activeDraftId=null):e.room_drafts[i]={id:this._activeDraftId,points:this._path.map(t=>[t[0]/Pf,t[1]/Pf]),segments:this._draftSegmentCms.map(t=>({cm:t}))},this._commitPhysicalGeometry(this._t("history.draft_segment_delete"),t)))}this._path=this._path.slice(0,-1)}}_undoActiveDraftPoint(){const t=this._activeDraftId,e=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=t?this._spaceModel()?.room_drafts.find(e=>e.id===t):null;s?(this._activeDraftId=s.id,this._path=s.points.map(t=>[...t]),this._draftSegmentCms=this._adoptDraftCms(this._path,s.segments.map(t=>t.cm),s.id),this._resumeDraftBySpace[this._space]=s.id):(this._activeDraftId=null,this._path=e?[e]:[],this._draftSegmentCms=[],t&&this._resumeDraftBySpace[this._space]===t&&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(t){for(const t of vf.values())for(const e of t)clearTimeout(e.evict);vf.clear(),wf=t&&t>0?t:1e4}static _warmBootStats(){let t=0,e=0;const i=[];for(const s of vf.values())for(const n of s)t++,n.dlg&&(e++,i.push(n.dlg.kind));return{keys:vf.size,slots:t,dlgs:e,drafts:i}}setConfig(t){const e=this._config,i=!!e&&Object.prototype.hasOwnProperty.call(e,"floor");this._config={icon_size:2.5,show_temperature:!0,live_states:!0,show_signal:!0,...t};(i!==this._hasFixedFloor||e?.floor!==this._config.floor)&&(this._hashApplied=!1,this._navApplied=!1,this._warmVpArmed=!1),this._config.kiosk&&(this._booting=!1,this._bootFading=!1),!this._hasFixedFloor&&t.default_floor&&this._commitSpace(t.default_floor,!0);try{this._zoomBySpace=JSON.parse(localStorage.getItem($f)||"{}")||{}}catch{this._zoomBySpace={}}try{const t=JSON.parse(localStorage.getItem(Cf)||"{}")||{};this._viewPreference=Object.fromEntries(Object.entries(t).filter(t=>"flat"===t[1]||"iso"===t[1]))}catch{this._viewPreference={}}this._labs=bg(cf);try{const t=JSON.parse(localStorage.getItem(Mf)||"null");this._kioskScale={icon:Es(t?.icon),font:Es(t?.font)}}catch{}try{const t=JSON.parse(localStorage.getItem(xf)||"null");t&&t.config&&Array.isArray(t.config.spaces)&&(this._serverCfg=t.config,this._cfgEpoch++,this._cfgRev=t.rev||0,this._cfgContentFingerprint=t.config_fingerprint||Id(t.config),this._layout=t.layout||{},this._layoutRev=t.layout_rev||0,this._layoutContentFingerprint=t.layout_fingerprint||Id(this._layout),this._virtualLights=mn(t.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 t=bf(this._config);if(this._warmKey===t&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const e=this.parentNode,i=this._warmIdx(e),s=vf.get(t);if(!s||!s.length)return;const n=s.find(t=>t.owner===this._warmGen);if(n)return this._warmLongReturn=!!n.freed&&Date.now()-n.freed>=Md,clearTimeout(n.evict),n.evict=0,n.freed=0,n.live=!0,this._warmSlot=n,this._warmKey=t,n.frameFingerprint&&this._continuity.adoptCompleteFrame(n.frameFingerprint),void(!this._devices.length&&n.devices?.length&&(this._devices=[...n.devices]));const{slot:o,sure:a}=((t,e,i,s)=>{const n=t=>{const e=!!i&&t.place?.deref()===i;return e&&t.idx===s?4:t.live?0:e?3:2};let o=null,a=0,r=0,l=null;for(const i of t){if(i.owner===e)continue;l=i;const t=n(i);t<=0||(t>a?(o=i,a=t,r=1):t===a&&r++)}return!o||r>1?{slot:o||l,sure:!1}:{slot:o,sure:!0}})(s,this._warmGen,e,i);o&&(this._warmLongReturn=!!o.freed&&Date.now()-o.freed>=Md,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},_f)),this._warmKey=t,a?(clearTimeout(o.evict),o.evict=0,o.owner=this._warmGen,o.place=e?new WeakRef(e):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:e?new WeakRef(e):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(t){const e=t?.children;if(!e)return-1;for(let t=0;t<e.length;t++)if(e[t]===this)return t;return-1}_warmRelease(){const t=this._warmSlot,e=this._warmKey;this._warmSlot=null,this._warmKey=null,t&&e&&(t.freed=Date.now(),t.owner===this._warmGen&&(t.live=!1),this._warmScheduleEvict(t,e))}_warmTrim(t){for(;t.length>4;){const e=t.findIndex(t=>!t.live);if(e<0)break;clearTimeout(t[e].evict),t.splice(e,1)}}_warmScheduleEvict(t,e){if(clearTimeout(t.evict),!(t.dlg||t.vp&&"view"!==t.vp.mode))return;const i=t.freed,s=t.owner;t.evict=window.setTimeout(()=>{if(t.evict=0,t.freed!==i||t.owner!==s)return;var n;t.dlg=null,t.vp=(n=t.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,t.frameFingerprint="";const o=vf.get(e);if(!t.live&&o&&o.length>1){const e=o.indexOf(t);e>=0&&o.splice(e,1)}},wf+250)}_warmAdoptViewport(t){const e=this._warmVp;if(!e||this._warmSlot?.path!==location.pathname)return void(this._warmVp=null);const i=this._fixedFloorState();if(this._hashApplied||!this._model.find(t=>t.id===e.space)||"valid"===i.kind&&i.id!==e.space||this._hasFixedFloor&&"valid"!==i.kind)return void(this._warmVp=null);this._commitSpace(e.space,!0),this._navApplied=!0,this._adoptMode("view"!==e.mode&&this._canEdit&&!t.kiosk?e.mode:"view"),this._pendingNavMode="view"===e.mode||this._canEdit||t.kiosk?null:e.mode,this._zoom=e.zoom;const s=this._effectiveProjection(),n=s===e.projection&&this._labsIso===e.activeLabsIso;if(this._view=n&&e.view?{...e.view}:null,this._viewModeSnap=n&&e.snap?{...e.snap}:null,!n&&e.logicalCenter){const t="iso"===s?xg([e.logicalCenter.x,e.logicalCenter.y],0):[e.logicalCenter.x,e.logicalCenter.y];this._applyView(e.zoom,t[0],t[1])}var o;this._tool="openwall"===(o=e.tool)||"closewall"===o?"boundary":"opening"===(o=function(t){return"partition"===t?"draw":t}(o))?"draw":"string"==typeof o&&If.has(o)?o:"draw",this._decorTool=e.decorTool,this._showHidden=e.showHidden,this._showFar!==e.showFar&&(this._showFar=e.showFar,this._frame=null),this._selId=e.selId,this._rszSel=e.rszSel,this._decorSel=e.decorSel,this._warmVpArmed=!0}_warmPatch(t,e=!1){if(this._config?.kiosk)return;const i=bf(this._config);if(!this._warmSlot||this._warmKey===i){if(!this._warmSlot){if(!e)return;const t=this.parentNode;this._warmKey=i,this._warmSlot={owner:this._warmGen,path:location.pathname,place:t?new WeakRef(t):null,idx:this._warmIdx(t),live:!0,hdrH:this._hdrH,stageH:0,vp:null,frameFingerprint:"",devices:null,dlg:null,freed:0,evict:0};const s=vf.get(i)||[];for(s.push(this._warmSlot),vf.set(i,s),this._warmTrim(s);vf.size>8;){const t=vf.keys().next().value;if(void 0===t||t===i)break;for(const e of vf.get(t)||[])clearTimeout(e.evict);vf.delete(t)}}Object.assign(this._warmSlot,t)}}_warmViewportState(){const t=this._effectiveProjection();return{space:this._space,mode:this._mode,projection:t,activeLabsIso:this._labsIso,logicalCenter:this._logicalViewCenter(t),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._rszSel,decorSel:this._decorSel}}_warmDialogState(){const t=(t,e)=>({kind:t,space:this._space,mode:this._mode,data:e});return this._tapConfirm||this._alignDialog||this._mergeDialog||this._importDialog||this._backupExportDialog||this._backupImportDialog?null:this._openingInfo?t("openingInfo",this._openingInfo.id):this._infoCard?t("info",this._infoCard.id):this._rulesDialog?this._rulesDialog.busy?null:t("rules",this._rulesDialog):this._settingsDialog?this._settingsDialog.busy?null:t("settings",this._settingsDialog):this._markerDialog?this._markerDialog.busy?null:t("marker",this._markerDialog):this._openingDialog?t("opening",this._openingDialog):this._backdropDialog?t("backdrop",this._backdropDialog):this._decorShapeDialog?t("decorShape",this._decorShapeDialog):this._decorTextDialog?t("decorText",this._decorTextDialog):this._roomDialog?t("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:t("space",this._spaceDialog):null}_warmSnapshot(){if(this._booting||this._config?.kiosk||"steady"!==this._continuity.state)return;const t={vp:this._warmViewportState(),frameFingerprint:this._continuity.frameFingerprint,devices:this._devices};if(this._warmRevivePending||(t.dlg=this._warmDialogState()),this.isConnected&&this._warmSlot?.owner===this._warmGen){const e=this.parentNode;t.place=e?new WeakRef(e):null,t.idx=this._warmIdx(e)}this._warmPatch(t)}_warmReviveDialog(){this._warmRevivePending=!1;const t=this._warmSlot;if(this._warmReviveTimer=void 0,!t||!t.dlg)return;const e=t.dlg,i=t.freed;if(t.dlg=null,t.freed=0,clearTimeout(t.evict),t.evict=0,i&&!(Date.now()-i>wf)&&e.space===this._space&&e.mode===this._mode){switch(e.kind){case"space":this._spaceDialog={...e.data,busy:!1,savedBusy:!1};break;case"marker":this._markerDialog={...e.data,busy:!1};break;case"settings":this._settingsDialog={...e.data,busy:!1};break;case"rules":this._rulesDialog={...e.data,busy:!1};break;case"opening":this._openingDialog={...e.data};break;case"backdrop":this._backdropDialog={...e.data};break;case"decorShape":this._decorShapeDialog={...e.data};break;case"decorText":{this._decorTextDialog={...e.data};const t=String(this._decorTextDialog?.text??"").length;this._decorTextSelection={start:t,end:t};break}case"room":{const t=e.data;this._roomEditId=t.editId,this._roomFill=t.fill,this._roomCustomFill=t.customFill||null,this._roomTempSrc=t.tempSrc,this._roomHumSrc=t.humSrc,this._roomSrcOpen=t.srcOpen,this._roomSrcFilter=t.srcFilter,this._roomNameScale=t.nameScale,this._roomLabelScale=t.labelScale,this._areaSel=t.areaSel,this._nameSel=t.nameSel,this._pendingSplit=t.pendingSplit,this._wallFaceBatch=t.wallFaceBatch||null,this._path=t.path,this._wallFaceBatch&&(this._activeDraftId=this._wallFaceBatch.activeDraftId,this._draftSegmentCms=[...this._wallFaceBatch.activeCms]),this._roomDialog=!0;break}case"info":{const t=this._devices.find(t=>t.id===e.data);t&&(this._infoCard=t);break}case"openingInfo":{const t=(this._curSpaceCfg?.openings||[]).find(t=>t.id===e.data);t&&(this._openingInfo=t);break}}this.requestUpdate()}}_cacheSnapshot(){if(this._serverCfg)try{this._cfgContentFingerprint=Id(this._serverCfg),this._layoutContentFingerprint=Id(this._layout),this._virtualLights=function(t,e,i){if(t.configRev===i)return t;const s=new Set((Array.isArray(e?.markers)?e.markers:[]).filter(yn).map(t=>t.id));return{...t,configRev:i,off:new Set([...t.off].filter(t=>s.has(t)))}}(this._virtualLights,this._serverCfg,this._cfgRev),localStorage.setItem(xf,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(t,e,i="plan"){return this._booting&&!this._continuity.hasCompleteFrame?this._continuity.token:(this._continuityDataReady=e,this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._resumeSettling=!0,this._continuity.beginCandidate(t,i))}_continuityStageValid(){const t=this._stageEl;return!!t&&t.clientWidth>0&&t.clientHeight>0}_continuityAssetsReady(){if(!this._model.length)return!1;const t=this._model.length?this._spaceModel():null;return!t?.bg?.href||this._signer.isReady(this.hass,t.bg.href)}_initialSpaceSelection(t,e=this._loadOk){const i=this._fixedFloorState(t,e);return"valid"===i.kind?{id:i.id,source:"fixed"}:this._hasFixedFloor?{id:null,source:"none"}:function(t){const e=new Set(t.spaceIds.filter(t=>!!t));if(!e.size)return{id:null,source:"none"};const i=[["hash",!1===t.acceptHash?null:t.hashSpace],["current",t.preserveCurrent?t.currentSpace:null],["saved",t.savedSpace],["default",t.defaultSpace],["first",t.spaceIds[0]]];for(const[t,s]of i)if(s&&e.has(s))return{id:s,source:t};return{id:null,source:"none"}}({spaceIds:t.map(t=>t.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(t,e=this._loadOk){const i=this._initialSpaceSelection(t,e);return i.id?(this._commitSpace(i.id,!0),"hash"===i.source&&(this._hashApplied=!0),"saved"===i.source&&(this._navApplied=!0),i):i}_candidateBackdrop(t,e=this._space){const i=xh(t),s=this._fixedFloorState(i,!0);if(this._hasFixedFloor&&"valid"!==s.kind)return"";const n=this._initialSpaceSelection(i,!0).id||(i.some(t=>t.id===e)?e:i[0]?.id);return i.find(t=>t.id===n)?.bg?.href||""}_visualFrameFingerprint(){const t=this._stageEl,e=t?[t.clientWidth,t.clientHeight]:[0,0];return Ad([this._cfgRev,this._cfgContentFingerprint||Id(this._serverCfg),this._layoutRev,this._layoutContentFingerprint||Id(this._layout),this._space,this._mode,this._view,e,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 t=this._continuity.token;if(this._candidateDeviceSnapshot&&this._candidateDeviceSnapshot!==this._visibleDeviceSnapshot&&this._stagedDeviceSnapshotToken!==t)return this._stagedDeviceSnapshotToken=t,void this.requestUpdate();this._continuityPaintToken!==t&&(this._continuityPaintToken=t,this._continuity.candidateReady(t)&&this._continuity.commitAfterPaint(t,{updateComplete:()=>this.updateComplete,stageValid:()=>this.isConnected&&this._continuityStageValid(),assetsReady:()=>this._continuityAssetsReady(),frameFingerprint:()=>this._visualFrameFingerprint()}).then(e=>{e&&t===this._continuity.token?(this._resumeSettling=!1,this._renderSnapshotAt=Date.now(),this._candidateDeviceSnapshot&&(this._visibleDeviceSnapshot=this._candidateDeviceSnapshot),this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._warmSnapshot()):t===this._continuity.token&&(this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._candidateDeviceSnapshot=null,this.requestUpdate())}))}_onBackdropLoaded(t,e){this._signer.markLoaded(this.hass,t,e),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 U;const t="connection"===this._continuity.recoveryReason;return W`<div class="recoveryoverlay phase-${this._continuity.overlayPhase}"
|
||
role="status" aria-live="polite" aria-atomic="true"
|
||
@pointerdown=${t=>t.stopPropagation()}
|
||
@click=${t=>t.stopPropagation()}
|
||
@wheel=${t=>t.stopPropagation()}>
|
||
<ha-icon icon="mdi:home-sync-outline"></ha-icon>
|
||
<span>${this._t(t?"continuity.restore_connection":"continuity.restore_plan")}</span>
|
||
${"recovery-error"===this._continuity.state?W`<button class="btn on" @click=${this._retryContinuity}>${this._t("continuity.retry")}</button>`:U}
|
||
</div>`}houseplanContinuityTrace(){return[...this._continuityHistory,...this._continuity.trace].slice(-80).map(t=>({...t,...t.stage?{stage:[...t.stage]}:{}}))}getCardSize(){return 12}get _norm(){return!(!this._serverCfg||!this._serverCfg.spaces.length)}_cfgFingerprint(){const t=this._serverCfg?.spaces||[];let e=t.length+":";for(const i of t){e+=(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 t of i.rooms||[]){const i=t.poly?.[0],s=t.poly?.[t.poly.length-1];e+=(t.poly?.length||0)+"."+(t.id||"")+"."+(t.open_to||[]).join("+")+"."+(t.area||"")+"."+JSON.stringify(t.settings||0)+"."+(t.x??"")+","+(t.y??"")+","+(t.w??"")+","+(t.h??"")+","+(i?i[0]+"/"+i[1]:"")+","+(s?s[0]+"/"+s[1]:"")+";"}}return e}get _model(){if(!this._serverCfg)return[];const t=this._cfgEpoch+"|"+this._cfgFingerprint();if(this._modelCache&&this._modelCache.key===t)return this._modelCache.model;const e=this._buildModel();return this._modelCache={key:t,model:e},e}_buildModel(){if(!this._serverCfg)return[];const t=this._renderCfg;return xh(t).map((e,i)=>{const s=t.spaces[i]?.plan_url;return e.bg&&s?{...e,bg:{...e.bg,href:s}}:e})}_spaceModel(){return this._hasFixedFloor?Ed(this._model,this._space):(t=this._model,e=this._space,t.find(t=>t.id===e)??t[0]);var t,e}_spaceModelById(t){return Ed(this._model,t)}_syncEmptySpaceState(){if(!!this._serverCfg&&0===this._serverCfg.spaces.length){if(!this._emptySpaceStateActive){this._emptySpaceStateActive=!0;for(const t of this._pointers.keys())for(const e of this.renderRoot.querySelectorAll("*"))try{e.hasPointerCapture?.(t)&&e.releasePointerCapture(t)}catch{}this._pointers.clear(),this._panStart=null,this._panLock=null,this._pinchStart=null,this._swipeStart=null,this._drag=null,this._rlResize=null,this._vacFit=null,this._compassDrag=!1,this._cancelModeTransition(!1),this._mode="view",this._clearGeometryGesture(),this._geometryHistory.clear(),this._resumeDraftBySpace={},this._tip=null,this._hoverRoom=null,this._openingInfo=null,this._closeInfoCard(),this._markerDialog=null,this._physicalDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._roomDialog=!1,"edit"===this._spaceDialog?.mode&&(this._spaceDialog=null),this._editorSecondary.closeForNavigation(),this._saveConfigDebounced.cancel(),this._frame=null,this._planSnapGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._decorSnapCache=null,this._commitSpace("",!0)}}else this._emptySpaceStateActive=!1}get _areaToSpace(){const t={};for(const e of this._model)for(const i of e.rooms)i.area&&(t[i.area]={space:e.id,room:i});return t}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._settings.filter_seeded?this._showHidden:!!this._settings.show_all}_toggleShowAll(){if(this._serverCfg){if(this._settings.filter_seeded)return this._showHidden=!this._showHidden,void this.requestUpdate();this._serverCfg={...this._serverCfg,settings:{...this._serverCfg.settings,show_all:!this._settings.show_all}},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const t=this._serverCfg,e=function(t){const e=t.hass,i=t.registry||An(e),s=Nn(e,i),n=On(e,i),{areaToSpace:o,markers:a,settings:r,excluded:l,iconRules:c}=t,h=!1!==r.group_lights,d=wo(a),p=yo(s,h).filter(t=>!ko(s,t.eid,d)),u=new Set(p.map(t=>t.area)),_=qn(s),m=Wn(a,n),g=new Set(a.map(t=>t.binding)),f=[];for(const t of Object.values(s.devices)){const n=t.area_id;if(!n||!o[n])continue;if("service"===t.entry_type)continue;if(g.has("device:"+t.id))continue;if("active"!==Bn(e,"device:"+t.id,i).kind)continue;const a=jn(s,t.id,_[t.id]||[],m);if(a.partial&&!a.entityIds.length)continue;const r=a.entityIds,d=Gn(s,t,r);let p=l.has(d)||"Group"===t.model||/scene/i.test(t.model||"")||/bridge/i.test((t.model||"")+(t.name||""))||"myheat"===d&&!!t.via_device_id;if(!p&&h&&u.has(n)){const e=(t.name_by_user||t.name||"").trim();"mdi:lightbulb"===bo(s,e,t.model,r,c)&&(p=!0)}p&&f.push("device:"+t.id)}return f}({hass:this.hass,registry:this._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,firstSpaceId:this._model[0]?.id||"",iconRules:this._iconRules});if(!e.length&&t.settings?.filter_seeded)return;t.markers=t.markers||[];const i=[];for(const s of e){const e="h"+s.slice(s.indexOf(":")+1);t.markers.push({id:e,binding:s,hidden:!0}),i.push(s.slice(s.indexOf(":")+1))}const s={...t.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(t=>!i.includes(t))),t.settings=s,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}get _iconRules(){const t=this._settings.icon_rules;if(!t||!Array.isArray(t)||!t.length)return;const e=JSON.stringify(t);return e!==this._rulesCompiledSrc&&(this._rulesCompiledSrc=e,this._rulesCompiled=Xt(t)),this._rulesCompiled}get _fillColors(){return ps(this._settings)}get _excluded(){const t=this._settings.exclude_integrations;return t?new Set(t):Yt}willUpdate(t){t.has("_serverCfg")&&this._cfgEpoch++,this._syncEmptySpaceState(),t.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 t=this._stageEl;t&&!this._roViewport&&(this._roViewport=new ResizeObserver(()=>this._refitView()),this._roViewport.observe(t)),t&&this._booting&&!this._bootTimer&&this._bootWatch();const e=this.renderRoot.querySelector(".hdr");if(e&&t&&!this._roHdr){const i=()=>{const e=this.renderRoot.querySelector("ha-card");if(!e)return;const i=t.getBoundingClientRect().top-e.getBoundingClientRect().top,s=Math.min(Math.max(e.getBoundingClientRect().top,0),120),n=Math.round(i+s);n>=0&&n!==this._hdrH&&(this._hdrH=n),n>=0&&!this._booting&&!this._config?.kiosk&&t.clientHeight>0&&this._warmPatch({hdrH:n,stageH:t.clientHeight})};this._roHdr=new ResizeObserver(()=>requestAnimationFrame(i)),this._roHdr.observe(e),this._onWinResize=()=>requestAnimationFrame(i),window.addEventListener("resize",this._onWinResize),i()}if(t&&!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 t=function(t){const e=t?.floors;if(!e||"object"!=typeof e)return[];const i=[];for(const t of Object.values(e))t&&t.floor_id&&i.push({id:t.floor_id,name:t.name||t.floor_id,level:t.level??null});return i.sort((t,e)=>{const i=t.level??1e9,s=e.level??1e9;return i!==s?i-s:t.name.localeCompare(e.name)}),i}(this.hass);t.length?this._importDialog={floors:t.map(t=>({...t,checked:!0}))}:this._openSpaceDialog("create")}}_adoptStructuralResponses(t,e,i){const s=t?.config,n=s&&Array.isArray(s.spaces)?s:null,o=Id(n),a=o!==(this._cfgContentFingerprint||Id(this._serverCfg));if(a&&(this._geometryHistory.clear(),this._pendingPhysicalWrites.clear(),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=n,this._cfgContentFingerprint=o),this._cfgRev=t?.rev??this._cfgRev,t&&("virtual_lights"in t||"config"in t)){const e=gn(this._virtualLights,t.virtual_lights,this._cfgRev,"virtual_lights"in t);vn(e)!==vn(this._virtualLights)&&(this._virtualLights=e,this._capturedSnapshotVirtual="")}let r=!1;if(void 0!==e||void 0!==i){const t=i??e?.layout??{},s=Id(t);r=s!==(this._layoutContentFingerprint||Id(this._layout)),r&&(this._layout=t,this._layoutContentFingerprint=s),this._layoutRev=e?.rev??this._layoutRev}return this._canOptimizeUndo=!(!t?.can_optimize_undo&&!e?.can_optimize_undo),this._undoKind=t?.undo_kind||e?.undo_kind||null,"boolean"==typeof t?.can_write&&(this._serverCanWrite=t.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 t=this._pendingNavMode;return this._pendingNavMode=null,this._setMode(t,!1),!0}async _loadFromServer(){this._loading=!0,this._loadTries++;const t=this._space,e=!!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=Id(n)!==(this._cfgContentFingerprint||Id(this._serverCfg))||Id(s?.layout??{})!==(this._layoutContentFingerprint||Id(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._adoptStructuralResponses(i,s),this._adoptInitialSpace(this._model,!0),this._resumePendingNavMode(),this._cacheSnapshot(),this._warmVpArmed&&this._space===this._warmVp?.space?this._warmVpArmed=!1:e&&this._space===t||this._restoreZoom(),this._loadOk=!0,this.hass.callWS({type:"houseplan/trail/get"}).then(t=>{this._vacSrvTrails=t?.trails||{},this.requestUpdate()}).catch(()=>{}),this._ensureLiveSyncSubscriptions()}catch(t){if(this._serverCfg)this._scheduleLoadRetry(!0);else if(this._loadTries>=8){this._serverStorage=!1;try{this._layout=JSON.parse(localStorage.getItem(kf)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1,this._continuityDataReady=!0,this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}}_ensureLiveSyncSubscriptions(){const t=this.hass?.connection;if(!t)return;if(t!==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=t),this._liveSyncAttempt)return;const e=this._liveSyncGeneration,i=[],s=(s,n,o,a)=>{s()||i.push(async()=>{const i=await t.subscribeEvents(a,o);e===this._liveSyncGeneration&&this.isConnected&&this.hass?.connection===t&&!s()?n(i):i?.()})};if(s(()=>this._unsubCfg,t=>{this._unsubCfg=t},"houseplan_config_updated",t=>{const e=Number(t?.data?.rev??-1);e!==this._cfgRev&&this._reloadConfigOnly(!1,e)}),s(()=>this._unsubTrail,t=>{this._unsubTrail=t},"houseplan_trail_updated",async()=>{try{const t=await this.hass.callWS({type:"houseplan/trail/get"});this._vacSrvTrails=t?.trails||{},this.requestUpdate()}catch{}}),s(()=>this._unsubLayout,t=>{this._unsubLayout=t},"houseplan_layout_updated",t=>this._onLayoutEvent(Number(t?.data?.rev??-1))),s(()=>this._unsubVirtual,t=>{this._unsubVirtual=t},"houseplan_virtual_light_updated",t=>{const e=wn(this._virtualLights,t?.data);e!==this._virtualLights&&(this._virtualLights=e,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}),!i.length)return;const n=function(t){return Promise.allSettled(t.map(t=>Promise.resolve().then(t)))}(i).then(()=>{});this._liveSyncAttempt=n,n.finally(()=>{this._liveSyncAttempt===n&&(this._liveSyncAttempt=null)})}async _reloadConfigOnly(t=!1,e){if(!t){if(void 0!==e&&e<=this._cfgRev)return;if(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(!1,e),400))}this._beginContinuityCandidate("config-reload",!1);try{const t=await this.hass.callWS({type:"houseplan/config/get"}),e=t?.config&&Array.isArray(t.config.spaces)?t.config:null;if(Id(e)!==(this._cfgContentFingerprint||Id(this._serverCfg))&&!await this._signer.prepareImage(this.hass,this._candidateBackdrop(e)))return this._continuity.note("asset-failed"),void this._scheduleLoadRetry(!0);const i=this._space;this._adoptStructuralResponses(t),this._adoptInitialSpace(this._model,!0),this._resumePendingNavMode(),this._cacheSnapshot(),this._space!==i&&this._restoreZoom(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}catch(t){this._showToast(this._t("toast.cfg_reload_failed",{err:this._errText(t)}))}finally{this._continuityDataReady=!0,this.requestUpdate()}}_scheduleLoadRetry(t=!1){if(void 0!==this._loadRetryTimer)return;const e=Math.min(8e3,500*2**Math.min(4,Math.max(1,this._loadTries-7)));this._loadRetryTimer=window.setTimeout(()=>{this._loadRetryTimer=void 0,!t&&this._loadOk||this._loading||!this.hass||this._loadFromServer()},e)}_ensureHaRegistryAuthority(){const t=this.hass?.connection||null;t&&t!==this._haRegistryConnection&&(this._haRegistryRelease?.(),this._haRegistryConnection=t,this._haRegistryRev=-1,this._haBindingCacheKey="",this._planHassMemo=null,this._haRegistryRelease=In(this.hass,this._onHaRegistryUpdate),this._onHaRegistryUpdate())}get _haRegistry(){return An(this.hass)}get _planHass(){const t=this._haRegistry,e=Fn(this.hass,t),i=this._planHassMemo;if(i&&i.hass===this.hass&&i.sig===e)return i.active;const s=Nn(this.hass,t),n=On(this.hass,t);return this._planHassMemo={hass:this.hass,sig:e,active:s,full:n},s}_captureRenderDeviceSnapshot(){if(!this.hass)return;const t=Date.now(),e=[...this._activityRt.entries()].map(([e,i])=>`${e}:${i.gen}:${i.flashTs}:`+(i.flashKind&&(i.expiresAt||i.flashTs+A_)>t?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===e)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=t=>{if(!t)return;const e=t.indexOf(":");if(e<0)return void a.add(t);const i=t.slice(0,e),s=t.slice(e+1);"device"===i?r.add(s):"entity"===i&&a.add(s)};for(const t of this._model)for(const e of t.rooms)e.area&&l.add(e.area),c(e.settings?.temp_source),c(e.settings?.hum_source);for(const t of this._serverCfg?.spaces||[])for(const e of t.openings||[])for(const t of gi(e))a.add(t);for(const t of this._serverCfg?.spaces||[])for(const e of t.decor||[])if("text"===e.kind){for(const t of String(e.text||"").matchAll(/\{([^{}\r\n]+)\}/g)){const e=Yi(t[1]);e?.entity&&a.add(e.entity)}e.entity&&a.add(e.entity),o.set(`decor:${t.id}:${e.id}`,ts(e.text,e,s,t=>!!s.entities?.[t]&&!!s.states?.[t]&&!ko(s,t,wo(this._markers))))}const h=ho(s,this._devices,null,this._virtualLights);for(const t of this._devices){for(const e of[!1,!0])n.set(g_(t.id,e),p_(s,t,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:e,activityRuntime:this._activityRt.get(t.id),sourceDetails:!1,lightDevices:this._devices,lightSources:h,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion}));if(this._isVacDev(t)){const e=this._vacSource(t,s),i=e?Yd(s?.states?.[e]?.attributes):null,n=this._vacRt.get(t.id);o.set(`vacuum:${t.id}`,{source:e,telemetry:i,mapId:i?this._vacMapId(t,i,s):null,runtime:n?{trail:n.trail,lastTs:n.lastTs,moving:n.moving,jump:n.jump}:null,server:this._vacSrvTrails[t.id]||null})}}const d=m_({sourceSequence:this._hassSequence,hass:s,devices:this._devices,presentations:n,positions:(p=this._model.length>0,u=this._devices,_=t=>this._livePos(t),p?new Map(u.map(t=>[t.id,_(t)])):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=e,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(t){return Bn(this.hass,t,this._haRegistry)}houseplanDiagnostics(){const t=function(t){const e=An(t);return{access:e.access,authoritative:e.authoritative,revision:e.revision,lastSuccess:e.lastSuccess,error:e.error}}(this.hass),e={active:0,ha_disabled:0,orphaned:0,unverified:0};for(const t of this._markers)t.removed||"virtual"===t.binding||e[this._bindingStatus(t.binding).kind]++;return{registry:{...t,lastSuccessAgeMs:t.lastSuccess?Math.max(0,Date.now()-t.lastSuccess):null},bindings:e}}_openBindingInHa(t){const[e,i]=t.split(":");if(i)if("device"!==e){if("entity"===e){const t=this._fullRegistryHass.entities?.[i];t?.device_id&&Ff("/config/devices/device/"+encodeURIComponent(t.device_id))}}else Ff("/config/devices/device/"+encodeURIComponent(i))}_bindingHasHaPage(t){const[e,i]=t.split(":");return!!i&&("device"===e||"entity"===e&&!!this._fullRegistryHass.entities?.[i]?.device_id)}_toggleMarkerDialogVisibility(){const t=this._markerDialog;if(!t)return;const e="ha"===t.bindingMode?this._bindingStatus(t.binding):null,i=t.hideFromPlan||"ha_disabled"===e?.kind;i&&"ha_disabled"===e?.kind?this._showToast(this._t("entity"===e.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device")):i&&"unverified"===e?.kind?this._showToast(this._t("toast.ha_binding_unverified")):this._markerDialog={...t,hideFromPlan:!i}}_hookConnection(){const t=this.hass?.connection;t&&t!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),t.addEventListener?.("ready",this._onConnReady),t.addEventListener?.("disconnected",this._onConnLost),t.addEventListener?.("reconnect-error",this._onConnLost),this._connHooked=t)}_display(t){return this._signer.display(this.hass,t)}_resign(){this._signer.resign(this.hass,Is(this._serverCfg))}_onLayoutEvent(t){t<=this._layoutRev||(clearTimeout(this._layoutSyncTimer),this._layoutSyncTimer=window.setTimeout(()=>{t<=this._layoutRev||this._reloadLayoutOnly()},200))}_noteLayoutRev(t){const e=t?.rev;"number"==typeof e&&e>this._layoutRev&&(this._layoutRev=e)}async _reloadLayoutOnly(){if(!this._serverStorage||!this.hass?.callWS)return;this._beginContinuityCandidate("layout-reload",!1);const t=new Map;for(const e of this._dirtyPos)this._layout[e]&&t.set(e,this._layout[e]);this._persistLayout.pending()&&this._persistLayout.flush();for(const[e,i]of this._sentPos)t.set(e,i);try{const e=await this.hass.callWS({type:"houseplan/layout/get"}),i={...e?.layout||{}};for(const[e,s]of t)i[e]=s;const s=Id(i);s!==(this._layoutContentFingerprint||Id(this._layout))&&(this._layout=i,this._layoutContentFingerprint=s),this._layoutRev=e?.rev??this._layoutRev,this._canOptimizeUndo=!!e?.can_optimize_undo,this._undoKind=e?.undo_kind||null,this._cacheSnapshot(),this.requestUpdate()}catch{}finally{this._continuityDataReady=!0,this.requestUpdate()}}_maybeRebuildDevices(){const t=this.hass;if(!t?.devices||!t?.entities||!t?.areas)return;const e=this._haRegistry,i=Fn(t,e)+":"+Object.keys(t.areas).length+":"+(this._norm?"n":"l")+":"+ra(t,this._config?.language);if(i===this._regSignature&&this._devices.length)return;this._regSignature=i;const s=new Map(this._devices.map(t=>[t.id,t.bindingStatus?.kind||"active"]));this._devices=So({hass:t,registry:e,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||"",loc:t=>this._t(t),iconRules:this._iconRules});const n=this._markers.filter(t=>!t.removed&&"virtual"!==t.binding).map(t=>t.binding).sort(),o=e.revision+":"+n.join("|");if(e.authoritative&&o!==this._haBindingCacheKey){const i=new Map;for(const s of n)i.set(s,Bn(t,s,e));!function(t){if("undefined"==typeof localStorage||!t.size)return;const e=Ln(),i=Date.now();for(const[s,n]of t)"active"===n.kind?e[s]={kind:"active",ts:i}:"ha_disabled"===n.kind&&(e[s]={kind:"ha_disabled",reason:n.reason,ts:i});const s=Object.fromEntries(Object.entries(e).sort((t,e)=>e[1].ts-t[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(t=>!t.hidden).map(t=>t.id));for(const t of this._vacRt.keys())a.has(t)||this._vacRt.delete(t);if(this._infoCard){const t=this._devices.find(t=>t.id===this._infoCard.id);this._infoCard=t&&"ha_disabled"!==t.bindingStatus?.kind?t:null}const r=this._devices.some(t=>"ha_disabled"===t.bindingStatus?.kind&&"ha_disabled"!==s.get(t.id));r&&("ha_disabled"!==this._infoCard?.bindingStatus?.kind&&"ha_disabled"!==this._devices.find(t=>t.id===this._infoCard?.id)?.bindingStatus?.kind||this._closeInfoCard(),this._drag&&"ha_disabled"===this._devices.find(t=>t.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)&&(zf(this,"hass-more-info",{entityId:null}),this._nativeMoreInfoEntity=null)}_syncNewDevices(){if(!this._norm||!this._loadOk||!this._serverCfg)return;const t=this._devices.filter(t=>!t.marker&&!t.virtual).map(t=>t.id).sort(),e=t.join(",");if(e===this._newSyncKey)return;this._newSyncKey=e;const i=this._settings,{fresh:s,known:n}=function(t,e){if(!Array.isArray(e))return{fresh:[],known:[...t]};const i=new Set(e),s=t.filter(t=>!i.has(t));return{fresh:s,known:s.length?[...e,...s]:e}}(t,i.known_devices);if(!Array.isArray(i.known_devices)||s.length){const t=[...new Set([...i.new_device_ids||[],...s])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:n,new_device_ids:t}},this._saveConfig()}}get _newIds(){const t=this._settings.new_device_ids;return new Set(Array.isArray(t)?t:[])}_ackNewDevice(t){if(!this._newIds.has(t)||!this._serverCfg)return;const e=this._settings;this._serverCfg={...this._serverCfg,settings:{...e,new_device_ids:(e.new_device_ids||[]).filter(e=>e!==t)}},this._saveConfig(),this.requestUpdate()}get _markers(){return this._serverCfg?.markers||[]}_roomLqi(t){if(!t)return null;const e=[];for(const i of this._renderDevices){if(i.area!==t||i.virtual)continue;const s=_o(this._renderPlanHass,i.entities);null!=s&&e.push(s)}return Ei(e)}_roomBounds(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),n=Math.min(...i);return{x:s,y:n,w:Math.max(...e)-s,h:Math.max(...i)-n}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}_defaultPositions(){const t={},e=this._config?.icon_size??2.5;for(const i of this._model){const s=e/100*jh(i)*1.3;for(const e of i.rooms){if(!e.area)continue;const n=this._devices.filter(t=>t.area===e.area&&t.space===i.id);if(!n.length)continue;const o=this._roomBounds(e),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((t,e)=>({x:o.x+a+d*(e%c+.5),y:o.y+a+p*(Math.floor(e/c)+.5)}));Oi(u,o,s,.5*a),n.forEach((e,i)=>t[e.id]=Th(u[i]))}}return t}_pos(t){const e=this._renderDeviceSnapshot?.positions.get(t.id);return e?{x:e.x,y:e.y}:this._livePos(t)}_livePos(t){const e=this._spaceModelById(t.space),i=this._layout[t.id];if(i)if(this._norm){if(i.s===t.space)return{x:i.x*Pf,y:i.y*Pf}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[t.id]?this._defPos[t.id]:e?Th(Wh(e)):{x:500,y:500}}_savePos(t,e,i){if(this._spaceModelById(t.space)){if(this._norm){const s=this._gridPitch,n=Math.round(e/s)*s,o=Math.round(i/s)*s,a=this._layout[t.id]?.k;this._layout={...this._layout,[t.id]:{s:t.space,x:Ih(n/Pf),y:Ih(o/Pf),...a?{k:a}:{}}}}else this._layout={...this._layout,[t.id]:{x:Math.round(e),y:Math.round(i)}};this._dirtyPos.add(t.id),this._persistLayout()}}_visualSamples(t){return r_(this._renderPlanHass,t,this._renderDevices,void 0,this._fullRegistryHass).samples}_devicePresentation(t,e=!1!==this._config?.show_signal,i=!1){const s=i?null:this._renderDeviceSnapshot?.presentations.get(g_(t.id,e));return s||p_(this._renderPlanHass,t,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:e,designPreview:i,activityRuntime:this._activityRt.get(t.id),sourceDetails:!1,lightDevices:this._renderDevices,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion})}_deviceVisual(t){return this._devicePresentation(t).visual}_stateClass(t,e=this._deviceVisual(t)){const i=this._devicePresentation(t);return i.effectiveHidden?"":("icon_ripple"!==i.display||!1===this._config?.live_states||"alarm"===e.status||e.activity,d_({...i,visual:e}).join(" "))}_liveTemp(t){if(!this._config?.show_temperature)return null;if(!0===t.marker?.use_climate_temp){const e=go(this._renderPlanHass,t.entities);if(null!=e)return e}return"mdi:thermometer"!==t.icon&&"mdi:air-filter"!==t.icon?null:mo(this._renderPlanHass,t.entities)}_bindingEntities(t){if("virtual"===t||!t)return[];const e=this._bindingStatus(t);return"active"===e.kind?e.enabledEntityIds:e.allEntityIds}_bindingHasClimate(t){return this._bindingEntities(t).some(t=>t.startsWith("climate."))}_bindingHasAlarm(t){return this._bindingEntities(t).some(t=>{const e=this._planHass?.states?.[t]||this.hass?.states?.[t],i=this._fullRegistryHass.entities[t]||this.hass?.entities?.[t];return Bs(t.split(".")[0],e?.attributes?.device_class||i?.device_class||i?.original_device_class)})}_liveHum(t){return this._config?.show_temperature&&t.primary&&fo(this.hass,t.primary)?vo(this.hass,t.entities):null}_deviceBindingActive(t,e=!0){if(t.virtual||"virtual"===t.bindingKind)return!0;if(!t.bindingKind||!t.bindingRef)return!1;const i=this._bindingStatus(`${t.bindingKind}:${t.bindingRef}`);return"active"===i.kind||(e&&this._showToast(this._t("ha_disabled"===i.kind?"toast.ha_disabled_action":"toast.ha_binding_unverified")),!1)}_openMoreInfo(t){t?this._planEntityAvailable(t)?(this._nativeMoreInfoEntity=t,zf(this,"hass-more-info",{entityId:t})):this._showToast(this._t("toast.ha_disabled_action")):this._showToast(this._t("toast.no_entity"))}_interruptViewGesture(t,e){if(clearTimeout(this._holdTimer),clearTimeout(this._kioskHoldTimer),void 0!==t)for(const i of[e,this._stageEl])try{i?.hasPointerCapture?.(t)&&i.releasePointerCapture(t)}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(t,e){"view"===this._mode&&(t.preventDefault(),t.stopPropagation(),this._deviceBindingActive(e)&&(e.primary?this._openMoreInfo(e.primary):this._infoCard=e))}_clickDevice(t,e){if(t.stopPropagation(),this._drag?.moved||this._suppressClick||this._holdFired)return;if("plan"===this._mode)return;if("devices"===this._mode)return void this._openMarkerDialog(e);const i=this._devices.find(t=>t.id===e.id);if(!i)return;const s=$p(i.tapAction,i.primary?.split(".")[0]),n=(t,e)=>{i.marker?.tap_confirm?this._tapConfirm={kind:"run",text:t,exec:e}:e()};if("toggle"===s){const t=this._toggleIntent(i);if(!t)return;if(!Tp(t))return void this._showUnavailableToggleTargets(t);const e=t=>{const e=Tp(t);if(!e)return;if("virtual-light"===e.kind)return this._startDevicePressFeedback(i.id),void this.hass.callWS({type:"houseplan/virtual_light/toggle",marker_id:e.markerId}).then(t=>{const e=wn(this._virtualLights,t);e!==this._virtualLights&&(this._virtualLights=e,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}).catch(t=>this._showToast(this._t("toast.virtual_light_toggle_failed",{err:this._errText(t)})));const{command:s}=e;this._startDevicePressFeedback(i.id),this.hass.callService(s.domain,s.service,s.data).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))},s=((o=t).targets.length?o.targets:o.skippedTargets).map(t=>t.name||t.entityId||t.ref).join(", ")||i.name;if(i.marker?.tap_confirm){const n=this._toggleConfirmationLines(t);if(!n.length)return;this._tapConfirm={kind:"toggle",text:this._t("confirm.tap_toggle",{name:s}),lines:n,initialIntent:t,deviceId:i.id,exec:()=>{const s=this._devices.find(t=>t.id===i.id),n=s?this._toggleIntent(s):null;n&&!Tp(n)&&this._showUnavailableToggleTargets(n)||(n&&Ip(t,n)?e(n):this._showToast(this._t("toast.tap_target_changed")))}}}else e(t);return}var o;if("info"===s)return this._interruptViewGesture(),void(this._infoCard=i);if(this._deviceBindingActive(i)){if("run"===s){const t=i.marker?.tap_target||"",e=function(t){const e=String(t||"").split(".")[0];return"automation"===e?{domain:"automation",service:"trigger"}:"script"===e?{domain:"script",service:"turn_on"}:"scene"===e?{domain:"scene",service:"turn_on"}:null}(t),s=this.hass.states[t];if(!e||!s)return void this._showToast(this._t("toast.run_target_missing"));const o=s.attributes?.friendly_name||t;return void n(this._t("confirm.tap_run",{name:o}),()=>{this._deviceBindingActive(i)&&this._planEntityAvailable(t)&&(this._startDevicePressFeedback(i.id),this.hass.callService(e.domain,e.service,{entity_id:t}).then(()=>{this._stampActivity(i.id,"event",this._activitySourceKey(i)),this.requestUpdate(),this._showToast(this._t("toast.run_started",{name:o}))}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)}))))})}"more-info"===s&&i.primary?this._openMoreInfo(i.primary):this._infoCard=i}}_showUnavailableToggleTargets(t){const e=function(t){if(!t||"group"!==t.kind||Tp(t)||"configured-targets-missing"!==t.noneReason)return[];if(!t.skippedTargets.length||t.skippedTargets.some(t=>!Rp.has(t.reason)&&"secure"!==t.reason))return[];const e=[],i=new Set;for(const s of t.skippedTargets){if(!Rp.has(s.reason))continue;const t=String(s.name||s.entityId||s.ref||"").trim();t&&!i.has(t)&&(i.add(t),e.push(t))}return e}(t);return!!e.length&&(this._showToast(1===e.length?this._t("toast.toggle_target_unavailable",{name:e[0]}):this._t("toast.toggle_targets_unavailable",{names:e.join(", ")})),!0)}_keyDevice(t,e){"Enter"!==t.key&&" "!==t.key||"view"!==this._mode&&"devices"!==this._mode||(t.preventDefault(),this._clickDevice(t,e))}_startDevicePressFeedback(t){const e=[...this.renderRoot.querySelectorAll(".dev[data-id]")].find(e=>e.dataset.id===t),i=e?.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(t)?.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(t,a);const r=()=>{this._devicePressAnimations.get(t)===a&&this._devicePressAnimations.delete(t)};a.addEventListener("finish",r,{once:!0}),a.addEventListener("cancel",r,{once:!0})}_cancelDevicePressFeedback(){for(const t of this._devicePressAnimations.values())t.cancel();this._devicePressAnimations.clear()}_pruneDevicePressFeedback(){if(!this._devicePressAnimations.size)return;const t=new Set([...this.renderRoot.querySelectorAll(".dev[data-id]")].map(t=>t.dataset.id||""));for(const[e,i]of this._devicePressAnimations)t.has(e)||(i.cancel(),this._devicePressAnimations.delete(e))}_t(t,e){return la(ra(this.hass,this._config?.language),t,e)}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(t){const e=`${t}.aria`,i=ra(this.hass,this._config?.language);return ca(i,t)&&ca(i,e)?W`<hp-help data-help-key=${t}
|
||
.text=${la(i,t)} .ariaLabel=${la(i,e)}></hp-help>`:U}get _stageEl(){return this.renderRoot.querySelector(".stage")}_contentItems(t){const e=[];for(const i of this._devices){if(i.space!==t.id||i.hidden)continue;const s=this._pos(i);e.push({minX:s.x,minY:s.y,maxX:s.x,maxY:s.y})}if(t.id===this._space){for(const t of this._openingsR){const i=Number(t.angle)*Math.PI/180,s=Math.cos(i)*t.rlen/2,n=Math.sin(i)*t.rlen/2,o=Fh([[t.rx-s,t.ry-n],[t.rx+s,t.ry+n]]);o&&e.push(o)}const i=this._decorH;for(const t of this._decorList){const s=Fh("line"===t.kind?[[t.x1*Pf,t.y1*i],[t.x2*Pf,t.y2*i]]:"text"===t.kind?[[t.x*Pf,t.y*i]]:Hr({x:t.x*Pf,y:t.y*i,w:t.w*Pf,h:t.h*i,angle:t.angle}));s&&e.push(s)}for(const i of this._physicalBodiesR(t)){const t=Fh(i);t&&e.push(t)}}return Nh(t,e)}_frameOf(){const t=this._spaceModel();if(!t){this._frame=null;const t={x:0,y:0,w:Pf,h:Pf};return{rect:t,all:t,outliers:0}}const e=this._frame,i="view"!==this._mode;if(e&&e.id===t.id&&this._bdDrag)return e;if(e&&e.id===t.id&&e.model===t&&e.layout===this._layout&&e.devs===this._devices&&e.far===this._showFar&&e.grow===i)return e;const s=Bh(this._contentItems(t));let n=s.all||qh(t),o=this._showFar?n:s.core||qh(t);return e&&e.id===t.id&&i&&e.grow&&(o=Rf(e.rect,o),n=Rf(e.all,n)),this._frame={id:t.id,model:t,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:n,outliers:s.outliers},this._frame}_isoSource(){const t=this._spaceModel();if(!t)return null;const e=this._spaceWalls,i=this._openPairs().flatMap(t=>t.segs),s=this._openingsR.map((t,e)=>({id:String(t.id||e),sourceIndex:e,type:t.type,x:t.rx,y:t.ry,angle:Number(t.angle)||0,length:t.rlen>0?t.rlen:900,flipH:!!t.flip_h,flipV:!!t.flip_v})),n=_h(64,this._cellCm),o=_h(10,this._cellCm),a=`${t.id}|${function(t){let e=2166136261;const i=t=>{if("number"!=typeof t)if("string"!=typeof t)if("boolean"!=typeof t)if(Array.isArray(t)){e=Ag(e,t.length);for(const e of t)i(e)}else{if(t&&"object"==typeof t){const e=t;for(const t of Object.keys(e).sort())i(t),i(e[t]);return}e=Ag(e,-1)}else e=Ag(e,t?1:0);else for(const i of t)e=Ag(e,i.codePointAt(0)||0);else e=Ag(e,t)};return i(t),e.toString(36)}({rooms:t.rooms,walls:e,openCuts:i,openings:s,partitions:t.partitions,roomDrafts:t.room_drafts,columns:t.wall_columns,cellCm:this._cellCm,gridPitch:this._gridPitch,wallKeyPitch:this._wallKeyPitch,camera:wg,wallHeight:n,floorEdgeHeight:o,algorithm:3})}`;return{key:a,build:()=>{const o=th(t,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(t)).all,a=this._roomWallOpeningInputs(this._openingsR,t),r=e.length||o.length?Rc(t.rooms,e,i,a,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf,o):null;if(r&&("failed-core"===r.status||"not-applicable"===r.status))throw new Error("wall boolean geometry failed");const l=r?.paperGeom??Mc(t.rooms,e,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf);if(!l)throw new Error("floor boolean geometry failed");const c=r?.openingIndex||Fc(t.rooms,e,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf),h=s.map(t=>{const i=this._openingsR[t.sourceIndex],s="gate"===t.type?!t.flipV:t.flipV,o=i?.partitionHost?Lp(i.partitionHost,s):e.length||"gate"===t.type?Hc(c,{x:t.x,y:t.y,angle:t.angle,length:t.length,flip_v:s}):{ox:0,oy:0,cm:0,side:-1};return function(t,e=64){if(!(zg([t.x,t.y,t.angle,t.length,t.face.ox,t.face.oy,e])&&t.length>0&&e>0))throw new Error("invalid isometric opening input");const i=t.length/2;let s;if("passage"===t.type)s=[];else if("gate"===t.type){const n=10*t.face.side;s=[Eg(t,0,[-i,0],[i,0],n,0,.88*e),Eg(t,1,[i,0],[-i,0],-n,0,.88*e)]}else s="window"===t.type?[Eg(t,0,[-i,0],[i,0],-90,.27*e,.78*e),Eg(t,1,[i,0],[-i,0],90,.27*e,.78*e)]:[Eg(t,0,[-i,0],[t.length,0],-90,0,.92*e)];return{id:t.id,sourceIndex:t.sourceIndex,type:t.type,leaves:Object.freeze(s.map(t=>Object.freeze(t)))}}({...t,face:o},n)});return{walls:r?.components.flatMap(t=>t.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 t=this._isoSource();if(!t)return null;const e=this._isoGeometryCache.get(t.key);if(e)return{key:t.key,...e};const i=this._frameOf().rect,s=t.build(),n=_h(64,this._cellCm),o=_h(10,this._cellCm),a=function(t){let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t)for(const t of o.leaves){const o=Math.hypot(t.closedVector[0],t.closedVector[1]);e=Math.min(e,t.hinge[0]-o),i=Math.min(i,t.hinge[1]-o),s=Math.max(s,t.hinge[0]+o),n=Math.max(n,t.hinge[1]+o)}return zg([e,i,s,n])?{x:e,y:i,w:s-e,h:n-i}:null}(s.openings),r=Mg({rect:a?Rf(i,a):i,wallHeight:n,openingHeight:n,floorDepth:o}),l=function(t,e=wg,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<(t||[]).length;r++){const l=t[r];for(let t=0;t<(l||[]).length;t++){const c=Pg(l[t],t>0);if(!(c.length<3||Math.abs(Dg(c))<1e-9)){s.push(Tg(c,e,i)),a+=c.length;for(let s=0;s<c.length;s++){const a=c[s],l=c[(s+1)%c.length],h=xg(a,0,e),d=xg(l,0,e);if(o.push(`M ${Cg(h)} L ${Cg(d)}`),Rg(a,l,e)<=1e-9)continue;const p=xg(l,i,e),u=xg(a,i,e);n.push({d:`M ${Cg(h)} L ${Cg(d)} L ${Cg(p)} L ${Cg(u)} Z`,depth:Math.max(h[1],d[1]),polygon:r,ring:t,edge:s})}}}}return n.sort((t,e)=>t.depth-e.depth||t.polygon-e.polygon||t.ring-e.ring||t.edge-e.edge),{topPath:s.join(" "),sides:n,contactPath:o.join(" "),edgeCount:a}}(s.walls,wg,n),c={geometry:l,floor:Ig(s.floor,o),openings:s.openings,frame:r};return ff(this._isoGeometryCache,t.key,c,8),{key:t.key,...c}}_effectiveProjection(){if("iso"!==this._desiredProjection||!this._model.length)return"flat";const t=this._isoSceneKey()||`${this._space}|invalid`;if(this._isoFallback.has(t))return"flat";try{const t=this._isoScene();return t?(e="iso",i=t.key,s=this._isoFallback,"iso"===e&&i&&!s.has(i)?"iso":"flat"):"flat"}catch(e){if(!this._isoFallback.has(t)){this._isoFallback.add(t);const i=t.split("|");console.warn(`HOUSEPLAN ISO FALLBACK: #89, space ${this._space}, fingerprint ${i[i.length-1]}, ${e instanceof Error?e.message:"renderer error"}`)}return"flat"}var e,i,s}_scenePoint(t){return"iso"===this._renderProjection?xg(t,0):t}_floorView(t){if("iso"!==this._renderProjection)return t;const e=$g([t.x,t.y]),i=$g([t.x+t.w,t.y+t.h]);return{x:e[0],y:e[1],w:i[0]-e[0],h:i[1]-e[1]}}_baseVb(){if("iso"===this._effectiveProjection()){if(!this._spaceDisplayForRender().showBorders){const t=Mg({rect:this._frameOf().rect,wallHeight:_h(64,this._cellCm)});return[t.x,t.y,t.w,t.h]}const t=this._isoScene()?.frame??this._frameOf().rect;return[t.x,t.y,t.w,t.h]}const t=this._frameOf().rect;return[t.x,t.y,t.w,t.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?U:W`<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 U;const t=this._view;if(!t||!t.w||!t.h)return U;const e=this._baseVb(),i=e[0],s=e[1],n=e[2],o=e[3];if(!(i+n<=t.x||i>=t.x+t.w||s+o<=t.y||s>=t.y+t.h))return U;const a=Math.atan2(s+o/2-(t.y+t.h/2),i+n/2-(t.x+t.w/2)),r=50+38*Math.cos(a),l=50+38*Math.sin(a);return W`<button class="homearrow" title=${this._t("canvas.home_tip")}
|
||
style="left:${r.toFixed(1)}%;top:${l.toFixed(1)}%"
|
||
@click=${t=>{t.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 t=this._stageEl,e=this._baseVb();return t&&t.clientHeight?t.clientWidth/t.clientHeight:e[2]/e[3]}_viewOr(t){return this._view&&this._view.w?this._view:Ni(t,this._stageAspect())}_roomLabelReferenceViewWidth(t){if(!this._markup)return t.w;const e=this._stageEl,i=this.renderRoot.querySelector(".editorchrome");if(!e||e.clientWidth<=0||e.clientHeight<=0)return t.w;const s=e.clientHeight+(i?.getBoundingClientRect().height||0);return s<=0?t.w:this._viewForModeTarget(this._zoom,void 0,void 0,e.clientWidth,s).w}_screenToVb(t,e){const i=this._stageEl,s=this._viewOr(this._baseVb()),n=i?.clientWidth||1,o=i?.clientHeight||1;return[s.x+t/n*s.w,s.y+e/o*s.h]}_clampView(t,e){const i=(t,e,i,s)=>{const n=1*Math.max(e,s),o=i-n,a=i+s-e+n;return Math.max(Math.min(o,a),Math.min(Math.max(o,a),t))};return{w:t.w,h:t.h,x:i(t.x,t.w,e.x,e.w),y:i(t.y,t.h,e.y,e.h)}}_applyView(t,e,i){const s=this._baseVb(),n=Ni(s,this._stageAspect()),o=Math.min(Of.ZOOM_MAX,Math.max(Of.ZOOM_MIN,t)),a=n.w/o,r=n.h/o,l=this._viewOr(s),c=e??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 t=()=>{if(!this._booting)return;const e=Date.now(),i=this._stageEl?this._stageEl.clientHeight:0;i!==this._bootLastH&&(this._bootLastH=i,this._bootLastChange=e);const s=e-this._bootStart;s>=1200||s>=700&&i>0&&e-this._bootLastChange>=250?this._bootSettled():this._bootTimer=window.setTimeout(t,100)};this._bootTimer=window.setTimeout(t,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 t=this._stageEl?.clientHeight??0;!this._config?.kiosk&&t>0&&this._warmPatch({hdrH:this._hdrH,stageH:t,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},_f)}_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 t=this._stageEl;if(!t||"visible"!==document.visibilityState||t.clientWidth<=0||t.clientHeight<=0)return void(this._viewportInvalidAt||(this._viewportInvalidAt=Date.now()));const e=[t.clientWidth,t.clientHeight],i=this._lastValidStageSize,s=!!i&&Math.abs(i[0]-e[0])<=.5&&Math.abs(i[1]-e[1])<=.5,n=this._viewportInvalidAt?Date.now()-this._viewportInvalidAt:0;if(this._viewportInvalidAt=0,!i)return this._lastValidStageSize=e,void(this._view||this._applyView(this._zoom));s?this._pendingRefitSize=null:(this._pendingRefitSize=e,this._refitRaf||(this._refitRaf=requestAnimationFrame(()=>{this._refitRaf=0;const t=this._pendingRefitSize;this._pendingRefitSize=null;const e=this._stageEl;if(!t||!e||e.clientWidth<=0||e.clientHeight<=0)return;if(Math.abs(e.clientWidth-t[0])>.5||Math.abs(e.clientHeight-t[1])>.5)return void this._refitView();const i=this._lastValidStageSize;if(i&&Math.abs(i[0]-t[0])<=.5&&Math.abs(i[1]-t[1])<=.5)return;this._lastValidStageSize=t;const s=this._view;n>=Md?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(t,e,i){const s=this._stageEl;if(!s)return;const n=Ni(this._baseVb(),this._stageAspect()),o=Math.min(Of.ZOOM_MAX,Math.max(Of.ZOOM_MIN,i));"opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1));const a=s.clientWidth,r=s.clientHeight,l=this._screenToVb(t,e),c=n.w/o,h=n.h/o;this._zoom=o,this._view=this._clampView({x:l[0]-t/a*c,y:l[1]-e/r*h,w:c,h:h},n)}_onWheel(t){const e=this._stageEl;if(!e)return;t.preventDefault();const i=e.getBoundingClientRect(),s=t.deltaY<0?1.15:1/1.15;this._zoomAt(t.clientX-i.left,t.clientY-i.top,this._zoom*s),this._saveZoom()}_stepZoom(t){const e=this._stageEl;e&&(this._zoomAt(e.clientWidth/2,e.clientHeight/2,this._zoom*(t>0?1.4:1/1.4)),this._saveZoom())}_resetZoom(){const t=this._baseVb();"opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1)),this._zoom=1,this._view=Ni(t,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem($f,JSON.stringify(this._zoomBySpace))}catch{}}}_restoreZoom(){const t=this._zoomBySpace[this._space]||1;this._zoom=t;const e=this._stageEl;if(e&&e.clientHeight){const e=this._baseVb();return this._applyView(t,e[0]+e[2]/2,e[1]+e[3]/2),void this.requestUpdate()}this._view=null,requestAnimationFrame(()=>{if(!this._stageEl)return;const e=this._baseVb();this._applyView(t,e[0]+e[2]/2,e[1]+e[3]/2),this.requestUpdate()})}_stagePointerDown(t){if(this._vacFit)return;if(this._kiosk&&(this._cyclePausedUntil=Date.now()+6e4,0===this._pointers.size?(this._swipeStart={x:t.clientX,y:t.clientY,id:t.pointerId},t.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&&t.target.closest?.(".roomlabel, .rlhandle, .rszhandle, .dev, .oplock, .op-hit, button"))return;if("devices"===this._mode&&t.target.closest(".dev"))return;if("decor"===this._mode&&this._decorPointerDown(t))return;this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY});const e=this._viewOr(this._baseVb());if(1===this._pointers.size)this._panStart={sx:t.clientX,sy:t.clientY,vx:e.x,vy:e.y},this._panLock=null,this._suppressClick=!1;else if(2===this._pointers.size){this._cancelBoundaryAnchor(),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();const t=[...this._pointers.values()],e=Math.hypot(t[0].x-t[1].x,t[0].y-t[1].y);this._pinchStart={dist:e,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(t){if(this._physicalRotate?.pid!==t.pointerId)if(this._physicalDrag?.pid!==t.pointerId)if(this._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId){if(this._decorDraft?.pid===t.pointerId){const e=this._decorDraft;let i=this._decorSnap(this._svgPoint(t),t.pointerType);if(t.shiftKey&&("rect"===e.kind||"ellipse"===e.kind)){const t=i[0]-e.a[0],s=i[1]-e.a[1],n=Math.max(Math.abs(t),Math.abs(s));i=this._snap([e.a[0]+(t<0?-n:n),e.a[1]+(s<0?-n:n)])}return void(this._decorDraft={...e,b:i})}if(this._decorMove?.pid!==t.pointerId)if(this._pointers.has(t.pointerId)){if(this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY}),this._markup&&1===this._pointers.size&&this._markupMove(t),this._pinchStart&&this._pointers.size>=2){"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();const t=[...this._pointers.values()],e=Math.hypot(t[0].x-t[1].x,t[0].y-t[1].y)/(this._pinchStart.dist||1),i=this._stageEl.getBoundingClientRect(),s=(t[0].x+t[1].x)/2-i.left,n=(t[0].y+t[1].y)/2-i.top;this._zoomAt(s,n,this._pinchStart.zoom*e),this._suppressClick=!0,this._saveZoom()}else if(this._panStart){const e=t.clientX-this._panStart.sx,i=t.clientY-this._panStart.sy;Math.abs(e)+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(e)+Math.abs(i)>8&&(this._panLock=this._swipeZone&&Math.abs(e)>1.5*Math.abs(i)?"swipe":"pan");const s=this._stageEl;if("pan"===this._panLock&&s){const t=this._baseVb(),n=this._viewOr(t),o=Ni(t,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-e/(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(t);else this._decorMoveUpdate(t)}else this._bdMove(t);else this._dtMove(t);else this._physicalMove(t);else this._physicalRotateMove(t)}_stagePointerLeave(t){this._markup&&("opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover())}_stagePointerUp(t){if(this._kiosk){clearTimeout(this._kioskHoldTimer);const e=this._swipeStart;if(this._swipeStart=null,e&&e.id===t.pointerId){const i=t.clientX-e.x,s=t.clientY-e.y;if(Math.abs(i)+Math.abs(s)<8){const t=Date.now();t-this._lastTap<350&&this._resetZoom(),this._lastTap=t}const n="pan"===this._panLock?null:function(t,e,i,s,n,o=60){if(i>1.001||s.length<2)return null;if(Math.abs(t)<o||Math.abs(t)<1.5*Math.abs(e))return null;const a=s.indexOf(n);if(a<0)return null;const r=s.length;return t<0?s[(a+1)%r]:s[(a-1+r)%r]}(i,s,this._zoom,this._model.map(t=>t.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===t.pointerId)return void this._physicalUp(t);if(this._physicalRotate?.pid===t.pointerId)return void this._physicalRotateUp(t);if(this._dtDrag?.pid===t.pointerId)return void this._dtUp();if(this._bdDrag?.pid===t.pointerId)return void this._bdUp();if(this._decorDraft?.pid===t.pointerId)return void this._decorCommitDraft();if(this._decorMove?.pid===t.pointerId)return this._decorMove.moved&&(this._recordGeometry(this._t("history.decor_move"),this._decorMove.before),this._saveConfig()),void(this._decorMove=null);const e=!!this._pinchStart||!!this._panStart;this._pointers.delete(t.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,setTimeout(()=>this._suppressClick=!1,0)),e&&0===this._pointers.size&&this.requestUpdate()}_clickRoom(t){!this._suppressClick&&t.area&&Ff("/config/areas/area/"+t.area)}_pointerDown(t,e){if("plan"===this._mode)return;if("view"===this._mode){this._holdFired=!1,clearTimeout(this._holdTimer);const i=t.pointerId,s=t.currentTarget;return void(this._holdTimer=window.setTimeout(()=>{this._holdFired=!0,this._interruptViewGesture(i,s),this._infoCard=e},600))}if("ha_disabled"===e.bindingStatus?.kind)return;t.preventDefault();const i=this._pos(e);this._drag={id:e.id,sx:t.clientX,sy:t.clientY,ox:i.x,oy:i.y,moved:!1},Nf(t),this._tip=null}_pointerMove(t,e){if(!this._drag||this._drag.id!==e.id)return;const i=this.renderRoot.querySelector(".stage");if(!i)return;const s=this._baseVb(),n=i.getBoundingClientRect(),o=this._viewOr(s),a=(t.clientX-this._drag.sx)/n.width*o.w,r=(t.clientY-this._drag.sy)/n.height*o.h;Math.abs(t.clientX-this._drag.sx)+Math.abs(t.clientY-this._drag.sy)>3&&(this._drag.moved=!0,clearTimeout(this._holdTimer));const l=Rh(this._drag.ox+a),c=Rh(this._drag.oy+r);this._savePos(e,l,c)}_pointerUp(t,e){if(clearTimeout(this._holdTimer),!this._drag||this._drag.id!==e.id)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&(this._selId=e.id,window.setTimeout(()=>this._drag=null,0))}_showToast(t){for(const t of this.renderRoot.querySelectorAll("hp-dialog"))t.closeTransientOverlays("toast");this._toast=t,clearTimeout(this._toastTimer),this._toastTimer=window.setTimeout(()=>{this._toast=""},3500)}_syncPointerHoverTargets(){const t=this._pointerModality.hoverEnabled;for(const e of this.renderRoot.querySelectorAll(Df))e.toggleAttribute("data-pointer-hover",t)}_syncPointerHoverSubtree(t){if(t.nodeType!==Node.ELEMENT_NODE)return;const e=t,i=this._pointerModality.hoverEnabled;e.matches(Df)&&e.toggleAttribute("data-pointer-hover",i);for(const t of e.querySelectorAll(Df))t.toggleAttribute("data-pointer-hover",i)}_clearTransientHover(t=!1){t&&this._pointerModality.suspend(),this._tip&&(this._tip=null),this._hoverRoom&&(this._hoverRoom=null)}_notePointer(t){this._boundaryPointerType=t.pointerType||"mouse";const e=this._pointerModality.modality,i=this._pointerModality.note(t);"touch"!==i&&"pen"!==i||e===i&&!this._tip&&!this._hoverRoom||this._clearTransientHover()}_guardTouchGesture(t){if(this._editorSecondary.handleOutsideDismiss(t))return;if("click"===t.type){if(!this._suppressClick&&!this._touchSequenceMultitouch&&Date.now()>this._touchClickBlockUntil)return;return t.preventDefault(),void t.stopImmediatePropagation()}const e=t;if(this._notePointer(e),"touch"===e.pointerType)if("pointerdown"!==t.type){if("pointermove"===t.type){const t=this._touchContacts.get(e.pointerId);if(!t)return;const i={...t,x:e.clientX,y:e.clientY};if(this._touchContacts.set(e.pointerId,i),this._touchSequenceMultitouch&&"view"===this._mode&&!this._vacFit&&this._pinchStart&&[...this._touchContacts.values()].every(t=>t.inStage)){this._pointers.set(e.pointerId,{x:e.clientX,y:e.clientY});const t=[...this._touchContacts.values()];if(t.length>=2&&this._stageEl){const[e,i]=t,s=Math.hypot(e.x-i.x,e.y-i.y)/(this._pinchStart.dist||1),n=this._stageEl.getBoundingClientRect();this._zoomAt((e.x+i.x)/2-n.left,(e.y+i.y)/2-n.top,this._pinchStart.zoom*s),this._saveZoom()}}return}if("pointerup"===t.type||"pointercancel"===t.type||"lostpointercapture"===t.type){if(this._clearTransientHover(),this._touchContacts.delete(e.pointerId),this._touchSequenceMultitouch){if(this._touchClickBlockUntil=Date.now()+500,this._pointers.delete(e.pointerId),!this._vacFit&&this._pointers.size>=2){const[t,e]=[...this._pointers.values()];this._pinchStart={dist:Math.hypot(t.x-e.x,t.y-e.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(e.pointerId,{x:e.clientX,y:e.clientY,inStage:!!e.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 t=[...this._touchContacts.values()];if("view"===this._mode&&!this._vacFit&&t.every(t=>t.inStage)){this._pointers=new Map([...this._touchContacts].map(([t,e])=>[t,{x:e.x,y:e.y}]));const[e,i]=t;this._pinchStart={dist:Math.hypot(e.x-i.x,e.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(t,e,i,s,n,o){this._notePointer(t),this._pointerModality.hoverEnabled&&(this._drag||(this._tip={x:t.clientX,y:t.clientY,title:e,meta:i,lqi:s,temp:n,hum:o}))}get _gridPitch(){return Ch}get _cellCm(){const t=Number(this._curSpaceCfg?.cell_cm);return Number.isFinite(t)&&t>0?t:5}_fmtLen(t,e){const i=function(t,e,i,s){return Math.hypot(e[0]-t[0],e[1]-t[1])/i*s}(t,e,this._gridPitch,this._cellCm);return li(i,"mi"===this.hass?.config?.unit_system?.length)}get _curSpaceCfg(){const t=this._rszPreview;return t&&t.space===this._space?t.sp:this._serverCfg?.spaces.find(t=>t.id===this._space)}get _renderCfg(){const t=this._rszPreview;return t&&this._serverCfg?{...this._serverCfg,spaces:this._serverCfg.spaces.map(e=>e.id===t.space?t.sp:e)}:this._serverCfg}get _spaceH(){return this._curSpaceCfg,Pf}get _segments(){const t=this._curSpaceCfg,e=this._spaceH;return pi(t?.rooms||[]).map(t=>[t[0]*Pf,t[1]*e,t[2]*Pf,t[3]*e])}_savedNav(){if(this._hasFixedFloor)return null;try{return JSON.parse(localStorage.getItem(Sf)||"null")}catch{return null}}_saveNav(){if(!this._hasFixedFloor)try{localStorage.setItem(Sf,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._backupImportDialog=null,this._backupExportDialog=null,this._settingsDialog=null,this._markerDialog=null,this._openingDialog=null,this._physicalDialog=null,this._wallDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._mergeDialog=null,this._roomDialog=!1,this._spaceDialog=null,this._importDialog=null;const t=this._warmSlot;t?.owner===this._warmGen&&(t.vp=this._warmViewportState(),t.dlg=null,t.frameFingerprint="",clearTimeout(t.evict),t.evict=0),this._saveNav()}_setMode(t,e=!0){this._endTabDrag(),this._pendingNavMode=null;const i=this._spaceModel();if(!i)return this._cancelModeTransition(!1),void(this._mode="view");if(this._kiosk&&"view"!==t)return;if(this._mode===t){if(!e&&"view"===t&&this._modeTransitionBusy){const e=this._currentModeVisual(t),i=this._modeTransitionTargetZoom,s=this._modeTransitionTargetCenterX,n=this._modeTransitionTargetCenterY;this._cancelModeTransition(!1),this._commitViewModeAtomic(e,i,s,n)}return}if(this._bootSoftCancel(),("plan"===t||"decor"===t)&&!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"!==t?{...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"===t!=("view"===s),c=a&&"view"===s&&"view"!==t?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"!==t&&!a){const e=this._view;if(this._viewModeSnap={space:this._space,zoom:this._zoom,cx:e?this._logicalViewCenter(n)?.x:void 0,cy:e?this._logicalViewCenter(n)?.y:void 0},"iso"===n){const e=this._logicalViewCenter("iso");d=e?.x,p=e?.y,this._view=null,this._mode=t,this._applyView(this._zoom,e?.x,e?.y)}l&&(h=1,d=void 0,p=void 0)}if("plan"===s&&this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._mode=t,this._editorChromeMode="view"===t?s:t,"view"===t){"view"===s||a||(this._modeTransitionEditorCamera={zoom:this._zoom,centerX:r?.viewport.centerX,centerY:r?.viewport.centerY});const t=this._viewModeSnap;if(t&&t.space===this._space){h=t.zoom;const e=this._labsIso&&"iso"===this._viewPreference[this._space]?"iso":"flat",i=null!=t.cx&&null!=t.cy&&"iso"===e?xg([t.cx,t.cy],0):null;d=i?.[0]??t.cx,p=i?.[1]??t.cy}else t&&(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"===t&&this._labsIso&&"iso"===this._viewPreference[this._space])&&(this._modeTransitionForceAtomic=!0);const u=++this._modeTransitionRequest;e?r?(this._modeTransitionPreparing=!0,this._modeTransitionVisual=r,this._prepareModeTransition(u,r,t,h,d,p)):(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._zoom=h,this._view=null,requestAnimationFrame(()=>{this.isConnected&&u===this._modeTransitionRequest&&this._mode===t&&(this._applyView(h,d,p),"view"===t&&(this._viewModeSnap=null,this._modeTransitionEditorCamera=null),this.requestUpdate())})):"view"===t&&this._commitViewModeAtomic(r,h,d,p),this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._openWallAnchor=null,this._boundaryRestoreGuard=null,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._rszSel=null,this._rszDrag=null,this._rszLive=null,this._rszPreview=null,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"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this._saveNav()}_primeDrawWallField(){null===this._drawWallField&&(this._drawWallField=_l(ll,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?_l(ll,this._imperial):this._drawWallField}get _drawWallCm(){const t=mf(this._drawWallFieldValue);if(null==t||t<=0)return null;const e=this._imperial?2.54*t:t,i="column"===this._tool?Wc:100;return e>=1&&e<=i?e:null}get _drawWallMaxCm(){return"column"===this._tool?Wc:100}_showPhysicalRange(t=this._drawWallMaxCm){this._showToast(this._t("toast.physical_range",{max:_l(t,this._imperial),unit:this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_mergeSpacePartitions(t,e){const i=t?.partitions||[];if(i.length<2)return 0;const s=om(i,{pitch:Dh,seedIds:e,geometry:rm(t,{excludeDraftId:this._activeDraftId})});return s.merged?(t.partitions=s.partitions,am(t.openings,t.partitions,s.openingMoves,{coordScale:Pf,cellCm:this._cellCm,gridPitch:this._gridPitch}),s.merged):0}_finishWallChain(){if("draw"!==this._tool||this._wallFaceBatch||this._roomDialog)return!0;const t=this._curSpaceCfg;if(!t||this._path.length<2)return this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._clearPlanSnapHover(),!0;const e=cu(this._path,lu(this._path.length-1,this._draftSegmentCms,this._drawWallCm,ll)),i=e.length;if((t.partitions||[]).length+i>Af)return this._showToast(this._t("toast.physical_limit")),!1;const s=this._geometrySnapshot();t.partitions||=[];const n=Date.now().toString(36),o=[];for(let s=0;s<i;s++){const i=e[s],a=`partition-${n}-${s}`;o.push(a),t.partitions.push({id:a,a:[i.a[0]/Pf,i.a[1]/Pf],b:[i.b[0]/Pf,i.b[1]/Pf],cm:i.cm})}return this._mergeSpacePartitions(t,o),this._activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==this._activeDraftId),t.room_drafts.length||delete t.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(t){t!==this._tool&&(this._wallFaceBatch&&this._roomDialogCancel(),("draw"!==this._tool||this._finishWallChain())&&(this._cancelPath(),this._tool=t,"draw"===t&&this._resumeLastDraft(),"resize"===t&&(this._rszSel=null),"boundary"!==t&&"wallthick"!==t||(this._wallDialog=null)))}_limitReached(t){const e=this._curSpaceCfg;if(!e)return!0;const i="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=Af:(e.wall_columns||[]).length>=500;return i&&this._showToast(this._t("toast.physical_limit")),i}_svgPoint(t){const e=this.renderRoot.querySelector(".stage").getBoundingClientRect();return this._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this._gridPitch;return[Rh(ri(t[0],e)),Rh(ri(t[1],e))]}_snapDrawPoint(t,e=!1){const i=this._path[this._path.length-1],s=e&&i?function(t,e,i,s=1/0){if(!(i>0&&t?.every(Number.isFinite)&&e?.every(Number.isFinite)))return[e[0],e[1]];const n=e[0]-t[0],o=e[1]-t[1];if(Math.abs(n)+Math.abs(o)<=1e-12)return[t[0],t[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 e=(t,e)=>{if(!e)return 1/0;const n=e>0?s-t:t+s;return Math.max(0,Math.floor((n+1e-9*i)/i))};p=Math.min(p,e(t[0],c),e(t[1],h))}return[t[0]+c*p*i,t[1]+h*p*i]}(i,t,this._gridPitch,Sh):t;return this._snap(s)}_planSnapOpeningCuts(t,e){if(!this._openingsR.length)return[];const i=this._openingWallIndexFor(t,e).value,s=[];for(const e of this._roomWallOpeningInputs(this._openingsR,t)){const t=Oc(i,e);if(!t.negative&&!t.positive)continue;const n=e.angle*Math.PI/180,o=Math.cos(n)*e.length/2,a=Math.sin(n)*e.length/2;s.push([e.x-o,e.y-a,e.x+o,e.y+a])}return s}_planSnapGeometrySnapshot(){const t=this._spaceModel();if(!t)return{key:`${this._space}|empty`,value:{segments:[],endpoints:[]}};const e=[this._space,this._cfgEpoch,this._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this._planSnapGeometryCache?.key===e)return this._planSnapGeometryCache;const i=this._openCuts(),s=tu({space:t,activeDraftId:this._activeDraftId,roomCuts:[...i,...this._planSnapOpeningCuts(t,i)],partitionCuts:this._partitionOpeningCuts(t),epsilon:2e-4*this._gridPitch});return this._planSnapGeometryCache={key:e,value:s},this._planSnapGeometryCache}_hiddenWallDiagnosticSnapshot(){const t=this._spaceModel();if(!t)return{key:`${this._space}|hidden-empty`,value:{segments:[],endpoints:[]}};const e=["hidden",this._space,this._cfgEpoch,this._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this._hiddenWallDiagnosticCache?.key===e)return this._hiddenWallDiagnosticCache;const i=Qp({space:t,activeDraftId:this._activeDraftId,epsilon:2e-4*this._gridPitch});return this._hiddenWallDiagnosticCache={key:e,value:i},this._hiddenWallDiagnosticCache}_planStructuralGeometrySnapshot(){const t=this._spaceModel();if(!t)return{key:`${this._space}|structural-empty`,value:{segments:[],endpoints:[]}};const e=["structural",this._space,this._cfgEpoch,this._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this._planStructuralGeometryCache?.key===e)return this._planStructuralGeometryCache;const i=tu({space:t,activeDraftId:this._activeDraftId,roomCuts:this._openCuts(),epsilon:2e-4*this._gridPitch});return this._planStructuralGeometryCache={key:e,value:i},this._planStructuralGeometryCache}_planSnapContextKey(t){const e=this._path[0],i=this._path[this._path.length-1];return[t,this._tool,this._path.length,e?`${e[0]},${e[1]}`:"",i?`${i[0]},${i[1]}`:""].join("|")}_resolvePlanDrawPoint(t,e){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=e&&s?au(i.value,t,{...o,anchor:s}):function(t,e,i){if(!(Gp(e)&&i.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const s=i.epsilon??jp,n=i.excludePoints||[],o=nu(su(t,e,i),i.distinguishTolerance||0);if(o)return o;let a=null;for(const o of t.segments){const t=[o.a[0],o.a[1],o.b[0],o.b[1]],r=Hs([e[0],e[1]],t);if(r>i.tolerance)continue;const l=ou(o,e,i.gridStep);!eu(l,n,s)&&iu(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,t,o),r="resolved"===a.kind?a.candidate:null,l=r?[...r.point]:this._snapDrawPoint(t,e),c=s?ua(s,l):l;return{point:c,candidate:r&&fi(c,r.point)?r:null,conflicts:"ambiguous"===a.kind?a.conflicts:[],ambiguous:"ambiguous"===a.kind,contextKey:this._planSnapContextKey(i.key)}}get _activePlanSnapCandidate(){if(!this._markup||"draw"!==this._tool)return null;const t=this._planSnapHover;if(!t)return null;const e=this._planSnapGeometrySnapshot();return t.contextKey===this._planSnapContextKey(e.key)?t.candidate:null}get _activePlanSnapConflicts(){if(!this._markup||"draw"!==this._tool)return[];const t=this._planSnapHover;if(!t)return[];const e=this._planSnapGeometrySnapshot();return t.contextKey===this._planSnapContextKey(e.key)?t.conflicts:[]}_clearPlanSnapHover(t=!0){this._planSnapHover=null,this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers([]),t&&(this._cursorPt=null)}_samePt(t,e){return fi(t,e)}_dropLegacySegments(){for(const t of this._serverCfg?.markers||[])"ripple"===t.display&&(t.display="icon_ripple");for(const t of this._serverCfg?.spaces||[]){delete t.segments;const e=new Set,i=t=>"string"==typeof t&&t.length>=1&&t.length<=64&&!e.has(t),s=t=>(e.add(t),!0),n=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=$h);if(Array.isArray(t.partitions)&&(t.partitions=t.partitions.filter(t=>t&&i(t.id)&&n(t.a)&&n(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&s(t.id)).map(t=>({...t,cm:Math.max(1,Math.min(100,Number(t.cm)||15))})),t.partitions.length||delete t.partitions),Array.isArray(t.wall_columns)&&(t.wall_columns=t.wall_columns.filter(t=>t&&i(t.id)&&n(t.center)&&s(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:Gc(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:Uc(t.angle)}})),t.wall_columns.length||delete t.wall_columns),Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t&&i(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(n)&&s(t.id)).map(t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],i=[];for(let s=1;s<t.points.length;s++){const n=[Number(t.points[s][0]),Number(t.points[s][1])];this._samePt(e[e.length-1],n)||(e.push(n),i.push({cm:Math.max(1,Math.min(100,Number(t.segments?.[s-1]?.cm)||15))}))}return{id:t.id,points:e,segments:i}}).filter(t=>t.points.length>=2),t.room_drafts.length||delete t.room_drafts),Array.isArray(t.walls)){const e=dd(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=Pl(t.walls,t.rooms||[],Dh,1,e),t.walls.length||delete t.walls}}}get _cfgWriting(){return this._writesPending>0}_writeConfig(){var t,e;this._writesPending++,this._writeChain=(t=this._writeChain,e=async()=>{if(!this._serverCfg)return;this._dropLegacySegments();const t=rl(Ur(this._serverCfg)),e=Id(t),i=[...this._pendingPhysicalWrites.entries()];for(const[e,s]of i){const i=zm(t.spaces.find(t=>t.id===e));if(i===s.fingerprint)continue;let n=!1;try{n=this._checkSpacePhysicalGeometry(t,e).ok}catch{n=!1}if(!n)throw this._restoreGeometryStateLocal(s.before),this._pendingPhysicalWrites.delete(e),this._geometryHistory.clear(),this._showToast(this._t("toast.geometry_unsafe")),Object.assign(new Error("unsafe wall geometry"),{code:"geometry-unsafe"});s.fingerprint=i}e!==Id(this._serverCfg)&&(this._serverCfg=t),this._cfgContentFingerprint=e;const s=await this.hass.callWS({type:"houseplan/config/set",config:t,expected_rev:this._cfgRev});this._cfgRev=s?.rev??this._cfgRev+1;for(const[t,e]of i)this._pendingPhysicalWrites.get(t)?.fingerprint===e.fingerprint&&this._pendingPhysicalWrites.delete(t)},t.catch(()=>{}).then(e));return this._writeChain.finally(()=>{this._writesPending--})}_saveConfig(){this._cfgEpoch++,this._saveConfigDebounced()}_geometrySnapshot(t=this._space){const e=this._serverCfg?.spaces.find(e=>e.id===t);if(!e)return null;const i=t=>JSON.parse(JSON.stringify(t)),s={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==e[t]&&(s[t]=e[t]);return{spaceId:t,rooms:i(e.rooms||[]),...Array.isArray(e.openings)?{openings:i(e.openings)}:{},...Array.isArray(e.walls)?{walls:i(e.walls)}:{},...Array.isArray(e.open_spans)?{open_spans:i(e.open_spans)}:{},...Array.isArray(e.room_drafts)?{room_drafts:i(e.room_drafts)}:{},...Array.isArray(e.partitions)?{partitions:i(e.partitions)}:{},...Array.isArray(e.wall_columns)?{wall_columns:i(e.wall_columns)}:{},...Array.isArray(e.decor)?{decor:i(e.decor)}:{},plan_transform:s}}_recordGeometry(t,e){if(!e)return;const i=this._geometrySnapshot(e.spaceId);i&&JSON.stringify(e)!==JSON.stringify(i)&&(this._geometryHistory.push({name:t,before:e,after:i}),this.requestUpdate())}_restoreGeometryStateLocal(t){const e=this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(!e)return!1;const i=t=>JSON.parse(JSON.stringify(t));e.rooms=i(t.rooms);const s=(t,s)=>{void 0!==s?e[t]=i(s):delete e[t]};s("openings",t.openings),s("walls",t.walls),s("open_spans",t.open_spans),s("room_drafts",t.room_drafts),s("partitions",t.partitions),s("wall_columns",t.wall_columns),s("decor",t.decor);for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];return Object.assign(e,i(t.plan_transform||{})),this._cfgEpoch++,this._modelCache=null,this._wallUnionCache=null,this._physicalBodiesCache=null,this._frame=null,this.requestUpdate(),!0}_commitPhysicalGeometry(t,e){if(!e||!this._serverCfg)return!1;const i=this._geometrySnapshot(e.spaceId);if(!i||zm(e)===zm(i))return!1;let s=!1;try{s=this._checkSpacePhysicalGeometry(this._serverCfg,e.spaceId).ok}catch{s=!1}if(!s)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this._showToast(this._t("toast.geometry_unsafe")),!1;this._recordGeometry(t,e);const n=this._serverCfg.spaces.find(t=>t.id===e.spaceId),o=this._pendingPhysicalWrites.get(e.spaceId);return this._pendingPhysicalWrites.set(e.spaceId,{before:o?.before||e,fingerprint:zm(n)}),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._openWallAnchor=null,this._boundaryRestoreGuard=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._rszSel=null,this._rszDrag=null,this._rszPreview=null,this._rszLive=null,this._decorDraft=null,this._decorMove=null,this._dtDrag=null,this._bdDrag=null}_cancelBoundaryAnchor(){return!("boundary"!==this._tool||!this._openWallAnchor)&&(this._openWallAnchor=null,this._boundaryRestoreGuard=null,this._clearPlanSnapHover(),this.requestUpdate(),!0)}_stagePointerCancel(t){if(clearTimeout(this._kioskHoldTimer),this._swipeStart?.id===t.pointerId&&(this._swipeStart=null),this._physicalDrag?.pid===t.pointerId||this._physicalRotate?.pid===t.pointerId)return void this._cancelPhysicalGesture();if(this._decorDraft?.pid===t.pointerId)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove?.pid===t.pointerId||this._dtDrag?.pid===t.pointerId||this._bdDrag?.pid===t.pointerId)return void this._cancelDecorGesture();this._cancelBoundaryAnchor(),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();const e=!!this._pinchStart||!!this._panStart;this._pointers.delete(t.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null),e&&0===this._pointers.size&&this.requestUpdate()}_applyGeometryState(t,e=!1){if(!this._canCommitSpace(t.spaceId))return!1;const i=this._geometrySnapshot(t.spaceId);if(!i||!this._restoreGeometryStateLocal(t))return!1;if(zm(i)!==zm(t)){let s=!1;try{const i=this._serverCfg?this._checkSpacePhysicalGeometry(this._serverCfg,t.spaceId):null;s=!!i?.ok||!(!e||"wall-degraded-extra"!==i?.reason)}catch{s=!1}if(!s)return this._restoreGeometryStateLocal(i),this._showToast(this._t("toast.geometry_unsafe")),!1;const n=this._serverCfg?.spaces.find(e=>e.id===t.spaceId),o=this._pendingPhysicalWrites.get(t.spaceId);this._pendingPhysicalWrites.set(t.spaceId,{before:o?.before||i,fingerprint:zm(n)})}return this._clearGeometryGesture(),this._space!==t.spaceId&&(this._commitSpace(t.spaceId),this._saveNav(),this._restoreZoom()),this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_roomAt(t){return this._spaceModel()?.rooms.find(e=>{const i=hi(e);return!!i&&wi(t,i)})}_overlapRoom(t){return this._spaceModel()?.rooms.find(e=>{const i=hi(e);return!!i&&Di(t,i)})}_pointInRoom(t,e){return e.poly?vi(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!Ia(t))return!0;const e=t.length,i=.001;for(let s=0;s<e;s++){const n=t[s],o=t[(s+1)%e];for(let a=s+1;a<e;a++){if(a===s+1||0===s&&a===e-1)continue;const r=t[a],l=t[(a+1)%e],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 t=this._curSpaceCfg;return!(!this._activeDraftId&&(t?.room_drafts||[]).length>=200||this._draftSegmentCount(t)>=2e3||(t?.partitions||[]).length+this._path.length>Af)||(this._showToast(this._t("toast.physical_limit")),!1)}_markupClick(t){if(this._vacFit)return;if(!this._markup)return;const e=this._spaceModel();if(!e)return void this._clearGeometryGesture();if(this._suppressClick)return;if(this._drag||this._rlResize)return;const i=t.composedPath?.()||[];if(i.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(i.some(t=>t?.classList?.contains?.("physical-hit")))return void("boundary"===this._tool&&this._showToast(this._t("toast.boundary_blocked")));const s=this._svgPoint(t);if("select"===this._tool)return void(this._physicalSel=null);if("resize"===this._tool){if(this._rszDrag||i.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...e.rooms].reverse().find(t=>this._pointInRoom(s,t));return void(this._rszSel=t?.id||null)}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("boundary"===this._tool)return void this._boundaryClick(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,t.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(t.ctrlKey||t.metaKey){if(t.preventDefault(),this._path.length<3)return;o=ua(this._path[this._path.length-1],this._path[0])}if(!this._path.length){if(!t.shiftKey&&!n.candidate&&this._offerExistingWallFace(s))return;const e=this._draftEndAt(o);return e?(this._activeDraftId=e.draft.id,this._resumeDraftBySpace[this._space]=e.draft.id,this._path=e.reverse?[...e.draft.points].reverse().map(t=>[...t]):e.draft.points.map(t=>[...t]),void(this._draftSegmentCms=this._adoptDraftCms(this._path,e.reverse?[...e.draft.segments].reverse().map(t=>t.cm):e.draft.segments.map(t=>t.cm),e.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(t=>[...t]);this._path=[...this._path,o],this._draftSegmentCms=[...this._draftSegmentCms,l],this._persistActiveDraftSegment(),this._offerWallFaces(c)}_draftEndAt(t,e){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===e)continue;if(s.points.length<2)continue;const i=s.points[0],o=s.points[s.points.length-1];if(Math.hypot(t[0]-o[0],t[1]-o[1])<=n)return{draft:s,reverse:!1};if(Math.hypot(t[0]-i[0],t[1]-i[1])<=n)return{draft:s,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this._curSpaceCfg;if(!e||!this._path.length)return;const i=this._path.map(t=>[...t]),s=this._wallGraphSources(i),n=Array.isArray(e.room_drafts)?e.room_drafts:[],o=this._activeDraftId?n.find(t=>t.id===this._activeDraftId):null,a=n.find(e=>e.id===t.draft.id);if(!a)return;const r=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),l=t.reverse?(a.segments||[]).map(t=>({...t})):[...a.segments||[]].reverse().map(t=>({...t})),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._draftSegmentCms.map((t,e)=>({...o?.segments?.[e]||{},cm:t})),u=[...this._path.map(t=>[...t]),...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(e)-v+f.length>2e3)return void this._showToast(this._t("toast.physical_limit"));if(m){const t=g.slice(0,-1);if(this._contourSelfIntersects(t)||Ti(t)<=1e-6)return void this._showToast(this._t("toast.contour_cannot_close"));const e=this._overlapRoom(t);if(e)return void this._showToast(this._t("toast.room_overlap",{name:e.name||""}))}const y=this._geometrySnapshot(),b=this._activeDraftId||t.draft.id,w={...o||a,id:b,points:g.map(t=>[t[0]/Pf,t[1]/Pf]),segments:f};e.room_drafts=n.filter(e=>e.id!==t.draft.id&&(!this._activeDraftId||e.id!==this._activeDraftId)),e.room_drafts.push(w),this._activeDraftId=b,this._resumeDraftBySpace[this._space]=b,this._path=g,this._draftSegmentCms=this._adoptDraftCms(g,f.map(t=>Number(t.cm)),b),this._physicalSel=null,this._commitPhysicalGeometry(this._t("history.draft_merge"),y),null!=d&&this._offerWallFaces(i,i.length-1,s)}_adoptDraftCms(t,e,i){const s=Math.max(0,t.length-1),n=lu(s,e,this._drawWallCm,ll);return e.length!==s&&console.debug(`[houseplan] draft ${i??"?"}: восстановлено толщин ${s-e.length} (#234)`),n}_persistActiveDraftSegment(){if(this._path.length<2||!this._curSpaceCfg)return;const t=this._geometrySnapshot(),e=this._curSpaceCfg;e.room_drafts||=[],this._activeDraftId||(this._activeDraftId="draft-"+Date.now().toString(36)),this._resumeDraftBySpace[this._space]=this._activeDraftId;const i=e.room_drafts.findIndex(t=>t.id===this._activeDraftId),s={...i>=0?e.room_drafts[i]:{},id:this._activeDraftId,points:this._path.map(t=>[t[0]/Pf,t[1]/Pf]),segments:this._draftSegmentCms.map((t,s)=>({...i>=0?e.room_drafts[i]?.segments?.[s]:{},cm:t}))};i>=0?e.room_drafts[i]=s:e.room_drafts.push(s),this._commitPhysicalGeometry(this._t("history.draft_segment"),t)}_activeWallSourceKey(t){return`active:${this._activeDraftId||"session"}:${t}`}_wallGraphSources(t){const e=this._planStructuralGeometrySnapshot().value.segments.map(t=>({a:t.a,b:t.b,key:`static:${t.key}`}));for(let i=0;i+1<t.length;i++)e.push({a:t[i],b:t[i+1],key:this._activeWallSourceKey(i)});return e}_wallFaceGraph(t,e){const i=`${this._space}|${e}|${JSON.stringify(t)}`,s=this._wallFaceGraphCache.findIndex(t=>t.key===i);if(s>=0){const[t]=this._wallFaceGraphCache.splice(s,1);return this._wallFaceGraphCache.push(t),t.value}const n=Ru(t,e);return this._wallFaceGraphCache.push({key:i,value:n}),this._wallFaceGraphCache.length>4&&this._wallFaceGraphCache.shift(),n}_offerWallFaces(t,e=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(e),o=2e-4*this._gridPitch;let a,r;try{const e=i||this._wallGraphSources(t);r=this._wallGraphSources(this._path),a=function(t,e,i){const s=new Set(t.faces.map(t=>t.key));return e.faces.filter(t=>!s.has(t.key)&&t.sourceKeys.includes(i))}(this._wallFaceGraph(e,o),this._wallFaceGraph(r,o),n)}catch{return}if(!a.length){const t=Wu(r,{requiredSourceKey:n,maxDistance:ml(2,this._cellCm,this._gridPitch),gridStep:this._gridPitch,epsilon:o});return"ambiguous"===t.kind?(this._wallRepairDiagnostic=t.proposals[0]||null,void this._showToast(this._t("toast.wall_repair_ambiguous"))):void("repair"!==t.kind||this._overlapRoom(t.face.ring)||this._beginWallFaceBatch([{...t.face,repair:t.proposal}]))}for(const t of s.rooms){const e=hi(t);if(!t.id||!e)continue;const i=zi(e,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((t,e)=>Math.abs(t.area-Ti(r))-Math.abs(e.area-Ti(r))||t.key.localeCompare(e.key))[0];if(!l)return;return void this._beginWallFaceBatch([{...l,ring:r.map(t=>[t[0],t[1]]),split:{roomId:t.id,mainPoly:o,newPoly:r},consumeAllActive:!0}])}const l=a.filter(t=>!(t.ring.length<3||this._contourSelfIntersects(t.ring))&&!this._overlapRoom(t.ring));l.length&&this._beginWallFaceBatch(l)}_beginWallFaceBatch(t){this._wallRepairDiagnostic=null,this._wallFaceBatch={candidates:[...t].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key)),index:0,decisions:[],activePath:this._path.map(t=>[...t]),activeCms:[...this._draftSegmentCms],activeDraftId:this._activeDraftId},this._clearPlanSnapHover(),this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),this._roomDialog=!0}_offerExistingWallFace(t){if(this._path.length||this._wallFaceBatch||this._roomDialog)return!1;const e=2e-4*this._gridPitch;try{let i,s=Iu(this._wallFaceGraph(this._wallGraphSources([]),e),t,e);if(!s){const n=Wu(this._wallGraphSources([]),{point:t,maxDistance:ml(2,this._cellCm,this._gridPitch),gridStep:this._gridPitch,epsilon:e});if("ambiguous"===n.kind)return this._wallRepairDiagnostic=n.proposals[0]||null,this._showToast(this._t("toast.wall_repair_ambiguous")),!0;if("repair"===n.kind)s=n.face,i=n.proposal;else if("none"===n.kind){const i=Wu(this._wallGraphSources([]),{point:t,maxDistance:this._cssPxToRender(12),gridStep:this._gridPitch,epsilon:e});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(t){const e=this._snap(t),i=this._drawWallCm;if(null==i)return void this._showPhysicalRange(Wc);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:e,cm:Gc(i),angle:0},o=(s.wall_columns||[]).find(t=>dh(t,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:[e[0]/Pf,e[1]/Pf]}),this._commitPhysicalGeometry(this._t("history.column_add"),a)}_openPhysicalDialog(t,e,i){const s=this._spaceModel();if(s)if("partition"===t){const i=s.partitions.find(t=>t.id===e);i&&(this._physicalDialog={kind:t,id:e,cm:_l(i.cm,this._imperial),length:this._fmtLen(i.a,i.b)})}else if("column"===t){const i=s.wall_columns.find(t=>t.id===e);i&&(this._physicalDialog={kind:t,id:e,cm:_l(i.cm,this._imperial),shape:i.shape,angle:this._angleField("square"===i.shape?Uc(i.angle):0)})}else{const n=s.room_drafts.find(t=>t.id===e),o=Math.max(0,Math.min(n?.segments.length?n.segments.length-1:0,i||0));n?.segments[o]&&(this._physicalDialog={kind:t,id:e,segment:o,cm:_l(n.segments[o].cm,this._imperial),length:this._fmtLen(n.points[o],n.points[o+1])})}}_physicalDown(t,e,i){const s=this._spaceModel();if(!s)return;t.stopPropagation(),Nf(t);const n=this._svgPoint(t),o=[];for(const t of[...s.wall_columns].reverse())ch(n,Zc(t,this._cellCm,this._gridPitch))&&o.push({kind:"column",id:t.id});for(const t of[...s.partitions].reverse()){const e=Xc(t.a,t.b,t.cm,this._cellCm,this._gridPitch);e&&ch(n,e)&&o.push({kind:"partition",id:t.id})}o.some(t=>t.kind===e&&t.id===i)||o.unshift({kind:e,id:i});const a=o.map(t=>`${t.kind}:${t.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(t.clientX-l.x,t.clientY-l.y)<=10?(l.index+1)%o.length:Math.max(0,o.findIndex(t=>t.kind===e&&t.id===i));this._physicalPickCycle={signature:a,index:c,x:t.clientX,y:t.clientY,at:r},e=o[c].kind,i=o[c].id;const h="partition"===e?s.partitions.find(t=>t.id===i):s.wall_columns.find(t=>t.id===i);h&&(this._physicalSel={kind:e,id:i},this._physicalDrag={pid:t.pointerId,kind:e,id:i,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(h)),delta:[0,0]})}_clampPhysicalDelta(t,e,i){const s="partition"===t?[e.a,e.b]:[e.center],n=s.map(t=>t[0]),o=s.map(t=>t[1]);return[Math.max(-$h*Pf-Math.min(...n),Math.min($h*Pf-Math.max(...n),i[0])),Math.max(-$h*Pf-Math.min(...o),Math.min($h*Pf-Math.max(...o),i[1]))]}_physicalMove(t){const e=this._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._svgPoint(t),s="partition"===e.kind?e.base.a:e.base.center,n=this._snap([s[0]+i[0]-e.start[0],s[1]+i[1]-e.start[1]]),o=this._clampPhysicalDelta(e.kind,e.base,[n[0]-s[0],n[1]-s[1]]);this._physicalDrag={...e,delta:o,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){const e=this._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);const i=this._spaceModel();if(!this._curSpaceCfg||!i)return;const s=this._curSpaceCfg;if("partition"===e.kind){const t=(s.partitions||[]).find(t=>t.id===e.id),i=e.base;if(t){const n={...i,a:[i.a[0]+e.delta[0],i.a[1]+e.delta[1]],b:[i.b[0]+e.delta[0],i.b[1]+e.delta[1]]};t.a=[n.a[0]/Pf,n.a[1]/Pf],t.b=[n.b[0]/Pf,n.b[1]/Pf];for(const t of s.openings||[]){if("partition"!==t.host?.kind||t.host.id!==e.id)continue;const i=Ep(t,[n],Pf,this._cellCm,this._gridPitch).resolved;i&&Object.assign(t,Wp(t,i,Pf))}}}else{const t=(s.wall_columns||[]).find(t=>t.id===e.id),n=e.base,o={...n,center:[n.center[0]+e.delta[0],n.center[1]+e.delta[1]]};if(i.wall_columns.some(t=>t.id!==e.id&&dh(t,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));t&&(t.center=[(n.center[0]+e.delta[0])/Pf,(n.center[1]+e.delta[1])/Pf])}this._commitPhysicalGeometry(this._t("history.physical_move"),e.before)}_registerPhysicalTap(t,e,i){const s=performance.now(),n=this._physicalLastTap?.kind===t&&this._physicalLastTap.id===e&&this._physicalLastTap.segment===i&&s-this._physicalLastTap.at<=360;this._physicalLastTap={kind:t,id:e,segment:i,at:s},n&&(this._physicalLastTap=null,this._openPhysicalDialog(t,e,i))}_cancelPhysicalGesture(){this._physicalDrag=null,this._physicalRotate=null,this.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape||!this._spaceModel())return;t.preventDefault(),t.stopPropagation(),Nf(t);const i=this._svgPoint(t);this._physicalSel={kind:"column",id:e.id},this._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(i[1]-e.center[1],i[0]-e.center[0])/Math.PI,baseAngle:Uc(e.angle),angle:Uc(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){const e=this._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const i=this._svgPoint(t),s=180*Math.atan2(i[1]-e.center[1],i[0]-e.center[0])/Math.PI,n=e.baseAngle+s-e.startAngle,o=Uc(t.shiftKey?n:5*Math.round(n/5));this._physicalRotate={...e,angle:o,moved:e.moved||Math.abs(n-e.baseAngle)>=.5}}_physicalRotateUp(t){const e=this._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation(),this._physicalRotate=null;const i=this._spaceModel();if(!e.moved||!this._curSpaceCfg||!i)return;const s=i.wall_columns.find(t=>t.id===e.id);if(!s||"square"!==s.shape)return;const n={...s,angle:e.angle};if(i.wall_columns.some(t=>t.id!==e.id&&dh(t,n,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));const o=this._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);o&&(o.angle=e.angle,this._commitPhysicalGeometry(this._t("history.physical_edit"),e.before))}_rszRooms(){const t=[],e=this._spaceModel();if(!e)return t;for(const i of e.rooms){const e=i.id?hi(i):null;e&&t.push({id:i.id,poly:e})}return t}_rszOpenings(){return this._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen,hosted:!!t.host,angle:t.angle,type:t.type}))}_rszObstacles(){const t=this._spaceModel();if(!t)return[];const e=[];for(const i of t.partitions||[])e.push({kind:"segment",a:[...i.a],b:[...i.b],half:ml(i.cm,this._cellCm,this._gridPitch)/2});for(const i of t.room_drafts||[])for(let t=0;t+1<i.points.length;t++)e.push({kind:"segment",a:[...i.points[t]],b:[...i.points[t+1]],half:ml(Number(i.segments?.[t]?.cm)||ll,this._cellCm,this._gridPitch)/2});for(const i of t.wall_columns||[]){const t=ml(i.cm,this._cellCm,this._gridPitch)/2;e.push({kind:"circle",center:[...i.center],radius:"square"===i.shape?t*Math.SQRT2:t})}return e}_rszOptsFor(t,e){const i=Dl(this._spaceWalls,t,e,this._wallKeyPitch,Pf),s=this._spaceWalls.filter(t=>Array.isArray(t.a)&&Array.isArray(t.b)&&t.a.length>=2&&t.b.length>=2).map(t=>({wall:t,a:[t.a[0]*Pf,t.a[1]*this._spaceH],b:[t.b[0]*Pf,t.b[1]*this._spaceH]})),n=Math.abs(t[0]-e[0])<=.05*this._gridPitch?"v":Math.abs(t[1]-e[1])<=.05*this._gridPitch?"h":null,o=s.filter(i=>!!n&&("h"===n?!(Math.abs(i.a[1]-t[1])>.05*this._gridPitch||Math.abs(i.b[1]-t[1])>.05*this._gridPitch)&&Math.min(Math.max(t[0],e[0]),Math.max(i.a[0],i.b[0]))-Math.max(Math.min(t[0],e[0]),Math.min(i.a[0],i.b[0]))>.05*this._gridPitch:!(Math.abs(i.a[0]-t[0])>.05*this._gridPitch||Math.abs(i.b[0]-t[0])>.05*this._gridPitch)&&Math.min(Math.max(t[1],e[1]),Math.max(i.a[1],i.b[1]))-Math.max(Math.min(t[1],e[1]),Math.min(i.a[1],i.b[1]))>.05*this._gridPitch)),a=new Set(o.map(t=>Number(t.wall.cm)).filter(t=>t>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(t,e,i){const s=this._rszDrag?.snap||i||this._rszSnapshot(),n=`${this._space}|${this._cellCm}|${this._gridPitch}|${s}`;this._rszEligibilityCache&&this._rszEligibilityCache.key===n||(this._rszEligibilityCache={key:n,values:new Map});const o=`${t}:${e}`,a=this._rszEligibilityCache.values.get(o);if(a)return a;const r=this._rszDrag?.rooms||this._rszRooms(),l=r.find(e=>e.id===t),c=l?.poly?.[e]||[0,0],h=l?.poly?.[(e+1)%(l?.poly?.length||1)]||[0,0],d=function(t,e,i,s,n){const{eps:o}=n,a=t.find(t=>t.id===i);if(!a||s<0||s>=(a.poly?.length||0)||a.poly.length<4||!Ia(a.poly))return{enabled:!1,reason:"invalid-geometry"};const r=a.poly[s],l=a.poly[(s+1)%a.poly.length],c=Ea(r,l,o);if(!c)return{enabled:!1,reason:"diagonal"};if(!La(a.poly,s,c,o))return{enabled:!1,reason:"side-angle"};if(n.thicknessConflict)return{enabled:!1,reason:"thickness-conflict"};for(const t of n.obstacles||[])if(Wa(t,r,l,n))return{enabled:!1,reason:"duplicate-physical-wall"};const h=Da(Sa(l,r)),d=[],p=new Set;let u=!1,_=!1;for(const e of t)if(e.id!==i)for(let t=0;t<e.poly.length;t++){const i=e.poly[t],s=e.poly[(t+1)%e.poly.length],n=Ha(r,l,i,s,o);n<=o||(p.add(e.id),Oa(r,l,i,s,o)?d.push({room:e,edge:t}):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 t=d[0];if(!Ia(t.room.poly))return{enabled:!1,reason:"invalid-geometry"};if(!La(t.room.poly,t.edge,c,o))return{enabled:!1,reason:"side-angle"};g.push(t.room.id),m[t.room.id]=t.edge}const f=[];for(const t of e)if(!(Ta([t.x,t.y],r,l)>2*o)){if(t.hosted||t.length>h+2*o)return{enabled:!1,reason:"opening-conflict"};f.push(t.id)}const v=Object.fromEntries(g.map(e=>[e,t.find(t=>t.id===e).poly.length])),y=[];for(const e of g){const i=t.find(t=>t.id===e),s=m[e],n=(s-1+i.poly.length)%i.poly.length,a=(s+1)%i.poly.length,r=Ua(t,e,n,1,o),l=Ua(t,e,a,0,o);if(!r||!l)return{enabled:!1,reason:"partial-shared"};y.push(r,l)}const b={roomId:i,edge:s,a:[...r],b:[...l],n:Ra(a.poly,s),roomIds:g,edgeByRoom:m,topology:v,movingOpeningIds:f,sideOwnership:y};if(!Qa(t,e,b,0,n))return{enabled:!1,reason:"invalid-geometry"};const w=Math.abs(Number(n.step));if(Number.isFinite(w)&&w>o){const i=[-w,w];if(!i.some(i=>Qa(t,e,b,i,n))){if(!i.some(i=>Ya(Ja(t,e,b,i),b,o)))return{enabled:!1,reason:"partial-shared"};const s={...n,obstacles:[]};return i.some(i=>Qa(t,e,b,i,s))?{enabled:!1,reason:"duplicate-physical-wall"}:e.length&&i.some(e=>Qa(t,[],b,e,n))?{enabled:!1,reason:"opening-conflict"}:{enabled:!1,reason:"invalid-geometry"}}}return{enabled:!0,plan:b}}(r,this._rszDrag?.openings||this._rszOpenings(),t,e,this._rszOptsFor(c,h));return this._rszEligibilityCache.values.set(o,d),d}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszApplyPreview(t,e){const i=this._rszDrag,s=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!i||!s||!this._serverCfg)return{ok:!1,reason:"missing-context"};const n=JSON.parse(i.snap),o={...s,rooms:n.rooms,openings:n.openings||[]};for(const t of["walls","open_spans","room_drafts","partitions","wall_columns","decor"])void 0!==n[t]?o[t]=n[t]:delete o[t];for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==n.plan_transform?.[t]?o[t]=n.plan_transform[t]:delete o[t];const a=this._spaceH;for(const[e,i]of Object.entries(t)){const t=o.rooms.find(t=>t.id===e);t&&(t.poly=i.map(t=>[t[0]/Pf,t[1]/a]),delete t.x,delete t.y,delete t.w,delete t.h)}for(const[t,i]of Object.entries(e)){const e=(o.openings||[]).find(e=>e.id===t);e&&(e.x=i[0]/Pf,e.y=i[1]/a)}const r=[],l=[];for(const t of i.changed){const e=i.rooms.find(e=>e.id===t),s=o.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const n=s.poly.map(t=>[t[0]*Pf,t[1]*a]);if(e.poly.length===n.length)for(let t=0;t<e.poly.length;t++)r.push([e.poly[t],e.poly[(t+1)%e.poly.length]]),l.push([n[t],n[(t+1)%n.length]])}if(r.length){const t=Sd(dd(o.open_spans),r,l,Pf);if(t.length?o.open_spans=t:delete o.open_spans,Array.isArray(o.walls)&&o.walls.length){const t=function(t,e,i,s,n=1,o="fixed-topology"){let a=!1;const r=Rl(t,e,i,s,n,o,()=>{a=!0});return{walls:a?(t||[]).map(t=>({...t})):r,rejected:a}}(o.walls,r,l,this._wallKeyPitch,Pf,"fixed-topology");if(t.rejected)return{ok:!1,reason:"wall-metadata"};o.walls=t.walls}}const c=t=>t.map(t=>Number(t.cm)).sort((t,e)=>t-e),h=c(n.walls||[]),d=c(o.walls||[]);if(JSON.stringify(h)!==JSON.stringify(d))return{ok:!1,reason:"wall-metadata"};if((n.open_spans||[]).length!==(o.open_spans||[]).length)return{ok:!1,reason:"open-span-metadata"};const p=[];for(const t of o.rooms||[]){const e=hi(t);if(e&&!(e.length<2))for(let t=0;t<e.length;t++)p.push([[e[t][0]*Pf,e[t][1]*Pf],[e[(t+1)%e.length][0]*Pf,e[(t+1)%e.length][1]*Pf]])}const u=t=>JSON.stringify([t?.key,t?.cm,t?.a,t?.b]),_=new Map;for(const t of n.walls||[]){const e=u(t);_.set(e,(_.get(e)||0)+1)}const m=[];for(const t of o.walls||[]){const e=u(t),i=_.get(e)||0;i?_.set(e,i-1):m.push(t)}return Al(m,p,this._wallKeyPitch,Pf,n.walls||[]).length?{ok:!1,reason:"wall-metadata"}:this._rszSpaceCandidateRenderable(this._space,o)?(this._rszPreview={space:this._space,sp:o},this._cfgEpoch++,{ok:!0}):{ok:!1,reason:"physical-geometry"}}_rszSpaceCandidateRenderable(t,e){if(!this._serverCfg)return!1;try{const i={...this._serverCfg,spaces:this._serverCfg.spaces.map(i=>i.id===t?e:i)};return this._checkSpacePhysicalGeometry(i,t).ok}catch{return!1}}_rszCandidateRenderable(t){return!!t&&t.space===this._space&&this._rszPreview===t&&this._rszSpaceCandidateRenderable(t.space,t.sp)}_rszEdgeDown(t,e,i){if("resize"!==this._tool||this._rszDrag)return;t.stopPropagation(),t.preventDefault();const s=this._rszRooms(),n=this._rszResolution(e,i);if(!n.enabled)return void this._showToast(this._rszReasonText(n.reason));Nf(t);const o=n.plan,a=this._svgPoint(t);this._rszDrag={pid:t.pointerId,start:[a[0],a[1]],roomId:e,plan:o,opts:this._rszOptsFor(o.a,o.b),rooms:s,openings:this._rszOpenings(),snap:this._rszSnapshot(),moved:!1,d:0,changed:[...o.roomIds],rejectNotified:!1}}_rszReasonText(t){return this._t(`resize.disabled.${t}`)}_rszDisabledActivate(t,e){t.stopPropagation(),t.preventDefault(),this._showToast(this._rszReasonText(e))}_rszDisabledKey(t,e){"Enter"!==t.key&&" "!==t.key||this._rszDisabledActivate(t,e)}_rszMove(t){const e=this._rszDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._svgPoint(t),s=e.plan,n=(o=e.start,a=i,r=s.n,[o?.[0],o?.[1],a?.[0],a?.[1],r?.[0],r?.[1]].every(Number.isFinite)?(a[0]-o[0])*r[0]+(a[1]-o[1])*r[1]:0);var o,a,r;const l=this._snap([s.a[0]+s.n[0]*n,s.a[1]+s.n[1]*n]),c=(l[0]-s.a[0])*s.n[0]+(l[1]-s.a[1])*s.n[1];let h=function(t,e,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=tr.get(i);c&&c.opts===o||(c={opts:o,values:new Map},tr.set(i,c));const h=c.values;let d=0;for(let s=Math.min(l,r),n=0;n<4096&&s<=r+1e-9;n++,s=Math.min(r,s+l)){const n=a*s,c=`${n.toFixed(9)}|${l.toFixed(9)}`;let p=h.get(c);if(void 0===p&&(p=Qa(t,e,i,n,o),h.size<4096&&h.set(c,p)),!p)break;if(d=n,Math.abs(s-r)<=1e-9)break}return d}(e.rooms,e.openings,s,c,this._gridPitch,e.opts);if(h===e.d&&e.moved)return;const d=this._rszPreview,p=this._rszLive,u=e.d;let _=Ja(e.rooms,e.openings,s,h);if(!this._rszApplyPreview(_.polys,_.openings).ok)return this._rszPreview=d,this._rszLive=p,e.d=u,e.rejectNotified||(e.rejectNotified=!0,this._showToast(this._t("resize.preview_failed"))),void this.requestUpdate();e.moved=!0,e.changed=[...s.roomIds],e.d=h,this._rszLive=this._rszEdgeLabels(_,s),this.requestUpdate()}_rszUp(t){const e=this._rszDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._rszPreview,s=e.moved&&Math.abs(e.d)>1e-9,n=this._rszSnapshot()===e.snap,o=Qa(e.rooms,e.openings,e.plan,e.d,e.opts),a=this._rszCandidateRenderable(i);if(this._rszDrag=null,this._rszLive=null,this._rszPreview=null,!s||!i)return this._cfgEpoch++,void this.requestUpdate();if(!n||!o||!a)return this._cfgEpoch++,this._showToast(this._t("resize.commit_failed")),void this.requestUpdate();const r=JSON.parse(e.snap),l=this._curSpaceCfg;l&&(l.rooms=i.sp.rooms,l.openings=i.sp.openings,Array.isArray(i.sp.walls)&&(i.sp.walls.length?l.walls=i.sp.walls:delete l.walls),Array.isArray(i.sp.open_spans)&&i.sp.open_spans.length?l.open_spans=i.sp.open_spans:delete l.open_spans),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._commitPhysicalGeometry(this._t("history.resize_room"),r),this._rszEligibilityCache=null,this.requestUpdate()}_rszCancelDrag(){this._rszDrag&&(this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._cfgEpoch++,this.requestUpdate())}_rszPointerCancel(t){const e=this._rszDrag;e&&e.pid===t.pointerId&&(t.stopPropagation(),this._rszCancelDrag())}_rszEdgeLabels(t,e){const i=this._rszDrag,s=[],n=t.polys[e.roomId]||i.rooms.find(t=>t.id===e.roomId).poly,o=n.length,a=this._rszInnerSpanCms(e.roomId,n,t.polys);for(const t of function(t,e){const i=t.length;return[(e-1+i)%i,(e+1)%i]}(n,e.edge)){const e=n[t],i=n[(t+1)%o],r=a?.[t];s.push({kind:"length",x:(e[0]+i[0])/2,y:(e[1]+i[1])/2,text:null==r?this._fmtLen(e,i):li(r,"mi"===this.hass?.config?.unit_system?.length),edge:{a:[e[0],e[1]],b:[i[0],i[1]]}})}const r="mi"===this.hass?.config?.unit_system?.length,l=e.roomIds,c=this._spaceWalls,h=this._openPairs().flatMap(t=>t.segs),d=this._physicalBodiesR(),p=this._baseVb(),u=this._view&&this._view.w>0&&this._view.h>0?this._view:{x:p[0],y:p[1],w:p[2],h:p[3]},[_,m]=this._lastValidStageSize||[u.w,u.h],g={...u,stageWidth:Math.max(1,_),stageHeight:Math.max(1,m)},f=this._spaceModel(),v=this._config?.icon_size??2.5,y=f?Gh(v>8?2.5:v,f,u.w,this._kiosk?this._kioskScale.icon:1)*g.stageWidth/100:24,b=Math.max(10,.77*y),w=this._t("room.settings_short"),k=b*(1.457+.66*Math.max(1,w.length)*.42);for(const n of l){const o=t.polys[n]||i.rooms.find(t=>t.id===n).poly,a=c.length&&_c(Object.entries(t.polys).map(([t,e])=>({id:t,poly:e})),n,c,h,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf)||o,l=ir(d.length?ah(oh(a,d))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:er(a,this._gridPitch,this._cellCm),r),p=nr({poly:o,edge:e.edgeByRoom[n],text:l,view:g,gearCenter:$i(o),gearWidthPx:k,gearHeightPx:b});s.push({kind:"area",roomId:n,x:p.anchor[0],y:p.anchor[1],text:l,placement:p})}return s}_rszInnerSpanCms(t,e,i){const s=Object.keys(i).length?Object.entries(i).map(([t,e])=>({id:t,poly:e})):this._spaceModel()?.rooms;if(!s?.length)return null;const n=this._spaceWalls;if(!n.length)return null;const o=function(t,e,i,s,n,o,a,r=1){const l=(t||[]).find(t=>t?.id===e),c=hi(l);if(!c||c.length<3)return null;const h=rc(t,e,i,s,n,o,a,r);if(!h)return c.map(()=>0);const d=4*jl(n,r);return c.map((t,e)=>{const i=c[(e+1)%c.length],s=[(t[0]+i[0])/2,(t[1]+i[1])/2];for(let t=0;t<h.poly.length;t++){const e=h.poly[t],i=h.poly[(t+1)%h.poly.length];if(pc(s[0],s[1],e[0],e[1],i[0],i[1])<=d)return Math.max(0,h.offsets[t]||0)}return 0})}(s,t,n,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,Pf);if(!o||o.length!==e.length)return null;const a=this._cellCm/this._gridPitch;return e.map((t,i)=>function(t,e,i){const s=t?.length||0;if(s<3||!Array.isArray(i)||i.length!==s)return 0;const n=(Math.trunc(e)%s+s)%s,o=t[n],a=t[(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=Ul(t,n),u=[o[0]+p[0]*h,o[1]+p[1]*h],_=e=>{const n=Math.max(0,Number(i[e])||0);if(!(n>0))return null;const a=t[e],r=t[(e+1)%s],l=r[0]-a[0],c=r[1]-a[1],h=Math.hypot(l,c);if(!(h>0))return null;const p=Ul(t,e),_=Kl(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}(e,i,o)*a)}_renderResizeMeasurements(){if(!this._rszLive?.length)return U;const t=this._rszLive.filter(t=>"length"===t.kind),e=this._rszLive.filter(t=>"area"===t.kind);return j`<g class="rszmeasurelayer" aria-hidden="true" pointer-events="none">
|
||
${t.map((t,e)=>j`<g class="rszmeasuredge"
|
||
data-hp="resize-measured-edge" data-edge-index=${e}>
|
||
<line class="rszmeasurehalo" x1=${t.edge.a[0]} y1=${t.edge.a[1]}
|
||
x2=${t.edge.b[0]} y2=${t.edge.b[1]}></line>
|
||
<line class="rszmeasureink" x1=${t.edge.a[0]} y1=${t.edge.a[1]}
|
||
x2=${t.edge.b[0]} y2=${t.edge.b[1]}></line>
|
||
</g>`)}
|
||
${e.map(t=>j`<line class="rszleader" data-hp="resize-area-leader"
|
||
data-room=${t.roomId}
|
||
x1=${t.placement.leader.a[0]} y1=${t.placement.leader.a[1]}
|
||
x2=${t.placement.leader.b[0]} y2=${t.placement.leader.b[1]}></line>`)}
|
||
</g>`}_renderResizeLayer(t){const e=Math.max(.013*t.w,5),i=e/2,s=t=>t.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._rszDrag?.snap||this._rszSnapshot();for(const t of a)for(let i=0;i<t.poly.length;i++){const a=t.poly[i],l=t.poly[(i+1)%t.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(t.id,i,r),u=!p.enabled,_=u?this._rszReasonText(p.reason):this._t("title.markup_resize");o.push(j`<circle class="rszhandle ${u?"disabled":""}"
|
||
cx="${c}" cy="${h}" r="${s(e)}" tabindex="0" role="button"
|
||
aria-disabled="${u?"true":"false"}" aria-label="${_}"
|
||
@pointerdown=${e=>this._rszEdgeDown(e,t.id,i)}
|
||
@pointermove=${t=>this._rszMove(t)}
|
||
@pointerup=${t=>this._rszUp(t)}
|
||
@pointercancel=${t=>this._rszPointerCancel(t)}
|
||
@lostpointercapture=${t=>this._rszPointerCancel(t)}
|
||
@click=${u?t=>this._rszDisabledActivate(t,p.reason):null}
|
||
@keydown=${u?t=>this._rszDisabledKey(t,p.reason):null}>
|
||
<title>${_}</title>
|
||
</circle>`),o.push(j`<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 j`${o}`}get _openingsR(){const t=this._curSpaceCfg,e=this._spaceH,i=this._spaceModel();return i?(t?.openings||[]).flatMap(t=>{const s={...t,rx:t.x*Pf,ry:t.y*e,rlen:t.length*Pf};if(!t.host)return[s];const n=Ep(t,i.partitions,Pf,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===t.host.id&&(o+=r.delta[0],a+=r.delta[1]),[{...t,rx:o,ry:a,rlen:n.resolved.length,angle:n.resolved.angle,partitionHost:n.resolved}]}):[]}_partitionOpeningCuts(t=this._spaceModel(),e=()=>!0){if(!t)return[];return Om(Nm(this._curSpaceCfg?.id===t.id?this._curSpaceCfg:null,t,this._cellCm,this._gridPitch,Pf),e)}_roomWallOpeningInputs(t=this._openingsR,e=this._spaceModel()){if(!e)return[];const i=this._openPairs().flatMap(t=>t.segs);return Hm(t,e,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf)}_openingFace(t,e,i){return t.partitionHost?Lp(t.partitionHost,i):Hc(e,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:i})}_cmToUnits(t){return t/this._cellCm*this._gridPitch}get _decorList(){const t=this._curSpaceCfg;return Array.isArray(t?.decor)?t.decor:[]}get _decorH(){return Pf}_decorResolvedStyle(t){return function(t,e,i,s=Rr){const n="rect"===t?.kind||"ellipse"===t?.kind,o=t,a=Rt(o?.color,s.color);return{color:a,opacity:Ir(o?.opacity,s.opacity),widthCm:Nr(t,e,i,s.widthCm),fill:!!n&&!0===o?.fill,fillColor:Rt(o?.fill_color,o?.fill?a:s.fillColor),fillOpacity:n&&o?.fill?Ir(o?.fill_opacity,.25):s.fillOpacity}}(t,this._cellCm,this._gridPitch,Rr)}_decorWidthUnits(t){return((t,e,i,s=Rr.widthCm)=>{const n=Number(t?.width_cm);if(Number.isFinite(n)&&n>0)return Fr(n,e,i);const o=Number(t?.width);return Number.isFinite(o)&&o>0?o:Fr(s,e,i)})(t,this._cellCm,this._gridPitch,Rr.widthCm)}_decorTextSizeCm(t){if("text"===t?.kind){const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?e:Er(es*is(t),this._cellCm,this._gridPitch)}return Er(es,this._cellCm,this._gridPitch)}_decorTextUnits(t){if("text"!==t.kind)return es;const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?Fr(e,this._cellCm,this._gridPitch):es*is(t)}_decorSmallField(t){return Math.round(100*(this._imperial?t/2.54:t))/100}_decorSmallCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(100,e)):.1}_decorTextCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(pf,e)):.1}_decorLargeField(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_decorLargeCm(t){const e=this._imperial?30.48*t:100*t;return Number.isFinite(e)?Math.max(.1,Math.min($h*this._cellCm,e)):.1}_angleField(t){const e=Number(t);return Number.isFinite(e)?String(Number(e.toFixed(3))):"0"}_decorBoxOf(t){return"rect"!==t.kind&&"ellipse"!==t.kind&&"furniture"!==t.kind?null:{x:t.x*Pf,y:t.y*this._decorH,w:t.w*Pf,h:t.h*this._decorH,angle:Ar(t.angle)||void 0}}_decorSnapGeometry(t){const e=this._spaceModel();if(!e)return{points:[],segments:[]};const i=t||"",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 e of this._decorList)if(e.id!==t)if("line"===e.kind){const t=[e.x1*Pf,e.y1*this._decorH],i=[e.x2*Pf,e.y2*this._decorH];n.push({points:[t,i,[(t[0]+i[0])/2,(t[1]+i[1])/2]],segments:[{a:t,b:i}]})}else if("text"===e.kind)n.push({points:[[e.x*Pf,e.y*this._decorH]],segments:[]});else{const t=this._decorBoxOf(e);t&&n.push(Lr(t))}for(const t of e.rooms){const e=hi(t);e?.length&&n.push({points:e.flatMap((t,i)=>{const s=e[(i+1)%e.length];return[t,[(t[0]+s[0])/2,(t[1]+s[1])/2]]}),segments:e.map((t,i)=>({a:t,b:e[(i+1)%e.length]}))})}const o=function(t){return{points:t.flatMap(t=>t.points),segments:t.flatMap(t=>t.segments)}}(n);return this._decorSnapCache={epoch:this._cfgEpoch,space:this._space,height:this._decorH,exclude:i,geometry:o},o}_decorSnap(t,e="mouse",i){const s=this._stageEl,n=this._viewOr(this._baseVb()),o="touch"===e||"pen"===e?14:8,a=s?n.w/Math.max(1,s.clientWidth)*o:this._gridPitch;return function(t,e,i,s){const n=s(t);let o=null;for(const n of e.points){const e=s(n),a=Math.hypot(e[0]-t[0],e[1]-t[1]);a<=i&&(!o||a<o.d)&&(o={p:e,d:a,kind:"point",target:n})}for(const n of e.segments){const e=qr(t,n.a,n.b),a=s(e),r=Math.hypot(a[0]-t[0],a[1]-t[1]);r<=i&&(!o||r<o.d)&&(o={p:a,d:r,kind:"edge",target:e})}return o?{point:o.p,target:o.target,kind:o.kind}:{point:n,target:null,kind:"grid"}}(t,this._decorSnapGeometry(i),a,t=>this._snap(t)).point}_replaceDecor(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>i.id===t?{...i,...e}:i),this.requestUpdate())}_cancelDecorGesture(){const t=this._decorMove?.before||this._dtDrag?.before||this._bdDrag?.before,e=t&&this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(t&&e){const i=t=>JSON.parse(JSON.stringify(t));void 0!==t.decor?e.decor=i(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];Object.assign(e,i(t.plan_transform||{})),this._cfgEpoch++}this._decorMove=null,this._dtDrag=null,this._bdDrag=null,this.requestUpdate()}_decorPointerDown(t){const e=this._decorTool;if("select"===e||"erase"===e?t.target.closest?.(".dshape"):null)return!0;if("line"===e||"rect"===e||"ellipse"===e){t.preventDefault();const i=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorDraft={kind:e,a:i,b:i,pid:t.pointerId},Nf(t),!0}if("text"===e){const e=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorTextDialog={x:Ih(e[0]/Pf),y:Ih(e[1]/this._decorH),text:"",color:this._decorStyle.color,opacity:this._decorStyle.opacity,angle:"0",sizeCm:Er(es,this._cellCm,this._gridPitch)},this._decorTextSelection={start:0,end:0},!0}if("furniture"===e)return!!this._furnPalette&&(t.preventDefault(),this._furnPlace(this._svgPoint(t),t.shiftKey),!0);if("select"===e&&(this._decorSel=null),this._bdMovable){const e=this._bdRect,i=this._svgPoint(t),s=e.x+e.w/2,n=e.y+e.h/2,o=-Ar(e.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]>=e.x&&l[0]<=e.x+e.w&&l[1]>=e.y&&l[1]<=e.y+e.h)return t.preventDefault(),this._bdStart(t)}return!1}_decorCommitDraft(){const t=this._decorDraft;if(this._decorDraft=null,!t)return;const e=.5*this._gridPitch;if(!function(t,e,i,s){const n=Math.abs(i[0]-e[0]),o=Math.abs(i[1]-e[1]);return"line"===t?Math.hypot(n,o)>=s:n>=s&&o>=s}(t.kind,t.a,t.b,e))return;const i=Pf,s=this._decorH,n=this._decorStyle,o=this._geometrySnapshot(),a="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),r=Ih;let l;if("line"===t.kind)l={id:a,kind:"line",x1:r(t.a[0]/i),y1:r(t.a[1]/s),x2:r(t.b[0]/i),y2:r(t.b[1]/s),...Or(n,!1)};else{const e=r(Math.min(t.a[0],t.b[0])/i),o=r(Math.min(t.a[1],t.b[1])/s),c=Math.abs(t.b[0]-t.a[0])/i,h=Math.abs(t.b[1]-t.a[1])/s;l={id:a,kind:t.kind,x:e,y:o,w:c,h:h,...Or(n,!0)}}this._curSpaceCfg.decor=[...this._decorList,l],this._decorSel=a,this._recordGeometry(this._t("history.decor_add"),o),this._saveConfig(),this.requestUpdate()}_decorShapeDown(t,e){if("decor"!==this._mode)return;const i=this._decorTool;if("text"===i){if("text"!==e.kind)return;return t.stopPropagation(),t.preventDefault(),void this._decorOpenText(e)}"select"!==i&&"erase"!==i||(t.stopPropagation(),t.preventDefault(),"erase"!==i?(this._decorSel=e.id,this._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1,before:this._geometrySnapshot()},Nf(t)):this._decorEraseConfirm={id:e.id,kind:e.kind})}_decorMoveUpdate(t){const e=this._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const i=this._svgPoint(t),s=e.orig,n=("line"===s.kind?s.x1:s.x)*Pf,o=("line"===s.kind?s.y1:s.y)*this._decorH,a=this._decorSnap([n+(i[0]-e.start[0]),o+(i[1]-e.start[1])],t.pointerType,e.id);let r=(a[0]-n)/Pf,l=(a[1]-o)/this._decorH;const c=e.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,_=$h;r=Math.max(-_-h,Math.min(_-h-p,r)),l=Math.max(-_-d,Math.min(_-d-u,l)),(r||l)&&(e.moved=!0);this._curSpaceCfg.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i=e.orig;return"line"===t.kind?{...t,x1:i.x1+r,y1:i.y1+l,x2:i.x2+r,y2:i.y2+l}:{...t,x:i.x+r,y:i.y+l}}),this.requestUpdate()}_decorShapeDbl(t,e){"decor"===this._mode&&"select"===this._decorTool&&(t.preventDefault(),t.stopPropagation(),this._decorMove=null,this._decorSel=e.id,this._openDecorProperties(e))}_openDecorProperties(t){if("decor"!==this._mode||"select"!==this._decorTool)return;if("text"===t.kind)return void this._decorOpenText(t);if(!["line","rect","ellipse","furniture"].includes(t.kind))return;const e=this._decorResolvedStyle(t),i="line"===t.kind?t:null,s=this._decorBoxOf(t);this._decorShapeDialog={id:t.id,kind:t.kind,color:e.color,opacity:e.opacity,widthCm:e.widthCm,angle:this._angleField(Ar(i?Ns([i.x1*Pf,i.y1*this._decorH],[i.x2*Pf,i.y2*this._decorH]):t.angle)),...i?{lengthCm:Er(Math.hypot((i.x2-i.x1)*Pf,(i.y2-i.y1)*this._decorH),this._cellCm,this._gridPitch),lineStyle:"dashed"===i.line_style?"dashed":"solid"}:{},...s?{sizeWCm:Er(s.w,this._cellCm,this._gridPitch),sizeHCm:Er(s.h,this._cellCm,this._gridPitch)}:{},..."furniture"===t.kind?{symbol:t.symbol}:{},..."rect"===t.kind||"ellipse"===t.kind?{fill:e.fill,fillColor:e.fillColor,fillOpacity:e.fillOpacity}:{}}}_decorOpenText(t){if("text"!==t.kind)return;let e=String(t.text??"");const i=[...e.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!Yi(t[1])),s=String(t.unit??"").trim(),n="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,o=i||s?"":Ji(t.entity,n),a=!(!String(t.entity??"").trim()||i||o&&!s);if(o&&!a){const t=e.indexOf("{}");e=t>=0?e.slice(0,t)+o+e.slice(t+2):`${e}${e?" ":""}${o}`}this._decorTextDialog={id:t.id,x:t.x,y:t.y,text:e,color:Rt(t.color,this._decorStyle.color),opacity:Ir(t.opacity,this._decorStyle.opacity),angle:this._angleField(t.angle),sizeCm:this._decorTextSizeCm(t),pickerEntity:t.entity||"",preserveLegacy:a||void 0},this._decorTextSelection={start:e.length,end:e.length}}_decorRememberTextSelection(t){this._decorTextSelection={start:t.selectionStart??t.value.length,end:t.selectionEnd??t.value.length}}_decorInsertLiveVariable(t){const e=this._decorTextDialog;if(!e)return;const i=Ji(e.pickerEntity,t);if(!i)return;const s=e.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={...e,text:a,preserveLegacy:void 0},this.updateComplete.then(()=>{const t=this.renderRoot.querySelector("textarea.dtarea");t&&(t.focus(),t.setSelectionRange(r,r))})}_decorSaveText(){const t=this._decorTextDialog,e=String(t?.text??"").replace(/\r\n?/g,"\n").trim();if(!t||!e)return void(this._decorTextDialog=null);const i=this._geometrySnapshot(),s=this._curSpaceCfg,n={color:t.color,opacity:Ir(t.opacity),size_cm:Number(Math.max(.1,Math.min(pf,t.sizeCm)).toFixed(4)),...Ar(t.angle)?{angle:Ar(t.angle)}:{}};if(t.id)s.decor=this._decorList.map(i=>{if(i.id!==t.id)return i;if("text"!==i.kind)return i;if(t.preserveLegacy){const{angle:t,size:s,scale:o,...a}=i;return{...a,text:e,...n}}const{entity:s,attr:o,unit:a,...r}=i,{angle:l,size:c,scale:h,...d}=r;return{...d,text:e,...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:t.x,y:t.y,text:e,...n}],this._decorSel=i}this._decorTextDialog=null,this._recordGeometry(this._t(t.id?"history.decor_edit":"history.decor_add"),i),this._saveConfig(),this.requestUpdate()}_decorSaveShape(){const t=this._decorShapeDialog;if(!t)return;const e=this._geometrySnapshot(),i={color:t.color,opacity:Ir(t.opacity),widthCm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),fill:!!t.fill,fillColor:t.fillColor||t.color,fillOpacity:Ir(t.fillOpacity,.25)};this._curSpaceCfg.decor=this._decorList.map(e=>{if(e.id!==t.id)return e;const s="rect"===t.kind||"ellipse"===t.kind,n=Or(i,s);if("line"===e.kind){const i=(e.x1+e.x2)/2*Pf,s=(e.y1+e.y2)/2*this._decorH,o=Fr(Math.max(.1,Number(t.lengthCm)||.1),this._cellCm,this._gridPitch),a=Ar(t.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}=e;return{...u,...n,x1:Ih(c[0]/Pf),y1:Ih(c[1]/this._decorH),x2:Ih(h[0]/Pf),y2:Ih(h[1]/this._decorH),..."dashed"===t.lineStyle?{line_style:"dashed"}:{}}}if("rect"===e.kind||"ellipse"===e.kind||"furniture"===e.kind){const i=e.w*Pf,s=e.h*this._decorH,o=Math.max(this._gridPitch,ri(Fr(Number(t.sizeWCm),this._cellCm,this._gridPitch),this._gridPitch)),a=Math.max(this._gridPitch,ri(Fr(Number(t.sizeHCm),this._cellCm,this._gridPitch),this._gridPitch)),r=e.x*Pf+i/2,l=e.y*this._decorH+s/2,c=Ar(t.angle),h=zr({x:r-o/2,y:l-a/2},c,t=>this._snap(t)),{width:d,angle:p,...u}=e;return{...u,...n,x:Ih(h[0]/Pf),y:Ih(h[1]/this._decorH),w:o/Pf,h:a/this._decorH,..."furniture"===e.kind&&t.symbol?{symbol:t.symbol}:{},...c?{angle:c}:{}}}return e}),this._decorStyle={...i,fill:"rect"===t.kind||"ellipse"===t.kind?i.fill:this._decorStyle.fill,fillColor:"rect"===t.kind||"ellipse"===t.kind?i.fillColor:this._decorStyle.fillColor,fillOpacity:"rect"===t.kind||"ellipse"===t.kind?i.fillOpacity:this._decorStyle.fillOpacity},this._decorShapeDialog=null,this._recordGeometry(this._t("history.decor_edit"),e),this._saveConfig(),this.requestUpdate()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(t=>t.id===this._decorSel)||null}_dtPivot(t){return"line"===t.kind?[(t.x1+t.x2)/2*Pf,(t.y1+t.y2)/2*this._decorH]:"furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind?[(t.x+t.w/2)*Pf,(t.y+t.h/2)*this._decorH]:[t.x*Pf,t.y*this._decorH]}_dtApply(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const s={...i};"text"===i.kind&&void 0!==e.textSizeCm&&(delete s.size,delete s.scale);const n={...s};return void 0!==e.textSizeCm&&(n.size_cm=Number(Math.max(.1,Math.min(pf,e.textSizeCm)).toFixed(4))),void 0!==e.angle&&(e.angle?n.angle=Number(e.angle.toFixed(2)):delete n.angle),n}),this._cfgEpoch++,this.requestUpdate())}_dtStart(t,e,i,s){const n=this._dtSel;if(!n)return;t.stopPropagation(),t.preventDefault();const[o,a]=this._dtPivot(n),r=this._svgPoint(t),l=this._decorBoxOf(n);this._dtDrag={id:n.id,kind:e,pid:t.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},Nf(t)}_dtMove(t){const e=this._dtDrag;if(!e)return;const i=this._svgPoint(t);if(void 0!==e.lineEnd&&"line"===e.origShape.kind){const s=this._decorSnap(i,t.pointerType,e.id),n=Ih(s[0]/Pf),o=Ih(s[1]/this._decorH),a=e.origShape,r=0===e.lineEnd?a.x1:a.x2,l=0===e.lineEnd?a.y1:a.y2;return(Math.abs(n-r)>1e-9||Math.abs(o-l)>1e-9)&&(e.moved=!0),void this._replaceDecor(e.id,0===e.lineEnd?{x1:n,y1:o}:{x2:n,y2:o})}if("scale"===e.kind&&e.orig){const s=Br(e.orig,e.sgx??1,e.sgy??1,i[0],i[1],!t.shiftKey,this._gridPitch,this._gridPitch),n=Math.abs(s.x-e.orig.x)>1e-6||Math.abs(s.y-e.orig.y)>1e-6||Math.abs(s.w-e.orig.w)>1e-6||Math.abs(s.h-e.orig.h)>1e-6;if(!n&&!e.moved)return;return e.moved||=n,void this._decorApplyBox(e.id,s)}if("scale"===e.kind){const t=Math.hypot(i[0]-e.ax,i[1]-e.ay);if(e.r0<1e-6)return;const s=Math.max(.1,Math.min(pf,e.textSizeCm0*(t/e.r0))),n=Math.abs(s-e.textSizeCm0)>1e-6;if(!n&&!e.moved)return;return e.moved||=n,void this._dtApply(e.id,{textSizeCm:s})}const s=180*Math.atan2(i[1]-e.ay,i[0]-e.ax)/Math.PI;let n=e.angle0+(s-e.a0);t.shiftKey||(n=5*Math.round(n/5)),n=(n%360+360)%360,n>180&&(n-=360);const o=Math.abs(n-e.angle0)>1e-6;(o||e.moved)&&(e.moved||=o,this._dtApply(e.id,{angle:n}))}_dtUp(){const t=this._dtDrag;this._dtDrag=null,t?.moved&&(this._recordGeometry(this._t("history.decor_transform"),t.before),this._saveConfig()),this.requestUpdate()}_dtMeasure(){const t=this._dtSel;if(!t)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let e;if("line"===t.kind){const i=t.x1*Pf,s=t.y1*this._decorH,n=t.x2*Pf,o=t.y2*this._decorH;e={id:t.id,x:Math.min(i,n),y:Math.min(s,o),w:Math.abs(n-i),h:Math.abs(o-s)}}else if("furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind)e={id:t.id,x:t.x*Pf,y:t.y*this._decorH,w:t.w*Pf,h:t.h*this._decorH};else{const i=this.renderRoot.querySelector(`text.dtext[data-id="${t.id}"]`);if(!i||"function"!=typeof i.getBBox)return;let s;try{s=i.getBBox()}catch{return}if(!s||!s.width&&!s.height)return;e={id:t.id,x:s.x,y:s.y,w:s.width,h:s.height}}const i=this._dtBox;i&&i.id===e.id&&Math.abs(i.x-e.x)<.01&&Math.abs(i.y-e.y)<.01&&Math.abs(i.w-e.w)<.01&&Math.abs(i.h-e.h)<.01||(this._dtBox=e,this.requestUpdate())}_deleteDecor(t){if(!this._decorList.some(e=>e.id===t))return;const e=this._geometrySnapshot();this._curSpaceCfg.decor=this._decorList.filter(e=>e.id!==t),this._decorSel===t&&(this._decorSel=null),this._recordGeometry(this._t("history.decor_delete"),e),this._saveConfig(),this.requestUpdate()}_decorDeleteSel(){this._decorSel&&this._deleteDecor(this._decorSel)}_confirmDecorErase(){const t=this._decorEraseConfirm;this._decorEraseConfirm=null,t&&this._deleteDecor(t.id)}get _furnWalls(){const t=this._rawPhysicalBodiesR().flatMap(t=>t.map((e,i)=>{const s=t[(i+1)%t.length];return[e[0],e[1],s[0],s[1]]}));return[...this._segments,...t]}get _furnWallReach(){return 6*this._gridPitch}_furnFieldValue(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_furnFieldToCm(t){return e=this._imperial?30.48*t:100*t,Number.isFinite(e)?Math.max(1,Math.min(1e4,e)):1;var e}_furnPick(t){const e=function(t){const e=Zh(t);return e?{w:e.w,h:e.h}:{w:60,h:60}}(t);this._furnPalette={symbol:t,w:e.w,h:e.h}}_furnPlace(t,e=!1){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const n=Pf,o=this._decorH,a=sd(td(i.w,this._cellCm,this._gridPitch,n)),r=sd(td(i.h,this._cellCm,this._gridPitch,n)),l=this._geometrySnapshot(),c=this._decorSnap(t);let h=c[0],d=c[1],p=0;const u=e?null:rd(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:Ih(h/n-a/2),y:Ih(d/o-r/2),w:a,h:r,...Or(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(t){const e=this._decorMove;if("furniture"!==e.orig.kind)return;const i=e.orig,s=this._curSpaceCfg;if(!s)return;const n=Pf,o=this._decorH,a=this._svgPoint(t),r=(i.x+i.w/2)*n+(a[0]-e.start[0]),l=(i.y+i.h/2)*o+(a[1]-e.start[1]);let c,h,d=Number(i.angle)||0;const p=t.shiftKey?null:rd(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],t.pointerType,e.id);c=s[0]/n,h=s[1]/o}c=Ih(c),h=Ih(h),(Math.abs(c-i.x)>1e-9||Math.abs(h-i.y)>1e-9||Math.abs(d-(Number(i.angle)||0))>1e-9)&&(e.moved=!0),s.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i={...t,x:c,y:h};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_decorApplyBox(t,e){const i=this._curSpaceCfg;if(!i)return;const s=Pf,n=this._decorH;i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const o=zr(e,i.angle,t=>this._snap(t));return{...i,x:Ih(o[0]/s),y:Ih(o[1]/n),w:Math.max(this._gridPitch/s,Math.min(2*$h,e.w/s)),h:Math.max(this._gridPitch/n,Math.min(2*$h,e.h/n))}}),this._cfgEpoch++,this.requestUpdate()}get _furnLive(){const t=this._dtDrag;if(!t||"scale"!==t.kind||!t.orig)return null;const e=this._decorList.find(e=>e.id===t.id);if(!e||"furniture"!==e.kind)return null;const i=Pf,s=this._decorH,n=e.w*i,o=e.h*s,a=function(t,e,i,s,n){const o=t+i/2,a=e+s/2,r=(Number(n)||0)*Math.PI/180,l=Math.cos(r),c=Math.sin(r),h=(t,e)=>{const i=t-o,s=e-a;return[o+i*l-s*c,a+i*c+s*l]};return[h(t,e),h(t+i,e),h(t+i,e+s),h(t,e+s)]}(e.x*i,e.y*s,n,o,Number(e.angle)||0),r=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[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 t=this._furnPalette,e=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return W`<div class="furnpalette" @pointerdown=${t=>t.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">
|
||
${Yh.map(e=>W`
|
||
<div class="furngroup" data-group=${e}>${this._t(`furn.group_${e}`)}</div>
|
||
<div class="furnrow">
|
||
${Qh(e).map(e=>W`<button
|
||
class="furnitem ${t?.symbol===e.id?"on":""}" data-symbol=${e.id}
|
||
title=${this._t(`furn.sym_${e.id}`)}
|
||
@click=${()=>this._furnPick(e.id)}>
|
||
${(t=>{const e=Zh(t),i=36/Math.max(e.w,e.h),s=e.w*i,n=e.h*i;return j`<svg class="furnprev" viewBox="0 0 40 40" aria-hidden="true"><g
|
||
transform="translate(${(40-s)/2} ${(40-n)/2})"><path
|
||
d=${od(t,s,n)} fill="none" stroke="currentColor"
|
||
stroke-width="1.2" stroke-linejoin="round"></path></g></svg>`})(e.id)}<span>${this._t(`furn.sym_${e.id}`)}</span>
|
||
</button>`)}
|
||
</div>`)}
|
||
</div>
|
||
${t?W`<div class="furnsize">
|
||
<label>${this._t("furn.width")}<span class="furnunit">${e}</span></label>
|
||
<input class="namein furnw" type="number" min="0.01" step="0.05"
|
||
.value=${String(this._furnFieldValue(t.w))}
|
||
@input=${e=>this._furnPalette={...t,w:this._furnFieldToCm(Number(e.target.value))}} />
|
||
<label>${this._t("furn.depth")}<span class="furnunit">${e}</span></label>
|
||
<input class="namein furnh" type="number" min="0.01" step="0.05"
|
||
.value=${String(this._furnFieldValue(t.h))}
|
||
@input=${e=>this._furnPalette={...t,h:this._furnFieldToCm(Number(e.target.value))}} />
|
||
<span class="furnhint">${this._t("furn.place_hint")}</span>
|
||
</div>`:W`<div class="furnsize"><span class="furnhint">${this._t("furn.pick_hint")}</span></div>`}
|
||
</div>`}get _bdBase(){const t=this._curSpaceCfg;return t?.plan_url?{...vh(t.plan_aspect,Pf)}:null}get _bdRect(){const t=this._curSpaceCfg;return t?.plan_url?wh(t,Pf):null}get _bdParams(){const t=this._curSpaceCfg,e=Number(t?.plan_x),i=Number(t?.plan_y),s=Number(t?.plan_scale),n=Number(t?.plan_scale_x),o=Number(t?.plan_scale_y),a=Number.isFinite(s)&&s>0?s:1;return{dx:Number.isFinite(e)?e:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(n)&&n>0?n:a,sy:Number.isFinite(o)&&o>0?o:a,angle:Ar(t?.plan_angle)}}_openBackdropDialog(t){if(!this._bdMovable||!this._bdRect)return;t?.preventDefault(),t?.stopPropagation(),this._bdDrag=null;const e=this._bdRect;this._backdropDialog={widthCm:Er(e.w,this._cellCm,this._gridPitch),heightCm:Er(e.h,this._cellCm,this._gridPitch),angle:this._angleField(e.angle)}}_saveBackdropDialog(){const t=this._backdropDialog,e=this._bdBase,i=this._bdRect;if(!t||!e||!i)return;const s=Ar(t.angle),n=this._geometrySnapshot(),o=Math.min(e.w*bh,Math.max(e.w*yh,ri(Fr(t.widthCm,this._cellCm,this._gridPitch),this._gridPitch))),a=Math.min(e.h*bh,Math.max(e.h*yh,ri(Fr(t.heightCm,this._cellCm,this._gridPitch),this._gridPitch))),r=zr({x:i.x+i.w/2-o/2,y:i.y+i.h/2-a/2},s,t=>this._snap(t));this._bdApply((r[0]-e.x)/Pf,(r[1]-e.y)/Pf,o/e.w,a/e.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(t,e,i,s,n){const o=this._curSpaceCfg;if(!o)return;o.plan_x=Number(Ih(t).toFixed(6)),o.plan_y=Number(Ih(e).toFixed(6)),delete o.plan_scale,o.plan_scale_x=Number(Math.min(bh,Math.max(yh,i)).toFixed(6)),o.plan_scale_y=Number(Math.min(bh,Math.max(yh,s)).toFixed(6));const a=Ar(n);a?o.plan_angle=Number(a.toFixed(2)):delete o.plan_angle,this._cfgEpoch++,this.requestUpdate()}_bdStart(t,e,i=!1){const s=this._bdBase,n=this._bdRect;if(!s||!n)return!1;const o=this._svgPoint(t),a=e?e[0]:0,r=e?e[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":e?"scale":"move",pid:t.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},Nf(t),!0}_bdMove(t){const e=this._bdDrag;if(!e)return;const i=this._svgPoint(t),s=e.base;if("move"===e.kind){const t=e.rect0.x,n=e.rect0.y,o=this._snap([t+(i[0]-e.sx),n+(i[1]-e.sy)]),a=Math.abs(o[0]-t)>1e-9||Math.abs(o[1]-n)>1e-9;if(!a&&!e.moved)return;return e.moved||=a,void this._bdApply((o[0]-s.x)/Pf,(o[1]-s.y)/Pf,e.p0.sx,e.p0.sy,e.p0.angle)}if("rotate"===e.kind){const s=e.rect0.x+e.rect0.w/2,n=e.rect0.y+e.rect0.h/2,o=180*Math.atan2(e.sy-n,e.sx-s)/Math.PI;let a=e.p0.angle+(180*Math.atan2(i[1]-n,i[0]-s)/Math.PI-o);t.shiftKey||(a=5*Math.round(a/5)),a=Ar(a);const r=Math.abs(a-e.p0.angle)>1e-9;if(!r&&!e.moved)return;return e.moved||=r,void this._bdApply(e.p0.dx,e.p0.dy,e.p0.sx,e.p0.sy,a)}const n=Br(e.rect0,e.sgx,e.sgy,i[0],i[1],!t.shiftKey,this._gridPitch,Math.min(s.w,s.h)*yh),o=n.w/Math.max(1e-9,s.w),a=n.h/Math.max(1e-9,s.h),r=Math.abs(o-e.p0.sx)>1e-9||Math.abs(a-e.p0.sy)>1e-9||Math.abs(n.x-e.rect0.x)>1e-9||Math.abs(n.y-e.rect0.y)>1e-9;if(!r&&!e.moved)return;e.moved||=r;const l=zr(n,e.p0.angle,t=>this._snap(t));this._bdApply((l[0]-s.x)/Pf,(l[1]-s.y)/Pf,o,a,e.p0.angle)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const t=this._bdParams;return 0!==t.dx||0!==t.dy||1!==t.sx||1!==t.sy||0!==t.angle}_bdReset(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot();delete t.plan_x,delete t.plan_y,delete t.plan_scale,delete t.plan_scale_x,delete t.plan_scale_y,delete t.plan_angle,this._bdDrag=null,this._recordGeometry(this._t("history.backdrop_transform"),e),this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate()}_bdUp(){const t=this._bdDrag;this._bdDrag=null,t?.moved&&(this._recordGeometry(this._t("history.backdrop_transform"),t.before),this._saveConfig()),this.requestUpdate()}get _bdLive(){if(!this._bdDrag)return null;const t=this._bdRect;return t?{x:t.x+t.w/2,y:t.y+t.h/2,text:`${this._fmtLen([0,0],[t.w,0])} × ${this._fmtLen([0,0],[0,t.h])}`}:null}_renderBackdropFrame(t){const e=this._bdRect;if(!this._bdActive||!e)return U;const i=.02*Math.max(t.w,t.h),s=i/4,n=e.x+e.w/2,o=e.y+e.h/2,a=Ar(e.angle),r=2.2*i;return j`<g class="bdframe" transform=${a?`rotate(${a} ${n} ${o})`:U}>
|
||
<rect class="bdbox" x="${e.x}" y="${e.y}" width="${e.w}" height="${e.h}"></rect>
|
||
<line class="dtstem" x1="${n}" y1="${e.y}" x2="${n}" y2="${e.y-r}"></line>
|
||
<circle class="bdhandle dtrot" cx="${n}" cy="${e.y-r}" r="${i.toFixed(1)}"
|
||
@pointerdown=${t=>{t.stopPropagation(),t.preventDefault(),this._bdStart(t,void 0,!0)}}></circle>
|
||
<circle class="bdknob" cx="${n}" cy="${e.y-r}" r="${s.toFixed(2)}"></circle>
|
||
${[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]].map(([t,n,o])=>{const a=t<0?e.x:e.x+e.w,r=n<0?e.y:e.y+e.h;return j`<circle
|
||
class="bdhandle bd-${o}" data-corner="${t+","+n}"
|
||
cx="${a}" cy="${r}" r="${i.toFixed(1)}"
|
||
@pointerdown=${e=>{e.stopPropagation(),e.preventDefault(),this._bdStart(e,[t,n])}}></circle><circle class="bdknob" cx="${a}" cy="${r}" r="${s.toFixed(2)}"></circle>`})}
|
||
</g>`}_renderTextFrame(t){const e=this._dtSel,i=this._dtBox;if(!e||!i||i.id!==e.id)return U;const s=.018*Math.max(t.w,t.h),n=s/4;if("line"===e.kind){const t=[e.x1*Pf,e.y1*this._decorH],i=[e.x2*Pf,e.y2*this._decorH];return j`<g class="dtframe dtlineframe">
|
||
<line class="dtbox" x1="${t[0]}" y1="${t[1]}" x2="${i[0]}" y2="${i[1]}"></line>
|
||
${[t,i].map((t,e)=>j`<circle class="dthandle dtendpoint" cx="${t[0]}" cy="${t[1]}"
|
||
r="${s.toFixed(1)}" @pointerdown=${t=>this._dtStart(t,"scale",void 0,e)}></circle>
|
||
<circle class="dtknob" cx="${t[0]}" cy="${t[1]}" r="${n.toFixed(2)}"></circle>`)}
|
||
</g>`}const[o,a]=this._dtPivot(e),r=Number(e.angle)||0,l=2.2*s;return j`<g class="dtframe" transform=${r?`rotate(${r} ${o} ${a})`:U}>
|
||
<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=${t=>this._dtStart(t,"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(([t,e,o])=>j`<circle class="dthandle dt-${o}"
|
||
cx="${t<0?i.x:i.x+i.w}" cy="${e<0?i.y:i.y+i.h}" r="${s.toFixed(1)}"
|
||
@pointerdown=${i=>this._dtStart(i,"scale",[t,e])}></circle><circle class="dtknob"
|
||
cx="${t<0?i.x:i.x+i.w}" cy="${e<0?i.y:i.y+i.h}" r="${n.toFixed(2)}"></circle>`)}
|
||
</g>`}_renderDecorLayer(){const t=Pf,e=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=t=>this._decorShapeDown(t,n),c=t=>this._decorShapeDbl(t,n);if("line"===n.kind)return j`<line class="${o}" data-hp="decor" data-id="${n.id}" data-kind="${n.kind}"
|
||
x1="${n.x1*t}" y1="${n.y1*e}" x2="${n.x2*t}" y2="${n.y2*e}"
|
||
stroke="${a.color}" stroke-opacity="${a.opacity}" stroke-width="${r}"
|
||
stroke-dasharray=${"dashed"===n.line_style?`${4*r} ${3*r}`:U}
|
||
stroke-linecap="round" stroke-linejoin="round"
|
||
@pointerdown=${l} @dblclick=${c}></line>
|
||
${i&&"select"===this._decorTool?j`<line class="dshape dselecthit"
|
||
data-hp="decor" data-id="${n.id}" data-kind="${n.kind}"
|
||
x1="${n.x1*t}" y1="${n.y1*e}" x2="${n.x2*t}" y2="${n.y2*e}"
|
||
@pointerdown=${l} @dblclick=${c}></line>`:U}
|
||
${s?j`<line class="dshape derasehit" data-hp="decor" data-id="${n.id}" data-kind="${n.kind}"
|
||
x1="${n.x1*t}" y1="${n.y1*e}" x2="${n.x2*t}" y2="${n.y2*e}"
|
||
@pointerdown=${l}></line>`:U}`;if("rect"===n.kind){const i=(n.x+n.w/2)*t,h=(n.y+n.h/2)*e,d=Ar(n.angle);return j`<rect class="${o}" data-hp="decor" data-id="${n.id}" data-kind="${n.kind}"
|
||
x="${n.x*t}" y="${n.y*e}" width="${n.w*t}" height="${n.h*e}"
|
||
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})`:U}
|
||
@pointerdown=${l} @dblclick=${c}></rect>
|
||
${s?j`<rect class="dshape derasehit" data-hp="decor" data-id="${n.id}" data-kind="${n.kind}"
|
||
x="${n.x*t}" y="${n.y*e}" width="${n.w*t}" height="${n.h*e}"
|
||
transform=${d?`rotate(${d} ${i} ${h})`:U} @pointerdown=${l}></rect>`:U}`}if("ellipse"===n.kind){const i=(n.x+n.w/2)*t,h=(n.y+n.h/2)*e,d=Ar(n.angle);return j`<ellipse class="${o}" data-hp="decor" data-id="${n.id}" data-kind="${n.kind}"
|
||
cx="${i}" cy="${h}"
|
||
rx="${n.w/2*t}" ry="${n.h/2*e}" 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})`:U}
|
||
@pointerdown=${l} @dblclick=${c}></ellipse>
|
||
${s?j`<ellipse class="dshape derasehit" data-hp="decor" data-id="${n.id}" data-kind="${n.kind}"
|
||
cx="${i}" cy="${h}" rx="${n.w/2*t}" ry="${n.h/2*e}"
|
||
transform=${d?`rotate(${d} ${i} ${h})`:U} @pointerdown=${l}></ellipse>`:U}`}if("furniture"===n.kind){const i=n.w*t,h=n.h*e,d=od(n.symbol,i,h);if(!d)return U;const p=Number(n.angle)||0,u=n.x*t+i/2,_=n.y*e+h/2,m=`${p?`rotate(${p} ${u} ${_}) `:""}translate(${n.x*t} ${n.y*e})`;return j`<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?j`<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>`:U}`}if("text"===n.kind){const i=this._decorTextUnits(n),s=this._renderDeviceSnapshot?.facts.get(`decor:${this._space}:${n.id}`),r=function(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}("string"==typeof s?s:ts(n.text,n,this._renderPlanHass,t=>this._renderEntityAvailable(t))),h=n.x*t,d=n.y*e,p=Number(n.angle)||0,u=d-(r.length-1)*i*1.2/2;return j`<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})`:U}
|
||
@pointerdown=${l} @dblclick=${c}>${r.map((t,e)=>j`<tspan x="${h}" y="${u+e*i*1.2}">${t}</tspan>`)}</text>`}return U});let o=U;const a=this._decorDraft;if(a){const t=this._decorStyle,e=Fr(t.widthCm,this._cellCm,this._gridPitch);if("line"===a.kind)o=j`<line class="ddraft" x1="${a.a[0]}" y1="${a.a[1]}" x2="${a.b[0]}" y2="${a.b[1]}"
|
||
stroke="${t.color}" stroke-opacity="${t.opacity}" stroke-width="${e}" 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?j`<rect class="ddraft" x="${i}" y="${s}" width="${n}" height="${r}" stroke="${t.color}"
|
||
stroke-opacity="${t.opacity}" stroke-width="${e}" fill="${t.fill?t.fillColor:"none"}" fill-opacity="${t.fill?t.fillOpacity:0}"></rect>`:j`<ellipse class="ddraft" cx="${i+n/2}" cy="${s+r/2}" rx="${n/2}" ry="${r/2}"
|
||
stroke="${t.color}" stroke-opacity="${t.opacity}" stroke-width="${e}" fill="${t.fill?t.fillColor:"none"}" fill-opacity="${t.fill?t.fillOpacity:0}"></ellipse>`}}return j`<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(t){return this._editorSecondary.renderGroupLauncher(t,this._editorToolbarGroups,this._editorSecondaryCopy)}get _editorSecondaryContextId(){const t=`editor:${this._mode}:${this._space}:${this._cfgEpoch}`,e=this._editorSecondary.activeGroup(this._editorToolbarGroups);if(e)return`${t}:group:${e.id}:${this._editorSecondary.groupGeneration}`;if("plan"===this._mode){const e=this._physicalSel;return e?`${t}:selection:${e.kind}:${e.id}:${e.segment??""}`:`${t}:tool:${this._tool}:${this._path.length}:${this._openWallAnchor?1:0}`}return"decor"===this._mode?"furniture"===this._decorTool?`${t}:palette:furniture:${this._furnPalette?.symbol||"none"}`:"select"===this._decorTool&&this._decorSel?`${t}:selection:decor:${this._decorSel}`:`${t}:tool:${this._decorTool}:${this._bdMoved?1:0}:${this._bdDrag?1:0}`:`${t}:none`}_runEditorContext(t,e){this._editorSecondary.runContext(t,this._editorSecondaryContextId,e)}_renderEditorGroupModel(t){return this._editorSecondary.renderGroupModel(t,this._editorSecondaryContextId,this._editorSecondaryCopy)}_renderDrawWallControl(){return W`<label class="drawwall ${null==this._drawWallCm?"invalid":""}">${this._t("wallthick.field")}
|
||
<input type="number" min=${_l(1,this._imperial)}
|
||
max=${_l(150,this._imperial)} step="any"
|
||
.value=${this._drawWallFieldValue}
|
||
@input=${t=>{this._drawWallField=t.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",{max:_l(this._drawWallMaxCm,this._imperial),unit:this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")})}</span>
|
||
</label>`}_renderPlanSecondary(){const t=this._editorSecondaryContextId,e=this._physicalSel;if(e){if(!("partition"===e.kind?!!this._curSpaceCfg.partitions?.some(t=>t.id===e.id):"column"===e.kind?!!this._curSpaceCfg.wall_columns?.some(t=>t.id===e.id):!!this._curSpaceCfg.room_drafts?.some(t=>t.id===e.id)))return null;const i="partition"===e.kind?this._t("markup.partition"):"column"===e.kind?this._t("markup.column"):this._t("markup.add");return{contextId:t,kind:"selection",ariaLabel:this._t("editor.context_actions",{object:i}),visibleLabel:i,content:W`
|
||
<button class="btn ghost" @click=${()=>this._runEditorContext(t,()=>{const t=this._physicalSel;t&&this._openPhysicalDialog(t.kind,t.id,t.segment)})}><ha-icon icon="mdi:tune"></ha-icon>${this._t("btn.properties")}</button>
|
||
<button class="btn danger" @click=${()=>this._runEditorContext(t,()=>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":"boundary"===this._tool?this._boundaryHintKey: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||"boundary"===this._tool&&!!this._openWallAnchor;return{contextId:t,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":"wallthick"===this._tool?"markup.wallthick":"markup.boundary")}),content:W`
|
||
${i?this._renderDrawWallControl():U}
|
||
${n?W`<span class="hint">${n}</span>`:U}
|
||
${s?W`<span class="hint" role=${"boundary"===this._tool?"status":U}
|
||
aria-live=${"boundary"===this._tool?"polite":U}>${this._t(s)}</span>`:U}
|
||
${"draw"===this._tool&&this._path.length?W`<button class="btn ghost" @click=${()=>this._runEditorContext(t,()=>this._cancelPath())}>
|
||
${this._t("btn.reset")}
|
||
</button>`:U}`}}_renderDecorSecondary(){const t=this._editorSecondaryContextId;if("furniture"===this._decorTool)return{contextId:t,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 e=this._dtSel;if(e){const i=this._t(`decor.${e.kind}`);return{contextId:t,kind:"selection",ariaLabel:this._t("editor.context_actions",{object:i}),visibleLabel:i,content:W`
|
||
<button class="btn ghost" @click=${()=>this._runEditorContext(t,()=>{const t=this._dtSel;t&&this._openDecorProperties(t)})}><ha-icon icon="mdi:tune"></ha-icon>${this._t("btn.properties")}</button>
|
||
<button class="btn danger" @click=${()=>this._runEditorContext(t,()=>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:t,kind:"tool",ariaLabel:this._t("editor.tool_options",{tool:this._t(i?`decor.${this._decorTool}`:"decor.backdrop")}),content:W`
|
||
${i?W`
|
||
<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=${t=>this._decorStyle={...this._decorStyle,...t.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=${t=>this._decorStyle={...this._decorStyle,widthCm:this._decorSmallCm(Number(t.target.value))}} />
|
||
<span class="opl">${this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}</span>
|
||
</label>
|
||
${s?W`<label class="dfill"><input type="checkbox" .checked=${this._decorStyle.fill}
|
||
@change=${t=>this._decorStyle={...this._decorStyle,fill:t.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=${t=>this._decorStyle={...this._decorStyle,fillColor:t.detail.color,fillOpacity:t.detail.opacity}}></hp-color-opacity>`:U}
|
||
`:U}
|
||
${n?W`<span class="bdhint">${this._t("decor.backdrop_hint")}</span>`:U}`}:null}_withBackdropReset(t){if(!this._bdMoved||this._bdDrag||"palette"===t?.kind)return t;const e=this._editorSecondaryContextId,i=W`<button class="btn bdreset" title=${this._t("decor.backdrop_reset")}
|
||
@click=${()=>this._runEditorContext(e,()=>this._bdReset())}>
|
||
<ha-icon icon="mdi:image-refresh-outline"></ha-icon>${this._t("decor.backdrop_reset")}
|
||
</button>`;return t?{...t,kind:"selection"===t.kind?"mixed":t.kind,content:W`${t.content}${i}`}:{contextId:e,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._infoCard||this._rulesDialog||this._settingsDialog||this._alignDialog||this._importDialog||this._kioskDialog||this._backupExportDialog||this._backupImportDialog||this._wallDialog)}_renderEditorSecondary(){if(!this._editing)return U;const t=this._editorSecondary.activeGroup(this._editorToolbarGroups),e=t?this._renderEditorGroupModel(t):"plan"===this._mode?this._renderPlanSecondary():"decor"===this._mode?this._withBackdropReset(this._renderDecorSecondary()):null;return this._editorSecondary.render(e,this._editorSecondaryDialogBlocked)}_renderDecorBar(){const t=[["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"]],e=this._geometryHistory.undoName,i=this._geometryHistory.redoName;return W`<div class="editbar decorbar">
|
||
<div class="editbar-tools" tabindex="-1" ?inert=${this._modeTransitionBusy}>
|
||
${t.map(([t,e,i])=>W`<button class="btn dtool ${this._decorTool===t?"on":""}"
|
||
data-editor-palette=${"furniture"===t?"furniture":U}
|
||
@click=${()=>{if("furniture"===t&&"furniture"===this._decorTool)return this._furnPalette=null,void(this._decorTool="select");"furniture"===t&&this._editorSecondary.openPalette(),this._decorTool=t,this._decorDraft=null,"furniture"!==t&&(this._furnPalette=null)}}
|
||
title=${this._t(i)}>
|
||
<ha-icon icon=${e}></ha-icon><span class="ml">${this._t(i)}</span>
|
||
</button>`)}
|
||
${this._editorToolbarGroups.map(t=>this._renderEditorGroupLauncher(t))}
|
||
<button class="btn ghost" @click=${this._undoGeometry} ?disabled=${!e}
|
||
title=${e?this._t("history.undo_named",{name:e}):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 t=this._decorEraseConfirm,e=this._t(`decor.${t.kind}`);return W`<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:e})}</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 t=this._decorTextDialog,e=(t.pickerEntity||"").trim(),i=e?this.hass?.states?.[e]:null;return W`<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=${t.text} autofocus
|
||
@input=${e=>{const i=e.target;this._decorRememberTextSelection(i),this._decorTextDialog={...t,text:i.value}}}
|
||
@click=${t=>this._decorRememberTextSelection(t.target)}
|
||
@keyup=${t=>this._decorRememberTextSelection(t.target)}
|
||
@select=${t=>this._decorRememberTextSelection(t.target)}
|
||
@blur=${t=>this._decorRememberTextSelection(t.target)}
|
||
@keydown=${t=>{t.stopPropagation(),"Enter"===t.key&&(t.ctrlKey||t.metaKey)&&this._decorSaveText()}}></textarea>
|
||
<hp-color-opacity .label=${this._t("decor.color")} .color=${t.color} .opacity=${t.opacity}
|
||
.opacityLabel=${this._t("space.opacity")} .pickerLabels=${this._colorPickerLabels}
|
||
@hp-color-opacity-change=${e=>this._decorTextDialog={...t,...e.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(pf)} step="0.1"
|
||
.value=${String(this._decorSmallField(t.sizeCm))}
|
||
@input=${e=>this._decorTextDialog={...t,sizeCm:this._decorTextCm(Number(e.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=${t.angle}
|
||
@input=${e=>this._decorTextDialog={...t,angle:e.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=${t.pickerEntity||""}
|
||
@input=${e=>this._decorTextDialog={...t,pickerEntity:e.target.value}} />
|
||
<datalist id="hp-dtext-ents">
|
||
${Object.keys(this.hass?.states||{}).map(t=>W`<option value=${t}></option>`)}
|
||
</datalist>
|
||
${e?W`
|
||
<label>${this._t("decor.live_attr")}</label>
|
||
<select id="decor-live-attribute" class="namein" .value=${""}
|
||
@change=${t=>{const e=t.target.value;e&&this._decorInsertLiveVariable("__state__"===e?null:e)}}>
|
||
<option value="">${this._t("decor.live_attr_ph")}</option>
|
||
<option value="__state__">${this._t("decor.live_state")}</option>
|
||
${Object.keys(i?.attributes||{}).filter(t=>!!Ji(e,t)).map(t=>W`<option value=${t}>${t}</option>`)}
|
||
</select>
|
||
`:U}
|
||
</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=${!t.text.trim()} @click=${()=>this._decorSaveText()}>${this._t("btn.save")}</button>
|
||
</div>
|
||
</hp-dialog>`}_renderDecorShapeDialog(){const t=this._decorShapeDialog,e="rect"===t.kind||"ellipse"===t.kind,i=this._t("decor."+t.kind),s=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return W`<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"===t.kind?W`
|
||
<label>${this._t("furn.symbol")}</label>
|
||
<select class="namein"
|
||
@change=${e=>this._decorShapeDialog={...t,symbol:e.target.value}}>
|
||
${Yh.map(e=>W`<optgroup label=${this._t(`furn.group_${e}`)}>
|
||
${Qh(e).map(e=>W`<option value=${e.id}
|
||
?selected=${e.id===t.symbol}>
|
||
${this._t(`furn.sym_${e.id}`)}
|
||
</option>`)}
|
||
</optgroup>`)}
|
||
</select>`:U}
|
||
<hp-color-opacity .label=${this._t("decor.color")} .color=${t.color} .opacity=${t.opacity}
|
||
.opacityLabel=${this._t("space.opacity")} .pickerLabels=${this._colorPickerLabels}
|
||
@hp-color-opacity-change=${e=>this._decorShapeDialog={...t,...e.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(t.widthCm))}
|
||
@input=${e=>this._decorShapeDialog={...t,widthCm:this._decorSmallCm(Number(e.target.value))}} /><span class="opl">${this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}</span></div>
|
||
${"line"===t.kind?W`
|
||
<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"!==t.lineStyle}
|
||
@change=${()=>this._decorShapeDialog={...t,lineStyle:"solid"}} />
|
||
<span>${this._t("decor.line_style_solid")}</span></label>
|
||
<label class="srcrow"><input type="radio" name="decor-line-style"
|
||
.checked=${"dashed"===t.lineStyle}
|
||
@change=${()=>this._decorShapeDialog={...t,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(t.lengthCm||0))}
|
||
@input=${e=>this._decorShapeDialog={...t,lengthCm:this._decorLargeCm(Number(e.target.value))}} />
|
||
<span class="opl">${s}</span></div>`:W`
|
||
<label>${this._t("decor.size")}</label>
|
||
<div class="colorrow"><input class="namein" type="number" min="0.01" step="0.01"
|
||
.value=${String(this._decorLargeField(t.sizeWCm||0))}
|
||
@input=${e=>this._decorShapeDialog={...t,sizeWCm:this._decorLargeCm(Number(e.target.value))}} />
|
||
<span>×</span><input class="namein" type="number" min="0.01" step="0.01"
|
||
.value=${String(this._decorLargeField(t.sizeHCm||0))}
|
||
@input=${e=>this._decorShapeDialog={...t,sizeHCm:this._decorLargeCm(Number(e.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=${t.angle}
|
||
@input=${e=>this._decorShapeDialog={...t,angle:e.target.value}} />
|
||
${e?W`<label class="dfill"><input type="checkbox" .checked=${!!t.fill}
|
||
@change=${e=>this._decorShapeDialog={...t,fill:e.target.checked}} />${this._t("decor.fill")}</label>
|
||
<hp-color-opacity .label=${this._t("decor.fill_color")}
|
||
.color=${t.fillColor||t.color} .opacity=${t.fillOpacity??.25}
|
||
.opacityLabel=${this._t("space.opacity")} .pickerLabels=${this._colorPickerLabels}
|
||
.disabled=${!t.fill}
|
||
@hp-color-opacity-change=${e=>this._decorShapeDialog={...t,fillColor:e.detail.color,fillOpacity:e.detail.opacity}}></hp-color-opacity>`:U}
|
||
</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 t=this._backdropDialog,e=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return W`<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(t.widthCm))}
|
||
@input=${e=>this._backdropDialog={...t,widthCm:this._decorLargeCm(Number(e.target.value))}} />
|
||
<span>×</span><input class="namein" type="number" min="0.01" step="0.01"
|
||
.value=${String(this._decorLargeField(t.heightCm))}
|
||
@input=${e=>this._backdropDialog={...t,heightCm:this._decorLargeCm(Number(e.target.value))}} />
|
||
<span class="opl">${e}</span></div>
|
||
<label>${this._t("decor.angle")}</label>
|
||
<input class="namein" type="number" min="-180" max="180" step="1" .value=${t.angle}
|
||
@input=${e=>this._backdropDialog={...t,angle:e.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(t){const e=this._stageEl,i=this._viewOr(this._baseVb());return e?.clientWidth&&e.clientHeight?Math.max(i.w/e.clientWidth,i.h/e.clientHeight)*t:this._gridPitch/8*t}get _boundaryCoarse(){return"touch"===this._boundaryPointerType||"pen"===this._boundaryPointerType}_boundaryTolerances(){return{hit:this._cssPxToRender(this._boundaryCoarse?22:12),cap:this._cssPxToRender(this._boundaryCoarse?10:6),ambiguity:this._cssPxToRender(6)}}_boundaryBlocked(t,e){const i=this._spaceModel();if(!i)return!1;const s=i=>ch(t,i)||i.some((s,n)=>{const o=i[(n+1)%i.length];return Hs(t,[s[0],s[1],o[0],o[1]])<=e});for(const t of i.wall_columns||[])if(s(Zc(t,this._cellCm,this._gridPitch)))return!0;for(const s of i.partitions||[]){const i=ml(s.cm,this._cellCm,this._gridPitch)/2;if(Hs(t,[s.a[0],s.a[1],s.b[0],s.b[1]])<=Math.max(e,i))return!0}for(const s of i.room_drafts||[])for(let i=0;i+1<s.points.length;i++){const n=s.points[i],o=s.points[i+1],a=ml(s.segments[i]?.cm||15,this._cellCm,this._gridPitch)/2;if(Hs(t,[n[0],n[1],o[0],o[1]])<=Math.max(e,a))return!0}return!1}_solidBoundaryPull(t,e=this._openCuts()){const i=this._boundaryTolerances(),s=this._spaceModel();if(!s)return i.hit;const n=dc(s.rooms,this._spaceWalls,e,t,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf);return Math.max(i.hit,n>0?ml(n,this._cellCm,this._gridPitch)/2:0)}_boundaryTargetAt(t){const e=this._viewOr(this._baseVb()),i=this._stageEl,s=[this._space,this._cfgEpoch,t[0],t[1],this._boundaryPointerType,e.x,e.y,e.w,e.h,i?.clientWidth||0,i?.clientHeight||0].join("|");if(this._boundaryTargetMemo?.key===s)return this._boundaryTargetMemo.value;const n=this._boundaryTolerances(),o=this._openCuts(),a=this._spaceModel();if(!a)return{kind:"none"};let r=function(t,e,i,s){const{openPull:n,openEndCap:o,solidPull:a,ambiguity:r,eps:l}=s,c=md(e,l),h=[];for(const e of i||[]){const i=yd(t,e,n,o);if(null==i)continue;const s=[(e[0]+e[2])/2,(e[1]+e[3])/2],a=c.find(t=>Hs(s,t.seg)<=4*l);h.push({value:e,seg:e,semantic:a?.pair||`open:${e.join(",")}`,distance:i})}const d=wd(h,r,l);if("ambiguous"===d)return{kind:"ambiguous",group:"open"};if(d)return{kind:"open",seg:d.value,distance:d.distance};const p=[];for(const e of c){const i=yd(t,e.seg,a(e.seg));null!=i&&p.push({value:e,seg:e.seg,semantic:e.pair,distance:i})}const u=wd(p,r,l);if("ambiguous"===u)return{kind:"ambiguous",group:"shared"};if(u)return{kind:"shared",a:u.value.a,b:u.value.b,edge:u.value.seg,distance:u.distance};const _=[];for(const i of e||[]){if(!i?.id)continue;const e=hi(i);if(e)for(let s=0;s<e.length;s++){const n=e[s],o=e[(s+1)%e.length],r=[n[0],n[1],o[0],o[1]],h=yd(t,r,a(r));if(null==h)continue;const d=pd(t,r).q;c.some(t=>Hs(d,t.seg)<=4*l)||_.push({value:{room:i,edge:r},seg:r,semantic:String(i.id),distance:h})}}const m=wd(_,r,l);return"ambiguous"===m?{kind:"ambiguous",group:"outer"}:m?{kind:"outer",...m.value,distance:m.distance}:{kind:"none"}}(t,a.rooms,o,{openPull:n.hit,openEndCap:n.cap,ambiguity:n.ambiguity,eps:.02*this._gridPitch,solidPull:t=>this._solidBoundaryPull(t,o)});return"shared"!==r.kind&&"open"!==r.kind||!this._boundaryBlocked(t,n.hit)||(r={kind:"blocked"}),this._boundaryTargetMemo={key:s,value:r},r}get _boundaryTarget(){return this._markup&&"boundary"===this._tool&&this._cursorPt?this._boundaryTargetAt(this._cursorPt):null}get _boundaryPreview(){const t=this._viewOr(this._baseVb()),e=this._stageEl,i=this._cursorPt,s=this._openWallAnchor,n=[this._markup,this._tool,this._space,this._cfgEpoch,this._boundaryPointerType,i?.[0]??"none",i?.[1]??"none",s?.p?.[0]??"none",s?.p?.[1]??"none",s?.edge?.join(",")??"none",s?.aId??"none",s?.bId??"none",t.x,t.y,t.w,t.h,e?.clientWidth||0,e?.clientHeight||0].join("|");if(this._boundaryPreviewMemo?.key===n)return this._boundaryPreviewMemo.value;const o=t=>(this._boundaryPreviewMemo={key:n,value:t},t);if(!this._markup||"boundary"!==this._tool)return o(null);if(s&&!i)return o({kind:"anchor",point:[...s.p]});if(!i)return o(null);const a=this._openCuts(),r=.02*this._gridPitch;if(s){const{p:t,edge:e}=s,n=ud(_d(i,e,xd(e,a,r),this._gridPitch,1.5*this._gridPitch),e),l=this._boundaryBlocked(i,this._boundaryTolerances().hit)||Hs(i,e)>this._solidBoundaryPull(e,a);return o({kind:"range",seg:[t[0],t[1],n[0],n[1]],invalid:l})}const l=this._boundaryTargetAt(i);if("shared"===l.kind){return o({kind:"anchor",point:_d(i,l.edge,xd(l.edge,a,r),this._gridPitch,1.5*this._gridPitch)})}if("open"===l.kind){const t=this._planClosedOpenSpan(l.seg),e=t?Xc([l.seg[0],l.seg[1]],[l.seg[2],l.seg[3]],t.cm,this._cellCm,this._gridPitch):null;return o({kind:"restore",seg:l.seg,body:e})}return"blocked"===l.kind||"ambiguous"===l.kind||"outer"===l.kind?o({kind:"invalid",point:i}):o(null)}get _boundaryHintKey(){if(this._openWallAnchor){const t=this._boundaryPreview;return"range"===t?.kind&&t.invalid?"markup.boundary_hint_retry":"markup.boundary_hint_second"}const t=this._boundaryTarget;return t&&"none"!==t.kind?"open"===t.kind?"markup.boundary_hint_restore":"shared"===t.kind?"markup.boundary_hint_open":"outer"===t.kind?"markup.boundary_hint_outer":"blocked"===t.kind?"markup.boundary_hint_blocked":"markup.boundary_hint_ambiguous":"markup.boundary_hint"}get _boundaryStageClass(){if(!this._markup||"boundary"!==this._tool)return"";if(this._openWallAnchor){const t=this._boundaryPreview;return"range"===t?.kind&&t.invalid?" boundary-invalid":" boundary-solid"}const t=this._boundaryTarget;return"open"===t?.kind?" boundary-open":"shared"===t?.kind?" boundary-solid":t&&"none"!==t.kind?" boundary-invalid":""}_renderOpenWalls(t){if(t&&!t.showBorders&&!this._editing)return j``;const e=this._openCuts(),i=this._boundaryPreview;if(!e.length&&!i)return j``;const s=(t,e=!1)=>e?j`<g class="boundary-point invalid">
|
||
<circle cx=${t[0]} cy=${t[1]} r=${this._cssPxToRender(5)}></circle>
|
||
<path d="M ${t[0]-this._cssPxToRender(4)} ${t[1]-this._cssPxToRender(4)}
|
||
L ${t[0]+this._cssPxToRender(4)} ${t[1]+this._cssPxToRender(4)}
|
||
M ${t[0]+this._cssPxToRender(4)} ${t[1]-this._cssPxToRender(4)}
|
||
L ${t[0]-this._cssPxToRender(4)} ${t[1]+this._cssPxToRender(4)}"></path>
|
||
</g>`:j`<circle class="boundary-point" cx=${t[0]} cy=${t[1]}
|
||
r=${this._cssPxToRender(5)}></circle>`;return j`<g class="openwalls" style="--ow-stroke:${t?.color||"var(--hp-muted)"}">
|
||
${e.map(t=>j`<line class="openwall"
|
||
x1="${t[0]}" y1="${t[1]}" x2="${t[2]}" y2="${t[3]}"></line>`)}
|
||
${"range"===i?.kind?j`<line class="openwall-preview boundary-range ${i.invalid?"invalid":""}"
|
||
x1="${i.seg[0]}" y1="${i.seg[1]}"
|
||
x2="${i.seg[2]}" y2="${i.seg[3]}"></line>
|
||
${s([i.seg[0],i.seg[1]])}
|
||
${s([i.seg[2],i.seg[3]],i.invalid)}`:U}
|
||
${"restore"===i?.kind&&i.body?j`<path class="openwall-preview boundary-restore"
|
||
d=${n=i.body,`M ${n.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`}></path>`:U}
|
||
${"anchor"===i?.kind?s(i.point):"invalid"===i?.kind?s(i.point,!0):U}
|
||
</g>`;var n}_openCuts(){const t=this._curSpaceCfg,e=this._spaceModel();return e?Fm(t,e,this._gridPitch,Pf):[]}_openPairs(){const t=this._openCuts();return Em(this._spaceModel()?.rooms||[],t,this._gridPitch)}_commitOpenSpans(t){const e=this._curSpaceCfg,i=this._spaceModel();if(!e||!i)return;const s=.02*this._gridPitch;this._cfgEpoch++;const n=i.rooms;let o=dd(e.open_spans);o.length&&t?.old.length?o=Sd(o,t.old,t.next,Pf):o.length||(o=fd(gd(n,null,Pf,s),Pf)),o=$d(o,n,Pf,s),this._persistOpenCuts(o.map(t=>cd(t,Pf)))}_persistOpenCuts(t){const e=this._curSpaceCfg,i=this._spaceModel();if(!e||!i)return;const s=.02*this._gridPitch,n=$d(fd(t,Pf),i.rooms,Pf,s),o=n.map(t=>cd(t,Pf));n.length?e.open_spans=n:delete e.open_spans,vd(e.rooms||[],i.rooms,o,s)}_planClosedOpenSpan(t){const e=this._curSpaceCfg,i=this._spaceModel();if(!e||!i)return null;const s=.02*this._gridPitch,n=this._openCuts();let o=Array.isArray(e.walls)?e.walls.slice():[];for(const t of lc(i.rooms,o,n,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf))!t.open&&t.cm>0&&(o=zl(o,t.a,t.b,t.cm,this._wallKeyPitch,Pf));const a=function(t,e,i){const s=[(e[0]+e[2])/2,(e[1]+e[3])/2];return t.filter(t=>{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return Math.hypot(e[0]-s[0],e[1]-s[1])>4*i})}(n,t,s);let r=this._normalizeWalls(o,a),l=dc(i.rooms,r,a,t,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf);if(!(l>0)){const e=[];for(const t of lc(i.rooms,r,a,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf))t.open||e.push([t.a[0],t.a[1],t.b[0],t.b[1]]);r=kd(r,t,e,this._wallKeyPitch,Pf,ll),r=this._normalizeWalls(r,a),l=dc(i.rooms,r,a,t,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf)}return{cuts:a,walls:r,cm:l>0?l:ll}}_closeOpenSpan(t){const e=this._curSpaceCfg;if(!e)return;const i=this._geometrySnapshot(),s=this._planClosedOpenSpan(t);s&&(s.walls.length?e.walls=s.walls:delete e.walls,this._persistOpenCuts(s.cuts),this._commitPhysicalGeometry(this._t("history.close_boundary"),i)&&this._showToast(this._t("toast.boundary_restored")),this.requestUpdate())}_boundaryClick(t){const e=this._boundaryRestoreGuard;if(e&&Date.now()>e.until&&(this._boundaryRestoreGuard=null),e&&Date.now()<=e.until&&Math.hypot(t[0]-e.point[0],t[1]-e.point[1])<=this._boundaryTolerances().hit)return;const i=.02*this._gridPitch,s=this._openCuts();if(this._openWallAnchor){const{p:e,edge:l}=this._openWallAnchor;if(Hs(t,l)>this._solidBoundaryPull(l,s))return void this._showToast(this._t("toast.boundary_same_edge"));if(this._boundaryBlocked(t,this._boundaryTolerances().hit))return void this._showToast(this._t("toast.boundary_blocked"));const c=ud(_d(t,l,xd(l,s,i),this._gridPitch,1.5*this._gridPitch),l),h=Math.hypot(c[0]-e[0],c[1]-e[1]);if(this._openWallAnchor=null,this._cursorPt=null,h<.5*this._gridPitch)return void this._showToast(this._t("toast.openwall_short"));const d=[e[0],e[1],c[0],c[1]],p=[...s,d],u=this._curSpaceCfg;if(!u)return;const _=this._geometrySnapshot(),m=this._normalizeWalls(u.walls,p);m.length?u.walls=m:delete u.walls;const g=(u.openings||[]).length;u.openings=(n=u.openings,o=d,a=Pf,r=6*this._gridPitch,n?.length?n.filter(t=>Hs([Number(t.x)*a,Number(t.y)*a],o)>r||!Tl([o[0],o[1]],[o[2],o[3]],Number(t.angle)||0)):n?n.slice():[]);const f=(u.openings||[]).length<g;return this._persistOpenCuts(p),this._commitPhysicalGeometry(this._t("history.open_boundary"),_)&&(f&&this._showToast(this._t("toast.openwall_openings_removed")),this._showToast(this._t("toast.boundary_opened"))),void this.requestUpdate()}var n,o,a,r;const l=this._boundaryTargetAt(t);if("open"===l.kind)return this._boundaryRestoreGuard={until:Date.now()+450,point:[...t]},this._cursorPt=null,void this._closeOpenSpan(l.seg);if("ambiguous"===l.kind)return void this._showToast(this._t("toast.boundary_ambiguous"));if("blocked"===l.kind)return void this._showToast(this._t("toast.boundary_blocked"));if("outer"===l.kind)return void this._showToast(this._t("toast.openwall_shared_only"));if("shared"!==l.kind)return void this._showToast(this._t("toast.openwall_pick"));const c=xd(l.edge,s,i),h=_d(t,l.edge,c,this._gridPitch,1.5*this._gridPitch);this._openWallAnchor={p:h,edge:l.edge,aId:l.a.id,bId:l.b.id},this.requestUpdate()}_deleteRoomClick(t){const e=this._spaceModel();if(!e)return;const i=[...e.rooms].reverse().find(e=>this._pointInRoom(t,e));i?i.id&&(this._roomDeleteDialog={roomId:i.id,name:i.name}):this._showToast(this._t("toast.delete_room_pick"))}get _wallKeyPitch(){return Dh}get _spaceWalls(){const t=this._curSpaceCfg?.walls;return Array.isArray(t)?t:[]}_cfgOpenCuts(){return dd(this._curSpaceCfg?.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_intervalCm(t){const e=this._spaceModel();return e?dc(e.rooms,this._spaceWalls,this._openCuts(),t,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf):0}_normalizeWalls(t,e){const i=this._spaceModel();if(!i)return[];return Pl(hc(i.rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf),this._curSpaceCfg?.rooms||[],Dh,1,e.map(t=>[t[0]/Pf,t[1]/Pf,t[2]/Pf,t[3]/Pf]))}_paperShapes(t){if(!this._spaceWalls.length)return di(t);const e=this._wallUnionGeometry();return e?.paperD?[{path:e.paperD}]:di(t)}_wallUnionGeometry(){const t=this._spaceModel();if(!t)return null;const e=this._spaceWalls,i=this._physicalBodiesR();if(!e.length&&!i.length)return null;const s=this._openPairs().flatMap(t=>t.segs),n=this._roomWallOpeningInputs(),o=`${this._space}|${this._cfgEpoch}|${t.rooms.length}`;return this._wallUnionCache&&this._wallUnionCache.key===o||(this._wallUnionCache={key:o,value:Ic(t.rooms,e,s,n,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf,i)}),this._wallUnionCache.value}_thickWallCuts(){const t=this._spaceModel();if(!t)return[];const e=this._spaceWalls;if(!e.length)return[];const i=this._openPairs().flatMap(t=>t.segs);return Ac(t.rooms,e,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_wallThickHit(t){const e=this._spaceModel();if(!e)return null;const i=6*this._gridPitch,s=this._openCuts();let n=null;for(const o of lc(e.rooms,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf)){const e=Hs(t,[o.a[0],o.a[1],o.b[0],o.b[1]]);e<=i&&(!n||e<n.d)&&(n={iv:o,d:e})}if(!n)return null;const o=n.iv;return{a:o.a,b:o.b,roomId:o.roomId,segs:[[o.a[0],o.a[1],o.b[0],o.b[1]]],open:o.open,cm:o.cm}}get _wallThickHover(){if(!this._markup||"wallthick"!==this._tool||!this._cursorPt||this._wallDialog)return null;const t=this._wallThickHit(this._cursorPt);if(!t)return null;const e=function(t,e,i){const s=Number.isFinite(t)&&t>0?t:0,n=Number.isFinite(e)&&e>0?e:5,o=Number.isFinite(i)&&i>0?i:0;return s>0?ml(s,n,o)/2:_h(1.5*o,n)}(t.cm,this._cellCm,this._gridPitch);let i="";for(const s of t.segs)i+=(i?" ":"")+ql([[s[0],s[1]],[s[2],s[3]]],e,!1);return{segs:t.segs,open:t.open,d:i}}_wallThickClick(t){const e=this._wallThickHit(t);if(!e)return void this._showToast(this._t("toast.wallthick_pick"));if(e.open)return void this._showToast(this._t("toast.wallthick_open"));const i=e.cm,s=this._viewOr(this._baseVb()),n=(e.a[0]+e.b[0])/2,o=(e.a[1]+e.b[1])/2;this._wallDialog={a:e.a,b:e.b,value:_l(i,this._imperial),roomId:e.roomId,sx:(n-s.x)/s.w*100,sy:(o-s.y)/s.h*100}}_wallThickApply(t){const e=this._wallDialog;if(!e)return;const i=this._curSpaceCfg,s=this._spaceModel();if(!i||!s)return;const n=e.value.trim(),o=n?mf(n):0;if(null==o)return void this._showPhysicalRange(100);const a=this._imperial?2.54*o:o;if(n&&(!Number.isFinite(a)||a<0||a>0&&a<1||a>100))return void this._showPhysicalRange(100);const r=this._geometrySnapshot(),l=n&&a>0?a:null,c=this._openCuts();let h;h=t&&e.roomId?Fl(i.walls,s.rooms,e.roomId,l,this._wallKeyPitch,c,Pf):zl(i.walls,e.a,e.b,l,this._wallKeyPitch,Pf),h=this._normalizeWalls(h,c),h.length?i.walls=h:delete i.walls,this._wallDialog=null,this._commitPhysicalGeometry(this._t("history.wall_thickness"),r)&&this._showToast(this._t(null==l?"toast.wallthick_cleared":"toast.wallthick_set")),this.requestUpdate()}_wallHatchDefs(t){if(!this._spaceWalls.length&&!this._physicalBodiesR().length&&!this._markup)return j``;const e=dl(this._cellCm);return j`<defs>
|
||
<pattern id="hp-wall-hatch" patternUnits="userSpaceOnUse"
|
||
width="${e}" height="${e}" patternTransform="rotate(45)">
|
||
<path d="M0 0 L0 ${e}" stroke="${t||"#607d8b"}" stroke-width="${e/8*2}"></path>
|
||
</pattern>
|
||
</defs>`}_resolvedRoomFills(t,e){const i=new Map,s=new Map;for(const n of t.rooms){const t=this._roomDialog&&n.id===this._roomEditId?this._roomFill||e.fill:zs(e.fill,n),o=this._roomDialog&&n.id===this._roomEditId?this._roomCustomFill||e.customFill:ls(e.customFill,n),a=_s(t,"lqi"===t&&n.area?this._roomLqi(n.area):null,"light"===t?uo(ho(this._renderPlanHass,this._renderDevices,n,this._virtualLights)):"none","temp"===t?this._roomTemp(n):null,e.tempMin,e.tempMax,this._fillColors,o);i.set(n,a),n.id&&s.set(n.id,a)}return{byRoom:i,byId:s}}_spaceDisplayForRender(){const t=cs(this._curSpaceCfg),e=this._spaceDialog;return e&&"edit"===e.mode&&e.spaceId===this._space?{...t,showBorders:e.showBorders,showNames:e.showNames,hideDecor:e.hideDecor,hideOpenings:e.hideOpenings,color:e.roomColor,opacity:e.roomOpacity,fill:e.fillMode,customFill:e.customFill?rs(e.customFill):as,glow:e.glowEnabled,tempMin:e.tempMin,tempMax:e.tempMax,showLqi:e.showLqi,cardFontScale:e.cardFontScale,labelTemp:e.labelTemp,labelHum:e.labelHum,labelLqi:e.labelLqi,labelLight:e.labelLight}:t}_openingWallIndexFor(t,e){const i=t.rooms.map(t=>`${t.id}:${t.poly?.map(t=>t.join(",")).join("/")||`${t.x},${t.y},${t.w},${t.h}`}`).join(";"),s=this._spaceWalls.map(t=>`${t.key}:${t.a?.join(",")||""}:${t.b?.join(",")||""}:${t.cm}`).join(";"),n=e.map(t=>t.join(",")).join(";"),o=[t.id,this._cfgEpoch,this._wallKeyPitch,this._cellCm,this._gridPitch,i,s,n].join("|");let a=this._openingWallIndexCache.get(o);return a||(a=Fc(t.rooms,this._spaceWalls,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf)),ff(this._openingWallIndexCache,o,a,4),{key:o,value:a}}_renderOpeningTunnelFills(t,e,i="data"){if(this._markup||!this._spaceWalls.length||!this._openingsR.length)return j``;if("glow-base"===i&&![...e.byRoom.values()].some(Boolean))return j``;const s=this._openPairs().flatMap(t=>t.segs),n=this._openingsR.map(t=>({x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),o=n.map(t=>`${t.x},${t.y},${t.angle},${t.length}`).join(";"),a=this._openingWallIndexFor(t,s),r=`${a.key}|${o}`;return this._openingTunnelCache&&this._openingTunnelCache.key===r||(this._openingTunnelCache={key:r,value:qc(a.value,n)}),v_({openings:this._openingsR,geometries:this._openingTunnelCache.value,fillsByRoomId:e.byId,idPrefix:`${t.id}-${i}`,groupClass:"data"===i?"opening-tunnels":"opening-tunnels glow-base-tunnels",dataLayer:i})}_resolvedGlowBase(t,e,i){const s=new Map,n=new Map,o=this._fillColors.glow_base;for(const a of t.rooms){const t=i.byRoom.get(a),r=Fs(e.glow,a)&&(!t||t.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(t,e){if(this._markup||![...e.byRoom.values()].some(Boolean))return j``;const i=new Map(t.rooms.map(t=>[t,hi(t)])),s=this._openPairs().flatMap(t=>t.segs),n=this._wallUnionGeometry()?.roomGeom,o=t=>"M "+t.map(t=>t[0]+" "+t[1]).join(" L ")+" Z",a=t.rooms.map(a=>{const r=e.byRoom.get(a)||null,l=i.get(a)||null;if(!r||!l)return U;const c=this._spaceWalls.length&&a.id&&_c(t.rooms,a.id,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf,n)||l,h=Pi(c,t.rooms.filter(t=>t!==a).map(t=>i.get(t)).filter(t=>!!t)),d=this._cleanFloor(a,c,t).path;return d||h.length?j`<path class="glow-base" data-room-id=${a.id||U}
|
||
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?j`<polygon class="glow-base" data-room-id=${a.id||U}
|
||
points="${c.map(t=>t.join(",")).join(" ")}"
|
||
fill=${r.color} fill-opacity=${r.opacity} pointer-events="none"></polygon>`:j`<rect class="glow-base" data-room-id=${a.id||U}
|
||
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 j`<g class="glow-base-layer" aria-hidden="true" pointer-events="none">${a}</g>`}_renderWallBodies(t){if("iso"===this._renderProjection)return j``;if(t&&!t.showBorders&&("view"===this._mode||"devices"===this._mode))return j``;const e=this._wallUnionGeometry();if(!e)return j``;const i=this._stageEl,s=this._viewOr(this._baseVb()),n=i&&i.clientWidth&&s.w?i.clientWidth/s.w:1,o=t?.color||"#607d8b",a=hl(e.depthUnits,n)||pl(dl(this._cellCm),n),r=this._fillColors.wall_fill;return j`<g class="wallbodies" style="--room-stroke:${o};--wall-fill:${r.c};--wall-fill-op:${r.a}">
|
||
${e.paths.map(t=>j`
|
||
<path class="wallbody-fill" data-component=${t.id} d="${t.d}"
|
||
fill="${r.c}" fill-opacity="${r.a}" fill-rule=${t.fillRule}
|
||
stroke="none" pointer-events="none"></path>
|
||
<path class="wallbody ${a?"solid":""}"
|
||
data-hp="wall" data-id="union" data-kind="union" data-component=${t.id}
|
||
d="${t.d}" fill="${a?"none":"url(#hp-wall-hatch)"}"
|
||
fill-rule=${t.fillRule}
|
||
stroke="${o}" stroke-width="${_h(.6,this._cellCm)}"
|
||
pointer-events="none"></path>`)}
|
||
</g>`}_isoDecorationLayers(t){const e="undefined"==typeof CSS||"function"!=typeof CSS.supports||CSS.supports("filter","blur(1px)"),i="function"==typeof matchMedia&&matchMedia("(forced-colors: active)").matches;return function(t){const e=!!t.showBorders;return{structural:e,panels:e&&!t.hideOpenings,shadows:e&&t.filtersSupported&&!t.forcedColors,materialNuance:e&&!t.forcedColors,floorSymbols:!t.hideOpenings&&!e}}({showBorders:t.showBorders,hideOpenings:t.hideOpenings,filtersSupported:e,forcedColors:i})}_isoOpeningPanels(t){if(!t.panels)return[];const e=this._isoScene();if(!e)return[];return e.openings.flatMap(t=>{const e=this._openingsR[t.sourceIndex];return e?Og(t,this._openingAmt(e)):[]}).sort((t,e)=>t.depth-e.depth||t.sourceIndex-e.sourceIndex||t.leaf-e.leaf)}_renderIsoDefs(t,e){const i=uh(this._cellCm);return j`<defs>
|
||
${"walls"===e&&t.materialNuance?j`
|
||
<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>`:U}
|
||
${"underlay"===e&&t.shadows?j`
|
||
<filter id="hp-iso-ambient-shadow" x="-12%" y="-12%" width="124%" height="130%">
|
||
<feGaussianBlur stdDeviation="${7*i}"></feGaussianBlur>
|
||
</filter>`:U}
|
||
${"shadows"===e&&t.shadows?j`
|
||
<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>`:U}
|
||
</defs>`}_renderIsoUnderlay(t){if(!t.structural)return j``;const e=this._isoScene()?.floor;return e?j`<g class="iso-underlay" data-hp="iso-underlay" aria-hidden="true" pointer-events="none">
|
||
${this._renderIsoDefs(t,"underlay")}
|
||
${t.shadows&&e.footprintPath?j`<path class="iso-ambient-shadow" d=${e.footprintPath}
|
||
transform="translate(0 ${_h(8,this._cellCm)})"></path>`:U}
|
||
<g class="iso-floor-edge">${e.sides.map(t=>j`<path class="iso-floor-side" d=${t.d} data-component=${t.component}
|
||
data-edge=${t.edge}></path>`)}</g>
|
||
</g>`:j``}_renderIsoShadows(t,e){if(!t.shadows)return j``;const i=this._isoScene()?.geometry;return i?j`<g class="iso-shadows" data-hp="iso-shadows" aria-hidden="true" pointer-events="none">
|
||
${this._renderIsoDefs(t,"shadows")}
|
||
<path class="iso-contact-shadow" d=${i.contactPath}></path>
|
||
<g class="iso-leaf-shadows">${e.map(t=>j`<path class="iso-leaf-shadow" d=${t.shadowD}
|
||
data-id=${t.id} data-leaf=${t.leaf}></path>`)}</g>
|
||
</g>`:j``}_renderIsoWalls(t,e){if("iso"!==this._renderProjection)return j``;if(!t.structural)return j``;const i=this._isoScene();return i?j`<g class="iso-walls" data-hp="iso-walls" data-fingerprint=${i.key}>
|
||
${this._renderIsoDefs(t,"walls")}
|
||
<g class="iso-wall-sides">${i.geometry.sides.map(t=>j`<path class="iso-wall-side" d=${t.d} data-edge=${t.edge}></path>`)}</g>
|
||
<path class="iso-wall-top" d=${i.geometry.topPath} fill-rule="evenodd"></path>
|
||
${t.panels?j`<g class="iso-openings" data-hp="iso-openings"
|
||
aria-hidden="true" pointer-events="none">${e.map(t=>j`<path class="iso-opening-panel iso-${t.type}" d=${t.d}
|
||
data-id=${t.id} data-kind=${t.type} data-leaf=${t.leaf}></path>`)}</g>`:U}
|
||
</g>`:j``}_roomHoverPaths(t){const e=this._hoverRoom;if("view"!==this._mode||!e||e.space!==t.id)return null;const i=t.rooms.find(t=>t===e.room||!!t.id&&t.id===e.room.id);if(!i)return null;const s=hi(i);if(!s)return null;const n=t.rooms.filter(t=>t!==i).map(t=>({room:t,poly:hi(t)})).filter(t=>!!t.poly),o=Pi(s,n.map(t=>t.poly)),a=this._openPairs(),r=a.flatMap(t=>t.segs),l=i.id?a.filter(t=>t.a.id===i.id||t.b.id===i.id).flatMap(t=>t.segs):a.flatMap(t=>t.segs),c=this._spaceWalls,h=this._wallUnionGeometry()?.roomGeom,d=c.length&&i.id&&_c(t.rooms,i.id,c,r,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf,h)||s,p=[{axis:s,face:d}];for(const e of o){const i=n.find(t=>t.poly===e)?.room;let s=e;if(c.length&&i?.id){const n=rc(t.rooms,i.id,c,r,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf);n&&(s=zc(n.poly,n.offsets)||e)}p.push({axis:e,face:s})}const u=this._roomWallOpeningInputs(this._openingsR,t).map(t=>{const e=t.angle*Math.PI/180,i=Math.cos(e)*t.length/2,s=Math.sin(e)*t.length/2;return[t.x-i,t.y-s,t.x+i,t.y+s]}),_=.02*this._gridPitch,m=l.concat(u),g=p.map(({axis:t,face:e})=>{const i=m.map(i=>((t,e,i)=>{const s=t[2]-t[0],n=t[3]-t[1],o=Math.hypot(s,n);if(o<_)return null;const a=s/o,r=n/o,l=(t[0]+t[2])/2,c=(t[1]+t[3])/2;let h=!1;for(let t=0;t<e.length;t++){const i=e[t],s=e[(t+1)%e.length],n=s[0]-i[0],o=s[1]-i[1],d=Math.hypot(n,o);if(!(d<_||Math.abs(a*(o/d)-r*(n/d))>.05)&&Hs([l,c],[i[0],i[1],s[0],s[1]])<=4*_){h=!0;break}}if(!h)return null;let d=null;for(let t=0;t<i.length;t++){const e=i[t],s=i[(t+1)%i.length],n=s[0]-e[0],o=s[1]-e[1],h=Math.hypot(n,o);if(h<_||Math.abs(a*(o/h)-r*(n/h))>.05)continue;const p=Hs([l,c],[e[0],e[1],s[0],s[1]]);(!d||p<d.d)&&(d={a:e,b:s,d:p})}if(!d)return null;const p=d.b[0]-d.a[0],u=d.b[1]-d.a[1],m=Math.hypot(p,u)||1,g=-u/m,f=p/m,v=(d.a[0]-l)*g+(d.a[1]-c)*f;return[t[0]+g*v,t[1]+f*v,t[2]+g*v,t[3]+f*v]})(i,t,e)).filter(t=>!!t);return i.length?Ps(e,i,_).map(t=>`M ${t[0]} ${t[1]} L ${t[2]} ${t[3]}`).join(" "):`M ${e.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`}).filter(Boolean).join(" ");if(!g)return null;const f=t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`;return{fillD:[this._cleanFloor(i,d,t).path||f(d),...o.map(f)].join(" "),outlineD:g}}_renderRoomHoverFill(t){return t?j`<g class="room-hover room-hover-fill-layer" pointer-events="none">
|
||
<path class="room-hover-fill" d="${t.fillD}" fill-rule="evenodd"></path>
|
||
</g>`:j``}_renderRoomHoverOutline(t){return t?j`<g class="room-hover room-hover-outline-layer" pointer-events="none">
|
||
<path class="room-hover-halo" d="${t.outlineD}"></path>
|
||
<path class="room-hover-outline" d="${t.outlineD}"></path>
|
||
</g>`:j``}_renderWallThickUi(){const t=this._wallThickHover;return t&&t.d?j`<path class="wallthick-hover ${t.open?"isopen":""}"
|
||
d="${t.d}"></path>`:j``}_renderWallThickDialog(){const t=this._wallDialog;return t?W`<div class="wallthick-dlg" style="left:${t.sx.toFixed(2)}%;top:${t.sy.toFixed(2)}%"
|
||
@click=${t=>t.stopPropagation()}>
|
||
<div class="row">
|
||
<label>${this._t("wallthick.field")}</label>
|
||
<input type="number" min="0" max="100" step="any" .value=${t.value}
|
||
@input=${e=>{this._wallDialog={...t,value:e.target.value}}}
|
||
@keydown=${t=>{"Enter"===t.key&&(t.preventDefault(),this._wallThickApply(!1))}} />
|
||
<span class="opl">${this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}</span>
|
||
</div>
|
||
<div class="row">
|
||
<button class="btn ghost" @click=${()=>this._wallThickApply(!0)}>
|
||
${this._t("wallthick.apply_room")}
|
||
</button>
|
||
<span class="spacer"></span>
|
||
<button class="btn on" @click=${()=>this._wallThickApply(!1)}>
|
||
<ha-icon icon="mdi:check"></ha-icon>
|
||
</button>
|
||
</div>
|
||
</div>`:W``}_openingAt(t){if(!this._openingsR.length)return null;const e=this._openingsR.flatMap(e=>{const i=e.angle*Math.PI/180,s=t[0]-e.rx,n=t[1]-e.ry,o=s*Math.cos(i)+n*Math.sin(i);return Math.abs(o)>e.rlen/2+_h(12,this._cellCm)?[]:[{o:e,localY:-s*Math.sin(i)+n*Math.cos(i)}]});if(!e.length)return null;const i=this._spaceModel();if(!i)return null;const s=this._openPairs().flatMap(t=>t.segs),n=this._openingWallIndexFor(i,s).value;return e.find(({o:t,localY:e})=>{const i="gate"===t.type?!t.flip_v:t.flip_v,s=t.partitionHost||this._spaceWalls.length||"gate"===t.type?this._openingFace(t,n,!!i):{ox:0,oy:0,cm:0,side:-1},o=Xu({type:t.type,length:t.rlen,angle:t.angle,amount:this._openingAmt(t),flipH:!!t.flip_h,flipV:!!t.flip_v,cellCm:this._cellCm,gridPitch:this._gridPitch,face:s});return Math.abs(e)<=o.hitHalf})?.o||null}_resolveOpeningPlacement(t){const e=this._openingPreset;if("opening"!==this._tool||!e)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:[...lc(i.rooms,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf),...Bp(i.partitions,this._cellCm,this._gridPitch)]}),this._openingDimensionContextCache&&this._openingDimensionContextCache.key===o||(this._openingDimensionContextCache={key:o,value:Qg({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:Pf,epsilon:2e-4*this._gridPitch})});const a=jg({pointer:[t[0],t[1]],preset:e,geometryRevision:this._cfgEpoch,renderedLength:this._cmToUnits(e.lengthCm),intervals:this._openingPlacementIntervalsCache.value,baseTolerance:1.5*this._gridPitch,bodyPointerPadding:this._cssPxToRender(this._boundaryCoarse?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 t=Ep({id:"preview",type:r.type,x:r.x/Pf,y:r.y/Pf,angle:r.angle,length:r.renderedLength/Pf,host:r.host},i.partitions,Pf,this._cellCm,this._gridPitch).resolved;c=t?Lp(t,l):{ox:0,oy:0,cm:0,side:-1}}else c=Hc(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=rf(r,this._openingDimensionContextCache.value).map(t=>({x:t.label[0],y:t.label[1],text:li(t.distance/this._gridPitch*this._cellCm,h),dimension:t}));return{...r,face:c,measure:{labels:d,guide:r.measure.guide}}}_activateOpeningPlacement(t){this._activateMarkupTool("opening"),"opening"===this._tool&&(this._openingPreset=function(t,e){return{type:t,lengthCm:Lg(t),flipH:!1,flipV:!1,revision:e}}(t,++this._openingPresetRevision),this._openingHoverCandidate=null,this._cursorPt=null)}_clearOpeningPlacement(t){this._openingHoverCandidate=null,this._openingJambBlockCm=null,t&&(this._openingPreset=null,this._openingRebindId=null)}_openingClick(t){const e=this._spaceModel();if(!e)return;const i=this._openingRebindId?null:this._openingAt(t);if(i)return void this._editOpening(i);const s=this._openingPreset;if(!s)return;const n=this._openingHoverCandidate,o=n&&Gg(n,[t[0],t[1]],s.revision,this._cfgEpoch)?n:this._resolveOpeningPlacement(t);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(t,e.rooms,i);return s&&function(t,e,i,s,n){for(const o of s)if(!(Hs([t,e],o)>n)&&Tl([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_virtual")):void this._showToast(this._t("toast.opening_no_wall"))}const a=this._openingRebindId?this._curSpaceCfg?.openings?.find(t=>t.id===this._openingRebindId):null;this._openingDialog={...a?{id:a.id,type:a.type,lengthCm:Math.round(a.length*Pf/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(t){this._openingDialog={id:t.id,type:t.type,lengthCm:Math.round(t.rlen/this._gridPitch*this._cellCm),lengthTouched:!1,contact:t.contact||"",lock:t.lock||"",invert:!!t.invert,flipH:!!t.flip_h,flipV:!!t.flip_v,...t.host?{host:{...t.host}}:{},x:t.rx,y:t.ry,angle:t.angle}}_opPointerDown(t,e){if("plan"===this._mode&&this._spaceModel()&&"resize"!==this._tool){t.preventDefault(),t.stopPropagation();try{Nf(t)}catch{}this._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;if(Math.abs(t.clientX-this._opDrag.sx)+Math.abs(t.clientY-this._opDrag.sy)<=3)return;const i=this._spaceModel();if(!i)return;const s=this._svgPoint(t),n=this._curSpaceCfg,o=n?.openings?.find(t=>t.id===e.id);if(!o)return;if("partition"===o.host?.kind){const t=i.partitions.find(t=>t.id===o.host.id);if(!t)return;const e=t.b[0]-t.a[0],n=t.b[1]-t.a[1],a=Math.hypot(e,n);if(!(a>1e-9))return;const r=e/a,l=n/a;if(Math.abs((s[0]-t.a[0])*l-(s[1]-t.a[1])*r)>4*this._gridPitch)return;const c=o.length*Pf/2+zp(t,this._cellCm,this._gridPitch);let h=(s[0]-t.a[0])*r+(s[1]-t.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=(t.a[0]+r*h)/Pf,u=(t.a[1]+l*h)/this._spaceH;let _=180*Math.atan2(n,e)/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*Pf);this._opMeasure=r.measure;const l=r.x/Pf,c=r.y/this._spaceH,h=o.x!==l||o.y!==c||o.angle!==a.angle;h&&(this._opDrag.dirty=!0),o.x=l,o.y=c,o.angle=a.angle,h&&this._cfgEpoch++,this.requestUpdate()}_opRuler(t,e){const i=this._spaceModel()?.rooms||[],s=this._gridPitch/2;let n=t.x,o=t.y,a=_i([n,o],t.angle,e,i,s);if(a&&a.centered&&(n!==a.wallCenter[0]||o!==a.wallCenter[1]))[n,o]=a.wallCenter,a=_i([n,o],t.angle,e,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(e/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],t.angle,e,i,s)||a}}if(!a)return{x:n,y:o,angle:t.angle,measure:null};const r="mi"===this.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:li(t/this._gridPitch*this._cellCm,r)});return{x:n,y:o,angle:t.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:t.angle}:null}}}_opPointerUp(t,e){if(!this._opDrag||this._opDrag.id!==e.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(t,e){"plan"===this._mode&&"resize"===this._tool||(t.stopPropagation(),this._opDrag?.moved||"plan"===this._mode&&this._editOpening(e))}_saveOpening(){const t=this._openingDialog,e=this._curSpaceCfg,i=this._spaceModel();if(!t||!e||!i)return;const s=this._geometrySnapshot(),n=this._spaceH,o=(e.openings||[]).find(e=>e.id===t.id),a={...o||{},id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Pf,y:t.y/n,angle:t.angle,length:o&&!t.lengthTouched?o.length:this._cmToUnits(Math.max(20,t.lengthCm))/Pf,...t.host?{host:{...t.host}}:{}};if(a.host){const t=Op(o,a)?Np(a,i.partitions,Pf,this._cellCm,this._gridPitch):Ep(a,i.partitions,Pf,this._cellCm,this._gridPitch);if(!t.resolved){const e=i.partitions.find(t=>t.id===a.host.id);if("does-not-fit-jamb"===t.reason&&e){const t=zp(e,this._cellCm,this._gridPitch);this._showToast(this._t("opening.partition_jamb_margin",{distance:li(t/this._gridPitch*this._cellCm,this._imperial)}))}else this._showToast(this._t("opening.partition_orphan"));return}const s=(e.openings||[]).flatMap(t=>{if(!t.host||t.id===a.id)return[];const e=Ep(t,i.partitions,Pf,this._cellCm,this._gridPitch).resolved;return e?[e]:[]});if(function(t,e,i=1e-9){const s=t.length/(2*t.axis.length),n=t.t-s,o=t.t+s;return e.some(e=>{if(e.host.id!==t.host.id||e.opening.id===t.opening.id)return!1;const s=e.length/(2*e.axis.length);return Math.max(n,e.t-s)<Math.min(o,e.t+s)-i})}(t.resolved,s))return void this._showToast(this._t("toast.opening_no_wall"));Object.assign(a,Wp(a,t.resolved,Pf))}else delete a.host;"passage"===t.type?(delete a.contact,delete a.lock,delete a.invert,delete a.flip_h,delete a.flip_v):(a.contact=t.contact||null,a.lock=("door"===t.type||"gate"===t.type)&&t.lock||null,a.invert=t.invert||void 0,a.flip_h="gate"!==t.type&&t.flipH||void 0,a.flip_v=t.flipV||void 0),e.openings=e.openings||[];const r=e.openings.findIndex(t=>t.id===a.id);r>=0?e.openings[r]=a:e.openings.push(a),this._commitPhysicalGeometry(this._t(t.id?"history.edit_opening":"history.add_opening"),s),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t?.id||!e?.openings)return;const i=this._geometrySnapshot();e.openings=e.openings.filter(e=>e.id!==t.id),this._commitPhysicalGeometry(this._t("history.delete_opening"),i),this._openingDialog=null,this.requestUpdate()}_contactCandidates(){const t=[];for(const e of Object.keys(this.hass.states)){if(!this._openingEntityAvailable(e))continue;const i=e.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[e],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&t.push([e,s?.attributes?.friendly_name||e,n?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.hass.states).filter(t=>t.startsWith("lock.")&&this._openingEntityAvailable(t)).map(t=>({value:t,label:this.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_toggleOpeningEntityPicker(t){const e=this._openingDialog;if(!e)return;const i="contact"===t?!e.contactOpen:!e.lockOpen;this._openingDialog={...e,contactOpen:"contact"===t&&i,lockOpen:"lock"===t&&i}}_filterOpeningEntities(t,e){const i=this._openingDialog;i&&(this._openingDialog="contact"===t?{...i,contactFilter:e}:{...i,lockFilter:e})}_selectOpeningEntity(t,e){const i=this._openingDialog;i&&(this._openingDialog="contact"===t?{...i,contact:e,contactOpen:!1,contactFilter:""}:{...i,lock:e,lockOpen:!1,lockFilter:""})}_mergeClick(t){const e=this._spaceModel();if(!e)return;const i=e.rooms,s=[...i].reverse().find(e=>this._pointInRoom(t,e));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(t=>t.id===this._mergeSel),a=o?hi(o):null,r=hi(s),l=a&&r?function(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=si(Ri(t),Ri(e));if(1!==i.length)return null;if(1!==i[0].length)return null;const s=i[0][0].slice(0,-1).map(t=>[t[0],t[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 t=this._mergeDialog,e=this._curSpaceCfg;if(!t||!e||!this._spaceModel())return;const i=this._geometrySnapshot(),s=this._spaceH,n="a"===t.pick?t.aId:t.bId,o="a"===t.pick?t.bId:t.aId,a=e.rooms.find(t=>t.id===n);if(!a)return void(this._mergeDialog=null);a.poly=t.poly.map(t=>[t[0]/Pf,t[1]/s]),delete a.x,delete a.y,delete a.w,delete a.h,e.rooms=e.rooms.filter(t=>t.id!==o),this._commitOpenSpans();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(t){const e=this._spaceModel();if(!e)return;const i=e.rooms;if(!this._splitSel){const e=[...i].reverse().find(e=>this._pointInRoom(t,e));if(!e?.id)return;return void(this._splitSel={roomId:e.id,pts:[]})}const s=i.find(t=>t.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(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;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=r*r+l*l;let h=c?((t[0]-o[0])*r+(t[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(t[0]-d[0],t[1]-d[1]);p<s&&(s=p,i=d)}return i}(t,n),l=r&&function(t,e,i){let s=null,n=1/0;for(let o=0;o<e.length;o++){const[a,r]=e[o],[l,c]=e[(o+1)%e.length],h=l-a,d=c-r,p=h*h+d*d;if(!p)continue;let u=((t[0]-a)*h+(t[1]-r)*d)/p;u=Math.max(0,Math.min(1,u));const _=Math.hypot(t[0]-(a+u*h),t[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]-t[0],r[1]-t[1])<=a?l:null,h=!!c&&bi(c,n,o),d=this._splitSel.pts;if(!d.length)return h?void(this._splitSel={...this._splitSel,pts:[c]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const e=this._snap(t);return wi(e,n,o)?void(this._splitSel={...this._splitSel,pts:[...d,e]}):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(t){if(!this._markup)return;const e=t.pointerType;if(e&&(this._boundaryPointerType=e),"column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this._tool||"boundary"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(t));const i="draw"===this._tool&&!this._contourClosed,s="split"===this._tool&&!!this._splitSel?.pts?.length;if(!i&&!s)return;const n=this._svgPoint(t);if(i){const e=this._resolvePlanDrawPoint(n,t.shiftKey);return this._planSnapHover={contextKey:e.contextKey,candidate:e.candidate,conflicts:e.conflicts},this._syncPlanSnapConflictMarkers(e.conflicts),this._path.length?void(this._cursorPt=e.point):void this._syncPlanSnapActiveMarker(e.candidate)}this._cursorPt=this._snap(n)}get _openingPreview(){const t=this._openingPreset;if("opening"!==this._tool||!t||!this._cursorPt)return null;const e=this._cursorPt,i=this._openingHoverCandidate;if(i&&Gg(i,[e[0],e[1]],t.revision,this._cfgEpoch))return i;if(this._openingAt(e))return this._openingHoverCandidate=null,null;const s=this._resolveOpeningPlacement(e);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(t){const e=this._wallFaceBatch;if(!e)return;const i=e.candidates[e.index];if(!i)return;if(!t&&(i.existing||i.repair)&&1===e.candidates.length)return this._wallFaceBatch=null,this._roomDialog=!1,this._nameSel="",this._areaSel="",void this.requestUpdate();const s=t?{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=[...e.decisions,s],o=e.index+1;if(o<e.candidates.length)return this._wallFaceBatch={...e,decisions:n,index:o},this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),void this.requestUpdate();this._wallFaceBatch={...e,decisions:n},this._applyWallFaceBatch()}_wallSourceCmAt(t,e,i){const s=t=>"number"==typeof t&&Number.isFinite(t)&&t>=0?t:null,n=.02*this._gridPitch,o=this._spaceModel();if(!o)return ll;for(const e of this._planSnapGeometrySnapshot().value.segments){if("room"===e.sourceKind||Hs(t,[e.a[0],e.a[1],e.b[0],e.b[1]])>n)continue;if("partition"===e.sourceKind)return s(o.partitions.find(t=>t.id===e.sourceId)?.cm)??ll;const i=e.sourceId.lastIndexOf(":"),a=i>=0?e.sourceId.slice(0,i):e.sourceId,r=i>=0?Number(e.sourceId.slice(i+1)):-1;return s(o.room_drafts.find(t=>t.id===a)?.segments[r]?.cm)??ll}const a=lu(Math.max(0,e.length-1),i,this._drawWallCm,ll);for(let i=0;i+1<e.length;i++){const s=e[i],o=e[i+1];if(Hs(t,[s[0],s[1],o[0],o[1]])<=n)return a[i]??ll}return ll}_activePathWithRepair(t,e){const i=t.map(t=>[...t]);if(!e?.sourceKey.startsWith("active:"))return i;const s=/:(\d+)$/.exec(e.sourceKey),n=s?Number(s[1])+("b"===e.endpoint?1:0):-1;return n>=0&&n<i.length&&(i[n]=[...e.to]),i}_validateWallRepair(t,e){const i=2e-4*this._gridPitch,s=this._wallGraphSources(e),n=s.find(e=>e.key===t.sourceKey),o=s.find(e=>e.key===t.targetSourceKey);if(!n||!o)return!1;const a="a"===t.endpoint?n.a:n.b;return!(Math.hypot(a[0]-t.from[0],a[1]-t.from[1])>i)&&(!(Hs(t.to,[o.a[0],o.a[1],o.b[0],o.b[1]])>i)&&!function(t,e){if(!t.sourceKey.startsWith("static:partition|"))return!1;const i=t.sourceKey.slice(17).split("|")[0];return!!i&&e.some(t=>"partition"===t.host?.kind&&t.host.id===i)}(t,this._curSpaceCfg?.openings||[]))}_applyWallRepair(t,e){const i=this._curSpaceCfg;if(!i||!this._validateWallRepair(t,e.activePath))return!1;const s=e=>{e[0]=t.to[0]/Pf,e[1]=t.to[1]/Pf};if(t.sourceKey.startsWith("active:")){const n=/:(\d+)$/.exec(t.sourceKey),o=n?Number(n[1])+("b"===t.endpoint?1:0):-1;if(o<0||o>=e.activePath.length)return!1;e.activePath[o]=[...t.to];const a=(i.room_drafts||[]).find(t=>t.id===e.activeDraftId);return a?.points?.[o]&&s(a.points[o]),!0}if(!t.sourceKey.startsWith("static:"))return!1;const[n,o]=t.sourceKey.slice(7).split("|");if("partition"===n){const e=(i.partitions||[]).find(t=>t.id===o);if(!e)return!1;const n=[e.a[0]*Pf,e.a[1]*Pf],a=[e.b[0]*Pf,e.b[1]*Pf],r=Math.hypot(n[0]-t.from[0],n[1]-t.from[1])<=2e-4*this._gridPitch?e.a:Math.hypot(a[0]-t.from[0],a[1]-t.from[1])<=2e-4*this._gridPitch?e.b:null;return!!r&&(s(r),!0)}if("draft"===n){const e=o.lastIndexOf(":"),n=e>=0?o.slice(0,e):"",a=e>=0?Number(o.slice(e+1)):-1,r=(i.room_drafts||[]).find(t=>t.id===n);if(!r?.points?.[a]||!r?.points?.[a+1])return!1;const l=[a,a+1].find(e=>Math.hypot(r.points[e][0]*Pf-t.from[0],r.points[e][1]*Pf-t.from[1])<=2e-4*this._gridPitch);return null!=l&&(s(r.points[l]),!0)}return!1}_applyWallFaceBatch(){const t=this._wallFaceBatch,e=this._curSpaceCfg,i=this._spaceModel();if(!t||!e||!i)return;const s=(t,e)=>{this._showToast(this._t(t,e)),this._roomDialogCancel()},n=t.decisions.filter(t=>t.create),o=n.map(t=>t.candidate.ring),a=i.rooms;for(let t=0;t<o.length;t++){if(this._contourSelfIntersects(o[t])||Ti(o[t])<=1e-6)return void s("toast.contour_cannot_close");for(let e=t+1;e<o.length;e++)if(Di(o[t],o[e]))return void s("toast.contour_cannot_close");const e=n[t].candidate.split?.roomId,i=a.find(i=>i.id!==e&&!!hi(i)&&Di(o[t],hi(i)));if(i)return void s("toast.room_overlap",{name:i.name||""})}if((e.rooms||[]).length+n.length>400)return void s("toast.physical_limit");if(n.some(t=>t.candidate.split&&!e.rooms.some(e=>e.id===t.candidate.split.roomId)))return void s("toast.contour_cannot_close");const r=n.map(t=>t.candidate.repair).filter(t=>!!t);if(r.length>1||r[0]&&!this._validateWallRepair(r[0],t.activePath))return void s("toast.wall_repair_changed");const l=this._activePathWithRepair(t.activePath,r[0]),c=2e-4*this._gridPitch,h=new Map;for(let e=0;e<t.activeCms.length;e++)h.set(this._activeWallSourceKey(e),t.activeCms[e]);const d=[];if(n.length){const t=new Set(n.flatMap(t=>t.candidate.atomKeys)),e=n.some(t=>t.candidate.consumeAllActive);for(const i of Tu(this._wallGraphSources(l),c)){const s=i.sourceKeys.find(t=>h.has(t));!s||e||t.has(i.key)||d.push({a:i.a,b:i.b,cm:h.get(s)})}}else d.push(...cu(l,lu(l.length-1,t.activeCms,this._drawWallCm,ll)));if((e.partitions||[]).length+d.length>Af)return void s("toast.physical_limit");const p=this._geometrySnapshot();if(r[0]&&!this._applyWallRepair(r[0],t))return void s("toast.wall_repair_changed");const u=n.some(t=>!!t.candidate.split),_=u?cc(i.rooms,e.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,Pf):null,m=u&&Array.isArray(e.walls)?e.walls:null;for(const t of n){const i=t.candidate.split;if(!i)continue;const n=e.rooms.find(t=>t.id===i.roomId);if(!n)return void s("toast.contour_cannot_close");n.poly=i.mainPoly.map(t=>[t[0]/Pf,t[1]/this._spaceH]),delete n.x,delete n.y,delete n.w,delete n.h}const g=Date.now().toString(36),f=[];if(n.forEach((t,i)=>{const s=t.area?this.hass.areas[t.area]?.name:"",n={id:`r${g}-${i}`,name:t.name||s||this._t("room.default_name"),area:t.area||null,poly:t.candidate.ring.map(t=>[t[0]/Pf,t[1]/this._spaceH]),...t.settings?{settings:JSON.parse(JSON.stringify(t.settings))}:{}};e.rooms.push(n),f.push({room:n,decision:t})}),t.activeDraftId&&Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(e=>e.id!==t.activeDraftId),e.room_drafts.length||delete e.room_drafts),d.length&&(e.partitions||=[],d.forEach((t,i)=>e.partitions.push({id:`partition-${g}-${i}`,a:[t.a[0]/Pf,t.a[1]/Pf],b:[t.b[0]/Pf,t.b[1]/Pf],cm:t.cm}))),u){this._commitOpenSpans();const t=this._normalizeWalls(_,this._openCuts());t.length?e.walls=t:m?.length?e.walls=m:delete e.walls}if(f.length){this._cfgEpoch++;const i=this._openCuts(),s=this._spaceModel();if(!s)return;let n=e.walls;for(const{room:e,decision:o}of f){const a=o.candidate.ring,r=this._wallSourceCmAt(a[0],t.activePath,t.activeCms);n=El(n,s.rooms,e.id,r,this._wallKeyPitch,i,Pf);for(const o of lc(s.rooms,n,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf)){if(o.roomId!==e.id||"outer"!==o.kind)continue;const i=[(o.a[0]+o.b[0])/2,(o.a[1]+o.b[1])/2];n=zl(n,o.a,o.b,this._wallSourceCmAt(i,t.activePath,t.activeCms),this._wallKeyPitch,Pf)}}const o=this._normalizeWalls(n,i);o.length?e.walls=o:delete e.walls}this._commitPhysicalGeometry(this._t("history.wall_face_batch"),p),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 v=new Set(n.map(t=>t.area).filter(t=>!!t));if(v.size){const t={...this._layout};for(const e of this._devices){if(!v.has(e.area||"")||e.space!==this._space)continue;if(this._layout[e.id])continue;const i=this._defPos[e.id];i&&(t[e.id]={s:this._space,x:i.x/Pf,y:i.y/Pf},this._dirtyPos.add(e.id))}this._layout=t,this._persistLayout()}this._showToast(this._t(n.length?"toast.wall_rooms_saved":"toast.wall_chain_saved",{n:n.length}))}_commitRoom(){const t=this._curSpaceCfg,e=this._spaceModel();if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,n=!!this._pendingSplit,o=n?cc(e.rooms,t.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,Pf):null,a=n&&Array.isArray(t.walls)?t.walls:null;let r;if(this._pendingSplit){const e=t.rooms.find(t=>t.id===this._pendingSplit.roomId);if(!e)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);e.poly=this._pendingSplit.mainPoly.map(t=>[t[0]/Pf,t[1]/s]),delete e.x,delete e.y,delete e.w,delete e.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(t=>[t[0]/Pf,t[1]/s]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(c),!n&&this._activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==this._activeDraftId),t.room_drafts.length||delete t.room_drafts),n){this._commitOpenSpans();const e=this._normalizeWalls(o,this._openCuts());e.length?t.walls=e:a?.length?t.walls=a:delete t.walls}if(!n){const e=lu(r.length,[...this._draftSegmentCms,this._closingWallCm??void 0],this._drawWallCm,ll),i=e[0];if(null!=i){this._cfgEpoch++;const s=this._openCuts(),n=this._spaceModel();if(!n)return;let o=El(t.walls,n.rooms,c.id,i,this._wallKeyPitch,s,Pf);for(const t of lc(n.rooms,o,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf)){if(t.roomId!==c.id||"outer"!==t.kind)continue;const i=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],s=r.findIndex((t,e)=>{const s=r[(e+1)%r.length];return Hs(i,[t[0],t[1],s[0],s[1]])<=.02*this._gridPitch});s>=0&&(o=zl(o,t.a,t.b,e[s],this._wallKeyPitch,Pf))}o=this._normalizeWalls(o,s),o.length?t.walls=o:delete t.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 t=Pf,e={...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&&(e[i.id]={s:this._space,x:s.x/Pf,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const u=this._model.find(t=>t.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._openWallAnchor=null,this._boundaryRestoreGuard=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const t=this._resumeDraftBySpace[this._space];if(!t)return;const e=this._spaceModel()?.room_drafts.find(e=>e.id===t);e?(this._activeDraftId=t,this._path=e.points.map(t=>[...t]),this._draftSegmentCms=this._adoptDraftCms(this._path,e.segments.map(t=>t.cm),e.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 t=this._wallFaceBatch;return this._wallFaceBatch=null,this._path=t.activePath.map(t=>[...t]),this._draftSegmentCms=[...t.activeCms],this._activeDraftId=t.activeDraftId,this._nameSel="",this._areaSel="",void this.requestUpdate()}if(this._pendingSplit)return this._pendingSplit=null,void(this._splitSel=null);this._undoPoint()}get _freeAreas(){const t=new Set;for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area&&t.add(i.area);for(const e of this._wallFaceBatch?.decisions||[])e.create&&e.area&&t.add(e.area);return Object.values(this.hass?.areas||{}).filter(e=>!t.has(e.area_id)).sort((t,e)=>(t.name||"").localeCompare(e.name||""))}_openMarkerDialog(t){if(this._vacAllCamerasFor=null,this._vacAllCameraCache=null,t&&this._ackNewDevice(t.id),this._norm)if(t){const e=t.marker,i=Object.prototype.hasOwnProperty.call(e||{},"is_light"),s=Object.prototype.hasOwnProperty.call(e||{},"light_entity"),n=Object.prototype.hasOwnProperty.call(e||{},"glow_color"),o=Object.prototype.hasOwnProperty.call(e||{},"value_badge"),a=Object.prototype.hasOwnProperty.call(e||{},"tap_action"),r=Object.prototype.hasOwnProperty.call(e||{},"toggle_entity"),l=ks(e?.glow_color),c=this._devicePresentation(t,!0).valueBadge,h=Wo(this._planHass,t,this._devices),d=jo(this._planHass,t,h);this._markerDialog={devId:t.id,name:t.name,binding:"virtual"===t.bindingKind?"virtual":t.bindingKind+":"+t.bindingRef,bindingMode:"virtual"===t.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===t.bindingKind&&!!this._fullRegistryHass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:Bi(t.marker?.display),rippleColor:Rt(t.marker?.ripple_color,""),rippleSize:Number(t.marker?.ripple_size)>0?Number(t.marker.ripple_size):1.5,size:Number(t.marker?.size)>0?Number(t.marker.size):1,angle:Number(t.marker?.angle)||0,tapAction:$p(t.marker?.tap_action,t.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:a,originalTapAction:t.marker?.tap_action,tapHintAnnouncement:"",toggleEntity:e?.toggle_entity||"",toggleEntityTouched:!1,originalHasToggleEntity:r,originalToggleEntity:e?.toggle_entity,tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",controls:Zn(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",lightRole:!0===e?.is_light?"always":!1===e?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:i,originalIsLight:e?.is_light,lightEntity:e?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:s,originalLightEntity:e?.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===e?.glow_color),originalGlowColor:l||(null===e?.glow_color?null:void 0),valueBadgeEnabled:o?!0===e?.value_badge?.enabled:!!c,valueBadgeSource:e?.value_badge?.source||c?.source||d,valueBadgePosition:e?.value_badge?.position||c?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:o,originalValueBadge:e?.value_badge,useClimateTemp:!0===t.marker?.use_climate_temp,glowRadius:Number(t.marker?.glow_radius_cm)>0?String(this._imperial?Math.round(Number(t.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(t.marker.glow_radius_cm))/100):"",model:t.model||"",link:t.link||"",description:t.description||"",pdfs:[...t.pdfs||[]],room:t.marker?.room_id?t.space+"#@"+t.marker.room_id:t.space&&t.area?t.space+"#"+t.area:"",hideFromPlan:!0===t.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 t=[];for(const e of Ui)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(e+".")&&this._planEntityAvailable(i)&&t.push({value:i,label:s?.attributes?.friendly_name||i,sub:this._t("run."+e)});return t.sort((t,e)=>t.sub.localeCompare(e.sub)||t.label.localeCompare(e.label))}_bindingCandidates(){const t=this._planHass,e=wo(this._markers),i=new Set(this._markers.filter(t=>t.removed).map(t=>t.binding)),s=new Set;for(const t of this._devices)t.id!==this._markerDialog?.devId&&("device"===t.bindingKind&&t.bindingRef&&s.add("device:"+t.bindingRef),"entity"===t.bindingKind&&t.bindingRef&&s.add("entity:"+t.bindingRef));const n=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&n.add(t.name.trim()+"|"+(t.area||""));const o=[];for(const e of Object.values(t.devices)){if("service"===e.entry_type)continue;const t="device:"+e.id;if(s.has(t))continue;const a=(e.name_by_user||e.name||e.id).trim();t!==this._markerDialog?.binding&&!i.has(t)&&n.has(a+"|"+(e.area_id||""))||o.push({value:t,label:a,sub:(e.model||this._t("marker.sub_device"))+("Group"===e.model?this._t("marker.sub_z2m_group"):"")})}const a=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[n,r]of Object.entries(t.entities)){const l="entity:"+n;if(s.has(l))continue;if(ko(t,n,e)&&!i.has(l))continue;const c=a.has(r.platform),h="group"===r.platform;if(!c&&!h)continue;if(r.hidden&&!i.has(l))continue;const d=t.states[n];o.push({value:l,label:r.name||d?.attributes?.friendly_name||n,sub:n.split(".")[0]+" · "+("group"===r.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const n=new Set(o.map(t=>t.value));for(const[a,r]of Object.entries(t.entities)){const l="entity:"+a;if(s.has(l)||n.has(l)||r.hidden&&!i.has(l))continue;const c=!!r.device_id&&e.devices.has(r.device_id);if(ko(t,a,e)&&!i.has(l)&&!c)continue;const h=t.states[a],d=r.name||h?.attributes?.friendly_name||a,p=r.device_id?t.devices[r.device_id]:null,u=p&&(p.name_by_user||p.name)||"";o.push({value:l,label:d,sub:a.split(".")[0]+" · "+this._t("marker.sub_entity")+(u?" · "+u:"")})}}const r=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=r?o.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(r)):o;return l.sort((t,e)=>t.label.localeCompare(e.label)),l.slice(0,200)}_physicalBodiesR(t=this._spaceModel()){if(!t)return[];const e=`${t.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===e)return this._physicalBodiesCache.all;const i=th(t,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(t));return this._physicalBodiesCache={key:e,...i},i.all}_rawPhysicalBodiesR(t=this._spaceModel()){if(!t)return[];this._physicalBodiesR(t);const e=this._physicalBodiesCache;return e?[...e.drafts,...e.partitions,...e.columns]:[]}_cleanFloor(t,e,i=this._spaceModel()){if(!i)return{floor:e,geom:null,path:"",area:ah([[[...e,e[0]]]])};const s=t.id||`#${i.rooms.indexOf(t)}`,n=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._rszPreview){const t=gf(this._cleanFloorCache,n);if(t.hit)return t.value}const o=e.map(t=>t[0]),a=e.map(t=>t[1]),r=[Math.min(...o),Math.min(...a),Math.max(...o),Math.max(...a)],l=this._physicalBodiesR(i).filter(t=>{const e=t.map(t=>t[0]),i=t.map(t=>t[1]);return Math.max(...e)>=r[0]&&Math.min(...e)<=r[2]&&Math.max(...i)>=r[1]&&Math.min(...i)<=r[3]}),c=l.length?oh(e,l):null,h={floor:e,geom:c,path:c?Jc(c):"",area:ah(c||[[[...e,e[0]]]])};return this._rszPreview||ff(this._cleanFloorCache,n,h,600),h}_autoIconForBinding(t){if("virtual"===t)return"mdi:map-marker";const[e,i]=t.split(":");if(!i)return"";const s=this._fullRegistryHass,n=this._bindingStatus(t),o="active"===n.kind?n.enabledEntityIds:n.allEntityIds;if("device"===e){const t=s.devices?.[i];if(!t)return"mdi:help-circle";const e=o;return e.some(t=>t.startsWith("lock."))?"mdi:lock":bo(s,t.name_by_user||t.name||"",t.model,e,this._iconRules)}if("entity"===e){const t=s.entities?.[i],e=this.hass.states?.[i],n=t?.name||e?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":bo(s,n,"",[i],this._iconRules)}return""}_allRoomsFlat(){const t=[];for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area?t.push({value:e.id+"#"+i.area,label:(e.title||e.id)+" · "+i.name}):i.id&&t.push({value:e.id+"#@"+i.id,label:(e.title||e.id)+" · "+i.name+" · "+this._t("marker.subarea")});return t}_errText(t){if(!t)return this._t("err.unknown");if("string"==typeof t)return t;if("invalid_passage_fields"===t.code){const e=String(t.message||t.error||"").match(/space=([^;]*);\s*opening=([^;]*);\s*fields=([^;]*)/);if(e){const t=this._serverCfg?.spaces?.find(t=>String(t.id)===e[1]),i={contact:"opening.contact_label",lock:"opening.lock_label",invert:"opening.invert",flip_h:"opening.flip_h",flip_v:"opening.flip_v"},s=e[3].split(",").filter(Boolean).map(t=>i[t]?this._t(i[t]):t).join(", ");return this._t("opening.invalid_passage_fields",{room:t?.title||e[1],fields:s})}}if("invalid_partition_opening_jamb_margin"===t.code){const e=String(t.message||t.error||"").match(/margin_cm=([^;]*)/),i=e?Number(e[1]):NaN;if(Number.isFinite(i))return this._t("opening.partition_jamb_margin",{distance:li(i,this._imperial)})}if(t.message)return t.message;if(t.error)return t.error;if(null!=t.code)return this._t("err.code",{code:t.code});try{return JSON.stringify(t)}catch{return String(t)}}_backupErrorText(t){const e=t?.code??t?.error;if("string"==typeof e){const t=`backup.error.${e}`,i=this._t(t);if(i!==t)return i}return this._errText(t)}async _pickMarkerFiles(t){const e=t.target,i=e.files?[...e.files]:[];if(e.value="",!i.length||!this._markerDialog)return;const s=this._markerDialog.uploadId||this._markerDialog.devId||"new",n=[];for(const t of i)try{const e=new FormData;e.append("marker_id",s),e.append("file",t,t.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,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 t={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(t[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||t.name,url:o.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}_markerLightFields(t){const e={};if(t.lightRoleTouched?"always"===t.lightRole?e.is_light=!0:"never"===t.lightRole&&(e.is_light=!1):t.originalHasIsLight&&(e.is_light=t.originalIsLight??null),t.lightEntityTouched?t.lightEntity&&(e.light_entity=t.lightEntity):t.originalHasLightEntity&&(e.light_entity=t.originalLightEntity??null),t.glowTouched){if("auto"!==t.glowMode){const i={c:Rt(t.glowColor,this._fillColors.glow_light.c)};"fixed"===t.glowMode&&(i.bri=Math.max(.01,Math.min(1,Math.round(t.glowBrightness)/100))),e.glow_color=i}}else t.originalHasGlowColor&&(e.glow_color=t.originalGlowColor??null);return e}_markerTapActionFields(t){return t.tapActionTouched?{tap_action:t.tapAction||null}:t.originalHasTapAction?{tap_action:t.originalTapAction??null}:{}}_markerToggleEntityFields(t){return(e={touched:t.toggleEntityTouched,originalHas:t.originalHasToggleEntity,original:t.originalToggleEntity,value:t.toggleEntity}).touched?e.value?{toggle_entity:e.value}:{}:e.originalHas?{toggle_entity:e.original??null}:{};var e}async _saveMarker(){const t=this._markerDialog;if(!t||t.busy)return;const e=this._effectiveMarkerTapAction(t);if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return;if("virtual"===t.binding&&!t.name.trim())return void this._showToast(this._t("toast.virtual_name_required"));if("run"===e&&!t.tapTarget)return void this._showToast(this._t("toast.run_target_required"));if(t.valueBadgeTouched&&t.valueBadgeEnabled&&!t.valueBadgeSource)return void this._showToast(this._t("toast.value_badge_source_required"));if("ha"===t.bindingMode){const e=this._bindingStatus(t.binding),i=t.devId?this._markers.find(e=>e.id===t.devId):null,s=!i||i.binding!==t.binding;if("active"!==e.kind&&s)return void this._showToast(this._t("ha_disabled"===e.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===e.kind&&!0===i?.hidden&&!t.hideFromPlan)return this._markerDialog={...t,hideFromPlan:!0},void this._showToast(this._t("entity"===e.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}const i=this._serverCfg;if(!i)return;const s=i.markers||[],n=qs(t.room);let o=n?.space||null;const a=n?.area||null,r=n?.roomId||null,l=Fi(t.binding,t.devId,()=>"v_"+Date.now().toString(36)),c=t.devId,h=c?this._devices.find(t=>t.id===c):null,d=n?.space||h?.space||null,p=d?this._spaceModelById(d):this._spaceModel();if(!p)return;const u=p.id;"virtual"!==t.binding||o||(o=u),this._markerDialog={...t,busy:!0};try{const n="virtual"===t.binding?[]:s.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),d=n.length>0,_=Zn(t.binding,t.controls,this._bindingEntities(t.binding)),m=s.find(t=>t.id===l||t.id===c)?.vacuum||null,g={id:l,vacuum:m,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&1.5!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle?t.angle:null,...this._markerTapActionFields(t),...this._markerToggleEntityFields(t),tap_target:"run"===e&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:_.length?_:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=mf(t.glowRadius);return null==e||e<=0?null:Math.round(this._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:!!t.hideFromPlan};("virtual"===t.binding||t.room)&&(g.space=o,g.area=a,g.room_id=r);const f=h?.marker?.room_id??null,v=!!t.room&&null!=h&&(h.space!==o||h.area!==a||f!==r);let y=!1;const b=t.uploadId||c;if(b&&b!==l&&g.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:b,to_id:l}),e=t?.mapping||{};g.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const n="/files/"+e+"/",o="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(n))return t;const e=t.url.split(n)[1]||"",[i,a]=[e.split("?")[0],e.includes("?")?"?"+e.split("?")[1]:""];if(s){const e=s[decodeURIComponent(i)]??s[i];return e?{...t,url:t.url.split(n+i)[0]+o+encodeURIComponent(e)+a}:t}return{...t,url:t.url.split(n).join(o)}})}(g.pdfs,b,l,e),y=Object.keys(e).length>0}catch(t){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(t)}))}i.markers=s,c&&c!==l&&(i.markers=function(t,e,i){if(!e||!i||e===i)return[...t];const s=`marker:${e}`,n=`marker:${i}`;return t.map(t=>{const e=Array.isArray(t.controls)?t.controls.map(t=>t===s?n:t):t.controls,i="derived_marker_state"===t.value_badge?.source?.kind&&t.value_badge.source.ref===s?{...t.value_badge,source:{...t.value_badge.source,ref:n}}:t.value_badge;return e===t.controls&&i===t.value_badge?t:{...t,controls:e,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(t=>t.id!==l&&t.id!==c&&("virtual"===g.binding||t.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 t=p.vb[0]+p.vb[2]/2,e=p.vb[1]+p.vb[3]/2;const i=r?p.rooms.find(t=>t.id===r):a?p.rooms.find(t=>t.area===a):void 0;i&&([t,e]=this._roomCenter(i)),w=this._normPos(u,t,e),this._layout={...this._layout,[l]:w}}if(await this._saveConfigNow(),w){const t=ol(w);this._layout={...this._layout,[l]:t},this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:l,pos:t}))}const $=new Set(n);c&&c!==l&&$.add(c),$.delete(l);for(const t of $)delete this._layout[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{});y&&b&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:b}).catch(()=>{}),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_saved"))}catch(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}async _deleteMarker(){const t=this._markerDialog;if(!t||t.busy||!t.devId)return;const e=t.devId?this._devices.find(e=>e.id===t.devId):null;if(!e)return;const i=t.name||this._t("device.fallback");if(!confirm(this._t("confirm.remove_marker",{name:i})))return;const s=this._serverCfg;s.markers=s.markers||[];const n=s.markers,o="virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"";if(!o)return;const a=function(t,e,i,s){const n=new Set([e]),o=t.filter(t=>{const o=t.id===e||!s&&t.binding===i;return o&&n.add(t.id),!o});return{markers:s?o:[...o,{id:e,binding:i,removed:!0,hidden:!0}],cleanupIds:n}}(s.markers,e.id,o,"virtual"===e.bindingKind);var r,l;s.markers=(r=a.markers,l=a.cleanupIds,r.map(t=>{if(!Array.isArray(t.controls))return t;const e=t.controls.filter(t=>!("string"==typeof t&&t.startsWith("marker:")&&l.has(t.slice(7))));return e.length===t.controls.length?t:{...t,controls:e.length?e:null}}));const c=a.cleanupIds;this._markerDialog={...t,busy:!0};try{await this._saveConfigNow();for(const t of c){delete this._layout[t],delete this._defPos[t],this._dirtyPos.delete(t),this._sentPos.delete(t);const e=this._activityRt.get(t);e&&clearTimeout(e.timer),this._activityRt.delete(t),this._vacRt.delete(t),delete this._vacSrvTrails[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{}),await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:t}).catch(()=>{}),await this.hass.callWS({type:"houseplan/trail/delete",marker_id:t}).catch(()=>{})}this._markerDialog=null,this._infoCard?.id===e.id&&this._closeInfoCard(),this._selId===e.id&&(this._selId=null),this._drag?.id===e.id&&(this._drag=null),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(t){this._serverCfg===s&&(s.markers=n),this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_normPos(t,e,i){return{s:t,x:e/Pf,y:i/Pf}}_openSpaceDialog(t,e){if(this._serverStorage&&this._serverCfg)if("edit"===t){const i=this._serverCfg.spaces.find(t=>t.id===e);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:t,spaceId:e,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:s.showBorders,showNames:s.showNames,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:$r({},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:gh(Number(i.cell_cm)>0?Number(i.cell_cm):5,this._imperial),cellCmTouched:!1,busy:!1}}else this._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,...Nd(),hideDecor:!1,hideOpenings:!1,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:mh(this._imperial),cellCmInput:gh(mh(this._imperial),this._imperial),cellCmTouched:!1,busy:!1};else this._showToast(this._t("toast.integration_missing"))}async _pickPlanFile(t){const e=t.target,i=e.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 t=0;t<n.length;t+=32768)o+=String.fromCharCode(...n.subarray(t,t+32768));const a=btoa(o),r=URL.createObjectURL(i),l=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:1.414),e.onerror=()=>t(1.414),e.src=r});URL.revokeObjectURL(r),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:a,aspect:l,name:i.name}}}_useServerPlan(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this._display(t);if(e){const i=await new Promise(t=>{const i=new Image;i.onload=()=>t(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>t(0),i.src=e}),s=this._spaceDialog;return s&&s.planUrl===t&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(t=>setTimeout(t,150)),this._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this._t("confirm.delete_plan",{name:t})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this._spaceDialog;e?.saved&&(this._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return W`<div class="savedplans muted">${this._t("space.loading")}</div>`;const e=t.saved||[];if(!e.length)return W`<div class="savedplans muted">${this._t("space.no_saved")}</div>`;return W`<div class="savedplans">
|
||
${e.map(e=>W`
|
||
<div class="savedplan ${e.url===t.planUrl?"cur":""}">
|
||
<img src=${this._display(e.url)} alt="" loading="lazy" decoding="async" />
|
||
<div class="savedmeta">
|
||
<b>${e.name}</b>
|
||
<span class="muted">${(t=>t>=1048576?(t/1048576).toFixed(1)+" MB":Math.round(t/1024)+" KB")(e.size)}${e.used_by.length?" · "+this._t("space.used_by",{list:e.used_by.join(", ")}):""}</span>
|
||
</div>
|
||
<button class="btn ghost" @click=${()=>this._useServerPlan(e.url)}
|
||
?disabled=${e.url===t.planUrl}>${this._t("btn.use")}</button>
|
||
<button class="btn ghost danger"
|
||
title=${e.used_by.length||e.url===t.planUrl?this._t("space.in_use"):this._t("btn.delete")}
|
||
?disabled=${e.used_by.length>0||e.url===t.planUrl}
|
||
@click=${()=>this._deleteServerPlan(e.name)}>
|
||
<ha-icon icon="mdi:trash-can-outline"></ha-icon>
|
||
</button>
|
||
</div>`)}
|
||
</div>`}async _saveSpaceDialog(){const t=this._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this._showToast(this._t("toast.plan_required"));const e="create"===t.mode&&0===(this._serverCfg?.spaces.length||0);this._spaceDialog={...t,busy:!0};try{const i="create"===t.mode?"s"+Date.now().toString(36):t.spaceId;let s=null;if("file"===t.source&&t.planFile){s={url:(await this.hass.callWS({type:"houseplan/plan/set",space_id:i,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let n=t.savedAspect||null;!s&&"file"===t.source&&t.planUrl&&!n&&this._aspectJob&&(n=await this._aspectJob||null);const o=this._serverCfg;let a;if("create"===t.mode)a={id:i,title:t.title.trim(),plan_url:null,view_box:[0,0,1,1],rooms:[]},o.spaces.push(a);else{if(a=o.spaces.find(t=>t.id===i),!a)throw new Error("space "+i+" is gone from the config");a.title=t.title.trim()}s?(a.plan_url=s.url,a.plan_aspect=s.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==a.plan_url&&(a.plan_url=t.planUrl,a.plan_aspect=n),"draw"===t.source&&(a.plan_url=null,a.plan_aspect=null,delete a.plan_x,delete a.plan_y,delete a.plan_scale,delete a.plan_scale_x,delete a.plan_scale_y,delete a.plan_angle),a.settings={...a.settings||{},show_borders:t.showBorders,show_names:t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,custom_fill:t.customFill||void 0,glow_enabled:t.glowEnabled,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):20,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):25,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},a.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(uf,t.cellCm)):5,await this._saveConfigNow(),this._spaceDialog=null,"create"===t.mode&&this._commitSpace(a.id),this._regSignature="",this._maybeRebuildDevices(),this._importQueue.length?this._openNextImport():e||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(e&&!this._importTotal?"toast.space_added_onboard":"import.done"))):(this._showToast("create"===t.mode?this._t("toast.space_added"):this._t("toast.space_saved")),"create"===t.mode&&("plan"!==this._mode?this._setMode("plan"):(this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._saveNav())))}catch(t){this._spaceDialog&&(this._spaceDialog={...this._spaceDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}async _deleteSpace(){const t=this._spaceDialog;if(!t||"edit"!==t.mode)return;const e=this._serverCfg;if(!e)return;const i=e.spaces.find(e=>e.id===t.spaceId),s=Am(e,this._layout||{},t.spaceId||""),n=1===e.spaces.length&&e.spaces[0]?.id===t.spaceId;if(!s.count||n){if(confirm(this._t("confirm.delete_space",{title:i.title}))){this._spaceDialog={...t,deleteBlockers:0,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._persistLayout.pending()&&this._persistLayout.flush(),await this._writeChain;const e=await this.hass.callWS({type:"houseplan/space/delete",space_id:t.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=e?.config_rev??this._cfgRev,this._layoutRev=e?.layout_rev??this._layoutRev,this._spaceDialog=null,this._space===t.spaceId&&this._commitSpace(this._serverCfg.spaces[0]?.id||""),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.space_deleted"))}catch(e){"conflict"!==e?.code&&"space_in_use"!==e?.code||await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]);const i=this._serverCfg;if(this._spaceDialog&&i){const e=Am(i,this._layout||{},t.spaceId||""),s=1===i.spaces.length&&i.spaces[0]?.id===t.spaceId;this._spaceDialog={...this._spaceDialog,busy:!1,deleteBlockers:s?0:e.count}}this._showToast(this._t("toast.delete_failed",{err:this._errText(e)}))}}}else this._spaceDialog={...t,deleteBlockers:s.count}}async _saveConfigNow(){this._cfgEpoch++;try{await this._writeConfig()}catch(t){throw"conflict"===t?.code&&await this._reloadConfigOnly(),t}}_startImport(){const t=this._importDialog;if(!t)return;const e=t.floors.filter(t=>t.checked).map(t=>t.name);this._importDialog=null,e.length?(this._importQueue=e,this._importTotal=e.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this._importQueue.shift();void 0!==t&&(this._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,...Nd(),hideDecor:!1,hideOpenings:!1,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:mh(this._imperial),cellCmInput:gh(mh(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 t=this._importDialog,e=t.floors.filter(t=>t.checked).length;return W`<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>
|
||
${t.floors.map((e,i)=>W`<label class="floorrow">
|
||
<input type="checkbox" .checked=${e.checked}
|
||
@change=${s=>{const n=[...t.floors];n[i]={...e,checked:s.target.checked},this._importDialog={floors:n}}} />
|
||
<span>${e.name}</span>
|
||
${null!=e.level?W`<span class="floorlvl">L${e.level}</span>`:U}
|
||
</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=${!e}>
|
||
<ha-icon icon="mdi:import"></ha-icon>${this._t("import.start",{n:e})}
|
||
</button>
|
||
</div>
|
||
</hp-dialog>`}_sunGlobal(){const t=this._settingsDialog;return t?{...this._settings,north_deg:t.northDeg??void 0,bg_mode:t.bgMode,sun_rays:t.sunRays}:this._settings}_sunSpace(){const t=this._spaceDialog,e=this._curSpaceCfg?.settings||{};return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,north_deg:t.northDeg??void 0,bg_mode:t.bgMode??void 0,sun_rays:t.sunRays??void 0}:e}_effNorth(){return $r(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Sr(this._sunGlobal(),this._sunSpace())}_dayCycleState(t=new Date){return(this._modeTransitionVisual?.viewWeight??("view"===this._mode?1:0))<=0||"daynight"!==this._effBgMode()?null:_r(this._renderPlanHass,t)}_syncDayCycleClock(){const t=this._dayCycleState();this._dayCycleClockKey=t?mr(t):"";const e="clock"===t?.source&&"hidden"!==this.ownerDocument.visibilityState&&this.isConnected;e&&!this._dayCycleTimer?this._dayCycleTimer=window.setInterval(this._dayCycleTick,3e4):!e&&this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_dayCycleVisibility(t){"hidden"!==t.kind?(this._dayCycleTick(),this._syncDayCycleClock()):this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_effSunRays(){return Mr(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Cr(this._renderPlanHass):null}_renderSunRays(t){const e=j``,i=this._modeTransitionVisual?.viewWeight??0;if(this._editing&&i<=0||!this._effSunRays())return this._sunFadeReset(),e;const s=this._effNorth(),n=null!==s?Cr(this._renderPlanHass):null;if(!n||n.elevation<=0)return this._sunFadeReset(),e;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 e;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=`${t.id}|${n.azimuth}|${n.elevation}|${s}|${this._cfgEpoch}`;if(!this._sunRaysCache||this._sunRaysCache.key!==a){const e=t.rooms.map(t=>({id:t.id||"",poly:hi(t)})).filter(t=>!!t.id&&!!t.poly),i=this._openingsR.filter(t=>"window"===t.type&&!t.host).map(t=>({id:t.id,x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),o=this._spaceWalls,r=this._openPairs().flatMap(t=>t.segs),l=this._openingWallIndexFor(t,r).value,c={},h={},d=this._wallUnionGeometry()?.roomGeom;if(o.length){for(const i of e){const e=_c(t.rooms,i.id,o,r,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf,d);e&&(c[i.id]=e)}for(const t of i){const e=Hc(l,{x:t.x,y:t.y,angle:t.angle,length:t.length});e.cm>0&&(h[t.id]=ml(e.cm,this._cellCm,this._gridPitch))}}let p=kr(e,i,n.azimuth,n.elevation,s,o.length?c:void 0,o.length?h:void 0);const u=this._physicalBodiesR(t);u.length&&(p=p.map(t=>{const e=function(t,e,i){return i>0?t.map(t=>function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<=2)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const n=[];for(let t=e.length-1;t>=0;t--){const s=e[t];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))}([...t,...t.map(t=>[t[0]+e[0]*i,t[1]+e[1]*i])])).filter(t=>t.length>=3):t}(u,t.dir,t.len),i=t.polys.map(t=>oh(t,e));return{...t,paths:i.map(Jc).filter(Boolean),polys:i.flatMap(lh)}}).filter(t=>t.paths?.length||t.polys.length)),this._sunRaysCache={key:a,rays:p,rims:p.map(t=>function(t,e=1e-4){const[i,s]=t.dir,n=-s,o=i,a=[];for(const r of[t.a,t.b]){const l=[];for(const a of t.polys)for(let t=0;t<a.length;t++){const c=a[t],h=a[(t+1)%a.length];if(Math.abs((c[0]-r[0])*n+(c[1]-r[1])*o)>e)continue;if(Math.abs((h[0]-r[0])*n+(h[1]-r[1])*o)>e)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)<=e||l.push(d<p?[d,p]:[p,d])}l.sort((t,e)=>t[0]-e[0]);const c=[];for(const t of l){const i=c[c.length-1];i&&t[0]<=i[1]+e?i[1]=Math.max(i[1],t[1]):c.push([t[0],t[1]])}for(const[t,e]of c)a.push([[r[0]+i*t,r[1]+s*t],[r[0]+i*e,r[1]+s*e]])}return a}(t))}}const r=this._sunRaysCache.rays,l=this._sunRaysCache.rims;if(!r.length)return e;const c=(h=function(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=gr[gr.length-1][1];for(let t=1;t<gr.length;t++){const[s,n]=gr[t-1],[o,a]=gr[t];if(e<=o){i=us(n,a,(e-s)/(o-s));break}}return{bg:i,planDim:.1*fr((10-e)/16),warmth:e<0?1:fr(1-e/10)}}(n.elevation).warmth,us("#ffe9c2","#ff9a45",fr(h)));var h;const d=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],p=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]];return j`<defs>
|
||
${r.map((t,e)=>{const i=(t.a[0]+t.b[0])/2,s=(t.a[1]+t.b[1])/2,n=i+t.normal[0]*t.depth,o=s+t.normal[1]*t.depth;return j`<linearGradient id="hp-sun-${e}" gradientUnits="userSpaceOnUse"
|
||
x1="${i}" y1="${s}" x2="${n}" y2="${o}">
|
||
${d.map(([t,e])=>j`<stop offset="${(100*t).toFixed(1)}%"
|
||
stop-color="${c}" stop-opacity="${(.3*e).toFixed(4)}"></stop>`)}
|
||
</linearGradient>
|
||
<linearGradient id="hp-sunrim-${e}" gradientUnits="userSpaceOnUse"
|
||
x1="${i}" y1="${s}" x2="${n}" y2="${o}">
|
||
${p.map(([t,e])=>j`<stop offset="${(100*t).toFixed(1)}%"
|
||
stop-color="${"#000000"}" stop-opacity="${(.42*e).toFixed(4)}"></stop>`)}
|
||
</linearGradient>`})}
|
||
</defs>
|
||
<g class="sunlayer hp-view-only-layer ${this._sunOut?"out":""}"
|
||
opacity="${this._modeTransitionVisual?.viewWeight??1}">
|
||
${r.map((t,e)=>t.paths?.length?t.paths.map(t=>j`<path d=${t} fill-rule="evenodd" fill="url(#hp-sun-${e})"></path>`):t.polys.map(t=>j`<polygon
|
||
points="${t.map(t=>t[0]+","+t[1]).join(" ")}" fill="url(#hp-sun-${e})"></polygon>`))}
|
||
${r.map((t,e)=>(l[e]||[]).map(t=>j`<line class="sunrim"
|
||
x1="${t[0][0]}" y1="${t[0][1]}" x2="${t[1][0]}" y2="${t[1][1]}"
|
||
stroke="url(#hp-sunrim-${e})" 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(t){const e=t.currentTarget.getBoundingClientRect(),i=t.clientX-(e.left+e.width/2),s=t.clientY-(e.top+e.height/2);if(Math.hypot(i,s)<5)return;let n=Math.round(180*Math.atan2(i,-s)/Math.PI);t.shiftKey&&(n=15*Math.round(n/15)),n=(n%360+360)%360,this._settingsDialog={...this._settingsDialog,northDeg:n}}_renderCompass(){const t=this._settingsDialog.northDeg;return W`<svg class="compass ${null===t?"unset":""}" viewBox="-60 -60 120 120"
|
||
@pointerdown=${t=>{t.currentTarget.setPointerCapture(t.pointerId),this._compassDrag=!0,this._compassPoint(t)}}
|
||
@pointermove=${t=>{this._compassDrag&&this._compassPoint(t)}}
|
||
@pointerup=${()=>this._compassDrag=!1}
|
||
@pointercancel=${()=>this._compassDrag=!1}>
|
||
<circle class="cring" r="50"></circle>
|
||
${[0,45,90,135,180,225,270,315].map(t=>j`<line class="ctick ${t%90?"minor":""}" x1="0" y1="-50" x2="0" y2="${t%90?-46:-43}"
|
||
transform="rotate(${t})"></line>`)}
|
||
<g class="cneedle" transform="rotate(${t??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===t?"—":t+"°"}</text>
|
||
</svg>`}_stageBg(t){const e=this._settingsDialog,i=this._spaceDialog,s=e?e.bgColor||"":hs(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":t.bgColor||"")||s}_stageBgHex(){const t=this._stageEl;if(t){const e=getComputedStyle(t).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(e)return"#"+e.slice(1,4).map(t=>(+t).toString(16).padStart(2,"0")).join("")}return"#111111"}_checkOptimizeGeometry(t){return qm(t,{fallbackSpaceName:t=>this._t("gs.align_preflight_space",{n:String(t)})})}_checkSpacePhysicalGeometry(t,e){return function(t,e,i={}){const s=(Array.isArray(t?.spaces)?t.spaces:[]).find(t=>String(t?.id||"")===String(e||"")),n=zm(s);if(!s)return{spaceId:String(e||""),displayName:"",status:"failed",reason:"prepare-exception",fingerprint:n,ok:!1};const o=qm({...t,spaces:[s]},{...i,fingerprint:()=>n}).spaces[0]||{spaceId:String(e||""),displayName:"",status:"failed",reason:"prepare-exception"};return{...o,fingerprint:n,ok:"failed"!==o.status}}(t,e,{fallbackSpaceName:t=>this._t("gs.align_preflight_space",{n:String(t)})})}_optimizeReferenceContext(t){const e=this._haRegistry,i=this._fullRegistryHass,s={},n=(...t)=>{for(const e of t){const t=String(e||"").trim();if(t)return t}return""};for(const[t,i]of Object.entries(e.devices||{}))s[t]=n(i?.name_by_user,i?.name,i?.model);for(const[t,i]of Object.entries(e.entities||{}))s[`lg_${t}`]=n(this.hass?.states?.[t]?.attributes?.friendly_name,i?.name,i?.original_name);for(const t of this._devices)s[t.id]=n(t.name,s[t.id]);for(const t of this._serverCfg?.markers||[]){const e=String(t.binding||"").indexOf(":"),o=e>0?t.binding.slice(0,e):"",a=e>0?t.binding.slice(e+1):"",r="device"===o?i?.devices?.[a]:"entity"===o?i?.entities?.[a]:null;s[t.id]=n(t.name,"device"===o?r?.name_by_user:null,r?.name,"entity"===o?this.hass?.states?.[a]?.attributes?.friendly_name:null,s[t.id])}return{effectiveAreaByMarker:Object.fromEntries(this._devices.filter(t=>!t.virtual&&!!t.area).map(t=>[t.id,t.area])),ownerRoster:{authoritative:e.authoritative,deviceIds:Object.keys(e.devices||{}),entityIds:[...new Set([...Object.keys(e.entities||{}),...Object.keys(this.hass?.states||{})])],names:s},removeLiveMissingPositions:t}}_previewAlignDialog(t){if(!this._norm||!this._serverCfg)return;const e=this._serverCfg.spaces||[],i=Im(this._serverCfg,this._layout||{},this._optimizeReferenceContext(t)),s=i.changed?this._checkOptimizeGeometry(i.config):null,n=Math.ceil(10*i.report.maxShiftCm)/10,o=e.find(t=>null!=t?.id&&String(t.id)===i.report.maxSpace),a=e.length>1&&o?String(o.title||o.id):"";this._alignDialog={report:i.report,config:i.config,layout:i.layout,cm:n,where:a,preflight:s,changed:i.changed,busy:!1,removeLiveMissingPositions:t}}async _runAlignToGrid(){let t=this._alignDialog;if(!t||t.busy||!this._serverCfg||!t.changed||!t.preflight?.ok)return;const e=Id(t.config);if(t.preflight.fingerprint!==e){const e=this._checkOptimizeGeometry(t.config);if(t={...t,preflight:e},this._alignDialog=t,!e.ok)return}this._clearGeometryGesture(),this._alignDialog={...t,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain;const e=await this.hass.callWS({type:"houseplan/plan/optimize",config:t.config,layout:t.layout,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});this._serverCfg=t.config,this._layout=t.layout,this._geometryHistory.clear(),this._cfgRev=e?.config_rev??this._cfgRev+1,this._layoutRev=e?.layout_rev??this._layoutRev+1,this._canOptimizeUndo=!!e?.can_undo,this._undoKind=e?.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.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(t.report.moved),m:String(t.report.migrated+t.report.canonicalized+t.report.coordsCanonicalized+t.report.latticeCoordinatesCanonicalized+t.report.wallsMerged+t.report.spansMerged+t.report.partitionsMerged+t.report.partitionsReconciled+t.report.openingsRehosted+t.report.wallsStraightened),r:String(t.report.spaceRefsRemapped+t.report.roomRefsRemapped+t.report.positionsRemapped+t.report.markersDetached+t.report.orphanRoomLabelsRemoved+t.report.orphanDevicePositionsRemoved+t.report.orphanGroupPositionsRemoved)}))}catch(t){this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),"conflict"===t?.code&&await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}async _undoPlanOptimization(){if(!this._canOptimizeUndo||this._optimizeUndoBusy)return;const t=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[e,i]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._serverCfg=e?.config||this._serverCfg,this._cfgRev=e?.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"===t?"backup.import_undone":"gs.optimize_undone"))}catch(t){this._canOptimizeUndo=!1,this._undoKind=null,this._showToast(this._t("toast.error",{err:this._errText(t)}))}finally{this._optimizeUndoBusy=!1,this.requestUpdate()}}async _runBackupExport(){const t=this._backupExportDialog;if(t&&!t.busy){this._backupExportDialog={...t,busy:!0,error:""};try{const e=await this.hass.callWS({type:"houseplan/export/create",kind:t.kind,space_id:"space"===t.kind?this._space:void 0,..."space"===t.kind&&t.planOnly?{plan_only:!0}:{},card_version:cf}),i=new Blob([JSON.stringify(e.document,null,2)+"\n"],{type:"application/json;charset=utf-8"}),s=URL.createObjectURL(i),n=document.createElement("a");n.href=s,n.download=e.filename||`houseplan-${t.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(t){this._backupExportDialog&&(this._backupExportDialog={...this._backupExportDialog,busy:!1,error:this._backupErrorText(t)})}}}async _pickBackupImport(t){const e=t.target,i=e.files?.[0];if(e.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 t="/api/houseplan/import/preview?duplicate_policy=skip",e={method:"POST",body:i,headers:{"content-type":"application/json"}},s=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth(t,e):await fetch(t,{...e,headers:{...e.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(t){this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,busy:!1,error:this._backupErrorText(t)})}}}async _setBackupDuplicatePolicy(t){const e=this._backupImportDialog;if(e&&!e.busy&&e.token&&t!==e.duplicatePolicy){this._backupImportDialog={...e,duplicatePolicy:t,busy:!0,error:""};try{const i=await this.hass.callWS({type:"houseplan/import/revalidate",token:e.token,duplicate_policy:t});this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,preview:{...e.preview,...i.preview},expectedConfigRev:i.expected_config_rev,expectedLayoutRev:i.expected_layout_rev,confirmMissing:!1,busy:!1})}catch(t){this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,duplicatePolicy:e.duplicatePolicy,preview:e.preview,confirmMissing:e.confirmMissing,busy:!1,error:this._backupErrorText(t)})}}}async _applyBackupImport(){const t=this._backupImportDialog;if(!t||t.busy||!t.token||!t.preview)return;if(t.preview.confirmation_required&&!t.confirmMissing)return;this._backupImportDialog={...t,busy:!0,error:""};const e=this._space;try{const i=await this.hass.callWS({type:"houseplan/import/apply",token:t.token,expected_config_rev:t.expectedConfigRev,expected_layout_rev:t.expectedLayoutRev,duplicate_policy:t.duplicatePolicy,confirm_missing_content:t.confirmMissing}),[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(t=>t.id===e)?e: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?t.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(t){if("conflict"===t?.code&&this._backupImportDialog?.token)try{const t=this._backupImportDialog,e=await this.hass.callWS({type:"houseplan/import/revalidate",token:t.token,duplicate_policy:t.duplicatePolicy});return void(this._backupImportDialog={...t,preview:{...t.preview,...e.preview},expectedConfigRev:e.expected_config_rev,expectedLayoutRev:e.expected_layout_rev,confirmMissing:!1,busy:!1,error:this._t("backup.revalidated")})}catch(e){t=e}this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,busy:!1,error:this._backupErrorText(t)})}}_renderBackupExportDialog(){const t=this._backupExportDialog,e=(this._serverCfg?.spaces||[]).find(t=>t.id===this._space);return W`<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"===t.kind}
|
||
@change=${()=>this._backupExportDialog={...t,kind:"full",planOnly:!1}} />
|
||
<span>${this._t("backup.full")}</span></label>
|
||
<label class="srcrow"><input type="radio" name="backup-kind" value="space"
|
||
.checked=${"space"===t.kind} ?disabled=${!e}
|
||
@change=${()=>this._backupExportDialog={...t,kind:"space"}} />
|
||
<span>${e?this._t("backup.current_space_title",{title:e.title||e.id}):this._t("backup.no_current_space")}</span></label>
|
||
${"space"===t.kind&&e?W`<label class="srcrow backupplanonly">
|
||
<input type="checkbox" .checked=${t.planOnly}
|
||
@change=${e=>this._backupExportDialog={...t,planOnly:e.target.checked}} />
|
||
<span><b>${this._t("backup.plan_only")}</b><small>${this._t("backup.plan_only_hint")}</small></span>
|
||
</label>`:U}
|
||
<div class="backupwarn">${this._t("backup.privacy_warning")}</div>
|
||
${t.error?W`<div class="backuperror" role="alert">${t.error}</div>`:U}
|
||
</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=${t.busy||"space"===t.kind&&!e}
|
||
@click=${this._runBackupExport}>
|
||
<ha-icon icon="mdi:download"></ha-icon>${t.busy?"…":this._t("backup.download")}
|
||
</button>
|
||
</div>
|
||
</hp-dialog>`}_renderBackupImportDialog(){const t=this._backupImportDialog,e=t.preview,i=e?.counts||{};return W`<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=${t.busy?"true":"false"}>
|
||
<div class="backupfile"><b>${t.filename}</b><span>${(t.size/1024).toFixed(1)} KB</span></div>
|
||
${t.busy&&!e?W`<div class="rhint" role="status" aria-live="polite">${this._t("backup.reading")}</div>`:U}
|
||
${t.error?W`<div class="backuperror" role="alert">${t.error}</div>`:U}
|
||
${e?W`
|
||
<div class="backupsummary">
|
||
<b>${this._t("full"===e.kind?"backup.full":"backup.current_space")}</b>
|
||
${e.plan_only?W`<span class="backupplanonlystatus">${this._t("backup.plan_only_preview")}</span>`:U}
|
||
<span>${this._t("same"===e.source?"backup.same_source":"backup.foreign_source")}</span>
|
||
<span>${this._t("backup.created",{value:e.created_at||"—"})}</span>
|
||
<span>${this._t("backup.versions",{card:e.card_version||"—",integration:e.integration_version||"—",model:String(e.model_version??"—")})}</span>
|
||
</div>
|
||
<div class="backupcounts">
|
||
<span>${this._t("backup.count_spaces",{n:"full"===e.kind?`${e.current_counts?.spaces||0} → ${i.spaces||0}`:String(i.spaces||0)})}</span>
|
||
<span>${this._t("backup.count_rooms",{n:"full"===e.kind?`${e.current_counts?.rooms||0} → ${i.rooms||0}`:String(i.rooms||0)})}</span>
|
||
<span>${this._t("backup.count_walls",{n:"full"===e.kind?`${e.current_counts?.walls||0} → ${i.walls||0}`:String(i.walls||0)})}</span>
|
||
<span>${this._t("backup.count_openings",{n:"full"===e.kind?`${e.current_counts?.openings||0} → ${i.openings||0}`:String(i.openings||0)})}</span>
|
||
<span>${this._t("backup.count_decor",{n:"full"===e.kind?`${e.current_counts?.decor||0} → ${i.decor||0}`:String(i.decor||0)})}</span>
|
||
<span>${this._t("backup.count_markers",{n:"full"===e.kind?`${e.current_counts?.markers||0} → ${i.markers||0}`:String(i.markers||0)})}</span>
|
||
<span>${this._t("backup.count_layout",{n:"full"===e.kind?`${e.current_counts?.layout||0} → ${i.layout||0}`:String(i.layout||0)})}</span>
|
||
</div>
|
||
${e.bindings?W`<div class="rhint">${this._t("backup.bindings",{device:String(e.bindings.device||0),entity:String(e.bindings.entity||0),virtual:String(e.bindings.virtual||0),legacy:String(e.legacy_positions||0)})}</div><div class="rhint">${this._t("backup.binding_status",{active:String(e.bindings.active||0),disabled:String(e.bindings.disabled||0),missing:String(e.bindings.missing||0)})}</div>`:U}
|
||
${e.missing_areas?.length?W`<div class="backupwarn">${this._t("backup.missing_areas",{areas:e.missing_areas.join(", ")})}</div>`:U}
|
||
${e.dropped_marker_links?W`<div class="backupwarn">${this._t("backup.dropped_marker_links",{n:String(e.dropped_marker_links)})}</div>`:U}
|
||
${e.repaired_target_refs?W`<div class="rhint">${this._t("backup.repaired_target_refs",{n:String(e.repaired_target_refs)})}</div>`:U}
|
||
${"full"===e.kind?W`
|
||
<div class="backupwarn">${this._t("backup.replace_warning")}</div>
|
||
${"foreign"===e.source?W`<div class="rhint">${this._t("backup.foreign_bookkeeping")}</div>`:U}`:W`
|
||
<div class="backupsummary"><b>${this._t("backup.final_name")}</b><span>${e.space_title}</span></div>
|
||
<div class="rhint">${this._t("backup.target_settings")}</div>
|
||
${e.duplicates?W`<fieldset class="backupchoices"><legend>${this._t("backup.duplicates",{n:String(e.duplicates)})}</legend>
|
||
<label><input type="radio" name="duplicate-policy" .checked=${"skip"===t.duplicatePolicy}
|
||
@change=${()=>this._setBackupDuplicatePolicy("skip")} />${this._t("backup.skip")}</label>
|
||
<label><input type="radio" name="duplicate-policy" .checked=${"virtual"===t.duplicatePolicy}
|
||
@change=${()=>this._setBackupDuplicatePolicy("virtual")} />${this._t("backup.virtual_copy")}</label>
|
||
</fieldset>`:U}`}
|
||
${e.content?.length?W`<div class="backupcontent">
|
||
<b>${this._t("backup.content")}</b>
|
||
${e.content.map(t=>W`<span>${t.url} · ${this._t("available"===t.state?"backup.content_available":"external"===t.state?"backup.content_external":"backup.content_detach_required")}</span>`)}
|
||
</div>`:U}
|
||
${e.confirmation_required?W`<label class="srcrow backupconfirm">
|
||
<input type="checkbox" .checked=${t.confirmMissing}
|
||
@change=${e=>this._backupImportDialog={...t,confirmMissing:e.target.checked}} />
|
||
<span>${this._t("backup.confirm_detach")}</span>
|
||
</label>`:U}
|
||
`:U}
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<button class="btn ghost" autofocus @click=${()=>this._backupImportDialog=null}>${this._t("btn.cancel")}</button>
|
||
<span class="spacer"></span>
|
||
${e?W`<button class="btn ${"full"===e.kind?"danger":"on"}"
|
||
?disabled=${t.busy||e.confirmation_required&&!t.confirmMissing} @click=${this._applyBackupImport}>
|
||
<ha-icon icon=${"full"===e.kind?"mdi:database-import":"mdi:plus"}></ha-icon>
|
||
${t.busy?"…":this._t("full"===e.kind?"backup.replace":"backup.add")}
|
||
</button>`:U}
|
||
</div>
|
||
</hp-dialog>`}_setFillColor(t,e){const i=this._settingsDialog;this._settingsDialog={...i,colors:{...i.colors,[t]:{...i.colors[t],...e}}}}async _saveSettingsDialog(){const t=this._settingsDialog;if(t&&!t.busy){this._settingsDialog={...t,busy:!0};try{const e=this._serverCfg,i=JSON.stringify(t.colors)===JSON.stringify(ds),s={...e.settings};i?delete s.fill_colors:s.fill_colors=t.colors;const n=this._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(n)&&n>0&&300!==Math.round(n)?s.glow_radius_cm=Math.round(n):delete s.glow_radius_cm,t.bgColor?s.bg_color=t.bgColor:delete s.bg_color,null!==t.northDeg&&Number.isInteger(t.northDeg)&&t.northDeg>=0&&t.northDeg<=359?s.north_deg=t.northDeg:delete s.north_deg,s.bg_mode=t.bgMode,t.sunRays?s.sun_rays=!0:delete s.sun_rays,delete s.weather_entity,this._serverCfg={...e,settings:s},await this._saveConfigNow(),this._settingsDialog=null,this.requestUpdate(),this._showToast(this._t("gs.saved"))}catch(t){this._settingsDialog&&(this._settingsDialog={...this._settingsDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}_boolInput(t,e,i=!1){const s=t=>e(!!t.target.checked);return customElements.get("ha-switch")?W`<ha-switch .checked=${t} .disabled=${i} @change=${s}></ha-switch>`:W`<input type="checkbox" .checked=${t} ?disabled=${i} @change=${s} />`}_rangeInput(t,e,i,s,n,o=!1,a){const r=t=>{const e=Number(t.target.value);Number.isFinite(e)&&n(e)};return customElements.get("ha-slider")?W`<ha-slider .min=${t} .max=${e} .step=${i} .value=${s}
|
||
.disabled=${o} aria-label=${a||U} @input=${r} @change=${r}></ha-slider>`:W`<input type="range" min=${t} max=${e} step=${i} .value=${String(s)}
|
||
?disabled=${o} aria-label=${a||U} @input=${r} />`}_renderColorRow(t,e){const i=this._settingsDialog.colors[t];return W`<div class="colorrow gsrow">
|
||
<hp-color-opacity .label=${this._t(e)}
|
||
.opacityLabel=${this._t("space.opacity")}
|
||
.pickerLabels=${this._colorPickerLabels}
|
||
.color=${i.c} .opacity=${i.a} .showOpacity=${!0}
|
||
@hp-color-opacity-change=${e=>{this._setFillColor(t,{c:e.detail.color,a:e.detail.opacity})}}></hp-color-opacity>
|
||
</div>`}get _glowRadiusCm(){const t=Number(this._settings.glow_radius_cm);return Number.isFinite(t)&&t>0?t:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const t=this._glowRadiusCm;return this._imperial?String(Math.round(t/30.48*10)/10):String(t/100)}_glowTransition(t,e){let i=this._glowRenderedSources.get(t);if(e){const e=this._glowFadeTimers.get(t);if(null!=e&&(clearTimeout(e),this._glowFadeTimers.delete(t)),null==i){this._suspendGlowFeatherForTransition(),i=++this._glowSourceSeq,this._glowRenderedSources.set(t,i),this._glowEnteringSources.add(t);const e=requestAnimationFrame(()=>{this._glowEnterRafs.get(t)===e&&(this._glowEnterRafs.delete(t),this._glowEnteringSources.delete(t),this.isConnected&&this.requestUpdate())});this._glowEnterRafs.set(t,e)}return{domId:i,entering:this._glowEnteringSources.has(t),leaving:!1}}if(null==i)return null;const s=this._glowEnterRafs.get(t);if(null!=s&&cancelAnimationFrame(s),this._glowEnterRafs.delete(t),this._glowEnteringSources.delete(t),!this._glowFadeTimers.has(t)){this._suspendGlowFeatherForTransition();const e=window.setTimeout(()=>{this._glowFadeTimers.get(t)===e&&(this._glowFadeTimers.delete(t),this._glowRenderedSources.delete(t),this._glowLastAppearance.delete(t),this.isConnected&&this.requestUpdate())},534);this._glowFadeTimers.set(t,e)}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 t=Math.max(0,this._glowFeatherSuspendUntil-Date.now())+17,e=()=>{this._glowFeatherResumeTimer=0,Date.now()<this._glowFeatherSuspendUntil?this._glowFeatherResumeTimer=window.setTimeout(e,this._glowFeatherSuspendUntil-Date.now()+17):(this._glowFeatherSuspendUntil=0,this.isConnected&&this.requestUpdate())};this._glowFeatherResumeTimer=window.setTimeout(e,t)}_forgetGlowSource(t){const e=this._glowFadeTimers.get(t);null!=e&&clearTimeout(e);const i=this._glowEnterRafs.get(t);null!=i&&cancelAnimationFrame(i),this._glowFadeTimers.delete(t),this._glowEnterRafs.delete(t),this._glowEnteringSources.delete(t),this._glowRenderedSources.delete(t),this._glowLastAppearance.delete(t)}_forgetGlowSpace(t){const e=`${t}|`;for(const t of this._glowRenderedSources.keys())t.startsWith(e)&&this._forgetGlowSource(t)}_warnGlowGeometryFallback(t,e,i,s){const n=`${t}|${e}|${i}`;if(!this._glowGeometryWarnings.has(n)){if(this._glowGeometryWarnings.size>=128){const t=this._glowGeometryWarnings.values().next().value;t&&this._glowGeometryWarnings.delete(t)}this._glowGeometryWarnings.add(n),console.warn(`HOUSEPLAN GLOW GEOMETRY FALLBACK: #218, space ${t}, room ${i}, phase ${s}`)}}_lightBarriers(t,e){const i=this._openPairs().flatMap(t=>t.segs),s=[...i],n=Math.max(this._cmToUnits(10),.5*this._gridPitch),o=t=>e.some(e=>this._pointInRoom(t,e.r)),a=this._openingsR.filter(t=>{if("door"!==(e=String(t.type))&&"gate"!==e&&"passage"!==e)return!1;var e;const i=t.angle*Math.PI/180,s=-Math.sin(i),a=Math.cos(i);return o([t.rx+s*n,t.ry+a*n])&&o([t.rx-s*n,t.ry-a*n])}),r=this._roomWallOpeningInputs(a,t);for(const t of r){const e=t.angle*Math.PI/180,i=Math.cos(e)*t.length/2,n=Math.sin(e)*t.length/2;s.push([t.x-i,t.y-n,t.x+i,t.y+n])}const l=new Set(a.filter(t=>t.partitionHost).map(t=>t.id)),c=[t.id,this._cfgEpoch,this._cellCm,this._gridPitch,[...l].sort().join(",")].join("|");this._lightPhysicalBodiesCache?.key!==c&&(this._lightPhysicalBodiesCache={key:c,all:th(t,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(t,t=>l.has(t.id))).all});const h=this._lightPhysicalBodiesCache.all;let d=2166136261;const p=t=>{d^=Math.round(64*(Number.isFinite(t)?t:0)),d=Math.imul(d,16777619)>>>0},u=this._spaceWalls;p(this._cellCm),p(this._gridPitch),p(this._wallKeyPitch);for(const{poly:t}of e){p(t.length);for(const e of t)p(e[0]),p(e[1])}for(const t of s)for(const e of t)p(e);for(const t of h){p(t.length);for(const e of t)p(e[0]),p(e[1])}for(const t of u)p(t.cm),p(t.a?.[0]??0),p(t.a?.[1]??0),p(t.b?.[0]??0),p(t.b?.[1]??0);const _=d.toString(36),m=`${t.id}|${_}`;if(this._lightBarrierCache?.key===m)return this._lightBarrierCache.value;const g=.02*this._gridPitch,f=[],v=u.length||h.length?Rc(t.rooms,u,i,r,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf,h):null;if(!v||"ok"!==v.status&&"degraded-extra"!==v.status)for(const t of h)f.push(...Uu(t));else for(const t of v.components)for(const e of rh(t.geom))f.push(...Uu(e));for(const{poly:t}of e)for(const e of s.length?Ps(t,s,g):Uu(t))f.push(e);const y={occluders:Gu(f),floor:e.map(t=>t.poly),fingerprint:_,masonryGeometry:!v||"ok"!==v.status&&"degraded-extra"!==v.status?[]:v.components.flatMap(t=>t.geom),opaqueBodies:h};return this._lightBarrierCache={key:m,value:y},y}_renderGlowLayer(t,e){const i=this._fillColors,s=this._glowRadiusCm/this._cellCm*this._gridPitch,n=t.rooms.map(t=>({r:t,poly:hi(t)})).filter(t=>!!t.poly),o=n.filter(({r:t})=>Fs(e.glow,t));if(!o.length)return this._forgetGlowSpace(t.id),j``;const{occluders:a,floor:r,fingerprint:l,masonryGeometry:c,opaqueBodies:h}=this._lightBarriers(t,n),d=ho(this._renderPlanHass,this._renderDevices,null,this._virtualLights).filter(e=>e.device.space===t.id),p=new Map;for(const t of d){if(!t.device.id)continue;const e=p.get(t.device.id)||[];e.push(t),p.set(t.device.id,e)}const u=[],_=new Set;for(const e of this._renderDevices){if(e.space!==t.id)continue;const o=po(p.get(e.id)||[]);if(!o)continue;const d=`${t.id}|${e.id}`;_.add(d);const m=$s(o.passive?{state:o.on?"on":"off",attributes:{}}:this._renderPlanHass.states[o.eid],e.marker?.glow_color,i.glow_light.c),g=Number(e.marker?.glow_radius_cm),f=Number.isFinite(g)&&g>0?g/this._cellCm*this._gridPitch:s,v=this._pos(e);if(hh([v.x,v.y],c,h)){this._forgetGlowSource(d);continue}const y=this._glowTransition(d,!!m);if(!y)continue;m&&this._glowLastAppearance.set(d,{c:m.c,alpha:Ss(m.bri,i.glow_light.a)});const b=this._glowLastAppearance.get(d);if(!b)continue;let w=null;const k=`${t.id}|${l}|${v.x.toFixed(4)},${v.y.toFixed(4)}|${f.toFixed(4)}`,x=gf(this._glowClipCache,k);if(x.hit)w=x.value;else{const e=Yu([v.x,v.y],f,a,96);w={lit:e.length>=3?nh([e],r,{onBoundsFailure:({boundIndex:e,phase:i})=>{const s=n[e]?.r;this._warnGlowGeometryFallback(t.id,l,s?.id||`#${e}`,i)}}):[]},ff(this._glowClipCache,k,w,256)}u.push({key:d,sourceEid:o.eid,domId:y.domId,entering:y.entering,leaving:y.leaving,pos:v,c:b.c,alpha:b.alpha,geometry:w,r:f})}const m=`${t.id}|`;for(const t of this._glowRenderedSources.keys())t.startsWith(m)&&!_.has(t)&&this._forgetGlowSource(t);if(!u.length)return j``;const g=this._spaceWalls,f=o.length===n.length?[]:this._openPairs().flatMap(t=>t.segs),v=this._wallUnionGeometry()?.roomGeom,y=o.length===n.length?null:o.map(({r:e,poly:i})=>{const s=g.length&&e.id&&_c(t.rooms,e.id,g,f,this._wallKeyPitch,this._cellCm,this._gridPitch,Pf,v)||i,o=this._cleanFloor(e,s,t).path,a=Pi(s,n.filter(t=>t.r!==e).map(t=>t.poly)),r=t=>"M "+t.map(t=>t[0]+" "+t[1]).join(" L ")+" Z";return[o||r(s),...a.map(r)].join(" ")}),b=this._viewOr(this._baseVb()),w=this._stageEl?.clientWidth&&b.w?this._stageEl.clientWidth/b.w:1,k=1/(w>0?w:1),x=!this._pinchStart&&!this._panStart&&Date.now()>=this._glowFeatherSuspendUntil;(null==this._glowFeatherUnits||x)&&(this._glowFeatherUnits=k);const $=this._glowFeatherUnits??k,S=4*$,M=u.reduce((t,e)=>({x:Math.min(t.x,e.pos.x-e.r-S),y:Math.min(t.y,e.pos.y-e.r-S),maxX:Math.max(t.maxX,e.pos.x+e.r+S),maxY:Math.max(t.maxY,e.pos.y+e.r+S),w:0,h:0}),{x:1/0,y:1/0,maxX:-1/0,maxY:-1/0,w:0,h:0});return M.w=M.maxX-M.x,M.h=M.maxY-M.y,j`<defs>
|
||
${Mt(u,t=>t.key,t=>{const e=t.domId,i=t.geometry;return j`
|
||
${""}
|
||
<radialGradient id="hp-glow-${e}" gradientUnits="userSpaceOnUse"
|
||
cx="${t.pos.x}" cy="${t.pos.y}" r="${t.r}">
|
||
${Tf.map(([e,i])=>j`
|
||
<stop offset="${e}%" stop-color="${t.c}"
|
||
stop-opacity="${(t.alpha*i).toFixed(4)}"></stop>`)}
|
||
</radialGradient>
|
||
${""}
|
||
${i?j`
|
||
<clipPath id="hp-glowclip-${e}">
|
||
<path class="glow-lit" d="${i.lit.join(" ")}"
|
||
clip-rule="evenodd" fill-rule="evenodd"></path>
|
||
</clipPath>`:U}`})}
|
||
${y?j`<clipPath id="hp-glow-enabled">${y.map(t=>j`<path d=${t} clip-rule="evenodd" fill-rule="evenodd"></path>`)}</clipPath>`:U}
|
||
${""}
|
||
<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)":U}>
|
||
<g class="glow-pools ${this._glowScreenBlend?"blend-screen":"blend-normal"}"
|
||
data-blend=${this._glowScreenBlend?"screen":"normal"}
|
||
data-feather-px="${2}"
|
||
clip-path=${y?"url(#hp-glow-enabled)":U}>
|
||
${Mt(u,t=>t.key,t=>{const e=t.domId;return j`
|
||
<g class="glow-spot ${t.entering?"is-entering":""} ${t.leaving?"is-leaving":""}"
|
||
data-glow-spot="${e}" data-glow-source="${t.sourceEid}">
|
||
<circle class="glow-pool"
|
||
cx="${t.pos.x}" cy="${t.pos.y}" r="${t.r}"
|
||
data-lit-parts="${t.geometry?.lit.length||0}"
|
||
data-feather-px="${2}"
|
||
fill="url(#hp-glow-${e})"
|
||
clip-path=${t.geometry?`url(#hp-glowclip-${e})`:U}></circle>
|
||
</g>`})}
|
||
</g>
|
||
</g>`}_renderAlignDialog(){const t=this._alignDialog,e=t.report,i=t.changed&&!t.preflight?.ok,s=t.preflight?.failures||[],n=s.slice(0,3).map(t=>t.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=e.spaceRefsRemapped+e.roomRefsRemapped+e.positionsRemapped+e.markersDetached,c=e.migrated+e.canonicalized+e.coordsCanonicalized+e.wallsMerged+e.spansMerged+e.partitionsMerged+e.partitionsReconciled+e.openingsRehosted+e.redundantDraftsRemoved,h=e.moved+e.rotated+e.removedDrafts+e.coordsCanonicalized+e.wallsStraightened,d=Math.ceil(10*e.maxStraightenShiftCm)/10,p=(this._serverCfg?.spaces||[]).find(t=>String(t?.id||"")===e.maxStraightenSpace),u=(this._serverCfg?.spaces||[]).length>1&&p?String(p.title||p.id):"",_=e.orphanRoomLabelsRemoved+e.orphanDevicePositionsRemoved+e.orphanGroupPositionsRemoved,m=e.liveMissingPositions.map(t=>t.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)}):""}):"",y=e.unverifiedPositions.some(t=>"registry_unavailable"===t.reason),b=t=>e.removedPositions.some(e=>e.id===t.id)?this._t("gs.optimize_detail_removed"):e.liveMissingPositions.some(e=>e.id===t.id)?this._t("gs.optimize_detail_live"):this._t("gs.optimize_detail_unverified"),w=t=>this._t("room_label"===t?"gs.optimize_detail_room_label":"group"===t?"gs.optimize_detail_group":"device"===t?"gs.optimize_detail_device":"gs.optimize_detail_unknown"),k=[...e.removedPositions,...e.liveMissingPositions.filter(t=>!e.removedPositions.some(e=>e.id===t.id)),...e.unverifiedPositions],x=k.slice(0,10),$=Math.max(0,k.length-x.length);return W`<hp-dialog .hass=${this.hass} .title=${this._t("gs.align_title")} icon="mdi:broom"
|
||
dismiss-on-scrim @hp-close=${()=>this._alignDialog=null}>
|
||
<div class="body">
|
||
${i?W`
|
||
<p class="alignmsg">${this._t("gs.align_preflight_failed",{spaces:o,more:r})}</p>
|
||
<div class="rhint">${this._t("gs.align_preflight_hint")}</div>`:t.changed?W`
|
||
${e.moved?W`<p class="alignmsg">${this._t("gs.align_count",{n:String(e.moved),total:String(e.total),cm:String(t.cm)})}</p>`:U}
|
||
${e.latticeCoordinatesCanonicalized?W`
|
||
<p class="alignmsg">${this._t("gs.optimize_lattice_summary",{n:String(e.latticeCoordinatesCanonicalized),cm:Yr(e.latticeMaxShiftCm)})}</p>
|
||
${e.latticeSpaces.map(t=>W`<p class="alignmsg">${this._t("gs.optimize_lattice_space",{space:t.space,n:String(t.canonicalized),far:String(t.far)})}</p>`)}
|
||
`:U}
|
||
${t.where?W`<p class="alignmsg">${this._t("gs.align_where",{s:t.where})}</p>`:U}
|
||
${e.rotated?W`<p class="alignmsg">${this._t("gs.align_turned",{n:String(e.rotated)})}</p>`:U}
|
||
${e.removedDrafts?W`<p class="alignmsg">${this._t("gs.align_removed_drafts",{n:String(e.removedDrafts)})}</p>`:U}
|
||
${e.redundantDraftsRemoved?W`<p class="alignmsg">${this._t("gs.optimize_redundant_drafts",{n:String(e.redundantDraftsRemoved)})}</p>`:U}
|
||
${c?W`<p class="alignmsg">${this._t("gs.optimize_changes",{m:String(e.migrated),c:String(e.canonicalized),p:String(e.coordsCanonicalized),w:String(e.wallsMerged),s:String(e.spansMerged),i:String(e.partitionsMerged)})}</p>`:U}
|
||
${e.partitionsReconciled?W`<p class="alignmsg">${this._t("gs.optimize_coincident_partitions",{n:String(e.partitionsReconciled)})}</p>`:U}
|
||
${e.openingsRehosted?W`<p class="alignmsg">${this._t("gs.optimize_openings_rehosted",{n:String(e.openingsRehosted)})}</p>`:U}
|
||
${e.wallsStraightened?W`<p class="alignmsg">${this._t("gs.optimize_walls_straightened",{n:String(e.wallsStraightened),cm:String(d)})}</p>`:U}
|
||
${u?W`<p class="alignmsg">${this._t("gs.optimize_walls_straightened_where",{s:u})}</p>`:U}
|
||
${e.glowSpacesMigrated||e.glowRoomsMigrated?W`<p class="alignmsg">${this._t("gs.optimize_glow_migration",{spaces:String(e.glowSpacesMigrated),rooms:String(e.glowRoomsMigrated)})}</p>`:U}
|
||
${h?W`<div class="rhint">${this._t("gs.align_warn")}</div>`:U}`:W`<p class="alignmsg">${this._t(e.liveMissingPositions.length||e.unverifiedPositions.length||e.nestedRefsUnresolved?"gs.optimize_no_automatic_changes":"gs.align_none")}</p>`}
|
||
${!i&&e.wallsStraightenSkipped?W`<p class="rhint">${this._t("gs.optimize_walls_straighten_skipped",{n:String(e.wallsStraightenSkipped)})}</p>`:U}
|
||
${l?W`<p class="alignmsg">${this._t("gs.optimize_references",{spaces:String(e.spaceRefsRemapped),rooms:String(e.roomRefsRemapped),positions:String(e.positionsRemapped),detached:String(e.markersDetached)})}</p>`:U}
|
||
${_?W`<p class="alignmsg">${this._t("gs.optimize_orphans_removed",{total:String(_),rooms:String(e.orphanRoomLabelsRemoved),devices:String(e.orphanDevicePositionsRemoved),groups:String(e.orphanGroupPositionsRemoved)})}</p>`:U}
|
||
${e.liveMissingPositions.length?W`<div class="optimize-live">
|
||
<p class="alignmsg">${this._t(t.removeLiveMissingPositions?"gs.optimize_live_positions_remove":"gs.optimize_live_positions",{n:String(e.liveMissingPositions.length),names:v})}</p>
|
||
<button class="btn ghost optimize-cleanup" type="button"
|
||
aria-pressed=${t.removeLiveMissingPositions?"true":"false"}
|
||
@click=${this._toggleOptimizeLivePositions} ?disabled=${t.busy}>
|
||
<ha-icon icon=${t.removeLiveMissingPositions?"mdi:undo":"mdi:map-marker-remove-outline"}></ha-icon>
|
||
${this._t(t.removeLiveMissingPositions?"gs.optimize_live_keep":"gs.optimize_live_remove")}
|
||
</button>
|
||
${t.removeLiveMissingPositions?W`<div class="rhint optimize-selected" role="status">
|
||
${this._t("gs.optimize_live_selected")}
|
||
</div>`:U}
|
||
</div>`:U}
|
||
${e.unverifiedPositions.length?W`<div class="rhint" role="alert">
|
||
${this._t("gs.optimize_unverified",{n:String(e.unverifiedPositions.length)})}
|
||
${y?` ${this._t("gs.optimize_registry_limited")}`:""}
|
||
</div>`:U}
|
||
${e.nestedRefsUnresolved?W`<div class="rhint" role="alert">${this._t("gs.optimize_vacuum_warning",{n:String(e.nestedRefsUnresolved)})}</div>`:U}
|
||
${k.length?W`<details class="optimize-details">
|
||
<summary>${this._t("gs.optimize_details")}</summary>
|
||
<ul>
|
||
${x.map(t=>W`<li>${this._t("gs.optimize_detail_item",{status:b(t),kind:w(t.kind),id:t.id,space:t.spaceId})}</li>`)}
|
||
</ul>
|
||
${$?W`<div class="rhint">${this._t("gs.optimize_details_more",{n:String($)})}</div>`:U}
|
||
</details>`:U}
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<span class="spacer"></span>
|
||
<button class="btn ghost" @click=${()=>this._alignDialog=null}>${this._t("btn.cancel")}</button>
|
||
${t.changed&&t.preflight?.ok?W`
|
||
<button class="btn on" @click=${this._runAlignToGrid} ?disabled=${t.busy}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${t.busy?"…":this._t("gs.align_run")}
|
||
</button>`:U}
|
||
</div>
|
||
</hp-dialog>`}_renderSettingsDialog(){return W`<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=${t=>{const e=mf(t.target.value);null!=e&&e>0&&(this._settingsDialog={...this._settingsDialog,glowRadius:e})}} />
|
||
<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=${t=>this._settingsDialog={...this._settingsDialog,bgMode:"daynight"===t.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?W`<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=${t=>{this._settingsDialog={...this._settingsDialog,bgColor:t.detail.color}}}></hp-color-opacity>
|
||
${this._settingsDialog.bgColor?W`<button class="btn ghost" @click=${()=>this._settingsDialog={...this._settingsDialog,bgColor:null}}>${this._t("gs.bg_default")}</button>`:W`<span class="opl">${this._t("gs.bg_theme")}</span>`}
|
||
</div>`:W`<div class="rhint">${this._t("gs.bg_daynight_hint")}</div>`}
|
||
<label class="dispsection">${this._t("gs.sun_group")}</label>
|
||
${Cr(this.hass)?U:W`<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=${t=>{const e=t.target.value.trim(),i=""===e?null:Math.round(Number(e));this._settingsDialog={...this._settingsDialog,northDeg:null!==i&&Number.isFinite(i)?Math.min(359,Math.max(0,i)):null}}} />
|
||
${null!==this._settingsDialog.northDeg?W`<button class="btn ghost" @click=${()=>this._settingsDialog={...this._settingsDialog,northDeg:null}}>${this._t("gs.north_clear")}</button>`:U}
|
||
</div>
|
||
${null===this._settingsDialog.northDeg?W`<div class="rhint">${this._t("gs.north_hint")}</div>`:U}
|
||
</div>
|
||
</div>
|
||
<label class="srcrow">
|
||
${this._boolInput(this._settingsDialog.sunRays,t=>this._settingsDialog={...this._settingsDialog,sunRays:t})}
|
||
<span>${this._t("gs.sun_rays")}</span>
|
||
</label>
|
||
${this._canEdit?W`
|
||
<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=${t=>t.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?W`
|
||
<button class="btn ghost" @click=${this._undoPlanOptimization}
|
||
?disabled=${this._optimizeUndoBusy}>
|
||
<ha-icon icon="mdi:undo-variant"></ha-icon>${this._t("backup.undo_import")}
|
||
</button>`:U}
|
||
</div>`:U}
|
||
<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?W`<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>`:U}
|
||
<label class="dispsection">${this._t("gs.about_group")}</label>
|
||
<div class="aboutver">${this._t("gs.about_version",{v:cf})}</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(t){this._rulesDialog={...this._rulesDialog,rules:t}}async _saveRules(){const t=this._rulesDialog;if(!t||t.busy)return;const e=t.rules.filter(t=>t.pattern.trim()&&t.icon.trim());this._rulesDialog={...t,busy:!0};try{const t=this._serverCfg,i=JSON.stringify(e)===JSON.stringify(Jt),s={...t.settings};i?delete s.icon_rules:s.icon_rules=e,this._serverCfg={...t,settings:s},await this._saveConfigNow(),this._rulesDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("rules.saved"))}catch(t){this._rulesDialog&&(this._rulesDialog={...this._rulesDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_renderRulesDialog(){const t=this._rulesDialog,e=Xt(t.rules),i=t.test.trim()?ee(t.test,"",e):null,s=(e,i)=>{const s=[...t.rules],n=e+i;n<0||n>=s.length||([s[e],s[n]]=[s[n],s[e]],this._rulesSet(s))};return W`<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=${t.test}
|
||
@input=${e=>this._rulesDialog={...t,test:e.target.value}} />
|
||
${i?W`<ha-icon icon=${i}></ha-icon><span class="rtesticon">${i}</span>`:U}
|
||
</div>
|
||
${t.rules.map((e,i)=>{const n=""!==e.pattern.trim()&&!function(t){try{return new RegExp(t,"i"),!0}catch{return!1}}(e.pattern);return W`<div class="rrow">
|
||
<input class="namein rpat ${n?"bad":""}" type="text"
|
||
placeholder=${this._t("rules.pattern_ph")}
|
||
title=${n?this._t("rules.invalid"):""}
|
||
.value=${e.pattern}
|
||
@input=${s=>{const n=[...t.rules];n[i]={...e,pattern:s.target.value},this._rulesSet(n)}} />
|
||
<input class="namein ricon" type="text" placeholder=${this._t("rules.icon_ph")}
|
||
.value=${e.icon}
|
||
@input=${s=>{const n=[...t.rules];n[i]={...e,icon:s.target.value},this._rulesSet(n)}} />
|
||
<ha-icon class="rprev" icon=${e.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(t.rules.filter((t,e)=>e!==i))}></ha-icon>
|
||
</div>`})}
|
||
<button class="btn ghost" @click=${()=>this._rulesSet([...t.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(Jt.map(t=>({...t})))}>
|
||
${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=${t.busy}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${t.busy?"…":this._t("btn.save")}
|
||
</button>
|
||
</div>
|
||
</hp-dialog>`}_saveKioskScale(t){this._kioskScale={...this._kioskScale,...t};try{localStorage.setItem(Mf,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const t=this._kioskScale,e=(e,i)=>W`<label>${i}</label>
|
||
<div class="colorrow">
|
||
${this._rangeInput(50,300,5,Math.round(100*t[e]),t=>this._saveKioskScale({[e]:t/100}))}
|
||
<span class="opv">${Math.round(100*t[e])}%</span>
|
||
</div>`;return W`<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>
|
||
${e("icon",this._t("kiosk.icon_scale"))}
|
||
${e("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(t){if("string"==typeof t)return t||"''";try{const e=JSON.stringify(t);return(void 0===e?String(t):e).slice(0,160)}catch{return String(t).slice(0,160)}}render(){if(!this._config||!this.hass)return U;const t=this._model,e=this.houseplanDiagnostics(),i=this._fixedFloorState(t);if("pending"===i.kind)return W`<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 W`<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(!t.length)return W`<ha-card
|
||
data-continuity-state=${this._continuity.state}
|
||
data-continuity-token=${this._continuity.token}
|
||
data-frame-fingerprint=${this._continuity.frameFingerprint||U}
|
||
data-recovery-reason=${(this._continuity.overlayVisible||"recovery-error"===this._continuity.state)&&this._continuity.recoveryReason||U}
|
||
data-ha-registry-access=${e.registry.access}
|
||
data-ha-disabled-bindings=${e.bindings.ha_disabled}
|
||
data-ha-unverified-bindings=${e.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?W`<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>`:W`<p class="muted">${this._t("empty.install")}</p>`}
|
||
</div>
|
||
${this._spaceDialog?this._renderSpaceDialog():U}
|
||
${this._importDialog?this._renderImportDialog():U}
|
||
${this._toast?W`<div class="toast" role="alert" aria-live="assertive">${this._toast}</div>`:U}
|
||
</ha-card>`;const s=this._spaceModel();if(!s)return U;const n="valid"===i.kind?[s]:t,o=s.vb,a=this._effectiveProjection();this._renderProjection=a,this._labs.active.length&&function(t){_g||bg(t);const e=ug,i=e.active.join(",");if(!i||i===gg)return;gg=i;const s=new Map(ng.map(t=>[t.id,t])),n=e.active.map(t=>{const e=s.get(t);return`${t} (#${e.issue}, expires ${e.expires})`}).join(", ");console.info(`HOUSEPLAN LABS: ${n}`)}(cf);const r="iso"===a,l="devices"===this._mode&&this._showAll,c=this._renderDevices.filter(t=>t.space===s.id&&(!t.hidden||l)),h=this._renderDeviceSnapshot,d=this._spaceDisplayForRender(),p=r?this._isoDecorationLayers(d):null,u=p?this._isoOpeningPanels(p):[],_=this._resolvedRoomFills(s,d),m=this._resolvedGlowBase(s,d,_),g=d.showLqi??this._config.show_signal??!0,f=this._config.icon_size??2.5,v=f>8?2.5:f,y=f_(v),b=this._viewOr(this._baseVb()),w=this._floorView(b),k=this._editing?"":this._stageBg(d),x=this._opMeasureView,$=this._decorMeasure,S=this._bdLive,M=this._furnLive,C="view"===this._mode?this._editorChromeMode:this._mode,D=this._roomHoverPaths(s),P=s.bg?this._display(s.bg.href):"",T=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null,R=this._modeTransitionVisual,I=this._dayCycleState(),A=R?.viewWeight??("view"===this._mode?1:0),z=this._modeTransition.state?.from.presentedMode,F=!this._markup||!!R&&("view"===R.presentedMode||"devices"===R.presentedMode||"view"===z||"devices"===z),E=R?.stageColor||k,N=R?.sceneBrightness??1;return W`
|
||
<ha-card
|
||
data-continuity-state=${this._continuity.state}
|
||
data-continuity-token=${this._continuity.token}
|
||
data-frame-fingerprint=${this._continuity.frameFingerprint||U}
|
||
data-device-snapshot-sequence=${h?.sourceSequence??U}
|
||
data-recovery-reason=${T||U}
|
||
data-ha-registry-access=${e.registry.access}
|
||
data-ha-disabled-bindings=${e.bindings.ha_disabled}
|
||
data-ha-unverified-bindings=${e.bindings.unverified}
|
||
@pointerover=${t=>this._notePointer(t)}
|
||
@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=${t=>this._tabPointerMove(t)}>
|
||
${n.map(t=>W`<button
|
||
data-hp="space-tab" data-id="${t.id}"
|
||
class="tab ${this._space===t.id?"active":""}${this._tabDrag?.moved&&this._tabDrag.id===t.id?" dragging":""}${this._tabDrag?.moved&&this._tabDrag.targetId===t.id?` drop-${this._tabDrag.placement}`:""}"
|
||
?data-reorderable=${this._canReorderTabs}
|
||
@pointerdown=${e=>this._tabPointerDown(e,t.id)}
|
||
@pointerup=${t=>this._tabPointerUp(t)}
|
||
@pointercancel=${()=>this._endTabDrag()}
|
||
@click=${()=>this._tabClick(t.id)}
|
||
>
|
||
${t.title}${this._norm&&this._canEdit?W`<ha-icon class="tabedit" icon="mdi:cog-outline"
|
||
title=${this._t("title.configure_space")}
|
||
@click=${e=>{e.stopPropagation(),this._openSpaceDialog("edit",t.id)}}></ha-icon>`:U}
|
||
</button>`)}
|
||
${""}
|
||
${!this._canEdit||this._kiosk||this._hasFixedFloor?U:W`<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?W`<div class="modes">
|
||
${[["plan","mdi:floor-plan"],["devices","mdi:tune-variant"],["decor","mdi:draw"]].map(([t,e])=>W`<button class="modetab ${this._mode===t?"active":""}"
|
||
data-editor-navigation=${t}
|
||
title=${this._t("mode."+t+"_tip")}
|
||
@click=${()=>this._setMode(t)}>
|
||
<ha-icon icon=${e}></ha-icon><span class="ml">${this._t("mode."+t)}</span>
|
||
${this._mode===t?W`<ha-icon class="closex" icon="mdi:close" title=${this._t("title.close_editor")}
|
||
data-editor-navigation="view"
|
||
@click=${t=>{t.stopPropagation(),this._setMode("view")}}></ha-icon>`:U}
|
||
</button>`)}
|
||
</div>`:U}
|
||
<span class="count">${this._t("count.devices",{n:c.filter(t=>!t.hidden).length})}</span>
|
||
<span class="spacer"></span>
|
||
${this._labsIso&&"view"===this._mode&&!this._kiosk?W`<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>`:U}
|
||
<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?W`<button class="btn" @click=${this._openSettingsDialog} title=${this._t("title.general_settings")}>
|
||
<ha-icon icon="mdi:cog-outline"></ha-icon>
|
||
</button>`:U}
|
||
</div>
|
||
${this._canEdit&&!this._kiosk?W`<div class="editorchrome ${this._editing||this._modeTransitionBusy?"open":""}${this._modeTransitionBusy?" transitioning":""}"
|
||
style=${R?`height:${R.editorChromeHeight}px;opacity:${R.editorWeight}`:U}
|
||
aria-hidden=${this._editing?"false":"true"}
|
||
?inert=${!this._editing}>
|
||
<div class="editorchrome-inner"
|
||
style=${R?`opacity:${R.toolbarContentOpacity}`:U}>
|
||
${"plan"===C?this._renderMarkupBar():"devices"===C?this._renderDevicesBar():this._renderDecorBar()}
|
||
</div>
|
||
</div>`:U}
|
||
</div>
|
||
|
||
<div class="stage ${this._markup?"markup tool-"+this._tool+("split"!==this._tool||this._splitSel?"":" pickstage")+("boundary"===this._tool?this._boundaryStageClass:"")+("wallthick"===this._tool&&this._wallThickHover?" wallhot":""):""} ${"decor"===this._mode?"dtool-"+this._decorTool:""} ${s.bg?"":"noplan"} mode-${this._mode}${this._bdMovable?" bdgrab":""}${this._bdDrag?" bdgrabbing":""}${I?` daycycle phase-${I.phase}`:""}${this._booting?" hpboot":""}${this._bootSoft?" hpsettle":""}${this._modeTransitionBusy?" mode-transition":""}"
|
||
?inert=${this._modeTransitionBusy}
|
||
style="height:${R?`${R.stageHeight}px`:this._kiosk?"100dvh":`calc(100dvh - ${this._hdrH}px)`}${E?`;background:${E}`:""};--hp-cell-visual-scale:${uh(this._cellCm)};--wall-fill:${this._fillColors.wall_fill.c};--wall-fill-op:${this._fillColors.wall_fill.a};--hp-mode-architecture-opacity:${R?R.architectureOpacity:"decor"===this._mode?.35:1};--hp-mode-view-weight:${R?.viewWeight??("view"===this._mode?1:0)};--hp-mode-editor-weight:${R?.editorWeight??("view"===this._mode?0:1)}${R?`;--hp-mode-paper:${R.paperColor}`:""}${I?`;${Pr(I)}`:""}"
|
||
@click=${t=>this._markupClick(t)}
|
||
@wheel=${t=>this._onWheel(t)}
|
||
@pointerdown=${t=>{this._notePointer(t),this._stagePointerDown(t)}}
|
||
@pointermove=${t=>this._stagePointerMove(t)}
|
||
@pointerleave=${t=>this._stagePointerLeave(t)}
|
||
@pointerup=${t=>this._stagePointerUp(t)}
|
||
@pointercancel=${t=>this._stagePointerCancel(t)}>
|
||
${Tr(I,A)}
|
||
${this._renderEditorSecondary()}
|
||
<div class="zoomwrap ${this._slide?"slide-"+this._slide:""}"
|
||
?inert=${this._continuity.overlayBlocksInteraction||this._modeTransitionBusy}
|
||
style="${1!==N?`filter:brightness(${N.toFixed(3)})`:""}">
|
||
${r&&p?.structural?j`<svg class="iso-underlay-svg"
|
||
viewBox="${b.x} ${b.y} ${b.w} ${b.h}"
|
||
preserveAspectRatio="xMidYMid meet" aria-hidden="true" pointer-events="none">
|
||
${this._renderIsoUnderlay(p)}
|
||
</svg>`:U}
|
||
<svg class=${p?.structural?"plan-svg":U}
|
||
viewBox=${p?.structural?`${b.x} ${b.y} ${b.w} ${b.h}`:`${w.x} ${w.y} ${w.w} ${w.h}`}
|
||
preserveAspectRatio=${p?.structural||!r?"xMidYMid meet":"none"}>
|
||
<g class=${p?.structural?"iso-floor-scene":U}
|
||
transform=${p?.structural?Sg():U}>
|
||
${""}
|
||
${this._wallHatchDefs(d.color)}${j`<g class="hp-paperg">${this._paperShapes(s.rooms).map(t=>"path"in t?j`<path class="hp-paper" d="${t.path}" fill-rule="evenodd" pointer-events="none"></path>`:"poly"in t?j`<polygon class="hp-paper" points="${t.poly}" pointer-events="none"></polygon>`:j`<rect class="hp-paper" x="${t.rect.x}" y="${t.rect.y}" width="${t.rect.w}" height="${t.rect.h}" rx="${t.rect.rx}" pointer-events="none"></rect>`)}</g>`}
|
||
${this._editing?this._renderMarkupDefs(o):U}
|
||
${""}
|
||
${this._editing&&!this._markup&&this._gridLevels()?j`<rect x="${b.x}" y="${b.y}" width="${b.w}" height="${b.h}" fill="url(#hp-grid-major)" pointer-events="none"></rect>`:U}
|
||
${s.bg&&P?j`<image class="hp-backdrop" href="${P}" x="${s.bg.x}" y="${s.bg.y}" width="${s.bg.w}" height="${s.bg.h}"
|
||
opacity="${R?.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})`:U}
|
||
@dblclick=${t=>this._openBackdropDialog(t)}
|
||
preserveAspectRatio="none" />`:U}
|
||
${(()=>{const t=this._openPairs(),e=new Map,i=t=>(e.has(t)||e.set(t,hi(t)),e.get(t));return s.rooms.filter(t=>t.area||"view"===this._mode||this._markup||d.showBorders).map(e=>{let n="room "+(s.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||e.id!==this._mergeSel&&e.id!==this._splitSel?.roomId||(n+=" picked");let o="";const a=zs(d.fill,e);if(!this._markup&&(d.showBorders||"none"!==a)){n+=" styled";const t=[];t.push(`--room-stroke:${d.color}`,`--room-stroke-op:${d.showBorders?d.opacity:0}`);const i=_.byRoom.get(e)||null;i?(n+=" filled",t.push(`--room-fill:${i.color}`,`--room-fill-op:${i.opacity}`)):t.push("--room-fill:transparent","--room-fill-op:0"),o=t.join(";")}let r;const l=t=>{"view"===this._mode&&(void 0===r&&(r=this._roomArea(e)),this._showTip(t,e.name||this._t("room.unnamed"),r?this._t("tip.area",{value:r}):"",g?this._roomLqi(e.area):null,this._roomTemp(e),this._roomHum(e)))},c=this._markup&&(e.id===this._mergeSel||e.id===this._splitSel?.roomId),h=e.id&&!c?t.filter(t=>t.a.id===e.id||t.b.id===e.id).flatMap(t=>t.segs):[],p=c?[]:this._thickWallCuts(),u=h.concat(p);u.length&&(n+=" noedge");const m=i(e),f=this._spaceWalls,v=f.length&&e.id&&m&&_c(s.rooms,e.id,f,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,Pf,this._wallUnionGeometry()?.roomGeom)||m,y=v?Pi(v,(b=e,s.rooms.filter(t=>t!==b).map(i).filter(Boolean))):[];var b;const w=t=>"M "+t.map(t=>t[0]+" "+t[1]).join(" L ")+" Z",k=v?this._cleanFloor(e,v,s).path:"",x=e.id||U,$=e.area||U,S=k&&v?j`<path class="${n}" style="${o}" fill-rule="evenodd"
|
||
data-hp="room" data-id=${x} data-area=${$}
|
||
d="${[k,...y.map(w)].join(" ")}"
|
||
@pointerenter=${t=>{this._notePointer(t),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:e})}}
|
||
@pointermove=${l}
|
||
@pointerleave=${()=>this._clearTransientHover()}></path>`:y.length&&v?j`<path class="${n}" style="${o}" fill-rule="evenodd"
|
||
data-hp="room" data-id=${x} data-area=${$}
|
||
d="${[v,...y].map(w).join(" ")}"
|
||
@pointerenter=${t=>{this._notePointer(t),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:e})}}
|
||
@pointermove=${l}
|
||
@pointerleave=${()=>this._clearTransientHover()}></path>`:v&&v!==m?j`<polygon class="${n}" style="${o}" points="${v.map(t=>t.join(",")).join(" ")}"
|
||
data-hp="room" data-id=${x} data-area=${$}
|
||
@pointerenter=${t=>{this._notePointer(t),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:e})}}
|
||
@pointermove=${l}
|
||
@pointerleave=${()=>this._clearTransientHover()}></polygon>`:e.poly?j`<polygon class="${n}" style="${o}" points="${e.poly.map(t=>t.join(",")).join(" ")}"
|
||
data-hp="room" data-id=${x} data-area=${$}
|
||
@pointerenter=${t=>{this._notePointer(t),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:e})}}
|
||
@pointermove=${l}
|
||
@pointerleave=${()=>this._clearTransientHover()}></polygon>`:j`<rect class="${n}" style="${o}"
|
||
data-hp="room" data-id=${x} data-area=${$}
|
||
x="${e.x}" y="${e.y}" width="${e.w}" height="${e.h}" rx="${.03*Math.min(e.w,e.h)}"
|
||
@pointerenter=${t=>{this._notePointer(t),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:e})}}
|
||
@pointermove=${l}
|
||
@pointerleave=${()=>this._clearTransientHover()}></rect>`,M=u.length&&m?Ps(m,u,.02*this._gridPitch):null,C=M?j`<path class="room-outline ${this._markup?"outlined":""}"
|
||
d="${M.map(t=>`M ${t[0]} ${t[1]} L ${t[2]} ${t[3]}`).join(" ")}"
|
||
style=${this._markup?U:`stroke:${d.color};stroke-opacity:${d.showBorders?d.opacity:0}`}></path>`:U;return j`${S}${C}`})})()}
|
||
${this._renderRoomHoverFill(D)}
|
||
${this._renderOpeningTunnelFills(s,_)}
|
||
${this._renderGlowBaseRooms(s,m)}
|
||
${this._renderOpeningTunnelFills(s,m,"glow-base")}
|
||
${""}
|
||
${d.hideDecor&&"decor"!==this._mode?U:this._renderDecorLayer()}
|
||
${F?this._renderGlowLayer(s,d):U}
|
||
${this._renderSunRays(s)}
|
||
${this._editing?j`<g class="hp-editor-only-layer"
|
||
opacity="${R?.editorWeight??1}">${this._renderAlignGuides()}</g>`:U}
|
||
${this._markup?j`<g class="hp-editor-only-layer"
|
||
opacity="${R?.editorWeight??1}">${this._renderMarkupLayer(o)}</g>`:U}
|
||
${""}
|
||
${""}
|
||
${this._editing?U:this._renderOpenWalls(d)}
|
||
${this._renderWallBodies(d)}
|
||
${this._markup&&"resize"===this._tool?this._renderResizeMeasurements():U}
|
||
${this._renderRoomHoverOutline(D)}
|
||
${""}
|
||
${this._editing?this._renderOpenWalls(d):U}
|
||
${this._markup?j`<g class="hp-editor-only-layer"
|
||
opacity="${R?.editorWeight??1}">${this._renderHiddenWallDiagnosticOverlay()}</g>`:U}
|
||
${this._markup?j`<g class="hp-editor-only-layer"
|
||
opacity="${R?.editorWeight??1}">${this._renderOpeningPlacementPreview()}</g>`:U}
|
||
${x?this._renderOpeningDimensionGuides(x):U}
|
||
${x?.guide?this._renderOpeningCenterTick(x.guide):U}
|
||
${this._markup&&"draw"===this._tool?j`<g class="hp-editor-only-layer"
|
||
opacity="${R?.editorWeight??1}">${this._renderPlanSnapOverlay()}</g>`:U}
|
||
${d.hideOpenings&&!this._markup||p&&!p.floorSymbols?U:this._renderOpenings(d)}
|
||
${this._renderWallThickUi()}
|
||
${this._markup&&"resize"===this._tool?this._renderResizeLayer(b):U}
|
||
${""}
|
||
${this._renderBackdropFrame(b)}
|
||
${this._renderTextFrame(b)}
|
||
</g>
|
||
</svg>
|
||
${r&&p?.structural?j`<svg class="iso-shadows-svg"
|
||
viewBox="${b.x} ${b.y} ${b.w} ${b.h}"
|
||
preserveAspectRatio="xMidYMid meet" aria-hidden="true" pointer-events="none">
|
||
${this._renderIsoShadows(p,u)}
|
||
</svg>
|
||
<svg class="iso-walls-svg" viewBox="${b.x} ${b.y} ${b.w} ${b.h}"
|
||
preserveAspectRatio="xMidYMid meet" aria-hidden="true" pointer-events="none">
|
||
${this._renderIsoWalls(p,u)}
|
||
</svg>`:U}
|
||
${""}
|
||
<div class="devlayer" style="--icon-size:${Gh(v,s,b.w,this._kiosk?this._kioskScale.icon:1).toFixed(3)}cqw;--device-base-size:${Gh(y,s,b.w,this._kiosk?this._kioskScale.icon:1).toFixed(3)}cqw;--rl-icon-size:${Gh(v,s,this._roomLabelReferenceViewWidth(b),this._kiosk?this._kioskScale.icon:1).toFixed(3)}cqw;--rl-font:${this._kiosk?this._kioskScale.font:1}">
|
||
${c.map(t=>this._renderDevice(t,b,g))}
|
||
${this._renderVacuums(c,b)}
|
||
${this._renderVacFit(b)}
|
||
${this._renderOpeningLocks(b)}
|
||
${d.showNames||this._markup?s.rooms.map(t=>this._renderRoomLabel(t,s,b,d)):U}
|
||
${this._markup?s.rooms.map(t=>this._renderRoomGear(t,s,b)):U}
|
||
</div>
|
||
${this._measureAnchor?W`<div class="measurelayer">${this._renderMeasureLabel(b)}</div>`:U}
|
||
${this._rszLive?W`<div class="measurelayer">${this._rszLive.map(t=>W`<div
|
||
class="measurelabel ${"area"===t.kind?"rszarea":"rszlength"}"
|
||
data-hp=${"area"===t.kind?"resize-area-label":"resize-length-label"}
|
||
data-room=${"area"===t.kind?t.roomId:U}
|
||
data-side=${"area"===t.kind?t.placement.side:U}
|
||
style="left:${((t.x-b.x)/b.w*100).toFixed(2)}%;top:${((t.y-b.y)/b.h*100).toFixed(2)}%;${"area"===t.kind?`--rsz-label-x:${t.placement.offsetXPx.toFixed(2)}px;--rsz-label-y:${t.placement.offsetYPx.toFixed(2)}px;--rsz-label-tangent:${t.placement.tangentOffsetPx.toFixed(2)}px`:""}">${t.text}</div>`)}</div>`:U}
|
||
${x?W`<div class="measurelayer">${x.labels.map(t=>W`<div
|
||
class="measurelabel opshoulder ${t.dimension?"opdimension":""}"
|
||
data-dimension-source=${t.dimension?.source||U}
|
||
data-dimension-room=${t.dimension?.roomId||U}
|
||
style="left:${((t.x-b.x)/b.w*100).toFixed(2)}%;top:${((t.y-b.y)/b.h*100).toFixed(2)}%;${t.dimension?`--op-label-shift-x:${(12*t.dimension.labelNormal[0]).toFixed(2)}px;--op-label-shift-y:${(12*t.dimension.labelNormal[1]).toFixed(2)}px`:""}">${t.text}</div>`)}</div>`:U}
|
||
${this._wallDialog?W`<div class="measurelayer">${this._renderWallThickDialog()}</div>`:U}
|
||
${$?W`<div class="measurelayer"><div
|
||
class="measurelabel dmeasure ${$.on45?"on45":""}"
|
||
style="left:${(($.x-b.x)/b.w*100).toFixed(2)}%;top:${(($.y-b.y)/b.h*100).toFixed(2)}%">${$.text}</div></div>`:U}
|
||
${M?W`<div class="measurelayer">${M.map(t=>W`<div
|
||
class="measurelabel furnmeasure"
|
||
style="left:${((t.x-b.x)/b.w*100).toFixed(2)}%;top:${((t.y-b.y)/b.h*100).toFixed(2)}%">${t.text}</div>`)}</div>`:U}
|
||
${S?W`<div class="measurelayer"><div
|
||
class="measurelabel bdmeasure"
|
||
style="left:${((S.x-b.x)/b.w*100).toFixed(2)}%;top:${((S.y-b.y)/b.h*100).toFixed(2)}%">${S.text}</div></div>`:U}
|
||
</div>
|
||
${this._zoom>1?W`<div class="zoombadge">${Math.round(100*this._zoom)}%</div>`:U}
|
||
${this._renderFarHint()}
|
||
${this._renderHomeArrow()}
|
||
${this._renderRecoveryOverlay()}
|
||
${this._booting||this._bootFading?W`<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>`:U}
|
||
</div>
|
||
|
||
${this._roomDialog?this._renderRoomDialog():U}
|
||
${this._mergeDialog?this._renderMergeDialog():U}
|
||
${this._openingDialog?this._renderOpeningDialog():U}
|
||
${this._physicalDialog?this._renderPhysicalDialog():U}
|
||
${this._partitionDeleteDialog?this._renderPartitionDeleteDialog():U}
|
||
${this._roomDeleteDialog?this._renderRoomDeleteDialog():U}
|
||
${this._openingInfo?this._renderOpeningInfoCard():U}
|
||
${this._decorTextDialog?this._renderDecorTextDialog():U}
|
||
${this._decorShapeDialog?this._renderDecorShapeDialog():U}
|
||
${this._backdropDialog?this._renderBackdropDialog():U}
|
||
${this._decorEraseConfirm?this._renderDecorEraseConfirm():U}
|
||
${this._spaceDialog?this._renderSpaceDialog():U}
|
||
${this._markerDialog?this._renderMarkerDialog():U}
|
||
${this._vacCalConfirm?W`<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>`:U}
|
||
${this._infoCard?this._renderInfoCard():U}
|
||
${this._rulesDialog?this._renderRulesDialog():U}
|
||
${this._settingsDialog?this._renderSettingsDialog():U}
|
||
${this._alignDialog?this._renderAlignDialog():U}
|
||
${this._backupExportDialog?this._renderBackupExportDialog():U}
|
||
${this._backupImportDialog?this._renderBackupImportDialog():U}
|
||
${this._importDialog?this._renderImportDialog():U}
|
||
${this._tip?W`<div class="tip" style="left:${this._tip.x+12}px;top:${this._tip.y+12}px">
|
||
<b>${this._tip.title}</b>${this._tip.meta?W`<span class="m">${this._tip.meta}</span>`:U}
|
||
${null!=this._tip.temp?W`<span class="m">${this._t("tip.temp_avg")} <b>${this._tip.temp}°</b></span>`:U}
|
||
${null!=this._tip.hum?W`<span class="m">${this._t("tip.hum_avg")} <b>${this._tip.hum}%</b></span>`:U}
|
||
${null!=this._tip.lqi?W`<span class="m">${this._t("tip.lqi")}
|
||
<b style="color:${ai(this._tip.lqi)}">${this._tip.lqi}</b></span>`:U}
|
||
</div>`:U}
|
||
${this._kiosk&&!this._hasFixedFloor&&this._kioskDots&&this._model.length>1?W`<div class="kioskdots">
|
||
${this._model.map(t=>W`<span class="kdot ${t.id===this._space?"on":""}"></span>`)}
|
||
</div>`:U}
|
||
${this._kioskDialog?this._renderKioskDialog():U}
|
||
${this._vacFit?W`<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>`:U}
|
||
${this._tapConfirm?W`<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?W`<p>${this._tapConfirm.text}</p>`:this._tapConfirm.lines.map((t,e)=>W`
|
||
<p class="tapconfirm-line" data-line=${e}>${t}</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 t=this._tapConfirm;this._tapConfirm=null,t.exec()}}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${this._t("btn.run")}
|
||
</button>
|
||
</div>
|
||
</hp-dialog>`:U}
|
||
${this._toast?W`<div class="toast" role="alert" aria-live="assertive">${this._toast}</div>`:U}
|
||
</ha-card>
|
||
`}_vacCandidateStatus(t,e,i=this._planHass){const s=this._bindingStatus("entity:"+t);if("ha_disabled"===s.kind)return"disabled";if("orphaned"===s.kind)return"missing";if("unverified"===s.kind)return"unverified";const n=i?.states?.[t];return"unavailable"===n?.state?"unavailable":e?.hasPosition?"ok":"unsupported"}_vacOpenAllCameras(t){const e=this._haRegistry.entities||{},i=[];for(const[t,s]of Object.entries(this.hass?.states||{})){if(!t.startsWith("camera."))continue;const n=Jd(t,s,e[t]);n&&i.push(n)}this._vacAllCameraCache={devId:t.id,candidates:i},this._vacAllCamerasFor=t.id}_vacSourceResolution(t,e=!1,i=this._planHass){const s=t.marker?.vacuum,n="string"==typeof s?.source&&!!s.source,o=new Set(t.entities||[]),a=new Set(o);n&&a.add(s.source);const r=i?.entities||{},l=[];for(const t of a){const e=i?.states?.[t],o=Jd(t,e,r[t]);o?l.push(o):n&&t===s.source&&l.push({entityId:t,name:String(e?.attributes?.friendly_name||t),platform:r[t]?.platform?String(r[t].platform):null,category:t.startsWith("camera.")?"camera":"partial",hasPosition:!1,hasRooms:!1,hasPath:!1,hasMapId:!1,score:0})}if(e&&this._vacAllCameraCache?.devId===t.id){const t=new Set(l.map(t=>t.entityId));for(const e of this._vacAllCameraCache.candidates)t.has(e.entityId)||l.push(e)}const c={};for(const t of l)c[t.entityId]=this._vacCandidateStatus(t.entityId,t,i);return function(t,e,i,s){const n="string"==typeof t&&t.length>0,o=new Set(e),a=Array.from(i).sort((e,i)=>n&&e.entityId===t?-1:n&&i.entityId===t?1:i.score-e.score||e.entityId.localeCompare(i.entityId));if(n){const e=a.find(e=>e.entityId===t);return{entityId:t,status:s[t]||(e?.hasPosition?"ok":"unverified"),pinned:!0,candidates:a}}const r=a.find(t=>o.has(t.entityId)&&t.hasPosition&&"ok"===s[t.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(t,e=this._planHass){if(!1===t.marker?.vacuum?.live)return null;const i=this._vacSourceResolution(t,!1,e);return"ok"===i.status||"unsupported"===i.status?i.entityId:null}_vacEntity(t){return t.primary?.startsWith("vacuum.")?t.primary:(t.entities||[]).find(t=>t.startsWith("vacuum."))||null}_isVacDev(t){return!!this._vacEntity(t)}_activitySourceKey(t){return this._activitySnapshot(t).sourceKey}_activitySnapshot(t,e=ho(this._planHass,this._devices,null,this._virtualLights)){const i=r_(this._planHass,t,this._devices,e,this._fullRegistryHass);return{samples:i.samples,sourceKey:h_(this._planHass,t,i)}}_stampActivity(t,e,i){let s=this._activityRt.get(t);s||(s=z_(i||"",[]),this._activityRt.set(t,s)),null!=i&&(s.sources=i),E_(s,e,Date.now(),window.clearTimeout.bind(window),t=>window.setTimeout(()=>this.requestUpdate(),t))}_syncActivityRuntime(){const t=new Map;if(!this.hass)return t;if(!1===this._config?.live_states){for(const t of this._activityRt.values())clearTimeout(t.timer);return this._activityRt.clear(),t}const e=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;e.add(s.id);const n=this._activitySnapshot(s,i);t.set(s.id,n);const{samples:o,sourceKey:a}=n;let r=this._activityRt.get(s.id);r?r.sources!==a&&F_(r,a,o,window.clearTimeout.bind(window)):(r=z_(a,o),this._activityRt.set(s.id,r))}for(const[t,i]of this._activityRt)e.has(t)||(clearTimeout(i.timer),this._activityRt.delete(t));return t}_activityTick(){if(!this.hass)return;const t=this._syncActivityRuntime();for(const e of this._devices){if(e.hidden)continue;if("icon_ripple"!==Bi(e.marker?.display))continue;const i=t.get(e.id)||this._activitySnapshot(e),{samples:s,sourceKey:n}=i,o=this._activityRt.get(e.id);if(!o||o.sources!==n)continue;const a=N_(o,s,window.clearTimeout.bind(window));a&&this._stampActivity(e.id,a,n)}}_vacTick(){if(this.hass)for(const t of this._devices){if(t.hidden||!this._isVacDev(t))continue;if("static_icon"===Bi(t.marker?.display)){this._vacRt.delete(t.id);continue}const e=this._vacSource(t);if(!e)continue;const i=this._vacEntity(t),s=tp(this.hass.states[i||""]?.state),n=Yd(this.hass.states[e]?.attributes);let o=this._vacRt.get(t.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(t.id,o)),s&&!o.moving&&(o.trail=[],o.lastPos=null);const a="never"!==op(t.marker?.vacuum)&&!n?.path.length;!s&&o.moving&&(o.endedTs=Date.now(),a&&o.lastPos&&(o.trail=Qd(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=s;const r=n?.pos;if(s&&r){const t=r.x+":"+r.y;if(t!==o.lastKey){const e=Date.now();o.jump=o.lastTs>0&&e-o.lastTs>1e4,o.lastKey=t,o.lastTs=e,a&&o.lastPos&&(o.trail=Qd(o.trail,o.lastPos,40)),o.lastPos=[r.x,r.y]}}}}_vacEnsureMarker(t){const e=this._serverCfg;if(!e)return null;e.markers=e.markers||[];const i=e.markers.find(e=>e.id===t.id);if(i)return i;if("device"!==t.bindingKind&&"entity"!==t.bindingKind||!t.bindingRef)return null;const s={id:t.id,binding:t.bindingKind+":"+t.bindingRef,space:t.space||null,area:t.area||null,hidden:!!t.hidden};return e.markers.push(s),s}_renderVacSection(t){const e=this._devices.find(e=>e.id===t.devId);if(!e||!this._isVacDev(e))return U;const i=e.marker?.vacuum||{},s=this._vacAllCamerasFor===e.id,n=this._vacSourceResolution(e,s),o=n.entityId,a=n.candidates.find(t=>t.entityId===o)||null,r=o?Yd(this.hass?.states[o]?.attributes):null,l=!1!==i.live&&("ok"===n.status||"unsupported"===n.status),c=this._vacPlanRoomAnchors(e.space),h=r?function(t,e){const i=new Set(e.map(Xd).filter(Boolean)),s=new Set;for(const e of t){const t=Xd(e.name||"");t&&i.has(t)&&s.add(t)}return s.size}(r.rooms,c.map(t=>t.name)):0,d=!!(l&&r&&h>=3),p=Object.keys(i.calibration||{}),u=t=>{const i=this._vacEnsureMarker(e);i&&(i.vacuum={...i.vacuum||{},...t},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate())},_=new Set(e.entities||[]),m=n.candidates.filter(t=>_.has(t.entityId)||t.entityId===i.source),g=s?n.candidates.filter(t=>t.entityId.startsWith("camera.")&&!m.some(e=>e.entityId===t.entityId)):[],f=t=>[t.hasPosition?this._t("vac.cap_position"):"",t.hasRooms?this._t("vac.cap_rooms_short"):"",t.hasPath?this._t("vac.cap_path"):"",t.hasMapId?this._t("vac.cap_map"):""].filter(Boolean).join(" · ")||this._t("vac.cap_none"),v=t=>W`
|
||
<button type="button" class="vacsource ${t.entityId===o?"on":""}"
|
||
@click=${()=>u({source:t.entityId})}>
|
||
<span><b>${t.name}</b><small>${t.entityId}</small></span>
|
||
<span class="vacsource-meta">${t.platform||this._t("vac.platform_unknown")} · ${f(t)}</span>
|
||
</button>`,y=n.candidates.some(t=>_.has(t.entityId)&&"known_xcme_incomplete"===t.category)||!!n.pinned&&!!o?.startsWith("camera.")&&!a?.hasPosition;return W`
|
||
<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?W`<div><span>${this._t("vac.diag_platform")}</span><b>${a.platform}</b></div>`:U}
|
||
<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?W`<div class="warn vacsource-warning">
|
||
<span>${this._t(`vac.source_banner_${n.status}`)}</span>
|
||
<button type="button" class="btn ghostbtn" @click=${()=>{const t=[...this.renderRoot.querySelectorAll("details.vacpicker")].find(t=>t.dataset.devId===e.id);t&&(t.open=!0,t.querySelector("summary")?.focus())}}>${this._t("vac.choose_source")}</button>
|
||
</div>`:U}
|
||
${y?W`<div class="warn vacxcme">
|
||
<b>${this._t("vac.xcme_hint")}</b>
|
||
<pre>attributes:
|
||
- vacuum_position
|
||
- rooms
|
||
- path
|
||
- map_name</pre>
|
||
</div>`:U}
|
||
<details class="vacpicker" data-dev-id=${e.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=${t=>{t.currentTarget.open?this._vacOpenAllCameras(e):(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):W`<div class="rhint">${this._t("vac.all_cameras_empty")}</div>`:U}
|
||
</details>
|
||
</div>
|
||
</details>
|
||
<div class="vacbtns">
|
||
${d?W`<button class="btn" @click=${()=>this._vacAutoCalibrate(e)}>${this._t("vac.autocal")}</button>`:U}
|
||
${l?W`<button class="btn ghostbtn" @click=${()=>this._vacStartFit(e)}>${this._t("vac.fit")}</button>`:U}
|
||
<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?W`
|
||
<label class="srcrow">
|
||
${this._boolInput(!1!==i.live,t=>u({live:!!t&&null}))}
|
||
<span>${this._t("vac.live")}</span>
|
||
</label>
|
||
<label>${this._t("vac.trail")}</label>
|
||
<select class="areasel"
|
||
@change=${t=>u({trail_mode:t.target.value,trail:null})}>
|
||
${["never","cleaning","always"].map(t=>W`
|
||
<option value=${t} ?selected=${op(i)===t}>${this._t("vac.trail_"+t)}</option>`)}
|
||
</select>
|
||
${p.length?W`<div class="rhint">${ss(this._t("vac.cal_maps"),{maps:p.join(", ")})}</div>`:U}
|
||
`:U}
|
||
</div>`}_vacMapId(t,e,i=this._planHass){const s=this._vacEntity(t),n=s?i?.states?.[s]?.attributes?.selected_map:null;return o=e.mapId,a=n,"default"!==o?o:null!=a?String(a):"default";var o,a}_vacSaveMatrix(t,e,i,s){const n=this._devices.find(e=>e.id===t),o=n?this._vacEnsureMarker(n):this._serverCfg?.markers?.find(e=>e.id===t);if(!o)return!1;const a={...o.vacuum||{}};return a.source=e,a.calibration={...a.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},o.vacuum=a,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_vacPlanRoomAnchors(t){return(this._spaceModelById(t)?.rooms||[]).map(t=>({room:t,poly:hi(t)})).filter(({room:t,poly:e})=>t.name&&e).map(({room:t,poly:e})=>{const i=jd(e);return i?{name:String(t.name),cx:i[0],cy:i[1]}:null}).filter(Boolean)}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?Yd(this.hass?.states[e]?.attributes):null;if(!e||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._vacPlanRoomAnchors(t.space),n=Zd(i.rooms,s);if(!n)return void this._showToast(this._t("vac.autocal_no_match"));const o=this._serverCfg?.spaces?.find(e=>e.id===t.space),a=Number(o?.cell_cm),r=Number.isFinite(a)&&a>0?a:5,l=function(t,e,i){return![t,e,i].every(Number.isFinite)||t<0||e<=0||i<=0?Number.POSITIVE_INFINITY:t/e*i}(n.residual,this._gridPitch,r),c=this._vacMapId(t,i);l>40?this._vacCalConfirm={markerId:t.id,source:e,mapId:c,matrix:n.matrix,rooms:n.matched.length,error:li(l,"mi"===this.hass?.config?.unit_system?.length)}:this._vacSaveMatrix(t.id,e,c,n.matrix)&&this._showToast(ss(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))}_vacApplyCalibrationProposal(t){const e=this._vacCalConfirm;if(e){if(this._vacCalConfirm=null,t){const t=this._devices.find(t=>t.id===e.markerId),i=sp(e.matrix);if(!t||!i)return;if(this._markerDialog=null,t.space!==this._space&&!this._commitSpace(t.space))return;return void(this._vacFit={markerId:e.markerId,source:e.source,mapId:e.mapId,p:i,drag:null})}this._vacSaveMatrix(e.markerId,e.source,e.mapId,e.matrix)&&this._showToast(ss(this._t("vac.autocal_done"),{rooms:String(e.rooms)}))}}_vacStartFit(t){const e=this._vacSource(t),i=e?Yd(this.hass?.states[e]?.attributes):null;if(!e||!i)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._vacMapId(t,i),n=t.marker?.vacuum?.calibration?.[s],o=this._spaceModelById(t.space);if(!o)return;const a=o.vb,r=n&&6===n.length&&sp(n)||function(t,e){const i=[],s=[];for(const e of t)null!=e.x0?(i.push(e.x0,e.x1),s.push(e.y0,e.y1)):(i.push(e.cx),s.push(e.cy));if(!i.length)return{ox:e[0]+e[2]/2,oy:e[1]+e[3]/2,s:e[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(e[2],e[3])/l,rot:0,mir:!0},h=ip(c),[d,p]=Ld(h,(n+o)/2,(a+r)/2);return c.ox=e[0]+e[2]/2-d,c.oy=e[1]+e[3]/2-p,c}(i.rooms,a);this._markerDialog=null,(t.space===this._space||this._commitSpace(t.space))&&(this._vacFit={markerId:t.id,source:e,mapId:s,p:r,drag:null})}_vacFitSave(){const t=this._vacFit;if(!t)return;const e=this._vacSaveMatrix(t.markerId,t.source,t.mapId,ip(t.p));this._vacFit=null,e&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(t){const e=this._vacFit;if(!e)return;const i=Yd(this.hass?.states[e.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),n={...e.p,...t};this._vacFit={...e,p:np(n,e.p,s[0],s[1])}}_vacGhostCentre(t){const e=[],i=[];for(const s of t)e.push(s.x0??s.cx,s.x1??s.cx),i.push(s.y0??s.cy,s.y1??s.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...i)+Math.max(...i))/2]:[0,0]}_vacDelta(t,e,i){const s=this._stageEl,n=s?.clientWidth||1,o=s?.clientHeight||1;return[e/n*t.w,i/o*t.h]}_vacFitPointer(t,e){const i=this._vacFit;if(!i)return;if(t.stopPropagation(),"pointerdown"===t.type){const e=t.target,s=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this._vacFit={...i,drag:s?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:Number(s.split(",")[0]),fy:Number(s.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:0,fy:0}})}const s=i.drag;if(s){if("pointermove"===t.type){const[n,o]=this._vacDelta(e,t.clientX-s.sx,t.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 t=Yd(this.hass?.states[i.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),a=ip(s.p0),[r,l]=Ld(a,e[0],e[1]),[c,h]=Ld(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:np(g,s.p0,s.fx,s.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this._vacFit={...i,drag:null})}}_renderVacFit(t){const e=this._vacFit;if(!e)return U;const i=Yd(this._renderPlanHass?.states?.[e.source]?.attributes);if(!i)return U;const s=ip(e.p),n=[],o=[],a=[];for(const t of i.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>Ld(s,t,e));e.forEach(([t,e])=>{o.push(t),a.push(e)});const[i,r]=Ld(s,t.cx,t.cy);n.push(j`<polygon points="${e.map(t=>t[0].toFixed(1)+","+t[1].toFixed(1)).join(" ")}"></polygon>
|
||
<text x="${i.toFixed(1)}" y="${r.toFixed(1)}">${t.name}</text>`)}let r=U;if(i.pos){const[e,n]=Ld(s,i.pos.x,i.pos.y);r=j`<circle class="vacfitdot" cx="${e.toFixed(1)}" cy="${n.toFixed(1)}" r="${(.012*t.w).toFixed(1)}"></circle>`}const l=[];if(o.length){const e=(()=>{const t=s[0]*s[4]-s[1]*s[3];return(e,i)=>[(s[4]*(e-s[2])-s[1]*(i-s[5]))/t,(-s[3]*(e-s[2])+s[0]*(i-s[5]))/t]})(),i=Math.min(...o),n=Math.max(...o),r=Math.min(...a),c=Math.max(...a),h=.022*t.w,d=h/4;for(const[t,s,o,a]of[[i,r,n,c],[n,r,i,c],[n,c,i,r],[i,c,n,r]]){const i=e(o,a);l.push(j`<circle class="vacfithandle" data-corner="${i[0]+","+i[1]}"
|
||
cx="${t.toFixed(1)}" cy="${s.toFixed(1)}" r="${h.toFixed(1)}"></circle>
|
||
<circle class="vacfitknob" cx="${t.toFixed(1)}" cy="${s.toFixed(1)}" r="${d.toFixed(2)}"></circle>`)}}return W`<svg class="vacfit" viewBox="${t.x} ${t.y} ${t.w} ${t.h}"
|
||
preserveAspectRatio="none"
|
||
@pointerdown=${e=>this._vacFitPointer(e,t)}
|
||
@pointermove=${e=>this._vacFitPointer(e,t)}
|
||
@pointerup=${e=>this._vacFitPointer(e,t)}
|
||
@pointercancel=${e=>this._vacFitPointer(e,t)}>${n}${r}${l}</svg>`}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const t=this.renderRoot,e=this._stageEl,i=this._vacLastView,s=t?.querySelectorAll?.(".vacpuck")||[];if(!e||!i||!s.length)return void(this._vacRaf=0);const n=e.getBoundingClientRect();for(const e of s){const s=e.getAttribute("data-mid"),o=e.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 e of t.querySelectorAll(`line.tip[data-mid="${s}"]`))e.setAttribute("x2",a.toFixed(1)),e.setAttribute("y2",r.toFixed(1))}this._vacRafLoop()})}_renderVacuums(t,e){if(this._markup||"decor"===this._mode)return U;const i=this._space+"|"+e.x+"|"+e.y+"|"+e.w+"|"+e.h,s=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const n=[],o=[];for(const i of t){if(i.hidden||!this._isVacDev(i))continue;if("static_icon"===Bi(i.marker?.display))continue;const t=this._renderDeviceSnapshot?.facts.get(`vacuum:${i.id}`),a=t?.source??this._vacSource(i,this._renderPlanHass);if(!a)continue;const r=t?.telemetry??Yd(this._renderPlanHass?.states[a]?.attributes);if(!r)continue;const l=String(t?.mapId??this._vacMapId(i,r,this._renderPlanHass)),c=i.marker?.vacuum?.calibration?.[l];if(!c||6!==c.length)continue;const h=t?.runtime??this._vacRt.get(i.id),d=h?.moving??!1,p=op(i.marker?.vacuum),u="always"===p||"cleaning"===p&&d,_=t?.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 t=g.points.map(([t,e])=>{const[i,s]=Ld(c,t,e),n=this._scenePoint([i,s]);return n[0].toFixed(1)+","+n[1].toFixed(1)}).join(" ");o.push(j`<g class="prev"><polyline class="case" points="${t}"></polyline><polyline class="core" points="${t}"></polyline></g>`)}if(u){const t=Vd(r,m,h?.trail||[]),e=!d||"integration"!==t.source&&"server"!==t.source?t.path:Kd(t.path);if(e.length){const t=e.map(t=>t.map(([t,e],i)=>{const[s,n]=Ld(c,t,e),o=this._scenePoint([s,n]);return`${i?"L":"M"} ${o[0].toFixed(1)} ${o[1].toFixed(1)}`}).join(" ")).join(" ");o.push(j`<path class="case" d="${t}"></path><path class="core" d="${t}"></path>`);const s=e[e.length-1];if(d&&s?.length>=2){const t=s[s.length-1],[e,n]=Ld(c,t[0],t[1]),a=this._scenePoint([e,n]),r=a[0].toFixed(1),l=a[1].toFixed(1);o.push(j`<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]=Ld(c,r.pos.x,r.pos.y),y=this._scenePoint([f,v]),b=(y[0]-e.x)/e.w*100,w=(y[1]-e.y)/e.h*100,k=h&&h.lastTs>0&&Date.now()-h.lastTs>6e4,x=i.marker?.icon||i.icon||"mdi:robot-vacuum";n.push(W`<div
|
||
data-mid="${i.id}"
|
||
class="vacpuck ${Go(this._renderPlanHass)} ${h?.jump||s?"jump":""} ${k?"stale":""}"
|
||
style="left:${b}%;top:${w}%"
|
||
title=${i.name}
|
||
@click=${t=>{t.stopPropagation();const e=this._vacEntity(i);e&&this._openMoreInfo(e)}}>
|
||
<ha-icon .icon=${x}></ha-icon>
|
||
</div>`)}return this._vacLastView=e,n.length&&!this._vacRaf&&this._vacRafLoop(),n.length||o.length?W`
|
||
${o.length?j`<svg class="vactrail" viewBox="${e.x} ${e.y} ${e.w} ${e.h}" preserveAspectRatio="none">${o}</svg>`:U}
|
||
${n}`:U}_renderDevice(t,e,i=!0){const s=this._pos(t),n=this._scenePoint([s.x,s.y]),o=(n[0]-e.x)/e.w*100,a=(n[1]-e.y)/e.h*100,r=this._devicePresentation(t,i),l=[`left:${o}%`,`top:${a}%`,...Uo(r)],c=r.disabledReason,h=r.haDisabled?this._t(`marker.ha_disabled_${c}`):t.userHidden?this._t("marker.hidden_ghost"):t.name,d=e_(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||"",Ro(r.valueBadge),null!=r.lqiText&&r.lqiBand?this._t(`marker.lqi_a11y_${r.lqiBand}`,{value:r.lqiText}):""].filter(Boolean).join(", "),_=[t.model,r.valueBadge?.fullText||"",null!=r.lqiText?"LQI "+r.lqiText:""].filter(Boolean).join(" · ");return W`<div
|
||
${""}
|
||
data-hp="device"
|
||
data-id="${t.id}"
|
||
data-entity=${t.primary||U}
|
||
data-area=${t.area||U}
|
||
data-binding-status=${r.haDisabled?"ha-disabled":t.bindingStatus?.kind||"active"}
|
||
data-disabled-reason=${c?c.replace("_","-"):U}
|
||
data-state=${d}
|
||
data-lqi-band=${null!=r.lqiText&&r.lqiBand||U}
|
||
role=${p?"button":U}
|
||
tabindex=${p?"0":U}
|
||
aria-label=${u}
|
||
class="dev ${Go(this._renderPlanHass)} ${r.classes.join(" ")} ${this._selId===t.id?"sel":""} ${t.virtual?"virtual":""} ${t.hidden?"ghost":""} ${r.haDisabled?"ha-disabled":""} ${null!=r.valueText?"valonly":""}"
|
||
style="${l.join(";")}"
|
||
@click=${e=>this._clickDevice(e,t)}
|
||
@keydown=${e=>this._keyDevice(e,t)}
|
||
@contextmenu=${e=>this._ctxDevice(e,t)}
|
||
@pointerover=${e=>this._showTip(e,t.name,r.haDisabled?h:_)}
|
||
@pointerleave=${()=>this._clearTransientHover()}
|
||
@pointerdown=${e=>this._pointerDown(e,t)}
|
||
@pointermove=${e=>{this._pointerMove(e,t),this._showTip(e,t.name,r.haDisabled?h:_)}}
|
||
@pointerup=${e=>this._pointerUp(e,t)}
|
||
@pointercancel=${e=>this._pointerUp(e,t)}
|
||
>
|
||
${Ko(r,{newDevice:this._newIds.has(t.id),newDeviceTitle:this._t("device.new"),disabledTitle:r.haDisabled?h:""})}
|
||
</div>`}_roomArea(t){const e=hi(t);if(!e)return null;const i=this._spaceModel();if(!i)return null;const s=this._spaceWalls,n=s.length&&t.id&&_c(i.rooms,t.id,s,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,Pf,this._wallUnionGeometry()?.roomGeom)||e,o=this._cleanFloor(t,n),a=this._cellCm/this._gridPitch;return ir(o.area*a*a/1e4,"mi"===this._renderPlanHass?.config?.unit_system?.length)}_roomTemp(t){const e=t.settings?.temp_source;return e?Mo(this._renderPlanHass,e,"temp",this._markers):t.area?this._climate().get(t.area)?.temp??null:null}_roomHum(t){const e=t.settings?.hum_source;return e?Mo(this._renderPlanHass,e,"hum",this._markers):t.area?this._climate().get(t.area)?.hum??null:null}_climate(){const t=this._serverCfg?.markers,e=this._renderPlanHass,i=this._climateCache;if(i&&i.h===e&&i.r===this._iconRules&&i.mk===t)return i.m;const s=function(t,e,i){const s=new Map;if(!t?.entities)return s;const n=wo(i),o=new Set;for(const t of i||[]){if(t?.removed||!0!==t?.use_climate_temp)continue;const e=(t.binding||"").indexOf(":");e>0&&o.add(t.binding.slice(e+1))}const a=new Map;for(const[e,i]of Object.entries(t.entities)){if(i.device_id&&n.devices.has(i.device_id)&&!n.liveEntities.has(e)||!i.device_id&&n.entities.has(e)&&!n.liveEntities.has(e))continue;const s=i.device_id?t.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&&e.startsWith("climate.")&&(o.has(e)||i.device_id&&o.has(i.device_id)))){if(Yt.has(i.platform))continue;if(Co.test(e))continue}let l=a.get(r);l||(l=new Map,a.set(r,l));const c=i.device_id||e;let h=l.get(c);if(!h){const n=t.states?.[e];h={name:(s?s.name_by_user||s.name:i.name||n?.attributes?.friendly_name||e)||e,model:s?.model,ents:[]},l.set(c,h)}h.ents.push(e)}for(const[i,n]of a){const a=[],r=[];for(const[i,s]of n){const n=bo(t,s.name,s.model,s.ents,e),l="mdi:thermometer"===n||"mdi:air-filter"===n;if(l){const e=mo(t,s.ents);null!=e&&a.push(e)}if(o.size>0&&(o.has(i)||s.ents.some(t=>o.has(t)))){const e=go(t,s.ents);null!=e&&a.push(e)}if(l||"mdi:water-percent"===n){const e=vo(t,s.ents);null!=e&&r.push(e)}}(a.length||r.length)&&s.set(i,{temp:a.length?Math.round(a.reduce((t,e)=>t+e,0)/a.length*10)/10:null,hum:r.length?Math.round(r.reduce((t,e)=>t+e,0)/r.length):null})}return s}(e,this._iconRules,t);return this._climateCache={h:e,r:this._iconRules,mk:t,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(t){if(!t.id)return;this._roomEditId=t.id,this._nameSel=t.name||"",this._areaSel=t.area||"",this._roomFill="glow"===t.settings?.fill_mode?"":t.settings?.fill_mode||"";const e=t.settings?.custom_fill;this._roomCustomFill=e&&"object"==typeof e?rs(e,cs(this._curSpaceCfg).customFill):null,this._roomTempSrc=t.settings?.temp_source||"",this._roomHumSrc=t.settings?.hum_source||"",this._roomNameScale=Es(t.settings?.name_scale),this._roomLabelScale=Es(t.settings?.label_scale),this._roomSrcOpen=null,this._roomSrcFilter="",this._roomDialog=!0}_roomSettingsFromDialog(){const t={};return this._roomFill&&(t.fill_mode=this._roomFill),this._roomCustomFill&&(t.custom_fill=this._roomCustomFill),this._roomTempSrc&&(t.temp_source=this._roomTempSrc),this._roomHumSrc&&(t.hum_source=this._roomHumSrc),1!==this._roomNameScale&&(t.name_scale=this._roomNameScale),1!==this._roomLabelScale&&(t.label_scale=this._roomLabelScale),Object.keys(t).length?t:null}_saveRoomEdit(){const t=this._curSpaceCfg,e=t?.rooms.find(t=>t.id===this._roomEditId);if(!e)return this._roomDialog=!1,void(this._roomEditId=null);e.name=this._nameSel.trim()||e.name,e.area=this._areaSel||null;const i=e.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?e.settings=s:delete e.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 t=this._planHass,e=wo(this._markers),i=this._roomSrcFilter.trim().toLowerCase(),s=[];for(const n of Object.values(t.devices)){if("service"===n.entry_type)continue;if(e.devices.has(n.id))continue;const t=(n.name_by_user||n.name||n.id).trim();i&&!t.toLowerCase().includes(i)||s.push({value:"device:"+n.id,label:t,sub:n.model||this._t("marker.sub_device")})}for(const[n,o]of Object.entries(t.entities)){if(!n.startsWith("sensor.")||o.hidden)continue;if(ko(t,n,e))continue;const a=o.name||t.states[n]?.attributes?.friendly_name||n;i&&!(a+" "+n).toLowerCase().includes(i)||s.push({value:"entity:"+n,label:a,sub:n})}return s.sort((t,e)=>t.label.localeCompare(e.label)),s.slice(0,200)}_roomSrcLabel(t){const e=t.indexOf(":"),i=t.slice(0,e),s=t.slice(e+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(t,e){const i=this._layout["rl_"+(t.id||"")];if(i&&i.s===e)return{x:i.x*Pf,y:i.y*Pf};const s=this._snap(this._roomCenter(t));return{x:s[0],y:s[1]}}_labelDown(t,e,i){if("plan"!==this._mode)return;t.preventDefault(),t.stopPropagation();const s=this._labelPos(e,i);this._drag={id:"rl_"+(e.id||""),sx:t.clientX,sy:t.clientY,ox:s.x,oy:s.y,moved:!1},Nf(t),this._tip=null}_labelMove(t,e,i){const s="rl_"+(e.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=(t.clientX-this._drag.sx)/r.width*l.w,h=(t.clientY-this._drag.sy)/r.height*l.h;Math.abs(t.clientX-this._drag.sx)+Math.abs(t.clientY-this._drag.sy)>3&&(this._drag.moved=!0);const d=Rh(this._drag.ox+c),p=Rh(this._drag.oy+h);this._savePos({id:s,space:i},d,p)}_labelUp(t){const e="rl_"+(t.id||"");if(!this._drag||this._drag.id!==e)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&window.setTimeout(()=>this._drag=null,0)}_labelScale(t){const e=this._layout["rl_"+(t.id||"")]?.k;return"number"==typeof e&&Number.isFinite(e)?Math.min(3,Math.max(.5,e)):1}_rlResizeDown(t,e,i){if("plan"!==this._mode)return;t.preventDefault(),t.stopPropagation();const s=t.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(t.clientX-o,t.clientY-a));this._rlResize={id:"rl_"+(e.id||""),space:i,k0:this._labelScale(e),cx:o,cy:a,d0:r},Nf(t)}_rlResizeMove(t){const e=this._rlResize;if(!e)return;t.stopPropagation();const i=Math.max(8,Math.hypot(t.clientX-e.cx,t.clientY-e.cy)),s=Math.min(3,Math.max(.5,e.k0*(i/e.d0))),n=this._layout[e.id];if(n)this._layout={...this._layout,[e.id]:{...n,k:s}};else{const t=e.id.slice(3),i=this._spaceModelById(e.space);if(!i)return;const n=i.rooms.find(e=>e.id===t);if(!n)return;const o=this._labelPos(n,e.space);this._layout={...this._layout,[e.id]:{s:e.space,x:o.x/Pf,y:o.y/Pf,k:s}}}this._dirtyPos.add(e.id)}_rlResizeUp(){this._rlResize&&(this._rlResize=null,this._persistLayout())}_renderRoomGear(t,e,i){if(!t.id)return U;let s=null;if(t.poly?(s=this._gearPtCache.get(t.poly)||null,s||(s=$i(t.poly),this._gearPtCache.set(t.poly,s))):null!=t.x&&null!=t.y&&(s=[t.x+(t.w||0)/2,t.y+(t.h||0)/2]),!s)return U;const n=(s[0]-i.x)/i.w*100,o=(s[1]-i.y)/i.h*100;return W`<button class="rlgearbtn" data-hp="room-settings" data-room=${t.id}
|
||
style="left:${n}%;top:${o}%"
|
||
title=${this._t("room.settings_title")}
|
||
@pointerdown=${t=>t.stopPropagation()}
|
||
@click=${e=>{e.stopPropagation(),this._openRoomEdit(t)}}>
|
||
<ha-icon icon="mdi:cog-outline"></ha-icon>
|
||
<span class="rlgeartext">${this._t("room.settings_short")}</span>
|
||
</button>`}_renderRoomLabel(t,e,i,s){if(!t.name&&!this._markup)return U;const n=this._labelPos(t,e.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(t),h=[];if(t.area||t.settings?.temp_source||t.settings?.hum_source||s.labelLight){if(s.labelTemp){const e=this._roomTemp(t);null!=e&&h.push(W`<span class="rlm"><ha-icon icon="mdi:thermometer"></ha-icon>${e}°</span>`)}if(s.labelHum){const e=this._roomHum(t);null!=e&&h.push(W`<span class="rlm"><ha-icon icon="mdi:water-percent"></ha-icon>${e}%</span>`)}if(s.labelLqi&&t.area){const e=this._roomLqi(t.area);null!=e&&h.push(W`<span class="rlm"><ha-icon icon="mdi:zigbee"></ha-icon>${e}</span>`)}if(s.labelLight){const e=(d=ho(this._renderPlanHass,this._renderDevices,t,this._virtualLights)).length?{on:d.filter(t=>t.on).length,total:d.length}:null;if(e){const t=0===e.on?this._t("roomcard.light_off"):e.on===e.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:e.on,total:e.total});h.push(W`<span class="rlm ${e.on?"lit":""}"><ha-icon icon=${e.on?"mdi:lightbulb-on":"mdi:lightbulb-outline"}></ha-icon>${t}</span>`)}}}var d;const p=!!t.area,u=!this._markup;return W`<div class="roomlabel ${h.length?"card":""}"
|
||
data-hp="room-label" data-id=${t.id||U} data-area=${t.area||U}
|
||
style="left:${a}%;top:${r}%;color:${s.color};opacity:${l};--rl-scale:${c};--rl-space:${s.cardFontScale};--rl-name:${Es(t.settings?.name_scale)};--rl-meta:${Es(t.settings?.label_scale)}"
|
||
@pointerdown=${i=>this._labelDown(i,t,e.id)}
|
||
@pointermove=${i=>this._labelMove(i,t,e.id)}
|
||
@pointerup=${()=>this._labelUp(t)}
|
||
@pointercancel=${()=>this._labelUp(t)}
|
||
><span class="rlname">${t.name||(this._markup?this._t("room.unnamed"):"")}${p?W`<ha-icon class="rlgo" icon="mdi:open-in-new"
|
||
title=${u?this._t("room.open_area"):U}
|
||
@click=${u?e=>{e.stopPropagation(),this._clickRoom(t)}:U}
|
||
@pointerdown=${u?t=>t.stopPropagation():U}></ha-icon>`:U}</span>
|
||
${h.length?W`<span class="rlmetrics">${h}</span>`:U}
|
||
${"plan"===this._mode?["tl","tr","bl","br"].map(i=>W`<span class="rlhandle ${i}"
|
||
@pointerdown=${i=>this._rlResizeDown(i,t,e.id)}
|
||
@pointermove=${t=>this._rlResizeMove(t)}
|
||
@pointerup=${()=>this._rlResizeUp()}
|
||
@pointercancel=${()=>this._rlResizeUp()}></span>`):U}
|
||
</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(t){const e=this._measureAnchor,i=this._cursorPt,s=(i[0]-t.x)/t.w*100,n=(i[1]-t.y)/t.h*100,o=Ns(e,i),a=Math.round(10*o)/10,r=function(t,e,i=.001){if(t.length<2||e.length<2)return!1;const s=Math.abs(e[0]-t[0]),n=Math.abs(e[1]-t[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}(e,i,2e-4*this._gridPitch);return W`<div class="measurelabel ${r?"on45":""}" style="left:${s}%;top:${n}%">
|
||
${this._fmtLen(e,i)} · ${a}°</div>`}get _decorMeasure(){const t=this._decorDraft;if(!t||"decor"!==this._mode)return null;const[e,i]=t.a,[s,n]=t.b;if(Math.abs(e-s)<1e-6&&Math.abs(i-n)<1e-6)return null;const o=(e+s)/2,a=(i+n)/2;if("line"===t.kind){const e=Ns(t.a,t.b);return{x:o,y:a,on45:Os(e),text:`${this._fmtLen(t.a,t.b)} · ${Math.round(10*e)/10}°`}}const r=this._fmtLen([e,i],[s,i]),l=this._fmtLen([s,i],[s,n]);if("ellipse"===t.kind){const t=this._fmtLen([0,0],[Math.abs(s-e)/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-e)-Math.abs(n-i))<1e-6?`R ${t}`:`Rx ${t} × Ry ${r}`}}return{x:o,y:a,on45:!1,text:`${r} × ${l} · ${ir(Er(Math.abs(s-e),this._cellCm,this._gridPitch)*Er(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 t=this._drag.id.slice(3),e=this._spaceModel()?.rooms.find(e=>e.id===t);return e?(()=>{const t=this._labelPos(e,this._space);return[t.x,t.y]})():null}return null}if("devices"===this._mode&&this._drag?.moved){const t=this._devices.find(t=>t.id===this._drag.id);return t?(()=>{const e=this._pos(t);return[e.x,e.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const t=this._decorList.find(t=>t.id===this._decorMove.id);if(!t)return null;const e=Pf,i=this._decorH;return"line"===t.kind?[t.x1*e,t.y1*i]:[t.x*e,t.y*i]}return null}return null}_alignCandidates(){const t=[],e=this._spaceModel();if(this._markup){if(!e)return t;if(this._drag?.id.startsWith("rl_")){const i=this._drag.id.slice(3);for(const s of e.rooms){if(!s.name||s.id===i)continue;const e=this._labelPos(s,this._space);t.push([e.x,e.y])}return t}for(const i of e.rooms){const e=hi(i);if(e)for(const i of e)t.push(i)}if("draw"===this._tool)for(const e of this._path)t.push(e);if("split"===this._tool&&this._splitSel?.pts)for(const e of this._splitSel.pts)t.push(e);return t}if("devices"===this._mode){for(const e of this._devices){if(e.space!==this._space||e.id===this._drag?.id||"ha_disabled"===e.bindingStatus?.kind)continue;const i=this._pos(e);t.push([i.x,i.y])}return t}if("decor"===this._mode){const e=this._decorMove?.id;return t.push(...this._decorSnapGeometry(e).points),this._decorDraft&&t.push(this._decorDraft.a),t}return t}_renderAlignGuides(){const t=this._alignPoint;if(!t)return j``;const e=this._drag?.id.startsWith("rl_")?.5*this._gridPitch:.05*this._gridPitch,i=function(t,e,i){let s=null,n=null;for(const o of e)if(!(Math.abs(o[0]-t[0])<1e-6&&Math.abs(o[1]-t[1])<1e-6)){if(Math.abs(o[0]-t[0])<=i){const e=Math.abs(o[1]-t[1]);e>1e-6&&(!s||e<s.d)&&(s={d:e,c:o})}if(Math.abs(o[1]-t[1])<=i){const e=Math.abs(o[0]-t[0]);e>1e-6&&(!n||e<n.d)&&(n={d:e,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}(t,this._alignCandidates(),e);if(!i.length)return j``;const s=this._gridPitch,n=1.5*s;return j`<g class="alignguides">
|
||
${i.map(e=>{const[i,o,a,r]="x"===e.axis?[e.at,e.from[1],e.at,t[1]+Math.sign(t[1]-e.from[1])*n]:[e.from[0],e.at,t[0]+Math.sign(t[0]-e.from[0])*n,e.at];return j`<line class="alignline" x1="${i}" y1="${o}" x2="${a}" y2="${r}"></line>
|
||
<circle class="aligndot" cx="${e.from[0]}" cy="${e.from[1]}"
|
||
r="${_h(.18*s,this._cellCm)}"></circle>`})}
|
||
</g>`}_renderOpeningCenterTick(t){const e=(t.angle+90)*Math.PI/180,i=_h(15,this._cellCm);return j`<line class="alignline opcentertick"
|
||
x1="${t.x-Math.cos(e)*i}" y1="${t.y-Math.sin(e)*i}"
|
||
x2="${t.x+Math.cos(e)*i}" y2="${t.y+Math.sin(e)*i}"></line>`}_renderOpeningDimensionGuides(t){const e=t.labels.flatMap(t=>t.dimension?[t.dimension]:[]);if(!e.length)return j``;const i=this._cssPxToRender(4);return j`<g class="opening-dimensions" aria-hidden="true" pointer-events="none">
|
||
${e.map(t=>{const e=-t.axis[1],s=t.axis[0],n=(t,n)=>j`<line
|
||
class="opening-dimension-tick" data-end=${n}
|
||
x1=${t[0]-e*i} y1=${t[1]-s*i}
|
||
x2=${t[0]+e*i} y2=${t[1]+s*i}></line>`;return j`<g class="opening-dimension" data-source=${t.source}
|
||
data-room=${t.roomId||U}>
|
||
<line class="opening-dimension-line"
|
||
x1=${t.from[0]} y1=${t.from[1]}
|
||
x2=${t.to[0]} y2=${t.to[1]}></line>
|
||
${n(t.from,"from")}${n(t.to,"to")}
|
||
</g>`})}
|
||
</g>`}_roomCenter(t){if(t.poly){const e=t.poly.length;return[t.poly.reduce((t,e)=>t+e[0],0)/e,t.poly.reduce((t,e)=>t+e[1],0)/e]}return[t.x+t.w/2,t.y+.1*Math.min(t.w,t.h)]}_openingAmt(t){const e=t.contact&&this._renderOpeningEntityAvailable(t.contact)?this._renderPlanHass.states[t.contact]?.state:null;return mi(t.type,e,!!t.invert)}_planEntityAvailable(t){return!!t&&(!ko(this._fullRegistryHass,t,wo(this._markers))&&"active"===this._bindingStatus("entity:"+t).kind)}_renderEntityAvailable(t){return!!t&&(!ko(this._renderPlanHass,t,wo(this._markers))&&(!!this._renderPlanHass.entities?.[t]&&!!this._renderPlanHass.states?.[t]))}_openingEntityAvailable(t){return function(t,e,i=An(t)){return!!e&&"active"===Bn(t,`entity:${e}`,i).kind}(this.hass,t,this._haRegistry)}_renderOpeningEntityAvailable(t){return e=this._renderPlanHass,!!(i=t)&&!!e?.states?.[i];var e,i}_renderOpeningPlacementPreview(){const t=this._openingPreview;if(!t)return j``;const e={type:t.type,length:t.renderedLength,angle:t.angle,amount:mi(t.type,null),flipH:t.flipH,flipV:t.flipV,base:"var(--hp-open)",tone:"var(--hp-open)",cellCm:this._cellCm,gridPitch:this._gridPitch,face:t.face},i="passage"===t.type?function(t,e){const i=Math.max(0,t.renderedLength)/2,s=Math.max(0,t.target.physicalHalfWidth),n=s+.18*Math.max(0,e),o=t=>({x1:t,y1:-n,x2:t,y2:n});return{rect:{x:-i,y:s?-s:0,width:2*i,height:2*s},boundaries:[o(-i),o(i)]}}(t,_h(this._gridPitch,this._cellCm)):null;return j`<g class="opening-preview" data-kind=${t.type}
|
||
aria-hidden="true" pointer-events="none"
|
||
transform="translate(${t.x} ${t.y}) rotate(${t.angle})">
|
||
${i?j`
|
||
<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(t=>j`
|
||
<line class="passage-preview-boundary" pointer-events="none"
|
||
x1=${t.x1} y1=${t.y1}
|
||
x2=${t.x2} y2=${t.y2}></line>`)}
|
||
`:Zu(e)}
|
||
</g>
|
||
<circle class="opening-preview-dot opghost-dot" aria-hidden="true" pointer-events="none"
|
||
cx=${t.x} cy=${t.y}
|
||
r=${_h(.18*this._gridPitch,this._cellCm)}></circle>`}_renderOpenings(t){const e=this._openingsR;if(!e.length)return j``;const i=this._spaceModel();if(!i)return j``;const s=t.color,n=this._spaceWalls,o=this._openPairs().flatMap(t=>t.segs),a=this._openingWallIndexFor(i,o).value;return j`${e.map(t=>{if(t.orphanReason)return j`<g class="opening orphan" data-hp="opening-orphan"
|
||
data-id=${t.id} role="button" tabindex="0"
|
||
aria-label=${this._t("opening.partition_orphan")}
|
||
transform="translate(${t.rx} ${t.ry})"
|
||
@click=${e=>{e.stopPropagation(),this._editOpening(t)}}>
|
||
<circle r=${_h(.55*this._gridPitch,this._cellCm)}></circle>
|
||
<text text-anchor="middle" dominant-baseline="central">!</text>
|
||
</g>`;const e=this._openingAmt(t),i=e>0&&!!t.contact&&this._renderOpeningEntityAvailable(t.contact)?"var(--hp-open)":s,o="gate"===t.type?!t.flip_v:t.flip_v,r=t.partitionHost||n.length||"gate"===t.type?this._openingFace(t,a,!!o):{ox:0,oy:0,cm:0,side:-1},l={type:t.type,length:t.rlen,angle:t.angle,amount:e,flipH:!!t.flip_h,flipV:!!t.flip_v,base:s,tone:i,cellCm:this._cellCm,gridPitch:this._gridPitch,face:r},{half:c,outlineHalf:h,hitHalf:d}=Xu(l),p=_h(10,this._cellCm),u=_h(12,this._cellCm);return j`<g class="opening" data-hp="opening" data-id="${t.id}" data-kind="${t.type}"
|
||
transform="translate(${t.rx} ${t.ry}) rotate(${t.angle})">
|
||
${Zu(l)}
|
||
<rect class="op-outline" x="${-c-p}" y="${-h}"
|
||
width="${t.rlen+2*p}" height="${2*h}"
|
||
rx="${_h(6,this._cellCm)}"></rect>
|
||
<rect class="op-hit" x="${-c-u}" y="${-d}"
|
||
width="${t.rlen+2*u}" height="${2*d}"
|
||
@click=${e=>this._opClick(e,t)}
|
||
@pointerdown=${e=>this._opPointerDown(e,t)}
|
||
@pointermove=${e=>this._opPointerMove(e,t)}
|
||
@pointerup=${e=>this._opPointerUp(e,t)}
|
||
@pointercancel=${e=>this._opPointerUp(e,t)}></rect>
|
||
</g>`})}`}_renderOpeningLocks(t){const e=this._openingsR.filter(t=>!t.orphanReason&&("door"===t.type||"gate"===t.type)&&t.lock&&this._renderOpeningEntityAvailable(t.lock));if(!e.length)return W``;const i=this._spaceModel();if(!i)return W``;const s=this._openPairs().flatMap(t=>t.segs),n=this._openingWallIndexFor(i,s).value;return W`${e.map(e=>{const i=this._renderPlanHass.states[e.lock]?.state,s="locked"===i,o=s||["unlocked","open","opening","unlocking","locking"].includes(String(i)),a=(e.angle+90)*Math.PI/180,r="gate"===e.type?this._openingFace(e,n,!e.flip_v):null,l=_h(16,this._cellCm),c=r?-l*r.side:l*(e.flip_v?-1:1),h=e.rx+Math.cos(a)*c,d=e.ry+Math.sin(a)*c,p=this._scenePoint([h,d]),u=(p[0]-t.x)/t.w*100,_=(p[1]-t.y)/t.h*100;return W`<div class="oplock ${Go(this._renderPlanHass)} ${s?"locked":o?"unlocked":"unknown"}"
|
||
style="left:${u}%;top:${_}%"
|
||
@click=${t=>{t.stopPropagation(),"view"===this._mode&&(this._openingInfo=e)}}>
|
||
<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(t,e){if(this._openingEntityAvailable(t)){if("unlock"===e){const e=this.hass?.states?.[t]?.attributes?.friendly_name||t;if(!confirm(this._t("confirm.unlock",{name:e})))return}this.hass?.callService?.("lock",e,{entity_id:t})}}_renderOpeningInfoCard(){const t=this._openingInfo,e="passage"!==t.type&&t.contact&&this._openingEntityAvailable(t.contact)?t.contact:null,i=("door"===t.type||"gate"===t.type)&&t.lock&&this._openingEntityAvailable(t.lock)?t.lock:null,s=e?this.hass.states[e]?.state:null,n=this._openingAmt(t),o=i?this.hass.states[i]?.state:null,a="door"===t.type?"opening.door":"gate"===t.type?"opening.gate":"passage"===t.type?"opening.passage":"opening.window",r="door"===t.type?"mdi:door":"gate"===t.type?"mdi:gate":"passage"===t.type?"mdi:arch":"mdi:window-closed-variant",l="gate"===t.type?n>0?"mdi:gate-open":"mdi:gate":n>0?"mdi:door-open":"mdi:door-closed",c=(t,e,i,s="")=>W`<div class="oprow ${s}"><ha-icon icon=${t}></ha-icon><span>${e}</span><b>${i}</b></div>`;return W`<hp-dialog .hass=${this.hass}
|
||
.title=${this._t(a)} icon=${r} dismiss-on-scrim
|
||
@hp-close=${()=>this._openingInfo=null}>
|
||
<div class="body">
|
||
${e?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"):U}
|
||
${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"):U}
|
||
${i&&("locked"===o||["unlocked","open"].includes(String(o)))?W`<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))?W`<button class="btn lockact" disabled>
|
||
<ha-icon icon="mdi:timer-sand"></ha-icon>${this._t("opening.lock_pending")}
|
||
</button>`:U}
|
||
${e||i?U:W`<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 t=this._openingDialog,e=this._spaceModel()?.partitions||[],i=t.id?this._curSpaceCfg?.openings?.find(e=>e.id===t.id):null,s={id:t.id||"preview",type:t.type,x:t.x/Pf,y:t.y/this._spaceH,angle:t.angle,length:i&&!t.lengthTouched?i.length:this._cmToUnits(Math.max(20,t.lengthCm))/Pf,...t.host?{host:t.host}:{}},n=Op(i,s),o=t.host?n?Np(s,e,Pf,this._cellCm,this._gridPitch):Ep(s,e,Pf,this._cellCm,this._gridPitch):null,a="does-not-fit-jamb"===o?.reason,r=t.host?e.find(e=>e.id===t.host.id):null,l=r?li(r.cm/2,this._imperial):"",c=!!t.host&&!o?.resolved&&!a,h="gate"===t.type?"mdi:gate":"window"===t.type?"mdi:window-closed-variant":"passage"===t.type?"mdi:arch":"mdi:door",d=(e,i)=>{const s="contact"===e?t.contact:t.lock,n="contact"===e?!!t.contactOpen:!!t.lockOpen,o="contact"===e?t.contactFilter||"":t.lockFilter||"",a=i.find(t=>t.value===s),r=a?.label||this.hass.states[s]?.attributes?.friendly_name||this._fullRegistryHass.entities[s]?.name||s,l=function(t,e,i=200){const s=e.trim().toLowerCase();return(s?t.filter(t=>t.label.toLowerCase().includes(s)||t.value.toLowerCase().includes(s)):t).slice(0,Math.max(0,i))}(i,o);return W`
|
||
<button type="button" class="dropbtn opening-entity-drop ${n?"open":""}"
|
||
data-opening-picker=${e} aria-expanded=${n?"true":"false"}
|
||
@click=${()=>this._toggleOpeningEntityPicker(e)}>
|
||
${s?W`<b>${r}</b><span class="ref">${s}</span>`:W`<span class="muted">${this._t("opening.none")}</span>`}
|
||
<ha-icon icon=${n?"mdi:chevron-up":"mdi:chevron-down"}></ha-icon>
|
||
</button>
|
||
${n?W`<div class="droppanel opening-entity-panel" data-opening-panel=${e}>
|
||
<input class="namein opening-entity-search" type="text"
|
||
placeholder=${this._t("opening.search_ph")} .value=${o}
|
||
@input=${t=>this._filterOpeningEntities(e,t.target.value)} />
|
||
<div class="candlist">
|
||
<button type="button" class="cand opening-entity-candidate ${s?"":"sel"}"
|
||
data-opening-entity="" @click=${()=>this._selectOpeningEntity(e,"")}>
|
||
<span class="cl">${this._t("opening.none")}</span>
|
||
</button>
|
||
${l.map(t=>W`
|
||
<button type="button"
|
||
class="cand opening-entity-candidate ${t.value===s?"sel":""}"
|
||
data-opening-entity=${t.value}
|
||
@click=${()=>this._selectOpeningEntity(e,t.value)}>
|
||
<span class="cl">${t.label}</span>
|
||
<span class="cs">${t.value}</span>
|
||
</button>`)}
|
||
${l.length?U:W`<div class="cand muted opening-entity-empty">${this._t("marker.nothing_found")}</div>`}
|
||
</div>
|
||
</div>`:U}`};return W`<hp-dialog .hass=${this.hass} wide
|
||
.title=${t.id?this._t("opening.edit"):this._t("opening.new")} icon=${h}
|
||
@hp-close=${()=>this._openingDialog=null}>
|
||
<div class="body">
|
||
${t.host?W`<label>${this._t("opening.host_partition")}</label>
|
||
<div class=${c||a?"habindingbanner":"rhint"}
|
||
role=${c||a?"status":U}>
|
||
${c||a?W`<ha-icon icon="mdi:alert-outline"></ha-icon>`:U}
|
||
<span>${c?this._t("opening.partition_orphan"):a?this._t("opening.partition_jamb_margin",{distance:l}):this._t("opening.host_partition")}</span>
|
||
</div>`:U}
|
||
<label>${this._t("opening.type_label")}</label>
|
||
<label class="srcrow"><input type="radio" name="optype" .checked=${"window"===t.type}
|
||
@change=${()=>this._openingDialog={...t,type:"window",lengthCm:t.id?t.lengthCm:Lg("window"),contactOpen:!1,lockOpen:!1}} />
|
||
<span>${this._t("opening.window")}</span></label>
|
||
<label class="srcrow"><input type="radio" name="optype" .checked=${"door"===t.type}
|
||
@change=${()=>this._openingDialog={...t,type:"door",lengthCm:t.id?t.lengthCm:Lg("door"),contactOpen:!1,lockOpen:!1}} />
|
||
<span>${this._t("opening.door")}</span></label>
|
||
<label class="srcrow"><input type="radio" name="optype" .checked=${"passage"===t.type}
|
||
@change=${()=>this._openingDialog={...t,type:"passage",lengthCm:t.id?t.lengthCm:Lg("passage"),contactOpen:!1,lockOpen:!1}} />
|
||
<span>${this._t("opening.passage")}</span></label>
|
||
<label class="srcrow"><input type="radio" name="optype" .checked=${"gate"===t.type}
|
||
@change=${()=>this._openingDialog={...t,type:"gate",lengthCm:t.id?t.lengthCm:Lg("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(t.lengthCm)}
|
||
@input=${e=>{const i=mf(e.target.value);null!=i&&(this._openingDialog={...t,lengthCm:i,lengthTouched:!0})}} />
|
||
|
||
${"passage"===t.type&&(t.contact||t.lock)?W`<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>`:U}
|
||
|
||
${"passage"!==t.type?W`<label>${this._t("opening.contact_label")}</label>
|
||
${d("contact",this._contactCandidates())}
|
||
${t.contact?W`<label class="srcrow">${this._boolInput(t.invert,e=>this._openingDialog={...t,invert:e})}
|
||
<span>${this._t("opening.invert")}</span></label>`:U}`:U}
|
||
|
||
${"door"===t.type||"gate"===t.type?W`<label>${this._t("opening.lock_label")}</label>
|
||
${d("lock",this._lockCandidates())}`:U}
|
||
|
||
${"gate"!==t.type&&"passage"!==t.type?W`<label class="srcrow">${this._boolInput(t.flipH,e=>this._openingDialog={...t,flipH:e})}
|
||
<span>${this._t("opening.flip_h")}</span></label>`:U}
|
||
${"passage"!==t.type?W`<label class="srcrow">${this._boolInput(t.flipV,e=>this._openingDialog={...t,flipV:e})}
|
||
<span>${this._t("opening.flip_v")}</span></label>`:U}
|
||
</div>
|
||
<div class="row dialog-action-footer" slot="footer">
|
||
${t.id?W`<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>`:U}
|
||
${c?W`<button class="btn ghost" @click=${this._rebindPartitionOpening}>
|
||
${this._t("opening.rebind_partition")}
|
||
</button>`:U}
|
||
<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 t=this._stageEl,e=this._viewOr(this._baseVb()),i=t&&t.clientWidth&&e.w?t.clientWidth/e.w:1;return function(t,e,i=7){if(!(t>0&&e>0&&Number.isFinite(e)))return null;const s=Uh.find(s=>t*s*e>=i);if(void 0===s)return null;const n=Uh.find(t=>t>=5*s)??5*s;return{fine:s,coarse:n}}(this._gridPitch,i)}_renderMarkupDefs(t){const e=this._gridLevels();if(!e)return j`<defs></defs>`;const i=this._gridPitch*e.fine,s=this._gridPitch*e.coarse,n=this._gridPitch*e.fine*.14;return j`<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 t=this._spaceModel();if(!t)return j``;const e=this._gridPitch,i=this._viewOr(this._baseVb()),s=this._stageEl,n=s?.clientWidth?i.w/s.clientWidth:e/8,o=Math.max(.22*e,8*n),a=Math.max(o,12*n),r=t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`,l=(t,e)=>this._physicalSel?.kind===t&&this._physicalSel.id===e||"column"===t&&this._duplicateColumnId===e,c=(t.room_drafts||[]).flatMap(t=>t.points.slice(0,-1).map((e,i)=>{const s=t.points[i+1],o=ml(t.segments[i]?.cm||15,this._cellCm,this._gridPitch);return j`<line class="physical-hit ${l("draft",t.id)?"selected":""}"
|
||
data-hp="room-draft" data-kind="segment" data-id=${t.id} data-segment=${i}
|
||
x1=${e[0]} y1=${e[1]} x2=${s[0]} y2=${s[1]}
|
||
stroke-width=${Math.max(24,o/Math.max(n,1e-9))}
|
||
vector-effect="non-scaling-stroke"
|
||
@pointerdown=${e=>{this._physicalSel={kind:"draft",id:t.id,segment:i},this._registerPhysicalTap("draft",t.id,i)}}></line>`})),h=(t.partitions||[]).map(t=>{const e=Xc(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?j`<path class="physical-hit ${l("partition",t.id)?"selected":""}"
|
||
data-hp="partition" data-kind="partition" data-id=${t.id} d=${r(e)}
|
||
stroke-width=${24} vector-effect="non-scaling-stroke"
|
||
@pointerdown=${e=>this._physicalDown(e,"partition",t.id)}
|
||
@pointermove=${t=>this._physicalMove(t)}
|
||
@pointerup=${t=>this._physicalUp(t)}></path>`:U}),d=(t.wall_columns||[]).map(t=>{const e=Zc(this._physicalRotate?.id===t.id?{...t,angle:this._physicalRotate.angle}:t,this._cellCm,this._gridPitch);return j`<path class="physical-hit ${l("column",t.id)?"selected":""}"
|
||
data-hp="wall-column" data-kind=${t.shape} data-id=${t.id} d=${r(e)}
|
||
stroke-width=${24} vector-effect="non-scaling-stroke"
|
||
@pointerdown=${e=>this._physicalDown(e,"column",t.id)}
|
||
@pointermove=${t=>this._physicalMove(t)}
|
||
@pointerup=${t=>this._physicalUp(t)}></path>`}),p=this._physicalDrag,u=(()=>{if(!p?.moved)return U;const t="partition"===p.kind?Xc(p.base.a,p.base.b,p.base.cm,this._cellCm,this._gridPitch):Zc(p.base,this._cellCm,this._gridPitch);return t?j`<path class="physical-drag" d=${r(t)}
|
||
transform="translate(${p.delta[0]} ${p.delta[1]})"></path>`:U})(),_=(()=>{const i=this._physicalSel;if(!i)return U;if("draft"===i.kind){const e=t.room_drafts.find(t=>t.id===i.id);return e?j`<g class="physical-chrome" data-kind="draft-selection">
|
||
<polyline class="frame" points=${e.points.map(t=>t.join(",")).join(" ")}></polyline>
|
||
${e.points.map(t=>j`<circle class="move-dot" cx=${t[0]} cy=${t[1]} r=${.55*o}></circle>`)}
|
||
</g>`:U}if("partition"===i.kind){const e=t.partitions.find(t=>t.id===i.id);if(!e)return U;const s=Xc(e.a,e.b,e.cm,this._cellCm,this._gridPitch);if(!s)return U;const n=(e.a[0]+e.b[0])/2,a=(e.a[1]+e.b[1])/2;return j`<g class="physical-chrome" data-kind="partition-selection">
|
||
<path class="frame" d=${r(s)}></path>
|
||
<circle class="move-dot" cx=${e.a[0]} cy=${e.a[1]} r=${.55*o}></circle>
|
||
<circle class="move-dot" cx=${e.b[0]} cy=${e.b[1]} r=${.55*o}></circle>
|
||
<circle class="move-dot" cx=${n} cy=${a} r=${o}></circle>
|
||
</g>`}const s=t.wall_columns.find(t=>t.id===i.id);if(!s)return U;const l=this._physicalRotate?.id===s.id?{...s,angle:this._physicalRotate.angle}:s,c=Zc(l,this._cellCm,this._gridPitch);if("square"!==l.shape)return j`<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=Uc(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(e,24*n),_[1]+u*Math.max(e,24*n)];return j`<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=${t=>this._physicalRotateDown(t,s)}
|
||
@pointermove=${t=>this._physicalRotateMove(t)}
|
||
@pointerup=${t=>this._physicalRotateUp(t)}></circle>
|
||
</g>`})();return j`<g class="physical-editor">${c}${h}${d}${u}${_}</g>`}_renderHiddenWallDiagnosticOverlay(){if(!this._markup)return j``;const t=this._hiddenWallDiagnosticSnapshot().value;if(!t.segments.length)return j``;const e=ml(5,this._cellCm,this._gridPitch);return j`<g class="hidden-wall-diagnostic" data-hp="hidden-wall-diagnostic"
|
||
data-segment-count=${t.segments.length}
|
||
data-endpoint-count=${t.endpoints.length}
|
||
aria-hidden="true" pointer-events="none">
|
||
${t.segments.map(t=>j`<line class="hidden-wall-line"
|
||
data-key=${t.key} data-source-kind=${t.sourceKind}
|
||
data-source-id=${t.sourceId}
|
||
x1=${t.a[0]} y1=${t.a[1]} x2=${t.b[0]} y2=${t.b[1]}
|
||
vector-effect="non-scaling-stroke" pointer-events="none"></line>`)}
|
||
${t.endpoints.map(t=>j`<circle class="hidden-wall-node"
|
||
data-key=${t.key} data-source-kind=${t.sourceKind}
|
||
data-source-id=${t.sourceId}
|
||
cx=${t.point[0]} cy=${t.point[1]} r=${e}
|
||
pointer-events="none"></circle>`)}
|
||
</g>`}_renderPlanSnapOverlay(){if(!this._markup||"draw"!==this._tool)return j``;const t=this._planSnapGeometrySnapshot().value,e=this._activePlanSnapCandidate,i=new Set(this._activePlanSnapConflicts.map(t=>t.key)),s=ml(5,this._cellCm,this._gridPitch),n=ml(10,this._cellCm,this._gridPitch);return j`<g class="plan-snap-overlay" data-hp="plan-snap-overlay"
|
||
data-segment-count=${t.segments.length}
|
||
data-endpoint-count=${t.endpoints.length}
|
||
aria-hidden="true" pointer-events="none">
|
||
${gt([t,s,[...i].sort().join("|")],()=>j`
|
||
${t.segments.map(t=>j`<line class="plan-snap-line"
|
||
data-key=${t.key} data-source-kind=${t.sourceKind}
|
||
x1=${t.a[0]} y1=${t.a[1]} x2=${t.b[0]} y2=${t.b[1]}
|
||
vector-effect="non-scaling-stroke" pointer-events="none"></line>`)}
|
||
${t.endpoints.map(t=>j`<circle class="plan-snap-node ${i.has(t.key)?"conflict":""}"
|
||
data-kind="endpoint" data-key=${t.key} data-active="false"
|
||
cx=${t.point[0]} cy=${t.point[1]} r=${s}
|
||
pointer-events="none"></circle>`)}
|
||
`)}
|
||
<circle class="plan-snap-node ${e?"active":""} ${"line"===e?.kind?"dynamic":""}"
|
||
data-hp="plan-snap-active-marker"
|
||
data-kind=${e?.kind??U} data-key=${e?.key??U}
|
||
data-active=${e?"true":"false"}
|
||
cx=${e?.point[0]??0} cy=${e?.point[1]??0} r=${n}
|
||
visibility=${e?"visible":"hidden"} pointer-events="none"></circle>
|
||
</g>`}_syncPlanSnapActiveMarker(t){const e=this.renderRoot?.querySelector('[data-hp="plan-snap-active-marker"]');if(e){if(e.setAttribute("class",`plan-snap-node${t?" active":""}${"line"===t?.kind?" dynamic":""}`),e.setAttribute("data-active",t?"true":"false"),e.setAttribute("visibility",t?"visible":"hidden"),!t)return e.removeAttribute("data-kind"),void e.removeAttribute("data-key");e.setAttribute("data-kind",t.kind),e.setAttribute("data-key",t.key),e.setAttribute("cx",String(t.point[0])),e.setAttribute("cy",String(t.point[1])),e.setAttribute("r",String(ml(10,this._cellCm,this._gridPitch)))}}_syncPlanSnapConflictMarkers(t){const e=new Set(t.map(t=>t.key));for(const t of this.renderRoot?.querySelectorAll('.plan-snap-node[data-kind="endpoint"]')||[])t.setAttribute("class","plan-snap-node"+(e.has(t.dataset.key||"")?" conflict":""))}_planSnapPhysicalSegment(t){let e=0;const i=this._spaceModel();if(!i)return null;if("partition"===t.sourceKind)e=Number(i.partitions.find(e=>e.id===t.sourceId)?.cm)||0;else if("draft"===t.sourceKind){const s=/^(.*):(\d+)$/.exec(t.sourceId),n=s?i.room_drafts.find(t=>t.id===s[1]):null;e=n&&s&&Number(n.segments[Number(s[2])]?.cm)||0}else e=dc(i.rooms,this._spaceWalls,this._openCuts(),[t.a[0],t.a[1],t.b[0],t.b[1]],this._wallKeyPitch,this._cellCm,this._gridPitch,Pf);return e>0?{a:[...t.a],b:[...t.b],halfDepth:ml(e,this._cellCm,this._gridPitch)/2}:null}_drawPreviewJoinPatchD(t,e){if(t.length<2)return"";const i=[];for(let s=0;s+1<t.length;s++)e[s]>0&&i.push({a:t[s],b:t[s+1],halfDepth:e[s]});if(!i.length)return"";const s=2e-4*this._gridPitch,n=this._planSnapGeometrySnapshot().value.segments.filter(e=>t.some(t=>Hs(t,[e.a[0],e.a[1],e.b[0],e.b[1]])<=s)).map(t=>this._planSnapPhysicalSegment(t)).filter(t=>!!t);return Bl([...i,...n],s).map(t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`).join(" ")}_renderMarkupLayer(t){const e=this._openPairs().flatMap(t=>t.segs),i=this._thickWallCuts(),s=e.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&&l>0?this._contourClosed?o:this._cursorPt?[...o,this._cursorPt]:o.length>=2?o:null:null)(),h=c?lu(c.length-1,this._contourClosed?[...this._draftSegmentCms,this._closingWallCm??void 0]:this._draftSegmentCms,l,ll).map(t=>ml(t,this._cellCm,this._gridPitch)/2):[],d=c?ql(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 j`
|
||
${this._gridLevels()?j`<rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" fill="url(#hp-grid-major)" pointer-events="none"></rect>`:U}
|
||
${n.map(t=>j`<line class="seg" x1="${t[0]}" y1="${t[1]}" x2="${t[2]}" y2="${t[3]}"></line>`)}
|
||
${this._renderPhysicalEditorLayer()}
|
||
${"column"===this._tool&&this._cursorPt&&this._drawWallCm?j`<path class="physical-drag" d=${(()=>`M ${Zc({shape:"square",center:this._cursorPt,cm:this._drawWallCm},this._cellCm,this._gridPitch).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`)()}></path>`:U}
|
||
${d?j`<path class="drawwall-preview-fill" d="${d}"></path>
|
||
<path class="drawwall-preview" d="${d}"></path>`:U}
|
||
${p?j`<path class="drawwall-preview-fill" d="${p}"></path>
|
||
<path class="drawwall-preview" style="stroke:none" d="${p}"></path>`:U}
|
||
${u?j`<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>`:U}
|
||
${o.length>1?j`<polyline class="pathline" points="${o.map(t=>t.join(",")).join(" ")}"></polyline>`:U}
|
||
${o.length&&this._cursorPt&&"draw"===this._tool&&!this._contourClosed?j`<line class="active-axis" x1="${o[o.length-1][0]}" y1="${o[o.length-1][1]}"
|
||
x2="${this._cursorPt[0]}" y2="${this._cursorPt[1]}" aria-hidden="true"></line>
|
||
${this._activePlanSnapCandidate||this._activePlanSnapConflicts.length?U:j`<circle class="active-vertex" cx="${this._cursorPt[0]}" cy="${this._cursorPt[1]}"
|
||
r="${_h(.22*a,this._cellCm)}" aria-hidden="true"></circle>`}`:U}
|
||
${o.map((t,e)=>j`<circle class="vertex ${0===e?"first":""}"
|
||
cx="${t[0]}" cy="${t[1]}" r="${_h(.22*a,this._cellCm)}"></circle>`)}
|
||
${"split"===this._tool&&this._splitSel?.pts?.length?j`${this._splitSel.pts.length>1?j`<polyline class="pathline" points="${this._splitSel.pts.map(t=>t.join(",")).join(" ")}"></polyline>`:U}
|
||
${this._splitSel.pts.map((t,e)=>j`<circle class="vertex ${0===e?"first":""}"
|
||
cx="${t[0]}" cy="${t[1]}" r="${_h(.22*a,this._cellCm)}"></circle>`)}
|
||
${this._cursorPt?j`<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>`:U}`:U}
|
||
`}_renderPartitionDeleteDialog(){const t=this._partitionDeleteDialog,e="mi"===this.hass?.config?.unit_system?.length;return W`<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:t.openings.length})}</p>
|
||
<ul aria-label=${this._t("confirm.delete_partition_openings_title")}>
|
||
${t.openings.map(t=>W`<li>${this._t("confirm.delete_partition_openings_item",{type:this._t(`opening.${t.type}`),length:li(t.length*Pf/this._gridPitch*this._cellCm,e)})}</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 t=this._roomDeleteDialog;return W`<hp-dialog .hass=${this.hass}
|
||
.title=${this._t("confirm.delete_room_title",{name:t.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 t=this._physicalDialog,e="column"===t.kind;return W`<hp-dialog .hass=${this.hass} wide
|
||
.title=${this._t(e?"physical.column_properties":"partition"===t.kind?"physical.partition_properties":"physical.draft_properties")}
|
||
icon=${e?"mdi:vector-square":"mdi:wall"}
|
||
@hp-close=${()=>this._physicalDialog=null}>
|
||
<div class="body">
|
||
${e?W`<label>${this._t("physical.shape")}</label>
|
||
<select class="areasel" @change=${e=>{const i=e.target.value;this._physicalDialog={...t,shape:i}}}>
|
||
<option value="square" ?selected=${"square"===t.shape}>${this._t("physical.square")}</option>
|
||
<option value="circle" ?selected=${"circle"===t.shape}>${this._t("physical.circle")}</option>
|
||
</select>`:U}
|
||
<label>${this._t(e?"circle"===t.shape?"physical.diameter":"physical.side":"wallthick.field")}</label>
|
||
<div class="row"><input class="namein tempin" type="number"
|
||
min=${_l(1,this._imperial)}
|
||
max=${_l(e?150:100,this._imperial)} step="any" .value=${t.cm}
|
||
@input=${e=>this._physicalDialog={...t,cm:e.target.value}} />
|
||
<span class="opl">${this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}</span></div>
|
||
${e&&"square"===t.shape?W`
|
||
<label>${this._t("physical.rotation")}</label>
|
||
<input class="namein tempin" type="number" min="0" max="89.999" step="5"
|
||
.value=${t.angle||"0"}
|
||
@input=${e=>this._physicalDialog={...t,angle:e.target.value}} />`:U}
|
||
${t.length?W`<div class="muted">${this._t("physical.length")}: ${t.length}</div>`:U}
|
||
</div>
|
||
<div class="row dialog-action-footer physicalfooter" slot="footer">
|
||
<div class="dialog-action-group dialog-action-danger">
|
||
${"draft"===t.kind?W`
|
||
<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>`:W`
|
||
<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 t=this._geometryHistory.undoName,e=this._geometryHistory.redoName,i="boundary"===this._tool&&!!this._openWallAnchor,s=i?this._t("markup.boundary_cancel"):t?this._t("history.undo_named",{name:t}):this._t("history.undo_empty"),n=i?this._t("markup.boundary_cancel"):e?this._t("history.redo_named",{name:e}):this._t("history.redo_empty");return W`<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(t=>this._renderEditorGroupLauncher(t))}
|
||
<button class="btn ${"boundary"===this._tool?"on":""}"
|
||
@click=${()=>this._activateMarkupTool("boundary")}
|
||
aria-pressed=${"boundary"===this._tool?"true":"false"}
|
||
title=${this._t("title.markup_boundary")}>
|
||
<ha-icon icon="mdi:border-style"></ha-icon>${this._t("markup.boundary")}
|
||
</button>
|
||
<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=${!t&&!i}
|
||
title=${s} aria-label=${s}>
|
||
<ha-icon icon="mdi:undo-variant" aria-hidden="true"></ha-icon>
|
||
</button>
|
||
<button class="btn ghost" @click=${this._redoGeometry}
|
||
?disabled=${!e&&!i}
|
||
title=${n} aria-label=${n}>
|
||
<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 W`<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" @click=${()=>this._openMarkerDialog()} title=${this._t("title.add_device")}>
|
||
<ha-icon icon="mdi:plus-box-outline"></ha-icon>${this._t("devbar.add")}
|
||
</button>
|
||
<button class="btn ${this._showAll?"on":""}" @click=${this._toggleShowAll}
|
||
title=${this._t("title.show_all")}>
|
||
<ha-icon icon="${this._showAll?"mdi:eye":"mdi:eye-off-outline"}"></ha-icon>${this._t("devbar.show_all")}
|
||
</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(t=>this._renderEditorGroupLauncher(t))}
|
||
</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>`}_cardEntities(t){const e=this._planHass,i=[],s=new Set,n=t=>{if(!t||s.has(t)||!e.states[t])return;const n=e.entities[t];if("config"===n?.entity_category||"diagnostic"===n?.entity_category)return;s.add(t);const o=t.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(o)?i.push({eid:t,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(o)?i.push({eid:t,kind:"open"}):["sensor","binary_sensor","number","select"].includes(o)&&i.push({eid:t,kind:"value"})};for(const i of ho(e,this._devices,null,this._virtualLights))if(i.device.id===t.id)for(const t of[...i.serviceEids,...i.stateEids])n(t);t.primary&&n(t.primary);for(const e of t.entities)n(e);return i.slice(0,12)}_cardToggle(t){const e=t.split(".")[0];"lock"!==e&&"alarm_control_panel"!==e&&this._planEntityAvailable(t)&&this.hass.callService("homeassistant","toggle",{entity_id:t}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))}_renderInfoCard(){const t=this._infoCard,e=t.primary?this.hass.states[t.primary]:void 0,i=e?Xi(this.hass,t.primary)?.text??e.state:null,s=(t.controls??t.marker?.controls??[]).filter(Ms).filter(t=>this._planEntityAvailable(t));return W`<hp-dialog .hass=${this.hass} .title=${t.name} .icon=${t.icon} wide
|
||
dismiss-on-scrim @hp-close=${this._closeInfoCard}>
|
||
<div class="body">
|
||
${(()=>{const e=this._cardEntities(t);return e.length?W`<div class="entlist">
|
||
${e.map(({eid:t,kind:e})=>{const i=this.hass.states[t],s=this.hass.entities[t]?.name||i?.attributes?.friendly_name||t,n=i?Xi(this.hass,t)?.text??i.state:"",o="on"===i?.state||["open","unlocked","playing","cleaning"].includes(i?.state);return W`<div class="entrow ${o?"on":""}">
|
||
<ha-icon icon=${vs(ee(s,"",this._iconRules),t.split(".")[0],i?.attributes?.device_class,i?.state,!1)}></ha-icon>
|
||
<span class="en">${s}</span>
|
||
${"toggle"===e?W`<button class="entbtn ${o?"on":""}"
|
||
@click=${()=>this._cardToggle(t)}>${n}</button>`:"open"===e?W`<button class="entbtn"
|
||
@click=${()=>{this._closeInfoCard(),this._openMoreInfo(t)}}>${n}</button>`:W`<span class="ev">${n}</span>`}
|
||
</div>`})}
|
||
</div>`:U})()}
|
||
${t.model?W`<div class="inforow"><span class="k">${this._t("info.model")}</span><span>${t.model}</span></div>`:U}
|
||
${i&&!this._cardEntities(t).length?W`<div class="inforow"><span class="k">${this._t("info.state")}</span><span>${i}</span></div>`:U}
|
||
${Hi(t.link)?W`<div class="inforow"><span class="k">${this._t("info.link")}</span>
|
||
<a href="${Hi(t.link)}" target="_blank" rel="noreferrer noopener">${t.link}</a></div>`:U}
|
||
${t.description?W`<div class="infodesc">${t.description}</div>`:U}
|
||
${t.pdfs&&t.pdfs.length?W`<div class="inforow"><span class="k">${this._t("info.manuals")}</span><span class="pdflist">
|
||
${t.pdfs.map(t=>W`<a class="pdf" href="${Hi(this._display(t.url))||"#"}" target="_blank" rel="noreferrer noopener">
|
||
<ha-icon icon="mdi:file-pdf-box"></ha-icon>${t.name}</a>`)}</span></div>`:U}
|
||
${s.length?W`<div class="inforow"><span class="k">${this._t("info.controls")}</span>
|
||
<span class="ctrlstates">
|
||
${s.map(t=>{const e=this.hass.states[t],i="on"===e?.state;return W`<span class="ctrlstate ${i?"on":""}">
|
||
<ha-icon icon=${i?"mdi:lightbulb-on":"mdi:lightbulb-outline"}></ha-icon>
|
||
${e?.attributes?.friendly_name||t}</span>`})}
|
||
</span></div>`:U}
|
||
${t.model||i||t.link||t.description||t.pdfs&&t.pdfs.length||s.length?U:W`<div class="infodesc muted">${this._t("info.none")}</div>`}
|
||
</div>
|
||
<div class="row infofooter" slot="footer">
|
||
<button class="btn" @click=${()=>{const e=t;this._closeInfoCard(),this._openMarkerDialog(e)}}>
|
||
<ha-icon icon="mdi:pencil"></ha-icon>${this._t("btn.edit")}
|
||
</button>
|
||
${t.primary?W`<button class="btn" @click=${()=>{const e=t.primary;this._closeInfoCard(),this._openMoreInfo(e)}}>
|
||
<ha-icon icon="mdi:open-in-new"></ha-icon>${this._t("btn.open_in_ha")}
|
||
</button>`:U}
|
||
<button class="btn ghost infofooter-close" @click=${this._closeInfoCard}>${this._t("btn.close")}</button>
|
||
</div>
|
||
</hp-dialog>`}_markerValueBadgeFields(t){return(e={touched:t.valueBadgeTouched,originalHas:t.originalHasValueBadge,original:t.originalValueBadge,enabled:t.valueBadgeEnabled,source:t.valueBadgeSource,position:t.valueBadgePosition}).touched?{value_badge:{enabled:e.enabled,source:e.source,position:e.position}}:e.originalHas?{value_badge:e.original}:{};var e}_markerDraft(t){if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return null;const e=qs(t.room),i=Fi(t.binding,t.devId,()=>"__hp_device_preview__"),s=this._markers.find(e=>e.id===i||e.id===t.devId),n=Zn(t.binding,t.controls,this._bindingEntities(t.binding)),o=this._effectiveStoredTapAction(t),a={id:i,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&1.5!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle||null,...this._markerTapActionFields(t),...this._markerToggleEntityFields(t),tap_target:"run"===o&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:n.length?n:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=mf(t.glowRadius);return null==e||e<=0?null:Math.round(this._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:t.hideFromPlan,vacuum:s?.vacuum||null};return("virtual"===t.binding||t.room)&&(a.space=e?.space||("virtual"===t.binding?this._space:null),a.area=e?.area||null,a.room_id=e?.roomId||null),a}_markerPreviewDevice(t){const e=this._markerDraft(t);if(!e)return null;const i=`${this._haRegistry.revision}\n${Id(this._markers)}\n${JSON.stringify(e)}`;if(this._markerPreviewMemo?.key===i)return this._markerPreviewMemo.device;const s=function(t){const{marker:e,siblingMarkers:i=[],...s}=t;return So({...s,markers:[...i.filter(t=>t.id!==e.id),e]}).find(t=>t.id===e.id)||null}({hass:this.hass,registry:this._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),marker:e,siblingMarkers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||this._space,loc:t=>this._t(t),iconRules:this._iconRules});return this._markerPreviewMemo={key:i,device:s},s}_markerPreviewDevices(t){const e=this._markerPreviewDevicesMemo;if(e?.base===this._devices&&e.preview===t)return e.devices;const i=[...this._devices.filter(e=>e.id!==t.id),t];return this._markerPreviewDevicesMemo={base:this._devices,preview:t,devices:i},i}_toggleIntent(t,e=this._devices){return Dp({hass:this._planHass,registryHass:this._fullRegistryHass,devices:e,device:t,virtualLights:this._virtualLights})}_toggleIntentForDialog(t){const e=this._markerPreviewDevice(t);if(!e)return null;const i=this._markerPreviewDevices(e);return this._toggleIntent(e,i)}_toggleStateText(t,e){const i=this._planHass?.states?.[t]||this.hass?.states?.[t];try{return i&&"function"==typeof this.hass?.formatEntityState?this.hass.formatEntityState(i):e}catch{return e}}_toggleConfirmationStateText(t){const e=String(t.state||"unknown"),i=t.entityId?this._toggleStateText(t.entityId,e):e;if(i.trim().toLocaleLowerCase()!==e.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"}[e];return s?this._t(s):e.replaceAll("_"," ").replaceAll("-"," ")}_toggleConfirmationLines(t){const e={"turn-on":"confirm.state_on","turn-off":"confirm.state_off",open:"confirm.state_open",close:"confirm.state_closed",stop:"confirm.state_stopped"};return function(t,e){if(!Tp(t)||!t.nextEffect||!t.targets.length)return[];const i="group"===t.kind,s=i?t.targets.filter(t=>"on"===t.state).length:0,n=i?0===s?e.groupAllOff():e.groupCurrent(s,t.targets.length):e.state(t.targets[0]);let o;return o="toggle"===t.nextEffect?e.expectedByHa():i&&"turn-on"===t.nextEffect?e.groupAllOn():i&&"turn-off"===t.nextEffect?e.groupAllOff():e.effect(t.nextEffect),[e.current(n),e.expected(o),...t.skippedTargets.length?[e.unavailable(t.skippedTargets.length)]:[]]}(t,{state:t=>this._toggleConfirmationStateText(t),current:t=>this._t("confirm.current_state",{state:t}),expected:t=>this._t("confirm.expected_state",{state:t}),groupCurrent:(t,e)=>this._t("confirm.group_current",{on:t,total:e}),groupAllOn:()=>this._t("confirm.group_all_on"),groupAllOff:()=>this._t("confirm.group_all_off"),unavailable:t=>this._t("confirm.unavailable_targets",{count:t}),effect:t=>this._t(e[t]),expectedByHa:()=>this._t("confirm.expected_by_ha")})}_toggleHintLines(t){if(!t)return[];const e=t=>this._t(`marker.toggle_effect_${t.replace("-","_")}`),i=t=>this._t(`marker.toggle_skip_${t.replace("-","_")}`);return function(t,e){const i=t.targets[0]||t.skippedTargets[0],s=[];return"group"===t.kind?t.targets.length&&s.push(e.group(t.targets)):i&&s.push(e.single(i)),t.nextEffect&&t.targets.length&&s.push("group"===t.kind?e.groupCurrentNext(t.targets,t.nextEffect):e.currentNext(t.targets[0],t.nextEffect)),t.skippedTargets.length&&s.push(e.skipped(t.skippedTargets)),!t.command&&t.noneReason&&s.push(e.none(t.noneReason)),s}(t,{single:t=>{if("via"in t&&"virtual-light"===t.via)return this._t("marker.virtual_light_target",{name:t.name});const e=t.entityId||("ref"in t?t.ref:""),i=t.name||e;return this._t("marker.toggle_hint_single",{name:i,id:e})},group:t=>this._t("marker.toggle_hint_group",{count:t.length,names:t.map(t=>`${t.name} (${t.entityId})`).join(", ")}),currentNext:(t,i)=>"virtual-light"===t.via?this._t("marker.virtual_light_current",{state:this._t("on"===t.state?"marker.virtual_light_state_on":"marker.virtual_light_state_off"),effect:e(i)}):this._t("marker.toggle_hint_current",{state:this._toggleStateText(t.entityId,t.state),effect:e(i)}),groupCurrentNext:(t,i)=>this._t("marker.toggle_hint_group_current",{on:t.filter(t=>"on"===t.state).length,count:t.length,effect:e(i)}),skipped:t=>this._t("marker.toggle_hint_skipped",{count:t.length,targets:t.map(t=>{const e=t.entityId||t.ref;return`${t.name||e} (${e}: ${i(t.reason)})`}).join(", ")}),none:t=>this._t(`marker.toggle_none_${t.replaceAll("-","_")}`)})}_effectiveStoredTapAction(t,e){return t.tapActionTouched?t.tapAction:$p(t.originalHasTapAction?t.originalTapAction:null,e)}_effectiveMarkerTapAction(t,e=this._markerPreviewDevice(t)){return this._effectiveStoredTapAction(t,e?.primary?.split(".")[0])}_announceToggleDraft(t){const e=this._markerPreviewDevice(t),i=t.tapActionTouched?t:{...t,tapAction:this._effectiveMarkerTapAction(t,e)},s="toggle"===i.tapAction?this._toggleHintLines(this._toggleIntentForDialog(i)).join(" "):"";return{...i,tapHintAnnouncement:s}}_valueBadgeForBinding(t,e){const i={...t,binding:e,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0},s=this._markerPreviewDevice(i);if(!s)return{valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0};const n=[...this._devices.filter(t=>t.id!==s.id),s],o=Wo(this._planHass,s,n),a=jo(this._planHass,s,o);return{valueBadgeEnabled:t.valueBadgeEnabled&&!!a,valueBadgeSource:a,valueBadgeTouched:!0}}_markerSpatialSource(t){const e=this._markerPreviewDevice(t);if(!e)return null;const i={...e,hidden:!1};return po(ho(this._planHass,[...this._devices.filter(t=>t.id!==i.id),i],null,this._virtualLights).filter(t=>t.device.id===i.id))}_markerAutoHasSpatialSource(t){const e={...t,lightRole:"auto",lightRoleTouched:!0},i=this._markerPreviewDevice(e);return!!i&&lo(this._planHass,{...i})}_setMarkerLightRole(t){const e=this._markerDialog;e&&(this._markerDialog={...e,lightRole:t,lightRoleTouched:!0})}_controlRefInfo(t){if(!t.startsWith("marker:"))return{label:this.hass.states[t]?.attributes?.friendly_name||t,sub:t,icon:t.startsWith("light.")?"mdi:lightbulb":"mdi:toggle-switch",warning:!this._planEntityAvailable(t)};const e=t.slice(7),i=this._markers.find(t=>t.id===e),s=this._devices.find(t=>t.id===e);if(!i||i.removed||!0!==i.is_light)return{label:this._t("marker.control_missing_label"),sub:`${this._t("marker.control_broken")} (${t})`,icon:"mdi:alert-outline",warning:!0};const n=this._serverCfg?.spaces.find(t=>t.id===(s?.space||i.space)),o=!!s&&to(s).length>0;return{label:i.name||s?.name||e,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(t){const e=t.source;if("derived_lqi"===e.kind)return this._t("marker.value_badge_lqi");if("derived_marker_state"===e.kind)return this._t("marker.value_badge_marker_state",{name:t.label});if("entity_state"===e.kind)return this._t("marker.value_badge_state",{name:t.label});const i=this.hass.states[e.entity_id]?.attributes?.friendly_name||this._fullRegistryHass.entities[e.entity_id]?.name||e.entity_id;return this._t(`marker.value_badge_attr_${e.attribute}`,{name:i})}_controlCandidates(t){const e=this._markerDraft(t)?.id||t.devId||"",i=[],s=new Set;for(const t of this._markers){if(t.id===e||t.removed||t.hidden||!0!==t.is_light)continue;if("virtual"!==t.binding&&"active"!==this._bindingStatus(t.binding).kind)continue;const n=this._devices.find(e=>e.id===t.id);if(!n||n.hidden)continue;const o=this._controlRefInfo(`marker:${t.id}`);for(const t of to(n))s.add(t);i.push({value:`marker:${t.id}`,label:o.label,sub:o.sub,icon:o.icon,search:`${o.label} ${o.sub} ${t.id} ${to(n).join(" ")}`.toLowerCase()})}const n=Object.keys(this.hass.states||{}).filter(e=>Ms(e)&&!s.has(e)&&Xn(t.binding,[e],this._bindingEntities(t.binding)).length>0&&this._planEntityAvailable(e)).map(t=>{const e=this._controlRefInfo(t);return{value:t,label:e.label,sub:t,icon:e.icon,search:`${e.label} ${t}`.toLowerCase()}}),o=t.controlsFilter.trim().toLowerCase();return[...i,...n].filter(e=>!t.controls.includes(e.value)&&(!o||e.search.includes(o))).slice(0,12).map(({search:t,...e})=>e)}_addControlRef(t,e){if(e.startsWith("marker:")){const i=this._markerDraft(t)?.id||t.devId||"";if(!i)return void this._showToast(this._t("toast.marker_binding_required"));const s=e.slice(7),n=this._markerDraft(t);if(function(t,e,i){if(!e||e===i)return!0;const s=new Map;for(const e of t)s.set(e.id,(e.controls||[]).filter(t=>"string"==typeof t&&t.startsWith("marker:")).map(t=>t.slice(7)));const n=new Set,o=[i];for(;o.length;){const t=o.pop();if(t===e)return!0;n.has(t)||(n.add(t),o.push(...s.get(t)||[]))}return!1}(n?[...this._markers.filter(t=>t.id!==i),n]:this._markers,i,s))return void this._showToast(this._t("toast.marker_control_cycle"))}this._markerDialog=this._announceToggleDraft({...t,controls:[...t.controls,e],controlsFilter:""})}_setMarkerGlowMode(t){const e=this._markerDialog;if(!e)return;if("auto"===t)return void(this._markerDialog={...e,glowMode:t,glowTouched:!0});const i=this._markerSpatialSource(e),s=xs(i?this._planHass.states[i.eid]:void 0,null,this._fillColors.glow_light.c),n=!e.glowColorDrafted,o="fixed"===t&&!e.glowBrightnessDrafted;this._markerDialog={...e,glowMode:t,glowColor:n?s.c:e.glowColor,glowBrightness:o?Math.max(1,Math.round(100*s.bri)):e.glowBrightness,glowColorDrafted:!0,glowBrightnessDrafted:e.glowBrightnessDrafted||"fixed"===t,glowTouched:!0}}_renderMarkerDialog(){const t=this._markerDialog,e="virtual"===t.bindingMode,i=this._bindingCandidates();this._bindingEntities(t.binding);const s=e?null:this._bindingStatus(t.binding),n=!e&&this._bindingHasHaPage(t.binding),o=this._markerPreviewDevice(t),a=this._effectiveMarkerTapAction(t,o),r=o?cs(this._serverCfg?.spaces.find(t=>t.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?p_(this._planHass,o,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:r?.showLqi??!1!==this._config?.show_signal,designPreview:!0,activityRuntime:this._activityRt.get(o.id),lightDevices:l,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion}):null,p=o?Wo(this._planHass,o,l):[],u=o?jo(this._planHass,o,p):null,_=t.valueBadgeTouched?t.valueBadgeEnabled:!!d?.valueBadge,m=t.valueBadgeTouched?t.valueBadgeSource:d?.valueBadge?.source||t.valueBadgeSource,g=t.valueBadgeTouched?t.valueBadgePosition:d?.valueBadge?.position||t.valueBadgePosition,f=Ao(m),v=!!m&&!p.some(t=>t.key===f),y=p.find(t=>t.key===f),b=d?.valueSource?d.valueSource.attribute?`attr:${d.valueSource.eid}:${d.valueSource.attribute}`:`state:${d.valueSource.eid}`:"",w=this._markerAutoHasSpatialSource(t),k=!!o&&function(t,e){return io(t,e).some(t=>!!t.eid)}(this._planHass,{...o}),x=function(t,e,i,s){const n="always"===t||"auto"===t&&e,o=n&&!i;return{sourceExists:n,fromSourceEnabled:n&&i,manualEnabled:n,radiusEnabled:n,passive:o,effectiveMode:o&&"auto"===s?"fixed":s}}(t.lightRole,w,k,t.glowMode),$=!x.sourceExists,S=!x.fromSourceEnabled,M=x.passive,C=x.effectiveMode,D="never"===t.lightRole?this._t("marker.glow_disabled_never"):"auto"!==t.lightRole||w?M?this._t("marker.glow_passive_hint"):this._t("marker.glow_disabled_no_entity"):this._t("marker.glow_disabled_auto"),P=o?to(o):[],T=o&&eo(o)||"",R=!!t.lightEntity&&!P.includes(t.lightEntity),I=o?yp(o):[],A=!!t.toggleEntity&&!I.includes(t.toggleEntity),z="toggle"===a?this._toggleIntentForDialog({...t,toggleEntity:"",toggleEntityTouched:!0}):null,F=z?[...z.targets,...z.skippedTargets].map(t=>t.entityId||("ref"in t?t.ref:"")).filter(Boolean).join(", "):"",E=(()=>{if(e)return null;const s=i.find(e=>e.value===t.binding);if(s)return s.label;const[n,o]=t.binding.split(":");return"device"===n?this._fullRegistryHass.devices[o]?.name_by_user||this._fullRegistryHass.devices[o]?.name||o:this._fullRegistryHass.entities[o]?.name||this.hass.states[o]?.attributes?.friendly_name||o})();return W`<hp-dialog .hass=${this.hass}
|
||
.title=${t.devId?this._t("info.device_header"):this._t("marker.new_device")}
|
||
icon="mdi:shape-plus" wide @hp-close=${()=>this._markerDialog=null}>
|
||
<div class="body">
|
||
${"ha_disabled"===s?.kind?W`<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?W`<button class="btn ghost" type="button" @click=${()=>this._openBindingInHa(t.binding)}>
|
||
<ha-icon icon="mdi:open-in-new"></ha-icon>${this._t("btn.open_in_ha")}
|
||
</button>`:U}
|
||
</div>`:"unverified"===s?.kind&&t.binding?W`<div class="habindingbanner limited" role="status">
|
||
<ha-icon icon="mdi:shield-alert-outline"></ha-icon>
|
||
<span>${this._t("marker.ha_registry_limited")}</span>
|
||
</div>`:U}
|
||
<label>${this._t("marker.name_label")}</label>
|
||
<input class="namein" type="text" placeholder=${this._t("marker.name_ph")}
|
||
.value=${t.name}
|
||
@input=${e=>this._markerDialog={...t,name:e.target.value}} />
|
||
|
||
<label>${this._t("marker.binding_label")}</label>
|
||
<div class="bindsel">
|
||
<label class="srcrow">
|
||
<input type="radio" name="bmode" .checked=${"virtual"===t.bindingMode}
|
||
@change=${()=>{const e={...t,bindingMode:"virtual",binding:"virtual",bindingOpen:!1,controls:Zn("virtual",t.controls),autoIcon:this._autoIconForBinding("virtual")};this._markerDialog=this._announceToggleDraft({...e,...this._valueBadgeForBinding(e,"virtual")})}} />
|
||
<span>${this._t("marker.virtual_option")}</span>
|
||
</label>
|
||
<div class="bindharow">
|
||
<label class="srcrow">
|
||
<input type="radio" name="bmode" .checked=${"ha"===t.bindingMode}
|
||
@change=${()=>this._markerDialog=this._announceToggleDraft({...t,bindingMode:"ha",binding:"virtual"===t.binding?"":t.binding,bindingOpen:"virtual"===t.binding||!t.binding})} />
|
||
<span>${this._t("marker.from_ha_option")}</span>
|
||
</label>
|
||
<label class="srcrow inline entcheck" title=${this._t("marker.show_entities_tip")}>
|
||
${this._boolInput(t.showEntities,e=>this._markerDialog={...t,showEntities:e},"ha"!==t.bindingMode)}
|
||
<span>${this._t("marker.show_entities")}</span>
|
||
</label>
|
||
</div>
|
||
${"ha"===t.bindingMode?W`<button class="dropbtn ${t.bindingOpen?"open":""}"
|
||
@click=${()=>this._markerDialog={...t,bindingOpen:!t.bindingOpen}}>
|
||
${E?W`<b>${E}</b><span class="ref">${t.binding}${"ha_disabled"===s?.kind?` · ${this._t("marker.binding_disabled")}`:""}</span>`:W`<span class="muted">${this._t("marker.pick_ph")}</span>`}
|
||
<ha-icon icon=${t.bindingOpen?"mdi:chevron-up":"mdi:chevron-down"}></ha-icon>
|
||
</button>
|
||
${t.bindingOpen?W`<div class="droppanel">
|
||
<input class="namein" type="text" placeholder=${this._t("marker.search_ph")}
|
||
.value=${t.bindingFilter}
|
||
@input=${e=>this._markerDialog={...t,bindingFilter:e.target.value}} />
|
||
<div class="candlist">
|
||
${i.map(e=>W`<div class="cand ${e.value===t.binding?"sel":""}"
|
||
@click=${()=>{const i={...t,binding:e.value,bindingOpen:!1,controls:Zn(e.value,t.controls,this._bindingEntities(e.value)),autoIcon:this._autoIconForBinding(e.value)};this._markerDialog=this._announceToggleDraft({...i,...this._valueBadgeForBinding(i,e.value)})}}>
|
||
<span class="cl">${e.label}</span><span class="cs">${e.sub}</span>
|
||
</div>`)}
|
||
${i.length?U:W`<div class="cand muted">${this._t("marker.nothing_found")}</div>`}
|
||
</div>
|
||
</div>`:U}`:U}
|
||
</div>
|
||
|
||
<label for="marker-room">${this._t("marker.room_label")}${e?"":this._t("marker.room_override")}</label>
|
||
<select id="marker-room" class="areasel"
|
||
@change=${e=>this._markerDialog={...t,room:e.target.value}}>
|
||
<option value="" ?selected=${!t.room}>
|
||
${e?this._t("marker.room_choose"):this._t("marker.room_auto")}
|
||
</option>
|
||
${this._allRoomsFlat().map(e=>W`<option value=${e.value} ?selected=${e.value===t.room}>${e.label}</option>`)}
|
||
</select>
|
||
|
||
${this._renderVacSection(t)}
|
||
|
||
<label>${this._t("marker.tap_label")}</label>
|
||
<select id="marker-tap-action" class="areasel"
|
||
aria-describedby=${"toggle"===a?"marker-toggle-hint":U}
|
||
@change=${e=>{const i={...t,tapAction:e.target.value,tapActionTouched:!0};this._markerDialog=this._announceToggleDraft(i)}}>
|
||
${qi.map(t=>[t,"tap."+t.replace("-","_")]).map(([t,e])=>W`<option value=${t} ?selected=${t===a}>
|
||
${this._t(e)}
|
||
</option>`)}
|
||
</select>
|
||
${"toggle"===a&&(I.length>1||A)?W`<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=${e=>{const i={...t,toggleEntity:e.target.value,toggleEntityTouched:!0};this._markerDialog=this._announceToggleDraft(i)}}>
|
||
<option value="" ?selected=${A||!t.toggleEntity}>
|
||
${this._t("marker.toggle_entity_auto",{entity:F||this._t("marker.toggle_entity_none")})}
|
||
</option>
|
||
${I.map(e=>W`<option value=${e}
|
||
?selected=${!A&&e===t.toggleEntity}>
|
||
${this.hass.states[e]?.attributes?.friendly_name||this._fullRegistryHass.entities[e]?.name||e} · ${e}
|
||
</option>`)}
|
||
</select>
|
||
${A?W`<p class="muted markerlightwarning" role="status">
|
||
<ha-icon icon="mdi:alert-outline"></ha-icon>
|
||
${this._t("marker.toggle_entity_missing",{entity:t.toggleEntity,fallback:F||this._t("marker.toggle_entity_none")})}
|
||
</p>`:U}
|
||
</div>`:U}
|
||
${"toggle"===a?W`<div id="marker-toggle-hint" class="rhint togglehint">
|
||
${h.map(t=>W`<div>${t}</div>`)}
|
||
</div>
|
||
<div class="sr-only" role="status" aria-live="polite">${t.tapHintAnnouncement}</div>`:U}
|
||
${"run"===a?(()=>{const e=t.runFilter.trim().toLowerCase(),i=this._runCandidates().filter(t=>!e||t.label.toLowerCase().includes(e)||t.value.includes(e)),s=t.tapTarget?this._runCandidates().find(e=>e.value===t.tapTarget):null;return W`
|
||
<label>${this._t("marker.run_target_label")}</label>
|
||
${t.tapTarget&&!s?W`<div class="rhint">${this._t("marker.run_target_gone",{id:t.tapTarget})}</div>`:U}
|
||
<input class="namein" type="text" placeholder=${this._t("marker.run_search_ph")}
|
||
.value=${s?s.label:t.runFilter}
|
||
@focus=${t=>{t.target.select()}}
|
||
@input=${e=>this._markerDialog={...t,runFilter:e.target.value,tapTarget:""}} />
|
||
${s?U:W`<div class="candlist">
|
||
${i.slice(0,40).map(e=>W`<div class="cand ${e.value===t.tapTarget?"sel":""}"
|
||
@click=${()=>this._markerDialog={...t,tapTarget:e.value,runFilter:""}}>
|
||
<span class="cl">${e.label}</span><span class="cs">${e.sub}</span>
|
||
</div>`)}
|
||
${i.length?U:W`<div class="cand muted">${this._t("marker.nothing_found")}</div>`}
|
||
</div>`}`})():U}
|
||
${"run"===a||"toggle"===a?W`<label class="srcrow" title=${this._t("marker.tap_confirm_tip")}>
|
||
${this._boolInput(t.tapConfirm,e=>this._markerDialog={...t,tapConfirm:e})}
|
||
<span>${this._t("marker.tap_confirm")}</span>
|
||
</label>`:U}
|
||
|
||
<label>${this._t("marker.controls_label")}</label>
|
||
<div class="rhint">${this._t("marker.controls_hint")}</div>
|
||
${t.controls.length?W`<div class="ctrlchips">
|
||
${t.controls.map(e=>{const i=this._controlRefInfo(e);return W`<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({...t,controls:t.controls.filter(t=>t!==e)})}></ha-icon>
|
||
</span>`})}
|
||
</div>`:U}
|
||
<input class="namein" type="text" placeholder=${this._t("marker.controls_filter")}
|
||
.value=${t.controlsFilter}
|
||
@input=${e=>this._markerDialog={...t,controlsFilter:e.target.value}} />
|
||
${t.controlsFilter.trim()?W`<div class="ctrllist">
|
||
${this._controlCandidates(t).map(e=>W`<button class="ctrlopt"
|
||
@click=${()=>this._addControlRef(t,e.value)}>
|
||
<ha-icon icon=${e.icon}></ha-icon>
|
||
${e.label}
|
||
<span class="sub">${e.sub}</span>
|
||
</button>`)}
|
||
</div>`:U}
|
||
|
||
${this._bindingHasClimate(t.binding)?W`<label class="srcrow climrow" title=${this._t("marker.use_climate_temp_tip")}>
|
||
${this._boolInput(t.useClimateTemp,e=>this._markerDialog={...t,useClimateTemp:e})}
|
||
<span>${this._t("marker.use_climate_temp")}</span>
|
||
</label>`:U}
|
||
<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"===t.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"===t.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"===t.lightRole} @change=${()=>this._setMarkerLightRole("never")} />
|
||
<span>${this._t("marker.light_role_never")}</span></label>
|
||
</div>
|
||
</fieldset>
|
||
|
||
${"always"===t.lightRole&&(P.length>1||R)?W`<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=${e=>this._markerDialog={...t,lightEntity:e.target.value,lightEntityTouched:!0}}>
|
||
<option value="" ?selected=${R||!t.lightEntity}>
|
||
${this._t("marker.light_entity_auto",{entity:T||this._t("marker.light_entity_none")})}
|
||
</option>
|
||
${P.map(e=>W`<option value=${e}
|
||
?selected=${!R&&e===t.lightEntity}>
|
||
${this.hass.states[e]?.attributes?.friendly_name||this._fullRegistryHass.entities[e]?.name||e} · ${e}
|
||
</option>`)}
|
||
</select>
|
||
${R?W`<p class="muted markerlightwarning" role="status">
|
||
<ha-icon icon="mdi:alert-outline"></ha-icon>
|
||
${this._t("marker.light_entity_missing",{entity:t.lightEntity,fallback:T||"—"})}
|
||
</p>`:U}
|
||
</div>`:U}
|
||
|
||
<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":U}
|
||
@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":U}
|
||
@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":U}
|
||
@change=${()=>this._setMarkerGlowMode("fixed")} />
|
||
<span>${this._t("marker.glow_mode_fixed")}</span></label>
|
||
</div>
|
||
${"auto"!==C?W`<div class="colorrow markerglowvalue">
|
||
<hp-color-opacity .label=${this._t("marker.glow_color")}
|
||
.color=${t.glowColor} .opacity=${1} .showOpacity=${!1}
|
||
.pickerLabels=${this._colorPickerLabels}
|
||
.disabled=${$}
|
||
@hp-color-opacity-change=${e=>{this._markerDialog={...t,glowMode:C,glowColor:e.detail.color,glowColorDrafted:!0,glowTouched:!0}}}></hp-color-opacity>
|
||
${"fixed"===C?W`
|
||
<span class="opl">${this._t("marker.glow_brightness")}</span>
|
||
${this._rangeInput(1,100,1,t.glowBrightness,e=>{this._markerDialog={...t,glowMode:C,glowBrightness:e,glowBrightnessDrafted:!0,glowTouched:!0}},$,this._t("marker.glow_brightness"))}
|
||
<span class="opv">${Math.round(t.glowBrightness)}%</span>`:U}
|
||
</div>`:U}
|
||
</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":U}
|
||
.value=${t.glowRadius}
|
||
@input=${e=>this._markerDialog={...t,glowRadius:e.target.value}} />
|
||
<span class="opl">${this._imperial?this._t("gs.unit_ft"):this._t("gs.unit_m")}</span>
|
||
</div>
|
||
</div>
|
||
${$||M?W`<p id="marker-glow-disabled-hint" class="muted markerlightdisabled" role="note">
|
||
<ha-icon icon="mdi:information-outline"></ha-icon>${D}
|
||
</p>`:U}
|
||
|
||
<label>${this._t("marker.icon_label")}</label>
|
||
${customElements.get("ha-icon-picker")?W`<ha-icon-picker .hass=${this.hass} .value=${t.icon||t.autoIcon}
|
||
.placeholder=${t.autoIcon||void 0}
|
||
.fallbackPath=${void 0}
|
||
@value-changed=${e=>{const i=e.detail.value||"";(t.icon||i!==t.autoIcon)&&(this._markerDialog={...t,icon:i})}}></ha-icon-picker>`:W`<input class="namein" type="text"
|
||
placeholder=${t.autoIcon||this._t("marker.icon_ph")}
|
||
.value=${t.icon}
|
||
@input=${e=>this._markerDialog={...t,icon:e.target.value}} />`}
|
||
${!t.icon&&t.autoIcon?W`<p class="muted iconauto"><ha-icon icon=${t.autoIcon}></ha-icon>
|
||
<span>${this._t("marker.icon_auto",{icon:t.autoIcon})}</span>
|
||
<button class="btn ghost" type="button"
|
||
@click=${()=>this._markerDialog={...t,icon:t.autoIcon}}>
|
||
${this._t("marker.icon_pin_auto")}
|
||
</button></p>`:U}
|
||
|
||
<label for="marker-display">${this._t("marker.display_label")}</label>
|
||
<select id="marker-display" class="areasel"
|
||
@change=${e=>this._markerDialog={...t,display:Bi(e.target.value)}}>
|
||
${Li.map(e=>W`<option value=${e} ?selected=${e===t.display}>
|
||
${this._t(hf[e])}
|
||
</option>`)}
|
||
</select>
|
||
<p class="muted">${this._t(df[t.display])}</p>
|
||
${"static_icon"===t.display&&this._bindingHasAlarm(t.binding)?W`<div class="habindingbanner" role="note">
|
||
<ha-icon icon="mdi:alert-outline"></ha-icon>
|
||
<span>${this._t("marker.static_alarm_warning")}</span>
|
||
</div>`:U}
|
||
<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(_,e=>{const i=m||u;this._markerDialog={...t,valueBadgeEnabled:e&&!!i,valueBadgeSource:i,valueBadgeTouched:!0}},"static_icon"===t.display||!p.length&&!t.valueBadgeSource)}
|
||
<span>${this._t("marker.value_badge_enabled")}</span>
|
||
</label>
|
||
${"static_icon"===t.display?W`<p class="muted markerlightdisabled" role="note">
|
||
<ha-icon icon="mdi:information-outline"></ha-icon>${this._t("marker.value_badge_static")}
|
||
</p>`:p.length||t.valueBadgeSource?U:W`<p class="muted markerlightdisabled" role="note">
|
||
<ha-icon icon="mdi:information-outline"></ha-icon>${this._t("marker.value_badge_empty")}
|
||
</p>`}
|
||
${_?W`
|
||
<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=${e=>this._markerDialog={...t,valueBadgeSource:zo(e.target.value),valueBadgeEnabled:!0,valueBadgeTouched:!0}}>
|
||
${v?W`<option value=${f} selected>
|
||
${this._t("marker.value_badge_missing")}
|
||
</option>`:U}
|
||
${p.map(t=>W`<option value=${t.key}
|
||
?selected=${t.key===f}
|
||
title=${t.technical}>
|
||
${this._valueBadgeCandidateLabel(t)} · ${t.value}
|
||
</option>`)}
|
||
</select>
|
||
${y?W`<p class="muted markerbadgetechnical"><code>${y.technical}</code></p>`:U}
|
||
${v?W`<p class="muted markerlightwarning" role="status">
|
||
<ha-icon icon="mdi:alert-outline"></ha-icon>${this._t("marker.value_badge_missing_hint")}
|
||
</p>`:U}
|
||
${"value"===t.display&&f===b?W`<p class="muted markerlightwarning" role="note">
|
||
<ha-icon icon="mdi:information-outline"></ha-icon>${this._t("marker.value_badge_duplicate")}
|
||
</p>`:U}
|
||
<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=${e=>this._markerDialog={...t,valueBadgeEnabled:_,valueBadgeSource:m,valueBadgePosition:e.target.value,valueBadgeTouched:!0}}>
|
||
${["right","bottom","left","top"].map(t=>W`
|
||
<option value=${t} ?selected=${t===g}>
|
||
${this._t(`marker.value_badge_${t}`)}
|
||
</option>`)}
|
||
</select>
|
||
`:U}
|
||
</fieldset>
|
||
${d?W`<hp-device-preview
|
||
.hass=${this.hass}
|
||
.presentation=${d}
|
||
.registry=${this._haRegistry}
|
||
.deviceName=${t.name.trim()||o?.name||E||""}>
|
||
</hp-device-preview>`:W`<div class="devicepreview-empty">
|
||
<ha-icon icon="mdi:eye-outline"></ha-icon>
|
||
<span>${this._t("marker.preview.select_source")}</span>
|
||
</div>`}
|
||
${"icon_ripple"===t.display?W`<div class="colorrow ripple-colorrow">
|
||
<hp-color-opacity .label=${this._t("marker.activity_color")}
|
||
.pickerLabels=${this._colorPickerLabels}
|
||
.color=${t.rippleColor||"#3ea6ff"} .opacity=${1} .showOpacity=${!1}
|
||
@hp-color-opacity-change=${e=>{this._markerDialog={...t,rippleColor:e.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,t.rippleSize,e=>this._markerDialog={...t,rippleSize:e})}
|
||
<span class="opv">×${t.rippleSize}</span>
|
||
</div>
|
||
<p class="muted" role="note">${this._t("marker.activity_alarm_note")}</p>`:U}
|
||
|
||
<label>${this._t("marker.size_label")}</label>
|
||
<div class="colorrow">
|
||
${this._rangeInput(.5,3,.1,t.size,e=>this._markerDialog={...t,size:e})}
|
||
<span class="opv">×${t.size.toFixed(1)}</span>
|
||
<span class="opl">${this._t("marker.angle_label")}</span>
|
||
${""}
|
||
${this._rangeInput(0,355,5,t.angle,e=>this._markerDialog={...t,angle:e})}
|
||
<span class="opv">${t.angle}°</span>
|
||
</div>
|
||
|
||
<label>${this._t("marker.model_label")}</label>
|
||
<input class="namein" type="text" placeholder=${this._t("marker.model_ph")}
|
||
.value=${t.model}
|
||
@input=${e=>this._markerDialog={...t,model:e.target.value}} />
|
||
|
||
<label>${this._t("marker.link_label")}</label>
|
||
<input class="namein" type="url" placeholder="https://…"
|
||
.value=${t.link}
|
||
@input=${e=>this._markerDialog={...t,link:e.target.value}} />
|
||
|
||
<label>${this._t("marker.desc_label")}</label>
|
||
<textarea class="descin" rows="4" placeholder=${this._t("marker.desc_ph")}
|
||
.value=${t.description}
|
||
@input=${e=>this._markerDialog={...t,description:e.target.value}}></textarea>
|
||
|
||
<label>${this._t("marker.manuals_label")}</label>
|
||
<div class="pdfedit">
|
||
${t.pdfs.map(t=>W`<span class="pdftag"><ha-icon icon="mdi:file-pdf-box"></ha-icon>
|
||
<a href="${Hi(this._display(t.url))||"#"}" target="_blank" rel="noreferrer noopener">${t.name}</a>
|
||
<ha-icon class="x" icon="mdi:close" @click=${()=>this._removeMarkerPdf(t.url)}></ha-icon></span>`)}
|
||
<span class="fileupload">
|
||
<button class="btn filebtn" type="button" @click=${t=>t.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=${t=>this._pickMarkerFiles(t)} />
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="row markerfooter" slot="footer">
|
||
<div class="markeractions">
|
||
${t.devId?W`<button class="btn" type="button"
|
||
?disabled=${t.busy}
|
||
aria-pressed=${t.hideFromPlan||"ha_disabled"===s?.kind?"true":"false"}
|
||
title=${this._t(t.hideFromPlan||"ha_disabled"===s?.kind?"marker.show_tip":"marker.hide_tip")}
|
||
@click=${this._toggleMarkerDialogVisibility}>
|
||
<ha-icon icon=${t.hideFromPlan||"ha_disabled"===s?.kind?"mdi:eye-outline":"mdi:eye-off-outline"}></ha-icon>
|
||
${this._t(t.hideFromPlan||"ha_disabled"===s?.kind?"marker.show":"marker.hide")}
|
||
</button>`:U}
|
||
${t.devId?W`<button class="btn danger" type="button" ?disabled=${t.busy}
|
||
title=${this._t("marker.delete_tip")} @click=${this._deleteMarker}>
|
||
<ha-icon icon="mdi:delete-outline"></ha-icon>${this._t("btn.delete")}
|
||
</button>`:U}
|
||
</div>
|
||
<div class="markersaveactions">
|
||
<button class="btn ghost" ?disabled=${t.busy}
|
||
@click=${()=>this._markerDialog=null}>${this._t("btn.cancel")}</button>
|
||
<button class="btn on" @click=${this._saveMarker}
|
||
?disabled=${t.busy||"ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding||!t.devId&&"active"!==s?.kind)}
|
||
title=${"ha"!==t.bindingMode||t.binding&&"virtual"!==t.binding?"":this._t("marker.pick_ph")}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${t.busy?"…":this._t("btn.save")}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</hp-dialog>`}_renderSpaceDialog(){const t=this._spaceDialog,e=this._importTotal>0&&"create"===t.mode?this._t("import.progress",{i:this._importTotal-this._importQueue.length,n:this._importTotal}):"",i=()=>{this._spaceDialog=null,this._importQueue=[],this._importTotal=0};return W`<hp-dialog .hass=${this.hass}
|
||
.title=${`${"create"===t.mode?this._t("space.new"):this._t("space.header")}${e?` · ${e}`:""}`}
|
||
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=${t.title}
|
||
@input=${e=>this._spaceDialog={...t,title:e.target.value}} />
|
||
<label>${this._t("space.plan_label")}</label>
|
||
<label class="srcrow">
|
||
<input type="radio" name="plansrc" .checked=${"file"===t.source}
|
||
@change=${()=>this._spaceDialog=Od(t,"file")} />
|
||
<span>${this._t("space.source_file")}</span>
|
||
</label>
|
||
${"file"===t.source?W`<div class="planrow">
|
||
${t.planFile?W`<span class="planname">${t.planFile.name}</span>`:t.planUrl?W`<img class="planprev" src=${this._display(t.planUrl)} alt=${this._t("space.plan_alt")} />`:W`<span class="planname muted">${this._t("space.no_plan")}</span>`}
|
||
<span class="fileupload">
|
||
<button class="btn filebtn" type="button" @click=${t=>t.currentTarget.nextElementSibling?.click()}>
|
||
<ha-icon icon="mdi:upload"></ha-icon>${t.planUrl||t.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=${t=>this._pickPlanFile(t)} />
|
||
</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>
|
||
${t.pickSaved?this._renderServerPlans(t):U}`:U}
|
||
<label class="srcrow">
|
||
<input type="radio" name="plansrc" .checked=${"draw"===t.source}
|
||
@change=${()=>this._spaceDialog=Od(t,"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=${gh(.1,this._imperial)}
|
||
max=${gh(uf,this._imperial)}
|
||
step="0.1" .value=${t.cellCmInput??gh(t.cellCm,this._imperial)}
|
||
@input=${e=>{const i=e.target.value,s=mf(i),n=null==s?null:(o=s,this._imperial?o*ph:o);var o;this._spaceDialog={...t,cellCmInput:i,cellCmTouched:!0,cellCm:null!=n&&n>0?Math.max(.1,Math.min(uf,n)):t.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(t.showBorders,e=>this._spaceDialog=Hd(t,"showBorders",e))}
|
||
<span>${this._t("space.show_borders")}</span>
|
||
</label>
|
||
<label class="srcrow">
|
||
${this._boolInput(t.showNames,e=>this._spaceDialog=Hd(t,"showNames",e))}
|
||
<span>${this._t("space.show_names")}</span>
|
||
</label>
|
||
<label class="srcrow">
|
||
${this._boolInput(t.showLqi,e=>this._spaceDialog={...t,showLqi:e})}
|
||
<span>${this._t("space.show_lqi")}</span>
|
||
</label>
|
||
${""}
|
||
<label class="srcrow">
|
||
${this._boolInput(t.hideDecor,e=>this._spaceDialog={...t,hideDecor:e})}
|
||
<span>${this._t("space.hide_decor")}</span>
|
||
</label>
|
||
<div class="rhint">${this._t("space.hide_decor_tip")}</div>
|
||
<label class="srcrow">
|
||
${this._boolInput(t.hideOpenings,e=>this._spaceDialog={...t,hideOpenings:e})}
|
||
<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(([e,i])=>W`<label class="srcrow">
|
||
${this._boolInput(t[e],i=>this._spaceDialog={...t,[e]: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*t.cardFontScale),e=>this._spaceDialog={...t,cardFontScale:e/100})}
|
||
<span class="opv">${Math.round(100*t.cardFontScale)}%</span>
|
||
</div>
|
||
${this._renderCardPreview(t.cardFontScale,1,1)}
|
||
<div class="colorrow">
|
||
<hp-color-opacity .label=${this._t("space.room_color")}
|
||
.opacityLabel=${this._t("space.opacity")}
|
||
.pickerLabels=${this._colorPickerLabels}
|
||
.color=${t.roomColor} .opacity=${t.roomOpacity} .showOpacity=${!0}
|
||
@hp-color-opacity-change=${e=>{this._spaceDialog={...t,roomColor:e.detail.color,roomOpacity:e.detail.opacity}}}></hp-color-opacity>
|
||
</div>
|
||
<label>${this._t("space.bg_mode")}</label>
|
||
<select class="areasel"
|
||
@change=${e=>{const i=e.target.value;this._spaceDialog={...t,bgMode:"static"===i||"daynight"===i?i:null}}}>
|
||
<option value="" ?selected=${null===t.bgMode}>${this._t("space.sun_inherit")}</option>
|
||
<option value="static" ?selected=${"static"===t.bgMode}>${this._t("gs.bg_static")}</option>
|
||
<option value="daynight" ?selected=${"daynight"===t.bgMode}>${this._t("gs.bg_daynight")}</option>
|
||
</select>
|
||
${"static"===(t.bgMode??Sr(this._settings,{}))?W`<div class="colorrow">
|
||
<hp-color-opacity .label=${this._t("space.bg_color")}
|
||
.pickerLabels=${this._colorPickerLabels}
|
||
.color=${t.bgColor||hs(this._settings,{bgColor:null})||this._stageBgHex()}
|
||
.opacity=${1} .showOpacity=${!1}
|
||
@hp-color-opacity-change=${e=>{this._spaceDialog={...t,bgColor:e.detail.color}}}></hp-color-opacity>
|
||
${t.bgColor?W`<button class="btn ghost" @click=${()=>this._spaceDialog={...t,bgColor:null}}>
|
||
${this._t("space.bg_inherit")}</button>`:W`<span class="opl">${this._t("space.bg_inherited")}</span>`}
|
||
</div>`:U}
|
||
<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===t.northDeg?"":String(t.northDeg)}
|
||
@input=${e=>{const i=e.target.value.trim(),s=""===i?null:Math.round(Number(i));this._spaceDialog={...t,northDeg:null!==s&&Number.isFinite(s)?Math.min(359,Math.max(0,s)):null}}} />
|
||
<span class="opl">${null===t.northDeg?this._t("space.north_inherited",{v:null===$r(this._settings,{})?"—":String($r(this._settings,{}))+"°"}):"°"}</span>
|
||
</div>
|
||
<label>${this._t("space.sun_rays")}</label>
|
||
<select class="areasel"
|
||
@change=${e=>{const i=e.target.value;this._spaceDialog={...t,sunRays:""===i?null:"1"===i}}}>
|
||
<option value="" ?selected=${null===t.sunRays}>${this._t("space.sun_inherit")}</option>
|
||
<option value="1" ?selected=${!0===t.sunRays}>${this._t("space.sun_on")}</option>
|
||
<option value="0" ?selected=${!1===t.sunRays}>${this._t("space.sun_off")}</option>
|
||
</select>
|
||
<label>${this._t("space.fill_label")}</label>
|
||
${Wi.map(t=>[t,"fill."+t]).map(([e,i])=>W`<label class="srcrow">
|
||
<input type="radio" name="fillmode" .checked=${t.fillMode===e}
|
||
@change=${()=>this._spaceDialog={...t,fillMode:e}} />
|
||
<span>${this._t(i)}</span>
|
||
${"temp"===e&&"temp"===t.fillMode?W`<span class="temprange">
|
||
<input class="namein tempin" type="number" step="0.5" .value=${String(t.tempMin)}
|
||
@input=${e=>{const i=mf(e.target.value);null!=i&&(this._spaceDialog={...t,tempMin:i})}} />
|
||
–
|
||
<input class="namein tempin" type="number" step="0.5" .value=${String(t.tempMax)}
|
||
@input=${e=>{const i=mf(e.target.value);null!=i&&(this._spaceDialog={...t,tempMax:i})}} />
|
||
°C
|
||
</span>`:U}
|
||
</label>
|
||
${"custom"===e&&"custom"===t.fillMode?W`<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=${(t.customFill||as).c}
|
||
.opacity=${(t.customFill||as).a}
|
||
@hp-color-opacity-change=${e=>{this._spaceDialog={...t,customFill:{c:e.detail.color,a:e.detail.opacity}}}}></hp-color-opacity>
|
||
${t.customFill?W`<button class="btn ghost" type="button"
|
||
@click=${()=>this._spaceDialog={...t,customFill:null}}>
|
||
${this._t("btn.reset")}</button>`:U}
|
||
</div>`:U}`)}
|
||
<label class="srcrow">
|
||
${this._boolInput(t.glowEnabled,e=>{this._spaceDialog={...t,glowEnabled:e}})}
|
||
<span>${this._t("space.glow_enabled")}</span>
|
||
</label>
|
||
${t.deleteBlockers?W`<div class="backuperror" role="alert">${this._t("space.delete_blocked",{n:String(t.deleteBlockers)})}</div>`:U}
|
||
</div>
|
||
<div class="row dialog-action-footer" slot="footer">
|
||
${"edit"===t.mode?W`<div class="dialog-action-group dialog-action-danger">
|
||
<button class="btn danger" @click=${this._deleteSpace} ?disabled=${t.busy}>
|
||
<ha-icon icon="mdi:delete-outline"></ha-icon>${this._t("btn.delete")}
|
||
</button>
|
||
</div>`:U}
|
||
<div class="dialog-action-group dialog-action-commit">
|
||
${this._importTotal>0&&"create"===t.mode?W`<button class="btn ghost" @click=${()=>this._skipImport()}>${this._t("btn.skip")}</button>`:U}
|
||
<button class="btn ghost" @click=${i}>${this._t("btn.cancel")}</button>
|
||
<button class="btn on" @click=${this._saveSpaceDialog}
|
||
?disabled=${!t.title.trim()||"file"===t.source&&!(t.planFile||t.planUrl)||t.busy}
|
||
title=${"file"!==t.source||t.planFile||t.planUrl?"":this._t("title.need_plan")}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${t.busy?"…":this._t("btn.save")}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</hp-dialog>`}_renderMergeDialog(){const t=this._mergeDialog,e=this._spaceModel()?.rooms||[],i=(i,s)=>{const n=e.find(t=>t.id===i),o=n?.area?this.hass.areas[n.area]?.name:null;return W`<label class="srcrow">
|
||
<input type="radio" name="mergekeep" .checked=${t.pick===s}
|
||
@change=${()=>this._mergeDialog={...t,pick:s}} />
|
||
<span>${n?.name||""} <span class="muted">· ${o||this._t("merge.no_area")}</span></span>
|
||
</label>`};return W`<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(t.aId,"a")}
|
||
${i(t.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(t,e,i){const s=18*t;return W`<div class="cardpreview">
|
||
<span class="cpname" style="font-size:${(s*e).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(t){const e="temp"===t?this._roomTempSrc:this._roomHumSrc,i=e=>{"temp"===t?this._roomTempSrc=e:this._roomHumSrc=e,this.requestUpdate()},s=this._roomSrcOpen===t;return W`
|
||
<label>${this._t("temp"===t?"room.temp_src_label":"room.hum_src_label")}</label>
|
||
<label class="srcrow">
|
||
<input type="radio" name="rsrc-${t}" .checked=${!e}
|
||
@change=${()=>{i(""),this._roomSrcOpen=null}} />
|
||
<span>${this._t("room.src_average")}</span>
|
||
</label>
|
||
<label class="srcrow">
|
||
<input type="radio" name="rsrc-${t}" .checked=${!!e}
|
||
@change=${()=>{this._roomSrcOpen=t,this._roomSrcFilter="",this.requestUpdate()}} />
|
||
<span>${this._t("room.src_pick")}</span>
|
||
</label>
|
||
${e||s?W`<button class="dropbtn ${s?"open":""}"
|
||
@click=${()=>{this._roomSrcOpen=s?null:t,this._roomSrcFilter=""}}>
|
||
${e?W`<b>${this._roomSrcLabel(e)}</b><span class="ref">${e}</span>`:W`<span class="muted">${this._t("room.src_ph")}</span>`}
|
||
<ha-icon icon=${s?"mdi:chevron-up":"mdi:chevron-down"}></ha-icon>
|
||
</button>
|
||
${s?W`<div class="droppanel">
|
||
<input class="namein" type="text" placeholder=${this._t("marker.search_ph")}
|
||
.value=${this._roomSrcFilter}
|
||
@input=${t=>{this._roomSrcFilter=t.target.value,this.requestUpdate()}} />
|
||
<div class="candlist">
|
||
${this._roomSrcCandidates().map(t=>W`<div class="cand ${t.value===e?"sel":""}"
|
||
@click=${()=>{i(t.value),this._roomSrcOpen=null}}>
|
||
<span class="cl">${t.label}</span><span class="cs">${t.sub}</span>
|
||
</div>`)}
|
||
</div>
|
||
</div>`:U}`:U}`}_renderRoomDialog(){const t=!!this._roomEditId,e=t?null:this._wallFaceBatch,i=e&&e.candidates.length>1?this._t("room.queue_progress",{current:e.index+1,total:e.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(t&&this._areaSel&&!r.some(t=>t.area_id===this._areaSel)){const t=this.hass.areas[this._areaSel];t&&r.unshift(t)}return W`<hp-dialog class="roomdialog" .hass=${this.hass} wide
|
||
.title=${t?this._t("room.settings_title"):i||this._t("room.new")}
|
||
icon=${t?"mdi:cog-outline":"mdi:floor-plan"} @hp-close=${this._roomDialogCancel}>
|
||
<div class="body">
|
||
${i?W`<p class="muted" role="status" aria-live="polite">
|
||
${i}
|
||
</p>`:U}
|
||
<label>${this._t("room.name_label")}</label>
|
||
<input class="namein" type="text" placeholder=${this._t("room.name_ph")}
|
||
.value=${this._nameSel}
|
||
@input=${t=>this._nameSel=t.target.value} />
|
||
<label>${this._t("room.area_label")}</label>
|
||
<select class="areasel"
|
||
@change=${t=>{this._areaSel=t.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(t=>W`<option value=${t.area_id} ?selected=${t.area_id===this._areaSel}>${t.name}</option>`)}
|
||
</select>
|
||
|
||
<label class="dispsection">${this._t("room.settings_section")}</label>
|
||
<label>${this._t("room.fill_label")}</label>
|
||
${[["","fill.inherit"],...ji.map(t=>[t,"fill."+t])].map(([t,e])=>W`<label class="srcrow inline">
|
||
<input type="radio" name="rfill" .checked=${this._roomFill===t}
|
||
@change=${()=>{this._roomFill=t,this.requestUpdate()}} />
|
||
<span>${this._t(e)}</span>
|
||
</label>`)}
|
||
${"custom"===o?W`<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=${t=>{this._roomCustomFill={c:t.detail.color,a:t.detail.opacity}}}></hp-color-opacity>
|
||
${this._roomCustomFill?W`<button class="btn ghost" type="button" @click=${()=>{this._roomCustomFill=null}}>${this._t("btn.reset")}</button>`:U}
|
||
</div>`:U}
|
||
${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),t=>{this._roomNameScale=t/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),t=>{this._roomLabelScale=t/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>
|
||
${t?W`<button class="btn on" @click=${()=>this._saveRoomEdit()} ?disabled=${!this._nameSel.trim()}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${this._t("btn.save")}
|
||
</button>`:W`${this._pendingSplit?U:W`<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>`}}Of.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},_rszSel:{state:!0},_rszLive:{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},_openWallAnchor:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_vacAllCamerasFor:{state:!0},_vacCalConfirm:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomCustomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_backupExportDialog:{state:!0},_backupImportDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},Of.ZOOM_MAX=8,Of.ZOOM_MIN=1/3,Of.styles=[Kt,q_],customElements.get("houseplan-card")||customElements.define("houseplan-card",Of),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-card"===t.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${cf} `,"background:#3ea6ff;color:#04121f;font-weight:700","");
|