+ `,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 Lt(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 Ht(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`${t}`,i),i):null}surface(t,e){return e?this.owner.shadowRoot?.querySelector(t)||null:this.root?.querySelector(t)||null}destroy(){this.root&<(G,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 Ut 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=Ut.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=Lt(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(Ht({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=Lt(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=Ht({h:this._hue,s:this._saturation,v:this._value}),s=Ht({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`
+
${this.label||n.title}
@@ -249,11 +249,11 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6c099935eee8c00d4fe12fcb7a86e6705c51
this._hexInput(t)}
@blur=${()=>this._commitHex()}
@keydown=${t=>this._hexKeyDown(t)} />
- ${this._hexInvalid?W`
${n.invalidHex}`:U}
+ ${this._hexInvalid?W`
${n.invalidHex}`:G}
${this.showOpacity?W`
${this.opacityLabel}
@@ -265,16 +265,16 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6c099935eee8c00d4fe12fcb7a86e6705c51
aria-label=${`${this.opacityLabel}, %`}
@change=${t=>this._setOpacity(Number(t.target.value)/100)} />
%
-
`:U}
+
`:G}
`}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`
${this.label}`:U}
+ ${this.label?W`
${this.label}`:G}
- ${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`
+ ${this._open&&!this.disabled&&this._supportsPopover()?this._pickerTemplate(!0):G}
+ `}}Ut.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}},Ut.styles=o`
:host {
position: relative;
display: inline-flex;
@@ -532,13 +532,13 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6c099935eee8c00d4fe12fcb7a86e6705c51
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`
{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`
${this.text}
`}_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`
+ @pointerenter=${this._surfacePointerEnter} @pointerleave=${this._surfacePointerLeave}>${this.text}
`}_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 G;const t=this.ariaLabel.trim();return W`
${this.text}
- ${this._usesPopover()?this._tooltipTemplate(!0):U}
+ ${this._usesPopover()?this._tooltipTemplate(!0):G}
`}}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;
@@ -1693,7 +1693,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6c099935eee8c00d4fe12fcb7a86e6705c51
.stage .opening-tunnels,
.stage .glow-base-layer,
.stage .glow-pools-frame,
- .stage .openwalls {
+ .stage .zero-walls {
opacity: var(--hp-mode-architecture-opacity, 1);
}
.opening-preview {
@@ -1783,13 +1783,8 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6c099935eee8c00d4fe12fcb7a86e6705c51
fill: var(--error-color, #f44336);
stroke: var(--error-color, #f44336);
}
- /* 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));
+ .zero-wall {
+ stroke: var(--zero-wall-stroke, var(--hp-muted));
stroke-width: calc(2.5px * var(--hp-cell-visual-scale, 1));
stroke-dasharray:
calc(7px * var(--hp-cell-visual-scale, 1))
@@ -1798,7 +1793,10 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6c099935eee8c00d4fe12fcb7a86e6705c51
pointer-events: none;
opacity: 0.9;
}
- /* Rooms with open/thick stretches: the polygon's own stroke is fully off.
+ .zero-walls.solid .zero-wall {
+ stroke-dasharray: none;
+ }
+ /* Rooms with zero/thick stretches: the polygon's own stroke is fully off.
The trimmed .room-outline draws normal walls; View hover gets its own
top overlay after the wall bodies. */
.room.noedge {
@@ -1847,43 +1845,6 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6c099935eee8c00d4fe12fcb7a86e6705c51
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;
}
@@ -1986,6 +1947,20 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6c099935eee8c00d4fe12fcb7a86e6705c51
stroke-opacity: 0.7;
pointer-events: none;
}
+ .drawwall-zero-preview {
+ fill: none;
+ stroke: var(--accent-color, #03a9f4);
+ stroke-width: calc(3px * var(--hp-cell-visual-scale, 1));
+ stroke-linecap: butt;
+ stroke-linejoin: round;
+ opacity: 0.72;
+ pointer-events: none;
+ }
+ .drawwall-zero-preview.dashed {
+ stroke-dasharray:
+ calc(7px * var(--hp-cell-visual-scale, 1))
+ calc(7px * var(--hp-cell-visual-scale, 1));
+ }
.vertex {
fill: #ffc14d;
stroke: #4a2800;
@@ -4363,36 +4338,36 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6c099935eee8c00d4fe12fcb7a86e6705c51
}
.markeractions:empty { display: none; }
.markersaveactions { margin-left: auto; }
-`],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 ge(t){for(var e,i,s=1,n=t.length,o=t[0]+"";s
c^i?1:-1;for(r=(l=n.length)<(c=o.length)?l:c,a=0;ao[a]^i?1:-1;return l==c?0:l>c^i?1:-1}function fe(t,e,i,s){if(ti||t!==oe(t))throw Error(ae+(s||"Argument")+("number"==typeof t?ti?" 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 ey?p.c=p.e=null:t.e=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,g);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;lr){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=f)?ye(l,a):be(l,a,"0");else if(o=(t=T(new M(t),e,i)).e,r=(l=ge(t.c)).length,1==s||2==s&&(e<=o||o<=m)){for(;ra),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=10;n/=10,s++);return(i=s+i*ce-1)>y?t.c=t.e=null:i=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=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),g=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(fe(i[0],-ue,0,e),fe(i[1],0,ue,e),m=i[0],f=i[1]):(fe(i,-ue,ue,e),m=-(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:g,EXPONENTIAL_AT:[m,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=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>>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=9e15?crypto.randomBytes(7).copy(e,r):(l.push(o%1e14),r+=7);r=n/7}if(!b)for(;r=10;o/=10,r++);ri-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,m,f,v=s.indexOf("."),y=_,b=g;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(ge(u.c),u.e,"0"),10,o,t),f.e=f.c.length),h=d=(m=e(s,n,o,r?(l=$,t):(l=t,$))).length;0==m[--d];m.pop());if(!m[0])return l.charAt(0);if(v<0?--h:(u.c=m,u.e=h,u.s=a,m=(u=i(u,f,y,b,o)).c,p=u.r,h=u.e),v=m[c=h+y+1],d=o/2,p=p||c<0||null!=m[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&m[c-1]||b==(u.s<0?8:7)),c<1||!m[0])s=p?be(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(m.length=c,p)for(--o;++m[--c]>o;)m[c]=0,c||(++h,m=[1].concat(m));for(d=m.length;!m[--d];);for(v=0,s="";v<=d;s+=l.charAt(m[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;ne[n]?1:-1;break}return o}function i(t,e,i,s){for(var n=0;i--;)t[i]-=n,n=t[i]1;t.splice(0,1));}return function(s,n,o,a,r){var l,c,h,d,p,u,_,g,m,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(m=(g=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)m.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=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(_=10;S/=10,h++);T(g,o+(g.e=h+c*ce-1)+1,a,d)}else g.e=c,g.r=+d;return g}}(),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 me(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=g: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),_,g)},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,g,void 0):c)},p.integerValue=function(t){var e=new M(this);return null==t?t=g:fe(t,0,8),T(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===me(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return me(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=me(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 me(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=me(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==g?-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;e0)for(;e--;h[i++]=0);for(e=le-1;s>r;){if(h[--s]=0;){for(i=0,p=y[n]%m,u=y[n]/m|0,o=n+(a=l);o>n;)i=((c=p*(c=v[--a]%m)+(r=u*c+(h=v[a]/m|0)*p)%m*m+_[o]+i)/g|0)+(r/m|0)+u*h,_[o--]=c%g;_[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=g: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=ge(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))),ge(o.c).slice(0,l)===(e=ge(s.c)).slice(0,l)){if(s.e0&&_>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,_,m=this,f=m.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(m);for(e=new M(u),h=s=new M(u),n=c=new M(u),_=ge(f),a=e.e=_.length-m.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=m.s,p=i(h,n,a*=2,g).minus(m).abs().comparedTo(i(c,s,a,g).minus(m).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<=m||o>=f?ye(ge(i.c),o):be(ge(i.c),o,"0"):10===t&&S?e=be(ge((i=T(new M(i),_+o+1,g)).c),i.e,"0"):(fe(t,2,$.length,"Base"),e=s(be(ge(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)=>te?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)),Le=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),He=t=>Le(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(He(n)).div(He(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 Le(n,s).div(He(n)).div(He(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{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;s0&&(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;t0?(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;t0)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.ide.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;t0){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;t1===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;eXe.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=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=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[t[0]+r[0]*e,t[1]+r[1]*e],m=(h+d)/2;return{wallA:g(h),wallB:g(d),sideA:u,sideB:_,midA:g((h-p)/2),midB:g((p+d)/2),wallCenter:g(m),centered:Math.abs(m)<=n}}function gi(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 mi(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])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;sn&&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;e1e-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;ng&&(g=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>g&&(g=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;sCi(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[0],t[1]]),[t[0][0],t[0][1]]]]}function Ii(t,e,i){for(let s=0;s1&&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{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=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=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 se?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 gs={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"]},ms=[["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(gs),...ms])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=gs[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;np||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,g=Math.max(0,Math.min(u,_)),m=Math.min(c,Math.max(u,_));m-g>i&&s.push([o[0]+h*g,o[1]+d*g,o[0]+h*m,o[1]+d*m])}}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(lo||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{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 Ls(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 Hs=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&&Hs.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 gn(t,e=0){return _n(t,e).snapshot}function mn(t,e,i,s){const n=_n(e,i);return s&&n.valid&&n.snapshot.configRev===i?t.configRev===i&&n.snapshot.rev{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 Ln(t,e){const i=[];for(const[s,n]of Object.entries(t||{}))n?.device_id===e&&i.push(s);return i}function Hn(){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=Ln(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 Hn()[t]||null}(e);if("device"===n){const e=a[o],i=Ln(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?"":[...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?"":[...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??""}`).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 g=new Map;for(const t of e){const e=h.get(t)||[],i=new Set(void 0===t.controls?e.filter(Ms):t.controls);g.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 m=new Map,f=t=>{const e=m.get(t.key);(!e||t.castsGlow&&!e.castsGlow)&&m.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)||!g.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=[...m.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 go(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 mo(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),g=yo(s,u).filter(t=>!ko(s,t.eid,_)),m=new Set(g.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 g=_.entityIds,v=_.partial?{kind:"active",enabledEntityIds:g,allEntityIds:g}:a,$=Gn(s,t,g),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,g,p);if(g.some(t=>t.startsWith("lock."))&&(D="mdi:lock"),S&&!c&&u&&"mdi:lightbulb"===D&&m.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:g,allEntities:v.allEntityIds,bindingStatus:v,bindingKind:"device",bindingRef:t.id,pdfs:[]};T.primary=Jn(s,g,D),"mdi:thermometer"!==D&&"mdi:air-filter"!==D||(T.temp=go(s,g)),T.primary&&fo(s,T.primary)&&(T.hum=vo(s,g)),x.push(T)}for(const t of g)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),g="active"===e.kind?e.enabledEntityIds:[];let m=a?bo(s,a.name_by_user||a.name||"",a.model,g,p):"mdi:help-circle";g.some(t=>t.startsWith("lock."))&&(m="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:m,entities:g,allEntities:e.allEntityIds.length?e.allEntityIds:a&&v[a.id]||[],bindingStatus:e,bindingKind:"device",bindingRef:l};f.primary=Jn(s,g,m),"mdi:thermometer"!==m&&"mdi:air-filter"!==m||(f.temp=go(s,g)),f.primary&&fo(s,f.primary)&&(f.hum=vo(s,g)),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),g=s.states[l],m=a?.name||g?.attributes?.friendly_name||l;let f=bo(s,m,"",[l],p);l.startsWith("lock.")&&(f="mdi:lock");const v={id:t.id,name:m,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=go(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?go(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=go(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 Lo(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 Ho(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=Lo(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=Ho(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`
+`],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]+"";sc^i?1:-1;for(r=(l=n.length)<(c=o.length)?l:c,a=0;ao[a]^i?1:-1;return l==c?0:l>c^i?1:-1}function fe(t,e,i,s){if(ti||t!==oe(t))throw Error(ae+(s||"Argument")+("number"==typeof t?ti?" 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 ey?p.c=p.e=null:t.e=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;lr){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=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(;ra),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=10;n/=10,s++);return(i=s+i*ce-1)>y?t.c=t.e=null:i=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=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=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>>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=9e15?crypto.randomBytes(7).copy(e,r):(l.push(o%1e14),r+=7);r=n/7}if(!b)for(;r=10;o/=10,r++);ri-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;ne[n]?1:-1;break}return o}function i(t,e,i,s){for(var n=0;i--;)t[i]-=n,n=t[i]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=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(_=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;e0)for(;e--;h[i++]=0);for(e=le-1;s>r;){if(h[--s]=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.e0&&_>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)=>te?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)),Le=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),He=t=>Le(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(He(n)).div(He(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 Le(n,s).div(He(n)).div(He(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)))},Ue=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{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}}},Ge=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,n=e.length;s0&&(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;t0?(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;t0)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.ide.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=Ue.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 Ue(n,!0),c=new Ue(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 Ue(e,!0),o=new Ue(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 Ue.comparePoints(r.leftSE.point,r.rightSE.point)>0&&r.swapEvents(),Ue.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;t0){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;t1===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;eXe.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=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=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[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])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;sn&&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;e1e-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;nm&&(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;sCi(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[0],t[1]]),[t[0][0],t[0][1]]]]}function Ii(t,e,i){for(let s=0;s1&&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{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=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=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 se?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;np||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(lo||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{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 Ls(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 Hs=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&&Hs.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"]),Us=new Set(["occupancy","presence"]),Gs=new Set(["door","window","garage_door","opening"]),Vs=new Set(["running","power"]),Ks=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Ys=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),Js=new Set([...Ys,"start","started","run","active","in_progress","wash","rinse","spin","dry"]),Xs=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping","stop","end","done","inactive"]),Zs=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),Qs=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"}:Us.has(o)?{...a,activity:"on"===s?"presence":"none"}:Gs.has(o)?{...a,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===o?{...a,activity:"on"===s?"transition":"none"}:Vs.has(o)&&"on"===s?{...a,status:"working",activity:"running"}:a;if("cover"===n)return{...a,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===n)return{...a,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===n)return{...a,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===n){const 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{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 Ln(t,e){const i=[];for(const[s,n]of Object.entries(t||{}))n?.device_id===e&&i.push(s);return i}function Hn(){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=Ln(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 Hn()[t]||null}(e);if("device"===n){const e=a[o],i=Ln(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 Un(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 Gn(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)||Us.has(i)||Gs.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=>Gn(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?"":[...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?"":[...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??""}`).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(!Gn(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,$=Un(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 Lo(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 Ho(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=>Gn(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=Lo(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=Ho(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 Uo(t){return"boolean"==typeof t?.themes?.darkMode?t.themes.darkMode?"theme-dark":"theme-light":""}function Go(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``:U}
- ${"dot"===i.reducedMotionIndicator?W``:U}
- ${e.newDevice?W``:U}
- ${t.haDisabled?W``:U}
+ aria-hidden="true">`:G}
+ ${"dot"===i.reducedMotionIndicator?W``:G}
+ ${e.newDevice?W``:G}
+ ${t.haDisabled?W``:G}
${null!=t.valueText?W`${t.valueText}`:W``}
+ style=${t.angle?`transform:rotate(${t.angle}deg)`:G}>`}
${r?W`
${o?W`${o.text}`:U}
+ >${o.text}`:G}
${n.map(t=>W`${t.text}${t.suffix}`)}
- `:U}
+ `:G}
${null!=t.lqiText?W`${t.lqiText}`: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.","toast.wall_model_migration_blocked":"The wall model could not be updated: {reason}. The plan was not changed. Run “Optimize plans”; if the error repeats, fix the conflicting wall geometry.","toast.wall_model_client_outdated":"Update the card and reload the page before editing the plan.","wall_model.reason.invalid-room":"invalid room contour","wall_model.reason.zero-length":"zero-length wall segment","wall_model.reason.third-owner":"wall shared by more than two rooms","wall_model.reason.duplicate-id":"conflicting wall identifiers","wall_model.reason.thickness-conflict":"conflicting wall thickness values","wall_model.reason.opening-host":"opening cannot be assigned to one wall","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. Copy the diagnostics with the button below and attach them to the bug report together with a space export.","gs.preflight_reason_prepare-exception":"Could not prepare the space geometry (exception while building the model)","gs.preflight_reason_wall-null":"The wall body did not build (the union came back empty)","gs.preflight_reason_wall-degraded-extra":"The wall body degraded with extra geometry","gs.preflight_reason_wall-failed-core":"The wall body core failed to assemble","gs.preflight_reason_wall-exception":"Wall construction threw an exception","gs.preflight_reason_floor-null":"The floor outline did not build","gs.preflight_reason_floor-exception":"Floor construction threw an exception","gs.preflight_copy":"Copy diagnostics","gs.preflight_copied":"Diagnostics copied","gs.preflight_update_hint":"The card and integration versions differ — update House Plan and retry.","gs.align_preflight_space":"Space {n}","gs.align_preflight_more":", and {n} more","gs.optimize_changes":"Model migrations: {m}; spaces updated: {c}; noisy coordinate values removed: {p}; merged real-wall fragments: {w}; virtual fragments: {s}; independent walls: {i}.","gs.wall_segments_migrated":"Wall segments stabilised: {n}.","gs.optimize_lattice_summary":"Noisy coordinate values canonicalized: {n}; maximum movement: {cm} cm.","gs.optimize_lattice_space":"{space}: coordinate values canonicalized: {n}; off-grid values left unchanged: {far}.","gs.optimize_coincident_partitions":"Hidden independent wall sections absorbed into room walls: {n}.","gs.optimize_openings_rehosted":"Openings reattached to room walls: {n}.","gs.optimize_walls_straightened":"Walls straightened: {n}; maximum movement: {cm} cm.","gs.optimize_walls_straightened_where":"Largest wall correction: {s}.","gs.optimize_walls_straighten_skipped":"Near-axis walls left unchanged because they could not be repaired safely: {n}.","gs.optimize_glow_migration":"Legacy Glow: {spaces} spaces → no data fill + independent Glow; {rooms} rooms → inherited data fill + independent Glow.","gs.optimize_references":"References repaired: spaces — {spaces}; rooms — {rooms}; positions — {positions}; devices detached from missing spaces — {detached}.","gs.optimize_reference_more":", and {n} more","gs.optimize_orphans_removed":"Forgotten records removed: {total} — room labels: {rooms}; devices: {devices}; group markers: {groups}. They belonged to spaces deleted earlier.","gs.optimize_live_positions":"Old positions in deleted spaces belong to existing objects: {n}{names}. They will be kept.","gs.optimize_live_positions_remove":"Old positions in deleted spaces belong to existing objects: {n}{names}. They are selected for removal.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Remove old positions","gs.optimize_live_keep":"Keep old positions","gs.optimize_live_selected":"Old positions will be removed after Optimize is applied.","gs.optimize_unverified":"Could not safely verify positions: {n}. They were left unchanged.","gs.optimize_registry_limited":"Full administrator access to the Home Assistant registries is required for a safe check.","gs.optimize_vacuum_warning":"Vacuum room mappings that still need review: {n}.","gs.optimize_details":"Details","gs.optimize_details_more":"And {n} more records.","gs.optimize_detail_removed":"will be removed","gs.optimize_detail_live":"will be kept","gs.optimize_detail_unverified":"not verified","gs.optimize_detail_room_label":"room label","gs.optimize_detail_device":"device","gs.optimize_detail_group":"group marker","gs.optimize_detail_unknown":"unknown owner","gs.optimize_detail_item":"{status}: {kind} {id}; old space {space}","gs.align_warn":"Elements deliberately placed between grid nodes will move. One undo is available after the operation, only until the next plan edit.","gs.align_run":"Optimize","gs.align_done":"Plans optimized: {n} elements moved, {m} records maintained, {r} references repaired","gs.optimize_undo":"Undo last optimization","gs.optimize_undone":"The last optimization was undone","decor.furniture":"Furniture","furn.title":"Furniture library","furn.symbol":"Symbol","furn.group_furniture":"Furniture","furn.group_appliance":"Appliances","furn.group_sanitary":"Plumbing","furn.group_other":"Other","furn.width":"Width","furn.depth":"Depth","furn.pick_hint":"Pick a symbol, then click on the plan.","furn.place_hint":"Click on the plan — the piece lands against the nearest wall. Shift places it free.","furn.sym_sofa":"Sofa","furn.sym_armchair":"Armchair","furn.sym_coffee_table":"Coffee table","furn.sym_table_dining":"Dining table","furn.sym_table_round":"Round table","furn.sym_chair":"Chair","furn.sym_desk":"Desk","furn.sym_bed_double":"Double bed","furn.sym_bed_single":"Single bed","furn.sym_nightstand":"Nightstand","furn.sym_wardrobe":"Wardrobe","furn.sym_bookshelf":"Bookshelf","furn.sym_fridge":"Fridge","furn.sym_stove":"Cooker","furn.sym_dishwasher":"Dishwasher","furn.sym_washer":"Washing machine","furn.sym_dryer":"Tumble dryer","furn.sym_tv":"TV","furn.sym_ac":"Air conditioner","furn.sym_water_heater":"Water heater","furn.sym_toilet":"Toilet","furn.sym_bathtub":"Bathtub","furn.sym_shower":"Shower","furn.sym_sink":"Washbasin","furn.sym_kitchen_sink":"Kitchen sink","furn.sym_bidet":"Bidet","furn.sym_stairs":"Stairs","furn.sym_fireplace":"Fireplace","furn.sym_plant":"Plant","furn.sym_rug":"Rug","common.yes":"Yes","common.no":"No","vac.diag_source":"Source","vac.diag_platform":"Integration","vac.diag_status":"Status","vac.diag_position":"Position","vac.diag_rooms":"Rooms","vac.diag_rooms_value":"{total} · {matched} names match · {readiness}","vac.autocal_ready":"auto-calibration available","vac.autocal_not_ready":"need 3 matching names","vac.diag_path":"Integration path","vac.diag_map":"Map ID","vac.source_none":"not selected","vac.source_status_ok":"Ready","vac.source_status_missing":"Missing","vac.source_status_disabled":"Disabled in Home Assistant","vac.source_status_unavailable":"Unavailable","vac.source_status_unverified":"Cannot verify with current permissions","vac.source_status_unsupported":"No position data","vac.source_status_none":"No source","vac.source_banner_missing":"The saved source no longer exists. It was not replaced automatically; choose another source or restore it in Home Assistant.","vac.source_banner_disabled":"The saved source is disabled in Home Assistant. Enable it there or choose another source.","vac.source_banner_unverified":"Current Home Assistant permissions cannot verify this saved source. It remains pinned and will not be replaced automatically.","vac.choose_source":"Choose source","vac.source_auto":"Automatic","vac.source_auto_hint":"Use a compatible entity from this device","vac.all_cameras":"All cameras","vac.all_cameras_warn":"A camera may not provide robot data. Choose one only if it is your vacuum map.","vac.all_cameras_empty":"No other camera entities found.","vac.platform_unknown":"unknown integration","vac.cap_position":"position","vac.cap_rooms_short":"rooms","vac.cap_path":"path","vac.cap_map":"map ID","vac.cap_none":"no robot data detected","vac.xcme_hint":"Enable these Xiaomi Cloud Map Extractor attributes:","vac.documentation":"Documentation","vac.residual_title":"Check automatic calibration","vac.residual_message":"The matched rooms disagree by up to {error}. Apply this approximate calibration, refine it manually, or cancel without changing the saved setup.","vac.apply_proposal":"Apply","gs.backup_group":"Backup and transfer","gs.backup_hint":"Download a portable JSON backup or preview and import a backup made by House Plan.","backup.export_open":"Export","backup.import_open":"Import","backup.export_title":"Export House Plan","backup.import_title":"Import House Plan","backup.export_hint":"Choose whether the backup should contain the whole House Plan configuration or only the current space.","backup.full":"Full backup","backup.current_space":"Current space","backup.current_space_title":"Current space: {title}","backup.no_current_space":"No current space","backup.plan_only":"Plan only","backup.plan_only_hint":"Keep rooms, walls, openings, decor and room-label positions without devices or Home Assistant bindings.","backup.plan_only_preview":"This file contains the plan only","backup.privacy_warning":"The archive keeps names, Home Assistant identifiers and exact coordinates. Internal plans and attachments are referenced, not embedded; runtime states and vacuum trails are not included.","backup.download":"Download JSON","backup.export_done":"Backup downloaded","backup.reading":"Checking the backup…","backup.revalidated":"The plan changed after this preview. The summary was refreshed; review it and confirm again.","backup.error.unauthorized":"You do not have permission to export or import this plan.","backup.error.not_ready":"House Plan is not ready yet. Try again in a moment.","backup.error.too_large":"The backup exceeds the 8 MiB limit.","backup.error.invalid_json":"The selected file is not valid JSON.","backup.error.invalid_format":"The selected file is not a House Plan backup.","backup.error.unsupported_export_version":"This backup format is not supported by the installed version.","backup.error.future_model":"The backup was created by a newer House Plan data model.","backup.error.invalid_config":"The backup contains an invalid House Plan configuration.","backup.error.wall_model_migration_blocked":"The backup wall model could not be upgraded safely. Optimize the source plan first, then export it again.","backup.error.invalid_layout":"The backup contains invalid object positions.","backup.error.invalid_content":"The backup contains invalid or inconsistent content references.","backup.error.space_not_found":"The selected space no longer exists.","backup.error.capacity_exceeded":"Adding this backup would exceed the plan limits.","backup.error.preview_expired":"The preview expired. Select the backup file again.","backup.error.preview_owner_mismatch":"This preview belongs to another Home Assistant user.","backup.error.conflict":"The plan changed after the preview. Review the refreshed summary.","backup.error.content_confirmation_required":"Confirm that unavailable local content may be detached.","backup.error.commit_failed":"The backup could not be applied safely. The previous plan was restored or is pending recovery.","backup.error.missing_plan":"A referenced plan file disappeared after the preview. Review the backup again.","backup.error.missing_content":"A referenced local attachment disappeared after the preview. Review the backup again.","backup.error.marker_control_missing":"A linked plan light is missing from the backup.","backup.error.marker_control_not_light":"A linked plan target is no longer marked as a light source.","backup.error.marker_control_self":"A light source cannot control itself.","backup.error.marker_control_cycle":"The backup contains a circular chain of light controls.","backup.error.duplicate_marker_control":"The backup contains a duplicate plan-light link.","backup.error.no_backup":"There is no import or optimization snapshot to restore.","backup.same_source":"Created on this Home Assistant instance","backup.foreign_source":"Created on another Home Assistant instance","backup.created":"Created: {value}","backup.versions":"Card {card}; integration {integration}; data model {model}","backup.count_spaces":"Spaces: {n}","backup.count_rooms":"Rooms: {n}","backup.count_walls":"Walls: {n}","backup.count_openings":"Openings: {n}","backup.count_decor":"Decor objects: {n}","backup.count_markers":"Devices: {n}","backup.count_layout":"Positions: {n}","backup.bindings":"Bindings — devices: {device}, entities: {entity}, virtual: {virtual}; positions without a space: {legacy}","backup.binding_status":"Target status — active: {active}, disabled: {disabled}, missing: {missing}","backup.missing_areas":"Areas missing on the target: {areas}","backup.dropped_marker_links":"Plan-light links outside this transferred space were omitted: {n}.","backup.repaired_target_refs":"Existing references restored by this space import: {n}.","backup.preserved_unresolved_refs":"References that could not be restored unambiguously were preserved: {n}.","backup.preserved_unresolved_hint":"No data was guessed or deleted. After the import, run Optimize plans to inspect the remaining references.","backup.import_details":"Import reference details","backup.import_detail.incoming_remapped":"References updated inside the imported copy: {n}","backup.import_detail.target_repaired":"Existing references restored: {n}","backup.import_detail.preserved_unresolved":"Unresolved references preserved: {n}","backup.import_detail.collisions":"Destination conflicts preserved safely: {n}","backup.import_detail.dropped_links":"Incoming links omitted by transfer rules: {n}","backup.import_detail.bounded_lineages":"Overly nested identifiers left bounded: {n}","backup.replace_warning":"This replaces the current configuration and layout. Uploaded files are never deleted. One undo remains available until the next plan edit.","backup.foreign_bookkeeping":"Instance-specific known/new-device bookkeeping will not be imported.","backup.final_name":"New space name","backup.target_settings":"The target instance's global settings remain unchanged.","backup.duplicates":"Bindings already present: {n}","backup.skip":"Skip duplicate devices","backup.virtual_copy":"Add safe static virtual copies","backup.content":"Referenced content","backup.content_available":"available locally","backup.content_external":"external link","backup.content_detach_required":"will be detached","backup.confirm_detach":"I understand that unavailable internal plans and attachments will be detached from the imported configuration.","backup.replace":"Replace","backup.add":"Add space","backup.space_done":"Space imported: {rooms} rooms, {markers} devices, {refs} existing references restored","backup.full_done":"Backup restored: {spaces} spaces, {rooms} rooms, {markers} devices","backup.undo_import":"Undo last full import","backup.import_undone":"Full import undone"};const aa={en:oa,ru:{"editor.context_actions":"Действия: {object}","editor.tool_options":"Параметры инструмента: {tool}","editor.palette":"Палитра: {tool}","editor.open_group":"Группа инструментов: {group}","editor.group_active":"{group} — активно: {item}","editor.disabled_action":"{action} недоступно: {reason}","color_picker.title":"Выбор цвета","color_picker.hue":"Оттенок","color_picker.saturation":"Насыщенность","color_picker.value":"Яркость","color_picker.hex":"Цвет HEX","color_picker.invalid_hex":"Введите цвет HEX из 3 или 6 цифр","btn.properties":"Свойства","btn.keep_as_walls":"Оставить стенами","btn.delete_room_keep_walls":"Удалить комнату, оставить стены","btn.delete_room_with_walls":"Удалить комнату и стены","title.markup_select":"Выбор и редактирование стен, колонн и сохранённых контуров","title.markup_column":"Колонна: кликните точку сетки для квадратной колонны","markup.partition":"Перегородка","markup.column":"Колонна","markup.hint_column":"кликните точку сетки, чтобы поставить квадратную колонну","history.draft_segment":"Добавление сегмента черновика комнаты","history.draft_merge":"Соединение незавершённых контуров комнаты","history.draft_segment_delete":"Удаление сегмента черновика комнаты","history.partition_add":"Добавление перегородки","history.column_add":"Добавление колонны","history.physical_edit":"Изменение физического объекта","history.physical_delete":"Удаление физического объекта","history.physical_move":"Перемещение физического объекта","history.contour_to_partitions":"Преобразование контура в замкнутые стены","history.wall_chain_finish":"Завершение цепочки стен","history.wall_face_batch":"Создание комнат из стен","toast.column_duplicate":"Колонна с тем же центром и внешним размером уже существует","confirm.delete_draft":"Удалить весь незавершённый контур комнаты?","confirm.delete_draft_segment":"Удалить этот сегмент черновика? Оставшийся контур может разделиться на два.","physical.partition_properties":"Свойства перегородки","physical.column_properties":"Свойства колонны","physical.draft_properties":"Свойства сегмента черновика","physical.shape":"Форма","physical.square":"Квадрат","physical.circle":"Круг","physical.diameter":"Диаметр","physical.side":"Сторона","physical.rotation":"Угол поворота","physical.length":"Длина","physical.allowed_range":"Допустимо: 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":"Изменение отменено: геометрию стен нельзя безопасно построить.","toast.wall_model_migration_blocked":"Не удалось обновить модель стен: {reason}. План не изменён. Запустите «Оптимизировать планы»; если ошибка повторится, исправьте конфликтующую геометрию стен.","toast.wall_model_client_outdated":"Обновите карточку и перезагрузите страницу перед редактированием плана.","wall_model.reason.invalid-room":"некорректный контур комнаты","wall_model.reason.zero-length":"отрезок стены нулевой длины","wall_model.reason.third-owner":"стена принадлежит более чем двум комнатам","wall_model.reason.duplicate-id":"конфликт идентификаторов стен","wall_model.reason.thickness-conflict":"конфликт значений толщины стены","wall_model.reason.opening-host":"проём нельзя однозначно привязать к стене","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":"Планы не изменены. Скопируйте диагностику кнопкой ниже и приложите её к отчёту об ошибке вместе с экспортом пространства.","gs.preflight_reason_prepare-exception":"Не удалось подготовить геометрию пространства (исключение при сборке модели)","gs.preflight_reason_wall-null":"Тело стен не построилось (объединение вернуло пустоту)","gs.preflight_reason_wall-degraded-extra":"Тело стен деградировало с лишней геометрией","gs.preflight_reason_wall-failed-core":"Ядро тела стен не собралось","gs.preflight_reason_wall-exception":"Построение стен упало с исключением","gs.preflight_reason_floor-null":"Контур пола не построился","gs.preflight_reason_floor-exception":"Построение пола упало с исключением","gs.preflight_copy":"Скопировать диагностику","gs.preflight_copied":"Диагностика скопирована","gs.preflight_update_hint":"Версии карточки и интеграции различаются — обновите House Plan и повторите.","gs.align_preflight_space":"Пространство {n}","gs.align_preflight_more":" и ещё {n}","gs.optimize_changes":"Миграций модели: {m}; обновлено пространств: {c}; устранён шум координат: {p}; объединено отрезков реальных стен: {w}; виртуальных: {s}; независимых: {i}.","gs.wall_segments_migrated":"Стабилизировано сегментов стен: {n}.","gs.optimize_lattice_summary":"Канонизировано шумовых значений координат: {n}; максимальный сдвиг: {cm} см.","gs.optimize_lattice_space":"{space}: канонизировано значений координат: {n}; оставлено значений вне сетки: {far}.","gs.optimize_coincident_partitions":"Скрытые участки независимых стен поглощены стенами комнат: {n}.","gs.optimize_openings_rehosted":"Проёмы перепривязаны к стенам комнат: {n}.","gs.optimize_walls_straightened":"Выпрямлено стен: {n}; максимальное перемещение: {cm} см.","gs.optimize_walls_straightened_where":"Максимальная правка стены: {s}.","gs.optimize_walls_straighten_skipped":"Оставлено почти осевых стен, которые нельзя безопасно выпрямить: {n}.","gs.optimize_glow_migration":"Старый Glow: пространств — {spaces} → без заливки данных + независимый Glow; комнат — {rooms} → наследуемая заливка + независимый Glow.","gs.optimize_references":"Исправлено ссылок: пространства — {spaces}; комнаты — {rooms}; позиции — {positions}; устройств отвязано от отсутствующих пространств — {detached}.","gs.optimize_reference_more":", и ещё {n}","gs.optimize_orphans_removed":"Убрано забытых записей: {total} — подписи комнат: {rooms}; устройства: {devices}; групповые метки: {groups}. Все они принадлежали пространствам, удалённым ранее.","gs.optimize_live_positions":"Старые позиции в удалённых пространствах принадлежат существующим объектам: {n}{names}. Они будут сохранены.","gs.optimize_live_positions_remove":"Старые позиции в удалённых пространствах принадлежат существующим объектам: {n}{names}. Они выбраны для удаления.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Убрать старые позиции","gs.optimize_live_keep":"Сохранить старые позиции","gs.optimize_live_selected":"Старые позиции будут убраны после применения оптимизации.","gs.optimize_unverified":"Не удалось безопасно проверить позиций: {n}. Они оставлены без изменений.","gs.optimize_registry_limited":"Для безопасной проверки нужен полный доступ администратора к реестрам Home Assistant.","gs.optimize_vacuum_warning":"Сопоставления комнат пылесоса требуют проверки: {n}.","gs.optimize_details":"Подробности","gs.optimize_details_more":"И ещё записей: {n}.","gs.optimize_detail_removed":"будет удалено","gs.optimize_detail_live":"будет сохранено","gs.optimize_detail_unverified":"не проверено","gs.optimize_detail_room_label":"подпись комнаты","gs.optimize_detail_device":"устройство","gs.optimize_detail_group":"групповая метка","gs.optimize_detail_unknown":"неизвестный владелец","gs.optimize_detail_item":"{status}: {kind} {id}; прежнее пространство {space}","gs.align_warn":"Элементы, намеренно поставленные между узлами, будут сдвинуты. После операции доступна одна отмена — только до следующего изменения плана.","gs.align_run":"Оптимизировать","gs.align_done":"Планы оптимизированы: сдвинуто элементов — {n}, обслужено записей — {m}, исправлено ссылок — {r}","gs.optimize_undo":"Отменить последнюю оптимизацию","gs.optimize_undone":"Последняя оптимизация отменена","decor.furniture":"Мебель","furn.title":"Библиотека мебели","furn.symbol":"Символ","furn.group_furniture":"Мебель","furn.group_appliance":"Техника","furn.group_sanitary":"Сантехника","furn.group_other":"Прочее","furn.width":"Ширина","furn.depth":"Глубина","furn.pick_hint":"Выберите символ и кликните по плану.","furn.place_hint":"Кликните по плану — предмет встанет к ближайшей стене. Shift — свободно.","furn.sym_sofa":"Диван","furn.sym_armchair":"Кресло","furn.sym_coffee_table":"Журнальный столик","furn.sym_table_dining":"Обеденный стол","furn.sym_table_round":"Круглый стол","furn.sym_chair":"Стул","furn.sym_desk":"Письменный стол","furn.sym_bed_double":"Двуспальная кровать","furn.sym_bed_single":"Односпальная кровать","furn.sym_nightstand":"Тумбочка","furn.sym_wardrobe":"Шкаф","furn.sym_bookshelf":"Стеллаж","furn.sym_fridge":"Холодильник","furn.sym_stove":"Плита","furn.sym_dishwasher":"Посудомоечная машина","furn.sym_washer":"Стиральная машина","furn.sym_dryer":"Сушильная машина","furn.sym_tv":"Телевизор","furn.sym_ac":"Кондиционер","furn.sym_water_heater":"Бойлер","furn.sym_toilet":"Унитаз","furn.sym_bathtub":"Ванна","furn.sym_shower":"Душ","furn.sym_sink":"Раковина","furn.sym_kitchen_sink":"Кухонная мойка","furn.sym_bidet":"Биде","furn.sym_stairs":"Лестница","furn.sym_fireplace":"Камин","furn.sym_plant":"Растение","furn.sym_rug":"Ковёр","common.yes":"Да","common.no":"Нет","vac.diag_source":"Источник","vac.diag_platform":"Интеграция","vac.diag_status":"Статус","vac.diag_position":"Позиция","vac.diag_rooms":"Комнаты","vac.diag_rooms_value":"{total} · совпало имён: {matched} · {readiness}","vac.autocal_ready":"автокалибровка доступна","vac.autocal_not_ready":"нужно 3 совпавших имени","vac.diag_path":"Путь интеграции","vac.diag_map":"ID карты","vac.source_none":"не выбран","vac.source_status_ok":"Готов","vac.source_status_missing":"Не найден","vac.source_status_disabled":"Деактивирован в Home Assistant","vac.source_status_unavailable":"Недоступен","vac.source_status_unverified":"Нельзя проверить с текущими правами","vac.source_status_unsupported":"Нет данных позиции","vac.source_status_none":"Нет источника","vac.source_banner_missing":"Сохранённый источник больше не существует. Он не заменён автоматически: выберите другой или восстановите его в Home Assistant.","vac.source_banner_disabled":"Сохранённый источник деактивирован в Home Assistant. Активируйте его там или выберите другой.","vac.source_banner_unverified":"Текущих прав Home Assistant недостаточно для проверки источника. Привязка сохранена и не будет заменена автоматически.","vac.choose_source":"Выбрать источник","vac.source_auto":"Автоматически","vac.source_auto_hint":"Использовать совместимую сущность этого устройства","vac.all_cameras":"Все камеры","vac.all_cameras_warn":"Камера может не отдавать данные робота. Выбирайте её только если это карта вашего пылесоса.","vac.all_cameras_empty":"Других сущностей камеры не найдено.","vac.platform_unknown":"интеграция неизвестна","vac.cap_position":"позиция","vac.cap_rooms_short":"комнаты","vac.cap_path":"путь","vac.cap_map":"ID карты","vac.cap_none":"данные робота не обнаружены","vac.xcme_hint":"Включите атрибуты Xiaomi Cloud Map Extractor:","vac.documentation":"Документация","vac.residual_title":"Проверьте автокалибровку","vac.residual_message":"Совпавшие комнаты расходятся максимум на {error}. Примените приблизительную калибровку, подгоните её вручную либо отмените без изменения сохранённых настроек.","vac.apply_proposal":"Применить","gs.backup_group":"Резервная копия и перенос","gs.backup_hint":"Скачайте переносимую JSON-копию или проверьте и импортируйте копию, созданную House Plan.","backup.export_open":"Экспорт","backup.import_open":"Импорт","backup.export_title":"Экспорт House Plan","backup.import_title":"Импорт House Plan","backup.export_hint":"Выберите, должна ли копия содержать всю конфигурацию House Plan или только текущее пространство.","backup.full":"Полная копия","backup.current_space":"Текущее пространство","backup.current_space_title":"Текущее пространство: {title}","backup.no_current_space":"Нет текущего пространства","backup.plan_only":"Только планировка","backup.plan_only_hint":"Сохранить комнаты, стены, проёмы, декор и позиции подписей комнат без устройств и привязок Home Assistant.","backup.plan_only_preview":"Файл содержит только планировку","backup.privacy_warning":"Архив сохраняет названия, идентификаторы Home Assistant и точные координаты. Внутренние планы и вложения указываются ссылками, но не вкладываются; текущие состояния и маршруты пылесосов не включаются.","backup.download":"Скачать JSON","backup.export_done":"Резервная копия скачана","backup.reading":"Проверяем резервную копию…","backup.revalidated":"После предпросмотра план изменился. Сводка обновлена — проверьте её и подтвердите ещё раз.","backup.error.unauthorized":"У вас нет прав на экспорт или импорт этого плана.","backup.error.not_ready":"House Plan ещё не готов. Повторите попытку через несколько секунд.","backup.error.too_large":"Размер резервной копии превышает 8 МиБ.","backup.error.invalid_json":"Выбранный файл не является корректным JSON.","backup.error.invalid_format":"Выбранный файл не является резервной копией House Plan.","backup.error.unsupported_export_version":"Эта версия формата резервной копии не поддерживается установленной версией.","backup.error.future_model":"Резервная копия создана в более новой версии модели данных House Plan.","backup.error.invalid_config":"Резервная копия содержит некорректную конфигурацию House Plan.","backup.error.wall_model_migration_blocked":"Не удалось безопасно обновить модель стен из резервной копии. Сначала оптимизируйте исходный план и повторите экспорт.","backup.error.invalid_layout":"Резервная копия содержит некорректные позиции объектов.","backup.error.invalid_content":"Резервная копия содержит некорректные или несогласованные ссылки на файлы.","backup.error.space_not_found":"Выбранное пространство больше не существует.","backup.error.capacity_exceeded":"Добавление этой копии превысит ограничения размера плана.","backup.error.preview_expired":"Время предпросмотра истекло. Выберите файл резервной копии заново.","backup.error.preview_owner_mismatch":"Этот предпросмотр принадлежит другому пользователю Home Assistant.","backup.error.conflict":"После предпросмотра план изменился. Проверьте обновлённую сводку.","backup.error.content_confirmation_required":"Подтвердите отсоединение недоступных локальных файлов.","backup.error.commit_failed":"Не удалось безопасно применить копию. Предыдущий план восстановлен или ожидает восстановления.","backup.error.missing_plan":"После предпросмотра исчез связанный файл плана. Проверьте копию ещё раз.","backup.error.missing_content":"После предпросмотра исчез связанный локальный файл. Проверьте копию ещё раз.","backup.error.marker_control_missing":"В резервной копии отсутствует связанный источник света на плане.","backup.error.marker_control_not_light":"Связанная цель больше не отмечена как источник света.","backup.error.marker_control_self":"Источник света не может управлять самим собой.","backup.error.marker_control_cycle":"Резервная копия содержит циклическую цепочку управления светом.","backup.error.duplicate_marker_control":"Резервная копия содержит повторяющуюся связь с источником света.","backup.error.no_backup":"Нет снимка импорта или оптимизации, который можно восстановить.","backup.same_source":"Создано на этом экземпляре Home Assistant","backup.foreign_source":"Создано на другом экземпляре Home Assistant","backup.created":"Создано: {value}","backup.versions":"Карточка {card}; интеграция {integration}; модель данных {model}","backup.count_spaces":"Пространств: {n}","backup.count_rooms":"Комнат: {n}","backup.count_walls":"Стен: {n}","backup.count_openings":"Проёмов: {n}","backup.count_decor":"Объектов декора: {n}","backup.count_markers":"Устройств: {n}","backup.count_layout":"Позиций: {n}","backup.bindings":"Привязки — устройства: {device}, сущности: {entity}, виртуальные: {virtual}; позиций без пространства: {legacy}","backup.binding_status":"Статус на целевом экземпляре — активных: {active}, деактивированных: {disabled}, отсутствующих: {missing}","backup.missing_areas":"На целевом экземпляре отсутствуют зоны: {areas}","backup.dropped_marker_links":"Связи с источниками света вне переносимого пространства пропущены: {n}.","backup.repaired_target_refs":"Существующих ссылок восстановлено этим импортом: {n}.","backup.preserved_unresolved_refs":"Неоднозначные ссылки сохранены без изменений: {n}.","backup.preserved_unresolved_hint":"House Plan ничего не угадывал и не удалял. После импорта запустите «Оптимизировать планы», чтобы проверить оставшиеся ссылки.","backup.import_details":"Подробности восстановления ссылок","backup.import_detail.incoming_remapped":"Ссылок обновлено внутри импортируемой копии: {n}","backup.import_detail.target_repaired":"Существующих ссылок восстановлено: {n}","backup.import_detail.preserved_unresolved":"Неразрешимых ссылок сохранено: {n}","backup.import_detail.collisions":"Конфликтов назначения безопасно сохранено: {n}","backup.import_detail.dropped_links":"Входящих связей пропущено по правилам переноса: {n}","backup.import_detail.bounded_lineages":"Чрезмерно вложенных идентификаторов ограничено: {n}","backup.replace_warning":"Текущая конфигурация и расположение будут заменены. Загруженные файлы не удаляются. Одну отмену можно выполнить до следующего изменения плана.","backup.foreign_bookkeeping":"Служебные списки известных и новых устройств другого экземпляра импортированы не будут.","backup.final_name":"Название нового пространства","backup.target_settings":"Глобальные настройки целевого экземпляра не изменятся.","backup.duplicates":"Уже существующих привязок: {n}","backup.skip":"Пропустить повторяющиеся устройства","backup.virtual_copy":"Добавить безопасные статичные виртуальные копии","backup.content":"Связанное содержимое","backup.content_available":"доступно локально","backup.content_external":"внешняя ссылка","backup.content_detach_required":"будет отвязано","backup.confirm_detach":"Я понимаю, что недоступные внутренние планы и вложения будут отвязаны от импортируемой конфигурации.","backup.replace":"Заменить","backup.add":"Добавить пространство","backup.space_done":"Пространство импортировано: комнат — {rooms}, устройств — {markers}, существующих ссылок восстановлено — {refs}","backup.full_done":"Копия восстановлена: пространств — {spaces}, комнат — {rooms}, устройств — {markers}","backup.undo_import":"Отменить последний полный импорт","backup.import_undone":"Полный импорт отменён"}};function ra(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(";"),g=e?this._t("continuous"===this._demoKind?"marker.preview.demo_continuous_notice":"marker.preview.demo_short_notice"):this._reason(t.explanation.reason),m=[this.deviceName,this._providerSummary(s),this._stateSummary(t),g,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`
+ style=${t.lqiColor?`color:${t.lqiColor}`:G}>${t.lqiText}`:G}
+ `}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: {min}–{max} {unit}","physical.delete_segment":"Delete segment","physical.delete_draft":"Delete entire outline","physical.column_size_title":"Outer side of the square column placed by the click (1–150 cm).","card.title":"House plan","count.devices":"{n} dev.","empty.no_spaces":"No spaces yet.","empty.add_first":"Add the first space and upload a floor plan.","empty.install":'Install the House Plan integration and add it in "Devices & services".',"btn.add_space":"Add space","btn.cancel":"Cancel","btn.save":"Save","btn.close":"Close","btn.delete":"Delete","btn.edit":"Edit","btn.open_in_ha":"Open in HA","btn.reset":"Reset","btn.attach":"Attach…","btn.upload":"Upload…","btn.replace":"Replace…","title.zoom_in":"Zoom in","title.zoom_out":"Zoom out","title.zoom_fit":"Fit all","title.add_device":"Add a device to the plan","title.show_all":"Show hidden and disabled devices as service markers (this tab only)","title.markup":"Room markup: grid, lines, outlines","title.configure_space":"Configure space","title.add_space":"Add space","title.markup_add":"Walls: draw a continuous chain; Shift locks to 45°. Changing tool or leaving the editor finishes it as independent walls","title.markup_merge":"Merge: click one room, then the neighbour it shares a wall with","title.markup_split":"Split a room: click the room, then two points on its walls","title.markup_delroom":"Delete room: click inside a room and confirm","title.need_plan":"Upload a floor-plan image","markup.add":"Walls","markup.merge":"Merge","markup.split":"Split","markup.resize":"Resize","title.markup_resize":"Resize rooms: drag an available horizontal or vertical wall handle","markup.hint_resize":"drag an available wall handle · the wall stops at the first unsafe position · Esc cancels · Ctrl+Z — undo","resize.disabled.diagonal":"This wall is angled; Resize supports only horizontal and vertical walls","resize.disabled.side-angle":"An adjoining wall is angled; Resize requires both adjoining walls to meet this wall at right angles","resize.disabled.duplicate-physical-wall":"A separate partition, unfinished outline, or column overlaps this wall; remove or move it before resizing","resize.disabled.partial-shared":"A neighbouring room uses only part of this wall, so it cannot be moved safely as one shared wall","resize.disabled.unequal-shared":"The neighbouring room uses different endpoints or wall length, so the walls cannot move together safely","resize.disabled.multiple-rooms":"Moving this wall would affect more than two rooms","resize.disabled.thickness-conflict":"The wall thickness cannot be preserved safely","resize.disabled.opening-conflict":"An opening on this wall cannot be moved safely","resize.disabled.invalid-geometry":"This wall cannot be resized without changing the plan structure","resize.commit_failed":"Resize was cancelled because the final plan did not pass the geometry check","resize.preview_failed":"Resize stopped at the last safe position because the plan geometry could not be preserved","markup.opening":"Opening","markup.delete_room":"Delete room","history.undo":"Undo","history.redo":"Redo","history.undo_named":"Undo: {name} (Ctrl+Z)","history.redo_named":"Redo: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Nothing to undo","history.redo_empty":"Nothing to redo","history.undone":"Undone: {name}","history.redone":"Redone: {name}","history.add_room":"Create room","history.split_room":"Split room","history.merge_rooms":"Merge rooms","history.resize_room":"Resize room","history.wall_thickness":"Change wall thickness","history.add_opening":"Add door or window opening","history.edit_opening":"Edit opening","history.move_opening":"Move opening","history.delete_opening":"Delete opening","history.delete_room":"Delete room","history.delete_room_keep_walls":"Delete room, keep walls","history.delete_room_with_walls":"Delete room and walls","history.decor_add":"Add decor object","history.decor_edit":"Edit decor object","history.decor_move":"Move decor object","history.decor_transform":"Transform decor object","history.decor_delete":"Delete decor object","history.backdrop_transform":"Transform plan backdrop","title.markup_opening":"Openings: click a wall to place, click an opening to edit","opening.new":"New opening","opening.edit":"Opening","opening.door":"Door","opening.window":"Window","opening.gate":"Gate","opening.passage":"Open passage","opening.passage_binding_warning":"Saving will remove the open/close sensor and lock.","opening.invalid_passage_fields":"The open passage on plan “{room}” has unsupported settings: {fields}.","opening.type_label":"Type","opening.length_label":"Length, cm","opening.contact_label":"Open/close sensor","opening.lock_label":"Lock","opening.none":"— none —","opening.search_ph":"Search: name or entity id…","opening.invert":"Invert open/closed","opening.flip_h":"Hinge on the other jamb","opening.flip_v":"Opens to the other side","opening.open":"Open","opening.closed":"Closed","opening.locked":"Locked","opening.unlocked":"Unlocked","opening.state_unknown":"unavailable","opening.no_entities":"No sensors bound — a static symbol on the plan.","toast.opening_no_wall":"Click next to a room wall or independent wall","opening.host_partition":"Independent wall","opening.partition_orphan":"The independent wall for this opening no longer exists","opening.partition_jamb_margin":"Leave at least {distance} between the opening and the end of the wall","opening.rebind_partition":"Attach to another independent wall","confirm.delete_partition_openings_title":"Delete wall and openings?","confirm.delete_partition_openings_body":"This wall contains {count} opening(s). They will be deleted together.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Delete","markup.hint_points":"points: {n} · Shift — 45° steps · Esc — finish chain · Ctrl+Z — undo a point · closing an area offers a room","markup.hint_start":"click a grid dot to start a wall chain","tip.lqi":"average zigbee signal:","tip.area":"area: {value}","info.device_header":"Device on the plan","info.model":"Model","info.state":"State","info.link":"Link","info.manuals":"Manuals","info.none":"No additional information","marker.new_device":"New device","marker.name_label":"Name (shown on the plan)","marker.name_ph":"Name","marker.binding_label":"Bind to an HA device","marker.binding_disabled":"disabled in Home Assistant","marker.virtual_option":"Virtual device (no binding)","marker.search_ph":"Search device / group…","marker.nothing_found":"nothing found","marker.room_label":"Room","marker.room_override":" (override placement)","marker.room_choose":"— select a room —","marker.room_auto":"— by device area (auto) —","marker.icon_label":"Icon","marker.icon_ph":"mdi:… (empty = auto)","marker.display_label":"Display","display.badge":"Icon + state","display.icon_ripple":"Icon + state and activity","display.static_icon":"Always static icon","marker.display_hint":"Icon + state changes the plate for work, open, alarm and unavailable states. Icon + state and activity additionally shows a short pulse for events and a continuous pulse for ongoing work, movement or presence. Value + state replaces the icon with one unambiguous HA value. A static icon never reacts to device states.","marker.display_hint_badge":"The icon and dynamic plate show device state without the ordinary activity pulse. Red alarms remain visible.","marker.display_hint_icon":"The icon and dynamic plate show device state without the ordinary activity pulse. Red alarms remain visible.","marker.display_hint_icon_ripple":"The icon, dynamic plate and pulse show a short pulse for events and a continuous pulse for work, motion or presence. Red alarms have separate priority.","marker.display_hint_value":"One unambiguous Home Assistant value replaces the icon while the plate continues to show state. Red alarms remain visible.","marker.display_hint_static_icon":"The theme-aware shell and icon always stay the same. State, activity, unavailability and alarms do not change the face.","marker.static_alarm_warning":"This device can report alarms. Static display hides the marker's visual alarm indication.","marker.preview.title":"Display preview","marker.preview.actual":"Now","marker.preview.example":"Example","marker.preview.integration":"Provided by","marker.preview.source":"Display source","marker.preview.current_state":"Current state","marker.preview.result":"On the plan","marker.preview.details":"Source details","marker.preview.select_source":"Choose a Home Assistant device or entity to see its actual display.","marker.preview.unknown_provider":"Unknown integration","marker.preview.virtual_provider":"House Plan · virtual device","marker.preview.no_source":"No active source","marker.preview.no_state":"No current state","marker.preview.mixed_states":"Several different states","marker.preview.multiple_sources":"{n} sources","marker.preview.more_sources":"+{n} more","marker.preview.scaled":"The preview is scaled to {n}% to fit. The saved size is unchanged.","marker.preview.demo_activity":"Show activity example","marker.preview.demo_notice":"Temporary activity example; the actual device state is unchanged.","marker.preview.demo_short":"Show short activity","marker.preview.demo_continuous":"Show continuous activity","marker.preview.stop_continuous":"Stop continuous activity","marker.preview.demo_short_notice":"Short activity example; the actual device state is unchanged.","marker.preview.demo_continuous_notice":"Continuous activity example; the actual device state is unchanged.","marker.preview.demo_already_visible":"The current state already shows a real activity or alarm effect.","marker.preview.reduced_motion":"System reduced-motion is enabled, so ordinary activity is shown as a dot.","marker.preview.reason.neutral":"Neutral dark plate","marker.preview.reason.working":"Yellow plate: the device is working now","marker.preview.reason.working_activity":"Yellow plate and activity effect: the device is working now","marker.preview.reason.open":"Orange plate: physically open or unlocked","marker.preview.reason.cover_icon_state":"Neutral plate; the cover entity controls the displayed state and icon","marker.preview.reason.presence":"Activity effect while presence is detected","marker.preview.reason.event":"Short activity effect after a detected event","marker.preview.reason.transition":"Activity effect while the device is moving or changing state","marker.preview.reason.media_neutral":"Media devices stay on a neutral dark plate while available","marker.preview.reason.unavailable":"Subdued neutral plate: unavailable or off","marker.preview.reason.alarm":"Red alarm plate; alarm indication is always shown","marker.preview.reason.live_states_disabled":"Live-state styling is disabled for this card","marker.preview.reason.value_no_state":"No usable state value; the icon is shown instead","marker.preview.reason.value_ambiguous_sources":"Several possible value sources; the icon is shown instead","marker.preview.reason.value_non_scalar":"The source did not return a simple value; the icon is shown instead","marker.preview.reason.value_virtual":"A virtual device has no Home Assistant value; the icon is shown instead","marker.preview.reason.vacuum_live_plan_only":"Live vacuum position and trail are available only on the full plan","marker.preview.reason.hidden_design_preview":"The device is hidden on the plan; this design preview remains visible","marker.preview.reason.composite_power_source":"State comes from the device Power entity; auxiliary switches are ignored","marker.preview.reason.activity_display_disabled":"The selected display mode does not show ordinary activity effects","marker.preview.reason.ha_disabled":"The Home Assistant binding is disabled and will be hidden on the plan","marker.preview.reason.orphaned":"The saved Home Assistant binding can no longer be found","marker.preview.reason.static_icon":"Static mode: device state does not change the icon","marker.activity_color":"Activity pulse color","marker.ripple_size":"Activity pulse size","marker.activity_alarm_note":"Color and size affect ordinary activity only and do not change red alarms.","marker.pulse_a11y_alarm":"Alarm","marker.pulse_a11y_event":"Recent event","marker.pulse_a11y_presence":"Presence detected","marker.pulse_a11y_transition":"Changing state","marker.pulse_a11y_running":"Working","marker.state_a11y_neutral":"Default state","marker.state_a11y_working":"Working","marker.state_a11y_open":"Open","marker.state_a11y_locked":"Locked","marker.state_a11y_unlocked":"Unlocked","marker.state_a11y_alarm":"Alarm","marker.state_a11y_unavailable":"Unavailable","marker.lqi_a11y_low":"LQI {value}, low signal","marker.lqi_a11y_mid":"LQI {value}, medium signal","marker.lqi_a11y_high":"LQI {value}, high signal","marker.size_label":"Icon size / rotation","marker.angle_label":"Rotate","marker.model_label":"Model","marker.model_ph":"e.g. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Description","marker.desc_ph":"Notes, specs…","marker.manuals_label":"Manuals (PDF etc.)","marker.sub_device":"device","marker.sub_z2m_group":" · Z2M group","marker.sub_group":"group","marker.sub_helper":"helper","space.new":"New space","space.header":"Space","space.title_label":"Title","space.title_ph":"e.g. Garage","space.plan_label":"Floor plan (background)","space.no_plan":"no plan image","space.plan_alt":"plan","room.new":"New room","room.name_label":"Display name","room.name_ph":"e.g. Terrace","room.area_label":"Home Assistant area (unassigned)","room.no_area_option":"— no area —","room.default_name":"Room","device.unnamed":"unnamed","device.light_group":"light group","device.fallback":"device","device.virtual":"virtual device","confirm.delete_room":'Delete room "{name}"?',"confirm.delete_room_title":'Delete room "{name}"?',"confirm.delete_room_body":"Choose whether the room's exclusive physical walls should remain. Walls shared with another room are always kept.","confirm.merge_rooms":'Merge rooms "{a}" and "{b}"?',"confirm.remove_marker":'Delete "{name}" from the plan? The device will disappear completely and stop contributing to plan data. You can add it again later.',"confirm.erase_decor":"Erase the {kind} object? You can undo this action from the editor history.","confirm.delete_space":'Delete space "{title}" with all its rooms and markup?',"space.delete_blocked":"This space is still used by {n} device(s). Move them to another space or delete them first.","toast.pos_save_failed":"Failed to save position: {err}","toast.no_entity":"The device has no suitable entity","toast.ha_disabled_action":"A Home Assistant object that is disabled cannot be used on the plan.","toast.ha_disabled_show_device":"A device disabled in Home Assistant cannot be shown on the plan. Enable it in Home Assistant first.","toast.ha_disabled_show_entity":"An entity disabled in Home Assistant cannot be shown on the plan. Enable it in Home Assistant first.","toast.ha_disabled_add":"A disabled Home Assistant object cannot be added to the plan. Enable it in Home Assistant first.","toast.ha_binding_unverified":"The object status could not be verified through the Home Assistant registry. Display and actions are temporarily unavailable.","toast.markup_needs_server":"Markup is available after the config is moved to the server","toast.space_order_changed":"Order changed. If any card pins its floor by number, check those panels.","toast.conflict":"Config was changed in another window — data refreshed, repeat your last action","toast.cfg_save_failed":"Failed to save config: {err}","toast.room_overlap":"The outline overlaps room “{name}” — rooms must not overlap","toast.contour_min_edges":"Draw at least two edges before closing the outline","toast.contour_cannot_close":"The outline cannot close because it is degenerate or intersects itself","toast.merge_not_adjacent":"Only rooms that share a wall can be merged","toast.rooms_merged":"Rooms merged into “{name}”","toast.split_pick_wall":"Start the cut on the room’s wall","toast.split_bad_cut":"The cut must run wall to wall inside the room, without crossing walls or itself","merge.header":"Merge rooms","merge.hint":"The merged room keeps one name and one area. The other area is released — its devices leave the plan until another room claims it.","merge.keep":"Keep","merge.no_area":"no area","room.split_header":"New room from the split","toast.room_saved":"Room saved ({n}). Devices added: {added}. Outline the next one or exit markup.","toast.room_saved_no_area":"Room saved ({n}, no area). Outline the next one or exit markup.","toast.marker_needs_server":"Device editing is available after the config is moved to the server","toast.virtual_name_required":"Enter a name for the virtual device","toast.marker_saved":"Device saved","toast.marker_removed":"Device deleted from the plan","toast.integration_missing":"The House Plan integration is not installed — management unavailable","toast.plan_formats":"Supported formats: SVG, PNG, JPG, WebP","toast.plan_required":"Upload a floor plan — it is required","toast.space_added_onboard":"Space added. Outline the rooms: click grid dots and close the contour.","toast.space_added":"Space added","toast.space_saved":"Space saved","toast.space_deleted":"Space deleted","toast.delete_failed":"Delete failed: {err}","toast.error":"Error: {err}","toast.toggle_target_unavailable":"Target “{name}” is unavailable — no action was performed","toast.toggle_targets_unavailable":"Targets are unavailable: {names}. No action was performed","toast.file_failed":'File "{name}" was not uploaded: {err}',"toast.files_attached":"Files attached: {n}","err.unknown":"unknown error","err.code":"code {code}","err.too_large":"file larger than {mb} MB","err.bad_ext":"unsupported type (PDF/image expected)","err.unauthorized":"administrator rights required","editor.title":"Title","editor.default_floor":"Initial space","editor.default_floor_missing":"Initial space “{id}” no longer exists. Choose another space.","editor.floor":"Fixed space","editor.floor_none":"— not fixed —","editor.floor_index":"YAML index {index} (preserved)","fixed_floor.loading":"Loading the fixed space…","fixed_floor.invalid_title":"The fixed space is unavailable","fixed_floor.invalid_body":"Check floor in the card configuration. The configured value is: {value}","editor.icon_size":"Icon size, % of plan width","editor.show_temperature":"Show temperature","editor.live_states":"Live states (on/off, open…)","editor.show_signal":"Show zigbee signal (LQI)","editor.language":"Interface language","editor.lang_auto":"Auto (HA profile)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Icon rules: which MDI icon devices get by name","rules.title":"Icon rules","rules.hint":"Rules are checked top-down against “device name + model” (case-insensitive regex); the first match wins. When nothing matches, the entity device class decides, then the generic chip icon.","rules.pattern_ph":"regex, e.g. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Add rule","rules.reset":"Reset to defaults","rules.test_ph":"Try a device name…","rules.invalid":"invalid regex","rules.saved":"Icon rules saved","btn.up":"Up","btn.down":"Down","tap.info":"Device card","tap.more_info":"HA more-info dialog","tap.toggle":"Toggle state","marker.tap_label":"Tap action for this device","tap.toggle_note":"The resolved state is shown below. Secure devices are never toggled from the plan.","import.title":"Create spaces from HA floors","import.hint":"Your Home Assistant already knows these floors. Pick the ones to turn into plan spaces — you will upload a floor-plan image for each one next. Rooms are then outlined by hand on the plan.","import.start":"Create {n} space(s)","import.manual":"Start from scratch","import.progress":"Floor {i} of {n}","import.done":"Spaces created. Outline the rooms: click grid dots and close the contour.","btn.skip":"Skip","space.scale_label":"Scale (grid cell size)","space.scale_unit":"cm per cell","space.scale_unit_imperial":"in per cell","space.display_section":"Display","space.show_borders":"Always show room borders","space.zero_wall_style":"Zero-thickness walls","space.zero_wall_dashed":"Dashed","space.zero_wall_solid":"Solid","space.zero_wall_help":"Dashed walls let light through. Solid walls block light even at zero thickness.","space.show_names":"Show room names (drag to move)","space.room_color":"Border & name color","space.opacity":"Opacity","space.fill_label":"Room fill","fill.none":"None","fill.lqi":"Zigbee signal","fill.light":"Lights","fill.custom":"Custom color","space.custom_fill":"Fill color","space.source_file":"I have a floor-plan image","space.source_draw":"No image — I'll outline rooms by hand","space.orientation":"Canvas","orient.landscape":"Landscape","orient.portrait":"Portrait","orient.square":"Square","fill.temp":"Temperature","space.temp_min":"Comfort from","space.temp_max":"to","tip.temp_avg":"average temperature:","tip.hum_avg":"average humidity:","space_card.button":"Open the space plan","space_card.not_found":"Space “{id}” not found","space_card.loading":"Loading…","continuity.restore_plan":"Restoring floor plan…","continuity.restore_connection":"Restoring device connection…","continuity.retry":"Try again","editor.space":"Space","editor.show_button":"Show button","editor.button_label":"Button label","editor.button_target":"Target dashboard path","marker.sub_entity":"entity","title.general_settings":"General settings","gs.title":"General settings","gs.hint":"Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.","gs.light_group":"Fill: lights","gs.light_on":"Lights on","gs.light_off":"All lights off","gs.temp_group":"Fill: temperature","gs.temp_cold":"Cold","gs.temp_ok":"Comfortable","gs.temp_hot":"Hot","gs.lqi_group":"Fill: zigbee signal","gs.lqi_low":"Weak signal","gs.lqi_high":"Strong signal","gs.reset":"Reset to defaults","gs.saved":"General settings saved","space.show_lqi":"Show zigbee signal (LQI) next to devices","space.hide_decor":"Hide the decorative layer","space.hide_decor_tip":"Lines, shapes, labels and furniture stay where they are — visible in the backdrop editor, not on the plan.","space.hide_openings":"Hide openings","space.hide_openings_tip":"Door, window and gate symbols are not drawn, but the openings keep working: light passes through, the sun comes in at a window, contact sensors still open them. The plan editor always shows them.","gs.light_none":"No light sources","mode.plan":"Plan editor","mode.devices":"Device editor","display.value":"Value + state","marker.subarea":"no area, manual","device.new":"New device — open its editor to dismiss","opening.unlock_action":"Unlock","opening.lock_action":"Lock","opening.lock_pending":"Working…","title.close_editor":"Close editor (back to view)","devbar.add":"Add","devbar.show_all":"Hidden and disabled","devbar.rules":"Icon rules","space.roomcard_section":"Room card shows:","space.label_temp":"Temperature","space.label_hum":"Humidity","space.label_lqi":"Average Zigbee signal","space.label_light":"Lights on/off","roomcard.light_on":"On","roomcard.light_off":"Off","roomcard.light_partial":"{on} of {total}","toast.split_pick_inside":"Intermediate cut points must be inside the room","mode.decor":"Background editor","decor.select":"Select","decor.line":"Line","decor.rect":"Rectangle","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Erase","decor.erase_confirm_title":"Erase object?","decor.color":"Color","decor.width":"Line width","decor.line_style":"Line style","decor.line_style_solid":"Solid","decor.line_style_dashed":"Dashed","decor.fill":"Fill","decor.fill_color":"Fill color","decor.length":"Length","decor.size":"Size","decor.angle":"Rotation angle, °","decor.text_size":"Text size","decor.backdrop_properties":"Plan backdrop properties","decor.text_title":"Text label","decor.object_title":"Edit {kind}","decor.text_label":"Text","decor.live_group":"Insert HA variable","decor.live_entity":"Entity","decor.live_entity_ph":"choose an entity","decor.live_attr":"Value","decor.live_attr_ph":"choose state or attribute","decor.live_state":"State","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag to move; pull a corner to resize; use the top handle to rotate. Shift changes proportions or frees the angle.","decor.backdrop_reset":"Reset the picture","decor.backdrop_reset_done":"The picture is back at its original place and size","marker.icon_auto":"Auto: {icon} (by icon rules; pick one to override)","marker.icon_pin_auto":"Pin","mode.plan_tip":"Plan editor — the geometry of the home: draw and split/merge rooms, bind them to HA areas, place doors, windows and gates, move room cards, set the scale","mode.devices_tip":"Device editor — everything about icons: drag to position, click to edit binding/icon/display, add virtual devices, icon rules","mode.decor_tip":"Background editor — purely visual decor under the plan: lines, rectangles, ovals and text labels that never react to clicks","space.glow_enabled":"Light-source glow","gs.glow_group":"Light-source glow","gs.glow_base":"House darkness","gs.glow_light":"Default light color / intensity","gs.wall_group":"Walls","gs.wall_fill":"Wall fill","gs.glow_radius":"Glow radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Controls other light sources","marker.controls_hint":"With tap action “Toggle”, a click flips every source added here. The marker’s own bound entity is controlled directly and cannot be added; use the switch below to classify it as a light.","marker.controls_filter":"Search lights and switches…","info.controls":"Controls","marker.glow_radius_label":"Glow radius","marker.glow_radius.help":"Sets the glow radius in metres or feet; an empty value uses the radius from general settings.","marker.glow_radius.help.aria":"Help: glow radius","markup.wallthick":"Thickness","markup.select":"Select","title.markup_wallthick":"Thickness — click a wall to set its thickness from 0 to 100 cm.","markup.hint_wallthick":"click a wall · Esc closes without applying","wallthick.field":"Thickness","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Apply to all walls of this room","markup.draw_wall_title":"Thickness of each new wall segment (0–100 cm). Shared walls keep the existing value.","room.queue_progress":"Room {current} of {total}","toast.wall_rooms_saved":"Created rooms: {n}","toast.wall_chain_saved":"Wall chain saved","toast.wallthick_pick":"Click a wall","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from {min} to {max} {unit}","toast.zero_wall_opening":"Remove the opening on this wall stretch first","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.","toast.wall_model_migration_blocked":"The wall model could not be updated: {reason}. The plan was not changed. Run “Optimize plans”; if the error repeats, fix the conflicting wall geometry.","toast.wall_model_client_outdated":"Update the card and reload the page before editing the plan.","wall_model.reason.invalid-room":"invalid room contour","wall_model.reason.zero-length":"zero-length wall segment","wall_model.reason.third-owner":"wall shared by more than two rooms","wall_model.reason.duplicate-id":"conflicting wall identifiers","wall_model.reason.thickness-conflict":"conflicting wall thickness values","wall_model.reason.opening-host":"opening cannot be assigned to one wall","toast.delete_room_pick":"Click inside the room you want to delete","toast.plan_snap_ambiguous":"Zoom in to choose the wall node","toast.wall_repair_ambiguous":"The room outline has more than one possible connection. Zoom in and connect it explicitly.","toast.wall_repair_too_large":"The gap is larger than 2 cm. Connect the walls before creating the room.","toast.wall_repair_changed":"The wall geometry changed. Try creating the room again.","toast.opening_on_zero_wall":"Openings cannot sit on a zero-thickness wall","marker.from_ha_option":"Pick from the HA list","marker.show_entities":"Show entities","marker.show_entities_tip":"Adds not only devices to the list, but all their entities too","marker.pick_ph":"Choose a device…","room.open_area":"Open the HA area","view.volumetric":"Volumetric view","view.flat":"Flat view","kiosk.title":"This screen's sizes","kiosk.hint":"Stored on this device only — every wall tablet or TV can have its own comfortable sizes.","kiosk.icon_scale":"Device icon size","kiosk.font_scale":"Room card text size","editor.kiosk":"Wall device (kiosk) mode","editor.cycle":"Auto-switch spaces every N seconds (kiosk, 0 = off)","room.settings_title":"Room settings","room.settings_section":"Room settings (override the space)","room.fill_label":"Fill in THIS room","fill.inherit":"As the space","room.custom_fill_space":"Space color","room.custom_fill_own":"Room color","room.temp_src_label":"Temperature source","room.hum_src_label":"Humidity source","room.src_average":"Average over the room's sensors (default)","room.src_pick":"A specific HA device or entity","room.src_ph":"Choose a source…","toast.room_updated":"Room updated","space.card_font":"Room-card font size (whole space)","room.sizes_section":"Font sizes","room.name_scale":"Room name size","room.label_scale":"Metrics size","preview.room_name":"Living room","toast.cfg_reload_failed":"Could not reload the plan from the server: {err}","room.settings_short":"Room settings","room.unnamed":"Unnamed room","marker.use_climate_temp":"Include the device temperature in the room","marker.use_climate_temp_tip":"Adds an air conditioner or thermostat current_temperature to the room average. Configure the external value badge separately below.","marker.value_badge_title":"Value badge","marker.value_badge.help":"Shows one selected value next to the icon. It does not affect room metrics, light, Glow or the tap action.","marker.value_badge.help.aria":"Help: device value badge","marker.value_badge_enabled":"Show a value badge","marker.value_badge_source":"Value","marker.value_badge_source.help":"Choose a specific state, supported attribute, or derived value of this device.","marker.value_badge_source.help.aria":"Help: value badge source","marker.value_badge_position":"Position","marker.value_badge_position.help":"The chosen side stays fixed and does not flip automatically at a plan edge.","marker.value_badge_position.help.aria":"Help: value badge position","marker.value_badge_right":"Right","marker.value_badge_bottom":"Below","marker.value_badge_left":"Left","marker.value_badge_top":"Above","marker.value_badge_empty":"This device has no available values","marker.value_badge_static":"A static icon does not show live values. The setting is preserved.","marker.value_badge_missing":"Source unavailable","marker.value_badge_missing_hint":"The saved source is currently unavailable. Keep it, disable the badge, or choose another value.","marker.value_badge_duplicate":"The same value is already shown inside the icon.","marker.value_badge_state":"State · {name}","marker.value_badge_marker_state":"Light-source state · {name}","marker.value_badge_lqi":"Average Zigbee signal quality","marker.value_badge_attr_current_temperature":"Current temperature · {name}","marker.value_badge_attr_temperature":"Target temperature · {name}","marker.value_badge_attr_current_humidity":"Current humidity · {name}","marker.value_badge_attr_humidity":"Humidity · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Speed · {name}","marker.value_badge_attr_brightness":"Brightness · {name}","marker.value_badge_attr_volume_level":"Volume · {name}","marker.value_badge_attr_battery_level":"Battery · {name}","marker.value_badge_attr_fan_speed":"Fan speed · {name}","marker.light_role_label":"Is this device a light source?","marker.light_role.help":"Controls whether this marker itself is a spatial light source. Linked lights above remain independent room-light controls.","marker.light_role.help.aria":"Help: whether this device is a light source","marker.light_role_auto_yes":"Auto (light source)","marker.light_role_auto_no":"Auto (not a light source)","marker.light_role_always":"Always a light source","marker.light_role_never":"Never a light source","marker.light_entity_label":"Leading light entity","marker.light_entity.help":"For a composite device, choose the entity whose state, colour and brightness represent this plan source. The automatic fallback remains compatible with older plans.","marker.light_entity.help.aria":"Help: leading light entity","marker.light_entity_auto":"Automatic ({entity})","marker.light_entity_none":"no controllable entity","marker.light_entity_missing":"The saved entity {entity} is unavailable. House Plan temporarily uses {fallback}; the saved choice will be restored if it returns.","marker.toggle_entity_label":"Entity to toggle","marker.toggle_entity.help":"For a composite device, choose the exact own light or switch operated by a tap. Automatic keeps the previous target rules and is independent from the leading light entity.","marker.toggle_entity.help.aria":"Help: entity to toggle","marker.toggle_entity_auto":"Automatic ({entity})","marker.toggle_entity_none":"no own controllable entity","marker.toggle_entity_missing":"The saved entity {entity} is no longer among this marker’s selectable channels. House Plan temporarily uses {fallback}; the saved choice will be restored if it returns.","marker.glow_color_label":"Glow colour and brightness","marker.glow_mode.help":"Use live source values, override only its colour, or fix both colour and brightness. The minimum is 1%; to disable the source, choose “Never”.","marker.glow_mode.help.aria":"Help: glow colour and brightness","marker.glow_mode_auto":"From source","marker.glow_mode_color":"Set colour","marker.glow_mode_fixed":"Set colour and brightness","marker.glow_color":"Glow colour","marker.glow_brightness":"Brightness","marker.glow_disabled_never":"Glow settings are unavailable because this marker is explicitly not a light source.","marker.glow_disabled_auto":"Auto mode found no spatial light source for this marker.","marker.glow_disabled_no_entity":"No active controllable Home Assistant entity is available for this spatial source.","marker.glow_passive_hint":"This source has no own Home Assistant data. Set its colour and brightness manually; its radius remains available.","marker.control_broken":"Saved source is missing or no longer marked as a light","marker.control_missing_label":"Missing plan light","marker.control_passive":"passive source","toast.marker_control_cycle":"This link would create a circular chain of light controls.","toast.marker_binding_required":"Choose a Home Assistant device before linking another light source.","confirm.unlock":"Unlock “{name}”?","toast.files_migrate_failed":"Attachments could not be moved to the new binding, links keep pointing at the old files: {err}","space.pick_saved":"Already uploaded","space.pick_saved_hint":"Plans stored on the server, including ones you detached earlier","space.no_saved":"No plans stored on the server yet.","space.loading":"Loading…","space.used_by":"in use: {list}","space.in_use":"A space still uses this plan — detach it first","btn.use":"Use","confirm.delete_plan":'Delete the plan file "{name}" from the server? This cannot be undone.',"toast.plans_list_failed":"Could not list the stored plans: {err}","toast.plan_delete_failed":"Could not delete the plan: {err}","marker.hide":"Hide","marker.hide_tip":'The device will disappear from the plan after saving but will still count toward the room signal. Restore it through "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. Copy the diagnostics with the button below and attach them to the bug report together with a space export.","gs.preflight_reason_prepare-exception":"Could not prepare the space geometry (exception while building the model)","gs.preflight_reason_wall-null":"The wall body did not build (the union came back empty)","gs.preflight_reason_wall-degraded-extra":"The wall body degraded with extra geometry","gs.preflight_reason_wall-failed-core":"The wall body core failed to assemble","gs.preflight_reason_wall-exception":"Wall construction threw an exception","gs.preflight_reason_floor-null":"The floor outline did not build","gs.preflight_reason_floor-exception":"Floor construction threw an exception","gs.preflight_copy":"Copy diagnostics","gs.preflight_copied":"Diagnostics copied","gs.preflight_update_hint":"The card and integration versions differ — update House Plan and retry.","gs.align_preflight_space":"Space {n}","gs.align_preflight_more":", and {n} more","gs.optimize_changes":"Model migrations: {m}; spaces updated: {c}; noisy coordinate values removed: {p}; merged real-wall fragments: {w}; virtual fragments: {s}; independent walls: {i}.","gs.wall_segments_migrated":"Wall segments stabilised: {n}.","gs.optimize_lattice_summary":"Noisy coordinate values canonicalized: {n}; maximum movement: {cm} cm.","gs.optimize_lattice_space":"{space}: coordinate values canonicalized: {n}; off-grid values left unchanged: {far}.","gs.optimize_coincident_partitions":"Hidden independent wall sections absorbed into room walls: {n}.","gs.optimize_openings_rehosted":"Openings reattached to room walls: {n}.","gs.optimize_walls_straightened":"Walls straightened: {n}; maximum movement: {cm} cm.","gs.optimize_walls_straightened_where":"Largest wall correction: {s}.","gs.optimize_walls_straighten_skipped":"Near-axis walls left unchanged because they could not be repaired safely: {n}.","gs.optimize_glow_migration":"Legacy Glow: {spaces} spaces → no data fill + independent Glow; {rooms} rooms → inherited data fill + independent Glow.","gs.optimize_references":"References repaired: spaces — {spaces}; rooms — {rooms}; positions — {positions}; devices detached from missing spaces — {detached}.","gs.optimize_reference_more":", and {n} more","gs.optimize_orphans_removed":"Forgotten records removed: {total} — room labels: {rooms}; devices: {devices}; group markers: {groups}. They belonged to spaces deleted earlier.","gs.optimize_live_positions":"Old positions in deleted spaces belong to existing objects: {n}{names}. They will be kept.","gs.optimize_live_positions_remove":"Old positions in deleted spaces belong to existing objects: {n}{names}. They are selected for removal.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Remove old positions","gs.optimize_live_keep":"Keep old positions","gs.optimize_live_selected":"Old positions will be removed after Optimize is applied.","gs.optimize_unverified":"Could not safely verify positions: {n}. They were left unchanged.","gs.optimize_registry_limited":"Full administrator access to the Home Assistant registries is required for a safe check.","gs.optimize_vacuum_warning":"Vacuum room mappings that still need review: {n}.","gs.optimize_details":"Details","gs.optimize_details_more":"And {n} more records.","gs.optimize_detail_removed":"will be removed","gs.optimize_detail_live":"will be kept","gs.optimize_detail_unverified":"not verified","gs.optimize_detail_room_label":"room label","gs.optimize_detail_device":"device","gs.optimize_detail_group":"group marker","gs.optimize_detail_unknown":"unknown owner","gs.optimize_detail_item":"{status}: {kind} {id}; old space {space}","gs.align_warn":"Elements deliberately placed between grid nodes will move. One undo is available after the operation, only until the next plan edit.","gs.align_run":"Optimize","gs.align_done":"Plans optimized: {n} elements moved, {m} records maintained, {r} references repaired","gs.optimize_undo":"Undo last optimization","gs.optimize_undone":"The last optimization was undone","decor.furniture":"Furniture","furn.title":"Furniture library","furn.symbol":"Symbol","furn.group_furniture":"Furniture","furn.group_appliance":"Appliances","furn.group_sanitary":"Plumbing","furn.group_other":"Other","furn.width":"Width","furn.depth":"Depth","furn.pick_hint":"Pick a symbol, then click on the plan.","furn.place_hint":"Click on the plan — the piece lands against the nearest wall. Shift places it free.","furn.sym_sofa":"Sofa","furn.sym_armchair":"Armchair","furn.sym_coffee_table":"Coffee table","furn.sym_table_dining":"Dining table","furn.sym_table_round":"Round table","furn.sym_chair":"Chair","furn.sym_desk":"Desk","furn.sym_bed_double":"Double bed","furn.sym_bed_single":"Single bed","furn.sym_nightstand":"Nightstand","furn.sym_wardrobe":"Wardrobe","furn.sym_bookshelf":"Bookshelf","furn.sym_fridge":"Fridge","furn.sym_stove":"Cooker","furn.sym_dishwasher":"Dishwasher","furn.sym_washer":"Washing machine","furn.sym_dryer":"Tumble dryer","furn.sym_tv":"TV","furn.sym_ac":"Air conditioner","furn.sym_water_heater":"Water heater","furn.sym_toilet":"Toilet","furn.sym_bathtub":"Bathtub","furn.sym_shower":"Shower","furn.sym_sink":"Washbasin","furn.sym_kitchen_sink":"Kitchen sink","furn.sym_bidet":"Bidet","furn.sym_stairs":"Stairs","furn.sym_fireplace":"Fireplace","furn.sym_plant":"Plant","furn.sym_rug":"Rug","common.yes":"Yes","common.no":"No","vac.diag_source":"Source","vac.diag_platform":"Integration","vac.diag_status":"Status","vac.diag_position":"Position","vac.diag_rooms":"Rooms","vac.diag_rooms_value":"{total} · {matched} names match · {readiness}","vac.autocal_ready":"auto-calibration available","vac.autocal_not_ready":"need 3 matching names","vac.diag_path":"Integration path","vac.diag_map":"Map ID","vac.source_none":"not selected","vac.source_status_ok":"Ready","vac.source_status_missing":"Missing","vac.source_status_disabled":"Disabled in Home Assistant","vac.source_status_unavailable":"Unavailable","vac.source_status_unverified":"Cannot verify with current permissions","vac.source_status_unsupported":"No position data","vac.source_status_none":"No source","vac.source_banner_missing":"The saved source no longer exists. It was not replaced automatically; choose another source or restore it in Home Assistant.","vac.source_banner_disabled":"The saved source is disabled in Home Assistant. Enable it there or choose another source.","vac.source_banner_unverified":"Current Home Assistant permissions cannot verify this saved source. It remains pinned and will not be replaced automatically.","vac.choose_source":"Choose source","vac.source_auto":"Automatic","vac.source_auto_hint":"Use a compatible entity from this device","vac.all_cameras":"All cameras","vac.all_cameras_warn":"A camera may not provide robot data. Choose one only if it is your vacuum map.","vac.all_cameras_empty":"No other camera entities found.","vac.platform_unknown":"unknown integration","vac.cap_position":"position","vac.cap_rooms_short":"rooms","vac.cap_path":"path","vac.cap_map":"map ID","vac.cap_none":"no robot data detected","vac.xcme_hint":"Enable these Xiaomi Cloud Map Extractor attributes:","vac.documentation":"Documentation","vac.residual_title":"Check automatic calibration","vac.residual_message":"The matched rooms disagree by up to {error}. Apply this approximate calibration, refine it manually, or cancel without changing the saved setup.","vac.apply_proposal":"Apply","gs.backup_group":"Backup and transfer","gs.backup_hint":"Download a portable JSON backup or preview and import a backup made by House Plan.","backup.export_open":"Export","backup.import_open":"Import","backup.export_title":"Export House Plan","backup.import_title":"Import House Plan","backup.export_hint":"Choose whether the backup should contain the whole House Plan configuration or only the current space.","backup.full":"Full backup","backup.current_space":"Current space","backup.current_space_title":"Current space: {title}","backup.no_current_space":"No current space","backup.plan_only":"Plan only","backup.plan_only_hint":"Keep rooms, walls, openings, decor and room-label positions without devices or Home Assistant bindings.","backup.plan_only_preview":"This file contains the plan only","backup.privacy_warning":"The archive keeps names, Home Assistant identifiers and exact coordinates. Internal plans and attachments are referenced, not embedded; runtime states and vacuum trails are not included.","backup.download":"Download JSON","backup.export_done":"Backup downloaded","backup.reading":"Checking the backup…","backup.revalidated":"The plan changed after this preview. The summary was refreshed; review it and confirm again.","backup.error.unauthorized":"You do not have permission to export or import this plan.","backup.error.not_ready":"House Plan is not ready yet. Try again in a moment.","backup.error.too_large":"The backup exceeds the 8 MiB limit.","backup.error.invalid_json":"The selected file is not valid JSON.","backup.error.invalid_format":"The selected file is not a House Plan backup.","backup.error.unsupported_export_version":"This backup format is not supported by the installed version.","backup.error.future_model":"The backup was created by a newer House Plan data model.","backup.error.invalid_config":"The backup contains an invalid House Plan configuration.","backup.error.wall_model_migration_blocked":"The backup wall model could not be upgraded safely. Optimize the source plan first, then export it again.","backup.error.invalid_layout":"The backup contains invalid object positions.","backup.error.invalid_content":"The backup contains invalid or inconsistent content references.","backup.error.space_not_found":"The selected space no longer exists.","backup.error.capacity_exceeded":"Adding this backup would exceed the plan limits.","backup.error.preview_expired":"The preview expired. Select the backup file again.","backup.error.preview_owner_mismatch":"This preview belongs to another Home Assistant user.","backup.error.conflict":"The plan changed after the preview. Review the refreshed summary.","backup.error.content_confirmation_required":"Confirm that unavailable local content may be detached.","backup.error.commit_failed":"The backup could not be applied safely. The previous plan was restored or is pending recovery.","backup.error.missing_plan":"A referenced plan file disappeared after the preview. Review the backup again.","backup.error.missing_content":"A referenced local attachment disappeared after the preview. Review the backup again.","backup.error.marker_control_missing":"A linked plan light is missing from the backup.","backup.error.marker_control_not_light":"A linked plan target is no longer marked as a light source.","backup.error.marker_control_self":"A light source cannot control itself.","backup.error.marker_control_cycle":"The backup contains a circular chain of light controls.","backup.error.duplicate_marker_control":"The backup contains a duplicate plan-light link.","backup.error.no_backup":"There is no import or optimization snapshot to restore.","backup.same_source":"Created on this Home Assistant instance","backup.foreign_source":"Created on another Home Assistant instance","backup.created":"Created: {value}","backup.versions":"Card {card}; integration {integration}; data model {model}","backup.count_spaces":"Spaces: {n}","backup.count_rooms":"Rooms: {n}","backup.count_walls":"Walls: {n}","backup.count_openings":"Openings: {n}","backup.count_decor":"Decor objects: {n}","backup.count_markers":"Devices: {n}","backup.count_layout":"Positions: {n}","backup.bindings":"Bindings — devices: {device}, entities: {entity}, virtual: {virtual}; positions without a space: {legacy}","backup.binding_status":"Target status — active: {active}, disabled: {disabled}, missing: {missing}","backup.missing_areas":"Areas missing on the target: {areas}","backup.dropped_marker_links":"Plan-light links outside this transferred space were omitted: {n}.","backup.repaired_target_refs":"Existing references restored by this space import: {n}.","backup.preserved_unresolved_refs":"References that could not be restored unambiguously were preserved: {n}.","backup.preserved_unresolved_hint":"No data was guessed or deleted. After the import, run Optimize plans to inspect the remaining references.","backup.import_details":"Import reference details","backup.import_detail.incoming_remapped":"References updated inside the imported copy: {n}","backup.import_detail.target_repaired":"Existing references restored: {n}","backup.import_detail.preserved_unresolved":"Unresolved references preserved: {n}","backup.import_detail.collisions":"Destination conflicts preserved safely: {n}","backup.import_detail.dropped_links":"Incoming links omitted by transfer rules: {n}","backup.import_detail.bounded_lineages":"Overly nested identifiers left bounded: {n}","backup.replace_warning":"This replaces the current configuration and layout. Uploaded files are never deleted. One undo remains available until the next plan edit.","backup.foreign_bookkeeping":"Instance-specific known/new-device bookkeeping will not be imported.","backup.final_name":"New space name","backup.target_settings":"The target instance's global settings remain unchanged.","backup.duplicates":"Bindings already present: {n}","backup.skip":"Skip duplicate devices","backup.virtual_copy":"Add safe static virtual copies","backup.content":"Referenced content","backup.content_available":"available locally","backup.content_external":"external link","backup.content_detach_required":"will be detached","backup.confirm_detach":"I understand that unavailable internal plans and attachments will be detached from the imported configuration.","backup.replace":"Replace","backup.add":"Add space","backup.space_done":"Space imported: {rooms} rooms, {markers} devices, {refs} existing references restored","backup.full_done":"Backup restored: {spaces} spaces, {rooms} rooms, {markers} devices","backup.undo_import":"Undo last full import","backup.import_undone":"Full import undone"};const aa={en:oa,ru:{"editor.context_actions":"Действия: {object}","editor.tool_options":"Параметры инструмента: {tool}","editor.palette":"Палитра: {tool}","editor.open_group":"Группа инструментов: {group}","editor.group_active":"{group} — активно: {item}","editor.disabled_action":"{action} недоступно: {reason}","color_picker.title":"Выбор цвета","color_picker.hue":"Оттенок","color_picker.saturation":"Насыщенность","color_picker.value":"Яркость","color_picker.hex":"Цвет HEX","color_picker.invalid_hex":"Введите цвет HEX из 3 или 6 цифр","btn.properties":"Свойства","btn.keep_as_walls":"Оставить стенами","btn.delete_room_keep_walls":"Удалить комнату, оставить стены","btn.delete_room_with_walls":"Удалить комнату и стены","title.markup_select":"Выбор и редактирование стен, колонн и сохранённых контуров","title.markup_column":"Колонна: кликните точку сетки для квадратной колонны","markup.partition":"Перегородка","markup.column":"Колонна","markup.hint_column":"кликните точку сетки, чтобы поставить квадратную колонну","history.draft_segment":"Добавление сегмента черновика комнаты","history.draft_merge":"Соединение незавершённых контуров комнаты","history.draft_segment_delete":"Удаление сегмента черновика комнаты","history.partition_add":"Добавление перегородки","history.column_add":"Добавление колонны","history.physical_edit":"Изменение физического объекта","history.physical_delete":"Удаление физического объекта","history.physical_move":"Перемещение физического объекта","history.contour_to_partitions":"Преобразование контура в замкнутые стены","history.wall_chain_finish":"Завершение цепочки стен","history.wall_face_batch":"Создание комнат из стен","toast.column_duplicate":"Колонна с тем же центром и внешним размером уже существует","confirm.delete_draft":"Удалить весь незавершённый контур комнаты?","confirm.delete_draft_segment":"Удалить этот сегмент черновика? Оставшийся контур может разделиться на два.","physical.partition_properties":"Свойства перегородки","physical.column_properties":"Свойства колонны","physical.draft_properties":"Свойства сегмента черновика","physical.shape":"Форма","physical.square":"Квадрат","physical.circle":"Круг","physical.diameter":"Диаметр","physical.side":"Сторона","physical.rotation":"Угол поворота","physical.length":"Длина","physical.allowed_range":"Допустимо: {min}–{max} {unit}","physical.delete_segment":"Удалить сегмент","physical.delete_draft":"Удалить весь контур","physical.column_size_title":"Внешняя сторона квадратной колонны, создаваемой кликом (1–150 см).","card.title":"План дома","count.devices":"{n} устр.","empty.no_spaces":"Пространств пока нет.","empty.add_first":"Добавьте первое пространство и загрузите план этажа.","empty.install":"Установите интеграцию House Plan и добавьте запись в «Устройства и службы».","btn.add_space":"Добавить пространство","btn.cancel":"Отмена","btn.save":"Сохранить","btn.close":"Закрыть","btn.delete":"Удалить","btn.edit":"Редактировать","btn.open_in_ha":"Открыть в HA","btn.reset":"Сброс","btn.attach":"Прикрепить…","btn.upload":"Загрузить…","btn.replace":"Заменить…","title.zoom_in":"Приблизить","title.zoom_out":"Отдалить","title.zoom_fit":"Вписать всё","title.add_device":"Добавить устройство на план","title.show_all":"Показать скрытые и деактивированные устройства служебными маркерами (только в этой вкладке)","title.markup":"Разметка комнат: сетка, линии, контуры","title.configure_space":"Настроить пространство","title.add_space":"Добавить пространство","title.markup_add":"Стены: рисуйте непрерывную цепочку; Shift фиксирует 45°. Смена инструмента или выход завершает её как независимые стены","title.markup_merge":"Объединить: клик по одной комнате, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить комнату: кликните внутри комнаты и подтвердите удаление","title.need_plan":"Загрузите подложку (план этажа)","markup.add":"Стены","markup.merge":"Объединить","markup.split":"Разделить","markup.resize":"Размер","title.markup_resize":"Изменение размера комнат: тяните доступную ручку горизонтальной или вертикальной стены","markup.hint_resize":"тяните доступную ручку стены · стена упирается в первую небезопасную позицию · Esc — отмена · Ctrl+Z — отмена шага","resize.disabled.diagonal":"Стена расположена под углом: Resize поддерживает только горизонтальные и вертикальные стены","resize.disabled.side-angle":"Примыкающая стена расположена под углом: для Resize обе примыкающие стены должны образовывать прямой угол","resize.disabled.duplicate-physical-wall":"Стену перекрывает отдельная перегородка, незавершённый контур или колонна; удалите или переместите её перед изменением размера","resize.disabled.partial-shared":"Соседняя комната использует только часть этой стены, поэтому её нельзя безопасно двигать как одну общую стену","resize.disabled.unequal-shared":"У соседней комнаты другие конечные точки или длина стены, поэтому стены нельзя безопасно двигать вместе","resize.disabled.multiple-rooms":"Перемещение затронуло бы больше двух комнат","resize.disabled.thickness-conflict":"Толщину стены нельзя безопасно сохранить","resize.disabled.opening-conflict":"Проём на этой стене нельзя безопасно переместить","resize.disabled.invalid-geometry":"Эту стену нельзя переместить без изменения структуры плана","resize.commit_failed":"Изменение размера отменено: итоговый план не прошёл проверку геометрии","resize.preview_failed":"Стена остановлена в последней безопасной позиции: геометрию плана нельзя сохранить без потерь","markup.opening":"Проём","markup.delete_room":"Удалить комнату","history.undo":"Отменить","history.redo":"Повторить","history.undo_named":"Отменить: {name} (Ctrl+Z)","history.redo_named":"Повторить: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Нет операций для отмены","history.redo_empty":"Нет операций для повтора","history.undone":"Отменено: {name}","history.redone":"Повторено: {name}","history.add_room":"Создание комнаты","history.split_room":"Разделение комнаты","history.merge_rooms":"Объединение комнат","history.resize_room":"Изменение размера комнаты","history.wall_thickness":"Изменение толщины стены","history.add_opening":"Добавление дверного или оконного проёма","history.edit_opening":"Изменение проёма","history.move_opening":"Перемещение проёма","history.delete_opening":"Удаление проёма","history.delete_room":"Удаление комнаты","history.delete_room_keep_walls":"Удаление комнаты с сохранением стен","history.delete_room_with_walls":"Удаление комнаты и её стен","history.decor_add":"Добавление объекта декора","history.decor_edit":"Изменение объекта декора","history.decor_move":"Перемещение объекта декора","history.decor_transform":"Трансформация объекта декора","history.decor_delete":"Удаление объекта декора","history.backdrop_transform":"Трансформация подложки плана","title.markup_opening":"Проёмы: клик по стене — добавить, клик по проёму — редактировать","opening.new":"Новый проём","opening.edit":"Проём","opening.door":"Дверь","opening.window":"Окно","opening.gate":"Ворота","opening.passage":"Открытый проём","opening.passage_binding_warning":"При сохранении датчик открытия и замок будут удалены.","opening.invalid_passage_fields":"У открытого проёма на плане «{room}» есть недопустимые параметры: {fields}.","opening.type_label":"Тип","opening.length_label":"Длина, см","opening.contact_label":"Датчик открытия","opening.lock_label":"Замок","opening.none":"— нет —","opening.search_ph":"Поиск: имя или entity_id…","opening.invert":"Инвертировать открыто/закрыто","opening.flip_h":"Петли с другой стороны","opening.flip_v":"Открывается в другую сторону","opening.open":"Открыто","opening.closed":"Закрыто","opening.locked":"Заперто","opening.unlocked":"Не заперто","opening.state_unknown":"недоступно","opening.no_entities":"Датчики не привязаны — статичный символ на плане.","toast.opening_no_wall":"Кликните рядом со стеной комнаты или независимой стеной","opening.host_partition":"Независимая стена","opening.partition_orphan":"Независимая стена этого проёма больше не существует","opening.partition_jamb_margin":"Оставьте от края проёма до торца стены минимум {distance}","opening.rebind_partition":"Привязать к другой независимой стене","confirm.delete_partition_openings_title":"Удалить стену и проёмы?","confirm.delete_partition_openings_body":"В стене есть проёмы: {count}. Они будут удалены вместе со стеной.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Удалить","markup.hint_points":"точек: {n} · Shift — шаг 45° · Esc — завершить цепочку · Ctrl+Z — убрать точку · при замыкании будет предложена комната","markup.hint_start":"кликните точку сетки, чтобы начать цепочку стен","tip.lqi":"средний сигнал zigbee:","tip.area":"площадь: {value}","info.device_header":"Устройство на плане","info.model":"Модель","info.state":"Состояние","info.link":"Ссылка","info.manuals":"Инструкции","info.none":"Нет дополнительной информации","marker.new_device":"Новое устройство","marker.name_label":"Имя (отображается на плане)","marker.name_ph":"Название","marker.binding_label":"Привязка к устройству HA","marker.binding_disabled":"деактивировано в Home Assistant","marker.virtual_option":"Виртуальное устройство (без привязки)","marker.search_ph":"Поиск устройства / группы…","marker.nothing_found":"ничего не найдено","marker.room_label":"Комната","marker.room_override":" (переопределить размещение)","marker.room_choose":"— выберите комнату —","marker.room_auto":"— по зоне устройства (авто) —","marker.icon_label":"Иконка","marker.icon_ph":"mdi:… (пусто = авто)","marker.display_label":"Отображение","display.badge":"Значок + состояние","display.icon_ripple":"Значок + состояние и активность","display.static_icon":"Всегда статичный значок","marker.display_hint":"Значок + состояние меняет подложку при работе, открытии, тревоге и недоступности. Значок + состояние и активность дополнительно показывает короткую пульсацию для событий и постоянную — для работы, движения или присутствия. Значение + состояние заменяет значок одним однозначным значением HA. Статичный значок не реагирует на состояния устройства.","marker.display_hint_badge":"Значок и динамическая подложка показывают состояние устройства без обычной пульсации активности. Красная тревога сохраняется.","marker.display_hint_icon":"Значок и динамическая подложка показывают состояние устройства без обычной пульсации активности. Красная тревога сохраняется.","marker.display_hint_icon_ripple":"Значок, динамическая подложка и пульсация: короткая — для событий, постоянная — для работы, движения или присутствия. Красная тревога имеет отдельный приоритет.","marker.display_hint_value":"Значок заменяется одним однозначным значением Home Assistant; подложка продолжает показывать состояние. Красная тревога сохраняется.","marker.display_hint_static_icon":"Подложка в цветах темы и значок всегда остаются одинаковыми. Состояния, активность, недоступность и тревоги не меняют отображение.","marker.static_alarm_warning":"Это устройство может сообщать о тревогах. Статичный режим скрывает визуальную тревогу маркера.","marker.preview.title":"Предпросмотр отображения","marker.preview.actual":"Сейчас","marker.preview.example":"Пример","marker.preview.integration":"Интеграция","marker.preview.source":"Источник отображения","marker.preview.current_state":"Текущее состояние","marker.preview.result":"На плане","marker.preview.details":"Подробнее об источниках","marker.preview.select_source":"Выберите устройство или сущность Home Assistant, чтобы увидеть фактическое отображение.","marker.preview.unknown_provider":"Интеграция неизвестна","marker.preview.virtual_provider":"House Plan · виртуальное устройство","marker.preview.no_source":"Нет активного источника","marker.preview.no_state":"Нет текущего состояния","marker.preview.mixed_states":"Несколько разных состояний","marker.preview.multiple_sources":"Источников: {n}","marker.preview.more_sources":"ещё {n}","marker.preview.scaled":"Предпросмотр уменьшен до {n}%, чтобы поместиться. Сохранённый размер не изменится.","marker.preview.demo_activity":"Показать пример активности","marker.preview.demo_notice":"Временный пример активности; фактическое состояние устройства не меняется.","marker.preview.demo_short":"Показать краткую активность","marker.preview.demo_continuous":"Показать постоянную активность","marker.preview.stop_continuous":"Остановить постоянную активность","marker.preview.demo_short_notice":"Пример краткой активности; фактическое состояние устройства не меняется.","marker.preview.demo_continuous_notice":"Пример постоянной активности; фактическое состояние устройства не меняется.","marker.preview.demo_already_visible":"Текущее состояние уже показывает реальную активность или тревогу.","marker.preview.reduced_motion":"В системе включено уменьшение движения, поэтому обычная активность показана точкой.","marker.preview.reason.neutral":"Нейтральная тёмная подложка","marker.preview.reason.working":"Жёлтая подложка: устройство сейчас работает","marker.preview.reason.working_activity":"Жёлтая подложка и эффект активности: устройство сейчас работает","marker.preview.reason.open":"Оранжевая подложка: физически открыто или разблокировано","marker.preview.reason.cover_icon_state":"Нейтральная подложка; состояние и значок определяет сущность шторы или заслонки","marker.preview.reason.presence":"Эффект активности, пока обнаружено присутствие","marker.preview.reason.event":"Короткий эффект активности после события","marker.preview.reason.transition":"Эффект активности во время движения или смены состояния","marker.preview.reason.media_neutral":"Медиаустройство остаётся на нейтральной тёмной подложке, пока доступно","marker.preview.reason.unavailable":"Приглушённая нейтральная подложка: отключено или недоступно","marker.preview.reason.alarm":"Красная тревожная подложка; тревога отображается всегда","marker.preview.reason.live_states_disabled":"Оформление по живым состояниям отключено в настройках карточки","marker.preview.reason.value_no_state":"Нет подходящего значения состояния — вместо него показан значок","marker.preview.reason.value_ambiguous_sources":"Подходит несколько источников значения — вместо него показан значок","marker.preview.reason.value_non_scalar":"Источник вернул не простое значение — вместо него показан значок","marker.preview.reason.value_virtual":"У виртуального устройства нет значения HA — вместо него показан значок","marker.preview.reason.vacuum_live_plan_only":"Живая позиция и след пылесоса доступны только на полном плане","marker.preview.reason.hidden_design_preview":"Устройство скрыто на плане, но его оформление видно в предпросмотре","marker.preview.reason.composite_power_source":"Состояние берётся из сущности питания устройства; вспомогательные переключатели не учитываются","marker.preview.reason.activity_display_disabled":"Выбранный режим отображения не показывает обычные эффекты активности","marker.preview.reason.ha_disabled":"Привязка отключена в Home Assistant, поэтому устройство будет скрыто на плане","marker.preview.reason.orphaned":"Сохранённая привязка Home Assistant больше не найдена","marker.preview.reason.static_icon":"Статичный режим: состояние устройства не меняет значок","marker.activity_color":"Цвет пульсации активности","marker.ripple_size":"Размер пульсации активности","marker.activity_alarm_note":"Цвет и размер относятся только к обычной активности и не влияют на красную тревогу.","marker.pulse_a11y_alarm":"Тревога","marker.pulse_a11y_event":"Недавнее событие","marker.pulse_a11y_presence":"Обнаружено присутствие","marker.pulse_a11y_transition":"Изменение состояния","marker.pulse_a11y_running":"Работает","marker.state_a11y_neutral":"Обычное состояние","marker.state_a11y_working":"Работает","marker.state_a11y_open":"Открыто","marker.state_a11y_locked":"Заблокировано","marker.state_a11y_unlocked":"Разблокировано","marker.state_a11y_alarm":"Тревога","marker.state_a11y_unavailable":"Недоступно","marker.lqi_a11y_low":"LQI {value}, слабый сигнал","marker.lqi_a11y_mid":"LQI {value}, средний сигнал","marker.lqi_a11y_high":"LQI {value}, сильный сигнал","marker.size_label":"Размер / поворот значка","marker.angle_label":"Поворот","marker.model_label":"Модель","marker.model_ph":"напр. Aqara T&H","marker.link_label":"Ссылка","marker.desc_label":"Описание","marker.desc_ph":"Заметки, характеристики…","marker.manuals_label":"Инструкции (PDF и т.п.)","marker.sub_device":"устройство","marker.sub_z2m_group":" · Z2M-группа","marker.sub_group":"группа","marker.sub_helper":"хелпер","space.new":"Новое пространство","space.header":"Пространство","space.title_label":"Название","space.title_ph":"Например: Гараж","space.plan_label":"Подложка (план)","space.no_plan":"нет подложки","space.plan_alt":"план","room.new":"Новая комната","room.name_label":"Отображаемое имя","room.name_ph":"Например: Терраса","room.area_label":"Зона Home Assistant (свободные)","room.no_area_option":"— без зоны —","room.default_name":"Комната","device.unnamed":"без имени","device.light_group":"группа света","device.fallback":"устройство","device.virtual":"виртуальное устройство","confirm.delete_room":"Удалить комнату «{name}»?","confirm.delete_room_title":"Удалить комнату «{name}»?","confirm.delete_room_body":"Выберите, нужно ли оставить физические стены, принадлежащие только этой комнате. Общие с другой комнатой стены сохраняются всегда.","confirm.merge_rooms":"Слить комнаты «{a}» и «{b}»?","confirm.remove_marker":"Удалить «{name}» с плана? Устройство исчезнет полностью и перестанет участвовать в данных плана. Позже его можно будет добавить заново.","confirm.erase_decor":"Стереть объект «{kind}»? Действие можно отменить из истории редактора.","confirm.delete_space":"Удалить пространство «{title}» со всеми комнатами и разметкой?","space.delete_blocked":"Это пространство всё ещё используется устройствами: {n}. Сначала перенесите их в другое пространство или удалите.","toast.pos_save_failed":"Не удалось сохранить позицию: {err}","toast.no_entity":"У устройства нет подходящей сущности","toast.ha_disabled_action":"Деактивированный объект Home Assistant нельзя использовать на плане.","toast.ha_disabled_show_device":"Деактивированное в Home Assistant устройство нельзя показать на плане. Сначала активируйте его в Home Assistant.","toast.ha_disabled_show_entity":"Деактивированную в Home Assistant сущность нельзя показать на плане. Сначала активируйте её в Home Assistant.","toast.ha_disabled_add":"Деактивированный объект Home Assistant нельзя добавить на план. Сначала активируйте его в Home Assistant.","toast.ha_binding_unverified":"Статус объекта не удалось подтвердить по реестру Home Assistant. Отображение и действия временно недоступны.","toast.markup_needs_server":"Разметка доступна после переноса конфига на сервер","toast.space_order_changed":"Порядок изменён. Если где-то этаж карточки задан номером, проверьте такие панели.","toast.conflict":"Конфиг изменён в другом окне — данные обновлены, повторите последнее действие","toast.cfg_save_failed":"Не удалось сохранить конфиг: {err}","toast.room_overlap":"Контур накладывается на комнату «{name}» — комнаты не должны накладываться","toast.contour_min_edges":"Чтобы замкнуть контур, сначала нарисуйте минимум две грани","toast.contour_cannot_close":"Контур нельзя замкнуть: он вырожден или пересекает сам себя","toast.merge_not_adjacent":"Объединять можно только комнаты с общей стеной","toast.rooms_merged":"Комнаты объединены в «{name}»","toast.split_pick_wall":"Начните разрез на стене комнаты","toast.split_bad_cut":"Разрез — от стены до стены внутри комнаты, без пересечения стен и самого себя","merge.header":"Объединение комнат","merge.hint":"У объединённой комнаты одно имя и одна зона. Вторая зона освобождается — её устройства уйдут с плана, пока их не заберёт другая комната.","merge.keep":"Оставить","merge.no_area":"без зоны","room.split_header":"Новая комната после разделения","toast.room_saved":"Комната сохранена ({n}). Устройств добавлено: {added}. Обведите следующую или выйдите из разметки.","toast.room_saved_no_area":"Комната сохранена ({n}, без зоны). Обведите следующую или выйдите из разметки.","toast.marker_needs_server":"Редактирование устройств доступно после переноса конфига на сервер","toast.virtual_name_required":"Укажите имя виртуального устройства","toast.marker_saved":"Устройство сохранено","toast.marker_removed":"Устройство удалено с плана","toast.integration_missing":"Интеграция House Plan не установлена — управление недоступно","toast.plan_formats":"Поддерживаются SVG, PNG, JPG, WebP","toast.plan_required":"Загрузите подложку — план этажа обязателен","toast.space_added_onboard":"Пространство добавлено. Обведите комнаты: кликайте по точкам сетки и замкните контур.","toast.space_added":"Пространство добавлено","toast.space_saved":"Пространство сохранено","toast.space_deleted":"Пространство удалено","toast.delete_failed":"Ошибка удаления: {err}","toast.error":"Ошибка: {err}","toast.toggle_target_unavailable":"Цель «{name}» недоступна — действие не выполнено","toast.toggle_targets_unavailable":"Цели недоступны: {names}. Действие не выполнено","toast.file_failed":"Файл «{name}» не загружен: {err}","toast.files_attached":"Прикреплено файлов: {n}","err.unknown":"неизвестная ошибка","err.code":"код {code}","err.too_large":"файл больше {mb} МБ","err.bad_ext":"недопустимый тип (нужен PDF/изображение)","err.unauthorized":"нужны права администратора","editor.title":"Заголовок","editor.default_floor":"Стартовое пространство","editor.default_floor_missing":"Стартовое пространство «{id}» больше не существует. Выберите другое.","editor.floor":"Закреплённое пространство","editor.floor_none":"— не закреплять —","editor.floor_index":"YAML-индекс {index} (сохранён)","fixed_floor.loading":"Загружаем закреплённое пространство…","fixed_floor.invalid_title":"Закреплённое пространство недоступно","fixed_floor.invalid_body":"Проверьте floor в конфигурации карточки. Настроенное значение: {value}","editor.icon_size":"Размер иконок, % ширины плана","editor.show_temperature":"Показывать температуру","editor.live_states":"Живые состояния (вкл/выкл, открыто…)","editor.show_signal":"Показывать сигнал zigbee (LQI)","editor.language":"Язык интерфейса","editor.lang_auto":"Авто (профиль HA)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Правила иконок: какая MDI-иконка достаётся устройству по имени","rules.title":"Правила иконок","rules.hint":"Правила проверяются сверху вниз по строке «имя устройства + модель» (regex без учёта регистра); срабатывает первое совпадение. Если ничего не подошло — решает device class сущности, затем — иконка-заглушка.","rules.pattern_ph":"regex, напр. розетк|plug","rules.icon_ph":"mdi:power-socket-de","rules.add":"Добавить правило","rules.reset":"Сбросить к умолчаниям","rules.test_ph":"Проверьте имя устройства…","rules.invalid":"некорректный regex","rules.saved":"Правила иконок сохранены","btn.up":"Вверх","btn.down":"Вниз","tap.info":"Карточка устройства","tap.more_info":"Диалог HA (more-info)","tap.toggle":"Переключить состояние","marker.tap_label":"Действие по нажатию для этого устройства","tap.toggle_note":"Под селектором показано, какое состояние изменится. Защищённые устройства не переключаются с плана.","import.title":"Создать пространства из этажей HA","import.hint":"Home Assistant уже знает эти этажи. Отметьте, какие превратить в пространства плана — далее для каждого попросим картинку плана. Комнаты затем обводятся вручную по плану.","import.start":"Создать: {n}","import.manual":"Начать с нуля","import.progress":"Этаж {i} из {n}","import.done":"Пространства созданы. Обведите комнаты: кликайте по точкам сетки и замкните контур.","btn.skip":"Пропустить","space.scale_label":"Масштаб (размер клетки сетки)","space.scale_unit":"см на клетку","space.scale_unit_imperial":"дюйм на клетку","space.display_section":"Отображение","space.show_borders":"Всегда отображать границы комнат","space.zero_wall_style":"Стены нулевой толщины","space.zero_wall_dashed":"Пунктирные","space.zero_wall_solid":"Сплошные","space.zero_wall_help":"Пунктирные стены пропускают свет. Сплошные стены блокируют свет, даже при нулевой толщине.","space.show_names":"Отображать названия комнат (перетаскиваются)","space.room_color":"Цвет границ и названий","space.opacity":"Прозрачность","space.fill_label":"Заливка комнат","fill.none":"Нет","fill.lqi":"По силе зигби-сигнала","fill.light":"По освещению","fill.custom":"Свой цвет","space.custom_fill":"Цвет заливки","space.source_file":"У меня есть картинка плана","space.source_draw":"Нет подложки — нарисую комнаты вручную","space.orientation":"Холст","orient.landscape":"Альбомный","orient.portrait":"Портретный","orient.square":"Квадрат","fill.temp":"По температуре","space.temp_min":"Комфорт от","space.temp_max":"до","tip.temp_avg":"средняя температура:","tip.hum_avg":"средняя влажность:","space_card.button":"Перейти к пространству","space_card.not_found":"Пространство «{id}» не найдено","space_card.loading":"Загрузка…","continuity.restore_plan":"Восстанавливаем план…","continuity.restore_connection":"Восстанавливаем подключение к устройствам…","continuity.retry":"Повторить","editor.space":"Пространство","editor.show_button":"Показывать кнопку","editor.button_label":"Текст кнопки","editor.button_target":"Путь дашборда (куда вести)","marker.sub_entity":"сущность","title.general_settings":"Общие настройки","gs.title":"Общие настройки","gs.hint":"Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.","gs.light_group":"Заливка: освещение","gs.light_on":"Свет включён","gs.light_off":"Весь свет выключен","gs.temp_group":"Заливка: температура","gs.temp_cold":"Холодно","gs.temp_ok":"Комфорт","gs.temp_hot":"Жарко","gs.lqi_group":"Заливка: зигби-сигнал","gs.lqi_low":"Слабый сигнал","gs.lqi_high":"Сильный сигнал","gs.reset":"Сбросить к умолчаниям","gs.saved":"Общие настройки сохранены","space.show_lqi":"Показывать зигби-сигнал (LQI) у устройств","space.hide_decor":"Скрыть декоративный слой","space.hide_decor_tip":"Линии, фигуры, надписи и мебель остаются на месте — их видно в редакторе подложки, но не на плане.","space.hide_openings":"Скрыть проёмы","space.hide_openings_tip":"Двери, окна и ворота не рисуются, но продолжают работать: свет проходит, солнце светит в окна, датчики открытия срабатывают. В редакторе плана проёмы видно всегда.","gs.light_none":"Нет источников света","mode.plan":"Редактор плана","mode.devices":"Редактор устройств","display.value":"Значение + состояние","marker.subarea":"без зоны, вручную","device.new":"Новое устройство — откройте его редактор, чтобы снять отметку","opening.unlock_action":"Открыть замок","opening.lock_action":"Закрыть замок","opening.lock_pending":"Выполняется…","title.close_editor":"Закрыть редактор (вернуться к просмотру)","devbar.add":"Добавить","devbar.show_all":"Скрытые и деактивированные","devbar.rules":"Правила иконок","space.roomcard_section":"В карточке комнаты:","space.label_temp":"Температура","space.label_hum":"Влажность","space.label_lqi":"Средний Zigbee-сигнал","space.label_light":"Свет вкл/выкл","roomcard.light_on":"Вкл","roomcard.light_off":"Выкл","roomcard.light_partial":"{on} из {total}","toast.split_pick_inside":"Промежуточные точки разреза — внутри комнаты","mode.decor":"Редактор подложки","decor.select":"Выбрать","decor.line":"Линия","decor.rect":"Прямоугольник","decor.ellipse":"Овал","decor.text":"Надпись","decor.erase":"Стереть","decor.erase_confirm_title":"Стереть объект?","decor.color":"Цвет","decor.width":"Толщина линии","decor.line_style":"Стиль линии","decor.line_style_solid":"Сплошная","decor.line_style_dashed":"Пунктирная","decor.fill":"Заливка","decor.fill_color":"Цвет заливки","decor.length":"Длина","decor.size":"Размер","decor.angle":"Угол поворота, °","decor.text_size":"Размер текста","decor.backdrop_properties":"Свойства картинки-подложки","decor.text_title":"Надпись","decor.object_title":"Редактирование: {kind}","decor.text_label":"Текст","decor.live_group":"Вставить переменную HA","decor.live_entity":"Сущность","decor.live_entity_ph":"выберите сущность","decor.live_attr":"Значение","decor.live_attr_ph":"выберите состояние или атрибут","decor.live_state":"Состояние","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — перемещение; угол — размер; верхнюю ручку — поворот. Shift меняет пропорции или снимает шаг угла.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","marker.icon_pin_auto":"Закрепить","mode.plan_tip":"Редактор плана — геометрия дома: рисование и объединение/разделение комнат, привязка к зонам HA, двери, окна и ворота, карточки комнат, масштаб","mode.devices_tip":"Редактор устройств — всё про значки: перетаскивание, клик — настройка привязки/иконки/отображения, виртуальные устройства, правила иконок","mode.decor_tip":"Редактор подложки — чисто визуальный декор под планом: линии, прямоугольники, овалы и надписи, не реагирующие на клики","space.glow_enabled":"Свечение источников света","gs.glow_group":"Свечение источников света","gs.glow_base":"Темнота дома","gs.glow_light":"Цвет света по умолчанию / интенсивность","gs.wall_group":"Стены","gs.wall_fill":"Цвет заливки стен","gs.glow_radius":"Радиус свечения","gs.unit_m":"м","gs.unit_ft":"фут","marker.controls_label":"Управляет другими источниками света","marker.controls_hint":"При действии «Переключить» клик разом переключает все добавленные здесь источники. Собственная сущность маркера управляется напрямую и сюда не добавляется; чтобы считать её светом, включите флаг ниже.","marker.controls_filter":"Поиск ламп и выключателей…","info.controls":"Управляет","marker.glow_radius_label":"Радиус свечения","marker.glow_radius.help":"Задаёт радиус свечения в метрах или футах; пустое значение использует радиус из общих настроек.","marker.glow_radius.help.aria":"Подсказка: радиус свечения","markup.wallthick":"Толщина","markup.select":"Выбрать","title.markup_wallthick":"Толщина — клик по стене задаёт толщину от 0 до 100 см.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены (0–100 см). Общие стены сохраняют существующее значение.","room.queue_progress":"Комната {current} из {total}","toast.wall_rooms_saved":"Создано комнат: {n}","toast.wall_chain_saved":"Цепочка стен сохранена","toast.wallthick_pick":"Кликните по стене","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от {min} до {max} {unit}","toast.zero_wall_opening":"Сначала удалите проём на этом участке стены","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","toast.geometry_unsafe":"Изменение отменено: геометрию стен нельзя безопасно построить.","toast.wall_model_migration_blocked":"Не удалось обновить модель стен: {reason}. План не изменён. Запустите «Оптимизировать планы»; если ошибка повторится, исправьте конфликтующую геометрию стен.","toast.wall_model_client_outdated":"Обновите карточку и перезагрузите страницу перед редактированием плана.","wall_model.reason.invalid-room":"некорректный контур комнаты","wall_model.reason.zero-length":"отрезок стены нулевой длины","wall_model.reason.third-owner":"стена принадлежит более чем двум комнатам","wall_model.reason.duplicate-id":"конфликт идентификаторов стен","wall_model.reason.thickness-conflict":"конфликт значений толщины стены","wall_model.reason.opening-host":"проём нельзя однозначно привязать к стене","toast.delete_room_pick":"Кликните внутри комнаты, которую нужно удалить","toast.plan_snap_ambiguous":"Увеличьте масштаб, чтобы выбрать узел стены","toast.wall_repair_ambiguous":"У контура несколько возможных соединений. Увеличьте масштаб и соедините стены явно.","toast.wall_repair_too_large":"Разрыв больше 2 см. Соедините стены перед созданием комнаты.","toast.wall_repair_changed":"Геометрия стен изменилась. Попробуйте создать комнату ещё раз.","toast.opening_on_zero_wall":"Проёмы на стене нулевой толщины запрещены","marker.from_ha_option":"Выбрать из списка HA","marker.show_entities":"Показывать сущности","marker.show_entities_tip":"Добавляет в список не только устройства, но и все их сущности","marker.pick_ph":"Выберите устройство…","room.open_area":"Открыть зону в HA","view.volumetric":"Объёмный вид","view.flat":"Плоский вид","kiosk.title":"Размеры на этом экране","kiosk.hint":"Хранится только на этом устройстве — у каждого настенного планшета или ТВ свои удобные размеры.","kiosk.icon_scale":"Размер значков устройств","kiosk.font_scale":"Размер текста карточек комнат","editor.kiosk":"Режим настенного устройства (киоск)","editor.cycle":"Автосмена пространств каждые N секунд (киоск, 0 = выкл)","room.settings_title":"Настройки комнаты","room.settings_section":"Настройки комнаты (переопределяют пространство)","room.fill_label":"Заливка в ЭТОЙ комнате","fill.inherit":"Как у пространства","room.custom_fill_space":"Цвет пространства","room.custom_fill_own":"Цвет комнаты","room.temp_src_label":"Источник температуры","room.hum_src_label":"Источник влажности","room.src_average":"Средняя по датчикам комнаты (по умолчанию)","room.src_pick":"Конкретное устройство или сущность HA","room.src_ph":"Выберите источник…","toast.room_updated":"Комната обновлена","space.card_font":"Размер шрифта карточек комнат (всё пространство)","room.sizes_section":"Размеры шрифтов","room.name_scale":"Размер названия","room.label_scale":"Размер подписей","preview.room_name":"Гостиная","toast.cfg_reload_failed":"Не удалось перечитать план с сервера: {err}","room.settings_short":"Настройки комнаты","room.unnamed":"Комната без имени","marker.use_climate_temp":"Учитывать температуру устройства в комнате","marker.use_climate_temp_tip":"Добавляет current_temperature кондиционера или термостата в среднюю температуру комнаты. Внешний бейдж со значением настраивается отдельно ниже.","marker.value_badge_title":"Бейдж со значением","marker.value_badge.help":"Показывает одно выбранное значение рядом со значком. Не влияет на комнатные показатели, свет, Glow и действие по нажатию.","marker.value_badge.help.aria":"Подсказка: бейдж со значением устройства","marker.value_badge_enabled":"Отображать бейдж со значением","marker.value_badge_source":"Значение","marker.value_badge_source.help":"Выберите конкретное состояние, поддерживаемый атрибут или производное значение этого устройства.","marker.value_badge_source.help.aria":"Подсказка: источник значения бейджа","marker.value_badge_position":"Расположение","marker.value_badge_position.help":"Выбранная сторона сохраняется постоянно и не переворачивается автоматически у края плана.","marker.value_badge_position.help.aria":"Подсказка: расположение бейджа","marker.value_badge_right":"Справа","marker.value_badge_bottom":"Снизу","marker.value_badge_left":"Слева","marker.value_badge_top":"Сверху","marker.value_badge_empty":"У этого устройства нет доступных значений","marker.value_badge_static":"Статичный значок не показывает живые значения. Настройка сохранится.","marker.value_badge_missing":"Источник недоступен","marker.value_badge_missing_hint":"Сохранённый источник сейчас недоступен. Можно сохранить его, отключить бейдж или выбрать другой.","marker.value_badge_duplicate":"То же значение уже показано внутри значка.","marker.value_badge_state":"Состояние · {name}","marker.value_badge_marker_state":"Состояние источника света · {name}","marker.value_badge_lqi":"Качество Zigbee-сигнала (среднее)","marker.value_badge_attr_current_temperature":"Текущая температура · {name}","marker.value_badge_attr_temperature":"Целевая температура · {name}","marker.value_badge_attr_current_humidity":"Текущая влажность · {name}","marker.value_badge_attr_humidity":"Влажность · {name}","marker.value_badge_attr_current_position":"Положение · {name}","marker.value_badge_attr_percentage":"Скорость · {name}","marker.value_badge_attr_brightness":"Яркость · {name}","marker.value_badge_attr_volume_level":"Громкость · {name}","marker.value_badge_attr_battery_level":"Заряд батареи · {name}","marker.value_badge_attr_fan_speed":"Скорость вентилятора · {name}","marker.light_role_label":"Является источником света","marker.light_role.help":"Определяет, является ли сам маркер пространственным источником света. Привязанные выше лампы остаются независимыми источниками для комнаты.","marker.light_role.help.aria":"Подсказка: является ли устройство источником света","marker.light_role_auto_yes":"Автоматически (источник света)","marker.light_role_auto_no":"Автоматически (не источник света)","marker.light_role_always":"Всегда источник света","marker.light_role_never":"Никогда не источник света","marker.light_entity_label":"Ведущая сущность света","marker.light_entity.help":"Для составного устройства выберите сущность, состояние, цвет и яркость которой представляют этот источник на плане. Автоматический вариант сохраняет совместимость со старыми планами.","marker.light_entity.help.aria":"Подсказка: ведущая сущность света","marker.light_entity_auto":"Автоматически ({entity})","marker.light_entity_none":"нет управляемой сущности","marker.light_entity_missing":"Сохранённая сущность {entity} недоступна. Временно используется {fallback}; выбор восстановится, если сущность вернётся.","marker.toggle_entity_label":"Переключаемая сущность","marker.toggle_entity.help":"Для составного устройства выберите собственную лампу или переключатель, которой управляет нажатие. Автоматический вариант сохраняет прежние правила цели и не зависит от ведущей сущности света.","marker.toggle_entity.help.aria":"Подсказка: переключаемая сущность","marker.toggle_entity_auto":"Автоматически ({entity})","marker.toggle_entity_none":"нет собственной управляемой сущности","marker.toggle_entity_missing":"Сохранённой сущности {entity} больше нет среди выбираемых каналов этого маркера. Временно используется {fallback}; выбор восстановится, если сущность вернётся.","marker.glow_color_label":"Цвет и яркость свечения","marker.glow_mode.help":"Используйте данные источника, задайте только цвет или зафиксируйте цвет и яркость. Минимум — 1%; чтобы отключить источник, выберите «Никогда».","marker.glow_mode.help.aria":"Подсказка: цвет и яркость свечения","marker.glow_mode_auto":"Из источника","marker.glow_mode_color":"Задать цвет","marker.glow_mode_fixed":"Задать цвет и яркость","marker.glow_color":"Цвет свечения","marker.glow_brightness":"Яркость","marker.glow_disabled_never":"Настройки свечения недоступны: маркер явно исключён из источников света.","marker.glow_disabled_auto":"В автоматическом режиме у этого маркера не найден пространственный источник света.","marker.glow_disabled_no_entity":"Для пространственного источника нет активной управляемой сущности Home Assistant.","marker.glow_passive_hint":"У источника нет собственных данных Home Assistant. Задайте цвет и яркость вручную; радиус остаётся доступным.","marker.control_broken":"Сохранённый источник отсутствует или больше не отмечен как источник света","marker.control_missing_label":"Отсутствующий источник на плане","marker.control_passive":"пассивный источник","toast.marker_control_cycle":"Такая связь создаст циклическую цепочку управления светом.","toast.marker_binding_required":"Сначала выберите устройство Home Assistant, затем добавьте управляемый источник света.","confirm.unlock":"Открыть замок «{name}»?","toast.files_migrate_failed":"Не удалось перенести вложения к новой привязке, ссылки остались на старые файлы: {err}","space.pick_saved":"Уже загруженные","space.pick_saved_hint":"Планы, сохранённые на сервере, включая отцеплённые ранее","space.no_saved":"На сервере пока нет сохранённых планов.","space.loading":"Загрузка…","space.used_by":"используется: {list}","space.in_use":"План используется пространством — сначала отцепите его","btn.use":"Выбрать","confirm.delete_plan":"Удалить файл плана «{name}» с сервера? Действие необратимо.","toast.plans_list_failed":"Не удалось получить список планов: {err}","toast.plan_delete_failed":"Не удалось удалить план: {err}","marker.hide":"Скрыть","marker.hide_tip":"Устройство исчезнет с плана после сохранения, но продолжит участвовать в расчёте сигнала комнаты. Вернуть его можно через кнопку «Скрытые и деактивированные» в редакторе устройств.","marker.show":"Показать","marker.show_tip":"Устройство снова появится на плане после сохранения.","marker.hidden_ghost":"Устройство скрыто пользователем","marker.ha_disabled_device":"Устройство деактивировано в Home Assistant и скрыто с плана.","marker.ha_disabled_entity":"Сущность деактивирована в Home Assistant и скрыта с плана.","marker.ha_disabled_all_entities":"У устройства нет активных сущностей Home Assistant, поэтому оно скрыто с плана.","marker.ha_registry_limited":"Полный реестр Home Assistant недоступен этому пользователю. Неподтверждённый объект временно нельзя показывать или использовать.","marker.delete_tip":"Полностью удалить устройство с плана и из всех расчётов. Позже его можно добавить заново.","tap.run":"Запустить автоматизацию/скрипт/сцену","tap.cover":"Открыть/закрыть (шторы/жалюзи)","marker.run_target_label":"Что запускать","marker.run_search_ph":"Поиск: автоматизация, скрипт или сцена…","marker.run_target_gone":"Цель {id} не найдена — выберите заново","marker.tap_confirm":"Спрашивать подтверждение","marker.tap_confirm_tip":"Перед выполнением показать диалог подтверждения — защита от случайных нажатий.","marker.toggle_hint_single":"Цель: {name} ({id}).","marker.virtual_light_target":"Виртуальный свет: {name}.","marker.virtual_light_current":"Ручное состояние: {state} → {effect}.","marker.virtual_light_state_on":"включён","marker.virtual_light_state_off":"выключен","marker.toggle_hint_group":"Переключатся источники ({count}): {names}.","marker.toggle_hint_current":"Сейчас: {state} → {effect}.","marker.toggle_hint_group_current":"Сейчас включено {on} из {count} → {effect}.","marker.toggle_hint_skipped":"Не будут затронуты ({count}): {targets}.","marker.toggle_effect_turn_on":"включится","marker.toggle_effect_turn_off":"выключится","marker.toggle_effect_open":"откроется","marker.toggle_effect_close":"закроется","marker.toggle_effect_stop":"остановится","marker.toggle_effect_toggle":"состояние переключится","marker.toggle_skip_missing":"не найдено","marker.toggle_skip_ha_disabled":"деактивировано в HA","marker.toggle_skip_unavailable":"недоступно","marker.toggle_skip_unsupported":"переключение не поддерживается","marker.toggle_skip_secure":"заблокировано из соображений безопасности","marker.toggle_none_no_binding":"У устройства нет привязки, состояние переключать нечем. По нажатию ничего не произойдёт.","marker.toggle_none_no_actionable_entity":"У устройства нет состояния, которое можно переключить. По нажатию ничего не произойдёт.","marker.toggle_none_configured_targets_missing":"Настроенные цели сейчас недоступны. Собственная сущность устройства не будет подставлена вместо них.","marker.toggle_none_ha_disabled":"Цель деактивирована в Home Assistant и не может использоваться на плане.","marker.toggle_none_unavailable":"Цель сейчас недоступна. По нажатию ничего не произойдёт.","marker.toggle_none_unsupported":"Для этой сущности Home Assistant не предоставляет безопасного переключения. По нажатию ничего не произойдёт.","marker.toggle_none_secure":"Переключение замков, сигнализации и защитных ворот с плана заблокировано из соображений безопасности.","run.automation":"автоматизация","run.script":"скрипт","run.scene":"сцена","confirm.tap_run":"Запустить «{name}»?","confirm.tap_toggle":"Переключить «{name}»?","confirm.tap_cover":"Открыть/закрыть «{name}»?","confirm.current_state":"Текущее состояние: {state}","confirm.expected_state":"После переключения: {state}","confirm.group_current":"включено {on} из {total}","confirm.group_all_on":"все включены","confirm.group_all_off":"все выключены","confirm.unavailable_targets":"Недоступно: {count}","confirm.expected_by_ha":"Состояние определит Home Assistant","confirm.state_on":"Включено","confirm.state_off":"Выключено","confirm.state_open":"Открыто","confirm.state_closed":"Закрыто","confirm.state_opening":"Открывается","confirm.state_closing":"Закрывается","confirm.state_stopped":"Остановлено","confirm.state_unknown":"Неизвестно","toast.run_started":"Запущено: {name}","toast.run_target_missing":"Цель запуска не найдена — проверьте настройки устройства","toast.run_target_required":"Выберите автоматизацию, скрипт или сцену","toast.tap_target_changed":"Цель действия изменилась. Повторите попытку.","toast.virtual_light_toggle_failed":"Не удалось переключить виртуальный свет: {err}","toast.value_badge_source_required":"Выберите значение для бейджа","btn.run":"Выполнить","vac.section":"Робот-пылесос: живая позиция","vac.autocal":"Настроить автоматически","vac.live":"Живая позиция на плане","vac.trail":"Показывать путь робота","vac.cal_maps":"Откалиброваны карты: {maps}","vac.autocal_no_rooms":"Интеграция не отдаёт список комнат — откройте «Подогнать вручную»","vac.autocal_no_match":"Не совпали имена комнат (нужно ≥3 общих) — откройте «Подогнать вручную»","vac.autocal_done":"Готово: привязка по {rooms} комнатам. Запустите уборку и проверьте","vac.cal_need_pos":"Робот сейчас не отдаёт координаты — запустите уборку и поставьте на паузу","vac.cal_done":"Калибровка сохранена. Запустите уборку и проверьте","vac.cal_cancelled":"Калибровка отменена","vac.fit":"Подогнать вручную","vac.fit_hint":"Перетащите карту робота на место, растяните за уголки","vac.fit_rotate":"Повернуть 90°","vac.fit_mirror":"Отразить","vac.trail_never":"Не показывать никогда","vac.trail_cleaning":"Во время уборки","vac.trail_always":"Показывать всегда","gs.bg_group":"Фон сцены","gs.bg_color":"Цвет фона вокруг плана","gs.bg_default":"Как в теме","gs.bg_theme":"по умолчанию — из темы","gs.bg_mode":"Фон плана","gs.bg_static":"Статичный цвет","gs.bg_daynight":"Следует за Солнцем","gs.bg_daynight_hint":"Фон проходит рассвет, день, закат и ночь по данным sun.sun, а при их недоступности — по локальным часам. Компас для фона не нужен.","gs.sun_group":"Солнце","gs.sun_missing":"Сущность sun.sun не найдена — фон следует локальным часам, а оконные лучи недоступны.","gs.north":"Север на плане","gs.north_ph":"не задан","gs.north_hint":"Север нужен только для оконных лучей (шаг 1°, с Shift 15°); солнечный фон работает без него.","gs.north_clear":"Сбросить","gs.north_letter":"С","gs.sun_rays":"Солнце в окнах","gs.about_group":"О карточке","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · документация и issues","gs.about_telegram":"Чат в Telegram","space.bg_color":"Цвет фона вокруг плана","space.bg_inherit":"Наследовать общий","space.bg_inherited":"наследуется из общих настроек","space.bg_mode":"Фон плана","space.north":"Север на плане (переопределение)","space.north_inherited":"наследуется: {v}","space.sun_rays":"Солнце в окнах","space.sun_inherit":"Наследовать общий","space.sun_on":"Вкл","space.sun_off":"Выкл","canvas.far_objects":"Объектов далеко от плана: {n}","canvas.show_far":"Показать","canvas.home_tip":"План там — нажмите, чтобы вписать","gs.grid_group":"Обслуживание планов","gs.grid_hint":"Обновляет модели данных, выравнивает элементы по сетке и объединяет лишние фрагменты стен. Перед записью будет показан точный отчёт.","gs.align_all":"Оптимизировать планы","gs.align_title":"Оптимизировать планы","gs.align_none":"Все планы уже используют актуальную и оптимальную модель данных.","gs.optimize_no_automatic_changes":"Автоматических изменений нет. Проверьте пункты ниже.","gs.align_count":"Сдвинется элементов: {n} из {total}, максимум на {cm} см.","gs.align_where":"Наибольший сдвиг — в пространстве «{s}».","gs.align_turned":"Проёмов с исправлением угла: {n}.","gs.align_removed_drafts":"Схлопнувшиеся на сетке некорректные контуры удалены: {n}.","gs.optimize_redundant_drafts":"Сохранённые цепочки стен, полностью скрытые стенами комнат, удалены: {n}.","gs.align_preflight_failed":"Не удалось безопасно проверить геометрию следующих пространств: {spaces}{more}.","gs.align_preflight_hint":"Планы не изменены. Скопируйте диагностику кнопкой ниже и приложите её к отчёту об ошибке вместе с экспортом пространства.","gs.preflight_reason_prepare-exception":"Не удалось подготовить геометрию пространства (исключение при сборке модели)","gs.preflight_reason_wall-null":"Тело стен не построилось (объединение вернуло пустоту)","gs.preflight_reason_wall-degraded-extra":"Тело стен деградировало с лишней геометрией","gs.preflight_reason_wall-failed-core":"Ядро тела стен не собралось","gs.preflight_reason_wall-exception":"Построение стен упало с исключением","gs.preflight_reason_floor-null":"Контур пола не построился","gs.preflight_reason_floor-exception":"Построение пола упало с исключением","gs.preflight_copy":"Скопировать диагностику","gs.preflight_copied":"Диагностика скопирована","gs.preflight_update_hint":"Версии карточки и интеграции различаются — обновите House Plan и повторите.","gs.align_preflight_space":"Пространство {n}","gs.align_preflight_more":" и ещё {n}","gs.optimize_changes":"Миграций модели: {m}; обновлено пространств: {c}; устранён шум координат: {p}; объединено отрезков реальных стен: {w}; виртуальных: {s}; независимых: {i}.","gs.wall_segments_migrated":"Стабилизировано сегментов стен: {n}.","gs.optimize_lattice_summary":"Канонизировано шумовых значений координат: {n}; максимальный сдвиг: {cm} см.","gs.optimize_lattice_space":"{space}: канонизировано значений координат: {n}; оставлено значений вне сетки: {far}.","gs.optimize_coincident_partitions":"Скрытые участки независимых стен поглощены стенами комнат: {n}.","gs.optimize_openings_rehosted":"Проёмы перепривязаны к стенам комнат: {n}.","gs.optimize_walls_straightened":"Выпрямлено стен: {n}; максимальное перемещение: {cm} см.","gs.optimize_walls_straightened_where":"Максимальная правка стены: {s}.","gs.optimize_walls_straighten_skipped":"Оставлено почти осевых стен, которые нельзя безопасно выпрямить: {n}.","gs.optimize_glow_migration":"Старый Glow: пространств — {spaces} → без заливки данных + независимый Glow; комнат — {rooms} → наследуемая заливка + независимый Glow.","gs.optimize_references":"Исправлено ссылок: пространства — {spaces}; комнаты — {rooms}; позиции — {positions}; устройств отвязано от отсутствующих пространств — {detached}.","gs.optimize_reference_more":", и ещё {n}","gs.optimize_orphans_removed":"Убрано забытых записей: {total} — подписи комнат: {rooms}; устройства: {devices}; групповые метки: {groups}. Все они принадлежали пространствам, удалённым ранее.","gs.optimize_live_positions":"Старые позиции в удалённых пространствах принадлежат существующим объектам: {n}{names}. Они будут сохранены.","gs.optimize_live_positions_remove":"Старые позиции в удалённых пространствах принадлежат существующим объектам: {n}{names}. Они выбраны для удаления.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Убрать старые позиции","gs.optimize_live_keep":"Сохранить старые позиции","gs.optimize_live_selected":"Старые позиции будут убраны после применения оптимизации.","gs.optimize_unverified":"Не удалось безопасно проверить позиций: {n}. Они оставлены без изменений.","gs.optimize_registry_limited":"Для безопасной проверки нужен полный доступ администратора к реестрам Home Assistant.","gs.optimize_vacuum_warning":"Сопоставления комнат пылесоса требуют проверки: {n}.","gs.optimize_details":"Подробности","gs.optimize_details_more":"И ещё записей: {n}.","gs.optimize_detail_removed":"будет удалено","gs.optimize_detail_live":"будет сохранено","gs.optimize_detail_unverified":"не проверено","gs.optimize_detail_room_label":"подпись комнаты","gs.optimize_detail_device":"устройство","gs.optimize_detail_group":"групповая метка","gs.optimize_detail_unknown":"неизвестный владелец","gs.optimize_detail_item":"{status}: {kind} {id}; прежнее пространство {space}","gs.align_warn":"Элементы, намеренно поставленные между узлами, будут сдвинуты. После операции доступна одна отмена — только до следующего изменения плана.","gs.align_run":"Оптимизировать","gs.align_done":"Планы оптимизированы: сдвинуто элементов — {n}, обслужено записей — {m}, исправлено ссылок — {r}","gs.optimize_undo":"Отменить последнюю оптимизацию","gs.optimize_undone":"Последняя оптимизация отменена","decor.furniture":"Мебель","furn.title":"Библиотека мебели","furn.symbol":"Символ","furn.group_furniture":"Мебель","furn.group_appliance":"Техника","furn.group_sanitary":"Сантехника","furn.group_other":"Прочее","furn.width":"Ширина","furn.depth":"Глубина","furn.pick_hint":"Выберите символ и кликните по плану.","furn.place_hint":"Кликните по плану — предмет встанет к ближайшей стене. Shift — свободно.","furn.sym_sofa":"Диван","furn.sym_armchair":"Кресло","furn.sym_coffee_table":"Журнальный столик","furn.sym_table_dining":"Обеденный стол","furn.sym_table_round":"Круглый стол","furn.sym_chair":"Стул","furn.sym_desk":"Письменный стол","furn.sym_bed_double":"Двуспальная кровать","furn.sym_bed_single":"Односпальная кровать","furn.sym_nightstand":"Тумбочка","furn.sym_wardrobe":"Шкаф","furn.sym_bookshelf":"Стеллаж","furn.sym_fridge":"Холодильник","furn.sym_stove":"Плита","furn.sym_dishwasher":"Посудомоечная машина","furn.sym_washer":"Стиральная машина","furn.sym_dryer":"Сушильная машина","furn.sym_tv":"Телевизор","furn.sym_ac":"Кондиционер","furn.sym_water_heater":"Бойлер","furn.sym_toilet":"Унитаз","furn.sym_bathtub":"Ванна","furn.sym_shower":"Душ","furn.sym_sink":"Раковина","furn.sym_kitchen_sink":"Кухонная мойка","furn.sym_bidet":"Биде","furn.sym_stairs":"Лестница","furn.sym_fireplace":"Камин","furn.sym_plant":"Растение","furn.sym_rug":"Ковёр","common.yes":"Да","common.no":"Нет","vac.diag_source":"Источник","vac.diag_platform":"Интеграция","vac.diag_status":"Статус","vac.diag_position":"Позиция","vac.diag_rooms":"Комнаты","vac.diag_rooms_value":"{total} · совпало имён: {matched} · {readiness}","vac.autocal_ready":"автокалибровка доступна","vac.autocal_not_ready":"нужно 3 совпавших имени","vac.diag_path":"Путь интеграции","vac.diag_map":"ID карты","vac.source_none":"не выбран","vac.source_status_ok":"Готов","vac.source_status_missing":"Не найден","vac.source_status_disabled":"Деактивирован в Home Assistant","vac.source_status_unavailable":"Недоступен","vac.source_status_unverified":"Нельзя проверить с текущими правами","vac.source_status_unsupported":"Нет данных позиции","vac.source_status_none":"Нет источника","vac.source_banner_missing":"Сохранённый источник больше не существует. Он не заменён автоматически: выберите другой или восстановите его в Home Assistant.","vac.source_banner_disabled":"Сохранённый источник деактивирован в Home Assistant. Активируйте его там или выберите другой.","vac.source_banner_unverified":"Текущих прав Home Assistant недостаточно для проверки источника. Привязка сохранена и не будет заменена автоматически.","vac.choose_source":"Выбрать источник","vac.source_auto":"Автоматически","vac.source_auto_hint":"Использовать совместимую сущность этого устройства","vac.all_cameras":"Все камеры","vac.all_cameras_warn":"Камера может не отдавать данные робота. Выбирайте её только если это карта вашего пылесоса.","vac.all_cameras_empty":"Других сущностей камеры не найдено.","vac.platform_unknown":"интеграция неизвестна","vac.cap_position":"позиция","vac.cap_rooms_short":"комнаты","vac.cap_path":"путь","vac.cap_map":"ID карты","vac.cap_none":"данные робота не обнаружены","vac.xcme_hint":"Включите атрибуты Xiaomi Cloud Map Extractor:","vac.documentation":"Документация","vac.residual_title":"Проверьте автокалибровку","vac.residual_message":"Совпавшие комнаты расходятся максимум на {error}. Примените приблизительную калибровку, подгоните её вручную либо отмените без изменения сохранённых настроек.","vac.apply_proposal":"Применить","gs.backup_group":"Резервная копия и перенос","gs.backup_hint":"Скачайте переносимую JSON-копию или проверьте и импортируйте копию, созданную House Plan.","backup.export_open":"Экспорт","backup.import_open":"Импорт","backup.export_title":"Экспорт House Plan","backup.import_title":"Импорт House Plan","backup.export_hint":"Выберите, должна ли копия содержать всю конфигурацию House Plan или только текущее пространство.","backup.full":"Полная копия","backup.current_space":"Текущее пространство","backup.current_space_title":"Текущее пространство: {title}","backup.no_current_space":"Нет текущего пространства","backup.plan_only":"Только планировка","backup.plan_only_hint":"Сохранить комнаты, стены, проёмы, декор и позиции подписей комнат без устройств и привязок Home Assistant.","backup.plan_only_preview":"Файл содержит только планировку","backup.privacy_warning":"Архив сохраняет названия, идентификаторы Home Assistant и точные координаты. Внутренние планы и вложения указываются ссылками, но не вкладываются; текущие состояния и маршруты пылесосов не включаются.","backup.download":"Скачать JSON","backup.export_done":"Резервная копия скачана","backup.reading":"Проверяем резервную копию…","backup.revalidated":"После предпросмотра план изменился. Сводка обновлена — проверьте её и подтвердите ещё раз.","backup.error.unauthorized":"У вас нет прав на экспорт или импорт этого плана.","backup.error.not_ready":"House Plan ещё не готов. Повторите попытку через несколько секунд.","backup.error.too_large":"Размер резервной копии превышает 8 МиБ.","backup.error.invalid_json":"Выбранный файл не является корректным JSON.","backup.error.invalid_format":"Выбранный файл не является резервной копией House Plan.","backup.error.unsupported_export_version":"Эта версия формата резервной копии не поддерживается установленной версией.","backup.error.future_model":"Резервная копия создана в более новой версии модели данных House Plan.","backup.error.invalid_config":"Резервная копия содержит некорректную конфигурацию House Plan.","backup.error.wall_model_migration_blocked":"Не удалось безопасно обновить модель стен из резервной копии. Сначала оптимизируйте исходный план и повторите экспорт.","backup.error.invalid_layout":"Резервная копия содержит некорректные позиции объектов.","backup.error.invalid_content":"Резервная копия содержит некорректные или несогласованные ссылки на файлы.","backup.error.space_not_found":"Выбранное пространство больше не существует.","backup.error.capacity_exceeded":"Добавление этой копии превысит ограничения размера плана.","backup.error.preview_expired":"Время предпросмотра истекло. Выберите файл резервной копии заново.","backup.error.preview_owner_mismatch":"Этот предпросмотр принадлежит другому пользователю Home Assistant.","backup.error.conflict":"После предпросмотра план изменился. Проверьте обновлённую сводку.","backup.error.content_confirmation_required":"Подтвердите отсоединение недоступных локальных файлов.","backup.error.commit_failed":"Не удалось безопасно применить копию. Предыдущий план восстановлен или ожидает восстановления.","backup.error.missing_plan":"После предпросмотра исчез связанный файл плана. Проверьте копию ещё раз.","backup.error.missing_content":"После предпросмотра исчез связанный локальный файл. Проверьте копию ещё раз.","backup.error.marker_control_missing":"В резервной копии отсутствует связанный источник света на плане.","backup.error.marker_control_not_light":"Связанная цель больше не отмечена как источник света.","backup.error.marker_control_self":"Источник света не может управлять самим собой.","backup.error.marker_control_cycle":"Резервная копия содержит циклическую цепочку управления светом.","backup.error.duplicate_marker_control":"Резервная копия содержит повторяющуюся связь с источником света.","backup.error.no_backup":"Нет снимка импорта или оптимизации, который можно восстановить.","backup.same_source":"Создано на этом экземпляре Home Assistant","backup.foreign_source":"Создано на другом экземпляре Home Assistant","backup.created":"Создано: {value}","backup.versions":"Карточка {card}; интеграция {integration}; модель данных {model}","backup.count_spaces":"Пространств: {n}","backup.count_rooms":"Комнат: {n}","backup.count_walls":"Стен: {n}","backup.count_openings":"Проёмов: {n}","backup.count_decor":"Объектов декора: {n}","backup.count_markers":"Устройств: {n}","backup.count_layout":"Позиций: {n}","backup.bindings":"Привязки — устройства: {device}, сущности: {entity}, виртуальные: {virtual}; позиций без пространства: {legacy}","backup.binding_status":"Статус на целевом экземпляре — активных: {active}, деактивированных: {disabled}, отсутствующих: {missing}","backup.missing_areas":"На целевом экземпляре отсутствуют зоны: {areas}","backup.dropped_marker_links":"Связи с источниками света вне переносимого пространства пропущены: {n}.","backup.repaired_target_refs":"Существующих ссылок восстановлено этим импортом: {n}.","backup.preserved_unresolved_refs":"Неоднозначные ссылки сохранены без изменений: {n}.","backup.preserved_unresolved_hint":"House Plan ничего не угадывал и не удалял. После импорта запустите «Оптимизировать планы», чтобы проверить оставшиеся ссылки.","backup.import_details":"Подробности восстановления ссылок","backup.import_detail.incoming_remapped":"Ссылок обновлено внутри импортируемой копии: {n}","backup.import_detail.target_repaired":"Существующих ссылок восстановлено: {n}","backup.import_detail.preserved_unresolved":"Неразрешимых ссылок сохранено: {n}","backup.import_detail.collisions":"Конфликтов назначения безопасно сохранено: {n}","backup.import_detail.dropped_links":"Входящих связей пропущено по правилам переноса: {n}","backup.import_detail.bounded_lineages":"Чрезмерно вложенных идентификаторов ограничено: {n}","backup.replace_warning":"Текущая конфигурация и расположение будут заменены. Загруженные файлы не удаляются. Одну отмену можно выполнить до следующего изменения плана.","backup.foreign_bookkeeping":"Служебные списки известных и новых устройств другого экземпляра импортированы не будут.","backup.final_name":"Название нового пространства","backup.target_settings":"Глобальные настройки целевого экземпляра не изменятся.","backup.duplicates":"Уже существующих привязок: {n}","backup.skip":"Пропустить повторяющиеся устройства","backup.virtual_copy":"Добавить безопасные статичные виртуальные копии","backup.content":"Связанное содержимое","backup.content_available":"доступно локально","backup.content_external":"внешняя ссылка","backup.content_detach_required":"будет отвязано","backup.confirm_detach":"Я понимаю, что недоступные внутренние планы и вложения будут отвязаны от импортируемой конфигурации.","backup.replace":"Заменить","backup.add":"Добавить пространство","backup.space_done":"Пространство импортировано: комнат — {rooms}, устройств — {markers}, существующих ссылок восстановлено — {refs}","backup.full_done":"Копия восстановлена: пространств — {spaces}, комнат — {rooms}, устройств — {markers}","backup.undo_import":"Отменить последний полный импорт","backup.import_undone":"Полный импорт отменён"}};function ra(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 G;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",Uo(this.hass),...i.classes,"virtual"===i.binding?"virtual":"",i.haDisabled?"ghost ha-disabled":"",null!=i.valueText?"valonly":""].filter(Boolean).join(" "),_=Go(i).join(";"),m=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`
${this._t("marker.preview.title")}
@@ -4400,7 +4375,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6c099935eee8c00d4fe12fcb7a86e6705c51
-
+
${Ko(i,{disabledTitle:this._reason("ha_disabled")})}
@@ -4411,11 +4386,11 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6c099935eee8c00d4fe12fcb7a86e6705c51
${this._t("marker.preview.integration")}${this._providerSummary(s)}
${this._t("marker.preview.source")}${this._sourceSummary(t)}
${this._t("marker.preview.current_state")}${this._stateSummary(t)}
-
${this._t("marker.preview.result")}${g}
- ${t.fallbackReason?W`
${this._reason(t.fallbackReason)}
`:U}
+
${this._t("marker.preview.result")}${m}
+ ${t.fallbackReason?W`
${this._reason(t.fallbackReason)}
`:G}
${t.explanation.notices.map(t=>W`
${this._reason(t)}
`)}
- ${h<1?W`
${this._t("marker.preview.scaled",{n:d})}
`:U}
- ${e&&this._reducedMotion?W`
${this._t("marker.preview.reduced_motion")}
`:U}
+ ${h<1?W`
${this._t("marker.preview.scaled",{n:d})}
`:G}
+ ${e&&this._reducedMotion?W`
${this._t("marker.preview.reduced_motion")}
`:G}
${t.visualSources.length||t.criticalSources.length?W`
@@ -4428,7 +4403,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6c099935eee8c00d4fe12fcb7a86e6705c51
${this._sourceProvider(t.eid)}
`)}
- `:U}
+ `:G}
${"icon_ripple"===t.display?W`
-
`:U}
+
`:G}
`}}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 {
@@ -4512,14 +4487,14 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6c099935eee8c00d4fe12fcb7a86e6705c51
.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]}`,ga=(t,e)=>t[0]===e[0]&&t[1]===e[1],ma=(t,e)=>{const i=_a(t),s=_a(e);return i{let e=0;for(let i=0;i{if(t.length<3)return!1;for(let e=0;e1e-12},ya=t=>{for(let e=0;e+1ga(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=>ga(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{let i=0;for(const s of t){const t=hi(s);if(t)for(let s=0;s{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;it[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(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||tMath.abs(a+(t-p)/m*(r-a));s=Math.min(s,f(_),f(g))}}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;ei)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 La(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 Ha(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}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;ts||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),g=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+1r>t.lo-s&&rt.roomId))].sort();a.push({lo:i,hi:n,owners:l})}return a}(_,Math.min(d,p),Math.max(d,p),n);return!g.length||g.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:g}}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&&et.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),g=Math.min(s.fixed,a),m=Math.max(s.fixed,a);for(let t=0;t+1u-i&&o<_+i,c=o>g-i&&o2||d>2)return!1;if(a&&c){if(h!==d)return!1}else if(!a&&c){if(d!==l||h>0&&h!==d)return!1}else if(2===h?2!==d:d>1)return!1}}return!0}function Ja(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)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),g=Pa(c);if(Math.abs(g)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=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),g=d[0]+l[0]*_,m=d[1]+l[1]*_,f=e=>{return i=g+h[0]*e,s=m+h[1]*e,n=u,o=18,a=p[0],r=p[1],l=t.gearWidthPx,c=t.gearHeightPx,2*Math.abs(i-a)=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 gr(t){return`${t.source}|${t.phase}|${t.sunX.toFixed(2)}|${t.sunY.toFixed(2)}|`+t.sunOpacity.toFixed(3)}const mr=[[-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]],g=Math.max(0,a?.[i.id]||0),m=i.x+_[0]*g/2,f=i.y+_[1]*g/2,v=Math.cos(p)*u,y=Math.sin(p)*u,b=[m-v,f-y],w=[m+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`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
{let e=0;for(let i=0;i{if(t.length<3)return!1;for(let e=0;e1e-12},ya=t=>{for(let e=0;e+1ma(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{let i=0;for(const s of t){const t=hi(s);if(t)for(let s=0;s{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;it[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(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||tMath.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;ei)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 La(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 Ha(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}const ja=(t,e)=>"h"===e?t[0]:t[1],Ua=(t,e)=>"h"===e?t[1]:t[0];function Ga(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=Ua(c,l),_=function(t,e,i,s){const n=[];for(const o of t)for(let t=0;ts||Math.abs(Ua(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+1r>t.lo-s&&rt.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(Ua(a,t.axis)-t.line)>i||Math.abs(Ua(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&&et.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+1u-i&&o<_+i,c=o>m-i&&o2||d>2)return!1;if(a&&c){if(h!==d)return!1}else if(!a&&c){if(d!==l||h>0&&h!==d)return!1}else if(2===h?2!==d:d>1)return!1}}return!0}function Ja(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)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)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=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)=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 G;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`
${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`
`})}
-
`}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 Lr(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 Hr(t){const e=Lr(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,g=e>0?-t.w/2:t.w/2,m=i>0?-t.h/2:t.h/2,f=u+g*c+m*d,v=_+g*h+m*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)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{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{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 e of Xr(t.wall_segments))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))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.wall_segments))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;function cl(t,e,i,s,n){const o=i[0]-t[0],a=i[1]-t[1],r=Math.hypot(o,a);if(!(r>0)||r<=n+1e-9)return null;const l=o/r,c=a/r,h=e=>{const s=(e[0]-t[0])*l+(e[1]-t[1])*c,o=(n-s)/(r-s);return!Number.isFinite(o)||o<-1e-9||o>1+1e-9?null:[e[0]+(i[0]-e[0])*o,e[1]+(i[1]-e[1])*o]},d=h(e),p=h(s);return d&&p?[[t[0],t[1]],e,d,p,s]:null}const hl=Math.sin(.25*Math.PI/180);function dl(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function pl(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 ul(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<2}function _l(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function gl(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 _l(t)/s*i}function fl(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function vl(t){return Math.max(1e-6*Math.abs(t),1e-9)}function yl(t,e){if(!(e>0&&Number.isFinite(t)))return t;const i=fl(t,e);return Math.abs(i-t)<=vl(e)?i:t}function bl(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 wl(t,e,i){const s=[yl(t[0],i),yl(t[1],i)],n=[yl(e[0],i),yl(e[1],i)],o=fl((s[0]+n[0])/2,i),a=fl((s[1]+n[1])/2,i),[r,l]=bl(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 kl(t,e,i,s){return 1===s?wl(t,e,i):wl([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function xl(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 $l(t,e,i,s,n){const o=n>0?n:1;return{key:kl(t,e,s,o),cm:_l(i),a:[t[0]/o,t[1]/o],b:[e[0]/o,e[1]/o]}}function Sl(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 Ml(t,e){const[i,s]=bl(t,e);let n=Math.atan2(s,i);return n<0&&(n+=Math.PI),n}function Cl(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function Dl(t,e,i,s,n=1){if(!t?.length)return null;const o=kl(e,i,s,n),a=t.find(t=>t.key===o);if(a)return a;const r=n>0?n:1,l=vl(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=xl(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=Ml(e,i),u=Math.max(.5*s,1e-9)*r;for(const e of Sl(t,r))if(Cl(e.ang,p)&&Math.hypot(e.x-h,e.y-d)<=u)return e.w;return null}function Pl(t,e,i,s,n=1){const o=Dl(t,e,i,s,n);if(o&&o.cm>0)return _l(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=Ml(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=xl(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||!Cl(Ml(t[0],t[1]),r))continue;if(gc(e[0],e[1],t[0][0],t[0][1],t[1][0],t[1][1])>l||gc(i[0],i[1],t[0][0],t[0][1],t[1][0],t[1][1])>l)continue;if(n+l(o.has(t.key)||(t=>{const e=xl(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 Cl(Ml(i,o),Ml(e,s))&&gc(i[0],i[1],e[0],e[1],s[0],s[1])<=l&&gc(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(!Cl(Ml(i,o),Ml(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 Rl(t,e,i,s=8){const[n,o]=bl(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 Il(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[t[0]+(e[0]-t[0])*i,t[1]+(e[1]-t[1])*i],g=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1])<=c,m=(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]:g(t,e.oa)?[...e.na]:g(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]=bl(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=_l(i);if(y.some(t=>t.entry.cm===a&&g(t.span[0],n)&&g(t.span[1],o)))return;const l=$l(n,o,a,s,r);v.push(l),y.push({entry:l,span:[n,o]})};for(const e of t){const t=xl(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:_l(e.cm)});continue}const p=[];for(const t of h){if(!Cl(Ml(i,s),Ml(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+1r>=t.lo-1e-12&&r<=t.hi+1e-12);if(!l.length){y.push([o,a]);continue}const h=[m(o,l[0].move),m(a,l[0].move)],f=l.slice(1).some(t=>{const e=m(o,t.move),i=m(a,t.move);return!g(h[0],e)||!g(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&&g(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(g(i,t[0])&&g(s,t[1])||g(i,t[1])&&g(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=[Sl([e],r)[0]];1!==r&&i.push(Sl([e],1)[0]);const s=i.filter(Boolean).some(t=>h.some(e=>{if(!Cl(t.ang,Ml(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&&gc(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=Sl([e],r)[0];if(t){const e=new Set;for(const i of h){if(!Cl(t.ang,Ml(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(gc(t.x,t.y,i.oa[0],i.oa[1],i.ob[0],i.ob[1])>l)continue;const o=m([t.x,t.y],i),[a,c]=bl(i.na,i.nb),h=Math.max(s*r,1e-6);e.add(kl([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:_l(e.cm)})}return v}function Al(t,e,i,s,n=1,o="affine"){return Il(t,e,i,s,n,o)}function zl(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=xl(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))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=xl(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,g=[];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&&g.push([l,c])}g.sort((t,e)=>t[0]-e[0]||t[1]-e[1]);let m=0;for(const[t,e]of g){if(t>m+r)break;if(m=Math.max(m,e),m>=p-r)break}mt.key!==a);return null==s||s<1?r:[...r,$l(e,i,s,n,o)]}function El(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=ec(t,e,i,s,n,o);if(!a)return[];const r=[];for(let t=0;t0||(c=Fl(c,e,i,s,n,a)))}return c}function Ol(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 Ll(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 Bl(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 ql(t,e){try{const i=oi(vc(t),vc(e));let s=null,n=0;for(const t of i||[]){const e=t?.[0]||[],i=Math.abs(Kl(e));e.length>=4&&i>n&&(n=i,s=e.slice(0,-1).map(t=>[t[0],t[1]]))}return s}catch{return null}}function Wl(t,e=1e-6){const i=(t||[]).map((t,e)=>({segment:t,index:e})).filter(({segment: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(({segment: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=>Ll(e,t,s))||o.push(t);const a=[];for(const t of o){const e=[];let n=!1;for(const{segment:o,index:a}of i){const i=Math.hypot(o.b[0]-o.a[0],o.b[1]-o.a[1]);Ll(t,o.a,s)?e.push({u:[(o.b[0]-o.a[0])/i,(o.b[1]-o.a[1])/i],halfDepth:o.halfDepth,length:i,index:a}):Ll(t,o.b,s)?e.push({u:[(o.a[0]-o.b[0])/i,(o.a[1]-o.b[1])/i],halfDepth:o.halfDepth,length:i,index:a}):Hl(t,o,s)&&(n=!0)}if(n||2!==e.length)continue;const[o,r]=e,l=o.u[0]*r.u[1]-o.u[1]*r.u[0];if(Math.abs(l)<1e-9)continue;const c=l<0?1:-1,h=[-o.u[1],o.u[0]],d=[-r.u[1],r.u[0]],p=[t[0]+h[0]*o.halfDepth*c,t[1]+h[1]*o.halfDepth*c],u=[t[0]-d[0]*r.halfDepth*c,t[1]-d[1]*r.halfDepth*c];if(!Xl(p,o.u,u,r.u))continue;const _=[[o,r,u,[d[0]*-c,d[1]*-c]],[r,o,p,[h[0]*c,h[1]*c]]];for(const[e,,i,n]of _){const o=Math.min(2*e.halfDepth,e.length),r=[-e.u[1]*e.halfDepth,e.u[0]*e.halfDepth],l=[[t[0]+r[0],t[1]+r[1]],[t[0]+e.u[0]*o+r[0],t[1]+e.u[1]*o+r[1]],[t[0]+e.u[0]*o-r[0],t[1]+e.u[1]*o-r[1]],[t[0]-r[0],t[1]-r[1]]],c=t=>(t[0]-i[0])*n[0]+(t[1]-i[1])*n[1],h=[];for(let t=0;t=-1e-12&&h.push(e),s>1e-12&&n<-1e-12||s<-1e-12&&n>1e-12){const t=s/(s-n);h.push([e[0]+(i[0]-e[0])*t,e[1]+(i[1]-e[1])*t])}}h.length>=3&&Math.abs(Kl(h))>s*s&&a.push({segmentIndex:e.index,wedge:h})}}return a}function jl(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=>Ll(e,t,s))||o.push(t);const a=[],r=i.map((t,e)=>({roomId:"",a:[t.a[0],t.a[1]],b:[t.b[0],t.b[1]],key:`join-${e}`,kind:"outer",cm:0,open:!1,half:t.halfDepth})),l=ac(r,s);for(const t of rc(l).fans)a.push(t);const c=t=>!!lc(l,t);for(const t of o){if(c(t))continue;const e=[];for(const n of i)Ll(t,n.a,s)?Bl(e,n.b[0]-n.a[0],n.b[1]-n.a[1],n.halfDepth):Ll(t,n.b,s)?Bl(e,n.a[0]-n.b[0],n.a[1]-n.b[1],n.halfDepth):Hl(t,n,s)&&(Bl(e,n.a[0]-t[0],n.a[1]-t[1],n.halfDepth),Bl(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;is*s&&a.push(g)}}}return a}function Gl(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=Zl(i,t);if(n&&o)return`${Ql(n)} ${Ql(tc(o))}`}}const n=[];for(let i=0;i=1e-9&&r>0&&n.push({a:o,b:a,halfDepth:r})}const o=n.map(Ol);for(const{segmentIndex:t,wedge:e}of Wl(n)){const i=o[t];if(!i)continue;const s=ql(i,e);s&&(o[t]=s)}const a=[...o.filter(t=>!!t),...jl(n)],r=function(t){let e=null;try{for(const i of t){if(i.length<3)continue;const t=vc(i);e=e?si(e,t):[t]}return e}catch{return null}}(a);return r?Tc(r):a.map(t=>Ql(t)).join(" ")}function Ul(t,e,i,s,n=1){if(!i.length)return!1;const o=Vl(s,n),a=(t[0]+e[0])/2,r=(t[1]+e[1])/2,[l,c]=bl(t,e);for(const t of i){const[e,i]=bl([t[0],t[1]],[t[2],t[3]]);if(!(Math.abs(l*i-c*e)>.05)&&gc(a,r,t[0],t[1],t[2],t[3])<=o)return!0}return!1}function Vl(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function Kl(t){let e=0;for(let i=0;it[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 Jl(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 Xl(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 Zl(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;o0||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]+g*p,e[1]+m*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(Jl(w,k)){const e=t[o],i=[e[0]+u*d,e[1]+_*d],s=[e[0]+g*p,e[1]+m*p];n.push(i),Math.hypot(s[0]-i[0],s[1]-i[1])>1e-9&&n.push(s);continue}const S=Xl(x,w,$,k),M=Math.max(d,p,1e-9),C=lc(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]+g*p,t[o][1]+m*p]),d>0||p>0||n.push([t[o][0],t[o][1]])}return n.length>=3?n:null}function Ql(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),r=hi(a);if(!r||r.length<3)return null;const l=Vl(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=xl(t,n);e&&c.push(e[0],e[1])}const h=[],d=[];for(let t=0;tl)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 ic(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;l0?(o[l]=_l(c.cm),a.add(c.key)):r.push(l)}if(!r.length)return o;const l=s>0?s:1,c=Math.max(.5*i,1e-9)*l,h=Sl(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=Ml(s,a);let p=null;for(const t of h){const e=xl(t.w,l);if(!e||!Cl(Ml(e[0],e[1]),d))continue;if(gc(s[0],s[1],e[0][0],e[0][1],e[1][0],e[1][1])>c||gc(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||oc||gc(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(gc(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&&o0?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]=sc(t.point,s),n=nc(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=oc(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]=sc(i.point,s),o=nc(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||nt.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 oc(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]=sc(t.point,s),n=nc(e,i),o=u.get(n)||[];o.push(t),u.set(n,o)}return{epsilon:s,coordinateScale:n,nodes:d,index:u}}function rc(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{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 g=t=>{Math.abs(Kl(t))>i&&e.fans.push(t)};if(_){const t=1.5*Math.max(o.halfDepth,a.halfDepth);g(cl([n[0],n[1]],h,_,d,t)??[[n[0],n[1]],h,_,d]);continue}if(l){g([[n[0],n[1]],h,d]);continue}const m=(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]*m(o.halfDepth,o.thickLength),h[1]+o.u[1]*m(o.halfDepth,o.thickLength)],v=[d[0]+a.u[0]*m(a.halfDepth,a.thickLength),d[1]+a.u[1]*m(a.halfDepth,a.thickLength)];g([[n[0],n[1]],h,f,v,d])}}return e}function lc(t,e){return!t||!Array.isArray(e)||e.length<2||!e.slice(0,2).every(Number.isFinite)?null:oc(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 cc(t,e,i,s,n,o,a){return ac(dc(t,e,i,s,n,o,a),4*Vl(s,a),a)}function hc(t,e,i,s,n,o,a,r=1){const l=ec(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,Vl(n,r)),h=function(t,e,i,s,n){const o=Vl(s,n),a=[];for(let r=0;rgc(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=ic(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 dc(t,e,i,s,n,o,a=1){const r=[];for(const l of t||[]){if(!l?.id)continue;const c=hc(t,l.id,e,i,s,n,o,a);if(c)for(let t=0;t0&&(r=Fl(r,t.a,t.b,t.cm,s,a));return r}function uc(t,e,i,s,n,o,a=1){if(!e?.length)return[];const r=dc(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=hc(t,r.id,e,i,s,n,o,a);if(l)for(let t=0;t0)||null===l.kinds[i]){t++;continue}const o=c(kl(l.poly[i],l.poly[(i+1)%l.poly.length],s,a));let r=t;for(;r+10&&p.push({a:[d[0],d[1]],b:[u[0],u[1]],key:kl(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,g=new Set,m=4*Vl(s,a);for(const t of p){const e=h.filter(e=>!g.has(e.key)&&e.cm===t.cm&&e.ownerSignature===t.ownerSignature&&Cl(Ml(e.a,e.b),Ml(t.a,t.b))&&gc(e.a[0],e.a[1],t.a[0],t.a[1],t.b[0],t.b[1])<=m&&gc(e.b[0],e.b[1],t.a[0],t.a[1],t.b[0],t.b[1])<=m);if(e.length){for(const t of e)g.add(t.key);_.has(t.key)||(_.add(t.key),u.push($l(t.a,t.b,t.cm,s,a)))}}for(const t of h)g.has(t.key)||_.has(t.key)||(_.add(t.key),u.push($l(t.a,t.b,t.cm,s,a)));return u}function _c(t,e,i,s,n,o,a,r=1){const l=Vl(n,r),c=(s[0]+s[2])/2,h=(s[1]+s[3])/2,d=Ml([s[0],s[1]],[s[2],s[3]]);let p=null;for(const s of dc(t,e,i,n,o,a,r)){if(!Cl(Ml(s.a,s.b),d))continue;const t=gc(c,h,s.a[0],s.a[1],s.b[0],s.b[1]);t>4*l||(!p||t[t[0],t[1]]),o=Math.abs(Kl(n));n.length>=3&&o>i&&(e=n,i=o)}return e}function fc(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=hc(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=cc(t,i,s,n,o,a,r),u=Zl(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,g=l??("ok"===_?.status||"degraded-extra"===_?.status?_.roomGeom:void 0);if(g)try{const t=mc(oi(vc(d.poly),g));if(t)return t}catch{}return function(t,e){try{return mc(ni(vc(t),vc(e)))}catch{return null}}(u,d.poly)||h.map(t=>[t[0],t[1]])}function vc(t){const e=t.map(t=>[t[0],t[1]]);return e.push([t[0][0],t[0][1]]),[e]}function yc(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 bc(t,e,i,s){const n=s?new Set(s):null;let o=null;for(let s=0;se.epsilon))continue;const l=Sc([[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=vc(l);o=o?si(o,c):c}}return o}function wc(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=Sc([[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(vc(a),i);Array.isArray(r)&&0!==r.length&&(s=s?si(s,r):r)}return s}function kc(t,e,i=2*(4*t.halfDepth+2*e.epsilon)){const s=function(t,e=hl){const i=new Set,s=Number.isFinite(e)&&e>=0?e:hl;for(let e=0;et-e)}(t);return s.length?bc(t,e,i,s):null}function xc(t,e,i,s,n){const o=function(t){let e=null;for(const i of t){const t=vc(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;o1e-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],g=Xl(u,a.u,_,r.u);if(!g)continue;const m=Math.hypot(g[0]-n.point[0],g[1]-n.point[1]);if(!Number.isFinite(m)||m<=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=Sc([[u[0]+a.u[0]*f,u[1]+a.u[1]*f],[_[0]+r.u[0]*v,_[1]+r.u[1]*v],g],t.coordinateScale);if(y&&s.push(y),i){const e=g[0]-n.point[0],i=g[1]-n.point[1],o=Math.hypot(e,i),a=m-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=Sc([[g[0]-l*r+h*r,g[1]-c*r+d*r],[g[0]-l*r-h*r,g[1]-c*r-d*r],[g[0]+l*r,g[1]+c*r]],t.coordinateScale);p&&s.push(p)}}}return s}({...e,nodes:[t]},i,s));return o&&n?oi(o,n):o}function $c(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=kc(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=xc(t,e,!1,!0,n);c&&(a=oi(a,c));let h=bc(t,e,r);const d=xc(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,vc([[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,vc(l));s?u=ni(u,s):i&&(u=ni(u,i));const _=vc(l),g=oi(a,_),m=i?oi(ni(a,_),i):null,f=wc(t,e,_);o=si(g,...m?[m]:[],...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 Sc(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(Kl(n))>s*s?n:null}function Mc(t,e,i,s){if(gc(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 Cc(t,e,i){const s=[],n=[];for(let o=0;oi*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(!Mc(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=3&&n.length===s.length?{poly:s,offsets:n}:null}function Dc(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(vc(l[0]));for(let t=1;t"outer"===t.kind&&t.half>0),p=4*Vl(s,a),u=r||ac(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=Cc(t,d,p);if(!e||!e.offsets.some(t=>t>0))continue;const i=zc(e.poly,e.offsets,u),s=Zl(e.poly,e.offsets,u);if(!i||!s)continue;const n=oi(vc(i),vc(s));_=_?si(_,n):n}return{centre:c,shell:_||[]}}function Pc(t,e,i,s,n,o,a=1){try{const r=Dc(t,e,i,s,n,o,a,cc(t,e,i,s,n,o,a));if(!r)return[];return r.shell?.length?si(r.centre,r.shell):r.centre}catch{return null}}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?" ":"")+Ql(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=cc(t,e,i,n,o,a,r);let p=0;for(const s of t||[]){if(!s?.id)continue;const l=hc(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=Zl(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*Vl(s,a),c=new Map;for(const r of dc(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||ac(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;i0?[t.slice(),p,g,_]:[t.slice(),_,g,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(Kl(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 g="exterior";try{const v=Dc(t,e,i,n,o,a,r,d);g="paper";const y=v?v.shell?.length?si(v.centre,v.shell):v.centre:[],b=t=>{const e=vc(t.outset);return t.inset?oi(e,vc(t.inset)):e};g="room-rings";let w=null;for(const t of h)try{const e=b(t);w=w?si(w,e):e}catch{}if(g="edge-bodies",v)for(const s of Ac(t,e,i,n,o,a,r))try{const t=ni(vc(s.quad),v.centre);w=w?si(w,t):t}catch{}g="junctions",w=function(t,e,i=1,s=si){let n=t;for(const t of e||[]){const e=Sc(t,i);if(e)try{const t=vc(e);n=n?s(n,t):t}catch{}}return n}(w,u,r),g="facade-clip",w&&v&&(w=ni(w,v.centre)),g="exterior-shell";const k=[];let x=0;if(v?.shell?.length)if(w)try{const t=si(w,v.shell);if(!yc(t))throw new Error("invalid shell union");w=t}catch{if(!yc(w)||!yc(v.shell))throw new Error("invalid shell");k.push({id:"exterior-shell",geom:v.shell}),x++}else w=v.shell;if(g="multi-wall-trim",w&&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){w=$c(w,{...d,nodes:e},v?.centre,y)}}if(g="junction-corners",d.nodes.length){const s=rc(d),l=function(t,e,i,s,n,o,a,r){try{const l=Dc(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}}(t,e,i,n,o,a,r,d);for(const t of s.fans)try{let e=[vc(t)];if(l&&(e=ni(e,l)),!e?.length)continue;w=w?si(w,e):e}catch{}m=w,f=Math.max(d.epsilon,1e-9)**2,w=Array.isArray(m)?m.map(t=>{if(!Array.isArray(t)||!t.length)return t;const[e,...i]=t;return Math.abs(Kl(e||[]))<=f?null:[e,...i.filter(t=>Math.abs(Kl(t||[]))>f)]}).filter(t=>!!t):m}const $=w||[];g="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]];w&&(w=oi(w,vc(d)));for(const t of k)t.geom=oi(t.geom,vc(d))}g="extras";const S=[];let M=0;const C=c.mergeExtra||((t,e)=>t?si(t,e):e);for(let t=0;tt.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]))||Math.abs(Ti(e))<=1e-9){M++;continue}const i=[vc(e)];try{const e=C(w,i,t);if(!yc(e))throw new Error("invalid extra union");w=e}catch{M++,yc(i)&&S.push({id:`extra-${t}`,geom:i})}}S.push(...k),S.sort((t,e)=>Tc(t.geom).localeCompare(Tc(e.geom)));const D=w||[];return{status:M||x?"degraded-extra":"ok",geom:D,components:[...yc(D)?[{id:"primary",geom:D}]:[],...S.map((t,e)=>({...t,id:`isolated-${e}`}))],roomGeom:$,paperGeom:y,depthUnits:p,openingIndex:_,degradedExtraCount:M+x}}catch{return c.onCoreFailure?.(g),{status:"failed-core",geom:[],components:[],roomGeom:[],paperGeom:[],depthUnits:p,openingIndex:null,degradedExtraCount:0}}var m,f}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=hc(t,c.id,e,i,s,n,o,a);if(!h)continue;const d=h.poly;for(let t=0;t0))continue;const u=kl(e,i,s,a);if(r.has(u))continue;r.add(u);const _=ml(p,n,o),[g,m]=Yl(d,t),f=-g,v=-m,y=_/2,b=[[e[0]+f*y,e[1]+v*y],[i[0]+f*y,i[1]+v*y],[i[0]+g*y,i[1]+m*y],[e[0]+g*y,e[1]+m*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]]);tc(t),e.slice().reverse();const n=[];for(let o=0;o0||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(Jl(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=Xl(x,w,$,k),M=Math.max(r,l,1e-9),C=lc(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=hc(t,l.id,e,i,s,n,o,a);if(!c)continue;const h=Math.abs(Ti(c.poly));for(let t=0;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=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 g=0;for(const e of t.edges){if(i&&!(e.half>0))continue;if(!Rl(e.a,e.b,o))continue;const[t,a]=bl(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,m=(e.b[0]-s)*l+(e.b[1]-n)*c,f=Math.max(-p,Math.min(r,m)),v=Math.min(p,Math.max(r,m));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:g++,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 m=t=>{const e=[..._.values()].filter(e=>e.side===t&&e.coverage>u);return e.sort(Nc),e[0]||null};return{negative:m(-1),positive:m(1)}}function Lc(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 Hc(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+1e+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),g=Math.max(p,u),m=`${n.key}|${e}`,f=s.get(m)||[];let v=[[_,g]];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)]),et[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(m,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:Hc(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 Ol({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 ch(s)}catch{return[t]}}function th(t,e){try{const i=oi([[...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]]]]);let s=null,n=0;for(const t of i||[]){const e=t?.[0]||[],i=Math.abs(Ti(e));e.length>=4&&i>n&&(n=i,s=e.slice(0,-1).map(t=>[t[0],t[1]]))}return s}catch{return null}}function eh(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=o.length&&(d[i.at].body=s))}for(const t of d)h.push(...Qc(t.body,r.get(t.id)||[],s));const u=(t.wall_columns||[]).map(t=>Zc(t,e,i)),_=jl([...o,...a],s).flatMap(t=>Qc(t,n,s));return{drafts:l,partitions:c,columns:u,patches:_,all:[...l,...h,..._,...u]}}function ih(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 sh=t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`;function nh(t){const e=[];for(const i of t||[]){if(rh([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(sh(i.slice(0,-1)))}t.length&&e.push(t.join(" "))}return e}function oh(t,e,i={}){const s=ih(t.filter(t=>t.length>=3)),n=e.some(t=>!Yc(t))?null:ih(e);if(!s)return[];if(n)try{return nh(ni(s,n))}catch{}return function(t,e,i){let s=null;for(let n=0;n=4&&e.push(t.slice(0,-1).map(t=>[t[0],t[1]]));return e}function ch(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 hh(t,e){let i=!1;for(let s=0,n=e.length-1;st[1]!=l>t[1]&&t[0]<(r-o)*(t[1]-a)/(l-a||1e-12)+o&&(i=!i)}return i}function dh(t,e,i){return function(t,e){for(const i of e||[]){const e=i?.[0];if(!e?.length||!hh(t,e))continue;let s=!1;for(let e=1;ehh(t,e))}function ph(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 uh=2.54;function _h(t){const e="number"==typeof t?t:NaN;return!Number.isFinite(e)||e<=0||5===e?1:5/e}function gh(t,e){return t*_h(e)}function mh(t){return t?uh:1}function fh(t,e){if(!e)return String(t);const i=t/uh;return String(Math.round(1e6*i)/1e6)}const vh=1e3;function yh(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 bh=.01,wh=100;function kh(t,e=1e3){const i=yh(t?.plan_aspect,e),s=Number(t?.plan_scale),n=Number.isFinite(s)&&s>0?Math.min(wh,Math.max(bh,s)):1,o=Number(t?.plan_scale_x),a=Number(t?.plan_scale_y),r=Number.isFinite(o)&&o>0?Math.min(wh,Math.max(bh,o)):n,l=Number.isFinite(a)&&a>0?Math.min(wh,Math.max(bh,a)):n,c=Number(t?.plan_x),h=Number(t?.plan_y),d=Ar(t?.plan_angle);return{x:i.x+(Number.isFinite(c)?Ah(c):0)*e,y:i.y+(Number.isFinite(h)?Ah(h):0)*e,w:i.w*r,h:i.h*l,...d?{angle:d}:{}}}function xh(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 $h(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=vh,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]*vh,i[1]*e,i[2]*vh,i[3]*e],bg:t.plan_url?{href:As(t.plan_url),...kh(t,vh)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...xh(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*vh:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*vh:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*vh,t[1]*e]):void 0,wall_ids:Array.isArray(i.wall_ids)?[...i.wall_ids]:void 0}}),room_drafts:(t.room_drafts||[]).map(t=>({id:t.id,points:(t.points||[]).map(t=>[t[0]*vh,t[1]*e]),segments:(t.segments||[]).map(t=>({..."string"==typeof t.id&&t.id?{id:t.id}:{},cm:Number(t.cm)}))})),partitions:(t.partitions||[]).map(t=>({id:t.id,a:[t.a[0]*vh,t.a[1]*e],b:[t.b[0]*vh,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]*vh,t.center[1]*e],cm:Number(t.cm),..."circle"===t.shape?{}:{angle:Uc(t.angle)}}))}}):[]}const Sh=5e3,Mh=Sh*vh,Ch=240,Dh=vh/Ch,Ph=1/Ch;function Th(t){if(!Number.isFinite(t))return t;const e=Math.round(t*Ch/vh)*vh/Ch;return Math.abs(e-t)<=1e-9*Dh?t:e}function Rh(t){return{x:Th(t.x),y:Th(t.y)}}function Ih(t){return Number.isFinite(t)?Math.min(Mh,Math.max(-Mh,t)):0}function Ah(t){return Number.isFinite(t)?Math.min(Sh,Math.max(-Sh,t)):0}const zh=1/3,Fh=200;function Eh(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)&&(ts&&(s=t),a>n&&(n=a))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:n}}function Nh(t){return t.poly&&t.poly.length?Eh(t.poly):null==t.x||null==t.y?null:Eh([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function Oh(t,e){const i=[];for(const e of t.rooms||[]){const t=Nh(e);t&&i.push(t)}if(t.bg){const e=Eh(Lr(t.bg));e&&i.push(e)}for(const t of e||[])if(Array.isArray(t)){const e=Eh([t]);e&&i.push(e)}else i.push(t);return i}const Lh=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.minXs&&(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 Bh(t,e){let{x:i,y:s,w:n,h:o}=t;n<30&&(i=i+n/2-100,n=Fh),o<30&&(s=s+o/2-100,o=Fh);const a=Math.max(n,o)*e;return{x:i-a,y:s-a,w:n+2*a,h:o+2*a}}function qh(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)<=Mh&&Math.abs(t.maxX)<=Mh&&Math.abs(t.minY)<=Mh&&Math.abs(t.maxY)<=Mh);if(!o.length)return{core:null,all:null,outliers:0};const a=Hh(o);if(o.length<4){const t=Bh(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=Lh(r),h=Lh(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,g=_&&_<=o.length*zh?o.filter((t,e)=>!u[e]):o;return{core:Bh(Hh(g)||a,i),all:Bh(a,i),outliers:g===o?0:_}}function Wh(t,e,i=.05){const s=qh(Oh(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,vh,vh];return{x:n[0],y:n[1],w:n[2],h:n[3]}}function jh(t){const e=Wh(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function Gh(t){const e=[];for(const i of t.rooms||[]){const t=Nh(i);t&&e.push(t)}const i=qh(e,{pad:0}).core,s=vh*_h(t.cellCm??5);return i?Math.max(s,Math.min(Mh,Math.max(i.w,i.h))):s}function Uh(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*Gh(e)*o/n}const Vh=[1,2,5,10,20,50,100,200,500,1e3];function Kh(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 Yh(t,e,i,s){const n=i["rl_"+(t.id||"")];if(n&&n.s===e)return{x:n.x*vh,y:n.y*vh};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 Rh({x:o[0],y:o[1]})}const Jh=["furniture","appliance","sanitary","other"],Xh=[{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]]}],Zh=new Map(Xh.map(t=>[t.id,t]));function Qh(t){return t&&Zh.get(t)||null}function td(t){return Xh.filter(e=>e.group===t)}function ed(t,e,i=Dh,s=1e3){const n=Number(e)>0?Number(e):5;return(Number(t)||0)/n*i/s}const id=5e-4,sd=Sh;function nd(t){return Number.isFinite(t)?Math.max(id,Math.min(sd,t)):id}const od=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function ad(t,e,i){const s=Qh(t);if(!(s&&e>0&&i>0))return"";const n=t=>od(t*e),o=t=>od(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${od(l*e)} ${od(c*i)} 0 0 1 ${n(s+l)} ${o(r)}A${od(l*e)} ${od(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{let e=(t%360+360)%360;return e>180&&(e-=360),e};function ld(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 g=((t-s)*d+(e-l)*p)/u;g=Math.max(0,Math.min(1,g));let m=s+g*d,f=l+g*p;const v=Math.hypot(t-m,e-f);if(!(v0){const t=Math.round(g*_/o)*o;m=s+t/_*d,f=l+t/_*p}a={cx:m+y*(i/2),cy:f+b*(i/2),angle:rd(180*Math.atan2(-y,b)/Math.PI),dist:v}}return a}function cd(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 hd(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 dd(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function pd(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!dd(t.a)||!dd(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 ud(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 _d(t,e){return ud(t,e).q}function gd(t,e,i,s,n){const o=ud(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&&e0?s:1,g=Math.round(u/_)*_,m=Math.max(0,Math.min(p,g))/p;return[l+h*m,c+d*m]}function md(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt?.id),a=pd(e);if(a.length)return Sd(a,t,i,s).map(t=>hd(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;tcd([t[0],t[1]],[t[2],t[3]],e))}function yd(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;tLs(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 bd(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:Ls(t,e)}function wd(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)&&(Ls([e[0],e[1]],t)<=4*i||Ls([t[0],t[1]],e)<=4*i))}function kd(t,e,i){if(!t.length)return null;t.sort((t,e)=>t.distance-e.distance);const s=t[0];for(let n=1;ne)break;if(o.semantic!==s.semantic||!wd(s.seg,o.seg,i))return"ambiguous"}return s}function xd(t,e,i,s,n=1,o=15){const a=function(t,e,i,s,n=1,o=15){const[a,r]=bl([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]=bl([e[0],e[1]],[e[2],e[3]]);if(Math.abs(a*o-r*i)>.05)continue;const d=Pl(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]);u0?l:o}(t,e,i,s,n,o);return Fl(t,[e[0],e[1]],[e[2],e[3]],a,s,n)}function $d(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(!(Ls(n,o)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Ls([o[0],o[1]],t)>i))for(const e of[[o[0],o[1]],[o[2],o[3]]])Ls(e,t)<=2*i&&s.push(e);return s}function Sd(t,e,i,s){const n=pd(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=hd(t,i),n=e[0],l=e[1],c=e[2]-n,h=e[3]-l,d=Math.hypot(c,h);if(d4*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)),g=Math.min(d,Math.max(a,c));if(g-ht.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])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(cd(e,n,i))}}return h}function Md(t,e,i,s){const n=pd(t);if(!n.length)return[];if(e.length!==i.length)return n;const o=[];for(const t of n){const n=hd(t,s),a=[n[0],n[1]],r=[n[2],n[3]];let l=a,c=r;for(let t=0;t{const e=ud(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(ud([(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(cd(l,c,s))}return o}const Cd=15e3,Dd=2e3,Pd=new WeakMap;function Td(t,e){let i=Pd.get(t);if(!i){const e=new Set;i={hiddenAt:"hidden"===t.visibilityState?Date.now():0,token:0,subscribers:e,onVisibility:()=>{const e=Pd.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>=Cd};for(const t of[...e.subscribers])t(n)},onPageShow:e=>{const i=Pd.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)}},Pd.set(t,i),t.addEventListener("visibilitychange",i.onVisibility),t.defaultView?.addEventListener("pageshow",i.onPageShow)}return i.subscribers.add(e),()=>{const i=Pd.get(t);i&&(i.subscribers.delete(e),i.subscribers.size||(t.removeEventListener("visibilitychange",i.onVisibility),t.defaultView?.removeEventListener("pageshow",i.onPageShow),Pd.delete(t)))}}const Rd={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 Id{constructor(t,e=Rd){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}),Dd)}),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 Ad(t){let e=2166136261,i=2654435769;const s=new WeakSet,n=t=>{for(let s=0;s>>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 zd(t){return Ad(t)}const Fd=new WeakMap;class Ed{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=Fd.get(t);return e||(e={signed:{},queued:new Set,inFlight:new Map,retry:new Map,listeners:new Set,settlers:new Set,references:new Map},Fd.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{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{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),Dd);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.atthis.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),Dd);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),Dd);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!!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.att.id===e)}function Od(t="file"){const e="draw"===t;return{source:t,showBorders:e,showNames:e,displayTouched:!1}}function Ld(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 Bd(t,e,i){return[t[0]*e+t[1]*i+t[2],t[3]*e+t[4]*i+t[5]]}function qd(t,e){let i=0;for(const[s,n]of e){const e=Bd(t,s[0],s[1]);i=Math.max(i,Math.hypot(e[0]-n[0],e[1]-n[1]))}return i}const Wd=t=>{if(null==t||""===t)return null;const e=Number(t);return Number.isFinite(e)?e:null},jd=t=>{const e=t,i=Wd(Array.isArray(e)?e[0]:e?.x),s=Wd(Array.isArray(e)?e[1]:e?.y);return null==i||null==s?null:[i,s]};function Gd(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=jd(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;tt+e[0],0)/e.length,e.reduce((t,e)=>t+e[1],0)/e.length]:[s/(3*i),n/(3*i)]}function Ud(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=jd(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=Gd(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 Vd(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(jd).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;tfunction(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;st.slice()),i=e[e.length-1];return i?(i.length>2?i.pop():e.pop(),e):[]}function Jd(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=Wd(e.x)&&null!=Wd(e.y)?{x:Wd(e.x),y:Wd(e.y),a:Wd(e.a??e.angle??e.theta)}:null,s=Vd(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=[[Wd(e.cx),Wd(e.cy)],[Wd(e.center?.x),Wd(e.center?.y)],[Wd(e.x),Wd(e.y)]].find(([t,e])=>null!=t&&null!=e);let o=s?.[0]??null,a=s?.[1]??null;const r=Ud(e.outline),l=Wd(e.x0),c=Wd(e.y0),h=Wd(e.x1),d=Wd(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 Xd(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=!!jd(s.vacuum_position??s.robot_position),r=!(!s.rooms||"object"!=typeof s.rooms),l=Vd(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 Zd=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function Qd(t,e){const i=new Map(e.map(t=>[Zd(t.name),t])),s=[],n=[];for(const e of t){const t=i.get(Zd(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,_],[g,m]]of t){if(![u,_,g,m].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,n+=_*_,o+=_,a+=1,r+=u*g,l+=_*g,c+=g,h+=u*m,d+=_*m,p+=m}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]]},g=_([r,l,c]),m=_([h,d,p]);if(!g||!m)return null;const f=[g[0],g[1],g[2],m[0],m[1],m[2]];return f.every(Number.isFinite)?f:null}(s);return o?{matrix:o,matched:n,residual:qd(o,s)}:null}function tp(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;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([n,_],[_,o]))}const n=[];for(let e=0;e600&&(n=n.filter((t,e)=>e%2==0||e===n.length-1)),n}function ep(t){return"cleaning"===t||"returning"===t||"on"===t}const ip={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function sp(t){const[e,i]=ip[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 np(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 op(t,e,i,s){const[n,o]=Bd(sp(e),i,s),a=sp({...t,ox:0,oy:0}),[r,l]=Bd(a,i,s);return{...t,ox:n-r,oy:o-l}}function ap(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}const rp={offStates:["off"],unknownUsesToggle:!0},lp={light:rp,switch:rp,fan:rp,humidifier:rp,input_boolean:rp,automation:rp,remote:rp,group:rp,climate:{...rp,featureMasks:{turn_on:256,turn_off:128,toggle:384}},media_player:{...rp,featureMasks:{turn_on:128,turn_off:256,toggle:384}},siren:{...rp,featureMasks:{turn_on:1,turn_off:2,toggle:3}},vacuum:{...rp,featureMasks:{turn_on:1,turn_off:2,toggle:3}},water_heater:{...rp,featureMasks:{turn_on:8,turn_off:8,toggle:8}},camera:{...rp,featureMasks:{turn_on:1,turn_off:1,toggle:1}}};function cp(t){return t.slice(0,t.indexOf("."))}function hp(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 dp(t,e,i){return hp(t,e,i)?{domain:e,service:i}:hp(t,"homeassistant",i)?{domain:"homeassistant",service:i}:null}function pp(t,e){return t?.entities?.[e]||null}function up(t,e,i){const s=t?.states?.[i],n=pp(e,i);return s?.attributes?.friendly_name||n?.name||n?.original_name||i}function _p(t,e){const i=pp(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 gp(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=cp(i);if("lock"===s||"alarm_control_panel"===s)return!0;if("cover"!==s)return!1;const n=t?.states?.[i],o=pp(e,i),a=String(n?.attributes?.device_class||o?.device_class||o?.original_device_class||"");return Gi.has(a)}function fp(t,e,i,s,n){return{ref:i,entityId:s,name:s?up(t,e,s):null,reason:n}}function vp(t,e,i,s,n,o=null){return{target:null,skipped:fp(t,e,i,s,n),semantics:o,nextEffect:null,command:null}}function yp(t,e,i,s,n=i){const o=cp(i);if("cover"!==o&&"valve"!==o)return function(t,e,i,s,n=i){const o=cp(i),a=t?.states?.[i];if(_p(e,i))return vp(t,e,n,i,"ha-disabled","power");if(mp(t,e,i))return vp(t,e,n,i,"secure");if(!a)return vp(t,e,n,i,"missing","power");if("unavailable"===a.state)return vp(t,e,n,i,"unavailable","power");const r=lp[o];if(!r)return vp(t,e,n,i,"unsupported");let l,c;if("unknown"===a.state||""===a.state){if(!r.unknownUsesToggle)return vp(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&&!gp(a,h))return vp(t,e,n,i,"unsupported","power");const d=dp(t,o,c);return d?{target:{entityId:i,name:up(t,e,i),state:String(a.state||""),via:s},skipped:null,semantics:"power",nextEffect:l,command:{...d,data:{entity_id:i}}}:vp(t,e,n,i,"unsupported","power")}(t,e,i,s,n);if(_p(e,i))return vp(t,e,n,i,"ha-disabled",o);if(mp(t,e,i))return vp(t,e,n,i,"secure",o);const a=t?.states?.[i];if(!a)return vp(t,e,n,i,"missing",o);if("unavailable"===a.state)return vp(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&&gp(i,n.feature)&&hp(t,e,n.service)?n:gp(i,3)&&hp(t,e,"toggle")?{service:"toggle",effect:"toggle"}:null}(t,o,a);if(!r)return vp(t,e,n,i,"unsupported",o);return{target:{entityId:i,name:up(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 bp(t){return"entity"===t.bindingKind?t.bindingRef&&Ms(t.bindingRef)?[t.bindingRef]:[]:"virtual"===t.bindingKind?[]:to(t)}function wp(t){const e=t.marker?.toggle_entity;return e&&bp(t).includes(e)?e:null}function kp(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 yp(t,e,s[0],n);let o=null,a=null;for(const i of s){const s=yp(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 xp(t){const e=t.skipped?.reason;return"missing"===e?"unavailable":e||"unsupported"}function $p(t,e){return{origin:t,kind:"none",semantics:null,targets:[],skippedTargets:[],noneReason:e,nextEffect:null,command:null}}function Sp(t,e){return"cover"===t||"toggle"===t?"toggle":"more-info"===t||"run"===t||"info"===t?t:(null==t||""===t)&&"light"===e?"toggle":"info"}function Mp(t,e){return{origin:t,kind:"single",semantics:e.semantics,targets:e.target?[e.target]:[],skippedTargets:e.skipped?[e.skipped]:[],noneReason:e.command?null:xp(e),nextEffect:e.nextEffect,command:e.command}}function Cp(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=yp(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=dp(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=>fp(t,e,i.entityId,i.entityId,"unsupported"))],noneReason:"unsupported",nextEffect:null,command:null}}function Dp(t){return String(t.marker?.id||t.id||"")}function Pp(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 Cp(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(fp(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?up(i,o,s):null),reason:"ha-disabled"})}else p.push(fp(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=Dp(s),_=c.get(r)?.controllers.find(t=>Dp(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(fp(i,o,t,null,a?"missing":"unsupported"))}return Cp(i,o,d,p)}(t,wp(i))}if("legacy-cover"===n){const t=i.entities.find(t=>t.startsWith("cover."))||i.allEntities?.find(t=>t.startsWith("cover."));return t?Mp(n,yp(e,s,t,"device-role")):$p(n,"no-actionable-entity")}const a=wp(i),r=a?yp(e,s,a,"entity"===i.bindingKind?"binding":"device-role"):kp(e,s,i);return r?Mp(n,r):$p(n,i.virtual||"virtual"===i.bindingKind?"no-actionable-entity":"no-binding")}function Tp(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 Rp(t){return t?t.operation?t.operation:t.command?{kind:"ha-service",command:t.command}:null:null}const Ip=new Set(["missing","ha-disabled","unavailable"]);function Ap(t,e){const i=Rp(t),s=Rp(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=Tp(t),s=Tp(e);return i.length===s.length&&i.every((t,e)=>t===s[e])}(i.command,s.command))}const zp=t=>!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]);function Fp(t,e=5,i=5){return ml(t.cm,e,i)/2}function Ep(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(!zp(r.a)||!zp(r.b)||!Number.isFinite(a.t)||a.t<0||a.t>1)return{resolved:null,reason:"invalid-position"};const l=r.b[0]-r.a[0],c=r.b[1]-r.a[1],h=Math.hypot(l,c),d=Number(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/2h-o+1e-9)return{resolved:null,reason:"does-not-fit"};const u=l/h,_=c/h;let g=180*Math.atan2(c,l)/Math.PI;return g>=90?g-=180:g<-90&&(g+=180),{reason:null,resolved:{opening:t,host:a,partition:r,center:[r.a[0]+l*a.t,r.a[1]+c*a.t],angle:g,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 Np(t,e,i=1,s=5,n=5){return Ep(t,e,i,s,n,0)}function Op(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 Np(t,e,i,s,n);const r=Ep(t,e,i,s,n,Fp(a,s,n));return"does-not-fit"===r.reason?{resolved:null,reason:"does-not-fit-jamb"}:r}function Lp(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 Bp(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 qp(t,e,i){return t.flatMap(t=>!zp(t.a)||!zp(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 Wp(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 jp(t,e,i){return{...t,x:e.center[0]/i,y:e.center[1]/i,angle:e.angle}}const Gp=.001;function Up(t){return!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function Vp(t){return`${t[0].toFixed(6)},${t[1].toFixed(6)}`}function Kp(t,e,i){return Math.abs(t[0]-e[0])"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]=Yp(t.a,t.b);return{a:e,b:i,key:`hidden|${Xp(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|${Jp(t)}|endpoint-${i}`,sourceKind:t.kind,sourceId:t.id}))).sort((t,e)=>t.key.localeCompare(e.key));return{segments:n,endpoints:o}}function eu(t){const e=t.epsilon??Gp,i=t.roomCuts||[],s=new Map;for(const e of t.partitionCuts||[]){if("string"!=typeof e.hostId||!e.hostId||!Up(e.a)||!Up(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+1Qp(s,t,e)))continue;const t=Vp(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 iu(t,e,i){return e.some(e=>Up(e)&&Kp(t,e,i))}function su(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 nu(t,e,i){const s=i.epsilon??Gp,n=i.excludePoints||[],o=[...t.endpoints,...(i.extraEndpoints||[]).filter(t=>Up(t.point)).map(t=>({point:[t.point[0],t.point[1]],key:t.key}))],a=new Map;for(const t of o){if(iu(t.point,n,s))continue;const e=Vp(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 ou(t,e){if(!t.length)return null;if(e>0){const i=new Set;for(let s=0;s1)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 au(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 ru(t,e,i){if(!(Up(e)&&Up(i.anchor)&&i.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const s=i.epsilon??Gp,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=nu(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=ou(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||iu(d,i.excludePoints||[],s)||!su(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 lu=.001;function cu(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=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function pu(t,e,i,s){return t*s-e*i}function uu(t){let e=0;for(let i=0;it.localeCompare(e)),e[0]}function vu(t,e,i){const s=[...t];for(let t=!0;t&&s.length>=3;){t=!1;for(let n=0;n=0){s.splice(n,1),t=!0;break}}}return s}function yu(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 bu(t,e){return[t.a[0]+(t.b[0]-t.a[0])*e,t.a[1]+(t.b[1]-t.a[1])*e]}function wu(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=bu(e,s);return Math.hypot(n[0]-t[0],n[1]-t[1])<=i?Math.max(0,Math.min(1,s)):null}function ku(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=pu(o,a,r,l),p=Math.max(Math.hypot(o,a),Math.hypot(r,l),1);if(Math.abs(d)>n*p){const t=pu(c,h,r,l)/d,e=pu(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(pu(c,h,o,a))>n*p)){for(const s of[e.a,e.b]){const e=wu(s,t,n);null!=e&&i.push(e)}for(const i of[t.a,t.b]){const t=wu(i,e,n);null!=t&&s.push(t)}}}function xu(t){let e=t+1|0;return e^=e<<13,e^=e>>>17,e^=e<<5,e>>>0}function $u(t,e){return t.minY-e.minY||t.index-e.index}function Su(t){return t.subtreeMaxY=Math.max(t.item.maxY,t.left?.subtreeMaxY??-1/0,t.right?.subtreeMaxY??-1/0),t}function Mu(t){const e=t.right;return t.right=e.left,e.left=Su(t),Su(e)}function Cu(t){const e=t.left;return t.left=e.right,e.right=Su(t),Su(e)}function Du(t,e){return t?($u(e,t.item)<0?(t.left=Du(t.left,e),t.left.priority0)t.right=Pu(t.right,e);else{if(!t.left)return t.right;if(!t.right)return t.left;t.left.priority=e&&Tu(t.left,e,i,s),t.item.minY<=i&&t.item.maxY>=e&&s.push(t.item),t.item.minY<=i&&Tu(t.right,e,i,s))}function Ru(t,e=.001){const i=Number.isFinite(e)&&e>0?e:lu,s=t.filter(t=>du(t.a)&&du(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(;lt.index-e.index);for(const o of n)ku(t[o.index],t[s.index],e[o.index],e[s.index],i);a.add(s.index),r=Du(r,s)}}(s,n,i);const o=new Map;for(let t=0;t({key:t,a:e.a,b:e.b,sourceKeys:[...e.sourceKeys].sort((t,e)=>t.localeCompare(e))})).sort((t,e)=>t.key.localeCompare(e.key))}function Iu(t,e=.001){const i=Number.isFinite(e)&&e>0?e:lu,s=Ru(t,i),n=new Map,o=new Map,a=new Map(s.map(t=>[t.key,t]));for(const t of s){const e=gu(t.a,i),s=gu(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=[gu(t.a,i),gu(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 g=e.map(t=>n.get(t)),m=uu(g);if(!(m>i*i))continue;const f=fu(vu(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:g,key:f,area:m,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 Au(t,e,i=.001){if(!du(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;ie[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 zu(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 Fu(t,e,i=.5){let s=Math.abs(t-e)%180;return s>90&&(s=180-s),s<=i}function Eu(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)&&(Fu(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)&&zu(e.a,t.a,t.b)<=i&&zu(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:Fu(e.angle,i)?zu([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 Nu(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 Ou=t=>!t.key.startsWith("static:room|");function Lu(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 Bu(t,e,i){if(Ou(t.source)!==Ou(e.source))return Ou(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 qu(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 Wu(t,e){return t.map(t=>t.key!==e.sourceKey?t:{...t,[e.endpoint]:[...e.to]})}function ju(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;ti)||l>e.maxDistance)continue;const c=Bu(a,r,e.gridStep||0),h=c===a?r:a;if(!Ou(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(qu(d),d)}for(const o of n)if(Ou(o.source))for(const n of t){if(n===o.source)continue;const t=Lu(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(qu(a),a)}const o=[];for(const n of s.values()){const s=Iu(Wu(t,n),i),a=e.point?Au(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||qu(t.proposal).localeCompare(qu(e.proposal))),!o.length)return{kind:"none"};return new Set(o.map(t=>`${qu(t.proposal)}|${t.face.key}`)).size>1?{kind:"ambiguous",proposals:o.map(t=>t.proposal)}:{kind:"repair",...o[0]}}const Gu=1e-5;function Uu(t){const e=t.map(()=>[]);for(let i=0;i=1-1e-9||p<=1e-9||p>=1-1e-9||(e[i].push(d),e[a].push(p))}}const i=[];for(let s=0;st-e),1];for(let t=1;t{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))},Yu=(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 Ju(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=Ku(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;tt-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=Yu(t,s,o,e);i=3?l:[]}class Xu 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``}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 Lr(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 Hr(t){const e=Lr(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,Ur=1e-4;function Gr(t){if(Array.isArray(t))return t.map(t=>Gr(t));if(null!==t&&"object"==typeof t){const e={};for(const[i,s]of Object.entries(t))e[i]=Gr(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)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{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{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 e of Xr(t.wall_segments))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