From 591f8f6ac90e337c7cd86208e8caa4e1de083bc5 Mon Sep 17 00:00:00 2001 From: Sergey Matyunin Date: Sat, 29 Aug 2026 22:10:23 +0300 Subject: [PATCH] Add selectable value face sources Issue: #378 User-Visible: yes --- .../houseplan/frontend/houseplan-assets.json | 112 +++---- ...-DFFLwJ_d.js => backdrop-pick-TMl_2iBT.js} | 2 +- .../frontend/houseplan-assets/de-B775TcPH.js | 1 + .../frontend/houseplan-assets/de-FazIOsg8.js | 1 - .../houseplan-assets/editor-CIkons5x.js | 10 +- .../frontend/houseplan-assets/fr-CFpxbPTS.js | 1 - .../frontend/houseplan-assets/fr-Cml_YfSz.js | 1 + ...B5mDyjuC.js => houseplan-card-wtxKVHkp.js} | 86 +++--- ...s => houseplan-editor-runtime-CM78ZHZU.js} | 279 ++++++++++-------- ... houseplan-onboarding-runtime-BP9ODa6H.js} | 2 +- .../houseplan/frontend/houseplan-card.js | 2 +- custom_components/houseplan/import_export.py | 54 +++- custom_components/houseplan/validation.py | 84 +++--- demo/smoke_value_face_source.mjs | 167 +++++++++++ dist/houseplan-assets.json | 112 +++---- ...-DFFLwJ_d.js => backdrop-pick-TMl_2iBT.js} | 2 +- dist/houseplan-assets/de-B775TcPH.js | 1 + dist/houseplan-assets/de-FazIOsg8.js | 1 - .../houseplan-assets/editor-CIkons5x.js | 10 +- dist/houseplan-assets/fr-CFpxbPTS.js | 1 - dist/houseplan-assets/fr-Cml_YfSz.js | 1 + ...B5mDyjuC.js => houseplan-card-wtxKVHkp.js} | 86 +++--- ...s => houseplan-editor-runtime-CM78ZHZU.js} | 279 ++++++++++-------- ... houseplan-onboarding-runtime-BP9ODa6H.js} | 2 +- dist/houseplan-card.js | 2 +- docs/ARCHITECTURE.md | 17 +- docs/CHANGELOG.md | 7 + docs/CHANGELOG.ru.md | 7 + docs/CONFIG-COMPATIBILITY.md | 11 + docs/DEVICE-PRESENTATION.md | 3 +- docs/USER-GUIDE.md | 6 + docs/USER-GUIDE.ru.md | 11 +- scripts/model-invariants.mjs | 11 + scripts/smoke-links.mjs | 11 + src/device-presentation.ts | 105 +++++-- src/device-value-badge.ts | 84 +++++- src/devices.ts | 7 +- src/houseplan-card.ts | 4 + src/houseplan-editor-runtime.ts | 74 ++++- src/i18n/de.json | 7 +- src/i18n/en.json | 7 +- src/i18n/fr.json | 7 +- src/i18n/ru.json | 7 +- src/types.ts | 3 + test/device-presentation-policy.test.mjs | 10 + test/device-presentation.test.mjs | 135 ++++++++- test/devices.test.mjs | 7 + .../device-presentation-decisions.mjs | 1 + test/native-select-contract.test.mjs | 2 +- tests_backend/test_ha_import_export.py | 62 ++++ tests_backend/test_validation.py | 72 +++++ 51 files changed, 1415 insertions(+), 562 deletions(-) rename custom_components/houseplan/frontend/houseplan-assets/{backdrop-pick-DFFLwJ_d.js => backdrop-pick-TMl_2iBT.js} (97%) create mode 100644 custom_components/houseplan/frontend/houseplan-assets/de-B775TcPH.js delete mode 100644 custom_components/houseplan/frontend/houseplan-assets/de-FazIOsg8.js rename dist/houseplan-assets/editor-CcxmUFV-.js => custom_components/houseplan/frontend/houseplan-assets/editor-CIkons5x.js (84%) delete mode 100644 custom_components/houseplan/frontend/houseplan-assets/fr-CFpxbPTS.js create mode 100644 custom_components/houseplan/frontend/houseplan-assets/fr-Cml_YfSz.js rename custom_components/houseplan/frontend/houseplan-assets/{houseplan-card-B5mDyjuC.js => houseplan-card-wtxKVHkp.js} (75%) rename custom_components/houseplan/frontend/houseplan-assets/{houseplan-editor-runtime-CisKVaG7.js => houseplan-editor-runtime-CM78ZHZU.js} (78%) rename custom_components/houseplan/frontend/houseplan-assets/{houseplan-onboarding-runtime-eqCB-Ax0.js => houseplan-onboarding-runtime-BP9ODa6H.js} (98%) create mode 100644 demo/smoke_value_face_source.mjs rename dist/houseplan-assets/{backdrop-pick-DFFLwJ_d.js => backdrop-pick-TMl_2iBT.js} (97%) create mode 100644 dist/houseplan-assets/de-B775TcPH.js delete mode 100644 dist/houseplan-assets/de-FazIOsg8.js rename custom_components/houseplan/frontend/houseplan-assets/editor-CcxmUFV-.js => dist/houseplan-assets/editor-CIkons5x.js (84%) delete mode 100644 dist/houseplan-assets/fr-CFpxbPTS.js create mode 100644 dist/houseplan-assets/fr-Cml_YfSz.js rename dist/houseplan-assets/{houseplan-card-B5mDyjuC.js => houseplan-card-wtxKVHkp.js} (75%) rename dist/houseplan-assets/{houseplan-editor-runtime-CisKVaG7.js => houseplan-editor-runtime-CM78ZHZU.js} (78%) rename dist/houseplan-assets/{houseplan-onboarding-runtime-eqCB-Ax0.js => houseplan-onboarding-runtime-BP9ODa6H.js} (98%) diff --git a/custom_components/houseplan/frontend/houseplan-assets.json b/custom_components/houseplan/frontend/houseplan-assets.json index 44ba61cf..ddfb0b2b 100644 --- a/custom_components/houseplan/frontend/houseplan-assets.json +++ b/custom_components/houseplan/frontend/houseplan-assets.json @@ -1,125 +1,125 @@ { "schema": 1, - "fingerprint": "b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c", + "fingerprint": "3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f", "entry": "houseplan-card.js", "initialViewFiles": [ - "houseplan-assets/houseplan-card-B5mDyjuC.js", + "houseplan-assets/houseplan-card-wtxKVHkp.js", "houseplan-card.js" ], - "initialViewGzipBytes": 277319, + "initialViewGzipBytes": 277948, "lazyFiles": [ - "houseplan-assets/backdrop-pick-DFFLwJ_d.js", - "houseplan-assets/de-FazIOsg8.js", - "houseplan-assets/editor-CcxmUFV-.js", - "houseplan-assets/fr-CFpxbPTS.js", - "houseplan-assets/houseplan-editor-runtime-CisKVaG7.js", - "houseplan-assets/houseplan-onboarding-runtime-eqCB-Ax0.js" + "houseplan-assets/backdrop-pick-TMl_2iBT.js", + "houseplan-assets/de-B775TcPH.js", + "houseplan-assets/editor-CIkons5x.js", + "houseplan-assets/fr-Cml_YfSz.js", + "houseplan-assets/houseplan-editor-runtime-CM78ZHZU.js", + "houseplan-assets/houseplan-onboarding-runtime-BP9ODa6H.js" ], - "lazyGzipBytes": 191754, + "lazyGzipBytes": 192277, "lazyEditorFiles": [ - "houseplan-assets/backdrop-pick-DFFLwJ_d.js", - "houseplan-assets/editor-CcxmUFV-.js", - "houseplan-assets/houseplan-editor-runtime-CisKVaG7.js" + "houseplan-assets/backdrop-pick-TMl_2iBT.js", + "houseplan-assets/editor-CIkons5x.js", + "houseplan-assets/houseplan-editor-runtime-CM78ZHZU.js" ], - "lazyEditorGzipBytes": 139917, + "lazyEditorGzipBytes": 140230, "lazyOnboardingFiles": [ - "houseplan-assets/backdrop-pick-DFFLwJ_d.js", - "houseplan-assets/houseplan-onboarding-runtime-eqCB-Ax0.js" + "houseplan-assets/backdrop-pick-TMl_2iBT.js", + "houseplan-assets/houseplan-onboarding-runtime-BP9ODa6H.js" ], "lazyOnboardingGzipBytes": 9451, "lazyLocaleFiles": [ - "houseplan-assets/de-FazIOsg8.js", - "houseplan-assets/fr-CFpxbPTS.js" + "houseplan-assets/de-B775TcPH.js", + "houseplan-assets/fr-Cml_YfSz.js" ], - "lazyLocaleGzipBytes": 45342, + "lazyLocaleGzipBytes": 45551, "files": [ { - "path": "houseplan-assets/backdrop-pick-DFFLwJ_d.js", - "sha256": "f7bdc0ee64684220a751eadda1397edb922b9c785993f5bce1d6b8273c619f21", + "path": "houseplan-assets/backdrop-pick-TMl_2iBT.js", + "sha256": "2527114d8c2e58532848ac449cb7ea6927b86f5883bad84c649dc740c3872eea", "rawBytes": 6967, - "gzipBytes": 2956, + "gzipBytes": 2955, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-B5mDyjuC.js" + "houseplan-assets/houseplan-card-wtxKVHkp.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/de-FazIOsg8.js", - "sha256": "50ca05e9607b7f597cd20b1a1fce1f92fd9bb07a4cb0db4e86c3c921053c9e89", - "rawBytes": 75090, - "gzipBytes": 22886, + "path": "houseplan-assets/de-B775TcPH.js", + "sha256": "287e8f70aad3d82cfe292f204e5fe58eef3738631ee3be7078de6fd88c5fde1f", + "rawBytes": 75572, + "gzipBytes": 22996, "isEntry": false, "imports": [], "dynamicImports": [] }, { - "path": "houseplan-assets/editor-CcxmUFV-.js", - "sha256": "27077f8f6ecd61eea9e8405099b91974f423b8f7a53eef9ae8cf066803c475ef", + "path": "houseplan-assets/editor-CIkons5x.js", + "sha256": "ecaeb979d94de7e54f08b090355bf757b64a34932cddc9442f023804565ff56c", "rawBytes": 3831, "gzipBytes": 1583, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-B5mDyjuC.js" + "houseplan-assets/houseplan-card-wtxKVHkp.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/fr-CFpxbPTS.js", - "sha256": "ce2f711435309dac25e14c0024eea6da2c443949728a0fad1b5062851fcc26d6", - "rawBytes": 77225, - "gzipBytes": 22456, + "path": "houseplan-assets/fr-Cml_YfSz.js", + "sha256": "eeaa5abe3d199602537bab6f7f40075ceab398a25515685dd5e465d3ea9c9b51", + "rawBytes": 77710, + "gzipBytes": 22555, "isEntry": false, "imports": [], "dynamicImports": [] }, { - "path": "houseplan-assets/houseplan-card-B5mDyjuC.js", - "sha256": "d2f3c9df28d6acc99e4c7960c6f3cf1d5b516b42c3b10102429ba263532f569a", - "rawBytes": 976343, - "gzipBytes": 276523, + "path": "houseplan-assets/houseplan-card-wtxKVHkp.js", + "sha256": "796b979c90d4791cbdadb991c407dbd7d6768880b5a856cc1b7ec19f1e317c7a", + "rawBytes": 979430, + "gzipBytes": 277152, "isEntry": false, "imports": [], "dynamicImports": [ - "houseplan-assets/de-FazIOsg8.js", - "houseplan-assets/editor-CcxmUFV-.js", - "houseplan-assets/fr-CFpxbPTS.js", - "houseplan-assets/houseplan-editor-runtime-CisKVaG7.js", - "houseplan-assets/houseplan-onboarding-runtime-eqCB-Ax0.js" + "houseplan-assets/de-B775TcPH.js", + "houseplan-assets/editor-CIkons5x.js", + "houseplan-assets/fr-Cml_YfSz.js", + "houseplan-assets/houseplan-editor-runtime-CM78ZHZU.js", + "houseplan-assets/houseplan-onboarding-runtime-BP9ODa6H.js" ] }, { - "path": "houseplan-assets/houseplan-editor-runtime-CisKVaG7.js", - "sha256": "d99951a97b78d46d05501d0e2e3f5a9cd73a1088a5e9a382d8899903b040bb97", - "rawBytes": 524211, - "gzipBytes": 135378, + "path": "houseplan-assets/houseplan-editor-runtime-CM78ZHZU.js", + "sha256": "e439a389b8556a98b6ee0641cbda9a47978b2a10e4279d6663848ec97005771e", + "rawBytes": 526486, + "gzipBytes": 135692, "isEntry": false, "imports": [ - "houseplan-assets/backdrop-pick-DFFLwJ_d.js", - "houseplan-assets/houseplan-card-B5mDyjuC.js" + "houseplan-assets/backdrop-pick-TMl_2iBT.js", + "houseplan-assets/houseplan-card-wtxKVHkp.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/houseplan-onboarding-runtime-eqCB-Ax0.js", - "sha256": "aba9dab2a60c386b0638636d2ebcc683bd44a56fea5ce594fda9456ee171371d", + "path": "houseplan-assets/houseplan-onboarding-runtime-BP9ODa6H.js", + "sha256": "d49d4e5b08de22682d8c548ffa90bd73175fc1d069eca75f98905b9a804cafb0", "rawBytes": 26195, - "gzipBytes": 6495, + "gzipBytes": 6496, "isEntry": false, "imports": [ - "houseplan-assets/backdrop-pick-DFFLwJ_d.js", - "houseplan-assets/houseplan-card-B5mDyjuC.js" + "houseplan-assets/backdrop-pick-TMl_2iBT.js", + "houseplan-assets/houseplan-card-wtxKVHkp.js" ], "dynamicImports": [] }, { "path": "houseplan-card.js", - "sha256": "376b5951ff76686ebc299f63f2a21bf56f331e1027c52eabd487664fc86cd45e", + "sha256": "785c3ec627b80c3cda8831faaa08f31c7021ad47b0be372d3326ce5456d7d087", "rawBytes": 1183, "gzipBytes": 796, "isEntry": true, "imports": [ - "houseplan-assets/houseplan-card-B5mDyjuC.js" + "houseplan-assets/houseplan-card-wtxKVHkp.js" ], "dynamicImports": [] } diff --git a/custom_components/houseplan/frontend/houseplan-assets/backdrop-pick-DFFLwJ_d.js b/custom_components/houseplan/frontend/houseplan-assets/backdrop-pick-TMl_2iBT.js similarity index 97% rename from custom_components/houseplan/frontend/houseplan-assets/backdrop-pick-DFFLwJ_d.js rename to custom_components/houseplan/frontend/houseplan-assets/backdrop-pick-TMl_2iBT.js index 642dc6d9..77809655 100644 --- a/custom_components/houseplan/frontend/houseplan-assets/backdrop-pick-DFFLwJ_d.js +++ b/custom_components/houseplan/frontend/houseplan-assets/backdrop-pick-TMl_2iBT.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";import{b as e}from"./houseplan-card-B5mDyjuC.js";function n(e,n){return{id:e,title:n,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function t(e="file"){const n="draw"===e;return{source:e,showBorders:n,showNames:n,displayTouched:!1}}function r(e,n){if(e.displayTouched)return{...e,source:n};const t="draw"===n;return{...e,source:n,showBorders:t,showNames:t}}function a(e,n,t){return{...e,[n]:t,displayTouched:!0}}function o(e,n,t){const r=(e?.spaces||[]).find(e=>e?.id===t),a=new Set((r?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),o=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===t||"string"==typeof e.room_id&&a.has(e.room_id)||n?.[e.id]?.s===t).map(e=>e.id))].sort((e,n)=>e.localeCompare(n));return{markerIds:o,count:o.length}}const i=1e6,l=()=>({kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}),d=(e,n,t)=>{if(!Number.isInteger(e)||!Number.isInteger(n)||e<=0||n<=0||e>i||n>i)return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};const r=e*n*4;return{kind:Math.max(e,n)>16384?"hard":r>134217728?"warn":"safe",width:e,height:n,alpha:t,decodedBytes:r}},s=(e,n)=>(e[n]<<24|e[n+1]<<16|e[n+2]<<8|e[n+3])>>>0,u=(e,n)=>e[n]<<8|e[n+1],c=(e,n)=>e[n]|e[n+1]<<8|e[n+2]<<16,h=(e,n)=>e[n]|e[n+1]<<8,f=(e,n,t)=>{for(let r=0;re.length)break;n+=12+t}}return d(n,t,a)}(e):"jpg"===n?function(e){if(e.length<4||255!==e[0]||216!==e[1])return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};let n=2;for(let t=0;t<256&&n+4<=e.length;t++){if(255!==e[n]){n++;continue}const t=e[n+1];if(216===t||t>=208&&t<=217){n+=2;continue}if(t>=192&&t<=207&&196!==t&&200!==t&&204!==t)return n+9>e.length?l():d(u(e,n+7),u(e,n+5),!1);if(218===t)return l();const r=u(e,n+2);if(r<2)return l();n+=2+r}return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}(e):"webp"===n?function(e){if(e.length<30||!f(e,0,"RIFF")||!f(e,8,"WEBP"))return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};if(f(e,12,"VP8X")){const n=!!(16&e[20]);return d(c(e,24)+1,c(e,27)+1,n)}if(f(e,12,"VP8 "))return 157!==e[23]||1!==e[24]||42!==e[25]?{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}:d(16383&h(e,26),16383&h(e,28),!1);if(f(e,12,"VP8L")){if(47!==e[20])return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};const n=e[21]|e[22]<<8|e[23]<<16|e[24]<<24;return d(1+(16383&n),1+(n>>14&16383),1==(n>>28&1))}return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}(e):{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}catch{return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}}const w={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"};function p(e){return new Promise((n,t)=>{const r=new FileReader;r.onerror=()=>t(r.error||new Error("read failed")),r.onload=()=>{const e=String(r.result||""),a=e.indexOf(",");a<0?t(new Error("unexpected data url")):n(e.slice(a+1))},r.readAsDataURL(e)})}async function b(e,n=1.414){const t=URL.createObjectURL(e);try{return await new Promise(e=>{const r=new Image;r.onload=()=>e(r.naturalWidth&&r.naturalHeight?r.naturalWidth/r.naturalHeight:n),r.onerror=()=>e(n),r.src=t})}finally{URL.revokeObjectURL(t)}}async function k(e,n,t){const[r,a]=await Promise.all([p(e),b(e)]);return{ext:n,b64:r,aspect:a,name:t}}async function m(e){const n=function(e){return w[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"")}(e);if(!n)return{kind:"reject"};if("svg"===n)return{kind:"pass",ext:n};const t=g(new Uint8Array(await e.arrayBuffer()),n);return"safe"===t.kind?{kind:"pass",ext:n}:{kind:"guard",state:{file:e,ext:n,probe:t,busy:!1}}}async function y(e){const n=createImageBitmap(e.file,{imageOrientation:"from-image"}),t=await Promise.race([n,new Promise((e,n)=>{setTimeout(()=>n(new Error("decode timeout")),globalThis.__HP_BACKDROP_TIMEOUT_MS??1e4)})]);try{const{width:n,height:r}=function(e,n,t=4096){const r=Math.min(1,t/Math.max(e,n));return{width:Math.max(1,Math.round(e*r)),height:Math.max(1,Math.round(n*r))}}(t.width,t.height,4096),a=e.probe.alpha,o=a?"image/png":"image/jpeg";let i;if("function"==typeof OffscreenCanvas){const e=new OffscreenCanvas(n,r),a=e.getContext("2d");if(!a)throw new Error("no 2d context");a.drawImage(t,0,0,n,r),i=await e.convertToBlob({type:o,quality:.9})}else{const e=document.createElement("canvas");e.width=n,e.height=r;const a=e.getContext("2d");if(!a)throw new Error("no 2d context");a.drawImage(t,0,0,n,r),i=await new Promise((n,t)=>{e.toBlob(e=>e?n(e):t(new Error("encode failed")),o,.9)})}const l=a?"png":"jpg";return{blob:i,ext:l,name:`${e.file.name.replace(/\.[^.]+$/,"")||"plan"}-reduced.${l}`}}finally{t.close()}}const _=e=>(e/1048576).toFixed(e>=10485760?0:1);function B(n,t,r,a){const o=n._backdropGuard;if(!o)return null;const{probe:i}=o,l="hard"===i.kind,d=l?n._t("backdrop.too_large_body",{w:i.width??0,h:i.height??0,limit:16384}):"unknown"===i.kind?n._t("backdrop.unknown_body"):n._t("backdrop.large_body",{w:i.width??0,h:i.height??0,fileMb:_(o.file.size),decodedMb:_(i.decodedBytes??0)}),s=()=>{n._backdropGuard?.busy||r()},u=()=>n._backdropGuard?.file===o.file;return e`e?.id===t),a=new Set((r?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),o=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===t||"string"==typeof e.room_id&&a.has(e.room_id)||n?.[e.id]?.s===t).map(e=>e.id))].sort((e,n)=>e.localeCompare(n));return{markerIds:o,count:o.length}}const i=1e6,l=()=>({kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}),d=(e,n,t)=>{if(!Number.isInteger(e)||!Number.isInteger(n)||e<=0||n<=0||e>i||n>i)return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};const r=e*n*4;return{kind:Math.max(e,n)>16384?"hard":r>134217728?"warn":"safe",width:e,height:n,alpha:t,decodedBytes:r}},s=(e,n)=>(e[n]<<24|e[n+1]<<16|e[n+2]<<8|e[n+3])>>>0,u=(e,n)=>e[n]<<8|e[n+1],c=(e,n)=>e[n]|e[n+1]<<8|e[n+2]<<16,h=(e,n)=>e[n]|e[n+1]<<8,f=(e,n,t)=>{for(let r=0;re.length)break;n+=12+t}}return d(n,t,a)}(e):"jpg"===n?function(e){if(e.length<4||255!==e[0]||216!==e[1])return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};let n=2;for(let t=0;t<256&&n+4<=e.length;t++){if(255!==e[n]){n++;continue}const t=e[n+1];if(216===t||t>=208&&t<=217){n+=2;continue}if(t>=192&&t<=207&&196!==t&&200!==t&&204!==t)return n+9>e.length?l():d(u(e,n+7),u(e,n+5),!1);if(218===t)return l();const r=u(e,n+2);if(r<2)return l();n+=2+r}return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}(e):"webp"===n?function(e){if(e.length<30||!f(e,0,"RIFF")||!f(e,8,"WEBP"))return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};if(f(e,12,"VP8X")){const n=!!(16&e[20]);return d(c(e,24)+1,c(e,27)+1,n)}if(f(e,12,"VP8 "))return 157!==e[23]||1!==e[24]||42!==e[25]?{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}:d(16383&h(e,26),16383&h(e,28),!1);if(f(e,12,"VP8L")){if(47!==e[20])return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};const n=e[21]|e[22]<<8|e[23]<<16|e[24]<<24;return d(1+(16383&n),1+(n>>14&16383),1==(n>>28&1))}return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}(e):{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}catch{return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}}const w={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"};function p(e){return new Promise((n,t)=>{const r=new FileReader;r.onerror=()=>t(r.error||new Error("read failed")),r.onload=()=>{const e=String(r.result||""),a=e.indexOf(",");a<0?t(new Error("unexpected data url")):n(e.slice(a+1))},r.readAsDataURL(e)})}async function b(e,n=1.414){const t=URL.createObjectURL(e);try{return await new Promise(e=>{const r=new Image;r.onload=()=>e(r.naturalWidth&&r.naturalHeight?r.naturalWidth/r.naturalHeight:n),r.onerror=()=>e(n),r.src=t})}finally{URL.revokeObjectURL(t)}}async function k(e,n,t){const[r,a]=await Promise.all([p(e),b(e)]);return{ext:n,b64:r,aspect:a,name:t}}async function m(e){const n=function(e){return w[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"")}(e);if(!n)return{kind:"reject"};if("svg"===n)return{kind:"pass",ext:n};const t=g(new Uint8Array(await e.arrayBuffer()),n);return"safe"===t.kind?{kind:"pass",ext:n}:{kind:"guard",state:{file:e,ext:n,probe:t,busy:!1}}}async function y(e){const n=createImageBitmap(e.file,{imageOrientation:"from-image"}),t=await Promise.race([n,new Promise((e,n)=>{setTimeout(()=>n(new Error("decode timeout")),globalThis.__HP_BACKDROP_TIMEOUT_MS??1e4)})]);try{const{width:n,height:r}=function(e,n,t=4096){const r=Math.min(1,t/Math.max(e,n));return{width:Math.max(1,Math.round(e*r)),height:Math.max(1,Math.round(n*r))}}(t.width,t.height,4096),a=e.probe.alpha,o=a?"image/png":"image/jpeg";let i;if("function"==typeof OffscreenCanvas){const e=new OffscreenCanvas(n,r),a=e.getContext("2d");if(!a)throw new Error("no 2d context");a.drawImage(t,0,0,n,r),i=await e.convertToBlob({type:o,quality:.9})}else{const e=document.createElement("canvas");e.width=n,e.height=r;const a=e.getContext("2d");if(!a)throw new Error("no 2d context");a.drawImage(t,0,0,n,r),i=await new Promise((n,t)=>{e.toBlob(e=>e?n(e):t(new Error("encode failed")),o,.9)})}const l=a?"png":"jpg";return{blob:i,ext:l,name:`${e.file.name.replace(/\.[^.]+$/,"")||"plan"}-reduced.${l}`}}finally{t.close()}}const _=e=>(e/1048576).toFixed(e>=10485760?0:1);function B(n,t,r,a){const o=n._backdropGuard;if(!o)return null;const{probe:i}=o,l="hard"===i.kind,d=l?n._t("backdrop.too_large_body",{w:i.width??0,h:i.height??0,limit:16384}):"unknown"===i.kind?n._t("backdrop.unknown_body"):n._t("backdrop.large_body",{w:i.width??0,h:i.height??0,fileMb:_(o.file.size),decodedMb:_(i.decodedBytes??0)}),s=()=>{n._backdropGuard?.busy||r()},u=()=>n._backdropGuard?.file===o.file;return e`s()}>

${d}

diff --git a/custom_components/houseplan/frontend/houseplan-assets/de-B775TcPH.js b/custom_components/houseplan/frontend/houseplan-assets/de-B775TcPH.js new file mode 100644 index 00000000..061b8ebf --- /dev/null +++ b/custom_components/houseplan/frontend/houseplan-assets/de-B775TcPH.js @@ -0,0 +1 @@ +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";var e={"editor.context_actions":"Aktionen: {object}","editor.tool_options":"Werkzeugoptionen: {tool}","editor.palette":"Palette: {tool}","editor.open_group":"Werkzeuggruppe: {group}","editor.group_active":"{group} — aktiv: {item}","editor.disabled_action":"{action} ist nicht verfügbar: {reason}","editor.loading":"Editor wird geladen…","editor.loading_aria":"Der Plan-Editor wird geladen","editor.load_failed":"Der Editor konnte nicht geladen werden.","editor.refresh_advice":"Seite aktualisieren und erneut versuchen.","editor.retry_advice":"Verbindung prüfen und erneut drücken.","color_picker.title":"Farbwähler","color_picker.hue":"Farbton","color_picker.saturation":"Sättigung","color_picker.value":"Helligkeit","color_picker.hex":"Hex-Farbe","color_picker.invalid_hex":"Geben Sie eine 3- oder 6-stellige Hex-Farbe ein","btn.properties":"Eigenschaften","btn.keep_as_walls":"Als Wände behalten","btn.delete_room_keep_walls":"Raum löschen, Wände behalten","btn.delete_room_with_walls":"Raum und Wände löschen","title.markup_select":"Wände, Säulen und gespeicherte Umrisse auswählen und bearbeiten","title.markup_column":"Säule: Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","markup.partition":"Trennwand","markup.column":"Säule","markup.hint_column":"Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","history.draft_segment":"Raum-Entwurfsegment hinzufügen","history.draft_merge":"Unvollständige Raumumrisse verbinden","history.draft_segment_delete":"Raum-Entwurfssegment entfernen","history.partition_add":"Trennwand hinzufügen","history.column_add":"Säule hinzufügen","history.physical_edit":"Physisches Objekt ändern","history.physical_delete":"Physisches Objekt löschen","history.physical_move":"Physisches Objekt verschieben","history.contour_to_partitions":"Umriss in geschlossene Wände umwandeln","history.wall_chain_finish":"Wandkette fertigstellen","history.wall_face_batch":"Räume aus Wänden erstellen","toast.column_duplicate":"Eine Säule mit demselben Zentrum und äußerer Größe existiert bereits","confirm.delete_draft":"Sofort den gesamten unvollständigen Raumumriss löschen?","confirm.delete_draft_segment":"Dieses Entwurfssegment löschen? Der verbleibende Umriss könnte sich in zwei teilen.","physical.partition_properties":"Eigenschaften der Trennwand","physical.column_properties":"Eigenschaften der Säule","physical.draft_properties":"Eigenschaften des Entwurfssegments","physical.shape":"Form","physical.square":"Quadrat","physical.circle":"Kreis","physical.diameter":"Durchmesser","physical.side":"Seite","physical.rotation":"Drehwinkel","physical.length":"Länge","physical.allowed_range":"Erlaubt: {min}–{max} {unit}","physical.delete_segment":"Segment löschen","physical.delete_draft":"Gesamten Umriss löschen","physical.column_size_title":"Außenmaß der per Klick platzierten quadratischen Säule (1–150 cm).","card.title":"Hausplan","count.devices":"{n} Entwicklung","empty.no_spaces":"Noch keine Bereiche.","empty.add_first":"Ersten Bereich hinzufügen und einen Grundriss hochladen.","empty.install":"Installieren Sie die House Plan-Integration und fügen Sie sie unter „Geräte & Dienste“ hinzu.","btn.add_space":"Bereich hinzufügen","btn.cancel":"Abbrechen","btn.save":"Speichern","btn.close":"Schließen","btn.delete":"Löschen","btn.edit":"Bearbeiten","btn.open_in_ha":"In HA öffnen","btn.reset":"Zurücksetzen","btn.attach":"Anhängen…","btn.upload":"Hochladen…","btn.replace":"Ersetzen…","title.zoom_in":"Zoomen","title.zoom_out":"Herauszoomen","title.zoom_fit":"Alles anpassen","title.markup":"Raum-Markierung: Gitter, Linien, Konturen","title.configure_space":"Bereich konfigurieren","title.add_space":"Bereich hinzufügen","title.markup_add":"Wände: eine durchgehende Kette zeichnen; Shift sperrt auf 45°. Werkzeugwechsel oder Verlassen des Editors beendet die Kette und wandelt sie in unabhängige Wände","title.markup_merge":"Zusammenführen: Klicken Sie auf einen Raum und dann auf den Nachbarraum, mit dem er eine Wand teilt","title.markup_split":"Raum teilen: Klicken Sie auf den Raum und dann auf zwei Punkte an seinen Wänden","title.markup_delroom":"Raum löschen: Klicken Sie in einen Raum und bestätigen Sie","title.need_plan":"Einen Grundriss hochladen","markup.add":"Wände","markup.merge":"Zusammenführen","markup.split":"Teilen","markup.resize":"Größe ändern","title.markup_resize":"Räume vergrößern/verkleinern: Ziehen Sie einen verfügbaren horizontalen oder vertikalen Wandgriff","markup.hint_resize":"Ziehen Sie einen verfügbaren Wandgriff · die Wand stoppt an der ersten unsicheren Position · Esc bricht ab · Strg+Z — rückgängig","resize.disabled.diagonal":"Diese Wand ist geneigt; Größe ändern unterstützt nur horizontale und vertikale Wände","resize.disabled.side-angle":"Eine angrenzende Wand ist geneigt; Größe ändern erfordert, dass beide angrenzenden Wände diese Wand im rechten Winkel treffen","resize.disabled.duplicate-physical-wall":"Eine separate Trennwand, unfertige Kontur oder ein Pfeiler überlappt diese Wand; entfernen oder verschieben Sie sie, bevor Sie die Größe ändern","resize.disabled.partial-shared":"Ein angrenzender Raum nutzt nur einen Teil dieser Wand, daher kann sie nicht sicher als gemeinsame Wand verschoben werden","resize.disabled.unequal-shared":"Der angrenzende Raum nutzt andere Endpunkte oder Wandlängen, daher können die Wände nicht gemeinsam sicher bewegt werden","resize.disabled.multiple-rooms":"Das Verschieben dieser Wand würde mehr als zwei Räume betreffen","resize.disabled.thickness-conflict":"Die Wandstärke kann nicht sicher beibehalten werden","resize.disabled.opening-conflict":"Eine Öffnung an dieser Wand kann nicht sicher verschoben werden","resize.disabled.invalid-geometry":"Diese Wand kann nicht ohne Änderung der Planstruktur geändert werden","resize.limit_stopped":"Wand gestoppt: der nächste Schritt würde ein Anschlusslimit verletzen","resize.commit_failed":"Größenänderung wurde abgebrochen, da der endgültige Plan die Geometrieprüfung nicht bestanden hat","resize.preview_failed":"Größenänderung am letzten sicheren Punkt gestoppt, da die Plan-Geometrie nicht beibehalten werden konnte","markup.opening":"Öffnung","markup.delete_room":"Raum löschen","history.undo":"Rückgängig","history.redo":"Wiederholen","history.undo_named":"Rückgängig: {name} (Strg+Z)","history.redo_named":"Wiederholen: {name} (Strg+Shift+Z / Strg+Y)","history.undo_empty":"Nichts zu widerrufen","history.redo_empty":"Nichts zu wiederholen","history.undone":"Rückgängig gemacht: {name}","history.redone":"Wiederholt: {name}","history.add_room":"Raum erstellen","history.split_room":"Raum teilen","history.merge_rooms":"Räume zusammenführen","history.resize_room":"Raumgröße ändern","history.wall_thickness":"Wandstärke ändern","history.add_opening":"Tür- oder Fensteröffnung hinzufügen","history.edit_opening":"Öffnung bearbeiten","history.move_opening":"Öffnung verschieben","history.delete_opening":"Öffnung löschen","history.delete_room":"Raum löschen","history.delete_room_keep_walls":"Raum löschen, Wände behalten","history.delete_room_with_walls":"Raum und Wände löschen","history.decor_add":"Dekorobjekt hinzufügen","history.decor_edit":"Dekorobjekt bearbeiten","history.decor_move":"Dekorobjekt verschieben","history.decor_transform":"Dekorobjekt transformieren","history.decor_delete":"Dekorobjekt löschen","history.backdrop_transform":"Plan-Hintergrund transformieren","opening.new":"Neue Öffnung","opening.edit":"Öffnung","opening.door":"Tür","opening.window":"Fenster","opening.gate":"Tor","opening.passage":"Offener Durchgang","opening.passage_binding_warning":"Das Speichern entfernt den Öffnungs-/Schließsensor und das Schloss.","opening.invalid_passage_fields":"Der offene Durchgang auf dem Plan „{room}“ hat nicht unterstützte Einstellungen: {fields}.","opening.type_label":"Typ","opening.length_label":"Länge, cm","opening.contact_label":"Öffnungs-/Schließsensor","opening.lock_label":"Schloss","opening.none":"— keine —","opening.search_ph":"Suche: Name oder Entitäts-ID…","opening.invert":"Geöffnet/geschlossen invertieren","opening.flip_h":"Scharnier an der anderen Zarge","opening.flip_v":"Öffnet zur anderen Seite","opening.open":"Offen","opening.closed":"Geschlossen","opening.locked":"Verriegelt","opening.unlocked":"Entriegelt","opening.state_unknown":"nicht verfügbar","opening.no_entities":"Keine Sensoren gebunden — ein statisches Symbol auf dem Plan.","toast.opening_no_wall":"Neben eine Raumwand oder eine unabhängige Wand klicken","opening.host_partition":"Unabhängige Wand","opening.partition_orphan":"Die unabhängige Wand für diese Öffnung existiert nicht mehr","opening.partition_jamb_margin":"Mindestens {distance} zwischen der Öffnung und dem Wandende lassen","opening.rebind_partition":"An eine andere unabhängige Wand anfügen","confirm.delete_partition_openings_title":"Wand und Öffnungen löschen?","confirm.delete_partition_openings_body":"Diese Wand enthält {count} Öffnung(en). Sie werden zusammen gelöscht.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Löschen","markup.hint_points":"Punkte: {n} · Shift — 45° Schritte · Esc — Kette beenden · Ctrl+Z — Punkt rückgängig machen · Schließen eines Bereichs erstellt einen Raum","markup.hint_start":"Klicken Sie auf einen Rasterpunkt, um eine Wandkette zu starten","tip.lqi":"durchschnittliches Zigbee-Signal:","tip.area":"Bereich: {value}","info.device_header":"Gerät im Plan","info.model":"Modell","info.state":"Status","info.link":"Verbindung","info.manuals":"Handbücher","info.none":"Keine zusätzlichen Informationen","marker.new_device":"Neues Gerät","marker.name_label":"Name (im Plan angezeigt)","marker.name_ph":"Name","marker.binding_label":"An ein HA-Gerät binden","marker.binding_disabled":"in Home Assistant deaktiviert","marker.virtual_option":"Virtuelles Gerät (keine Bindung)","marker.search_ph":"Gerät/Gruppe suchen…","marker.nothing_found":"Nichts gefunden","marker.room_label":"Raum","marker.room_override":" (Platzierung überschreiben)","marker.room_choose":"— Raum auswählen —","marker.room_auto":"— nach Gerätebereich (auto) —","marker.icon_label":"Symbol","marker.icon_ph":"mdi:… (leer = automatisch)","marker.display_label":"Anzeige","display.badge":"Symbol + Zustand","display.icon_ripple":"Symbol + Zustand und Aktivität","display.static_icon":"Immer statisches Symbol","marker.display_hint":"Symbol + Zustand ändert die Platte für die Zustände Arbeit, geöffnet, Alarm und nicht verfügbar. Symbol + Zustand und Aktivität zeigt zusätzlich einen kurzen Impuls für Ereignisse und einen kontinuierlichen Impuls für laufende Arbeit, Bewegung oder Präsenz. Wert + Zustand ersetzt das Symbol durch einen eindeutigen HA-Wert. Ein statisches Symbol reagiert niemals auf Gerätezustände.","marker.display_hint_badge":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon_ripple":"Das Symbol, die dynamische Platte und der Puls zeigen einen kurzen Puls für Ereignisse und einen kontinuierlichen Puls für Arbeit, Bewegung oder Anwesenheit. Rote Alarme haben eine separate Priorität.","marker.display_hint_value":"Ein ausgewählter oder automatisch ermittelter Home Assistant-Wert ersetzt das Symbol, während die Platte weiterhin den Zustand anzeigt. Rote Alarme bleiben sichtbar.","marker.display_hint_static_icon":"Die themenabhängige Hülle und das Symbol bleiben immer gleich. Zustand, Aktivität, Nichtverfügbarkeit und Alarme verändern nicht das Erscheinungsbild.","marker.static_alarm_warning":"Dieses Gerät kann Alarme melden. Die statische Anzeige verbirgt die visuelle Alarmanzeige des Markers.","marker.preview.title":"Anzeigevorschau","marker.preview.actual":"Jetzt","marker.preview.example":"Beispiel","marker.preview.integration":"Bereitgestellt von","marker.preview.source":"Anzeigquelle","marker.preview.current_state":"Aktueller Zustand","marker.preview.result":"Auf dem Plan","marker.preview.details":"Quelldetails","marker.preview.select_source":"Ein Home-Assistant-Gerät oder eine Entität auswählen, um die tatsächliche Anzeige zu sehen.","marker.preview.unknown_provider":"Unbekannte Integration","marker.preview.virtual_provider":"House Plan · virtuelles Gerät","marker.preview.no_source":"Keine aktive Quelle","marker.preview.no_state":"Kein aktueller Zustand","marker.preview.mixed_states":"Mehrere verschiedene Zustände","marker.preview.multiple_sources":"{n}-Quellen","marker.preview.more_sources":"+{n} mehr","marker.preview.scaled":"Die Vorschau wird auf {n}% skaliert, um zu passen. Die gespeicherte Größe bleibt unverändert.","marker.preview.demo_short":"Kurze Aktivität anzeigen","marker.preview.demo_continuous":"Kontinuierliche Aktivität anzeigen","marker.preview.stop_continuous":"Kontinuierliche Aktivität stoppen","marker.preview.demo_short_notice":"Kurzes Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_continuous_notice":"Kontinuierliches Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_already_visible":"Der aktuelle Zustand zeigt bereits eine echte Aktivitäts- oder Alarmeffekt.","marker.preview.reduced_motion":"Systemweit reduzierte Bewegungen sind aktiviert, daher wird gewöhnliche Aktivität als Punkt angezeigt.","marker.preview.reason.neutral":"Neutrale dunkle Platte","marker.preview.reason.working":"Gelbe Platte: Das Gerät arbeitet jetzt","marker.preview.reason.working_activity":"Gelbe Platte und Aktivitätseffekt: das Gerät funktioniert jetzt","marker.preview.reason.open":"Orange Platte: physisch geöffnet oder entriegelt","marker.preview.reason.cover_icon_state":"Neutrale Platte; die Abdeckungseinheit steuert den angezeigten Zustand und das Symbol","marker.preview.reason.presence":"Aktivitätseffekt, während Präsenz erkannt wird","marker.preview.reason.event":"Kurzer Aktivitätseffekt nach einem erkannten Ereignis","marker.preview.reason.transition":"Aktivitätseffekt, während das Gerät sich bewegt oder den Zustand ändert","marker.preview.reason.media_neutral":"Mediengeräte bleiben im verfügbaren Zustand auf einer neutralen dunklen Platte","marker.preview.reason.unavailable":"Gedämpfte neutrale Platte: nicht verfügbar oder ausgeschaltet","marker.preview.reason.alarm":"Rote Alarmplatte; Alarmanzeige wird immer angezeigt","marker.preview.reason.live_states_disabled":"Live-Zustandsstil ist für diese Karte deaktiviert","marker.preview.reason.value_no_state":"Kein nutzbarer Zustandswert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_ambiguous_sources":"Mehrere mögliche Wertequellen; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_non_scalar":"Die Quelle lieferte keinen einfachen Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_virtual":"Ein virtuelles Gerät hat keinen Home Assistant-Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.vacuum_live_plan_only":"Echtzeitposition und -spur des Staubsaugers sind nur im vollständigen Plan verfügbar","marker.preview.reason.hidden_design_preview":"Das Gerät ist im Plan ausgeblendet; diese Designvorschau bleibt sichtbar","marker.preview.reason.composite_power_source":"Der Status stammt vom Power-Entity des Geräts; Hilfsschalter werden ignoriert","marker.preview.reason.activity_display_disabled":"Der gewählte Anzeigemodus zeigt gewöhnliche Aktivitätseffekte nicht an","marker.preview.reason.ha_disabled":"Die Home Assistant-Verbindung ist deaktiviert und wird im Plan ausgeblendet","marker.preview.reason.orphaned":"Die gespeicherte Home Assistant-Verbindung konnte nicht mehr gefunden werden","marker.preview.reason.static_icon":"Statischer Modus: Der Gerätestatus ändert das Symbol nicht","marker.activity_color":"Aktivitätspulsfarbe","marker.ripple_size":"Aktivitätspulsgröße","marker.activity_alarm_note":"Farbe und Größe betreffen nur gewöhnliche Aktivität und ändern keine roten Alarme.","marker.pulse_a11y_alarm":"Alarm","marker.pulse_a11y_event":"Kürzliches Ereignis","marker.pulse_a11y_presence":"Anwesenheit erkannt","marker.pulse_a11y_transition":"Statusänderung","marker.pulse_a11y_running":"Arbeitet","marker.state_a11y_neutral":"Standardzustand","marker.state_a11y_working":"Funktionierend","marker.state_a11y_open":"Offen","marker.state_a11y_locked":"Verschlossen","marker.state_a11y_unlocked":"Entriegelt","marker.state_a11y_alarm":"Alarm","marker.state_a11y_unavailable":"Nicht verfügbar","marker.lqi_a11y_low":"LQI {value}, schwaches Signal","marker.lqi_a11y_mid":"LQI {value}, mittleres Signal","marker.lqi_a11y_high":"LQI {value}, starkes Signal","marker.size_label":"Symbolgröße / Drehung","marker.angle_label":"Drehen","marker.model_label":"Modell","marker.model_ph":"z.B. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Beschreibung","marker.desc_ph":"Notizen, Spezifikationen…","marker.manuals_label":"Handbücher (PDF usw.)","marker.sub_device":"Gerät","marker.sub_z2m_group":"· Z2M Gruppe","marker.sub_group":"Gruppe","marker.sub_helper":"Helfer","space.new":"Neuer Bereich","space.header":"Bereich","space.title_label":"Titel","space.title_ph":"z.B. Garage","space.plan_label":"Grundriss (Hintergrund)","space.no_plan":"Kein Planbild","space.plan_alt":"Grundriss","room.new":"Neuer Raum","room.name_label":"Anzeigename","room.name_ph":"z.B. Terrasse","room.area_label":"Home Assistant Bereich (nicht zugewiesen)","room.no_area_option":"— kein Bereich —","room.default_name":"Raum","device.unnamed":"unbenannt","device.light_group":"Lichtgruppe","device.fallback":"Gerät","device.virtual":"Virtuelles Gerät","confirm.delete_room":'Raum "{name}" löschen?',"confirm.delete_room_title":'Raum "{name}" löschen?',"confirm.delete_room_body":"Festlegen, ob die ausschließlich zu diesem Raum gehörenden physischen Wände erhalten bleiben. Mit einem anderen Raum geteilte Wände bleiben immer erhalten.","confirm.remove_marker":"„{name}“ aus dem Plan löschen? Das Gerät verschwindet vollständig und trägt nicht mehr zu den Plandaten bei. Es kann später erneut hinzugefügt werden.","confirm.erase_decor":"Das Objekt „{kind}“ löschen? Diese Aktion kann im Editor rückgängig gemacht werden.","confirm.delete_space":"Bereich „{title}“ mit allen Räumen und Markierungen löschen?","space.delete_blocked":"Dieser Bereich wird noch von {n} Gerät(en) verwendet. Die Geräte zuerst in einen anderen Bereich verschieben oder löschen.","toast.pos_save_failed":"Position nicht gespeichert: {err}","toast.no_entity":"Das Gerät hat keine geeignete Entität","toast.ha_disabled_action":"Ein deaktiviertes Home Assistant-Objekt kann im Plan nicht verwendet werden.","toast.ha_disabled_show_device":"Ein in Home Assistant deaktiviertes Gerät kann im Plan nicht angezeigt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_disabled_show_entity":"Eine in Home Assistant deaktivierte Entität kann im Plan nicht angezeigt werden. Aktivieren Sie sie zuerst in Home Assistant.","toast.ha_disabled_add":"Ein deaktiviertes Home Assistant-Objekt kann nicht dem Plan hinzugefügt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_binding_unverified":"Der Status des Objekts konnte über das Home Assistant-Register nicht überprüft werden. Anzeige und Aktionen sind vorübergehend nicht verfügbar.","toast.markup_needs_server":"Markup ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde.","toast.space_order_changed":"Reihenfolge geändert. Wenn eine Karte ihr Stockwerk nach Nummer festlegt, überprüfen Sie diese Paneele.","toast.conflict":"Die Konfiguration wurde in einem anderen Fenster geändert — Daten aktualisiert, wiederholen Sie Ihre letzte Aktion.","toast.cfg_save_failed":"Speichern der Konfiguration fehlgeschlagen: {err}","toast.room_overlap":"Die Umrisse überlappen den Raum „{name}“ — Räume dürfen sich nicht überlappen","toast.contour_cannot_close":"Die Kontur kann nicht geschlossen werden, da sie entartet ist oder sich selbst schneidet","toast.merge_not_adjacent":"Es können nur Räume zusammengeführt werden, die eine Wand teilen","toast.rooms_merged":"Räume zu „{name}“ zusammengeführt","toast.split_pick_wall":"Beginnen Sie den Schnitt an der Wand des Raumes","toast.split_bad_cut":"Der Schnitt muss wand-zu-wand innerhalb des Raumes verlaufen, ohne Wände oder sich selbst zu kreuzen","merge.header":"Räume zusammenführen","merge.hint":"Der zusammengeführte Raum behält einen Namen und eine Fläche. Die andere Fläche wird freigegeben — ihre Geräte verlassen den Plan, bis ein anderer Raum sie beansprucht.","merge.keep":"Behalten","merge.no_area":"keine Fläche","toast.room_saved":"Raum gespeichert ({n}). Hinzugefügte Geräte: {added}. Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.room_saved_no_area":"Raum gespeichert ({n}, keine Fläche). Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.marker_needs_server":"Die Gerätebearbeitung ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde","toast.virtual_name_required":"Einen Namen für das virtuelle Gerät eingeben","toast.marker_saved":"Gerät gespeichert","toast.marker_removed":"Gerät aus dem Plan gelöscht","toast.integration_missing":"Die House Plan-Integration ist nicht installiert — Verwaltung nicht verfügbar","toast.plan_formats":"Unterstützte Formate: SVG, PNG, JPG, WebP","toast.plan_required":"Grundriss hochladen — erforderlich","toast.space_added_onboard":"Bereich hinzugefügt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","toast.space_added":"Bereich hinzugefügt","toast.space_saved":"Bereich gespeichert","toast.space_deleted":"Bereich gelöscht","toast.delete_failed":"Löschen fehlgeschlagen: {err}","toast.error":"Fehler: {err}","toast.toggle_target_unavailable":"Ziel „{name}“ ist nicht verfügbar — keine Aktion durchgeführt","toast.toggle_targets_unavailable":"Ziele nicht verfügbar: {names}. Keine Aktion durchgeführt","toast.file_failed":'Datei "{name}" wurde nicht hochgeladen: {err}',"toast.files_attached":"Dateien angehängt: {n}","err.unknown":"unbekannter Fehler","err.code":"Code {code}","err.too_large":"Datei größer als {mb} MB","err.bad_ext":"nicht unterstützter Typ (PDF/Bild erwartet)","err.unauthorized":"Administratorrechte erforderlich","editor.title":"Titel","editor.default_floor":"Anfangsbereich","editor.default_floor_missing":"Anfangsbereich „{id}“ existiert nicht mehr. Wählen Sie einen anderen Bereich.","editor.floor":"Fester Bereich","editor.floor_none":"— nicht fest —","editor.floor_index":"YAML-Index {index} (beibehalten)","fixed_floor.loading":"Lade den festen Bereich…","fixed_floor.invalid_title":"Der feste Bereich ist nicht verfügbar","fixed_floor.invalid_body":"Überprüfen Sie die Etage in der Kartenkonfiguration. Der konfigurierte Wert ist: {value}","editor.icon_size":"Symbolgröße, % der Planbreite","editor.show_temperature":"Temperatur anzeigen","editor.live_states":"Live-Zustände (ein/aus, offen…)","editor.light_pools":"Lichtkegel und Wandschatten","editor.show_signal":"Zigbee-Signal anzeigen (LQI)","editor.language":"Schnittstellensprache","editor.lang_auto":"Automatisch (HA-Profil)","title.icon_rules":"Symbolregeln: welche MDI-Symbole Geräte nach Namen erhalten","rules.title":"Symbolregeln","rules.hint":'Regeln werden von oben anhand von "Gerätename + Modell" (Großschreibungs-unempfindliches Regex) überprüft; das erste Match gewinnt. Wenn nichts übereinstimmt, entscheidet die Entität-Geräteklasse, dann das generische Chip-Symbol.',"rules.pattern_ph":"regex, z. B. Stecker|Steckdose","rules.icon_ph":"mdi:power-socket-de","rules.add":"Regel hinzufügen","rules.reset":"Zurücksetzen auf Standardeinstellungen","rules.test_ph":"Versuch mal einen Gerätenamen...","rules.invalid":"ungültiges regex","rules.saved":"Icon-Regeln gespeichert","btn.up":"Nach oben","btn.down":"Nach unten","tap.info":"Gerätekarte","tap.more_info":"HA Mehr-Info-Dialog","tap.toggle":"Zustand umschalten","marker.tap_label":"Tap-Aktion für dieses Gerät","tap.toggle_note":"Der aufgelöste Zustand ist unten dargestellt. Sichere Geräte werden niemals aus dem Plan umgeschaltet.","import.title":"Bereiche aus HA-Etagen erstellen","import.hint":"Home Assistant kennt diese Etagen bereits. Die Etagen auswählen, die zu Planbereichen werden sollen; anschließend für jeden Bereich einen Grundriss hochladen. Die Räume werden danach von Hand eingezeichnet.","import.start":"{n} Bereich(e) erstellen","import.manual":"Von Grund auf neu starten","import.progress":"Etage {i} von {n}","import.done":"Bereiche erstellt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","btn.skip":"Überspringen","space.scale_label":"Maßstab (Rasterzellen-Größe)","space.scale_unit":"cm pro Zelle","space.scale_unit_imperial":"Zoll pro Zelle","space.display_section":"Anzeige","space.show_borders":"Raumgrenzen immer anzeigen","space.zero_wall_style":"Wände ohne Dicke","space.zero_wall_dashed":"Gestrichelt","space.zero_wall_solid":"Durchgezogen","space.zero_wall_help":"Gestrichelte Wände lassen Licht durch. Durchgezogene Wände blockieren Licht auch bei einer Dicke von 0.","space.show_names":"Raumnamen anzeigen (zum Verschieben ziehen)","space.room_color":"Farbe der Begrenzung & des Namens","space.opacity":"Deckkraft","space.fill_label":"Raumausfüllung","fill.none":"Keine","fill.lqi":"Zigbee-Signal","fill.light":"Lichter","fill.custom":"Benutzerdefinierte Farbe","space.custom_fill":"Füllfarbe","space.source_file":"Ich habe einen Grundriss als Bild","space.source_draw":"Kein Bild – ich werde die Räume von Hand umreißen","fill.temp":"Temperatur","tip.temp_avg":"Durchschnittstemperatur:","tip.hum_avg":"Durchschnittliche Luftfeuchtigkeit:","space_card.button":"Grundriss öffnen","space_card.not_found":"Bereich „{id}“ nicht gefunden","space_card.loading":"Laden…","continuity.restore_plan":"Grundriss wiederherstellen…","continuity.restore_connection":"Geräteverbindung wiederherstellen…","continuity.retry":"Erneut versuchen","editor.space":"Bereich","editor.framing":"Bildausschnitt","editor.fit_content":"Alle sichtbaren Inhalte","editor.fit_house":"Eng an der Hausgeometrie","editor.show_button":"Schaltfläche anzeigen","editor.button_label":"Beschriftung der Schaltfläche","editor.button_target":"Ziel-Dashboard-Pfad","marker.sub_entity":"Entität","title.general_settings":"Allgemeine Einstellungen","gs.title":"Allgemeine Einstellungen","gs.hint":"Füllfarben gelten für jeden Bereich; jede Farbe hat ihre eigene Deckkraft. Der Füllmodus wird in den Einstellungen des jeweiligen Bereichs festgelegt.","gs.light_group":"Füllung: Licht","gs.light_on":"Lichter an","gs.light_off":"Alle Lichter aus","gs.temp_group":"Füllung: Temperatur","gs.temp_cold":"Kalt","gs.temp_ok":"Angenehm","gs.temp_hot":"Heiß","gs.lqi_group":"Füllung: Zigbee-Signal","gs.lqi_low":"Schwaches Signal","gs.lqi_high":"Starkes Signal","gs.reset":"Auf Standard zurücksetzen","gs.saved":"Allgemeine Einstellungen gespeichert","space.show_lqi":"Zigbee-Signal (LQI) neben Geräten anzeigen","space.hide_decor":"Dekorationsschicht ausblenden","space.hide_decor_tip":"Linien, Formen, Beschriftungen und Möbel bleiben an ihrem Platz — im Hintergrundeditor sichtbar, aber nicht auf dem Plan.","space.hide_openings":"Öffnungen ausblenden","space.hide_openings_tip":"Tür-, Fenster- und Tor-Symbole werden nicht gezeichnet, aber die Öffnungen funktionieren weiterhin: Licht fällt hindurch, die Sonne kommt durch ein Fenster herein, Kontaktsensoren öffnen sie weiterhin. Der Planeditor zeigt sie immer an.","gs.light_none":"Keine Lichtquellen","mode.plan":"Planeditor","mode.devices":"Geräteeditor","display.value":"Wert + Zustand","marker.subarea":"kein Bereich, manuell","device.new":"Neues Gerät — Editor öffnen, um zu schließen","opening.unlock_action":"Entsperren","opening.lock_action":"Sperren","opening.lock_pending":"Wird ausgeführt…","title.close_editor":"Editor schließen (zur Ansicht zurück)","title.add_device":"Gerät zum Plan hinzufügen","devbar.add":"Hinzufügen","devbar.rules":"Symbolregeln","device_inbox.button":"Geräte","device_inbox.title":"Geräte auf dem Plan","device_inbox.search":"Geräte, Entitäten und Integrationen suchen…","device_inbox.add_virtual":"Virtuelles Gerät hinzufügen","device_inbox.tab_on_plan":"Auf Plan","device_inbox.tab_available":"Verfügbar","device_inbox.tab_hidden":"Versteckt","device_inbox.tab_readd":"Wieder verfügbar","device_inbox.only_new":"Nur neu","device_inbox.show_entities":"Entitäten anzeigen","device_inbox.show_hidden":"Versteckte auf Plan anzeigen","device_inbox.new":"Neu","device_inbox.find":"Auf dem Plan finden","device_inbox.edit":"Bearbeiten","device_inbox.hide":"Ausblenden","device_inbox.show":"Anzeigen","device_inbox.add":"Hinzufügen","device_inbox.readd":"Wieder hinzufügen","device_inbox.hide_available":"Aus der Liste ausblenden","device_inbox.show_more":"Mehr anzeigen","device_inbox.more_actions":"Weitere Aktionen","device_inbox.find_hidden_hint":"Zuerst „Versteckte auf Plan anzeigen“ aktivieren","device_inbox.show_disabled":"Diese Bindung in Home Assistant aktivieren, bevor sie angezeigt wird","device_inbox.empty_on_plan":"Es befinden sich noch keine passenden Geräte auf dem Plan.","device_inbox.empty_available":"Es sind keine Geräte zum Hinzufügen verfügbar.","device_inbox.empty_hidden":"Es gibt keine versteckten Geräte.","device_inbox.empty_readd":"Es sind keine entfernten Geräte verfügbar, um sie erneut hinzuzufügen.","device_inbox.reason_visible_auto":"Automatisch erkannt","device_inbox.reason_visible_explicit":"Explizit hinzugefügt","device_inbox.reason_manual_hidden":"Vom Benutzer versteckt","device_inbox.reason_automatic_hidden":"Automatisch versteckt","device_inbox.reason_service_entry":"Nur Service-Registry-Eintrag","device_inbox.reason_excluded_integration":"Integration durch Gerätefilter ausgeschlossen","device_inbox.reason_excluded_domain":"Nicht-räumlicher Entitätstyp","device_inbox.reason_grouped_light":"Dargestellt durch eine Raumlichtgruppe","device_inbox.reason_represented_by_parent":"Dargestellt durch sein übergeordnetes Gerät","device_inbox.reason_removed":"Zuvor aus dem Plan entfernt","device_inbox.reason_available":"Zum Hinzufügen zum Plan verfügbar","device_inbox.reason_no_bound_room":"HA-Bereich ist nicht mit einem Planraum verknüpft","device_inbox.status_ha_disabled":"In Home Assistant deaktiviert","device_inbox.status_orphaned":"Bindung ist in Home Assistant nicht mehr vorhanden","device_inbox.status_unverified":"Home Assistant-Register vorübergehend nicht verfügbar","device_inbox.saved":"Geräteliste aktualisiert","space.roomcard_section":"Raumkarte zeigt:","space.label_temp":"Temperatur","space.label_hum":"Luftfeuchtigkeit","space.label_lqi":"Durchschnittliches Zigbee-Signal","space.label_light":"Lichter ein/aus","roomcard.light_on":"Ein","roomcard.light_off":"Aus","roomcard.light_partial":"{on} von {total}","toast.split_pick_inside":"Zwischenliegende Schnittpunkte müssen innerhalb des Raumes liegen","mode.decor":"Hintergrundeditor","decor.select":"Auswählen","decor.line":"Linie","decor.rect":"Rechteck","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Löschen","decor.erase_confirm_title":"Objekt löschen?","decor.color":"Farbe","decor.width":"Linienbreite","decor.line_style":"Linienstil","decor.line_style_solid":"Durchgehend","decor.line_style_dashed":"Gepunktet","decor.fill":"Füllen","decor.fill_color":"Füllfarbe","decor.length":"Länge","decor.size":"Größe","decor.angle":"Rotationswinkel, °","decor.text_size":"Textgröße","decor.backdrop_properties":"Eigenschaften des Planhintergrunds","decor.text_title":"Textbeschriftung","decor.object_title":"{kind} bearbeiten","decor.text_label":"Text","decor.live_group":"HA-Variable einfügen","decor.live_entity":"Entität","decor.live_entity_ph":"Wählen Sie eine Entität","decor.live_attr":"Wert","decor.live_attr_ph":"Wählen Sie Status oder Attribut","decor.live_state":"Status","decor.backdrop":"Hintergrundbild","decor.backdrop_hint":"Ziehen zum Verschieben; eine Ecke ziehen zum Größenändern; den oberen Griff zum Drehen verwenden. Shift ändert das Verhältnis oder befreit den Winkel.","decor.backdrop_reset":"Bild zurücksetzen","decor.backdrop_reset_done":"Das Bild ist wieder an seinem ursprünglichen Platz und in seiner ursprünglichen Größe","marker.icon_auto":"Auto: {icon} (nach Symbolregeln; wählen Sie eines zur Überschreibung)","marker.icon_pin_auto":"Anheften","mode.plan_tip":"Planungseditor — die Geometrie des Hauses: Räume zeichnen und teilen/zusammenführen, sie mit HA-Bereichen verbinden, Türen, Fenster und Tore platzieren, Raumkarten verschieben, Maßstab festlegen","mode.devices_tip":"Geräteeditor — alles über Symbole: ziehen zum Positionieren, klicken zum Bearbeiten von Bindung/Symbol/Anzeige, virtuelle Geräte hinzufügen, Symbolregeln","mode.decor_tip":"Hintergrundeditor — rein visuelle Dekoration unter dem Plan: Linien, Rechtecke, Ellipsen und Textbeschriftungen, die niemals auf Klicks reagieren","space.glow_enabled":"Lichtquellen-Glühen","gs.glow_group":"Glow von Lichtquellen","gs.glow_base":"Grunddunkelheit des Hauses","gs.glow_light":"Standardlichtfarbe / Intensität","gs.wall_group":"Wände","gs.wall_fill":"Wandfüllung","gs.glow_radius":"Glow-Radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Steuert andere Lichtquellen","marker.controls_hint":"Mit der Tippen-Aktion „Umschalten“ wechselt ein Klick jede hier hinzugefügte Quelle. Die eigene zugeordnete Entität des Markers wird direkt gesteuert und kann nicht hinzugefügt werden; verwenden Sie den untenstehenden Schalter, um ihn als Licht zu klassifizieren.","marker.controls_filter":"Suchlichter und Schalter…","info.controls":"Steuerungen","marker.glow_radius_label":"Leuchtradius","marker.glow_radius.help":"Legt den Leuchtradius in Metern oder Fuß fest; ein leerer Wert verwendet den Radius aus den allgemeinen Einstellungen.","marker.glow_radius.help.aria":"Hilfe: Leuchtradius","markup.wallthick":"Dicke","markup.select":"Auswählen","title.markup_wallthick":"Dicke — klicken Sie auf eine Wand, um ihre Dicke von 0 bis 100 cm festzulegen.","markup.hint_wallthick":"klicken Sie auf eine Wand · Esc schließt ohne Anwendung","wallthick.field":"Dicke","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Auf alle Wände dieses Raums anwenden","markup.draw_wall_title":"Dicke jedes neuen Wandsegments (0–100 cm). Gemeinsame Wände behalten den bestehenden Wert.","room.queue_progress":"Raum {current} von {total}","toast.wall_rooms_saved":"Erstellte Räume: {n}","toast.wall_chain_saved":"Wandkette gespeichert","toast.wallthick_pick":"Eine Wand anklicken","toast.wallthick_set":"Wandstärke eingestellt","toast.wallthick_cleared":"Wandstärke entfernt","toast.physical_range":"Einen Wert von {min} bis {max} {unit} eingeben","toast.zero_wall_opening_conflict":"Entfernen Sie zuerst die Öffnung an diesem Wandsegment.","toast.zero_wall_ambiguous":"Das Wandsegment ist mehrdeutig. Vereinfachen oder passen Sie den Anschluss an.","toast.zero_wall_migration_blocked":"Der Bereich wurde nicht konvertiert: {reason}. Es wurden keine Daten geändert.","toast.physical_angle":"Einen Drehwinkel von 0° bis unter 90° eingeben","toast.physical_limit":"Der Bereich hat das Limit für diesen Geometrietyp erreicht","toast.geometry_unsafe":"Änderung abgebrochen: Wandgeometrie konnte nicht sicher erstellt werden.","junction.limit_angle":"Der Winkel zwischen den Wänden ist zu spitz: {actual}°, mindestens {limit}°.","junction.limit_valence":"Zu viele Wände treffen in einem Knoten aufeinander: {actual}, maximal {limit}.","junction.limit_length":"Das Wandsegment ist zu kurz: {actual} cm, mindestens {limit} cm.","junction.limit_distance":"Wände und Knoten sind zu dicht beieinander: {actual} cm, Minimum {limit} cm.","junction.limit_clearance":"Kein Wohnbereich ist übrig: {actual} cm², Minimum {limit} cm².","junction.limit_check_failed":"Die Überprüfung der Verbindungen konnte nicht durchgeführt werden — die Änderung wurde nicht gespeichert.","toast.wall_model_migration_blocked":"Das Wandmodell konnte nicht aktualisiert werden: {reason}. Der Plan wurde nicht geändert. Führen Sie „Pläne optimieren“ aus; wenn der Fehler erneut auftritt, beheben Sie die widersprüchliche Wandgeometrie.","toast.wall_model_client_outdated":"Aktualisieren Sie die Karte und laden Sie die Seite neu, bevor Sie den Plan bearbeiten.","wall_model.reason.invalid-room":"ungültiger Raumkontur","wall_model.reason.zero-length":"Wandsegment mit null Länge","wall_model.reason.third-owner":"Wand, die von mehr als zwei Räumen geteilt wird","wall_model.reason.duplicate-id":"widersprüchliche Wandkennungen","wall_model.reason.thickness-conflict":"widersprüchliche Wandstärkenwerte","wall_model.reason.opening-host":"Öffnung kann nicht einer Wand zugewiesen werden","toast.delete_room_pick":"Klicken Sie in den Raum, den Sie löschen möchten","toast.plan_snap_ambiguous":"Zoomen Sie hinein, um den Wandknoten auszuwählen","toast.wall_repair_ambiguous":"Der Raumumriss hat mehr als eine mögliche Verbindung. Zoomen Sie hinein und verbinden Sie ihn explizit.","toast.wall_repair_too_large":"Die Lücke ist größer als 2 cm. Verbinden Sie die Wände, bevor Sie den Raum erstellen.","toast.wall_repair_changed":"Die Wandgeometrie hat sich geändert. Versuchen Sie erneut, den Raum zu erstellen.","toast.opening_on_zero_wall":"Öffnungen können nicht auf einer Wand mit null Dicke sitzen.","marker.from_ha_option":"Wählen Sie aus der HA-Liste.","marker.show_entities":"Entitäten anzeigen.","marker.show_entities_tip":"Fügt nicht nur Geräte zur Liste hinzu, sondern auch alle ihre Entitäten.","marker.pick_ph":"Wählen Sie ein Gerät…","room.open_area":"Öffnen Sie den HA-Bereich.","view.volumetric":"Volumetrische Ansicht.","view.flat":"Flache Ansicht.","kiosk.title":"Die Größen dieses Bildschirms.","kiosk.hint":"Nur auf diesem Gerät gespeichert – jedes Wandtablet oder TV kann seine eigenen komfortablen Größen haben.","kiosk.icon_scale":"Gerätesymbolgröße.","kiosk.font_scale":"Textgröße der Raumkarte.","editor.kiosk":"Wandgeräte- (Kiosk-)Modus.","editor.cycle":"Bereiche automatisch alle N Sekunden wechseln (Kiosk, 0 = aus)","room.settings_title":"Raumeinstellungen.","room.settings_section":"Raumeinstellungen (überschreibt den Bereich)","room.fill_label":"Diesen Raum ausfüllen","fill.inherit":"Als den Bereich","room.custom_fill_space":"Bereichsfarbe","room.custom_fill_own":"Raumfarbe","room.temp_src_label":"Temperaturquelle","room.hum_src_label":"Luftfeuchtigkeitsquelle","room.src_average":"Durchschnitt über die Sensoren des Raums (Standard)","room.src_pick":"Ein bestimmtes HA-Gerät oder Entität","room.src_ph":"Quelle wählen…","toast.room_updated":"Raum aktualisiert","space.card_font":"Schriftgröße der Raumkarte (gesamter Bereich)","room.sizes_section":"Schriftgrößen","room.name_scale":"Raumname Größe","room.label_scale":"Metrikgröße","preview.room_name":"Wohnzimmer","toast.cfg_reload_failed":"Der Plan konnte nicht vom Server neu geladen werden: {err}","toast.locale_load_failed":"Das Sprachpaket konnte nicht geladen werden. Bis zum Neuladen der Seite wird Englisch verwendet.","room.settings_short":"Raumeinstellungen","room.unnamed":"Unbenannter Raum","marker.use_climate_temp":"Geräte-Temperatur im Raum berücksichtigen","marker.use_climate_temp_tip":"Fügt die aktuelle Temperatur einer Klimaanlage oder eines Thermostats zum Raumdurchschnitt hinzu. Das externe Wert-Label unten separat konfigurieren.","marker.value_badge_title":"Wert-Label","marker.value_badge.help":"Zeigt einen ausgewählten Wert neben dem Symbol an. Dies beeinflusst nicht die Raumkennzahlen, Licht, Glanz oder die Tippaktion.","marker.value_badge.help.aria":"Hilfe: Gerätewert-Badge","marker.value_badge_enabled":"Zeige ein Wert-Badge","marker.value_badge_source":"Wert","marker.value_badge_source.help":"Wählen Sie einen bestimmten Zustand, ein unterstütztes Attribut oder einen abgeleiteten Wert dieses Geräts.","marker.value_badge_source.help.aria":"Hilfe: Quelle des Wert-Badges","marker.value_badge_position":"Position","marker.value_badge_position.help":"Die gewählte Seite bleibt fest und dreht sich nicht automatisch an einem Planrand.","marker.value_badge_position.help.aria":"Hilfe: Position des Wert-Badges","marker.value_badge_right":"Rechts","marker.value_badge_bottom":"Unten","marker.value_badge_left":"Links","marker.value_badge_top":"Oben","marker.value_badge_empty":"Für dieses Gerät stehen keine Werte zur Verfügung","marker.value_badge_static":"Ein statisches Symbol zeigt keine Live-Werte an. Die Einstellung wird beibehalten.","marker.value_badge_missing":"Quelle nicht verfügbar","marker.value_badge_missing_hint":"Die gespeicherte Quelle ist derzeit nicht verfügbar. Behalten Sie sie bei, deaktivieren Sie das Badge oder wählen Sie einen anderen Wert.","marker.value_badge_duplicate":"Derselbe Wert wird bereits im Symbol angezeigt.","marker.value_badge_state":"Status · {name}","marker.value_badge_marker_state":"Lichtquellstatus · {name}","marker.value_badge_lqi":"Durchschnittliche Zigbee-Signalqualität","marker.value_badge_attr_current_temperature":"Aktuelle Temperatur · {name}","marker.value_badge_attr_temperature":"Zieltemperatur · {name}","marker.value_badge_attr_current_humidity":"Aktuelle Luftfeuchtigkeit · {name}","marker.value_badge_attr_humidity":"Luftfeuchtigkeit · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Geschwindigkeit · {name}","marker.value_badge_attr_brightness":"Helligkeit · {name}","marker.value_badge_attr_volume_level":"Lautstärke · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Lüftergeschwindigkeit · {name}","marker.value_source":"Wertquelle","marker.value_source.help":"Wähle den Wert, der das Symbol ersetzt. Die Tipp-Aktion bleibt unverändert.","marker.value_source.help.aria":"Hilfe: Wertquelle in der Gerätefläche","marker.value_source_auto":"Automatisch (wie bisher)","marker.value_source_missing_hint":"Die gespeicherte Quelle ist derzeit nicht verfügbar. Bis sie wieder verfügbar ist oder ein anderer Wert gewählt wird, zeigt die Fläche —.","marker.light_role_label":"Ist dieses Gerät eine Lichtquelle?","marker.light_role.help":"Steuert, ob dieser Marker selbst eine räumliche Lichtquelle ist. Verknüpfte Lichter oben bleiben unabhängige Raumlichtsteuerungen.","marker.light_role.help.aria":"Hilfe: ob dieses Gerät eine Lichtquelle ist","marker.light_role_auto_yes":"Automatisch (Lichtquelle)","marker.light_role_auto_no":"Automatisch (keine Lichtquelle)","marker.light_role_always":"Immer eine Lichtquelle","marker.light_role_never":"Nie eine Lichtquelle","marker.light_entity_label":"Führende Lichtereinheit","marker.light_entity.help":"Für ein Verbundgerät wählen Sie die Entität, deren Zustand, Farbe und Helligkeit diese Planquelle darstellen. Der automatische Rückgriff bleibt mit älteren Plänen kompatibel.","marker.light_entity.help.aria":"Hilfe: führende Lichtentität","marker.light_entity_auto":"Automatisch ({entity})","marker.light_entity_none":"keine steuerbare Entität","marker.light_entity_missing":"Die gespeicherte Entität {entity} ist nicht verfügbar. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.toggle_entity_label":"Zu toggelnde Entität","marker.toggle_entity.help":"Für ein Verbundgerät wählen Sie das genaue eigene Licht oder den Schalter, der durch Tippen bedient wird. Automatisch behält die vorherigen Zielregeln bei und ist unabhängig von der führenden Lichtentität.","marker.toggle_entity.help.aria":"Hilfe: zu toggelnde Entität","marker.toggle_entity_auto":"Automatisch ({entity})","marker.toggle_entity_none":"keine eigene steuerbare Entität","marker.toggle_entity_missing":"Die gespeicherte Entität {entity} ist nicht mehr unter den auswählbaren Kanälen dieses Markers. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.glow_color_label":"Leuchtfarbe und Helligkeit","marker.glow_mode.help":"Verwenden Sie Live-Quellwerte, überschreiben Sie nur deren Farbe oder fixieren Sie sowohl Farbe als auch Helligkeit. Das Minimum beträgt 1 %; um die Quelle zu deaktivieren, wählen Sie „Nie“.","marker.glow_mode.help.aria":"Hilfe: Leuchtfarbe und Helligkeit","marker.glow_mode_auto":"Von Quelle","marker.glow_mode_color":"Farbe einstellen","marker.glow_mode_fixed":"Farbe und Helligkeit einstellen","marker.glow_color":"Leuchtfarbe","marker.glow_brightness":"Helligkeit","marker.glow_disabled_never":"Leuchteinstellungen sind nicht verfügbar, da dieser Marker ausdrücklich keine Lichtquelle ist.","marker.glow_disabled_auto":"Im Automatikmodus wurde keine räumliche Lichtquelle für diesen Marker gefunden.","marker.glow_disabled_no_entity":"Keine aktive steuerbare Home Assistant-Entität ist für diese räumliche Quelle verfügbar.","marker.glow_passive_hint":"Diese Quelle hat keine eigenen Home Assistant-Daten. Stellen Sie ihre Farbe und Helligkeit manuell ein; ihr Radius bleibt verfügbar.","marker.control_broken":"Gespeicherte Quelle fehlt oder ist nicht mehr als Licht markiert","marker.control_missing_label":"Fehlendes Planlicht","marker.control_passive":"passive Quelle","toast.marker_control_cycle":"Dieser Link würde eine kreisförmige Kette von Lichtsteuerungen erzeugen.","toast.marker_binding_required":"Wählen Sie ein Home Assistant-Gerät, bevor Sie eine andere Lichtquelle verlinken.","confirm.unlock":"„{name}“ entsperren?","toast.files_migrate_failed":"Anhänge konnten nicht zur neuen Bindung verschoben werden, Links zeigen weiterhin auf die alten Dateien: {err}","space.pick_saved":"Bereits hochgeladen","space.pick_saved_hint":"Auf dem Server gespeicherte Pläne, einschließlich solcher, die Sie zuvor getrennt haben","space.no_saved":"Noch keine Pläne auf dem Server gespeichert.","space.loading":"Laden…","space.used_by":"in Benutzung: {list}","space.in_use":"Ein Bereich verwendet diesen Plan noch — zuerst die Verknüpfung lösen","btn.use":"Verwenden","confirm.delete_plan":"Plandatei „{name}“ vom Server löschen? Dies kann nicht rückgängig gemacht werden.","toast.plans_list_failed":"Konnte die gespeicherten Pläne nicht auflisten: {err}","toast.plan_delete_failed":"Konnte den Plan nicht löschen: {err}","marker.hide":"Ausblenden","marker.hide_tip":'Das Gerät wird nach dem Speichern aus dem Plan verschwinden, aber weiterhin zum Signalsignal des Raums zählen. Stellen Sie es über den Katalog "Geräte" im Geräte-Editor wieder her.',"marker.show":"Anzeigen","marker.show_tip":"Das Gerät wird nach dem Speichern wieder im Plan erscheinen.","marker.hidden_ghost":"Vom Benutzer ausgeblendetes Gerät","marker.ha_disabled_device":"Das Gerät ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_entity":"Die Entität ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_all_entities":"Das Gerät hat keine aktiven Home Assistant-Entitäten, daher ist es im Plan ausgeblendet.","marker.ha_registry_limited":"Das vollständige Home Assistant-Register ist für diesen Benutzer nicht verfügbar. Das nicht verifizierte Objekt kann vorerst nicht angezeigt oder verwendet werden.","marker.delete_tip":"Löschen Sie das Gerät vollständig aus dem Plan und allen Aggregaten. Sie können es später wieder hinzufügen.","tap.run":"Führen Sie Automation/Skript/Szene aus","marker.run_target_label":"Was ausführen","marker.run_search_ph":"Suchen: Automation, Skript oder Szene…","marker.run_target_gone":"Ziel {id} nicht gefunden — erneut auswählen","marker.tap_confirm":"Um Bestätigung bitten","marker.tap_confirm_tip":"Vor der Ausführung ein Bestätigungsdialogfeld anzeigen — eine Vorsichtsmaßnahme gegen versehentliche Berührungen.","marker.toggle_hint_single":"Ziel: {name} ({id}).","marker.virtual_light_target":"Virtuelles Licht: {name}.","marker.virtual_light_current":"Manueller Zustand: {state} → {effect}.","marker.virtual_light_state_on":"ein","marker.virtual_light_state_off":"aus","marker.toggle_hint_group":"Wird {count}-Quelle(n) umschalten: {names}.","marker.toggle_hint_current":"Jetzt: {state} → {effect}.","marker.toggle_hint_group_current":"Derzeit an: {on} von {count} → {effect}.","marker.toggle_hint_skipped":"Wird übersprungen ({count}): {targets}.","marker.toggle_effect_turn_on":"wird eingeschaltet","marker.toggle_effect_turn_off":"wird ausgeschaltet","marker.toggle_effect_open":"wird geöffnet","marker.toggle_effect_close":"wird geschlossen","marker.toggle_effect_stop":"wird gestoppt","marker.toggle_effect_toggle":"Zustand wird umgeschaltet","marker.toggle_skip_missing":"nicht gefunden","marker.toggle_skip_ha_disabled":"in HA deaktiviert","marker.toggle_skip_unavailable":"nicht verfügbar","marker.toggle_skip_unsupported":"Umschalten wird nicht unterstützt","marker.toggle_skip_secure":"aus Sicherheitsgründen blockiert","marker.toggle_none_no_binding":"Dieses Gerät hat keine Bindung, daher gibt es keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_no_actionable_entity":"Dieses Gerät hat keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_configured_targets_missing":"Die konfigurierten Ziele sind nicht verfügbar. Die eigene Entität des Geräts wird nicht ersetzt.","marker.toggle_none_ha_disabled":"Das Ziel ist in Home Assistant deaktiviert und kann im Plan nicht verwendet werden.","marker.toggle_none_unavailable":"Das Ziel ist derzeit nicht verfügbar. Tippen bewirkt nichts.","marker.toggle_none_unsupported":"Home Assistant bietet keine sichere Umschaltfunktion für dieses Gerät. Tippen bewirkt nichts.","marker.toggle_none_secure":"Das Umschalten von Schlössern, Alarmen und gesicherten Toren über den Plan ist aus Sicherheitsgründen gesperrt.","run.automation":"Automatisierung","run.script":"Skript","run.scene":"Szene","confirm.tap_run":'"{name}" ausführen?',"confirm.tap_toggle":'"{name}" umschalten?',"confirm.current_state":"Aktueller Zustand: {state}","confirm.expected_state":"Nach dem Umschalten: {state}","confirm.group_current":"an {on} von {total}","confirm.group_all_on":"alle sind eingeschaltet","confirm.group_all_off":"alle sind ausgeschaltet","confirm.unavailable_targets":"Nicht verfügbar: {count}","confirm.expected_by_ha":"Home Assistant ermittelt den Zustand","confirm.state_on":"Ein","confirm.state_off":"Aus","confirm.state_open":"Offen","confirm.state_closed":"Geschlossen","confirm.state_opening":"Wird geöffnet","confirm.state_closing":"Wird geschlossen","confirm.state_stopped":"Gestoppt","confirm.state_unknown":"Unbekannt","toast.run_started":"Gestartet: {name}","toast.run_target_missing":"Ausführungsziel nicht gefunden — überprüfen Sie die Geräteeinstellungen","toast.run_target_required":"Wählen Sie eine Automatisierung, ein Skript oder eine Szene aus","toast.tap_target_changed":"Das Ziel der Aktion hat sich geändert. Versuchen Sie es erneut.","toast.virtual_light_toggle_failed":"Das virtuelle Licht konnte nicht umgeschaltet werden: {err}","toast.value_badge_source_required":"Einen Wert für den Badge auswählen","btn.run":"Ausführen","vac.section":"Roboterstaubsauger: aktuelle Position","vac.autocal":"Automatisch einrichten","vac.live":"Aktuelle Position im Plan","vac.trail":"Roboterpfad anzeigen","vac.cal_maps":"Kalibrierte Karten: {maps}","vac.autocal_no_rooms":'Die Integration meldet keine Raumliste — öffnen Sie "Manuell anpassen"',"vac.autocal_no_match":'Raumnamen stimmten nicht überein (mindestens 3 müssen übereinstimmen) — öffnen Sie "Manuell anpassen"',"vac.autocal_done":"Fertig: über {rooms} Räume zugeordnet. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_need_pos":"Der Roboter meldet keine Koordinaten — starten Sie eine Reinigung und pausieren Sie sie","vac.cal_done":"Kalibrierung gespeichert. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_cancelled":"Kalibrierung abgebrochen","vac.fit":"Manuell anpassen","vac.fit_hint":"Roboterkarte an die richtige Position ziehen und an den Ecken skalieren","vac.fit_rotate":"90° drehen","vac.fit_mirror":"Spiegeln","vac.trail_never":"Nie","vac.trail_cleaning":"Beim Reinigen","vac.trail_always":"Immer","gs.bg_group":"Bühnenhintergrund","gs.bg_color":"Hintergrund um den Plan","gs.bg_default":"Standardthema","gs.bg_theme":"Standardthema","gs.bg_mode":"Planhintergrund","gs.bg_static":"Statische Farbe","gs.bg_daynight":"Folgt der Sonne","gs.bg_daynight_hint":"Der Hintergrund wechselt durch Morgendämmerung, Tag, Abenddämmerung und Nacht mithilfe von sun.sun oder Ihrer lokalen Uhr, wenn keine Sonnendaten verfügbar sind. Für den Hintergrund ist kein Kompass erforderlich.","gs.sun_group":"Sonne","gs.sun_missing":"Die sun.sun-Entität wurde nicht gefunden — der Hintergrund folgt Ihrer lokalen Uhr; Fensterstrahlen sind nicht verfügbar.","gs.north":"Norden auf dem Plan","gs.north_ph":"nicht gesetzt","gs.north_hint":"Norden nur für Fensterstrahlen einstellen (1° Schritte, 15° mit Shift); der Sonnenhintergrund funktioniert auch ohne ihn.","gs.north_clear":"Löschen","gs.north_letter":"N","gs.sun_rays":"Sonnenlicht durch Fenster","gs.about_group":"Über","gs.about_version":"House Plan Card v{v}","gs.about_github":"GitHub · Dokumentation & Probleme","gs.about_telegram":"Telegram-Chat","space.bg_color":"Hintergrund rund um den Plan","space.bg_inherit":"Allgemein erben","space.bg_inherited":"erbt allgemeine Einstellungen","space.bg_mode":"Planhintergrund","space.north":"Norden auf dem Plan (überschreiben)","space.north_inherited":"geerbt: {v}","space.sun_rays":"Sonnenlicht durch Fenster","space.sun_inherit":"Allgemein erben","space.sun_on":"Ein","space.sun_off":"Aus","canvas.far_objects":"{n} Objekt(e) weit vom Plan entfernt","canvas.show_far":"Anzeigen","canvas.home_tip":"Der Plan ist dort drüben — klicken, um ihn anzupassen","gs.grid_group":"Planwartung","gs.grid_hint":"Aktualisiert Datenmodelle, richtet Planelemente am Raster aus und fasst redundante Wandfragmente zusammen. Ein exakter Bericht wird angezeigt, bevor etwas gespeichert wird.","gs.align_all":"Pläne optimieren","gs.align_title":"Pläne optimieren","gs.align_none":"Alle Pläne verwenden bereits das aktuelle optimierte Datenmodell.","gs.optimize_no_automatic_changes":"Es gibt keine automatischen Änderungen, die angewendet werden müssen. Überprüfen Sie die unten stehenden Punkte.","gs.align_count":"{n} von {total} Elementen werden sich um höchstens {cm} cm bewegen.","gs.align_where":"Die größte Verschiebung ist in „{s}“.","gs.align_turned":"Öffnungen, deren Winkel korrigiert wurde: {n}.","gs.align_removed_drafts":"Ungültige Umrisse vom Raster zusammengeklappt und entfernt: {n}.","gs.optimize_redundant_drafts":"Gespeicherte Wandketten, die durch feste Raumwände verdeckt waren, wurden entfernt: {n}.","gs.align_preflight_failed":"Die Geometrie der folgenden Bereiche konnte nicht sicher überprüft werden: {spaces}{more}.","gs.align_preflight_hint":"Pläne wurden nicht geändert. Kopieren Sie die Diagnosen mit dem untenstehenden Button und fügen Sie sie zusammen mit einem Raumexport dem Fehlerbericht bei.","gs.preflight_reason_prepare-exception":"Die Bereichsgeometrie konnte nicht vorbereitet werden (Ausnahme beim Erstellen des Modells)","gs.preflight_reason_wall-null":"Der Wandkörper wurde nicht erstellt (die Vereinigung lieferte leer zurück)","gs.preflight_reason_wall-degraded-extra":"Der Wandkörper wurde mit zusätzlicher Geometrie beschädigt","gs.preflight_reason_wall-failed-core":"Der Kern des Wandkörpers konnte nicht zusammengesetzt werden","gs.preflight_reason_wall-exception":"Die Wandkonstruktion warf eine Ausnahme","gs.preflight_reason_floor-null":"Der Fußbodenumriss wurde nicht erstellt","gs.preflight_reason_floor-exception":"Bodenaufbau hat eine Ausnahme ausgelöst","gs.preflight_copy":"Diagnosen kopieren","gs.preflight_copied":"Diagnosen kopiert","gs.preflight_update_hint":"Die Karten- und Integrationsversionen unterscheiden sich — Hausplan aktualisieren und erneut versuchen.","gs.align_preflight_space":"Bereich {n}","gs.align_preflight_more":", und {n} mehr","gs.optimize_changes":"Modellmigrationen: {m}; aktualisierte Bereiche: {c}; entfernte verrauschte Koordinatenwerte: {p}; zusammengeführte Fragmente realer Wände: {w}; zusammengeführte Fragmente von Wänden ohne Dicke: {s}; unabhängige Wände: {i}.","gs.zero_walls_migrated":"Virtuelle Wandspannweiten konvertiert: {n}.","gs.wall_segments_migrated":"Wandsegmente stabilisiert: {n}.","gs.optimize_lattice_summary":"Rauschen in den Koordinatenwerten kanonisiert: {n}; maximale Bewegung: {cm} cm.","gs.optimize_lattice_space":"{space}: Koordinatenwerte kanonisiert: {n}; außerhalb des Rasters liegende Werte unverändert gelassen: {far}.","gs.optimize_coincident_partitions":"Versteckte unabhängige Wandabschnitte in Raumwände integriert: {n}.","gs.optimize_openings_rehosted":"Öffnungen wieder an Raumwände angebracht: {n}.","gs.optimize_walls_straightened":"Wände begradigt: {n}; maximale Bewegung: {cm} cm.","gs.optimize_walls_straightened_where":"Größte Wandkorrektur: {s}.","gs.optimize_walls_straighten_skipped":"Nahe Achsenwände unverändert gelassen, da sie nicht sicher repariert werden konnten: {n}.","gs.optimize_glow_migration":"Legacy Glow: {spaces} Bereiche → keine Datenfüllung + unabhängiges Glow; {rooms} Räume → geerbte Datenfüllung + unabhängiges Glow.","gs.optimize_references":"Referenzen repariert: Bereiche — {spaces}; Räume — {rooms}; Positionen — {positions}; Geräte aus fehlenden Bereichen gelöst — {detached}.","gs.optimize_reference_more":", und {n} mehr","gs.optimize_orphans_removed":"Vergessene Einträge entfernt: {total} — Raumbezeichnungen: {rooms}; Geräte: {devices}; Gruppenmarkierungen: {groups}. Sie gehörten zu zuvor gelöschten Bereichen.","gs.optimize_live_positions":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie werden beibehalten.","gs.optimize_live_positions_remove":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie sind zur Entfernung ausgewählt.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Alte Positionen entfernen","gs.optimize_live_keep":"Alte Positionen beibehalten","gs.optimize_live_selected":"Alte Positionen werden nach der Anwendung von Optimieren entfernt.","gs.optimize_unverified":"Positionen konnten nicht sicher überprüft werden: {n}. Sie wurden unverändert belassen.","gs.optimize_registry_limited":"Voller Administratorzugriff auf die Home Assistant-Registries ist für eine sichere Überprüfung erforderlich.","gs.optimize_vacuum_warning":"Raumzuordnungen, die noch überprüft werden müssen: {n}.","gs.optimize_details":"Details","gs.optimize_details_more":"Und {n} weitere Einträge.","gs.optimize_detail_removed":"wird entfernt","gs.optimize_detail_live":"wird beibehalten","gs.optimize_detail_unverified":"nicht überprüft","gs.optimize_detail_room_label":"Raumkennzeichnung","gs.optimize_detail_device":"Gerät","gs.optimize_detail_group":"Gruppenkennzeichen","gs.optimize_detail_unknown":"unbekannter Eigentümer","gs.optimize_detail_item":"{status}: {kind} {id}; alter Bereich {space}","gs.align_warn":"Absichtlich zwischen Rasterknoten platzierte Elemente werden sich bewegen. Nach der Operation ist nur eine Rückgängig-Funktion verfügbar, und zwar nur bis zur nächsten Planbearbeitung.","gs.align_run":"Optimieren","gs.align_done":"Pläne optimiert: {n} Elemente bewegt, {m} Datensätze beibehalten, {r} Verweise repariert","gs.optimize_undo":"Letzte Optimierung rückgängig machen","gs.optimize_undone":"Die letzte Optimierung wurde rückgängig gemacht","decor.furniture":"Möbel","furn.title":"Möbelbibliothek","furn.symbol":"Symbol","furn.group_furniture":"Möbel","furn.group_appliance":"Geräte","furn.group_sanitary":"Sanitär","furn.group_other":"Sonstiges","furn.width":"Breite","furn.depth":"Tiefe","furn.back_to_categories":"Alle Kategorien","furn.pick_hint":"Wählen Sie eine Kategorie und dann ein Symbol.","furn.place_hint":"Klicken Sie auf den Plan — das Element wird an der nächsten Wand platziert. Mit Shift wird es frei platziert.","furn.cat_air_conditioner":"Klimaanlagen","furn.cat_armchair":"Sessel","furn.cat_bathtub":"Badewannen","furn.cat_bed":"Betten","furn.cat_bidet":"Bidets","furn.cat_boiler":"Warmwasserbereiter","furn.cat_chair":"Stühle","furn.cat_coffee_table":"Couchtische","furn.cat_cooktop":"Kochfelder","furn.cat_dining_table":"Esstische","furn.cat_dishwasher":"Geschirrspüler","furn.cat_dryer":"Wäschetrockner","furn.cat_fireplace":"Kamine","furn.cat_fridge":"Kühlschränke","furn.cat_kitchen_cabinet":"Küchenschränke","furn.cat_kitchen_sink":"Küchenspülen","furn.cat_nightstand":"Schränke","furn.cat_plant":"Pflanzen","furn.cat_rug":"Teppiche","furn.cat_shelving":"Regale","furn.cat_shower":"Duschen","furn.cat_sink":"Waschbecken","furn.cat_sofa":"Sofas","furn.cat_stairs":"Treppen","furn.cat_toilet":"Toiletten","furn.cat_tv":"Fernseher","furn.cat_wardrobe":"Kleiderschränke","furn.cat_washer":"Waschmaschinen","furn.cat_work_table":"Schreibtische","furn.sym_sofa":"Zweisitzer-Sofa","furn.sym_sofa_three_seat":"Dreisitzer-Sofa","furn.sym_sofa_corner_right":"Ecksofa rechts","furn.sym_armchair":"Polstersessel","furn.sym_armchair_office":"Bürostuhl","furn.sym_coffee_table":"Rechteckiger Couchtisch","furn.sym_coffee_table_round":"Runder Couchtisch","furn.sym_coffee_table_oval":"Ovaler Couchtisch","furn.sym_coffee_table_rounded":"Abgerundeter Couchtisch","furn.sym_table_dining":"Rechteckiger Esstisch","furn.sym_table_round":"Rundtisch","furn.sym_table_dining_oval":"Ovaler Esstisch","furn.sym_table_dining_rounded":"Abgerundeter Esstisch","furn.sym_chair":"Stuhl","furn.sym_chair_bar":"Barhocker","furn.sym_desk":"Rechteckiger Schreibtisch","furn.sym_desk_corner":"Eckschreibtisch","furn.sym_bed_double":"Doppelbett","furn.sym_bed_single":"Einzelbett","furn.sym_nightstand":"Nachttisch","furn.sym_cabinet_tv":"TV-Schrank","furn.sym_cabinet_shoe":"Schuhschrank","furn.sym_cabinet_sink":"Waschtischunterschrank","furn.sym_wardrobe":"Kleiderschrank","furn.sym_bookshelf":"Bücherschrank","furn.sym_wall_unit":"Schrankwand","furn.sym_kitchen_floor":"Küchen-Unterschrank","furn.sym_kitchen_floor_corner":"Küchen-Eckunterschrank","furn.sym_kitchen_wall":"Küchen-Hängeschrank","furn.sym_kitchen_wall_corner":"Küchen-Eckhängeschrank","furn.sym_shelf_floor":"Standregal","furn.sym_shelf_wall":"Wandregal","furn.sym_fridge":"Kühlschrank","furn.sym_stove":"Vierflammiges Kochfeld","furn.sym_cooktop_two":"Zweiflammiges Kochfeld","furn.sym_dishwasher":"Geschirrspüler","furn.sym_washer":"Waschmaschine","furn.sym_dryer":"Wäschetrockner","furn.sym_tv":"Fernseher mit Standfuß","furn.sym_tv_wall":"Wandfernseher","furn.sym_ac":"Klimaanlage","furn.sym_water_heater":"Wassererhitzer","furn.sym_toilet":"Stand-WC","furn.sym_toilet_built_in":"Wand-WC","furn.sym_bathtub":"Rechteckige Badewanne","furn.sym_bathtub_corner":"Eckbadewanne","furn.sym_shower":"Dusche","furn.sym_sink":"Waschbecken","furn.sym_kitchen_sink":"Einzel-Küchenspüle","furn.sym_kitchen_sink_double":"Doppel-Küchenspüle","furn.sym_bidet":"Stand-Bidet","furn.sym_bidet_built_in":"Wand-Bidet","furn.sym_stairs":"Treppe","furn.sym_fireplace":"Kamin","furn.sym_plant":"Pflanze","furn.sym_rug":"Teppich","common.yes":"Ja","common.no":"Nein","vac.diag_source":"Quelle","vac.diag_platform":"Integration","vac.diag_status":"Status","vac.diag_position":"Position","vac.diag_rooms":"Räume","vac.diag_rooms_value":"{total} · {matched} Namen stimmen überein · {readiness}","vac.autocal_ready":"Auto-Kalibrierung verfügbar","vac.autocal_not_ready":"3 übereinstimmende Namen erforderlich","vac.diag_path":"Integrationspfad","vac.diag_map":"Karten-ID","vac.source_none":"nicht ausgewählt","vac.source_status_ok":"Bereit","vac.source_status_missing":"Fehlend","vac.source_status_disabled":"In Home Assistant deaktiviert","vac.source_status_unavailable":"Nicht verfügbar","vac.source_status_unverified":"Kann mit den aktuellen Berechtigungen nicht verifizieren","vac.source_status_unsupported":"Keine Positionsdaten","vac.source_status_none":"Keine Quelle","vac.source_banner_missing":"Die gespeicherte Quelle existiert nicht mehr. Sie wurde nicht automatisch ersetzt; wählen Sie eine andere Quelle oder stellen Sie sie in Home Assistant wieder her.","vac.source_banner_disabled":"Die gespeicherte Quelle ist in Home Assistant deaktiviert. Aktivieren Sie sie dort oder wählen Sie eine andere Quelle.","vac.source_banner_unverified":"Die aktuellen Home Assistant-Berechtigungen können diese gespeicherte Quelle nicht verifizieren. Sie bleibt angepinnt und wird nicht automatisch ersetzt.","vac.choose_source":"Quelle wählen","vac.source_auto":"Automatisch","vac.source_auto_hint":"Verwenden Sie eine kompatible Entität von diesem Gerät","vac.all_cameras":"Alle Kameras","vac.all_cameras_warn":"Eine Kamera liefert möglicherweise keine Roboter-Daten. Wählen Sie eine nur, wenn es Ihre Saugroboter-Karte ist.","vac.all_cameras_empty":"Keine anderen Kamera-Entitäten gefunden.","vac.platform_unknown":"unbekannte Integration","vac.cap_position":"Position","vac.cap_rooms_short":"Räume","vac.cap_path":"Pfad","vac.cap_map":"Karten-ID","vac.cap_none":"Keine Roboter-Daten erkannt","vac.xcme_hint":"Aktivieren Sie diese Xiaomi Cloud Map Extractor-Attribute:","vac.documentation":"Dokumentation","vac.residual_title":"Automatische Kalibrierung überprüfen","vac.residual_message":"Die zugeordneten Räume weichen um bis zu {error} voneinander ab. Diese ungefähre Kalibrierung anwenden, manuell verfeinern oder ohne Änderung der gespeicherten Einrichtung abbrechen.","vac.apply_proposal":"Anwenden","gs.backup_group":"Sichern und übertragen","gs.backup_hint":"Laden Sie ein tragbares JSON-Backup herunter oder sehen Sie eine Vorschau und importieren Sie ein Backup, das mit House Plan erstellt wurde.","backup.export_open":"Exportieren","backup.import_open":"Importieren","backup.export_title":"House Plan exportieren","backup.import_title":"House Plan importieren","backup.export_hint":"Wählen Sie, ob das Backup die gesamte House Plan-Konfiguration oder nur den aktuellen Bereich enthalten soll.","backup.full":"Vollständiges Backup","backup.current_space":"Aktueller Bereich","backup.current_space_title":"Aktueller Bereich: {title}","backup.no_current_space":"Kein aktueller Bereich","backup.plan_only":"Nur Plan","backup.plan_only_hint":"Räume, Wände, Öffnungen, Dekor und Raumbezeichnungspositionen ohne Geräte oder Home Assistant-Bindungen beibehalten.","backup.plan_only_preview":"Diese Datei enthält nur den Plan.","backup.privacy_warning":"Das Archiv enthält Namen, Home Assistant-Kennungen und genaue Koordinaten. Interne Pläne und Anhänge werden referenziert, nicht eingebettet; Laufzeitzustände und Staubsaugerspuren sind nicht enthalten.","backup.download":"JSON herunterladen","backup.export_done":"Backup heruntergeladen","backup.reading":"Überprüfe das Backup…","backup.revalidated":"Der Plan wurde nach dieser Vorschau geändert. Die Zusammenfassung wurde aktualisiert; überprüfen Sie sie und bestätigen Sie erneut.","backup.error.unauthorized":"Sie haben keine Berechtigung, diesen Plan zu exportieren oder zu importieren.","backup.error.not_ready":"Der Hausplan ist noch nicht fertig. Versuchen Sie es in einem Moment erneut.","backup.error.too_large":"Das Backup überschreitet das 8-MiB-Limit.","backup.error.invalid_json":"Die ausgewählte Datei ist kein gültiges JSON.","backup.error.invalid_format":"Die ausgewählte Datei ist kein House Plan-Backup.","backup.error.unsupported_export_version":"Dieses Backup-Format wird von der installierten Version nicht unterstützt.","backup.error.future_model":"Das Backup wurde mit einem neueren House-Plan-Datenmodell erstellt.","backup.error.invalid_config":"Das Backup enthält eine ungültige House-Plan-Konfiguration.","backup.error.wall_model_migration_blocked":"Das Backup-Wandmodell konnte nicht sicher aktualisiert werden. Optimieren Sie zuerst den Quellplan und exportieren Sie ihn dann erneut.","backup.error.invalid_layout":"Das Backup enthält ungültige Objektpositionen.","backup.error.invalid_content":"Das Backup enthält ungültige oder inkonsistente Inhaltsreferenzen.","backup.error.space_not_found":"Der ausgewählte Bereich existiert nicht mehr.","backup.error.capacity_exceeded":"Durch das Hinzufügen dieses Backups würden die Planlimits überschritten.","backup.error.preview_expired":"Die Vorschau ist abgelaufen. Wählen Sie die Backup-Datei erneut aus.","backup.error.preview_owner_mismatch":"Diese Vorschau gehört zu einem anderen Home-Assistant-Benutzer.","backup.error.conflict":"Der Plan hat sich nach der Vorschau geändert. Überprüfen Sie die aktualisierte Zusammenfassung.","backup.error.content_confirmation_required":"Bestätigen Sie, dass nicht verfügbare lokale Inhalte getrennt werden können.","backup.error.commit_failed":"Das Backup konnte nicht sicher angewendet werden. Der vorherige Plan wurde wiederhergestellt oder wartet auf die Wiederherstellung.","backup.error.missing_plan":"Eine referenzierte Plan-Datei ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.missing_content":"Ein referenziertes lokales Anhangselement ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.marker_control_missing":"Ein verknüpftes Planlicht fehlt im Backup.","backup.error.marker_control_not_light":"Ein verknüpftes Planziel ist nicht mehr als Lichtquelle markiert.","backup.error.marker_control_self":"Eine Lichtquelle kann sich nicht selbst steuern.","backup.error.marker_control_cycle":"Das Backup enthält eine kreisförmige Kette von Lichtsteuerungen.","backup.error.duplicate_marker_control":"Das Backup enthält eine doppelte Plan-Licht-Verknüpfung.","backup.error.no_backup":"Es gibt keinen Import- oder Optimierungssnapshot zur Wiederherstellung.","backup.same_source":"Erstellt auf dieser Home Assistant-Instanz","backup.foreign_source":"Erstellt auf einer anderen Home Assistant-Instanz","backup.created":"Erstellt: {value}","backup.versions":"Karte {card}; Integration {integration}; Datenmodell {model}","backup.count_spaces":"Bereiche: {n}","backup.count_rooms":"Räume: {n}","backup.count_walls":"Wände: {n}","backup.count_openings":"Öffnungen: {n}","backup.count_decor":"Dekorationsobjekte: {n}","backup.count_markers":"Geräte: {n}","backup.count_layout":"Positionen: {n}","backup.bindings":"Verknüpfungen — Geräte: {device}, Entitäten: {entity}, virtuell: {virtual}; Positionen ohne Bereich: {legacy}","backup.binding_status":"Zielstatus — aktiv: {active}, deaktiviert: {disabled}, fehlt: {missing}","backup.missing_areas":"Auf dem Ziel fehlende Bereiche: {areas}","backup.dropped_marker_links":"Planlicht-Verknüpfungen außerhalb dieses übertragenen Bereichs wurden ausgelassen: {n}.","backup.repaired_target_refs":"Durch den Import dieses Bereichs wiederhergestellte vorhandene Referenzen: {n}.","backup.preserved_unresolved_refs":"Referenzen, die nicht eindeutig wiederhergestellt werden konnten, wurden beibehalten: {n}.","backup.preserved_unresolved_hint":"Es wurden keine Daten geraten oder gelöscht. Führen Sie nach dem Import Optimierungspläne aus, um die verbleibenden Referenzen zu überprüfen.","backup.import_details":"Importieren von Referenzdetails","backup.import_detail.incoming_remapped":"Referenzen innerhalb der importierten Kopie aktualisiert: {n}","backup.import_detail.target_repaired":"Vorhandene Referenzen wiederhergestellt: {n}","backup.import_detail.preserved_unresolved":"Unaufgelöste Referenzen beibehalten: {n}","backup.import_detail.collisions":"Zielkonflikte sicher beibehalten: {n}","backup.import_detail.dropped_links":"Eingehende Links durch Übertragungsregeln ausgelassen: {n}","backup.import_detail.bounded_lineages":"Übermäßig verschachtelte Kennungen begrenzt belassen: {n}","backup.replace_warning":"Dies ersetzt die aktuelle Konfiguration und das Layout. Hochgeladene Dateien werden niemals gelöscht. Eine Rückgängig-Möglichkeit bleibt bis zur nächsten Planbearbeitung verfügbar.","backup.foreign_bookkeeping":"Instanzspezifische bekannte/neue Gerätebuchführung wird nicht importiert.","backup.final_name":"Name des neuen Bereichs","backup.target_settings":"Die globalen Einstellungen der Zielinstanz bleiben unverändert.","backup.duplicates":"Bindungen bereits vorhanden: {n}","backup.skip":"Doppelte Geräte überspringen","backup.virtual_copy":"Sichere statische virtuelle Kopien hinzufügen","backup.content":"Referenzierter Inhalt","backup.content_available":"lokal verfügbar","backup.content_external":"externer Link","backup.content_detach_required":"wird getrennt","backup.confirm_detach":"Ich verstehe, dass nicht verfügbare interne Pläne und Anhänge von der importierten Konfiguration getrennt werden.","backup.replace":"Ersetzen","backup.add":"Bereich hinzufügen","backup.space_done":"Bereich importiert: {rooms} Räume, {markers} Geräte, {refs} bestehende Referenzen wiederhergestellt","backup.full_done":"Backup wiederhergestellt: {spaces} Bereiche, {rooms} Räume, {markers} Geräte","backup.undo_import":"Letzten vollständigen Import rückgängig machen","backup.import_undone":"Vollständiger Import rückgängig gemacht","backdrop.large_title":"Großes Bild","backdrop.large_body":"Dieses Bild ist {w}×{h} ({fileMb} MB Datei) und benötigt etwa {decodedMb} MB Speicher zur Anzeige. Auf Tablets kann das die Seite zum Absturz bringen.","backdrop.unknown_body":"Die Bildabmessungen konnten nicht gelesen werden — die Datei ist möglicherweise beschädigt. Fortfahren kann die Seite auf Tablets zum Absturz bringen.","backdrop.use_downscaled":"Verkleinerte Kopie hochladen","backdrop.reducing":"Wird verkleinert…","backdrop.keep_original":"Original behalten","backdrop.too_large_title":"Bild ist zu groß","backdrop.too_large_body":"Dieses Bild überschreitet die Anzeigegrenzen des Browsers ({w}×{h}, Limit {limit} px pro Seite). Bitte verkleinern Sie es in einem Desktop-Editor und laden Sie es erneut hoch.","backdrop.downscale_failed":"Die verkleinerte Kopie konnte nicht erstellt werden. Der aktuelle Plan wurde nicht geändert."};const n="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";export{e as dictionary,n as fingerprint}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/de-FazIOsg8.js b/custom_components/houseplan/frontend/houseplan-assets/de-FazIOsg8.js deleted file mode 100644 index f9b3bdc0..00000000 --- a/custom_components/houseplan/frontend/houseplan-assets/de-FazIOsg8.js +++ /dev/null @@ -1 +0,0 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";var e={"editor.context_actions":"Aktionen: {object}","editor.tool_options":"Werkzeugoptionen: {tool}","editor.palette":"Palette: {tool}","editor.open_group":"Werkzeuggruppe: {group}","editor.group_active":"{group} — aktiv: {item}","editor.disabled_action":"{action} ist nicht verfügbar: {reason}","editor.loading":"Editor wird geladen…","editor.loading_aria":"Der Plan-Editor wird geladen","editor.load_failed":"Der Editor konnte nicht geladen werden.","editor.refresh_advice":"Seite aktualisieren und erneut versuchen.","editor.retry_advice":"Verbindung prüfen und erneut drücken.","color_picker.title":"Farbwähler","color_picker.hue":"Farbton","color_picker.saturation":"Sättigung","color_picker.value":"Helligkeit","color_picker.hex":"Hex-Farbe","color_picker.invalid_hex":"Geben Sie eine 3- oder 6-stellige Hex-Farbe ein","btn.properties":"Eigenschaften","btn.keep_as_walls":"Als Wände behalten","btn.delete_room_keep_walls":"Raum löschen, Wände behalten","btn.delete_room_with_walls":"Raum und Wände löschen","title.markup_select":"Wände, Säulen und gespeicherte Umrisse auswählen und bearbeiten","title.markup_column":"Säule: Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","markup.partition":"Trennwand","markup.column":"Säule","markup.hint_column":"Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","history.draft_segment":"Raum-Entwurfsegment hinzufügen","history.draft_merge":"Unvollständige Raumumrisse verbinden","history.draft_segment_delete":"Raum-Entwurfssegment entfernen","history.partition_add":"Trennwand hinzufügen","history.column_add":"Säule hinzufügen","history.physical_edit":"Physisches Objekt ändern","history.physical_delete":"Physisches Objekt löschen","history.physical_move":"Physisches Objekt verschieben","history.contour_to_partitions":"Umriss in geschlossene Wände umwandeln","history.wall_chain_finish":"Wandkette fertigstellen","history.wall_face_batch":"Räume aus Wänden erstellen","toast.column_duplicate":"Eine Säule mit demselben Zentrum und äußerer Größe existiert bereits","confirm.delete_draft":"Sofort den gesamten unvollständigen Raumumriss löschen?","confirm.delete_draft_segment":"Dieses Entwurfssegment löschen? Der verbleibende Umriss könnte sich in zwei teilen.","physical.partition_properties":"Eigenschaften der Trennwand","physical.column_properties":"Eigenschaften der Säule","physical.draft_properties":"Eigenschaften des Entwurfssegments","physical.shape":"Form","physical.square":"Quadrat","physical.circle":"Kreis","physical.diameter":"Durchmesser","physical.side":"Seite","physical.rotation":"Drehwinkel","physical.length":"Länge","physical.allowed_range":"Erlaubt: {min}–{max} {unit}","physical.delete_segment":"Segment löschen","physical.delete_draft":"Gesamten Umriss löschen","physical.column_size_title":"Außenmaß der per Klick platzierten quadratischen Säule (1–150 cm).","card.title":"Hausplan","count.devices":"{n} Entwicklung","empty.no_spaces":"Noch keine Bereiche.","empty.add_first":"Ersten Bereich hinzufügen und einen Grundriss hochladen.","empty.install":"Installieren Sie die House Plan-Integration und fügen Sie sie unter „Geräte & Dienste“ hinzu.","btn.add_space":"Bereich hinzufügen","btn.cancel":"Abbrechen","btn.save":"Speichern","btn.close":"Schließen","btn.delete":"Löschen","btn.edit":"Bearbeiten","btn.open_in_ha":"In HA öffnen","btn.reset":"Zurücksetzen","btn.attach":"Anhängen…","btn.upload":"Hochladen…","btn.replace":"Ersetzen…","title.zoom_in":"Zoomen","title.zoom_out":"Herauszoomen","title.zoom_fit":"Alles anpassen","title.markup":"Raum-Markierung: Gitter, Linien, Konturen","title.configure_space":"Bereich konfigurieren","title.add_space":"Bereich hinzufügen","title.markup_add":"Wände: eine durchgehende Kette zeichnen; Shift sperrt auf 45°. Werkzeugwechsel oder Verlassen des Editors beendet die Kette und wandelt sie in unabhängige Wände","title.markup_merge":"Zusammenführen: Klicken Sie auf einen Raum und dann auf den Nachbarraum, mit dem er eine Wand teilt","title.markup_split":"Raum teilen: Klicken Sie auf den Raum und dann auf zwei Punkte an seinen Wänden","title.markup_delroom":"Raum löschen: Klicken Sie in einen Raum und bestätigen Sie","title.need_plan":"Einen Grundriss hochladen","markup.add":"Wände","markup.merge":"Zusammenführen","markup.split":"Teilen","markup.resize":"Größe ändern","title.markup_resize":"Räume vergrößern/verkleinern: Ziehen Sie einen verfügbaren horizontalen oder vertikalen Wandgriff","markup.hint_resize":"Ziehen Sie einen verfügbaren Wandgriff · die Wand stoppt an der ersten unsicheren Position · Esc bricht ab · Strg+Z — rückgängig","resize.disabled.diagonal":"Diese Wand ist geneigt; Größe ändern unterstützt nur horizontale und vertikale Wände","resize.disabled.side-angle":"Eine angrenzende Wand ist geneigt; Größe ändern erfordert, dass beide angrenzenden Wände diese Wand im rechten Winkel treffen","resize.disabled.duplicate-physical-wall":"Eine separate Trennwand, unfertige Kontur oder ein Pfeiler überlappt diese Wand; entfernen oder verschieben Sie sie, bevor Sie die Größe ändern","resize.disabled.partial-shared":"Ein angrenzender Raum nutzt nur einen Teil dieser Wand, daher kann sie nicht sicher als gemeinsame Wand verschoben werden","resize.disabled.unequal-shared":"Der angrenzende Raum nutzt andere Endpunkte oder Wandlängen, daher können die Wände nicht gemeinsam sicher bewegt werden","resize.disabled.multiple-rooms":"Das Verschieben dieser Wand würde mehr als zwei Räume betreffen","resize.disabled.thickness-conflict":"Die Wandstärke kann nicht sicher beibehalten werden","resize.disabled.opening-conflict":"Eine Öffnung an dieser Wand kann nicht sicher verschoben werden","resize.disabled.invalid-geometry":"Diese Wand kann nicht ohne Änderung der Planstruktur geändert werden","resize.limit_stopped":"Wand gestoppt: der nächste Schritt würde ein Anschlusslimit verletzen","resize.commit_failed":"Größenänderung wurde abgebrochen, da der endgültige Plan die Geometrieprüfung nicht bestanden hat","resize.preview_failed":"Größenänderung am letzten sicheren Punkt gestoppt, da die Plan-Geometrie nicht beibehalten werden konnte","markup.opening":"Öffnung","markup.delete_room":"Raum löschen","history.undo":"Rückgängig","history.redo":"Wiederholen","history.undo_named":"Rückgängig: {name} (Strg+Z)","history.redo_named":"Wiederholen: {name} (Strg+Shift+Z / Strg+Y)","history.undo_empty":"Nichts zu widerrufen","history.redo_empty":"Nichts zu wiederholen","history.undone":"Rückgängig gemacht: {name}","history.redone":"Wiederholt: {name}","history.add_room":"Raum erstellen","history.split_room":"Raum teilen","history.merge_rooms":"Räume zusammenführen","history.resize_room":"Raumgröße ändern","history.wall_thickness":"Wandstärke ändern","history.add_opening":"Tür- oder Fensteröffnung hinzufügen","history.edit_opening":"Öffnung bearbeiten","history.move_opening":"Öffnung verschieben","history.delete_opening":"Öffnung löschen","history.delete_room":"Raum löschen","history.delete_room_keep_walls":"Raum löschen, Wände behalten","history.delete_room_with_walls":"Raum und Wände löschen","history.decor_add":"Dekorobjekt hinzufügen","history.decor_edit":"Dekorobjekt bearbeiten","history.decor_move":"Dekorobjekt verschieben","history.decor_transform":"Dekorobjekt transformieren","history.decor_delete":"Dekorobjekt löschen","history.backdrop_transform":"Plan-Hintergrund transformieren","opening.new":"Neue Öffnung","opening.edit":"Öffnung","opening.door":"Tür","opening.window":"Fenster","opening.gate":"Tor","opening.passage":"Offener Durchgang","opening.passage_binding_warning":"Das Speichern entfernt den Öffnungs-/Schließsensor und das Schloss.","opening.invalid_passage_fields":"Der offene Durchgang auf dem Plan „{room}“ hat nicht unterstützte Einstellungen: {fields}.","opening.type_label":"Typ","opening.length_label":"Länge, cm","opening.contact_label":"Öffnungs-/Schließsensor","opening.lock_label":"Schloss","opening.none":"— keine —","opening.search_ph":"Suche: Name oder Entitäts-ID…","opening.invert":"Geöffnet/geschlossen invertieren","opening.flip_h":"Scharnier an der anderen Zarge","opening.flip_v":"Öffnet zur anderen Seite","opening.open":"Offen","opening.closed":"Geschlossen","opening.locked":"Verriegelt","opening.unlocked":"Entriegelt","opening.state_unknown":"nicht verfügbar","opening.no_entities":"Keine Sensoren gebunden — ein statisches Symbol auf dem Plan.","toast.opening_no_wall":"Neben eine Raumwand oder eine unabhängige Wand klicken","opening.host_partition":"Unabhängige Wand","opening.partition_orphan":"Die unabhängige Wand für diese Öffnung existiert nicht mehr","opening.partition_jamb_margin":"Mindestens {distance} zwischen der Öffnung und dem Wandende lassen","opening.rebind_partition":"An eine andere unabhängige Wand anfügen","confirm.delete_partition_openings_title":"Wand und Öffnungen löschen?","confirm.delete_partition_openings_body":"Diese Wand enthält {count} Öffnung(en). Sie werden zusammen gelöscht.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Löschen","markup.hint_points":"Punkte: {n} · Shift — 45° Schritte · Esc — Kette beenden · Ctrl+Z — Punkt rückgängig machen · Schließen eines Bereichs erstellt einen Raum","markup.hint_start":"Klicken Sie auf einen Rasterpunkt, um eine Wandkette zu starten","tip.lqi":"durchschnittliches Zigbee-Signal:","tip.area":"Bereich: {value}","info.device_header":"Gerät im Plan","info.model":"Modell","info.state":"Status","info.link":"Verbindung","info.manuals":"Handbücher","info.none":"Keine zusätzlichen Informationen","marker.new_device":"Neues Gerät","marker.name_label":"Name (im Plan angezeigt)","marker.name_ph":"Name","marker.binding_label":"An ein HA-Gerät binden","marker.binding_disabled":"in Home Assistant deaktiviert","marker.virtual_option":"Virtuelles Gerät (keine Bindung)","marker.search_ph":"Gerät/Gruppe suchen…","marker.nothing_found":"Nichts gefunden","marker.room_label":"Raum","marker.room_override":" (Platzierung überschreiben)","marker.room_choose":"— Raum auswählen —","marker.room_auto":"— nach Gerätebereich (auto) —","marker.icon_label":"Symbol","marker.icon_ph":"mdi:… (leer = automatisch)","marker.display_label":"Anzeige","display.badge":"Symbol + Zustand","display.icon_ripple":"Symbol + Zustand und Aktivität","display.static_icon":"Immer statisches Symbol","marker.display_hint":"Symbol + Zustand ändert die Platte für die Zustände Arbeit, geöffnet, Alarm und nicht verfügbar. Symbol + Zustand und Aktivität zeigt zusätzlich einen kurzen Impuls für Ereignisse und einen kontinuierlichen Impuls für laufende Arbeit, Bewegung oder Präsenz. Wert + Zustand ersetzt das Symbol durch einen eindeutigen HA-Wert. Ein statisches Symbol reagiert niemals auf Gerätezustände.","marker.display_hint_badge":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon_ripple":"Das Symbol, die dynamische Platte und der Puls zeigen einen kurzen Puls für Ereignisse und einen kontinuierlichen Puls für Arbeit, Bewegung oder Anwesenheit. Rote Alarme haben eine separate Priorität.","marker.display_hint_value":"Ein eindeutiger Home Assistant-Wert ersetzt das Symbol, während die Platte weiterhin den Zustand anzeigt. Rote Alarme bleiben sichtbar.","marker.display_hint_static_icon":"Die themenabhängige Hülle und das Symbol bleiben immer gleich. Zustand, Aktivität, Nichtverfügbarkeit und Alarme verändern nicht das Erscheinungsbild.","marker.static_alarm_warning":"Dieses Gerät kann Alarme melden. Die statische Anzeige verbirgt die visuelle Alarmanzeige des Markers.","marker.preview.title":"Anzeigevorschau","marker.preview.actual":"Jetzt","marker.preview.example":"Beispiel","marker.preview.integration":"Bereitgestellt von","marker.preview.source":"Anzeigquelle","marker.preview.current_state":"Aktueller Zustand","marker.preview.result":"Auf dem Plan","marker.preview.details":"Quelldetails","marker.preview.select_source":"Ein Home-Assistant-Gerät oder eine Entität auswählen, um die tatsächliche Anzeige zu sehen.","marker.preview.unknown_provider":"Unbekannte Integration","marker.preview.virtual_provider":"House Plan · virtuelles Gerät","marker.preview.no_source":"Keine aktive Quelle","marker.preview.no_state":"Kein aktueller Zustand","marker.preview.mixed_states":"Mehrere verschiedene Zustände","marker.preview.multiple_sources":"{n}-Quellen","marker.preview.more_sources":"+{n} mehr","marker.preview.scaled":"Die Vorschau wird auf {n}% skaliert, um zu passen. Die gespeicherte Größe bleibt unverändert.","marker.preview.demo_short":"Kurze Aktivität anzeigen","marker.preview.demo_continuous":"Kontinuierliche Aktivität anzeigen","marker.preview.stop_continuous":"Kontinuierliche Aktivität stoppen","marker.preview.demo_short_notice":"Kurzes Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_continuous_notice":"Kontinuierliches Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_already_visible":"Der aktuelle Zustand zeigt bereits eine echte Aktivitäts- oder Alarmeffekt.","marker.preview.reduced_motion":"Systemweit reduzierte Bewegungen sind aktiviert, daher wird gewöhnliche Aktivität als Punkt angezeigt.","marker.preview.reason.neutral":"Neutrale dunkle Platte","marker.preview.reason.working":"Gelbe Platte: Das Gerät arbeitet jetzt","marker.preview.reason.working_activity":"Gelbe Platte und Aktivitätseffekt: das Gerät funktioniert jetzt","marker.preview.reason.open":"Orange Platte: physisch geöffnet oder entriegelt","marker.preview.reason.cover_icon_state":"Neutrale Platte; die Abdeckungseinheit steuert den angezeigten Zustand und das Symbol","marker.preview.reason.presence":"Aktivitätseffekt, während Präsenz erkannt wird","marker.preview.reason.event":"Kurzer Aktivitätseffekt nach einem erkannten Ereignis","marker.preview.reason.transition":"Aktivitätseffekt, während das Gerät sich bewegt oder den Zustand ändert","marker.preview.reason.media_neutral":"Mediengeräte bleiben im verfügbaren Zustand auf einer neutralen dunklen Platte","marker.preview.reason.unavailable":"Gedämpfte neutrale Platte: nicht verfügbar oder ausgeschaltet","marker.preview.reason.alarm":"Rote Alarmplatte; Alarmanzeige wird immer angezeigt","marker.preview.reason.live_states_disabled":"Live-Zustandsstil ist für diese Karte deaktiviert","marker.preview.reason.value_no_state":"Kein nutzbarer Zustandswert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_ambiguous_sources":"Mehrere mögliche Wertequellen; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_non_scalar":"Die Quelle lieferte keinen einfachen Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_virtual":"Ein virtuelles Gerät hat keinen Home Assistant-Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.vacuum_live_plan_only":"Echtzeitposition und -spur des Staubsaugers sind nur im vollständigen Plan verfügbar","marker.preview.reason.hidden_design_preview":"Das Gerät ist im Plan ausgeblendet; diese Designvorschau bleibt sichtbar","marker.preview.reason.composite_power_source":"Der Status stammt vom Power-Entity des Geräts; Hilfsschalter werden ignoriert","marker.preview.reason.activity_display_disabled":"Der gewählte Anzeigemodus zeigt gewöhnliche Aktivitätseffekte nicht an","marker.preview.reason.ha_disabled":"Die Home Assistant-Verbindung ist deaktiviert und wird im Plan ausgeblendet","marker.preview.reason.orphaned":"Die gespeicherte Home Assistant-Verbindung konnte nicht mehr gefunden werden","marker.preview.reason.static_icon":"Statischer Modus: Der Gerätestatus ändert das Symbol nicht","marker.activity_color":"Aktivitätspulsfarbe","marker.ripple_size":"Aktivitätspulsgröße","marker.activity_alarm_note":"Farbe und Größe betreffen nur gewöhnliche Aktivität und ändern keine roten Alarme.","marker.pulse_a11y_alarm":"Alarm","marker.pulse_a11y_event":"Kürzliches Ereignis","marker.pulse_a11y_presence":"Anwesenheit erkannt","marker.pulse_a11y_transition":"Statusänderung","marker.pulse_a11y_running":"Arbeitet","marker.state_a11y_neutral":"Standardzustand","marker.state_a11y_working":"Funktionierend","marker.state_a11y_open":"Offen","marker.state_a11y_locked":"Verschlossen","marker.state_a11y_unlocked":"Entriegelt","marker.state_a11y_alarm":"Alarm","marker.state_a11y_unavailable":"Nicht verfügbar","marker.lqi_a11y_low":"LQI {value}, schwaches Signal","marker.lqi_a11y_mid":"LQI {value}, mittleres Signal","marker.lqi_a11y_high":"LQI {value}, starkes Signal","marker.size_label":"Symbolgröße / Drehung","marker.angle_label":"Drehen","marker.model_label":"Modell","marker.model_ph":"z.B. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Beschreibung","marker.desc_ph":"Notizen, Spezifikationen…","marker.manuals_label":"Handbücher (PDF usw.)","marker.sub_device":"Gerät","marker.sub_z2m_group":"· Z2M Gruppe","marker.sub_group":"Gruppe","marker.sub_helper":"Helfer","space.new":"Neuer Bereich","space.header":"Bereich","space.title_label":"Titel","space.title_ph":"z.B. Garage","space.plan_label":"Grundriss (Hintergrund)","space.no_plan":"Kein Planbild","space.plan_alt":"Grundriss","room.new":"Neuer Raum","room.name_label":"Anzeigename","room.name_ph":"z.B. Terrasse","room.area_label":"Home Assistant Bereich (nicht zugewiesen)","room.no_area_option":"— kein Bereich —","room.default_name":"Raum","device.unnamed":"unbenannt","device.light_group":"Lichtgruppe","device.fallback":"Gerät","device.virtual":"Virtuelles Gerät","confirm.delete_room":'Raum "{name}" löschen?',"confirm.delete_room_title":'Raum "{name}" löschen?',"confirm.delete_room_body":"Festlegen, ob die ausschließlich zu diesem Raum gehörenden physischen Wände erhalten bleiben. Mit einem anderen Raum geteilte Wände bleiben immer erhalten.","confirm.remove_marker":"„{name}“ aus dem Plan löschen? Das Gerät verschwindet vollständig und trägt nicht mehr zu den Plandaten bei. Es kann später erneut hinzugefügt werden.","confirm.erase_decor":"Das Objekt „{kind}“ löschen? Diese Aktion kann im Editor rückgängig gemacht werden.","confirm.delete_space":"Bereich „{title}“ mit allen Räumen und Markierungen löschen?","space.delete_blocked":"Dieser Bereich wird noch von {n} Gerät(en) verwendet. Die Geräte zuerst in einen anderen Bereich verschieben oder löschen.","toast.pos_save_failed":"Position nicht gespeichert: {err}","toast.no_entity":"Das Gerät hat keine geeignete Entität","toast.ha_disabled_action":"Ein deaktiviertes Home Assistant-Objekt kann im Plan nicht verwendet werden.","toast.ha_disabled_show_device":"Ein in Home Assistant deaktiviertes Gerät kann im Plan nicht angezeigt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_disabled_show_entity":"Eine in Home Assistant deaktivierte Entität kann im Plan nicht angezeigt werden. Aktivieren Sie sie zuerst in Home Assistant.","toast.ha_disabled_add":"Ein deaktiviertes Home Assistant-Objekt kann nicht dem Plan hinzugefügt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_binding_unverified":"Der Status des Objekts konnte über das Home Assistant-Register nicht überprüft werden. Anzeige und Aktionen sind vorübergehend nicht verfügbar.","toast.markup_needs_server":"Markup ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde.","toast.space_order_changed":"Reihenfolge geändert. Wenn eine Karte ihr Stockwerk nach Nummer festlegt, überprüfen Sie diese Paneele.","toast.conflict":"Die Konfiguration wurde in einem anderen Fenster geändert — Daten aktualisiert, wiederholen Sie Ihre letzte Aktion.","toast.cfg_save_failed":"Speichern der Konfiguration fehlgeschlagen: {err}","toast.room_overlap":"Die Umrisse überlappen den Raum „{name}“ — Räume dürfen sich nicht überlappen","toast.contour_cannot_close":"Die Kontur kann nicht geschlossen werden, da sie entartet ist oder sich selbst schneidet","toast.merge_not_adjacent":"Es können nur Räume zusammengeführt werden, die eine Wand teilen","toast.rooms_merged":"Räume zu „{name}“ zusammengeführt","toast.split_pick_wall":"Beginnen Sie den Schnitt an der Wand des Raumes","toast.split_bad_cut":"Der Schnitt muss wand-zu-wand innerhalb des Raumes verlaufen, ohne Wände oder sich selbst zu kreuzen","merge.header":"Räume zusammenführen","merge.hint":"Der zusammengeführte Raum behält einen Namen und eine Fläche. Die andere Fläche wird freigegeben — ihre Geräte verlassen den Plan, bis ein anderer Raum sie beansprucht.","merge.keep":"Behalten","merge.no_area":"keine Fläche","toast.room_saved":"Raum gespeichert ({n}). Hinzugefügte Geräte: {added}. Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.room_saved_no_area":"Raum gespeichert ({n}, keine Fläche). Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.marker_needs_server":"Die Gerätebearbeitung ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde","toast.virtual_name_required":"Einen Namen für das virtuelle Gerät eingeben","toast.marker_saved":"Gerät gespeichert","toast.marker_removed":"Gerät aus dem Plan gelöscht","toast.integration_missing":"Die House Plan-Integration ist nicht installiert — Verwaltung nicht verfügbar","toast.plan_formats":"Unterstützte Formate: SVG, PNG, JPG, WebP","toast.plan_required":"Grundriss hochladen — erforderlich","toast.space_added_onboard":"Bereich hinzugefügt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","toast.space_added":"Bereich hinzugefügt","toast.space_saved":"Bereich gespeichert","toast.space_deleted":"Bereich gelöscht","toast.delete_failed":"Löschen fehlgeschlagen: {err}","toast.error":"Fehler: {err}","toast.toggle_target_unavailable":"Ziel „{name}“ ist nicht verfügbar — keine Aktion durchgeführt","toast.toggle_targets_unavailable":"Ziele nicht verfügbar: {names}. Keine Aktion durchgeführt","toast.file_failed":'Datei "{name}" wurde nicht hochgeladen: {err}',"toast.files_attached":"Dateien angehängt: {n}","err.unknown":"unbekannter Fehler","err.code":"Code {code}","err.too_large":"Datei größer als {mb} MB","err.bad_ext":"nicht unterstützter Typ (PDF/Bild erwartet)","err.unauthorized":"Administratorrechte erforderlich","editor.title":"Titel","editor.default_floor":"Anfangsbereich","editor.default_floor_missing":"Anfangsbereich „{id}“ existiert nicht mehr. Wählen Sie einen anderen Bereich.","editor.floor":"Fester Bereich","editor.floor_none":"— nicht fest —","editor.floor_index":"YAML-Index {index} (beibehalten)","fixed_floor.loading":"Lade den festen Bereich…","fixed_floor.invalid_title":"Der feste Bereich ist nicht verfügbar","fixed_floor.invalid_body":"Überprüfen Sie die Etage in der Kartenkonfiguration. Der konfigurierte Wert ist: {value}","editor.icon_size":"Symbolgröße, % der Planbreite","editor.show_temperature":"Temperatur anzeigen","editor.live_states":"Live-Zustände (ein/aus, offen…)","editor.light_pools":"Lichtkegel und Wandschatten","editor.show_signal":"Zigbee-Signal anzeigen (LQI)","editor.language":"Schnittstellensprache","editor.lang_auto":"Automatisch (HA-Profil)","title.icon_rules":"Symbolregeln: welche MDI-Symbole Geräte nach Namen erhalten","rules.title":"Symbolregeln","rules.hint":'Regeln werden von oben anhand von "Gerätename + Modell" (Großschreibungs-unempfindliches Regex) überprüft; das erste Match gewinnt. Wenn nichts übereinstimmt, entscheidet die Entität-Geräteklasse, dann das generische Chip-Symbol.',"rules.pattern_ph":"regex, z. B. Stecker|Steckdose","rules.icon_ph":"mdi:power-socket-de","rules.add":"Regel hinzufügen","rules.reset":"Zurücksetzen auf Standardeinstellungen","rules.test_ph":"Versuch mal einen Gerätenamen...","rules.invalid":"ungültiges regex","rules.saved":"Icon-Regeln gespeichert","btn.up":"Nach oben","btn.down":"Nach unten","tap.info":"Gerätekarte","tap.more_info":"HA Mehr-Info-Dialog","tap.toggle":"Zustand umschalten","marker.tap_label":"Tap-Aktion für dieses Gerät","tap.toggle_note":"Der aufgelöste Zustand ist unten dargestellt. Sichere Geräte werden niemals aus dem Plan umgeschaltet.","import.title":"Bereiche aus HA-Etagen erstellen","import.hint":"Home Assistant kennt diese Etagen bereits. Die Etagen auswählen, die zu Planbereichen werden sollen; anschließend für jeden Bereich einen Grundriss hochladen. Die Räume werden danach von Hand eingezeichnet.","import.start":"{n} Bereich(e) erstellen","import.manual":"Von Grund auf neu starten","import.progress":"Etage {i} von {n}","import.done":"Bereiche erstellt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","btn.skip":"Überspringen","space.scale_label":"Maßstab (Rasterzellen-Größe)","space.scale_unit":"cm pro Zelle","space.scale_unit_imperial":"Zoll pro Zelle","space.display_section":"Anzeige","space.show_borders":"Raumgrenzen immer anzeigen","space.zero_wall_style":"Wände ohne Dicke","space.zero_wall_dashed":"Gestrichelt","space.zero_wall_solid":"Durchgezogen","space.zero_wall_help":"Gestrichelte Wände lassen Licht durch. Durchgezogene Wände blockieren Licht auch bei einer Dicke von 0.","space.show_names":"Raumnamen anzeigen (zum Verschieben ziehen)","space.room_color":"Farbe der Begrenzung & des Namens","space.opacity":"Deckkraft","space.fill_label":"Raumausfüllung","fill.none":"Keine","fill.lqi":"Zigbee-Signal","fill.light":"Lichter","fill.custom":"Benutzerdefinierte Farbe","space.custom_fill":"Füllfarbe","space.source_file":"Ich habe einen Grundriss als Bild","space.source_draw":"Kein Bild – ich werde die Räume von Hand umreißen","fill.temp":"Temperatur","tip.temp_avg":"Durchschnittstemperatur:","tip.hum_avg":"Durchschnittliche Luftfeuchtigkeit:","space_card.button":"Grundriss öffnen","space_card.not_found":"Bereich „{id}“ nicht gefunden","space_card.loading":"Laden…","continuity.restore_plan":"Grundriss wiederherstellen…","continuity.restore_connection":"Geräteverbindung wiederherstellen…","continuity.retry":"Erneut versuchen","editor.space":"Bereich","editor.framing":"Bildausschnitt","editor.fit_content":"Alle sichtbaren Inhalte","editor.fit_house":"Eng an der Hausgeometrie","editor.show_button":"Schaltfläche anzeigen","editor.button_label":"Beschriftung der Schaltfläche","editor.button_target":"Ziel-Dashboard-Pfad","marker.sub_entity":"Entität","title.general_settings":"Allgemeine Einstellungen","gs.title":"Allgemeine Einstellungen","gs.hint":"Füllfarben gelten für jeden Bereich; jede Farbe hat ihre eigene Deckkraft. Der Füllmodus wird in den Einstellungen des jeweiligen Bereichs festgelegt.","gs.light_group":"Füllung: Licht","gs.light_on":"Lichter an","gs.light_off":"Alle Lichter aus","gs.temp_group":"Füllung: Temperatur","gs.temp_cold":"Kalt","gs.temp_ok":"Angenehm","gs.temp_hot":"Heiß","gs.lqi_group":"Füllung: Zigbee-Signal","gs.lqi_low":"Schwaches Signal","gs.lqi_high":"Starkes Signal","gs.reset":"Auf Standard zurücksetzen","gs.saved":"Allgemeine Einstellungen gespeichert","space.show_lqi":"Zigbee-Signal (LQI) neben Geräten anzeigen","space.hide_decor":"Dekorationsschicht ausblenden","space.hide_decor_tip":"Linien, Formen, Beschriftungen und Möbel bleiben an ihrem Platz — im Hintergrundeditor sichtbar, aber nicht auf dem Plan.","space.hide_openings":"Öffnungen ausblenden","space.hide_openings_tip":"Tür-, Fenster- und Tor-Symbole werden nicht gezeichnet, aber die Öffnungen funktionieren weiterhin: Licht fällt hindurch, die Sonne kommt durch ein Fenster herein, Kontaktsensoren öffnen sie weiterhin. Der Planeditor zeigt sie immer an.","gs.light_none":"Keine Lichtquellen","mode.plan":"Planeditor","mode.devices":"Geräteeditor","display.value":"Wert + Zustand","marker.subarea":"kein Bereich, manuell","device.new":"Neues Gerät — Editor öffnen, um zu schließen","opening.unlock_action":"Entsperren","opening.lock_action":"Sperren","opening.lock_pending":"Wird ausgeführt…","title.close_editor":"Editor schließen (zur Ansicht zurück)","title.add_device":"Gerät zum Plan hinzufügen","devbar.add":"Hinzufügen","devbar.rules":"Symbolregeln","device_inbox.button":"Geräte","device_inbox.title":"Geräte auf dem Plan","device_inbox.search":"Geräte, Entitäten und Integrationen suchen…","device_inbox.add_virtual":"Virtuelles Gerät hinzufügen","device_inbox.tab_on_plan":"Auf Plan","device_inbox.tab_available":"Verfügbar","device_inbox.tab_hidden":"Versteckt","device_inbox.tab_readd":"Wieder verfügbar","device_inbox.only_new":"Nur neu","device_inbox.show_entities":"Entitäten anzeigen","device_inbox.show_hidden":"Versteckte auf Plan anzeigen","device_inbox.new":"Neu","device_inbox.find":"Auf dem Plan finden","device_inbox.edit":"Bearbeiten","device_inbox.hide":"Ausblenden","device_inbox.show":"Anzeigen","device_inbox.add":"Hinzufügen","device_inbox.readd":"Wieder hinzufügen","device_inbox.hide_available":"Aus der Liste ausblenden","device_inbox.show_more":"Mehr anzeigen","device_inbox.more_actions":"Weitere Aktionen","device_inbox.find_hidden_hint":"Zuerst „Versteckte auf Plan anzeigen“ aktivieren","device_inbox.show_disabled":"Diese Bindung in Home Assistant aktivieren, bevor sie angezeigt wird","device_inbox.empty_on_plan":"Es befinden sich noch keine passenden Geräte auf dem Plan.","device_inbox.empty_available":"Es sind keine Geräte zum Hinzufügen verfügbar.","device_inbox.empty_hidden":"Es gibt keine versteckten Geräte.","device_inbox.empty_readd":"Es sind keine entfernten Geräte verfügbar, um sie erneut hinzuzufügen.","device_inbox.reason_visible_auto":"Automatisch erkannt","device_inbox.reason_visible_explicit":"Explizit hinzugefügt","device_inbox.reason_manual_hidden":"Vom Benutzer versteckt","device_inbox.reason_automatic_hidden":"Automatisch versteckt","device_inbox.reason_service_entry":"Nur Service-Registry-Eintrag","device_inbox.reason_excluded_integration":"Integration durch Gerätefilter ausgeschlossen","device_inbox.reason_excluded_domain":"Nicht-räumlicher Entitätstyp","device_inbox.reason_grouped_light":"Dargestellt durch eine Raumlichtgruppe","device_inbox.reason_represented_by_parent":"Dargestellt durch sein übergeordnetes Gerät","device_inbox.reason_removed":"Zuvor aus dem Plan entfernt","device_inbox.reason_available":"Zum Hinzufügen zum Plan verfügbar","device_inbox.reason_no_bound_room":"HA-Bereich ist nicht mit einem Planraum verknüpft","device_inbox.status_ha_disabled":"In Home Assistant deaktiviert","device_inbox.status_orphaned":"Bindung ist in Home Assistant nicht mehr vorhanden","device_inbox.status_unverified":"Home Assistant-Register vorübergehend nicht verfügbar","device_inbox.saved":"Geräteliste aktualisiert","space.roomcard_section":"Raumkarte zeigt:","space.label_temp":"Temperatur","space.label_hum":"Luftfeuchtigkeit","space.label_lqi":"Durchschnittliches Zigbee-Signal","space.label_light":"Lichter ein/aus","roomcard.light_on":"Ein","roomcard.light_off":"Aus","roomcard.light_partial":"{on} von {total}","toast.split_pick_inside":"Zwischenliegende Schnittpunkte müssen innerhalb des Raumes liegen","mode.decor":"Hintergrundeditor","decor.select":"Auswählen","decor.line":"Linie","decor.rect":"Rechteck","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Löschen","decor.erase_confirm_title":"Objekt löschen?","decor.color":"Farbe","decor.width":"Linienbreite","decor.line_style":"Linienstil","decor.line_style_solid":"Durchgehend","decor.line_style_dashed":"Gepunktet","decor.fill":"Füllen","decor.fill_color":"Füllfarbe","decor.length":"Länge","decor.size":"Größe","decor.angle":"Rotationswinkel, °","decor.text_size":"Textgröße","decor.backdrop_properties":"Eigenschaften des Planhintergrunds","decor.text_title":"Textbeschriftung","decor.object_title":"{kind} bearbeiten","decor.text_label":"Text","decor.live_group":"HA-Variable einfügen","decor.live_entity":"Entität","decor.live_entity_ph":"Wählen Sie eine Entität","decor.live_attr":"Wert","decor.live_attr_ph":"Wählen Sie Status oder Attribut","decor.live_state":"Status","decor.backdrop":"Hintergrundbild","decor.backdrop_hint":"Ziehen zum Verschieben; eine Ecke ziehen zum Größenändern; den oberen Griff zum Drehen verwenden. Shift ändert das Verhältnis oder befreit den Winkel.","decor.backdrop_reset":"Bild zurücksetzen","decor.backdrop_reset_done":"Das Bild ist wieder an seinem ursprünglichen Platz und in seiner ursprünglichen Größe","marker.icon_auto":"Auto: {icon} (nach Symbolregeln; wählen Sie eines zur Überschreibung)","marker.icon_pin_auto":"Anheften","mode.plan_tip":"Planungseditor — die Geometrie des Hauses: Räume zeichnen und teilen/zusammenführen, sie mit HA-Bereichen verbinden, Türen, Fenster und Tore platzieren, Raumkarten verschieben, Maßstab festlegen","mode.devices_tip":"Geräteeditor — alles über Symbole: ziehen zum Positionieren, klicken zum Bearbeiten von Bindung/Symbol/Anzeige, virtuelle Geräte hinzufügen, Symbolregeln","mode.decor_tip":"Hintergrundeditor — rein visuelle Dekoration unter dem Plan: Linien, Rechtecke, Ellipsen und Textbeschriftungen, die niemals auf Klicks reagieren","space.glow_enabled":"Lichtquellen-Glühen","gs.glow_group":"Glow von Lichtquellen","gs.glow_base":"Grunddunkelheit des Hauses","gs.glow_light":"Standardlichtfarbe / Intensität","gs.wall_group":"Wände","gs.wall_fill":"Wandfüllung","gs.glow_radius":"Glow-Radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Steuert andere Lichtquellen","marker.controls_hint":"Mit der Tippen-Aktion „Umschalten“ wechselt ein Klick jede hier hinzugefügte Quelle. Die eigene zugeordnete Entität des Markers wird direkt gesteuert und kann nicht hinzugefügt werden; verwenden Sie den untenstehenden Schalter, um ihn als Licht zu klassifizieren.","marker.controls_filter":"Suchlichter und Schalter…","info.controls":"Steuerungen","marker.glow_radius_label":"Leuchtradius","marker.glow_radius.help":"Legt den Leuchtradius in Metern oder Fuß fest; ein leerer Wert verwendet den Radius aus den allgemeinen Einstellungen.","marker.glow_radius.help.aria":"Hilfe: Leuchtradius","markup.wallthick":"Dicke","markup.select":"Auswählen","title.markup_wallthick":"Dicke — klicken Sie auf eine Wand, um ihre Dicke von 0 bis 100 cm festzulegen.","markup.hint_wallthick":"klicken Sie auf eine Wand · Esc schließt ohne Anwendung","wallthick.field":"Dicke","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Auf alle Wände dieses Raums anwenden","markup.draw_wall_title":"Dicke jedes neuen Wandsegments (0–100 cm). Gemeinsame Wände behalten den bestehenden Wert.","room.queue_progress":"Raum {current} von {total}","toast.wall_rooms_saved":"Erstellte Räume: {n}","toast.wall_chain_saved":"Wandkette gespeichert","toast.wallthick_pick":"Eine Wand anklicken","toast.wallthick_set":"Wandstärke eingestellt","toast.wallthick_cleared":"Wandstärke entfernt","toast.physical_range":"Einen Wert von {min} bis {max} {unit} eingeben","toast.zero_wall_opening_conflict":"Entfernen Sie zuerst die Öffnung an diesem Wandsegment.","toast.zero_wall_ambiguous":"Das Wandsegment ist mehrdeutig. Vereinfachen oder passen Sie den Anschluss an.","toast.zero_wall_migration_blocked":"Der Bereich wurde nicht konvertiert: {reason}. Es wurden keine Daten geändert.","toast.physical_angle":"Einen Drehwinkel von 0° bis unter 90° eingeben","toast.physical_limit":"Der Bereich hat das Limit für diesen Geometrietyp erreicht","toast.geometry_unsafe":"Änderung abgebrochen: Wandgeometrie konnte nicht sicher erstellt werden.","junction.limit_angle":"Der Winkel zwischen den Wänden ist zu spitz: {actual}°, mindestens {limit}°.","junction.limit_valence":"Zu viele Wände treffen in einem Knoten aufeinander: {actual}, maximal {limit}.","junction.limit_length":"Das Wandsegment ist zu kurz: {actual} cm, mindestens {limit} cm.","junction.limit_distance":"Wände und Knoten sind zu dicht beieinander: {actual} cm, Minimum {limit} cm.","junction.limit_clearance":"Kein Wohnbereich ist übrig: {actual} cm², Minimum {limit} cm².","junction.limit_check_failed":"Die Überprüfung der Verbindungen konnte nicht durchgeführt werden — die Änderung wurde nicht gespeichert.","toast.wall_model_migration_blocked":"Das Wandmodell konnte nicht aktualisiert werden: {reason}. Der Plan wurde nicht geändert. Führen Sie „Pläne optimieren“ aus; wenn der Fehler erneut auftritt, beheben Sie die widersprüchliche Wandgeometrie.","toast.wall_model_client_outdated":"Aktualisieren Sie die Karte und laden Sie die Seite neu, bevor Sie den Plan bearbeiten.","wall_model.reason.invalid-room":"ungültiger Raumkontur","wall_model.reason.zero-length":"Wandsegment mit null Länge","wall_model.reason.third-owner":"Wand, die von mehr als zwei Räumen geteilt wird","wall_model.reason.duplicate-id":"widersprüchliche Wandkennungen","wall_model.reason.thickness-conflict":"widersprüchliche Wandstärkenwerte","wall_model.reason.opening-host":"Öffnung kann nicht einer Wand zugewiesen werden","toast.delete_room_pick":"Klicken Sie in den Raum, den Sie löschen möchten","toast.plan_snap_ambiguous":"Zoomen Sie hinein, um den Wandknoten auszuwählen","toast.wall_repair_ambiguous":"Der Raumumriss hat mehr als eine mögliche Verbindung. Zoomen Sie hinein und verbinden Sie ihn explizit.","toast.wall_repair_too_large":"Die Lücke ist größer als 2 cm. Verbinden Sie die Wände, bevor Sie den Raum erstellen.","toast.wall_repair_changed":"Die Wandgeometrie hat sich geändert. Versuchen Sie erneut, den Raum zu erstellen.","toast.opening_on_zero_wall":"Öffnungen können nicht auf einer Wand mit null Dicke sitzen.","marker.from_ha_option":"Wählen Sie aus der HA-Liste.","marker.show_entities":"Entitäten anzeigen.","marker.show_entities_tip":"Fügt nicht nur Geräte zur Liste hinzu, sondern auch alle ihre Entitäten.","marker.pick_ph":"Wählen Sie ein Gerät…","room.open_area":"Öffnen Sie den HA-Bereich.","view.volumetric":"Volumetrische Ansicht.","view.flat":"Flache Ansicht.","kiosk.title":"Die Größen dieses Bildschirms.","kiosk.hint":"Nur auf diesem Gerät gespeichert – jedes Wandtablet oder TV kann seine eigenen komfortablen Größen haben.","kiosk.icon_scale":"Gerätesymbolgröße.","kiosk.font_scale":"Textgröße der Raumkarte.","editor.kiosk":"Wandgeräte- (Kiosk-)Modus.","editor.cycle":"Bereiche automatisch alle N Sekunden wechseln (Kiosk, 0 = aus)","room.settings_title":"Raumeinstellungen.","room.settings_section":"Raumeinstellungen (überschreibt den Bereich)","room.fill_label":"Diesen Raum ausfüllen","fill.inherit":"Als den Bereich","room.custom_fill_space":"Bereichsfarbe","room.custom_fill_own":"Raumfarbe","room.temp_src_label":"Temperaturquelle","room.hum_src_label":"Luftfeuchtigkeitsquelle","room.src_average":"Durchschnitt über die Sensoren des Raums (Standard)","room.src_pick":"Ein bestimmtes HA-Gerät oder Entität","room.src_ph":"Quelle wählen…","toast.room_updated":"Raum aktualisiert","space.card_font":"Schriftgröße der Raumkarte (gesamter Bereich)","room.sizes_section":"Schriftgrößen","room.name_scale":"Raumname Größe","room.label_scale":"Metrikgröße","preview.room_name":"Wohnzimmer","toast.cfg_reload_failed":"Der Plan konnte nicht vom Server neu geladen werden: {err}","toast.locale_load_failed":"Das Sprachpaket konnte nicht geladen werden. Bis zum Neuladen der Seite wird Englisch verwendet.","room.settings_short":"Raumeinstellungen","room.unnamed":"Unbenannter Raum","marker.use_climate_temp":"Geräte-Temperatur im Raum berücksichtigen","marker.use_climate_temp_tip":"Fügt die aktuelle Temperatur einer Klimaanlage oder eines Thermostats zum Raumdurchschnitt hinzu. Das externe Wert-Label unten separat konfigurieren.","marker.value_badge_title":"Wert-Label","marker.value_badge.help":"Zeigt einen ausgewählten Wert neben dem Symbol an. Dies beeinflusst nicht die Raumkennzahlen, Licht, Glanz oder die Tippaktion.","marker.value_badge.help.aria":"Hilfe: Gerätewert-Badge","marker.value_badge_enabled":"Zeige ein Wert-Badge","marker.value_badge_source":"Wert","marker.value_badge_source.help":"Wählen Sie einen bestimmten Zustand, ein unterstütztes Attribut oder einen abgeleiteten Wert dieses Geräts.","marker.value_badge_source.help.aria":"Hilfe: Quelle des Wert-Badges","marker.value_badge_position":"Position","marker.value_badge_position.help":"Die gewählte Seite bleibt fest und dreht sich nicht automatisch an einem Planrand.","marker.value_badge_position.help.aria":"Hilfe: Position des Wert-Badges","marker.value_badge_right":"Rechts","marker.value_badge_bottom":"Unten","marker.value_badge_left":"Links","marker.value_badge_top":"Oben","marker.value_badge_empty":"Für dieses Gerät stehen keine Werte zur Verfügung","marker.value_badge_static":"Ein statisches Symbol zeigt keine Live-Werte an. Die Einstellung wird beibehalten.","marker.value_badge_missing":"Quelle nicht verfügbar","marker.value_badge_missing_hint":"Die gespeicherte Quelle ist derzeit nicht verfügbar. Behalten Sie sie bei, deaktivieren Sie das Badge oder wählen Sie einen anderen Wert.","marker.value_badge_duplicate":"Derselbe Wert wird bereits im Symbol angezeigt.","marker.value_badge_state":"Status · {name}","marker.value_badge_marker_state":"Lichtquellstatus · {name}","marker.value_badge_lqi":"Durchschnittliche Zigbee-Signalqualität","marker.value_badge_attr_current_temperature":"Aktuelle Temperatur · {name}","marker.value_badge_attr_temperature":"Zieltemperatur · {name}","marker.value_badge_attr_current_humidity":"Aktuelle Luftfeuchtigkeit · {name}","marker.value_badge_attr_humidity":"Luftfeuchtigkeit · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Geschwindigkeit · {name}","marker.value_badge_attr_brightness":"Helligkeit · {name}","marker.value_badge_attr_volume_level":"Lautstärke · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Lüftergeschwindigkeit · {name}","marker.light_role_label":"Ist dieses Gerät eine Lichtquelle?","marker.light_role.help":"Steuert, ob dieser Marker selbst eine räumliche Lichtquelle ist. Verknüpfte Lichter oben bleiben unabhängige Raumlichtsteuerungen.","marker.light_role.help.aria":"Hilfe: ob dieses Gerät eine Lichtquelle ist","marker.light_role_auto_yes":"Automatisch (Lichtquelle)","marker.light_role_auto_no":"Automatisch (keine Lichtquelle)","marker.light_role_always":"Immer eine Lichtquelle","marker.light_role_never":"Nie eine Lichtquelle","marker.light_entity_label":"Führende Lichtereinheit","marker.light_entity.help":"Für ein Verbundgerät wählen Sie die Entität, deren Zustand, Farbe und Helligkeit diese Planquelle darstellen. Der automatische Rückgriff bleibt mit älteren Plänen kompatibel.","marker.light_entity.help.aria":"Hilfe: führende Lichtentität","marker.light_entity_auto":"Automatisch ({entity})","marker.light_entity_none":"keine steuerbare Entität","marker.light_entity_missing":"Die gespeicherte Entität {entity} ist nicht verfügbar. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.toggle_entity_label":"Zu toggelnde Entität","marker.toggle_entity.help":"Für ein Verbundgerät wählen Sie das genaue eigene Licht oder den Schalter, der durch Tippen bedient wird. Automatisch behält die vorherigen Zielregeln bei und ist unabhängig von der führenden Lichtentität.","marker.toggle_entity.help.aria":"Hilfe: zu toggelnde Entität","marker.toggle_entity_auto":"Automatisch ({entity})","marker.toggle_entity_none":"keine eigene steuerbare Entität","marker.toggle_entity_missing":"Die gespeicherte Entität {entity} ist nicht mehr unter den auswählbaren Kanälen dieses Markers. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.glow_color_label":"Leuchtfarbe und Helligkeit","marker.glow_mode.help":"Verwenden Sie Live-Quellwerte, überschreiben Sie nur deren Farbe oder fixieren Sie sowohl Farbe als auch Helligkeit. Das Minimum beträgt 1 %; um die Quelle zu deaktivieren, wählen Sie „Nie“.","marker.glow_mode.help.aria":"Hilfe: Leuchtfarbe und Helligkeit","marker.glow_mode_auto":"Von Quelle","marker.glow_mode_color":"Farbe einstellen","marker.glow_mode_fixed":"Farbe und Helligkeit einstellen","marker.glow_color":"Leuchtfarbe","marker.glow_brightness":"Helligkeit","marker.glow_disabled_never":"Leuchteinstellungen sind nicht verfügbar, da dieser Marker ausdrücklich keine Lichtquelle ist.","marker.glow_disabled_auto":"Im Automatikmodus wurde keine räumliche Lichtquelle für diesen Marker gefunden.","marker.glow_disabled_no_entity":"Keine aktive steuerbare Home Assistant-Entität ist für diese räumliche Quelle verfügbar.","marker.glow_passive_hint":"Diese Quelle hat keine eigenen Home Assistant-Daten. Stellen Sie ihre Farbe und Helligkeit manuell ein; ihr Radius bleibt verfügbar.","marker.control_broken":"Gespeicherte Quelle fehlt oder ist nicht mehr als Licht markiert","marker.control_missing_label":"Fehlendes Planlicht","marker.control_passive":"passive Quelle","toast.marker_control_cycle":"Dieser Link würde eine kreisförmige Kette von Lichtsteuerungen erzeugen.","toast.marker_binding_required":"Wählen Sie ein Home Assistant-Gerät, bevor Sie eine andere Lichtquelle verlinken.","confirm.unlock":"„{name}“ entsperren?","toast.files_migrate_failed":"Anhänge konnten nicht zur neuen Bindung verschoben werden, Links zeigen weiterhin auf die alten Dateien: {err}","space.pick_saved":"Bereits hochgeladen","space.pick_saved_hint":"Auf dem Server gespeicherte Pläne, einschließlich solcher, die Sie zuvor getrennt haben","space.no_saved":"Noch keine Pläne auf dem Server gespeichert.","space.loading":"Laden…","space.used_by":"in Benutzung: {list}","space.in_use":"Ein Bereich verwendet diesen Plan noch — zuerst die Verknüpfung lösen","btn.use":"Verwenden","confirm.delete_plan":"Plandatei „{name}“ vom Server löschen? Dies kann nicht rückgängig gemacht werden.","toast.plans_list_failed":"Konnte die gespeicherten Pläne nicht auflisten: {err}","toast.plan_delete_failed":"Konnte den Plan nicht löschen: {err}","marker.hide":"Ausblenden","marker.hide_tip":'Das Gerät wird nach dem Speichern aus dem Plan verschwinden, aber weiterhin zum Signalsignal des Raums zählen. Stellen Sie es über den Katalog "Geräte" im Geräte-Editor wieder her.',"marker.show":"Anzeigen","marker.show_tip":"Das Gerät wird nach dem Speichern wieder im Plan erscheinen.","marker.hidden_ghost":"Vom Benutzer ausgeblendetes Gerät","marker.ha_disabled_device":"Das Gerät ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_entity":"Die Entität ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_all_entities":"Das Gerät hat keine aktiven Home Assistant-Entitäten, daher ist es im Plan ausgeblendet.","marker.ha_registry_limited":"Das vollständige Home Assistant-Register ist für diesen Benutzer nicht verfügbar. Das nicht verifizierte Objekt kann vorerst nicht angezeigt oder verwendet werden.","marker.delete_tip":"Löschen Sie das Gerät vollständig aus dem Plan und allen Aggregaten. Sie können es später wieder hinzufügen.","tap.run":"Führen Sie Automation/Skript/Szene aus","marker.run_target_label":"Was ausführen","marker.run_search_ph":"Suchen: Automation, Skript oder Szene…","marker.run_target_gone":"Ziel {id} nicht gefunden — erneut auswählen","marker.tap_confirm":"Um Bestätigung bitten","marker.tap_confirm_tip":"Vor der Ausführung ein Bestätigungsdialogfeld anzeigen — eine Vorsichtsmaßnahme gegen versehentliche Berührungen.","marker.toggle_hint_single":"Ziel: {name} ({id}).","marker.virtual_light_target":"Virtuelles Licht: {name}.","marker.virtual_light_current":"Manueller Zustand: {state} → {effect}.","marker.virtual_light_state_on":"ein","marker.virtual_light_state_off":"aus","marker.toggle_hint_group":"Wird {count}-Quelle(n) umschalten: {names}.","marker.toggle_hint_current":"Jetzt: {state} → {effect}.","marker.toggle_hint_group_current":"Derzeit an: {on} von {count} → {effect}.","marker.toggle_hint_skipped":"Wird übersprungen ({count}): {targets}.","marker.toggle_effect_turn_on":"wird eingeschaltet","marker.toggle_effect_turn_off":"wird ausgeschaltet","marker.toggle_effect_open":"wird geöffnet","marker.toggle_effect_close":"wird geschlossen","marker.toggle_effect_stop":"wird gestoppt","marker.toggle_effect_toggle":"Zustand wird umgeschaltet","marker.toggle_skip_missing":"nicht gefunden","marker.toggle_skip_ha_disabled":"in HA deaktiviert","marker.toggle_skip_unavailable":"nicht verfügbar","marker.toggle_skip_unsupported":"Umschalten wird nicht unterstützt","marker.toggle_skip_secure":"aus Sicherheitsgründen blockiert","marker.toggle_none_no_binding":"Dieses Gerät hat keine Bindung, daher gibt es keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_no_actionable_entity":"Dieses Gerät hat keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_configured_targets_missing":"Die konfigurierten Ziele sind nicht verfügbar. Die eigene Entität des Geräts wird nicht ersetzt.","marker.toggle_none_ha_disabled":"Das Ziel ist in Home Assistant deaktiviert und kann im Plan nicht verwendet werden.","marker.toggle_none_unavailable":"Das Ziel ist derzeit nicht verfügbar. Tippen bewirkt nichts.","marker.toggle_none_unsupported":"Home Assistant bietet keine sichere Umschaltfunktion für dieses Gerät. Tippen bewirkt nichts.","marker.toggle_none_secure":"Das Umschalten von Schlössern, Alarmen und gesicherten Toren über den Plan ist aus Sicherheitsgründen gesperrt.","run.automation":"Automatisierung","run.script":"Skript","run.scene":"Szene","confirm.tap_run":'"{name}" ausführen?',"confirm.tap_toggle":'"{name}" umschalten?',"confirm.current_state":"Aktueller Zustand: {state}","confirm.expected_state":"Nach dem Umschalten: {state}","confirm.group_current":"an {on} von {total}","confirm.group_all_on":"alle sind eingeschaltet","confirm.group_all_off":"alle sind ausgeschaltet","confirm.unavailable_targets":"Nicht verfügbar: {count}","confirm.expected_by_ha":"Home Assistant ermittelt den Zustand","confirm.state_on":"Ein","confirm.state_off":"Aus","confirm.state_open":"Offen","confirm.state_closed":"Geschlossen","confirm.state_opening":"Wird geöffnet","confirm.state_closing":"Wird geschlossen","confirm.state_stopped":"Gestoppt","confirm.state_unknown":"Unbekannt","toast.run_started":"Gestartet: {name}","toast.run_target_missing":"Ausführungsziel nicht gefunden — überprüfen Sie die Geräteeinstellungen","toast.run_target_required":"Wählen Sie eine Automatisierung, ein Skript oder eine Szene aus","toast.tap_target_changed":"Das Ziel der Aktion hat sich geändert. Versuchen Sie es erneut.","toast.virtual_light_toggle_failed":"Das virtuelle Licht konnte nicht umgeschaltet werden: {err}","toast.value_badge_source_required":"Einen Wert für den Badge auswählen","btn.run":"Ausführen","vac.section":"Roboterstaubsauger: aktuelle Position","vac.autocal":"Automatisch einrichten","vac.live":"Aktuelle Position im Plan","vac.trail":"Roboterpfad anzeigen","vac.cal_maps":"Kalibrierte Karten: {maps}","vac.autocal_no_rooms":'Die Integration meldet keine Raumliste — öffnen Sie "Manuell anpassen"',"vac.autocal_no_match":'Raumnamen stimmten nicht überein (mindestens 3 müssen übereinstimmen) — öffnen Sie "Manuell anpassen"',"vac.autocal_done":"Fertig: über {rooms} Räume zugeordnet. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_need_pos":"Der Roboter meldet keine Koordinaten — starten Sie eine Reinigung und pausieren Sie sie","vac.cal_done":"Kalibrierung gespeichert. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_cancelled":"Kalibrierung abgebrochen","vac.fit":"Manuell anpassen","vac.fit_hint":"Roboterkarte an die richtige Position ziehen und an den Ecken skalieren","vac.fit_rotate":"90° drehen","vac.fit_mirror":"Spiegeln","vac.trail_never":"Nie","vac.trail_cleaning":"Beim Reinigen","vac.trail_always":"Immer","gs.bg_group":"Bühnenhintergrund","gs.bg_color":"Hintergrund um den Plan","gs.bg_default":"Standardthema","gs.bg_theme":"Standardthema","gs.bg_mode":"Planhintergrund","gs.bg_static":"Statische Farbe","gs.bg_daynight":"Folgt der Sonne","gs.bg_daynight_hint":"Der Hintergrund wechselt durch Morgendämmerung, Tag, Abenddämmerung und Nacht mithilfe von sun.sun oder Ihrer lokalen Uhr, wenn keine Sonnendaten verfügbar sind. Für den Hintergrund ist kein Kompass erforderlich.","gs.sun_group":"Sonne","gs.sun_missing":"Die sun.sun-Entität wurde nicht gefunden — der Hintergrund folgt Ihrer lokalen Uhr; Fensterstrahlen sind nicht verfügbar.","gs.north":"Norden auf dem Plan","gs.north_ph":"nicht gesetzt","gs.north_hint":"Norden nur für Fensterstrahlen einstellen (1° Schritte, 15° mit Shift); der Sonnenhintergrund funktioniert auch ohne ihn.","gs.north_clear":"Löschen","gs.north_letter":"N","gs.sun_rays":"Sonnenlicht durch Fenster","gs.about_group":"Über","gs.about_version":"House Plan Card v{v}","gs.about_github":"GitHub · Dokumentation & Probleme","gs.about_telegram":"Telegram-Chat","space.bg_color":"Hintergrund rund um den Plan","space.bg_inherit":"Allgemein erben","space.bg_inherited":"erbt allgemeine Einstellungen","space.bg_mode":"Planhintergrund","space.north":"Norden auf dem Plan (überschreiben)","space.north_inherited":"geerbt: {v}","space.sun_rays":"Sonnenlicht durch Fenster","space.sun_inherit":"Allgemein erben","space.sun_on":"Ein","space.sun_off":"Aus","canvas.far_objects":"{n} Objekt(e) weit vom Plan entfernt","canvas.show_far":"Anzeigen","canvas.home_tip":"Der Plan ist dort drüben — klicken, um ihn anzupassen","gs.grid_group":"Planwartung","gs.grid_hint":"Aktualisiert Datenmodelle, richtet Planelemente am Raster aus und fasst redundante Wandfragmente zusammen. Ein exakter Bericht wird angezeigt, bevor etwas gespeichert wird.","gs.align_all":"Pläne optimieren","gs.align_title":"Pläne optimieren","gs.align_none":"Alle Pläne verwenden bereits das aktuelle optimierte Datenmodell.","gs.optimize_no_automatic_changes":"Es gibt keine automatischen Änderungen, die angewendet werden müssen. Überprüfen Sie die unten stehenden Punkte.","gs.align_count":"{n} von {total} Elementen werden sich um höchstens {cm} cm bewegen.","gs.align_where":"Die größte Verschiebung ist in „{s}“.","gs.align_turned":"Öffnungen, deren Winkel korrigiert wurde: {n}.","gs.align_removed_drafts":"Ungültige Umrisse vom Raster zusammengeklappt und entfernt: {n}.","gs.optimize_redundant_drafts":"Gespeicherte Wandketten, die durch feste Raumwände verdeckt waren, wurden entfernt: {n}.","gs.align_preflight_failed":"Die Geometrie der folgenden Bereiche konnte nicht sicher überprüft werden: {spaces}{more}.","gs.align_preflight_hint":"Pläne wurden nicht geändert. Kopieren Sie die Diagnosen mit dem untenstehenden Button und fügen Sie sie zusammen mit einem Raumexport dem Fehlerbericht bei.","gs.preflight_reason_prepare-exception":"Die Bereichsgeometrie konnte nicht vorbereitet werden (Ausnahme beim Erstellen des Modells)","gs.preflight_reason_wall-null":"Der Wandkörper wurde nicht erstellt (die Vereinigung lieferte leer zurück)","gs.preflight_reason_wall-degraded-extra":"Der Wandkörper wurde mit zusätzlicher Geometrie beschädigt","gs.preflight_reason_wall-failed-core":"Der Kern des Wandkörpers konnte nicht zusammengesetzt werden","gs.preflight_reason_wall-exception":"Die Wandkonstruktion warf eine Ausnahme","gs.preflight_reason_floor-null":"Der Fußbodenumriss wurde nicht erstellt","gs.preflight_reason_floor-exception":"Bodenaufbau hat eine Ausnahme ausgelöst","gs.preflight_copy":"Diagnosen kopieren","gs.preflight_copied":"Diagnosen kopiert","gs.preflight_update_hint":"Die Karten- und Integrationsversionen unterscheiden sich — Hausplan aktualisieren und erneut versuchen.","gs.align_preflight_space":"Bereich {n}","gs.align_preflight_more":", und {n} mehr","gs.optimize_changes":"Modellmigrationen: {m}; aktualisierte Bereiche: {c}; entfernte verrauschte Koordinatenwerte: {p}; zusammengeführte Fragmente realer Wände: {w}; zusammengeführte Fragmente von Wänden ohne Dicke: {s}; unabhängige Wände: {i}.","gs.zero_walls_migrated":"Virtuelle Wandspannweiten konvertiert: {n}.","gs.wall_segments_migrated":"Wandsegmente stabilisiert: {n}.","gs.optimize_lattice_summary":"Rauschen in den Koordinatenwerten kanonisiert: {n}; maximale Bewegung: {cm} cm.","gs.optimize_lattice_space":"{space}: Koordinatenwerte kanonisiert: {n}; außerhalb des Rasters liegende Werte unverändert gelassen: {far}.","gs.optimize_coincident_partitions":"Versteckte unabhängige Wandabschnitte in Raumwände integriert: {n}.","gs.optimize_openings_rehosted":"Öffnungen wieder an Raumwände angebracht: {n}.","gs.optimize_walls_straightened":"Wände begradigt: {n}; maximale Bewegung: {cm} cm.","gs.optimize_walls_straightened_where":"Größte Wandkorrektur: {s}.","gs.optimize_walls_straighten_skipped":"Nahe Achsenwände unverändert gelassen, da sie nicht sicher repariert werden konnten: {n}.","gs.optimize_glow_migration":"Legacy Glow: {spaces} Bereiche → keine Datenfüllung + unabhängiges Glow; {rooms} Räume → geerbte Datenfüllung + unabhängiges Glow.","gs.optimize_references":"Referenzen repariert: Bereiche — {spaces}; Räume — {rooms}; Positionen — {positions}; Geräte aus fehlenden Bereichen gelöst — {detached}.","gs.optimize_reference_more":", und {n} mehr","gs.optimize_orphans_removed":"Vergessene Einträge entfernt: {total} — Raumbezeichnungen: {rooms}; Geräte: {devices}; Gruppenmarkierungen: {groups}. Sie gehörten zu zuvor gelöschten Bereichen.","gs.optimize_live_positions":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie werden beibehalten.","gs.optimize_live_positions_remove":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie sind zur Entfernung ausgewählt.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Alte Positionen entfernen","gs.optimize_live_keep":"Alte Positionen beibehalten","gs.optimize_live_selected":"Alte Positionen werden nach der Anwendung von Optimieren entfernt.","gs.optimize_unverified":"Positionen konnten nicht sicher überprüft werden: {n}. Sie wurden unverändert belassen.","gs.optimize_registry_limited":"Voller Administratorzugriff auf die Home Assistant-Registries ist für eine sichere Überprüfung erforderlich.","gs.optimize_vacuum_warning":"Raumzuordnungen, die noch überprüft werden müssen: {n}.","gs.optimize_details":"Details","gs.optimize_details_more":"Und {n} weitere Einträge.","gs.optimize_detail_removed":"wird entfernt","gs.optimize_detail_live":"wird beibehalten","gs.optimize_detail_unverified":"nicht überprüft","gs.optimize_detail_room_label":"Raumkennzeichnung","gs.optimize_detail_device":"Gerät","gs.optimize_detail_group":"Gruppenkennzeichen","gs.optimize_detail_unknown":"unbekannter Eigentümer","gs.optimize_detail_item":"{status}: {kind} {id}; alter Bereich {space}","gs.align_warn":"Absichtlich zwischen Rasterknoten platzierte Elemente werden sich bewegen. Nach der Operation ist nur eine Rückgängig-Funktion verfügbar, und zwar nur bis zur nächsten Planbearbeitung.","gs.align_run":"Optimieren","gs.align_done":"Pläne optimiert: {n} Elemente bewegt, {m} Datensätze beibehalten, {r} Verweise repariert","gs.optimize_undo":"Letzte Optimierung rückgängig machen","gs.optimize_undone":"Die letzte Optimierung wurde rückgängig gemacht","decor.furniture":"Möbel","furn.title":"Möbelbibliothek","furn.symbol":"Symbol","furn.group_furniture":"Möbel","furn.group_appliance":"Geräte","furn.group_sanitary":"Sanitär","furn.group_other":"Sonstiges","furn.width":"Breite","furn.depth":"Tiefe","furn.back_to_categories":"Alle Kategorien","furn.pick_hint":"Wählen Sie eine Kategorie und dann ein Symbol.","furn.place_hint":"Klicken Sie auf den Plan — das Element wird an der nächsten Wand platziert. Mit Shift wird es frei platziert.","furn.cat_air_conditioner":"Klimaanlagen","furn.cat_armchair":"Sessel","furn.cat_bathtub":"Badewannen","furn.cat_bed":"Betten","furn.cat_bidet":"Bidets","furn.cat_boiler":"Warmwasserbereiter","furn.cat_chair":"Stühle","furn.cat_coffee_table":"Couchtische","furn.cat_cooktop":"Kochfelder","furn.cat_dining_table":"Esstische","furn.cat_dishwasher":"Geschirrspüler","furn.cat_dryer":"Wäschetrockner","furn.cat_fireplace":"Kamine","furn.cat_fridge":"Kühlschränke","furn.cat_kitchen_cabinet":"Küchenschränke","furn.cat_kitchen_sink":"Küchenspülen","furn.cat_nightstand":"Schränke","furn.cat_plant":"Pflanzen","furn.cat_rug":"Teppiche","furn.cat_shelving":"Regale","furn.cat_shower":"Duschen","furn.cat_sink":"Waschbecken","furn.cat_sofa":"Sofas","furn.cat_stairs":"Treppen","furn.cat_toilet":"Toiletten","furn.cat_tv":"Fernseher","furn.cat_wardrobe":"Kleiderschränke","furn.cat_washer":"Waschmaschinen","furn.cat_work_table":"Schreibtische","furn.sym_sofa":"Zweisitzer-Sofa","furn.sym_sofa_three_seat":"Dreisitzer-Sofa","furn.sym_sofa_corner_right":"Ecksofa rechts","furn.sym_armchair":"Polstersessel","furn.sym_armchair_office":"Bürostuhl","furn.sym_coffee_table":"Rechteckiger Couchtisch","furn.sym_coffee_table_round":"Runder Couchtisch","furn.sym_coffee_table_oval":"Ovaler Couchtisch","furn.sym_coffee_table_rounded":"Abgerundeter Couchtisch","furn.sym_table_dining":"Rechteckiger Esstisch","furn.sym_table_round":"Rundtisch","furn.sym_table_dining_oval":"Ovaler Esstisch","furn.sym_table_dining_rounded":"Abgerundeter Esstisch","furn.sym_chair":"Stuhl","furn.sym_chair_bar":"Barhocker","furn.sym_desk":"Rechteckiger Schreibtisch","furn.sym_desk_corner":"Eckschreibtisch","furn.sym_bed_double":"Doppelbett","furn.sym_bed_single":"Einzelbett","furn.sym_nightstand":"Nachttisch","furn.sym_cabinet_tv":"TV-Schrank","furn.sym_cabinet_shoe":"Schuhschrank","furn.sym_cabinet_sink":"Waschtischunterschrank","furn.sym_wardrobe":"Kleiderschrank","furn.sym_bookshelf":"Bücherschrank","furn.sym_wall_unit":"Schrankwand","furn.sym_kitchen_floor":"Küchen-Unterschrank","furn.sym_kitchen_floor_corner":"Küchen-Eckunterschrank","furn.sym_kitchen_wall":"Küchen-Hängeschrank","furn.sym_kitchen_wall_corner":"Küchen-Eckhängeschrank","furn.sym_shelf_floor":"Standregal","furn.sym_shelf_wall":"Wandregal","furn.sym_fridge":"Kühlschrank","furn.sym_stove":"Vierflammiges Kochfeld","furn.sym_cooktop_two":"Zweiflammiges Kochfeld","furn.sym_dishwasher":"Geschirrspüler","furn.sym_washer":"Waschmaschine","furn.sym_dryer":"Wäschetrockner","furn.sym_tv":"Fernseher mit Standfuß","furn.sym_tv_wall":"Wandfernseher","furn.sym_ac":"Klimaanlage","furn.sym_water_heater":"Wassererhitzer","furn.sym_toilet":"Stand-WC","furn.sym_toilet_built_in":"Wand-WC","furn.sym_bathtub":"Rechteckige Badewanne","furn.sym_bathtub_corner":"Eckbadewanne","furn.sym_shower":"Dusche","furn.sym_sink":"Waschbecken","furn.sym_kitchen_sink":"Einzel-Küchenspüle","furn.sym_kitchen_sink_double":"Doppel-Küchenspüle","furn.sym_bidet":"Stand-Bidet","furn.sym_bidet_built_in":"Wand-Bidet","furn.sym_stairs":"Treppe","furn.sym_fireplace":"Kamin","furn.sym_plant":"Pflanze","furn.sym_rug":"Teppich","common.yes":"Ja","common.no":"Nein","vac.diag_source":"Quelle","vac.diag_platform":"Integration","vac.diag_status":"Status","vac.diag_position":"Position","vac.diag_rooms":"Räume","vac.diag_rooms_value":"{total} · {matched} Namen stimmen überein · {readiness}","vac.autocal_ready":"Auto-Kalibrierung verfügbar","vac.autocal_not_ready":"3 übereinstimmende Namen erforderlich","vac.diag_path":"Integrationspfad","vac.diag_map":"Karten-ID","vac.source_none":"nicht ausgewählt","vac.source_status_ok":"Bereit","vac.source_status_missing":"Fehlend","vac.source_status_disabled":"In Home Assistant deaktiviert","vac.source_status_unavailable":"Nicht verfügbar","vac.source_status_unverified":"Kann mit den aktuellen Berechtigungen nicht verifizieren","vac.source_status_unsupported":"Keine Positionsdaten","vac.source_status_none":"Keine Quelle","vac.source_banner_missing":"Die gespeicherte Quelle existiert nicht mehr. Sie wurde nicht automatisch ersetzt; wählen Sie eine andere Quelle oder stellen Sie sie in Home Assistant wieder her.","vac.source_banner_disabled":"Die gespeicherte Quelle ist in Home Assistant deaktiviert. Aktivieren Sie sie dort oder wählen Sie eine andere Quelle.","vac.source_banner_unverified":"Die aktuellen Home Assistant-Berechtigungen können diese gespeicherte Quelle nicht verifizieren. Sie bleibt angepinnt und wird nicht automatisch ersetzt.","vac.choose_source":"Quelle wählen","vac.source_auto":"Automatisch","vac.source_auto_hint":"Verwenden Sie eine kompatible Entität von diesem Gerät","vac.all_cameras":"Alle Kameras","vac.all_cameras_warn":"Eine Kamera liefert möglicherweise keine Roboter-Daten. Wählen Sie eine nur, wenn es Ihre Saugroboter-Karte ist.","vac.all_cameras_empty":"Keine anderen Kamera-Entitäten gefunden.","vac.platform_unknown":"unbekannte Integration","vac.cap_position":"Position","vac.cap_rooms_short":"Räume","vac.cap_path":"Pfad","vac.cap_map":"Karten-ID","vac.cap_none":"Keine Roboter-Daten erkannt","vac.xcme_hint":"Aktivieren Sie diese Xiaomi Cloud Map Extractor-Attribute:","vac.documentation":"Dokumentation","vac.residual_title":"Automatische Kalibrierung überprüfen","vac.residual_message":"Die zugeordneten Räume weichen um bis zu {error} voneinander ab. Diese ungefähre Kalibrierung anwenden, manuell verfeinern oder ohne Änderung der gespeicherten Einrichtung abbrechen.","vac.apply_proposal":"Anwenden","gs.backup_group":"Sichern und übertragen","gs.backup_hint":"Laden Sie ein tragbares JSON-Backup herunter oder sehen Sie eine Vorschau und importieren Sie ein Backup, das mit House Plan erstellt wurde.","backup.export_open":"Exportieren","backup.import_open":"Importieren","backup.export_title":"House Plan exportieren","backup.import_title":"House Plan importieren","backup.export_hint":"Wählen Sie, ob das Backup die gesamte House Plan-Konfiguration oder nur den aktuellen Bereich enthalten soll.","backup.full":"Vollständiges Backup","backup.current_space":"Aktueller Bereich","backup.current_space_title":"Aktueller Bereich: {title}","backup.no_current_space":"Kein aktueller Bereich","backup.plan_only":"Nur Plan","backup.plan_only_hint":"Räume, Wände, Öffnungen, Dekor und Raumbezeichnungspositionen ohne Geräte oder Home Assistant-Bindungen beibehalten.","backup.plan_only_preview":"Diese Datei enthält nur den Plan.","backup.privacy_warning":"Das Archiv enthält Namen, Home Assistant-Kennungen und genaue Koordinaten. Interne Pläne und Anhänge werden referenziert, nicht eingebettet; Laufzeitzustände und Staubsaugerspuren sind nicht enthalten.","backup.download":"JSON herunterladen","backup.export_done":"Backup heruntergeladen","backup.reading":"Überprüfe das Backup…","backup.revalidated":"Der Plan wurde nach dieser Vorschau geändert. Die Zusammenfassung wurde aktualisiert; überprüfen Sie sie und bestätigen Sie erneut.","backup.error.unauthorized":"Sie haben keine Berechtigung, diesen Plan zu exportieren oder zu importieren.","backup.error.not_ready":"Der Hausplan ist noch nicht fertig. Versuchen Sie es in einem Moment erneut.","backup.error.too_large":"Das Backup überschreitet das 8-MiB-Limit.","backup.error.invalid_json":"Die ausgewählte Datei ist kein gültiges JSON.","backup.error.invalid_format":"Die ausgewählte Datei ist kein House Plan-Backup.","backup.error.unsupported_export_version":"Dieses Backup-Format wird von der installierten Version nicht unterstützt.","backup.error.future_model":"Das Backup wurde mit einem neueren House-Plan-Datenmodell erstellt.","backup.error.invalid_config":"Das Backup enthält eine ungültige House-Plan-Konfiguration.","backup.error.wall_model_migration_blocked":"Das Backup-Wandmodell konnte nicht sicher aktualisiert werden. Optimieren Sie zuerst den Quellplan und exportieren Sie ihn dann erneut.","backup.error.invalid_layout":"Das Backup enthält ungültige Objektpositionen.","backup.error.invalid_content":"Das Backup enthält ungültige oder inkonsistente Inhaltsreferenzen.","backup.error.space_not_found":"Der ausgewählte Bereich existiert nicht mehr.","backup.error.capacity_exceeded":"Durch das Hinzufügen dieses Backups würden die Planlimits überschritten.","backup.error.preview_expired":"Die Vorschau ist abgelaufen. Wählen Sie die Backup-Datei erneut aus.","backup.error.preview_owner_mismatch":"Diese Vorschau gehört zu einem anderen Home-Assistant-Benutzer.","backup.error.conflict":"Der Plan hat sich nach der Vorschau geändert. Überprüfen Sie die aktualisierte Zusammenfassung.","backup.error.content_confirmation_required":"Bestätigen Sie, dass nicht verfügbare lokale Inhalte getrennt werden können.","backup.error.commit_failed":"Das Backup konnte nicht sicher angewendet werden. Der vorherige Plan wurde wiederhergestellt oder wartet auf die Wiederherstellung.","backup.error.missing_plan":"Eine referenzierte Plan-Datei ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.missing_content":"Ein referenziertes lokales Anhangselement ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.marker_control_missing":"Ein verknüpftes Planlicht fehlt im Backup.","backup.error.marker_control_not_light":"Ein verknüpftes Planziel ist nicht mehr als Lichtquelle markiert.","backup.error.marker_control_self":"Eine Lichtquelle kann sich nicht selbst steuern.","backup.error.marker_control_cycle":"Das Backup enthält eine kreisförmige Kette von Lichtsteuerungen.","backup.error.duplicate_marker_control":"Das Backup enthält eine doppelte Plan-Licht-Verknüpfung.","backup.error.no_backup":"Es gibt keinen Import- oder Optimierungssnapshot zur Wiederherstellung.","backup.same_source":"Erstellt auf dieser Home Assistant-Instanz","backup.foreign_source":"Erstellt auf einer anderen Home Assistant-Instanz","backup.created":"Erstellt: {value}","backup.versions":"Karte {card}; Integration {integration}; Datenmodell {model}","backup.count_spaces":"Bereiche: {n}","backup.count_rooms":"Räume: {n}","backup.count_walls":"Wände: {n}","backup.count_openings":"Öffnungen: {n}","backup.count_decor":"Dekorationsobjekte: {n}","backup.count_markers":"Geräte: {n}","backup.count_layout":"Positionen: {n}","backup.bindings":"Verknüpfungen — Geräte: {device}, Entitäten: {entity}, virtuell: {virtual}; Positionen ohne Bereich: {legacy}","backup.binding_status":"Zielstatus — aktiv: {active}, deaktiviert: {disabled}, fehlt: {missing}","backup.missing_areas":"Auf dem Ziel fehlende Bereiche: {areas}","backup.dropped_marker_links":"Planlicht-Verknüpfungen außerhalb dieses übertragenen Bereichs wurden ausgelassen: {n}.","backup.repaired_target_refs":"Durch den Import dieses Bereichs wiederhergestellte vorhandene Referenzen: {n}.","backup.preserved_unresolved_refs":"Referenzen, die nicht eindeutig wiederhergestellt werden konnten, wurden beibehalten: {n}.","backup.preserved_unresolved_hint":"Es wurden keine Daten geraten oder gelöscht. Führen Sie nach dem Import Optimierungspläne aus, um die verbleibenden Referenzen zu überprüfen.","backup.import_details":"Importieren von Referenzdetails","backup.import_detail.incoming_remapped":"Referenzen innerhalb der importierten Kopie aktualisiert: {n}","backup.import_detail.target_repaired":"Vorhandene Referenzen wiederhergestellt: {n}","backup.import_detail.preserved_unresolved":"Unaufgelöste Referenzen beibehalten: {n}","backup.import_detail.collisions":"Zielkonflikte sicher beibehalten: {n}","backup.import_detail.dropped_links":"Eingehende Links durch Übertragungsregeln ausgelassen: {n}","backup.import_detail.bounded_lineages":"Übermäßig verschachtelte Kennungen begrenzt belassen: {n}","backup.replace_warning":"Dies ersetzt die aktuelle Konfiguration und das Layout. Hochgeladene Dateien werden niemals gelöscht. Eine Rückgängig-Möglichkeit bleibt bis zur nächsten Planbearbeitung verfügbar.","backup.foreign_bookkeeping":"Instanzspezifische bekannte/neue Gerätebuchführung wird nicht importiert.","backup.final_name":"Name des neuen Bereichs","backup.target_settings":"Die globalen Einstellungen der Zielinstanz bleiben unverändert.","backup.duplicates":"Bindungen bereits vorhanden: {n}","backup.skip":"Doppelte Geräte überspringen","backup.virtual_copy":"Sichere statische virtuelle Kopien hinzufügen","backup.content":"Referenzierter Inhalt","backup.content_available":"lokal verfügbar","backup.content_external":"externer Link","backup.content_detach_required":"wird getrennt","backup.confirm_detach":"Ich verstehe, dass nicht verfügbare interne Pläne und Anhänge von der importierten Konfiguration getrennt werden.","backup.replace":"Ersetzen","backup.add":"Bereich hinzufügen","backup.space_done":"Bereich importiert: {rooms} Räume, {markers} Geräte, {refs} bestehende Referenzen wiederhergestellt","backup.full_done":"Backup wiederhergestellt: {spaces} Bereiche, {rooms} Räume, {markers} Geräte","backup.undo_import":"Letzten vollständigen Import rückgängig machen","backup.import_undone":"Vollständiger Import rückgängig gemacht","backdrop.large_title":"Großes Bild","backdrop.large_body":"Dieses Bild ist {w}×{h} ({fileMb} MB Datei) und benötigt etwa {decodedMb} MB Speicher zur Anzeige. Auf Tablets kann das die Seite zum Absturz bringen.","backdrop.unknown_body":"Die Bildabmessungen konnten nicht gelesen werden — die Datei ist möglicherweise beschädigt. Fortfahren kann die Seite auf Tablets zum Absturz bringen.","backdrop.use_downscaled":"Verkleinerte Kopie hochladen","backdrop.reducing":"Wird verkleinert…","backdrop.keep_original":"Original behalten","backdrop.too_large_title":"Bild ist zu groß","backdrop.too_large_body":"Dieses Bild überschreitet die Anzeigegrenzen des Browsers ({w}×{h}, Limit {limit} px pro Seite). Bitte verkleinern Sie es in einem Desktop-Editor und laden Sie es erneut hoch.","backdrop.downscale_failed":"Die verkleinerte Kopie konnte nicht erstellt werden. Der aktuelle Plan wurde nicht geändert."};const n="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";export{e as dictionary,n as fingerprint}; diff --git a/dist/houseplan-assets/editor-CcxmUFV-.js b/custom_components/houseplan/frontend/houseplan-assets/editor-CIkons5x.js similarity index 84% rename from dist/houseplan-assets/editor-CcxmUFV-.js rename to custom_components/houseplan/frontend/houseplan-assets/editor-CIkons5x.js index f0511882..1e597f75 100644 --- a/dist/houseplan-assets/editor-CcxmUFV-.js +++ b/custom_components/houseplan/frontend/houseplan-assets/editor-CIkons5x.js @@ -1,14 +1,14 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";import{c as e,l as o,t,c_ as s,A as a,c$ as i,d0 as l,d1 as n,E as r,b as c}from"./houseplan-card-B5mDyjuC.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,f={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},h=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`h[e.name]||e.name} + .computeLabel=${e=>f[e.name]||e.name} @value-changed=${this._valueChanged} >`;return c` - ${d(f.slice(0,3))} + ${d(h.slice(0,3))} ${_?c``:a} - ${d(f.slice(3))} - `}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h); + ${d(h.slice(3))} + `}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}f.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",f); diff --git a/custom_components/houseplan/frontend/houseplan-assets/fr-CFpxbPTS.js b/custom_components/houseplan/frontend/houseplan-assets/fr-CFpxbPTS.js deleted file mode 100644 index 1a4c3165..00000000 --- a/custom_components/houseplan/frontend/houseplan-assets/fr-CFpxbPTS.js +++ /dev/null @@ -1 +0,0 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";var e={"editor.context_actions":"Actions : {object}","editor.tool_options":"Options de l’outil : {tool}","editor.palette":"Palette : {tool}","editor.open_group":"Groupe d’outils : {group}","editor.group_active":"{group} — actif : {item}","editor.disabled_action":"{action} est indisponible : {reason}","editor.loading":"Chargement de l'éditeur…","editor.loading_aria":"L'éditeur de plan est en cours de chargement","editor.load_failed":"Impossible de charger l'éditeur.","editor.refresh_advice":"Actualisez la page et réessayez.","editor.retry_advice":"Vérifiez votre connexion et appuyez à nouveau.","color_picker.title":"Sélecteur de couleur","color_picker.hue":"Teinte","color_picker.saturation":"Saturation","color_picker.value":"Luminosité","color_picker.hex":"Couleur hexadécimale","color_picker.invalid_hex":"Saisissez une couleur hexadécimale à 3 ou 6 chiffres","btn.properties":"Propriétés","btn.keep_as_walls":"Conserver comme murs","btn.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","btn.delete_room_with_walls":"Supprimer la pièce et les murs","title.markup_select":"Sélectionner et modifier les murs, colonnes et contours enregistrés","title.markup_column":"Colonne : cliquez sur un point de la grille pour placer une colonne carrée","markup.partition":"Cloison","markup.column":"Colonne","markup.hint_column":"cliquez sur un point de la grille pour placer une colonne carrée","history.draft_segment":"Ajouter un segment de brouillon de pièce","history.draft_merge":"Relier des contours de pièce inachevés","history.draft_segment_delete":"Supprimer un segment de brouillon de pièce","history.partition_add":"Ajouter une cloison","history.column_add":"Ajouter une colonne","history.physical_edit":"Modifier l’objet physique","history.physical_delete":"Supprimer l’objet physique","history.physical_move":"Déplacer l’objet physique","history.contour_to_partitions":"Convertir le contour en murs fermés","history.wall_chain_finish":"Terminer la chaîne de murs","history.wall_face_batch":"Créer des pièces à partir des murs","toast.column_duplicate":"Une colonne avec le même centre et la même dimension extérieure existe déjà","confirm.delete_draft":"Supprimer tout le contour de pièce inachevé ?","confirm.delete_draft_segment":"Supprimer ce segment de brouillon ? Le contour restant peut se scinder en deux.","physical.partition_properties":"Propriétés de la cloison","physical.column_properties":"Propriétés de la colonne","physical.draft_properties":"Propriétés du segment de brouillon","physical.shape":"Forme","physical.square":"Carré","physical.circle":"Cercle","physical.diameter":"Diamètre","physical.side":"Côté","physical.rotation":"Angle de rotation","physical.length":"Longueur","physical.allowed_range":"Autorisé : {min}–{max} {unit}","physical.delete_segment":"Supprimer le segment","physical.delete_draft":"Supprimer tout le contour","physical.column_size_title":"Côté extérieur de la colonne carrée placée par le clic (1–150 cm).","card.title":"Plan de la maison","count.devices":"{n} app.","empty.no_spaces":"Aucun espace pour le moment.","empty.add_first":"Ajoutez le premier espace et importez un plan.","empty.install":"Installez l’intégration House Plan et ajoutez-la dans « Appareils et services ».","btn.add_space":"Ajouter un espace","btn.cancel":"Annuler","btn.save":"Enregistrer","btn.close":"Fermer","btn.delete":"Supprimer","btn.edit":"Modifier","btn.open_in_ha":"Ouvrir dans HA","btn.reset":"Réinitialiser","btn.attach":"Joindre…","btn.upload":"Importer…","btn.replace":"Remplacer…","title.zoom_in":"Zoomer","title.zoom_out":"Dézoomer","title.zoom_fit":"Tout ajuster","title.markup":"Traçage des pièces : grille, lignes, contours","title.configure_space":"Configurer l’espace","title.add_space":"Ajouter un espace","title.markup_add":"Murs : tracez une chaîne continue ; Maj verrouille à 45°. Changer d’outil ou quitter l’éditeur termine la chaîne en murs indépendants","title.markup_merge":"Fusionner : cliquez sur une pièce, puis sur la pièce voisine avec laquelle elle partage un mur","title.markup_split":"Diviser une pièce : cliquez sur la pièce, puis sur deux points de ses murs","title.markup_delroom":"Supprimer une pièce : cliquez à l’intérieur de la pièce puis confirmez","title.need_plan":"Importer une image de plan","markup.add":"Murs","markup.merge":"Fusionner","markup.split":"Diviser","markup.resize":"Redimensionner","title.markup_resize":"Redimensionner les pièces : faites glisser une poignée disponible sur un mur horizontal ou vertical","markup.hint_resize":"faites glisser une poignée de mur disponible · le mur s’arrête à la première position non sûre · Échap annule · Ctrl+Z — annuler","resize.disabled.diagonal":"Ce mur est incliné ; le redimensionnement ne prend en charge que les murs horizontaux et verticaux","resize.disabled.side-angle":"Un mur adjacent est incliné ; le redimensionnement exige que les deux murs adjacents rencontrent ce mur à angle droit","resize.disabled.duplicate-physical-wall":"Une cloison distincte, un contour inachevé ou une colonne chevauche ce mur ; supprimez-la ou déplacez-la avant de redimensionner","resize.disabled.partial-shared":"Une pièce voisine n’utilise qu’une partie de ce mur ; il ne peut donc pas être déplacé en toute sécurité comme un mur partagé unique","resize.disabled.unequal-shared":"La pièce voisine utilise des extrémités ou une longueur de mur différentes ; les murs ne peuvent donc pas être déplacés ensemble en toute sécurité","resize.disabled.multiple-rooms":"Déplacer ce mur affecterait plus de deux pièces","resize.disabled.thickness-conflict":"L’épaisseur du mur ne peut pas être conservée en toute sécurité","resize.disabled.opening-conflict":"Une ouverture sur ce mur ne peut pas être déplacée en toute sécurité","resize.disabled.invalid-geometry":"Ce mur ne peut pas être redimensionné sans modifier la structure du plan","resize.limit_stopped":"Mur arrêté : l'étape suivante enfreindrait une limite de jonction","resize.commit_failed":"Le redimensionnement a été annulé car le plan final n’a pas passé le contrôle de géométrie","resize.preview_failed":"Le redimensionnement s’est arrêté à la dernière position sûre car la géométrie du plan ne pouvait pas être préservée","markup.opening":"Ouverture","markup.delete_room":"Supprimer la pièce","history.undo":"Annuler","history.redo":"Rétablir","history.undo_named":"Annuler : {name} (Ctrl+Z)","history.redo_named":"Rétablir : {name} (Ctrl+Maj+Z / Ctrl+Y)","history.undo_empty":"Rien à annuler","history.redo_empty":"Rien à rétablir","history.undone":"Annulé : {name}","history.redone":"Rétabli : {name}","history.add_room":"Créer une pièce","history.split_room":"Diviser la pièce","history.merge_rooms":"Fusionner les pièces","history.resize_room":"Redimensionner la pièce","history.wall_thickness":"Modifier l’épaisseur du mur","history.add_opening":"Ajouter une ouverture de porte ou de fenêtre","history.edit_opening":"Modifier l’ouverture","history.move_opening":"Déplacer l’ouverture","history.delete_opening":"Supprimer l’ouverture","history.delete_room":"Supprimer la pièce","history.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","history.delete_room_with_walls":"Supprimer la pièce et les murs","history.decor_add":"Ajouter un objet décoratif","history.decor_edit":"Modifier l’objet décoratif","history.decor_move":"Déplacer l’objet décoratif","history.decor_transform":"Transformer l’objet décoratif","history.decor_delete":"Supprimer l’objet décoratif","history.backdrop_transform":"Transformer l’arrière-plan du plan","opening.new":"Nouvelle ouverture","opening.edit":"Ouverture","opening.door":"Porte","opening.window":"Fenêtre","opening.gate":"Portail","opening.passage":"Passage ouvert","opening.passage_binding_warning":"L’enregistrement supprimera le capteur d’ouverture/fermeture et la serrure.","opening.invalid_passage_fields":"Le passage ouvert du plan « {room} » possède des paramètres non pris en charge : {fields}.","opening.type_label":"Type","opening.length_label":"Longueur, cm","opening.contact_label":"Capteur d’ouverture/fermeture","opening.lock_label":"Serrure","opening.none":"— aucun —","opening.search_ph":"Rechercher : nom ou ID d’entité…","opening.invert":"Inverser ouvert/fermé","opening.flip_h":"Charnière sur l’autre montant","opening.flip_v":"Ouverture de l’autre côté","opening.open":"Ouvert","opening.closed":"Fermé","opening.locked":"Verrouillé","opening.unlocked":"Déverrouillé","opening.state_unknown":"indisponible","opening.no_entities":"Aucun capteur associé — symbole statique sur le plan.","toast.opening_no_wall":"Cliquez près d’un mur de pièce ou d’un mur indépendant","opening.host_partition":"Mur indépendant","opening.partition_orphan":"Le mur indépendant de cette ouverture n’existe plus","opening.partition_jamb_margin":"Laissez au moins {distance} entre l’ouverture et l’extrémité du mur","opening.rebind_partition":"Associer à un autre mur indépendant","confirm.delete_partition_openings_title":"Supprimer le mur et les ouvertures ?","confirm.delete_partition_openings_body":"Ce mur contient {count} ouverture(s). Elles seront supprimées avec lui.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Supprimer","markup.hint_points":"points : {n} · Maj — pas de 45° · Échap — terminer la chaîne · Ctrl+Z — annuler un point · fermer une zone propose de créer une pièce","markup.hint_start":"cliquez sur un point de la grille pour commencer une chaîne de murs","tip.lqi":"signal Zigbee moyen :","tip.area":"surface : {value}","info.device_header":"Appareil sur le plan","info.model":"Modèle","info.state":"État","info.link":"Lien","info.manuals":"Manuels","info.none":"Aucune information supplémentaire","marker.new_device":"Nouvel appareil","marker.name_label":"Nom (affiché sur le plan)","marker.name_ph":"Nom","marker.binding_label":"Associer à un appareil HA","marker.binding_disabled":"désactivé dans Home Assistant","marker.virtual_option":"Appareil virtuel (sans association)","marker.search_ph":"Rechercher un appareil / groupe…","marker.nothing_found":"aucun résultat","marker.room_label":"Pièce","marker.room_override":" (placement forcé)","marker.room_choose":"— sélectionner une pièce —","marker.room_auto":"— selon la zone de l’appareil (auto) —","marker.icon_label":"Icône","marker.icon_ph":"mdi:… (vide = auto)","marker.display_label":"Affichage","display.badge":"Icône + état","display.icon_ripple":"Icône + état et activité","display.static_icon":"Icône toujours statique","marker.display_hint":"Icône + état modifie le fond pour les états en fonctionnement, ouvert, alarme et indisponible. Icône + état et activité ajoute une brève pulsation pour les événements et une pulsation continue pour une activité, un mouvement ou une présence en cours. Valeur + état remplace l’icône par une valeur HA non ambiguë. Une icône statique ne réagit jamais aux états de l’appareil.","marker.display_hint_badge":"L’icône et le fond dynamique affichent l’état de l’appareil sans la pulsation d’activité habituelle. Les alarmes rouges restent visibles.","marker.display_hint_icon":"L’icône et le fond dynamique affichent l’état de l’appareil sans la pulsation d’activité habituelle. Les alarmes rouges restent visibles.","marker.display_hint_icon_ripple":"L’icône, le fond dynamique et la pulsation affichent une brève animation pour les événements et une animation continue pour le fonctionnement, le mouvement ou la présence. Les alarmes rouges restent prioritaires.","marker.display_hint_value":"Une valeur Home Assistant non ambiguë remplace l’icône tandis que le fond continue d’indiquer l’état. Les alarmes rouges restent visibles.","marker.display_hint_static_icon":"Le contour adapté au thème et l’icône restent toujours identiques. L’état, l’activité, l’indisponibilité et les alarmes ne modifient pas l’apparence.","marker.static_alarm_warning":"Cet appareil peut signaler des alarmes. L’affichage statique masque l’indication visuelle d’alarme du marqueur.","marker.preview.title":"Aperçu de l’affichage","marker.preview.actual":"Actuel","marker.preview.example":"Exemple","marker.preview.integration":"Fourni par","marker.preview.source":"Source d’affichage","marker.preview.current_state":"État actuel","marker.preview.result":"Sur le plan","marker.preview.details":"Détails de la source","marker.preview.select_source":"Choisissez un appareil ou une entité Home Assistant pour voir son affichage réel.","marker.preview.unknown_provider":"Intégration inconnue","marker.preview.virtual_provider":"House Plan · appareil virtuel","marker.preview.no_source":"Aucune source active","marker.preview.no_state":"Aucun état actuel","marker.preview.mixed_states":"Plusieurs états différents","marker.preview.multiple_sources":"{n} sources","marker.preview.more_sources":"+{n} de plus","marker.preview.scaled":"L’aperçu est redimensionné à {n}% pour tenir dans la zone. La taille enregistrée reste inchangée.","marker.preview.demo_short":"Afficher une activité brève","marker.preview.demo_continuous":"Afficher une activité continue","marker.preview.stop_continuous":"Arrêter l’activité continue","marker.preview.demo_short_notice":"Exemple d’activité brève ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_continuous_notice":"Exemple d’activité continue ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_already_visible":"L’état actuel affiche déjà un véritable effet d’activité ou d’alarme.","marker.preview.reduced_motion":"La réduction des animations est activée sur le système ; l’activité normale est donc affichée sous forme de point.","marker.preview.reason.neutral":"Fond sombre neutre","marker.preview.reason.working":"Fond jaune : l’appareil fonctionne actuellement","marker.preview.reason.working_activity":"Fond jaune et effet d’activité : l’appareil fonctionne actuellement","marker.preview.reason.open":"Fond orange : physiquement ouvert ou déverrouillé","marker.preview.reason.cover_icon_state":"Fond neutre ; l’entité de volet contrôle l’état et l’icône affichés","marker.preview.reason.presence":"Effet d’activité tant qu’une présence est détectée","marker.preview.reason.event":"Effet d’activité bref après un événement détecté","marker.preview.reason.transition":"Effet d’activité pendant que l’appareil se déplace ou change d’état","marker.preview.reason.media_neutral":"Les appareils multimédias restent sur un fond sombre neutre tant qu’ils sont disponibles","marker.preview.reason.unavailable":"Fond neutre atténué : indisponible ou éteint","marker.preview.reason.alarm":"Fond d’alarme rouge ; l’indication d’alarme est toujours affichée","marker.preview.reason.live_states_disabled":"Le style selon les états en direct est désactivé pour cette carte","marker.preview.reason.value_no_state":"Aucune valeur d’état exploitable ; l’icône est affichée à la place","marker.preview.reason.value_ambiguous_sources":"Plusieurs sources de valeur possibles ; l’icône est affichée à la place","marker.preview.reason.value_non_scalar":"La source n’a pas renvoyé de valeur simple ; l’icône est affichée à la place","marker.preview.reason.value_virtual":"Un appareil virtuel n’a aucune valeur Home Assistant ; l’icône est affichée à la place","marker.preview.reason.vacuum_live_plan_only":"La position et la trajectoire en direct de l’aspirateur ne sont disponibles que sur le plan complet","marker.preview.reason.hidden_design_preview":"L’appareil est masqué sur le plan ; cet aperçu de conception reste visible","marker.preview.reason.composite_power_source":"L’état provient de l’entité d’alimentation de l’appareil ; les interrupteurs auxiliaires sont ignorés","marker.preview.reason.activity_display_disabled":"Le mode d’affichage sélectionné n’affiche pas les effets d’activité habituels","marker.preview.reason.ha_disabled":"L’association Home Assistant est désactivée et sera masquée sur le plan","marker.preview.reason.orphaned":"L’association Home Assistant enregistrée est introuvable","marker.preview.reason.static_icon":"Mode statique : l’état de l’appareil ne modifie pas l’icône","marker.activity_color":"Couleur de la pulsation d’activité","marker.ripple_size":"Taille de la pulsation d’activité","marker.activity_alarm_note":"La couleur et la taille n’affectent que l’activité normale et ne modifient pas les alarmes rouges.","marker.pulse_a11y_alarm":"Alarme","marker.pulse_a11y_event":"Événement récent","marker.pulse_a11y_presence":"Présence détectée","marker.pulse_a11y_transition":"Changement d’état","marker.pulse_a11y_running":"En fonctionnement","marker.state_a11y_neutral":"État par défaut","marker.state_a11y_working":"En fonctionnement","marker.state_a11y_open":"Ouvert","marker.state_a11y_locked":"Verrouillé","marker.state_a11y_unlocked":"Déverrouillé","marker.state_a11y_alarm":"Alarme","marker.state_a11y_unavailable":"Indisponible","marker.lqi_a11y_low":"LQI {value}, signal faible","marker.lqi_a11y_mid":"LQI {value}, signal moyen","marker.lqi_a11y_high":"LQI {value}, signal fort","marker.size_label":"Taille / rotation de l’icône","marker.angle_label":"Rotation","marker.model_label":"Modèle","marker.model_ph":"ex. Aqara T&H","marker.link_label":"Lien","marker.desc_label":"Description","marker.desc_ph":"Notes, caractéristiques…","marker.manuals_label":"Manuels (PDF, etc.)","marker.sub_device":"appareil","marker.sub_z2m_group":" · groupe Z2M","marker.sub_group":"groupe","marker.sub_helper":"assistant","space.new":"Nouvel espace","space.header":"Espace","space.title_label":"Titre","space.title_ph":"ex. Garage","space.plan_label":"Plan (arrière-plan)","space.no_plan":"aucune image de plan","space.plan_alt":"plan","room.new":"Nouvelle pièce","room.name_label":"Nom affiché","room.name_ph":"ex. Terrasse","room.area_label":"Zone Home Assistant (non attribuée)","room.no_area_option":"— aucune zone —","room.default_name":"Pièce","device.unnamed":"sans nom","device.light_group":"groupe de lumières","device.fallback":"appareil","device.virtual":"appareil virtuel","confirm.delete_room":"Supprimer la pièce « {name} » ?","confirm.delete_room_title":"Supprimer la pièce « {name} » ?","confirm.delete_room_body":"Choisissez si les murs physiques propres à la pièce doivent être conservés. Les murs partagés avec une autre pièce sont toujours conservés.","confirm.remove_marker":"Supprimer « {name} » du plan ? L’appareil disparaîtra complètement et ne contribuera plus aux données du plan. Vous pourrez l’ajouter de nouveau plus tard.","confirm.erase_decor":"Effacer l’objet {kind} ? Vous pouvez annuler cette action depuis l’historique de l’éditeur.","confirm.delete_space":"Supprimer l’espace « {title} » avec toutes ses pièces et son tracé ?","space.delete_blocked":"Cet espace est encore utilisé par {n} appareil(s). Déplacez-les vers un autre espace ou supprimez-les d’abord.","toast.pos_save_failed":"Échec de l’enregistrement de la position : {err}","toast.no_entity":"L’appareil n’a aucune entité adaptée","toast.ha_disabled_action":"Un objet Home Assistant désactivé ne peut pas être utilisé sur le plan.","toast.ha_disabled_show_device":"Un appareil désactivé dans Home Assistant ne peut pas être affiché sur le plan. Activez-le d’abord dans Home Assistant.","toast.ha_disabled_show_entity":"Une entité désactivée dans Home Assistant ne peut pas être affichée sur le plan. Activez-la d’abord dans Home Assistant.","toast.ha_disabled_add":"Un objet Home Assistant désactivé ne peut pas être ajouté au plan. Activez-le d’abord dans Home Assistant.","toast.ha_binding_unverified":"L’état de l’objet n’a pas pu être vérifié via le registre Home Assistant. L’affichage et les actions sont temporairement indisponibles.","toast.markup_needs_server":"Le tracé est disponible une fois la configuration déplacée sur le serveur","toast.space_order_changed":"L’ordre a changé. Si une carte fixe son étage par numéro, vérifiez ces tableaux de bord.","toast.conflict":"La configuration a été modifiée dans une autre fenêtre — données actualisées, répétez votre dernière action","toast.cfg_save_failed":"Échec de l’enregistrement de la configuration : {err}","toast.room_overlap":"Le contour chevauche la pièce « {name} » — les pièces ne doivent pas se chevaucher","toast.contour_cannot_close":"Le contour ne peut pas être fermé car il est dégénéré ou s’auto-intersecte","toast.merge_not_adjacent":"Seules les pièces partageant un mur peuvent être fusionnées","toast.rooms_merged":"Pièces fusionnées dans « {name} »","toast.split_pick_wall":"Commencez la découpe sur le mur de la pièce","toast.split_bad_cut":"La découpe doit aller d’un mur à l’autre à l’intérieur de la pièce, sans croiser de mur ni se croiser elle-même","merge.header":"Fusionner les pièces","merge.hint":"La pièce fusionnée conserve un nom et une zone. L’autre zone est libérée — ses appareils quittent le plan jusqu’à ce qu’une autre pièce la récupère.","merge.keep":"Conserver","merge.no_area":"aucune zone","toast.room_saved":"Pièce enregistrée ({n}). Appareils ajoutés : {added}. Tracez la suivante ou quittez le mode tracé.","toast.room_saved_no_area":"Pièce enregistrée ({n}, sans zone). Tracez la suivante ou quittez le mode tracé.","toast.marker_needs_server":"La modification des appareils est disponible une fois la configuration déplacée sur le serveur","toast.virtual_name_required":"Saisissez un nom pour l’appareil virtuel","toast.marker_saved":"Appareil enregistré","toast.marker_removed":"Appareil supprimé du plan","toast.integration_missing":"L’intégration House Plan n’est pas installée — gestion indisponible","toast.plan_formats":"Formats pris en charge : SVG, PNG, JPG, WebP","toast.plan_required":"Importez un plan — il est obligatoire","toast.space_added_onboard":"Espace ajouté. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","toast.space_added":"Espace ajouté","toast.space_saved":"Espace enregistré","toast.space_deleted":"Espace supprimé","toast.delete_failed":"Échec de la suppression : {err}","toast.error":"Erreur : {err}","toast.toggle_target_unavailable":"La cible « {name} » est indisponible — aucune action n’a été effectuée","toast.toggle_targets_unavailable":"Cibles indisponibles : {names}. Aucune action n’a été effectuée","toast.file_failed":"Le fichier « {name} » n’a pas été importé : {err}","toast.files_attached":"Fichiers joints : {n}","err.unknown":"erreur inconnue","err.code":"code {code}","err.too_large":"fichier supérieur à {mb} Mo","err.bad_ext":"type non pris en charge (PDF/image attendu)","err.unauthorized":"droits administrateur requis","editor.title":"Titre","editor.default_floor":"Espace initial","editor.default_floor_missing":"L’espace initial « {id} » n’existe plus. Choisissez un autre espace.","editor.floor":"Espace fixe","editor.floor_none":"— non fixé —","editor.floor_index":"Index YAML {index} (conservé)","fixed_floor.loading":"Chargement de l’espace fixe…","fixed_floor.invalid_title":"L’espace fixe est indisponible","fixed_floor.invalid_body":"Vérifiez floor dans la configuration de la carte. Valeur configurée : {value}","editor.icon_size":"Taille des icônes, % de la largeur du plan","editor.show_temperature":"Afficher la température","editor.live_states":"États en direct (on/off, ouvert…)","editor.light_pools":"Halos lumineux et ombres des murs","editor.show_signal":"Afficher le signal Zigbee (LQI)","editor.language":"Langue de l’interface","editor.lang_auto":"Auto (profil HA)","title.icon_rules":"Règles d’icônes : quelle icône MDI attribuer aux appareils selon leur nom","rules.title":"Règles d’icônes","rules.hint":"Les règles sont testées de haut en bas sur « nom de l’appareil + modèle » (expression régulière insensible à la casse) ; la première correspondance gagne. Si rien ne correspond, la classe d’appareil de l’entité est utilisée, puis l’icône générique.","rules.pattern_ph":"regex, ex. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Ajouter une règle","rules.reset":"Réinitialiser par défaut","rules.test_ph":"Tester un nom d’appareil…","rules.invalid":"expression régulière invalide","rules.saved":"Règles d’icônes enregistrées","btn.up":"Monter","btn.down":"Descendre","tap.info":"Fiche de l’appareil","tap.more_info":"Boîte de dialogue Plus d’infos de HA","tap.toggle":"Basculer l’état","marker.tap_label":"Action au toucher pour cet appareil","tap.toggle_note":"L’état déterminé est affiché ci-dessous. Les appareils sécurisés ne sont jamais basculés depuis le plan.","import.title":"Créer des espaces à partir des étages HA","import.hint":"Votre Home Assistant connaît déjà ces étages. Sélectionnez ceux à transformer en espaces du plan — vous importerez ensuite une image de plan pour chacun. Les pièces sont ensuite tracées manuellement sur le plan.","import.start":"Créer {n} espace(s)","import.manual":"Commencer de zéro","import.progress":"Étage {i} sur {n}","import.done":"Espaces créés. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","btn.skip":"Passer","space.scale_label":"Échelle (taille d’une cellule de grille)","space.scale_unit":"cm par cellule","space.scale_unit_imperial":"po par cellule","space.display_section":"Affichage","space.show_borders":"Toujours afficher les contours des pièces","space.zero_wall_style":"Murs d’épaisseur nulle","space.zero_wall_dashed":"Pointillés","space.zero_wall_solid":"Plein","space.zero_wall_help":"Les murs en pointillés laissent passer la lumière. Les murs pleins bloquent la lumière même avec une épaisseur nulle.","space.show_names":"Afficher les noms des pièces (faire glisser pour déplacer)","space.room_color":"Couleur du contour et du nom","space.opacity":"Opacité","space.fill_label":"Remplissage de la pièce","fill.none":"Aucun","fill.lqi":"Signal Zigbee","fill.light":"Lumières","fill.custom":"Couleur personnalisée","space.custom_fill":"Couleur de remplissage","space.source_file":"J’ai une image de plan","space.source_draw":"Aucune image — je tracerai les pièces à la main","fill.temp":"Température","tip.temp_avg":"température moyenne :","tip.hum_avg":"humidité moyenne :","space_card.button":"Ouvrir le plan de l’espace","space_card.not_found":"Espace « {id} » introuvable","space_card.loading":"Chargement…","continuity.restore_plan":"Restauration du plan…","continuity.restore_connection":"Restauration de la connexion aux appareils…","continuity.retry":"Réessayer","editor.space":"Espace","editor.framing":"Cadrage","editor.fit_content":"Tout le contenu visible","editor.fit_house":"Ajusté à la géométrie de la maison","editor.show_button":"Afficher le bouton","editor.button_label":"Libellé du bouton","editor.button_target":"Chemin du tableau de bord cible","marker.sub_entity":"entité","title.general_settings":"Paramètres généraux","gs.title":"Paramètres généraux","gs.hint":"Les couleurs de remplissage s’appliquent à tous les espaces ; chaque couleur possède sa propre opacité. Le mode de remplissage utilisé par un espace se règle dans la boîte de dialogue de cet espace.","gs.light_group":"Remplissage : lumières","gs.light_on":"Lumières allumées","gs.light_off":"Toutes les lumières éteintes","gs.temp_group":"Remplissage : température","gs.temp_cold":"Froid","gs.temp_ok":"Confortable","gs.temp_hot":"Chaud","gs.lqi_group":"Remplissage : signal Zigbee","gs.lqi_low":"Signal faible","gs.lqi_high":"Signal fort","gs.reset":"Réinitialiser par défaut","gs.saved":"Paramètres généraux enregistrés","space.show_lqi":"Afficher le signal Zigbee (LQI) à côté des appareils","space.hide_decor":"Masquer la couche décorative","space.hide_decor_tip":"Les lignes, formes, étiquettes et meubles restent à leur place — visibles dans l’éditeur d’arrière-plan, mais pas sur le plan.","space.hide_openings":"Masquer les ouvertures","space.hide_openings_tip":"Les symboles de portes, fenêtres et portails ne sont pas dessinés, mais les ouvertures continuent de fonctionner : la lumière les traverse, le soleil entre par les fenêtres et les capteurs de contact continuent de les ouvrir. L’éditeur de plan les affiche toujours.","gs.light_none":"Aucune source lumineuse","mode.plan":"Éditeur de plan","mode.devices":"Éditeur d’appareils","display.value":"Valeur + état","marker.subarea":"aucune zone, manuel","device.new":"Nouvel appareil — ouvrez son éditeur pour fermer","opening.unlock_action":"Déverrouiller","opening.lock_action":"Verrouiller","opening.lock_pending":"En cours…","title.close_editor":"Fermer l’éditeur (retour à la vue)","title.add_device":"Ajouter un appareil au plan","devbar.add":"Ajouter","devbar.rules":"Règles d’icônes","device_inbox.button":"Appareils","device_inbox.title":"Appareils sur le plan","device_inbox.search":"Rechercher appareils, entités et intégrations…","device_inbox.add_virtual":"Ajouter un appareil virtuel","device_inbox.tab_on_plan":"Sur le plan","device_inbox.tab_available":"Disponibles","device_inbox.tab_hidden":"Masqués","device_inbox.tab_readd":"De nouveau disponibles","device_inbox.only_new":"Nouveaux uniquement","device_inbox.show_entities":"Afficher les entités","device_inbox.show_hidden":"Afficher les masqués sur le plan","device_inbox.new":"Nouveau","device_inbox.find":"Trouver sur le plan","device_inbox.edit":"Modifier","device_inbox.hide":"Masquer","device_inbox.show":"Afficher","device_inbox.add":"Ajouter","device_inbox.readd":"Ajouter à nouveau","device_inbox.hide_available":"Masquer de la liste","device_inbox.show_more":"Afficher plus","device_inbox.more_actions":"Plus d'actions","device_inbox.find_hidden_hint":"Activez d'abord « Afficher les masqués sur le plan »","device_inbox.show_disabled":"Activez cette liaison dans Home Assistant avant de l'afficher","device_inbox.empty_on_plan":"Aucun appareil correspondant n'est encore sur le plan.","device_inbox.empty_available":"Aucun appareil disponible à ajouter.","device_inbox.empty_hidden":"Il n'y a aucun appareil masqué.","device_inbox.empty_readd":"Aucun appareil supprimé n'est disponible pour un nouvel ajout.","device_inbox.reason_visible_auto":"Découvert automatiquement","device_inbox.reason_visible_explicit":"Ajouté explicitement","device_inbox.reason_manual_hidden":"Masqué par l'utilisateur","device_inbox.reason_automatic_hidden":"Masqué automatiquement","device_inbox.reason_service_entry":"Entrée de registre de service uniquement","device_inbox.reason_excluded_integration":"Intégration exclue par les filtres d'appareils","device_inbox.reason_excluded_domain":"Type d'entité non spatial","device_inbox.reason_grouped_light":"Représenté par un groupe de lumières de la pièce","device_inbox.reason_represented_by_parent":"Représenté par son appareil parent","device_inbox.reason_removed":"Précédemment retiré du plan","device_inbox.reason_available":"Disponible pour ajout au plan","device_inbox.reason_no_bound_room":"La zone HA n'est liée à aucune pièce du plan","device_inbox.status_ha_disabled":"Désactivé dans Home Assistant","device_inbox.status_orphaned":"La liaison n'existe plus dans Home Assistant","device_inbox.status_unverified":"Le registre Home Assistant est temporairement indisponible","device_inbox.saved":"Liste des appareils mise à jour","space.roomcard_section":"La carte de pièce affiche :","space.label_temp":"Température","space.label_hum":"Humidité","space.label_lqi":"Signal Zigbee moyen","space.label_light":"Lumières allumées/éteintes","roomcard.light_on":"Allumé","roomcard.light_off":"Éteint","roomcard.light_partial":"{on} sur {total}","toast.split_pick_inside":"Les points intermédiaires de découpe doivent se trouver à l’intérieur de la pièce","mode.decor":"Éditeur d’arrière-plan","decor.select":"Sélectionner","decor.line":"Ligne","decor.rect":"Rectangle","decor.ellipse":"Ovale","decor.text":"Texte","decor.erase":"Effacer","decor.erase_confirm_title":"Effacer l’objet ?","decor.color":"Couleur","decor.width":"Épaisseur de ligne","decor.line_style":"Style de ligne","decor.line_style_solid":"Plein","decor.line_style_dashed":"Pointillés","decor.fill":"Remplissage","decor.fill_color":"Couleur de remplissage","decor.length":"Longueur","decor.size":"Taille","decor.angle":"Angle de rotation, °","decor.text_size":"Taille du texte","decor.backdrop_properties":"Propriétés de l’arrière-plan du plan","decor.text_title":"Étiquette de texte","decor.object_title":"Modifier {kind}","decor.text_label":"Texte","decor.live_group":"Insérer une variable HA","decor.live_entity":"Entité","decor.live_entity_ph":"choisir une entité","decor.live_attr":"Valeur","decor.live_attr_ph":"choisir l’état ou un attribut","decor.live_state":"État","decor.backdrop":"Image d’arrière-plan","decor.backdrop_hint":"Faites glisser pour déplacer ; tirez un coin pour redimensionner ; utilisez la poignée supérieure pour faire pivoter. Maj modifie les proportions ou libère l’angle.","decor.backdrop_reset":"Réinitialiser l’image","decor.backdrop_reset_done":"L’image a retrouvé sa position et sa taille d’origine","marker.icon_auto":"Auto : {icon} (selon les règles d’icônes ; choisissez-en une pour remplacer)","marker.icon_pin_auto":"Épingle","mode.plan_tip":"Éditeur de plan — la géométrie du logement : dessiner et diviser/fusionner les pièces, les associer aux zones HA, placer portes, fenêtres et portails, déplacer les cartes de pièces, régler l’échelle","mode.devices_tip":"Éditeur d’appareils — tout ce qui concerne les icônes : faire glisser pour positionner, cliquer pour modifier l’association/l’icône/l’affichage, ajouter des appareils virtuels, règles d’icônes","mode.decor_tip":"Éditeur d’arrière-plan — décoration purement visuelle sous le plan : lignes, rectangles, ovales et étiquettes de texte qui ne réagissent jamais aux clics","space.glow_enabled":"Halo des sources lumineuses","gs.glow_group":"Halo des sources lumineuses","gs.glow_base":"Obscurité de la maison","gs.glow_light":"Couleur / intensité de lumière par défaut","gs.wall_group":"Murs","gs.wall_fill":"Remplissage des murs","gs.glow_radius":"Rayon du halo","gs.unit_m":"m","gs.unit_ft":"pi","marker.controls_label":"Contrôle d’autres sources lumineuses","marker.controls_hint":"Avec l’action au toucher « Basculer », un clic inverse l’état de chaque source ajoutée ici. L’entité propre associée au marqueur est contrôlée directement et ne peut pas être ajoutée ; utilisez l’interrupteur ci-dessous pour la classer comme lumière.","marker.controls_filter":"Rechercher des lumières et interrupteurs…","info.controls":"Contrôle","marker.glow_radius_label":"Rayon du halo","marker.glow_radius.help":"Définit le rayon du halo en mètres ou en pieds ; une valeur vide utilise le rayon des paramètres généraux.","marker.glow_radius.help.aria":"Aide : rayon du halo","markup.wallthick":"Épaisseur","markup.select":"Sélectionner","title.markup_wallthick":"Épaisseur — cliquez sur un mur pour définir son épaisseur de 0 à 100 cm.","markup.hint_wallthick":"cliquez sur un mur · Échap ferme sans appliquer","wallthick.field":"Épaisseur","wallthick.unit_cm":"cm","wallthick.unit_in":"po","wallthick.apply_room":"Appliquer à tous les murs de cette pièce","markup.draw_wall_title":"Épaisseur de chaque nouveau segment de mur (0–100 cm). Les murs partagés conservent la valeur existante.","room.queue_progress":"Pièce {current} sur {total}","toast.wall_rooms_saved":"Pièces créées : {n}","toast.wall_chain_saved":"Chaîne de murs enregistrée","toast.wallthick_pick":"Cliquez sur un mur","toast.wallthick_set":"Épaisseur du mur définie","toast.wallthick_cleared":"Épaisseur du mur supprimée","toast.physical_range":"Saisissez une valeur comprise entre {min} et {max} {unit}","toast.zero_wall_opening_conflict":"Supprimez d’abord l’ouverture sur ce segment de mur.","toast.zero_wall_ambiguous":"Le segment de mur est ambigu. Simplifiez ou ajustez la jonction.","toast.zero_wall_migration_blocked":"L’espace n’a pas été converti : {reason}. Aucune donnée n’a été modifiée.","toast.physical_angle":"Saisissez un angle de rotation compris entre 0° inclus et 90° exclu","toast.physical_limit":"L’espace a atteint la limite pour ce type de géométrie","toast.geometry_unsafe":"Modification annulée : la géométrie des murs n’a pas pu être construite en toute sécurité.","junction.limit_angle":"L'angle entre les murs est trop aigu : {actual}°, minimum {limit}°.","junction.limit_valence":"Trop de murs se rejoignent en un nœud : {actual}, maximum {limit}.","junction.limit_length":"Le segment de mur est trop court : {actual} cm, minimum {limit} cm.","junction.limit_distance":"Les murs et les nœuds sont trop proches : {actual} cm, minimum {limit} cm.","junction.limit_clearance":"Il ne reste plus d'intérieur de pièce : {actual} cm², minimum {limit} cm².","junction.limit_check_failed":"La vérification des jonctions n'a pas pu s'exécuter — la modification n'a pas été enregistrée","toast.wall_model_migration_blocked":"Le modèle de murs n’a pas pu être mis à jour : {reason}. Le plan n’a pas été modifié. Lancez « Optimiser les plans » ; si l’erreur se reproduit, corrigez la géométrie de mur conflictuelle.","toast.wall_model_client_outdated":"Mettez à jour la carte et rechargez la page avant de modifier le plan.","wall_model.reason.invalid-room":"contour de pièce invalide","wall_model.reason.zero-length":"segment de mur de longueur nulle","wall_model.reason.third-owner":"mur partagé par plus de deux pièces","wall_model.reason.duplicate-id":"identifiants de mur en conflit","wall_model.reason.thickness-conflict":"valeurs d’épaisseur de mur en conflit","wall_model.reason.opening-host":"l’ouverture ne peut pas être attribuée à un seul mur","toast.delete_room_pick":"Cliquez à l’intérieur de la pièce que vous souhaitez supprimer","toast.plan_snap_ambiguous":"Zoomez pour choisir le nœud du mur","toast.wall_repair_ambiguous":"Le contour de la pièce possède plusieurs connexions possibles. Zoomez et reliez-le explicitement.","toast.wall_repair_too_large":"L’écart dépasse 2 cm. Reliez les murs avant de créer la pièce.","toast.wall_repair_changed":"La géométrie des murs a changé. Essayez de créer de nouveau la pièce.","toast.opening_on_zero_wall":"Les ouvertures ne peuvent pas être placées sur un mur d’épaisseur nulle","marker.from_ha_option":"Choisir dans la liste HA","marker.show_entities":"Afficher les entités","marker.show_entities_tip":"Ajoute à la liste non seulement les appareils, mais aussi toutes leurs entités","marker.pick_ph":"Choisir un appareil…","room.open_area":"Ouvrir la zone HA","view.volumetric":"Vue volumétrique","view.flat":"Vue plane","kiosk.title":"Tailles pour cet écran","kiosk.hint":"Enregistrées uniquement sur cet appareil — chaque tablette murale ou TV peut avoir ses propres tailles confortables.","kiosk.icon_scale":"Taille des icônes d’appareils","kiosk.font_scale":"Taille du texte des cartes de pièces","editor.kiosk":"Mode appareil mural (kiosque)","editor.cycle":"Changer automatiquement d’espace toutes les N secondes (kiosque, 0 = désactivé)","room.settings_title":"Paramètres de la pièce","room.settings_section":"Paramètres de la pièce (remplacent ceux de l’espace)","room.fill_label":"Remplissage de CETTE pièce","fill.inherit":"Comme l’espace","room.custom_fill_space":"Couleur de l’espace","room.custom_fill_own":"Couleur de la pièce","room.temp_src_label":"Source de température","room.hum_src_label":"Source d’humidité","room.src_average":"Moyenne des capteurs de la pièce (par défaut)","room.src_pick":"Un appareil ou une entité HA spécifique","room.src_ph":"Choisir une source…","toast.room_updated":"Pièce mise à jour","space.card_font":"Taille de police des cartes de pièces (tout l’espace)","room.sizes_section":"Tailles de police","room.name_scale":"Taille du nom de la pièce","room.label_scale":"Taille des mesures","preview.room_name":"Salon","toast.cfg_reload_failed":"Impossible de recharger le plan depuis le serveur : {err}","toast.locale_load_failed":"Impossible de charger le pack de langue. L'anglais est utilisé jusqu'au rechargement de la page.","room.settings_short":"Paramètres de la pièce","room.unnamed":"Pièce sans nom","marker.use_climate_temp":"Inclure la température de l’appareil dans la pièce","marker.use_climate_temp_tip":"Ajoute la valeur current_temperature d’un climatiseur ou thermostat à la moyenne de la pièce. Configurez séparément le badge de valeur externe ci-dessous.","marker.value_badge_title":"Badge de valeur","marker.value_badge.help":"Affiche une valeur sélectionnée à côté de l’icône. Cela n’affecte pas les mesures de la pièce, la lumière, le halo ni l’action au toucher.","marker.value_badge.help.aria":"Aide : badge de valeur de l’appareil","marker.value_badge_enabled":"Afficher un badge de valeur","marker.value_badge_source":"Valeur","marker.value_badge_source.help":"Choisissez un état précis, un attribut pris en charge ou une valeur dérivée de cet appareil.","marker.value_badge_source.help.aria":"Aide : source du badge de valeur","marker.value_badge_position":"Position","marker.value_badge_position.help":"Le côté choisi reste fixe et ne bascule pas automatiquement au bord du plan.","marker.value_badge_position.help.aria":"Aide : position du badge de valeur","marker.value_badge_right":"À droite","marker.value_badge_bottom":"En dessous","marker.value_badge_left":"À gauche","marker.value_badge_top":"Au-dessus","marker.value_badge_empty":"Cet appareil n’a aucune valeur disponible","marker.value_badge_static":"Une icône statique n’affiche pas de valeurs en direct. Le paramètre est conservé.","marker.value_badge_missing":"Source indisponible","marker.value_badge_missing_hint":"La source enregistrée est actuellement indisponible. Conservez-la, désactivez le badge ou choisissez une autre valeur.","marker.value_badge_duplicate":"La même valeur est déjà affichée dans l’icône.","marker.value_badge_state":"État · {name}","marker.value_badge_marker_state":"État de la source lumineuse · {name}","marker.value_badge_lqi":"Qualité moyenne du signal Zigbee","marker.value_badge_attr_current_temperature":"Température actuelle · {name}","marker.value_badge_attr_temperature":"Température cible · {name}","marker.value_badge_attr_current_humidity":"Humidité actuelle · {name}","marker.value_badge_attr_humidity":"Humidité · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Vitesse · {name}","marker.value_badge_attr_brightness":"Luminosité · {name}","marker.value_badge_attr_volume_level":"Volume · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Vitesse du ventilateur · {name}","marker.light_role_label":"Cet appareil est-il une source lumineuse ?","marker.light_role.help":"Détermine si ce marqueur est lui-même une source lumineuse spatiale. Les lumières liées ci-dessus restent des contrôles de lumière de pièce indépendants.","marker.light_role.help.aria":"Aide : cet appareil est-il une source lumineuse","marker.light_role_auto_yes":"Auto (source lumineuse)","marker.light_role_auto_no":"Auto (pas une source lumineuse)","marker.light_role_always":"Toujours une source lumineuse","marker.light_role_never":"Jamais une source lumineuse","marker.light_entity_label":"Entité lumineuse principale","marker.light_entity.help":"Pour un appareil composite, choisissez l’entité dont l’état, la couleur et la luminosité représentent cette source sur le plan. Le choix automatique reste compatible avec les anciens plans.","marker.light_entity.help.aria":"Aide : entité lumineuse principale","marker.light_entity_auto":"Automatique ({entity})","marker.light_entity_none":"aucune entité contrôlable","marker.light_entity_missing":"L’entité enregistrée {entity} est indisponible. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.toggle_entity_label":"Entité à basculer","marker.toggle_entity.help":"Pour un appareil composite, choisissez précisément la lumière ou l’interrupteur propre à actionner par un toucher. Automatique conserve les règles de cible précédentes et reste indépendant de l’entité lumineuse principale.","marker.toggle_entity.help.aria":"Aide : entité à basculer","marker.toggle_entity_auto":"Automatique ({entity})","marker.toggle_entity_none":"aucune entité propre contrôlable","marker.toggle_entity_missing":"L’entité enregistrée {entity} ne fait plus partie des canaux sélectionnables de ce marqueur. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.glow_color_label":"Couleur et luminosité du halo","marker.glow_mode.help":"Utilisez les valeurs en direct de la source, remplacez uniquement sa couleur ou fixez à la fois la couleur et la luminosité. Le minimum est 1 % ; pour désactiver la source, choisissez « Jamais ».","marker.glow_mode.help.aria":"Aide : couleur et luminosité du halo","marker.glow_mode_auto":"Depuis la source","marker.glow_mode_color":"Définir la couleur","marker.glow_mode_fixed":"Définir la couleur et la luminosité","marker.glow_color":"Couleur du halo","marker.glow_brightness":"Luminosité","marker.glow_disabled_never":"Les paramètres du halo sont indisponibles car ce marqueur est explicitement défini comme n’étant pas une source lumineuse.","marker.glow_disabled_auto":"Le mode Auto n’a trouvé aucune source lumineuse spatiale pour ce marqueur.","marker.glow_disabled_no_entity":"Aucune entité Home Assistant contrôlable et active n’est disponible pour cette source spatiale.","marker.glow_passive_hint":"Cette source ne possède aucune donnée Home Assistant propre. Définissez manuellement sa couleur et sa luminosité ; son rayon reste disponible.","marker.control_broken":"La source enregistrée est absente ou n’est plus marquée comme lumière","marker.control_missing_label":"Lumière du plan manquante","marker.control_passive":"source passive","toast.marker_control_cycle":"Ce lien créerait une chaîne circulaire de contrôles de lumière.","toast.marker_binding_required":"Choisissez un appareil Home Assistant avant de lier une autre source lumineuse.","confirm.unlock":"Déverrouiller « {name} » ?","toast.files_migrate_failed":"Les pièces jointes n’ont pas pu être déplacées vers la nouvelle association ; les liens continuent de pointer vers les anciens fichiers : {err}","space.pick_saved":"Déjà importé","space.pick_saved_hint":"Plans stockés sur le serveur, y compris ceux que vous avez précédemment dissociés","space.no_saved":"Aucun plan n’est encore stocké sur le serveur.","space.loading":"Chargement…","space.used_by":"utilisé par : {list}","space.in_use":"Un espace utilise encore ce plan — dissociez-le d’abord","btn.use":"Utiliser","confirm.delete_plan":"Supprimer le fichier de plan « {name} » du serveur ? Cette action est irréversible.","toast.plans_list_failed":"Impossible de lister les plans stockés : {err}","toast.plan_delete_failed":"Impossible de supprimer le plan : {err}","marker.hide":"Masquer","marker.hide_tip":"L’appareil disparaîtra du plan après l’enregistrement mais continuera de compter dans le signal de la pièce. Restaurez-le via « Masqués et désactivés » dans l’éditeur d’appareils.","marker.show":"Afficher","marker.show_tip":"L’appareil apparaîtra de nouveau sur le plan après l’enregistrement.","marker.hidden_ghost":"Appareil masqué par l’utilisateur","marker.ha_disabled_device":"L’appareil est désactivé dans Home Assistant et masqué du plan.","marker.ha_disabled_entity":"L’entité est désactivée dans Home Assistant et masquée du plan.","marker.ha_disabled_all_entities":"L’appareil n’a aucune entité Home Assistant active ; il est donc masqué du plan.","marker.ha_registry_limited":"Le registre Home Assistant complet n’est pas accessible à cet utilisateur. L’objet non vérifié ne peut pas être affiché ni utilisé pour le moment.","marker.delete_tip":"Supprimer complètement l’appareil du plan et de tous les agrégats. Vous pourrez l’ajouter de nouveau plus tard.","tap.run":"Exécuter une automatisation/un script/une scène","marker.run_target_label":"Élément à exécuter","marker.run_search_ph":"Rechercher : automatisation, script ou scène…","marker.run_target_gone":"Cible {id} introuvable — choisissez-en une autre","marker.tap_confirm":"Demander une confirmation","marker.tap_confirm_tip":"Afficher une boîte de dialogue de confirmation avant l’action — protection contre les appuis accidentels.","marker.toggle_hint_single":"Cible : {name} ({id}).","marker.virtual_light_target":"Lumière virtuelle : {name}.","marker.virtual_light_current":"État manuel : {state} → {effect}.","marker.virtual_light_state_on":"allumé","marker.virtual_light_state_off":"éteint","marker.toggle_hint_group":"Basculera {count} source(s) : {names}.","marker.toggle_hint_current":"Actuellement : {state} → {effect}.","marker.toggle_hint_group_current":"Actuellement allumées : {on} sur {count} → {effect}.","marker.toggle_hint_skipped":"Seront ignorées ({count}) : {targets}.","marker.toggle_effect_turn_on":"sera allumé","marker.toggle_effect_turn_off":"sera éteint","marker.toggle_effect_open":"sera ouvert","marker.toggle_effect_close":"sera fermé","marker.toggle_effect_stop":"sera arrêté","marker.toggle_effect_toggle":"l’état sera basculé","marker.toggle_skip_missing":"introuvable","marker.toggle_skip_ha_disabled":"désactivé dans HA","marker.toggle_skip_unavailable":"indisponible","marker.toggle_skip_unsupported":"basculement non pris en charge","marker.toggle_skip_secure":"bloqué pour des raisons de sécurité","marker.toggle_none_no_binding":"Cet appareil n’a aucune association ; il n’y a donc aucun état à basculer. Un toucher ne fera rien.","marker.toggle_none_no_actionable_entity":"Cet appareil n’a aucun état pouvant être basculé. Un toucher ne fera rien.","marker.toggle_none_configured_targets_missing":"Les cibles configurées sont indisponibles. L’entité propre de l’appareil ne leur sera pas substituée.","marker.toggle_none_ha_disabled":"La cible est désactivée dans Home Assistant et ne peut pas être utilisée sur le plan.","marker.toggle_none_unavailable":"La cible est actuellement indisponible. Un toucher ne fera rien.","marker.toggle_none_unsupported":"Home Assistant n’expose aucune opération de basculement sûre pour cette entité. Un toucher ne fera rien.","marker.toggle_none_secure":"Le basculement des serrures, alarmes et portails sécurisés depuis le plan est bloqué pour des raisons de sécurité.","run.automation":"automatisation","run.script":"script","run.scene":"scène","confirm.tap_run":"Exécuter « {name} » ?","confirm.tap_toggle":"Basculer « {name} » ?","confirm.current_state":"État actuel : {state}","confirm.expected_state":"Après basculement : {state}","confirm.group_current":"{on} allumé(s) sur {total}","confirm.group_all_on":"tout est allumé","confirm.group_all_off":"tout est éteint","confirm.unavailable_targets":"Indisponibles : {count}","confirm.expected_by_ha":"Home Assistant déterminera l’état","confirm.state_on":"Allumé","confirm.state_off":"Éteint","confirm.state_open":"Ouvert","confirm.state_closed":"Fermé","confirm.state_opening":"Ouverture en cours","confirm.state_closing":"Fermeture en cours","confirm.state_stopped":"Arrêté","confirm.state_unknown":"Inconnu","toast.run_started":"Démarré : {name}","toast.run_target_missing":"Cible d’exécution introuvable — vérifiez les paramètres de l’appareil","toast.run_target_required":"Choisissez une automatisation, un script ou une scène","toast.tap_target_changed":"La cible de l’action a changé. Réessayez.","toast.virtual_light_toggle_failed":"Impossible de basculer la lumière virtuelle : {err}","toast.value_badge_source_required":"Choisissez une valeur pour le badge","btn.run":"Exécuter","vac.section":"Robot aspirateur : position en direct","vac.autocal":"Configurer automatiquement","vac.live":"Position en direct sur le plan","vac.trail":"Afficher le trajet du robot","vac.cal_maps":"Cartes calibrées : {maps}","vac.autocal_no_rooms":"L’intégration ne fournit aucune liste de pièces — ouvrez « Ajuster manuellement »","vac.autocal_no_match":"Les noms de pièces ne correspondent pas (il en faut ≥3 en commun) — ouvrez « Ajuster manuellement »","vac.autocal_done":"Terminé : association via {rooms} pièces. Lancez un nettoyage et vérifiez","vac.cal_need_pos":"Le robot ne transmet pas de coordonnées — lancez un nettoyage puis mettez-le en pause","vac.cal_done":"Calibration enregistrée. Lancez un nettoyage et vérifiez","vac.cal_cancelled":"Calibration annulée","vac.fit":"Ajuster manuellement","vac.fit_hint":"Faites glisser la carte du robot pour la positionner, étirez-la par les coins","vac.fit_rotate":"Rotation de 90°","vac.fit_mirror":"Miroir","vac.trail_never":"Jamais","vac.trail_cleaning":"Pendant le nettoyage","vac.trail_always":"Toujours","gs.bg_group":"Arrière-plan de la scène","gs.bg_color":"Arrière-plan autour du plan","gs.bg_default":"Valeur par défaut du thème","gs.bg_theme":"valeur par défaut du thème","gs.bg_mode":"Arrière-plan du plan","gs.bg_static":"Couleur statique","gs.bg_daynight":"Suit le soleil","gs.bg_daynight_hint":"L’arrière-plan évolue entre l’aube, le jour, le crépuscule et la nuit en utilisant sun.sun, ou votre heure locale si les données du soleil sont indisponibles. Aucune orientation du nord n’est nécessaire pour l’arrière-plan.","gs.sun_group":"Soleil","gs.sun_missing":"L’entité sun.sun est introuvable — l’arrière-plan suit votre heure locale ; les rayons de soleil par les fenêtres sont indisponibles.","gs.north":"Nord sur le plan","gs.north_ph":"non défini","gs.north_hint":"Définissez le nord uniquement pour les rayons par les fenêtres (pas de 1°, 15° avec Maj) ; l’arrière-plan Soleil fonctionne sans lui.","gs.north_clear":"Effacer","gs.north_letter":"N","gs.sun_rays":"Lumière du soleil à travers les fenêtres","gs.about_group":"À propos","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · documentation et problèmes","gs.about_telegram":"Discussion Telegram","space.bg_color":"Arrière-plan autour du plan","space.bg_inherit":"Hériter des paramètres généraux","space.bg_inherited":"hérite des paramètres généraux","space.bg_mode":"Arrière-plan du plan","space.north":"Nord sur le plan (remplacement)","space.north_inherited":"hérité : {v}","space.sun_rays":"Lumière du soleil à travers les fenêtres","space.sun_inherit":"Hériter des paramètres généraux","space.sun_on":"Activé","space.sun_off":"Désactivé","canvas.far_objects":"{n} objet(s) éloigné(s) du plan","canvas.show_far":"Afficher","canvas.home_tip":"Le plan est là-bas — cliquez pour l’ajuster à l’écran","gs.grid_group":"Maintenance du plan","gs.grid_hint":"Met à jour les modèles de données, aligne les éléments du plan sur la grille et fusionne les fragments de murs redondants. Un rapport précis est affiché avant tout enregistrement.","gs.align_all":"Optimiser les plans","gs.align_title":"Optimiser les plans","gs.align_none":"Tous les plans utilisent déjà le modèle de données optimisé actuel.","gs.optimize_no_automatic_changes":"Aucune modification automatique n’est à appliquer. Vérifiez les éléments ci-dessous.","gs.align_count":"{n} élément(s) sur {total} seront déplacés, de {cm} cm au maximum.","gs.align_where":"Le plus grand déplacement se trouve dans « {s} ».","gs.align_turned":"Ouvertures dont l’angle est corrigé : {n}.","gs.align_removed_drafts":"Contours invalides réduits par la grille et supprimés : {n}.","gs.optimize_redundant_drafts":"Chaînes de murs enregistrées masquées par des murs de pièces pleins et supprimées : {n}.","gs.align_preflight_failed":"Impossible de vérifier en toute sécurité la géométrie des espaces suivants : {spaces}{more}.","gs.align_preflight_hint":"Les plans n’ont pas été modifiés. Copiez les diagnostics avec le bouton ci-dessous et joignez-les au rapport de bug avec un export de l’espace.","gs.preflight_reason_prepare-exception":"Impossible de préparer la géométrie de l’espace (exception lors de la construction du modèle)","gs.preflight_reason_wall-null":"Le corps du mur n’a pas pu être construit (l’union a renvoyé un résultat vide)","gs.preflight_reason_wall-degraded-extra":"Le corps du mur s’est dégradé avec une géométrie supplémentaire","gs.preflight_reason_wall-failed-core":"Le noyau du corps du mur n’a pas pu être assemblé","gs.preflight_reason_wall-exception":"La construction du mur a déclenché une exception","gs.preflight_reason_floor-null":"Le contour du sol n’a pas pu être construit","gs.preflight_reason_floor-exception":"La construction du sol a déclenché une exception","gs.preflight_copy":"Copier les diagnostics","gs.preflight_copied":"Diagnostics copiés","gs.preflight_update_hint":"Les versions de la carte et de l’intégration diffèrent — mettez House Plan à jour puis réessayez.","gs.align_preflight_space":"Espace {n}","gs.align_preflight_more":", et {n} de plus","gs.optimize_changes":"Migrations de modèle : {m} ; espaces mis à jour : {c} ; valeurs de coordonnées parasites supprimées : {p} ; fragments de murs réels fusionnés : {w} ; fragments de murs d’épaisseur nulle fusionnés : {s} ; murs indépendants : {i}.","gs.zero_walls_migrated":"Portions de murs virtuels converties : {n}.","gs.wall_segments_migrated":"Segments de murs stabilisés : {n}.","gs.optimize_lattice_summary":"Valeurs de coordonnées parasites normalisées : {n} ; déplacement maximal : {cm} cm.","gs.optimize_lattice_space":"{space} : valeurs de coordonnées normalisées : {n} ; valeurs hors grille laissées inchangées : {far}.","gs.optimize_coincident_partitions":"Sections de murs indépendants masquées absorbées dans les murs des pièces : {n}.","gs.optimize_openings_rehosted":"Ouvertures rattachées aux murs des pièces : {n}.","gs.optimize_walls_straightened":"Murs redressés : {n} ; déplacement maximal : {cm} cm.","gs.optimize_walls_straightened_where":"Plus grande correction de mur : {s}.","gs.optimize_walls_straighten_skipped":"Murs proches d’un axe laissés inchangés car ils ne pouvaient pas être réparés en toute sécurité : {n}.","gs.optimize_glow_migration":"Ancien mode Glow : {spaces} espaces → aucun remplissage de données + Glow indépendant ; {rooms} pièces → remplissage de données hérité + Glow indépendant.","gs.optimize_references":"Références réparées : espaces — {spaces} ; pièces — {rooms} ; positions — {positions} ; appareils dissociés des espaces manquants — {detached}.","gs.optimize_reference_more":", et {n} de plus","gs.optimize_orphans_removed":"Enregistrements oubliés supprimés : {total} — étiquettes de pièces : {rooms} ; appareils : {devices} ; marqueurs de groupe : {groups}. Ils appartenaient à des espaces supprimés précédemment.","gs.optimize_live_positions":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles seront conservées.","gs.optimize_live_positions_remove":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles sont sélectionnées pour suppression.","gs.optimize_live_names":" : {names}{more}","gs.optimize_live_remove":"Supprimer les anciennes positions","gs.optimize_live_keep":"Conserver les anciennes positions","gs.optimize_live_selected":"Les anciennes positions seront supprimées après l’application de l’optimisation.","gs.optimize_unverified":"Impossible de vérifier en toute sécurité les positions : {n}. Elles ont été laissées inchangées.","gs.optimize_registry_limited":"Un accès administrateur complet aux registres Home Assistant est requis pour une vérification sûre.","gs.optimize_vacuum_warning":"Associations de pièces d’aspirateur nécessitant encore une vérification : {n}.","gs.optimize_details":"Détails","gs.optimize_details_more":"Et {n} enregistrement(s) de plus.","gs.optimize_detail_removed":"sera supprimé","gs.optimize_detail_live":"sera conservé","gs.optimize_detail_unverified":"non vérifié","gs.optimize_detail_room_label":"étiquette de pièce","gs.optimize_detail_device":"appareil","gs.optimize_detail_group":"marqueur de groupe","gs.optimize_detail_unknown":"propriétaire inconnu","gs.optimize_detail_item":"{status} : {kind} {id} ; ancien espace {space}","gs.align_warn":"Les éléments volontairement placés entre les nœuds de la grille seront déplacés. Une seule annulation est disponible après l’opération, uniquement jusqu’à la prochaine modification du plan.","gs.align_run":"Optimiser","gs.align_done":"Plans optimisés : {n} élément(s) déplacé(s), {m} enregistrement(s) maintenu(s), {r} référence(s) réparée(s)","gs.optimize_undo":"Annuler la dernière optimisation","gs.optimize_undone":"La dernière optimisation a été annulée","decor.furniture":"Mobilier","furn.title":"Bibliothèque de mobilier","furn.symbol":"Symbole","furn.group_furniture":"Mobilier","furn.group_appliance":"Électroménager","furn.group_sanitary":"Sanitaires","furn.group_other":"Autre","furn.width":"Largeur","furn.depth":"Profondeur","furn.back_to_categories":"Toutes les catégories","furn.pick_hint":"Choisissez un symbole, puis cliquez sur le plan.","furn.place_hint":"Cliquez sur le plan — le meuble se place contre le mur le plus proche. Maj permet un placement libre.","furn.cat_air_conditioner":"Climatiseurs","furn.cat_armchair":"Fauteuils","furn.cat_bathtub":"Baignoires","furn.cat_bed":"Lits","furn.cat_bidet":"Bidets","furn.cat_boiler":"Chauffe-eau","furn.cat_chair":"Chaises","furn.cat_coffee_table":"Tables basses","furn.cat_cooktop":"Plaques de cuisson","furn.cat_dining_table":"Tables à manger","furn.cat_dishwasher":"Lave-vaisselle","furn.cat_dryer":"Sèche-linge","furn.cat_fireplace":"Cheminées","furn.cat_fridge":"Réfrigérateurs","furn.cat_kitchen_cabinet":"Meubles de cuisine","furn.cat_kitchen_sink":"Éviers de cuisine","furn.cat_nightstand":"Meubles","furn.cat_plant":"Plantes","furn.cat_rug":"Tapis","furn.cat_shelving":"Étagères","furn.cat_shower":"Douches","furn.cat_sink":"Lavabos","furn.cat_sofa":"Canapés","furn.cat_stairs":"Escaliers","furn.cat_toilet":"Toilettes","furn.cat_tv":"Téléviseurs","furn.cat_wardrobe":"Armoires","furn.cat_washer":"Lave-linge","furn.cat_work_table":"Bureaux","furn.sym_sofa":"Canapé","furn.sym_sofa_three_seat":"Canapé trois places","furn.sym_sofa_corner_right":"Canapé d'angle droit","furn.sym_armchair":"Fauteuil","furn.sym_armchair_office":"Chaise de bureau","furn.sym_coffee_table":"Table basse","furn.sym_coffee_table_round":"Table basse ronde","furn.sym_coffee_table_oval":"Table basse ovale","furn.sym_coffee_table_rounded":"Table basse arrondie","furn.sym_table_dining":"Table à manger","furn.sym_table_round":"Table ronde","furn.sym_table_dining_oval":"Table à manger ovale","furn.sym_table_dining_rounded":"Table à manger arrondie","furn.sym_chair":"Chaise","furn.sym_chair_bar":"Tabouret de bar","furn.sym_desk":"Bureau","furn.sym_desk_corner":"Bureau d'angle","furn.sym_bed_double":"Lit double","furn.sym_bed_single":"Lit simple","furn.sym_nightstand":"Table de chevet","furn.sym_cabinet_tv":"Meuble TV","furn.sym_cabinet_shoe":"Meuble à chaussures","furn.sym_cabinet_sink":"Meuble sous lavabo","furn.sym_wardrobe":"Armoire","furn.sym_bookshelf":"Bibliothèque","furn.sym_wall_unit":"Élément mural","furn.sym_kitchen_floor":"Module de cuisine bas","furn.sym_kitchen_floor_corner":"Module de cuisine bas d'angle","furn.sym_kitchen_wall":"Module de cuisine haut","furn.sym_kitchen_wall_corner":"Module de cuisine haut d'angle","furn.sym_shelf_floor":"Étagère au sol","furn.sym_shelf_wall":"Étagère murale","furn.sym_fridge":"Réfrigérateur","furn.sym_stove":"Cuisinière","furn.sym_cooktop_two":"Plaque à deux feux","furn.sym_dishwasher":"Lave-vaisselle","furn.sym_washer":"Lave-linge","furn.sym_dryer":"Sèche-linge","furn.sym_tv":"Téléviseur","furn.sym_tv_wall":"Téléviseur mural","furn.sym_ac":"Climatiseur","furn.sym_water_heater":"Chauffe-eau","furn.sym_toilet":"Toilettes","furn.sym_toilet_built_in":"Toilettes suspendues","furn.sym_bathtub":"Baignoire","furn.sym_bathtub_corner":"Baignoire d'angle","furn.sym_shower":"Douche","furn.sym_sink":"Lavabo","furn.sym_kitchen_sink":"Évier de cuisine","furn.sym_kitchen_sink_double":"Évier de cuisine double","furn.sym_bidet":"Bidet","furn.sym_bidet_built_in":"Bidet suspendu","furn.sym_stairs":"Escalier","furn.sym_fireplace":"Cheminée","furn.sym_plant":"Plante","furn.sym_rug":"Tapis","common.yes":"Oui","common.no":"Non","vac.diag_source":"Source","vac.diag_platform":"Intégration","vac.diag_status":"État","vac.diag_position":"Position","vac.diag_rooms":"Pièces","vac.diag_rooms_value":"{total} · {matched} noms correspondent · {readiness}","vac.autocal_ready":"calibration automatique disponible","vac.autocal_not_ready":"3 noms correspondants requis","vac.diag_path":"Chemin de l’intégration","vac.diag_map":"ID de carte","vac.source_none":"non sélectionné","vac.source_status_ok":"Prêt","vac.source_status_missing":"Manquant","vac.source_status_disabled":"Désactivé dans Home Assistant","vac.source_status_unavailable":"Indisponible","vac.source_status_unverified":"Impossible à vérifier avec les autorisations actuelles","vac.source_status_unsupported":"Aucune donnée de position","vac.source_status_none":"Aucune source","vac.source_banner_missing":"La source enregistrée n’existe plus. Elle n’a pas été remplacée automatiquement ; choisissez une autre source ou restaurez-la dans Home Assistant.","vac.source_banner_disabled":"La source enregistrée est désactivée dans Home Assistant. Activez-la ou choisissez une autre source.","vac.source_banner_unverified":"Les autorisations Home Assistant actuelles ne permettent pas de vérifier cette source enregistrée. Elle reste épinglée et ne sera pas remplacée automatiquement.","vac.choose_source":"Choisir la source","vac.source_auto":"Automatique","vac.source_auto_hint":"Utiliser une entité compatible de cet appareil","vac.all_cameras":"Toutes les caméras","vac.all_cameras_warn":"Une caméra peut ne pas fournir de données du robot. Choisissez-en une uniquement s’il s’agit de la carte de votre aspirateur.","vac.all_cameras_empty":"Aucune autre entité caméra trouvée.","vac.platform_unknown":"intégration inconnue","vac.cap_position":"position","vac.cap_rooms_short":"pièces","vac.cap_path":"trajet","vac.cap_map":"ID de carte","vac.cap_none":"aucune donnée de robot détectée","vac.xcme_hint":"Activez ces attributs Xiaomi Cloud Map Extractor :","vac.documentation":"Documentation","vac.residual_title":"Vérifier la calibration automatique","vac.residual_message":"Les pièces correspondantes présentent un écart maximal de {error}. Appliquez cette calibration approximative, affinez-la manuellement ou annulez sans modifier la configuration enregistrée.","vac.apply_proposal":"Appliquer","gs.backup_group":"Sauvegarde et transfert","gs.backup_hint":"Téléchargez une sauvegarde JSON portable ou prévisualisez et importez une sauvegarde créée par House Plan.","backup.export_open":"Exporter","backup.import_open":"Importer","backup.export_title":"Exporter House Plan","backup.import_title":"Importer House Plan","backup.export_hint":"Choisissez si la sauvegarde doit contenir toute la configuration House Plan ou uniquement l’espace actuel.","backup.full":"Sauvegarde complète","backup.current_space":"Espace actuel","backup.current_space_title":"Espace actuel : {title}","backup.no_current_space":"Aucun espace actuel","backup.plan_only":"Plan uniquement","backup.plan_only_hint":"Conserver les pièces, murs, ouvertures, décorations et positions des étiquettes de pièces sans les appareils ni les associations Home Assistant.","backup.plan_only_preview":"Ce fichier contient uniquement le plan","backup.privacy_warning":"L’archive conserve les noms, identifiants Home Assistant et coordonnées exactes. Les plans internes et pièces jointes sont référencés, pas intégrés ; les états d’exécution et trajets d’aspirateur ne sont pas inclus.","backup.download":"Télécharger le JSON","backup.export_done":"Sauvegarde téléchargée","backup.reading":"Vérification de la sauvegarde…","backup.revalidated":"Le plan a changé depuis cet aperçu. Le résumé a été actualisé ; vérifiez-le et confirmez de nouveau.","backup.error.unauthorized":"Vous n’avez pas l’autorisation d’exporter ou d’importer ce plan.","backup.error.not_ready":"House Plan n’est pas encore prêt. Réessayez dans un instant.","backup.error.too_large":"La sauvegarde dépasse la limite de 8 Mio.","backup.error.invalid_json":"Le fichier sélectionné n’est pas un JSON valide.","backup.error.invalid_format":"Le fichier sélectionné n’est pas une sauvegarde House Plan.","backup.error.unsupported_export_version":"Ce format de sauvegarde n’est pas pris en charge par la version installée.","backup.error.future_model":"La sauvegarde a été créée avec un modèle de données House Plan plus récent.","backup.error.invalid_config":"La sauvegarde contient une configuration House Plan invalide.","backup.error.wall_model_migration_blocked":"Le modèle de murs de la sauvegarde n’a pas pu être mis à niveau en toute sécurité. Optimisez d’abord le plan source, puis exportez-le de nouveau.","backup.error.invalid_layout":"La sauvegarde contient des positions d’objets invalides.","backup.error.invalid_content":"La sauvegarde contient des références de contenu invalides ou incohérentes.","backup.error.space_not_found":"L’espace sélectionné n’existe plus.","backup.error.capacity_exceeded":"L’ajout de cette sauvegarde dépasserait les limites du plan.","backup.error.preview_expired":"L’aperçu a expiré. Sélectionnez de nouveau le fichier de sauvegarde.","backup.error.preview_owner_mismatch":"Cet aperçu appartient à un autre utilisateur Home Assistant.","backup.error.conflict":"Le plan a changé depuis l’aperçu. Vérifiez le résumé actualisé.","backup.error.content_confirmation_required":"Confirmez que le contenu local indisponible peut être dissocié.","backup.error.commit_failed":"La sauvegarde n’a pas pu être appliquée en toute sécurité. Le plan précédent a été restauré ou est en attente de récupération.","backup.error.missing_plan":"Un fichier de plan référencé a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.missing_content":"Une pièce jointe locale référencée a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.marker_control_missing":"Une lumière liée au plan est absente de la sauvegarde.","backup.error.marker_control_not_light":"Une cible liée au plan n’est plus marquée comme source lumineuse.","backup.error.marker_control_self":"Une source lumineuse ne peut pas se contrôler elle-même.","backup.error.marker_control_cycle":"La sauvegarde contient une chaîne circulaire de contrôles de lumière.","backup.error.duplicate_marker_control":"La sauvegarde contient un lien de lumière du plan en doublon.","backup.error.no_backup":"Aucun instantané d’importation ou d’optimisation n’est disponible à restaurer.","backup.same_source":"Créée sur cette instance Home Assistant","backup.foreign_source":"Créée sur une autre instance Home Assistant","backup.created":"Créée : {value}","backup.versions":"Carte {card} ; intégration {integration} ; modèle de données {model}","backup.count_spaces":"Espaces : {n}","backup.count_rooms":"Pièces : {n}","backup.count_walls":"Murs : {n}","backup.count_openings":"Ouvertures : {n}","backup.count_decor":"Objets décoratifs : {n}","backup.count_markers":"Appareils : {n}","backup.count_layout":"Positions : {n}","backup.bindings":"Associations — appareils : {device}, entités : {entity}, virtuels : {virtual} ; positions sans espace : {legacy}","backup.binding_status":"État des cibles — actives : {active}, désactivées : {disabled}, manquantes : {missing}","backup.missing_areas":"Zones absentes sur la cible : {areas}","backup.dropped_marker_links":"Liens de lumières du plan hors de cet espace transféré omis : {n}.","backup.repaired_target_refs":"Références existantes restaurées par cet import d’espace : {n}.","backup.preserved_unresolved_refs":"Références qui n’ont pas pu être restaurées sans ambiguïté conservées : {n}.","backup.preserved_unresolved_hint":"Aucune donnée n’a été devinée ni supprimée. Après l’importation, lancez « Optimiser les plans » pour examiner les références restantes.","backup.import_details":"Détails des références d’importation","backup.import_detail.incoming_remapped":"Références mises à jour dans la copie importée : {n}","backup.import_detail.target_repaired":"Références existantes restaurées : {n}","backup.import_detail.preserved_unresolved":"Références non résolues conservées : {n}","backup.import_detail.collisions":"Conflits de destination conservés en toute sécurité : {n}","backup.import_detail.dropped_links":"Liens entrants omis selon les règles de transfert : {n}","backup.import_detail.bounded_lineages":"Identifiants trop imbriqués maintenus dans les limites : {n}","backup.replace_warning":"Cela remplace la configuration et la disposition actuelles. Les fichiers importés ne sont jamais supprimés. Une seule annulation reste disponible jusqu’à la prochaine modification du plan.","backup.foreign_bookkeeping":"Les données de suivi des appareils connus/nouveaux propres à l’instance ne seront pas importées.","backup.final_name":"Nom du nouvel espace","backup.target_settings":"Les paramètres généraux de l’instance cible restent inchangés.","backup.duplicates":"Associations déjà présentes : {n}","backup.skip":"Ignorer les appareils en doublon","backup.virtual_copy":"Ajouter des copies virtuelles statiques sûres","backup.content":"Contenu référencé","backup.content_available":"disponible localement","backup.content_external":"lien externe","backup.content_detach_required":"sera dissocié","backup.confirm_detach":"Je comprends que les plans internes et pièces jointes indisponibles seront dissociés de la configuration importée.","backup.replace":"Remplacer","backup.add":"Ajouter un espace","backup.space_done":"Espace importé : {rooms} pièces, {markers} appareils, {refs} références existantes restaurées","backup.full_done":"Sauvegarde restaurée : {spaces} espaces, {rooms} pièces, {markers} appareils","backup.undo_import":"Annuler la dernière importation complète","backup.import_undone":"Importation complète annulée","backdrop.large_title":"Grande image","backdrop.large_body":"Cette image fait {w}×{h} (fichier de {fileMb} Mo) et nécessite environ {decodedMb} Mo de mémoire pour s'afficher. Sur tablette, cela peut faire planter la page.","backdrop.unknown_body":"Les dimensions de l'image n'ont pas pu être lues — le fichier est peut-être endommagé. Continuer peut faire planter la page sur tablette.","backdrop.use_downscaled":"Téléverser une copie réduite","backdrop.reducing":"Réduction…","backdrop.keep_original":"Conserver l'original","backdrop.too_large_title":"Image trop grande","backdrop.too_large_body":"Cette image dépasse ce que les navigateurs peuvent afficher ({w}×{h}, limite {limit} px par côté). Réduisez-la dans un éditeur de bureau puis téléversez-la à nouveau.","backdrop.downscale_failed":"Impossible de créer la copie réduite. Le plan actuel n'a pas été modifié."};const a="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";export{e as dictionary,a as fingerprint}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/fr-Cml_YfSz.js b/custom_components/houseplan/frontend/houseplan-assets/fr-Cml_YfSz.js new file mode 100644 index 00000000..04753aab --- /dev/null +++ b/custom_components/houseplan/frontend/houseplan-assets/fr-Cml_YfSz.js @@ -0,0 +1 @@ +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";var e={"editor.context_actions":"Actions : {object}","editor.tool_options":"Options de l’outil : {tool}","editor.palette":"Palette : {tool}","editor.open_group":"Groupe d’outils : {group}","editor.group_active":"{group} — actif : {item}","editor.disabled_action":"{action} est indisponible : {reason}","editor.loading":"Chargement de l'éditeur…","editor.loading_aria":"L'éditeur de plan est en cours de chargement","editor.load_failed":"Impossible de charger l'éditeur.","editor.refresh_advice":"Actualisez la page et réessayez.","editor.retry_advice":"Vérifiez votre connexion et appuyez à nouveau.","color_picker.title":"Sélecteur de couleur","color_picker.hue":"Teinte","color_picker.saturation":"Saturation","color_picker.value":"Luminosité","color_picker.hex":"Couleur hexadécimale","color_picker.invalid_hex":"Saisissez une couleur hexadécimale à 3 ou 6 chiffres","btn.properties":"Propriétés","btn.keep_as_walls":"Conserver comme murs","btn.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","btn.delete_room_with_walls":"Supprimer la pièce et les murs","title.markup_select":"Sélectionner et modifier les murs, colonnes et contours enregistrés","title.markup_column":"Colonne : cliquez sur un point de la grille pour placer une colonne carrée","markup.partition":"Cloison","markup.column":"Colonne","markup.hint_column":"cliquez sur un point de la grille pour placer une colonne carrée","history.draft_segment":"Ajouter un segment de brouillon de pièce","history.draft_merge":"Relier des contours de pièce inachevés","history.draft_segment_delete":"Supprimer un segment de brouillon de pièce","history.partition_add":"Ajouter une cloison","history.column_add":"Ajouter une colonne","history.physical_edit":"Modifier l’objet physique","history.physical_delete":"Supprimer l’objet physique","history.physical_move":"Déplacer l’objet physique","history.contour_to_partitions":"Convertir le contour en murs fermés","history.wall_chain_finish":"Terminer la chaîne de murs","history.wall_face_batch":"Créer des pièces à partir des murs","toast.column_duplicate":"Une colonne avec le même centre et la même dimension extérieure existe déjà","confirm.delete_draft":"Supprimer tout le contour de pièce inachevé ?","confirm.delete_draft_segment":"Supprimer ce segment de brouillon ? Le contour restant peut se scinder en deux.","physical.partition_properties":"Propriétés de la cloison","physical.column_properties":"Propriétés de la colonne","physical.draft_properties":"Propriétés du segment de brouillon","physical.shape":"Forme","physical.square":"Carré","physical.circle":"Cercle","physical.diameter":"Diamètre","physical.side":"Côté","physical.rotation":"Angle de rotation","physical.length":"Longueur","physical.allowed_range":"Autorisé : {min}–{max} {unit}","physical.delete_segment":"Supprimer le segment","physical.delete_draft":"Supprimer tout le contour","physical.column_size_title":"Côté extérieur de la colonne carrée placée par le clic (1–150 cm).","card.title":"Plan de la maison","count.devices":"{n} app.","empty.no_spaces":"Aucun espace pour le moment.","empty.add_first":"Ajoutez le premier espace et importez un plan.","empty.install":"Installez l’intégration House Plan et ajoutez-la dans « Appareils et services ».","btn.add_space":"Ajouter un espace","btn.cancel":"Annuler","btn.save":"Enregistrer","btn.close":"Fermer","btn.delete":"Supprimer","btn.edit":"Modifier","btn.open_in_ha":"Ouvrir dans HA","btn.reset":"Réinitialiser","btn.attach":"Joindre…","btn.upload":"Importer…","btn.replace":"Remplacer…","title.zoom_in":"Zoomer","title.zoom_out":"Dézoomer","title.zoom_fit":"Tout ajuster","title.markup":"Traçage des pièces : grille, lignes, contours","title.configure_space":"Configurer l’espace","title.add_space":"Ajouter un espace","title.markup_add":"Murs : tracez une chaîne continue ; Maj verrouille à 45°. Changer d’outil ou quitter l’éditeur termine la chaîne en murs indépendants","title.markup_merge":"Fusionner : cliquez sur une pièce, puis sur la pièce voisine avec laquelle elle partage un mur","title.markup_split":"Diviser une pièce : cliquez sur la pièce, puis sur deux points de ses murs","title.markup_delroom":"Supprimer une pièce : cliquez à l’intérieur de la pièce puis confirmez","title.need_plan":"Importer une image de plan","markup.add":"Murs","markup.merge":"Fusionner","markup.split":"Diviser","markup.resize":"Redimensionner","title.markup_resize":"Redimensionner les pièces : faites glisser une poignée disponible sur un mur horizontal ou vertical","markup.hint_resize":"faites glisser une poignée de mur disponible · le mur s’arrête à la première position non sûre · Échap annule · Ctrl+Z — annuler","resize.disabled.diagonal":"Ce mur est incliné ; le redimensionnement ne prend en charge que les murs horizontaux et verticaux","resize.disabled.side-angle":"Un mur adjacent est incliné ; le redimensionnement exige que les deux murs adjacents rencontrent ce mur à angle droit","resize.disabled.duplicate-physical-wall":"Une cloison distincte, un contour inachevé ou une colonne chevauche ce mur ; supprimez-la ou déplacez-la avant de redimensionner","resize.disabled.partial-shared":"Une pièce voisine n’utilise qu’une partie de ce mur ; il ne peut donc pas être déplacé en toute sécurité comme un mur partagé unique","resize.disabled.unequal-shared":"La pièce voisine utilise des extrémités ou une longueur de mur différentes ; les murs ne peuvent donc pas être déplacés ensemble en toute sécurité","resize.disabled.multiple-rooms":"Déplacer ce mur affecterait plus de deux pièces","resize.disabled.thickness-conflict":"L’épaisseur du mur ne peut pas être conservée en toute sécurité","resize.disabled.opening-conflict":"Une ouverture sur ce mur ne peut pas être déplacée en toute sécurité","resize.disabled.invalid-geometry":"Ce mur ne peut pas être redimensionné sans modifier la structure du plan","resize.limit_stopped":"Mur arrêté : l'étape suivante enfreindrait une limite de jonction","resize.commit_failed":"Le redimensionnement a été annulé car le plan final n’a pas passé le contrôle de géométrie","resize.preview_failed":"Le redimensionnement s’est arrêté à la dernière position sûre car la géométrie du plan ne pouvait pas être préservée","markup.opening":"Ouverture","markup.delete_room":"Supprimer la pièce","history.undo":"Annuler","history.redo":"Rétablir","history.undo_named":"Annuler : {name} (Ctrl+Z)","history.redo_named":"Rétablir : {name} (Ctrl+Maj+Z / Ctrl+Y)","history.undo_empty":"Rien à annuler","history.redo_empty":"Rien à rétablir","history.undone":"Annulé : {name}","history.redone":"Rétabli : {name}","history.add_room":"Créer une pièce","history.split_room":"Diviser la pièce","history.merge_rooms":"Fusionner les pièces","history.resize_room":"Redimensionner la pièce","history.wall_thickness":"Modifier l’épaisseur du mur","history.add_opening":"Ajouter une ouverture de porte ou de fenêtre","history.edit_opening":"Modifier l’ouverture","history.move_opening":"Déplacer l’ouverture","history.delete_opening":"Supprimer l’ouverture","history.delete_room":"Supprimer la pièce","history.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","history.delete_room_with_walls":"Supprimer la pièce et les murs","history.decor_add":"Ajouter un objet décoratif","history.decor_edit":"Modifier l’objet décoratif","history.decor_move":"Déplacer l’objet décoratif","history.decor_transform":"Transformer l’objet décoratif","history.decor_delete":"Supprimer l’objet décoratif","history.backdrop_transform":"Transformer l’arrière-plan du plan","opening.new":"Nouvelle ouverture","opening.edit":"Ouverture","opening.door":"Porte","opening.window":"Fenêtre","opening.gate":"Portail","opening.passage":"Passage ouvert","opening.passage_binding_warning":"L’enregistrement supprimera le capteur d’ouverture/fermeture et la serrure.","opening.invalid_passage_fields":"Le passage ouvert du plan « {room} » possède des paramètres non pris en charge : {fields}.","opening.type_label":"Type","opening.length_label":"Longueur, cm","opening.contact_label":"Capteur d’ouverture/fermeture","opening.lock_label":"Serrure","opening.none":"— aucun —","opening.search_ph":"Rechercher : nom ou ID d’entité…","opening.invert":"Inverser ouvert/fermé","opening.flip_h":"Charnière sur l’autre montant","opening.flip_v":"Ouverture de l’autre côté","opening.open":"Ouvert","opening.closed":"Fermé","opening.locked":"Verrouillé","opening.unlocked":"Déverrouillé","opening.state_unknown":"indisponible","opening.no_entities":"Aucun capteur associé — symbole statique sur le plan.","toast.opening_no_wall":"Cliquez près d’un mur de pièce ou d’un mur indépendant","opening.host_partition":"Mur indépendant","opening.partition_orphan":"Le mur indépendant de cette ouverture n’existe plus","opening.partition_jamb_margin":"Laissez au moins {distance} entre l’ouverture et l’extrémité du mur","opening.rebind_partition":"Associer à un autre mur indépendant","confirm.delete_partition_openings_title":"Supprimer le mur et les ouvertures ?","confirm.delete_partition_openings_body":"Ce mur contient {count} ouverture(s). Elles seront supprimées avec lui.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Supprimer","markup.hint_points":"points : {n} · Maj — pas de 45° · Échap — terminer la chaîne · Ctrl+Z — annuler un point · fermer une zone propose de créer une pièce","markup.hint_start":"cliquez sur un point de la grille pour commencer une chaîne de murs","tip.lqi":"signal Zigbee moyen :","tip.area":"surface : {value}","info.device_header":"Appareil sur le plan","info.model":"Modèle","info.state":"État","info.link":"Lien","info.manuals":"Manuels","info.none":"Aucune information supplémentaire","marker.new_device":"Nouvel appareil","marker.name_label":"Nom (affiché sur le plan)","marker.name_ph":"Nom","marker.binding_label":"Associer à un appareil HA","marker.binding_disabled":"désactivé dans Home Assistant","marker.virtual_option":"Appareil virtuel (sans association)","marker.search_ph":"Rechercher un appareil / groupe…","marker.nothing_found":"aucun résultat","marker.room_label":"Pièce","marker.room_override":" (placement forcé)","marker.room_choose":"— sélectionner une pièce —","marker.room_auto":"— selon la zone de l’appareil (auto) —","marker.icon_label":"Icône","marker.icon_ph":"mdi:… (vide = auto)","marker.display_label":"Affichage","display.badge":"Icône + état","display.icon_ripple":"Icône + état et activité","display.static_icon":"Icône toujours statique","marker.display_hint":"Icône + état modifie le fond pour les états en fonctionnement, ouvert, alarme et indisponible. Icône + état et activité ajoute une brève pulsation pour les événements et une pulsation continue pour une activité, un mouvement ou une présence en cours. Valeur + état remplace l’icône par une valeur HA non ambiguë. Une icône statique ne réagit jamais aux états de l’appareil.","marker.display_hint_badge":"L’icône et le fond dynamique affichent l’état de l’appareil sans la pulsation d’activité habituelle. Les alarmes rouges restent visibles.","marker.display_hint_icon":"L’icône et le fond dynamique affichent l’état de l’appareil sans la pulsation d’activité habituelle. Les alarmes rouges restent visibles.","marker.display_hint_icon_ripple":"L’icône, le fond dynamique et la pulsation affichent une brève animation pour les événements et une animation continue pour le fonctionnement, le mouvement ou la présence. Les alarmes rouges restent prioritaires.","marker.display_hint_value":"Une valeur Home Assistant sélectionnée ou résolue automatiquement remplace l’icône tandis que le fond continue d’indiquer l’état. Les alarmes rouges restent visibles.","marker.display_hint_static_icon":"Le contour adapté au thème et l’icône restent toujours identiques. L’état, l’activité, l’indisponibilité et les alarmes ne modifient pas l’apparence.","marker.static_alarm_warning":"Cet appareil peut signaler des alarmes. L’affichage statique masque l’indication visuelle d’alarme du marqueur.","marker.preview.title":"Aperçu de l’affichage","marker.preview.actual":"Actuel","marker.preview.example":"Exemple","marker.preview.integration":"Fourni par","marker.preview.source":"Source d’affichage","marker.preview.current_state":"État actuel","marker.preview.result":"Sur le plan","marker.preview.details":"Détails de la source","marker.preview.select_source":"Choisissez un appareil ou une entité Home Assistant pour voir son affichage réel.","marker.preview.unknown_provider":"Intégration inconnue","marker.preview.virtual_provider":"House Plan · appareil virtuel","marker.preview.no_source":"Aucune source active","marker.preview.no_state":"Aucun état actuel","marker.preview.mixed_states":"Plusieurs états différents","marker.preview.multiple_sources":"{n} sources","marker.preview.more_sources":"+{n} de plus","marker.preview.scaled":"L’aperçu est redimensionné à {n}% pour tenir dans la zone. La taille enregistrée reste inchangée.","marker.preview.demo_short":"Afficher une activité brève","marker.preview.demo_continuous":"Afficher une activité continue","marker.preview.stop_continuous":"Arrêter l’activité continue","marker.preview.demo_short_notice":"Exemple d’activité brève ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_continuous_notice":"Exemple d’activité continue ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_already_visible":"L’état actuel affiche déjà un véritable effet d’activité ou d’alarme.","marker.preview.reduced_motion":"La réduction des animations est activée sur le système ; l’activité normale est donc affichée sous forme de point.","marker.preview.reason.neutral":"Fond sombre neutre","marker.preview.reason.working":"Fond jaune : l’appareil fonctionne actuellement","marker.preview.reason.working_activity":"Fond jaune et effet d’activité : l’appareil fonctionne actuellement","marker.preview.reason.open":"Fond orange : physiquement ouvert ou déverrouillé","marker.preview.reason.cover_icon_state":"Fond neutre ; l’entité de volet contrôle l’état et l’icône affichés","marker.preview.reason.presence":"Effet d’activité tant qu’une présence est détectée","marker.preview.reason.event":"Effet d’activité bref après un événement détecté","marker.preview.reason.transition":"Effet d’activité pendant que l’appareil se déplace ou change d’état","marker.preview.reason.media_neutral":"Les appareils multimédias restent sur un fond sombre neutre tant qu’ils sont disponibles","marker.preview.reason.unavailable":"Fond neutre atténué : indisponible ou éteint","marker.preview.reason.alarm":"Fond d’alarme rouge ; l’indication d’alarme est toujours affichée","marker.preview.reason.live_states_disabled":"Le style selon les états en direct est désactivé pour cette carte","marker.preview.reason.value_no_state":"Aucune valeur d’état exploitable ; l’icône est affichée à la place","marker.preview.reason.value_ambiguous_sources":"Plusieurs sources de valeur possibles ; l’icône est affichée à la place","marker.preview.reason.value_non_scalar":"La source n’a pas renvoyé de valeur simple ; l’icône est affichée à la place","marker.preview.reason.value_virtual":"Un appareil virtuel n’a aucune valeur Home Assistant ; l’icône est affichée à la place","marker.preview.reason.vacuum_live_plan_only":"La position et la trajectoire en direct de l’aspirateur ne sont disponibles que sur le plan complet","marker.preview.reason.hidden_design_preview":"L’appareil est masqué sur le plan ; cet aperçu de conception reste visible","marker.preview.reason.composite_power_source":"L’état provient de l’entité d’alimentation de l’appareil ; les interrupteurs auxiliaires sont ignorés","marker.preview.reason.activity_display_disabled":"Le mode d’affichage sélectionné n’affiche pas les effets d’activité habituels","marker.preview.reason.ha_disabled":"L’association Home Assistant est désactivée et sera masquée sur le plan","marker.preview.reason.orphaned":"L’association Home Assistant enregistrée est introuvable","marker.preview.reason.static_icon":"Mode statique : l’état de l’appareil ne modifie pas l’icône","marker.activity_color":"Couleur de la pulsation d’activité","marker.ripple_size":"Taille de la pulsation d’activité","marker.activity_alarm_note":"La couleur et la taille n’affectent que l’activité normale et ne modifient pas les alarmes rouges.","marker.pulse_a11y_alarm":"Alarme","marker.pulse_a11y_event":"Événement récent","marker.pulse_a11y_presence":"Présence détectée","marker.pulse_a11y_transition":"Changement d’état","marker.pulse_a11y_running":"En fonctionnement","marker.state_a11y_neutral":"État par défaut","marker.state_a11y_working":"En fonctionnement","marker.state_a11y_open":"Ouvert","marker.state_a11y_locked":"Verrouillé","marker.state_a11y_unlocked":"Déverrouillé","marker.state_a11y_alarm":"Alarme","marker.state_a11y_unavailable":"Indisponible","marker.lqi_a11y_low":"LQI {value}, signal faible","marker.lqi_a11y_mid":"LQI {value}, signal moyen","marker.lqi_a11y_high":"LQI {value}, signal fort","marker.size_label":"Taille / rotation de l’icône","marker.angle_label":"Rotation","marker.model_label":"Modèle","marker.model_ph":"ex. Aqara T&H","marker.link_label":"Lien","marker.desc_label":"Description","marker.desc_ph":"Notes, caractéristiques…","marker.manuals_label":"Manuels (PDF, etc.)","marker.sub_device":"appareil","marker.sub_z2m_group":" · groupe Z2M","marker.sub_group":"groupe","marker.sub_helper":"assistant","space.new":"Nouvel espace","space.header":"Espace","space.title_label":"Titre","space.title_ph":"ex. Garage","space.plan_label":"Plan (arrière-plan)","space.no_plan":"aucune image de plan","space.plan_alt":"plan","room.new":"Nouvelle pièce","room.name_label":"Nom affiché","room.name_ph":"ex. Terrasse","room.area_label":"Zone Home Assistant (non attribuée)","room.no_area_option":"— aucune zone —","room.default_name":"Pièce","device.unnamed":"sans nom","device.light_group":"groupe de lumières","device.fallback":"appareil","device.virtual":"appareil virtuel","confirm.delete_room":"Supprimer la pièce « {name} » ?","confirm.delete_room_title":"Supprimer la pièce « {name} » ?","confirm.delete_room_body":"Choisissez si les murs physiques propres à la pièce doivent être conservés. Les murs partagés avec une autre pièce sont toujours conservés.","confirm.remove_marker":"Supprimer « {name} » du plan ? L’appareil disparaîtra complètement et ne contribuera plus aux données du plan. Vous pourrez l’ajouter de nouveau plus tard.","confirm.erase_decor":"Effacer l’objet {kind} ? Vous pouvez annuler cette action depuis l’historique de l’éditeur.","confirm.delete_space":"Supprimer l’espace « {title} » avec toutes ses pièces et son tracé ?","space.delete_blocked":"Cet espace est encore utilisé par {n} appareil(s). Déplacez-les vers un autre espace ou supprimez-les d’abord.","toast.pos_save_failed":"Échec de l’enregistrement de la position : {err}","toast.no_entity":"L’appareil n’a aucune entité adaptée","toast.ha_disabled_action":"Un objet Home Assistant désactivé ne peut pas être utilisé sur le plan.","toast.ha_disabled_show_device":"Un appareil désactivé dans Home Assistant ne peut pas être affiché sur le plan. Activez-le d’abord dans Home Assistant.","toast.ha_disabled_show_entity":"Une entité désactivée dans Home Assistant ne peut pas être affichée sur le plan. Activez-la d’abord dans Home Assistant.","toast.ha_disabled_add":"Un objet Home Assistant désactivé ne peut pas être ajouté au plan. Activez-le d’abord dans Home Assistant.","toast.ha_binding_unverified":"L’état de l’objet n’a pas pu être vérifié via le registre Home Assistant. L’affichage et les actions sont temporairement indisponibles.","toast.markup_needs_server":"Le tracé est disponible une fois la configuration déplacée sur le serveur","toast.space_order_changed":"L’ordre a changé. Si une carte fixe son étage par numéro, vérifiez ces tableaux de bord.","toast.conflict":"La configuration a été modifiée dans une autre fenêtre — données actualisées, répétez votre dernière action","toast.cfg_save_failed":"Échec de l’enregistrement de la configuration : {err}","toast.room_overlap":"Le contour chevauche la pièce « {name} » — les pièces ne doivent pas se chevaucher","toast.contour_cannot_close":"Le contour ne peut pas être fermé car il est dégénéré ou s’auto-intersecte","toast.merge_not_adjacent":"Seules les pièces partageant un mur peuvent être fusionnées","toast.rooms_merged":"Pièces fusionnées dans « {name} »","toast.split_pick_wall":"Commencez la découpe sur le mur de la pièce","toast.split_bad_cut":"La découpe doit aller d’un mur à l’autre à l’intérieur de la pièce, sans croiser de mur ni se croiser elle-même","merge.header":"Fusionner les pièces","merge.hint":"La pièce fusionnée conserve un nom et une zone. L’autre zone est libérée — ses appareils quittent le plan jusqu’à ce qu’une autre pièce la récupère.","merge.keep":"Conserver","merge.no_area":"aucune zone","toast.room_saved":"Pièce enregistrée ({n}). Appareils ajoutés : {added}. Tracez la suivante ou quittez le mode tracé.","toast.room_saved_no_area":"Pièce enregistrée ({n}, sans zone). Tracez la suivante ou quittez le mode tracé.","toast.marker_needs_server":"La modification des appareils est disponible une fois la configuration déplacée sur le serveur","toast.virtual_name_required":"Saisissez un nom pour l’appareil virtuel","toast.marker_saved":"Appareil enregistré","toast.marker_removed":"Appareil supprimé du plan","toast.integration_missing":"L’intégration House Plan n’est pas installée — gestion indisponible","toast.plan_formats":"Formats pris en charge : SVG, PNG, JPG, WebP","toast.plan_required":"Importez un plan — il est obligatoire","toast.space_added_onboard":"Espace ajouté. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","toast.space_added":"Espace ajouté","toast.space_saved":"Espace enregistré","toast.space_deleted":"Espace supprimé","toast.delete_failed":"Échec de la suppression : {err}","toast.error":"Erreur : {err}","toast.toggle_target_unavailable":"La cible « {name} » est indisponible — aucune action n’a été effectuée","toast.toggle_targets_unavailable":"Cibles indisponibles : {names}. Aucune action n’a été effectuée","toast.file_failed":"Le fichier « {name} » n’a pas été importé : {err}","toast.files_attached":"Fichiers joints : {n}","err.unknown":"erreur inconnue","err.code":"code {code}","err.too_large":"fichier supérieur à {mb} Mo","err.bad_ext":"type non pris en charge (PDF/image attendu)","err.unauthorized":"droits administrateur requis","editor.title":"Titre","editor.default_floor":"Espace initial","editor.default_floor_missing":"L’espace initial « {id} » n’existe plus. Choisissez un autre espace.","editor.floor":"Espace fixe","editor.floor_none":"— non fixé —","editor.floor_index":"Index YAML {index} (conservé)","fixed_floor.loading":"Chargement de l’espace fixe…","fixed_floor.invalid_title":"L’espace fixe est indisponible","fixed_floor.invalid_body":"Vérifiez floor dans la configuration de la carte. Valeur configurée : {value}","editor.icon_size":"Taille des icônes, % de la largeur du plan","editor.show_temperature":"Afficher la température","editor.live_states":"États en direct (on/off, ouvert…)","editor.light_pools":"Halos lumineux et ombres des murs","editor.show_signal":"Afficher le signal Zigbee (LQI)","editor.language":"Langue de l’interface","editor.lang_auto":"Auto (profil HA)","title.icon_rules":"Règles d’icônes : quelle icône MDI attribuer aux appareils selon leur nom","rules.title":"Règles d’icônes","rules.hint":"Les règles sont testées de haut en bas sur « nom de l’appareil + modèle » (expression régulière insensible à la casse) ; la première correspondance gagne. Si rien ne correspond, la classe d’appareil de l’entité est utilisée, puis l’icône générique.","rules.pattern_ph":"regex, ex. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Ajouter une règle","rules.reset":"Réinitialiser par défaut","rules.test_ph":"Tester un nom d’appareil…","rules.invalid":"expression régulière invalide","rules.saved":"Règles d’icônes enregistrées","btn.up":"Monter","btn.down":"Descendre","tap.info":"Fiche de l’appareil","tap.more_info":"Boîte de dialogue Plus d’infos de HA","tap.toggle":"Basculer l’état","marker.tap_label":"Action au toucher pour cet appareil","tap.toggle_note":"L’état déterminé est affiché ci-dessous. Les appareils sécurisés ne sont jamais basculés depuis le plan.","import.title":"Créer des espaces à partir des étages HA","import.hint":"Votre Home Assistant connaît déjà ces étages. Sélectionnez ceux à transformer en espaces du plan — vous importerez ensuite une image de plan pour chacun. Les pièces sont ensuite tracées manuellement sur le plan.","import.start":"Créer {n} espace(s)","import.manual":"Commencer de zéro","import.progress":"Étage {i} sur {n}","import.done":"Espaces créés. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","btn.skip":"Passer","space.scale_label":"Échelle (taille d’une cellule de grille)","space.scale_unit":"cm par cellule","space.scale_unit_imperial":"po par cellule","space.display_section":"Affichage","space.show_borders":"Toujours afficher les contours des pièces","space.zero_wall_style":"Murs d’épaisseur nulle","space.zero_wall_dashed":"Pointillés","space.zero_wall_solid":"Plein","space.zero_wall_help":"Les murs en pointillés laissent passer la lumière. Les murs pleins bloquent la lumière même avec une épaisseur nulle.","space.show_names":"Afficher les noms des pièces (faire glisser pour déplacer)","space.room_color":"Couleur du contour et du nom","space.opacity":"Opacité","space.fill_label":"Remplissage de la pièce","fill.none":"Aucun","fill.lqi":"Signal Zigbee","fill.light":"Lumières","fill.custom":"Couleur personnalisée","space.custom_fill":"Couleur de remplissage","space.source_file":"J’ai une image de plan","space.source_draw":"Aucune image — je tracerai les pièces à la main","fill.temp":"Température","tip.temp_avg":"température moyenne :","tip.hum_avg":"humidité moyenne :","space_card.button":"Ouvrir le plan de l’espace","space_card.not_found":"Espace « {id} » introuvable","space_card.loading":"Chargement…","continuity.restore_plan":"Restauration du plan…","continuity.restore_connection":"Restauration de la connexion aux appareils…","continuity.retry":"Réessayer","editor.space":"Espace","editor.framing":"Cadrage","editor.fit_content":"Tout le contenu visible","editor.fit_house":"Ajusté à la géométrie de la maison","editor.show_button":"Afficher le bouton","editor.button_label":"Libellé du bouton","editor.button_target":"Chemin du tableau de bord cible","marker.sub_entity":"entité","title.general_settings":"Paramètres généraux","gs.title":"Paramètres généraux","gs.hint":"Les couleurs de remplissage s’appliquent à tous les espaces ; chaque couleur possède sa propre opacité. Le mode de remplissage utilisé par un espace se règle dans la boîte de dialogue de cet espace.","gs.light_group":"Remplissage : lumières","gs.light_on":"Lumières allumées","gs.light_off":"Toutes les lumières éteintes","gs.temp_group":"Remplissage : température","gs.temp_cold":"Froid","gs.temp_ok":"Confortable","gs.temp_hot":"Chaud","gs.lqi_group":"Remplissage : signal Zigbee","gs.lqi_low":"Signal faible","gs.lqi_high":"Signal fort","gs.reset":"Réinitialiser par défaut","gs.saved":"Paramètres généraux enregistrés","space.show_lqi":"Afficher le signal Zigbee (LQI) à côté des appareils","space.hide_decor":"Masquer la couche décorative","space.hide_decor_tip":"Les lignes, formes, étiquettes et meubles restent à leur place — visibles dans l’éditeur d’arrière-plan, mais pas sur le plan.","space.hide_openings":"Masquer les ouvertures","space.hide_openings_tip":"Les symboles de portes, fenêtres et portails ne sont pas dessinés, mais les ouvertures continuent de fonctionner : la lumière les traverse, le soleil entre par les fenêtres et les capteurs de contact continuent de les ouvrir. L’éditeur de plan les affiche toujours.","gs.light_none":"Aucune source lumineuse","mode.plan":"Éditeur de plan","mode.devices":"Éditeur d’appareils","display.value":"Valeur + état","marker.subarea":"aucune zone, manuel","device.new":"Nouvel appareil — ouvrez son éditeur pour fermer","opening.unlock_action":"Déverrouiller","opening.lock_action":"Verrouiller","opening.lock_pending":"En cours…","title.close_editor":"Fermer l’éditeur (retour à la vue)","title.add_device":"Ajouter un appareil au plan","devbar.add":"Ajouter","devbar.rules":"Règles d’icônes","device_inbox.button":"Appareils","device_inbox.title":"Appareils sur le plan","device_inbox.search":"Rechercher appareils, entités et intégrations…","device_inbox.add_virtual":"Ajouter un appareil virtuel","device_inbox.tab_on_plan":"Sur le plan","device_inbox.tab_available":"Disponibles","device_inbox.tab_hidden":"Masqués","device_inbox.tab_readd":"De nouveau disponibles","device_inbox.only_new":"Nouveaux uniquement","device_inbox.show_entities":"Afficher les entités","device_inbox.show_hidden":"Afficher les masqués sur le plan","device_inbox.new":"Nouveau","device_inbox.find":"Trouver sur le plan","device_inbox.edit":"Modifier","device_inbox.hide":"Masquer","device_inbox.show":"Afficher","device_inbox.add":"Ajouter","device_inbox.readd":"Ajouter à nouveau","device_inbox.hide_available":"Masquer de la liste","device_inbox.show_more":"Afficher plus","device_inbox.more_actions":"Plus d'actions","device_inbox.find_hidden_hint":"Activez d'abord « Afficher les masqués sur le plan »","device_inbox.show_disabled":"Activez cette liaison dans Home Assistant avant de l'afficher","device_inbox.empty_on_plan":"Aucun appareil correspondant n'est encore sur le plan.","device_inbox.empty_available":"Aucun appareil disponible à ajouter.","device_inbox.empty_hidden":"Il n'y a aucun appareil masqué.","device_inbox.empty_readd":"Aucun appareil supprimé n'est disponible pour un nouvel ajout.","device_inbox.reason_visible_auto":"Découvert automatiquement","device_inbox.reason_visible_explicit":"Ajouté explicitement","device_inbox.reason_manual_hidden":"Masqué par l'utilisateur","device_inbox.reason_automatic_hidden":"Masqué automatiquement","device_inbox.reason_service_entry":"Entrée de registre de service uniquement","device_inbox.reason_excluded_integration":"Intégration exclue par les filtres d'appareils","device_inbox.reason_excluded_domain":"Type d'entité non spatial","device_inbox.reason_grouped_light":"Représenté par un groupe de lumières de la pièce","device_inbox.reason_represented_by_parent":"Représenté par son appareil parent","device_inbox.reason_removed":"Précédemment retiré du plan","device_inbox.reason_available":"Disponible pour ajout au plan","device_inbox.reason_no_bound_room":"La zone HA n'est liée à aucune pièce du plan","device_inbox.status_ha_disabled":"Désactivé dans Home Assistant","device_inbox.status_orphaned":"La liaison n'existe plus dans Home Assistant","device_inbox.status_unverified":"Le registre Home Assistant est temporairement indisponible","device_inbox.saved":"Liste des appareils mise à jour","space.roomcard_section":"La carte de pièce affiche :","space.label_temp":"Température","space.label_hum":"Humidité","space.label_lqi":"Signal Zigbee moyen","space.label_light":"Lumières allumées/éteintes","roomcard.light_on":"Allumé","roomcard.light_off":"Éteint","roomcard.light_partial":"{on} sur {total}","toast.split_pick_inside":"Les points intermédiaires de découpe doivent se trouver à l’intérieur de la pièce","mode.decor":"Éditeur d’arrière-plan","decor.select":"Sélectionner","decor.line":"Ligne","decor.rect":"Rectangle","decor.ellipse":"Ovale","decor.text":"Texte","decor.erase":"Effacer","decor.erase_confirm_title":"Effacer l’objet ?","decor.color":"Couleur","decor.width":"Épaisseur de ligne","decor.line_style":"Style de ligne","decor.line_style_solid":"Plein","decor.line_style_dashed":"Pointillés","decor.fill":"Remplissage","decor.fill_color":"Couleur de remplissage","decor.length":"Longueur","decor.size":"Taille","decor.angle":"Angle de rotation, °","decor.text_size":"Taille du texte","decor.backdrop_properties":"Propriétés de l’arrière-plan du plan","decor.text_title":"Étiquette de texte","decor.object_title":"Modifier {kind}","decor.text_label":"Texte","decor.live_group":"Insérer une variable HA","decor.live_entity":"Entité","decor.live_entity_ph":"choisir une entité","decor.live_attr":"Valeur","decor.live_attr_ph":"choisir l’état ou un attribut","decor.live_state":"État","decor.backdrop":"Image d’arrière-plan","decor.backdrop_hint":"Faites glisser pour déplacer ; tirez un coin pour redimensionner ; utilisez la poignée supérieure pour faire pivoter. Maj modifie les proportions ou libère l’angle.","decor.backdrop_reset":"Réinitialiser l’image","decor.backdrop_reset_done":"L’image a retrouvé sa position et sa taille d’origine","marker.icon_auto":"Auto : {icon} (selon les règles d’icônes ; choisissez-en une pour remplacer)","marker.icon_pin_auto":"Épingle","mode.plan_tip":"Éditeur de plan — la géométrie du logement : dessiner et diviser/fusionner les pièces, les associer aux zones HA, placer portes, fenêtres et portails, déplacer les cartes de pièces, régler l’échelle","mode.devices_tip":"Éditeur d’appareils — tout ce qui concerne les icônes : faire glisser pour positionner, cliquer pour modifier l’association/l’icône/l’affichage, ajouter des appareils virtuels, règles d’icônes","mode.decor_tip":"Éditeur d’arrière-plan — décoration purement visuelle sous le plan : lignes, rectangles, ovales et étiquettes de texte qui ne réagissent jamais aux clics","space.glow_enabled":"Halo des sources lumineuses","gs.glow_group":"Halo des sources lumineuses","gs.glow_base":"Obscurité de la maison","gs.glow_light":"Couleur / intensité de lumière par défaut","gs.wall_group":"Murs","gs.wall_fill":"Remplissage des murs","gs.glow_radius":"Rayon du halo","gs.unit_m":"m","gs.unit_ft":"pi","marker.controls_label":"Contrôle d’autres sources lumineuses","marker.controls_hint":"Avec l’action au toucher « Basculer », un clic inverse l’état de chaque source ajoutée ici. L’entité propre associée au marqueur est contrôlée directement et ne peut pas être ajoutée ; utilisez l’interrupteur ci-dessous pour la classer comme lumière.","marker.controls_filter":"Rechercher des lumières et interrupteurs…","info.controls":"Contrôle","marker.glow_radius_label":"Rayon du halo","marker.glow_radius.help":"Définit le rayon du halo en mètres ou en pieds ; une valeur vide utilise le rayon des paramètres généraux.","marker.glow_radius.help.aria":"Aide : rayon du halo","markup.wallthick":"Épaisseur","markup.select":"Sélectionner","title.markup_wallthick":"Épaisseur — cliquez sur un mur pour définir son épaisseur de 0 à 100 cm.","markup.hint_wallthick":"cliquez sur un mur · Échap ferme sans appliquer","wallthick.field":"Épaisseur","wallthick.unit_cm":"cm","wallthick.unit_in":"po","wallthick.apply_room":"Appliquer à tous les murs de cette pièce","markup.draw_wall_title":"Épaisseur de chaque nouveau segment de mur (0–100 cm). Les murs partagés conservent la valeur existante.","room.queue_progress":"Pièce {current} sur {total}","toast.wall_rooms_saved":"Pièces créées : {n}","toast.wall_chain_saved":"Chaîne de murs enregistrée","toast.wallthick_pick":"Cliquez sur un mur","toast.wallthick_set":"Épaisseur du mur définie","toast.wallthick_cleared":"Épaisseur du mur supprimée","toast.physical_range":"Saisissez une valeur comprise entre {min} et {max} {unit}","toast.zero_wall_opening_conflict":"Supprimez d’abord l’ouverture sur ce segment de mur.","toast.zero_wall_ambiguous":"Le segment de mur est ambigu. Simplifiez ou ajustez la jonction.","toast.zero_wall_migration_blocked":"L’espace n’a pas été converti : {reason}. Aucune donnée n’a été modifiée.","toast.physical_angle":"Saisissez un angle de rotation compris entre 0° inclus et 90° exclu","toast.physical_limit":"L’espace a atteint la limite pour ce type de géométrie","toast.geometry_unsafe":"Modification annulée : la géométrie des murs n’a pas pu être construite en toute sécurité.","junction.limit_angle":"L'angle entre les murs est trop aigu : {actual}°, minimum {limit}°.","junction.limit_valence":"Trop de murs se rejoignent en un nœud : {actual}, maximum {limit}.","junction.limit_length":"Le segment de mur est trop court : {actual} cm, minimum {limit} cm.","junction.limit_distance":"Les murs et les nœuds sont trop proches : {actual} cm, minimum {limit} cm.","junction.limit_clearance":"Il ne reste plus d'intérieur de pièce : {actual} cm², minimum {limit} cm².","junction.limit_check_failed":"La vérification des jonctions n'a pas pu s'exécuter — la modification n'a pas été enregistrée","toast.wall_model_migration_blocked":"Le modèle de murs n’a pas pu être mis à jour : {reason}. Le plan n’a pas été modifié. Lancez « Optimiser les plans » ; si l’erreur se reproduit, corrigez la géométrie de mur conflictuelle.","toast.wall_model_client_outdated":"Mettez à jour la carte et rechargez la page avant de modifier le plan.","wall_model.reason.invalid-room":"contour de pièce invalide","wall_model.reason.zero-length":"segment de mur de longueur nulle","wall_model.reason.third-owner":"mur partagé par plus de deux pièces","wall_model.reason.duplicate-id":"identifiants de mur en conflit","wall_model.reason.thickness-conflict":"valeurs d’épaisseur de mur en conflit","wall_model.reason.opening-host":"l’ouverture ne peut pas être attribuée à un seul mur","toast.delete_room_pick":"Cliquez à l’intérieur de la pièce que vous souhaitez supprimer","toast.plan_snap_ambiguous":"Zoomez pour choisir le nœud du mur","toast.wall_repair_ambiguous":"Le contour de la pièce possède plusieurs connexions possibles. Zoomez et reliez-le explicitement.","toast.wall_repair_too_large":"L’écart dépasse 2 cm. Reliez les murs avant de créer la pièce.","toast.wall_repair_changed":"La géométrie des murs a changé. Essayez de créer de nouveau la pièce.","toast.opening_on_zero_wall":"Les ouvertures ne peuvent pas être placées sur un mur d’épaisseur nulle","marker.from_ha_option":"Choisir dans la liste HA","marker.show_entities":"Afficher les entités","marker.show_entities_tip":"Ajoute à la liste non seulement les appareils, mais aussi toutes leurs entités","marker.pick_ph":"Choisir un appareil…","room.open_area":"Ouvrir la zone HA","view.volumetric":"Vue volumétrique","view.flat":"Vue plane","kiosk.title":"Tailles pour cet écran","kiosk.hint":"Enregistrées uniquement sur cet appareil — chaque tablette murale ou TV peut avoir ses propres tailles confortables.","kiosk.icon_scale":"Taille des icônes d’appareils","kiosk.font_scale":"Taille du texte des cartes de pièces","editor.kiosk":"Mode appareil mural (kiosque)","editor.cycle":"Changer automatiquement d’espace toutes les N secondes (kiosque, 0 = désactivé)","room.settings_title":"Paramètres de la pièce","room.settings_section":"Paramètres de la pièce (remplacent ceux de l’espace)","room.fill_label":"Remplissage de CETTE pièce","fill.inherit":"Comme l’espace","room.custom_fill_space":"Couleur de l’espace","room.custom_fill_own":"Couleur de la pièce","room.temp_src_label":"Source de température","room.hum_src_label":"Source d’humidité","room.src_average":"Moyenne des capteurs de la pièce (par défaut)","room.src_pick":"Un appareil ou une entité HA spécifique","room.src_ph":"Choisir une source…","toast.room_updated":"Pièce mise à jour","space.card_font":"Taille de police des cartes de pièces (tout l’espace)","room.sizes_section":"Tailles de police","room.name_scale":"Taille du nom de la pièce","room.label_scale":"Taille des mesures","preview.room_name":"Salon","toast.cfg_reload_failed":"Impossible de recharger le plan depuis le serveur : {err}","toast.locale_load_failed":"Impossible de charger le pack de langue. L'anglais est utilisé jusqu'au rechargement de la page.","room.settings_short":"Paramètres de la pièce","room.unnamed":"Pièce sans nom","marker.use_climate_temp":"Inclure la température de l’appareil dans la pièce","marker.use_climate_temp_tip":"Ajoute la valeur current_temperature d’un climatiseur ou thermostat à la moyenne de la pièce. Configurez séparément le badge de valeur externe ci-dessous.","marker.value_badge_title":"Badge de valeur","marker.value_badge.help":"Affiche une valeur sélectionnée à côté de l’icône. Cela n’affecte pas les mesures de la pièce, la lumière, le halo ni l’action au toucher.","marker.value_badge.help.aria":"Aide : badge de valeur de l’appareil","marker.value_badge_enabled":"Afficher un badge de valeur","marker.value_badge_source":"Valeur","marker.value_badge_source.help":"Choisissez un état précis, un attribut pris en charge ou une valeur dérivée de cet appareil.","marker.value_badge_source.help.aria":"Aide : source du badge de valeur","marker.value_badge_position":"Position","marker.value_badge_position.help":"Le côté choisi reste fixe et ne bascule pas automatiquement au bord du plan.","marker.value_badge_position.help.aria":"Aide : position du badge de valeur","marker.value_badge_right":"À droite","marker.value_badge_bottom":"En dessous","marker.value_badge_left":"À gauche","marker.value_badge_top":"Au-dessus","marker.value_badge_empty":"Cet appareil n’a aucune valeur disponible","marker.value_badge_static":"Une icône statique n’affiche pas de valeurs en direct. Le paramètre est conservé.","marker.value_badge_missing":"Source indisponible","marker.value_badge_missing_hint":"La source enregistrée est actuellement indisponible. Conservez-la, désactivez le badge ou choisissez une autre valeur.","marker.value_badge_duplicate":"La même valeur est déjà affichée dans l’icône.","marker.value_badge_state":"État · {name}","marker.value_badge_marker_state":"État de la source lumineuse · {name}","marker.value_badge_lqi":"Qualité moyenne du signal Zigbee","marker.value_badge_attr_current_temperature":"Température actuelle · {name}","marker.value_badge_attr_temperature":"Température cible · {name}","marker.value_badge_attr_current_humidity":"Humidité actuelle · {name}","marker.value_badge_attr_humidity":"Humidité · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Vitesse · {name}","marker.value_badge_attr_brightness":"Luminosité · {name}","marker.value_badge_attr_volume_level":"Volume · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Vitesse du ventilateur · {name}","marker.value_source":"Source de la valeur","marker.value_source.help":"Choisissez la valeur qui remplace l’icône. L’action au toucher ne change pas.","marker.value_source.help.aria":"Aide : source de la valeur dans le marqueur","marker.value_source_auto":"Automatique (comme avant)","marker.value_source_missing_hint":"La source enregistrée est indisponible. Le marqueur affiche — jusqu’à son retour ou jusqu’au choix d’une autre valeur.","marker.light_role_label":"Cet appareil est-il une source lumineuse ?","marker.light_role.help":"Détermine si ce marqueur est lui-même une source lumineuse spatiale. Les lumières liées ci-dessus restent des contrôles de lumière de pièce indépendants.","marker.light_role.help.aria":"Aide : cet appareil est-il une source lumineuse","marker.light_role_auto_yes":"Auto (source lumineuse)","marker.light_role_auto_no":"Auto (pas une source lumineuse)","marker.light_role_always":"Toujours une source lumineuse","marker.light_role_never":"Jamais une source lumineuse","marker.light_entity_label":"Entité lumineuse principale","marker.light_entity.help":"Pour un appareil composite, choisissez l’entité dont l’état, la couleur et la luminosité représentent cette source sur le plan. Le choix automatique reste compatible avec les anciens plans.","marker.light_entity.help.aria":"Aide : entité lumineuse principale","marker.light_entity_auto":"Automatique ({entity})","marker.light_entity_none":"aucune entité contrôlable","marker.light_entity_missing":"L’entité enregistrée {entity} est indisponible. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.toggle_entity_label":"Entité à basculer","marker.toggle_entity.help":"Pour un appareil composite, choisissez précisément la lumière ou l’interrupteur propre à actionner par un toucher. Automatique conserve les règles de cible précédentes et reste indépendant de l’entité lumineuse principale.","marker.toggle_entity.help.aria":"Aide : entité à basculer","marker.toggle_entity_auto":"Automatique ({entity})","marker.toggle_entity_none":"aucune entité propre contrôlable","marker.toggle_entity_missing":"L’entité enregistrée {entity} ne fait plus partie des canaux sélectionnables de ce marqueur. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.glow_color_label":"Couleur et luminosité du halo","marker.glow_mode.help":"Utilisez les valeurs en direct de la source, remplacez uniquement sa couleur ou fixez à la fois la couleur et la luminosité. Le minimum est 1 % ; pour désactiver la source, choisissez « Jamais ».","marker.glow_mode.help.aria":"Aide : couleur et luminosité du halo","marker.glow_mode_auto":"Depuis la source","marker.glow_mode_color":"Définir la couleur","marker.glow_mode_fixed":"Définir la couleur et la luminosité","marker.glow_color":"Couleur du halo","marker.glow_brightness":"Luminosité","marker.glow_disabled_never":"Les paramètres du halo sont indisponibles car ce marqueur est explicitement défini comme n’étant pas une source lumineuse.","marker.glow_disabled_auto":"Le mode Auto n’a trouvé aucune source lumineuse spatiale pour ce marqueur.","marker.glow_disabled_no_entity":"Aucune entité Home Assistant contrôlable et active n’est disponible pour cette source spatiale.","marker.glow_passive_hint":"Cette source ne possède aucune donnée Home Assistant propre. Définissez manuellement sa couleur et sa luminosité ; son rayon reste disponible.","marker.control_broken":"La source enregistrée est absente ou n’est plus marquée comme lumière","marker.control_missing_label":"Lumière du plan manquante","marker.control_passive":"source passive","toast.marker_control_cycle":"Ce lien créerait une chaîne circulaire de contrôles de lumière.","toast.marker_binding_required":"Choisissez un appareil Home Assistant avant de lier une autre source lumineuse.","confirm.unlock":"Déverrouiller « {name} » ?","toast.files_migrate_failed":"Les pièces jointes n’ont pas pu être déplacées vers la nouvelle association ; les liens continuent de pointer vers les anciens fichiers : {err}","space.pick_saved":"Déjà importé","space.pick_saved_hint":"Plans stockés sur le serveur, y compris ceux que vous avez précédemment dissociés","space.no_saved":"Aucun plan n’est encore stocké sur le serveur.","space.loading":"Chargement…","space.used_by":"utilisé par : {list}","space.in_use":"Un espace utilise encore ce plan — dissociez-le d’abord","btn.use":"Utiliser","confirm.delete_plan":"Supprimer le fichier de plan « {name} » du serveur ? Cette action est irréversible.","toast.plans_list_failed":"Impossible de lister les plans stockés : {err}","toast.plan_delete_failed":"Impossible de supprimer le plan : {err}","marker.hide":"Masquer","marker.hide_tip":"L’appareil disparaîtra du plan après l’enregistrement mais continuera de compter dans le signal de la pièce. Restaurez-le via « Masqués et désactivés » dans l’éditeur d’appareils.","marker.show":"Afficher","marker.show_tip":"L’appareil apparaîtra de nouveau sur le plan après l’enregistrement.","marker.hidden_ghost":"Appareil masqué par l’utilisateur","marker.ha_disabled_device":"L’appareil est désactivé dans Home Assistant et masqué du plan.","marker.ha_disabled_entity":"L’entité est désactivée dans Home Assistant et masquée du plan.","marker.ha_disabled_all_entities":"L’appareil n’a aucune entité Home Assistant active ; il est donc masqué du plan.","marker.ha_registry_limited":"Le registre Home Assistant complet n’est pas accessible à cet utilisateur. L’objet non vérifié ne peut pas être affiché ni utilisé pour le moment.","marker.delete_tip":"Supprimer complètement l’appareil du plan et de tous les agrégats. Vous pourrez l’ajouter de nouveau plus tard.","tap.run":"Exécuter une automatisation/un script/une scène","marker.run_target_label":"Élément à exécuter","marker.run_search_ph":"Rechercher : automatisation, script ou scène…","marker.run_target_gone":"Cible {id} introuvable — choisissez-en une autre","marker.tap_confirm":"Demander une confirmation","marker.tap_confirm_tip":"Afficher une boîte de dialogue de confirmation avant l’action — protection contre les appuis accidentels.","marker.toggle_hint_single":"Cible : {name} ({id}).","marker.virtual_light_target":"Lumière virtuelle : {name}.","marker.virtual_light_current":"État manuel : {state} → {effect}.","marker.virtual_light_state_on":"allumé","marker.virtual_light_state_off":"éteint","marker.toggle_hint_group":"Basculera {count} source(s) : {names}.","marker.toggle_hint_current":"Actuellement : {state} → {effect}.","marker.toggle_hint_group_current":"Actuellement allumées : {on} sur {count} → {effect}.","marker.toggle_hint_skipped":"Seront ignorées ({count}) : {targets}.","marker.toggle_effect_turn_on":"sera allumé","marker.toggle_effect_turn_off":"sera éteint","marker.toggle_effect_open":"sera ouvert","marker.toggle_effect_close":"sera fermé","marker.toggle_effect_stop":"sera arrêté","marker.toggle_effect_toggle":"l’état sera basculé","marker.toggle_skip_missing":"introuvable","marker.toggle_skip_ha_disabled":"désactivé dans HA","marker.toggle_skip_unavailable":"indisponible","marker.toggle_skip_unsupported":"basculement non pris en charge","marker.toggle_skip_secure":"bloqué pour des raisons de sécurité","marker.toggle_none_no_binding":"Cet appareil n’a aucune association ; il n’y a donc aucun état à basculer. Un toucher ne fera rien.","marker.toggle_none_no_actionable_entity":"Cet appareil n’a aucun état pouvant être basculé. Un toucher ne fera rien.","marker.toggle_none_configured_targets_missing":"Les cibles configurées sont indisponibles. L’entité propre de l’appareil ne leur sera pas substituée.","marker.toggle_none_ha_disabled":"La cible est désactivée dans Home Assistant et ne peut pas être utilisée sur le plan.","marker.toggle_none_unavailable":"La cible est actuellement indisponible. Un toucher ne fera rien.","marker.toggle_none_unsupported":"Home Assistant n’expose aucune opération de basculement sûre pour cette entité. Un toucher ne fera rien.","marker.toggle_none_secure":"Le basculement des serrures, alarmes et portails sécurisés depuis le plan est bloqué pour des raisons de sécurité.","run.automation":"automatisation","run.script":"script","run.scene":"scène","confirm.tap_run":"Exécuter « {name} » ?","confirm.tap_toggle":"Basculer « {name} » ?","confirm.current_state":"État actuel : {state}","confirm.expected_state":"Après basculement : {state}","confirm.group_current":"{on} allumé(s) sur {total}","confirm.group_all_on":"tout est allumé","confirm.group_all_off":"tout est éteint","confirm.unavailable_targets":"Indisponibles : {count}","confirm.expected_by_ha":"Home Assistant déterminera l’état","confirm.state_on":"Allumé","confirm.state_off":"Éteint","confirm.state_open":"Ouvert","confirm.state_closed":"Fermé","confirm.state_opening":"Ouverture en cours","confirm.state_closing":"Fermeture en cours","confirm.state_stopped":"Arrêté","confirm.state_unknown":"Inconnu","toast.run_started":"Démarré : {name}","toast.run_target_missing":"Cible d’exécution introuvable — vérifiez les paramètres de l’appareil","toast.run_target_required":"Choisissez une automatisation, un script ou une scène","toast.tap_target_changed":"La cible de l’action a changé. Réessayez.","toast.virtual_light_toggle_failed":"Impossible de basculer la lumière virtuelle : {err}","toast.value_badge_source_required":"Choisissez une valeur pour le badge","btn.run":"Exécuter","vac.section":"Robot aspirateur : position en direct","vac.autocal":"Configurer automatiquement","vac.live":"Position en direct sur le plan","vac.trail":"Afficher le trajet du robot","vac.cal_maps":"Cartes calibrées : {maps}","vac.autocal_no_rooms":"L’intégration ne fournit aucune liste de pièces — ouvrez « Ajuster manuellement »","vac.autocal_no_match":"Les noms de pièces ne correspondent pas (il en faut ≥3 en commun) — ouvrez « Ajuster manuellement »","vac.autocal_done":"Terminé : association via {rooms} pièces. Lancez un nettoyage et vérifiez","vac.cal_need_pos":"Le robot ne transmet pas de coordonnées — lancez un nettoyage puis mettez-le en pause","vac.cal_done":"Calibration enregistrée. Lancez un nettoyage et vérifiez","vac.cal_cancelled":"Calibration annulée","vac.fit":"Ajuster manuellement","vac.fit_hint":"Faites glisser la carte du robot pour la positionner, étirez-la par les coins","vac.fit_rotate":"Rotation de 90°","vac.fit_mirror":"Miroir","vac.trail_never":"Jamais","vac.trail_cleaning":"Pendant le nettoyage","vac.trail_always":"Toujours","gs.bg_group":"Arrière-plan de la scène","gs.bg_color":"Arrière-plan autour du plan","gs.bg_default":"Valeur par défaut du thème","gs.bg_theme":"valeur par défaut du thème","gs.bg_mode":"Arrière-plan du plan","gs.bg_static":"Couleur statique","gs.bg_daynight":"Suit le soleil","gs.bg_daynight_hint":"L’arrière-plan évolue entre l’aube, le jour, le crépuscule et la nuit en utilisant sun.sun, ou votre heure locale si les données du soleil sont indisponibles. Aucune orientation du nord n’est nécessaire pour l’arrière-plan.","gs.sun_group":"Soleil","gs.sun_missing":"L’entité sun.sun est introuvable — l’arrière-plan suit votre heure locale ; les rayons de soleil par les fenêtres sont indisponibles.","gs.north":"Nord sur le plan","gs.north_ph":"non défini","gs.north_hint":"Définissez le nord uniquement pour les rayons par les fenêtres (pas de 1°, 15° avec Maj) ; l’arrière-plan Soleil fonctionne sans lui.","gs.north_clear":"Effacer","gs.north_letter":"N","gs.sun_rays":"Lumière du soleil à travers les fenêtres","gs.about_group":"À propos","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · documentation et problèmes","gs.about_telegram":"Discussion Telegram","space.bg_color":"Arrière-plan autour du plan","space.bg_inherit":"Hériter des paramètres généraux","space.bg_inherited":"hérite des paramètres généraux","space.bg_mode":"Arrière-plan du plan","space.north":"Nord sur le plan (remplacement)","space.north_inherited":"hérité : {v}","space.sun_rays":"Lumière du soleil à travers les fenêtres","space.sun_inherit":"Hériter des paramètres généraux","space.sun_on":"Activé","space.sun_off":"Désactivé","canvas.far_objects":"{n} objet(s) éloigné(s) du plan","canvas.show_far":"Afficher","canvas.home_tip":"Le plan est là-bas — cliquez pour l’ajuster à l’écran","gs.grid_group":"Maintenance du plan","gs.grid_hint":"Met à jour les modèles de données, aligne les éléments du plan sur la grille et fusionne les fragments de murs redondants. Un rapport précis est affiché avant tout enregistrement.","gs.align_all":"Optimiser les plans","gs.align_title":"Optimiser les plans","gs.align_none":"Tous les plans utilisent déjà le modèle de données optimisé actuel.","gs.optimize_no_automatic_changes":"Aucune modification automatique n’est à appliquer. Vérifiez les éléments ci-dessous.","gs.align_count":"{n} élément(s) sur {total} seront déplacés, de {cm} cm au maximum.","gs.align_where":"Le plus grand déplacement se trouve dans « {s} ».","gs.align_turned":"Ouvertures dont l’angle est corrigé : {n}.","gs.align_removed_drafts":"Contours invalides réduits par la grille et supprimés : {n}.","gs.optimize_redundant_drafts":"Chaînes de murs enregistrées masquées par des murs de pièces pleins et supprimées : {n}.","gs.align_preflight_failed":"Impossible de vérifier en toute sécurité la géométrie des espaces suivants : {spaces}{more}.","gs.align_preflight_hint":"Les plans n’ont pas été modifiés. Copiez les diagnostics avec le bouton ci-dessous et joignez-les au rapport de bug avec un export de l’espace.","gs.preflight_reason_prepare-exception":"Impossible de préparer la géométrie de l’espace (exception lors de la construction du modèle)","gs.preflight_reason_wall-null":"Le corps du mur n’a pas pu être construit (l’union a renvoyé un résultat vide)","gs.preflight_reason_wall-degraded-extra":"Le corps du mur s’est dégradé avec une géométrie supplémentaire","gs.preflight_reason_wall-failed-core":"Le noyau du corps du mur n’a pas pu être assemblé","gs.preflight_reason_wall-exception":"La construction du mur a déclenché une exception","gs.preflight_reason_floor-null":"Le contour du sol n’a pas pu être construit","gs.preflight_reason_floor-exception":"La construction du sol a déclenché une exception","gs.preflight_copy":"Copier les diagnostics","gs.preflight_copied":"Diagnostics copiés","gs.preflight_update_hint":"Les versions de la carte et de l’intégration diffèrent — mettez House Plan à jour puis réessayez.","gs.align_preflight_space":"Espace {n}","gs.align_preflight_more":", et {n} de plus","gs.optimize_changes":"Migrations de modèle : {m} ; espaces mis à jour : {c} ; valeurs de coordonnées parasites supprimées : {p} ; fragments de murs réels fusionnés : {w} ; fragments de murs d’épaisseur nulle fusionnés : {s} ; murs indépendants : {i}.","gs.zero_walls_migrated":"Portions de murs virtuels converties : {n}.","gs.wall_segments_migrated":"Segments de murs stabilisés : {n}.","gs.optimize_lattice_summary":"Valeurs de coordonnées parasites normalisées : {n} ; déplacement maximal : {cm} cm.","gs.optimize_lattice_space":"{space} : valeurs de coordonnées normalisées : {n} ; valeurs hors grille laissées inchangées : {far}.","gs.optimize_coincident_partitions":"Sections de murs indépendants masquées absorbées dans les murs des pièces : {n}.","gs.optimize_openings_rehosted":"Ouvertures rattachées aux murs des pièces : {n}.","gs.optimize_walls_straightened":"Murs redressés : {n} ; déplacement maximal : {cm} cm.","gs.optimize_walls_straightened_where":"Plus grande correction de mur : {s}.","gs.optimize_walls_straighten_skipped":"Murs proches d’un axe laissés inchangés car ils ne pouvaient pas être réparés en toute sécurité : {n}.","gs.optimize_glow_migration":"Ancien mode Glow : {spaces} espaces → aucun remplissage de données + Glow indépendant ; {rooms} pièces → remplissage de données hérité + Glow indépendant.","gs.optimize_references":"Références réparées : espaces — {spaces} ; pièces — {rooms} ; positions — {positions} ; appareils dissociés des espaces manquants — {detached}.","gs.optimize_reference_more":", et {n} de plus","gs.optimize_orphans_removed":"Enregistrements oubliés supprimés : {total} — étiquettes de pièces : {rooms} ; appareils : {devices} ; marqueurs de groupe : {groups}. Ils appartenaient à des espaces supprimés précédemment.","gs.optimize_live_positions":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles seront conservées.","gs.optimize_live_positions_remove":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles sont sélectionnées pour suppression.","gs.optimize_live_names":" : {names}{more}","gs.optimize_live_remove":"Supprimer les anciennes positions","gs.optimize_live_keep":"Conserver les anciennes positions","gs.optimize_live_selected":"Les anciennes positions seront supprimées après l’application de l’optimisation.","gs.optimize_unverified":"Impossible de vérifier en toute sécurité les positions : {n}. Elles ont été laissées inchangées.","gs.optimize_registry_limited":"Un accès administrateur complet aux registres Home Assistant est requis pour une vérification sûre.","gs.optimize_vacuum_warning":"Associations de pièces d’aspirateur nécessitant encore une vérification : {n}.","gs.optimize_details":"Détails","gs.optimize_details_more":"Et {n} enregistrement(s) de plus.","gs.optimize_detail_removed":"sera supprimé","gs.optimize_detail_live":"sera conservé","gs.optimize_detail_unverified":"non vérifié","gs.optimize_detail_room_label":"étiquette de pièce","gs.optimize_detail_device":"appareil","gs.optimize_detail_group":"marqueur de groupe","gs.optimize_detail_unknown":"propriétaire inconnu","gs.optimize_detail_item":"{status} : {kind} {id} ; ancien espace {space}","gs.align_warn":"Les éléments volontairement placés entre les nœuds de la grille seront déplacés. Une seule annulation est disponible après l’opération, uniquement jusqu’à la prochaine modification du plan.","gs.align_run":"Optimiser","gs.align_done":"Plans optimisés : {n} élément(s) déplacé(s), {m} enregistrement(s) maintenu(s), {r} référence(s) réparée(s)","gs.optimize_undo":"Annuler la dernière optimisation","gs.optimize_undone":"La dernière optimisation a été annulée","decor.furniture":"Mobilier","furn.title":"Bibliothèque de mobilier","furn.symbol":"Symbole","furn.group_furniture":"Mobilier","furn.group_appliance":"Électroménager","furn.group_sanitary":"Sanitaires","furn.group_other":"Autre","furn.width":"Largeur","furn.depth":"Profondeur","furn.back_to_categories":"Toutes les catégories","furn.pick_hint":"Choisissez un symbole, puis cliquez sur le plan.","furn.place_hint":"Cliquez sur le plan — le meuble se place contre le mur le plus proche. Maj permet un placement libre.","furn.cat_air_conditioner":"Climatiseurs","furn.cat_armchair":"Fauteuils","furn.cat_bathtub":"Baignoires","furn.cat_bed":"Lits","furn.cat_bidet":"Bidets","furn.cat_boiler":"Chauffe-eau","furn.cat_chair":"Chaises","furn.cat_coffee_table":"Tables basses","furn.cat_cooktop":"Plaques de cuisson","furn.cat_dining_table":"Tables à manger","furn.cat_dishwasher":"Lave-vaisselle","furn.cat_dryer":"Sèche-linge","furn.cat_fireplace":"Cheminées","furn.cat_fridge":"Réfrigérateurs","furn.cat_kitchen_cabinet":"Meubles de cuisine","furn.cat_kitchen_sink":"Éviers de cuisine","furn.cat_nightstand":"Meubles","furn.cat_plant":"Plantes","furn.cat_rug":"Tapis","furn.cat_shelving":"Étagères","furn.cat_shower":"Douches","furn.cat_sink":"Lavabos","furn.cat_sofa":"Canapés","furn.cat_stairs":"Escaliers","furn.cat_toilet":"Toilettes","furn.cat_tv":"Téléviseurs","furn.cat_wardrobe":"Armoires","furn.cat_washer":"Lave-linge","furn.cat_work_table":"Bureaux","furn.sym_sofa":"Canapé","furn.sym_sofa_three_seat":"Canapé trois places","furn.sym_sofa_corner_right":"Canapé d'angle droit","furn.sym_armchair":"Fauteuil","furn.sym_armchair_office":"Chaise de bureau","furn.sym_coffee_table":"Table basse","furn.sym_coffee_table_round":"Table basse ronde","furn.sym_coffee_table_oval":"Table basse ovale","furn.sym_coffee_table_rounded":"Table basse arrondie","furn.sym_table_dining":"Table à manger","furn.sym_table_round":"Table ronde","furn.sym_table_dining_oval":"Table à manger ovale","furn.sym_table_dining_rounded":"Table à manger arrondie","furn.sym_chair":"Chaise","furn.sym_chair_bar":"Tabouret de bar","furn.sym_desk":"Bureau","furn.sym_desk_corner":"Bureau d'angle","furn.sym_bed_double":"Lit double","furn.sym_bed_single":"Lit simple","furn.sym_nightstand":"Table de chevet","furn.sym_cabinet_tv":"Meuble TV","furn.sym_cabinet_shoe":"Meuble à chaussures","furn.sym_cabinet_sink":"Meuble sous lavabo","furn.sym_wardrobe":"Armoire","furn.sym_bookshelf":"Bibliothèque","furn.sym_wall_unit":"Élément mural","furn.sym_kitchen_floor":"Module de cuisine bas","furn.sym_kitchen_floor_corner":"Module de cuisine bas d'angle","furn.sym_kitchen_wall":"Module de cuisine haut","furn.sym_kitchen_wall_corner":"Module de cuisine haut d'angle","furn.sym_shelf_floor":"Étagère au sol","furn.sym_shelf_wall":"Étagère murale","furn.sym_fridge":"Réfrigérateur","furn.sym_stove":"Cuisinière","furn.sym_cooktop_two":"Plaque à deux feux","furn.sym_dishwasher":"Lave-vaisselle","furn.sym_washer":"Lave-linge","furn.sym_dryer":"Sèche-linge","furn.sym_tv":"Téléviseur","furn.sym_tv_wall":"Téléviseur mural","furn.sym_ac":"Climatiseur","furn.sym_water_heater":"Chauffe-eau","furn.sym_toilet":"Toilettes","furn.sym_toilet_built_in":"Toilettes suspendues","furn.sym_bathtub":"Baignoire","furn.sym_bathtub_corner":"Baignoire d'angle","furn.sym_shower":"Douche","furn.sym_sink":"Lavabo","furn.sym_kitchen_sink":"Évier de cuisine","furn.sym_kitchen_sink_double":"Évier de cuisine double","furn.sym_bidet":"Bidet","furn.sym_bidet_built_in":"Bidet suspendu","furn.sym_stairs":"Escalier","furn.sym_fireplace":"Cheminée","furn.sym_plant":"Plante","furn.sym_rug":"Tapis","common.yes":"Oui","common.no":"Non","vac.diag_source":"Source","vac.diag_platform":"Intégration","vac.diag_status":"État","vac.diag_position":"Position","vac.diag_rooms":"Pièces","vac.diag_rooms_value":"{total} · {matched} noms correspondent · {readiness}","vac.autocal_ready":"calibration automatique disponible","vac.autocal_not_ready":"3 noms correspondants requis","vac.diag_path":"Chemin de l’intégration","vac.diag_map":"ID de carte","vac.source_none":"non sélectionné","vac.source_status_ok":"Prêt","vac.source_status_missing":"Manquant","vac.source_status_disabled":"Désactivé dans Home Assistant","vac.source_status_unavailable":"Indisponible","vac.source_status_unverified":"Impossible à vérifier avec les autorisations actuelles","vac.source_status_unsupported":"Aucune donnée de position","vac.source_status_none":"Aucune source","vac.source_banner_missing":"La source enregistrée n’existe plus. Elle n’a pas été remplacée automatiquement ; choisissez une autre source ou restaurez-la dans Home Assistant.","vac.source_banner_disabled":"La source enregistrée est désactivée dans Home Assistant. Activez-la ou choisissez une autre source.","vac.source_banner_unverified":"Les autorisations Home Assistant actuelles ne permettent pas de vérifier cette source enregistrée. Elle reste épinglée et ne sera pas remplacée automatiquement.","vac.choose_source":"Choisir la source","vac.source_auto":"Automatique","vac.source_auto_hint":"Utiliser une entité compatible de cet appareil","vac.all_cameras":"Toutes les caméras","vac.all_cameras_warn":"Une caméra peut ne pas fournir de données du robot. Choisissez-en une uniquement s’il s’agit de la carte de votre aspirateur.","vac.all_cameras_empty":"Aucune autre entité caméra trouvée.","vac.platform_unknown":"intégration inconnue","vac.cap_position":"position","vac.cap_rooms_short":"pièces","vac.cap_path":"trajet","vac.cap_map":"ID de carte","vac.cap_none":"aucune donnée de robot détectée","vac.xcme_hint":"Activez ces attributs Xiaomi Cloud Map Extractor :","vac.documentation":"Documentation","vac.residual_title":"Vérifier la calibration automatique","vac.residual_message":"Les pièces correspondantes présentent un écart maximal de {error}. Appliquez cette calibration approximative, affinez-la manuellement ou annulez sans modifier la configuration enregistrée.","vac.apply_proposal":"Appliquer","gs.backup_group":"Sauvegarde et transfert","gs.backup_hint":"Téléchargez une sauvegarde JSON portable ou prévisualisez et importez une sauvegarde créée par House Plan.","backup.export_open":"Exporter","backup.import_open":"Importer","backup.export_title":"Exporter House Plan","backup.import_title":"Importer House Plan","backup.export_hint":"Choisissez si la sauvegarde doit contenir toute la configuration House Plan ou uniquement l’espace actuel.","backup.full":"Sauvegarde complète","backup.current_space":"Espace actuel","backup.current_space_title":"Espace actuel : {title}","backup.no_current_space":"Aucun espace actuel","backup.plan_only":"Plan uniquement","backup.plan_only_hint":"Conserver les pièces, murs, ouvertures, décorations et positions des étiquettes de pièces sans les appareils ni les associations Home Assistant.","backup.plan_only_preview":"Ce fichier contient uniquement le plan","backup.privacy_warning":"L’archive conserve les noms, identifiants Home Assistant et coordonnées exactes. Les plans internes et pièces jointes sont référencés, pas intégrés ; les états d’exécution et trajets d’aspirateur ne sont pas inclus.","backup.download":"Télécharger le JSON","backup.export_done":"Sauvegarde téléchargée","backup.reading":"Vérification de la sauvegarde…","backup.revalidated":"Le plan a changé depuis cet aperçu. Le résumé a été actualisé ; vérifiez-le et confirmez de nouveau.","backup.error.unauthorized":"Vous n’avez pas l’autorisation d’exporter ou d’importer ce plan.","backup.error.not_ready":"House Plan n’est pas encore prêt. Réessayez dans un instant.","backup.error.too_large":"La sauvegarde dépasse la limite de 8 Mio.","backup.error.invalid_json":"Le fichier sélectionné n’est pas un JSON valide.","backup.error.invalid_format":"Le fichier sélectionné n’est pas une sauvegarde House Plan.","backup.error.unsupported_export_version":"Ce format de sauvegarde n’est pas pris en charge par la version installée.","backup.error.future_model":"La sauvegarde a été créée avec un modèle de données House Plan plus récent.","backup.error.invalid_config":"La sauvegarde contient une configuration House Plan invalide.","backup.error.wall_model_migration_blocked":"Le modèle de murs de la sauvegarde n’a pas pu être mis à niveau en toute sécurité. Optimisez d’abord le plan source, puis exportez-le de nouveau.","backup.error.invalid_layout":"La sauvegarde contient des positions d’objets invalides.","backup.error.invalid_content":"La sauvegarde contient des références de contenu invalides ou incohérentes.","backup.error.space_not_found":"L’espace sélectionné n’existe plus.","backup.error.capacity_exceeded":"L’ajout de cette sauvegarde dépasserait les limites du plan.","backup.error.preview_expired":"L’aperçu a expiré. Sélectionnez de nouveau le fichier de sauvegarde.","backup.error.preview_owner_mismatch":"Cet aperçu appartient à un autre utilisateur Home Assistant.","backup.error.conflict":"Le plan a changé depuis l’aperçu. Vérifiez le résumé actualisé.","backup.error.content_confirmation_required":"Confirmez que le contenu local indisponible peut être dissocié.","backup.error.commit_failed":"La sauvegarde n’a pas pu être appliquée en toute sécurité. Le plan précédent a été restauré ou est en attente de récupération.","backup.error.missing_plan":"Un fichier de plan référencé a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.missing_content":"Une pièce jointe locale référencée a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.marker_control_missing":"Une lumière liée au plan est absente de la sauvegarde.","backup.error.marker_control_not_light":"Une cible liée au plan n’est plus marquée comme source lumineuse.","backup.error.marker_control_self":"Une source lumineuse ne peut pas se contrôler elle-même.","backup.error.marker_control_cycle":"La sauvegarde contient une chaîne circulaire de contrôles de lumière.","backup.error.duplicate_marker_control":"La sauvegarde contient un lien de lumière du plan en doublon.","backup.error.no_backup":"Aucun instantané d’importation ou d’optimisation n’est disponible à restaurer.","backup.same_source":"Créée sur cette instance Home Assistant","backup.foreign_source":"Créée sur une autre instance Home Assistant","backup.created":"Créée : {value}","backup.versions":"Carte {card} ; intégration {integration} ; modèle de données {model}","backup.count_spaces":"Espaces : {n}","backup.count_rooms":"Pièces : {n}","backup.count_walls":"Murs : {n}","backup.count_openings":"Ouvertures : {n}","backup.count_decor":"Objets décoratifs : {n}","backup.count_markers":"Appareils : {n}","backup.count_layout":"Positions : {n}","backup.bindings":"Associations — appareils : {device}, entités : {entity}, virtuels : {virtual} ; positions sans espace : {legacy}","backup.binding_status":"État des cibles — actives : {active}, désactivées : {disabled}, manquantes : {missing}","backup.missing_areas":"Zones absentes sur la cible : {areas}","backup.dropped_marker_links":"Liens de lumières du plan hors de cet espace transféré omis : {n}.","backup.repaired_target_refs":"Références existantes restaurées par cet import d’espace : {n}.","backup.preserved_unresolved_refs":"Références qui n’ont pas pu être restaurées sans ambiguïté conservées : {n}.","backup.preserved_unresolved_hint":"Aucune donnée n’a été devinée ni supprimée. Après l’importation, lancez « Optimiser les plans » pour examiner les références restantes.","backup.import_details":"Détails des références d’importation","backup.import_detail.incoming_remapped":"Références mises à jour dans la copie importée : {n}","backup.import_detail.target_repaired":"Références existantes restaurées : {n}","backup.import_detail.preserved_unresolved":"Références non résolues conservées : {n}","backup.import_detail.collisions":"Conflits de destination conservés en toute sécurité : {n}","backup.import_detail.dropped_links":"Liens entrants omis selon les règles de transfert : {n}","backup.import_detail.bounded_lineages":"Identifiants trop imbriqués maintenus dans les limites : {n}","backup.replace_warning":"Cela remplace la configuration et la disposition actuelles. Les fichiers importés ne sont jamais supprimés. Une seule annulation reste disponible jusqu’à la prochaine modification du plan.","backup.foreign_bookkeeping":"Les données de suivi des appareils connus/nouveaux propres à l’instance ne seront pas importées.","backup.final_name":"Nom du nouvel espace","backup.target_settings":"Les paramètres généraux de l’instance cible restent inchangés.","backup.duplicates":"Associations déjà présentes : {n}","backup.skip":"Ignorer les appareils en doublon","backup.virtual_copy":"Ajouter des copies virtuelles statiques sûres","backup.content":"Contenu référencé","backup.content_available":"disponible localement","backup.content_external":"lien externe","backup.content_detach_required":"sera dissocié","backup.confirm_detach":"Je comprends que les plans internes et pièces jointes indisponibles seront dissociés de la configuration importée.","backup.replace":"Remplacer","backup.add":"Ajouter un espace","backup.space_done":"Espace importé : {rooms} pièces, {markers} appareils, {refs} références existantes restaurées","backup.full_done":"Sauvegarde restaurée : {spaces} espaces, {rooms} pièces, {markers} appareils","backup.undo_import":"Annuler la dernière importation complète","backup.import_undone":"Importation complète annulée","backdrop.large_title":"Grande image","backdrop.large_body":"Cette image fait {w}×{h} (fichier de {fileMb} Mo) et nécessite environ {decodedMb} Mo de mémoire pour s'afficher. Sur tablette, cela peut faire planter la page.","backdrop.unknown_body":"Les dimensions de l'image n'ont pas pu être lues — le fichier est peut-être endommagé. Continuer peut faire planter la page sur tablette.","backdrop.use_downscaled":"Téléverser une copie réduite","backdrop.reducing":"Réduction…","backdrop.keep_original":"Conserver l'original","backdrop.too_large_title":"Image trop grande","backdrop.too_large_body":"Cette image dépasse ce que les navigateurs peuvent afficher ({w}×{h}, limite {limit} px par côté). Réduisez-la dans un éditeur de bureau puis téléversez-la à nouveau.","backdrop.downscale_failed":"Impossible de créer la copie réduite. Le plan actuel n'a pas été modifié."};const a="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";export{e as dictionary,a as fingerprint}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/houseplan-card-B5mDyjuC.js b/custom_components/houseplan/frontend/houseplan-assets/houseplan-card-wtxKVHkp.js similarity index 75% rename from custom_components/houseplan/frontend/houseplan-assets/houseplan-card-B5mDyjuC.js rename to custom_components/houseplan/frontend/houseplan-assets/houseplan-card-wtxKVHkp.js index 8e5c017f..bb07bc78 100644 --- a/custom_components/houseplan/frontend/houseplan-assets/houseplan-card-B5mDyjuC.js +++ b/custom_components/houseplan/frontend/houseplan-assets/houseplan-card-wtxKVHkp.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";const e=globalThis,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),n=new WeakMap;let r=class{constructor(e,t,n){if(this._$cssResult$=!0,n!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const i=this.t;if(t&&void 0===e){const t=void 0!==i&&1===i.length;t&&(e=n.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&n.set(i,e))}return e}toString(){return this.cssText}};const o=(e,...t)=>{const n=1===e.length?e[0]:t.reduce((t,i,n)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[n+1],e[0]);return new r(n,e,i)},s=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new r("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,m=p.trustedTypes,_=m?m.emptyScript:"",f=p.reactiveElementPolyfillSupport,g=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?_:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},y=(e,t)=>!a(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=b){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const i=Symbol(),n=this.getPropertyDescriptor(e,i,t);void 0!==n&&l(this.prototype,e,n)}}static getPropertyDescriptor(e,t,i){const{get:n,set:r}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:n,set(t){const o=n?.call(this);r?.call(this,t),this.requestUpdate(e,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??b}static _$Ei(){if(this.hasOwnProperty(g("elementProperties")))return;const e=u(this);e.finalize(),void 0!==e.l&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(g("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(g("properties"))){const e=this.properties,t=[...h(e),...d(e)];for(const i of t)this.createProperty(i,e[i])}const e=this[Symbol.metadata];if(null!==e){const t=litPropertyMetadata.get(e);if(void 0!==t)for(const[e,i]of t)this.elementProperties.set(e,i)}this._$Eh=new Map;for(const[e,t]of this.elementProperties){const i=this._$Eu(e,t);void 0!==i&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(s(e))}else void 0!==e&&t.push(s(e));return t}static _$Eu(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),void 0!==this.renderRoot&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const i of t.keys())this.hasOwnProperty(i)&&(e.set(i,this[i]),delete this[i]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,n)=>{if(t)i.adoptedStyleSheets=n.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of n){const n=document.createElement("style"),r=e.litNonce;void 0!==r&&n.setAttribute("nonce",r),n.textContent=t.cssText,i.appendChild(n)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$ET(e,t){const i=this.constructor.elementProperties.get(e),n=this.constructor._$Eu(e,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$Em=null}}_$AK(e,t){const i=this.constructor,n=i._$Eh.get(e);if(void 0!==n&&this._$Em!==n){const e=i.getPropertyOptions(n),r="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=n;const o=r.fromAttribute(t,e.type);this[n]=o??this._$Ej?.get(n)??o,this._$Em=null}}requestUpdate(e,t,i,n=!1,r){if(void 0!==e){const o=this.constructor;if(!1===n&&(r=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(r,t)||i.useDefault&&i.reflect&&r===this._$Ej?.get(e)&&!this.hasAttribute(o._$Eu(e,i))))return;this.C(e,t,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:i,reflect:n,wrapped:r},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==r||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===n&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}const e=this.constructor.elementProperties;if(e.size>0)for(const[t,i]of e){const{wrapped:e}=i,n=this[t];!0!==e||this._$AL.has(t)||void 0===n||this.C(t,void 0,i,n)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[g("elementProperties")]=new Map,w[g("finalized")]=new Map,f?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=e=>e,$=k.trustedTypes,S=$?$.createPolicy("lit-html",{createHTML:e=>e}):void 0,M="$lit$",R=`lit$${Math.random().toFixed(9).slice(2)}$`,T="?"+R,C=`<${T}>`,D=document,z=()=>D.createComment(""),A=e=>null===e||"object"!=typeof e&&"function"!=typeof e,P=Array.isArray,O="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,E=/>/g,H=RegExp(`>|${O}(?:([^\\s"'>=/]+)(${O}*=${O}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),N=/'/g,L=/"/g,W=/^(?:script|style|textarea|title)$/i,q=e=>(t,...i)=>({_$litType$:e,strings:t,values:i}),B=q(1),j=q(2),U=Symbol.for("lit-noChange"),V=Symbol.for("lit-nothing"),G=new WeakMap,K=D.createTreeWalker(D,129);function Y(e,t){if(!P(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(t):t}const X=(e,t)=>{const i=e.length-1,n=[];let r,o=2===t?"":3===t?"":"",s=F;for(let t=0;t"===l[0]?(s=r??F,c=-1):void 0===l[1]?c=-2:(c=s.lastIndex-l[2].length,a=l[1],s=void 0===l[3]?H:'"'===l[3]?L:N):s===L||s===N?s=H:s===I||s===E?s=F:(s=H,r=void 0);const d=s===H&&e[t+1].startsWith("/>")?" ":"";o+=s===F?i+C:c>=0?(n.push(a),i.slice(0,c)+M+i.slice(c)+R+d):i+R+(-2===c?t:d)}return[Y(e,o+(e[i]||"")+(2===t?"":3===t?"":"")),n]};class Z{constructor({strings:e,_$litType$:t},i){let n;this.parts=[];let r=0,o=0;const s=e.length-1,a=this.parts,[l,c]=X(e,t);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(n=K.nextNode())&&a.length0){n.textContent=$?$.emptyScript:"";for(let i=0;iP(e)||"function"==typeof e?.[Symbol.iterator])(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==V&&A(this._$AH)?this._$AA.nextSibling.data=e:this.T(D.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,n="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===n)this._$AH.p(t);else{const e=new Q(n,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=G.get(e.strings);return void 0===t&&G.set(e.strings,t=new Z(e)),t}k(e){P(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,n=0;for(const r of e)n===t.length?t.push(i=new ee(this.O(z()),this.O(z()),this,this.options)):i=t[n],i._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=V}_$AI(e,t=this,i,n){const r=this.strings;let o=!1;if(void 0===r)e=J(this,e,t,0),o=!A(e)||e!==this._$AH&&e!==U,o&&(this._$AH=e);else{const n=e;let s,a;for(e=r[0],s=0;s{const n=i?.renderBefore??t;let r=n._$litPart$;if(void 0===r){const e=i?.renderBefore??null;n._$litPart$=r=new ee(t.insertBefore(z(),e),e,void 0,i??{})}return r._$AI(e),r},ce=globalThis;let he=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=le(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return U}};he._$litElement$=!0,he.finalized=!0,ce.litElementHydrateSupport?.({LitElement:he});const de=ce.litElementPolyfillSupport;de?.({LitElement:he}),(ce.litElementVersions??=[]).push("4.2.2");const ue=new WeakMap;let pe=0;class me extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++pe,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=e=>{if("Escape"===e.key){e.preventDefault(),e.stopImmediatePropagation(),this._pruneOverlays();const t=this._overlays[this._overlays.length-1];return t?void this._closeOverlay(t,"escape"):void this._requestClose()}if("Tab"!==e.key||this._useHaDialog)return;const t=this.renderRoot.querySelector(".close"),i=t?[t,...this._focusableElements()]:this._focusableElements();if(!i.length)return e.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const n=this._deepActiveElement(),r=i[0],o=i[i.length-1];!e.shiftKey||n!==r&&i.includes(n)?e.shiftKey||n!==o||(e.preventDefault(),r.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=ue.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),ue.set(e,t),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const e=[...this._overlays];this._overlays=[];for(const t of e.reverse())t.close("disconnect");const t=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const n=t?ue.get(t):void 0;n?.dialogs.delete(this),super.disconnectedCallback(),t&&n&&requestAnimationFrame(()=>{const e=ue.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void ue.delete(t);const n=i?.closest("hp-dialog");i?.isConnected&&n&&e.dialogs.has(n)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._useHaDialog){const e=this.renderRoot.querySelector("dialog");e&&!e.open&&e.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let e=document.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e&&e!==document.body?e:null}_focusableElements(){const e=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),t=[],i=new Set,n=r=>{if(!i.has(r)){if(i.add(r),r instanceof HTMLElement){if(r.matches(e)&&t.push(r),r instanceof HTMLSlotElement){for(const e of r.assignedNodes({flatten:!0}))n(e);return}if(r.shadowRoot){for(const e of r.shadowRoot.childNodes)n(e);return}}for(const e of r.childNodes)n(e)}};for(const e of this.childNodes)n(e);const r=this.overlayPortal();return r&&n(r),t.filter(e=>{let t=e;for(;t;){const e=getComputedStyle(t);if(t.hidden||t.inert||"true"===t.getAttribute("aria-hidden")||"none"===e.display||"hidden"===e.visibility)return!1;if(t=t.assignedSlot||t.parentElement||(t.getRootNode()instanceof ShadowRoot?t.getRootNode().host:null),t===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(e=>e.owner.isConnected)}_closeOverlay(e,t){const i=this._overlays.findIndex(t=>t.token===e.token);i>=0&&this._overlays.splice(i,1),e.close(t)}registerOverlay(e){this._pruneOverlays();const t=this._overlays.find(t=>t.owner===e.owner);t&&this._overlays.splice(this._overlays.indexOf(t),1);const i=e.group||"transient";for(const e of[...this._overlays].reverse())e.group===i&&this._closeOverlay(e,"exclusive");const n={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(n);let r=!1;return()=>{if(r)return;r=!0;const e=this._overlays.findIndex(e=>e.token===n.token);e>=0&&this._overlays.splice(e,1)}}closeTransientOverlays(e="outside"){this._pruneOverlays();const t=[...this._overlays].filter(e=>"transient"===(e.group||"transient"));for(const i of t.reverse())this._closeOverlay(i,e);return t.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const e=B` +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";const e=globalThis,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),n=new WeakMap;let r=class{constructor(e,t,n){if(this._$cssResult$=!0,n!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const i=this.t;if(t&&void 0===e){const t=void 0!==i&&1===i.length;t&&(e=n.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&n.set(i,e))}return e}toString(){return this.cssText}};const o=(e,...t)=>{const n=1===e.length?e[0]:t.reduce((t,i,n)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[n+1],e[0]);return new r(n,e,i)},s=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new r("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,m=p.trustedTypes,_=m?m.emptyScript:"",f=p.reactiveElementPolyfillSupport,g=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?_:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},y=(e,t)=>!a(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=b){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const i=Symbol(),n=this.getPropertyDescriptor(e,i,t);void 0!==n&&l(this.prototype,e,n)}}static getPropertyDescriptor(e,t,i){const{get:n,set:r}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:n,set(t){const o=n?.call(this);r?.call(this,t),this.requestUpdate(e,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??b}static _$Ei(){if(this.hasOwnProperty(g("elementProperties")))return;const e=u(this);e.finalize(),void 0!==e.l&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(g("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(g("properties"))){const e=this.properties,t=[...h(e),...d(e)];for(const i of t)this.createProperty(i,e[i])}const e=this[Symbol.metadata];if(null!==e){const t=litPropertyMetadata.get(e);if(void 0!==t)for(const[e,i]of t)this.elementProperties.set(e,i)}this._$Eh=new Map;for(const[e,t]of this.elementProperties){const i=this._$Eu(e,t);void 0!==i&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(s(e))}else void 0!==e&&t.push(s(e));return t}static _$Eu(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),void 0!==this.renderRoot&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const i of t.keys())this.hasOwnProperty(i)&&(e.set(i,this[i]),delete this[i]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,n)=>{if(t)i.adoptedStyleSheets=n.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of n){const n=document.createElement("style"),r=e.litNonce;void 0!==r&&n.setAttribute("nonce",r),n.textContent=t.cssText,i.appendChild(n)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$ET(e,t){const i=this.constructor.elementProperties.get(e),n=this.constructor._$Eu(e,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$Em=null}}_$AK(e,t){const i=this.constructor,n=i._$Eh.get(e);if(void 0!==n&&this._$Em!==n){const e=i.getPropertyOptions(n),r="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=n;const o=r.fromAttribute(t,e.type);this[n]=o??this._$Ej?.get(n)??o,this._$Em=null}}requestUpdate(e,t,i,n=!1,r){if(void 0!==e){const o=this.constructor;if(!1===n&&(r=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(r,t)||i.useDefault&&i.reflect&&r===this._$Ej?.get(e)&&!this.hasAttribute(o._$Eu(e,i))))return;this.C(e,t,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:i,reflect:n,wrapped:r},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==r||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===n&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}const e=this.constructor.elementProperties;if(e.size>0)for(const[t,i]of e){const{wrapped:e}=i,n=this[t];!0!==e||this._$AL.has(t)||void 0===n||this.C(t,void 0,i,n)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[g("elementProperties")]=new Map,w[g("finalized")]=new Map,f?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=e=>e,$=k.trustedTypes,S=$?$.createPolicy("lit-html",{createHTML:e=>e}):void 0,M="$lit$",R=`lit$${Math.random().toFixed(9).slice(2)}$`,T="?"+R,C=`<${T}>`,D=document,z=()=>D.createComment(""),A=e=>null===e||"object"!=typeof e&&"function"!=typeof e,P=Array.isArray,O="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,E=/>/g,H=RegExp(`>|${O}(?:([^\\s"'>=/]+)(${O}*=${O}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),N=/'/g,L=/"/g,W=/^(?:script|style|textarea|title)$/i,q=e=>(t,...i)=>({_$litType$:e,strings:t,values:i}),B=q(1),j=q(2),U=Symbol.for("lit-noChange"),V=Symbol.for("lit-nothing"),G=new WeakMap,K=D.createTreeWalker(D,129);function Y(e,t){if(!P(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(t):t}const X=(e,t)=>{const i=e.length-1,n=[];let r,o=2===t?"":3===t?"":"",s=F;for(let t=0;t"===l[0]?(s=r??F,c=-1):void 0===l[1]?c=-2:(c=s.lastIndex-l[2].length,a=l[1],s=void 0===l[3]?H:'"'===l[3]?L:N):s===L||s===N?s=H:s===I||s===E?s=F:(s=H,r=void 0);const d=s===H&&e[t+1].startsWith("/>")?" ":"";o+=s===F?i+C:c>=0?(n.push(a),i.slice(0,c)+M+i.slice(c)+R+d):i+R+(-2===c?t:d)}return[Y(e,o+(e[i]||"")+(2===t?"":3===t?"":"")),n]};class Z{constructor({strings:e,_$litType$:t},i){let n;this.parts=[];let r=0,o=0;const s=e.length-1,a=this.parts,[l,c]=X(e,t);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(n=K.nextNode())&&a.length0){n.textContent=$?$.emptyScript:"";for(let i=0;iP(e)||"function"==typeof e?.[Symbol.iterator])(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==V&&A(this._$AH)?this._$AA.nextSibling.data=e:this.T(D.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,n="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===n)this._$AH.p(t);else{const e=new Q(n,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=G.get(e.strings);return void 0===t&&G.set(e.strings,t=new Z(e)),t}k(e){P(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,n=0;for(const r of e)n===t.length?t.push(i=new ee(this.O(z()),this.O(z()),this,this.options)):i=t[n],i._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=V}_$AI(e,t=this,i,n){const r=this.strings;let o=!1;if(void 0===r)e=J(this,e,t,0),o=!A(e)||e!==this._$AH&&e!==U,o&&(this._$AH=e);else{const n=e;let s,a;for(e=r[0],s=0;s{const n=i?.renderBefore??t;let r=n._$litPart$;if(void 0===r){const e=i?.renderBefore??null;n._$litPart$=r=new ee(t.insertBefore(z(),e),e,void 0,i??{})}return r._$AI(e),r},ce=globalThis;let he=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=le(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return U}};he._$litElement$=!0,he.finalized=!0,ce.litElementHydrateSupport?.({LitElement:he});const de=ce.litElementPolyfillSupport;de?.({LitElement:he}),(ce.litElementVersions??=[]).push("4.2.2");const ue=new WeakMap;let pe=0;class me extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++pe,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=e=>{if("Escape"===e.key){e.preventDefault(),e.stopImmediatePropagation(),this._pruneOverlays();const t=this._overlays[this._overlays.length-1];return t?void this._closeOverlay(t,"escape"):void this._requestClose()}if("Tab"!==e.key||this._useHaDialog)return;const t=this.renderRoot.querySelector(".close"),i=t?[t,...this._focusableElements()]:this._focusableElements();if(!i.length)return e.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const n=this._deepActiveElement(),r=i[0],o=i[i.length-1];!e.shiftKey||n!==r&&i.includes(n)?e.shiftKey||n!==o||(e.preventDefault(),r.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=ue.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),ue.set(e,t),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const e=[...this._overlays];this._overlays=[];for(const t of e.reverse())t.close("disconnect");const t=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const n=t?ue.get(t):void 0;n?.dialogs.delete(this),super.disconnectedCallback(),t&&n&&requestAnimationFrame(()=>{const e=ue.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void ue.delete(t);const n=i?.closest("hp-dialog");i?.isConnected&&n&&e.dialogs.has(n)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._useHaDialog){const e=this.renderRoot.querySelector("dialog");e&&!e.open&&e.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let e=document.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e&&e!==document.body?e:null}_focusableElements(){const e=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),t=[],i=new Set,n=r=>{if(!i.has(r)){if(i.add(r),r instanceof HTMLElement){if(r.matches(e)&&t.push(r),r instanceof HTMLSlotElement){for(const e of r.assignedNodes({flatten:!0}))n(e);return}if(r.shadowRoot){for(const e of r.shadowRoot.childNodes)n(e);return}}for(const e of r.childNodes)n(e)}};for(const e of this.childNodes)n(e);const r=this.overlayPortal();return r&&n(r),t.filter(e=>{let t=e;for(;t;){const e=getComputedStyle(t);if(t.hidden||t.inert||"true"===t.getAttribute("aria-hidden")||"none"===e.display||"hidden"===e.visibility)return!1;if(t=t.assignedSlot||t.parentElement||(t.getRootNode()instanceof ShadowRoot?t.getRootNode().host:null),t===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(e=>e.owner.isConnected)}_closeOverlay(e,t){const i=this._overlays.findIndex(t=>t.token===e.token);i>=0&&this._overlays.splice(i,1),e.close(t)}registerOverlay(e){this._pruneOverlays();const t=this._overlays.find(t=>t.owner===e.owner);t&&this._overlays.splice(this._overlays.indexOf(t),1);const i=e.group||"transient";for(const e of[...this._overlays].reverse())e.group===i&&this._closeOverlay(e,"exclusive");const n={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(n);let r=!1;return()=>{if(r)return;r=!0;const e=this._overlays.findIndex(e=>e.token===n.token);e>=0&&this._overlays.splice(e,1)}}closeTransientOverlays(e="outside"){this._pruneOverlays();const t=[...this._overlays].filter(e=>"transient"===(e.group||"transient"));for(const i of t.reverse())this._closeOverlay(i,e);return t.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const e=B` ${this.icon?B``:V} ${this.title} `;return this._useHaDialog?B` `})} - `}const tr={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},ir=(e,t=1)=>{const i=Number(e);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):t},nr=e=>{let t=Number(e);return Number.isFinite(t)?(t=(t%360+360)%360,t>180?t-360:t):0};function rr(e,t,i){return nr(t)?[e.x,e.y]:i([e.x,e.y])}function or(e,t,i,n){const r=Math.abs(i[0]-t[0]),o=Math.abs(i[1]-t[1]);return"line"===e?Math.hypot(r,o)>=n:r>=n&&o>=n}const sr=(e,t,i)=>{const n=Number(e),r=Number.isFinite(t)&&t>0?t:5;return Number.isFinite(n)&&n>0?n/r*i:0},ar=(e,t,i)=>{const n=Number(e),r=Number.isFinite(t)&&t>0?t:5;return Number.isFinite(n)&&i>0?n/i*r:0},lr=(e,t,i,n=tr.widthCm)=>{const r=Number(e?.width_cm);if(Number.isFinite(r)&&r>0)return r;const o=Number(e?.width);return Number.isFinite(o)&&o>0?ar(o,t,i):n};function cr(e){const t=tr;return e.color===t.color&&e.opacity===t.opacity&&e.widthCm===t.widthCm&&e.fill===t.fill&&e.fillColor===t.fillColor&&e.fillOpacity===t.fillOpacity?null:{color:e.color,opacity:ir(e.opacity),width_cm:Math.max(.1,Math.min(100,Number(e.widthCm)||.1)),fill:!!e.fill,fill_color:e.fillColor,fill_opacity:ir(e.fillOpacity,.25)}}function hr(e,t){return{color:e.color,opacity:ir(e.opacity),width_cm:Math.max(.1,Math.min(100,Number(e.widthCm)||.1)),...t?{fill:e.fill,fill_color:e.fillColor,fill_opacity:ir(e.fillOpacity,.25)}:{}}}function dr(e){const t=e.x+e.w/2,i=e.y+e.h/2,n=nr(e.angle)*Math.PI/180,r=Math.cos(n),o=Math.sin(n),s=(e,n)=>{const s=e-t,a=n-i;return[t+s*r-a*o,i+s*o+a*r]};return[s(e.x,e.y),s(e.x+e.w,e.y),s(e.x+e.w,e.y+e.h),s(e.x,e.y+e.h)]}function ur(e){const t=dr(e),i=(e,t)=>[(e[0]+t[0])/2,(e[1]+t[1])/2],n=[(t[0][0]+t[2][0])/2,(t[0][1]+t[2][1])/2];return{points:[...t,i(t[0],t[1]),i(t[1],t[2]),i(t[2],t[3]),i(t[3],t[0]),n],segments:t.map((e,i)=>({a:e,b:t[(i+1)%4]}))}}function pr(e,t,i,n,r,o,s,a){const l=nr(e.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l),d=-Math.sin(l),u=Math.cos(l),p=e.x+e.w/2,m=e.y+e.h/2,_=t>0?-e.w/2:e.w/2,f=i>0?-e.h/2:e.h/2,g=p+_*c+f*d,v=m+_*h+f*u,y=n-g,b=r-v;let w=(y*c+b*h)*(t>0?1:-1),k=(y*d+b*u)*(i>0?1:-1),x=null;if(o){const t=w/Math.max(e.w,a),i=k/Math.max(e.h,a);x=Math.max(a/Math.max(e.w,a),a/Math.max(e.h,a),t,i),w=e.w*x,k=e.h*x}if(s>0)if(o){const t=Math.max(1,Math.round(e.w/s)),i=Math.max(1,Math.round(e.h/s));if(Math.abs(e.w-t*s)<1e-6&&Math.abs(e.h-i*s)<1e-6){const n=(e,t)=>{let i=Math.abs(e),n=Math.abs(t);for(;n;)[i,n]=[n,i%n];return Math.max(1,i)},r=1/n(t,i),o=Math.max(a/e.w,a/e.h),s=Math.max(o,Math.round((x??1)/r)*r);w=e.w*s,k=e.h*s}else{e.w>=e.h?(w=Math.round(w/s)*s,k=w*(e.h/Math.max(e.w,a))):(k=Math.round(k/s)*s,w=k*(e.w/Math.max(e.h,a)))}}else w=Math.round(w/s)*s,k=Math.round(k/s)*s;w=Math.max(a,w),k=Math.max(a,k);return{x:g+(t>0?w/2:-w/2)*c+(i>0?k/2:-k/2)*d-w/2,y:v+(t>0?w/2:-w/2)*h+(i>0?k/2:-k/2)*u-k/2,w:w,h:k,angle:nr(e.angle)||void 0}}const mr=(e,t,i)=>{const n=i[0]-t[0],r=i[1]-t[1],o=n*n+r*r;if(o<1e-12)return[...t];const s=Math.max(0,Math.min(1,((e[0]-t[0])*n+(e[1]-t[1])*r)/o));return[t[0]+n*s,t[1]+r*s]};function _r(e,t,i,n){const r=n(e);let o=null;for(const r of t.points){const t=n(r),s=Math.hypot(t[0]-e[0],t[1]-e[1]);s<=i&&(!o||se.points),segments:e.flatMap(e=>e.segments)}}const gr=Math.tan(.25*Math.PI/180);function vr(e,t){const i=Math.abs(Number(t[0])-Number(e[0])),n=Math.abs(Number(t[1])-Number(e[1]));if(!([i,n].every(Number.isFinite)&&i>0&&n>0))return null;const r=i>=n?"horizontal":"vertical",o=Math.max(i,n),s=Math.min(i,n);return s/o>gr?null:{axis:r,major:o,minor:s,angleDegrees:180*Math.atan2(s,o)/Math.PI}}function yr(e,t){const i=vr(e,t);return i?"horizontal"===i.axis?[Number(t[0]),Number(e[1])]:[Number(e[0]),Number(t[1])]:[Number(t[0]),Number(t[1])]}const br=e=>`${e[0]},${e[1]}`,wr=(e,t)=>e[0]===t[0]&&e[1]===t[1],kr=(e,t)=>{const i=br(e),n=br(t);return i{let t=0;for(let i=0;i{if(e.length<3)return!1;for(let t=0;t1e-12},Sr=e=>{for(let t=0;t+1wr(e,t.from)?[...t.to]:[...e],Rr=(e,t)=>e.map(e=>Array.isArray(e?.poly)?{...e,poly:e.poly.map(e=>Mr(e,t))}:e),Tr=(e,t,i)=>{const n=e.flatMap((e,t)=>Et(e)?.some(e=>wr(e,i.from))?[t]:[]);if(!n.length)return!1;for(const i of n){const n=Et(e[i]),r=Et(t[i]);if(!n||!r)continue;if(!$r(r))return!1;const o=Math.sign(xr(n)),s=Math.sign(xr(r));if(o&&s&&o!==s)return!1}const r=new Set;for(const e of n){const i=Et(t[e]);if(i)for(let n=0;n{let i=0;for(const n of e){const e=Et(n);if(e)for(let n=0;n{const{a:i,b:n,axis:r}=e,o="horizontal"===r?{from:n,to:[n[0],i[1]]}:{from:n,to:[i[0],n[1]]},s="horizontal"===r?{from:i,to:[i[0],n[1]]}:{from:i,to:[n[0],i[1]]};return[{move:o,preservedDegree:Cr(t,i)},{move:s,preservedDegree:Cr(t,n)}].sort((e,t)=>t.preservedDegree-e.preservedDegree||br(e.move.to).localeCompare(br(t.move.to))||br(e.move.from).localeCompare(br(t.move.from))).map(e=>e.move)};function zr(e){const t=JSON.parse(JSON.stringify(e||{}));let i=Array.isArray(t.rooms)?t.rooms:[];const n=new Map;for(const e of i){const t=Et(e);if(t)for(let e=0;ee.key.localeCompare(t.key))){let t=null;for(const n of Dr(e,i)){const e=a.get(br(n.from));if(e&&e!==br(n.to))continue;const r=Rr(i,n);if(Tr(i,r,n)){t=n,i=r;break}}t?(a.set(br(t.from),br(t.to)),r++,s=Math.max(s,Math.hypot(t.to[0]-t.from[0],t.to[1]-t.from[1]))):o++}t.rooms=i;for(const e of t.room_drafts||[]){if(!Array.isArray(e?.points)||e.points.length<2)continue;const t=e.points.map(e=>[...e]),i=t.slice(0,-1).flatMap((i,n)=>{const r=t[n+1],o=vr(i,r);return o?[{key:`${String(e.id||"")}:${n}`,a:[...i],b:[...r],axis:o.axis}]:[]});let n=t;for(const e of i){let t=null;for(const i of Dr(e,[{poly:n}])){const e=n.map(e=>Mr(e,i)),r=e.length>=4&&wr(e[0],e[e.length-1]),o=r?e.slice(0,-1):e;if(r?$r(o):Sr(o)){t=i,n=e;break}}t?(r++,s=Math.max(s,Math.hypot(t.to[0]-t.from[0],t.to[1]-t.from[1]))):o++}e.points=n}for(const e of t.partitions||[]){if(!Array.isArray(e?.a)||!Array.isArray(e?.b))continue;const i=vr(e.a,e.b);if(!i)continue;const n={key:String(e.id||kr(e.a,e.b)),a:[...e.a],b:[...e.b],axis:i.axis};let a=null;for(const i of Dr(n,[{poly:[e.a,e.b]}])){const n=Mr(e.a,i),r=Mr(e.b,i),o=Math.hypot(r[0]-n[0],r[1]-n[1]);if(!(o>0))continue;const s=(t.openings||[]).filter(t=>"partition"===t?.host?.kind&&t.host.id===e.id).every(e=>{const t=Number(e.host.t),i=Number(e.length);return Number.isFinite(t)&&t>=0&&t<=1&&Number.isFinite(i)&&i>0&&t*o-i/2>=-1e-12&&t*o+i/2<=o+1e-12});if(s){a=i,e.a=n,e.b=r;break}}a?(r++,s=Math.max(s,Math.hypot(a.to[0]-a.from[0],a.to[1]-a.from[1]))):o++}return{space:t,report:{wallsStraightened:r,wallsStraightenSkipped:o,maxStraightenShift:s},changed:r>0}}const Ar=10**9,Pr=240,Or=1e-4;function Fr(e){if(Array.isArray(e))return e.map(e=>Fr(e));if(null!==e&&"object"==typeof e){const t={};for(const[i,n]of Object.entries(e))t[i]=Fr(n);return t}return e}function Ir(e){if("number"!=typeof e||!Number.isFinite(e))return e;const t=(e<0||Object.is(e,-0)?-1:1)*(Math.floor(Math.abs(e)*Ar+.5)/Ar);return 0===t?0:t}function Er(e){if("number"!=typeof e||!Number.isFinite(e))return e;const t=e*Pr,i=Math.round(t);if(Math.abs(t-i)null!==Nr(e)):[]}function Wr(e,t){for(const i of t)Object.prototype.hasOwnProperty.call(e,i)&&(e[i]=Ir(e[i]))}function qr(e,t){for(const i of t)Object.prototype.hasOwnProperty.call(e,i)&&(e[i]=Er(e[i]))}function Br(e){if(Array.isArray(e))for(let t=0;t{for(const r of n){const n=t[r];"number"==typeof n&&Number.isFinite(n)&&i(n,e)}},n=t=>{if(Array.isArray(t))for(let n=0;n{if(Array.isArray(e))for(const t of e)n(t)};for(const i of Lr(e.rooms))t(i,["x","y","w","h"]),r(i.poly);for(const t of Lr(e.walls))n(t.a),n(t.b);for(const t of Lr(e.wall_segments))n(t.a),n(t.b);for(const i of Lr(e.openings))t(i,["x","y"]);for(const i of Lr(e.decor))"line"===i.kind?t(i,["x1","y1","x2","y2"]):"rect"===i.kind||"ellipse"===i.kind||"furniture"===i.kind?t(i,["x","y","w","h"]):"text"===i.kind&&t(i,["x","y"]);for(const t of Lr(e.room_drafts))r(t.points);for(const t of Lr(e.partitions))n(t.a),n(t.b);for(const t of Lr(e.wall_columns))n(t.center);for(const t of Lr(e.open_spans))n(t.a),n(t.b)}const r=Nr(t);if(!r)return;const o=new Map;if(n)for(const e of Lr(n.spaces))null!=e.id&&o.set(String(e.id),e);for(const e of Object.values(r)){const t=Nr(e);if(!t)continue;const n=null!=t.s&&o.get(String(t.s))||null;for(const e of["x","y"]){const r=t[e];"number"==typeof r&&Number.isFinite(r)&&i(r,n)}}}function Vr(e){const t=Number(e?.cell_cm);return t>0?t:5}function Gr(e,t={}){const i={canonicalized:0,far:0,maxShift:0,maxShiftCm:0,spaces:[],bySpace:new Map},n=Lr(Nr(e)?.spaces);let r=5;for(const e of n)r=Math.max(r,Vr(e));Ur(e,t,(e,t)=>{const n=e*Pr,o=Math.abs(n-Math.round(n)),s=Er(e),a=o>0&&o=Or))return;const l=a&&"number"==typeof s?Math.abs(s-e):0;if(a?i.canonicalized++:i.far++,i.maxShift=Math.max(i.maxShift,l),i.maxShiftCm=Math.max(i.maxShiftCm,l*Pr*(t?Vr(t):r)),!t?.id||!a)return;const c=String(t.id);let h=i.bySpace.get(c);h||(h={spaceId:c,space:String(t.title||c),canonicalized:0,far:0,maxShift:0,maxShiftCm:0},i.bySpace.set(c,h)),h.canonicalized++,h.maxShift=Math.max(h.maxShift,l),h.maxShiftCm=Math.max(h.maxShiftCm,l*Pr*Vr(t))}),i.bySpace.size&&Ur(e,t,(e,t)=>{if(!t?.id)return;const n=i.bySpace.get(String(t.id));if(!n)return;const r=e*Pr;Math.abs(r-Math.round(r))>=Or&&n.far++}),i.spaces=[...i.bySpace.values()];const{bySpace:o,...s}=i;return s}function Kr(e){return function(e){const t=e,i=Nr(t);i&&qr(i,["x","y"]);return t}(Fr(e))}function Yr(e){const t=e,i=Nr(t);if(!i)return t;for(const e of Object.values(i)){const t=Nr(e);t&&qr(t,["x","y"])}return t}function Xr(e){return Zr(Fr(e))}function Zr(e){const t=e,i=Nr(t);if(!i)return t;for(const e of Lr(i.spaces)){Wr(e,["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"]);for(const t of Lr(e.rooms))qr(t,["x","y","w","h"]),jr(t.poly);for(const t of Lr(e.walls))Br(t.a),Br(t.b);for(const t of Lr(e.wall_segments))Br(t.a),Br(t.b);for(const t of Lr(e.openings)){qr(t,["x","y"]),Wr(t,["angle","length"]);const e=Nr(t.host);e&&Wr(e,["t"])}for(const t of Lr(e.decor))"line"===t.kind?qr(t,["x1","y1","x2","y2"]):"rect"===t.kind||"ellipse"===t.kind||"furniture"===t.kind?(qr(t,["x","y","w","h"]),Wr(t,["angle"])):"text"===t.kind&&(qr(t,["x","y"]),Wr(t,["scale","angle"]));for(const t of Lr(e.room_drafts))jr(t.points);for(const t of Lr(e.partitions))Br(t.a),Br(t.b);for(const t of Lr(e.wall_columns))Br(t.center),"square"===t.shape&&Wr(t,["angle"]);for(const t of Lr(e.open_spans))Br(t.a),Br(t.b)}for(const e of Lr(i.markers))Wr(e,["angle"]);return t}const Jr=15,Qr=8;function eo(e,t,i,n,r){const o=i[0]-e[0],s=i[1]-e[1],a=Math.hypot(o,s);if(!(a>0)||a<=r+1e-9)return null;const l=o/a,c=s/a,h=t=>{const n=(t[0]-e[0])*l+(t[1]-e[1])*c,o=(r-n)/(a-n);return!Number.isFinite(o)||o<-1e-9||o>1+1e-9?null:[t[0]+(i[0]-t[0])*o,t[1]+(i[1]-t[1])*o]},d=h(t),u=h(n);return d&&u?[[e[0],e[1]],t,d,u,n]:null}const to=Math.sin(.25*Math.PI/180);function io(e,t){return Number.isFinite(e)&&e>0&&Number.isFinite(t)&&t>0&&e*t<3}function no(e){const t=Number(e)>0?Number(e):5;if(5===t)return 8;const i=5/t*8;return Math.min(80,Math.max(.5,i))}function ro(e,t){return Number.isFinite(e)&&e>0&&Number.isFinite(t)&&t>0&&e*t<2}function oo(e){return Number.isFinite(e)?Math.max(1,Math.min(100,e)):1}function so(e,t){return!Number.isFinite(e)||e<0?"":String(t?Math.round(e/2.54*100)/100:Math.round(100*e)/100)}function ao(e,t,i){if(!Number.isFinite(e)||e<=0)return 0;const n=Number(t)>0?Number(t):5;return oo(e)/n*i}function lo(e,t){return t>0&&Number.isFinite(e)?Math.round(e/t)*t:e}function co(e){return Math.max(1e-6*Math.abs(e),1e-9)}function ho(e,t){if(!(t>0&&Number.isFinite(e)))return e;const i=lo(e,t);return Math.abs(i-e)<=co(t)?i:e}function uo(e,t){let i=t[0]-e[0],n=t[1]-e[1];const r=Math.hypot(i,n);return r<1e-12?[1,0]:(i/=r,n/=r,(i<-1e-12||Math.abs(i)<=1e-12&&n<0)&&(i=-i,n=-n),[i,n])}function po(e,t,i){const n=[ho(e[0],i),ho(e[1],i)],r=[ho(t[0],i),ho(t[1],i)],o=lo((n[0]+r[0])/2,i),s=lo((n[1]+r[1])/2,i),[a,l]=uo(n,r);let c=Math.atan2(l,a);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)},${s.toFixed(d)}@${h.toFixed(4)}`}function mo(e,t,i,n){return 1===n?po(e,t,i):po([e[0]/n,e[1]/n],[t[0]/n,t[1]/n],i)}function _o(e,t){if(!Array.isArray(e.a)||!Array.isArray(e.b)||e.a.length<2||e.b.length<2)return null;const i=[Number(e.a[0]),Number(e.a[1]),Number(e.b[0]),Number(e.b[1])];if(!i.every(Number.isFinite))return null;const n=t>0?t:1;return[[i[0]*n,i[1]*n],[i[2]*n,i[3]*n]]}function fo(e,t,i,n,r){const o=r>0?r:1;return{key:mo(e,t,n,o),cm:oo(i),a:[e[0]/o,e[1]/o],b:[t[0]/o,t[1]/o]}}function go(e,t){const i=t>0?t:1,n=[];for(const t of e){const e=t.key.lastIndexOf("@");if(e<0)continue;const[r,o]=t.key.slice(0,e).split(",").map(Number),s=Number(t.key.slice(e+1));[r,o,s].every(Number.isFinite)&&n.push({w:t,x:r*i,y:o*i,ang:s})}return n}function vo(e,t){const[i,n]=uo(e,t);let r=Math.atan2(n,i);return r<0&&(r+=Math.PI),r}function yo(e,t){let i=Math.abs(e-t);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function bo(e,t,i,n,r=1){if(!e?.length)return null;const o=mo(t,i,n,r),s=e.find(e=>e.key===o);if(s)return s;const a=r>0?r:1,l=co(n)*a,c=(e,t)=>Math.abs(e[0]-t[0])<=l&&Math.abs(e[1]-t[1])<=l;for(const n of e){const e=_o(n,a);if(e&&(c(e[0],t)&&c(e[1],i)||c(e[0],i)&&c(e[1],t)))return n}const h=(t[0]+i[0])/2,d=(t[1]+i[1])/2,u=vo(t,i),p=Math.max(.5*n,1e-9)*a;for(const t of go(e,a))if(yo(t.ang,u)&&Math.hypot(t.x-h,t.y-d)<=p)return t.w;return null}function wo(e,t,i,n,r=1){const o=bo(e,t,i,n,r);if(o&&o.cm>0)return oo(o.cm);const s=function(e,t,i,n,r){if(!e?.length)return null;const o=r>0?r:1,s=Math.hypot(i[0]-t[0],i[1]-t[1]);if(s<1e-12)return null;const a=vo(t,i),l=Math.max(.5*n,1e-9)*o;let c=null;for(const n of e){if(!(n.cm>0))continue;const e=_o(n,o);if(!e)continue;const r=Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1]);if(r<1e-12||!yo(vo(e[0],e[1]),a))continue;if(hs(t[0],t[1],e[0][0],e[0][1],e[1][0],e[1][1])>l||hs(i[0],i[1],e[0][0],e[0][1],e[1][0],e[1][1])>l)continue;if(r+l(o.has(e.key)||(e=>{const t=_o(e,n);if(!t)return!1;const[i,o]=t,a=o[0]-i[0],c=o[1]-i[1],h=Math.hypot(a,c);if(h<=l)return!1;if(!s.some(e=>{const t=[e[0],e[1]],n=[e[2],e[3]];return yo(vo(i,o),vo(t,n))&&hs(i[0],i[1],t[0],t[1],n[0],n[1])<=l&&hs(o[0],o[1],t[0],t[1],n[0],n[1])<=l}))return!1;const d=(r||[]).some(e=>{const t=[e[0],e[1]],n=[e[2],e[3]];if(!yo(vo(i,o),vo(t,n)))return!1;const r=e=>Math.abs((e[0]-i[0])*c-(e[1]-i[1])*a)/h;if(r(t)>l||r(n)>l)return!1;const s=h*h,d=((t[0]-i[0])*a+(t[1]-i[1])*c)/s,u=((n[0]-i[0])*a+(n[1]-i[1])*c)/s;return Math.min(1,Math.max(d,u))-Math.max(0,Math.min(d,u))>l/h});return!d})(e))&&e.cm>=1&&e.cm<=100)}function xo(e,t,i,n=8){const[r,o]=uo(e,t);let s=Math.atan2(o,r);s<0&&(s+=Math.PI);let a=i*Math.PI/180%Math.PI;a<0&&(a+=Math.PI);let l=Math.abs(s-a);return l>Math.PI/2&&(l=Math.PI-l),l<=n*Math.PI/180}function $o(e,t,i,n,r=1,o="affine",s){if(!e?.length)return[];if(t.length!==i.length)return"fixed-topology"===o&&s?.(),e.slice();const a=r>0?r:1,l=Math.max(.5*n,1e-9)*a,c=Math.max(n*a*1e-6,1e-9),h=[],d=new Map,u=new Map,p=(e,t,i)=>{const n=e.get(t)||new Set;n.add(i),e.set(t,n)};for(let e=0;e[e[0]+(t[0]-e[0])*i,e[1]+(t[1]-e[1])*i],_=(e,t)=>Math.hypot(e[0]-t[0],e[1]-t[1])<=c,f=(e,t)=>{if("fixed-topology"===o){const i=t.na[0]-t.oa[0],n=t.na[1]-t.oa[1],r=t.nb[0]-t.ob[0],o=t.nb[1]-t.ob[1];return Math.hypot(i-r,n-o)<=c?[e[0]+i,e[1]+n]:_(e,t.oa)?[...t.na]:_(e,t.ob)?[...t.nb]:[...e]}const i=Math.max(0,Math.min(1,((e[0]-t.oa[0])*t.dx+(e[1]-t.oa[1])*t.dy)/t.len2));return m(t.na,t.nb,i)},g=(e,t)=>{const[i,n]=uo(e,t);return(t[0]-e[0])*i+(t[1]-e[1])*n>=0?[[...e],[...t]]:[[...t],[...e]]},v=[],y=[],b=(e,t,i)=>{if(Math.hypot(t[0]-e[0],t[1]-e[1])<=c)return;const[r,o]=g(e,t),s=oo(i);if(y.some(e=>e.entry.cm===s&&_(e.span[0],r)&&_(e.span[1],o)))return;const l=fo(r,o,s,n,a);v.push(l),y.push({entry:l,span:[r,o]})};for(const t of e){const e=_o(t,a);if(e){const[i,n]=g(e[0],e[1]),r=n[0]-i[0],s=n[1]-i[1],a=r*r+s*s,d=Math.sqrt(a);if(d<=c){v.push({...t,cm:oo(t.cm)});continue}const u=[];for(const e of h){if(!yo(vo(i,n),vo(e.oa,e.ob)))continue;const t=e=>Math.abs((e[0]-i[0])*s-(e[1]-i[1])*r)/d;if(t(e.oa)>l||t(e.ob)>l)continue;const o=((e.oa[0]-i[0])*r+(e.oa[1]-i[1])*s)/a,h=((e.ob[0]-i[0])*r+(e.ob[1]-i[1])*s)/a,p=Math.max(0,Math.min(o,h)),m=Math.min(1,Math.max(o,h));(m-p)*d>c&&u.push({lo:p,hi:m,move:e})}if(!u.length){"fixed-topology"===o?v.push({...t}):b(i,n,t.cm);continue}const p=[0,1,...u.flatMap(({lo:e,hi:t})=>[e,t])].sort((e,t)=>e-t).filter((e,t,i)=>0===t||Math.abs(e-i[t-1])*d>c),y=[];for(let e=0;e+1a>=e.lo-1e-12&&a<=e.hi+1e-12);if(!l.length){y.push([o,s]);continue}const h=[f(o,l[0].move),f(s,l[0].move)],g=l.slice(1).some(e=>{const t=f(o,e.move),i=f(s,e.move);return!_(h[0],t)||!_(h[1],i)});y.push(g?[o,s]:h)}const w=(e,t)=>{const i=e[1][0]-e[0][0],n=e[1][1]-e[0][1],r=t[1][0]-t[0][0],o=t[1][1]-t[0][1],s=Math.hypot(i,n);return!(s<=c||i*r+n*o<=0)&&Math.abs(i*o-n*r)/s<=c},k=[];for(const e of y){const t=k[k.length-1];t&&_(t[1],e[0])&&w(t,e)?t[1]=e[1]:k.push([[...e[0]],[...e[1]]])}if("fixed-topology"===o&&1===k.length){const[i,n]=k[0];if(_(i,e[0])&&_(n,e[1])||_(i,e[1])&&_(n,e[0])){v.push({...t});continue}}for(const[e,i]of k)b(e,i,t.cm);continue}if("fixed-topology"===o){const e=u.get(t.key);if(1===e?.size){const i=[...e][0];v.push(i===t.key?{...t}:{...t,key:i});continue}const i=[go([t],a)[0]];1!==a&&i.push(go([t],1)[0]);const n=i.filter(Boolean).some(e=>h.some(t=>{if(!yo(e.ang,vo(t.oa,t.ob)))return!1;const i=((e.x-t.oa[0])*t.dx+(e.y-t.oa[1])*t.dy)/t.len2;return i>=-1e-6&&i<=1.000001&&hs(e.x,e.y,t.oa[0],t.oa[1],t.ob[0],t.ob[1])<=l}));((e?.size||0)>1||n)&&s?.(),v.push({...t});continue}let i="";const r=d.get(t.key);if(1===r?.size&&(i=[...r][0]),!i){const e=go([t],a)[0];if(e){const t=new Set;for(const i of h){if(!yo(e.ang,vo(i.oa,i.ob)))continue;const r=((e.x-i.oa[0])*i.dx+(e.y-i.oa[1])*i.dy)/i.len2;if(r<-1e-6||r>1.000001)continue;if(hs(e.x,e.y,i.oa[0],i.oa[1],i.ob[0],i.ob[1])>l)continue;const o=f([e.x,e.y],i),[s,c]=uo(i.na,i.nb),h=Math.max(n*a,1e-6);t.add(mo([o[0]-s*h,o[1]-c*h],[o[0]+s*h,o[1]+c*h],n,a))}1===t.size&&(i=[...t][0])}}v.push({...t,key:i||t.key,cm:oo(t.cm)})}return v}function So(e,t,i,n,r=1,o="affine"){return $o(e,t,i,n,r,o)}function Mo(e,t,i,n,r=1,o="fixed-topology"){let s=!1;const a=$o(e,t,i,n,r,o,()=>{s=!0});return{walls:s?(e||[]).map(e=>({...e})):a,rejected:s}}function Ro(e,t,i,n=1,r=[]){const o=n>0?n:1,s=Math.abs(i),a=Math.max(s*o*Or,1e-9),l=(r||[]).flatMap(e=>{const t=_o(e,o);return t?t.map(e=>[...e]):[]}),c=e=>e.every((e,t)=>(e=>{if(!(s>0))return!0;const t=e/o/s;return Math.abs(t-Math.round(t))Math.abs(e-i[t])<=a)),h=[],d=e=>JSON.stringify([e.key,e.cm,e.a,e.b]);for(const i of e||[]){const e=_o(i,o);if(!e)continue;const[n,r]=e;if(![n[0],n[1],r[0],r[1]].every(Number.isFinite)||!c(n)||!c(r)){h.push(d(i));continue}const s=r[0]-n[0],l=r[1]-n[1],u=Math.hypot(s,l);if(u<=a){h.push(d(i));continue}const p=s/u,m=l/u,_=[];for(const e of t){const[t,i]=e;if(![t?.[0],t?.[1],i?.[0],i?.[1]].every(Number.isFinite))continue;const r=e=>Math.abs((e[0]-n[0])*m-(e[1]-n[1])*p);if(r(t)>a||r(i)>a)continue;const o=(t[0]-n[0])*p+(t[1]-n[1])*m,s=(i[0]-n[0])*p+(i[1]-n[1])*m,l=Math.max(0,Math.min(o,s)),c=Math.min(u,Math.max(o,s));c-l>a&&_.push([l,c])}_.sort((e,t)=>e[0]-t[0]||e[1]-t[1]);let f=0;for(const[e,t]of _){if(e>f+a)break;if(f=Math.max(f,t),f>=u-a)break}fe.key!==s);return null==n||n<1?a:[...a,fo(t,i,n,r,o)]}function Co(e,t,i,n,r,o=[],s=1){let a=e?e.slice():[];for(const e of function(e,t,i,n,r=1,o=[]){const s=Xo(e,t,i,n,r,o);if(!s)return[];const a=[];for(let e=0;e0||(c=To(c,t,i,n,r,s)))}return c}function zo(e){const{a:t,b:i,halfDepth:n}=e;if(!Array.isArray(t)||!Array.isArray(i)||t.length<2||i.length<2||![t[0],t[1],i[0],i[1]].every(Number.isFinite))return null;const r=i[0]-t[0],o=i[1]-t[1],s=Math.hypot(r,o);if(!(s>1e-9&&n>0&&Number.isFinite(n)))return null;const a=-o/s*n,l=r/s*n;return[[t[0]+a,t[1]+l],[i[0]+a,i[1]+l],[i[0]-a,i[1]-l],[t[0]-a,t[1]-l]]}function Ao(e,t,i){return Math.hypot(e[0]-t[0],e[1]-t[1])<=i}function Po(e,t,i){const n=t.b[0]-t.a[0],r=t.b[1]-t.a[1],o=n*n+r*r;if(!(o>i*i))return!1;const s=((e[0]-t.a[0])*n+(e[1]-t.a[1])*r)/o;if(!(s>0&&s<1))return!1;const a=[t.a[0]+n*s,t.a[1]+r*s];return Math.hypot(e[0]-a[0],e[1]-a[1])<=i}function Oo(e,t,i,n){const r=Math.hypot(t,i);if(!(r>1e-9&&n>0))return;const o=[t/r,i/r],s=e.find(e=>Math.abs(e.u[0]*o[1]-e.u[1]*o[0])<1e-9&&e.u[0]*o[0]+e.u[1]*o[1]>1-1e-9);s?s.halfDepth=Math.max(s.halfDepth,n):e.push({u:o,halfDepth:n})}function Fo(e,t){try{const i=Mt(ps(e),ps(t));let n=null,r=0;for(const e of i||[]){const t=e?.[0]||[],i=Math.abs(Wo(t));t.length>=4&&i>r&&(r=i,n=t.slice(0,-1).map(e=>[e[0],e[1]]))}return n}catch{return null}}function Io(e,t=1e-6){const i=(e||[]).map((e,t)=>({segment:e,index:t})).filter(({segment:e})=>e&&Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2&&e.a.every(Number.isFinite)&&e.b.every(Number.isFinite)&&Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>1e-9);if(i.length<2)return[];const n=Math.max(Number.isFinite(t)?t:0,1e-9),r=i.flatMap(({segment:e})=>[e.a,e.b]).map(e=>[e[0],e[1]]).sort((e,t)=>e[0]-t[0]||e[1]-t[1]),o=[];for(const e of r)o.some(t=>Ao(t,e,n))||o.push(e);const s=[];for(const e of o){const t=[];let r=!1;for(const{segment:o,index:s}of i){const i=Math.hypot(o.b[0]-o.a[0],o.b[1]-o.a[1]);Ao(e,o.a,n)?t.push({u:[(o.b[0]-o.a[0])/i,(o.b[1]-o.a[1])/i],halfDepth:o.halfDepth,length:i,index:s}):Ao(e,o.b,n)?t.push({u:[(o.a[0]-o.b[0])/i,(o.a[1]-o.b[1])/i],halfDepth:o.halfDepth,length:i,index:s}):Po(e,o,n)&&(r=!0)}if(r||2!==t.length)continue;const[o,a]=t,l=o.u[0]*a.u[1]-o.u[1]*a.u[0];if(Math.abs(l)<1e-9)continue;const c=l<0?1:-1,h=[-o.u[1],o.u[0]],d=[-a.u[1],a.u[0]],u=[e[0]+h[0]*o.halfDepth*c,e[1]+h[1]*o.halfDepth*c],p=[e[0]-d[0]*a.halfDepth*c,e[1]-d[1]*a.halfDepth*c];if(!jo(u,o.u,p,a.u))continue;const m=[[o,a,p,[d[0]*-c,d[1]*-c]],[a,o,u,[h[0]*c,h[1]*c]]];for(const[t,,i,r]of m){const o=Math.min(2*t.halfDepth,t.length),a=[-t.u[1]*t.halfDepth,t.u[0]*t.halfDepth],l=[[e[0]+a[0],e[1]+a[1]],[e[0]+t.u[0]*o+a[0],e[1]+t.u[1]*o+a[1]],[e[0]+t.u[0]*o-a[0],e[1]+t.u[1]*o-a[1]],[e[0]-a[0],e[1]-a[1]]],c=e=>(e[0]-i[0])*r[0]+(e[1]-i[1])*r[1],h=[];for(let e=0;e=-1e-12&&h.push(t),n>1e-12&&r<-1e-12||n<-1e-12&&r>1e-12){const e=n/(n-r);h.push([t[0]+(i[0]-t[0])*e,t[1]+(i[1]-t[1])*e])}}h.length>=3&&Math.abs(Wo(h))>n*n&&s.push({segmentIndex:t.index,wedge:h})}}return s}function Eo(e,t=1e-6){const i=(e||[]).filter(e=>e&&Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2&&e.a.every(Number.isFinite)&&e.b.every(Number.isFinite)&&Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>1e-9);if(i.length<2)return[];const n=Math.max(Number.isFinite(t)?t:0,1e-9),r=i.flatMap(e=>[e.a,e.b]).map(e=>[e[0],e[1]]).sort((e,t)=>e[0]-t[0]||e[1]-t[1]),o=[];for(const e of r)o.some(t=>Ao(t,e,n))||o.push(e);const s=[],a=i.map((e,t)=>({roomId:"",a:[e.a[0],e.a[1]],b:[e.b[0],e.b[1]],key:`join-${t}`,kind:"outer",cm:0,open:!1,half:e.halfDepth})),l=ts(a,n);for(const e of is(l).fans)s.push(e);const c=e=>!!ns(l,e);for(const e of o){if(c(e))continue;const t=[];for(const r of i)Ao(e,r.a,n)?Oo(t,r.b[0]-r.a[0],r.b[1]-r.a[1],r.halfDepth):Ao(e,r.b,n)?Oo(t,r.a[0]-r.b[0],r.a[1]-r.b[1],r.halfDepth):Po(e,r,n)&&(Oo(t,r.a[0]-e[0],r.a[1]-e[1],r.halfDepth),Oo(t,r.b[0]-e[0],r.b[1]-e[1],r.halfDepth));if(!(t.length<2)){t.sort((e,t)=>Math.atan2(e.u[1],e.u[0])-Math.atan2(t.u[1],t.u[0])||e.halfDepth-t.halfDepth);for(let i=0;in*n&&s.push(_)}}}return s}function Ho(e,t,i,n){if(!(t>0)||!e||e.length<2)return"";if(i&&e.length>=3){let i=e;const r=e[e.length-1];if(e.length>=4&&Math.hypot(e[0][0]-r[0],e[0][1]-r[1])<1e-9&&(i=e.slice(0,-1)),i.length>=3){const e=i.map((e,i)=>n?.[i]||t),r=As(i,e),o=Go(i,e);if(r&&o)return`${Ko(r)} ${Ko(Yo(o))}`}}const r=[];for(let i=0;i=1e-9&&a>0&&r.push({a:o,b:s,halfDepth:a})}const o=r.map(zo);for(const{segmentIndex:e,wedge:t}of Io(r)){const i=o[e];if(!i)continue;const n=Fo(i,t);n&&(o[e]=n)}const s=[...o.filter(e=>!!e),...Eo(r)],a=function(e){let t=null;try{for(const i of e){if(i.length<3)continue;const e=ps(i);t=t?$t(t,e):[e]}return t}catch{return null}}(s);return a?Ss(a):s.map(e=>Ko(e)).join(" ")}function No(e,t,i,n,r=1){if(!i.length)return!1;const o=Lo(n,r),s=(e[0]+t[0])/2,a=(e[1]+t[1])/2,[l,c]=uo(e,t);for(const e of i){const[t,i]=uo([e[0],e[1]],[e[2],e[3]]);if(!(Math.abs(l*i-c*t)>.05)&&hs(s,a,e[0],e[1],e[2],e[3])<=o)return!0}return!1}function Lo(e,t){return Math.max(e*(t>0?t:1)*.04,1e-9)}function Wo(e){let t=0;for(let i=0;ie[1]!=l>e[1]&&e[0]<(a-o)*(e[1]-s)/(l-s+0)+o&&(i=!i)}return i}([c[0]+.001*a,c[1]+.001*l],e)||(a=-a,l=-l),[a,l]}function Bo(e,t){const i=e[0]*t[1]-e[1]*t[0],n=e[0]*t[0]+e[1]*t[1];return Math.abs(i)<1e-9&&n>0}function jo(e,t,i,n){const r=t[0]*n[1]-t[1]*n[0];if(Math.abs(r)<1e-12)return null;const o=[i[0]-e[0],i[1]-e[1]],s=(o[0]*n[1]-o[1]*n[0])/r;return[e[0]+s*t[0],e[1]+s*t[1]]}function Uo(e,t,i){const n=e?.length||0;if(n<3||!Array.isArray(i)||i.length!==n)return 0;const r=(Math.trunc(t)%n+n)%n,o=e[r],s=e[(r+1)%n],a=s[0]-o[0],l=s[1]-o[1],c=Math.hypot(a,l);if(!(c>0))return 0;const h=Math.max(0,Number(i[r])||0);if(!(h>0))return c;const d=[a/c,l/c],u=qo(e,r),p=[o[0]+u[0]*h,o[1]+u[1]*h],m=t=>{const r=Math.max(0,Number(i[t])||0);if(!(r>0))return null;const s=e[t],a=e[(t+1)%n],l=a[0]-s[0],c=a[1]-s[1],h=Math.hypot(l,c);if(!(h>0))return null;const u=qo(e,t),m=jo(p,d,[s[0]+u[0]*r,s[1]+u[1]*r],[l/h,c/h]);return m?(m[0]-o[0])*d[0]+(m[1]-o[1])*d[1]:null},_=m((r-1+n)%n)??0,f=(m((r+1)%n)??c)-_;return f>0?f:0}function Vo(e,t,i,n,r,o,s,a=1){const l=(e||[]).find(e=>e?.id===t),c=Et(l);if(!c||c.length<3)return null;const h=os(e,t,i,n,r,o,s,a);if(!h)return c.map(()=>0);const d=4*Lo(r,a);return c.map((e,t)=>{const i=c[(t+1)%c.length],n=[(e[0]+i[0])/2,(e[1]+i[1])/2];for(let e=0;e!(e>0)))return e.map(e=>[e[0],e[1]]);const r=[];for(let o=0;o0||u>0)){r.push([e[o][0],e[o][1]]);continue}if(d>0!=u>0){const t=e[o],i=d>0?[t[0]+p*d,t[1]+m*d]:[t[0],t[1]],n=u>0?[t[0]+_*u,t[1]+f*u]:[t[0],t[1]];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}if(Bo(w,k)){const t=e[o],i=[t[0]+p*d,t[1]+m*d],n=[t[0]+_*u,t[1]+f*u];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}const S=jo(x,w,$,k),M=Math.max(d,u,1e-9),R=ns(i,e[o])?.limit??4*M;if(S){const i=Math.hypot(S[0]-e[o][0],S[1]-e[o][1]);if(Number.isFinite(i)&&i<=R){r.push(S);continue}if(Rs(e,t,o)){r.push(S);continue}}d>0&&r.push([e[o][0]+p*d,e[o][1]+m*d]),u>0&&r.push([e[o][0]+_*u,e[o][1]+f*u]),d>0||u>0||r.push([e[o][0],e[o][1]])}return r.length>=3?r:null}function Ko(e,t=!0){if(!e.length)return"";let i=`M ${e[0][0]} ${e[0][1]}`;for(let t=1;te?.id===t),a=Et(s);if(!a||a.length<3)return null;const l=Lo(n,r),c=[];for(const i of e||[]){if(!i||i.id===t)continue;const e=Et(i);if(e)for(const t of cn(a,e,l))c.push([t[0],t[1]],[t[2],t[3]])}for(const e of i||[])c.push([e[0],e[1]],[e[2],e[3]]);for(const e of o||[]){const t=_o(e,r);t&&c.push(t[0],t[1])}const h=[],d=[];for(let e=0;el)continue;const s=((e[0]-t[0])*(i[0]-t[0])+(e[1]-t[1])*(i[1]-t[1]))/(n*n);s<=r||s>=1-r||(o.some(e=>Math.abs(e-s)*n<=2*l)||o.push(s))}o.sort((e,t)=>e-t);for(const n of o)h.push([t[0]+(i[0]-t[0])*n,t[1]+(i[1]-t[1])*n]),d.push(e)}return{poly:h,parent:d,orig:a}}function Zo(e,t,i,n){const r=t.poly.length,o=new Array(r).fill(0);if(!e?.length)return o;const s=new Set,a=[];for(let l=0;l0?(o[l]=oo(c.cm),s.add(c.key)):a.push(l)}if(!a.length)return o;const l=n>0?n:1,c=Math.max(.5*i,1e-9)*l,h=go(e,l).filter(e=>e.w.cm>0);for(let e=a.length-1;e>=0;e--){const i=a[e],n=t.poly[i],s=t.poly[(i+1)%r],d=vo(n,s);let u=null;for(const e of h){const t=_o(e.w,l);if(!t||!yo(vo(t[0],t[1]),d))continue;if(hs(n[0],n[1],t[0][0],t[0][1],t[1][0],t[1][1])>c||hs(s[0],s[1],t[0][0],t[0][1],t[1][0],t[1][1])>c)continue;const i=Math.hypot(s[0]-n[0],s[1]-n[1]),r=Math.hypot(t[1][0]-t[0][0],t[1][1]-t[0][1]),o=Math.max(0,r-i);(!u||oc||hs(r[0],r[1],t[0][0],t[0][1],t[1][0],t[1][1])>c)continue;i=!0,o=Math.max(0,Math.hypot(t[1][0]-t[0][0],t[1][1]-t[0][1])-m)}else{if(hs(e.x,e.y,n[0],n[1],r[0],r[1])>c)continue;o=Math.hypot(e.x-d,e.y-u)}(!p||i&&!p.exact||i===p.exact&&o0?i:1,o=(e||[]).filter(e=>e&&!e.open&&null!==e.kind&&Number.isFinite(e.half)&&e.half>0&&Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2&&[e.a[0],e.a[1],e.b[0],e.b[1]].every(Number.isFinite)&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>n).sort((e,t)=>e.key.localeCompare(t.key)||e.a[0]-t.a[0]||e.a[1]-t.a[1]||e.b[0]-t.b[0]||e.b[1]-t.b[1]||e.half-t.half),s=new Map;for(const e of o){const t=s.get(e.key);t?e.half>t.half&&s.set(e.key,{...t,half:e.half}):s.set(e.key,e)}const a=[...s.values()].flatMap(e=>[{point:[e.a[0],e.a[1]],other:e.b,halfDepth:e.half,kind:e.kind,key:e.key},{point:[e.b[0],e.b[1]],other:e.a,halfDepth:e.half,kind:e.kind,key:e.key}]).sort((e,t)=>e.point[0]-t.point[0]||e.point[1]-t.point[1]||e.other[0]-t.other[0]||e.other[1]-t.other[1]||e.halfDepth-t.halfDepth),l=new Map;for(const e of a){const[t,i]=Jo(e.point,n),r=Qo(t,i),o=l.get(r)||[];o.push(e),l.set(r,o)}const c=[],h=new Map;for(const e of a){const t=es(h,e.point,n).filter(t=>Math.hypot(t.point[0]-e.point[0],t.point[1]-e.point[1])<=n).sort((t,i)=>Math.hypot(t.point[0]-e.point[0],t.point[1]-e.point[1])-Math.hypot(i.point[0]-e.point[0],i.point[1]-e.point[1])||t.point[0]-i.point[0]||t.point[1]-i.point[1]);let i=t[0];if(!i){i={point:[...e.point],rays:[]},c.push(i);const[t,r]=Jo(i.point,n),o=Qo(t,r),s=h.get(o)||[];s.push(i),h.set(o,s)}const r=e.other[0]-e.point[0],o=e.other[1]-e.point[1],s=Math.hypot(r,o);if(!(s>n))continue;const a=[r/s,o/s];let l=Math.atan2(a[1],a[0]);l<0&&(l+=2*Math.PI),i.rays.push({u:a,halfDepth:e.halfDepth,length:s,angle:l})}const d=[],u=e=>{const t=1e-9*Math.max(1,r),i=e.filter(e=>Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Number.isFinite(e.length)&&e.length>n);return i.filter((e,n)=>!i.some((i,r)=>r!==n&&i.halfDepth>=e.halfDepth-t&&i.length>=e.length-t&&(i.halfDepth>e.halfDepth+t||i.length>e.length+t||re.length-t.length||e.halfDepth-t.halfDepth).map(e=>({...e}))};for(const e of c){const t=e.rays.sort((e,t)=>e.angle-t.angle||e.length-t.length||e.halfDepth-t.halfDepth),i=[];for(const e of t){const t=i[i.length-1];t&&Math.abs(e.angle-t.angle)<=1e-9?t.supports.push({halfDepth:e.halfDepth,length:e.length}):i.push({u:[...e.u],angle:e.angle,supports:[{halfDepth:e.halfDepth,length:e.length}]})}if(i.length>1&&2*Math.PI-i[i.length-1].angle+i[0].angle<=1e-9){const e=i.pop();i[0].supports.push(...e.supports)}if(i.length<3)continue;const r=i.map(t=>{const i=u(t.supports),r=new Set,o=[];for(const s of i){const i=[e.point[0]+t.u[0]*s.length,e.point[1]+t.u[1]*s.length];for(const t of es(l,i,n)){if("shared"!==t.kind||Math.hypot(t.point[0]-i[0],t.point[1]-i[1])>n)continue;const s=t.other[0]-t.point[0],a=t.other[1]-t.point[1],l=Math.hypot(s,a);if(!(l>n))continue;const c=s/l,h=a/l;if(Math.hypot(t.other[0]-e.point[0],t.other[1]-e.point[1])<=n)continue;const d=`${t.key}|${t.point[0]}|${t.point[1]}|${t.other[0]}|${t.other[1]}|${t.halfDepth}`;r.has(d)||(r.add(d),o.push({start:[t.point[0],t.point[1]],u:[c,h],length:l,halfDepth:t.halfDepth}))}}return o.sort((e,t)=>e.start[0]-t.start[0]||e.start[1]-t.start[1]||e.u[0]-t.u[0]||e.u[1]-t.u[1]||e.length-t.length||e.halfDepth-t.halfDepth),{u:[...t.u],halfDepth:Math.max(...i.map(e=>e.halfDepth)),length:Math.max(...i.map(e=>e.length)),supports:i,continuations:o}}).filter(e=>Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Number.isFinite(e.length)&&e.length>n);if(r.length<3)continue;const o=Math.max(...r.map(e=>e.halfDepth));o>0&&Number.isFinite(o)&&d.push({point:[...e.point],rays:r,halfDepth:o,limit:1.25*o})}d.sort((e,t)=>e.point[0]-t.point[0]||e.point[1]-t.point[1]);const p=new Map;for(const e of d){const[t,i]=Jo(e.point,n),r=Qo(t,i),o=p.get(r)||[];o.push(e),p.set(r,o)}return{epsilon:n,coordinateScale:r,nodes:d,index:p}}function is(e){const t={fans:[],supports:[]};if(!e?.nodes?.length)return t;const i=Math.max(e.epsilon,1e-9)**2;for(const n of e.nodes){const e=n.rays.filter(e=>Number.isFinite(e.halfDepth)&&e.halfDepth>0).map(e=>({...e,thickLength:Math.max(...e.supports.filter(t=>t.halfDepth>=e.halfDepth-1e-12).map(e=>e.length),0),angle:(()=>{const t=Math.atan2(e.u[1],e.u[0]);return t<0?t+2*Math.PI:t})()})).sort((e,t)=>e.angle-t.angle);if(e.length<2)continue;const r=n.point;for(const i of e)for(const e of i.supports){if(!(e.halfDepth>0&&e.length>0))continue;const n=-i.u[1]*e.halfDepth,o=i.u[0]*e.halfDepth,s=[r[0]+i.u[0]*e.length,r[1]+i.u[1]*e.length];t.supports.push([[r[0]+n,r[1]+o],[s[0]+n,s[1]+o],[s[0]-n,s[1]-o],[r[0]-n,r[1]-o]])}for(let n=0;n{const e=s.angle-o.angle;return e>0?e:e+2*Math.PI})();if(a<1e-9)continue;const l=a>Math.PI+1e-9,c=4*Math.max(o.halfDepth,s.halfDepth),h=[r[0]-o.u[1]*o.halfDepth,r[1]+o.u[0]*o.halfDepth],d=[r[0]+s.u[1]*s.halfDepth,r[1]-s.u[0]*s.halfDepth],u=o.u[0]*s.u[1]-o.u[1]*s.u[0],p=e=>{let t=Math.atan2(e[1]-r[1],e[0]-r[0])-o.angle;for(;t<0;)t+=2*Math.PI;return t<=a+1e-9};let m=null;if(Math.abs(u)>1e-9){const e=((d[0]-h[0])*s.u[1]-(d[1]-h[1])*s.u[0])/u,t=((d[0]-h[0])*o.u[1]-(d[1]-h[1])*o.u[0])/u,i=[h[0]+o.u[0]*e,h[1]+o.u[1]*e];(l?e<=1e-9&&t<=1e-9:e>1e-9&&e<=o.thickLength&&t<=s.thickLength)&&Math.hypot(i[0]-r[0],i[1]-r[1])<=c&&p(i)&&(m=i)}const _=e=>{Math.abs(Wo(e))>i&&t.fans.push(e)};if(m){const e=1.5*Math.max(o.halfDepth,s.halfDepth);_(eo([r[0],r[1]],h,m,d,e)??[[r[0],r[1]],h,m,d]);continue}if(l){_([[r[0],r[1]],h,d]);continue}const f=(e,t)=>Math.min(t,Math.sqrt(Math.max(c**2-e**2,0)),2*Math.max(o.halfDepth,s.halfDepth)),g=[h[0]+o.u[0]*f(o.halfDepth,o.thickLength),h[1]+o.u[1]*f(o.halfDepth,o.thickLength)],v=[d[0]+s.u[0]*f(s.halfDepth,s.thickLength),d[1]+s.u[1]*f(s.halfDepth,s.thickLength)];_([[r[0],r[1]],h,g,v,d])}}return t}function ns(e,t){return!e||!Array.isArray(t)||t.length<2||!t.slice(0,2).every(Number.isFinite)?null:es(e.index,t,e.epsilon).filter(i=>Math.hypot(i.point[0]-t[0],i.point[1]-t[1])<=e.epsilon).sort((e,i)=>Math.hypot(e.point[0]-t[0],e.point[1]-t[1])-Math.hypot(i.point[0]-t[0],i.point[1]-t[1])||e.point[0]-i.point[0]||e.point[1]-i.point[1])[0]||null}function rs(e,t,i,n,r,o,s){return ts(ss(e,t,i,n,r,o,s),4*Lo(n,s),s)}function os(e,t,i,n,r,o,s,a=1){const l=Xo(e,t,n,r,a,i);if(!l)return null;const c=function(e,t,i){const n=(e||[]).find(e=>e?.id===t),r=Et(n);if(!r)return[];const o=[];for(const n of e||[]){if(!n||n.id===t)continue;const e=Et(n);if(e)for(const t of cn(r,e,i))o.push(t)}return o}(e,t,Lo(r,a)),h=function(e,t,i,n,r){const o=Lo(n,r),s=[];for(let a=0;ahs(h,d,e[0],e[1],e[2],e[3])<=o);s.push(u?"shared":"outer")}return s}(l.poly,c,n,r,a),d=Zo(i,l,r,a),u=d.map((e,t)=>h[t]&&e>0?ao(e,o,s)/2:0);return{...l,kinds:h,cms:d,offsets:u}}function ss(e,t,i,n,r,o,s=1){const a=[];for(const l of e||[]){if(!l?.id)continue;const c=os(e,l.id,t,i,n,r,o,s);if(c)for(let e=0;e0&&(a=To(a,e.a,e.b,e.cm,n,s));return a}function ls(e,t,i,n,r,o,s=1){if(!t?.length)return[];const a=ss(e,t,i,n,r,o,s),l=new Map;for(const e of a){if(e.open||!e.kind||!e.roomId)continue;const t=l.get(e.key)||new Set;t.add(e.roomId),l.set(e.key,t)}const c=e=>{const t=[...l.get(e)||[]].sort();return 1!==t.length&&2!==t.length?`ambiguous:${e}`:`${1===t.length?"outer":"shared"}:${t.join("|")}`},h=[],d=new Set;for(const e of a)!e.open&&e.cm>0&&!d.has(e.key)&&(d.add(e.key),h.push({...e,ownerSignature:c(e.key)}));const u=[];for(const a of e||[]){if(!a?.id)continue;const l=os(e,a.id,t,i,n,r,o,s);if(l)for(let e=0;e0)||null===l.kinds[i]){e++;continue}const o=c(mo(l.poly[i],l.poly[(i+1)%l.poly.length],n,s));let a=e;for(;a+10&&u.push({a:[d[0],d[1]],b:[p[0],p[1]],key:mo(d,p,n,s),cm:r,len:m,ownerSignature:o}),e=a+1}}}u.sort((e,t)=>t.len-e.len||e.key.localeCompare(t.key));const p=[],m=new Set,_=new Set,f=4*Lo(n,s);for(const e of u){const t=h.filter(t=>!_.has(t.key)&&t.cm===e.cm&&t.ownerSignature===e.ownerSignature&&yo(vo(t.a,t.b),vo(e.a,e.b))&&hs(t.a[0],t.a[1],e.a[0],e.a[1],e.b[0],e.b[1])<=f&&hs(t.b[0],t.b[1],e.a[0],e.a[1],e.b[0],e.b[1])<=f);if(t.length){for(const e of t)_.add(e.key);m.has(e.key)||(m.add(e.key),p.push(fo(e.a,e.b,e.cm,n,s)))}}for(const e of h)_.has(e.key)||m.has(e.key)||(m.add(e.key),p.push(fo(e.a,e.b,e.cm,n,s)));return p}function cs(e,t,i,n,r,o,s,a=1){const l=Lo(r,a),c=(n[0]+n[2])/2,h=(n[1]+n[3])/2,d=vo([n[0],n[1]],[n[2],n[3]]);let u=null;for(const n of ss(e,t,i,r,o,s,a)){if(!yo(vo(n.a,n.b),d))continue;const e=hs(c,h,n.a[0],n.a[1],n.b[0],n.b[1]);e>4*l||(!u||e[e[0],e[1]]),o=Math.abs(Wo(r));r.length>=3&&o>i&&(t=r,i=o)}return t}function us(e,t,i,n,r,o,s,a=1,l,c){const h=(e||[]).find(e=>e?.id===t),d=Et(h);if(!d||d.length<3)return null;if(!i?.length)return d.map(e=>[e[0],e[1]]);const u=os(e,t,i,n,r,o,s,a);if(!u||!u.offsets.some(e=>e>0))return d.map(e=>[e[0],e[1]]);const p=c||rs(e,i,n,r,o,s,a),m=Go(u.poly,u.offsets,p);if(!m)return d.map(e=>[e[0],e[1]]);if(!p.nodes.length)return m;const _=void 0===l?Ts(e,i,n,[],r,o,s,a):null,f=l??("ok"===_?.status||"degraded-extra"===_?.status?_.roomGeom:void 0);if(f)try{const e=ds(Mt(ps(u.poly),f));if(e)return e}catch{}return function(e,t){try{return ds(St(ps(e),ps(t)))}catch{return null}}(m,u.poly)||d.map(e=>[e[0],e[1]])}function ps(e){const t=e.map(e=>[e[0],e[1]]);return t.push([e[0][0],e[0][1]]),[t]}function ms(e){return!(!Array.isArray(e)||!e.length)&&e.every(e=>Array.isArray(e)&&e.length&&e.every(e=>Array.isArray(e)&&e.length>=4&&e.every(e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1])))&&Math.abs(li(e[0]))>1e-9)}function _s(e,t,i,n){const r=n?new Set(n):null;let o=null;for(let n=0;nt.epsilon))continue;const l=bs([[e.point[0]+a[0]*n.halfDepth,e.point[1]+a[1]*n.halfDepth],[e.point[0]+s.u[0]*r+a[0]*n.halfDepth,e.point[1]+s.u[1]*r+a[1]*n.halfDepth],[e.point[0]+s.u[0]*r-a[0]*n.halfDepth,e.point[1]+s.u[1]*r-a[1]*n.halfDepth],[e.point[0]-a[0]*n.halfDepth,e.point[1]-a[1]*n.halfDepth]],t.coordinateScale);if(!l)continue;const c=ps(l);o=o?$t(o,c):c}}return o}function fs(e,t,i){let n=null;for(const r of e.rays)for(const e of r.continuations){const r=[-e.u[1],e.u[0]],o=[e.start[0]+e.u[0]*e.length,e.start[1]+e.u[1]*e.length],s=bs([[e.start[0]+r[0]*e.halfDepth,e.start[1]+r[1]*e.halfDepth],[o[0]+r[0]*e.halfDepth,o[1]+r[1]*e.halfDepth],[o[0]-r[0]*e.halfDepth,o[1]-r[1]*e.halfDepth],[e.start[0]-r[0]*e.halfDepth,e.start[1]-r[1]*e.halfDepth]],t.coordinateScale);if(!s)continue;const a=St(ps(s),i);Array.isArray(a)&&0!==a.length&&(n=n?$t(n,a):a)}return n}function gs(e,t,i=2*(4*e.halfDepth+2*t.epsilon)){const n=function(e,t=to){const i=new Set,n=Number.isFinite(t)&&t>=0?t:to;for(let t=0;te-t)}(e);return n.length?_s(e,t,i,n):null}function vs(e,t,i,n,r){const o=function(e){let t=null;for(const i of e){const e=ps(i);try{t=t?$t(t,e):e}catch{}}return t}(function(e,t,i=!1){if(!e)return[];const n=[];for(const r of e.nodes)for(let o=0;o1e-9)||h>=Math.PI-1e-9)continue;const d=[-s.u[1],s.u[0]],u=[-a.u[1],a.u[0]],p=[r.point[0]+d[0]*s.halfDepth,r.point[1]+d[1]*s.halfDepth],m=[r.point[0]-u[0]*a.halfDepth,r.point[1]-u[1]*a.halfDepth],_=jo(p,s.u,m,a.u);if(!_)continue;const f=Math.hypot(_[0]-r.point[0],_[1]-r.point[1]);if(!Number.isFinite(f)||f<=r.limit)continue;const g=t?Math.sqrt(Math.max(0,r.limit*r.limit-s.halfDepth*s.halfDepth)):0,v=t?Math.sqrt(Math.max(0,r.limit*r.limit-a.halfDepth*a.halfDepth)):0,y=bs([[p[0]+s.u[0]*g,p[1]+s.u[1]*g],[m[0]+a.u[0]*v,m[1]+a.u[1]*v],_],e.coordinateScale);if(y&&n.push(y),i){const t=_[0]-r.point[0],i=_[1]-r.point[1],o=Math.hypot(t,i),s=f-r.limit;if(o>e.epsilon&&s>e.epsilon){const a=Math.min(Math.max(8*e.epsilon,.05*r.halfDepth),.25*s),l=t/o,c=i/o,h=-c,d=l,u=bs([[_[0]-l*a+h*a,_[1]-c*a+d*a],[_[0]-l*a-h*a,_[1]-c*a-d*a],[_[0]+l*a,_[1]+c*a]],e.coordinateScale);u&&n.push(u)}}}return n}({...t,nodes:[e]},i,n));return o&&r?Mt(o,r):o}function ys(e,t,i,n){if(!e||!t.nodes.length)return e;let r=null;try{r=function(e){let t=null;for(const i of e.nodes){const n=gs(i,e);n&&(t=t?$t(t,n):n)}return t}(t)}catch{return e}let o=e;for(const e of t.nodes){const s=4*e.halfDepth+2*t.epsilon,a=2*s,l=[[e.point[0]-s,e.point[1]-s],[e.point[0]+s,e.point[1]-s],[e.point[0]+s,e.point[1]+s],[e.point[0]-s,e.point[1]+s]];try{let s=o;const c=vs(e,t,!1,!0,r);c&&(s=Mt(s,c));let h=_s(e,t,a);const d=vs(e,t,!0,!0,r);d&&(h=Mt(h,d)),r&&(h=$t(h,r));const u=.02*Math.min(...e.rays.map(e=>e.halfDepth));if(h=$t(h,ps([[e.point[0]-u,e.point[1]-u],[e.point[0]+u,e.point[1]-u],[e.point[0]+u,e.point[1]+u],[e.point[0]-u,e.point[1]+u]])),!h)continue;let p=St(h,ps(l));n?p=St(p,n):i&&(p=St(p,i));const m=ps(l),_=Mt(s,m),f=i?Mt(St(s,m),i):null,g=fs(e,t,m);o=$t(_,...f?[f]:[],...g?[g]:[],p)}catch{}}if(r)try{let e=r;n?e=St(e,n):i&&(e=St(e,i)),o=$t(o,e)}catch{}return o}function bs(e,t=1){if(!Array.isArray(e)||e.length<3)return null;const i=Number.isFinite(t)&&t>0?t:1,n=1e-12*Math.max(1,i),r=[];for(const t of e){if(!Array.isArray(t)||t.length<2)return null;const e=Number(t[0]),i=Number(t[1]);if(!Number.isFinite(e)||!Number.isFinite(i))return null;const o=Math.round(e/n)*n,s=Math.round(i/n)*n;if(!Number.isFinite(o)||!Number.isFinite(s))return null;r.push([Object.is(o,-0)?0:o,Object.is(s,-0)?0:s])}return Math.abs(Wo(r))>n*n?r:null}function ws(e,t,i,n){if(hs(e[0],e[1],t[0],t[1],i[0],i[1])>n)return!1;const r=i[0]-t[0],o=i[1]-t[1],s=(e[0]-t[0])*r+(e[1]-t[1])*o,a=r*r+o*o,l=n*Math.sqrt(a);return s>=-l&&s<=a+l}function ks(e,t,i){const n=[],r=[];for(let o=0;oi*i))continue;const d=i/Math.sqrt(h),u=[0,1];for(const e of t)for(const t of[e.a,e.b]){if(!ws(t,s,a,i))continue;const e=((t[0]-s[0])*l+(t[1]-s[1])*c)/h;e>d&&e<1-d&&u.push(e)}u.sort((e,t)=>e-t);const p=u.filter((e,t)=>0===t||Math.abs(e-u[t-1])>d);for(let e=0;e=3&&r.length===n.length?{poly:n,offsets:r}:null}function xs(e,t,i,n,r,o,s,a){const l=(e||[]).map(Et).filter(e=>!!e&&e.length>=3);if(!l.length)return null;let c=$t(ps(l[0]));for(let e=1;e"outer"===e.kind&&e.half>0),u=4*Lo(n,s),p=a||ts(h,u,s);let m=null;for(const e of function(e){const t=[];for(const i of Array.isArray(e)?e:[])if(Array.isArray(i))for(const e of i){if(!Array.isArray(e)||e.length<4)continue;const i=e.slice(0,-1).map(e=>[e[0],e[1]]);i.length>=3&&t.push(i)}return t}(c)){const t=ks(e,d,u);if(!t||!t.offsets.some(e=>e>0))continue;const i=As(t.poly,t.offsets,p),n=Go(t.poly,t.offsets,p);if(!i||!n)continue;const r=Mt(ps(i),ps(n));m=m?$t(m,r):r}return{centre:c,shell:m||[]}}function $s(e,t,i,n,r,o,s=1){try{const a=xs(e,t,i,n,r,o,s,rs(e,t,i,n,r,o,s));if(!a)return[];return a.shell?.length?$t(a.centre,a.shell):a.centre}catch{return null}}function Ss(e){if(!e)return"";let t="";for(const i of e)if(Array.isArray(i))for(const e of i){if(!Array.isArray(e)||e.length<4)continue;const i=e.slice(0,e.length-1);i.length<3||(t+=(t?" ":"")+Ko(i.map(e=>[e[0],e[1]])))}return t}const Ms=15;function Rs(e,t,i){const n=e?.length||0;if(n<3||t?.length!==n)return!1;const r=e[(i-1+n)%n],o=e[i],s=e[(i+1)%n],a=[r[0]-o[0],r[1]-o[1]],l=[s[0]-o[0],s[1]-o[1]],c=Math.hypot(a[0],a[1]),h=Math.hypot(l[0],l[1]);if(!(c>1e-9&&h>1e-9))return!1;const d=Math.max(-1,Math.min(1,(a[0]*l[0]+a[1]*l[1])/(c*h))),u=Math.acos(d);if(!(u>1e-9))return!1;if(u>=Ms*Math.PI/180)return!1;const p=Math.max(0,t[(i-1+n)%n]),m=Math.max(0,t[i]);if(!(p>0||m>0))return!1;const _=Math.sin(u);if(!(_>1e-12))return!1;const f=(m+p*d)/_,g=(p+m*d)/_;return Number.isFinite(f)&&Number.isFinite(g)&&f>0&&g>0&&fe>0))continue;for(const e of l.offsets)e>0&&(u=Math.max(u,2*e));const c=As(l.poly,l.offsets,d),p=Go(l.poly,l.offsets,d);c&&h.push({outset:c,inset:p})}for(const e of l){const t=e.map(e=>e[0]),i=e.map(e=>e[1]);if(t.length){const n=Math.min(Math.max(...t)-Math.min(...t),Math.max(...i)-Math.min(...i)),r=Math.min(...e.map((t,i)=>{const n=e[(i+1)%e.length];return Math.hypot(n[0]-t[0],n[1]-t[1])}));u=Math.max(u,e.length>16?n:r)}}const p=function(e,t,i,n,r,o,s,a){if(!t?.length||!i?.length)return[];const l=4*Lo(n,s),c=new Map;for(const a of ss(e,t,i,n,r,o,s))!a.open&&a.half>0&&!c.has(a.key)&&c.set(a.key,a);const h=[...c.values()];if(h.length<2)return[];const d=a||ts(h,l),u=[];for(const e of i)for(const t of[[e[0],e[1]],[e[2],e[3]]])u.some(e=>Math.hypot(e[0]-t[0],e[1]-t[1])<=l)||u.push(t);const p=[],m=(e,t)=>{let i=0,n=0;if(Math.hypot(e.a[0]-t[0],e.a[1]-t[1])<=l)i=e.b[0]-e.a[0],n=e.b[1]-e.a[1];else{if(!(Math.hypot(e.b[0]-t[0],e.b[1]-t[1])<=l))return null;i=e.a[0]-e.b[0],n=e.a[1]-e.b[1]}const r=Math.hypot(i,n);return r>l?[i/r,n/r]:null};for(const e of u){const t=h.map(t=>({iv:t,u:m(t,e)})).filter(e=>!!e.u);for(let i=0;i0?[e.slice(),u,_,m]:[e.slice(),m,_,u];else{if(!g)continue;{const t=[-r.u[1],r.u[0]],i=[-o.u[1],o.u[0]],n=s<0?1:-1,a=[e[0]+t[0]*r.iv.half*n,e[1]+t[1]*r.iv.half*n],l=[e[0]-i[0]*o.iv.half*n,e[1]-i[1]*o.iv.half*n];y=s>0?[e.slice(),a,l]:[e.slice(),l,a]}}Math.abs(Wo(y))>l*l&&p.push(y)}}return p}(e,t,i,r,o,s,a,d),m=n.length?Ps(e,t,i,r,o,s,a):null;let _="exterior";try{const v=xs(e,t,i,r,o,s,a,d);_="paper";const y=v?v.shell?.length?$t(v.centre,v.shell):v.centre:[],b=e=>{const t=ps(e.outset);if(!e.inset)return t;const i=ps(e.inset);return Mt(t,i)};_="room-rings";let w=null;for(const e of h)try{const t=b(e);w=w?$t(w,t):t}catch{}if(_="edge-bodies",v)for(const n of zs(e,t,i,r,o,s,a))try{const e=St(ps(n.quad),v.centre);w=w?$t(w,e):e}catch{}_="junctions",w=function(e,t,i=1,n=$t){let r=e;for(const e of t||[]){const t=bs(e,i);if(t)try{const e=ps(t);r=r?n(r,e):e}catch{}}return r}(w,p,a),_="facade-clip",w&&v&&(w=St(w,v.centre)),_="exterior-shell";const k=[];let x=0;if(v?.shell?.length)if(w)try{const e=$t(w,v.shell);if(!ms(e))throw new Error("invalid shell union");w=e}catch{if(!ms(w)||!ms(v.shell))throw new Error("invalid shell");k.push({id:"exterior-shell",geom:v.shell}),x++}else w=v.shell;if(_="multi-wall-trim",w&&d.nodes.length){const e=e=>e.rays.some(e=>e.supports.some(e=>e.length<2*e.halfDepth)),t=d.nodes.filter(e);if(t.length){w=ys(w,{...d,nodes:t},v?.centre,y)}}if(_="junction-corners",d.nodes.length){const n=is(d),l=function(e,t,i,n,r,o,s,a){try{const l=xs(e,t,i,n,r,o,s,{epsilon:a.epsilon,coordinateScale:a.coordinateScale,nodes:[],index:new Map});return l?l.shell?.length?$t(l.centre,l.shell):l.centre:null}catch{return null}}(e,t,i,r,o,s,a,d);for(const e of n.fans)try{let t=[ps(e)];if(l&&(t=St(t,l)),!t?.length)continue;w=w?$t(w,t):t}catch{}f=w,g=Math.max(d.epsilon,1e-9)**2,w=Array.isArray(f)?f.map(e=>{if(!Array.isArray(e)||!e.length)return e;const[t,...i]=e;return Math.abs(Wo(t||[]))<=g?null:[t,...i.filter(e=>Math.abs(Wo(e||[]))>g)]}).filter(e=>!!e):f}const $=w||[],S=[...ms($)?[{id:"room-primary",geom:$}]:[],...k.map((e,t)=>({id:`room-isolated-${t}`,geom:e.geom}))];_="openings";for(const e of n){if(!(e.length>0))continue;const t=Is(m,e,!0);if(!t.negative&&!t.positive)continue;const i=e.angle*Math.PI/180,n=Math.cos(i),o=Math.sin(i),s=-o,l=n,c=e.length/2,h=1.25*Math.max(u,r*a),d=[[e.x-n*c-s*h,e.y-o*c-l*h],[e.x+n*c-s*h,e.y+o*c-l*h],[e.x+n*c+s*h,e.y+o*c+l*h],[e.x-n*c+s*h,e.y-o*c+l*h]];w&&(w=Mt(w,ps(d)));for(const e of k)e.geom=Mt(e.geom,ps(d))}_="extras";const M=[];let R=0;const T=c.mergeExtra||((e,t)=>e?$t(e,t):t);for(let e=0;ee.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]))||Math.abs(li(t))<=1e-9){R++;continue}const i=[ps(t)];try{const t=T(w,i,e);if(!ms(t))throw new Error("invalid extra union");w=t}catch{R++,ms(i)&&M.push({id:`extra-${e}`,geom:i})}}M.push(...k),M.sort((e,t)=>Ss(e.geom).localeCompare(Ss(t.geom)));const C=w||[];return{status:R||x?"degraded-extra":"ok",geom:C,components:[...ms(C)?[{id:"primary",geom:C}]:[],...M.map((e,t)=>({...e,id:`isolated-${t}`}))],roomGeom:$,roomComponents:S,paperGeom:y,depthUnits:u,openingPadUnits:1.25*Math.max(u,r*a),openingIndex:m,multiWallNodes:d,degradedExtraCount:R+x}}catch{return c.onCoreFailure?.(_),{status:"failed-core",geom:[],components:[],roomGeom:[],paperGeom:[],roomComponents:[],depthUnits:u,openingIndex:null,multiWallNodes:d,degradedExtraCount:0}}var f,g}function Cs(e,t,i,n=[],r,o,s,a=1,l=[],c={}){if(!t?.length&&!l.length)return null;return Ds(Ts(e,t,i,n,r,o,s,a,l,c))}function Ds(e){if("failed-core"===e.status||"not-applicable"===e.status)return null;const t=e.components.map(e=>({id:e.id,d:Ss(e.geom),fillRule:"evenodd"})).filter(e=>!!e.d),i=t[0]?.d||"",n=Ss(e.paperGeom);if(t.length){const r={status:e.status,d:i,paths:t,components:e.components,roomGeom:e.roomGeom,multiWallNodes:e.multiWallNodes,paperD:n,depthUnits:e.depthUnits,fillRule:"evenodd"};return Object.defineProperties(r,{roomComponents:{value:e.roomComponents||[],enumerable:!1},openingIndex:{value:e.openingIndex,enumerable:!1},openingPadUnits:{value:e.openingPadUnits,enumerable:!1}}),r}return null}function zs(e,t,i,n,r,o,s=1){if(!t?.length)return[];const a=new Set,l=[];for(const c of e||[]){if(!c?.id)continue;const h=os(e,c.id,t,i,n,r,o,s);if(!h)continue;const d=h.poly;for(let e=0;e0))continue;const p=mo(t,i,n,s);if(a.has(p))continue;a.add(p);const m=ao(u,r,o),[_,f]=qo(d,e),g=-_,v=-f,y=m/2,b=[[t[0]+g*y,t[1]+v*y],[i[0]+g*y,i[1]+v*y],[i[0]+_*y,i[1]+f*y],[t[0]+_*y,t[1]+f*y]];l.push({key:p,kind:c,cm:u,quad:b,a:[t[0],t[1]],b:[i[0],i[1]],depthUnits:m})}}return l}function As(e,t,i){const n=e?.length||0;if(n<3||t.length!==n)return null;if(t.every(e=>!(e>0)))return e.map(e=>[e[0],e[1]]);Yo(e),t.slice().reverse();const r=[];for(let o=0;o0||l>0)){r.push([e[o][0],e[o][1]]);continue}if(a>0!=l>0){const t=e[o],i=a>0?[t[0]-c*a,t[1]-h*a]:[t[0],t[1]],n=l>0?[t[0]-d*l,t[1]-u*l]:[t[0],t[1]];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}if(Bo(w,k)){const t=e[o],i=[t[0]-c*a,t[1]-h*a],n=[t[0]-d*l,t[1]-u*l];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}const S=jo(x,w,$,k),M=Math.max(a,l,1e-9),R=ns(i,e[o])?.limit??4*M;if(S){const i=Math.hypot(S[0]-e[o][0],S[1]-e[o][1]);if(Number.isFinite(i)&&i<=R){r.push(S);continue}if(Rs(e,t,o)){r.push([e[o][0],e[o][1]]);continue}}a>0&&r.push([e[o][0]-c*a,e[o][1]-h*a]),l>0&&r.push([e[o][0]-d*l,e[o][1]-u*l])}return r.length>=3?r:null}function Ps(e,t,i,n,r,o,s=1){const a=[];for(const l of e||[]){if(!l?.id)continue;const c=os(e,l.id,t,i,n,r,o,s);if(!c)continue;const h=Math.abs(li(c.poly));for(let e=0;e[Math.max(t,e.x0),Math.min(i,e.x1)]).filter(e=>e[1]-e[0]>n).sort((e,t)=>e[0]-t[0]||e[1]-t[1]);if(!r.length)return{coverage:0,full:!1};let o=r[0][0],s=r[0][1],a=0,l=o<=t+n;for(let e=1;e=i-n,{coverage:a,full:l}}function Fs(e,t){return Number(t.full)-Number(e.full)||e.faceDistance-t.faceDistance||e.area-t.area||e.roomId.localeCompare(t.roomId)}function Is(e,t,i=!1){const n=Number(t?.x),r=Number(t?.y),o=Number(t?.angle),s=Number(t?.length);if(!([n,r,o,s].every(Number.isFinite)&&s>0))return{negative:null,positive:null};const a=o*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=-c,d=l,u=s/2,p=Math.max(1e-9,e.adjacencyEps),m=new Map;let _=0;for(const t of e.edges){if(i&&!(t.half>0))continue;if(!xo(t.a,t.b,o))continue;const[e,s]=uo(t.a,t.b);if(Math.abs((n-t.a[0])*s-(r-t.a[1])*e)>p)continue;const a=(t.a[0]-n)*l+(t.a[1]-r)*c,f=(t.b[0]-n)*l+(t.b[1]-r)*c,g=Math.max(-u,Math.min(a,f)),v=Math.min(u,Math.max(a,f));if(v-g<=p)continue;const y=t.inward[0]*h+t.inward[1]*d>=0?1:-1,b=((t.a[0]+t.b[0])/2-n)*h+((t.a[1]+t.b[1])/2-r)*d,w=Math.abs(b+y*t.half),k=`${y}|${t.roomId}`,x={x0:g,x1:v,half:t.half,cm:t.cm,key:t.key,axis:[e,s]},$=m.get(k);$?($.pieces.push(x),$.faceDistance=Math.min($.faceDistance,w)):m.set(k,{roomId:t.roomId,side:y,order:_++,pieces:[x],faceDistance:w,area:t.area,coverage:0,full:!1})}for(const e of m.values()){const t=Os(e.pieces,-u,u,p);e.coverage=t.coverage,e.full=t.full}const f=e=>{const t=[...m.values()].filter(t=>t.side===e&&t.coverage>p);return t.sort(Fs),t[0]||null};return{negative:f(-1),positive:f(1)}}function Es(e,t){const i=Is(e,t),n=[i.negative,i.positive].filter(e=>!!e);if(!n.length)return{ox:0,oy:0,cm:0,side:-1};const r=i.negative&&i.positive?-1:n[0].side,o=t.flip_v?-r:r,s=(-1===o?i.negative:i.positive)||n[0],a=[...s.pieces].sort((e,t)=>(e.x0<=0&&e.x1>=0?0:Math.min(Math.abs(e.x0),Math.abs(e.x1)))-(t.x0<=0&&t.x1>=0?0:Math.min(Math.abs(t.x0),Math.abs(t.x1)))||t.x1-t.x0-(e.x1-e.x0)||e.key.localeCompare(t.key))[0];if(!(a.half>0&&a.cm>0))return{ox:0,oy:0,cm:0,side:o};const l=t.angle*Math.PI/180,c=-Math.sin(l),h=Math.cos(l);return{ox:c*o*a.half,oy:h*o*a.half,cm:a.cm,side:o}}function Hs(e,t){const i=1e-9,n=t.filter(e=>Number.isFinite(e.x0)&&Number.isFinite(e.x1)&&Number.isFinite(e.half)&&e.x1>e.x0&&e.half>0);if(!n.length)return"";const r=n.flatMap(e=>[e.x0,e.x1]).sort((e,t)=>e-t),o=[];for(const e of r){const t=o[o.length-1];(void 0===t||e>t+i)&&o.push(e)}const s=[];for(let e=0;e+1t+i))continue;const a=(t+r)/2,l=n.reduce((e,t)=>a>=t.x0-i&&a<=t.x1+i?Math.max(e,t.half):e,0);if(!(l>0))continue;const c=s[s.length-1];c&&t<=c.x1+i&&Math.abs(l-c.half)<=i?c.x1=r:s.push({x0:t,x1:r,half:l})}const a=[];for(const e of s){const t=a[a.length-1],n=t?.[t.length-1];n&&e.x0<=n.x1+i?(e.x0=n.x1,t.push(e)):a.push([e])}return a.map(t=>{const i=t[0],n=t[t.length-1],r=Math.min(.25*Math.min(...t.map(e=>e.half)),.75),o=-e*r,s=[];if(1===e){s.push(`M ${i.x0} ${o} L ${n.x1} ${o}`);for(let e=t.length-1;e>=0;e--){const i=t[e];s.push(`L ${i.x1} ${i.half} L ${i.x0} ${i.half}`)}}else{s.push(`M ${n.x1} ${o} L ${i.x0} ${o}`);for(const e of t)s.push(`L ${e.x0} ${-e.half} L ${e.x1} ${-e.half}`)}return s.push("Z"),s.join(" ")}).join(" ")}function Ns(e,t,i,n){if(!n)return i;const r=e.angle*Math.PI/180,o=Math.cos(r),s=Math.sin(r),a=[],l=1e-9;for(const r of i){const[i,c]=r.axis,h=o*i+s*c;if(Math.abs(h)<=l)continue;const d=e.x*i+e.y*c,u=d+h*r.x0,p=d+h*r.x1,m=Math.min(u,p),_=Math.max(u,p),f=`${r.key}|${t}`,g=n.get(f)||[];let v=[[m,_]];for(const[e,t]of g){const i=[];for(const[n,r]of v)t<=n+l||e>=r-l?i.push([n,r]):(e>n+l&&i.push([n,Math.min(r,e)]),te[0]-t[0]||e[1]-t[1]),b=[];for(const e of y){const t=b[b.length-1];t&&e[0]<=t[1]+l?t[1]=Math.max(t[1],e[1]):b.push([e[0],e[1]])}n.set(f,b)}return a}function Ls(e,t){const i=new Map;return t.map(t=>function(e,t,i){const n=Is(e,t,!0),r=n.negative,o=n.positive;if(!r&&!o)return null;let s;if(r&&o)s=[{candidate:r,side:-1},{candidate:o,side:1}];else{const e=r||o;s=[{candidate:e,side:-1},{candidate:e,side:1}]}const a=s.map(({candidate:e,side:n})=>({candidate:e,side:n,pieces:Ns(t,n,e.pieces,i)})),l=a.map(({candidate:e,side:t,pieces:i})=>({side:t,roomId:e.roomId,d:Hs(t,i)})),c=a.flatMap(({pieces:e})=>e);if(!c.length)return null;const h=Math.max(...c.map(e=>e.half));return{faces:l,minY:-h,maxY:h,wallKey:[...new Set(c.map(e=>e.key))].sort().join("|")}}(e,t,i))}const Ws=150,qs=1e-6;function Bs(e){return Number.isFinite(e)?Math.max(1,Math.min(Ws,e)):1}function js(e){return((Number.isFinite(Number(e))?Number(e):0)%90+90)%90}const Us=e=>{const t=e.map(e=>[e[0],e[1]]);return!t.length||t[0][0]===t[t.length-1][0]&&t[0][1]===t[t.length-1][1]||t.push([...t[0]]),[t]},Vs=(e,t)=>e[0]===t[0]&&e[1]===t[1];function Gs(e,t=1e-6){const i=Number.isFinite(t)&&t>0?t:qs,n=[];for(const t of e||[]){if(!Array.isArray(t)||t.length<2)return null;const e=Number(t[0]),r=Number(t[1]);if(!Number.isFinite(e)||!Number.isFinite(r))return null;const o=Math.round(e/i)*i,s=Math.round(r/i)*i;if(!Number.isFinite(o)||!Number.isFinite(s))return null;const a=[Object.is(o,-0)?0:o,Object.is(s,-0)?0:s];n.length&&Vs(n[n.length-1],a)||n.push(a)}return n.length>1&&Vs(n[0],n[n.length-1])&&n.pop(),n.length<3||new Set(n.map(e=>`${e[0]},${e[1]}`)).size<3?null:li(n)>i*i?n:null}function Ks(e){const t=[];for(const i of e||[])for(const e of i||[]){const i=(e||[]).filter(e=>Array.isArray(e)&&e.length>=2);i.length<4||t.push(`M ${i.slice(0,-1).map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`)}return t.join(" ")}function Ys(e,t,i,n,r){return zo({a:e,b:t,halfDepth:ao(i,n,r)/2})}function Xs(e,t,i){const n=Number(t)>0?Number(t):5,r=Bs(e.cm)/n*i,o=e.center[0],s=e.center[1];if("circle"===e.shape){const e=r/2;return Array.from({length:96},(t,i)=>{const n=i/96*Math.PI*2;return[o+Math.cos(n)*e,s+Math.sin(n)*e]})}const a=r/2,l=js(e.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l);return[[-a,-a],[a,-a],[a,a],[-a,a]].map(([e,t])=>[o+e*c-t*h,s+e*h+t*c])}function Zs(e,t){const i=Number.isFinite(t)?Math.max(0,Math.min(1,t)):0,n=(e.a[0]+e.b[0])/2,r=(e.a[1]+e.b[1])/2,o=(e.b[0]-e.a[0])*i/2,s=(e.b[1]-e.a[1])*i/2;return{...e,a:[n-o,r-s],b:[n+o,r+s]}}function Js(e,t,i=1e-9){if(!t.length)return[e];let n=[Us(e)];try{for(const e of t){const t=e.b[0]-e.a[0],r=e.b[1]-e.a[1],o=Math.hypot(t,r);if(!(o>i))continue;const s=t/o,a=r/o,l=-a,c=s,h=1.25*Math.max(Number(e.depth)||0,4*i),d=Math.max(2*i,1e-9*o),u=[[e.a[0]-s*d-l*h,e.a[1]-a*d-c*h],[e.b[0]+s*d-l*h,e.b[1]+a*d-c*h],[e.b[0]+s*d+l*h,e.b[1]+a*d+c*h],[e.a[0]-s*d+l*h,e.a[1]-a*d+c*h]];n=Mt(n,Us(u))}return la(n)}catch{return[e]}}function Qs(e,t){try{const i=Mt([[...e.map(e=>[e[0],e[1]]),[e[0][0],e[0][1]]]],[[...t.map(e=>[e[0],e[1]]),[t[0][0],t[0][1]]]]);let n=null,r=0;for(const e of i||[]){const t=e?.[0]||[],i=Math.abs(li(t));t.length>=4&&i>r&&(r=i,n=t.slice(0,-1).map(e=>[e[0],e[1]]))}return n}catch{return null}}function ea(e,t,i,n=Math.max(2e-4*i,1e-9),r=[]){const o=[],s=[],a=new Map;for(const e of r){const t=a.get(e.hostId)||[];t.push(e),a.set(e.hostId,t)}const l=[],c=[],h=[];for(const n of e.room_drafts||[])for(let e=0;e+10))continue;const a={a:n.points[e],b:n.points[e+1],halfDepth:s},c=zo(a);c&&(o.push(a),l.push(c))}const d=[];for(const n of e.partitions||[]){const e={a:n.a,b:n.b,halfDepth:ao(n.cm,t,i)/2};if(!(e.halfDepth>0))continue;const r=zo(e);r&&(s.push(e),c.push(r),d.push({id:n.id,body:r}))}const u=[...o,...s];for(const{segmentIndex:e,wedge:t}of Io(u,n)){const i=e=o.length&&(d[i.at].body=n))}for(const e of d)h.push(...Js(e.body,a.get(e.id)||[],n));const p=(e.wall_columns||[]).map(e=>Xs(e,t,i)),m=Eo([...o,...s],n).flatMap(e=>Js(e,r,n));return{drafts:l,partitions:c,columns:p,patches:m,all:[...l,...h,...m,...p]}}function ta(e){try{const t=e.map(e=>Gs(e)).filter(e=>!!e).map(e=>Us(e));return t.length?$t(t[0],...t.slice(1)):null}catch{return null}}const ia=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`;function na(e){const t=[];for(const i of e||[]){if(sa([i])<=1e-6)continue;const e=[];for(const t of i||[]){const i=(t||[]).filter(e=>Array.isArray(e)&&e.length>=2);i.length<4||e.push(ia(i.slice(0,-1)))}e.length&&t.push(e.join(" "))}return t}function ra(e,t,i={}){const n=ta(e.filter(e=>e.length>=3)),r=t.some(e=>!Gs(e))?null:ta(t);if(!n)return[];if(r)try{return na(St(n,r))}catch{}return function(e,t,i){let n=null;for(let r=0;r=4&&t.push(e.slice(0,-1).map(e=>[e[0],e[1]]));return t}function la(e){const t=[];for(const i of e||[]){const e=i?.[0];e?.length>=4&&t.push(e.slice(0,-1).map(e=>[e[0],e[1]]))}return t}function ca(e,t){let i=!1;for(let n=0,r=t.length-1;ne[1]!=l>e[1]&&e[0]<(a-o)*(e[1]-s)/(l-s||1e-12)+o&&(i=!i)}return i}function ha(e,t,i){return function(e,t){for(const i of t||[]){const t=i?.[0];if(!t?.length||!ca(e,t))continue;let n=!1;for(let t=1;tca(e,t))}function da(e,t,i){if(Math.hypot(e.center[0]-t.center[0],e.center[1]-t.center[1])>i)return!1;if(Math.abs(Bs(e.cm)-Bs(t.cm))>1e-6)return!1;if(e.shape!==t.shape)return!0;if("circle"===e.shape||"circle"===t.shape)return!0;const n=Math.abs(js(e.angle)-js(t.angle));return Math.min(n,90-n)<=1e-6}const ua=2.54;function pa(e){const t="number"==typeof e?e:NaN;return!Number.isFinite(t)||t<=0||5===t?1:5/t}function ma(e,t){return e*pa(t)}function _a(e){return e?ua:1}function fa(e,t){if(!t)return String(e);const i=e/ua;return String(Math.round(1e6*i)/1e6)}function ga(e,t){return t?e*ua:e}const va=1e3;function ya(e,t){const i=Number(e),n=Number.isFinite(i)&&i>0?i:1,r=n>=1?t:t*n,o=n>=1?t/n:t;return{x:(t-r)/2,y:(t-o)/2,w:r,h:o}}const ba=.01,wa=100;function ka(e,t=1e3){const i=ya(e?.plan_aspect,t),n=Number(e?.plan_scale),r=Number.isFinite(n)&&n>0?Math.min(wa,Math.max(ba,n)):1,o=Number(e?.plan_scale_x),s=Number(e?.plan_scale_y),a=Number.isFinite(o)&&o>0?Math.min(wa,Math.max(ba,o)):r,l=Number.isFinite(s)&&s>0?Math.min(wa,Math.max(ba,s)):r,c=Number(e?.plan_x),h=Number(e?.plan_y),d=nr(e?.plan_angle);return{x:i.x+(Number.isFinite(c)?Pa(c):0)*t,y:i.y+(Number.isFinite(h)?Pa(h):0)*t,w:i.w*a,h:i.h*l,...d?{angle:d}:{}}}function xa(e){if(null==e.x||null==e.y)return{x:e.x,y:e.y,w:e.w,h:e.h};const t=Number(e.w)||0,i=Number(e.h)||0;return{x:t<0?e.x+t:e.x,y:i<0?e.y+i:e.y,w:Math.abs(t),h:Math.abs(i)}}function $a(e){return e&&Array.isArray(e.spaces)?e.spaces.map(e=>{const t=va,i=function(e){return Array.isArray(e)&&4===e.length&&e.every(e=>Number.isFinite(e))&&e[2]>1e-6&&e[3]>1e-6?e:[0,0,1,1]}(e.view_box);return{id:e.id,title:e.title,cellCm:Number.isFinite(Number(e.cell_cm))&&Number(e.cell_cm)>0?Number(e.cell_cm):5,vb:[i[0]*va,i[1]*t,i[2]*va,i[3]*t],bg:e.plan_url?{href:_n(e.plan_url),...ka(e,va)}:null,rooms:(e.rooms||[]).map(e=>{const i={...e,...xa(e)};return{id:i.id,name:i.name,area:i.area??null,open_to:i.open_to||void 0,settings:i.settings||void 0,x:null!=i.x?i.x*va:void 0,y:null!=i.y?i.y*t:void 0,w:null!=i.w?i.w*va:void 0,h:null!=i.h?i.h*t:void 0,poly:i.poly?i.poly.map(e=>[e[0]*va,e[1]*t]):void 0,wall_ids:Array.isArray(i.wall_ids)?[...i.wall_ids]:void 0}}),wall_segments:(e.wall_segments||[]).map(e=>({...e,id:String(e.id),a:[Number(e.a?.[0])*va,Number(e.a?.[1])*t],b:[Number(e.b?.[0])*va,Number(e.b?.[1])*t],cm:Number(e.cm)})),room_drafts:(e.room_drafts||[]).map(e=>({id:e.id,points:(e.points||[]).map(e=>[e[0]*va,e[1]*t]),segments:(e.segments||[]).map(e=>({..."string"==typeof e.id&&e.id?{id:e.id}:{},cm:Number(e.cm)}))})),partitions:(e.partitions||[]).map(e=>({id:e.id,a:[e.a[0]*va,e.a[1]*t],b:[e.b[0]*va,e.b[1]*t],cm:Number(e.cm)})),wall_columns:(e.wall_columns||[]).map(e=>({id:e.id,shape:"circle"===e.shape?"circle":"square",center:[e.center[0]*va,e.center[1]*t],cm:Number(e.cm),..."circle"===e.shape?{}:{angle:js(e.angle)}}))}}):[]}const Sa=5e3,Ma=Sa*va,Ra=240,Ta=va/Ra,Ca=1/Ra;function Da(e){if(!Number.isFinite(e))return e;const t=Math.round(e*Ra/va)*va/Ra;return Math.abs(t-e)<=1e-9*Ta?e:t}function za(e){return{x:Da(e.x),y:Da(e.y)}}function Aa(e){return Number.isFinite(e)?Math.min(Ma,Math.max(-Ma,e)):0}function Pa(e){return Number.isFinite(e)?Math.min(Sa,Math.max(-Sa,e)):0}const Oa=1/3,Fa=200;function Ia(e){return"house"===e?"house":"content"}function Ea(e){let t=1/0,i=1/0,n=-1/0,r=-1/0;for(const o of e){const e=Number(o[0]),s=Number(o[1]);Number.isFinite(e)&&Number.isFinite(s)&&(en&&(n=e),s>r&&(r=s))}return t>n?null:{minX:t,minY:i,maxX:n,maxY:r}}function Ha(e){const t=[],i=e=>{if(Array.isArray(e))if(e.length>=2&&Number.isFinite(Number(e[0]))&&Number.isFinite(Number(e[1])))t.push([Number(e[0]),Number(e[1])]);else for(const t of e)i(t)};return i(e),Ea(t)}function Na(e,t){const i=Number.isFinite(t)&&t>0?t:0;return{minX:e.minX-i,minY:e.minY-i,maxX:e.maxX+i,maxY:e.maxY+i}}function La(e){return e.poly&&e.poly.length?Ea(e.poly):null==e.x||null==e.y?null:Ea([[e.x,e.y],[e.x+(e.w||0),e.y+(e.h||0)]])}function Wa(e,t){const i=[];for(const t of e.rooms||[]){const e=La(t);e&&i.push(e)}if(e.bg){const t=Ea(dr(e.bg));t&&i.push(t)}for(const e of t||[])if(Array.isArray(e)){const t=Ea([e]);t&&i.push(t)}else i.push(e);return i}const qa=e=>{if(!e.length)return 0;const t=[...e].sort((e,t)=>e-t),i=t.length>>1;return t.length%2?t[i]:(t[i-1]+t[i])/2},Ba=e=>{let t=1/0,i=1/0,n=-1/0,r=-1/0;for(const o of e)o.minXn&&(n=o.maxX),o.maxY>r&&(r=o.maxY);return t>n||i>r?null:{x:t,y:i,w:n-t,h:r-i}};function ja(e,t){let{x:i,y:n,w:r,h:o}=e;r<30&&(i=i+r/2-100,r=Fa),o<30&&(n=n+o/2-100,o=Fa);const s="number"==typeof t?{top:t,right:t,bottom:t,left:t}:t,a=Math.max(r,o),l=a*s.top,c=a*s.right,h=a*s.bottom,d=a*s.left;return{x:i-d,y:n-l,w:r+d+c,h:o+l+h}}function Ua(e,t={}){const i=t.pad??.05,n=t.k??10,r=t.minSpread??50,o=e.filter(e=>Number.isFinite(e.minX)&&Number.isFinite(e.minY)&&Number.isFinite(e.maxX)&&Number.isFinite(e.maxY)&&Math.abs(e.minX)<=Ma&&Math.abs(e.maxX)<=Ma&&Math.abs(e.minY)<=Ma&&Math.abs(e.maxY)<=Ma);if(!o.length)return{core:null,all:null,outliers:0};const s=Ba(o);if(o.length<4){const e=ja(s,i);return{core:e,all:e,outliers:0}}const a=o.map(e=>(e.minX+e.maxX)/2),l=o.map(e=>(e.minY+e.maxY)/2),c=qa(a),h=qa(l),d=o.map((e,t)=>Math.max(Math.abs(a[t]-c),Math.abs(l[t]-h))),u=Math.max(((e,t)=>e.length?e[Math.min(e.length-1,Math.max(0,Math.round(t*(e.length-1))))]:0)([...d].sort((e,t)=>e-t),.75),r),p=d.map(e=>e>n*u),m=p.filter(Boolean).length,_=m&&m<=o.length*Oa?o.filter((e,t)=>!p[t]):o;return{core:ja(Ba(_)||s,i),all:ja(s,i),outliers:_===o?0:m}}function Va(e){return Ua(e,{pad:0}).all}function Ga(e,t,i=.05){const n=Ua(Wa(e,t),{pad:i});if(n.core)return n.core;const r=e.vb&&4===e.vb.length&&e.vb[2]>0&&e.vb[3]>0?e.vb:[0,0,va,va];return{x:r[0],y:r[1],w:r[2],h:r[3]}}function Ka(e){const t=Ga(e);return{x:t.x+t.w/2,y:t.y+t.h/2}}function Ya(e){const t=[];for(const i of e.rooms||[]){const e=La(i);e&&t.push(e)}const i=Ua(t,{pad:0}).core,n=va*pa(e.cellCm??5);return i?Math.max(n,Math.min(Ma,Math.max(i.w,i.h))):n}function Xa(e,t,i,n=1){const r=Number(i),o=Number.isFinite(n)&&n>0?n:1;return!Number.isFinite(r)||r<=0?e*o:e*Ya(t)*o/r}const Za=[1,2,5,10,20,50,100,200,500,1e3];function Ja(e,t,i=7){if(!(e>0&&t>0&&Number.isFinite(t)))return null;const n=Za.find(n=>e*n*t>=i);if(void 0===n)return null;const r=Za.find(e=>e>=5*n)??5*n;return{fine:n,coarse:r}}function Qa(e){if(e.poly&&e.poly.length){const t=e.poly.map(e=>e[0]),i=e.poly.map(e=>e[1]),n=Math.min(...t),r=Math.min(...i);return{x:n,y:r,w:Math.max(...t)-n,h:Math.max(...i)-r}}return{x:e.x??0,y:e.y??0,w:e.w??0,h:e.h??0}}function el(e,t,i,n,r){const o=t[e.id];return o&&o.s===e.space?{x:o.x*va,y:o.y*va}:n[e.id]?n[e.id]:za(Ka(r))}function tl(e,t,i,n){const r=i["rl_"+(e.id||"")];if(r&&r.s===t)return{x:r.x*va,y:r.y*va};const o=function(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}(e);return za({x:o[0],y:o[1]})}const il=["furniture","appliance","sanitary","other"],nl=[{id:"sofa",group:"furniture",category:"sofa",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",category:"armchair",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",category:"coffee_table",w:110,h:60,g:[["r",0,0,1,1],["r",.08,.14,.84,.72]]},{id:"table_dining",group:"furniture",category:"dining_table",w:140,h:80,g:[["r",0,0,1,1],["r",.06,.11,.88,.78]]},{id:"table_round",group:"furniture",category:"dining_table",w:120,h:120,g:[["e",.5,.5,.5,.5],["e",.5,.5,.41,.41]]},{id:"chair",group:"furniture",category:"chair",w:45,h:45,g:[["r",0,0,1,.18],["r",.06,.18,.88,.8]]},{id:"desk",group:"furniture",category:"work_table",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",category:"bed",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",category:"bed",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",category:"nightstand",w:45,h:40,g:[["r",0,0,1,1],["r",.12,.14,.76,.33],["r",.12,.53,.76,.33]]},{id:"wardrobe",group:"furniture",category:"wardrobe",w:100,h:60,g:[["r",0,0,1,1],["l",0,.72,1,.72],["l",.5,.72,.5,1]]},{id:"bookshelf",group:"furniture",category:"wardrobe",w:80,h:30,g:[["r",0,0,1,1],["l",.34,0,.34,1],["l",.67,0,.67,1]]},{id:"fridge",group:"appliance",category:"fridge",w:60,h:65,g:[["r",0,0,1,1],["l",0,.36,1,.36],["l",.83,.44,.83,.64]]},{id:"stove",group:"appliance",category:"cooktop",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",category:"dishwasher",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",category:"washer",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",category:"dryer",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",category:"tv",w:120,h:30,g:[["r",0,0,1,.42],["l",.5,.42,.5,.72],["l",.3,.72,.7,.72]]},{id:"ac",group:"appliance",category:"air_conditioner",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",category:"boiler",w:45,h:45,g:[["e",.5,.5,.5,.5],["e",.5,.5,.31,.31]]},{id:"toilet",group:"sanitary",category:"toilet",w:40,h:70,g:[["r",.06,0,.88,.2],["e",.5,.58,.37,.35],["e",.5,.58,.22,.2]]},{id:"bathtub",group:"sanitary",category:"bathtub",w:170,h:75,g:[["r",0,0,1,1],["r",.05,.11,.77,.78],["e",.89,.5,.045,.1]]},{id:"shower",group:"sanitary",category:"shower",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",category:"sink",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",category:"kitchen_sink",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",category:"bidet",w:40,h:55,g:[["e",.5,.5,.44,.5],["e",.5,.5,.26,.3]]},{id:"stairs",group:"other",category:"stairs",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",category:"fireplace",w:120,h:40,g:[["r",0,0,1,1],["p",.22,1,.22,.42,.78,.42,.78,1]]},{id:"plant",group:"other",category:"plant",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",category:"rug",w:200,h:140,g:[["r",0,0,1,1],["r",.06,.09,.88,.82]]}],rl=new Set(["fridge","dishwasher","washer","dryer","ac","water_heater","shower","sink","stairs","fireplace","plant","rug"]),ol=[...[{id:"coffee_table",group:"furniture",category:"coffee_table",w:120,h:60,art:{d:"M3.84 5.884c0-1.21 1.048-2.192 2.34-2.192h107.64c1.293 0 2.34.981 2.34 2.192v48.23c0 1.212-1.047 2.193-2.34 2.193H6.18c-1.292 0-2.34-.981-2.34-2.192z M103.056 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m-93.6-35.077V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877",viewW:120,viewH:60}},{id:"coffee_table_round",group:"furniture",category:"coffee_table",w:80,h:80,art:{d:"M3.765 40a36.235 36.235 0 1 0 72.47 0 36.235 36.235 0 1 0-72.47 0 m56.26 26.96-3.843-3.843a.906.906 0 0 1 0-1.28l3.843-3.844a.906.906 0 0 1 1.281 0l3.844 3.843a.906.906 0 0 1 0 1.281l-3.844 3.844a.906.906 0 0 1-1.28 0m-.001 34.66-3.843-3.843a.906.906 0 0 1 0-1.281l3.843-3.844a.906.906 0 0 1 1.281 0l3.843 3.844a.906.906 0 0 1 0 1.28l-3.843 3.844a.906.906 0 0 1-1.281 0m-34.423-34.66-3.843-3.843a.906.906 0 0 1 0-1.281l3.843-3.844a.906.906 0 0 1 1.281 0l3.844 3.844a.906.906 0 0 1 0 1.28l-3.844 3.844a.906.906 0 0 1-1.28 0m-.001 34.66-3.843-3.843a.906.906 0 0 1 0-1.281l3.843-3.843a.906.906 0 0 1 1.281 0l3.844 3.843a.906.906 0 0 1 0 1.281l-3.844 3.843a.906.906 0 0 1-1.28 0",viewW:80,viewH:80}},{id:"coffee_table_oval",group:"furniture",category:"coffee_table",w:120,h:60,art:{d:"M3.84 30c0-14.53 12.572-26.308 28.08-26.308h56.16c15.509 0 28.08 11.778 28.08 26.308s-12.571 26.307-28.08 26.307H31.92C16.412 56.307 3.84 44.53 3.84 30 M80.592 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.077v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877M31.92 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.876h-5.616a.936.877 0 0 1-.936-.876m0 35.077v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.876h-5.616a.936.877 0 0 1-.936-.876",viewW:120,viewH:60}},{id:"coffee_table_rounded",group:"furniture",category:"coffee_table",w:120,h:60,art:{d:"M3.84 12.461c0-4.843 4.19-8.77 9.36-8.77h93.6c5.17 0 9.36 3.927 9.36 8.77v35.077c0 4.843-4.19 8.77-9.36 8.77H13.2c-5.17 0-9.36-3.927-9.36-8.77z M103.056 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m-93.6-35.077V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877",viewW:120,viewH:60}},{id:"table_dining",group:"furniture",category:"dining_table",w:160,h:90,art:{d:"M3.879 6.08c0-1.265 1.065-2.29 2.378-2.29h147.484c1.314 0 2.38 1.025 2.38 2.29v77.84c0 1.265-1.066 2.29-2.38 2.29H6.257c-1.313 0-2.378-1.025-2.378-2.29z M138.994 19.358v-5.495a.952.916 0 0 1 .951-.915h5.71a.952.916 0 0 1 .95.915v5.495a.952.916 0 0 1-.95.916h-5.71a.952.916 0 0 1-.951-.916m-.001 56.778v-5.495a.952.916 0 0 1 .952-.915h5.709a.952.916 0 0 1 .951.915v5.495a.952.916 0 0 1-.951.916h-5.71a.952.916 0 0 1-.95-.916m-125.6-56.778v-5.495a.952.916 0 0 1 .951-.916h5.71a.952.916 0 0 1 .95.916v5.495a.952.916 0 0 1-.95.915h-5.71a.952.916 0 0 1-.951-.915m-.001 56.779v-5.495a.952.916 0 0 1 .952-.916h5.709a.952.916 0 0 1 .951.916v5.495a.952.916 0 0 1-.951.915h-5.71a.952.916 0 0 1-.95-.915",viewW:160,viewH:90}},{id:"table_round",group:"furniture",category:"dining_table",w:110,h:110,art:{d:"M3.826 55a51.174 51.174 0 1 0 102.349 0A51.174 51.174 0 1 0 3.826 55 m81.005 32.303-3.947-3.947a.93.93 0 0 1 0-1.316l3.947-3.948a.93.93 0 0 1 1.316 0l3.948 3.948a.93.93 0 0 1 0 1.316l-3.948 3.947a.93.93 0 0 1-1.316 0m0 54.897-3.947-3.948a.93.93 0 0 1 0-1.316l3.947-3.947a.93.93 0 0 1 1.316 0l3.948 3.947a.93.93 0 0 1 0 1.316L82.32 87.2a.93.93 0 0 1-1.316 0M27.04 32.304l-3.947-3.948a.93.93 0 0 1 0-1.315l3.948-3.948a.93.93 0 0 1 1.315 0l3.948 3.948a.93.93 0 0 1 0 1.315l-3.948 3.948a.93.93 0 0 1-1.315 0m-.001 54.604-3.947-3.948a.93.93 0 0 1 0-1.316l3.948-3.947a.93.93 0 0 1 1.315 0l3.948 3.947a.93.93 0 0 1 0 1.316l-3.948 3.948a.93.93 0 0 1-1.315 0",viewW:110,viewH:110}},{id:"table_dining_oval",group:"furniture",category:"dining_table",w:180,h:100,art:{d:"M3.892 50c0-25.51 21.418-46.19 47.838-46.19h76.54c26.42 0 47.839 20.68 47.839 46.19S154.69 96.19 128.27 96.19H51.73C25.31 96.19 3.892 75.51 3.892 50 M122.53 19.514V13.97a.957.924 0 0 1 .956-.924h5.74a.957.924 0 0 1 .958.924v5.543a.957.924 0 0 1-.957.924h-5.74a.957.924 0 0 1-.958-.924m.001 66.516v-5.543a.957.924 0 0 1 .957-.924h5.74a.957.924 0 0 1 .957.924v5.543a.957.924 0 0 1-.957.924h-5.74a.957.924 0 0 1-.957-.924M49.816 19.515v-5.543a.957.924 0 0 1 .957-.924h5.74a.957.924 0 0 1 .958.924v5.542a.957.924 0 0 1-.957.924h-5.74a.957.924 0 0 1-.958-.924m0 66.516v-5.543a.957.924 0 0 1 .957-.924h5.74a.957.924 0 0 1 .957.924v5.543a.957.924 0 0 1-.957.923h-5.74a.957.924 0 0 1-.957-.924",viewW:180,viewH:100}},{id:"table_dining_rounded",group:"furniture",category:"dining_table",w:160,h:90,art:{d:"M3.879 17.527c0-7.587 6.39-13.737 14.272-13.737h123.696c7.883 0 14.273 6.15 14.273 13.737v54.947c0 7.587-6.39 13.737-14.273 13.737H18.151c-7.882 0-14.272-6.15-14.272-13.737z M138.994 19.358v-5.495a.952.916 0 0 1 .951-.915h5.71a.952.916 0 0 1 .95.915v5.495a.952.916 0 0 1-.95.916h-5.71a.952.916 0 0 1-.951-.916m-.001 56.778v-5.495a.952.916 0 0 1 .952-.915h5.709a.952.916 0 0 1 .951.915v5.495a.952.916 0 0 1-.951.916h-5.71a.952.916 0 0 1-.95-.916m-125.6-56.778v-5.495a.952.916 0 0 1 .951-.916h5.71a.952.916 0 0 1 .95.916v5.495a.952.916 0 0 1-.95.915h-5.71a.952.916 0 0 1-.951-.915m-.001 56.779v-5.495a.952.916 0 0 1 .952-.916h5.709a.952.916 0 0 1 .951.916v5.495a.952.916 0 0 1-.951.915h-5.71a.952.916 0 0 1-.95-.915",viewW:160,viewH:90}},{id:"desk",group:"furniture",category:"work_table",w:140,h:70,art:{d:"M3.862 5.967c0-1.234 1.057-2.234 2.362-2.234h127.552c1.305 0 2.362 1 2.362 2.234v58.066c0 1.234-1.057 2.233-2.362 2.233H6.224c-1.305 0-2.362-1-2.362-2.233z M9.531 15.346v-5.36a.945.893 0 0 1 .945-.893h5.67a.945.893 0 0 1 .944.893v5.36a.945.893 0 0 1-.945.894h-5.669a.945.893 0 0 1-.945-.894m113.379.001v-5.36a.945.893 0 0 1 .945-.894h5.67a.945.893 0 0 1 .944.894v5.36a.945.893 0 0 1-.945.893h-5.669a.945.893 0 0 1-.945-.893M9.53 60.013v-5.36a.945.893 0 0 1 .946-.893h5.669a.945.893 0 0 1 .945.893v5.36a.945.893 0 0 1-.945.894h-5.67a.945.893 0 0 1-.944-.894m113.38.001v-5.36a.945.893 0 0 1 .945-.894h5.669a.945.893 0 0 1 .945.894v5.36a.945.893 0 0 1-.945.893h-5.67a.945.893 0 0 1-.944-.893m-97.318-2.681h88.814",viewW:140,viewH:70}},{id:"desk_corner",group:"furniture",category:"work_table",w:160,h:160,art:{d:"M156.12 66.202a2.38 2.38 0 0 1-2.379 2.38H94.272c-13.138 0-23.788 10.65-23.788 23.787v61.372a2.38 2.38 0 0 1-2.379 2.38H6.257a2.38 2.38 0 0 1-2.378-2.38V6.257A2.38 2.38 0 0 1 6.257 3.88h147.484a2.38 2.38 0 0 1 2.38 2.378z M9.588 16.249v-5.71a.95.95 0 0 1 .951-.95h5.71a.95.95 0 0 1 .95.95v5.71a.95.95 0 0 1-.95.951h-5.71a.95.95 0 0 1-.951-.951m133.212.001v-5.71a.95.95 0 0 1 .951-.951h5.71a.95.95 0 0 1 .95.951v5.71a.95.95 0 0 1-.95.951h-5.71a.95.95 0 0 1-.951-.951m0 45.672v-5.71a.95.95 0 0 1 .952-.951h5.709a.95.95 0 0 1 .951.951v5.71a.95.95 0 0 1-.951.951h-5.71a.95.95 0 0 1-.95-.951M9.587 149.46v-5.708a.95.95 0 0 1 .952-.952h5.709a.95.95 0 0 1 .951.952v5.709a.95.95 0 0 1-.951.951h-5.71a.95.95 0 0 1-.951-.951m47.576 0v-5.709a.95.95 0 0 1 .952-.951h5.709a.95.95 0 0 1 .951.951v5.71a.95.95 0 0 1-.951.95h-5.71a.95.95 0 0 1-.951-.95m51.382-90.396H95.569a34.6 34.6 0 0 0-34.6 34.6v12.976",viewW:160,viewH:160}},{id:"chair",group:"furniture",category:"chair",w:50,h:50,art:{d:"M39.452 12.433H10.548a4.398 4.398 0 1 1 0-8.796h28.904a4.398 4.398 0 0 1 0 8.796m-28.904 0H8.035a4.4 4.4 0 0 0-4.398 4.399V40.08a6.283 6.283 0 0 0 6.283 6.284h30.16a6.283 6.283 0 0 0 6.284-6.284V16.832c0-2.43-1.97-4.399-4.399-4.399h-2.513",viewW:50,viewH:50}},{id:"chair_bar",group:"furniture",category:"chair",w:45,h:48,art:{d:"M38.579 17.168c1.326 2.526 1.947 6.015 1.947 9.071 0 10.018-8.07 18.139-18.025 18.139S4.475 36.257 4.475 26.239c0-3.056.586-6.552 1.912-9.078m32.192.007a3.59 3.59 0 0 1-3.333-.976m3.333.976a3.6 3.6 0 0 0 1.765-.976m-33.827.996-.13-.027a3.6 3.6 0 0 1-1.731-.969m30.59 0a18 18 0 0 0-5.848-3.932 17.93 17.93 0 0 0-13.796 0 18 18 0 0 0-5.848 3.932 3.59 3.59 0 0 1-3.237.996m28.729-.996a3.59 3.59 0 0 0 5.098 0m-33.827.996a3.6 3.6 0 0 1-1.861-.996 3.644 3.644 0 0 1 0-5.13 25.2 25.2 0 0 1 8.187-5.505A25.1 25.1 0 0 1 22.5 3.623a25.1 25.1 0 0 1 9.657 1.934 25.2 25.2 0 0 1 8.187 5.505 3.644 3.644 0 0 1 0 5.13",viewW:45,viewH:48}},{id:"armchair",group:"furniture",category:"armchair",w:90,h:90,art:{d:"M75.702 13.011q.012-.225.012-.453c0-4.843-3.917-8.768-8.748-8.768H23.034c-4.83 0-8.747 3.925-8.747 8.768q0 .228.011.453M77.132 72.09q.59.091 1.206.092c4.348 0 7.873-3.533 7.873-7.892V20.45c0-4.359-3.525-7.892-7.873-7.892-.924 0-1.812.16-2.636.453a7.89 7.89 0 0 0-5.237 7.438v.148m0 0a8.7 8.7 0 0 1-3.499.73H23.034a8.7 8.7 0 0 1-3.499-.73m50.93 0V64.29a7.89 7.89 0 0 0 6.667 7.8M14.298 13.01a7.89 7.89 0 0 1 5.237 7.438v.148m0 0V64.29a7.89 7.89 0 0 1-6.666 7.8q-.59.091-1.206.092c-4.348 0-7.873-3.533-7.873-7.892V20.45c0-4.359 3.525-7.892 7.873-7.892.924 0 1.811.16 2.635.453M77.132 72.09c-1.585 8.05-8.667 14.12-17.164 14.12H30.032c-8.496 0-15.578-6.07-17.163-14.12",viewW:90,viewH:90}},{id:"armchair_office",group:"furniture",category:"armchair",w:65,h:65,art:{d:"M56.968 18.845q0-.368-.05-.72M49.051 13.8H15.95a5.04 5.04 0 0 1-5.038-5.043 5.04 5.04 0 0 1 5.038-5.043h33.103a5.04 5.04 0 0 1 5.038 5.043 5.04 5.04 0 0 1-5.038 5.043m-33.103 0h-2.88a5.04 5.04 0 0 0-4.986 4.323M49.052 13.8h2.879a5.04 5.04 0 0 1 4.986 4.324m.051 24.826a4.32 4.32 0 0 0 4.318-4.323v-16.18a4.32 4.32 0 0 0-4.369-4.322m.051 24.826a4.32 4.32 0 0 1-4.318-4.323V22.447a4.32 4.32 0 0 1 4.267-4.322m-11.84 37.24h4.695c.736 0 1.446-.111 2.115-.317a7.21 7.21 0 0 0 5.081-6.889v-5.208M8.084 18.124h-.051a4.32 4.32 0 0 0-4.318 4.323v16.18a4.32 4.32 0 0 0 4.318 4.324 4.32 4.32 0 0 0 4.317-4.323V22.447a4.32 4.32 0 0 0-4.266-4.323m11.935 37.24h-4.79a7.2 7.2 0 0 1-2.04-.294 7.21 7.21 0 0 1-5.156-6.91v-5.21m37.045 12.414 5.177 5.183c.969.97 2.534.987 3.524.038a2.526 2.526 0 0 0 .038-3.605l-1.93-1.932m-6.81.316H20.02m-6.83-.294-1.909 1.91c-.998 1-.98 2.627.038 3.605.99.949 2.555.932 3.524-.038l5.177-5.183",viewW:65,viewH:65}},{id:"sofa",group:"furniture",category:"sofa",w:180,h:90,art:{d:"M90 19.365v57.111m0-57.11V7.683m0 11.681c0 2.151-1.836 3.894-4.1 3.894H30.544a10.7 10.7 0 0 1-4.136-.825M90 19.365c0 2.151 1.836 3.894 4.1 3.894h55.356c1.472 0 2.871-.294 4.137-.825M90 76.476c0 5.376-4.59 9.735-10.25 9.735H30.543c-5.266 0-9.605-3.772-10.185-8.625M90 76.476c0 5.376 4.59 9.735 10.25 9.735h49.206c5.266 0 9.605-3.772 10.185-8.625M90 7.684c0-2.15-1.837-3.894-4.101-3.894H30.544c-5.661 0-10.25 4.358-10.25 9.735q0 .416.036.825M90 7.684c0-2.15 1.836-3.894 4.1-3.894h55.356c5.661 0 10.25 4.358 10.25 9.735q0 .416-.036.825m0 0a10.7 10.7 0 0 1 4.137-.825h2.05c5.662 0 10.252 4.358 10.252 9.734v45.43c0 5.376-4.59 9.734-10.251 9.734h-2.05c-1.484 0-2.894-.3-4.167-.837m0 0c-3.585-1.517-6.084-4.93-6.084-8.898V23.26q0-.417.036-.825c.32-3.622 2.728-6.68 6.077-8.084m-139.34 0c3.35 1.404 5.758 4.462 6.078 8.084q.036.408.036.825v45.43c0 3.967-2.5 7.38-6.085 8.897a10.7 10.7 0 0 1-4.166.837h-2.05c-5.662 0-10.251-4.358-10.251-9.735V23.26c0-5.376 4.59-9.734 10.25-9.734h2.05c1.473 0 2.872.294 4.138.825",viewW:180,viewH:90}},{id:"sofa_three_seat",group:"furniture",category:"sofa",w:240,h:90,art:{d:"M86.553 18.593V76.96m0-58.366V7.491m0 11.102c0 2.044-1.762 3.701-3.935 3.701H29.496c-1.413 0-2.756-.28-3.97-.784m61.027-2.917c0 2.044 1.762 3.701 3.935 3.701h59.024c2.174 0 3.935-1.657 3.935-3.7M86.553 76.958c0 5.11-4.404 9.252-9.837 9.252h-47.22c-5.054 0-9.218-3.585-9.775-8.198m66.832-1.054c0 5.11 4.404 9.252 9.837 9.252h47.22c5.433 0 9.837-4.142 9.837-9.252m0-58.366V76.96m0-58.366V7.491m0 11.102c0 2.044 1.762 3.701 3.936 3.701h53.122c1.413 0 2.756-.28 3.97-.784m-61.028 55.449c0 5.11 4.405 9.252 9.838 9.252h47.22c5.054 0 9.218-3.585 9.775-8.198M86.553 7.491c0-2.044-1.762-3.701-3.935-3.701H29.496c-5.433 0-9.838 4.142-9.838 9.252q0 .396.035.784m66.86-6.335c0-2.044 1.762-3.701 3.935-3.701h59.024c2.174 0 3.935 1.657 3.935 3.7m0 0c0-2.043 1.762-3.7 3.936-3.7h53.122c5.433 0 9.838 4.142 9.838 9.252 0 .258.022.517 0 .77m-.063 64.201c1.22.512 2.574.796 3.998.796h1.967c5.433 0 9.837-4.142 9.837-9.252V22.294c0-5.11-4.404-9.252-9.837-9.252h-1.967c-5.153 0-9.38 3.725-9.803 8.468q-.036.387-.035.784v47.263c0 3.771 2.399 7.015 5.84 8.456M19.693 13.826a10.3 10.3 0 0 0-3.97-.784h-1.967c-5.433 0-9.838 4.142-9.838 9.252v47.263c0 5.11 4.405 9.252 9.838 9.252h1.967c1.424 0 2.777-.284 3.998-.796 3.441-1.441 5.84-4.685 5.84-8.456V22.294a9 9 0 0 0-.035-.784c-.307-3.443-2.618-6.349-5.833-7.684",viewW:240,viewH:90}},{id:"sofa_corner_right",group:"furniture",category:"sofa",w:260,h:170,art:{d:"M93.673 20.687V83.69m0-63.004v-12.6m0 12.6c0 2.32-1.913 4.2-4.273 4.2H31.704a10.8 10.8 0 0 1-4.311-.89m66.28-3.31c0 2.32 1.914 4.2 4.274 4.2h64.106c2.36 0 4.274-1.88 4.274-4.2M93.673 83.692c0 5.8-4.783 10.501-10.684 10.501H31.704c-5.489 0-10.011-4.068-10.616-9.304m72.585-1.197c0 5.8 4.784 10.501 10.684 10.501h51.286c5.9 0 10.684-4.701 10.684-10.5m0-63.005V83.69m0-63.004v-12.6m0 12.6c0 2.32 1.914 4.2 4.273 4.2h57.696c1.534 0 2.993-.317 4.312-.89m-66.28 59.695v71.923c0 5.799 4.782 10.5 10.684 10.5h51.284c5.49 0 10.012-4.068 10.616-9.304V84.888M93.673 8.086c0-2.32-1.913-4.2-4.273-4.2H31.704c-5.9 0-10.684 4.7-10.684 10.5q0 .45.038.89m72.615-7.19c0-2.32 1.914-4.2 4.274-4.2h64.106c2.36 0 4.274 1.88 4.274 4.2m0 0c0-2.32 1.914-4.2 4.273-4.2h57.696c5.9 0 10.685 4.7 10.685 10.5 0 .293.024.587 0 .874m-.07 69.628c1.327.58 2.797.904 4.344.904h2.136c5.901 0 10.684-4.702 10.684-10.501V24.887c0-5.8-4.783-10.5-10.684-10.5h-2.136c-5.596 0-10.187 4.227-10.647 9.61q-.038.44-.038.89V75.29c0 4.28 2.605 7.961 6.342 9.597M21.059 15.276a10.8 10.8 0 0 0-4.312-.89H14.61c-5.9 0-10.684 4.701-10.684 10.5v50.405c0 5.8 4.783 10.5 10.684 10.5h2.137c1.546 0 3.016-.322 4.342-.903 3.737-1.636 6.342-5.317 6.342-9.597V24.887q0-.45-.037-.89c-.334-3.907-2.844-7.206-6.335-8.72",viewW:260,viewH:170}},{id:"bed_single",group:"furniture",category:"bed",w:90,h:200,art:{d:"M3.79 14.024c0 5.59 4.318 10.122 9.645 10.122h63.13c5.328 0 9.646-4.532 9.646-10.122m-22.797 46.93V46.23c0-5.082-3.926-9.201-8.769-9.201h-19.29c-4.842 0-8.768 4.12-8.768 9.201v14.723m-22.797 0h82.42m0 22.085H3.79m8.768 113.06h64.885c4.842 0 8.768-4.12 8.768-9.202V13.104c0-5.082-3.926-9.202-8.768-9.202H12.558c-4.843 0-8.768 4.12-8.768 9.202v173.792c0 5.082 3.925 9.202 8.768 9.202",viewW:90,viewH:200}},{id:"bed_double",group:"furniture",category:"bed",w:160,h:200,art:{d:"M3.879 14.073c0 5.618 4.516 10.172 10.088 10.172h132.065c5.571 0 10.088-4.554 10.088-10.172m-81.165 47.16V46.438c0-5.107-4.106-9.247-9.171-9.247H45.607c-5.065 0-9.17 4.14-9.17 9.247v14.795m89.877 0V46.438c0-5.107-4.107-9.247-9.172-9.247H96.966c-5.065 0-9.171 4.14-9.171 9.247v14.795m-83.916 0H156.12m0 22.192H3.88m9.17 112.673h133.9c5.065 0 9.171-4.14 9.171-9.247V13.15c0-5.107-4.106-9.247-9.171-9.247H13.05c-5.064 0-9.17 4.14-9.17 9.247v173.7c0 5.107 4.106 9.247 9.17 9.247",viewW:160,viewH:200}},{id:"nightstand",group:"furniture",category:"nightstand",w:50,h:40,art:{d:"M5.773 3.555h38.454a2.136 2.056 0 0 1 2.137 2.056v28.777a2.136 2.056 0 0 1-2.137 2.056H5.773a2.136 2.056 0 0 1-2.136-2.056V5.611a2.136 2.056 0 0 1 2.136-2.056",viewW:50,viewH:40}},{id:"cabinet_tv",group:"furniture",category:"nightstand",w:140,h:45,art:{d:"M56.772 17.88h5.315m0 0h5.315m-5.315 0v9.214m9.832-9.214 5.315 9.45 5.314-9.45M6.224 41.4h127.552c1.305 0 2.362-.94 2.362-2.1V5.7c0-1.16-1.057-2.1-2.362-2.1H6.224c-1.305 0-2.362.94-2.362 2.1v33.6c0 1.16 1.057 2.1 2.362 2.1",viewW:140,viewH:45}},{id:"cabinet_shoe",group:"furniture",category:"nightstand",w:80,h:35,art:{d:"m39.722 16.231 1.505-1.596m1.134 3.15 1.53-1.738M6.03 31.5h67.94c1.251 0 2.265-.895 2.265-2v-24c0-1.105-1.014-2-2.264-2H6.03c-1.251 0-2.265.895-2.265 2v24c0 1.105 1.014 2 2.265 2m41.966-14.305L46.92 17.1a3.4 3.4 0 0 1-1.813-.706l-4.583-3.62c-1.264-1-3.277-.326-3.475 1.161-.185 1.39-1.983 2.1-3.28 1.299l-1.532-.947c-.968-.513-2.2.103-2.2 1.1v3.463c0 1.559 1.43 2.823 3.196 2.823h14.51c1.404 0 2.542-1.005 2.542-2.245 0-1.153-.99-2.118-2.289-2.233",viewW:80,viewH:35}},{id:"cabinet_sink",group:"furniture",category:"nightstand",w:80,h:50,art:{d:"M3.765 5.773c0-1.18 1.014-2.136 2.265-2.136h67.94c1.251 0 2.265.956 2.265 2.136v38.454c0 1.18-1.014 2.137-2.264 2.137H6.03c-1.251 0-2.265-.957-2.265-2.137z M36.088 32.135c2.525 1.88 6.205 1.88 8.73 0s3.167-5.101 1.52-7.637l-5.879-8.043-5.893 8.043c-1.644 2.536-1.003 5.755 1.522 7.637",viewW:80,viewH:50}},{id:"bookshelf",group:"furniture",category:"wardrobe",w:100,h:35,art:{d:"M57.12 20.488H45.473c-.514 0-1.008.124-1.372.345s-.568.52-.568.832m4.076-8.236h5.434M6.119 3.5h87.762c1.276 0 2.31.895 2.31 2v24c0 1.105-1.034 2-2.31 2H6.12c-1.275 0-2.31-.895-2.31-2v-24c0-1.105 1.035-2 2.31-2m51 6.4v14.118H45.474a2.04 2.04 0 0 1-1.372-.517 1.7 1.7 0 0 1-.568-1.248V11.665c0-.468.204-.917.568-1.248a2.04 2.04 0 0 1 1.372-.517z",viewW:100,viewH:35}},{id:"wall_unit",group:"furniture",category:"wardrobe",w:240,h:45,art:{d:"M6.336 41.4h227.328a2.418 2.1 0 0 0 2.418-2.1V5.7a2.418 2.1 0 0 0-2.418-2.1H6.336a2.418 2.1 0 0 0-2.418 2.1v33.6a2.418 2.1 0 0 0 2.418 2.1m43.744-5.88V9.48m46.432 26.04V9.48m46.433 26.04V9.48m46.433 26.04V9.48",viewW:240,viewH:45}},{id:"wardrobe",group:"furniture",category:"wardrobe",w:180,h:60,art:{d:"M93.87 20.25a3.3 3.3 0 0 0-1.072-2.407 3.8 3.8 0 0 0-2.586-.997c-.97 0-1.9.358-2.587.997a3.3 3.3 0 0 0-1.071 2.407c0 2.838 1.225 5.107 3.658 6.81h-.015m0 0 14.58 7.537c.57.295 1.046.726 1.377 1.25.331.523.505 1.12.505 1.727v1.401a3.3 3.3 0 0 1-1.071 2.407 3.8 3.8 0 0 1-2.587.998H77.393a3.8 3.8 0 0 1-2.586-.998 3.3 3.3 0 0 1-1.072-2.407v-1.4c0-.608.174-1.205.505-1.728a3.55 3.55 0 0 1 1.377-1.25zM6.284 3.691h167.433c1.32 0 2.392.981 2.392 2.192v48.23c0 1.212-1.071 2.193-2.392 2.193H6.284c-1.321 0-2.392-.981-2.392-2.192V5.885c0-1.212 1.07-2.193 2.392-2.193",viewW:180,viewH:60}},{id:"kitchen_floor",group:"furniture",category:"kitchen_cabinet",w:60,h:60,art:{d:"M3.692 5.884c0-1.21.981-2.192 2.192-2.192h48.23c1.212 0 2.193.981 2.193 2.192v48.23a2.19 2.19 0 0 1-2.192 2.193H5.885a2.19 2.19 0 0 1-2.193-2.192z",viewW:60,viewH:60}},{id:"kitchen_floor_corner",group:"furniture",category:"kitchen_cabinet",w:90,h:90,art:{d:"M45.944 41.767a2.29 2.29 0 0 0 2.29 2.29H83.92a2.29 2.29 0 0 1 2.29 2.29V83.92a2.29 2.29 0 0 1-2.29 2.29H6.08a2.29 2.29 0 0 1-2.29-2.29V6.08a2.29 2.29 0 0 1 2.29-2.29h37.576a2.29 2.29 0 0 1 2.29 2.29z",viewW:90,viewH:90}},{id:"kitchen_wall",group:"furniture",category:"kitchen_cabinet",w:60,h:35,art:{d:"m4.444 31.033 50.75-27.007m-50.752-.06 50.75 27.008M5.884 31.5h48.23c1.212 0 2.193-.895 2.193-2v-24c0-1.105-.981-2-2.192-2H5.885c-1.212 0-2.193.895-2.193 2v24c0 1.105.981 2 2.192 2",viewW:60,viewH:35}},{id:"kitchen_wall_corner",group:"furniture",category:"kitchen_cabinet",w:60,h:60,art:{d:"M3.772 29.59 29.88 4.383M4.57 55.43 55.43 30M4.175 4.383l26.701 23.863m-27.184.877L55.43 55.43M28.41 3.692H5.884c-1.21 0-2.192.981-2.192 2.192v48.23c0 1.212.981 2.193 2.192 2.193h48.23a2.19 2.19 0 0 0 2.193-2.192V31.589c0-1.21-.981-2.192-2.192-2.192h-21.32a2.19 2.19 0 0 1-2.193-2.192V5.885a2.19 2.19 0 0 0-2.192-2.193",viewW:60,viewH:60}},{id:"shelf_floor",group:"furniture",category:"shelving",w:100,h:35,art:{d:"M6.12 31.5h87.76a2.31 2 0 0 0 2.31-2v-24a2.31 2 0 0 0-2.31-2H6.12a2.31 2 0 0 0-2.31 2v24a2.31 2 0 0 0 2.31 2 M9.089 25.933c-.494.112-.776.546-.63.97s.664.675 1.157.564l-.263-.767zM90.912 9.067c.493-.112.775-.546.63-.97s-.664-.675-1.158-.564l.264.767zM11.875 26.956c.493-.112.775-.546.63-.97s-.664-.676-1.158-.564l.264.767zm3.988-2.556c-.493.112-.775.546-.63.969s.664.676 1.158.564l-.264-.766zm5.045.511c.493-.112.775-.545.63-.969s-.664-.676-1.158-.564l.264.766zm3.988-2.555c-.493.111-.775.545-.63.968.146.424.664.677 1.158.565l-.264-.767zm5.044.51c.494-.111.776-.545.63-.968-.145-.424-.664-.676-1.157-.565l.263.767zm3.99-2.555c-.495.112-.777.546-.63.97.145.423.663.675 1.157.563l-.264-.766zm5.043.511c.494-.112.776-.545.63-.969s-.664-.676-1.158-.564l.264.767zm3.989-2.555c-.494.111-.776.545-.63.969s.664.676 1.157.564l-.263-.767zm5.044.51c.494-.11.776-.545.63-.968-.146-.424-.664-.676-1.158-.565l.264.767zm3.989-2.555c-.494.112-.776.546-.63.97.145.423.664.675 1.157.564l-.264-.767zm5.044.511c.493-.111.775-.545.63-.969s-.664-.676-1.158-.564l.264.767zm3.988-2.555c-.493.112-.775.545-.63.969s.664.676 1.158.564l-.264-.767zm5.045.51c.493-.11.775-.545.63-.968-.146-.424-.664-.676-1.158-.564l.264.766zm3.988-2.555c-.493.112-.775.546-.63.97s.664.675 1.158.564l-.264-.767zm5.044.511c.494-.111.776-.545.63-.968-.145-.424-.664-.677-1.157-.565l.264.767zm3.99-2.555c-.494.112-.777.545-.63.969.145.423.663.676 1.157.564l-.264-.766zm5.043.511c.494-.112.776-.546.63-.969s-.664-.676-1.157-.564l.263.766zm3.989-2.556c-.494.112-.776.546-.63.97s.664.676 1.158.564l-.264-.767zM9.353 26.7l.263.767 2.259-.511-.264-.767-.264-.767-2.258.511zm6.774-1.533.264.766 4.517-1.022-.264-.767-.264-.766-4.517 1.022zm9.033-2.045.264.767 4.516-1.022-.264-.767-.263-.767-4.517 1.023zm9.033-2.044.264.766 4.516-1.022-.264-.766-.264-.767-4.516 1.022zm9.033-2.045.263.767 4.517-1.022-.264-.767-.264-.767-4.516 1.023zm9.032-2.044.264.767 4.517-1.023-.264-.766-.264-.767-4.516 1.022zm9.033-2.045.264.767 4.517-1.022-.264-.767-.264-.766-4.517 1.022zm9.033-2.044.264.767 4.516-1.023-.263-.766-.264-.767-4.517 1.022zm9.033-2.044.264.766 4.516-1.022-.264-.767-.263-.766-4.517 1.022zM88.39 8.81l.264.767 2.258-.511-.264-.767-.264-.767-2.258.511z M9.089 9.067c-.494-.112-.776-.546-.63-.97s.664-.675 1.157-.564l-.263.767zm81.823 16.866c.493.112.775.546.63.97s-.664.675-1.158.564l.264-.767zM11.875 8.044c.493.112.775.546.63.97s-.664.676-1.158.564l.264-.767zm3.988 2.556c-.493-.112-.775-.546-.63-.969s.664-.676 1.158-.564l-.264.766zm5.045-.511c.493.112.775.545.63.969s-.664.676-1.158.564l.264-.766zm3.988 2.555c-.493-.111-.775-.545-.63-.968.146-.424.664-.677 1.158-.565l-.264.767zm5.044-.51c.494.111.776.545.63.968-.145.424-.664.676-1.157.565l.263-.767zm3.99 2.555c-.495-.112-.777-.546-.63-.97.145-.423.663-.675 1.157-.563l-.264.766zm5.043-.511c.494.112.776.545.63.969s-.664.676-1.158.564l.264-.767zm3.989 2.555c-.494-.111-.776-.545-.63-.969s.664-.676 1.157-.564l-.263.767zm5.044-.51c.494.11.776.545.63.968-.146.424-.664.676-1.158.565l.264-.767zm3.989 2.555c-.494-.112-.776-.546-.63-.97.145-.423.664-.675 1.157-.564l-.264.767zm5.044-.511c.493.111.775.545.63.969s-.664.676-1.158.564l.264-.767zm3.988 2.555c-.493-.112-.775-.545-.63-.969s.664-.676 1.158-.564l-.264.767zm5.045-.51c.493.11.775.545.63.968-.146.424-.664.676-1.158.564l.264-.766zm3.988 2.555c-.493-.112-.775-.546-.63-.97s.664-.675 1.158-.564l-.264.767zm5.044-.511c.494.111.776.545.63.968-.145.424-.664.677-1.157.565l.264-.767zm3.99 2.555c-.494-.112-.777-.545-.63-.969.145-.423.663-.676 1.157-.564l-.264.766zm5.043-.511c.494.112.776.546.63.969s-.664.676-1.157.564l.263-.766zm3.989 2.556c-.494-.112-.776-.546-.63-.97s.664-.676 1.158-.564l-.264.767zM9.353 8.3l.263-.767 2.259.511-.264.767-.264.767-2.258-.511zm6.774 1.533.264-.766 4.517 1.022-.264.767-.264.766-4.517-1.022zm9.033 2.045.264-.767 4.516 1.022-.264.767-.263.767-4.517-1.023zm9.033 2.044.264-.766 4.516 1.022-.264.766-.264.767-4.516-1.022zm9.033 2.045.263-.767 4.517 1.022-.264.767-.264.767-4.516-1.023zm9.032 2.044.264-.767 4.517 1.023-.264.766-.264.767-4.516-1.022zm9.033 2.045.264-.767 4.517 1.022-.264.767-.264.766-4.517-1.022zm9.033 2.044.264-.767 4.516 1.023-.263.766-.264.767-4.517-1.022zm9.033 2.044.264-.766 4.516 1.022-.264.767-.263.766-4.517-1.022zm9.033 2.045.264-.767 2.258.511-.264.767-.264.767-2.258-.511z",viewW:100,viewH:35}},{id:"shelf_wall",group:"furniture",category:"shelving",w:100,h:25,art:{d:"M18.36 3.15v2.567c0 1.012-1.034 1.833-2.31 1.833h-.924c-1.275 0-2.309-.82-2.309-1.833V3.15m73.905 0v2.567c0 1.012-1.034 1.833-2.31 1.833h-.924c-1.275 0-2.31-.82-2.31-1.833V3.15m-75.29 0H93.65c1.276 0 2.31.82 2.31 1.833V19.65c0 1.012-1.034 1.833-2.31 1.833H5.888c-1.275 0-2.31-.82-2.31-1.833V4.983c0-1.012 1.035-1.833 2.31-1.833",viewW:100,viewH:25}},{id:"cooktop_two",group:"appliance",category:"cooktop",w:30,h:50,art:{d:"M15 30.982v-1.71m3.086 5.128h1.542M15 39.527v-1.709M10.372 34.4h1.542M5.84 46.364h18.322c1.331 0 2.41-1.196 2.41-2.67V6.306c0-1.475-1.079-2.67-2.41-2.67H5.839c-1.331 0-2.41 1.195-2.41 2.67v37.386c0 1.475 1.079 2.67 2.41 2.67m14.56-29.91c0 3.304-2.417 5.982-5.4 5.982-2.981 0-5.4-2.678-5.4-5.981s2.419-5.982 5.4-5.982c2.983 0 5.4 2.678 5.4 5.982M18.858 34.4c0 2.36-1.727 4.273-3.857 4.273s-3.857-1.913-3.857-4.273S12.87 30.127 15 30.127s3.857 1.913 3.857 4.273",viewW:30,viewH:50}},{id:"stove",group:"appliance",category:"cooktop",w:60,h:60,art:{d:"M41.4 35.261a.877.877 0 1 0-1.754 0zm4.384 6.138a.877.877 0 1 0 0-1.753zM41.4 44.03a.877.877 0 1 0-1.753 0zm-4.384-2.63a.877.877 0 1 0 0-1.754zM5.885 3.691v.877h48.23V2.815H5.885zm50.422 2.192h-.877v48.23h1.754V5.885zm-2.192 50.423v-.877H5.885v1.754h48.23zM3.692 54.115h.877V5.885H2.815v48.23zm2.192 2.192v-.877a1.315 1.315 0 0 1-1.315-1.315H2.815a3.07 3.07 0 0 0 3.07 3.069zm50.423-2.192h-.877c0 .726-.589 1.315-1.315 1.315v1.754a3.07 3.07 0 0 0 3.069-3.07zM54.115 3.692v.877c.726 0 1.315.589 1.315 1.315h1.754a3.07 3.07 0 0 0-3.07-3.069zm-48.23 0v-.877a3.07 3.07 0 0 0-3.07 3.07h1.754c0-.727.589-1.316 1.315-1.316zm19.73 36.83h-.877a5.26 5.26 0 0 1-5.262 5.262v1.754a7.015 7.015 0 0 0 7.016-7.015zm-6.139 6.139v-.877a5.26 5.26 0 0 1-5.261-5.261h-1.754a7.015 7.015 0 0 0 7.015 7.015zm-6.138-6.138h.877a5.26 5.26 0 0 1 5.261-5.262v-1.754a7.015 7.015 0 0 0-7.015 7.016zm6.138-6.139v.877a5.26 5.26 0 0 1 5.262 5.262h1.754a7.015 7.015 0 0 0-7.016-7.016zm27.185-14.908h-.877a5.26 5.26 0 0 1-5.261 5.262v1.754a7.015 7.015 0 0 0 7.015-7.016zm-6.138 6.139v-.877a5.26 5.26 0 0 1-5.262-5.262h-1.754a7.015 7.015 0 0 0 7.016 7.016zm-6.139-6.139h.877a5.26 5.26 0 0 1 5.262-5.261v-1.754a7.015 7.015 0 0 0-7.016 7.015zm6.139-6.138v.877a5.26 5.26 0 0 1 5.261 5.261h1.754a7.015 7.015 0 0 0-7.015-7.015zm4.384 27.185h-.877a3.51 3.51 0 0 1-3.507 3.507v1.754a5.26 5.26 0 0 0 5.261-5.261zm-4.384 4.384v-.877a3.51 3.51 0 0 1-3.508-3.507H35.26a5.26 5.26 0 0 0 5.262 5.261zm-4.385-4.384h.877a3.51 3.51 0 0 1 3.508-3.508V35.26a5.26 5.26 0 0 0-5.262 5.262zm4.385-4.385v.877a3.51 3.51 0 0 1 3.507 3.508h1.754a5.26 5.26 0 0 0-5.261-5.262zm0 .877h.876V35.26h-1.753v1.754zm3.508 3.508v.876h1.753v-1.753h-1.753zm-3.508 5.261h.876V44.03h-1.753v1.754zm-5.261-5.261v.876h1.753v-1.753h-1.753zm-9.647-21.047h-.877a5.26 5.26 0 0 1-5.262 5.262v1.754a7.015 7.015 0 0 0 7.016-7.016zm-6.139 6.139v-.877a5.26 5.26 0 0 1-5.261-5.262h-1.754a7.015 7.015 0 0 0 7.015 7.016zm-6.138-6.139h.877a5.26 5.26 0 0 1 5.261-5.261v-1.754a7.015 7.015 0 0 0-7.015 7.015zm6.138-6.138v.877a5.26 5.26 0 0 1 5.262 5.261h1.754a7.015 7.015 0 0 0-7.016-7.015z",viewW:60,viewH:60}},{id:"tv",group:"appliance",category:"tv",w:120,h:28,art:{d:"M45.134 10.766H6.18c-1.292 0-2.34.848-2.34 1.894v2.68c0 1.045 1.048 1.893 2.34 1.893h38.954m0-6.467V5.288c0-1.046 1.048-1.894 2.34-1.894h26.704c1.292 0 2.34.848 2.34 1.894v5.478m-31.384 0h31.384m0 0h37.302c1.293 0 2.34.848 2.34 1.894v2.68c0 1.045-1.047 1.893-2.34 1.893H76.518m-31.384 0v5.479c0 1.046 1.048 1.894 2.34 1.894h26.704c1.292 0 2.34-.848 2.34-1.894v-5.479m-31.384 0h31.384",viewW:120,viewH:28}},{id:"tv_wall",group:"appliance",category:"tv",w:120,h:15,art:{d:"M35.664 11.4c-.517 0-.936.269-.936.6s.419.6.936.6zm49.553 1.2c.517 0 .936-.269.936-.6s-.42-.6-.936-.6zM6.18 3v.6h107.64V2.4H6.18zm109.98 1.5h-.936v1.8h1.872V4.5zm-2.34 3.3v-.6H6.18v1.2h107.64zM3.84 6.3h.936V4.5H2.904v1.8zm2.34 1.5v-.6c-.775 0-1.404-.403-1.404-.9H2.904c0 1.16 1.467 2.1 3.276 2.1zm109.98-1.5h-.936c0 .497-.629.9-1.404.9v1.2c1.81 0 3.276-.94 3.276-2.1zM113.82 3v.6c.775 0 1.404.403 1.404.9h1.872c0-1.16-1.467-2.1-3.276-2.1zM6.18 3v-.6c-1.81 0-3.276.94-3.276 2.1h1.872c0-.497.629-.9 1.404-.9zm61.308 4.2h-.936V12h1.872V7.2zM52.512 12h.936V7.2h-1.872V12zm-16.848 0v.6h49.553v-1.2H35.664z",viewW:120,viewH:15}},{id:"toilet",group:"sanitary",category:"toilet",w:40,h:70,art:{d:"M3.555 48.976c0 9.55 7.125 17.29 15.914 17.29h1.061c8.79 0 15.914-7.74 15.914-17.29V5.966c0-1.233-.92-2.233-2.056-2.233H5.611c-1.135 0-2.056 1-2.056 2.234z M7.666 49.12c0 7.003 5.225 12.68 11.67 12.68h1.327c6.445 0 11.67-5.677 11.67-12.68V31.873c0-1.233-.92-2.233-2.056-2.233H9.722c-1.135 0-2.056 1-2.056 2.233zM33.792 3.733c1.465 0 2.652 1.29 2.652 2.882v12.103c0 1.592-1.187 2.882-2.652 2.882H6.208c-1.465 0-2.653-1.29-2.653-2.882V6.615c0-1.591 1.188-2.882 2.653-2.882z M24.11 50.186c0 2.467-1.84 4.467-4.11 4.467s-4.111-2-4.111-4.467 1.84-4.466 4.11-4.466 4.112 2 4.112 4.466",viewW:40,viewH:70}},{id:"toilet_built_in",group:"sanitary",category:"toilet",w:40,h:55,art:{d:"M3.555 34.56c0 9.263 7.125 16.774 15.914 16.774h1.061c8.79 0 15.914-7.51 15.914-16.775V5.833c0-1.196-.92-2.166-2.056-2.166H5.611c-1.135 0-2.056.97-2.056 2.166z M7.666 34.7c0 6.793 5.225 12.3 11.67 12.3h1.327c6.445 0 11.67-5.507 11.67-12.3V14.5c0-1.197-.92-2.167-2.056-2.167H9.722c-1.135 0-2.056.97-2.056 2.167z M24.11 35.733c0 2.394-1.84 4.334-4.11 4.334s-4.111-1.94-4.111-4.334 1.84-4.333 4.11-4.333 4.112 1.94 4.112 4.333",viewW:40,viewH:55}},{id:"bathtub",group:"sanitary",category:"bathtub",w:170,h:75,art:{d:"M3.885 6c0-1.243 1.068-2.25 2.386-2.25h157.458c1.318 0 2.386 1.007 2.386 2.25v63c0 1.243-1.068 2.25-2.386 2.25H6.27c-1.318 0-2.386-1.007-2.386-2.25z M13.428 37.5c0-13.669 11.75-24.75 26.243-24.75h90.658c14.493 0 26.243 11.081 26.243 24.75s-11.75 24.75-26.243 24.75H39.67c-14.493 0-26.243-11.081-26.243-24.75 M31.56 37.5c0 2.237-1.923 4.05-4.295 4.05s-4.294-1.813-4.294-4.05 1.923-4.05 4.294-4.05c2.372 0 4.295 1.813 4.295 4.05",viewW:170,viewH:75}},{id:"bathtub_corner",group:"sanitary",category:"bathtub",w:140,h:140,art:{d:"M3.862 6.224a2.36 2.36 0 0 1 2.362-2.362H133.97c1.197 0 2.168.97 2.168 2.168 0 71.857-58.251 130.108-130.108 130.108a2.17 2.17 0 0 1-2.168-2.168z M13.31 32.207c0-10.437 8.46-18.897 18.897-18.897h87.628a2.13 2.13 0 0 1 2.13 2.13c0 58.833-47.692 106.526-106.524 106.526a2.13 2.13 0 0 1-2.13-2.131z M32.207 27.483a4.724 4.724 0 1 1-9.449 0 4.724 4.724 0 0 1 9.449 0",viewW:140,viewH:140}},{id:"bidet",group:"sanitary",category:"bidet",w:40,h:60,art:{d:"M22.466 3.692v10.085c0 1.21-.92 2.192-2.055 2.192h-.822c-1.136 0-2.056-.981-2.056-2.192V3.692m4.933 8.77h2.878c1.135 0 2.056-.982 2.056-2.193v-.877c0-1.21-.92-2.192-2.056-2.192h-2.878m-4.933 5.262h-2.878c-1.135 0-2.055-.982-2.055-2.193v-.877c0-1.21.92-2.192 2.055-2.192h2.878M5.611 3.692h28.777c1.136 0 2.056.981 2.056 2.192v33.45c0 9.374-7.125 16.973-15.914 16.973h-1.06c-8.79 0-15.915-7.599-15.915-16.972V5.885c0-1.212.92-2.193 2.056-2.193m4.11 15.785h20.556c1.136 0 2.056.981 2.056 2.192v17.807c0 6.874-5.225 12.447-11.67 12.447h-1.327c-6.445 0-11.67-5.573-11.67-12.447V21.67c0-1.21.92-2.192 2.056-2.192m13.567 7.892c0 1.937-1.472 3.508-3.288 3.508-1.817 0-3.29-1.57-3.29-3.508s1.473-3.507 3.29-3.507 3.288 1.57 3.288 3.507",viewW:40,viewH:60}},{id:"bidet_built_in",group:"sanitary",category:"bidet",w:40,h:50,art:{d:"M17.318 12.182h-8.06c-.88 0-1.592.74-1.592 1.654v16.126c0 6.698 5.225 12.129 11.67 12.129h1.327c6.445 0 11.67-5.43 11.67-12.13V13.837c0-.914-.713-1.654-1.591-1.654H22.68m-.215-8.545v9.827c0 1.18-.92 2.136-2.055 2.136h-.822c-1.136 0-2.056-.956-2.056-2.136V3.637m10.689 8.545V3.637m-16.445 0v8.545M5.611 3.637h28.777c1.136 0 2.056.956 2.056 2.136v24.051c0 9.135-7.125 16.54-15.914 16.54h-1.06c-8.79 0-15.915-7.405-15.915-16.54V5.773c0-1.18.92-2.136 2.056-2.136M23.289 23.29c0 1.888-1.473 3.418-3.29 3.418s-3.288-1.53-3.288-3.418 1.472-3.418 3.289-3.418c1.816 0 3.288 1.53 3.288 3.418",viewW:40,viewH:50}},{id:"kitchen_sink",group:"sanitary",category:"kitchen_sink",w:60,h:50,art:{d:"M38.188 8.7h10.598c1.211 0 2.193.956 2.193 2.136v28.2c0 1.18-.982 2.136-2.193 2.136H11.08c-1.21 0-2.192-.956-2.192-2.136v-28.2c0-1.18.981-2.136 2.192-2.136h10.599m10.885-5.128V15.11c0 1.18-.981 2.136-2.192 2.136h-.877c-1.21 0-2.192-.957-2.192-2.136V3.572m5.261 7.691h3.07c1.21 0 2.192-.956 2.192-2.136v-.855c0-1.18-.982-2.136-2.192-2.136h-3.07m-5.261 5.127h-3.07c-1.21 0-2.192-.956-2.192-2.136v-.855c0-1.18.982-2.136 2.193-2.136h3.069M5.817 46.299h48.231c1.21 0 2.192-.956 2.192-2.136V5.709c0-1.18-.981-2.137-2.192-2.137H5.818c-1.211 0-2.193.957-2.193 2.137v38.454c0 1.18.982 2.136 2.192 2.136M33.44 25.79c0 1.888-1.57 3.418-3.507 3.418s-3.508-1.53-3.508-3.418c0-1.887 1.57-3.418 3.508-3.418s3.507 1.53 3.507 3.418",viewW:60,viewH:50}},{id:"kitchen_sink_double",group:"sanitary",category:"kitchen_sink",w:90,h:50,art:{d:"M53.173 12.182c-.468 0-.847.354-.847.79s.38.79.847.79zm21.979 1.58c.467 0 .846-.353.846-.79 0-.436-.379-.79-.846-.79zM6.01 3.572v.79h77.842v-1.58H6.01zM86.14 5.71h-.847v38.454h1.694V5.709zM83.85 46.3v-.79H6.01v1.58h77.842zM3.72 44.163h.847V5.709H2.873v38.454zm2.29 2.136v-.79c-.797 0-1.443-.603-1.443-1.346H2.873c0 1.616 1.404 2.926 3.137 2.926zm80.131-2.136h-.847c0 .743-.646 1.346-1.442 1.346v1.58c1.732 0 3.136-1.31 3.136-2.926zm-2.29-40.59v.79c.797 0 1.443.602 1.443 1.346h1.694c0-1.617-1.404-2.927-3.136-2.927zm-77.841 0v-.79c-1.733 0-3.137 1.31-3.137 2.926h1.694c0-.744.646-1.347 1.443-1.347zm5.494 37.599v.79h28.39v-1.58h-28.39zm30.68-2.136h.846v-28.2h-1.694v28.2zm-32.97-28.2h-.846v28.2h1.694v-28.2zM39.895 8.7v-.79h-5.669v1.58h5.669zm-22.721 0v-.79h-5.669v1.58h5.669zm-7.958 2.136h.847c0-.744.645-1.346 1.442-1.346V7.91c-1.732 0-3.136 1.31-3.136 2.926zm32.968 0h.847c0-1.616-1.404-2.927-3.136-2.927v1.58c.797 0 1.442.603 1.442 1.347zm-2.29 30.336v.79c1.733 0 3.137-1.31 3.137-2.926h-1.694c0 .743-.645 1.346-1.442 1.346zm-28.389 0v-.79c-.797 0-1.442-.603-1.442-1.346H8.368c0 1.616 1.404 2.926 3.136 2.926zm13.737-23.927v.79h.916v-1.58h-.916zm3.205-2.136h.847V3.572H27.6V15.11zM22.952 3.572h-.847V15.11h1.693V3.572zm3.205 13.673v.79c1.732 0 3.136-1.31 3.136-2.926H27.6c0 .743-.646 1.346-1.443 1.346zm-.916 0v-.79c-.797 0-1.443-.603-1.443-1.346h-1.693c0 1.616 1.404 2.926 3.136 2.926zm3.205-5.982v.79h3.206v-1.58h-3.206zm5.495-2.136h.847v-.855h-1.694v.855zm-2.29-2.991v-.79h-3.205v1.58h3.206zm2.29 2.136h.847c0-1.616-1.404-2.926-3.136-2.926v1.58c.796 0 1.442.603 1.442 1.346zm-2.29 2.991v.79c1.733 0 3.137-1.31 3.137-2.926h-1.694c0 .743-.646 1.346-1.442 1.346zm-11.905 0v.79h3.206v-1.58h-3.206zm3.206-5.127v-.79h-3.206v1.58h3.206zm-5.495 2.136h-.847v.855h1.694v-.855zm2.29-2.136v-.79c-1.733 0-3.137 1.31-3.137 2.926h1.694c0-.743.646-1.346 1.442-1.346zm0 5.127v-.79c-.797 0-1.443-.603-1.443-1.346H16.61c0 1.616 1.404 2.926 3.136 2.926zm9.615 14.527h-.847c0 1.452-1.26 2.628-2.816 2.628v1.58c2.49 0 4.51-1.884 4.51-4.208zM25.7 29.208v-.79c-1.555 0-2.816-1.176-2.816-2.628H21.19c0 2.324 2.02 4.209 4.51 4.209zm-3.663-3.418h.847c0-1.451 1.26-2.628 2.816-2.628v-1.58c-2.49 0-4.51 1.884-4.51 4.208zm3.663-3.418v.79c1.555 0 2.816 1.177 2.816 2.628h1.694c0-2.324-2.02-4.208-4.51-4.208zm24.268 18.8v.79h28.39v-1.58h-28.39zm30.68-2.136h.846v-28.2H79.8v28.2zM78.356 8.7v-.79h-28.39v1.58h28.39zm-30.68 2.136h-.846v28.2h1.694v-28.2zm2.29-2.136v-.79c-1.732 0-3.136 1.31-3.136 2.926h1.694c0-.744.646-1.346 1.442-1.346zm30.68 2.136h.846c0-1.616-1.404-2.927-3.136-2.927v1.58c.797 0 1.443.603 1.443 1.347zm-2.29 30.336v.79c1.732 0 3.136-1.31 3.136-2.926H79.8c0 .743-.646 1.346-1.443 1.346zm-28.39 0v-.79c-.796 0-1.442-.603-1.442-1.346H46.83c0 1.616 1.404 2.926 3.136 2.926zm3.206-28.2v.79h21.979v-1.58h-21.98z",viewW:90,viewH:50}}].map(e=>({id:e.id,group:e.group,category:e.category,w:e.w,h:e.h,art:e.art})),...nl.filter(e=>rl.has(e.id))],sl=new Map(ol.map(e=>[e.id,e]));function al(e){return e&&sl.get(e)||null}function ll(e){return ol.filter(t=>t.group===e)}function cl(e){const t=al(e);return t?{w:t.w,h:t.h}:{w:60,h:60}}function hl(e,t,i=Ta,n=1e3){const r=Number(t)>0?Number(t):5;return(Number(e)||0)/r*i/n}const dl=5e-4,ul=Sa;function pl(e){return Number.isFinite(e)?Math.max(dl,Math.min(ul,e)):dl}function ml(e){return Number.isFinite(e)?Math.max(1,Math.min(1e4,e)):1}const _l=e=>{const t=Math.round(1e3*e)/1e3;return Object.is(t,-0)?"0":String(t)};function fl(e){const t=al(e);if(!t)return null;if(t.art)return t.art;const i=function(e,t,i){if(!e.g)return"";const n=e=>_l(e*t),r=e=>_l(e*i),o=[];for(const s of e.g)if("r"===s[0]){const[,e,t,i,a]=s;o.push(`M${n(e)} ${r(t)}H${n(e+i)}V${r(t+a)}H${n(e)}Z`)}else if("l"===s[0]){const[,e,t,i,a]=s;o.push(`M${n(e)} ${r(t)}L${n(i)} ${r(a)}`)}else if("e"===s[0]){const[,e,a,l,c]=s;o.push(`M${n(e-l)} ${r(a)}A${_l(l*t)} ${_l(c*i)} 0 0 1 ${n(e+l)} ${r(a)}A${_l(l*t)} ${_l(c*i)} 0 0 1 ${n(e-l)} ${r(a)}Z`)}else{const e=s.slice(1);if(e.length<4)continue;let t=`M${n(e[0])} ${r(e[1])}`;for(let i=2;i+10?n:Number.isFinite(r)&&r>0?r:1,s=Number(t),a=o*(Number.isFinite(s)&&s>0?s:1);return Number.isFinite(a)&&a>0?a:o}const vl=e=>{let t=(e%360+360)%360;return t>180&&(t-=360),t};function yl(e,t,i,n,r,o=0){let s=null,a=r;for(const r of n){const[n,l,c,h]=r,d=c-n,u=h-l,p=d*d+u*u;if(!p)continue;const m=Math.sqrt(p);let _=((e-n)*d+(t-l)*u)/p;_=Math.max(0,Math.min(1,_));let f=n+_*d,g=l+_*u;const v=Math.hypot(e-f,t-g);if(!(v0){const e=Math.round(_*m/o)*o;f=n+e/m*d,g=l+e/m*u}s={cx:f+y*(i/2),cy:g+b*(i/2),angle:vl(180*Math.atan2(-y,b)/Math.PI),dist:v}}return s}function bl(e){const{symbol:t,widthCm:i,depthCm:n,point:r,canvasW:o,canvasH:s,cellCm:a,gridPitch:l,walls:c,wallReach:h,free:d=!1}=e;if(!(fl(t)&&s>0&&Number.isFinite(r[0])&&Number.isFinite(r[1])))return null;const u=pl(hl(i,a,l,o)),p=pl(hl(n,a,l,o));let m=r[0],_=r[1],f=0;const g=d?null:yl(m,_,p*s,c,h,l);return g&&(m=g.cx,_=g.cy,f=g.angle),{symbol:t,x:Pa(m/o-u/2),y:Pa(_/s-p/2),w:u,h:p,angle:Number(f.toFixed(2))}}const wl=15e3,kl=2e3,xl=new WeakMap;function $l(e,t){let i=xl.get(e);if(!i){const t=new Set;i={hiddenAt:"hidden"===e.visibilityState?Date.now():0,token:0,subscribers:t,onVisibility:()=>{const t=xl.get(e);if(!t)return;const i=Date.now();if("hidden"===e.visibilityState){t.hiddenAt||(t.hiddenAt=i);const e={kind:"hidden",token:t.token,at:i,hiddenFor:0,long:!1};for(const i of[...t.subscribers])i(e);return}const n=t.hiddenAt?Math.max(0,i-t.hiddenAt):0;t.hiddenAt=0,t.token++;const r={kind:"visible",token:t.token,at:i,hiddenFor:n,long:n>=wl};for(const e of[...t.subscribers])e(r)},onPageShow:t=>{const i=xl.get(e);if(!i)return;const n=Date.now();i.token++;const r={kind:"pageshow",token:i.token,at:n,hiddenFor:0,long:!!t.persisted,persisted:!!t.persisted};for(const e of[...i.subscribers])e(r)}},xl.set(e,i),e.addEventListener("visibilitychange",i.onVisibility),e.defaultView?.addEventListener("pageshow",i.onPageShow)}return i.subscribers.add(t),()=>{const i=xl.get(e);i&&(i.subscribers.delete(t),i.subscribers.size||(e.removeEventListener("visibilitychange",i.onVisibility),e.defaultView?.removeEventListener("pageshow",i.onPageShow),xl.delete(e)))}}const Sl={now:()=>Date.now(),setTimeout:(e,t)=>window.setTimeout(e,t),clearTimeout:e=>window.clearTimeout(e),requestAnimationFrame:e=>window.requestAnimationFrame(e),cancelAnimationFrame:e=>window.cancelAnimationFrame(e)};class Ml{constructor(e,t=Sl){this.onChange=e,this.clock=t,this._state="steady",this._token=0,this._frameFingerprint="",this._hasCompleteFrame=!1,this._overlayPhase="none",this._recoveryReason=null,this._overlayTimer=0,this._overlayRaf=0,this._overlayOpaqueAt=0,this._barrierRafs=new Set,this._trace=[],this._disposed=!1}get state(){return this._state}get token(){return this._token}get frameFingerprint(){return this._frameFingerprint}get hasCompleteFrame(){return this._hasCompleteFrame}get overlayPhase(){return this._overlayPhase}get overlayVisible(){return"none"!==this._overlayPhase}get overlayBlocksInteraction(){return this.overlayVisible}get recoveryReason(){return this._recoveryReason}get trace(){return this._trace.map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}note(e,t={}){this.record(e,void 0,t)}record(e,t,i={}){this._trace.push({at:this.clock.now(),token:this._token,event:e,state:this._state,...t?{reason:t}:{},...i}),this._trace.length>80&&this._trace.splice(0,this._trace.length-80)}changed(){this._disposed||this.onChange()}adoptCompleteFrame(e){e&&(this._hasCompleteFrame=!0,this._frameFingerprint=e,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-adopted"),this.changed())}markCompleteFrame(e){e&&(this._hasCompleteFrame=!0,this._frameFingerprint=e,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-complete"),this.changed())}refreshCompleteFrame(e){!this._disposed&&this._hasCompleteFrame&&"steady"===this._state&&e&&e!==this._frameFingerprint&&(this._frameFingerprint=e,this.record("frame-refreshed"))}visibility(e){return"hidden"===e.kind?(this.record("visibility-hidden"),this._token):e.long||"visible"!==e.kind&&"pageshow"!==e.kind?this.beginCandidate("pageshow"===e.kind?"pageshow":"long-resume"):(this.record("pageshow"===e.kind?"pageshow-noop":"visibility-visible-quick"),this._token)}beginCandidate(e,t="plan"){return this._token++,this._recoveryReason=t,this._hasCompleteFrame?(this._state="connection"===t?"offline-stale":"holding",this.clearOverlay()):this.overlayVisible?this._state="overlay-visible":(this._state="overlay-pending",this.scheduleOverlay(t)),this.record("candidate-start",e),this.changed(),this._token}connectionLost(){return this.beginCandidate("connection-lost","connection")}candidateReady(e){return!this._disposed&&e===this._token&&(this._state="candidate-ready",this.record("candidate-ready"),this.changed(),!0)}async commitAfterPaint(e,t){if(this._disposed||e!==this._token)return!1;let i=!0;const n=(async()=>{for(await t.updateComplete();i&&!this._disposed&&e===this._token;)if(t.stageValid()&&t.assetsReady()){if(await this.nextFrame(),e!==this._token||!t.stageValid()||!t.assetsReady())continue;if(await this.nextFrame(),e===this._token&&t.stageValid()&&t.assetsReady())return!0}else await this.nextFrame();return!1})();let r=0;const o=new Promise(e=>{r=this.clock.setTimeout(()=>e({ready:!1,timedOut:!0}),kl)}),s=await Promise.race([n.then(e=>({ready:e,timedOut:!1})),o]);return i=!1,this.clock.clearTimeout(r),!this._disposed&&e===this._token&&(!(!s.ready&&!s.timedOut)&&(s.ready?(this._hasCompleteFrame=!0,this._frameFingerprint=t.frameFingerprint(),this.record("paint-barrier"),this.finishOverlayAfterCommit(),!0):(this.record("paint-barrier-timeout"),this._hasCompleteFrame?(this._state="connection"===this._recoveryReason?"offline-stale":"steady",this.clearOverlay()):(this._state="recovery-error","opaque"!==this._overlayPhase&&this.showOverlayNow()),this.changed(),!1)))}retry(e=this._recoveryReason||"plan"){return this.beginCandidate("retry",e)}nextFrame(){return new Promise(e=>{const t=this.clock.requestAnimationFrame(()=>{this._barrierRafs.delete(t),e()});this._barrierRafs.add(t)})}scheduleOverlay(e){this.clearOverlayTimer(),this._recoveryReason=e,this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._hasCompleteFrame||"overlay-pending"!==this._state&&"candidate-ready"!==this._state||(this._state="overlay-visible",this._overlayPhase="entering",this.record("overlay-enter"),this.changed(),this._overlayRaf=this.clock.requestAnimationFrame(()=>{this._overlayRaf=0,"entering"===this._overlayPhase&&(this._overlayPhase="fading-in",this.changed(),this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,"fading-in"===this._overlayPhase&&(this._overlayPhase="opaque",this._overlayOpaqueAt=this.clock.now(),this.record("overlay-opaque"),this.changed())},150))}))},150)}showOverlayNow(){this.clearOverlayTimer(),this._overlayPhase="opaque",this._overlayOpaqueAt=this.clock.now(),this.record("overlay-error")}finishOverlayAfterCommit(){if(this.clearOverlayTimer(),"none"===this._overlayPhase||"entering"===this._overlayPhase||"fading-in"===this._overlayPhase)return this.clearOverlay(),this._state="steady",this._recoveryReason=null,this.record("candidate-committed"),void this.changed();const e=Math.max(0,this._overlayOpaqueAt+250-this.clock.now());this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._overlayPhase="leaving",this.record("overlay-leave"),this.changed(),this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._overlayPhase="none",this._state="steady",this._recoveryReason=null,this.record("candidate-committed"),this.changed()},180)},e)}clearOverlayTimer(){this._overlayTimer&&this.clock.clearTimeout(this._overlayTimer),this._overlayTimer=0}clearOverlay(){this.clearOverlayTimer(),this._overlayRaf&&this.clock.cancelAnimationFrame(this._overlayRaf),this._overlayRaf=0,this._overlayPhase="none",this._overlayOpaqueAt=0}dispose(){this._disposed=!0,this._token++,this.clearOverlay();for(const e of this._barrierRafs)this.clock.cancelAnimationFrame(e);this._barrierRafs.clear()}}function Rl(e){let t=2166136261,i=2654435769;const n=new WeakSet,r=e=>{for(let n=0;n>>0)+(i>>>2)}},o=e=>{if(null===e)return void r("n");const t=typeof e;if("string"===t)return void r(`s${e.length}:${e}`);if("number"===t)return void r(`d${Number.isNaN(e)?"NaN":String(e)}`);if("boolean"===t)return void r(e?"t":"f");if("undefined"===t)return void r("u");if("object"!==t)return void r(`${t}:${String(e)}`);const i=e;if(n.has(i))r("[cycle]");else{if(n.add(i),Array.isArray(e)){r("[");for(const t of e)o(t);return r("]"),void n.delete(i)}r("{");for(const t of Object.keys(e).sort())r(t),o(e[t]);r("}"),n.delete(i)}};return o(e),`${(t>>>0).toString(16).padStart(8,"0")}${(i>>>0).toString(16).padStart(8,"0")}`}function Tl(e){return Rl(e)}const Cl=new WeakMap;class Dl{constructor(e,t=()=>Date.now()){this.onUpdate=e,this.now=t,this.fallbackAuthority={},this.referenceOwner={},this.referenced=new Set,this.queued=new Set,this.disposed=!1,this.sharedUpdate=()=>{this.disposed||this.onUpdate()}}bind(e){const t=((e,t)=>{const i=e?.connection||e?.callWS||t;return!i||"object"!=typeof i&&"function"!=typeof i?t:i})(e,this.fallbackAuthority);return this.shared&&this.authority===t||(this.shared&&(this.shared.listeners.delete(this.sharedUpdate),this.shared.references.delete(this.referenceOwner)),this.authority=t,this.shared=(e=>{let t=Cl.get(e);return t||(t={signed:{},queued:new Set,inFlight:new Map,retry:new Map,listeners:new Set,settlers:new Set,references:new Map},Cl.set(e,t)),t})(t),this.disposed||(this.shared.listeners.add(this.sharedUpdate),this.shared.references.set(this.referenceOwner,new Set(this.referenced)))),this.shared}start(e,t){this.disposed=!1,this.referenced=new Set(t());const i=this.bind(e());i.listeners.add(this.sharedUpdate),i.references.set(this.referenceOwner,new Set(this.referenced)),this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(e(),t()),288e5)}dispose(){if(this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.shared){this.shared.listeners.delete(this.sharedUpdate),this.shared.references.delete(this.referenceOwner);for(const e of this.queued)this.shared.queued.delete(e)}this.queued.clear()}invalidate(e){const t=this.bind(e);clearTimeout(this.batchTimer),this.queued.clear(),t.queued.clear(),t.inFlight.clear(),t.retry.clear(),t.signed={}}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(e,t){const i=_n(t);if(!i.startsWith("/api/houseplan/content/"))return i;const n=this.bind(e);this.referenced.add(i),n.references.set(this.referenceOwner,new Set(this.referenced));const r=n.signed[i],o=r?this.now()-r.at:1/0;return o{const t=[...this.queued];this.queued.clear();for(const e of t)i.queued.delete(e);this.sign(e,t)},30))}sign(e,t){if(!t.length||!e?.callWS)return;const i=this.bind(e);for(const n of function(e,t){const i=Math.max(1,Math.floor(t)),n=[];for(let t=0;t{const n=i.inFlight.get(e);return!(void 0!==n&&t-n<15e3)&&(i.inFlight.set(e,t),!0)});r.length&&e.callWS({type:"houseplan/content/sign",paths:r}).then(e=>{const t=this.now();let n=0,o=!1;for(const s of r){const r=e?.urls?.[s];if("string"==typeof r&&r){const e=i.signed[s];e?.loaded&&e.url!==r?(e.pending={url:r,at:t},this.preloadReplacement(i,s,r,t)):(i.signed[s]={url:r,at:t,loaded:e?.url===r&&!!e.loaded},o=!0),i.retry.delete(s),n++}else this.backOff(i,s)}if(n&&(this.trimShared(i),o))for(const e of[...i.listeners])e()}).catch(()=>{for(const e of r)this.backOff(i,e)}).finally(()=>{for(const e of r)i.inFlight.get(e)===t&&i.inFlight.delete(e);for(const e of[...i.settlers])e()})}}preloadReplacement(e,t,i,n){const r=()=>{const r=e.signed[t];if(r?.pending?.url===i){e.signed[t]={url:i,at:n,loaded:!0},e.retry.delete(t),this.trimShared(e);for(const t of[...e.listeners])t()}},o=()=>{const n=e.signed[t];n?.pending?.url===i&&(delete n.pending,this.backOff(e,t))};if("undefined"==typeof Image)return void r();const s=new Image;s.onload=()=>{("function"==typeof s.decode?s.decode():Promise.resolve()).then(r).catch(o)},s.onerror=o,s.src=i}preloadCurrentImage(e,t,i){if(i.loaded)return Promise.resolve(!0);if(i.preload)return i.preload;if("undefined"==typeof Image)return i.loaded=!0,Promise.resolve(!0);const n=i.url;return i.preload=new Promise(r=>{const o=new Image;let s=!1;const a=o=>{if(s)return;s=!0,clearTimeout(l);const a=e.signed[t];if(a===i&&delete a.preload,o&&a===i&&a.url===n){a.loaded=!0;for(const t of[...e.listeners])t();r(!0)}else o||a!==i||this.backOff(e,t),r(!1)};o.onload=()=>{("function"==typeof o.decode?o.decode():Promise.resolve()).then(()=>a(!0)).catch(()=>a(!1))},o.onerror=()=>a(!1);const l=setTimeout(()=>a(!1),kl);o.src=n}),i.preload}prepareImage(e,t){const i=_n(t);if(!i.startsWith("/api/houseplan/content/"))return Promise.resolve(!0);const n=this.bind(e),r=()=>{const t=n.signed[i];if(t&&this.now()-t.atthis.now()&&!n.inFlight.has(i)&&!n.queued.has(i)?Promise.resolve(!1):null},o=r();return o?(s=o,new Promise(e=>{let t=!1;const i=i=>{t||(t=!0,clearTimeout(n),e(i))},n=setTimeout(()=>i(!1),kl);s.then(i).catch(()=>i(!1))})):new Promise(e=>{let t=!1,i=null;const o=i=>{t||(t=!0,clearTimeout(a),n.settlers.delete(s),e(i))},s=()=>{t||i||(i=r(),i&&i.then(o).catch(()=>o(!1)))},a=setTimeout(()=>o(!1),kl);n.settlers.add(s),s()});var s}backOff(e,t){const i=e.retry.get(t)?.delay||0,n=Math.min(6e4,i?2*i:2e3);e.retry.set(t,{notBefore:this.now()+n,delay:n})}trimShared(e){const t=Object.entries(e.signed);if(t.length<=512)return;const i=new Set(e.inFlight.keys());for(const t of e.references.values())for(const e of t)i.add(e);for(const[e,n]of t)(n.pending||n.preload)&&i.add(e);t.sort((e,t)=>t[1].at-e[1].at);const n=t.filter(([e])=>i.has(e));for(const e of t){if(n.length>=512)break;i.has(e[0])||n.push(e)}e.signed=Object.fromEntries(n)}resign(e,t){const i=this.bind(e);this.referenced=new Set(t),i.references.set(this.referenceOwner,new Set(this.referenced));const n=this.now(),r=new Set;for(const e of i.references.values())for(const t of e)r.add(t);const o={};for(const[e,t]of Object.entries(i.signed))r.has(e)&&n-t.at!!o[e]&&!o[e].pending))}markLoaded(e,t,i){const n=_n(t);if(!n.startsWith("/api/houseplan/content/"))return;const r=this.bind(e).signed[n];if(r&&(!i||r.url===i)&&!r.loaded){r.loaded=!0;for(const t of[...this.bind(e).listeners])t()}}isReady(e,t){const i=_n(t);if(!i.startsWith("/api/houseplan/content/"))return!0;const n=this.bind(e).signed[i];return!!n&&this.now()-n.ate.id===t)}var Al="M11,18H13V16H11V18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,6A4,4 0 0,0 8,10H10A2,2 0 0,1 12,8A2,2 0 0,1 14,10C14,12 11,11.75 11,15H13C13,12.75 16,12.5 16,10A4,4 0 0,0 12,6Z";function Pl(e,t,i){return[e[0]*t+e[1]*i+e[2],e[3]*t+e[4]*i+e[5]]}function Ol(e,t){let i=0;for(const[n,r]of t){const t=Pl(e,n[0],n[1]);i=Math.max(i,Math.hypot(t[0]-r[0],t[1]-r[1]))}return i}const Fl=e=>{if(null==e||""===e)return null;const t=Number(e);return Number.isFinite(t)?t:null},Il=e=>{const t=e,i=Fl(Array.isArray(t)?t[0]:t?.x),n=Fl(Array.isArray(t)?t[1]:t?.y);return null==i||null==n?null:[i,n]};function El(e){if(!Array.isArray(e))return null;const t=[];for(const i of e){const e=Il(i);if(!e)return null;t.push(e)}if(t.length>1){const e=t[0],i=t[t.length-1];e[0]===i[0]&&e[1]===i[1]&&t.pop()}if(t.length<3)return null;let i=0,n=0,r=0;for(let e=0;ee+t[0],0)/t.length,t.reduce((e,t)=>e+t[1],0)/t.length]:[n/(3*i),r/(3*i)]}function Hl(e){if(!Array.isArray(e))return null;const t=[];for(const i of e){const e=Il(i);if(!e)return null;t.push(e)}if(t.length>1){const e=t[0],i=t[t.length-1];e[0]===i[0]&&e[1]===i[1]&&t.pop()}const i=El(t);if(!i)return null;const n=t.map(e=>e[0]),r=t.map(e=>e[1]);return{centroid:i,bbox:[Math.min(...n),Math.min(...r),Math.max(...n),Math.max(...r)]}}function Nl(e){let t=e;if(t&&"object"==typeof t&&!Array.isArray(t)){const e=t;t=e.path??e.points??t}if(!Array.isArray(t)||!t.length)return[];let i=(t.some(e=>(e=>Array.isArray(e)?e.length>=2&&!Array.isArray(e[0])&&("object"!=typeof e[0]||null==e[0]):!!e&&"object"==typeof e&&("x"in e||"y"in e))(e))?[t]:t.filter(Array.isArray)).map(e=>e.map(Il).filter(e=>null!=e)).filter(e=>e.length>=2);i.length>64&&(i=i.slice(-64));const n=i.reduce((e,t)=>e+t.length,0);if(n<=4e3)return i.map(e=>e.slice());const r=4e3-2*i.length,o=i.map(e=>e.length-2),s=o.reduce((e,t)=>e+t,0),a=o.map(e=>r*e/s),l=a.map(Math.floor);let c=r-l.reduce((e,t)=>e+t,0);const h=a.map((e,t)=>({index:t,fraction:e-Math.floor(e)})).sort((e,t)=>t.fraction-e.fraction||e.index-t.index);for(let e=0;efunction(e,t){if(t>=e.length)return e.slice();if(t<=2)return[e[0],e[e.length-1]];const i=[];for(let n=0;ne.slice()),i=t[t.length-1];return i?(i.length>2?i.pop():t.pop(),t):[]}const ql=e=>Number.isFinite(e[0])&&Number.isFinite(e[1]),Bl=(e,t)=>e[0]===t[0]&&e[1]===t[1];function jl(e){if(!e)return null;const t=e.vacuum_position||e.robot_position||null,i=t&&null!=Fl(t.x)&&null!=Fl(t.y)?{x:Fl(t.x),y:Fl(t.y),a:Fl(t.a??t.angle??t.theta)}:null,n=Nl(e.path?.path??e.path?.points??e.path),r=[],o=e.rooms,s=Array.isArray(o)?o.map((e,t)=>[String(e?.id??t),e]):o&&"object"==typeof o?Object.entries(o):[];for(const[e,t]of s){if(!t||"object"!=typeof t)continue;const i=String(t.name??t.label??"").trim(),n=[[Fl(t.cx),Fl(t.cy)],[Fl(t.center?.x),Fl(t.center?.y)],[Fl(t.x),Fl(t.y)]].find(([e,t])=>null!=e&&null!=t);let o=n?.[0]??null,s=n?.[1]??null;const a=Hl(t.outline),l=Fl(t.x0),c=Fl(t.y0),h=Fl(t.x1),d=Fl(t.y1),u=(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)||a?.bbox||null;if(null!=o&&null!=s||!a||([o,s]=a.centroid),null!=o&&null!=s||!u||(o=(u[0]+u[2])/2,s=(u[1]+u[3])/2),i&&null!=o&&null!=s){const t={id:e,name:i,cx:o,cy:s};u&&([t.x0,t.y0,t.x1,t.y1]=u),r.push(t)}}const a=function(e){return String(e.map_name??e.current_map??e.map_index??e.selected_map??"default")}(e);return i||r.length||n.length?{pos:i,path:n,rooms:r,mapId:a}:null}function Ul(e,t,i){return![e,t,i].every(Number.isFinite)||e<0||t<=0||i<=0?Number.POSITIVE_INFINITY:e/t*i}const Vl=40;function Gl(e,t,i){if(!e||!t)return null;const n=t.attributes||{},r=e.split(".")[0]||"",o=null!=i?.platform?String(i.platform):null,s=!!Il(n.vacuum_position??n.robot_position),a=!(!n.rooms||"object"!=typeof n.rooms),l=Nl(n.path?.path??n.path?.points??n.path).length>0,c=[n.map_name,n.current_map,n.map_index,n.selected_map].some(e=>null!=e),h="xiaomi_cloud_map_extractor"===o,d=a||l||c;let u=null;if(s?u="compatible":h?u="known_xcme_incomplete":d?u="partial":"camera"===r&&(u="camera"),!u)return null;const p=s?"camera"===r?300:200:h?100:d?50:0;return{entityId:e,name:String(n.friendly_name||e),platform:o,category:u,hasPosition:s,hasRooms:a,hasPath:l,hasMapId:c,score:p}}const Kl=e=>e.toLowerCase().replace(/[\s_\-.,]+/g,"");function Yl(e,t){const i=new Set(t.map(Kl).filter(Boolean)),n=new Set;for(const t of e){const e=Kl(t.name||"");e&&i.has(e)&&n.add(e)}return n.size}function Xl(e,t){const i=new Map(t.map(e=>[Kl(e.name),e])),n=[],r=[];for(const t of e){const e=i.get(Kl(t.name));e&&(n.push([[t.cx,t.cy],[e.cx,e.cy]]),r.push(t.name))}if(n.length<3)return null;const o=function(e){if(e.length<3)return null;let t=0,i=0,n=0,r=0,o=0,s=0,a=0,l=0,c=0,h=0,d=0,u=0;for(const[[p,m],[_,f]]of e){if(![p,m,_,f].every(Number.isFinite))return null;t+=p*p,i+=p*m,n+=p,r+=m*m,o+=m,s+=1,a+=p*_,l+=m*_,c+=_,h+=p*f,d+=m*f,u+=f}const p=[t,i,n,i,r,o,n,o,s],m=e=>{const[t,i,n,r,o,s,a,l,c]=p,h=t*(o*c-s*l)-i*(r*c-s*a)+n*(r*l-o*a);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(o*c-s*l)/h,(n*l-i*c)/h,(i*s-n*o)/h,(s*a-r*c)/h,(t*c-n*a)/h,(n*r-t*s)/h,(r*l-o*a)/h,(i*a-t*l)/h,(t*o-i*r)/h];return[d[0]*e[0]+d[1]*e[1]+d[2]*e[2],d[3]*e[0]+d[4]*e[1]+d[5]*e[2],d[6]*e[0]+d[7]*e[1]+d[8]*e[2]]},_=m([a,l,c]),f=m([h,d,u]);if(!_||!f)return null;const g=[_[0],_[1],_[2],f[0],f[1],f[2]];return g.every(Number.isFinite)?g:null}(n);return o?{matrix:o,matched:r,residual:Ol(o,n)}:null}function Zl(e,t,i){const n=e[e.length-1];if(n&&n[0]===t[0]&&n[1]===t[1])return e;if(e.push(t),e.length<=600)return e;let r=function(e,t){if(e.length<3)return e.slice();const i=new Uint8Array(e.length);i[0]=i[e.length-1]=1;const n=[[0,e.length-1]];for(;n.length;){const[r,o]=n.pop(),[s,a]=e[r],[l,c]=e[o],h=l-s,d=c-a,u=Math.hypot(h,d)||1e-9;let p=0,m=-1;for(let t=r+1;tp&&(p=i,m=t)}m>0&&p>t&&(i[m]=1,n.push([r,m],[m,o]))}const r=[];for(let t=0;t600&&(r=r.filter((e,t)=>t%2==0||t===r.length-1)),r}function Jl(e){return"cleaning"===e||"returning"===e||"on"===e}const Ql={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function ec(e){const[t,i]=Ql[e.rot]||[1,0],n=e.mir?-1:1;return[e.s*t*n,-e.s*i,e.ox,e.s*i*n,e.s*t,e.oy]}function tc(e){const t=e[0]*e[4]-e[1]*e[3];if(!Number.isFinite(t)||Math.abs(t)<1e-12)return null;const i=t<0,n=Math.sqrt(Math.abs(t));let r=180*Math.atan2(-e[1],e[4])/Math.PI;return r=(90*Math.round(r/90)%360+360)%360,{ox:e[2],oy:e[5],s:n,rot:r,mir:i}}function ic(e,t){const i=[],n=[];for(const t of e)null!=t.x0?(i.push(t.x0,t.x1),n.push(t.y0,t.y1)):(i.push(t.cx),n.push(t.cy));if(!i.length)return{ox:t[0]+t[2]/2,oy:t[1]+t[3]/2,s:t[2]/1e4,rot:0,mir:!0};const r=Math.min(...i),o=Math.max(...i),s=Math.min(...n),a=Math.max(...n),l=Math.max(o-r,a-s)||1,c={ox:0,oy:0,s:.6*Math.min(t[2],t[3])/l,rot:0,mir:!0},h=ec(c),[d,u]=Pl(h,(r+o)/2,(s+a)/2);return c.ox=t[0]+t[2]/2-d,c.oy=t[1]+t[3]/2-u,c}function nc(e,t,i,n){const[r,o]=Pl(ec(t),i,n),s=ec({...e,ox:0,oy:0}),[a,l]=Pl(s,i,n);return{...e,ox:r-a,oy:o-l}}function rc(e){const t=e?.trail_mode;return"never"===t||"cleaning"===t||"always"===t?t:!1===e?.trail?"never":"cleaning"}const oc={availability:"available",status:"alarm",activity:"none"},sc=new Set(["motion","vibration","sound"]),ac=new Set(["occupancy","presence"]),lc=new Set(["door","window","garage_door","opening"]),cc=new Set(["running","power"]),hc=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const dc=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),uc=new Set([...dc,"start","started","run","active","in_progress","wash","rinse","spin","dry"]),pc=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping","stop","end","done","inactive"]),mc=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),_c=e=>""===e||"unknown"===e||"unavailable"===e||"__missing__"===e,fc=e=>String(e??"").trim().toLowerCase(),gc=e=>fc(e).replace(/[\s-]+/g,"_").replace(/_+/g,"_").replace(/^_|_$/g,""),vc=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]]),yc=new Set(["wifi","connection","signal","battery"]);function bc(e,t){const i=e?.entities?.[t]||{};if("config"===i.entity_category)return null;const n=String(t||"").split(".").slice(1).join("."),r=[i.translation_key,i.original_name,i.name],o=[...r,n];r.some(e=>fc(e))||o.push(e?.states?.[t]?.attributes?.friendly_name);const s=o.map(gc).filter(Boolean);if(s.some(e=>e.split("_").some(e=>yc.has(e))))return null;let a=null;for(const e of s)for(const[t,i]of vc)(e===t||e.endsWith(`_${t}`))&&(a=null==a?i:Math.min(a,i));return a}const wc=(e,t)=>null!=bc(e,t);function kc(e,t){if(!t.startsWith("switch."))return!1;const i=e?.entities?.[t]||{},n=e?.states?.[t],r=t.slice(7).toLowerCase();if(/(?:^|_)(?:main_)?power$/.test(r))return!0;if([i.translation_key,i.original_name,i.name].map(fc).some(e=>["power","main power","power switch","питание"].includes(e)))return!0;const o=fc(n?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(o)||/(?:^|[\s._-])питание$/.test(o)}function xc(e){for(const t of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=fc(e?.[t]);if(dc.has(i)||pc.has(i))return i}return""}function $c(e,t){const i=e?.states?.[t],n=i?fc(i.state):"__missing__",r=String(t||"").split(".")[0],o=fc(i?.attributes?.device_class),s={eid:t,state:n,availability:_c(n)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===s.availability)return s;if(function(e,t,i){return"alarm_control_panel"===e?"triggered"===i:"on"===i&&Sn(e,t)}(r,o,n))return{...s,status:"alarm"};if("binary_sensor"===r)return sc.has(o)?{...s,edge:"rising"}:ac.has(o)?{...s,activity:"on"===n?"presence":"none"}:lc.has(o)?{...s,status:"on"===n?"open":"neutral",edge:"rising"}:"moving"===o?{...s,activity:"on"===n?"transition":"none"}:cc.has(o)&&"on"===n?{...s,status:"working",activity:"running"}:s;if("cover"===r)return{...s,activity:"opening"===n||"closing"===n?"transition":"none",edge:"terminal_transition"};if("lock"===r)return{...s,status:"unlocked"===n||"open"===n?"open":"neutral",activity:"locking"===n||"unlocking"===n?"transition":"none",edge:"terminal_transition"};if("valve"===r)return{...s,status:["open","opening","closing"].includes(n)?"open":"neutral",activity:"opening"===n||"closing"===n?"transition":"none",edge:"terminal_transition"};if("climate"===r){const e=xc(i.attributes),t=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(fc):[],r=!pc.has(n)&&(mc.has(n)||dc.has(n)||t.includes(n));return(e?dc.has(e):r)?{...s,status:"working",activity:"running"}:s}if(["light","switch","fan","humidifier"].includes(r))return"on"===n?{...s,status:"working",activity:"running"}:s;if("media_player"===r)return"off"===n?{...s,availability:"unavailable"}:s;if("vacuum"===r)return"cleaning"===n?{...s,status:"working",activity:"running"}:"returning"===n?{...s,status:"working",activity:"transition"}:s;if("script"===r)return"on"===n?{...s,status:"working",activity:"running"}:s;if("automation"===r)return s;if("button"===r||"event"===r)return{...s,edge:"change"};const a=xc(i.attributes);return dc.has(a)||dc.has(n)&&!pc.has(n)?{...s,status:"working",activity:"running"}:s}function Sc(e,t,i){const n=i.filter(t=>t.startsWith("switch.")&&!e?.entities?.[t]?.entity_category),r=t.find(t=>kc(e,t));if(!(n.length>1&&!!r))return t.map(t=>$c(e,t));const o=t.find(t=>t!==r&&wc(e,t)),s=$c(e,r);return"unavailable"===s.availability||"off"===s.state?t.map(t=>({eid:t,state:e?.states?.[t]?fc(e.states[t].state):"__missing__",availability:"unavailable",status:"neutral",activity:"none",edge:"none"})):t.map(t=>{if(t===o)return function(e,t){const i=e?.states?.[t],n=i?fc(i.state):"__missing__",r={eid:t,state:n,availability:_c(n)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===r.availability)return r;const o=xc(i?.attributes);return dc.has(o)||uc.has(n)&&!pc.has(n)?{...r,status:"working",activity:"running"}:r}(e,t);return{...t===r?s:$c(e,t),status:"neutral",activity:"none",edge:"none"}})}function Mc(e){if(!e.length)return{availability:"available",status:"neutral",activity:"none"};const t=e.filter(e=>"available"===e.availability);if(!t.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(t.some(e=>"alarm"===e.status))return oc;const i=t.some(e=>"working"===e.status)?"working":t.some(e=>"open"===e.status)?"open":"neutral",n=t.some(e=>"transition"===e.activity)?"transition":t.some(e=>"presence"===e.activity)?"presence":t.some(e=>"running"===e.activity)?"running":"none";return{availability:"available",status:i,activity:n}}function Rc(e,t){if(!(e=>!!e&&!_c(e))(e)||"unavailable"===t.availability||e===t.state)return null;if("rising"===t.edge)return"off"===e&&"on"===t.state?"event":null;if("change"===t.edge)return"event";if("terminal_transition"===t.edge){const i=new Set([e,t.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}const Tc=(e,t=0)=>{const i=Number(e);return Number.isInteger(i)&&i>=0?i:t},Cc=(e,t)=>{const i={rev:0,configRev:Tc(t),off:new Set};if(null==e)return{snapshot:i,valid:!1};const n=e.off;return!Number.isInteger(e.rev)||Number(e.rev)<0||!Number.isInteger(e.config_rev)||Number(e.config_rev)<0||!Array.isArray(n)||n.some(e=>"string"!=typeof e||!e)?{snapshot:i,valid:!1}:{snapshot:{rev:Number(e.rev),configRev:Number(e.config_rev),off:new Set(n)},valid:!0}};function Dc(e,t=0){return Cc(e,t).snapshot}function zc(e,t,i,n){const r=Cc(t,i);return n&&r.valid&&r.snapshot.configRev===i?e.configRev===i&&r.snapshot.rev{try{const[i,n]=await Promise.all([e.callWS({type:"config/device_registry/list"}),e.callWS({type:"config/entity_registry/list"})]),r=Bc(i,"id"),o=Bc(n,"entity_id");if(!r||!o)throw new Error("invalid_registry_response");t.devices=r,t.entities=o,t.authoritative=!0,t.access="full",t.lastSuccess=Date.now(),t.error=void 0}catch(e){t.authoritative=!1,t.access="limited",t.error=function(e){if(e&&"object"==typeof e){const t=e;return String(t.message||t.code||t.error||"registry_unavailable")}return String(e||"registry_unavailable")}(e)}finally{t.revision++,t.loading=void 0,function(e){for(const t of[...e.listeners])try{t()}catch{}}(t)}})()),t.loading}function Uc(e,t){void 0===t.reloadTimer&&(t.reloadTimer=globalThis.setTimeout(()=>{t.reloadTimer=void 0,jc(e,t)},80))}async function Vc(e,t){if(t.subscribing||t.unsubDevice&&t.unsubEntity)return t.subscribing;const i=e?.connection?.subscribeEvents;return"function"==typeof i?(t.subscribing=(async()=>{try{t.unsubDevice||(t.unsubDevice=await i.call(e.connection,()=>Uc(e,t),"device_registry_updated")),t.unsubEntity||(t.unsubEntity=await i.call(e.connection,()=>Uc(e,t),"entity_registry_updated"))}catch{}finally{0===t.refs&&(t.unsubDevice?.(),t.unsubEntity?.(),t.unsubDevice=void 0,t.unsubEntity=void 0),t.subscribing=void 0}})(),t.subscribing):void 0}function Gc(e,t){const i=qc(e);if(!i)return()=>{};const n=0===i.refs;i.refs++,i.listeners.add(t),("pending"===i.access||n)&&jc(e,i),Vc(e,i);let r=!1;return()=>{r||(r=!0,i.listeners.delete(t),i.refs=Math.max(0,i.refs-1),i.refs>0||(i.unsubDevice?.(),i.unsubEntity?.(),i.unsubDevice=void 0,i.unsubEntity=void 0,void 0!==i.reloadTimer&&globalThis.clearTimeout(i.reloadTimer),i.reloadTimer=void 0))}}function Kc(e){const t=qc(e);if(!t)return{revision:0,authoritative:!1,access:"limited",devices:e?.devices||{},entities:e?.entities||{},lastSuccess:0,error:"registry_unavailable"};let i=e?.devices||{},n=e?.entities||{};if(t.authoritative){const r=t.liveDevices!==i||t.liveEntities!==n,o=void 0!==t.liveDevices||void 0!==t.liveEntities;if(r&&(t.liveDevices=i,t.liveEntities=n,t.projectedRevision=void 0,o&&Uc(e,t)),t.projectedRevision!==t.revision||!t.projectedDevices||!t.projectedEntities){const e={...t.devices},r={...t.entities};for(const[t,n]of Object.entries(i))Object.prototype.hasOwnProperty.call(e,t)||(e[t]=n);for(const[e,t]of Object.entries(n))Object.prototype.hasOwnProperty.call(r,e)||(r[e]=t);t.projectedDevices=e,t.projectedEntities=r,t.projectedRevision=t.revision}i=t.projectedDevices,n=t.projectedEntities}return{revision:t.revision,authoritative:t.authoritative,access:t.access,devices:i,entities:n,lastSuccess:t.lastSuccess,error:t.error}}function Yc(e){if(!e||"object"!=typeof e&&"function"!=typeof e)return 0;let t=Hc.get(e);return t||(t=Nc++,Hc.set(e,t)),t}function Xc(e,t=Kc(e)){return[t.revision,t.access,Yc(t.devices),Yc(t.entities)].join(":")}function Zc(e){return!!e&&null==e.disabled_by}function Jc(e,t=Kc(e)){const i={},n={},r={};for(const[e,n]of Object.entries(t.devices||{}))Zc(n)&&(i[e]=n);for(const[e,i]of Object.entries(t.entities||{})){if(!Zc(i))continue;const r=i.device_id?t.devices?.[i.device_id]:null;t.authoritative&&i.device_id&&!r||(r&&!Zc(r)||(n[e]=i))}for(const[i,n]of Object.entries(e?.states||{})){const e=t.entities?.[i];if(e&&!Zc(e))continue;const o=e?.device_id?t.devices?.[e.device_id]:null;t.authoritative&&e?.device_id&&!o||(o&&!Zc(o)||(r[i]=n))}return{...e,devices:i,entities:n,states:r}}function Qc(e,t=Kc(e)){return{...e,devices:t.devices||{},entities:t.entities||{}}}function eh(e,t){const i=[];for(const[n,r]of Object.entries(e||{}))r?.device_id===t&&i.push(n);return i}function th(){if(Wc)return Wc;if("undefined"==typeof localStorage)return{};try{const e=JSON.parse(localStorage.getItem(Lc)||"{}");if(!e||"object"!=typeof e||Array.isArray(e))return{};const t=Date.now(),i={};for(const[n,r]of Object.entries(e))!r||"active"!==r.kind&&"ha_disabled"!==r.kind||!Number.isFinite(r.ts)||t-r.ts>7776e6||(i[n]=r);return Wc=i,Wc}catch{return{}}}function ih(e,t,i=Kc(e)){if(!t||"virtual"===t)return{kind:"active",enabledEntityIds:[],allEntityIds:[]};const n=t.indexOf(":");if(n<1)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const r=t.slice(0,n),o=t.slice(n+1);if("device"!==r&&"entity"!==r||!o)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const s=i.devices||{},a=i.entities||{};if(i.authoritative){if("device"===r){const e=s[o];if(!e)return{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[]};const t=eh(a,o);if(!Zc(e))return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:t};const i=t.filter(e=>Zc(a[e]));return t.length&&!i.length?{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:t}:{kind:"active",enabledEntityIds:i,allEntityIds:t}}const t=a[o];return t&&!Zc(t)?{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]}:t?.device_id&&!s[t.device_id]?{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[o]}:t?.device_id&&!Zc(s[t.device_id])?{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]}:t||e?.states?.[o]?{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}:{kind:"orphaned",reason:"entity_missing",enabledEntityIds:[],allEntityIds:[]}}const l=function(e){return th()[e]||null}(t);if("device"===r){const t=s[o],i=eh(a,o);if(null!=t?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:i};const n=i.filter(e=>{const t=a[e];return null==t?.disabled_by&&(!t.device_id||null==s[t.device_id]?.disabled_by)});if(i.length&&!n.length&&i.every(e=>null!=a[e]?.disabled_by))return{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:i};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"device",enabledEntityIds:[],allEntityIds:i};if(t||n.some(t=>!!e?.states?.[t]))return{kind:"active",enabledEntityIds:n,allEntityIds:i}}else{const t=a[o];if(null!=t?.disabled_by)return{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]};if(t?.device_id&&null!=s[t.device_id]?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"entity",enabledEntityIds:[],allEntityIds:[o]};if(t||e?.states?.[o])return{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}}return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]}}function nh(e){const t={};for(const[i,n]of Object.entries(e.entities)){if(!n?.device_id||!Zc(n))continue;const r=e.devices?.[n.device_id];r&&!Zc(r)||(t[n.device_id]=t[n.device_id]||[]).push(i)}return t}function rh(e,t){const i=new Map;for(const n of e){if(!0===n?.removed)continue;const e=n?.binding||"";if(!e.startsWith("entity:"))continue;const r=e.slice(7);if(!r)continue;const o=t?.entities?.[r]?.device_id;if(!o)continue;const s=i.get(o)||new Set;s.add(r),i.set(o,s)}return{byDevice:i}}function oh(e,t,i,n){const r=n.byDevice.get(t);return r?.size?{partial:!0,entityIds:i.filter(t=>!r.has(t)&&!e?.entities?.[t]?.hidden)}:{partial:!1,entityIds:[...i]}}function sh(e,t,i){if(t.identifiers?.[0]?.[0])return t.identifiers[0][0];for(const t of i){const i=e.entities[t]?.platform;if(i)return i}return""}function ah(e,t){if(/_device_temperature$/.test(t))return!1;if(e.entities?.[t]?.entity_category)return!1;const i=e.states[t];if(!i)return/_temperature$/.test(t);const n=i.attributes||{};return"temperature"===n.device_class||/°C|°F/.test(n.unit_of_measurement||"")||/_temperature$/.test(t)}const lh=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],ch=e=>[...e.filter(e=>!e.reg?.hidden),...e.filter(e=>!!e.reg?.hidden)];function hh(e,t){const i=t.map(t=>({eid:t,reg:e?.entities?.[t]})).filter(e=>!!e.reg);if(!i.length)return[];const n=i.filter(e=>!e.reg.entity_category),r=n.length?n:i;for(const e of lh){const t=r.filter(t=>t.eid.startsWith(e+"."));if(t.length)return ch(t).map(e=>e.eid)}const o=r.filter(t=>function(e,t){if(!t.startsWith("binary_sensor."))return!1;const i=fc(e?.states?.[t]?.attributes?.device_class||e?.entities?.[t]?.device_class||e?.entities?.[t]?.original_device_class);return sc.has(i)||ac.has(i)||lc.has(i)||cc.has(i)||"moving"===i||hc.has(i)}(e,t.eid));if(o.length)return ch(o).map(e=>e.eid);const s=r.filter(e=>e.eid.startsWith("switch."));if(s.length){const t=ch(s),n=t.find(t=>kc(e,t.eid));if(s.length>1&&n){const t=i.filter(t=>t.eid!==n.eid&&"config"!==t.reg?.entity_category&&null!=bc(e,t.eid)),r=t.filter(e=>!e.reg?.entity_category),o=r.length?r:t;for(const t of[0,1,2]){const i=ch(o.filter(i=>bc(e,i.eid)===t));if(i.length)return[i[0].eid,n.eid]}}return[(n||t[0]).eid]}const a=[];for(const e of xe)a.push(...ch(r.filter(t=>t.eid.startsWith(e+"."))));return a.push(...ch(r.filter(e=>!xe.includes(e.eid.split(".")[0])))),a.map(e=>e.eid)}function dh(e,t,i){const n=t.map(t=>({eid:t,reg:e.entities[t],st:e.states[t]})).filter(e=>e.reg),r=[n.filter(e=>!e.reg.hidden&&!e.reg.entity_category),n.filter(e=>!e.reg.entity_category),n.filter(e=>!e.reg.hidden),n];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const t of r){const i=t.find(t=>ah(e,t.eid));if(i)return i.eid}const o=hh(e,t);return o.find(ln)||o[0]}function uh(e,t,i=[]){return[...new Set(ph(e,t,i))].filter(e=>ln(e))}function ph(e,t,i=[]){const n=e?.startsWith("entity:")?new Set([e.slice(7)]):new Set(i);return(t||[]).filter(e=>"string"==typeof e&&!n.has(e))}function mh(e,t){if("string"==typeof t)return e.area===t;const i=e.marker?.room_id;return i?!!t.id&&i===t.id:!!t.area&&e.area===t.area}function _h(e){const t=e.marker?.binding?.startsWith("entity:")?e.marker.binding.slice(7):null,i=e.entities.filter(ln),n=e.primary&&ln(e.primary)?e.primary:null;return[...new Set([t,n,...i].filter(e=>!!e&&ln(e)))]}function fh(e){const t=_h(e),i=e.marker?.light_entity;return i&&t.includes(i)?i:t[0]||null}function gh(e,t){if(!1===t.marker?.is_light)return[];if(!0===t.marker?.is_light){return[{eid:fh(t),via:"forced"}]}const i=t.primary||hh(e,t.entities)[0];return i&&!i.startsWith("light.")?[]:t.entities.filter(t=>t.startsWith("light.")&&!!e.states?.[t]).map(e=>({eid:e,via:"light"}))}const vh=new WeakMap,yh=new WeakMap;function bh(e){return e.map(e=>[e.id||"",!0===e.hidden?1:0,e.primary||"",[...e.entities].join(","),void 0===e.controls?"":[...e.controls].join(","),e.marker?.id||"",e.marker?.binding||"",e.marker?.is_light,e.marker?.tap_action||"",!0===e.marker?.removed?1:0,e.marker?.light_entity||"",null==e.marker?.controls?"":[...e.marker.controls].join(",")].join("")).join("")}function wh(e){const t=bh(e),i=vh.get(e);if(i?.fingerprint===t)return i;const n=e.filter(e=>!e.hidden),r=new Map,o=new Map;for(const t of e){o.set(t,ph(t.marker?.binding,t.marker?.controls??t.controls,t.entities));const e=t.hidden||!0!==t.marker?.is_light?null:t.marker?.id||t.id;e&&r.set(String(e),t)}const s=new Map;for(const t of e){const e=o.get(t)||[],i=new Set(void 0===t.controls?e.filter(ln):t.controls.filter(ln)),n=fh(t),a=i.size?[...i]:n?[n]:[];for(const i of e){if(!i.startsWith("marker:"))continue;const e=i.slice(7);if(!e||e===String(t.marker?.id||t.id||""))continue;if(!r.has(e))continue;const n=s.get(e)||{markerId:e,controllers:[],driverEids:[]};n.controllers.push({device:t,driverEids:[...a]}),n.driverEids=[...new Set([...n.driverEids,...a])],s.set(e,n)}}const a={fingerprint:t,visible:n,markerById:r,persistedByDevice:o,incomingByMarker:s};return vh.set(e,a),a}function kh(e){return wh(e).incomingByMarker}function xh(e,t){const i=uh(t.marker?.binding,t.controls??t.marker?.controls,t.entities),n=ph(t.marker?.binding,t.marker?.controls??t.controls,t.entities);return(null!=t.marker?.is_light||!i.length&&!n.some(e=>e.startsWith("marker:")))&&gh(e,t).length>0}function $h(e,t){return gh(e,t).some(e=>!!e.eid)}function Sh(e,t,i,n){const r="always"===e||"auto"===e&&t,o=r&&!i;return{sourceExists:r,fromSourceEnabled:r&&i,manualEnabled:r,radiusEnabled:r,passive:o,effectiveMode:o&&"auto"===n?"fixed":n}}function Mh(e,t){const i=uh(t.marker?.binding,t.controls??t.marker?.controls,t.entities).map(e=>({eid:e,via:"controls"})),n=xh(e,t)?gh(e,t):[];for(const e of n)e.eid&&i.some(t=>t.eid===e.eid)||i.push(e);return i}function Rh(e,t,i,n){if(null!=i)return Rh(e,t,null,n).filter(e=>mh(e.device,i));const r=bh(t),o=function(e,t){const i=new Set;for(const e of t){for(const t of e.entities)i.add(t);for(const t of e.controls||e.marker?.controls||[])"string"!=typeof t||t.startsWith("marker:")||i.add(t)}return[...i].sort().map(t=>`${t}:${e?.states?.[t]?.state??""}`).join("|")}(e,t),s=Pc(n),a=yh.get(t);if(a?.graphFingerprint===r&&a.stateFingerprint===o&&a.virtualLightFingerprint===s&&a.registry===e?.entities)return a.sources;const{visible:l,markerById:c,persistedByDevice:h,incomingByMarker:d}=wh(t),u=null==i?l:l.filter(e=>mh(e,i)),p=new Map,m=new Map;for(const t of u){const i=Mh(e,t).filter(e=>"controls"!==e.via);for(const n of i){const i=!0===t.marker?.is_light?String(t.marker?.id||t.id||""):"",r=i?`marker:${i}`:`entity:${n.eid}`,o=n.eid?[n.eid]:[],s={key:r,eid:n.eid||"",stateEids:o,serviceEids:o,device:t,via:n.via,castsGlow:!0,passive:!n.eid,on:!n.eid||"on"===e.states?.[n.eid]?.state};p.set(t,[...p.get(t)||[],s]),n.eid&&m.set(n.eid,s)}}const _=new Map;for(const e of t){const t=h.get(e)||[],i=new Set(void 0===e.controls?t.filter(ln):e.controls);_.set(e,i)}for(const t of p.values())for(const i of t){if(!i.passive)continue;const t=i.key.slice(7),r=d.get(t);i.on=!r||r.driverEids.some(t=>"on"===e.states?.[t]?.state),!r&&Oc(i.device.marker)&&(i.on=Fc(i.device.marker,n))}const f=new Map,g=e=>{const t=f.get(e.key);(!t||e.castsGlow&&!t.castsGlow)&&f.set(e.key,e)};for(const t of l){const n=h.get(t)||[],r=null==i||mh(t,i);for(const i of n){if(i.startsWith("marker:")){const e=c.get(i.slice(7)),t=e?p.get(e):null;if(t)for(const e of t)g(e);continue}if(!ln(i)||!_.get(t)?.has(i))continue;const n=m.get(i);n?g(n):r&&g({key:`entity:${i}`,eid:i,stateEids:[i],serviceEids:[i],device:t,via:"controls",castsGlow:!1,passive:!1,on:"on"===e.states?.[i]?.state})}for(const e of p.get(t)||[])g(e)}const v=[...f.values()];return yh.set(t,{graphFingerprint:r,stateFingerprint:o,virtualLightFingerprint:s,registry:e?.entities,sources:v}),v}function Th(e){const t=e.filter(e=>e.castsGlow);return t.find(e=>e.on)||t[0]||null}function Ch(e){return e.length?e.some(e=>e.on)?"on":"off":"none"}function Dh(e,t){const i=[];for(const n of t){const t=e.states[n];if(!t)continue;const r=(t.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(n)||"lqi"===r){const e=parseFloat(t.state);isNaN(e)||i.push(e);continue}const o=t.attributes?.linkquality??t.attributes?.lqi;if(null!=o){const e=parseFloat(o);isNaN(e)||i.push(e)}}return _i(i)}function zh(e,t){for(const i of t){if(!ah(e,i))continue;const t=e.states[i];if(!t)continue;const n=parseFloat(t.state);if(!isNaN(n))return Math.round(10*n)/10}return null}function Ah(e,t){for(const i of t){if(!i.startsWith("climate."))continue;const t=e.states[i];if(!t||"unavailable"===t.state||"unknown"===t.state)continue;const n=parseFloat(t.attributes?.current_temperature);if(Number.isFinite(n))return Math.round(10*n)/10}return null}function Ph(e,t){if(e.entities?.[t]?.entity_category)return!1;const i=e.states[t];if(!i)return/_humidity$/.test(t);const n=i.attributes||{};return"humidity"===n.device_class||"%"===n.unit_of_measurement&&/_humidity$/.test(t)||/_humidity$/.test(t)}function Oh(e,t){for(const i of t){if(!Ph(e,i))continue;const t=e.states[i];if(!t)continue;const n=parseFloat(t.state);if(!isNaN(n))return Math.round(n)}return null}function Fh(e,t){if(!t)return[];const i=[];for(const[t,n]of Object.entries(e.entities)){if(!t.startsWith("light.")||n.hidden||!Zc(n))continue;let r=null;if("group"===n.platform)r=n.area_id||null;else{if(!n.device_id)continue;{const t=e.devices[n.device_id];if(!Zc(t))continue;if("Group"!==t?.model)continue;r=t.area_id||n.area_id||null}}if(!r)continue;const o=e.states[t];i.push({eid:t,name:n.name||o?.attributes?.friendly_name||t,area:r})}return i}function Ih(e,t,i,n,r){const o=ke(t,i,r);if(o!==we)return o;const s=[];for(const t of n){const i=e.states[t]?.attributes?.device_class;i&&s.push(i)}return function(e){for(const t of e){const e=be[t];if(e)return e}return null}(s)??we}function Eh(e,t){return e.map(e=>{if(!Array.isArray(e.controls))return e;const i=e.controls.filter(e=>!("string"==typeof e&&e.startsWith("marker:")&&t.has(e.slice(7))));return i.length===e.controls.length?e:{...e,controls:i.length?i:null}})}function Hh(e,t,i){if(!t||!i||t===i)return[...e];const n=`marker:${t}`,r=`marker:${i}`;return e.map(e=>{const t=Array.isArray(e.controls)?e.controls.map(e=>e===n?r:e):e.controls,i="derived_marker_state"===e.value_badge?.source?.kind&&e.value_badge.source.ref===n?{...e.value_badge,source:{...e.value_badge.source,ref:r}}:e.value_badge;return t===e.controls&&i===e.value_badge?e:{...e,controls:t,value_badge:i}})}function Nh(e,t,i){if(!t||t===i)return!0;const n=new Map;for(const t of e)n.set(t.id,(t.controls||[]).filter(e=>"string"==typeof e&&e.startsWith("marker:")).map(e=>e.slice(7)));const r=new Set,o=[i];for(;o.length;){const e=o.pop();if(e===t)return!0;r.has(e)||(r.add(e),o.push(...n.get(e)||[]))}return!1}function Lh(e,t,i,n){const r=new Set([t]),o=e.filter(e=>{const o=e.id===t||!n&&e.binding===i;return o&&r.add(e.id),!o});return{markers:n?o:[...o,{id:t,binding:i,removed:!0,hidden:!0}],cleanupIds:r}}function Wh(e){const t=new Set,i=new Set,n=new Set;for(const r of e||[]){const e=String(r.binding||"").indexOf(":");if(e<1)continue;const o=r.binding.slice(0,e),s=r.binding.slice(e+1);s&&(!0===r?.removed?"device"===o?t.add(s):"entity"===o&&i.add(s):"entity"===o&&n.add(s))}return{devices:t,entities:i,liveEntities:n}}function qh(e,t,i){if(i.liveEntities.has(t))return!1;if(i.entities.has(t))return!0;const n=e?.entities?.[t]?.device_id;return!!n&&i.devices.has(n)}function Bh(e,t,i,n,r){const o=uh(t.binding,t.controls,e.entities).filter(e=>!qh(i,e,n)).filter(e=>"active"===ih(i,"entity:"+e,r).kind);e.marker=t,e.controls=o,e.userHidden=!0===t.hidden,e.hidden=e.userHidden||"ha_disabled"===e.bindingStatus?.kind,t.name&&(e.name=t.name),t.icon&&(e.icon=t.icon),null!=t.model&&(e.model=t.model),e.link=t.link??null,e.description=t.description??null,e.pdfs=t.pdfs||[],e.tapAction=t.tap_action??null}function jh(e,t,i,n){if("string"==typeof e.room_id&&e.room_id.length>0&&null===e.area)return{area:"",space:e.space||n};const r=e.area||t||"";return{area:r,space:r&&i[r]||e.space||n}}function Uh(e){const t=e.hass,i=e.registry||Kc(t),n=Jc(t,i),r=Qc(t,i),{areaToSpace:o,markers:s,settings:a,excluded:l,showAll:c,firstSpaceId:h,loc:d,iconRules:u}=e,p=!1!==a.group_lights,m=Wh(s),_=Fh(n,p).filter(e=>!qh(n,e.eid,m)),f=new Set(_.map(e=>e.area)),g=nh(n),v=function(e){const t={};for(const[i,n]of Object.entries(e.entities||{}))n?.device_id&&(t[n.device_id]=t[n.device_id]||[]).push(i);return t}(r),y=rh(s,r),b=new Set;for(const e of s){const[t,i]=e.binding.split(":");"device"!==t&&"entity"!==t||!i||b.add(e.binding)}const w=(e,t)=>s.find(i=>i.binding===e+":"+t),k={},x=[];for(const e of Object.values(n.devices)){const r=e.area_id;if(!r||!o[r])continue;if("service"===e.entry_type)continue;if(b.has("device:"+e.id))continue;const s=ih(t,"device:"+e.id,i);if("active"!==s.kind)continue;const h=w("device",e.id);if(h&&h.hidden&&!a.filter_seeded)continue;const m=oh(n,e.id,g[e.id]||[],y);if(m.partial&&!m.entityIds.length)continue;const _=m.entityIds,v=m.partial?{kind:"active",enabledEntityIds:_,allEntityIds:_}:s,$=sh(n,e,_),S=!a.filter_seeded;if(S&&!c){if(l.has($))continue;if("Group"===e.model)continue;if(/scene/i.test(e.model||""))continue;if(/bridge/i.test((e.model||"")+(e.name||"")))continue;if("myheat"===$&&e.via_device_id)continue}const M=(e.name_by_user||e.name||d("device.unnamed")).trim(),R=M+"|"+r;let T=Ih(n,M,e.model,_,u);if(_.some(e=>e.startsWith("lock."))&&(T="mdi:lock"),S&&!c&&p&&"mdi:lightbulb"===T&&f.has(r))continue;k[R]=(k[R]||0)+1;const C=k[R]>1?M+" "+k[R]:M,D={id:e.id,name:C,model:e.model||"",area:r,space:o[r],icon:T,entities:_,allEntities:v.allEntityIds,bindingStatus:v,bindingKind:"device",bindingRef:e.id,pdfs:[]};D.primary=dh(n,_,T),"mdi:thermometer"!==T&&"mdi:air-filter"!==T||(D.temp=zh(n,_)),D.primary&&Ph(n,D.primary)&&(D.hum=Oh(n,_)),x.push(D)}for(const e of _)o[e.area]&&(b.has("entity:"+e.eid)||x.push({id:"lg_"+e.eid,name:e.name,model:d("device.light_group"),area:e.area,space:o[e.area],icon:"mdi:lightbulb-group",entities:[e.eid],allEntities:[e.eid],bindingStatus:{kind:"active",enabledEntityIds:[e.eid],allEntityIds:[e.eid]},primary:e.eid,bindingKind:"entity",bindingRef:e.eid,pdfs:[]}));for(const e of s){if(e.removed)continue;const[s,l]=e.binding.split(":"),c="device"===s||"entity"===s?ih(t,e.binding,i):null;if(!e.hidden||a.filter_seeded||"ha_disabled"===c?.kind)if("device"===s){const t=c;if("unverified"===t.kind)continue;const s=r.devices[l],{area:a,space:p}=jh(e,s?.area_id,o,h),_="active"===t.kind?t.enabledEntityIds:[];let f=s?Ih(n,s.name_by_user||s.name||"",s.model,_,u):"mdi:help-circle";_.some(e=>e.startsWith("lock."))&&(f="mdi:lock");const g={id:e.id,name:s?.name_by_user||s?.name||d("device.fallback"),model:s?.model||"",area:a,space:p,icon:f,entities:_,allEntities:t.allEntityIds.length?t.allEntityIds:s&&v[s.id]||[],bindingStatus:t,bindingKind:"device",bindingRef:l};g.primary=dh(n,_,f),"mdi:thermometer"!==f&&"mdi:air-filter"!==f||(g.temp=zh(n,_)),g.primary&&Ph(n,g.primary)&&(g.hum=Oh(n,_)),Bh(g,e,r,m,i),x.push(g)}else if("entity"===s){if(qh(r,l,m))continue;const t=c;if("unverified"===t.kind)continue;const s=r.entities[l],a=s?.area_id||s?.device_id&&r.devices[s.device_id]?.area_id||"",{area:d,space:p}=jh(e,a,o,h),_=n.states[l],f=s?.name||_?.attributes?.friendly_name||l;let g=Ih(n,f,"",[l],u);l.startsWith("lock.")&&(g="mdi:lock");const v={id:e.id,name:f,model:"",area:d,space:p,icon:g,entities:"active"===t.kind?[l]:[],allEntities:[l],bindingStatus:t,primary:"active"===t.kind?l:void 0,bindingKind:"entity",bindingRef:l};"mdi:thermometer"!==g&&"mdi:air-filter"!==g||!v.entities.length||(v.temp=zh(n,v.entities)),v.entities.length&&Ph(n,l)&&(v.hum=Oh(n,v.entities)),Bh(v,e,r,m,i),x.push(v)}else{const t=e.area||"",n=e.space||t&&o[t]||h,s={id:e.id,name:e.name||d("device.virtual"),model:e.model||"",area:t,space:n,icon:e.icon||"mdi:map-marker",entities:[],allEntities:[],bindingStatus:{kind:"active",enabledEntityIds:[],allEntityIds:[]},bindingKind:"virtual",virtual:!0};Bh(s,e,r,m,i),x.push(s)}}return x}function Vh(e){const{marker:t,siblingMarkers:i=[],...n}=e;return Uh({...n,markers:[...i.filter(e=>e.id!==t.id),t]}).find(e=>e.id===t.id)||null}function Gh(e,t,i,n){if(!t)return null;if(function(e,t,i){if(!t)return!1;const n=t.indexOf(":");if(n<1)return!1;const r=t.slice(0,n),o=t.slice(n+1),s=Wh(i);return"device"===r?s.devices.has(o):"entity"===r&&qh(e,o,s)}(e,t,n))return null;const r=t.indexOf(":");if(r<0)return null;const o=t.slice(0,r),s=t.slice(r+1);if(!s)return null;if("entity"===o){const t=parseFloat(e.states[s]?.state);return Number.isFinite(t)?"temp"===i?Math.round(10*t)/10:Math.round(t):null}if("device"===o){const t=Object.entries(e.entities).filter(([,e])=>e.device_id===s).map(([e])=>e);return"temp"===i?zh(e,t):Oh(e,t)}return null}const Kh=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 Yh(e,t){return t.area?t.area:e&&t.id?`@room/${encodeURIComponent(e)}/${encodeURIComponent(t.id)}`:null}function Xh(e){return null==e.area&&e.space&&e.room_id?Yh(e.space,{id:e.room_id,area:null}):e.area||null}function Zh(e,t,i){const n=new Map;if(!e?.entities)return n;const r=Wh(i),o=new Set,s=new Map,a=new Map;for(const e of i||[]){if(e?.removed)continue;const t=(e.binding||"").indexOf(":");if(t<=0)continue;const i=e.binding.slice(0,t),n=e.binding.slice(t+1);if(!n)continue;!0===e.use_climate_temp&&o.add(n);const r=Xh(e);r&&("entity"!==i||s.has(n)?"device"!==i||a.has(n)||a.set(n,r):s.set(n,r))}const l=new Map;for(const[t,i]of Object.entries(e.entities)){if(i.device_id&&r.devices.has(i.device_id)&&!r.liveEntities.has(t)||!i.device_id&&r.entities.has(t)&&!r.liveEntities.has(t))continue;const n=i.device_id?e.devices?.[i.device_id]:null;if(!Zc(i)||n&&!Zc(n))continue;const c=s.get(t)||(i.device_id?a.get(i.device_id):null)||i.area_id||n?.area_id||null;if(!c)continue;if(i.entity_category)continue;if(!(o.size>0&&t.startsWith("climate.")&&(o.has(t)||i.device_id&&o.has(i.device_id)))){if(_e.has(i.platform))continue;if(Kh.test(t))continue}let h=l.get(c);h||(h=new Map,l.set(c,h));const d=i.device_id||t;let u=h.get(d);if(!u){const r=e.states?.[t];u={name:(n?n.name_by_user||n.name:i.name||r?.attributes?.friendly_name||t)||t,model:n?.model,ents:[]},h.set(d,u)}u.ents.push(t)}for(const[i,r]of l){const s=[],a=[];for(const[i,n]of r){const r=Ih(e,n.name,n.model,n.ents,t),l="mdi:thermometer"===r||"mdi:air-filter"===r;if(l){const t=zh(e,n.ents);null!=t&&s.push(t)}if(o.size>0&&(o.has(i)||n.ents.some(e=>o.has(e)))){const t=Ah(e,n.ents);null!=t&&s.push(t)}if(l||"mdi:water-percent"===r){const t=Oh(e,n.ents);null!=t&&a.push(t)}}(s.length||a.length)&&n.set(i,{temp:s.length?Math.round(s.reduce((e,t)=>e+t,0)/s.length*10)/10:null,hum:a.length?Math.round(a.reduce((e,t)=>e+t,0)/a.length):null})}return n}function Jh(e,t,i){const n=[];for(const r of t){if(r.area!==i||r.virtual)continue;const t=Dh(e,r.entities);null!=t&&n.push(t)}return _i(n)}const Qh={offStates:["off"],unknownUsesToggle:!0},ed={light:Qh,switch:Qh,fan:Qh,humidifier:Qh,input_boolean:Qh,automation:Qh,remote:Qh,group:Qh,climate:{...Qh,featureMasks:{turn_on:256,turn_off:128,toggle:384}},media_player:{...Qh,featureMasks:{turn_on:128,turn_off:256,toggle:384}},siren:{...Qh,featureMasks:{turn_on:1,turn_off:2,toggle:3}},vacuum:{...Qh,featureMasks:{turn_on:1,turn_off:2,toggle:3}},water_heater:{...Qh,featureMasks:{turn_on:8,turn_off:8,toggle:8}},camera:{...Qh,featureMasks:{turn_on:1,turn_off:1,toggle:1}}};function td(e){return e.slice(0,e.indexOf("."))}function id(e,t,i){if(!e?.services||"object"!=typeof e.services)return!1;const n=e.services?.[t];return!!n&&Object.prototype.hasOwnProperty.call(n,i)}function nd(e,t,i){return id(e,t,i)?{domain:t,service:i}:id(e,"homeassistant",i)?{domain:"homeassistant",service:i}:null}function rd(e,t){return e?.entities?.[t]||null}function od(e,t,i){const n=e?.states?.[i],r=rd(t,i);return n?.attributes?.friendly_name||r?.name||r?.original_name||i}function sd(e,t){const i=rd(e,t);if(null!=i?.disabled_by)return!0;const n=i?.device_id?e?.devices?.[i.device_id]:null;return null!=n?.disabled_by}function ad(e,t){const i=e?.attributes?.supported_features;if(null==i||""===i)return!1;const n=Number(i);return Number.isFinite(n)&&(n&t)===t}function ld(e,t,i){const n=td(i);if("lock"===n||"alarm_control_panel"===n)return!0;if("cover"!==n)return!1;const r=e?.states?.[i],o=rd(t,i),s=String(r?.attributes?.device_class||o?.device_class||o?.original_device_class||"");return $i.has(s)}function cd(e,t,i,n,r){return{ref:i,entityId:n,name:n?od(e,t,n):null,reason:r}}function hd(e,t,i,n,r,o=null){return{target:null,skipped:cd(e,t,i,n,r),semantics:o,nextEffect:null,command:null}}function dd(e,t,i,n,r=i){const o=td(i);if("cover"!==o&&"valve"!==o)return function(e,t,i,n,r=i){const o=td(i),s=e?.states?.[i];if(sd(t,i))return hd(e,t,r,i,"ha-disabled","power");if(ld(e,t,i))return hd(e,t,r,i,"secure");if(!s)return hd(e,t,r,i,"missing","power");if("unavailable"===s.state)return hd(e,t,r,i,"unavailable","power");const a=ed[o];if(!a)return hd(e,t,r,i,"unsupported");let l,c;if("unknown"===s.state||""===s.state){if(!a.unknownUsesToggle)return hd(e,t,r,i,"unsupported","power");l="toggle",c="toggle"}else a.offStates.includes(String(s.state))?(l="turn-on",c="turn_on"):(l="turn-off",c="turn_off");const h=a.featureMasks?.[c];if(h&&!ad(s,h))return hd(e,t,r,i,"unsupported","power");const d=nd(e,o,c);return d?{target:{entityId:i,name:od(e,t,i),state:String(s.state||""),via:n},skipped:null,semantics:"power",nextEffect:l,command:{...d,data:{entity_id:i}}}:hd(e,t,r,i,"unsupported","power")}(e,t,i,n,r);if(sd(t,i))return hd(e,t,r,i,"ha-disabled",o);if(ld(e,t,i))return hd(e,t,r,i,"secure",o);const s=e?.states?.[i];if(!s)return hd(e,t,r,i,"missing",o);if("unavailable"===s.state)return hd(e,t,r,i,"unavailable",o);const a=function(e,t,i){const n=String(i?.state||""),r="closed"===n?{service:`open_${t}`,effect:"open",feature:1}:"open"===n?{service:`close_${t}`,effect:"close",feature:2}:"opening"===n||"closing"===n?{service:`stop_${t}`,effect:"stop",feature:8}:null;return r&&ad(i,r.feature)&&id(e,t,r.service)?r:ad(i,3)&&id(e,t,"toggle")?{service:"toggle",effect:"toggle"}:null}(e,o,s);if(!a)return hd(e,t,r,i,"unsupported",o);return{target:{entityId:i,name:od(e,t,i),state:String(s.state||""),via:n},skipped:null,semantics:o,nextEffect:a.effect,command:{domain:o,service:a.service,data:{entity_id:i}}}}function ud(e){return"entity"===e.bindingKind?e.bindingRef&&ln(e.bindingRef)?[e.bindingRef]:[]:"virtual"===e.bindingKind?[]:_h(e)}function pd(e){const t=e.marker?.toggle_entity;return t&&ud(e).includes(t)?t:null}function md(e,t,i){const n=function(e,t){if("entity"===e.bindingKind&&e.bindingRef){const t=e.marker?.light_entity?fh(e):null;return[...new Set([t,e.bindingRef].filter(e=>!!e))]}const i=e.entities.length?e.entities:e.allEntities||[],n=e.marker?.light_entity||e.primary&&ln(e.primary)?fh(e):null;return[...new Set([n,...hh(t,i)].filter(e=>!!e))]}(i,t);if(!n.length)return null;const r="entity"===i.bindingKind?"binding":"device-role";if("binding"===r)return dd(e,t,n[0],r);let o=null,s=null;for(const i of n){const n=dd(e,t,i,r);if(n.command)return n;const a=n.skipped?.reason;if("missing"===a||"unavailable"===a||"secure"===a)return n;"ha-disabled"!==a?o||=n:s||=n}return o||s}function _d(e){const t=e.skipped?.reason;return"missing"===t?"unavailable":t||"unsupported"}function fd(e,t){return{origin:e,kind:"none",semantics:null,targets:[],skippedTargets:[],noneReason:t,nextEffect:null,command:null}}function gd(e,t){return"cover"===e||"toggle"===e?"toggle":"more-info"===e||"run"===e||"info"===e?e:(null==e||""===e)&&"light"===t?"toggle":"info"}function vd(e,t){return{origin:e,kind:"single",semantics:t.semantics,targets:t.target?[t.target]:[],skippedTargets:t.skipped?[t.skipped]:[],noneReason:t.command?null:_d(t),nextEffect:t.nextEffect,command:t.command}}function yd(e,t,i,n=[]){const r=new Map,o=[],s=new Set,a=e=>{const t=`${e.ref}\n${e.entityId||""}\n${e.reason}`;s.has(t)||(s.add(t),o.push(e))};for(const e of n)a(e);for(const n of i){const i=dd(e,t,n.entityId,n.via,n.ref);i.target?r.set(n.entityId,r.get(n.entityId)||i.target):i.skipped&&a(i.skipped)}const l=[...r.values()];if(!l.length)return{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:l,skippedTargets:o,noneReason:o.length&&o.every(e=>"secure"===e.reason)?"secure":"configured-targets-missing",nextEffect:null,command:null};const c=l.some(e=>"on"===e.state)?"turn_off":"turn_on",h=nd(e,"homeassistant",c);return h?{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:l,skippedTargets:o,noneReason:null,nextEffect:"turn_on"===c?"turn-on":"turn-off",command:{...h,data:{entity_id:l.map(e=>e.entityId)}}}:{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:[],skippedTargets:[...o,...l.map(i=>cd(e,t,i.entityId,i.entityId,"unsupported"))],noneReason:"unsupported",nextEffect:null,command:null}}function bd(e){return String(e.marker?.id||e.id||"")}function wd(e){const{hass:t,device:i}=e,n=e.registryHass||t,r=function(e){return"toggle"===e.tapAction?"explicit-toggle":"cover"===e.tapAction?"legacy-cover":!e.tapAction&&e.primary?.startsWith("light.")?"default-light":null}(i);if(!r)return null;const o=i.marker;if("explicit-toggle"===r&&Oc(o)){const t=String(o?.id||i.id||""),n=kh(e.devices).get(t);if(n)return function(e,t){const i=e.registryHass||e.hass;return yd(e.hass,i,t.driverEids.map(e=>({entityId:e,via:"control-marker-driver",ref:`marker:${t.markerId}`})))}(e,n);const s=Fc(o,e.virtualLights);return{origin:r,kind:"single",semantics:"power",targets:[{entityId:"",name:i.name,state:s?"on":"off",via:"virtual-light"}],skippedTargets:[],noneReason:null,nextEffect:s?"turn-off":"turn-on",command:null,operation:{kind:"virtual-light",markerId:o.id}}}if("explicit-toggle"===r){if(ph(i.marker?.binding,i.marker?.controls??i.controls,i.entities).length)return function(e,t=null){const{hass:i,device:n,devices:r}=e,o=e.registryHass||i,s=ph(n.marker?.binding,n.marker?.controls??n.controls,n.entities),a=e.lightSources||Rh(i,r,null,e.virtualLights),l=new Map;for(const e of a){if(!e.key.startsWith("marker:"))continue;const t=l.get(e.key)||[];t.push(e),l.set(e.key,t)}const c=kh(r),h=new Map;for(const e of r){const t=String(e.marker?.id||e.id||"");t&&h.set(t,e)}const d=[],u=[];t&&d.push({entityId:t,via:"entity"===n.bindingKind?"binding":"device-role",ref:t});for(const e of s){if(!e.startsWith("marker:")){ln(e)?d.push({entityId:e,via:"control-entity",ref:e}):u.push(cd(i,o,e,e.includes(".")?e:null,"unsupported"));continue}const t=l.get(e)||[];if(!t.length){const t=h.get(e.slice(7));if("ha_disabled"===t?.bindingStatus?.kind){const n=t.bindingStatus.allEntityIds[0]||null;u.push({ref:e,entityId:n,name:t.name||(n?od(i,o,n):null),reason:"ha-disabled"})}else u.push(cd(i,o,e,null,"missing"));continue}const r=[...new Set(t.flatMap(e=>e.serviceEids))];if(r.length){for(const t of r)d.push({entityId:t,via:"control-entity",ref:e});continue}const s=t.some(e=>e.passive),a=e.slice(7),p=bd(n),m=c.get(a)?.controllers.find(e=>bd(e.device)===p);if(s&&m?.driverEids.length)for(const t of m.driverEids)d.push({entityId:t,via:"control-marker-driver",ref:e});else u.push(cd(i,o,e,null,s?"missing":"unsupported"))}return yd(i,o,d,u)}(e,pd(i))}if("legacy-cover"===r){const e=i.entities.find(e=>e.startsWith("cover."))||i.allEntities?.find(e=>e.startsWith("cover."));return e?vd(r,dd(t,n,e,"device-role")):fd(r,"no-actionable-entity")}const s=pd(i),a=s?dd(t,n,s,"entity"===i.bindingKind?"binding":"device-role"):md(t,n,i);return a?vd(r,a):fd(r,i.virtual||"virtual"===i.bindingKind?"no-actionable-entity":"no-binding")}function kd(e){if(!e)return[];const t=Array.isArray(e.data.entity_id)?e.data.entity_id:[e.data.entity_id];return[...new Set(t)].sort()}function xd(e){return e?e.operation?e.operation:e.command?{kind:"ha-service",command:e.command}:null:null}const $d=new Set(["missing","ha-disabled","unavailable"]);function Sd(e,t){const i=xd(e),n=xd(t);return!(!i||!n||i.kind!==n.kind)&&("virtual-light"===i.kind&&"virtual-light"===n.kind?i.markerId===n.markerId:"ha-service"===i.kind&&"ha-service"===n.kind&&function(e,t){const i=kd(e),n=kd(t);return i.length===n.length&&i.every((e,t)=>e===n[t])}(i.command,n.command))}function Md(e,t){const i=e.targets[0]||e.skippedTargets[0],n=[];return"group"===e.kind?e.targets.length&&n.push(t.group(e.targets)):i&&n.push(t.single(i)),e.nextEffect&&e.targets.length&&n.push("group"===e.kind?t.groupCurrentNext(e.targets,e.nextEffect):t.currentNext(e.targets[0],e.nextEffect)),e.skippedTargets.length&&n.push(t.skipped(e.skippedTargets)),!e.command&&e.noneReason&&n.push(t.none(e.noneReason)),n}const Rd=e=>!!e&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]);function Td(e,t=5,i=5){return ao(e.cm,t,i)/2}function Cd(e,t,i=1,n=5,r=5,o=0){const s=e.host;if(!s||"partition"!==s.kind||"string"!=typeof s.id||!s.id)return{resolved:null,reason:"invalid-host"};const a=t.find(e=>e.id===s.id);if(!a)return{resolved:null,reason:"missing-partition"};if(!Rd(a.a)||!Rd(a.b)||!Number.isFinite(s.t)||s.t<0||s.t>1)return{resolved:null,reason:"invalid-position"};const l=a.b[0]-a.a[0],c=a.b[1]-a.a[1],h=Math.hypot(l,c),d=Number(e.length)*i;if(!(h>1e-9&&d>0&&Number.isFinite(d)))return{resolved:null,reason:"invalid-length"};const u=s.t*h;if(u-d/2h-o+1e-9)return{resolved:null,reason:"does-not-fit"};const p=l/h,m=c/h;let _=180*Math.atan2(c,l)/Math.PI;return _>=90?_-=180:_<-90&&(_+=180),{reason:null,resolved:{opening:e,host:s,partition:a,center:[a.a[0]+l*s.t,a.a[1]+c*s.t],angle:_,length:d,depth:ao(a.cm,n,r),t:s.t,axis:{a:[a.a[0],a.a[1]],b:[a.b[0],a.b[1]],ux:p,uy:m,length:h}}}}function Dd(e,t,i=1,n=5,r=5){return Cd(e,t,i,n,r,0)}function zd(e,t,i=1,n=5,r=5){const o=e.host,s="partition"===o?.kind?t.find(e=>e.id===o.id):void 0;if(!s)return Dd(e,t,i,n,r);const a=Cd(e,t,i,n,r,Td(s,n,r));return"does-not-fit"===a.reason?{resolved:null,reason:"does-not-fit-jamb"}:a}function Ad(e,t){return!e||(e.length!==t.length||e.host?.kind!==t.host?.kind||e.host?.id!==t.host?.id||e.host?.t!==t.host?.t)}function Pd(e){const{center:t,length:i,axis:n,host:r}=e,o=i/2;return{hostId:r.id,a:[t[0]-n.ux*o,t[1]-n.uy*o],b:[t[0]+n.ux*o,t[1]+n.uy*o],depth:e.depth}}function Od(e,t=!1){const i=t?1:-1,n=e.depth/2;return{ox:-e.axis.uy*i*n,oy:e.axis.ux*i*n,cm:e.partition.cm,side:i}}function Fd(e,t,i){return e.flatMap(e=>!Rd(e.a)||!Rd(e.b)||Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])<=1e-9?[]:[{roomId:"",a:[e.a[0],e.a[1]],b:[e.b[0],e.b[1]],key:`partition:${e.id}`,kind:"outer",cm:e.cm,open:!1,half:ao(e.cm,t,i)/2,partitionHost:{kind:"partition",id:e.id}}])}function Id(e,t,i){const{center:n,length:r,axis:o}=e,s=r/2,a=t.flatMap(e=>{if(!e.kind||e.open)return[];const t=e.b[0]-e.a[0],r=e.b[1]-e.a[1],a=Math.hypot(t,r);if(!(a>i))return[];const l=t/a,c=r/a;if(Math.abs(o.ux*c-o.uy*l)>1e-6)return[];if(Math.abs((n[0]-e.a[0])*c-(n[1]-e.a[1])*l)>i)return[];const h=e=>(e[0]-n[0])*o.ux+(e[1]-n[1])*o.uy,d=h(e.a),u=h(e.b),p=Math.max(-s,Math.min(d,u)),m=Math.min(s,Math.max(d,u));return m>=p-i?[[p,m]]:[]});if(!a.length)return!1;a.sort((e,t)=>e[0]-t[0]||e[1]-t[1]);let l=-s;for(const[e,t]of a){if(e>l+i)return!1;if(l=Math.max(l,t),l>=s-i)return!0}return!1}function Ed(e,t,i=1e-9){const n=e.length/(2*e.axis.length),r=e.t-n,o=e.t+n;return t.some(t=>{if(t.host.id!==e.host.id||t.opening.id===e.opening.id)return!1;const n=t.length/(2*t.axis.length);return Math.max(r,t.t-n)`}const tr={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},ir=(e,t=1)=>{const i=Number(e);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):t},nr=e=>{let t=Number(e);return Number.isFinite(t)?(t=(t%360+360)%360,t>180?t-360:t):0};function rr(e,t,i){return nr(t)?[e.x,e.y]:i([e.x,e.y])}function or(e,t,i,n){const r=Math.abs(i[0]-t[0]),o=Math.abs(i[1]-t[1]);return"line"===e?Math.hypot(r,o)>=n:r>=n&&o>=n}const sr=(e,t,i)=>{const n=Number(e),r=Number.isFinite(t)&&t>0?t:5;return Number.isFinite(n)&&n>0?n/r*i:0},ar=(e,t,i)=>{const n=Number(e),r=Number.isFinite(t)&&t>0?t:5;return Number.isFinite(n)&&i>0?n/i*r:0},lr=(e,t,i,n=tr.widthCm)=>{const r=Number(e?.width_cm);if(Number.isFinite(r)&&r>0)return r;const o=Number(e?.width);return Number.isFinite(o)&&o>0?ar(o,t,i):n};function cr(e){const t=tr;return e.color===t.color&&e.opacity===t.opacity&&e.widthCm===t.widthCm&&e.fill===t.fill&&e.fillColor===t.fillColor&&e.fillOpacity===t.fillOpacity?null:{color:e.color,opacity:ir(e.opacity),width_cm:Math.max(.1,Math.min(100,Number(e.widthCm)||.1)),fill:!!e.fill,fill_color:e.fillColor,fill_opacity:ir(e.fillOpacity,.25)}}function hr(e,t){return{color:e.color,opacity:ir(e.opacity),width_cm:Math.max(.1,Math.min(100,Number(e.widthCm)||.1)),...t?{fill:e.fill,fill_color:e.fillColor,fill_opacity:ir(e.fillOpacity,.25)}:{}}}function dr(e){const t=e.x+e.w/2,i=e.y+e.h/2,n=nr(e.angle)*Math.PI/180,r=Math.cos(n),o=Math.sin(n),s=(e,n)=>{const s=e-t,a=n-i;return[t+s*r-a*o,i+s*o+a*r]};return[s(e.x,e.y),s(e.x+e.w,e.y),s(e.x+e.w,e.y+e.h),s(e.x,e.y+e.h)]}function ur(e){const t=dr(e),i=(e,t)=>[(e[0]+t[0])/2,(e[1]+t[1])/2],n=[(t[0][0]+t[2][0])/2,(t[0][1]+t[2][1])/2];return{points:[...t,i(t[0],t[1]),i(t[1],t[2]),i(t[2],t[3]),i(t[3],t[0]),n],segments:t.map((e,i)=>({a:e,b:t[(i+1)%4]}))}}function pr(e,t,i,n,r,o,s,a){const l=nr(e.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l),d=-Math.sin(l),u=Math.cos(l),p=e.x+e.w/2,m=e.y+e.h/2,_=t>0?-e.w/2:e.w/2,f=i>0?-e.h/2:e.h/2,g=p+_*c+f*d,v=m+_*h+f*u,y=n-g,b=r-v;let w=(y*c+b*h)*(t>0?1:-1),k=(y*d+b*u)*(i>0?1:-1),x=null;if(o){const t=w/Math.max(e.w,a),i=k/Math.max(e.h,a);x=Math.max(a/Math.max(e.w,a),a/Math.max(e.h,a),t,i),w=e.w*x,k=e.h*x}if(s>0)if(o){const t=Math.max(1,Math.round(e.w/s)),i=Math.max(1,Math.round(e.h/s));if(Math.abs(e.w-t*s)<1e-6&&Math.abs(e.h-i*s)<1e-6){const n=(e,t)=>{let i=Math.abs(e),n=Math.abs(t);for(;n;)[i,n]=[n,i%n];return Math.max(1,i)},r=1/n(t,i),o=Math.max(a/e.w,a/e.h),s=Math.max(o,Math.round((x??1)/r)*r);w=e.w*s,k=e.h*s}else{e.w>=e.h?(w=Math.round(w/s)*s,k=w*(e.h/Math.max(e.w,a))):(k=Math.round(k/s)*s,w=k*(e.w/Math.max(e.h,a)))}}else w=Math.round(w/s)*s,k=Math.round(k/s)*s;w=Math.max(a,w),k=Math.max(a,k);return{x:g+(t>0?w/2:-w/2)*c+(i>0?k/2:-k/2)*d-w/2,y:v+(t>0?w/2:-w/2)*h+(i>0?k/2:-k/2)*u-k/2,w:w,h:k,angle:nr(e.angle)||void 0}}const mr=(e,t,i)=>{const n=i[0]-t[0],r=i[1]-t[1],o=n*n+r*r;if(o<1e-12)return[...t];const s=Math.max(0,Math.min(1,((e[0]-t[0])*n+(e[1]-t[1])*r)/o));return[t[0]+n*s,t[1]+r*s]};function _r(e,t,i,n){const r=n(e);let o=null;for(const r of t.points){const t=n(r),s=Math.hypot(t[0]-e[0],t[1]-e[1]);s<=i&&(!o||se.points),segments:e.flatMap(e=>e.segments)}}const gr=Math.tan(.25*Math.PI/180);function vr(e,t){const i=Math.abs(Number(t[0])-Number(e[0])),n=Math.abs(Number(t[1])-Number(e[1]));if(!([i,n].every(Number.isFinite)&&i>0&&n>0))return null;const r=i>=n?"horizontal":"vertical",o=Math.max(i,n),s=Math.min(i,n);return s/o>gr?null:{axis:r,major:o,minor:s,angleDegrees:180*Math.atan2(s,o)/Math.PI}}function yr(e,t){const i=vr(e,t);return i?"horizontal"===i.axis?[Number(t[0]),Number(e[1])]:[Number(e[0]),Number(t[1])]:[Number(t[0]),Number(t[1])]}const br=e=>`${e[0]},${e[1]}`,wr=(e,t)=>e[0]===t[0]&&e[1]===t[1],kr=(e,t)=>{const i=br(e),n=br(t);return i{let t=0;for(let i=0;i{if(e.length<3)return!1;for(let t=0;t1e-12},Sr=e=>{for(let t=0;t+1wr(e,t.from)?[...t.to]:[...e],Rr=(e,t)=>e.map(e=>Array.isArray(e?.poly)?{...e,poly:e.poly.map(e=>Mr(e,t))}:e),Tr=(e,t,i)=>{const n=e.flatMap((e,t)=>Et(e)?.some(e=>wr(e,i.from))?[t]:[]);if(!n.length)return!1;for(const i of n){const n=Et(e[i]),r=Et(t[i]);if(!n||!r)continue;if(!$r(r))return!1;const o=Math.sign(xr(n)),s=Math.sign(xr(r));if(o&&s&&o!==s)return!1}const r=new Set;for(const e of n){const i=Et(t[e]);if(i)for(let n=0;n{let i=0;for(const n of e){const e=Et(n);if(e)for(let n=0;n{const{a:i,b:n,axis:r}=e,o="horizontal"===r?{from:n,to:[n[0],i[1]]}:{from:n,to:[i[0],n[1]]},s="horizontal"===r?{from:i,to:[i[0],n[1]]}:{from:i,to:[n[0],i[1]]};return[{move:o,preservedDegree:Cr(t,i)},{move:s,preservedDegree:Cr(t,n)}].sort((e,t)=>t.preservedDegree-e.preservedDegree||br(e.move.to).localeCompare(br(t.move.to))||br(e.move.from).localeCompare(br(t.move.from))).map(e=>e.move)};function zr(e){const t=JSON.parse(JSON.stringify(e||{}));let i=Array.isArray(t.rooms)?t.rooms:[];const n=new Map;for(const e of i){const t=Et(e);if(t)for(let e=0;ee.key.localeCompare(t.key))){let t=null;for(const n of Dr(e,i)){const e=a.get(br(n.from));if(e&&e!==br(n.to))continue;const r=Rr(i,n);if(Tr(i,r,n)){t=n,i=r;break}}t?(a.set(br(t.from),br(t.to)),r++,s=Math.max(s,Math.hypot(t.to[0]-t.from[0],t.to[1]-t.from[1]))):o++}t.rooms=i;for(const e of t.room_drafts||[]){if(!Array.isArray(e?.points)||e.points.length<2)continue;const t=e.points.map(e=>[...e]),i=t.slice(0,-1).flatMap((i,n)=>{const r=t[n+1],o=vr(i,r);return o?[{key:`${String(e.id||"")}:${n}`,a:[...i],b:[...r],axis:o.axis}]:[]});let n=t;for(const e of i){let t=null;for(const i of Dr(e,[{poly:n}])){const e=n.map(e=>Mr(e,i)),r=e.length>=4&&wr(e[0],e[e.length-1]),o=r?e.slice(0,-1):e;if(r?$r(o):Sr(o)){t=i,n=e;break}}t?(r++,s=Math.max(s,Math.hypot(t.to[0]-t.from[0],t.to[1]-t.from[1]))):o++}e.points=n}for(const e of t.partitions||[]){if(!Array.isArray(e?.a)||!Array.isArray(e?.b))continue;const i=vr(e.a,e.b);if(!i)continue;const n={key:String(e.id||kr(e.a,e.b)),a:[...e.a],b:[...e.b],axis:i.axis};let a=null;for(const i of Dr(n,[{poly:[e.a,e.b]}])){const n=Mr(e.a,i),r=Mr(e.b,i),o=Math.hypot(r[0]-n[0],r[1]-n[1]);if(!(o>0))continue;const s=(t.openings||[]).filter(t=>"partition"===t?.host?.kind&&t.host.id===e.id).every(e=>{const t=Number(e.host.t),i=Number(e.length);return Number.isFinite(t)&&t>=0&&t<=1&&Number.isFinite(i)&&i>0&&t*o-i/2>=-1e-12&&t*o+i/2<=o+1e-12});if(s){a=i,e.a=n,e.b=r;break}}a?(r++,s=Math.max(s,Math.hypot(a.to[0]-a.from[0],a.to[1]-a.from[1]))):o++}return{space:t,report:{wallsStraightened:r,wallsStraightenSkipped:o,maxStraightenShift:s},changed:r>0}}const Ar=10**9,Pr=240,Or=1e-4;function Fr(e){if(Array.isArray(e))return e.map(e=>Fr(e));if(null!==e&&"object"==typeof e){const t={};for(const[i,n]of Object.entries(e))t[i]=Fr(n);return t}return e}function Ir(e){if("number"!=typeof e||!Number.isFinite(e))return e;const t=(e<0||Object.is(e,-0)?-1:1)*(Math.floor(Math.abs(e)*Ar+.5)/Ar);return 0===t?0:t}function Er(e){if("number"!=typeof e||!Number.isFinite(e))return e;const t=e*Pr,i=Math.round(t);if(Math.abs(t-i)null!==Nr(e)):[]}function Wr(e,t){for(const i of t)Object.prototype.hasOwnProperty.call(e,i)&&(e[i]=Ir(e[i]))}function qr(e,t){for(const i of t)Object.prototype.hasOwnProperty.call(e,i)&&(e[i]=Er(e[i]))}function Br(e){if(Array.isArray(e))for(let t=0;t{for(const r of n){const n=t[r];"number"==typeof n&&Number.isFinite(n)&&i(n,e)}},n=t=>{if(Array.isArray(t))for(let n=0;n{if(Array.isArray(e))for(const t of e)n(t)};for(const i of Lr(e.rooms))t(i,["x","y","w","h"]),r(i.poly);for(const t of Lr(e.walls))n(t.a),n(t.b);for(const t of Lr(e.wall_segments))n(t.a),n(t.b);for(const i of Lr(e.openings))t(i,["x","y"]);for(const i of Lr(e.decor))"line"===i.kind?t(i,["x1","y1","x2","y2"]):"rect"===i.kind||"ellipse"===i.kind||"furniture"===i.kind?t(i,["x","y","w","h"]):"text"===i.kind&&t(i,["x","y"]);for(const t of Lr(e.room_drafts))r(t.points);for(const t of Lr(e.partitions))n(t.a),n(t.b);for(const t of Lr(e.wall_columns))n(t.center);for(const t of Lr(e.open_spans))n(t.a),n(t.b)}const r=Nr(t);if(!r)return;const o=new Map;if(n)for(const e of Lr(n.spaces))null!=e.id&&o.set(String(e.id),e);for(const e of Object.values(r)){const t=Nr(e);if(!t)continue;const n=null!=t.s&&o.get(String(t.s))||null;for(const e of["x","y"]){const r=t[e];"number"==typeof r&&Number.isFinite(r)&&i(r,n)}}}function Vr(e){const t=Number(e?.cell_cm);return t>0?t:5}function Gr(e,t={}){const i={canonicalized:0,far:0,maxShift:0,maxShiftCm:0,spaces:[],bySpace:new Map},n=Lr(Nr(e)?.spaces);let r=5;for(const e of n)r=Math.max(r,Vr(e));Ur(e,t,(e,t)=>{const n=e*Pr,o=Math.abs(n-Math.round(n)),s=Er(e),a=o>0&&o=Or))return;const l=a&&"number"==typeof s?Math.abs(s-e):0;if(a?i.canonicalized++:i.far++,i.maxShift=Math.max(i.maxShift,l),i.maxShiftCm=Math.max(i.maxShiftCm,l*Pr*(t?Vr(t):r)),!t?.id||!a)return;const c=String(t.id);let h=i.bySpace.get(c);h||(h={spaceId:c,space:String(t.title||c),canonicalized:0,far:0,maxShift:0,maxShiftCm:0},i.bySpace.set(c,h)),h.canonicalized++,h.maxShift=Math.max(h.maxShift,l),h.maxShiftCm=Math.max(h.maxShiftCm,l*Pr*Vr(t))}),i.bySpace.size&&Ur(e,t,(e,t)=>{if(!t?.id)return;const n=i.bySpace.get(String(t.id));if(!n)return;const r=e*Pr;Math.abs(r-Math.round(r))>=Or&&n.far++}),i.spaces=[...i.bySpace.values()];const{bySpace:o,...s}=i;return s}function Kr(e){return function(e){const t=e,i=Nr(t);i&&qr(i,["x","y"]);return t}(Fr(e))}function Yr(e){const t=e,i=Nr(t);if(!i)return t;for(const e of Object.values(i)){const t=Nr(e);t&&qr(t,["x","y"])}return t}function Xr(e){return Zr(Fr(e))}function Zr(e){const t=e,i=Nr(t);if(!i)return t;for(const e of Lr(i.spaces)){Wr(e,["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"]);for(const t of Lr(e.rooms))qr(t,["x","y","w","h"]),jr(t.poly);for(const t of Lr(e.walls))Br(t.a),Br(t.b);for(const t of Lr(e.wall_segments))Br(t.a),Br(t.b);for(const t of Lr(e.openings)){qr(t,["x","y"]),Wr(t,["angle","length"]);const e=Nr(t.host);e&&Wr(e,["t"])}for(const t of Lr(e.decor))"line"===t.kind?qr(t,["x1","y1","x2","y2"]):"rect"===t.kind||"ellipse"===t.kind||"furniture"===t.kind?(qr(t,["x","y","w","h"]),Wr(t,["angle"])):"text"===t.kind&&(qr(t,["x","y"]),Wr(t,["scale","angle"]));for(const t of Lr(e.room_drafts))jr(t.points);for(const t of Lr(e.partitions))Br(t.a),Br(t.b);for(const t of Lr(e.wall_columns))Br(t.center),"square"===t.shape&&Wr(t,["angle"]);for(const t of Lr(e.open_spans))Br(t.a),Br(t.b)}for(const e of Lr(i.markers))Wr(e,["angle"]);return t}const Jr=15,Qr=8;function eo(e,t,i,n,r){const o=i[0]-e[0],s=i[1]-e[1],a=Math.hypot(o,s);if(!(a>0)||a<=r+1e-9)return null;const l=o/a,c=s/a,h=t=>{const n=(t[0]-e[0])*l+(t[1]-e[1])*c,o=(r-n)/(a-n);return!Number.isFinite(o)||o<-1e-9||o>1+1e-9?null:[t[0]+(i[0]-t[0])*o,t[1]+(i[1]-t[1])*o]},d=h(t),u=h(n);return d&&u?[[e[0],e[1]],t,d,u,n]:null}const to=Math.sin(.25*Math.PI/180);function io(e,t){return Number.isFinite(e)&&e>0&&Number.isFinite(t)&&t>0&&e*t<3}function no(e){const t=Number(e)>0?Number(e):5;if(5===t)return 8;const i=5/t*8;return Math.min(80,Math.max(.5,i))}function ro(e,t){return Number.isFinite(e)&&e>0&&Number.isFinite(t)&&t>0&&e*t<2}function oo(e){return Number.isFinite(e)?Math.max(1,Math.min(100,e)):1}function so(e,t){return!Number.isFinite(e)||e<0?"":String(t?Math.round(e/2.54*100)/100:Math.round(100*e)/100)}function ao(e,t,i){if(!Number.isFinite(e)||e<=0)return 0;const n=Number(t)>0?Number(t):5;return oo(e)/n*i}function lo(e,t){return t>0&&Number.isFinite(e)?Math.round(e/t)*t:e}function co(e){return Math.max(1e-6*Math.abs(e),1e-9)}function ho(e,t){if(!(t>0&&Number.isFinite(e)))return e;const i=lo(e,t);return Math.abs(i-e)<=co(t)?i:e}function uo(e,t){let i=t[0]-e[0],n=t[1]-e[1];const r=Math.hypot(i,n);return r<1e-12?[1,0]:(i/=r,n/=r,(i<-1e-12||Math.abs(i)<=1e-12&&n<0)&&(i=-i,n=-n),[i,n])}function po(e,t,i){const n=[ho(e[0],i),ho(e[1],i)],r=[ho(t[0],i),ho(t[1],i)],o=lo((n[0]+r[0])/2,i),s=lo((n[1]+r[1])/2,i),[a,l]=uo(n,r);let c=Math.atan2(l,a);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)},${s.toFixed(d)}@${h.toFixed(4)}`}function mo(e,t,i,n){return 1===n?po(e,t,i):po([e[0]/n,e[1]/n],[t[0]/n,t[1]/n],i)}function _o(e,t){if(!Array.isArray(e.a)||!Array.isArray(e.b)||e.a.length<2||e.b.length<2)return null;const i=[Number(e.a[0]),Number(e.a[1]),Number(e.b[0]),Number(e.b[1])];if(!i.every(Number.isFinite))return null;const n=t>0?t:1;return[[i[0]*n,i[1]*n],[i[2]*n,i[3]*n]]}function fo(e,t,i,n,r){const o=r>0?r:1;return{key:mo(e,t,n,o),cm:oo(i),a:[e[0]/o,e[1]/o],b:[t[0]/o,t[1]/o]}}function go(e,t){const i=t>0?t:1,n=[];for(const t of e){const e=t.key.lastIndexOf("@");if(e<0)continue;const[r,o]=t.key.slice(0,e).split(",").map(Number),s=Number(t.key.slice(e+1));[r,o,s].every(Number.isFinite)&&n.push({w:t,x:r*i,y:o*i,ang:s})}return n}function vo(e,t){const[i,n]=uo(e,t);let r=Math.atan2(n,i);return r<0&&(r+=Math.PI),r}function yo(e,t){let i=Math.abs(e-t);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function bo(e,t,i,n,r=1){if(!e?.length)return null;const o=mo(t,i,n,r),s=e.find(e=>e.key===o);if(s)return s;const a=r>0?r:1,l=co(n)*a,c=(e,t)=>Math.abs(e[0]-t[0])<=l&&Math.abs(e[1]-t[1])<=l;for(const n of e){const e=_o(n,a);if(e&&(c(e[0],t)&&c(e[1],i)||c(e[0],i)&&c(e[1],t)))return n}const h=(t[0]+i[0])/2,d=(t[1]+i[1])/2,u=vo(t,i),p=Math.max(.5*n,1e-9)*a;for(const t of go(e,a))if(yo(t.ang,u)&&Math.hypot(t.x-h,t.y-d)<=p)return t.w;return null}function wo(e,t,i,n,r=1){const o=bo(e,t,i,n,r);if(o&&o.cm>0)return oo(o.cm);const s=function(e,t,i,n,r){if(!e?.length)return null;const o=r>0?r:1,s=Math.hypot(i[0]-t[0],i[1]-t[1]);if(s<1e-12)return null;const a=vo(t,i),l=Math.max(.5*n,1e-9)*o;let c=null;for(const n of e){if(!(n.cm>0))continue;const e=_o(n,o);if(!e)continue;const r=Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1]);if(r<1e-12||!yo(vo(e[0],e[1]),a))continue;if(hs(t[0],t[1],e[0][0],e[0][1],e[1][0],e[1][1])>l||hs(i[0],i[1],e[0][0],e[0][1],e[1][0],e[1][1])>l)continue;if(r+l(o.has(e.key)||(e=>{const t=_o(e,n);if(!t)return!1;const[i,o]=t,a=o[0]-i[0],c=o[1]-i[1],h=Math.hypot(a,c);if(h<=l)return!1;if(!s.some(e=>{const t=[e[0],e[1]],n=[e[2],e[3]];return yo(vo(i,o),vo(t,n))&&hs(i[0],i[1],t[0],t[1],n[0],n[1])<=l&&hs(o[0],o[1],t[0],t[1],n[0],n[1])<=l}))return!1;const d=(r||[]).some(e=>{const t=[e[0],e[1]],n=[e[2],e[3]];if(!yo(vo(i,o),vo(t,n)))return!1;const r=e=>Math.abs((e[0]-i[0])*c-(e[1]-i[1])*a)/h;if(r(t)>l||r(n)>l)return!1;const s=h*h,d=((t[0]-i[0])*a+(t[1]-i[1])*c)/s,u=((n[0]-i[0])*a+(n[1]-i[1])*c)/s;return Math.min(1,Math.max(d,u))-Math.max(0,Math.min(d,u))>l/h});return!d})(e))&&e.cm>=1&&e.cm<=100)}function xo(e,t,i,n=8){const[r,o]=uo(e,t);let s=Math.atan2(o,r);s<0&&(s+=Math.PI);let a=i*Math.PI/180%Math.PI;a<0&&(a+=Math.PI);let l=Math.abs(s-a);return l>Math.PI/2&&(l=Math.PI-l),l<=n*Math.PI/180}function $o(e,t,i,n,r=1,o="affine",s){if(!e?.length)return[];if(t.length!==i.length)return"fixed-topology"===o&&s?.(),e.slice();const a=r>0?r:1,l=Math.max(.5*n,1e-9)*a,c=Math.max(n*a*1e-6,1e-9),h=[],d=new Map,u=new Map,p=(e,t,i)=>{const n=e.get(t)||new Set;n.add(i),e.set(t,n)};for(let e=0;e[e[0]+(t[0]-e[0])*i,e[1]+(t[1]-e[1])*i],_=(e,t)=>Math.hypot(e[0]-t[0],e[1]-t[1])<=c,f=(e,t)=>{if("fixed-topology"===o){const i=t.na[0]-t.oa[0],n=t.na[1]-t.oa[1],r=t.nb[0]-t.ob[0],o=t.nb[1]-t.ob[1];return Math.hypot(i-r,n-o)<=c?[e[0]+i,e[1]+n]:_(e,t.oa)?[...t.na]:_(e,t.ob)?[...t.nb]:[...e]}const i=Math.max(0,Math.min(1,((e[0]-t.oa[0])*t.dx+(e[1]-t.oa[1])*t.dy)/t.len2));return m(t.na,t.nb,i)},g=(e,t)=>{const[i,n]=uo(e,t);return(t[0]-e[0])*i+(t[1]-e[1])*n>=0?[[...e],[...t]]:[[...t],[...e]]},v=[],y=[],b=(e,t,i)=>{if(Math.hypot(t[0]-e[0],t[1]-e[1])<=c)return;const[r,o]=g(e,t),s=oo(i);if(y.some(e=>e.entry.cm===s&&_(e.span[0],r)&&_(e.span[1],o)))return;const l=fo(r,o,s,n,a);v.push(l),y.push({entry:l,span:[r,o]})};for(const t of e){const e=_o(t,a);if(e){const[i,n]=g(e[0],e[1]),r=n[0]-i[0],s=n[1]-i[1],a=r*r+s*s,d=Math.sqrt(a);if(d<=c){v.push({...t,cm:oo(t.cm)});continue}const u=[];for(const e of h){if(!yo(vo(i,n),vo(e.oa,e.ob)))continue;const t=e=>Math.abs((e[0]-i[0])*s-(e[1]-i[1])*r)/d;if(t(e.oa)>l||t(e.ob)>l)continue;const o=((e.oa[0]-i[0])*r+(e.oa[1]-i[1])*s)/a,h=((e.ob[0]-i[0])*r+(e.ob[1]-i[1])*s)/a,p=Math.max(0,Math.min(o,h)),m=Math.min(1,Math.max(o,h));(m-p)*d>c&&u.push({lo:p,hi:m,move:e})}if(!u.length){"fixed-topology"===o?v.push({...t}):b(i,n,t.cm);continue}const p=[0,1,...u.flatMap(({lo:e,hi:t})=>[e,t])].sort((e,t)=>e-t).filter((e,t,i)=>0===t||Math.abs(e-i[t-1])*d>c),y=[];for(let e=0;e+1a>=e.lo-1e-12&&a<=e.hi+1e-12);if(!l.length){y.push([o,s]);continue}const h=[f(o,l[0].move),f(s,l[0].move)],g=l.slice(1).some(e=>{const t=f(o,e.move),i=f(s,e.move);return!_(h[0],t)||!_(h[1],i)});y.push(g?[o,s]:h)}const w=(e,t)=>{const i=e[1][0]-e[0][0],n=e[1][1]-e[0][1],r=t[1][0]-t[0][0],o=t[1][1]-t[0][1],s=Math.hypot(i,n);return!(s<=c||i*r+n*o<=0)&&Math.abs(i*o-n*r)/s<=c},k=[];for(const e of y){const t=k[k.length-1];t&&_(t[1],e[0])&&w(t,e)?t[1]=e[1]:k.push([[...e[0]],[...e[1]]])}if("fixed-topology"===o&&1===k.length){const[i,n]=k[0];if(_(i,e[0])&&_(n,e[1])||_(i,e[1])&&_(n,e[0])){v.push({...t});continue}}for(const[e,i]of k)b(e,i,t.cm);continue}if("fixed-topology"===o){const e=u.get(t.key);if(1===e?.size){const i=[...e][0];v.push(i===t.key?{...t}:{...t,key:i});continue}const i=[go([t],a)[0]];1!==a&&i.push(go([t],1)[0]);const n=i.filter(Boolean).some(e=>h.some(t=>{if(!yo(e.ang,vo(t.oa,t.ob)))return!1;const i=((e.x-t.oa[0])*t.dx+(e.y-t.oa[1])*t.dy)/t.len2;return i>=-1e-6&&i<=1.000001&&hs(e.x,e.y,t.oa[0],t.oa[1],t.ob[0],t.ob[1])<=l}));((e?.size||0)>1||n)&&s?.(),v.push({...t});continue}let i="";const r=d.get(t.key);if(1===r?.size&&(i=[...r][0]),!i){const e=go([t],a)[0];if(e){const t=new Set;for(const i of h){if(!yo(e.ang,vo(i.oa,i.ob)))continue;const r=((e.x-i.oa[0])*i.dx+(e.y-i.oa[1])*i.dy)/i.len2;if(r<-1e-6||r>1.000001)continue;if(hs(e.x,e.y,i.oa[0],i.oa[1],i.ob[0],i.ob[1])>l)continue;const o=f([e.x,e.y],i),[s,c]=uo(i.na,i.nb),h=Math.max(n*a,1e-6);t.add(mo([o[0]-s*h,o[1]-c*h],[o[0]+s*h,o[1]+c*h],n,a))}1===t.size&&(i=[...t][0])}}v.push({...t,key:i||t.key,cm:oo(t.cm)})}return v}function So(e,t,i,n,r=1,o="affine"){return $o(e,t,i,n,r,o)}function Mo(e,t,i,n,r=1,o="fixed-topology"){let s=!1;const a=$o(e,t,i,n,r,o,()=>{s=!0});return{walls:s?(e||[]).map(e=>({...e})):a,rejected:s}}function Ro(e,t,i,n=1,r=[]){const o=n>0?n:1,s=Math.abs(i),a=Math.max(s*o*Or,1e-9),l=(r||[]).flatMap(e=>{const t=_o(e,o);return t?t.map(e=>[...e]):[]}),c=e=>e.every((e,t)=>(e=>{if(!(s>0))return!0;const t=e/o/s;return Math.abs(t-Math.round(t))Math.abs(e-i[t])<=a)),h=[],d=e=>JSON.stringify([e.key,e.cm,e.a,e.b]);for(const i of e||[]){const e=_o(i,o);if(!e)continue;const[n,r]=e;if(![n[0],n[1],r[0],r[1]].every(Number.isFinite)||!c(n)||!c(r)){h.push(d(i));continue}const s=r[0]-n[0],l=r[1]-n[1],u=Math.hypot(s,l);if(u<=a){h.push(d(i));continue}const p=s/u,m=l/u,_=[];for(const e of t){const[t,i]=e;if(![t?.[0],t?.[1],i?.[0],i?.[1]].every(Number.isFinite))continue;const r=e=>Math.abs((e[0]-n[0])*m-(e[1]-n[1])*p);if(r(t)>a||r(i)>a)continue;const o=(t[0]-n[0])*p+(t[1]-n[1])*m,s=(i[0]-n[0])*p+(i[1]-n[1])*m,l=Math.max(0,Math.min(o,s)),c=Math.min(u,Math.max(o,s));c-l>a&&_.push([l,c])}_.sort((e,t)=>e[0]-t[0]||e[1]-t[1]);let f=0;for(const[e,t]of _){if(e>f+a)break;if(f=Math.max(f,t),f>=u-a)break}fe.key!==s);return null==n||n<1?a:[...a,fo(t,i,n,r,o)]}function Co(e,t,i,n,r,o=[],s=1){let a=e?e.slice():[];for(const e of function(e,t,i,n,r=1,o=[]){const s=Xo(e,t,i,n,r,o);if(!s)return[];const a=[];for(let e=0;e0||(c=To(c,t,i,n,r,s)))}return c}function zo(e){const{a:t,b:i,halfDepth:n}=e;if(!Array.isArray(t)||!Array.isArray(i)||t.length<2||i.length<2||![t[0],t[1],i[0],i[1]].every(Number.isFinite))return null;const r=i[0]-t[0],o=i[1]-t[1],s=Math.hypot(r,o);if(!(s>1e-9&&n>0&&Number.isFinite(n)))return null;const a=-o/s*n,l=r/s*n;return[[t[0]+a,t[1]+l],[i[0]+a,i[1]+l],[i[0]-a,i[1]-l],[t[0]-a,t[1]-l]]}function Ao(e,t,i){return Math.hypot(e[0]-t[0],e[1]-t[1])<=i}function Po(e,t,i){const n=t.b[0]-t.a[0],r=t.b[1]-t.a[1],o=n*n+r*r;if(!(o>i*i))return!1;const s=((e[0]-t.a[0])*n+(e[1]-t.a[1])*r)/o;if(!(s>0&&s<1))return!1;const a=[t.a[0]+n*s,t.a[1]+r*s];return Math.hypot(e[0]-a[0],e[1]-a[1])<=i}function Oo(e,t,i,n){const r=Math.hypot(t,i);if(!(r>1e-9&&n>0))return;const o=[t/r,i/r],s=e.find(e=>Math.abs(e.u[0]*o[1]-e.u[1]*o[0])<1e-9&&e.u[0]*o[0]+e.u[1]*o[1]>1-1e-9);s?s.halfDepth=Math.max(s.halfDepth,n):e.push({u:o,halfDepth:n})}function Fo(e,t){try{const i=Mt(ps(e),ps(t));let n=null,r=0;for(const e of i||[]){const t=e?.[0]||[],i=Math.abs(Wo(t));t.length>=4&&i>r&&(r=i,n=t.slice(0,-1).map(e=>[e[0],e[1]]))}return n}catch{return null}}function Io(e,t=1e-6){const i=(e||[]).map((e,t)=>({segment:e,index:t})).filter(({segment:e})=>e&&Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2&&e.a.every(Number.isFinite)&&e.b.every(Number.isFinite)&&Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>1e-9);if(i.length<2)return[];const n=Math.max(Number.isFinite(t)?t:0,1e-9),r=i.flatMap(({segment:e})=>[e.a,e.b]).map(e=>[e[0],e[1]]).sort((e,t)=>e[0]-t[0]||e[1]-t[1]),o=[];for(const e of r)o.some(t=>Ao(t,e,n))||o.push(e);const s=[];for(const e of o){const t=[];let r=!1;for(const{segment:o,index:s}of i){const i=Math.hypot(o.b[0]-o.a[0],o.b[1]-o.a[1]);Ao(e,o.a,n)?t.push({u:[(o.b[0]-o.a[0])/i,(o.b[1]-o.a[1])/i],halfDepth:o.halfDepth,length:i,index:s}):Ao(e,o.b,n)?t.push({u:[(o.a[0]-o.b[0])/i,(o.a[1]-o.b[1])/i],halfDepth:o.halfDepth,length:i,index:s}):Po(e,o,n)&&(r=!0)}if(r||2!==t.length)continue;const[o,a]=t,l=o.u[0]*a.u[1]-o.u[1]*a.u[0];if(Math.abs(l)<1e-9)continue;const c=l<0?1:-1,h=[-o.u[1],o.u[0]],d=[-a.u[1],a.u[0]],u=[e[0]+h[0]*o.halfDepth*c,e[1]+h[1]*o.halfDepth*c],p=[e[0]-d[0]*a.halfDepth*c,e[1]-d[1]*a.halfDepth*c];if(!jo(u,o.u,p,a.u))continue;const m=[[o,a,p,[d[0]*-c,d[1]*-c]],[a,o,u,[h[0]*c,h[1]*c]]];for(const[t,,i,r]of m){const o=Math.min(2*t.halfDepth,t.length),a=[-t.u[1]*t.halfDepth,t.u[0]*t.halfDepth],l=[[e[0]+a[0],e[1]+a[1]],[e[0]+t.u[0]*o+a[0],e[1]+t.u[1]*o+a[1]],[e[0]+t.u[0]*o-a[0],e[1]+t.u[1]*o-a[1]],[e[0]-a[0],e[1]-a[1]]],c=e=>(e[0]-i[0])*r[0]+(e[1]-i[1])*r[1],h=[];for(let e=0;e=-1e-12&&h.push(t),n>1e-12&&r<-1e-12||n<-1e-12&&r>1e-12){const e=n/(n-r);h.push([t[0]+(i[0]-t[0])*e,t[1]+(i[1]-t[1])*e])}}h.length>=3&&Math.abs(Wo(h))>n*n&&s.push({segmentIndex:t.index,wedge:h})}}return s}function Eo(e,t=1e-6){const i=(e||[]).filter(e=>e&&Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2&&e.a.every(Number.isFinite)&&e.b.every(Number.isFinite)&&Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>1e-9);if(i.length<2)return[];const n=Math.max(Number.isFinite(t)?t:0,1e-9),r=i.flatMap(e=>[e.a,e.b]).map(e=>[e[0],e[1]]).sort((e,t)=>e[0]-t[0]||e[1]-t[1]),o=[];for(const e of r)o.some(t=>Ao(t,e,n))||o.push(e);const s=[],a=i.map((e,t)=>({roomId:"",a:[e.a[0],e.a[1]],b:[e.b[0],e.b[1]],key:`join-${t}`,kind:"outer",cm:0,open:!1,half:e.halfDepth})),l=ts(a,n);for(const e of is(l).fans)s.push(e);const c=e=>!!ns(l,e);for(const e of o){if(c(e))continue;const t=[];for(const r of i)Ao(e,r.a,n)?Oo(t,r.b[0]-r.a[0],r.b[1]-r.a[1],r.halfDepth):Ao(e,r.b,n)?Oo(t,r.a[0]-r.b[0],r.a[1]-r.b[1],r.halfDepth):Po(e,r,n)&&(Oo(t,r.a[0]-e[0],r.a[1]-e[1],r.halfDepth),Oo(t,r.b[0]-e[0],r.b[1]-e[1],r.halfDepth));if(!(t.length<2)){t.sort((e,t)=>Math.atan2(e.u[1],e.u[0])-Math.atan2(t.u[1],t.u[0])||e.halfDepth-t.halfDepth);for(let i=0;in*n&&s.push(_)}}}return s}function Ho(e,t,i,n){if(!(t>0)||!e||e.length<2)return"";if(i&&e.length>=3){let i=e;const r=e[e.length-1];if(e.length>=4&&Math.hypot(e[0][0]-r[0],e[0][1]-r[1])<1e-9&&(i=e.slice(0,-1)),i.length>=3){const e=i.map((e,i)=>n?.[i]||t),r=As(i,e),o=Go(i,e);if(r&&o)return`${Ko(r)} ${Ko(Yo(o))}`}}const r=[];for(let i=0;i=1e-9&&a>0&&r.push({a:o,b:s,halfDepth:a})}const o=r.map(zo);for(const{segmentIndex:e,wedge:t}of Io(r)){const i=o[e];if(!i)continue;const n=Fo(i,t);n&&(o[e]=n)}const s=[...o.filter(e=>!!e),...Eo(r)],a=function(e){let t=null;try{for(const i of e){if(i.length<3)continue;const e=ps(i);t=t?$t(t,e):[e]}return t}catch{return null}}(s);return a?Ss(a):s.map(e=>Ko(e)).join(" ")}function No(e,t,i,n,r=1){if(!i.length)return!1;const o=Lo(n,r),s=(e[0]+t[0])/2,a=(e[1]+t[1])/2,[l,c]=uo(e,t);for(const e of i){const[t,i]=uo([e[0],e[1]],[e[2],e[3]]);if(!(Math.abs(l*i-c*t)>.05)&&hs(s,a,e[0],e[1],e[2],e[3])<=o)return!0}return!1}function Lo(e,t){return Math.max(e*(t>0?t:1)*.04,1e-9)}function Wo(e){let t=0;for(let i=0;ie[1]!=l>e[1]&&e[0]<(a-o)*(e[1]-s)/(l-s+0)+o&&(i=!i)}return i}([c[0]+.001*a,c[1]+.001*l],e)||(a=-a,l=-l),[a,l]}function Bo(e,t){const i=e[0]*t[1]-e[1]*t[0],n=e[0]*t[0]+e[1]*t[1];return Math.abs(i)<1e-9&&n>0}function jo(e,t,i,n){const r=t[0]*n[1]-t[1]*n[0];if(Math.abs(r)<1e-12)return null;const o=[i[0]-e[0],i[1]-e[1]],s=(o[0]*n[1]-o[1]*n[0])/r;return[e[0]+s*t[0],e[1]+s*t[1]]}function Uo(e,t,i){const n=e?.length||0;if(n<3||!Array.isArray(i)||i.length!==n)return 0;const r=(Math.trunc(t)%n+n)%n,o=e[r],s=e[(r+1)%n],a=s[0]-o[0],l=s[1]-o[1],c=Math.hypot(a,l);if(!(c>0))return 0;const h=Math.max(0,Number(i[r])||0);if(!(h>0))return c;const d=[a/c,l/c],u=qo(e,r),p=[o[0]+u[0]*h,o[1]+u[1]*h],m=t=>{const r=Math.max(0,Number(i[t])||0);if(!(r>0))return null;const s=e[t],a=e[(t+1)%n],l=a[0]-s[0],c=a[1]-s[1],h=Math.hypot(l,c);if(!(h>0))return null;const u=qo(e,t),m=jo(p,d,[s[0]+u[0]*r,s[1]+u[1]*r],[l/h,c/h]);return m?(m[0]-o[0])*d[0]+(m[1]-o[1])*d[1]:null},_=m((r-1+n)%n)??0,f=(m((r+1)%n)??c)-_;return f>0?f:0}function Vo(e,t,i,n,r,o,s,a=1){const l=(e||[]).find(e=>e?.id===t),c=Et(l);if(!c||c.length<3)return null;const h=os(e,t,i,n,r,o,s,a);if(!h)return c.map(()=>0);const d=4*Lo(r,a);return c.map((e,t)=>{const i=c[(t+1)%c.length],n=[(e[0]+i[0])/2,(e[1]+i[1])/2];for(let e=0;e!(e>0)))return e.map(e=>[e[0],e[1]]);const r=[];for(let o=0;o0||u>0)){r.push([e[o][0],e[o][1]]);continue}if(d>0!=u>0){const t=e[o],i=d>0?[t[0]+p*d,t[1]+m*d]:[t[0],t[1]],n=u>0?[t[0]+_*u,t[1]+f*u]:[t[0],t[1]];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}if(Bo(w,k)){const t=e[o],i=[t[0]+p*d,t[1]+m*d],n=[t[0]+_*u,t[1]+f*u];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}const S=jo(x,w,$,k),M=Math.max(d,u,1e-9),R=ns(i,e[o])?.limit??4*M;if(S){const i=Math.hypot(S[0]-e[o][0],S[1]-e[o][1]);if(Number.isFinite(i)&&i<=R){r.push(S);continue}if(Rs(e,t,o)){r.push(S);continue}}d>0&&r.push([e[o][0]+p*d,e[o][1]+m*d]),u>0&&r.push([e[o][0]+_*u,e[o][1]+f*u]),d>0||u>0||r.push([e[o][0],e[o][1]])}return r.length>=3?r:null}function Ko(e,t=!0){if(!e.length)return"";let i=`M ${e[0][0]} ${e[0][1]}`;for(let t=1;te?.id===t),a=Et(s);if(!a||a.length<3)return null;const l=Lo(n,r),c=[];for(const i of e||[]){if(!i||i.id===t)continue;const e=Et(i);if(e)for(const t of cn(a,e,l))c.push([t[0],t[1]],[t[2],t[3]])}for(const e of i||[])c.push([e[0],e[1]],[e[2],e[3]]);for(const e of o||[]){const t=_o(e,r);t&&c.push(t[0],t[1])}const h=[],d=[];for(let e=0;el)continue;const s=((e[0]-t[0])*(i[0]-t[0])+(e[1]-t[1])*(i[1]-t[1]))/(n*n);s<=r||s>=1-r||(o.some(e=>Math.abs(e-s)*n<=2*l)||o.push(s))}o.sort((e,t)=>e-t);for(const n of o)h.push([t[0]+(i[0]-t[0])*n,t[1]+(i[1]-t[1])*n]),d.push(e)}return{poly:h,parent:d,orig:a}}function Zo(e,t,i,n){const r=t.poly.length,o=new Array(r).fill(0);if(!e?.length)return o;const s=new Set,a=[];for(let l=0;l0?(o[l]=oo(c.cm),s.add(c.key)):a.push(l)}if(!a.length)return o;const l=n>0?n:1,c=Math.max(.5*i,1e-9)*l,h=go(e,l).filter(e=>e.w.cm>0);for(let e=a.length-1;e>=0;e--){const i=a[e],n=t.poly[i],s=t.poly[(i+1)%r],d=vo(n,s);let u=null;for(const e of h){const t=_o(e.w,l);if(!t||!yo(vo(t[0],t[1]),d))continue;if(hs(n[0],n[1],t[0][0],t[0][1],t[1][0],t[1][1])>c||hs(s[0],s[1],t[0][0],t[0][1],t[1][0],t[1][1])>c)continue;const i=Math.hypot(s[0]-n[0],s[1]-n[1]),r=Math.hypot(t[1][0]-t[0][0],t[1][1]-t[0][1]),o=Math.max(0,r-i);(!u||oc||hs(r[0],r[1],t[0][0],t[0][1],t[1][0],t[1][1])>c)continue;i=!0,o=Math.max(0,Math.hypot(t[1][0]-t[0][0],t[1][1]-t[0][1])-m)}else{if(hs(e.x,e.y,n[0],n[1],r[0],r[1])>c)continue;o=Math.hypot(e.x-d,e.y-u)}(!p||i&&!p.exact||i===p.exact&&o0?i:1,o=(e||[]).filter(e=>e&&!e.open&&null!==e.kind&&Number.isFinite(e.half)&&e.half>0&&Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2&&[e.a[0],e.a[1],e.b[0],e.b[1]].every(Number.isFinite)&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>n).sort((e,t)=>e.key.localeCompare(t.key)||e.a[0]-t.a[0]||e.a[1]-t.a[1]||e.b[0]-t.b[0]||e.b[1]-t.b[1]||e.half-t.half),s=new Map;for(const e of o){const t=s.get(e.key);t?e.half>t.half&&s.set(e.key,{...t,half:e.half}):s.set(e.key,e)}const a=[...s.values()].flatMap(e=>[{point:[e.a[0],e.a[1]],other:e.b,halfDepth:e.half,kind:e.kind,key:e.key},{point:[e.b[0],e.b[1]],other:e.a,halfDepth:e.half,kind:e.kind,key:e.key}]).sort((e,t)=>e.point[0]-t.point[0]||e.point[1]-t.point[1]||e.other[0]-t.other[0]||e.other[1]-t.other[1]||e.halfDepth-t.halfDepth),l=new Map;for(const e of a){const[t,i]=Jo(e.point,n),r=Qo(t,i),o=l.get(r)||[];o.push(e),l.set(r,o)}const c=[],h=new Map;for(const e of a){const t=es(h,e.point,n).filter(t=>Math.hypot(t.point[0]-e.point[0],t.point[1]-e.point[1])<=n).sort((t,i)=>Math.hypot(t.point[0]-e.point[0],t.point[1]-e.point[1])-Math.hypot(i.point[0]-e.point[0],i.point[1]-e.point[1])||t.point[0]-i.point[0]||t.point[1]-i.point[1]);let i=t[0];if(!i){i={point:[...e.point],rays:[]},c.push(i);const[t,r]=Jo(i.point,n),o=Qo(t,r),s=h.get(o)||[];s.push(i),h.set(o,s)}const r=e.other[0]-e.point[0],o=e.other[1]-e.point[1],s=Math.hypot(r,o);if(!(s>n))continue;const a=[r/s,o/s];let l=Math.atan2(a[1],a[0]);l<0&&(l+=2*Math.PI),i.rays.push({u:a,halfDepth:e.halfDepth,length:s,angle:l})}const d=[],u=e=>{const t=1e-9*Math.max(1,r),i=e.filter(e=>Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Number.isFinite(e.length)&&e.length>n);return i.filter((e,n)=>!i.some((i,r)=>r!==n&&i.halfDepth>=e.halfDepth-t&&i.length>=e.length-t&&(i.halfDepth>e.halfDepth+t||i.length>e.length+t||re.length-t.length||e.halfDepth-t.halfDepth).map(e=>({...e}))};for(const e of c){const t=e.rays.sort((e,t)=>e.angle-t.angle||e.length-t.length||e.halfDepth-t.halfDepth),i=[];for(const e of t){const t=i[i.length-1];t&&Math.abs(e.angle-t.angle)<=1e-9?t.supports.push({halfDepth:e.halfDepth,length:e.length}):i.push({u:[...e.u],angle:e.angle,supports:[{halfDepth:e.halfDepth,length:e.length}]})}if(i.length>1&&2*Math.PI-i[i.length-1].angle+i[0].angle<=1e-9){const e=i.pop();i[0].supports.push(...e.supports)}if(i.length<3)continue;const r=i.map(t=>{const i=u(t.supports),r=new Set,o=[];for(const s of i){const i=[e.point[0]+t.u[0]*s.length,e.point[1]+t.u[1]*s.length];for(const t of es(l,i,n)){if("shared"!==t.kind||Math.hypot(t.point[0]-i[0],t.point[1]-i[1])>n)continue;const s=t.other[0]-t.point[0],a=t.other[1]-t.point[1],l=Math.hypot(s,a);if(!(l>n))continue;const c=s/l,h=a/l;if(Math.hypot(t.other[0]-e.point[0],t.other[1]-e.point[1])<=n)continue;const d=`${t.key}|${t.point[0]}|${t.point[1]}|${t.other[0]}|${t.other[1]}|${t.halfDepth}`;r.has(d)||(r.add(d),o.push({start:[t.point[0],t.point[1]],u:[c,h],length:l,halfDepth:t.halfDepth}))}}return o.sort((e,t)=>e.start[0]-t.start[0]||e.start[1]-t.start[1]||e.u[0]-t.u[0]||e.u[1]-t.u[1]||e.length-t.length||e.halfDepth-t.halfDepth),{u:[...t.u],halfDepth:Math.max(...i.map(e=>e.halfDepth)),length:Math.max(...i.map(e=>e.length)),supports:i,continuations:o}}).filter(e=>Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Number.isFinite(e.length)&&e.length>n);if(r.length<3)continue;const o=Math.max(...r.map(e=>e.halfDepth));o>0&&Number.isFinite(o)&&d.push({point:[...e.point],rays:r,halfDepth:o,limit:1.25*o})}d.sort((e,t)=>e.point[0]-t.point[0]||e.point[1]-t.point[1]);const p=new Map;for(const e of d){const[t,i]=Jo(e.point,n),r=Qo(t,i),o=p.get(r)||[];o.push(e),p.set(r,o)}return{epsilon:n,coordinateScale:r,nodes:d,index:p}}function is(e){const t={fans:[],supports:[]};if(!e?.nodes?.length)return t;const i=Math.max(e.epsilon,1e-9)**2;for(const n of e.nodes){const e=n.rays.filter(e=>Number.isFinite(e.halfDepth)&&e.halfDepth>0).map(e=>({...e,thickLength:Math.max(...e.supports.filter(t=>t.halfDepth>=e.halfDepth-1e-12).map(e=>e.length),0),angle:(()=>{const t=Math.atan2(e.u[1],e.u[0]);return t<0?t+2*Math.PI:t})()})).sort((e,t)=>e.angle-t.angle);if(e.length<2)continue;const r=n.point;for(const i of e)for(const e of i.supports){if(!(e.halfDepth>0&&e.length>0))continue;const n=-i.u[1]*e.halfDepth,o=i.u[0]*e.halfDepth,s=[r[0]+i.u[0]*e.length,r[1]+i.u[1]*e.length];t.supports.push([[r[0]+n,r[1]+o],[s[0]+n,s[1]+o],[s[0]-n,s[1]-o],[r[0]-n,r[1]-o]])}for(let n=0;n{const e=s.angle-o.angle;return e>0?e:e+2*Math.PI})();if(a<1e-9)continue;const l=a>Math.PI+1e-9,c=4*Math.max(o.halfDepth,s.halfDepth),h=[r[0]-o.u[1]*o.halfDepth,r[1]+o.u[0]*o.halfDepth],d=[r[0]+s.u[1]*s.halfDepth,r[1]-s.u[0]*s.halfDepth],u=o.u[0]*s.u[1]-o.u[1]*s.u[0],p=e=>{let t=Math.atan2(e[1]-r[1],e[0]-r[0])-o.angle;for(;t<0;)t+=2*Math.PI;return t<=a+1e-9};let m=null;if(Math.abs(u)>1e-9){const e=((d[0]-h[0])*s.u[1]-(d[1]-h[1])*s.u[0])/u,t=((d[0]-h[0])*o.u[1]-(d[1]-h[1])*o.u[0])/u,i=[h[0]+o.u[0]*e,h[1]+o.u[1]*e];(l?e<=1e-9&&t<=1e-9:e>1e-9&&e<=o.thickLength&&t<=s.thickLength)&&Math.hypot(i[0]-r[0],i[1]-r[1])<=c&&p(i)&&(m=i)}const _=e=>{Math.abs(Wo(e))>i&&t.fans.push(e)};if(m){const e=1.5*Math.max(o.halfDepth,s.halfDepth);_(eo([r[0],r[1]],h,m,d,e)??[[r[0],r[1]],h,m,d]);continue}if(l){_([[r[0],r[1]],h,d]);continue}const f=(e,t)=>Math.min(t,Math.sqrt(Math.max(c**2-e**2,0)),2*Math.max(o.halfDepth,s.halfDepth)),g=[h[0]+o.u[0]*f(o.halfDepth,o.thickLength),h[1]+o.u[1]*f(o.halfDepth,o.thickLength)],v=[d[0]+s.u[0]*f(s.halfDepth,s.thickLength),d[1]+s.u[1]*f(s.halfDepth,s.thickLength)];_([[r[0],r[1]],h,g,v,d])}}return t}function ns(e,t){return!e||!Array.isArray(t)||t.length<2||!t.slice(0,2).every(Number.isFinite)?null:es(e.index,t,e.epsilon).filter(i=>Math.hypot(i.point[0]-t[0],i.point[1]-t[1])<=e.epsilon).sort((e,i)=>Math.hypot(e.point[0]-t[0],e.point[1]-t[1])-Math.hypot(i.point[0]-t[0],i.point[1]-t[1])||e.point[0]-i.point[0]||e.point[1]-i.point[1])[0]||null}function rs(e,t,i,n,r,o,s){return ts(ss(e,t,i,n,r,o,s),4*Lo(n,s),s)}function os(e,t,i,n,r,o,s,a=1){const l=Xo(e,t,n,r,a,i);if(!l)return null;const c=function(e,t,i){const n=(e||[]).find(e=>e?.id===t),r=Et(n);if(!r)return[];const o=[];for(const n of e||[]){if(!n||n.id===t)continue;const e=Et(n);if(e)for(const t of cn(r,e,i))o.push(t)}return o}(e,t,Lo(r,a)),h=function(e,t,i,n,r){const o=Lo(n,r),s=[];for(let a=0;ahs(h,d,e[0],e[1],e[2],e[3])<=o);s.push(u?"shared":"outer")}return s}(l.poly,c,n,r,a),d=Zo(i,l,r,a),u=d.map((e,t)=>h[t]&&e>0?ao(e,o,s)/2:0);return{...l,kinds:h,cms:d,offsets:u}}function ss(e,t,i,n,r,o,s=1){const a=[];for(const l of e||[]){if(!l?.id)continue;const c=os(e,l.id,t,i,n,r,o,s);if(c)for(let e=0;e0&&(a=To(a,e.a,e.b,e.cm,n,s));return a}function ls(e,t,i,n,r,o,s=1){if(!t?.length)return[];const a=ss(e,t,i,n,r,o,s),l=new Map;for(const e of a){if(e.open||!e.kind||!e.roomId)continue;const t=l.get(e.key)||new Set;t.add(e.roomId),l.set(e.key,t)}const c=e=>{const t=[...l.get(e)||[]].sort();return 1!==t.length&&2!==t.length?`ambiguous:${e}`:`${1===t.length?"outer":"shared"}:${t.join("|")}`},h=[],d=new Set;for(const e of a)!e.open&&e.cm>0&&!d.has(e.key)&&(d.add(e.key),h.push({...e,ownerSignature:c(e.key)}));const u=[];for(const a of e||[]){if(!a?.id)continue;const l=os(e,a.id,t,i,n,r,o,s);if(l)for(let e=0;e0)||null===l.kinds[i]){e++;continue}const o=c(mo(l.poly[i],l.poly[(i+1)%l.poly.length],n,s));let a=e;for(;a+10&&u.push({a:[d[0],d[1]],b:[p[0],p[1]],key:mo(d,p,n,s),cm:r,len:m,ownerSignature:o}),e=a+1}}}u.sort((e,t)=>t.len-e.len||e.key.localeCompare(t.key));const p=[],m=new Set,_=new Set,f=4*Lo(n,s);for(const e of u){const t=h.filter(t=>!_.has(t.key)&&t.cm===e.cm&&t.ownerSignature===e.ownerSignature&&yo(vo(t.a,t.b),vo(e.a,e.b))&&hs(t.a[0],t.a[1],e.a[0],e.a[1],e.b[0],e.b[1])<=f&&hs(t.b[0],t.b[1],e.a[0],e.a[1],e.b[0],e.b[1])<=f);if(t.length){for(const e of t)_.add(e.key);m.has(e.key)||(m.add(e.key),p.push(fo(e.a,e.b,e.cm,n,s)))}}for(const e of h)_.has(e.key)||m.has(e.key)||(m.add(e.key),p.push(fo(e.a,e.b,e.cm,n,s)));return p}function cs(e,t,i,n,r,o,s,a=1){const l=Lo(r,a),c=(n[0]+n[2])/2,h=(n[1]+n[3])/2,d=vo([n[0],n[1]],[n[2],n[3]]);let u=null;for(const n of ss(e,t,i,r,o,s,a)){if(!yo(vo(n.a,n.b),d))continue;const e=hs(c,h,n.a[0],n.a[1],n.b[0],n.b[1]);e>4*l||(!u||e[e[0],e[1]]),o=Math.abs(Wo(r));r.length>=3&&o>i&&(t=r,i=o)}return t}function us(e,t,i,n,r,o,s,a=1,l,c){const h=(e||[]).find(e=>e?.id===t),d=Et(h);if(!d||d.length<3)return null;if(!i?.length)return d.map(e=>[e[0],e[1]]);const u=os(e,t,i,n,r,o,s,a);if(!u||!u.offsets.some(e=>e>0))return d.map(e=>[e[0],e[1]]);const p=c||rs(e,i,n,r,o,s,a),m=Go(u.poly,u.offsets,p);if(!m)return d.map(e=>[e[0],e[1]]);if(!p.nodes.length)return m;const _=void 0===l?Ts(e,i,n,[],r,o,s,a):null,f=l??("ok"===_?.status||"degraded-extra"===_?.status?_.roomGeom:void 0);if(f)try{const e=ds(Mt(ps(u.poly),f));if(e)return e}catch{}return function(e,t){try{return ds(St(ps(e),ps(t)))}catch{return null}}(m,u.poly)||d.map(e=>[e[0],e[1]])}function ps(e){const t=e.map(e=>[e[0],e[1]]);return t.push([e[0][0],e[0][1]]),[t]}function ms(e){return!(!Array.isArray(e)||!e.length)&&e.every(e=>Array.isArray(e)&&e.length&&e.every(e=>Array.isArray(e)&&e.length>=4&&e.every(e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1])))&&Math.abs(li(e[0]))>1e-9)}function _s(e,t,i,n){const r=n?new Set(n):null;let o=null;for(let n=0;nt.epsilon))continue;const l=bs([[e.point[0]+a[0]*n.halfDepth,e.point[1]+a[1]*n.halfDepth],[e.point[0]+s.u[0]*r+a[0]*n.halfDepth,e.point[1]+s.u[1]*r+a[1]*n.halfDepth],[e.point[0]+s.u[0]*r-a[0]*n.halfDepth,e.point[1]+s.u[1]*r-a[1]*n.halfDepth],[e.point[0]-a[0]*n.halfDepth,e.point[1]-a[1]*n.halfDepth]],t.coordinateScale);if(!l)continue;const c=ps(l);o=o?$t(o,c):c}}return o}function fs(e,t,i){let n=null;for(const r of e.rays)for(const e of r.continuations){const r=[-e.u[1],e.u[0]],o=[e.start[0]+e.u[0]*e.length,e.start[1]+e.u[1]*e.length],s=bs([[e.start[0]+r[0]*e.halfDepth,e.start[1]+r[1]*e.halfDepth],[o[0]+r[0]*e.halfDepth,o[1]+r[1]*e.halfDepth],[o[0]-r[0]*e.halfDepth,o[1]-r[1]*e.halfDepth],[e.start[0]-r[0]*e.halfDepth,e.start[1]-r[1]*e.halfDepth]],t.coordinateScale);if(!s)continue;const a=St(ps(s),i);Array.isArray(a)&&0!==a.length&&(n=n?$t(n,a):a)}return n}function gs(e,t,i=2*(4*e.halfDepth+2*t.epsilon)){const n=function(e,t=to){const i=new Set,n=Number.isFinite(t)&&t>=0?t:to;for(let t=0;te-t)}(e);return n.length?_s(e,t,i,n):null}function vs(e,t,i,n,r){const o=function(e){let t=null;for(const i of e){const e=ps(i);try{t=t?$t(t,e):e}catch{}}return t}(function(e,t,i=!1){if(!e)return[];const n=[];for(const r of e.nodes)for(let o=0;o1e-9)||h>=Math.PI-1e-9)continue;const d=[-s.u[1],s.u[0]],u=[-a.u[1],a.u[0]],p=[r.point[0]+d[0]*s.halfDepth,r.point[1]+d[1]*s.halfDepth],m=[r.point[0]-u[0]*a.halfDepth,r.point[1]-u[1]*a.halfDepth],_=jo(p,s.u,m,a.u);if(!_)continue;const f=Math.hypot(_[0]-r.point[0],_[1]-r.point[1]);if(!Number.isFinite(f)||f<=r.limit)continue;const g=t?Math.sqrt(Math.max(0,r.limit*r.limit-s.halfDepth*s.halfDepth)):0,v=t?Math.sqrt(Math.max(0,r.limit*r.limit-a.halfDepth*a.halfDepth)):0,y=bs([[p[0]+s.u[0]*g,p[1]+s.u[1]*g],[m[0]+a.u[0]*v,m[1]+a.u[1]*v],_],e.coordinateScale);if(y&&n.push(y),i){const t=_[0]-r.point[0],i=_[1]-r.point[1],o=Math.hypot(t,i),s=f-r.limit;if(o>e.epsilon&&s>e.epsilon){const a=Math.min(Math.max(8*e.epsilon,.05*r.halfDepth),.25*s),l=t/o,c=i/o,h=-c,d=l,u=bs([[_[0]-l*a+h*a,_[1]-c*a+d*a],[_[0]-l*a-h*a,_[1]-c*a-d*a],[_[0]+l*a,_[1]+c*a]],e.coordinateScale);u&&n.push(u)}}}return n}({...t,nodes:[e]},i,n));return o&&r?Mt(o,r):o}function ys(e,t,i,n){if(!e||!t.nodes.length)return e;let r=null;try{r=function(e){let t=null;for(const i of e.nodes){const n=gs(i,e);n&&(t=t?$t(t,n):n)}return t}(t)}catch{return e}let o=e;for(const e of t.nodes){const s=4*e.halfDepth+2*t.epsilon,a=2*s,l=[[e.point[0]-s,e.point[1]-s],[e.point[0]+s,e.point[1]-s],[e.point[0]+s,e.point[1]+s],[e.point[0]-s,e.point[1]+s]];try{let s=o;const c=vs(e,t,!1,!0,r);c&&(s=Mt(s,c));let h=_s(e,t,a);const d=vs(e,t,!0,!0,r);d&&(h=Mt(h,d)),r&&(h=$t(h,r));const u=.02*Math.min(...e.rays.map(e=>e.halfDepth));if(h=$t(h,ps([[e.point[0]-u,e.point[1]-u],[e.point[0]+u,e.point[1]-u],[e.point[0]+u,e.point[1]+u],[e.point[0]-u,e.point[1]+u]])),!h)continue;let p=St(h,ps(l));n?p=St(p,n):i&&(p=St(p,i));const m=ps(l),_=Mt(s,m),f=i?Mt(St(s,m),i):null,g=fs(e,t,m);o=$t(_,...f?[f]:[],...g?[g]:[],p)}catch{}}if(r)try{let e=r;n?e=St(e,n):i&&(e=St(e,i)),o=$t(o,e)}catch{}return o}function bs(e,t=1){if(!Array.isArray(e)||e.length<3)return null;const i=Number.isFinite(t)&&t>0?t:1,n=1e-12*Math.max(1,i),r=[];for(const t of e){if(!Array.isArray(t)||t.length<2)return null;const e=Number(t[0]),i=Number(t[1]);if(!Number.isFinite(e)||!Number.isFinite(i))return null;const o=Math.round(e/n)*n,s=Math.round(i/n)*n;if(!Number.isFinite(o)||!Number.isFinite(s))return null;r.push([Object.is(o,-0)?0:o,Object.is(s,-0)?0:s])}return Math.abs(Wo(r))>n*n?r:null}function ws(e,t,i,n){if(hs(e[0],e[1],t[0],t[1],i[0],i[1])>n)return!1;const r=i[0]-t[0],o=i[1]-t[1],s=(e[0]-t[0])*r+(e[1]-t[1])*o,a=r*r+o*o,l=n*Math.sqrt(a);return s>=-l&&s<=a+l}function ks(e,t,i){const n=[],r=[];for(let o=0;oi*i))continue;const d=i/Math.sqrt(h),u=[0,1];for(const e of t)for(const t of[e.a,e.b]){if(!ws(t,s,a,i))continue;const e=((t[0]-s[0])*l+(t[1]-s[1])*c)/h;e>d&&e<1-d&&u.push(e)}u.sort((e,t)=>e-t);const p=u.filter((e,t)=>0===t||Math.abs(e-u[t-1])>d);for(let e=0;e=3&&r.length===n.length?{poly:n,offsets:r}:null}function xs(e,t,i,n,r,o,s,a){const l=(e||[]).map(Et).filter(e=>!!e&&e.length>=3);if(!l.length)return null;let c=$t(ps(l[0]));for(let e=1;e"outer"===e.kind&&e.half>0),u=4*Lo(n,s),p=a||ts(h,u,s);let m=null;for(const e of function(e){const t=[];for(const i of Array.isArray(e)?e:[])if(Array.isArray(i))for(const e of i){if(!Array.isArray(e)||e.length<4)continue;const i=e.slice(0,-1).map(e=>[e[0],e[1]]);i.length>=3&&t.push(i)}return t}(c)){const t=ks(e,d,u);if(!t||!t.offsets.some(e=>e>0))continue;const i=As(t.poly,t.offsets,p),n=Go(t.poly,t.offsets,p);if(!i||!n)continue;const r=Mt(ps(i),ps(n));m=m?$t(m,r):r}return{centre:c,shell:m||[]}}function $s(e,t,i,n,r,o,s=1){try{const a=xs(e,t,i,n,r,o,s,rs(e,t,i,n,r,o,s));if(!a)return[];return a.shell?.length?$t(a.centre,a.shell):a.centre}catch{return null}}function Ss(e){if(!e)return"";let t="";for(const i of e)if(Array.isArray(i))for(const e of i){if(!Array.isArray(e)||e.length<4)continue;const i=e.slice(0,e.length-1);i.length<3||(t+=(t?" ":"")+Ko(i.map(e=>[e[0],e[1]])))}return t}const Ms=15;function Rs(e,t,i){const n=e?.length||0;if(n<3||t?.length!==n)return!1;const r=e[(i-1+n)%n],o=e[i],s=e[(i+1)%n],a=[r[0]-o[0],r[1]-o[1]],l=[s[0]-o[0],s[1]-o[1]],c=Math.hypot(a[0],a[1]),h=Math.hypot(l[0],l[1]);if(!(c>1e-9&&h>1e-9))return!1;const d=Math.max(-1,Math.min(1,(a[0]*l[0]+a[1]*l[1])/(c*h))),u=Math.acos(d);if(!(u>1e-9))return!1;if(u>=Ms*Math.PI/180)return!1;const p=Math.max(0,t[(i-1+n)%n]),m=Math.max(0,t[i]);if(!(p>0||m>0))return!1;const _=Math.sin(u);if(!(_>1e-12))return!1;const f=(m+p*d)/_,g=(p+m*d)/_;return Number.isFinite(f)&&Number.isFinite(g)&&f>0&&g>0&&fe>0))continue;for(const e of l.offsets)e>0&&(u=Math.max(u,2*e));const c=As(l.poly,l.offsets,d),p=Go(l.poly,l.offsets,d);c&&h.push({outset:c,inset:p})}for(const e of l){const t=e.map(e=>e[0]),i=e.map(e=>e[1]);if(t.length){const n=Math.min(Math.max(...t)-Math.min(...t),Math.max(...i)-Math.min(...i)),r=Math.min(...e.map((t,i)=>{const n=e[(i+1)%e.length];return Math.hypot(n[0]-t[0],n[1]-t[1])}));u=Math.max(u,e.length>16?n:r)}}const p=function(e,t,i,n,r,o,s,a){if(!t?.length||!i?.length)return[];const l=4*Lo(n,s),c=new Map;for(const a of ss(e,t,i,n,r,o,s))!a.open&&a.half>0&&!c.has(a.key)&&c.set(a.key,a);const h=[...c.values()];if(h.length<2)return[];const d=a||ts(h,l),u=[];for(const e of i)for(const t of[[e[0],e[1]],[e[2],e[3]]])u.some(e=>Math.hypot(e[0]-t[0],e[1]-t[1])<=l)||u.push(t);const p=[],m=(e,t)=>{let i=0,n=0;if(Math.hypot(e.a[0]-t[0],e.a[1]-t[1])<=l)i=e.b[0]-e.a[0],n=e.b[1]-e.a[1];else{if(!(Math.hypot(e.b[0]-t[0],e.b[1]-t[1])<=l))return null;i=e.a[0]-e.b[0],n=e.a[1]-e.b[1]}const r=Math.hypot(i,n);return r>l?[i/r,n/r]:null};for(const e of u){const t=h.map(t=>({iv:t,u:m(t,e)})).filter(e=>!!e.u);for(let i=0;i0?[e.slice(),u,_,m]:[e.slice(),m,_,u];else{if(!g)continue;{const t=[-r.u[1],r.u[0]],i=[-o.u[1],o.u[0]],n=s<0?1:-1,a=[e[0]+t[0]*r.iv.half*n,e[1]+t[1]*r.iv.half*n],l=[e[0]-i[0]*o.iv.half*n,e[1]-i[1]*o.iv.half*n];y=s>0?[e.slice(),a,l]:[e.slice(),l,a]}}Math.abs(Wo(y))>l*l&&p.push(y)}}return p}(e,t,i,r,o,s,a,d),m=n.length?Ps(e,t,i,r,o,s,a):null;let _="exterior";try{const v=xs(e,t,i,r,o,s,a,d);_="paper";const y=v?v.shell?.length?$t(v.centre,v.shell):v.centre:[],b=e=>{const t=ps(e.outset);if(!e.inset)return t;const i=ps(e.inset);return Mt(t,i)};_="room-rings";let w=null;for(const e of h)try{const t=b(e);w=w?$t(w,t):t}catch{}if(_="edge-bodies",v)for(const n of zs(e,t,i,r,o,s,a))try{const e=St(ps(n.quad),v.centre);w=w?$t(w,e):e}catch{}_="junctions",w=function(e,t,i=1,n=$t){let r=e;for(const e of t||[]){const t=bs(e,i);if(t)try{const e=ps(t);r=r?n(r,e):e}catch{}}return r}(w,p,a),_="facade-clip",w&&v&&(w=St(w,v.centre)),_="exterior-shell";const k=[];let x=0;if(v?.shell?.length)if(w)try{const e=$t(w,v.shell);if(!ms(e))throw new Error("invalid shell union");w=e}catch{if(!ms(w)||!ms(v.shell))throw new Error("invalid shell");k.push({id:"exterior-shell",geom:v.shell}),x++}else w=v.shell;if(_="multi-wall-trim",w&&d.nodes.length){const e=e=>e.rays.some(e=>e.supports.some(e=>e.length<2*e.halfDepth)),t=d.nodes.filter(e);if(t.length){w=ys(w,{...d,nodes:t},v?.centre,y)}}if(_="junction-corners",d.nodes.length){const n=is(d),l=function(e,t,i,n,r,o,s,a){try{const l=xs(e,t,i,n,r,o,s,{epsilon:a.epsilon,coordinateScale:a.coordinateScale,nodes:[],index:new Map});return l?l.shell?.length?$t(l.centre,l.shell):l.centre:null}catch{return null}}(e,t,i,r,o,s,a,d);for(const e of n.fans)try{let t=[ps(e)];if(l&&(t=St(t,l)),!t?.length)continue;w=w?$t(w,t):t}catch{}f=w,g=Math.max(d.epsilon,1e-9)**2,w=Array.isArray(f)?f.map(e=>{if(!Array.isArray(e)||!e.length)return e;const[t,...i]=e;return Math.abs(Wo(t||[]))<=g?null:[t,...i.filter(e=>Math.abs(Wo(e||[]))>g)]}).filter(e=>!!e):f}const $=w||[],S=[...ms($)?[{id:"room-primary",geom:$}]:[],...k.map((e,t)=>({id:`room-isolated-${t}`,geom:e.geom}))];_="openings";for(const e of n){if(!(e.length>0))continue;const t=Is(m,e,!0);if(!t.negative&&!t.positive)continue;const i=e.angle*Math.PI/180,n=Math.cos(i),o=Math.sin(i),s=-o,l=n,c=e.length/2,h=1.25*Math.max(u,r*a),d=[[e.x-n*c-s*h,e.y-o*c-l*h],[e.x+n*c-s*h,e.y+o*c-l*h],[e.x+n*c+s*h,e.y+o*c+l*h],[e.x-n*c+s*h,e.y-o*c+l*h]];w&&(w=Mt(w,ps(d)));for(const e of k)e.geom=Mt(e.geom,ps(d))}_="extras";const M=[];let R=0;const T=c.mergeExtra||((e,t)=>e?$t(e,t):t);for(let e=0;ee.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]))||Math.abs(li(t))<=1e-9){R++;continue}const i=[ps(t)];try{const t=T(w,i,e);if(!ms(t))throw new Error("invalid extra union");w=t}catch{R++,ms(i)&&M.push({id:`extra-${e}`,geom:i})}}M.push(...k),M.sort((e,t)=>Ss(e.geom).localeCompare(Ss(t.geom)));const C=w||[];return{status:R||x?"degraded-extra":"ok",geom:C,components:[...ms(C)?[{id:"primary",geom:C}]:[],...M.map((e,t)=>({...e,id:`isolated-${t}`}))],roomGeom:$,roomComponents:S,paperGeom:y,depthUnits:u,openingPadUnits:1.25*Math.max(u,r*a),openingIndex:m,multiWallNodes:d,degradedExtraCount:R+x}}catch{return c.onCoreFailure?.(_),{status:"failed-core",geom:[],components:[],roomGeom:[],paperGeom:[],roomComponents:[],depthUnits:u,openingIndex:null,multiWallNodes:d,degradedExtraCount:0}}var f,g}function Cs(e,t,i,n=[],r,o,s,a=1,l=[],c={}){if(!t?.length&&!l.length)return null;return Ds(Ts(e,t,i,n,r,o,s,a,l,c))}function Ds(e){if("failed-core"===e.status||"not-applicable"===e.status)return null;const t=e.components.map(e=>({id:e.id,d:Ss(e.geom),fillRule:"evenodd"})).filter(e=>!!e.d),i=t[0]?.d||"",n=Ss(e.paperGeom);if(t.length){const r={status:e.status,d:i,paths:t,components:e.components,roomGeom:e.roomGeom,multiWallNodes:e.multiWallNodes,paperD:n,depthUnits:e.depthUnits,fillRule:"evenodd"};return Object.defineProperties(r,{roomComponents:{value:e.roomComponents||[],enumerable:!1},openingIndex:{value:e.openingIndex,enumerable:!1},openingPadUnits:{value:e.openingPadUnits,enumerable:!1}}),r}return null}function zs(e,t,i,n,r,o,s=1){if(!t?.length)return[];const a=new Set,l=[];for(const c of e||[]){if(!c?.id)continue;const h=os(e,c.id,t,i,n,r,o,s);if(!h)continue;const d=h.poly;for(let e=0;e0))continue;const p=mo(t,i,n,s);if(a.has(p))continue;a.add(p);const m=ao(u,r,o),[_,f]=qo(d,e),g=-_,v=-f,y=m/2,b=[[t[0]+g*y,t[1]+v*y],[i[0]+g*y,i[1]+v*y],[i[0]+_*y,i[1]+f*y],[t[0]+_*y,t[1]+f*y]];l.push({key:p,kind:c,cm:u,quad:b,a:[t[0],t[1]],b:[i[0],i[1]],depthUnits:m})}}return l}function As(e,t,i){const n=e?.length||0;if(n<3||t.length!==n)return null;if(t.every(e=>!(e>0)))return e.map(e=>[e[0],e[1]]);Yo(e),t.slice().reverse();const r=[];for(let o=0;o0||l>0)){r.push([e[o][0],e[o][1]]);continue}if(a>0!=l>0){const t=e[o],i=a>0?[t[0]-c*a,t[1]-h*a]:[t[0],t[1]],n=l>0?[t[0]-d*l,t[1]-u*l]:[t[0],t[1]];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}if(Bo(w,k)){const t=e[o],i=[t[0]-c*a,t[1]-h*a],n=[t[0]-d*l,t[1]-u*l];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}const S=jo(x,w,$,k),M=Math.max(a,l,1e-9),R=ns(i,e[o])?.limit??4*M;if(S){const i=Math.hypot(S[0]-e[o][0],S[1]-e[o][1]);if(Number.isFinite(i)&&i<=R){r.push(S);continue}if(Rs(e,t,o)){r.push([e[o][0],e[o][1]]);continue}}a>0&&r.push([e[o][0]-c*a,e[o][1]-h*a]),l>0&&r.push([e[o][0]-d*l,e[o][1]-u*l])}return r.length>=3?r:null}function Ps(e,t,i,n,r,o,s=1){const a=[];for(const l of e||[]){if(!l?.id)continue;const c=os(e,l.id,t,i,n,r,o,s);if(!c)continue;const h=Math.abs(li(c.poly));for(let e=0;e[Math.max(t,e.x0),Math.min(i,e.x1)]).filter(e=>e[1]-e[0]>n).sort((e,t)=>e[0]-t[0]||e[1]-t[1]);if(!r.length)return{coverage:0,full:!1};let o=r[0][0],s=r[0][1],a=0,l=o<=t+n;for(let e=1;e=i-n,{coverage:a,full:l}}function Fs(e,t){return Number(t.full)-Number(e.full)||e.faceDistance-t.faceDistance||e.area-t.area||e.roomId.localeCompare(t.roomId)}function Is(e,t,i=!1){const n=Number(t?.x),r=Number(t?.y),o=Number(t?.angle),s=Number(t?.length);if(!([n,r,o,s].every(Number.isFinite)&&s>0))return{negative:null,positive:null};const a=o*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=-c,d=l,u=s/2,p=Math.max(1e-9,e.adjacencyEps),m=new Map;let _=0;for(const t of e.edges){if(i&&!(t.half>0))continue;if(!xo(t.a,t.b,o))continue;const[e,s]=uo(t.a,t.b);if(Math.abs((n-t.a[0])*s-(r-t.a[1])*e)>p)continue;const a=(t.a[0]-n)*l+(t.a[1]-r)*c,f=(t.b[0]-n)*l+(t.b[1]-r)*c,g=Math.max(-u,Math.min(a,f)),v=Math.min(u,Math.max(a,f));if(v-g<=p)continue;const y=t.inward[0]*h+t.inward[1]*d>=0?1:-1,b=((t.a[0]+t.b[0])/2-n)*h+((t.a[1]+t.b[1])/2-r)*d,w=Math.abs(b+y*t.half),k=`${y}|${t.roomId}`,x={x0:g,x1:v,half:t.half,cm:t.cm,key:t.key,axis:[e,s]},$=m.get(k);$?($.pieces.push(x),$.faceDistance=Math.min($.faceDistance,w)):m.set(k,{roomId:t.roomId,side:y,order:_++,pieces:[x],faceDistance:w,area:t.area,coverage:0,full:!1})}for(const e of m.values()){const t=Os(e.pieces,-u,u,p);e.coverage=t.coverage,e.full=t.full}const f=e=>{const t=[...m.values()].filter(t=>t.side===e&&t.coverage>p);return t.sort(Fs),t[0]||null};return{negative:f(-1),positive:f(1)}}function Es(e,t){const i=Is(e,t),n=[i.negative,i.positive].filter(e=>!!e);if(!n.length)return{ox:0,oy:0,cm:0,side:-1};const r=i.negative&&i.positive?-1:n[0].side,o=t.flip_v?-r:r,s=(-1===o?i.negative:i.positive)||n[0],a=[...s.pieces].sort((e,t)=>(e.x0<=0&&e.x1>=0?0:Math.min(Math.abs(e.x0),Math.abs(e.x1)))-(t.x0<=0&&t.x1>=0?0:Math.min(Math.abs(t.x0),Math.abs(t.x1)))||t.x1-t.x0-(e.x1-e.x0)||e.key.localeCompare(t.key))[0];if(!(a.half>0&&a.cm>0))return{ox:0,oy:0,cm:0,side:o};const l=t.angle*Math.PI/180,c=-Math.sin(l),h=Math.cos(l);return{ox:c*o*a.half,oy:h*o*a.half,cm:a.cm,side:o}}function Hs(e,t){const i=1e-9,n=t.filter(e=>Number.isFinite(e.x0)&&Number.isFinite(e.x1)&&Number.isFinite(e.half)&&e.x1>e.x0&&e.half>0);if(!n.length)return"";const r=n.flatMap(e=>[e.x0,e.x1]).sort((e,t)=>e-t),o=[];for(const e of r){const t=o[o.length-1];(void 0===t||e>t+i)&&o.push(e)}const s=[];for(let e=0;e+1t+i))continue;const a=(t+r)/2,l=n.reduce((e,t)=>a>=t.x0-i&&a<=t.x1+i?Math.max(e,t.half):e,0);if(!(l>0))continue;const c=s[s.length-1];c&&t<=c.x1+i&&Math.abs(l-c.half)<=i?c.x1=r:s.push({x0:t,x1:r,half:l})}const a=[];for(const e of s){const t=a[a.length-1],n=t?.[t.length-1];n&&e.x0<=n.x1+i?(e.x0=n.x1,t.push(e)):a.push([e])}return a.map(t=>{const i=t[0],n=t[t.length-1],r=Math.min(.25*Math.min(...t.map(e=>e.half)),.75),o=-e*r,s=[];if(1===e){s.push(`M ${i.x0} ${o} L ${n.x1} ${o}`);for(let e=t.length-1;e>=0;e--){const i=t[e];s.push(`L ${i.x1} ${i.half} L ${i.x0} ${i.half}`)}}else{s.push(`M ${n.x1} ${o} L ${i.x0} ${o}`);for(const e of t)s.push(`L ${e.x0} ${-e.half} L ${e.x1} ${-e.half}`)}return s.push("Z"),s.join(" ")}).join(" ")}function Ns(e,t,i,n){if(!n)return i;const r=e.angle*Math.PI/180,o=Math.cos(r),s=Math.sin(r),a=[],l=1e-9;for(const r of i){const[i,c]=r.axis,h=o*i+s*c;if(Math.abs(h)<=l)continue;const d=e.x*i+e.y*c,u=d+h*r.x0,p=d+h*r.x1,m=Math.min(u,p),_=Math.max(u,p),f=`${r.key}|${t}`,g=n.get(f)||[];let v=[[m,_]];for(const[e,t]of g){const i=[];for(const[n,r]of v)t<=n+l||e>=r-l?i.push([n,r]):(e>n+l&&i.push([n,Math.min(r,e)]),te[0]-t[0]||e[1]-t[1]),b=[];for(const e of y){const t=b[b.length-1];t&&e[0]<=t[1]+l?t[1]=Math.max(t[1],e[1]):b.push([e[0],e[1]])}n.set(f,b)}return a}function Ls(e,t){const i=new Map;return t.map(t=>function(e,t,i){const n=Is(e,t,!0),r=n.negative,o=n.positive;if(!r&&!o)return null;let s;if(r&&o)s=[{candidate:r,side:-1},{candidate:o,side:1}];else{const e=r||o;s=[{candidate:e,side:-1},{candidate:e,side:1}]}const a=s.map(({candidate:e,side:n})=>({candidate:e,side:n,pieces:Ns(t,n,e.pieces,i)})),l=a.map(({candidate:e,side:t,pieces:i})=>({side:t,roomId:e.roomId,d:Hs(t,i)})),c=a.flatMap(({pieces:e})=>e);if(!c.length)return null;const h=Math.max(...c.map(e=>e.half));return{faces:l,minY:-h,maxY:h,wallKey:[...new Set(c.map(e=>e.key))].sort().join("|")}}(e,t,i))}const Ws=150,qs=1e-6;function Bs(e){return Number.isFinite(e)?Math.max(1,Math.min(Ws,e)):1}function js(e){return((Number.isFinite(Number(e))?Number(e):0)%90+90)%90}const Us=e=>{const t=e.map(e=>[e[0],e[1]]);return!t.length||t[0][0]===t[t.length-1][0]&&t[0][1]===t[t.length-1][1]||t.push([...t[0]]),[t]},Vs=(e,t)=>e[0]===t[0]&&e[1]===t[1];function Gs(e,t=1e-6){const i=Number.isFinite(t)&&t>0?t:qs,n=[];for(const t of e||[]){if(!Array.isArray(t)||t.length<2)return null;const e=Number(t[0]),r=Number(t[1]);if(!Number.isFinite(e)||!Number.isFinite(r))return null;const o=Math.round(e/i)*i,s=Math.round(r/i)*i;if(!Number.isFinite(o)||!Number.isFinite(s))return null;const a=[Object.is(o,-0)?0:o,Object.is(s,-0)?0:s];n.length&&Vs(n[n.length-1],a)||n.push(a)}return n.length>1&&Vs(n[0],n[n.length-1])&&n.pop(),n.length<3||new Set(n.map(e=>`${e[0]},${e[1]}`)).size<3?null:li(n)>i*i?n:null}function Ks(e){const t=[];for(const i of e||[])for(const e of i||[]){const i=(e||[]).filter(e=>Array.isArray(e)&&e.length>=2);i.length<4||t.push(`M ${i.slice(0,-1).map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`)}return t.join(" ")}function Ys(e,t,i,n,r){return zo({a:e,b:t,halfDepth:ao(i,n,r)/2})}function Xs(e,t,i){const n=Number(t)>0?Number(t):5,r=Bs(e.cm)/n*i,o=e.center[0],s=e.center[1];if("circle"===e.shape){const e=r/2;return Array.from({length:96},(t,i)=>{const n=i/96*Math.PI*2;return[o+Math.cos(n)*e,s+Math.sin(n)*e]})}const a=r/2,l=js(e.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l);return[[-a,-a],[a,-a],[a,a],[-a,a]].map(([e,t])=>[o+e*c-t*h,s+e*h+t*c])}function Zs(e,t){const i=Number.isFinite(t)?Math.max(0,Math.min(1,t)):0,n=(e.a[0]+e.b[0])/2,r=(e.a[1]+e.b[1])/2,o=(e.b[0]-e.a[0])*i/2,s=(e.b[1]-e.a[1])*i/2;return{...e,a:[n-o,r-s],b:[n+o,r+s]}}function Js(e,t,i=1e-9){if(!t.length)return[e];let n=[Us(e)];try{for(const e of t){const t=e.b[0]-e.a[0],r=e.b[1]-e.a[1],o=Math.hypot(t,r);if(!(o>i))continue;const s=t/o,a=r/o,l=-a,c=s,h=1.25*Math.max(Number(e.depth)||0,4*i),d=Math.max(2*i,1e-9*o),u=[[e.a[0]-s*d-l*h,e.a[1]-a*d-c*h],[e.b[0]+s*d-l*h,e.b[1]+a*d-c*h],[e.b[0]+s*d+l*h,e.b[1]+a*d+c*h],[e.a[0]-s*d+l*h,e.a[1]-a*d+c*h]];n=Mt(n,Us(u))}return la(n)}catch{return[e]}}function Qs(e,t){try{const i=Mt([[...e.map(e=>[e[0],e[1]]),[e[0][0],e[0][1]]]],[[...t.map(e=>[e[0],e[1]]),[t[0][0],t[0][1]]]]);let n=null,r=0;for(const e of i||[]){const t=e?.[0]||[],i=Math.abs(li(t));t.length>=4&&i>r&&(r=i,n=t.slice(0,-1).map(e=>[e[0],e[1]]))}return n}catch{return null}}function ea(e,t,i,n=Math.max(2e-4*i,1e-9),r=[]){const o=[],s=[],a=new Map;for(const e of r){const t=a.get(e.hostId)||[];t.push(e),a.set(e.hostId,t)}const l=[],c=[],h=[];for(const n of e.room_drafts||[])for(let e=0;e+10))continue;const a={a:n.points[e],b:n.points[e+1],halfDepth:s},c=zo(a);c&&(o.push(a),l.push(c))}const d=[];for(const n of e.partitions||[]){const e={a:n.a,b:n.b,halfDepth:ao(n.cm,t,i)/2};if(!(e.halfDepth>0))continue;const r=zo(e);r&&(s.push(e),c.push(r),d.push({id:n.id,body:r}))}const u=[...o,...s];for(const{segmentIndex:e,wedge:t}of Io(u,n)){const i=e=o.length&&(d[i.at].body=n))}for(const e of d)h.push(...Js(e.body,a.get(e.id)||[],n));const p=(e.wall_columns||[]).map(e=>Xs(e,t,i)),m=Eo([...o,...s],n).flatMap(e=>Js(e,r,n));return{drafts:l,partitions:c,columns:p,patches:m,all:[...l,...h,...m,...p]}}function ta(e){try{const t=e.map(e=>Gs(e)).filter(e=>!!e).map(e=>Us(e));return t.length?$t(t[0],...t.slice(1)):null}catch{return null}}const ia=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`;function na(e){const t=[];for(const i of e||[]){if(sa([i])<=1e-6)continue;const e=[];for(const t of i||[]){const i=(t||[]).filter(e=>Array.isArray(e)&&e.length>=2);i.length<4||e.push(ia(i.slice(0,-1)))}e.length&&t.push(e.join(" "))}return t}function ra(e,t,i={}){const n=ta(e.filter(e=>e.length>=3)),r=t.some(e=>!Gs(e))?null:ta(t);if(!n)return[];if(r)try{return na(St(n,r))}catch{}return function(e,t,i){let n=null;for(let r=0;r=4&&t.push(e.slice(0,-1).map(e=>[e[0],e[1]]));return t}function la(e){const t=[];for(const i of e||[]){const e=i?.[0];e?.length>=4&&t.push(e.slice(0,-1).map(e=>[e[0],e[1]]))}return t}function ca(e,t){let i=!1;for(let n=0,r=t.length-1;ne[1]!=l>e[1]&&e[0]<(a-o)*(e[1]-s)/(l-s||1e-12)+o&&(i=!i)}return i}function ha(e,t,i){return function(e,t){for(const i of t||[]){const t=i?.[0];if(!t?.length||!ca(e,t))continue;let n=!1;for(let t=1;tca(e,t))}function da(e,t,i){if(Math.hypot(e.center[0]-t.center[0],e.center[1]-t.center[1])>i)return!1;if(Math.abs(Bs(e.cm)-Bs(t.cm))>1e-6)return!1;if(e.shape!==t.shape)return!0;if("circle"===e.shape||"circle"===t.shape)return!0;const n=Math.abs(js(e.angle)-js(t.angle));return Math.min(n,90-n)<=1e-6}const ua=2.54;function pa(e){const t="number"==typeof e?e:NaN;return!Number.isFinite(t)||t<=0||5===t?1:5/t}function ma(e,t){return e*pa(t)}function _a(e){return e?ua:1}function fa(e,t){if(!t)return String(e);const i=e/ua;return String(Math.round(1e6*i)/1e6)}function ga(e,t){return t?e*ua:e}const va=1e3;function ya(e,t){const i=Number(e),n=Number.isFinite(i)&&i>0?i:1,r=n>=1?t:t*n,o=n>=1?t/n:t;return{x:(t-r)/2,y:(t-o)/2,w:r,h:o}}const ba=.01,wa=100;function ka(e,t=1e3){const i=ya(e?.plan_aspect,t),n=Number(e?.plan_scale),r=Number.isFinite(n)&&n>0?Math.min(wa,Math.max(ba,n)):1,o=Number(e?.plan_scale_x),s=Number(e?.plan_scale_y),a=Number.isFinite(o)&&o>0?Math.min(wa,Math.max(ba,o)):r,l=Number.isFinite(s)&&s>0?Math.min(wa,Math.max(ba,s)):r,c=Number(e?.plan_x),h=Number(e?.plan_y),d=nr(e?.plan_angle);return{x:i.x+(Number.isFinite(c)?Pa(c):0)*t,y:i.y+(Number.isFinite(h)?Pa(h):0)*t,w:i.w*a,h:i.h*l,...d?{angle:d}:{}}}function xa(e){if(null==e.x||null==e.y)return{x:e.x,y:e.y,w:e.w,h:e.h};const t=Number(e.w)||0,i=Number(e.h)||0;return{x:t<0?e.x+t:e.x,y:i<0?e.y+i:e.y,w:Math.abs(t),h:Math.abs(i)}}function $a(e){return e&&Array.isArray(e.spaces)?e.spaces.map(e=>{const t=va,i=function(e){return Array.isArray(e)&&4===e.length&&e.every(e=>Number.isFinite(e))&&e[2]>1e-6&&e[3]>1e-6?e:[0,0,1,1]}(e.view_box);return{id:e.id,title:e.title,cellCm:Number.isFinite(Number(e.cell_cm))&&Number(e.cell_cm)>0?Number(e.cell_cm):5,vb:[i[0]*va,i[1]*t,i[2]*va,i[3]*t],bg:e.plan_url?{href:_n(e.plan_url),...ka(e,va)}:null,rooms:(e.rooms||[]).map(e=>{const i={...e,...xa(e)};return{id:i.id,name:i.name,area:i.area??null,open_to:i.open_to||void 0,settings:i.settings||void 0,x:null!=i.x?i.x*va:void 0,y:null!=i.y?i.y*t:void 0,w:null!=i.w?i.w*va:void 0,h:null!=i.h?i.h*t:void 0,poly:i.poly?i.poly.map(e=>[e[0]*va,e[1]*t]):void 0,wall_ids:Array.isArray(i.wall_ids)?[...i.wall_ids]:void 0}}),wall_segments:(e.wall_segments||[]).map(e=>({...e,id:String(e.id),a:[Number(e.a?.[0])*va,Number(e.a?.[1])*t],b:[Number(e.b?.[0])*va,Number(e.b?.[1])*t],cm:Number(e.cm)})),room_drafts:(e.room_drafts||[]).map(e=>({id:e.id,points:(e.points||[]).map(e=>[e[0]*va,e[1]*t]),segments:(e.segments||[]).map(e=>({..."string"==typeof e.id&&e.id?{id:e.id}:{},cm:Number(e.cm)}))})),partitions:(e.partitions||[]).map(e=>({id:e.id,a:[e.a[0]*va,e.a[1]*t],b:[e.b[0]*va,e.b[1]*t],cm:Number(e.cm)})),wall_columns:(e.wall_columns||[]).map(e=>({id:e.id,shape:"circle"===e.shape?"circle":"square",center:[e.center[0]*va,e.center[1]*t],cm:Number(e.cm),..."circle"===e.shape?{}:{angle:js(e.angle)}}))}}):[]}const Sa=5e3,Ma=Sa*va,Ra=240,Ta=va/Ra,Ca=1/Ra;function Da(e){if(!Number.isFinite(e))return e;const t=Math.round(e*Ra/va)*va/Ra;return Math.abs(t-e)<=1e-9*Ta?e:t}function za(e){return{x:Da(e.x),y:Da(e.y)}}function Aa(e){return Number.isFinite(e)?Math.min(Ma,Math.max(-Ma,e)):0}function Pa(e){return Number.isFinite(e)?Math.min(Sa,Math.max(-Sa,e)):0}const Oa=1/3,Fa=200;function Ia(e){return"house"===e?"house":"content"}function Ea(e){let t=1/0,i=1/0,n=-1/0,r=-1/0;for(const o of e){const e=Number(o[0]),s=Number(o[1]);Number.isFinite(e)&&Number.isFinite(s)&&(en&&(n=e),s>r&&(r=s))}return t>n?null:{minX:t,minY:i,maxX:n,maxY:r}}function Ha(e){const t=[],i=e=>{if(Array.isArray(e))if(e.length>=2&&Number.isFinite(Number(e[0]))&&Number.isFinite(Number(e[1])))t.push([Number(e[0]),Number(e[1])]);else for(const t of e)i(t)};return i(e),Ea(t)}function Na(e,t){const i=Number.isFinite(t)&&t>0?t:0;return{minX:e.minX-i,minY:e.minY-i,maxX:e.maxX+i,maxY:e.maxY+i}}function La(e){return e.poly&&e.poly.length?Ea(e.poly):null==e.x||null==e.y?null:Ea([[e.x,e.y],[e.x+(e.w||0),e.y+(e.h||0)]])}function Wa(e,t){const i=[];for(const t of e.rooms||[]){const e=La(t);e&&i.push(e)}if(e.bg){const t=Ea(dr(e.bg));t&&i.push(t)}for(const e of t||[])if(Array.isArray(e)){const t=Ea([e]);t&&i.push(t)}else i.push(e);return i}const qa=e=>{if(!e.length)return 0;const t=[...e].sort((e,t)=>e-t),i=t.length>>1;return t.length%2?t[i]:(t[i-1]+t[i])/2},Ba=e=>{let t=1/0,i=1/0,n=-1/0,r=-1/0;for(const o of e)o.minXn&&(n=o.maxX),o.maxY>r&&(r=o.maxY);return t>n||i>r?null:{x:t,y:i,w:n-t,h:r-i}};function ja(e,t){let{x:i,y:n,w:r,h:o}=e;r<30&&(i=i+r/2-100,r=Fa),o<30&&(n=n+o/2-100,o=Fa);const s="number"==typeof t?{top:t,right:t,bottom:t,left:t}:t,a=Math.max(r,o),l=a*s.top,c=a*s.right,h=a*s.bottom,d=a*s.left;return{x:i-d,y:n-l,w:r+d+c,h:o+l+h}}function Ua(e,t={}){const i=t.pad??.05,n=t.k??10,r=t.minSpread??50,o=e.filter(e=>Number.isFinite(e.minX)&&Number.isFinite(e.minY)&&Number.isFinite(e.maxX)&&Number.isFinite(e.maxY)&&Math.abs(e.minX)<=Ma&&Math.abs(e.maxX)<=Ma&&Math.abs(e.minY)<=Ma&&Math.abs(e.maxY)<=Ma);if(!o.length)return{core:null,all:null,outliers:0};const s=Ba(o);if(o.length<4){const e=ja(s,i);return{core:e,all:e,outliers:0}}const a=o.map(e=>(e.minX+e.maxX)/2),l=o.map(e=>(e.minY+e.maxY)/2),c=qa(a),h=qa(l),d=o.map((e,t)=>Math.max(Math.abs(a[t]-c),Math.abs(l[t]-h))),u=Math.max(((e,t)=>e.length?e[Math.min(e.length-1,Math.max(0,Math.round(t*(e.length-1))))]:0)([...d].sort((e,t)=>e-t),.75),r),p=d.map(e=>e>n*u),m=p.filter(Boolean).length,_=m&&m<=o.length*Oa?o.filter((e,t)=>!p[t]):o;return{core:ja(Ba(_)||s,i),all:ja(s,i),outliers:_===o?0:m}}function Va(e){return Ua(e,{pad:0}).all}function Ga(e,t,i=.05){const n=Ua(Wa(e,t),{pad:i});if(n.core)return n.core;const r=e.vb&&4===e.vb.length&&e.vb[2]>0&&e.vb[3]>0?e.vb:[0,0,va,va];return{x:r[0],y:r[1],w:r[2],h:r[3]}}function Ka(e){const t=Ga(e);return{x:t.x+t.w/2,y:t.y+t.h/2}}function Ya(e){const t=[];for(const i of e.rooms||[]){const e=La(i);e&&t.push(e)}const i=Ua(t,{pad:0}).core,n=va*pa(e.cellCm??5);return i?Math.max(n,Math.min(Ma,Math.max(i.w,i.h))):n}function Xa(e,t,i,n=1){const r=Number(i),o=Number.isFinite(n)&&n>0?n:1;return!Number.isFinite(r)||r<=0?e*o:e*Ya(t)*o/r}const Za=[1,2,5,10,20,50,100,200,500,1e3];function Ja(e,t,i=7){if(!(e>0&&t>0&&Number.isFinite(t)))return null;const n=Za.find(n=>e*n*t>=i);if(void 0===n)return null;const r=Za.find(e=>e>=5*n)??5*n;return{fine:n,coarse:r}}function Qa(e){if(e.poly&&e.poly.length){const t=e.poly.map(e=>e[0]),i=e.poly.map(e=>e[1]),n=Math.min(...t),r=Math.min(...i);return{x:n,y:r,w:Math.max(...t)-n,h:Math.max(...i)-r}}return{x:e.x??0,y:e.y??0,w:e.w??0,h:e.h??0}}function el(e,t,i,n,r){const o=t[e.id];return o&&o.s===e.space?{x:o.x*va,y:o.y*va}:n[e.id]?n[e.id]:za(Ka(r))}function tl(e,t,i,n){const r=i["rl_"+(e.id||"")];if(r&&r.s===t)return{x:r.x*va,y:r.y*va};const o=function(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}(e);return za({x:o[0],y:o[1]})}const il=["furniture","appliance","sanitary","other"],nl=[{id:"sofa",group:"furniture",category:"sofa",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",category:"armchair",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",category:"coffee_table",w:110,h:60,g:[["r",0,0,1,1],["r",.08,.14,.84,.72]]},{id:"table_dining",group:"furniture",category:"dining_table",w:140,h:80,g:[["r",0,0,1,1],["r",.06,.11,.88,.78]]},{id:"table_round",group:"furniture",category:"dining_table",w:120,h:120,g:[["e",.5,.5,.5,.5],["e",.5,.5,.41,.41]]},{id:"chair",group:"furniture",category:"chair",w:45,h:45,g:[["r",0,0,1,.18],["r",.06,.18,.88,.8]]},{id:"desk",group:"furniture",category:"work_table",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",category:"bed",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",category:"bed",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",category:"nightstand",w:45,h:40,g:[["r",0,0,1,1],["r",.12,.14,.76,.33],["r",.12,.53,.76,.33]]},{id:"wardrobe",group:"furniture",category:"wardrobe",w:100,h:60,g:[["r",0,0,1,1],["l",0,.72,1,.72],["l",.5,.72,.5,1]]},{id:"bookshelf",group:"furniture",category:"wardrobe",w:80,h:30,g:[["r",0,0,1,1],["l",.34,0,.34,1],["l",.67,0,.67,1]]},{id:"fridge",group:"appliance",category:"fridge",w:60,h:65,g:[["r",0,0,1,1],["l",0,.36,1,.36],["l",.83,.44,.83,.64]]},{id:"stove",group:"appliance",category:"cooktop",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",category:"dishwasher",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",category:"washer",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",category:"dryer",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",category:"tv",w:120,h:30,g:[["r",0,0,1,.42],["l",.5,.42,.5,.72],["l",.3,.72,.7,.72]]},{id:"ac",group:"appliance",category:"air_conditioner",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",category:"boiler",w:45,h:45,g:[["e",.5,.5,.5,.5],["e",.5,.5,.31,.31]]},{id:"toilet",group:"sanitary",category:"toilet",w:40,h:70,g:[["r",.06,0,.88,.2],["e",.5,.58,.37,.35],["e",.5,.58,.22,.2]]},{id:"bathtub",group:"sanitary",category:"bathtub",w:170,h:75,g:[["r",0,0,1,1],["r",.05,.11,.77,.78],["e",.89,.5,.045,.1]]},{id:"shower",group:"sanitary",category:"shower",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",category:"sink",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",category:"kitchen_sink",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",category:"bidet",w:40,h:55,g:[["e",.5,.5,.44,.5],["e",.5,.5,.26,.3]]},{id:"stairs",group:"other",category:"stairs",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",category:"fireplace",w:120,h:40,g:[["r",0,0,1,1],["p",.22,1,.22,.42,.78,.42,.78,1]]},{id:"plant",group:"other",category:"plant",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",category:"rug",w:200,h:140,g:[["r",0,0,1,1],["r",.06,.09,.88,.82]]}],rl=new Set(["fridge","dishwasher","washer","dryer","ac","water_heater","shower","sink","stairs","fireplace","plant","rug"]),ol=[...[{id:"coffee_table",group:"furniture",category:"coffee_table",w:120,h:60,art:{d:"M3.84 5.884c0-1.21 1.048-2.192 2.34-2.192h107.64c1.293 0 2.34.981 2.34 2.192v48.23c0 1.212-1.047 2.193-2.34 2.193H6.18c-1.292 0-2.34-.981-2.34-2.192z M103.056 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m-93.6-35.077V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877",viewW:120,viewH:60}},{id:"coffee_table_round",group:"furniture",category:"coffee_table",w:80,h:80,art:{d:"M3.765 40a36.235 36.235 0 1 0 72.47 0 36.235 36.235 0 1 0-72.47 0 m56.26 26.96-3.843-3.843a.906.906 0 0 1 0-1.28l3.843-3.844a.906.906 0 0 1 1.281 0l3.844 3.843a.906.906 0 0 1 0 1.281l-3.844 3.844a.906.906 0 0 1-1.28 0m-.001 34.66-3.843-3.843a.906.906 0 0 1 0-1.281l3.843-3.844a.906.906 0 0 1 1.281 0l3.843 3.844a.906.906 0 0 1 0 1.28l-3.843 3.844a.906.906 0 0 1-1.281 0m-34.423-34.66-3.843-3.843a.906.906 0 0 1 0-1.281l3.843-3.844a.906.906 0 0 1 1.281 0l3.844 3.844a.906.906 0 0 1 0 1.28l-3.844 3.844a.906.906 0 0 1-1.28 0m-.001 34.66-3.843-3.843a.906.906 0 0 1 0-1.281l3.843-3.843a.906.906 0 0 1 1.281 0l3.844 3.843a.906.906 0 0 1 0 1.281l-3.844 3.843a.906.906 0 0 1-1.28 0",viewW:80,viewH:80}},{id:"coffee_table_oval",group:"furniture",category:"coffee_table",w:120,h:60,art:{d:"M3.84 30c0-14.53 12.572-26.308 28.08-26.308h56.16c15.509 0 28.08 11.778 28.08 26.308s-12.571 26.307-28.08 26.307H31.92C16.412 56.307 3.84 44.53 3.84 30 M80.592 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.077v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877M31.92 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.876h-5.616a.936.877 0 0 1-.936-.876m0 35.077v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.876h-5.616a.936.877 0 0 1-.936-.876",viewW:120,viewH:60}},{id:"coffee_table_rounded",group:"furniture",category:"coffee_table",w:120,h:60,art:{d:"M3.84 12.461c0-4.843 4.19-8.77 9.36-8.77h93.6c5.17 0 9.36 3.927 9.36 8.77v35.077c0 4.843-4.19 8.77-9.36 8.77H13.2c-5.17 0-9.36-3.927-9.36-8.77z M103.056 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m-93.6-35.077V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877",viewW:120,viewH:60}},{id:"table_dining",group:"furniture",category:"dining_table",w:160,h:90,art:{d:"M3.879 6.08c0-1.265 1.065-2.29 2.378-2.29h147.484c1.314 0 2.38 1.025 2.38 2.29v77.84c0 1.265-1.066 2.29-2.38 2.29H6.257c-1.313 0-2.378-1.025-2.378-2.29z M138.994 19.358v-5.495a.952.916 0 0 1 .951-.915h5.71a.952.916 0 0 1 .95.915v5.495a.952.916 0 0 1-.95.916h-5.71a.952.916 0 0 1-.951-.916m-.001 56.778v-5.495a.952.916 0 0 1 .952-.915h5.709a.952.916 0 0 1 .951.915v5.495a.952.916 0 0 1-.951.916h-5.71a.952.916 0 0 1-.95-.916m-125.6-56.778v-5.495a.952.916 0 0 1 .951-.916h5.71a.952.916 0 0 1 .95.916v5.495a.952.916 0 0 1-.95.915h-5.71a.952.916 0 0 1-.951-.915m-.001 56.779v-5.495a.952.916 0 0 1 .952-.916h5.709a.952.916 0 0 1 .951.916v5.495a.952.916 0 0 1-.951.915h-5.71a.952.916 0 0 1-.95-.915",viewW:160,viewH:90}},{id:"table_round",group:"furniture",category:"dining_table",w:110,h:110,art:{d:"M3.826 55a51.174 51.174 0 1 0 102.349 0A51.174 51.174 0 1 0 3.826 55 m81.005 32.303-3.947-3.947a.93.93 0 0 1 0-1.316l3.947-3.948a.93.93 0 0 1 1.316 0l3.948 3.948a.93.93 0 0 1 0 1.316l-3.948 3.947a.93.93 0 0 1-1.316 0m0 54.897-3.947-3.948a.93.93 0 0 1 0-1.316l3.947-3.947a.93.93 0 0 1 1.316 0l3.948 3.947a.93.93 0 0 1 0 1.316L82.32 87.2a.93.93 0 0 1-1.316 0M27.04 32.304l-3.947-3.948a.93.93 0 0 1 0-1.315l3.948-3.948a.93.93 0 0 1 1.315 0l3.948 3.948a.93.93 0 0 1 0 1.315l-3.948 3.948a.93.93 0 0 1-1.315 0m-.001 54.604-3.947-3.948a.93.93 0 0 1 0-1.316l3.948-3.947a.93.93 0 0 1 1.315 0l3.948 3.947a.93.93 0 0 1 0 1.316l-3.948 3.948a.93.93 0 0 1-1.315 0",viewW:110,viewH:110}},{id:"table_dining_oval",group:"furniture",category:"dining_table",w:180,h:100,art:{d:"M3.892 50c0-25.51 21.418-46.19 47.838-46.19h76.54c26.42 0 47.839 20.68 47.839 46.19S154.69 96.19 128.27 96.19H51.73C25.31 96.19 3.892 75.51 3.892 50 M122.53 19.514V13.97a.957.924 0 0 1 .956-.924h5.74a.957.924 0 0 1 .958.924v5.543a.957.924 0 0 1-.957.924h-5.74a.957.924 0 0 1-.958-.924m.001 66.516v-5.543a.957.924 0 0 1 .957-.924h5.74a.957.924 0 0 1 .957.924v5.543a.957.924 0 0 1-.957.924h-5.74a.957.924 0 0 1-.957-.924M49.816 19.515v-5.543a.957.924 0 0 1 .957-.924h5.74a.957.924 0 0 1 .958.924v5.542a.957.924 0 0 1-.957.924h-5.74a.957.924 0 0 1-.958-.924m0 66.516v-5.543a.957.924 0 0 1 .957-.924h5.74a.957.924 0 0 1 .957.924v5.543a.957.924 0 0 1-.957.923h-5.74a.957.924 0 0 1-.957-.924",viewW:180,viewH:100}},{id:"table_dining_rounded",group:"furniture",category:"dining_table",w:160,h:90,art:{d:"M3.879 17.527c0-7.587 6.39-13.737 14.272-13.737h123.696c7.883 0 14.273 6.15 14.273 13.737v54.947c0 7.587-6.39 13.737-14.273 13.737H18.151c-7.882 0-14.272-6.15-14.272-13.737z M138.994 19.358v-5.495a.952.916 0 0 1 .951-.915h5.71a.952.916 0 0 1 .95.915v5.495a.952.916 0 0 1-.95.916h-5.71a.952.916 0 0 1-.951-.916m-.001 56.778v-5.495a.952.916 0 0 1 .952-.915h5.709a.952.916 0 0 1 .951.915v5.495a.952.916 0 0 1-.951.916h-5.71a.952.916 0 0 1-.95-.916m-125.6-56.778v-5.495a.952.916 0 0 1 .951-.916h5.71a.952.916 0 0 1 .95.916v5.495a.952.916 0 0 1-.95.915h-5.71a.952.916 0 0 1-.951-.915m-.001 56.779v-5.495a.952.916 0 0 1 .952-.916h5.709a.952.916 0 0 1 .951.916v5.495a.952.916 0 0 1-.951.915h-5.71a.952.916 0 0 1-.95-.915",viewW:160,viewH:90}},{id:"desk",group:"furniture",category:"work_table",w:140,h:70,art:{d:"M3.862 5.967c0-1.234 1.057-2.234 2.362-2.234h127.552c1.305 0 2.362 1 2.362 2.234v58.066c0 1.234-1.057 2.233-2.362 2.233H6.224c-1.305 0-2.362-1-2.362-2.233z M9.531 15.346v-5.36a.945.893 0 0 1 .945-.893h5.67a.945.893 0 0 1 .944.893v5.36a.945.893 0 0 1-.945.894h-5.669a.945.893 0 0 1-.945-.894m113.379.001v-5.36a.945.893 0 0 1 .945-.894h5.67a.945.893 0 0 1 .944.894v5.36a.945.893 0 0 1-.945.893h-5.669a.945.893 0 0 1-.945-.893M9.53 60.013v-5.36a.945.893 0 0 1 .946-.893h5.669a.945.893 0 0 1 .945.893v5.36a.945.893 0 0 1-.945.894h-5.67a.945.893 0 0 1-.944-.894m113.38.001v-5.36a.945.893 0 0 1 .945-.894h5.669a.945.893 0 0 1 .945.894v5.36a.945.893 0 0 1-.945.893h-5.67a.945.893 0 0 1-.944-.893m-97.318-2.681h88.814",viewW:140,viewH:70}},{id:"desk_corner",group:"furniture",category:"work_table",w:160,h:160,art:{d:"M156.12 66.202a2.38 2.38 0 0 1-2.379 2.38H94.272c-13.138 0-23.788 10.65-23.788 23.787v61.372a2.38 2.38 0 0 1-2.379 2.38H6.257a2.38 2.38 0 0 1-2.378-2.38V6.257A2.38 2.38 0 0 1 6.257 3.88h147.484a2.38 2.38 0 0 1 2.38 2.378z M9.588 16.249v-5.71a.95.95 0 0 1 .951-.95h5.71a.95.95 0 0 1 .95.95v5.71a.95.95 0 0 1-.95.951h-5.71a.95.95 0 0 1-.951-.951m133.212.001v-5.71a.95.95 0 0 1 .951-.951h5.71a.95.95 0 0 1 .95.951v5.71a.95.95 0 0 1-.95.951h-5.71a.95.95 0 0 1-.951-.951m0 45.672v-5.71a.95.95 0 0 1 .952-.951h5.709a.95.95 0 0 1 .951.951v5.71a.95.95 0 0 1-.951.951h-5.71a.95.95 0 0 1-.95-.951M9.587 149.46v-5.708a.95.95 0 0 1 .952-.952h5.709a.95.95 0 0 1 .951.952v5.709a.95.95 0 0 1-.951.951h-5.71a.95.95 0 0 1-.951-.951m47.576 0v-5.709a.95.95 0 0 1 .952-.951h5.709a.95.95 0 0 1 .951.951v5.71a.95.95 0 0 1-.951.95h-5.71a.95.95 0 0 1-.951-.95m51.382-90.396H95.569a34.6 34.6 0 0 0-34.6 34.6v12.976",viewW:160,viewH:160}},{id:"chair",group:"furniture",category:"chair",w:50,h:50,art:{d:"M39.452 12.433H10.548a4.398 4.398 0 1 1 0-8.796h28.904a4.398 4.398 0 0 1 0 8.796m-28.904 0H8.035a4.4 4.4 0 0 0-4.398 4.399V40.08a6.283 6.283 0 0 0 6.283 6.284h30.16a6.283 6.283 0 0 0 6.284-6.284V16.832c0-2.43-1.97-4.399-4.399-4.399h-2.513",viewW:50,viewH:50}},{id:"chair_bar",group:"furniture",category:"chair",w:45,h:48,art:{d:"M38.579 17.168c1.326 2.526 1.947 6.015 1.947 9.071 0 10.018-8.07 18.139-18.025 18.139S4.475 36.257 4.475 26.239c0-3.056.586-6.552 1.912-9.078m32.192.007a3.59 3.59 0 0 1-3.333-.976m3.333.976a3.6 3.6 0 0 0 1.765-.976m-33.827.996-.13-.027a3.6 3.6 0 0 1-1.731-.969m30.59 0a18 18 0 0 0-5.848-3.932 17.93 17.93 0 0 0-13.796 0 18 18 0 0 0-5.848 3.932 3.59 3.59 0 0 1-3.237.996m28.729-.996a3.59 3.59 0 0 0 5.098 0m-33.827.996a3.6 3.6 0 0 1-1.861-.996 3.644 3.644 0 0 1 0-5.13 25.2 25.2 0 0 1 8.187-5.505A25.1 25.1 0 0 1 22.5 3.623a25.1 25.1 0 0 1 9.657 1.934 25.2 25.2 0 0 1 8.187 5.505 3.644 3.644 0 0 1 0 5.13",viewW:45,viewH:48}},{id:"armchair",group:"furniture",category:"armchair",w:90,h:90,art:{d:"M75.702 13.011q.012-.225.012-.453c0-4.843-3.917-8.768-8.748-8.768H23.034c-4.83 0-8.747 3.925-8.747 8.768q0 .228.011.453M77.132 72.09q.59.091 1.206.092c4.348 0 7.873-3.533 7.873-7.892V20.45c0-4.359-3.525-7.892-7.873-7.892-.924 0-1.812.16-2.636.453a7.89 7.89 0 0 0-5.237 7.438v.148m0 0a8.7 8.7 0 0 1-3.499.73H23.034a8.7 8.7 0 0 1-3.499-.73m50.93 0V64.29a7.89 7.89 0 0 0 6.667 7.8M14.298 13.01a7.89 7.89 0 0 1 5.237 7.438v.148m0 0V64.29a7.89 7.89 0 0 1-6.666 7.8q-.59.091-1.206.092c-4.348 0-7.873-3.533-7.873-7.892V20.45c0-4.359 3.525-7.892 7.873-7.892.924 0 1.811.16 2.635.453M77.132 72.09c-1.585 8.05-8.667 14.12-17.164 14.12H30.032c-8.496 0-15.578-6.07-17.163-14.12",viewW:90,viewH:90}},{id:"armchair_office",group:"furniture",category:"armchair",w:65,h:65,art:{d:"M56.968 18.845q0-.368-.05-.72M49.051 13.8H15.95a5.04 5.04 0 0 1-5.038-5.043 5.04 5.04 0 0 1 5.038-5.043h33.103a5.04 5.04 0 0 1 5.038 5.043 5.04 5.04 0 0 1-5.038 5.043m-33.103 0h-2.88a5.04 5.04 0 0 0-4.986 4.323M49.052 13.8h2.879a5.04 5.04 0 0 1 4.986 4.324m.051 24.826a4.32 4.32 0 0 0 4.318-4.323v-16.18a4.32 4.32 0 0 0-4.369-4.322m.051 24.826a4.32 4.32 0 0 1-4.318-4.323V22.447a4.32 4.32 0 0 1 4.267-4.322m-11.84 37.24h4.695c.736 0 1.446-.111 2.115-.317a7.21 7.21 0 0 0 5.081-6.889v-5.208M8.084 18.124h-.051a4.32 4.32 0 0 0-4.318 4.323v16.18a4.32 4.32 0 0 0 4.318 4.324 4.32 4.32 0 0 0 4.317-4.323V22.447a4.32 4.32 0 0 0-4.266-4.323m11.935 37.24h-4.79a7.2 7.2 0 0 1-2.04-.294 7.21 7.21 0 0 1-5.156-6.91v-5.21m37.045 12.414 5.177 5.183c.969.97 2.534.987 3.524.038a2.526 2.526 0 0 0 .038-3.605l-1.93-1.932m-6.81.316H20.02m-6.83-.294-1.909 1.91c-.998 1-.98 2.627.038 3.605.99.949 2.555.932 3.524-.038l5.177-5.183",viewW:65,viewH:65}},{id:"sofa",group:"furniture",category:"sofa",w:180,h:90,art:{d:"M90 19.365v57.111m0-57.11V7.683m0 11.681c0 2.151-1.836 3.894-4.1 3.894H30.544a10.7 10.7 0 0 1-4.136-.825M90 19.365c0 2.151 1.836 3.894 4.1 3.894h55.356c1.472 0 2.871-.294 4.137-.825M90 76.476c0 5.376-4.59 9.735-10.25 9.735H30.543c-5.266 0-9.605-3.772-10.185-8.625M90 76.476c0 5.376 4.59 9.735 10.25 9.735h49.206c5.266 0 9.605-3.772 10.185-8.625M90 7.684c0-2.15-1.837-3.894-4.101-3.894H30.544c-5.661 0-10.25 4.358-10.25 9.735q0 .416.036.825M90 7.684c0-2.15 1.836-3.894 4.1-3.894h55.356c5.661 0 10.25 4.358 10.25 9.735q0 .416-.036.825m0 0a10.7 10.7 0 0 1 4.137-.825h2.05c5.662 0 10.252 4.358 10.252 9.734v45.43c0 5.376-4.59 9.734-10.251 9.734h-2.05c-1.484 0-2.894-.3-4.167-.837m0 0c-3.585-1.517-6.084-4.93-6.084-8.898V23.26q0-.417.036-.825c.32-3.622 2.728-6.68 6.077-8.084m-139.34 0c3.35 1.404 5.758 4.462 6.078 8.084q.036.408.036.825v45.43c0 3.967-2.5 7.38-6.085 8.897a10.7 10.7 0 0 1-4.166.837h-2.05c-5.662 0-10.251-4.358-10.251-9.735V23.26c0-5.376 4.59-9.734 10.25-9.734h2.05c1.473 0 2.872.294 4.138.825",viewW:180,viewH:90}},{id:"sofa_three_seat",group:"furniture",category:"sofa",w:240,h:90,art:{d:"M86.553 18.593V76.96m0-58.366V7.491m0 11.102c0 2.044-1.762 3.701-3.935 3.701H29.496c-1.413 0-2.756-.28-3.97-.784m61.027-2.917c0 2.044 1.762 3.701 3.935 3.701h59.024c2.174 0 3.935-1.657 3.935-3.7M86.553 76.958c0 5.11-4.404 9.252-9.837 9.252h-47.22c-5.054 0-9.218-3.585-9.775-8.198m66.832-1.054c0 5.11 4.404 9.252 9.837 9.252h47.22c5.433 0 9.837-4.142 9.837-9.252m0-58.366V76.96m0-58.366V7.491m0 11.102c0 2.044 1.762 3.701 3.936 3.701h53.122c1.413 0 2.756-.28 3.97-.784m-61.028 55.449c0 5.11 4.405 9.252 9.838 9.252h47.22c5.054 0 9.218-3.585 9.775-8.198M86.553 7.491c0-2.044-1.762-3.701-3.935-3.701H29.496c-5.433 0-9.838 4.142-9.838 9.252q0 .396.035.784m66.86-6.335c0-2.044 1.762-3.701 3.935-3.701h59.024c2.174 0 3.935 1.657 3.935 3.7m0 0c0-2.043 1.762-3.7 3.936-3.7h53.122c5.433 0 9.838 4.142 9.838 9.252 0 .258.022.517 0 .77m-.063 64.201c1.22.512 2.574.796 3.998.796h1.967c5.433 0 9.837-4.142 9.837-9.252V22.294c0-5.11-4.404-9.252-9.837-9.252h-1.967c-5.153 0-9.38 3.725-9.803 8.468q-.036.387-.035.784v47.263c0 3.771 2.399 7.015 5.84 8.456M19.693 13.826a10.3 10.3 0 0 0-3.97-.784h-1.967c-5.433 0-9.838 4.142-9.838 9.252v47.263c0 5.11 4.405 9.252 9.838 9.252h1.967c1.424 0 2.777-.284 3.998-.796 3.441-1.441 5.84-4.685 5.84-8.456V22.294a9 9 0 0 0-.035-.784c-.307-3.443-2.618-6.349-5.833-7.684",viewW:240,viewH:90}},{id:"sofa_corner_right",group:"furniture",category:"sofa",w:260,h:170,art:{d:"M93.673 20.687V83.69m0-63.004v-12.6m0 12.6c0 2.32-1.913 4.2-4.273 4.2H31.704a10.8 10.8 0 0 1-4.311-.89m66.28-3.31c0 2.32 1.914 4.2 4.274 4.2h64.106c2.36 0 4.274-1.88 4.274-4.2M93.673 83.692c0 5.8-4.783 10.501-10.684 10.501H31.704c-5.489 0-10.011-4.068-10.616-9.304m72.585-1.197c0 5.8 4.784 10.501 10.684 10.501h51.286c5.9 0 10.684-4.701 10.684-10.5m0-63.005V83.69m0-63.004v-12.6m0 12.6c0 2.32 1.914 4.2 4.273 4.2h57.696c1.534 0 2.993-.317 4.312-.89m-66.28 59.695v71.923c0 5.799 4.782 10.5 10.684 10.5h51.284c5.49 0 10.012-4.068 10.616-9.304V84.888M93.673 8.086c0-2.32-1.913-4.2-4.273-4.2H31.704c-5.9 0-10.684 4.7-10.684 10.5q0 .45.038.89m72.615-7.19c0-2.32 1.914-4.2 4.274-4.2h64.106c2.36 0 4.274 1.88 4.274 4.2m0 0c0-2.32 1.914-4.2 4.273-4.2h57.696c5.9 0 10.685 4.7 10.685 10.5 0 .293.024.587 0 .874m-.07 69.628c1.327.58 2.797.904 4.344.904h2.136c5.901 0 10.684-4.702 10.684-10.501V24.887c0-5.8-4.783-10.5-10.684-10.5h-2.136c-5.596 0-10.187 4.227-10.647 9.61q-.038.44-.038.89V75.29c0 4.28 2.605 7.961 6.342 9.597M21.059 15.276a10.8 10.8 0 0 0-4.312-.89H14.61c-5.9 0-10.684 4.701-10.684 10.5v50.405c0 5.8 4.783 10.5 10.684 10.5h2.137c1.546 0 3.016-.322 4.342-.903 3.737-1.636 6.342-5.317 6.342-9.597V24.887q0-.45-.037-.89c-.334-3.907-2.844-7.206-6.335-8.72",viewW:260,viewH:170}},{id:"bed_single",group:"furniture",category:"bed",w:90,h:200,art:{d:"M3.79 14.024c0 5.59 4.318 10.122 9.645 10.122h63.13c5.328 0 9.646-4.532 9.646-10.122m-22.797 46.93V46.23c0-5.082-3.926-9.201-8.769-9.201h-19.29c-4.842 0-8.768 4.12-8.768 9.201v14.723m-22.797 0h82.42m0 22.085H3.79m8.768 113.06h64.885c4.842 0 8.768-4.12 8.768-9.202V13.104c0-5.082-3.926-9.202-8.768-9.202H12.558c-4.843 0-8.768 4.12-8.768 9.202v173.792c0 5.082 3.925 9.202 8.768 9.202",viewW:90,viewH:200}},{id:"bed_double",group:"furniture",category:"bed",w:160,h:200,art:{d:"M3.879 14.073c0 5.618 4.516 10.172 10.088 10.172h132.065c5.571 0 10.088-4.554 10.088-10.172m-81.165 47.16V46.438c0-5.107-4.106-9.247-9.171-9.247H45.607c-5.065 0-9.17 4.14-9.17 9.247v14.795m89.877 0V46.438c0-5.107-4.107-9.247-9.172-9.247H96.966c-5.065 0-9.171 4.14-9.171 9.247v14.795m-83.916 0H156.12m0 22.192H3.88m9.17 112.673h133.9c5.065 0 9.171-4.14 9.171-9.247V13.15c0-5.107-4.106-9.247-9.171-9.247H13.05c-5.064 0-9.17 4.14-9.17 9.247v173.7c0 5.107 4.106 9.247 9.17 9.247",viewW:160,viewH:200}},{id:"nightstand",group:"furniture",category:"nightstand",w:50,h:40,art:{d:"M5.773 3.555h38.454a2.136 2.056 0 0 1 2.137 2.056v28.777a2.136 2.056 0 0 1-2.137 2.056H5.773a2.136 2.056 0 0 1-2.136-2.056V5.611a2.136 2.056 0 0 1 2.136-2.056",viewW:50,viewH:40}},{id:"cabinet_tv",group:"furniture",category:"nightstand",w:140,h:45,art:{d:"M56.772 17.88h5.315m0 0h5.315m-5.315 0v9.214m9.832-9.214 5.315 9.45 5.314-9.45M6.224 41.4h127.552c1.305 0 2.362-.94 2.362-2.1V5.7c0-1.16-1.057-2.1-2.362-2.1H6.224c-1.305 0-2.362.94-2.362 2.1v33.6c0 1.16 1.057 2.1 2.362 2.1",viewW:140,viewH:45}},{id:"cabinet_shoe",group:"furniture",category:"nightstand",w:80,h:35,art:{d:"m39.722 16.231 1.505-1.596m1.134 3.15 1.53-1.738M6.03 31.5h67.94c1.251 0 2.265-.895 2.265-2v-24c0-1.105-1.014-2-2.264-2H6.03c-1.251 0-2.265.895-2.265 2v24c0 1.105 1.014 2 2.265 2m41.966-14.305L46.92 17.1a3.4 3.4 0 0 1-1.813-.706l-4.583-3.62c-1.264-1-3.277-.326-3.475 1.161-.185 1.39-1.983 2.1-3.28 1.299l-1.532-.947c-.968-.513-2.2.103-2.2 1.1v3.463c0 1.559 1.43 2.823 3.196 2.823h14.51c1.404 0 2.542-1.005 2.542-2.245 0-1.153-.99-2.118-2.289-2.233",viewW:80,viewH:35}},{id:"cabinet_sink",group:"furniture",category:"nightstand",w:80,h:50,art:{d:"M3.765 5.773c0-1.18 1.014-2.136 2.265-2.136h67.94c1.251 0 2.265.956 2.265 2.136v38.454c0 1.18-1.014 2.137-2.264 2.137H6.03c-1.251 0-2.265-.957-2.265-2.137z M36.088 32.135c2.525 1.88 6.205 1.88 8.73 0s3.167-5.101 1.52-7.637l-5.879-8.043-5.893 8.043c-1.644 2.536-1.003 5.755 1.522 7.637",viewW:80,viewH:50}},{id:"bookshelf",group:"furniture",category:"wardrobe",w:100,h:35,art:{d:"M57.12 20.488H45.473c-.514 0-1.008.124-1.372.345s-.568.52-.568.832m4.076-8.236h5.434M6.119 3.5h87.762c1.276 0 2.31.895 2.31 2v24c0 1.105-1.034 2-2.31 2H6.12c-1.275 0-2.31-.895-2.31-2v-24c0-1.105 1.035-2 2.31-2m51 6.4v14.118H45.474a2.04 2.04 0 0 1-1.372-.517 1.7 1.7 0 0 1-.568-1.248V11.665c0-.468.204-.917.568-1.248a2.04 2.04 0 0 1 1.372-.517z",viewW:100,viewH:35}},{id:"wall_unit",group:"furniture",category:"wardrobe",w:240,h:45,art:{d:"M6.336 41.4h227.328a2.418 2.1 0 0 0 2.418-2.1V5.7a2.418 2.1 0 0 0-2.418-2.1H6.336a2.418 2.1 0 0 0-2.418 2.1v33.6a2.418 2.1 0 0 0 2.418 2.1m43.744-5.88V9.48m46.432 26.04V9.48m46.433 26.04V9.48m46.433 26.04V9.48",viewW:240,viewH:45}},{id:"wardrobe",group:"furniture",category:"wardrobe",w:180,h:60,art:{d:"M93.87 20.25a3.3 3.3 0 0 0-1.072-2.407 3.8 3.8 0 0 0-2.586-.997c-.97 0-1.9.358-2.587.997a3.3 3.3 0 0 0-1.071 2.407c0 2.838 1.225 5.107 3.658 6.81h-.015m0 0 14.58 7.537c.57.295 1.046.726 1.377 1.25.331.523.505 1.12.505 1.727v1.401a3.3 3.3 0 0 1-1.071 2.407 3.8 3.8 0 0 1-2.587.998H77.393a3.8 3.8 0 0 1-2.586-.998 3.3 3.3 0 0 1-1.072-2.407v-1.4c0-.608.174-1.205.505-1.728a3.55 3.55 0 0 1 1.377-1.25zM6.284 3.691h167.433c1.32 0 2.392.981 2.392 2.192v48.23c0 1.212-1.071 2.193-2.392 2.193H6.284c-1.321 0-2.392-.981-2.392-2.192V5.885c0-1.212 1.07-2.193 2.392-2.193",viewW:180,viewH:60}},{id:"kitchen_floor",group:"furniture",category:"kitchen_cabinet",w:60,h:60,art:{d:"M3.692 5.884c0-1.21.981-2.192 2.192-2.192h48.23c1.212 0 2.193.981 2.193 2.192v48.23a2.19 2.19 0 0 1-2.192 2.193H5.885a2.19 2.19 0 0 1-2.193-2.192z",viewW:60,viewH:60}},{id:"kitchen_floor_corner",group:"furniture",category:"kitchen_cabinet",w:90,h:90,art:{d:"M45.944 41.767a2.29 2.29 0 0 0 2.29 2.29H83.92a2.29 2.29 0 0 1 2.29 2.29V83.92a2.29 2.29 0 0 1-2.29 2.29H6.08a2.29 2.29 0 0 1-2.29-2.29V6.08a2.29 2.29 0 0 1 2.29-2.29h37.576a2.29 2.29 0 0 1 2.29 2.29z",viewW:90,viewH:90}},{id:"kitchen_wall",group:"furniture",category:"kitchen_cabinet",w:60,h:35,art:{d:"m4.444 31.033 50.75-27.007m-50.752-.06 50.75 27.008M5.884 31.5h48.23c1.212 0 2.193-.895 2.193-2v-24c0-1.105-.981-2-2.192-2H5.885c-1.212 0-2.193.895-2.193 2v24c0 1.105.981 2 2.192 2",viewW:60,viewH:35}},{id:"kitchen_wall_corner",group:"furniture",category:"kitchen_cabinet",w:60,h:60,art:{d:"M3.772 29.59 29.88 4.383M4.57 55.43 55.43 30M4.175 4.383l26.701 23.863m-27.184.877L55.43 55.43M28.41 3.692H5.884c-1.21 0-2.192.981-2.192 2.192v48.23c0 1.212.981 2.193 2.192 2.193h48.23a2.19 2.19 0 0 0 2.193-2.192V31.589c0-1.21-.981-2.192-2.192-2.192h-21.32a2.19 2.19 0 0 1-2.193-2.192V5.885a2.19 2.19 0 0 0-2.192-2.193",viewW:60,viewH:60}},{id:"shelf_floor",group:"furniture",category:"shelving",w:100,h:35,art:{d:"M6.12 31.5h87.76a2.31 2 0 0 0 2.31-2v-24a2.31 2 0 0 0-2.31-2H6.12a2.31 2 0 0 0-2.31 2v24a2.31 2 0 0 0 2.31 2 M9.089 25.933c-.494.112-.776.546-.63.97s.664.675 1.157.564l-.263-.767zM90.912 9.067c.493-.112.775-.546.63-.97s-.664-.675-1.158-.564l.264.767zM11.875 26.956c.493-.112.775-.546.63-.97s-.664-.676-1.158-.564l.264.767zm3.988-2.556c-.493.112-.775.546-.63.969s.664.676 1.158.564l-.264-.766zm5.045.511c.493-.112.775-.545.63-.969s-.664-.676-1.158-.564l.264.766zm3.988-2.555c-.493.111-.775.545-.63.968.146.424.664.677 1.158.565l-.264-.767zm5.044.51c.494-.111.776-.545.63-.968-.145-.424-.664-.676-1.157-.565l.263.767zm3.99-2.555c-.495.112-.777.546-.63.97.145.423.663.675 1.157.563l-.264-.766zm5.043.511c.494-.112.776-.545.63-.969s-.664-.676-1.158-.564l.264.767zm3.989-2.555c-.494.111-.776.545-.63.969s.664.676 1.157.564l-.263-.767zm5.044.51c.494-.11.776-.545.63-.968-.146-.424-.664-.676-1.158-.565l.264.767zm3.989-2.555c-.494.112-.776.546-.63.97.145.423.664.675 1.157.564l-.264-.767zm5.044.511c.493-.111.775-.545.63-.969s-.664-.676-1.158-.564l.264.767zm3.988-2.555c-.493.112-.775.545-.63.969s.664.676 1.158.564l-.264-.767zm5.045.51c.493-.11.775-.545.63-.968-.146-.424-.664-.676-1.158-.564l.264.766zm3.988-2.555c-.493.112-.775.546-.63.97s.664.675 1.158.564l-.264-.767zm5.044.511c.494-.111.776-.545.63-.968-.145-.424-.664-.677-1.157-.565l.264.767zm3.99-2.555c-.494.112-.777.545-.63.969.145.423.663.676 1.157.564l-.264-.766zm5.043.511c.494-.112.776-.546.63-.969s-.664-.676-1.157-.564l.263.766zm3.989-2.556c-.494.112-.776.546-.63.97s.664.676 1.158.564l-.264-.767zM9.353 26.7l.263.767 2.259-.511-.264-.767-.264-.767-2.258.511zm6.774-1.533.264.766 4.517-1.022-.264-.767-.264-.766-4.517 1.022zm9.033-2.045.264.767 4.516-1.022-.264-.767-.263-.767-4.517 1.023zm9.033-2.044.264.766 4.516-1.022-.264-.766-.264-.767-4.516 1.022zm9.033-2.045.263.767 4.517-1.022-.264-.767-.264-.767-4.516 1.023zm9.032-2.044.264.767 4.517-1.023-.264-.766-.264-.767-4.516 1.022zm9.033-2.045.264.767 4.517-1.022-.264-.767-.264-.766-4.517 1.022zm9.033-2.044.264.767 4.516-1.023-.263-.766-.264-.767-4.517 1.022zm9.033-2.044.264.766 4.516-1.022-.264-.767-.263-.766-4.517 1.022zM88.39 8.81l.264.767 2.258-.511-.264-.767-.264-.767-2.258.511z M9.089 9.067c-.494-.112-.776-.546-.63-.97s.664-.675 1.157-.564l-.263.767zm81.823 16.866c.493.112.775.546.63.97s-.664.675-1.158.564l.264-.767zM11.875 8.044c.493.112.775.546.63.97s-.664.676-1.158.564l.264-.767zm3.988 2.556c-.493-.112-.775-.546-.63-.969s.664-.676 1.158-.564l-.264.766zm5.045-.511c.493.112.775.545.63.969s-.664.676-1.158.564l.264-.766zm3.988 2.555c-.493-.111-.775-.545-.63-.968.146-.424.664-.677 1.158-.565l-.264.767zm5.044-.51c.494.111.776.545.63.968-.145.424-.664.676-1.157.565l.263-.767zm3.99 2.555c-.495-.112-.777-.546-.63-.97.145-.423.663-.675 1.157-.563l-.264.766zm5.043-.511c.494.112.776.545.63.969s-.664.676-1.158.564l.264-.767zm3.989 2.555c-.494-.111-.776-.545-.63-.969s.664-.676 1.157-.564l-.263.767zm5.044-.51c.494.11.776.545.63.968-.146.424-.664.676-1.158.565l.264-.767zm3.989 2.555c-.494-.112-.776-.546-.63-.97.145-.423.664-.675 1.157-.564l-.264.767zm5.044-.511c.493.111.775.545.63.969s-.664.676-1.158.564l.264-.767zm3.988 2.555c-.493-.112-.775-.545-.63-.969s.664-.676 1.158-.564l-.264.767zm5.045-.51c.493.11.775.545.63.968-.146.424-.664.676-1.158.564l.264-.766zm3.988 2.555c-.493-.112-.775-.546-.63-.97s.664-.675 1.158-.564l-.264.767zm5.044-.511c.494.111.776.545.63.968-.145.424-.664.677-1.157.565l.264-.767zm3.99 2.555c-.494-.112-.777-.545-.63-.969.145-.423.663-.676 1.157-.564l-.264.766zm5.043-.511c.494.112.776.546.63.969s-.664.676-1.157.564l.263-.766zm3.989 2.556c-.494-.112-.776-.546-.63-.97s.664-.676 1.158-.564l-.264.767zM9.353 8.3l.263-.767 2.259.511-.264.767-.264.767-2.258-.511zm6.774 1.533.264-.766 4.517 1.022-.264.767-.264.766-4.517-1.022zm9.033 2.045.264-.767 4.516 1.022-.264.767-.263.767-4.517-1.023zm9.033 2.044.264-.766 4.516 1.022-.264.766-.264.767-4.516-1.022zm9.033 2.045.263-.767 4.517 1.022-.264.767-.264.767-4.516-1.023zm9.032 2.044.264-.767 4.517 1.023-.264.766-.264.767-4.516-1.022zm9.033 2.045.264-.767 4.517 1.022-.264.767-.264.766-4.517-1.022zm9.033 2.044.264-.767 4.516 1.023-.263.766-.264.767-4.517-1.022zm9.033 2.044.264-.766 4.516 1.022-.264.767-.263.766-4.517-1.022zm9.033 2.045.264-.767 2.258.511-.264.767-.264.767-2.258-.511z",viewW:100,viewH:35}},{id:"shelf_wall",group:"furniture",category:"shelving",w:100,h:25,art:{d:"M18.36 3.15v2.567c0 1.012-1.034 1.833-2.31 1.833h-.924c-1.275 0-2.309-.82-2.309-1.833V3.15m73.905 0v2.567c0 1.012-1.034 1.833-2.31 1.833h-.924c-1.275 0-2.31-.82-2.31-1.833V3.15m-75.29 0H93.65c1.276 0 2.31.82 2.31 1.833V19.65c0 1.012-1.034 1.833-2.31 1.833H5.888c-1.275 0-2.31-.82-2.31-1.833V4.983c0-1.012 1.035-1.833 2.31-1.833",viewW:100,viewH:25}},{id:"cooktop_two",group:"appliance",category:"cooktop",w:30,h:50,art:{d:"M15 30.982v-1.71m3.086 5.128h1.542M15 39.527v-1.709M10.372 34.4h1.542M5.84 46.364h18.322c1.331 0 2.41-1.196 2.41-2.67V6.306c0-1.475-1.079-2.67-2.41-2.67H5.839c-1.331 0-2.41 1.195-2.41 2.67v37.386c0 1.475 1.079 2.67 2.41 2.67m14.56-29.91c0 3.304-2.417 5.982-5.4 5.982-2.981 0-5.4-2.678-5.4-5.981s2.419-5.982 5.4-5.982c2.983 0 5.4 2.678 5.4 5.982M18.858 34.4c0 2.36-1.727 4.273-3.857 4.273s-3.857-1.913-3.857-4.273S12.87 30.127 15 30.127s3.857 1.913 3.857 4.273",viewW:30,viewH:50}},{id:"stove",group:"appliance",category:"cooktop",w:60,h:60,art:{d:"M41.4 35.261a.877.877 0 1 0-1.754 0zm4.384 6.138a.877.877 0 1 0 0-1.753zM41.4 44.03a.877.877 0 1 0-1.753 0zm-4.384-2.63a.877.877 0 1 0 0-1.754zM5.885 3.691v.877h48.23V2.815H5.885zm50.422 2.192h-.877v48.23h1.754V5.885zm-2.192 50.423v-.877H5.885v1.754h48.23zM3.692 54.115h.877V5.885H2.815v48.23zm2.192 2.192v-.877a1.315 1.315 0 0 1-1.315-1.315H2.815a3.07 3.07 0 0 0 3.07 3.069zm50.423-2.192h-.877c0 .726-.589 1.315-1.315 1.315v1.754a3.07 3.07 0 0 0 3.069-3.07zM54.115 3.692v.877c.726 0 1.315.589 1.315 1.315h1.754a3.07 3.07 0 0 0-3.07-3.069zm-48.23 0v-.877a3.07 3.07 0 0 0-3.07 3.07h1.754c0-.727.589-1.316 1.315-1.316zm19.73 36.83h-.877a5.26 5.26 0 0 1-5.262 5.262v1.754a7.015 7.015 0 0 0 7.016-7.015zm-6.139 6.139v-.877a5.26 5.26 0 0 1-5.261-5.261h-1.754a7.015 7.015 0 0 0 7.015 7.015zm-6.138-6.138h.877a5.26 5.26 0 0 1 5.261-5.262v-1.754a7.015 7.015 0 0 0-7.015 7.016zm6.138-6.139v.877a5.26 5.26 0 0 1 5.262 5.262h1.754a7.015 7.015 0 0 0-7.016-7.016zm27.185-14.908h-.877a5.26 5.26 0 0 1-5.261 5.262v1.754a7.015 7.015 0 0 0 7.015-7.016zm-6.138 6.139v-.877a5.26 5.26 0 0 1-5.262-5.262h-1.754a7.015 7.015 0 0 0 7.016 7.016zm-6.139-6.139h.877a5.26 5.26 0 0 1 5.262-5.261v-1.754a7.015 7.015 0 0 0-7.016 7.015zm6.139-6.138v.877a5.26 5.26 0 0 1 5.261 5.261h1.754a7.015 7.015 0 0 0-7.015-7.015zm4.384 27.185h-.877a3.51 3.51 0 0 1-3.507 3.507v1.754a5.26 5.26 0 0 0 5.261-5.261zm-4.384 4.384v-.877a3.51 3.51 0 0 1-3.508-3.507H35.26a5.26 5.26 0 0 0 5.262 5.261zm-4.385-4.384h.877a3.51 3.51 0 0 1 3.508-3.508V35.26a5.26 5.26 0 0 0-5.262 5.262zm4.385-4.385v.877a3.51 3.51 0 0 1 3.507 3.508h1.754a5.26 5.26 0 0 0-5.261-5.262zm0 .877h.876V35.26h-1.753v1.754zm3.508 3.508v.876h1.753v-1.753h-1.753zm-3.508 5.261h.876V44.03h-1.753v1.754zm-5.261-5.261v.876h1.753v-1.753h-1.753zm-9.647-21.047h-.877a5.26 5.26 0 0 1-5.262 5.262v1.754a7.015 7.015 0 0 0 7.016-7.016zm-6.139 6.139v-.877a5.26 5.26 0 0 1-5.261-5.262h-1.754a7.015 7.015 0 0 0 7.015 7.016zm-6.138-6.139h.877a5.26 5.26 0 0 1 5.261-5.261v-1.754a7.015 7.015 0 0 0-7.015 7.015zm6.138-6.138v.877a5.26 5.26 0 0 1 5.262 5.261h1.754a7.015 7.015 0 0 0-7.016-7.015z",viewW:60,viewH:60}},{id:"tv",group:"appliance",category:"tv",w:120,h:28,art:{d:"M45.134 10.766H6.18c-1.292 0-2.34.848-2.34 1.894v2.68c0 1.045 1.048 1.893 2.34 1.893h38.954m0-6.467V5.288c0-1.046 1.048-1.894 2.34-1.894h26.704c1.292 0 2.34.848 2.34 1.894v5.478m-31.384 0h31.384m0 0h37.302c1.293 0 2.34.848 2.34 1.894v2.68c0 1.045-1.047 1.893-2.34 1.893H76.518m-31.384 0v5.479c0 1.046 1.048 1.894 2.34 1.894h26.704c1.292 0 2.34-.848 2.34-1.894v-5.479m-31.384 0h31.384",viewW:120,viewH:28}},{id:"tv_wall",group:"appliance",category:"tv",w:120,h:15,art:{d:"M35.664 11.4c-.517 0-.936.269-.936.6s.419.6.936.6zm49.553 1.2c.517 0 .936-.269.936-.6s-.42-.6-.936-.6zM6.18 3v.6h107.64V2.4H6.18zm109.98 1.5h-.936v1.8h1.872V4.5zm-2.34 3.3v-.6H6.18v1.2h107.64zM3.84 6.3h.936V4.5H2.904v1.8zm2.34 1.5v-.6c-.775 0-1.404-.403-1.404-.9H2.904c0 1.16 1.467 2.1 3.276 2.1zm109.98-1.5h-.936c0 .497-.629.9-1.404.9v1.2c1.81 0 3.276-.94 3.276-2.1zM113.82 3v.6c.775 0 1.404.403 1.404.9h1.872c0-1.16-1.467-2.1-3.276-2.1zM6.18 3v-.6c-1.81 0-3.276.94-3.276 2.1h1.872c0-.497.629-.9 1.404-.9zm61.308 4.2h-.936V12h1.872V7.2zM52.512 12h.936V7.2h-1.872V12zm-16.848 0v.6h49.553v-1.2H35.664z",viewW:120,viewH:15}},{id:"toilet",group:"sanitary",category:"toilet",w:40,h:70,art:{d:"M3.555 48.976c0 9.55 7.125 17.29 15.914 17.29h1.061c8.79 0 15.914-7.74 15.914-17.29V5.966c0-1.233-.92-2.233-2.056-2.233H5.611c-1.135 0-2.056 1-2.056 2.234z M7.666 49.12c0 7.003 5.225 12.68 11.67 12.68h1.327c6.445 0 11.67-5.677 11.67-12.68V31.873c0-1.233-.92-2.233-2.056-2.233H9.722c-1.135 0-2.056 1-2.056 2.233zM33.792 3.733c1.465 0 2.652 1.29 2.652 2.882v12.103c0 1.592-1.187 2.882-2.652 2.882H6.208c-1.465 0-2.653-1.29-2.653-2.882V6.615c0-1.591 1.188-2.882 2.653-2.882z M24.11 50.186c0 2.467-1.84 4.467-4.11 4.467s-4.111-2-4.111-4.467 1.84-4.466 4.11-4.466 4.112 2 4.112 4.466",viewW:40,viewH:70}},{id:"toilet_built_in",group:"sanitary",category:"toilet",w:40,h:55,art:{d:"M3.555 34.56c0 9.263 7.125 16.774 15.914 16.774h1.061c8.79 0 15.914-7.51 15.914-16.775V5.833c0-1.196-.92-2.166-2.056-2.166H5.611c-1.135 0-2.056.97-2.056 2.166z M7.666 34.7c0 6.793 5.225 12.3 11.67 12.3h1.327c6.445 0 11.67-5.507 11.67-12.3V14.5c0-1.197-.92-2.167-2.056-2.167H9.722c-1.135 0-2.056.97-2.056 2.167z M24.11 35.733c0 2.394-1.84 4.334-4.11 4.334s-4.111-1.94-4.111-4.334 1.84-4.333 4.11-4.333 4.112 1.94 4.112 4.333",viewW:40,viewH:55}},{id:"bathtub",group:"sanitary",category:"bathtub",w:170,h:75,art:{d:"M3.885 6c0-1.243 1.068-2.25 2.386-2.25h157.458c1.318 0 2.386 1.007 2.386 2.25v63c0 1.243-1.068 2.25-2.386 2.25H6.27c-1.318 0-2.386-1.007-2.386-2.25z M13.428 37.5c0-13.669 11.75-24.75 26.243-24.75h90.658c14.493 0 26.243 11.081 26.243 24.75s-11.75 24.75-26.243 24.75H39.67c-14.493 0-26.243-11.081-26.243-24.75 M31.56 37.5c0 2.237-1.923 4.05-4.295 4.05s-4.294-1.813-4.294-4.05 1.923-4.05 4.294-4.05c2.372 0 4.295 1.813 4.295 4.05",viewW:170,viewH:75}},{id:"bathtub_corner",group:"sanitary",category:"bathtub",w:140,h:140,art:{d:"M3.862 6.224a2.36 2.36 0 0 1 2.362-2.362H133.97c1.197 0 2.168.97 2.168 2.168 0 71.857-58.251 130.108-130.108 130.108a2.17 2.17 0 0 1-2.168-2.168z M13.31 32.207c0-10.437 8.46-18.897 18.897-18.897h87.628a2.13 2.13 0 0 1 2.13 2.13c0 58.833-47.692 106.526-106.524 106.526a2.13 2.13 0 0 1-2.13-2.131z M32.207 27.483a4.724 4.724 0 1 1-9.449 0 4.724 4.724 0 0 1 9.449 0",viewW:140,viewH:140}},{id:"bidet",group:"sanitary",category:"bidet",w:40,h:60,art:{d:"M22.466 3.692v10.085c0 1.21-.92 2.192-2.055 2.192h-.822c-1.136 0-2.056-.981-2.056-2.192V3.692m4.933 8.77h2.878c1.135 0 2.056-.982 2.056-2.193v-.877c0-1.21-.92-2.192-2.056-2.192h-2.878m-4.933 5.262h-2.878c-1.135 0-2.055-.982-2.055-2.193v-.877c0-1.21.92-2.192 2.055-2.192h2.878M5.611 3.692h28.777c1.136 0 2.056.981 2.056 2.192v33.45c0 9.374-7.125 16.973-15.914 16.973h-1.06c-8.79 0-15.915-7.599-15.915-16.972V5.885c0-1.212.92-2.193 2.056-2.193m4.11 15.785h20.556c1.136 0 2.056.981 2.056 2.192v17.807c0 6.874-5.225 12.447-11.67 12.447h-1.327c-6.445 0-11.67-5.573-11.67-12.447V21.67c0-1.21.92-2.192 2.056-2.192m13.567 7.892c0 1.937-1.472 3.508-3.288 3.508-1.817 0-3.29-1.57-3.29-3.508s1.473-3.507 3.29-3.507 3.288 1.57 3.288 3.507",viewW:40,viewH:60}},{id:"bidet_built_in",group:"sanitary",category:"bidet",w:40,h:50,art:{d:"M17.318 12.182h-8.06c-.88 0-1.592.74-1.592 1.654v16.126c0 6.698 5.225 12.129 11.67 12.129h1.327c6.445 0 11.67-5.43 11.67-12.13V13.837c0-.914-.713-1.654-1.591-1.654H22.68m-.215-8.545v9.827c0 1.18-.92 2.136-2.055 2.136h-.822c-1.136 0-2.056-.956-2.056-2.136V3.637m10.689 8.545V3.637m-16.445 0v8.545M5.611 3.637h28.777c1.136 0 2.056.956 2.056 2.136v24.051c0 9.135-7.125 16.54-15.914 16.54h-1.06c-8.79 0-15.915-7.405-15.915-16.54V5.773c0-1.18.92-2.136 2.056-2.136M23.289 23.29c0 1.888-1.473 3.418-3.29 3.418s-3.288-1.53-3.288-3.418 1.472-3.418 3.289-3.418c1.816 0 3.288 1.53 3.288 3.418",viewW:40,viewH:50}},{id:"kitchen_sink",group:"sanitary",category:"kitchen_sink",w:60,h:50,art:{d:"M38.188 8.7h10.598c1.211 0 2.193.956 2.193 2.136v28.2c0 1.18-.982 2.136-2.193 2.136H11.08c-1.21 0-2.192-.956-2.192-2.136v-28.2c0-1.18.981-2.136 2.192-2.136h10.599m10.885-5.128V15.11c0 1.18-.981 2.136-2.192 2.136h-.877c-1.21 0-2.192-.957-2.192-2.136V3.572m5.261 7.691h3.07c1.21 0 2.192-.956 2.192-2.136v-.855c0-1.18-.982-2.136-2.192-2.136h-3.07m-5.261 5.127h-3.07c-1.21 0-2.192-.956-2.192-2.136v-.855c0-1.18.982-2.136 2.193-2.136h3.069M5.817 46.299h48.231c1.21 0 2.192-.956 2.192-2.136V5.709c0-1.18-.981-2.137-2.192-2.137H5.818c-1.211 0-2.193.957-2.193 2.137v38.454c0 1.18.982 2.136 2.192 2.136M33.44 25.79c0 1.888-1.57 3.418-3.507 3.418s-3.508-1.53-3.508-3.418c0-1.887 1.57-3.418 3.508-3.418s3.507 1.53 3.507 3.418",viewW:60,viewH:50}},{id:"kitchen_sink_double",group:"sanitary",category:"kitchen_sink",w:90,h:50,art:{d:"M53.173 12.182c-.468 0-.847.354-.847.79s.38.79.847.79zm21.979 1.58c.467 0 .846-.353.846-.79 0-.436-.379-.79-.846-.79zM6.01 3.572v.79h77.842v-1.58H6.01zM86.14 5.71h-.847v38.454h1.694V5.709zM83.85 46.3v-.79H6.01v1.58h77.842zM3.72 44.163h.847V5.709H2.873v38.454zm2.29 2.136v-.79c-.797 0-1.443-.603-1.443-1.346H2.873c0 1.616 1.404 2.926 3.137 2.926zm80.131-2.136h-.847c0 .743-.646 1.346-1.442 1.346v1.58c1.732 0 3.136-1.31 3.136-2.926zm-2.29-40.59v.79c.797 0 1.443.602 1.443 1.346h1.694c0-1.617-1.404-2.927-3.136-2.927zm-77.841 0v-.79c-1.733 0-3.137 1.31-3.137 2.926h1.694c0-.744.646-1.347 1.443-1.347zm5.494 37.599v.79h28.39v-1.58h-28.39zm30.68-2.136h.846v-28.2h-1.694v28.2zm-32.97-28.2h-.846v28.2h1.694v-28.2zM39.895 8.7v-.79h-5.669v1.58h5.669zm-22.721 0v-.79h-5.669v1.58h5.669zm-7.958 2.136h.847c0-.744.645-1.346 1.442-1.346V7.91c-1.732 0-3.136 1.31-3.136 2.926zm32.968 0h.847c0-1.616-1.404-2.927-3.136-2.927v1.58c.797 0 1.442.603 1.442 1.347zm-2.29 30.336v.79c1.733 0 3.137-1.31 3.137-2.926h-1.694c0 .743-.645 1.346-1.442 1.346zm-28.389 0v-.79c-.797 0-1.442-.603-1.442-1.346H8.368c0 1.616 1.404 2.926 3.136 2.926zm13.737-23.927v.79h.916v-1.58h-.916zm3.205-2.136h.847V3.572H27.6V15.11zM22.952 3.572h-.847V15.11h1.693V3.572zm3.205 13.673v.79c1.732 0 3.136-1.31 3.136-2.926H27.6c0 .743-.646 1.346-1.443 1.346zm-.916 0v-.79c-.797 0-1.443-.603-1.443-1.346h-1.693c0 1.616 1.404 2.926 3.136 2.926zm3.205-5.982v.79h3.206v-1.58h-3.206zm5.495-2.136h.847v-.855h-1.694v.855zm-2.29-2.991v-.79h-3.205v1.58h3.206zm2.29 2.136h.847c0-1.616-1.404-2.926-3.136-2.926v1.58c.796 0 1.442.603 1.442 1.346zm-2.29 2.991v.79c1.733 0 3.137-1.31 3.137-2.926h-1.694c0 .743-.646 1.346-1.442 1.346zm-11.905 0v.79h3.206v-1.58h-3.206zm3.206-5.127v-.79h-3.206v1.58h3.206zm-5.495 2.136h-.847v.855h1.694v-.855zm2.29-2.136v-.79c-1.733 0-3.137 1.31-3.137 2.926h1.694c0-.743.646-1.346 1.442-1.346zm0 5.127v-.79c-.797 0-1.443-.603-1.443-1.346H16.61c0 1.616 1.404 2.926 3.136 2.926zm9.615 14.527h-.847c0 1.452-1.26 2.628-2.816 2.628v1.58c2.49 0 4.51-1.884 4.51-4.208zM25.7 29.208v-.79c-1.555 0-2.816-1.176-2.816-2.628H21.19c0 2.324 2.02 4.209 4.51 4.209zm-3.663-3.418h.847c0-1.451 1.26-2.628 2.816-2.628v-1.58c-2.49 0-4.51 1.884-4.51 4.208zm3.663-3.418v.79c1.555 0 2.816 1.177 2.816 2.628h1.694c0-2.324-2.02-4.208-4.51-4.208zm24.268 18.8v.79h28.39v-1.58h-28.39zm30.68-2.136h.846v-28.2H79.8v28.2zM78.356 8.7v-.79h-28.39v1.58h28.39zm-30.68 2.136h-.846v28.2h1.694v-28.2zm2.29-2.136v-.79c-1.732 0-3.136 1.31-3.136 2.926h1.694c0-.744.646-1.346 1.442-1.346zm30.68 2.136h.846c0-1.616-1.404-2.927-3.136-2.927v1.58c.797 0 1.443.603 1.443 1.347zm-2.29 30.336v.79c1.732 0 3.136-1.31 3.136-2.926H79.8c0 .743-.646 1.346-1.443 1.346zm-28.39 0v-.79c-.796 0-1.442-.603-1.442-1.346H46.83c0 1.616 1.404 2.926 3.136 2.926zm3.206-28.2v.79h21.979v-1.58h-21.98z",viewW:90,viewH:50}}].map(e=>({id:e.id,group:e.group,category:e.category,w:e.w,h:e.h,art:e.art})),...nl.filter(e=>rl.has(e.id))],sl=new Map(ol.map(e=>[e.id,e]));function al(e){return e&&sl.get(e)||null}function ll(e){return ol.filter(t=>t.group===e)}function cl(e){const t=al(e);return t?{w:t.w,h:t.h}:{w:60,h:60}}function hl(e,t,i=Ta,n=1e3){const r=Number(t)>0?Number(t):5;return(Number(e)||0)/r*i/n}const dl=5e-4,ul=Sa;function pl(e){return Number.isFinite(e)?Math.max(dl,Math.min(ul,e)):dl}function ml(e){return Number.isFinite(e)?Math.max(1,Math.min(1e4,e)):1}const _l=e=>{const t=Math.round(1e3*e)/1e3;return Object.is(t,-0)?"0":String(t)};function fl(e){const t=al(e);if(!t)return null;if(t.art)return t.art;const i=function(e,t,i){if(!e.g)return"";const n=e=>_l(e*t),r=e=>_l(e*i),o=[];for(const s of e.g)if("r"===s[0]){const[,e,t,i,a]=s;o.push(`M${n(e)} ${r(t)}H${n(e+i)}V${r(t+a)}H${n(e)}Z`)}else if("l"===s[0]){const[,e,t,i,a]=s;o.push(`M${n(e)} ${r(t)}L${n(i)} ${r(a)}`)}else if("e"===s[0]){const[,e,a,l,c]=s;o.push(`M${n(e-l)} ${r(a)}A${_l(l*t)} ${_l(c*i)} 0 0 1 ${n(e+l)} ${r(a)}A${_l(l*t)} ${_l(c*i)} 0 0 1 ${n(e-l)} ${r(a)}Z`)}else{const e=s.slice(1);if(e.length<4)continue;let t=`M${n(e[0])} ${r(e[1])}`;for(let i=2;i+10?n:Number.isFinite(r)&&r>0?r:1,s=Number(t),a=o*(Number.isFinite(s)&&s>0?s:1);return Number.isFinite(a)&&a>0?a:o}const vl=e=>{let t=(e%360+360)%360;return t>180&&(t-=360),t};function yl(e,t,i,n,r,o=0){let s=null,a=r;for(const r of n){const[n,l,c,h]=r,d=c-n,u=h-l,p=d*d+u*u;if(!p)continue;const m=Math.sqrt(p);let _=((e-n)*d+(t-l)*u)/p;_=Math.max(0,Math.min(1,_));let f=n+_*d,g=l+_*u;const v=Math.hypot(e-f,t-g);if(!(v0){const e=Math.round(_*m/o)*o;f=n+e/m*d,g=l+e/m*u}s={cx:f+y*(i/2),cy:g+b*(i/2),angle:vl(180*Math.atan2(-y,b)/Math.PI),dist:v}}return s}function bl(e){const{symbol:t,widthCm:i,depthCm:n,point:r,canvasW:o,canvasH:s,cellCm:a,gridPitch:l,walls:c,wallReach:h,free:d=!1}=e;if(!(fl(t)&&s>0&&Number.isFinite(r[0])&&Number.isFinite(r[1])))return null;const u=pl(hl(i,a,l,o)),p=pl(hl(n,a,l,o));let m=r[0],_=r[1],f=0;const g=d?null:yl(m,_,p*s,c,h,l);return g&&(m=g.cx,_=g.cy,f=g.angle),{symbol:t,x:Pa(m/o-u/2),y:Pa(_/s-p/2),w:u,h:p,angle:Number(f.toFixed(2))}}const wl=15e3,kl=2e3,xl=new WeakMap;function $l(e,t){let i=xl.get(e);if(!i){const t=new Set;i={hiddenAt:"hidden"===e.visibilityState?Date.now():0,token:0,subscribers:t,onVisibility:()=>{const t=xl.get(e);if(!t)return;const i=Date.now();if("hidden"===e.visibilityState){t.hiddenAt||(t.hiddenAt=i);const e={kind:"hidden",token:t.token,at:i,hiddenFor:0,long:!1};for(const i of[...t.subscribers])i(e);return}const n=t.hiddenAt?Math.max(0,i-t.hiddenAt):0;t.hiddenAt=0,t.token++;const r={kind:"visible",token:t.token,at:i,hiddenFor:n,long:n>=wl};for(const e of[...t.subscribers])e(r)},onPageShow:t=>{const i=xl.get(e);if(!i)return;const n=Date.now();i.token++;const r={kind:"pageshow",token:i.token,at:n,hiddenFor:0,long:!!t.persisted,persisted:!!t.persisted};for(const e of[...i.subscribers])e(r)}},xl.set(e,i),e.addEventListener("visibilitychange",i.onVisibility),e.defaultView?.addEventListener("pageshow",i.onPageShow)}return i.subscribers.add(t),()=>{const i=xl.get(e);i&&(i.subscribers.delete(t),i.subscribers.size||(e.removeEventListener("visibilitychange",i.onVisibility),e.defaultView?.removeEventListener("pageshow",i.onPageShow),xl.delete(e)))}}const Sl={now:()=>Date.now(),setTimeout:(e,t)=>window.setTimeout(e,t),clearTimeout:e=>window.clearTimeout(e),requestAnimationFrame:e=>window.requestAnimationFrame(e),cancelAnimationFrame:e=>window.cancelAnimationFrame(e)};class Ml{constructor(e,t=Sl){this.onChange=e,this.clock=t,this._state="steady",this._token=0,this._frameFingerprint="",this._hasCompleteFrame=!1,this._overlayPhase="none",this._recoveryReason=null,this._overlayTimer=0,this._overlayRaf=0,this._overlayOpaqueAt=0,this._barrierRafs=new Set,this._trace=[],this._disposed=!1}get state(){return this._state}get token(){return this._token}get frameFingerprint(){return this._frameFingerprint}get hasCompleteFrame(){return this._hasCompleteFrame}get overlayPhase(){return this._overlayPhase}get overlayVisible(){return"none"!==this._overlayPhase}get overlayBlocksInteraction(){return this.overlayVisible}get recoveryReason(){return this._recoveryReason}get trace(){return this._trace.map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}note(e,t={}){this.record(e,void 0,t)}record(e,t,i={}){this._trace.push({at:this.clock.now(),token:this._token,event:e,state:this._state,...t?{reason:t}:{},...i}),this._trace.length>80&&this._trace.splice(0,this._trace.length-80)}changed(){this._disposed||this.onChange()}adoptCompleteFrame(e){e&&(this._hasCompleteFrame=!0,this._frameFingerprint=e,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-adopted"),this.changed())}markCompleteFrame(e){e&&(this._hasCompleteFrame=!0,this._frameFingerprint=e,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-complete"),this.changed())}refreshCompleteFrame(e){!this._disposed&&this._hasCompleteFrame&&"steady"===this._state&&e&&e!==this._frameFingerprint&&(this._frameFingerprint=e,this.record("frame-refreshed"))}visibility(e){return"hidden"===e.kind?(this.record("visibility-hidden"),this._token):e.long||"visible"!==e.kind&&"pageshow"!==e.kind?this.beginCandidate("pageshow"===e.kind?"pageshow":"long-resume"):(this.record("pageshow"===e.kind?"pageshow-noop":"visibility-visible-quick"),this._token)}beginCandidate(e,t="plan"){return this._token++,this._recoveryReason=t,this._hasCompleteFrame?(this._state="connection"===t?"offline-stale":"holding",this.clearOverlay()):this.overlayVisible?this._state="overlay-visible":(this._state="overlay-pending",this.scheduleOverlay(t)),this.record("candidate-start",e),this.changed(),this._token}connectionLost(){return this.beginCandidate("connection-lost","connection")}candidateReady(e){return!this._disposed&&e===this._token&&(this._state="candidate-ready",this.record("candidate-ready"),this.changed(),!0)}async commitAfterPaint(e,t){if(this._disposed||e!==this._token)return!1;let i=!0;const n=(async()=>{for(await t.updateComplete();i&&!this._disposed&&e===this._token;)if(t.stageValid()&&t.assetsReady()){if(await this.nextFrame(),e!==this._token||!t.stageValid()||!t.assetsReady())continue;if(await this.nextFrame(),e===this._token&&t.stageValid()&&t.assetsReady())return!0}else await this.nextFrame();return!1})();let r=0;const o=new Promise(e=>{r=this.clock.setTimeout(()=>e({ready:!1,timedOut:!0}),kl)}),s=await Promise.race([n.then(e=>({ready:e,timedOut:!1})),o]);return i=!1,this.clock.clearTimeout(r),!this._disposed&&e===this._token&&(!(!s.ready&&!s.timedOut)&&(s.ready?(this._hasCompleteFrame=!0,this._frameFingerprint=t.frameFingerprint(),this.record("paint-barrier"),this.finishOverlayAfterCommit(),!0):(this.record("paint-barrier-timeout"),this._hasCompleteFrame?(this._state="connection"===this._recoveryReason?"offline-stale":"steady",this.clearOverlay()):(this._state="recovery-error","opaque"!==this._overlayPhase&&this.showOverlayNow()),this.changed(),!1)))}retry(e=this._recoveryReason||"plan"){return this.beginCandidate("retry",e)}nextFrame(){return new Promise(e=>{const t=this.clock.requestAnimationFrame(()=>{this._barrierRafs.delete(t),e()});this._barrierRafs.add(t)})}scheduleOverlay(e){this.clearOverlayTimer(),this._recoveryReason=e,this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._hasCompleteFrame||"overlay-pending"!==this._state&&"candidate-ready"!==this._state||(this._state="overlay-visible",this._overlayPhase="entering",this.record("overlay-enter"),this.changed(),this._overlayRaf=this.clock.requestAnimationFrame(()=>{this._overlayRaf=0,"entering"===this._overlayPhase&&(this._overlayPhase="fading-in",this.changed(),this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,"fading-in"===this._overlayPhase&&(this._overlayPhase="opaque",this._overlayOpaqueAt=this.clock.now(),this.record("overlay-opaque"),this.changed())},150))}))},150)}showOverlayNow(){this.clearOverlayTimer(),this._overlayPhase="opaque",this._overlayOpaqueAt=this.clock.now(),this.record("overlay-error")}finishOverlayAfterCommit(){if(this.clearOverlayTimer(),"none"===this._overlayPhase||"entering"===this._overlayPhase||"fading-in"===this._overlayPhase)return this.clearOverlay(),this._state="steady",this._recoveryReason=null,this.record("candidate-committed"),void this.changed();const e=Math.max(0,this._overlayOpaqueAt+250-this.clock.now());this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._overlayPhase="leaving",this.record("overlay-leave"),this.changed(),this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._overlayPhase="none",this._state="steady",this._recoveryReason=null,this.record("candidate-committed"),this.changed()},180)},e)}clearOverlayTimer(){this._overlayTimer&&this.clock.clearTimeout(this._overlayTimer),this._overlayTimer=0}clearOverlay(){this.clearOverlayTimer(),this._overlayRaf&&this.clock.cancelAnimationFrame(this._overlayRaf),this._overlayRaf=0,this._overlayPhase="none",this._overlayOpaqueAt=0}dispose(){this._disposed=!0,this._token++,this.clearOverlay();for(const e of this._barrierRafs)this.clock.cancelAnimationFrame(e);this._barrierRafs.clear()}}function Rl(e){let t=2166136261,i=2654435769;const n=new WeakSet,r=e=>{for(let n=0;n>>0)+(i>>>2)}},o=e=>{if(null===e)return void r("n");const t=typeof e;if("string"===t)return void r(`s${e.length}:${e}`);if("number"===t)return void r(`d${Number.isNaN(e)?"NaN":String(e)}`);if("boolean"===t)return void r(e?"t":"f");if("undefined"===t)return void r("u");if("object"!==t)return void r(`${t}:${String(e)}`);const i=e;if(n.has(i))r("[cycle]");else{if(n.add(i),Array.isArray(e)){r("[");for(const t of e)o(t);return r("]"),void n.delete(i)}r("{");for(const t of Object.keys(e).sort())r(t),o(e[t]);r("}"),n.delete(i)}};return o(e),`${(t>>>0).toString(16).padStart(8,"0")}${(i>>>0).toString(16).padStart(8,"0")}`}function Tl(e){return Rl(e)}const Cl=new WeakMap;class Dl{constructor(e,t=()=>Date.now()){this.onUpdate=e,this.now=t,this.fallbackAuthority={},this.referenceOwner={},this.referenced=new Set,this.queued=new Set,this.disposed=!1,this.sharedUpdate=()=>{this.disposed||this.onUpdate()}}bind(e){const t=((e,t)=>{const i=e?.connection||e?.callWS||t;return!i||"object"!=typeof i&&"function"!=typeof i?t:i})(e,this.fallbackAuthority);return this.shared&&this.authority===t||(this.shared&&(this.shared.listeners.delete(this.sharedUpdate),this.shared.references.delete(this.referenceOwner)),this.authority=t,this.shared=(e=>{let t=Cl.get(e);return t||(t={signed:{},queued:new Set,inFlight:new Map,retry:new Map,listeners:new Set,settlers:new Set,references:new Map},Cl.set(e,t)),t})(t),this.disposed||(this.shared.listeners.add(this.sharedUpdate),this.shared.references.set(this.referenceOwner,new Set(this.referenced)))),this.shared}start(e,t){this.disposed=!1,this.referenced=new Set(t());const i=this.bind(e());i.listeners.add(this.sharedUpdate),i.references.set(this.referenceOwner,new Set(this.referenced)),this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(e(),t()),288e5)}dispose(){if(this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.shared){this.shared.listeners.delete(this.sharedUpdate),this.shared.references.delete(this.referenceOwner);for(const e of this.queued)this.shared.queued.delete(e)}this.queued.clear()}invalidate(e){const t=this.bind(e);clearTimeout(this.batchTimer),this.queued.clear(),t.queued.clear(),t.inFlight.clear(),t.retry.clear(),t.signed={}}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(e,t){const i=_n(t);if(!i.startsWith("/api/houseplan/content/"))return i;const n=this.bind(e);this.referenced.add(i),n.references.set(this.referenceOwner,new Set(this.referenced));const r=n.signed[i],o=r?this.now()-r.at:1/0;return o{const t=[...this.queued];this.queued.clear();for(const e of t)i.queued.delete(e);this.sign(e,t)},30))}sign(e,t){if(!t.length||!e?.callWS)return;const i=this.bind(e);for(const n of function(e,t){const i=Math.max(1,Math.floor(t)),n=[];for(let t=0;t{const n=i.inFlight.get(e);return!(void 0!==n&&t-n<15e3)&&(i.inFlight.set(e,t),!0)});r.length&&e.callWS({type:"houseplan/content/sign",paths:r}).then(e=>{const t=this.now();let n=0,o=!1;for(const s of r){const r=e?.urls?.[s];if("string"==typeof r&&r){const e=i.signed[s];e?.loaded&&e.url!==r?(e.pending={url:r,at:t},this.preloadReplacement(i,s,r,t)):(i.signed[s]={url:r,at:t,loaded:e?.url===r&&!!e.loaded},o=!0),i.retry.delete(s),n++}else this.backOff(i,s)}if(n&&(this.trimShared(i),o))for(const e of[...i.listeners])e()}).catch(()=>{for(const e of r)this.backOff(i,e)}).finally(()=>{for(const e of r)i.inFlight.get(e)===t&&i.inFlight.delete(e);for(const e of[...i.settlers])e()})}}preloadReplacement(e,t,i,n){const r=()=>{const r=e.signed[t];if(r?.pending?.url===i){e.signed[t]={url:i,at:n,loaded:!0},e.retry.delete(t),this.trimShared(e);for(const t of[...e.listeners])t()}},o=()=>{const n=e.signed[t];n?.pending?.url===i&&(delete n.pending,this.backOff(e,t))};if("undefined"==typeof Image)return void r();const s=new Image;s.onload=()=>{("function"==typeof s.decode?s.decode():Promise.resolve()).then(r).catch(o)},s.onerror=o,s.src=i}preloadCurrentImage(e,t,i){if(i.loaded)return Promise.resolve(!0);if(i.preload)return i.preload;if("undefined"==typeof Image)return i.loaded=!0,Promise.resolve(!0);const n=i.url;return i.preload=new Promise(r=>{const o=new Image;let s=!1;const a=o=>{if(s)return;s=!0,clearTimeout(l);const a=e.signed[t];if(a===i&&delete a.preload,o&&a===i&&a.url===n){a.loaded=!0;for(const t of[...e.listeners])t();r(!0)}else o||a!==i||this.backOff(e,t),r(!1)};o.onload=()=>{("function"==typeof o.decode?o.decode():Promise.resolve()).then(()=>a(!0)).catch(()=>a(!1))},o.onerror=()=>a(!1);const l=setTimeout(()=>a(!1),kl);o.src=n}),i.preload}prepareImage(e,t){const i=_n(t);if(!i.startsWith("/api/houseplan/content/"))return Promise.resolve(!0);const n=this.bind(e),r=()=>{const t=n.signed[i];if(t&&this.now()-t.atthis.now()&&!n.inFlight.has(i)&&!n.queued.has(i)?Promise.resolve(!1):null},o=r();return o?(s=o,new Promise(e=>{let t=!1;const i=i=>{t||(t=!0,clearTimeout(n),e(i))},n=setTimeout(()=>i(!1),kl);s.then(i).catch(()=>i(!1))})):new Promise(e=>{let t=!1,i=null;const o=i=>{t||(t=!0,clearTimeout(a),n.settlers.delete(s),e(i))},s=()=>{t||i||(i=r(),i&&i.then(o).catch(()=>o(!1)))},a=setTimeout(()=>o(!1),kl);n.settlers.add(s),s()});var s}backOff(e,t){const i=e.retry.get(t)?.delay||0,n=Math.min(6e4,i?2*i:2e3);e.retry.set(t,{notBefore:this.now()+n,delay:n})}trimShared(e){const t=Object.entries(e.signed);if(t.length<=512)return;const i=new Set(e.inFlight.keys());for(const t of e.references.values())for(const e of t)i.add(e);for(const[e,n]of t)(n.pending||n.preload)&&i.add(e);t.sort((e,t)=>t[1].at-e[1].at);const n=t.filter(([e])=>i.has(e));for(const e of t){if(n.length>=512)break;i.has(e[0])||n.push(e)}e.signed=Object.fromEntries(n)}resign(e,t){const i=this.bind(e);this.referenced=new Set(t),i.references.set(this.referenceOwner,new Set(this.referenced));const n=this.now(),r=new Set;for(const e of i.references.values())for(const t of e)r.add(t);const o={};for(const[e,t]of Object.entries(i.signed))r.has(e)&&n-t.at!!o[e]&&!o[e].pending))}markLoaded(e,t,i){const n=_n(t);if(!n.startsWith("/api/houseplan/content/"))return;const r=this.bind(e).signed[n];if(r&&(!i||r.url===i)&&!r.loaded){r.loaded=!0;for(const t of[...this.bind(e).listeners])t()}}isReady(e,t){const i=_n(t);if(!i.startsWith("/api/houseplan/content/"))return!0;const n=this.bind(e).signed[i];return!!n&&this.now()-n.ate.id===t)}var Al="M11,18H13V16H11V18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,6A4,4 0 0,0 8,10H10A2,2 0 0,1 12,8A2,2 0 0,1 14,10C14,12 11,11.75 11,15H13C13,12.75 16,12.5 16,10A4,4 0 0,0 12,6Z";function Pl(e,t,i){return[e[0]*t+e[1]*i+e[2],e[3]*t+e[4]*i+e[5]]}function Ol(e,t){let i=0;for(const[n,r]of t){const t=Pl(e,n[0],n[1]);i=Math.max(i,Math.hypot(t[0]-r[0],t[1]-r[1]))}return i}const Fl=e=>{if(null==e||""===e)return null;const t=Number(e);return Number.isFinite(t)?t:null},Il=e=>{const t=e,i=Fl(Array.isArray(t)?t[0]:t?.x),n=Fl(Array.isArray(t)?t[1]:t?.y);return null==i||null==n?null:[i,n]};function El(e){if(!Array.isArray(e))return null;const t=[];for(const i of e){const e=Il(i);if(!e)return null;t.push(e)}if(t.length>1){const e=t[0],i=t[t.length-1];e[0]===i[0]&&e[1]===i[1]&&t.pop()}if(t.length<3)return null;let i=0,n=0,r=0;for(let e=0;ee+t[0],0)/t.length,t.reduce((e,t)=>e+t[1],0)/t.length]:[n/(3*i),r/(3*i)]}function Hl(e){if(!Array.isArray(e))return null;const t=[];for(const i of e){const e=Il(i);if(!e)return null;t.push(e)}if(t.length>1){const e=t[0],i=t[t.length-1];e[0]===i[0]&&e[1]===i[1]&&t.pop()}const i=El(t);if(!i)return null;const n=t.map(e=>e[0]),r=t.map(e=>e[1]);return{centroid:i,bbox:[Math.min(...n),Math.min(...r),Math.max(...n),Math.max(...r)]}}function Nl(e){let t=e;if(t&&"object"==typeof t&&!Array.isArray(t)){const e=t;t=e.path??e.points??t}if(!Array.isArray(t)||!t.length)return[];let i=(t.some(e=>(e=>Array.isArray(e)?e.length>=2&&!Array.isArray(e[0])&&("object"!=typeof e[0]||null==e[0]):!!e&&"object"==typeof e&&("x"in e||"y"in e))(e))?[t]:t.filter(Array.isArray)).map(e=>e.map(Il).filter(e=>null!=e)).filter(e=>e.length>=2);i.length>64&&(i=i.slice(-64));const n=i.reduce((e,t)=>e+t.length,0);if(n<=4e3)return i.map(e=>e.slice());const r=4e3-2*i.length,o=i.map(e=>e.length-2),s=o.reduce((e,t)=>e+t,0),a=o.map(e=>r*e/s),l=a.map(Math.floor);let c=r-l.reduce((e,t)=>e+t,0);const h=a.map((e,t)=>({index:t,fraction:e-Math.floor(e)})).sort((e,t)=>t.fraction-e.fraction||e.index-t.index);for(let e=0;efunction(e,t){if(t>=e.length)return e.slice();if(t<=2)return[e[0],e[e.length-1]];const i=[];for(let n=0;ne.slice()),i=t[t.length-1];return i?(i.length>2?i.pop():t.pop(),t):[]}const ql=e=>Number.isFinite(e[0])&&Number.isFinite(e[1]),Bl=(e,t)=>e[0]===t[0]&&e[1]===t[1];function jl(e){if(!e)return null;const t=e.vacuum_position||e.robot_position||null,i=t&&null!=Fl(t.x)&&null!=Fl(t.y)?{x:Fl(t.x),y:Fl(t.y),a:Fl(t.a??t.angle??t.theta)}:null,n=Nl(e.path?.path??e.path?.points??e.path),r=[],o=e.rooms,s=Array.isArray(o)?o.map((e,t)=>[String(e?.id??t),e]):o&&"object"==typeof o?Object.entries(o):[];for(const[e,t]of s){if(!t||"object"!=typeof t)continue;const i=String(t.name??t.label??"").trim(),n=[[Fl(t.cx),Fl(t.cy)],[Fl(t.center?.x),Fl(t.center?.y)],[Fl(t.x),Fl(t.y)]].find(([e,t])=>null!=e&&null!=t);let o=n?.[0]??null,s=n?.[1]??null;const a=Hl(t.outline),l=Fl(t.x0),c=Fl(t.y0),h=Fl(t.x1),d=Fl(t.y1),u=(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)||a?.bbox||null;if(null!=o&&null!=s||!a||([o,s]=a.centroid),null!=o&&null!=s||!u||(o=(u[0]+u[2])/2,s=(u[1]+u[3])/2),i&&null!=o&&null!=s){const t={id:e,name:i,cx:o,cy:s};u&&([t.x0,t.y0,t.x1,t.y1]=u),r.push(t)}}const a=function(e){return String(e.map_name??e.current_map??e.map_index??e.selected_map??"default")}(e);return i||r.length||n.length?{pos:i,path:n,rooms:r,mapId:a}:null}function Ul(e,t,i){return![e,t,i].every(Number.isFinite)||e<0||t<=0||i<=0?Number.POSITIVE_INFINITY:e/t*i}const Vl=40;function Gl(e,t,i){if(!e||!t)return null;const n=t.attributes||{},r=e.split(".")[0]||"",o=null!=i?.platform?String(i.platform):null,s=!!Il(n.vacuum_position??n.robot_position),a=!(!n.rooms||"object"!=typeof n.rooms),l=Nl(n.path?.path??n.path?.points??n.path).length>0,c=[n.map_name,n.current_map,n.map_index,n.selected_map].some(e=>null!=e),h="xiaomi_cloud_map_extractor"===o,d=a||l||c;let u=null;if(s?u="compatible":h?u="known_xcme_incomplete":d?u="partial":"camera"===r&&(u="camera"),!u)return null;const p=s?"camera"===r?300:200:h?100:d?50:0;return{entityId:e,name:String(n.friendly_name||e),platform:o,category:u,hasPosition:s,hasRooms:a,hasPath:l,hasMapId:c,score:p}}const Kl=e=>e.toLowerCase().replace(/[\s_\-.,]+/g,"");function Yl(e,t){const i=new Set(t.map(Kl).filter(Boolean)),n=new Set;for(const t of e){const e=Kl(t.name||"");e&&i.has(e)&&n.add(e)}return n.size}function Xl(e,t){const i=new Map(t.map(e=>[Kl(e.name),e])),n=[],r=[];for(const t of e){const e=i.get(Kl(t.name));e&&(n.push([[t.cx,t.cy],[e.cx,e.cy]]),r.push(t.name))}if(n.length<3)return null;const o=function(e){if(e.length<3)return null;let t=0,i=0,n=0,r=0,o=0,s=0,a=0,l=0,c=0,h=0,d=0,u=0;for(const[[p,m],[_,f]]of e){if(![p,m,_,f].every(Number.isFinite))return null;t+=p*p,i+=p*m,n+=p,r+=m*m,o+=m,s+=1,a+=p*_,l+=m*_,c+=_,h+=p*f,d+=m*f,u+=f}const p=[t,i,n,i,r,o,n,o,s],m=e=>{const[t,i,n,r,o,s,a,l,c]=p,h=t*(o*c-s*l)-i*(r*c-s*a)+n*(r*l-o*a);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(o*c-s*l)/h,(n*l-i*c)/h,(i*s-n*o)/h,(s*a-r*c)/h,(t*c-n*a)/h,(n*r-t*s)/h,(r*l-o*a)/h,(i*a-t*l)/h,(t*o-i*r)/h];return[d[0]*e[0]+d[1]*e[1]+d[2]*e[2],d[3]*e[0]+d[4]*e[1]+d[5]*e[2],d[6]*e[0]+d[7]*e[1]+d[8]*e[2]]},_=m([a,l,c]),f=m([h,d,u]);if(!_||!f)return null;const g=[_[0],_[1],_[2],f[0],f[1],f[2]];return g.every(Number.isFinite)?g:null}(n);return o?{matrix:o,matched:r,residual:Ol(o,n)}:null}function Zl(e,t,i){const n=e[e.length-1];if(n&&n[0]===t[0]&&n[1]===t[1])return e;if(e.push(t),e.length<=600)return e;let r=function(e,t){if(e.length<3)return e.slice();const i=new Uint8Array(e.length);i[0]=i[e.length-1]=1;const n=[[0,e.length-1]];for(;n.length;){const[r,o]=n.pop(),[s,a]=e[r],[l,c]=e[o],h=l-s,d=c-a,u=Math.hypot(h,d)||1e-9;let p=0,m=-1;for(let t=r+1;tp&&(p=i,m=t)}m>0&&p>t&&(i[m]=1,n.push([r,m],[m,o]))}const r=[];for(let t=0;t600&&(r=r.filter((e,t)=>t%2==0||t===r.length-1)),r}function Jl(e){return"cleaning"===e||"returning"===e||"on"===e}const Ql={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function ec(e){const[t,i]=Ql[e.rot]||[1,0],n=e.mir?-1:1;return[e.s*t*n,-e.s*i,e.ox,e.s*i*n,e.s*t,e.oy]}function tc(e){const t=e[0]*e[4]-e[1]*e[3];if(!Number.isFinite(t)||Math.abs(t)<1e-12)return null;const i=t<0,n=Math.sqrt(Math.abs(t));let r=180*Math.atan2(-e[1],e[4])/Math.PI;return r=(90*Math.round(r/90)%360+360)%360,{ox:e[2],oy:e[5],s:n,rot:r,mir:i}}function ic(e,t){const i=[],n=[];for(const t of e)null!=t.x0?(i.push(t.x0,t.x1),n.push(t.y0,t.y1)):(i.push(t.cx),n.push(t.cy));if(!i.length)return{ox:t[0]+t[2]/2,oy:t[1]+t[3]/2,s:t[2]/1e4,rot:0,mir:!0};const r=Math.min(...i),o=Math.max(...i),s=Math.min(...n),a=Math.max(...n),l=Math.max(o-r,a-s)||1,c={ox:0,oy:0,s:.6*Math.min(t[2],t[3])/l,rot:0,mir:!0},h=ec(c),[d,u]=Pl(h,(r+o)/2,(s+a)/2);return c.ox=t[0]+t[2]/2-d,c.oy=t[1]+t[3]/2-u,c}function nc(e,t,i,n){const[r,o]=Pl(ec(t),i,n),s=ec({...e,ox:0,oy:0}),[a,l]=Pl(s,i,n);return{...e,ox:r-a,oy:o-l}}function rc(e){const t=e?.trail_mode;return"never"===t||"cleaning"===t||"always"===t?t:!1===e?.trail?"never":"cleaning"}const oc={availability:"available",status:"alarm",activity:"none"},sc=new Set(["motion","vibration","sound"]),ac=new Set(["occupancy","presence"]),lc=new Set(["door","window","garage_door","opening"]),cc=new Set(["running","power"]),hc=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const dc=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),uc=new Set([...dc,"start","started","run","active","in_progress","wash","rinse","spin","dry"]),pc=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping","stop","end","done","inactive"]),mc=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),_c=e=>""===e||"unknown"===e||"unavailable"===e||"__missing__"===e,fc=e=>String(e??"").trim().toLowerCase(),gc=e=>fc(e).replace(/[\s-]+/g,"_").replace(/_+/g,"_").replace(/^_|_$/g,""),vc=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]]),yc=new Set(["wifi","connection","signal","battery"]);function bc(e,t){const i=e?.entities?.[t]||{};if("config"===i.entity_category)return null;const n=String(t||"").split(".").slice(1).join("."),r=[i.translation_key,i.original_name,i.name],o=[...r,n];r.some(e=>fc(e))||o.push(e?.states?.[t]?.attributes?.friendly_name);const s=o.map(gc).filter(Boolean);if(s.some(e=>e.split("_").some(e=>yc.has(e))))return null;let a=null;for(const e of s)for(const[t,i]of vc)(e===t||e.endsWith(`_${t}`))&&(a=null==a?i:Math.min(a,i));return a}const wc=(e,t)=>null!=bc(e,t);function kc(e,t){if(!t.startsWith("switch."))return!1;const i=e?.entities?.[t]||{},n=e?.states?.[t],r=t.slice(7).toLowerCase();if(/(?:^|_)(?:main_)?power$/.test(r))return!0;if([i.translation_key,i.original_name,i.name].map(fc).some(e=>["power","main power","power switch","питание"].includes(e)))return!0;const o=fc(n?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(o)||/(?:^|[\s._-])питание$/.test(o)}function xc(e){for(const t of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=fc(e?.[t]);if(dc.has(i)||pc.has(i))return i}return""}function $c(e,t){const i=e?.states?.[t],n=i?fc(i.state):"__missing__",r=String(t||"").split(".")[0],o=fc(i?.attributes?.device_class),s={eid:t,state:n,availability:_c(n)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===s.availability)return s;if(function(e,t,i){return"alarm_control_panel"===e?"triggered"===i:"on"===i&&Sn(e,t)}(r,o,n))return{...s,status:"alarm"};if("binary_sensor"===r)return sc.has(o)?{...s,edge:"rising"}:ac.has(o)?{...s,activity:"on"===n?"presence":"none"}:lc.has(o)?{...s,status:"on"===n?"open":"neutral",edge:"rising"}:"moving"===o?{...s,activity:"on"===n?"transition":"none"}:cc.has(o)&&"on"===n?{...s,status:"working",activity:"running"}:s;if("cover"===r)return{...s,activity:"opening"===n||"closing"===n?"transition":"none",edge:"terminal_transition"};if("lock"===r)return{...s,status:"unlocked"===n||"open"===n?"open":"neutral",activity:"locking"===n||"unlocking"===n?"transition":"none",edge:"terminal_transition"};if("valve"===r)return{...s,status:["open","opening","closing"].includes(n)?"open":"neutral",activity:"opening"===n||"closing"===n?"transition":"none",edge:"terminal_transition"};if("climate"===r){const e=xc(i.attributes),t=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(fc):[],r=!pc.has(n)&&(mc.has(n)||dc.has(n)||t.includes(n));return(e?dc.has(e):r)?{...s,status:"working",activity:"running"}:s}if(["light","switch","fan","humidifier"].includes(r))return"on"===n?{...s,status:"working",activity:"running"}:s;if("media_player"===r)return"off"===n?{...s,availability:"unavailable"}:s;if("vacuum"===r)return"cleaning"===n?{...s,status:"working",activity:"running"}:"returning"===n?{...s,status:"working",activity:"transition"}:s;if("script"===r)return"on"===n?{...s,status:"working",activity:"running"}:s;if("automation"===r)return s;if("button"===r||"event"===r)return{...s,edge:"change"};const a=xc(i.attributes);return dc.has(a)||dc.has(n)&&!pc.has(n)?{...s,status:"working",activity:"running"}:s}function Sc(e,t,i){const n=i.filter(t=>t.startsWith("switch.")&&!e?.entities?.[t]?.entity_category),r=t.find(t=>kc(e,t));if(!(n.length>1&&!!r))return t.map(t=>$c(e,t));const o=t.find(t=>t!==r&&wc(e,t)),s=$c(e,r);return"unavailable"===s.availability||"off"===s.state?t.map(t=>({eid:t,state:e?.states?.[t]?fc(e.states[t].state):"__missing__",availability:"unavailable",status:"neutral",activity:"none",edge:"none"})):t.map(t=>{if(t===o)return function(e,t){const i=e?.states?.[t],n=i?fc(i.state):"__missing__",r={eid:t,state:n,availability:_c(n)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===r.availability)return r;const o=xc(i?.attributes);return dc.has(o)||uc.has(n)&&!pc.has(n)?{...r,status:"working",activity:"running"}:r}(e,t);return{...t===r?s:$c(e,t),status:"neutral",activity:"none",edge:"none"}})}function Mc(e){if(!e.length)return{availability:"available",status:"neutral",activity:"none"};const t=e.filter(e=>"available"===e.availability);if(!t.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(t.some(e=>"alarm"===e.status))return oc;const i=t.some(e=>"working"===e.status)?"working":t.some(e=>"open"===e.status)?"open":"neutral",n=t.some(e=>"transition"===e.activity)?"transition":t.some(e=>"presence"===e.activity)?"presence":t.some(e=>"running"===e.activity)?"running":"none";return{availability:"available",status:i,activity:n}}function Rc(e,t){if(!(e=>!!e&&!_c(e))(e)||"unavailable"===t.availability||e===t.state)return null;if("rising"===t.edge)return"off"===e&&"on"===t.state?"event":null;if("change"===t.edge)return"event";if("terminal_transition"===t.edge){const i=new Set([e,t.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}const Tc=(e,t=0)=>{const i=Number(e);return Number.isInteger(i)&&i>=0?i:t},Cc=(e,t)=>{const i={rev:0,configRev:Tc(t),off:new Set};if(null==e)return{snapshot:i,valid:!1};const n=e.off;return!Number.isInteger(e.rev)||Number(e.rev)<0||!Number.isInteger(e.config_rev)||Number(e.config_rev)<0||!Array.isArray(n)||n.some(e=>"string"!=typeof e||!e)?{snapshot:i,valid:!1}:{snapshot:{rev:Number(e.rev),configRev:Number(e.config_rev),off:new Set(n)},valid:!0}};function Dc(e,t=0){return Cc(e,t).snapshot}function zc(e,t,i,n){const r=Cc(t,i);return n&&r.valid&&r.snapshot.configRev===i?e.configRev===i&&r.snapshot.rev{try{const[i,n]=await Promise.all([e.callWS({type:"config/device_registry/list"}),e.callWS({type:"config/entity_registry/list"})]),r=Bc(i,"id"),o=Bc(n,"entity_id");if(!r||!o)throw new Error("invalid_registry_response");t.devices=r,t.entities=o,t.authoritative=!0,t.access="full",t.lastSuccess=Date.now(),t.error=void 0}catch(e){t.authoritative=!1,t.access="limited",t.error=function(e){if(e&&"object"==typeof e){const t=e;return String(t.message||t.code||t.error||"registry_unavailable")}return String(e||"registry_unavailable")}(e)}finally{t.revision++,t.loading=void 0,function(e){for(const t of[...e.listeners])try{t()}catch{}}(t)}})()),t.loading}function Uc(e,t){void 0===t.reloadTimer&&(t.reloadTimer=globalThis.setTimeout(()=>{t.reloadTimer=void 0,jc(e,t)},80))}async function Vc(e,t){if(t.subscribing||t.unsubDevice&&t.unsubEntity)return t.subscribing;const i=e?.connection?.subscribeEvents;return"function"==typeof i?(t.subscribing=(async()=>{try{t.unsubDevice||(t.unsubDevice=await i.call(e.connection,()=>Uc(e,t),"device_registry_updated")),t.unsubEntity||(t.unsubEntity=await i.call(e.connection,()=>Uc(e,t),"entity_registry_updated"))}catch{}finally{0===t.refs&&(t.unsubDevice?.(),t.unsubEntity?.(),t.unsubDevice=void 0,t.unsubEntity=void 0),t.subscribing=void 0}})(),t.subscribing):void 0}function Gc(e,t){const i=qc(e);if(!i)return()=>{};const n=0===i.refs;i.refs++,i.listeners.add(t),("pending"===i.access||n)&&jc(e,i),Vc(e,i);let r=!1;return()=>{r||(r=!0,i.listeners.delete(t),i.refs=Math.max(0,i.refs-1),i.refs>0||(i.unsubDevice?.(),i.unsubEntity?.(),i.unsubDevice=void 0,i.unsubEntity=void 0,void 0!==i.reloadTimer&&globalThis.clearTimeout(i.reloadTimer),i.reloadTimer=void 0))}}function Kc(e){const t=qc(e);if(!t)return{revision:0,authoritative:!1,access:"limited",devices:e?.devices||{},entities:e?.entities||{},lastSuccess:0,error:"registry_unavailable"};let i=e?.devices||{},n=e?.entities||{};if(t.authoritative){const r=t.liveDevices!==i||t.liveEntities!==n,o=void 0!==t.liveDevices||void 0!==t.liveEntities;if(r&&(t.liveDevices=i,t.liveEntities=n,t.projectedRevision=void 0,o&&Uc(e,t)),t.projectedRevision!==t.revision||!t.projectedDevices||!t.projectedEntities){const e={...t.devices},r={...t.entities};for(const[t,n]of Object.entries(i))Object.prototype.hasOwnProperty.call(e,t)||(e[t]=n);for(const[e,t]of Object.entries(n))Object.prototype.hasOwnProperty.call(r,e)||(r[e]=t);t.projectedDevices=e,t.projectedEntities=r,t.projectedRevision=t.revision}i=t.projectedDevices,n=t.projectedEntities}return{revision:t.revision,authoritative:t.authoritative,access:t.access,devices:i,entities:n,lastSuccess:t.lastSuccess,error:t.error}}function Yc(e){if(!e||"object"!=typeof e&&"function"!=typeof e)return 0;let t=Hc.get(e);return t||(t=Nc++,Hc.set(e,t)),t}function Xc(e,t=Kc(e)){return[t.revision,t.access,Yc(t.devices),Yc(t.entities)].join(":")}function Zc(e){return!!e&&null==e.disabled_by}function Jc(e,t=Kc(e)){const i={},n={},r={};for(const[e,n]of Object.entries(t.devices||{}))Zc(n)&&(i[e]=n);for(const[e,i]of Object.entries(t.entities||{})){if(!Zc(i))continue;const r=i.device_id?t.devices?.[i.device_id]:null;t.authoritative&&i.device_id&&!r||(r&&!Zc(r)||(n[e]=i))}for(const[i,n]of Object.entries(e?.states||{})){const e=t.entities?.[i];if(e&&!Zc(e))continue;const o=e?.device_id?t.devices?.[e.device_id]:null;t.authoritative&&e?.device_id&&!o||(o&&!Zc(o)||(r[i]=n))}return{...e,devices:i,entities:n,states:r}}function Qc(e,t=Kc(e)){return{...e,devices:t.devices||{},entities:t.entities||{}}}function eh(e,t){const i=[];for(const[n,r]of Object.entries(e||{}))r?.device_id===t&&i.push(n);return i}function th(){if(Wc)return Wc;if("undefined"==typeof localStorage)return{};try{const e=JSON.parse(localStorage.getItem(Lc)||"{}");if(!e||"object"!=typeof e||Array.isArray(e))return{};const t=Date.now(),i={};for(const[n,r]of Object.entries(e))!r||"active"!==r.kind&&"ha_disabled"!==r.kind||!Number.isFinite(r.ts)||t-r.ts>7776e6||(i[n]=r);return Wc=i,Wc}catch{return{}}}function ih(e,t,i=Kc(e)){if(!t||"virtual"===t)return{kind:"active",enabledEntityIds:[],allEntityIds:[]};const n=t.indexOf(":");if(n<1)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const r=t.slice(0,n),o=t.slice(n+1);if("device"!==r&&"entity"!==r||!o)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const s=i.devices||{},a=i.entities||{};if(i.authoritative){if("device"===r){const e=s[o];if(!e)return{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[]};const t=eh(a,o);if(!Zc(e))return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:t};const i=t.filter(e=>Zc(a[e]));return t.length&&!i.length?{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:t}:{kind:"active",enabledEntityIds:i,allEntityIds:t}}const t=a[o];return t&&!Zc(t)?{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]}:t?.device_id&&!s[t.device_id]?{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[o]}:t?.device_id&&!Zc(s[t.device_id])?{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]}:t||e?.states?.[o]?{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}:{kind:"orphaned",reason:"entity_missing",enabledEntityIds:[],allEntityIds:[]}}const l=function(e){return th()[e]||null}(t);if("device"===r){const t=s[o],i=eh(a,o);if(null!=t?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:i};const n=i.filter(e=>{const t=a[e];return null==t?.disabled_by&&(!t.device_id||null==s[t.device_id]?.disabled_by)});if(i.length&&!n.length&&i.every(e=>null!=a[e]?.disabled_by))return{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:i};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"device",enabledEntityIds:[],allEntityIds:i};if(t||n.some(t=>!!e?.states?.[t]))return{kind:"active",enabledEntityIds:n,allEntityIds:i}}else{const t=a[o];if(null!=t?.disabled_by)return{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]};if(t?.device_id&&null!=s[t.device_id]?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"entity",enabledEntityIds:[],allEntityIds:[o]};if(t||e?.states?.[o])return{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}}return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]}}function nh(e){const t={};for(const[i,n]of Object.entries(e.entities)){if(!n?.device_id||!Zc(n))continue;const r=e.devices?.[n.device_id];r&&!Zc(r)||(t[n.device_id]=t[n.device_id]||[]).push(i)}return t}function rh(e,t){const i=new Map;for(const n of e){if(!0===n?.removed)continue;const e=n?.binding||"";if(!e.startsWith("entity:"))continue;const r=e.slice(7);if(!r)continue;const o=t?.entities?.[r]?.device_id;if(!o)continue;const s=i.get(o)||new Set;s.add(r),i.set(o,s)}return{byDevice:i}}function oh(e,t,i,n){const r=n.byDevice.get(t);return r?.size?{partial:!0,entityIds:i.filter(t=>!r.has(t)&&!e?.entities?.[t]?.hidden)}:{partial:!1,entityIds:[...i]}}function sh(e,t,i){if(t.identifiers?.[0]?.[0])return t.identifiers[0][0];for(const t of i){const i=e.entities[t]?.platform;if(i)return i}return""}function ah(e,t){if(/_device_temperature$/.test(t))return!1;if(e.entities?.[t]?.entity_category)return!1;const i=e.states[t];if(!i)return/_temperature$/.test(t);const n=i.attributes||{};return"temperature"===n.device_class||/°C|°F/.test(n.unit_of_measurement||"")||/_temperature$/.test(t)}const lh=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],ch=e=>[...e.filter(e=>!e.reg?.hidden),...e.filter(e=>!!e.reg?.hidden)];function hh(e,t){const i=t.map(t=>({eid:t,reg:e?.entities?.[t]})).filter(e=>!!e.reg);if(!i.length)return[];const n=i.filter(e=>!e.reg.entity_category),r=n.length?n:i;for(const e of lh){const t=r.filter(t=>t.eid.startsWith(e+"."));if(t.length)return ch(t).map(e=>e.eid)}const o=r.filter(t=>function(e,t){if(!t.startsWith("binary_sensor."))return!1;const i=fc(e?.states?.[t]?.attributes?.device_class||e?.entities?.[t]?.device_class||e?.entities?.[t]?.original_device_class);return sc.has(i)||ac.has(i)||lc.has(i)||cc.has(i)||"moving"===i||hc.has(i)}(e,t.eid));if(o.length)return ch(o).map(e=>e.eid);const s=r.filter(e=>e.eid.startsWith("switch."));if(s.length){const t=ch(s),n=t.find(t=>kc(e,t.eid));if(s.length>1&&n){const t=i.filter(t=>t.eid!==n.eid&&"config"!==t.reg?.entity_category&&null!=bc(e,t.eid)),r=t.filter(e=>!e.reg?.entity_category),o=r.length?r:t;for(const t of[0,1,2]){const i=ch(o.filter(i=>bc(e,i.eid)===t));if(i.length)return[i[0].eid,n.eid]}}return[(n||t[0]).eid]}const a=[];for(const e of xe)a.push(...ch(r.filter(t=>t.eid.startsWith(e+"."))));return a.push(...ch(r.filter(e=>!xe.includes(e.eid.split(".")[0])))),a.map(e=>e.eid)}function dh(e,t,i){const n=t.map(t=>({eid:t,reg:e.entities[t],st:e.states[t]})).filter(e=>e.reg),r=[n.filter(e=>!e.reg.hidden&&!e.reg.entity_category),n.filter(e=>!e.reg.entity_category),n.filter(e=>!e.reg.hidden),n];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const t of r){const i=t.find(t=>ah(e,t.eid));if(i)return i.eid}const o=hh(e,t);return o.find(ln)||o[0]}function uh(e,t,i=[]){return[...new Set(ph(e,t,i))].filter(e=>ln(e))}function ph(e,t,i=[]){const n=e?.startsWith("entity:")?new Set([e.slice(7)]):new Set(i);return(t||[]).filter(e=>"string"==typeof e&&!n.has(e))}function mh(e,t){if("string"==typeof t)return e.area===t;const i=e.marker?.room_id;return i?!!t.id&&i===t.id:!!t.area&&e.area===t.area}function _h(e){const t=e.marker?.binding?.startsWith("entity:")?e.marker.binding.slice(7):null,i=e.entities.filter(ln),n=e.primary&&ln(e.primary)?e.primary:null;return[...new Set([t,n,...i].filter(e=>!!e&&ln(e)))]}function fh(e){const t=_h(e),i=e.marker?.light_entity;return i&&t.includes(i)?i:t[0]||null}function gh(e,t){if(!1===t.marker?.is_light)return[];if(!0===t.marker?.is_light){return[{eid:fh(t),via:"forced"}]}const i=t.primary||hh(e,t.entities)[0];return i&&!i.startsWith("light.")?[]:t.entities.filter(t=>t.startsWith("light.")&&!!e.states?.[t]).map(e=>({eid:e,via:"light"}))}const vh=new WeakMap,yh=new WeakMap;function bh(e){return e.map(e=>[e.id||"",!0===e.hidden?1:0,e.primary||"",[...e.entities].join(","),void 0===e.controls?"":[...e.controls].join(","),e.marker?.id||"",e.marker?.binding||"",e.marker?.is_light,e.marker?.tap_action||"",!0===e.marker?.removed?1:0,e.marker?.light_entity||"",null==e.marker?.controls?"":[...e.marker.controls].join(",")].join("")).join("")}function wh(e){const t=bh(e),i=vh.get(e);if(i?.fingerprint===t)return i;const n=e.filter(e=>!e.hidden),r=new Map,o=new Map;for(const t of e){o.set(t,ph(t.marker?.binding,t.marker?.controls??t.controls,t.entities));const e=t.hidden||!0!==t.marker?.is_light?null:t.marker?.id||t.id;e&&r.set(String(e),t)}const s=new Map;for(const t of e){const e=o.get(t)||[],i=new Set(void 0===t.controls?e.filter(ln):t.controls.filter(ln)),n=fh(t),a=i.size?[...i]:n?[n]:[];for(const i of e){if(!i.startsWith("marker:"))continue;const e=i.slice(7);if(!e||e===String(t.marker?.id||t.id||""))continue;if(!r.has(e))continue;const n=s.get(e)||{markerId:e,controllers:[],driverEids:[]};n.controllers.push({device:t,driverEids:[...a]}),n.driverEids=[...new Set([...n.driverEids,...a])],s.set(e,n)}}const a={fingerprint:t,visible:n,markerById:r,persistedByDevice:o,incomingByMarker:s};return vh.set(e,a),a}function kh(e){return wh(e).incomingByMarker}function xh(e,t){const i=uh(t.marker?.binding,t.controls??t.marker?.controls,t.entities),n=ph(t.marker?.binding,t.marker?.controls??t.controls,t.entities);return(null!=t.marker?.is_light||!i.length&&!n.some(e=>e.startsWith("marker:")))&&gh(e,t).length>0}function $h(e,t){return gh(e,t).some(e=>!!e.eid)}function Sh(e,t,i,n){const r="always"===e||"auto"===e&&t,o=r&&!i;return{sourceExists:r,fromSourceEnabled:r&&i,manualEnabled:r,radiusEnabled:r,passive:o,effectiveMode:o&&"auto"===n?"fixed":n}}function Mh(e,t){const i=uh(t.marker?.binding,t.controls??t.marker?.controls,t.entities).map(e=>({eid:e,via:"controls"})),n=xh(e,t)?gh(e,t):[];for(const e of n)e.eid&&i.some(t=>t.eid===e.eid)||i.push(e);return i}function Rh(e,t,i,n){if(null!=i)return Rh(e,t,null,n).filter(e=>mh(e.device,i));const r=bh(t),o=function(e,t){const i=new Set;for(const e of t){for(const t of e.entities)i.add(t);for(const t of e.controls||e.marker?.controls||[])"string"!=typeof t||t.startsWith("marker:")||i.add(t)}return[...i].sort().map(t=>`${t}:${e?.states?.[t]?.state??""}`).join("|")}(e,t),s=Pc(n),a=yh.get(t);if(a?.graphFingerprint===r&&a.stateFingerprint===o&&a.virtualLightFingerprint===s&&a.registry===e?.entities)return a.sources;const{visible:l,markerById:c,persistedByDevice:h,incomingByMarker:d}=wh(t),u=null==i?l:l.filter(e=>mh(e,i)),p=new Map,m=new Map;for(const t of u){const i=Mh(e,t).filter(e=>"controls"!==e.via);for(const n of i){const i=!0===t.marker?.is_light?String(t.marker?.id||t.id||""):"",r=i?`marker:${i}`:`entity:${n.eid}`,o=n.eid?[n.eid]:[],s={key:r,eid:n.eid||"",stateEids:o,serviceEids:o,device:t,via:n.via,castsGlow:!0,passive:!n.eid,on:!n.eid||"on"===e.states?.[n.eid]?.state};p.set(t,[...p.get(t)||[],s]),n.eid&&m.set(n.eid,s)}}const _=new Map;for(const e of t){const t=h.get(e)||[],i=new Set(void 0===e.controls?t.filter(ln):e.controls);_.set(e,i)}for(const t of p.values())for(const i of t){if(!i.passive)continue;const t=i.key.slice(7),r=d.get(t);i.on=!r||r.driverEids.some(t=>"on"===e.states?.[t]?.state),!r&&Oc(i.device.marker)&&(i.on=Fc(i.device.marker,n))}const f=new Map,g=e=>{const t=f.get(e.key);(!t||e.castsGlow&&!t.castsGlow)&&f.set(e.key,e)};for(const t of l){const n=h.get(t)||[],r=null==i||mh(t,i);for(const i of n){if(i.startsWith("marker:")){const e=c.get(i.slice(7)),t=e?p.get(e):null;if(t)for(const e of t)g(e);continue}if(!ln(i)||!_.get(t)?.has(i))continue;const n=m.get(i);n?g(n):r&&g({key:`entity:${i}`,eid:i,stateEids:[i],serviceEids:[i],device:t,via:"controls",castsGlow:!1,passive:!1,on:"on"===e.states?.[i]?.state})}for(const e of p.get(t)||[])g(e)}const v=[...f.values()];return yh.set(t,{graphFingerprint:r,stateFingerprint:o,virtualLightFingerprint:s,registry:e?.entities,sources:v}),v}function Th(e){const t=e.filter(e=>e.castsGlow);return t.find(e=>e.on)||t[0]||null}function Ch(e){return e.length?e.some(e=>e.on)?"on":"off":"none"}function Dh(e,t){const i=[];for(const n of t){const t=e.states[n];if(!t)continue;const r=(t.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(n)||"lqi"===r){const e=parseFloat(t.state);isNaN(e)||i.push(e);continue}const o=t.attributes?.linkquality??t.attributes?.lqi;if(null!=o){const e=parseFloat(o);isNaN(e)||i.push(e)}}return _i(i)}function zh(e,t){for(const i of t){if(!ah(e,i))continue;const t=e.states[i];if(!t)continue;const n=parseFloat(t.state);if(!isNaN(n))return Math.round(10*n)/10}return null}function Ah(e,t){for(const i of t){if(!i.startsWith("climate."))continue;const t=e.states[i];if(!t||"unavailable"===t.state||"unknown"===t.state)continue;const n=parseFloat(t.attributes?.current_temperature);if(Number.isFinite(n))return Math.round(10*n)/10}return null}function Ph(e,t){if(e.entities?.[t]?.entity_category)return!1;const i=e.states[t];if(!i)return/_humidity$/.test(t);const n=i.attributes||{};return"humidity"===n.device_class||"%"===n.unit_of_measurement&&/_humidity$/.test(t)||/_humidity$/.test(t)}function Oh(e,t){for(const i of t){if(!Ph(e,i))continue;const t=e.states[i];if(!t)continue;const n=parseFloat(t.state);if(!isNaN(n))return Math.round(n)}return null}function Fh(e,t){if(!t)return[];const i=[];for(const[t,n]of Object.entries(e.entities)){if(!t.startsWith("light.")||n.hidden||!Zc(n))continue;let r=null;if("group"===n.platform)r=n.area_id||null;else{if(!n.device_id)continue;{const t=e.devices[n.device_id];if(!Zc(t))continue;if("Group"!==t?.model)continue;r=t.area_id||n.area_id||null}}if(!r)continue;const o=e.states[t];i.push({eid:t,name:n.name||o?.attributes?.friendly_name||t,area:r})}return i}function Ih(e,t,i,n,r){const o=ke(t,i,r);if(o!==we)return o;const s=[];for(const t of n){const i=e.states[t]?.attributes?.device_class;i&&s.push(i)}return function(e){for(const t of e){const e=be[t];if(e)return e}return null}(s)??we}function Eh(e,t){return e.map(e=>{if(!Array.isArray(e.controls))return e;const i=e.controls.filter(e=>!("string"==typeof e&&e.startsWith("marker:")&&t.has(e.slice(7))));return i.length===e.controls.length?e:{...e,controls:i.length?i:null}})}function Hh(e,t,i){if(!t||!i||t===i)return[...e];const n=`marker:${t}`,r=`marker:${i}`;return e.map(e=>{const t=Array.isArray(e.controls)?e.controls.map(e=>e===n?r:e):e.controls,i="derived_marker_state"===e.value_badge?.source?.kind&&e.value_badge.source.ref===n?{...e.value_badge,source:{...e.value_badge.source,ref:r}}:e.value_badge,o="derived_marker_state"===e.value_source?.kind&&e.value_source.ref===n?{...e.value_source,ref:r}:e.value_source;return t===e.controls&&i===e.value_badge&&o===e.value_source?e:{...e,controls:t,value_badge:i,value_source:o}})}function Nh(e,t,i){if(!t||t===i)return!0;const n=new Map;for(const t of e)n.set(t.id,(t.controls||[]).filter(e=>"string"==typeof e&&e.startsWith("marker:")).map(e=>e.slice(7)));const r=new Set,o=[i];for(;o.length;){const e=o.pop();if(e===t)return!0;r.has(e)||(r.add(e),o.push(...n.get(e)||[]))}return!1}function Lh(e,t,i,n){const r=new Set([t]),o=e.filter(e=>{const o=e.id===t||!n&&e.binding===i;return o&&r.add(e.id),!o});return{markers:n?o:[...o,{id:t,binding:i,removed:!0,hidden:!0}],cleanupIds:r}}function Wh(e){const t=new Set,i=new Set,n=new Set;for(const r of e||[]){const e=String(r.binding||"").indexOf(":");if(e<1)continue;const o=r.binding.slice(0,e),s=r.binding.slice(e+1);s&&(!0===r?.removed?"device"===o?t.add(s):"entity"===o&&i.add(s):"entity"===o&&n.add(s))}return{devices:t,entities:i,liveEntities:n}}function qh(e,t,i){if(i.liveEntities.has(t))return!1;if(i.entities.has(t))return!0;const n=e?.entities?.[t]?.device_id;return!!n&&i.devices.has(n)}function Bh(e,t,i,n,r){const o=uh(t.binding,t.controls,e.entities).filter(e=>!qh(i,e,n)).filter(e=>"active"===ih(i,"entity:"+e,r).kind);e.marker=t,e.controls=o,e.userHidden=!0===t.hidden,e.hidden=e.userHidden||"ha_disabled"===e.bindingStatus?.kind,t.name&&(e.name=t.name),t.icon&&(e.icon=t.icon),null!=t.model&&(e.model=t.model),e.link=t.link??null,e.description=t.description??null,e.pdfs=t.pdfs||[],e.tapAction=t.tap_action??null}function jh(e,t,i,n){if("string"==typeof e.room_id&&e.room_id.length>0&&null===e.area)return{area:"",space:e.space||n};const r=e.area||t||"";return{area:r,space:r&&i[r]||e.space||n}}function Uh(e){const t=e.hass,i=e.registry||Kc(t),n=Jc(t,i),r=Qc(t,i),{areaToSpace:o,markers:s,settings:a,excluded:l,showAll:c,firstSpaceId:h,loc:d,iconRules:u}=e,p=!1!==a.group_lights,m=Wh(s),_=Fh(n,p).filter(e=>!qh(n,e.eid,m)),f=new Set(_.map(e=>e.area)),g=nh(n),v=function(e){const t={};for(const[i,n]of Object.entries(e.entities||{}))n?.device_id&&(t[n.device_id]=t[n.device_id]||[]).push(i);return t}(r),y=rh(s,r),b=new Set;for(const e of s){const[t,i]=e.binding.split(":");"device"!==t&&"entity"!==t||!i||b.add(e.binding)}const w=(e,t)=>s.find(i=>i.binding===e+":"+t),k={},x=[];for(const e of Object.values(n.devices)){const r=e.area_id;if(!r||!o[r])continue;if("service"===e.entry_type)continue;if(b.has("device:"+e.id))continue;const s=ih(t,"device:"+e.id,i);if("active"!==s.kind)continue;const h=w("device",e.id);if(h&&h.hidden&&!a.filter_seeded)continue;const m=oh(n,e.id,g[e.id]||[],y);if(m.partial&&!m.entityIds.length)continue;const _=m.entityIds,v=m.partial?{kind:"active",enabledEntityIds:_,allEntityIds:_}:s,$=sh(n,e,_),S=!a.filter_seeded;if(S&&!c){if(l.has($))continue;if("Group"===e.model)continue;if(/scene/i.test(e.model||""))continue;if(/bridge/i.test((e.model||"")+(e.name||"")))continue;if("myheat"===$&&e.via_device_id)continue}const M=(e.name_by_user||e.name||d("device.unnamed")).trim(),R=M+"|"+r;let T=Ih(n,M,e.model,_,u);if(_.some(e=>e.startsWith("lock."))&&(T="mdi:lock"),S&&!c&&p&&"mdi:lightbulb"===T&&f.has(r))continue;k[R]=(k[R]||0)+1;const C=k[R]>1?M+" "+k[R]:M,D={id:e.id,name:C,model:e.model||"",area:r,space:o[r],icon:T,entities:_,allEntities:v.allEntityIds,bindingStatus:v,bindingKind:"device",bindingRef:e.id,pdfs:[]};D.primary=dh(n,_,T),"mdi:thermometer"!==T&&"mdi:air-filter"!==T||(D.temp=zh(n,_)),D.primary&&Ph(n,D.primary)&&(D.hum=Oh(n,_)),x.push(D)}for(const e of _)o[e.area]&&(b.has("entity:"+e.eid)||x.push({id:"lg_"+e.eid,name:e.name,model:d("device.light_group"),area:e.area,space:o[e.area],icon:"mdi:lightbulb-group",entities:[e.eid],allEntities:[e.eid],bindingStatus:{kind:"active",enabledEntityIds:[e.eid],allEntityIds:[e.eid]},primary:e.eid,bindingKind:"entity",bindingRef:e.eid,pdfs:[]}));for(const e of s){if(e.removed)continue;const[s,l]=e.binding.split(":"),c="device"===s||"entity"===s?ih(t,e.binding,i):null;if(!e.hidden||a.filter_seeded||"ha_disabled"===c?.kind)if("device"===s){const t=c;if("unverified"===t.kind)continue;const s=r.devices[l],{area:a,space:p}=jh(e,s?.area_id,o,h),_="active"===t.kind?t.enabledEntityIds:[];let f=s?Ih(n,s.name_by_user||s.name||"",s.model,_,u):"mdi:help-circle";_.some(e=>e.startsWith("lock."))&&(f="mdi:lock");const g={id:e.id,name:s?.name_by_user||s?.name||d("device.fallback"),model:s?.model||"",area:a,space:p,icon:f,entities:_,allEntities:t.allEntityIds.length?t.allEntityIds:s&&v[s.id]||[],bindingStatus:t,bindingKind:"device",bindingRef:l};g.primary=dh(n,_,f),"mdi:thermometer"!==f&&"mdi:air-filter"!==f||(g.temp=zh(n,_)),g.primary&&Ph(n,g.primary)&&(g.hum=Oh(n,_)),Bh(g,e,r,m,i),x.push(g)}else if("entity"===s){if(qh(r,l,m))continue;const t=c;if("unverified"===t.kind)continue;const s=r.entities[l],a=s?.area_id||s?.device_id&&r.devices[s.device_id]?.area_id||"",{area:d,space:p}=jh(e,a,o,h),_=n.states[l],f=s?.name||_?.attributes?.friendly_name||l;let g=Ih(n,f,"",[l],u);l.startsWith("lock.")&&(g="mdi:lock");const v={id:e.id,name:f,model:"",area:d,space:p,icon:g,entities:"active"===t.kind?[l]:[],allEntities:[l],bindingStatus:t,primary:"active"===t.kind?l:void 0,bindingKind:"entity",bindingRef:l};"mdi:thermometer"!==g&&"mdi:air-filter"!==g||!v.entities.length||(v.temp=zh(n,v.entities)),v.entities.length&&Ph(n,l)&&(v.hum=Oh(n,v.entities)),Bh(v,e,r,m,i),x.push(v)}else{const t=e.area||"",n=e.space||t&&o[t]||h,s={id:e.id,name:e.name||d("device.virtual"),model:e.model||"",area:t,space:n,icon:e.icon||"mdi:map-marker",entities:[],allEntities:[],bindingStatus:{kind:"active",enabledEntityIds:[],allEntityIds:[]},bindingKind:"virtual",virtual:!0};Bh(s,e,r,m,i),x.push(s)}}return x}function Vh(e){const{marker:t,siblingMarkers:i=[],...n}=e;return Uh({...n,markers:[...i.filter(e=>e.id!==t.id),t]}).find(e=>e.id===t.id)||null}function Gh(e,t,i,n){if(!t)return null;if(function(e,t,i){if(!t)return!1;const n=t.indexOf(":");if(n<1)return!1;const r=t.slice(0,n),o=t.slice(n+1),s=Wh(i);return"device"===r?s.devices.has(o):"entity"===r&&qh(e,o,s)}(e,t,n))return null;const r=t.indexOf(":");if(r<0)return null;const o=t.slice(0,r),s=t.slice(r+1);if(!s)return null;if("entity"===o){const t=parseFloat(e.states[s]?.state);return Number.isFinite(t)?"temp"===i?Math.round(10*t)/10:Math.round(t):null}if("device"===o){const t=Object.entries(e.entities).filter(([,e])=>e.device_id===s).map(([e])=>e);return"temp"===i?zh(e,t):Oh(e,t)}return null}const Kh=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 Yh(e,t){return t.area?t.area:e&&t.id?`@room/${encodeURIComponent(e)}/${encodeURIComponent(t.id)}`:null}function Xh(e){return null==e.area&&e.space&&e.room_id?Yh(e.space,{id:e.room_id,area:null}):e.area||null}function Zh(e,t,i){const n=new Map;if(!e?.entities)return n;const r=Wh(i),o=new Set,s=new Map,a=new Map;for(const e of i||[]){if(e?.removed)continue;const t=(e.binding||"").indexOf(":");if(t<=0)continue;const i=e.binding.slice(0,t),n=e.binding.slice(t+1);if(!n)continue;!0===e.use_climate_temp&&o.add(n);const r=Xh(e);r&&("entity"!==i||s.has(n)?"device"!==i||a.has(n)||a.set(n,r):s.set(n,r))}const l=new Map;for(const[t,i]of Object.entries(e.entities)){if(i.device_id&&r.devices.has(i.device_id)&&!r.liveEntities.has(t)||!i.device_id&&r.entities.has(t)&&!r.liveEntities.has(t))continue;const n=i.device_id?e.devices?.[i.device_id]:null;if(!Zc(i)||n&&!Zc(n))continue;const c=s.get(t)||(i.device_id?a.get(i.device_id):null)||i.area_id||n?.area_id||null;if(!c)continue;if(i.entity_category)continue;if(!(o.size>0&&t.startsWith("climate.")&&(o.has(t)||i.device_id&&o.has(i.device_id)))){if(_e.has(i.platform))continue;if(Kh.test(t))continue}let h=l.get(c);h||(h=new Map,l.set(c,h));const d=i.device_id||t;let u=h.get(d);if(!u){const r=e.states?.[t];u={name:(n?n.name_by_user||n.name:i.name||r?.attributes?.friendly_name||t)||t,model:n?.model,ents:[]},h.set(d,u)}u.ents.push(t)}for(const[i,r]of l){const s=[],a=[];for(const[i,n]of r){const r=Ih(e,n.name,n.model,n.ents,t),l="mdi:thermometer"===r||"mdi:air-filter"===r;if(l){const t=zh(e,n.ents);null!=t&&s.push(t)}if(o.size>0&&(o.has(i)||n.ents.some(e=>o.has(e)))){const t=Ah(e,n.ents);null!=t&&s.push(t)}if(l||"mdi:water-percent"===r){const t=Oh(e,n.ents);null!=t&&a.push(t)}}(s.length||a.length)&&n.set(i,{temp:s.length?Math.round(s.reduce((e,t)=>e+t,0)/s.length*10)/10:null,hum:a.length?Math.round(a.reduce((e,t)=>e+t,0)/a.length):null})}return n}function Jh(e,t,i){const n=[];for(const r of t){if(r.area!==i||r.virtual)continue;const t=Dh(e,r.entities);null!=t&&n.push(t)}return _i(n)}const Qh={offStates:["off"],unknownUsesToggle:!0},ed={light:Qh,switch:Qh,fan:Qh,humidifier:Qh,input_boolean:Qh,automation:Qh,remote:Qh,group:Qh,climate:{...Qh,featureMasks:{turn_on:256,turn_off:128,toggle:384}},media_player:{...Qh,featureMasks:{turn_on:128,turn_off:256,toggle:384}},siren:{...Qh,featureMasks:{turn_on:1,turn_off:2,toggle:3}},vacuum:{...Qh,featureMasks:{turn_on:1,turn_off:2,toggle:3}},water_heater:{...Qh,featureMasks:{turn_on:8,turn_off:8,toggle:8}},camera:{...Qh,featureMasks:{turn_on:1,turn_off:1,toggle:1}}};function td(e){return e.slice(0,e.indexOf("."))}function id(e,t,i){if(!e?.services||"object"!=typeof e.services)return!1;const n=e.services?.[t];return!!n&&Object.prototype.hasOwnProperty.call(n,i)}function nd(e,t,i){return id(e,t,i)?{domain:t,service:i}:id(e,"homeassistant",i)?{domain:"homeassistant",service:i}:null}function rd(e,t){return e?.entities?.[t]||null}function od(e,t,i){const n=e?.states?.[i],r=rd(t,i);return n?.attributes?.friendly_name||r?.name||r?.original_name||i}function sd(e,t){const i=rd(e,t);if(null!=i?.disabled_by)return!0;const n=i?.device_id?e?.devices?.[i.device_id]:null;return null!=n?.disabled_by}function ad(e,t){const i=e?.attributes?.supported_features;if(null==i||""===i)return!1;const n=Number(i);return Number.isFinite(n)&&(n&t)===t}function ld(e,t,i){const n=td(i);if("lock"===n||"alarm_control_panel"===n)return!0;if("cover"!==n)return!1;const r=e?.states?.[i],o=rd(t,i),s=String(r?.attributes?.device_class||o?.device_class||o?.original_device_class||"");return $i.has(s)}function cd(e,t,i,n,r){return{ref:i,entityId:n,name:n?od(e,t,n):null,reason:r}}function hd(e,t,i,n,r,o=null){return{target:null,skipped:cd(e,t,i,n,r),semantics:o,nextEffect:null,command:null}}function dd(e,t,i,n,r=i){const o=td(i);if("cover"!==o&&"valve"!==o)return function(e,t,i,n,r=i){const o=td(i),s=e?.states?.[i];if(sd(t,i))return hd(e,t,r,i,"ha-disabled","power");if(ld(e,t,i))return hd(e,t,r,i,"secure");if(!s)return hd(e,t,r,i,"missing","power");if("unavailable"===s.state)return hd(e,t,r,i,"unavailable","power");const a=ed[o];if(!a)return hd(e,t,r,i,"unsupported");let l,c;if("unknown"===s.state||""===s.state){if(!a.unknownUsesToggle)return hd(e,t,r,i,"unsupported","power");l="toggle",c="toggle"}else a.offStates.includes(String(s.state))?(l="turn-on",c="turn_on"):(l="turn-off",c="turn_off");const h=a.featureMasks?.[c];if(h&&!ad(s,h))return hd(e,t,r,i,"unsupported","power");const d=nd(e,o,c);return d?{target:{entityId:i,name:od(e,t,i),state:String(s.state||""),via:n},skipped:null,semantics:"power",nextEffect:l,command:{...d,data:{entity_id:i}}}:hd(e,t,r,i,"unsupported","power")}(e,t,i,n,r);if(sd(t,i))return hd(e,t,r,i,"ha-disabled",o);if(ld(e,t,i))return hd(e,t,r,i,"secure",o);const s=e?.states?.[i];if(!s)return hd(e,t,r,i,"missing",o);if("unavailable"===s.state)return hd(e,t,r,i,"unavailable",o);const a=function(e,t,i){const n=String(i?.state||""),r="closed"===n?{service:`open_${t}`,effect:"open",feature:1}:"open"===n?{service:`close_${t}`,effect:"close",feature:2}:"opening"===n||"closing"===n?{service:`stop_${t}`,effect:"stop",feature:8}:null;return r&&ad(i,r.feature)&&id(e,t,r.service)?r:ad(i,3)&&id(e,t,"toggle")?{service:"toggle",effect:"toggle"}:null}(e,o,s);if(!a)return hd(e,t,r,i,"unsupported",o);return{target:{entityId:i,name:od(e,t,i),state:String(s.state||""),via:n},skipped:null,semantics:o,nextEffect:a.effect,command:{domain:o,service:a.service,data:{entity_id:i}}}}function ud(e){return"entity"===e.bindingKind?e.bindingRef&&ln(e.bindingRef)?[e.bindingRef]:[]:"virtual"===e.bindingKind?[]:_h(e)}function pd(e){const t=e.marker?.toggle_entity;return t&&ud(e).includes(t)?t:null}function md(e,t,i){const n=function(e,t){if("entity"===e.bindingKind&&e.bindingRef){const t=e.marker?.light_entity?fh(e):null;return[...new Set([t,e.bindingRef].filter(e=>!!e))]}const i=e.entities.length?e.entities:e.allEntities||[],n=e.marker?.light_entity||e.primary&&ln(e.primary)?fh(e):null;return[...new Set([n,...hh(t,i)].filter(e=>!!e))]}(i,t);if(!n.length)return null;const r="entity"===i.bindingKind?"binding":"device-role";if("binding"===r)return dd(e,t,n[0],r);let o=null,s=null;for(const i of n){const n=dd(e,t,i,r);if(n.command)return n;const a=n.skipped?.reason;if("missing"===a||"unavailable"===a||"secure"===a)return n;"ha-disabled"!==a?o||=n:s||=n}return o||s}function _d(e){const t=e.skipped?.reason;return"missing"===t?"unavailable":t||"unsupported"}function fd(e,t){return{origin:e,kind:"none",semantics:null,targets:[],skippedTargets:[],noneReason:t,nextEffect:null,command:null}}function gd(e,t){return"cover"===e||"toggle"===e?"toggle":"more-info"===e||"run"===e||"info"===e?e:(null==e||""===e)&&"light"===t?"toggle":"info"}function vd(e,t){return{origin:e,kind:"single",semantics:t.semantics,targets:t.target?[t.target]:[],skippedTargets:t.skipped?[t.skipped]:[],noneReason:t.command?null:_d(t),nextEffect:t.nextEffect,command:t.command}}function yd(e,t,i,n=[]){const r=new Map,o=[],s=new Set,a=e=>{const t=`${e.ref}\n${e.entityId||""}\n${e.reason}`;s.has(t)||(s.add(t),o.push(e))};for(const e of n)a(e);for(const n of i){const i=dd(e,t,n.entityId,n.via,n.ref);i.target?r.set(n.entityId,r.get(n.entityId)||i.target):i.skipped&&a(i.skipped)}const l=[...r.values()];if(!l.length)return{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:l,skippedTargets:o,noneReason:o.length&&o.every(e=>"secure"===e.reason)?"secure":"configured-targets-missing",nextEffect:null,command:null};const c=l.some(e=>"on"===e.state)?"turn_off":"turn_on",h=nd(e,"homeassistant",c);return h?{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:l,skippedTargets:o,noneReason:null,nextEffect:"turn_on"===c?"turn-on":"turn-off",command:{...h,data:{entity_id:l.map(e=>e.entityId)}}}:{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:[],skippedTargets:[...o,...l.map(i=>cd(e,t,i.entityId,i.entityId,"unsupported"))],noneReason:"unsupported",nextEffect:null,command:null}}function bd(e){return String(e.marker?.id||e.id||"")}function wd(e){const{hass:t,device:i}=e,n=e.registryHass||t,r=function(e){return"toggle"===e.tapAction?"explicit-toggle":"cover"===e.tapAction?"legacy-cover":!e.tapAction&&e.primary?.startsWith("light.")?"default-light":null}(i);if(!r)return null;const o=i.marker;if("explicit-toggle"===r&&Oc(o)){const t=String(o?.id||i.id||""),n=kh(e.devices).get(t);if(n)return function(e,t){const i=e.registryHass||e.hass;return yd(e.hass,i,t.driverEids.map(e=>({entityId:e,via:"control-marker-driver",ref:`marker:${t.markerId}`})))}(e,n);const s=Fc(o,e.virtualLights);return{origin:r,kind:"single",semantics:"power",targets:[{entityId:"",name:i.name,state:s?"on":"off",via:"virtual-light"}],skippedTargets:[],noneReason:null,nextEffect:s?"turn-off":"turn-on",command:null,operation:{kind:"virtual-light",markerId:o.id}}}if("explicit-toggle"===r){if(ph(i.marker?.binding,i.marker?.controls??i.controls,i.entities).length)return function(e,t=null){const{hass:i,device:n,devices:r}=e,o=e.registryHass||i,s=ph(n.marker?.binding,n.marker?.controls??n.controls,n.entities),a=e.lightSources||Rh(i,r,null,e.virtualLights),l=new Map;for(const e of a){if(!e.key.startsWith("marker:"))continue;const t=l.get(e.key)||[];t.push(e),l.set(e.key,t)}const c=kh(r),h=new Map;for(const e of r){const t=String(e.marker?.id||e.id||"");t&&h.set(t,e)}const d=[],u=[];t&&d.push({entityId:t,via:"entity"===n.bindingKind?"binding":"device-role",ref:t});for(const e of s){if(!e.startsWith("marker:")){ln(e)?d.push({entityId:e,via:"control-entity",ref:e}):u.push(cd(i,o,e,e.includes(".")?e:null,"unsupported"));continue}const t=l.get(e)||[];if(!t.length){const t=h.get(e.slice(7));if("ha_disabled"===t?.bindingStatus?.kind){const n=t.bindingStatus.allEntityIds[0]||null;u.push({ref:e,entityId:n,name:t.name||(n?od(i,o,n):null),reason:"ha-disabled"})}else u.push(cd(i,o,e,null,"missing"));continue}const r=[...new Set(t.flatMap(e=>e.serviceEids))];if(r.length){for(const t of r)d.push({entityId:t,via:"control-entity",ref:e});continue}const s=t.some(e=>e.passive),a=e.slice(7),p=bd(n),m=c.get(a)?.controllers.find(e=>bd(e.device)===p);if(s&&m?.driverEids.length)for(const t of m.driverEids)d.push({entityId:t,via:"control-marker-driver",ref:e});else u.push(cd(i,o,e,null,s?"missing":"unsupported"))}return yd(i,o,d,u)}(e,pd(i))}if("legacy-cover"===r){const e=i.entities.find(e=>e.startsWith("cover."))||i.allEntities?.find(e=>e.startsWith("cover."));return e?vd(r,dd(t,n,e,"device-role")):fd(r,"no-actionable-entity")}const s=pd(i),a=s?dd(t,n,s,"entity"===i.bindingKind?"binding":"device-role"):md(t,n,i);return a?vd(r,a):fd(r,i.virtual||"virtual"===i.bindingKind?"no-actionable-entity":"no-binding")}function kd(e){if(!e)return[];const t=Array.isArray(e.data.entity_id)?e.data.entity_id:[e.data.entity_id];return[...new Set(t)].sort()}function xd(e){return e?e.operation?e.operation:e.command?{kind:"ha-service",command:e.command}:null:null}const $d=new Set(["missing","ha-disabled","unavailable"]);function Sd(e,t){const i=xd(e),n=xd(t);return!(!i||!n||i.kind!==n.kind)&&("virtual-light"===i.kind&&"virtual-light"===n.kind?i.markerId===n.markerId:"ha-service"===i.kind&&"ha-service"===n.kind&&function(e,t){const i=kd(e),n=kd(t);return i.length===n.length&&i.every((e,t)=>e===n[t])}(i.command,n.command))}function Md(e,t){const i=e.targets[0]||e.skippedTargets[0],n=[];return"group"===e.kind?e.targets.length&&n.push(t.group(e.targets)):i&&n.push(t.single(i)),e.nextEffect&&e.targets.length&&n.push("group"===e.kind?t.groupCurrentNext(e.targets,e.nextEffect):t.currentNext(e.targets[0],e.nextEffect)),e.skippedTargets.length&&n.push(t.skipped(e.skippedTargets)),!e.command&&e.noneReason&&n.push(t.none(e.noneReason)),n}const Rd=e=>!!e&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]);function Td(e,t=5,i=5){return ao(e.cm,t,i)/2}function Cd(e,t,i=1,n=5,r=5,o=0){const s=e.host;if(!s||"partition"!==s.kind||"string"!=typeof s.id||!s.id)return{resolved:null,reason:"invalid-host"};const a=t.find(e=>e.id===s.id);if(!a)return{resolved:null,reason:"missing-partition"};if(!Rd(a.a)||!Rd(a.b)||!Number.isFinite(s.t)||s.t<0||s.t>1)return{resolved:null,reason:"invalid-position"};const l=a.b[0]-a.a[0],c=a.b[1]-a.a[1],h=Math.hypot(l,c),d=Number(e.length)*i;if(!(h>1e-9&&d>0&&Number.isFinite(d)))return{resolved:null,reason:"invalid-length"};const u=s.t*h;if(u-d/2h-o+1e-9)return{resolved:null,reason:"does-not-fit"};const p=l/h,m=c/h;let _=180*Math.atan2(c,l)/Math.PI;return _>=90?_-=180:_<-90&&(_+=180),{reason:null,resolved:{opening:e,host:s,partition:a,center:[a.a[0]+l*s.t,a.a[1]+c*s.t],angle:_,length:d,depth:ao(a.cm,n,r),t:s.t,axis:{a:[a.a[0],a.a[1]],b:[a.b[0],a.b[1]],ux:p,uy:m,length:h}}}}function Dd(e,t,i=1,n=5,r=5){return Cd(e,t,i,n,r,0)}function zd(e,t,i=1,n=5,r=5){const o=e.host,s="partition"===o?.kind?t.find(e=>e.id===o.id):void 0;if(!s)return Dd(e,t,i,n,r);const a=Cd(e,t,i,n,r,Td(s,n,r));return"does-not-fit"===a.reason?{resolved:null,reason:"does-not-fit-jamb"}:a}function Ad(e,t){return!e||(e.length!==t.length||e.host?.kind!==t.host?.kind||e.host?.id!==t.host?.id||e.host?.t!==t.host?.t)}function Pd(e){const{center:t,length:i,axis:n,host:r}=e,o=i/2;return{hostId:r.id,a:[t[0]-n.ux*o,t[1]-n.uy*o],b:[t[0]+n.ux*o,t[1]+n.uy*o],depth:e.depth}}function Od(e,t=!1){const i=t?1:-1,n=e.depth/2;return{ox:-e.axis.uy*i*n,oy:e.axis.ux*i*n,cm:e.partition.cm,side:i}}function Fd(e,t,i){return e.flatMap(e=>!Rd(e.a)||!Rd(e.b)||Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])<=1e-9?[]:[{roomId:"",a:[e.a[0],e.a[1]],b:[e.b[0],e.b[1]],key:`partition:${e.id}`,kind:"outer",cm:e.cm,open:!1,half:ao(e.cm,t,i)/2,partitionHost:{kind:"partition",id:e.id}}])}function Id(e,t,i){const{center:n,length:r,axis:o}=e,s=r/2,a=t.flatMap(e=>{if(!e.kind||e.open)return[];const t=e.b[0]-e.a[0],r=e.b[1]-e.a[1],a=Math.hypot(t,r);if(!(a>i))return[];const l=t/a,c=r/a;if(Math.abs(o.ux*c-o.uy*l)>1e-6)return[];if(Math.abs((n[0]-e.a[0])*c-(n[1]-e.a[1])*l)>i)return[];const h=e=>(e[0]-n[0])*o.ux+(e[1]-n[1])*o.uy,d=h(e.a),u=h(e.b),p=Math.max(-s,Math.min(d,u)),m=Math.min(s,Math.max(d,u));return m>=p-i?[[p,m]]:[]});if(!a.length)return!1;a.sort((e,t)=>e[0]-t[0]||e[1]-t[1]);let l=-s;for(const[e,t]of a){if(e>l+i)return!1;if(l=Math.max(l,t),l>=s-i)return!0}return!1}function Ed(e,t,i=1e-9){const n=e.length/(2*e.axis.length),r=e.t-n,o=e.t+n;return t.some(t=>{if(t.host.id!==e.host.id||t.opening.id===e.opening.id)return!1;const n=t.length/(2*t.axis.length);return Math.max(r,t.t-n)`:V} ${null!=e.lqiText?B`${e.lqiText}`:V} - `}function Xu(e){return!Number.isFinite(e)||e<=0?2.25:e/2.5*2.25}function Zu({openings:e,geometries:t,fillsByRoomId:i,idPrefix:n="data",groupClass:r="opening-tunnels",dataLayer:o="data"}){const s=e.map((e,r)=>{const o=t[r];if(!o)return V;const s=o.faces.find(e=>-1===e.side),a=o.faces.find(e=>1===e.side);if(!s||!a)return V;const l=i.get(s.roomId)||null,c=i.get(a.roomId)||null;if(!l&&!c)return V;const h=`${s.d} ${a.d}`,d=!!l&&!!c&&l.color===c.color&&l.opacity===c.opacity,u=`translate(${e.rx} ${e.ry}) rotate(${e.angle})`;if(d)return j`{const o=t[r];if(!o)return V;const s=o.faces.find(e=>-1===e.side),a=o.faces.find(e=>1===e.side);if(!s||!a)return V;const l=i.get(s.roomId)||null,c=i.get(a.roomId)||null;if(!l&&!c)return V;const h=`${s.d} ${a.d}`,d=!!l&&!!c&&l.color===c.color&&l.opacity===c.opacity,u=`translate(${e.rx} ${e.ry}) rotate(${e.angle})`;if(d)return j``;const p=o.maxY-o.minY;if(!(p>0))return V;const m=`${(100*Math.max(0,Math.min(1,-o.minY/p))).toFixed(6)}%`,_=l||{color:"#000000",opacity:0},f=c||{color:"#000000",opacity:0},g=`hp-opening-tunnel-${n.replace(/[^a-zA-Z0-9_-]/g,"-")}-${r}`;return j` - `});return j``}function Ju(e,t,i){const n=i>0?i:1;return{a:[e[0]/n,e[1]/n],b:[t[0]/n,t[1]/n]}}function Qu(e,t){const i=t>0?t:1;return[e.a[0]*i,e.a[1]*i,e.b[0]*i,e.b[1]*i]}function ep(e){return Array.isArray(e)&&e.length>=2&&Number.isFinite(Number(e[0]))&&Number.isFinite(Number(e[1]))}function tp(e){if(!Array.isArray(e))return[];const t=[];for(const i of e){if(!i||"object"!=typeof i)continue;const e=i;if(!ep(e.a)||!ep(e.b))continue;const n=[Number(e.a[0]),Number(e.a[1])],r=[Number(e.b[0]),Number(e.b[1])];Math.hypot(r[0]-n[0],r[1]-n[1])<.001||t.push({a:n,b:r})}return t}function ip(e,t,i,n,r=!0){const o=(e||[]).filter(e=>e?.id),s=tp(t);if(s.length)return function(e,t,i,n){const r=tp(e);if(!r.length)return[];const o=function(e,t){const i=[],n=(e||[]).filter(e=>e?.id);for(let e=0;e4*n||o>4*n)continue;const s=(e[0]-r)*u+(e[1]-l)*p,c=(e[2]-r)*u+(e[3]-l)*p,h=Math.max(0,Math.min(s,c)),_=Math.min(d,Math.max(s,c));if(_-he.lo-t.lo||e.hi-t.hi);const i=[];for(const e of t){const t=i[i.length-1];t&&e.lo<=t.hi+a?t.hi=Math.max(t.hi,e.hi):i.push({...e})}for(const t of i){const i=[r+u*t.lo,l+p*t.lo],n=[r+u*t.hi,l+p*t.hi];Math.hypot(n[0]-i[0],n[1]-i[1])i.pair===e&&Math.abs(i.ux*s-i.uy*o)<=1e-6&&Math.abs((t[0]-i.origin[0])*i.uy-(t[1]-i.origin[1])*i.ux)<=c&&Math.abs((t[2]-i.origin[0])*i.uy-(t[3]-i.origin[1])*i.ux)<=c);h||(h={pair:e,origin:[t[0],t[1]],ux:o,uy:s,ranges:[]},l.push(h));const d=(t[0]-h.origin[0])*h.ux+(t[1]-h.origin[1])*h.uy,u=(t[2]-h.origin[0])*h.ux+(t[3]-h.origin[1])*h.uy;h.ranges.push({lo:Math.min(d,u),hi:Math.max(d,u)})}const h=[];for(const e of l){e.ranges.sort((e,t)=>e.lo-t.lo||e.hi-t.hi);const t=[];for(const i of e.ranges){const e=t[t.length-1];e&&i.lo<=e.hi+a?e.hi=Math.max(e.hi,i.hi):t.push({...i})}for(const n of t){const t=[e.origin[0]+e.ux*n.lo,e.origin[1]+e.uy*n.lo],r=[e.origin[0]+e.ux*n.hi,e.origin[1]+e.uy*n.hi];Math.hypot(r[0]-t[0],r[1]-t[1])>=a&&h.push(Ju(t,r,i))}}return h}(s,e,i,n).map(e=>Qu(e,i));if(!r)return[];const a=[],l=(e,t)=>(e.open_to||[]).includes(t.id)||(t.open_to||[]).includes(e.id);for(let e=0;er)&&xo([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}const rp=e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(Number(e[0]))&&Number.isFinite(Number(e[1])),op=(e,t,i)=>[Number(e[0])*i,Number(e[1])*i,Number(t[0])*i,Number(t[1])*i],sp=e=>{const t=`${Number(e[0]).toFixed(9)},${Number(e[1]).toFixed(9)}`,i=`${Number(e[2]).toFixed(9)},${Number(e[3]).toFixed(9)}`;return t<=i?`${t}|${i}`:`${i}|${t}`},ap=e=>{const t=new Map;for(const i of e.flat())!Array.isArray(i)||i.length<4||!i.every(Number.isFinite)||Math.hypot(i[2]-i[0],i[3]-i[1])<=1e-9||t.set(sp(i),[...i]);return[...t.values()]};function lp(e){return"solid"===e?.zero_wall_style?"solid":"dashed"}function cp(e,t=1,i){const n=[];for(const r of Array.isArray(e?.wall_segments)?e.wall_segments:[])0===Number(r?.cm)&&rp(r?.a)&&rp(r?.b)&&(i&&!i.has(String(r.id||""))||n.push(op(r.a,r.b,t)));return ap([n])}function hp(e,t=1){const i=[];for(const n of Array.isArray(e?.partitions)?e.partitions:[])0===Number(n?.cm)&&rp(n?.a)&&rp(n?.b)&&i.push(op(n.a,n.b,t));for(const n of Array.isArray(e?.room_drafts)?e.room_drafts:[]){const e=Array.isArray(n?.points)?n.points:[];for(let r=0;r+1e?.host?.kind===t.kind&&e.host.id===t.id)}function mp(e){return e instanceof Error?e.name||"Error":typeof e}function _p(e){return Rl({id:e?.id??"",cell_cm:e?.cell_cm,rooms:e?.rooms||[],walls:e?.walls||[],wall_segments:e?.wall_segments||[],open_spans:e?.open_spans||[],openings:e?.openings||[],partitions:e?.partitions||[],room_drafts:e?.room_drafts||[],wall_columns:e?.wall_columns||[]})}function fp(e,t,i={}){const n=(Array.isArray(e?.spaces)?e.spaces:[]).find(e=>String(e?.id||"")===String(t||"")),r=_p(n);if(!n)return{spaceId:String(t||""),displayName:"",status:"failed",reason:"prepare-exception",fingerprint:r,ok:!1};const o=kp({...e,spaces:[n]},{...i,fingerprint:()=>r}).spaces[0]||{spaceId:String(t||""),displayName:"",status:"failed",reason:"prepare-exception"};return{...o,fingerprint:r,ok:"failed"!==o.status}}function gp(e,t,i,n=Ta,r=1e3){return(Array.isArray(e?.openings)?e.openings:[]).flatMap(e=>{const o={...e,rx:Number(e.x)*r,ry:Number(e.y)*r,rlen:Number(e.length)*r};if(!e.host||"wall"===e.host.kind)return[o];const s=Dd(e,t.partitions,r,i,n);return s.resolved?[{...e,rx:s.resolved.center[0],ry:s.resolved.center[1],rlen:s.resolved.length,angle:s.resolved.angle,partitionHost:s.resolved}]:[]})}function vp(e,t=()=>!0){return e.flatMap(e=>e.host&&e.partitionHost&&t(e)?[Pd(e.partitionHost)]:[])}function yp(e,t,i,n,r,o,s=Ta,a=1e3){const l=ss(t.rooms,[...i],n,r,o,s,a);return e.flatMap(e=>{const t={x:e.rx,y:e.ry,angle:Number(e.angle)||0,length:e.rlen};return e.host&&"wall"!==e.host.kind?e.partitionHost&&Id(e.partitionHost,l,2e-4*s)?[t]:[]:[t]})}function bp(e,t){const i=Ca,n=Ta,r=va,o=Number.isFinite(Number(t.cellCm))&&Number(t.cellCm)>0?Number(t.cellCm):5,s=Array.isArray(e?.walls)?e.walls:[],a=function(e,t,i=Ta,n=1e3){return ip(t.rooms,e?.open_spans,n,.02*i)}(e,t,n,r),l=function(e,t,i=Ta){if(!t.length)return[];const n=e.filter(e=>e?.id),r=.02*i,o=[];for(let e=0;e{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return h.some(e=>xn(t,e)<4*r)}).map(e=>[...e]);d.length&&o.push({a:s,b:a,segs:d})}return o}(t.rooms,a,n).flatMap(e=>e.segs),c=gp(e,t,o,n,r),h=vp(c),d=ea(t,o,n,2e-4*n,h).all;return{space:t,walls:s,openCuts:l,openings:c,roomOpenings:yp(c,t,s,l,i,o,n,r),partitionCuts:h,physicalBodies:d,wallKeyPitch:i,cellCm:o,gridPitch:n,coordScale:r}}function wp(e,t,i){const n="string"==typeof e?.title?e.title.trim():"",r=null==e?.id?"":String(e.id).trim();return{spaceId:r,displayName:n||r||i(t+1)}}function kp(e,t={}){const i=(t.fingerprint||Rl)(e),n=Array.isArray(e?.spaces)?e.spaces:[],r=t.fallbackSpaceName||(e=>`Space ${e}`),o=t.prepareSpace||bp,s=t.wallPass||Ts,a=t.floorPass||$s,l=[];for(let i=0;i0||d.physicalBodies.length>0;if(!d.space.rooms.length&&!u){l.push({...h,status:"not-applicable"});continue}let p=null;if(u){try{p=s(d.space.rooms,d.walls,d.openCuts,d.roomOpenings,d.wallKeyPitch,d.cellCm,d.gridPitch,d.coordScale,d.physicalBodies)}catch(e){l.push({...h,status:"failed",reason:"wall-exception",detail:mp(e)});continue}if(null==p){l.push({...h,status:"failed",reason:"wall-null"});continue}if("degraded-extra"===p.status){l.push({...h,status:"failed",reason:"wall-degraded-extra"});continue}if("failed-core"===p.status){l.push({...h,status:"failed",reason:"wall-failed-core"});continue}t.captureWallGeometry?.(d,p)}if(d.space.rooms.length&&null==p?.paperGeom){let e;try{e=a(d.space.rooms,d.walls,d.openCuts,d.wallKeyPitch,d.cellCm,d.gridPitch,d.coordScale)}catch(e){l.push({...h,status:"failed",reason:"floor-exception",detail:mp(e)});continue}if(null==e){l.push({...h,status:"failed",reason:"floor-null"});continue}}l.push({...h,status:"ok"})}const c=l.filter(e=>"failed"===e.status);return{fingerprint:i,spaces:l,failures:c,ok:0===c.length}}const xp=2,$p=e=>(...t)=>({_$litDirective$:e,values:t});let Sp=class{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,i){this._$Ct=e,this._$AM=t,this._$Ci=i}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}};const{I:Mp}=se,Rp=e=>e,Tp=()=>document.createComment(""),Cp=(e,t,i)=>{const n=e._$AA.parentNode,r=void 0===t?e._$AB:t._$AA;if(void 0===i){const t=n.insertBefore(Tp(),r),o=n.insertBefore(Tp(),r);i=new Mp(t,o,e,e.options)}else{const t=i._$AB.nextSibling,o=i._$AM,s=o!==e;if(s){let t;i._$AQ?.(e),i._$AM=e,void 0!==i._$AP&&(t=e._$AU)!==o._$AU&&i._$AP(t)}if(t!==r||s){let e=i._$AA;for(;e!==t;){const t=Rp(e).nextSibling;Rp(n).insertBefore(e,r),e=t}}}return i},Dp=(e,t,i=e)=>(e._$AI(t,i),e),zp={},Ap=(e,t=zp)=>e._$AH=t,Pp=e=>{e._$AR(),e._$AA.remove()},Op=(e,t,i)=>{const n=new Map;for(let r=t;r<=i;r++)n.set(e[r],r);return n},Fp=$p(class extends Sp{constructor(e){if(super(e),e.type!==xp)throw Error("repeat() can only be used in text expressions")}dt(e,t,i){let n;void 0===i?i=t:void 0!==t&&(n=t);const r=[],o=[];let s=0;for(const t of e)r[s]=n?n(t,s):s,o[s]=i(t,s),s++;return{values:o,keys:r}}render(e,t,i){return this.dt(e,t,i).values}update(e,[t,i,n]){const r=(e=>e._$AH)(e),{values:o,keys:s}=this.dt(t,i,n);if(!Array.isArray(r))return this.ut=s,o;const a=this.ut??=[],l=[];let c,h,d=0,u=r.length-1,p=0,m=o.length-1;for(;d<=u&&p<=m;)if(null===r[d])d++;else if(null===r[u])u--;else if(a[d]===s[p])l[p]=Dp(r[d],o[p]),d++,p++;else if(a[u]===s[m])l[m]=Dp(r[u],o[m]),u--,m--;else if(a[d]===s[m])l[m]=Dp(r[d],o[m]),Cp(e,l[m+1],r[d]),d++,m--;else if(a[u]===s[p])l[p]=Dp(r[u],o[p]),Cp(e,r[d],r[u]),u--,p++;else if(void 0===c&&(c=Op(s,p,m),h=Op(a,d,u)),c.has(a[d]))if(c.has(a[u])){const t=h.get(s[p]),i=void 0!==t?r[t]:null;if(null===i){const t=Cp(e,r[d]);Dp(t,o[p]),l[p]=t}else l[p]=Dp(i,o[p]),Cp(e,r[d],i),r[t]=null;p++}else Pp(r[u]),u--;else Pp(r[d]),d++;for(;p<=m;){const t=Cp(e,l[m+1]);Dp(t,o[p]),l[p++]=t}for(;d<=u;){const e=r[d++];null!==e&&Pp(e)}return this.ut=s,Ap(e,l),U}}),Ip=1e-5;function Ep(e){const t=e.map(()=>[]);for(let i=0;i=1-1e-9||u<=1e-9||u>=1-1e-9||(t[i].push(d),t[s].push(u))}}const i=[];for(let n=0;ne-t),1];for(let e=1;e{const i=t[2]-t[0],n=t[3]-t[1],r=i*i+n*n;if(!(r>0))return Math.hypot(e[0]-t[0],e[1]-t[1]);const o=Math.max(0,Math.min(1,((e[0]-t[0])*i+(e[1]-t[1])*n)/r));return Math.hypot(e[0]-(t[0]+o*i),e[1]-(t[1]+o*n))},Lp=(e,t,i,n)=>{const r=n[2]-n[0],o=n[3]-n[1],s=t*o-i*r;if(Math.abs(s)<1e-12)return 1/0;const a=n[0]-e[0],l=n[1]-e[1],c=(a*o-l*r)/s;if(!(c>1e-9))return 1/0;const h=(a*i-l*t)/s;return h<-1e-9||h>1+1e-9?1/0:c};const Wp=[[0,1],[45,.88],[70,.62],[86,.32],[100,0]];function qp(){return{clipCache:new Map,geometryWarnings:new Set,featherUnits:null,renderedSources:new Map,lastAppearance:new Map,enteringSources:new Set,enterRafs:new Map,fadeTimers:new Map,featherSuspendUntil:0,featherResumeTimer:0,sourceSeq:0}}function Bp(e,t){if(!e.clipCache.has(t))return{hit:!1,value:null};const i=e.clipCache.get(t)??null;return e.clipCache.delete(t),e.clipCache.set(t,i),{hit:!0,value:i}}function jp(e,t,i,n=256){for(e.clipCache.delete(t),e.clipCache.set(t,i);e.clipCache.size>n;){const t=e.clipCache.keys().next().value;if(void 0===t)break;e.clipCache.delete(t)}}function Up(e){const t=Rh(e.hass,e.devices,null,e.virtualLights).filter(t=>t.device.space===e.spaceId),i=new Map;for(const e of t){if(!e.device.id)continue;const t=i.get(e.device.id)||[];t.push(e),i.set(e.device.id,t)}const n=[];for(const t of e.devices){if(!t.id||t.space!==e.spaceId)continue;const r=Th(i.get(t.id)||[]);if(!r)continue;const o=sn(r.passive?{state:r.on?"on":"off",attributes:{}}:e.hass.states[r.eid],t.marker?.glow_color,e.defaultColor),s=Number(t.marker?.glow_radius_cm),a=Number.isFinite(s)&&s>0?s/e.cellCm*e.gridPitch:e.defaultRadiusUnits;n.push({key:`${e.spaceId}|${t.id}`,sourceEid:r.eid,pos:e.position(t),radius:a,appearance:o?{c:o.c,alpha:an(o.bri,e.paletteAlpha)}:null})}return n}function Vp(e){const t=Rl([e.rawSpaceConfig,e.cellCm,e.gridPitch]),i=e.space.rooms.flatMap(e=>{const t=Et(e);return t?[{room:e,poly:t}]:[]}),n=Math.max(10/e.cellCm*e.gridPitch,.5*e.gridPitch),r=e=>i.some(({poly:t})=>Yt(e,t)),o=e.openings.flatMap(t=>{if("door"!==(i=String(t.type))&&"gate"!==i&&"passage"!==i)return[];var i;const o=t.angle*Math.PI/180,s=-Math.sin(o),a=Math.cos(o);return r([t.rx+s*n,t.ry+a*n])&&r([t.rx-s*n,t.ry-a*n])?[{opening:t,amount:jt(e.openingAmount(t))}]:[]});return{geometryFingerprint:t,fingerprint:Rl([t,o.map(({opening:e,amount:t})=>({id:e.id,type:e.type,contact:e.contact,amount:t})).filter(e=>!!e.contact&&("door"===e.type||"gate"===e.type)).map(e=>{const t=Math.max(0,Math.min(1,Number(e.amount)||0));return`${e.id}:${t.toFixed(3)}`}).sort().join("|")]),polygons:i,passageStates:o}}function Gp(e){const{revision:t}=e,i=`${e.space.id}|${t.fingerprint}`,n=[...e.zeroWalls.transmissive],r=yp(t.passageStates.filter(({amount:e})=>e>0).map(({opening:e,amount:t})=>({...e,rlen:Ut(e.rlen,t)})),e.space,[...e.walls],e.zeroWalls.contour,e.wallKeyPitch,e.cellCm,e.gridPitch,e.coordScale);for(const e of r){const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,r=Math.sin(t)*e.length/2;n.push([e.x-i,e.y-r,e.x+i,e.y+r])}const o=t.passageStates.flatMap(({opening:e,amount:t})=>t>0&&e.partitionHost?[Zs(Pd(e.partitionHost),t)]:[]),s=e.physicalBodies(o,i),a=[],l=e.sharedWallGeometry?.sourceFingerprint,c=e.sharedWallGeometry&&l===t.geometryFingerprint?function(e,t=[],i=[],n={}){if("failed-core"===e.status||"not-applicable"===e.status)return null;const r=e.roomComponents?.length?e.roomComponents:ms(e.roomGeom)?[{id:"room-primary",geom:e.roomGeom}]:[];let o=r[0]?.geom||null;const s=r.slice(1).map(e=>({...e}));try{for(const i of t){if(!(i.length>0&&e.openingIndex))continue;const t=Is(e.openingIndex,i,!0);if(!t.negative&&!t.positive)continue;const n=i.angle*Math.PI/180,r=Math.cos(n),a=Math.sin(n),l=-a,c=r,h=i.length/2,d=e.openingPadUnits??1.25*Math.max(e.depthUnits,1),u=ps([[i.x-r*h-l*d,i.y-a*h-c*d],[i.x+r*h-l*d,i.y+a*h-c*d],[i.x+r*h+l*d,i.y+a*h+c*d],[i.x-r*h+l*d,i.y-a*h+c*d]]);o&&(o=Mt(o,u));for(const e of s)e.geom=Mt(e.geom,u)}const r=[...s];let a=s.length;const l=n.mergeExtra||((e,t)=>e?$t(e,t):t);for(let e=0;ee.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]))||Math.abs(li(t))<=1e-9){a++;continue}const n=[ps(t)];try{const t=l(o,n,e);if(!ms(t))throw new Error("invalid extra union");o=t}catch{a++,ms(n)&&r.push({id:`policy-extra-${e}`,geom:n})}}r.sort((e,t)=>Ss(e.geom).localeCompare(Ss(t.geom)));const c=o||[];return{status:a?"degraded-extra":"ok",geom:c,components:[...ms(c)?[{id:"primary",geom:c}]:[],...r.map((e,t)=>({...e,id:`isolated-${t}`}))]}}catch{return null}}(e.sharedWallGeometry,r,s):null,h=c||(e.walls.length||s.length?Ts(e.space.rooms,[...e.walls],e.zeroWalls.contour,r,e.wallKeyPitch,e.cellCm,e.gridPitch,e.coordScale,s):null);if(!h||"ok"!==h.status&&"degraded-extra"!==h.status)for(const e of s)a.push(...Hp(e));else for(const e of h.components)for(const t of aa(e.geom))a.push(...Hp(t));const d=.02*e.gridPitch;for(const{poly:e}of t.polygons){const t=n.length?dn(e,n,d):Hp(e);for(const e of t)a.push(e)}for(const t of e.zeroWalls.barriers)a.push(t);return{occluders:Ep(a),floor:t.polygons.map(({poly:e})=>e),fingerprint:t.fingerprint,masonryGeometry:!h||"ok"!==h.status&&"degraded-extra"!==h.status?[]:h.components.flatMap(e=>e.geom),opaqueBodies:s}}function Kp(e){const t=function(e,t,i,n=96){if(!(t>0&&Number.isFinite(e[0])&&Number.isFinite(e[1])))return[];const r=[];for(const n of i){if(!n||n.length<4)continue;if(![n[0],n[1],n[2],n[3]].every(Number.isFinite))continue;const i=Np(e,n);if(i<1e-7)return[];i>t||r.push(n)}const o=[],s=Math.max(12,Math.round(n));for(let e=0;ee-t);const l=[];let c=Number.NEGATIVE_INFINITY;for(const i of o){if(i-c<1e-9)continue;c=i;const n=Math.cos(i),o=Math.sin(i);let s=t;for(const t of r){const i=Lp(e,n,o,t);i=3?l:[]}([e.source.x,e.source.y],e.radius,e.scene.occluders,96);return{lit:t.length>=3?ra([t],e.scene.floor,{onBoundsFailure:({boundIndex:t,phase:i})=>{const n=e.polygons[t]?.room;e.onBoundsFailure?.(n?.id||`#${t}`,i)}}):[]}}function Yp(e,t){return ha([e.x,e.y],t.masonryGeometry,t.opaqueBodies)}function Xp(e,t){if(t.reducedMotion())return;const i=t.window();e.featherSuspendUntil=Math.max(e.featherSuspendUntil,Date.now()+500),i.clearTimeout(e.featherResumeTimer);const n=()=>{e.featherResumeTimer=0,Date.now(){e.enterRafs.get(i)===n&&(e.enterRafs.delete(i),e.enteringSources.delete(i),t.isConnected()&&t.requestUpdate())});e.enterRafs.set(i,n)}return{domId:o,entering:e.enteringSources.has(i),leaving:!1}}if(null==o)return null;const s=e.enterRafs.get(i);if(null!=s&&r.cancelAnimationFrame(s),e.enterRafs.delete(i),e.enteringSources.delete(i),!e.fadeTimers.has(i)){Xp(e,t);const n=r.setTimeout(()=>{e.fadeTimers.get(i)===n&&(e.fadeTimers.delete(i),e.renderedSources.delete(i),e.lastAppearance.delete(i),t.isConnected()&&t.requestUpdate())},534);e.fadeTimers.set(i,n)}return{domId:o,entering:!1,leaving:!0}}function Jp(e,t,i){const n=t.window(),r=e.fadeTimers.get(i),o=e.enterRafs.get(i);null!=r&&n.clearTimeout(r),null!=o&&n.cancelAnimationFrame(o),e.fadeTimers.delete(i),e.enterRafs.delete(i),e.enteringSources.delete(i),e.renderedSources.delete(i),e.lastAppearance.delete(i)}function Qp(e,t,i){const n=`${i}|`;for(const i of[...e.renderedSources.keys()])i.startsWith(n)&&Jp(e,t,i)}function em(e,t,i,n){const r=`${i}|`;for(const i of[...e.renderedSources.keys()])i.startsWith(r)&&!n.has(i)&&Jp(e,t,i)}function tm(e,t){const i=t.window();for(const t of e.fadeTimers.values())i.clearTimeout(t);for(const t of e.enterRafs.values())i.cancelAnimationFrame(t);i.clearTimeout(e.featherResumeTimer),e.clipCache.clear(),e.geometryWarnings.clear(),e.fadeTimers.clear(),e.enterRafs.clear(),e.enteringSources.clear(),e.renderedSources.clear(),e.lastAppearance.clear(),e.featherUnits=null,e.featherSuspendUntil=0,e.featherResumeTimer=0,e.sourceSeq=0}function im(e,t,i,n,r){const o=`${t}|${i}|${n}`;if(!e.geometryWarnings.has(o)){if(e.geometryWarnings.size>=128){const t=e.geometryWarnings.values().next().value;t&&e.geometryWarnings.delete(t)}e.geometryWarnings.add(o),console.warn(`HOUSEPLAN GLOW GEOMETRY FALLBACK: #218, space ${t}, room ${n}, phase ${r}`)}}function nm(e,t,i){const n=1/(t>0?t:1),r=i&&Date.now()>=e.featherSuspendUntil;return(null==e.featherUnits||r)&&(e.featherUnits=n),{feather:e.featherUnits??n,enabled:r}}function rm(e){if(!e.spots.length)return j``;const t=4*e.feather,i=e.spots.reduce((e,i)=>({x:Math.min(e.x,i.pos.x-i.r-t),y:Math.min(e.y,i.pos.y-i.r-t),maxX:Math.max(e.maxX,i.pos.x+i.r+t),maxY:Math.max(e.maxY,i.pos.y+i.r+t),w:0,h:0}),{x:1/0,y:1/0,maxX:-1/0,maxY:-1/0,w:0,h:0});i.w=i.maxX-i.x,i.h=i.maxY-i.y;const n=e.enabledClip?.length?e.enabledClip:null;return j` - ${Fp(e.spots,e=>e.key,e=>{const t=e.domId;return j` + `});return j``}function Qu(e,t,i){const n=i>0?i:1;return{a:[e[0]/n,e[1]/n],b:[t[0]/n,t[1]/n]}}function ep(e,t){const i=t>0?t:1;return[e.a[0]*i,e.a[1]*i,e.b[0]*i,e.b[1]*i]}function tp(e){return Array.isArray(e)&&e.length>=2&&Number.isFinite(Number(e[0]))&&Number.isFinite(Number(e[1]))}function ip(e){if(!Array.isArray(e))return[];const t=[];for(const i of e){if(!i||"object"!=typeof i)continue;const e=i;if(!tp(e.a)||!tp(e.b))continue;const n=[Number(e.a[0]),Number(e.a[1])],r=[Number(e.b[0]),Number(e.b[1])];Math.hypot(r[0]-n[0],r[1]-n[1])<.001||t.push({a:n,b:r})}return t}function np(e,t,i,n,r=!0){const o=(e||[]).filter(e=>e?.id),s=ip(t);if(s.length)return function(e,t,i,n){const r=ip(e);if(!r.length)return[];const o=function(e,t){const i=[],n=(e||[]).filter(e=>e?.id);for(let e=0;e4*n||o>4*n)continue;const s=(e[0]-r)*u+(e[1]-l)*p,c=(e[2]-r)*u+(e[3]-l)*p,h=Math.max(0,Math.min(s,c)),_=Math.min(d,Math.max(s,c));if(_-he.lo-t.lo||e.hi-t.hi);const i=[];for(const e of t){const t=i[i.length-1];t&&e.lo<=t.hi+a?t.hi=Math.max(t.hi,e.hi):i.push({...e})}for(const t of i){const i=[r+u*t.lo,l+p*t.lo],n=[r+u*t.hi,l+p*t.hi];Math.hypot(n[0]-i[0],n[1]-i[1])i.pair===e&&Math.abs(i.ux*s-i.uy*o)<=1e-6&&Math.abs((t[0]-i.origin[0])*i.uy-(t[1]-i.origin[1])*i.ux)<=c&&Math.abs((t[2]-i.origin[0])*i.uy-(t[3]-i.origin[1])*i.ux)<=c);h||(h={pair:e,origin:[t[0],t[1]],ux:o,uy:s,ranges:[]},l.push(h));const d=(t[0]-h.origin[0])*h.ux+(t[1]-h.origin[1])*h.uy,u=(t[2]-h.origin[0])*h.ux+(t[3]-h.origin[1])*h.uy;h.ranges.push({lo:Math.min(d,u),hi:Math.max(d,u)})}const h=[];for(const e of l){e.ranges.sort((e,t)=>e.lo-t.lo||e.hi-t.hi);const t=[];for(const i of e.ranges){const e=t[t.length-1];e&&i.lo<=e.hi+a?e.hi=Math.max(e.hi,i.hi):t.push({...i})}for(const n of t){const t=[e.origin[0]+e.ux*n.lo,e.origin[1]+e.uy*n.lo],r=[e.origin[0]+e.ux*n.hi,e.origin[1]+e.uy*n.hi];Math.hypot(r[0]-t[0],r[1]-t[1])>=a&&h.push(Qu(t,r,i))}}return h}(s,e,i,n).map(e=>ep(e,i));if(!r)return[];const a=[],l=(e,t)=>(e.open_to||[]).includes(t.id)||(t.open_to||[]).includes(e.id);for(let e=0;er)&&xo([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}const op=e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(Number(e[0]))&&Number.isFinite(Number(e[1])),sp=(e,t,i)=>[Number(e[0])*i,Number(e[1])*i,Number(t[0])*i,Number(t[1])*i],ap=e=>{const t=`${Number(e[0]).toFixed(9)},${Number(e[1]).toFixed(9)}`,i=`${Number(e[2]).toFixed(9)},${Number(e[3]).toFixed(9)}`;return t<=i?`${t}|${i}`:`${i}|${t}`},lp=e=>{const t=new Map;for(const i of e.flat())!Array.isArray(i)||i.length<4||!i.every(Number.isFinite)||Math.hypot(i[2]-i[0],i[3]-i[1])<=1e-9||t.set(ap(i),[...i]);return[...t.values()]};function cp(e){return"solid"===e?.zero_wall_style?"solid":"dashed"}function hp(e,t=1,i){const n=[];for(const r of Array.isArray(e?.wall_segments)?e.wall_segments:[])0===Number(r?.cm)&&op(r?.a)&&op(r?.b)&&(i&&!i.has(String(r.id||""))||n.push(sp(r.a,r.b,t)));return lp([n])}function dp(e,t=1){const i=[];for(const n of Array.isArray(e?.partitions)?e.partitions:[])0===Number(n?.cm)&&op(n?.a)&&op(n?.b)&&i.push(sp(n.a,n.b,t));for(const n of Array.isArray(e?.room_drafts)?e.room_drafts:[]){const e=Array.isArray(n?.points)?n.points:[];for(let r=0;r+1e?.host?.kind===t.kind&&e.host.id===t.id)}function _p(e){return e instanceof Error?e.name||"Error":typeof e}function fp(e){return Rl({id:e?.id??"",cell_cm:e?.cell_cm,rooms:e?.rooms||[],walls:e?.walls||[],wall_segments:e?.wall_segments||[],open_spans:e?.open_spans||[],openings:e?.openings||[],partitions:e?.partitions||[],room_drafts:e?.room_drafts||[],wall_columns:e?.wall_columns||[]})}function gp(e,t,i={}){const n=(Array.isArray(e?.spaces)?e.spaces:[]).find(e=>String(e?.id||"")===String(t||"")),r=fp(n);if(!n)return{spaceId:String(t||""),displayName:"",status:"failed",reason:"prepare-exception",fingerprint:r,ok:!1};const o=xp({...e,spaces:[n]},{...i,fingerprint:()=>r}).spaces[0]||{spaceId:String(t||""),displayName:"",status:"failed",reason:"prepare-exception"};return{...o,fingerprint:r,ok:"failed"!==o.status}}function vp(e,t,i,n=Ta,r=1e3){return(Array.isArray(e?.openings)?e.openings:[]).flatMap(e=>{const o={...e,rx:Number(e.x)*r,ry:Number(e.y)*r,rlen:Number(e.length)*r};if(!e.host||"wall"===e.host.kind)return[o];const s=Dd(e,t.partitions,r,i,n);return s.resolved?[{...e,rx:s.resolved.center[0],ry:s.resolved.center[1],rlen:s.resolved.length,angle:s.resolved.angle,partitionHost:s.resolved}]:[]})}function yp(e,t=()=>!0){return e.flatMap(e=>e.host&&e.partitionHost&&t(e)?[Pd(e.partitionHost)]:[])}function bp(e,t,i,n,r,o,s=Ta,a=1e3){const l=ss(t.rooms,[...i],n,r,o,s,a);return e.flatMap(e=>{const t={x:e.rx,y:e.ry,angle:Number(e.angle)||0,length:e.rlen};return e.host&&"wall"!==e.host.kind?e.partitionHost&&Id(e.partitionHost,l,2e-4*s)?[t]:[]:[t]})}function wp(e,t){const i=Ca,n=Ta,r=va,o=Number.isFinite(Number(t.cellCm))&&Number(t.cellCm)>0?Number(t.cellCm):5,s=Array.isArray(e?.walls)?e.walls:[],a=function(e,t,i=Ta,n=1e3){return np(t.rooms,e?.open_spans,n,.02*i)}(e,t,n,r),l=function(e,t,i=Ta){if(!t.length)return[];const n=e.filter(e=>e?.id),r=.02*i,o=[];for(let e=0;e{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return h.some(e=>xn(t,e)<4*r)}).map(e=>[...e]);d.length&&o.push({a:s,b:a,segs:d})}return o}(t.rooms,a,n).flatMap(e=>e.segs),c=vp(e,t,o,n,r),h=yp(c),d=ea(t,o,n,2e-4*n,h).all;return{space:t,walls:s,openCuts:l,openings:c,roomOpenings:bp(c,t,s,l,i,o,n,r),partitionCuts:h,physicalBodies:d,wallKeyPitch:i,cellCm:o,gridPitch:n,coordScale:r}}function kp(e,t,i){const n="string"==typeof e?.title?e.title.trim():"",r=null==e?.id?"":String(e.id).trim();return{spaceId:r,displayName:n||r||i(t+1)}}function xp(e,t={}){const i=(t.fingerprint||Rl)(e),n=Array.isArray(e?.spaces)?e.spaces:[],r=t.fallbackSpaceName||(e=>`Space ${e}`),o=t.prepareSpace||wp,s=t.wallPass||Ts,a=t.floorPass||$s,l=[];for(let i=0;i0||d.physicalBodies.length>0;if(!d.space.rooms.length&&!u){l.push({...h,status:"not-applicable"});continue}let p=null;if(u){try{p=s(d.space.rooms,d.walls,d.openCuts,d.roomOpenings,d.wallKeyPitch,d.cellCm,d.gridPitch,d.coordScale,d.physicalBodies)}catch(e){l.push({...h,status:"failed",reason:"wall-exception",detail:_p(e)});continue}if(null==p){l.push({...h,status:"failed",reason:"wall-null"});continue}if("degraded-extra"===p.status){l.push({...h,status:"failed",reason:"wall-degraded-extra"});continue}if("failed-core"===p.status){l.push({...h,status:"failed",reason:"wall-failed-core"});continue}t.captureWallGeometry?.(d,p)}if(d.space.rooms.length&&null==p?.paperGeom){let e;try{e=a(d.space.rooms,d.walls,d.openCuts,d.wallKeyPitch,d.cellCm,d.gridPitch,d.coordScale)}catch(e){l.push({...h,status:"failed",reason:"floor-exception",detail:_p(e)});continue}if(null==e){l.push({...h,status:"failed",reason:"floor-null"});continue}}l.push({...h,status:"ok"})}const c=l.filter(e=>"failed"===e.status);return{fingerprint:i,spaces:l,failures:c,ok:0===c.length}}const $p=2,Sp=e=>(...t)=>({_$litDirective$:e,values:t});let Mp=class{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,i){this._$Ct=e,this._$AM=t,this._$Ci=i}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}};const{I:Rp}=se,Tp=e=>e,Cp=()=>document.createComment(""),Dp=(e,t,i)=>{const n=e._$AA.parentNode,r=void 0===t?e._$AB:t._$AA;if(void 0===i){const t=n.insertBefore(Cp(),r),o=n.insertBefore(Cp(),r);i=new Rp(t,o,e,e.options)}else{const t=i._$AB.nextSibling,o=i._$AM,s=o!==e;if(s){let t;i._$AQ?.(e),i._$AM=e,void 0!==i._$AP&&(t=e._$AU)!==o._$AU&&i._$AP(t)}if(t!==r||s){let e=i._$AA;for(;e!==t;){const t=Tp(e).nextSibling;Tp(n).insertBefore(e,r),e=t}}}return i},zp=(e,t,i=e)=>(e._$AI(t,i),e),Ap={},Pp=(e,t=Ap)=>e._$AH=t,Op=e=>{e._$AR(),e._$AA.remove()},Fp=(e,t,i)=>{const n=new Map;for(let r=t;r<=i;r++)n.set(e[r],r);return n},Ip=Sp(class extends Mp{constructor(e){if(super(e),e.type!==$p)throw Error("repeat() can only be used in text expressions")}dt(e,t,i){let n;void 0===i?i=t:void 0!==t&&(n=t);const r=[],o=[];let s=0;for(const t of e)r[s]=n?n(t,s):s,o[s]=i(t,s),s++;return{values:o,keys:r}}render(e,t,i){return this.dt(e,t,i).values}update(e,[t,i,n]){const r=(e=>e._$AH)(e),{values:o,keys:s}=this.dt(t,i,n);if(!Array.isArray(r))return this.ut=s,o;const a=this.ut??=[],l=[];let c,h,d=0,u=r.length-1,p=0,m=o.length-1;for(;d<=u&&p<=m;)if(null===r[d])d++;else if(null===r[u])u--;else if(a[d]===s[p])l[p]=zp(r[d],o[p]),d++,p++;else if(a[u]===s[m])l[m]=zp(r[u],o[m]),u--,m--;else if(a[d]===s[m])l[m]=zp(r[d],o[m]),Dp(e,l[m+1],r[d]),d++,m--;else if(a[u]===s[p])l[p]=zp(r[u],o[p]),Dp(e,r[d],r[u]),u--,p++;else if(void 0===c&&(c=Fp(s,p,m),h=Fp(a,d,u)),c.has(a[d]))if(c.has(a[u])){const t=h.get(s[p]),i=void 0!==t?r[t]:null;if(null===i){const t=Dp(e,r[d]);zp(t,o[p]),l[p]=t}else l[p]=zp(i,o[p]),Dp(e,r[d],i),r[t]=null;p++}else Op(r[u]),u--;else Op(r[d]),d++;for(;p<=m;){const t=Dp(e,l[m+1]);zp(t,o[p]),l[p++]=t}for(;d<=u;){const e=r[d++];null!==e&&Op(e)}return this.ut=s,Pp(e,l),U}}),Ep=1e-5;function Hp(e){const t=e.map(()=>[]);for(let i=0;i=1-1e-9||u<=1e-9||u>=1-1e-9||(t[i].push(d),t[s].push(u))}}const i=[];for(let n=0;ne-t),1];for(let e=1;e{const i=t[2]-t[0],n=t[3]-t[1],r=i*i+n*n;if(!(r>0))return Math.hypot(e[0]-t[0],e[1]-t[1]);const o=Math.max(0,Math.min(1,((e[0]-t[0])*i+(e[1]-t[1])*n)/r));return Math.hypot(e[0]-(t[0]+o*i),e[1]-(t[1]+o*n))},Wp=(e,t,i,n)=>{const r=n[2]-n[0],o=n[3]-n[1],s=t*o-i*r;if(Math.abs(s)<1e-12)return 1/0;const a=n[0]-e[0],l=n[1]-e[1],c=(a*o-l*r)/s;if(!(c>1e-9))return 1/0;const h=(a*i-l*t)/s;return h<-1e-9||h>1+1e-9?1/0:c};const qp=[[0,1],[45,.88],[70,.62],[86,.32],[100,0]];function Bp(){return{clipCache:new Map,geometryWarnings:new Set,featherUnits:null,renderedSources:new Map,lastAppearance:new Map,enteringSources:new Set,enterRafs:new Map,fadeTimers:new Map,featherSuspendUntil:0,featherResumeTimer:0,sourceSeq:0}}function jp(e,t){if(!e.clipCache.has(t))return{hit:!1,value:null};const i=e.clipCache.get(t)??null;return e.clipCache.delete(t),e.clipCache.set(t,i),{hit:!0,value:i}}function Up(e,t,i,n=256){for(e.clipCache.delete(t),e.clipCache.set(t,i);e.clipCache.size>n;){const t=e.clipCache.keys().next().value;if(void 0===t)break;e.clipCache.delete(t)}}function Vp(e){const t=Rh(e.hass,e.devices,null,e.virtualLights).filter(t=>t.device.space===e.spaceId),i=new Map;for(const e of t){if(!e.device.id)continue;const t=i.get(e.device.id)||[];t.push(e),i.set(e.device.id,t)}const n=[];for(const t of e.devices){if(!t.id||t.space!==e.spaceId)continue;const r=Th(i.get(t.id)||[]);if(!r)continue;const o=sn(r.passive?{state:r.on?"on":"off",attributes:{}}:e.hass.states[r.eid],t.marker?.glow_color,e.defaultColor),s=Number(t.marker?.glow_radius_cm),a=Number.isFinite(s)&&s>0?s/e.cellCm*e.gridPitch:e.defaultRadiusUnits;n.push({key:`${e.spaceId}|${t.id}`,sourceEid:r.eid,pos:e.position(t),radius:a,appearance:o?{c:o.c,alpha:an(o.bri,e.paletteAlpha)}:null})}return n}function Gp(e){const t=Rl([e.rawSpaceConfig,e.cellCm,e.gridPitch]),i=e.space.rooms.flatMap(e=>{const t=Et(e);return t?[{room:e,poly:t}]:[]}),n=Math.max(10/e.cellCm*e.gridPitch,.5*e.gridPitch),r=e=>i.some(({poly:t})=>Yt(e,t)),o=e.openings.flatMap(t=>{if("door"!==(i=String(t.type))&&"gate"!==i&&"passage"!==i)return[];var i;const o=t.angle*Math.PI/180,s=-Math.sin(o),a=Math.cos(o);return r([t.rx+s*n,t.ry+a*n])&&r([t.rx-s*n,t.ry-a*n])?[{opening:t,amount:jt(e.openingAmount(t))}]:[]});return{geometryFingerprint:t,fingerprint:Rl([t,o.map(({opening:e,amount:t})=>({id:e.id,type:e.type,contact:e.contact,amount:t})).filter(e=>!!e.contact&&("door"===e.type||"gate"===e.type)).map(e=>{const t=Math.max(0,Math.min(1,Number(e.amount)||0));return`${e.id}:${t.toFixed(3)}`}).sort().join("|")]),polygons:i,passageStates:o}}function Kp(e){const{revision:t}=e,i=`${e.space.id}|${t.fingerprint}`,n=[...e.zeroWalls.transmissive],r=bp(t.passageStates.filter(({amount:e})=>e>0).map(({opening:e,amount:t})=>({...e,rlen:Ut(e.rlen,t)})),e.space,[...e.walls],e.zeroWalls.contour,e.wallKeyPitch,e.cellCm,e.gridPitch,e.coordScale);for(const e of r){const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,r=Math.sin(t)*e.length/2;n.push([e.x-i,e.y-r,e.x+i,e.y+r])}const o=t.passageStates.flatMap(({opening:e,amount:t})=>t>0&&e.partitionHost?[Zs(Pd(e.partitionHost),t)]:[]),s=e.physicalBodies(o,i),a=[],l=e.sharedWallGeometry?.sourceFingerprint,c=e.sharedWallGeometry&&l===t.geometryFingerprint?function(e,t=[],i=[],n={}){if("failed-core"===e.status||"not-applicable"===e.status)return null;const r=e.roomComponents?.length?e.roomComponents:ms(e.roomGeom)?[{id:"room-primary",geom:e.roomGeom}]:[];let o=r[0]?.geom||null;const s=r.slice(1).map(e=>({...e}));try{for(const i of t){if(!(i.length>0&&e.openingIndex))continue;const t=Is(e.openingIndex,i,!0);if(!t.negative&&!t.positive)continue;const n=i.angle*Math.PI/180,r=Math.cos(n),a=Math.sin(n),l=-a,c=r,h=i.length/2,d=e.openingPadUnits??1.25*Math.max(e.depthUnits,1),u=ps([[i.x-r*h-l*d,i.y-a*h-c*d],[i.x+r*h-l*d,i.y+a*h-c*d],[i.x+r*h+l*d,i.y+a*h+c*d],[i.x-r*h+l*d,i.y-a*h+c*d]]);o&&(o=Mt(o,u));for(const e of s)e.geom=Mt(e.geom,u)}const r=[...s];let a=s.length;const l=n.mergeExtra||((e,t)=>e?$t(e,t):t);for(let e=0;ee.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]))||Math.abs(li(t))<=1e-9){a++;continue}const n=[ps(t)];try{const t=l(o,n,e);if(!ms(t))throw new Error("invalid extra union");o=t}catch{a++,ms(n)&&r.push({id:`policy-extra-${e}`,geom:n})}}r.sort((e,t)=>Ss(e.geom).localeCompare(Ss(t.geom)));const c=o||[];return{status:a?"degraded-extra":"ok",geom:c,components:[...ms(c)?[{id:"primary",geom:c}]:[],...r.map((e,t)=>({...e,id:`isolated-${t}`}))]}}catch{return null}}(e.sharedWallGeometry,r,s):null,h=c||(e.walls.length||s.length?Ts(e.space.rooms,[...e.walls],e.zeroWalls.contour,r,e.wallKeyPitch,e.cellCm,e.gridPitch,e.coordScale,s):null);if(!h||"ok"!==h.status&&"degraded-extra"!==h.status)for(const e of s)a.push(...Np(e));else for(const e of h.components)for(const t of aa(e.geom))a.push(...Np(t));const d=.02*e.gridPitch;for(const{poly:e}of t.polygons){const t=n.length?dn(e,n,d):Np(e);for(const e of t)a.push(e)}for(const t of e.zeroWalls.barriers)a.push(t);return{occluders:Hp(a),floor:t.polygons.map(({poly:e})=>e),fingerprint:t.fingerprint,masonryGeometry:!h||"ok"!==h.status&&"degraded-extra"!==h.status?[]:h.components.flatMap(e=>e.geom),opaqueBodies:s}}function Yp(e){const t=function(e,t,i,n=96){if(!(t>0&&Number.isFinite(e[0])&&Number.isFinite(e[1])))return[];const r=[];for(const n of i){if(!n||n.length<4)continue;if(![n[0],n[1],n[2],n[3]].every(Number.isFinite))continue;const i=Lp(e,n);if(i<1e-7)return[];i>t||r.push(n)}const o=[],s=Math.max(12,Math.round(n));for(let e=0;ee-t);const l=[];let c=Number.NEGATIVE_INFINITY;for(const i of o){if(i-c<1e-9)continue;c=i;const n=Math.cos(i),o=Math.sin(i);let s=t;for(const t of r){const i=Wp(e,n,o,t);i=3?l:[]}([e.source.x,e.source.y],e.radius,e.scene.occluders,96);return{lit:t.length>=3?ra([t],e.scene.floor,{onBoundsFailure:({boundIndex:t,phase:i})=>{const n=e.polygons[t]?.room;e.onBoundsFailure?.(n?.id||`#${t}`,i)}}):[]}}function Xp(e,t){return ha([e.x,e.y],t.masonryGeometry,t.opaqueBodies)}function Zp(e,t){if(t.reducedMotion())return;const i=t.window();e.featherSuspendUntil=Math.max(e.featherSuspendUntil,Date.now()+500),i.clearTimeout(e.featherResumeTimer);const n=()=>{e.featherResumeTimer=0,Date.now(){e.enterRafs.get(i)===n&&(e.enterRafs.delete(i),e.enteringSources.delete(i),t.isConnected()&&t.requestUpdate())});e.enterRafs.set(i,n)}return{domId:o,entering:e.enteringSources.has(i),leaving:!1}}if(null==o)return null;const s=e.enterRafs.get(i);if(null!=s&&r.cancelAnimationFrame(s),e.enterRafs.delete(i),e.enteringSources.delete(i),!e.fadeTimers.has(i)){Zp(e,t);const n=r.setTimeout(()=>{e.fadeTimers.get(i)===n&&(e.fadeTimers.delete(i),e.renderedSources.delete(i),e.lastAppearance.delete(i),t.isConnected()&&t.requestUpdate())},534);e.fadeTimers.set(i,n)}return{domId:o,entering:!1,leaving:!0}}function Qp(e,t,i){const n=t.window(),r=e.fadeTimers.get(i),o=e.enterRafs.get(i);null!=r&&n.clearTimeout(r),null!=o&&n.cancelAnimationFrame(o),e.fadeTimers.delete(i),e.enterRafs.delete(i),e.enteringSources.delete(i),e.renderedSources.delete(i),e.lastAppearance.delete(i)}function em(e,t,i){const n=`${i}|`;for(const i of[...e.renderedSources.keys()])i.startsWith(n)&&Qp(e,t,i)}function tm(e,t,i,n){const r=`${i}|`;for(const i of[...e.renderedSources.keys()])i.startsWith(r)&&!n.has(i)&&Qp(e,t,i)}function im(e,t){const i=t.window();for(const t of e.fadeTimers.values())i.clearTimeout(t);for(const t of e.enterRafs.values())i.cancelAnimationFrame(t);i.clearTimeout(e.featherResumeTimer),e.clipCache.clear(),e.geometryWarnings.clear(),e.fadeTimers.clear(),e.enterRafs.clear(),e.enteringSources.clear(),e.renderedSources.clear(),e.lastAppearance.clear(),e.featherUnits=null,e.featherSuspendUntil=0,e.featherResumeTimer=0,e.sourceSeq=0}function nm(e,t,i,n,r){const o=`${t}|${i}|${n}`;if(!e.geometryWarnings.has(o)){if(e.geometryWarnings.size>=128){const t=e.geometryWarnings.values().next().value;t&&e.geometryWarnings.delete(t)}e.geometryWarnings.add(o),console.warn(`HOUSEPLAN GLOW GEOMETRY FALLBACK: #218, space ${t}, room ${n}, phase ${r}`)}}function rm(e,t,i){const n=1/(t>0?t:1),r=i&&Date.now()>=e.featherSuspendUntil;return(null==e.featherUnits||r)&&(e.featherUnits=n),{feather:e.featherUnits??n,enabled:r}}function om(e){if(!e.spots.length)return j``;const t=4*e.feather,i=e.spots.reduce((e,i)=>({x:Math.min(e.x,i.pos.x-i.r-t),y:Math.min(e.y,i.pos.y-i.r-t),maxX:Math.max(e.maxX,i.pos.x+i.r+t),maxY:Math.max(e.maxY,i.pos.y+i.r+t),w:0,h:0}),{x:1/0,y:1/0,maxX:-1/0,maxY:-1/0,w:0,h:0});i.w=i.maxX-i.x,i.h=i.maxY-i.y;const n=e.enabledClip?.length?e.enabledClip:null;return j` + ${Ip(e.spots,e=>e.key,e=>{const t=e.domId;return j` - ${Wp.map(([t,i])=>j` + ${qp.map(([t,i])=>j` `)} @@ -4188,7 +4188,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 data-blend=${e.screenBlend?"screen":"normal"} data-feather-px="${2}" clip-path=${n?"url(#hp-glow-enabled)":V}> - ${Fp(e.spots,e=>e.key,e=>j` + ${Ip(e.spots,e=>e.key,e=>j` tu(e.lang,t),iconRules:r})}function um(e){const t=$a(e.cfg).find(t=>t.id===e.spaceId);if(!t)return null;const i=ji(e.cfg.spaces.find(t=>t.id===e.spaceId)),n=Gi(e.cfg.settings),r=e.iconSize??2.5,o=r>8?2.5:r,s=Xu(o),a=e.registry?Jc(e.hass,e.registry):e.hass,l=e.registry?Qc(e.hass,e.registry):e.hass,c=(e.devices||dm(e)).filter(t=>t.space===e.spaceId),h=c.filter(e=>!e.hidden),d=function(e,t,i){const n={},r=i/100*Ya(t)*1.3;for(const i of t.rooms){if(!i.area)continue;const t=e.filter(e=>e.area===i.area);if(!t.length)continue;const o=Qa(i),s=.1*Math.min(o.w,o.h),a=o.w-2*s,l=o.h-2*s,c=Math.max(1,Math.round(Math.sqrt(t.length*a/Math.max(l,1)))),h=a/c,d=l/Math.max(Math.ceil(t.length/c),1),u=t.map((e,t)=>({x:o.x+s+h*(t%c+.5),y:o.y+s+d*(Math.floor(t/c)+.5)}));gi(u,o,r,.5*s),t.forEach((e,t)=>n[e.id]=za(u[t]))}return n}(c,t,o),u=ge(e.cfg.settings?.icon_rules?.length?e.cfg.settings.icon_rules:fe),p=Zh(a,u,e.cfg.markers||[]),m=i=>{const n=i.settings?.temp_source;if(n)return Gh(a,n,"temp",e.cfg.markers||[]);const r=Yh(t.id,i);return r?p.get(r)?.temp??null:null},_=[];for(const t of h){const i=e.layout[t.id];if(i&&i.s===e.spaceId){const e=i.x*va,t=i.y*va;_.push({minX:e,minY:t,maxX:e,maxY:t})}}const f=e.cfg.spaces.find(t=>t.id===e.spaceId)||{},g=Array.isArray(f.walls)?f.walls:[],v=Number(f.cell_cm)>0?Number(f.cell_cm):5,y=up(f,t,va,.02*Ta),b=(f.openings||[]).flatMap(e=>{if("partition"!==e.host?.kind)return[];const i=Dd(e,t.partitions,va,v,Ta).resolved;return i?[i]:[]}),w=Rl({partitions:t.partitions,roomDrafts:t.room_drafts,columns:t.wall_columns,cellCm:v,hostedOpenings:b.map(e=>({id:e.opening.id,host:e.host,length:e.length,type:e.opening.type}))}),k=function(e,t,i,n){let r=sm.get(e);r||(r=new Map,sm.set(e,r));const o=r.get(t);if(o?.fingerprint===i)return o.value;const s=n();return r.set(t,{fingerprint:i,value:s}),s}(e.cfg,t.id,w,()=>ea(t,v,Ta,2e-4*Ta,b.map(Pd)).all);for(const e of k){const t=e.map(e=>e[0]),i=e.map(e=>e[1]);t.length&&_.push({minX:Math.min(...t),minY:Math.min(...i),maxX:Math.max(...t),maxY:Math.max(...i)})}for(const e of y.lines)_.push({minX:Math.min(e[0],e[2]),minY:Math.min(e[1],e[3]),maxX:Math.max(e[0],e[2]),maxY:Math.max(e[1],e[3])});const x=function(e,t=1e3){const i=[];for(const[n,r]of(e||[]).entries()){if("passage"!==r?.type)continue;const e={...r,id:String(r.id||`passage-${n}`),type:"passage",rx:Number(r.x)*t,ry:Number(r.y)*t,rlen:Number(r.length)*t};[e.rx,e.ry,e.rlen,e.angle].every(Number.isFinite)&&e.rlen>0&&i.push(e)}return i}((f.openings||[]).flatMap(e=>{if(!e.host||"wall"===e.host.kind)return[e];const t=b.find(t=>t.opening.id===e.id);return t?[Hd(e,t,va)]:[]}),va),$=ss(t.rooms,g,y.contour,Ca,v,Ta,va),S=b.filter(e=>Id(e,$,2e-4*Ta)).map(e=>({x:e.center[0],y:e.center[1],angle:e.angle,length:e.length})),M=Ia(e.fit),R=!!(g.length||k.length&&(i.showBorders||"house"===M)),T=R?Rl(x.length?{rooms:t.rooms,walls:g,extras:k,cellCm:v,zero:y.contour,passages:x.map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),hostedCompositeOpenings:S}:{rooms:t.rooms,walls:g,extras:k,cellCm:v,zero:y.contour}):"",C=R?function(e,t,i,n){let r=om.get(e);r||(r=new Map,om.set(e,r));const o=r.get(t);if(o?.fingerprint===i)return o.value;const s=n();return r.set(t,{fingerprint:i,value:s}),s}(e.cfg,t.id,T,()=>{const e=Cs(t.rooms,g,y.contour,[...x.filter(e=>"partition"!==e.host?.kind).map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),...S],Ca,v,Ta,va,k);return e&&Object.defineProperty(e,"sourceFingerprint",{value:Rl([f,v,Ta]),enumerable:!1}),e}):null;let D=Ga(t,_,e.compactTopFrame?{top:0,right:.05,bottom:.05,left:.05}:.05);if("house"===M){const e=[],n=ma(2.5,v)/2,r=ma(.6,v)/2;for(const i of t.rooms){const t=La(i);t&&e.push(Na(t,n))}for(const t of C?.components||[]){const i=Ha(t.geom);i&&e.push(Na(i,r))}for(const t of k){const i=Ha(t);i&&e.push(Na(i,r))}for(const t of y.lines){const i={minX:Math.min(t[0],t[2]),minY:Math.min(t[1],t[3]),maxX:Math.max(t[0],t[2]),maxY:Math.max(t[1],t[3])};e.push(Na(i,n))}if(!i.hideOpenings)for(const t of b){const i=t.opening,n="gate"===i.type?!i.flip_v:!!i.flip_v,r=au({type:i.type,length:t.length,angle:t.angle,flipH:!!i.flip_h,flipV:!!i.flip_v,cellCm:v,gridPitch:Ta,face:Od(t,n)},t.center);r&&e.push(r)}D=Va(e)||D}const z=[D.x,D.y,D.w,D.h],A=new Map(t.rooms.map(t=>{const r=fn(i.fill,t);return[t,Yi(r,"lqi"===r&&t.area?Jh(a,c,t.area):null,"light"===r?Ch(Rh(a,c,t,e.virtualLights)):"none","temp"===r?m(t):null,i.tempMin,i.tempMax,n,Bi(i.customFill,t))]})),P=new Map;for(const e of t.rooms)e.id&&P.set(e.id,A.get(e)||null);const O=t.rooms.filter(e=>e.area||i.showBorders||"none"!==fn(i.fill,e)).map(e=>{let n="room "+(t.bg?"overlay":"yard"),r="";const o=fn(i.fill,e);if(i.showBorders||"none"!==o){n+=" styled";const t=[`--room-stroke:${i.color}`,`--room-stroke-op:${i.showBorders&&!y.contour.length?i.opacity:0}`],o=A.get(e)||null;o?(n+=" filled",t.push(`--room-fill:${o.color}`,`--room-fill-op:${o.opacity.toFixed(3)}`)):t.push("--room-fill:transparent","--room-fill-op:0"),r=t.join(";")}const s=e.id||V,a=e.area||V,l=e.poly?j``}const sm=new WeakMap,am=new WeakMap,lm=new WeakMap,cm=new WeakMap;function hm(e,t){const i=e.get(t);return void 0!==i&&(e.delete(t),e.set(t,i)),i}function dm(e,t,i,n){for(e.delete(t),e.set(t,i);e.size>n;)e.delete(e.keys().next().value)}function um(e){const t=$a(e.cfg),i={};for(const t of e.cfg.spaces||[])for(const e of t.rooms||[])e.area&&(i[e.area]=t.id);const n=e.cfg.settings?.exclude_integrations?new Set(e.cfg.settings.exclude_integrations):_e,r=ge(e.cfg.settings?.icon_rules?.length?e.cfg.settings.icon_rules:fe);return Uh({hass:e.hass,registry:e.registry,areaToSpace:i,markers:e.cfg.markers||[],settings:e.cfg.settings||{},excluded:n,showAll:!!e.cfg.settings?.show_all,firstSpaceId:t[0]?.id||"",loc:t=>tu(e.lang,t),iconRules:r})}function pm(e){const t=$a(e.cfg).find(t=>t.id===e.spaceId);if(!t)return null;const i=ji(e.cfg.spaces.find(t=>t.id===e.spaceId)),n=Gi(e.cfg.settings),r=e.iconSize??2.5,o=r>8?2.5:r,s=Zu(o),a=e.registry?Jc(e.hass,e.registry):e.hass,l=e.registry?Qc(e.hass,e.registry):e.hass,c=(e.devices||um(e)).filter(t=>t.space===e.spaceId),h=c.filter(e=>!e.hidden),d=function(e,t,i){const n={},r=i/100*Ya(t)*1.3;for(const i of t.rooms){if(!i.area)continue;const t=e.filter(e=>e.area===i.area);if(!t.length)continue;const o=Qa(i),s=.1*Math.min(o.w,o.h),a=o.w-2*s,l=o.h-2*s,c=Math.max(1,Math.round(Math.sqrt(t.length*a/Math.max(l,1)))),h=a/c,d=l/Math.max(Math.ceil(t.length/c),1),u=t.map((e,t)=>({x:o.x+s+h*(t%c+.5),y:o.y+s+d*(Math.floor(t/c)+.5)}));gi(u,o,r,.5*s),t.forEach((e,t)=>n[e.id]=za(u[t]))}return n}(c,t,o),u=ge(e.cfg.settings?.icon_rules?.length?e.cfg.settings.icon_rules:fe),p=Zh(a,u,e.cfg.markers||[]),m=i=>{const n=i.settings?.temp_source;if(n)return Gh(a,n,"temp",e.cfg.markers||[]);const r=Yh(t.id,i);return r?p.get(r)?.temp??null:null},_=[];for(const t of h){const i=e.layout[t.id];if(i&&i.s===e.spaceId){const e=i.x*va,t=i.y*va;_.push({minX:e,minY:t,maxX:e,maxY:t})}}const f=e.cfg.spaces.find(t=>t.id===e.spaceId)||{},g=Array.isArray(f.walls)?f.walls:[],v=Number(f.cell_cm)>0?Number(f.cell_cm):5,y=pp(f,t,va,.02*Ta),b=(f.openings||[]).flatMap(e=>{if("partition"!==e.host?.kind)return[];const i=Dd(e,t.partitions,va,v,Ta).resolved;return i?[i]:[]}),w=Rl({partitions:t.partitions,roomDrafts:t.room_drafts,columns:t.wall_columns,cellCm:v,hostedOpenings:b.map(e=>({id:e.opening.id,host:e.host,length:e.length,type:e.opening.type}))}),k=function(e,t,i,n){let r=am.get(e);r||(r=new Map,am.set(e,r));const o=r.get(t);if(o?.fingerprint===i)return o.value;const s=n();return r.set(t,{fingerprint:i,value:s}),s}(e.cfg,t.id,w,()=>ea(t,v,Ta,2e-4*Ta,b.map(Pd)).all);for(const e of k){const t=e.map(e=>e[0]),i=e.map(e=>e[1]);t.length&&_.push({minX:Math.min(...t),minY:Math.min(...i),maxX:Math.max(...t),maxY:Math.max(...i)})}for(const e of y.lines)_.push({minX:Math.min(e[0],e[2]),minY:Math.min(e[1],e[3]),maxX:Math.max(e[0],e[2]),maxY:Math.max(e[1],e[3])});const x=function(e,t=1e3){const i=[];for(const[n,r]of(e||[]).entries()){if("passage"!==r?.type)continue;const e={...r,id:String(r.id||`passage-${n}`),type:"passage",rx:Number(r.x)*t,ry:Number(r.y)*t,rlen:Number(r.length)*t};[e.rx,e.ry,e.rlen,e.angle].every(Number.isFinite)&&e.rlen>0&&i.push(e)}return i}((f.openings||[]).flatMap(e=>{if(!e.host||"wall"===e.host.kind)return[e];const t=b.find(t=>t.opening.id===e.id);return t?[Hd(e,t,va)]:[]}),va),$=ss(t.rooms,g,y.contour,Ca,v,Ta,va),S=b.filter(e=>Id(e,$,2e-4*Ta)).map(e=>({x:e.center[0],y:e.center[1],angle:e.angle,length:e.length})),M=Ia(e.fit),R=!!(g.length||k.length&&(i.showBorders||"house"===M)),T=R?Rl(x.length?{rooms:t.rooms,walls:g,extras:k,cellCm:v,zero:y.contour,passages:x.map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),hostedCompositeOpenings:S}:{rooms:t.rooms,walls:g,extras:k,cellCm:v,zero:y.contour}):"",C=R?function(e,t,i,n){let r=sm.get(e);r||(r=new Map,sm.set(e,r));const o=r.get(t);if(o?.fingerprint===i)return o.value;const s=n();return r.set(t,{fingerprint:i,value:s}),s}(e.cfg,t.id,T,()=>{const e=Cs(t.rooms,g,y.contour,[...x.filter(e=>"partition"!==e.host?.kind).map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),...S],Ca,v,Ta,va,k);return e&&Object.defineProperty(e,"sourceFingerprint",{value:Rl([f,v,Ta]),enumerable:!1}),e}):null;let D=Ga(t,_,e.compactTopFrame?{top:0,right:.05,bottom:.05,left:.05}:.05);if("house"===M){const e=[],n=ma(2.5,v)/2,r=ma(.6,v)/2;for(const i of t.rooms){const t=La(i);t&&e.push(Na(t,n))}for(const t of C?.components||[]){const i=Ha(t.geom);i&&e.push(Na(i,r))}for(const t of k){const i=Ha(t);i&&e.push(Na(i,r))}for(const t of y.lines){const i={minX:Math.min(t[0],t[2]),minY:Math.min(t[1],t[3]),maxX:Math.max(t[0],t[2]),maxY:Math.max(t[1],t[3])};e.push(Na(i,n))}if(!i.hideOpenings)for(const t of b){const i=t.opening,n="gate"===i.type?!i.flip_v:!!i.flip_v,r=au({type:i.type,length:t.length,angle:t.angle,flipH:!!i.flip_h,flipV:!!i.flip_v,cellCm:v,gridPitch:Ta,face:Od(t,n)},t.center);r&&e.push(r)}D=Va(e)||D}const z=[D.x,D.y,D.w,D.h],A=new Map(t.rooms.map(t=>{const r=fn(i.fill,t);return[t,Yi(r,"lqi"===r&&t.area?Jh(a,c,t.area):null,"light"===r?Ch(Rh(a,c,t,e.virtualLights)):"none","temp"===r?m(t):null,i.tempMin,i.tempMax,n,Bi(i.customFill,t))]})),P=new Map;for(const e of t.rooms)e.id&&P.set(e.id,A.get(e)||null);const O=t.rooms.filter(e=>e.area||i.showBorders||"none"!==fn(i.fill,e)).map(e=>{let n="room "+(t.bg?"overlay":"yard"),r="";const o=fn(i.fill,e);if(i.showBorders||"none"!==o){n+=" styled";const t=[`--room-stroke:${i.color}`,`--room-stroke-op:${i.showBorders&&!y.contour.length?i.opacity:0}`],o=A.get(e)||null;o?(n+=" filled",t.push(`--room-fill:${o.color}`,`--room-fill-op:${o.opacity.toFixed(3)}`)):t.push("--room-fill:transparent","--room-fill-op:0"),r=t.join(";")}const s=e.id||V,a=e.area||V,l=e.poly?j``:j``;return l}),F=t.rooms.filter(e=>gn(i.glow,e)),I=F.filter(e=>{const t=A.get(e);return!t||t.opacity<=0}).map(e=>e.poly?j`
`}_renderVacSection(t){const e=this.host._devices.find(e=>e.id===t.devId);if(!e||!this.host._isVacDev(e))return a;const s=e.marker?.vacuum||{},i=this.host._vacAllCamerasFor===e.id,n=this.host._vacSourceResolution(e,i),r=n.entityId,h=n.candidates.find(t=>t.entityId===r)||null,l=r?Is(this.host.hass?.states[r]?.attributes):null,c=!1!==s.live&&("ok"===n.status||"unsupported"===n.status),d=this._vacPlanRoomAnchors(e.space),p=l?Ts(l.rooms,d.map(t=>t.name)):0,_=!!(c&&l&&p>=3),u=Object.keys(s.calibration||{}),m=t=>{const s=this.host._vacEnsureMarker(e);s&&(s.vacuum={...s.vacuum||{},...t},this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate())},g=new Set(e.entities||[]),f=n.candidates.filter(t=>g.has(t.entityId)||t.entityId===s.source),b=i?n.candidates.filter(t=>t.entityId.startsWith("camera.")&&!f.some(e=>e.entityId===t.entityId)):[],v=t=>[t.hasPosition?this.host._t("vac.cap_position"):"",t.hasRooms?this.host._t("vac.cap_rooms_short"):"",t.hasPath?this.host._t("vac.cap_path"):"",t.hasMapId?this.host._t("vac.cap_map"):""].filter(Boolean).join(" · ")||this.host._t("vac.cap_none"),y=t=>o` +
`}_renderVacSection(t){const e=this.host._devices.find(e=>e.id===t.devId);if(!e||!this.host._isVacDev(e))return a;const s=e.marker?.vacuum||{},i=this.host._vacAllCamerasFor===e.id,n=this.host._vacSourceResolution(e,i),r=n.entityId,h=n.candidates.find(t=>t.entityId===r)||null,l=r?Is(this.host.hass?.states[r]?.attributes):null,c=!1!==s.live&&("ok"===n.status||"unsupported"===n.status),d=this._vacPlanRoomAnchors(e.space),p=l?Ts(l.rooms,d.map(t=>t.name)):0,u=!!(c&&l&&p>=3),_=Object.keys(s.calibration||{}),m=t=>{const s=this.host._vacEnsureMarker(e);s&&(s.vacuum={...s.vacuum||{},...t},this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate())},g=new Set(e.entities||[]),f=n.candidates.filter(t=>g.has(t.entityId)||t.entityId===s.source),b=i?n.candidates.filter(t=>t.entityId.startsWith("camera.")&&!f.some(e=>e.entityId===t.entityId)):[],v=t=>[t.hasPosition?this.host._t("vac.cap_position"):"",t.hasRooms?this.host._t("vac.cap_rooms_short"):"",t.hasPath?this.host._t("vac.cap_path"):"",t.hasMapId?this.host._t("vac.cap_map"):""].filter(Boolean).join(" · ")||this.host._t("vac.cap_none"),y=t=>o` `:a} + ${u?o``:a} ${c?o``:a} ${this.host._t("vac.documentation")} @@ -1406,9 +1406,9 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 ${["never","cleaning","always"].map(t=>o` `)} - ${u.length?o`
${Rs(this.host._t("vac.cal_maps"),{maps:u.join(", ")})}
`:a} + ${_.length?o`
${Rs(this.host._t("vac.cal_maps"),{maps:_.join(", ")})}
`:a} `:a} - `}_vacMapId(t,e,s=this.host._planHass){return this.host._vacMapId(t,e,s)}_vacSaveMatrix(t,e,s,i){const o=this.host._devices.find(e=>e.id===t),a=o?this.host._vacEnsureMarker(o):this.host._serverCfg?.markers?.find(e=>e.id===t);if(!a)return!1;const n={...a.vacuum||{}};return n.source=e,n.calibration={...n.calibration||{},[s]:i.map(t=>Number(t.toFixed(6)))},a.vacuum=n,this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate(),!0}_vacPlanRoomAnchors(t){return(this.host._spaceModelById(t)?.rooms||[]).map(t=>({room:t,poly:R(t)})).filter(({room:t,poly:e})=>t.name&&e).map(({room:t,poly:e})=>{const s=Fs(e);return s?{name:String(t.name),cx:s[0],cy:s[1]}:null}).filter(Boolean)}_vacAutoCalibrate(t){const e=this.host._vacSource(t),s=e?Is(this.host.hass?.states[e]?.attributes):null;if(!e||!s||s.rooms.length<3)return void this.host._showToast(this.host._t("vac.autocal_no_rooms"));const i=this._vacPlanRoomAnchors(t.space),o=As(s.rooms,i);if(!o)return void this.host._showToast(this.host._t("vac.autocal_no_match"));const a=this.host._serverCfg?.spaces?.find(e=>e.id===t.space),n=Number(a?.cell_cm),r=Number.isFinite(n)&&n>0?n:5,h=Ns(o.residual,this.host._gridPitch,r),l=this._vacMapId(t,s);h>Hs?this.host._vacCalConfirm={markerId:t.id,source:e,mapId:l,matrix:o.matrix,rooms:o.matched.length,error:ee(h,"mi"===this.host.hass?.config?.unit_system?.length)}:this._vacSaveMatrix(t.id,e,l,o.matrix)&&this.host._showToast(Rs(this.host._t("vac.autocal_done"),{rooms:String(o.matched.length)}))}_vacApplyCalibrationProposal(t){const e=this.host._vacCalConfirm;if(e){if(this.host._vacCalConfirm=null,t){const t=this.host._devices.find(t=>t.id===e.markerId),s=Ls(e.matrix);if(!t||!s)return;if(this.host._markerDialog=null,t.space!==this.host._space&&!this.host._commitSpace(t.space))return;return void(this.host._vacFit={markerId:e.markerId,source:e.source,mapId:e.mapId,p:s,drag:null})}this._vacSaveMatrix(e.markerId,e.source,e.mapId,e.matrix)&&this.host._showToast(Rs(this.host._t("vac.autocal_done"),{rooms:String(e.rooms)}))}}_vacStartFit(t){const e=this.host._vacSource(t),s=e?Is(this.host.hass?.states[e]?.attributes):null;if(!e||!s)return void this.host._showToast(this.host._t("vac.cal_need_pos"));const i=this._vacMapId(t,s),o=t.marker?.vacuum?.calibration?.[i],a=this.host._spaceModelById(t.space);if(!a)return;const n=a.vb,r=o&&6===o.length&&Ls(o)||Os(s.rooms,n);this.host._markerDialog=null,(t.space===this.host._space||this.host._commitSpace(t.space))&&(this.host._vacFit={markerId:t.id,source:e,mapId:i,p:r,drag:null})}_vacFitSave(){const t=this.host._vacFit;if(!t)return;const e=this._vacSaveMatrix(t.markerId,t.source,t.mapId,Es(t.p));this.host._vacFit=null,e&&this.host._showToast(this.host._t("vac.cal_done"))}_vacFitTurn(t){const e=this.host._vacFit;if(!e)return;const s=Is(this.host.hass?.states[e.source]?.attributes),i=this._vacGhostCentre(s?.rooms||[]),o={...e.p,...t};this.host._vacFit={...e,p:Bs(o,e.p,i[0],i[1])}}_vacGhostCentre(t){const e=[],s=[];for(const i of t)e.push(i.x0??i.cx,i.x1??i.cx),s.push(i.y0??i.cy,i.y1??i.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...s)+Math.max(...s))/2]:[0,0]}_vacDelta(t,e,s){const i=this.host._stageEl,o=i?.clientWidth||1,a=i?.clientHeight||1;return[e/o*t.w,s/a*t.h]}_vacFitPointer(t,e){const s=this.host._vacFit;if(!s)return;if(t.stopPropagation(),"pointerdown"===t.type){const e=t.target,i=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this.host._vacFit={...s,drag:i?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:Number(i.split(",")[0]),fy:Number(i.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:0,fy:0}})}const i=s.drag;if(i){if("pointermove"===t.type){const[o,a]=this._vacDelta(e,t.clientX-i.sx,t.clientY-i.sy);if("move"===i.kind)this.host._vacFit={...s,p:{...i.p0,ox:i.p0.ox+o,oy:i.p0.oy+a}};else{const t=Is(this.host.hass?.states[s.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),n=Es(i.p0),[r,h]=Ws(n,e[0],e[1]),[l,c]=Ws(n,i.fx,i.fy),d=Math.hypot(r-l,h-c)||1,[p,_]=[2*r-l,2*h-c],u=Math.hypot(p+2*o-l,_+2*a-c)/2,m=Math.max(.05,u/d),g={...i.p0,s:i.p0.s*m};this.host._vacFit={...s,p:Bs(g,i.p0,i.fx,i.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this.host._vacFit={...s,drag:null})}}_renderVacFit(t){const e=this.host._vacFit;if(!e)return a;const s=Is(this.host._renderPlanHass?.states?.[e.source]?.attributes);if(!s)return a;const i=Es(e.p),n=[],r=[],h=[];for(const t of s.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>Ws(i,t,e));e.forEach(([t,e])=>{r.push(t),h.push(e)});const[s,o]=Ws(i,t.cx,t.cy);n.push(ce` + `}_vacMapId(t,e,s=this.host._planHass){return this.host._vacMapId(t,e,s)}_vacSaveMatrix(t,e,s,i){const o=this.host._devices.find(e=>e.id===t),a=o?this.host._vacEnsureMarker(o):this.host._serverCfg?.markers?.find(e=>e.id===t);if(!a)return!1;const n={...a.vacuum||{}};return n.source=e,n.calibration={...n.calibration||{},[s]:i.map(t=>Number(t.toFixed(6)))},a.vacuum=n,this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate(),!0}_vacPlanRoomAnchors(t){return(this.host._spaceModelById(t)?.rooms||[]).map(t=>({room:t,poly:R(t)})).filter(({room:t,poly:e})=>t.name&&e).map(({room:t,poly:e})=>{const s=Fs(e);return s?{name:String(t.name),cx:s[0],cy:s[1]}:null}).filter(Boolean)}_vacAutoCalibrate(t){const e=this.host._vacSource(t),s=e?Is(this.host.hass?.states[e]?.attributes):null;if(!e||!s||s.rooms.length<3)return void this.host._showToast(this.host._t("vac.autocal_no_rooms"));const i=this._vacPlanRoomAnchors(t.space),o=As(s.rooms,i);if(!o)return void this.host._showToast(this.host._t("vac.autocal_no_match"));const a=this.host._serverCfg?.spaces?.find(e=>e.id===t.space),n=Number(a?.cell_cm),r=Number.isFinite(n)&&n>0?n:5,h=Ns(o.residual,this.host._gridPitch,r),l=this._vacMapId(t,s);h>Hs?this.host._vacCalConfirm={markerId:t.id,source:e,mapId:l,matrix:o.matrix,rooms:o.matched.length,error:ee(h,"mi"===this.host.hass?.config?.unit_system?.length)}:this._vacSaveMatrix(t.id,e,l,o.matrix)&&this.host._showToast(Rs(this.host._t("vac.autocal_done"),{rooms:String(o.matched.length)}))}_vacApplyCalibrationProposal(t){const e=this.host._vacCalConfirm;if(e){if(this.host._vacCalConfirm=null,t){const t=this.host._devices.find(t=>t.id===e.markerId),s=Ls(e.matrix);if(!t||!s)return;if(this.host._markerDialog=null,t.space!==this.host._space&&!this.host._commitSpace(t.space))return;return void(this.host._vacFit={markerId:e.markerId,source:e.source,mapId:e.mapId,p:s,drag:null})}this._vacSaveMatrix(e.markerId,e.source,e.mapId,e.matrix)&&this.host._showToast(Rs(this.host._t("vac.autocal_done"),{rooms:String(e.rooms)}))}}_vacStartFit(t){const e=this.host._vacSource(t),s=e?Is(this.host.hass?.states[e]?.attributes):null;if(!e||!s)return void this.host._showToast(this.host._t("vac.cal_need_pos"));const i=this._vacMapId(t,s),o=t.marker?.vacuum?.calibration?.[i],a=this.host._spaceModelById(t.space);if(!a)return;const n=a.vb,r=o&&6===o.length&&Ls(o)||Os(s.rooms,n);this.host._markerDialog=null,(t.space===this.host._space||this.host._commitSpace(t.space))&&(this.host._vacFit={markerId:t.id,source:e,mapId:i,p:r,drag:null})}_vacFitSave(){const t=this.host._vacFit;if(!t)return;const e=this._vacSaveMatrix(t.markerId,t.source,t.mapId,Es(t.p));this.host._vacFit=null,e&&this.host._showToast(this.host._t("vac.cal_done"))}_vacFitTurn(t){const e=this.host._vacFit;if(!e)return;const s=Is(this.host.hass?.states[e.source]?.attributes),i=this._vacGhostCentre(s?.rooms||[]),o={...e.p,...t};this.host._vacFit={...e,p:Bs(o,e.p,i[0],i[1])}}_vacGhostCentre(t){const e=[],s=[];for(const i of t)e.push(i.x0??i.cx,i.x1??i.cx),s.push(i.y0??i.cy,i.y1??i.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...s)+Math.max(...s))/2]:[0,0]}_vacDelta(t,e,s){const i=this.host._stageEl,o=i?.clientWidth||1,a=i?.clientHeight||1;return[e/o*t.w,s/a*t.h]}_vacFitPointer(t,e){const s=this.host._vacFit;if(!s)return;if(t.stopPropagation(),"pointerdown"===t.type){const e=t.target,i=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this.host._vacFit={...s,drag:i?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:Number(i.split(",")[0]),fy:Number(i.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:0,fy:0}})}const i=s.drag;if(i){if("pointermove"===t.type){const[o,a]=this._vacDelta(e,t.clientX-i.sx,t.clientY-i.sy);if("move"===i.kind)this.host._vacFit={...s,p:{...i.p0,ox:i.p0.ox+o,oy:i.p0.oy+a}};else{const t=Is(this.host.hass?.states[s.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),n=Es(i.p0),[r,h]=Ws(n,e[0],e[1]),[l,c]=Ws(n,i.fx,i.fy),d=Math.hypot(r-l,h-c)||1,[p,u]=[2*r-l,2*h-c],_=Math.hypot(p+2*o-l,u+2*a-c)/2,m=Math.max(.05,_/d),g={...i.p0,s:i.p0.s*m};this.host._vacFit={...s,p:Bs(g,i.p0,i.fx,i.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this.host._vacFit={...s,drag:null})}}_renderVacFit(t){const e=this.host._vacFit;if(!e)return a;const s=Is(this.host._renderPlanHass?.states?.[e.source]?.attributes);if(!s)return a;const i=Es(e.p),n=[],r=[],h=[];for(const t of s.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>Ws(i,t,e));e.forEach(([t,e])=>{r.push(t),h.push(e)});const[s,o]=Ws(i,t.cx,t.cy);n.push(ce` ${t.name}`)}let l=a;if(s.pos){const[e,o]=Ws(i,s.pos.x,s.pos.y);l=ce``}const c=[];if(r.length){const e=(()=>{const t=i[0]*i[4]-i[1]*i[3];return(e,s)=>[(i[4]*(e-i[2])-i[1]*(s-i[5]))/t,(-i[3]*(e-i[2])+i[0]*(s-i[5]))/t]})(),s=Math.min(...r),o=Math.max(...r),a=Math.min(...h),n=Math.max(...h),l=.022*t.w,d=l/4;for(const[t,i,r,h]of[[s,a,o,n],[o,a,s,n],[o,n,s,a],[s,n,o,a]]){const s=e(r,h);c.push(ce` `)}}return o`t.id===this.host._roomEditId);if(!e)return this.host._roomDialog=!1,void(this.host._roomEditId=null);e.name=this.host._nameSel.trim()||e.name,e.area=this.host._areaSel||null;const s=e.settings||{},i={...s};this.host._roomFill?i.fill_mode=this.host._roomFill:delete i.fill_mode,this.host._roomCustomFill?i.custom_fill=this.host._roomCustomFill:delete i.custom_fill,"glow"===s.fill_mode&&"boolean"!=typeof s.glow&&(i.glow=!0),this.host._roomTempSrc?i.temp_source=this.host._roomTempSrc:delete i.temp_source,this.host._roomHumSrc?i.hum_source=this.host._roomHumSrc:delete i.hum_source,1!==this.host._roomNameScale?i.name_scale=this.host._roomNameScale:delete i.name_scale,1!==this.host._roomLabelScale?i.label_scale=this.host._roomLabelScale:delete i.label_scale,Object.keys(i).length?e.settings=i:delete e.settings,this._saveConfig(),this.host._roomDialog=!1,this.host._roomEditId=null,this.host._nameSel="",this.host._areaSel="",this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate(),this.host._showToast(this.host._t("toast.room_updated"))}_roomSrcCandidates(){const t=this.host._planHass,e=x(this.host._markers),s=this.host._roomSrcFilter.trim().toLowerCase(),i=[];for(const o of Object.values(t.devices)){if("service"===o.entry_type)continue;if(e.devices.has(o.id))continue;const t=(o.name_by_user||o.name||o.id).trim();s&&!t.toLowerCase().includes(s)||i.push({value:"device:"+o.id,label:t,sub:o.model||this.host._t("marker.sub_device")})}for(const[o,a]of Object.entries(t.entities)){if(!o.startsWith("sensor.")||a.hidden)continue;if(S(t,o,e))continue;const n=a.name||t.states[o]?.attributes?.friendly_name||o;s&&!(n+" "+o).toLowerCase().includes(s)||i.push({value:"entity:"+o,label:n,sub:o})}return i.sort((t,e)=>t.label.localeCompare(e.label)),i.slice(0,200)}_roomSrcLabel(t){const e=t.indexOf(":"),s=t.slice(0,e),i=t.slice(e+1);return"device"===s?this.host._fullRegistryHass.devices[i]?.name_by_user||this.host._fullRegistryHass.devices[i]?.name||i:this.host._fullRegistryHass.entities[i]?.name||this.host.hass.states[i]?.attributes?.friendly_name||i}_labelPos(t,e){const s=this.host._layout["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*oh,y:s.y*oh};const i=this._snap(this.host._roomCenter(t));return{x:i[0],y:i[1]}}_labelDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=this._labelPos(e,s);this.host._drag={id:"rl_"+(e.id||""),sx:t.clientX,sy:t.clientY,ox:i.x,oy:i.y,moved:!1},nh(t),this.host._tip=null}_labelMove(t,e,s){const i="rl_"+(e.id||"");if(!this.host._drag||this.host._drag.id!==i)return;const o=this.host._stageEl;if(!o)return;const a=this.host._spaceModelById(s);if(!a)return;const n=a.vb,r=o.getBoundingClientRect(),h=this.host._viewOr(n),l=(t.clientX-this.host._drag.sx)/r.width*h.w,c=(t.clientY-this.host._drag.sy)/r.height*h.h;Math.abs(t.clientX-this.host._drag.sx)+Math.abs(t.clientY-this.host._drag.sy)>3&&(this.host._drag.moved=!0);const d=Rt(this.host._drag.ox+l),p=Rt(this.host._drag.oy+c);this.host._savePos({id:i,space:s},d,p)}_labelUp(t){const e="rl_"+(t.id||"");if(!this.host._drag||this.host._drag.id!==e)return;const s=this.host._drag.moved;this.host._drag=s?this.host._drag:null,s&&window.setTimeout(()=>this.host._drag=null,0)}_labelScale(t){const e=this.host._layout["rl_"+(t.id||"")]?.k;return"number"==typeof e&&Number.isFinite(e)?Math.min(3,Math.max(.5,e)):1}_rlResizeDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=t.target.closest(".roomlabel");if(!i)return;const o=i.getBoundingClientRect(),a=o.left+o.width/2,n=o.top+o.height/2,r=Math.max(8,Math.hypot(t.clientX-a,t.clientY-n));this.host._rlResize={id:"rl_"+(e.id||""),space:s,k0:this._labelScale(e),cx:a,cy:n,d0:r},nh(t)}_rlResizeMove(t){const e=this.host._rlResize;if(!e)return;t.stopPropagation();const s=Math.max(8,Math.hypot(t.clientX-e.cx,t.clientY-e.cy)),i=Math.min(3,Math.max(.5,e.k0*(s/e.d0))),o=this.host._layout[e.id];if(o)this.host._layout={...this.host._layout,[e.id]:{...o,k:i}};else{const t=e.id.slice(3),s=this.host._spaceModelById(e.space);if(!s)return;const o=s.rooms.find(e=>e.id===t);if(!o)return;const a=this._labelPos(o,e.space);this.host._layout={...this.host._layout,[e.id]:{s:e.space,x:a.x/oh,y:a.y/oh,k:i}}}this.host._dirtyPos.add(e.id)}_rlResizeUp(){this.host._rlResize&&(this.host._rlResize=null,this.host._persistLayout())}_renderRoomGear(t,e,s){if(!t.id)return a;let i=null;if(t.poly?(i=this.host._gearPtCache.get(t.poly)||null,i||(i=re(t.poly),this.host._gearPtCache.set(t.poly,i))):null!=t.x&&null!=t.y&&(i=[t.x+(t.w||0)/2,t.y+(t.h||0)/2]),!i)return a;const n=(i[0]-s.x)/s.w*100,r=(i[1]-s.y)/s.h*100;return o``}_resetRoomDialogFields(){this.host._roomEditId=null,this.host._roomFill="",this.host._roomCustomFill=null,this.host._roomTempSrc="",this.host._roomHumSrc="",this.host._roomSrcOpen=null,this.host._roomSrcFilter="",this.host._roomNameScale=1,this.host._roomLabelScale=1}_openRoomEdit(t){if(!t.id)return;this.host._roomEditId=t.id,this.host._nameSel=t.name||"",this.host._areaSel=t.area||"",this.host._roomFill="glow"===t.settings?.fill_mode?"":t.settings?.fill_mode||"";const e=t.settings?.custom_fill;this.host._roomCustomFill=e&&"object"==typeof e?ps(e,ds(this.host._curSpaceCfg).customFill):null,this.host._roomTempSrc=t.settings?.temp_source||"",this.host._roomHumSrc=t.settings?.hum_source||"",this.host._roomNameScale=Gs(t.settings?.name_scale),this.host._roomLabelScale=Gs(t.settings?.label_scale),this.host._roomSrcOpen=null,this.host._roomSrcFilter="",this.host._roomDialog=!0}_roomSettingsFromDialog(){const t={};return this.host._roomFill&&(t.fill_mode=this.host._roomFill),this.host._roomCustomFill&&(t.custom_fill=this.host._roomCustomFill),this.host._roomTempSrc&&(t.temp_source=this.host._roomTempSrc),this.host._roomHumSrc&&(t.hum_source=this.host._roomHumSrc),1!==this.host._roomNameScale&&(t.name_scale=this.host._roomNameScale),1!==this.host._roomLabelScale&&(t.label_scale=this.host._roomLabelScale),Object.keys(t).length?t:null}_saveRoomEdit(){const t=this.host._curSpaceCfg,e=t?.rooms.find(t=>t.id===this.host._roomEditId);if(!e)return this.host._roomDialog=!1,void(this.host._roomEditId=null);e.name=this.host._nameSel.trim()||e.name,e.area=this.host._areaSel||null;const s=e.settings||{},i={...s};this.host._roomFill?i.fill_mode=this.host._roomFill:delete i.fill_mode,this.host._roomCustomFill?i.custom_fill=this.host._roomCustomFill:delete i.custom_fill,"glow"===s.fill_mode&&"boolean"!=typeof s.glow&&(i.glow=!0),this.host._roomTempSrc?i.temp_source=this.host._roomTempSrc:delete i.temp_source,this.host._roomHumSrc?i.hum_source=this.host._roomHumSrc:delete i.hum_source,1!==this.host._roomNameScale?i.name_scale=this.host._roomNameScale:delete i.name_scale,1!==this.host._roomLabelScale?i.label_scale=this.host._roomLabelScale:delete i.label_scale,Object.keys(i).length?e.settings=i:delete e.settings,this._saveConfig(),this.host._roomDialog=!1,this.host._roomEditId=null,this.host._nameSel="",this.host._areaSel="",this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate(),this.host._showToast(this.host._t("toast.room_updated"))}_roomSrcCandidates(){const t=this.host._planHass,e=x(this.host._markers),s=this.host._roomSrcFilter.trim().toLowerCase(),i=[];for(const o of Object.values(t.devices)){if("service"===o.entry_type)continue;if(e.devices.has(o.id))continue;const t=(o.name_by_user||o.name||o.id).trim();s&&!t.toLowerCase().includes(s)||i.push({value:"device:"+o.id,label:t,sub:o.model||this.host._t("marker.sub_device")})}for(const[o,a]of Object.entries(t.entities)){if(!o.startsWith("sensor.")||a.hidden)continue;if(S(t,o,e))continue;const n=a.name||t.states[o]?.attributes?.friendly_name||o;s&&!(n+" "+o).toLowerCase().includes(s)||i.push({value:"entity:"+o,label:n,sub:o})}return i.sort((t,e)=>t.label.localeCompare(e.label)),i.slice(0,200)}_roomSrcLabel(t){const e=t.indexOf(":"),s=t.slice(0,e),i=t.slice(e+1);return"device"===s?this.host._fullRegistryHass.devices[i]?.name_by_user||this.host._fullRegistryHass.devices[i]?.name||i:this.host._fullRegistryHass.entities[i]?.name||this.host.hass.states[i]?.attributes?.friendly_name||i}_labelPos(t,e){const s=this.host._layout["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*ah,y:s.y*ah};const i=this._snap(this.host._roomCenter(t));return{x:i[0],y:i[1]}}_labelDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=this._labelPos(e,s);this.host._drag={id:"rl_"+(e.id||""),sx:t.clientX,sy:t.clientY,ox:i.x,oy:i.y,moved:!1},rh(t),this.host._tip=null}_labelMove(t,e,s){const i="rl_"+(e.id||"");if(!this.host._drag||this.host._drag.id!==i)return;const o=this.host._stageEl;if(!o)return;const a=this.host._spaceModelById(s);if(!a)return;const n=a.vb,r=o.getBoundingClientRect(),h=this.host._viewOr(n),l=(t.clientX-this.host._drag.sx)/r.width*h.w,c=(t.clientY-this.host._drag.sy)/r.height*h.h;Math.abs(t.clientX-this.host._drag.sx)+Math.abs(t.clientY-this.host._drag.sy)>3&&(this.host._drag.moved=!0);const d=Rt(this.host._drag.ox+l),p=Rt(this.host._drag.oy+c);this.host._savePos({id:i,space:s},d,p)}_labelUp(t){const e="rl_"+(t.id||"");if(!this.host._drag||this.host._drag.id!==e)return;const s=this.host._drag.moved;this.host._drag=s?this.host._drag:null,s&&window.setTimeout(()=>this.host._drag=null,0)}_labelScale(t){const e=this.host._layout["rl_"+(t.id||"")]?.k;return"number"==typeof e&&Number.isFinite(e)?Math.min(3,Math.max(.5,e)):1}_rlResizeDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=t.target.closest(".roomlabel");if(!i)return;const o=i.getBoundingClientRect(),a=o.left+o.width/2,n=o.top+o.height/2,r=Math.max(8,Math.hypot(t.clientX-a,t.clientY-n));this.host._rlResize={id:"rl_"+(e.id||""),space:s,k0:this._labelScale(e),cx:a,cy:n,d0:r},rh(t)}_rlResizeMove(t){const e=this.host._rlResize;if(!e)return;t.stopPropagation();const s=Math.max(8,Math.hypot(t.clientX-e.cx,t.clientY-e.cy)),i=Math.min(3,Math.max(.5,e.k0*(s/e.d0))),o=this.host._layout[e.id];if(o)this.host._layout={...this.host._layout,[e.id]:{...o,k:i}};else{const t=e.id.slice(3),s=this.host._spaceModelById(e.space);if(!s)return;const o=s.rooms.find(e=>e.id===t);if(!o)return;const a=this._labelPos(o,e.space);this.host._layout={...this.host._layout,[e.id]:{s:e.space,x:a.x/ah,y:a.y/ah,k:i}}}this.host._dirtyPos.add(e.id)}_rlResizeUp(){this.host._rlResize&&(this.host._rlResize=null,this.host._persistLayout())}_renderRoomGear(t,e,s){if(!t.id)return a;let i=null;if(t.poly?(i=this.host._gearPtCache.get(t.poly)||null,i||(i=re(t.poly),this.host._gearPtCache.set(t.poly,i))):null!=t.x&&null!=t.y&&(i=[t.x+(t.w||0)/2,t.y+(t.h||0)/2]),!i)return a;const n=(i[0]-s.x)/s.w*100,r=(i[1]-s.y)/s.h*100;return o` - `}_markerValueBadgeFields(t){return Qs({touched:t.valueBadgeTouched,originalHas:t.originalHasValueBadge,original:t.originalValueBadge,enabled:t.valueBadgeEnabled,source:t.valueBadgeSource,position:t.valueBadgePosition})}_markerDraft(t){if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return null;const e=as(t.room),s=Xe(t.binding,t.devId,()=>"__hp_device_preview__"),i=this.host._markers.find(e=>e.id===s||e.id===t.devId),o=es(t.binding,t.controls,this.host._bindingEntities(t.binding)),a=this._effectiveStoredTapAction(t),n={id:s,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&1.5!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle||null,...this._markerTapActionFields(t),...this._markerToggleEntityFields(t),tap_target:"run"===a&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:o.length?o:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=sh(t.glowRadius);return null==e||e<=0?null:Math.round(this.host._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:t.hideFromPlan,vacuum:i?.vacuum||null};return("virtual"===t.binding||t.room)&&(n.space=e?.space||("virtual"===t.binding?this.host._space:null),n.area=e?.area||null,n.room_id=e?.roomId||null),n}_markerPreviewDevice(t){const e=this._markerDraft(t);if(!e)return null;const s=`${this.host._haRegistry.revision}\n${Ot(this.host._markers)}\n${JSON.stringify(e)}`;if(this.host._markerPreviewMemo?.key===s)return this.host._markerPreviewMemo.device;const i=ti({hass:this.host.hass,registry:this.host._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this.host._areaToSpace).map(([t,e])=>[t,e.space])),marker:e,siblingMarkers:this.host._markers,settings:this.host._settings,excluded:this.host._excluded,showAll:this.host._showAll,firstSpaceId:this.host._model[0]?.id||this.host._space,loc:t=>this.host._t(t),iconRules:this.host._iconRules});return this.host._markerPreviewMemo={key:s,device:i},i}_markerPreviewDevices(t){const e=this.host._markerPreviewDevicesMemo;if(e?.base===this.host._devices&&e.preview===t)return e.devices;const s=[...this.host._devices.filter(e=>e.id!==t.id),t];return this.host._markerPreviewDevicesMemo={base:this.host._devices,preview:t,devices:s},s}_toggleIntent(t,e=this.host._devices){return this.host._toggleIntent(t,e)}_toggleIntentForDialog(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s=this._markerPreviewDevices(e);return this._toggleIntent(e,s)}_toggleStateText(t,e){return this.host._toggleStateText(t,e)}_toggleConfirmationStateText(t){return this.host._toggleConfirmationStateText(t)}_toggleConfirmationLines(t){return this.host._toggleConfirmationLines(t)}_toggleHintLines(t){if(!t)return[];const e=t=>this.host._t(`marker.toggle_effect_${t.replace("-","_")}`),s=t=>this.host._t(`marker.toggle_skip_${t.replace("-","_")}`);return ei(t,{single:t=>{if("via"in t&&"virtual-light"===t.via)return this.host._t("marker.virtual_light_target",{name:t.name});const e=t.entityId||("ref"in t?t.ref:""),s=t.name||e;return this.host._t("marker.toggle_hint_single",{name:s,id:e})},group:t=>this.host._t("marker.toggle_hint_group",{count:t.length,names:t.map(t=>`${t.name} (${t.entityId})`).join(", ")}),currentNext:(t,s)=>"virtual-light"===t.via?this.host._t("marker.virtual_light_current",{state:this.host._t("on"===t.state?"marker.virtual_light_state_on":"marker.virtual_light_state_off"),effect:e(s)}):this.host._t("marker.toggle_hint_current",{state:this._toggleStateText(t.entityId,t.state),effect:e(s)}),groupCurrentNext:(t,s)=>this.host._t("marker.toggle_hint_group_current",{on:t.filter(t=>"on"===t.state).length,count:t.length,effect:e(s)}),skipped:t=>this.host._t("marker.toggle_hint_skipped",{count:t.length,targets:t.map(t=>{const e=t.entityId||t.ref;return`${t.name||e} (${e}: ${s(t.reason)})`}).join(", ")}),none:t=>this.host._t(`marker.toggle_none_${t.replaceAll("-","_")}`)})}_effectiveStoredTapAction(t,e){return t.tapActionTouched?t.tapAction:ss(t.originalHasTapAction?t.originalTapAction:null,e)}_effectiveMarkerTapAction(t,e=this._markerPreviewDevice(t)){return this._effectiveStoredTapAction(t,e?.primary?.split(".")[0])}_announceToggleDraft(t){const e=this._markerPreviewDevice(t),s=t.tapActionTouched?t:{...t,tapAction:this._effectiveMarkerTapAction(t,e)},i="toggle"===s.tapAction?this._toggleHintLines(this._toggleIntentForDialog(s)).join(" "):"";return{...s,tapHintAnnouncement:i}}_valueBadgeForBinding(t,e){const s={...t,binding:e,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0},i=this._markerPreviewDevice(s);if(!i)return{valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0};const o=[...this.host._devices.filter(t=>t.id!==i.id),i],a=Qe(this.host._planHass,i,o),n=ts(this.host._planHass,i,a);return{valueBadgeEnabled:t.valueBadgeEnabled&&!!n,valueBadgeSource:n,valueBadgeTouched:!0}}_markerSpatialSource(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s={...e,hidden:!1};return si(ii(this.host._planHass,[...this.host._devices.filter(t=>t.id!==s.id),s],null,this.host._virtualLights).filter(t=>t.device.id===s.id))}_markerAutoHasSpatialSource(t){const e={...t,lightRole:"auto",lightRoleTouched:!0},s=this._markerPreviewDevice(e);return!!s&&oi(this.host._planHass,{...s})}_setMarkerLightRole(t){const e=this.host._markerDialog;e&&(this.host._markerDialog={...e,lightRole:t,lightRoleTouched:!0})}_controlRefInfo(t){if(!t.startsWith("marker:"))return{label:this.host.hass.states[t]?.attributes?.friendly_name||t,sub:t,icon:t.startsWith("light.")?"mdi:lightbulb":"mdi:toggle-switch",warning:!this.host._planEntityAvailable(t)};const e=t.slice(7),s=this.host._markers.find(t=>t.id===e),i=this.host._devices.find(t=>t.id===e);if(!s||s.removed||!0!==s.is_light)return{label:this.host._t("marker.control_missing_label"),sub:`${this.host._t("marker.control_broken")} (${t})`,icon:"mdi:alert-outline",warning:!0};const o=this.host._serverCfg?.spaces.find(t=>t.id===(i?.space||s.space)),a=!!i&&ai(i).length>0;return{label:s.name||i?.name||e,sub:[o?.title||i?.space||s.space,s.room_id||i?.area,a?"":this.host._t("marker.control_passive")].filter(Boolean).join(" · "),icon:s.icon||i?.icon||"mdi:lightbulb-outline",warning:!0===s.hidden||!0===i?.hidden}}_valueBadgeCandidateLabel(t){const e=t.source;if("derived_lqi"===e.kind)return this.host._t("marker.value_badge_lqi");if("derived_marker_state"===e.kind)return this.host._t("marker.value_badge_marker_state",{name:t.label});if("entity_state"===e.kind)return this.host._t("marker.value_badge_state",{name:t.label});const s=this.host.hass.states[e.entity_id]?.attributes?.friendly_name||this.host._fullRegistryHass.entities[e.entity_id]?.name||e.entity_id;return this.host._t(`marker.value_badge_attr_${e.attribute}`,{name:s})}_controlCandidates(t){const e=this._markerDraft(t)?.id||t.devId||"",s=[],i=new Set;for(const t of this.host._markers){if(t.id===e||t.removed||t.hidden||!0!==t.is_light)continue;if("virtual"!==t.binding&&"active"!==this.host._bindingStatus(t.binding).kind)continue;const o=this.host._devices.find(e=>e.id===t.id);if(!o||o.hidden)continue;const a=this._controlRefInfo(`marker:${t.id}`);for(const t of ai(o))i.add(t);s.push({value:`marker:${t.id}`,label:a.label,sub:a.sub,icon:a.icon,search:`${a.label} ${a.sub} ${t.id} ${ai(o).join(" ")}`.toLowerCase()})}const o=Object.keys(this.host.hass.states||{}).filter(e=>ni(e)&&!i.has(e)&&ri(t.binding,[e],this.host._bindingEntities(t.binding)).length>0&&this.host._planEntityAvailable(e)).map(t=>{const e=this._controlRefInfo(t);return{value:t,label:e.label,sub:t,icon:e.icon,search:`${e.label} ${t}`.toLowerCase()}}),a=t.controlsFilter.trim().toLowerCase();return[...s,...o].filter(e=>!t.controls.includes(e.value)&&(!a||e.search.includes(a))).slice(0,12).map(({search:t,...e})=>e)}_addControlRef(t,e){if(e.startsWith("marker:")){const s=this._markerDraft(t)?.id||t.devId||"";if(!s)return void this.host._showToast(this.host._t("toast.marker_binding_required"));const i=e.slice(7),o=this._markerDraft(t),a=o?[...this.host._markers.filter(t=>t.id!==s),o]:this.host._markers;if(hi(a,s,i))return void this.host._showToast(this.host._t("toast.marker_control_cycle"))}this.host._markerDialog=this._announceToggleDraft({...t,controls:[...t.controls,e],controlsFilter:""})}_setMarkerGlowMode(t){const e=this.host._markerDialog;if(!e)return;if("auto"===t)return void(this.host._markerDialog={...e,glowMode:t,glowTouched:!0});const s=this._markerSpatialSource(e),i=li(s?this.host._planHass.states[s.eid]:void 0,null,this.host._fillColors.glow_light.c),o=!e.glowColorDrafted,a="fixed"===t&&!e.glowBrightnessDrafted;this.host._markerDialog={...e,glowMode:t,glowColor:o?i.c:e.glowColor,glowBrightness:a?Math.max(1,Math.round(100*i.bri)):e.glowBrightness,glowColorDrafted:!0,glowBrightnessDrafted:e.glowBrightnessDrafted||"fixed"===t,glowTouched:!0}}_renderMarkerDialog(){const t=this.host._markerDialog,e="virtual"===t.bindingMode,s=this._bindingCandidates();this.host._bindingEntities(t.binding);const i=e?null:this.host._bindingStatus(t.binding),n=!e&&this.host._bindingHasHaPage(t.binding),r=this._markerPreviewDevice(t),h=this._effectiveMarkerTapAction(t,r),l=r?ds(this.host._serverCfg?.spaces.find(t=>t.id===r.space)):null,c=r?this._markerPreviewDevices(r):this.host._devices,d="toggle"===h&&r?this._toggleIntent(r,c):null,p=this._toggleHintLines(d),_=r?ci(this.host._planHass,r,{liveStates:!1!==this.host._config?.live_states,showTemperature:!1!==this.host._config?.show_temperature,showSignal:l?.showLqi??!1!==this.host._config?.show_signal,designPreview:!0,activityRuntime:this.host._activityRt.get(r.id),lightDevices:c,registryHass:this.host._fullRegistryHass,reducedMotion:this.host._reducedMotion}):null,u=r?Qe(this.host._planHass,r,c):[],m=r?ts(this.host._planHass,r,u):null,g=t.valueBadgeTouched?t.valueBadgeEnabled:!!_?.valueBadge,f=t.valueBadgeTouched?t.valueBadgeSource:_?.valueBadge?.source||t.valueBadgeSource,b=t.valueBadgeTouched?t.valueBadgePosition:_?.valueBadge?.position||t.valueBadgePosition,v=di(f),y=!!f&&!u.some(t=>t.key===v),$=u.find(t=>t.key===v),w=_?.valueSource?_.valueSource.attribute?`attr:${_.valueSource.eid}:${_.valueSource.attribute}`:`state:${_.valueSource.eid}`:"",k=this._markerAutoHasSpatialSource(t),x=!!r&&pi(this.host._planHass,{...r}),S=_i(t.lightRole,k,x,t.glowMode),M=!S.sourceExists,D=!S.fromSourceEnabled,C=S.passive,P=S.effectiveMode,I="never"===t.lightRole?this.host._t("marker.glow_disabled_never"):"auto"!==t.lightRole||k?C?this.host._t("marker.glow_passive_hint"):this.host._t("marker.glow_disabled_no_entity"):this.host._t("marker.glow_disabled_auto"),T=r?ai(r):[],R=r&&ui(r)||"",z=!!t.lightEntity&&!T.includes(t.lightEntity),F=r?mi(r):[],A=!!t.toggleEntity&&!F.includes(t.toggleEntity),N="toggle"===h?this._toggleIntentForDialog({...t,toggleEntity:"",toggleEntityTouched:!0}):null,H=N?[...N.targets,...N.skippedTargets].map(t=>t.entityId||("ref"in t?t.ref:"")).filter(Boolean).join(", "):"",L=(()=>{if(e)return null;const i=s.find(e=>e.value===t.binding);if(i)return i.label;const[o,a]=t.binding.split(":");return"device"===o?this.host._fullRegistryHass.devices[a]?.name_by_user||this.host._fullRegistryHass.devices[a]?.name||a:this.host._fullRegistryHass.entities[a]?.name||this.host.hass.states[a]?.attributes?.friendly_name||a})();return o``}_markerValueBadgeFields(t){return Qs({touched:t.valueBadgeTouched,originalHas:t.originalHasValueBadge,original:t.originalValueBadge,enabled:t.valueBadgeEnabled,source:t.valueBadgeSource,position:t.valueBadgePosition})}_markerValueSourceFields(t){return ti({touched:t.valueSourceTouched,originalHas:t.originalHasValueSource,original:t.originalValueSource,source:t.valueSource})}_markerDraft(t){if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return null;const e=as(t.room),s=Xe(t.binding,t.devId,()=>"__hp_device_preview__"),i=this.host._markers.find(e=>e.id===s||e.id===t.devId),o=es(t.binding,t.controls,this.host._bindingEntities(t.binding)),a=this._effectiveStoredTapAction(t),n={id:s,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&1.5!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle||null,...this._markerTapActionFields(t),...this._markerToggleEntityFields(t),tap_target:"run"===a&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:o.length?o:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),...this._markerValueSourceFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=ih(t.glowRadius);return null==e||e<=0?null:Math.round(this.host._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:t.hideFromPlan,vacuum:i?.vacuum||null};return("virtual"===t.binding||t.room)&&(n.space=e?.space||("virtual"===t.binding?this.host._space:null),n.area=e?.area||null,n.room_id=e?.roomId||null),n}_markerPreviewDevice(t){const e=this._markerDraft(t);if(!e)return null;const s=`${this.host._haRegistry.revision}\n${Ot(this.host._markers)}\n${JSON.stringify(e)}`;if(this.host._markerPreviewMemo?.key===s)return this.host._markerPreviewMemo.device;const i=ei({hass:this.host.hass,registry:this.host._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this.host._areaToSpace).map(([t,e])=>[t,e.space])),marker:e,siblingMarkers:this.host._markers,settings:this.host._settings,excluded:this.host._excluded,showAll:this.host._showAll,firstSpaceId:this.host._model[0]?.id||this.host._space,loc:t=>this.host._t(t),iconRules:this.host._iconRules});return this.host._markerPreviewMemo={key:s,device:i},i}_markerPreviewDevices(t){const e=this.host._markerPreviewDevicesMemo;if(e?.base===this.host._devices&&e.preview===t)return e.devices;const s=[...this.host._devices.filter(e=>e.id!==t.id),t];return this.host._markerPreviewDevicesMemo={base:this.host._devices,preview:t,devices:s},s}_toggleIntent(t,e=this.host._devices){return this.host._toggleIntent(t,e)}_toggleIntentForDialog(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s=this._markerPreviewDevices(e);return this._toggleIntent(e,s)}_toggleStateText(t,e){return this.host._toggleStateText(t,e)}_toggleConfirmationStateText(t){return this.host._toggleConfirmationStateText(t)}_toggleConfirmationLines(t){return this.host._toggleConfirmationLines(t)}_toggleHintLines(t){if(!t)return[];const e=t=>this.host._t(`marker.toggle_effect_${t.replace("-","_")}`),s=t=>this.host._t(`marker.toggle_skip_${t.replace("-","_")}`);return si(t,{single:t=>{if("via"in t&&"virtual-light"===t.via)return this.host._t("marker.virtual_light_target",{name:t.name});const e=t.entityId||("ref"in t?t.ref:""),s=t.name||e;return this.host._t("marker.toggle_hint_single",{name:s,id:e})},group:t=>this.host._t("marker.toggle_hint_group",{count:t.length,names:t.map(t=>`${t.name} (${t.entityId})`).join(", ")}),currentNext:(t,s)=>"virtual-light"===t.via?this.host._t("marker.virtual_light_current",{state:this.host._t("on"===t.state?"marker.virtual_light_state_on":"marker.virtual_light_state_off"),effect:e(s)}):this.host._t("marker.toggle_hint_current",{state:this._toggleStateText(t.entityId,t.state),effect:e(s)}),groupCurrentNext:(t,s)=>this.host._t("marker.toggle_hint_group_current",{on:t.filter(t=>"on"===t.state).length,count:t.length,effect:e(s)}),skipped:t=>this.host._t("marker.toggle_hint_skipped",{count:t.length,targets:t.map(t=>{const e=t.entityId||t.ref;return`${t.name||e} (${e}: ${s(t.reason)})`}).join(", ")}),none:t=>this.host._t(`marker.toggle_none_${t.replaceAll("-","_")}`)})}_effectiveStoredTapAction(t,e){return t.tapActionTouched?t.tapAction:ss(t.originalHasTapAction?t.originalTapAction:null,e)}_effectiveMarkerTapAction(t,e=this._markerPreviewDevice(t)){return this._effectiveStoredTapAction(t,e?.primary?.split(".")[0])}_announceToggleDraft(t){const e=this._markerPreviewDevice(t),s=t.tapActionTouched?t:{...t,tapAction:this._effectiveMarkerTapAction(t,e)},i="toggle"===s.tapAction?this._toggleHintLines(this._toggleIntentForDialog(s)).join(" "):"";return{...s,tapHintAnnouncement:i}}_valueBadgeForBinding(t,e){const s={...t,binding:e,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0},i=this._markerPreviewDevice(s);if(!i)return{valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0};const o=[...this.host._devices.filter(t=>t.id!==i.id),i],a=Qe(this.host._planHass,i,o),n=ts(this.host._planHass,i,a);return{valueBadgeEnabled:t.valueBadgeEnabled&&!!n,valueBadgeSource:n,valueBadgeTouched:!0}}_markerSpatialSource(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s={...e,hidden:!1};return ii(oi(this.host._planHass,[...this.host._devices.filter(t=>t.id!==s.id),s],null,this.host._virtualLights).filter(t=>t.device.id===s.id))}_markerAutoHasSpatialSource(t){const e={...t,lightRole:"auto",lightRoleTouched:!0},s=this._markerPreviewDevice(e);return!!s&&ai(this.host._planHass,{...s})}_setMarkerLightRole(t){const e=this.host._markerDialog;e&&(this.host._markerDialog={...e,lightRole:t,lightRoleTouched:!0})}_controlRefInfo(t){if(!t.startsWith("marker:"))return{label:this.host.hass.states[t]?.attributes?.friendly_name||t,sub:t,icon:t.startsWith("light.")?"mdi:lightbulb":"mdi:toggle-switch",warning:!this.host._planEntityAvailable(t)};const e=t.slice(7),s=this.host._markers.find(t=>t.id===e),i=this.host._devices.find(t=>t.id===e);if(!s||s.removed||!0!==s.is_light)return{label:this.host._t("marker.control_missing_label"),sub:`${this.host._t("marker.control_broken")} (${t})`,icon:"mdi:alert-outline",warning:!0};const o=this.host._serverCfg?.spaces.find(t=>t.id===(i?.space||s.space)),a=!!i&&ni(i).length>0;return{label:s.name||i?.name||e,sub:[o?.title||i?.space||s.space,s.room_id||i?.area,a?"":this.host._t("marker.control_passive")].filter(Boolean).join(" · "),icon:s.icon||i?.icon||"mdi:lightbulb-outline",warning:!0===s.hidden||!0===i?.hidden}}_valueBadgeCandidateLabel(t){const e=t.source;if("derived_lqi"===e.kind)return this.host._t("marker.value_badge_lqi");if("derived_marker_state"===e.kind)return this.host._t("marker.value_badge_marker_state",{name:t.label});if("entity_state"===e.kind)return this.host._t("marker.value_badge_state",{name:t.label});const s=this.host.hass.states[e.entity_id]?.attributes?.friendly_name||this.host._fullRegistryHass.entities[e.entity_id]?.name||e.entity_id;return this.host._t(`marker.value_badge_attr_${e.attribute}`,{name:s})}_controlCandidates(t){const e=this._markerDraft(t)?.id||t.devId||"",s=[],i=new Set;for(const t of this.host._markers){if(t.id===e||t.removed||t.hidden||!0!==t.is_light)continue;if("virtual"!==t.binding&&"active"!==this.host._bindingStatus(t.binding).kind)continue;const o=this.host._devices.find(e=>e.id===t.id);if(!o||o.hidden)continue;const a=this._controlRefInfo(`marker:${t.id}`);for(const t of ni(o))i.add(t);s.push({value:`marker:${t.id}`,label:a.label,sub:a.sub,icon:a.icon,search:`${a.label} ${a.sub} ${t.id} ${ni(o).join(" ")}`.toLowerCase()})}const o=Object.keys(this.host.hass.states||{}).filter(e=>ri(e)&&!i.has(e)&&hi(t.binding,[e],this.host._bindingEntities(t.binding)).length>0&&this.host._planEntityAvailable(e)).map(t=>{const e=this._controlRefInfo(t);return{value:t,label:e.label,sub:t,icon:e.icon,search:`${e.label} ${t}`.toLowerCase()}}),a=t.controlsFilter.trim().toLowerCase();return[...s,...o].filter(e=>!t.controls.includes(e.value)&&(!a||e.search.includes(a))).slice(0,12).map(({search:t,...e})=>e)}_addControlRef(t,e){if(e.startsWith("marker:")){const s=this._markerDraft(t)?.id||t.devId||"";if(!s)return void this.host._showToast(this.host._t("toast.marker_binding_required"));const i=e.slice(7),o=this._markerDraft(t),a=o?[...this.host._markers.filter(t=>t.id!==s),o]:this.host._markers;if(li(a,s,i))return void this.host._showToast(this.host._t("toast.marker_control_cycle"))}this.host._markerDialog=this._announceToggleDraft({...t,controls:[...t.controls,e],controlsFilter:""})}_setMarkerGlowMode(t){const e=this.host._markerDialog;if(!e)return;if("auto"===t)return void(this.host._markerDialog={...e,glowMode:t,glowTouched:!0});const s=this._markerSpatialSource(e),i=ci(s?this.host._planHass.states[s.eid]:void 0,null,this.host._fillColors.glow_light.c),o=!e.glowColorDrafted,a="fixed"===t&&!e.glowBrightnessDrafted;this.host._markerDialog={...e,glowMode:t,glowColor:o?i.c:e.glowColor,glowBrightness:a?Math.max(1,Math.round(100*i.bri)):e.glowBrightness,glowColorDrafted:!0,glowBrightnessDrafted:e.glowBrightnessDrafted||"fixed"===t,glowTouched:!0}}_renderMarkerDialog(){const t=this.host._markerDialog,e="virtual"===t.bindingMode,s=this._bindingCandidates();this.host._bindingEntities(t.binding);const i=e?null:this.host._bindingStatus(t.binding),n=!e&&this.host._bindingHasHaPage(t.binding),r=this._markerPreviewDevice(t),h=this._effectiveMarkerTapAction(t,r),l=r?ds(this.host._serverCfg?.spaces.find(t=>t.id===r.space)):null,c=r?this._markerPreviewDevices(r):this.host._devices,d="toggle"===h&&r?this._toggleIntent(r,c):null,p=this._toggleHintLines(d),u=r?di(this.host._planHass,r,{liveStates:!1!==this.host._config?.live_states,showTemperature:!1!==this.host._config?.show_temperature,showSignal:l?.showLqi??!1!==this.host._config?.show_signal,designPreview:!0,activityRuntime:this.host._activityRt.get(r.id),lightDevices:c,registryHass:this.host._fullRegistryHass,reducedMotion:this.host._reducedMotion}):null,_=r?Qe(this.host._planHass,r,c):[],m=r?ts(this.host._planHass,r,_):null,g=t.valueBadgeTouched?t.valueBadgeEnabled:!!u?.valueBadge,f=t.valueBadgeTouched?t.valueBadgeSource:u?.valueBadge?.source||t.valueBadgeSource,b=t.valueBadgeTouched?t.valueBadgePosition:u?.valueBadge?.position||t.valueBadgePosition,v=pi(f),y=!!f&&!_.some(t=>t.key===v),$=_.find(t=>t.key===v),w=pi(t.valueSource),k=!(!t.valueSource||w&&_.some(t=>t.key===w)),x=_.find(t=>t.key===w),S=u?.valueSource?.sourceKey||"",M=this._markerAutoHasSpatialSource(t),D=!!r&&ui(this.host._planHass,{...r}),C=_i(t.lightRole,M,D,t.glowMode),P=!C.sourceExists,I=!C.fromSourceEnabled,T=C.passive,R=C.effectiveMode,z="never"===t.lightRole?this.host._t("marker.glow_disabled_never"):"auto"!==t.lightRole||M?T?this.host._t("marker.glow_passive_hint"):this.host._t("marker.glow_disabled_no_entity"):this.host._t("marker.glow_disabled_auto"),F=r?ni(r):[],A=r&&mi(r)||"",N=!!t.lightEntity&&!F.includes(t.lightEntity),H=r?gi(r):[],L=!!t.toggleEntity&&!H.includes(t.toggleEntity),O="toggle"===h?this._toggleIntentForDialog({...t,toggleEntity:"",toggleEntityTouched:!0}):null,E=O?[...O.targets,...O.skippedTargets].map(t=>t.entityId||("ref"in t?t.ref:"")).filter(Boolean).join(", "):"",B=(()=>{if(e)return null;const i=s.find(e=>e.value===t.binding);if(i)return i.label;const[o,a]=t.binding.split(":");return"device"===o?this.host._fullRegistryHass.devices[a]?.name_by_user||this.host._fullRegistryHass.devices[a]?.name||a:this.host._fullRegistryHass.entities[a]?.name||this.host.hass.states[a]?.attributes?.friendly_name||a})();return o`this._closeMarkerDialog()}>
@@ -1948,7 +1948,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
@@ -1964,7 +1964,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
${"ha"===t.bindingMode?o` ${t.bindingOpen?o`
@@ -1973,7 +1973,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 @input=${e=>this.host._markerDialog={...t,bindingFilter:e.target.value}} />
${s.map(e=>o`
{const s={...t,binding:e.value,bindingOpen:!1,controls:es(e.value,t.controls,this.host._bindingEntities(e.value)),autoIcon:this.host._autoIconForBinding(e.value)};this.host._markerDialog=this._announceToggleDraft({...s,...this._valueBadgeForBinding(s,e.value)})}}> + @click=${()=>{const s={...t,binding:e.value,bindingOpen:!1,controls:es(e.value,t.controls,this.host._bindingEntities(e.value)),autoIcon:this.host._autoIconForBinding(e.value)};this.host._markerDialog=this._announceToggleDraft({...s,...this._valueBadgeForBinding(s,e.value),valueSource:null,valueSourceTouched:!0})}}> ${e.label}${e.sub}
`)} ${s.length?a:o`
${this.host._t("marker.nothing_found")}
`} @@ -1996,28 +1996,28 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 - ${"toggle"===h&&(F.length>1||A)?o`
+ ${"toggle"===h&&(H.length>1||L)?o`
${this._help("marker.toggle_entity.help")}
- ${A?o`

+ ${L?o`

- ${this.host._t("marker.toggle_entity_missing",{entity:t.toggleEntity,fallback:H||this.host._t("marker.toggle_entity_none")})} + ${this.host._t("marker.toggle_entity_missing",{entity:t.toggleEntity,fallback:E||this.host._t("marker.toggle_entity_none")})}

`:a}
`:a} ${"toggle"===h?o`
@@ -2072,7 +2072,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
+ ${this.host._t(M?"marker.light_role_auto_yes":"marker.light_role_auto_no")} @@ -2082,55 +2082,55 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
- ${"always"===t.lightRole&&(T.length>1||z)?o`
+ ${"always"===t.lightRole&&(F.length>1||N)?o`
${this._help("marker.light_entity.help")}
- ${z?o`

+ ${N?o`

- ${this.host._t("marker.light_entity_missing",{entity:t.lightEntity,fallback:R||"—"})} + ${this.host._t("marker.light_entity_missing",{entity:t.lightEntity,fallback:A||"—"})}

`:a}
`:a} -
+
${this.host._t("marker.glow_color_label")}${this._help("marker.glow_mode.help")}
- ${"auto"!==P?o`
+ ${"auto"!==R?o`
{this.host._markerDialog={...t,glowMode:P,glowColor:e.detail.color,glowColorDrafted:!0,glowTouched:!0}}}> - ${"fixed"===P?o` + .disabled=${P} + @hp-color-opacity-change=${e=>{this.host._markerDialog={...t,glowMode:R,glowColor:e.detail.color,glowColorDrafted:!0,glowTouched:!0}}}> + ${"fixed"===R?o` ${this.host._t("marker.glow_brightness")} - ${this._rangeInput(1,100,1,t.glowBrightness,e=>{this.host._markerDialog={...t,glowMode:P,glowBrightness:e,glowBrightnessDrafted:!0,glowTouched:!0}},M,this.host._t("marker.glow_brightness"))} + ${this._rangeInput(1,100,1,t.glowBrightness,e=>{this.host._markerDialog={...t,glowMode:R,glowBrightness:e,glowBrightnessDrafted:!0,glowTouched:!0}},P,this.host._t("marker.glow_brightness"))} ${Math.round(t.glowBrightness)}%`:a}
`:a}
@@ -2141,15 +2141,15 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
this.host._markerDialog={...t,glowRadius:e.target.value}} /> ${this.host._imperial?this.host._t("gs.unit_ft"):this.host._t("gs.unit_m")}
- ${M||C?o`

- ${I} + ${P||T?o`

+ ${z}

`:a} @@ -2170,11 +2170,38 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 -

${this.host._t(Qr[t.display])}

+

${this.host._t(th[t.display])}

+ ${"value"===t.display?o`
+
+ + ${this._help("marker.value_source.help")} +
+ + ${x?o`

${x.technical}

`:a} + ${e?o`

+ + ${this.host._t("marker.preview.reason.value_virtual")} +

`:k?o`

+ ${this.host._t("marker.value_source_missing_hint")} +

`:a} +
`:a} ${"static_icon"===t.display&&this.host._bindingHasAlarm(t.binding)?o`
${this.host._t("marker.static_alarm_warning")} @@ -2182,12 +2209,12 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
${this.host._t("marker.value_badge_title")}${this._help("marker.value_badge.help")} ${"static_icon"===t.display?o`

${this.host._t("marker.value_badge_static")} -

`:u.length||t.valueBadgeSource?a:o`

+

`:_.length||t.valueBadgeSource?a:o`

${this.host._t("marker.value_badge_empty")}

`} ${g?o` @@ -2196,11 +2223,11 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 ${this._help("marker.value_badge_source.help")}
`:a} - ${_?o` + .deviceName=${t.name.trim()||r?.name||B||""}> `:o`
${this.host._t("marker.preview.select_source")} @@ -2276,7 +2303,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
${t.pdfs.map(t=>o` - ${t.name} + ${t.name} this._removeMarkerPdf(t.url)}>`)} `}
- `}}export{rh as EDITOR_RUNTIME_FINGERPRINT,hh as HouseplanEditorRuntime}; + `}}export{hh as EDITOR_RUNTIME_FINGERPRINT,lh as HouseplanEditorRuntime}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-eqCB-Ax0.js b/custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-BP9ODa6H.js similarity index 98% rename from custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-eqCB-Ax0.js rename to custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-BP9ODa6H.js index 7cb8b8a2..1edbd1cc 100644 --- a/custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-eqCB-Ax0.js +++ b/custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-BP9ODa6H.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";import{bT as t,bU as s,bV as e,bW as o,aq as a,bX as l,c0 as i,bY as h,bZ as c,b_ as n,b$ as r,A as p,b as _,cS as d,ap as u,ar as g,cT as m}from"./houseplan-card-B5mDyjuC.js";import{i as b,c as $,e as v,r as f,a as y,b as w,s as D,t as S}from"./backdrop-pick-DFFLwJ_d.js";const k=1e3,C=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},M="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";class F{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),c=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,n="none"===h.fill?{...c||e,a:0}:c;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:n,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:n,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:c,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await $(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await v(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return f(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??p}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return _`
${this.host._t("space.loading")}
`;const s=t.saved||[];if(!s.length)return _`
${this.host._t("space.no_saved")}
`;return _`
+globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";import{bT as t,bU as s,bV as e,bW as o,aq as a,bX as l,c0 as i,bY as h,bZ as c,b_ as n,b$ as r,A as p,b as _,cT as d,ap as u,ar as g,cU as m}from"./houseplan-card-wtxKVHkp.js";import{i as b,c as $,e as v,r as f,a as y,b as w,s as D,t as S}from"./backdrop-pick-TMl_2iBT.js";const k=1e3,C=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},M="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";class F{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),c=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,n="none"===h.fill?{...c||e,a:0}:c;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:n,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:n,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:c,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await $(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await v(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return f(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??p}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return _`
${this.host._t("space.loading")}
`;const s=t.saved||[];if(!s.length)return _`
${this.host._t("space.no_saved")}
`;return _`
${s.map(s=>{return _`
diff --git a/custom_components/houseplan/frontend/houseplan-card.js b/custom_components/houseplan/frontend/houseplan-card.js index 52c03bb8..70c7b46e 100644 --- a/custom_components/houseplan/frontend/houseplan-card.js +++ b/custom_components/houseplan/frontend/houseplan-card.js @@ -1 +1 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";try{await import("./houseplan-assets/houseplan-card-B5mDyjuC.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)} +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";try{await import("./houseplan-assets/houseplan-card-wtxKVHkp.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)} diff --git a/custom_components/houseplan/import_export.py b/custom_components/houseplan/import_export.py index 21455a24..40083fa6 100644 --- a/custom_components/houseplan/import_export.py +++ b/custom_components/houseplan/import_export.py @@ -510,6 +510,13 @@ def create_export( badge["enabled"] = False badge["source"] = None dropped_marker_links += 1 + value_source = marker.get("value_source") + ref = value_source.get("ref") if isinstance(value_source, dict) \ + and value_source.get("kind") == "derived_marker_state" else None + if isinstance(ref, str) and ref.startswith("marker:") \ + and ref[len("marker:"):] not in selected_ids: + marker.pop("value_source", None) + dropped_marker_links += 1 config = { "spaces": [_json_copy(space)], "markers": _json_copy(selected_markers), @@ -699,7 +706,7 @@ def _transfer_dropped_marker_links(document: dict[str, Any]) -> int: if not isinstance(transfer, dict): raise ImportFailure("invalid_format", "Transfer metadata must be an object") value = transfer.get("dropped_marker_links", 0) - maximum = MAX_MARKERS * (MAX_CONTROLS + 1) + maximum = MAX_MARKERS * (MAX_CONTROLS + 2) if isinstance(value, bool) or not isinstance(value, int) or not 0 <= value <= maximum: raise ImportFailure("invalid_format", "Invalid dropped marker link count") return value @@ -803,6 +810,16 @@ def _drop_invalid_import_marker_links( badge["enabled"] = False badge["source"] = None dropped += 1 + source = marker.get("value_source") + if isinstance(source, dict) and source.get("kind") == "derived_marker_state": + ref = source.get("ref") + target_id = ref[len("marker:"):] \ + if isinstance(ref, str) and ref.startswith("marker:") else "" + target = by_id.get(target_id) + if not target_id or target is None or target.get("removed") is True \ + or target.get("is_light") is not True: + marker.pop("value_source", None) + dropped += 1 return dropped @@ -1110,6 +1127,20 @@ def _repair_target_space_refs( preserve_related( marker_id, "marker.value_badge", old_target, resolve_marker, ) + source = marker.get("value_source") + if isinstance(source, dict) and source.get("kind") == "derived_marker_state": + ref = source.get("ref") + old_target = ref[len("marker:"):] \ + if isinstance(ref, str) and ref.startswith("marker:") else None + mapped = replace( + marker_id, "marker.value_source", old_target, resolve_marker_link, + ) + if mapped is not None: + source["ref"] = "marker:" + mapped + else: + preserve_related( + marker_id, "marker.value_source", old_target, resolve_marker, + ) # Destination wins collisions. Rekey only proven dead plan-owned keys; # opaque HA owners remain literal. @@ -1345,13 +1376,17 @@ def build_space_merge( source = badge.get("source") if isinstance(badge, dict) else None if isinstance(source, dict) and source.get("kind") == "derived_marker_state": dropped_marker_links += 1 + value_source = marker.get("value_source") + if isinstance(value_source, dict) \ + and value_source.get("kind") == "derived_marker_state": + dropped_marker_links += 1 marker["binding"] = "virtual" marker["display"] = "static_icon" for field in ( "area", "controls", "tap_action", "tap_target", "tap_confirm", "vacuum", "is_light", "use_climate_temp", "glow_color", "glow_radius_cm", "light_entity", "toggle_entity", "hidden", "removed", - "value_badge", + "value_badge", "value_source", ): marker.pop(field, None) output_markers.append(marker) @@ -1395,6 +1430,21 @@ def build_space_merge( badge["enabled"] = False badge["source"] = None dropped_marker_links += 1 + source = marker.get("value_source") + if isinstance(source, dict) and source.get("kind") == "derived_marker_state": + ref = source.get("ref") + old_target = ref[len("marker:"):] \ + if isinstance(ref, str) and ref.startswith("marker:") else "" + target = None if old_target in virtualized_targets else marker_map.get(old_target) + if target: + source["ref"] = "marker:" + target + _report_remap( + reference_report, "incoming", "marker.value_source", + str(marker.get("id", "?")), old_target, + ) + else: + marker.pop("value_source", None) + dropped_marker_links += 1 output_markers_by_id = { str(marker.get("id")): marker for marker in output_markers diff --git a/custom_components/houseplan/validation.py b/custom_components/houseplan/validation.py index 048daead..d41b0807 100644 --- a/custom_components/houseplan/validation.py +++ b/custom_components/houseplan/validation.py @@ -805,7 +805,7 @@ def _matching_previous_marker( def validate_marker_value_badges( config: dict, previous: dict | None = None, *, validate_all: bool = False ) -> None: - """Validate only new/changed badge data; dormant future/legacy data round-trips.""" + """Validate changed badge/face sources; dormant future data round-trips.""" markers = config.get("markers") or [] by_id = {str(marker.get("id")): marker for marker in markers} old_markers = (previous or {}).get("markers") or [] @@ -814,30 +814,14 @@ def validate_marker_value_badges( consumed_old_ids: set[str] = set() known_source_fields = {"kind", "entity_id", "attribute", "ref"} - for marker_id, marker in by_id.items(): - old_marker = _matching_previous_marker( - marker, marker_id, old_by_id, old_markers, new_ids, - consumed_old_ids, validate_all, - ) - badge = marker.get("value_badge") - old_badge = None if validate_all else (old_marker or {}).get("value_badge") - if not validate_all and badge == old_badge: - continue - if badge is None: - continue - if not isinstance(badge, dict): - raise MarkerControlError("invalid_value_badge", "Value badge must be an object") - if not isinstance(badge.get("enabled"), bool): - raise MarkerControlError("invalid_value_badge", "Value badge enabled must be boolean") - if badge.get("position") not in VALUE_BADGE_POSITIONS: - raise MarkerControlError("invalid_value_badge_position", "Invalid value badge position") - source = badge.get("source") - if badge["enabled"] and not isinstance(source, dict): - raise MarkerControlError("value_badge_source_required", "Enabled value badge needs a source") - if source is None: - continue + def validate_source(source: object, field: str) -> None: + prefix = "value_badge" if field == "value_badge" else "value_source" + source_error = "invalid_value_badge_source" if field == "value_badge" \ + else "invalid_value_source" + attribute_error = "invalid_value_badge_attribute" if field == "value_badge" \ + else "invalid_value_source_attribute" if not isinstance(source, dict) or source.get("kind") not in VALUE_BADGE_SOURCE_KINDS: - raise MarkerControlError("invalid_value_badge_source", "Invalid value badge source") + raise MarkerControlError(source_error, f"Invalid {field} source") kind = source["kind"] allowed_fields = { "entity_state": {"kind", "entity_id"}, @@ -846,23 +830,51 @@ def validate_marker_value_badges( "derived_marker_state": {"kind", "ref"}, }[kind] if (known_source_fields & set(source)) - allowed_fields: - raise MarkerControlError("invalid_value_badge_source", "Inconsistent value badge source") + raise MarkerControlError(source_error, f"Inconsistent {field} source") if kind in {"entity_state", "entity_attribute"}: entity_id = source.get("entity_id") if not isinstance(entity_id, str) or not _CONTROL_ENTITY_ID_RE.fullmatch(entity_id): - raise MarkerControlError("invalid_value_badge_source", "Invalid value badge entity id") - if kind == "entity_attribute": - if source.get("attribute") not in VALUE_BADGE_ATTRIBUTES: - raise MarkerControlError("invalid_value_badge_attribute", "Invalid value badge attribute") + raise MarkerControlError(source_error, f"Invalid {field} entity id") + if kind == "entity_attribute" and source.get("attribute") not in VALUE_BADGE_ATTRIBUTES: + raise MarkerControlError(attribute_error, f"Invalid {field} attribute") if kind == "derived_marker_state": ref = source.get("ref") - if not isinstance(ref, str) or not ref.startswith(MARKER_CONTROL_PREFIX) or not ref[len(MARKER_CONTROL_PREFIX):]: - raise MarkerControlError("invalid_value_badge_source", "Invalid marker value badge target") + if not isinstance(ref, str) or not ref.startswith(MARKER_CONTROL_PREFIX) \ + or not ref[len(MARKER_CONTROL_PREFIX):]: + raise MarkerControlError(source_error, f"Invalid {field} target") target = by_id.get(ref[len(MARKER_CONTROL_PREFIX):]) if target is None or target.get("removed") is True: - raise MarkerControlError("value_badge_marker_missing", "Marker value badge target does not exist") + raise MarkerControlError(f"{prefix}_marker_missing", f"{field} target does not exist") if target.get("is_light") is not True: - raise MarkerControlError("value_badge_marker_not_light", "Marker value badge target is not a forced light") + raise MarkerControlError(f"{prefix}_marker_not_light", f"{field} target is not a forced light") + + for marker_id, marker in by_id.items(): + old_marker = _matching_previous_marker( + marker, marker_id, old_by_id, old_markers, new_ids, + consumed_old_ids, validate_all, + ) + badge = marker.get("value_badge") + old_badge = None if validate_all else (old_marker or {}).get("value_badge") + if validate_all or badge != old_badge: + if badge is not None: + if not isinstance(badge, dict): + raise MarkerControlError("invalid_value_badge", "Value badge must be an object") + if not isinstance(badge.get("enabled"), bool): + raise MarkerControlError("invalid_value_badge", "Value badge enabled must be boolean") + if badge.get("position") not in VALUE_BADGE_POSITIONS: + raise MarkerControlError("invalid_value_badge_position", "Invalid value badge position") + source = badge.get("source") + if badge["enabled"] and not isinstance(source, dict): + raise MarkerControlError( + "value_badge_source_required", "Enabled value badge needs a source" + ) + if source is not None: + validate_source(source, "value_badge") + + source = marker.get("value_source") + old_source = None if validate_all else (old_marker or {}).get("value_source") + if (validate_all or source != old_source) and source is not None: + validate_source(source, "value_source") def validate_marker_light_entities( @@ -1743,6 +1755,12 @@ MARKER_SCHEMA = vol.Schema( extra=vol.ALLOW_EXTRA, ), ), + # Explicit source for display:value. Semantic delta validation shares + # the badge source contract while keeping untouched future literals. + vol.Optional("value_source"): vol.Any( + None, + vol.Schema({}, extra=vol.ALLOW_EXTRA), + ), # climate current_temperature: badge + room-average vote (off unless True) vol.Optional("use_climate_temp"): vol.Any(bool, None), vol.Optional("room_id"): vol.Any(str, None), diff --git a/demo/smoke_value_face_source.mjs b/demo/smoke_value_face_source.mjs new file mode 100644 index 00000000..7a2855cd --- /dev/null +++ b/demo/smoke_value_face_source.mjs @@ -0,0 +1,167 @@ +// Issue #378: an explicit Value + state source is selected in the real dialog, +// previewed, saved, reopened and projected by both renderers without changing +// the device action. Missing live data must keep the selection and render a dash. +import { launch, checkAll, finish } from './serve.mjs'; + +const { page, browser } = await launch({ width: 1100, height: 900 }, 1); +const res = await page.evaluate(async () => { + const c = window.__card; + const sr = () => c.shadowRoot || c.renderRoot; + const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + const entityId = 'cover.value_face_awning'; + const deviceId = 'd_value_face_awning'; + const source = { kind: 'entity_attribute', entity_id: entityId, attribute: 'current_position' }; + const sourceKey = `attr:${entityId}:current_position`; + const calls = []; + const setState = async (state, position) => { + c.hass = { + ...c.hass, + devices: { + ...c.hass.devices, + [deviceId]: { + id: deviceId, name: 'Value face awning', model: 'Test cover', area_id: 'bedroom', + identifiers: [['demo', deviceId]], entry_type: null, via_device_id: null, + }, + }, + entities: { + ...c.hass.entities, + [entityId]: { entity_id: entityId, device_id: deviceId, platform: 'demo' }, + }, + states: { + ...c.hass.states, + [entityId]: { + entity_id: entityId, state, + attributes: { + friendly_name: 'Value face awning', device_class: 'awning', + supported_features: 15, current_position: position, + }, + }, + }, + callService: async (domain, service, data) => { + calls.push([domain, service, data]); + return {}; + }, + }; + c._regSignature = ''; + c._maybeRebuildDevices(); + const item = c._devices.find((candidate) => candidate.bindingRef === deviceId); + if (item) c._layout = { ...c._layout, [item.id]: { s: item.space, x: 0.12, y: 0.88 } }; + c.requestUpdate(); + await c.updateComplete; + }; + const item = () => c._devices.find((candidate) => candidate.bindingRef === deviceId); + const faceValue = (root, selector) => root.querySelector(selector) + ?.querySelector('.valtext')?.textContent?.trim() || ''; + + await setState('open', 42); + c._setMode('devices'); + c._openMarkerDialog(item()); + await c.updateComplete; + c._markerDialog = { + ...c._markerDialog, display: 'value', tapAction: 'toggle', tapActionTouched: true, + }; + c.requestUpdate(); + await c.updateComplete; + + const select = sr().querySelector('#marker-value-source'); + const candidateValues = [...(select?.options || [])].map((option) => option.value); + select.value = sourceKey; + select.dispatchEvent(new Event('change', { bubbles: true, composed: true })); + await c.updateComplete; + const preview = sr().querySelector('hp-device-preview'); + await preview?.updateComplete; + const preview42 = faceValue(preview?.renderRoot, '.dev') === '42 %'; + const draftSourceExact = JSON.stringify(c._markerDialog?.valueSource) === JSON.stringify(source); + + await c._saveMarker(); + await c.updateComplete; + let saved = (c._serverCfg.markers || []).find((marker) => marker.binding === `device:${deviceId}`); + const savedExact = JSON.stringify(saved?.value_source) === JSON.stringify(source); + const actionSaved = saved?.tap_action === 'toggle'; + + c._openMarkerDialog(item()); + await c.updateComplete; + const reopenedSelect = sr().querySelector('#marker-value-source'); + const reopenedExact = reopenedSelect?.value === sourceKey + && reopenedSelect?.selectedOptions?.[0]?.value === sourceKey; + + // Cancel an unsaved switch to auto: the persisted source must survive. + reopenedSelect.value = ''; + reopenedSelect.dispatchEvent(new Event('change', { bubbles: true, composed: true })); + await c.updateComplete; + c._closeMarkerDialog(); + await c.updateComplete; + saved = (c._serverCfg.markers || []).find((marker) => marker.binding === `device:${deviceId}`); + const cancelKeptSource = JSON.stringify(saved?.value_source) === JSON.stringify(source); + + c._setMode('view'); + c._regSignature = ''; + c._maybeRebuildDevices(); + c.requestUpdate(); + await c.updateComplete; + const plan42 = faceValue(sr(), `.dev[data-id="${item().id}"]`) === '42 %'; + calls.length = 0; + c._clickDevice({ stopPropagation() {} }, item()); + await wait(20); + const actionUnchanged = JSON.stringify(calls.at(-1)) === JSON.stringify([ + 'cover', 'close_cover', { entity_id: entityId }, + ]); + + await customElements.whenDefined('houseplan-space-card'); + const staticCard = document.createElement('houseplan-space-card'); + staticCard.setConfig({ type: 'custom:houseplan-space-card', space: item().space }); + staticCard._snap = { + config: structuredClone(c._serverCfg), rev: c._cfgRev, + configFingerprint: `fixture-${c._cfgRev}`, + layout: structuredClone(c._layout), layoutRev: c._layoutRev, + layoutFingerprint: `fixture-${c._layoutRev}`, + }; + staticCard._loadedOnce = true; + staticCard.hass = c.hass; + document.body.appendChild(staticCard); + const started = Date.now(); + while (!staticCard.renderRoot?.querySelector('.hp-static-stage') && Date.now() - started < 6000) { + await wait(60); + } + await staticCard.updateComplete; + const static42 = faceValue(staticCard.renderRoot, `.dev[data-id="${item().id}"]`) === '42 %'; + + await setState('unavailable', 42); + c._setMode('view'); + c.requestUpdate(); + await c.updateComplete; + await wait(120); + await c.updateComplete; + const unavailableDash = faceValue(sr(), `.dev[data-id="${item().id}"]`) === '—'; + const unavailableStillValue = sr().querySelector(`.dev[data-id="${item().id}"]`)?.classList + .contains('valonly') === true; + + await setState('open', 55); + c._setMode('view'); + c.requestUpdate(); + await c.updateComplete; + const recovered55 = faceValue(sr(), `.dev[data-id="${item().id}"]`) === '55 %'; + + // A real binding choice resets the old source to automatic before Save. + c._setMode('devices'); + c._openMarkerDialog(item()); + await c.updateComplete; + const virtualBinding = [...sr().querySelectorAll('input[name="bmode"]')] + .find((node) => node.parentElement?.textContent?.includes(c._t('marker.virtual_option'))); + virtualBinding?.click(); + await c.updateComplete; + const bindingResetToAuto = !!virtualBinding && c._markerDialog?.binding === 'virtual' + && c._markerDialog?.valueSource === null + && c._markerDialog?.valueSourceTouched === true; + c._closeMarkerDialog(); + + return { + candidatePresent: candidateValues.includes(sourceKey), + preview42, draftSourceExact, savedExact, actionSaved, reopenedExact, + cancelKeptSource, plan42, static42, actionUnchanged, + unavailableDash, unavailableStillValue, recovered55, bindingResetToAuto, + }; +}); + +checkAll(res); +await finish(browser, res); diff --git a/dist/houseplan-assets.json b/dist/houseplan-assets.json index 44ba61cf..ddfb0b2b 100644 --- a/dist/houseplan-assets.json +++ b/dist/houseplan-assets.json @@ -1,125 +1,125 @@ { "schema": 1, - "fingerprint": "b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c", + "fingerprint": "3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f", "entry": "houseplan-card.js", "initialViewFiles": [ - "houseplan-assets/houseplan-card-B5mDyjuC.js", + "houseplan-assets/houseplan-card-wtxKVHkp.js", "houseplan-card.js" ], - "initialViewGzipBytes": 277319, + "initialViewGzipBytes": 277948, "lazyFiles": [ - "houseplan-assets/backdrop-pick-DFFLwJ_d.js", - "houseplan-assets/de-FazIOsg8.js", - "houseplan-assets/editor-CcxmUFV-.js", - "houseplan-assets/fr-CFpxbPTS.js", - "houseplan-assets/houseplan-editor-runtime-CisKVaG7.js", - "houseplan-assets/houseplan-onboarding-runtime-eqCB-Ax0.js" + "houseplan-assets/backdrop-pick-TMl_2iBT.js", + "houseplan-assets/de-B775TcPH.js", + "houseplan-assets/editor-CIkons5x.js", + "houseplan-assets/fr-Cml_YfSz.js", + "houseplan-assets/houseplan-editor-runtime-CM78ZHZU.js", + "houseplan-assets/houseplan-onboarding-runtime-BP9ODa6H.js" ], - "lazyGzipBytes": 191754, + "lazyGzipBytes": 192277, "lazyEditorFiles": [ - "houseplan-assets/backdrop-pick-DFFLwJ_d.js", - "houseplan-assets/editor-CcxmUFV-.js", - "houseplan-assets/houseplan-editor-runtime-CisKVaG7.js" + "houseplan-assets/backdrop-pick-TMl_2iBT.js", + "houseplan-assets/editor-CIkons5x.js", + "houseplan-assets/houseplan-editor-runtime-CM78ZHZU.js" ], - "lazyEditorGzipBytes": 139917, + "lazyEditorGzipBytes": 140230, "lazyOnboardingFiles": [ - "houseplan-assets/backdrop-pick-DFFLwJ_d.js", - "houseplan-assets/houseplan-onboarding-runtime-eqCB-Ax0.js" + "houseplan-assets/backdrop-pick-TMl_2iBT.js", + "houseplan-assets/houseplan-onboarding-runtime-BP9ODa6H.js" ], "lazyOnboardingGzipBytes": 9451, "lazyLocaleFiles": [ - "houseplan-assets/de-FazIOsg8.js", - "houseplan-assets/fr-CFpxbPTS.js" + "houseplan-assets/de-B775TcPH.js", + "houseplan-assets/fr-Cml_YfSz.js" ], - "lazyLocaleGzipBytes": 45342, + "lazyLocaleGzipBytes": 45551, "files": [ { - "path": "houseplan-assets/backdrop-pick-DFFLwJ_d.js", - "sha256": "f7bdc0ee64684220a751eadda1397edb922b9c785993f5bce1d6b8273c619f21", + "path": "houseplan-assets/backdrop-pick-TMl_2iBT.js", + "sha256": "2527114d8c2e58532848ac449cb7ea6927b86f5883bad84c649dc740c3872eea", "rawBytes": 6967, - "gzipBytes": 2956, + "gzipBytes": 2955, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-B5mDyjuC.js" + "houseplan-assets/houseplan-card-wtxKVHkp.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/de-FazIOsg8.js", - "sha256": "50ca05e9607b7f597cd20b1a1fce1f92fd9bb07a4cb0db4e86c3c921053c9e89", - "rawBytes": 75090, - "gzipBytes": 22886, + "path": "houseplan-assets/de-B775TcPH.js", + "sha256": "287e8f70aad3d82cfe292f204e5fe58eef3738631ee3be7078de6fd88c5fde1f", + "rawBytes": 75572, + "gzipBytes": 22996, "isEntry": false, "imports": [], "dynamicImports": [] }, { - "path": "houseplan-assets/editor-CcxmUFV-.js", - "sha256": "27077f8f6ecd61eea9e8405099b91974f423b8f7a53eef9ae8cf066803c475ef", + "path": "houseplan-assets/editor-CIkons5x.js", + "sha256": "ecaeb979d94de7e54f08b090355bf757b64a34932cddc9442f023804565ff56c", "rawBytes": 3831, "gzipBytes": 1583, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-B5mDyjuC.js" + "houseplan-assets/houseplan-card-wtxKVHkp.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/fr-CFpxbPTS.js", - "sha256": "ce2f711435309dac25e14c0024eea6da2c443949728a0fad1b5062851fcc26d6", - "rawBytes": 77225, - "gzipBytes": 22456, + "path": "houseplan-assets/fr-Cml_YfSz.js", + "sha256": "eeaa5abe3d199602537bab6f7f40075ceab398a25515685dd5e465d3ea9c9b51", + "rawBytes": 77710, + "gzipBytes": 22555, "isEntry": false, "imports": [], "dynamicImports": [] }, { - "path": "houseplan-assets/houseplan-card-B5mDyjuC.js", - "sha256": "d2f3c9df28d6acc99e4c7960c6f3cf1d5b516b42c3b10102429ba263532f569a", - "rawBytes": 976343, - "gzipBytes": 276523, + "path": "houseplan-assets/houseplan-card-wtxKVHkp.js", + "sha256": "796b979c90d4791cbdadb991c407dbd7d6768880b5a856cc1b7ec19f1e317c7a", + "rawBytes": 979430, + "gzipBytes": 277152, "isEntry": false, "imports": [], "dynamicImports": [ - "houseplan-assets/de-FazIOsg8.js", - "houseplan-assets/editor-CcxmUFV-.js", - "houseplan-assets/fr-CFpxbPTS.js", - "houseplan-assets/houseplan-editor-runtime-CisKVaG7.js", - "houseplan-assets/houseplan-onboarding-runtime-eqCB-Ax0.js" + "houseplan-assets/de-B775TcPH.js", + "houseplan-assets/editor-CIkons5x.js", + "houseplan-assets/fr-Cml_YfSz.js", + "houseplan-assets/houseplan-editor-runtime-CM78ZHZU.js", + "houseplan-assets/houseplan-onboarding-runtime-BP9ODa6H.js" ] }, { - "path": "houseplan-assets/houseplan-editor-runtime-CisKVaG7.js", - "sha256": "d99951a97b78d46d05501d0e2e3f5a9cd73a1088a5e9a382d8899903b040bb97", - "rawBytes": 524211, - "gzipBytes": 135378, + "path": "houseplan-assets/houseplan-editor-runtime-CM78ZHZU.js", + "sha256": "e439a389b8556a98b6ee0641cbda9a47978b2a10e4279d6663848ec97005771e", + "rawBytes": 526486, + "gzipBytes": 135692, "isEntry": false, "imports": [ - "houseplan-assets/backdrop-pick-DFFLwJ_d.js", - "houseplan-assets/houseplan-card-B5mDyjuC.js" + "houseplan-assets/backdrop-pick-TMl_2iBT.js", + "houseplan-assets/houseplan-card-wtxKVHkp.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/houseplan-onboarding-runtime-eqCB-Ax0.js", - "sha256": "aba9dab2a60c386b0638636d2ebcc683bd44a56fea5ce594fda9456ee171371d", + "path": "houseplan-assets/houseplan-onboarding-runtime-BP9ODa6H.js", + "sha256": "d49d4e5b08de22682d8c548ffa90bd73175fc1d069eca75f98905b9a804cafb0", "rawBytes": 26195, - "gzipBytes": 6495, + "gzipBytes": 6496, "isEntry": false, "imports": [ - "houseplan-assets/backdrop-pick-DFFLwJ_d.js", - "houseplan-assets/houseplan-card-B5mDyjuC.js" + "houseplan-assets/backdrop-pick-TMl_2iBT.js", + "houseplan-assets/houseplan-card-wtxKVHkp.js" ], "dynamicImports": [] }, { "path": "houseplan-card.js", - "sha256": "376b5951ff76686ebc299f63f2a21bf56f331e1027c52eabd487664fc86cd45e", + "sha256": "785c3ec627b80c3cda8831faaa08f31c7021ad47b0be372d3326ce5456d7d087", "rawBytes": 1183, "gzipBytes": 796, "isEntry": true, "imports": [ - "houseplan-assets/houseplan-card-B5mDyjuC.js" + "houseplan-assets/houseplan-card-wtxKVHkp.js" ], "dynamicImports": [] } diff --git a/dist/houseplan-assets/backdrop-pick-DFFLwJ_d.js b/dist/houseplan-assets/backdrop-pick-TMl_2iBT.js similarity index 97% rename from dist/houseplan-assets/backdrop-pick-DFFLwJ_d.js rename to dist/houseplan-assets/backdrop-pick-TMl_2iBT.js index 642dc6d9..77809655 100644 --- a/dist/houseplan-assets/backdrop-pick-DFFLwJ_d.js +++ b/dist/houseplan-assets/backdrop-pick-TMl_2iBT.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";import{b as e}from"./houseplan-card-B5mDyjuC.js";function n(e,n){return{id:e,title:n,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function t(e="file"){const n="draw"===e;return{source:e,showBorders:n,showNames:n,displayTouched:!1}}function r(e,n){if(e.displayTouched)return{...e,source:n};const t="draw"===n;return{...e,source:n,showBorders:t,showNames:t}}function a(e,n,t){return{...e,[n]:t,displayTouched:!0}}function o(e,n,t){const r=(e?.spaces||[]).find(e=>e?.id===t),a=new Set((r?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),o=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===t||"string"==typeof e.room_id&&a.has(e.room_id)||n?.[e.id]?.s===t).map(e=>e.id))].sort((e,n)=>e.localeCompare(n));return{markerIds:o,count:o.length}}const i=1e6,l=()=>({kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}),d=(e,n,t)=>{if(!Number.isInteger(e)||!Number.isInteger(n)||e<=0||n<=0||e>i||n>i)return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};const r=e*n*4;return{kind:Math.max(e,n)>16384?"hard":r>134217728?"warn":"safe",width:e,height:n,alpha:t,decodedBytes:r}},s=(e,n)=>(e[n]<<24|e[n+1]<<16|e[n+2]<<8|e[n+3])>>>0,u=(e,n)=>e[n]<<8|e[n+1],c=(e,n)=>e[n]|e[n+1]<<8|e[n+2]<<16,h=(e,n)=>e[n]|e[n+1]<<8,f=(e,n,t)=>{for(let r=0;re.length)break;n+=12+t}}return d(n,t,a)}(e):"jpg"===n?function(e){if(e.length<4||255!==e[0]||216!==e[1])return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};let n=2;for(let t=0;t<256&&n+4<=e.length;t++){if(255!==e[n]){n++;continue}const t=e[n+1];if(216===t||t>=208&&t<=217){n+=2;continue}if(t>=192&&t<=207&&196!==t&&200!==t&&204!==t)return n+9>e.length?l():d(u(e,n+7),u(e,n+5),!1);if(218===t)return l();const r=u(e,n+2);if(r<2)return l();n+=2+r}return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}(e):"webp"===n?function(e){if(e.length<30||!f(e,0,"RIFF")||!f(e,8,"WEBP"))return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};if(f(e,12,"VP8X")){const n=!!(16&e[20]);return d(c(e,24)+1,c(e,27)+1,n)}if(f(e,12,"VP8 "))return 157!==e[23]||1!==e[24]||42!==e[25]?{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}:d(16383&h(e,26),16383&h(e,28),!1);if(f(e,12,"VP8L")){if(47!==e[20])return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};const n=e[21]|e[22]<<8|e[23]<<16|e[24]<<24;return d(1+(16383&n),1+(n>>14&16383),1==(n>>28&1))}return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}(e):{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}catch{return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}}const w={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"};function p(e){return new Promise((n,t)=>{const r=new FileReader;r.onerror=()=>t(r.error||new Error("read failed")),r.onload=()=>{const e=String(r.result||""),a=e.indexOf(",");a<0?t(new Error("unexpected data url")):n(e.slice(a+1))},r.readAsDataURL(e)})}async function b(e,n=1.414){const t=URL.createObjectURL(e);try{return await new Promise(e=>{const r=new Image;r.onload=()=>e(r.naturalWidth&&r.naturalHeight?r.naturalWidth/r.naturalHeight:n),r.onerror=()=>e(n),r.src=t})}finally{URL.revokeObjectURL(t)}}async function k(e,n,t){const[r,a]=await Promise.all([p(e),b(e)]);return{ext:n,b64:r,aspect:a,name:t}}async function m(e){const n=function(e){return w[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"")}(e);if(!n)return{kind:"reject"};if("svg"===n)return{kind:"pass",ext:n};const t=g(new Uint8Array(await e.arrayBuffer()),n);return"safe"===t.kind?{kind:"pass",ext:n}:{kind:"guard",state:{file:e,ext:n,probe:t,busy:!1}}}async function y(e){const n=createImageBitmap(e.file,{imageOrientation:"from-image"}),t=await Promise.race([n,new Promise((e,n)=>{setTimeout(()=>n(new Error("decode timeout")),globalThis.__HP_BACKDROP_TIMEOUT_MS??1e4)})]);try{const{width:n,height:r}=function(e,n,t=4096){const r=Math.min(1,t/Math.max(e,n));return{width:Math.max(1,Math.round(e*r)),height:Math.max(1,Math.round(n*r))}}(t.width,t.height,4096),a=e.probe.alpha,o=a?"image/png":"image/jpeg";let i;if("function"==typeof OffscreenCanvas){const e=new OffscreenCanvas(n,r),a=e.getContext("2d");if(!a)throw new Error("no 2d context");a.drawImage(t,0,0,n,r),i=await e.convertToBlob({type:o,quality:.9})}else{const e=document.createElement("canvas");e.width=n,e.height=r;const a=e.getContext("2d");if(!a)throw new Error("no 2d context");a.drawImage(t,0,0,n,r),i=await new Promise((n,t)=>{e.toBlob(e=>e?n(e):t(new Error("encode failed")),o,.9)})}const l=a?"png":"jpg";return{blob:i,ext:l,name:`${e.file.name.replace(/\.[^.]+$/,"")||"plan"}-reduced.${l}`}}finally{t.close()}}const _=e=>(e/1048576).toFixed(e>=10485760?0:1);function B(n,t,r,a){const o=n._backdropGuard;if(!o)return null;const{probe:i}=o,l="hard"===i.kind,d=l?n._t("backdrop.too_large_body",{w:i.width??0,h:i.height??0,limit:16384}):"unknown"===i.kind?n._t("backdrop.unknown_body"):n._t("backdrop.large_body",{w:i.width??0,h:i.height??0,fileMb:_(o.file.size),decodedMb:_(i.decodedBytes??0)}),s=()=>{n._backdropGuard?.busy||r()},u=()=>n._backdropGuard?.file===o.file;return e`e?.id===t),a=new Set((r?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),o=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===t||"string"==typeof e.room_id&&a.has(e.room_id)||n?.[e.id]?.s===t).map(e=>e.id))].sort((e,n)=>e.localeCompare(n));return{markerIds:o,count:o.length}}const i=1e6,l=()=>({kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}),d=(e,n,t)=>{if(!Number.isInteger(e)||!Number.isInteger(n)||e<=0||n<=0||e>i||n>i)return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};const r=e*n*4;return{kind:Math.max(e,n)>16384?"hard":r>134217728?"warn":"safe",width:e,height:n,alpha:t,decodedBytes:r}},s=(e,n)=>(e[n]<<24|e[n+1]<<16|e[n+2]<<8|e[n+3])>>>0,u=(e,n)=>e[n]<<8|e[n+1],c=(e,n)=>e[n]|e[n+1]<<8|e[n+2]<<16,h=(e,n)=>e[n]|e[n+1]<<8,f=(e,n,t)=>{for(let r=0;re.length)break;n+=12+t}}return d(n,t,a)}(e):"jpg"===n?function(e){if(e.length<4||255!==e[0]||216!==e[1])return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};let n=2;for(let t=0;t<256&&n+4<=e.length;t++){if(255!==e[n]){n++;continue}const t=e[n+1];if(216===t||t>=208&&t<=217){n+=2;continue}if(t>=192&&t<=207&&196!==t&&200!==t&&204!==t)return n+9>e.length?l():d(u(e,n+7),u(e,n+5),!1);if(218===t)return l();const r=u(e,n+2);if(r<2)return l();n+=2+r}return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}(e):"webp"===n?function(e){if(e.length<30||!f(e,0,"RIFF")||!f(e,8,"WEBP"))return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};if(f(e,12,"VP8X")){const n=!!(16&e[20]);return d(c(e,24)+1,c(e,27)+1,n)}if(f(e,12,"VP8 "))return 157!==e[23]||1!==e[24]||42!==e[25]?{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}:d(16383&h(e,26),16383&h(e,28),!1);if(f(e,12,"VP8L")){if(47!==e[20])return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};const n=e[21]|e[22]<<8|e[23]<<16|e[24]<<24;return d(1+(16383&n),1+(n>>14&16383),1==(n>>28&1))}return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}(e):{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}catch{return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}}const w={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"};function p(e){return new Promise((n,t)=>{const r=new FileReader;r.onerror=()=>t(r.error||new Error("read failed")),r.onload=()=>{const e=String(r.result||""),a=e.indexOf(",");a<0?t(new Error("unexpected data url")):n(e.slice(a+1))},r.readAsDataURL(e)})}async function b(e,n=1.414){const t=URL.createObjectURL(e);try{return await new Promise(e=>{const r=new Image;r.onload=()=>e(r.naturalWidth&&r.naturalHeight?r.naturalWidth/r.naturalHeight:n),r.onerror=()=>e(n),r.src=t})}finally{URL.revokeObjectURL(t)}}async function k(e,n,t){const[r,a]=await Promise.all([p(e),b(e)]);return{ext:n,b64:r,aspect:a,name:t}}async function m(e){const n=function(e){return w[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"")}(e);if(!n)return{kind:"reject"};if("svg"===n)return{kind:"pass",ext:n};const t=g(new Uint8Array(await e.arrayBuffer()),n);return"safe"===t.kind?{kind:"pass",ext:n}:{kind:"guard",state:{file:e,ext:n,probe:t,busy:!1}}}async function y(e){const n=createImageBitmap(e.file,{imageOrientation:"from-image"}),t=await Promise.race([n,new Promise((e,n)=>{setTimeout(()=>n(new Error("decode timeout")),globalThis.__HP_BACKDROP_TIMEOUT_MS??1e4)})]);try{const{width:n,height:r}=function(e,n,t=4096){const r=Math.min(1,t/Math.max(e,n));return{width:Math.max(1,Math.round(e*r)),height:Math.max(1,Math.round(n*r))}}(t.width,t.height,4096),a=e.probe.alpha,o=a?"image/png":"image/jpeg";let i;if("function"==typeof OffscreenCanvas){const e=new OffscreenCanvas(n,r),a=e.getContext("2d");if(!a)throw new Error("no 2d context");a.drawImage(t,0,0,n,r),i=await e.convertToBlob({type:o,quality:.9})}else{const e=document.createElement("canvas");e.width=n,e.height=r;const a=e.getContext("2d");if(!a)throw new Error("no 2d context");a.drawImage(t,0,0,n,r),i=await new Promise((n,t)=>{e.toBlob(e=>e?n(e):t(new Error("encode failed")),o,.9)})}const l=a?"png":"jpg";return{blob:i,ext:l,name:`${e.file.name.replace(/\.[^.]+$/,"")||"plan"}-reduced.${l}`}}finally{t.close()}}const _=e=>(e/1048576).toFixed(e>=10485760?0:1);function B(n,t,r,a){const o=n._backdropGuard;if(!o)return null;const{probe:i}=o,l="hard"===i.kind,d=l?n._t("backdrop.too_large_body",{w:i.width??0,h:i.height??0,limit:16384}):"unknown"===i.kind?n._t("backdrop.unknown_body"):n._t("backdrop.large_body",{w:i.width??0,h:i.height??0,fileMb:_(o.file.size),decodedMb:_(i.decodedBytes??0)}),s=()=>{n._backdropGuard?.busy||r()},u=()=>n._backdropGuard?.file===o.file;return e`s()}>

${d}

diff --git a/dist/houseplan-assets/de-B775TcPH.js b/dist/houseplan-assets/de-B775TcPH.js new file mode 100644 index 00000000..061b8ebf --- /dev/null +++ b/dist/houseplan-assets/de-B775TcPH.js @@ -0,0 +1 @@ +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";var e={"editor.context_actions":"Aktionen: {object}","editor.tool_options":"Werkzeugoptionen: {tool}","editor.palette":"Palette: {tool}","editor.open_group":"Werkzeuggruppe: {group}","editor.group_active":"{group} — aktiv: {item}","editor.disabled_action":"{action} ist nicht verfügbar: {reason}","editor.loading":"Editor wird geladen…","editor.loading_aria":"Der Plan-Editor wird geladen","editor.load_failed":"Der Editor konnte nicht geladen werden.","editor.refresh_advice":"Seite aktualisieren und erneut versuchen.","editor.retry_advice":"Verbindung prüfen und erneut drücken.","color_picker.title":"Farbwähler","color_picker.hue":"Farbton","color_picker.saturation":"Sättigung","color_picker.value":"Helligkeit","color_picker.hex":"Hex-Farbe","color_picker.invalid_hex":"Geben Sie eine 3- oder 6-stellige Hex-Farbe ein","btn.properties":"Eigenschaften","btn.keep_as_walls":"Als Wände behalten","btn.delete_room_keep_walls":"Raum löschen, Wände behalten","btn.delete_room_with_walls":"Raum und Wände löschen","title.markup_select":"Wände, Säulen und gespeicherte Umrisse auswählen und bearbeiten","title.markup_column":"Säule: Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","markup.partition":"Trennwand","markup.column":"Säule","markup.hint_column":"Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","history.draft_segment":"Raum-Entwurfsegment hinzufügen","history.draft_merge":"Unvollständige Raumumrisse verbinden","history.draft_segment_delete":"Raum-Entwurfssegment entfernen","history.partition_add":"Trennwand hinzufügen","history.column_add":"Säule hinzufügen","history.physical_edit":"Physisches Objekt ändern","history.physical_delete":"Physisches Objekt löschen","history.physical_move":"Physisches Objekt verschieben","history.contour_to_partitions":"Umriss in geschlossene Wände umwandeln","history.wall_chain_finish":"Wandkette fertigstellen","history.wall_face_batch":"Räume aus Wänden erstellen","toast.column_duplicate":"Eine Säule mit demselben Zentrum und äußerer Größe existiert bereits","confirm.delete_draft":"Sofort den gesamten unvollständigen Raumumriss löschen?","confirm.delete_draft_segment":"Dieses Entwurfssegment löschen? Der verbleibende Umriss könnte sich in zwei teilen.","physical.partition_properties":"Eigenschaften der Trennwand","physical.column_properties":"Eigenschaften der Säule","physical.draft_properties":"Eigenschaften des Entwurfssegments","physical.shape":"Form","physical.square":"Quadrat","physical.circle":"Kreis","physical.diameter":"Durchmesser","physical.side":"Seite","physical.rotation":"Drehwinkel","physical.length":"Länge","physical.allowed_range":"Erlaubt: {min}–{max} {unit}","physical.delete_segment":"Segment löschen","physical.delete_draft":"Gesamten Umriss löschen","physical.column_size_title":"Außenmaß der per Klick platzierten quadratischen Säule (1–150 cm).","card.title":"Hausplan","count.devices":"{n} Entwicklung","empty.no_spaces":"Noch keine Bereiche.","empty.add_first":"Ersten Bereich hinzufügen und einen Grundriss hochladen.","empty.install":"Installieren Sie die House Plan-Integration und fügen Sie sie unter „Geräte & Dienste“ hinzu.","btn.add_space":"Bereich hinzufügen","btn.cancel":"Abbrechen","btn.save":"Speichern","btn.close":"Schließen","btn.delete":"Löschen","btn.edit":"Bearbeiten","btn.open_in_ha":"In HA öffnen","btn.reset":"Zurücksetzen","btn.attach":"Anhängen…","btn.upload":"Hochladen…","btn.replace":"Ersetzen…","title.zoom_in":"Zoomen","title.zoom_out":"Herauszoomen","title.zoom_fit":"Alles anpassen","title.markup":"Raum-Markierung: Gitter, Linien, Konturen","title.configure_space":"Bereich konfigurieren","title.add_space":"Bereich hinzufügen","title.markup_add":"Wände: eine durchgehende Kette zeichnen; Shift sperrt auf 45°. Werkzeugwechsel oder Verlassen des Editors beendet die Kette und wandelt sie in unabhängige Wände","title.markup_merge":"Zusammenführen: Klicken Sie auf einen Raum und dann auf den Nachbarraum, mit dem er eine Wand teilt","title.markup_split":"Raum teilen: Klicken Sie auf den Raum und dann auf zwei Punkte an seinen Wänden","title.markup_delroom":"Raum löschen: Klicken Sie in einen Raum und bestätigen Sie","title.need_plan":"Einen Grundriss hochladen","markup.add":"Wände","markup.merge":"Zusammenführen","markup.split":"Teilen","markup.resize":"Größe ändern","title.markup_resize":"Räume vergrößern/verkleinern: Ziehen Sie einen verfügbaren horizontalen oder vertikalen Wandgriff","markup.hint_resize":"Ziehen Sie einen verfügbaren Wandgriff · die Wand stoppt an der ersten unsicheren Position · Esc bricht ab · Strg+Z — rückgängig","resize.disabled.diagonal":"Diese Wand ist geneigt; Größe ändern unterstützt nur horizontale und vertikale Wände","resize.disabled.side-angle":"Eine angrenzende Wand ist geneigt; Größe ändern erfordert, dass beide angrenzenden Wände diese Wand im rechten Winkel treffen","resize.disabled.duplicate-physical-wall":"Eine separate Trennwand, unfertige Kontur oder ein Pfeiler überlappt diese Wand; entfernen oder verschieben Sie sie, bevor Sie die Größe ändern","resize.disabled.partial-shared":"Ein angrenzender Raum nutzt nur einen Teil dieser Wand, daher kann sie nicht sicher als gemeinsame Wand verschoben werden","resize.disabled.unequal-shared":"Der angrenzende Raum nutzt andere Endpunkte oder Wandlängen, daher können die Wände nicht gemeinsam sicher bewegt werden","resize.disabled.multiple-rooms":"Das Verschieben dieser Wand würde mehr als zwei Räume betreffen","resize.disabled.thickness-conflict":"Die Wandstärke kann nicht sicher beibehalten werden","resize.disabled.opening-conflict":"Eine Öffnung an dieser Wand kann nicht sicher verschoben werden","resize.disabled.invalid-geometry":"Diese Wand kann nicht ohne Änderung der Planstruktur geändert werden","resize.limit_stopped":"Wand gestoppt: der nächste Schritt würde ein Anschlusslimit verletzen","resize.commit_failed":"Größenänderung wurde abgebrochen, da der endgültige Plan die Geometrieprüfung nicht bestanden hat","resize.preview_failed":"Größenänderung am letzten sicheren Punkt gestoppt, da die Plan-Geometrie nicht beibehalten werden konnte","markup.opening":"Öffnung","markup.delete_room":"Raum löschen","history.undo":"Rückgängig","history.redo":"Wiederholen","history.undo_named":"Rückgängig: {name} (Strg+Z)","history.redo_named":"Wiederholen: {name} (Strg+Shift+Z / Strg+Y)","history.undo_empty":"Nichts zu widerrufen","history.redo_empty":"Nichts zu wiederholen","history.undone":"Rückgängig gemacht: {name}","history.redone":"Wiederholt: {name}","history.add_room":"Raum erstellen","history.split_room":"Raum teilen","history.merge_rooms":"Räume zusammenführen","history.resize_room":"Raumgröße ändern","history.wall_thickness":"Wandstärke ändern","history.add_opening":"Tür- oder Fensteröffnung hinzufügen","history.edit_opening":"Öffnung bearbeiten","history.move_opening":"Öffnung verschieben","history.delete_opening":"Öffnung löschen","history.delete_room":"Raum löschen","history.delete_room_keep_walls":"Raum löschen, Wände behalten","history.delete_room_with_walls":"Raum und Wände löschen","history.decor_add":"Dekorobjekt hinzufügen","history.decor_edit":"Dekorobjekt bearbeiten","history.decor_move":"Dekorobjekt verschieben","history.decor_transform":"Dekorobjekt transformieren","history.decor_delete":"Dekorobjekt löschen","history.backdrop_transform":"Plan-Hintergrund transformieren","opening.new":"Neue Öffnung","opening.edit":"Öffnung","opening.door":"Tür","opening.window":"Fenster","opening.gate":"Tor","opening.passage":"Offener Durchgang","opening.passage_binding_warning":"Das Speichern entfernt den Öffnungs-/Schließsensor und das Schloss.","opening.invalid_passage_fields":"Der offene Durchgang auf dem Plan „{room}“ hat nicht unterstützte Einstellungen: {fields}.","opening.type_label":"Typ","opening.length_label":"Länge, cm","opening.contact_label":"Öffnungs-/Schließsensor","opening.lock_label":"Schloss","opening.none":"— keine —","opening.search_ph":"Suche: Name oder Entitäts-ID…","opening.invert":"Geöffnet/geschlossen invertieren","opening.flip_h":"Scharnier an der anderen Zarge","opening.flip_v":"Öffnet zur anderen Seite","opening.open":"Offen","opening.closed":"Geschlossen","opening.locked":"Verriegelt","opening.unlocked":"Entriegelt","opening.state_unknown":"nicht verfügbar","opening.no_entities":"Keine Sensoren gebunden — ein statisches Symbol auf dem Plan.","toast.opening_no_wall":"Neben eine Raumwand oder eine unabhängige Wand klicken","opening.host_partition":"Unabhängige Wand","opening.partition_orphan":"Die unabhängige Wand für diese Öffnung existiert nicht mehr","opening.partition_jamb_margin":"Mindestens {distance} zwischen der Öffnung und dem Wandende lassen","opening.rebind_partition":"An eine andere unabhängige Wand anfügen","confirm.delete_partition_openings_title":"Wand und Öffnungen löschen?","confirm.delete_partition_openings_body":"Diese Wand enthält {count} Öffnung(en). Sie werden zusammen gelöscht.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Löschen","markup.hint_points":"Punkte: {n} · Shift — 45° Schritte · Esc — Kette beenden · Ctrl+Z — Punkt rückgängig machen · Schließen eines Bereichs erstellt einen Raum","markup.hint_start":"Klicken Sie auf einen Rasterpunkt, um eine Wandkette zu starten","tip.lqi":"durchschnittliches Zigbee-Signal:","tip.area":"Bereich: {value}","info.device_header":"Gerät im Plan","info.model":"Modell","info.state":"Status","info.link":"Verbindung","info.manuals":"Handbücher","info.none":"Keine zusätzlichen Informationen","marker.new_device":"Neues Gerät","marker.name_label":"Name (im Plan angezeigt)","marker.name_ph":"Name","marker.binding_label":"An ein HA-Gerät binden","marker.binding_disabled":"in Home Assistant deaktiviert","marker.virtual_option":"Virtuelles Gerät (keine Bindung)","marker.search_ph":"Gerät/Gruppe suchen…","marker.nothing_found":"Nichts gefunden","marker.room_label":"Raum","marker.room_override":" (Platzierung überschreiben)","marker.room_choose":"— Raum auswählen —","marker.room_auto":"— nach Gerätebereich (auto) —","marker.icon_label":"Symbol","marker.icon_ph":"mdi:… (leer = automatisch)","marker.display_label":"Anzeige","display.badge":"Symbol + Zustand","display.icon_ripple":"Symbol + Zustand und Aktivität","display.static_icon":"Immer statisches Symbol","marker.display_hint":"Symbol + Zustand ändert die Platte für die Zustände Arbeit, geöffnet, Alarm und nicht verfügbar. Symbol + Zustand und Aktivität zeigt zusätzlich einen kurzen Impuls für Ereignisse und einen kontinuierlichen Impuls für laufende Arbeit, Bewegung oder Präsenz. Wert + Zustand ersetzt das Symbol durch einen eindeutigen HA-Wert. Ein statisches Symbol reagiert niemals auf Gerätezustände.","marker.display_hint_badge":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon_ripple":"Das Symbol, die dynamische Platte und der Puls zeigen einen kurzen Puls für Ereignisse und einen kontinuierlichen Puls für Arbeit, Bewegung oder Anwesenheit. Rote Alarme haben eine separate Priorität.","marker.display_hint_value":"Ein ausgewählter oder automatisch ermittelter Home Assistant-Wert ersetzt das Symbol, während die Platte weiterhin den Zustand anzeigt. Rote Alarme bleiben sichtbar.","marker.display_hint_static_icon":"Die themenabhängige Hülle und das Symbol bleiben immer gleich. Zustand, Aktivität, Nichtverfügbarkeit und Alarme verändern nicht das Erscheinungsbild.","marker.static_alarm_warning":"Dieses Gerät kann Alarme melden. Die statische Anzeige verbirgt die visuelle Alarmanzeige des Markers.","marker.preview.title":"Anzeigevorschau","marker.preview.actual":"Jetzt","marker.preview.example":"Beispiel","marker.preview.integration":"Bereitgestellt von","marker.preview.source":"Anzeigquelle","marker.preview.current_state":"Aktueller Zustand","marker.preview.result":"Auf dem Plan","marker.preview.details":"Quelldetails","marker.preview.select_source":"Ein Home-Assistant-Gerät oder eine Entität auswählen, um die tatsächliche Anzeige zu sehen.","marker.preview.unknown_provider":"Unbekannte Integration","marker.preview.virtual_provider":"House Plan · virtuelles Gerät","marker.preview.no_source":"Keine aktive Quelle","marker.preview.no_state":"Kein aktueller Zustand","marker.preview.mixed_states":"Mehrere verschiedene Zustände","marker.preview.multiple_sources":"{n}-Quellen","marker.preview.more_sources":"+{n} mehr","marker.preview.scaled":"Die Vorschau wird auf {n}% skaliert, um zu passen. Die gespeicherte Größe bleibt unverändert.","marker.preview.demo_short":"Kurze Aktivität anzeigen","marker.preview.demo_continuous":"Kontinuierliche Aktivität anzeigen","marker.preview.stop_continuous":"Kontinuierliche Aktivität stoppen","marker.preview.demo_short_notice":"Kurzes Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_continuous_notice":"Kontinuierliches Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_already_visible":"Der aktuelle Zustand zeigt bereits eine echte Aktivitäts- oder Alarmeffekt.","marker.preview.reduced_motion":"Systemweit reduzierte Bewegungen sind aktiviert, daher wird gewöhnliche Aktivität als Punkt angezeigt.","marker.preview.reason.neutral":"Neutrale dunkle Platte","marker.preview.reason.working":"Gelbe Platte: Das Gerät arbeitet jetzt","marker.preview.reason.working_activity":"Gelbe Platte und Aktivitätseffekt: das Gerät funktioniert jetzt","marker.preview.reason.open":"Orange Platte: physisch geöffnet oder entriegelt","marker.preview.reason.cover_icon_state":"Neutrale Platte; die Abdeckungseinheit steuert den angezeigten Zustand und das Symbol","marker.preview.reason.presence":"Aktivitätseffekt, während Präsenz erkannt wird","marker.preview.reason.event":"Kurzer Aktivitätseffekt nach einem erkannten Ereignis","marker.preview.reason.transition":"Aktivitätseffekt, während das Gerät sich bewegt oder den Zustand ändert","marker.preview.reason.media_neutral":"Mediengeräte bleiben im verfügbaren Zustand auf einer neutralen dunklen Platte","marker.preview.reason.unavailable":"Gedämpfte neutrale Platte: nicht verfügbar oder ausgeschaltet","marker.preview.reason.alarm":"Rote Alarmplatte; Alarmanzeige wird immer angezeigt","marker.preview.reason.live_states_disabled":"Live-Zustandsstil ist für diese Karte deaktiviert","marker.preview.reason.value_no_state":"Kein nutzbarer Zustandswert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_ambiguous_sources":"Mehrere mögliche Wertequellen; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_non_scalar":"Die Quelle lieferte keinen einfachen Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_virtual":"Ein virtuelles Gerät hat keinen Home Assistant-Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.vacuum_live_plan_only":"Echtzeitposition und -spur des Staubsaugers sind nur im vollständigen Plan verfügbar","marker.preview.reason.hidden_design_preview":"Das Gerät ist im Plan ausgeblendet; diese Designvorschau bleibt sichtbar","marker.preview.reason.composite_power_source":"Der Status stammt vom Power-Entity des Geräts; Hilfsschalter werden ignoriert","marker.preview.reason.activity_display_disabled":"Der gewählte Anzeigemodus zeigt gewöhnliche Aktivitätseffekte nicht an","marker.preview.reason.ha_disabled":"Die Home Assistant-Verbindung ist deaktiviert und wird im Plan ausgeblendet","marker.preview.reason.orphaned":"Die gespeicherte Home Assistant-Verbindung konnte nicht mehr gefunden werden","marker.preview.reason.static_icon":"Statischer Modus: Der Gerätestatus ändert das Symbol nicht","marker.activity_color":"Aktivitätspulsfarbe","marker.ripple_size":"Aktivitätspulsgröße","marker.activity_alarm_note":"Farbe und Größe betreffen nur gewöhnliche Aktivität und ändern keine roten Alarme.","marker.pulse_a11y_alarm":"Alarm","marker.pulse_a11y_event":"Kürzliches Ereignis","marker.pulse_a11y_presence":"Anwesenheit erkannt","marker.pulse_a11y_transition":"Statusänderung","marker.pulse_a11y_running":"Arbeitet","marker.state_a11y_neutral":"Standardzustand","marker.state_a11y_working":"Funktionierend","marker.state_a11y_open":"Offen","marker.state_a11y_locked":"Verschlossen","marker.state_a11y_unlocked":"Entriegelt","marker.state_a11y_alarm":"Alarm","marker.state_a11y_unavailable":"Nicht verfügbar","marker.lqi_a11y_low":"LQI {value}, schwaches Signal","marker.lqi_a11y_mid":"LQI {value}, mittleres Signal","marker.lqi_a11y_high":"LQI {value}, starkes Signal","marker.size_label":"Symbolgröße / Drehung","marker.angle_label":"Drehen","marker.model_label":"Modell","marker.model_ph":"z.B. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Beschreibung","marker.desc_ph":"Notizen, Spezifikationen…","marker.manuals_label":"Handbücher (PDF usw.)","marker.sub_device":"Gerät","marker.sub_z2m_group":"· Z2M Gruppe","marker.sub_group":"Gruppe","marker.sub_helper":"Helfer","space.new":"Neuer Bereich","space.header":"Bereich","space.title_label":"Titel","space.title_ph":"z.B. Garage","space.plan_label":"Grundriss (Hintergrund)","space.no_plan":"Kein Planbild","space.plan_alt":"Grundriss","room.new":"Neuer Raum","room.name_label":"Anzeigename","room.name_ph":"z.B. Terrasse","room.area_label":"Home Assistant Bereich (nicht zugewiesen)","room.no_area_option":"— kein Bereich —","room.default_name":"Raum","device.unnamed":"unbenannt","device.light_group":"Lichtgruppe","device.fallback":"Gerät","device.virtual":"Virtuelles Gerät","confirm.delete_room":'Raum "{name}" löschen?',"confirm.delete_room_title":'Raum "{name}" löschen?',"confirm.delete_room_body":"Festlegen, ob die ausschließlich zu diesem Raum gehörenden physischen Wände erhalten bleiben. Mit einem anderen Raum geteilte Wände bleiben immer erhalten.","confirm.remove_marker":"„{name}“ aus dem Plan löschen? Das Gerät verschwindet vollständig und trägt nicht mehr zu den Plandaten bei. Es kann später erneut hinzugefügt werden.","confirm.erase_decor":"Das Objekt „{kind}“ löschen? Diese Aktion kann im Editor rückgängig gemacht werden.","confirm.delete_space":"Bereich „{title}“ mit allen Räumen und Markierungen löschen?","space.delete_blocked":"Dieser Bereich wird noch von {n} Gerät(en) verwendet. Die Geräte zuerst in einen anderen Bereich verschieben oder löschen.","toast.pos_save_failed":"Position nicht gespeichert: {err}","toast.no_entity":"Das Gerät hat keine geeignete Entität","toast.ha_disabled_action":"Ein deaktiviertes Home Assistant-Objekt kann im Plan nicht verwendet werden.","toast.ha_disabled_show_device":"Ein in Home Assistant deaktiviertes Gerät kann im Plan nicht angezeigt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_disabled_show_entity":"Eine in Home Assistant deaktivierte Entität kann im Plan nicht angezeigt werden. Aktivieren Sie sie zuerst in Home Assistant.","toast.ha_disabled_add":"Ein deaktiviertes Home Assistant-Objekt kann nicht dem Plan hinzugefügt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_binding_unverified":"Der Status des Objekts konnte über das Home Assistant-Register nicht überprüft werden. Anzeige und Aktionen sind vorübergehend nicht verfügbar.","toast.markup_needs_server":"Markup ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde.","toast.space_order_changed":"Reihenfolge geändert. Wenn eine Karte ihr Stockwerk nach Nummer festlegt, überprüfen Sie diese Paneele.","toast.conflict":"Die Konfiguration wurde in einem anderen Fenster geändert — Daten aktualisiert, wiederholen Sie Ihre letzte Aktion.","toast.cfg_save_failed":"Speichern der Konfiguration fehlgeschlagen: {err}","toast.room_overlap":"Die Umrisse überlappen den Raum „{name}“ — Räume dürfen sich nicht überlappen","toast.contour_cannot_close":"Die Kontur kann nicht geschlossen werden, da sie entartet ist oder sich selbst schneidet","toast.merge_not_adjacent":"Es können nur Räume zusammengeführt werden, die eine Wand teilen","toast.rooms_merged":"Räume zu „{name}“ zusammengeführt","toast.split_pick_wall":"Beginnen Sie den Schnitt an der Wand des Raumes","toast.split_bad_cut":"Der Schnitt muss wand-zu-wand innerhalb des Raumes verlaufen, ohne Wände oder sich selbst zu kreuzen","merge.header":"Räume zusammenführen","merge.hint":"Der zusammengeführte Raum behält einen Namen und eine Fläche. Die andere Fläche wird freigegeben — ihre Geräte verlassen den Plan, bis ein anderer Raum sie beansprucht.","merge.keep":"Behalten","merge.no_area":"keine Fläche","toast.room_saved":"Raum gespeichert ({n}). Hinzugefügte Geräte: {added}. Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.room_saved_no_area":"Raum gespeichert ({n}, keine Fläche). Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.marker_needs_server":"Die Gerätebearbeitung ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde","toast.virtual_name_required":"Einen Namen für das virtuelle Gerät eingeben","toast.marker_saved":"Gerät gespeichert","toast.marker_removed":"Gerät aus dem Plan gelöscht","toast.integration_missing":"Die House Plan-Integration ist nicht installiert — Verwaltung nicht verfügbar","toast.plan_formats":"Unterstützte Formate: SVG, PNG, JPG, WebP","toast.plan_required":"Grundriss hochladen — erforderlich","toast.space_added_onboard":"Bereich hinzugefügt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","toast.space_added":"Bereich hinzugefügt","toast.space_saved":"Bereich gespeichert","toast.space_deleted":"Bereich gelöscht","toast.delete_failed":"Löschen fehlgeschlagen: {err}","toast.error":"Fehler: {err}","toast.toggle_target_unavailable":"Ziel „{name}“ ist nicht verfügbar — keine Aktion durchgeführt","toast.toggle_targets_unavailable":"Ziele nicht verfügbar: {names}. Keine Aktion durchgeführt","toast.file_failed":'Datei "{name}" wurde nicht hochgeladen: {err}',"toast.files_attached":"Dateien angehängt: {n}","err.unknown":"unbekannter Fehler","err.code":"Code {code}","err.too_large":"Datei größer als {mb} MB","err.bad_ext":"nicht unterstützter Typ (PDF/Bild erwartet)","err.unauthorized":"Administratorrechte erforderlich","editor.title":"Titel","editor.default_floor":"Anfangsbereich","editor.default_floor_missing":"Anfangsbereich „{id}“ existiert nicht mehr. Wählen Sie einen anderen Bereich.","editor.floor":"Fester Bereich","editor.floor_none":"— nicht fest —","editor.floor_index":"YAML-Index {index} (beibehalten)","fixed_floor.loading":"Lade den festen Bereich…","fixed_floor.invalid_title":"Der feste Bereich ist nicht verfügbar","fixed_floor.invalid_body":"Überprüfen Sie die Etage in der Kartenkonfiguration. Der konfigurierte Wert ist: {value}","editor.icon_size":"Symbolgröße, % der Planbreite","editor.show_temperature":"Temperatur anzeigen","editor.live_states":"Live-Zustände (ein/aus, offen…)","editor.light_pools":"Lichtkegel und Wandschatten","editor.show_signal":"Zigbee-Signal anzeigen (LQI)","editor.language":"Schnittstellensprache","editor.lang_auto":"Automatisch (HA-Profil)","title.icon_rules":"Symbolregeln: welche MDI-Symbole Geräte nach Namen erhalten","rules.title":"Symbolregeln","rules.hint":'Regeln werden von oben anhand von "Gerätename + Modell" (Großschreibungs-unempfindliches Regex) überprüft; das erste Match gewinnt. Wenn nichts übereinstimmt, entscheidet die Entität-Geräteklasse, dann das generische Chip-Symbol.',"rules.pattern_ph":"regex, z. B. Stecker|Steckdose","rules.icon_ph":"mdi:power-socket-de","rules.add":"Regel hinzufügen","rules.reset":"Zurücksetzen auf Standardeinstellungen","rules.test_ph":"Versuch mal einen Gerätenamen...","rules.invalid":"ungültiges regex","rules.saved":"Icon-Regeln gespeichert","btn.up":"Nach oben","btn.down":"Nach unten","tap.info":"Gerätekarte","tap.more_info":"HA Mehr-Info-Dialog","tap.toggle":"Zustand umschalten","marker.tap_label":"Tap-Aktion für dieses Gerät","tap.toggle_note":"Der aufgelöste Zustand ist unten dargestellt. Sichere Geräte werden niemals aus dem Plan umgeschaltet.","import.title":"Bereiche aus HA-Etagen erstellen","import.hint":"Home Assistant kennt diese Etagen bereits. Die Etagen auswählen, die zu Planbereichen werden sollen; anschließend für jeden Bereich einen Grundriss hochladen. Die Räume werden danach von Hand eingezeichnet.","import.start":"{n} Bereich(e) erstellen","import.manual":"Von Grund auf neu starten","import.progress":"Etage {i} von {n}","import.done":"Bereiche erstellt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","btn.skip":"Überspringen","space.scale_label":"Maßstab (Rasterzellen-Größe)","space.scale_unit":"cm pro Zelle","space.scale_unit_imperial":"Zoll pro Zelle","space.display_section":"Anzeige","space.show_borders":"Raumgrenzen immer anzeigen","space.zero_wall_style":"Wände ohne Dicke","space.zero_wall_dashed":"Gestrichelt","space.zero_wall_solid":"Durchgezogen","space.zero_wall_help":"Gestrichelte Wände lassen Licht durch. Durchgezogene Wände blockieren Licht auch bei einer Dicke von 0.","space.show_names":"Raumnamen anzeigen (zum Verschieben ziehen)","space.room_color":"Farbe der Begrenzung & des Namens","space.opacity":"Deckkraft","space.fill_label":"Raumausfüllung","fill.none":"Keine","fill.lqi":"Zigbee-Signal","fill.light":"Lichter","fill.custom":"Benutzerdefinierte Farbe","space.custom_fill":"Füllfarbe","space.source_file":"Ich habe einen Grundriss als Bild","space.source_draw":"Kein Bild – ich werde die Räume von Hand umreißen","fill.temp":"Temperatur","tip.temp_avg":"Durchschnittstemperatur:","tip.hum_avg":"Durchschnittliche Luftfeuchtigkeit:","space_card.button":"Grundriss öffnen","space_card.not_found":"Bereich „{id}“ nicht gefunden","space_card.loading":"Laden…","continuity.restore_plan":"Grundriss wiederherstellen…","continuity.restore_connection":"Geräteverbindung wiederherstellen…","continuity.retry":"Erneut versuchen","editor.space":"Bereich","editor.framing":"Bildausschnitt","editor.fit_content":"Alle sichtbaren Inhalte","editor.fit_house":"Eng an der Hausgeometrie","editor.show_button":"Schaltfläche anzeigen","editor.button_label":"Beschriftung der Schaltfläche","editor.button_target":"Ziel-Dashboard-Pfad","marker.sub_entity":"Entität","title.general_settings":"Allgemeine Einstellungen","gs.title":"Allgemeine Einstellungen","gs.hint":"Füllfarben gelten für jeden Bereich; jede Farbe hat ihre eigene Deckkraft. Der Füllmodus wird in den Einstellungen des jeweiligen Bereichs festgelegt.","gs.light_group":"Füllung: Licht","gs.light_on":"Lichter an","gs.light_off":"Alle Lichter aus","gs.temp_group":"Füllung: Temperatur","gs.temp_cold":"Kalt","gs.temp_ok":"Angenehm","gs.temp_hot":"Heiß","gs.lqi_group":"Füllung: Zigbee-Signal","gs.lqi_low":"Schwaches Signal","gs.lqi_high":"Starkes Signal","gs.reset":"Auf Standard zurücksetzen","gs.saved":"Allgemeine Einstellungen gespeichert","space.show_lqi":"Zigbee-Signal (LQI) neben Geräten anzeigen","space.hide_decor":"Dekorationsschicht ausblenden","space.hide_decor_tip":"Linien, Formen, Beschriftungen und Möbel bleiben an ihrem Platz — im Hintergrundeditor sichtbar, aber nicht auf dem Plan.","space.hide_openings":"Öffnungen ausblenden","space.hide_openings_tip":"Tür-, Fenster- und Tor-Symbole werden nicht gezeichnet, aber die Öffnungen funktionieren weiterhin: Licht fällt hindurch, die Sonne kommt durch ein Fenster herein, Kontaktsensoren öffnen sie weiterhin. Der Planeditor zeigt sie immer an.","gs.light_none":"Keine Lichtquellen","mode.plan":"Planeditor","mode.devices":"Geräteeditor","display.value":"Wert + Zustand","marker.subarea":"kein Bereich, manuell","device.new":"Neues Gerät — Editor öffnen, um zu schließen","opening.unlock_action":"Entsperren","opening.lock_action":"Sperren","opening.lock_pending":"Wird ausgeführt…","title.close_editor":"Editor schließen (zur Ansicht zurück)","title.add_device":"Gerät zum Plan hinzufügen","devbar.add":"Hinzufügen","devbar.rules":"Symbolregeln","device_inbox.button":"Geräte","device_inbox.title":"Geräte auf dem Plan","device_inbox.search":"Geräte, Entitäten und Integrationen suchen…","device_inbox.add_virtual":"Virtuelles Gerät hinzufügen","device_inbox.tab_on_plan":"Auf Plan","device_inbox.tab_available":"Verfügbar","device_inbox.tab_hidden":"Versteckt","device_inbox.tab_readd":"Wieder verfügbar","device_inbox.only_new":"Nur neu","device_inbox.show_entities":"Entitäten anzeigen","device_inbox.show_hidden":"Versteckte auf Plan anzeigen","device_inbox.new":"Neu","device_inbox.find":"Auf dem Plan finden","device_inbox.edit":"Bearbeiten","device_inbox.hide":"Ausblenden","device_inbox.show":"Anzeigen","device_inbox.add":"Hinzufügen","device_inbox.readd":"Wieder hinzufügen","device_inbox.hide_available":"Aus der Liste ausblenden","device_inbox.show_more":"Mehr anzeigen","device_inbox.more_actions":"Weitere Aktionen","device_inbox.find_hidden_hint":"Zuerst „Versteckte auf Plan anzeigen“ aktivieren","device_inbox.show_disabled":"Diese Bindung in Home Assistant aktivieren, bevor sie angezeigt wird","device_inbox.empty_on_plan":"Es befinden sich noch keine passenden Geräte auf dem Plan.","device_inbox.empty_available":"Es sind keine Geräte zum Hinzufügen verfügbar.","device_inbox.empty_hidden":"Es gibt keine versteckten Geräte.","device_inbox.empty_readd":"Es sind keine entfernten Geräte verfügbar, um sie erneut hinzuzufügen.","device_inbox.reason_visible_auto":"Automatisch erkannt","device_inbox.reason_visible_explicit":"Explizit hinzugefügt","device_inbox.reason_manual_hidden":"Vom Benutzer versteckt","device_inbox.reason_automatic_hidden":"Automatisch versteckt","device_inbox.reason_service_entry":"Nur Service-Registry-Eintrag","device_inbox.reason_excluded_integration":"Integration durch Gerätefilter ausgeschlossen","device_inbox.reason_excluded_domain":"Nicht-räumlicher Entitätstyp","device_inbox.reason_grouped_light":"Dargestellt durch eine Raumlichtgruppe","device_inbox.reason_represented_by_parent":"Dargestellt durch sein übergeordnetes Gerät","device_inbox.reason_removed":"Zuvor aus dem Plan entfernt","device_inbox.reason_available":"Zum Hinzufügen zum Plan verfügbar","device_inbox.reason_no_bound_room":"HA-Bereich ist nicht mit einem Planraum verknüpft","device_inbox.status_ha_disabled":"In Home Assistant deaktiviert","device_inbox.status_orphaned":"Bindung ist in Home Assistant nicht mehr vorhanden","device_inbox.status_unverified":"Home Assistant-Register vorübergehend nicht verfügbar","device_inbox.saved":"Geräteliste aktualisiert","space.roomcard_section":"Raumkarte zeigt:","space.label_temp":"Temperatur","space.label_hum":"Luftfeuchtigkeit","space.label_lqi":"Durchschnittliches Zigbee-Signal","space.label_light":"Lichter ein/aus","roomcard.light_on":"Ein","roomcard.light_off":"Aus","roomcard.light_partial":"{on} von {total}","toast.split_pick_inside":"Zwischenliegende Schnittpunkte müssen innerhalb des Raumes liegen","mode.decor":"Hintergrundeditor","decor.select":"Auswählen","decor.line":"Linie","decor.rect":"Rechteck","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Löschen","decor.erase_confirm_title":"Objekt löschen?","decor.color":"Farbe","decor.width":"Linienbreite","decor.line_style":"Linienstil","decor.line_style_solid":"Durchgehend","decor.line_style_dashed":"Gepunktet","decor.fill":"Füllen","decor.fill_color":"Füllfarbe","decor.length":"Länge","decor.size":"Größe","decor.angle":"Rotationswinkel, °","decor.text_size":"Textgröße","decor.backdrop_properties":"Eigenschaften des Planhintergrunds","decor.text_title":"Textbeschriftung","decor.object_title":"{kind} bearbeiten","decor.text_label":"Text","decor.live_group":"HA-Variable einfügen","decor.live_entity":"Entität","decor.live_entity_ph":"Wählen Sie eine Entität","decor.live_attr":"Wert","decor.live_attr_ph":"Wählen Sie Status oder Attribut","decor.live_state":"Status","decor.backdrop":"Hintergrundbild","decor.backdrop_hint":"Ziehen zum Verschieben; eine Ecke ziehen zum Größenändern; den oberen Griff zum Drehen verwenden. Shift ändert das Verhältnis oder befreit den Winkel.","decor.backdrop_reset":"Bild zurücksetzen","decor.backdrop_reset_done":"Das Bild ist wieder an seinem ursprünglichen Platz und in seiner ursprünglichen Größe","marker.icon_auto":"Auto: {icon} (nach Symbolregeln; wählen Sie eines zur Überschreibung)","marker.icon_pin_auto":"Anheften","mode.plan_tip":"Planungseditor — die Geometrie des Hauses: Räume zeichnen und teilen/zusammenführen, sie mit HA-Bereichen verbinden, Türen, Fenster und Tore platzieren, Raumkarten verschieben, Maßstab festlegen","mode.devices_tip":"Geräteeditor — alles über Symbole: ziehen zum Positionieren, klicken zum Bearbeiten von Bindung/Symbol/Anzeige, virtuelle Geräte hinzufügen, Symbolregeln","mode.decor_tip":"Hintergrundeditor — rein visuelle Dekoration unter dem Plan: Linien, Rechtecke, Ellipsen und Textbeschriftungen, die niemals auf Klicks reagieren","space.glow_enabled":"Lichtquellen-Glühen","gs.glow_group":"Glow von Lichtquellen","gs.glow_base":"Grunddunkelheit des Hauses","gs.glow_light":"Standardlichtfarbe / Intensität","gs.wall_group":"Wände","gs.wall_fill":"Wandfüllung","gs.glow_radius":"Glow-Radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Steuert andere Lichtquellen","marker.controls_hint":"Mit der Tippen-Aktion „Umschalten“ wechselt ein Klick jede hier hinzugefügte Quelle. Die eigene zugeordnete Entität des Markers wird direkt gesteuert und kann nicht hinzugefügt werden; verwenden Sie den untenstehenden Schalter, um ihn als Licht zu klassifizieren.","marker.controls_filter":"Suchlichter und Schalter…","info.controls":"Steuerungen","marker.glow_radius_label":"Leuchtradius","marker.glow_radius.help":"Legt den Leuchtradius in Metern oder Fuß fest; ein leerer Wert verwendet den Radius aus den allgemeinen Einstellungen.","marker.glow_radius.help.aria":"Hilfe: Leuchtradius","markup.wallthick":"Dicke","markup.select":"Auswählen","title.markup_wallthick":"Dicke — klicken Sie auf eine Wand, um ihre Dicke von 0 bis 100 cm festzulegen.","markup.hint_wallthick":"klicken Sie auf eine Wand · Esc schließt ohne Anwendung","wallthick.field":"Dicke","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Auf alle Wände dieses Raums anwenden","markup.draw_wall_title":"Dicke jedes neuen Wandsegments (0–100 cm). Gemeinsame Wände behalten den bestehenden Wert.","room.queue_progress":"Raum {current} von {total}","toast.wall_rooms_saved":"Erstellte Räume: {n}","toast.wall_chain_saved":"Wandkette gespeichert","toast.wallthick_pick":"Eine Wand anklicken","toast.wallthick_set":"Wandstärke eingestellt","toast.wallthick_cleared":"Wandstärke entfernt","toast.physical_range":"Einen Wert von {min} bis {max} {unit} eingeben","toast.zero_wall_opening_conflict":"Entfernen Sie zuerst die Öffnung an diesem Wandsegment.","toast.zero_wall_ambiguous":"Das Wandsegment ist mehrdeutig. Vereinfachen oder passen Sie den Anschluss an.","toast.zero_wall_migration_blocked":"Der Bereich wurde nicht konvertiert: {reason}. Es wurden keine Daten geändert.","toast.physical_angle":"Einen Drehwinkel von 0° bis unter 90° eingeben","toast.physical_limit":"Der Bereich hat das Limit für diesen Geometrietyp erreicht","toast.geometry_unsafe":"Änderung abgebrochen: Wandgeometrie konnte nicht sicher erstellt werden.","junction.limit_angle":"Der Winkel zwischen den Wänden ist zu spitz: {actual}°, mindestens {limit}°.","junction.limit_valence":"Zu viele Wände treffen in einem Knoten aufeinander: {actual}, maximal {limit}.","junction.limit_length":"Das Wandsegment ist zu kurz: {actual} cm, mindestens {limit} cm.","junction.limit_distance":"Wände und Knoten sind zu dicht beieinander: {actual} cm, Minimum {limit} cm.","junction.limit_clearance":"Kein Wohnbereich ist übrig: {actual} cm², Minimum {limit} cm².","junction.limit_check_failed":"Die Überprüfung der Verbindungen konnte nicht durchgeführt werden — die Änderung wurde nicht gespeichert.","toast.wall_model_migration_blocked":"Das Wandmodell konnte nicht aktualisiert werden: {reason}. Der Plan wurde nicht geändert. Führen Sie „Pläne optimieren“ aus; wenn der Fehler erneut auftritt, beheben Sie die widersprüchliche Wandgeometrie.","toast.wall_model_client_outdated":"Aktualisieren Sie die Karte und laden Sie die Seite neu, bevor Sie den Plan bearbeiten.","wall_model.reason.invalid-room":"ungültiger Raumkontur","wall_model.reason.zero-length":"Wandsegment mit null Länge","wall_model.reason.third-owner":"Wand, die von mehr als zwei Räumen geteilt wird","wall_model.reason.duplicate-id":"widersprüchliche Wandkennungen","wall_model.reason.thickness-conflict":"widersprüchliche Wandstärkenwerte","wall_model.reason.opening-host":"Öffnung kann nicht einer Wand zugewiesen werden","toast.delete_room_pick":"Klicken Sie in den Raum, den Sie löschen möchten","toast.plan_snap_ambiguous":"Zoomen Sie hinein, um den Wandknoten auszuwählen","toast.wall_repair_ambiguous":"Der Raumumriss hat mehr als eine mögliche Verbindung. Zoomen Sie hinein und verbinden Sie ihn explizit.","toast.wall_repair_too_large":"Die Lücke ist größer als 2 cm. Verbinden Sie die Wände, bevor Sie den Raum erstellen.","toast.wall_repair_changed":"Die Wandgeometrie hat sich geändert. Versuchen Sie erneut, den Raum zu erstellen.","toast.opening_on_zero_wall":"Öffnungen können nicht auf einer Wand mit null Dicke sitzen.","marker.from_ha_option":"Wählen Sie aus der HA-Liste.","marker.show_entities":"Entitäten anzeigen.","marker.show_entities_tip":"Fügt nicht nur Geräte zur Liste hinzu, sondern auch alle ihre Entitäten.","marker.pick_ph":"Wählen Sie ein Gerät…","room.open_area":"Öffnen Sie den HA-Bereich.","view.volumetric":"Volumetrische Ansicht.","view.flat":"Flache Ansicht.","kiosk.title":"Die Größen dieses Bildschirms.","kiosk.hint":"Nur auf diesem Gerät gespeichert – jedes Wandtablet oder TV kann seine eigenen komfortablen Größen haben.","kiosk.icon_scale":"Gerätesymbolgröße.","kiosk.font_scale":"Textgröße der Raumkarte.","editor.kiosk":"Wandgeräte- (Kiosk-)Modus.","editor.cycle":"Bereiche automatisch alle N Sekunden wechseln (Kiosk, 0 = aus)","room.settings_title":"Raumeinstellungen.","room.settings_section":"Raumeinstellungen (überschreibt den Bereich)","room.fill_label":"Diesen Raum ausfüllen","fill.inherit":"Als den Bereich","room.custom_fill_space":"Bereichsfarbe","room.custom_fill_own":"Raumfarbe","room.temp_src_label":"Temperaturquelle","room.hum_src_label":"Luftfeuchtigkeitsquelle","room.src_average":"Durchschnitt über die Sensoren des Raums (Standard)","room.src_pick":"Ein bestimmtes HA-Gerät oder Entität","room.src_ph":"Quelle wählen…","toast.room_updated":"Raum aktualisiert","space.card_font":"Schriftgröße der Raumkarte (gesamter Bereich)","room.sizes_section":"Schriftgrößen","room.name_scale":"Raumname Größe","room.label_scale":"Metrikgröße","preview.room_name":"Wohnzimmer","toast.cfg_reload_failed":"Der Plan konnte nicht vom Server neu geladen werden: {err}","toast.locale_load_failed":"Das Sprachpaket konnte nicht geladen werden. Bis zum Neuladen der Seite wird Englisch verwendet.","room.settings_short":"Raumeinstellungen","room.unnamed":"Unbenannter Raum","marker.use_climate_temp":"Geräte-Temperatur im Raum berücksichtigen","marker.use_climate_temp_tip":"Fügt die aktuelle Temperatur einer Klimaanlage oder eines Thermostats zum Raumdurchschnitt hinzu. Das externe Wert-Label unten separat konfigurieren.","marker.value_badge_title":"Wert-Label","marker.value_badge.help":"Zeigt einen ausgewählten Wert neben dem Symbol an. Dies beeinflusst nicht die Raumkennzahlen, Licht, Glanz oder die Tippaktion.","marker.value_badge.help.aria":"Hilfe: Gerätewert-Badge","marker.value_badge_enabled":"Zeige ein Wert-Badge","marker.value_badge_source":"Wert","marker.value_badge_source.help":"Wählen Sie einen bestimmten Zustand, ein unterstütztes Attribut oder einen abgeleiteten Wert dieses Geräts.","marker.value_badge_source.help.aria":"Hilfe: Quelle des Wert-Badges","marker.value_badge_position":"Position","marker.value_badge_position.help":"Die gewählte Seite bleibt fest und dreht sich nicht automatisch an einem Planrand.","marker.value_badge_position.help.aria":"Hilfe: Position des Wert-Badges","marker.value_badge_right":"Rechts","marker.value_badge_bottom":"Unten","marker.value_badge_left":"Links","marker.value_badge_top":"Oben","marker.value_badge_empty":"Für dieses Gerät stehen keine Werte zur Verfügung","marker.value_badge_static":"Ein statisches Symbol zeigt keine Live-Werte an. Die Einstellung wird beibehalten.","marker.value_badge_missing":"Quelle nicht verfügbar","marker.value_badge_missing_hint":"Die gespeicherte Quelle ist derzeit nicht verfügbar. Behalten Sie sie bei, deaktivieren Sie das Badge oder wählen Sie einen anderen Wert.","marker.value_badge_duplicate":"Derselbe Wert wird bereits im Symbol angezeigt.","marker.value_badge_state":"Status · {name}","marker.value_badge_marker_state":"Lichtquellstatus · {name}","marker.value_badge_lqi":"Durchschnittliche Zigbee-Signalqualität","marker.value_badge_attr_current_temperature":"Aktuelle Temperatur · {name}","marker.value_badge_attr_temperature":"Zieltemperatur · {name}","marker.value_badge_attr_current_humidity":"Aktuelle Luftfeuchtigkeit · {name}","marker.value_badge_attr_humidity":"Luftfeuchtigkeit · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Geschwindigkeit · {name}","marker.value_badge_attr_brightness":"Helligkeit · {name}","marker.value_badge_attr_volume_level":"Lautstärke · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Lüftergeschwindigkeit · {name}","marker.value_source":"Wertquelle","marker.value_source.help":"Wähle den Wert, der das Symbol ersetzt. Die Tipp-Aktion bleibt unverändert.","marker.value_source.help.aria":"Hilfe: Wertquelle in der Gerätefläche","marker.value_source_auto":"Automatisch (wie bisher)","marker.value_source_missing_hint":"Die gespeicherte Quelle ist derzeit nicht verfügbar. Bis sie wieder verfügbar ist oder ein anderer Wert gewählt wird, zeigt die Fläche —.","marker.light_role_label":"Ist dieses Gerät eine Lichtquelle?","marker.light_role.help":"Steuert, ob dieser Marker selbst eine räumliche Lichtquelle ist. Verknüpfte Lichter oben bleiben unabhängige Raumlichtsteuerungen.","marker.light_role.help.aria":"Hilfe: ob dieses Gerät eine Lichtquelle ist","marker.light_role_auto_yes":"Automatisch (Lichtquelle)","marker.light_role_auto_no":"Automatisch (keine Lichtquelle)","marker.light_role_always":"Immer eine Lichtquelle","marker.light_role_never":"Nie eine Lichtquelle","marker.light_entity_label":"Führende Lichtereinheit","marker.light_entity.help":"Für ein Verbundgerät wählen Sie die Entität, deren Zustand, Farbe und Helligkeit diese Planquelle darstellen. Der automatische Rückgriff bleibt mit älteren Plänen kompatibel.","marker.light_entity.help.aria":"Hilfe: führende Lichtentität","marker.light_entity_auto":"Automatisch ({entity})","marker.light_entity_none":"keine steuerbare Entität","marker.light_entity_missing":"Die gespeicherte Entität {entity} ist nicht verfügbar. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.toggle_entity_label":"Zu toggelnde Entität","marker.toggle_entity.help":"Für ein Verbundgerät wählen Sie das genaue eigene Licht oder den Schalter, der durch Tippen bedient wird. Automatisch behält die vorherigen Zielregeln bei und ist unabhängig von der führenden Lichtentität.","marker.toggle_entity.help.aria":"Hilfe: zu toggelnde Entität","marker.toggle_entity_auto":"Automatisch ({entity})","marker.toggle_entity_none":"keine eigene steuerbare Entität","marker.toggle_entity_missing":"Die gespeicherte Entität {entity} ist nicht mehr unter den auswählbaren Kanälen dieses Markers. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.glow_color_label":"Leuchtfarbe und Helligkeit","marker.glow_mode.help":"Verwenden Sie Live-Quellwerte, überschreiben Sie nur deren Farbe oder fixieren Sie sowohl Farbe als auch Helligkeit. Das Minimum beträgt 1 %; um die Quelle zu deaktivieren, wählen Sie „Nie“.","marker.glow_mode.help.aria":"Hilfe: Leuchtfarbe und Helligkeit","marker.glow_mode_auto":"Von Quelle","marker.glow_mode_color":"Farbe einstellen","marker.glow_mode_fixed":"Farbe und Helligkeit einstellen","marker.glow_color":"Leuchtfarbe","marker.glow_brightness":"Helligkeit","marker.glow_disabled_never":"Leuchteinstellungen sind nicht verfügbar, da dieser Marker ausdrücklich keine Lichtquelle ist.","marker.glow_disabled_auto":"Im Automatikmodus wurde keine räumliche Lichtquelle für diesen Marker gefunden.","marker.glow_disabled_no_entity":"Keine aktive steuerbare Home Assistant-Entität ist für diese räumliche Quelle verfügbar.","marker.glow_passive_hint":"Diese Quelle hat keine eigenen Home Assistant-Daten. Stellen Sie ihre Farbe und Helligkeit manuell ein; ihr Radius bleibt verfügbar.","marker.control_broken":"Gespeicherte Quelle fehlt oder ist nicht mehr als Licht markiert","marker.control_missing_label":"Fehlendes Planlicht","marker.control_passive":"passive Quelle","toast.marker_control_cycle":"Dieser Link würde eine kreisförmige Kette von Lichtsteuerungen erzeugen.","toast.marker_binding_required":"Wählen Sie ein Home Assistant-Gerät, bevor Sie eine andere Lichtquelle verlinken.","confirm.unlock":"„{name}“ entsperren?","toast.files_migrate_failed":"Anhänge konnten nicht zur neuen Bindung verschoben werden, Links zeigen weiterhin auf die alten Dateien: {err}","space.pick_saved":"Bereits hochgeladen","space.pick_saved_hint":"Auf dem Server gespeicherte Pläne, einschließlich solcher, die Sie zuvor getrennt haben","space.no_saved":"Noch keine Pläne auf dem Server gespeichert.","space.loading":"Laden…","space.used_by":"in Benutzung: {list}","space.in_use":"Ein Bereich verwendet diesen Plan noch — zuerst die Verknüpfung lösen","btn.use":"Verwenden","confirm.delete_plan":"Plandatei „{name}“ vom Server löschen? Dies kann nicht rückgängig gemacht werden.","toast.plans_list_failed":"Konnte die gespeicherten Pläne nicht auflisten: {err}","toast.plan_delete_failed":"Konnte den Plan nicht löschen: {err}","marker.hide":"Ausblenden","marker.hide_tip":'Das Gerät wird nach dem Speichern aus dem Plan verschwinden, aber weiterhin zum Signalsignal des Raums zählen. Stellen Sie es über den Katalog "Geräte" im Geräte-Editor wieder her.',"marker.show":"Anzeigen","marker.show_tip":"Das Gerät wird nach dem Speichern wieder im Plan erscheinen.","marker.hidden_ghost":"Vom Benutzer ausgeblendetes Gerät","marker.ha_disabled_device":"Das Gerät ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_entity":"Die Entität ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_all_entities":"Das Gerät hat keine aktiven Home Assistant-Entitäten, daher ist es im Plan ausgeblendet.","marker.ha_registry_limited":"Das vollständige Home Assistant-Register ist für diesen Benutzer nicht verfügbar. Das nicht verifizierte Objekt kann vorerst nicht angezeigt oder verwendet werden.","marker.delete_tip":"Löschen Sie das Gerät vollständig aus dem Plan und allen Aggregaten. Sie können es später wieder hinzufügen.","tap.run":"Führen Sie Automation/Skript/Szene aus","marker.run_target_label":"Was ausführen","marker.run_search_ph":"Suchen: Automation, Skript oder Szene…","marker.run_target_gone":"Ziel {id} nicht gefunden — erneut auswählen","marker.tap_confirm":"Um Bestätigung bitten","marker.tap_confirm_tip":"Vor der Ausführung ein Bestätigungsdialogfeld anzeigen — eine Vorsichtsmaßnahme gegen versehentliche Berührungen.","marker.toggle_hint_single":"Ziel: {name} ({id}).","marker.virtual_light_target":"Virtuelles Licht: {name}.","marker.virtual_light_current":"Manueller Zustand: {state} → {effect}.","marker.virtual_light_state_on":"ein","marker.virtual_light_state_off":"aus","marker.toggle_hint_group":"Wird {count}-Quelle(n) umschalten: {names}.","marker.toggle_hint_current":"Jetzt: {state} → {effect}.","marker.toggle_hint_group_current":"Derzeit an: {on} von {count} → {effect}.","marker.toggle_hint_skipped":"Wird übersprungen ({count}): {targets}.","marker.toggle_effect_turn_on":"wird eingeschaltet","marker.toggle_effect_turn_off":"wird ausgeschaltet","marker.toggle_effect_open":"wird geöffnet","marker.toggle_effect_close":"wird geschlossen","marker.toggle_effect_stop":"wird gestoppt","marker.toggle_effect_toggle":"Zustand wird umgeschaltet","marker.toggle_skip_missing":"nicht gefunden","marker.toggle_skip_ha_disabled":"in HA deaktiviert","marker.toggle_skip_unavailable":"nicht verfügbar","marker.toggle_skip_unsupported":"Umschalten wird nicht unterstützt","marker.toggle_skip_secure":"aus Sicherheitsgründen blockiert","marker.toggle_none_no_binding":"Dieses Gerät hat keine Bindung, daher gibt es keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_no_actionable_entity":"Dieses Gerät hat keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_configured_targets_missing":"Die konfigurierten Ziele sind nicht verfügbar. Die eigene Entität des Geräts wird nicht ersetzt.","marker.toggle_none_ha_disabled":"Das Ziel ist in Home Assistant deaktiviert und kann im Plan nicht verwendet werden.","marker.toggle_none_unavailable":"Das Ziel ist derzeit nicht verfügbar. Tippen bewirkt nichts.","marker.toggle_none_unsupported":"Home Assistant bietet keine sichere Umschaltfunktion für dieses Gerät. Tippen bewirkt nichts.","marker.toggle_none_secure":"Das Umschalten von Schlössern, Alarmen und gesicherten Toren über den Plan ist aus Sicherheitsgründen gesperrt.","run.automation":"Automatisierung","run.script":"Skript","run.scene":"Szene","confirm.tap_run":'"{name}" ausführen?',"confirm.tap_toggle":'"{name}" umschalten?',"confirm.current_state":"Aktueller Zustand: {state}","confirm.expected_state":"Nach dem Umschalten: {state}","confirm.group_current":"an {on} von {total}","confirm.group_all_on":"alle sind eingeschaltet","confirm.group_all_off":"alle sind ausgeschaltet","confirm.unavailable_targets":"Nicht verfügbar: {count}","confirm.expected_by_ha":"Home Assistant ermittelt den Zustand","confirm.state_on":"Ein","confirm.state_off":"Aus","confirm.state_open":"Offen","confirm.state_closed":"Geschlossen","confirm.state_opening":"Wird geöffnet","confirm.state_closing":"Wird geschlossen","confirm.state_stopped":"Gestoppt","confirm.state_unknown":"Unbekannt","toast.run_started":"Gestartet: {name}","toast.run_target_missing":"Ausführungsziel nicht gefunden — überprüfen Sie die Geräteeinstellungen","toast.run_target_required":"Wählen Sie eine Automatisierung, ein Skript oder eine Szene aus","toast.tap_target_changed":"Das Ziel der Aktion hat sich geändert. Versuchen Sie es erneut.","toast.virtual_light_toggle_failed":"Das virtuelle Licht konnte nicht umgeschaltet werden: {err}","toast.value_badge_source_required":"Einen Wert für den Badge auswählen","btn.run":"Ausführen","vac.section":"Roboterstaubsauger: aktuelle Position","vac.autocal":"Automatisch einrichten","vac.live":"Aktuelle Position im Plan","vac.trail":"Roboterpfad anzeigen","vac.cal_maps":"Kalibrierte Karten: {maps}","vac.autocal_no_rooms":'Die Integration meldet keine Raumliste — öffnen Sie "Manuell anpassen"',"vac.autocal_no_match":'Raumnamen stimmten nicht überein (mindestens 3 müssen übereinstimmen) — öffnen Sie "Manuell anpassen"',"vac.autocal_done":"Fertig: über {rooms} Räume zugeordnet. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_need_pos":"Der Roboter meldet keine Koordinaten — starten Sie eine Reinigung und pausieren Sie sie","vac.cal_done":"Kalibrierung gespeichert. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_cancelled":"Kalibrierung abgebrochen","vac.fit":"Manuell anpassen","vac.fit_hint":"Roboterkarte an die richtige Position ziehen und an den Ecken skalieren","vac.fit_rotate":"90° drehen","vac.fit_mirror":"Spiegeln","vac.trail_never":"Nie","vac.trail_cleaning":"Beim Reinigen","vac.trail_always":"Immer","gs.bg_group":"Bühnenhintergrund","gs.bg_color":"Hintergrund um den Plan","gs.bg_default":"Standardthema","gs.bg_theme":"Standardthema","gs.bg_mode":"Planhintergrund","gs.bg_static":"Statische Farbe","gs.bg_daynight":"Folgt der Sonne","gs.bg_daynight_hint":"Der Hintergrund wechselt durch Morgendämmerung, Tag, Abenddämmerung und Nacht mithilfe von sun.sun oder Ihrer lokalen Uhr, wenn keine Sonnendaten verfügbar sind. Für den Hintergrund ist kein Kompass erforderlich.","gs.sun_group":"Sonne","gs.sun_missing":"Die sun.sun-Entität wurde nicht gefunden — der Hintergrund folgt Ihrer lokalen Uhr; Fensterstrahlen sind nicht verfügbar.","gs.north":"Norden auf dem Plan","gs.north_ph":"nicht gesetzt","gs.north_hint":"Norden nur für Fensterstrahlen einstellen (1° Schritte, 15° mit Shift); der Sonnenhintergrund funktioniert auch ohne ihn.","gs.north_clear":"Löschen","gs.north_letter":"N","gs.sun_rays":"Sonnenlicht durch Fenster","gs.about_group":"Über","gs.about_version":"House Plan Card v{v}","gs.about_github":"GitHub · Dokumentation & Probleme","gs.about_telegram":"Telegram-Chat","space.bg_color":"Hintergrund rund um den Plan","space.bg_inherit":"Allgemein erben","space.bg_inherited":"erbt allgemeine Einstellungen","space.bg_mode":"Planhintergrund","space.north":"Norden auf dem Plan (überschreiben)","space.north_inherited":"geerbt: {v}","space.sun_rays":"Sonnenlicht durch Fenster","space.sun_inherit":"Allgemein erben","space.sun_on":"Ein","space.sun_off":"Aus","canvas.far_objects":"{n} Objekt(e) weit vom Plan entfernt","canvas.show_far":"Anzeigen","canvas.home_tip":"Der Plan ist dort drüben — klicken, um ihn anzupassen","gs.grid_group":"Planwartung","gs.grid_hint":"Aktualisiert Datenmodelle, richtet Planelemente am Raster aus und fasst redundante Wandfragmente zusammen. Ein exakter Bericht wird angezeigt, bevor etwas gespeichert wird.","gs.align_all":"Pläne optimieren","gs.align_title":"Pläne optimieren","gs.align_none":"Alle Pläne verwenden bereits das aktuelle optimierte Datenmodell.","gs.optimize_no_automatic_changes":"Es gibt keine automatischen Änderungen, die angewendet werden müssen. Überprüfen Sie die unten stehenden Punkte.","gs.align_count":"{n} von {total} Elementen werden sich um höchstens {cm} cm bewegen.","gs.align_where":"Die größte Verschiebung ist in „{s}“.","gs.align_turned":"Öffnungen, deren Winkel korrigiert wurde: {n}.","gs.align_removed_drafts":"Ungültige Umrisse vom Raster zusammengeklappt und entfernt: {n}.","gs.optimize_redundant_drafts":"Gespeicherte Wandketten, die durch feste Raumwände verdeckt waren, wurden entfernt: {n}.","gs.align_preflight_failed":"Die Geometrie der folgenden Bereiche konnte nicht sicher überprüft werden: {spaces}{more}.","gs.align_preflight_hint":"Pläne wurden nicht geändert. Kopieren Sie die Diagnosen mit dem untenstehenden Button und fügen Sie sie zusammen mit einem Raumexport dem Fehlerbericht bei.","gs.preflight_reason_prepare-exception":"Die Bereichsgeometrie konnte nicht vorbereitet werden (Ausnahme beim Erstellen des Modells)","gs.preflight_reason_wall-null":"Der Wandkörper wurde nicht erstellt (die Vereinigung lieferte leer zurück)","gs.preflight_reason_wall-degraded-extra":"Der Wandkörper wurde mit zusätzlicher Geometrie beschädigt","gs.preflight_reason_wall-failed-core":"Der Kern des Wandkörpers konnte nicht zusammengesetzt werden","gs.preflight_reason_wall-exception":"Die Wandkonstruktion warf eine Ausnahme","gs.preflight_reason_floor-null":"Der Fußbodenumriss wurde nicht erstellt","gs.preflight_reason_floor-exception":"Bodenaufbau hat eine Ausnahme ausgelöst","gs.preflight_copy":"Diagnosen kopieren","gs.preflight_copied":"Diagnosen kopiert","gs.preflight_update_hint":"Die Karten- und Integrationsversionen unterscheiden sich — Hausplan aktualisieren und erneut versuchen.","gs.align_preflight_space":"Bereich {n}","gs.align_preflight_more":", und {n} mehr","gs.optimize_changes":"Modellmigrationen: {m}; aktualisierte Bereiche: {c}; entfernte verrauschte Koordinatenwerte: {p}; zusammengeführte Fragmente realer Wände: {w}; zusammengeführte Fragmente von Wänden ohne Dicke: {s}; unabhängige Wände: {i}.","gs.zero_walls_migrated":"Virtuelle Wandspannweiten konvertiert: {n}.","gs.wall_segments_migrated":"Wandsegmente stabilisiert: {n}.","gs.optimize_lattice_summary":"Rauschen in den Koordinatenwerten kanonisiert: {n}; maximale Bewegung: {cm} cm.","gs.optimize_lattice_space":"{space}: Koordinatenwerte kanonisiert: {n}; außerhalb des Rasters liegende Werte unverändert gelassen: {far}.","gs.optimize_coincident_partitions":"Versteckte unabhängige Wandabschnitte in Raumwände integriert: {n}.","gs.optimize_openings_rehosted":"Öffnungen wieder an Raumwände angebracht: {n}.","gs.optimize_walls_straightened":"Wände begradigt: {n}; maximale Bewegung: {cm} cm.","gs.optimize_walls_straightened_where":"Größte Wandkorrektur: {s}.","gs.optimize_walls_straighten_skipped":"Nahe Achsenwände unverändert gelassen, da sie nicht sicher repariert werden konnten: {n}.","gs.optimize_glow_migration":"Legacy Glow: {spaces} Bereiche → keine Datenfüllung + unabhängiges Glow; {rooms} Räume → geerbte Datenfüllung + unabhängiges Glow.","gs.optimize_references":"Referenzen repariert: Bereiche — {spaces}; Räume — {rooms}; Positionen — {positions}; Geräte aus fehlenden Bereichen gelöst — {detached}.","gs.optimize_reference_more":", und {n} mehr","gs.optimize_orphans_removed":"Vergessene Einträge entfernt: {total} — Raumbezeichnungen: {rooms}; Geräte: {devices}; Gruppenmarkierungen: {groups}. Sie gehörten zu zuvor gelöschten Bereichen.","gs.optimize_live_positions":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie werden beibehalten.","gs.optimize_live_positions_remove":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie sind zur Entfernung ausgewählt.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Alte Positionen entfernen","gs.optimize_live_keep":"Alte Positionen beibehalten","gs.optimize_live_selected":"Alte Positionen werden nach der Anwendung von Optimieren entfernt.","gs.optimize_unverified":"Positionen konnten nicht sicher überprüft werden: {n}. Sie wurden unverändert belassen.","gs.optimize_registry_limited":"Voller Administratorzugriff auf die Home Assistant-Registries ist für eine sichere Überprüfung erforderlich.","gs.optimize_vacuum_warning":"Raumzuordnungen, die noch überprüft werden müssen: {n}.","gs.optimize_details":"Details","gs.optimize_details_more":"Und {n} weitere Einträge.","gs.optimize_detail_removed":"wird entfernt","gs.optimize_detail_live":"wird beibehalten","gs.optimize_detail_unverified":"nicht überprüft","gs.optimize_detail_room_label":"Raumkennzeichnung","gs.optimize_detail_device":"Gerät","gs.optimize_detail_group":"Gruppenkennzeichen","gs.optimize_detail_unknown":"unbekannter Eigentümer","gs.optimize_detail_item":"{status}: {kind} {id}; alter Bereich {space}","gs.align_warn":"Absichtlich zwischen Rasterknoten platzierte Elemente werden sich bewegen. Nach der Operation ist nur eine Rückgängig-Funktion verfügbar, und zwar nur bis zur nächsten Planbearbeitung.","gs.align_run":"Optimieren","gs.align_done":"Pläne optimiert: {n} Elemente bewegt, {m} Datensätze beibehalten, {r} Verweise repariert","gs.optimize_undo":"Letzte Optimierung rückgängig machen","gs.optimize_undone":"Die letzte Optimierung wurde rückgängig gemacht","decor.furniture":"Möbel","furn.title":"Möbelbibliothek","furn.symbol":"Symbol","furn.group_furniture":"Möbel","furn.group_appliance":"Geräte","furn.group_sanitary":"Sanitär","furn.group_other":"Sonstiges","furn.width":"Breite","furn.depth":"Tiefe","furn.back_to_categories":"Alle Kategorien","furn.pick_hint":"Wählen Sie eine Kategorie und dann ein Symbol.","furn.place_hint":"Klicken Sie auf den Plan — das Element wird an der nächsten Wand platziert. Mit Shift wird es frei platziert.","furn.cat_air_conditioner":"Klimaanlagen","furn.cat_armchair":"Sessel","furn.cat_bathtub":"Badewannen","furn.cat_bed":"Betten","furn.cat_bidet":"Bidets","furn.cat_boiler":"Warmwasserbereiter","furn.cat_chair":"Stühle","furn.cat_coffee_table":"Couchtische","furn.cat_cooktop":"Kochfelder","furn.cat_dining_table":"Esstische","furn.cat_dishwasher":"Geschirrspüler","furn.cat_dryer":"Wäschetrockner","furn.cat_fireplace":"Kamine","furn.cat_fridge":"Kühlschränke","furn.cat_kitchen_cabinet":"Küchenschränke","furn.cat_kitchen_sink":"Küchenspülen","furn.cat_nightstand":"Schränke","furn.cat_plant":"Pflanzen","furn.cat_rug":"Teppiche","furn.cat_shelving":"Regale","furn.cat_shower":"Duschen","furn.cat_sink":"Waschbecken","furn.cat_sofa":"Sofas","furn.cat_stairs":"Treppen","furn.cat_toilet":"Toiletten","furn.cat_tv":"Fernseher","furn.cat_wardrobe":"Kleiderschränke","furn.cat_washer":"Waschmaschinen","furn.cat_work_table":"Schreibtische","furn.sym_sofa":"Zweisitzer-Sofa","furn.sym_sofa_three_seat":"Dreisitzer-Sofa","furn.sym_sofa_corner_right":"Ecksofa rechts","furn.sym_armchair":"Polstersessel","furn.sym_armchair_office":"Bürostuhl","furn.sym_coffee_table":"Rechteckiger Couchtisch","furn.sym_coffee_table_round":"Runder Couchtisch","furn.sym_coffee_table_oval":"Ovaler Couchtisch","furn.sym_coffee_table_rounded":"Abgerundeter Couchtisch","furn.sym_table_dining":"Rechteckiger Esstisch","furn.sym_table_round":"Rundtisch","furn.sym_table_dining_oval":"Ovaler Esstisch","furn.sym_table_dining_rounded":"Abgerundeter Esstisch","furn.sym_chair":"Stuhl","furn.sym_chair_bar":"Barhocker","furn.sym_desk":"Rechteckiger Schreibtisch","furn.sym_desk_corner":"Eckschreibtisch","furn.sym_bed_double":"Doppelbett","furn.sym_bed_single":"Einzelbett","furn.sym_nightstand":"Nachttisch","furn.sym_cabinet_tv":"TV-Schrank","furn.sym_cabinet_shoe":"Schuhschrank","furn.sym_cabinet_sink":"Waschtischunterschrank","furn.sym_wardrobe":"Kleiderschrank","furn.sym_bookshelf":"Bücherschrank","furn.sym_wall_unit":"Schrankwand","furn.sym_kitchen_floor":"Küchen-Unterschrank","furn.sym_kitchen_floor_corner":"Küchen-Eckunterschrank","furn.sym_kitchen_wall":"Küchen-Hängeschrank","furn.sym_kitchen_wall_corner":"Küchen-Eckhängeschrank","furn.sym_shelf_floor":"Standregal","furn.sym_shelf_wall":"Wandregal","furn.sym_fridge":"Kühlschrank","furn.sym_stove":"Vierflammiges Kochfeld","furn.sym_cooktop_two":"Zweiflammiges Kochfeld","furn.sym_dishwasher":"Geschirrspüler","furn.sym_washer":"Waschmaschine","furn.sym_dryer":"Wäschetrockner","furn.sym_tv":"Fernseher mit Standfuß","furn.sym_tv_wall":"Wandfernseher","furn.sym_ac":"Klimaanlage","furn.sym_water_heater":"Wassererhitzer","furn.sym_toilet":"Stand-WC","furn.sym_toilet_built_in":"Wand-WC","furn.sym_bathtub":"Rechteckige Badewanne","furn.sym_bathtub_corner":"Eckbadewanne","furn.sym_shower":"Dusche","furn.sym_sink":"Waschbecken","furn.sym_kitchen_sink":"Einzel-Küchenspüle","furn.sym_kitchen_sink_double":"Doppel-Küchenspüle","furn.sym_bidet":"Stand-Bidet","furn.sym_bidet_built_in":"Wand-Bidet","furn.sym_stairs":"Treppe","furn.sym_fireplace":"Kamin","furn.sym_plant":"Pflanze","furn.sym_rug":"Teppich","common.yes":"Ja","common.no":"Nein","vac.diag_source":"Quelle","vac.diag_platform":"Integration","vac.diag_status":"Status","vac.diag_position":"Position","vac.diag_rooms":"Räume","vac.diag_rooms_value":"{total} · {matched} Namen stimmen überein · {readiness}","vac.autocal_ready":"Auto-Kalibrierung verfügbar","vac.autocal_not_ready":"3 übereinstimmende Namen erforderlich","vac.diag_path":"Integrationspfad","vac.diag_map":"Karten-ID","vac.source_none":"nicht ausgewählt","vac.source_status_ok":"Bereit","vac.source_status_missing":"Fehlend","vac.source_status_disabled":"In Home Assistant deaktiviert","vac.source_status_unavailable":"Nicht verfügbar","vac.source_status_unverified":"Kann mit den aktuellen Berechtigungen nicht verifizieren","vac.source_status_unsupported":"Keine Positionsdaten","vac.source_status_none":"Keine Quelle","vac.source_banner_missing":"Die gespeicherte Quelle existiert nicht mehr. Sie wurde nicht automatisch ersetzt; wählen Sie eine andere Quelle oder stellen Sie sie in Home Assistant wieder her.","vac.source_banner_disabled":"Die gespeicherte Quelle ist in Home Assistant deaktiviert. Aktivieren Sie sie dort oder wählen Sie eine andere Quelle.","vac.source_banner_unverified":"Die aktuellen Home Assistant-Berechtigungen können diese gespeicherte Quelle nicht verifizieren. Sie bleibt angepinnt und wird nicht automatisch ersetzt.","vac.choose_source":"Quelle wählen","vac.source_auto":"Automatisch","vac.source_auto_hint":"Verwenden Sie eine kompatible Entität von diesem Gerät","vac.all_cameras":"Alle Kameras","vac.all_cameras_warn":"Eine Kamera liefert möglicherweise keine Roboter-Daten. Wählen Sie eine nur, wenn es Ihre Saugroboter-Karte ist.","vac.all_cameras_empty":"Keine anderen Kamera-Entitäten gefunden.","vac.platform_unknown":"unbekannte Integration","vac.cap_position":"Position","vac.cap_rooms_short":"Räume","vac.cap_path":"Pfad","vac.cap_map":"Karten-ID","vac.cap_none":"Keine Roboter-Daten erkannt","vac.xcme_hint":"Aktivieren Sie diese Xiaomi Cloud Map Extractor-Attribute:","vac.documentation":"Dokumentation","vac.residual_title":"Automatische Kalibrierung überprüfen","vac.residual_message":"Die zugeordneten Räume weichen um bis zu {error} voneinander ab. Diese ungefähre Kalibrierung anwenden, manuell verfeinern oder ohne Änderung der gespeicherten Einrichtung abbrechen.","vac.apply_proposal":"Anwenden","gs.backup_group":"Sichern und übertragen","gs.backup_hint":"Laden Sie ein tragbares JSON-Backup herunter oder sehen Sie eine Vorschau und importieren Sie ein Backup, das mit House Plan erstellt wurde.","backup.export_open":"Exportieren","backup.import_open":"Importieren","backup.export_title":"House Plan exportieren","backup.import_title":"House Plan importieren","backup.export_hint":"Wählen Sie, ob das Backup die gesamte House Plan-Konfiguration oder nur den aktuellen Bereich enthalten soll.","backup.full":"Vollständiges Backup","backup.current_space":"Aktueller Bereich","backup.current_space_title":"Aktueller Bereich: {title}","backup.no_current_space":"Kein aktueller Bereich","backup.plan_only":"Nur Plan","backup.plan_only_hint":"Räume, Wände, Öffnungen, Dekor und Raumbezeichnungspositionen ohne Geräte oder Home Assistant-Bindungen beibehalten.","backup.plan_only_preview":"Diese Datei enthält nur den Plan.","backup.privacy_warning":"Das Archiv enthält Namen, Home Assistant-Kennungen und genaue Koordinaten. Interne Pläne und Anhänge werden referenziert, nicht eingebettet; Laufzeitzustände und Staubsaugerspuren sind nicht enthalten.","backup.download":"JSON herunterladen","backup.export_done":"Backup heruntergeladen","backup.reading":"Überprüfe das Backup…","backup.revalidated":"Der Plan wurde nach dieser Vorschau geändert. Die Zusammenfassung wurde aktualisiert; überprüfen Sie sie und bestätigen Sie erneut.","backup.error.unauthorized":"Sie haben keine Berechtigung, diesen Plan zu exportieren oder zu importieren.","backup.error.not_ready":"Der Hausplan ist noch nicht fertig. Versuchen Sie es in einem Moment erneut.","backup.error.too_large":"Das Backup überschreitet das 8-MiB-Limit.","backup.error.invalid_json":"Die ausgewählte Datei ist kein gültiges JSON.","backup.error.invalid_format":"Die ausgewählte Datei ist kein House Plan-Backup.","backup.error.unsupported_export_version":"Dieses Backup-Format wird von der installierten Version nicht unterstützt.","backup.error.future_model":"Das Backup wurde mit einem neueren House-Plan-Datenmodell erstellt.","backup.error.invalid_config":"Das Backup enthält eine ungültige House-Plan-Konfiguration.","backup.error.wall_model_migration_blocked":"Das Backup-Wandmodell konnte nicht sicher aktualisiert werden. Optimieren Sie zuerst den Quellplan und exportieren Sie ihn dann erneut.","backup.error.invalid_layout":"Das Backup enthält ungültige Objektpositionen.","backup.error.invalid_content":"Das Backup enthält ungültige oder inkonsistente Inhaltsreferenzen.","backup.error.space_not_found":"Der ausgewählte Bereich existiert nicht mehr.","backup.error.capacity_exceeded":"Durch das Hinzufügen dieses Backups würden die Planlimits überschritten.","backup.error.preview_expired":"Die Vorschau ist abgelaufen. Wählen Sie die Backup-Datei erneut aus.","backup.error.preview_owner_mismatch":"Diese Vorschau gehört zu einem anderen Home-Assistant-Benutzer.","backup.error.conflict":"Der Plan hat sich nach der Vorschau geändert. Überprüfen Sie die aktualisierte Zusammenfassung.","backup.error.content_confirmation_required":"Bestätigen Sie, dass nicht verfügbare lokale Inhalte getrennt werden können.","backup.error.commit_failed":"Das Backup konnte nicht sicher angewendet werden. Der vorherige Plan wurde wiederhergestellt oder wartet auf die Wiederherstellung.","backup.error.missing_plan":"Eine referenzierte Plan-Datei ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.missing_content":"Ein referenziertes lokales Anhangselement ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.marker_control_missing":"Ein verknüpftes Planlicht fehlt im Backup.","backup.error.marker_control_not_light":"Ein verknüpftes Planziel ist nicht mehr als Lichtquelle markiert.","backup.error.marker_control_self":"Eine Lichtquelle kann sich nicht selbst steuern.","backup.error.marker_control_cycle":"Das Backup enthält eine kreisförmige Kette von Lichtsteuerungen.","backup.error.duplicate_marker_control":"Das Backup enthält eine doppelte Plan-Licht-Verknüpfung.","backup.error.no_backup":"Es gibt keinen Import- oder Optimierungssnapshot zur Wiederherstellung.","backup.same_source":"Erstellt auf dieser Home Assistant-Instanz","backup.foreign_source":"Erstellt auf einer anderen Home Assistant-Instanz","backup.created":"Erstellt: {value}","backup.versions":"Karte {card}; Integration {integration}; Datenmodell {model}","backup.count_spaces":"Bereiche: {n}","backup.count_rooms":"Räume: {n}","backup.count_walls":"Wände: {n}","backup.count_openings":"Öffnungen: {n}","backup.count_decor":"Dekorationsobjekte: {n}","backup.count_markers":"Geräte: {n}","backup.count_layout":"Positionen: {n}","backup.bindings":"Verknüpfungen — Geräte: {device}, Entitäten: {entity}, virtuell: {virtual}; Positionen ohne Bereich: {legacy}","backup.binding_status":"Zielstatus — aktiv: {active}, deaktiviert: {disabled}, fehlt: {missing}","backup.missing_areas":"Auf dem Ziel fehlende Bereiche: {areas}","backup.dropped_marker_links":"Planlicht-Verknüpfungen außerhalb dieses übertragenen Bereichs wurden ausgelassen: {n}.","backup.repaired_target_refs":"Durch den Import dieses Bereichs wiederhergestellte vorhandene Referenzen: {n}.","backup.preserved_unresolved_refs":"Referenzen, die nicht eindeutig wiederhergestellt werden konnten, wurden beibehalten: {n}.","backup.preserved_unresolved_hint":"Es wurden keine Daten geraten oder gelöscht. Führen Sie nach dem Import Optimierungspläne aus, um die verbleibenden Referenzen zu überprüfen.","backup.import_details":"Importieren von Referenzdetails","backup.import_detail.incoming_remapped":"Referenzen innerhalb der importierten Kopie aktualisiert: {n}","backup.import_detail.target_repaired":"Vorhandene Referenzen wiederhergestellt: {n}","backup.import_detail.preserved_unresolved":"Unaufgelöste Referenzen beibehalten: {n}","backup.import_detail.collisions":"Zielkonflikte sicher beibehalten: {n}","backup.import_detail.dropped_links":"Eingehende Links durch Übertragungsregeln ausgelassen: {n}","backup.import_detail.bounded_lineages":"Übermäßig verschachtelte Kennungen begrenzt belassen: {n}","backup.replace_warning":"Dies ersetzt die aktuelle Konfiguration und das Layout. Hochgeladene Dateien werden niemals gelöscht. Eine Rückgängig-Möglichkeit bleibt bis zur nächsten Planbearbeitung verfügbar.","backup.foreign_bookkeeping":"Instanzspezifische bekannte/neue Gerätebuchführung wird nicht importiert.","backup.final_name":"Name des neuen Bereichs","backup.target_settings":"Die globalen Einstellungen der Zielinstanz bleiben unverändert.","backup.duplicates":"Bindungen bereits vorhanden: {n}","backup.skip":"Doppelte Geräte überspringen","backup.virtual_copy":"Sichere statische virtuelle Kopien hinzufügen","backup.content":"Referenzierter Inhalt","backup.content_available":"lokal verfügbar","backup.content_external":"externer Link","backup.content_detach_required":"wird getrennt","backup.confirm_detach":"Ich verstehe, dass nicht verfügbare interne Pläne und Anhänge von der importierten Konfiguration getrennt werden.","backup.replace":"Ersetzen","backup.add":"Bereich hinzufügen","backup.space_done":"Bereich importiert: {rooms} Räume, {markers} Geräte, {refs} bestehende Referenzen wiederhergestellt","backup.full_done":"Backup wiederhergestellt: {spaces} Bereiche, {rooms} Räume, {markers} Geräte","backup.undo_import":"Letzten vollständigen Import rückgängig machen","backup.import_undone":"Vollständiger Import rückgängig gemacht","backdrop.large_title":"Großes Bild","backdrop.large_body":"Dieses Bild ist {w}×{h} ({fileMb} MB Datei) und benötigt etwa {decodedMb} MB Speicher zur Anzeige. Auf Tablets kann das die Seite zum Absturz bringen.","backdrop.unknown_body":"Die Bildabmessungen konnten nicht gelesen werden — die Datei ist möglicherweise beschädigt. Fortfahren kann die Seite auf Tablets zum Absturz bringen.","backdrop.use_downscaled":"Verkleinerte Kopie hochladen","backdrop.reducing":"Wird verkleinert…","backdrop.keep_original":"Original behalten","backdrop.too_large_title":"Bild ist zu groß","backdrop.too_large_body":"Dieses Bild überschreitet die Anzeigegrenzen des Browsers ({w}×{h}, Limit {limit} px pro Seite). Bitte verkleinern Sie es in einem Desktop-Editor und laden Sie es erneut hoch.","backdrop.downscale_failed":"Die verkleinerte Kopie konnte nicht erstellt werden. Der aktuelle Plan wurde nicht geändert."};const n="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";export{e as dictionary,n as fingerprint}; diff --git a/dist/houseplan-assets/de-FazIOsg8.js b/dist/houseplan-assets/de-FazIOsg8.js deleted file mode 100644 index f9b3bdc0..00000000 --- a/dist/houseplan-assets/de-FazIOsg8.js +++ /dev/null @@ -1 +0,0 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";var e={"editor.context_actions":"Aktionen: {object}","editor.tool_options":"Werkzeugoptionen: {tool}","editor.palette":"Palette: {tool}","editor.open_group":"Werkzeuggruppe: {group}","editor.group_active":"{group} — aktiv: {item}","editor.disabled_action":"{action} ist nicht verfügbar: {reason}","editor.loading":"Editor wird geladen…","editor.loading_aria":"Der Plan-Editor wird geladen","editor.load_failed":"Der Editor konnte nicht geladen werden.","editor.refresh_advice":"Seite aktualisieren und erneut versuchen.","editor.retry_advice":"Verbindung prüfen und erneut drücken.","color_picker.title":"Farbwähler","color_picker.hue":"Farbton","color_picker.saturation":"Sättigung","color_picker.value":"Helligkeit","color_picker.hex":"Hex-Farbe","color_picker.invalid_hex":"Geben Sie eine 3- oder 6-stellige Hex-Farbe ein","btn.properties":"Eigenschaften","btn.keep_as_walls":"Als Wände behalten","btn.delete_room_keep_walls":"Raum löschen, Wände behalten","btn.delete_room_with_walls":"Raum und Wände löschen","title.markup_select":"Wände, Säulen und gespeicherte Umrisse auswählen und bearbeiten","title.markup_column":"Säule: Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","markup.partition":"Trennwand","markup.column":"Säule","markup.hint_column":"Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","history.draft_segment":"Raum-Entwurfsegment hinzufügen","history.draft_merge":"Unvollständige Raumumrisse verbinden","history.draft_segment_delete":"Raum-Entwurfssegment entfernen","history.partition_add":"Trennwand hinzufügen","history.column_add":"Säule hinzufügen","history.physical_edit":"Physisches Objekt ändern","history.physical_delete":"Physisches Objekt löschen","history.physical_move":"Physisches Objekt verschieben","history.contour_to_partitions":"Umriss in geschlossene Wände umwandeln","history.wall_chain_finish":"Wandkette fertigstellen","history.wall_face_batch":"Räume aus Wänden erstellen","toast.column_duplicate":"Eine Säule mit demselben Zentrum und äußerer Größe existiert bereits","confirm.delete_draft":"Sofort den gesamten unvollständigen Raumumriss löschen?","confirm.delete_draft_segment":"Dieses Entwurfssegment löschen? Der verbleibende Umriss könnte sich in zwei teilen.","physical.partition_properties":"Eigenschaften der Trennwand","physical.column_properties":"Eigenschaften der Säule","physical.draft_properties":"Eigenschaften des Entwurfssegments","physical.shape":"Form","physical.square":"Quadrat","physical.circle":"Kreis","physical.diameter":"Durchmesser","physical.side":"Seite","physical.rotation":"Drehwinkel","physical.length":"Länge","physical.allowed_range":"Erlaubt: {min}–{max} {unit}","physical.delete_segment":"Segment löschen","physical.delete_draft":"Gesamten Umriss löschen","physical.column_size_title":"Außenmaß der per Klick platzierten quadratischen Säule (1–150 cm).","card.title":"Hausplan","count.devices":"{n} Entwicklung","empty.no_spaces":"Noch keine Bereiche.","empty.add_first":"Ersten Bereich hinzufügen und einen Grundriss hochladen.","empty.install":"Installieren Sie die House Plan-Integration und fügen Sie sie unter „Geräte & Dienste“ hinzu.","btn.add_space":"Bereich hinzufügen","btn.cancel":"Abbrechen","btn.save":"Speichern","btn.close":"Schließen","btn.delete":"Löschen","btn.edit":"Bearbeiten","btn.open_in_ha":"In HA öffnen","btn.reset":"Zurücksetzen","btn.attach":"Anhängen…","btn.upload":"Hochladen…","btn.replace":"Ersetzen…","title.zoom_in":"Zoomen","title.zoom_out":"Herauszoomen","title.zoom_fit":"Alles anpassen","title.markup":"Raum-Markierung: Gitter, Linien, Konturen","title.configure_space":"Bereich konfigurieren","title.add_space":"Bereich hinzufügen","title.markup_add":"Wände: eine durchgehende Kette zeichnen; Shift sperrt auf 45°. Werkzeugwechsel oder Verlassen des Editors beendet die Kette und wandelt sie in unabhängige Wände","title.markup_merge":"Zusammenführen: Klicken Sie auf einen Raum und dann auf den Nachbarraum, mit dem er eine Wand teilt","title.markup_split":"Raum teilen: Klicken Sie auf den Raum und dann auf zwei Punkte an seinen Wänden","title.markup_delroom":"Raum löschen: Klicken Sie in einen Raum und bestätigen Sie","title.need_plan":"Einen Grundriss hochladen","markup.add":"Wände","markup.merge":"Zusammenführen","markup.split":"Teilen","markup.resize":"Größe ändern","title.markup_resize":"Räume vergrößern/verkleinern: Ziehen Sie einen verfügbaren horizontalen oder vertikalen Wandgriff","markup.hint_resize":"Ziehen Sie einen verfügbaren Wandgriff · die Wand stoppt an der ersten unsicheren Position · Esc bricht ab · Strg+Z — rückgängig","resize.disabled.diagonal":"Diese Wand ist geneigt; Größe ändern unterstützt nur horizontale und vertikale Wände","resize.disabled.side-angle":"Eine angrenzende Wand ist geneigt; Größe ändern erfordert, dass beide angrenzenden Wände diese Wand im rechten Winkel treffen","resize.disabled.duplicate-physical-wall":"Eine separate Trennwand, unfertige Kontur oder ein Pfeiler überlappt diese Wand; entfernen oder verschieben Sie sie, bevor Sie die Größe ändern","resize.disabled.partial-shared":"Ein angrenzender Raum nutzt nur einen Teil dieser Wand, daher kann sie nicht sicher als gemeinsame Wand verschoben werden","resize.disabled.unequal-shared":"Der angrenzende Raum nutzt andere Endpunkte oder Wandlängen, daher können die Wände nicht gemeinsam sicher bewegt werden","resize.disabled.multiple-rooms":"Das Verschieben dieser Wand würde mehr als zwei Räume betreffen","resize.disabled.thickness-conflict":"Die Wandstärke kann nicht sicher beibehalten werden","resize.disabled.opening-conflict":"Eine Öffnung an dieser Wand kann nicht sicher verschoben werden","resize.disabled.invalid-geometry":"Diese Wand kann nicht ohne Änderung der Planstruktur geändert werden","resize.limit_stopped":"Wand gestoppt: der nächste Schritt würde ein Anschlusslimit verletzen","resize.commit_failed":"Größenänderung wurde abgebrochen, da der endgültige Plan die Geometrieprüfung nicht bestanden hat","resize.preview_failed":"Größenänderung am letzten sicheren Punkt gestoppt, da die Plan-Geometrie nicht beibehalten werden konnte","markup.opening":"Öffnung","markup.delete_room":"Raum löschen","history.undo":"Rückgängig","history.redo":"Wiederholen","history.undo_named":"Rückgängig: {name} (Strg+Z)","history.redo_named":"Wiederholen: {name} (Strg+Shift+Z / Strg+Y)","history.undo_empty":"Nichts zu widerrufen","history.redo_empty":"Nichts zu wiederholen","history.undone":"Rückgängig gemacht: {name}","history.redone":"Wiederholt: {name}","history.add_room":"Raum erstellen","history.split_room":"Raum teilen","history.merge_rooms":"Räume zusammenführen","history.resize_room":"Raumgröße ändern","history.wall_thickness":"Wandstärke ändern","history.add_opening":"Tür- oder Fensteröffnung hinzufügen","history.edit_opening":"Öffnung bearbeiten","history.move_opening":"Öffnung verschieben","history.delete_opening":"Öffnung löschen","history.delete_room":"Raum löschen","history.delete_room_keep_walls":"Raum löschen, Wände behalten","history.delete_room_with_walls":"Raum und Wände löschen","history.decor_add":"Dekorobjekt hinzufügen","history.decor_edit":"Dekorobjekt bearbeiten","history.decor_move":"Dekorobjekt verschieben","history.decor_transform":"Dekorobjekt transformieren","history.decor_delete":"Dekorobjekt löschen","history.backdrop_transform":"Plan-Hintergrund transformieren","opening.new":"Neue Öffnung","opening.edit":"Öffnung","opening.door":"Tür","opening.window":"Fenster","opening.gate":"Tor","opening.passage":"Offener Durchgang","opening.passage_binding_warning":"Das Speichern entfernt den Öffnungs-/Schließsensor und das Schloss.","opening.invalid_passage_fields":"Der offene Durchgang auf dem Plan „{room}“ hat nicht unterstützte Einstellungen: {fields}.","opening.type_label":"Typ","opening.length_label":"Länge, cm","opening.contact_label":"Öffnungs-/Schließsensor","opening.lock_label":"Schloss","opening.none":"— keine —","opening.search_ph":"Suche: Name oder Entitäts-ID…","opening.invert":"Geöffnet/geschlossen invertieren","opening.flip_h":"Scharnier an der anderen Zarge","opening.flip_v":"Öffnet zur anderen Seite","opening.open":"Offen","opening.closed":"Geschlossen","opening.locked":"Verriegelt","opening.unlocked":"Entriegelt","opening.state_unknown":"nicht verfügbar","opening.no_entities":"Keine Sensoren gebunden — ein statisches Symbol auf dem Plan.","toast.opening_no_wall":"Neben eine Raumwand oder eine unabhängige Wand klicken","opening.host_partition":"Unabhängige Wand","opening.partition_orphan":"Die unabhängige Wand für diese Öffnung existiert nicht mehr","opening.partition_jamb_margin":"Mindestens {distance} zwischen der Öffnung und dem Wandende lassen","opening.rebind_partition":"An eine andere unabhängige Wand anfügen","confirm.delete_partition_openings_title":"Wand und Öffnungen löschen?","confirm.delete_partition_openings_body":"Diese Wand enthält {count} Öffnung(en). Sie werden zusammen gelöscht.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Löschen","markup.hint_points":"Punkte: {n} · Shift — 45° Schritte · Esc — Kette beenden · Ctrl+Z — Punkt rückgängig machen · Schließen eines Bereichs erstellt einen Raum","markup.hint_start":"Klicken Sie auf einen Rasterpunkt, um eine Wandkette zu starten","tip.lqi":"durchschnittliches Zigbee-Signal:","tip.area":"Bereich: {value}","info.device_header":"Gerät im Plan","info.model":"Modell","info.state":"Status","info.link":"Verbindung","info.manuals":"Handbücher","info.none":"Keine zusätzlichen Informationen","marker.new_device":"Neues Gerät","marker.name_label":"Name (im Plan angezeigt)","marker.name_ph":"Name","marker.binding_label":"An ein HA-Gerät binden","marker.binding_disabled":"in Home Assistant deaktiviert","marker.virtual_option":"Virtuelles Gerät (keine Bindung)","marker.search_ph":"Gerät/Gruppe suchen…","marker.nothing_found":"Nichts gefunden","marker.room_label":"Raum","marker.room_override":" (Platzierung überschreiben)","marker.room_choose":"— Raum auswählen —","marker.room_auto":"— nach Gerätebereich (auto) —","marker.icon_label":"Symbol","marker.icon_ph":"mdi:… (leer = automatisch)","marker.display_label":"Anzeige","display.badge":"Symbol + Zustand","display.icon_ripple":"Symbol + Zustand und Aktivität","display.static_icon":"Immer statisches Symbol","marker.display_hint":"Symbol + Zustand ändert die Platte für die Zustände Arbeit, geöffnet, Alarm und nicht verfügbar. Symbol + Zustand und Aktivität zeigt zusätzlich einen kurzen Impuls für Ereignisse und einen kontinuierlichen Impuls für laufende Arbeit, Bewegung oder Präsenz. Wert + Zustand ersetzt das Symbol durch einen eindeutigen HA-Wert. Ein statisches Symbol reagiert niemals auf Gerätezustände.","marker.display_hint_badge":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon_ripple":"Das Symbol, die dynamische Platte und der Puls zeigen einen kurzen Puls für Ereignisse und einen kontinuierlichen Puls für Arbeit, Bewegung oder Anwesenheit. Rote Alarme haben eine separate Priorität.","marker.display_hint_value":"Ein eindeutiger Home Assistant-Wert ersetzt das Symbol, während die Platte weiterhin den Zustand anzeigt. Rote Alarme bleiben sichtbar.","marker.display_hint_static_icon":"Die themenabhängige Hülle und das Symbol bleiben immer gleich. Zustand, Aktivität, Nichtverfügbarkeit und Alarme verändern nicht das Erscheinungsbild.","marker.static_alarm_warning":"Dieses Gerät kann Alarme melden. Die statische Anzeige verbirgt die visuelle Alarmanzeige des Markers.","marker.preview.title":"Anzeigevorschau","marker.preview.actual":"Jetzt","marker.preview.example":"Beispiel","marker.preview.integration":"Bereitgestellt von","marker.preview.source":"Anzeigquelle","marker.preview.current_state":"Aktueller Zustand","marker.preview.result":"Auf dem Plan","marker.preview.details":"Quelldetails","marker.preview.select_source":"Ein Home-Assistant-Gerät oder eine Entität auswählen, um die tatsächliche Anzeige zu sehen.","marker.preview.unknown_provider":"Unbekannte Integration","marker.preview.virtual_provider":"House Plan · virtuelles Gerät","marker.preview.no_source":"Keine aktive Quelle","marker.preview.no_state":"Kein aktueller Zustand","marker.preview.mixed_states":"Mehrere verschiedene Zustände","marker.preview.multiple_sources":"{n}-Quellen","marker.preview.more_sources":"+{n} mehr","marker.preview.scaled":"Die Vorschau wird auf {n}% skaliert, um zu passen. Die gespeicherte Größe bleibt unverändert.","marker.preview.demo_short":"Kurze Aktivität anzeigen","marker.preview.demo_continuous":"Kontinuierliche Aktivität anzeigen","marker.preview.stop_continuous":"Kontinuierliche Aktivität stoppen","marker.preview.demo_short_notice":"Kurzes Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_continuous_notice":"Kontinuierliches Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_already_visible":"Der aktuelle Zustand zeigt bereits eine echte Aktivitäts- oder Alarmeffekt.","marker.preview.reduced_motion":"Systemweit reduzierte Bewegungen sind aktiviert, daher wird gewöhnliche Aktivität als Punkt angezeigt.","marker.preview.reason.neutral":"Neutrale dunkle Platte","marker.preview.reason.working":"Gelbe Platte: Das Gerät arbeitet jetzt","marker.preview.reason.working_activity":"Gelbe Platte und Aktivitätseffekt: das Gerät funktioniert jetzt","marker.preview.reason.open":"Orange Platte: physisch geöffnet oder entriegelt","marker.preview.reason.cover_icon_state":"Neutrale Platte; die Abdeckungseinheit steuert den angezeigten Zustand und das Symbol","marker.preview.reason.presence":"Aktivitätseffekt, während Präsenz erkannt wird","marker.preview.reason.event":"Kurzer Aktivitätseffekt nach einem erkannten Ereignis","marker.preview.reason.transition":"Aktivitätseffekt, während das Gerät sich bewegt oder den Zustand ändert","marker.preview.reason.media_neutral":"Mediengeräte bleiben im verfügbaren Zustand auf einer neutralen dunklen Platte","marker.preview.reason.unavailable":"Gedämpfte neutrale Platte: nicht verfügbar oder ausgeschaltet","marker.preview.reason.alarm":"Rote Alarmplatte; Alarmanzeige wird immer angezeigt","marker.preview.reason.live_states_disabled":"Live-Zustandsstil ist für diese Karte deaktiviert","marker.preview.reason.value_no_state":"Kein nutzbarer Zustandswert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_ambiguous_sources":"Mehrere mögliche Wertequellen; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_non_scalar":"Die Quelle lieferte keinen einfachen Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_virtual":"Ein virtuelles Gerät hat keinen Home Assistant-Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.vacuum_live_plan_only":"Echtzeitposition und -spur des Staubsaugers sind nur im vollständigen Plan verfügbar","marker.preview.reason.hidden_design_preview":"Das Gerät ist im Plan ausgeblendet; diese Designvorschau bleibt sichtbar","marker.preview.reason.composite_power_source":"Der Status stammt vom Power-Entity des Geräts; Hilfsschalter werden ignoriert","marker.preview.reason.activity_display_disabled":"Der gewählte Anzeigemodus zeigt gewöhnliche Aktivitätseffekte nicht an","marker.preview.reason.ha_disabled":"Die Home Assistant-Verbindung ist deaktiviert und wird im Plan ausgeblendet","marker.preview.reason.orphaned":"Die gespeicherte Home Assistant-Verbindung konnte nicht mehr gefunden werden","marker.preview.reason.static_icon":"Statischer Modus: Der Gerätestatus ändert das Symbol nicht","marker.activity_color":"Aktivitätspulsfarbe","marker.ripple_size":"Aktivitätspulsgröße","marker.activity_alarm_note":"Farbe und Größe betreffen nur gewöhnliche Aktivität und ändern keine roten Alarme.","marker.pulse_a11y_alarm":"Alarm","marker.pulse_a11y_event":"Kürzliches Ereignis","marker.pulse_a11y_presence":"Anwesenheit erkannt","marker.pulse_a11y_transition":"Statusänderung","marker.pulse_a11y_running":"Arbeitet","marker.state_a11y_neutral":"Standardzustand","marker.state_a11y_working":"Funktionierend","marker.state_a11y_open":"Offen","marker.state_a11y_locked":"Verschlossen","marker.state_a11y_unlocked":"Entriegelt","marker.state_a11y_alarm":"Alarm","marker.state_a11y_unavailable":"Nicht verfügbar","marker.lqi_a11y_low":"LQI {value}, schwaches Signal","marker.lqi_a11y_mid":"LQI {value}, mittleres Signal","marker.lqi_a11y_high":"LQI {value}, starkes Signal","marker.size_label":"Symbolgröße / Drehung","marker.angle_label":"Drehen","marker.model_label":"Modell","marker.model_ph":"z.B. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Beschreibung","marker.desc_ph":"Notizen, Spezifikationen…","marker.manuals_label":"Handbücher (PDF usw.)","marker.sub_device":"Gerät","marker.sub_z2m_group":"· Z2M Gruppe","marker.sub_group":"Gruppe","marker.sub_helper":"Helfer","space.new":"Neuer Bereich","space.header":"Bereich","space.title_label":"Titel","space.title_ph":"z.B. Garage","space.plan_label":"Grundriss (Hintergrund)","space.no_plan":"Kein Planbild","space.plan_alt":"Grundriss","room.new":"Neuer Raum","room.name_label":"Anzeigename","room.name_ph":"z.B. Terrasse","room.area_label":"Home Assistant Bereich (nicht zugewiesen)","room.no_area_option":"— kein Bereich —","room.default_name":"Raum","device.unnamed":"unbenannt","device.light_group":"Lichtgruppe","device.fallback":"Gerät","device.virtual":"Virtuelles Gerät","confirm.delete_room":'Raum "{name}" löschen?',"confirm.delete_room_title":'Raum "{name}" löschen?',"confirm.delete_room_body":"Festlegen, ob die ausschließlich zu diesem Raum gehörenden physischen Wände erhalten bleiben. Mit einem anderen Raum geteilte Wände bleiben immer erhalten.","confirm.remove_marker":"„{name}“ aus dem Plan löschen? Das Gerät verschwindet vollständig und trägt nicht mehr zu den Plandaten bei. Es kann später erneut hinzugefügt werden.","confirm.erase_decor":"Das Objekt „{kind}“ löschen? Diese Aktion kann im Editor rückgängig gemacht werden.","confirm.delete_space":"Bereich „{title}“ mit allen Räumen und Markierungen löschen?","space.delete_blocked":"Dieser Bereich wird noch von {n} Gerät(en) verwendet. Die Geräte zuerst in einen anderen Bereich verschieben oder löschen.","toast.pos_save_failed":"Position nicht gespeichert: {err}","toast.no_entity":"Das Gerät hat keine geeignete Entität","toast.ha_disabled_action":"Ein deaktiviertes Home Assistant-Objekt kann im Plan nicht verwendet werden.","toast.ha_disabled_show_device":"Ein in Home Assistant deaktiviertes Gerät kann im Plan nicht angezeigt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_disabled_show_entity":"Eine in Home Assistant deaktivierte Entität kann im Plan nicht angezeigt werden. Aktivieren Sie sie zuerst in Home Assistant.","toast.ha_disabled_add":"Ein deaktiviertes Home Assistant-Objekt kann nicht dem Plan hinzugefügt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_binding_unverified":"Der Status des Objekts konnte über das Home Assistant-Register nicht überprüft werden. Anzeige und Aktionen sind vorübergehend nicht verfügbar.","toast.markup_needs_server":"Markup ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde.","toast.space_order_changed":"Reihenfolge geändert. Wenn eine Karte ihr Stockwerk nach Nummer festlegt, überprüfen Sie diese Paneele.","toast.conflict":"Die Konfiguration wurde in einem anderen Fenster geändert — Daten aktualisiert, wiederholen Sie Ihre letzte Aktion.","toast.cfg_save_failed":"Speichern der Konfiguration fehlgeschlagen: {err}","toast.room_overlap":"Die Umrisse überlappen den Raum „{name}“ — Räume dürfen sich nicht überlappen","toast.contour_cannot_close":"Die Kontur kann nicht geschlossen werden, da sie entartet ist oder sich selbst schneidet","toast.merge_not_adjacent":"Es können nur Räume zusammengeführt werden, die eine Wand teilen","toast.rooms_merged":"Räume zu „{name}“ zusammengeführt","toast.split_pick_wall":"Beginnen Sie den Schnitt an der Wand des Raumes","toast.split_bad_cut":"Der Schnitt muss wand-zu-wand innerhalb des Raumes verlaufen, ohne Wände oder sich selbst zu kreuzen","merge.header":"Räume zusammenführen","merge.hint":"Der zusammengeführte Raum behält einen Namen und eine Fläche. Die andere Fläche wird freigegeben — ihre Geräte verlassen den Plan, bis ein anderer Raum sie beansprucht.","merge.keep":"Behalten","merge.no_area":"keine Fläche","toast.room_saved":"Raum gespeichert ({n}). Hinzugefügte Geräte: {added}. Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.room_saved_no_area":"Raum gespeichert ({n}, keine Fläche). Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.marker_needs_server":"Die Gerätebearbeitung ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde","toast.virtual_name_required":"Einen Namen für das virtuelle Gerät eingeben","toast.marker_saved":"Gerät gespeichert","toast.marker_removed":"Gerät aus dem Plan gelöscht","toast.integration_missing":"Die House Plan-Integration ist nicht installiert — Verwaltung nicht verfügbar","toast.plan_formats":"Unterstützte Formate: SVG, PNG, JPG, WebP","toast.plan_required":"Grundriss hochladen — erforderlich","toast.space_added_onboard":"Bereich hinzugefügt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","toast.space_added":"Bereich hinzugefügt","toast.space_saved":"Bereich gespeichert","toast.space_deleted":"Bereich gelöscht","toast.delete_failed":"Löschen fehlgeschlagen: {err}","toast.error":"Fehler: {err}","toast.toggle_target_unavailable":"Ziel „{name}“ ist nicht verfügbar — keine Aktion durchgeführt","toast.toggle_targets_unavailable":"Ziele nicht verfügbar: {names}. Keine Aktion durchgeführt","toast.file_failed":'Datei "{name}" wurde nicht hochgeladen: {err}',"toast.files_attached":"Dateien angehängt: {n}","err.unknown":"unbekannter Fehler","err.code":"Code {code}","err.too_large":"Datei größer als {mb} MB","err.bad_ext":"nicht unterstützter Typ (PDF/Bild erwartet)","err.unauthorized":"Administratorrechte erforderlich","editor.title":"Titel","editor.default_floor":"Anfangsbereich","editor.default_floor_missing":"Anfangsbereich „{id}“ existiert nicht mehr. Wählen Sie einen anderen Bereich.","editor.floor":"Fester Bereich","editor.floor_none":"— nicht fest —","editor.floor_index":"YAML-Index {index} (beibehalten)","fixed_floor.loading":"Lade den festen Bereich…","fixed_floor.invalid_title":"Der feste Bereich ist nicht verfügbar","fixed_floor.invalid_body":"Überprüfen Sie die Etage in der Kartenkonfiguration. Der konfigurierte Wert ist: {value}","editor.icon_size":"Symbolgröße, % der Planbreite","editor.show_temperature":"Temperatur anzeigen","editor.live_states":"Live-Zustände (ein/aus, offen…)","editor.light_pools":"Lichtkegel und Wandschatten","editor.show_signal":"Zigbee-Signal anzeigen (LQI)","editor.language":"Schnittstellensprache","editor.lang_auto":"Automatisch (HA-Profil)","title.icon_rules":"Symbolregeln: welche MDI-Symbole Geräte nach Namen erhalten","rules.title":"Symbolregeln","rules.hint":'Regeln werden von oben anhand von "Gerätename + Modell" (Großschreibungs-unempfindliches Regex) überprüft; das erste Match gewinnt. Wenn nichts übereinstimmt, entscheidet die Entität-Geräteklasse, dann das generische Chip-Symbol.',"rules.pattern_ph":"regex, z. B. Stecker|Steckdose","rules.icon_ph":"mdi:power-socket-de","rules.add":"Regel hinzufügen","rules.reset":"Zurücksetzen auf Standardeinstellungen","rules.test_ph":"Versuch mal einen Gerätenamen...","rules.invalid":"ungültiges regex","rules.saved":"Icon-Regeln gespeichert","btn.up":"Nach oben","btn.down":"Nach unten","tap.info":"Gerätekarte","tap.more_info":"HA Mehr-Info-Dialog","tap.toggle":"Zustand umschalten","marker.tap_label":"Tap-Aktion für dieses Gerät","tap.toggle_note":"Der aufgelöste Zustand ist unten dargestellt. Sichere Geräte werden niemals aus dem Plan umgeschaltet.","import.title":"Bereiche aus HA-Etagen erstellen","import.hint":"Home Assistant kennt diese Etagen bereits. Die Etagen auswählen, die zu Planbereichen werden sollen; anschließend für jeden Bereich einen Grundriss hochladen. Die Räume werden danach von Hand eingezeichnet.","import.start":"{n} Bereich(e) erstellen","import.manual":"Von Grund auf neu starten","import.progress":"Etage {i} von {n}","import.done":"Bereiche erstellt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","btn.skip":"Überspringen","space.scale_label":"Maßstab (Rasterzellen-Größe)","space.scale_unit":"cm pro Zelle","space.scale_unit_imperial":"Zoll pro Zelle","space.display_section":"Anzeige","space.show_borders":"Raumgrenzen immer anzeigen","space.zero_wall_style":"Wände ohne Dicke","space.zero_wall_dashed":"Gestrichelt","space.zero_wall_solid":"Durchgezogen","space.zero_wall_help":"Gestrichelte Wände lassen Licht durch. Durchgezogene Wände blockieren Licht auch bei einer Dicke von 0.","space.show_names":"Raumnamen anzeigen (zum Verschieben ziehen)","space.room_color":"Farbe der Begrenzung & des Namens","space.opacity":"Deckkraft","space.fill_label":"Raumausfüllung","fill.none":"Keine","fill.lqi":"Zigbee-Signal","fill.light":"Lichter","fill.custom":"Benutzerdefinierte Farbe","space.custom_fill":"Füllfarbe","space.source_file":"Ich habe einen Grundriss als Bild","space.source_draw":"Kein Bild – ich werde die Räume von Hand umreißen","fill.temp":"Temperatur","tip.temp_avg":"Durchschnittstemperatur:","tip.hum_avg":"Durchschnittliche Luftfeuchtigkeit:","space_card.button":"Grundriss öffnen","space_card.not_found":"Bereich „{id}“ nicht gefunden","space_card.loading":"Laden…","continuity.restore_plan":"Grundriss wiederherstellen…","continuity.restore_connection":"Geräteverbindung wiederherstellen…","continuity.retry":"Erneut versuchen","editor.space":"Bereich","editor.framing":"Bildausschnitt","editor.fit_content":"Alle sichtbaren Inhalte","editor.fit_house":"Eng an der Hausgeometrie","editor.show_button":"Schaltfläche anzeigen","editor.button_label":"Beschriftung der Schaltfläche","editor.button_target":"Ziel-Dashboard-Pfad","marker.sub_entity":"Entität","title.general_settings":"Allgemeine Einstellungen","gs.title":"Allgemeine Einstellungen","gs.hint":"Füllfarben gelten für jeden Bereich; jede Farbe hat ihre eigene Deckkraft. Der Füllmodus wird in den Einstellungen des jeweiligen Bereichs festgelegt.","gs.light_group":"Füllung: Licht","gs.light_on":"Lichter an","gs.light_off":"Alle Lichter aus","gs.temp_group":"Füllung: Temperatur","gs.temp_cold":"Kalt","gs.temp_ok":"Angenehm","gs.temp_hot":"Heiß","gs.lqi_group":"Füllung: Zigbee-Signal","gs.lqi_low":"Schwaches Signal","gs.lqi_high":"Starkes Signal","gs.reset":"Auf Standard zurücksetzen","gs.saved":"Allgemeine Einstellungen gespeichert","space.show_lqi":"Zigbee-Signal (LQI) neben Geräten anzeigen","space.hide_decor":"Dekorationsschicht ausblenden","space.hide_decor_tip":"Linien, Formen, Beschriftungen und Möbel bleiben an ihrem Platz — im Hintergrundeditor sichtbar, aber nicht auf dem Plan.","space.hide_openings":"Öffnungen ausblenden","space.hide_openings_tip":"Tür-, Fenster- und Tor-Symbole werden nicht gezeichnet, aber die Öffnungen funktionieren weiterhin: Licht fällt hindurch, die Sonne kommt durch ein Fenster herein, Kontaktsensoren öffnen sie weiterhin. Der Planeditor zeigt sie immer an.","gs.light_none":"Keine Lichtquellen","mode.plan":"Planeditor","mode.devices":"Geräteeditor","display.value":"Wert + Zustand","marker.subarea":"kein Bereich, manuell","device.new":"Neues Gerät — Editor öffnen, um zu schließen","opening.unlock_action":"Entsperren","opening.lock_action":"Sperren","opening.lock_pending":"Wird ausgeführt…","title.close_editor":"Editor schließen (zur Ansicht zurück)","title.add_device":"Gerät zum Plan hinzufügen","devbar.add":"Hinzufügen","devbar.rules":"Symbolregeln","device_inbox.button":"Geräte","device_inbox.title":"Geräte auf dem Plan","device_inbox.search":"Geräte, Entitäten und Integrationen suchen…","device_inbox.add_virtual":"Virtuelles Gerät hinzufügen","device_inbox.tab_on_plan":"Auf Plan","device_inbox.tab_available":"Verfügbar","device_inbox.tab_hidden":"Versteckt","device_inbox.tab_readd":"Wieder verfügbar","device_inbox.only_new":"Nur neu","device_inbox.show_entities":"Entitäten anzeigen","device_inbox.show_hidden":"Versteckte auf Plan anzeigen","device_inbox.new":"Neu","device_inbox.find":"Auf dem Plan finden","device_inbox.edit":"Bearbeiten","device_inbox.hide":"Ausblenden","device_inbox.show":"Anzeigen","device_inbox.add":"Hinzufügen","device_inbox.readd":"Wieder hinzufügen","device_inbox.hide_available":"Aus der Liste ausblenden","device_inbox.show_more":"Mehr anzeigen","device_inbox.more_actions":"Weitere Aktionen","device_inbox.find_hidden_hint":"Zuerst „Versteckte auf Plan anzeigen“ aktivieren","device_inbox.show_disabled":"Diese Bindung in Home Assistant aktivieren, bevor sie angezeigt wird","device_inbox.empty_on_plan":"Es befinden sich noch keine passenden Geräte auf dem Plan.","device_inbox.empty_available":"Es sind keine Geräte zum Hinzufügen verfügbar.","device_inbox.empty_hidden":"Es gibt keine versteckten Geräte.","device_inbox.empty_readd":"Es sind keine entfernten Geräte verfügbar, um sie erneut hinzuzufügen.","device_inbox.reason_visible_auto":"Automatisch erkannt","device_inbox.reason_visible_explicit":"Explizit hinzugefügt","device_inbox.reason_manual_hidden":"Vom Benutzer versteckt","device_inbox.reason_automatic_hidden":"Automatisch versteckt","device_inbox.reason_service_entry":"Nur Service-Registry-Eintrag","device_inbox.reason_excluded_integration":"Integration durch Gerätefilter ausgeschlossen","device_inbox.reason_excluded_domain":"Nicht-räumlicher Entitätstyp","device_inbox.reason_grouped_light":"Dargestellt durch eine Raumlichtgruppe","device_inbox.reason_represented_by_parent":"Dargestellt durch sein übergeordnetes Gerät","device_inbox.reason_removed":"Zuvor aus dem Plan entfernt","device_inbox.reason_available":"Zum Hinzufügen zum Plan verfügbar","device_inbox.reason_no_bound_room":"HA-Bereich ist nicht mit einem Planraum verknüpft","device_inbox.status_ha_disabled":"In Home Assistant deaktiviert","device_inbox.status_orphaned":"Bindung ist in Home Assistant nicht mehr vorhanden","device_inbox.status_unverified":"Home Assistant-Register vorübergehend nicht verfügbar","device_inbox.saved":"Geräteliste aktualisiert","space.roomcard_section":"Raumkarte zeigt:","space.label_temp":"Temperatur","space.label_hum":"Luftfeuchtigkeit","space.label_lqi":"Durchschnittliches Zigbee-Signal","space.label_light":"Lichter ein/aus","roomcard.light_on":"Ein","roomcard.light_off":"Aus","roomcard.light_partial":"{on} von {total}","toast.split_pick_inside":"Zwischenliegende Schnittpunkte müssen innerhalb des Raumes liegen","mode.decor":"Hintergrundeditor","decor.select":"Auswählen","decor.line":"Linie","decor.rect":"Rechteck","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Löschen","decor.erase_confirm_title":"Objekt löschen?","decor.color":"Farbe","decor.width":"Linienbreite","decor.line_style":"Linienstil","decor.line_style_solid":"Durchgehend","decor.line_style_dashed":"Gepunktet","decor.fill":"Füllen","decor.fill_color":"Füllfarbe","decor.length":"Länge","decor.size":"Größe","decor.angle":"Rotationswinkel, °","decor.text_size":"Textgröße","decor.backdrop_properties":"Eigenschaften des Planhintergrunds","decor.text_title":"Textbeschriftung","decor.object_title":"{kind} bearbeiten","decor.text_label":"Text","decor.live_group":"HA-Variable einfügen","decor.live_entity":"Entität","decor.live_entity_ph":"Wählen Sie eine Entität","decor.live_attr":"Wert","decor.live_attr_ph":"Wählen Sie Status oder Attribut","decor.live_state":"Status","decor.backdrop":"Hintergrundbild","decor.backdrop_hint":"Ziehen zum Verschieben; eine Ecke ziehen zum Größenändern; den oberen Griff zum Drehen verwenden. Shift ändert das Verhältnis oder befreit den Winkel.","decor.backdrop_reset":"Bild zurücksetzen","decor.backdrop_reset_done":"Das Bild ist wieder an seinem ursprünglichen Platz und in seiner ursprünglichen Größe","marker.icon_auto":"Auto: {icon} (nach Symbolregeln; wählen Sie eines zur Überschreibung)","marker.icon_pin_auto":"Anheften","mode.plan_tip":"Planungseditor — die Geometrie des Hauses: Räume zeichnen und teilen/zusammenführen, sie mit HA-Bereichen verbinden, Türen, Fenster und Tore platzieren, Raumkarten verschieben, Maßstab festlegen","mode.devices_tip":"Geräteeditor — alles über Symbole: ziehen zum Positionieren, klicken zum Bearbeiten von Bindung/Symbol/Anzeige, virtuelle Geräte hinzufügen, Symbolregeln","mode.decor_tip":"Hintergrundeditor — rein visuelle Dekoration unter dem Plan: Linien, Rechtecke, Ellipsen und Textbeschriftungen, die niemals auf Klicks reagieren","space.glow_enabled":"Lichtquellen-Glühen","gs.glow_group":"Glow von Lichtquellen","gs.glow_base":"Grunddunkelheit des Hauses","gs.glow_light":"Standardlichtfarbe / Intensität","gs.wall_group":"Wände","gs.wall_fill":"Wandfüllung","gs.glow_radius":"Glow-Radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Steuert andere Lichtquellen","marker.controls_hint":"Mit der Tippen-Aktion „Umschalten“ wechselt ein Klick jede hier hinzugefügte Quelle. Die eigene zugeordnete Entität des Markers wird direkt gesteuert und kann nicht hinzugefügt werden; verwenden Sie den untenstehenden Schalter, um ihn als Licht zu klassifizieren.","marker.controls_filter":"Suchlichter und Schalter…","info.controls":"Steuerungen","marker.glow_radius_label":"Leuchtradius","marker.glow_radius.help":"Legt den Leuchtradius in Metern oder Fuß fest; ein leerer Wert verwendet den Radius aus den allgemeinen Einstellungen.","marker.glow_radius.help.aria":"Hilfe: Leuchtradius","markup.wallthick":"Dicke","markup.select":"Auswählen","title.markup_wallthick":"Dicke — klicken Sie auf eine Wand, um ihre Dicke von 0 bis 100 cm festzulegen.","markup.hint_wallthick":"klicken Sie auf eine Wand · Esc schließt ohne Anwendung","wallthick.field":"Dicke","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Auf alle Wände dieses Raums anwenden","markup.draw_wall_title":"Dicke jedes neuen Wandsegments (0–100 cm). Gemeinsame Wände behalten den bestehenden Wert.","room.queue_progress":"Raum {current} von {total}","toast.wall_rooms_saved":"Erstellte Räume: {n}","toast.wall_chain_saved":"Wandkette gespeichert","toast.wallthick_pick":"Eine Wand anklicken","toast.wallthick_set":"Wandstärke eingestellt","toast.wallthick_cleared":"Wandstärke entfernt","toast.physical_range":"Einen Wert von {min} bis {max} {unit} eingeben","toast.zero_wall_opening_conflict":"Entfernen Sie zuerst die Öffnung an diesem Wandsegment.","toast.zero_wall_ambiguous":"Das Wandsegment ist mehrdeutig. Vereinfachen oder passen Sie den Anschluss an.","toast.zero_wall_migration_blocked":"Der Bereich wurde nicht konvertiert: {reason}. Es wurden keine Daten geändert.","toast.physical_angle":"Einen Drehwinkel von 0° bis unter 90° eingeben","toast.physical_limit":"Der Bereich hat das Limit für diesen Geometrietyp erreicht","toast.geometry_unsafe":"Änderung abgebrochen: Wandgeometrie konnte nicht sicher erstellt werden.","junction.limit_angle":"Der Winkel zwischen den Wänden ist zu spitz: {actual}°, mindestens {limit}°.","junction.limit_valence":"Zu viele Wände treffen in einem Knoten aufeinander: {actual}, maximal {limit}.","junction.limit_length":"Das Wandsegment ist zu kurz: {actual} cm, mindestens {limit} cm.","junction.limit_distance":"Wände und Knoten sind zu dicht beieinander: {actual} cm, Minimum {limit} cm.","junction.limit_clearance":"Kein Wohnbereich ist übrig: {actual} cm², Minimum {limit} cm².","junction.limit_check_failed":"Die Überprüfung der Verbindungen konnte nicht durchgeführt werden — die Änderung wurde nicht gespeichert.","toast.wall_model_migration_blocked":"Das Wandmodell konnte nicht aktualisiert werden: {reason}. Der Plan wurde nicht geändert. Führen Sie „Pläne optimieren“ aus; wenn der Fehler erneut auftritt, beheben Sie die widersprüchliche Wandgeometrie.","toast.wall_model_client_outdated":"Aktualisieren Sie die Karte und laden Sie die Seite neu, bevor Sie den Plan bearbeiten.","wall_model.reason.invalid-room":"ungültiger Raumkontur","wall_model.reason.zero-length":"Wandsegment mit null Länge","wall_model.reason.third-owner":"Wand, die von mehr als zwei Räumen geteilt wird","wall_model.reason.duplicate-id":"widersprüchliche Wandkennungen","wall_model.reason.thickness-conflict":"widersprüchliche Wandstärkenwerte","wall_model.reason.opening-host":"Öffnung kann nicht einer Wand zugewiesen werden","toast.delete_room_pick":"Klicken Sie in den Raum, den Sie löschen möchten","toast.plan_snap_ambiguous":"Zoomen Sie hinein, um den Wandknoten auszuwählen","toast.wall_repair_ambiguous":"Der Raumumriss hat mehr als eine mögliche Verbindung. Zoomen Sie hinein und verbinden Sie ihn explizit.","toast.wall_repair_too_large":"Die Lücke ist größer als 2 cm. Verbinden Sie die Wände, bevor Sie den Raum erstellen.","toast.wall_repair_changed":"Die Wandgeometrie hat sich geändert. Versuchen Sie erneut, den Raum zu erstellen.","toast.opening_on_zero_wall":"Öffnungen können nicht auf einer Wand mit null Dicke sitzen.","marker.from_ha_option":"Wählen Sie aus der HA-Liste.","marker.show_entities":"Entitäten anzeigen.","marker.show_entities_tip":"Fügt nicht nur Geräte zur Liste hinzu, sondern auch alle ihre Entitäten.","marker.pick_ph":"Wählen Sie ein Gerät…","room.open_area":"Öffnen Sie den HA-Bereich.","view.volumetric":"Volumetrische Ansicht.","view.flat":"Flache Ansicht.","kiosk.title":"Die Größen dieses Bildschirms.","kiosk.hint":"Nur auf diesem Gerät gespeichert – jedes Wandtablet oder TV kann seine eigenen komfortablen Größen haben.","kiosk.icon_scale":"Gerätesymbolgröße.","kiosk.font_scale":"Textgröße der Raumkarte.","editor.kiosk":"Wandgeräte- (Kiosk-)Modus.","editor.cycle":"Bereiche automatisch alle N Sekunden wechseln (Kiosk, 0 = aus)","room.settings_title":"Raumeinstellungen.","room.settings_section":"Raumeinstellungen (überschreibt den Bereich)","room.fill_label":"Diesen Raum ausfüllen","fill.inherit":"Als den Bereich","room.custom_fill_space":"Bereichsfarbe","room.custom_fill_own":"Raumfarbe","room.temp_src_label":"Temperaturquelle","room.hum_src_label":"Luftfeuchtigkeitsquelle","room.src_average":"Durchschnitt über die Sensoren des Raums (Standard)","room.src_pick":"Ein bestimmtes HA-Gerät oder Entität","room.src_ph":"Quelle wählen…","toast.room_updated":"Raum aktualisiert","space.card_font":"Schriftgröße der Raumkarte (gesamter Bereich)","room.sizes_section":"Schriftgrößen","room.name_scale":"Raumname Größe","room.label_scale":"Metrikgröße","preview.room_name":"Wohnzimmer","toast.cfg_reload_failed":"Der Plan konnte nicht vom Server neu geladen werden: {err}","toast.locale_load_failed":"Das Sprachpaket konnte nicht geladen werden. Bis zum Neuladen der Seite wird Englisch verwendet.","room.settings_short":"Raumeinstellungen","room.unnamed":"Unbenannter Raum","marker.use_climate_temp":"Geräte-Temperatur im Raum berücksichtigen","marker.use_climate_temp_tip":"Fügt die aktuelle Temperatur einer Klimaanlage oder eines Thermostats zum Raumdurchschnitt hinzu. Das externe Wert-Label unten separat konfigurieren.","marker.value_badge_title":"Wert-Label","marker.value_badge.help":"Zeigt einen ausgewählten Wert neben dem Symbol an. Dies beeinflusst nicht die Raumkennzahlen, Licht, Glanz oder die Tippaktion.","marker.value_badge.help.aria":"Hilfe: Gerätewert-Badge","marker.value_badge_enabled":"Zeige ein Wert-Badge","marker.value_badge_source":"Wert","marker.value_badge_source.help":"Wählen Sie einen bestimmten Zustand, ein unterstütztes Attribut oder einen abgeleiteten Wert dieses Geräts.","marker.value_badge_source.help.aria":"Hilfe: Quelle des Wert-Badges","marker.value_badge_position":"Position","marker.value_badge_position.help":"Die gewählte Seite bleibt fest und dreht sich nicht automatisch an einem Planrand.","marker.value_badge_position.help.aria":"Hilfe: Position des Wert-Badges","marker.value_badge_right":"Rechts","marker.value_badge_bottom":"Unten","marker.value_badge_left":"Links","marker.value_badge_top":"Oben","marker.value_badge_empty":"Für dieses Gerät stehen keine Werte zur Verfügung","marker.value_badge_static":"Ein statisches Symbol zeigt keine Live-Werte an. Die Einstellung wird beibehalten.","marker.value_badge_missing":"Quelle nicht verfügbar","marker.value_badge_missing_hint":"Die gespeicherte Quelle ist derzeit nicht verfügbar. Behalten Sie sie bei, deaktivieren Sie das Badge oder wählen Sie einen anderen Wert.","marker.value_badge_duplicate":"Derselbe Wert wird bereits im Symbol angezeigt.","marker.value_badge_state":"Status · {name}","marker.value_badge_marker_state":"Lichtquellstatus · {name}","marker.value_badge_lqi":"Durchschnittliche Zigbee-Signalqualität","marker.value_badge_attr_current_temperature":"Aktuelle Temperatur · {name}","marker.value_badge_attr_temperature":"Zieltemperatur · {name}","marker.value_badge_attr_current_humidity":"Aktuelle Luftfeuchtigkeit · {name}","marker.value_badge_attr_humidity":"Luftfeuchtigkeit · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Geschwindigkeit · {name}","marker.value_badge_attr_brightness":"Helligkeit · {name}","marker.value_badge_attr_volume_level":"Lautstärke · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Lüftergeschwindigkeit · {name}","marker.light_role_label":"Ist dieses Gerät eine Lichtquelle?","marker.light_role.help":"Steuert, ob dieser Marker selbst eine räumliche Lichtquelle ist. Verknüpfte Lichter oben bleiben unabhängige Raumlichtsteuerungen.","marker.light_role.help.aria":"Hilfe: ob dieses Gerät eine Lichtquelle ist","marker.light_role_auto_yes":"Automatisch (Lichtquelle)","marker.light_role_auto_no":"Automatisch (keine Lichtquelle)","marker.light_role_always":"Immer eine Lichtquelle","marker.light_role_never":"Nie eine Lichtquelle","marker.light_entity_label":"Führende Lichtereinheit","marker.light_entity.help":"Für ein Verbundgerät wählen Sie die Entität, deren Zustand, Farbe und Helligkeit diese Planquelle darstellen. Der automatische Rückgriff bleibt mit älteren Plänen kompatibel.","marker.light_entity.help.aria":"Hilfe: führende Lichtentität","marker.light_entity_auto":"Automatisch ({entity})","marker.light_entity_none":"keine steuerbare Entität","marker.light_entity_missing":"Die gespeicherte Entität {entity} ist nicht verfügbar. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.toggle_entity_label":"Zu toggelnde Entität","marker.toggle_entity.help":"Für ein Verbundgerät wählen Sie das genaue eigene Licht oder den Schalter, der durch Tippen bedient wird. Automatisch behält die vorherigen Zielregeln bei und ist unabhängig von der führenden Lichtentität.","marker.toggle_entity.help.aria":"Hilfe: zu toggelnde Entität","marker.toggle_entity_auto":"Automatisch ({entity})","marker.toggle_entity_none":"keine eigene steuerbare Entität","marker.toggle_entity_missing":"Die gespeicherte Entität {entity} ist nicht mehr unter den auswählbaren Kanälen dieses Markers. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.glow_color_label":"Leuchtfarbe und Helligkeit","marker.glow_mode.help":"Verwenden Sie Live-Quellwerte, überschreiben Sie nur deren Farbe oder fixieren Sie sowohl Farbe als auch Helligkeit. Das Minimum beträgt 1 %; um die Quelle zu deaktivieren, wählen Sie „Nie“.","marker.glow_mode.help.aria":"Hilfe: Leuchtfarbe und Helligkeit","marker.glow_mode_auto":"Von Quelle","marker.glow_mode_color":"Farbe einstellen","marker.glow_mode_fixed":"Farbe und Helligkeit einstellen","marker.glow_color":"Leuchtfarbe","marker.glow_brightness":"Helligkeit","marker.glow_disabled_never":"Leuchteinstellungen sind nicht verfügbar, da dieser Marker ausdrücklich keine Lichtquelle ist.","marker.glow_disabled_auto":"Im Automatikmodus wurde keine räumliche Lichtquelle für diesen Marker gefunden.","marker.glow_disabled_no_entity":"Keine aktive steuerbare Home Assistant-Entität ist für diese räumliche Quelle verfügbar.","marker.glow_passive_hint":"Diese Quelle hat keine eigenen Home Assistant-Daten. Stellen Sie ihre Farbe und Helligkeit manuell ein; ihr Radius bleibt verfügbar.","marker.control_broken":"Gespeicherte Quelle fehlt oder ist nicht mehr als Licht markiert","marker.control_missing_label":"Fehlendes Planlicht","marker.control_passive":"passive Quelle","toast.marker_control_cycle":"Dieser Link würde eine kreisförmige Kette von Lichtsteuerungen erzeugen.","toast.marker_binding_required":"Wählen Sie ein Home Assistant-Gerät, bevor Sie eine andere Lichtquelle verlinken.","confirm.unlock":"„{name}“ entsperren?","toast.files_migrate_failed":"Anhänge konnten nicht zur neuen Bindung verschoben werden, Links zeigen weiterhin auf die alten Dateien: {err}","space.pick_saved":"Bereits hochgeladen","space.pick_saved_hint":"Auf dem Server gespeicherte Pläne, einschließlich solcher, die Sie zuvor getrennt haben","space.no_saved":"Noch keine Pläne auf dem Server gespeichert.","space.loading":"Laden…","space.used_by":"in Benutzung: {list}","space.in_use":"Ein Bereich verwendet diesen Plan noch — zuerst die Verknüpfung lösen","btn.use":"Verwenden","confirm.delete_plan":"Plandatei „{name}“ vom Server löschen? Dies kann nicht rückgängig gemacht werden.","toast.plans_list_failed":"Konnte die gespeicherten Pläne nicht auflisten: {err}","toast.plan_delete_failed":"Konnte den Plan nicht löschen: {err}","marker.hide":"Ausblenden","marker.hide_tip":'Das Gerät wird nach dem Speichern aus dem Plan verschwinden, aber weiterhin zum Signalsignal des Raums zählen. Stellen Sie es über den Katalog "Geräte" im Geräte-Editor wieder her.',"marker.show":"Anzeigen","marker.show_tip":"Das Gerät wird nach dem Speichern wieder im Plan erscheinen.","marker.hidden_ghost":"Vom Benutzer ausgeblendetes Gerät","marker.ha_disabled_device":"Das Gerät ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_entity":"Die Entität ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_all_entities":"Das Gerät hat keine aktiven Home Assistant-Entitäten, daher ist es im Plan ausgeblendet.","marker.ha_registry_limited":"Das vollständige Home Assistant-Register ist für diesen Benutzer nicht verfügbar. Das nicht verifizierte Objekt kann vorerst nicht angezeigt oder verwendet werden.","marker.delete_tip":"Löschen Sie das Gerät vollständig aus dem Plan und allen Aggregaten. Sie können es später wieder hinzufügen.","tap.run":"Führen Sie Automation/Skript/Szene aus","marker.run_target_label":"Was ausführen","marker.run_search_ph":"Suchen: Automation, Skript oder Szene…","marker.run_target_gone":"Ziel {id} nicht gefunden — erneut auswählen","marker.tap_confirm":"Um Bestätigung bitten","marker.tap_confirm_tip":"Vor der Ausführung ein Bestätigungsdialogfeld anzeigen — eine Vorsichtsmaßnahme gegen versehentliche Berührungen.","marker.toggle_hint_single":"Ziel: {name} ({id}).","marker.virtual_light_target":"Virtuelles Licht: {name}.","marker.virtual_light_current":"Manueller Zustand: {state} → {effect}.","marker.virtual_light_state_on":"ein","marker.virtual_light_state_off":"aus","marker.toggle_hint_group":"Wird {count}-Quelle(n) umschalten: {names}.","marker.toggle_hint_current":"Jetzt: {state} → {effect}.","marker.toggle_hint_group_current":"Derzeit an: {on} von {count} → {effect}.","marker.toggle_hint_skipped":"Wird übersprungen ({count}): {targets}.","marker.toggle_effect_turn_on":"wird eingeschaltet","marker.toggle_effect_turn_off":"wird ausgeschaltet","marker.toggle_effect_open":"wird geöffnet","marker.toggle_effect_close":"wird geschlossen","marker.toggle_effect_stop":"wird gestoppt","marker.toggle_effect_toggle":"Zustand wird umgeschaltet","marker.toggle_skip_missing":"nicht gefunden","marker.toggle_skip_ha_disabled":"in HA deaktiviert","marker.toggle_skip_unavailable":"nicht verfügbar","marker.toggle_skip_unsupported":"Umschalten wird nicht unterstützt","marker.toggle_skip_secure":"aus Sicherheitsgründen blockiert","marker.toggle_none_no_binding":"Dieses Gerät hat keine Bindung, daher gibt es keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_no_actionable_entity":"Dieses Gerät hat keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_configured_targets_missing":"Die konfigurierten Ziele sind nicht verfügbar. Die eigene Entität des Geräts wird nicht ersetzt.","marker.toggle_none_ha_disabled":"Das Ziel ist in Home Assistant deaktiviert und kann im Plan nicht verwendet werden.","marker.toggle_none_unavailable":"Das Ziel ist derzeit nicht verfügbar. Tippen bewirkt nichts.","marker.toggle_none_unsupported":"Home Assistant bietet keine sichere Umschaltfunktion für dieses Gerät. Tippen bewirkt nichts.","marker.toggle_none_secure":"Das Umschalten von Schlössern, Alarmen und gesicherten Toren über den Plan ist aus Sicherheitsgründen gesperrt.","run.automation":"Automatisierung","run.script":"Skript","run.scene":"Szene","confirm.tap_run":'"{name}" ausführen?',"confirm.tap_toggle":'"{name}" umschalten?',"confirm.current_state":"Aktueller Zustand: {state}","confirm.expected_state":"Nach dem Umschalten: {state}","confirm.group_current":"an {on} von {total}","confirm.group_all_on":"alle sind eingeschaltet","confirm.group_all_off":"alle sind ausgeschaltet","confirm.unavailable_targets":"Nicht verfügbar: {count}","confirm.expected_by_ha":"Home Assistant ermittelt den Zustand","confirm.state_on":"Ein","confirm.state_off":"Aus","confirm.state_open":"Offen","confirm.state_closed":"Geschlossen","confirm.state_opening":"Wird geöffnet","confirm.state_closing":"Wird geschlossen","confirm.state_stopped":"Gestoppt","confirm.state_unknown":"Unbekannt","toast.run_started":"Gestartet: {name}","toast.run_target_missing":"Ausführungsziel nicht gefunden — überprüfen Sie die Geräteeinstellungen","toast.run_target_required":"Wählen Sie eine Automatisierung, ein Skript oder eine Szene aus","toast.tap_target_changed":"Das Ziel der Aktion hat sich geändert. Versuchen Sie es erneut.","toast.virtual_light_toggle_failed":"Das virtuelle Licht konnte nicht umgeschaltet werden: {err}","toast.value_badge_source_required":"Einen Wert für den Badge auswählen","btn.run":"Ausführen","vac.section":"Roboterstaubsauger: aktuelle Position","vac.autocal":"Automatisch einrichten","vac.live":"Aktuelle Position im Plan","vac.trail":"Roboterpfad anzeigen","vac.cal_maps":"Kalibrierte Karten: {maps}","vac.autocal_no_rooms":'Die Integration meldet keine Raumliste — öffnen Sie "Manuell anpassen"',"vac.autocal_no_match":'Raumnamen stimmten nicht überein (mindestens 3 müssen übereinstimmen) — öffnen Sie "Manuell anpassen"',"vac.autocal_done":"Fertig: über {rooms} Räume zugeordnet. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_need_pos":"Der Roboter meldet keine Koordinaten — starten Sie eine Reinigung und pausieren Sie sie","vac.cal_done":"Kalibrierung gespeichert. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_cancelled":"Kalibrierung abgebrochen","vac.fit":"Manuell anpassen","vac.fit_hint":"Roboterkarte an die richtige Position ziehen und an den Ecken skalieren","vac.fit_rotate":"90° drehen","vac.fit_mirror":"Spiegeln","vac.trail_never":"Nie","vac.trail_cleaning":"Beim Reinigen","vac.trail_always":"Immer","gs.bg_group":"Bühnenhintergrund","gs.bg_color":"Hintergrund um den Plan","gs.bg_default":"Standardthema","gs.bg_theme":"Standardthema","gs.bg_mode":"Planhintergrund","gs.bg_static":"Statische Farbe","gs.bg_daynight":"Folgt der Sonne","gs.bg_daynight_hint":"Der Hintergrund wechselt durch Morgendämmerung, Tag, Abenddämmerung und Nacht mithilfe von sun.sun oder Ihrer lokalen Uhr, wenn keine Sonnendaten verfügbar sind. Für den Hintergrund ist kein Kompass erforderlich.","gs.sun_group":"Sonne","gs.sun_missing":"Die sun.sun-Entität wurde nicht gefunden — der Hintergrund folgt Ihrer lokalen Uhr; Fensterstrahlen sind nicht verfügbar.","gs.north":"Norden auf dem Plan","gs.north_ph":"nicht gesetzt","gs.north_hint":"Norden nur für Fensterstrahlen einstellen (1° Schritte, 15° mit Shift); der Sonnenhintergrund funktioniert auch ohne ihn.","gs.north_clear":"Löschen","gs.north_letter":"N","gs.sun_rays":"Sonnenlicht durch Fenster","gs.about_group":"Über","gs.about_version":"House Plan Card v{v}","gs.about_github":"GitHub · Dokumentation & Probleme","gs.about_telegram":"Telegram-Chat","space.bg_color":"Hintergrund rund um den Plan","space.bg_inherit":"Allgemein erben","space.bg_inherited":"erbt allgemeine Einstellungen","space.bg_mode":"Planhintergrund","space.north":"Norden auf dem Plan (überschreiben)","space.north_inherited":"geerbt: {v}","space.sun_rays":"Sonnenlicht durch Fenster","space.sun_inherit":"Allgemein erben","space.sun_on":"Ein","space.sun_off":"Aus","canvas.far_objects":"{n} Objekt(e) weit vom Plan entfernt","canvas.show_far":"Anzeigen","canvas.home_tip":"Der Plan ist dort drüben — klicken, um ihn anzupassen","gs.grid_group":"Planwartung","gs.grid_hint":"Aktualisiert Datenmodelle, richtet Planelemente am Raster aus und fasst redundante Wandfragmente zusammen. Ein exakter Bericht wird angezeigt, bevor etwas gespeichert wird.","gs.align_all":"Pläne optimieren","gs.align_title":"Pläne optimieren","gs.align_none":"Alle Pläne verwenden bereits das aktuelle optimierte Datenmodell.","gs.optimize_no_automatic_changes":"Es gibt keine automatischen Änderungen, die angewendet werden müssen. Überprüfen Sie die unten stehenden Punkte.","gs.align_count":"{n} von {total} Elementen werden sich um höchstens {cm} cm bewegen.","gs.align_where":"Die größte Verschiebung ist in „{s}“.","gs.align_turned":"Öffnungen, deren Winkel korrigiert wurde: {n}.","gs.align_removed_drafts":"Ungültige Umrisse vom Raster zusammengeklappt und entfernt: {n}.","gs.optimize_redundant_drafts":"Gespeicherte Wandketten, die durch feste Raumwände verdeckt waren, wurden entfernt: {n}.","gs.align_preflight_failed":"Die Geometrie der folgenden Bereiche konnte nicht sicher überprüft werden: {spaces}{more}.","gs.align_preflight_hint":"Pläne wurden nicht geändert. Kopieren Sie die Diagnosen mit dem untenstehenden Button und fügen Sie sie zusammen mit einem Raumexport dem Fehlerbericht bei.","gs.preflight_reason_prepare-exception":"Die Bereichsgeometrie konnte nicht vorbereitet werden (Ausnahme beim Erstellen des Modells)","gs.preflight_reason_wall-null":"Der Wandkörper wurde nicht erstellt (die Vereinigung lieferte leer zurück)","gs.preflight_reason_wall-degraded-extra":"Der Wandkörper wurde mit zusätzlicher Geometrie beschädigt","gs.preflight_reason_wall-failed-core":"Der Kern des Wandkörpers konnte nicht zusammengesetzt werden","gs.preflight_reason_wall-exception":"Die Wandkonstruktion warf eine Ausnahme","gs.preflight_reason_floor-null":"Der Fußbodenumriss wurde nicht erstellt","gs.preflight_reason_floor-exception":"Bodenaufbau hat eine Ausnahme ausgelöst","gs.preflight_copy":"Diagnosen kopieren","gs.preflight_copied":"Diagnosen kopiert","gs.preflight_update_hint":"Die Karten- und Integrationsversionen unterscheiden sich — Hausplan aktualisieren und erneut versuchen.","gs.align_preflight_space":"Bereich {n}","gs.align_preflight_more":", und {n} mehr","gs.optimize_changes":"Modellmigrationen: {m}; aktualisierte Bereiche: {c}; entfernte verrauschte Koordinatenwerte: {p}; zusammengeführte Fragmente realer Wände: {w}; zusammengeführte Fragmente von Wänden ohne Dicke: {s}; unabhängige Wände: {i}.","gs.zero_walls_migrated":"Virtuelle Wandspannweiten konvertiert: {n}.","gs.wall_segments_migrated":"Wandsegmente stabilisiert: {n}.","gs.optimize_lattice_summary":"Rauschen in den Koordinatenwerten kanonisiert: {n}; maximale Bewegung: {cm} cm.","gs.optimize_lattice_space":"{space}: Koordinatenwerte kanonisiert: {n}; außerhalb des Rasters liegende Werte unverändert gelassen: {far}.","gs.optimize_coincident_partitions":"Versteckte unabhängige Wandabschnitte in Raumwände integriert: {n}.","gs.optimize_openings_rehosted":"Öffnungen wieder an Raumwände angebracht: {n}.","gs.optimize_walls_straightened":"Wände begradigt: {n}; maximale Bewegung: {cm} cm.","gs.optimize_walls_straightened_where":"Größte Wandkorrektur: {s}.","gs.optimize_walls_straighten_skipped":"Nahe Achsenwände unverändert gelassen, da sie nicht sicher repariert werden konnten: {n}.","gs.optimize_glow_migration":"Legacy Glow: {spaces} Bereiche → keine Datenfüllung + unabhängiges Glow; {rooms} Räume → geerbte Datenfüllung + unabhängiges Glow.","gs.optimize_references":"Referenzen repariert: Bereiche — {spaces}; Räume — {rooms}; Positionen — {positions}; Geräte aus fehlenden Bereichen gelöst — {detached}.","gs.optimize_reference_more":", und {n} mehr","gs.optimize_orphans_removed":"Vergessene Einträge entfernt: {total} — Raumbezeichnungen: {rooms}; Geräte: {devices}; Gruppenmarkierungen: {groups}. Sie gehörten zu zuvor gelöschten Bereichen.","gs.optimize_live_positions":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie werden beibehalten.","gs.optimize_live_positions_remove":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie sind zur Entfernung ausgewählt.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Alte Positionen entfernen","gs.optimize_live_keep":"Alte Positionen beibehalten","gs.optimize_live_selected":"Alte Positionen werden nach der Anwendung von Optimieren entfernt.","gs.optimize_unverified":"Positionen konnten nicht sicher überprüft werden: {n}. Sie wurden unverändert belassen.","gs.optimize_registry_limited":"Voller Administratorzugriff auf die Home Assistant-Registries ist für eine sichere Überprüfung erforderlich.","gs.optimize_vacuum_warning":"Raumzuordnungen, die noch überprüft werden müssen: {n}.","gs.optimize_details":"Details","gs.optimize_details_more":"Und {n} weitere Einträge.","gs.optimize_detail_removed":"wird entfernt","gs.optimize_detail_live":"wird beibehalten","gs.optimize_detail_unverified":"nicht überprüft","gs.optimize_detail_room_label":"Raumkennzeichnung","gs.optimize_detail_device":"Gerät","gs.optimize_detail_group":"Gruppenkennzeichen","gs.optimize_detail_unknown":"unbekannter Eigentümer","gs.optimize_detail_item":"{status}: {kind} {id}; alter Bereich {space}","gs.align_warn":"Absichtlich zwischen Rasterknoten platzierte Elemente werden sich bewegen. Nach der Operation ist nur eine Rückgängig-Funktion verfügbar, und zwar nur bis zur nächsten Planbearbeitung.","gs.align_run":"Optimieren","gs.align_done":"Pläne optimiert: {n} Elemente bewegt, {m} Datensätze beibehalten, {r} Verweise repariert","gs.optimize_undo":"Letzte Optimierung rückgängig machen","gs.optimize_undone":"Die letzte Optimierung wurde rückgängig gemacht","decor.furniture":"Möbel","furn.title":"Möbelbibliothek","furn.symbol":"Symbol","furn.group_furniture":"Möbel","furn.group_appliance":"Geräte","furn.group_sanitary":"Sanitär","furn.group_other":"Sonstiges","furn.width":"Breite","furn.depth":"Tiefe","furn.back_to_categories":"Alle Kategorien","furn.pick_hint":"Wählen Sie eine Kategorie und dann ein Symbol.","furn.place_hint":"Klicken Sie auf den Plan — das Element wird an der nächsten Wand platziert. Mit Shift wird es frei platziert.","furn.cat_air_conditioner":"Klimaanlagen","furn.cat_armchair":"Sessel","furn.cat_bathtub":"Badewannen","furn.cat_bed":"Betten","furn.cat_bidet":"Bidets","furn.cat_boiler":"Warmwasserbereiter","furn.cat_chair":"Stühle","furn.cat_coffee_table":"Couchtische","furn.cat_cooktop":"Kochfelder","furn.cat_dining_table":"Esstische","furn.cat_dishwasher":"Geschirrspüler","furn.cat_dryer":"Wäschetrockner","furn.cat_fireplace":"Kamine","furn.cat_fridge":"Kühlschränke","furn.cat_kitchen_cabinet":"Küchenschränke","furn.cat_kitchen_sink":"Küchenspülen","furn.cat_nightstand":"Schränke","furn.cat_plant":"Pflanzen","furn.cat_rug":"Teppiche","furn.cat_shelving":"Regale","furn.cat_shower":"Duschen","furn.cat_sink":"Waschbecken","furn.cat_sofa":"Sofas","furn.cat_stairs":"Treppen","furn.cat_toilet":"Toiletten","furn.cat_tv":"Fernseher","furn.cat_wardrobe":"Kleiderschränke","furn.cat_washer":"Waschmaschinen","furn.cat_work_table":"Schreibtische","furn.sym_sofa":"Zweisitzer-Sofa","furn.sym_sofa_three_seat":"Dreisitzer-Sofa","furn.sym_sofa_corner_right":"Ecksofa rechts","furn.sym_armchair":"Polstersessel","furn.sym_armchair_office":"Bürostuhl","furn.sym_coffee_table":"Rechteckiger Couchtisch","furn.sym_coffee_table_round":"Runder Couchtisch","furn.sym_coffee_table_oval":"Ovaler Couchtisch","furn.sym_coffee_table_rounded":"Abgerundeter Couchtisch","furn.sym_table_dining":"Rechteckiger Esstisch","furn.sym_table_round":"Rundtisch","furn.sym_table_dining_oval":"Ovaler Esstisch","furn.sym_table_dining_rounded":"Abgerundeter Esstisch","furn.sym_chair":"Stuhl","furn.sym_chair_bar":"Barhocker","furn.sym_desk":"Rechteckiger Schreibtisch","furn.sym_desk_corner":"Eckschreibtisch","furn.sym_bed_double":"Doppelbett","furn.sym_bed_single":"Einzelbett","furn.sym_nightstand":"Nachttisch","furn.sym_cabinet_tv":"TV-Schrank","furn.sym_cabinet_shoe":"Schuhschrank","furn.sym_cabinet_sink":"Waschtischunterschrank","furn.sym_wardrobe":"Kleiderschrank","furn.sym_bookshelf":"Bücherschrank","furn.sym_wall_unit":"Schrankwand","furn.sym_kitchen_floor":"Küchen-Unterschrank","furn.sym_kitchen_floor_corner":"Küchen-Eckunterschrank","furn.sym_kitchen_wall":"Küchen-Hängeschrank","furn.sym_kitchen_wall_corner":"Küchen-Eckhängeschrank","furn.sym_shelf_floor":"Standregal","furn.sym_shelf_wall":"Wandregal","furn.sym_fridge":"Kühlschrank","furn.sym_stove":"Vierflammiges Kochfeld","furn.sym_cooktop_two":"Zweiflammiges Kochfeld","furn.sym_dishwasher":"Geschirrspüler","furn.sym_washer":"Waschmaschine","furn.sym_dryer":"Wäschetrockner","furn.sym_tv":"Fernseher mit Standfuß","furn.sym_tv_wall":"Wandfernseher","furn.sym_ac":"Klimaanlage","furn.sym_water_heater":"Wassererhitzer","furn.sym_toilet":"Stand-WC","furn.sym_toilet_built_in":"Wand-WC","furn.sym_bathtub":"Rechteckige Badewanne","furn.sym_bathtub_corner":"Eckbadewanne","furn.sym_shower":"Dusche","furn.sym_sink":"Waschbecken","furn.sym_kitchen_sink":"Einzel-Küchenspüle","furn.sym_kitchen_sink_double":"Doppel-Küchenspüle","furn.sym_bidet":"Stand-Bidet","furn.sym_bidet_built_in":"Wand-Bidet","furn.sym_stairs":"Treppe","furn.sym_fireplace":"Kamin","furn.sym_plant":"Pflanze","furn.sym_rug":"Teppich","common.yes":"Ja","common.no":"Nein","vac.diag_source":"Quelle","vac.diag_platform":"Integration","vac.diag_status":"Status","vac.diag_position":"Position","vac.diag_rooms":"Räume","vac.diag_rooms_value":"{total} · {matched} Namen stimmen überein · {readiness}","vac.autocal_ready":"Auto-Kalibrierung verfügbar","vac.autocal_not_ready":"3 übereinstimmende Namen erforderlich","vac.diag_path":"Integrationspfad","vac.diag_map":"Karten-ID","vac.source_none":"nicht ausgewählt","vac.source_status_ok":"Bereit","vac.source_status_missing":"Fehlend","vac.source_status_disabled":"In Home Assistant deaktiviert","vac.source_status_unavailable":"Nicht verfügbar","vac.source_status_unverified":"Kann mit den aktuellen Berechtigungen nicht verifizieren","vac.source_status_unsupported":"Keine Positionsdaten","vac.source_status_none":"Keine Quelle","vac.source_banner_missing":"Die gespeicherte Quelle existiert nicht mehr. Sie wurde nicht automatisch ersetzt; wählen Sie eine andere Quelle oder stellen Sie sie in Home Assistant wieder her.","vac.source_banner_disabled":"Die gespeicherte Quelle ist in Home Assistant deaktiviert. Aktivieren Sie sie dort oder wählen Sie eine andere Quelle.","vac.source_banner_unverified":"Die aktuellen Home Assistant-Berechtigungen können diese gespeicherte Quelle nicht verifizieren. Sie bleibt angepinnt und wird nicht automatisch ersetzt.","vac.choose_source":"Quelle wählen","vac.source_auto":"Automatisch","vac.source_auto_hint":"Verwenden Sie eine kompatible Entität von diesem Gerät","vac.all_cameras":"Alle Kameras","vac.all_cameras_warn":"Eine Kamera liefert möglicherweise keine Roboter-Daten. Wählen Sie eine nur, wenn es Ihre Saugroboter-Karte ist.","vac.all_cameras_empty":"Keine anderen Kamera-Entitäten gefunden.","vac.platform_unknown":"unbekannte Integration","vac.cap_position":"Position","vac.cap_rooms_short":"Räume","vac.cap_path":"Pfad","vac.cap_map":"Karten-ID","vac.cap_none":"Keine Roboter-Daten erkannt","vac.xcme_hint":"Aktivieren Sie diese Xiaomi Cloud Map Extractor-Attribute:","vac.documentation":"Dokumentation","vac.residual_title":"Automatische Kalibrierung überprüfen","vac.residual_message":"Die zugeordneten Räume weichen um bis zu {error} voneinander ab. Diese ungefähre Kalibrierung anwenden, manuell verfeinern oder ohne Änderung der gespeicherten Einrichtung abbrechen.","vac.apply_proposal":"Anwenden","gs.backup_group":"Sichern und übertragen","gs.backup_hint":"Laden Sie ein tragbares JSON-Backup herunter oder sehen Sie eine Vorschau und importieren Sie ein Backup, das mit House Plan erstellt wurde.","backup.export_open":"Exportieren","backup.import_open":"Importieren","backup.export_title":"House Plan exportieren","backup.import_title":"House Plan importieren","backup.export_hint":"Wählen Sie, ob das Backup die gesamte House Plan-Konfiguration oder nur den aktuellen Bereich enthalten soll.","backup.full":"Vollständiges Backup","backup.current_space":"Aktueller Bereich","backup.current_space_title":"Aktueller Bereich: {title}","backup.no_current_space":"Kein aktueller Bereich","backup.plan_only":"Nur Plan","backup.plan_only_hint":"Räume, Wände, Öffnungen, Dekor und Raumbezeichnungspositionen ohne Geräte oder Home Assistant-Bindungen beibehalten.","backup.plan_only_preview":"Diese Datei enthält nur den Plan.","backup.privacy_warning":"Das Archiv enthält Namen, Home Assistant-Kennungen und genaue Koordinaten. Interne Pläne und Anhänge werden referenziert, nicht eingebettet; Laufzeitzustände und Staubsaugerspuren sind nicht enthalten.","backup.download":"JSON herunterladen","backup.export_done":"Backup heruntergeladen","backup.reading":"Überprüfe das Backup…","backup.revalidated":"Der Plan wurde nach dieser Vorschau geändert. Die Zusammenfassung wurde aktualisiert; überprüfen Sie sie und bestätigen Sie erneut.","backup.error.unauthorized":"Sie haben keine Berechtigung, diesen Plan zu exportieren oder zu importieren.","backup.error.not_ready":"Der Hausplan ist noch nicht fertig. Versuchen Sie es in einem Moment erneut.","backup.error.too_large":"Das Backup überschreitet das 8-MiB-Limit.","backup.error.invalid_json":"Die ausgewählte Datei ist kein gültiges JSON.","backup.error.invalid_format":"Die ausgewählte Datei ist kein House Plan-Backup.","backup.error.unsupported_export_version":"Dieses Backup-Format wird von der installierten Version nicht unterstützt.","backup.error.future_model":"Das Backup wurde mit einem neueren House-Plan-Datenmodell erstellt.","backup.error.invalid_config":"Das Backup enthält eine ungültige House-Plan-Konfiguration.","backup.error.wall_model_migration_blocked":"Das Backup-Wandmodell konnte nicht sicher aktualisiert werden. Optimieren Sie zuerst den Quellplan und exportieren Sie ihn dann erneut.","backup.error.invalid_layout":"Das Backup enthält ungültige Objektpositionen.","backup.error.invalid_content":"Das Backup enthält ungültige oder inkonsistente Inhaltsreferenzen.","backup.error.space_not_found":"Der ausgewählte Bereich existiert nicht mehr.","backup.error.capacity_exceeded":"Durch das Hinzufügen dieses Backups würden die Planlimits überschritten.","backup.error.preview_expired":"Die Vorschau ist abgelaufen. Wählen Sie die Backup-Datei erneut aus.","backup.error.preview_owner_mismatch":"Diese Vorschau gehört zu einem anderen Home-Assistant-Benutzer.","backup.error.conflict":"Der Plan hat sich nach der Vorschau geändert. Überprüfen Sie die aktualisierte Zusammenfassung.","backup.error.content_confirmation_required":"Bestätigen Sie, dass nicht verfügbare lokale Inhalte getrennt werden können.","backup.error.commit_failed":"Das Backup konnte nicht sicher angewendet werden. Der vorherige Plan wurde wiederhergestellt oder wartet auf die Wiederherstellung.","backup.error.missing_plan":"Eine referenzierte Plan-Datei ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.missing_content":"Ein referenziertes lokales Anhangselement ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.marker_control_missing":"Ein verknüpftes Planlicht fehlt im Backup.","backup.error.marker_control_not_light":"Ein verknüpftes Planziel ist nicht mehr als Lichtquelle markiert.","backup.error.marker_control_self":"Eine Lichtquelle kann sich nicht selbst steuern.","backup.error.marker_control_cycle":"Das Backup enthält eine kreisförmige Kette von Lichtsteuerungen.","backup.error.duplicate_marker_control":"Das Backup enthält eine doppelte Plan-Licht-Verknüpfung.","backup.error.no_backup":"Es gibt keinen Import- oder Optimierungssnapshot zur Wiederherstellung.","backup.same_source":"Erstellt auf dieser Home Assistant-Instanz","backup.foreign_source":"Erstellt auf einer anderen Home Assistant-Instanz","backup.created":"Erstellt: {value}","backup.versions":"Karte {card}; Integration {integration}; Datenmodell {model}","backup.count_spaces":"Bereiche: {n}","backup.count_rooms":"Räume: {n}","backup.count_walls":"Wände: {n}","backup.count_openings":"Öffnungen: {n}","backup.count_decor":"Dekorationsobjekte: {n}","backup.count_markers":"Geräte: {n}","backup.count_layout":"Positionen: {n}","backup.bindings":"Verknüpfungen — Geräte: {device}, Entitäten: {entity}, virtuell: {virtual}; Positionen ohne Bereich: {legacy}","backup.binding_status":"Zielstatus — aktiv: {active}, deaktiviert: {disabled}, fehlt: {missing}","backup.missing_areas":"Auf dem Ziel fehlende Bereiche: {areas}","backup.dropped_marker_links":"Planlicht-Verknüpfungen außerhalb dieses übertragenen Bereichs wurden ausgelassen: {n}.","backup.repaired_target_refs":"Durch den Import dieses Bereichs wiederhergestellte vorhandene Referenzen: {n}.","backup.preserved_unresolved_refs":"Referenzen, die nicht eindeutig wiederhergestellt werden konnten, wurden beibehalten: {n}.","backup.preserved_unresolved_hint":"Es wurden keine Daten geraten oder gelöscht. Führen Sie nach dem Import Optimierungspläne aus, um die verbleibenden Referenzen zu überprüfen.","backup.import_details":"Importieren von Referenzdetails","backup.import_detail.incoming_remapped":"Referenzen innerhalb der importierten Kopie aktualisiert: {n}","backup.import_detail.target_repaired":"Vorhandene Referenzen wiederhergestellt: {n}","backup.import_detail.preserved_unresolved":"Unaufgelöste Referenzen beibehalten: {n}","backup.import_detail.collisions":"Zielkonflikte sicher beibehalten: {n}","backup.import_detail.dropped_links":"Eingehende Links durch Übertragungsregeln ausgelassen: {n}","backup.import_detail.bounded_lineages":"Übermäßig verschachtelte Kennungen begrenzt belassen: {n}","backup.replace_warning":"Dies ersetzt die aktuelle Konfiguration und das Layout. Hochgeladene Dateien werden niemals gelöscht. Eine Rückgängig-Möglichkeit bleibt bis zur nächsten Planbearbeitung verfügbar.","backup.foreign_bookkeeping":"Instanzspezifische bekannte/neue Gerätebuchführung wird nicht importiert.","backup.final_name":"Name des neuen Bereichs","backup.target_settings":"Die globalen Einstellungen der Zielinstanz bleiben unverändert.","backup.duplicates":"Bindungen bereits vorhanden: {n}","backup.skip":"Doppelte Geräte überspringen","backup.virtual_copy":"Sichere statische virtuelle Kopien hinzufügen","backup.content":"Referenzierter Inhalt","backup.content_available":"lokal verfügbar","backup.content_external":"externer Link","backup.content_detach_required":"wird getrennt","backup.confirm_detach":"Ich verstehe, dass nicht verfügbare interne Pläne und Anhänge von der importierten Konfiguration getrennt werden.","backup.replace":"Ersetzen","backup.add":"Bereich hinzufügen","backup.space_done":"Bereich importiert: {rooms} Räume, {markers} Geräte, {refs} bestehende Referenzen wiederhergestellt","backup.full_done":"Backup wiederhergestellt: {spaces} Bereiche, {rooms} Räume, {markers} Geräte","backup.undo_import":"Letzten vollständigen Import rückgängig machen","backup.import_undone":"Vollständiger Import rückgängig gemacht","backdrop.large_title":"Großes Bild","backdrop.large_body":"Dieses Bild ist {w}×{h} ({fileMb} MB Datei) und benötigt etwa {decodedMb} MB Speicher zur Anzeige. Auf Tablets kann das die Seite zum Absturz bringen.","backdrop.unknown_body":"Die Bildabmessungen konnten nicht gelesen werden — die Datei ist möglicherweise beschädigt. Fortfahren kann die Seite auf Tablets zum Absturz bringen.","backdrop.use_downscaled":"Verkleinerte Kopie hochladen","backdrop.reducing":"Wird verkleinert…","backdrop.keep_original":"Original behalten","backdrop.too_large_title":"Bild ist zu groß","backdrop.too_large_body":"Dieses Bild überschreitet die Anzeigegrenzen des Browsers ({w}×{h}, Limit {limit} px pro Seite). Bitte verkleinern Sie es in einem Desktop-Editor und laden Sie es erneut hoch.","backdrop.downscale_failed":"Die verkleinerte Kopie konnte nicht erstellt werden. Der aktuelle Plan wurde nicht geändert."};const n="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";export{e as dictionary,n as fingerprint}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/editor-CcxmUFV-.js b/dist/houseplan-assets/editor-CIkons5x.js similarity index 84% rename from custom_components/houseplan/frontend/houseplan-assets/editor-CcxmUFV-.js rename to dist/houseplan-assets/editor-CIkons5x.js index f0511882..1e597f75 100644 --- a/custom_components/houseplan/frontend/houseplan-assets/editor-CcxmUFV-.js +++ b/dist/houseplan-assets/editor-CIkons5x.js @@ -1,14 +1,14 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";import{c as e,l as o,t,c_ as s,A as a,c$ as i,d0 as l,d1 as n,E as r,b as c}from"./houseplan-card-B5mDyjuC.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,f={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},h=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`h[e.name]||e.name} + .computeLabel=${e=>f[e.name]||e.name} @value-changed=${this._valueChanged} >`;return c` - ${d(f.slice(0,3))} + ${d(h.slice(0,3))} ${_?c``:a} - ${d(f.slice(3))} - `}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h); + ${d(h.slice(3))} + `}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}f.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",f); diff --git a/dist/houseplan-assets/fr-CFpxbPTS.js b/dist/houseplan-assets/fr-CFpxbPTS.js deleted file mode 100644 index 1a4c3165..00000000 --- a/dist/houseplan-assets/fr-CFpxbPTS.js +++ /dev/null @@ -1 +0,0 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";var e={"editor.context_actions":"Actions : {object}","editor.tool_options":"Options de l’outil : {tool}","editor.palette":"Palette : {tool}","editor.open_group":"Groupe d’outils : {group}","editor.group_active":"{group} — actif : {item}","editor.disabled_action":"{action} est indisponible : {reason}","editor.loading":"Chargement de l'éditeur…","editor.loading_aria":"L'éditeur de plan est en cours de chargement","editor.load_failed":"Impossible de charger l'éditeur.","editor.refresh_advice":"Actualisez la page et réessayez.","editor.retry_advice":"Vérifiez votre connexion et appuyez à nouveau.","color_picker.title":"Sélecteur de couleur","color_picker.hue":"Teinte","color_picker.saturation":"Saturation","color_picker.value":"Luminosité","color_picker.hex":"Couleur hexadécimale","color_picker.invalid_hex":"Saisissez une couleur hexadécimale à 3 ou 6 chiffres","btn.properties":"Propriétés","btn.keep_as_walls":"Conserver comme murs","btn.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","btn.delete_room_with_walls":"Supprimer la pièce et les murs","title.markup_select":"Sélectionner et modifier les murs, colonnes et contours enregistrés","title.markup_column":"Colonne : cliquez sur un point de la grille pour placer une colonne carrée","markup.partition":"Cloison","markup.column":"Colonne","markup.hint_column":"cliquez sur un point de la grille pour placer une colonne carrée","history.draft_segment":"Ajouter un segment de brouillon de pièce","history.draft_merge":"Relier des contours de pièce inachevés","history.draft_segment_delete":"Supprimer un segment de brouillon de pièce","history.partition_add":"Ajouter une cloison","history.column_add":"Ajouter une colonne","history.physical_edit":"Modifier l’objet physique","history.physical_delete":"Supprimer l’objet physique","history.physical_move":"Déplacer l’objet physique","history.contour_to_partitions":"Convertir le contour en murs fermés","history.wall_chain_finish":"Terminer la chaîne de murs","history.wall_face_batch":"Créer des pièces à partir des murs","toast.column_duplicate":"Une colonne avec le même centre et la même dimension extérieure existe déjà","confirm.delete_draft":"Supprimer tout le contour de pièce inachevé ?","confirm.delete_draft_segment":"Supprimer ce segment de brouillon ? Le contour restant peut se scinder en deux.","physical.partition_properties":"Propriétés de la cloison","physical.column_properties":"Propriétés de la colonne","physical.draft_properties":"Propriétés du segment de brouillon","physical.shape":"Forme","physical.square":"Carré","physical.circle":"Cercle","physical.diameter":"Diamètre","physical.side":"Côté","physical.rotation":"Angle de rotation","physical.length":"Longueur","physical.allowed_range":"Autorisé : {min}–{max} {unit}","physical.delete_segment":"Supprimer le segment","physical.delete_draft":"Supprimer tout le contour","physical.column_size_title":"Côté extérieur de la colonne carrée placée par le clic (1–150 cm).","card.title":"Plan de la maison","count.devices":"{n} app.","empty.no_spaces":"Aucun espace pour le moment.","empty.add_first":"Ajoutez le premier espace et importez un plan.","empty.install":"Installez l’intégration House Plan et ajoutez-la dans « Appareils et services ».","btn.add_space":"Ajouter un espace","btn.cancel":"Annuler","btn.save":"Enregistrer","btn.close":"Fermer","btn.delete":"Supprimer","btn.edit":"Modifier","btn.open_in_ha":"Ouvrir dans HA","btn.reset":"Réinitialiser","btn.attach":"Joindre…","btn.upload":"Importer…","btn.replace":"Remplacer…","title.zoom_in":"Zoomer","title.zoom_out":"Dézoomer","title.zoom_fit":"Tout ajuster","title.markup":"Traçage des pièces : grille, lignes, contours","title.configure_space":"Configurer l’espace","title.add_space":"Ajouter un espace","title.markup_add":"Murs : tracez une chaîne continue ; Maj verrouille à 45°. Changer d’outil ou quitter l’éditeur termine la chaîne en murs indépendants","title.markup_merge":"Fusionner : cliquez sur une pièce, puis sur la pièce voisine avec laquelle elle partage un mur","title.markup_split":"Diviser une pièce : cliquez sur la pièce, puis sur deux points de ses murs","title.markup_delroom":"Supprimer une pièce : cliquez à l’intérieur de la pièce puis confirmez","title.need_plan":"Importer une image de plan","markup.add":"Murs","markup.merge":"Fusionner","markup.split":"Diviser","markup.resize":"Redimensionner","title.markup_resize":"Redimensionner les pièces : faites glisser une poignée disponible sur un mur horizontal ou vertical","markup.hint_resize":"faites glisser une poignée de mur disponible · le mur s’arrête à la première position non sûre · Échap annule · Ctrl+Z — annuler","resize.disabled.diagonal":"Ce mur est incliné ; le redimensionnement ne prend en charge que les murs horizontaux et verticaux","resize.disabled.side-angle":"Un mur adjacent est incliné ; le redimensionnement exige que les deux murs adjacents rencontrent ce mur à angle droit","resize.disabled.duplicate-physical-wall":"Une cloison distincte, un contour inachevé ou une colonne chevauche ce mur ; supprimez-la ou déplacez-la avant de redimensionner","resize.disabled.partial-shared":"Une pièce voisine n’utilise qu’une partie de ce mur ; il ne peut donc pas être déplacé en toute sécurité comme un mur partagé unique","resize.disabled.unequal-shared":"La pièce voisine utilise des extrémités ou une longueur de mur différentes ; les murs ne peuvent donc pas être déplacés ensemble en toute sécurité","resize.disabled.multiple-rooms":"Déplacer ce mur affecterait plus de deux pièces","resize.disabled.thickness-conflict":"L’épaisseur du mur ne peut pas être conservée en toute sécurité","resize.disabled.opening-conflict":"Une ouverture sur ce mur ne peut pas être déplacée en toute sécurité","resize.disabled.invalid-geometry":"Ce mur ne peut pas être redimensionné sans modifier la structure du plan","resize.limit_stopped":"Mur arrêté : l'étape suivante enfreindrait une limite de jonction","resize.commit_failed":"Le redimensionnement a été annulé car le plan final n’a pas passé le contrôle de géométrie","resize.preview_failed":"Le redimensionnement s’est arrêté à la dernière position sûre car la géométrie du plan ne pouvait pas être préservée","markup.opening":"Ouverture","markup.delete_room":"Supprimer la pièce","history.undo":"Annuler","history.redo":"Rétablir","history.undo_named":"Annuler : {name} (Ctrl+Z)","history.redo_named":"Rétablir : {name} (Ctrl+Maj+Z / Ctrl+Y)","history.undo_empty":"Rien à annuler","history.redo_empty":"Rien à rétablir","history.undone":"Annulé : {name}","history.redone":"Rétabli : {name}","history.add_room":"Créer une pièce","history.split_room":"Diviser la pièce","history.merge_rooms":"Fusionner les pièces","history.resize_room":"Redimensionner la pièce","history.wall_thickness":"Modifier l’épaisseur du mur","history.add_opening":"Ajouter une ouverture de porte ou de fenêtre","history.edit_opening":"Modifier l’ouverture","history.move_opening":"Déplacer l’ouverture","history.delete_opening":"Supprimer l’ouverture","history.delete_room":"Supprimer la pièce","history.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","history.delete_room_with_walls":"Supprimer la pièce et les murs","history.decor_add":"Ajouter un objet décoratif","history.decor_edit":"Modifier l’objet décoratif","history.decor_move":"Déplacer l’objet décoratif","history.decor_transform":"Transformer l’objet décoratif","history.decor_delete":"Supprimer l’objet décoratif","history.backdrop_transform":"Transformer l’arrière-plan du plan","opening.new":"Nouvelle ouverture","opening.edit":"Ouverture","opening.door":"Porte","opening.window":"Fenêtre","opening.gate":"Portail","opening.passage":"Passage ouvert","opening.passage_binding_warning":"L’enregistrement supprimera le capteur d’ouverture/fermeture et la serrure.","opening.invalid_passage_fields":"Le passage ouvert du plan « {room} » possède des paramètres non pris en charge : {fields}.","opening.type_label":"Type","opening.length_label":"Longueur, cm","opening.contact_label":"Capteur d’ouverture/fermeture","opening.lock_label":"Serrure","opening.none":"— aucun —","opening.search_ph":"Rechercher : nom ou ID d’entité…","opening.invert":"Inverser ouvert/fermé","opening.flip_h":"Charnière sur l’autre montant","opening.flip_v":"Ouverture de l’autre côté","opening.open":"Ouvert","opening.closed":"Fermé","opening.locked":"Verrouillé","opening.unlocked":"Déverrouillé","opening.state_unknown":"indisponible","opening.no_entities":"Aucun capteur associé — symbole statique sur le plan.","toast.opening_no_wall":"Cliquez près d’un mur de pièce ou d’un mur indépendant","opening.host_partition":"Mur indépendant","opening.partition_orphan":"Le mur indépendant de cette ouverture n’existe plus","opening.partition_jamb_margin":"Laissez au moins {distance} entre l’ouverture et l’extrémité du mur","opening.rebind_partition":"Associer à un autre mur indépendant","confirm.delete_partition_openings_title":"Supprimer le mur et les ouvertures ?","confirm.delete_partition_openings_body":"Ce mur contient {count} ouverture(s). Elles seront supprimées avec lui.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Supprimer","markup.hint_points":"points : {n} · Maj — pas de 45° · Échap — terminer la chaîne · Ctrl+Z — annuler un point · fermer une zone propose de créer une pièce","markup.hint_start":"cliquez sur un point de la grille pour commencer une chaîne de murs","tip.lqi":"signal Zigbee moyen :","tip.area":"surface : {value}","info.device_header":"Appareil sur le plan","info.model":"Modèle","info.state":"État","info.link":"Lien","info.manuals":"Manuels","info.none":"Aucune information supplémentaire","marker.new_device":"Nouvel appareil","marker.name_label":"Nom (affiché sur le plan)","marker.name_ph":"Nom","marker.binding_label":"Associer à un appareil HA","marker.binding_disabled":"désactivé dans Home Assistant","marker.virtual_option":"Appareil virtuel (sans association)","marker.search_ph":"Rechercher un appareil / groupe…","marker.nothing_found":"aucun résultat","marker.room_label":"Pièce","marker.room_override":" (placement forcé)","marker.room_choose":"— sélectionner une pièce —","marker.room_auto":"— selon la zone de l’appareil (auto) —","marker.icon_label":"Icône","marker.icon_ph":"mdi:… (vide = auto)","marker.display_label":"Affichage","display.badge":"Icône + état","display.icon_ripple":"Icône + état et activité","display.static_icon":"Icône toujours statique","marker.display_hint":"Icône + état modifie le fond pour les états en fonctionnement, ouvert, alarme et indisponible. Icône + état et activité ajoute une brève pulsation pour les événements et une pulsation continue pour une activité, un mouvement ou une présence en cours. Valeur + état remplace l’icône par une valeur HA non ambiguë. Une icône statique ne réagit jamais aux états de l’appareil.","marker.display_hint_badge":"L’icône et le fond dynamique affichent l’état de l’appareil sans la pulsation d’activité habituelle. Les alarmes rouges restent visibles.","marker.display_hint_icon":"L’icône et le fond dynamique affichent l’état de l’appareil sans la pulsation d’activité habituelle. Les alarmes rouges restent visibles.","marker.display_hint_icon_ripple":"L’icône, le fond dynamique et la pulsation affichent une brève animation pour les événements et une animation continue pour le fonctionnement, le mouvement ou la présence. Les alarmes rouges restent prioritaires.","marker.display_hint_value":"Une valeur Home Assistant non ambiguë remplace l’icône tandis que le fond continue d’indiquer l’état. Les alarmes rouges restent visibles.","marker.display_hint_static_icon":"Le contour adapté au thème et l’icône restent toujours identiques. L’état, l’activité, l’indisponibilité et les alarmes ne modifient pas l’apparence.","marker.static_alarm_warning":"Cet appareil peut signaler des alarmes. L’affichage statique masque l’indication visuelle d’alarme du marqueur.","marker.preview.title":"Aperçu de l’affichage","marker.preview.actual":"Actuel","marker.preview.example":"Exemple","marker.preview.integration":"Fourni par","marker.preview.source":"Source d’affichage","marker.preview.current_state":"État actuel","marker.preview.result":"Sur le plan","marker.preview.details":"Détails de la source","marker.preview.select_source":"Choisissez un appareil ou une entité Home Assistant pour voir son affichage réel.","marker.preview.unknown_provider":"Intégration inconnue","marker.preview.virtual_provider":"House Plan · appareil virtuel","marker.preview.no_source":"Aucune source active","marker.preview.no_state":"Aucun état actuel","marker.preview.mixed_states":"Plusieurs états différents","marker.preview.multiple_sources":"{n} sources","marker.preview.more_sources":"+{n} de plus","marker.preview.scaled":"L’aperçu est redimensionné à {n}% pour tenir dans la zone. La taille enregistrée reste inchangée.","marker.preview.demo_short":"Afficher une activité brève","marker.preview.demo_continuous":"Afficher une activité continue","marker.preview.stop_continuous":"Arrêter l’activité continue","marker.preview.demo_short_notice":"Exemple d’activité brève ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_continuous_notice":"Exemple d’activité continue ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_already_visible":"L’état actuel affiche déjà un véritable effet d’activité ou d’alarme.","marker.preview.reduced_motion":"La réduction des animations est activée sur le système ; l’activité normale est donc affichée sous forme de point.","marker.preview.reason.neutral":"Fond sombre neutre","marker.preview.reason.working":"Fond jaune : l’appareil fonctionne actuellement","marker.preview.reason.working_activity":"Fond jaune et effet d’activité : l’appareil fonctionne actuellement","marker.preview.reason.open":"Fond orange : physiquement ouvert ou déverrouillé","marker.preview.reason.cover_icon_state":"Fond neutre ; l’entité de volet contrôle l’état et l’icône affichés","marker.preview.reason.presence":"Effet d’activité tant qu’une présence est détectée","marker.preview.reason.event":"Effet d’activité bref après un événement détecté","marker.preview.reason.transition":"Effet d’activité pendant que l’appareil se déplace ou change d’état","marker.preview.reason.media_neutral":"Les appareils multimédias restent sur un fond sombre neutre tant qu’ils sont disponibles","marker.preview.reason.unavailable":"Fond neutre atténué : indisponible ou éteint","marker.preview.reason.alarm":"Fond d’alarme rouge ; l’indication d’alarme est toujours affichée","marker.preview.reason.live_states_disabled":"Le style selon les états en direct est désactivé pour cette carte","marker.preview.reason.value_no_state":"Aucune valeur d’état exploitable ; l’icône est affichée à la place","marker.preview.reason.value_ambiguous_sources":"Plusieurs sources de valeur possibles ; l’icône est affichée à la place","marker.preview.reason.value_non_scalar":"La source n’a pas renvoyé de valeur simple ; l’icône est affichée à la place","marker.preview.reason.value_virtual":"Un appareil virtuel n’a aucune valeur Home Assistant ; l’icône est affichée à la place","marker.preview.reason.vacuum_live_plan_only":"La position et la trajectoire en direct de l’aspirateur ne sont disponibles que sur le plan complet","marker.preview.reason.hidden_design_preview":"L’appareil est masqué sur le plan ; cet aperçu de conception reste visible","marker.preview.reason.composite_power_source":"L’état provient de l’entité d’alimentation de l’appareil ; les interrupteurs auxiliaires sont ignorés","marker.preview.reason.activity_display_disabled":"Le mode d’affichage sélectionné n’affiche pas les effets d’activité habituels","marker.preview.reason.ha_disabled":"L’association Home Assistant est désactivée et sera masquée sur le plan","marker.preview.reason.orphaned":"L’association Home Assistant enregistrée est introuvable","marker.preview.reason.static_icon":"Mode statique : l’état de l’appareil ne modifie pas l’icône","marker.activity_color":"Couleur de la pulsation d’activité","marker.ripple_size":"Taille de la pulsation d’activité","marker.activity_alarm_note":"La couleur et la taille n’affectent que l’activité normale et ne modifient pas les alarmes rouges.","marker.pulse_a11y_alarm":"Alarme","marker.pulse_a11y_event":"Événement récent","marker.pulse_a11y_presence":"Présence détectée","marker.pulse_a11y_transition":"Changement d’état","marker.pulse_a11y_running":"En fonctionnement","marker.state_a11y_neutral":"État par défaut","marker.state_a11y_working":"En fonctionnement","marker.state_a11y_open":"Ouvert","marker.state_a11y_locked":"Verrouillé","marker.state_a11y_unlocked":"Déverrouillé","marker.state_a11y_alarm":"Alarme","marker.state_a11y_unavailable":"Indisponible","marker.lqi_a11y_low":"LQI {value}, signal faible","marker.lqi_a11y_mid":"LQI {value}, signal moyen","marker.lqi_a11y_high":"LQI {value}, signal fort","marker.size_label":"Taille / rotation de l’icône","marker.angle_label":"Rotation","marker.model_label":"Modèle","marker.model_ph":"ex. Aqara T&H","marker.link_label":"Lien","marker.desc_label":"Description","marker.desc_ph":"Notes, caractéristiques…","marker.manuals_label":"Manuels (PDF, etc.)","marker.sub_device":"appareil","marker.sub_z2m_group":" · groupe Z2M","marker.sub_group":"groupe","marker.sub_helper":"assistant","space.new":"Nouvel espace","space.header":"Espace","space.title_label":"Titre","space.title_ph":"ex. Garage","space.plan_label":"Plan (arrière-plan)","space.no_plan":"aucune image de plan","space.plan_alt":"plan","room.new":"Nouvelle pièce","room.name_label":"Nom affiché","room.name_ph":"ex. Terrasse","room.area_label":"Zone Home Assistant (non attribuée)","room.no_area_option":"— aucune zone —","room.default_name":"Pièce","device.unnamed":"sans nom","device.light_group":"groupe de lumières","device.fallback":"appareil","device.virtual":"appareil virtuel","confirm.delete_room":"Supprimer la pièce « {name} » ?","confirm.delete_room_title":"Supprimer la pièce « {name} » ?","confirm.delete_room_body":"Choisissez si les murs physiques propres à la pièce doivent être conservés. Les murs partagés avec une autre pièce sont toujours conservés.","confirm.remove_marker":"Supprimer « {name} » du plan ? L’appareil disparaîtra complètement et ne contribuera plus aux données du plan. Vous pourrez l’ajouter de nouveau plus tard.","confirm.erase_decor":"Effacer l’objet {kind} ? Vous pouvez annuler cette action depuis l’historique de l’éditeur.","confirm.delete_space":"Supprimer l’espace « {title} » avec toutes ses pièces et son tracé ?","space.delete_blocked":"Cet espace est encore utilisé par {n} appareil(s). Déplacez-les vers un autre espace ou supprimez-les d’abord.","toast.pos_save_failed":"Échec de l’enregistrement de la position : {err}","toast.no_entity":"L’appareil n’a aucune entité adaptée","toast.ha_disabled_action":"Un objet Home Assistant désactivé ne peut pas être utilisé sur le plan.","toast.ha_disabled_show_device":"Un appareil désactivé dans Home Assistant ne peut pas être affiché sur le plan. Activez-le d’abord dans Home Assistant.","toast.ha_disabled_show_entity":"Une entité désactivée dans Home Assistant ne peut pas être affichée sur le plan. Activez-la d’abord dans Home Assistant.","toast.ha_disabled_add":"Un objet Home Assistant désactivé ne peut pas être ajouté au plan. Activez-le d’abord dans Home Assistant.","toast.ha_binding_unverified":"L’état de l’objet n’a pas pu être vérifié via le registre Home Assistant. L’affichage et les actions sont temporairement indisponibles.","toast.markup_needs_server":"Le tracé est disponible une fois la configuration déplacée sur le serveur","toast.space_order_changed":"L’ordre a changé. Si une carte fixe son étage par numéro, vérifiez ces tableaux de bord.","toast.conflict":"La configuration a été modifiée dans une autre fenêtre — données actualisées, répétez votre dernière action","toast.cfg_save_failed":"Échec de l’enregistrement de la configuration : {err}","toast.room_overlap":"Le contour chevauche la pièce « {name} » — les pièces ne doivent pas se chevaucher","toast.contour_cannot_close":"Le contour ne peut pas être fermé car il est dégénéré ou s’auto-intersecte","toast.merge_not_adjacent":"Seules les pièces partageant un mur peuvent être fusionnées","toast.rooms_merged":"Pièces fusionnées dans « {name} »","toast.split_pick_wall":"Commencez la découpe sur le mur de la pièce","toast.split_bad_cut":"La découpe doit aller d’un mur à l’autre à l’intérieur de la pièce, sans croiser de mur ni se croiser elle-même","merge.header":"Fusionner les pièces","merge.hint":"La pièce fusionnée conserve un nom et une zone. L’autre zone est libérée — ses appareils quittent le plan jusqu’à ce qu’une autre pièce la récupère.","merge.keep":"Conserver","merge.no_area":"aucune zone","toast.room_saved":"Pièce enregistrée ({n}). Appareils ajoutés : {added}. Tracez la suivante ou quittez le mode tracé.","toast.room_saved_no_area":"Pièce enregistrée ({n}, sans zone). Tracez la suivante ou quittez le mode tracé.","toast.marker_needs_server":"La modification des appareils est disponible une fois la configuration déplacée sur le serveur","toast.virtual_name_required":"Saisissez un nom pour l’appareil virtuel","toast.marker_saved":"Appareil enregistré","toast.marker_removed":"Appareil supprimé du plan","toast.integration_missing":"L’intégration House Plan n’est pas installée — gestion indisponible","toast.plan_formats":"Formats pris en charge : SVG, PNG, JPG, WebP","toast.plan_required":"Importez un plan — il est obligatoire","toast.space_added_onboard":"Espace ajouté. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","toast.space_added":"Espace ajouté","toast.space_saved":"Espace enregistré","toast.space_deleted":"Espace supprimé","toast.delete_failed":"Échec de la suppression : {err}","toast.error":"Erreur : {err}","toast.toggle_target_unavailable":"La cible « {name} » est indisponible — aucune action n’a été effectuée","toast.toggle_targets_unavailable":"Cibles indisponibles : {names}. Aucune action n’a été effectuée","toast.file_failed":"Le fichier « {name} » n’a pas été importé : {err}","toast.files_attached":"Fichiers joints : {n}","err.unknown":"erreur inconnue","err.code":"code {code}","err.too_large":"fichier supérieur à {mb} Mo","err.bad_ext":"type non pris en charge (PDF/image attendu)","err.unauthorized":"droits administrateur requis","editor.title":"Titre","editor.default_floor":"Espace initial","editor.default_floor_missing":"L’espace initial « {id} » n’existe plus. Choisissez un autre espace.","editor.floor":"Espace fixe","editor.floor_none":"— non fixé —","editor.floor_index":"Index YAML {index} (conservé)","fixed_floor.loading":"Chargement de l’espace fixe…","fixed_floor.invalid_title":"L’espace fixe est indisponible","fixed_floor.invalid_body":"Vérifiez floor dans la configuration de la carte. Valeur configurée : {value}","editor.icon_size":"Taille des icônes, % de la largeur du plan","editor.show_temperature":"Afficher la température","editor.live_states":"États en direct (on/off, ouvert…)","editor.light_pools":"Halos lumineux et ombres des murs","editor.show_signal":"Afficher le signal Zigbee (LQI)","editor.language":"Langue de l’interface","editor.lang_auto":"Auto (profil HA)","title.icon_rules":"Règles d’icônes : quelle icône MDI attribuer aux appareils selon leur nom","rules.title":"Règles d’icônes","rules.hint":"Les règles sont testées de haut en bas sur « nom de l’appareil + modèle » (expression régulière insensible à la casse) ; la première correspondance gagne. Si rien ne correspond, la classe d’appareil de l’entité est utilisée, puis l’icône générique.","rules.pattern_ph":"regex, ex. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Ajouter une règle","rules.reset":"Réinitialiser par défaut","rules.test_ph":"Tester un nom d’appareil…","rules.invalid":"expression régulière invalide","rules.saved":"Règles d’icônes enregistrées","btn.up":"Monter","btn.down":"Descendre","tap.info":"Fiche de l’appareil","tap.more_info":"Boîte de dialogue Plus d’infos de HA","tap.toggle":"Basculer l’état","marker.tap_label":"Action au toucher pour cet appareil","tap.toggle_note":"L’état déterminé est affiché ci-dessous. Les appareils sécurisés ne sont jamais basculés depuis le plan.","import.title":"Créer des espaces à partir des étages HA","import.hint":"Votre Home Assistant connaît déjà ces étages. Sélectionnez ceux à transformer en espaces du plan — vous importerez ensuite une image de plan pour chacun. Les pièces sont ensuite tracées manuellement sur le plan.","import.start":"Créer {n} espace(s)","import.manual":"Commencer de zéro","import.progress":"Étage {i} sur {n}","import.done":"Espaces créés. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","btn.skip":"Passer","space.scale_label":"Échelle (taille d’une cellule de grille)","space.scale_unit":"cm par cellule","space.scale_unit_imperial":"po par cellule","space.display_section":"Affichage","space.show_borders":"Toujours afficher les contours des pièces","space.zero_wall_style":"Murs d’épaisseur nulle","space.zero_wall_dashed":"Pointillés","space.zero_wall_solid":"Plein","space.zero_wall_help":"Les murs en pointillés laissent passer la lumière. Les murs pleins bloquent la lumière même avec une épaisseur nulle.","space.show_names":"Afficher les noms des pièces (faire glisser pour déplacer)","space.room_color":"Couleur du contour et du nom","space.opacity":"Opacité","space.fill_label":"Remplissage de la pièce","fill.none":"Aucun","fill.lqi":"Signal Zigbee","fill.light":"Lumières","fill.custom":"Couleur personnalisée","space.custom_fill":"Couleur de remplissage","space.source_file":"J’ai une image de plan","space.source_draw":"Aucune image — je tracerai les pièces à la main","fill.temp":"Température","tip.temp_avg":"température moyenne :","tip.hum_avg":"humidité moyenne :","space_card.button":"Ouvrir le plan de l’espace","space_card.not_found":"Espace « {id} » introuvable","space_card.loading":"Chargement…","continuity.restore_plan":"Restauration du plan…","continuity.restore_connection":"Restauration de la connexion aux appareils…","continuity.retry":"Réessayer","editor.space":"Espace","editor.framing":"Cadrage","editor.fit_content":"Tout le contenu visible","editor.fit_house":"Ajusté à la géométrie de la maison","editor.show_button":"Afficher le bouton","editor.button_label":"Libellé du bouton","editor.button_target":"Chemin du tableau de bord cible","marker.sub_entity":"entité","title.general_settings":"Paramètres généraux","gs.title":"Paramètres généraux","gs.hint":"Les couleurs de remplissage s’appliquent à tous les espaces ; chaque couleur possède sa propre opacité. Le mode de remplissage utilisé par un espace se règle dans la boîte de dialogue de cet espace.","gs.light_group":"Remplissage : lumières","gs.light_on":"Lumières allumées","gs.light_off":"Toutes les lumières éteintes","gs.temp_group":"Remplissage : température","gs.temp_cold":"Froid","gs.temp_ok":"Confortable","gs.temp_hot":"Chaud","gs.lqi_group":"Remplissage : signal Zigbee","gs.lqi_low":"Signal faible","gs.lqi_high":"Signal fort","gs.reset":"Réinitialiser par défaut","gs.saved":"Paramètres généraux enregistrés","space.show_lqi":"Afficher le signal Zigbee (LQI) à côté des appareils","space.hide_decor":"Masquer la couche décorative","space.hide_decor_tip":"Les lignes, formes, étiquettes et meubles restent à leur place — visibles dans l’éditeur d’arrière-plan, mais pas sur le plan.","space.hide_openings":"Masquer les ouvertures","space.hide_openings_tip":"Les symboles de portes, fenêtres et portails ne sont pas dessinés, mais les ouvertures continuent de fonctionner : la lumière les traverse, le soleil entre par les fenêtres et les capteurs de contact continuent de les ouvrir. L’éditeur de plan les affiche toujours.","gs.light_none":"Aucune source lumineuse","mode.plan":"Éditeur de plan","mode.devices":"Éditeur d’appareils","display.value":"Valeur + état","marker.subarea":"aucune zone, manuel","device.new":"Nouvel appareil — ouvrez son éditeur pour fermer","opening.unlock_action":"Déverrouiller","opening.lock_action":"Verrouiller","opening.lock_pending":"En cours…","title.close_editor":"Fermer l’éditeur (retour à la vue)","title.add_device":"Ajouter un appareil au plan","devbar.add":"Ajouter","devbar.rules":"Règles d’icônes","device_inbox.button":"Appareils","device_inbox.title":"Appareils sur le plan","device_inbox.search":"Rechercher appareils, entités et intégrations…","device_inbox.add_virtual":"Ajouter un appareil virtuel","device_inbox.tab_on_plan":"Sur le plan","device_inbox.tab_available":"Disponibles","device_inbox.tab_hidden":"Masqués","device_inbox.tab_readd":"De nouveau disponibles","device_inbox.only_new":"Nouveaux uniquement","device_inbox.show_entities":"Afficher les entités","device_inbox.show_hidden":"Afficher les masqués sur le plan","device_inbox.new":"Nouveau","device_inbox.find":"Trouver sur le plan","device_inbox.edit":"Modifier","device_inbox.hide":"Masquer","device_inbox.show":"Afficher","device_inbox.add":"Ajouter","device_inbox.readd":"Ajouter à nouveau","device_inbox.hide_available":"Masquer de la liste","device_inbox.show_more":"Afficher plus","device_inbox.more_actions":"Plus d'actions","device_inbox.find_hidden_hint":"Activez d'abord « Afficher les masqués sur le plan »","device_inbox.show_disabled":"Activez cette liaison dans Home Assistant avant de l'afficher","device_inbox.empty_on_plan":"Aucun appareil correspondant n'est encore sur le plan.","device_inbox.empty_available":"Aucun appareil disponible à ajouter.","device_inbox.empty_hidden":"Il n'y a aucun appareil masqué.","device_inbox.empty_readd":"Aucun appareil supprimé n'est disponible pour un nouvel ajout.","device_inbox.reason_visible_auto":"Découvert automatiquement","device_inbox.reason_visible_explicit":"Ajouté explicitement","device_inbox.reason_manual_hidden":"Masqué par l'utilisateur","device_inbox.reason_automatic_hidden":"Masqué automatiquement","device_inbox.reason_service_entry":"Entrée de registre de service uniquement","device_inbox.reason_excluded_integration":"Intégration exclue par les filtres d'appareils","device_inbox.reason_excluded_domain":"Type d'entité non spatial","device_inbox.reason_grouped_light":"Représenté par un groupe de lumières de la pièce","device_inbox.reason_represented_by_parent":"Représenté par son appareil parent","device_inbox.reason_removed":"Précédemment retiré du plan","device_inbox.reason_available":"Disponible pour ajout au plan","device_inbox.reason_no_bound_room":"La zone HA n'est liée à aucune pièce du plan","device_inbox.status_ha_disabled":"Désactivé dans Home Assistant","device_inbox.status_orphaned":"La liaison n'existe plus dans Home Assistant","device_inbox.status_unverified":"Le registre Home Assistant est temporairement indisponible","device_inbox.saved":"Liste des appareils mise à jour","space.roomcard_section":"La carte de pièce affiche :","space.label_temp":"Température","space.label_hum":"Humidité","space.label_lqi":"Signal Zigbee moyen","space.label_light":"Lumières allumées/éteintes","roomcard.light_on":"Allumé","roomcard.light_off":"Éteint","roomcard.light_partial":"{on} sur {total}","toast.split_pick_inside":"Les points intermédiaires de découpe doivent se trouver à l’intérieur de la pièce","mode.decor":"Éditeur d’arrière-plan","decor.select":"Sélectionner","decor.line":"Ligne","decor.rect":"Rectangle","decor.ellipse":"Ovale","decor.text":"Texte","decor.erase":"Effacer","decor.erase_confirm_title":"Effacer l’objet ?","decor.color":"Couleur","decor.width":"Épaisseur de ligne","decor.line_style":"Style de ligne","decor.line_style_solid":"Plein","decor.line_style_dashed":"Pointillés","decor.fill":"Remplissage","decor.fill_color":"Couleur de remplissage","decor.length":"Longueur","decor.size":"Taille","decor.angle":"Angle de rotation, °","decor.text_size":"Taille du texte","decor.backdrop_properties":"Propriétés de l’arrière-plan du plan","decor.text_title":"Étiquette de texte","decor.object_title":"Modifier {kind}","decor.text_label":"Texte","decor.live_group":"Insérer une variable HA","decor.live_entity":"Entité","decor.live_entity_ph":"choisir une entité","decor.live_attr":"Valeur","decor.live_attr_ph":"choisir l’état ou un attribut","decor.live_state":"État","decor.backdrop":"Image d’arrière-plan","decor.backdrop_hint":"Faites glisser pour déplacer ; tirez un coin pour redimensionner ; utilisez la poignée supérieure pour faire pivoter. Maj modifie les proportions ou libère l’angle.","decor.backdrop_reset":"Réinitialiser l’image","decor.backdrop_reset_done":"L’image a retrouvé sa position et sa taille d’origine","marker.icon_auto":"Auto : {icon} (selon les règles d’icônes ; choisissez-en une pour remplacer)","marker.icon_pin_auto":"Épingle","mode.plan_tip":"Éditeur de plan — la géométrie du logement : dessiner et diviser/fusionner les pièces, les associer aux zones HA, placer portes, fenêtres et portails, déplacer les cartes de pièces, régler l’échelle","mode.devices_tip":"Éditeur d’appareils — tout ce qui concerne les icônes : faire glisser pour positionner, cliquer pour modifier l’association/l’icône/l’affichage, ajouter des appareils virtuels, règles d’icônes","mode.decor_tip":"Éditeur d’arrière-plan — décoration purement visuelle sous le plan : lignes, rectangles, ovales et étiquettes de texte qui ne réagissent jamais aux clics","space.glow_enabled":"Halo des sources lumineuses","gs.glow_group":"Halo des sources lumineuses","gs.glow_base":"Obscurité de la maison","gs.glow_light":"Couleur / intensité de lumière par défaut","gs.wall_group":"Murs","gs.wall_fill":"Remplissage des murs","gs.glow_radius":"Rayon du halo","gs.unit_m":"m","gs.unit_ft":"pi","marker.controls_label":"Contrôle d’autres sources lumineuses","marker.controls_hint":"Avec l’action au toucher « Basculer », un clic inverse l’état de chaque source ajoutée ici. L’entité propre associée au marqueur est contrôlée directement et ne peut pas être ajoutée ; utilisez l’interrupteur ci-dessous pour la classer comme lumière.","marker.controls_filter":"Rechercher des lumières et interrupteurs…","info.controls":"Contrôle","marker.glow_radius_label":"Rayon du halo","marker.glow_radius.help":"Définit le rayon du halo en mètres ou en pieds ; une valeur vide utilise le rayon des paramètres généraux.","marker.glow_radius.help.aria":"Aide : rayon du halo","markup.wallthick":"Épaisseur","markup.select":"Sélectionner","title.markup_wallthick":"Épaisseur — cliquez sur un mur pour définir son épaisseur de 0 à 100 cm.","markup.hint_wallthick":"cliquez sur un mur · Échap ferme sans appliquer","wallthick.field":"Épaisseur","wallthick.unit_cm":"cm","wallthick.unit_in":"po","wallthick.apply_room":"Appliquer à tous les murs de cette pièce","markup.draw_wall_title":"Épaisseur de chaque nouveau segment de mur (0–100 cm). Les murs partagés conservent la valeur existante.","room.queue_progress":"Pièce {current} sur {total}","toast.wall_rooms_saved":"Pièces créées : {n}","toast.wall_chain_saved":"Chaîne de murs enregistrée","toast.wallthick_pick":"Cliquez sur un mur","toast.wallthick_set":"Épaisseur du mur définie","toast.wallthick_cleared":"Épaisseur du mur supprimée","toast.physical_range":"Saisissez une valeur comprise entre {min} et {max} {unit}","toast.zero_wall_opening_conflict":"Supprimez d’abord l’ouverture sur ce segment de mur.","toast.zero_wall_ambiguous":"Le segment de mur est ambigu. Simplifiez ou ajustez la jonction.","toast.zero_wall_migration_blocked":"L’espace n’a pas été converti : {reason}. Aucune donnée n’a été modifiée.","toast.physical_angle":"Saisissez un angle de rotation compris entre 0° inclus et 90° exclu","toast.physical_limit":"L’espace a atteint la limite pour ce type de géométrie","toast.geometry_unsafe":"Modification annulée : la géométrie des murs n’a pas pu être construite en toute sécurité.","junction.limit_angle":"L'angle entre les murs est trop aigu : {actual}°, minimum {limit}°.","junction.limit_valence":"Trop de murs se rejoignent en un nœud : {actual}, maximum {limit}.","junction.limit_length":"Le segment de mur est trop court : {actual} cm, minimum {limit} cm.","junction.limit_distance":"Les murs et les nœuds sont trop proches : {actual} cm, minimum {limit} cm.","junction.limit_clearance":"Il ne reste plus d'intérieur de pièce : {actual} cm², minimum {limit} cm².","junction.limit_check_failed":"La vérification des jonctions n'a pas pu s'exécuter — la modification n'a pas été enregistrée","toast.wall_model_migration_blocked":"Le modèle de murs n’a pas pu être mis à jour : {reason}. Le plan n’a pas été modifié. Lancez « Optimiser les plans » ; si l’erreur se reproduit, corrigez la géométrie de mur conflictuelle.","toast.wall_model_client_outdated":"Mettez à jour la carte et rechargez la page avant de modifier le plan.","wall_model.reason.invalid-room":"contour de pièce invalide","wall_model.reason.zero-length":"segment de mur de longueur nulle","wall_model.reason.third-owner":"mur partagé par plus de deux pièces","wall_model.reason.duplicate-id":"identifiants de mur en conflit","wall_model.reason.thickness-conflict":"valeurs d’épaisseur de mur en conflit","wall_model.reason.opening-host":"l’ouverture ne peut pas être attribuée à un seul mur","toast.delete_room_pick":"Cliquez à l’intérieur de la pièce que vous souhaitez supprimer","toast.plan_snap_ambiguous":"Zoomez pour choisir le nœud du mur","toast.wall_repair_ambiguous":"Le contour de la pièce possède plusieurs connexions possibles. Zoomez et reliez-le explicitement.","toast.wall_repair_too_large":"L’écart dépasse 2 cm. Reliez les murs avant de créer la pièce.","toast.wall_repair_changed":"La géométrie des murs a changé. Essayez de créer de nouveau la pièce.","toast.opening_on_zero_wall":"Les ouvertures ne peuvent pas être placées sur un mur d’épaisseur nulle","marker.from_ha_option":"Choisir dans la liste HA","marker.show_entities":"Afficher les entités","marker.show_entities_tip":"Ajoute à la liste non seulement les appareils, mais aussi toutes leurs entités","marker.pick_ph":"Choisir un appareil…","room.open_area":"Ouvrir la zone HA","view.volumetric":"Vue volumétrique","view.flat":"Vue plane","kiosk.title":"Tailles pour cet écran","kiosk.hint":"Enregistrées uniquement sur cet appareil — chaque tablette murale ou TV peut avoir ses propres tailles confortables.","kiosk.icon_scale":"Taille des icônes d’appareils","kiosk.font_scale":"Taille du texte des cartes de pièces","editor.kiosk":"Mode appareil mural (kiosque)","editor.cycle":"Changer automatiquement d’espace toutes les N secondes (kiosque, 0 = désactivé)","room.settings_title":"Paramètres de la pièce","room.settings_section":"Paramètres de la pièce (remplacent ceux de l’espace)","room.fill_label":"Remplissage de CETTE pièce","fill.inherit":"Comme l’espace","room.custom_fill_space":"Couleur de l’espace","room.custom_fill_own":"Couleur de la pièce","room.temp_src_label":"Source de température","room.hum_src_label":"Source d’humidité","room.src_average":"Moyenne des capteurs de la pièce (par défaut)","room.src_pick":"Un appareil ou une entité HA spécifique","room.src_ph":"Choisir une source…","toast.room_updated":"Pièce mise à jour","space.card_font":"Taille de police des cartes de pièces (tout l’espace)","room.sizes_section":"Tailles de police","room.name_scale":"Taille du nom de la pièce","room.label_scale":"Taille des mesures","preview.room_name":"Salon","toast.cfg_reload_failed":"Impossible de recharger le plan depuis le serveur : {err}","toast.locale_load_failed":"Impossible de charger le pack de langue. L'anglais est utilisé jusqu'au rechargement de la page.","room.settings_short":"Paramètres de la pièce","room.unnamed":"Pièce sans nom","marker.use_climate_temp":"Inclure la température de l’appareil dans la pièce","marker.use_climate_temp_tip":"Ajoute la valeur current_temperature d’un climatiseur ou thermostat à la moyenne de la pièce. Configurez séparément le badge de valeur externe ci-dessous.","marker.value_badge_title":"Badge de valeur","marker.value_badge.help":"Affiche une valeur sélectionnée à côté de l’icône. Cela n’affecte pas les mesures de la pièce, la lumière, le halo ni l’action au toucher.","marker.value_badge.help.aria":"Aide : badge de valeur de l’appareil","marker.value_badge_enabled":"Afficher un badge de valeur","marker.value_badge_source":"Valeur","marker.value_badge_source.help":"Choisissez un état précis, un attribut pris en charge ou une valeur dérivée de cet appareil.","marker.value_badge_source.help.aria":"Aide : source du badge de valeur","marker.value_badge_position":"Position","marker.value_badge_position.help":"Le côté choisi reste fixe et ne bascule pas automatiquement au bord du plan.","marker.value_badge_position.help.aria":"Aide : position du badge de valeur","marker.value_badge_right":"À droite","marker.value_badge_bottom":"En dessous","marker.value_badge_left":"À gauche","marker.value_badge_top":"Au-dessus","marker.value_badge_empty":"Cet appareil n’a aucune valeur disponible","marker.value_badge_static":"Une icône statique n’affiche pas de valeurs en direct. Le paramètre est conservé.","marker.value_badge_missing":"Source indisponible","marker.value_badge_missing_hint":"La source enregistrée est actuellement indisponible. Conservez-la, désactivez le badge ou choisissez une autre valeur.","marker.value_badge_duplicate":"La même valeur est déjà affichée dans l’icône.","marker.value_badge_state":"État · {name}","marker.value_badge_marker_state":"État de la source lumineuse · {name}","marker.value_badge_lqi":"Qualité moyenne du signal Zigbee","marker.value_badge_attr_current_temperature":"Température actuelle · {name}","marker.value_badge_attr_temperature":"Température cible · {name}","marker.value_badge_attr_current_humidity":"Humidité actuelle · {name}","marker.value_badge_attr_humidity":"Humidité · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Vitesse · {name}","marker.value_badge_attr_brightness":"Luminosité · {name}","marker.value_badge_attr_volume_level":"Volume · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Vitesse du ventilateur · {name}","marker.light_role_label":"Cet appareil est-il une source lumineuse ?","marker.light_role.help":"Détermine si ce marqueur est lui-même une source lumineuse spatiale. Les lumières liées ci-dessus restent des contrôles de lumière de pièce indépendants.","marker.light_role.help.aria":"Aide : cet appareil est-il une source lumineuse","marker.light_role_auto_yes":"Auto (source lumineuse)","marker.light_role_auto_no":"Auto (pas une source lumineuse)","marker.light_role_always":"Toujours une source lumineuse","marker.light_role_never":"Jamais une source lumineuse","marker.light_entity_label":"Entité lumineuse principale","marker.light_entity.help":"Pour un appareil composite, choisissez l’entité dont l’état, la couleur et la luminosité représentent cette source sur le plan. Le choix automatique reste compatible avec les anciens plans.","marker.light_entity.help.aria":"Aide : entité lumineuse principale","marker.light_entity_auto":"Automatique ({entity})","marker.light_entity_none":"aucune entité contrôlable","marker.light_entity_missing":"L’entité enregistrée {entity} est indisponible. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.toggle_entity_label":"Entité à basculer","marker.toggle_entity.help":"Pour un appareil composite, choisissez précisément la lumière ou l’interrupteur propre à actionner par un toucher. Automatique conserve les règles de cible précédentes et reste indépendant de l’entité lumineuse principale.","marker.toggle_entity.help.aria":"Aide : entité à basculer","marker.toggle_entity_auto":"Automatique ({entity})","marker.toggle_entity_none":"aucune entité propre contrôlable","marker.toggle_entity_missing":"L’entité enregistrée {entity} ne fait plus partie des canaux sélectionnables de ce marqueur. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.glow_color_label":"Couleur et luminosité du halo","marker.glow_mode.help":"Utilisez les valeurs en direct de la source, remplacez uniquement sa couleur ou fixez à la fois la couleur et la luminosité. Le minimum est 1 % ; pour désactiver la source, choisissez « Jamais ».","marker.glow_mode.help.aria":"Aide : couleur et luminosité du halo","marker.glow_mode_auto":"Depuis la source","marker.glow_mode_color":"Définir la couleur","marker.glow_mode_fixed":"Définir la couleur et la luminosité","marker.glow_color":"Couleur du halo","marker.glow_brightness":"Luminosité","marker.glow_disabled_never":"Les paramètres du halo sont indisponibles car ce marqueur est explicitement défini comme n’étant pas une source lumineuse.","marker.glow_disabled_auto":"Le mode Auto n’a trouvé aucune source lumineuse spatiale pour ce marqueur.","marker.glow_disabled_no_entity":"Aucune entité Home Assistant contrôlable et active n’est disponible pour cette source spatiale.","marker.glow_passive_hint":"Cette source ne possède aucune donnée Home Assistant propre. Définissez manuellement sa couleur et sa luminosité ; son rayon reste disponible.","marker.control_broken":"La source enregistrée est absente ou n’est plus marquée comme lumière","marker.control_missing_label":"Lumière du plan manquante","marker.control_passive":"source passive","toast.marker_control_cycle":"Ce lien créerait une chaîne circulaire de contrôles de lumière.","toast.marker_binding_required":"Choisissez un appareil Home Assistant avant de lier une autre source lumineuse.","confirm.unlock":"Déverrouiller « {name} » ?","toast.files_migrate_failed":"Les pièces jointes n’ont pas pu être déplacées vers la nouvelle association ; les liens continuent de pointer vers les anciens fichiers : {err}","space.pick_saved":"Déjà importé","space.pick_saved_hint":"Plans stockés sur le serveur, y compris ceux que vous avez précédemment dissociés","space.no_saved":"Aucun plan n’est encore stocké sur le serveur.","space.loading":"Chargement…","space.used_by":"utilisé par : {list}","space.in_use":"Un espace utilise encore ce plan — dissociez-le d’abord","btn.use":"Utiliser","confirm.delete_plan":"Supprimer le fichier de plan « {name} » du serveur ? Cette action est irréversible.","toast.plans_list_failed":"Impossible de lister les plans stockés : {err}","toast.plan_delete_failed":"Impossible de supprimer le plan : {err}","marker.hide":"Masquer","marker.hide_tip":"L’appareil disparaîtra du plan après l’enregistrement mais continuera de compter dans le signal de la pièce. Restaurez-le via « Masqués et désactivés » dans l’éditeur d’appareils.","marker.show":"Afficher","marker.show_tip":"L’appareil apparaîtra de nouveau sur le plan après l’enregistrement.","marker.hidden_ghost":"Appareil masqué par l’utilisateur","marker.ha_disabled_device":"L’appareil est désactivé dans Home Assistant et masqué du plan.","marker.ha_disabled_entity":"L’entité est désactivée dans Home Assistant et masquée du plan.","marker.ha_disabled_all_entities":"L’appareil n’a aucune entité Home Assistant active ; il est donc masqué du plan.","marker.ha_registry_limited":"Le registre Home Assistant complet n’est pas accessible à cet utilisateur. L’objet non vérifié ne peut pas être affiché ni utilisé pour le moment.","marker.delete_tip":"Supprimer complètement l’appareil du plan et de tous les agrégats. Vous pourrez l’ajouter de nouveau plus tard.","tap.run":"Exécuter une automatisation/un script/une scène","marker.run_target_label":"Élément à exécuter","marker.run_search_ph":"Rechercher : automatisation, script ou scène…","marker.run_target_gone":"Cible {id} introuvable — choisissez-en une autre","marker.tap_confirm":"Demander une confirmation","marker.tap_confirm_tip":"Afficher une boîte de dialogue de confirmation avant l’action — protection contre les appuis accidentels.","marker.toggle_hint_single":"Cible : {name} ({id}).","marker.virtual_light_target":"Lumière virtuelle : {name}.","marker.virtual_light_current":"État manuel : {state} → {effect}.","marker.virtual_light_state_on":"allumé","marker.virtual_light_state_off":"éteint","marker.toggle_hint_group":"Basculera {count} source(s) : {names}.","marker.toggle_hint_current":"Actuellement : {state} → {effect}.","marker.toggle_hint_group_current":"Actuellement allumées : {on} sur {count} → {effect}.","marker.toggle_hint_skipped":"Seront ignorées ({count}) : {targets}.","marker.toggle_effect_turn_on":"sera allumé","marker.toggle_effect_turn_off":"sera éteint","marker.toggle_effect_open":"sera ouvert","marker.toggle_effect_close":"sera fermé","marker.toggle_effect_stop":"sera arrêté","marker.toggle_effect_toggle":"l’état sera basculé","marker.toggle_skip_missing":"introuvable","marker.toggle_skip_ha_disabled":"désactivé dans HA","marker.toggle_skip_unavailable":"indisponible","marker.toggle_skip_unsupported":"basculement non pris en charge","marker.toggle_skip_secure":"bloqué pour des raisons de sécurité","marker.toggle_none_no_binding":"Cet appareil n’a aucune association ; il n’y a donc aucun état à basculer. Un toucher ne fera rien.","marker.toggle_none_no_actionable_entity":"Cet appareil n’a aucun état pouvant être basculé. Un toucher ne fera rien.","marker.toggle_none_configured_targets_missing":"Les cibles configurées sont indisponibles. L’entité propre de l’appareil ne leur sera pas substituée.","marker.toggle_none_ha_disabled":"La cible est désactivée dans Home Assistant et ne peut pas être utilisée sur le plan.","marker.toggle_none_unavailable":"La cible est actuellement indisponible. Un toucher ne fera rien.","marker.toggle_none_unsupported":"Home Assistant n’expose aucune opération de basculement sûre pour cette entité. Un toucher ne fera rien.","marker.toggle_none_secure":"Le basculement des serrures, alarmes et portails sécurisés depuis le plan est bloqué pour des raisons de sécurité.","run.automation":"automatisation","run.script":"script","run.scene":"scène","confirm.tap_run":"Exécuter « {name} » ?","confirm.tap_toggle":"Basculer « {name} » ?","confirm.current_state":"État actuel : {state}","confirm.expected_state":"Après basculement : {state}","confirm.group_current":"{on} allumé(s) sur {total}","confirm.group_all_on":"tout est allumé","confirm.group_all_off":"tout est éteint","confirm.unavailable_targets":"Indisponibles : {count}","confirm.expected_by_ha":"Home Assistant déterminera l’état","confirm.state_on":"Allumé","confirm.state_off":"Éteint","confirm.state_open":"Ouvert","confirm.state_closed":"Fermé","confirm.state_opening":"Ouverture en cours","confirm.state_closing":"Fermeture en cours","confirm.state_stopped":"Arrêté","confirm.state_unknown":"Inconnu","toast.run_started":"Démarré : {name}","toast.run_target_missing":"Cible d’exécution introuvable — vérifiez les paramètres de l’appareil","toast.run_target_required":"Choisissez une automatisation, un script ou une scène","toast.tap_target_changed":"La cible de l’action a changé. Réessayez.","toast.virtual_light_toggle_failed":"Impossible de basculer la lumière virtuelle : {err}","toast.value_badge_source_required":"Choisissez une valeur pour le badge","btn.run":"Exécuter","vac.section":"Robot aspirateur : position en direct","vac.autocal":"Configurer automatiquement","vac.live":"Position en direct sur le plan","vac.trail":"Afficher le trajet du robot","vac.cal_maps":"Cartes calibrées : {maps}","vac.autocal_no_rooms":"L’intégration ne fournit aucune liste de pièces — ouvrez « Ajuster manuellement »","vac.autocal_no_match":"Les noms de pièces ne correspondent pas (il en faut ≥3 en commun) — ouvrez « Ajuster manuellement »","vac.autocal_done":"Terminé : association via {rooms} pièces. Lancez un nettoyage et vérifiez","vac.cal_need_pos":"Le robot ne transmet pas de coordonnées — lancez un nettoyage puis mettez-le en pause","vac.cal_done":"Calibration enregistrée. Lancez un nettoyage et vérifiez","vac.cal_cancelled":"Calibration annulée","vac.fit":"Ajuster manuellement","vac.fit_hint":"Faites glisser la carte du robot pour la positionner, étirez-la par les coins","vac.fit_rotate":"Rotation de 90°","vac.fit_mirror":"Miroir","vac.trail_never":"Jamais","vac.trail_cleaning":"Pendant le nettoyage","vac.trail_always":"Toujours","gs.bg_group":"Arrière-plan de la scène","gs.bg_color":"Arrière-plan autour du plan","gs.bg_default":"Valeur par défaut du thème","gs.bg_theme":"valeur par défaut du thème","gs.bg_mode":"Arrière-plan du plan","gs.bg_static":"Couleur statique","gs.bg_daynight":"Suit le soleil","gs.bg_daynight_hint":"L’arrière-plan évolue entre l’aube, le jour, le crépuscule et la nuit en utilisant sun.sun, ou votre heure locale si les données du soleil sont indisponibles. Aucune orientation du nord n’est nécessaire pour l’arrière-plan.","gs.sun_group":"Soleil","gs.sun_missing":"L’entité sun.sun est introuvable — l’arrière-plan suit votre heure locale ; les rayons de soleil par les fenêtres sont indisponibles.","gs.north":"Nord sur le plan","gs.north_ph":"non défini","gs.north_hint":"Définissez le nord uniquement pour les rayons par les fenêtres (pas de 1°, 15° avec Maj) ; l’arrière-plan Soleil fonctionne sans lui.","gs.north_clear":"Effacer","gs.north_letter":"N","gs.sun_rays":"Lumière du soleil à travers les fenêtres","gs.about_group":"À propos","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · documentation et problèmes","gs.about_telegram":"Discussion Telegram","space.bg_color":"Arrière-plan autour du plan","space.bg_inherit":"Hériter des paramètres généraux","space.bg_inherited":"hérite des paramètres généraux","space.bg_mode":"Arrière-plan du plan","space.north":"Nord sur le plan (remplacement)","space.north_inherited":"hérité : {v}","space.sun_rays":"Lumière du soleil à travers les fenêtres","space.sun_inherit":"Hériter des paramètres généraux","space.sun_on":"Activé","space.sun_off":"Désactivé","canvas.far_objects":"{n} objet(s) éloigné(s) du plan","canvas.show_far":"Afficher","canvas.home_tip":"Le plan est là-bas — cliquez pour l’ajuster à l’écran","gs.grid_group":"Maintenance du plan","gs.grid_hint":"Met à jour les modèles de données, aligne les éléments du plan sur la grille et fusionne les fragments de murs redondants. Un rapport précis est affiché avant tout enregistrement.","gs.align_all":"Optimiser les plans","gs.align_title":"Optimiser les plans","gs.align_none":"Tous les plans utilisent déjà le modèle de données optimisé actuel.","gs.optimize_no_automatic_changes":"Aucune modification automatique n’est à appliquer. Vérifiez les éléments ci-dessous.","gs.align_count":"{n} élément(s) sur {total} seront déplacés, de {cm} cm au maximum.","gs.align_where":"Le plus grand déplacement se trouve dans « {s} ».","gs.align_turned":"Ouvertures dont l’angle est corrigé : {n}.","gs.align_removed_drafts":"Contours invalides réduits par la grille et supprimés : {n}.","gs.optimize_redundant_drafts":"Chaînes de murs enregistrées masquées par des murs de pièces pleins et supprimées : {n}.","gs.align_preflight_failed":"Impossible de vérifier en toute sécurité la géométrie des espaces suivants : {spaces}{more}.","gs.align_preflight_hint":"Les plans n’ont pas été modifiés. Copiez les diagnostics avec le bouton ci-dessous et joignez-les au rapport de bug avec un export de l’espace.","gs.preflight_reason_prepare-exception":"Impossible de préparer la géométrie de l’espace (exception lors de la construction du modèle)","gs.preflight_reason_wall-null":"Le corps du mur n’a pas pu être construit (l’union a renvoyé un résultat vide)","gs.preflight_reason_wall-degraded-extra":"Le corps du mur s’est dégradé avec une géométrie supplémentaire","gs.preflight_reason_wall-failed-core":"Le noyau du corps du mur n’a pas pu être assemblé","gs.preflight_reason_wall-exception":"La construction du mur a déclenché une exception","gs.preflight_reason_floor-null":"Le contour du sol n’a pas pu être construit","gs.preflight_reason_floor-exception":"La construction du sol a déclenché une exception","gs.preflight_copy":"Copier les diagnostics","gs.preflight_copied":"Diagnostics copiés","gs.preflight_update_hint":"Les versions de la carte et de l’intégration diffèrent — mettez House Plan à jour puis réessayez.","gs.align_preflight_space":"Espace {n}","gs.align_preflight_more":", et {n} de plus","gs.optimize_changes":"Migrations de modèle : {m} ; espaces mis à jour : {c} ; valeurs de coordonnées parasites supprimées : {p} ; fragments de murs réels fusionnés : {w} ; fragments de murs d’épaisseur nulle fusionnés : {s} ; murs indépendants : {i}.","gs.zero_walls_migrated":"Portions de murs virtuels converties : {n}.","gs.wall_segments_migrated":"Segments de murs stabilisés : {n}.","gs.optimize_lattice_summary":"Valeurs de coordonnées parasites normalisées : {n} ; déplacement maximal : {cm} cm.","gs.optimize_lattice_space":"{space} : valeurs de coordonnées normalisées : {n} ; valeurs hors grille laissées inchangées : {far}.","gs.optimize_coincident_partitions":"Sections de murs indépendants masquées absorbées dans les murs des pièces : {n}.","gs.optimize_openings_rehosted":"Ouvertures rattachées aux murs des pièces : {n}.","gs.optimize_walls_straightened":"Murs redressés : {n} ; déplacement maximal : {cm} cm.","gs.optimize_walls_straightened_where":"Plus grande correction de mur : {s}.","gs.optimize_walls_straighten_skipped":"Murs proches d’un axe laissés inchangés car ils ne pouvaient pas être réparés en toute sécurité : {n}.","gs.optimize_glow_migration":"Ancien mode Glow : {spaces} espaces → aucun remplissage de données + Glow indépendant ; {rooms} pièces → remplissage de données hérité + Glow indépendant.","gs.optimize_references":"Références réparées : espaces — {spaces} ; pièces — {rooms} ; positions — {positions} ; appareils dissociés des espaces manquants — {detached}.","gs.optimize_reference_more":", et {n} de plus","gs.optimize_orphans_removed":"Enregistrements oubliés supprimés : {total} — étiquettes de pièces : {rooms} ; appareils : {devices} ; marqueurs de groupe : {groups}. Ils appartenaient à des espaces supprimés précédemment.","gs.optimize_live_positions":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles seront conservées.","gs.optimize_live_positions_remove":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles sont sélectionnées pour suppression.","gs.optimize_live_names":" : {names}{more}","gs.optimize_live_remove":"Supprimer les anciennes positions","gs.optimize_live_keep":"Conserver les anciennes positions","gs.optimize_live_selected":"Les anciennes positions seront supprimées après l’application de l’optimisation.","gs.optimize_unverified":"Impossible de vérifier en toute sécurité les positions : {n}. Elles ont été laissées inchangées.","gs.optimize_registry_limited":"Un accès administrateur complet aux registres Home Assistant est requis pour une vérification sûre.","gs.optimize_vacuum_warning":"Associations de pièces d’aspirateur nécessitant encore une vérification : {n}.","gs.optimize_details":"Détails","gs.optimize_details_more":"Et {n} enregistrement(s) de plus.","gs.optimize_detail_removed":"sera supprimé","gs.optimize_detail_live":"sera conservé","gs.optimize_detail_unverified":"non vérifié","gs.optimize_detail_room_label":"étiquette de pièce","gs.optimize_detail_device":"appareil","gs.optimize_detail_group":"marqueur de groupe","gs.optimize_detail_unknown":"propriétaire inconnu","gs.optimize_detail_item":"{status} : {kind} {id} ; ancien espace {space}","gs.align_warn":"Les éléments volontairement placés entre les nœuds de la grille seront déplacés. Une seule annulation est disponible après l’opération, uniquement jusqu’à la prochaine modification du plan.","gs.align_run":"Optimiser","gs.align_done":"Plans optimisés : {n} élément(s) déplacé(s), {m} enregistrement(s) maintenu(s), {r} référence(s) réparée(s)","gs.optimize_undo":"Annuler la dernière optimisation","gs.optimize_undone":"La dernière optimisation a été annulée","decor.furniture":"Mobilier","furn.title":"Bibliothèque de mobilier","furn.symbol":"Symbole","furn.group_furniture":"Mobilier","furn.group_appliance":"Électroménager","furn.group_sanitary":"Sanitaires","furn.group_other":"Autre","furn.width":"Largeur","furn.depth":"Profondeur","furn.back_to_categories":"Toutes les catégories","furn.pick_hint":"Choisissez un symbole, puis cliquez sur le plan.","furn.place_hint":"Cliquez sur le plan — le meuble se place contre le mur le plus proche. Maj permet un placement libre.","furn.cat_air_conditioner":"Climatiseurs","furn.cat_armchair":"Fauteuils","furn.cat_bathtub":"Baignoires","furn.cat_bed":"Lits","furn.cat_bidet":"Bidets","furn.cat_boiler":"Chauffe-eau","furn.cat_chair":"Chaises","furn.cat_coffee_table":"Tables basses","furn.cat_cooktop":"Plaques de cuisson","furn.cat_dining_table":"Tables à manger","furn.cat_dishwasher":"Lave-vaisselle","furn.cat_dryer":"Sèche-linge","furn.cat_fireplace":"Cheminées","furn.cat_fridge":"Réfrigérateurs","furn.cat_kitchen_cabinet":"Meubles de cuisine","furn.cat_kitchen_sink":"Éviers de cuisine","furn.cat_nightstand":"Meubles","furn.cat_plant":"Plantes","furn.cat_rug":"Tapis","furn.cat_shelving":"Étagères","furn.cat_shower":"Douches","furn.cat_sink":"Lavabos","furn.cat_sofa":"Canapés","furn.cat_stairs":"Escaliers","furn.cat_toilet":"Toilettes","furn.cat_tv":"Téléviseurs","furn.cat_wardrobe":"Armoires","furn.cat_washer":"Lave-linge","furn.cat_work_table":"Bureaux","furn.sym_sofa":"Canapé","furn.sym_sofa_three_seat":"Canapé trois places","furn.sym_sofa_corner_right":"Canapé d'angle droit","furn.sym_armchair":"Fauteuil","furn.sym_armchair_office":"Chaise de bureau","furn.sym_coffee_table":"Table basse","furn.sym_coffee_table_round":"Table basse ronde","furn.sym_coffee_table_oval":"Table basse ovale","furn.sym_coffee_table_rounded":"Table basse arrondie","furn.sym_table_dining":"Table à manger","furn.sym_table_round":"Table ronde","furn.sym_table_dining_oval":"Table à manger ovale","furn.sym_table_dining_rounded":"Table à manger arrondie","furn.sym_chair":"Chaise","furn.sym_chair_bar":"Tabouret de bar","furn.sym_desk":"Bureau","furn.sym_desk_corner":"Bureau d'angle","furn.sym_bed_double":"Lit double","furn.sym_bed_single":"Lit simple","furn.sym_nightstand":"Table de chevet","furn.sym_cabinet_tv":"Meuble TV","furn.sym_cabinet_shoe":"Meuble à chaussures","furn.sym_cabinet_sink":"Meuble sous lavabo","furn.sym_wardrobe":"Armoire","furn.sym_bookshelf":"Bibliothèque","furn.sym_wall_unit":"Élément mural","furn.sym_kitchen_floor":"Module de cuisine bas","furn.sym_kitchen_floor_corner":"Module de cuisine bas d'angle","furn.sym_kitchen_wall":"Module de cuisine haut","furn.sym_kitchen_wall_corner":"Module de cuisine haut d'angle","furn.sym_shelf_floor":"Étagère au sol","furn.sym_shelf_wall":"Étagère murale","furn.sym_fridge":"Réfrigérateur","furn.sym_stove":"Cuisinière","furn.sym_cooktop_two":"Plaque à deux feux","furn.sym_dishwasher":"Lave-vaisselle","furn.sym_washer":"Lave-linge","furn.sym_dryer":"Sèche-linge","furn.sym_tv":"Téléviseur","furn.sym_tv_wall":"Téléviseur mural","furn.sym_ac":"Climatiseur","furn.sym_water_heater":"Chauffe-eau","furn.sym_toilet":"Toilettes","furn.sym_toilet_built_in":"Toilettes suspendues","furn.sym_bathtub":"Baignoire","furn.sym_bathtub_corner":"Baignoire d'angle","furn.sym_shower":"Douche","furn.sym_sink":"Lavabo","furn.sym_kitchen_sink":"Évier de cuisine","furn.sym_kitchen_sink_double":"Évier de cuisine double","furn.sym_bidet":"Bidet","furn.sym_bidet_built_in":"Bidet suspendu","furn.sym_stairs":"Escalier","furn.sym_fireplace":"Cheminée","furn.sym_plant":"Plante","furn.sym_rug":"Tapis","common.yes":"Oui","common.no":"Non","vac.diag_source":"Source","vac.diag_platform":"Intégration","vac.diag_status":"État","vac.diag_position":"Position","vac.diag_rooms":"Pièces","vac.diag_rooms_value":"{total} · {matched} noms correspondent · {readiness}","vac.autocal_ready":"calibration automatique disponible","vac.autocal_not_ready":"3 noms correspondants requis","vac.diag_path":"Chemin de l’intégration","vac.diag_map":"ID de carte","vac.source_none":"non sélectionné","vac.source_status_ok":"Prêt","vac.source_status_missing":"Manquant","vac.source_status_disabled":"Désactivé dans Home Assistant","vac.source_status_unavailable":"Indisponible","vac.source_status_unverified":"Impossible à vérifier avec les autorisations actuelles","vac.source_status_unsupported":"Aucune donnée de position","vac.source_status_none":"Aucune source","vac.source_banner_missing":"La source enregistrée n’existe plus. Elle n’a pas été remplacée automatiquement ; choisissez une autre source ou restaurez-la dans Home Assistant.","vac.source_banner_disabled":"La source enregistrée est désactivée dans Home Assistant. Activez-la ou choisissez une autre source.","vac.source_banner_unverified":"Les autorisations Home Assistant actuelles ne permettent pas de vérifier cette source enregistrée. Elle reste épinglée et ne sera pas remplacée automatiquement.","vac.choose_source":"Choisir la source","vac.source_auto":"Automatique","vac.source_auto_hint":"Utiliser une entité compatible de cet appareil","vac.all_cameras":"Toutes les caméras","vac.all_cameras_warn":"Une caméra peut ne pas fournir de données du robot. Choisissez-en une uniquement s’il s’agit de la carte de votre aspirateur.","vac.all_cameras_empty":"Aucune autre entité caméra trouvée.","vac.platform_unknown":"intégration inconnue","vac.cap_position":"position","vac.cap_rooms_short":"pièces","vac.cap_path":"trajet","vac.cap_map":"ID de carte","vac.cap_none":"aucune donnée de robot détectée","vac.xcme_hint":"Activez ces attributs Xiaomi Cloud Map Extractor :","vac.documentation":"Documentation","vac.residual_title":"Vérifier la calibration automatique","vac.residual_message":"Les pièces correspondantes présentent un écart maximal de {error}. Appliquez cette calibration approximative, affinez-la manuellement ou annulez sans modifier la configuration enregistrée.","vac.apply_proposal":"Appliquer","gs.backup_group":"Sauvegarde et transfert","gs.backup_hint":"Téléchargez une sauvegarde JSON portable ou prévisualisez et importez une sauvegarde créée par House Plan.","backup.export_open":"Exporter","backup.import_open":"Importer","backup.export_title":"Exporter House Plan","backup.import_title":"Importer House Plan","backup.export_hint":"Choisissez si la sauvegarde doit contenir toute la configuration House Plan ou uniquement l’espace actuel.","backup.full":"Sauvegarde complète","backup.current_space":"Espace actuel","backup.current_space_title":"Espace actuel : {title}","backup.no_current_space":"Aucun espace actuel","backup.plan_only":"Plan uniquement","backup.plan_only_hint":"Conserver les pièces, murs, ouvertures, décorations et positions des étiquettes de pièces sans les appareils ni les associations Home Assistant.","backup.plan_only_preview":"Ce fichier contient uniquement le plan","backup.privacy_warning":"L’archive conserve les noms, identifiants Home Assistant et coordonnées exactes. Les plans internes et pièces jointes sont référencés, pas intégrés ; les états d’exécution et trajets d’aspirateur ne sont pas inclus.","backup.download":"Télécharger le JSON","backup.export_done":"Sauvegarde téléchargée","backup.reading":"Vérification de la sauvegarde…","backup.revalidated":"Le plan a changé depuis cet aperçu. Le résumé a été actualisé ; vérifiez-le et confirmez de nouveau.","backup.error.unauthorized":"Vous n’avez pas l’autorisation d’exporter ou d’importer ce plan.","backup.error.not_ready":"House Plan n’est pas encore prêt. Réessayez dans un instant.","backup.error.too_large":"La sauvegarde dépasse la limite de 8 Mio.","backup.error.invalid_json":"Le fichier sélectionné n’est pas un JSON valide.","backup.error.invalid_format":"Le fichier sélectionné n’est pas une sauvegarde House Plan.","backup.error.unsupported_export_version":"Ce format de sauvegarde n’est pas pris en charge par la version installée.","backup.error.future_model":"La sauvegarde a été créée avec un modèle de données House Plan plus récent.","backup.error.invalid_config":"La sauvegarde contient une configuration House Plan invalide.","backup.error.wall_model_migration_blocked":"Le modèle de murs de la sauvegarde n’a pas pu être mis à niveau en toute sécurité. Optimisez d’abord le plan source, puis exportez-le de nouveau.","backup.error.invalid_layout":"La sauvegarde contient des positions d’objets invalides.","backup.error.invalid_content":"La sauvegarde contient des références de contenu invalides ou incohérentes.","backup.error.space_not_found":"L’espace sélectionné n’existe plus.","backup.error.capacity_exceeded":"L’ajout de cette sauvegarde dépasserait les limites du plan.","backup.error.preview_expired":"L’aperçu a expiré. Sélectionnez de nouveau le fichier de sauvegarde.","backup.error.preview_owner_mismatch":"Cet aperçu appartient à un autre utilisateur Home Assistant.","backup.error.conflict":"Le plan a changé depuis l’aperçu. Vérifiez le résumé actualisé.","backup.error.content_confirmation_required":"Confirmez que le contenu local indisponible peut être dissocié.","backup.error.commit_failed":"La sauvegarde n’a pas pu être appliquée en toute sécurité. Le plan précédent a été restauré ou est en attente de récupération.","backup.error.missing_plan":"Un fichier de plan référencé a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.missing_content":"Une pièce jointe locale référencée a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.marker_control_missing":"Une lumière liée au plan est absente de la sauvegarde.","backup.error.marker_control_not_light":"Une cible liée au plan n’est plus marquée comme source lumineuse.","backup.error.marker_control_self":"Une source lumineuse ne peut pas se contrôler elle-même.","backup.error.marker_control_cycle":"La sauvegarde contient une chaîne circulaire de contrôles de lumière.","backup.error.duplicate_marker_control":"La sauvegarde contient un lien de lumière du plan en doublon.","backup.error.no_backup":"Aucun instantané d’importation ou d’optimisation n’est disponible à restaurer.","backup.same_source":"Créée sur cette instance Home Assistant","backup.foreign_source":"Créée sur une autre instance Home Assistant","backup.created":"Créée : {value}","backup.versions":"Carte {card} ; intégration {integration} ; modèle de données {model}","backup.count_spaces":"Espaces : {n}","backup.count_rooms":"Pièces : {n}","backup.count_walls":"Murs : {n}","backup.count_openings":"Ouvertures : {n}","backup.count_decor":"Objets décoratifs : {n}","backup.count_markers":"Appareils : {n}","backup.count_layout":"Positions : {n}","backup.bindings":"Associations — appareils : {device}, entités : {entity}, virtuels : {virtual} ; positions sans espace : {legacy}","backup.binding_status":"État des cibles — actives : {active}, désactivées : {disabled}, manquantes : {missing}","backup.missing_areas":"Zones absentes sur la cible : {areas}","backup.dropped_marker_links":"Liens de lumières du plan hors de cet espace transféré omis : {n}.","backup.repaired_target_refs":"Références existantes restaurées par cet import d’espace : {n}.","backup.preserved_unresolved_refs":"Références qui n’ont pas pu être restaurées sans ambiguïté conservées : {n}.","backup.preserved_unresolved_hint":"Aucune donnée n’a été devinée ni supprimée. Après l’importation, lancez « Optimiser les plans » pour examiner les références restantes.","backup.import_details":"Détails des références d’importation","backup.import_detail.incoming_remapped":"Références mises à jour dans la copie importée : {n}","backup.import_detail.target_repaired":"Références existantes restaurées : {n}","backup.import_detail.preserved_unresolved":"Références non résolues conservées : {n}","backup.import_detail.collisions":"Conflits de destination conservés en toute sécurité : {n}","backup.import_detail.dropped_links":"Liens entrants omis selon les règles de transfert : {n}","backup.import_detail.bounded_lineages":"Identifiants trop imbriqués maintenus dans les limites : {n}","backup.replace_warning":"Cela remplace la configuration et la disposition actuelles. Les fichiers importés ne sont jamais supprimés. Une seule annulation reste disponible jusqu’à la prochaine modification du plan.","backup.foreign_bookkeeping":"Les données de suivi des appareils connus/nouveaux propres à l’instance ne seront pas importées.","backup.final_name":"Nom du nouvel espace","backup.target_settings":"Les paramètres généraux de l’instance cible restent inchangés.","backup.duplicates":"Associations déjà présentes : {n}","backup.skip":"Ignorer les appareils en doublon","backup.virtual_copy":"Ajouter des copies virtuelles statiques sûres","backup.content":"Contenu référencé","backup.content_available":"disponible localement","backup.content_external":"lien externe","backup.content_detach_required":"sera dissocié","backup.confirm_detach":"Je comprends que les plans internes et pièces jointes indisponibles seront dissociés de la configuration importée.","backup.replace":"Remplacer","backup.add":"Ajouter un espace","backup.space_done":"Espace importé : {rooms} pièces, {markers} appareils, {refs} références existantes restaurées","backup.full_done":"Sauvegarde restaurée : {spaces} espaces, {rooms} pièces, {markers} appareils","backup.undo_import":"Annuler la dernière importation complète","backup.import_undone":"Importation complète annulée","backdrop.large_title":"Grande image","backdrop.large_body":"Cette image fait {w}×{h} (fichier de {fileMb} Mo) et nécessite environ {decodedMb} Mo de mémoire pour s'afficher. Sur tablette, cela peut faire planter la page.","backdrop.unknown_body":"Les dimensions de l'image n'ont pas pu être lues — le fichier est peut-être endommagé. Continuer peut faire planter la page sur tablette.","backdrop.use_downscaled":"Téléverser une copie réduite","backdrop.reducing":"Réduction…","backdrop.keep_original":"Conserver l'original","backdrop.too_large_title":"Image trop grande","backdrop.too_large_body":"Cette image dépasse ce que les navigateurs peuvent afficher ({w}×{h}, limite {limit} px par côté). Réduisez-la dans un éditeur de bureau puis téléversez-la à nouveau.","backdrop.downscale_failed":"Impossible de créer la copie réduite. Le plan actuel n'a pas été modifié."};const a="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";export{e as dictionary,a as fingerprint}; diff --git a/dist/houseplan-assets/fr-Cml_YfSz.js b/dist/houseplan-assets/fr-Cml_YfSz.js new file mode 100644 index 00000000..04753aab --- /dev/null +++ b/dist/houseplan-assets/fr-Cml_YfSz.js @@ -0,0 +1 @@ +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";var e={"editor.context_actions":"Actions : {object}","editor.tool_options":"Options de l’outil : {tool}","editor.palette":"Palette : {tool}","editor.open_group":"Groupe d’outils : {group}","editor.group_active":"{group} — actif : {item}","editor.disabled_action":"{action} est indisponible : {reason}","editor.loading":"Chargement de l'éditeur…","editor.loading_aria":"L'éditeur de plan est en cours de chargement","editor.load_failed":"Impossible de charger l'éditeur.","editor.refresh_advice":"Actualisez la page et réessayez.","editor.retry_advice":"Vérifiez votre connexion et appuyez à nouveau.","color_picker.title":"Sélecteur de couleur","color_picker.hue":"Teinte","color_picker.saturation":"Saturation","color_picker.value":"Luminosité","color_picker.hex":"Couleur hexadécimale","color_picker.invalid_hex":"Saisissez une couleur hexadécimale à 3 ou 6 chiffres","btn.properties":"Propriétés","btn.keep_as_walls":"Conserver comme murs","btn.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","btn.delete_room_with_walls":"Supprimer la pièce et les murs","title.markup_select":"Sélectionner et modifier les murs, colonnes et contours enregistrés","title.markup_column":"Colonne : cliquez sur un point de la grille pour placer une colonne carrée","markup.partition":"Cloison","markup.column":"Colonne","markup.hint_column":"cliquez sur un point de la grille pour placer une colonne carrée","history.draft_segment":"Ajouter un segment de brouillon de pièce","history.draft_merge":"Relier des contours de pièce inachevés","history.draft_segment_delete":"Supprimer un segment de brouillon de pièce","history.partition_add":"Ajouter une cloison","history.column_add":"Ajouter une colonne","history.physical_edit":"Modifier l’objet physique","history.physical_delete":"Supprimer l’objet physique","history.physical_move":"Déplacer l’objet physique","history.contour_to_partitions":"Convertir le contour en murs fermés","history.wall_chain_finish":"Terminer la chaîne de murs","history.wall_face_batch":"Créer des pièces à partir des murs","toast.column_duplicate":"Une colonne avec le même centre et la même dimension extérieure existe déjà","confirm.delete_draft":"Supprimer tout le contour de pièce inachevé ?","confirm.delete_draft_segment":"Supprimer ce segment de brouillon ? Le contour restant peut se scinder en deux.","physical.partition_properties":"Propriétés de la cloison","physical.column_properties":"Propriétés de la colonne","physical.draft_properties":"Propriétés du segment de brouillon","physical.shape":"Forme","physical.square":"Carré","physical.circle":"Cercle","physical.diameter":"Diamètre","physical.side":"Côté","physical.rotation":"Angle de rotation","physical.length":"Longueur","physical.allowed_range":"Autorisé : {min}–{max} {unit}","physical.delete_segment":"Supprimer le segment","physical.delete_draft":"Supprimer tout le contour","physical.column_size_title":"Côté extérieur de la colonne carrée placée par le clic (1–150 cm).","card.title":"Plan de la maison","count.devices":"{n} app.","empty.no_spaces":"Aucun espace pour le moment.","empty.add_first":"Ajoutez le premier espace et importez un plan.","empty.install":"Installez l’intégration House Plan et ajoutez-la dans « Appareils et services ».","btn.add_space":"Ajouter un espace","btn.cancel":"Annuler","btn.save":"Enregistrer","btn.close":"Fermer","btn.delete":"Supprimer","btn.edit":"Modifier","btn.open_in_ha":"Ouvrir dans HA","btn.reset":"Réinitialiser","btn.attach":"Joindre…","btn.upload":"Importer…","btn.replace":"Remplacer…","title.zoom_in":"Zoomer","title.zoom_out":"Dézoomer","title.zoom_fit":"Tout ajuster","title.markup":"Traçage des pièces : grille, lignes, contours","title.configure_space":"Configurer l’espace","title.add_space":"Ajouter un espace","title.markup_add":"Murs : tracez une chaîne continue ; Maj verrouille à 45°. Changer d’outil ou quitter l’éditeur termine la chaîne en murs indépendants","title.markup_merge":"Fusionner : cliquez sur une pièce, puis sur la pièce voisine avec laquelle elle partage un mur","title.markup_split":"Diviser une pièce : cliquez sur la pièce, puis sur deux points de ses murs","title.markup_delroom":"Supprimer une pièce : cliquez à l’intérieur de la pièce puis confirmez","title.need_plan":"Importer une image de plan","markup.add":"Murs","markup.merge":"Fusionner","markup.split":"Diviser","markup.resize":"Redimensionner","title.markup_resize":"Redimensionner les pièces : faites glisser une poignée disponible sur un mur horizontal ou vertical","markup.hint_resize":"faites glisser une poignée de mur disponible · le mur s’arrête à la première position non sûre · Échap annule · Ctrl+Z — annuler","resize.disabled.diagonal":"Ce mur est incliné ; le redimensionnement ne prend en charge que les murs horizontaux et verticaux","resize.disabled.side-angle":"Un mur adjacent est incliné ; le redimensionnement exige que les deux murs adjacents rencontrent ce mur à angle droit","resize.disabled.duplicate-physical-wall":"Une cloison distincte, un contour inachevé ou une colonne chevauche ce mur ; supprimez-la ou déplacez-la avant de redimensionner","resize.disabled.partial-shared":"Une pièce voisine n’utilise qu’une partie de ce mur ; il ne peut donc pas être déplacé en toute sécurité comme un mur partagé unique","resize.disabled.unequal-shared":"La pièce voisine utilise des extrémités ou une longueur de mur différentes ; les murs ne peuvent donc pas être déplacés ensemble en toute sécurité","resize.disabled.multiple-rooms":"Déplacer ce mur affecterait plus de deux pièces","resize.disabled.thickness-conflict":"L’épaisseur du mur ne peut pas être conservée en toute sécurité","resize.disabled.opening-conflict":"Une ouverture sur ce mur ne peut pas être déplacée en toute sécurité","resize.disabled.invalid-geometry":"Ce mur ne peut pas être redimensionné sans modifier la structure du plan","resize.limit_stopped":"Mur arrêté : l'étape suivante enfreindrait une limite de jonction","resize.commit_failed":"Le redimensionnement a été annulé car le plan final n’a pas passé le contrôle de géométrie","resize.preview_failed":"Le redimensionnement s’est arrêté à la dernière position sûre car la géométrie du plan ne pouvait pas être préservée","markup.opening":"Ouverture","markup.delete_room":"Supprimer la pièce","history.undo":"Annuler","history.redo":"Rétablir","history.undo_named":"Annuler : {name} (Ctrl+Z)","history.redo_named":"Rétablir : {name} (Ctrl+Maj+Z / Ctrl+Y)","history.undo_empty":"Rien à annuler","history.redo_empty":"Rien à rétablir","history.undone":"Annulé : {name}","history.redone":"Rétabli : {name}","history.add_room":"Créer une pièce","history.split_room":"Diviser la pièce","history.merge_rooms":"Fusionner les pièces","history.resize_room":"Redimensionner la pièce","history.wall_thickness":"Modifier l’épaisseur du mur","history.add_opening":"Ajouter une ouverture de porte ou de fenêtre","history.edit_opening":"Modifier l’ouverture","history.move_opening":"Déplacer l’ouverture","history.delete_opening":"Supprimer l’ouverture","history.delete_room":"Supprimer la pièce","history.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","history.delete_room_with_walls":"Supprimer la pièce et les murs","history.decor_add":"Ajouter un objet décoratif","history.decor_edit":"Modifier l’objet décoratif","history.decor_move":"Déplacer l’objet décoratif","history.decor_transform":"Transformer l’objet décoratif","history.decor_delete":"Supprimer l’objet décoratif","history.backdrop_transform":"Transformer l’arrière-plan du plan","opening.new":"Nouvelle ouverture","opening.edit":"Ouverture","opening.door":"Porte","opening.window":"Fenêtre","opening.gate":"Portail","opening.passage":"Passage ouvert","opening.passage_binding_warning":"L’enregistrement supprimera le capteur d’ouverture/fermeture et la serrure.","opening.invalid_passage_fields":"Le passage ouvert du plan « {room} » possède des paramètres non pris en charge : {fields}.","opening.type_label":"Type","opening.length_label":"Longueur, cm","opening.contact_label":"Capteur d’ouverture/fermeture","opening.lock_label":"Serrure","opening.none":"— aucun —","opening.search_ph":"Rechercher : nom ou ID d’entité…","opening.invert":"Inverser ouvert/fermé","opening.flip_h":"Charnière sur l’autre montant","opening.flip_v":"Ouverture de l’autre côté","opening.open":"Ouvert","opening.closed":"Fermé","opening.locked":"Verrouillé","opening.unlocked":"Déverrouillé","opening.state_unknown":"indisponible","opening.no_entities":"Aucun capteur associé — symbole statique sur le plan.","toast.opening_no_wall":"Cliquez près d’un mur de pièce ou d’un mur indépendant","opening.host_partition":"Mur indépendant","opening.partition_orphan":"Le mur indépendant de cette ouverture n’existe plus","opening.partition_jamb_margin":"Laissez au moins {distance} entre l’ouverture et l’extrémité du mur","opening.rebind_partition":"Associer à un autre mur indépendant","confirm.delete_partition_openings_title":"Supprimer le mur et les ouvertures ?","confirm.delete_partition_openings_body":"Ce mur contient {count} ouverture(s). Elles seront supprimées avec lui.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Supprimer","markup.hint_points":"points : {n} · Maj — pas de 45° · Échap — terminer la chaîne · Ctrl+Z — annuler un point · fermer une zone propose de créer une pièce","markup.hint_start":"cliquez sur un point de la grille pour commencer une chaîne de murs","tip.lqi":"signal Zigbee moyen :","tip.area":"surface : {value}","info.device_header":"Appareil sur le plan","info.model":"Modèle","info.state":"État","info.link":"Lien","info.manuals":"Manuels","info.none":"Aucune information supplémentaire","marker.new_device":"Nouvel appareil","marker.name_label":"Nom (affiché sur le plan)","marker.name_ph":"Nom","marker.binding_label":"Associer à un appareil HA","marker.binding_disabled":"désactivé dans Home Assistant","marker.virtual_option":"Appareil virtuel (sans association)","marker.search_ph":"Rechercher un appareil / groupe…","marker.nothing_found":"aucun résultat","marker.room_label":"Pièce","marker.room_override":" (placement forcé)","marker.room_choose":"— sélectionner une pièce —","marker.room_auto":"— selon la zone de l’appareil (auto) —","marker.icon_label":"Icône","marker.icon_ph":"mdi:… (vide = auto)","marker.display_label":"Affichage","display.badge":"Icône + état","display.icon_ripple":"Icône + état et activité","display.static_icon":"Icône toujours statique","marker.display_hint":"Icône + état modifie le fond pour les états en fonctionnement, ouvert, alarme et indisponible. Icône + état et activité ajoute une brève pulsation pour les événements et une pulsation continue pour une activité, un mouvement ou une présence en cours. Valeur + état remplace l’icône par une valeur HA non ambiguë. Une icône statique ne réagit jamais aux états de l’appareil.","marker.display_hint_badge":"L’icône et le fond dynamique affichent l’état de l’appareil sans la pulsation d’activité habituelle. Les alarmes rouges restent visibles.","marker.display_hint_icon":"L’icône et le fond dynamique affichent l’état de l’appareil sans la pulsation d’activité habituelle. Les alarmes rouges restent visibles.","marker.display_hint_icon_ripple":"L’icône, le fond dynamique et la pulsation affichent une brève animation pour les événements et une animation continue pour le fonctionnement, le mouvement ou la présence. Les alarmes rouges restent prioritaires.","marker.display_hint_value":"Une valeur Home Assistant sélectionnée ou résolue automatiquement remplace l’icône tandis que le fond continue d’indiquer l’état. Les alarmes rouges restent visibles.","marker.display_hint_static_icon":"Le contour adapté au thème et l’icône restent toujours identiques. L’état, l’activité, l’indisponibilité et les alarmes ne modifient pas l’apparence.","marker.static_alarm_warning":"Cet appareil peut signaler des alarmes. L’affichage statique masque l’indication visuelle d’alarme du marqueur.","marker.preview.title":"Aperçu de l’affichage","marker.preview.actual":"Actuel","marker.preview.example":"Exemple","marker.preview.integration":"Fourni par","marker.preview.source":"Source d’affichage","marker.preview.current_state":"État actuel","marker.preview.result":"Sur le plan","marker.preview.details":"Détails de la source","marker.preview.select_source":"Choisissez un appareil ou une entité Home Assistant pour voir son affichage réel.","marker.preview.unknown_provider":"Intégration inconnue","marker.preview.virtual_provider":"House Plan · appareil virtuel","marker.preview.no_source":"Aucune source active","marker.preview.no_state":"Aucun état actuel","marker.preview.mixed_states":"Plusieurs états différents","marker.preview.multiple_sources":"{n} sources","marker.preview.more_sources":"+{n} de plus","marker.preview.scaled":"L’aperçu est redimensionné à {n}% pour tenir dans la zone. La taille enregistrée reste inchangée.","marker.preview.demo_short":"Afficher une activité brève","marker.preview.demo_continuous":"Afficher une activité continue","marker.preview.stop_continuous":"Arrêter l’activité continue","marker.preview.demo_short_notice":"Exemple d’activité brève ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_continuous_notice":"Exemple d’activité continue ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_already_visible":"L’état actuel affiche déjà un véritable effet d’activité ou d’alarme.","marker.preview.reduced_motion":"La réduction des animations est activée sur le système ; l’activité normale est donc affichée sous forme de point.","marker.preview.reason.neutral":"Fond sombre neutre","marker.preview.reason.working":"Fond jaune : l’appareil fonctionne actuellement","marker.preview.reason.working_activity":"Fond jaune et effet d’activité : l’appareil fonctionne actuellement","marker.preview.reason.open":"Fond orange : physiquement ouvert ou déverrouillé","marker.preview.reason.cover_icon_state":"Fond neutre ; l’entité de volet contrôle l’état et l’icône affichés","marker.preview.reason.presence":"Effet d’activité tant qu’une présence est détectée","marker.preview.reason.event":"Effet d’activité bref après un événement détecté","marker.preview.reason.transition":"Effet d’activité pendant que l’appareil se déplace ou change d’état","marker.preview.reason.media_neutral":"Les appareils multimédias restent sur un fond sombre neutre tant qu’ils sont disponibles","marker.preview.reason.unavailable":"Fond neutre atténué : indisponible ou éteint","marker.preview.reason.alarm":"Fond d’alarme rouge ; l’indication d’alarme est toujours affichée","marker.preview.reason.live_states_disabled":"Le style selon les états en direct est désactivé pour cette carte","marker.preview.reason.value_no_state":"Aucune valeur d’état exploitable ; l’icône est affichée à la place","marker.preview.reason.value_ambiguous_sources":"Plusieurs sources de valeur possibles ; l’icône est affichée à la place","marker.preview.reason.value_non_scalar":"La source n’a pas renvoyé de valeur simple ; l’icône est affichée à la place","marker.preview.reason.value_virtual":"Un appareil virtuel n’a aucune valeur Home Assistant ; l’icône est affichée à la place","marker.preview.reason.vacuum_live_plan_only":"La position et la trajectoire en direct de l’aspirateur ne sont disponibles que sur le plan complet","marker.preview.reason.hidden_design_preview":"L’appareil est masqué sur le plan ; cet aperçu de conception reste visible","marker.preview.reason.composite_power_source":"L’état provient de l’entité d’alimentation de l’appareil ; les interrupteurs auxiliaires sont ignorés","marker.preview.reason.activity_display_disabled":"Le mode d’affichage sélectionné n’affiche pas les effets d’activité habituels","marker.preview.reason.ha_disabled":"L’association Home Assistant est désactivée et sera masquée sur le plan","marker.preview.reason.orphaned":"L’association Home Assistant enregistrée est introuvable","marker.preview.reason.static_icon":"Mode statique : l’état de l’appareil ne modifie pas l’icône","marker.activity_color":"Couleur de la pulsation d’activité","marker.ripple_size":"Taille de la pulsation d’activité","marker.activity_alarm_note":"La couleur et la taille n’affectent que l’activité normale et ne modifient pas les alarmes rouges.","marker.pulse_a11y_alarm":"Alarme","marker.pulse_a11y_event":"Événement récent","marker.pulse_a11y_presence":"Présence détectée","marker.pulse_a11y_transition":"Changement d’état","marker.pulse_a11y_running":"En fonctionnement","marker.state_a11y_neutral":"État par défaut","marker.state_a11y_working":"En fonctionnement","marker.state_a11y_open":"Ouvert","marker.state_a11y_locked":"Verrouillé","marker.state_a11y_unlocked":"Déverrouillé","marker.state_a11y_alarm":"Alarme","marker.state_a11y_unavailable":"Indisponible","marker.lqi_a11y_low":"LQI {value}, signal faible","marker.lqi_a11y_mid":"LQI {value}, signal moyen","marker.lqi_a11y_high":"LQI {value}, signal fort","marker.size_label":"Taille / rotation de l’icône","marker.angle_label":"Rotation","marker.model_label":"Modèle","marker.model_ph":"ex. Aqara T&H","marker.link_label":"Lien","marker.desc_label":"Description","marker.desc_ph":"Notes, caractéristiques…","marker.manuals_label":"Manuels (PDF, etc.)","marker.sub_device":"appareil","marker.sub_z2m_group":" · groupe Z2M","marker.sub_group":"groupe","marker.sub_helper":"assistant","space.new":"Nouvel espace","space.header":"Espace","space.title_label":"Titre","space.title_ph":"ex. Garage","space.plan_label":"Plan (arrière-plan)","space.no_plan":"aucune image de plan","space.plan_alt":"plan","room.new":"Nouvelle pièce","room.name_label":"Nom affiché","room.name_ph":"ex. Terrasse","room.area_label":"Zone Home Assistant (non attribuée)","room.no_area_option":"— aucune zone —","room.default_name":"Pièce","device.unnamed":"sans nom","device.light_group":"groupe de lumières","device.fallback":"appareil","device.virtual":"appareil virtuel","confirm.delete_room":"Supprimer la pièce « {name} » ?","confirm.delete_room_title":"Supprimer la pièce « {name} » ?","confirm.delete_room_body":"Choisissez si les murs physiques propres à la pièce doivent être conservés. Les murs partagés avec une autre pièce sont toujours conservés.","confirm.remove_marker":"Supprimer « {name} » du plan ? L’appareil disparaîtra complètement et ne contribuera plus aux données du plan. Vous pourrez l’ajouter de nouveau plus tard.","confirm.erase_decor":"Effacer l’objet {kind} ? Vous pouvez annuler cette action depuis l’historique de l’éditeur.","confirm.delete_space":"Supprimer l’espace « {title} » avec toutes ses pièces et son tracé ?","space.delete_blocked":"Cet espace est encore utilisé par {n} appareil(s). Déplacez-les vers un autre espace ou supprimez-les d’abord.","toast.pos_save_failed":"Échec de l’enregistrement de la position : {err}","toast.no_entity":"L’appareil n’a aucune entité adaptée","toast.ha_disabled_action":"Un objet Home Assistant désactivé ne peut pas être utilisé sur le plan.","toast.ha_disabled_show_device":"Un appareil désactivé dans Home Assistant ne peut pas être affiché sur le plan. Activez-le d’abord dans Home Assistant.","toast.ha_disabled_show_entity":"Une entité désactivée dans Home Assistant ne peut pas être affichée sur le plan. Activez-la d’abord dans Home Assistant.","toast.ha_disabled_add":"Un objet Home Assistant désactivé ne peut pas être ajouté au plan. Activez-le d’abord dans Home Assistant.","toast.ha_binding_unverified":"L’état de l’objet n’a pas pu être vérifié via le registre Home Assistant. L’affichage et les actions sont temporairement indisponibles.","toast.markup_needs_server":"Le tracé est disponible une fois la configuration déplacée sur le serveur","toast.space_order_changed":"L’ordre a changé. Si une carte fixe son étage par numéro, vérifiez ces tableaux de bord.","toast.conflict":"La configuration a été modifiée dans une autre fenêtre — données actualisées, répétez votre dernière action","toast.cfg_save_failed":"Échec de l’enregistrement de la configuration : {err}","toast.room_overlap":"Le contour chevauche la pièce « {name} » — les pièces ne doivent pas se chevaucher","toast.contour_cannot_close":"Le contour ne peut pas être fermé car il est dégénéré ou s’auto-intersecte","toast.merge_not_adjacent":"Seules les pièces partageant un mur peuvent être fusionnées","toast.rooms_merged":"Pièces fusionnées dans « {name} »","toast.split_pick_wall":"Commencez la découpe sur le mur de la pièce","toast.split_bad_cut":"La découpe doit aller d’un mur à l’autre à l’intérieur de la pièce, sans croiser de mur ni se croiser elle-même","merge.header":"Fusionner les pièces","merge.hint":"La pièce fusionnée conserve un nom et une zone. L’autre zone est libérée — ses appareils quittent le plan jusqu’à ce qu’une autre pièce la récupère.","merge.keep":"Conserver","merge.no_area":"aucune zone","toast.room_saved":"Pièce enregistrée ({n}). Appareils ajoutés : {added}. Tracez la suivante ou quittez le mode tracé.","toast.room_saved_no_area":"Pièce enregistrée ({n}, sans zone). Tracez la suivante ou quittez le mode tracé.","toast.marker_needs_server":"La modification des appareils est disponible une fois la configuration déplacée sur le serveur","toast.virtual_name_required":"Saisissez un nom pour l’appareil virtuel","toast.marker_saved":"Appareil enregistré","toast.marker_removed":"Appareil supprimé du plan","toast.integration_missing":"L’intégration House Plan n’est pas installée — gestion indisponible","toast.plan_formats":"Formats pris en charge : SVG, PNG, JPG, WebP","toast.plan_required":"Importez un plan — il est obligatoire","toast.space_added_onboard":"Espace ajouté. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","toast.space_added":"Espace ajouté","toast.space_saved":"Espace enregistré","toast.space_deleted":"Espace supprimé","toast.delete_failed":"Échec de la suppression : {err}","toast.error":"Erreur : {err}","toast.toggle_target_unavailable":"La cible « {name} » est indisponible — aucune action n’a été effectuée","toast.toggle_targets_unavailable":"Cibles indisponibles : {names}. Aucune action n’a été effectuée","toast.file_failed":"Le fichier « {name} » n’a pas été importé : {err}","toast.files_attached":"Fichiers joints : {n}","err.unknown":"erreur inconnue","err.code":"code {code}","err.too_large":"fichier supérieur à {mb} Mo","err.bad_ext":"type non pris en charge (PDF/image attendu)","err.unauthorized":"droits administrateur requis","editor.title":"Titre","editor.default_floor":"Espace initial","editor.default_floor_missing":"L’espace initial « {id} » n’existe plus. Choisissez un autre espace.","editor.floor":"Espace fixe","editor.floor_none":"— non fixé —","editor.floor_index":"Index YAML {index} (conservé)","fixed_floor.loading":"Chargement de l’espace fixe…","fixed_floor.invalid_title":"L’espace fixe est indisponible","fixed_floor.invalid_body":"Vérifiez floor dans la configuration de la carte. Valeur configurée : {value}","editor.icon_size":"Taille des icônes, % de la largeur du plan","editor.show_temperature":"Afficher la température","editor.live_states":"États en direct (on/off, ouvert…)","editor.light_pools":"Halos lumineux et ombres des murs","editor.show_signal":"Afficher le signal Zigbee (LQI)","editor.language":"Langue de l’interface","editor.lang_auto":"Auto (profil HA)","title.icon_rules":"Règles d’icônes : quelle icône MDI attribuer aux appareils selon leur nom","rules.title":"Règles d’icônes","rules.hint":"Les règles sont testées de haut en bas sur « nom de l’appareil + modèle » (expression régulière insensible à la casse) ; la première correspondance gagne. Si rien ne correspond, la classe d’appareil de l’entité est utilisée, puis l’icône générique.","rules.pattern_ph":"regex, ex. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Ajouter une règle","rules.reset":"Réinitialiser par défaut","rules.test_ph":"Tester un nom d’appareil…","rules.invalid":"expression régulière invalide","rules.saved":"Règles d’icônes enregistrées","btn.up":"Monter","btn.down":"Descendre","tap.info":"Fiche de l’appareil","tap.more_info":"Boîte de dialogue Plus d’infos de HA","tap.toggle":"Basculer l’état","marker.tap_label":"Action au toucher pour cet appareil","tap.toggle_note":"L’état déterminé est affiché ci-dessous. Les appareils sécurisés ne sont jamais basculés depuis le plan.","import.title":"Créer des espaces à partir des étages HA","import.hint":"Votre Home Assistant connaît déjà ces étages. Sélectionnez ceux à transformer en espaces du plan — vous importerez ensuite une image de plan pour chacun. Les pièces sont ensuite tracées manuellement sur le plan.","import.start":"Créer {n} espace(s)","import.manual":"Commencer de zéro","import.progress":"Étage {i} sur {n}","import.done":"Espaces créés. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","btn.skip":"Passer","space.scale_label":"Échelle (taille d’une cellule de grille)","space.scale_unit":"cm par cellule","space.scale_unit_imperial":"po par cellule","space.display_section":"Affichage","space.show_borders":"Toujours afficher les contours des pièces","space.zero_wall_style":"Murs d’épaisseur nulle","space.zero_wall_dashed":"Pointillés","space.zero_wall_solid":"Plein","space.zero_wall_help":"Les murs en pointillés laissent passer la lumière. Les murs pleins bloquent la lumière même avec une épaisseur nulle.","space.show_names":"Afficher les noms des pièces (faire glisser pour déplacer)","space.room_color":"Couleur du contour et du nom","space.opacity":"Opacité","space.fill_label":"Remplissage de la pièce","fill.none":"Aucun","fill.lqi":"Signal Zigbee","fill.light":"Lumières","fill.custom":"Couleur personnalisée","space.custom_fill":"Couleur de remplissage","space.source_file":"J’ai une image de plan","space.source_draw":"Aucune image — je tracerai les pièces à la main","fill.temp":"Température","tip.temp_avg":"température moyenne :","tip.hum_avg":"humidité moyenne :","space_card.button":"Ouvrir le plan de l’espace","space_card.not_found":"Espace « {id} » introuvable","space_card.loading":"Chargement…","continuity.restore_plan":"Restauration du plan…","continuity.restore_connection":"Restauration de la connexion aux appareils…","continuity.retry":"Réessayer","editor.space":"Espace","editor.framing":"Cadrage","editor.fit_content":"Tout le contenu visible","editor.fit_house":"Ajusté à la géométrie de la maison","editor.show_button":"Afficher le bouton","editor.button_label":"Libellé du bouton","editor.button_target":"Chemin du tableau de bord cible","marker.sub_entity":"entité","title.general_settings":"Paramètres généraux","gs.title":"Paramètres généraux","gs.hint":"Les couleurs de remplissage s’appliquent à tous les espaces ; chaque couleur possède sa propre opacité. Le mode de remplissage utilisé par un espace se règle dans la boîte de dialogue de cet espace.","gs.light_group":"Remplissage : lumières","gs.light_on":"Lumières allumées","gs.light_off":"Toutes les lumières éteintes","gs.temp_group":"Remplissage : température","gs.temp_cold":"Froid","gs.temp_ok":"Confortable","gs.temp_hot":"Chaud","gs.lqi_group":"Remplissage : signal Zigbee","gs.lqi_low":"Signal faible","gs.lqi_high":"Signal fort","gs.reset":"Réinitialiser par défaut","gs.saved":"Paramètres généraux enregistrés","space.show_lqi":"Afficher le signal Zigbee (LQI) à côté des appareils","space.hide_decor":"Masquer la couche décorative","space.hide_decor_tip":"Les lignes, formes, étiquettes et meubles restent à leur place — visibles dans l’éditeur d’arrière-plan, mais pas sur le plan.","space.hide_openings":"Masquer les ouvertures","space.hide_openings_tip":"Les symboles de portes, fenêtres et portails ne sont pas dessinés, mais les ouvertures continuent de fonctionner : la lumière les traverse, le soleil entre par les fenêtres et les capteurs de contact continuent de les ouvrir. L’éditeur de plan les affiche toujours.","gs.light_none":"Aucune source lumineuse","mode.plan":"Éditeur de plan","mode.devices":"Éditeur d’appareils","display.value":"Valeur + état","marker.subarea":"aucune zone, manuel","device.new":"Nouvel appareil — ouvrez son éditeur pour fermer","opening.unlock_action":"Déverrouiller","opening.lock_action":"Verrouiller","opening.lock_pending":"En cours…","title.close_editor":"Fermer l’éditeur (retour à la vue)","title.add_device":"Ajouter un appareil au plan","devbar.add":"Ajouter","devbar.rules":"Règles d’icônes","device_inbox.button":"Appareils","device_inbox.title":"Appareils sur le plan","device_inbox.search":"Rechercher appareils, entités et intégrations…","device_inbox.add_virtual":"Ajouter un appareil virtuel","device_inbox.tab_on_plan":"Sur le plan","device_inbox.tab_available":"Disponibles","device_inbox.tab_hidden":"Masqués","device_inbox.tab_readd":"De nouveau disponibles","device_inbox.only_new":"Nouveaux uniquement","device_inbox.show_entities":"Afficher les entités","device_inbox.show_hidden":"Afficher les masqués sur le plan","device_inbox.new":"Nouveau","device_inbox.find":"Trouver sur le plan","device_inbox.edit":"Modifier","device_inbox.hide":"Masquer","device_inbox.show":"Afficher","device_inbox.add":"Ajouter","device_inbox.readd":"Ajouter à nouveau","device_inbox.hide_available":"Masquer de la liste","device_inbox.show_more":"Afficher plus","device_inbox.more_actions":"Plus d'actions","device_inbox.find_hidden_hint":"Activez d'abord « Afficher les masqués sur le plan »","device_inbox.show_disabled":"Activez cette liaison dans Home Assistant avant de l'afficher","device_inbox.empty_on_plan":"Aucun appareil correspondant n'est encore sur le plan.","device_inbox.empty_available":"Aucun appareil disponible à ajouter.","device_inbox.empty_hidden":"Il n'y a aucun appareil masqué.","device_inbox.empty_readd":"Aucun appareil supprimé n'est disponible pour un nouvel ajout.","device_inbox.reason_visible_auto":"Découvert automatiquement","device_inbox.reason_visible_explicit":"Ajouté explicitement","device_inbox.reason_manual_hidden":"Masqué par l'utilisateur","device_inbox.reason_automatic_hidden":"Masqué automatiquement","device_inbox.reason_service_entry":"Entrée de registre de service uniquement","device_inbox.reason_excluded_integration":"Intégration exclue par les filtres d'appareils","device_inbox.reason_excluded_domain":"Type d'entité non spatial","device_inbox.reason_grouped_light":"Représenté par un groupe de lumières de la pièce","device_inbox.reason_represented_by_parent":"Représenté par son appareil parent","device_inbox.reason_removed":"Précédemment retiré du plan","device_inbox.reason_available":"Disponible pour ajout au plan","device_inbox.reason_no_bound_room":"La zone HA n'est liée à aucune pièce du plan","device_inbox.status_ha_disabled":"Désactivé dans Home Assistant","device_inbox.status_orphaned":"La liaison n'existe plus dans Home Assistant","device_inbox.status_unverified":"Le registre Home Assistant est temporairement indisponible","device_inbox.saved":"Liste des appareils mise à jour","space.roomcard_section":"La carte de pièce affiche :","space.label_temp":"Température","space.label_hum":"Humidité","space.label_lqi":"Signal Zigbee moyen","space.label_light":"Lumières allumées/éteintes","roomcard.light_on":"Allumé","roomcard.light_off":"Éteint","roomcard.light_partial":"{on} sur {total}","toast.split_pick_inside":"Les points intermédiaires de découpe doivent se trouver à l’intérieur de la pièce","mode.decor":"Éditeur d’arrière-plan","decor.select":"Sélectionner","decor.line":"Ligne","decor.rect":"Rectangle","decor.ellipse":"Ovale","decor.text":"Texte","decor.erase":"Effacer","decor.erase_confirm_title":"Effacer l’objet ?","decor.color":"Couleur","decor.width":"Épaisseur de ligne","decor.line_style":"Style de ligne","decor.line_style_solid":"Plein","decor.line_style_dashed":"Pointillés","decor.fill":"Remplissage","decor.fill_color":"Couleur de remplissage","decor.length":"Longueur","decor.size":"Taille","decor.angle":"Angle de rotation, °","decor.text_size":"Taille du texte","decor.backdrop_properties":"Propriétés de l’arrière-plan du plan","decor.text_title":"Étiquette de texte","decor.object_title":"Modifier {kind}","decor.text_label":"Texte","decor.live_group":"Insérer une variable HA","decor.live_entity":"Entité","decor.live_entity_ph":"choisir une entité","decor.live_attr":"Valeur","decor.live_attr_ph":"choisir l’état ou un attribut","decor.live_state":"État","decor.backdrop":"Image d’arrière-plan","decor.backdrop_hint":"Faites glisser pour déplacer ; tirez un coin pour redimensionner ; utilisez la poignée supérieure pour faire pivoter. Maj modifie les proportions ou libère l’angle.","decor.backdrop_reset":"Réinitialiser l’image","decor.backdrop_reset_done":"L’image a retrouvé sa position et sa taille d’origine","marker.icon_auto":"Auto : {icon} (selon les règles d’icônes ; choisissez-en une pour remplacer)","marker.icon_pin_auto":"Épingle","mode.plan_tip":"Éditeur de plan — la géométrie du logement : dessiner et diviser/fusionner les pièces, les associer aux zones HA, placer portes, fenêtres et portails, déplacer les cartes de pièces, régler l’échelle","mode.devices_tip":"Éditeur d’appareils — tout ce qui concerne les icônes : faire glisser pour positionner, cliquer pour modifier l’association/l’icône/l’affichage, ajouter des appareils virtuels, règles d’icônes","mode.decor_tip":"Éditeur d’arrière-plan — décoration purement visuelle sous le plan : lignes, rectangles, ovales et étiquettes de texte qui ne réagissent jamais aux clics","space.glow_enabled":"Halo des sources lumineuses","gs.glow_group":"Halo des sources lumineuses","gs.glow_base":"Obscurité de la maison","gs.glow_light":"Couleur / intensité de lumière par défaut","gs.wall_group":"Murs","gs.wall_fill":"Remplissage des murs","gs.glow_radius":"Rayon du halo","gs.unit_m":"m","gs.unit_ft":"pi","marker.controls_label":"Contrôle d’autres sources lumineuses","marker.controls_hint":"Avec l’action au toucher « Basculer », un clic inverse l’état de chaque source ajoutée ici. L’entité propre associée au marqueur est contrôlée directement et ne peut pas être ajoutée ; utilisez l’interrupteur ci-dessous pour la classer comme lumière.","marker.controls_filter":"Rechercher des lumières et interrupteurs…","info.controls":"Contrôle","marker.glow_radius_label":"Rayon du halo","marker.glow_radius.help":"Définit le rayon du halo en mètres ou en pieds ; une valeur vide utilise le rayon des paramètres généraux.","marker.glow_radius.help.aria":"Aide : rayon du halo","markup.wallthick":"Épaisseur","markup.select":"Sélectionner","title.markup_wallthick":"Épaisseur — cliquez sur un mur pour définir son épaisseur de 0 à 100 cm.","markup.hint_wallthick":"cliquez sur un mur · Échap ferme sans appliquer","wallthick.field":"Épaisseur","wallthick.unit_cm":"cm","wallthick.unit_in":"po","wallthick.apply_room":"Appliquer à tous les murs de cette pièce","markup.draw_wall_title":"Épaisseur de chaque nouveau segment de mur (0–100 cm). Les murs partagés conservent la valeur existante.","room.queue_progress":"Pièce {current} sur {total}","toast.wall_rooms_saved":"Pièces créées : {n}","toast.wall_chain_saved":"Chaîne de murs enregistrée","toast.wallthick_pick":"Cliquez sur un mur","toast.wallthick_set":"Épaisseur du mur définie","toast.wallthick_cleared":"Épaisseur du mur supprimée","toast.physical_range":"Saisissez une valeur comprise entre {min} et {max} {unit}","toast.zero_wall_opening_conflict":"Supprimez d’abord l’ouverture sur ce segment de mur.","toast.zero_wall_ambiguous":"Le segment de mur est ambigu. Simplifiez ou ajustez la jonction.","toast.zero_wall_migration_blocked":"L’espace n’a pas été converti : {reason}. Aucune donnée n’a été modifiée.","toast.physical_angle":"Saisissez un angle de rotation compris entre 0° inclus et 90° exclu","toast.physical_limit":"L’espace a atteint la limite pour ce type de géométrie","toast.geometry_unsafe":"Modification annulée : la géométrie des murs n’a pas pu être construite en toute sécurité.","junction.limit_angle":"L'angle entre les murs est trop aigu : {actual}°, minimum {limit}°.","junction.limit_valence":"Trop de murs se rejoignent en un nœud : {actual}, maximum {limit}.","junction.limit_length":"Le segment de mur est trop court : {actual} cm, minimum {limit} cm.","junction.limit_distance":"Les murs et les nœuds sont trop proches : {actual} cm, minimum {limit} cm.","junction.limit_clearance":"Il ne reste plus d'intérieur de pièce : {actual} cm², minimum {limit} cm².","junction.limit_check_failed":"La vérification des jonctions n'a pas pu s'exécuter — la modification n'a pas été enregistrée","toast.wall_model_migration_blocked":"Le modèle de murs n’a pas pu être mis à jour : {reason}. Le plan n’a pas été modifié. Lancez « Optimiser les plans » ; si l’erreur se reproduit, corrigez la géométrie de mur conflictuelle.","toast.wall_model_client_outdated":"Mettez à jour la carte et rechargez la page avant de modifier le plan.","wall_model.reason.invalid-room":"contour de pièce invalide","wall_model.reason.zero-length":"segment de mur de longueur nulle","wall_model.reason.third-owner":"mur partagé par plus de deux pièces","wall_model.reason.duplicate-id":"identifiants de mur en conflit","wall_model.reason.thickness-conflict":"valeurs d’épaisseur de mur en conflit","wall_model.reason.opening-host":"l’ouverture ne peut pas être attribuée à un seul mur","toast.delete_room_pick":"Cliquez à l’intérieur de la pièce que vous souhaitez supprimer","toast.plan_snap_ambiguous":"Zoomez pour choisir le nœud du mur","toast.wall_repair_ambiguous":"Le contour de la pièce possède plusieurs connexions possibles. Zoomez et reliez-le explicitement.","toast.wall_repair_too_large":"L’écart dépasse 2 cm. Reliez les murs avant de créer la pièce.","toast.wall_repair_changed":"La géométrie des murs a changé. Essayez de créer de nouveau la pièce.","toast.opening_on_zero_wall":"Les ouvertures ne peuvent pas être placées sur un mur d’épaisseur nulle","marker.from_ha_option":"Choisir dans la liste HA","marker.show_entities":"Afficher les entités","marker.show_entities_tip":"Ajoute à la liste non seulement les appareils, mais aussi toutes leurs entités","marker.pick_ph":"Choisir un appareil…","room.open_area":"Ouvrir la zone HA","view.volumetric":"Vue volumétrique","view.flat":"Vue plane","kiosk.title":"Tailles pour cet écran","kiosk.hint":"Enregistrées uniquement sur cet appareil — chaque tablette murale ou TV peut avoir ses propres tailles confortables.","kiosk.icon_scale":"Taille des icônes d’appareils","kiosk.font_scale":"Taille du texte des cartes de pièces","editor.kiosk":"Mode appareil mural (kiosque)","editor.cycle":"Changer automatiquement d’espace toutes les N secondes (kiosque, 0 = désactivé)","room.settings_title":"Paramètres de la pièce","room.settings_section":"Paramètres de la pièce (remplacent ceux de l’espace)","room.fill_label":"Remplissage de CETTE pièce","fill.inherit":"Comme l’espace","room.custom_fill_space":"Couleur de l’espace","room.custom_fill_own":"Couleur de la pièce","room.temp_src_label":"Source de température","room.hum_src_label":"Source d’humidité","room.src_average":"Moyenne des capteurs de la pièce (par défaut)","room.src_pick":"Un appareil ou une entité HA spécifique","room.src_ph":"Choisir une source…","toast.room_updated":"Pièce mise à jour","space.card_font":"Taille de police des cartes de pièces (tout l’espace)","room.sizes_section":"Tailles de police","room.name_scale":"Taille du nom de la pièce","room.label_scale":"Taille des mesures","preview.room_name":"Salon","toast.cfg_reload_failed":"Impossible de recharger le plan depuis le serveur : {err}","toast.locale_load_failed":"Impossible de charger le pack de langue. L'anglais est utilisé jusqu'au rechargement de la page.","room.settings_short":"Paramètres de la pièce","room.unnamed":"Pièce sans nom","marker.use_climate_temp":"Inclure la température de l’appareil dans la pièce","marker.use_climate_temp_tip":"Ajoute la valeur current_temperature d’un climatiseur ou thermostat à la moyenne de la pièce. Configurez séparément le badge de valeur externe ci-dessous.","marker.value_badge_title":"Badge de valeur","marker.value_badge.help":"Affiche une valeur sélectionnée à côté de l’icône. Cela n’affecte pas les mesures de la pièce, la lumière, le halo ni l’action au toucher.","marker.value_badge.help.aria":"Aide : badge de valeur de l’appareil","marker.value_badge_enabled":"Afficher un badge de valeur","marker.value_badge_source":"Valeur","marker.value_badge_source.help":"Choisissez un état précis, un attribut pris en charge ou une valeur dérivée de cet appareil.","marker.value_badge_source.help.aria":"Aide : source du badge de valeur","marker.value_badge_position":"Position","marker.value_badge_position.help":"Le côté choisi reste fixe et ne bascule pas automatiquement au bord du plan.","marker.value_badge_position.help.aria":"Aide : position du badge de valeur","marker.value_badge_right":"À droite","marker.value_badge_bottom":"En dessous","marker.value_badge_left":"À gauche","marker.value_badge_top":"Au-dessus","marker.value_badge_empty":"Cet appareil n’a aucune valeur disponible","marker.value_badge_static":"Une icône statique n’affiche pas de valeurs en direct. Le paramètre est conservé.","marker.value_badge_missing":"Source indisponible","marker.value_badge_missing_hint":"La source enregistrée est actuellement indisponible. Conservez-la, désactivez le badge ou choisissez une autre valeur.","marker.value_badge_duplicate":"La même valeur est déjà affichée dans l’icône.","marker.value_badge_state":"État · {name}","marker.value_badge_marker_state":"État de la source lumineuse · {name}","marker.value_badge_lqi":"Qualité moyenne du signal Zigbee","marker.value_badge_attr_current_temperature":"Température actuelle · {name}","marker.value_badge_attr_temperature":"Température cible · {name}","marker.value_badge_attr_current_humidity":"Humidité actuelle · {name}","marker.value_badge_attr_humidity":"Humidité · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Vitesse · {name}","marker.value_badge_attr_brightness":"Luminosité · {name}","marker.value_badge_attr_volume_level":"Volume · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Vitesse du ventilateur · {name}","marker.value_source":"Source de la valeur","marker.value_source.help":"Choisissez la valeur qui remplace l’icône. L’action au toucher ne change pas.","marker.value_source.help.aria":"Aide : source de la valeur dans le marqueur","marker.value_source_auto":"Automatique (comme avant)","marker.value_source_missing_hint":"La source enregistrée est indisponible. Le marqueur affiche — jusqu’à son retour ou jusqu’au choix d’une autre valeur.","marker.light_role_label":"Cet appareil est-il une source lumineuse ?","marker.light_role.help":"Détermine si ce marqueur est lui-même une source lumineuse spatiale. Les lumières liées ci-dessus restent des contrôles de lumière de pièce indépendants.","marker.light_role.help.aria":"Aide : cet appareil est-il une source lumineuse","marker.light_role_auto_yes":"Auto (source lumineuse)","marker.light_role_auto_no":"Auto (pas une source lumineuse)","marker.light_role_always":"Toujours une source lumineuse","marker.light_role_never":"Jamais une source lumineuse","marker.light_entity_label":"Entité lumineuse principale","marker.light_entity.help":"Pour un appareil composite, choisissez l’entité dont l’état, la couleur et la luminosité représentent cette source sur le plan. Le choix automatique reste compatible avec les anciens plans.","marker.light_entity.help.aria":"Aide : entité lumineuse principale","marker.light_entity_auto":"Automatique ({entity})","marker.light_entity_none":"aucune entité contrôlable","marker.light_entity_missing":"L’entité enregistrée {entity} est indisponible. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.toggle_entity_label":"Entité à basculer","marker.toggle_entity.help":"Pour un appareil composite, choisissez précisément la lumière ou l’interrupteur propre à actionner par un toucher. Automatique conserve les règles de cible précédentes et reste indépendant de l’entité lumineuse principale.","marker.toggle_entity.help.aria":"Aide : entité à basculer","marker.toggle_entity_auto":"Automatique ({entity})","marker.toggle_entity_none":"aucune entité propre contrôlable","marker.toggle_entity_missing":"L’entité enregistrée {entity} ne fait plus partie des canaux sélectionnables de ce marqueur. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.glow_color_label":"Couleur et luminosité du halo","marker.glow_mode.help":"Utilisez les valeurs en direct de la source, remplacez uniquement sa couleur ou fixez à la fois la couleur et la luminosité. Le minimum est 1 % ; pour désactiver la source, choisissez « Jamais ».","marker.glow_mode.help.aria":"Aide : couleur et luminosité du halo","marker.glow_mode_auto":"Depuis la source","marker.glow_mode_color":"Définir la couleur","marker.glow_mode_fixed":"Définir la couleur et la luminosité","marker.glow_color":"Couleur du halo","marker.glow_brightness":"Luminosité","marker.glow_disabled_never":"Les paramètres du halo sont indisponibles car ce marqueur est explicitement défini comme n’étant pas une source lumineuse.","marker.glow_disabled_auto":"Le mode Auto n’a trouvé aucune source lumineuse spatiale pour ce marqueur.","marker.glow_disabled_no_entity":"Aucune entité Home Assistant contrôlable et active n’est disponible pour cette source spatiale.","marker.glow_passive_hint":"Cette source ne possède aucune donnée Home Assistant propre. Définissez manuellement sa couleur et sa luminosité ; son rayon reste disponible.","marker.control_broken":"La source enregistrée est absente ou n’est plus marquée comme lumière","marker.control_missing_label":"Lumière du plan manquante","marker.control_passive":"source passive","toast.marker_control_cycle":"Ce lien créerait une chaîne circulaire de contrôles de lumière.","toast.marker_binding_required":"Choisissez un appareil Home Assistant avant de lier une autre source lumineuse.","confirm.unlock":"Déverrouiller « {name} » ?","toast.files_migrate_failed":"Les pièces jointes n’ont pas pu être déplacées vers la nouvelle association ; les liens continuent de pointer vers les anciens fichiers : {err}","space.pick_saved":"Déjà importé","space.pick_saved_hint":"Plans stockés sur le serveur, y compris ceux que vous avez précédemment dissociés","space.no_saved":"Aucun plan n’est encore stocké sur le serveur.","space.loading":"Chargement…","space.used_by":"utilisé par : {list}","space.in_use":"Un espace utilise encore ce plan — dissociez-le d’abord","btn.use":"Utiliser","confirm.delete_plan":"Supprimer le fichier de plan « {name} » du serveur ? Cette action est irréversible.","toast.plans_list_failed":"Impossible de lister les plans stockés : {err}","toast.plan_delete_failed":"Impossible de supprimer le plan : {err}","marker.hide":"Masquer","marker.hide_tip":"L’appareil disparaîtra du plan après l’enregistrement mais continuera de compter dans le signal de la pièce. Restaurez-le via « Masqués et désactivés » dans l’éditeur d’appareils.","marker.show":"Afficher","marker.show_tip":"L’appareil apparaîtra de nouveau sur le plan après l’enregistrement.","marker.hidden_ghost":"Appareil masqué par l’utilisateur","marker.ha_disabled_device":"L’appareil est désactivé dans Home Assistant et masqué du plan.","marker.ha_disabled_entity":"L’entité est désactivée dans Home Assistant et masquée du plan.","marker.ha_disabled_all_entities":"L’appareil n’a aucune entité Home Assistant active ; il est donc masqué du plan.","marker.ha_registry_limited":"Le registre Home Assistant complet n’est pas accessible à cet utilisateur. L’objet non vérifié ne peut pas être affiché ni utilisé pour le moment.","marker.delete_tip":"Supprimer complètement l’appareil du plan et de tous les agrégats. Vous pourrez l’ajouter de nouveau plus tard.","tap.run":"Exécuter une automatisation/un script/une scène","marker.run_target_label":"Élément à exécuter","marker.run_search_ph":"Rechercher : automatisation, script ou scène…","marker.run_target_gone":"Cible {id} introuvable — choisissez-en une autre","marker.tap_confirm":"Demander une confirmation","marker.tap_confirm_tip":"Afficher une boîte de dialogue de confirmation avant l’action — protection contre les appuis accidentels.","marker.toggle_hint_single":"Cible : {name} ({id}).","marker.virtual_light_target":"Lumière virtuelle : {name}.","marker.virtual_light_current":"État manuel : {state} → {effect}.","marker.virtual_light_state_on":"allumé","marker.virtual_light_state_off":"éteint","marker.toggle_hint_group":"Basculera {count} source(s) : {names}.","marker.toggle_hint_current":"Actuellement : {state} → {effect}.","marker.toggle_hint_group_current":"Actuellement allumées : {on} sur {count} → {effect}.","marker.toggle_hint_skipped":"Seront ignorées ({count}) : {targets}.","marker.toggle_effect_turn_on":"sera allumé","marker.toggle_effect_turn_off":"sera éteint","marker.toggle_effect_open":"sera ouvert","marker.toggle_effect_close":"sera fermé","marker.toggle_effect_stop":"sera arrêté","marker.toggle_effect_toggle":"l’état sera basculé","marker.toggle_skip_missing":"introuvable","marker.toggle_skip_ha_disabled":"désactivé dans HA","marker.toggle_skip_unavailable":"indisponible","marker.toggle_skip_unsupported":"basculement non pris en charge","marker.toggle_skip_secure":"bloqué pour des raisons de sécurité","marker.toggle_none_no_binding":"Cet appareil n’a aucune association ; il n’y a donc aucun état à basculer. Un toucher ne fera rien.","marker.toggle_none_no_actionable_entity":"Cet appareil n’a aucun état pouvant être basculé. Un toucher ne fera rien.","marker.toggle_none_configured_targets_missing":"Les cibles configurées sont indisponibles. L’entité propre de l’appareil ne leur sera pas substituée.","marker.toggle_none_ha_disabled":"La cible est désactivée dans Home Assistant et ne peut pas être utilisée sur le plan.","marker.toggle_none_unavailable":"La cible est actuellement indisponible. Un toucher ne fera rien.","marker.toggle_none_unsupported":"Home Assistant n’expose aucune opération de basculement sûre pour cette entité. Un toucher ne fera rien.","marker.toggle_none_secure":"Le basculement des serrures, alarmes et portails sécurisés depuis le plan est bloqué pour des raisons de sécurité.","run.automation":"automatisation","run.script":"script","run.scene":"scène","confirm.tap_run":"Exécuter « {name} » ?","confirm.tap_toggle":"Basculer « {name} » ?","confirm.current_state":"État actuel : {state}","confirm.expected_state":"Après basculement : {state}","confirm.group_current":"{on} allumé(s) sur {total}","confirm.group_all_on":"tout est allumé","confirm.group_all_off":"tout est éteint","confirm.unavailable_targets":"Indisponibles : {count}","confirm.expected_by_ha":"Home Assistant déterminera l’état","confirm.state_on":"Allumé","confirm.state_off":"Éteint","confirm.state_open":"Ouvert","confirm.state_closed":"Fermé","confirm.state_opening":"Ouverture en cours","confirm.state_closing":"Fermeture en cours","confirm.state_stopped":"Arrêté","confirm.state_unknown":"Inconnu","toast.run_started":"Démarré : {name}","toast.run_target_missing":"Cible d’exécution introuvable — vérifiez les paramètres de l’appareil","toast.run_target_required":"Choisissez une automatisation, un script ou une scène","toast.tap_target_changed":"La cible de l’action a changé. Réessayez.","toast.virtual_light_toggle_failed":"Impossible de basculer la lumière virtuelle : {err}","toast.value_badge_source_required":"Choisissez une valeur pour le badge","btn.run":"Exécuter","vac.section":"Robot aspirateur : position en direct","vac.autocal":"Configurer automatiquement","vac.live":"Position en direct sur le plan","vac.trail":"Afficher le trajet du robot","vac.cal_maps":"Cartes calibrées : {maps}","vac.autocal_no_rooms":"L’intégration ne fournit aucune liste de pièces — ouvrez « Ajuster manuellement »","vac.autocal_no_match":"Les noms de pièces ne correspondent pas (il en faut ≥3 en commun) — ouvrez « Ajuster manuellement »","vac.autocal_done":"Terminé : association via {rooms} pièces. Lancez un nettoyage et vérifiez","vac.cal_need_pos":"Le robot ne transmet pas de coordonnées — lancez un nettoyage puis mettez-le en pause","vac.cal_done":"Calibration enregistrée. Lancez un nettoyage et vérifiez","vac.cal_cancelled":"Calibration annulée","vac.fit":"Ajuster manuellement","vac.fit_hint":"Faites glisser la carte du robot pour la positionner, étirez-la par les coins","vac.fit_rotate":"Rotation de 90°","vac.fit_mirror":"Miroir","vac.trail_never":"Jamais","vac.trail_cleaning":"Pendant le nettoyage","vac.trail_always":"Toujours","gs.bg_group":"Arrière-plan de la scène","gs.bg_color":"Arrière-plan autour du plan","gs.bg_default":"Valeur par défaut du thème","gs.bg_theme":"valeur par défaut du thème","gs.bg_mode":"Arrière-plan du plan","gs.bg_static":"Couleur statique","gs.bg_daynight":"Suit le soleil","gs.bg_daynight_hint":"L’arrière-plan évolue entre l’aube, le jour, le crépuscule et la nuit en utilisant sun.sun, ou votre heure locale si les données du soleil sont indisponibles. Aucune orientation du nord n’est nécessaire pour l’arrière-plan.","gs.sun_group":"Soleil","gs.sun_missing":"L’entité sun.sun est introuvable — l’arrière-plan suit votre heure locale ; les rayons de soleil par les fenêtres sont indisponibles.","gs.north":"Nord sur le plan","gs.north_ph":"non défini","gs.north_hint":"Définissez le nord uniquement pour les rayons par les fenêtres (pas de 1°, 15° avec Maj) ; l’arrière-plan Soleil fonctionne sans lui.","gs.north_clear":"Effacer","gs.north_letter":"N","gs.sun_rays":"Lumière du soleil à travers les fenêtres","gs.about_group":"À propos","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · documentation et problèmes","gs.about_telegram":"Discussion Telegram","space.bg_color":"Arrière-plan autour du plan","space.bg_inherit":"Hériter des paramètres généraux","space.bg_inherited":"hérite des paramètres généraux","space.bg_mode":"Arrière-plan du plan","space.north":"Nord sur le plan (remplacement)","space.north_inherited":"hérité : {v}","space.sun_rays":"Lumière du soleil à travers les fenêtres","space.sun_inherit":"Hériter des paramètres généraux","space.sun_on":"Activé","space.sun_off":"Désactivé","canvas.far_objects":"{n} objet(s) éloigné(s) du plan","canvas.show_far":"Afficher","canvas.home_tip":"Le plan est là-bas — cliquez pour l’ajuster à l’écran","gs.grid_group":"Maintenance du plan","gs.grid_hint":"Met à jour les modèles de données, aligne les éléments du plan sur la grille et fusionne les fragments de murs redondants. Un rapport précis est affiché avant tout enregistrement.","gs.align_all":"Optimiser les plans","gs.align_title":"Optimiser les plans","gs.align_none":"Tous les plans utilisent déjà le modèle de données optimisé actuel.","gs.optimize_no_automatic_changes":"Aucune modification automatique n’est à appliquer. Vérifiez les éléments ci-dessous.","gs.align_count":"{n} élément(s) sur {total} seront déplacés, de {cm} cm au maximum.","gs.align_where":"Le plus grand déplacement se trouve dans « {s} ».","gs.align_turned":"Ouvertures dont l’angle est corrigé : {n}.","gs.align_removed_drafts":"Contours invalides réduits par la grille et supprimés : {n}.","gs.optimize_redundant_drafts":"Chaînes de murs enregistrées masquées par des murs de pièces pleins et supprimées : {n}.","gs.align_preflight_failed":"Impossible de vérifier en toute sécurité la géométrie des espaces suivants : {spaces}{more}.","gs.align_preflight_hint":"Les plans n’ont pas été modifiés. Copiez les diagnostics avec le bouton ci-dessous et joignez-les au rapport de bug avec un export de l’espace.","gs.preflight_reason_prepare-exception":"Impossible de préparer la géométrie de l’espace (exception lors de la construction du modèle)","gs.preflight_reason_wall-null":"Le corps du mur n’a pas pu être construit (l’union a renvoyé un résultat vide)","gs.preflight_reason_wall-degraded-extra":"Le corps du mur s’est dégradé avec une géométrie supplémentaire","gs.preflight_reason_wall-failed-core":"Le noyau du corps du mur n’a pas pu être assemblé","gs.preflight_reason_wall-exception":"La construction du mur a déclenché une exception","gs.preflight_reason_floor-null":"Le contour du sol n’a pas pu être construit","gs.preflight_reason_floor-exception":"La construction du sol a déclenché une exception","gs.preflight_copy":"Copier les diagnostics","gs.preflight_copied":"Diagnostics copiés","gs.preflight_update_hint":"Les versions de la carte et de l’intégration diffèrent — mettez House Plan à jour puis réessayez.","gs.align_preflight_space":"Espace {n}","gs.align_preflight_more":", et {n} de plus","gs.optimize_changes":"Migrations de modèle : {m} ; espaces mis à jour : {c} ; valeurs de coordonnées parasites supprimées : {p} ; fragments de murs réels fusionnés : {w} ; fragments de murs d’épaisseur nulle fusionnés : {s} ; murs indépendants : {i}.","gs.zero_walls_migrated":"Portions de murs virtuels converties : {n}.","gs.wall_segments_migrated":"Segments de murs stabilisés : {n}.","gs.optimize_lattice_summary":"Valeurs de coordonnées parasites normalisées : {n} ; déplacement maximal : {cm} cm.","gs.optimize_lattice_space":"{space} : valeurs de coordonnées normalisées : {n} ; valeurs hors grille laissées inchangées : {far}.","gs.optimize_coincident_partitions":"Sections de murs indépendants masquées absorbées dans les murs des pièces : {n}.","gs.optimize_openings_rehosted":"Ouvertures rattachées aux murs des pièces : {n}.","gs.optimize_walls_straightened":"Murs redressés : {n} ; déplacement maximal : {cm} cm.","gs.optimize_walls_straightened_where":"Plus grande correction de mur : {s}.","gs.optimize_walls_straighten_skipped":"Murs proches d’un axe laissés inchangés car ils ne pouvaient pas être réparés en toute sécurité : {n}.","gs.optimize_glow_migration":"Ancien mode Glow : {spaces} espaces → aucun remplissage de données + Glow indépendant ; {rooms} pièces → remplissage de données hérité + Glow indépendant.","gs.optimize_references":"Références réparées : espaces — {spaces} ; pièces — {rooms} ; positions — {positions} ; appareils dissociés des espaces manquants — {detached}.","gs.optimize_reference_more":", et {n} de plus","gs.optimize_orphans_removed":"Enregistrements oubliés supprimés : {total} — étiquettes de pièces : {rooms} ; appareils : {devices} ; marqueurs de groupe : {groups}. Ils appartenaient à des espaces supprimés précédemment.","gs.optimize_live_positions":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles seront conservées.","gs.optimize_live_positions_remove":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles sont sélectionnées pour suppression.","gs.optimize_live_names":" : {names}{more}","gs.optimize_live_remove":"Supprimer les anciennes positions","gs.optimize_live_keep":"Conserver les anciennes positions","gs.optimize_live_selected":"Les anciennes positions seront supprimées après l’application de l’optimisation.","gs.optimize_unverified":"Impossible de vérifier en toute sécurité les positions : {n}. Elles ont été laissées inchangées.","gs.optimize_registry_limited":"Un accès administrateur complet aux registres Home Assistant est requis pour une vérification sûre.","gs.optimize_vacuum_warning":"Associations de pièces d’aspirateur nécessitant encore une vérification : {n}.","gs.optimize_details":"Détails","gs.optimize_details_more":"Et {n} enregistrement(s) de plus.","gs.optimize_detail_removed":"sera supprimé","gs.optimize_detail_live":"sera conservé","gs.optimize_detail_unverified":"non vérifié","gs.optimize_detail_room_label":"étiquette de pièce","gs.optimize_detail_device":"appareil","gs.optimize_detail_group":"marqueur de groupe","gs.optimize_detail_unknown":"propriétaire inconnu","gs.optimize_detail_item":"{status} : {kind} {id} ; ancien espace {space}","gs.align_warn":"Les éléments volontairement placés entre les nœuds de la grille seront déplacés. Une seule annulation est disponible après l’opération, uniquement jusqu’à la prochaine modification du plan.","gs.align_run":"Optimiser","gs.align_done":"Plans optimisés : {n} élément(s) déplacé(s), {m} enregistrement(s) maintenu(s), {r} référence(s) réparée(s)","gs.optimize_undo":"Annuler la dernière optimisation","gs.optimize_undone":"La dernière optimisation a été annulée","decor.furniture":"Mobilier","furn.title":"Bibliothèque de mobilier","furn.symbol":"Symbole","furn.group_furniture":"Mobilier","furn.group_appliance":"Électroménager","furn.group_sanitary":"Sanitaires","furn.group_other":"Autre","furn.width":"Largeur","furn.depth":"Profondeur","furn.back_to_categories":"Toutes les catégories","furn.pick_hint":"Choisissez un symbole, puis cliquez sur le plan.","furn.place_hint":"Cliquez sur le plan — le meuble se place contre le mur le plus proche. Maj permet un placement libre.","furn.cat_air_conditioner":"Climatiseurs","furn.cat_armchair":"Fauteuils","furn.cat_bathtub":"Baignoires","furn.cat_bed":"Lits","furn.cat_bidet":"Bidets","furn.cat_boiler":"Chauffe-eau","furn.cat_chair":"Chaises","furn.cat_coffee_table":"Tables basses","furn.cat_cooktop":"Plaques de cuisson","furn.cat_dining_table":"Tables à manger","furn.cat_dishwasher":"Lave-vaisselle","furn.cat_dryer":"Sèche-linge","furn.cat_fireplace":"Cheminées","furn.cat_fridge":"Réfrigérateurs","furn.cat_kitchen_cabinet":"Meubles de cuisine","furn.cat_kitchen_sink":"Éviers de cuisine","furn.cat_nightstand":"Meubles","furn.cat_plant":"Plantes","furn.cat_rug":"Tapis","furn.cat_shelving":"Étagères","furn.cat_shower":"Douches","furn.cat_sink":"Lavabos","furn.cat_sofa":"Canapés","furn.cat_stairs":"Escaliers","furn.cat_toilet":"Toilettes","furn.cat_tv":"Téléviseurs","furn.cat_wardrobe":"Armoires","furn.cat_washer":"Lave-linge","furn.cat_work_table":"Bureaux","furn.sym_sofa":"Canapé","furn.sym_sofa_three_seat":"Canapé trois places","furn.sym_sofa_corner_right":"Canapé d'angle droit","furn.sym_armchair":"Fauteuil","furn.sym_armchair_office":"Chaise de bureau","furn.sym_coffee_table":"Table basse","furn.sym_coffee_table_round":"Table basse ronde","furn.sym_coffee_table_oval":"Table basse ovale","furn.sym_coffee_table_rounded":"Table basse arrondie","furn.sym_table_dining":"Table à manger","furn.sym_table_round":"Table ronde","furn.sym_table_dining_oval":"Table à manger ovale","furn.sym_table_dining_rounded":"Table à manger arrondie","furn.sym_chair":"Chaise","furn.sym_chair_bar":"Tabouret de bar","furn.sym_desk":"Bureau","furn.sym_desk_corner":"Bureau d'angle","furn.sym_bed_double":"Lit double","furn.sym_bed_single":"Lit simple","furn.sym_nightstand":"Table de chevet","furn.sym_cabinet_tv":"Meuble TV","furn.sym_cabinet_shoe":"Meuble à chaussures","furn.sym_cabinet_sink":"Meuble sous lavabo","furn.sym_wardrobe":"Armoire","furn.sym_bookshelf":"Bibliothèque","furn.sym_wall_unit":"Élément mural","furn.sym_kitchen_floor":"Module de cuisine bas","furn.sym_kitchen_floor_corner":"Module de cuisine bas d'angle","furn.sym_kitchen_wall":"Module de cuisine haut","furn.sym_kitchen_wall_corner":"Module de cuisine haut d'angle","furn.sym_shelf_floor":"Étagère au sol","furn.sym_shelf_wall":"Étagère murale","furn.sym_fridge":"Réfrigérateur","furn.sym_stove":"Cuisinière","furn.sym_cooktop_two":"Plaque à deux feux","furn.sym_dishwasher":"Lave-vaisselle","furn.sym_washer":"Lave-linge","furn.sym_dryer":"Sèche-linge","furn.sym_tv":"Téléviseur","furn.sym_tv_wall":"Téléviseur mural","furn.sym_ac":"Climatiseur","furn.sym_water_heater":"Chauffe-eau","furn.sym_toilet":"Toilettes","furn.sym_toilet_built_in":"Toilettes suspendues","furn.sym_bathtub":"Baignoire","furn.sym_bathtub_corner":"Baignoire d'angle","furn.sym_shower":"Douche","furn.sym_sink":"Lavabo","furn.sym_kitchen_sink":"Évier de cuisine","furn.sym_kitchen_sink_double":"Évier de cuisine double","furn.sym_bidet":"Bidet","furn.sym_bidet_built_in":"Bidet suspendu","furn.sym_stairs":"Escalier","furn.sym_fireplace":"Cheminée","furn.sym_plant":"Plante","furn.sym_rug":"Tapis","common.yes":"Oui","common.no":"Non","vac.diag_source":"Source","vac.diag_platform":"Intégration","vac.diag_status":"État","vac.diag_position":"Position","vac.diag_rooms":"Pièces","vac.diag_rooms_value":"{total} · {matched} noms correspondent · {readiness}","vac.autocal_ready":"calibration automatique disponible","vac.autocal_not_ready":"3 noms correspondants requis","vac.diag_path":"Chemin de l’intégration","vac.diag_map":"ID de carte","vac.source_none":"non sélectionné","vac.source_status_ok":"Prêt","vac.source_status_missing":"Manquant","vac.source_status_disabled":"Désactivé dans Home Assistant","vac.source_status_unavailable":"Indisponible","vac.source_status_unverified":"Impossible à vérifier avec les autorisations actuelles","vac.source_status_unsupported":"Aucune donnée de position","vac.source_status_none":"Aucune source","vac.source_banner_missing":"La source enregistrée n’existe plus. Elle n’a pas été remplacée automatiquement ; choisissez une autre source ou restaurez-la dans Home Assistant.","vac.source_banner_disabled":"La source enregistrée est désactivée dans Home Assistant. Activez-la ou choisissez une autre source.","vac.source_banner_unverified":"Les autorisations Home Assistant actuelles ne permettent pas de vérifier cette source enregistrée. Elle reste épinglée et ne sera pas remplacée automatiquement.","vac.choose_source":"Choisir la source","vac.source_auto":"Automatique","vac.source_auto_hint":"Utiliser une entité compatible de cet appareil","vac.all_cameras":"Toutes les caméras","vac.all_cameras_warn":"Une caméra peut ne pas fournir de données du robot. Choisissez-en une uniquement s’il s’agit de la carte de votre aspirateur.","vac.all_cameras_empty":"Aucune autre entité caméra trouvée.","vac.platform_unknown":"intégration inconnue","vac.cap_position":"position","vac.cap_rooms_short":"pièces","vac.cap_path":"trajet","vac.cap_map":"ID de carte","vac.cap_none":"aucune donnée de robot détectée","vac.xcme_hint":"Activez ces attributs Xiaomi Cloud Map Extractor :","vac.documentation":"Documentation","vac.residual_title":"Vérifier la calibration automatique","vac.residual_message":"Les pièces correspondantes présentent un écart maximal de {error}. Appliquez cette calibration approximative, affinez-la manuellement ou annulez sans modifier la configuration enregistrée.","vac.apply_proposal":"Appliquer","gs.backup_group":"Sauvegarde et transfert","gs.backup_hint":"Téléchargez une sauvegarde JSON portable ou prévisualisez et importez une sauvegarde créée par House Plan.","backup.export_open":"Exporter","backup.import_open":"Importer","backup.export_title":"Exporter House Plan","backup.import_title":"Importer House Plan","backup.export_hint":"Choisissez si la sauvegarde doit contenir toute la configuration House Plan ou uniquement l’espace actuel.","backup.full":"Sauvegarde complète","backup.current_space":"Espace actuel","backup.current_space_title":"Espace actuel : {title}","backup.no_current_space":"Aucun espace actuel","backup.plan_only":"Plan uniquement","backup.plan_only_hint":"Conserver les pièces, murs, ouvertures, décorations et positions des étiquettes de pièces sans les appareils ni les associations Home Assistant.","backup.plan_only_preview":"Ce fichier contient uniquement le plan","backup.privacy_warning":"L’archive conserve les noms, identifiants Home Assistant et coordonnées exactes. Les plans internes et pièces jointes sont référencés, pas intégrés ; les états d’exécution et trajets d’aspirateur ne sont pas inclus.","backup.download":"Télécharger le JSON","backup.export_done":"Sauvegarde téléchargée","backup.reading":"Vérification de la sauvegarde…","backup.revalidated":"Le plan a changé depuis cet aperçu. Le résumé a été actualisé ; vérifiez-le et confirmez de nouveau.","backup.error.unauthorized":"Vous n’avez pas l’autorisation d’exporter ou d’importer ce plan.","backup.error.not_ready":"House Plan n’est pas encore prêt. Réessayez dans un instant.","backup.error.too_large":"La sauvegarde dépasse la limite de 8 Mio.","backup.error.invalid_json":"Le fichier sélectionné n’est pas un JSON valide.","backup.error.invalid_format":"Le fichier sélectionné n’est pas une sauvegarde House Plan.","backup.error.unsupported_export_version":"Ce format de sauvegarde n’est pas pris en charge par la version installée.","backup.error.future_model":"La sauvegarde a été créée avec un modèle de données House Plan plus récent.","backup.error.invalid_config":"La sauvegarde contient une configuration House Plan invalide.","backup.error.wall_model_migration_blocked":"Le modèle de murs de la sauvegarde n’a pas pu être mis à niveau en toute sécurité. Optimisez d’abord le plan source, puis exportez-le de nouveau.","backup.error.invalid_layout":"La sauvegarde contient des positions d’objets invalides.","backup.error.invalid_content":"La sauvegarde contient des références de contenu invalides ou incohérentes.","backup.error.space_not_found":"L’espace sélectionné n’existe plus.","backup.error.capacity_exceeded":"L’ajout de cette sauvegarde dépasserait les limites du plan.","backup.error.preview_expired":"L’aperçu a expiré. Sélectionnez de nouveau le fichier de sauvegarde.","backup.error.preview_owner_mismatch":"Cet aperçu appartient à un autre utilisateur Home Assistant.","backup.error.conflict":"Le plan a changé depuis l’aperçu. Vérifiez le résumé actualisé.","backup.error.content_confirmation_required":"Confirmez que le contenu local indisponible peut être dissocié.","backup.error.commit_failed":"La sauvegarde n’a pas pu être appliquée en toute sécurité. Le plan précédent a été restauré ou est en attente de récupération.","backup.error.missing_plan":"Un fichier de plan référencé a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.missing_content":"Une pièce jointe locale référencée a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.marker_control_missing":"Une lumière liée au plan est absente de la sauvegarde.","backup.error.marker_control_not_light":"Une cible liée au plan n’est plus marquée comme source lumineuse.","backup.error.marker_control_self":"Une source lumineuse ne peut pas se contrôler elle-même.","backup.error.marker_control_cycle":"La sauvegarde contient une chaîne circulaire de contrôles de lumière.","backup.error.duplicate_marker_control":"La sauvegarde contient un lien de lumière du plan en doublon.","backup.error.no_backup":"Aucun instantané d’importation ou d’optimisation n’est disponible à restaurer.","backup.same_source":"Créée sur cette instance Home Assistant","backup.foreign_source":"Créée sur une autre instance Home Assistant","backup.created":"Créée : {value}","backup.versions":"Carte {card} ; intégration {integration} ; modèle de données {model}","backup.count_spaces":"Espaces : {n}","backup.count_rooms":"Pièces : {n}","backup.count_walls":"Murs : {n}","backup.count_openings":"Ouvertures : {n}","backup.count_decor":"Objets décoratifs : {n}","backup.count_markers":"Appareils : {n}","backup.count_layout":"Positions : {n}","backup.bindings":"Associations — appareils : {device}, entités : {entity}, virtuels : {virtual} ; positions sans espace : {legacy}","backup.binding_status":"État des cibles — actives : {active}, désactivées : {disabled}, manquantes : {missing}","backup.missing_areas":"Zones absentes sur la cible : {areas}","backup.dropped_marker_links":"Liens de lumières du plan hors de cet espace transféré omis : {n}.","backup.repaired_target_refs":"Références existantes restaurées par cet import d’espace : {n}.","backup.preserved_unresolved_refs":"Références qui n’ont pas pu être restaurées sans ambiguïté conservées : {n}.","backup.preserved_unresolved_hint":"Aucune donnée n’a été devinée ni supprimée. Après l’importation, lancez « Optimiser les plans » pour examiner les références restantes.","backup.import_details":"Détails des références d’importation","backup.import_detail.incoming_remapped":"Références mises à jour dans la copie importée : {n}","backup.import_detail.target_repaired":"Références existantes restaurées : {n}","backup.import_detail.preserved_unresolved":"Références non résolues conservées : {n}","backup.import_detail.collisions":"Conflits de destination conservés en toute sécurité : {n}","backup.import_detail.dropped_links":"Liens entrants omis selon les règles de transfert : {n}","backup.import_detail.bounded_lineages":"Identifiants trop imbriqués maintenus dans les limites : {n}","backup.replace_warning":"Cela remplace la configuration et la disposition actuelles. Les fichiers importés ne sont jamais supprimés. Une seule annulation reste disponible jusqu’à la prochaine modification du plan.","backup.foreign_bookkeeping":"Les données de suivi des appareils connus/nouveaux propres à l’instance ne seront pas importées.","backup.final_name":"Nom du nouvel espace","backup.target_settings":"Les paramètres généraux de l’instance cible restent inchangés.","backup.duplicates":"Associations déjà présentes : {n}","backup.skip":"Ignorer les appareils en doublon","backup.virtual_copy":"Ajouter des copies virtuelles statiques sûres","backup.content":"Contenu référencé","backup.content_available":"disponible localement","backup.content_external":"lien externe","backup.content_detach_required":"sera dissocié","backup.confirm_detach":"Je comprends que les plans internes et pièces jointes indisponibles seront dissociés de la configuration importée.","backup.replace":"Remplacer","backup.add":"Ajouter un espace","backup.space_done":"Espace importé : {rooms} pièces, {markers} appareils, {refs} références existantes restaurées","backup.full_done":"Sauvegarde restaurée : {spaces} espaces, {rooms} pièces, {markers} appareils","backup.undo_import":"Annuler la dernière importation complète","backup.import_undone":"Importation complète annulée","backdrop.large_title":"Grande image","backdrop.large_body":"Cette image fait {w}×{h} (fichier de {fileMb} Mo) et nécessite environ {decodedMb} Mo de mémoire pour s'afficher. Sur tablette, cela peut faire planter la page.","backdrop.unknown_body":"Les dimensions de l'image n'ont pas pu être lues — le fichier est peut-être endommagé. Continuer peut faire planter la page sur tablette.","backdrop.use_downscaled":"Téléverser une copie réduite","backdrop.reducing":"Réduction…","backdrop.keep_original":"Conserver l'original","backdrop.too_large_title":"Image trop grande","backdrop.too_large_body":"Cette image dépasse ce que les navigateurs peuvent afficher ({w}×{h}, limite {limit} px par côté). Réduisez-la dans un éditeur de bureau puis téléversez-la à nouveau.","backdrop.downscale_failed":"Impossible de créer la copie réduite. Le plan actuel n'a pas été modifié."};const a="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";export{e as dictionary,a as fingerprint}; diff --git a/dist/houseplan-assets/houseplan-card-B5mDyjuC.js b/dist/houseplan-assets/houseplan-card-wtxKVHkp.js similarity index 75% rename from dist/houseplan-assets/houseplan-card-B5mDyjuC.js rename to dist/houseplan-assets/houseplan-card-wtxKVHkp.js index 8e5c017f..bb07bc78 100644 --- a/dist/houseplan-assets/houseplan-card-B5mDyjuC.js +++ b/dist/houseplan-assets/houseplan-card-wtxKVHkp.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";const e=globalThis,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),n=new WeakMap;let r=class{constructor(e,t,n){if(this._$cssResult$=!0,n!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const i=this.t;if(t&&void 0===e){const t=void 0!==i&&1===i.length;t&&(e=n.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&n.set(i,e))}return e}toString(){return this.cssText}};const o=(e,...t)=>{const n=1===e.length?e[0]:t.reduce((t,i,n)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[n+1],e[0]);return new r(n,e,i)},s=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new r("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,m=p.trustedTypes,_=m?m.emptyScript:"",f=p.reactiveElementPolyfillSupport,g=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?_:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},y=(e,t)=>!a(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=b){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const i=Symbol(),n=this.getPropertyDescriptor(e,i,t);void 0!==n&&l(this.prototype,e,n)}}static getPropertyDescriptor(e,t,i){const{get:n,set:r}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:n,set(t){const o=n?.call(this);r?.call(this,t),this.requestUpdate(e,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??b}static _$Ei(){if(this.hasOwnProperty(g("elementProperties")))return;const e=u(this);e.finalize(),void 0!==e.l&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(g("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(g("properties"))){const e=this.properties,t=[...h(e),...d(e)];for(const i of t)this.createProperty(i,e[i])}const e=this[Symbol.metadata];if(null!==e){const t=litPropertyMetadata.get(e);if(void 0!==t)for(const[e,i]of t)this.elementProperties.set(e,i)}this._$Eh=new Map;for(const[e,t]of this.elementProperties){const i=this._$Eu(e,t);void 0!==i&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(s(e))}else void 0!==e&&t.push(s(e));return t}static _$Eu(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),void 0!==this.renderRoot&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const i of t.keys())this.hasOwnProperty(i)&&(e.set(i,this[i]),delete this[i]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,n)=>{if(t)i.adoptedStyleSheets=n.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of n){const n=document.createElement("style"),r=e.litNonce;void 0!==r&&n.setAttribute("nonce",r),n.textContent=t.cssText,i.appendChild(n)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$ET(e,t){const i=this.constructor.elementProperties.get(e),n=this.constructor._$Eu(e,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$Em=null}}_$AK(e,t){const i=this.constructor,n=i._$Eh.get(e);if(void 0!==n&&this._$Em!==n){const e=i.getPropertyOptions(n),r="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=n;const o=r.fromAttribute(t,e.type);this[n]=o??this._$Ej?.get(n)??o,this._$Em=null}}requestUpdate(e,t,i,n=!1,r){if(void 0!==e){const o=this.constructor;if(!1===n&&(r=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(r,t)||i.useDefault&&i.reflect&&r===this._$Ej?.get(e)&&!this.hasAttribute(o._$Eu(e,i))))return;this.C(e,t,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:i,reflect:n,wrapped:r},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==r||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===n&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}const e=this.constructor.elementProperties;if(e.size>0)for(const[t,i]of e){const{wrapped:e}=i,n=this[t];!0!==e||this._$AL.has(t)||void 0===n||this.C(t,void 0,i,n)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[g("elementProperties")]=new Map,w[g("finalized")]=new Map,f?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=e=>e,$=k.trustedTypes,S=$?$.createPolicy("lit-html",{createHTML:e=>e}):void 0,M="$lit$",R=`lit$${Math.random().toFixed(9).slice(2)}$`,T="?"+R,C=`<${T}>`,D=document,z=()=>D.createComment(""),A=e=>null===e||"object"!=typeof e&&"function"!=typeof e,P=Array.isArray,O="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,E=/>/g,H=RegExp(`>|${O}(?:([^\\s"'>=/]+)(${O}*=${O}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),N=/'/g,L=/"/g,W=/^(?:script|style|textarea|title)$/i,q=e=>(t,...i)=>({_$litType$:e,strings:t,values:i}),B=q(1),j=q(2),U=Symbol.for("lit-noChange"),V=Symbol.for("lit-nothing"),G=new WeakMap,K=D.createTreeWalker(D,129);function Y(e,t){if(!P(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(t):t}const X=(e,t)=>{const i=e.length-1,n=[];let r,o=2===t?"":3===t?"":"",s=F;for(let t=0;t"===l[0]?(s=r??F,c=-1):void 0===l[1]?c=-2:(c=s.lastIndex-l[2].length,a=l[1],s=void 0===l[3]?H:'"'===l[3]?L:N):s===L||s===N?s=H:s===I||s===E?s=F:(s=H,r=void 0);const d=s===H&&e[t+1].startsWith("/>")?" ":"";o+=s===F?i+C:c>=0?(n.push(a),i.slice(0,c)+M+i.slice(c)+R+d):i+R+(-2===c?t:d)}return[Y(e,o+(e[i]||"")+(2===t?"":3===t?"":"")),n]};class Z{constructor({strings:e,_$litType$:t},i){let n;this.parts=[];let r=0,o=0;const s=e.length-1,a=this.parts,[l,c]=X(e,t);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(n=K.nextNode())&&a.length0){n.textContent=$?$.emptyScript:"";for(let i=0;iP(e)||"function"==typeof e?.[Symbol.iterator])(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==V&&A(this._$AH)?this._$AA.nextSibling.data=e:this.T(D.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,n="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===n)this._$AH.p(t);else{const e=new Q(n,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=G.get(e.strings);return void 0===t&&G.set(e.strings,t=new Z(e)),t}k(e){P(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,n=0;for(const r of e)n===t.length?t.push(i=new ee(this.O(z()),this.O(z()),this,this.options)):i=t[n],i._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=V}_$AI(e,t=this,i,n){const r=this.strings;let o=!1;if(void 0===r)e=J(this,e,t,0),o=!A(e)||e!==this._$AH&&e!==U,o&&(this._$AH=e);else{const n=e;let s,a;for(e=r[0],s=0;s{const n=i?.renderBefore??t;let r=n._$litPart$;if(void 0===r){const e=i?.renderBefore??null;n._$litPart$=r=new ee(t.insertBefore(z(),e),e,void 0,i??{})}return r._$AI(e),r},ce=globalThis;let he=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=le(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return U}};he._$litElement$=!0,he.finalized=!0,ce.litElementHydrateSupport?.({LitElement:he});const de=ce.litElementPolyfillSupport;de?.({LitElement:he}),(ce.litElementVersions??=[]).push("4.2.2");const ue=new WeakMap;let pe=0;class me extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++pe,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=e=>{if("Escape"===e.key){e.preventDefault(),e.stopImmediatePropagation(),this._pruneOverlays();const t=this._overlays[this._overlays.length-1];return t?void this._closeOverlay(t,"escape"):void this._requestClose()}if("Tab"!==e.key||this._useHaDialog)return;const t=this.renderRoot.querySelector(".close"),i=t?[t,...this._focusableElements()]:this._focusableElements();if(!i.length)return e.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const n=this._deepActiveElement(),r=i[0],o=i[i.length-1];!e.shiftKey||n!==r&&i.includes(n)?e.shiftKey||n!==o||(e.preventDefault(),r.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=ue.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),ue.set(e,t),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const e=[...this._overlays];this._overlays=[];for(const t of e.reverse())t.close("disconnect");const t=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const n=t?ue.get(t):void 0;n?.dialogs.delete(this),super.disconnectedCallback(),t&&n&&requestAnimationFrame(()=>{const e=ue.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void ue.delete(t);const n=i?.closest("hp-dialog");i?.isConnected&&n&&e.dialogs.has(n)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._useHaDialog){const e=this.renderRoot.querySelector("dialog");e&&!e.open&&e.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let e=document.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e&&e!==document.body?e:null}_focusableElements(){const e=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),t=[],i=new Set,n=r=>{if(!i.has(r)){if(i.add(r),r instanceof HTMLElement){if(r.matches(e)&&t.push(r),r instanceof HTMLSlotElement){for(const e of r.assignedNodes({flatten:!0}))n(e);return}if(r.shadowRoot){for(const e of r.shadowRoot.childNodes)n(e);return}}for(const e of r.childNodes)n(e)}};for(const e of this.childNodes)n(e);const r=this.overlayPortal();return r&&n(r),t.filter(e=>{let t=e;for(;t;){const e=getComputedStyle(t);if(t.hidden||t.inert||"true"===t.getAttribute("aria-hidden")||"none"===e.display||"hidden"===e.visibility)return!1;if(t=t.assignedSlot||t.parentElement||(t.getRootNode()instanceof ShadowRoot?t.getRootNode().host:null),t===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(e=>e.owner.isConnected)}_closeOverlay(e,t){const i=this._overlays.findIndex(t=>t.token===e.token);i>=0&&this._overlays.splice(i,1),e.close(t)}registerOverlay(e){this._pruneOverlays();const t=this._overlays.find(t=>t.owner===e.owner);t&&this._overlays.splice(this._overlays.indexOf(t),1);const i=e.group||"transient";for(const e of[...this._overlays].reverse())e.group===i&&this._closeOverlay(e,"exclusive");const n={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(n);let r=!1;return()=>{if(r)return;r=!0;const e=this._overlays.findIndex(e=>e.token===n.token);e>=0&&this._overlays.splice(e,1)}}closeTransientOverlays(e="outside"){this._pruneOverlays();const t=[...this._overlays].filter(e=>"transient"===(e.group||"transient"));for(const i of t.reverse())this._closeOverlay(i,e);return t.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const e=B` +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";const e=globalThis,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),n=new WeakMap;let r=class{constructor(e,t,n){if(this._$cssResult$=!0,n!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const i=this.t;if(t&&void 0===e){const t=void 0!==i&&1===i.length;t&&(e=n.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&n.set(i,e))}return e}toString(){return this.cssText}};const o=(e,...t)=>{const n=1===e.length?e[0]:t.reduce((t,i,n)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[n+1],e[0]);return new r(n,e,i)},s=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new r("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,m=p.trustedTypes,_=m?m.emptyScript:"",f=p.reactiveElementPolyfillSupport,g=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?_:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},y=(e,t)=>!a(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=b){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const i=Symbol(),n=this.getPropertyDescriptor(e,i,t);void 0!==n&&l(this.prototype,e,n)}}static getPropertyDescriptor(e,t,i){const{get:n,set:r}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:n,set(t){const o=n?.call(this);r?.call(this,t),this.requestUpdate(e,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??b}static _$Ei(){if(this.hasOwnProperty(g("elementProperties")))return;const e=u(this);e.finalize(),void 0!==e.l&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(g("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(g("properties"))){const e=this.properties,t=[...h(e),...d(e)];for(const i of t)this.createProperty(i,e[i])}const e=this[Symbol.metadata];if(null!==e){const t=litPropertyMetadata.get(e);if(void 0!==t)for(const[e,i]of t)this.elementProperties.set(e,i)}this._$Eh=new Map;for(const[e,t]of this.elementProperties){const i=this._$Eu(e,t);void 0!==i&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(s(e))}else void 0!==e&&t.push(s(e));return t}static _$Eu(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),void 0!==this.renderRoot&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const i of t.keys())this.hasOwnProperty(i)&&(e.set(i,this[i]),delete this[i]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,n)=>{if(t)i.adoptedStyleSheets=n.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of n){const n=document.createElement("style"),r=e.litNonce;void 0!==r&&n.setAttribute("nonce",r),n.textContent=t.cssText,i.appendChild(n)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$ET(e,t){const i=this.constructor.elementProperties.get(e),n=this.constructor._$Eu(e,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$Em=null}}_$AK(e,t){const i=this.constructor,n=i._$Eh.get(e);if(void 0!==n&&this._$Em!==n){const e=i.getPropertyOptions(n),r="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=n;const o=r.fromAttribute(t,e.type);this[n]=o??this._$Ej?.get(n)??o,this._$Em=null}}requestUpdate(e,t,i,n=!1,r){if(void 0!==e){const o=this.constructor;if(!1===n&&(r=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(r,t)||i.useDefault&&i.reflect&&r===this._$Ej?.get(e)&&!this.hasAttribute(o._$Eu(e,i))))return;this.C(e,t,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:i,reflect:n,wrapped:r},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==r||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===n&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}const e=this.constructor.elementProperties;if(e.size>0)for(const[t,i]of e){const{wrapped:e}=i,n=this[t];!0!==e||this._$AL.has(t)||void 0===n||this.C(t,void 0,i,n)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[g("elementProperties")]=new Map,w[g("finalized")]=new Map,f?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=e=>e,$=k.trustedTypes,S=$?$.createPolicy("lit-html",{createHTML:e=>e}):void 0,M="$lit$",R=`lit$${Math.random().toFixed(9).slice(2)}$`,T="?"+R,C=`<${T}>`,D=document,z=()=>D.createComment(""),A=e=>null===e||"object"!=typeof e&&"function"!=typeof e,P=Array.isArray,O="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,E=/>/g,H=RegExp(`>|${O}(?:([^\\s"'>=/]+)(${O}*=${O}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),N=/'/g,L=/"/g,W=/^(?:script|style|textarea|title)$/i,q=e=>(t,...i)=>({_$litType$:e,strings:t,values:i}),B=q(1),j=q(2),U=Symbol.for("lit-noChange"),V=Symbol.for("lit-nothing"),G=new WeakMap,K=D.createTreeWalker(D,129);function Y(e,t){if(!P(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(t):t}const X=(e,t)=>{const i=e.length-1,n=[];let r,o=2===t?"":3===t?"":"",s=F;for(let t=0;t"===l[0]?(s=r??F,c=-1):void 0===l[1]?c=-2:(c=s.lastIndex-l[2].length,a=l[1],s=void 0===l[3]?H:'"'===l[3]?L:N):s===L||s===N?s=H:s===I||s===E?s=F:(s=H,r=void 0);const d=s===H&&e[t+1].startsWith("/>")?" ":"";o+=s===F?i+C:c>=0?(n.push(a),i.slice(0,c)+M+i.slice(c)+R+d):i+R+(-2===c?t:d)}return[Y(e,o+(e[i]||"")+(2===t?"":3===t?"":"")),n]};class Z{constructor({strings:e,_$litType$:t},i){let n;this.parts=[];let r=0,o=0;const s=e.length-1,a=this.parts,[l,c]=X(e,t);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(n=K.nextNode())&&a.length0){n.textContent=$?$.emptyScript:"";for(let i=0;iP(e)||"function"==typeof e?.[Symbol.iterator])(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==V&&A(this._$AH)?this._$AA.nextSibling.data=e:this.T(D.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,n="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===n)this._$AH.p(t);else{const e=new Q(n,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=G.get(e.strings);return void 0===t&&G.set(e.strings,t=new Z(e)),t}k(e){P(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,n=0;for(const r of e)n===t.length?t.push(i=new ee(this.O(z()),this.O(z()),this,this.options)):i=t[n],i._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=V}_$AI(e,t=this,i,n){const r=this.strings;let o=!1;if(void 0===r)e=J(this,e,t,0),o=!A(e)||e!==this._$AH&&e!==U,o&&(this._$AH=e);else{const n=e;let s,a;for(e=r[0],s=0;s{const n=i?.renderBefore??t;let r=n._$litPart$;if(void 0===r){const e=i?.renderBefore??null;n._$litPart$=r=new ee(t.insertBefore(z(),e),e,void 0,i??{})}return r._$AI(e),r},ce=globalThis;let he=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=le(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return U}};he._$litElement$=!0,he.finalized=!0,ce.litElementHydrateSupport?.({LitElement:he});const de=ce.litElementPolyfillSupport;de?.({LitElement:he}),(ce.litElementVersions??=[]).push("4.2.2");const ue=new WeakMap;let pe=0;class me extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++pe,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=e=>{if("Escape"===e.key){e.preventDefault(),e.stopImmediatePropagation(),this._pruneOverlays();const t=this._overlays[this._overlays.length-1];return t?void this._closeOverlay(t,"escape"):void this._requestClose()}if("Tab"!==e.key||this._useHaDialog)return;const t=this.renderRoot.querySelector(".close"),i=t?[t,...this._focusableElements()]:this._focusableElements();if(!i.length)return e.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const n=this._deepActiveElement(),r=i[0],o=i[i.length-1];!e.shiftKey||n!==r&&i.includes(n)?e.shiftKey||n!==o||(e.preventDefault(),r.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=ue.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),ue.set(e,t),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const e=[...this._overlays];this._overlays=[];for(const t of e.reverse())t.close("disconnect");const t=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const n=t?ue.get(t):void 0;n?.dialogs.delete(this),super.disconnectedCallback(),t&&n&&requestAnimationFrame(()=>{const e=ue.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void ue.delete(t);const n=i?.closest("hp-dialog");i?.isConnected&&n&&e.dialogs.has(n)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._useHaDialog){const e=this.renderRoot.querySelector("dialog");e&&!e.open&&e.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let e=document.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e&&e!==document.body?e:null}_focusableElements(){const e=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),t=[],i=new Set,n=r=>{if(!i.has(r)){if(i.add(r),r instanceof HTMLElement){if(r.matches(e)&&t.push(r),r instanceof HTMLSlotElement){for(const e of r.assignedNodes({flatten:!0}))n(e);return}if(r.shadowRoot){for(const e of r.shadowRoot.childNodes)n(e);return}}for(const e of r.childNodes)n(e)}};for(const e of this.childNodes)n(e);const r=this.overlayPortal();return r&&n(r),t.filter(e=>{let t=e;for(;t;){const e=getComputedStyle(t);if(t.hidden||t.inert||"true"===t.getAttribute("aria-hidden")||"none"===e.display||"hidden"===e.visibility)return!1;if(t=t.assignedSlot||t.parentElement||(t.getRootNode()instanceof ShadowRoot?t.getRootNode().host:null),t===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(e=>e.owner.isConnected)}_closeOverlay(e,t){const i=this._overlays.findIndex(t=>t.token===e.token);i>=0&&this._overlays.splice(i,1),e.close(t)}registerOverlay(e){this._pruneOverlays();const t=this._overlays.find(t=>t.owner===e.owner);t&&this._overlays.splice(this._overlays.indexOf(t),1);const i=e.group||"transient";for(const e of[...this._overlays].reverse())e.group===i&&this._closeOverlay(e,"exclusive");const n={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(n);let r=!1;return()=>{if(r)return;r=!0;const e=this._overlays.findIndex(e=>e.token===n.token);e>=0&&this._overlays.splice(e,1)}}closeTransientOverlays(e="outside"){this._pruneOverlays();const t=[...this._overlays].filter(e=>"transient"===(e.group||"transient"));for(const i of t.reverse())this._closeOverlay(i,e);return t.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const e=B` ${this.icon?B``:V} ${this.title} `;return this._useHaDialog?B`
`})} -
`}const tr={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},ir=(e,t=1)=>{const i=Number(e);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):t},nr=e=>{let t=Number(e);return Number.isFinite(t)?(t=(t%360+360)%360,t>180?t-360:t):0};function rr(e,t,i){return nr(t)?[e.x,e.y]:i([e.x,e.y])}function or(e,t,i,n){const r=Math.abs(i[0]-t[0]),o=Math.abs(i[1]-t[1]);return"line"===e?Math.hypot(r,o)>=n:r>=n&&o>=n}const sr=(e,t,i)=>{const n=Number(e),r=Number.isFinite(t)&&t>0?t:5;return Number.isFinite(n)&&n>0?n/r*i:0},ar=(e,t,i)=>{const n=Number(e),r=Number.isFinite(t)&&t>0?t:5;return Number.isFinite(n)&&i>0?n/i*r:0},lr=(e,t,i,n=tr.widthCm)=>{const r=Number(e?.width_cm);if(Number.isFinite(r)&&r>0)return r;const o=Number(e?.width);return Number.isFinite(o)&&o>0?ar(o,t,i):n};function cr(e){const t=tr;return e.color===t.color&&e.opacity===t.opacity&&e.widthCm===t.widthCm&&e.fill===t.fill&&e.fillColor===t.fillColor&&e.fillOpacity===t.fillOpacity?null:{color:e.color,opacity:ir(e.opacity),width_cm:Math.max(.1,Math.min(100,Number(e.widthCm)||.1)),fill:!!e.fill,fill_color:e.fillColor,fill_opacity:ir(e.fillOpacity,.25)}}function hr(e,t){return{color:e.color,opacity:ir(e.opacity),width_cm:Math.max(.1,Math.min(100,Number(e.widthCm)||.1)),...t?{fill:e.fill,fill_color:e.fillColor,fill_opacity:ir(e.fillOpacity,.25)}:{}}}function dr(e){const t=e.x+e.w/2,i=e.y+e.h/2,n=nr(e.angle)*Math.PI/180,r=Math.cos(n),o=Math.sin(n),s=(e,n)=>{const s=e-t,a=n-i;return[t+s*r-a*o,i+s*o+a*r]};return[s(e.x,e.y),s(e.x+e.w,e.y),s(e.x+e.w,e.y+e.h),s(e.x,e.y+e.h)]}function ur(e){const t=dr(e),i=(e,t)=>[(e[0]+t[0])/2,(e[1]+t[1])/2],n=[(t[0][0]+t[2][0])/2,(t[0][1]+t[2][1])/2];return{points:[...t,i(t[0],t[1]),i(t[1],t[2]),i(t[2],t[3]),i(t[3],t[0]),n],segments:t.map((e,i)=>({a:e,b:t[(i+1)%4]}))}}function pr(e,t,i,n,r,o,s,a){const l=nr(e.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l),d=-Math.sin(l),u=Math.cos(l),p=e.x+e.w/2,m=e.y+e.h/2,_=t>0?-e.w/2:e.w/2,f=i>0?-e.h/2:e.h/2,g=p+_*c+f*d,v=m+_*h+f*u,y=n-g,b=r-v;let w=(y*c+b*h)*(t>0?1:-1),k=(y*d+b*u)*(i>0?1:-1),x=null;if(o){const t=w/Math.max(e.w,a),i=k/Math.max(e.h,a);x=Math.max(a/Math.max(e.w,a),a/Math.max(e.h,a),t,i),w=e.w*x,k=e.h*x}if(s>0)if(o){const t=Math.max(1,Math.round(e.w/s)),i=Math.max(1,Math.round(e.h/s));if(Math.abs(e.w-t*s)<1e-6&&Math.abs(e.h-i*s)<1e-6){const n=(e,t)=>{let i=Math.abs(e),n=Math.abs(t);for(;n;)[i,n]=[n,i%n];return Math.max(1,i)},r=1/n(t,i),o=Math.max(a/e.w,a/e.h),s=Math.max(o,Math.round((x??1)/r)*r);w=e.w*s,k=e.h*s}else{e.w>=e.h?(w=Math.round(w/s)*s,k=w*(e.h/Math.max(e.w,a))):(k=Math.round(k/s)*s,w=k*(e.w/Math.max(e.h,a)))}}else w=Math.round(w/s)*s,k=Math.round(k/s)*s;w=Math.max(a,w),k=Math.max(a,k);return{x:g+(t>0?w/2:-w/2)*c+(i>0?k/2:-k/2)*d-w/2,y:v+(t>0?w/2:-w/2)*h+(i>0?k/2:-k/2)*u-k/2,w:w,h:k,angle:nr(e.angle)||void 0}}const mr=(e,t,i)=>{const n=i[0]-t[0],r=i[1]-t[1],o=n*n+r*r;if(o<1e-12)return[...t];const s=Math.max(0,Math.min(1,((e[0]-t[0])*n+(e[1]-t[1])*r)/o));return[t[0]+n*s,t[1]+r*s]};function _r(e,t,i,n){const r=n(e);let o=null;for(const r of t.points){const t=n(r),s=Math.hypot(t[0]-e[0],t[1]-e[1]);s<=i&&(!o||se.points),segments:e.flatMap(e=>e.segments)}}const gr=Math.tan(.25*Math.PI/180);function vr(e,t){const i=Math.abs(Number(t[0])-Number(e[0])),n=Math.abs(Number(t[1])-Number(e[1]));if(!([i,n].every(Number.isFinite)&&i>0&&n>0))return null;const r=i>=n?"horizontal":"vertical",o=Math.max(i,n),s=Math.min(i,n);return s/o>gr?null:{axis:r,major:o,minor:s,angleDegrees:180*Math.atan2(s,o)/Math.PI}}function yr(e,t){const i=vr(e,t);return i?"horizontal"===i.axis?[Number(t[0]),Number(e[1])]:[Number(e[0]),Number(t[1])]:[Number(t[0]),Number(t[1])]}const br=e=>`${e[0]},${e[1]}`,wr=(e,t)=>e[0]===t[0]&&e[1]===t[1],kr=(e,t)=>{const i=br(e),n=br(t);return i{let t=0;for(let i=0;i{if(e.length<3)return!1;for(let t=0;t1e-12},Sr=e=>{for(let t=0;t+1wr(e,t.from)?[...t.to]:[...e],Rr=(e,t)=>e.map(e=>Array.isArray(e?.poly)?{...e,poly:e.poly.map(e=>Mr(e,t))}:e),Tr=(e,t,i)=>{const n=e.flatMap((e,t)=>Et(e)?.some(e=>wr(e,i.from))?[t]:[]);if(!n.length)return!1;for(const i of n){const n=Et(e[i]),r=Et(t[i]);if(!n||!r)continue;if(!$r(r))return!1;const o=Math.sign(xr(n)),s=Math.sign(xr(r));if(o&&s&&o!==s)return!1}const r=new Set;for(const e of n){const i=Et(t[e]);if(i)for(let n=0;n{let i=0;for(const n of e){const e=Et(n);if(e)for(let n=0;n{const{a:i,b:n,axis:r}=e,o="horizontal"===r?{from:n,to:[n[0],i[1]]}:{from:n,to:[i[0],n[1]]},s="horizontal"===r?{from:i,to:[i[0],n[1]]}:{from:i,to:[n[0],i[1]]};return[{move:o,preservedDegree:Cr(t,i)},{move:s,preservedDegree:Cr(t,n)}].sort((e,t)=>t.preservedDegree-e.preservedDegree||br(e.move.to).localeCompare(br(t.move.to))||br(e.move.from).localeCompare(br(t.move.from))).map(e=>e.move)};function zr(e){const t=JSON.parse(JSON.stringify(e||{}));let i=Array.isArray(t.rooms)?t.rooms:[];const n=new Map;for(const e of i){const t=Et(e);if(t)for(let e=0;ee.key.localeCompare(t.key))){let t=null;for(const n of Dr(e,i)){const e=a.get(br(n.from));if(e&&e!==br(n.to))continue;const r=Rr(i,n);if(Tr(i,r,n)){t=n,i=r;break}}t?(a.set(br(t.from),br(t.to)),r++,s=Math.max(s,Math.hypot(t.to[0]-t.from[0],t.to[1]-t.from[1]))):o++}t.rooms=i;for(const e of t.room_drafts||[]){if(!Array.isArray(e?.points)||e.points.length<2)continue;const t=e.points.map(e=>[...e]),i=t.slice(0,-1).flatMap((i,n)=>{const r=t[n+1],o=vr(i,r);return o?[{key:`${String(e.id||"")}:${n}`,a:[...i],b:[...r],axis:o.axis}]:[]});let n=t;for(const e of i){let t=null;for(const i of Dr(e,[{poly:n}])){const e=n.map(e=>Mr(e,i)),r=e.length>=4&&wr(e[0],e[e.length-1]),o=r?e.slice(0,-1):e;if(r?$r(o):Sr(o)){t=i,n=e;break}}t?(r++,s=Math.max(s,Math.hypot(t.to[0]-t.from[0],t.to[1]-t.from[1]))):o++}e.points=n}for(const e of t.partitions||[]){if(!Array.isArray(e?.a)||!Array.isArray(e?.b))continue;const i=vr(e.a,e.b);if(!i)continue;const n={key:String(e.id||kr(e.a,e.b)),a:[...e.a],b:[...e.b],axis:i.axis};let a=null;for(const i of Dr(n,[{poly:[e.a,e.b]}])){const n=Mr(e.a,i),r=Mr(e.b,i),o=Math.hypot(r[0]-n[0],r[1]-n[1]);if(!(o>0))continue;const s=(t.openings||[]).filter(t=>"partition"===t?.host?.kind&&t.host.id===e.id).every(e=>{const t=Number(e.host.t),i=Number(e.length);return Number.isFinite(t)&&t>=0&&t<=1&&Number.isFinite(i)&&i>0&&t*o-i/2>=-1e-12&&t*o+i/2<=o+1e-12});if(s){a=i,e.a=n,e.b=r;break}}a?(r++,s=Math.max(s,Math.hypot(a.to[0]-a.from[0],a.to[1]-a.from[1]))):o++}return{space:t,report:{wallsStraightened:r,wallsStraightenSkipped:o,maxStraightenShift:s},changed:r>0}}const Ar=10**9,Pr=240,Or=1e-4;function Fr(e){if(Array.isArray(e))return e.map(e=>Fr(e));if(null!==e&&"object"==typeof e){const t={};for(const[i,n]of Object.entries(e))t[i]=Fr(n);return t}return e}function Ir(e){if("number"!=typeof e||!Number.isFinite(e))return e;const t=(e<0||Object.is(e,-0)?-1:1)*(Math.floor(Math.abs(e)*Ar+.5)/Ar);return 0===t?0:t}function Er(e){if("number"!=typeof e||!Number.isFinite(e))return e;const t=e*Pr,i=Math.round(t);if(Math.abs(t-i)null!==Nr(e)):[]}function Wr(e,t){for(const i of t)Object.prototype.hasOwnProperty.call(e,i)&&(e[i]=Ir(e[i]))}function qr(e,t){for(const i of t)Object.prototype.hasOwnProperty.call(e,i)&&(e[i]=Er(e[i]))}function Br(e){if(Array.isArray(e))for(let t=0;t{for(const r of n){const n=t[r];"number"==typeof n&&Number.isFinite(n)&&i(n,e)}},n=t=>{if(Array.isArray(t))for(let n=0;n{if(Array.isArray(e))for(const t of e)n(t)};for(const i of Lr(e.rooms))t(i,["x","y","w","h"]),r(i.poly);for(const t of Lr(e.walls))n(t.a),n(t.b);for(const t of Lr(e.wall_segments))n(t.a),n(t.b);for(const i of Lr(e.openings))t(i,["x","y"]);for(const i of Lr(e.decor))"line"===i.kind?t(i,["x1","y1","x2","y2"]):"rect"===i.kind||"ellipse"===i.kind||"furniture"===i.kind?t(i,["x","y","w","h"]):"text"===i.kind&&t(i,["x","y"]);for(const t of Lr(e.room_drafts))r(t.points);for(const t of Lr(e.partitions))n(t.a),n(t.b);for(const t of Lr(e.wall_columns))n(t.center);for(const t of Lr(e.open_spans))n(t.a),n(t.b)}const r=Nr(t);if(!r)return;const o=new Map;if(n)for(const e of Lr(n.spaces))null!=e.id&&o.set(String(e.id),e);for(const e of Object.values(r)){const t=Nr(e);if(!t)continue;const n=null!=t.s&&o.get(String(t.s))||null;for(const e of["x","y"]){const r=t[e];"number"==typeof r&&Number.isFinite(r)&&i(r,n)}}}function Vr(e){const t=Number(e?.cell_cm);return t>0?t:5}function Gr(e,t={}){const i={canonicalized:0,far:0,maxShift:0,maxShiftCm:0,spaces:[],bySpace:new Map},n=Lr(Nr(e)?.spaces);let r=5;for(const e of n)r=Math.max(r,Vr(e));Ur(e,t,(e,t)=>{const n=e*Pr,o=Math.abs(n-Math.round(n)),s=Er(e),a=o>0&&o=Or))return;const l=a&&"number"==typeof s?Math.abs(s-e):0;if(a?i.canonicalized++:i.far++,i.maxShift=Math.max(i.maxShift,l),i.maxShiftCm=Math.max(i.maxShiftCm,l*Pr*(t?Vr(t):r)),!t?.id||!a)return;const c=String(t.id);let h=i.bySpace.get(c);h||(h={spaceId:c,space:String(t.title||c),canonicalized:0,far:0,maxShift:0,maxShiftCm:0},i.bySpace.set(c,h)),h.canonicalized++,h.maxShift=Math.max(h.maxShift,l),h.maxShiftCm=Math.max(h.maxShiftCm,l*Pr*Vr(t))}),i.bySpace.size&&Ur(e,t,(e,t)=>{if(!t?.id)return;const n=i.bySpace.get(String(t.id));if(!n)return;const r=e*Pr;Math.abs(r-Math.round(r))>=Or&&n.far++}),i.spaces=[...i.bySpace.values()];const{bySpace:o,...s}=i;return s}function Kr(e){return function(e){const t=e,i=Nr(t);i&&qr(i,["x","y"]);return t}(Fr(e))}function Yr(e){const t=e,i=Nr(t);if(!i)return t;for(const e of Object.values(i)){const t=Nr(e);t&&qr(t,["x","y"])}return t}function Xr(e){return Zr(Fr(e))}function Zr(e){const t=e,i=Nr(t);if(!i)return t;for(const e of Lr(i.spaces)){Wr(e,["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"]);for(const t of Lr(e.rooms))qr(t,["x","y","w","h"]),jr(t.poly);for(const t of Lr(e.walls))Br(t.a),Br(t.b);for(const t of Lr(e.wall_segments))Br(t.a),Br(t.b);for(const t of Lr(e.openings)){qr(t,["x","y"]),Wr(t,["angle","length"]);const e=Nr(t.host);e&&Wr(e,["t"])}for(const t of Lr(e.decor))"line"===t.kind?qr(t,["x1","y1","x2","y2"]):"rect"===t.kind||"ellipse"===t.kind||"furniture"===t.kind?(qr(t,["x","y","w","h"]),Wr(t,["angle"])):"text"===t.kind&&(qr(t,["x","y"]),Wr(t,["scale","angle"]));for(const t of Lr(e.room_drafts))jr(t.points);for(const t of Lr(e.partitions))Br(t.a),Br(t.b);for(const t of Lr(e.wall_columns))Br(t.center),"square"===t.shape&&Wr(t,["angle"]);for(const t of Lr(e.open_spans))Br(t.a),Br(t.b)}for(const e of Lr(i.markers))Wr(e,["angle"]);return t}const Jr=15,Qr=8;function eo(e,t,i,n,r){const o=i[0]-e[0],s=i[1]-e[1],a=Math.hypot(o,s);if(!(a>0)||a<=r+1e-9)return null;const l=o/a,c=s/a,h=t=>{const n=(t[0]-e[0])*l+(t[1]-e[1])*c,o=(r-n)/(a-n);return!Number.isFinite(o)||o<-1e-9||o>1+1e-9?null:[t[0]+(i[0]-t[0])*o,t[1]+(i[1]-t[1])*o]},d=h(t),u=h(n);return d&&u?[[e[0],e[1]],t,d,u,n]:null}const to=Math.sin(.25*Math.PI/180);function io(e,t){return Number.isFinite(e)&&e>0&&Number.isFinite(t)&&t>0&&e*t<3}function no(e){const t=Number(e)>0?Number(e):5;if(5===t)return 8;const i=5/t*8;return Math.min(80,Math.max(.5,i))}function ro(e,t){return Number.isFinite(e)&&e>0&&Number.isFinite(t)&&t>0&&e*t<2}function oo(e){return Number.isFinite(e)?Math.max(1,Math.min(100,e)):1}function so(e,t){return!Number.isFinite(e)||e<0?"":String(t?Math.round(e/2.54*100)/100:Math.round(100*e)/100)}function ao(e,t,i){if(!Number.isFinite(e)||e<=0)return 0;const n=Number(t)>0?Number(t):5;return oo(e)/n*i}function lo(e,t){return t>0&&Number.isFinite(e)?Math.round(e/t)*t:e}function co(e){return Math.max(1e-6*Math.abs(e),1e-9)}function ho(e,t){if(!(t>0&&Number.isFinite(e)))return e;const i=lo(e,t);return Math.abs(i-e)<=co(t)?i:e}function uo(e,t){let i=t[0]-e[0],n=t[1]-e[1];const r=Math.hypot(i,n);return r<1e-12?[1,0]:(i/=r,n/=r,(i<-1e-12||Math.abs(i)<=1e-12&&n<0)&&(i=-i,n=-n),[i,n])}function po(e,t,i){const n=[ho(e[0],i),ho(e[1],i)],r=[ho(t[0],i),ho(t[1],i)],o=lo((n[0]+r[0])/2,i),s=lo((n[1]+r[1])/2,i),[a,l]=uo(n,r);let c=Math.atan2(l,a);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)},${s.toFixed(d)}@${h.toFixed(4)}`}function mo(e,t,i,n){return 1===n?po(e,t,i):po([e[0]/n,e[1]/n],[t[0]/n,t[1]/n],i)}function _o(e,t){if(!Array.isArray(e.a)||!Array.isArray(e.b)||e.a.length<2||e.b.length<2)return null;const i=[Number(e.a[0]),Number(e.a[1]),Number(e.b[0]),Number(e.b[1])];if(!i.every(Number.isFinite))return null;const n=t>0?t:1;return[[i[0]*n,i[1]*n],[i[2]*n,i[3]*n]]}function fo(e,t,i,n,r){const o=r>0?r:1;return{key:mo(e,t,n,o),cm:oo(i),a:[e[0]/o,e[1]/o],b:[t[0]/o,t[1]/o]}}function go(e,t){const i=t>0?t:1,n=[];for(const t of e){const e=t.key.lastIndexOf("@");if(e<0)continue;const[r,o]=t.key.slice(0,e).split(",").map(Number),s=Number(t.key.slice(e+1));[r,o,s].every(Number.isFinite)&&n.push({w:t,x:r*i,y:o*i,ang:s})}return n}function vo(e,t){const[i,n]=uo(e,t);let r=Math.atan2(n,i);return r<0&&(r+=Math.PI),r}function yo(e,t){let i=Math.abs(e-t);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function bo(e,t,i,n,r=1){if(!e?.length)return null;const o=mo(t,i,n,r),s=e.find(e=>e.key===o);if(s)return s;const a=r>0?r:1,l=co(n)*a,c=(e,t)=>Math.abs(e[0]-t[0])<=l&&Math.abs(e[1]-t[1])<=l;for(const n of e){const e=_o(n,a);if(e&&(c(e[0],t)&&c(e[1],i)||c(e[0],i)&&c(e[1],t)))return n}const h=(t[0]+i[0])/2,d=(t[1]+i[1])/2,u=vo(t,i),p=Math.max(.5*n,1e-9)*a;for(const t of go(e,a))if(yo(t.ang,u)&&Math.hypot(t.x-h,t.y-d)<=p)return t.w;return null}function wo(e,t,i,n,r=1){const o=bo(e,t,i,n,r);if(o&&o.cm>0)return oo(o.cm);const s=function(e,t,i,n,r){if(!e?.length)return null;const o=r>0?r:1,s=Math.hypot(i[0]-t[0],i[1]-t[1]);if(s<1e-12)return null;const a=vo(t,i),l=Math.max(.5*n,1e-9)*o;let c=null;for(const n of e){if(!(n.cm>0))continue;const e=_o(n,o);if(!e)continue;const r=Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1]);if(r<1e-12||!yo(vo(e[0],e[1]),a))continue;if(hs(t[0],t[1],e[0][0],e[0][1],e[1][0],e[1][1])>l||hs(i[0],i[1],e[0][0],e[0][1],e[1][0],e[1][1])>l)continue;if(r+l(o.has(e.key)||(e=>{const t=_o(e,n);if(!t)return!1;const[i,o]=t,a=o[0]-i[0],c=o[1]-i[1],h=Math.hypot(a,c);if(h<=l)return!1;if(!s.some(e=>{const t=[e[0],e[1]],n=[e[2],e[3]];return yo(vo(i,o),vo(t,n))&&hs(i[0],i[1],t[0],t[1],n[0],n[1])<=l&&hs(o[0],o[1],t[0],t[1],n[0],n[1])<=l}))return!1;const d=(r||[]).some(e=>{const t=[e[0],e[1]],n=[e[2],e[3]];if(!yo(vo(i,o),vo(t,n)))return!1;const r=e=>Math.abs((e[0]-i[0])*c-(e[1]-i[1])*a)/h;if(r(t)>l||r(n)>l)return!1;const s=h*h,d=((t[0]-i[0])*a+(t[1]-i[1])*c)/s,u=((n[0]-i[0])*a+(n[1]-i[1])*c)/s;return Math.min(1,Math.max(d,u))-Math.max(0,Math.min(d,u))>l/h});return!d})(e))&&e.cm>=1&&e.cm<=100)}function xo(e,t,i,n=8){const[r,o]=uo(e,t);let s=Math.atan2(o,r);s<0&&(s+=Math.PI);let a=i*Math.PI/180%Math.PI;a<0&&(a+=Math.PI);let l=Math.abs(s-a);return l>Math.PI/2&&(l=Math.PI-l),l<=n*Math.PI/180}function $o(e,t,i,n,r=1,o="affine",s){if(!e?.length)return[];if(t.length!==i.length)return"fixed-topology"===o&&s?.(),e.slice();const a=r>0?r:1,l=Math.max(.5*n,1e-9)*a,c=Math.max(n*a*1e-6,1e-9),h=[],d=new Map,u=new Map,p=(e,t,i)=>{const n=e.get(t)||new Set;n.add(i),e.set(t,n)};for(let e=0;e[e[0]+(t[0]-e[0])*i,e[1]+(t[1]-e[1])*i],_=(e,t)=>Math.hypot(e[0]-t[0],e[1]-t[1])<=c,f=(e,t)=>{if("fixed-topology"===o){const i=t.na[0]-t.oa[0],n=t.na[1]-t.oa[1],r=t.nb[0]-t.ob[0],o=t.nb[1]-t.ob[1];return Math.hypot(i-r,n-o)<=c?[e[0]+i,e[1]+n]:_(e,t.oa)?[...t.na]:_(e,t.ob)?[...t.nb]:[...e]}const i=Math.max(0,Math.min(1,((e[0]-t.oa[0])*t.dx+(e[1]-t.oa[1])*t.dy)/t.len2));return m(t.na,t.nb,i)},g=(e,t)=>{const[i,n]=uo(e,t);return(t[0]-e[0])*i+(t[1]-e[1])*n>=0?[[...e],[...t]]:[[...t],[...e]]},v=[],y=[],b=(e,t,i)=>{if(Math.hypot(t[0]-e[0],t[1]-e[1])<=c)return;const[r,o]=g(e,t),s=oo(i);if(y.some(e=>e.entry.cm===s&&_(e.span[0],r)&&_(e.span[1],o)))return;const l=fo(r,o,s,n,a);v.push(l),y.push({entry:l,span:[r,o]})};for(const t of e){const e=_o(t,a);if(e){const[i,n]=g(e[0],e[1]),r=n[0]-i[0],s=n[1]-i[1],a=r*r+s*s,d=Math.sqrt(a);if(d<=c){v.push({...t,cm:oo(t.cm)});continue}const u=[];for(const e of h){if(!yo(vo(i,n),vo(e.oa,e.ob)))continue;const t=e=>Math.abs((e[0]-i[0])*s-(e[1]-i[1])*r)/d;if(t(e.oa)>l||t(e.ob)>l)continue;const o=((e.oa[0]-i[0])*r+(e.oa[1]-i[1])*s)/a,h=((e.ob[0]-i[0])*r+(e.ob[1]-i[1])*s)/a,p=Math.max(0,Math.min(o,h)),m=Math.min(1,Math.max(o,h));(m-p)*d>c&&u.push({lo:p,hi:m,move:e})}if(!u.length){"fixed-topology"===o?v.push({...t}):b(i,n,t.cm);continue}const p=[0,1,...u.flatMap(({lo:e,hi:t})=>[e,t])].sort((e,t)=>e-t).filter((e,t,i)=>0===t||Math.abs(e-i[t-1])*d>c),y=[];for(let e=0;e+1a>=e.lo-1e-12&&a<=e.hi+1e-12);if(!l.length){y.push([o,s]);continue}const h=[f(o,l[0].move),f(s,l[0].move)],g=l.slice(1).some(e=>{const t=f(o,e.move),i=f(s,e.move);return!_(h[0],t)||!_(h[1],i)});y.push(g?[o,s]:h)}const w=(e,t)=>{const i=e[1][0]-e[0][0],n=e[1][1]-e[0][1],r=t[1][0]-t[0][0],o=t[1][1]-t[0][1],s=Math.hypot(i,n);return!(s<=c||i*r+n*o<=0)&&Math.abs(i*o-n*r)/s<=c},k=[];for(const e of y){const t=k[k.length-1];t&&_(t[1],e[0])&&w(t,e)?t[1]=e[1]:k.push([[...e[0]],[...e[1]]])}if("fixed-topology"===o&&1===k.length){const[i,n]=k[0];if(_(i,e[0])&&_(n,e[1])||_(i,e[1])&&_(n,e[0])){v.push({...t});continue}}for(const[e,i]of k)b(e,i,t.cm);continue}if("fixed-topology"===o){const e=u.get(t.key);if(1===e?.size){const i=[...e][0];v.push(i===t.key?{...t}:{...t,key:i});continue}const i=[go([t],a)[0]];1!==a&&i.push(go([t],1)[0]);const n=i.filter(Boolean).some(e=>h.some(t=>{if(!yo(e.ang,vo(t.oa,t.ob)))return!1;const i=((e.x-t.oa[0])*t.dx+(e.y-t.oa[1])*t.dy)/t.len2;return i>=-1e-6&&i<=1.000001&&hs(e.x,e.y,t.oa[0],t.oa[1],t.ob[0],t.ob[1])<=l}));((e?.size||0)>1||n)&&s?.(),v.push({...t});continue}let i="";const r=d.get(t.key);if(1===r?.size&&(i=[...r][0]),!i){const e=go([t],a)[0];if(e){const t=new Set;for(const i of h){if(!yo(e.ang,vo(i.oa,i.ob)))continue;const r=((e.x-i.oa[0])*i.dx+(e.y-i.oa[1])*i.dy)/i.len2;if(r<-1e-6||r>1.000001)continue;if(hs(e.x,e.y,i.oa[0],i.oa[1],i.ob[0],i.ob[1])>l)continue;const o=f([e.x,e.y],i),[s,c]=uo(i.na,i.nb),h=Math.max(n*a,1e-6);t.add(mo([o[0]-s*h,o[1]-c*h],[o[0]+s*h,o[1]+c*h],n,a))}1===t.size&&(i=[...t][0])}}v.push({...t,key:i||t.key,cm:oo(t.cm)})}return v}function So(e,t,i,n,r=1,o="affine"){return $o(e,t,i,n,r,o)}function Mo(e,t,i,n,r=1,o="fixed-topology"){let s=!1;const a=$o(e,t,i,n,r,o,()=>{s=!0});return{walls:s?(e||[]).map(e=>({...e})):a,rejected:s}}function Ro(e,t,i,n=1,r=[]){const o=n>0?n:1,s=Math.abs(i),a=Math.max(s*o*Or,1e-9),l=(r||[]).flatMap(e=>{const t=_o(e,o);return t?t.map(e=>[...e]):[]}),c=e=>e.every((e,t)=>(e=>{if(!(s>0))return!0;const t=e/o/s;return Math.abs(t-Math.round(t))Math.abs(e-i[t])<=a)),h=[],d=e=>JSON.stringify([e.key,e.cm,e.a,e.b]);for(const i of e||[]){const e=_o(i,o);if(!e)continue;const[n,r]=e;if(![n[0],n[1],r[0],r[1]].every(Number.isFinite)||!c(n)||!c(r)){h.push(d(i));continue}const s=r[0]-n[0],l=r[1]-n[1],u=Math.hypot(s,l);if(u<=a){h.push(d(i));continue}const p=s/u,m=l/u,_=[];for(const e of t){const[t,i]=e;if(![t?.[0],t?.[1],i?.[0],i?.[1]].every(Number.isFinite))continue;const r=e=>Math.abs((e[0]-n[0])*m-(e[1]-n[1])*p);if(r(t)>a||r(i)>a)continue;const o=(t[0]-n[0])*p+(t[1]-n[1])*m,s=(i[0]-n[0])*p+(i[1]-n[1])*m,l=Math.max(0,Math.min(o,s)),c=Math.min(u,Math.max(o,s));c-l>a&&_.push([l,c])}_.sort((e,t)=>e[0]-t[0]||e[1]-t[1]);let f=0;for(const[e,t]of _){if(e>f+a)break;if(f=Math.max(f,t),f>=u-a)break}fe.key!==s);return null==n||n<1?a:[...a,fo(t,i,n,r,o)]}function Co(e,t,i,n,r,o=[],s=1){let a=e?e.slice():[];for(const e of function(e,t,i,n,r=1,o=[]){const s=Xo(e,t,i,n,r,o);if(!s)return[];const a=[];for(let e=0;e0||(c=To(c,t,i,n,r,s)))}return c}function zo(e){const{a:t,b:i,halfDepth:n}=e;if(!Array.isArray(t)||!Array.isArray(i)||t.length<2||i.length<2||![t[0],t[1],i[0],i[1]].every(Number.isFinite))return null;const r=i[0]-t[0],o=i[1]-t[1],s=Math.hypot(r,o);if(!(s>1e-9&&n>0&&Number.isFinite(n)))return null;const a=-o/s*n,l=r/s*n;return[[t[0]+a,t[1]+l],[i[0]+a,i[1]+l],[i[0]-a,i[1]-l],[t[0]-a,t[1]-l]]}function Ao(e,t,i){return Math.hypot(e[0]-t[0],e[1]-t[1])<=i}function Po(e,t,i){const n=t.b[0]-t.a[0],r=t.b[1]-t.a[1],o=n*n+r*r;if(!(o>i*i))return!1;const s=((e[0]-t.a[0])*n+(e[1]-t.a[1])*r)/o;if(!(s>0&&s<1))return!1;const a=[t.a[0]+n*s,t.a[1]+r*s];return Math.hypot(e[0]-a[0],e[1]-a[1])<=i}function Oo(e,t,i,n){const r=Math.hypot(t,i);if(!(r>1e-9&&n>0))return;const o=[t/r,i/r],s=e.find(e=>Math.abs(e.u[0]*o[1]-e.u[1]*o[0])<1e-9&&e.u[0]*o[0]+e.u[1]*o[1]>1-1e-9);s?s.halfDepth=Math.max(s.halfDepth,n):e.push({u:o,halfDepth:n})}function Fo(e,t){try{const i=Mt(ps(e),ps(t));let n=null,r=0;for(const e of i||[]){const t=e?.[0]||[],i=Math.abs(Wo(t));t.length>=4&&i>r&&(r=i,n=t.slice(0,-1).map(e=>[e[0],e[1]]))}return n}catch{return null}}function Io(e,t=1e-6){const i=(e||[]).map((e,t)=>({segment:e,index:t})).filter(({segment:e})=>e&&Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2&&e.a.every(Number.isFinite)&&e.b.every(Number.isFinite)&&Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>1e-9);if(i.length<2)return[];const n=Math.max(Number.isFinite(t)?t:0,1e-9),r=i.flatMap(({segment:e})=>[e.a,e.b]).map(e=>[e[0],e[1]]).sort((e,t)=>e[0]-t[0]||e[1]-t[1]),o=[];for(const e of r)o.some(t=>Ao(t,e,n))||o.push(e);const s=[];for(const e of o){const t=[];let r=!1;for(const{segment:o,index:s}of i){const i=Math.hypot(o.b[0]-o.a[0],o.b[1]-o.a[1]);Ao(e,o.a,n)?t.push({u:[(o.b[0]-o.a[0])/i,(o.b[1]-o.a[1])/i],halfDepth:o.halfDepth,length:i,index:s}):Ao(e,o.b,n)?t.push({u:[(o.a[0]-o.b[0])/i,(o.a[1]-o.b[1])/i],halfDepth:o.halfDepth,length:i,index:s}):Po(e,o,n)&&(r=!0)}if(r||2!==t.length)continue;const[o,a]=t,l=o.u[0]*a.u[1]-o.u[1]*a.u[0];if(Math.abs(l)<1e-9)continue;const c=l<0?1:-1,h=[-o.u[1],o.u[0]],d=[-a.u[1],a.u[0]],u=[e[0]+h[0]*o.halfDepth*c,e[1]+h[1]*o.halfDepth*c],p=[e[0]-d[0]*a.halfDepth*c,e[1]-d[1]*a.halfDepth*c];if(!jo(u,o.u,p,a.u))continue;const m=[[o,a,p,[d[0]*-c,d[1]*-c]],[a,o,u,[h[0]*c,h[1]*c]]];for(const[t,,i,r]of m){const o=Math.min(2*t.halfDepth,t.length),a=[-t.u[1]*t.halfDepth,t.u[0]*t.halfDepth],l=[[e[0]+a[0],e[1]+a[1]],[e[0]+t.u[0]*o+a[0],e[1]+t.u[1]*o+a[1]],[e[0]+t.u[0]*o-a[0],e[1]+t.u[1]*o-a[1]],[e[0]-a[0],e[1]-a[1]]],c=e=>(e[0]-i[0])*r[0]+(e[1]-i[1])*r[1],h=[];for(let e=0;e=-1e-12&&h.push(t),n>1e-12&&r<-1e-12||n<-1e-12&&r>1e-12){const e=n/(n-r);h.push([t[0]+(i[0]-t[0])*e,t[1]+(i[1]-t[1])*e])}}h.length>=3&&Math.abs(Wo(h))>n*n&&s.push({segmentIndex:t.index,wedge:h})}}return s}function Eo(e,t=1e-6){const i=(e||[]).filter(e=>e&&Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2&&e.a.every(Number.isFinite)&&e.b.every(Number.isFinite)&&Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>1e-9);if(i.length<2)return[];const n=Math.max(Number.isFinite(t)?t:0,1e-9),r=i.flatMap(e=>[e.a,e.b]).map(e=>[e[0],e[1]]).sort((e,t)=>e[0]-t[0]||e[1]-t[1]),o=[];for(const e of r)o.some(t=>Ao(t,e,n))||o.push(e);const s=[],a=i.map((e,t)=>({roomId:"",a:[e.a[0],e.a[1]],b:[e.b[0],e.b[1]],key:`join-${t}`,kind:"outer",cm:0,open:!1,half:e.halfDepth})),l=ts(a,n);for(const e of is(l).fans)s.push(e);const c=e=>!!ns(l,e);for(const e of o){if(c(e))continue;const t=[];for(const r of i)Ao(e,r.a,n)?Oo(t,r.b[0]-r.a[0],r.b[1]-r.a[1],r.halfDepth):Ao(e,r.b,n)?Oo(t,r.a[0]-r.b[0],r.a[1]-r.b[1],r.halfDepth):Po(e,r,n)&&(Oo(t,r.a[0]-e[0],r.a[1]-e[1],r.halfDepth),Oo(t,r.b[0]-e[0],r.b[1]-e[1],r.halfDepth));if(!(t.length<2)){t.sort((e,t)=>Math.atan2(e.u[1],e.u[0])-Math.atan2(t.u[1],t.u[0])||e.halfDepth-t.halfDepth);for(let i=0;in*n&&s.push(_)}}}return s}function Ho(e,t,i,n){if(!(t>0)||!e||e.length<2)return"";if(i&&e.length>=3){let i=e;const r=e[e.length-1];if(e.length>=4&&Math.hypot(e[0][0]-r[0],e[0][1]-r[1])<1e-9&&(i=e.slice(0,-1)),i.length>=3){const e=i.map((e,i)=>n?.[i]||t),r=As(i,e),o=Go(i,e);if(r&&o)return`${Ko(r)} ${Ko(Yo(o))}`}}const r=[];for(let i=0;i=1e-9&&a>0&&r.push({a:o,b:s,halfDepth:a})}const o=r.map(zo);for(const{segmentIndex:e,wedge:t}of Io(r)){const i=o[e];if(!i)continue;const n=Fo(i,t);n&&(o[e]=n)}const s=[...o.filter(e=>!!e),...Eo(r)],a=function(e){let t=null;try{for(const i of e){if(i.length<3)continue;const e=ps(i);t=t?$t(t,e):[e]}return t}catch{return null}}(s);return a?Ss(a):s.map(e=>Ko(e)).join(" ")}function No(e,t,i,n,r=1){if(!i.length)return!1;const o=Lo(n,r),s=(e[0]+t[0])/2,a=(e[1]+t[1])/2,[l,c]=uo(e,t);for(const e of i){const[t,i]=uo([e[0],e[1]],[e[2],e[3]]);if(!(Math.abs(l*i-c*t)>.05)&&hs(s,a,e[0],e[1],e[2],e[3])<=o)return!0}return!1}function Lo(e,t){return Math.max(e*(t>0?t:1)*.04,1e-9)}function Wo(e){let t=0;for(let i=0;ie[1]!=l>e[1]&&e[0]<(a-o)*(e[1]-s)/(l-s+0)+o&&(i=!i)}return i}([c[0]+.001*a,c[1]+.001*l],e)||(a=-a,l=-l),[a,l]}function Bo(e,t){const i=e[0]*t[1]-e[1]*t[0],n=e[0]*t[0]+e[1]*t[1];return Math.abs(i)<1e-9&&n>0}function jo(e,t,i,n){const r=t[0]*n[1]-t[1]*n[0];if(Math.abs(r)<1e-12)return null;const o=[i[0]-e[0],i[1]-e[1]],s=(o[0]*n[1]-o[1]*n[0])/r;return[e[0]+s*t[0],e[1]+s*t[1]]}function Uo(e,t,i){const n=e?.length||0;if(n<3||!Array.isArray(i)||i.length!==n)return 0;const r=(Math.trunc(t)%n+n)%n,o=e[r],s=e[(r+1)%n],a=s[0]-o[0],l=s[1]-o[1],c=Math.hypot(a,l);if(!(c>0))return 0;const h=Math.max(0,Number(i[r])||0);if(!(h>0))return c;const d=[a/c,l/c],u=qo(e,r),p=[o[0]+u[0]*h,o[1]+u[1]*h],m=t=>{const r=Math.max(0,Number(i[t])||0);if(!(r>0))return null;const s=e[t],a=e[(t+1)%n],l=a[0]-s[0],c=a[1]-s[1],h=Math.hypot(l,c);if(!(h>0))return null;const u=qo(e,t),m=jo(p,d,[s[0]+u[0]*r,s[1]+u[1]*r],[l/h,c/h]);return m?(m[0]-o[0])*d[0]+(m[1]-o[1])*d[1]:null},_=m((r-1+n)%n)??0,f=(m((r+1)%n)??c)-_;return f>0?f:0}function Vo(e,t,i,n,r,o,s,a=1){const l=(e||[]).find(e=>e?.id===t),c=Et(l);if(!c||c.length<3)return null;const h=os(e,t,i,n,r,o,s,a);if(!h)return c.map(()=>0);const d=4*Lo(r,a);return c.map((e,t)=>{const i=c[(t+1)%c.length],n=[(e[0]+i[0])/2,(e[1]+i[1])/2];for(let e=0;e!(e>0)))return e.map(e=>[e[0],e[1]]);const r=[];for(let o=0;o0||u>0)){r.push([e[o][0],e[o][1]]);continue}if(d>0!=u>0){const t=e[o],i=d>0?[t[0]+p*d,t[1]+m*d]:[t[0],t[1]],n=u>0?[t[0]+_*u,t[1]+f*u]:[t[0],t[1]];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}if(Bo(w,k)){const t=e[o],i=[t[0]+p*d,t[1]+m*d],n=[t[0]+_*u,t[1]+f*u];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}const S=jo(x,w,$,k),M=Math.max(d,u,1e-9),R=ns(i,e[o])?.limit??4*M;if(S){const i=Math.hypot(S[0]-e[o][0],S[1]-e[o][1]);if(Number.isFinite(i)&&i<=R){r.push(S);continue}if(Rs(e,t,o)){r.push(S);continue}}d>0&&r.push([e[o][0]+p*d,e[o][1]+m*d]),u>0&&r.push([e[o][0]+_*u,e[o][1]+f*u]),d>0||u>0||r.push([e[o][0],e[o][1]])}return r.length>=3?r:null}function Ko(e,t=!0){if(!e.length)return"";let i=`M ${e[0][0]} ${e[0][1]}`;for(let t=1;te?.id===t),a=Et(s);if(!a||a.length<3)return null;const l=Lo(n,r),c=[];for(const i of e||[]){if(!i||i.id===t)continue;const e=Et(i);if(e)for(const t of cn(a,e,l))c.push([t[0],t[1]],[t[2],t[3]])}for(const e of i||[])c.push([e[0],e[1]],[e[2],e[3]]);for(const e of o||[]){const t=_o(e,r);t&&c.push(t[0],t[1])}const h=[],d=[];for(let e=0;el)continue;const s=((e[0]-t[0])*(i[0]-t[0])+(e[1]-t[1])*(i[1]-t[1]))/(n*n);s<=r||s>=1-r||(o.some(e=>Math.abs(e-s)*n<=2*l)||o.push(s))}o.sort((e,t)=>e-t);for(const n of o)h.push([t[0]+(i[0]-t[0])*n,t[1]+(i[1]-t[1])*n]),d.push(e)}return{poly:h,parent:d,orig:a}}function Zo(e,t,i,n){const r=t.poly.length,o=new Array(r).fill(0);if(!e?.length)return o;const s=new Set,a=[];for(let l=0;l0?(o[l]=oo(c.cm),s.add(c.key)):a.push(l)}if(!a.length)return o;const l=n>0?n:1,c=Math.max(.5*i,1e-9)*l,h=go(e,l).filter(e=>e.w.cm>0);for(let e=a.length-1;e>=0;e--){const i=a[e],n=t.poly[i],s=t.poly[(i+1)%r],d=vo(n,s);let u=null;for(const e of h){const t=_o(e.w,l);if(!t||!yo(vo(t[0],t[1]),d))continue;if(hs(n[0],n[1],t[0][0],t[0][1],t[1][0],t[1][1])>c||hs(s[0],s[1],t[0][0],t[0][1],t[1][0],t[1][1])>c)continue;const i=Math.hypot(s[0]-n[0],s[1]-n[1]),r=Math.hypot(t[1][0]-t[0][0],t[1][1]-t[0][1]),o=Math.max(0,r-i);(!u||oc||hs(r[0],r[1],t[0][0],t[0][1],t[1][0],t[1][1])>c)continue;i=!0,o=Math.max(0,Math.hypot(t[1][0]-t[0][0],t[1][1]-t[0][1])-m)}else{if(hs(e.x,e.y,n[0],n[1],r[0],r[1])>c)continue;o=Math.hypot(e.x-d,e.y-u)}(!p||i&&!p.exact||i===p.exact&&o0?i:1,o=(e||[]).filter(e=>e&&!e.open&&null!==e.kind&&Number.isFinite(e.half)&&e.half>0&&Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2&&[e.a[0],e.a[1],e.b[0],e.b[1]].every(Number.isFinite)&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>n).sort((e,t)=>e.key.localeCompare(t.key)||e.a[0]-t.a[0]||e.a[1]-t.a[1]||e.b[0]-t.b[0]||e.b[1]-t.b[1]||e.half-t.half),s=new Map;for(const e of o){const t=s.get(e.key);t?e.half>t.half&&s.set(e.key,{...t,half:e.half}):s.set(e.key,e)}const a=[...s.values()].flatMap(e=>[{point:[e.a[0],e.a[1]],other:e.b,halfDepth:e.half,kind:e.kind,key:e.key},{point:[e.b[0],e.b[1]],other:e.a,halfDepth:e.half,kind:e.kind,key:e.key}]).sort((e,t)=>e.point[0]-t.point[0]||e.point[1]-t.point[1]||e.other[0]-t.other[0]||e.other[1]-t.other[1]||e.halfDepth-t.halfDepth),l=new Map;for(const e of a){const[t,i]=Jo(e.point,n),r=Qo(t,i),o=l.get(r)||[];o.push(e),l.set(r,o)}const c=[],h=new Map;for(const e of a){const t=es(h,e.point,n).filter(t=>Math.hypot(t.point[0]-e.point[0],t.point[1]-e.point[1])<=n).sort((t,i)=>Math.hypot(t.point[0]-e.point[0],t.point[1]-e.point[1])-Math.hypot(i.point[0]-e.point[0],i.point[1]-e.point[1])||t.point[0]-i.point[0]||t.point[1]-i.point[1]);let i=t[0];if(!i){i={point:[...e.point],rays:[]},c.push(i);const[t,r]=Jo(i.point,n),o=Qo(t,r),s=h.get(o)||[];s.push(i),h.set(o,s)}const r=e.other[0]-e.point[0],o=e.other[1]-e.point[1],s=Math.hypot(r,o);if(!(s>n))continue;const a=[r/s,o/s];let l=Math.atan2(a[1],a[0]);l<0&&(l+=2*Math.PI),i.rays.push({u:a,halfDepth:e.halfDepth,length:s,angle:l})}const d=[],u=e=>{const t=1e-9*Math.max(1,r),i=e.filter(e=>Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Number.isFinite(e.length)&&e.length>n);return i.filter((e,n)=>!i.some((i,r)=>r!==n&&i.halfDepth>=e.halfDepth-t&&i.length>=e.length-t&&(i.halfDepth>e.halfDepth+t||i.length>e.length+t||re.length-t.length||e.halfDepth-t.halfDepth).map(e=>({...e}))};for(const e of c){const t=e.rays.sort((e,t)=>e.angle-t.angle||e.length-t.length||e.halfDepth-t.halfDepth),i=[];for(const e of t){const t=i[i.length-1];t&&Math.abs(e.angle-t.angle)<=1e-9?t.supports.push({halfDepth:e.halfDepth,length:e.length}):i.push({u:[...e.u],angle:e.angle,supports:[{halfDepth:e.halfDepth,length:e.length}]})}if(i.length>1&&2*Math.PI-i[i.length-1].angle+i[0].angle<=1e-9){const e=i.pop();i[0].supports.push(...e.supports)}if(i.length<3)continue;const r=i.map(t=>{const i=u(t.supports),r=new Set,o=[];for(const s of i){const i=[e.point[0]+t.u[0]*s.length,e.point[1]+t.u[1]*s.length];for(const t of es(l,i,n)){if("shared"!==t.kind||Math.hypot(t.point[0]-i[0],t.point[1]-i[1])>n)continue;const s=t.other[0]-t.point[0],a=t.other[1]-t.point[1],l=Math.hypot(s,a);if(!(l>n))continue;const c=s/l,h=a/l;if(Math.hypot(t.other[0]-e.point[0],t.other[1]-e.point[1])<=n)continue;const d=`${t.key}|${t.point[0]}|${t.point[1]}|${t.other[0]}|${t.other[1]}|${t.halfDepth}`;r.has(d)||(r.add(d),o.push({start:[t.point[0],t.point[1]],u:[c,h],length:l,halfDepth:t.halfDepth}))}}return o.sort((e,t)=>e.start[0]-t.start[0]||e.start[1]-t.start[1]||e.u[0]-t.u[0]||e.u[1]-t.u[1]||e.length-t.length||e.halfDepth-t.halfDepth),{u:[...t.u],halfDepth:Math.max(...i.map(e=>e.halfDepth)),length:Math.max(...i.map(e=>e.length)),supports:i,continuations:o}}).filter(e=>Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Number.isFinite(e.length)&&e.length>n);if(r.length<3)continue;const o=Math.max(...r.map(e=>e.halfDepth));o>0&&Number.isFinite(o)&&d.push({point:[...e.point],rays:r,halfDepth:o,limit:1.25*o})}d.sort((e,t)=>e.point[0]-t.point[0]||e.point[1]-t.point[1]);const p=new Map;for(const e of d){const[t,i]=Jo(e.point,n),r=Qo(t,i),o=p.get(r)||[];o.push(e),p.set(r,o)}return{epsilon:n,coordinateScale:r,nodes:d,index:p}}function is(e){const t={fans:[],supports:[]};if(!e?.nodes?.length)return t;const i=Math.max(e.epsilon,1e-9)**2;for(const n of e.nodes){const e=n.rays.filter(e=>Number.isFinite(e.halfDepth)&&e.halfDepth>0).map(e=>({...e,thickLength:Math.max(...e.supports.filter(t=>t.halfDepth>=e.halfDepth-1e-12).map(e=>e.length),0),angle:(()=>{const t=Math.atan2(e.u[1],e.u[0]);return t<0?t+2*Math.PI:t})()})).sort((e,t)=>e.angle-t.angle);if(e.length<2)continue;const r=n.point;for(const i of e)for(const e of i.supports){if(!(e.halfDepth>0&&e.length>0))continue;const n=-i.u[1]*e.halfDepth,o=i.u[0]*e.halfDepth,s=[r[0]+i.u[0]*e.length,r[1]+i.u[1]*e.length];t.supports.push([[r[0]+n,r[1]+o],[s[0]+n,s[1]+o],[s[0]-n,s[1]-o],[r[0]-n,r[1]-o]])}for(let n=0;n{const e=s.angle-o.angle;return e>0?e:e+2*Math.PI})();if(a<1e-9)continue;const l=a>Math.PI+1e-9,c=4*Math.max(o.halfDepth,s.halfDepth),h=[r[0]-o.u[1]*o.halfDepth,r[1]+o.u[0]*o.halfDepth],d=[r[0]+s.u[1]*s.halfDepth,r[1]-s.u[0]*s.halfDepth],u=o.u[0]*s.u[1]-o.u[1]*s.u[0],p=e=>{let t=Math.atan2(e[1]-r[1],e[0]-r[0])-o.angle;for(;t<0;)t+=2*Math.PI;return t<=a+1e-9};let m=null;if(Math.abs(u)>1e-9){const e=((d[0]-h[0])*s.u[1]-(d[1]-h[1])*s.u[0])/u,t=((d[0]-h[0])*o.u[1]-(d[1]-h[1])*o.u[0])/u,i=[h[0]+o.u[0]*e,h[1]+o.u[1]*e];(l?e<=1e-9&&t<=1e-9:e>1e-9&&e<=o.thickLength&&t<=s.thickLength)&&Math.hypot(i[0]-r[0],i[1]-r[1])<=c&&p(i)&&(m=i)}const _=e=>{Math.abs(Wo(e))>i&&t.fans.push(e)};if(m){const e=1.5*Math.max(o.halfDepth,s.halfDepth);_(eo([r[0],r[1]],h,m,d,e)??[[r[0],r[1]],h,m,d]);continue}if(l){_([[r[0],r[1]],h,d]);continue}const f=(e,t)=>Math.min(t,Math.sqrt(Math.max(c**2-e**2,0)),2*Math.max(o.halfDepth,s.halfDepth)),g=[h[0]+o.u[0]*f(o.halfDepth,o.thickLength),h[1]+o.u[1]*f(o.halfDepth,o.thickLength)],v=[d[0]+s.u[0]*f(s.halfDepth,s.thickLength),d[1]+s.u[1]*f(s.halfDepth,s.thickLength)];_([[r[0],r[1]],h,g,v,d])}}return t}function ns(e,t){return!e||!Array.isArray(t)||t.length<2||!t.slice(0,2).every(Number.isFinite)?null:es(e.index,t,e.epsilon).filter(i=>Math.hypot(i.point[0]-t[0],i.point[1]-t[1])<=e.epsilon).sort((e,i)=>Math.hypot(e.point[0]-t[0],e.point[1]-t[1])-Math.hypot(i.point[0]-t[0],i.point[1]-t[1])||e.point[0]-i.point[0]||e.point[1]-i.point[1])[0]||null}function rs(e,t,i,n,r,o,s){return ts(ss(e,t,i,n,r,o,s),4*Lo(n,s),s)}function os(e,t,i,n,r,o,s,a=1){const l=Xo(e,t,n,r,a,i);if(!l)return null;const c=function(e,t,i){const n=(e||[]).find(e=>e?.id===t),r=Et(n);if(!r)return[];const o=[];for(const n of e||[]){if(!n||n.id===t)continue;const e=Et(n);if(e)for(const t of cn(r,e,i))o.push(t)}return o}(e,t,Lo(r,a)),h=function(e,t,i,n,r){const o=Lo(n,r),s=[];for(let a=0;ahs(h,d,e[0],e[1],e[2],e[3])<=o);s.push(u?"shared":"outer")}return s}(l.poly,c,n,r,a),d=Zo(i,l,r,a),u=d.map((e,t)=>h[t]&&e>0?ao(e,o,s)/2:0);return{...l,kinds:h,cms:d,offsets:u}}function ss(e,t,i,n,r,o,s=1){const a=[];for(const l of e||[]){if(!l?.id)continue;const c=os(e,l.id,t,i,n,r,o,s);if(c)for(let e=0;e0&&(a=To(a,e.a,e.b,e.cm,n,s));return a}function ls(e,t,i,n,r,o,s=1){if(!t?.length)return[];const a=ss(e,t,i,n,r,o,s),l=new Map;for(const e of a){if(e.open||!e.kind||!e.roomId)continue;const t=l.get(e.key)||new Set;t.add(e.roomId),l.set(e.key,t)}const c=e=>{const t=[...l.get(e)||[]].sort();return 1!==t.length&&2!==t.length?`ambiguous:${e}`:`${1===t.length?"outer":"shared"}:${t.join("|")}`},h=[],d=new Set;for(const e of a)!e.open&&e.cm>0&&!d.has(e.key)&&(d.add(e.key),h.push({...e,ownerSignature:c(e.key)}));const u=[];for(const a of e||[]){if(!a?.id)continue;const l=os(e,a.id,t,i,n,r,o,s);if(l)for(let e=0;e0)||null===l.kinds[i]){e++;continue}const o=c(mo(l.poly[i],l.poly[(i+1)%l.poly.length],n,s));let a=e;for(;a+10&&u.push({a:[d[0],d[1]],b:[p[0],p[1]],key:mo(d,p,n,s),cm:r,len:m,ownerSignature:o}),e=a+1}}}u.sort((e,t)=>t.len-e.len||e.key.localeCompare(t.key));const p=[],m=new Set,_=new Set,f=4*Lo(n,s);for(const e of u){const t=h.filter(t=>!_.has(t.key)&&t.cm===e.cm&&t.ownerSignature===e.ownerSignature&&yo(vo(t.a,t.b),vo(e.a,e.b))&&hs(t.a[0],t.a[1],e.a[0],e.a[1],e.b[0],e.b[1])<=f&&hs(t.b[0],t.b[1],e.a[0],e.a[1],e.b[0],e.b[1])<=f);if(t.length){for(const e of t)_.add(e.key);m.has(e.key)||(m.add(e.key),p.push(fo(e.a,e.b,e.cm,n,s)))}}for(const e of h)_.has(e.key)||m.has(e.key)||(m.add(e.key),p.push(fo(e.a,e.b,e.cm,n,s)));return p}function cs(e,t,i,n,r,o,s,a=1){const l=Lo(r,a),c=(n[0]+n[2])/2,h=(n[1]+n[3])/2,d=vo([n[0],n[1]],[n[2],n[3]]);let u=null;for(const n of ss(e,t,i,r,o,s,a)){if(!yo(vo(n.a,n.b),d))continue;const e=hs(c,h,n.a[0],n.a[1],n.b[0],n.b[1]);e>4*l||(!u||e[e[0],e[1]]),o=Math.abs(Wo(r));r.length>=3&&o>i&&(t=r,i=o)}return t}function us(e,t,i,n,r,o,s,a=1,l,c){const h=(e||[]).find(e=>e?.id===t),d=Et(h);if(!d||d.length<3)return null;if(!i?.length)return d.map(e=>[e[0],e[1]]);const u=os(e,t,i,n,r,o,s,a);if(!u||!u.offsets.some(e=>e>0))return d.map(e=>[e[0],e[1]]);const p=c||rs(e,i,n,r,o,s,a),m=Go(u.poly,u.offsets,p);if(!m)return d.map(e=>[e[0],e[1]]);if(!p.nodes.length)return m;const _=void 0===l?Ts(e,i,n,[],r,o,s,a):null,f=l??("ok"===_?.status||"degraded-extra"===_?.status?_.roomGeom:void 0);if(f)try{const e=ds(Mt(ps(u.poly),f));if(e)return e}catch{}return function(e,t){try{return ds(St(ps(e),ps(t)))}catch{return null}}(m,u.poly)||d.map(e=>[e[0],e[1]])}function ps(e){const t=e.map(e=>[e[0],e[1]]);return t.push([e[0][0],e[0][1]]),[t]}function ms(e){return!(!Array.isArray(e)||!e.length)&&e.every(e=>Array.isArray(e)&&e.length&&e.every(e=>Array.isArray(e)&&e.length>=4&&e.every(e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1])))&&Math.abs(li(e[0]))>1e-9)}function _s(e,t,i,n){const r=n?new Set(n):null;let o=null;for(let n=0;nt.epsilon))continue;const l=bs([[e.point[0]+a[0]*n.halfDepth,e.point[1]+a[1]*n.halfDepth],[e.point[0]+s.u[0]*r+a[0]*n.halfDepth,e.point[1]+s.u[1]*r+a[1]*n.halfDepth],[e.point[0]+s.u[0]*r-a[0]*n.halfDepth,e.point[1]+s.u[1]*r-a[1]*n.halfDepth],[e.point[0]-a[0]*n.halfDepth,e.point[1]-a[1]*n.halfDepth]],t.coordinateScale);if(!l)continue;const c=ps(l);o=o?$t(o,c):c}}return o}function fs(e,t,i){let n=null;for(const r of e.rays)for(const e of r.continuations){const r=[-e.u[1],e.u[0]],o=[e.start[0]+e.u[0]*e.length,e.start[1]+e.u[1]*e.length],s=bs([[e.start[0]+r[0]*e.halfDepth,e.start[1]+r[1]*e.halfDepth],[o[0]+r[0]*e.halfDepth,o[1]+r[1]*e.halfDepth],[o[0]-r[0]*e.halfDepth,o[1]-r[1]*e.halfDepth],[e.start[0]-r[0]*e.halfDepth,e.start[1]-r[1]*e.halfDepth]],t.coordinateScale);if(!s)continue;const a=St(ps(s),i);Array.isArray(a)&&0!==a.length&&(n=n?$t(n,a):a)}return n}function gs(e,t,i=2*(4*e.halfDepth+2*t.epsilon)){const n=function(e,t=to){const i=new Set,n=Number.isFinite(t)&&t>=0?t:to;for(let t=0;te-t)}(e);return n.length?_s(e,t,i,n):null}function vs(e,t,i,n,r){const o=function(e){let t=null;for(const i of e){const e=ps(i);try{t=t?$t(t,e):e}catch{}}return t}(function(e,t,i=!1){if(!e)return[];const n=[];for(const r of e.nodes)for(let o=0;o1e-9)||h>=Math.PI-1e-9)continue;const d=[-s.u[1],s.u[0]],u=[-a.u[1],a.u[0]],p=[r.point[0]+d[0]*s.halfDepth,r.point[1]+d[1]*s.halfDepth],m=[r.point[0]-u[0]*a.halfDepth,r.point[1]-u[1]*a.halfDepth],_=jo(p,s.u,m,a.u);if(!_)continue;const f=Math.hypot(_[0]-r.point[0],_[1]-r.point[1]);if(!Number.isFinite(f)||f<=r.limit)continue;const g=t?Math.sqrt(Math.max(0,r.limit*r.limit-s.halfDepth*s.halfDepth)):0,v=t?Math.sqrt(Math.max(0,r.limit*r.limit-a.halfDepth*a.halfDepth)):0,y=bs([[p[0]+s.u[0]*g,p[1]+s.u[1]*g],[m[0]+a.u[0]*v,m[1]+a.u[1]*v],_],e.coordinateScale);if(y&&n.push(y),i){const t=_[0]-r.point[0],i=_[1]-r.point[1],o=Math.hypot(t,i),s=f-r.limit;if(o>e.epsilon&&s>e.epsilon){const a=Math.min(Math.max(8*e.epsilon,.05*r.halfDepth),.25*s),l=t/o,c=i/o,h=-c,d=l,u=bs([[_[0]-l*a+h*a,_[1]-c*a+d*a],[_[0]-l*a-h*a,_[1]-c*a-d*a],[_[0]+l*a,_[1]+c*a]],e.coordinateScale);u&&n.push(u)}}}return n}({...t,nodes:[e]},i,n));return o&&r?Mt(o,r):o}function ys(e,t,i,n){if(!e||!t.nodes.length)return e;let r=null;try{r=function(e){let t=null;for(const i of e.nodes){const n=gs(i,e);n&&(t=t?$t(t,n):n)}return t}(t)}catch{return e}let o=e;for(const e of t.nodes){const s=4*e.halfDepth+2*t.epsilon,a=2*s,l=[[e.point[0]-s,e.point[1]-s],[e.point[0]+s,e.point[1]-s],[e.point[0]+s,e.point[1]+s],[e.point[0]-s,e.point[1]+s]];try{let s=o;const c=vs(e,t,!1,!0,r);c&&(s=Mt(s,c));let h=_s(e,t,a);const d=vs(e,t,!0,!0,r);d&&(h=Mt(h,d)),r&&(h=$t(h,r));const u=.02*Math.min(...e.rays.map(e=>e.halfDepth));if(h=$t(h,ps([[e.point[0]-u,e.point[1]-u],[e.point[0]+u,e.point[1]-u],[e.point[0]+u,e.point[1]+u],[e.point[0]-u,e.point[1]+u]])),!h)continue;let p=St(h,ps(l));n?p=St(p,n):i&&(p=St(p,i));const m=ps(l),_=Mt(s,m),f=i?Mt(St(s,m),i):null,g=fs(e,t,m);o=$t(_,...f?[f]:[],...g?[g]:[],p)}catch{}}if(r)try{let e=r;n?e=St(e,n):i&&(e=St(e,i)),o=$t(o,e)}catch{}return o}function bs(e,t=1){if(!Array.isArray(e)||e.length<3)return null;const i=Number.isFinite(t)&&t>0?t:1,n=1e-12*Math.max(1,i),r=[];for(const t of e){if(!Array.isArray(t)||t.length<2)return null;const e=Number(t[0]),i=Number(t[1]);if(!Number.isFinite(e)||!Number.isFinite(i))return null;const o=Math.round(e/n)*n,s=Math.round(i/n)*n;if(!Number.isFinite(o)||!Number.isFinite(s))return null;r.push([Object.is(o,-0)?0:o,Object.is(s,-0)?0:s])}return Math.abs(Wo(r))>n*n?r:null}function ws(e,t,i,n){if(hs(e[0],e[1],t[0],t[1],i[0],i[1])>n)return!1;const r=i[0]-t[0],o=i[1]-t[1],s=(e[0]-t[0])*r+(e[1]-t[1])*o,a=r*r+o*o,l=n*Math.sqrt(a);return s>=-l&&s<=a+l}function ks(e,t,i){const n=[],r=[];for(let o=0;oi*i))continue;const d=i/Math.sqrt(h),u=[0,1];for(const e of t)for(const t of[e.a,e.b]){if(!ws(t,s,a,i))continue;const e=((t[0]-s[0])*l+(t[1]-s[1])*c)/h;e>d&&e<1-d&&u.push(e)}u.sort((e,t)=>e-t);const p=u.filter((e,t)=>0===t||Math.abs(e-u[t-1])>d);for(let e=0;e=3&&r.length===n.length?{poly:n,offsets:r}:null}function xs(e,t,i,n,r,o,s,a){const l=(e||[]).map(Et).filter(e=>!!e&&e.length>=3);if(!l.length)return null;let c=$t(ps(l[0]));for(let e=1;e"outer"===e.kind&&e.half>0),u=4*Lo(n,s),p=a||ts(h,u,s);let m=null;for(const e of function(e){const t=[];for(const i of Array.isArray(e)?e:[])if(Array.isArray(i))for(const e of i){if(!Array.isArray(e)||e.length<4)continue;const i=e.slice(0,-1).map(e=>[e[0],e[1]]);i.length>=3&&t.push(i)}return t}(c)){const t=ks(e,d,u);if(!t||!t.offsets.some(e=>e>0))continue;const i=As(t.poly,t.offsets,p),n=Go(t.poly,t.offsets,p);if(!i||!n)continue;const r=Mt(ps(i),ps(n));m=m?$t(m,r):r}return{centre:c,shell:m||[]}}function $s(e,t,i,n,r,o,s=1){try{const a=xs(e,t,i,n,r,o,s,rs(e,t,i,n,r,o,s));if(!a)return[];return a.shell?.length?$t(a.centre,a.shell):a.centre}catch{return null}}function Ss(e){if(!e)return"";let t="";for(const i of e)if(Array.isArray(i))for(const e of i){if(!Array.isArray(e)||e.length<4)continue;const i=e.slice(0,e.length-1);i.length<3||(t+=(t?" ":"")+Ko(i.map(e=>[e[0],e[1]])))}return t}const Ms=15;function Rs(e,t,i){const n=e?.length||0;if(n<3||t?.length!==n)return!1;const r=e[(i-1+n)%n],o=e[i],s=e[(i+1)%n],a=[r[0]-o[0],r[1]-o[1]],l=[s[0]-o[0],s[1]-o[1]],c=Math.hypot(a[0],a[1]),h=Math.hypot(l[0],l[1]);if(!(c>1e-9&&h>1e-9))return!1;const d=Math.max(-1,Math.min(1,(a[0]*l[0]+a[1]*l[1])/(c*h))),u=Math.acos(d);if(!(u>1e-9))return!1;if(u>=Ms*Math.PI/180)return!1;const p=Math.max(0,t[(i-1+n)%n]),m=Math.max(0,t[i]);if(!(p>0||m>0))return!1;const _=Math.sin(u);if(!(_>1e-12))return!1;const f=(m+p*d)/_,g=(p+m*d)/_;return Number.isFinite(f)&&Number.isFinite(g)&&f>0&&g>0&&fe>0))continue;for(const e of l.offsets)e>0&&(u=Math.max(u,2*e));const c=As(l.poly,l.offsets,d),p=Go(l.poly,l.offsets,d);c&&h.push({outset:c,inset:p})}for(const e of l){const t=e.map(e=>e[0]),i=e.map(e=>e[1]);if(t.length){const n=Math.min(Math.max(...t)-Math.min(...t),Math.max(...i)-Math.min(...i)),r=Math.min(...e.map((t,i)=>{const n=e[(i+1)%e.length];return Math.hypot(n[0]-t[0],n[1]-t[1])}));u=Math.max(u,e.length>16?n:r)}}const p=function(e,t,i,n,r,o,s,a){if(!t?.length||!i?.length)return[];const l=4*Lo(n,s),c=new Map;for(const a of ss(e,t,i,n,r,o,s))!a.open&&a.half>0&&!c.has(a.key)&&c.set(a.key,a);const h=[...c.values()];if(h.length<2)return[];const d=a||ts(h,l),u=[];for(const e of i)for(const t of[[e[0],e[1]],[e[2],e[3]]])u.some(e=>Math.hypot(e[0]-t[0],e[1]-t[1])<=l)||u.push(t);const p=[],m=(e,t)=>{let i=0,n=0;if(Math.hypot(e.a[0]-t[0],e.a[1]-t[1])<=l)i=e.b[0]-e.a[0],n=e.b[1]-e.a[1];else{if(!(Math.hypot(e.b[0]-t[0],e.b[1]-t[1])<=l))return null;i=e.a[0]-e.b[0],n=e.a[1]-e.b[1]}const r=Math.hypot(i,n);return r>l?[i/r,n/r]:null};for(const e of u){const t=h.map(t=>({iv:t,u:m(t,e)})).filter(e=>!!e.u);for(let i=0;i0?[e.slice(),u,_,m]:[e.slice(),m,_,u];else{if(!g)continue;{const t=[-r.u[1],r.u[0]],i=[-o.u[1],o.u[0]],n=s<0?1:-1,a=[e[0]+t[0]*r.iv.half*n,e[1]+t[1]*r.iv.half*n],l=[e[0]-i[0]*o.iv.half*n,e[1]-i[1]*o.iv.half*n];y=s>0?[e.slice(),a,l]:[e.slice(),l,a]}}Math.abs(Wo(y))>l*l&&p.push(y)}}return p}(e,t,i,r,o,s,a,d),m=n.length?Ps(e,t,i,r,o,s,a):null;let _="exterior";try{const v=xs(e,t,i,r,o,s,a,d);_="paper";const y=v?v.shell?.length?$t(v.centre,v.shell):v.centre:[],b=e=>{const t=ps(e.outset);if(!e.inset)return t;const i=ps(e.inset);return Mt(t,i)};_="room-rings";let w=null;for(const e of h)try{const t=b(e);w=w?$t(w,t):t}catch{}if(_="edge-bodies",v)for(const n of zs(e,t,i,r,o,s,a))try{const e=St(ps(n.quad),v.centre);w=w?$t(w,e):e}catch{}_="junctions",w=function(e,t,i=1,n=$t){let r=e;for(const e of t||[]){const t=bs(e,i);if(t)try{const e=ps(t);r=r?n(r,e):e}catch{}}return r}(w,p,a),_="facade-clip",w&&v&&(w=St(w,v.centre)),_="exterior-shell";const k=[];let x=0;if(v?.shell?.length)if(w)try{const e=$t(w,v.shell);if(!ms(e))throw new Error("invalid shell union");w=e}catch{if(!ms(w)||!ms(v.shell))throw new Error("invalid shell");k.push({id:"exterior-shell",geom:v.shell}),x++}else w=v.shell;if(_="multi-wall-trim",w&&d.nodes.length){const e=e=>e.rays.some(e=>e.supports.some(e=>e.length<2*e.halfDepth)),t=d.nodes.filter(e);if(t.length){w=ys(w,{...d,nodes:t},v?.centre,y)}}if(_="junction-corners",d.nodes.length){const n=is(d),l=function(e,t,i,n,r,o,s,a){try{const l=xs(e,t,i,n,r,o,s,{epsilon:a.epsilon,coordinateScale:a.coordinateScale,nodes:[],index:new Map});return l?l.shell?.length?$t(l.centre,l.shell):l.centre:null}catch{return null}}(e,t,i,r,o,s,a,d);for(const e of n.fans)try{let t=[ps(e)];if(l&&(t=St(t,l)),!t?.length)continue;w=w?$t(w,t):t}catch{}f=w,g=Math.max(d.epsilon,1e-9)**2,w=Array.isArray(f)?f.map(e=>{if(!Array.isArray(e)||!e.length)return e;const[t,...i]=e;return Math.abs(Wo(t||[]))<=g?null:[t,...i.filter(e=>Math.abs(Wo(e||[]))>g)]}).filter(e=>!!e):f}const $=w||[],S=[...ms($)?[{id:"room-primary",geom:$}]:[],...k.map((e,t)=>({id:`room-isolated-${t}`,geom:e.geom}))];_="openings";for(const e of n){if(!(e.length>0))continue;const t=Is(m,e,!0);if(!t.negative&&!t.positive)continue;const i=e.angle*Math.PI/180,n=Math.cos(i),o=Math.sin(i),s=-o,l=n,c=e.length/2,h=1.25*Math.max(u,r*a),d=[[e.x-n*c-s*h,e.y-o*c-l*h],[e.x+n*c-s*h,e.y+o*c-l*h],[e.x+n*c+s*h,e.y+o*c+l*h],[e.x-n*c+s*h,e.y-o*c+l*h]];w&&(w=Mt(w,ps(d)));for(const e of k)e.geom=Mt(e.geom,ps(d))}_="extras";const M=[];let R=0;const T=c.mergeExtra||((e,t)=>e?$t(e,t):t);for(let e=0;ee.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]))||Math.abs(li(t))<=1e-9){R++;continue}const i=[ps(t)];try{const t=T(w,i,e);if(!ms(t))throw new Error("invalid extra union");w=t}catch{R++,ms(i)&&M.push({id:`extra-${e}`,geom:i})}}M.push(...k),M.sort((e,t)=>Ss(e.geom).localeCompare(Ss(t.geom)));const C=w||[];return{status:R||x?"degraded-extra":"ok",geom:C,components:[...ms(C)?[{id:"primary",geom:C}]:[],...M.map((e,t)=>({...e,id:`isolated-${t}`}))],roomGeom:$,roomComponents:S,paperGeom:y,depthUnits:u,openingPadUnits:1.25*Math.max(u,r*a),openingIndex:m,multiWallNodes:d,degradedExtraCount:R+x}}catch{return c.onCoreFailure?.(_),{status:"failed-core",geom:[],components:[],roomGeom:[],paperGeom:[],roomComponents:[],depthUnits:u,openingIndex:null,multiWallNodes:d,degradedExtraCount:0}}var f,g}function Cs(e,t,i,n=[],r,o,s,a=1,l=[],c={}){if(!t?.length&&!l.length)return null;return Ds(Ts(e,t,i,n,r,o,s,a,l,c))}function Ds(e){if("failed-core"===e.status||"not-applicable"===e.status)return null;const t=e.components.map(e=>({id:e.id,d:Ss(e.geom),fillRule:"evenodd"})).filter(e=>!!e.d),i=t[0]?.d||"",n=Ss(e.paperGeom);if(t.length){const r={status:e.status,d:i,paths:t,components:e.components,roomGeom:e.roomGeom,multiWallNodes:e.multiWallNodes,paperD:n,depthUnits:e.depthUnits,fillRule:"evenodd"};return Object.defineProperties(r,{roomComponents:{value:e.roomComponents||[],enumerable:!1},openingIndex:{value:e.openingIndex,enumerable:!1},openingPadUnits:{value:e.openingPadUnits,enumerable:!1}}),r}return null}function zs(e,t,i,n,r,o,s=1){if(!t?.length)return[];const a=new Set,l=[];for(const c of e||[]){if(!c?.id)continue;const h=os(e,c.id,t,i,n,r,o,s);if(!h)continue;const d=h.poly;for(let e=0;e0))continue;const p=mo(t,i,n,s);if(a.has(p))continue;a.add(p);const m=ao(u,r,o),[_,f]=qo(d,e),g=-_,v=-f,y=m/2,b=[[t[0]+g*y,t[1]+v*y],[i[0]+g*y,i[1]+v*y],[i[0]+_*y,i[1]+f*y],[t[0]+_*y,t[1]+f*y]];l.push({key:p,kind:c,cm:u,quad:b,a:[t[0],t[1]],b:[i[0],i[1]],depthUnits:m})}}return l}function As(e,t,i){const n=e?.length||0;if(n<3||t.length!==n)return null;if(t.every(e=>!(e>0)))return e.map(e=>[e[0],e[1]]);Yo(e),t.slice().reverse();const r=[];for(let o=0;o0||l>0)){r.push([e[o][0],e[o][1]]);continue}if(a>0!=l>0){const t=e[o],i=a>0?[t[0]-c*a,t[1]-h*a]:[t[0],t[1]],n=l>0?[t[0]-d*l,t[1]-u*l]:[t[0],t[1]];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}if(Bo(w,k)){const t=e[o],i=[t[0]-c*a,t[1]-h*a],n=[t[0]-d*l,t[1]-u*l];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}const S=jo(x,w,$,k),M=Math.max(a,l,1e-9),R=ns(i,e[o])?.limit??4*M;if(S){const i=Math.hypot(S[0]-e[o][0],S[1]-e[o][1]);if(Number.isFinite(i)&&i<=R){r.push(S);continue}if(Rs(e,t,o)){r.push([e[o][0],e[o][1]]);continue}}a>0&&r.push([e[o][0]-c*a,e[o][1]-h*a]),l>0&&r.push([e[o][0]-d*l,e[o][1]-u*l])}return r.length>=3?r:null}function Ps(e,t,i,n,r,o,s=1){const a=[];for(const l of e||[]){if(!l?.id)continue;const c=os(e,l.id,t,i,n,r,o,s);if(!c)continue;const h=Math.abs(li(c.poly));for(let e=0;e[Math.max(t,e.x0),Math.min(i,e.x1)]).filter(e=>e[1]-e[0]>n).sort((e,t)=>e[0]-t[0]||e[1]-t[1]);if(!r.length)return{coverage:0,full:!1};let o=r[0][0],s=r[0][1],a=0,l=o<=t+n;for(let e=1;e=i-n,{coverage:a,full:l}}function Fs(e,t){return Number(t.full)-Number(e.full)||e.faceDistance-t.faceDistance||e.area-t.area||e.roomId.localeCompare(t.roomId)}function Is(e,t,i=!1){const n=Number(t?.x),r=Number(t?.y),o=Number(t?.angle),s=Number(t?.length);if(!([n,r,o,s].every(Number.isFinite)&&s>0))return{negative:null,positive:null};const a=o*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=-c,d=l,u=s/2,p=Math.max(1e-9,e.adjacencyEps),m=new Map;let _=0;for(const t of e.edges){if(i&&!(t.half>0))continue;if(!xo(t.a,t.b,o))continue;const[e,s]=uo(t.a,t.b);if(Math.abs((n-t.a[0])*s-(r-t.a[1])*e)>p)continue;const a=(t.a[0]-n)*l+(t.a[1]-r)*c,f=(t.b[0]-n)*l+(t.b[1]-r)*c,g=Math.max(-u,Math.min(a,f)),v=Math.min(u,Math.max(a,f));if(v-g<=p)continue;const y=t.inward[0]*h+t.inward[1]*d>=0?1:-1,b=((t.a[0]+t.b[0])/2-n)*h+((t.a[1]+t.b[1])/2-r)*d,w=Math.abs(b+y*t.half),k=`${y}|${t.roomId}`,x={x0:g,x1:v,half:t.half,cm:t.cm,key:t.key,axis:[e,s]},$=m.get(k);$?($.pieces.push(x),$.faceDistance=Math.min($.faceDistance,w)):m.set(k,{roomId:t.roomId,side:y,order:_++,pieces:[x],faceDistance:w,area:t.area,coverage:0,full:!1})}for(const e of m.values()){const t=Os(e.pieces,-u,u,p);e.coverage=t.coverage,e.full=t.full}const f=e=>{const t=[...m.values()].filter(t=>t.side===e&&t.coverage>p);return t.sort(Fs),t[0]||null};return{negative:f(-1),positive:f(1)}}function Es(e,t){const i=Is(e,t),n=[i.negative,i.positive].filter(e=>!!e);if(!n.length)return{ox:0,oy:0,cm:0,side:-1};const r=i.negative&&i.positive?-1:n[0].side,o=t.flip_v?-r:r,s=(-1===o?i.negative:i.positive)||n[0],a=[...s.pieces].sort((e,t)=>(e.x0<=0&&e.x1>=0?0:Math.min(Math.abs(e.x0),Math.abs(e.x1)))-(t.x0<=0&&t.x1>=0?0:Math.min(Math.abs(t.x0),Math.abs(t.x1)))||t.x1-t.x0-(e.x1-e.x0)||e.key.localeCompare(t.key))[0];if(!(a.half>0&&a.cm>0))return{ox:0,oy:0,cm:0,side:o};const l=t.angle*Math.PI/180,c=-Math.sin(l),h=Math.cos(l);return{ox:c*o*a.half,oy:h*o*a.half,cm:a.cm,side:o}}function Hs(e,t){const i=1e-9,n=t.filter(e=>Number.isFinite(e.x0)&&Number.isFinite(e.x1)&&Number.isFinite(e.half)&&e.x1>e.x0&&e.half>0);if(!n.length)return"";const r=n.flatMap(e=>[e.x0,e.x1]).sort((e,t)=>e-t),o=[];for(const e of r){const t=o[o.length-1];(void 0===t||e>t+i)&&o.push(e)}const s=[];for(let e=0;e+1t+i))continue;const a=(t+r)/2,l=n.reduce((e,t)=>a>=t.x0-i&&a<=t.x1+i?Math.max(e,t.half):e,0);if(!(l>0))continue;const c=s[s.length-1];c&&t<=c.x1+i&&Math.abs(l-c.half)<=i?c.x1=r:s.push({x0:t,x1:r,half:l})}const a=[];for(const e of s){const t=a[a.length-1],n=t?.[t.length-1];n&&e.x0<=n.x1+i?(e.x0=n.x1,t.push(e)):a.push([e])}return a.map(t=>{const i=t[0],n=t[t.length-1],r=Math.min(.25*Math.min(...t.map(e=>e.half)),.75),o=-e*r,s=[];if(1===e){s.push(`M ${i.x0} ${o} L ${n.x1} ${o}`);for(let e=t.length-1;e>=0;e--){const i=t[e];s.push(`L ${i.x1} ${i.half} L ${i.x0} ${i.half}`)}}else{s.push(`M ${n.x1} ${o} L ${i.x0} ${o}`);for(const e of t)s.push(`L ${e.x0} ${-e.half} L ${e.x1} ${-e.half}`)}return s.push("Z"),s.join(" ")}).join(" ")}function Ns(e,t,i,n){if(!n)return i;const r=e.angle*Math.PI/180,o=Math.cos(r),s=Math.sin(r),a=[],l=1e-9;for(const r of i){const[i,c]=r.axis,h=o*i+s*c;if(Math.abs(h)<=l)continue;const d=e.x*i+e.y*c,u=d+h*r.x0,p=d+h*r.x1,m=Math.min(u,p),_=Math.max(u,p),f=`${r.key}|${t}`,g=n.get(f)||[];let v=[[m,_]];for(const[e,t]of g){const i=[];for(const[n,r]of v)t<=n+l||e>=r-l?i.push([n,r]):(e>n+l&&i.push([n,Math.min(r,e)]),te[0]-t[0]||e[1]-t[1]),b=[];for(const e of y){const t=b[b.length-1];t&&e[0]<=t[1]+l?t[1]=Math.max(t[1],e[1]):b.push([e[0],e[1]])}n.set(f,b)}return a}function Ls(e,t){const i=new Map;return t.map(t=>function(e,t,i){const n=Is(e,t,!0),r=n.negative,o=n.positive;if(!r&&!o)return null;let s;if(r&&o)s=[{candidate:r,side:-1},{candidate:o,side:1}];else{const e=r||o;s=[{candidate:e,side:-1},{candidate:e,side:1}]}const a=s.map(({candidate:e,side:n})=>({candidate:e,side:n,pieces:Ns(t,n,e.pieces,i)})),l=a.map(({candidate:e,side:t,pieces:i})=>({side:t,roomId:e.roomId,d:Hs(t,i)})),c=a.flatMap(({pieces:e})=>e);if(!c.length)return null;const h=Math.max(...c.map(e=>e.half));return{faces:l,minY:-h,maxY:h,wallKey:[...new Set(c.map(e=>e.key))].sort().join("|")}}(e,t,i))}const Ws=150,qs=1e-6;function Bs(e){return Number.isFinite(e)?Math.max(1,Math.min(Ws,e)):1}function js(e){return((Number.isFinite(Number(e))?Number(e):0)%90+90)%90}const Us=e=>{const t=e.map(e=>[e[0],e[1]]);return!t.length||t[0][0]===t[t.length-1][0]&&t[0][1]===t[t.length-1][1]||t.push([...t[0]]),[t]},Vs=(e,t)=>e[0]===t[0]&&e[1]===t[1];function Gs(e,t=1e-6){const i=Number.isFinite(t)&&t>0?t:qs,n=[];for(const t of e||[]){if(!Array.isArray(t)||t.length<2)return null;const e=Number(t[0]),r=Number(t[1]);if(!Number.isFinite(e)||!Number.isFinite(r))return null;const o=Math.round(e/i)*i,s=Math.round(r/i)*i;if(!Number.isFinite(o)||!Number.isFinite(s))return null;const a=[Object.is(o,-0)?0:o,Object.is(s,-0)?0:s];n.length&&Vs(n[n.length-1],a)||n.push(a)}return n.length>1&&Vs(n[0],n[n.length-1])&&n.pop(),n.length<3||new Set(n.map(e=>`${e[0]},${e[1]}`)).size<3?null:li(n)>i*i?n:null}function Ks(e){const t=[];for(const i of e||[])for(const e of i||[]){const i=(e||[]).filter(e=>Array.isArray(e)&&e.length>=2);i.length<4||t.push(`M ${i.slice(0,-1).map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`)}return t.join(" ")}function Ys(e,t,i,n,r){return zo({a:e,b:t,halfDepth:ao(i,n,r)/2})}function Xs(e,t,i){const n=Number(t)>0?Number(t):5,r=Bs(e.cm)/n*i,o=e.center[0],s=e.center[1];if("circle"===e.shape){const e=r/2;return Array.from({length:96},(t,i)=>{const n=i/96*Math.PI*2;return[o+Math.cos(n)*e,s+Math.sin(n)*e]})}const a=r/2,l=js(e.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l);return[[-a,-a],[a,-a],[a,a],[-a,a]].map(([e,t])=>[o+e*c-t*h,s+e*h+t*c])}function Zs(e,t){const i=Number.isFinite(t)?Math.max(0,Math.min(1,t)):0,n=(e.a[0]+e.b[0])/2,r=(e.a[1]+e.b[1])/2,o=(e.b[0]-e.a[0])*i/2,s=(e.b[1]-e.a[1])*i/2;return{...e,a:[n-o,r-s],b:[n+o,r+s]}}function Js(e,t,i=1e-9){if(!t.length)return[e];let n=[Us(e)];try{for(const e of t){const t=e.b[0]-e.a[0],r=e.b[1]-e.a[1],o=Math.hypot(t,r);if(!(o>i))continue;const s=t/o,a=r/o,l=-a,c=s,h=1.25*Math.max(Number(e.depth)||0,4*i),d=Math.max(2*i,1e-9*o),u=[[e.a[0]-s*d-l*h,e.a[1]-a*d-c*h],[e.b[0]+s*d-l*h,e.b[1]+a*d-c*h],[e.b[0]+s*d+l*h,e.b[1]+a*d+c*h],[e.a[0]-s*d+l*h,e.a[1]-a*d+c*h]];n=Mt(n,Us(u))}return la(n)}catch{return[e]}}function Qs(e,t){try{const i=Mt([[...e.map(e=>[e[0],e[1]]),[e[0][0],e[0][1]]]],[[...t.map(e=>[e[0],e[1]]),[t[0][0],t[0][1]]]]);let n=null,r=0;for(const e of i||[]){const t=e?.[0]||[],i=Math.abs(li(t));t.length>=4&&i>r&&(r=i,n=t.slice(0,-1).map(e=>[e[0],e[1]]))}return n}catch{return null}}function ea(e,t,i,n=Math.max(2e-4*i,1e-9),r=[]){const o=[],s=[],a=new Map;for(const e of r){const t=a.get(e.hostId)||[];t.push(e),a.set(e.hostId,t)}const l=[],c=[],h=[];for(const n of e.room_drafts||[])for(let e=0;e+10))continue;const a={a:n.points[e],b:n.points[e+1],halfDepth:s},c=zo(a);c&&(o.push(a),l.push(c))}const d=[];for(const n of e.partitions||[]){const e={a:n.a,b:n.b,halfDepth:ao(n.cm,t,i)/2};if(!(e.halfDepth>0))continue;const r=zo(e);r&&(s.push(e),c.push(r),d.push({id:n.id,body:r}))}const u=[...o,...s];for(const{segmentIndex:e,wedge:t}of Io(u,n)){const i=e=o.length&&(d[i.at].body=n))}for(const e of d)h.push(...Js(e.body,a.get(e.id)||[],n));const p=(e.wall_columns||[]).map(e=>Xs(e,t,i)),m=Eo([...o,...s],n).flatMap(e=>Js(e,r,n));return{drafts:l,partitions:c,columns:p,patches:m,all:[...l,...h,...m,...p]}}function ta(e){try{const t=e.map(e=>Gs(e)).filter(e=>!!e).map(e=>Us(e));return t.length?$t(t[0],...t.slice(1)):null}catch{return null}}const ia=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`;function na(e){const t=[];for(const i of e||[]){if(sa([i])<=1e-6)continue;const e=[];for(const t of i||[]){const i=(t||[]).filter(e=>Array.isArray(e)&&e.length>=2);i.length<4||e.push(ia(i.slice(0,-1)))}e.length&&t.push(e.join(" "))}return t}function ra(e,t,i={}){const n=ta(e.filter(e=>e.length>=3)),r=t.some(e=>!Gs(e))?null:ta(t);if(!n)return[];if(r)try{return na(St(n,r))}catch{}return function(e,t,i){let n=null;for(let r=0;r=4&&t.push(e.slice(0,-1).map(e=>[e[0],e[1]]));return t}function la(e){const t=[];for(const i of e||[]){const e=i?.[0];e?.length>=4&&t.push(e.slice(0,-1).map(e=>[e[0],e[1]]))}return t}function ca(e,t){let i=!1;for(let n=0,r=t.length-1;ne[1]!=l>e[1]&&e[0]<(a-o)*(e[1]-s)/(l-s||1e-12)+o&&(i=!i)}return i}function ha(e,t,i){return function(e,t){for(const i of t||[]){const t=i?.[0];if(!t?.length||!ca(e,t))continue;let n=!1;for(let t=1;tca(e,t))}function da(e,t,i){if(Math.hypot(e.center[0]-t.center[0],e.center[1]-t.center[1])>i)return!1;if(Math.abs(Bs(e.cm)-Bs(t.cm))>1e-6)return!1;if(e.shape!==t.shape)return!0;if("circle"===e.shape||"circle"===t.shape)return!0;const n=Math.abs(js(e.angle)-js(t.angle));return Math.min(n,90-n)<=1e-6}const ua=2.54;function pa(e){const t="number"==typeof e?e:NaN;return!Number.isFinite(t)||t<=0||5===t?1:5/t}function ma(e,t){return e*pa(t)}function _a(e){return e?ua:1}function fa(e,t){if(!t)return String(e);const i=e/ua;return String(Math.round(1e6*i)/1e6)}function ga(e,t){return t?e*ua:e}const va=1e3;function ya(e,t){const i=Number(e),n=Number.isFinite(i)&&i>0?i:1,r=n>=1?t:t*n,o=n>=1?t/n:t;return{x:(t-r)/2,y:(t-o)/2,w:r,h:o}}const ba=.01,wa=100;function ka(e,t=1e3){const i=ya(e?.plan_aspect,t),n=Number(e?.plan_scale),r=Number.isFinite(n)&&n>0?Math.min(wa,Math.max(ba,n)):1,o=Number(e?.plan_scale_x),s=Number(e?.plan_scale_y),a=Number.isFinite(o)&&o>0?Math.min(wa,Math.max(ba,o)):r,l=Number.isFinite(s)&&s>0?Math.min(wa,Math.max(ba,s)):r,c=Number(e?.plan_x),h=Number(e?.plan_y),d=nr(e?.plan_angle);return{x:i.x+(Number.isFinite(c)?Pa(c):0)*t,y:i.y+(Number.isFinite(h)?Pa(h):0)*t,w:i.w*a,h:i.h*l,...d?{angle:d}:{}}}function xa(e){if(null==e.x||null==e.y)return{x:e.x,y:e.y,w:e.w,h:e.h};const t=Number(e.w)||0,i=Number(e.h)||0;return{x:t<0?e.x+t:e.x,y:i<0?e.y+i:e.y,w:Math.abs(t),h:Math.abs(i)}}function $a(e){return e&&Array.isArray(e.spaces)?e.spaces.map(e=>{const t=va,i=function(e){return Array.isArray(e)&&4===e.length&&e.every(e=>Number.isFinite(e))&&e[2]>1e-6&&e[3]>1e-6?e:[0,0,1,1]}(e.view_box);return{id:e.id,title:e.title,cellCm:Number.isFinite(Number(e.cell_cm))&&Number(e.cell_cm)>0?Number(e.cell_cm):5,vb:[i[0]*va,i[1]*t,i[2]*va,i[3]*t],bg:e.plan_url?{href:_n(e.plan_url),...ka(e,va)}:null,rooms:(e.rooms||[]).map(e=>{const i={...e,...xa(e)};return{id:i.id,name:i.name,area:i.area??null,open_to:i.open_to||void 0,settings:i.settings||void 0,x:null!=i.x?i.x*va:void 0,y:null!=i.y?i.y*t:void 0,w:null!=i.w?i.w*va:void 0,h:null!=i.h?i.h*t:void 0,poly:i.poly?i.poly.map(e=>[e[0]*va,e[1]*t]):void 0,wall_ids:Array.isArray(i.wall_ids)?[...i.wall_ids]:void 0}}),wall_segments:(e.wall_segments||[]).map(e=>({...e,id:String(e.id),a:[Number(e.a?.[0])*va,Number(e.a?.[1])*t],b:[Number(e.b?.[0])*va,Number(e.b?.[1])*t],cm:Number(e.cm)})),room_drafts:(e.room_drafts||[]).map(e=>({id:e.id,points:(e.points||[]).map(e=>[e[0]*va,e[1]*t]),segments:(e.segments||[]).map(e=>({..."string"==typeof e.id&&e.id?{id:e.id}:{},cm:Number(e.cm)}))})),partitions:(e.partitions||[]).map(e=>({id:e.id,a:[e.a[0]*va,e.a[1]*t],b:[e.b[0]*va,e.b[1]*t],cm:Number(e.cm)})),wall_columns:(e.wall_columns||[]).map(e=>({id:e.id,shape:"circle"===e.shape?"circle":"square",center:[e.center[0]*va,e.center[1]*t],cm:Number(e.cm),..."circle"===e.shape?{}:{angle:js(e.angle)}}))}}):[]}const Sa=5e3,Ma=Sa*va,Ra=240,Ta=va/Ra,Ca=1/Ra;function Da(e){if(!Number.isFinite(e))return e;const t=Math.round(e*Ra/va)*va/Ra;return Math.abs(t-e)<=1e-9*Ta?e:t}function za(e){return{x:Da(e.x),y:Da(e.y)}}function Aa(e){return Number.isFinite(e)?Math.min(Ma,Math.max(-Ma,e)):0}function Pa(e){return Number.isFinite(e)?Math.min(Sa,Math.max(-Sa,e)):0}const Oa=1/3,Fa=200;function Ia(e){return"house"===e?"house":"content"}function Ea(e){let t=1/0,i=1/0,n=-1/0,r=-1/0;for(const o of e){const e=Number(o[0]),s=Number(o[1]);Number.isFinite(e)&&Number.isFinite(s)&&(en&&(n=e),s>r&&(r=s))}return t>n?null:{minX:t,minY:i,maxX:n,maxY:r}}function Ha(e){const t=[],i=e=>{if(Array.isArray(e))if(e.length>=2&&Number.isFinite(Number(e[0]))&&Number.isFinite(Number(e[1])))t.push([Number(e[0]),Number(e[1])]);else for(const t of e)i(t)};return i(e),Ea(t)}function Na(e,t){const i=Number.isFinite(t)&&t>0?t:0;return{minX:e.minX-i,minY:e.minY-i,maxX:e.maxX+i,maxY:e.maxY+i}}function La(e){return e.poly&&e.poly.length?Ea(e.poly):null==e.x||null==e.y?null:Ea([[e.x,e.y],[e.x+(e.w||0),e.y+(e.h||0)]])}function Wa(e,t){const i=[];for(const t of e.rooms||[]){const e=La(t);e&&i.push(e)}if(e.bg){const t=Ea(dr(e.bg));t&&i.push(t)}for(const e of t||[])if(Array.isArray(e)){const t=Ea([e]);t&&i.push(t)}else i.push(e);return i}const qa=e=>{if(!e.length)return 0;const t=[...e].sort((e,t)=>e-t),i=t.length>>1;return t.length%2?t[i]:(t[i-1]+t[i])/2},Ba=e=>{let t=1/0,i=1/0,n=-1/0,r=-1/0;for(const o of e)o.minXn&&(n=o.maxX),o.maxY>r&&(r=o.maxY);return t>n||i>r?null:{x:t,y:i,w:n-t,h:r-i}};function ja(e,t){let{x:i,y:n,w:r,h:o}=e;r<30&&(i=i+r/2-100,r=Fa),o<30&&(n=n+o/2-100,o=Fa);const s="number"==typeof t?{top:t,right:t,bottom:t,left:t}:t,a=Math.max(r,o),l=a*s.top,c=a*s.right,h=a*s.bottom,d=a*s.left;return{x:i-d,y:n-l,w:r+d+c,h:o+l+h}}function Ua(e,t={}){const i=t.pad??.05,n=t.k??10,r=t.minSpread??50,o=e.filter(e=>Number.isFinite(e.minX)&&Number.isFinite(e.minY)&&Number.isFinite(e.maxX)&&Number.isFinite(e.maxY)&&Math.abs(e.minX)<=Ma&&Math.abs(e.maxX)<=Ma&&Math.abs(e.minY)<=Ma&&Math.abs(e.maxY)<=Ma);if(!o.length)return{core:null,all:null,outliers:0};const s=Ba(o);if(o.length<4){const e=ja(s,i);return{core:e,all:e,outliers:0}}const a=o.map(e=>(e.minX+e.maxX)/2),l=o.map(e=>(e.minY+e.maxY)/2),c=qa(a),h=qa(l),d=o.map((e,t)=>Math.max(Math.abs(a[t]-c),Math.abs(l[t]-h))),u=Math.max(((e,t)=>e.length?e[Math.min(e.length-1,Math.max(0,Math.round(t*(e.length-1))))]:0)([...d].sort((e,t)=>e-t),.75),r),p=d.map(e=>e>n*u),m=p.filter(Boolean).length,_=m&&m<=o.length*Oa?o.filter((e,t)=>!p[t]):o;return{core:ja(Ba(_)||s,i),all:ja(s,i),outliers:_===o?0:m}}function Va(e){return Ua(e,{pad:0}).all}function Ga(e,t,i=.05){const n=Ua(Wa(e,t),{pad:i});if(n.core)return n.core;const r=e.vb&&4===e.vb.length&&e.vb[2]>0&&e.vb[3]>0?e.vb:[0,0,va,va];return{x:r[0],y:r[1],w:r[2],h:r[3]}}function Ka(e){const t=Ga(e);return{x:t.x+t.w/2,y:t.y+t.h/2}}function Ya(e){const t=[];for(const i of e.rooms||[]){const e=La(i);e&&t.push(e)}const i=Ua(t,{pad:0}).core,n=va*pa(e.cellCm??5);return i?Math.max(n,Math.min(Ma,Math.max(i.w,i.h))):n}function Xa(e,t,i,n=1){const r=Number(i),o=Number.isFinite(n)&&n>0?n:1;return!Number.isFinite(r)||r<=0?e*o:e*Ya(t)*o/r}const Za=[1,2,5,10,20,50,100,200,500,1e3];function Ja(e,t,i=7){if(!(e>0&&t>0&&Number.isFinite(t)))return null;const n=Za.find(n=>e*n*t>=i);if(void 0===n)return null;const r=Za.find(e=>e>=5*n)??5*n;return{fine:n,coarse:r}}function Qa(e){if(e.poly&&e.poly.length){const t=e.poly.map(e=>e[0]),i=e.poly.map(e=>e[1]),n=Math.min(...t),r=Math.min(...i);return{x:n,y:r,w:Math.max(...t)-n,h:Math.max(...i)-r}}return{x:e.x??0,y:e.y??0,w:e.w??0,h:e.h??0}}function el(e,t,i,n,r){const o=t[e.id];return o&&o.s===e.space?{x:o.x*va,y:o.y*va}:n[e.id]?n[e.id]:za(Ka(r))}function tl(e,t,i,n){const r=i["rl_"+(e.id||"")];if(r&&r.s===t)return{x:r.x*va,y:r.y*va};const o=function(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}(e);return za({x:o[0],y:o[1]})}const il=["furniture","appliance","sanitary","other"],nl=[{id:"sofa",group:"furniture",category:"sofa",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",category:"armchair",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",category:"coffee_table",w:110,h:60,g:[["r",0,0,1,1],["r",.08,.14,.84,.72]]},{id:"table_dining",group:"furniture",category:"dining_table",w:140,h:80,g:[["r",0,0,1,1],["r",.06,.11,.88,.78]]},{id:"table_round",group:"furniture",category:"dining_table",w:120,h:120,g:[["e",.5,.5,.5,.5],["e",.5,.5,.41,.41]]},{id:"chair",group:"furniture",category:"chair",w:45,h:45,g:[["r",0,0,1,.18],["r",.06,.18,.88,.8]]},{id:"desk",group:"furniture",category:"work_table",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",category:"bed",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",category:"bed",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",category:"nightstand",w:45,h:40,g:[["r",0,0,1,1],["r",.12,.14,.76,.33],["r",.12,.53,.76,.33]]},{id:"wardrobe",group:"furniture",category:"wardrobe",w:100,h:60,g:[["r",0,0,1,1],["l",0,.72,1,.72],["l",.5,.72,.5,1]]},{id:"bookshelf",group:"furniture",category:"wardrobe",w:80,h:30,g:[["r",0,0,1,1],["l",.34,0,.34,1],["l",.67,0,.67,1]]},{id:"fridge",group:"appliance",category:"fridge",w:60,h:65,g:[["r",0,0,1,1],["l",0,.36,1,.36],["l",.83,.44,.83,.64]]},{id:"stove",group:"appliance",category:"cooktop",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",category:"dishwasher",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",category:"washer",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",category:"dryer",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",category:"tv",w:120,h:30,g:[["r",0,0,1,.42],["l",.5,.42,.5,.72],["l",.3,.72,.7,.72]]},{id:"ac",group:"appliance",category:"air_conditioner",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",category:"boiler",w:45,h:45,g:[["e",.5,.5,.5,.5],["e",.5,.5,.31,.31]]},{id:"toilet",group:"sanitary",category:"toilet",w:40,h:70,g:[["r",.06,0,.88,.2],["e",.5,.58,.37,.35],["e",.5,.58,.22,.2]]},{id:"bathtub",group:"sanitary",category:"bathtub",w:170,h:75,g:[["r",0,0,1,1],["r",.05,.11,.77,.78],["e",.89,.5,.045,.1]]},{id:"shower",group:"sanitary",category:"shower",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",category:"sink",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",category:"kitchen_sink",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",category:"bidet",w:40,h:55,g:[["e",.5,.5,.44,.5],["e",.5,.5,.26,.3]]},{id:"stairs",group:"other",category:"stairs",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",category:"fireplace",w:120,h:40,g:[["r",0,0,1,1],["p",.22,1,.22,.42,.78,.42,.78,1]]},{id:"plant",group:"other",category:"plant",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",category:"rug",w:200,h:140,g:[["r",0,0,1,1],["r",.06,.09,.88,.82]]}],rl=new Set(["fridge","dishwasher","washer","dryer","ac","water_heater","shower","sink","stairs","fireplace","plant","rug"]),ol=[...[{id:"coffee_table",group:"furniture",category:"coffee_table",w:120,h:60,art:{d:"M3.84 5.884c0-1.21 1.048-2.192 2.34-2.192h107.64c1.293 0 2.34.981 2.34 2.192v48.23c0 1.212-1.047 2.193-2.34 2.193H6.18c-1.292 0-2.34-.981-2.34-2.192z M103.056 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m-93.6-35.077V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877",viewW:120,viewH:60}},{id:"coffee_table_round",group:"furniture",category:"coffee_table",w:80,h:80,art:{d:"M3.765 40a36.235 36.235 0 1 0 72.47 0 36.235 36.235 0 1 0-72.47 0 m56.26 26.96-3.843-3.843a.906.906 0 0 1 0-1.28l3.843-3.844a.906.906 0 0 1 1.281 0l3.844 3.843a.906.906 0 0 1 0 1.281l-3.844 3.844a.906.906 0 0 1-1.28 0m-.001 34.66-3.843-3.843a.906.906 0 0 1 0-1.281l3.843-3.844a.906.906 0 0 1 1.281 0l3.843 3.844a.906.906 0 0 1 0 1.28l-3.843 3.844a.906.906 0 0 1-1.281 0m-34.423-34.66-3.843-3.843a.906.906 0 0 1 0-1.281l3.843-3.844a.906.906 0 0 1 1.281 0l3.844 3.844a.906.906 0 0 1 0 1.28l-3.844 3.844a.906.906 0 0 1-1.28 0m-.001 34.66-3.843-3.843a.906.906 0 0 1 0-1.281l3.843-3.843a.906.906 0 0 1 1.281 0l3.844 3.843a.906.906 0 0 1 0 1.281l-3.844 3.843a.906.906 0 0 1-1.28 0",viewW:80,viewH:80}},{id:"coffee_table_oval",group:"furniture",category:"coffee_table",w:120,h:60,art:{d:"M3.84 30c0-14.53 12.572-26.308 28.08-26.308h56.16c15.509 0 28.08 11.778 28.08 26.308s-12.571 26.307-28.08 26.307H31.92C16.412 56.307 3.84 44.53 3.84 30 M80.592 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.077v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877M31.92 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.876h-5.616a.936.877 0 0 1-.936-.876m0 35.077v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.876h-5.616a.936.877 0 0 1-.936-.876",viewW:120,viewH:60}},{id:"coffee_table_rounded",group:"furniture",category:"coffee_table",w:120,h:60,art:{d:"M3.84 12.461c0-4.843 4.19-8.77 9.36-8.77h93.6c5.17 0 9.36 3.927 9.36 8.77v35.077c0 4.843-4.19 8.77-9.36 8.77H13.2c-5.17 0-9.36-3.927-9.36-8.77z M103.056 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m-93.6-35.077V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877",viewW:120,viewH:60}},{id:"table_dining",group:"furniture",category:"dining_table",w:160,h:90,art:{d:"M3.879 6.08c0-1.265 1.065-2.29 2.378-2.29h147.484c1.314 0 2.38 1.025 2.38 2.29v77.84c0 1.265-1.066 2.29-2.38 2.29H6.257c-1.313 0-2.378-1.025-2.378-2.29z M138.994 19.358v-5.495a.952.916 0 0 1 .951-.915h5.71a.952.916 0 0 1 .95.915v5.495a.952.916 0 0 1-.95.916h-5.71a.952.916 0 0 1-.951-.916m-.001 56.778v-5.495a.952.916 0 0 1 .952-.915h5.709a.952.916 0 0 1 .951.915v5.495a.952.916 0 0 1-.951.916h-5.71a.952.916 0 0 1-.95-.916m-125.6-56.778v-5.495a.952.916 0 0 1 .951-.916h5.71a.952.916 0 0 1 .95.916v5.495a.952.916 0 0 1-.95.915h-5.71a.952.916 0 0 1-.951-.915m-.001 56.779v-5.495a.952.916 0 0 1 .952-.916h5.709a.952.916 0 0 1 .951.916v5.495a.952.916 0 0 1-.951.915h-5.71a.952.916 0 0 1-.95-.915",viewW:160,viewH:90}},{id:"table_round",group:"furniture",category:"dining_table",w:110,h:110,art:{d:"M3.826 55a51.174 51.174 0 1 0 102.349 0A51.174 51.174 0 1 0 3.826 55 m81.005 32.303-3.947-3.947a.93.93 0 0 1 0-1.316l3.947-3.948a.93.93 0 0 1 1.316 0l3.948 3.948a.93.93 0 0 1 0 1.316l-3.948 3.947a.93.93 0 0 1-1.316 0m0 54.897-3.947-3.948a.93.93 0 0 1 0-1.316l3.947-3.947a.93.93 0 0 1 1.316 0l3.948 3.947a.93.93 0 0 1 0 1.316L82.32 87.2a.93.93 0 0 1-1.316 0M27.04 32.304l-3.947-3.948a.93.93 0 0 1 0-1.315l3.948-3.948a.93.93 0 0 1 1.315 0l3.948 3.948a.93.93 0 0 1 0 1.315l-3.948 3.948a.93.93 0 0 1-1.315 0m-.001 54.604-3.947-3.948a.93.93 0 0 1 0-1.316l3.948-3.947a.93.93 0 0 1 1.315 0l3.948 3.947a.93.93 0 0 1 0 1.316l-3.948 3.948a.93.93 0 0 1-1.315 0",viewW:110,viewH:110}},{id:"table_dining_oval",group:"furniture",category:"dining_table",w:180,h:100,art:{d:"M3.892 50c0-25.51 21.418-46.19 47.838-46.19h76.54c26.42 0 47.839 20.68 47.839 46.19S154.69 96.19 128.27 96.19H51.73C25.31 96.19 3.892 75.51 3.892 50 M122.53 19.514V13.97a.957.924 0 0 1 .956-.924h5.74a.957.924 0 0 1 .958.924v5.543a.957.924 0 0 1-.957.924h-5.74a.957.924 0 0 1-.958-.924m.001 66.516v-5.543a.957.924 0 0 1 .957-.924h5.74a.957.924 0 0 1 .957.924v5.543a.957.924 0 0 1-.957.924h-5.74a.957.924 0 0 1-.957-.924M49.816 19.515v-5.543a.957.924 0 0 1 .957-.924h5.74a.957.924 0 0 1 .958.924v5.542a.957.924 0 0 1-.957.924h-5.74a.957.924 0 0 1-.958-.924m0 66.516v-5.543a.957.924 0 0 1 .957-.924h5.74a.957.924 0 0 1 .957.924v5.543a.957.924 0 0 1-.957.923h-5.74a.957.924 0 0 1-.957-.924",viewW:180,viewH:100}},{id:"table_dining_rounded",group:"furniture",category:"dining_table",w:160,h:90,art:{d:"M3.879 17.527c0-7.587 6.39-13.737 14.272-13.737h123.696c7.883 0 14.273 6.15 14.273 13.737v54.947c0 7.587-6.39 13.737-14.273 13.737H18.151c-7.882 0-14.272-6.15-14.272-13.737z M138.994 19.358v-5.495a.952.916 0 0 1 .951-.915h5.71a.952.916 0 0 1 .95.915v5.495a.952.916 0 0 1-.95.916h-5.71a.952.916 0 0 1-.951-.916m-.001 56.778v-5.495a.952.916 0 0 1 .952-.915h5.709a.952.916 0 0 1 .951.915v5.495a.952.916 0 0 1-.951.916h-5.71a.952.916 0 0 1-.95-.916m-125.6-56.778v-5.495a.952.916 0 0 1 .951-.916h5.71a.952.916 0 0 1 .95.916v5.495a.952.916 0 0 1-.95.915h-5.71a.952.916 0 0 1-.951-.915m-.001 56.779v-5.495a.952.916 0 0 1 .952-.916h5.709a.952.916 0 0 1 .951.916v5.495a.952.916 0 0 1-.951.915h-5.71a.952.916 0 0 1-.95-.915",viewW:160,viewH:90}},{id:"desk",group:"furniture",category:"work_table",w:140,h:70,art:{d:"M3.862 5.967c0-1.234 1.057-2.234 2.362-2.234h127.552c1.305 0 2.362 1 2.362 2.234v58.066c0 1.234-1.057 2.233-2.362 2.233H6.224c-1.305 0-2.362-1-2.362-2.233z M9.531 15.346v-5.36a.945.893 0 0 1 .945-.893h5.67a.945.893 0 0 1 .944.893v5.36a.945.893 0 0 1-.945.894h-5.669a.945.893 0 0 1-.945-.894m113.379.001v-5.36a.945.893 0 0 1 .945-.894h5.67a.945.893 0 0 1 .944.894v5.36a.945.893 0 0 1-.945.893h-5.669a.945.893 0 0 1-.945-.893M9.53 60.013v-5.36a.945.893 0 0 1 .946-.893h5.669a.945.893 0 0 1 .945.893v5.36a.945.893 0 0 1-.945.894h-5.67a.945.893 0 0 1-.944-.894m113.38.001v-5.36a.945.893 0 0 1 .945-.894h5.669a.945.893 0 0 1 .945.894v5.36a.945.893 0 0 1-.945.893h-5.67a.945.893 0 0 1-.944-.893m-97.318-2.681h88.814",viewW:140,viewH:70}},{id:"desk_corner",group:"furniture",category:"work_table",w:160,h:160,art:{d:"M156.12 66.202a2.38 2.38 0 0 1-2.379 2.38H94.272c-13.138 0-23.788 10.65-23.788 23.787v61.372a2.38 2.38 0 0 1-2.379 2.38H6.257a2.38 2.38 0 0 1-2.378-2.38V6.257A2.38 2.38 0 0 1 6.257 3.88h147.484a2.38 2.38 0 0 1 2.38 2.378z M9.588 16.249v-5.71a.95.95 0 0 1 .951-.95h5.71a.95.95 0 0 1 .95.95v5.71a.95.95 0 0 1-.95.951h-5.71a.95.95 0 0 1-.951-.951m133.212.001v-5.71a.95.95 0 0 1 .951-.951h5.71a.95.95 0 0 1 .95.951v5.71a.95.95 0 0 1-.95.951h-5.71a.95.95 0 0 1-.951-.951m0 45.672v-5.71a.95.95 0 0 1 .952-.951h5.709a.95.95 0 0 1 .951.951v5.71a.95.95 0 0 1-.951.951h-5.71a.95.95 0 0 1-.95-.951M9.587 149.46v-5.708a.95.95 0 0 1 .952-.952h5.709a.95.95 0 0 1 .951.952v5.709a.95.95 0 0 1-.951.951h-5.71a.95.95 0 0 1-.951-.951m47.576 0v-5.709a.95.95 0 0 1 .952-.951h5.709a.95.95 0 0 1 .951.951v5.71a.95.95 0 0 1-.951.95h-5.71a.95.95 0 0 1-.951-.95m51.382-90.396H95.569a34.6 34.6 0 0 0-34.6 34.6v12.976",viewW:160,viewH:160}},{id:"chair",group:"furniture",category:"chair",w:50,h:50,art:{d:"M39.452 12.433H10.548a4.398 4.398 0 1 1 0-8.796h28.904a4.398 4.398 0 0 1 0 8.796m-28.904 0H8.035a4.4 4.4 0 0 0-4.398 4.399V40.08a6.283 6.283 0 0 0 6.283 6.284h30.16a6.283 6.283 0 0 0 6.284-6.284V16.832c0-2.43-1.97-4.399-4.399-4.399h-2.513",viewW:50,viewH:50}},{id:"chair_bar",group:"furniture",category:"chair",w:45,h:48,art:{d:"M38.579 17.168c1.326 2.526 1.947 6.015 1.947 9.071 0 10.018-8.07 18.139-18.025 18.139S4.475 36.257 4.475 26.239c0-3.056.586-6.552 1.912-9.078m32.192.007a3.59 3.59 0 0 1-3.333-.976m3.333.976a3.6 3.6 0 0 0 1.765-.976m-33.827.996-.13-.027a3.6 3.6 0 0 1-1.731-.969m30.59 0a18 18 0 0 0-5.848-3.932 17.93 17.93 0 0 0-13.796 0 18 18 0 0 0-5.848 3.932 3.59 3.59 0 0 1-3.237.996m28.729-.996a3.59 3.59 0 0 0 5.098 0m-33.827.996a3.6 3.6 0 0 1-1.861-.996 3.644 3.644 0 0 1 0-5.13 25.2 25.2 0 0 1 8.187-5.505A25.1 25.1 0 0 1 22.5 3.623a25.1 25.1 0 0 1 9.657 1.934 25.2 25.2 0 0 1 8.187 5.505 3.644 3.644 0 0 1 0 5.13",viewW:45,viewH:48}},{id:"armchair",group:"furniture",category:"armchair",w:90,h:90,art:{d:"M75.702 13.011q.012-.225.012-.453c0-4.843-3.917-8.768-8.748-8.768H23.034c-4.83 0-8.747 3.925-8.747 8.768q0 .228.011.453M77.132 72.09q.59.091 1.206.092c4.348 0 7.873-3.533 7.873-7.892V20.45c0-4.359-3.525-7.892-7.873-7.892-.924 0-1.812.16-2.636.453a7.89 7.89 0 0 0-5.237 7.438v.148m0 0a8.7 8.7 0 0 1-3.499.73H23.034a8.7 8.7 0 0 1-3.499-.73m50.93 0V64.29a7.89 7.89 0 0 0 6.667 7.8M14.298 13.01a7.89 7.89 0 0 1 5.237 7.438v.148m0 0V64.29a7.89 7.89 0 0 1-6.666 7.8q-.59.091-1.206.092c-4.348 0-7.873-3.533-7.873-7.892V20.45c0-4.359 3.525-7.892 7.873-7.892.924 0 1.811.16 2.635.453M77.132 72.09c-1.585 8.05-8.667 14.12-17.164 14.12H30.032c-8.496 0-15.578-6.07-17.163-14.12",viewW:90,viewH:90}},{id:"armchair_office",group:"furniture",category:"armchair",w:65,h:65,art:{d:"M56.968 18.845q0-.368-.05-.72M49.051 13.8H15.95a5.04 5.04 0 0 1-5.038-5.043 5.04 5.04 0 0 1 5.038-5.043h33.103a5.04 5.04 0 0 1 5.038 5.043 5.04 5.04 0 0 1-5.038 5.043m-33.103 0h-2.88a5.04 5.04 0 0 0-4.986 4.323M49.052 13.8h2.879a5.04 5.04 0 0 1 4.986 4.324m.051 24.826a4.32 4.32 0 0 0 4.318-4.323v-16.18a4.32 4.32 0 0 0-4.369-4.322m.051 24.826a4.32 4.32 0 0 1-4.318-4.323V22.447a4.32 4.32 0 0 1 4.267-4.322m-11.84 37.24h4.695c.736 0 1.446-.111 2.115-.317a7.21 7.21 0 0 0 5.081-6.889v-5.208M8.084 18.124h-.051a4.32 4.32 0 0 0-4.318 4.323v16.18a4.32 4.32 0 0 0 4.318 4.324 4.32 4.32 0 0 0 4.317-4.323V22.447a4.32 4.32 0 0 0-4.266-4.323m11.935 37.24h-4.79a7.2 7.2 0 0 1-2.04-.294 7.21 7.21 0 0 1-5.156-6.91v-5.21m37.045 12.414 5.177 5.183c.969.97 2.534.987 3.524.038a2.526 2.526 0 0 0 .038-3.605l-1.93-1.932m-6.81.316H20.02m-6.83-.294-1.909 1.91c-.998 1-.98 2.627.038 3.605.99.949 2.555.932 3.524-.038l5.177-5.183",viewW:65,viewH:65}},{id:"sofa",group:"furniture",category:"sofa",w:180,h:90,art:{d:"M90 19.365v57.111m0-57.11V7.683m0 11.681c0 2.151-1.836 3.894-4.1 3.894H30.544a10.7 10.7 0 0 1-4.136-.825M90 19.365c0 2.151 1.836 3.894 4.1 3.894h55.356c1.472 0 2.871-.294 4.137-.825M90 76.476c0 5.376-4.59 9.735-10.25 9.735H30.543c-5.266 0-9.605-3.772-10.185-8.625M90 76.476c0 5.376 4.59 9.735 10.25 9.735h49.206c5.266 0 9.605-3.772 10.185-8.625M90 7.684c0-2.15-1.837-3.894-4.101-3.894H30.544c-5.661 0-10.25 4.358-10.25 9.735q0 .416.036.825M90 7.684c0-2.15 1.836-3.894 4.1-3.894h55.356c5.661 0 10.25 4.358 10.25 9.735q0 .416-.036.825m0 0a10.7 10.7 0 0 1 4.137-.825h2.05c5.662 0 10.252 4.358 10.252 9.734v45.43c0 5.376-4.59 9.734-10.251 9.734h-2.05c-1.484 0-2.894-.3-4.167-.837m0 0c-3.585-1.517-6.084-4.93-6.084-8.898V23.26q0-.417.036-.825c.32-3.622 2.728-6.68 6.077-8.084m-139.34 0c3.35 1.404 5.758 4.462 6.078 8.084q.036.408.036.825v45.43c0 3.967-2.5 7.38-6.085 8.897a10.7 10.7 0 0 1-4.166.837h-2.05c-5.662 0-10.251-4.358-10.251-9.735V23.26c0-5.376 4.59-9.734 10.25-9.734h2.05c1.473 0 2.872.294 4.138.825",viewW:180,viewH:90}},{id:"sofa_three_seat",group:"furniture",category:"sofa",w:240,h:90,art:{d:"M86.553 18.593V76.96m0-58.366V7.491m0 11.102c0 2.044-1.762 3.701-3.935 3.701H29.496c-1.413 0-2.756-.28-3.97-.784m61.027-2.917c0 2.044 1.762 3.701 3.935 3.701h59.024c2.174 0 3.935-1.657 3.935-3.7M86.553 76.958c0 5.11-4.404 9.252-9.837 9.252h-47.22c-5.054 0-9.218-3.585-9.775-8.198m66.832-1.054c0 5.11 4.404 9.252 9.837 9.252h47.22c5.433 0 9.837-4.142 9.837-9.252m0-58.366V76.96m0-58.366V7.491m0 11.102c0 2.044 1.762 3.701 3.936 3.701h53.122c1.413 0 2.756-.28 3.97-.784m-61.028 55.449c0 5.11 4.405 9.252 9.838 9.252h47.22c5.054 0 9.218-3.585 9.775-8.198M86.553 7.491c0-2.044-1.762-3.701-3.935-3.701H29.496c-5.433 0-9.838 4.142-9.838 9.252q0 .396.035.784m66.86-6.335c0-2.044 1.762-3.701 3.935-3.701h59.024c2.174 0 3.935 1.657 3.935 3.7m0 0c0-2.043 1.762-3.7 3.936-3.7h53.122c5.433 0 9.838 4.142 9.838 9.252 0 .258.022.517 0 .77m-.063 64.201c1.22.512 2.574.796 3.998.796h1.967c5.433 0 9.837-4.142 9.837-9.252V22.294c0-5.11-4.404-9.252-9.837-9.252h-1.967c-5.153 0-9.38 3.725-9.803 8.468q-.036.387-.035.784v47.263c0 3.771 2.399 7.015 5.84 8.456M19.693 13.826a10.3 10.3 0 0 0-3.97-.784h-1.967c-5.433 0-9.838 4.142-9.838 9.252v47.263c0 5.11 4.405 9.252 9.838 9.252h1.967c1.424 0 2.777-.284 3.998-.796 3.441-1.441 5.84-4.685 5.84-8.456V22.294a9 9 0 0 0-.035-.784c-.307-3.443-2.618-6.349-5.833-7.684",viewW:240,viewH:90}},{id:"sofa_corner_right",group:"furniture",category:"sofa",w:260,h:170,art:{d:"M93.673 20.687V83.69m0-63.004v-12.6m0 12.6c0 2.32-1.913 4.2-4.273 4.2H31.704a10.8 10.8 0 0 1-4.311-.89m66.28-3.31c0 2.32 1.914 4.2 4.274 4.2h64.106c2.36 0 4.274-1.88 4.274-4.2M93.673 83.692c0 5.8-4.783 10.501-10.684 10.501H31.704c-5.489 0-10.011-4.068-10.616-9.304m72.585-1.197c0 5.8 4.784 10.501 10.684 10.501h51.286c5.9 0 10.684-4.701 10.684-10.5m0-63.005V83.69m0-63.004v-12.6m0 12.6c0 2.32 1.914 4.2 4.273 4.2h57.696c1.534 0 2.993-.317 4.312-.89m-66.28 59.695v71.923c0 5.799 4.782 10.5 10.684 10.5h51.284c5.49 0 10.012-4.068 10.616-9.304V84.888M93.673 8.086c0-2.32-1.913-4.2-4.273-4.2H31.704c-5.9 0-10.684 4.7-10.684 10.5q0 .45.038.89m72.615-7.19c0-2.32 1.914-4.2 4.274-4.2h64.106c2.36 0 4.274 1.88 4.274 4.2m0 0c0-2.32 1.914-4.2 4.273-4.2h57.696c5.9 0 10.685 4.7 10.685 10.5 0 .293.024.587 0 .874m-.07 69.628c1.327.58 2.797.904 4.344.904h2.136c5.901 0 10.684-4.702 10.684-10.501V24.887c0-5.8-4.783-10.5-10.684-10.5h-2.136c-5.596 0-10.187 4.227-10.647 9.61q-.038.44-.038.89V75.29c0 4.28 2.605 7.961 6.342 9.597M21.059 15.276a10.8 10.8 0 0 0-4.312-.89H14.61c-5.9 0-10.684 4.701-10.684 10.5v50.405c0 5.8 4.783 10.5 10.684 10.5h2.137c1.546 0 3.016-.322 4.342-.903 3.737-1.636 6.342-5.317 6.342-9.597V24.887q0-.45-.037-.89c-.334-3.907-2.844-7.206-6.335-8.72",viewW:260,viewH:170}},{id:"bed_single",group:"furniture",category:"bed",w:90,h:200,art:{d:"M3.79 14.024c0 5.59 4.318 10.122 9.645 10.122h63.13c5.328 0 9.646-4.532 9.646-10.122m-22.797 46.93V46.23c0-5.082-3.926-9.201-8.769-9.201h-19.29c-4.842 0-8.768 4.12-8.768 9.201v14.723m-22.797 0h82.42m0 22.085H3.79m8.768 113.06h64.885c4.842 0 8.768-4.12 8.768-9.202V13.104c0-5.082-3.926-9.202-8.768-9.202H12.558c-4.843 0-8.768 4.12-8.768 9.202v173.792c0 5.082 3.925 9.202 8.768 9.202",viewW:90,viewH:200}},{id:"bed_double",group:"furniture",category:"bed",w:160,h:200,art:{d:"M3.879 14.073c0 5.618 4.516 10.172 10.088 10.172h132.065c5.571 0 10.088-4.554 10.088-10.172m-81.165 47.16V46.438c0-5.107-4.106-9.247-9.171-9.247H45.607c-5.065 0-9.17 4.14-9.17 9.247v14.795m89.877 0V46.438c0-5.107-4.107-9.247-9.172-9.247H96.966c-5.065 0-9.171 4.14-9.171 9.247v14.795m-83.916 0H156.12m0 22.192H3.88m9.17 112.673h133.9c5.065 0 9.171-4.14 9.171-9.247V13.15c0-5.107-4.106-9.247-9.171-9.247H13.05c-5.064 0-9.17 4.14-9.17 9.247v173.7c0 5.107 4.106 9.247 9.17 9.247",viewW:160,viewH:200}},{id:"nightstand",group:"furniture",category:"nightstand",w:50,h:40,art:{d:"M5.773 3.555h38.454a2.136 2.056 0 0 1 2.137 2.056v28.777a2.136 2.056 0 0 1-2.137 2.056H5.773a2.136 2.056 0 0 1-2.136-2.056V5.611a2.136 2.056 0 0 1 2.136-2.056",viewW:50,viewH:40}},{id:"cabinet_tv",group:"furniture",category:"nightstand",w:140,h:45,art:{d:"M56.772 17.88h5.315m0 0h5.315m-5.315 0v9.214m9.832-9.214 5.315 9.45 5.314-9.45M6.224 41.4h127.552c1.305 0 2.362-.94 2.362-2.1V5.7c0-1.16-1.057-2.1-2.362-2.1H6.224c-1.305 0-2.362.94-2.362 2.1v33.6c0 1.16 1.057 2.1 2.362 2.1",viewW:140,viewH:45}},{id:"cabinet_shoe",group:"furniture",category:"nightstand",w:80,h:35,art:{d:"m39.722 16.231 1.505-1.596m1.134 3.15 1.53-1.738M6.03 31.5h67.94c1.251 0 2.265-.895 2.265-2v-24c0-1.105-1.014-2-2.264-2H6.03c-1.251 0-2.265.895-2.265 2v24c0 1.105 1.014 2 2.265 2m41.966-14.305L46.92 17.1a3.4 3.4 0 0 1-1.813-.706l-4.583-3.62c-1.264-1-3.277-.326-3.475 1.161-.185 1.39-1.983 2.1-3.28 1.299l-1.532-.947c-.968-.513-2.2.103-2.2 1.1v3.463c0 1.559 1.43 2.823 3.196 2.823h14.51c1.404 0 2.542-1.005 2.542-2.245 0-1.153-.99-2.118-2.289-2.233",viewW:80,viewH:35}},{id:"cabinet_sink",group:"furniture",category:"nightstand",w:80,h:50,art:{d:"M3.765 5.773c0-1.18 1.014-2.136 2.265-2.136h67.94c1.251 0 2.265.956 2.265 2.136v38.454c0 1.18-1.014 2.137-2.264 2.137H6.03c-1.251 0-2.265-.957-2.265-2.137z M36.088 32.135c2.525 1.88 6.205 1.88 8.73 0s3.167-5.101 1.52-7.637l-5.879-8.043-5.893 8.043c-1.644 2.536-1.003 5.755 1.522 7.637",viewW:80,viewH:50}},{id:"bookshelf",group:"furniture",category:"wardrobe",w:100,h:35,art:{d:"M57.12 20.488H45.473c-.514 0-1.008.124-1.372.345s-.568.52-.568.832m4.076-8.236h5.434M6.119 3.5h87.762c1.276 0 2.31.895 2.31 2v24c0 1.105-1.034 2-2.31 2H6.12c-1.275 0-2.31-.895-2.31-2v-24c0-1.105 1.035-2 2.31-2m51 6.4v14.118H45.474a2.04 2.04 0 0 1-1.372-.517 1.7 1.7 0 0 1-.568-1.248V11.665c0-.468.204-.917.568-1.248a2.04 2.04 0 0 1 1.372-.517z",viewW:100,viewH:35}},{id:"wall_unit",group:"furniture",category:"wardrobe",w:240,h:45,art:{d:"M6.336 41.4h227.328a2.418 2.1 0 0 0 2.418-2.1V5.7a2.418 2.1 0 0 0-2.418-2.1H6.336a2.418 2.1 0 0 0-2.418 2.1v33.6a2.418 2.1 0 0 0 2.418 2.1m43.744-5.88V9.48m46.432 26.04V9.48m46.433 26.04V9.48m46.433 26.04V9.48",viewW:240,viewH:45}},{id:"wardrobe",group:"furniture",category:"wardrobe",w:180,h:60,art:{d:"M93.87 20.25a3.3 3.3 0 0 0-1.072-2.407 3.8 3.8 0 0 0-2.586-.997c-.97 0-1.9.358-2.587.997a3.3 3.3 0 0 0-1.071 2.407c0 2.838 1.225 5.107 3.658 6.81h-.015m0 0 14.58 7.537c.57.295 1.046.726 1.377 1.25.331.523.505 1.12.505 1.727v1.401a3.3 3.3 0 0 1-1.071 2.407 3.8 3.8 0 0 1-2.587.998H77.393a3.8 3.8 0 0 1-2.586-.998 3.3 3.3 0 0 1-1.072-2.407v-1.4c0-.608.174-1.205.505-1.728a3.55 3.55 0 0 1 1.377-1.25zM6.284 3.691h167.433c1.32 0 2.392.981 2.392 2.192v48.23c0 1.212-1.071 2.193-2.392 2.193H6.284c-1.321 0-2.392-.981-2.392-2.192V5.885c0-1.212 1.07-2.193 2.392-2.193",viewW:180,viewH:60}},{id:"kitchen_floor",group:"furniture",category:"kitchen_cabinet",w:60,h:60,art:{d:"M3.692 5.884c0-1.21.981-2.192 2.192-2.192h48.23c1.212 0 2.193.981 2.193 2.192v48.23a2.19 2.19 0 0 1-2.192 2.193H5.885a2.19 2.19 0 0 1-2.193-2.192z",viewW:60,viewH:60}},{id:"kitchen_floor_corner",group:"furniture",category:"kitchen_cabinet",w:90,h:90,art:{d:"M45.944 41.767a2.29 2.29 0 0 0 2.29 2.29H83.92a2.29 2.29 0 0 1 2.29 2.29V83.92a2.29 2.29 0 0 1-2.29 2.29H6.08a2.29 2.29 0 0 1-2.29-2.29V6.08a2.29 2.29 0 0 1 2.29-2.29h37.576a2.29 2.29 0 0 1 2.29 2.29z",viewW:90,viewH:90}},{id:"kitchen_wall",group:"furniture",category:"kitchen_cabinet",w:60,h:35,art:{d:"m4.444 31.033 50.75-27.007m-50.752-.06 50.75 27.008M5.884 31.5h48.23c1.212 0 2.193-.895 2.193-2v-24c0-1.105-.981-2-2.192-2H5.885c-1.212 0-2.193.895-2.193 2v24c0 1.105.981 2 2.192 2",viewW:60,viewH:35}},{id:"kitchen_wall_corner",group:"furniture",category:"kitchen_cabinet",w:60,h:60,art:{d:"M3.772 29.59 29.88 4.383M4.57 55.43 55.43 30M4.175 4.383l26.701 23.863m-27.184.877L55.43 55.43M28.41 3.692H5.884c-1.21 0-2.192.981-2.192 2.192v48.23c0 1.212.981 2.193 2.192 2.193h48.23a2.19 2.19 0 0 0 2.193-2.192V31.589c0-1.21-.981-2.192-2.192-2.192h-21.32a2.19 2.19 0 0 1-2.193-2.192V5.885a2.19 2.19 0 0 0-2.192-2.193",viewW:60,viewH:60}},{id:"shelf_floor",group:"furniture",category:"shelving",w:100,h:35,art:{d:"M6.12 31.5h87.76a2.31 2 0 0 0 2.31-2v-24a2.31 2 0 0 0-2.31-2H6.12a2.31 2 0 0 0-2.31 2v24a2.31 2 0 0 0 2.31 2 M9.089 25.933c-.494.112-.776.546-.63.97s.664.675 1.157.564l-.263-.767zM90.912 9.067c.493-.112.775-.546.63-.97s-.664-.675-1.158-.564l.264.767zM11.875 26.956c.493-.112.775-.546.63-.97s-.664-.676-1.158-.564l.264.767zm3.988-2.556c-.493.112-.775.546-.63.969s.664.676 1.158.564l-.264-.766zm5.045.511c.493-.112.775-.545.63-.969s-.664-.676-1.158-.564l.264.766zm3.988-2.555c-.493.111-.775.545-.63.968.146.424.664.677 1.158.565l-.264-.767zm5.044.51c.494-.111.776-.545.63-.968-.145-.424-.664-.676-1.157-.565l.263.767zm3.99-2.555c-.495.112-.777.546-.63.97.145.423.663.675 1.157.563l-.264-.766zm5.043.511c.494-.112.776-.545.63-.969s-.664-.676-1.158-.564l.264.767zm3.989-2.555c-.494.111-.776.545-.63.969s.664.676 1.157.564l-.263-.767zm5.044.51c.494-.11.776-.545.63-.968-.146-.424-.664-.676-1.158-.565l.264.767zm3.989-2.555c-.494.112-.776.546-.63.97.145.423.664.675 1.157.564l-.264-.767zm5.044.511c.493-.111.775-.545.63-.969s-.664-.676-1.158-.564l.264.767zm3.988-2.555c-.493.112-.775.545-.63.969s.664.676 1.158.564l-.264-.767zm5.045.51c.493-.11.775-.545.63-.968-.146-.424-.664-.676-1.158-.564l.264.766zm3.988-2.555c-.493.112-.775.546-.63.97s.664.675 1.158.564l-.264-.767zm5.044.511c.494-.111.776-.545.63-.968-.145-.424-.664-.677-1.157-.565l.264.767zm3.99-2.555c-.494.112-.777.545-.63.969.145.423.663.676 1.157.564l-.264-.766zm5.043.511c.494-.112.776-.546.63-.969s-.664-.676-1.157-.564l.263.766zm3.989-2.556c-.494.112-.776.546-.63.97s.664.676 1.158.564l-.264-.767zM9.353 26.7l.263.767 2.259-.511-.264-.767-.264-.767-2.258.511zm6.774-1.533.264.766 4.517-1.022-.264-.767-.264-.766-4.517 1.022zm9.033-2.045.264.767 4.516-1.022-.264-.767-.263-.767-4.517 1.023zm9.033-2.044.264.766 4.516-1.022-.264-.766-.264-.767-4.516 1.022zm9.033-2.045.263.767 4.517-1.022-.264-.767-.264-.767-4.516 1.023zm9.032-2.044.264.767 4.517-1.023-.264-.766-.264-.767-4.516 1.022zm9.033-2.045.264.767 4.517-1.022-.264-.767-.264-.766-4.517 1.022zm9.033-2.044.264.767 4.516-1.023-.263-.766-.264-.767-4.517 1.022zm9.033-2.044.264.766 4.516-1.022-.264-.767-.263-.766-4.517 1.022zM88.39 8.81l.264.767 2.258-.511-.264-.767-.264-.767-2.258.511z M9.089 9.067c-.494-.112-.776-.546-.63-.97s.664-.675 1.157-.564l-.263.767zm81.823 16.866c.493.112.775.546.63.97s-.664.675-1.158.564l.264-.767zM11.875 8.044c.493.112.775.546.63.97s-.664.676-1.158.564l.264-.767zm3.988 2.556c-.493-.112-.775-.546-.63-.969s.664-.676 1.158-.564l-.264.766zm5.045-.511c.493.112.775.545.63.969s-.664.676-1.158.564l.264-.766zm3.988 2.555c-.493-.111-.775-.545-.63-.968.146-.424.664-.677 1.158-.565l-.264.767zm5.044-.51c.494.111.776.545.63.968-.145.424-.664.676-1.157.565l.263-.767zm3.99 2.555c-.495-.112-.777-.546-.63-.97.145-.423.663-.675 1.157-.563l-.264.766zm5.043-.511c.494.112.776.545.63.969s-.664.676-1.158.564l.264-.767zm3.989 2.555c-.494-.111-.776-.545-.63-.969s.664-.676 1.157-.564l-.263.767zm5.044-.51c.494.11.776.545.63.968-.146.424-.664.676-1.158.565l.264-.767zm3.989 2.555c-.494-.112-.776-.546-.63-.97.145-.423.664-.675 1.157-.564l-.264.767zm5.044-.511c.493.111.775.545.63.969s-.664.676-1.158.564l.264-.767zm3.988 2.555c-.493-.112-.775-.545-.63-.969s.664-.676 1.158-.564l-.264.767zm5.045-.51c.493.11.775.545.63.968-.146.424-.664.676-1.158.564l.264-.766zm3.988 2.555c-.493-.112-.775-.546-.63-.97s.664-.675 1.158-.564l-.264.767zm5.044-.511c.494.111.776.545.63.968-.145.424-.664.677-1.157.565l.264-.767zm3.99 2.555c-.494-.112-.777-.545-.63-.969.145-.423.663-.676 1.157-.564l-.264.766zm5.043-.511c.494.112.776.546.63.969s-.664.676-1.157.564l.263-.766zm3.989 2.556c-.494-.112-.776-.546-.63-.97s.664-.676 1.158-.564l-.264.767zM9.353 8.3l.263-.767 2.259.511-.264.767-.264.767-2.258-.511zm6.774 1.533.264-.766 4.517 1.022-.264.767-.264.766-4.517-1.022zm9.033 2.045.264-.767 4.516 1.022-.264.767-.263.767-4.517-1.023zm9.033 2.044.264-.766 4.516 1.022-.264.766-.264.767-4.516-1.022zm9.033 2.045.263-.767 4.517 1.022-.264.767-.264.767-4.516-1.023zm9.032 2.044.264-.767 4.517 1.023-.264.766-.264.767-4.516-1.022zm9.033 2.045.264-.767 4.517 1.022-.264.767-.264.766-4.517-1.022zm9.033 2.044.264-.767 4.516 1.023-.263.766-.264.767-4.517-1.022zm9.033 2.044.264-.766 4.516 1.022-.264.767-.263.766-4.517-1.022zm9.033 2.045.264-.767 2.258.511-.264.767-.264.767-2.258-.511z",viewW:100,viewH:35}},{id:"shelf_wall",group:"furniture",category:"shelving",w:100,h:25,art:{d:"M18.36 3.15v2.567c0 1.012-1.034 1.833-2.31 1.833h-.924c-1.275 0-2.309-.82-2.309-1.833V3.15m73.905 0v2.567c0 1.012-1.034 1.833-2.31 1.833h-.924c-1.275 0-2.31-.82-2.31-1.833V3.15m-75.29 0H93.65c1.276 0 2.31.82 2.31 1.833V19.65c0 1.012-1.034 1.833-2.31 1.833H5.888c-1.275 0-2.31-.82-2.31-1.833V4.983c0-1.012 1.035-1.833 2.31-1.833",viewW:100,viewH:25}},{id:"cooktop_two",group:"appliance",category:"cooktop",w:30,h:50,art:{d:"M15 30.982v-1.71m3.086 5.128h1.542M15 39.527v-1.709M10.372 34.4h1.542M5.84 46.364h18.322c1.331 0 2.41-1.196 2.41-2.67V6.306c0-1.475-1.079-2.67-2.41-2.67H5.839c-1.331 0-2.41 1.195-2.41 2.67v37.386c0 1.475 1.079 2.67 2.41 2.67m14.56-29.91c0 3.304-2.417 5.982-5.4 5.982-2.981 0-5.4-2.678-5.4-5.981s2.419-5.982 5.4-5.982c2.983 0 5.4 2.678 5.4 5.982M18.858 34.4c0 2.36-1.727 4.273-3.857 4.273s-3.857-1.913-3.857-4.273S12.87 30.127 15 30.127s3.857 1.913 3.857 4.273",viewW:30,viewH:50}},{id:"stove",group:"appliance",category:"cooktop",w:60,h:60,art:{d:"M41.4 35.261a.877.877 0 1 0-1.754 0zm4.384 6.138a.877.877 0 1 0 0-1.753zM41.4 44.03a.877.877 0 1 0-1.753 0zm-4.384-2.63a.877.877 0 1 0 0-1.754zM5.885 3.691v.877h48.23V2.815H5.885zm50.422 2.192h-.877v48.23h1.754V5.885zm-2.192 50.423v-.877H5.885v1.754h48.23zM3.692 54.115h.877V5.885H2.815v48.23zm2.192 2.192v-.877a1.315 1.315 0 0 1-1.315-1.315H2.815a3.07 3.07 0 0 0 3.07 3.069zm50.423-2.192h-.877c0 .726-.589 1.315-1.315 1.315v1.754a3.07 3.07 0 0 0 3.069-3.07zM54.115 3.692v.877c.726 0 1.315.589 1.315 1.315h1.754a3.07 3.07 0 0 0-3.07-3.069zm-48.23 0v-.877a3.07 3.07 0 0 0-3.07 3.07h1.754c0-.727.589-1.316 1.315-1.316zm19.73 36.83h-.877a5.26 5.26 0 0 1-5.262 5.262v1.754a7.015 7.015 0 0 0 7.016-7.015zm-6.139 6.139v-.877a5.26 5.26 0 0 1-5.261-5.261h-1.754a7.015 7.015 0 0 0 7.015 7.015zm-6.138-6.138h.877a5.26 5.26 0 0 1 5.261-5.262v-1.754a7.015 7.015 0 0 0-7.015 7.016zm6.138-6.139v.877a5.26 5.26 0 0 1 5.262 5.262h1.754a7.015 7.015 0 0 0-7.016-7.016zm27.185-14.908h-.877a5.26 5.26 0 0 1-5.261 5.262v1.754a7.015 7.015 0 0 0 7.015-7.016zm-6.138 6.139v-.877a5.26 5.26 0 0 1-5.262-5.262h-1.754a7.015 7.015 0 0 0 7.016 7.016zm-6.139-6.139h.877a5.26 5.26 0 0 1 5.262-5.261v-1.754a7.015 7.015 0 0 0-7.016 7.015zm6.139-6.138v.877a5.26 5.26 0 0 1 5.261 5.261h1.754a7.015 7.015 0 0 0-7.015-7.015zm4.384 27.185h-.877a3.51 3.51 0 0 1-3.507 3.507v1.754a5.26 5.26 0 0 0 5.261-5.261zm-4.384 4.384v-.877a3.51 3.51 0 0 1-3.508-3.507H35.26a5.26 5.26 0 0 0 5.262 5.261zm-4.385-4.384h.877a3.51 3.51 0 0 1 3.508-3.508V35.26a5.26 5.26 0 0 0-5.262 5.262zm4.385-4.385v.877a3.51 3.51 0 0 1 3.507 3.508h1.754a5.26 5.26 0 0 0-5.261-5.262zm0 .877h.876V35.26h-1.753v1.754zm3.508 3.508v.876h1.753v-1.753h-1.753zm-3.508 5.261h.876V44.03h-1.753v1.754zm-5.261-5.261v.876h1.753v-1.753h-1.753zm-9.647-21.047h-.877a5.26 5.26 0 0 1-5.262 5.262v1.754a7.015 7.015 0 0 0 7.016-7.016zm-6.139 6.139v-.877a5.26 5.26 0 0 1-5.261-5.262h-1.754a7.015 7.015 0 0 0 7.015 7.016zm-6.138-6.139h.877a5.26 5.26 0 0 1 5.261-5.261v-1.754a7.015 7.015 0 0 0-7.015 7.015zm6.138-6.138v.877a5.26 5.26 0 0 1 5.262 5.261h1.754a7.015 7.015 0 0 0-7.016-7.015z",viewW:60,viewH:60}},{id:"tv",group:"appliance",category:"tv",w:120,h:28,art:{d:"M45.134 10.766H6.18c-1.292 0-2.34.848-2.34 1.894v2.68c0 1.045 1.048 1.893 2.34 1.893h38.954m0-6.467V5.288c0-1.046 1.048-1.894 2.34-1.894h26.704c1.292 0 2.34.848 2.34 1.894v5.478m-31.384 0h31.384m0 0h37.302c1.293 0 2.34.848 2.34 1.894v2.68c0 1.045-1.047 1.893-2.34 1.893H76.518m-31.384 0v5.479c0 1.046 1.048 1.894 2.34 1.894h26.704c1.292 0 2.34-.848 2.34-1.894v-5.479m-31.384 0h31.384",viewW:120,viewH:28}},{id:"tv_wall",group:"appliance",category:"tv",w:120,h:15,art:{d:"M35.664 11.4c-.517 0-.936.269-.936.6s.419.6.936.6zm49.553 1.2c.517 0 .936-.269.936-.6s-.42-.6-.936-.6zM6.18 3v.6h107.64V2.4H6.18zm109.98 1.5h-.936v1.8h1.872V4.5zm-2.34 3.3v-.6H6.18v1.2h107.64zM3.84 6.3h.936V4.5H2.904v1.8zm2.34 1.5v-.6c-.775 0-1.404-.403-1.404-.9H2.904c0 1.16 1.467 2.1 3.276 2.1zm109.98-1.5h-.936c0 .497-.629.9-1.404.9v1.2c1.81 0 3.276-.94 3.276-2.1zM113.82 3v.6c.775 0 1.404.403 1.404.9h1.872c0-1.16-1.467-2.1-3.276-2.1zM6.18 3v-.6c-1.81 0-3.276.94-3.276 2.1h1.872c0-.497.629-.9 1.404-.9zm61.308 4.2h-.936V12h1.872V7.2zM52.512 12h.936V7.2h-1.872V12zm-16.848 0v.6h49.553v-1.2H35.664z",viewW:120,viewH:15}},{id:"toilet",group:"sanitary",category:"toilet",w:40,h:70,art:{d:"M3.555 48.976c0 9.55 7.125 17.29 15.914 17.29h1.061c8.79 0 15.914-7.74 15.914-17.29V5.966c0-1.233-.92-2.233-2.056-2.233H5.611c-1.135 0-2.056 1-2.056 2.234z M7.666 49.12c0 7.003 5.225 12.68 11.67 12.68h1.327c6.445 0 11.67-5.677 11.67-12.68V31.873c0-1.233-.92-2.233-2.056-2.233H9.722c-1.135 0-2.056 1-2.056 2.233zM33.792 3.733c1.465 0 2.652 1.29 2.652 2.882v12.103c0 1.592-1.187 2.882-2.652 2.882H6.208c-1.465 0-2.653-1.29-2.653-2.882V6.615c0-1.591 1.188-2.882 2.653-2.882z M24.11 50.186c0 2.467-1.84 4.467-4.11 4.467s-4.111-2-4.111-4.467 1.84-4.466 4.11-4.466 4.112 2 4.112 4.466",viewW:40,viewH:70}},{id:"toilet_built_in",group:"sanitary",category:"toilet",w:40,h:55,art:{d:"M3.555 34.56c0 9.263 7.125 16.774 15.914 16.774h1.061c8.79 0 15.914-7.51 15.914-16.775V5.833c0-1.196-.92-2.166-2.056-2.166H5.611c-1.135 0-2.056.97-2.056 2.166z M7.666 34.7c0 6.793 5.225 12.3 11.67 12.3h1.327c6.445 0 11.67-5.507 11.67-12.3V14.5c0-1.197-.92-2.167-2.056-2.167H9.722c-1.135 0-2.056.97-2.056 2.167z M24.11 35.733c0 2.394-1.84 4.334-4.11 4.334s-4.111-1.94-4.111-4.334 1.84-4.333 4.11-4.333 4.112 1.94 4.112 4.333",viewW:40,viewH:55}},{id:"bathtub",group:"sanitary",category:"bathtub",w:170,h:75,art:{d:"M3.885 6c0-1.243 1.068-2.25 2.386-2.25h157.458c1.318 0 2.386 1.007 2.386 2.25v63c0 1.243-1.068 2.25-2.386 2.25H6.27c-1.318 0-2.386-1.007-2.386-2.25z M13.428 37.5c0-13.669 11.75-24.75 26.243-24.75h90.658c14.493 0 26.243 11.081 26.243 24.75s-11.75 24.75-26.243 24.75H39.67c-14.493 0-26.243-11.081-26.243-24.75 M31.56 37.5c0 2.237-1.923 4.05-4.295 4.05s-4.294-1.813-4.294-4.05 1.923-4.05 4.294-4.05c2.372 0 4.295 1.813 4.295 4.05",viewW:170,viewH:75}},{id:"bathtub_corner",group:"sanitary",category:"bathtub",w:140,h:140,art:{d:"M3.862 6.224a2.36 2.36 0 0 1 2.362-2.362H133.97c1.197 0 2.168.97 2.168 2.168 0 71.857-58.251 130.108-130.108 130.108a2.17 2.17 0 0 1-2.168-2.168z M13.31 32.207c0-10.437 8.46-18.897 18.897-18.897h87.628a2.13 2.13 0 0 1 2.13 2.13c0 58.833-47.692 106.526-106.524 106.526a2.13 2.13 0 0 1-2.13-2.131z M32.207 27.483a4.724 4.724 0 1 1-9.449 0 4.724 4.724 0 0 1 9.449 0",viewW:140,viewH:140}},{id:"bidet",group:"sanitary",category:"bidet",w:40,h:60,art:{d:"M22.466 3.692v10.085c0 1.21-.92 2.192-2.055 2.192h-.822c-1.136 0-2.056-.981-2.056-2.192V3.692m4.933 8.77h2.878c1.135 0 2.056-.982 2.056-2.193v-.877c0-1.21-.92-2.192-2.056-2.192h-2.878m-4.933 5.262h-2.878c-1.135 0-2.055-.982-2.055-2.193v-.877c0-1.21.92-2.192 2.055-2.192h2.878M5.611 3.692h28.777c1.136 0 2.056.981 2.056 2.192v33.45c0 9.374-7.125 16.973-15.914 16.973h-1.06c-8.79 0-15.915-7.599-15.915-16.972V5.885c0-1.212.92-2.193 2.056-2.193m4.11 15.785h20.556c1.136 0 2.056.981 2.056 2.192v17.807c0 6.874-5.225 12.447-11.67 12.447h-1.327c-6.445 0-11.67-5.573-11.67-12.447V21.67c0-1.21.92-2.192 2.056-2.192m13.567 7.892c0 1.937-1.472 3.508-3.288 3.508-1.817 0-3.29-1.57-3.29-3.508s1.473-3.507 3.29-3.507 3.288 1.57 3.288 3.507",viewW:40,viewH:60}},{id:"bidet_built_in",group:"sanitary",category:"bidet",w:40,h:50,art:{d:"M17.318 12.182h-8.06c-.88 0-1.592.74-1.592 1.654v16.126c0 6.698 5.225 12.129 11.67 12.129h1.327c6.445 0 11.67-5.43 11.67-12.13V13.837c0-.914-.713-1.654-1.591-1.654H22.68m-.215-8.545v9.827c0 1.18-.92 2.136-2.055 2.136h-.822c-1.136 0-2.056-.956-2.056-2.136V3.637m10.689 8.545V3.637m-16.445 0v8.545M5.611 3.637h28.777c1.136 0 2.056.956 2.056 2.136v24.051c0 9.135-7.125 16.54-15.914 16.54h-1.06c-8.79 0-15.915-7.405-15.915-16.54V5.773c0-1.18.92-2.136 2.056-2.136M23.289 23.29c0 1.888-1.473 3.418-3.29 3.418s-3.288-1.53-3.288-3.418 1.472-3.418 3.289-3.418c1.816 0 3.288 1.53 3.288 3.418",viewW:40,viewH:50}},{id:"kitchen_sink",group:"sanitary",category:"kitchen_sink",w:60,h:50,art:{d:"M38.188 8.7h10.598c1.211 0 2.193.956 2.193 2.136v28.2c0 1.18-.982 2.136-2.193 2.136H11.08c-1.21 0-2.192-.956-2.192-2.136v-28.2c0-1.18.981-2.136 2.192-2.136h10.599m10.885-5.128V15.11c0 1.18-.981 2.136-2.192 2.136h-.877c-1.21 0-2.192-.957-2.192-2.136V3.572m5.261 7.691h3.07c1.21 0 2.192-.956 2.192-2.136v-.855c0-1.18-.982-2.136-2.192-2.136h-3.07m-5.261 5.127h-3.07c-1.21 0-2.192-.956-2.192-2.136v-.855c0-1.18.982-2.136 2.193-2.136h3.069M5.817 46.299h48.231c1.21 0 2.192-.956 2.192-2.136V5.709c0-1.18-.981-2.137-2.192-2.137H5.818c-1.211 0-2.193.957-2.193 2.137v38.454c0 1.18.982 2.136 2.192 2.136M33.44 25.79c0 1.888-1.57 3.418-3.507 3.418s-3.508-1.53-3.508-3.418c0-1.887 1.57-3.418 3.508-3.418s3.507 1.53 3.507 3.418",viewW:60,viewH:50}},{id:"kitchen_sink_double",group:"sanitary",category:"kitchen_sink",w:90,h:50,art:{d:"M53.173 12.182c-.468 0-.847.354-.847.79s.38.79.847.79zm21.979 1.58c.467 0 .846-.353.846-.79 0-.436-.379-.79-.846-.79zM6.01 3.572v.79h77.842v-1.58H6.01zM86.14 5.71h-.847v38.454h1.694V5.709zM83.85 46.3v-.79H6.01v1.58h77.842zM3.72 44.163h.847V5.709H2.873v38.454zm2.29 2.136v-.79c-.797 0-1.443-.603-1.443-1.346H2.873c0 1.616 1.404 2.926 3.137 2.926zm80.131-2.136h-.847c0 .743-.646 1.346-1.442 1.346v1.58c1.732 0 3.136-1.31 3.136-2.926zm-2.29-40.59v.79c.797 0 1.443.602 1.443 1.346h1.694c0-1.617-1.404-2.927-3.136-2.927zm-77.841 0v-.79c-1.733 0-3.137 1.31-3.137 2.926h1.694c0-.744.646-1.347 1.443-1.347zm5.494 37.599v.79h28.39v-1.58h-28.39zm30.68-2.136h.846v-28.2h-1.694v28.2zm-32.97-28.2h-.846v28.2h1.694v-28.2zM39.895 8.7v-.79h-5.669v1.58h5.669zm-22.721 0v-.79h-5.669v1.58h5.669zm-7.958 2.136h.847c0-.744.645-1.346 1.442-1.346V7.91c-1.732 0-3.136 1.31-3.136 2.926zm32.968 0h.847c0-1.616-1.404-2.927-3.136-2.927v1.58c.797 0 1.442.603 1.442 1.347zm-2.29 30.336v.79c1.733 0 3.137-1.31 3.137-2.926h-1.694c0 .743-.645 1.346-1.442 1.346zm-28.389 0v-.79c-.797 0-1.442-.603-1.442-1.346H8.368c0 1.616 1.404 2.926 3.136 2.926zm13.737-23.927v.79h.916v-1.58h-.916zm3.205-2.136h.847V3.572H27.6V15.11zM22.952 3.572h-.847V15.11h1.693V3.572zm3.205 13.673v.79c1.732 0 3.136-1.31 3.136-2.926H27.6c0 .743-.646 1.346-1.443 1.346zm-.916 0v-.79c-.797 0-1.443-.603-1.443-1.346h-1.693c0 1.616 1.404 2.926 3.136 2.926zm3.205-5.982v.79h3.206v-1.58h-3.206zm5.495-2.136h.847v-.855h-1.694v.855zm-2.29-2.991v-.79h-3.205v1.58h3.206zm2.29 2.136h.847c0-1.616-1.404-2.926-3.136-2.926v1.58c.796 0 1.442.603 1.442 1.346zm-2.29 2.991v.79c1.733 0 3.137-1.31 3.137-2.926h-1.694c0 .743-.646 1.346-1.442 1.346zm-11.905 0v.79h3.206v-1.58h-3.206zm3.206-5.127v-.79h-3.206v1.58h3.206zm-5.495 2.136h-.847v.855h1.694v-.855zm2.29-2.136v-.79c-1.733 0-3.137 1.31-3.137 2.926h1.694c0-.743.646-1.346 1.442-1.346zm0 5.127v-.79c-.797 0-1.443-.603-1.443-1.346H16.61c0 1.616 1.404 2.926 3.136 2.926zm9.615 14.527h-.847c0 1.452-1.26 2.628-2.816 2.628v1.58c2.49 0 4.51-1.884 4.51-4.208zM25.7 29.208v-.79c-1.555 0-2.816-1.176-2.816-2.628H21.19c0 2.324 2.02 4.209 4.51 4.209zm-3.663-3.418h.847c0-1.451 1.26-2.628 2.816-2.628v-1.58c-2.49 0-4.51 1.884-4.51 4.208zm3.663-3.418v.79c1.555 0 2.816 1.177 2.816 2.628h1.694c0-2.324-2.02-4.208-4.51-4.208zm24.268 18.8v.79h28.39v-1.58h-28.39zm30.68-2.136h.846v-28.2H79.8v28.2zM78.356 8.7v-.79h-28.39v1.58h28.39zm-30.68 2.136h-.846v28.2h1.694v-28.2zm2.29-2.136v-.79c-1.732 0-3.136 1.31-3.136 2.926h1.694c0-.744.646-1.346 1.442-1.346zm30.68 2.136h.846c0-1.616-1.404-2.927-3.136-2.927v1.58c.797 0 1.443.603 1.443 1.347zm-2.29 30.336v.79c1.732 0 3.136-1.31 3.136-2.926H79.8c0 .743-.646 1.346-1.443 1.346zm-28.39 0v-.79c-.796 0-1.442-.603-1.442-1.346H46.83c0 1.616 1.404 2.926 3.136 2.926zm3.206-28.2v.79h21.979v-1.58h-21.98z",viewW:90,viewH:50}}].map(e=>({id:e.id,group:e.group,category:e.category,w:e.w,h:e.h,art:e.art})),...nl.filter(e=>rl.has(e.id))],sl=new Map(ol.map(e=>[e.id,e]));function al(e){return e&&sl.get(e)||null}function ll(e){return ol.filter(t=>t.group===e)}function cl(e){const t=al(e);return t?{w:t.w,h:t.h}:{w:60,h:60}}function hl(e,t,i=Ta,n=1e3){const r=Number(t)>0?Number(t):5;return(Number(e)||0)/r*i/n}const dl=5e-4,ul=Sa;function pl(e){return Number.isFinite(e)?Math.max(dl,Math.min(ul,e)):dl}function ml(e){return Number.isFinite(e)?Math.max(1,Math.min(1e4,e)):1}const _l=e=>{const t=Math.round(1e3*e)/1e3;return Object.is(t,-0)?"0":String(t)};function fl(e){const t=al(e);if(!t)return null;if(t.art)return t.art;const i=function(e,t,i){if(!e.g)return"";const n=e=>_l(e*t),r=e=>_l(e*i),o=[];for(const s of e.g)if("r"===s[0]){const[,e,t,i,a]=s;o.push(`M${n(e)} ${r(t)}H${n(e+i)}V${r(t+a)}H${n(e)}Z`)}else if("l"===s[0]){const[,e,t,i,a]=s;o.push(`M${n(e)} ${r(t)}L${n(i)} ${r(a)}`)}else if("e"===s[0]){const[,e,a,l,c]=s;o.push(`M${n(e-l)} ${r(a)}A${_l(l*t)} ${_l(c*i)} 0 0 1 ${n(e+l)} ${r(a)}A${_l(l*t)} ${_l(c*i)} 0 0 1 ${n(e-l)} ${r(a)}Z`)}else{const e=s.slice(1);if(e.length<4)continue;let t=`M${n(e[0])} ${r(e[1])}`;for(let i=2;i+10?n:Number.isFinite(r)&&r>0?r:1,s=Number(t),a=o*(Number.isFinite(s)&&s>0?s:1);return Number.isFinite(a)&&a>0?a:o}const vl=e=>{let t=(e%360+360)%360;return t>180&&(t-=360),t};function yl(e,t,i,n,r,o=0){let s=null,a=r;for(const r of n){const[n,l,c,h]=r,d=c-n,u=h-l,p=d*d+u*u;if(!p)continue;const m=Math.sqrt(p);let _=((e-n)*d+(t-l)*u)/p;_=Math.max(0,Math.min(1,_));let f=n+_*d,g=l+_*u;const v=Math.hypot(e-f,t-g);if(!(v0){const e=Math.round(_*m/o)*o;f=n+e/m*d,g=l+e/m*u}s={cx:f+y*(i/2),cy:g+b*(i/2),angle:vl(180*Math.atan2(-y,b)/Math.PI),dist:v}}return s}function bl(e){const{symbol:t,widthCm:i,depthCm:n,point:r,canvasW:o,canvasH:s,cellCm:a,gridPitch:l,walls:c,wallReach:h,free:d=!1}=e;if(!(fl(t)&&s>0&&Number.isFinite(r[0])&&Number.isFinite(r[1])))return null;const u=pl(hl(i,a,l,o)),p=pl(hl(n,a,l,o));let m=r[0],_=r[1],f=0;const g=d?null:yl(m,_,p*s,c,h,l);return g&&(m=g.cx,_=g.cy,f=g.angle),{symbol:t,x:Pa(m/o-u/2),y:Pa(_/s-p/2),w:u,h:p,angle:Number(f.toFixed(2))}}const wl=15e3,kl=2e3,xl=new WeakMap;function $l(e,t){let i=xl.get(e);if(!i){const t=new Set;i={hiddenAt:"hidden"===e.visibilityState?Date.now():0,token:0,subscribers:t,onVisibility:()=>{const t=xl.get(e);if(!t)return;const i=Date.now();if("hidden"===e.visibilityState){t.hiddenAt||(t.hiddenAt=i);const e={kind:"hidden",token:t.token,at:i,hiddenFor:0,long:!1};for(const i of[...t.subscribers])i(e);return}const n=t.hiddenAt?Math.max(0,i-t.hiddenAt):0;t.hiddenAt=0,t.token++;const r={kind:"visible",token:t.token,at:i,hiddenFor:n,long:n>=wl};for(const e of[...t.subscribers])e(r)},onPageShow:t=>{const i=xl.get(e);if(!i)return;const n=Date.now();i.token++;const r={kind:"pageshow",token:i.token,at:n,hiddenFor:0,long:!!t.persisted,persisted:!!t.persisted};for(const e of[...i.subscribers])e(r)}},xl.set(e,i),e.addEventListener("visibilitychange",i.onVisibility),e.defaultView?.addEventListener("pageshow",i.onPageShow)}return i.subscribers.add(t),()=>{const i=xl.get(e);i&&(i.subscribers.delete(t),i.subscribers.size||(e.removeEventListener("visibilitychange",i.onVisibility),e.defaultView?.removeEventListener("pageshow",i.onPageShow),xl.delete(e)))}}const Sl={now:()=>Date.now(),setTimeout:(e,t)=>window.setTimeout(e,t),clearTimeout:e=>window.clearTimeout(e),requestAnimationFrame:e=>window.requestAnimationFrame(e),cancelAnimationFrame:e=>window.cancelAnimationFrame(e)};class Ml{constructor(e,t=Sl){this.onChange=e,this.clock=t,this._state="steady",this._token=0,this._frameFingerprint="",this._hasCompleteFrame=!1,this._overlayPhase="none",this._recoveryReason=null,this._overlayTimer=0,this._overlayRaf=0,this._overlayOpaqueAt=0,this._barrierRafs=new Set,this._trace=[],this._disposed=!1}get state(){return this._state}get token(){return this._token}get frameFingerprint(){return this._frameFingerprint}get hasCompleteFrame(){return this._hasCompleteFrame}get overlayPhase(){return this._overlayPhase}get overlayVisible(){return"none"!==this._overlayPhase}get overlayBlocksInteraction(){return this.overlayVisible}get recoveryReason(){return this._recoveryReason}get trace(){return this._trace.map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}note(e,t={}){this.record(e,void 0,t)}record(e,t,i={}){this._trace.push({at:this.clock.now(),token:this._token,event:e,state:this._state,...t?{reason:t}:{},...i}),this._trace.length>80&&this._trace.splice(0,this._trace.length-80)}changed(){this._disposed||this.onChange()}adoptCompleteFrame(e){e&&(this._hasCompleteFrame=!0,this._frameFingerprint=e,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-adopted"),this.changed())}markCompleteFrame(e){e&&(this._hasCompleteFrame=!0,this._frameFingerprint=e,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-complete"),this.changed())}refreshCompleteFrame(e){!this._disposed&&this._hasCompleteFrame&&"steady"===this._state&&e&&e!==this._frameFingerprint&&(this._frameFingerprint=e,this.record("frame-refreshed"))}visibility(e){return"hidden"===e.kind?(this.record("visibility-hidden"),this._token):e.long||"visible"!==e.kind&&"pageshow"!==e.kind?this.beginCandidate("pageshow"===e.kind?"pageshow":"long-resume"):(this.record("pageshow"===e.kind?"pageshow-noop":"visibility-visible-quick"),this._token)}beginCandidate(e,t="plan"){return this._token++,this._recoveryReason=t,this._hasCompleteFrame?(this._state="connection"===t?"offline-stale":"holding",this.clearOverlay()):this.overlayVisible?this._state="overlay-visible":(this._state="overlay-pending",this.scheduleOverlay(t)),this.record("candidate-start",e),this.changed(),this._token}connectionLost(){return this.beginCandidate("connection-lost","connection")}candidateReady(e){return!this._disposed&&e===this._token&&(this._state="candidate-ready",this.record("candidate-ready"),this.changed(),!0)}async commitAfterPaint(e,t){if(this._disposed||e!==this._token)return!1;let i=!0;const n=(async()=>{for(await t.updateComplete();i&&!this._disposed&&e===this._token;)if(t.stageValid()&&t.assetsReady()){if(await this.nextFrame(),e!==this._token||!t.stageValid()||!t.assetsReady())continue;if(await this.nextFrame(),e===this._token&&t.stageValid()&&t.assetsReady())return!0}else await this.nextFrame();return!1})();let r=0;const o=new Promise(e=>{r=this.clock.setTimeout(()=>e({ready:!1,timedOut:!0}),kl)}),s=await Promise.race([n.then(e=>({ready:e,timedOut:!1})),o]);return i=!1,this.clock.clearTimeout(r),!this._disposed&&e===this._token&&(!(!s.ready&&!s.timedOut)&&(s.ready?(this._hasCompleteFrame=!0,this._frameFingerprint=t.frameFingerprint(),this.record("paint-barrier"),this.finishOverlayAfterCommit(),!0):(this.record("paint-barrier-timeout"),this._hasCompleteFrame?(this._state="connection"===this._recoveryReason?"offline-stale":"steady",this.clearOverlay()):(this._state="recovery-error","opaque"!==this._overlayPhase&&this.showOverlayNow()),this.changed(),!1)))}retry(e=this._recoveryReason||"plan"){return this.beginCandidate("retry",e)}nextFrame(){return new Promise(e=>{const t=this.clock.requestAnimationFrame(()=>{this._barrierRafs.delete(t),e()});this._barrierRafs.add(t)})}scheduleOverlay(e){this.clearOverlayTimer(),this._recoveryReason=e,this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._hasCompleteFrame||"overlay-pending"!==this._state&&"candidate-ready"!==this._state||(this._state="overlay-visible",this._overlayPhase="entering",this.record("overlay-enter"),this.changed(),this._overlayRaf=this.clock.requestAnimationFrame(()=>{this._overlayRaf=0,"entering"===this._overlayPhase&&(this._overlayPhase="fading-in",this.changed(),this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,"fading-in"===this._overlayPhase&&(this._overlayPhase="opaque",this._overlayOpaqueAt=this.clock.now(),this.record("overlay-opaque"),this.changed())},150))}))},150)}showOverlayNow(){this.clearOverlayTimer(),this._overlayPhase="opaque",this._overlayOpaqueAt=this.clock.now(),this.record("overlay-error")}finishOverlayAfterCommit(){if(this.clearOverlayTimer(),"none"===this._overlayPhase||"entering"===this._overlayPhase||"fading-in"===this._overlayPhase)return this.clearOverlay(),this._state="steady",this._recoveryReason=null,this.record("candidate-committed"),void this.changed();const e=Math.max(0,this._overlayOpaqueAt+250-this.clock.now());this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._overlayPhase="leaving",this.record("overlay-leave"),this.changed(),this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._overlayPhase="none",this._state="steady",this._recoveryReason=null,this.record("candidate-committed"),this.changed()},180)},e)}clearOverlayTimer(){this._overlayTimer&&this.clock.clearTimeout(this._overlayTimer),this._overlayTimer=0}clearOverlay(){this.clearOverlayTimer(),this._overlayRaf&&this.clock.cancelAnimationFrame(this._overlayRaf),this._overlayRaf=0,this._overlayPhase="none",this._overlayOpaqueAt=0}dispose(){this._disposed=!0,this._token++,this.clearOverlay();for(const e of this._barrierRafs)this.clock.cancelAnimationFrame(e);this._barrierRafs.clear()}}function Rl(e){let t=2166136261,i=2654435769;const n=new WeakSet,r=e=>{for(let n=0;n>>0)+(i>>>2)}},o=e=>{if(null===e)return void r("n");const t=typeof e;if("string"===t)return void r(`s${e.length}:${e}`);if("number"===t)return void r(`d${Number.isNaN(e)?"NaN":String(e)}`);if("boolean"===t)return void r(e?"t":"f");if("undefined"===t)return void r("u");if("object"!==t)return void r(`${t}:${String(e)}`);const i=e;if(n.has(i))r("[cycle]");else{if(n.add(i),Array.isArray(e)){r("[");for(const t of e)o(t);return r("]"),void n.delete(i)}r("{");for(const t of Object.keys(e).sort())r(t),o(e[t]);r("}"),n.delete(i)}};return o(e),`${(t>>>0).toString(16).padStart(8,"0")}${(i>>>0).toString(16).padStart(8,"0")}`}function Tl(e){return Rl(e)}const Cl=new WeakMap;class Dl{constructor(e,t=()=>Date.now()){this.onUpdate=e,this.now=t,this.fallbackAuthority={},this.referenceOwner={},this.referenced=new Set,this.queued=new Set,this.disposed=!1,this.sharedUpdate=()=>{this.disposed||this.onUpdate()}}bind(e){const t=((e,t)=>{const i=e?.connection||e?.callWS||t;return!i||"object"!=typeof i&&"function"!=typeof i?t:i})(e,this.fallbackAuthority);return this.shared&&this.authority===t||(this.shared&&(this.shared.listeners.delete(this.sharedUpdate),this.shared.references.delete(this.referenceOwner)),this.authority=t,this.shared=(e=>{let t=Cl.get(e);return t||(t={signed:{},queued:new Set,inFlight:new Map,retry:new Map,listeners:new Set,settlers:new Set,references:new Map},Cl.set(e,t)),t})(t),this.disposed||(this.shared.listeners.add(this.sharedUpdate),this.shared.references.set(this.referenceOwner,new Set(this.referenced)))),this.shared}start(e,t){this.disposed=!1,this.referenced=new Set(t());const i=this.bind(e());i.listeners.add(this.sharedUpdate),i.references.set(this.referenceOwner,new Set(this.referenced)),this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(e(),t()),288e5)}dispose(){if(this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.shared){this.shared.listeners.delete(this.sharedUpdate),this.shared.references.delete(this.referenceOwner);for(const e of this.queued)this.shared.queued.delete(e)}this.queued.clear()}invalidate(e){const t=this.bind(e);clearTimeout(this.batchTimer),this.queued.clear(),t.queued.clear(),t.inFlight.clear(),t.retry.clear(),t.signed={}}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(e,t){const i=_n(t);if(!i.startsWith("/api/houseplan/content/"))return i;const n=this.bind(e);this.referenced.add(i),n.references.set(this.referenceOwner,new Set(this.referenced));const r=n.signed[i],o=r?this.now()-r.at:1/0;return o{const t=[...this.queued];this.queued.clear();for(const e of t)i.queued.delete(e);this.sign(e,t)},30))}sign(e,t){if(!t.length||!e?.callWS)return;const i=this.bind(e);for(const n of function(e,t){const i=Math.max(1,Math.floor(t)),n=[];for(let t=0;t{const n=i.inFlight.get(e);return!(void 0!==n&&t-n<15e3)&&(i.inFlight.set(e,t),!0)});r.length&&e.callWS({type:"houseplan/content/sign",paths:r}).then(e=>{const t=this.now();let n=0,o=!1;for(const s of r){const r=e?.urls?.[s];if("string"==typeof r&&r){const e=i.signed[s];e?.loaded&&e.url!==r?(e.pending={url:r,at:t},this.preloadReplacement(i,s,r,t)):(i.signed[s]={url:r,at:t,loaded:e?.url===r&&!!e.loaded},o=!0),i.retry.delete(s),n++}else this.backOff(i,s)}if(n&&(this.trimShared(i),o))for(const e of[...i.listeners])e()}).catch(()=>{for(const e of r)this.backOff(i,e)}).finally(()=>{for(const e of r)i.inFlight.get(e)===t&&i.inFlight.delete(e);for(const e of[...i.settlers])e()})}}preloadReplacement(e,t,i,n){const r=()=>{const r=e.signed[t];if(r?.pending?.url===i){e.signed[t]={url:i,at:n,loaded:!0},e.retry.delete(t),this.trimShared(e);for(const t of[...e.listeners])t()}},o=()=>{const n=e.signed[t];n?.pending?.url===i&&(delete n.pending,this.backOff(e,t))};if("undefined"==typeof Image)return void r();const s=new Image;s.onload=()=>{("function"==typeof s.decode?s.decode():Promise.resolve()).then(r).catch(o)},s.onerror=o,s.src=i}preloadCurrentImage(e,t,i){if(i.loaded)return Promise.resolve(!0);if(i.preload)return i.preload;if("undefined"==typeof Image)return i.loaded=!0,Promise.resolve(!0);const n=i.url;return i.preload=new Promise(r=>{const o=new Image;let s=!1;const a=o=>{if(s)return;s=!0,clearTimeout(l);const a=e.signed[t];if(a===i&&delete a.preload,o&&a===i&&a.url===n){a.loaded=!0;for(const t of[...e.listeners])t();r(!0)}else o||a!==i||this.backOff(e,t),r(!1)};o.onload=()=>{("function"==typeof o.decode?o.decode():Promise.resolve()).then(()=>a(!0)).catch(()=>a(!1))},o.onerror=()=>a(!1);const l=setTimeout(()=>a(!1),kl);o.src=n}),i.preload}prepareImage(e,t){const i=_n(t);if(!i.startsWith("/api/houseplan/content/"))return Promise.resolve(!0);const n=this.bind(e),r=()=>{const t=n.signed[i];if(t&&this.now()-t.atthis.now()&&!n.inFlight.has(i)&&!n.queued.has(i)?Promise.resolve(!1):null},o=r();return o?(s=o,new Promise(e=>{let t=!1;const i=i=>{t||(t=!0,clearTimeout(n),e(i))},n=setTimeout(()=>i(!1),kl);s.then(i).catch(()=>i(!1))})):new Promise(e=>{let t=!1,i=null;const o=i=>{t||(t=!0,clearTimeout(a),n.settlers.delete(s),e(i))},s=()=>{t||i||(i=r(),i&&i.then(o).catch(()=>o(!1)))},a=setTimeout(()=>o(!1),kl);n.settlers.add(s),s()});var s}backOff(e,t){const i=e.retry.get(t)?.delay||0,n=Math.min(6e4,i?2*i:2e3);e.retry.set(t,{notBefore:this.now()+n,delay:n})}trimShared(e){const t=Object.entries(e.signed);if(t.length<=512)return;const i=new Set(e.inFlight.keys());for(const t of e.references.values())for(const e of t)i.add(e);for(const[e,n]of t)(n.pending||n.preload)&&i.add(e);t.sort((e,t)=>t[1].at-e[1].at);const n=t.filter(([e])=>i.has(e));for(const e of t){if(n.length>=512)break;i.has(e[0])||n.push(e)}e.signed=Object.fromEntries(n)}resign(e,t){const i=this.bind(e);this.referenced=new Set(t),i.references.set(this.referenceOwner,new Set(this.referenced));const n=this.now(),r=new Set;for(const e of i.references.values())for(const t of e)r.add(t);const o={};for(const[e,t]of Object.entries(i.signed))r.has(e)&&n-t.at!!o[e]&&!o[e].pending))}markLoaded(e,t,i){const n=_n(t);if(!n.startsWith("/api/houseplan/content/"))return;const r=this.bind(e).signed[n];if(r&&(!i||r.url===i)&&!r.loaded){r.loaded=!0;for(const t of[...this.bind(e).listeners])t()}}isReady(e,t){const i=_n(t);if(!i.startsWith("/api/houseplan/content/"))return!0;const n=this.bind(e).signed[i];return!!n&&this.now()-n.ate.id===t)}var Al="M11,18H13V16H11V18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,6A4,4 0 0,0 8,10H10A2,2 0 0,1 12,8A2,2 0 0,1 14,10C14,12 11,11.75 11,15H13C13,12.75 16,12.5 16,10A4,4 0 0,0 12,6Z";function Pl(e,t,i){return[e[0]*t+e[1]*i+e[2],e[3]*t+e[4]*i+e[5]]}function Ol(e,t){let i=0;for(const[n,r]of t){const t=Pl(e,n[0],n[1]);i=Math.max(i,Math.hypot(t[0]-r[0],t[1]-r[1]))}return i}const Fl=e=>{if(null==e||""===e)return null;const t=Number(e);return Number.isFinite(t)?t:null},Il=e=>{const t=e,i=Fl(Array.isArray(t)?t[0]:t?.x),n=Fl(Array.isArray(t)?t[1]:t?.y);return null==i||null==n?null:[i,n]};function El(e){if(!Array.isArray(e))return null;const t=[];for(const i of e){const e=Il(i);if(!e)return null;t.push(e)}if(t.length>1){const e=t[0],i=t[t.length-1];e[0]===i[0]&&e[1]===i[1]&&t.pop()}if(t.length<3)return null;let i=0,n=0,r=0;for(let e=0;ee+t[0],0)/t.length,t.reduce((e,t)=>e+t[1],0)/t.length]:[n/(3*i),r/(3*i)]}function Hl(e){if(!Array.isArray(e))return null;const t=[];for(const i of e){const e=Il(i);if(!e)return null;t.push(e)}if(t.length>1){const e=t[0],i=t[t.length-1];e[0]===i[0]&&e[1]===i[1]&&t.pop()}const i=El(t);if(!i)return null;const n=t.map(e=>e[0]),r=t.map(e=>e[1]);return{centroid:i,bbox:[Math.min(...n),Math.min(...r),Math.max(...n),Math.max(...r)]}}function Nl(e){let t=e;if(t&&"object"==typeof t&&!Array.isArray(t)){const e=t;t=e.path??e.points??t}if(!Array.isArray(t)||!t.length)return[];let i=(t.some(e=>(e=>Array.isArray(e)?e.length>=2&&!Array.isArray(e[0])&&("object"!=typeof e[0]||null==e[0]):!!e&&"object"==typeof e&&("x"in e||"y"in e))(e))?[t]:t.filter(Array.isArray)).map(e=>e.map(Il).filter(e=>null!=e)).filter(e=>e.length>=2);i.length>64&&(i=i.slice(-64));const n=i.reduce((e,t)=>e+t.length,0);if(n<=4e3)return i.map(e=>e.slice());const r=4e3-2*i.length,o=i.map(e=>e.length-2),s=o.reduce((e,t)=>e+t,0),a=o.map(e=>r*e/s),l=a.map(Math.floor);let c=r-l.reduce((e,t)=>e+t,0);const h=a.map((e,t)=>({index:t,fraction:e-Math.floor(e)})).sort((e,t)=>t.fraction-e.fraction||e.index-t.index);for(let e=0;efunction(e,t){if(t>=e.length)return e.slice();if(t<=2)return[e[0],e[e.length-1]];const i=[];for(let n=0;ne.slice()),i=t[t.length-1];return i?(i.length>2?i.pop():t.pop(),t):[]}const ql=e=>Number.isFinite(e[0])&&Number.isFinite(e[1]),Bl=(e,t)=>e[0]===t[0]&&e[1]===t[1];function jl(e){if(!e)return null;const t=e.vacuum_position||e.robot_position||null,i=t&&null!=Fl(t.x)&&null!=Fl(t.y)?{x:Fl(t.x),y:Fl(t.y),a:Fl(t.a??t.angle??t.theta)}:null,n=Nl(e.path?.path??e.path?.points??e.path),r=[],o=e.rooms,s=Array.isArray(o)?o.map((e,t)=>[String(e?.id??t),e]):o&&"object"==typeof o?Object.entries(o):[];for(const[e,t]of s){if(!t||"object"!=typeof t)continue;const i=String(t.name??t.label??"").trim(),n=[[Fl(t.cx),Fl(t.cy)],[Fl(t.center?.x),Fl(t.center?.y)],[Fl(t.x),Fl(t.y)]].find(([e,t])=>null!=e&&null!=t);let o=n?.[0]??null,s=n?.[1]??null;const a=Hl(t.outline),l=Fl(t.x0),c=Fl(t.y0),h=Fl(t.x1),d=Fl(t.y1),u=(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)||a?.bbox||null;if(null!=o&&null!=s||!a||([o,s]=a.centroid),null!=o&&null!=s||!u||(o=(u[0]+u[2])/2,s=(u[1]+u[3])/2),i&&null!=o&&null!=s){const t={id:e,name:i,cx:o,cy:s};u&&([t.x0,t.y0,t.x1,t.y1]=u),r.push(t)}}const a=function(e){return String(e.map_name??e.current_map??e.map_index??e.selected_map??"default")}(e);return i||r.length||n.length?{pos:i,path:n,rooms:r,mapId:a}:null}function Ul(e,t,i){return![e,t,i].every(Number.isFinite)||e<0||t<=0||i<=0?Number.POSITIVE_INFINITY:e/t*i}const Vl=40;function Gl(e,t,i){if(!e||!t)return null;const n=t.attributes||{},r=e.split(".")[0]||"",o=null!=i?.platform?String(i.platform):null,s=!!Il(n.vacuum_position??n.robot_position),a=!(!n.rooms||"object"!=typeof n.rooms),l=Nl(n.path?.path??n.path?.points??n.path).length>0,c=[n.map_name,n.current_map,n.map_index,n.selected_map].some(e=>null!=e),h="xiaomi_cloud_map_extractor"===o,d=a||l||c;let u=null;if(s?u="compatible":h?u="known_xcme_incomplete":d?u="partial":"camera"===r&&(u="camera"),!u)return null;const p=s?"camera"===r?300:200:h?100:d?50:0;return{entityId:e,name:String(n.friendly_name||e),platform:o,category:u,hasPosition:s,hasRooms:a,hasPath:l,hasMapId:c,score:p}}const Kl=e=>e.toLowerCase().replace(/[\s_\-.,]+/g,"");function Yl(e,t){const i=new Set(t.map(Kl).filter(Boolean)),n=new Set;for(const t of e){const e=Kl(t.name||"");e&&i.has(e)&&n.add(e)}return n.size}function Xl(e,t){const i=new Map(t.map(e=>[Kl(e.name),e])),n=[],r=[];for(const t of e){const e=i.get(Kl(t.name));e&&(n.push([[t.cx,t.cy],[e.cx,e.cy]]),r.push(t.name))}if(n.length<3)return null;const o=function(e){if(e.length<3)return null;let t=0,i=0,n=0,r=0,o=0,s=0,a=0,l=0,c=0,h=0,d=0,u=0;for(const[[p,m],[_,f]]of e){if(![p,m,_,f].every(Number.isFinite))return null;t+=p*p,i+=p*m,n+=p,r+=m*m,o+=m,s+=1,a+=p*_,l+=m*_,c+=_,h+=p*f,d+=m*f,u+=f}const p=[t,i,n,i,r,o,n,o,s],m=e=>{const[t,i,n,r,o,s,a,l,c]=p,h=t*(o*c-s*l)-i*(r*c-s*a)+n*(r*l-o*a);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(o*c-s*l)/h,(n*l-i*c)/h,(i*s-n*o)/h,(s*a-r*c)/h,(t*c-n*a)/h,(n*r-t*s)/h,(r*l-o*a)/h,(i*a-t*l)/h,(t*o-i*r)/h];return[d[0]*e[0]+d[1]*e[1]+d[2]*e[2],d[3]*e[0]+d[4]*e[1]+d[5]*e[2],d[6]*e[0]+d[7]*e[1]+d[8]*e[2]]},_=m([a,l,c]),f=m([h,d,u]);if(!_||!f)return null;const g=[_[0],_[1],_[2],f[0],f[1],f[2]];return g.every(Number.isFinite)?g:null}(n);return o?{matrix:o,matched:r,residual:Ol(o,n)}:null}function Zl(e,t,i){const n=e[e.length-1];if(n&&n[0]===t[0]&&n[1]===t[1])return e;if(e.push(t),e.length<=600)return e;let r=function(e,t){if(e.length<3)return e.slice();const i=new Uint8Array(e.length);i[0]=i[e.length-1]=1;const n=[[0,e.length-1]];for(;n.length;){const[r,o]=n.pop(),[s,a]=e[r],[l,c]=e[o],h=l-s,d=c-a,u=Math.hypot(h,d)||1e-9;let p=0,m=-1;for(let t=r+1;tp&&(p=i,m=t)}m>0&&p>t&&(i[m]=1,n.push([r,m],[m,o]))}const r=[];for(let t=0;t600&&(r=r.filter((e,t)=>t%2==0||t===r.length-1)),r}function Jl(e){return"cleaning"===e||"returning"===e||"on"===e}const Ql={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function ec(e){const[t,i]=Ql[e.rot]||[1,0],n=e.mir?-1:1;return[e.s*t*n,-e.s*i,e.ox,e.s*i*n,e.s*t,e.oy]}function tc(e){const t=e[0]*e[4]-e[1]*e[3];if(!Number.isFinite(t)||Math.abs(t)<1e-12)return null;const i=t<0,n=Math.sqrt(Math.abs(t));let r=180*Math.atan2(-e[1],e[4])/Math.PI;return r=(90*Math.round(r/90)%360+360)%360,{ox:e[2],oy:e[5],s:n,rot:r,mir:i}}function ic(e,t){const i=[],n=[];for(const t of e)null!=t.x0?(i.push(t.x0,t.x1),n.push(t.y0,t.y1)):(i.push(t.cx),n.push(t.cy));if(!i.length)return{ox:t[0]+t[2]/2,oy:t[1]+t[3]/2,s:t[2]/1e4,rot:0,mir:!0};const r=Math.min(...i),o=Math.max(...i),s=Math.min(...n),a=Math.max(...n),l=Math.max(o-r,a-s)||1,c={ox:0,oy:0,s:.6*Math.min(t[2],t[3])/l,rot:0,mir:!0},h=ec(c),[d,u]=Pl(h,(r+o)/2,(s+a)/2);return c.ox=t[0]+t[2]/2-d,c.oy=t[1]+t[3]/2-u,c}function nc(e,t,i,n){const[r,o]=Pl(ec(t),i,n),s=ec({...e,ox:0,oy:0}),[a,l]=Pl(s,i,n);return{...e,ox:r-a,oy:o-l}}function rc(e){const t=e?.trail_mode;return"never"===t||"cleaning"===t||"always"===t?t:!1===e?.trail?"never":"cleaning"}const oc={availability:"available",status:"alarm",activity:"none"},sc=new Set(["motion","vibration","sound"]),ac=new Set(["occupancy","presence"]),lc=new Set(["door","window","garage_door","opening"]),cc=new Set(["running","power"]),hc=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const dc=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),uc=new Set([...dc,"start","started","run","active","in_progress","wash","rinse","spin","dry"]),pc=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping","stop","end","done","inactive"]),mc=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),_c=e=>""===e||"unknown"===e||"unavailable"===e||"__missing__"===e,fc=e=>String(e??"").trim().toLowerCase(),gc=e=>fc(e).replace(/[\s-]+/g,"_").replace(/_+/g,"_").replace(/^_|_$/g,""),vc=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]]),yc=new Set(["wifi","connection","signal","battery"]);function bc(e,t){const i=e?.entities?.[t]||{};if("config"===i.entity_category)return null;const n=String(t||"").split(".").slice(1).join("."),r=[i.translation_key,i.original_name,i.name],o=[...r,n];r.some(e=>fc(e))||o.push(e?.states?.[t]?.attributes?.friendly_name);const s=o.map(gc).filter(Boolean);if(s.some(e=>e.split("_").some(e=>yc.has(e))))return null;let a=null;for(const e of s)for(const[t,i]of vc)(e===t||e.endsWith(`_${t}`))&&(a=null==a?i:Math.min(a,i));return a}const wc=(e,t)=>null!=bc(e,t);function kc(e,t){if(!t.startsWith("switch."))return!1;const i=e?.entities?.[t]||{},n=e?.states?.[t],r=t.slice(7).toLowerCase();if(/(?:^|_)(?:main_)?power$/.test(r))return!0;if([i.translation_key,i.original_name,i.name].map(fc).some(e=>["power","main power","power switch","питание"].includes(e)))return!0;const o=fc(n?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(o)||/(?:^|[\s._-])питание$/.test(o)}function xc(e){for(const t of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=fc(e?.[t]);if(dc.has(i)||pc.has(i))return i}return""}function $c(e,t){const i=e?.states?.[t],n=i?fc(i.state):"__missing__",r=String(t||"").split(".")[0],o=fc(i?.attributes?.device_class),s={eid:t,state:n,availability:_c(n)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===s.availability)return s;if(function(e,t,i){return"alarm_control_panel"===e?"triggered"===i:"on"===i&&Sn(e,t)}(r,o,n))return{...s,status:"alarm"};if("binary_sensor"===r)return sc.has(o)?{...s,edge:"rising"}:ac.has(o)?{...s,activity:"on"===n?"presence":"none"}:lc.has(o)?{...s,status:"on"===n?"open":"neutral",edge:"rising"}:"moving"===o?{...s,activity:"on"===n?"transition":"none"}:cc.has(o)&&"on"===n?{...s,status:"working",activity:"running"}:s;if("cover"===r)return{...s,activity:"opening"===n||"closing"===n?"transition":"none",edge:"terminal_transition"};if("lock"===r)return{...s,status:"unlocked"===n||"open"===n?"open":"neutral",activity:"locking"===n||"unlocking"===n?"transition":"none",edge:"terminal_transition"};if("valve"===r)return{...s,status:["open","opening","closing"].includes(n)?"open":"neutral",activity:"opening"===n||"closing"===n?"transition":"none",edge:"terminal_transition"};if("climate"===r){const e=xc(i.attributes),t=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(fc):[],r=!pc.has(n)&&(mc.has(n)||dc.has(n)||t.includes(n));return(e?dc.has(e):r)?{...s,status:"working",activity:"running"}:s}if(["light","switch","fan","humidifier"].includes(r))return"on"===n?{...s,status:"working",activity:"running"}:s;if("media_player"===r)return"off"===n?{...s,availability:"unavailable"}:s;if("vacuum"===r)return"cleaning"===n?{...s,status:"working",activity:"running"}:"returning"===n?{...s,status:"working",activity:"transition"}:s;if("script"===r)return"on"===n?{...s,status:"working",activity:"running"}:s;if("automation"===r)return s;if("button"===r||"event"===r)return{...s,edge:"change"};const a=xc(i.attributes);return dc.has(a)||dc.has(n)&&!pc.has(n)?{...s,status:"working",activity:"running"}:s}function Sc(e,t,i){const n=i.filter(t=>t.startsWith("switch.")&&!e?.entities?.[t]?.entity_category),r=t.find(t=>kc(e,t));if(!(n.length>1&&!!r))return t.map(t=>$c(e,t));const o=t.find(t=>t!==r&&wc(e,t)),s=$c(e,r);return"unavailable"===s.availability||"off"===s.state?t.map(t=>({eid:t,state:e?.states?.[t]?fc(e.states[t].state):"__missing__",availability:"unavailable",status:"neutral",activity:"none",edge:"none"})):t.map(t=>{if(t===o)return function(e,t){const i=e?.states?.[t],n=i?fc(i.state):"__missing__",r={eid:t,state:n,availability:_c(n)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===r.availability)return r;const o=xc(i?.attributes);return dc.has(o)||uc.has(n)&&!pc.has(n)?{...r,status:"working",activity:"running"}:r}(e,t);return{...t===r?s:$c(e,t),status:"neutral",activity:"none",edge:"none"}})}function Mc(e){if(!e.length)return{availability:"available",status:"neutral",activity:"none"};const t=e.filter(e=>"available"===e.availability);if(!t.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(t.some(e=>"alarm"===e.status))return oc;const i=t.some(e=>"working"===e.status)?"working":t.some(e=>"open"===e.status)?"open":"neutral",n=t.some(e=>"transition"===e.activity)?"transition":t.some(e=>"presence"===e.activity)?"presence":t.some(e=>"running"===e.activity)?"running":"none";return{availability:"available",status:i,activity:n}}function Rc(e,t){if(!(e=>!!e&&!_c(e))(e)||"unavailable"===t.availability||e===t.state)return null;if("rising"===t.edge)return"off"===e&&"on"===t.state?"event":null;if("change"===t.edge)return"event";if("terminal_transition"===t.edge){const i=new Set([e,t.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}const Tc=(e,t=0)=>{const i=Number(e);return Number.isInteger(i)&&i>=0?i:t},Cc=(e,t)=>{const i={rev:0,configRev:Tc(t),off:new Set};if(null==e)return{snapshot:i,valid:!1};const n=e.off;return!Number.isInteger(e.rev)||Number(e.rev)<0||!Number.isInteger(e.config_rev)||Number(e.config_rev)<0||!Array.isArray(n)||n.some(e=>"string"!=typeof e||!e)?{snapshot:i,valid:!1}:{snapshot:{rev:Number(e.rev),configRev:Number(e.config_rev),off:new Set(n)},valid:!0}};function Dc(e,t=0){return Cc(e,t).snapshot}function zc(e,t,i,n){const r=Cc(t,i);return n&&r.valid&&r.snapshot.configRev===i?e.configRev===i&&r.snapshot.rev{try{const[i,n]=await Promise.all([e.callWS({type:"config/device_registry/list"}),e.callWS({type:"config/entity_registry/list"})]),r=Bc(i,"id"),o=Bc(n,"entity_id");if(!r||!o)throw new Error("invalid_registry_response");t.devices=r,t.entities=o,t.authoritative=!0,t.access="full",t.lastSuccess=Date.now(),t.error=void 0}catch(e){t.authoritative=!1,t.access="limited",t.error=function(e){if(e&&"object"==typeof e){const t=e;return String(t.message||t.code||t.error||"registry_unavailable")}return String(e||"registry_unavailable")}(e)}finally{t.revision++,t.loading=void 0,function(e){for(const t of[...e.listeners])try{t()}catch{}}(t)}})()),t.loading}function Uc(e,t){void 0===t.reloadTimer&&(t.reloadTimer=globalThis.setTimeout(()=>{t.reloadTimer=void 0,jc(e,t)},80))}async function Vc(e,t){if(t.subscribing||t.unsubDevice&&t.unsubEntity)return t.subscribing;const i=e?.connection?.subscribeEvents;return"function"==typeof i?(t.subscribing=(async()=>{try{t.unsubDevice||(t.unsubDevice=await i.call(e.connection,()=>Uc(e,t),"device_registry_updated")),t.unsubEntity||(t.unsubEntity=await i.call(e.connection,()=>Uc(e,t),"entity_registry_updated"))}catch{}finally{0===t.refs&&(t.unsubDevice?.(),t.unsubEntity?.(),t.unsubDevice=void 0,t.unsubEntity=void 0),t.subscribing=void 0}})(),t.subscribing):void 0}function Gc(e,t){const i=qc(e);if(!i)return()=>{};const n=0===i.refs;i.refs++,i.listeners.add(t),("pending"===i.access||n)&&jc(e,i),Vc(e,i);let r=!1;return()=>{r||(r=!0,i.listeners.delete(t),i.refs=Math.max(0,i.refs-1),i.refs>0||(i.unsubDevice?.(),i.unsubEntity?.(),i.unsubDevice=void 0,i.unsubEntity=void 0,void 0!==i.reloadTimer&&globalThis.clearTimeout(i.reloadTimer),i.reloadTimer=void 0))}}function Kc(e){const t=qc(e);if(!t)return{revision:0,authoritative:!1,access:"limited",devices:e?.devices||{},entities:e?.entities||{},lastSuccess:0,error:"registry_unavailable"};let i=e?.devices||{},n=e?.entities||{};if(t.authoritative){const r=t.liveDevices!==i||t.liveEntities!==n,o=void 0!==t.liveDevices||void 0!==t.liveEntities;if(r&&(t.liveDevices=i,t.liveEntities=n,t.projectedRevision=void 0,o&&Uc(e,t)),t.projectedRevision!==t.revision||!t.projectedDevices||!t.projectedEntities){const e={...t.devices},r={...t.entities};for(const[t,n]of Object.entries(i))Object.prototype.hasOwnProperty.call(e,t)||(e[t]=n);for(const[e,t]of Object.entries(n))Object.prototype.hasOwnProperty.call(r,e)||(r[e]=t);t.projectedDevices=e,t.projectedEntities=r,t.projectedRevision=t.revision}i=t.projectedDevices,n=t.projectedEntities}return{revision:t.revision,authoritative:t.authoritative,access:t.access,devices:i,entities:n,lastSuccess:t.lastSuccess,error:t.error}}function Yc(e){if(!e||"object"!=typeof e&&"function"!=typeof e)return 0;let t=Hc.get(e);return t||(t=Nc++,Hc.set(e,t)),t}function Xc(e,t=Kc(e)){return[t.revision,t.access,Yc(t.devices),Yc(t.entities)].join(":")}function Zc(e){return!!e&&null==e.disabled_by}function Jc(e,t=Kc(e)){const i={},n={},r={};for(const[e,n]of Object.entries(t.devices||{}))Zc(n)&&(i[e]=n);for(const[e,i]of Object.entries(t.entities||{})){if(!Zc(i))continue;const r=i.device_id?t.devices?.[i.device_id]:null;t.authoritative&&i.device_id&&!r||(r&&!Zc(r)||(n[e]=i))}for(const[i,n]of Object.entries(e?.states||{})){const e=t.entities?.[i];if(e&&!Zc(e))continue;const o=e?.device_id?t.devices?.[e.device_id]:null;t.authoritative&&e?.device_id&&!o||(o&&!Zc(o)||(r[i]=n))}return{...e,devices:i,entities:n,states:r}}function Qc(e,t=Kc(e)){return{...e,devices:t.devices||{},entities:t.entities||{}}}function eh(e,t){const i=[];for(const[n,r]of Object.entries(e||{}))r?.device_id===t&&i.push(n);return i}function th(){if(Wc)return Wc;if("undefined"==typeof localStorage)return{};try{const e=JSON.parse(localStorage.getItem(Lc)||"{}");if(!e||"object"!=typeof e||Array.isArray(e))return{};const t=Date.now(),i={};for(const[n,r]of Object.entries(e))!r||"active"!==r.kind&&"ha_disabled"!==r.kind||!Number.isFinite(r.ts)||t-r.ts>7776e6||(i[n]=r);return Wc=i,Wc}catch{return{}}}function ih(e,t,i=Kc(e)){if(!t||"virtual"===t)return{kind:"active",enabledEntityIds:[],allEntityIds:[]};const n=t.indexOf(":");if(n<1)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const r=t.slice(0,n),o=t.slice(n+1);if("device"!==r&&"entity"!==r||!o)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const s=i.devices||{},a=i.entities||{};if(i.authoritative){if("device"===r){const e=s[o];if(!e)return{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[]};const t=eh(a,o);if(!Zc(e))return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:t};const i=t.filter(e=>Zc(a[e]));return t.length&&!i.length?{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:t}:{kind:"active",enabledEntityIds:i,allEntityIds:t}}const t=a[o];return t&&!Zc(t)?{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]}:t?.device_id&&!s[t.device_id]?{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[o]}:t?.device_id&&!Zc(s[t.device_id])?{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]}:t||e?.states?.[o]?{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}:{kind:"orphaned",reason:"entity_missing",enabledEntityIds:[],allEntityIds:[]}}const l=function(e){return th()[e]||null}(t);if("device"===r){const t=s[o],i=eh(a,o);if(null!=t?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:i};const n=i.filter(e=>{const t=a[e];return null==t?.disabled_by&&(!t.device_id||null==s[t.device_id]?.disabled_by)});if(i.length&&!n.length&&i.every(e=>null!=a[e]?.disabled_by))return{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:i};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"device",enabledEntityIds:[],allEntityIds:i};if(t||n.some(t=>!!e?.states?.[t]))return{kind:"active",enabledEntityIds:n,allEntityIds:i}}else{const t=a[o];if(null!=t?.disabled_by)return{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]};if(t?.device_id&&null!=s[t.device_id]?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"entity",enabledEntityIds:[],allEntityIds:[o]};if(t||e?.states?.[o])return{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}}return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]}}function nh(e){const t={};for(const[i,n]of Object.entries(e.entities)){if(!n?.device_id||!Zc(n))continue;const r=e.devices?.[n.device_id];r&&!Zc(r)||(t[n.device_id]=t[n.device_id]||[]).push(i)}return t}function rh(e,t){const i=new Map;for(const n of e){if(!0===n?.removed)continue;const e=n?.binding||"";if(!e.startsWith("entity:"))continue;const r=e.slice(7);if(!r)continue;const o=t?.entities?.[r]?.device_id;if(!o)continue;const s=i.get(o)||new Set;s.add(r),i.set(o,s)}return{byDevice:i}}function oh(e,t,i,n){const r=n.byDevice.get(t);return r?.size?{partial:!0,entityIds:i.filter(t=>!r.has(t)&&!e?.entities?.[t]?.hidden)}:{partial:!1,entityIds:[...i]}}function sh(e,t,i){if(t.identifiers?.[0]?.[0])return t.identifiers[0][0];for(const t of i){const i=e.entities[t]?.platform;if(i)return i}return""}function ah(e,t){if(/_device_temperature$/.test(t))return!1;if(e.entities?.[t]?.entity_category)return!1;const i=e.states[t];if(!i)return/_temperature$/.test(t);const n=i.attributes||{};return"temperature"===n.device_class||/°C|°F/.test(n.unit_of_measurement||"")||/_temperature$/.test(t)}const lh=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],ch=e=>[...e.filter(e=>!e.reg?.hidden),...e.filter(e=>!!e.reg?.hidden)];function hh(e,t){const i=t.map(t=>({eid:t,reg:e?.entities?.[t]})).filter(e=>!!e.reg);if(!i.length)return[];const n=i.filter(e=>!e.reg.entity_category),r=n.length?n:i;for(const e of lh){const t=r.filter(t=>t.eid.startsWith(e+"."));if(t.length)return ch(t).map(e=>e.eid)}const o=r.filter(t=>function(e,t){if(!t.startsWith("binary_sensor."))return!1;const i=fc(e?.states?.[t]?.attributes?.device_class||e?.entities?.[t]?.device_class||e?.entities?.[t]?.original_device_class);return sc.has(i)||ac.has(i)||lc.has(i)||cc.has(i)||"moving"===i||hc.has(i)}(e,t.eid));if(o.length)return ch(o).map(e=>e.eid);const s=r.filter(e=>e.eid.startsWith("switch."));if(s.length){const t=ch(s),n=t.find(t=>kc(e,t.eid));if(s.length>1&&n){const t=i.filter(t=>t.eid!==n.eid&&"config"!==t.reg?.entity_category&&null!=bc(e,t.eid)),r=t.filter(e=>!e.reg?.entity_category),o=r.length?r:t;for(const t of[0,1,2]){const i=ch(o.filter(i=>bc(e,i.eid)===t));if(i.length)return[i[0].eid,n.eid]}}return[(n||t[0]).eid]}const a=[];for(const e of xe)a.push(...ch(r.filter(t=>t.eid.startsWith(e+"."))));return a.push(...ch(r.filter(e=>!xe.includes(e.eid.split(".")[0])))),a.map(e=>e.eid)}function dh(e,t,i){const n=t.map(t=>({eid:t,reg:e.entities[t],st:e.states[t]})).filter(e=>e.reg),r=[n.filter(e=>!e.reg.hidden&&!e.reg.entity_category),n.filter(e=>!e.reg.entity_category),n.filter(e=>!e.reg.hidden),n];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const t of r){const i=t.find(t=>ah(e,t.eid));if(i)return i.eid}const o=hh(e,t);return o.find(ln)||o[0]}function uh(e,t,i=[]){return[...new Set(ph(e,t,i))].filter(e=>ln(e))}function ph(e,t,i=[]){const n=e?.startsWith("entity:")?new Set([e.slice(7)]):new Set(i);return(t||[]).filter(e=>"string"==typeof e&&!n.has(e))}function mh(e,t){if("string"==typeof t)return e.area===t;const i=e.marker?.room_id;return i?!!t.id&&i===t.id:!!t.area&&e.area===t.area}function _h(e){const t=e.marker?.binding?.startsWith("entity:")?e.marker.binding.slice(7):null,i=e.entities.filter(ln),n=e.primary&&ln(e.primary)?e.primary:null;return[...new Set([t,n,...i].filter(e=>!!e&&ln(e)))]}function fh(e){const t=_h(e),i=e.marker?.light_entity;return i&&t.includes(i)?i:t[0]||null}function gh(e,t){if(!1===t.marker?.is_light)return[];if(!0===t.marker?.is_light){return[{eid:fh(t),via:"forced"}]}const i=t.primary||hh(e,t.entities)[0];return i&&!i.startsWith("light.")?[]:t.entities.filter(t=>t.startsWith("light.")&&!!e.states?.[t]).map(e=>({eid:e,via:"light"}))}const vh=new WeakMap,yh=new WeakMap;function bh(e){return e.map(e=>[e.id||"",!0===e.hidden?1:0,e.primary||"",[...e.entities].join(","),void 0===e.controls?"":[...e.controls].join(","),e.marker?.id||"",e.marker?.binding||"",e.marker?.is_light,e.marker?.tap_action||"",!0===e.marker?.removed?1:0,e.marker?.light_entity||"",null==e.marker?.controls?"":[...e.marker.controls].join(",")].join("")).join("")}function wh(e){const t=bh(e),i=vh.get(e);if(i?.fingerprint===t)return i;const n=e.filter(e=>!e.hidden),r=new Map,o=new Map;for(const t of e){o.set(t,ph(t.marker?.binding,t.marker?.controls??t.controls,t.entities));const e=t.hidden||!0!==t.marker?.is_light?null:t.marker?.id||t.id;e&&r.set(String(e),t)}const s=new Map;for(const t of e){const e=o.get(t)||[],i=new Set(void 0===t.controls?e.filter(ln):t.controls.filter(ln)),n=fh(t),a=i.size?[...i]:n?[n]:[];for(const i of e){if(!i.startsWith("marker:"))continue;const e=i.slice(7);if(!e||e===String(t.marker?.id||t.id||""))continue;if(!r.has(e))continue;const n=s.get(e)||{markerId:e,controllers:[],driverEids:[]};n.controllers.push({device:t,driverEids:[...a]}),n.driverEids=[...new Set([...n.driverEids,...a])],s.set(e,n)}}const a={fingerprint:t,visible:n,markerById:r,persistedByDevice:o,incomingByMarker:s};return vh.set(e,a),a}function kh(e){return wh(e).incomingByMarker}function xh(e,t){const i=uh(t.marker?.binding,t.controls??t.marker?.controls,t.entities),n=ph(t.marker?.binding,t.marker?.controls??t.controls,t.entities);return(null!=t.marker?.is_light||!i.length&&!n.some(e=>e.startsWith("marker:")))&&gh(e,t).length>0}function $h(e,t){return gh(e,t).some(e=>!!e.eid)}function Sh(e,t,i,n){const r="always"===e||"auto"===e&&t,o=r&&!i;return{sourceExists:r,fromSourceEnabled:r&&i,manualEnabled:r,radiusEnabled:r,passive:o,effectiveMode:o&&"auto"===n?"fixed":n}}function Mh(e,t){const i=uh(t.marker?.binding,t.controls??t.marker?.controls,t.entities).map(e=>({eid:e,via:"controls"})),n=xh(e,t)?gh(e,t):[];for(const e of n)e.eid&&i.some(t=>t.eid===e.eid)||i.push(e);return i}function Rh(e,t,i,n){if(null!=i)return Rh(e,t,null,n).filter(e=>mh(e.device,i));const r=bh(t),o=function(e,t){const i=new Set;for(const e of t){for(const t of e.entities)i.add(t);for(const t of e.controls||e.marker?.controls||[])"string"!=typeof t||t.startsWith("marker:")||i.add(t)}return[...i].sort().map(t=>`${t}:${e?.states?.[t]?.state??""}`).join("|")}(e,t),s=Pc(n),a=yh.get(t);if(a?.graphFingerprint===r&&a.stateFingerprint===o&&a.virtualLightFingerprint===s&&a.registry===e?.entities)return a.sources;const{visible:l,markerById:c,persistedByDevice:h,incomingByMarker:d}=wh(t),u=null==i?l:l.filter(e=>mh(e,i)),p=new Map,m=new Map;for(const t of u){const i=Mh(e,t).filter(e=>"controls"!==e.via);for(const n of i){const i=!0===t.marker?.is_light?String(t.marker?.id||t.id||""):"",r=i?`marker:${i}`:`entity:${n.eid}`,o=n.eid?[n.eid]:[],s={key:r,eid:n.eid||"",stateEids:o,serviceEids:o,device:t,via:n.via,castsGlow:!0,passive:!n.eid,on:!n.eid||"on"===e.states?.[n.eid]?.state};p.set(t,[...p.get(t)||[],s]),n.eid&&m.set(n.eid,s)}}const _=new Map;for(const e of t){const t=h.get(e)||[],i=new Set(void 0===e.controls?t.filter(ln):e.controls);_.set(e,i)}for(const t of p.values())for(const i of t){if(!i.passive)continue;const t=i.key.slice(7),r=d.get(t);i.on=!r||r.driverEids.some(t=>"on"===e.states?.[t]?.state),!r&&Oc(i.device.marker)&&(i.on=Fc(i.device.marker,n))}const f=new Map,g=e=>{const t=f.get(e.key);(!t||e.castsGlow&&!t.castsGlow)&&f.set(e.key,e)};for(const t of l){const n=h.get(t)||[],r=null==i||mh(t,i);for(const i of n){if(i.startsWith("marker:")){const e=c.get(i.slice(7)),t=e?p.get(e):null;if(t)for(const e of t)g(e);continue}if(!ln(i)||!_.get(t)?.has(i))continue;const n=m.get(i);n?g(n):r&&g({key:`entity:${i}`,eid:i,stateEids:[i],serviceEids:[i],device:t,via:"controls",castsGlow:!1,passive:!1,on:"on"===e.states?.[i]?.state})}for(const e of p.get(t)||[])g(e)}const v=[...f.values()];return yh.set(t,{graphFingerprint:r,stateFingerprint:o,virtualLightFingerprint:s,registry:e?.entities,sources:v}),v}function Th(e){const t=e.filter(e=>e.castsGlow);return t.find(e=>e.on)||t[0]||null}function Ch(e){return e.length?e.some(e=>e.on)?"on":"off":"none"}function Dh(e,t){const i=[];for(const n of t){const t=e.states[n];if(!t)continue;const r=(t.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(n)||"lqi"===r){const e=parseFloat(t.state);isNaN(e)||i.push(e);continue}const o=t.attributes?.linkquality??t.attributes?.lqi;if(null!=o){const e=parseFloat(o);isNaN(e)||i.push(e)}}return _i(i)}function zh(e,t){for(const i of t){if(!ah(e,i))continue;const t=e.states[i];if(!t)continue;const n=parseFloat(t.state);if(!isNaN(n))return Math.round(10*n)/10}return null}function Ah(e,t){for(const i of t){if(!i.startsWith("climate."))continue;const t=e.states[i];if(!t||"unavailable"===t.state||"unknown"===t.state)continue;const n=parseFloat(t.attributes?.current_temperature);if(Number.isFinite(n))return Math.round(10*n)/10}return null}function Ph(e,t){if(e.entities?.[t]?.entity_category)return!1;const i=e.states[t];if(!i)return/_humidity$/.test(t);const n=i.attributes||{};return"humidity"===n.device_class||"%"===n.unit_of_measurement&&/_humidity$/.test(t)||/_humidity$/.test(t)}function Oh(e,t){for(const i of t){if(!Ph(e,i))continue;const t=e.states[i];if(!t)continue;const n=parseFloat(t.state);if(!isNaN(n))return Math.round(n)}return null}function Fh(e,t){if(!t)return[];const i=[];for(const[t,n]of Object.entries(e.entities)){if(!t.startsWith("light.")||n.hidden||!Zc(n))continue;let r=null;if("group"===n.platform)r=n.area_id||null;else{if(!n.device_id)continue;{const t=e.devices[n.device_id];if(!Zc(t))continue;if("Group"!==t?.model)continue;r=t.area_id||n.area_id||null}}if(!r)continue;const o=e.states[t];i.push({eid:t,name:n.name||o?.attributes?.friendly_name||t,area:r})}return i}function Ih(e,t,i,n,r){const o=ke(t,i,r);if(o!==we)return o;const s=[];for(const t of n){const i=e.states[t]?.attributes?.device_class;i&&s.push(i)}return function(e){for(const t of e){const e=be[t];if(e)return e}return null}(s)??we}function Eh(e,t){return e.map(e=>{if(!Array.isArray(e.controls))return e;const i=e.controls.filter(e=>!("string"==typeof e&&e.startsWith("marker:")&&t.has(e.slice(7))));return i.length===e.controls.length?e:{...e,controls:i.length?i:null}})}function Hh(e,t,i){if(!t||!i||t===i)return[...e];const n=`marker:${t}`,r=`marker:${i}`;return e.map(e=>{const t=Array.isArray(e.controls)?e.controls.map(e=>e===n?r:e):e.controls,i="derived_marker_state"===e.value_badge?.source?.kind&&e.value_badge.source.ref===n?{...e.value_badge,source:{...e.value_badge.source,ref:r}}:e.value_badge;return t===e.controls&&i===e.value_badge?e:{...e,controls:t,value_badge:i}})}function Nh(e,t,i){if(!t||t===i)return!0;const n=new Map;for(const t of e)n.set(t.id,(t.controls||[]).filter(e=>"string"==typeof e&&e.startsWith("marker:")).map(e=>e.slice(7)));const r=new Set,o=[i];for(;o.length;){const e=o.pop();if(e===t)return!0;r.has(e)||(r.add(e),o.push(...n.get(e)||[]))}return!1}function Lh(e,t,i,n){const r=new Set([t]),o=e.filter(e=>{const o=e.id===t||!n&&e.binding===i;return o&&r.add(e.id),!o});return{markers:n?o:[...o,{id:t,binding:i,removed:!0,hidden:!0}],cleanupIds:r}}function Wh(e){const t=new Set,i=new Set,n=new Set;for(const r of e||[]){const e=String(r.binding||"").indexOf(":");if(e<1)continue;const o=r.binding.slice(0,e),s=r.binding.slice(e+1);s&&(!0===r?.removed?"device"===o?t.add(s):"entity"===o&&i.add(s):"entity"===o&&n.add(s))}return{devices:t,entities:i,liveEntities:n}}function qh(e,t,i){if(i.liveEntities.has(t))return!1;if(i.entities.has(t))return!0;const n=e?.entities?.[t]?.device_id;return!!n&&i.devices.has(n)}function Bh(e,t,i,n,r){const o=uh(t.binding,t.controls,e.entities).filter(e=>!qh(i,e,n)).filter(e=>"active"===ih(i,"entity:"+e,r).kind);e.marker=t,e.controls=o,e.userHidden=!0===t.hidden,e.hidden=e.userHidden||"ha_disabled"===e.bindingStatus?.kind,t.name&&(e.name=t.name),t.icon&&(e.icon=t.icon),null!=t.model&&(e.model=t.model),e.link=t.link??null,e.description=t.description??null,e.pdfs=t.pdfs||[],e.tapAction=t.tap_action??null}function jh(e,t,i,n){if("string"==typeof e.room_id&&e.room_id.length>0&&null===e.area)return{area:"",space:e.space||n};const r=e.area||t||"";return{area:r,space:r&&i[r]||e.space||n}}function Uh(e){const t=e.hass,i=e.registry||Kc(t),n=Jc(t,i),r=Qc(t,i),{areaToSpace:o,markers:s,settings:a,excluded:l,showAll:c,firstSpaceId:h,loc:d,iconRules:u}=e,p=!1!==a.group_lights,m=Wh(s),_=Fh(n,p).filter(e=>!qh(n,e.eid,m)),f=new Set(_.map(e=>e.area)),g=nh(n),v=function(e){const t={};for(const[i,n]of Object.entries(e.entities||{}))n?.device_id&&(t[n.device_id]=t[n.device_id]||[]).push(i);return t}(r),y=rh(s,r),b=new Set;for(const e of s){const[t,i]=e.binding.split(":");"device"!==t&&"entity"!==t||!i||b.add(e.binding)}const w=(e,t)=>s.find(i=>i.binding===e+":"+t),k={},x=[];for(const e of Object.values(n.devices)){const r=e.area_id;if(!r||!o[r])continue;if("service"===e.entry_type)continue;if(b.has("device:"+e.id))continue;const s=ih(t,"device:"+e.id,i);if("active"!==s.kind)continue;const h=w("device",e.id);if(h&&h.hidden&&!a.filter_seeded)continue;const m=oh(n,e.id,g[e.id]||[],y);if(m.partial&&!m.entityIds.length)continue;const _=m.entityIds,v=m.partial?{kind:"active",enabledEntityIds:_,allEntityIds:_}:s,$=sh(n,e,_),S=!a.filter_seeded;if(S&&!c){if(l.has($))continue;if("Group"===e.model)continue;if(/scene/i.test(e.model||""))continue;if(/bridge/i.test((e.model||"")+(e.name||"")))continue;if("myheat"===$&&e.via_device_id)continue}const M=(e.name_by_user||e.name||d("device.unnamed")).trim(),R=M+"|"+r;let T=Ih(n,M,e.model,_,u);if(_.some(e=>e.startsWith("lock."))&&(T="mdi:lock"),S&&!c&&p&&"mdi:lightbulb"===T&&f.has(r))continue;k[R]=(k[R]||0)+1;const C=k[R]>1?M+" "+k[R]:M,D={id:e.id,name:C,model:e.model||"",area:r,space:o[r],icon:T,entities:_,allEntities:v.allEntityIds,bindingStatus:v,bindingKind:"device",bindingRef:e.id,pdfs:[]};D.primary=dh(n,_,T),"mdi:thermometer"!==T&&"mdi:air-filter"!==T||(D.temp=zh(n,_)),D.primary&&Ph(n,D.primary)&&(D.hum=Oh(n,_)),x.push(D)}for(const e of _)o[e.area]&&(b.has("entity:"+e.eid)||x.push({id:"lg_"+e.eid,name:e.name,model:d("device.light_group"),area:e.area,space:o[e.area],icon:"mdi:lightbulb-group",entities:[e.eid],allEntities:[e.eid],bindingStatus:{kind:"active",enabledEntityIds:[e.eid],allEntityIds:[e.eid]},primary:e.eid,bindingKind:"entity",bindingRef:e.eid,pdfs:[]}));for(const e of s){if(e.removed)continue;const[s,l]=e.binding.split(":"),c="device"===s||"entity"===s?ih(t,e.binding,i):null;if(!e.hidden||a.filter_seeded||"ha_disabled"===c?.kind)if("device"===s){const t=c;if("unverified"===t.kind)continue;const s=r.devices[l],{area:a,space:p}=jh(e,s?.area_id,o,h),_="active"===t.kind?t.enabledEntityIds:[];let f=s?Ih(n,s.name_by_user||s.name||"",s.model,_,u):"mdi:help-circle";_.some(e=>e.startsWith("lock."))&&(f="mdi:lock");const g={id:e.id,name:s?.name_by_user||s?.name||d("device.fallback"),model:s?.model||"",area:a,space:p,icon:f,entities:_,allEntities:t.allEntityIds.length?t.allEntityIds:s&&v[s.id]||[],bindingStatus:t,bindingKind:"device",bindingRef:l};g.primary=dh(n,_,f),"mdi:thermometer"!==f&&"mdi:air-filter"!==f||(g.temp=zh(n,_)),g.primary&&Ph(n,g.primary)&&(g.hum=Oh(n,_)),Bh(g,e,r,m,i),x.push(g)}else if("entity"===s){if(qh(r,l,m))continue;const t=c;if("unverified"===t.kind)continue;const s=r.entities[l],a=s?.area_id||s?.device_id&&r.devices[s.device_id]?.area_id||"",{area:d,space:p}=jh(e,a,o,h),_=n.states[l],f=s?.name||_?.attributes?.friendly_name||l;let g=Ih(n,f,"",[l],u);l.startsWith("lock.")&&(g="mdi:lock");const v={id:e.id,name:f,model:"",area:d,space:p,icon:g,entities:"active"===t.kind?[l]:[],allEntities:[l],bindingStatus:t,primary:"active"===t.kind?l:void 0,bindingKind:"entity",bindingRef:l};"mdi:thermometer"!==g&&"mdi:air-filter"!==g||!v.entities.length||(v.temp=zh(n,v.entities)),v.entities.length&&Ph(n,l)&&(v.hum=Oh(n,v.entities)),Bh(v,e,r,m,i),x.push(v)}else{const t=e.area||"",n=e.space||t&&o[t]||h,s={id:e.id,name:e.name||d("device.virtual"),model:e.model||"",area:t,space:n,icon:e.icon||"mdi:map-marker",entities:[],allEntities:[],bindingStatus:{kind:"active",enabledEntityIds:[],allEntityIds:[]},bindingKind:"virtual",virtual:!0};Bh(s,e,r,m,i),x.push(s)}}return x}function Vh(e){const{marker:t,siblingMarkers:i=[],...n}=e;return Uh({...n,markers:[...i.filter(e=>e.id!==t.id),t]}).find(e=>e.id===t.id)||null}function Gh(e,t,i,n){if(!t)return null;if(function(e,t,i){if(!t)return!1;const n=t.indexOf(":");if(n<1)return!1;const r=t.slice(0,n),o=t.slice(n+1),s=Wh(i);return"device"===r?s.devices.has(o):"entity"===r&&qh(e,o,s)}(e,t,n))return null;const r=t.indexOf(":");if(r<0)return null;const o=t.slice(0,r),s=t.slice(r+1);if(!s)return null;if("entity"===o){const t=parseFloat(e.states[s]?.state);return Number.isFinite(t)?"temp"===i?Math.round(10*t)/10:Math.round(t):null}if("device"===o){const t=Object.entries(e.entities).filter(([,e])=>e.device_id===s).map(([e])=>e);return"temp"===i?zh(e,t):Oh(e,t)}return null}const Kh=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 Yh(e,t){return t.area?t.area:e&&t.id?`@room/${encodeURIComponent(e)}/${encodeURIComponent(t.id)}`:null}function Xh(e){return null==e.area&&e.space&&e.room_id?Yh(e.space,{id:e.room_id,area:null}):e.area||null}function Zh(e,t,i){const n=new Map;if(!e?.entities)return n;const r=Wh(i),o=new Set,s=new Map,a=new Map;for(const e of i||[]){if(e?.removed)continue;const t=(e.binding||"").indexOf(":");if(t<=0)continue;const i=e.binding.slice(0,t),n=e.binding.slice(t+1);if(!n)continue;!0===e.use_climate_temp&&o.add(n);const r=Xh(e);r&&("entity"!==i||s.has(n)?"device"!==i||a.has(n)||a.set(n,r):s.set(n,r))}const l=new Map;for(const[t,i]of Object.entries(e.entities)){if(i.device_id&&r.devices.has(i.device_id)&&!r.liveEntities.has(t)||!i.device_id&&r.entities.has(t)&&!r.liveEntities.has(t))continue;const n=i.device_id?e.devices?.[i.device_id]:null;if(!Zc(i)||n&&!Zc(n))continue;const c=s.get(t)||(i.device_id?a.get(i.device_id):null)||i.area_id||n?.area_id||null;if(!c)continue;if(i.entity_category)continue;if(!(o.size>0&&t.startsWith("climate.")&&(o.has(t)||i.device_id&&o.has(i.device_id)))){if(_e.has(i.platform))continue;if(Kh.test(t))continue}let h=l.get(c);h||(h=new Map,l.set(c,h));const d=i.device_id||t;let u=h.get(d);if(!u){const r=e.states?.[t];u={name:(n?n.name_by_user||n.name:i.name||r?.attributes?.friendly_name||t)||t,model:n?.model,ents:[]},h.set(d,u)}u.ents.push(t)}for(const[i,r]of l){const s=[],a=[];for(const[i,n]of r){const r=Ih(e,n.name,n.model,n.ents,t),l="mdi:thermometer"===r||"mdi:air-filter"===r;if(l){const t=zh(e,n.ents);null!=t&&s.push(t)}if(o.size>0&&(o.has(i)||n.ents.some(e=>o.has(e)))){const t=Ah(e,n.ents);null!=t&&s.push(t)}if(l||"mdi:water-percent"===r){const t=Oh(e,n.ents);null!=t&&a.push(t)}}(s.length||a.length)&&n.set(i,{temp:s.length?Math.round(s.reduce((e,t)=>e+t,0)/s.length*10)/10:null,hum:a.length?Math.round(a.reduce((e,t)=>e+t,0)/a.length):null})}return n}function Jh(e,t,i){const n=[];for(const r of t){if(r.area!==i||r.virtual)continue;const t=Dh(e,r.entities);null!=t&&n.push(t)}return _i(n)}const Qh={offStates:["off"],unknownUsesToggle:!0},ed={light:Qh,switch:Qh,fan:Qh,humidifier:Qh,input_boolean:Qh,automation:Qh,remote:Qh,group:Qh,climate:{...Qh,featureMasks:{turn_on:256,turn_off:128,toggle:384}},media_player:{...Qh,featureMasks:{turn_on:128,turn_off:256,toggle:384}},siren:{...Qh,featureMasks:{turn_on:1,turn_off:2,toggle:3}},vacuum:{...Qh,featureMasks:{turn_on:1,turn_off:2,toggle:3}},water_heater:{...Qh,featureMasks:{turn_on:8,turn_off:8,toggle:8}},camera:{...Qh,featureMasks:{turn_on:1,turn_off:1,toggle:1}}};function td(e){return e.slice(0,e.indexOf("."))}function id(e,t,i){if(!e?.services||"object"!=typeof e.services)return!1;const n=e.services?.[t];return!!n&&Object.prototype.hasOwnProperty.call(n,i)}function nd(e,t,i){return id(e,t,i)?{domain:t,service:i}:id(e,"homeassistant",i)?{domain:"homeassistant",service:i}:null}function rd(e,t){return e?.entities?.[t]||null}function od(e,t,i){const n=e?.states?.[i],r=rd(t,i);return n?.attributes?.friendly_name||r?.name||r?.original_name||i}function sd(e,t){const i=rd(e,t);if(null!=i?.disabled_by)return!0;const n=i?.device_id?e?.devices?.[i.device_id]:null;return null!=n?.disabled_by}function ad(e,t){const i=e?.attributes?.supported_features;if(null==i||""===i)return!1;const n=Number(i);return Number.isFinite(n)&&(n&t)===t}function ld(e,t,i){const n=td(i);if("lock"===n||"alarm_control_panel"===n)return!0;if("cover"!==n)return!1;const r=e?.states?.[i],o=rd(t,i),s=String(r?.attributes?.device_class||o?.device_class||o?.original_device_class||"");return $i.has(s)}function cd(e,t,i,n,r){return{ref:i,entityId:n,name:n?od(e,t,n):null,reason:r}}function hd(e,t,i,n,r,o=null){return{target:null,skipped:cd(e,t,i,n,r),semantics:o,nextEffect:null,command:null}}function dd(e,t,i,n,r=i){const o=td(i);if("cover"!==o&&"valve"!==o)return function(e,t,i,n,r=i){const o=td(i),s=e?.states?.[i];if(sd(t,i))return hd(e,t,r,i,"ha-disabled","power");if(ld(e,t,i))return hd(e,t,r,i,"secure");if(!s)return hd(e,t,r,i,"missing","power");if("unavailable"===s.state)return hd(e,t,r,i,"unavailable","power");const a=ed[o];if(!a)return hd(e,t,r,i,"unsupported");let l,c;if("unknown"===s.state||""===s.state){if(!a.unknownUsesToggle)return hd(e,t,r,i,"unsupported","power");l="toggle",c="toggle"}else a.offStates.includes(String(s.state))?(l="turn-on",c="turn_on"):(l="turn-off",c="turn_off");const h=a.featureMasks?.[c];if(h&&!ad(s,h))return hd(e,t,r,i,"unsupported","power");const d=nd(e,o,c);return d?{target:{entityId:i,name:od(e,t,i),state:String(s.state||""),via:n},skipped:null,semantics:"power",nextEffect:l,command:{...d,data:{entity_id:i}}}:hd(e,t,r,i,"unsupported","power")}(e,t,i,n,r);if(sd(t,i))return hd(e,t,r,i,"ha-disabled",o);if(ld(e,t,i))return hd(e,t,r,i,"secure",o);const s=e?.states?.[i];if(!s)return hd(e,t,r,i,"missing",o);if("unavailable"===s.state)return hd(e,t,r,i,"unavailable",o);const a=function(e,t,i){const n=String(i?.state||""),r="closed"===n?{service:`open_${t}`,effect:"open",feature:1}:"open"===n?{service:`close_${t}`,effect:"close",feature:2}:"opening"===n||"closing"===n?{service:`stop_${t}`,effect:"stop",feature:8}:null;return r&&ad(i,r.feature)&&id(e,t,r.service)?r:ad(i,3)&&id(e,t,"toggle")?{service:"toggle",effect:"toggle"}:null}(e,o,s);if(!a)return hd(e,t,r,i,"unsupported",o);return{target:{entityId:i,name:od(e,t,i),state:String(s.state||""),via:n},skipped:null,semantics:o,nextEffect:a.effect,command:{domain:o,service:a.service,data:{entity_id:i}}}}function ud(e){return"entity"===e.bindingKind?e.bindingRef&&ln(e.bindingRef)?[e.bindingRef]:[]:"virtual"===e.bindingKind?[]:_h(e)}function pd(e){const t=e.marker?.toggle_entity;return t&&ud(e).includes(t)?t:null}function md(e,t,i){const n=function(e,t){if("entity"===e.bindingKind&&e.bindingRef){const t=e.marker?.light_entity?fh(e):null;return[...new Set([t,e.bindingRef].filter(e=>!!e))]}const i=e.entities.length?e.entities:e.allEntities||[],n=e.marker?.light_entity||e.primary&&ln(e.primary)?fh(e):null;return[...new Set([n,...hh(t,i)].filter(e=>!!e))]}(i,t);if(!n.length)return null;const r="entity"===i.bindingKind?"binding":"device-role";if("binding"===r)return dd(e,t,n[0],r);let o=null,s=null;for(const i of n){const n=dd(e,t,i,r);if(n.command)return n;const a=n.skipped?.reason;if("missing"===a||"unavailable"===a||"secure"===a)return n;"ha-disabled"!==a?o||=n:s||=n}return o||s}function _d(e){const t=e.skipped?.reason;return"missing"===t?"unavailable":t||"unsupported"}function fd(e,t){return{origin:e,kind:"none",semantics:null,targets:[],skippedTargets:[],noneReason:t,nextEffect:null,command:null}}function gd(e,t){return"cover"===e||"toggle"===e?"toggle":"more-info"===e||"run"===e||"info"===e?e:(null==e||""===e)&&"light"===t?"toggle":"info"}function vd(e,t){return{origin:e,kind:"single",semantics:t.semantics,targets:t.target?[t.target]:[],skippedTargets:t.skipped?[t.skipped]:[],noneReason:t.command?null:_d(t),nextEffect:t.nextEffect,command:t.command}}function yd(e,t,i,n=[]){const r=new Map,o=[],s=new Set,a=e=>{const t=`${e.ref}\n${e.entityId||""}\n${e.reason}`;s.has(t)||(s.add(t),o.push(e))};for(const e of n)a(e);for(const n of i){const i=dd(e,t,n.entityId,n.via,n.ref);i.target?r.set(n.entityId,r.get(n.entityId)||i.target):i.skipped&&a(i.skipped)}const l=[...r.values()];if(!l.length)return{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:l,skippedTargets:o,noneReason:o.length&&o.every(e=>"secure"===e.reason)?"secure":"configured-targets-missing",nextEffect:null,command:null};const c=l.some(e=>"on"===e.state)?"turn_off":"turn_on",h=nd(e,"homeassistant",c);return h?{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:l,skippedTargets:o,noneReason:null,nextEffect:"turn_on"===c?"turn-on":"turn-off",command:{...h,data:{entity_id:l.map(e=>e.entityId)}}}:{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:[],skippedTargets:[...o,...l.map(i=>cd(e,t,i.entityId,i.entityId,"unsupported"))],noneReason:"unsupported",nextEffect:null,command:null}}function bd(e){return String(e.marker?.id||e.id||"")}function wd(e){const{hass:t,device:i}=e,n=e.registryHass||t,r=function(e){return"toggle"===e.tapAction?"explicit-toggle":"cover"===e.tapAction?"legacy-cover":!e.tapAction&&e.primary?.startsWith("light.")?"default-light":null}(i);if(!r)return null;const o=i.marker;if("explicit-toggle"===r&&Oc(o)){const t=String(o?.id||i.id||""),n=kh(e.devices).get(t);if(n)return function(e,t){const i=e.registryHass||e.hass;return yd(e.hass,i,t.driverEids.map(e=>({entityId:e,via:"control-marker-driver",ref:`marker:${t.markerId}`})))}(e,n);const s=Fc(o,e.virtualLights);return{origin:r,kind:"single",semantics:"power",targets:[{entityId:"",name:i.name,state:s?"on":"off",via:"virtual-light"}],skippedTargets:[],noneReason:null,nextEffect:s?"turn-off":"turn-on",command:null,operation:{kind:"virtual-light",markerId:o.id}}}if("explicit-toggle"===r){if(ph(i.marker?.binding,i.marker?.controls??i.controls,i.entities).length)return function(e,t=null){const{hass:i,device:n,devices:r}=e,o=e.registryHass||i,s=ph(n.marker?.binding,n.marker?.controls??n.controls,n.entities),a=e.lightSources||Rh(i,r,null,e.virtualLights),l=new Map;for(const e of a){if(!e.key.startsWith("marker:"))continue;const t=l.get(e.key)||[];t.push(e),l.set(e.key,t)}const c=kh(r),h=new Map;for(const e of r){const t=String(e.marker?.id||e.id||"");t&&h.set(t,e)}const d=[],u=[];t&&d.push({entityId:t,via:"entity"===n.bindingKind?"binding":"device-role",ref:t});for(const e of s){if(!e.startsWith("marker:")){ln(e)?d.push({entityId:e,via:"control-entity",ref:e}):u.push(cd(i,o,e,e.includes(".")?e:null,"unsupported"));continue}const t=l.get(e)||[];if(!t.length){const t=h.get(e.slice(7));if("ha_disabled"===t?.bindingStatus?.kind){const n=t.bindingStatus.allEntityIds[0]||null;u.push({ref:e,entityId:n,name:t.name||(n?od(i,o,n):null),reason:"ha-disabled"})}else u.push(cd(i,o,e,null,"missing"));continue}const r=[...new Set(t.flatMap(e=>e.serviceEids))];if(r.length){for(const t of r)d.push({entityId:t,via:"control-entity",ref:e});continue}const s=t.some(e=>e.passive),a=e.slice(7),p=bd(n),m=c.get(a)?.controllers.find(e=>bd(e.device)===p);if(s&&m?.driverEids.length)for(const t of m.driverEids)d.push({entityId:t,via:"control-marker-driver",ref:e});else u.push(cd(i,o,e,null,s?"missing":"unsupported"))}return yd(i,o,d,u)}(e,pd(i))}if("legacy-cover"===r){const e=i.entities.find(e=>e.startsWith("cover."))||i.allEntities?.find(e=>e.startsWith("cover."));return e?vd(r,dd(t,n,e,"device-role")):fd(r,"no-actionable-entity")}const s=pd(i),a=s?dd(t,n,s,"entity"===i.bindingKind?"binding":"device-role"):md(t,n,i);return a?vd(r,a):fd(r,i.virtual||"virtual"===i.bindingKind?"no-actionable-entity":"no-binding")}function kd(e){if(!e)return[];const t=Array.isArray(e.data.entity_id)?e.data.entity_id:[e.data.entity_id];return[...new Set(t)].sort()}function xd(e){return e?e.operation?e.operation:e.command?{kind:"ha-service",command:e.command}:null:null}const $d=new Set(["missing","ha-disabled","unavailable"]);function Sd(e,t){const i=xd(e),n=xd(t);return!(!i||!n||i.kind!==n.kind)&&("virtual-light"===i.kind&&"virtual-light"===n.kind?i.markerId===n.markerId:"ha-service"===i.kind&&"ha-service"===n.kind&&function(e,t){const i=kd(e),n=kd(t);return i.length===n.length&&i.every((e,t)=>e===n[t])}(i.command,n.command))}function Md(e,t){const i=e.targets[0]||e.skippedTargets[0],n=[];return"group"===e.kind?e.targets.length&&n.push(t.group(e.targets)):i&&n.push(t.single(i)),e.nextEffect&&e.targets.length&&n.push("group"===e.kind?t.groupCurrentNext(e.targets,e.nextEffect):t.currentNext(e.targets[0],e.nextEffect)),e.skippedTargets.length&&n.push(t.skipped(e.skippedTargets)),!e.command&&e.noneReason&&n.push(t.none(e.noneReason)),n}const Rd=e=>!!e&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]);function Td(e,t=5,i=5){return ao(e.cm,t,i)/2}function Cd(e,t,i=1,n=5,r=5,o=0){const s=e.host;if(!s||"partition"!==s.kind||"string"!=typeof s.id||!s.id)return{resolved:null,reason:"invalid-host"};const a=t.find(e=>e.id===s.id);if(!a)return{resolved:null,reason:"missing-partition"};if(!Rd(a.a)||!Rd(a.b)||!Number.isFinite(s.t)||s.t<0||s.t>1)return{resolved:null,reason:"invalid-position"};const l=a.b[0]-a.a[0],c=a.b[1]-a.a[1],h=Math.hypot(l,c),d=Number(e.length)*i;if(!(h>1e-9&&d>0&&Number.isFinite(d)))return{resolved:null,reason:"invalid-length"};const u=s.t*h;if(u-d/2h-o+1e-9)return{resolved:null,reason:"does-not-fit"};const p=l/h,m=c/h;let _=180*Math.atan2(c,l)/Math.PI;return _>=90?_-=180:_<-90&&(_+=180),{reason:null,resolved:{opening:e,host:s,partition:a,center:[a.a[0]+l*s.t,a.a[1]+c*s.t],angle:_,length:d,depth:ao(a.cm,n,r),t:s.t,axis:{a:[a.a[0],a.a[1]],b:[a.b[0],a.b[1]],ux:p,uy:m,length:h}}}}function Dd(e,t,i=1,n=5,r=5){return Cd(e,t,i,n,r,0)}function zd(e,t,i=1,n=5,r=5){const o=e.host,s="partition"===o?.kind?t.find(e=>e.id===o.id):void 0;if(!s)return Dd(e,t,i,n,r);const a=Cd(e,t,i,n,r,Td(s,n,r));return"does-not-fit"===a.reason?{resolved:null,reason:"does-not-fit-jamb"}:a}function Ad(e,t){return!e||(e.length!==t.length||e.host?.kind!==t.host?.kind||e.host?.id!==t.host?.id||e.host?.t!==t.host?.t)}function Pd(e){const{center:t,length:i,axis:n,host:r}=e,o=i/2;return{hostId:r.id,a:[t[0]-n.ux*o,t[1]-n.uy*o],b:[t[0]+n.ux*o,t[1]+n.uy*o],depth:e.depth}}function Od(e,t=!1){const i=t?1:-1,n=e.depth/2;return{ox:-e.axis.uy*i*n,oy:e.axis.ux*i*n,cm:e.partition.cm,side:i}}function Fd(e,t,i){return e.flatMap(e=>!Rd(e.a)||!Rd(e.b)||Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])<=1e-9?[]:[{roomId:"",a:[e.a[0],e.a[1]],b:[e.b[0],e.b[1]],key:`partition:${e.id}`,kind:"outer",cm:e.cm,open:!1,half:ao(e.cm,t,i)/2,partitionHost:{kind:"partition",id:e.id}}])}function Id(e,t,i){const{center:n,length:r,axis:o}=e,s=r/2,a=t.flatMap(e=>{if(!e.kind||e.open)return[];const t=e.b[0]-e.a[0],r=e.b[1]-e.a[1],a=Math.hypot(t,r);if(!(a>i))return[];const l=t/a,c=r/a;if(Math.abs(o.ux*c-o.uy*l)>1e-6)return[];if(Math.abs((n[0]-e.a[0])*c-(n[1]-e.a[1])*l)>i)return[];const h=e=>(e[0]-n[0])*o.ux+(e[1]-n[1])*o.uy,d=h(e.a),u=h(e.b),p=Math.max(-s,Math.min(d,u)),m=Math.min(s,Math.max(d,u));return m>=p-i?[[p,m]]:[]});if(!a.length)return!1;a.sort((e,t)=>e[0]-t[0]||e[1]-t[1]);let l=-s;for(const[e,t]of a){if(e>l+i)return!1;if(l=Math.max(l,t),l>=s-i)return!0}return!1}function Ed(e,t,i=1e-9){const n=e.length/(2*e.axis.length),r=e.t-n,o=e.t+n;return t.some(t=>{if(t.host.id!==e.host.id||t.opening.id===e.opening.id)return!1;const n=t.length/(2*t.axis.length);return Math.max(r,t.t-n)`}const tr={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},ir=(e,t=1)=>{const i=Number(e);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):t},nr=e=>{let t=Number(e);return Number.isFinite(t)?(t=(t%360+360)%360,t>180?t-360:t):0};function rr(e,t,i){return nr(t)?[e.x,e.y]:i([e.x,e.y])}function or(e,t,i,n){const r=Math.abs(i[0]-t[0]),o=Math.abs(i[1]-t[1]);return"line"===e?Math.hypot(r,o)>=n:r>=n&&o>=n}const sr=(e,t,i)=>{const n=Number(e),r=Number.isFinite(t)&&t>0?t:5;return Number.isFinite(n)&&n>0?n/r*i:0},ar=(e,t,i)=>{const n=Number(e),r=Number.isFinite(t)&&t>0?t:5;return Number.isFinite(n)&&i>0?n/i*r:0},lr=(e,t,i,n=tr.widthCm)=>{const r=Number(e?.width_cm);if(Number.isFinite(r)&&r>0)return r;const o=Number(e?.width);return Number.isFinite(o)&&o>0?ar(o,t,i):n};function cr(e){const t=tr;return e.color===t.color&&e.opacity===t.opacity&&e.widthCm===t.widthCm&&e.fill===t.fill&&e.fillColor===t.fillColor&&e.fillOpacity===t.fillOpacity?null:{color:e.color,opacity:ir(e.opacity),width_cm:Math.max(.1,Math.min(100,Number(e.widthCm)||.1)),fill:!!e.fill,fill_color:e.fillColor,fill_opacity:ir(e.fillOpacity,.25)}}function hr(e,t){return{color:e.color,opacity:ir(e.opacity),width_cm:Math.max(.1,Math.min(100,Number(e.widthCm)||.1)),...t?{fill:e.fill,fill_color:e.fillColor,fill_opacity:ir(e.fillOpacity,.25)}:{}}}function dr(e){const t=e.x+e.w/2,i=e.y+e.h/2,n=nr(e.angle)*Math.PI/180,r=Math.cos(n),o=Math.sin(n),s=(e,n)=>{const s=e-t,a=n-i;return[t+s*r-a*o,i+s*o+a*r]};return[s(e.x,e.y),s(e.x+e.w,e.y),s(e.x+e.w,e.y+e.h),s(e.x,e.y+e.h)]}function ur(e){const t=dr(e),i=(e,t)=>[(e[0]+t[0])/2,(e[1]+t[1])/2],n=[(t[0][0]+t[2][0])/2,(t[0][1]+t[2][1])/2];return{points:[...t,i(t[0],t[1]),i(t[1],t[2]),i(t[2],t[3]),i(t[3],t[0]),n],segments:t.map((e,i)=>({a:e,b:t[(i+1)%4]}))}}function pr(e,t,i,n,r,o,s,a){const l=nr(e.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l),d=-Math.sin(l),u=Math.cos(l),p=e.x+e.w/2,m=e.y+e.h/2,_=t>0?-e.w/2:e.w/2,f=i>0?-e.h/2:e.h/2,g=p+_*c+f*d,v=m+_*h+f*u,y=n-g,b=r-v;let w=(y*c+b*h)*(t>0?1:-1),k=(y*d+b*u)*(i>0?1:-1),x=null;if(o){const t=w/Math.max(e.w,a),i=k/Math.max(e.h,a);x=Math.max(a/Math.max(e.w,a),a/Math.max(e.h,a),t,i),w=e.w*x,k=e.h*x}if(s>0)if(o){const t=Math.max(1,Math.round(e.w/s)),i=Math.max(1,Math.round(e.h/s));if(Math.abs(e.w-t*s)<1e-6&&Math.abs(e.h-i*s)<1e-6){const n=(e,t)=>{let i=Math.abs(e),n=Math.abs(t);for(;n;)[i,n]=[n,i%n];return Math.max(1,i)},r=1/n(t,i),o=Math.max(a/e.w,a/e.h),s=Math.max(o,Math.round((x??1)/r)*r);w=e.w*s,k=e.h*s}else{e.w>=e.h?(w=Math.round(w/s)*s,k=w*(e.h/Math.max(e.w,a))):(k=Math.round(k/s)*s,w=k*(e.w/Math.max(e.h,a)))}}else w=Math.round(w/s)*s,k=Math.round(k/s)*s;w=Math.max(a,w),k=Math.max(a,k);return{x:g+(t>0?w/2:-w/2)*c+(i>0?k/2:-k/2)*d-w/2,y:v+(t>0?w/2:-w/2)*h+(i>0?k/2:-k/2)*u-k/2,w:w,h:k,angle:nr(e.angle)||void 0}}const mr=(e,t,i)=>{const n=i[0]-t[0],r=i[1]-t[1],o=n*n+r*r;if(o<1e-12)return[...t];const s=Math.max(0,Math.min(1,((e[0]-t[0])*n+(e[1]-t[1])*r)/o));return[t[0]+n*s,t[1]+r*s]};function _r(e,t,i,n){const r=n(e);let o=null;for(const r of t.points){const t=n(r),s=Math.hypot(t[0]-e[0],t[1]-e[1]);s<=i&&(!o||se.points),segments:e.flatMap(e=>e.segments)}}const gr=Math.tan(.25*Math.PI/180);function vr(e,t){const i=Math.abs(Number(t[0])-Number(e[0])),n=Math.abs(Number(t[1])-Number(e[1]));if(!([i,n].every(Number.isFinite)&&i>0&&n>0))return null;const r=i>=n?"horizontal":"vertical",o=Math.max(i,n),s=Math.min(i,n);return s/o>gr?null:{axis:r,major:o,minor:s,angleDegrees:180*Math.atan2(s,o)/Math.PI}}function yr(e,t){const i=vr(e,t);return i?"horizontal"===i.axis?[Number(t[0]),Number(e[1])]:[Number(e[0]),Number(t[1])]:[Number(t[0]),Number(t[1])]}const br=e=>`${e[0]},${e[1]}`,wr=(e,t)=>e[0]===t[0]&&e[1]===t[1],kr=(e,t)=>{const i=br(e),n=br(t);return i{let t=0;for(let i=0;i{if(e.length<3)return!1;for(let t=0;t1e-12},Sr=e=>{for(let t=0;t+1wr(e,t.from)?[...t.to]:[...e],Rr=(e,t)=>e.map(e=>Array.isArray(e?.poly)?{...e,poly:e.poly.map(e=>Mr(e,t))}:e),Tr=(e,t,i)=>{const n=e.flatMap((e,t)=>Et(e)?.some(e=>wr(e,i.from))?[t]:[]);if(!n.length)return!1;for(const i of n){const n=Et(e[i]),r=Et(t[i]);if(!n||!r)continue;if(!$r(r))return!1;const o=Math.sign(xr(n)),s=Math.sign(xr(r));if(o&&s&&o!==s)return!1}const r=new Set;for(const e of n){const i=Et(t[e]);if(i)for(let n=0;n{let i=0;for(const n of e){const e=Et(n);if(e)for(let n=0;n{const{a:i,b:n,axis:r}=e,o="horizontal"===r?{from:n,to:[n[0],i[1]]}:{from:n,to:[i[0],n[1]]},s="horizontal"===r?{from:i,to:[i[0],n[1]]}:{from:i,to:[n[0],i[1]]};return[{move:o,preservedDegree:Cr(t,i)},{move:s,preservedDegree:Cr(t,n)}].sort((e,t)=>t.preservedDegree-e.preservedDegree||br(e.move.to).localeCompare(br(t.move.to))||br(e.move.from).localeCompare(br(t.move.from))).map(e=>e.move)};function zr(e){const t=JSON.parse(JSON.stringify(e||{}));let i=Array.isArray(t.rooms)?t.rooms:[];const n=new Map;for(const e of i){const t=Et(e);if(t)for(let e=0;ee.key.localeCompare(t.key))){let t=null;for(const n of Dr(e,i)){const e=a.get(br(n.from));if(e&&e!==br(n.to))continue;const r=Rr(i,n);if(Tr(i,r,n)){t=n,i=r;break}}t?(a.set(br(t.from),br(t.to)),r++,s=Math.max(s,Math.hypot(t.to[0]-t.from[0],t.to[1]-t.from[1]))):o++}t.rooms=i;for(const e of t.room_drafts||[]){if(!Array.isArray(e?.points)||e.points.length<2)continue;const t=e.points.map(e=>[...e]),i=t.slice(0,-1).flatMap((i,n)=>{const r=t[n+1],o=vr(i,r);return o?[{key:`${String(e.id||"")}:${n}`,a:[...i],b:[...r],axis:o.axis}]:[]});let n=t;for(const e of i){let t=null;for(const i of Dr(e,[{poly:n}])){const e=n.map(e=>Mr(e,i)),r=e.length>=4&&wr(e[0],e[e.length-1]),o=r?e.slice(0,-1):e;if(r?$r(o):Sr(o)){t=i,n=e;break}}t?(r++,s=Math.max(s,Math.hypot(t.to[0]-t.from[0],t.to[1]-t.from[1]))):o++}e.points=n}for(const e of t.partitions||[]){if(!Array.isArray(e?.a)||!Array.isArray(e?.b))continue;const i=vr(e.a,e.b);if(!i)continue;const n={key:String(e.id||kr(e.a,e.b)),a:[...e.a],b:[...e.b],axis:i.axis};let a=null;for(const i of Dr(n,[{poly:[e.a,e.b]}])){const n=Mr(e.a,i),r=Mr(e.b,i),o=Math.hypot(r[0]-n[0],r[1]-n[1]);if(!(o>0))continue;const s=(t.openings||[]).filter(t=>"partition"===t?.host?.kind&&t.host.id===e.id).every(e=>{const t=Number(e.host.t),i=Number(e.length);return Number.isFinite(t)&&t>=0&&t<=1&&Number.isFinite(i)&&i>0&&t*o-i/2>=-1e-12&&t*o+i/2<=o+1e-12});if(s){a=i,e.a=n,e.b=r;break}}a?(r++,s=Math.max(s,Math.hypot(a.to[0]-a.from[0],a.to[1]-a.from[1]))):o++}return{space:t,report:{wallsStraightened:r,wallsStraightenSkipped:o,maxStraightenShift:s},changed:r>0}}const Ar=10**9,Pr=240,Or=1e-4;function Fr(e){if(Array.isArray(e))return e.map(e=>Fr(e));if(null!==e&&"object"==typeof e){const t={};for(const[i,n]of Object.entries(e))t[i]=Fr(n);return t}return e}function Ir(e){if("number"!=typeof e||!Number.isFinite(e))return e;const t=(e<0||Object.is(e,-0)?-1:1)*(Math.floor(Math.abs(e)*Ar+.5)/Ar);return 0===t?0:t}function Er(e){if("number"!=typeof e||!Number.isFinite(e))return e;const t=e*Pr,i=Math.round(t);if(Math.abs(t-i)null!==Nr(e)):[]}function Wr(e,t){for(const i of t)Object.prototype.hasOwnProperty.call(e,i)&&(e[i]=Ir(e[i]))}function qr(e,t){for(const i of t)Object.prototype.hasOwnProperty.call(e,i)&&(e[i]=Er(e[i]))}function Br(e){if(Array.isArray(e))for(let t=0;t{for(const r of n){const n=t[r];"number"==typeof n&&Number.isFinite(n)&&i(n,e)}},n=t=>{if(Array.isArray(t))for(let n=0;n{if(Array.isArray(e))for(const t of e)n(t)};for(const i of Lr(e.rooms))t(i,["x","y","w","h"]),r(i.poly);for(const t of Lr(e.walls))n(t.a),n(t.b);for(const t of Lr(e.wall_segments))n(t.a),n(t.b);for(const i of Lr(e.openings))t(i,["x","y"]);for(const i of Lr(e.decor))"line"===i.kind?t(i,["x1","y1","x2","y2"]):"rect"===i.kind||"ellipse"===i.kind||"furniture"===i.kind?t(i,["x","y","w","h"]):"text"===i.kind&&t(i,["x","y"]);for(const t of Lr(e.room_drafts))r(t.points);for(const t of Lr(e.partitions))n(t.a),n(t.b);for(const t of Lr(e.wall_columns))n(t.center);for(const t of Lr(e.open_spans))n(t.a),n(t.b)}const r=Nr(t);if(!r)return;const o=new Map;if(n)for(const e of Lr(n.spaces))null!=e.id&&o.set(String(e.id),e);for(const e of Object.values(r)){const t=Nr(e);if(!t)continue;const n=null!=t.s&&o.get(String(t.s))||null;for(const e of["x","y"]){const r=t[e];"number"==typeof r&&Number.isFinite(r)&&i(r,n)}}}function Vr(e){const t=Number(e?.cell_cm);return t>0?t:5}function Gr(e,t={}){const i={canonicalized:0,far:0,maxShift:0,maxShiftCm:0,spaces:[],bySpace:new Map},n=Lr(Nr(e)?.spaces);let r=5;for(const e of n)r=Math.max(r,Vr(e));Ur(e,t,(e,t)=>{const n=e*Pr,o=Math.abs(n-Math.round(n)),s=Er(e),a=o>0&&o=Or))return;const l=a&&"number"==typeof s?Math.abs(s-e):0;if(a?i.canonicalized++:i.far++,i.maxShift=Math.max(i.maxShift,l),i.maxShiftCm=Math.max(i.maxShiftCm,l*Pr*(t?Vr(t):r)),!t?.id||!a)return;const c=String(t.id);let h=i.bySpace.get(c);h||(h={spaceId:c,space:String(t.title||c),canonicalized:0,far:0,maxShift:0,maxShiftCm:0},i.bySpace.set(c,h)),h.canonicalized++,h.maxShift=Math.max(h.maxShift,l),h.maxShiftCm=Math.max(h.maxShiftCm,l*Pr*Vr(t))}),i.bySpace.size&&Ur(e,t,(e,t)=>{if(!t?.id)return;const n=i.bySpace.get(String(t.id));if(!n)return;const r=e*Pr;Math.abs(r-Math.round(r))>=Or&&n.far++}),i.spaces=[...i.bySpace.values()];const{bySpace:o,...s}=i;return s}function Kr(e){return function(e){const t=e,i=Nr(t);i&&qr(i,["x","y"]);return t}(Fr(e))}function Yr(e){const t=e,i=Nr(t);if(!i)return t;for(const e of Object.values(i)){const t=Nr(e);t&&qr(t,["x","y"])}return t}function Xr(e){return Zr(Fr(e))}function Zr(e){const t=e,i=Nr(t);if(!i)return t;for(const e of Lr(i.spaces)){Wr(e,["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"]);for(const t of Lr(e.rooms))qr(t,["x","y","w","h"]),jr(t.poly);for(const t of Lr(e.walls))Br(t.a),Br(t.b);for(const t of Lr(e.wall_segments))Br(t.a),Br(t.b);for(const t of Lr(e.openings)){qr(t,["x","y"]),Wr(t,["angle","length"]);const e=Nr(t.host);e&&Wr(e,["t"])}for(const t of Lr(e.decor))"line"===t.kind?qr(t,["x1","y1","x2","y2"]):"rect"===t.kind||"ellipse"===t.kind||"furniture"===t.kind?(qr(t,["x","y","w","h"]),Wr(t,["angle"])):"text"===t.kind&&(qr(t,["x","y"]),Wr(t,["scale","angle"]));for(const t of Lr(e.room_drafts))jr(t.points);for(const t of Lr(e.partitions))Br(t.a),Br(t.b);for(const t of Lr(e.wall_columns))Br(t.center),"square"===t.shape&&Wr(t,["angle"]);for(const t of Lr(e.open_spans))Br(t.a),Br(t.b)}for(const e of Lr(i.markers))Wr(e,["angle"]);return t}const Jr=15,Qr=8;function eo(e,t,i,n,r){const o=i[0]-e[0],s=i[1]-e[1],a=Math.hypot(o,s);if(!(a>0)||a<=r+1e-9)return null;const l=o/a,c=s/a,h=t=>{const n=(t[0]-e[0])*l+(t[1]-e[1])*c,o=(r-n)/(a-n);return!Number.isFinite(o)||o<-1e-9||o>1+1e-9?null:[t[0]+(i[0]-t[0])*o,t[1]+(i[1]-t[1])*o]},d=h(t),u=h(n);return d&&u?[[e[0],e[1]],t,d,u,n]:null}const to=Math.sin(.25*Math.PI/180);function io(e,t){return Number.isFinite(e)&&e>0&&Number.isFinite(t)&&t>0&&e*t<3}function no(e){const t=Number(e)>0?Number(e):5;if(5===t)return 8;const i=5/t*8;return Math.min(80,Math.max(.5,i))}function ro(e,t){return Number.isFinite(e)&&e>0&&Number.isFinite(t)&&t>0&&e*t<2}function oo(e){return Number.isFinite(e)?Math.max(1,Math.min(100,e)):1}function so(e,t){return!Number.isFinite(e)||e<0?"":String(t?Math.round(e/2.54*100)/100:Math.round(100*e)/100)}function ao(e,t,i){if(!Number.isFinite(e)||e<=0)return 0;const n=Number(t)>0?Number(t):5;return oo(e)/n*i}function lo(e,t){return t>0&&Number.isFinite(e)?Math.round(e/t)*t:e}function co(e){return Math.max(1e-6*Math.abs(e),1e-9)}function ho(e,t){if(!(t>0&&Number.isFinite(e)))return e;const i=lo(e,t);return Math.abs(i-e)<=co(t)?i:e}function uo(e,t){let i=t[0]-e[0],n=t[1]-e[1];const r=Math.hypot(i,n);return r<1e-12?[1,0]:(i/=r,n/=r,(i<-1e-12||Math.abs(i)<=1e-12&&n<0)&&(i=-i,n=-n),[i,n])}function po(e,t,i){const n=[ho(e[0],i),ho(e[1],i)],r=[ho(t[0],i),ho(t[1],i)],o=lo((n[0]+r[0])/2,i),s=lo((n[1]+r[1])/2,i),[a,l]=uo(n,r);let c=Math.atan2(l,a);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)},${s.toFixed(d)}@${h.toFixed(4)}`}function mo(e,t,i,n){return 1===n?po(e,t,i):po([e[0]/n,e[1]/n],[t[0]/n,t[1]/n],i)}function _o(e,t){if(!Array.isArray(e.a)||!Array.isArray(e.b)||e.a.length<2||e.b.length<2)return null;const i=[Number(e.a[0]),Number(e.a[1]),Number(e.b[0]),Number(e.b[1])];if(!i.every(Number.isFinite))return null;const n=t>0?t:1;return[[i[0]*n,i[1]*n],[i[2]*n,i[3]*n]]}function fo(e,t,i,n,r){const o=r>0?r:1;return{key:mo(e,t,n,o),cm:oo(i),a:[e[0]/o,e[1]/o],b:[t[0]/o,t[1]/o]}}function go(e,t){const i=t>0?t:1,n=[];for(const t of e){const e=t.key.lastIndexOf("@");if(e<0)continue;const[r,o]=t.key.slice(0,e).split(",").map(Number),s=Number(t.key.slice(e+1));[r,o,s].every(Number.isFinite)&&n.push({w:t,x:r*i,y:o*i,ang:s})}return n}function vo(e,t){const[i,n]=uo(e,t);let r=Math.atan2(n,i);return r<0&&(r+=Math.PI),r}function yo(e,t){let i=Math.abs(e-t);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function bo(e,t,i,n,r=1){if(!e?.length)return null;const o=mo(t,i,n,r),s=e.find(e=>e.key===o);if(s)return s;const a=r>0?r:1,l=co(n)*a,c=(e,t)=>Math.abs(e[0]-t[0])<=l&&Math.abs(e[1]-t[1])<=l;for(const n of e){const e=_o(n,a);if(e&&(c(e[0],t)&&c(e[1],i)||c(e[0],i)&&c(e[1],t)))return n}const h=(t[0]+i[0])/2,d=(t[1]+i[1])/2,u=vo(t,i),p=Math.max(.5*n,1e-9)*a;for(const t of go(e,a))if(yo(t.ang,u)&&Math.hypot(t.x-h,t.y-d)<=p)return t.w;return null}function wo(e,t,i,n,r=1){const o=bo(e,t,i,n,r);if(o&&o.cm>0)return oo(o.cm);const s=function(e,t,i,n,r){if(!e?.length)return null;const o=r>0?r:1,s=Math.hypot(i[0]-t[0],i[1]-t[1]);if(s<1e-12)return null;const a=vo(t,i),l=Math.max(.5*n,1e-9)*o;let c=null;for(const n of e){if(!(n.cm>0))continue;const e=_o(n,o);if(!e)continue;const r=Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1]);if(r<1e-12||!yo(vo(e[0],e[1]),a))continue;if(hs(t[0],t[1],e[0][0],e[0][1],e[1][0],e[1][1])>l||hs(i[0],i[1],e[0][0],e[0][1],e[1][0],e[1][1])>l)continue;if(r+l(o.has(e.key)||(e=>{const t=_o(e,n);if(!t)return!1;const[i,o]=t,a=o[0]-i[0],c=o[1]-i[1],h=Math.hypot(a,c);if(h<=l)return!1;if(!s.some(e=>{const t=[e[0],e[1]],n=[e[2],e[3]];return yo(vo(i,o),vo(t,n))&&hs(i[0],i[1],t[0],t[1],n[0],n[1])<=l&&hs(o[0],o[1],t[0],t[1],n[0],n[1])<=l}))return!1;const d=(r||[]).some(e=>{const t=[e[0],e[1]],n=[e[2],e[3]];if(!yo(vo(i,o),vo(t,n)))return!1;const r=e=>Math.abs((e[0]-i[0])*c-(e[1]-i[1])*a)/h;if(r(t)>l||r(n)>l)return!1;const s=h*h,d=((t[0]-i[0])*a+(t[1]-i[1])*c)/s,u=((n[0]-i[0])*a+(n[1]-i[1])*c)/s;return Math.min(1,Math.max(d,u))-Math.max(0,Math.min(d,u))>l/h});return!d})(e))&&e.cm>=1&&e.cm<=100)}function xo(e,t,i,n=8){const[r,o]=uo(e,t);let s=Math.atan2(o,r);s<0&&(s+=Math.PI);let a=i*Math.PI/180%Math.PI;a<0&&(a+=Math.PI);let l=Math.abs(s-a);return l>Math.PI/2&&(l=Math.PI-l),l<=n*Math.PI/180}function $o(e,t,i,n,r=1,o="affine",s){if(!e?.length)return[];if(t.length!==i.length)return"fixed-topology"===o&&s?.(),e.slice();const a=r>0?r:1,l=Math.max(.5*n,1e-9)*a,c=Math.max(n*a*1e-6,1e-9),h=[],d=new Map,u=new Map,p=(e,t,i)=>{const n=e.get(t)||new Set;n.add(i),e.set(t,n)};for(let e=0;e[e[0]+(t[0]-e[0])*i,e[1]+(t[1]-e[1])*i],_=(e,t)=>Math.hypot(e[0]-t[0],e[1]-t[1])<=c,f=(e,t)=>{if("fixed-topology"===o){const i=t.na[0]-t.oa[0],n=t.na[1]-t.oa[1],r=t.nb[0]-t.ob[0],o=t.nb[1]-t.ob[1];return Math.hypot(i-r,n-o)<=c?[e[0]+i,e[1]+n]:_(e,t.oa)?[...t.na]:_(e,t.ob)?[...t.nb]:[...e]}const i=Math.max(0,Math.min(1,((e[0]-t.oa[0])*t.dx+(e[1]-t.oa[1])*t.dy)/t.len2));return m(t.na,t.nb,i)},g=(e,t)=>{const[i,n]=uo(e,t);return(t[0]-e[0])*i+(t[1]-e[1])*n>=0?[[...e],[...t]]:[[...t],[...e]]},v=[],y=[],b=(e,t,i)=>{if(Math.hypot(t[0]-e[0],t[1]-e[1])<=c)return;const[r,o]=g(e,t),s=oo(i);if(y.some(e=>e.entry.cm===s&&_(e.span[0],r)&&_(e.span[1],o)))return;const l=fo(r,o,s,n,a);v.push(l),y.push({entry:l,span:[r,o]})};for(const t of e){const e=_o(t,a);if(e){const[i,n]=g(e[0],e[1]),r=n[0]-i[0],s=n[1]-i[1],a=r*r+s*s,d=Math.sqrt(a);if(d<=c){v.push({...t,cm:oo(t.cm)});continue}const u=[];for(const e of h){if(!yo(vo(i,n),vo(e.oa,e.ob)))continue;const t=e=>Math.abs((e[0]-i[0])*s-(e[1]-i[1])*r)/d;if(t(e.oa)>l||t(e.ob)>l)continue;const o=((e.oa[0]-i[0])*r+(e.oa[1]-i[1])*s)/a,h=((e.ob[0]-i[0])*r+(e.ob[1]-i[1])*s)/a,p=Math.max(0,Math.min(o,h)),m=Math.min(1,Math.max(o,h));(m-p)*d>c&&u.push({lo:p,hi:m,move:e})}if(!u.length){"fixed-topology"===o?v.push({...t}):b(i,n,t.cm);continue}const p=[0,1,...u.flatMap(({lo:e,hi:t})=>[e,t])].sort((e,t)=>e-t).filter((e,t,i)=>0===t||Math.abs(e-i[t-1])*d>c),y=[];for(let e=0;e+1a>=e.lo-1e-12&&a<=e.hi+1e-12);if(!l.length){y.push([o,s]);continue}const h=[f(o,l[0].move),f(s,l[0].move)],g=l.slice(1).some(e=>{const t=f(o,e.move),i=f(s,e.move);return!_(h[0],t)||!_(h[1],i)});y.push(g?[o,s]:h)}const w=(e,t)=>{const i=e[1][0]-e[0][0],n=e[1][1]-e[0][1],r=t[1][0]-t[0][0],o=t[1][1]-t[0][1],s=Math.hypot(i,n);return!(s<=c||i*r+n*o<=0)&&Math.abs(i*o-n*r)/s<=c},k=[];for(const e of y){const t=k[k.length-1];t&&_(t[1],e[0])&&w(t,e)?t[1]=e[1]:k.push([[...e[0]],[...e[1]]])}if("fixed-topology"===o&&1===k.length){const[i,n]=k[0];if(_(i,e[0])&&_(n,e[1])||_(i,e[1])&&_(n,e[0])){v.push({...t});continue}}for(const[e,i]of k)b(e,i,t.cm);continue}if("fixed-topology"===o){const e=u.get(t.key);if(1===e?.size){const i=[...e][0];v.push(i===t.key?{...t}:{...t,key:i});continue}const i=[go([t],a)[0]];1!==a&&i.push(go([t],1)[0]);const n=i.filter(Boolean).some(e=>h.some(t=>{if(!yo(e.ang,vo(t.oa,t.ob)))return!1;const i=((e.x-t.oa[0])*t.dx+(e.y-t.oa[1])*t.dy)/t.len2;return i>=-1e-6&&i<=1.000001&&hs(e.x,e.y,t.oa[0],t.oa[1],t.ob[0],t.ob[1])<=l}));((e?.size||0)>1||n)&&s?.(),v.push({...t});continue}let i="";const r=d.get(t.key);if(1===r?.size&&(i=[...r][0]),!i){const e=go([t],a)[0];if(e){const t=new Set;for(const i of h){if(!yo(e.ang,vo(i.oa,i.ob)))continue;const r=((e.x-i.oa[0])*i.dx+(e.y-i.oa[1])*i.dy)/i.len2;if(r<-1e-6||r>1.000001)continue;if(hs(e.x,e.y,i.oa[0],i.oa[1],i.ob[0],i.ob[1])>l)continue;const o=f([e.x,e.y],i),[s,c]=uo(i.na,i.nb),h=Math.max(n*a,1e-6);t.add(mo([o[0]-s*h,o[1]-c*h],[o[0]+s*h,o[1]+c*h],n,a))}1===t.size&&(i=[...t][0])}}v.push({...t,key:i||t.key,cm:oo(t.cm)})}return v}function So(e,t,i,n,r=1,o="affine"){return $o(e,t,i,n,r,o)}function Mo(e,t,i,n,r=1,o="fixed-topology"){let s=!1;const a=$o(e,t,i,n,r,o,()=>{s=!0});return{walls:s?(e||[]).map(e=>({...e})):a,rejected:s}}function Ro(e,t,i,n=1,r=[]){const o=n>0?n:1,s=Math.abs(i),a=Math.max(s*o*Or,1e-9),l=(r||[]).flatMap(e=>{const t=_o(e,o);return t?t.map(e=>[...e]):[]}),c=e=>e.every((e,t)=>(e=>{if(!(s>0))return!0;const t=e/o/s;return Math.abs(t-Math.round(t))Math.abs(e-i[t])<=a)),h=[],d=e=>JSON.stringify([e.key,e.cm,e.a,e.b]);for(const i of e||[]){const e=_o(i,o);if(!e)continue;const[n,r]=e;if(![n[0],n[1],r[0],r[1]].every(Number.isFinite)||!c(n)||!c(r)){h.push(d(i));continue}const s=r[0]-n[0],l=r[1]-n[1],u=Math.hypot(s,l);if(u<=a){h.push(d(i));continue}const p=s/u,m=l/u,_=[];for(const e of t){const[t,i]=e;if(![t?.[0],t?.[1],i?.[0],i?.[1]].every(Number.isFinite))continue;const r=e=>Math.abs((e[0]-n[0])*m-(e[1]-n[1])*p);if(r(t)>a||r(i)>a)continue;const o=(t[0]-n[0])*p+(t[1]-n[1])*m,s=(i[0]-n[0])*p+(i[1]-n[1])*m,l=Math.max(0,Math.min(o,s)),c=Math.min(u,Math.max(o,s));c-l>a&&_.push([l,c])}_.sort((e,t)=>e[0]-t[0]||e[1]-t[1]);let f=0;for(const[e,t]of _){if(e>f+a)break;if(f=Math.max(f,t),f>=u-a)break}fe.key!==s);return null==n||n<1?a:[...a,fo(t,i,n,r,o)]}function Co(e,t,i,n,r,o=[],s=1){let a=e?e.slice():[];for(const e of function(e,t,i,n,r=1,o=[]){const s=Xo(e,t,i,n,r,o);if(!s)return[];const a=[];for(let e=0;e0||(c=To(c,t,i,n,r,s)))}return c}function zo(e){const{a:t,b:i,halfDepth:n}=e;if(!Array.isArray(t)||!Array.isArray(i)||t.length<2||i.length<2||![t[0],t[1],i[0],i[1]].every(Number.isFinite))return null;const r=i[0]-t[0],o=i[1]-t[1],s=Math.hypot(r,o);if(!(s>1e-9&&n>0&&Number.isFinite(n)))return null;const a=-o/s*n,l=r/s*n;return[[t[0]+a,t[1]+l],[i[0]+a,i[1]+l],[i[0]-a,i[1]-l],[t[0]-a,t[1]-l]]}function Ao(e,t,i){return Math.hypot(e[0]-t[0],e[1]-t[1])<=i}function Po(e,t,i){const n=t.b[0]-t.a[0],r=t.b[1]-t.a[1],o=n*n+r*r;if(!(o>i*i))return!1;const s=((e[0]-t.a[0])*n+(e[1]-t.a[1])*r)/o;if(!(s>0&&s<1))return!1;const a=[t.a[0]+n*s,t.a[1]+r*s];return Math.hypot(e[0]-a[0],e[1]-a[1])<=i}function Oo(e,t,i,n){const r=Math.hypot(t,i);if(!(r>1e-9&&n>0))return;const o=[t/r,i/r],s=e.find(e=>Math.abs(e.u[0]*o[1]-e.u[1]*o[0])<1e-9&&e.u[0]*o[0]+e.u[1]*o[1]>1-1e-9);s?s.halfDepth=Math.max(s.halfDepth,n):e.push({u:o,halfDepth:n})}function Fo(e,t){try{const i=Mt(ps(e),ps(t));let n=null,r=0;for(const e of i||[]){const t=e?.[0]||[],i=Math.abs(Wo(t));t.length>=4&&i>r&&(r=i,n=t.slice(0,-1).map(e=>[e[0],e[1]]))}return n}catch{return null}}function Io(e,t=1e-6){const i=(e||[]).map((e,t)=>({segment:e,index:t})).filter(({segment:e})=>e&&Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2&&e.a.every(Number.isFinite)&&e.b.every(Number.isFinite)&&Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>1e-9);if(i.length<2)return[];const n=Math.max(Number.isFinite(t)?t:0,1e-9),r=i.flatMap(({segment:e})=>[e.a,e.b]).map(e=>[e[0],e[1]]).sort((e,t)=>e[0]-t[0]||e[1]-t[1]),o=[];for(const e of r)o.some(t=>Ao(t,e,n))||o.push(e);const s=[];for(const e of o){const t=[];let r=!1;for(const{segment:o,index:s}of i){const i=Math.hypot(o.b[0]-o.a[0],o.b[1]-o.a[1]);Ao(e,o.a,n)?t.push({u:[(o.b[0]-o.a[0])/i,(o.b[1]-o.a[1])/i],halfDepth:o.halfDepth,length:i,index:s}):Ao(e,o.b,n)?t.push({u:[(o.a[0]-o.b[0])/i,(o.a[1]-o.b[1])/i],halfDepth:o.halfDepth,length:i,index:s}):Po(e,o,n)&&(r=!0)}if(r||2!==t.length)continue;const[o,a]=t,l=o.u[0]*a.u[1]-o.u[1]*a.u[0];if(Math.abs(l)<1e-9)continue;const c=l<0?1:-1,h=[-o.u[1],o.u[0]],d=[-a.u[1],a.u[0]],u=[e[0]+h[0]*o.halfDepth*c,e[1]+h[1]*o.halfDepth*c],p=[e[0]-d[0]*a.halfDepth*c,e[1]-d[1]*a.halfDepth*c];if(!jo(u,o.u,p,a.u))continue;const m=[[o,a,p,[d[0]*-c,d[1]*-c]],[a,o,u,[h[0]*c,h[1]*c]]];for(const[t,,i,r]of m){const o=Math.min(2*t.halfDepth,t.length),a=[-t.u[1]*t.halfDepth,t.u[0]*t.halfDepth],l=[[e[0]+a[0],e[1]+a[1]],[e[0]+t.u[0]*o+a[0],e[1]+t.u[1]*o+a[1]],[e[0]+t.u[0]*o-a[0],e[1]+t.u[1]*o-a[1]],[e[0]-a[0],e[1]-a[1]]],c=e=>(e[0]-i[0])*r[0]+(e[1]-i[1])*r[1],h=[];for(let e=0;e=-1e-12&&h.push(t),n>1e-12&&r<-1e-12||n<-1e-12&&r>1e-12){const e=n/(n-r);h.push([t[0]+(i[0]-t[0])*e,t[1]+(i[1]-t[1])*e])}}h.length>=3&&Math.abs(Wo(h))>n*n&&s.push({segmentIndex:t.index,wedge:h})}}return s}function Eo(e,t=1e-6){const i=(e||[]).filter(e=>e&&Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2&&e.a.every(Number.isFinite)&&e.b.every(Number.isFinite)&&Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>1e-9);if(i.length<2)return[];const n=Math.max(Number.isFinite(t)?t:0,1e-9),r=i.flatMap(e=>[e.a,e.b]).map(e=>[e[0],e[1]]).sort((e,t)=>e[0]-t[0]||e[1]-t[1]),o=[];for(const e of r)o.some(t=>Ao(t,e,n))||o.push(e);const s=[],a=i.map((e,t)=>({roomId:"",a:[e.a[0],e.a[1]],b:[e.b[0],e.b[1]],key:`join-${t}`,kind:"outer",cm:0,open:!1,half:e.halfDepth})),l=ts(a,n);for(const e of is(l).fans)s.push(e);const c=e=>!!ns(l,e);for(const e of o){if(c(e))continue;const t=[];for(const r of i)Ao(e,r.a,n)?Oo(t,r.b[0]-r.a[0],r.b[1]-r.a[1],r.halfDepth):Ao(e,r.b,n)?Oo(t,r.a[0]-r.b[0],r.a[1]-r.b[1],r.halfDepth):Po(e,r,n)&&(Oo(t,r.a[0]-e[0],r.a[1]-e[1],r.halfDepth),Oo(t,r.b[0]-e[0],r.b[1]-e[1],r.halfDepth));if(!(t.length<2)){t.sort((e,t)=>Math.atan2(e.u[1],e.u[0])-Math.atan2(t.u[1],t.u[0])||e.halfDepth-t.halfDepth);for(let i=0;in*n&&s.push(_)}}}return s}function Ho(e,t,i,n){if(!(t>0)||!e||e.length<2)return"";if(i&&e.length>=3){let i=e;const r=e[e.length-1];if(e.length>=4&&Math.hypot(e[0][0]-r[0],e[0][1]-r[1])<1e-9&&(i=e.slice(0,-1)),i.length>=3){const e=i.map((e,i)=>n?.[i]||t),r=As(i,e),o=Go(i,e);if(r&&o)return`${Ko(r)} ${Ko(Yo(o))}`}}const r=[];for(let i=0;i=1e-9&&a>0&&r.push({a:o,b:s,halfDepth:a})}const o=r.map(zo);for(const{segmentIndex:e,wedge:t}of Io(r)){const i=o[e];if(!i)continue;const n=Fo(i,t);n&&(o[e]=n)}const s=[...o.filter(e=>!!e),...Eo(r)],a=function(e){let t=null;try{for(const i of e){if(i.length<3)continue;const e=ps(i);t=t?$t(t,e):[e]}return t}catch{return null}}(s);return a?Ss(a):s.map(e=>Ko(e)).join(" ")}function No(e,t,i,n,r=1){if(!i.length)return!1;const o=Lo(n,r),s=(e[0]+t[0])/2,a=(e[1]+t[1])/2,[l,c]=uo(e,t);for(const e of i){const[t,i]=uo([e[0],e[1]],[e[2],e[3]]);if(!(Math.abs(l*i-c*t)>.05)&&hs(s,a,e[0],e[1],e[2],e[3])<=o)return!0}return!1}function Lo(e,t){return Math.max(e*(t>0?t:1)*.04,1e-9)}function Wo(e){let t=0;for(let i=0;ie[1]!=l>e[1]&&e[0]<(a-o)*(e[1]-s)/(l-s+0)+o&&(i=!i)}return i}([c[0]+.001*a,c[1]+.001*l],e)||(a=-a,l=-l),[a,l]}function Bo(e,t){const i=e[0]*t[1]-e[1]*t[0],n=e[0]*t[0]+e[1]*t[1];return Math.abs(i)<1e-9&&n>0}function jo(e,t,i,n){const r=t[0]*n[1]-t[1]*n[0];if(Math.abs(r)<1e-12)return null;const o=[i[0]-e[0],i[1]-e[1]],s=(o[0]*n[1]-o[1]*n[0])/r;return[e[0]+s*t[0],e[1]+s*t[1]]}function Uo(e,t,i){const n=e?.length||0;if(n<3||!Array.isArray(i)||i.length!==n)return 0;const r=(Math.trunc(t)%n+n)%n,o=e[r],s=e[(r+1)%n],a=s[0]-o[0],l=s[1]-o[1],c=Math.hypot(a,l);if(!(c>0))return 0;const h=Math.max(0,Number(i[r])||0);if(!(h>0))return c;const d=[a/c,l/c],u=qo(e,r),p=[o[0]+u[0]*h,o[1]+u[1]*h],m=t=>{const r=Math.max(0,Number(i[t])||0);if(!(r>0))return null;const s=e[t],a=e[(t+1)%n],l=a[0]-s[0],c=a[1]-s[1],h=Math.hypot(l,c);if(!(h>0))return null;const u=qo(e,t),m=jo(p,d,[s[0]+u[0]*r,s[1]+u[1]*r],[l/h,c/h]);return m?(m[0]-o[0])*d[0]+(m[1]-o[1])*d[1]:null},_=m((r-1+n)%n)??0,f=(m((r+1)%n)??c)-_;return f>0?f:0}function Vo(e,t,i,n,r,o,s,a=1){const l=(e||[]).find(e=>e?.id===t),c=Et(l);if(!c||c.length<3)return null;const h=os(e,t,i,n,r,o,s,a);if(!h)return c.map(()=>0);const d=4*Lo(r,a);return c.map((e,t)=>{const i=c[(t+1)%c.length],n=[(e[0]+i[0])/2,(e[1]+i[1])/2];for(let e=0;e!(e>0)))return e.map(e=>[e[0],e[1]]);const r=[];for(let o=0;o0||u>0)){r.push([e[o][0],e[o][1]]);continue}if(d>0!=u>0){const t=e[o],i=d>0?[t[0]+p*d,t[1]+m*d]:[t[0],t[1]],n=u>0?[t[0]+_*u,t[1]+f*u]:[t[0],t[1]];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}if(Bo(w,k)){const t=e[o],i=[t[0]+p*d,t[1]+m*d],n=[t[0]+_*u,t[1]+f*u];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}const S=jo(x,w,$,k),M=Math.max(d,u,1e-9),R=ns(i,e[o])?.limit??4*M;if(S){const i=Math.hypot(S[0]-e[o][0],S[1]-e[o][1]);if(Number.isFinite(i)&&i<=R){r.push(S);continue}if(Rs(e,t,o)){r.push(S);continue}}d>0&&r.push([e[o][0]+p*d,e[o][1]+m*d]),u>0&&r.push([e[o][0]+_*u,e[o][1]+f*u]),d>0||u>0||r.push([e[o][0],e[o][1]])}return r.length>=3?r:null}function Ko(e,t=!0){if(!e.length)return"";let i=`M ${e[0][0]} ${e[0][1]}`;for(let t=1;te?.id===t),a=Et(s);if(!a||a.length<3)return null;const l=Lo(n,r),c=[];for(const i of e||[]){if(!i||i.id===t)continue;const e=Et(i);if(e)for(const t of cn(a,e,l))c.push([t[0],t[1]],[t[2],t[3]])}for(const e of i||[])c.push([e[0],e[1]],[e[2],e[3]]);for(const e of o||[]){const t=_o(e,r);t&&c.push(t[0],t[1])}const h=[],d=[];for(let e=0;el)continue;const s=((e[0]-t[0])*(i[0]-t[0])+(e[1]-t[1])*(i[1]-t[1]))/(n*n);s<=r||s>=1-r||(o.some(e=>Math.abs(e-s)*n<=2*l)||o.push(s))}o.sort((e,t)=>e-t);for(const n of o)h.push([t[0]+(i[0]-t[0])*n,t[1]+(i[1]-t[1])*n]),d.push(e)}return{poly:h,parent:d,orig:a}}function Zo(e,t,i,n){const r=t.poly.length,o=new Array(r).fill(0);if(!e?.length)return o;const s=new Set,a=[];for(let l=0;l0?(o[l]=oo(c.cm),s.add(c.key)):a.push(l)}if(!a.length)return o;const l=n>0?n:1,c=Math.max(.5*i,1e-9)*l,h=go(e,l).filter(e=>e.w.cm>0);for(let e=a.length-1;e>=0;e--){const i=a[e],n=t.poly[i],s=t.poly[(i+1)%r],d=vo(n,s);let u=null;for(const e of h){const t=_o(e.w,l);if(!t||!yo(vo(t[0],t[1]),d))continue;if(hs(n[0],n[1],t[0][0],t[0][1],t[1][0],t[1][1])>c||hs(s[0],s[1],t[0][0],t[0][1],t[1][0],t[1][1])>c)continue;const i=Math.hypot(s[0]-n[0],s[1]-n[1]),r=Math.hypot(t[1][0]-t[0][0],t[1][1]-t[0][1]),o=Math.max(0,r-i);(!u||oc||hs(r[0],r[1],t[0][0],t[0][1],t[1][0],t[1][1])>c)continue;i=!0,o=Math.max(0,Math.hypot(t[1][0]-t[0][0],t[1][1]-t[0][1])-m)}else{if(hs(e.x,e.y,n[0],n[1],r[0],r[1])>c)continue;o=Math.hypot(e.x-d,e.y-u)}(!p||i&&!p.exact||i===p.exact&&o0?i:1,o=(e||[]).filter(e=>e&&!e.open&&null!==e.kind&&Number.isFinite(e.half)&&e.half>0&&Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2&&[e.a[0],e.a[1],e.b[0],e.b[1]].every(Number.isFinite)&&Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])>n).sort((e,t)=>e.key.localeCompare(t.key)||e.a[0]-t.a[0]||e.a[1]-t.a[1]||e.b[0]-t.b[0]||e.b[1]-t.b[1]||e.half-t.half),s=new Map;for(const e of o){const t=s.get(e.key);t?e.half>t.half&&s.set(e.key,{...t,half:e.half}):s.set(e.key,e)}const a=[...s.values()].flatMap(e=>[{point:[e.a[0],e.a[1]],other:e.b,halfDepth:e.half,kind:e.kind,key:e.key},{point:[e.b[0],e.b[1]],other:e.a,halfDepth:e.half,kind:e.kind,key:e.key}]).sort((e,t)=>e.point[0]-t.point[0]||e.point[1]-t.point[1]||e.other[0]-t.other[0]||e.other[1]-t.other[1]||e.halfDepth-t.halfDepth),l=new Map;for(const e of a){const[t,i]=Jo(e.point,n),r=Qo(t,i),o=l.get(r)||[];o.push(e),l.set(r,o)}const c=[],h=new Map;for(const e of a){const t=es(h,e.point,n).filter(t=>Math.hypot(t.point[0]-e.point[0],t.point[1]-e.point[1])<=n).sort((t,i)=>Math.hypot(t.point[0]-e.point[0],t.point[1]-e.point[1])-Math.hypot(i.point[0]-e.point[0],i.point[1]-e.point[1])||t.point[0]-i.point[0]||t.point[1]-i.point[1]);let i=t[0];if(!i){i={point:[...e.point],rays:[]},c.push(i);const[t,r]=Jo(i.point,n),o=Qo(t,r),s=h.get(o)||[];s.push(i),h.set(o,s)}const r=e.other[0]-e.point[0],o=e.other[1]-e.point[1],s=Math.hypot(r,o);if(!(s>n))continue;const a=[r/s,o/s];let l=Math.atan2(a[1],a[0]);l<0&&(l+=2*Math.PI),i.rays.push({u:a,halfDepth:e.halfDepth,length:s,angle:l})}const d=[],u=e=>{const t=1e-9*Math.max(1,r),i=e.filter(e=>Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Number.isFinite(e.length)&&e.length>n);return i.filter((e,n)=>!i.some((i,r)=>r!==n&&i.halfDepth>=e.halfDepth-t&&i.length>=e.length-t&&(i.halfDepth>e.halfDepth+t||i.length>e.length+t||re.length-t.length||e.halfDepth-t.halfDepth).map(e=>({...e}))};for(const e of c){const t=e.rays.sort((e,t)=>e.angle-t.angle||e.length-t.length||e.halfDepth-t.halfDepth),i=[];for(const e of t){const t=i[i.length-1];t&&Math.abs(e.angle-t.angle)<=1e-9?t.supports.push({halfDepth:e.halfDepth,length:e.length}):i.push({u:[...e.u],angle:e.angle,supports:[{halfDepth:e.halfDepth,length:e.length}]})}if(i.length>1&&2*Math.PI-i[i.length-1].angle+i[0].angle<=1e-9){const e=i.pop();i[0].supports.push(...e.supports)}if(i.length<3)continue;const r=i.map(t=>{const i=u(t.supports),r=new Set,o=[];for(const s of i){const i=[e.point[0]+t.u[0]*s.length,e.point[1]+t.u[1]*s.length];for(const t of es(l,i,n)){if("shared"!==t.kind||Math.hypot(t.point[0]-i[0],t.point[1]-i[1])>n)continue;const s=t.other[0]-t.point[0],a=t.other[1]-t.point[1],l=Math.hypot(s,a);if(!(l>n))continue;const c=s/l,h=a/l;if(Math.hypot(t.other[0]-e.point[0],t.other[1]-e.point[1])<=n)continue;const d=`${t.key}|${t.point[0]}|${t.point[1]}|${t.other[0]}|${t.other[1]}|${t.halfDepth}`;r.has(d)||(r.add(d),o.push({start:[t.point[0],t.point[1]],u:[c,h],length:l,halfDepth:t.halfDepth}))}}return o.sort((e,t)=>e.start[0]-t.start[0]||e.start[1]-t.start[1]||e.u[0]-t.u[0]||e.u[1]-t.u[1]||e.length-t.length||e.halfDepth-t.halfDepth),{u:[...t.u],halfDepth:Math.max(...i.map(e=>e.halfDepth)),length:Math.max(...i.map(e=>e.length)),supports:i,continuations:o}}).filter(e=>Number.isFinite(e.halfDepth)&&e.halfDepth>0&&Number.isFinite(e.length)&&e.length>n);if(r.length<3)continue;const o=Math.max(...r.map(e=>e.halfDepth));o>0&&Number.isFinite(o)&&d.push({point:[...e.point],rays:r,halfDepth:o,limit:1.25*o})}d.sort((e,t)=>e.point[0]-t.point[0]||e.point[1]-t.point[1]);const p=new Map;for(const e of d){const[t,i]=Jo(e.point,n),r=Qo(t,i),o=p.get(r)||[];o.push(e),p.set(r,o)}return{epsilon:n,coordinateScale:r,nodes:d,index:p}}function is(e){const t={fans:[],supports:[]};if(!e?.nodes?.length)return t;const i=Math.max(e.epsilon,1e-9)**2;for(const n of e.nodes){const e=n.rays.filter(e=>Number.isFinite(e.halfDepth)&&e.halfDepth>0).map(e=>({...e,thickLength:Math.max(...e.supports.filter(t=>t.halfDepth>=e.halfDepth-1e-12).map(e=>e.length),0),angle:(()=>{const t=Math.atan2(e.u[1],e.u[0]);return t<0?t+2*Math.PI:t})()})).sort((e,t)=>e.angle-t.angle);if(e.length<2)continue;const r=n.point;for(const i of e)for(const e of i.supports){if(!(e.halfDepth>0&&e.length>0))continue;const n=-i.u[1]*e.halfDepth,o=i.u[0]*e.halfDepth,s=[r[0]+i.u[0]*e.length,r[1]+i.u[1]*e.length];t.supports.push([[r[0]+n,r[1]+o],[s[0]+n,s[1]+o],[s[0]-n,s[1]-o],[r[0]-n,r[1]-o]])}for(let n=0;n{const e=s.angle-o.angle;return e>0?e:e+2*Math.PI})();if(a<1e-9)continue;const l=a>Math.PI+1e-9,c=4*Math.max(o.halfDepth,s.halfDepth),h=[r[0]-o.u[1]*o.halfDepth,r[1]+o.u[0]*o.halfDepth],d=[r[0]+s.u[1]*s.halfDepth,r[1]-s.u[0]*s.halfDepth],u=o.u[0]*s.u[1]-o.u[1]*s.u[0],p=e=>{let t=Math.atan2(e[1]-r[1],e[0]-r[0])-o.angle;for(;t<0;)t+=2*Math.PI;return t<=a+1e-9};let m=null;if(Math.abs(u)>1e-9){const e=((d[0]-h[0])*s.u[1]-(d[1]-h[1])*s.u[0])/u,t=((d[0]-h[0])*o.u[1]-(d[1]-h[1])*o.u[0])/u,i=[h[0]+o.u[0]*e,h[1]+o.u[1]*e];(l?e<=1e-9&&t<=1e-9:e>1e-9&&e<=o.thickLength&&t<=s.thickLength)&&Math.hypot(i[0]-r[0],i[1]-r[1])<=c&&p(i)&&(m=i)}const _=e=>{Math.abs(Wo(e))>i&&t.fans.push(e)};if(m){const e=1.5*Math.max(o.halfDepth,s.halfDepth);_(eo([r[0],r[1]],h,m,d,e)??[[r[0],r[1]],h,m,d]);continue}if(l){_([[r[0],r[1]],h,d]);continue}const f=(e,t)=>Math.min(t,Math.sqrt(Math.max(c**2-e**2,0)),2*Math.max(o.halfDepth,s.halfDepth)),g=[h[0]+o.u[0]*f(o.halfDepth,o.thickLength),h[1]+o.u[1]*f(o.halfDepth,o.thickLength)],v=[d[0]+s.u[0]*f(s.halfDepth,s.thickLength),d[1]+s.u[1]*f(s.halfDepth,s.thickLength)];_([[r[0],r[1]],h,g,v,d])}}return t}function ns(e,t){return!e||!Array.isArray(t)||t.length<2||!t.slice(0,2).every(Number.isFinite)?null:es(e.index,t,e.epsilon).filter(i=>Math.hypot(i.point[0]-t[0],i.point[1]-t[1])<=e.epsilon).sort((e,i)=>Math.hypot(e.point[0]-t[0],e.point[1]-t[1])-Math.hypot(i.point[0]-t[0],i.point[1]-t[1])||e.point[0]-i.point[0]||e.point[1]-i.point[1])[0]||null}function rs(e,t,i,n,r,o,s){return ts(ss(e,t,i,n,r,o,s),4*Lo(n,s),s)}function os(e,t,i,n,r,o,s,a=1){const l=Xo(e,t,n,r,a,i);if(!l)return null;const c=function(e,t,i){const n=(e||[]).find(e=>e?.id===t),r=Et(n);if(!r)return[];const o=[];for(const n of e||[]){if(!n||n.id===t)continue;const e=Et(n);if(e)for(const t of cn(r,e,i))o.push(t)}return o}(e,t,Lo(r,a)),h=function(e,t,i,n,r){const o=Lo(n,r),s=[];for(let a=0;ahs(h,d,e[0],e[1],e[2],e[3])<=o);s.push(u?"shared":"outer")}return s}(l.poly,c,n,r,a),d=Zo(i,l,r,a),u=d.map((e,t)=>h[t]&&e>0?ao(e,o,s)/2:0);return{...l,kinds:h,cms:d,offsets:u}}function ss(e,t,i,n,r,o,s=1){const a=[];for(const l of e||[]){if(!l?.id)continue;const c=os(e,l.id,t,i,n,r,o,s);if(c)for(let e=0;e0&&(a=To(a,e.a,e.b,e.cm,n,s));return a}function ls(e,t,i,n,r,o,s=1){if(!t?.length)return[];const a=ss(e,t,i,n,r,o,s),l=new Map;for(const e of a){if(e.open||!e.kind||!e.roomId)continue;const t=l.get(e.key)||new Set;t.add(e.roomId),l.set(e.key,t)}const c=e=>{const t=[...l.get(e)||[]].sort();return 1!==t.length&&2!==t.length?`ambiguous:${e}`:`${1===t.length?"outer":"shared"}:${t.join("|")}`},h=[],d=new Set;for(const e of a)!e.open&&e.cm>0&&!d.has(e.key)&&(d.add(e.key),h.push({...e,ownerSignature:c(e.key)}));const u=[];for(const a of e||[]){if(!a?.id)continue;const l=os(e,a.id,t,i,n,r,o,s);if(l)for(let e=0;e0)||null===l.kinds[i]){e++;continue}const o=c(mo(l.poly[i],l.poly[(i+1)%l.poly.length],n,s));let a=e;for(;a+10&&u.push({a:[d[0],d[1]],b:[p[0],p[1]],key:mo(d,p,n,s),cm:r,len:m,ownerSignature:o}),e=a+1}}}u.sort((e,t)=>t.len-e.len||e.key.localeCompare(t.key));const p=[],m=new Set,_=new Set,f=4*Lo(n,s);for(const e of u){const t=h.filter(t=>!_.has(t.key)&&t.cm===e.cm&&t.ownerSignature===e.ownerSignature&&yo(vo(t.a,t.b),vo(e.a,e.b))&&hs(t.a[0],t.a[1],e.a[0],e.a[1],e.b[0],e.b[1])<=f&&hs(t.b[0],t.b[1],e.a[0],e.a[1],e.b[0],e.b[1])<=f);if(t.length){for(const e of t)_.add(e.key);m.has(e.key)||(m.add(e.key),p.push(fo(e.a,e.b,e.cm,n,s)))}}for(const e of h)_.has(e.key)||m.has(e.key)||(m.add(e.key),p.push(fo(e.a,e.b,e.cm,n,s)));return p}function cs(e,t,i,n,r,o,s,a=1){const l=Lo(r,a),c=(n[0]+n[2])/2,h=(n[1]+n[3])/2,d=vo([n[0],n[1]],[n[2],n[3]]);let u=null;for(const n of ss(e,t,i,r,o,s,a)){if(!yo(vo(n.a,n.b),d))continue;const e=hs(c,h,n.a[0],n.a[1],n.b[0],n.b[1]);e>4*l||(!u||e[e[0],e[1]]),o=Math.abs(Wo(r));r.length>=3&&o>i&&(t=r,i=o)}return t}function us(e,t,i,n,r,o,s,a=1,l,c){const h=(e||[]).find(e=>e?.id===t),d=Et(h);if(!d||d.length<3)return null;if(!i?.length)return d.map(e=>[e[0],e[1]]);const u=os(e,t,i,n,r,o,s,a);if(!u||!u.offsets.some(e=>e>0))return d.map(e=>[e[0],e[1]]);const p=c||rs(e,i,n,r,o,s,a),m=Go(u.poly,u.offsets,p);if(!m)return d.map(e=>[e[0],e[1]]);if(!p.nodes.length)return m;const _=void 0===l?Ts(e,i,n,[],r,o,s,a):null,f=l??("ok"===_?.status||"degraded-extra"===_?.status?_.roomGeom:void 0);if(f)try{const e=ds(Mt(ps(u.poly),f));if(e)return e}catch{}return function(e,t){try{return ds(St(ps(e),ps(t)))}catch{return null}}(m,u.poly)||d.map(e=>[e[0],e[1]])}function ps(e){const t=e.map(e=>[e[0],e[1]]);return t.push([e[0][0],e[0][1]]),[t]}function ms(e){return!(!Array.isArray(e)||!e.length)&&e.every(e=>Array.isArray(e)&&e.length&&e.every(e=>Array.isArray(e)&&e.length>=4&&e.every(e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1])))&&Math.abs(li(e[0]))>1e-9)}function _s(e,t,i,n){const r=n?new Set(n):null;let o=null;for(let n=0;nt.epsilon))continue;const l=bs([[e.point[0]+a[0]*n.halfDepth,e.point[1]+a[1]*n.halfDepth],[e.point[0]+s.u[0]*r+a[0]*n.halfDepth,e.point[1]+s.u[1]*r+a[1]*n.halfDepth],[e.point[0]+s.u[0]*r-a[0]*n.halfDepth,e.point[1]+s.u[1]*r-a[1]*n.halfDepth],[e.point[0]-a[0]*n.halfDepth,e.point[1]-a[1]*n.halfDepth]],t.coordinateScale);if(!l)continue;const c=ps(l);o=o?$t(o,c):c}}return o}function fs(e,t,i){let n=null;for(const r of e.rays)for(const e of r.continuations){const r=[-e.u[1],e.u[0]],o=[e.start[0]+e.u[0]*e.length,e.start[1]+e.u[1]*e.length],s=bs([[e.start[0]+r[0]*e.halfDepth,e.start[1]+r[1]*e.halfDepth],[o[0]+r[0]*e.halfDepth,o[1]+r[1]*e.halfDepth],[o[0]-r[0]*e.halfDepth,o[1]-r[1]*e.halfDepth],[e.start[0]-r[0]*e.halfDepth,e.start[1]-r[1]*e.halfDepth]],t.coordinateScale);if(!s)continue;const a=St(ps(s),i);Array.isArray(a)&&0!==a.length&&(n=n?$t(n,a):a)}return n}function gs(e,t,i=2*(4*e.halfDepth+2*t.epsilon)){const n=function(e,t=to){const i=new Set,n=Number.isFinite(t)&&t>=0?t:to;for(let t=0;te-t)}(e);return n.length?_s(e,t,i,n):null}function vs(e,t,i,n,r){const o=function(e){let t=null;for(const i of e){const e=ps(i);try{t=t?$t(t,e):e}catch{}}return t}(function(e,t,i=!1){if(!e)return[];const n=[];for(const r of e.nodes)for(let o=0;o1e-9)||h>=Math.PI-1e-9)continue;const d=[-s.u[1],s.u[0]],u=[-a.u[1],a.u[0]],p=[r.point[0]+d[0]*s.halfDepth,r.point[1]+d[1]*s.halfDepth],m=[r.point[0]-u[0]*a.halfDepth,r.point[1]-u[1]*a.halfDepth],_=jo(p,s.u,m,a.u);if(!_)continue;const f=Math.hypot(_[0]-r.point[0],_[1]-r.point[1]);if(!Number.isFinite(f)||f<=r.limit)continue;const g=t?Math.sqrt(Math.max(0,r.limit*r.limit-s.halfDepth*s.halfDepth)):0,v=t?Math.sqrt(Math.max(0,r.limit*r.limit-a.halfDepth*a.halfDepth)):0,y=bs([[p[0]+s.u[0]*g,p[1]+s.u[1]*g],[m[0]+a.u[0]*v,m[1]+a.u[1]*v],_],e.coordinateScale);if(y&&n.push(y),i){const t=_[0]-r.point[0],i=_[1]-r.point[1],o=Math.hypot(t,i),s=f-r.limit;if(o>e.epsilon&&s>e.epsilon){const a=Math.min(Math.max(8*e.epsilon,.05*r.halfDepth),.25*s),l=t/o,c=i/o,h=-c,d=l,u=bs([[_[0]-l*a+h*a,_[1]-c*a+d*a],[_[0]-l*a-h*a,_[1]-c*a-d*a],[_[0]+l*a,_[1]+c*a]],e.coordinateScale);u&&n.push(u)}}}return n}({...t,nodes:[e]},i,n));return o&&r?Mt(o,r):o}function ys(e,t,i,n){if(!e||!t.nodes.length)return e;let r=null;try{r=function(e){let t=null;for(const i of e.nodes){const n=gs(i,e);n&&(t=t?$t(t,n):n)}return t}(t)}catch{return e}let o=e;for(const e of t.nodes){const s=4*e.halfDepth+2*t.epsilon,a=2*s,l=[[e.point[0]-s,e.point[1]-s],[e.point[0]+s,e.point[1]-s],[e.point[0]+s,e.point[1]+s],[e.point[0]-s,e.point[1]+s]];try{let s=o;const c=vs(e,t,!1,!0,r);c&&(s=Mt(s,c));let h=_s(e,t,a);const d=vs(e,t,!0,!0,r);d&&(h=Mt(h,d)),r&&(h=$t(h,r));const u=.02*Math.min(...e.rays.map(e=>e.halfDepth));if(h=$t(h,ps([[e.point[0]-u,e.point[1]-u],[e.point[0]+u,e.point[1]-u],[e.point[0]+u,e.point[1]+u],[e.point[0]-u,e.point[1]+u]])),!h)continue;let p=St(h,ps(l));n?p=St(p,n):i&&(p=St(p,i));const m=ps(l),_=Mt(s,m),f=i?Mt(St(s,m),i):null,g=fs(e,t,m);o=$t(_,...f?[f]:[],...g?[g]:[],p)}catch{}}if(r)try{let e=r;n?e=St(e,n):i&&(e=St(e,i)),o=$t(o,e)}catch{}return o}function bs(e,t=1){if(!Array.isArray(e)||e.length<3)return null;const i=Number.isFinite(t)&&t>0?t:1,n=1e-12*Math.max(1,i),r=[];for(const t of e){if(!Array.isArray(t)||t.length<2)return null;const e=Number(t[0]),i=Number(t[1]);if(!Number.isFinite(e)||!Number.isFinite(i))return null;const o=Math.round(e/n)*n,s=Math.round(i/n)*n;if(!Number.isFinite(o)||!Number.isFinite(s))return null;r.push([Object.is(o,-0)?0:o,Object.is(s,-0)?0:s])}return Math.abs(Wo(r))>n*n?r:null}function ws(e,t,i,n){if(hs(e[0],e[1],t[0],t[1],i[0],i[1])>n)return!1;const r=i[0]-t[0],o=i[1]-t[1],s=(e[0]-t[0])*r+(e[1]-t[1])*o,a=r*r+o*o,l=n*Math.sqrt(a);return s>=-l&&s<=a+l}function ks(e,t,i){const n=[],r=[];for(let o=0;oi*i))continue;const d=i/Math.sqrt(h),u=[0,1];for(const e of t)for(const t of[e.a,e.b]){if(!ws(t,s,a,i))continue;const e=((t[0]-s[0])*l+(t[1]-s[1])*c)/h;e>d&&e<1-d&&u.push(e)}u.sort((e,t)=>e-t);const p=u.filter((e,t)=>0===t||Math.abs(e-u[t-1])>d);for(let e=0;e=3&&r.length===n.length?{poly:n,offsets:r}:null}function xs(e,t,i,n,r,o,s,a){const l=(e||[]).map(Et).filter(e=>!!e&&e.length>=3);if(!l.length)return null;let c=$t(ps(l[0]));for(let e=1;e"outer"===e.kind&&e.half>0),u=4*Lo(n,s),p=a||ts(h,u,s);let m=null;for(const e of function(e){const t=[];for(const i of Array.isArray(e)?e:[])if(Array.isArray(i))for(const e of i){if(!Array.isArray(e)||e.length<4)continue;const i=e.slice(0,-1).map(e=>[e[0],e[1]]);i.length>=3&&t.push(i)}return t}(c)){const t=ks(e,d,u);if(!t||!t.offsets.some(e=>e>0))continue;const i=As(t.poly,t.offsets,p),n=Go(t.poly,t.offsets,p);if(!i||!n)continue;const r=Mt(ps(i),ps(n));m=m?$t(m,r):r}return{centre:c,shell:m||[]}}function $s(e,t,i,n,r,o,s=1){try{const a=xs(e,t,i,n,r,o,s,rs(e,t,i,n,r,o,s));if(!a)return[];return a.shell?.length?$t(a.centre,a.shell):a.centre}catch{return null}}function Ss(e){if(!e)return"";let t="";for(const i of e)if(Array.isArray(i))for(const e of i){if(!Array.isArray(e)||e.length<4)continue;const i=e.slice(0,e.length-1);i.length<3||(t+=(t?" ":"")+Ko(i.map(e=>[e[0],e[1]])))}return t}const Ms=15;function Rs(e,t,i){const n=e?.length||0;if(n<3||t?.length!==n)return!1;const r=e[(i-1+n)%n],o=e[i],s=e[(i+1)%n],a=[r[0]-o[0],r[1]-o[1]],l=[s[0]-o[0],s[1]-o[1]],c=Math.hypot(a[0],a[1]),h=Math.hypot(l[0],l[1]);if(!(c>1e-9&&h>1e-9))return!1;const d=Math.max(-1,Math.min(1,(a[0]*l[0]+a[1]*l[1])/(c*h))),u=Math.acos(d);if(!(u>1e-9))return!1;if(u>=Ms*Math.PI/180)return!1;const p=Math.max(0,t[(i-1+n)%n]),m=Math.max(0,t[i]);if(!(p>0||m>0))return!1;const _=Math.sin(u);if(!(_>1e-12))return!1;const f=(m+p*d)/_,g=(p+m*d)/_;return Number.isFinite(f)&&Number.isFinite(g)&&f>0&&g>0&&fe>0))continue;for(const e of l.offsets)e>0&&(u=Math.max(u,2*e));const c=As(l.poly,l.offsets,d),p=Go(l.poly,l.offsets,d);c&&h.push({outset:c,inset:p})}for(const e of l){const t=e.map(e=>e[0]),i=e.map(e=>e[1]);if(t.length){const n=Math.min(Math.max(...t)-Math.min(...t),Math.max(...i)-Math.min(...i)),r=Math.min(...e.map((t,i)=>{const n=e[(i+1)%e.length];return Math.hypot(n[0]-t[0],n[1]-t[1])}));u=Math.max(u,e.length>16?n:r)}}const p=function(e,t,i,n,r,o,s,a){if(!t?.length||!i?.length)return[];const l=4*Lo(n,s),c=new Map;for(const a of ss(e,t,i,n,r,o,s))!a.open&&a.half>0&&!c.has(a.key)&&c.set(a.key,a);const h=[...c.values()];if(h.length<2)return[];const d=a||ts(h,l),u=[];for(const e of i)for(const t of[[e[0],e[1]],[e[2],e[3]]])u.some(e=>Math.hypot(e[0]-t[0],e[1]-t[1])<=l)||u.push(t);const p=[],m=(e,t)=>{let i=0,n=0;if(Math.hypot(e.a[0]-t[0],e.a[1]-t[1])<=l)i=e.b[0]-e.a[0],n=e.b[1]-e.a[1];else{if(!(Math.hypot(e.b[0]-t[0],e.b[1]-t[1])<=l))return null;i=e.a[0]-e.b[0],n=e.a[1]-e.b[1]}const r=Math.hypot(i,n);return r>l?[i/r,n/r]:null};for(const e of u){const t=h.map(t=>({iv:t,u:m(t,e)})).filter(e=>!!e.u);for(let i=0;i0?[e.slice(),u,_,m]:[e.slice(),m,_,u];else{if(!g)continue;{const t=[-r.u[1],r.u[0]],i=[-o.u[1],o.u[0]],n=s<0?1:-1,a=[e[0]+t[0]*r.iv.half*n,e[1]+t[1]*r.iv.half*n],l=[e[0]-i[0]*o.iv.half*n,e[1]-i[1]*o.iv.half*n];y=s>0?[e.slice(),a,l]:[e.slice(),l,a]}}Math.abs(Wo(y))>l*l&&p.push(y)}}return p}(e,t,i,r,o,s,a,d),m=n.length?Ps(e,t,i,r,o,s,a):null;let _="exterior";try{const v=xs(e,t,i,r,o,s,a,d);_="paper";const y=v?v.shell?.length?$t(v.centre,v.shell):v.centre:[],b=e=>{const t=ps(e.outset);if(!e.inset)return t;const i=ps(e.inset);return Mt(t,i)};_="room-rings";let w=null;for(const e of h)try{const t=b(e);w=w?$t(w,t):t}catch{}if(_="edge-bodies",v)for(const n of zs(e,t,i,r,o,s,a))try{const e=St(ps(n.quad),v.centre);w=w?$t(w,e):e}catch{}_="junctions",w=function(e,t,i=1,n=$t){let r=e;for(const e of t||[]){const t=bs(e,i);if(t)try{const e=ps(t);r=r?n(r,e):e}catch{}}return r}(w,p,a),_="facade-clip",w&&v&&(w=St(w,v.centre)),_="exterior-shell";const k=[];let x=0;if(v?.shell?.length)if(w)try{const e=$t(w,v.shell);if(!ms(e))throw new Error("invalid shell union");w=e}catch{if(!ms(w)||!ms(v.shell))throw new Error("invalid shell");k.push({id:"exterior-shell",geom:v.shell}),x++}else w=v.shell;if(_="multi-wall-trim",w&&d.nodes.length){const e=e=>e.rays.some(e=>e.supports.some(e=>e.length<2*e.halfDepth)),t=d.nodes.filter(e);if(t.length){w=ys(w,{...d,nodes:t},v?.centre,y)}}if(_="junction-corners",d.nodes.length){const n=is(d),l=function(e,t,i,n,r,o,s,a){try{const l=xs(e,t,i,n,r,o,s,{epsilon:a.epsilon,coordinateScale:a.coordinateScale,nodes:[],index:new Map});return l?l.shell?.length?$t(l.centre,l.shell):l.centre:null}catch{return null}}(e,t,i,r,o,s,a,d);for(const e of n.fans)try{let t=[ps(e)];if(l&&(t=St(t,l)),!t?.length)continue;w=w?$t(w,t):t}catch{}f=w,g=Math.max(d.epsilon,1e-9)**2,w=Array.isArray(f)?f.map(e=>{if(!Array.isArray(e)||!e.length)return e;const[t,...i]=e;return Math.abs(Wo(t||[]))<=g?null:[t,...i.filter(e=>Math.abs(Wo(e||[]))>g)]}).filter(e=>!!e):f}const $=w||[],S=[...ms($)?[{id:"room-primary",geom:$}]:[],...k.map((e,t)=>({id:`room-isolated-${t}`,geom:e.geom}))];_="openings";for(const e of n){if(!(e.length>0))continue;const t=Is(m,e,!0);if(!t.negative&&!t.positive)continue;const i=e.angle*Math.PI/180,n=Math.cos(i),o=Math.sin(i),s=-o,l=n,c=e.length/2,h=1.25*Math.max(u,r*a),d=[[e.x-n*c-s*h,e.y-o*c-l*h],[e.x+n*c-s*h,e.y+o*c-l*h],[e.x+n*c+s*h,e.y+o*c+l*h],[e.x-n*c+s*h,e.y-o*c+l*h]];w&&(w=Mt(w,ps(d)));for(const e of k)e.geom=Mt(e.geom,ps(d))}_="extras";const M=[];let R=0;const T=c.mergeExtra||((e,t)=>e?$t(e,t):t);for(let e=0;ee.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]))||Math.abs(li(t))<=1e-9){R++;continue}const i=[ps(t)];try{const t=T(w,i,e);if(!ms(t))throw new Error("invalid extra union");w=t}catch{R++,ms(i)&&M.push({id:`extra-${e}`,geom:i})}}M.push(...k),M.sort((e,t)=>Ss(e.geom).localeCompare(Ss(t.geom)));const C=w||[];return{status:R||x?"degraded-extra":"ok",geom:C,components:[...ms(C)?[{id:"primary",geom:C}]:[],...M.map((e,t)=>({...e,id:`isolated-${t}`}))],roomGeom:$,roomComponents:S,paperGeom:y,depthUnits:u,openingPadUnits:1.25*Math.max(u,r*a),openingIndex:m,multiWallNodes:d,degradedExtraCount:R+x}}catch{return c.onCoreFailure?.(_),{status:"failed-core",geom:[],components:[],roomGeom:[],paperGeom:[],roomComponents:[],depthUnits:u,openingIndex:null,multiWallNodes:d,degradedExtraCount:0}}var f,g}function Cs(e,t,i,n=[],r,o,s,a=1,l=[],c={}){if(!t?.length&&!l.length)return null;return Ds(Ts(e,t,i,n,r,o,s,a,l,c))}function Ds(e){if("failed-core"===e.status||"not-applicable"===e.status)return null;const t=e.components.map(e=>({id:e.id,d:Ss(e.geom),fillRule:"evenodd"})).filter(e=>!!e.d),i=t[0]?.d||"",n=Ss(e.paperGeom);if(t.length){const r={status:e.status,d:i,paths:t,components:e.components,roomGeom:e.roomGeom,multiWallNodes:e.multiWallNodes,paperD:n,depthUnits:e.depthUnits,fillRule:"evenodd"};return Object.defineProperties(r,{roomComponents:{value:e.roomComponents||[],enumerable:!1},openingIndex:{value:e.openingIndex,enumerable:!1},openingPadUnits:{value:e.openingPadUnits,enumerable:!1}}),r}return null}function zs(e,t,i,n,r,o,s=1){if(!t?.length)return[];const a=new Set,l=[];for(const c of e||[]){if(!c?.id)continue;const h=os(e,c.id,t,i,n,r,o,s);if(!h)continue;const d=h.poly;for(let e=0;e0))continue;const p=mo(t,i,n,s);if(a.has(p))continue;a.add(p);const m=ao(u,r,o),[_,f]=qo(d,e),g=-_,v=-f,y=m/2,b=[[t[0]+g*y,t[1]+v*y],[i[0]+g*y,i[1]+v*y],[i[0]+_*y,i[1]+f*y],[t[0]+_*y,t[1]+f*y]];l.push({key:p,kind:c,cm:u,quad:b,a:[t[0],t[1]],b:[i[0],i[1]],depthUnits:m})}}return l}function As(e,t,i){const n=e?.length||0;if(n<3||t.length!==n)return null;if(t.every(e=>!(e>0)))return e.map(e=>[e[0],e[1]]);Yo(e),t.slice().reverse();const r=[];for(let o=0;o0||l>0)){r.push([e[o][0],e[o][1]]);continue}if(a>0!=l>0){const t=e[o],i=a>0?[t[0]-c*a,t[1]-h*a]:[t[0],t[1]],n=l>0?[t[0]-d*l,t[1]-u*l]:[t[0],t[1]];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}if(Bo(w,k)){const t=e[o],i=[t[0]-c*a,t[1]-h*a],n=[t[0]-d*l,t[1]-u*l];r.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&r.push(n);continue}const S=jo(x,w,$,k),M=Math.max(a,l,1e-9),R=ns(i,e[o])?.limit??4*M;if(S){const i=Math.hypot(S[0]-e[o][0],S[1]-e[o][1]);if(Number.isFinite(i)&&i<=R){r.push(S);continue}if(Rs(e,t,o)){r.push([e[o][0],e[o][1]]);continue}}a>0&&r.push([e[o][0]-c*a,e[o][1]-h*a]),l>0&&r.push([e[o][0]-d*l,e[o][1]-u*l])}return r.length>=3?r:null}function Ps(e,t,i,n,r,o,s=1){const a=[];for(const l of e||[]){if(!l?.id)continue;const c=os(e,l.id,t,i,n,r,o,s);if(!c)continue;const h=Math.abs(li(c.poly));for(let e=0;e[Math.max(t,e.x0),Math.min(i,e.x1)]).filter(e=>e[1]-e[0]>n).sort((e,t)=>e[0]-t[0]||e[1]-t[1]);if(!r.length)return{coverage:0,full:!1};let o=r[0][0],s=r[0][1],a=0,l=o<=t+n;for(let e=1;e=i-n,{coverage:a,full:l}}function Fs(e,t){return Number(t.full)-Number(e.full)||e.faceDistance-t.faceDistance||e.area-t.area||e.roomId.localeCompare(t.roomId)}function Is(e,t,i=!1){const n=Number(t?.x),r=Number(t?.y),o=Number(t?.angle),s=Number(t?.length);if(!([n,r,o,s].every(Number.isFinite)&&s>0))return{negative:null,positive:null};const a=o*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=-c,d=l,u=s/2,p=Math.max(1e-9,e.adjacencyEps),m=new Map;let _=0;for(const t of e.edges){if(i&&!(t.half>0))continue;if(!xo(t.a,t.b,o))continue;const[e,s]=uo(t.a,t.b);if(Math.abs((n-t.a[0])*s-(r-t.a[1])*e)>p)continue;const a=(t.a[0]-n)*l+(t.a[1]-r)*c,f=(t.b[0]-n)*l+(t.b[1]-r)*c,g=Math.max(-u,Math.min(a,f)),v=Math.min(u,Math.max(a,f));if(v-g<=p)continue;const y=t.inward[0]*h+t.inward[1]*d>=0?1:-1,b=((t.a[0]+t.b[0])/2-n)*h+((t.a[1]+t.b[1])/2-r)*d,w=Math.abs(b+y*t.half),k=`${y}|${t.roomId}`,x={x0:g,x1:v,half:t.half,cm:t.cm,key:t.key,axis:[e,s]},$=m.get(k);$?($.pieces.push(x),$.faceDistance=Math.min($.faceDistance,w)):m.set(k,{roomId:t.roomId,side:y,order:_++,pieces:[x],faceDistance:w,area:t.area,coverage:0,full:!1})}for(const e of m.values()){const t=Os(e.pieces,-u,u,p);e.coverage=t.coverage,e.full=t.full}const f=e=>{const t=[...m.values()].filter(t=>t.side===e&&t.coverage>p);return t.sort(Fs),t[0]||null};return{negative:f(-1),positive:f(1)}}function Es(e,t){const i=Is(e,t),n=[i.negative,i.positive].filter(e=>!!e);if(!n.length)return{ox:0,oy:0,cm:0,side:-1};const r=i.negative&&i.positive?-1:n[0].side,o=t.flip_v?-r:r,s=(-1===o?i.negative:i.positive)||n[0],a=[...s.pieces].sort((e,t)=>(e.x0<=0&&e.x1>=0?0:Math.min(Math.abs(e.x0),Math.abs(e.x1)))-(t.x0<=0&&t.x1>=0?0:Math.min(Math.abs(t.x0),Math.abs(t.x1)))||t.x1-t.x0-(e.x1-e.x0)||e.key.localeCompare(t.key))[0];if(!(a.half>0&&a.cm>0))return{ox:0,oy:0,cm:0,side:o};const l=t.angle*Math.PI/180,c=-Math.sin(l),h=Math.cos(l);return{ox:c*o*a.half,oy:h*o*a.half,cm:a.cm,side:o}}function Hs(e,t){const i=1e-9,n=t.filter(e=>Number.isFinite(e.x0)&&Number.isFinite(e.x1)&&Number.isFinite(e.half)&&e.x1>e.x0&&e.half>0);if(!n.length)return"";const r=n.flatMap(e=>[e.x0,e.x1]).sort((e,t)=>e-t),o=[];for(const e of r){const t=o[o.length-1];(void 0===t||e>t+i)&&o.push(e)}const s=[];for(let e=0;e+1t+i))continue;const a=(t+r)/2,l=n.reduce((e,t)=>a>=t.x0-i&&a<=t.x1+i?Math.max(e,t.half):e,0);if(!(l>0))continue;const c=s[s.length-1];c&&t<=c.x1+i&&Math.abs(l-c.half)<=i?c.x1=r:s.push({x0:t,x1:r,half:l})}const a=[];for(const e of s){const t=a[a.length-1],n=t?.[t.length-1];n&&e.x0<=n.x1+i?(e.x0=n.x1,t.push(e)):a.push([e])}return a.map(t=>{const i=t[0],n=t[t.length-1],r=Math.min(.25*Math.min(...t.map(e=>e.half)),.75),o=-e*r,s=[];if(1===e){s.push(`M ${i.x0} ${o} L ${n.x1} ${o}`);for(let e=t.length-1;e>=0;e--){const i=t[e];s.push(`L ${i.x1} ${i.half} L ${i.x0} ${i.half}`)}}else{s.push(`M ${n.x1} ${o} L ${i.x0} ${o}`);for(const e of t)s.push(`L ${e.x0} ${-e.half} L ${e.x1} ${-e.half}`)}return s.push("Z"),s.join(" ")}).join(" ")}function Ns(e,t,i,n){if(!n)return i;const r=e.angle*Math.PI/180,o=Math.cos(r),s=Math.sin(r),a=[],l=1e-9;for(const r of i){const[i,c]=r.axis,h=o*i+s*c;if(Math.abs(h)<=l)continue;const d=e.x*i+e.y*c,u=d+h*r.x0,p=d+h*r.x1,m=Math.min(u,p),_=Math.max(u,p),f=`${r.key}|${t}`,g=n.get(f)||[];let v=[[m,_]];for(const[e,t]of g){const i=[];for(const[n,r]of v)t<=n+l||e>=r-l?i.push([n,r]):(e>n+l&&i.push([n,Math.min(r,e)]),te[0]-t[0]||e[1]-t[1]),b=[];for(const e of y){const t=b[b.length-1];t&&e[0]<=t[1]+l?t[1]=Math.max(t[1],e[1]):b.push([e[0],e[1]])}n.set(f,b)}return a}function Ls(e,t){const i=new Map;return t.map(t=>function(e,t,i){const n=Is(e,t,!0),r=n.negative,o=n.positive;if(!r&&!o)return null;let s;if(r&&o)s=[{candidate:r,side:-1},{candidate:o,side:1}];else{const e=r||o;s=[{candidate:e,side:-1},{candidate:e,side:1}]}const a=s.map(({candidate:e,side:n})=>({candidate:e,side:n,pieces:Ns(t,n,e.pieces,i)})),l=a.map(({candidate:e,side:t,pieces:i})=>({side:t,roomId:e.roomId,d:Hs(t,i)})),c=a.flatMap(({pieces:e})=>e);if(!c.length)return null;const h=Math.max(...c.map(e=>e.half));return{faces:l,minY:-h,maxY:h,wallKey:[...new Set(c.map(e=>e.key))].sort().join("|")}}(e,t,i))}const Ws=150,qs=1e-6;function Bs(e){return Number.isFinite(e)?Math.max(1,Math.min(Ws,e)):1}function js(e){return((Number.isFinite(Number(e))?Number(e):0)%90+90)%90}const Us=e=>{const t=e.map(e=>[e[0],e[1]]);return!t.length||t[0][0]===t[t.length-1][0]&&t[0][1]===t[t.length-1][1]||t.push([...t[0]]),[t]},Vs=(e,t)=>e[0]===t[0]&&e[1]===t[1];function Gs(e,t=1e-6){const i=Number.isFinite(t)&&t>0?t:qs,n=[];for(const t of e||[]){if(!Array.isArray(t)||t.length<2)return null;const e=Number(t[0]),r=Number(t[1]);if(!Number.isFinite(e)||!Number.isFinite(r))return null;const o=Math.round(e/i)*i,s=Math.round(r/i)*i;if(!Number.isFinite(o)||!Number.isFinite(s))return null;const a=[Object.is(o,-0)?0:o,Object.is(s,-0)?0:s];n.length&&Vs(n[n.length-1],a)||n.push(a)}return n.length>1&&Vs(n[0],n[n.length-1])&&n.pop(),n.length<3||new Set(n.map(e=>`${e[0]},${e[1]}`)).size<3?null:li(n)>i*i?n:null}function Ks(e){const t=[];for(const i of e||[])for(const e of i||[]){const i=(e||[]).filter(e=>Array.isArray(e)&&e.length>=2);i.length<4||t.push(`M ${i.slice(0,-1).map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`)}return t.join(" ")}function Ys(e,t,i,n,r){return zo({a:e,b:t,halfDepth:ao(i,n,r)/2})}function Xs(e,t,i){const n=Number(t)>0?Number(t):5,r=Bs(e.cm)/n*i,o=e.center[0],s=e.center[1];if("circle"===e.shape){const e=r/2;return Array.from({length:96},(t,i)=>{const n=i/96*Math.PI*2;return[o+Math.cos(n)*e,s+Math.sin(n)*e]})}const a=r/2,l=js(e.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l);return[[-a,-a],[a,-a],[a,a],[-a,a]].map(([e,t])=>[o+e*c-t*h,s+e*h+t*c])}function Zs(e,t){const i=Number.isFinite(t)?Math.max(0,Math.min(1,t)):0,n=(e.a[0]+e.b[0])/2,r=(e.a[1]+e.b[1])/2,o=(e.b[0]-e.a[0])*i/2,s=(e.b[1]-e.a[1])*i/2;return{...e,a:[n-o,r-s],b:[n+o,r+s]}}function Js(e,t,i=1e-9){if(!t.length)return[e];let n=[Us(e)];try{for(const e of t){const t=e.b[0]-e.a[0],r=e.b[1]-e.a[1],o=Math.hypot(t,r);if(!(o>i))continue;const s=t/o,a=r/o,l=-a,c=s,h=1.25*Math.max(Number(e.depth)||0,4*i),d=Math.max(2*i,1e-9*o),u=[[e.a[0]-s*d-l*h,e.a[1]-a*d-c*h],[e.b[0]+s*d-l*h,e.b[1]+a*d-c*h],[e.b[0]+s*d+l*h,e.b[1]+a*d+c*h],[e.a[0]-s*d+l*h,e.a[1]-a*d+c*h]];n=Mt(n,Us(u))}return la(n)}catch{return[e]}}function Qs(e,t){try{const i=Mt([[...e.map(e=>[e[0],e[1]]),[e[0][0],e[0][1]]]],[[...t.map(e=>[e[0],e[1]]),[t[0][0],t[0][1]]]]);let n=null,r=0;for(const e of i||[]){const t=e?.[0]||[],i=Math.abs(li(t));t.length>=4&&i>r&&(r=i,n=t.slice(0,-1).map(e=>[e[0],e[1]]))}return n}catch{return null}}function ea(e,t,i,n=Math.max(2e-4*i,1e-9),r=[]){const o=[],s=[],a=new Map;for(const e of r){const t=a.get(e.hostId)||[];t.push(e),a.set(e.hostId,t)}const l=[],c=[],h=[];for(const n of e.room_drafts||[])for(let e=0;e+10))continue;const a={a:n.points[e],b:n.points[e+1],halfDepth:s},c=zo(a);c&&(o.push(a),l.push(c))}const d=[];for(const n of e.partitions||[]){const e={a:n.a,b:n.b,halfDepth:ao(n.cm,t,i)/2};if(!(e.halfDepth>0))continue;const r=zo(e);r&&(s.push(e),c.push(r),d.push({id:n.id,body:r}))}const u=[...o,...s];for(const{segmentIndex:e,wedge:t}of Io(u,n)){const i=e=o.length&&(d[i.at].body=n))}for(const e of d)h.push(...Js(e.body,a.get(e.id)||[],n));const p=(e.wall_columns||[]).map(e=>Xs(e,t,i)),m=Eo([...o,...s],n).flatMap(e=>Js(e,r,n));return{drafts:l,partitions:c,columns:p,patches:m,all:[...l,...h,...m,...p]}}function ta(e){try{const t=e.map(e=>Gs(e)).filter(e=>!!e).map(e=>Us(e));return t.length?$t(t[0],...t.slice(1)):null}catch{return null}}const ia=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`;function na(e){const t=[];for(const i of e||[]){if(sa([i])<=1e-6)continue;const e=[];for(const t of i||[]){const i=(t||[]).filter(e=>Array.isArray(e)&&e.length>=2);i.length<4||e.push(ia(i.slice(0,-1)))}e.length&&t.push(e.join(" "))}return t}function ra(e,t,i={}){const n=ta(e.filter(e=>e.length>=3)),r=t.some(e=>!Gs(e))?null:ta(t);if(!n)return[];if(r)try{return na(St(n,r))}catch{}return function(e,t,i){let n=null;for(let r=0;r=4&&t.push(e.slice(0,-1).map(e=>[e[0],e[1]]));return t}function la(e){const t=[];for(const i of e||[]){const e=i?.[0];e?.length>=4&&t.push(e.slice(0,-1).map(e=>[e[0],e[1]]))}return t}function ca(e,t){let i=!1;for(let n=0,r=t.length-1;ne[1]!=l>e[1]&&e[0]<(a-o)*(e[1]-s)/(l-s||1e-12)+o&&(i=!i)}return i}function ha(e,t,i){return function(e,t){for(const i of t||[]){const t=i?.[0];if(!t?.length||!ca(e,t))continue;let n=!1;for(let t=1;tca(e,t))}function da(e,t,i){if(Math.hypot(e.center[0]-t.center[0],e.center[1]-t.center[1])>i)return!1;if(Math.abs(Bs(e.cm)-Bs(t.cm))>1e-6)return!1;if(e.shape!==t.shape)return!0;if("circle"===e.shape||"circle"===t.shape)return!0;const n=Math.abs(js(e.angle)-js(t.angle));return Math.min(n,90-n)<=1e-6}const ua=2.54;function pa(e){const t="number"==typeof e?e:NaN;return!Number.isFinite(t)||t<=0||5===t?1:5/t}function ma(e,t){return e*pa(t)}function _a(e){return e?ua:1}function fa(e,t){if(!t)return String(e);const i=e/ua;return String(Math.round(1e6*i)/1e6)}function ga(e,t){return t?e*ua:e}const va=1e3;function ya(e,t){const i=Number(e),n=Number.isFinite(i)&&i>0?i:1,r=n>=1?t:t*n,o=n>=1?t/n:t;return{x:(t-r)/2,y:(t-o)/2,w:r,h:o}}const ba=.01,wa=100;function ka(e,t=1e3){const i=ya(e?.plan_aspect,t),n=Number(e?.plan_scale),r=Number.isFinite(n)&&n>0?Math.min(wa,Math.max(ba,n)):1,o=Number(e?.plan_scale_x),s=Number(e?.plan_scale_y),a=Number.isFinite(o)&&o>0?Math.min(wa,Math.max(ba,o)):r,l=Number.isFinite(s)&&s>0?Math.min(wa,Math.max(ba,s)):r,c=Number(e?.plan_x),h=Number(e?.plan_y),d=nr(e?.plan_angle);return{x:i.x+(Number.isFinite(c)?Pa(c):0)*t,y:i.y+(Number.isFinite(h)?Pa(h):0)*t,w:i.w*a,h:i.h*l,...d?{angle:d}:{}}}function xa(e){if(null==e.x||null==e.y)return{x:e.x,y:e.y,w:e.w,h:e.h};const t=Number(e.w)||0,i=Number(e.h)||0;return{x:t<0?e.x+t:e.x,y:i<0?e.y+i:e.y,w:Math.abs(t),h:Math.abs(i)}}function $a(e){return e&&Array.isArray(e.spaces)?e.spaces.map(e=>{const t=va,i=function(e){return Array.isArray(e)&&4===e.length&&e.every(e=>Number.isFinite(e))&&e[2]>1e-6&&e[3]>1e-6?e:[0,0,1,1]}(e.view_box);return{id:e.id,title:e.title,cellCm:Number.isFinite(Number(e.cell_cm))&&Number(e.cell_cm)>0?Number(e.cell_cm):5,vb:[i[0]*va,i[1]*t,i[2]*va,i[3]*t],bg:e.plan_url?{href:_n(e.plan_url),...ka(e,va)}:null,rooms:(e.rooms||[]).map(e=>{const i={...e,...xa(e)};return{id:i.id,name:i.name,area:i.area??null,open_to:i.open_to||void 0,settings:i.settings||void 0,x:null!=i.x?i.x*va:void 0,y:null!=i.y?i.y*t:void 0,w:null!=i.w?i.w*va:void 0,h:null!=i.h?i.h*t:void 0,poly:i.poly?i.poly.map(e=>[e[0]*va,e[1]*t]):void 0,wall_ids:Array.isArray(i.wall_ids)?[...i.wall_ids]:void 0}}),wall_segments:(e.wall_segments||[]).map(e=>({...e,id:String(e.id),a:[Number(e.a?.[0])*va,Number(e.a?.[1])*t],b:[Number(e.b?.[0])*va,Number(e.b?.[1])*t],cm:Number(e.cm)})),room_drafts:(e.room_drafts||[]).map(e=>({id:e.id,points:(e.points||[]).map(e=>[e[0]*va,e[1]*t]),segments:(e.segments||[]).map(e=>({..."string"==typeof e.id&&e.id?{id:e.id}:{},cm:Number(e.cm)}))})),partitions:(e.partitions||[]).map(e=>({id:e.id,a:[e.a[0]*va,e.a[1]*t],b:[e.b[0]*va,e.b[1]*t],cm:Number(e.cm)})),wall_columns:(e.wall_columns||[]).map(e=>({id:e.id,shape:"circle"===e.shape?"circle":"square",center:[e.center[0]*va,e.center[1]*t],cm:Number(e.cm),..."circle"===e.shape?{}:{angle:js(e.angle)}}))}}):[]}const Sa=5e3,Ma=Sa*va,Ra=240,Ta=va/Ra,Ca=1/Ra;function Da(e){if(!Number.isFinite(e))return e;const t=Math.round(e*Ra/va)*va/Ra;return Math.abs(t-e)<=1e-9*Ta?e:t}function za(e){return{x:Da(e.x),y:Da(e.y)}}function Aa(e){return Number.isFinite(e)?Math.min(Ma,Math.max(-Ma,e)):0}function Pa(e){return Number.isFinite(e)?Math.min(Sa,Math.max(-Sa,e)):0}const Oa=1/3,Fa=200;function Ia(e){return"house"===e?"house":"content"}function Ea(e){let t=1/0,i=1/0,n=-1/0,r=-1/0;for(const o of e){const e=Number(o[0]),s=Number(o[1]);Number.isFinite(e)&&Number.isFinite(s)&&(en&&(n=e),s>r&&(r=s))}return t>n?null:{minX:t,minY:i,maxX:n,maxY:r}}function Ha(e){const t=[],i=e=>{if(Array.isArray(e))if(e.length>=2&&Number.isFinite(Number(e[0]))&&Number.isFinite(Number(e[1])))t.push([Number(e[0]),Number(e[1])]);else for(const t of e)i(t)};return i(e),Ea(t)}function Na(e,t){const i=Number.isFinite(t)&&t>0?t:0;return{minX:e.minX-i,minY:e.minY-i,maxX:e.maxX+i,maxY:e.maxY+i}}function La(e){return e.poly&&e.poly.length?Ea(e.poly):null==e.x||null==e.y?null:Ea([[e.x,e.y],[e.x+(e.w||0),e.y+(e.h||0)]])}function Wa(e,t){const i=[];for(const t of e.rooms||[]){const e=La(t);e&&i.push(e)}if(e.bg){const t=Ea(dr(e.bg));t&&i.push(t)}for(const e of t||[])if(Array.isArray(e)){const t=Ea([e]);t&&i.push(t)}else i.push(e);return i}const qa=e=>{if(!e.length)return 0;const t=[...e].sort((e,t)=>e-t),i=t.length>>1;return t.length%2?t[i]:(t[i-1]+t[i])/2},Ba=e=>{let t=1/0,i=1/0,n=-1/0,r=-1/0;for(const o of e)o.minXn&&(n=o.maxX),o.maxY>r&&(r=o.maxY);return t>n||i>r?null:{x:t,y:i,w:n-t,h:r-i}};function ja(e,t){let{x:i,y:n,w:r,h:o}=e;r<30&&(i=i+r/2-100,r=Fa),o<30&&(n=n+o/2-100,o=Fa);const s="number"==typeof t?{top:t,right:t,bottom:t,left:t}:t,a=Math.max(r,o),l=a*s.top,c=a*s.right,h=a*s.bottom,d=a*s.left;return{x:i-d,y:n-l,w:r+d+c,h:o+l+h}}function Ua(e,t={}){const i=t.pad??.05,n=t.k??10,r=t.minSpread??50,o=e.filter(e=>Number.isFinite(e.minX)&&Number.isFinite(e.minY)&&Number.isFinite(e.maxX)&&Number.isFinite(e.maxY)&&Math.abs(e.minX)<=Ma&&Math.abs(e.maxX)<=Ma&&Math.abs(e.minY)<=Ma&&Math.abs(e.maxY)<=Ma);if(!o.length)return{core:null,all:null,outliers:0};const s=Ba(o);if(o.length<4){const e=ja(s,i);return{core:e,all:e,outliers:0}}const a=o.map(e=>(e.minX+e.maxX)/2),l=o.map(e=>(e.minY+e.maxY)/2),c=qa(a),h=qa(l),d=o.map((e,t)=>Math.max(Math.abs(a[t]-c),Math.abs(l[t]-h))),u=Math.max(((e,t)=>e.length?e[Math.min(e.length-1,Math.max(0,Math.round(t*(e.length-1))))]:0)([...d].sort((e,t)=>e-t),.75),r),p=d.map(e=>e>n*u),m=p.filter(Boolean).length,_=m&&m<=o.length*Oa?o.filter((e,t)=>!p[t]):o;return{core:ja(Ba(_)||s,i),all:ja(s,i),outliers:_===o?0:m}}function Va(e){return Ua(e,{pad:0}).all}function Ga(e,t,i=.05){const n=Ua(Wa(e,t),{pad:i});if(n.core)return n.core;const r=e.vb&&4===e.vb.length&&e.vb[2]>0&&e.vb[3]>0?e.vb:[0,0,va,va];return{x:r[0],y:r[1],w:r[2],h:r[3]}}function Ka(e){const t=Ga(e);return{x:t.x+t.w/2,y:t.y+t.h/2}}function Ya(e){const t=[];for(const i of e.rooms||[]){const e=La(i);e&&t.push(e)}const i=Ua(t,{pad:0}).core,n=va*pa(e.cellCm??5);return i?Math.max(n,Math.min(Ma,Math.max(i.w,i.h))):n}function Xa(e,t,i,n=1){const r=Number(i),o=Number.isFinite(n)&&n>0?n:1;return!Number.isFinite(r)||r<=0?e*o:e*Ya(t)*o/r}const Za=[1,2,5,10,20,50,100,200,500,1e3];function Ja(e,t,i=7){if(!(e>0&&t>0&&Number.isFinite(t)))return null;const n=Za.find(n=>e*n*t>=i);if(void 0===n)return null;const r=Za.find(e=>e>=5*n)??5*n;return{fine:n,coarse:r}}function Qa(e){if(e.poly&&e.poly.length){const t=e.poly.map(e=>e[0]),i=e.poly.map(e=>e[1]),n=Math.min(...t),r=Math.min(...i);return{x:n,y:r,w:Math.max(...t)-n,h:Math.max(...i)-r}}return{x:e.x??0,y:e.y??0,w:e.w??0,h:e.h??0}}function el(e,t,i,n,r){const o=t[e.id];return o&&o.s===e.space?{x:o.x*va,y:o.y*va}:n[e.id]?n[e.id]:za(Ka(r))}function tl(e,t,i,n){const r=i["rl_"+(e.id||"")];if(r&&r.s===t)return{x:r.x*va,y:r.y*va};const o=function(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}(e);return za({x:o[0],y:o[1]})}const il=["furniture","appliance","sanitary","other"],nl=[{id:"sofa",group:"furniture",category:"sofa",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",category:"armchair",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",category:"coffee_table",w:110,h:60,g:[["r",0,0,1,1],["r",.08,.14,.84,.72]]},{id:"table_dining",group:"furniture",category:"dining_table",w:140,h:80,g:[["r",0,0,1,1],["r",.06,.11,.88,.78]]},{id:"table_round",group:"furniture",category:"dining_table",w:120,h:120,g:[["e",.5,.5,.5,.5],["e",.5,.5,.41,.41]]},{id:"chair",group:"furniture",category:"chair",w:45,h:45,g:[["r",0,0,1,.18],["r",.06,.18,.88,.8]]},{id:"desk",group:"furniture",category:"work_table",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",category:"bed",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",category:"bed",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",category:"nightstand",w:45,h:40,g:[["r",0,0,1,1],["r",.12,.14,.76,.33],["r",.12,.53,.76,.33]]},{id:"wardrobe",group:"furniture",category:"wardrobe",w:100,h:60,g:[["r",0,0,1,1],["l",0,.72,1,.72],["l",.5,.72,.5,1]]},{id:"bookshelf",group:"furniture",category:"wardrobe",w:80,h:30,g:[["r",0,0,1,1],["l",.34,0,.34,1],["l",.67,0,.67,1]]},{id:"fridge",group:"appliance",category:"fridge",w:60,h:65,g:[["r",0,0,1,1],["l",0,.36,1,.36],["l",.83,.44,.83,.64]]},{id:"stove",group:"appliance",category:"cooktop",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",category:"dishwasher",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",category:"washer",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",category:"dryer",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",category:"tv",w:120,h:30,g:[["r",0,0,1,.42],["l",.5,.42,.5,.72],["l",.3,.72,.7,.72]]},{id:"ac",group:"appliance",category:"air_conditioner",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",category:"boiler",w:45,h:45,g:[["e",.5,.5,.5,.5],["e",.5,.5,.31,.31]]},{id:"toilet",group:"sanitary",category:"toilet",w:40,h:70,g:[["r",.06,0,.88,.2],["e",.5,.58,.37,.35],["e",.5,.58,.22,.2]]},{id:"bathtub",group:"sanitary",category:"bathtub",w:170,h:75,g:[["r",0,0,1,1],["r",.05,.11,.77,.78],["e",.89,.5,.045,.1]]},{id:"shower",group:"sanitary",category:"shower",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",category:"sink",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",category:"kitchen_sink",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",category:"bidet",w:40,h:55,g:[["e",.5,.5,.44,.5],["e",.5,.5,.26,.3]]},{id:"stairs",group:"other",category:"stairs",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",category:"fireplace",w:120,h:40,g:[["r",0,0,1,1],["p",.22,1,.22,.42,.78,.42,.78,1]]},{id:"plant",group:"other",category:"plant",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",category:"rug",w:200,h:140,g:[["r",0,0,1,1],["r",.06,.09,.88,.82]]}],rl=new Set(["fridge","dishwasher","washer","dryer","ac","water_heater","shower","sink","stairs","fireplace","plant","rug"]),ol=[...[{id:"coffee_table",group:"furniture",category:"coffee_table",w:120,h:60,art:{d:"M3.84 5.884c0-1.21 1.048-2.192 2.34-2.192h107.64c1.293 0 2.34.981 2.34 2.192v48.23c0 1.212-1.047 2.193-2.34 2.193H6.18c-1.292 0-2.34-.981-2.34-2.192z M103.056 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m-93.6-35.077V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877",viewW:120,viewH:60}},{id:"coffee_table_round",group:"furniture",category:"coffee_table",w:80,h:80,art:{d:"M3.765 40a36.235 36.235 0 1 0 72.47 0 36.235 36.235 0 1 0-72.47 0 m56.26 26.96-3.843-3.843a.906.906 0 0 1 0-1.28l3.843-3.844a.906.906 0 0 1 1.281 0l3.844 3.843a.906.906 0 0 1 0 1.281l-3.844 3.844a.906.906 0 0 1-1.28 0m-.001 34.66-3.843-3.843a.906.906 0 0 1 0-1.281l3.843-3.844a.906.906 0 0 1 1.281 0l3.843 3.844a.906.906 0 0 1 0 1.28l-3.843 3.844a.906.906 0 0 1-1.281 0m-34.423-34.66-3.843-3.843a.906.906 0 0 1 0-1.281l3.843-3.844a.906.906 0 0 1 1.281 0l3.844 3.844a.906.906 0 0 1 0 1.28l-3.844 3.844a.906.906 0 0 1-1.28 0m-.001 34.66-3.843-3.843a.906.906 0 0 1 0-1.281l3.843-3.843a.906.906 0 0 1 1.281 0l3.844 3.843a.906.906 0 0 1 0 1.281l-3.844 3.843a.906.906 0 0 1-1.28 0",viewW:80,viewH:80}},{id:"coffee_table_oval",group:"furniture",category:"coffee_table",w:120,h:60,art:{d:"M3.84 30c0-14.53 12.572-26.308 28.08-26.308h56.16c15.509 0 28.08 11.778 28.08 26.308s-12.571 26.307-28.08 26.307H31.92C16.412 56.307 3.84 44.53 3.84 30 M80.592 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.077v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877M31.92 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.876h-5.616a.936.877 0 0 1-.936-.876m0 35.077v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.876h-5.616a.936.877 0 0 1-.936-.876",viewW:120,viewH:60}},{id:"coffee_table_rounded",group:"furniture",category:"coffee_table",w:120,h:60,art:{d:"M3.84 12.461c0-4.843 4.19-8.77 9.36-8.77h93.6c5.17 0 9.36 3.927 9.36 8.77v35.077c0 4.843-4.19 8.77-9.36 8.77H13.2c-5.17 0-9.36-3.927-9.36-8.77z M103.056 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m-93.6-35.077V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877",viewW:120,viewH:60}},{id:"table_dining",group:"furniture",category:"dining_table",w:160,h:90,art:{d:"M3.879 6.08c0-1.265 1.065-2.29 2.378-2.29h147.484c1.314 0 2.38 1.025 2.38 2.29v77.84c0 1.265-1.066 2.29-2.38 2.29H6.257c-1.313 0-2.378-1.025-2.378-2.29z M138.994 19.358v-5.495a.952.916 0 0 1 .951-.915h5.71a.952.916 0 0 1 .95.915v5.495a.952.916 0 0 1-.95.916h-5.71a.952.916 0 0 1-.951-.916m-.001 56.778v-5.495a.952.916 0 0 1 .952-.915h5.709a.952.916 0 0 1 .951.915v5.495a.952.916 0 0 1-.951.916h-5.71a.952.916 0 0 1-.95-.916m-125.6-56.778v-5.495a.952.916 0 0 1 .951-.916h5.71a.952.916 0 0 1 .95.916v5.495a.952.916 0 0 1-.95.915h-5.71a.952.916 0 0 1-.951-.915m-.001 56.779v-5.495a.952.916 0 0 1 .952-.916h5.709a.952.916 0 0 1 .951.916v5.495a.952.916 0 0 1-.951.915h-5.71a.952.916 0 0 1-.95-.915",viewW:160,viewH:90}},{id:"table_round",group:"furniture",category:"dining_table",w:110,h:110,art:{d:"M3.826 55a51.174 51.174 0 1 0 102.349 0A51.174 51.174 0 1 0 3.826 55 m81.005 32.303-3.947-3.947a.93.93 0 0 1 0-1.316l3.947-3.948a.93.93 0 0 1 1.316 0l3.948 3.948a.93.93 0 0 1 0 1.316l-3.948 3.947a.93.93 0 0 1-1.316 0m0 54.897-3.947-3.948a.93.93 0 0 1 0-1.316l3.947-3.947a.93.93 0 0 1 1.316 0l3.948 3.947a.93.93 0 0 1 0 1.316L82.32 87.2a.93.93 0 0 1-1.316 0M27.04 32.304l-3.947-3.948a.93.93 0 0 1 0-1.315l3.948-3.948a.93.93 0 0 1 1.315 0l3.948 3.948a.93.93 0 0 1 0 1.315l-3.948 3.948a.93.93 0 0 1-1.315 0m-.001 54.604-3.947-3.948a.93.93 0 0 1 0-1.316l3.948-3.947a.93.93 0 0 1 1.315 0l3.948 3.947a.93.93 0 0 1 0 1.316l-3.948 3.948a.93.93 0 0 1-1.315 0",viewW:110,viewH:110}},{id:"table_dining_oval",group:"furniture",category:"dining_table",w:180,h:100,art:{d:"M3.892 50c0-25.51 21.418-46.19 47.838-46.19h76.54c26.42 0 47.839 20.68 47.839 46.19S154.69 96.19 128.27 96.19H51.73C25.31 96.19 3.892 75.51 3.892 50 M122.53 19.514V13.97a.957.924 0 0 1 .956-.924h5.74a.957.924 0 0 1 .958.924v5.543a.957.924 0 0 1-.957.924h-5.74a.957.924 0 0 1-.958-.924m.001 66.516v-5.543a.957.924 0 0 1 .957-.924h5.74a.957.924 0 0 1 .957.924v5.543a.957.924 0 0 1-.957.924h-5.74a.957.924 0 0 1-.957-.924M49.816 19.515v-5.543a.957.924 0 0 1 .957-.924h5.74a.957.924 0 0 1 .958.924v5.542a.957.924 0 0 1-.957.924h-5.74a.957.924 0 0 1-.958-.924m0 66.516v-5.543a.957.924 0 0 1 .957-.924h5.74a.957.924 0 0 1 .957.924v5.543a.957.924 0 0 1-.957.923h-5.74a.957.924 0 0 1-.957-.924",viewW:180,viewH:100}},{id:"table_dining_rounded",group:"furniture",category:"dining_table",w:160,h:90,art:{d:"M3.879 17.527c0-7.587 6.39-13.737 14.272-13.737h123.696c7.883 0 14.273 6.15 14.273 13.737v54.947c0 7.587-6.39 13.737-14.273 13.737H18.151c-7.882 0-14.272-6.15-14.272-13.737z M138.994 19.358v-5.495a.952.916 0 0 1 .951-.915h5.71a.952.916 0 0 1 .95.915v5.495a.952.916 0 0 1-.95.916h-5.71a.952.916 0 0 1-.951-.916m-.001 56.778v-5.495a.952.916 0 0 1 .952-.915h5.709a.952.916 0 0 1 .951.915v5.495a.952.916 0 0 1-.951.916h-5.71a.952.916 0 0 1-.95-.916m-125.6-56.778v-5.495a.952.916 0 0 1 .951-.916h5.71a.952.916 0 0 1 .95.916v5.495a.952.916 0 0 1-.95.915h-5.71a.952.916 0 0 1-.951-.915m-.001 56.779v-5.495a.952.916 0 0 1 .952-.916h5.709a.952.916 0 0 1 .951.916v5.495a.952.916 0 0 1-.951.915h-5.71a.952.916 0 0 1-.95-.915",viewW:160,viewH:90}},{id:"desk",group:"furniture",category:"work_table",w:140,h:70,art:{d:"M3.862 5.967c0-1.234 1.057-2.234 2.362-2.234h127.552c1.305 0 2.362 1 2.362 2.234v58.066c0 1.234-1.057 2.233-2.362 2.233H6.224c-1.305 0-2.362-1-2.362-2.233z M9.531 15.346v-5.36a.945.893 0 0 1 .945-.893h5.67a.945.893 0 0 1 .944.893v5.36a.945.893 0 0 1-.945.894h-5.669a.945.893 0 0 1-.945-.894m113.379.001v-5.36a.945.893 0 0 1 .945-.894h5.67a.945.893 0 0 1 .944.894v5.36a.945.893 0 0 1-.945.893h-5.669a.945.893 0 0 1-.945-.893M9.53 60.013v-5.36a.945.893 0 0 1 .946-.893h5.669a.945.893 0 0 1 .945.893v5.36a.945.893 0 0 1-.945.894h-5.67a.945.893 0 0 1-.944-.894m113.38.001v-5.36a.945.893 0 0 1 .945-.894h5.669a.945.893 0 0 1 .945.894v5.36a.945.893 0 0 1-.945.893h-5.67a.945.893 0 0 1-.944-.893m-97.318-2.681h88.814",viewW:140,viewH:70}},{id:"desk_corner",group:"furniture",category:"work_table",w:160,h:160,art:{d:"M156.12 66.202a2.38 2.38 0 0 1-2.379 2.38H94.272c-13.138 0-23.788 10.65-23.788 23.787v61.372a2.38 2.38 0 0 1-2.379 2.38H6.257a2.38 2.38 0 0 1-2.378-2.38V6.257A2.38 2.38 0 0 1 6.257 3.88h147.484a2.38 2.38 0 0 1 2.38 2.378z M9.588 16.249v-5.71a.95.95 0 0 1 .951-.95h5.71a.95.95 0 0 1 .95.95v5.71a.95.95 0 0 1-.95.951h-5.71a.95.95 0 0 1-.951-.951m133.212.001v-5.71a.95.95 0 0 1 .951-.951h5.71a.95.95 0 0 1 .95.951v5.71a.95.95 0 0 1-.95.951h-5.71a.95.95 0 0 1-.951-.951m0 45.672v-5.71a.95.95 0 0 1 .952-.951h5.709a.95.95 0 0 1 .951.951v5.71a.95.95 0 0 1-.951.951h-5.71a.95.95 0 0 1-.95-.951M9.587 149.46v-5.708a.95.95 0 0 1 .952-.952h5.709a.95.95 0 0 1 .951.952v5.709a.95.95 0 0 1-.951.951h-5.71a.95.95 0 0 1-.951-.951m47.576 0v-5.709a.95.95 0 0 1 .952-.951h5.709a.95.95 0 0 1 .951.951v5.71a.95.95 0 0 1-.951.95h-5.71a.95.95 0 0 1-.951-.95m51.382-90.396H95.569a34.6 34.6 0 0 0-34.6 34.6v12.976",viewW:160,viewH:160}},{id:"chair",group:"furniture",category:"chair",w:50,h:50,art:{d:"M39.452 12.433H10.548a4.398 4.398 0 1 1 0-8.796h28.904a4.398 4.398 0 0 1 0 8.796m-28.904 0H8.035a4.4 4.4 0 0 0-4.398 4.399V40.08a6.283 6.283 0 0 0 6.283 6.284h30.16a6.283 6.283 0 0 0 6.284-6.284V16.832c0-2.43-1.97-4.399-4.399-4.399h-2.513",viewW:50,viewH:50}},{id:"chair_bar",group:"furniture",category:"chair",w:45,h:48,art:{d:"M38.579 17.168c1.326 2.526 1.947 6.015 1.947 9.071 0 10.018-8.07 18.139-18.025 18.139S4.475 36.257 4.475 26.239c0-3.056.586-6.552 1.912-9.078m32.192.007a3.59 3.59 0 0 1-3.333-.976m3.333.976a3.6 3.6 0 0 0 1.765-.976m-33.827.996-.13-.027a3.6 3.6 0 0 1-1.731-.969m30.59 0a18 18 0 0 0-5.848-3.932 17.93 17.93 0 0 0-13.796 0 18 18 0 0 0-5.848 3.932 3.59 3.59 0 0 1-3.237.996m28.729-.996a3.59 3.59 0 0 0 5.098 0m-33.827.996a3.6 3.6 0 0 1-1.861-.996 3.644 3.644 0 0 1 0-5.13 25.2 25.2 0 0 1 8.187-5.505A25.1 25.1 0 0 1 22.5 3.623a25.1 25.1 0 0 1 9.657 1.934 25.2 25.2 0 0 1 8.187 5.505 3.644 3.644 0 0 1 0 5.13",viewW:45,viewH:48}},{id:"armchair",group:"furniture",category:"armchair",w:90,h:90,art:{d:"M75.702 13.011q.012-.225.012-.453c0-4.843-3.917-8.768-8.748-8.768H23.034c-4.83 0-8.747 3.925-8.747 8.768q0 .228.011.453M77.132 72.09q.59.091 1.206.092c4.348 0 7.873-3.533 7.873-7.892V20.45c0-4.359-3.525-7.892-7.873-7.892-.924 0-1.812.16-2.636.453a7.89 7.89 0 0 0-5.237 7.438v.148m0 0a8.7 8.7 0 0 1-3.499.73H23.034a8.7 8.7 0 0 1-3.499-.73m50.93 0V64.29a7.89 7.89 0 0 0 6.667 7.8M14.298 13.01a7.89 7.89 0 0 1 5.237 7.438v.148m0 0V64.29a7.89 7.89 0 0 1-6.666 7.8q-.59.091-1.206.092c-4.348 0-7.873-3.533-7.873-7.892V20.45c0-4.359 3.525-7.892 7.873-7.892.924 0 1.811.16 2.635.453M77.132 72.09c-1.585 8.05-8.667 14.12-17.164 14.12H30.032c-8.496 0-15.578-6.07-17.163-14.12",viewW:90,viewH:90}},{id:"armchair_office",group:"furniture",category:"armchair",w:65,h:65,art:{d:"M56.968 18.845q0-.368-.05-.72M49.051 13.8H15.95a5.04 5.04 0 0 1-5.038-5.043 5.04 5.04 0 0 1 5.038-5.043h33.103a5.04 5.04 0 0 1 5.038 5.043 5.04 5.04 0 0 1-5.038 5.043m-33.103 0h-2.88a5.04 5.04 0 0 0-4.986 4.323M49.052 13.8h2.879a5.04 5.04 0 0 1 4.986 4.324m.051 24.826a4.32 4.32 0 0 0 4.318-4.323v-16.18a4.32 4.32 0 0 0-4.369-4.322m.051 24.826a4.32 4.32 0 0 1-4.318-4.323V22.447a4.32 4.32 0 0 1 4.267-4.322m-11.84 37.24h4.695c.736 0 1.446-.111 2.115-.317a7.21 7.21 0 0 0 5.081-6.889v-5.208M8.084 18.124h-.051a4.32 4.32 0 0 0-4.318 4.323v16.18a4.32 4.32 0 0 0 4.318 4.324 4.32 4.32 0 0 0 4.317-4.323V22.447a4.32 4.32 0 0 0-4.266-4.323m11.935 37.24h-4.79a7.2 7.2 0 0 1-2.04-.294 7.21 7.21 0 0 1-5.156-6.91v-5.21m37.045 12.414 5.177 5.183c.969.97 2.534.987 3.524.038a2.526 2.526 0 0 0 .038-3.605l-1.93-1.932m-6.81.316H20.02m-6.83-.294-1.909 1.91c-.998 1-.98 2.627.038 3.605.99.949 2.555.932 3.524-.038l5.177-5.183",viewW:65,viewH:65}},{id:"sofa",group:"furniture",category:"sofa",w:180,h:90,art:{d:"M90 19.365v57.111m0-57.11V7.683m0 11.681c0 2.151-1.836 3.894-4.1 3.894H30.544a10.7 10.7 0 0 1-4.136-.825M90 19.365c0 2.151 1.836 3.894 4.1 3.894h55.356c1.472 0 2.871-.294 4.137-.825M90 76.476c0 5.376-4.59 9.735-10.25 9.735H30.543c-5.266 0-9.605-3.772-10.185-8.625M90 76.476c0 5.376 4.59 9.735 10.25 9.735h49.206c5.266 0 9.605-3.772 10.185-8.625M90 7.684c0-2.15-1.837-3.894-4.101-3.894H30.544c-5.661 0-10.25 4.358-10.25 9.735q0 .416.036.825M90 7.684c0-2.15 1.836-3.894 4.1-3.894h55.356c5.661 0 10.25 4.358 10.25 9.735q0 .416-.036.825m0 0a10.7 10.7 0 0 1 4.137-.825h2.05c5.662 0 10.252 4.358 10.252 9.734v45.43c0 5.376-4.59 9.734-10.251 9.734h-2.05c-1.484 0-2.894-.3-4.167-.837m0 0c-3.585-1.517-6.084-4.93-6.084-8.898V23.26q0-.417.036-.825c.32-3.622 2.728-6.68 6.077-8.084m-139.34 0c3.35 1.404 5.758 4.462 6.078 8.084q.036.408.036.825v45.43c0 3.967-2.5 7.38-6.085 8.897a10.7 10.7 0 0 1-4.166.837h-2.05c-5.662 0-10.251-4.358-10.251-9.735V23.26c0-5.376 4.59-9.734 10.25-9.734h2.05c1.473 0 2.872.294 4.138.825",viewW:180,viewH:90}},{id:"sofa_three_seat",group:"furniture",category:"sofa",w:240,h:90,art:{d:"M86.553 18.593V76.96m0-58.366V7.491m0 11.102c0 2.044-1.762 3.701-3.935 3.701H29.496c-1.413 0-2.756-.28-3.97-.784m61.027-2.917c0 2.044 1.762 3.701 3.935 3.701h59.024c2.174 0 3.935-1.657 3.935-3.7M86.553 76.958c0 5.11-4.404 9.252-9.837 9.252h-47.22c-5.054 0-9.218-3.585-9.775-8.198m66.832-1.054c0 5.11 4.404 9.252 9.837 9.252h47.22c5.433 0 9.837-4.142 9.837-9.252m0-58.366V76.96m0-58.366V7.491m0 11.102c0 2.044 1.762 3.701 3.936 3.701h53.122c1.413 0 2.756-.28 3.97-.784m-61.028 55.449c0 5.11 4.405 9.252 9.838 9.252h47.22c5.054 0 9.218-3.585 9.775-8.198M86.553 7.491c0-2.044-1.762-3.701-3.935-3.701H29.496c-5.433 0-9.838 4.142-9.838 9.252q0 .396.035.784m66.86-6.335c0-2.044 1.762-3.701 3.935-3.701h59.024c2.174 0 3.935 1.657 3.935 3.7m0 0c0-2.043 1.762-3.7 3.936-3.7h53.122c5.433 0 9.838 4.142 9.838 9.252 0 .258.022.517 0 .77m-.063 64.201c1.22.512 2.574.796 3.998.796h1.967c5.433 0 9.837-4.142 9.837-9.252V22.294c0-5.11-4.404-9.252-9.837-9.252h-1.967c-5.153 0-9.38 3.725-9.803 8.468q-.036.387-.035.784v47.263c0 3.771 2.399 7.015 5.84 8.456M19.693 13.826a10.3 10.3 0 0 0-3.97-.784h-1.967c-5.433 0-9.838 4.142-9.838 9.252v47.263c0 5.11 4.405 9.252 9.838 9.252h1.967c1.424 0 2.777-.284 3.998-.796 3.441-1.441 5.84-4.685 5.84-8.456V22.294a9 9 0 0 0-.035-.784c-.307-3.443-2.618-6.349-5.833-7.684",viewW:240,viewH:90}},{id:"sofa_corner_right",group:"furniture",category:"sofa",w:260,h:170,art:{d:"M93.673 20.687V83.69m0-63.004v-12.6m0 12.6c0 2.32-1.913 4.2-4.273 4.2H31.704a10.8 10.8 0 0 1-4.311-.89m66.28-3.31c0 2.32 1.914 4.2 4.274 4.2h64.106c2.36 0 4.274-1.88 4.274-4.2M93.673 83.692c0 5.8-4.783 10.501-10.684 10.501H31.704c-5.489 0-10.011-4.068-10.616-9.304m72.585-1.197c0 5.8 4.784 10.501 10.684 10.501h51.286c5.9 0 10.684-4.701 10.684-10.5m0-63.005V83.69m0-63.004v-12.6m0 12.6c0 2.32 1.914 4.2 4.273 4.2h57.696c1.534 0 2.993-.317 4.312-.89m-66.28 59.695v71.923c0 5.799 4.782 10.5 10.684 10.5h51.284c5.49 0 10.012-4.068 10.616-9.304V84.888M93.673 8.086c0-2.32-1.913-4.2-4.273-4.2H31.704c-5.9 0-10.684 4.7-10.684 10.5q0 .45.038.89m72.615-7.19c0-2.32 1.914-4.2 4.274-4.2h64.106c2.36 0 4.274 1.88 4.274 4.2m0 0c0-2.32 1.914-4.2 4.273-4.2h57.696c5.9 0 10.685 4.7 10.685 10.5 0 .293.024.587 0 .874m-.07 69.628c1.327.58 2.797.904 4.344.904h2.136c5.901 0 10.684-4.702 10.684-10.501V24.887c0-5.8-4.783-10.5-10.684-10.5h-2.136c-5.596 0-10.187 4.227-10.647 9.61q-.038.44-.038.89V75.29c0 4.28 2.605 7.961 6.342 9.597M21.059 15.276a10.8 10.8 0 0 0-4.312-.89H14.61c-5.9 0-10.684 4.701-10.684 10.5v50.405c0 5.8 4.783 10.5 10.684 10.5h2.137c1.546 0 3.016-.322 4.342-.903 3.737-1.636 6.342-5.317 6.342-9.597V24.887q0-.45-.037-.89c-.334-3.907-2.844-7.206-6.335-8.72",viewW:260,viewH:170}},{id:"bed_single",group:"furniture",category:"bed",w:90,h:200,art:{d:"M3.79 14.024c0 5.59 4.318 10.122 9.645 10.122h63.13c5.328 0 9.646-4.532 9.646-10.122m-22.797 46.93V46.23c0-5.082-3.926-9.201-8.769-9.201h-19.29c-4.842 0-8.768 4.12-8.768 9.201v14.723m-22.797 0h82.42m0 22.085H3.79m8.768 113.06h64.885c4.842 0 8.768-4.12 8.768-9.202V13.104c0-5.082-3.926-9.202-8.768-9.202H12.558c-4.843 0-8.768 4.12-8.768 9.202v173.792c0 5.082 3.925 9.202 8.768 9.202",viewW:90,viewH:200}},{id:"bed_double",group:"furniture",category:"bed",w:160,h:200,art:{d:"M3.879 14.073c0 5.618 4.516 10.172 10.088 10.172h132.065c5.571 0 10.088-4.554 10.088-10.172m-81.165 47.16V46.438c0-5.107-4.106-9.247-9.171-9.247H45.607c-5.065 0-9.17 4.14-9.17 9.247v14.795m89.877 0V46.438c0-5.107-4.107-9.247-9.172-9.247H96.966c-5.065 0-9.171 4.14-9.171 9.247v14.795m-83.916 0H156.12m0 22.192H3.88m9.17 112.673h133.9c5.065 0 9.171-4.14 9.171-9.247V13.15c0-5.107-4.106-9.247-9.171-9.247H13.05c-5.064 0-9.17 4.14-9.17 9.247v173.7c0 5.107 4.106 9.247 9.17 9.247",viewW:160,viewH:200}},{id:"nightstand",group:"furniture",category:"nightstand",w:50,h:40,art:{d:"M5.773 3.555h38.454a2.136 2.056 0 0 1 2.137 2.056v28.777a2.136 2.056 0 0 1-2.137 2.056H5.773a2.136 2.056 0 0 1-2.136-2.056V5.611a2.136 2.056 0 0 1 2.136-2.056",viewW:50,viewH:40}},{id:"cabinet_tv",group:"furniture",category:"nightstand",w:140,h:45,art:{d:"M56.772 17.88h5.315m0 0h5.315m-5.315 0v9.214m9.832-9.214 5.315 9.45 5.314-9.45M6.224 41.4h127.552c1.305 0 2.362-.94 2.362-2.1V5.7c0-1.16-1.057-2.1-2.362-2.1H6.224c-1.305 0-2.362.94-2.362 2.1v33.6c0 1.16 1.057 2.1 2.362 2.1",viewW:140,viewH:45}},{id:"cabinet_shoe",group:"furniture",category:"nightstand",w:80,h:35,art:{d:"m39.722 16.231 1.505-1.596m1.134 3.15 1.53-1.738M6.03 31.5h67.94c1.251 0 2.265-.895 2.265-2v-24c0-1.105-1.014-2-2.264-2H6.03c-1.251 0-2.265.895-2.265 2v24c0 1.105 1.014 2 2.265 2m41.966-14.305L46.92 17.1a3.4 3.4 0 0 1-1.813-.706l-4.583-3.62c-1.264-1-3.277-.326-3.475 1.161-.185 1.39-1.983 2.1-3.28 1.299l-1.532-.947c-.968-.513-2.2.103-2.2 1.1v3.463c0 1.559 1.43 2.823 3.196 2.823h14.51c1.404 0 2.542-1.005 2.542-2.245 0-1.153-.99-2.118-2.289-2.233",viewW:80,viewH:35}},{id:"cabinet_sink",group:"furniture",category:"nightstand",w:80,h:50,art:{d:"M3.765 5.773c0-1.18 1.014-2.136 2.265-2.136h67.94c1.251 0 2.265.956 2.265 2.136v38.454c0 1.18-1.014 2.137-2.264 2.137H6.03c-1.251 0-2.265-.957-2.265-2.137z M36.088 32.135c2.525 1.88 6.205 1.88 8.73 0s3.167-5.101 1.52-7.637l-5.879-8.043-5.893 8.043c-1.644 2.536-1.003 5.755 1.522 7.637",viewW:80,viewH:50}},{id:"bookshelf",group:"furniture",category:"wardrobe",w:100,h:35,art:{d:"M57.12 20.488H45.473c-.514 0-1.008.124-1.372.345s-.568.52-.568.832m4.076-8.236h5.434M6.119 3.5h87.762c1.276 0 2.31.895 2.31 2v24c0 1.105-1.034 2-2.31 2H6.12c-1.275 0-2.31-.895-2.31-2v-24c0-1.105 1.035-2 2.31-2m51 6.4v14.118H45.474a2.04 2.04 0 0 1-1.372-.517 1.7 1.7 0 0 1-.568-1.248V11.665c0-.468.204-.917.568-1.248a2.04 2.04 0 0 1 1.372-.517z",viewW:100,viewH:35}},{id:"wall_unit",group:"furniture",category:"wardrobe",w:240,h:45,art:{d:"M6.336 41.4h227.328a2.418 2.1 0 0 0 2.418-2.1V5.7a2.418 2.1 0 0 0-2.418-2.1H6.336a2.418 2.1 0 0 0-2.418 2.1v33.6a2.418 2.1 0 0 0 2.418 2.1m43.744-5.88V9.48m46.432 26.04V9.48m46.433 26.04V9.48m46.433 26.04V9.48",viewW:240,viewH:45}},{id:"wardrobe",group:"furniture",category:"wardrobe",w:180,h:60,art:{d:"M93.87 20.25a3.3 3.3 0 0 0-1.072-2.407 3.8 3.8 0 0 0-2.586-.997c-.97 0-1.9.358-2.587.997a3.3 3.3 0 0 0-1.071 2.407c0 2.838 1.225 5.107 3.658 6.81h-.015m0 0 14.58 7.537c.57.295 1.046.726 1.377 1.25.331.523.505 1.12.505 1.727v1.401a3.3 3.3 0 0 1-1.071 2.407 3.8 3.8 0 0 1-2.587.998H77.393a3.8 3.8 0 0 1-2.586-.998 3.3 3.3 0 0 1-1.072-2.407v-1.4c0-.608.174-1.205.505-1.728a3.55 3.55 0 0 1 1.377-1.25zM6.284 3.691h167.433c1.32 0 2.392.981 2.392 2.192v48.23c0 1.212-1.071 2.193-2.392 2.193H6.284c-1.321 0-2.392-.981-2.392-2.192V5.885c0-1.212 1.07-2.193 2.392-2.193",viewW:180,viewH:60}},{id:"kitchen_floor",group:"furniture",category:"kitchen_cabinet",w:60,h:60,art:{d:"M3.692 5.884c0-1.21.981-2.192 2.192-2.192h48.23c1.212 0 2.193.981 2.193 2.192v48.23a2.19 2.19 0 0 1-2.192 2.193H5.885a2.19 2.19 0 0 1-2.193-2.192z",viewW:60,viewH:60}},{id:"kitchen_floor_corner",group:"furniture",category:"kitchen_cabinet",w:90,h:90,art:{d:"M45.944 41.767a2.29 2.29 0 0 0 2.29 2.29H83.92a2.29 2.29 0 0 1 2.29 2.29V83.92a2.29 2.29 0 0 1-2.29 2.29H6.08a2.29 2.29 0 0 1-2.29-2.29V6.08a2.29 2.29 0 0 1 2.29-2.29h37.576a2.29 2.29 0 0 1 2.29 2.29z",viewW:90,viewH:90}},{id:"kitchen_wall",group:"furniture",category:"kitchen_cabinet",w:60,h:35,art:{d:"m4.444 31.033 50.75-27.007m-50.752-.06 50.75 27.008M5.884 31.5h48.23c1.212 0 2.193-.895 2.193-2v-24c0-1.105-.981-2-2.192-2H5.885c-1.212 0-2.193.895-2.193 2v24c0 1.105.981 2 2.192 2",viewW:60,viewH:35}},{id:"kitchen_wall_corner",group:"furniture",category:"kitchen_cabinet",w:60,h:60,art:{d:"M3.772 29.59 29.88 4.383M4.57 55.43 55.43 30M4.175 4.383l26.701 23.863m-27.184.877L55.43 55.43M28.41 3.692H5.884c-1.21 0-2.192.981-2.192 2.192v48.23c0 1.212.981 2.193 2.192 2.193h48.23a2.19 2.19 0 0 0 2.193-2.192V31.589c0-1.21-.981-2.192-2.192-2.192h-21.32a2.19 2.19 0 0 1-2.193-2.192V5.885a2.19 2.19 0 0 0-2.192-2.193",viewW:60,viewH:60}},{id:"shelf_floor",group:"furniture",category:"shelving",w:100,h:35,art:{d:"M6.12 31.5h87.76a2.31 2 0 0 0 2.31-2v-24a2.31 2 0 0 0-2.31-2H6.12a2.31 2 0 0 0-2.31 2v24a2.31 2 0 0 0 2.31 2 M9.089 25.933c-.494.112-.776.546-.63.97s.664.675 1.157.564l-.263-.767zM90.912 9.067c.493-.112.775-.546.63-.97s-.664-.675-1.158-.564l.264.767zM11.875 26.956c.493-.112.775-.546.63-.97s-.664-.676-1.158-.564l.264.767zm3.988-2.556c-.493.112-.775.546-.63.969s.664.676 1.158.564l-.264-.766zm5.045.511c.493-.112.775-.545.63-.969s-.664-.676-1.158-.564l.264.766zm3.988-2.555c-.493.111-.775.545-.63.968.146.424.664.677 1.158.565l-.264-.767zm5.044.51c.494-.111.776-.545.63-.968-.145-.424-.664-.676-1.157-.565l.263.767zm3.99-2.555c-.495.112-.777.546-.63.97.145.423.663.675 1.157.563l-.264-.766zm5.043.511c.494-.112.776-.545.63-.969s-.664-.676-1.158-.564l.264.767zm3.989-2.555c-.494.111-.776.545-.63.969s.664.676 1.157.564l-.263-.767zm5.044.51c.494-.11.776-.545.63-.968-.146-.424-.664-.676-1.158-.565l.264.767zm3.989-2.555c-.494.112-.776.546-.63.97.145.423.664.675 1.157.564l-.264-.767zm5.044.511c.493-.111.775-.545.63-.969s-.664-.676-1.158-.564l.264.767zm3.988-2.555c-.493.112-.775.545-.63.969s.664.676 1.158.564l-.264-.767zm5.045.51c.493-.11.775-.545.63-.968-.146-.424-.664-.676-1.158-.564l.264.766zm3.988-2.555c-.493.112-.775.546-.63.97s.664.675 1.158.564l-.264-.767zm5.044.511c.494-.111.776-.545.63-.968-.145-.424-.664-.677-1.157-.565l.264.767zm3.99-2.555c-.494.112-.777.545-.63.969.145.423.663.676 1.157.564l-.264-.766zm5.043.511c.494-.112.776-.546.63-.969s-.664-.676-1.157-.564l.263.766zm3.989-2.556c-.494.112-.776.546-.63.97s.664.676 1.158.564l-.264-.767zM9.353 26.7l.263.767 2.259-.511-.264-.767-.264-.767-2.258.511zm6.774-1.533.264.766 4.517-1.022-.264-.767-.264-.766-4.517 1.022zm9.033-2.045.264.767 4.516-1.022-.264-.767-.263-.767-4.517 1.023zm9.033-2.044.264.766 4.516-1.022-.264-.766-.264-.767-4.516 1.022zm9.033-2.045.263.767 4.517-1.022-.264-.767-.264-.767-4.516 1.023zm9.032-2.044.264.767 4.517-1.023-.264-.766-.264-.767-4.516 1.022zm9.033-2.045.264.767 4.517-1.022-.264-.767-.264-.766-4.517 1.022zm9.033-2.044.264.767 4.516-1.023-.263-.766-.264-.767-4.517 1.022zm9.033-2.044.264.766 4.516-1.022-.264-.767-.263-.766-4.517 1.022zM88.39 8.81l.264.767 2.258-.511-.264-.767-.264-.767-2.258.511z M9.089 9.067c-.494-.112-.776-.546-.63-.97s.664-.675 1.157-.564l-.263.767zm81.823 16.866c.493.112.775.546.63.97s-.664.675-1.158.564l.264-.767zM11.875 8.044c.493.112.775.546.63.97s-.664.676-1.158.564l.264-.767zm3.988 2.556c-.493-.112-.775-.546-.63-.969s.664-.676 1.158-.564l-.264.766zm5.045-.511c.493.112.775.545.63.969s-.664.676-1.158.564l.264-.766zm3.988 2.555c-.493-.111-.775-.545-.63-.968.146-.424.664-.677 1.158-.565l-.264.767zm5.044-.51c.494.111.776.545.63.968-.145.424-.664.676-1.157.565l.263-.767zm3.99 2.555c-.495-.112-.777-.546-.63-.97.145-.423.663-.675 1.157-.563l-.264.766zm5.043-.511c.494.112.776.545.63.969s-.664.676-1.158.564l.264-.767zm3.989 2.555c-.494-.111-.776-.545-.63-.969s.664-.676 1.157-.564l-.263.767zm5.044-.51c.494.11.776.545.63.968-.146.424-.664.676-1.158.565l.264-.767zm3.989 2.555c-.494-.112-.776-.546-.63-.97.145-.423.664-.675 1.157-.564l-.264.767zm5.044-.511c.493.111.775.545.63.969s-.664.676-1.158.564l.264-.767zm3.988 2.555c-.493-.112-.775-.545-.63-.969s.664-.676 1.158-.564l-.264.767zm5.045-.51c.493.11.775.545.63.968-.146.424-.664.676-1.158.564l.264-.766zm3.988 2.555c-.493-.112-.775-.546-.63-.97s.664-.675 1.158-.564l-.264.767zm5.044-.511c.494.111.776.545.63.968-.145.424-.664.677-1.157.565l.264-.767zm3.99 2.555c-.494-.112-.777-.545-.63-.969.145-.423.663-.676 1.157-.564l-.264.766zm5.043-.511c.494.112.776.546.63.969s-.664.676-1.157.564l.263-.766zm3.989 2.556c-.494-.112-.776-.546-.63-.97s.664-.676 1.158-.564l-.264.767zM9.353 8.3l.263-.767 2.259.511-.264.767-.264.767-2.258-.511zm6.774 1.533.264-.766 4.517 1.022-.264.767-.264.766-4.517-1.022zm9.033 2.045.264-.767 4.516 1.022-.264.767-.263.767-4.517-1.023zm9.033 2.044.264-.766 4.516 1.022-.264.766-.264.767-4.516-1.022zm9.033 2.045.263-.767 4.517 1.022-.264.767-.264.767-4.516-1.023zm9.032 2.044.264-.767 4.517 1.023-.264.766-.264.767-4.516-1.022zm9.033 2.045.264-.767 4.517 1.022-.264.767-.264.766-4.517-1.022zm9.033 2.044.264-.767 4.516 1.023-.263.766-.264.767-4.517-1.022zm9.033 2.044.264-.766 4.516 1.022-.264.767-.263.766-4.517-1.022zm9.033 2.045.264-.767 2.258.511-.264.767-.264.767-2.258-.511z",viewW:100,viewH:35}},{id:"shelf_wall",group:"furniture",category:"shelving",w:100,h:25,art:{d:"M18.36 3.15v2.567c0 1.012-1.034 1.833-2.31 1.833h-.924c-1.275 0-2.309-.82-2.309-1.833V3.15m73.905 0v2.567c0 1.012-1.034 1.833-2.31 1.833h-.924c-1.275 0-2.31-.82-2.31-1.833V3.15m-75.29 0H93.65c1.276 0 2.31.82 2.31 1.833V19.65c0 1.012-1.034 1.833-2.31 1.833H5.888c-1.275 0-2.31-.82-2.31-1.833V4.983c0-1.012 1.035-1.833 2.31-1.833",viewW:100,viewH:25}},{id:"cooktop_two",group:"appliance",category:"cooktop",w:30,h:50,art:{d:"M15 30.982v-1.71m3.086 5.128h1.542M15 39.527v-1.709M10.372 34.4h1.542M5.84 46.364h18.322c1.331 0 2.41-1.196 2.41-2.67V6.306c0-1.475-1.079-2.67-2.41-2.67H5.839c-1.331 0-2.41 1.195-2.41 2.67v37.386c0 1.475 1.079 2.67 2.41 2.67m14.56-29.91c0 3.304-2.417 5.982-5.4 5.982-2.981 0-5.4-2.678-5.4-5.981s2.419-5.982 5.4-5.982c2.983 0 5.4 2.678 5.4 5.982M18.858 34.4c0 2.36-1.727 4.273-3.857 4.273s-3.857-1.913-3.857-4.273S12.87 30.127 15 30.127s3.857 1.913 3.857 4.273",viewW:30,viewH:50}},{id:"stove",group:"appliance",category:"cooktop",w:60,h:60,art:{d:"M41.4 35.261a.877.877 0 1 0-1.754 0zm4.384 6.138a.877.877 0 1 0 0-1.753zM41.4 44.03a.877.877 0 1 0-1.753 0zm-4.384-2.63a.877.877 0 1 0 0-1.754zM5.885 3.691v.877h48.23V2.815H5.885zm50.422 2.192h-.877v48.23h1.754V5.885zm-2.192 50.423v-.877H5.885v1.754h48.23zM3.692 54.115h.877V5.885H2.815v48.23zm2.192 2.192v-.877a1.315 1.315 0 0 1-1.315-1.315H2.815a3.07 3.07 0 0 0 3.07 3.069zm50.423-2.192h-.877c0 .726-.589 1.315-1.315 1.315v1.754a3.07 3.07 0 0 0 3.069-3.07zM54.115 3.692v.877c.726 0 1.315.589 1.315 1.315h1.754a3.07 3.07 0 0 0-3.07-3.069zm-48.23 0v-.877a3.07 3.07 0 0 0-3.07 3.07h1.754c0-.727.589-1.316 1.315-1.316zm19.73 36.83h-.877a5.26 5.26 0 0 1-5.262 5.262v1.754a7.015 7.015 0 0 0 7.016-7.015zm-6.139 6.139v-.877a5.26 5.26 0 0 1-5.261-5.261h-1.754a7.015 7.015 0 0 0 7.015 7.015zm-6.138-6.138h.877a5.26 5.26 0 0 1 5.261-5.262v-1.754a7.015 7.015 0 0 0-7.015 7.016zm6.138-6.139v.877a5.26 5.26 0 0 1 5.262 5.262h1.754a7.015 7.015 0 0 0-7.016-7.016zm27.185-14.908h-.877a5.26 5.26 0 0 1-5.261 5.262v1.754a7.015 7.015 0 0 0 7.015-7.016zm-6.138 6.139v-.877a5.26 5.26 0 0 1-5.262-5.262h-1.754a7.015 7.015 0 0 0 7.016 7.016zm-6.139-6.139h.877a5.26 5.26 0 0 1 5.262-5.261v-1.754a7.015 7.015 0 0 0-7.016 7.015zm6.139-6.138v.877a5.26 5.26 0 0 1 5.261 5.261h1.754a7.015 7.015 0 0 0-7.015-7.015zm4.384 27.185h-.877a3.51 3.51 0 0 1-3.507 3.507v1.754a5.26 5.26 0 0 0 5.261-5.261zm-4.384 4.384v-.877a3.51 3.51 0 0 1-3.508-3.507H35.26a5.26 5.26 0 0 0 5.262 5.261zm-4.385-4.384h.877a3.51 3.51 0 0 1 3.508-3.508V35.26a5.26 5.26 0 0 0-5.262 5.262zm4.385-4.385v.877a3.51 3.51 0 0 1 3.507 3.508h1.754a5.26 5.26 0 0 0-5.261-5.262zm0 .877h.876V35.26h-1.753v1.754zm3.508 3.508v.876h1.753v-1.753h-1.753zm-3.508 5.261h.876V44.03h-1.753v1.754zm-5.261-5.261v.876h1.753v-1.753h-1.753zm-9.647-21.047h-.877a5.26 5.26 0 0 1-5.262 5.262v1.754a7.015 7.015 0 0 0 7.016-7.016zm-6.139 6.139v-.877a5.26 5.26 0 0 1-5.261-5.262h-1.754a7.015 7.015 0 0 0 7.015 7.016zm-6.138-6.139h.877a5.26 5.26 0 0 1 5.261-5.261v-1.754a7.015 7.015 0 0 0-7.015 7.015zm6.138-6.138v.877a5.26 5.26 0 0 1 5.262 5.261h1.754a7.015 7.015 0 0 0-7.016-7.015z",viewW:60,viewH:60}},{id:"tv",group:"appliance",category:"tv",w:120,h:28,art:{d:"M45.134 10.766H6.18c-1.292 0-2.34.848-2.34 1.894v2.68c0 1.045 1.048 1.893 2.34 1.893h38.954m0-6.467V5.288c0-1.046 1.048-1.894 2.34-1.894h26.704c1.292 0 2.34.848 2.34 1.894v5.478m-31.384 0h31.384m0 0h37.302c1.293 0 2.34.848 2.34 1.894v2.68c0 1.045-1.047 1.893-2.34 1.893H76.518m-31.384 0v5.479c0 1.046 1.048 1.894 2.34 1.894h26.704c1.292 0 2.34-.848 2.34-1.894v-5.479m-31.384 0h31.384",viewW:120,viewH:28}},{id:"tv_wall",group:"appliance",category:"tv",w:120,h:15,art:{d:"M35.664 11.4c-.517 0-.936.269-.936.6s.419.6.936.6zm49.553 1.2c.517 0 .936-.269.936-.6s-.42-.6-.936-.6zM6.18 3v.6h107.64V2.4H6.18zm109.98 1.5h-.936v1.8h1.872V4.5zm-2.34 3.3v-.6H6.18v1.2h107.64zM3.84 6.3h.936V4.5H2.904v1.8zm2.34 1.5v-.6c-.775 0-1.404-.403-1.404-.9H2.904c0 1.16 1.467 2.1 3.276 2.1zm109.98-1.5h-.936c0 .497-.629.9-1.404.9v1.2c1.81 0 3.276-.94 3.276-2.1zM113.82 3v.6c.775 0 1.404.403 1.404.9h1.872c0-1.16-1.467-2.1-3.276-2.1zM6.18 3v-.6c-1.81 0-3.276.94-3.276 2.1h1.872c0-.497.629-.9 1.404-.9zm61.308 4.2h-.936V12h1.872V7.2zM52.512 12h.936V7.2h-1.872V12zm-16.848 0v.6h49.553v-1.2H35.664z",viewW:120,viewH:15}},{id:"toilet",group:"sanitary",category:"toilet",w:40,h:70,art:{d:"M3.555 48.976c0 9.55 7.125 17.29 15.914 17.29h1.061c8.79 0 15.914-7.74 15.914-17.29V5.966c0-1.233-.92-2.233-2.056-2.233H5.611c-1.135 0-2.056 1-2.056 2.234z M7.666 49.12c0 7.003 5.225 12.68 11.67 12.68h1.327c6.445 0 11.67-5.677 11.67-12.68V31.873c0-1.233-.92-2.233-2.056-2.233H9.722c-1.135 0-2.056 1-2.056 2.233zM33.792 3.733c1.465 0 2.652 1.29 2.652 2.882v12.103c0 1.592-1.187 2.882-2.652 2.882H6.208c-1.465 0-2.653-1.29-2.653-2.882V6.615c0-1.591 1.188-2.882 2.653-2.882z M24.11 50.186c0 2.467-1.84 4.467-4.11 4.467s-4.111-2-4.111-4.467 1.84-4.466 4.11-4.466 4.112 2 4.112 4.466",viewW:40,viewH:70}},{id:"toilet_built_in",group:"sanitary",category:"toilet",w:40,h:55,art:{d:"M3.555 34.56c0 9.263 7.125 16.774 15.914 16.774h1.061c8.79 0 15.914-7.51 15.914-16.775V5.833c0-1.196-.92-2.166-2.056-2.166H5.611c-1.135 0-2.056.97-2.056 2.166z M7.666 34.7c0 6.793 5.225 12.3 11.67 12.3h1.327c6.445 0 11.67-5.507 11.67-12.3V14.5c0-1.197-.92-2.167-2.056-2.167H9.722c-1.135 0-2.056.97-2.056 2.167z M24.11 35.733c0 2.394-1.84 4.334-4.11 4.334s-4.111-1.94-4.111-4.334 1.84-4.333 4.11-4.333 4.112 1.94 4.112 4.333",viewW:40,viewH:55}},{id:"bathtub",group:"sanitary",category:"bathtub",w:170,h:75,art:{d:"M3.885 6c0-1.243 1.068-2.25 2.386-2.25h157.458c1.318 0 2.386 1.007 2.386 2.25v63c0 1.243-1.068 2.25-2.386 2.25H6.27c-1.318 0-2.386-1.007-2.386-2.25z M13.428 37.5c0-13.669 11.75-24.75 26.243-24.75h90.658c14.493 0 26.243 11.081 26.243 24.75s-11.75 24.75-26.243 24.75H39.67c-14.493 0-26.243-11.081-26.243-24.75 M31.56 37.5c0 2.237-1.923 4.05-4.295 4.05s-4.294-1.813-4.294-4.05 1.923-4.05 4.294-4.05c2.372 0 4.295 1.813 4.295 4.05",viewW:170,viewH:75}},{id:"bathtub_corner",group:"sanitary",category:"bathtub",w:140,h:140,art:{d:"M3.862 6.224a2.36 2.36 0 0 1 2.362-2.362H133.97c1.197 0 2.168.97 2.168 2.168 0 71.857-58.251 130.108-130.108 130.108a2.17 2.17 0 0 1-2.168-2.168z M13.31 32.207c0-10.437 8.46-18.897 18.897-18.897h87.628a2.13 2.13 0 0 1 2.13 2.13c0 58.833-47.692 106.526-106.524 106.526a2.13 2.13 0 0 1-2.13-2.131z M32.207 27.483a4.724 4.724 0 1 1-9.449 0 4.724 4.724 0 0 1 9.449 0",viewW:140,viewH:140}},{id:"bidet",group:"sanitary",category:"bidet",w:40,h:60,art:{d:"M22.466 3.692v10.085c0 1.21-.92 2.192-2.055 2.192h-.822c-1.136 0-2.056-.981-2.056-2.192V3.692m4.933 8.77h2.878c1.135 0 2.056-.982 2.056-2.193v-.877c0-1.21-.92-2.192-2.056-2.192h-2.878m-4.933 5.262h-2.878c-1.135 0-2.055-.982-2.055-2.193v-.877c0-1.21.92-2.192 2.055-2.192h2.878M5.611 3.692h28.777c1.136 0 2.056.981 2.056 2.192v33.45c0 9.374-7.125 16.973-15.914 16.973h-1.06c-8.79 0-15.915-7.599-15.915-16.972V5.885c0-1.212.92-2.193 2.056-2.193m4.11 15.785h20.556c1.136 0 2.056.981 2.056 2.192v17.807c0 6.874-5.225 12.447-11.67 12.447h-1.327c-6.445 0-11.67-5.573-11.67-12.447V21.67c0-1.21.92-2.192 2.056-2.192m13.567 7.892c0 1.937-1.472 3.508-3.288 3.508-1.817 0-3.29-1.57-3.29-3.508s1.473-3.507 3.29-3.507 3.288 1.57 3.288 3.507",viewW:40,viewH:60}},{id:"bidet_built_in",group:"sanitary",category:"bidet",w:40,h:50,art:{d:"M17.318 12.182h-8.06c-.88 0-1.592.74-1.592 1.654v16.126c0 6.698 5.225 12.129 11.67 12.129h1.327c6.445 0 11.67-5.43 11.67-12.13V13.837c0-.914-.713-1.654-1.591-1.654H22.68m-.215-8.545v9.827c0 1.18-.92 2.136-2.055 2.136h-.822c-1.136 0-2.056-.956-2.056-2.136V3.637m10.689 8.545V3.637m-16.445 0v8.545M5.611 3.637h28.777c1.136 0 2.056.956 2.056 2.136v24.051c0 9.135-7.125 16.54-15.914 16.54h-1.06c-8.79 0-15.915-7.405-15.915-16.54V5.773c0-1.18.92-2.136 2.056-2.136M23.289 23.29c0 1.888-1.473 3.418-3.29 3.418s-3.288-1.53-3.288-3.418 1.472-3.418 3.289-3.418c1.816 0 3.288 1.53 3.288 3.418",viewW:40,viewH:50}},{id:"kitchen_sink",group:"sanitary",category:"kitchen_sink",w:60,h:50,art:{d:"M38.188 8.7h10.598c1.211 0 2.193.956 2.193 2.136v28.2c0 1.18-.982 2.136-2.193 2.136H11.08c-1.21 0-2.192-.956-2.192-2.136v-28.2c0-1.18.981-2.136 2.192-2.136h10.599m10.885-5.128V15.11c0 1.18-.981 2.136-2.192 2.136h-.877c-1.21 0-2.192-.957-2.192-2.136V3.572m5.261 7.691h3.07c1.21 0 2.192-.956 2.192-2.136v-.855c0-1.18-.982-2.136-2.192-2.136h-3.07m-5.261 5.127h-3.07c-1.21 0-2.192-.956-2.192-2.136v-.855c0-1.18.982-2.136 2.193-2.136h3.069M5.817 46.299h48.231c1.21 0 2.192-.956 2.192-2.136V5.709c0-1.18-.981-2.137-2.192-2.137H5.818c-1.211 0-2.193.957-2.193 2.137v38.454c0 1.18.982 2.136 2.192 2.136M33.44 25.79c0 1.888-1.57 3.418-3.507 3.418s-3.508-1.53-3.508-3.418c0-1.887 1.57-3.418 3.508-3.418s3.507 1.53 3.507 3.418",viewW:60,viewH:50}},{id:"kitchen_sink_double",group:"sanitary",category:"kitchen_sink",w:90,h:50,art:{d:"M53.173 12.182c-.468 0-.847.354-.847.79s.38.79.847.79zm21.979 1.58c.467 0 .846-.353.846-.79 0-.436-.379-.79-.846-.79zM6.01 3.572v.79h77.842v-1.58H6.01zM86.14 5.71h-.847v38.454h1.694V5.709zM83.85 46.3v-.79H6.01v1.58h77.842zM3.72 44.163h.847V5.709H2.873v38.454zm2.29 2.136v-.79c-.797 0-1.443-.603-1.443-1.346H2.873c0 1.616 1.404 2.926 3.137 2.926zm80.131-2.136h-.847c0 .743-.646 1.346-1.442 1.346v1.58c1.732 0 3.136-1.31 3.136-2.926zm-2.29-40.59v.79c.797 0 1.443.602 1.443 1.346h1.694c0-1.617-1.404-2.927-3.136-2.927zm-77.841 0v-.79c-1.733 0-3.137 1.31-3.137 2.926h1.694c0-.744.646-1.347 1.443-1.347zm5.494 37.599v.79h28.39v-1.58h-28.39zm30.68-2.136h.846v-28.2h-1.694v28.2zm-32.97-28.2h-.846v28.2h1.694v-28.2zM39.895 8.7v-.79h-5.669v1.58h5.669zm-22.721 0v-.79h-5.669v1.58h5.669zm-7.958 2.136h.847c0-.744.645-1.346 1.442-1.346V7.91c-1.732 0-3.136 1.31-3.136 2.926zm32.968 0h.847c0-1.616-1.404-2.927-3.136-2.927v1.58c.797 0 1.442.603 1.442 1.347zm-2.29 30.336v.79c1.733 0 3.137-1.31 3.137-2.926h-1.694c0 .743-.645 1.346-1.442 1.346zm-28.389 0v-.79c-.797 0-1.442-.603-1.442-1.346H8.368c0 1.616 1.404 2.926 3.136 2.926zm13.737-23.927v.79h.916v-1.58h-.916zm3.205-2.136h.847V3.572H27.6V15.11zM22.952 3.572h-.847V15.11h1.693V3.572zm3.205 13.673v.79c1.732 0 3.136-1.31 3.136-2.926H27.6c0 .743-.646 1.346-1.443 1.346zm-.916 0v-.79c-.797 0-1.443-.603-1.443-1.346h-1.693c0 1.616 1.404 2.926 3.136 2.926zm3.205-5.982v.79h3.206v-1.58h-3.206zm5.495-2.136h.847v-.855h-1.694v.855zm-2.29-2.991v-.79h-3.205v1.58h3.206zm2.29 2.136h.847c0-1.616-1.404-2.926-3.136-2.926v1.58c.796 0 1.442.603 1.442 1.346zm-2.29 2.991v.79c1.733 0 3.137-1.31 3.137-2.926h-1.694c0 .743-.646 1.346-1.442 1.346zm-11.905 0v.79h3.206v-1.58h-3.206zm3.206-5.127v-.79h-3.206v1.58h3.206zm-5.495 2.136h-.847v.855h1.694v-.855zm2.29-2.136v-.79c-1.733 0-3.137 1.31-3.137 2.926h1.694c0-.743.646-1.346 1.442-1.346zm0 5.127v-.79c-.797 0-1.443-.603-1.443-1.346H16.61c0 1.616 1.404 2.926 3.136 2.926zm9.615 14.527h-.847c0 1.452-1.26 2.628-2.816 2.628v1.58c2.49 0 4.51-1.884 4.51-4.208zM25.7 29.208v-.79c-1.555 0-2.816-1.176-2.816-2.628H21.19c0 2.324 2.02 4.209 4.51 4.209zm-3.663-3.418h.847c0-1.451 1.26-2.628 2.816-2.628v-1.58c-2.49 0-4.51 1.884-4.51 4.208zm3.663-3.418v.79c1.555 0 2.816 1.177 2.816 2.628h1.694c0-2.324-2.02-4.208-4.51-4.208zm24.268 18.8v.79h28.39v-1.58h-28.39zm30.68-2.136h.846v-28.2H79.8v28.2zM78.356 8.7v-.79h-28.39v1.58h28.39zm-30.68 2.136h-.846v28.2h1.694v-28.2zm2.29-2.136v-.79c-1.732 0-3.136 1.31-3.136 2.926h1.694c0-.744.646-1.346 1.442-1.346zm30.68 2.136h.846c0-1.616-1.404-2.927-3.136-2.927v1.58c.797 0 1.443.603 1.443 1.347zm-2.29 30.336v.79c1.732 0 3.136-1.31 3.136-2.926H79.8c0 .743-.646 1.346-1.443 1.346zm-28.39 0v-.79c-.796 0-1.442-.603-1.442-1.346H46.83c0 1.616 1.404 2.926 3.136 2.926zm3.206-28.2v.79h21.979v-1.58h-21.98z",viewW:90,viewH:50}}].map(e=>({id:e.id,group:e.group,category:e.category,w:e.w,h:e.h,art:e.art})),...nl.filter(e=>rl.has(e.id))],sl=new Map(ol.map(e=>[e.id,e]));function al(e){return e&&sl.get(e)||null}function ll(e){return ol.filter(t=>t.group===e)}function cl(e){const t=al(e);return t?{w:t.w,h:t.h}:{w:60,h:60}}function hl(e,t,i=Ta,n=1e3){const r=Number(t)>0?Number(t):5;return(Number(e)||0)/r*i/n}const dl=5e-4,ul=Sa;function pl(e){return Number.isFinite(e)?Math.max(dl,Math.min(ul,e)):dl}function ml(e){return Number.isFinite(e)?Math.max(1,Math.min(1e4,e)):1}const _l=e=>{const t=Math.round(1e3*e)/1e3;return Object.is(t,-0)?"0":String(t)};function fl(e){const t=al(e);if(!t)return null;if(t.art)return t.art;const i=function(e,t,i){if(!e.g)return"";const n=e=>_l(e*t),r=e=>_l(e*i),o=[];for(const s of e.g)if("r"===s[0]){const[,e,t,i,a]=s;o.push(`M${n(e)} ${r(t)}H${n(e+i)}V${r(t+a)}H${n(e)}Z`)}else if("l"===s[0]){const[,e,t,i,a]=s;o.push(`M${n(e)} ${r(t)}L${n(i)} ${r(a)}`)}else if("e"===s[0]){const[,e,a,l,c]=s;o.push(`M${n(e-l)} ${r(a)}A${_l(l*t)} ${_l(c*i)} 0 0 1 ${n(e+l)} ${r(a)}A${_l(l*t)} ${_l(c*i)} 0 0 1 ${n(e-l)} ${r(a)}Z`)}else{const e=s.slice(1);if(e.length<4)continue;let t=`M${n(e[0])} ${r(e[1])}`;for(let i=2;i+10?n:Number.isFinite(r)&&r>0?r:1,s=Number(t),a=o*(Number.isFinite(s)&&s>0?s:1);return Number.isFinite(a)&&a>0?a:o}const vl=e=>{let t=(e%360+360)%360;return t>180&&(t-=360),t};function yl(e,t,i,n,r,o=0){let s=null,a=r;for(const r of n){const[n,l,c,h]=r,d=c-n,u=h-l,p=d*d+u*u;if(!p)continue;const m=Math.sqrt(p);let _=((e-n)*d+(t-l)*u)/p;_=Math.max(0,Math.min(1,_));let f=n+_*d,g=l+_*u;const v=Math.hypot(e-f,t-g);if(!(v0){const e=Math.round(_*m/o)*o;f=n+e/m*d,g=l+e/m*u}s={cx:f+y*(i/2),cy:g+b*(i/2),angle:vl(180*Math.atan2(-y,b)/Math.PI),dist:v}}return s}function bl(e){const{symbol:t,widthCm:i,depthCm:n,point:r,canvasW:o,canvasH:s,cellCm:a,gridPitch:l,walls:c,wallReach:h,free:d=!1}=e;if(!(fl(t)&&s>0&&Number.isFinite(r[0])&&Number.isFinite(r[1])))return null;const u=pl(hl(i,a,l,o)),p=pl(hl(n,a,l,o));let m=r[0],_=r[1],f=0;const g=d?null:yl(m,_,p*s,c,h,l);return g&&(m=g.cx,_=g.cy,f=g.angle),{symbol:t,x:Pa(m/o-u/2),y:Pa(_/s-p/2),w:u,h:p,angle:Number(f.toFixed(2))}}const wl=15e3,kl=2e3,xl=new WeakMap;function $l(e,t){let i=xl.get(e);if(!i){const t=new Set;i={hiddenAt:"hidden"===e.visibilityState?Date.now():0,token:0,subscribers:t,onVisibility:()=>{const t=xl.get(e);if(!t)return;const i=Date.now();if("hidden"===e.visibilityState){t.hiddenAt||(t.hiddenAt=i);const e={kind:"hidden",token:t.token,at:i,hiddenFor:0,long:!1};for(const i of[...t.subscribers])i(e);return}const n=t.hiddenAt?Math.max(0,i-t.hiddenAt):0;t.hiddenAt=0,t.token++;const r={kind:"visible",token:t.token,at:i,hiddenFor:n,long:n>=wl};for(const e of[...t.subscribers])e(r)},onPageShow:t=>{const i=xl.get(e);if(!i)return;const n=Date.now();i.token++;const r={kind:"pageshow",token:i.token,at:n,hiddenFor:0,long:!!t.persisted,persisted:!!t.persisted};for(const e of[...i.subscribers])e(r)}},xl.set(e,i),e.addEventListener("visibilitychange",i.onVisibility),e.defaultView?.addEventListener("pageshow",i.onPageShow)}return i.subscribers.add(t),()=>{const i=xl.get(e);i&&(i.subscribers.delete(t),i.subscribers.size||(e.removeEventListener("visibilitychange",i.onVisibility),e.defaultView?.removeEventListener("pageshow",i.onPageShow),xl.delete(e)))}}const Sl={now:()=>Date.now(),setTimeout:(e,t)=>window.setTimeout(e,t),clearTimeout:e=>window.clearTimeout(e),requestAnimationFrame:e=>window.requestAnimationFrame(e),cancelAnimationFrame:e=>window.cancelAnimationFrame(e)};class Ml{constructor(e,t=Sl){this.onChange=e,this.clock=t,this._state="steady",this._token=0,this._frameFingerprint="",this._hasCompleteFrame=!1,this._overlayPhase="none",this._recoveryReason=null,this._overlayTimer=0,this._overlayRaf=0,this._overlayOpaqueAt=0,this._barrierRafs=new Set,this._trace=[],this._disposed=!1}get state(){return this._state}get token(){return this._token}get frameFingerprint(){return this._frameFingerprint}get hasCompleteFrame(){return this._hasCompleteFrame}get overlayPhase(){return this._overlayPhase}get overlayVisible(){return"none"!==this._overlayPhase}get overlayBlocksInteraction(){return this.overlayVisible}get recoveryReason(){return this._recoveryReason}get trace(){return this._trace.map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}note(e,t={}){this.record(e,void 0,t)}record(e,t,i={}){this._trace.push({at:this.clock.now(),token:this._token,event:e,state:this._state,...t?{reason:t}:{},...i}),this._trace.length>80&&this._trace.splice(0,this._trace.length-80)}changed(){this._disposed||this.onChange()}adoptCompleteFrame(e){e&&(this._hasCompleteFrame=!0,this._frameFingerprint=e,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-adopted"),this.changed())}markCompleteFrame(e){e&&(this._hasCompleteFrame=!0,this._frameFingerprint=e,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-complete"),this.changed())}refreshCompleteFrame(e){!this._disposed&&this._hasCompleteFrame&&"steady"===this._state&&e&&e!==this._frameFingerprint&&(this._frameFingerprint=e,this.record("frame-refreshed"))}visibility(e){return"hidden"===e.kind?(this.record("visibility-hidden"),this._token):e.long||"visible"!==e.kind&&"pageshow"!==e.kind?this.beginCandidate("pageshow"===e.kind?"pageshow":"long-resume"):(this.record("pageshow"===e.kind?"pageshow-noop":"visibility-visible-quick"),this._token)}beginCandidate(e,t="plan"){return this._token++,this._recoveryReason=t,this._hasCompleteFrame?(this._state="connection"===t?"offline-stale":"holding",this.clearOverlay()):this.overlayVisible?this._state="overlay-visible":(this._state="overlay-pending",this.scheduleOverlay(t)),this.record("candidate-start",e),this.changed(),this._token}connectionLost(){return this.beginCandidate("connection-lost","connection")}candidateReady(e){return!this._disposed&&e===this._token&&(this._state="candidate-ready",this.record("candidate-ready"),this.changed(),!0)}async commitAfterPaint(e,t){if(this._disposed||e!==this._token)return!1;let i=!0;const n=(async()=>{for(await t.updateComplete();i&&!this._disposed&&e===this._token;)if(t.stageValid()&&t.assetsReady()){if(await this.nextFrame(),e!==this._token||!t.stageValid()||!t.assetsReady())continue;if(await this.nextFrame(),e===this._token&&t.stageValid()&&t.assetsReady())return!0}else await this.nextFrame();return!1})();let r=0;const o=new Promise(e=>{r=this.clock.setTimeout(()=>e({ready:!1,timedOut:!0}),kl)}),s=await Promise.race([n.then(e=>({ready:e,timedOut:!1})),o]);return i=!1,this.clock.clearTimeout(r),!this._disposed&&e===this._token&&(!(!s.ready&&!s.timedOut)&&(s.ready?(this._hasCompleteFrame=!0,this._frameFingerprint=t.frameFingerprint(),this.record("paint-barrier"),this.finishOverlayAfterCommit(),!0):(this.record("paint-barrier-timeout"),this._hasCompleteFrame?(this._state="connection"===this._recoveryReason?"offline-stale":"steady",this.clearOverlay()):(this._state="recovery-error","opaque"!==this._overlayPhase&&this.showOverlayNow()),this.changed(),!1)))}retry(e=this._recoveryReason||"plan"){return this.beginCandidate("retry",e)}nextFrame(){return new Promise(e=>{const t=this.clock.requestAnimationFrame(()=>{this._barrierRafs.delete(t),e()});this._barrierRafs.add(t)})}scheduleOverlay(e){this.clearOverlayTimer(),this._recoveryReason=e,this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._hasCompleteFrame||"overlay-pending"!==this._state&&"candidate-ready"!==this._state||(this._state="overlay-visible",this._overlayPhase="entering",this.record("overlay-enter"),this.changed(),this._overlayRaf=this.clock.requestAnimationFrame(()=>{this._overlayRaf=0,"entering"===this._overlayPhase&&(this._overlayPhase="fading-in",this.changed(),this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,"fading-in"===this._overlayPhase&&(this._overlayPhase="opaque",this._overlayOpaqueAt=this.clock.now(),this.record("overlay-opaque"),this.changed())},150))}))},150)}showOverlayNow(){this.clearOverlayTimer(),this._overlayPhase="opaque",this._overlayOpaqueAt=this.clock.now(),this.record("overlay-error")}finishOverlayAfterCommit(){if(this.clearOverlayTimer(),"none"===this._overlayPhase||"entering"===this._overlayPhase||"fading-in"===this._overlayPhase)return this.clearOverlay(),this._state="steady",this._recoveryReason=null,this.record("candidate-committed"),void this.changed();const e=Math.max(0,this._overlayOpaqueAt+250-this.clock.now());this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._overlayPhase="leaving",this.record("overlay-leave"),this.changed(),this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._overlayPhase="none",this._state="steady",this._recoveryReason=null,this.record("candidate-committed"),this.changed()},180)},e)}clearOverlayTimer(){this._overlayTimer&&this.clock.clearTimeout(this._overlayTimer),this._overlayTimer=0}clearOverlay(){this.clearOverlayTimer(),this._overlayRaf&&this.clock.cancelAnimationFrame(this._overlayRaf),this._overlayRaf=0,this._overlayPhase="none",this._overlayOpaqueAt=0}dispose(){this._disposed=!0,this._token++,this.clearOverlay();for(const e of this._barrierRafs)this.clock.cancelAnimationFrame(e);this._barrierRafs.clear()}}function Rl(e){let t=2166136261,i=2654435769;const n=new WeakSet,r=e=>{for(let n=0;n>>0)+(i>>>2)}},o=e=>{if(null===e)return void r("n");const t=typeof e;if("string"===t)return void r(`s${e.length}:${e}`);if("number"===t)return void r(`d${Number.isNaN(e)?"NaN":String(e)}`);if("boolean"===t)return void r(e?"t":"f");if("undefined"===t)return void r("u");if("object"!==t)return void r(`${t}:${String(e)}`);const i=e;if(n.has(i))r("[cycle]");else{if(n.add(i),Array.isArray(e)){r("[");for(const t of e)o(t);return r("]"),void n.delete(i)}r("{");for(const t of Object.keys(e).sort())r(t),o(e[t]);r("}"),n.delete(i)}};return o(e),`${(t>>>0).toString(16).padStart(8,"0")}${(i>>>0).toString(16).padStart(8,"0")}`}function Tl(e){return Rl(e)}const Cl=new WeakMap;class Dl{constructor(e,t=()=>Date.now()){this.onUpdate=e,this.now=t,this.fallbackAuthority={},this.referenceOwner={},this.referenced=new Set,this.queued=new Set,this.disposed=!1,this.sharedUpdate=()=>{this.disposed||this.onUpdate()}}bind(e){const t=((e,t)=>{const i=e?.connection||e?.callWS||t;return!i||"object"!=typeof i&&"function"!=typeof i?t:i})(e,this.fallbackAuthority);return this.shared&&this.authority===t||(this.shared&&(this.shared.listeners.delete(this.sharedUpdate),this.shared.references.delete(this.referenceOwner)),this.authority=t,this.shared=(e=>{let t=Cl.get(e);return t||(t={signed:{},queued:new Set,inFlight:new Map,retry:new Map,listeners:new Set,settlers:new Set,references:new Map},Cl.set(e,t)),t})(t),this.disposed||(this.shared.listeners.add(this.sharedUpdate),this.shared.references.set(this.referenceOwner,new Set(this.referenced)))),this.shared}start(e,t){this.disposed=!1,this.referenced=new Set(t());const i=this.bind(e());i.listeners.add(this.sharedUpdate),i.references.set(this.referenceOwner,new Set(this.referenced)),this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(e(),t()),288e5)}dispose(){if(this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.shared){this.shared.listeners.delete(this.sharedUpdate),this.shared.references.delete(this.referenceOwner);for(const e of this.queued)this.shared.queued.delete(e)}this.queued.clear()}invalidate(e){const t=this.bind(e);clearTimeout(this.batchTimer),this.queued.clear(),t.queued.clear(),t.inFlight.clear(),t.retry.clear(),t.signed={}}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(e,t){const i=_n(t);if(!i.startsWith("/api/houseplan/content/"))return i;const n=this.bind(e);this.referenced.add(i),n.references.set(this.referenceOwner,new Set(this.referenced));const r=n.signed[i],o=r?this.now()-r.at:1/0;return o{const t=[...this.queued];this.queued.clear();for(const e of t)i.queued.delete(e);this.sign(e,t)},30))}sign(e,t){if(!t.length||!e?.callWS)return;const i=this.bind(e);for(const n of function(e,t){const i=Math.max(1,Math.floor(t)),n=[];for(let t=0;t{const n=i.inFlight.get(e);return!(void 0!==n&&t-n<15e3)&&(i.inFlight.set(e,t),!0)});r.length&&e.callWS({type:"houseplan/content/sign",paths:r}).then(e=>{const t=this.now();let n=0,o=!1;for(const s of r){const r=e?.urls?.[s];if("string"==typeof r&&r){const e=i.signed[s];e?.loaded&&e.url!==r?(e.pending={url:r,at:t},this.preloadReplacement(i,s,r,t)):(i.signed[s]={url:r,at:t,loaded:e?.url===r&&!!e.loaded},o=!0),i.retry.delete(s),n++}else this.backOff(i,s)}if(n&&(this.trimShared(i),o))for(const e of[...i.listeners])e()}).catch(()=>{for(const e of r)this.backOff(i,e)}).finally(()=>{for(const e of r)i.inFlight.get(e)===t&&i.inFlight.delete(e);for(const e of[...i.settlers])e()})}}preloadReplacement(e,t,i,n){const r=()=>{const r=e.signed[t];if(r?.pending?.url===i){e.signed[t]={url:i,at:n,loaded:!0},e.retry.delete(t),this.trimShared(e);for(const t of[...e.listeners])t()}},o=()=>{const n=e.signed[t];n?.pending?.url===i&&(delete n.pending,this.backOff(e,t))};if("undefined"==typeof Image)return void r();const s=new Image;s.onload=()=>{("function"==typeof s.decode?s.decode():Promise.resolve()).then(r).catch(o)},s.onerror=o,s.src=i}preloadCurrentImage(e,t,i){if(i.loaded)return Promise.resolve(!0);if(i.preload)return i.preload;if("undefined"==typeof Image)return i.loaded=!0,Promise.resolve(!0);const n=i.url;return i.preload=new Promise(r=>{const o=new Image;let s=!1;const a=o=>{if(s)return;s=!0,clearTimeout(l);const a=e.signed[t];if(a===i&&delete a.preload,o&&a===i&&a.url===n){a.loaded=!0;for(const t of[...e.listeners])t();r(!0)}else o||a!==i||this.backOff(e,t),r(!1)};o.onload=()=>{("function"==typeof o.decode?o.decode():Promise.resolve()).then(()=>a(!0)).catch(()=>a(!1))},o.onerror=()=>a(!1);const l=setTimeout(()=>a(!1),kl);o.src=n}),i.preload}prepareImage(e,t){const i=_n(t);if(!i.startsWith("/api/houseplan/content/"))return Promise.resolve(!0);const n=this.bind(e),r=()=>{const t=n.signed[i];if(t&&this.now()-t.atthis.now()&&!n.inFlight.has(i)&&!n.queued.has(i)?Promise.resolve(!1):null},o=r();return o?(s=o,new Promise(e=>{let t=!1;const i=i=>{t||(t=!0,clearTimeout(n),e(i))},n=setTimeout(()=>i(!1),kl);s.then(i).catch(()=>i(!1))})):new Promise(e=>{let t=!1,i=null;const o=i=>{t||(t=!0,clearTimeout(a),n.settlers.delete(s),e(i))},s=()=>{t||i||(i=r(),i&&i.then(o).catch(()=>o(!1)))},a=setTimeout(()=>o(!1),kl);n.settlers.add(s),s()});var s}backOff(e,t){const i=e.retry.get(t)?.delay||0,n=Math.min(6e4,i?2*i:2e3);e.retry.set(t,{notBefore:this.now()+n,delay:n})}trimShared(e){const t=Object.entries(e.signed);if(t.length<=512)return;const i=new Set(e.inFlight.keys());for(const t of e.references.values())for(const e of t)i.add(e);for(const[e,n]of t)(n.pending||n.preload)&&i.add(e);t.sort((e,t)=>t[1].at-e[1].at);const n=t.filter(([e])=>i.has(e));for(const e of t){if(n.length>=512)break;i.has(e[0])||n.push(e)}e.signed=Object.fromEntries(n)}resign(e,t){const i=this.bind(e);this.referenced=new Set(t),i.references.set(this.referenceOwner,new Set(this.referenced));const n=this.now(),r=new Set;for(const e of i.references.values())for(const t of e)r.add(t);const o={};for(const[e,t]of Object.entries(i.signed))r.has(e)&&n-t.at!!o[e]&&!o[e].pending))}markLoaded(e,t,i){const n=_n(t);if(!n.startsWith("/api/houseplan/content/"))return;const r=this.bind(e).signed[n];if(r&&(!i||r.url===i)&&!r.loaded){r.loaded=!0;for(const t of[...this.bind(e).listeners])t()}}isReady(e,t){const i=_n(t);if(!i.startsWith("/api/houseplan/content/"))return!0;const n=this.bind(e).signed[i];return!!n&&this.now()-n.ate.id===t)}var Al="M11,18H13V16H11V18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,6A4,4 0 0,0 8,10H10A2,2 0 0,1 12,8A2,2 0 0,1 14,10C14,12 11,11.75 11,15H13C13,12.75 16,12.5 16,10A4,4 0 0,0 12,6Z";function Pl(e,t,i){return[e[0]*t+e[1]*i+e[2],e[3]*t+e[4]*i+e[5]]}function Ol(e,t){let i=0;for(const[n,r]of t){const t=Pl(e,n[0],n[1]);i=Math.max(i,Math.hypot(t[0]-r[0],t[1]-r[1]))}return i}const Fl=e=>{if(null==e||""===e)return null;const t=Number(e);return Number.isFinite(t)?t:null},Il=e=>{const t=e,i=Fl(Array.isArray(t)?t[0]:t?.x),n=Fl(Array.isArray(t)?t[1]:t?.y);return null==i||null==n?null:[i,n]};function El(e){if(!Array.isArray(e))return null;const t=[];for(const i of e){const e=Il(i);if(!e)return null;t.push(e)}if(t.length>1){const e=t[0],i=t[t.length-1];e[0]===i[0]&&e[1]===i[1]&&t.pop()}if(t.length<3)return null;let i=0,n=0,r=0;for(let e=0;ee+t[0],0)/t.length,t.reduce((e,t)=>e+t[1],0)/t.length]:[n/(3*i),r/(3*i)]}function Hl(e){if(!Array.isArray(e))return null;const t=[];for(const i of e){const e=Il(i);if(!e)return null;t.push(e)}if(t.length>1){const e=t[0],i=t[t.length-1];e[0]===i[0]&&e[1]===i[1]&&t.pop()}const i=El(t);if(!i)return null;const n=t.map(e=>e[0]),r=t.map(e=>e[1]);return{centroid:i,bbox:[Math.min(...n),Math.min(...r),Math.max(...n),Math.max(...r)]}}function Nl(e){let t=e;if(t&&"object"==typeof t&&!Array.isArray(t)){const e=t;t=e.path??e.points??t}if(!Array.isArray(t)||!t.length)return[];let i=(t.some(e=>(e=>Array.isArray(e)?e.length>=2&&!Array.isArray(e[0])&&("object"!=typeof e[0]||null==e[0]):!!e&&"object"==typeof e&&("x"in e||"y"in e))(e))?[t]:t.filter(Array.isArray)).map(e=>e.map(Il).filter(e=>null!=e)).filter(e=>e.length>=2);i.length>64&&(i=i.slice(-64));const n=i.reduce((e,t)=>e+t.length,0);if(n<=4e3)return i.map(e=>e.slice());const r=4e3-2*i.length,o=i.map(e=>e.length-2),s=o.reduce((e,t)=>e+t,0),a=o.map(e=>r*e/s),l=a.map(Math.floor);let c=r-l.reduce((e,t)=>e+t,0);const h=a.map((e,t)=>({index:t,fraction:e-Math.floor(e)})).sort((e,t)=>t.fraction-e.fraction||e.index-t.index);for(let e=0;efunction(e,t){if(t>=e.length)return e.slice();if(t<=2)return[e[0],e[e.length-1]];const i=[];for(let n=0;ne.slice()),i=t[t.length-1];return i?(i.length>2?i.pop():t.pop(),t):[]}const ql=e=>Number.isFinite(e[0])&&Number.isFinite(e[1]),Bl=(e,t)=>e[0]===t[0]&&e[1]===t[1];function jl(e){if(!e)return null;const t=e.vacuum_position||e.robot_position||null,i=t&&null!=Fl(t.x)&&null!=Fl(t.y)?{x:Fl(t.x),y:Fl(t.y),a:Fl(t.a??t.angle??t.theta)}:null,n=Nl(e.path?.path??e.path?.points??e.path),r=[],o=e.rooms,s=Array.isArray(o)?o.map((e,t)=>[String(e?.id??t),e]):o&&"object"==typeof o?Object.entries(o):[];for(const[e,t]of s){if(!t||"object"!=typeof t)continue;const i=String(t.name??t.label??"").trim(),n=[[Fl(t.cx),Fl(t.cy)],[Fl(t.center?.x),Fl(t.center?.y)],[Fl(t.x),Fl(t.y)]].find(([e,t])=>null!=e&&null!=t);let o=n?.[0]??null,s=n?.[1]??null;const a=Hl(t.outline),l=Fl(t.x0),c=Fl(t.y0),h=Fl(t.x1),d=Fl(t.y1),u=(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)||a?.bbox||null;if(null!=o&&null!=s||!a||([o,s]=a.centroid),null!=o&&null!=s||!u||(o=(u[0]+u[2])/2,s=(u[1]+u[3])/2),i&&null!=o&&null!=s){const t={id:e,name:i,cx:o,cy:s};u&&([t.x0,t.y0,t.x1,t.y1]=u),r.push(t)}}const a=function(e){return String(e.map_name??e.current_map??e.map_index??e.selected_map??"default")}(e);return i||r.length||n.length?{pos:i,path:n,rooms:r,mapId:a}:null}function Ul(e,t,i){return![e,t,i].every(Number.isFinite)||e<0||t<=0||i<=0?Number.POSITIVE_INFINITY:e/t*i}const Vl=40;function Gl(e,t,i){if(!e||!t)return null;const n=t.attributes||{},r=e.split(".")[0]||"",o=null!=i?.platform?String(i.platform):null,s=!!Il(n.vacuum_position??n.robot_position),a=!(!n.rooms||"object"!=typeof n.rooms),l=Nl(n.path?.path??n.path?.points??n.path).length>0,c=[n.map_name,n.current_map,n.map_index,n.selected_map].some(e=>null!=e),h="xiaomi_cloud_map_extractor"===o,d=a||l||c;let u=null;if(s?u="compatible":h?u="known_xcme_incomplete":d?u="partial":"camera"===r&&(u="camera"),!u)return null;const p=s?"camera"===r?300:200:h?100:d?50:0;return{entityId:e,name:String(n.friendly_name||e),platform:o,category:u,hasPosition:s,hasRooms:a,hasPath:l,hasMapId:c,score:p}}const Kl=e=>e.toLowerCase().replace(/[\s_\-.,]+/g,"");function Yl(e,t){const i=new Set(t.map(Kl).filter(Boolean)),n=new Set;for(const t of e){const e=Kl(t.name||"");e&&i.has(e)&&n.add(e)}return n.size}function Xl(e,t){const i=new Map(t.map(e=>[Kl(e.name),e])),n=[],r=[];for(const t of e){const e=i.get(Kl(t.name));e&&(n.push([[t.cx,t.cy],[e.cx,e.cy]]),r.push(t.name))}if(n.length<3)return null;const o=function(e){if(e.length<3)return null;let t=0,i=0,n=0,r=0,o=0,s=0,a=0,l=0,c=0,h=0,d=0,u=0;for(const[[p,m],[_,f]]of e){if(![p,m,_,f].every(Number.isFinite))return null;t+=p*p,i+=p*m,n+=p,r+=m*m,o+=m,s+=1,a+=p*_,l+=m*_,c+=_,h+=p*f,d+=m*f,u+=f}const p=[t,i,n,i,r,o,n,o,s],m=e=>{const[t,i,n,r,o,s,a,l,c]=p,h=t*(o*c-s*l)-i*(r*c-s*a)+n*(r*l-o*a);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(o*c-s*l)/h,(n*l-i*c)/h,(i*s-n*o)/h,(s*a-r*c)/h,(t*c-n*a)/h,(n*r-t*s)/h,(r*l-o*a)/h,(i*a-t*l)/h,(t*o-i*r)/h];return[d[0]*e[0]+d[1]*e[1]+d[2]*e[2],d[3]*e[0]+d[4]*e[1]+d[5]*e[2],d[6]*e[0]+d[7]*e[1]+d[8]*e[2]]},_=m([a,l,c]),f=m([h,d,u]);if(!_||!f)return null;const g=[_[0],_[1],_[2],f[0],f[1],f[2]];return g.every(Number.isFinite)?g:null}(n);return o?{matrix:o,matched:r,residual:Ol(o,n)}:null}function Zl(e,t,i){const n=e[e.length-1];if(n&&n[0]===t[0]&&n[1]===t[1])return e;if(e.push(t),e.length<=600)return e;let r=function(e,t){if(e.length<3)return e.slice();const i=new Uint8Array(e.length);i[0]=i[e.length-1]=1;const n=[[0,e.length-1]];for(;n.length;){const[r,o]=n.pop(),[s,a]=e[r],[l,c]=e[o],h=l-s,d=c-a,u=Math.hypot(h,d)||1e-9;let p=0,m=-1;for(let t=r+1;tp&&(p=i,m=t)}m>0&&p>t&&(i[m]=1,n.push([r,m],[m,o]))}const r=[];for(let t=0;t600&&(r=r.filter((e,t)=>t%2==0||t===r.length-1)),r}function Jl(e){return"cleaning"===e||"returning"===e||"on"===e}const Ql={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function ec(e){const[t,i]=Ql[e.rot]||[1,0],n=e.mir?-1:1;return[e.s*t*n,-e.s*i,e.ox,e.s*i*n,e.s*t,e.oy]}function tc(e){const t=e[0]*e[4]-e[1]*e[3];if(!Number.isFinite(t)||Math.abs(t)<1e-12)return null;const i=t<0,n=Math.sqrt(Math.abs(t));let r=180*Math.atan2(-e[1],e[4])/Math.PI;return r=(90*Math.round(r/90)%360+360)%360,{ox:e[2],oy:e[5],s:n,rot:r,mir:i}}function ic(e,t){const i=[],n=[];for(const t of e)null!=t.x0?(i.push(t.x0,t.x1),n.push(t.y0,t.y1)):(i.push(t.cx),n.push(t.cy));if(!i.length)return{ox:t[0]+t[2]/2,oy:t[1]+t[3]/2,s:t[2]/1e4,rot:0,mir:!0};const r=Math.min(...i),o=Math.max(...i),s=Math.min(...n),a=Math.max(...n),l=Math.max(o-r,a-s)||1,c={ox:0,oy:0,s:.6*Math.min(t[2],t[3])/l,rot:0,mir:!0},h=ec(c),[d,u]=Pl(h,(r+o)/2,(s+a)/2);return c.ox=t[0]+t[2]/2-d,c.oy=t[1]+t[3]/2-u,c}function nc(e,t,i,n){const[r,o]=Pl(ec(t),i,n),s=ec({...e,ox:0,oy:0}),[a,l]=Pl(s,i,n);return{...e,ox:r-a,oy:o-l}}function rc(e){const t=e?.trail_mode;return"never"===t||"cleaning"===t||"always"===t?t:!1===e?.trail?"never":"cleaning"}const oc={availability:"available",status:"alarm",activity:"none"},sc=new Set(["motion","vibration","sound"]),ac=new Set(["occupancy","presence"]),lc=new Set(["door","window","garage_door","opening"]),cc=new Set(["running","power"]),hc=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const dc=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),uc=new Set([...dc,"start","started","run","active","in_progress","wash","rinse","spin","dry"]),pc=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping","stop","end","done","inactive"]),mc=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),_c=e=>""===e||"unknown"===e||"unavailable"===e||"__missing__"===e,fc=e=>String(e??"").trim().toLowerCase(),gc=e=>fc(e).replace(/[\s-]+/g,"_").replace(/_+/g,"_").replace(/^_|_$/g,""),vc=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]]),yc=new Set(["wifi","connection","signal","battery"]);function bc(e,t){const i=e?.entities?.[t]||{};if("config"===i.entity_category)return null;const n=String(t||"").split(".").slice(1).join("."),r=[i.translation_key,i.original_name,i.name],o=[...r,n];r.some(e=>fc(e))||o.push(e?.states?.[t]?.attributes?.friendly_name);const s=o.map(gc).filter(Boolean);if(s.some(e=>e.split("_").some(e=>yc.has(e))))return null;let a=null;for(const e of s)for(const[t,i]of vc)(e===t||e.endsWith(`_${t}`))&&(a=null==a?i:Math.min(a,i));return a}const wc=(e,t)=>null!=bc(e,t);function kc(e,t){if(!t.startsWith("switch."))return!1;const i=e?.entities?.[t]||{},n=e?.states?.[t],r=t.slice(7).toLowerCase();if(/(?:^|_)(?:main_)?power$/.test(r))return!0;if([i.translation_key,i.original_name,i.name].map(fc).some(e=>["power","main power","power switch","питание"].includes(e)))return!0;const o=fc(n?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(o)||/(?:^|[\s._-])питание$/.test(o)}function xc(e){for(const t of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=fc(e?.[t]);if(dc.has(i)||pc.has(i))return i}return""}function $c(e,t){const i=e?.states?.[t],n=i?fc(i.state):"__missing__",r=String(t||"").split(".")[0],o=fc(i?.attributes?.device_class),s={eid:t,state:n,availability:_c(n)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===s.availability)return s;if(function(e,t,i){return"alarm_control_panel"===e?"triggered"===i:"on"===i&&Sn(e,t)}(r,o,n))return{...s,status:"alarm"};if("binary_sensor"===r)return sc.has(o)?{...s,edge:"rising"}:ac.has(o)?{...s,activity:"on"===n?"presence":"none"}:lc.has(o)?{...s,status:"on"===n?"open":"neutral",edge:"rising"}:"moving"===o?{...s,activity:"on"===n?"transition":"none"}:cc.has(o)&&"on"===n?{...s,status:"working",activity:"running"}:s;if("cover"===r)return{...s,activity:"opening"===n||"closing"===n?"transition":"none",edge:"terminal_transition"};if("lock"===r)return{...s,status:"unlocked"===n||"open"===n?"open":"neutral",activity:"locking"===n||"unlocking"===n?"transition":"none",edge:"terminal_transition"};if("valve"===r)return{...s,status:["open","opening","closing"].includes(n)?"open":"neutral",activity:"opening"===n||"closing"===n?"transition":"none",edge:"terminal_transition"};if("climate"===r){const e=xc(i.attributes),t=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(fc):[],r=!pc.has(n)&&(mc.has(n)||dc.has(n)||t.includes(n));return(e?dc.has(e):r)?{...s,status:"working",activity:"running"}:s}if(["light","switch","fan","humidifier"].includes(r))return"on"===n?{...s,status:"working",activity:"running"}:s;if("media_player"===r)return"off"===n?{...s,availability:"unavailable"}:s;if("vacuum"===r)return"cleaning"===n?{...s,status:"working",activity:"running"}:"returning"===n?{...s,status:"working",activity:"transition"}:s;if("script"===r)return"on"===n?{...s,status:"working",activity:"running"}:s;if("automation"===r)return s;if("button"===r||"event"===r)return{...s,edge:"change"};const a=xc(i.attributes);return dc.has(a)||dc.has(n)&&!pc.has(n)?{...s,status:"working",activity:"running"}:s}function Sc(e,t,i){const n=i.filter(t=>t.startsWith("switch.")&&!e?.entities?.[t]?.entity_category),r=t.find(t=>kc(e,t));if(!(n.length>1&&!!r))return t.map(t=>$c(e,t));const o=t.find(t=>t!==r&&wc(e,t)),s=$c(e,r);return"unavailable"===s.availability||"off"===s.state?t.map(t=>({eid:t,state:e?.states?.[t]?fc(e.states[t].state):"__missing__",availability:"unavailable",status:"neutral",activity:"none",edge:"none"})):t.map(t=>{if(t===o)return function(e,t){const i=e?.states?.[t],n=i?fc(i.state):"__missing__",r={eid:t,state:n,availability:_c(n)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===r.availability)return r;const o=xc(i?.attributes);return dc.has(o)||uc.has(n)&&!pc.has(n)?{...r,status:"working",activity:"running"}:r}(e,t);return{...t===r?s:$c(e,t),status:"neutral",activity:"none",edge:"none"}})}function Mc(e){if(!e.length)return{availability:"available",status:"neutral",activity:"none"};const t=e.filter(e=>"available"===e.availability);if(!t.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(t.some(e=>"alarm"===e.status))return oc;const i=t.some(e=>"working"===e.status)?"working":t.some(e=>"open"===e.status)?"open":"neutral",n=t.some(e=>"transition"===e.activity)?"transition":t.some(e=>"presence"===e.activity)?"presence":t.some(e=>"running"===e.activity)?"running":"none";return{availability:"available",status:i,activity:n}}function Rc(e,t){if(!(e=>!!e&&!_c(e))(e)||"unavailable"===t.availability||e===t.state)return null;if("rising"===t.edge)return"off"===e&&"on"===t.state?"event":null;if("change"===t.edge)return"event";if("terminal_transition"===t.edge){const i=new Set([e,t.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}const Tc=(e,t=0)=>{const i=Number(e);return Number.isInteger(i)&&i>=0?i:t},Cc=(e,t)=>{const i={rev:0,configRev:Tc(t),off:new Set};if(null==e)return{snapshot:i,valid:!1};const n=e.off;return!Number.isInteger(e.rev)||Number(e.rev)<0||!Number.isInteger(e.config_rev)||Number(e.config_rev)<0||!Array.isArray(n)||n.some(e=>"string"!=typeof e||!e)?{snapshot:i,valid:!1}:{snapshot:{rev:Number(e.rev),configRev:Number(e.config_rev),off:new Set(n)},valid:!0}};function Dc(e,t=0){return Cc(e,t).snapshot}function zc(e,t,i,n){const r=Cc(t,i);return n&&r.valid&&r.snapshot.configRev===i?e.configRev===i&&r.snapshot.rev{try{const[i,n]=await Promise.all([e.callWS({type:"config/device_registry/list"}),e.callWS({type:"config/entity_registry/list"})]),r=Bc(i,"id"),o=Bc(n,"entity_id");if(!r||!o)throw new Error("invalid_registry_response");t.devices=r,t.entities=o,t.authoritative=!0,t.access="full",t.lastSuccess=Date.now(),t.error=void 0}catch(e){t.authoritative=!1,t.access="limited",t.error=function(e){if(e&&"object"==typeof e){const t=e;return String(t.message||t.code||t.error||"registry_unavailable")}return String(e||"registry_unavailable")}(e)}finally{t.revision++,t.loading=void 0,function(e){for(const t of[...e.listeners])try{t()}catch{}}(t)}})()),t.loading}function Uc(e,t){void 0===t.reloadTimer&&(t.reloadTimer=globalThis.setTimeout(()=>{t.reloadTimer=void 0,jc(e,t)},80))}async function Vc(e,t){if(t.subscribing||t.unsubDevice&&t.unsubEntity)return t.subscribing;const i=e?.connection?.subscribeEvents;return"function"==typeof i?(t.subscribing=(async()=>{try{t.unsubDevice||(t.unsubDevice=await i.call(e.connection,()=>Uc(e,t),"device_registry_updated")),t.unsubEntity||(t.unsubEntity=await i.call(e.connection,()=>Uc(e,t),"entity_registry_updated"))}catch{}finally{0===t.refs&&(t.unsubDevice?.(),t.unsubEntity?.(),t.unsubDevice=void 0,t.unsubEntity=void 0),t.subscribing=void 0}})(),t.subscribing):void 0}function Gc(e,t){const i=qc(e);if(!i)return()=>{};const n=0===i.refs;i.refs++,i.listeners.add(t),("pending"===i.access||n)&&jc(e,i),Vc(e,i);let r=!1;return()=>{r||(r=!0,i.listeners.delete(t),i.refs=Math.max(0,i.refs-1),i.refs>0||(i.unsubDevice?.(),i.unsubEntity?.(),i.unsubDevice=void 0,i.unsubEntity=void 0,void 0!==i.reloadTimer&&globalThis.clearTimeout(i.reloadTimer),i.reloadTimer=void 0))}}function Kc(e){const t=qc(e);if(!t)return{revision:0,authoritative:!1,access:"limited",devices:e?.devices||{},entities:e?.entities||{},lastSuccess:0,error:"registry_unavailable"};let i=e?.devices||{},n=e?.entities||{};if(t.authoritative){const r=t.liveDevices!==i||t.liveEntities!==n,o=void 0!==t.liveDevices||void 0!==t.liveEntities;if(r&&(t.liveDevices=i,t.liveEntities=n,t.projectedRevision=void 0,o&&Uc(e,t)),t.projectedRevision!==t.revision||!t.projectedDevices||!t.projectedEntities){const e={...t.devices},r={...t.entities};for(const[t,n]of Object.entries(i))Object.prototype.hasOwnProperty.call(e,t)||(e[t]=n);for(const[e,t]of Object.entries(n))Object.prototype.hasOwnProperty.call(r,e)||(r[e]=t);t.projectedDevices=e,t.projectedEntities=r,t.projectedRevision=t.revision}i=t.projectedDevices,n=t.projectedEntities}return{revision:t.revision,authoritative:t.authoritative,access:t.access,devices:i,entities:n,lastSuccess:t.lastSuccess,error:t.error}}function Yc(e){if(!e||"object"!=typeof e&&"function"!=typeof e)return 0;let t=Hc.get(e);return t||(t=Nc++,Hc.set(e,t)),t}function Xc(e,t=Kc(e)){return[t.revision,t.access,Yc(t.devices),Yc(t.entities)].join(":")}function Zc(e){return!!e&&null==e.disabled_by}function Jc(e,t=Kc(e)){const i={},n={},r={};for(const[e,n]of Object.entries(t.devices||{}))Zc(n)&&(i[e]=n);for(const[e,i]of Object.entries(t.entities||{})){if(!Zc(i))continue;const r=i.device_id?t.devices?.[i.device_id]:null;t.authoritative&&i.device_id&&!r||(r&&!Zc(r)||(n[e]=i))}for(const[i,n]of Object.entries(e?.states||{})){const e=t.entities?.[i];if(e&&!Zc(e))continue;const o=e?.device_id?t.devices?.[e.device_id]:null;t.authoritative&&e?.device_id&&!o||(o&&!Zc(o)||(r[i]=n))}return{...e,devices:i,entities:n,states:r}}function Qc(e,t=Kc(e)){return{...e,devices:t.devices||{},entities:t.entities||{}}}function eh(e,t){const i=[];for(const[n,r]of Object.entries(e||{}))r?.device_id===t&&i.push(n);return i}function th(){if(Wc)return Wc;if("undefined"==typeof localStorage)return{};try{const e=JSON.parse(localStorage.getItem(Lc)||"{}");if(!e||"object"!=typeof e||Array.isArray(e))return{};const t=Date.now(),i={};for(const[n,r]of Object.entries(e))!r||"active"!==r.kind&&"ha_disabled"!==r.kind||!Number.isFinite(r.ts)||t-r.ts>7776e6||(i[n]=r);return Wc=i,Wc}catch{return{}}}function ih(e,t,i=Kc(e)){if(!t||"virtual"===t)return{kind:"active",enabledEntityIds:[],allEntityIds:[]};const n=t.indexOf(":");if(n<1)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const r=t.slice(0,n),o=t.slice(n+1);if("device"!==r&&"entity"!==r||!o)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const s=i.devices||{},a=i.entities||{};if(i.authoritative){if("device"===r){const e=s[o];if(!e)return{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[]};const t=eh(a,o);if(!Zc(e))return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:t};const i=t.filter(e=>Zc(a[e]));return t.length&&!i.length?{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:t}:{kind:"active",enabledEntityIds:i,allEntityIds:t}}const t=a[o];return t&&!Zc(t)?{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]}:t?.device_id&&!s[t.device_id]?{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[o]}:t?.device_id&&!Zc(s[t.device_id])?{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]}:t||e?.states?.[o]?{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}:{kind:"orphaned",reason:"entity_missing",enabledEntityIds:[],allEntityIds:[]}}const l=function(e){return th()[e]||null}(t);if("device"===r){const t=s[o],i=eh(a,o);if(null!=t?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:i};const n=i.filter(e=>{const t=a[e];return null==t?.disabled_by&&(!t.device_id||null==s[t.device_id]?.disabled_by)});if(i.length&&!n.length&&i.every(e=>null!=a[e]?.disabled_by))return{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:i};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"device",enabledEntityIds:[],allEntityIds:i};if(t||n.some(t=>!!e?.states?.[t]))return{kind:"active",enabledEntityIds:n,allEntityIds:i}}else{const t=a[o];if(null!=t?.disabled_by)return{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]};if(t?.device_id&&null!=s[t.device_id]?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"entity",enabledEntityIds:[],allEntityIds:[o]};if(t||e?.states?.[o])return{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}}return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]}}function nh(e){const t={};for(const[i,n]of Object.entries(e.entities)){if(!n?.device_id||!Zc(n))continue;const r=e.devices?.[n.device_id];r&&!Zc(r)||(t[n.device_id]=t[n.device_id]||[]).push(i)}return t}function rh(e,t){const i=new Map;for(const n of e){if(!0===n?.removed)continue;const e=n?.binding||"";if(!e.startsWith("entity:"))continue;const r=e.slice(7);if(!r)continue;const o=t?.entities?.[r]?.device_id;if(!o)continue;const s=i.get(o)||new Set;s.add(r),i.set(o,s)}return{byDevice:i}}function oh(e,t,i,n){const r=n.byDevice.get(t);return r?.size?{partial:!0,entityIds:i.filter(t=>!r.has(t)&&!e?.entities?.[t]?.hidden)}:{partial:!1,entityIds:[...i]}}function sh(e,t,i){if(t.identifiers?.[0]?.[0])return t.identifiers[0][0];for(const t of i){const i=e.entities[t]?.platform;if(i)return i}return""}function ah(e,t){if(/_device_temperature$/.test(t))return!1;if(e.entities?.[t]?.entity_category)return!1;const i=e.states[t];if(!i)return/_temperature$/.test(t);const n=i.attributes||{};return"temperature"===n.device_class||/°C|°F/.test(n.unit_of_measurement||"")||/_temperature$/.test(t)}const lh=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],ch=e=>[...e.filter(e=>!e.reg?.hidden),...e.filter(e=>!!e.reg?.hidden)];function hh(e,t){const i=t.map(t=>({eid:t,reg:e?.entities?.[t]})).filter(e=>!!e.reg);if(!i.length)return[];const n=i.filter(e=>!e.reg.entity_category),r=n.length?n:i;for(const e of lh){const t=r.filter(t=>t.eid.startsWith(e+"."));if(t.length)return ch(t).map(e=>e.eid)}const o=r.filter(t=>function(e,t){if(!t.startsWith("binary_sensor."))return!1;const i=fc(e?.states?.[t]?.attributes?.device_class||e?.entities?.[t]?.device_class||e?.entities?.[t]?.original_device_class);return sc.has(i)||ac.has(i)||lc.has(i)||cc.has(i)||"moving"===i||hc.has(i)}(e,t.eid));if(o.length)return ch(o).map(e=>e.eid);const s=r.filter(e=>e.eid.startsWith("switch."));if(s.length){const t=ch(s),n=t.find(t=>kc(e,t.eid));if(s.length>1&&n){const t=i.filter(t=>t.eid!==n.eid&&"config"!==t.reg?.entity_category&&null!=bc(e,t.eid)),r=t.filter(e=>!e.reg?.entity_category),o=r.length?r:t;for(const t of[0,1,2]){const i=ch(o.filter(i=>bc(e,i.eid)===t));if(i.length)return[i[0].eid,n.eid]}}return[(n||t[0]).eid]}const a=[];for(const e of xe)a.push(...ch(r.filter(t=>t.eid.startsWith(e+"."))));return a.push(...ch(r.filter(e=>!xe.includes(e.eid.split(".")[0])))),a.map(e=>e.eid)}function dh(e,t,i){const n=t.map(t=>({eid:t,reg:e.entities[t],st:e.states[t]})).filter(e=>e.reg),r=[n.filter(e=>!e.reg.hidden&&!e.reg.entity_category),n.filter(e=>!e.reg.entity_category),n.filter(e=>!e.reg.hidden),n];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const t of r){const i=t.find(t=>ah(e,t.eid));if(i)return i.eid}const o=hh(e,t);return o.find(ln)||o[0]}function uh(e,t,i=[]){return[...new Set(ph(e,t,i))].filter(e=>ln(e))}function ph(e,t,i=[]){const n=e?.startsWith("entity:")?new Set([e.slice(7)]):new Set(i);return(t||[]).filter(e=>"string"==typeof e&&!n.has(e))}function mh(e,t){if("string"==typeof t)return e.area===t;const i=e.marker?.room_id;return i?!!t.id&&i===t.id:!!t.area&&e.area===t.area}function _h(e){const t=e.marker?.binding?.startsWith("entity:")?e.marker.binding.slice(7):null,i=e.entities.filter(ln),n=e.primary&&ln(e.primary)?e.primary:null;return[...new Set([t,n,...i].filter(e=>!!e&&ln(e)))]}function fh(e){const t=_h(e),i=e.marker?.light_entity;return i&&t.includes(i)?i:t[0]||null}function gh(e,t){if(!1===t.marker?.is_light)return[];if(!0===t.marker?.is_light){return[{eid:fh(t),via:"forced"}]}const i=t.primary||hh(e,t.entities)[0];return i&&!i.startsWith("light.")?[]:t.entities.filter(t=>t.startsWith("light.")&&!!e.states?.[t]).map(e=>({eid:e,via:"light"}))}const vh=new WeakMap,yh=new WeakMap;function bh(e){return e.map(e=>[e.id||"",!0===e.hidden?1:0,e.primary||"",[...e.entities].join(","),void 0===e.controls?"":[...e.controls].join(","),e.marker?.id||"",e.marker?.binding||"",e.marker?.is_light,e.marker?.tap_action||"",!0===e.marker?.removed?1:0,e.marker?.light_entity||"",null==e.marker?.controls?"":[...e.marker.controls].join(",")].join("")).join("")}function wh(e){const t=bh(e),i=vh.get(e);if(i?.fingerprint===t)return i;const n=e.filter(e=>!e.hidden),r=new Map,o=new Map;for(const t of e){o.set(t,ph(t.marker?.binding,t.marker?.controls??t.controls,t.entities));const e=t.hidden||!0!==t.marker?.is_light?null:t.marker?.id||t.id;e&&r.set(String(e),t)}const s=new Map;for(const t of e){const e=o.get(t)||[],i=new Set(void 0===t.controls?e.filter(ln):t.controls.filter(ln)),n=fh(t),a=i.size?[...i]:n?[n]:[];for(const i of e){if(!i.startsWith("marker:"))continue;const e=i.slice(7);if(!e||e===String(t.marker?.id||t.id||""))continue;if(!r.has(e))continue;const n=s.get(e)||{markerId:e,controllers:[],driverEids:[]};n.controllers.push({device:t,driverEids:[...a]}),n.driverEids=[...new Set([...n.driverEids,...a])],s.set(e,n)}}const a={fingerprint:t,visible:n,markerById:r,persistedByDevice:o,incomingByMarker:s};return vh.set(e,a),a}function kh(e){return wh(e).incomingByMarker}function xh(e,t){const i=uh(t.marker?.binding,t.controls??t.marker?.controls,t.entities),n=ph(t.marker?.binding,t.marker?.controls??t.controls,t.entities);return(null!=t.marker?.is_light||!i.length&&!n.some(e=>e.startsWith("marker:")))&&gh(e,t).length>0}function $h(e,t){return gh(e,t).some(e=>!!e.eid)}function Sh(e,t,i,n){const r="always"===e||"auto"===e&&t,o=r&&!i;return{sourceExists:r,fromSourceEnabled:r&&i,manualEnabled:r,radiusEnabled:r,passive:o,effectiveMode:o&&"auto"===n?"fixed":n}}function Mh(e,t){const i=uh(t.marker?.binding,t.controls??t.marker?.controls,t.entities).map(e=>({eid:e,via:"controls"})),n=xh(e,t)?gh(e,t):[];for(const e of n)e.eid&&i.some(t=>t.eid===e.eid)||i.push(e);return i}function Rh(e,t,i,n){if(null!=i)return Rh(e,t,null,n).filter(e=>mh(e.device,i));const r=bh(t),o=function(e,t){const i=new Set;for(const e of t){for(const t of e.entities)i.add(t);for(const t of e.controls||e.marker?.controls||[])"string"!=typeof t||t.startsWith("marker:")||i.add(t)}return[...i].sort().map(t=>`${t}:${e?.states?.[t]?.state??""}`).join("|")}(e,t),s=Pc(n),a=yh.get(t);if(a?.graphFingerprint===r&&a.stateFingerprint===o&&a.virtualLightFingerprint===s&&a.registry===e?.entities)return a.sources;const{visible:l,markerById:c,persistedByDevice:h,incomingByMarker:d}=wh(t),u=null==i?l:l.filter(e=>mh(e,i)),p=new Map,m=new Map;for(const t of u){const i=Mh(e,t).filter(e=>"controls"!==e.via);for(const n of i){const i=!0===t.marker?.is_light?String(t.marker?.id||t.id||""):"",r=i?`marker:${i}`:`entity:${n.eid}`,o=n.eid?[n.eid]:[],s={key:r,eid:n.eid||"",stateEids:o,serviceEids:o,device:t,via:n.via,castsGlow:!0,passive:!n.eid,on:!n.eid||"on"===e.states?.[n.eid]?.state};p.set(t,[...p.get(t)||[],s]),n.eid&&m.set(n.eid,s)}}const _=new Map;for(const e of t){const t=h.get(e)||[],i=new Set(void 0===e.controls?t.filter(ln):e.controls);_.set(e,i)}for(const t of p.values())for(const i of t){if(!i.passive)continue;const t=i.key.slice(7),r=d.get(t);i.on=!r||r.driverEids.some(t=>"on"===e.states?.[t]?.state),!r&&Oc(i.device.marker)&&(i.on=Fc(i.device.marker,n))}const f=new Map,g=e=>{const t=f.get(e.key);(!t||e.castsGlow&&!t.castsGlow)&&f.set(e.key,e)};for(const t of l){const n=h.get(t)||[],r=null==i||mh(t,i);for(const i of n){if(i.startsWith("marker:")){const e=c.get(i.slice(7)),t=e?p.get(e):null;if(t)for(const e of t)g(e);continue}if(!ln(i)||!_.get(t)?.has(i))continue;const n=m.get(i);n?g(n):r&&g({key:`entity:${i}`,eid:i,stateEids:[i],serviceEids:[i],device:t,via:"controls",castsGlow:!1,passive:!1,on:"on"===e.states?.[i]?.state})}for(const e of p.get(t)||[])g(e)}const v=[...f.values()];return yh.set(t,{graphFingerprint:r,stateFingerprint:o,virtualLightFingerprint:s,registry:e?.entities,sources:v}),v}function Th(e){const t=e.filter(e=>e.castsGlow);return t.find(e=>e.on)||t[0]||null}function Ch(e){return e.length?e.some(e=>e.on)?"on":"off":"none"}function Dh(e,t){const i=[];for(const n of t){const t=e.states[n];if(!t)continue;const r=(t.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(n)||"lqi"===r){const e=parseFloat(t.state);isNaN(e)||i.push(e);continue}const o=t.attributes?.linkquality??t.attributes?.lqi;if(null!=o){const e=parseFloat(o);isNaN(e)||i.push(e)}}return _i(i)}function zh(e,t){for(const i of t){if(!ah(e,i))continue;const t=e.states[i];if(!t)continue;const n=parseFloat(t.state);if(!isNaN(n))return Math.round(10*n)/10}return null}function Ah(e,t){for(const i of t){if(!i.startsWith("climate."))continue;const t=e.states[i];if(!t||"unavailable"===t.state||"unknown"===t.state)continue;const n=parseFloat(t.attributes?.current_temperature);if(Number.isFinite(n))return Math.round(10*n)/10}return null}function Ph(e,t){if(e.entities?.[t]?.entity_category)return!1;const i=e.states[t];if(!i)return/_humidity$/.test(t);const n=i.attributes||{};return"humidity"===n.device_class||"%"===n.unit_of_measurement&&/_humidity$/.test(t)||/_humidity$/.test(t)}function Oh(e,t){for(const i of t){if(!Ph(e,i))continue;const t=e.states[i];if(!t)continue;const n=parseFloat(t.state);if(!isNaN(n))return Math.round(n)}return null}function Fh(e,t){if(!t)return[];const i=[];for(const[t,n]of Object.entries(e.entities)){if(!t.startsWith("light.")||n.hidden||!Zc(n))continue;let r=null;if("group"===n.platform)r=n.area_id||null;else{if(!n.device_id)continue;{const t=e.devices[n.device_id];if(!Zc(t))continue;if("Group"!==t?.model)continue;r=t.area_id||n.area_id||null}}if(!r)continue;const o=e.states[t];i.push({eid:t,name:n.name||o?.attributes?.friendly_name||t,area:r})}return i}function Ih(e,t,i,n,r){const o=ke(t,i,r);if(o!==we)return o;const s=[];for(const t of n){const i=e.states[t]?.attributes?.device_class;i&&s.push(i)}return function(e){for(const t of e){const e=be[t];if(e)return e}return null}(s)??we}function Eh(e,t){return e.map(e=>{if(!Array.isArray(e.controls))return e;const i=e.controls.filter(e=>!("string"==typeof e&&e.startsWith("marker:")&&t.has(e.slice(7))));return i.length===e.controls.length?e:{...e,controls:i.length?i:null}})}function Hh(e,t,i){if(!t||!i||t===i)return[...e];const n=`marker:${t}`,r=`marker:${i}`;return e.map(e=>{const t=Array.isArray(e.controls)?e.controls.map(e=>e===n?r:e):e.controls,i="derived_marker_state"===e.value_badge?.source?.kind&&e.value_badge.source.ref===n?{...e.value_badge,source:{...e.value_badge.source,ref:r}}:e.value_badge,o="derived_marker_state"===e.value_source?.kind&&e.value_source.ref===n?{...e.value_source,ref:r}:e.value_source;return t===e.controls&&i===e.value_badge&&o===e.value_source?e:{...e,controls:t,value_badge:i,value_source:o}})}function Nh(e,t,i){if(!t||t===i)return!0;const n=new Map;for(const t of e)n.set(t.id,(t.controls||[]).filter(e=>"string"==typeof e&&e.startsWith("marker:")).map(e=>e.slice(7)));const r=new Set,o=[i];for(;o.length;){const e=o.pop();if(e===t)return!0;r.has(e)||(r.add(e),o.push(...n.get(e)||[]))}return!1}function Lh(e,t,i,n){const r=new Set([t]),o=e.filter(e=>{const o=e.id===t||!n&&e.binding===i;return o&&r.add(e.id),!o});return{markers:n?o:[...o,{id:t,binding:i,removed:!0,hidden:!0}],cleanupIds:r}}function Wh(e){const t=new Set,i=new Set,n=new Set;for(const r of e||[]){const e=String(r.binding||"").indexOf(":");if(e<1)continue;const o=r.binding.slice(0,e),s=r.binding.slice(e+1);s&&(!0===r?.removed?"device"===o?t.add(s):"entity"===o&&i.add(s):"entity"===o&&n.add(s))}return{devices:t,entities:i,liveEntities:n}}function qh(e,t,i){if(i.liveEntities.has(t))return!1;if(i.entities.has(t))return!0;const n=e?.entities?.[t]?.device_id;return!!n&&i.devices.has(n)}function Bh(e,t,i,n,r){const o=uh(t.binding,t.controls,e.entities).filter(e=>!qh(i,e,n)).filter(e=>"active"===ih(i,"entity:"+e,r).kind);e.marker=t,e.controls=o,e.userHidden=!0===t.hidden,e.hidden=e.userHidden||"ha_disabled"===e.bindingStatus?.kind,t.name&&(e.name=t.name),t.icon&&(e.icon=t.icon),null!=t.model&&(e.model=t.model),e.link=t.link??null,e.description=t.description??null,e.pdfs=t.pdfs||[],e.tapAction=t.tap_action??null}function jh(e,t,i,n){if("string"==typeof e.room_id&&e.room_id.length>0&&null===e.area)return{area:"",space:e.space||n};const r=e.area||t||"";return{area:r,space:r&&i[r]||e.space||n}}function Uh(e){const t=e.hass,i=e.registry||Kc(t),n=Jc(t,i),r=Qc(t,i),{areaToSpace:o,markers:s,settings:a,excluded:l,showAll:c,firstSpaceId:h,loc:d,iconRules:u}=e,p=!1!==a.group_lights,m=Wh(s),_=Fh(n,p).filter(e=>!qh(n,e.eid,m)),f=new Set(_.map(e=>e.area)),g=nh(n),v=function(e){const t={};for(const[i,n]of Object.entries(e.entities||{}))n?.device_id&&(t[n.device_id]=t[n.device_id]||[]).push(i);return t}(r),y=rh(s,r),b=new Set;for(const e of s){const[t,i]=e.binding.split(":");"device"!==t&&"entity"!==t||!i||b.add(e.binding)}const w=(e,t)=>s.find(i=>i.binding===e+":"+t),k={},x=[];for(const e of Object.values(n.devices)){const r=e.area_id;if(!r||!o[r])continue;if("service"===e.entry_type)continue;if(b.has("device:"+e.id))continue;const s=ih(t,"device:"+e.id,i);if("active"!==s.kind)continue;const h=w("device",e.id);if(h&&h.hidden&&!a.filter_seeded)continue;const m=oh(n,e.id,g[e.id]||[],y);if(m.partial&&!m.entityIds.length)continue;const _=m.entityIds,v=m.partial?{kind:"active",enabledEntityIds:_,allEntityIds:_}:s,$=sh(n,e,_),S=!a.filter_seeded;if(S&&!c){if(l.has($))continue;if("Group"===e.model)continue;if(/scene/i.test(e.model||""))continue;if(/bridge/i.test((e.model||"")+(e.name||"")))continue;if("myheat"===$&&e.via_device_id)continue}const M=(e.name_by_user||e.name||d("device.unnamed")).trim(),R=M+"|"+r;let T=Ih(n,M,e.model,_,u);if(_.some(e=>e.startsWith("lock."))&&(T="mdi:lock"),S&&!c&&p&&"mdi:lightbulb"===T&&f.has(r))continue;k[R]=(k[R]||0)+1;const C=k[R]>1?M+" "+k[R]:M,D={id:e.id,name:C,model:e.model||"",area:r,space:o[r],icon:T,entities:_,allEntities:v.allEntityIds,bindingStatus:v,bindingKind:"device",bindingRef:e.id,pdfs:[]};D.primary=dh(n,_,T),"mdi:thermometer"!==T&&"mdi:air-filter"!==T||(D.temp=zh(n,_)),D.primary&&Ph(n,D.primary)&&(D.hum=Oh(n,_)),x.push(D)}for(const e of _)o[e.area]&&(b.has("entity:"+e.eid)||x.push({id:"lg_"+e.eid,name:e.name,model:d("device.light_group"),area:e.area,space:o[e.area],icon:"mdi:lightbulb-group",entities:[e.eid],allEntities:[e.eid],bindingStatus:{kind:"active",enabledEntityIds:[e.eid],allEntityIds:[e.eid]},primary:e.eid,bindingKind:"entity",bindingRef:e.eid,pdfs:[]}));for(const e of s){if(e.removed)continue;const[s,l]=e.binding.split(":"),c="device"===s||"entity"===s?ih(t,e.binding,i):null;if(!e.hidden||a.filter_seeded||"ha_disabled"===c?.kind)if("device"===s){const t=c;if("unverified"===t.kind)continue;const s=r.devices[l],{area:a,space:p}=jh(e,s?.area_id,o,h),_="active"===t.kind?t.enabledEntityIds:[];let f=s?Ih(n,s.name_by_user||s.name||"",s.model,_,u):"mdi:help-circle";_.some(e=>e.startsWith("lock."))&&(f="mdi:lock");const g={id:e.id,name:s?.name_by_user||s?.name||d("device.fallback"),model:s?.model||"",area:a,space:p,icon:f,entities:_,allEntities:t.allEntityIds.length?t.allEntityIds:s&&v[s.id]||[],bindingStatus:t,bindingKind:"device",bindingRef:l};g.primary=dh(n,_,f),"mdi:thermometer"!==f&&"mdi:air-filter"!==f||(g.temp=zh(n,_)),g.primary&&Ph(n,g.primary)&&(g.hum=Oh(n,_)),Bh(g,e,r,m,i),x.push(g)}else if("entity"===s){if(qh(r,l,m))continue;const t=c;if("unverified"===t.kind)continue;const s=r.entities[l],a=s?.area_id||s?.device_id&&r.devices[s.device_id]?.area_id||"",{area:d,space:p}=jh(e,a,o,h),_=n.states[l],f=s?.name||_?.attributes?.friendly_name||l;let g=Ih(n,f,"",[l],u);l.startsWith("lock.")&&(g="mdi:lock");const v={id:e.id,name:f,model:"",area:d,space:p,icon:g,entities:"active"===t.kind?[l]:[],allEntities:[l],bindingStatus:t,primary:"active"===t.kind?l:void 0,bindingKind:"entity",bindingRef:l};"mdi:thermometer"!==g&&"mdi:air-filter"!==g||!v.entities.length||(v.temp=zh(n,v.entities)),v.entities.length&&Ph(n,l)&&(v.hum=Oh(n,v.entities)),Bh(v,e,r,m,i),x.push(v)}else{const t=e.area||"",n=e.space||t&&o[t]||h,s={id:e.id,name:e.name||d("device.virtual"),model:e.model||"",area:t,space:n,icon:e.icon||"mdi:map-marker",entities:[],allEntities:[],bindingStatus:{kind:"active",enabledEntityIds:[],allEntityIds:[]},bindingKind:"virtual",virtual:!0};Bh(s,e,r,m,i),x.push(s)}}return x}function Vh(e){const{marker:t,siblingMarkers:i=[],...n}=e;return Uh({...n,markers:[...i.filter(e=>e.id!==t.id),t]}).find(e=>e.id===t.id)||null}function Gh(e,t,i,n){if(!t)return null;if(function(e,t,i){if(!t)return!1;const n=t.indexOf(":");if(n<1)return!1;const r=t.slice(0,n),o=t.slice(n+1),s=Wh(i);return"device"===r?s.devices.has(o):"entity"===r&&qh(e,o,s)}(e,t,n))return null;const r=t.indexOf(":");if(r<0)return null;const o=t.slice(0,r),s=t.slice(r+1);if(!s)return null;if("entity"===o){const t=parseFloat(e.states[s]?.state);return Number.isFinite(t)?"temp"===i?Math.round(10*t)/10:Math.round(t):null}if("device"===o){const t=Object.entries(e.entities).filter(([,e])=>e.device_id===s).map(([e])=>e);return"temp"===i?zh(e,t):Oh(e,t)}return null}const Kh=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 Yh(e,t){return t.area?t.area:e&&t.id?`@room/${encodeURIComponent(e)}/${encodeURIComponent(t.id)}`:null}function Xh(e){return null==e.area&&e.space&&e.room_id?Yh(e.space,{id:e.room_id,area:null}):e.area||null}function Zh(e,t,i){const n=new Map;if(!e?.entities)return n;const r=Wh(i),o=new Set,s=new Map,a=new Map;for(const e of i||[]){if(e?.removed)continue;const t=(e.binding||"").indexOf(":");if(t<=0)continue;const i=e.binding.slice(0,t),n=e.binding.slice(t+1);if(!n)continue;!0===e.use_climate_temp&&o.add(n);const r=Xh(e);r&&("entity"!==i||s.has(n)?"device"!==i||a.has(n)||a.set(n,r):s.set(n,r))}const l=new Map;for(const[t,i]of Object.entries(e.entities)){if(i.device_id&&r.devices.has(i.device_id)&&!r.liveEntities.has(t)||!i.device_id&&r.entities.has(t)&&!r.liveEntities.has(t))continue;const n=i.device_id?e.devices?.[i.device_id]:null;if(!Zc(i)||n&&!Zc(n))continue;const c=s.get(t)||(i.device_id?a.get(i.device_id):null)||i.area_id||n?.area_id||null;if(!c)continue;if(i.entity_category)continue;if(!(o.size>0&&t.startsWith("climate.")&&(o.has(t)||i.device_id&&o.has(i.device_id)))){if(_e.has(i.platform))continue;if(Kh.test(t))continue}let h=l.get(c);h||(h=new Map,l.set(c,h));const d=i.device_id||t;let u=h.get(d);if(!u){const r=e.states?.[t];u={name:(n?n.name_by_user||n.name:i.name||r?.attributes?.friendly_name||t)||t,model:n?.model,ents:[]},h.set(d,u)}u.ents.push(t)}for(const[i,r]of l){const s=[],a=[];for(const[i,n]of r){const r=Ih(e,n.name,n.model,n.ents,t),l="mdi:thermometer"===r||"mdi:air-filter"===r;if(l){const t=zh(e,n.ents);null!=t&&s.push(t)}if(o.size>0&&(o.has(i)||n.ents.some(e=>o.has(e)))){const t=Ah(e,n.ents);null!=t&&s.push(t)}if(l||"mdi:water-percent"===r){const t=Oh(e,n.ents);null!=t&&a.push(t)}}(s.length||a.length)&&n.set(i,{temp:s.length?Math.round(s.reduce((e,t)=>e+t,0)/s.length*10)/10:null,hum:a.length?Math.round(a.reduce((e,t)=>e+t,0)/a.length):null})}return n}function Jh(e,t,i){const n=[];for(const r of t){if(r.area!==i||r.virtual)continue;const t=Dh(e,r.entities);null!=t&&n.push(t)}return _i(n)}const Qh={offStates:["off"],unknownUsesToggle:!0},ed={light:Qh,switch:Qh,fan:Qh,humidifier:Qh,input_boolean:Qh,automation:Qh,remote:Qh,group:Qh,climate:{...Qh,featureMasks:{turn_on:256,turn_off:128,toggle:384}},media_player:{...Qh,featureMasks:{turn_on:128,turn_off:256,toggle:384}},siren:{...Qh,featureMasks:{turn_on:1,turn_off:2,toggle:3}},vacuum:{...Qh,featureMasks:{turn_on:1,turn_off:2,toggle:3}},water_heater:{...Qh,featureMasks:{turn_on:8,turn_off:8,toggle:8}},camera:{...Qh,featureMasks:{turn_on:1,turn_off:1,toggle:1}}};function td(e){return e.slice(0,e.indexOf("."))}function id(e,t,i){if(!e?.services||"object"!=typeof e.services)return!1;const n=e.services?.[t];return!!n&&Object.prototype.hasOwnProperty.call(n,i)}function nd(e,t,i){return id(e,t,i)?{domain:t,service:i}:id(e,"homeassistant",i)?{domain:"homeassistant",service:i}:null}function rd(e,t){return e?.entities?.[t]||null}function od(e,t,i){const n=e?.states?.[i],r=rd(t,i);return n?.attributes?.friendly_name||r?.name||r?.original_name||i}function sd(e,t){const i=rd(e,t);if(null!=i?.disabled_by)return!0;const n=i?.device_id?e?.devices?.[i.device_id]:null;return null!=n?.disabled_by}function ad(e,t){const i=e?.attributes?.supported_features;if(null==i||""===i)return!1;const n=Number(i);return Number.isFinite(n)&&(n&t)===t}function ld(e,t,i){const n=td(i);if("lock"===n||"alarm_control_panel"===n)return!0;if("cover"!==n)return!1;const r=e?.states?.[i],o=rd(t,i),s=String(r?.attributes?.device_class||o?.device_class||o?.original_device_class||"");return $i.has(s)}function cd(e,t,i,n,r){return{ref:i,entityId:n,name:n?od(e,t,n):null,reason:r}}function hd(e,t,i,n,r,o=null){return{target:null,skipped:cd(e,t,i,n,r),semantics:o,nextEffect:null,command:null}}function dd(e,t,i,n,r=i){const o=td(i);if("cover"!==o&&"valve"!==o)return function(e,t,i,n,r=i){const o=td(i),s=e?.states?.[i];if(sd(t,i))return hd(e,t,r,i,"ha-disabled","power");if(ld(e,t,i))return hd(e,t,r,i,"secure");if(!s)return hd(e,t,r,i,"missing","power");if("unavailable"===s.state)return hd(e,t,r,i,"unavailable","power");const a=ed[o];if(!a)return hd(e,t,r,i,"unsupported");let l,c;if("unknown"===s.state||""===s.state){if(!a.unknownUsesToggle)return hd(e,t,r,i,"unsupported","power");l="toggle",c="toggle"}else a.offStates.includes(String(s.state))?(l="turn-on",c="turn_on"):(l="turn-off",c="turn_off");const h=a.featureMasks?.[c];if(h&&!ad(s,h))return hd(e,t,r,i,"unsupported","power");const d=nd(e,o,c);return d?{target:{entityId:i,name:od(e,t,i),state:String(s.state||""),via:n},skipped:null,semantics:"power",nextEffect:l,command:{...d,data:{entity_id:i}}}:hd(e,t,r,i,"unsupported","power")}(e,t,i,n,r);if(sd(t,i))return hd(e,t,r,i,"ha-disabled",o);if(ld(e,t,i))return hd(e,t,r,i,"secure",o);const s=e?.states?.[i];if(!s)return hd(e,t,r,i,"missing",o);if("unavailable"===s.state)return hd(e,t,r,i,"unavailable",o);const a=function(e,t,i){const n=String(i?.state||""),r="closed"===n?{service:`open_${t}`,effect:"open",feature:1}:"open"===n?{service:`close_${t}`,effect:"close",feature:2}:"opening"===n||"closing"===n?{service:`stop_${t}`,effect:"stop",feature:8}:null;return r&&ad(i,r.feature)&&id(e,t,r.service)?r:ad(i,3)&&id(e,t,"toggle")?{service:"toggle",effect:"toggle"}:null}(e,o,s);if(!a)return hd(e,t,r,i,"unsupported",o);return{target:{entityId:i,name:od(e,t,i),state:String(s.state||""),via:n},skipped:null,semantics:o,nextEffect:a.effect,command:{domain:o,service:a.service,data:{entity_id:i}}}}function ud(e){return"entity"===e.bindingKind?e.bindingRef&&ln(e.bindingRef)?[e.bindingRef]:[]:"virtual"===e.bindingKind?[]:_h(e)}function pd(e){const t=e.marker?.toggle_entity;return t&&ud(e).includes(t)?t:null}function md(e,t,i){const n=function(e,t){if("entity"===e.bindingKind&&e.bindingRef){const t=e.marker?.light_entity?fh(e):null;return[...new Set([t,e.bindingRef].filter(e=>!!e))]}const i=e.entities.length?e.entities:e.allEntities||[],n=e.marker?.light_entity||e.primary&&ln(e.primary)?fh(e):null;return[...new Set([n,...hh(t,i)].filter(e=>!!e))]}(i,t);if(!n.length)return null;const r="entity"===i.bindingKind?"binding":"device-role";if("binding"===r)return dd(e,t,n[0],r);let o=null,s=null;for(const i of n){const n=dd(e,t,i,r);if(n.command)return n;const a=n.skipped?.reason;if("missing"===a||"unavailable"===a||"secure"===a)return n;"ha-disabled"!==a?o||=n:s||=n}return o||s}function _d(e){const t=e.skipped?.reason;return"missing"===t?"unavailable":t||"unsupported"}function fd(e,t){return{origin:e,kind:"none",semantics:null,targets:[],skippedTargets:[],noneReason:t,nextEffect:null,command:null}}function gd(e,t){return"cover"===e||"toggle"===e?"toggle":"more-info"===e||"run"===e||"info"===e?e:(null==e||""===e)&&"light"===t?"toggle":"info"}function vd(e,t){return{origin:e,kind:"single",semantics:t.semantics,targets:t.target?[t.target]:[],skippedTargets:t.skipped?[t.skipped]:[],noneReason:t.command?null:_d(t),nextEffect:t.nextEffect,command:t.command}}function yd(e,t,i,n=[]){const r=new Map,o=[],s=new Set,a=e=>{const t=`${e.ref}\n${e.entityId||""}\n${e.reason}`;s.has(t)||(s.add(t),o.push(e))};for(const e of n)a(e);for(const n of i){const i=dd(e,t,n.entityId,n.via,n.ref);i.target?r.set(n.entityId,r.get(n.entityId)||i.target):i.skipped&&a(i.skipped)}const l=[...r.values()];if(!l.length)return{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:l,skippedTargets:o,noneReason:o.length&&o.every(e=>"secure"===e.reason)?"secure":"configured-targets-missing",nextEffect:null,command:null};const c=l.some(e=>"on"===e.state)?"turn_off":"turn_on",h=nd(e,"homeassistant",c);return h?{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:l,skippedTargets:o,noneReason:null,nextEffect:"turn_on"===c?"turn-on":"turn-off",command:{...h,data:{entity_id:l.map(e=>e.entityId)}}}:{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:[],skippedTargets:[...o,...l.map(i=>cd(e,t,i.entityId,i.entityId,"unsupported"))],noneReason:"unsupported",nextEffect:null,command:null}}function bd(e){return String(e.marker?.id||e.id||"")}function wd(e){const{hass:t,device:i}=e,n=e.registryHass||t,r=function(e){return"toggle"===e.tapAction?"explicit-toggle":"cover"===e.tapAction?"legacy-cover":!e.tapAction&&e.primary?.startsWith("light.")?"default-light":null}(i);if(!r)return null;const o=i.marker;if("explicit-toggle"===r&&Oc(o)){const t=String(o?.id||i.id||""),n=kh(e.devices).get(t);if(n)return function(e,t){const i=e.registryHass||e.hass;return yd(e.hass,i,t.driverEids.map(e=>({entityId:e,via:"control-marker-driver",ref:`marker:${t.markerId}`})))}(e,n);const s=Fc(o,e.virtualLights);return{origin:r,kind:"single",semantics:"power",targets:[{entityId:"",name:i.name,state:s?"on":"off",via:"virtual-light"}],skippedTargets:[],noneReason:null,nextEffect:s?"turn-off":"turn-on",command:null,operation:{kind:"virtual-light",markerId:o.id}}}if("explicit-toggle"===r){if(ph(i.marker?.binding,i.marker?.controls??i.controls,i.entities).length)return function(e,t=null){const{hass:i,device:n,devices:r}=e,o=e.registryHass||i,s=ph(n.marker?.binding,n.marker?.controls??n.controls,n.entities),a=e.lightSources||Rh(i,r,null,e.virtualLights),l=new Map;for(const e of a){if(!e.key.startsWith("marker:"))continue;const t=l.get(e.key)||[];t.push(e),l.set(e.key,t)}const c=kh(r),h=new Map;for(const e of r){const t=String(e.marker?.id||e.id||"");t&&h.set(t,e)}const d=[],u=[];t&&d.push({entityId:t,via:"entity"===n.bindingKind?"binding":"device-role",ref:t});for(const e of s){if(!e.startsWith("marker:")){ln(e)?d.push({entityId:e,via:"control-entity",ref:e}):u.push(cd(i,o,e,e.includes(".")?e:null,"unsupported"));continue}const t=l.get(e)||[];if(!t.length){const t=h.get(e.slice(7));if("ha_disabled"===t?.bindingStatus?.kind){const n=t.bindingStatus.allEntityIds[0]||null;u.push({ref:e,entityId:n,name:t.name||(n?od(i,o,n):null),reason:"ha-disabled"})}else u.push(cd(i,o,e,null,"missing"));continue}const r=[...new Set(t.flatMap(e=>e.serviceEids))];if(r.length){for(const t of r)d.push({entityId:t,via:"control-entity",ref:e});continue}const s=t.some(e=>e.passive),a=e.slice(7),p=bd(n),m=c.get(a)?.controllers.find(e=>bd(e.device)===p);if(s&&m?.driverEids.length)for(const t of m.driverEids)d.push({entityId:t,via:"control-marker-driver",ref:e});else u.push(cd(i,o,e,null,s?"missing":"unsupported"))}return yd(i,o,d,u)}(e,pd(i))}if("legacy-cover"===r){const e=i.entities.find(e=>e.startsWith("cover."))||i.allEntities?.find(e=>e.startsWith("cover."));return e?vd(r,dd(t,n,e,"device-role")):fd(r,"no-actionable-entity")}const s=pd(i),a=s?dd(t,n,s,"entity"===i.bindingKind?"binding":"device-role"):md(t,n,i);return a?vd(r,a):fd(r,i.virtual||"virtual"===i.bindingKind?"no-actionable-entity":"no-binding")}function kd(e){if(!e)return[];const t=Array.isArray(e.data.entity_id)?e.data.entity_id:[e.data.entity_id];return[...new Set(t)].sort()}function xd(e){return e?e.operation?e.operation:e.command?{kind:"ha-service",command:e.command}:null:null}const $d=new Set(["missing","ha-disabled","unavailable"]);function Sd(e,t){const i=xd(e),n=xd(t);return!(!i||!n||i.kind!==n.kind)&&("virtual-light"===i.kind&&"virtual-light"===n.kind?i.markerId===n.markerId:"ha-service"===i.kind&&"ha-service"===n.kind&&function(e,t){const i=kd(e),n=kd(t);return i.length===n.length&&i.every((e,t)=>e===n[t])}(i.command,n.command))}function Md(e,t){const i=e.targets[0]||e.skippedTargets[0],n=[];return"group"===e.kind?e.targets.length&&n.push(t.group(e.targets)):i&&n.push(t.single(i)),e.nextEffect&&e.targets.length&&n.push("group"===e.kind?t.groupCurrentNext(e.targets,e.nextEffect):t.currentNext(e.targets[0],e.nextEffect)),e.skippedTargets.length&&n.push(t.skipped(e.skippedTargets)),!e.command&&e.noneReason&&n.push(t.none(e.noneReason)),n}const Rd=e=>!!e&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]);function Td(e,t=5,i=5){return ao(e.cm,t,i)/2}function Cd(e,t,i=1,n=5,r=5,o=0){const s=e.host;if(!s||"partition"!==s.kind||"string"!=typeof s.id||!s.id)return{resolved:null,reason:"invalid-host"};const a=t.find(e=>e.id===s.id);if(!a)return{resolved:null,reason:"missing-partition"};if(!Rd(a.a)||!Rd(a.b)||!Number.isFinite(s.t)||s.t<0||s.t>1)return{resolved:null,reason:"invalid-position"};const l=a.b[0]-a.a[0],c=a.b[1]-a.a[1],h=Math.hypot(l,c),d=Number(e.length)*i;if(!(h>1e-9&&d>0&&Number.isFinite(d)))return{resolved:null,reason:"invalid-length"};const u=s.t*h;if(u-d/2h-o+1e-9)return{resolved:null,reason:"does-not-fit"};const p=l/h,m=c/h;let _=180*Math.atan2(c,l)/Math.PI;return _>=90?_-=180:_<-90&&(_+=180),{reason:null,resolved:{opening:e,host:s,partition:a,center:[a.a[0]+l*s.t,a.a[1]+c*s.t],angle:_,length:d,depth:ao(a.cm,n,r),t:s.t,axis:{a:[a.a[0],a.a[1]],b:[a.b[0],a.b[1]],ux:p,uy:m,length:h}}}}function Dd(e,t,i=1,n=5,r=5){return Cd(e,t,i,n,r,0)}function zd(e,t,i=1,n=5,r=5){const o=e.host,s="partition"===o?.kind?t.find(e=>e.id===o.id):void 0;if(!s)return Dd(e,t,i,n,r);const a=Cd(e,t,i,n,r,Td(s,n,r));return"does-not-fit"===a.reason?{resolved:null,reason:"does-not-fit-jamb"}:a}function Ad(e,t){return!e||(e.length!==t.length||e.host?.kind!==t.host?.kind||e.host?.id!==t.host?.id||e.host?.t!==t.host?.t)}function Pd(e){const{center:t,length:i,axis:n,host:r}=e,o=i/2;return{hostId:r.id,a:[t[0]-n.ux*o,t[1]-n.uy*o],b:[t[0]+n.ux*o,t[1]+n.uy*o],depth:e.depth}}function Od(e,t=!1){const i=t?1:-1,n=e.depth/2;return{ox:-e.axis.uy*i*n,oy:e.axis.ux*i*n,cm:e.partition.cm,side:i}}function Fd(e,t,i){return e.flatMap(e=>!Rd(e.a)||!Rd(e.b)||Math.hypot(e.b[0]-e.a[0],e.b[1]-e.a[1])<=1e-9?[]:[{roomId:"",a:[e.a[0],e.a[1]],b:[e.b[0],e.b[1]],key:`partition:${e.id}`,kind:"outer",cm:e.cm,open:!1,half:ao(e.cm,t,i)/2,partitionHost:{kind:"partition",id:e.id}}])}function Id(e,t,i){const{center:n,length:r,axis:o}=e,s=r/2,a=t.flatMap(e=>{if(!e.kind||e.open)return[];const t=e.b[0]-e.a[0],r=e.b[1]-e.a[1],a=Math.hypot(t,r);if(!(a>i))return[];const l=t/a,c=r/a;if(Math.abs(o.ux*c-o.uy*l)>1e-6)return[];if(Math.abs((n[0]-e.a[0])*c-(n[1]-e.a[1])*l)>i)return[];const h=e=>(e[0]-n[0])*o.ux+(e[1]-n[1])*o.uy,d=h(e.a),u=h(e.b),p=Math.max(-s,Math.min(d,u)),m=Math.min(s,Math.max(d,u));return m>=p-i?[[p,m]]:[]});if(!a.length)return!1;a.sort((e,t)=>e[0]-t[0]||e[1]-t[1]);let l=-s;for(const[e,t]of a){if(e>l+i)return!1;if(l=Math.max(l,t),l>=s-i)return!0}return!1}function Ed(e,t,i=1e-9){const n=e.length/(2*e.axis.length),r=e.t-n,o=e.t+n;return t.some(t=>{if(t.host.id!==e.host.id||t.opening.id===e.opening.id)return!1;const n=t.length/(2*t.axis.length);return Math.max(r,t.t-n)
- `}let pm=null,mm=null,_m=0,fm=-1,gm=null,vm=[];const ym=new Set,bm=(e,t)=>{"function"==typeof e&&t.push(e)};function wm(){if(pm)return pm;try{const e=JSON.parse(localStorage.getItem("houseplan_card_cfg_v1")||"null");if(e&&e.config&&Array.isArray(e.config.spaces)){const t=e.layout||{};return{config:e.config,rev:e.rev||0,configFingerprint:e.config_fingerprint||Rl(e.config),layout:t,layoutRev:e.layout_rev||0,layoutFingerprint:e.layout_fingerprint||Rl(t),virtualLights:Dc(e.virtual_lights,e.rev||0)}}}catch{}return null}function km(e,t=!1){if(t&&(pm=null,_m++,mm))return mm.catch(()=>null).then(()=>km(e,!1));if(pm)return Promise.resolve(pm);if(mm)return fm!==_m?mm.catch(()=>null).then(()=>km(e,!1)):mm;return fm=_m,mm=async function(e,t){const[i,n]=await Promise.all([e.callWS({type:"houseplan/config/get"}),e.callWS({type:"houseplan/layout/get"})]),r=i?.rev??0,o=pm?zc(pm.virtualLights,i?.virtual_lights,r,!!i&&"virtual_lights"in i):Dc(i?.virtual_lights,r),s={config:i?.config??null,rev:r,configFingerprint:Rl(i?.config??null),layout:n?.layout??{},layoutRev:n?.rev??0,layoutFingerprint:Rl(n?.layout??{}),virtualLights:o};t===_m&&(pm=s);const a=e.connection;if(a?.subscribeEvents&&gm!==a){for(const e of vm)e();vm=[],gm=a;const e=()=>{pm=null,_m++,ym.forEach(e=>e())},t=[];try{if(bm(await a.subscribeEvents(e,"houseplan_config_updated"),t),bm(await a.subscribeEvents(e,"houseplan_layout_updated"),t),bm(await a.subscribeEvents(e=>{if(pm){const t=Ic(pm.virtualLights,e?.data);if(t===pm.virtualLights)return;pm={...pm,virtualLights:t}}else _m++;ym.forEach(e=>e())},"houseplan_virtual_light_updated"),t),gm===a)vm=t;else for(const e of t)e()}catch{for(const e of t)e();gm===a&&(gm=null)}}return s}(e,_m).finally(()=>{mm=null,fm=-1}),mm}const xm=3300;function $m(e,t){return{sources:e,last:Object.fromEntries(t.map(e=>[e.eid,e.state])),flashTs:0,flashKind:null,timer:0,gen:0,expiresAt:0,alarmActive:"alarm"===Mc(t).status}}function Sm(e,t,i,n){n(e.timer);const r=$m(t,i);Object.assign(e,r)}function Mm(e,t,i,n,r){e.flashTs&&i-e.flashTs"transition"===e.activity)&&(i(e.timer),e.flashTs=0,e.flashKind=null,e.expiresAt=0);if("alarm"===Mc(t).status){e.alarmActive||(i(e.timer),e.flashTs=0,e.flashKind=null,e.expiresAt=0);for(const i of t)e.last[i.eid]=i.state;return e.alarmActive=!0,null}if(e.alarmActive){for(const i of t)e.last[i.eid]=i.state;return e.alarmActive=!1,null}let n=null;for(const i of t){const t=Rc(e.last[i.eid],i);("event"===t||!n&&t)&&(n=t),e.last[i.eid]=i.state}return n}class Tm{constructor(e,t=()=>{}){this._host=e,this._onGateChange=t,this._modality="unknown",this._gate=!1,this._media=null,this._connected=!1,this._onMediaChange=()=>this._syncGate()}get modality(){return this._modality}get hoverEnabled(){return this._gate}connect(e=globalThis.window){this._connected||(this._connected=!0,this._modality="unknown",this._media="function"==typeof e?.matchMedia?e.matchMedia("(any-hover: hover) and (any-pointer: fine)"):null,this._media?.addEventListener?.("change",this._onMediaChange),this._setGate(!1))}disconnect(){this._media?.removeEventListener?.("change",this._onMediaChange),this._media=null,this._connected=!1,this._modality="unknown",this._setGate(!1)}note(e){return this._modality=function(e,t){const i="mouse"===(n=t.pointerType)||"touch"===n||"pen"===n?n:null;var n;return i?"mouse"===i&&t.sourceCapabilities?.firesTouchEvents?e:i:e}(this._modality,e),this._syncGate(),this._modality}suspend(){this._setGate(!1)}_syncGate(){var e,t;this._setGate((e=this._modality,t=!!this._media?.matches,"mouse"===e&&t))}_setGate(e){this._gate!==e&&(this._gate=e,this._host.toggleAttribute("data-pointer-hover",e),this._onGateChange(e))}}const Cm=new WeakMap;const Dm=(e,t,i)=>255===e[i+3]&&t.every((t,n)=>Math.abs(e[i+n]-t)<=2);async function zm(e){const t=e.defaultView;if(!t||!t.CSS?.supports?.("mix-blend-mode","screen"))return!1;const i=[128,32,16],n=[16,64,128],r=[9,19,29],o=`\n \n \n \n \n \n `,s=t.URL.createObjectURL(new Blob([o],{type:"image/svg+xml"}));try{const o=await new Promise((e,i)=>{const n=new t.Image,r=t.setTimeout(()=>i(new Error("SVG blend probe timeout")),1e3);n.onload=()=>{t.clearTimeout(r),e(n)},n.onerror=()=>{t.clearTimeout(r),i(new Error("SVG blend probe failed"))},n.src=s}),a=e.createElement("canvas");a.width=4,a.height=1;const l=a.getContext("2d",{willReadFrequently:!0});if(!l)return!1;l.drawImage(o,0,0,4,1);const c=l.getImageData(0,0,4,1).data;return Dm(c,i,0)&&Dm(c,function(e,t){return[0,1,2].map(i=>Math.round(255-(255-e[i])*(255-t[i])/255))}(i,n),4)&&Dm(c,n,8)&&Dm(c,r,12)}catch{return!1}finally{t.URL.revokeObjectURL(s)}}function Am(e){const t=Cm.get(e);if(t)return t.promise;const i={promise:Promise.resolve(!1)};return i.promise=zm(e).catch(()=>!1).then(e=>(i.resolved=e,e)),Cm.set(e,i),i.promise}function Pm(e){return Cm.get(e)?.resolved}class Om extends he{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return eu(this.hass,this._config?.language)}get _schema(){const e=this._spaces||[];return[e.length?{name:"space",selector:{select:{mode:"dropdown",options:e}}}:{name:"space",selector:{text:{}}},{name:"title",selector:{text:{}}},{name:"fit",selector:{select:{mode:"dropdown",options:[{value:"content",label:tu(this._lang,"editor.fit_content")},{value:"house",label:tu(this._lang,"editor.fit_house")}]}}},{name:"show_button",selector:{boolean:{}}},{name:"button_label",selector:{text:{}}},{name:"button_target",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"light_pools",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}}]}render(){if(!this.hass||!this._config)return V;const e=Bd(this,Kd,eu(this.hass,this._config.language));if("cold"===e)return jd();if("warm"===e)return U;this._loadSpaces();const t=this._lang,i={space:tu(t,"editor.space"),title:tu(t,"editor.title"),fit:tu(t,"editor.framing"),show_button:tu(t,"editor.show_button"),button_label:tu(t,"editor.button_label"),button_target:tu(t,"editor.button_target"),icon_size:tu(t,"editor.icon_size"),show_temperature:tu(t,"editor.show_temperature"),live_states:tu(t,"editor.live_states"),light_pools:tu(t,"editor.light_pools"),show_signal:tu(t,"editor.show_signal")};return B`{"function"==typeof e&&t.push(e)};function km(){if(mm)return mm;try{const e=JSON.parse(localStorage.getItem("houseplan_card_cfg_v1")||"null");if(e&&e.config&&Array.isArray(e.config.spaces)){const t=e.layout||{};return{config:e.config,rev:e.rev||0,configFingerprint:e.config_fingerprint||Rl(e.config),layout:t,layoutRev:e.layout_rev||0,layoutFingerprint:e.layout_fingerprint||Rl(t),virtualLights:Dc(e.virtual_lights,e.rev||0)}}}catch{}return null}function xm(e,t=!1){if(t&&(mm=null,fm++,_m))return _m.catch(()=>null).then(()=>xm(e,!1));if(mm)return Promise.resolve(mm);if(_m)return gm!==fm?_m.catch(()=>null).then(()=>xm(e,!1)):_m;return gm=fm,_m=async function(e,t){const[i,n]=await Promise.all([e.callWS({type:"houseplan/config/get"}),e.callWS({type:"houseplan/layout/get"})]),r=i?.rev??0,o=mm?zc(mm.virtualLights,i?.virtual_lights,r,!!i&&"virtual_lights"in i):Dc(i?.virtual_lights,r),s={config:i?.config??null,rev:r,configFingerprint:Rl(i?.config??null),layout:n?.layout??{},layoutRev:n?.rev??0,layoutFingerprint:Rl(n?.layout??{}),virtualLights:o};t===fm&&(mm=s);const a=e.connection;if(a?.subscribeEvents&&vm!==a){for(const e of ym)e();ym=[],vm=a;const e=()=>{mm=null,fm++,bm.forEach(e=>e())},t=[];try{if(wm(await a.subscribeEvents(e,"houseplan_config_updated"),t),wm(await a.subscribeEvents(e,"houseplan_layout_updated"),t),wm(await a.subscribeEvents(e=>{if(mm){const t=Ic(mm.virtualLights,e?.data);if(t===mm.virtualLights)return;mm={...mm,virtualLights:t}}else fm++;bm.forEach(e=>e())},"houseplan_virtual_light_updated"),t),vm===a)ym=t;else for(const e of t)e()}catch{for(const e of t)e();vm===a&&(vm=null)}}return s}(e,fm).finally(()=>{_m=null,gm=-1}),_m}const $m=3300;function Sm(e,t){return{sources:e,last:Object.fromEntries(t.map(e=>[e.eid,e.state])),flashTs:0,flashKind:null,timer:0,gen:0,expiresAt:0,alarmActive:"alarm"===Mc(t).status}}function Mm(e,t,i,n){n(e.timer);const r=Sm(t,i);Object.assign(e,r)}function Rm(e,t,i,n,r){e.flashTs&&i-e.flashTs<$m&&"event"===e.flashKind&&"transition"===t||(e.flashTs=i,e.expiresAt=i+$m,e.flashKind=t,e.gen++,n(e.timer),e.timer=r(3360))}function Tm(e,t,i){"transition"===e.flashKind&&t.some(e=>"transition"===e.activity)&&(i(e.timer),e.flashTs=0,e.flashKind=null,e.expiresAt=0);if("alarm"===Mc(t).status){e.alarmActive||(i(e.timer),e.flashTs=0,e.flashKind=null,e.expiresAt=0);for(const i of t)e.last[i.eid]=i.state;return e.alarmActive=!0,null}if(e.alarmActive){for(const i of t)e.last[i.eid]=i.state;return e.alarmActive=!1,null}let n=null;for(const i of t){const t=Rc(e.last[i.eid],i);("event"===t||!n&&t)&&(n=t),e.last[i.eid]=i.state}return n}class Cm{constructor(e,t=()=>{}){this._host=e,this._onGateChange=t,this._modality="unknown",this._gate=!1,this._media=null,this._connected=!1,this._onMediaChange=()=>this._syncGate()}get modality(){return this._modality}get hoverEnabled(){return this._gate}connect(e=globalThis.window){this._connected||(this._connected=!0,this._modality="unknown",this._media="function"==typeof e?.matchMedia?e.matchMedia("(any-hover: hover) and (any-pointer: fine)"):null,this._media?.addEventListener?.("change",this._onMediaChange),this._setGate(!1))}disconnect(){this._media?.removeEventListener?.("change",this._onMediaChange),this._media=null,this._connected=!1,this._modality="unknown",this._setGate(!1)}note(e){return this._modality=function(e,t){const i="mouse"===(n=t.pointerType)||"touch"===n||"pen"===n?n:null;var n;return i?"mouse"===i&&t.sourceCapabilities?.firesTouchEvents?e:i:e}(this._modality,e),this._syncGate(),this._modality}suspend(){this._setGate(!1)}_syncGate(){var e,t;this._setGate((e=this._modality,t=!!this._media?.matches,"mouse"===e&&t))}_setGate(e){this._gate!==e&&(this._gate=e,this._host.toggleAttribute("data-pointer-hover",e),this._onGateChange(e))}}const Dm=new WeakMap;const zm=(e,t,i)=>255===e[i+3]&&t.every((t,n)=>Math.abs(e[i+n]-t)<=2);async function Am(e){const t=e.defaultView;if(!t||!t.CSS?.supports?.("mix-blend-mode","screen"))return!1;const i=[128,32,16],n=[16,64,128],r=[9,19,29],o=`\n \n \n \n \n \n `,s=t.URL.createObjectURL(new Blob([o],{type:"image/svg+xml"}));try{const o=await new Promise((e,i)=>{const n=new t.Image,r=t.setTimeout(()=>i(new Error("SVG blend probe timeout")),1e3);n.onload=()=>{t.clearTimeout(r),e(n)},n.onerror=()=>{t.clearTimeout(r),i(new Error("SVG blend probe failed"))},n.src=s}),a=e.createElement("canvas");a.width=4,a.height=1;const l=a.getContext("2d",{willReadFrequently:!0});if(!l)return!1;l.drawImage(o,0,0,4,1);const c=l.getImageData(0,0,4,1).data;return zm(c,i,0)&&zm(c,function(e,t){return[0,1,2].map(i=>Math.round(255-(255-e[i])*(255-t[i])/255))}(i,n),4)&&zm(c,n,8)&&zm(c,r,12)}catch{return!1}finally{t.URL.revokeObjectURL(s)}}function Pm(e){const t=Dm.get(e);if(t)return t.promise;const i={promise:Promise.resolve(!1)};return i.promise=Am(e).catch(()=>!1).then(e=>(i.resolved=e,e)),Dm.set(e,i),i.promise}function Om(e){return Dm.get(e)?.resolved}class Fm extends he{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return eu(this.hass,this._config?.language)}get _schema(){const e=this._spaces||[];return[e.length?{name:"space",selector:{select:{mode:"dropdown",options:e}}}:{name:"space",selector:{text:{}}},{name:"title",selector:{text:{}}},{name:"fit",selector:{select:{mode:"dropdown",options:[{value:"content",label:tu(this._lang,"editor.fit_content")},{value:"house",label:tu(this._lang,"editor.fit_house")}]}}},{name:"show_button",selector:{boolean:{}}},{name:"button_label",selector:{text:{}}},{name:"button_target",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"light_pools",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}}]}render(){if(!this.hass||!this._config)return V;const e=Bd(this,Kd,eu(this.hass,this._config.language));if("cold"===e)return jd();if("warm"===e)return U;this._loadSpaces();const t=this._lang,i={space:tu(t,"editor.space"),title:tu(t,"editor.title"),fit:tu(t,"editor.framing"),show_button:tu(t,"editor.show_button"),button_label:tu(t,"editor.button_label"),button_target:tu(t,"editor.button_target"),icon_size:tu(t,"editor.icon_size"),show_temperature:tu(t,"editor.show_temperature"),live_states:tu(t,"editor.live_states"),light_pools:tu(t,"editor.light_pools"),show_signal:tu(t,"editor.show_signal")};return B`i[e.name]||e.name} @value-changed=${this._valueChanged} - >`}_valueChanged(e){const t={...this._config||{},...e.detail.value};delete t.aspect_ratio;const i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:t},this.dispatchEvent(i)}}Om.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",Om);const Fm=e=>{history.pushState(null,"",e),((e,t,i)=>{const n=new Event(t,{bubbles:!0,composed:!0});n.detail=i??{},e.dispatchEvent(n)})(window,"location-changed",{replace:!1})};class Im extends he{constructor(){super(...arguments),this._snap=null,this._loading=!1,this._reloadQueued=!1,this._forceReloadQueued=!1,this._reloadRetryTimer=0,this._dayCycleTimer=0,this._dayCycleClockKey="",this._stageWidth=0,this._pendingStageWidth=0,this._stageWidthRaf=0,this._haRegistryConnection=null,this._haRegistryRevision=-1,this._devices=[],this._continuity=this._newContinuityController(),this._continuityHistory=[],this._continuityEpoch=0,this._continuityDataReady=!0,this._continuityPaintToken=-1,this._continuityDisposed=!1,this._renderSnapshotAt=Date.now(),this._hassSequence=0,this._connHooked=null,this._connectionWasLost=!1,this._visibleDeviceSnapshot=null,this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._capturedSnapshotSequence=-1,this._capturedSnapshotDevices=null,this._capturedSnapshotActivity="",this._capturedSnapshotVirtual="",this._activityRuntime=new Map,this._glowRuntimeState=qp(),this._glowRuntimeHost={window:()=>this.ownerDocument.defaultView||window,isConnected:()=>this.isConnected,requestUpdate:()=>this.requestUpdate(),reducedMotion:()=>this._reducedMotion},this._glowScreenBlend=!1,this._reducedMotion=!1,this._pointerModality=new Tm(this),this._onMotionChange=e=>{this._reducedMotion=e.matches,this._capturedSnapshotSequence=-1,this.requestUpdate()},this._onHaRegistryUpdate=()=>{const e=Kc(this.hass).revision;e!==this._haRegistryRevision&&(this._haRegistryRevision=e,this._refreshDevices(),this._capturedSnapshotSequence=-1,this.requestUpdate())},this._dayCycleTick=()=>{if(!this.isConnected||"hidden"===this.ownerDocument.visibilityState)return;const e=this._dayCycleState();if(!e)return this._dayCycleTimer&&(window.clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),void(this._dayCycleClockKey="");const t=Nn(e);t!==this._dayCycleClockKey&&(this._dayCycleClockKey=t,this.requestUpdate())},this._pageVisibility=e=>{if(this._continuity.visibility(e),this._dayCycleVisibility(e),"hidden"!==e.kind){if(!e.long){const e=Date.now();let t=!1;for(const i of this._activityRuntime.values())!i.flashKind||(i.expiresAt||i.flashTs+3300)>e||(i.flashTs=0,i.flashKind=null,i.expiresAt=0,t=!0);return void(t&&(this._capturedSnapshotSequence=-1,this.requestUpdate()))}Date.now()-this._renderSnapshotAt>1e3&&this._continuity.note("device-snapshot-stale"),this._continuityDataReady=!1,this._continuityPaintToken=-1,this._load(!0)}else this._pointerModality.suspend()},this._onConnLost=()=>{this._connectionWasLost=!0,this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.connectionLost()},this._onConnReady=()=>{!this._connectionWasLost&&this._continuity.hasCompleteFrame?this._beginContinuityCandidate("connection-ready",!1,"plan"):(this._continuityDataReady=!1,this._continuityPaintToken=-1),this._load(!0)},this._onAssetLoaded=(e,t)=>{this._signer.markLoaded(this.hass,e,t),this._continuity.note("asset-ready"),this._continuityPaintToken=-1,"steady"!==this._continuity.state&&this.requestUpdate()},this._loadedOnce=!1,this._signer=new Dl(()=>this.requestUpdate()),this._retryContinuity=()=>{this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.retry(this._continuity.recoveryReason||"plan"),this._load(!0)},this._goToSpace=()=>{const e=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");Fm(`${e}#space=${encodeURIComponent(this._config.space)}`)}}_ensureHaRegistryAuthority(){const e=this.hass?.connection||null;e&&e!==this._haRegistryConnection&&(this._haRegistryRelease?.(),this._haRegistryConnection=e,this._haRegistryRevision=-1,this._haRegistryRelease=Gc(this.hass,this._onHaRegistryUpdate),this._onHaRegistryUpdate())}_newContinuityController(){return new Ml(()=>{this._continuityEpoch++,this.isConnected&&this.requestUpdate()})}_dayCycleState(e=new Date){const t=this._snap?.config;if(!t||!this._config)return null;const i=t.spaces?.find(e=>e.id===this._config?.space)?.settings||{};return"daynight"!==Yn(t.settings,i)?null:Hn(this._renderDeviceSnapshot?.hass||this.hass,e)}_syncDayCycleClock(){const e=this._dayCycleState();this._dayCycleClockKey=e?Nn(e):"";const t="clock"===e?.source&&"hidden"!==this.ownerDocument.visibilityState&&this.isConnected;t&&!this._dayCycleTimer?this._dayCycleTimer=window.setInterval(this._dayCycleTick,3e4):!t&&this._dayCycleTimer&&(window.clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_dayCycleVisibility(e){"hidden"!==e.kind?(this._dayCycleTick(),this._syncDayCycleClock()):this._dayCycleTimer&&(window.clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_hookConnection(){const e=this.hass?.connection;e&&e!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),e.addEventListener?.("ready",this._onConnReady),e.addEventListener?.("disconnected",this._onConnLost),e.addEventListener?.("reconnect-error",this._onConnLost),this._connHooked=e)}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(e){const t=wm();return{type:"custom:houseplan-space-card",space:$a(t?.config||null)[0]?.id||"",show_button:!0,fit:"content",live_states:!0,show_temperature:!0,show_signal:!0,light_pools:!1}}setConfig(e){if(!e||!e.space)throw new Error('houseplan-space-card: "space" is required');this._config={show_button:!0,button_target:"/plan-doma",live_states:!0,show_temperature:!0,show_signal:!0,light_pools:!1,...e,fit:Ia(e.fit)},!0!==this._config.light_pools?tm(this._glowRuntimeState,this._glowRuntimeHost):this.isConnected&&this._resolveGlowBlend(),this._snap=this._snap||wm()}connectedCallback(){var e;this._continuityDisposed&&(this._continuity=this._newContinuityController(),this._continuityDisposed=!1,this._continuityPaintToken=-1),super.connectedCallback(),this._pointerModality.connect(this.ownerDocument.defaultView),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this._resolveGlowBlend(),this.hass&&this._ensureHaRegistryAuthority(),this._continuityUnsub?.(),this._continuityUnsub=$l(this.ownerDocument,this._pageVisibility),this._unsub=(e=()=>{this._beginContinuityCandidate("config-event",!1),this._reloadQueued=!0,this._load()},ym.add(e),()=>ym.delete(e)),this._signer.start(()=>this.hass,()=>this._referenced())}disconnectedCallback(){this._pointerModality.disconnect(),this._continuityUnsub?.(),this._continuityUnsub=void 0,this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,this._unsub?.(),this._unsub=void 0,window.clearTimeout(this._reloadRetryTimer),this._reloadRetryTimer=0,this._dayCycleTimer&&(window.clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),this._dayCycleClockKey="",this._stageObserver?.disconnect(),this._stageObserver=void 0,this._observedStage=void 0,this._stageWidthRaf&&cancelAnimationFrame(this._stageWidthRaf),this._stageWidthRaf=0,this._pendingStageWidth=0,this._signer.dispose(),this._haRegistryRelease?.(),this._haRegistryRelease=void 0,this._haRegistryConnection=null,this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),this._connHooked=null;for(const e of this._activityRuntime.values())window.clearTimeout(e.timer);this._activityRuntime.clear(),tm(this._glowRuntimeState,this._glowRuntimeHost),this._continuityHistory=[...this._continuityHistory,...this._continuity.trace].slice(-80),this._continuity.dispose(),this._continuityDisposed=!0,super.disconnectedCallback()}_resolveGlowBlend(){if(!this._config?.light_pools)return;const e=Pm(this.ownerDocument);void 0!==e&&(this._glowScreenBlend=e),Am(this.ownerDocument).then(e=>{e!==this._glowScreenBlend&&(this._glowScreenBlend=e,this.isConnected&&this.requestUpdate())})}willUpdate(e){e.has("hass")&&this.hass&&(this._hassSequence++,this._renderSnapshotAt=Date.now(),this._continuity.note("hass-snapshot"),this._ensureHaRegistryAuthority(),this._hookConnection()),!this.hass||this._loading||this._snap&&!e.has("hass")||this._snap&&this._loadedOnce||this._load(),(e.has("hass")||e.has("_snap")||e.has("_config")||!this._devices.length)&&this._refreshDevices(),this._captureRenderDeviceSnapshot(),this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._continuity.refreshCompleteFrame(this._frameFingerprint())}_stampActivity(e,t){Mm(e,t,Date.now(),window.clearTimeout.bind(window),e=>window.setTimeout(()=>this.requestUpdate(),e))}_syncActivity(e,t,i=this.hass){if(!1===this._config?.live_states){for(const e of this._activityRuntime.values())window.clearTimeout(e.timer);return void this._activityRuntime.clear()}const n=new Set,r=Rh(t,e,null,this._snap?.virtualLights);for(const o of e){if(o.hidden)continue;if("icon_ripple"!==bi(o.marker?.display))continue;n.add(o.id);const s=Iu(t,o,e,r,i),a=s.samples,l=Nu(t,o,s);let c=this._activityRuntime.get(o.id);if(!c||c.sources!==l){c&&window.clearTimeout(c.timer),c=$m(l,a),this._activityRuntime.set(o.id,c);continue}const h=Rm(c,a,window.clearTimeout.bind(window));h&&this._stampActivity(c,h)}for(const[e,t]of this._activityRuntime)n.has(e)||(window.clearTimeout(t.timer),this._activityRuntime.delete(e))}_refreshDevices(){if(!this.hass||!this._snap?.config||!this._config)return;const e=Kc(this.hass),t=dm({hass:this.hass,registry:e,cfg:this._snap.config,lang:this._lang});this._syncActivity(t,Jc(this.hass,e),this.hass),this._devices=t}_captureRenderDeviceSnapshot(){if(!this.hass)return;const e=Date.now(),t=[...this._activityRuntime.entries()].map(([t,i])=>`${t}:${i.gen}:${i.flashTs}:`+(i.flashKind&&(i.expiresAt||i.flashTs+3300)>e?1:0)).join("|"),i=this._snap?.virtualLights?`${this._snap.virtualLights.configRev}:${this._snap.virtualLights.rev}`:"";if(this._capturedSnapshotSequence===this._hassSequence&&this._capturedSnapshotDevices===this._devices&&this._capturedSnapshotActivity===t&&this._capturedSnapshotVirtual===i)return;const n=Jc(this.hass,Kc(this.hass)),r=new Map,o=new Set(["sun.sun"]),s=new Set,a=new Set,l=e=>{if(!e)return;const t=e.indexOf(":");if(t<0)return void o.add(e);const i=e.slice(0,t),n=e.slice(t+1);"device"===i?s.add(n):"entity"===i&&o.add(n)},c=$a(this._snap?.config||null).find(e=>e.id===this._config?.space);for(const e of c?.rooms||[])e.area&&a.add(e.area),l(e.settings?.temp_source),l(e.settings?.hum_source);const h=this._snap?.config?.spaces?.find(e=>e.id===this._config?.space);for(const e of h?.openings||[])for(const t of Vt(e))o.add(t);const d=Rh(n,this._devices,null,this._snap?.virtualLights);for(const e of this._devices)for(const t of[!1,!0])r.set(Uu(e.id,t),Wu(n,e,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:t,activityRuntime:this._activityRuntime.get(e.id),sourceDetails:!1,lightDevices:this._devices,lightSources:d,registryHass:this.hass,reducedMotion:this._reducedMotion}));const u=ju({sourceSequence:this._hassSequence,hass:n,devices:this._devices,presentations:r,entityIds:o,deviceIds:s,areaIds:a});this._capturedSnapshotSequence=this._hassSequence,this._capturedSnapshotDevices=this._devices,this._capturedSnapshotActivity=t,this._capturedSnapshotVirtual=i,this._visibleDeviceSnapshot&&"steady"!==this._continuity.state?this._candidateDeviceSnapshot=u:(this._visibleDeviceSnapshot=u,this._candidateDeviceSnapshot=null)}get _renderDeviceSnapshot(){return this._stagedDeviceSnapshotToken===this._continuity.token?this._candidateDeviceSnapshot||this._visibleDeviceSnapshot:this._visibleDeviceSnapshot||this._candidateDeviceSnapshot}_beginContinuityCandidate(e,t,i="plan"){return this._continuityDataReady=t,this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._continuity.beginCandidate(e,i)}_backdropRaw(){return this._snap?.config&&this._config&&$a(this._snap.config).find(e=>e.id===this._config.space)?.bg?.href||""}_candidateBackdrop(e){return e&&this._config&&$a(e).find(e=>e.id===this._config.space)?.bg?.href||""}_assetsReady(){const e=this._backdropRaw();return!e||this._signer.isReady(this.hass,e)}_frameFingerprint(){const e=this._snap;return Tl([e?.rev||0,e?.configFingerprint||Rl(e?.config),e?.layoutRev||0,e?.layoutFingerprint||Rl(e?.layout),e?.virtualLights?`${e.virtualLights.configRev}:${e.virtualLights.rev}`:"",this._config?.space||"",!0===this._config?.light_pools,this._stageWidth,this.hass?.themes?.darkMode??this.hass?.themes?.default_theme??""])}_stageValid(){const e=this._observedStage;return!!e&&e.clientWidth>0&&e.clientHeight>0}_settleContinuityFrame(){if(!this._stageValid())return;if(!this._continuity.hasCompleteFrame&&"steady"===this._continuity.state){if(this._assetsReady())return this._renderSnapshotAt=Date.now(),void this._continuity.markCompleteFrame(this._frameFingerprint());this._beginContinuityCandidate("asset-wait",!0,"asset")}if(!this._continuityDataReady)return;if(!["holding","offline-stale","overlay-pending","overlay-visible","candidate-ready"].includes(this._continuity.state))return;const e=this._continuity.token;if(this._candidateDeviceSnapshot&&this._candidateDeviceSnapshot!==this._visibleDeviceSnapshot&&this._stagedDeviceSnapshotToken!==e)return this._stagedDeviceSnapshotToken=e,void this.requestUpdate();this._continuityPaintToken!==e&&(this._continuityPaintToken=e,this._continuity.candidateReady(e)&&this._continuity.commitAfterPaint(e,{updateComplete:()=>this.updateComplete,stageValid:()=>this.isConnected&&this._stageValid(),assetsReady:()=>this._assetsReady(),frameFingerprint:()=>this._frameFingerprint()}).then(t=>{t&&e===this._continuity.token?(this._renderSnapshotAt=Date.now(),this._candidateDeviceSnapshot&&(this._visibleDeviceSnapshot=this._candidateDeviceSnapshot),this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1):e===this._continuity.token&&(this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._candidateDeviceSnapshot=null,this.requestUpdate())}))}updated(){const e=this.renderRoot.querySelector(".hp-static-stage")||void 0;if(e!==this._observedStage)if(this._stageObserver?.disconnect(),this._observedStage=e,e){const t=()=>{const i=e.clientWidth;if(!(i<=0||Math.abs(i-this._stageWidth)<=.5)){if(this._stageWidth<=0)return this._stageWidth=i,void this.requestUpdate();this._pendingStageWidth=i,this._stageWidthRaf||(this._stageWidthRaf=requestAnimationFrame(()=>{this._stageWidthRaf=0;const e=this._pendingStageWidth;this._pendingStageWidth=0,!e||!this._observedStage||this._observedStage.clientWidth<=0||(Math.abs(this._observedStage.clientWidth-e)>.5?t():Math.abs(e-this._stageWidth)<=.5||(this._continuity.hasCompleteFrame&&this._beginContinuityCandidate("stage-resize",!0,"stage-size"),this._stageWidth=e,this.requestUpdate()))}))}};this._stageObserver=new ResizeObserver(t),this._stageObserver.observe(e),t()}else this._stageObserver=void 0;this._syncDayCycleClock(),this._settleContinuityFrame()}async _load(e=!1){if(this.hass){if(this._loading)return this._reloadQueued=!0,void(this._forceReloadQueued||=e);this._loading=!0,this._reloadQueued=!1;try{const t=await km(this.hass,e),i=!this._snap||this._snap.configFingerprint!==t.configFingerprint,n=!this._snap||this._snap.layoutFingerprint!==t.layoutFingerprint,r=!this._snap||this._snap.virtualLights!==t.virtualLights;if(i&&!await this._signer.prepareImage(this.hass,this._candidateBackdrop(t.config)))return this._continuity.note("asset-failed"),window.clearTimeout(this._reloadRetryTimer),void(this._reloadRetryTimer=window.setTimeout(()=>{this._load(!0)},1e3));window.clearTimeout(this._reloadRetryTimer),this._reloadRetryTimer=0,(i||n)&&this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._beginContinuityCandidate("structural-response",!0),i||n||r?this._snap=t:this._snap&&(this._snap.rev=t.rev,this._snap.layoutRev=t.layoutRev),i&&this._continuity.note("config-candidate",{configRev:t.rev}),n&&this._continuity.note("layout-candidate",{layoutRev:t.layoutRev}),r&&(this._capturedSnapshotSequence=-1),this._loadedOnce=!0,this._connectionWasLost=!1,this._continuityDataReady=!0,this._refreshDevices()}catch{}finally{if(this._loading=!1,this._continuityDataReady=!0,this.requestUpdate(),this._reloadQueued){const e=this._forceReloadQueued;this._reloadQueued=!1,this._forceReloadQueued=!1,this._load(e)}}}}get _lang(){return eu(this.hass,this._config?.language)}getCardSize(){const e=$a(this._snap?.config||null).find(e=>e.id===this._config?.space);if(e){const t=e.vb[3]/e.vb[2];return Math.max(3,Math.round(8*t))+(!1===this._config?.show_button?0:1)}return 6}_errorCard(e){return B``}_valueChanged(e){const t={...this._config||{},...e.detail.value};delete t.aspect_ratio;const i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:t},this.dispatchEvent(i)}}Fm.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",Fm);const Im=e=>{history.pushState(null,"",e),((e,t,i)=>{const n=new Event(t,{bubbles:!0,composed:!0});n.detail=i??{},e.dispatchEvent(n)})(window,"location-changed",{replace:!1})};class Em extends he{constructor(){super(...arguments),this._snap=null,this._loading=!1,this._reloadQueued=!1,this._forceReloadQueued=!1,this._reloadRetryTimer=0,this._dayCycleTimer=0,this._dayCycleClockKey="",this._stageWidth=0,this._pendingStageWidth=0,this._stageWidthRaf=0,this._haRegistryConnection=null,this._haRegistryRevision=-1,this._devices=[],this._continuity=this._newContinuityController(),this._continuityHistory=[],this._continuityEpoch=0,this._continuityDataReady=!0,this._continuityPaintToken=-1,this._continuityDisposed=!1,this._renderSnapshotAt=Date.now(),this._hassSequence=0,this._connHooked=null,this._connectionWasLost=!1,this._visibleDeviceSnapshot=null,this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._capturedSnapshotSequence=-1,this._capturedSnapshotDevices=null,this._capturedSnapshotActivity="",this._capturedSnapshotVirtual="",this._activityRuntime=new Map,this._glowRuntimeState=Bp(),this._glowRuntimeHost={window:()=>this.ownerDocument.defaultView||window,isConnected:()=>this.isConnected,requestUpdate:()=>this.requestUpdate(),reducedMotion:()=>this._reducedMotion},this._glowScreenBlend=!1,this._reducedMotion=!1,this._pointerModality=new Cm(this),this._onMotionChange=e=>{this._reducedMotion=e.matches,this._capturedSnapshotSequence=-1,this.requestUpdate()},this._onHaRegistryUpdate=()=>{const e=Kc(this.hass).revision;e!==this._haRegistryRevision&&(this._haRegistryRevision=e,this._refreshDevices(),this._capturedSnapshotSequence=-1,this.requestUpdate())},this._dayCycleTick=()=>{if(!this.isConnected||"hidden"===this.ownerDocument.visibilityState)return;const e=this._dayCycleState();if(!e)return this._dayCycleTimer&&(window.clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),void(this._dayCycleClockKey="");const t=Nn(e);t!==this._dayCycleClockKey&&(this._dayCycleClockKey=t,this.requestUpdate())},this._pageVisibility=e=>{if(this._continuity.visibility(e),this._dayCycleVisibility(e),"hidden"!==e.kind){if(!e.long){const e=Date.now();let t=!1;for(const i of this._activityRuntime.values())!i.flashKind||(i.expiresAt||i.flashTs+3300)>e||(i.flashTs=0,i.flashKind=null,i.expiresAt=0,t=!0);return void(t&&(this._capturedSnapshotSequence=-1,this.requestUpdate()))}Date.now()-this._renderSnapshotAt>1e3&&this._continuity.note("device-snapshot-stale"),this._continuityDataReady=!1,this._continuityPaintToken=-1,this._load(!0)}else this._pointerModality.suspend()},this._onConnLost=()=>{this._connectionWasLost=!0,this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.connectionLost()},this._onConnReady=()=>{!this._connectionWasLost&&this._continuity.hasCompleteFrame?this._beginContinuityCandidate("connection-ready",!1,"plan"):(this._continuityDataReady=!1,this._continuityPaintToken=-1),this._load(!0)},this._onAssetLoaded=(e,t)=>{this._signer.markLoaded(this.hass,e,t),this._continuity.note("asset-ready"),this._continuityPaintToken=-1,"steady"!==this._continuity.state&&this.requestUpdate()},this._loadedOnce=!1,this._signer=new Dl(()=>this.requestUpdate()),this._retryContinuity=()=>{this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.retry(this._continuity.recoveryReason||"plan"),this._load(!0)},this._goToSpace=()=>{const e=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");Im(`${e}#space=${encodeURIComponent(this._config.space)}`)}}_ensureHaRegistryAuthority(){const e=this.hass?.connection||null;e&&e!==this._haRegistryConnection&&(this._haRegistryRelease?.(),this._haRegistryConnection=e,this._haRegistryRevision=-1,this._haRegistryRelease=Gc(this.hass,this._onHaRegistryUpdate),this._onHaRegistryUpdate())}_newContinuityController(){return new Ml(()=>{this._continuityEpoch++,this.isConnected&&this.requestUpdate()})}_dayCycleState(e=new Date){const t=this._snap?.config;if(!t||!this._config)return null;const i=t.spaces?.find(e=>e.id===this._config?.space)?.settings||{};return"daynight"!==Yn(t.settings,i)?null:Hn(this._renderDeviceSnapshot?.hass||this.hass,e)}_syncDayCycleClock(){const e=this._dayCycleState();this._dayCycleClockKey=e?Nn(e):"";const t="clock"===e?.source&&"hidden"!==this.ownerDocument.visibilityState&&this.isConnected;t&&!this._dayCycleTimer?this._dayCycleTimer=window.setInterval(this._dayCycleTick,3e4):!t&&this._dayCycleTimer&&(window.clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_dayCycleVisibility(e){"hidden"!==e.kind?(this._dayCycleTick(),this._syncDayCycleClock()):this._dayCycleTimer&&(window.clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_hookConnection(){const e=this.hass?.connection;e&&e!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),e.addEventListener?.("ready",this._onConnReady),e.addEventListener?.("disconnected",this._onConnLost),e.addEventListener?.("reconnect-error",this._onConnLost),this._connHooked=e)}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(e){const t=km();return{type:"custom:houseplan-space-card",space:$a(t?.config||null)[0]?.id||"",show_button:!0,fit:"content",live_states:!0,show_temperature:!0,show_signal:!0,light_pools:!1}}setConfig(e){if(!e||!e.space)throw new Error('houseplan-space-card: "space" is required');this._config={show_button:!0,button_target:"/plan-doma",live_states:!0,show_temperature:!0,show_signal:!0,light_pools:!1,...e,fit:Ia(e.fit)},!0!==this._config.light_pools?im(this._glowRuntimeState,this._glowRuntimeHost):this.isConnected&&this._resolveGlowBlend(),this._snap=this._snap||km()}connectedCallback(){var e;this._continuityDisposed&&(this._continuity=this._newContinuityController(),this._continuityDisposed=!1,this._continuityPaintToken=-1),super.connectedCallback(),this._pointerModality.connect(this.ownerDocument.defaultView),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this._resolveGlowBlend(),this.hass&&this._ensureHaRegistryAuthority(),this._continuityUnsub?.(),this._continuityUnsub=$l(this.ownerDocument,this._pageVisibility),this._unsub=(e=()=>{this._beginContinuityCandidate("config-event",!1),this._reloadQueued=!0,this._load()},bm.add(e),()=>bm.delete(e)),this._signer.start(()=>this.hass,()=>this._referenced())}disconnectedCallback(){this._pointerModality.disconnect(),this._continuityUnsub?.(),this._continuityUnsub=void 0,this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,this._unsub?.(),this._unsub=void 0,window.clearTimeout(this._reloadRetryTimer),this._reloadRetryTimer=0,this._dayCycleTimer&&(window.clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),this._dayCycleClockKey="",this._stageObserver?.disconnect(),this._stageObserver=void 0,this._observedStage=void 0,this._stageWidthRaf&&cancelAnimationFrame(this._stageWidthRaf),this._stageWidthRaf=0,this._pendingStageWidth=0,this._signer.dispose(),this._haRegistryRelease?.(),this._haRegistryRelease=void 0,this._haRegistryConnection=null,this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),this._connHooked=null;for(const e of this._activityRuntime.values())window.clearTimeout(e.timer);this._activityRuntime.clear(),im(this._glowRuntimeState,this._glowRuntimeHost),this._continuityHistory=[...this._continuityHistory,...this._continuity.trace].slice(-80),this._continuity.dispose(),this._continuityDisposed=!0,super.disconnectedCallback()}_resolveGlowBlend(){if(!this._config?.light_pools)return;const e=Om(this.ownerDocument);void 0!==e&&(this._glowScreenBlend=e),Pm(this.ownerDocument).then(e=>{e!==this._glowScreenBlend&&(this._glowScreenBlend=e,this.isConnected&&this.requestUpdate())})}willUpdate(e){e.has("hass")&&this.hass&&(this._hassSequence++,this._renderSnapshotAt=Date.now(),this._continuity.note("hass-snapshot"),this._ensureHaRegistryAuthority(),this._hookConnection()),!this.hass||this._loading||this._snap&&!e.has("hass")||this._snap&&this._loadedOnce||this._load(),(e.has("hass")||e.has("_snap")||e.has("_config")||!this._devices.length)&&this._refreshDevices(),this._captureRenderDeviceSnapshot(),this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._continuity.refreshCompleteFrame(this._frameFingerprint())}_stampActivity(e,t){Rm(e,t,Date.now(),window.clearTimeout.bind(window),e=>window.setTimeout(()=>this.requestUpdate(),e))}_syncActivity(e,t,i=this.hass){if(!1===this._config?.live_states){for(const e of this._activityRuntime.values())window.clearTimeout(e.timer);return void this._activityRuntime.clear()}const n=new Set,r=Rh(t,e,null,this._snap?.virtualLights);for(const o of e){if(o.hidden)continue;if("icon_ripple"!==bi(o.marker?.display))continue;n.add(o.id);const s=Eu(t,o,e,r,i),a=s.samples,l=Lu(t,o,s);let c=this._activityRuntime.get(o.id);if(!c||c.sources!==l){c&&window.clearTimeout(c.timer),c=Sm(l,a),this._activityRuntime.set(o.id,c);continue}const h=Tm(c,a,window.clearTimeout.bind(window));h&&this._stampActivity(c,h)}for(const[e,t]of this._activityRuntime)n.has(e)||(window.clearTimeout(t.timer),this._activityRuntime.delete(e))}_refreshDevices(){if(!this.hass||!this._snap?.config||!this._config)return;const e=Kc(this.hass),t=um({hass:this.hass,registry:e,cfg:this._snap.config,lang:this._lang});this._syncActivity(t,Jc(this.hass,e),this.hass),this._devices=t}_captureRenderDeviceSnapshot(){if(!this.hass)return;const e=Date.now(),t=[...this._activityRuntime.entries()].map(([t,i])=>`${t}:${i.gen}:${i.flashTs}:`+(i.flashKind&&(i.expiresAt||i.flashTs+3300)>e?1:0)).join("|"),i=this._snap?.virtualLights?`${this._snap.virtualLights.configRev}:${this._snap.virtualLights.rev}`:"";if(this._capturedSnapshotSequence===this._hassSequence&&this._capturedSnapshotDevices===this._devices&&this._capturedSnapshotActivity===t&&this._capturedSnapshotVirtual===i)return;const n=Jc(this.hass,Kc(this.hass)),r=new Map,o=new Set(["sun.sun"]),s=new Set,a=new Set,l=e=>{if(!e)return;const t=e.indexOf(":");if(t<0)return void o.add(e);const i=e.slice(0,t),n=e.slice(t+1);"device"===i?s.add(n):"entity"===i&&o.add(n)},c=$a(this._snap?.config||null).find(e=>e.id===this._config?.space);for(const e of c?.rooms||[])e.area&&a.add(e.area),l(e.settings?.temp_source),l(e.settings?.hum_source);const h=this._snap?.config?.spaces?.find(e=>e.id===this._config?.space);for(const e of h?.openings||[])for(const t of Vt(e))o.add(t);const d=Rh(n,this._devices,null,this._snap?.virtualLights);for(const e of this._devices)for(const t of[!1,!0])r.set(Vu(e.id,t),qu(n,e,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:t,activityRuntime:this._activityRuntime.get(e.id),sourceDetails:!1,lightDevices:this._devices,lightSources:d,registryHass:this.hass,reducedMotion:this._reducedMotion}));const u=Uu({sourceSequence:this._hassSequence,hass:n,devices:this._devices,presentations:r,entityIds:o,deviceIds:s,areaIds:a});this._capturedSnapshotSequence=this._hassSequence,this._capturedSnapshotDevices=this._devices,this._capturedSnapshotActivity=t,this._capturedSnapshotVirtual=i,this._visibleDeviceSnapshot&&"steady"!==this._continuity.state?this._candidateDeviceSnapshot=u:(this._visibleDeviceSnapshot=u,this._candidateDeviceSnapshot=null)}get _renderDeviceSnapshot(){return this._stagedDeviceSnapshotToken===this._continuity.token?this._candidateDeviceSnapshot||this._visibleDeviceSnapshot:this._visibleDeviceSnapshot||this._candidateDeviceSnapshot}_beginContinuityCandidate(e,t,i="plan"){return this._continuityDataReady=t,this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._continuity.beginCandidate(e,i)}_backdropRaw(){return this._snap?.config&&this._config&&$a(this._snap.config).find(e=>e.id===this._config.space)?.bg?.href||""}_candidateBackdrop(e){return e&&this._config&&$a(e).find(e=>e.id===this._config.space)?.bg?.href||""}_assetsReady(){const e=this._backdropRaw();return!e||this._signer.isReady(this.hass,e)}_frameFingerprint(){const e=this._snap;return Tl([e?.rev||0,e?.configFingerprint||Rl(e?.config),e?.layoutRev||0,e?.layoutFingerprint||Rl(e?.layout),e?.virtualLights?`${e.virtualLights.configRev}:${e.virtualLights.rev}`:"",this._config?.space||"",!0===this._config?.light_pools,this._stageWidth,this.hass?.themes?.darkMode??this.hass?.themes?.default_theme??""])}_stageValid(){const e=this._observedStage;return!!e&&e.clientWidth>0&&e.clientHeight>0}_settleContinuityFrame(){if(!this._stageValid())return;if(!this._continuity.hasCompleteFrame&&"steady"===this._continuity.state){if(this._assetsReady())return this._renderSnapshotAt=Date.now(),void this._continuity.markCompleteFrame(this._frameFingerprint());this._beginContinuityCandidate("asset-wait",!0,"asset")}if(!this._continuityDataReady)return;if(!["holding","offline-stale","overlay-pending","overlay-visible","candidate-ready"].includes(this._continuity.state))return;const e=this._continuity.token;if(this._candidateDeviceSnapshot&&this._candidateDeviceSnapshot!==this._visibleDeviceSnapshot&&this._stagedDeviceSnapshotToken!==e)return this._stagedDeviceSnapshotToken=e,void this.requestUpdate();this._continuityPaintToken!==e&&(this._continuityPaintToken=e,this._continuity.candidateReady(e)&&this._continuity.commitAfterPaint(e,{updateComplete:()=>this.updateComplete,stageValid:()=>this.isConnected&&this._stageValid(),assetsReady:()=>this._assetsReady(),frameFingerprint:()=>this._frameFingerprint()}).then(t=>{t&&e===this._continuity.token?(this._renderSnapshotAt=Date.now(),this._candidateDeviceSnapshot&&(this._visibleDeviceSnapshot=this._candidateDeviceSnapshot),this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1):e===this._continuity.token&&(this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._candidateDeviceSnapshot=null,this.requestUpdate())}))}updated(){const e=this.renderRoot.querySelector(".hp-static-stage")||void 0;if(e!==this._observedStage)if(this._stageObserver?.disconnect(),this._observedStage=e,e){const t=()=>{const i=e.clientWidth;if(!(i<=0||Math.abs(i-this._stageWidth)<=.5)){if(this._stageWidth<=0)return this._stageWidth=i,void this.requestUpdate();this._pendingStageWidth=i,this._stageWidthRaf||(this._stageWidthRaf=requestAnimationFrame(()=>{this._stageWidthRaf=0;const e=this._pendingStageWidth;this._pendingStageWidth=0,!e||!this._observedStage||this._observedStage.clientWidth<=0||(Math.abs(this._observedStage.clientWidth-e)>.5?t():Math.abs(e-this._stageWidth)<=.5||(this._continuity.hasCompleteFrame&&this._beginContinuityCandidate("stage-resize",!0,"stage-size"),this._stageWidth=e,this.requestUpdate()))}))}};this._stageObserver=new ResizeObserver(t),this._stageObserver.observe(e),t()}else this._stageObserver=void 0;this._syncDayCycleClock(),this._settleContinuityFrame()}async _load(e=!1){if(this.hass){if(this._loading)return this._reloadQueued=!0,void(this._forceReloadQueued||=e);this._loading=!0,this._reloadQueued=!1;try{const t=await xm(this.hass,e),i=!this._snap||this._snap.configFingerprint!==t.configFingerprint,n=!this._snap||this._snap.layoutFingerprint!==t.layoutFingerprint,r=!this._snap||this._snap.virtualLights!==t.virtualLights;if(i&&!await this._signer.prepareImage(this.hass,this._candidateBackdrop(t.config)))return this._continuity.note("asset-failed"),window.clearTimeout(this._reloadRetryTimer),void(this._reloadRetryTimer=window.setTimeout(()=>{this._load(!0)},1e3));window.clearTimeout(this._reloadRetryTimer),this._reloadRetryTimer=0,(i||n)&&this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._beginContinuityCandidate("structural-response",!0),i||n||r?this._snap=t:this._snap&&(this._snap.rev=t.rev,this._snap.layoutRev=t.layoutRev),i&&this._continuity.note("config-candidate",{configRev:t.rev}),n&&this._continuity.note("layout-candidate",{layoutRev:t.layoutRev}),r&&(this._capturedSnapshotSequence=-1),this._loadedOnce=!0,this._connectionWasLost=!1,this._continuityDataReady=!0,this._refreshDevices()}catch{}finally{if(this._loading=!1,this._continuityDataReady=!0,this.requestUpdate(),this._reloadQueued){const e=this._forceReloadQueued;this._reloadQueued=!1,this._forceReloadQueued=!1,this._load(e)}}}}get _lang(){return eu(this.hass,this._config?.language)}getCardSize(){const e=$a(this._snap?.config||null).find(e=>e.id===this._config?.space);if(e){const t=e.vb[3]/e.vb[2];return Math.max(3,Math.round(8*t))+(!1===this._config?.show_button?0:1)}return 6}_errorCard(e){return B` ${tu(this._lang,e?"continuity.restore_connection":"continuity.restore_plan")} ${"recovery-error"===this._continuity.state?B``:V} -
`}houseplanContinuityTrace(){return[...this._continuityHistory,...this._continuity.trace].slice(-80).map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}render(){if(!this._config||!this.hass)return V;const e=Bd(this,Kd,eu(this.hass,this._config.language));if("cold"===e)return jd();if("warm"===e)return U;const t=this._snap?.config;if(!t)return this._errorCard(tu(this._lang,"space_card.loading"));const i=this._config.space,n=$a(t).find(e=>e.id===i),r=void 0!==this._config.title?this._config.title:n?.title||"",o=this._renderDeviceSnapshot,s=um({hass:o?.hass||this.hass,cfg:t,layout:this._snap?.layout||{},spaceId:i,iconSize:this._config.icon_size,fit:this._config.fit,compactTopFrame:""===this._config.title||null===this._config.title,stageWidth:this._stageWidth,lang:this._lang,displayUrl:e=>this._signer.display(this.hass,e),assetLoaded:this._onAssetLoaded,registry:o?{revision:o.sourceSequence,authoritative:!1,access:"limited",entities:{...o.hass.entities},devices:{...o.hass.devices},lastSuccess:o.capturedAt}:Kc(this.hass),devices:[...o?.devices||this._devices],presentations:o?.presentations,activityRuntime:this._activityRuntime,reducedMotion:this._reducedMotion,virtualLights:this._snap?.virtualLights,lightPools:!0===this._config.light_pools,glowRuntime:{state:this._glowRuntimeState,host:this._glowRuntimeHost,screenBlend:this._glowScreenBlend},liveStates:!1!==this._config.live_states,showTemperature:!1!==this._config.show_temperature,showSignal:!1!==this._config.show_signal,inert:this._continuity.overlayBlocksInteraction});if(!s)return this._errorCard(tu(this._lang,"space_card.not_found",{id:i}));const a=!1!==this._config.show_button,l=this._config.button_label||tu(this._lang,"space_card.button"),c=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null;return B` +
`}houseplanContinuityTrace(){return[...this._continuityHistory,...this._continuity.trace].slice(-80).map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}render(){if(!this._config||!this.hass)return V;const e=Bd(this,Kd,eu(this.hass,this._config.language));if("cold"===e)return jd();if("warm"===e)return U;const t=this._snap?.config;if(!t)return this._errorCard(tu(this._lang,"space_card.loading"));const i=this._config.space,n=$a(t).find(e=>e.id===i),r=void 0!==this._config.title?this._config.title:n?.title||"",o=this._renderDeviceSnapshot,s=pm({hass:o?.hass||this.hass,cfg:t,layout:this._snap?.layout||{},spaceId:i,iconSize:this._config.icon_size,fit:this._config.fit,compactTopFrame:""===this._config.title||null===this._config.title,stageWidth:this._stageWidth,lang:this._lang,displayUrl:e=>this._signer.display(this.hass,e),assetLoaded:this._onAssetLoaded,registry:o?{revision:o.sourceSequence,authoritative:!1,access:"limited",entities:{...o.hass.entities},devices:{...o.hass.devices},lastSuccess:o.capturedAt}:Kc(this.hass),devices:[...o?.devices||this._devices],presentations:o?.presentations,activityRuntime:this._activityRuntime,reducedMotion:this._reducedMotion,virtualLights:this._snap?.virtualLights,lightPools:!0===this._config.light_pools,glowRuntime:{state:this._glowRuntimeState,host:this._glowRuntimeHost,screenBlend:this._glowScreenBlend},liveStates:!1!==this._config.live_states,showTemperature:!1!==this._config.show_temperature,showSignal:!1!==this._config.show_signal,inert:this._continuity.overlayBlocksInteraction});if(!s)return this._errorCard(tu(this._lang,"space_card.not_found",{id:i}));const a=!1!==this._config.show_button,l=this._config.button_label||tu(this._lang,"space_card.button"),c=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null;return B` ${l}
`:V} - `}}Im.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0},_continuityEpoch:{state:!0}},Im.styles=[Nd,o` + `}}Em.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0},_continuityEpoch:{state:!0}},Em.styles=[Nd,o` .hp-static-title { font-weight: 700; padding: 10px 14px 6px; @@ -4391,7 +4391,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 text-align: center; color: var(--secondary-text-color, #9aa4ad); } - `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",Im),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-space-card"===e.type)||window.customCards.push({type:"houseplan-space-card",name:"House Plan — Space (static)",description:"Read-only live schematic of a single houseplan space, with a deep-link button.",preview:!1,documentation:"https://github.com/Matysh/houseplan-card"});const Em=o` + `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",Em),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-space-card"===e.type)||window.customCards.push({type:"houseplan-space-card",name:"House Plan — Space (static)",description:"Read-only live schematic of a single houseplan space, with a deep-link button.",preview:!1,documentation:"https://github.com/Matysh/houseplan-card"});const Hm=o` :host { --hp-editor-tray-bg-fallback: color-mix(in srgb, var(--card-background-color, var(--hp-bg)) 94%, var(--hp-line)); --hp-editor-tray-bg: color-mix(in srgb, var(--card-background-color, var(--hp-bg)) 86%, transparent); @@ -4562,7 +4562,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 .editor-secondary { transition: none; } .editor-group-launcher .group-chevron { transition: none; } } -`;function Hm(e,t){return e.catch(()=>{}).then(t)}class Nm{constructor(e=50){this._undo=[],this._redo=[],this._limit=Math.max(30,Math.floor(e))}get canUndo(){return this._undo.length>0}get canRedo(){return this._redo.length>0}get undoName(){return this._undo[this._undo.length-1]?.name??null}get redoName(){return this._redo[this._redo.length-1]?.name??null}get size(){return this._undo.length}push(e){this._undo.push(e),this._undo.length>this._limit&&this._undo.splice(0,this._undo.length-this._limit),this._redo=[]}undo(){const e=this._undo.pop()??null;return e&&this._redo.push(e),e}redo(){const e=this._redo.pop()??null;return e&&this._undo.push(e),e}clear(){this._undo=[],this._redo=[]}}const Lm={now:()=>performance.now(),requestFrame:e=>requestAnimationFrame(e),cancelFrame:e=>cancelAnimationFrame(e)},Wm=e=>Math.max(0,Math.min(1,e)),qm=(e,t,i)=>e+(t-e)*i,Bm=(e,t,i)=>{const n=1-e;return 3*n*n*e*t+3*n*e*e*i+e*e*e};function jm(e,t){return{centerX:e.x+e.w/2,centerY:e.y+e.h/2,pixelsPerUnit:t>0&&e.w>0?t/e.w:1,viewBox:{...e}}}function Um(e,t,i){const n=Math.max(1e-9,e.pixelsPerUnit),r=Math.max(1e-9,t/n),o=Math.max(1e-9,i/n);return{x:e.centerX-r/2,y:e.centerY-o/2,w:r,h:o}}function Vm(e){const t=e.trim(),i=/^rgba?\(\s*([\d.]+)[, ]+([\d.]+)[, ]+([\d.]+)(?:\s*[,/]\s*([\d.]+))?\s*\)$/i.exec(t);if(i)return[Number(i[1]),Number(i[2]),Number(i[3]),null==i[4]?1:Number(i[4])];const n=/^#([0-9a-f]{6}|[0-9a-f]{8})$/i.exec(t)?.[1];return n?[parseInt(n.slice(0,2),16),parseInt(n.slice(2,4),16),parseInt(n.slice(4,6),16),8===n.length?parseInt(n.slice(6,8),16)/255:1]:null}function Gm(e,t,i){const n=Vm(e),r=Vm(t);if(!n||!r)return i<.5?e:t;const o=Wm(i);return`rgba(${Math.round(qm(n[0],r[0],o))}, ${Math.round(qm(n[1],r[1],o))}, ${Math.round(qm(n[2],r[2],o))}, ${qm(n[3],r[3],o).toFixed(3)})`}function Km(e,t,i){const n=(e=>{const t=Wm(e);if(0===t||1===t)return t;let i=0,n=1,r=t;for(let e=0;e<12;e++)r=(i+n)/2,Bm(r,.2,.2){const t=this._state;if(!t||t.token!==r)return;const i=Wm((e-t.startedAt)/t.duration);t.presented=i>=1?t.to:Km(t.from,t.to,i),t.phase=i>=1?"settling":"running",this._hooks.frame(t),i<1?this._raf=this._clock.requestFrame(s):(this._raf=0,this._hooks.settled(t),this._state?.token===r&&(this._state=null))};return this._raf=this._clock.requestFrame(s),r}cancel(e=!0){this._raf&&this._clock.cancelFrame(this._raf),this._raf=0;const t=this._state;this._state=null,this._token++,t&&e&&(t.phase="settling",t.presented=t.to,this._hooks.frame(t),this._hooks.settled(t))}dispose(){this.cancel(!1)}}function Xm(e,t){return`${e("editor.load_failed")} ${e(t.terminal?"editor.refresh_advice":"editor.retry_advice")}`}class Zm extends Error{}class Jm{constructor(e){this.options=e,this._state="idle",this._inFlight=null}get state(){return this._state}ensure(){return"ready"===this._state?Promise.resolve(!0):"failed"===this._state?Promise.resolve(!1):(this._inFlight||(this._setState("loading"),this._inFlight=this._loadWithRetry().finally(()=>{this._inFlight=null})),this._inFlight)}async _loadWithRetry(){let e=new Error("Editor runtime did not load"),t=!1;for(const i of[0,1])try{const e=await this.options.load(i);if(e.fingerprint!==this.options.expectedFingerprint)throw new Zm(`Editor runtime fingerprint mismatch: expected ${this.options.expectedFingerprint}, got ${e.fingerprint}`);const t=e.create();return this.options.install(t),this._setState("ready"),!0}catch(i){e=i,i instanceof Zm&&(t=!0)}return this._setState(t?"failed":"idle"),this.options.failed?.(e,{terminal:t}),!1}_setState(e){e!==this._state&&(this._state=e,this.options.stateChanged?.(e))}}const Qm="houseplan_card_labs_v1",e_=Object.freeze([Object.freeze({id:"iso",issue:89,since:"1.62.0",expires:"1.65.0",summary:"Volumetric plan renderer"})]);function t_(e){const t=/^(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?$/.exec(String(e||"").trim());if(!t)return null;const i=t.slice(1).map(Number);return i.every(e=>Number.isSafeInteger(e))?i:null}function i_(e,t){for(let i=0;i<3;i++)if(e[i]!==t[i])return e[i]e.split(",")).map(e=>e.trim()).filter(Boolean)}function l_(e,t,i,n=e_){const r=function(e,t=e_){const i=t_(e),n=new Map;if(!i||!r_(t))return n;for(const e of t){const t=t_(e.since),r=t_(e.expires);i_(i,t)>=0&&i_(i,r)<0&&n.set(e.id,e)}return n}(i,n),o=new Set(r_(n)?n.map(e=>e.id):[]),s=new Set(function(e){if(!e)return[];try{const t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>"string"==typeof e):[]}catch{return[]}}(t).filter(e=>r.has(e)));let a=!1;const l=e=>{if("off"===e)return s.clear(),void(a=!0);const t=e.startsWith("-"),i=t?e.slice(1):e;o.has(i)&&(a=!0,t||!r.has(i)?s.delete(i):s.add(i))};for(const t of a_(new URLSearchParams(String(e.search||"").replace(/^\?/,""))))l(t);for(const t of a_(o_(e.hash)))l(t);const c=Object.freeze([...s].sort());return{active:c,space:s_(e.hash),persist:a?JSON.stringify(c):void 0,knownUrlOperation:a}}let c_={active:Object.freeze([]),space:""},h_="",d_=!1,u_="";const p_=new Set;function m_(){if("undefined"==typeof window)return c_;const e=l_(window.location,function(){try{return window.localStorage.getItem(Qm)}catch{return null}}(),h_);void 0!==e.persist&&function(e){try{window.localStorage.setItem(Qm,e)}catch{}}(e.persist),c_={active:e.active,space:e.space},window.__hpLabs=e.active;for(const e of p_)e(c_);return c_}function __(){m_()}function f_(e){if(h_=e,"undefined"==typeof window)return c_;if(!d_){d_=!0,window.__hpLabsListenerCleanup?.(),window.addEventListener("hashchange",__),window.addEventListener("popstate",__);const e=()=>{window.removeEventListener("hashchange",__),window.removeEventListener("popstate",__),window.__hpLabsListenerCleanup===e&&delete window.__hpLabsListenerCleanup};window.__hpLabsListenerCleanup=e}return m_()}const g_=Object.freeze({rotDeg:0,tiltDeg:20,xyScale:1,zScale:1,origin:Object.freeze([500,500])});function v_(e){return[e.rotDeg,e.tiltDeg,e.xyScale,e.zScale,e.origin[0],e.origin[1]].every(Number.isFinite)&&Math.abs(e.xyScale)>1e-12&&Math.abs(Math.cos(e.tiltDeg*Math.PI/180))>1e-12}function y_(e,t,i=g_){if(!(v_(i)&&Number.isFinite(e[0])&&Number.isFinite(e[1])&&Number.isFinite(t)))throw new Error("invalid isometric projection input");const n=i.rotDeg*Math.PI/180,r=i.tiltDeg*Math.PI/180,o=e[0]-i.origin[0],s=e[1]-i.origin[1],a=(o*Math.cos(n)-s*Math.sin(n))*i.xyScale,l=(o*Math.sin(n)+s*Math.cos(n))*i.xyScale;return[i.origin[0]+a,i.origin[1]+l*Math.cos(r)-t*i.zScale*Math.sin(r)]}function b_(e,t=g_){if(!v_(t)||!Number.isFinite(e[0])||!Number.isFinite(e[1]))throw new Error("invalid isometric projection input");const i=t.rotDeg*Math.PI/180,n=t.tiltDeg*Math.PI/180,r=(e[0]-t.origin[0])/t.xyScale,o=(e[1]-t.origin[1])/(t.xyScale*Math.cos(n));return[t.origin[0]+r*Math.cos(i)+o*Math.sin(i),t.origin[1]-r*Math.sin(i)+o*Math.cos(i)]}function w_(e=g_){return`matrix(${function(e=g_){if(!v_(e))throw new Error("invalid isometric camera");const t=e.rotDeg*Math.PI/180,i=e.tiltDeg*Math.PI/180,n=e.xyScale*Math.cos(t),r=-e.xyScale*Math.sin(t),o=e.xyScale*Math.sin(t)*Math.cos(i),s=e.xyScale*Math.cos(t)*Math.cos(i);return[n,o,r,s,e.origin[0]-n*e.origin[0]-r*e.origin[1],e.origin[1]-o*e.origin[0]-s*e.origin[1]]}(e).map(e=>Number(e.toFixed(12))).join(" ")})`}function k_(e,t=g_){const{rect:i,wallHeight:n}=e,r=e.openingHeight??n,o=e.floorDepth??0;if(!(i.w>=0)||!(i.h>=0)||!Number.isFinite(n)||!Number.isFinite(r)||!Number.isFinite(o)||n<0||r<0||o<0)throw new Error("invalid isometric frame");const s=[[i.x,i.y],[i.x+i.w,i.y],[i.x+i.w,i.y+i.h],[i.x,i.y+i.h]],a=Math.max(n,r),l=s.flatMap(e=>[y_(e,-o,t),y_(e,a,t)]),c=l.map(e=>e[0]),h=l.map(e=>e[1]),d=Math.min(...c),u=Math.min(...h);return{x:d,y:u,w:Math.max(...c)-d,h:Math.max(...h)-u}}const x_=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function $_(e){let t=0;for(let i=0;iArray.isArray(e)&&Number.isFinite(e[0])&&Number.isFinite(e[1])).map(e=>[Number(e[0]),Number(e[1])]),i=t[t.length-1];return t.length>1&&t[0][0]===i[0]&&t[0][1]===i[1]&&t.pop(),t}(e);if(i.length<3)return[];const n=!t;return $_(i)>0===n?i:[...i].reverse()}function M_(e,t,i){return e.length?`M ${e.map(e=>x_(y_(e,i,t))).join(" L ")} Z`:""}function R_(e,t,i){const n=t[1]-e[1],r=-(t[0]-e[0]),o=i.rotDeg*Math.PI/180;return n*Math.sin(o)+r*Math.cos(o)}function T_(e,t,i=g_){if(!Number.isFinite(t)||t<0)throw new Error("invalid floor edge height");const n=(e||[]).map(e=>(e||[]).map((e,t)=>function(e,t){const i=S_(e,t);if(!i.length)return i;let n=0;for(let e=1;e0)).filter(e=>e.length>=3&&Math.abs($_(e))>=1e-9)).filter(e=>e.length&&$_(e[0])>0);n.sort((e,t)=>{const i=e[0][0],n=t[0][0];return i[0]-n[0]||i[1]-n[1]||Math.abs($_(t[0]))-Math.abs($_(e[0]))});const r=[],o=[];let s=0;for(let e=0;ee.depth-t.depth||e.component-t.component||e.edge-t.edge),{footprintPath:r.join(" "),sides:o,componentCount:n.length,edgeCount:s}}function C_(e,t){return e^=Math.round(64*(Number.isFinite(t)?t:0)),Math.imul(e,16777619)>>>0}const D_=e=>e.every(Number.isFinite);function z_(e,t,i,n){const r=e[0]*i,o=e[1]*n,s=t*Math.PI/180;return[r*Math.cos(s)-o*Math.sin(s),r*Math.sin(s)+o*Math.cos(s)]}function A_(e,t,i,n,r,o,s){const a=e.flipH?-1:1,l="gate"===e.type?1:e.flipV?-1:1,c=(e.type,e.flipV,e.angle,e.face,{ox:0,oy:0}),h=function(e,t){return[e[0]+t[0],e[1]+t[1]]}([e.x+c.ox,e.y+c.oy],z_(i,e.angle,a,l));return{leaf:t,hinge:h,closedVector:z_(n,e.angle,a,l),quarterVector:z_([-n[1],n[0]],e.angle,a,l),turnDeg:r,bottom:o,top:s}}const P_=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function O_(e,t,i=g_){const n=Math.max(0,Math.min(1,Number.isFinite(t)?t:0));return e.leaves.map(t=>{const r=function(e,t){const i=e.turnDeg*t*Math.PI/180;return[e.hinge[0]+e.closedVector[0]*Math.cos(i)+e.quarterVector[0]*Math.sin(i),e.hinge[1]+e.closedVector[1]*Math.cos(i)+e.quarterVector[1]*Math.sin(i)]}(t,n),o=y_(t.hinge,t.bottom,i),s=y_(r,t.bottom,i),a=y_(r,t.top,i),l=y_(t.hinge,t.top,i);return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaf:t.leaf,d:`M ${P_(o)} L ${P_(s)} L ${P_(a)} L ${P_(l)} Z`,shadowD:`M ${P_(y_(t.hinge,0,i))} L ${P_(y_(r,0,i))}`,depth:Math.max(o[1],s[1])}})}const F_={window:120,door:90,passage:90,gate:300};function I_(e){return F_[e]}function E_(e,t){return{type:e,lengthCm:I_(e),flipH:!1,flipV:!1,revision:t}}function H_(e,t){const i=Math.max(0,e.renderedLength)/2,n=Math.max(0,e.target.physicalHalfWidth),r=n+.18*Math.max(0,t),o=e=>({x1:e,y1:-r,x2:e,y2:r});return{rect:{x:-i,y:n?-n:0,width:2*i,height:2*n},boundaries:[o(-i),o(i)]}}function N_(e,t){const i=[e[0],e[1]],n=[t[0],t[1]];return function(e,t){return e[0]-t[0]||e[1]-t[1]}(i,n)<=0?{a:i,b:n}:{a:n,b:i}}function L_(e){const t=new Map;return e.forEach((e,i)=>{if(!e.kind||e.open)return;const n=N_(e.a,e.b);if(Math.hypot(n.b[0]-n.a[0],n.b[1]-n.a[1])<=1e-9)return;const r=function(e,t){const i=e=>(Math.abs(e)<=5e-10?0:Math.round(1e6*e)/1e6).toFixed(6);return`${i(e[0])},${i(e[1])}>${i(t[0])},${i(t[1])}`}(n.a,n.b),o=t.get(r);if(o)return o.physicalHalfWidth=Math.max(o.physicalHalfWidth,e.half||0),o.sourceOrder=Math.min(o.sourceOrder,i),void(e.partitionHost&&(o.partitionHost&&o.partitionHost.id!==e.partitionHost.id?o.ambiguousPartitionHost=!0:o.partitionHost=e.partitionHost));t.set(r,{segmentKey:r,a:n.a,b:n.b,physicalHalfWidth:Math.max(0,e.half||0),sourceOrder:i,...e.partitionHost?{partitionHost:e.partitionHost}:{}})}),[...t.values()]}function W_(e,t){if(Math.abs(e.distance-t.distance)>1e-9)return e.distance-t.distance;if(Math.abs(e.perpendicular-t.perpendicular)>1e-9)return e.perpendicular-t.perpendicular;return(e.target.segmentKeyt.target.segmentKey?1:0)||e.target.sourceOrder-t.target.sourceOrder}function q_(e){const t=L_(e.intervals).map(t=>{const i=function(e,t){const i=t.b[0]-t.a[0],n=t.b[1]-t.a[1],r=Math.hypot(i,n),o=i/r,s=n/r,a=(e[0]-t.a[0])*o+(e[1]-t.a[1])*s,l=Math.max(0,Math.min(r,a)),c=t.a[0]+l*o,h=t.a[1]+l*s;return{along:l,length:r,x:c,y:h,distance:Math.hypot(e[0]-c,e[1]-h),perpendicular:Math.abs((e[0]-t.a[0])*s-(e[1]-t.a[1])*o)}}(e.pointer,t);return{target:t,...i,envelope:Math.max(e.baseTolerance,t.physicalHalfWidth+e.bodyPointerPadding)}}).filter(e=>e.distance<=e.envelope+1e-9).filter(t=>!function(e,t,i,n,r){const o=t.b[0]-t.a[0],s=t.b[1]-t.a[1],a=Math.hypot(o,s);if(!(a>1e-9))return!1;const l=o/a,c=s/a;for(const o of i){if(!o.open&&o.kind)continue;const i=N_(o.a,o.b),s=i.b[0]-i.a[0],a=i.b[1]-i.a[1],h=Math.hypot(s,a);if(!(h>1e-9))continue;const d=s/h,u=a/h;if(Math.abs(l*u-c*d)>1e-6)continue;if(Math.abs((i.a[0]-t.a[0])*c-(i.a[1]-t.a[1])*l)>r)continue;const p=(e[0]-i.a[0])*d+(e[1]-i.a[1])*u,m=1e-9;if(!(p<=m||p>=h-m)&&Math.abs((e[0]-i.a[0])*u-(e[1]-i.a[1])*d)<=n+1e-9)return!0}return!1}(e.pointer,t.target,e.intervals,t.envelope,Math.max(1e-9,Math.min(e.baseTolerance,.04*e.gridStep)))),i=t.filter(t=>!!t.target.partitionHost&&e.renderedLength+2*t.target.physicalHalfWidth>t.length+1e-9).sort(W_),n=t.filter(t=>!t.target.partitionHost||e.renderedLength+2*t.target.physicalHalfWidth<=t.length+1e-9).sort(W_);let r=n[0];if(!r)return{candidate:null,jambBlockedTarget:i[0]?.target||null};const o=n.filter(e=>Math.abs(e.distance-r.distance)<=1e-9&&Math.abs(e.perpendicular-r.perpendicular)<=1e-9),s=o.filter(e=>e.target.partitionHost);if(s.length){if(1!==new Set(s.map(e=>e.target.partitionHost.id)).size||s.some(e=>e.target.ambiguousPartitionHost))return{candidate:null,jambBlockedTarget:null};const e=s[0],t=e.target.b[0]-e.target.a[0],i=e.target.b[1]-e.target.a[1],n=Math.hypot(t,i),a=t/n,l=i/n;if(!o.every(t=>{const i=t.target.b[0]-t.target.a[0],n=t.target.b[1]-t.target.a[1],r=Math.hypot(i,n),o=i/r,s=n/r,c=Math.abs(a*s-l*o)<=1e-6,h=Math.abs((t.target.a[0]-e.target.a[0])*l-(t.target.a[1]-e.target.a[1])*a);return c&&h<=1e-9}))return{candidate:null,jambBlockedTarget:null};r=e}if(r.target.ambiguousPartitionHost)return{candidate:null,jambBlockedTarget:null};const{target:a,length:l}=r,c=a.b[0]-a.a[0],h=a.b[1]-a.a[1],d=c/l,u=h/l,p=a.partitionHost?a.physicalHalfWidth:0,m=Math.min(Math.max(0,e.renderedLength)/2+p,l/2);let _=r.along;const f=Math.max(e.gridStep,1e-9),g=l/2;_=Math.abs(_-g)<=f/2?g:Math.round(_/f)*f,_=Math.max(m,Math.min(l-m,_));const v=a.a[0]+d*_,y=a.a[1]+u*_,b=Math.max(0,e.renderedLength)/2,w=_-b,k=_+b,x=Math.max(0,w),$=Math.max(0,l-k),S=[a.a[0]+d*(w-x/2),a.a[1]+u*(w-x/2)],M=[a.a[0]+d*(k+$/2),a.a[1]+u*(k+$/2)];let R=180*Math.atan2(h,c)/Math.PI;R>=90?R-=180:R<-90&&(R+=180);const T=Math.abs(_-g)<=1e-9;return{candidate:{presetRevision:e.preset.revision,geometryRevision:e.geometryRevision,pointer:[e.pointer[0],e.pointer[1]],type:e.preset.type,lengthCm:e.preset.lengthCm,flipH:e.preset.flipH,flipV:e.preset.flipV,x:v,y:y,angle:R,renderedLength:e.renderedLength,target:a,...a.partitionHost?{host:{...a.partitionHost,t:l>0?_/l:0}}:{},measure:{labels:[{distance:x,midpoint:S},{distance:$,midpoint:M}],guide:T?{x:v,y:y,angle:R}:null}},jambBlockedTarget:null}}function B_(e,t,i,n,r=1e-6){return e.presetRevision===i&&e.geometryRevision===n&&Math.hypot(e.pointer[0]-t[0],e.pointer[1]-t[1])<=r}function j_(e){return!(!e.canEdit||e.kiosk||e.fixedFloor)&&("view"!==e.mode&&("mouse"===e.pointerType&&e.spaceCount>1))}let U_=0;const V_="1.69.0-beta.4",G_="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c",K_=1500,Y_=(e,t)=>{if(!e.has(t))return{hit:!1};const i=e.get(t);return e.delete(t),e.set(t,i),{hit:!0,value:i}},X_=(e,t,i,n)=>{for(e.delete(t),e.set(t,i);e.size>n;){const t=e.keys().next().value;if(void 0===t)break;e.delete(t)}},Z_=new Map;let J_=0;const Q_=e=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(e??{})}`;let ef=1e4;const tf="houseplan_card_layout_v1",nf="houseplan_card_cfg_v1",rf="houseplan_card_zoom_v1",of="houseplan_card_nav_v1",sf="houseplan_card_kiosk_v1",af="houseplan_card_view_v1",lf="hp-dialog, hp-help, hp-color-opacity, hp-device-preview",cf=1e3,hf=(e,t)=>{const i=Math.min(e.x,t.x),n=Math.min(e.y,t.y);return{x:i,y:n,w:Math.max(e.x+e.w,t.x+t.w)-i,h:Math.max(e.y+e.h,t.y+t.h)-n}},df=new Set(["select","draw","column","merge","split","resize","opening","wallthick","delroom"]),uf=(e,t,i)=>{const n=new Event(t,{bubbles:!0,composed:!0});n.detail=i??{},e.dispatchEvent(n)},pf=e=>{history.pushState(null,"",e),uf(window,"location-changed",{replace:!1})},mf=(e,t)=>{let i,n=null;const r=(...r)=>{clearTimeout(i),n=r,i=window.setTimeout(()=>{i=void 0;const t=n;n=null,t&&e(...t)},t)};return r.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const t=n;n=null,t&&e(...t)},r.cancel=()=>{clearTimeout(i),i=void 0,n=null},r.pending=()=>void 0!==i,r},_f=e=>{try{e.target?.setPointerCapture?.(e.pointerId)}catch{}};class ff extends he{constructor(){super(...arguments),this._editorRuntime=null,this._onboardingRuntime=null,this._editorRuntimeLoadingVisible=!1,this._backdropGuard=null,this._editorModeRequest=0,this._warmModeRequest=0,this._editorRuntimeLoader=new Jm({expectedFingerprint:G_,load:async e=>{const t=0===e?await import("./houseplan-editor-runtime-CisKVaG7.js"):await import((()=>{const e=new URL("./houseplan-editor-runtime-CisKVaG7.js",import.meta.url);return e.searchParams.set("hp_retry",`${V_}-${++U_}`),e.href})());return{fingerprint:t.EDITOR_RUNTIME_FINGERPRINT,create:()=>new t.HouseplanEditorRuntime(this)}},install:e=>{this._editorRuntime=e},stateChanged:e=>this._editorRuntimeStateChanged(e),failed:(e,t)=>{console.error("[houseplan] unable to load editor runtime",e),this._showToast(Xm(e=>this._t(e),t))}}),this._onboardingRuntimeLoader=new Jm({expectedFingerprint:G_,load:async e=>{const t=0===e?await import("./houseplan-onboarding-runtime-eqCB-Ax0.js"):await import((()=>{const e=new URL("./houseplan-onboarding-runtime-eqCB-Ax0.js",import.meta.url);return e.searchParams.set("hp_retry",`${V_}-${++U_}`),e.href})());return{fingerprint:t.ONBOARDING_RUNTIME_FINGERPRINT,create:()=>new t.HouseplanOnboardingRuntime(this)}},install:e=>{this._onboardingRuntime=e},failed:(e,t)=>{console.error("[houseplan] unable to load onboarding runtime",e),this._showToast(Xm(e=>this._t(e),t))}}),this._space="f1",this._layout={},this._serverStorage=!1,this._loadOk=!1,this._serverCanWrite=null,this._loading=!1,this._loadTries=0,this._serverCfg=null,this._cfgRev=0,this._cfgContentFingerprint="",this._unsubCfg=null,this._unsubLayout=null,this._unsubVirtual=null,this._liveSyncAttempt=null,this._liveSyncGeneration=0,this._liveSyncConnection=null,this._layoutRev=0,this._layoutContentFingerprint="",this._virtualLights=Dc(null),this._canOptimizeUndo=!1,this._undoKind=null,this._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._hoverRoom=null,this._pointerModality=new Tm(this,()=>this._syncPointerHoverTargets()),this._devicePressAnimations=new Map,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...tr},this._decorStyleSeeded=!1,this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorEraseConfirm=null,this._decorTextDialog=null,this._decorShapeDialog=null,this._backdropDialog=null,this._decorTextSelection={start:0,end:0},this._furnPalette=null,this._furnCategory=null,this._furnPreviewInput=null,this._furnTouchPending=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._reducedMotion=!1,this._onMotionChange=e=>{this._reducedMotion=e.matches,this._cancelDevicePressFeedback(),e.matches&&this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:e.matches&&this._modeTransition.active&&this._cancelModeTransition(!0),this.requestUpdate()},this._editorChromeMode="plan",this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransitionRequest=0,this._modeTransitionTargetZoom=1,this._modeTransitionEditorCamera=null,this._modeTransition=new Ym({frame:e=>this._applyModeTransitionFrame(e),settled:e=>this._settleModeTransition(e)}),this._editorSecondaryCopy={groupActive:(e,t)=>this._t("editor.group_active",{group:e,item:t}),openGroup:e=>this._t("editor.open_group",{group:e}),disabledAction:(e,t)=>this._t("editor.disabled_action",{action:e,reason:t})},this._tool="draw",this._geometryHistory=new Nm(50),this._wallDialog=null,this._drawWallField=null,this._activeDraftId=null,this._resumeDraftBySpace={},this._physicalSel=null,this._physicalDialog=null,this._partitionDeleteDialog=null,this._roomDeleteDialog=null,this._physicalDrag=null,this._physicalRotate=null,this._physicalLastTap=null,this._physicalPickCycle=null,this._wallUnionCacheValue=null,this._wallUnionPool=new Map,this._isoGeometryCache=new Map,this._isoFallback=new Set,this._openingTunnelCache=null,this._openingWallIndexCache=new Map,this._openingPlacementIntervalsCache=null,this._openingDimensionContextCache=null,this._planSnapGeometryCache=null,this._planStructuralGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._physicalBodiesCache=null,this._lightPhysicalBodiesCache=null,this._cleanFloorCache=new Map,this._innerContourCache=new Map,this._glowRuntimeState=qp(),this._glowRuntimeHost={window:()=>this.ownerDocument.defaultView||window,isConnected:()=>this.isConnected,requestUpdate:()=>this.requestUpdate(),reducedMotion:()=>this._reducedMotion},this._lightBarrierCache=null,this._lightBarrierPool=new Map,this._glowScreenBlend=!1,this._duplicateColumnId=null,this._duplicateColumnTimer=0,this._rszLimitViolation=null,this._path=[],this._cursorPt=null,this._planSnapHover=null,this._mergeSel=null,this._openingPreset=null,this._openingRebindId=null,this._openingPresetRevision=0,this._openingHoverCandidate=null,this._openingJambBlockCm=null,this._openingDialog=null,this._openingInfo=null,this._opDrag=null,this._opMeasure=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._wallFaceBatch=null,this._wallRepairDiagnostic=null,this._wallFaceGraphCache=[],this._areaSel="",this._nameSel="",this._roomDialog=!1,this._roomEditId=null,this._roomFill="",this._roomCustomFill=null,this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1,this._zoom=1,this._view=null,this._zoomBySpace={},this._viewModeSnap=null,this._pointers=new Map,this._panStart=null,this._panLock=null,this._pinchStart=null,this._suppressClick=!1,this._touchContacts=new Map,this._touchSequenceMultitouch=!1,this._touchClickBlockUntil=0,this._connectedPath="",this._routeDepartureHandled=!1,this._onLocationChanged=()=>{this._connectedPath&&location.pathname!==this._connectedPath?this._leaveCardRoute():location.pathname===this._connectedPath&&(this._routeDepartureHandled=!1)},this._touchGestureGuard={capture:!0,handleEvent:e=>this._guardTouchGesture(e)},this._hdrH=118,this._booting=!0,this._bootFading=!1,this._bootSettling=!1,this._bootSettleRaf=0,this._bootLastH=-1,this._bootStart=0,this._bootLastChange=0,this._bootSoft=!1,this._tapConfirm=null,this._onboardingShown=!1,this._rulesDialog=null,this._preflightClipboardFallback=null,this._haIntegrationVersion=null,this._alignDialog=null,this._settingsDialog=null,this._backupExportDialog=null,this._backupImportDialog=null,this._sunRaysCache=null,this._dayCycleTimer=0,this._dayCycleClockKey="",this._compassDrag=!1,this._importDialog=null,this._importQueue=[],this._importTotal=0,this._rulesCompiledSrc="",this._infoCard=null,this._nativeMoreInfoEntity=null,this._deviceInbox=null,this._deviceInboxReturn=null,this._deviceInboxMemo=null,this._markerDialog=null,this._spaceDialog=null,this._keyHandler=e=>this._onKey(e),this._warmVp=null,this._warmVpArmed=!1,this._warmLongReturn=!1,this._warmRevivePending=!1,this._warmGen=++J_,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._labs={active:Object.freeze([]),space:""},this._viewPreference={},this._renderProjection="flat",this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._activityRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._continuity=this._newContinuityController(),this._continuityHistory=[],this._continuityEpoch=0,this._continuityDataReady=!0,this._continuityPaintToken=-1,this._continuityDisposed=!1,this._renderSnapshotAt=Date.now(),this._hassSequence=0,this._visibleDeviceSnapshot=null,this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._capturedSnapshotSequence=-1,this._capturedSnapshotDevices=null,this._capturedSnapshotLayout=null,this._capturedSnapshotActivity="",this._capturedSnapshotConfigEpoch=-1,this._capturedSnapshotVirtual="",this._lastValidStageSize=null,this._pendingRefitSize=null,this._refitRaf=0,this._pageVisibility=e=>{if(this._continuity.visibility(e),this._dayCycleVisibility(e),"hidden"===e.kind)return this._clearTransientHover(!0),this._cancelDevicePressFeedback(),void(this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:this._cancelModeTransition(!0));if(this._vacJumpOnce=!0,!e.long){const e=Date.now();let t=!1;for(const i of this._activityRt.values())!i.flashKind||(i.expiresAt||i.flashTs+xm)>e||(i.flashTs=0,i.flashKind=null,i.expiresAt=0,t=!0);return void(t&&this.requestUpdate())}Date.now()-this._renderSnapshotAt>1e3&&this._continuity.note("device-snapshot-stale"),this._continuityDataReady=!1,this._continuityPaintToken=-1,this._resumeSettling=!0,this._loading?this.requestUpdate():this._loadFromServer()},this._resumeSettling=!1,this._viewportInvalidAt=0,this._vacFit=null,this._vacAllCamerasFor=null,this._vacAllCameraCache=null,this._vacCalConfirm=null,this._kioskDots=!1,this._cyclePausedUntil=0,this._swipeStart=null,this._tabDrag=null,this._tabDragRelease=null,this._tabSuppressClick=!1,this._tabOrderWarned=!1,this._lastTap=0,this._onLabsSnapshot=e=>{const t=this._effectiveProjection();this._labs=e;const i=this._effectiveProjection();this._convertProjectionView(t,i),this.requestUpdate()},this._onHashChange=()=>{if(this._hasFixedFloor)return;const e=this._hashSpace();if(e&&this._model.find(t=>t.id===e)&&e!==this._space){if(this._wallFaceBatch&&this._roomDialogCancel(),"plan"===this._mode&&"draw"===this._tool&&!this._finishWallChain())return;this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._commitSpace(e),this._selId=null,this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),this.requestUpdate()}},this._drag=null,this._rlResize=null,this._holdFired=!1,this._retryContinuity=()=>{this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.retry(this._continuity.recoveryReason||"plan"),this._loading||this._loadFromServer()},this._cfgEpoch=0,this._modelCache=null,this._emptySpaceStateActive=!1,this._decorSnapCache=null,this._markerPreviewMemo=null,this._markerPreviewDevicesMemo=null,this._showHidden=!1,this._connHooked=null,this._connectionWasLost=!1,this._haRegistryConnection=null,this._haRegistryRev=-1,this._haBindingCacheKey="",this._planHassMemo=null,this._onHaRegistryUpdate=()=>{const e=Kc(this.hass);e.revision===this._haRegistryRev&&this._devices.length||(this._haRegistryRev=e.revision,this._planHassMemo=null,this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate())},this._onConnReady=()=>{if(this._loadTries=0,clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,function(e){const t=qc(e);t&&(Uc(e,t),Vc(e,t))}(this.hass),this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(this._onConnReady,400));!this._connectionWasLost&&this._continuity.hasCompleteFrame?this._beginContinuityCandidate("connection-ready",!1,"plan"):(this._continuityDataReady=!1,this._continuityPaintToken=-1),this._loading||this._loadFromServer()},this._onConnLost=()=>{this._booting&&!this._continuity.hasCompleteFrame||(this._connectionWasLost=!0,this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.connectionLost())},this._signer=new Dl(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=mf(()=>{if(this._serverStorage){const e=[...this._dirtyPos];this._dirtyPos.clear();for(const t of e){const e=Kr(this._layout[t]);e&&(this._layout={...this._layout,[t]:e},this._sentPos.set(t,e),this.hass.callWS({type:"houseplan/layout/update",device_id:t,pos:e}).then(e=>this._noteLayoutRev(e)).catch(e=>this._showToast(this._t("toast.pos_save_failed",{err:this._errText(e)}))).finally(()=>{this._sentPos.get(t)===e&&this._sentPos.delete(t)}))}this._cacheSnapshot()}else this._layout=Yr(Fr(this._layout)),localStorage.setItem(tf,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._pendingPhysicalWrites=new Map,this._undoGeometry=()=>this._editorRuntimeOrThrow()._undoGeometry(),this._redoGeometry=()=>this._editorRuntimeOrThrow()._redoGeometry(),this._saveConfigDebounced=mf(()=>{this._serverCfg&&this._writeConfig().catch(e=>{const t=!0===e?.physicalGeometryRolledBack;"geometry-unsafe"!==e?.code&&("wall_model_client_outdated"===e?.code?this._showToast(this._t("toast.wall_model_client_outdated")):"conflict"===e?.code?(this._showToast(this._t("toast.conflict")),t||(this._cancelPath(),this._reloadConfigOnly(!0))):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(e)})),t&&this._reloadRejectedPhysicalWrite())})},500),this._draftSegmentCms=[],this._closingWallCm=null,this._savePhysicalDialog=()=>this._editorRuntimeOrThrow()._savePhysicalDialog(),this._deletePhysicalSelection=()=>this._editorRuntimeOrThrow()._deletePhysicalSelection(),this._confirmPartitionDelete=()=>this._editorRuntimeOrThrow()._confirmPartitionDelete(),this._deleteDraftWhole=()=>this._editorRuntimeOrThrow()._deleteDraftWhole(),this._deleteDraftSegment=()=>this._editorRuntimeOrThrow()._deleteDraftSegment(),this._confirmRoomDelete=e=>this._editorRuntimeOrThrow()._confirmRoomDelete(e),this._rebindPartitionOpening=()=>this._editorRuntimeOrThrow()._rebindPartitionOpening(),this._keepClosedAsPartitions=()=>this._editorRuntimeOrThrow()._keepClosedAsPartitions(),this._toggleServerPlans=async()=>this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._toggleServerPlans():this._editorRuntimeOrThrow()._toggleServerPlans(),this._aspectJob=null,this._dayCycleTick=()=>{if(!this.isConnected||"hidden"===this.ownerDocument.visibilityState)return;const e=this._dayCycleState();if(!e)return this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),void(this._dayCycleClockKey="");const t=Nn(e);t!==this._dayCycleClockKey&&(this._dayCycleClockKey=t,this.requestUpdate())},this._sunShown=!1,this._sunOut=!1,this._sunOutTimer=0,this._openSettingsDialog=()=>{if(this._editorRuntime)return this._editorRuntime._openSettingsDialog();this._ensureEditorRuntime().then(e=>{e&&this._openSettingsDialog()})},this._reportedPreflightFingerprint=null,this._openAlignDialog=()=>this._editorRuntimeOrThrow()._openAlignDialog(),this._toggleOptimizeLivePositions=()=>this._editorRuntimeOrThrow()._toggleOptimizeLivePositions(),this._optimizeUndoBusy=!1,this._openBackupExport=()=>this._editorRuntimeOrThrow()._openBackupExport(),this._openRulesDialog=()=>this._editorRuntimeOrThrow()._openRulesDialog(),this._climateCache=null,this._gearPtCache=new WeakMap}_editorRuntimeOrThrow(){if(!this._editorRuntime)throw new Error("Houseplan editor runtime is not loaded");return this._editorRuntime}async _ensureEditorRuntime(){return this._editorRuntimeLoader.ensure()}async _ensureOnboardingRuntime(){return this._onboardingRuntimeLoader.ensure()}_editorRuntimeStateChanged(e){clearTimeout(this._editorRuntimeLoadingTimer),this._editorRuntimeLoadingTimer=void 0,"loading"!==e?(this._editorRuntimeLoadingVisible=!1,this.requestUpdate()):this._editorRuntimeLoadingTimer=window.setTimeout(()=>{this._editorRuntimeLoadingVisible=!0,this.requestUpdate()},150)}async _requestMode(e,t=!0,i=!1){const n=++this._editorModeRequest;if(i&&(this._warmModeRequest=n,this._refitRaf&&(cancelAnimationFrame(this._refitRaf),this._refitRaf=0),this._pendingRefitSize=null),"view"===e||await this._ensureEditorRuntime()){if(n===this._editorModeRequest&&this.isConnected)return i?(this._adoptMode(e),this._warmRevivePending&&(clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmReviveDialog()),this.requestUpdate(),void this.updateComplete.then(()=>requestAnimationFrame(()=>requestAnimationFrame(()=>{if(this._warmModeRequest!==n||n!==this._editorModeRequest)return;const e=this._stageEl;this._lastValidStageSize=e&&e.clientWidth>0&&e.clientHeight>0?[e.clientWidth,e.clientHeight]:null,this._pendingRefitSize=null,this._warmModeRequest=0})))):void this._setMode(e,t);this._warmModeRequest===n&&(this._warmModeRequest=0)}else this._warmModeRequest===n&&(this._warmModeRequest=0)}_seedDecorStyle(e){if(this._decorStyleSeeded||!e)return;this._decorStyleSeeded=!0;const t=e.settings?.decor_default_style;t&&(this._decorStyle=function(e,t){if(!e||"object"!=typeof e)return{...t};const i=e,n="string"==typeof i.color&&i.color?i.color:t.color,r="string"==typeof i.fill_color&&i.fill_color?i.fill_color:t.fillColor,o=Number(i.width_cm);return{color:n,opacity:ir(i.opacity,t.opacity),widthCm:Number.isFinite(o)&&o>0?Math.max(.1,Math.min(100,o)):t.widthCm,fill:"boolean"==typeof i.fill?i.fill:t.fill,fillColor:r,fillOpacity:ir(i.fill_opacity,t.fillOpacity)}}(t,tr))}get _canEdit(){return!!this._norm&&(!0===this._serverCanWrite||!1!==this._serverCanWrite&&!0===this.hass?.user?.is_admin)}get _kiosk(){return!!this._config?.kiosk}_showKioskDots(){this._kioskDots=!0,clearTimeout(this._kioskDotsTimer),this._kioskDotsTimer=window.setTimeout(()=>this._kioskDots=!1,2500)}get _modeTransitionBusy(){return this._modeTransitionPreparing||this._modeTransition.active}_cssColor(e,t){const i=String(e||"").trim();if(!i)return t;const n=document.createElement("span");n.style.cssText=`position:absolute;visibility:hidden;color:${i}`,this.renderRoot.append(n);const r=getComputedStyle(n).color||t;return n.remove(),r}_currentModeVisual(e=this._mode){const t=this._modeTransition.presented||this._modeTransitionVisual;if(t)return{...t,viewport:{...t.viewport,viewBox:{...t.viewport.viewBox}}};const i=this._stageEl;if(!i||i.clientWidth<=0||i.clientHeight<=0)return null;const n=this.renderRoot.querySelector(".editorchrome"),r=this._viewOr(this._baseVb()),o=this.renderRoot.querySelector(".hp-paper"),s=this.renderRoot.querySelector(".hp-backdrop"),a=this.renderRoot.querySelector(".zoomwrap"),l=a?getComputedStyle(a).filter:"",c=Number(/brightness\(([^)]+)\)/.exec(l)?.[1]);return{presentedMode:e,editorChromeHeight:"view"===e?0:n?.getBoundingClientRect().height||0,stageWidth:i.clientWidth,stageHeight:i.clientHeight,viewport:jm(r,i.clientWidth),stageColor:getComputedStyle(i).backgroundColor||"rgb(255, 255, 255)",paperColor:o?getComputedStyle(o).fill:"rgb(255, 255, 255)",sceneBrightness:Number.isFinite(c)?c:1,architectureOpacity:"decor"===e?.35:1,backdropOpacity:s&&Number(getComputedStyle(s).opacity)||1,viewWeight:"view"===e?1:0,editorWeight:"view"===e?0:1,toolbarContentOpacity:"view"===e?0:1}}_viewForModeTarget(e,t,i,n,r){const o=fi(this._baseVb(),n/Math.max(1,r)),s=Math.min(ff.ZOOM_MAX,Math.max(ff.ZOOM_MIN,e)),a=o.w/s,l=o.h/s,c=t??o.x+o.w/2,h=i??o.y+o.h/2;return this._clampView({x:c-a/2,y:h-l/2,w:a,h:l},o)}_targetStageColor(e){return"view"!==e?"rgb(255, 255, 255)":this._cssColor(this._stageBg(this._spaceDisplayForRender()),this._cssColor("var(--ha-card-background, var(--card-background-color, #111))","rgb(17, 17, 17)"))}_targetPaperColor(e){return"view"===e&&this._spaceModel()?.bg?this._cssColor("var(--ha-card-background, var(--card-background-color, #111))","rgb(17, 17, 17)"):"rgb(255, 255, 255)"}_targetBrightness(e){return 1}_applyModeTransitionFrame(e){e.targetMode===this._mode&&(this._modeTransitionVisual=e.presented,this._view={...e.presented.viewport.viewBox},this.requestUpdate())}_settleModeTransition(e){if(e.targetMode!==this._mode)return;const t=this._modeTransitionRequest;this._view={...e.to.viewport.viewBox},this._zoom=this._modeTransitionTargetZoom,this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._lastValidStageSize=[e.to.stageWidth,e.to.stageHeight],"view"===this._mode&&(this._saveZoom(),this._viewModeSnap=null,this._modeTransitionEditorCamera=null),this.requestUpdate(),this.updateComplete.then(()=>{if(!this.isConnected||t!==this._modeTransitionRequest||this._modeTransitionBusy||e.targetMode!==this._mode)return;const i=this._stageEl;if(i&&i.clientWidth>0&&i.clientHeight>0&&(Math.abs(i.clientWidth-e.to.stageWidth)>.5||Math.abs(i.clientHeight-e.to.stageHeight)>.5)){const e=this._view;this._lastValidStageSize=[i.clientWidth,i.clientHeight],this._applyView(this._zoom,e?e.x+e.w/2:void 0,e?e.y+e.h/2:void 0),this.requestUpdate()}const n=this.renderRoot.activeElement;(!n||!n.isConnected||!!n.closest?.(".editorchrome, .stage"))&&this.renderRoot.querySelector(".modetab.active")?.focus?.({preventScroll:!0})})}_cancelModeTransition(e=!0){const t=!!this._modeTransition.state;this._modeTransitionRequest++,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransition.cancel(e),e&&t||(this._modeTransitionVisual=null)}_adoptMode(e){this._cancelModeTransition(!1),this._mode=e,"view"!==e&&(this._editorChromeMode=e)}_commitViewModeAtomic(e,t,i,n){if(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._modeTransitionForceAtomic=!1,this._zoom=t,e){const r=this._stageEl?.clientWidth||e.stageWidth,o=Math.max(1,e.stageHeight+e.editorChromeHeight);this._view=this._viewForModeTarget(t,i,n,r,o),this._lastValidStageSize=[r,o]}else this._view=null;this._viewModeSnap=null,this._modeTransitionEditorCamera=null,this._saveZoom(),this.requestUpdate()}_prepareModeTransition(e,t,i,n,r,o){this.updateComplete.then(()=>{if(!this.isConnected||e!==this._modeTransitionRequest||this._mode!==i)return void(e===this._modeTransitionRequest&&(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._modeTransitionForceAtomic=!1,this.requestUpdate()));const s=this.renderRoot.querySelector(".editorchrome"),a=s?.querySelector(".editorchrome-inner"),l="view"===i?0:a?.scrollHeight||a?.getBoundingClientRect().height||0,c=Math.max(1,t.stageHeight+t.editorChromeHeight),h=Math.max(1,c-l),d=this._stageEl?.clientWidth||t.stageWidth;if(d<=0||h<=0)return this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._applyView(n,r,o),void this.requestUpdate();const u=this._viewForModeTarget(n,r,o,d,h),p={presentedMode:i,editorChromeHeight:l,stageWidth:d,stageHeight:h,viewport:jm(u,d),stageColor:this._targetStageColor(i),paperColor:this._targetPaperColor(i),sceneBrightness:this._targetBrightness(i),architectureOpacity:"decor"===i?.35:1,backdropOpacity:"decor"===i&&"backdrop"!==this._decorTool?.5:1,viewWeight:"view"===i?1:0,editorWeight:"view"===i?0:1,toolbarContentOpacity:"view"===i?0:1};this._modeTransitionPreparing=!1;const m=this._modeTransitionForceAtomic;this._modeTransitionForceAtomic=!1,this._modeTransition.start(t,p,i,this._reducedMotion||m?0:220)})}get _hasFixedFloor(){return!!this._config&&Object.prototype.hasOwnProperty.call(this._config,"floor")}_fixedFloorState(e=this._model,t=this._loadOk){const i=this._config?.floor,n=function(e){if(!e.hasFloor)return{kind:"absent"};const t=e.floor;if("string"==typeof t)return t.length?e.spaceIds.includes(t)?{kind:"valid",id:t,source:"id"}:{kind:"invalid",reason:"unknown-id",value:t}:{kind:"invalid",reason:"empty-id",value:t};if("number"==typeof t){if(!Number.isFinite(t))return{kind:"invalid",reason:"non-finite-index",value:t};if(!Number.isInteger(t))return{kind:"invalid",reason:"fractional-index",value:t};if(t<0)return{kind:"invalid",reason:"negative-index",value:t};const i=e.spaceIds[t];return void 0===i?{kind:"invalid",reason:"out-of-range-index",value:t}:{kind:"valid",id:i,source:"index"}}return{kind:"invalid",reason:"invalid-type",value:t}}({spaceIds:e.map(e=>e.id),hasFloor:this._hasFixedFloor,floor:i});return!this._hasFixedFloor||t?n:"number"==typeof i?"valid"===n.kind||"invalid"===n.kind&&"out-of-range-index"===n.reason?{kind:"pending",value:i}:n:"valid"===n.kind||"invalid"===n.kind&&"unknown-id"!==n.reason?n:{kind:"pending",value:i}}_canCommitSpace(e,t=!1){if(t||!this._hasFixedFloor)return!0;const i=this._fixedFloorState();return"valid"===i.kind&&i.id===e}_commitSpace(e,t=!1){return!!this._canCommitSpace(e,t)&&(e!==this._space&&(this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._editorRuntime?._clearFurniturePreview()),this._space=e,!0)}_slideTo(e,t){if(e===this._space)return!0;if(!this._canCommitSpace(e))return!1;if(this._wallFaceBatch&&this._roomDialogCancel(),"plan"===this._mode&&"draw"===this._tool&&!this._finishWallChain())return!1;this._cancelModeTransition(!0);const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;return this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._commitSpace(e),this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._selId=null,this._physicalSel=null,this._editorSecondary?.closeForNavigation(),this._physicalDialog=null,this._physicalDrag=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),i||(this._slide=t,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slideTimer=void 0,this._slide="",this.requestUpdate()},190),this.requestUpdate()),!0}get _canReorderTabs(){return j_({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:"mouse",spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})}_tabPointerDown(e,t){j_({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:e.pointerType,spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})&&(_f(e),this._tabDragRelease=e=>this._tabPointerUp(e),window.addEventListener("pointerup",this._tabDragRelease),window.addEventListener("pointercancel",this._tabDragRelease),this._tabDrag={id:t,pointerId:e.pointerId,x:e.clientX,y:e.clientY,moved:!1,targetId:null,placement:null})}_tabDropTargetAt(e,t,i){const n=this._model.map(e=>e.id),r=n.indexOf(i);if(r<0)return null;const o=this.renderRoot.querySelectorAll('[data-hp="space-tab"]');for(const s of o){const o=s.dataset.id||"";if(!o||o===i)continue;const a=s.getBoundingClientRect();if(ea.right||ta.bottom)continue;const l=n.indexOf(o);return l<0?null:{targetId:o,placement:l=4)))return;var i,n;const r=this._tabDropTargetAt(e.clientX,e.clientY,t.id);t.moved&&t.targetId===r?.targetId&&t.placement===r?.placement||(this._tabDrag={...t,moved:!0,targetId:r?.targetId||null,placement:r?.placement||null})}_tabPointerUp(e){const t=this._tabDrag;if(t&&t.pointerId!==e.pointerId)return;const i="pointerup"===e.type&&t?.moved?this._tabDropTargetAt(e.clientX,e.clientY,t.id):null;"pointerup"===e.type&&t?.moved&&this._suppressNextTabClick(),this._endTabDrag(),t?.moved&&i&&this._commitTabOrder(t.id,i.targetId)}_endTabDrag(){this._tabDrag=null,this._tabDragRelease&&(window.removeEventListener("pointerup",this._tabDragRelease),window.removeEventListener("pointercancel",this._tabDragRelease),this._tabDragRelease=null)}_tabClick(e){if(this._tabSuppressClick)return this._tabSuppressClick=!1,clearTimeout(this._tabSuppressClickTimer),void(this._tabSuppressClickTimer=void 0);this._pickSpace(e)}_suppressNextTabClick(){this._tabSuppressClick=!0,clearTimeout(this._tabSuppressClickTimer),this._tabSuppressClickTimer=window.setTimeout(()=>{this._tabSuppressClick=!1,this._tabSuppressClickTimer=void 0},0)}_commitTabOrder(e,t){const i=this._serverCfg;if(!i||!this._canReorderTabs)return;const n=this._model.map(e=>e.id),r=function(e,t,i){const n=e.indexOf(t),r=e.indexOf(i);if(n<0||r<0||n===r)return e;const o=e.slice();return o.splice(n,1),o.splice(r,0,t),o}(n,e,t);if(r===n)return;const o=new Map(this._devices.map(e=>[String(e.id),String(e.area||"")])),s=function(e,t,i,n=()=>""){if(!i)return[];const r=[];for(const o of e){if(!o||!0===o.removed)continue;const e="string"==typeof o.id?o.id:"";if(!e)continue;if("string"==typeof o.space&&o.space)continue;const s=("string"==typeof o.area?o.area:"")||n(e)||"";s&&t[s]||r.push({id:e,space:i})}return r}(i.markers||[],Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),n[0]||"",e=>o.get(e)||"");if(s.length){const e=new Map(s.map(e=>[e.id,e.space]));for(const t of i.markers||[]){const i=e.get(String(t.id));i&&(t.space=i)}}i.spaces=function(e,t){const i=new Map(t.map((e,t)=>[e,t]));return e.map((e,t)=>({space:e,index:t})).sort((e,t)=>(i.get(String(e.space?.id))??Number.MAX_SAFE_INTEGER)-(i.get(String(t.space?.id))??Number.MAX_SAFE_INTEGER)||e.index-t.index).map(e=>e.space)}(i.spaces||[],r),this._saveConfig(),this._tabOrderWarned||(this._tabOrderWarned=!0,this._showToast(this._t("toast.space_order_changed")))}_pickSpace(e){if(this._endTabDrag(),e===this._space)return;const t=this._model.map(e=>e.id),i=t.indexOf(this._space),n=t.indexOf(e);this._navApplied=!0,this._showFar=!1,this._frame=null,this._slideTo(e,i>=0&&n0&&Date.now()>=this._cyclePausedUntil&&this._model.length>1&&this._zoom<=1.001){const e=this._model.map(e=>e.id),t=e.indexOf(this._space);this._slideTo(e[(t+1)%e.length],"left"),this._showKioskDots()}}_syncCycleTimer(){clearInterval(this._cycleTimer),this._cycleTimer=void 0,this.isConnected&&!this._hasFixedFloor&&this._config?.kiosk&&Number(this._config.cycle)>0&&(this._cycleTimer=window.setInterval(()=>this._cycleTick(),1e3*Number(this._config.cycle)))}get _editing(){return"plan"===this._mode||"devices"===this._mode||"decor"===this._mode}get _markup(){return"plan"===this._mode}get _wallUnionCache(){return this._wallUnionCacheValue}set _wallUnionCache(e){this._wallUnionCacheValue=e,null===e&&this._wallUnionPool.clear()}get _glowClipCache(){return this._glowRuntimeState.clipCache}get _glowGeometryWarnings(){return this._glowRuntimeState.geometryWarnings}get _glowFeatherUnits(){return this._glowRuntimeState.featherUnits}set _glowFeatherUnits(e){this._glowRuntimeState.featherUnits=e}get _glowRenderedSources(){return this._glowRuntimeState.renderedSources}get _glowLastAppearance(){return this._glowRuntimeState.lastAppearance}get _glowEnteringSources(){return this._glowRuntimeState.enteringSources}get _glowEnterRafs(){return this._glowRuntimeState.enterRafs}get _glowFadeTimers(){return this._glowRuntimeState.fadeTimers}get _glowFeatherSuspendUntil(){return this._glowRuntimeState.featherSuspendUntil}set _glowFeatherSuspendUntil(e){this._glowRuntimeState.featherSuspendUntil=e}get _glowFeatherResumeTimer(){return this._glowRuntimeState.featherResumeTimer}set _glowFeatherResumeTimer(e){this._glowRuntimeState.featherResumeTimer=e}get _glowSourceSeq(){return this._glowRuntimeState.sourceSeq}set _glowSourceSeq(e){this._glowRuntimeState.sourceSeq=e}_newContinuityController(){return new Ml(()=>{this._resumeSettling="steady"!==this._continuity.state,this._continuityEpoch++,this.isConnected&&this.requestUpdate()})}get _labsIso(){return this._labs.active.includes("iso")}get _desiredProjection(){return"view"===this._mode&&this._labsIso&&"iso"===this._viewPreference[this._space]?"iso":"flat"}_saveViewPreference(){try{localStorage.setItem(af,JSON.stringify(this._viewPreference))}catch{}}_logicalViewCenter(e){const t=this._view;if(!t)return null;const i=[t.x+t.w/2,t.y+t.h/2],n="iso"===e?b_(i):i;return{x:n[0],y:n[1]}}_convertProjectionView(e,t){if(e===t)return;const i=this._logicalViewCenter(e);this._view=null;const n=i?"iso"===t?y_([i.x,i.y],0):[i.x,i.y]:null;this._applyView(this._zoom,n?.[0],n?.[1]),this._warmPatch({vp:this._warmViewportState()}),this.requestUpdate()}_setProjection(e){if(!this._labsIso||"view"!==this._mode)return;const t=this._effectiveProjection();if(this._viewPreference={...this._viewPreference,[this._space]:e},"iso"===e){const e=this._isoSceneKey();e&&this._isoFallback.delete(e)}this._saveViewPreference();const i=this._effectiveProjection();this._convertProjectionView(t,i)}_hashSpace(){return s_(window.location.hash||"")}connectedCallback(){this._connectedPath=location.pathname,this._routeDepartureHandled=!1,window.addEventListener("location-changed",this._onLocationChanged),window.addEventListener("popstate",this._onLocationChanged),this._continuityDisposed&&(this._continuity=this._newContinuityController(),this._continuityDisposed=!1,this._continuityPaintToken=-1);const e=Pm(this.ownerDocument);var t;void 0!==e&&(this._glowScreenBlend=e),this._continuityUnsub?.(),this._continuityUnsub=$l(this.ownerDocument,this._pageVisibility),this._languageFailureUnsub?.(),this._languageFailureUnsub=(t=()=>{this._showToast(this._t("toast.locale_load_failed"))},Gd.add(t),()=>Gd.delete(t)),super.connectedCallback(),this._pointerModality.connect(this.ownerDocument.defaultView);const i=this.ownerDocument.defaultView?.MutationObserver;i&&(this._pointerHoverObserver=new i(e=>{for(const t of e)for(const e of t.addedNodes)this._syncPointerHoverSubtree(e)}),this._pointerHoverObserver.observe(this.renderRoot,{childList:!0,subtree:!0})),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),Am(this.ownerDocument).then(e=>{e!==this._glowScreenBlend&&(this._glowScreenBlend=e,this.isConnected&&this.requestUpdate())}),this.hass&&this._ensureHaRegistryAuthority(),window.addEventListener("keydown",this._keyHandler),this._signer.start(()=>this.hass,()=>mn(this._serverCfg)),this._syncCycleTimer(),window.addEventListener("hashchange",this._onHashChange),this._labsUnsub?.(),this._labsUnsub=function(e,t){p_.add(t);const i=f_(e);return"undefined"==typeof window&&t(i),()=>{p_.delete(t)}}(V_,this._onLabsSnapshot),this._booting?this._bootWatch():this._bootFading&&(clearTimeout(this._bootTimer),this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220)),this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},K_)),!this._loadOk&&this._serverCfg&&this.hass&&this._scheduleLoadRetry(),!this._warmSlot&&this._config&&this._warmAdopt(),this._loadOk&&this._ensureLiveSyncSubscriptions(),this._warmVp&&!this._warmRevivePending&&void 0===this._warmReviveTimer&&(this._warmRevivePending=!0,this._warmReviveTimer=window.setTimeout(()=>this._warmReviveDialog(),0)),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1,this.requestUpdate()}disconnectedCallback(){this._connectedPath&&location.pathname!==this._connectedPath&&this._leaveCardRoute(),window.removeEventListener("location-changed",this._onLocationChanged),window.removeEventListener("popstate",this._onLocationChanged),this._continuityUnsub?.(),this._continuityUnsub=void 0,this._languageFailureUnsub?.(),this._languageFailureUnsub=void 0,this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._refitRaf&&(cancelAnimationFrame(this._refitRaf),this._refitRaf=0),this._warmModeRequest=0,this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),this._dayCycleClockKey="",this._bootSettleRaf&&(cancelAnimationFrame(this._bootSettleRaf),this._bootSettleRaf=0),this._bootSettling=!1;for(const e of this._activityRt.values())clearTimeout(e.timer);window.removeEventListener("keydown",this._keyHandler),this._endTabDrag(),clearTimeout(this._tabSuppressClickTimer),this._tabSuppressClickTimer=void 0,this._tabSuppressClick=!1,clearInterval(this._cycleTimer),clearTimeout(this._kioskDotsTimer),clearTimeout(this._kioskHoldTimer),clearTimeout(this._reloadRetry),clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),this._connHooked=null,this._haRegistryRelease?.(),this._haRegistryRelease=void 0,this._haRegistryConnection=null,this._signer.dispose(),clearTimeout(this._toastTimer),clearTimeout(this._slideTimer),clearTimeout(this._editorRuntimeLoadingTimer),this._editorRuntimeLoadingTimer=void 0,this._editorRuntimeLoadingVisible=!1,this._modeTransition.dispose(),this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransitionRequest++,this._slideTimer=void 0,this._slide="",clearTimeout(this._bootTimer),this._bootTimer=void 0,clearTimeout(this._bootSoftTimer),this._saveConfigDebounced.flush(),window.removeEventListener("hashchange",this._onHashChange),this._labsUnsub?.(),this._labsUnsub=void 0,clearTimeout(this._holdTimer),this._roViewport?.disconnect(),this._roViewport=void 0,this._roHdr?.disconnect(),this._roHdr=void 0,this._onWinResize&&(window.removeEventListener("resize",this._onWinResize),this._onWinResize=void 0),this._unsubCfg&&(this._unsubCfg(),this._unsubCfg=null),this._unsubLayout&&(this._unsubLayout(),this._unsubLayout=null),this._unsubVirtual&&(this._unsubVirtual(),this._unsubVirtual=null),this._unsubTrail&&(this._unsubTrail(),this._unsubTrail=void 0),this._liveSyncGeneration++,this._liveSyncAttempt=null,this._liveSyncConnection=null,clearTimeout(this._layoutSyncTimer),clearTimeout(this._duplicateColumnTimer),tm(this._glowRuntimeState,this._glowRuntimeHost),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._editorRuntime?._clearFurniturePreview(),this._editorRuntime?._furnShiftDetach(),this._touchContacts.clear(),this._touchSequenceMultitouch=!1,this._touchClickBlockUntil=0,this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._pointerHoverObserver?.disconnect(),this._pointerHoverObserver=void 0,this._pointerModality.disconnect(),this._editorSecondary?.reset(),this._resumeSettling=!1,this._continuityHistory=[...this._continuityHistory,...this._continuity.trace].slice(-80),this._continuity.dispose(),this._continuityDisposed=!0,super.disconnectedCallback()}_onKey(e){if("Escape"===e.key&&this._vacFit)return this._vacFit=null,this._showToast(this._t("vac.cal_cancelled")),void e.stopPropagation();if("Escape"===e.key){if(this._tapConfirm)return void(this._tapConfirm=null);if(this._vacCalConfirm)return void(this._vacCalConfirm=null);if(this._decorEraseConfirm)return void(this._decorEraseConfirm=null);if(this._openingInfo)return void(this._openingInfo=null);if(this._infoCard)return void this._closeInfoCard();if(this._rulesDialog)return void(this._rulesDialog=null);if(this._alignDialog)return this._alignDialog=null,void(this._preflightClipboardFallback=null);if(this._backupImportDialog)return void(this._backupImportDialog=null);if(this._backupExportDialog)return void(this._backupExportDialog=null);if(this._settingsDialog)return void(this._settingsDialog=null);if(this._markerDialog)return void this._closeMarkerDialog();if(this._deviceInbox)return void(this._deviceInbox=null);if(this._openingDialog)return void(this._openingDialog=null);if(this._physicalDialog)return void(this._physicalDialog=null);if(this._backdropDialog)return void(this._backdropDialog=null);if(this._decorShapeDialog)return void(this._decorShapeDialog=null);if(this._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0);if(this._editorSecondary?.hasOpenGroup)return e.preventDefault(),void this._editorSecondary?.closeGroup(!0)}const t=e.composedPath?.()||[e.target],i=t.some(e=>e?.matches?.('input, textarea, select, [contenteditable="true"]')),n=t.some(e=>e?.classList?.contains?.("editor-secondary")),r=e.ctrlKey||e.metaKey,o=e.key.toLowerCase(),s=/^[a-z]$/.test(o),a="z"===o||!s&&"KeyZ"===e.code,l="y"===o||!s&&"KeyY"===e.code,c=r&&(a&&e.shiftKey||l),h=r&&a&&!e.shiftKey;if("decor"===this._mode){if((h||c)&&i)return;return c?(e.preventDefault(),void this._redoGeometry()):h?(e.preventDefault(),this._decorDraft?void(this._decorDraft=null):this._decorMove||this._dtDrag||this._bdDrag?void this._cancelDecorGesture():void this._undoGeometry()):"Delete"!==e.key&&"Backspace"!==e.key||!this._decorSel||i||n?void("Escape"===e.key&&(e.preventDefault(),this._decorDraft?this._decorDraft=null:this._decorMove||this._dtDrag||this._bdDrag?this._cancelDecorGesture():"furniture"===this._decorTool?(this._editorRuntime?._clearFurniturePreview(),this._editorRuntime?._furnShiftDetach(),this._furnPalette=null,this._furnCategory=null,this._decorTool="select"):this._decorSel?this._decorSel=null:"select"!==this._decorTool?this._decorTool="select":this._setMode("view"))):(e.preventDefault(),void this._decorDeleteSel())}if(this._markup&&(!h&&!c||!i)){if(("Delete"===e.key||"Backspace"===e.key)&&this._physicalSel&&!i&&!n)return e.preventDefault(),void this._deletePhysicalSelection();if(c)return e.preventDefault(),void this._redoGeometry();if(h)return e.preventDefault(),this._resize?.dragging?void this._rszCancelDrag():this._wallFaceBatch?(this._roomDialogCancel(),void(this._activeDraftId&&this._path.length>1?this._undoActiveDraftPoint():this._undoPoint())):"draw"===this._tool&&this._path.length?void(this._activeDraftId&&this._path.length>1?this._undoActiveDraftPoint():this._undoPoint()):"split"===this._tool&&this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},void(this._splitSel.pts.length||(this._cursorPt=null))):void this._undoGeometry();if("Escape"===e.key)return this._physicalDrag||this._physicalRotate?(e.preventDefault(),void this._cancelPhysicalGesture()):this._roomDialog?(e.preventDefault(),void this._roomDialogCancel()):"draw"===this._tool&&this._path.length?(e.preventDefault(),void this._finishWallChain()):this._physicalSel?(e.preventDefault(),void(this._physicalSel=null)):"resize"===this._tool?(e.preventDefault(),this._resize?.dragging?void this._rszCancelDrag():("exit-tool"===this._resize?.escapeIdle()&&(this._tool="draw"),void this.requestUpdate())):"split"===this._tool?(e.preventDefault(),void(this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},this._splitSel.pts.length||(this._cursorPt=null)):this._splitSel?this._splitSel=null:this._tool="draw")):"merge"===this._tool?(e.preventDefault(),void(this._mergeSel?this._mergeSel=null:this._tool="draw")):this._wallDialog?(e.preventDefault(),void(this._wallDialog=null)):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool&&"column"!==this._tool||(e.preventDefault(),"opening"===this._tool&&this._clearOpeningPlacement(!0),this._tool="draw"))}}_undoPoint(){if(this._path.length){if(this._contourClosed)return this._path=this._path.slice(0,-1),void(this._closingWallCm=null);if(this._activeDraftId&&this._path.length>1&&this._curSpaceCfg){const e=this._geometrySnapshot();this._path=this._path.slice(0,-1),this._draftSegmentCms=this._draftSegmentCms.slice(0,-1);const t=this._curSpaceCfg,i=(t.room_drafts||[]).findIndex(e=>e.id===this._activeDraftId);if(i>=0){const n=Array.isArray(t.room_drafts[i]?.segments)?t.room_drafts[i].segments:[];this._path.length<2?(t.room_drafts.splice(i,1),t.room_drafts.length||delete t.room_drafts,this._activeDraftId=null):t.room_drafts[i]={id:this._activeDraftId,points:this._path.map(e=>[e[0]/cf,e[1]/cf]),segments:this._draftSegmentCms.map((e,t)=>({...n[t]||{},cm:e}))},this._commitPhysicalGeometry(this._t("history.draft_segment_delete"),e)}return}this._path=this._path.slice(0,-1)}}_undoActiveDraftPoint(){const e=this._activeDraftId,t=this._path[0]?[...this._path[0]]:null,i=this._geometryHistory.undo();if(!i)return void this._undoPoint();if(!this._applyGeometryState(i.before,!0))return void this._geometryHistory.clear();const n=e?this._spaceModel()?.room_drafts.find(t=>t.id===e):null;n?(this._activeDraftId=n.id,this._path=n.points.map(e=>[...e]),this._draftSegmentCms=this._adoptDraftCms(this._path,n.segments.map(e=>e.cm),n.id),this._resumeDraftBySpace[this._space]=n.id):(this._activeDraftId=null,this._path=t?[t]:[],this._draftSegmentCms=[],e&&this._resumeDraftBySpace[this._space]===e&&delete this._resumeDraftBySpace[this._space]),this._clearPlanSnapHover(),this._showToast(this._t("history.undone",{name:i.name}))}static async getConfigElement(){return await import("./editor-CcxmUFV-.js"),document.createElement("houseplan-card-editor")}static getStubConfig(){return{type:"custom:houseplan-card"}}static _warmBootReset(e){for(const e of Z_.values())for(const t of e)clearTimeout(t.evict);Z_.clear(),ef=e&&e>0?e:1e4}static _warmBootStats(){let e=0,t=0;const i=[];for(const n of Z_.values())for(const r of n)e++,r.dlg&&(t++,i.push(r.dlg.kind));return{keys:Z_.size,slots:e,dlgs:t,drafts:i}}setConfig(e){const t=this._config,i=!!t&&Object.prototype.hasOwnProperty.call(t,"floor");this._config={icon_size:2.5,show_temperature:!0,live_states:!0,show_signal:!0,...e};(i!==this._hasFixedFloor||t?.floor!==this._config.floor)&&(this._hashApplied=!1,this._navApplied=!1,this._warmVpArmed=!1),this._config.kiosk&&(this._booting=!1,this._bootFading=!1),!this._hasFixedFloor&&e.default_floor&&this._commitSpace(e.default_floor,!0);try{this._zoomBySpace=JSON.parse(localStorage.getItem(rf)||"{}")||{}}catch{this._zoomBySpace={}}try{const e=JSON.parse(localStorage.getItem(af)||"{}")||{};this._viewPreference=Object.fromEntries(Object.entries(e).filter(e=>"flat"===e[1]||"iso"===e[1]))}catch{this._viewPreference={}}this._labs=f_(V_);try{const e=JSON.parse(localStorage.getItem(sf)||"null");this._kioskScale={icon:yn(e?.icon),font:yn(e?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(nf)||"null");e&&e.config&&Array.isArray(e.config.spaces)&&(this._serverCfg=e.config,this._seedDecorStyle(this._serverCfg),this._cfgEpoch++,this._cfgRev=e.rev||0,this._cfgContentFingerprint=e.config_fingerprint||Rl(e.config),this._layout=e.layout||{},this._layoutRev=e.layout_rev||0,this._layoutContentFingerprint=e.layout_fingerprint||Rl(this._layout),this._virtualLights=Dc(e.virtual_lights,this._cfgRev),this._serverStorage=!0)}catch{}this._adoptInitialSpace(this._model,this._loadOk),"view"!==this._mode||this._view||(this._zoom=this._zoomBySpace[this._space]||1),this.isConnected&&(this._syncCycleTimer(),this._warmAdopt(),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1)}_warmAdopt(){if(this._config?.kiosk)return;const e=Q_(this._config);if(this._warmKey===e&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const t=this.parentNode,i=this._warmIdx(t),n=Z_.get(e);if(!n||!n.length)return;const r=n.find(e=>e.owner===this._warmGen);if(r)return this._warmLongReturn=!!r.freed&&Date.now()-r.freed>=wl,clearTimeout(r.evict),r.evict=0,r.freed=0,r.live=!0,this._warmSlot=r,this._warmKey=e,r.frameFingerprint&&this._continuity.adoptCompleteFrame(r.frameFingerprint),void(!this._devices.length&&r.devices?.length&&(this._devices=[...r.devices]));const{slot:o,sure:s}=((e,t,i,n)=>{const r=e=>{const t=!!i&&e.place?.deref()===i;return t&&e.idx===n?4:e.live?0:t?3:2};let o=null,s=0,a=0,l=null;for(const i of e){if(i.owner===t)continue;l=i;const e=r(i);e<=0||(e>s?(o=i,s=e,a=1):e===s&&a++)}return!o||a>1?{slot:o||l,sure:!1}:{slot:o,sure:!0}})(n,this._warmGen,t,i);o&&(this._warmLongReturn=!!o.freed&&Date.now()-o.freed>=wl,this._booting=!1,this._bootFading=!1,this._hdrH=o.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},K_)),this._warmKey=e,s?(clearTimeout(o.evict),o.evict=0,o.owner=this._warmGen,o.place=t?new WeakRef(t):null,o.idx=i,o.live=!0,this._warmSlot=o,this._warmVp=o.vp,o.frameFingerprint&&this._continuity.adoptCompleteFrame(o.frameFingerprint),!this._devices.length&&o.devices?.length&&(this._devices=[...o.devices]),this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,path:location.pathname,place:t?new WeakRef(t):null,idx:i,live:!0,hdrH:o.hdrH,stageH:o.stageH,vp:null,frameFingerprint:"",devices:null,dlg:null,freed:0,evict:0},n.push(this._warmSlot),this._warmTrim(n)))}_warmIdx(e){const t=e?.children;if(!t)return-1;for(let e=0;e4;){const t=e.findIndex(e=>!e.live);if(t<0)break;clearTimeout(e[t].evict),e.splice(t,1)}}_warmScheduleEvict(e,t){if(clearTimeout(e.evict),!(e.dlg||e.vp&&"view"!==e.vp.mode))return;const i=e.freed,n=e.owner;e.evict=window.setTimeout(()=>{if(e.evict=0,e.freed!==i||e.owner!==n)return;var r;e.dlg=null,e.vp=(r=e.vp)&&"view"!==r.mode?{...r,mode:"view",zoom:r.snap?.space===r.space?r.snap.zoom:r.zoom,view:null,snap:null,tool:"draw",decorTool:"select",showHidden:!1,selId:null,rszSel:null,decorSel:null}:r,e.frameFingerprint="";const o=Z_.get(t);if(!e.live&&o&&o.length>1){const t=o.indexOf(e);t>=0&&o.splice(t,1)}},ef+250)}_warmAdoptViewport(e){const t=this._warmVp;if(!t||this._warmSlot?.path!==location.pathname)return void(this._warmVp=null);const i=this._fixedFloorState();if(this._hashApplied||!this._model.find(e=>e.id===t.space)||"valid"===i.kind&&i.id!==t.space||this._hasFixedFloor&&"valid"!==i.kind)return void(this._warmVp=null);this._commitSpace(t.space,!0),this._navApplied=!0;const n="view"!==t.mode&&this._canEdit&&!e.kiosk?t.mode:"view";this._adoptMode("view"),this._pendingNavMode="view"===t.mode||this._canEdit||e.kiosk?null:t.mode,this._zoom=t.zoom;const r=this._effectiveProjection(),o=r===t.projection&&this._labsIso===t.activeLabsIso;if(this._view=o&&t.view?{...t.view}:null,this._viewModeSnap=o&&t.snap?{...t.snap}:null,!o&&t.logicalCenter){const e="iso"===r?y_([t.logicalCenter.x,t.logicalCenter.y],0):[t.logicalCenter.x,t.logicalCenter.y];this._applyView(t.zoom,e[0],e[1])}var s;this._tool="opening"===(s=function(e){return"partition"===e?"draw":e}(s=t.tool))?"draw":"string"==typeof s&&df.has(s)?s:"draw",this._decorTool=t.decorTool,this._showHidden=t.showHidden,this._showFar!==t.showFar&&(this._showFar=t.showFar,this._frame=null),this._selId=t.selId,this._resize?.restoreSelection(t.rszSel),this._decorSel=t.decorSel,this._warmVpArmed=!0,"view"!==n&&this._requestMode(n,!1,!0)}_warmPatch(e,t=!1){if(this._config?.kiosk)return;const i=Q_(this._config);if(!this._warmSlot||this._warmKey===i){if(!this._warmSlot){if(!t)return;const e=this.parentNode;this._warmKey=i,this._warmSlot={owner:this._warmGen,path:location.pathname,place:e?new WeakRef(e):null,idx:this._warmIdx(e),live:!0,hdrH:this._hdrH,stageH:0,vp:null,frameFingerprint:"",devices:null,dlg:null,freed:0,evict:0};const n=Z_.get(i)||[];for(n.push(this._warmSlot),Z_.set(i,n),this._warmTrim(n);Z_.size>8;){const e=Z_.keys().next().value;if(void 0===e||e===i)break;for(const t of Z_.get(e)||[])clearTimeout(t.evict);Z_.delete(e)}}Object.assign(this._warmSlot,e)}}_warmViewportState(){const e=this._effectiveProjection();return{space:this._space,mode:this._mode,projection:e,activeLabsIso:this._labsIso,logicalCenter:this._logicalViewCenter(e),zoom:this._zoom,view:this._view?{...this._view}:null,snap:this._viewModeSnap?{...this._viewModeSnap}:null,tool:this._tool,decorTool:this._decorTool,showHidden:this._showHidden,showFar:this._showFar,selId:this._selId,rszSel:this._resize?.selectedRoomId,decorSel:this._decorSel}}_warmDialogState(){const e=(e,t)=>({kind:e,space:this._space,mode:this._mode,data:t});return this._tapConfirm||this._alignDialog||this._mergeDialog||this._importDialog||this._backupExportDialog||this._backupImportDialog?null:this._openingInfo?e("openingInfo",this._openingInfo.id):this._infoCard?e("info",this._infoCard.id):this._rulesDialog?this._rulesDialog.busy?null:e("rules",this._rulesDialog):this._settingsDialog?this._settingsDialog.busy?null:e("settings",this._settingsDialog):this._markerDialog?this._markerDialog.busy?null:e("marker",this._markerDialog):this._openingDialog?e("opening",this._openingDialog):this._backdropDialog?e("backdrop",this._backdropDialog):this._decorShapeDialog?e("decorShape",this._decorShapeDialog):this._decorTextDialog?e("decorText",this._decorTextDialog):this._roomDialog?e("room",{editId:this._roomEditId,fill:this._roomFill,customFill:this._roomCustomFill,tempSrc:this._roomTempSrc,humSrc:this._roomHumSrc,srcOpen:this._roomSrcOpen,srcFilter:this._roomSrcFilter,nameScale:this._roomNameScale,labelScale:this._roomLabelScale,areaSel:this._areaSel,nameSel:this._nameSel,pendingSplit:this._pendingSplit,wallFaceBatch:this._wallFaceBatch,path:this._path}):this._spaceDialog?this._spaceDialog.busy?null:e("space",this._spaceDialog):null}_warmSnapshot(){if(this._booting||this._config?.kiosk||"steady"!==this._continuity.state)return;const e={vp:this._warmViewportState(),frameFingerprint:this._continuity.frameFingerprint,devices:this._devices};if(this._warmRevivePending||(e.dlg=this._warmDialogState()),this.isConnected&&this._warmSlot?.owner===this._warmGen){const t=this.parentNode;e.place=t?new WeakRef(t):null,e.idx=this._warmIdx(t)}this._warmPatch(e)}_warmReviveDialog(){const e=this._warmSlot;if(this._warmReviveTimer=void 0,!e||!e.dlg)return void(this._warmRevivePending=!1);const t=e.dlg,i=e.freed;if(t.mode===this._mode||"view"===t.mode||this._warmVp?.mode!==t.mode||this._editorRuntime){if(this._warmRevivePending=!1,e.dlg=null,e.freed=0,clearTimeout(e.evict),e.evict=0,i&&!(Date.now()-i>ef)&&t.space===this._space&&t.mode===this._mode){switch(t.kind){case"space":this._spaceDialog={...t.data,busy:!1,savedBusy:!1};break;case"marker":this._markerDialog={...t.data,busy:!1};break;case"settings":this._settingsDialog={...t.data,busy:!1};break;case"rules":this._rulesDialog={...t.data,busy:!1};break;case"opening":this._openingDialog={...t.data};break;case"backdrop":this._backdropDialog={...t.data};break;case"decorShape":this._decorShapeDialog={...t.data};break;case"decorText":{this._decorTextDialog={...t.data};const e=String(this._decorTextDialog?.text??"").length;this._decorTextSelection={start:e,end:e};break}case"room":{const e=t.data;this._roomEditId=e.editId,this._roomFill=e.fill,this._roomCustomFill=e.customFill||null,this._roomTempSrc=e.tempSrc,this._roomHumSrc=e.humSrc,this._roomSrcOpen=e.srcOpen,this._roomSrcFilter=e.srcFilter,this._roomNameScale=e.nameScale,this._roomLabelScale=e.labelScale,this._areaSel=e.areaSel,this._nameSel=e.nameSel,this._pendingSplit=e.pendingSplit,this._wallFaceBatch=e.wallFaceBatch||null,this._path=e.path,this._wallFaceBatch&&(this._activeDraftId=this._wallFaceBatch.activeDraftId,this._draftSegmentCms=[...this._wallFaceBatch.activeCms]),this._roomDialog=!0;break}case"info":{const e=this._devices.find(e=>e.id===t.data);e&&(this._infoCard=e);break}case"openingInfo":{const e=(this._curSpaceCfg?.openings||[]).find(e=>e.id===t.data);e&&(this._openingInfo=e);break}}this.requestUpdate()}}else this._warmRevivePending=!0}_cacheSnapshot(){if(this._serverCfg)try{this._cfgContentFingerprint=Rl(this._serverCfg),this._layoutContentFingerprint=Rl(this._layout),this._virtualLights=function(e,t,i){if(e.configRev===i)return e;const n=new Set((Array.isArray(t?.markers)?t.markers:[]).filter(Oc).map(e=>e.id));return{...e,configRev:i,off:new Set([...e.off].filter(e=>n.has(e)))}}(this._virtualLights,this._serverCfg,this._cfgRev),localStorage.setItem(nf,JSON.stringify({config:this._serverCfg,rev:this._cfgRev,config_fingerprint:this._cfgContentFingerprint,layout:this._layout,layout_rev:this._layoutRev,layout_fingerprint:this._layoutContentFingerprint,virtual_lights:Ac(this._virtualLights)}))}catch{}}_beginContinuityCandidate(e,t,i="plan"){return this._booting&&!this._continuity.hasCompleteFrame?this._continuity.token:(this._continuityDataReady=t,this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._resumeSettling=!0,this._continuity.beginCandidate(e,i))}_continuityStageValid(){const e=this._stageEl;return!!e&&e.clientWidth>0&&e.clientHeight>0}_continuityAssetsReady(){if(!this._model.length)return!1;const e=this._model.length?this._spaceModel():null;return!e?.bg?.href||this._signer.isReady(this.hass,e.bg.href)}_initialSpaceSelection(e,t=this._loadOk){const i=this._fixedFloorState(e,t);return"valid"===i.kind?{id:i.id,source:"fixed"}:this._hasFixedFloor?{id:null,source:"none"}:function(e){const t=new Set(e.spaceIds.filter(e=>!!e));if(!t.size)return{id:null,source:"none"};const i=[["hash",!1===e.acceptHash?null:e.hashSpace],["current",e.preserveCurrent?e.currentSpace:null],["saved",e.savedSpace],["default",e.defaultSpace],["first",e.spaceIds[0]]];for(const[e,n]of i)if(n&&t.has(n))return{id:n,source:e};return{id:null,source:"none"}}({spaceIds:e.map(e=>e.id),hashSpace:this._hashSpace(),acceptHash:!this._hashApplied,currentSpace:this._space,preserveCurrent:this._hashApplied||this._navApplied||this._warmVpArmed,savedSpace:this._savedNav()?.space,defaultSpace:this._config?.default_floor})}_adoptInitialSpace(e,t=this._loadOk){const i=this._initialSpaceSelection(e,t);return i.id?(this._commitSpace(i.id,!0),"hash"===i.source&&(this._hashApplied=!0),"saved"===i.source&&(this._navApplied=!0),i):i}_candidateBackdrop(e,t=this._space){const i=$a(e),n=this._fixedFloorState(i,!0);if(this._hasFixedFloor&&"valid"!==n.kind)return"";const r=this._initialSpaceSelection(i,!0).id||(i.some(e=>e.id===t)?t:i[0]?.id);return i.find(e=>e.id===r)?.bg?.href||""}_visualFrameFingerprint(){const e=this._stageEl,t=e?[e.clientWidth,e.clientHeight]:[0,0];return Tl([this._cfgRev,this._cfgContentFingerprint||Rl(this._serverCfg),this._layoutRev,this._layoutContentFingerprint||Rl(this._layout),this._space,this._mode,this._view,t,this._glowScreenBlend?"screen":"normal",this.hass?.themes?.darkMode??this.hass?.themes?.default_theme??""])}_settleContinuityFrame(){if(this._booting||!this._continuityStageValid())return;if(!this._continuity.hasCompleteFrame&&"steady"===this._continuity.state)return void(this._continuityAssetsReady()&&(this._renderSnapshotAt=Date.now(),this._continuity.markCompleteFrame(this._visualFrameFingerprint())));if(!this._continuityDataReady)return;if(!["holding","offline-stale","overlay-pending","overlay-visible","candidate-ready"].includes(this._continuity.state))return;const e=this._continuity.token;if(this._candidateDeviceSnapshot&&this._candidateDeviceSnapshot!==this._visibleDeviceSnapshot&&this._stagedDeviceSnapshotToken!==e)return this._stagedDeviceSnapshotToken=e,void this.requestUpdate();this._continuityPaintToken!==e&&(this._continuityPaintToken=e,this._continuity.candidateReady(e)&&this._continuity.commitAfterPaint(e,{updateComplete:()=>this.updateComplete,stageValid:()=>this.isConnected&&this._continuityStageValid(),assetsReady:()=>this._continuityAssetsReady(),frameFingerprint:()=>this._visualFrameFingerprint()}).then(t=>{t&&e===this._continuity.token?(this._resumeSettling=!1,this._renderSnapshotAt=Date.now(),this._candidateDeviceSnapshot&&(this._visibleDeviceSnapshot=this._candidateDeviceSnapshot),this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._warmSnapshot()):e===this._continuity.token&&(this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._candidateDeviceSnapshot=null,this.requestUpdate())}))}_onBackdropLoaded(e,t){this._signer.markLoaded(this.hass,e,t),this._continuity.note("asset-ready"),this._continuityPaintToken=-1,"steady"!==this._continuity.state&&this.requestUpdate()}_renderRecoveryOverlay(){if(!this._continuity.overlayVisible&&"recovery-error"!==this._continuity.state)return V;const e="connection"===this._continuity.recoveryReason;return B`
{}).then(t)}class Lm{constructor(e=50){this._undo=[],this._redo=[],this._limit=Math.max(30,Math.floor(e))}get canUndo(){return this._undo.length>0}get canRedo(){return this._redo.length>0}get undoName(){return this._undo[this._undo.length-1]?.name??null}get redoName(){return this._redo[this._redo.length-1]?.name??null}get size(){return this._undo.length}push(e){this._undo.push(e),this._undo.length>this._limit&&this._undo.splice(0,this._undo.length-this._limit),this._redo=[]}undo(){const e=this._undo.pop()??null;return e&&this._redo.push(e),e}redo(){const e=this._redo.pop()??null;return e&&this._undo.push(e),e}clear(){this._undo=[],this._redo=[]}}const Wm={now:()=>performance.now(),requestFrame:e=>requestAnimationFrame(e),cancelFrame:e=>cancelAnimationFrame(e)},qm=e=>Math.max(0,Math.min(1,e)),Bm=(e,t,i)=>e+(t-e)*i,jm=(e,t,i)=>{const n=1-e;return 3*n*n*e*t+3*n*e*e*i+e*e*e};function Um(e,t){return{centerX:e.x+e.w/2,centerY:e.y+e.h/2,pixelsPerUnit:t>0&&e.w>0?t/e.w:1,viewBox:{...e}}}function Vm(e,t,i){const n=Math.max(1e-9,e.pixelsPerUnit),r=Math.max(1e-9,t/n),o=Math.max(1e-9,i/n);return{x:e.centerX-r/2,y:e.centerY-o/2,w:r,h:o}}function Gm(e){const t=e.trim(),i=/^rgba?\(\s*([\d.]+)[, ]+([\d.]+)[, ]+([\d.]+)(?:\s*[,/]\s*([\d.]+))?\s*\)$/i.exec(t);if(i)return[Number(i[1]),Number(i[2]),Number(i[3]),null==i[4]?1:Number(i[4])];const n=/^#([0-9a-f]{6}|[0-9a-f]{8})$/i.exec(t)?.[1];return n?[parseInt(n.slice(0,2),16),parseInt(n.slice(2,4),16),parseInt(n.slice(4,6),16),8===n.length?parseInt(n.slice(6,8),16)/255:1]:null}function Km(e,t,i){const n=Gm(e),r=Gm(t);if(!n||!r)return i<.5?e:t;const o=qm(i);return`rgba(${Math.round(Bm(n[0],r[0],o))}, ${Math.round(Bm(n[1],r[1],o))}, ${Math.round(Bm(n[2],r[2],o))}, ${Bm(n[3],r[3],o).toFixed(3)})`}function Ym(e,t,i){const n=(e=>{const t=qm(e);if(0===t||1===t)return t;let i=0,n=1,r=t;for(let e=0;e<12;e++)r=(i+n)/2,jm(r,.2,.2){const t=this._state;if(!t||t.token!==r)return;const i=qm((e-t.startedAt)/t.duration);t.presented=i>=1?t.to:Ym(t.from,t.to,i),t.phase=i>=1?"settling":"running",this._hooks.frame(t),i<1?this._raf=this._clock.requestFrame(s):(this._raf=0,this._hooks.settled(t),this._state?.token===r&&(this._state=null))};return this._raf=this._clock.requestFrame(s),r}cancel(e=!0){this._raf&&this._clock.cancelFrame(this._raf),this._raf=0;const t=this._state;this._state=null,this._token++,t&&e&&(t.phase="settling",t.presented=t.to,this._hooks.frame(t),this._hooks.settled(t))}dispose(){this.cancel(!1)}}function Zm(e,t){return`${e("editor.load_failed")} ${e(t.terminal?"editor.refresh_advice":"editor.retry_advice")}`}class Jm extends Error{}class Qm{constructor(e){this.options=e,this._state="idle",this._inFlight=null}get state(){return this._state}ensure(){return"ready"===this._state?Promise.resolve(!0):"failed"===this._state?Promise.resolve(!1):(this._inFlight||(this._setState("loading"),this._inFlight=this._loadWithRetry().finally(()=>{this._inFlight=null})),this._inFlight)}async _loadWithRetry(){let e=new Error("Editor runtime did not load"),t=!1;for(const i of[0,1])try{const e=await this.options.load(i);if(e.fingerprint!==this.options.expectedFingerprint)throw new Jm(`Editor runtime fingerprint mismatch: expected ${this.options.expectedFingerprint}, got ${e.fingerprint}`);const t=e.create();return this.options.install(t),this._setState("ready"),!0}catch(i){e=i,i instanceof Jm&&(t=!0)}return this._setState(t?"failed":"idle"),this.options.failed?.(e,{terminal:t}),!1}_setState(e){e!==this._state&&(this._state=e,this.options.stateChanged?.(e))}}const e_="houseplan_card_labs_v1",t_=Object.freeze([Object.freeze({id:"iso",issue:89,since:"1.62.0",expires:"1.65.0",summary:"Volumetric plan renderer"})]);function i_(e){const t=/^(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?$/.exec(String(e||"").trim());if(!t)return null;const i=t.slice(1).map(Number);return i.every(e=>Number.isSafeInteger(e))?i:null}function n_(e,t){for(let i=0;i<3;i++)if(e[i]!==t[i])return e[i]e.split(",")).map(e=>e.trim()).filter(Boolean)}function c_(e,t,i,n=t_){const r=function(e,t=t_){const i=i_(e),n=new Map;if(!i||!o_(t))return n;for(const e of t){const t=i_(e.since),r=i_(e.expires);n_(i,t)>=0&&n_(i,r)<0&&n.set(e.id,e)}return n}(i,n),o=new Set(o_(n)?n.map(e=>e.id):[]),s=new Set(function(e){if(!e)return[];try{const t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>"string"==typeof e):[]}catch{return[]}}(t).filter(e=>r.has(e)));let a=!1;const l=e=>{if("off"===e)return s.clear(),void(a=!0);const t=e.startsWith("-"),i=t?e.slice(1):e;o.has(i)&&(a=!0,t||!r.has(i)?s.delete(i):s.add(i))};for(const t of l_(new URLSearchParams(String(e.search||"").replace(/^\?/,""))))l(t);for(const t of l_(s_(e.hash)))l(t);const c=Object.freeze([...s].sort());return{active:c,space:a_(e.hash),persist:a?JSON.stringify(c):void 0,knownUrlOperation:a}}let h_={active:Object.freeze([]),space:""},d_="",u_=!1,p_="";const m_=new Set;function __(){if("undefined"==typeof window)return h_;const e=c_(window.location,function(){try{return window.localStorage.getItem(e_)}catch{return null}}(),d_);void 0!==e.persist&&function(e){try{window.localStorage.setItem(e_,e)}catch{}}(e.persist),h_={active:e.active,space:e.space},window.__hpLabs=e.active;for(const e of m_)e(h_);return h_}function f_(){__()}function g_(e){if(d_=e,"undefined"==typeof window)return h_;if(!u_){u_=!0,window.__hpLabsListenerCleanup?.(),window.addEventListener("hashchange",f_),window.addEventListener("popstate",f_);const e=()=>{window.removeEventListener("hashchange",f_),window.removeEventListener("popstate",f_),window.__hpLabsListenerCleanup===e&&delete window.__hpLabsListenerCleanup};window.__hpLabsListenerCleanup=e}return __()}const v_=Object.freeze({rotDeg:0,tiltDeg:20,xyScale:1,zScale:1,origin:Object.freeze([500,500])});function y_(e){return[e.rotDeg,e.tiltDeg,e.xyScale,e.zScale,e.origin[0],e.origin[1]].every(Number.isFinite)&&Math.abs(e.xyScale)>1e-12&&Math.abs(Math.cos(e.tiltDeg*Math.PI/180))>1e-12}function b_(e,t,i=v_){if(!(y_(i)&&Number.isFinite(e[0])&&Number.isFinite(e[1])&&Number.isFinite(t)))throw new Error("invalid isometric projection input");const n=i.rotDeg*Math.PI/180,r=i.tiltDeg*Math.PI/180,o=e[0]-i.origin[0],s=e[1]-i.origin[1],a=(o*Math.cos(n)-s*Math.sin(n))*i.xyScale,l=(o*Math.sin(n)+s*Math.cos(n))*i.xyScale;return[i.origin[0]+a,i.origin[1]+l*Math.cos(r)-t*i.zScale*Math.sin(r)]}function w_(e,t=v_){if(!y_(t)||!Number.isFinite(e[0])||!Number.isFinite(e[1]))throw new Error("invalid isometric projection input");const i=t.rotDeg*Math.PI/180,n=t.tiltDeg*Math.PI/180,r=(e[0]-t.origin[0])/t.xyScale,o=(e[1]-t.origin[1])/(t.xyScale*Math.cos(n));return[t.origin[0]+r*Math.cos(i)+o*Math.sin(i),t.origin[1]-r*Math.sin(i)+o*Math.cos(i)]}function k_(e=v_){return`matrix(${function(e=v_){if(!y_(e))throw new Error("invalid isometric camera");const t=e.rotDeg*Math.PI/180,i=e.tiltDeg*Math.PI/180,n=e.xyScale*Math.cos(t),r=-e.xyScale*Math.sin(t),o=e.xyScale*Math.sin(t)*Math.cos(i),s=e.xyScale*Math.cos(t)*Math.cos(i);return[n,o,r,s,e.origin[0]-n*e.origin[0]-r*e.origin[1],e.origin[1]-o*e.origin[0]-s*e.origin[1]]}(e).map(e=>Number(e.toFixed(12))).join(" ")})`}function x_(e,t=v_){const{rect:i,wallHeight:n}=e,r=e.openingHeight??n,o=e.floorDepth??0;if(!(i.w>=0)||!(i.h>=0)||!Number.isFinite(n)||!Number.isFinite(r)||!Number.isFinite(o)||n<0||r<0||o<0)throw new Error("invalid isometric frame");const s=[[i.x,i.y],[i.x+i.w,i.y],[i.x+i.w,i.y+i.h],[i.x,i.y+i.h]],a=Math.max(n,r),l=s.flatMap(e=>[b_(e,-o,t),b_(e,a,t)]),c=l.map(e=>e[0]),h=l.map(e=>e[1]),d=Math.min(...c),u=Math.min(...h);return{x:d,y:u,w:Math.max(...c)-d,h:Math.max(...h)-u}}const $_=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function S_(e){let t=0;for(let i=0;iArray.isArray(e)&&Number.isFinite(e[0])&&Number.isFinite(e[1])).map(e=>[Number(e[0]),Number(e[1])]),i=t[t.length-1];return t.length>1&&t[0][0]===i[0]&&t[0][1]===i[1]&&t.pop(),t}(e);if(i.length<3)return[];const n=!t;return S_(i)>0===n?i:[...i].reverse()}function R_(e,t,i){return e.length?`M ${e.map(e=>$_(b_(e,i,t))).join(" L ")} Z`:""}function T_(e,t,i){const n=t[1]-e[1],r=-(t[0]-e[0]),o=i.rotDeg*Math.PI/180;return n*Math.sin(o)+r*Math.cos(o)}function C_(e,t,i=v_){if(!Number.isFinite(t)||t<0)throw new Error("invalid floor edge height");const n=(e||[]).map(e=>(e||[]).map((e,t)=>function(e,t){const i=M_(e,t);if(!i.length)return i;let n=0;for(let e=1;e0)).filter(e=>e.length>=3&&Math.abs(S_(e))>=1e-9)).filter(e=>e.length&&S_(e[0])>0);n.sort((e,t)=>{const i=e[0][0],n=t[0][0];return i[0]-n[0]||i[1]-n[1]||Math.abs(S_(t[0]))-Math.abs(S_(e[0]))});const r=[],o=[];let s=0;for(let e=0;ee.depth-t.depth||e.component-t.component||e.edge-t.edge),{footprintPath:r.join(" "),sides:o,componentCount:n.length,edgeCount:s}}function D_(e,t){return e^=Math.round(64*(Number.isFinite(t)?t:0)),Math.imul(e,16777619)>>>0}const z_=e=>e.every(Number.isFinite);function A_(e,t,i,n){const r=e[0]*i,o=e[1]*n,s=t*Math.PI/180;return[r*Math.cos(s)-o*Math.sin(s),r*Math.sin(s)+o*Math.cos(s)]}function P_(e,t,i,n,r,o,s){const a=e.flipH?-1:1,l="gate"===e.type?1:e.flipV?-1:1,c=(e.type,e.flipV,e.angle,e.face,{ox:0,oy:0}),h=function(e,t){return[e[0]+t[0],e[1]+t[1]]}([e.x+c.ox,e.y+c.oy],A_(i,e.angle,a,l));return{leaf:t,hinge:h,closedVector:A_(n,e.angle,a,l),quarterVector:A_([-n[1],n[0]],e.angle,a,l),turnDeg:r,bottom:o,top:s}}const O_=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function F_(e,t,i=v_){const n=Math.max(0,Math.min(1,Number.isFinite(t)?t:0));return e.leaves.map(t=>{const r=function(e,t){const i=e.turnDeg*t*Math.PI/180;return[e.hinge[0]+e.closedVector[0]*Math.cos(i)+e.quarterVector[0]*Math.sin(i),e.hinge[1]+e.closedVector[1]*Math.cos(i)+e.quarterVector[1]*Math.sin(i)]}(t,n),o=b_(t.hinge,t.bottom,i),s=b_(r,t.bottom,i),a=b_(r,t.top,i),l=b_(t.hinge,t.top,i);return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaf:t.leaf,d:`M ${O_(o)} L ${O_(s)} L ${O_(a)} L ${O_(l)} Z`,shadowD:`M ${O_(b_(t.hinge,0,i))} L ${O_(b_(r,0,i))}`,depth:Math.max(o[1],s[1])}})}const I_={window:120,door:90,passage:90,gate:300};function E_(e){return I_[e]}function H_(e,t){return{type:e,lengthCm:E_(e),flipH:!1,flipV:!1,revision:t}}function N_(e,t){const i=Math.max(0,e.renderedLength)/2,n=Math.max(0,e.target.physicalHalfWidth),r=n+.18*Math.max(0,t),o=e=>({x1:e,y1:-r,x2:e,y2:r});return{rect:{x:-i,y:n?-n:0,width:2*i,height:2*n},boundaries:[o(-i),o(i)]}}function L_(e,t){const i=[e[0],e[1]],n=[t[0],t[1]];return function(e,t){return e[0]-t[0]||e[1]-t[1]}(i,n)<=0?{a:i,b:n}:{a:n,b:i}}function W_(e){const t=new Map;return e.forEach((e,i)=>{if(!e.kind||e.open)return;const n=L_(e.a,e.b);if(Math.hypot(n.b[0]-n.a[0],n.b[1]-n.a[1])<=1e-9)return;const r=function(e,t){const i=e=>(Math.abs(e)<=5e-10?0:Math.round(1e6*e)/1e6).toFixed(6);return`${i(e[0])},${i(e[1])}>${i(t[0])},${i(t[1])}`}(n.a,n.b),o=t.get(r);if(o)return o.physicalHalfWidth=Math.max(o.physicalHalfWidth,e.half||0),o.sourceOrder=Math.min(o.sourceOrder,i),void(e.partitionHost&&(o.partitionHost&&o.partitionHost.id!==e.partitionHost.id?o.ambiguousPartitionHost=!0:o.partitionHost=e.partitionHost));t.set(r,{segmentKey:r,a:n.a,b:n.b,physicalHalfWidth:Math.max(0,e.half||0),sourceOrder:i,...e.partitionHost?{partitionHost:e.partitionHost}:{}})}),[...t.values()]}function q_(e,t){if(Math.abs(e.distance-t.distance)>1e-9)return e.distance-t.distance;if(Math.abs(e.perpendicular-t.perpendicular)>1e-9)return e.perpendicular-t.perpendicular;return(e.target.segmentKeyt.target.segmentKey?1:0)||e.target.sourceOrder-t.target.sourceOrder}function B_(e){const t=W_(e.intervals).map(t=>{const i=function(e,t){const i=t.b[0]-t.a[0],n=t.b[1]-t.a[1],r=Math.hypot(i,n),o=i/r,s=n/r,a=(e[0]-t.a[0])*o+(e[1]-t.a[1])*s,l=Math.max(0,Math.min(r,a)),c=t.a[0]+l*o,h=t.a[1]+l*s;return{along:l,length:r,x:c,y:h,distance:Math.hypot(e[0]-c,e[1]-h),perpendicular:Math.abs((e[0]-t.a[0])*s-(e[1]-t.a[1])*o)}}(e.pointer,t);return{target:t,...i,envelope:Math.max(e.baseTolerance,t.physicalHalfWidth+e.bodyPointerPadding)}}).filter(e=>e.distance<=e.envelope+1e-9).filter(t=>!function(e,t,i,n,r){const o=t.b[0]-t.a[0],s=t.b[1]-t.a[1],a=Math.hypot(o,s);if(!(a>1e-9))return!1;const l=o/a,c=s/a;for(const o of i){if(!o.open&&o.kind)continue;const i=L_(o.a,o.b),s=i.b[0]-i.a[0],a=i.b[1]-i.a[1],h=Math.hypot(s,a);if(!(h>1e-9))continue;const d=s/h,u=a/h;if(Math.abs(l*u-c*d)>1e-6)continue;if(Math.abs((i.a[0]-t.a[0])*c-(i.a[1]-t.a[1])*l)>r)continue;const p=(e[0]-i.a[0])*d+(e[1]-i.a[1])*u,m=1e-9;if(!(p<=m||p>=h-m)&&Math.abs((e[0]-i.a[0])*u-(e[1]-i.a[1])*d)<=n+1e-9)return!0}return!1}(e.pointer,t.target,e.intervals,t.envelope,Math.max(1e-9,Math.min(e.baseTolerance,.04*e.gridStep)))),i=t.filter(t=>!!t.target.partitionHost&&e.renderedLength+2*t.target.physicalHalfWidth>t.length+1e-9).sort(q_),n=t.filter(t=>!t.target.partitionHost||e.renderedLength+2*t.target.physicalHalfWidth<=t.length+1e-9).sort(q_);let r=n[0];if(!r)return{candidate:null,jambBlockedTarget:i[0]?.target||null};const o=n.filter(e=>Math.abs(e.distance-r.distance)<=1e-9&&Math.abs(e.perpendicular-r.perpendicular)<=1e-9),s=o.filter(e=>e.target.partitionHost);if(s.length){if(1!==new Set(s.map(e=>e.target.partitionHost.id)).size||s.some(e=>e.target.ambiguousPartitionHost))return{candidate:null,jambBlockedTarget:null};const e=s[0],t=e.target.b[0]-e.target.a[0],i=e.target.b[1]-e.target.a[1],n=Math.hypot(t,i),a=t/n,l=i/n;if(!o.every(t=>{const i=t.target.b[0]-t.target.a[0],n=t.target.b[1]-t.target.a[1],r=Math.hypot(i,n),o=i/r,s=n/r,c=Math.abs(a*s-l*o)<=1e-6,h=Math.abs((t.target.a[0]-e.target.a[0])*l-(t.target.a[1]-e.target.a[1])*a);return c&&h<=1e-9}))return{candidate:null,jambBlockedTarget:null};r=e}if(r.target.ambiguousPartitionHost)return{candidate:null,jambBlockedTarget:null};const{target:a,length:l}=r,c=a.b[0]-a.a[0],h=a.b[1]-a.a[1],d=c/l,u=h/l,p=a.partitionHost?a.physicalHalfWidth:0,m=Math.min(Math.max(0,e.renderedLength)/2+p,l/2);let _=r.along;const f=Math.max(e.gridStep,1e-9),g=l/2;_=Math.abs(_-g)<=f/2?g:Math.round(_/f)*f,_=Math.max(m,Math.min(l-m,_));const v=a.a[0]+d*_,y=a.a[1]+u*_,b=Math.max(0,e.renderedLength)/2,w=_-b,k=_+b,x=Math.max(0,w),$=Math.max(0,l-k),S=[a.a[0]+d*(w-x/2),a.a[1]+u*(w-x/2)],M=[a.a[0]+d*(k+$/2),a.a[1]+u*(k+$/2)];let R=180*Math.atan2(h,c)/Math.PI;R>=90?R-=180:R<-90&&(R+=180);const T=Math.abs(_-g)<=1e-9;return{candidate:{presetRevision:e.preset.revision,geometryRevision:e.geometryRevision,pointer:[e.pointer[0],e.pointer[1]],type:e.preset.type,lengthCm:e.preset.lengthCm,flipH:e.preset.flipH,flipV:e.preset.flipV,x:v,y:y,angle:R,renderedLength:e.renderedLength,target:a,...a.partitionHost?{host:{...a.partitionHost,t:l>0?_/l:0}}:{},measure:{labels:[{distance:x,midpoint:S},{distance:$,midpoint:M}],guide:T?{x:v,y:y,angle:R}:null}},jambBlockedTarget:null}}function j_(e,t,i,n,r=1e-6){return e.presetRevision===i&&e.geometryRevision===n&&Math.hypot(e.pointer[0]-t[0],e.pointer[1]-t[1])<=r}function U_(e){return!(!e.canEdit||e.kiosk||e.fixedFloor)&&("view"!==e.mode&&("mouse"===e.pointerType&&e.spaceCount>1))}let V_=0;const G_="1.69.0-beta.4",K_="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f",Y_=1500,X_=(e,t)=>{if(!e.has(t))return{hit:!1};const i=e.get(t);return e.delete(t),e.set(t,i),{hit:!0,value:i}},Z_=(e,t,i,n)=>{for(e.delete(t),e.set(t,i);e.size>n;){const t=e.keys().next().value;if(void 0===t)break;e.delete(t)}},J_=new Map;let Q_=0;const ef=e=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(e??{})}`;let tf=1e4;const nf="houseplan_card_layout_v1",rf="houseplan_card_cfg_v1",of="houseplan_card_zoom_v1",sf="houseplan_card_nav_v1",af="houseplan_card_kiosk_v1",lf="houseplan_card_view_v1",cf="hp-dialog, hp-help, hp-color-opacity, hp-device-preview",hf=1e3,df=(e,t)=>{const i=Math.min(e.x,t.x),n=Math.min(e.y,t.y);return{x:i,y:n,w:Math.max(e.x+e.w,t.x+t.w)-i,h:Math.max(e.y+e.h,t.y+t.h)-n}},uf=new Set(["select","draw","column","merge","split","resize","opening","wallthick","delroom"]),pf=(e,t,i)=>{const n=new Event(t,{bubbles:!0,composed:!0});n.detail=i??{},e.dispatchEvent(n)},mf=e=>{history.pushState(null,"",e),pf(window,"location-changed",{replace:!1})},_f=(e,t)=>{let i,n=null;const r=(...r)=>{clearTimeout(i),n=r,i=window.setTimeout(()=>{i=void 0;const t=n;n=null,t&&e(...t)},t)};return r.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const t=n;n=null,t&&e(...t)},r.cancel=()=>{clearTimeout(i),i=void 0,n=null},r.pending=()=>void 0!==i,r},ff=e=>{try{e.target?.setPointerCapture?.(e.pointerId)}catch{}};class gf extends he{constructor(){super(...arguments),this._editorRuntime=null,this._onboardingRuntime=null,this._editorRuntimeLoadingVisible=!1,this._backdropGuard=null,this._editorModeRequest=0,this._warmModeRequest=0,this._editorRuntimeLoader=new Qm({expectedFingerprint:K_,load:async e=>{const t=0===e?await import("./houseplan-editor-runtime-CM78ZHZU.js"):await import((()=>{const e=new URL("./houseplan-editor-runtime-CM78ZHZU.js",import.meta.url);return e.searchParams.set("hp_retry",`${G_}-${++V_}`),e.href})());return{fingerprint:t.EDITOR_RUNTIME_FINGERPRINT,create:()=>new t.HouseplanEditorRuntime(this)}},install:e=>{this._editorRuntime=e},stateChanged:e=>this._editorRuntimeStateChanged(e),failed:(e,t)=>{console.error("[houseplan] unable to load editor runtime",e),this._showToast(Zm(e=>this._t(e),t))}}),this._onboardingRuntimeLoader=new Qm({expectedFingerprint:K_,load:async e=>{const t=0===e?await import("./houseplan-onboarding-runtime-BP9ODa6H.js"):await import((()=>{const e=new URL("./houseplan-onboarding-runtime-BP9ODa6H.js",import.meta.url);return e.searchParams.set("hp_retry",`${G_}-${++V_}`),e.href})());return{fingerprint:t.ONBOARDING_RUNTIME_FINGERPRINT,create:()=>new t.HouseplanOnboardingRuntime(this)}},install:e=>{this._onboardingRuntime=e},failed:(e,t)=>{console.error("[houseplan] unable to load onboarding runtime",e),this._showToast(Zm(e=>this._t(e),t))}}),this._space="f1",this._layout={},this._serverStorage=!1,this._loadOk=!1,this._serverCanWrite=null,this._loading=!1,this._loadTries=0,this._serverCfg=null,this._cfgRev=0,this._cfgContentFingerprint="",this._unsubCfg=null,this._unsubLayout=null,this._unsubVirtual=null,this._liveSyncAttempt=null,this._liveSyncGeneration=0,this._liveSyncConnection=null,this._layoutRev=0,this._layoutContentFingerprint="",this._virtualLights=Dc(null),this._canOptimizeUndo=!1,this._undoKind=null,this._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._hoverRoom=null,this._pointerModality=new Cm(this,()=>this._syncPointerHoverTargets()),this._devicePressAnimations=new Map,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...tr},this._decorStyleSeeded=!1,this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorEraseConfirm=null,this._decorTextDialog=null,this._decorShapeDialog=null,this._backdropDialog=null,this._decorTextSelection={start:0,end:0},this._furnPalette=null,this._furnCategory=null,this._furnPreviewInput=null,this._furnTouchPending=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._reducedMotion=!1,this._onMotionChange=e=>{this._reducedMotion=e.matches,this._cancelDevicePressFeedback(),e.matches&&this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:e.matches&&this._modeTransition.active&&this._cancelModeTransition(!0),this.requestUpdate()},this._editorChromeMode="plan",this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransitionRequest=0,this._modeTransitionTargetZoom=1,this._modeTransitionEditorCamera=null,this._modeTransition=new Xm({frame:e=>this._applyModeTransitionFrame(e),settled:e=>this._settleModeTransition(e)}),this._editorSecondaryCopy={groupActive:(e,t)=>this._t("editor.group_active",{group:e,item:t}),openGroup:e=>this._t("editor.open_group",{group:e}),disabledAction:(e,t)=>this._t("editor.disabled_action",{action:e,reason:t})},this._tool="draw",this._geometryHistory=new Lm(50),this._wallDialog=null,this._drawWallField=null,this._activeDraftId=null,this._resumeDraftBySpace={},this._physicalSel=null,this._physicalDialog=null,this._partitionDeleteDialog=null,this._roomDeleteDialog=null,this._physicalDrag=null,this._physicalRotate=null,this._physicalLastTap=null,this._physicalPickCycle=null,this._wallUnionCacheValue=null,this._wallUnionPool=new Map,this._isoGeometryCache=new Map,this._isoFallback=new Set,this._openingTunnelCache=null,this._openingWallIndexCache=new Map,this._openingPlacementIntervalsCache=null,this._openingDimensionContextCache=null,this._planSnapGeometryCache=null,this._planStructuralGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._physicalBodiesCache=null,this._lightPhysicalBodiesCache=null,this._cleanFloorCache=new Map,this._innerContourCache=new Map,this._glowRuntimeState=Bp(),this._glowRuntimeHost={window:()=>this.ownerDocument.defaultView||window,isConnected:()=>this.isConnected,requestUpdate:()=>this.requestUpdate(),reducedMotion:()=>this._reducedMotion},this._lightBarrierCache=null,this._lightBarrierPool=new Map,this._glowScreenBlend=!1,this._duplicateColumnId=null,this._duplicateColumnTimer=0,this._rszLimitViolation=null,this._path=[],this._cursorPt=null,this._planSnapHover=null,this._mergeSel=null,this._openingPreset=null,this._openingRebindId=null,this._openingPresetRevision=0,this._openingHoverCandidate=null,this._openingJambBlockCm=null,this._openingDialog=null,this._openingInfo=null,this._opDrag=null,this._opMeasure=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._wallFaceBatch=null,this._wallRepairDiagnostic=null,this._wallFaceGraphCache=[],this._areaSel="",this._nameSel="",this._roomDialog=!1,this._roomEditId=null,this._roomFill="",this._roomCustomFill=null,this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1,this._zoom=1,this._view=null,this._zoomBySpace={},this._viewModeSnap=null,this._pointers=new Map,this._panStart=null,this._panLock=null,this._pinchStart=null,this._suppressClick=!1,this._touchContacts=new Map,this._touchSequenceMultitouch=!1,this._touchClickBlockUntil=0,this._connectedPath="",this._routeDepartureHandled=!1,this._onLocationChanged=()=>{this._connectedPath&&location.pathname!==this._connectedPath?this._leaveCardRoute():location.pathname===this._connectedPath&&(this._routeDepartureHandled=!1)},this._touchGestureGuard={capture:!0,handleEvent:e=>this._guardTouchGesture(e)},this._hdrH=118,this._booting=!0,this._bootFading=!1,this._bootSettling=!1,this._bootSettleRaf=0,this._bootLastH=-1,this._bootStart=0,this._bootLastChange=0,this._bootSoft=!1,this._tapConfirm=null,this._onboardingShown=!1,this._rulesDialog=null,this._preflightClipboardFallback=null,this._haIntegrationVersion=null,this._alignDialog=null,this._settingsDialog=null,this._backupExportDialog=null,this._backupImportDialog=null,this._sunRaysCache=null,this._dayCycleTimer=0,this._dayCycleClockKey="",this._compassDrag=!1,this._importDialog=null,this._importQueue=[],this._importTotal=0,this._rulesCompiledSrc="",this._infoCard=null,this._nativeMoreInfoEntity=null,this._deviceInbox=null,this._deviceInboxReturn=null,this._deviceInboxMemo=null,this._markerDialog=null,this._spaceDialog=null,this._keyHandler=e=>this._onKey(e),this._warmVp=null,this._warmVpArmed=!1,this._warmLongReturn=!1,this._warmRevivePending=!1,this._warmGen=++Q_,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._labs={active:Object.freeze([]),space:""},this._viewPreference={},this._renderProjection="flat",this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._activityRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._continuity=this._newContinuityController(),this._continuityHistory=[],this._continuityEpoch=0,this._continuityDataReady=!0,this._continuityPaintToken=-1,this._continuityDisposed=!1,this._renderSnapshotAt=Date.now(),this._hassSequence=0,this._visibleDeviceSnapshot=null,this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._capturedSnapshotSequence=-1,this._capturedSnapshotDevices=null,this._capturedSnapshotLayout=null,this._capturedSnapshotActivity="",this._capturedSnapshotConfigEpoch=-1,this._capturedSnapshotVirtual="",this._lastValidStageSize=null,this._pendingRefitSize=null,this._refitRaf=0,this._pageVisibility=e=>{if(this._continuity.visibility(e),this._dayCycleVisibility(e),"hidden"===e.kind)return this._clearTransientHover(!0),this._cancelDevicePressFeedback(),void(this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:this._cancelModeTransition(!0));if(this._vacJumpOnce=!0,!e.long){const e=Date.now();let t=!1;for(const i of this._activityRt.values())!i.flashKind||(i.expiresAt||i.flashTs+$m)>e||(i.flashTs=0,i.flashKind=null,i.expiresAt=0,t=!0);return void(t&&this.requestUpdate())}Date.now()-this._renderSnapshotAt>1e3&&this._continuity.note("device-snapshot-stale"),this._continuityDataReady=!1,this._continuityPaintToken=-1,this._resumeSettling=!0,this._loading?this.requestUpdate():this._loadFromServer()},this._resumeSettling=!1,this._viewportInvalidAt=0,this._vacFit=null,this._vacAllCamerasFor=null,this._vacAllCameraCache=null,this._vacCalConfirm=null,this._kioskDots=!1,this._cyclePausedUntil=0,this._swipeStart=null,this._tabDrag=null,this._tabDragRelease=null,this._tabSuppressClick=!1,this._tabOrderWarned=!1,this._lastTap=0,this._onLabsSnapshot=e=>{const t=this._effectiveProjection();this._labs=e;const i=this._effectiveProjection();this._convertProjectionView(t,i),this.requestUpdate()},this._onHashChange=()=>{if(this._hasFixedFloor)return;const e=this._hashSpace();if(e&&this._model.find(t=>t.id===e)&&e!==this._space){if(this._wallFaceBatch&&this._roomDialogCancel(),"plan"===this._mode&&"draw"===this._tool&&!this._finishWallChain())return;this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._commitSpace(e),this._selId=null,this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),this.requestUpdate()}},this._drag=null,this._rlResize=null,this._holdFired=!1,this._retryContinuity=()=>{this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.retry(this._continuity.recoveryReason||"plan"),this._loading||this._loadFromServer()},this._cfgEpoch=0,this._modelCache=null,this._emptySpaceStateActive=!1,this._decorSnapCache=null,this._markerPreviewMemo=null,this._markerPreviewDevicesMemo=null,this._showHidden=!1,this._connHooked=null,this._connectionWasLost=!1,this._haRegistryConnection=null,this._haRegistryRev=-1,this._haBindingCacheKey="",this._planHassMemo=null,this._onHaRegistryUpdate=()=>{const e=Kc(this.hass);e.revision===this._haRegistryRev&&this._devices.length||(this._haRegistryRev=e.revision,this._planHassMemo=null,this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate())},this._onConnReady=()=>{if(this._loadTries=0,clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,function(e){const t=qc(e);t&&(Uc(e,t),Vc(e,t))}(this.hass),this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(this._onConnReady,400));!this._connectionWasLost&&this._continuity.hasCompleteFrame?this._beginContinuityCandidate("connection-ready",!1,"plan"):(this._continuityDataReady=!1,this._continuityPaintToken=-1),this._loading||this._loadFromServer()},this._onConnLost=()=>{this._booting&&!this._continuity.hasCompleteFrame||(this._connectionWasLost=!0,this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.connectionLost())},this._signer=new Dl(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=_f(()=>{if(this._serverStorage){const e=[...this._dirtyPos];this._dirtyPos.clear();for(const t of e){const e=Kr(this._layout[t]);e&&(this._layout={...this._layout,[t]:e},this._sentPos.set(t,e),this.hass.callWS({type:"houseplan/layout/update",device_id:t,pos:e}).then(e=>this._noteLayoutRev(e)).catch(e=>this._showToast(this._t("toast.pos_save_failed",{err:this._errText(e)}))).finally(()=>{this._sentPos.get(t)===e&&this._sentPos.delete(t)}))}this._cacheSnapshot()}else this._layout=Yr(Fr(this._layout)),localStorage.setItem(nf,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._pendingPhysicalWrites=new Map,this._undoGeometry=()=>this._editorRuntimeOrThrow()._undoGeometry(),this._redoGeometry=()=>this._editorRuntimeOrThrow()._redoGeometry(),this._saveConfigDebounced=_f(()=>{this._serverCfg&&this._writeConfig().catch(e=>{const t=!0===e?.physicalGeometryRolledBack;"geometry-unsafe"!==e?.code&&("wall_model_client_outdated"===e?.code?this._showToast(this._t("toast.wall_model_client_outdated")):"conflict"===e?.code?(this._showToast(this._t("toast.conflict")),t||(this._cancelPath(),this._reloadConfigOnly(!0))):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(e)})),t&&this._reloadRejectedPhysicalWrite())})},500),this._draftSegmentCms=[],this._closingWallCm=null,this._savePhysicalDialog=()=>this._editorRuntimeOrThrow()._savePhysicalDialog(),this._deletePhysicalSelection=()=>this._editorRuntimeOrThrow()._deletePhysicalSelection(),this._confirmPartitionDelete=()=>this._editorRuntimeOrThrow()._confirmPartitionDelete(),this._deleteDraftWhole=()=>this._editorRuntimeOrThrow()._deleteDraftWhole(),this._deleteDraftSegment=()=>this._editorRuntimeOrThrow()._deleteDraftSegment(),this._confirmRoomDelete=e=>this._editorRuntimeOrThrow()._confirmRoomDelete(e),this._rebindPartitionOpening=()=>this._editorRuntimeOrThrow()._rebindPartitionOpening(),this._keepClosedAsPartitions=()=>this._editorRuntimeOrThrow()._keepClosedAsPartitions(),this._toggleServerPlans=async()=>this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._toggleServerPlans():this._editorRuntimeOrThrow()._toggleServerPlans(),this._aspectJob=null,this._dayCycleTick=()=>{if(!this.isConnected||"hidden"===this.ownerDocument.visibilityState)return;const e=this._dayCycleState();if(!e)return this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),void(this._dayCycleClockKey="");const t=Nn(e);t!==this._dayCycleClockKey&&(this._dayCycleClockKey=t,this.requestUpdate())},this._sunShown=!1,this._sunOut=!1,this._sunOutTimer=0,this._openSettingsDialog=()=>{if(this._editorRuntime)return this._editorRuntime._openSettingsDialog();this._ensureEditorRuntime().then(e=>{e&&this._openSettingsDialog()})},this._reportedPreflightFingerprint=null,this._openAlignDialog=()=>this._editorRuntimeOrThrow()._openAlignDialog(),this._toggleOptimizeLivePositions=()=>this._editorRuntimeOrThrow()._toggleOptimizeLivePositions(),this._optimizeUndoBusy=!1,this._openBackupExport=()=>this._editorRuntimeOrThrow()._openBackupExport(),this._openRulesDialog=()=>this._editorRuntimeOrThrow()._openRulesDialog(),this._climateCache=null,this._gearPtCache=new WeakMap}_editorRuntimeOrThrow(){if(!this._editorRuntime)throw new Error("Houseplan editor runtime is not loaded");return this._editorRuntime}async _ensureEditorRuntime(){return this._editorRuntimeLoader.ensure()}async _ensureOnboardingRuntime(){return this._onboardingRuntimeLoader.ensure()}_editorRuntimeStateChanged(e){clearTimeout(this._editorRuntimeLoadingTimer),this._editorRuntimeLoadingTimer=void 0,"loading"!==e?(this._editorRuntimeLoadingVisible=!1,this.requestUpdate()):this._editorRuntimeLoadingTimer=window.setTimeout(()=>{this._editorRuntimeLoadingVisible=!0,this.requestUpdate()},150)}async _requestMode(e,t=!0,i=!1){const n=++this._editorModeRequest;if(i&&(this._warmModeRequest=n,this._refitRaf&&(cancelAnimationFrame(this._refitRaf),this._refitRaf=0),this._pendingRefitSize=null),"view"===e||await this._ensureEditorRuntime()){if(n===this._editorModeRequest&&this.isConnected)return i?(this._adoptMode(e),this._warmRevivePending&&(clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmReviveDialog()),this.requestUpdate(),void this.updateComplete.then(()=>requestAnimationFrame(()=>requestAnimationFrame(()=>{if(this._warmModeRequest!==n||n!==this._editorModeRequest)return;const e=this._stageEl;this._lastValidStageSize=e&&e.clientWidth>0&&e.clientHeight>0?[e.clientWidth,e.clientHeight]:null,this._pendingRefitSize=null,this._warmModeRequest=0})))):void this._setMode(e,t);this._warmModeRequest===n&&(this._warmModeRequest=0)}else this._warmModeRequest===n&&(this._warmModeRequest=0)}_seedDecorStyle(e){if(this._decorStyleSeeded||!e)return;this._decorStyleSeeded=!0;const t=e.settings?.decor_default_style;t&&(this._decorStyle=function(e,t){if(!e||"object"!=typeof e)return{...t};const i=e,n="string"==typeof i.color&&i.color?i.color:t.color,r="string"==typeof i.fill_color&&i.fill_color?i.fill_color:t.fillColor,o=Number(i.width_cm);return{color:n,opacity:ir(i.opacity,t.opacity),widthCm:Number.isFinite(o)&&o>0?Math.max(.1,Math.min(100,o)):t.widthCm,fill:"boolean"==typeof i.fill?i.fill:t.fill,fillColor:r,fillOpacity:ir(i.fill_opacity,t.fillOpacity)}}(t,tr))}get _canEdit(){return!!this._norm&&(!0===this._serverCanWrite||!1!==this._serverCanWrite&&!0===this.hass?.user?.is_admin)}get _kiosk(){return!!this._config?.kiosk}_showKioskDots(){this._kioskDots=!0,clearTimeout(this._kioskDotsTimer),this._kioskDotsTimer=window.setTimeout(()=>this._kioskDots=!1,2500)}get _modeTransitionBusy(){return this._modeTransitionPreparing||this._modeTransition.active}_cssColor(e,t){const i=String(e||"").trim();if(!i)return t;const n=document.createElement("span");n.style.cssText=`position:absolute;visibility:hidden;color:${i}`,this.renderRoot.append(n);const r=getComputedStyle(n).color||t;return n.remove(),r}_currentModeVisual(e=this._mode){const t=this._modeTransition.presented||this._modeTransitionVisual;if(t)return{...t,viewport:{...t.viewport,viewBox:{...t.viewport.viewBox}}};const i=this._stageEl;if(!i||i.clientWidth<=0||i.clientHeight<=0)return null;const n=this.renderRoot.querySelector(".editorchrome"),r=this._viewOr(this._baseVb()),o=this.renderRoot.querySelector(".hp-paper"),s=this.renderRoot.querySelector(".hp-backdrop"),a=this.renderRoot.querySelector(".zoomwrap"),l=a?getComputedStyle(a).filter:"",c=Number(/brightness\(([^)]+)\)/.exec(l)?.[1]);return{presentedMode:e,editorChromeHeight:"view"===e?0:n?.getBoundingClientRect().height||0,stageWidth:i.clientWidth,stageHeight:i.clientHeight,viewport:Um(r,i.clientWidth),stageColor:getComputedStyle(i).backgroundColor||"rgb(255, 255, 255)",paperColor:o?getComputedStyle(o).fill:"rgb(255, 255, 255)",sceneBrightness:Number.isFinite(c)?c:1,architectureOpacity:"decor"===e?.35:1,backdropOpacity:s&&Number(getComputedStyle(s).opacity)||1,viewWeight:"view"===e?1:0,editorWeight:"view"===e?0:1,toolbarContentOpacity:"view"===e?0:1}}_viewForModeTarget(e,t,i,n,r){const o=fi(this._baseVb(),n/Math.max(1,r)),s=Math.min(gf.ZOOM_MAX,Math.max(gf.ZOOM_MIN,e)),a=o.w/s,l=o.h/s,c=t??o.x+o.w/2,h=i??o.y+o.h/2;return this._clampView({x:c-a/2,y:h-l/2,w:a,h:l},o)}_targetStageColor(e){return"view"!==e?"rgb(255, 255, 255)":this._cssColor(this._stageBg(this._spaceDisplayForRender()),this._cssColor("var(--ha-card-background, var(--card-background-color, #111))","rgb(17, 17, 17)"))}_targetPaperColor(e){return"view"===e&&this._spaceModel()?.bg?this._cssColor("var(--ha-card-background, var(--card-background-color, #111))","rgb(17, 17, 17)"):"rgb(255, 255, 255)"}_targetBrightness(e){return 1}_applyModeTransitionFrame(e){e.targetMode===this._mode&&(this._modeTransitionVisual=e.presented,this._view={...e.presented.viewport.viewBox},this.requestUpdate())}_settleModeTransition(e){if(e.targetMode!==this._mode)return;const t=this._modeTransitionRequest;this._view={...e.to.viewport.viewBox},this._zoom=this._modeTransitionTargetZoom,this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._lastValidStageSize=[e.to.stageWidth,e.to.stageHeight],"view"===this._mode&&(this._saveZoom(),this._viewModeSnap=null,this._modeTransitionEditorCamera=null),this.requestUpdate(),this.updateComplete.then(()=>{if(!this.isConnected||t!==this._modeTransitionRequest||this._modeTransitionBusy||e.targetMode!==this._mode)return;const i=this._stageEl;if(i&&i.clientWidth>0&&i.clientHeight>0&&(Math.abs(i.clientWidth-e.to.stageWidth)>.5||Math.abs(i.clientHeight-e.to.stageHeight)>.5)){const e=this._view;this._lastValidStageSize=[i.clientWidth,i.clientHeight],this._applyView(this._zoom,e?e.x+e.w/2:void 0,e?e.y+e.h/2:void 0),this.requestUpdate()}const n=this.renderRoot.activeElement;(!n||!n.isConnected||!!n.closest?.(".editorchrome, .stage"))&&this.renderRoot.querySelector(".modetab.active")?.focus?.({preventScroll:!0})})}_cancelModeTransition(e=!0){const t=!!this._modeTransition.state;this._modeTransitionRequest++,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransition.cancel(e),e&&t||(this._modeTransitionVisual=null)}_adoptMode(e){this._cancelModeTransition(!1),this._mode=e,"view"!==e&&(this._editorChromeMode=e)}_commitViewModeAtomic(e,t,i,n){if(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._modeTransitionForceAtomic=!1,this._zoom=t,e){const r=this._stageEl?.clientWidth||e.stageWidth,o=Math.max(1,e.stageHeight+e.editorChromeHeight);this._view=this._viewForModeTarget(t,i,n,r,o),this._lastValidStageSize=[r,o]}else this._view=null;this._viewModeSnap=null,this._modeTransitionEditorCamera=null,this._saveZoom(),this.requestUpdate()}_prepareModeTransition(e,t,i,n,r,o){this.updateComplete.then(()=>{if(!this.isConnected||e!==this._modeTransitionRequest||this._mode!==i)return void(e===this._modeTransitionRequest&&(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._modeTransitionForceAtomic=!1,this.requestUpdate()));const s=this.renderRoot.querySelector(".editorchrome"),a=s?.querySelector(".editorchrome-inner"),l="view"===i?0:a?.scrollHeight||a?.getBoundingClientRect().height||0,c=Math.max(1,t.stageHeight+t.editorChromeHeight),h=Math.max(1,c-l),d=this._stageEl?.clientWidth||t.stageWidth;if(d<=0||h<=0)return this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._applyView(n,r,o),void this.requestUpdate();const u=this._viewForModeTarget(n,r,o,d,h),p={presentedMode:i,editorChromeHeight:l,stageWidth:d,stageHeight:h,viewport:Um(u,d),stageColor:this._targetStageColor(i),paperColor:this._targetPaperColor(i),sceneBrightness:this._targetBrightness(i),architectureOpacity:"decor"===i?.35:1,backdropOpacity:"decor"===i&&"backdrop"!==this._decorTool?.5:1,viewWeight:"view"===i?1:0,editorWeight:"view"===i?0:1,toolbarContentOpacity:"view"===i?0:1};this._modeTransitionPreparing=!1;const m=this._modeTransitionForceAtomic;this._modeTransitionForceAtomic=!1,this._modeTransition.start(t,p,i,this._reducedMotion||m?0:220)})}get _hasFixedFloor(){return!!this._config&&Object.prototype.hasOwnProperty.call(this._config,"floor")}_fixedFloorState(e=this._model,t=this._loadOk){const i=this._config?.floor,n=function(e){if(!e.hasFloor)return{kind:"absent"};const t=e.floor;if("string"==typeof t)return t.length?e.spaceIds.includes(t)?{kind:"valid",id:t,source:"id"}:{kind:"invalid",reason:"unknown-id",value:t}:{kind:"invalid",reason:"empty-id",value:t};if("number"==typeof t){if(!Number.isFinite(t))return{kind:"invalid",reason:"non-finite-index",value:t};if(!Number.isInteger(t))return{kind:"invalid",reason:"fractional-index",value:t};if(t<0)return{kind:"invalid",reason:"negative-index",value:t};const i=e.spaceIds[t];return void 0===i?{kind:"invalid",reason:"out-of-range-index",value:t}:{kind:"valid",id:i,source:"index"}}return{kind:"invalid",reason:"invalid-type",value:t}}({spaceIds:e.map(e=>e.id),hasFloor:this._hasFixedFloor,floor:i});return!this._hasFixedFloor||t?n:"number"==typeof i?"valid"===n.kind||"invalid"===n.kind&&"out-of-range-index"===n.reason?{kind:"pending",value:i}:n:"valid"===n.kind||"invalid"===n.kind&&"unknown-id"!==n.reason?n:{kind:"pending",value:i}}_canCommitSpace(e,t=!1){if(t||!this._hasFixedFloor)return!0;const i=this._fixedFloorState();return"valid"===i.kind&&i.id===e}_commitSpace(e,t=!1){return!!this._canCommitSpace(e,t)&&(e!==this._space&&(this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._editorRuntime?._clearFurniturePreview()),this._space=e,!0)}_slideTo(e,t){if(e===this._space)return!0;if(!this._canCommitSpace(e))return!1;if(this._wallFaceBatch&&this._roomDialogCancel(),"plan"===this._mode&&"draw"===this._tool&&!this._finishWallChain())return!1;this._cancelModeTransition(!0);const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;return this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._commitSpace(e),this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._selId=null,this._physicalSel=null,this._editorSecondary?.closeForNavigation(),this._physicalDialog=null,this._physicalDrag=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),i||(this._slide=t,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slideTimer=void 0,this._slide="",this.requestUpdate()},190),this.requestUpdate()),!0}get _canReorderTabs(){return U_({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:"mouse",spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})}_tabPointerDown(e,t){U_({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:e.pointerType,spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})&&(ff(e),this._tabDragRelease=e=>this._tabPointerUp(e),window.addEventListener("pointerup",this._tabDragRelease),window.addEventListener("pointercancel",this._tabDragRelease),this._tabDrag={id:t,pointerId:e.pointerId,x:e.clientX,y:e.clientY,moved:!1,targetId:null,placement:null})}_tabDropTargetAt(e,t,i){const n=this._model.map(e=>e.id),r=n.indexOf(i);if(r<0)return null;const o=this.renderRoot.querySelectorAll('[data-hp="space-tab"]');for(const s of o){const o=s.dataset.id||"";if(!o||o===i)continue;const a=s.getBoundingClientRect();if(ea.right||ta.bottom)continue;const l=n.indexOf(o);return l<0?null:{targetId:o,placement:l=4)))return;var i,n;const r=this._tabDropTargetAt(e.clientX,e.clientY,t.id);t.moved&&t.targetId===r?.targetId&&t.placement===r?.placement||(this._tabDrag={...t,moved:!0,targetId:r?.targetId||null,placement:r?.placement||null})}_tabPointerUp(e){const t=this._tabDrag;if(t&&t.pointerId!==e.pointerId)return;const i="pointerup"===e.type&&t?.moved?this._tabDropTargetAt(e.clientX,e.clientY,t.id):null;"pointerup"===e.type&&t?.moved&&this._suppressNextTabClick(),this._endTabDrag(),t?.moved&&i&&this._commitTabOrder(t.id,i.targetId)}_endTabDrag(){this._tabDrag=null,this._tabDragRelease&&(window.removeEventListener("pointerup",this._tabDragRelease),window.removeEventListener("pointercancel",this._tabDragRelease),this._tabDragRelease=null)}_tabClick(e){if(this._tabSuppressClick)return this._tabSuppressClick=!1,clearTimeout(this._tabSuppressClickTimer),void(this._tabSuppressClickTimer=void 0);this._pickSpace(e)}_suppressNextTabClick(){this._tabSuppressClick=!0,clearTimeout(this._tabSuppressClickTimer),this._tabSuppressClickTimer=window.setTimeout(()=>{this._tabSuppressClick=!1,this._tabSuppressClickTimer=void 0},0)}_commitTabOrder(e,t){const i=this._serverCfg;if(!i||!this._canReorderTabs)return;const n=this._model.map(e=>e.id),r=function(e,t,i){const n=e.indexOf(t),r=e.indexOf(i);if(n<0||r<0||n===r)return e;const o=e.slice();return o.splice(n,1),o.splice(r,0,t),o}(n,e,t);if(r===n)return;const o=new Map(this._devices.map(e=>[String(e.id),String(e.area||"")])),s=function(e,t,i,n=()=>""){if(!i)return[];const r=[];for(const o of e){if(!o||!0===o.removed)continue;const e="string"==typeof o.id?o.id:"";if(!e)continue;if("string"==typeof o.space&&o.space)continue;const s=("string"==typeof o.area?o.area:"")||n(e)||"";s&&t[s]||r.push({id:e,space:i})}return r}(i.markers||[],Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),n[0]||"",e=>o.get(e)||"");if(s.length){const e=new Map(s.map(e=>[e.id,e.space]));for(const t of i.markers||[]){const i=e.get(String(t.id));i&&(t.space=i)}}i.spaces=function(e,t){const i=new Map(t.map((e,t)=>[e,t]));return e.map((e,t)=>({space:e,index:t})).sort((e,t)=>(i.get(String(e.space?.id))??Number.MAX_SAFE_INTEGER)-(i.get(String(t.space?.id))??Number.MAX_SAFE_INTEGER)||e.index-t.index).map(e=>e.space)}(i.spaces||[],r),this._saveConfig(),this._tabOrderWarned||(this._tabOrderWarned=!0,this._showToast(this._t("toast.space_order_changed")))}_pickSpace(e){if(this._endTabDrag(),e===this._space)return;const t=this._model.map(e=>e.id),i=t.indexOf(this._space),n=t.indexOf(e);this._navApplied=!0,this._showFar=!1,this._frame=null,this._slideTo(e,i>=0&&n0&&Date.now()>=this._cyclePausedUntil&&this._model.length>1&&this._zoom<=1.001){const e=this._model.map(e=>e.id),t=e.indexOf(this._space);this._slideTo(e[(t+1)%e.length],"left"),this._showKioskDots()}}_syncCycleTimer(){clearInterval(this._cycleTimer),this._cycleTimer=void 0,this.isConnected&&!this._hasFixedFloor&&this._config?.kiosk&&Number(this._config.cycle)>0&&(this._cycleTimer=window.setInterval(()=>this._cycleTick(),1e3*Number(this._config.cycle)))}get _editing(){return"plan"===this._mode||"devices"===this._mode||"decor"===this._mode}get _markup(){return"plan"===this._mode}get _wallUnionCache(){return this._wallUnionCacheValue}set _wallUnionCache(e){this._wallUnionCacheValue=e,null===e&&this._wallUnionPool.clear()}get _glowClipCache(){return this._glowRuntimeState.clipCache}get _glowGeometryWarnings(){return this._glowRuntimeState.geometryWarnings}get _glowFeatherUnits(){return this._glowRuntimeState.featherUnits}set _glowFeatherUnits(e){this._glowRuntimeState.featherUnits=e}get _glowRenderedSources(){return this._glowRuntimeState.renderedSources}get _glowLastAppearance(){return this._glowRuntimeState.lastAppearance}get _glowEnteringSources(){return this._glowRuntimeState.enteringSources}get _glowEnterRafs(){return this._glowRuntimeState.enterRafs}get _glowFadeTimers(){return this._glowRuntimeState.fadeTimers}get _glowFeatherSuspendUntil(){return this._glowRuntimeState.featherSuspendUntil}set _glowFeatherSuspendUntil(e){this._glowRuntimeState.featherSuspendUntil=e}get _glowFeatherResumeTimer(){return this._glowRuntimeState.featherResumeTimer}set _glowFeatherResumeTimer(e){this._glowRuntimeState.featherResumeTimer=e}get _glowSourceSeq(){return this._glowRuntimeState.sourceSeq}set _glowSourceSeq(e){this._glowRuntimeState.sourceSeq=e}_newContinuityController(){return new Ml(()=>{this._resumeSettling="steady"!==this._continuity.state,this._continuityEpoch++,this.isConnected&&this.requestUpdate()})}get _labsIso(){return this._labs.active.includes("iso")}get _desiredProjection(){return"view"===this._mode&&this._labsIso&&"iso"===this._viewPreference[this._space]?"iso":"flat"}_saveViewPreference(){try{localStorage.setItem(lf,JSON.stringify(this._viewPreference))}catch{}}_logicalViewCenter(e){const t=this._view;if(!t)return null;const i=[t.x+t.w/2,t.y+t.h/2],n="iso"===e?w_(i):i;return{x:n[0],y:n[1]}}_convertProjectionView(e,t){if(e===t)return;const i=this._logicalViewCenter(e);this._view=null;const n=i?"iso"===t?b_([i.x,i.y],0):[i.x,i.y]:null;this._applyView(this._zoom,n?.[0],n?.[1]),this._warmPatch({vp:this._warmViewportState()}),this.requestUpdate()}_setProjection(e){if(!this._labsIso||"view"!==this._mode)return;const t=this._effectiveProjection();if(this._viewPreference={...this._viewPreference,[this._space]:e},"iso"===e){const e=this._isoSceneKey();e&&this._isoFallback.delete(e)}this._saveViewPreference();const i=this._effectiveProjection();this._convertProjectionView(t,i)}_hashSpace(){return a_(window.location.hash||"")}connectedCallback(){this._connectedPath=location.pathname,this._routeDepartureHandled=!1,window.addEventListener("location-changed",this._onLocationChanged),window.addEventListener("popstate",this._onLocationChanged),this._continuityDisposed&&(this._continuity=this._newContinuityController(),this._continuityDisposed=!1,this._continuityPaintToken=-1);const e=Om(this.ownerDocument);var t;void 0!==e&&(this._glowScreenBlend=e),this._continuityUnsub?.(),this._continuityUnsub=$l(this.ownerDocument,this._pageVisibility),this._languageFailureUnsub?.(),this._languageFailureUnsub=(t=()=>{this._showToast(this._t("toast.locale_load_failed"))},Gd.add(t),()=>Gd.delete(t)),super.connectedCallback(),this._pointerModality.connect(this.ownerDocument.defaultView);const i=this.ownerDocument.defaultView?.MutationObserver;i&&(this._pointerHoverObserver=new i(e=>{for(const t of e)for(const e of t.addedNodes)this._syncPointerHoverSubtree(e)}),this._pointerHoverObserver.observe(this.renderRoot,{childList:!0,subtree:!0})),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),Pm(this.ownerDocument).then(e=>{e!==this._glowScreenBlend&&(this._glowScreenBlend=e,this.isConnected&&this.requestUpdate())}),this.hass&&this._ensureHaRegistryAuthority(),window.addEventListener("keydown",this._keyHandler),this._signer.start(()=>this.hass,()=>mn(this._serverCfg)),this._syncCycleTimer(),window.addEventListener("hashchange",this._onHashChange),this._labsUnsub?.(),this._labsUnsub=function(e,t){m_.add(t);const i=g_(e);return"undefined"==typeof window&&t(i),()=>{m_.delete(t)}}(G_,this._onLabsSnapshot),this._booting?this._bootWatch():this._bootFading&&(clearTimeout(this._bootTimer),this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220)),this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Y_)),!this._loadOk&&this._serverCfg&&this.hass&&this._scheduleLoadRetry(),!this._warmSlot&&this._config&&this._warmAdopt(),this._loadOk&&this._ensureLiveSyncSubscriptions(),this._warmVp&&!this._warmRevivePending&&void 0===this._warmReviveTimer&&(this._warmRevivePending=!0,this._warmReviveTimer=window.setTimeout(()=>this._warmReviveDialog(),0)),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1,this.requestUpdate()}disconnectedCallback(){this._connectedPath&&location.pathname!==this._connectedPath&&this._leaveCardRoute(),window.removeEventListener("location-changed",this._onLocationChanged),window.removeEventListener("popstate",this._onLocationChanged),this._continuityUnsub?.(),this._continuityUnsub=void 0,this._languageFailureUnsub?.(),this._languageFailureUnsub=void 0,this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._refitRaf&&(cancelAnimationFrame(this._refitRaf),this._refitRaf=0),this._warmModeRequest=0,this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),this._dayCycleClockKey="",this._bootSettleRaf&&(cancelAnimationFrame(this._bootSettleRaf),this._bootSettleRaf=0),this._bootSettling=!1;for(const e of this._activityRt.values())clearTimeout(e.timer);window.removeEventListener("keydown",this._keyHandler),this._endTabDrag(),clearTimeout(this._tabSuppressClickTimer),this._tabSuppressClickTimer=void 0,this._tabSuppressClick=!1,clearInterval(this._cycleTimer),clearTimeout(this._kioskDotsTimer),clearTimeout(this._kioskHoldTimer),clearTimeout(this._reloadRetry),clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),this._connHooked=null,this._haRegistryRelease?.(),this._haRegistryRelease=void 0,this._haRegistryConnection=null,this._signer.dispose(),clearTimeout(this._toastTimer),clearTimeout(this._slideTimer),clearTimeout(this._editorRuntimeLoadingTimer),this._editorRuntimeLoadingTimer=void 0,this._editorRuntimeLoadingVisible=!1,this._modeTransition.dispose(),this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransitionRequest++,this._slideTimer=void 0,this._slide="",clearTimeout(this._bootTimer),this._bootTimer=void 0,clearTimeout(this._bootSoftTimer),this._saveConfigDebounced.flush(),window.removeEventListener("hashchange",this._onHashChange),this._labsUnsub?.(),this._labsUnsub=void 0,clearTimeout(this._holdTimer),this._roViewport?.disconnect(),this._roViewport=void 0,this._roHdr?.disconnect(),this._roHdr=void 0,this._onWinResize&&(window.removeEventListener("resize",this._onWinResize),this._onWinResize=void 0),this._unsubCfg&&(this._unsubCfg(),this._unsubCfg=null),this._unsubLayout&&(this._unsubLayout(),this._unsubLayout=null),this._unsubVirtual&&(this._unsubVirtual(),this._unsubVirtual=null),this._unsubTrail&&(this._unsubTrail(),this._unsubTrail=void 0),this._liveSyncGeneration++,this._liveSyncAttempt=null,this._liveSyncConnection=null,clearTimeout(this._layoutSyncTimer),clearTimeout(this._duplicateColumnTimer),im(this._glowRuntimeState,this._glowRuntimeHost),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._editorRuntime?._clearFurniturePreview(),this._editorRuntime?._furnShiftDetach(),this._touchContacts.clear(),this._touchSequenceMultitouch=!1,this._touchClickBlockUntil=0,this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._pointerHoverObserver?.disconnect(),this._pointerHoverObserver=void 0,this._pointerModality.disconnect(),this._editorSecondary?.reset(),this._resumeSettling=!1,this._continuityHistory=[...this._continuityHistory,...this._continuity.trace].slice(-80),this._continuity.dispose(),this._continuityDisposed=!0,super.disconnectedCallback()}_onKey(e){if("Escape"===e.key&&this._vacFit)return this._vacFit=null,this._showToast(this._t("vac.cal_cancelled")),void e.stopPropagation();if("Escape"===e.key){if(this._tapConfirm)return void(this._tapConfirm=null);if(this._vacCalConfirm)return void(this._vacCalConfirm=null);if(this._decorEraseConfirm)return void(this._decorEraseConfirm=null);if(this._openingInfo)return void(this._openingInfo=null);if(this._infoCard)return void this._closeInfoCard();if(this._rulesDialog)return void(this._rulesDialog=null);if(this._alignDialog)return this._alignDialog=null,void(this._preflightClipboardFallback=null);if(this._backupImportDialog)return void(this._backupImportDialog=null);if(this._backupExportDialog)return void(this._backupExportDialog=null);if(this._settingsDialog)return void(this._settingsDialog=null);if(this._markerDialog)return void this._closeMarkerDialog();if(this._deviceInbox)return void(this._deviceInbox=null);if(this._openingDialog)return void(this._openingDialog=null);if(this._physicalDialog)return void(this._physicalDialog=null);if(this._backdropDialog)return void(this._backdropDialog=null);if(this._decorShapeDialog)return void(this._decorShapeDialog=null);if(this._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0);if(this._editorSecondary?.hasOpenGroup)return e.preventDefault(),void this._editorSecondary?.closeGroup(!0)}const t=e.composedPath?.()||[e.target],i=t.some(e=>e?.matches?.('input, textarea, select, [contenteditable="true"]')),n=t.some(e=>e?.classList?.contains?.("editor-secondary")),r=e.ctrlKey||e.metaKey,o=e.key.toLowerCase(),s=/^[a-z]$/.test(o),a="z"===o||!s&&"KeyZ"===e.code,l="y"===o||!s&&"KeyY"===e.code,c=r&&(a&&e.shiftKey||l),h=r&&a&&!e.shiftKey;if("decor"===this._mode){if((h||c)&&i)return;return c?(e.preventDefault(),void this._redoGeometry()):h?(e.preventDefault(),this._decorDraft?void(this._decorDraft=null):this._decorMove||this._dtDrag||this._bdDrag?void this._cancelDecorGesture():void this._undoGeometry()):"Delete"!==e.key&&"Backspace"!==e.key||!this._decorSel||i||n?void("Escape"===e.key&&(e.preventDefault(),this._decorDraft?this._decorDraft=null:this._decorMove||this._dtDrag||this._bdDrag?this._cancelDecorGesture():"furniture"===this._decorTool?(this._editorRuntime?._clearFurniturePreview(),this._editorRuntime?._furnShiftDetach(),this._furnPalette=null,this._furnCategory=null,this._decorTool="select"):this._decorSel?this._decorSel=null:"select"!==this._decorTool?this._decorTool="select":this._setMode("view"))):(e.preventDefault(),void this._decorDeleteSel())}if(this._markup&&(!h&&!c||!i)){if(("Delete"===e.key||"Backspace"===e.key)&&this._physicalSel&&!i&&!n)return e.preventDefault(),void this._deletePhysicalSelection();if(c)return e.preventDefault(),void this._redoGeometry();if(h)return e.preventDefault(),this._resize?.dragging?void this._rszCancelDrag():this._wallFaceBatch?(this._roomDialogCancel(),void(this._activeDraftId&&this._path.length>1?this._undoActiveDraftPoint():this._undoPoint())):"draw"===this._tool&&this._path.length?void(this._activeDraftId&&this._path.length>1?this._undoActiveDraftPoint():this._undoPoint()):"split"===this._tool&&this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},void(this._splitSel.pts.length||(this._cursorPt=null))):void this._undoGeometry();if("Escape"===e.key)return this._physicalDrag||this._physicalRotate?(e.preventDefault(),void this._cancelPhysicalGesture()):this._roomDialog?(e.preventDefault(),void this._roomDialogCancel()):"draw"===this._tool&&this._path.length?(e.preventDefault(),void this._finishWallChain()):this._physicalSel?(e.preventDefault(),void(this._physicalSel=null)):"resize"===this._tool?(e.preventDefault(),this._resize?.dragging?void this._rszCancelDrag():("exit-tool"===this._resize?.escapeIdle()&&(this._tool="draw"),void this.requestUpdate())):"split"===this._tool?(e.preventDefault(),void(this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},this._splitSel.pts.length||(this._cursorPt=null)):this._splitSel?this._splitSel=null:this._tool="draw")):"merge"===this._tool?(e.preventDefault(),void(this._mergeSel?this._mergeSel=null:this._tool="draw")):this._wallDialog?(e.preventDefault(),void(this._wallDialog=null)):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool&&"column"!==this._tool||(e.preventDefault(),"opening"===this._tool&&this._clearOpeningPlacement(!0),this._tool="draw"))}}_undoPoint(){if(this._path.length){if(this._contourClosed)return this._path=this._path.slice(0,-1),void(this._closingWallCm=null);if(this._activeDraftId&&this._path.length>1&&this._curSpaceCfg){const e=this._geometrySnapshot();this._path=this._path.slice(0,-1),this._draftSegmentCms=this._draftSegmentCms.slice(0,-1);const t=this._curSpaceCfg,i=(t.room_drafts||[]).findIndex(e=>e.id===this._activeDraftId);if(i>=0){const n=Array.isArray(t.room_drafts[i]?.segments)?t.room_drafts[i].segments:[];this._path.length<2?(t.room_drafts.splice(i,1),t.room_drafts.length||delete t.room_drafts,this._activeDraftId=null):t.room_drafts[i]={id:this._activeDraftId,points:this._path.map(e=>[e[0]/hf,e[1]/hf]),segments:this._draftSegmentCms.map((e,t)=>({...n[t]||{},cm:e}))},this._commitPhysicalGeometry(this._t("history.draft_segment_delete"),e)}return}this._path=this._path.slice(0,-1)}}_undoActiveDraftPoint(){const e=this._activeDraftId,t=this._path[0]?[...this._path[0]]:null,i=this._geometryHistory.undo();if(!i)return void this._undoPoint();if(!this._applyGeometryState(i.before,!0))return void this._geometryHistory.clear();const n=e?this._spaceModel()?.room_drafts.find(t=>t.id===e):null;n?(this._activeDraftId=n.id,this._path=n.points.map(e=>[...e]),this._draftSegmentCms=this._adoptDraftCms(this._path,n.segments.map(e=>e.cm),n.id),this._resumeDraftBySpace[this._space]=n.id):(this._activeDraftId=null,this._path=t?[t]:[],this._draftSegmentCms=[],e&&this._resumeDraftBySpace[this._space]===e&&delete this._resumeDraftBySpace[this._space]),this._clearPlanSnapHover(),this._showToast(this._t("history.undone",{name:i.name}))}static async getConfigElement(){return await import("./editor-CIkons5x.js"),document.createElement("houseplan-card-editor")}static getStubConfig(){return{type:"custom:houseplan-card"}}static _warmBootReset(e){for(const e of J_.values())for(const t of e)clearTimeout(t.evict);J_.clear(),tf=e&&e>0?e:1e4}static _warmBootStats(){let e=0,t=0;const i=[];for(const n of J_.values())for(const r of n)e++,r.dlg&&(t++,i.push(r.dlg.kind));return{keys:J_.size,slots:e,dlgs:t,drafts:i}}setConfig(e){const t=this._config,i=!!t&&Object.prototype.hasOwnProperty.call(t,"floor");this._config={icon_size:2.5,show_temperature:!0,live_states:!0,show_signal:!0,...e};(i!==this._hasFixedFloor||t?.floor!==this._config.floor)&&(this._hashApplied=!1,this._navApplied=!1,this._warmVpArmed=!1),this._config.kiosk&&(this._booting=!1,this._bootFading=!1),!this._hasFixedFloor&&e.default_floor&&this._commitSpace(e.default_floor,!0);try{this._zoomBySpace=JSON.parse(localStorage.getItem(of)||"{}")||{}}catch{this._zoomBySpace={}}try{const e=JSON.parse(localStorage.getItem(lf)||"{}")||{};this._viewPreference=Object.fromEntries(Object.entries(e).filter(e=>"flat"===e[1]||"iso"===e[1]))}catch{this._viewPreference={}}this._labs=g_(G_);try{const e=JSON.parse(localStorage.getItem(af)||"null");this._kioskScale={icon:yn(e?.icon),font:yn(e?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(rf)||"null");e&&e.config&&Array.isArray(e.config.spaces)&&(this._serverCfg=e.config,this._seedDecorStyle(this._serverCfg),this._cfgEpoch++,this._cfgRev=e.rev||0,this._cfgContentFingerprint=e.config_fingerprint||Rl(e.config),this._layout=e.layout||{},this._layoutRev=e.layout_rev||0,this._layoutContentFingerprint=e.layout_fingerprint||Rl(this._layout),this._virtualLights=Dc(e.virtual_lights,this._cfgRev),this._serverStorage=!0)}catch{}this._adoptInitialSpace(this._model,this._loadOk),"view"!==this._mode||this._view||(this._zoom=this._zoomBySpace[this._space]||1),this.isConnected&&(this._syncCycleTimer(),this._warmAdopt(),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1)}_warmAdopt(){if(this._config?.kiosk)return;const e=ef(this._config);if(this._warmKey===e&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const t=this.parentNode,i=this._warmIdx(t),n=J_.get(e);if(!n||!n.length)return;const r=n.find(e=>e.owner===this._warmGen);if(r)return this._warmLongReturn=!!r.freed&&Date.now()-r.freed>=wl,clearTimeout(r.evict),r.evict=0,r.freed=0,r.live=!0,this._warmSlot=r,this._warmKey=e,r.frameFingerprint&&this._continuity.adoptCompleteFrame(r.frameFingerprint),void(!this._devices.length&&r.devices?.length&&(this._devices=[...r.devices]));const{slot:o,sure:s}=((e,t,i,n)=>{const r=e=>{const t=!!i&&e.place?.deref()===i;return t&&e.idx===n?4:e.live?0:t?3:2};let o=null,s=0,a=0,l=null;for(const i of e){if(i.owner===t)continue;l=i;const e=r(i);e<=0||(e>s?(o=i,s=e,a=1):e===s&&a++)}return!o||a>1?{slot:o||l,sure:!1}:{slot:o,sure:!0}})(n,this._warmGen,t,i);o&&(this._warmLongReturn=!!o.freed&&Date.now()-o.freed>=wl,this._booting=!1,this._bootFading=!1,this._hdrH=o.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Y_)),this._warmKey=e,s?(clearTimeout(o.evict),o.evict=0,o.owner=this._warmGen,o.place=t?new WeakRef(t):null,o.idx=i,o.live=!0,this._warmSlot=o,this._warmVp=o.vp,o.frameFingerprint&&this._continuity.adoptCompleteFrame(o.frameFingerprint),!this._devices.length&&o.devices?.length&&(this._devices=[...o.devices]),this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,path:location.pathname,place:t?new WeakRef(t):null,idx:i,live:!0,hdrH:o.hdrH,stageH:o.stageH,vp:null,frameFingerprint:"",devices:null,dlg:null,freed:0,evict:0},n.push(this._warmSlot),this._warmTrim(n)))}_warmIdx(e){const t=e?.children;if(!t)return-1;for(let e=0;e4;){const t=e.findIndex(e=>!e.live);if(t<0)break;clearTimeout(e[t].evict),e.splice(t,1)}}_warmScheduleEvict(e,t){if(clearTimeout(e.evict),!(e.dlg||e.vp&&"view"!==e.vp.mode))return;const i=e.freed,n=e.owner;e.evict=window.setTimeout(()=>{if(e.evict=0,e.freed!==i||e.owner!==n)return;var r;e.dlg=null,e.vp=(r=e.vp)&&"view"!==r.mode?{...r,mode:"view",zoom:r.snap?.space===r.space?r.snap.zoom:r.zoom,view:null,snap:null,tool:"draw",decorTool:"select",showHidden:!1,selId:null,rszSel:null,decorSel:null}:r,e.frameFingerprint="";const o=J_.get(t);if(!e.live&&o&&o.length>1){const t=o.indexOf(e);t>=0&&o.splice(t,1)}},tf+250)}_warmAdoptViewport(e){const t=this._warmVp;if(!t||this._warmSlot?.path!==location.pathname)return void(this._warmVp=null);const i=this._fixedFloorState();if(this._hashApplied||!this._model.find(e=>e.id===t.space)||"valid"===i.kind&&i.id!==t.space||this._hasFixedFloor&&"valid"!==i.kind)return void(this._warmVp=null);this._commitSpace(t.space,!0),this._navApplied=!0;const n="view"!==t.mode&&this._canEdit&&!e.kiosk?t.mode:"view";this._adoptMode("view"),this._pendingNavMode="view"===t.mode||this._canEdit||e.kiosk?null:t.mode,this._zoom=t.zoom;const r=this._effectiveProjection(),o=r===t.projection&&this._labsIso===t.activeLabsIso;if(this._view=o&&t.view?{...t.view}:null,this._viewModeSnap=o&&t.snap?{...t.snap}:null,!o&&t.logicalCenter){const e="iso"===r?b_([t.logicalCenter.x,t.logicalCenter.y],0):[t.logicalCenter.x,t.logicalCenter.y];this._applyView(t.zoom,e[0],e[1])}var s;this._tool="opening"===(s=function(e){return"partition"===e?"draw":e}(s=t.tool))?"draw":"string"==typeof s&&uf.has(s)?s:"draw",this._decorTool=t.decorTool,this._showHidden=t.showHidden,this._showFar!==t.showFar&&(this._showFar=t.showFar,this._frame=null),this._selId=t.selId,this._resize?.restoreSelection(t.rszSel),this._decorSel=t.decorSel,this._warmVpArmed=!0,"view"!==n&&this._requestMode(n,!1,!0)}_warmPatch(e,t=!1){if(this._config?.kiosk)return;const i=ef(this._config);if(!this._warmSlot||this._warmKey===i){if(!this._warmSlot){if(!t)return;const e=this.parentNode;this._warmKey=i,this._warmSlot={owner:this._warmGen,path:location.pathname,place:e?new WeakRef(e):null,idx:this._warmIdx(e),live:!0,hdrH:this._hdrH,stageH:0,vp:null,frameFingerprint:"",devices:null,dlg:null,freed:0,evict:0};const n=J_.get(i)||[];for(n.push(this._warmSlot),J_.set(i,n),this._warmTrim(n);J_.size>8;){const e=J_.keys().next().value;if(void 0===e||e===i)break;for(const t of J_.get(e)||[])clearTimeout(t.evict);J_.delete(e)}}Object.assign(this._warmSlot,e)}}_warmViewportState(){const e=this._effectiveProjection();return{space:this._space,mode:this._mode,projection:e,activeLabsIso:this._labsIso,logicalCenter:this._logicalViewCenter(e),zoom:this._zoom,view:this._view?{...this._view}:null,snap:this._viewModeSnap?{...this._viewModeSnap}:null,tool:this._tool,decorTool:this._decorTool,showHidden:this._showHidden,showFar:this._showFar,selId:this._selId,rszSel:this._resize?.selectedRoomId,decorSel:this._decorSel}}_warmDialogState(){const e=(e,t)=>({kind:e,space:this._space,mode:this._mode,data:t});return this._tapConfirm||this._alignDialog||this._mergeDialog||this._importDialog||this._backupExportDialog||this._backupImportDialog?null:this._openingInfo?e("openingInfo",this._openingInfo.id):this._infoCard?e("info",this._infoCard.id):this._rulesDialog?this._rulesDialog.busy?null:e("rules",this._rulesDialog):this._settingsDialog?this._settingsDialog.busy?null:e("settings",this._settingsDialog):this._markerDialog?this._markerDialog.busy?null:e("marker",this._markerDialog):this._openingDialog?e("opening",this._openingDialog):this._backdropDialog?e("backdrop",this._backdropDialog):this._decorShapeDialog?e("decorShape",this._decorShapeDialog):this._decorTextDialog?e("decorText",this._decorTextDialog):this._roomDialog?e("room",{editId:this._roomEditId,fill:this._roomFill,customFill:this._roomCustomFill,tempSrc:this._roomTempSrc,humSrc:this._roomHumSrc,srcOpen:this._roomSrcOpen,srcFilter:this._roomSrcFilter,nameScale:this._roomNameScale,labelScale:this._roomLabelScale,areaSel:this._areaSel,nameSel:this._nameSel,pendingSplit:this._pendingSplit,wallFaceBatch:this._wallFaceBatch,path:this._path}):this._spaceDialog?this._spaceDialog.busy?null:e("space",this._spaceDialog):null}_warmSnapshot(){if(this._booting||this._config?.kiosk||"steady"!==this._continuity.state)return;const e={vp:this._warmViewportState(),frameFingerprint:this._continuity.frameFingerprint,devices:this._devices};if(this._warmRevivePending||(e.dlg=this._warmDialogState()),this.isConnected&&this._warmSlot?.owner===this._warmGen){const t=this.parentNode;e.place=t?new WeakRef(t):null,e.idx=this._warmIdx(t)}this._warmPatch(e)}_warmReviveDialog(){const e=this._warmSlot;if(this._warmReviveTimer=void 0,!e||!e.dlg)return void(this._warmRevivePending=!1);const t=e.dlg,i=e.freed;if(t.mode===this._mode||"view"===t.mode||this._warmVp?.mode!==t.mode||this._editorRuntime){if(this._warmRevivePending=!1,e.dlg=null,e.freed=0,clearTimeout(e.evict),e.evict=0,i&&!(Date.now()-i>tf)&&t.space===this._space&&t.mode===this._mode){switch(t.kind){case"space":this._spaceDialog={...t.data,busy:!1,savedBusy:!1};break;case"marker":this._markerDialog={...t.data,busy:!1};break;case"settings":this._settingsDialog={...t.data,busy:!1};break;case"rules":this._rulesDialog={...t.data,busy:!1};break;case"opening":this._openingDialog={...t.data};break;case"backdrop":this._backdropDialog={...t.data};break;case"decorShape":this._decorShapeDialog={...t.data};break;case"decorText":{this._decorTextDialog={...t.data};const e=String(this._decorTextDialog?.text??"").length;this._decorTextSelection={start:e,end:e};break}case"room":{const e=t.data;this._roomEditId=e.editId,this._roomFill=e.fill,this._roomCustomFill=e.customFill||null,this._roomTempSrc=e.tempSrc,this._roomHumSrc=e.humSrc,this._roomSrcOpen=e.srcOpen,this._roomSrcFilter=e.srcFilter,this._roomNameScale=e.nameScale,this._roomLabelScale=e.labelScale,this._areaSel=e.areaSel,this._nameSel=e.nameSel,this._pendingSplit=e.pendingSplit,this._wallFaceBatch=e.wallFaceBatch||null,this._path=e.path,this._wallFaceBatch&&(this._activeDraftId=this._wallFaceBatch.activeDraftId,this._draftSegmentCms=[...this._wallFaceBatch.activeCms]),this._roomDialog=!0;break}case"info":{const e=this._devices.find(e=>e.id===t.data);e&&(this._infoCard=e);break}case"openingInfo":{const e=(this._curSpaceCfg?.openings||[]).find(e=>e.id===t.data);e&&(this._openingInfo=e);break}}this.requestUpdate()}}else this._warmRevivePending=!0}_cacheSnapshot(){if(this._serverCfg)try{this._cfgContentFingerprint=Rl(this._serverCfg),this._layoutContentFingerprint=Rl(this._layout),this._virtualLights=function(e,t,i){if(e.configRev===i)return e;const n=new Set((Array.isArray(t?.markers)?t.markers:[]).filter(Oc).map(e=>e.id));return{...e,configRev:i,off:new Set([...e.off].filter(e=>n.has(e)))}}(this._virtualLights,this._serverCfg,this._cfgRev),localStorage.setItem(rf,JSON.stringify({config:this._serverCfg,rev:this._cfgRev,config_fingerprint:this._cfgContentFingerprint,layout:this._layout,layout_rev:this._layoutRev,layout_fingerprint:this._layoutContentFingerprint,virtual_lights:Ac(this._virtualLights)}))}catch{}}_beginContinuityCandidate(e,t,i="plan"){return this._booting&&!this._continuity.hasCompleteFrame?this._continuity.token:(this._continuityDataReady=t,this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._resumeSettling=!0,this._continuity.beginCandidate(e,i))}_continuityStageValid(){const e=this._stageEl;return!!e&&e.clientWidth>0&&e.clientHeight>0}_continuityAssetsReady(){if(!this._model.length)return!1;const e=this._model.length?this._spaceModel():null;return!e?.bg?.href||this._signer.isReady(this.hass,e.bg.href)}_initialSpaceSelection(e,t=this._loadOk){const i=this._fixedFloorState(e,t);return"valid"===i.kind?{id:i.id,source:"fixed"}:this._hasFixedFloor?{id:null,source:"none"}:function(e){const t=new Set(e.spaceIds.filter(e=>!!e));if(!t.size)return{id:null,source:"none"};const i=[["hash",!1===e.acceptHash?null:e.hashSpace],["current",e.preserveCurrent?e.currentSpace:null],["saved",e.savedSpace],["default",e.defaultSpace],["first",e.spaceIds[0]]];for(const[e,n]of i)if(n&&t.has(n))return{id:n,source:e};return{id:null,source:"none"}}({spaceIds:e.map(e=>e.id),hashSpace:this._hashSpace(),acceptHash:!this._hashApplied,currentSpace:this._space,preserveCurrent:this._hashApplied||this._navApplied||this._warmVpArmed,savedSpace:this._savedNav()?.space,defaultSpace:this._config?.default_floor})}_adoptInitialSpace(e,t=this._loadOk){const i=this._initialSpaceSelection(e,t);return i.id?(this._commitSpace(i.id,!0),"hash"===i.source&&(this._hashApplied=!0),"saved"===i.source&&(this._navApplied=!0),i):i}_candidateBackdrop(e,t=this._space){const i=$a(e),n=this._fixedFloorState(i,!0);if(this._hasFixedFloor&&"valid"!==n.kind)return"";const r=this._initialSpaceSelection(i,!0).id||(i.some(e=>e.id===t)?t:i[0]?.id);return i.find(e=>e.id===r)?.bg?.href||""}_visualFrameFingerprint(){const e=this._stageEl,t=e?[e.clientWidth,e.clientHeight]:[0,0];return Tl([this._cfgRev,this._cfgContentFingerprint||Rl(this._serverCfg),this._layoutRev,this._layoutContentFingerprint||Rl(this._layout),this._space,this._mode,this._view,t,this._glowScreenBlend?"screen":"normal",this.hass?.themes?.darkMode??this.hass?.themes?.default_theme??""])}_settleContinuityFrame(){if(this._booting||!this._continuityStageValid())return;if(!this._continuity.hasCompleteFrame&&"steady"===this._continuity.state)return void(this._continuityAssetsReady()&&(this._renderSnapshotAt=Date.now(),this._continuity.markCompleteFrame(this._visualFrameFingerprint())));if(!this._continuityDataReady)return;if(!["holding","offline-stale","overlay-pending","overlay-visible","candidate-ready"].includes(this._continuity.state))return;const e=this._continuity.token;if(this._candidateDeviceSnapshot&&this._candidateDeviceSnapshot!==this._visibleDeviceSnapshot&&this._stagedDeviceSnapshotToken!==e)return this._stagedDeviceSnapshotToken=e,void this.requestUpdate();this._continuityPaintToken!==e&&(this._continuityPaintToken=e,this._continuity.candidateReady(e)&&this._continuity.commitAfterPaint(e,{updateComplete:()=>this.updateComplete,stageValid:()=>this.isConnected&&this._continuityStageValid(),assetsReady:()=>this._continuityAssetsReady(),frameFingerprint:()=>this._visualFrameFingerprint()}).then(t=>{t&&e===this._continuity.token?(this._resumeSettling=!1,this._renderSnapshotAt=Date.now(),this._candidateDeviceSnapshot&&(this._visibleDeviceSnapshot=this._candidateDeviceSnapshot),this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._warmSnapshot()):e===this._continuity.token&&(this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._candidateDeviceSnapshot=null,this.requestUpdate())}))}_onBackdropLoaded(e,t){this._signer.markLoaded(this.hass,e,t),this._continuity.note("asset-ready"),this._continuityPaintToken=-1,"steady"!==this._continuity.state&&this.requestUpdate()}_renderRecoveryOverlay(){if(!this._continuity.overlayVisible&&"recovery-error"!==this._continuity.state)return V;const e="connection"===this._continuity.recoveryReason;return B`
e.stopPropagation()} @click=${e=>e.stopPropagation()} @@ -4574,7 +4574,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 aria-label=${this._t("editor.loading_aria")}> ${this._t("editor.loading")} -
`:V}houseplanContinuityTrace(){return[...this._continuityHistory,...this._continuity.trace].slice(-80).map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}getCardSize(){return 12}get _norm(){return!(!this._serverCfg||!this._serverCfg.spaces.length)}_cfgFingerprint(){const e=this._serverCfg?.spaces||[];let t=e.length+":";for(const i of e){t+=(i.id||"")+","+(i.plan_aspect||"")+","+(i.plan_url||"").length+","+(i.plan_x??"")+","+(i.plan_y??"")+","+(i.plan_scale??"")+","+(i.plan_scale_x??"")+","+(i.plan_scale_y??"")+","+(i.plan_angle??"")+","+(i.rooms?.length||0)+","+(i.openings?.length||0)+","+(i.decor?.length||0)+";";for(const e of i.rooms||[]){const i=e.poly?.[0],n=e.poly?.[e.poly.length-1];t+=(e.poly?.length||0)+"."+(e.id||"")+"."+(e.open_to||[]).join("+")+"."+(e.area||"")+"."+JSON.stringify(e.settings||0)+"."+(e.x??"")+","+(e.y??"")+","+(e.w??"")+","+(e.h??"")+","+(i?i[0]+"/"+i[1]:"")+","+(n?n[0]+"/"+n[1]:"")+";"}}return t}get _model(){if(!this._serverCfg)return[];const e=this._cfgEpoch+"|"+this._cfgFingerprint();if(this._modelCache&&this._modelCache.key===e)return this._modelCache.model;const t=this._buildModel();return this._modelCache={key:e,model:t},t}_buildModel(){if(!this._serverCfg)return[];const e=this._renderCfg;return $a(e).map((t,i)=>{const n=e.spaces[i]?.plan_url;return t.bg&&n?{...t,bg:{...t.bg,href:n}}:t})}_spaceModel(){return this._hasFixedFloor?zl(this._model,this._space):(e=this._model,t=this._space,e.find(e=>e.id===t)??e[0]);var e,t}_spaceModelById(e){return zl(this._model,e)}_syncEmptySpaceState(){if(!!this._serverCfg&&0===this._serverCfg.spaces.length){if(!this._emptySpaceStateActive){this._emptySpaceStateActive=!0;for(const e of this._pointers.keys())for(const t of this.renderRoot.querySelectorAll("*"))try{t.hasPointerCapture?.(e)&&t.releasePointerCapture(e)}catch{}this._pointers.clear(),this._panStart=null,this._panLock=null,this._pinchStart=null,this._swipeStart=null,this._drag=null,this._rlResize=null,this._vacFit=null,this._compassDrag=!1,this._cancelModeTransition(!1),this._mode="view",this._clearGeometryGesture(),this._geometryHistory.clear(),this._resumeDraftBySpace={},this._tip=null,this._hoverRoom=null,this._openingInfo=null,this._closeInfoCard(),this._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._physicalDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._roomDialog=!1,"edit"===this._spaceDialog?.mode&&(this._spaceDialog=null),this._editorSecondary?.closeForNavigation(),this._saveConfigDebounced.cancel(),this._frame=null,this._planSnapGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._decorSnapCache=null,this._commitSpace("",!0)}}else this._emptySpaceStateActive=!1}get _areaToSpace(){const e={};for(const t of this._model)for(const i of t.rooms)i.area&&(e[i.area]={space:t.id,room:i});return e}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._showHidden||!this._settings.filter_seeded&&!!this._settings.show_all}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const e=this._serverCfg,t=function(e){const t=e.hass,i=e.registry||Kc(t),n=Jc(t,i),r=Qc(t,i),{areaToSpace:o,markers:s,settings:a,excluded:l,iconRules:c}=e,h=!1!==a.group_lights,d=Wh(s),u=Fh(n,h).filter(e=>!qh(n,e.eid,d)),p=new Set(u.map(e=>e.area)),m=nh(n),_=rh(s,r),f=new Set(s.map(e=>e.binding)),g=[];for(const e of Object.values(n.devices)){const r=e.area_id;if(!r||!o[r])continue;if("service"===e.entry_type)continue;if(f.has("device:"+e.id))continue;if("active"!==ih(t,"device:"+e.id,i).kind)continue;const s=oh(n,e.id,m[e.id]||[],_);if(s.partial&&!s.entityIds.length)continue;const a=s.entityIds,d=sh(n,e,a);let u=l.has(d)||"Group"===e.model||/scene/i.test(e.model||"")||/bridge/i.test((e.model||"")+(e.name||""))||"myheat"===d&&!!e.via_device_id;if(!u&&h&&p.has(r)){const t=(e.name_by_user||e.name||"").trim();"mdi:lightbulb"===Ih(n,t,e.model,a,c)&&(u=!0)}u&&g.push("device:"+e.id)}return g}({hass:this.hass,registry:this._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,firstSpaceId:this._model[0]?.id||"",iconRules:this._iconRules});if(!t.length&&e.settings?.filter_seeded)return;e.markers=e.markers||[];const i=[];for(const n of t){const t="h"+n.slice(n.indexOf(":")+1);e.markers.push({id:t,binding:n,hidden:!0}),i.push(n.slice(n.indexOf(":")+1))}const n={...e.settings||{},filter_seeded:!0};delete n.show_all,i.length&&Array.isArray(n.new_device_ids)&&(n.new_device_ids=n.new_device_ids.filter(e=>!i.includes(e))),e.settings=n,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}get _iconRules(){const e=this._settings.icon_rules;if(!e||!Array.isArray(e)||!e.length)return;const t=JSON.stringify(e);return t!==this._rulesCompiledSrc&&(this._rulesCompiledSrc=t,this._rulesCompiled=ge(e)),this._rulesCompiled}get _fillColors(){return Gi(this._settings)}get _excluded(){const e=this._settings.exclude_integrations;return e?new Set(e):_e}willUpdate(e){e.has("_serverCfg")&&this._cfgEpoch++,this._syncEmptySpaceState(),e.has("hass")&&this.hass&&(this._hassSequence++,this._renderSnapshotAt=Date.now(),this._continuity.note("hass-snapshot"),this._ensureHaRegistryAuthority(),this._planHassMemo=null,this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices(),this._vacTick(),this._activityTick()),this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._continuity.refreshCompleteFrame(this._visualFrameFingerprint()),this._captureRenderDeviceSnapshot()}updated(){this._pruneDevicePressFeedback(),this._syncDayCycleClock(),this._warmSnapshot(),this._editorRuntime&&this._dtMeasure();const e=this._stageEl;e&&!this._roViewport&&(this._roViewport=new ResizeObserver(()=>this._refitView()),this._roViewport.observe(e)),e&&this._booting&&!this._bootTimer&&this._bootWatch();const t=this.renderRoot.querySelector(".hdr");if(t&&e&&!this._roHdr){const i=()=>{const t=this.renderRoot.querySelector("ha-card");if(!t)return;const i=e.getBoundingClientRect().top-t.getBoundingClientRect().top,n=Math.min(Math.max(t.getBoundingClientRect().top,0),120),r=Math.round(i+n);r>=0&&r!==this._hdrH&&(this._hdrH=r),r>=0&&!this._booting&&!this._config?.kiosk&&e.clientHeight>0&&this._warmPatch({hdrH:r,stageH:e.clientHeight})};this._roHdr=new ResizeObserver(()=>requestAnimationFrame(i)),this._roHdr.observe(t),this._onWinResize=()=>requestAnimationFrame(i),window.addEventListener("resize",this._onWinResize),i()}if(e&&!this._view&&this._refitView(),this._editorSecondary?.afterRender(),this._settleContinuityFrame(),this._serverStorage&&this._loadOk&&0===this._model.length&&!this._spaceDialog&&!this._importDialog&&!this._onboardingShown){this._onboardingShown=!0;const e=function(e){const t=e?.floors;if(!t||"object"!=typeof t)return[];const i=[];for(const e of Object.values(t))e&&e.floor_id&&i.push({id:e.floor_id,name:e.name||e.floor_id,level:e.level??null});return i.sort((e,t)=>{const i=e.level??1e9,n=t.level??1e9;return i!==n?i-n:e.name.localeCompare(t.name)}),i}(this.hass);e.length?this._importDialog={floors:e.map(e=>({...e,checked:!0}))}:this._openSpaceDialog("create")}}_adoptStructuralResponses(e,t,i){const n=e?.config,r=n&&Array.isArray(n.spaces)?n:null,o=Rl(r),s=o!==(this._cfgContentFingerprint||Rl(this._serverCfg));if(s&&(this._geometryHistory.clear(),this._pendingPhysicalWrites.clear(),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=r,this._seedDecorStyle(this._serverCfg),this._cfgContentFingerprint=o),this._cfgRev=e?.rev??this._cfgRev,e&&("virtual_lights"in e||"config"in e)){const t=zc(this._virtualLights,e.virtual_lights,this._cfgRev,"virtual_lights"in e);Pc(t)!==Pc(this._virtualLights)&&(this._virtualLights=t,this._capturedSnapshotVirtual="")}let a=!1;if(void 0!==t||void 0!==i){const e=i??t?.layout??{},n=Rl(e);a=n!==(this._layoutContentFingerprint||Rl(this._layout)),a&&(this._layout=e,this._layoutContentFingerprint=n),this._layoutRev=t?.rev??this._layoutRev}return this._canOptimizeUndo=!(!e?.can_optimize_undo&&!t?.can_optimize_undo),this._haIntegrationVersion="string"==typeof e?.integration_version?e.integration_version:this._haIntegrationVersion,this._undoKind=e?.undo_kind||t?.undo_kind||null,"boolean"==typeof e?.can_write&&(this._serverCanWrite=e.can_write),s&&this._continuity.note("config-candidate",{configRev:this._cfgRev}),a&&this._continuity.note("layout-candidate",{layoutRev:this._layoutRev}),{configChanged:s,layoutChanged:a}}_resumePendingNavMode(){if(!this._pendingNavMode||!this._canEdit||this._config?.kiosk)return!1;const e=this._pendingNavMode;return this._pendingNavMode=null,this._setMode(e,!1),!0}async _loadFromServer(){this._loading=!0,this._loadTries++;const e=this._space,t=!!this._view;try{const[i,n]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]),r=i?.config&&Array.isArray(i.config.spaces)?i.config:null,o=Rl(r)!==(this._cfgContentFingerprint||Rl(this._serverCfg))||Rl(n?.layout??{})!==(this._layoutContentFingerprint||Rl(this._layout));if(o){if(!await this._signer.prepareImage(this.hass,this._candidateBackdrop(r)))return this._continuity.note("asset-failed"),void this._scheduleLoadRetry(!0)}o&&this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._beginContinuityCandidate("structural-response",!0),this._connectionWasLost=!1,this._serverStorage=!0,"boolean"==typeof i?.can_write&&(this._serverCanWrite=i.can_write),this._canOptimizeUndo=!(!i?.can_optimize_undo&&!n?.can_optimize_undo),this._haIntegrationVersion="string"==typeof i?.integration_version?i.integration_version:this._haIntegrationVersion,this._adoptStructuralResponses(i,n),this._adoptInitialSpace(this._model,!0),this._resumePendingNavMode(),this._cacheSnapshot(),this._warmVpArmed&&this._space===this._warmVp?.space?this._warmVpArmed=!1:t&&this._space===e||this._restoreZoom(),this._loadOk=!0,this.hass.callWS({type:"houseplan/trail/get"}).then(e=>{this._vacSrvTrails=e?.trails||{},this.requestUpdate()}).catch(()=>{}),this._ensureLiveSyncSubscriptions()}catch(e){if(this._serverCfg)this._scheduleLoadRetry(!0);else if(this._loadTries>=8){this._serverStorage=!1;try{this._layout=JSON.parse(localStorage.getItem(tf)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1,this._continuityDataReady=!0,this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}}_ensureLiveSyncSubscriptions(){const e=this.hass?.connection;if(!e)return;if(e!==this._liveSyncConnection&&(this._unsubCfg?.(),this._unsubCfg=null,this._unsubLayout?.(),this._unsubLayout=null,this._unsubTrail?.(),this._unsubTrail=void 0,this._unsubVirtual?.(),this._unsubVirtual=null,this._liveSyncGeneration++,this._liveSyncAttempt=null,this._liveSyncConnection=e),this._liveSyncAttempt)return;const t=this._liveSyncGeneration,i=[],n=(n,r,o,s)=>{n()||i.push(async()=>{const i=await e.subscribeEvents(s,o);t===this._liveSyncGeneration&&this.isConnected&&this.hass?.connection===e&&!n()?r(i):i?.()})};if(n(()=>this._unsubCfg,e=>{this._unsubCfg=e},"houseplan_config_updated",e=>{const t=Number(e?.data?.rev??-1);t!==this._cfgRev&&this._reloadConfigOnly(!1,t)}),n(()=>this._unsubTrail,e=>{this._unsubTrail=e},"houseplan_trail_updated",async()=>{try{const e=await this.hass.callWS({type:"houseplan/trail/get"});this._vacSrvTrails=e?.trails||{},this.requestUpdate()}catch{}}),n(()=>this._unsubLayout,e=>{this._unsubLayout=e},"houseplan_layout_updated",e=>this._onLayoutEvent(Number(e?.data?.rev??-1))),n(()=>this._unsubVirtual,e=>{this._unsubVirtual=e},"houseplan_virtual_light_updated",e=>{const t=Ic(this._virtualLights,e?.data);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}),!i.length)return;const r=function(e){return Promise.allSettled(e.map(e=>Promise.resolve().then(e)))}(i).then(()=>{});this._liveSyncAttempt=r,r.finally(()=>{this._liveSyncAttempt===r&&(this._liveSyncAttempt=null)})}async _reloadConfigOnly(e=!1,t){if(!e){if(void 0!==t&&t<=this._cfgRev)return;if(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(!1,t),400))}this._beginContinuityCandidate("config-reload",!1);try{const e=await this.hass.callWS({type:"houseplan/config/get"}),t=e?.config&&Array.isArray(e.config.spaces)?e.config:null;if(Rl(t)!==(this._cfgContentFingerprint||Rl(this._serverCfg))&&!await this._signer.prepareImage(this.hass,this._candidateBackdrop(t)))return this._continuity.note("asset-failed"),void this._scheduleLoadRetry(!0);const i=this._space;this._adoptStructuralResponses(e),this._adoptInitialSpace(this._model,!0),this._resumePendingNavMode(),this._cacheSnapshot(),this._space!==i&&this._restoreZoom(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}catch(e){this._showToast(this._t("toast.cfg_reload_failed",{err:this._errText(e)}))}finally{this._continuityDataReady=!0,this.requestUpdate()}}_scheduleLoadRetry(e=!1){if(void 0!==this._loadRetryTimer)return;const t=Math.min(8e3,500*2**Math.min(4,Math.max(1,this._loadTries-7)));this._loadRetryTimer=window.setTimeout(()=>{this._loadRetryTimer=void 0,!e&&this._loadOk||this._loading||!this.hass||this._loadFromServer()},t)}_ensureHaRegistryAuthority(){const e=this.hass?.connection||null;e&&e!==this._haRegistryConnection&&(this._haRegistryRelease?.(),this._haRegistryConnection=e,this._haRegistryRev=-1,this._haBindingCacheKey="",this._planHassMemo=null,this._haRegistryRelease=Gc(this.hass,this._onHaRegistryUpdate),this._onHaRegistryUpdate())}get _haRegistry(){return Kc(this.hass)}get _planHass(){const e=this._haRegistry,t=Xc(this.hass,e),i=this._planHassMemo;if(i&&i.hass===this.hass&&i.sig===t)return i.active;const n=Jc(this.hass,e),r=Qc(this.hass,e);return this._planHassMemo={hass:this.hass,sig:t,active:n,full:r},n}_captureRenderDeviceSnapshot(){if(!this.hass)return;const e=Date.now(),t=[...this._activityRt.entries()].map(([t,i])=>`${t}:${i.gen}:${i.flashTs}:`+(i.flashKind&&(i.expiresAt||i.flashTs+xm)>e?1:0)).join("|"),i=Pc(this._virtualLights);if(this._capturedSnapshotSequence===this._hassSequence&&this._capturedSnapshotDevices===this._devices&&this._capturedSnapshotLayout===this._layout&&this._capturedSnapshotConfigEpoch===this._cfgEpoch&&this._capturedSnapshotVirtual===i&&this._capturedSnapshotActivity===t)return;const n=this._planHass,r=new Map,o=new Map,s=new Set(["sun.sun"]);this._vacFit?.source&&s.add(this._vacFit.source);const a=new Set,l=new Set,c=e=>{if(!e)return;const t=e.indexOf(":");if(t<0)return void s.add(e);const i=e.slice(0,t),n=e.slice(t+1);"device"===i?a.add(n):"entity"===i&&s.add(n)};for(const e of this._model)for(const t of e.rooms)t.area&&l.add(t.area),c(t.settings?.temp_source),c(t.settings?.hum_source);for(const e of this._serverCfg?.spaces||[])for(const t of e.openings||[])for(const e of Vt(t))s.add(e);for(const e of this._serverCfg?.spaces||[])for(const t of e.decor||[])if("text"===t.kind){for(const e of String(t.text||"").matchAll(/\{([^{}\r\n]+)\}/g)){const t=Ti(e[1]);t?.entity&&s.add(t.entity)}t.entity&&s.add(t.entity),o.set(`decor:${e.id}:${t.id}`,Pi(t.text,t,n,e=>!!n.entities?.[e]&&!!n.states?.[e]&&!qh(n,e,Wh(this._markers))))}const h=Rh(n,this._devices,null,this._virtualLights);for(const e of this._devices){for(const t of[!1,!0])r.set(Uu(e.id,t),Wu(n,e,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:t,activityRuntime:this._activityRt.get(e.id),sourceDetails:!1,lightDevices:this._devices,lightSources:h,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion}));if(this._isVacDev(e)){const t=this._vacSource(e,n),i=t?jl(n?.states?.[t]?.attributes):null,r=this._vacRt.get(e.id);o.set(`vacuum:${e.id}`,{source:t,telemetry:i,mapId:i?this._vacMapId(e,i,n):null,runtime:r?{trail:r.trail,lastTs:r.lastTs,moving:r.moving,jump:r.jump}:null,server:this._vacSrvTrails[e.id]||null})}}const d=ju({sourceSequence:this._hassSequence,hass:n,devices:this._devices,presentations:r,positions:(u=this._model.length>0,p=this._devices,m=e=>this._livePos(e),u?new Map(p.map(e=>[e.id,m(e)])):new Map),facts:o,entityIds:s,deviceIds:a,areaIds:l});var u,p,m;this._capturedSnapshotSequence=this._hassSequence,this._capturedSnapshotDevices=this._devices,this._capturedSnapshotLayout=this._layout,this._capturedSnapshotActivity=t,this._capturedSnapshotConfigEpoch=this._cfgEpoch,this._capturedSnapshotVirtual=i,this._visibleDeviceSnapshot&&"steady"!==this._continuity.state?this._candidateDeviceSnapshot=d:(this._visibleDeviceSnapshot=d,this._candidateDeviceSnapshot=null)}get _renderDeviceSnapshot(){return this._stagedDeviceSnapshotToken===this._continuity.token?this._candidateDeviceSnapshot||this._visibleDeviceSnapshot:this._visibleDeviceSnapshot||this._candidateDeviceSnapshot}get _renderPlanHass(){return this._renderDeviceSnapshot?.hass||this._planHass}get _renderDevices(){return this._renderDeviceSnapshot?.devices||this._devices}get _fullRegistryHass(){return this._planHass,this._planHassMemo?.full||this.hass}_bindingStatus(e){return ih(this.hass,e,this._haRegistry)}houseplanDiagnostics(){const e=function(e){const t=Kc(e);return{access:t.access,authoritative:t.authoritative,revision:t.revision,lastSuccess:t.lastSuccess,error:t.error}}(this.hass),t={active:0,ha_disabled:0,orphaned:0,unverified:0};for(const e of this._markers)e.removed||"virtual"===e.binding||t[this._bindingStatus(e.binding).kind]++;return{registry:{...e,lastSuccessAgeMs:e.lastSuccess?Math.max(0,Date.now()-e.lastSuccess):null},bindings:t}}_openBindingInHa(e){const[t,i]=e.split(":");if(i)if("device"!==t){if("entity"===t){const e=this._fullRegistryHass.entities?.[i];e?.device_id&&pf("/config/devices/device/"+encodeURIComponent(e.device_id))}}else pf("/config/devices/device/"+encodeURIComponent(i))}_bindingHasHaPage(e){const[t,i]=e.split(":");return!!i&&("device"===t||"entity"===t&&!!this._fullRegistryHass.entities?.[i]?.device_id)}_toggleMarkerDialogVisibility(){const e=this._markerDialog;if(!e)return;const t="ha"===e.bindingMode?this._bindingStatus(e.binding):null,i=e.hideFromPlan||"ha_disabled"===t?.kind;i&&"ha_disabled"===t?.kind?this._showToast(this._t("entity"===t.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device")):i&&"unverified"===t?.kind?this._showToast(this._t("toast.ha_binding_unverified")):this._markerDialog={...e,hideFromPlan:!i}}_hookConnection(){const e=this.hass?.connection;e&&e!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),e.addEventListener?.("ready",this._onConnReady),e.addEventListener?.("disconnected",this._onConnLost),e.addEventListener?.("reconnect-error",this._onConnLost),this._connHooked=e)}_display(e){return this._signer.display(this.hass,e)}_resign(){this._signer.resign(this.hass,mn(this._serverCfg))}_onLayoutEvent(e){e<=this._layoutRev||(clearTimeout(this._layoutSyncTimer),this._layoutSyncTimer=window.setTimeout(()=>{e<=this._layoutRev||this._reloadLayoutOnly()},200))}_noteLayoutRev(e){const t=e?.rev;"number"==typeof t&&t>this._layoutRev&&(this._layoutRev=t)}async _reloadLayoutOnly(){if(!this._serverStorage||!this.hass?.callWS)return;this._beginContinuityCandidate("layout-reload",!1);const e=new Map;for(const t of this._dirtyPos)this._layout[t]&&e.set(t,this._layout[t]);this._persistLayout.pending()&&this._persistLayout.flush();for(const[t,i]of this._sentPos)e.set(t,i);try{const t=await this.hass.callWS({type:"houseplan/layout/get"}),i={...t?.layout||{}};for(const[t,n]of e)i[t]=n;const n=Rl(i);n!==(this._layoutContentFingerprint||Rl(this._layout))&&(this._layout=i,this._layoutContentFingerprint=n),this._layoutRev=t?.rev??this._layoutRev,this._canOptimizeUndo=!!t?.can_optimize_undo,this._haIntegrationVersion="string"==typeof t?.integration_version?t.integration_version:this._haIntegrationVersion,this._undoKind=t?.undo_kind||null,this._cacheSnapshot(),this.requestUpdate()}catch{}finally{this._continuityDataReady=!0,this.requestUpdate()}}_maybeRebuildDevices(){const e=this.hass;if(!e?.devices||!e?.entities||!e?.areas)return;const t=this._haRegistry,i=Xc(e,t)+":"+Object.keys(e.areas).length+":"+(this._norm?"n":"l")+":"+eu(e,this._config?.language);if(i===this._regSignature&&this._devices.length)return;this._regSignature=i;const n=new Map(this._devices.map(e=>[e.id,e.bindingStatus?.kind||"active"]));this._devices=Uh({hass:e,registry:t,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||"",loc:e=>this._t(e),iconRules:this._iconRules});const r=this._markers.filter(e=>!e.removed&&"virtual"!==e.binding).map(e=>e.binding).sort(),o=t.revision+":"+r.join("|");if(t.authoritative&&o!==this._haBindingCacheKey){const i=new Map;for(const n of r)i.set(n,ih(e,n,t));!function(e){if("undefined"==typeof localStorage||!e.size)return;const t=th(),i=Date.now();for(const[n,r]of e)"active"===r.kind?t[n]={kind:"active",ts:i}:"ha_disabled"===r.kind&&(t[n]={kind:"ha_disabled",reason:r.reason,ts:i});const n=Object.fromEntries(Object.entries(t).sort((e,t)=>t[1].ts-e[1].ts).slice(0,1500));Wc=n;try{localStorage.setItem(Lc,JSON.stringify(n))}catch{}}(i),this._haBindingCacheKey=o}this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),this._syncActivityRuntime();const s=new Set(this._devices.filter(e=>!e.hidden).map(e=>e.id));for(const e of this._vacRt.keys())s.has(e)||this._vacRt.delete(e);if(this._infoCard){const e=this._devices.find(e=>e.id===this._infoCard.id);this._infoCard=e&&"ha_disabled"!==e.bindingStatus?.kind?e:null}const a=this._devices.some(e=>"ha_disabled"===e.bindingStatus?.kind&&"ha_disabled"!==n.get(e.id));a&&("ha_disabled"!==this._infoCard?.bindingStatus?.kind&&"ha_disabled"!==this._devices.find(e=>e.id===this._infoCard?.id)?.bindingStatus?.kind||this._closeInfoCard(),this._drag&&"ha_disabled"===this._devices.find(e=>e.id===this._drag.id)?.bindingStatus?.kind&&(this._drag=null),clearTimeout(this._holdTimer),this._holdFired=!1,this._tip=null,this._tapConfirm=null),this._nativeMoreInfoEntity&&!this._planEntityAvailable(this._nativeMoreInfoEntity)&&(uf(this,"hass-more-info",{entityId:null}),this._nativeMoreInfoEntity=null)}_syncNewDevices(){if(!this._norm||!this._loadOk||!this._serverCfg)return;const e=this._devices.filter(e=>!e.marker&&!e.virtual).map(e=>e.id).sort(),t=e.join(",");if(t===this._newSyncKey)return;this._newSyncKey=t;const i=this._settings,{fresh:n,known:r}=function(e,t){if(!Array.isArray(t))return{fresh:[],known:[...e]};const i=new Set(t),n=e.filter(e=>!i.has(e));return{fresh:n,known:n.length?[...t,...n]:t}}(e,i.known_devices);if(!Array.isArray(i.known_devices)||n.length){const e=[...new Set([...i.new_device_ids||[],...n])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:r,new_device_ids:e}},this._saveConfig()}}get _newIds(){const e=this._settings.new_device_ids;return new Set(Array.isArray(e)?e:[])}_ackNewDevice(e){if(!this._newIds.has(e)||!this._serverCfg)return;const t=this._settings;this._serverCfg={...this._serverCfg,settings:{...t,new_device_ids:(t.new_device_ids||[]).filter(t=>t!==e)}},this._saveConfig(),this.requestUpdate()}get _markers(){return this._serverCfg?.markers||[]}_roomLqi(e){if(!e)return null;const t=[];for(const i of this._renderDevices){if(i.area!==e||i.virtual)continue;const n=Dh(this._renderPlanHass,i.entities);null!=n&&t.push(n)}return _i(t)}_roomBounds(e){if(e.poly&&e.poly.length){const t=e.poly.map(e=>e[0]),i=e.poly.map(e=>e[1]),n=Math.min(...t),r=Math.min(...i);return{x:n,y:r,w:Math.max(...t)-n,h:Math.max(...i)-r}}return{x:e.x??0,y:e.y??0,w:e.w??0,h:e.h??0}}_defaultPositions(){const e={},t=this._config?.icon_size??2.5;for(const i of this._model){const n=t/100*Ya(i)*1.3;for(const t of i.rooms){if(!t.area)continue;const r=this._devices.filter(e=>e.area===t.area&&e.space===i.id);if(!r.length)continue;const o=this._roomBounds(t),s=.1*Math.min(o.w,o.h),a=o.w-2*s,l=o.h-2*s,c=Math.max(1,Math.round(Math.sqrt(r.length*a/Math.max(l,1)))),h=Math.ceil(r.length/c),d=a/c,u=l/Math.max(h,1),p=r.map((e,t)=>({x:o.x+s+d*(t%c+.5),y:o.y+s+u*(Math.floor(t/c)+.5)}));gi(p,o,n,.5*s),r.forEach((t,i)=>e[t.id]=za(p[i]))}}return e}_pos(e){const t=this._renderDeviceSnapshot?.positions.get(e.id);return t?{x:t.x,y:t.y}:this._livePos(e)}_livePos(e){const t=this._spaceModelById(e.space),i=this._layout[e.id];if(i)if(this._norm){if(i.s===e.space)return{x:i.x*cf,y:i.y*cf}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[e.id]?this._defPos[e.id]:t?za(Ka(t)):{x:500,y:500}}_savePos(e,t,i){if(this._spaceModelById(e.space)){if(this._norm){const n=this._gridPitch,r=Math.round(t/n)*n,o=Math.round(i/n)*n,s=this._layout[e.id]?.k;this._layout={...this._layout,[e.id]:{s:e.space,x:Pa(r/cf),y:Pa(o/cf),...s?{k:s}:{}}}}else this._layout={...this._layout,[e.id]:{x:Math.round(t),y:Math.round(i)}};this._dirtyPos.add(e.id),this._persistLayout()}}_visualSamples(e){return Iu(this._renderPlanHass,e,this._renderDevices,void 0,this._fullRegistryHass).samples}_devicePresentation(e,t=!1!==this._config?.show_signal,i=!1){const n=i?null:this._renderDeviceSnapshot?.presentations.get(Uu(e.id,t));return n||Wu(this._renderPlanHass,e,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:t,designPreview:i,activityRuntime:this._activityRt.get(e.id),sourceDetails:!1,lightDevices:this._renderDevices,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion})}_deviceVisual(e){return this._devicePresentation(e).visual}_stateClass(e,t=this._deviceVisual(e)){const i=this._devicePresentation(e);return i.effectiveHidden?"":("icon_ripple"!==i.display||!1===this._config?.live_states||"alarm"===t.status||t.activity,Lu({...i,visual:t}).join(" "))}_liveTemp(e){if(!this._config?.show_temperature)return null;if(!0===e.marker?.use_climate_temp){const t=Ah(this._renderPlanHass,e.entities);if(null!=t)return t}return"mdi:thermometer"!==e.icon&&"mdi:air-filter"!==e.icon?null:zh(this._renderPlanHass,e.entities)}_bindingEntities(e){if("virtual"===e||!e)return[];const t=this._bindingStatus(e);return"active"===t.kind?t.enabledEntityIds:t.allEntityIds}_bindingHasClimate(e){return this._bindingEntities(e).some(e=>e.startsWith("climate."))}_bindingHasAlarm(e){return this._bindingEntities(e).some(e=>{const t=this._planHass?.states?.[e]||this.hass?.states?.[e],i=this._fullRegistryHass.entities[e]||this.hass?.entities?.[e];return Sn(e.split(".")[0],t?.attributes?.device_class||i?.device_class||i?.original_device_class)})}_liveHum(e){return this._config?.show_temperature&&e.primary&&Ph(this.hass,e.primary)?Oh(this.hass,e.entities):null}_deviceBindingActive(e,t=!0){if(e.virtual||"virtual"===e.bindingKind)return!0;if(!e.bindingKind||!e.bindingRef)return!1;const i=this._bindingStatus(`${e.bindingKind}:${e.bindingRef}`);return"active"===i.kind||(t&&this._showToast(this._t("ha_disabled"===i.kind?"toast.ha_disabled_action":"toast.ha_binding_unverified")),!1)}_openMoreInfo(e){e?this._planEntityAvailable(e)?(this._nativeMoreInfoEntity=e,uf(this,"hass-more-info",{entityId:e})):this._showToast(this._t("toast.ha_disabled_action")):this._showToast(this._t("toast.no_entity"))}_interruptViewGesture(e,t){if(clearTimeout(this._holdTimer),clearTimeout(this._kioskHoldTimer),void 0!==e)for(const i of[t,this._stageEl])try{i?.hasPointerCapture?.(e)&&i.releasePointerCapture(e)}catch{}this._pointers.clear(),this._panStart=null,this._panLock=null,this._pinchStart=null,this._swipeStart=null}_closeInfoCard(){this._interruptViewGesture(),this._holdFired=!1,this._infoCard=null}_ctxDevice(e,t){"view"===this._mode&&(e.preventDefault(),e.stopPropagation(),this._deviceBindingActive(t)&&(t.primary?this._openMoreInfo(t.primary):this._infoCard=t))}_clickDevice(e,t){if("view"!==this._mode&&"devices"!==this._mode)return;if(e.stopPropagation(),this._drag?.moved||this._suppressClick||this._holdFired)return;if("devices"===this._mode)return void this._openMarkerDialog(t);const i=this._devices.find(e=>e.id===t.id);if(!i)return;const n=gd(i.tapAction,i.primary?.split(".")[0]),r=(e,t)=>{i.marker?.tap_confirm?this._tapConfirm={kind:"run",text:e,exec:t}:t()};if("toggle"===n){const e=this._toggleIntent(i);if(!e)return;if(!xd(e))return void this._showUnavailableToggleTargets(e);const t=e=>{const t=xd(e);if(!t)return;if("virtual-light"===t.kind)return this._startDevicePressFeedback(i.id),void this.hass.callWS({type:"houseplan/virtual_light/toggle",marker_id:t.markerId}).then(e=>{const t=Ic(this._virtualLights,e);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}).catch(e=>this._showToast(this._t("toast.virtual_light_toggle_failed",{err:this._errText(e)})));const{command:n}=t;this._startDevicePressFeedback(i.id),this.hass.callService(n.domain,n.service,n.data).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))},n=((o=e).targets.length?o.targets:o.skippedTargets).map(e=>e.name||e.entityId||e.ref).join(", ")||i.name;if(i.marker?.tap_confirm){const r=this._toggleConfirmationLines(e);if(!r.length)return;this._tapConfirm={kind:"toggle",text:this._t("confirm.tap_toggle",{name:n}),lines:r,initialIntent:e,deviceId:i.id,exec:()=>{const n=this._devices.find(e=>e.id===i.id),r=n?this._toggleIntent(n):null;r&&!xd(r)&&this._showUnavailableToggleTargets(r)||(r&&Sd(e,r)?t(r):this._showToast(this._t("toast.tap_target_changed")))}}}else t(e);return}var o;if("info"===n)return this._interruptViewGesture(),void(this._infoCard=i);if(this._deviceBindingActive(i)){if("run"===n){const e=i.marker?.tap_target||"",t=function(e){const t=String(e||"").split(".")[0];return"automation"===t?{domain:"automation",service:"trigger"}:"script"===t?{domain:"script",service:"turn_on"}:"scene"===t?{domain:"scene",service:"turn_on"}:null}(e),n=this.hass.states[e];if(!t||!n)return void this._showToast(this._t("toast.run_target_missing"));const o=n.attributes?.friendly_name||e;return void r(this._t("confirm.tap_run",{name:o}),()=>{this._deviceBindingActive(i)&&this._planEntityAvailable(e)&&(this._startDevicePressFeedback(i.id),this.hass.callService(t.domain,t.service,{entity_id:e}).then(()=>{this._stampActivity(i.id,"event",this._activitySourceKey(i)),this.requestUpdate(),this._showToast(this._t("toast.run_started",{name:o}))}).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)}))))})}"more-info"===n&&i.primary?this._openMoreInfo(i.primary):this._infoCard=i}}_showUnavailableToggleTargets(e){const t=function(e){if(!e||"group"!==e.kind||xd(e)||"configured-targets-missing"!==e.noneReason)return[];if(!e.skippedTargets.length||e.skippedTargets.some(e=>!$d.has(e.reason)&&"secure"!==e.reason))return[];const t=[],i=new Set;for(const n of e.skippedTargets){if(!$d.has(n.reason))continue;const e=String(n.name||n.entityId||n.ref||"").trim();e&&!i.has(e)&&(i.add(e),t.push(e))}return t}(e);return!!t.length&&(this._showToast(1===t.length?this._t("toast.toggle_target_unavailable",{name:t[0]}):this._t("toast.toggle_targets_unavailable",{names:t.join(", ")})),!0)}_keyDevice(e,t){"Enter"!==e.key&&" "!==e.key||"view"!==this._mode&&"devices"!==this._mode||(e.preventDefault(),this._clickDevice(e,t))}_startDevicePressFeedback(e){const t=[...this.renderRoot.querySelectorAll(".dev[data-id]")].find(t=>t.dataset.id===e),i=t?.querySelector(".device-shell");if(!i||"function"!=typeof i.animate)return;const n=Number.parseFloat(this.ownerDocument.defaultView?.getComputedStyle(i).scale||""),r=Number.isFinite(n)?Math.max(.95,Math.min(1,n)):1;this._devicePressAnimations.get(e)?.cancel();const o=this._reducedMotion?[{outlineColor:"transparent",outlineStyle:"solid",outlineWidth:"0px"},{outlineColor:"var(--hp-accent, #3ea6ff)",outlineStyle:"solid",outlineWidth:"2px",outlineOffset:"2px",offset:.5},{outlineColor:"transparent",outlineStyle:"solid",outlineWidth:"0px"}]:[{scale:String(r)},{scale:"0.95",offset:.5},{scale:"1"}],s=i.animate(o,{duration:200,easing:"cubic-bezier(.22,.61,.36,1)"});this._devicePressAnimations.set(e,s);const a=()=>{this._devicePressAnimations.get(e)===s&&this._devicePressAnimations.delete(e)};s.addEventListener("finish",a,{once:!0}),s.addEventListener("cancel",a,{once:!0})}_cancelDevicePressFeedback(){for(const e of this._devicePressAnimations.values())e.cancel();this._devicePressAnimations.clear()}_pruneDevicePressFeedback(){if(!this._devicePressAnimations.size)return;const e=new Set([...this.renderRoot.querySelectorAll(".dev[data-id]")].map(e=>e.dataset.id||""));for(const[t,i]of this._devicePressAnimations)e.has(t)||(i.cancel(),this._devicePressAnimations.delete(t))}_t(e,t){return tu(eu(this.hass,this._config?.language),e,t)}get _colorPickerLabels(){return{title:this._t("color_picker.title"),hue:this._t("color_picker.hue"),saturation:this._t("color_picker.saturation"),value:this._t("color_picker.value"),hex:this._t("color_picker.hex"),invalidHex:this._t("color_picker.invalid_hex")}}_help(e){return this._editorRuntimeOrThrow()._help(e)}get _stageEl(){return this.renderRoot.querySelector(".stage")}_contentItems(e){const t=[];for(const i of this._devices){if(i.space!==e.id||i.hidden)continue;const n=this._pos(i);t.push({minX:n.x,minY:n.y,maxX:n.x,maxY:n.y})}if(e.id===this._space){for(const e of this._openingsR){const i=Number(e.angle)*Math.PI/180,n=Math.cos(i)*e.rlen/2,r=Math.sin(i)*e.rlen/2,o=Ea([[e.rx-n,e.ry-r],[e.rx+n,e.ry+r]]);o&&t.push(o)}const i=this._decorH;for(const e of this._decorList){const n=Ea("line"===e.kind?[[e.x1*cf,e.y1*i],[e.x2*cf,e.y2*i]]:"text"===e.kind?[[e.x*cf,e.y*i]]:dr({x:e.x*cf,y:e.y*i,w:e.w*cf,h:e.h*i,angle:e.angle}));n&&t.push(n)}for(const i of this._physicalBodiesR(e)){const e=Ea(i);e&&t.push(e)}}return Wa(e,t)}_frameOf(){const e=this._spaceModel();if(!e){this._frame=null;const e={x:0,y:0,w:cf,h:cf};return{rect:e,all:e,outliers:0}}const t=this._frame,i="view"!==this._mode;if(t&&t.id===e.id&&this._bdDrag)return t;if(t&&t.id===e.id&&t.model===e&&t.layout===this._layout&&t.devs===this._devices&&t.far===this._showFar&&t.grow===i)return t;const n=Ua(this._contentItems(e));let r=n.all||Ga(e),o=this._showFar?r:n.core||Ga(e);return t&&t.id===e.id&&i&&t.grow&&(o=hf(t.rect,o),r=hf(t.all,r)),this._frame={id:e.id,model:e,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:r,outliers:n.outliers},this._frame}_isoSource(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._openCuts(),n=this._openingsR.map((e,t)=>({id:String(e.id||t),sourceIndex:t,type:e.type,x:e.rx,y:e.ry,angle:Number(e.angle)||0,length:e.rlen>0?e.rlen:900,flipH:!!e.flip_h,flipV:!!e.flip_v})),r=ma(64,this._cellCm),o=ma(10,this._cellCm),s=`${e.id}|${function(e){let t=2166136261;const i=e=>{if("number"!=typeof e)if("string"!=typeof e)if("boolean"!=typeof e)if(Array.isArray(e)){t=C_(t,e.length);for(const t of e)i(t)}else{if(e&&"object"==typeof e){const t=e;for(const e of Object.keys(t).sort())i(e),i(t[e]);return}t=C_(t,-1)}else t=C_(t,e?1:0);else for(const i of e)t=C_(t,i.codePointAt(0)||0);else t=C_(t,e)};return i(e),t.toString(36)}({rooms:e.rooms,walls:t,openCuts:i,openings:n,partitions:e.partitions,roomDrafts:e.room_drafts,columns:e.wall_columns,cellCm:this._cellCm,gridPitch:this._gridPitch,wallKeyPitch:this._wallKeyPitch,camera:g_,wallHeight:r,floorEdgeHeight:o,algorithm:3})}`;return{key:s,build:()=>{const o=ea(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e)).all,s=this._roomWallOpeningInputs(this._openingsR,e),a=t.length||o.length?Ts(e.rooms,t,i,s,this._wallKeyPitch,this._cellCm,this._gridPitch,cf,o):null;if(a&&("failed-core"===a.status||"not-applicable"===a.status))throw new Error("wall boolean geometry failed");const l=a?.paperGeom??$s(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,cf);if(!l)throw new Error("floor boolean geometry failed");const c=a?.openingIndex||Ps(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,cf),h=n.map(e=>{const i=this._openingsR[e.sourceIndex],n="gate"===e.type?!e.flipV:e.flipV,o=i?.partitionHost?Od(i.partitionHost,n):t.length||"gate"===e.type?Es(c,{x:e.x,y:e.y,angle:e.angle,length:e.length,flip_v:n}):{ox:0,oy:0,cm:0,side:-1};return function(e,t=64){if(!(D_([e.x,e.y,e.angle,e.length,e.face.ox,e.face.oy,t])&&e.length>0&&t>0))throw new Error("invalid isometric opening input");const i=e.length/2;let n;if("passage"===e.type)n=[];else if("gate"===e.type){const r=10*e.face.side;n=[A_(e,0,[-i,0],[i,0],r,0,.88*t),A_(e,1,[i,0],[-i,0],-r,0,.88*t)]}else n="window"===e.type?[A_(e,0,[-i,0],[i,0],-90,.27*t,.78*t),A_(e,1,[i,0],[-i,0],90,.27*t,.78*t)]:[A_(e,0,[-i,0],[e.length,0],-90,0,.92*t)];return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaves:Object.freeze(n.map(e=>Object.freeze(e)))}}({...e,face:o},r)});return{walls:a?.components.flatMap(e=>e.geom)||[],floor:l,openings:Object.freeze(h)}}}}_isoSceneKey(){if(!this._labsIso||"view"!==this._mode)return null;try{return this._isoSource()?.key??null}catch{return`${this._space}|invalid`}}_isoScene(){const e=this._isoSource();if(!e)return null;const t=this._isoGeometryCache.get(e.key);if(t)return{key:e.key,...t};const i=this._frameOf().rect,n=e.build(),r=ma(64,this._cellCm),o=ma(10,this._cellCm),s=function(e){let t=1/0,i=1/0,n=-1/0,r=-1/0;for(const o of e)for(const e of o.leaves){const o=Math.hypot(e.closedVector[0],e.closedVector[1]);t=Math.min(t,e.hinge[0]-o),i=Math.min(i,e.hinge[1]-o),n=Math.max(n,e.hinge[0]+o),r=Math.max(r,e.hinge[1]+o)}return D_([t,i,n,r])?{x:t,y:i,w:n-t,h:r-i}:null}(n.openings),a=k_({rect:s?hf(i,s):i,wallHeight:r,openingHeight:r,floorDepth:o}),l=function(e,t=g_,i=64){if(!Number.isFinite(i)||i<0)throw new Error("invalid wall height");const n=[],r=[],o=[];let s=0;for(let a=0;a<(e||[]).length;a++){const l=e[a];for(let e=0;e<(l||[]).length;e++){const c=S_(l[e],e>0);if(!(c.length<3||Math.abs($_(c))<1e-9)){n.push(M_(c,t,i)),s+=c.length;for(let n=0;ne.depth-t.depth||e.polygon-t.polygon||e.ring-t.ring||e.edge-t.edge),{topPath:n.join(" "),sides:r,contactPath:o.join(" "),edgeCount:s}}(n.walls,g_,r),c={geometry:l,floor:T_(n.floor,o),openings:n.openings,frame:a};return X_(this._isoGeometryCache,e.key,c,8),{key:e.key,...c}}_effectiveProjection(){if("iso"!==this._desiredProjection||!this._model.length)return"flat";const e=this._isoSceneKey()||`${this._space}|invalid`;if(this._isoFallback.has(e))return"flat";try{const e=this._isoScene();return e?(t="iso",i=e.key,n=this._isoFallback,"iso"===t&&i&&!n.has(i)?"iso":"flat"):"flat"}catch(t){if(!this._isoFallback.has(e)){this._isoFallback.add(e);const i=e.split("|");console.warn(`HOUSEPLAN ISO FALLBACK: #89, space ${this._space}, fingerprint ${i[i.length-1]}, ${t instanceof Error?t.message:"renderer error"}`)}return"flat"}var t,i,n}_scenePoint(e){return"iso"===this._renderProjection?y_(e,0):e}_floorView(e){if("iso"!==this._renderProjection)return e;const t=b_([e.x,e.y]),i=b_([e.x+e.w,e.y+e.h]);return{x:t[0],y:t[1],w:i[0]-t[0],h:i[1]-t[1]}}_baseVb(){if("iso"===this._effectiveProjection()){if(!this._spaceDisplayForRender().showBorders){const e=k_({rect:this._frameOf().rect,wallHeight:ma(64,this._cellCm)});return[e.x,e.y,e.w,e.h]}const e=this._isoScene()?.frame??this._frameOf().rect;return[e.x,e.y,e.w,e.h]}const e=this._frameOf().rect;return[e.x,e.y,e.w,e.h]}get _outliers(){return this._showFar?0:this._frameOf().outliers}_fitFar(){this._showFar=!0,this._frame=null,this._resetZoom()}_fitAll(){this._showFar=!0,this._frame=null,this._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?V:B`
+
`:V}houseplanContinuityTrace(){return[...this._continuityHistory,...this._continuity.trace].slice(-80).map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}getCardSize(){return 12}get _norm(){return!(!this._serverCfg||!this._serverCfg.spaces.length)}_cfgFingerprint(){const e=this._serverCfg?.spaces||[];let t=e.length+":";for(const i of e){t+=(i.id||"")+","+(i.plan_aspect||"")+","+(i.plan_url||"").length+","+(i.plan_x??"")+","+(i.plan_y??"")+","+(i.plan_scale??"")+","+(i.plan_scale_x??"")+","+(i.plan_scale_y??"")+","+(i.plan_angle??"")+","+(i.rooms?.length||0)+","+(i.openings?.length||0)+","+(i.decor?.length||0)+";";for(const e of i.rooms||[]){const i=e.poly?.[0],n=e.poly?.[e.poly.length-1];t+=(e.poly?.length||0)+"."+(e.id||"")+"."+(e.open_to||[]).join("+")+"."+(e.area||"")+"."+JSON.stringify(e.settings||0)+"."+(e.x??"")+","+(e.y??"")+","+(e.w??"")+","+(e.h??"")+","+(i?i[0]+"/"+i[1]:"")+","+(n?n[0]+"/"+n[1]:"")+";"}}return t}get _model(){if(!this._serverCfg)return[];const e=this._cfgEpoch+"|"+this._cfgFingerprint();if(this._modelCache&&this._modelCache.key===e)return this._modelCache.model;const t=this._buildModel();return this._modelCache={key:e,model:t},t}_buildModel(){if(!this._serverCfg)return[];const e=this._renderCfg;return $a(e).map((t,i)=>{const n=e.spaces[i]?.plan_url;return t.bg&&n?{...t,bg:{...t.bg,href:n}}:t})}_spaceModel(){return this._hasFixedFloor?zl(this._model,this._space):(e=this._model,t=this._space,e.find(e=>e.id===t)??e[0]);var e,t}_spaceModelById(e){return zl(this._model,e)}_syncEmptySpaceState(){if(!!this._serverCfg&&0===this._serverCfg.spaces.length){if(!this._emptySpaceStateActive){this._emptySpaceStateActive=!0;for(const e of this._pointers.keys())for(const t of this.renderRoot.querySelectorAll("*"))try{t.hasPointerCapture?.(e)&&t.releasePointerCapture(e)}catch{}this._pointers.clear(),this._panStart=null,this._panLock=null,this._pinchStart=null,this._swipeStart=null,this._drag=null,this._rlResize=null,this._vacFit=null,this._compassDrag=!1,this._cancelModeTransition(!1),this._mode="view",this._clearGeometryGesture(),this._geometryHistory.clear(),this._resumeDraftBySpace={},this._tip=null,this._hoverRoom=null,this._openingInfo=null,this._closeInfoCard(),this._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._physicalDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._roomDialog=!1,"edit"===this._spaceDialog?.mode&&(this._spaceDialog=null),this._editorSecondary?.closeForNavigation(),this._saveConfigDebounced.cancel(),this._frame=null,this._planSnapGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._decorSnapCache=null,this._commitSpace("",!0)}}else this._emptySpaceStateActive=!1}get _areaToSpace(){const e={};for(const t of this._model)for(const i of t.rooms)i.area&&(e[i.area]={space:t.id,room:i});return e}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._showHidden||!this._settings.filter_seeded&&!!this._settings.show_all}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const e=this._serverCfg,t=function(e){const t=e.hass,i=e.registry||Kc(t),n=Jc(t,i),r=Qc(t,i),{areaToSpace:o,markers:s,settings:a,excluded:l,iconRules:c}=e,h=!1!==a.group_lights,d=Wh(s),u=Fh(n,h).filter(e=>!qh(n,e.eid,d)),p=new Set(u.map(e=>e.area)),m=nh(n),_=rh(s,r),f=new Set(s.map(e=>e.binding)),g=[];for(const e of Object.values(n.devices)){const r=e.area_id;if(!r||!o[r])continue;if("service"===e.entry_type)continue;if(f.has("device:"+e.id))continue;if("active"!==ih(t,"device:"+e.id,i).kind)continue;const s=oh(n,e.id,m[e.id]||[],_);if(s.partial&&!s.entityIds.length)continue;const a=s.entityIds,d=sh(n,e,a);let u=l.has(d)||"Group"===e.model||/scene/i.test(e.model||"")||/bridge/i.test((e.model||"")+(e.name||""))||"myheat"===d&&!!e.via_device_id;if(!u&&h&&p.has(r)){const t=(e.name_by_user||e.name||"").trim();"mdi:lightbulb"===Ih(n,t,e.model,a,c)&&(u=!0)}u&&g.push("device:"+e.id)}return g}({hass:this.hass,registry:this._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,firstSpaceId:this._model[0]?.id||"",iconRules:this._iconRules});if(!t.length&&e.settings?.filter_seeded)return;e.markers=e.markers||[];const i=[];for(const n of t){const t="h"+n.slice(n.indexOf(":")+1);e.markers.push({id:t,binding:n,hidden:!0}),i.push(n.slice(n.indexOf(":")+1))}const n={...e.settings||{},filter_seeded:!0};delete n.show_all,i.length&&Array.isArray(n.new_device_ids)&&(n.new_device_ids=n.new_device_ids.filter(e=>!i.includes(e))),e.settings=n,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}get _iconRules(){const e=this._settings.icon_rules;if(!e||!Array.isArray(e)||!e.length)return;const t=JSON.stringify(e);return t!==this._rulesCompiledSrc&&(this._rulesCompiledSrc=t,this._rulesCompiled=ge(e)),this._rulesCompiled}get _fillColors(){return Gi(this._settings)}get _excluded(){const e=this._settings.exclude_integrations;return e?new Set(e):_e}willUpdate(e){e.has("_serverCfg")&&this._cfgEpoch++,this._syncEmptySpaceState(),e.has("hass")&&this.hass&&(this._hassSequence++,this._renderSnapshotAt=Date.now(),this._continuity.note("hass-snapshot"),this._ensureHaRegistryAuthority(),this._planHassMemo=null,this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices(),this._vacTick(),this._activityTick()),this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._continuity.refreshCompleteFrame(this._visualFrameFingerprint()),this._captureRenderDeviceSnapshot()}updated(){this._pruneDevicePressFeedback(),this._syncDayCycleClock(),this._warmSnapshot(),this._editorRuntime&&this._dtMeasure();const e=this._stageEl;e&&!this._roViewport&&(this._roViewport=new ResizeObserver(()=>this._refitView()),this._roViewport.observe(e)),e&&this._booting&&!this._bootTimer&&this._bootWatch();const t=this.renderRoot.querySelector(".hdr");if(t&&e&&!this._roHdr){const i=()=>{const t=this.renderRoot.querySelector("ha-card");if(!t)return;const i=e.getBoundingClientRect().top-t.getBoundingClientRect().top,n=Math.min(Math.max(t.getBoundingClientRect().top,0),120),r=Math.round(i+n);r>=0&&r!==this._hdrH&&(this._hdrH=r),r>=0&&!this._booting&&!this._config?.kiosk&&e.clientHeight>0&&this._warmPatch({hdrH:r,stageH:e.clientHeight})};this._roHdr=new ResizeObserver(()=>requestAnimationFrame(i)),this._roHdr.observe(t),this._onWinResize=()=>requestAnimationFrame(i),window.addEventListener("resize",this._onWinResize),i()}if(e&&!this._view&&this._refitView(),this._editorSecondary?.afterRender(),this._settleContinuityFrame(),this._serverStorage&&this._loadOk&&0===this._model.length&&!this._spaceDialog&&!this._importDialog&&!this._onboardingShown){this._onboardingShown=!0;const e=function(e){const t=e?.floors;if(!t||"object"!=typeof t)return[];const i=[];for(const e of Object.values(t))e&&e.floor_id&&i.push({id:e.floor_id,name:e.name||e.floor_id,level:e.level??null});return i.sort((e,t)=>{const i=e.level??1e9,n=t.level??1e9;return i!==n?i-n:e.name.localeCompare(t.name)}),i}(this.hass);e.length?this._importDialog={floors:e.map(e=>({...e,checked:!0}))}:this._openSpaceDialog("create")}}_adoptStructuralResponses(e,t,i){const n=e?.config,r=n&&Array.isArray(n.spaces)?n:null,o=Rl(r),s=o!==(this._cfgContentFingerprint||Rl(this._serverCfg));if(s&&(this._geometryHistory.clear(),this._pendingPhysicalWrites.clear(),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=r,this._seedDecorStyle(this._serverCfg),this._cfgContentFingerprint=o),this._cfgRev=e?.rev??this._cfgRev,e&&("virtual_lights"in e||"config"in e)){const t=zc(this._virtualLights,e.virtual_lights,this._cfgRev,"virtual_lights"in e);Pc(t)!==Pc(this._virtualLights)&&(this._virtualLights=t,this._capturedSnapshotVirtual="")}let a=!1;if(void 0!==t||void 0!==i){const e=i??t?.layout??{},n=Rl(e);a=n!==(this._layoutContentFingerprint||Rl(this._layout)),a&&(this._layout=e,this._layoutContentFingerprint=n),this._layoutRev=t?.rev??this._layoutRev}return this._canOptimizeUndo=!(!e?.can_optimize_undo&&!t?.can_optimize_undo),this._haIntegrationVersion="string"==typeof e?.integration_version?e.integration_version:this._haIntegrationVersion,this._undoKind=e?.undo_kind||t?.undo_kind||null,"boolean"==typeof e?.can_write&&(this._serverCanWrite=e.can_write),s&&this._continuity.note("config-candidate",{configRev:this._cfgRev}),a&&this._continuity.note("layout-candidate",{layoutRev:this._layoutRev}),{configChanged:s,layoutChanged:a}}_resumePendingNavMode(){if(!this._pendingNavMode||!this._canEdit||this._config?.kiosk)return!1;const e=this._pendingNavMode;return this._pendingNavMode=null,this._setMode(e,!1),!0}async _loadFromServer(){this._loading=!0,this._loadTries++;const e=this._space,t=!!this._view;try{const[i,n]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]),r=i?.config&&Array.isArray(i.config.spaces)?i.config:null,o=Rl(r)!==(this._cfgContentFingerprint||Rl(this._serverCfg))||Rl(n?.layout??{})!==(this._layoutContentFingerprint||Rl(this._layout));if(o){if(!await this._signer.prepareImage(this.hass,this._candidateBackdrop(r)))return this._continuity.note("asset-failed"),void this._scheduleLoadRetry(!0)}o&&this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._beginContinuityCandidate("structural-response",!0),this._connectionWasLost=!1,this._serverStorage=!0,"boolean"==typeof i?.can_write&&(this._serverCanWrite=i.can_write),this._canOptimizeUndo=!(!i?.can_optimize_undo&&!n?.can_optimize_undo),this._haIntegrationVersion="string"==typeof i?.integration_version?i.integration_version:this._haIntegrationVersion,this._adoptStructuralResponses(i,n),this._adoptInitialSpace(this._model,!0),this._resumePendingNavMode(),this._cacheSnapshot(),this._warmVpArmed&&this._space===this._warmVp?.space?this._warmVpArmed=!1:t&&this._space===e||this._restoreZoom(),this._loadOk=!0,this.hass.callWS({type:"houseplan/trail/get"}).then(e=>{this._vacSrvTrails=e?.trails||{},this.requestUpdate()}).catch(()=>{}),this._ensureLiveSyncSubscriptions()}catch(e){if(this._serverCfg)this._scheduleLoadRetry(!0);else if(this._loadTries>=8){this._serverStorage=!1;try{this._layout=JSON.parse(localStorage.getItem(nf)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1,this._continuityDataReady=!0,this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}}_ensureLiveSyncSubscriptions(){const e=this.hass?.connection;if(!e)return;if(e!==this._liveSyncConnection&&(this._unsubCfg?.(),this._unsubCfg=null,this._unsubLayout?.(),this._unsubLayout=null,this._unsubTrail?.(),this._unsubTrail=void 0,this._unsubVirtual?.(),this._unsubVirtual=null,this._liveSyncGeneration++,this._liveSyncAttempt=null,this._liveSyncConnection=e),this._liveSyncAttempt)return;const t=this._liveSyncGeneration,i=[],n=(n,r,o,s)=>{n()||i.push(async()=>{const i=await e.subscribeEvents(s,o);t===this._liveSyncGeneration&&this.isConnected&&this.hass?.connection===e&&!n()?r(i):i?.()})};if(n(()=>this._unsubCfg,e=>{this._unsubCfg=e},"houseplan_config_updated",e=>{const t=Number(e?.data?.rev??-1);t!==this._cfgRev&&this._reloadConfigOnly(!1,t)}),n(()=>this._unsubTrail,e=>{this._unsubTrail=e},"houseplan_trail_updated",async()=>{try{const e=await this.hass.callWS({type:"houseplan/trail/get"});this._vacSrvTrails=e?.trails||{},this.requestUpdate()}catch{}}),n(()=>this._unsubLayout,e=>{this._unsubLayout=e},"houseplan_layout_updated",e=>this._onLayoutEvent(Number(e?.data?.rev??-1))),n(()=>this._unsubVirtual,e=>{this._unsubVirtual=e},"houseplan_virtual_light_updated",e=>{const t=Ic(this._virtualLights,e?.data);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}),!i.length)return;const r=function(e){return Promise.allSettled(e.map(e=>Promise.resolve().then(e)))}(i).then(()=>{});this._liveSyncAttempt=r,r.finally(()=>{this._liveSyncAttempt===r&&(this._liveSyncAttempt=null)})}async _reloadConfigOnly(e=!1,t){if(!e){if(void 0!==t&&t<=this._cfgRev)return;if(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(!1,t),400))}this._beginContinuityCandidate("config-reload",!1);try{const e=await this.hass.callWS({type:"houseplan/config/get"}),t=e?.config&&Array.isArray(e.config.spaces)?e.config:null;if(Rl(t)!==(this._cfgContentFingerprint||Rl(this._serverCfg))&&!await this._signer.prepareImage(this.hass,this._candidateBackdrop(t)))return this._continuity.note("asset-failed"),void this._scheduleLoadRetry(!0);const i=this._space;this._adoptStructuralResponses(e),this._adoptInitialSpace(this._model,!0),this._resumePendingNavMode(),this._cacheSnapshot(),this._space!==i&&this._restoreZoom(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}catch(e){this._showToast(this._t("toast.cfg_reload_failed",{err:this._errText(e)}))}finally{this._continuityDataReady=!0,this.requestUpdate()}}_scheduleLoadRetry(e=!1){if(void 0!==this._loadRetryTimer)return;const t=Math.min(8e3,500*2**Math.min(4,Math.max(1,this._loadTries-7)));this._loadRetryTimer=window.setTimeout(()=>{this._loadRetryTimer=void 0,!e&&this._loadOk||this._loading||!this.hass||this._loadFromServer()},t)}_ensureHaRegistryAuthority(){const e=this.hass?.connection||null;e&&e!==this._haRegistryConnection&&(this._haRegistryRelease?.(),this._haRegistryConnection=e,this._haRegistryRev=-1,this._haBindingCacheKey="",this._planHassMemo=null,this._haRegistryRelease=Gc(this.hass,this._onHaRegistryUpdate),this._onHaRegistryUpdate())}get _haRegistry(){return Kc(this.hass)}get _planHass(){const e=this._haRegistry,t=Xc(this.hass,e),i=this._planHassMemo;if(i&&i.hass===this.hass&&i.sig===t)return i.active;const n=Jc(this.hass,e),r=Qc(this.hass,e);return this._planHassMemo={hass:this.hass,sig:t,active:n,full:r},n}_captureRenderDeviceSnapshot(){if(!this.hass)return;const e=Date.now(),t=[...this._activityRt.entries()].map(([t,i])=>`${t}:${i.gen}:${i.flashTs}:`+(i.flashKind&&(i.expiresAt||i.flashTs+$m)>e?1:0)).join("|"),i=Pc(this._virtualLights);if(this._capturedSnapshotSequence===this._hassSequence&&this._capturedSnapshotDevices===this._devices&&this._capturedSnapshotLayout===this._layout&&this._capturedSnapshotConfigEpoch===this._cfgEpoch&&this._capturedSnapshotVirtual===i&&this._capturedSnapshotActivity===t)return;const n=this._planHass,r=new Map,o=new Map,s=new Set(["sun.sun"]);this._vacFit?.source&&s.add(this._vacFit.source);const a=new Set,l=new Set,c=e=>{if(!e)return;const t=e.indexOf(":");if(t<0)return void s.add(e);const i=e.slice(0,t),n=e.slice(t+1);"device"===i?a.add(n):"entity"===i&&s.add(n)};for(const e of this._model)for(const t of e.rooms)t.area&&l.add(t.area),c(t.settings?.temp_source),c(t.settings?.hum_source);for(const e of this._serverCfg?.spaces||[])for(const t of e.openings||[])for(const e of Vt(t))s.add(e);for(const e of this._serverCfg?.spaces||[])for(const t of e.decor||[])if("text"===t.kind){for(const e of String(t.text||"").matchAll(/\{([^{}\r\n]+)\}/g)){const t=Ti(e[1]);t?.entity&&s.add(t.entity)}t.entity&&s.add(t.entity),o.set(`decor:${e.id}:${t.id}`,Pi(t.text,t,n,e=>!!n.entities?.[e]&&!!n.states?.[e]&&!qh(n,e,Wh(this._markers))))}const h=Rh(n,this._devices,null,this._virtualLights);for(const e of this._devices){for(const t of[!1,!0])r.set(Vu(e.id,t),qu(n,e,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:t,activityRuntime:this._activityRt.get(e.id),sourceDetails:!1,lightDevices:this._devices,lightSources:h,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion}));if(this._isVacDev(e)){const t=this._vacSource(e,n),i=t?jl(n?.states?.[t]?.attributes):null,r=this._vacRt.get(e.id);o.set(`vacuum:${e.id}`,{source:t,telemetry:i,mapId:i?this._vacMapId(e,i,n):null,runtime:r?{trail:r.trail,lastTs:r.lastTs,moving:r.moving,jump:r.jump}:null,server:this._vacSrvTrails[e.id]||null})}}const d=Uu({sourceSequence:this._hassSequence,hass:n,devices:this._devices,presentations:r,positions:(u=this._model.length>0,p=this._devices,m=e=>this._livePos(e),u?new Map(p.map(e=>[e.id,m(e)])):new Map),facts:o,entityIds:s,deviceIds:a,areaIds:l});var u,p,m;this._capturedSnapshotSequence=this._hassSequence,this._capturedSnapshotDevices=this._devices,this._capturedSnapshotLayout=this._layout,this._capturedSnapshotActivity=t,this._capturedSnapshotConfigEpoch=this._cfgEpoch,this._capturedSnapshotVirtual=i,this._visibleDeviceSnapshot&&"steady"!==this._continuity.state?this._candidateDeviceSnapshot=d:(this._visibleDeviceSnapshot=d,this._candidateDeviceSnapshot=null)}get _renderDeviceSnapshot(){return this._stagedDeviceSnapshotToken===this._continuity.token?this._candidateDeviceSnapshot||this._visibleDeviceSnapshot:this._visibleDeviceSnapshot||this._candidateDeviceSnapshot}get _renderPlanHass(){return this._renderDeviceSnapshot?.hass||this._planHass}get _renderDevices(){return this._renderDeviceSnapshot?.devices||this._devices}get _fullRegistryHass(){return this._planHass,this._planHassMemo?.full||this.hass}_bindingStatus(e){return ih(this.hass,e,this._haRegistry)}houseplanDiagnostics(){const e=function(e){const t=Kc(e);return{access:t.access,authoritative:t.authoritative,revision:t.revision,lastSuccess:t.lastSuccess,error:t.error}}(this.hass),t={active:0,ha_disabled:0,orphaned:0,unverified:0};for(const e of this._markers)e.removed||"virtual"===e.binding||t[this._bindingStatus(e.binding).kind]++;return{registry:{...e,lastSuccessAgeMs:e.lastSuccess?Math.max(0,Date.now()-e.lastSuccess):null},bindings:t}}_openBindingInHa(e){const[t,i]=e.split(":");if(i)if("device"!==t){if("entity"===t){const e=this._fullRegistryHass.entities?.[i];e?.device_id&&mf("/config/devices/device/"+encodeURIComponent(e.device_id))}}else mf("/config/devices/device/"+encodeURIComponent(i))}_bindingHasHaPage(e){const[t,i]=e.split(":");return!!i&&("device"===t||"entity"===t&&!!this._fullRegistryHass.entities?.[i]?.device_id)}_toggleMarkerDialogVisibility(){const e=this._markerDialog;if(!e)return;const t="ha"===e.bindingMode?this._bindingStatus(e.binding):null,i=e.hideFromPlan||"ha_disabled"===t?.kind;i&&"ha_disabled"===t?.kind?this._showToast(this._t("entity"===t.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device")):i&&"unverified"===t?.kind?this._showToast(this._t("toast.ha_binding_unverified")):this._markerDialog={...e,hideFromPlan:!i}}_hookConnection(){const e=this.hass?.connection;e&&e!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),e.addEventListener?.("ready",this._onConnReady),e.addEventListener?.("disconnected",this._onConnLost),e.addEventListener?.("reconnect-error",this._onConnLost),this._connHooked=e)}_display(e){return this._signer.display(this.hass,e)}_resign(){this._signer.resign(this.hass,mn(this._serverCfg))}_onLayoutEvent(e){e<=this._layoutRev||(clearTimeout(this._layoutSyncTimer),this._layoutSyncTimer=window.setTimeout(()=>{e<=this._layoutRev||this._reloadLayoutOnly()},200))}_noteLayoutRev(e){const t=e?.rev;"number"==typeof t&&t>this._layoutRev&&(this._layoutRev=t)}async _reloadLayoutOnly(){if(!this._serverStorage||!this.hass?.callWS)return;this._beginContinuityCandidate("layout-reload",!1);const e=new Map;for(const t of this._dirtyPos)this._layout[t]&&e.set(t,this._layout[t]);this._persistLayout.pending()&&this._persistLayout.flush();for(const[t,i]of this._sentPos)e.set(t,i);try{const t=await this.hass.callWS({type:"houseplan/layout/get"}),i={...t?.layout||{}};for(const[t,n]of e)i[t]=n;const n=Rl(i);n!==(this._layoutContentFingerprint||Rl(this._layout))&&(this._layout=i,this._layoutContentFingerprint=n),this._layoutRev=t?.rev??this._layoutRev,this._canOptimizeUndo=!!t?.can_optimize_undo,this._haIntegrationVersion="string"==typeof t?.integration_version?t.integration_version:this._haIntegrationVersion,this._undoKind=t?.undo_kind||null,this._cacheSnapshot(),this.requestUpdate()}catch{}finally{this._continuityDataReady=!0,this.requestUpdate()}}_maybeRebuildDevices(){const e=this.hass;if(!e?.devices||!e?.entities||!e?.areas)return;const t=this._haRegistry,i=Xc(e,t)+":"+Object.keys(e.areas).length+":"+(this._norm?"n":"l")+":"+eu(e,this._config?.language);if(i===this._regSignature&&this._devices.length)return;this._regSignature=i;const n=new Map(this._devices.map(e=>[e.id,e.bindingStatus?.kind||"active"]));this._devices=Uh({hass:e,registry:t,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||"",loc:e=>this._t(e),iconRules:this._iconRules});const r=this._markers.filter(e=>!e.removed&&"virtual"!==e.binding).map(e=>e.binding).sort(),o=t.revision+":"+r.join("|");if(t.authoritative&&o!==this._haBindingCacheKey){const i=new Map;for(const n of r)i.set(n,ih(e,n,t));!function(e){if("undefined"==typeof localStorage||!e.size)return;const t=th(),i=Date.now();for(const[n,r]of e)"active"===r.kind?t[n]={kind:"active",ts:i}:"ha_disabled"===r.kind&&(t[n]={kind:"ha_disabled",reason:r.reason,ts:i});const n=Object.fromEntries(Object.entries(t).sort((e,t)=>t[1].ts-e[1].ts).slice(0,1500));Wc=n;try{localStorage.setItem(Lc,JSON.stringify(n))}catch{}}(i),this._haBindingCacheKey=o}this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),this._syncActivityRuntime();const s=new Set(this._devices.filter(e=>!e.hidden).map(e=>e.id));for(const e of this._vacRt.keys())s.has(e)||this._vacRt.delete(e);if(this._infoCard){const e=this._devices.find(e=>e.id===this._infoCard.id);this._infoCard=e&&"ha_disabled"!==e.bindingStatus?.kind?e:null}const a=this._devices.some(e=>"ha_disabled"===e.bindingStatus?.kind&&"ha_disabled"!==n.get(e.id));a&&("ha_disabled"!==this._infoCard?.bindingStatus?.kind&&"ha_disabled"!==this._devices.find(e=>e.id===this._infoCard?.id)?.bindingStatus?.kind||this._closeInfoCard(),this._drag&&"ha_disabled"===this._devices.find(e=>e.id===this._drag.id)?.bindingStatus?.kind&&(this._drag=null),clearTimeout(this._holdTimer),this._holdFired=!1,this._tip=null,this._tapConfirm=null),this._nativeMoreInfoEntity&&!this._planEntityAvailable(this._nativeMoreInfoEntity)&&(pf(this,"hass-more-info",{entityId:null}),this._nativeMoreInfoEntity=null)}_syncNewDevices(){if(!this._norm||!this._loadOk||!this._serverCfg)return;const e=this._devices.filter(e=>!e.marker&&!e.virtual).map(e=>e.id).sort(),t=e.join(",");if(t===this._newSyncKey)return;this._newSyncKey=t;const i=this._settings,{fresh:n,known:r}=function(e,t){if(!Array.isArray(t))return{fresh:[],known:[...e]};const i=new Set(t),n=e.filter(e=>!i.has(e));return{fresh:n,known:n.length?[...t,...n]:t}}(e,i.known_devices);if(!Array.isArray(i.known_devices)||n.length){const e=[...new Set([...i.new_device_ids||[],...n])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:r,new_device_ids:e}},this._saveConfig()}}get _newIds(){const e=this._settings.new_device_ids;return new Set(Array.isArray(e)?e:[])}_ackNewDevice(e){if(!this._newIds.has(e)||!this._serverCfg)return;const t=this._settings;this._serverCfg={...this._serverCfg,settings:{...t,new_device_ids:(t.new_device_ids||[]).filter(t=>t!==e)}},this._saveConfig(),this.requestUpdate()}get _markers(){return this._serverCfg?.markers||[]}_roomLqi(e){if(!e)return null;const t=[];for(const i of this._renderDevices){if(i.area!==e||i.virtual)continue;const n=Dh(this._renderPlanHass,i.entities);null!=n&&t.push(n)}return _i(t)}_roomBounds(e){if(e.poly&&e.poly.length){const t=e.poly.map(e=>e[0]),i=e.poly.map(e=>e[1]),n=Math.min(...t),r=Math.min(...i);return{x:n,y:r,w:Math.max(...t)-n,h:Math.max(...i)-r}}return{x:e.x??0,y:e.y??0,w:e.w??0,h:e.h??0}}_defaultPositions(){const e={},t=this._config?.icon_size??2.5;for(const i of this._model){const n=t/100*Ya(i)*1.3;for(const t of i.rooms){if(!t.area)continue;const r=this._devices.filter(e=>e.area===t.area&&e.space===i.id);if(!r.length)continue;const o=this._roomBounds(t),s=.1*Math.min(o.w,o.h),a=o.w-2*s,l=o.h-2*s,c=Math.max(1,Math.round(Math.sqrt(r.length*a/Math.max(l,1)))),h=Math.ceil(r.length/c),d=a/c,u=l/Math.max(h,1),p=r.map((e,t)=>({x:o.x+s+d*(t%c+.5),y:o.y+s+u*(Math.floor(t/c)+.5)}));gi(p,o,n,.5*s),r.forEach((t,i)=>e[t.id]=za(p[i]))}}return e}_pos(e){const t=this._renderDeviceSnapshot?.positions.get(e.id);return t?{x:t.x,y:t.y}:this._livePos(e)}_livePos(e){const t=this._spaceModelById(e.space),i=this._layout[e.id];if(i)if(this._norm){if(i.s===e.space)return{x:i.x*hf,y:i.y*hf}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[e.id]?this._defPos[e.id]:t?za(Ka(t)):{x:500,y:500}}_savePos(e,t,i){if(this._spaceModelById(e.space)){if(this._norm){const n=this._gridPitch,r=Math.round(t/n)*n,o=Math.round(i/n)*n,s=this._layout[e.id]?.k;this._layout={...this._layout,[e.id]:{s:e.space,x:Pa(r/hf),y:Pa(o/hf),...s?{k:s}:{}}}}else this._layout={...this._layout,[e.id]:{x:Math.round(t),y:Math.round(i)}};this._dirtyPos.add(e.id),this._persistLayout()}}_visualSamples(e){return Eu(this._renderPlanHass,e,this._renderDevices,void 0,this._fullRegistryHass).samples}_devicePresentation(e,t=!1!==this._config?.show_signal,i=!1){const n=i?null:this._renderDeviceSnapshot?.presentations.get(Vu(e.id,t));return n||qu(this._renderPlanHass,e,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:t,designPreview:i,activityRuntime:this._activityRt.get(e.id),sourceDetails:!1,lightDevices:this._renderDevices,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion})}_deviceVisual(e){return this._devicePresentation(e).visual}_stateClass(e,t=this._deviceVisual(e)){const i=this._devicePresentation(e);return i.effectiveHidden?"":("icon_ripple"!==i.display||!1===this._config?.live_states||"alarm"===t.status||t.activity,Wu({...i,visual:t}).join(" "))}_liveTemp(e){if(!this._config?.show_temperature)return null;if(!0===e.marker?.use_climate_temp){const t=Ah(this._renderPlanHass,e.entities);if(null!=t)return t}return"mdi:thermometer"!==e.icon&&"mdi:air-filter"!==e.icon?null:zh(this._renderPlanHass,e.entities)}_bindingEntities(e){if("virtual"===e||!e)return[];const t=this._bindingStatus(e);return"active"===t.kind?t.enabledEntityIds:t.allEntityIds}_bindingHasClimate(e){return this._bindingEntities(e).some(e=>e.startsWith("climate."))}_bindingHasAlarm(e){return this._bindingEntities(e).some(e=>{const t=this._planHass?.states?.[e]||this.hass?.states?.[e],i=this._fullRegistryHass.entities[e]||this.hass?.entities?.[e];return Sn(e.split(".")[0],t?.attributes?.device_class||i?.device_class||i?.original_device_class)})}_liveHum(e){return this._config?.show_temperature&&e.primary&&Ph(this.hass,e.primary)?Oh(this.hass,e.entities):null}_deviceBindingActive(e,t=!0){if(e.virtual||"virtual"===e.bindingKind)return!0;if(!e.bindingKind||!e.bindingRef)return!1;const i=this._bindingStatus(`${e.bindingKind}:${e.bindingRef}`);return"active"===i.kind||(t&&this._showToast(this._t("ha_disabled"===i.kind?"toast.ha_disabled_action":"toast.ha_binding_unverified")),!1)}_openMoreInfo(e){e?this._planEntityAvailable(e)?(this._nativeMoreInfoEntity=e,pf(this,"hass-more-info",{entityId:e})):this._showToast(this._t("toast.ha_disabled_action")):this._showToast(this._t("toast.no_entity"))}_interruptViewGesture(e,t){if(clearTimeout(this._holdTimer),clearTimeout(this._kioskHoldTimer),void 0!==e)for(const i of[t,this._stageEl])try{i?.hasPointerCapture?.(e)&&i.releasePointerCapture(e)}catch{}this._pointers.clear(),this._panStart=null,this._panLock=null,this._pinchStart=null,this._swipeStart=null}_closeInfoCard(){this._interruptViewGesture(),this._holdFired=!1,this._infoCard=null}_ctxDevice(e,t){"view"===this._mode&&(e.preventDefault(),e.stopPropagation(),this._deviceBindingActive(t)&&(t.primary?this._openMoreInfo(t.primary):this._infoCard=t))}_clickDevice(e,t){if("view"!==this._mode&&"devices"!==this._mode)return;if(e.stopPropagation(),this._drag?.moved||this._suppressClick||this._holdFired)return;if("devices"===this._mode)return void this._openMarkerDialog(t);const i=this._devices.find(e=>e.id===t.id);if(!i)return;const n=gd(i.tapAction,i.primary?.split(".")[0]),r=(e,t)=>{i.marker?.tap_confirm?this._tapConfirm={kind:"run",text:e,exec:t}:t()};if("toggle"===n){const e=this._toggleIntent(i);if(!e)return;if(!xd(e))return void this._showUnavailableToggleTargets(e);const t=e=>{const t=xd(e);if(!t)return;if("virtual-light"===t.kind)return this._startDevicePressFeedback(i.id),void this.hass.callWS({type:"houseplan/virtual_light/toggle",marker_id:t.markerId}).then(e=>{const t=Ic(this._virtualLights,e);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}).catch(e=>this._showToast(this._t("toast.virtual_light_toggle_failed",{err:this._errText(e)})));const{command:n}=t;this._startDevicePressFeedback(i.id),this.hass.callService(n.domain,n.service,n.data).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))},n=((o=e).targets.length?o.targets:o.skippedTargets).map(e=>e.name||e.entityId||e.ref).join(", ")||i.name;if(i.marker?.tap_confirm){const r=this._toggleConfirmationLines(e);if(!r.length)return;this._tapConfirm={kind:"toggle",text:this._t("confirm.tap_toggle",{name:n}),lines:r,initialIntent:e,deviceId:i.id,exec:()=>{const n=this._devices.find(e=>e.id===i.id),r=n?this._toggleIntent(n):null;r&&!xd(r)&&this._showUnavailableToggleTargets(r)||(r&&Sd(e,r)?t(r):this._showToast(this._t("toast.tap_target_changed")))}}}else t(e);return}var o;if("info"===n)return this._interruptViewGesture(),void(this._infoCard=i);if(this._deviceBindingActive(i)){if("run"===n){const e=i.marker?.tap_target||"",t=function(e){const t=String(e||"").split(".")[0];return"automation"===t?{domain:"automation",service:"trigger"}:"script"===t?{domain:"script",service:"turn_on"}:"scene"===t?{domain:"scene",service:"turn_on"}:null}(e),n=this.hass.states[e];if(!t||!n)return void this._showToast(this._t("toast.run_target_missing"));const o=n.attributes?.friendly_name||e;return void r(this._t("confirm.tap_run",{name:o}),()=>{this._deviceBindingActive(i)&&this._planEntityAvailable(e)&&(this._startDevicePressFeedback(i.id),this.hass.callService(t.domain,t.service,{entity_id:e}).then(()=>{this._stampActivity(i.id,"event",this._activitySourceKey(i)),this.requestUpdate(),this._showToast(this._t("toast.run_started",{name:o}))}).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)}))))})}"more-info"===n&&i.primary?this._openMoreInfo(i.primary):this._infoCard=i}}_showUnavailableToggleTargets(e){const t=function(e){if(!e||"group"!==e.kind||xd(e)||"configured-targets-missing"!==e.noneReason)return[];if(!e.skippedTargets.length||e.skippedTargets.some(e=>!$d.has(e.reason)&&"secure"!==e.reason))return[];const t=[],i=new Set;for(const n of e.skippedTargets){if(!$d.has(n.reason))continue;const e=String(n.name||n.entityId||n.ref||"").trim();e&&!i.has(e)&&(i.add(e),t.push(e))}return t}(e);return!!t.length&&(this._showToast(1===t.length?this._t("toast.toggle_target_unavailable",{name:t[0]}):this._t("toast.toggle_targets_unavailable",{names:t.join(", ")})),!0)}_keyDevice(e,t){"Enter"!==e.key&&" "!==e.key||"view"!==this._mode&&"devices"!==this._mode||(e.preventDefault(),this._clickDevice(e,t))}_startDevicePressFeedback(e){const t=[...this.renderRoot.querySelectorAll(".dev[data-id]")].find(t=>t.dataset.id===e),i=t?.querySelector(".device-shell");if(!i||"function"!=typeof i.animate)return;const n=Number.parseFloat(this.ownerDocument.defaultView?.getComputedStyle(i).scale||""),r=Number.isFinite(n)?Math.max(.95,Math.min(1,n)):1;this._devicePressAnimations.get(e)?.cancel();const o=this._reducedMotion?[{outlineColor:"transparent",outlineStyle:"solid",outlineWidth:"0px"},{outlineColor:"var(--hp-accent, #3ea6ff)",outlineStyle:"solid",outlineWidth:"2px",outlineOffset:"2px",offset:.5},{outlineColor:"transparent",outlineStyle:"solid",outlineWidth:"0px"}]:[{scale:String(r)},{scale:"0.95",offset:.5},{scale:"1"}],s=i.animate(o,{duration:200,easing:"cubic-bezier(.22,.61,.36,1)"});this._devicePressAnimations.set(e,s);const a=()=>{this._devicePressAnimations.get(e)===s&&this._devicePressAnimations.delete(e)};s.addEventListener("finish",a,{once:!0}),s.addEventListener("cancel",a,{once:!0})}_cancelDevicePressFeedback(){for(const e of this._devicePressAnimations.values())e.cancel();this._devicePressAnimations.clear()}_pruneDevicePressFeedback(){if(!this._devicePressAnimations.size)return;const e=new Set([...this.renderRoot.querySelectorAll(".dev[data-id]")].map(e=>e.dataset.id||""));for(const[t,i]of this._devicePressAnimations)e.has(t)||(i.cancel(),this._devicePressAnimations.delete(t))}_t(e,t){return tu(eu(this.hass,this._config?.language),e,t)}get _colorPickerLabels(){return{title:this._t("color_picker.title"),hue:this._t("color_picker.hue"),saturation:this._t("color_picker.saturation"),value:this._t("color_picker.value"),hex:this._t("color_picker.hex"),invalidHex:this._t("color_picker.invalid_hex")}}_help(e){return this._editorRuntimeOrThrow()._help(e)}get _stageEl(){return this.renderRoot.querySelector(".stage")}_contentItems(e){const t=[];for(const i of this._devices){if(i.space!==e.id||i.hidden)continue;const n=this._pos(i);t.push({minX:n.x,minY:n.y,maxX:n.x,maxY:n.y})}if(e.id===this._space){for(const e of this._openingsR){const i=Number(e.angle)*Math.PI/180,n=Math.cos(i)*e.rlen/2,r=Math.sin(i)*e.rlen/2,o=Ea([[e.rx-n,e.ry-r],[e.rx+n,e.ry+r]]);o&&t.push(o)}const i=this._decorH;for(const e of this._decorList){const n=Ea("line"===e.kind?[[e.x1*hf,e.y1*i],[e.x2*hf,e.y2*i]]:"text"===e.kind?[[e.x*hf,e.y*i]]:dr({x:e.x*hf,y:e.y*i,w:e.w*hf,h:e.h*i,angle:e.angle}));n&&t.push(n)}for(const i of this._physicalBodiesR(e)){const e=Ea(i);e&&t.push(e)}}return Wa(e,t)}_frameOf(){const e=this._spaceModel();if(!e){this._frame=null;const e={x:0,y:0,w:hf,h:hf};return{rect:e,all:e,outliers:0}}const t=this._frame,i="view"!==this._mode;if(t&&t.id===e.id&&this._bdDrag)return t;if(t&&t.id===e.id&&t.model===e&&t.layout===this._layout&&t.devs===this._devices&&t.far===this._showFar&&t.grow===i)return t;const n=Ua(this._contentItems(e));let r=n.all||Ga(e),o=this._showFar?r:n.core||Ga(e);return t&&t.id===e.id&&i&&t.grow&&(o=df(t.rect,o),r=df(t.all,r)),this._frame={id:e.id,model:e,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:r,outliers:n.outliers},this._frame}_isoSource(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._openCuts(),n=this._openingsR.map((e,t)=>({id:String(e.id||t),sourceIndex:t,type:e.type,x:e.rx,y:e.ry,angle:Number(e.angle)||0,length:e.rlen>0?e.rlen:900,flipH:!!e.flip_h,flipV:!!e.flip_v})),r=ma(64,this._cellCm),o=ma(10,this._cellCm),s=`${e.id}|${function(e){let t=2166136261;const i=e=>{if("number"!=typeof e)if("string"!=typeof e)if("boolean"!=typeof e)if(Array.isArray(e)){t=D_(t,e.length);for(const t of e)i(t)}else{if(e&&"object"==typeof e){const t=e;for(const e of Object.keys(t).sort())i(e),i(t[e]);return}t=D_(t,-1)}else t=D_(t,e?1:0);else for(const i of e)t=D_(t,i.codePointAt(0)||0);else t=D_(t,e)};return i(e),t.toString(36)}({rooms:e.rooms,walls:t,openCuts:i,openings:n,partitions:e.partitions,roomDrafts:e.room_drafts,columns:e.wall_columns,cellCm:this._cellCm,gridPitch:this._gridPitch,wallKeyPitch:this._wallKeyPitch,camera:v_,wallHeight:r,floorEdgeHeight:o,algorithm:3})}`;return{key:s,build:()=>{const o=ea(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e)).all,s=this._roomWallOpeningInputs(this._openingsR,e),a=t.length||o.length?Ts(e.rooms,t,i,s,this._wallKeyPitch,this._cellCm,this._gridPitch,hf,o):null;if(a&&("failed-core"===a.status||"not-applicable"===a.status))throw new Error("wall boolean geometry failed");const l=a?.paperGeom??$s(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,hf);if(!l)throw new Error("floor boolean geometry failed");const c=a?.openingIndex||Ps(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,hf),h=n.map(e=>{const i=this._openingsR[e.sourceIndex],n="gate"===e.type?!e.flipV:e.flipV,o=i?.partitionHost?Od(i.partitionHost,n):t.length||"gate"===e.type?Es(c,{x:e.x,y:e.y,angle:e.angle,length:e.length,flip_v:n}):{ox:0,oy:0,cm:0,side:-1};return function(e,t=64){if(!(z_([e.x,e.y,e.angle,e.length,e.face.ox,e.face.oy,t])&&e.length>0&&t>0))throw new Error("invalid isometric opening input");const i=e.length/2;let n;if("passage"===e.type)n=[];else if("gate"===e.type){const r=10*e.face.side;n=[P_(e,0,[-i,0],[i,0],r,0,.88*t),P_(e,1,[i,0],[-i,0],-r,0,.88*t)]}else n="window"===e.type?[P_(e,0,[-i,0],[i,0],-90,.27*t,.78*t),P_(e,1,[i,0],[-i,0],90,.27*t,.78*t)]:[P_(e,0,[-i,0],[e.length,0],-90,0,.92*t)];return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaves:Object.freeze(n.map(e=>Object.freeze(e)))}}({...e,face:o},r)});return{walls:a?.components.flatMap(e=>e.geom)||[],floor:l,openings:Object.freeze(h)}}}}_isoSceneKey(){if(!this._labsIso||"view"!==this._mode)return null;try{return this._isoSource()?.key??null}catch{return`${this._space}|invalid`}}_isoScene(){const e=this._isoSource();if(!e)return null;const t=this._isoGeometryCache.get(e.key);if(t)return{key:e.key,...t};const i=this._frameOf().rect,n=e.build(),r=ma(64,this._cellCm),o=ma(10,this._cellCm),s=function(e){let t=1/0,i=1/0,n=-1/0,r=-1/0;for(const o of e)for(const e of o.leaves){const o=Math.hypot(e.closedVector[0],e.closedVector[1]);t=Math.min(t,e.hinge[0]-o),i=Math.min(i,e.hinge[1]-o),n=Math.max(n,e.hinge[0]+o),r=Math.max(r,e.hinge[1]+o)}return z_([t,i,n,r])?{x:t,y:i,w:n-t,h:r-i}:null}(n.openings),a=x_({rect:s?df(i,s):i,wallHeight:r,openingHeight:r,floorDepth:o}),l=function(e,t=v_,i=64){if(!Number.isFinite(i)||i<0)throw new Error("invalid wall height");const n=[],r=[],o=[];let s=0;for(let a=0;a<(e||[]).length;a++){const l=e[a];for(let e=0;e<(l||[]).length;e++){const c=M_(l[e],e>0);if(!(c.length<3||Math.abs(S_(c))<1e-9)){n.push(R_(c,t,i)),s+=c.length;for(let n=0;ne.depth-t.depth||e.polygon-t.polygon||e.ring-t.ring||e.edge-t.edge),{topPath:n.join(" "),sides:r,contactPath:o.join(" "),edgeCount:s}}(n.walls,v_,r),c={geometry:l,floor:C_(n.floor,o),openings:n.openings,frame:a};return Z_(this._isoGeometryCache,e.key,c,8),{key:e.key,...c}}_effectiveProjection(){if("iso"!==this._desiredProjection||!this._model.length)return"flat";const e=this._isoSceneKey()||`${this._space}|invalid`;if(this._isoFallback.has(e))return"flat";try{const e=this._isoScene();return e?(t="iso",i=e.key,n=this._isoFallback,"iso"===t&&i&&!n.has(i)?"iso":"flat"):"flat"}catch(t){if(!this._isoFallback.has(e)){this._isoFallback.add(e);const i=e.split("|");console.warn(`HOUSEPLAN ISO FALLBACK: #89, space ${this._space}, fingerprint ${i[i.length-1]}, ${t instanceof Error?t.message:"renderer error"}`)}return"flat"}var t,i,n}_scenePoint(e){return"iso"===this._renderProjection?b_(e,0):e}_floorView(e){if("iso"!==this._renderProjection)return e;const t=w_([e.x,e.y]),i=w_([e.x+e.w,e.y+e.h]);return{x:t[0],y:t[1],w:i[0]-t[0],h:i[1]-t[1]}}_baseVb(){if("iso"===this._effectiveProjection()){if(!this._spaceDisplayForRender().showBorders){const e=x_({rect:this._frameOf().rect,wallHeight:ma(64,this._cellCm)});return[e.x,e.y,e.w,e.h]}const e=this._isoScene()?.frame??this._frameOf().rect;return[e.x,e.y,e.w,e.h]}const e=this._frameOf().rect;return[e.x,e.y,e.w,e.h]}get _outliers(){return this._showFar?0:this._frameOf().outliers}_fitFar(){this._showFar=!0,this._frame=null,this._resetZoom()}_fitAll(){this._showFar=!0,this._frame=null,this._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?V:B`
${this._t("canvas.far_objects",{n:this._outliers})} @@ -4582,7 +4582,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 style="left:${a.toFixed(1)}%;top:${l.toFixed(1)}%" @click=${e=>{e.stopPropagation(),this._fitAll()}}> - `}_stageAspect(){const e=this._stageEl,t=this._baseVb();return e&&e.clientHeight?e.clientWidth/e.clientHeight:t[2]/t[3]}_viewOr(e){return this._view&&this._view.w?this._view:fi(e,this._stageAspect())}_roomLabelReferenceViewWidth(e){if(!this._markup)return e.w;const t=this._stageEl,i=this.renderRoot.querySelector(".editorchrome");if(!t||t.clientWidth<=0||t.clientHeight<=0)return e.w;const n=t.clientHeight+(i?.getBoundingClientRect().height||0);return n<=0?e.w:this._viewForModeTarget(this._zoom,void 0,void 0,t.clientWidth,n).w}_screenToVb(e,t){const i=this._stageEl,n=this._viewOr(this._baseVb()),r=i?.clientWidth||1,o=i?.clientHeight||1;return[n.x+e/r*n.w,n.y+t/o*n.h]}_clampView(e,t){const i=(e,t,i,n)=>{const r=1*Math.max(t,n),o=i-r,s=i+n-t+r;return Math.max(Math.min(o,s),Math.min(Math.max(o,s),e))};return{w:e.w,h:e.h,x:i(e.x,e.w,t.x,t.w),y:i(e.y,e.h,t.y,t.h)}}_applyView(e,t,i){const n=this._baseVb(),r=fi(n,this._stageAspect()),o=Math.min(ff.ZOOM_MAX,Math.max(ff.ZOOM_MIN,e)),s=r.w/o,a=r.h/o,l=this._viewOr(n),c=t??l.x+l.w/2,h=i??l.y+l.h/2,d=this._clampView({x:c-s/2,y:h-a/2,w:s,h:a},r);return!(this._view&&Math.abs(this._zoom-o)<1e-9&&Math.abs(this._view.x-d.x)<1e-6&&Math.abs(this._view.y-d.y)<1e-6&&Math.abs(this._view.w-d.w)<1e-6&&Math.abs(this._view.h-d.h)<1e-6)&&("opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1)),this._zoom=o,this._view=d,!0)}_bootWatch(){clearTimeout(this._bootTimer),this._bootStart=Date.now(),this._bootLastH=-1,this._bootLastChange=this._bootStart;const e=()=>{if(!this._booting)return;const t=Date.now(),i=this._stageEl?this._stageEl.clientHeight:0;i!==this._bootLastH&&(this._bootLastH=i,this._bootLastChange=t);const n=t-this._bootStart;n>=1200||n>=700&&i>0&&t-this._bootLastChange>=250?this._bootSettled():this._bootTimer=window.setTimeout(e,100)};this._bootTimer=window.setTimeout(e,100)}_bootSettled(){this._booting&&!this._bootSettling&&(this._bootSettling=!0,this._refitView(),this._bootSettleRaf=requestAnimationFrame(()=>{this._bootSettleRaf=0,this._finishBootSettled()}))}_finishBootSettled(){if(!this._booting)return;this._refitView(),this._booting=!1,this._bootSettling=!1;const e=this._stageEl?.clientHeight??0;!this._config?.kiosk&&e>0&&this._warmPatch({hdrH:this._hdrH,stageH:e,vp:this._warmViewportState()},!0),this._bootFading=!0,this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220),this._bootSoft=!0,clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},K_)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_beginResumeSettle(){this._booting||this._resumeSettling||(this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:this._cancelModeTransition(!0),this._viewportInvalidAt=0,this._beginContinuityCandidate("warm-resume",!1),this._loading?this.requestUpdate():this._loadFromServer())}_refitView(){if(this._modeTransitionBusy||this._warmModeRequest)return;const e=this._stageEl;if(!e||"visible"!==document.visibilityState||e.clientWidth<=0||e.clientHeight<=0)return void(this._viewportInvalidAt||(this._viewportInvalidAt=Date.now()));const t=[e.clientWidth,e.clientHeight],i=this._lastValidStageSize,n=!!i&&Math.abs(i[0]-t[0])<=.5&&Math.abs(i[1]-t[1])<=.5,r=this._viewportInvalidAt?Date.now()-this._viewportInvalidAt:0;if(this._viewportInvalidAt=0,!i)return this._lastValidStageSize=t,void(this._view||this._applyView(this._zoom));n?this._pendingRefitSize=null:(this._pendingRefitSize=t,this._refitRaf||(this._refitRaf=requestAnimationFrame(()=>{this._refitRaf=0;const e=this._pendingRefitSize;this._pendingRefitSize=null;const t=this._stageEl;if(!e||!t||t.clientWidth<=0||t.clientHeight<=0)return;if(Math.abs(t.clientWidth-e[0])>.5||Math.abs(t.clientHeight-e[1])>.5)return void this._refitView();const i=this._lastValidStageSize;if(i&&Math.abs(i[0]-e[0])<=.5&&Math.abs(i[1]-e[1])<=.5)return;this._lastValidStageSize=e;const n=this._view;r>=wl?this._beginContinuityCandidate("stage-size-restored",!0,"stage-size"):this._continuity.hasCompleteFrame&&this._beginContinuityCandidate("stage-resize",!0,"stage-size"),this._applyView(this._zoom,n?n.x+n.w/2:void 0,n?n.y+n.h/2:void 0)})))}_zoomAt(e,t,i){const n=this._stageEl;if(!n)return;const r=fi(this._baseVb(),this._stageAspect()),o=Math.min(ff.ZOOM_MAX,Math.max(ff.ZOOM_MIN,i));"opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1));const s=n.clientWidth,a=n.clientHeight,l=this._screenToVb(e,t),c=r.w/o,h=r.h/o;this._zoom=o,this._view=this._clampView({x:l[0]-e/s*c,y:l[1]-t/a*h,w:c,h:h},r)}_onWheel(e){const t=this._stageEl;if(!t)return;e.preventDefault();const i=t.getBoundingClientRect(),n=e.deltaY<0?1.15:1/1.15;this._zoomAt(e.clientX-i.left,e.clientY-i.top,this._zoom*n),this._saveZoom()}_stepZoom(e){const t=this._stageEl;t&&(this._zoomAt(t.clientWidth/2,t.clientHeight/2,this._zoom*(e>0?1.4:1/1.4)),this._saveZoom())}_resetZoom(){const e=this._baseVb();"opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1)),this._zoom=1,this._view=fi(e,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(rf,JSON.stringify(this._zoomBySpace))}catch{}}}_restoreZoom(){const e=this._zoomBySpace[this._space]||1;this._zoom=e;const t=this._stageEl;if(t&&t.clientHeight){const t=this._baseVb();return this._applyView(e,t[0]+t[2]/2,t[1]+t[3]/2),void this.requestUpdate()}this._view=null,requestAnimationFrame(()=>{if(!this._stageEl)return;const t=this._baseVb();this._applyView(e,t[0]+t[2]/2,t[1]+t[3]/2),this.requestUpdate()})}_stagePointerDown(e){if(this._vacFit)return;if(this._kiosk&&(this._cyclePausedUntil=Date.now()+6e4,0===this._pointers.size?(this._swipeStart={x:e.clientX,y:e.clientY,id:e.pointerId},e.target.closest?.(".dev, .roomlabel, .oplock")||(clearTimeout(this._kioskHoldTimer),this._kioskHoldTimer=window.setTimeout(()=>{this._kioskDialog=!0,this._swipeStart=null},3e3))):(this._swipeStart=null,clearTimeout(this._kioskHoldTimer))),this._drag)return;if(this._markup&&e.target.closest?.(".roomlabel, .rlhandle, .rszhandle, .dev, .oplock, .op-hit, button"))return;if("devices"===this._mode&&e.target.closest(".dev"))return;if("decor"===this._mode&&this._decorPointerDown(e))return;this._pointers.set(e.pointerId,{x:e.clientX,y:e.clientY});const t=this._viewOr(this._baseVb());if(1===this._pointers.size)this._panStart={sx:e.clientX,sy:e.clientY,vx:t.x,vy:t.y},this._panLock=null,this._suppressClick=!1;else if(2===this._pointers.size){"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();const e=[...this._pointers.values()],t=Math.hypot(e[0].x-e[1].x,e[0].y-e[1].y);this._pinchStart={dist:t,zoom:this._zoom},this._panStart=null,this._panLock=null}}get _swipeZone(){return!this._hasFixedFloor&&this._kiosk&&this._zoom<=1.001&&this._model.length>1}_stagePointerMove(e){if(this._physicalRotate?.pid!==e.pointerId)if(this._physicalDrag?.pid!==e.pointerId)if(this._dtDrag?.pid!==e.pointerId)if(this._bdDrag?.pid!==e.pointerId){if(this._decorDraft?.pid===e.pointerId){const t=this._decorDraft;let i=this._decorSnap(this._svgPoint(e),e.pointerType);if(e.shiftKey&&("rect"===t.kind||"ellipse"===t.kind)){const e=i[0]-t.a[0],n=i[1]-t.a[1],r=Math.max(Math.abs(e),Math.abs(n));i=this._snap([t.a[0]+(e<0?-r:r),t.a[1]+(n<0?-r:r)])}return void(this._decorDraft={...t,b:i})}if(this._decorMove?.pid!==e.pointerId){if("decor"!==this._mode||"furniture"!==this._decorTool||!this._editorRuntime||(this._notePointer(e),!this._editorRuntime._furnPointerMove(e,this._pointerModality.hoverEnabled)))if(this._pointers.has(e.pointerId)){if(this._pointers.set(e.pointerId,{x:e.clientX,y:e.clientY}),this._markup&&1===this._pointers.size&&this._markupMove(e),this._pinchStart&&this._pointers.size>=2){"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();const e=[...this._pointers.values()],t=Math.hypot(e[0].x-e[1].x,e[0].y-e[1].y)/(this._pinchStart.dist||1),i=this._stageEl.getBoundingClientRect(),n=(e[0].x+e[1].x)/2-i.left,r=(e[0].y+e[1].y)/2-i.top;this._zoomAt(n,r,this._pinchStart.zoom*t),this._suppressClick=!0,this._saveZoom()}else if(this._panStart){const t=e.clientX-this._panStart.sx,i=e.clientY-this._panStart.sy;Math.abs(t)+Math.abs(i)>4&&(this._suppressClick=!0,clearTimeout(this._holdTimer),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover()),null===this._panLock&&Math.abs(t)+Math.abs(i)>8&&(this._panLock=this._swipeZone&&Math.abs(t)>1.5*Math.abs(i)?"swipe":"pan");const n=this._stageEl;if("pan"===this._panLock&&n){const e=this._baseVb(),r=this._viewOr(e),o=fi(e,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-t/(n.clientWidth||1)*r.w,y:this._panStart.vy-i/(n.clientHeight||1)*r.h,w:r.w,h:r.h},o)}}}else this._markupMove(e)}else this._decorMoveUpdate(e)}else this._bdMove(e);else this._dtMove(e);else this._physicalMove(e);else this._physicalRotateMove(e)}_stagePointerLeave(e){"decor"===this._mode&&this._editorRuntime?._furnPointerLeave(e),this._markup&&("opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover())}_stagePointerUp(e){if(this._kiosk){clearTimeout(this._kioskHoldTimer);const t=this._swipeStart;if(this._swipeStart=null,t&&t.id===e.pointerId){const i=e.clientX-t.x,n=e.clientY-t.y;if(Math.abs(i)+Math.abs(n)<8){const e=Date.now();e-this._lastTap<350&&this._resetZoom(),this._lastTap=e}const r="pan"===this._panLock?null:function(e,t,i,n,r,o=60){if(i>1.001||n.length<2)return null;if(Math.abs(e)e.id),this._space);r&&this._slideTo(r,i<0?"left":"right")&&(this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}if(this._physicalDrag?.pid===e.pointerId)return void this._physicalUp(e);if(this._physicalRotate?.pid===e.pointerId)return void this._physicalRotateUp(e);if(this._dtDrag?.pid===e.pointerId)return void this._dtUp();if(this._bdDrag?.pid===e.pointerId)return void this._bdUp();if(this._decorDraft?.pid===e.pointerId)return void this._decorCommitDraft();if(this._decorMove?.pid===e.pointerId)return this._decorMove.moved&&(this._recordGeometry(this._t("history.decor_move"),this._decorMove.before),this._saveConfig()),void(this._decorMove=null);if(this._editorRuntime?._furnPointerUp(e))return;const t=!!this._pinchStart||!!this._panStart;this._pointers.delete(e.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,setTimeout(()=>this._suppressClick=!1,0)),t&&0===this._pointers.size&&this.requestUpdate()}_clickRoom(e){!this._suppressClick&&e.area&&pf("/config/areas/area/"+e.area)}_pointerDown(e,t){if("view"!==this._mode&&"devices"!==this._mode)return;if("view"===this._mode){this._holdFired=!1,clearTimeout(this._holdTimer);const i=e.pointerId,n=e.currentTarget;return void(this._holdTimer=window.setTimeout(()=>{this._holdFired=!0,this._interruptViewGesture(i,n),this._infoCard=t},600))}if("ha_disabled"===t.bindingStatus?.kind)return;e.preventDefault();const i=this._pos(t);this._drag={id:t.id,sx:e.clientX,sy:e.clientY,ox:i.x,oy:i.y,moved:!1},_f(e),this._tip=null}_pointerMove(e,t){if("devices"!==this._mode)return;if(!this._drag||this._drag.id!==t.id)return;const i=this.renderRoot.querySelector(".stage");if(!i)return;const n=this._baseVb(),r=i.getBoundingClientRect(),o=this._viewOr(n),s=(e.clientX-this._drag.sx)/r.width*o.w,a=(e.clientY-this._drag.sy)/r.height*o.h;Math.abs(e.clientX-this._drag.sx)+Math.abs(e.clientY-this._drag.sy)>3&&(this._drag.moved=!0,clearTimeout(this._holdTimer));const l=Aa(this._drag.ox+s),c=Aa(this._drag.oy+a);this._savePos(t,l,c)}_pointerUp(e,t){if(clearTimeout(this._holdTimer),"devices"!==this._mode)return;if(!this._drag||this._drag.id!==t.id)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&(this._selId=t.id,window.setTimeout(()=>this._drag=null,0))}_showToast(e){for(const e of this.renderRoot.querySelectorAll("hp-dialog"))e.closeTransientOverlays("toast");this._toast=e,clearTimeout(this._toastTimer),this._toastTimer=window.setTimeout(()=>{this._toast=""},3500)}_syncPointerHoverTargets(){const e=this._pointerModality.hoverEnabled;for(const t of this.renderRoot.querySelectorAll(lf))t.toggleAttribute("data-pointer-hover",e)}_syncPointerHoverSubtree(e){if(e.nodeType!==Node.ELEMENT_NODE)return;const t=e,i=this._pointerModality.hoverEnabled;t.matches(lf)&&t.toggleAttribute("data-pointer-hover",i);for(const e of t.querySelectorAll(lf))e.toggleAttribute("data-pointer-hover",i)}_clearTransientHover(e=!1){e&&this._pointerModality.suspend(),this._tip&&(this._tip=null),this._hoverRoom&&(this._hoverRoom=null)}_notePointer(e){const t=this._pointerModality.modality,i=this._pointerModality.note(e);"touch"!==i&&"pen"!==i||t===i&&!this._tip&&!this._hoverRoom||this._clearTransientHover()}_guardTouchGesture(e){if(this._editorSecondary?.handleOutsideDismiss(e))return;if("click"===e.type){if(!this._suppressClick&&!this._touchSequenceMultitouch&&Date.now()>this._touchClickBlockUntil)return;return e.preventDefault(),void e.stopImmediatePropagation()}const t=e;if(this._notePointer(t),"touch"===t.pointerType)if("pointerdown"!==e.type){if("pointermove"===e.type){const e=this._touchContacts.get(t.pointerId);if(!e)return;const i={...e,x:t.clientX,y:t.clientY};if(this._touchContacts.set(t.pointerId,i),this._touchSequenceMultitouch&&"view"===this._mode&&!this._vacFit&&this._pinchStart&&[...this._touchContacts.values()].every(e=>e.inStage)){this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY});const e=[...this._touchContacts.values()];if(e.length>=2&&this._stageEl){const[t,i]=e,n=Math.hypot(t.x-i.x,t.y-i.y)/(this._pinchStart.dist||1),r=this._stageEl.getBoundingClientRect();this._zoomAt((t.x+i.x)/2-r.left,(t.y+i.y)/2-r.top,this._pinchStart.zoom*n),this._saveZoom()}}return}if("pointerup"===e.type||"pointercancel"===e.type||"lostpointercapture"===e.type){if(this._clearTransientHover(),this._touchContacts.delete(t.pointerId),this._touchSequenceMultitouch){if(this._touchClickBlockUntil=Date.now()+500,this._pointers.delete(t.pointerId),!this._vacFit&&this._pointers.size>=2){const[e,t]=[...this._pointers.values()];this._pinchStart={dist:Math.hypot(e.x-t.x,e.y-t.y),zoom:this._zoom}}else this._pinchStart=null;0===this._pointers.size&&(this._panStart=null,this._panLock=null)}0===this._touchContacts.size&&(this._touchSequenceMultitouch=!1)}}else if(this._touchContacts.set(t.pointerId,{x:t.clientX,y:t.clientY,inStage:!!t.target?.closest?.(".stage")}),this._touchContacts.size>=2){this._clearTransientHover(),this._touchSequenceMultitouch=!0,this._touchClickBlockUntil=Number.POSITIVE_INFINITY,clearTimeout(this._holdTimer),clearTimeout(this._kioskHoldTimer),this._swipeStart=null;const e=[...this._touchContacts.values()];if("view"===this._mode&&!this._vacFit&&e.every(e=>e.inStage)){this._pointers=new Map([...this._touchContacts].map(([e,t])=>[e,{x:t.x,y:t.y}]));const[t,i]=e;this._pinchStart={dist:Math.hypot(t.x-i.x,t.y-i.y),zoom:this._zoom},this._panStart=null,this._panLock=null}else this._vacFit&&(this._pointers.clear(),this._pinchStart=null,this._panStart=null,this._panLock=null)}}_showTip(e,t,i,n,r,o){this._notePointer(e),this._pointerModality.hoverEnabled&&(this._drag||(this._tip={x:e.clientX,y:e.clientY,title:t,meta:i,lqi:n,temp:r,hum:o}))}get _gridPitch(){return Ta}get _cellCm(){const e=Number(this._curSpaceCfg?.cell_cm);return Number.isFinite(e)&&e>0?e:5}_fmtLen(e,t){const i=function(e,t,i,n){return Math.hypot(t[0]-e[0],t[1]-e[1])/i*n}(e,t,this._gridPitch,this._cellCm);return Ft(i,"mi"===this.hass?.config?.unit_system?.length)}get _curSpaceCfg(){const e=this._resize?.preview;return e&&e.space===this._space?e.sp:this._serverCfg?.spaces.find(e=>e.id===this._space)}get _renderCfg(){const e=this._resize?.preview;return e&&this._serverCfg?{...this._serverCfg,spaces:this._serverCfg.spaces.map(t=>t.id===e.space?e.sp:t)}:this._serverCfg}get _spaceH(){return this._curSpaceCfg,cf}get _segments(){const e=this._curSpaceCfg,t=this._spaceH;return Nt(e?.rooms||[]).map(e=>[e[0]*cf,e[1]*t,e[2]*cf,e[3]*t])}_savedNav(){if(this._hasFixedFloor)return null;try{return JSON.parse(localStorage.getItem(of)||"null")}catch{return null}}_saveNav(){if(!this._hasFixedFloor)try{localStorage.setItem(of,JSON.stringify({space:this._space}))}catch{}}_leaveCardRoute(){if(this._routeDepartureHandled)return;this._routeDepartureHandled=!0,"view"!==this._mode&&this._setMode("view",!1),this._pendingNavMode=null,this._geometryHistory.clear(),this._activeDraftId=null,this._resumeDraftBySpace={},this._draftSegmentCms=[],this._closingWallCm=null,this._drawWallField=null,this._showHidden=!1,this._tapConfirm=null,this._vacCalConfirm=null,this._decorEraseConfirm=null,this._openingInfo=null,this._closeInfoCard(),this._rulesDialog=null,this._alignDialog=null,this._preflightClipboardFallback=null,this._backupImportDialog=null,this._backupExportDialog=null,this._settingsDialog=null,this._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._openingDialog=null,this._physicalDialog=null,this._wallDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._mergeDialog=null,this._roomDialog=!1,this._spaceDialog=null,this._importDialog=null;const e=this._warmSlot;e?.owner===this._warmGen&&(e.vp=this._warmViewportState(),e.dlg=null,e.frameFingerprint="",clearTimeout(e.evict),e.evict=0),this._saveNav()}_setMode(e,t=!0){return this._warmModeRequest=0,this._editorRuntime?this._editorRuntime._setMode(e,t):"view"===e?void this._editorModeRequest++:void this._requestMode(e,t)}_primeDrawWallField(){return this._editorRuntimeOrThrow()._primeDrawWallField()}get _drawWallFieldValue(){return null===this._drawWallField?so(15,this._imperial):this._drawWallField}get _drawWallCm(){const e=(e=>{const t=String(e??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(t))return null;const i=Number(t);return Number.isFinite(i)?i:null})(this._drawWallFieldValue);if(null==e||e<0)return null;const t=this._imperial?2.54*e:e,i="column"===this._tool?Ws:100;return t>=("column"===this._tool?1:0)&&t<=i?t:null}get _drawWallMaxCm(){return"column"===this._tool?Ws:100}_showPhysicalRange(e=this._drawWallMaxCm,t=0){return this._editorRuntimeOrThrow()._showPhysicalRange(e,t)}_draftSegmentCount(e=this._curSpaceCfg){return this._editorRuntimeOrThrow()._draftSegmentCount(e)}_mergeSpacePartitions(e,t){return this._editorRuntimeOrThrow()._mergeSpacePartitions(e,t)}_finishWallChain(){return this._editorRuntimeOrThrow()._finishWallChain()}_activateMarkupTool(e){return this._editorRuntimeOrThrow()._activateMarkupTool(e)}_limitReached(e){return this._editorRuntimeOrThrow()._limitReached(e)}_svgPoint(e){return this._editorRuntimeOrThrow()._svgPoint(e)}_snap(e){return this._editorRuntimeOrThrow()._snap(e)}_snapDrawPoint(e,t=!1){return this._editorRuntimeOrThrow()._snapDrawPoint(e,t)}_planSnapOpeningCuts(e,t){return this._editorRuntimeOrThrow()._planSnapOpeningCuts(e,t)}_planSnapGeometrySnapshot(){return this._editorRuntimeOrThrow()._planSnapGeometrySnapshot()}_hiddenWallDiagnosticSnapshot(){return this._editorRuntimeOrThrow()._hiddenWallDiagnosticSnapshot()}_planStructuralGeometrySnapshot(){return this._editorRuntimeOrThrow()._planStructuralGeometrySnapshot()}_planSnapContextKey(e){return this._editorRuntimeOrThrow()._planSnapContextKey(e)}_resolvePlanDrawPoint(e,t){return this._editorRuntimeOrThrow()._resolvePlanDrawPoint(e,t)}get _activePlanSnapCandidate(){if(!this._markup||"draw"!==this._tool)return null;const e=this._planSnapHover;if(!e)return null;const t=this._planSnapGeometrySnapshot();return e.contextKey===this._planSnapContextKey(t.key)?e.candidate:null}get _activePlanSnapConflicts(){if(!this._markup||"draw"!==this._tool)return[];const e=this._planSnapHover;if(!e)return[];const t=this._planSnapGeometrySnapshot();return e.contextKey===this._planSnapContextKey(t.key)?e.conflicts:[]}_clearPlanSnapHover(e=!0){return this._editorRuntime?this._editorRuntimeOrThrow()._clearPlanSnapHover(e):(this._planSnapHover=null,void(e&&(this._cursorPt=null)))}_samePt(e,t){return Kt(e,t)}_dropLegacySegments(){return this._editorRuntimeOrThrow()._dropLegacySegments()}_rollbackRejectedPhysicalWrites(e){return this._editorRuntimeOrThrow()._rollbackRejectedPhysicalWrites(e)}async _reloadRejectedPhysicalWrite(){return this._editorRuntimeOrThrow()._reloadRejectedPhysicalWrite()}get _cfgWriting(){return this._writesPending>0}async _sendConfigCandidate(e){const t=Xr(e),i=await this.hass.callWS({type:"houseplan/config/set",config:t,expected_rev:this._cfgRev});this._cfgRev=i?.rev??this._cfgRev+1}_writeConfig(){if(this._editorRuntime)return this._editorRuntime._writeConfig();this._writesPending++,this._writeChain=Hm(this._writeChain,async()=>{if(!this._serverCfg)return;const e=Xr(this._serverCfg),t=Rl(e);t!==Rl(this._serverCfg)&&(this._serverCfg=e),this._cfgContentFingerprint=t,await this._sendConfigCandidate(e)});return this._writeChain.finally(()=>{this._writesPending--})}_saveConfig(){this._cfgEpoch++,this._saveConfigDebounced()}_geometrySnapshotFromConfig(e,t){return this._editorRuntimeOrThrow()._geometrySnapshotFromConfig(e,t)}_geometrySnapshot(e=this._space){return this._editorRuntimeOrThrow()._geometrySnapshot(e)}_recordGeometry(e,t){return this._editorRuntimeOrThrow()._recordGeometry(e,t)}_restoreGeometryStateInConfig(e,t,i=!1){return this._editorRuntimeOrThrow()._restoreGeometryStateInConfig(e,t,i)}_restoreGeometryStateLocal(e){return this._editorRuntimeOrThrow()._restoreGeometryStateLocal(e)}_wallModelBlockerLabel(e){return this._editorRuntimeOrThrow()._wallModelBlockerLabel(e)}_hasLegacyZeroWallFields(e=this._serverCfg){return this._editorRuntimeOrThrow()._hasLegacyZeroWallFields(e)}_showWallModelMigrationBlocked(e){return this._editorRuntimeOrThrow()._showWallModelMigrationBlocked(e)}_limitSegmentsOf(e){return this._editorRuntimeOrThrow()._limitSegmentsOf(e)}_junctionLimitViolations(e,t,i){return this._editorRuntimeOrThrow()._junctionLimitViolations(e,t,i)}_junctionLimitLabel(e){return this._editorRuntimeOrThrow()._junctionLimitLabel(e)}_junctionLimitsIntroduced(e,t,i){return this._editorRuntimeOrThrow()._junctionLimitsIntroduced(e,t,i)}_commitPhysicalGeometry(e,t,i=[]){return this._editorRuntimeOrThrow()._commitPhysicalGeometry(e,t,i)}_clearGeometryGesture(){return this._editorRuntime?this._editorRuntimeOrThrow()._clearGeometryGesture():(this._path=[],this._planSnapHover=null,this._cursorPt=null,this._openingHoverCandidate=null,this._openingJambBlockCm=null,this._openingPreset=null,this._openingRebindId=null,this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._wallFaceBatch=null,this._wallRepairDiagnostic=null,this._roomDeleteDialog=null,this._wallDialog=null,this._physicalDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._openingDialog=null,this._resize?.reset(),this._decorDraft=null,this._decorMove=null,this._dtDrag=null,void(this._bdDrag=null))}_stagePointerCancel(e){if(this._editorRuntime)return this._editorRuntime._stagePointerCancel(e)}_applyGeometryState(e,t=!1){return this._editorRuntimeOrThrow()._applyGeometryState(e,t)}_roomAt(e){return this._editorRuntimeOrThrow()._roomAt(e)}_overlapRoom(e){return this._editorRuntimeOrThrow()._overlapRoom(e)}_pointInRoom(e,t){return t.poly?Yt(e,t.poly):null!=t.x&&e[0]>=t.x&&e[0]<=t.x+t.w&&e[1]>=t.y&&e[1]<=t.y+t.h}_contourSelfIntersects(e){return this._editorRuntimeOrThrow()._contourSelfIntersects(e)}_canAppendRoomDraftPoint(){return this._editorRuntimeOrThrow()._canAppendRoomDraftPoint()}_markupClick(e){if(this._editorRuntime)return this._editorRuntime._markupClick(e)}_draftEndAt(e,t){return this._editorRuntimeOrThrow()._draftEndAt(e,t)}_mergeDraftEndpoint(e){return this._editorRuntimeOrThrow()._mergeDraftEndpoint(e)}_adoptDraftCms(e,t,i){return this._editorRuntimeOrThrow()._adoptDraftCms(e,t,i)}_draftSegmentsForPath(e,t,i){return this._editorRuntimeOrThrow()._draftSegmentsForPath(e,t,i)}_persistActiveDraftSegment(){return this._editorRuntimeOrThrow()._persistActiveDraftSegment()}_activeWallSourceKey(e){return this._editorRuntimeOrThrow()._activeWallSourceKey(e)}_wallGraphSources(e){return this._editorRuntimeOrThrow()._wallGraphSources(e)}_wallFaceGraph(e,t){return this._editorRuntimeOrThrow()._wallFaceGraph(e,t)}_offerWallFaces(e,t=this._path.length-2,i){return this._editorRuntimeOrThrow()._offerWallFaces(e,t,i)}_beginWallFaceBatch(e){return this._editorRuntimeOrThrow()._beginWallFaceBatch(e)}_offerExistingWallFace(e){return this._editorRuntimeOrThrow()._offerExistingWallFace(e)}_columnClick(e){return this._editorRuntimeOrThrow()._columnClick(e)}_openPhysicalDialog(e,t,i){return this._editorRuntimeOrThrow()._openPhysicalDialog(e,t,i)}_physicalDown(e,t,i){return this._editorRuntimeOrThrow()._physicalDown(e,t,i)}_clampPhysicalDelta(e,t,i){return this._editorRuntimeOrThrow()._clampPhysicalDelta(e,t,i)}_physicalMove(e){return this._editorRuntimeOrThrow()._physicalMove(e)}_physicalUp(e){return this._editorRuntimeOrThrow()._physicalUp(e)}_registerPhysicalTap(e,t,i){return this._editorRuntimeOrThrow()._registerPhysicalTap(e,t,i)}_cancelPhysicalGesture(){return this._editorRuntimeOrThrow()._cancelPhysicalGesture()}_physicalRotateDown(e,t){return this._editorRuntimeOrThrow()._physicalRotateDown(e,t)}_physicalRotateMove(e){return this._editorRuntimeOrThrow()._physicalRotateMove(e)}_physicalRotateUp(e){return this._editorRuntimeOrThrow()._physicalRotateUp(e)}_rszRooms(){return this._editorRuntimeOrThrow()._rszRooms()}_rszOpenings(){return this._editorRuntimeOrThrow()._rszOpenings()}_rszObstacles(){return this._editorRuntimeOrThrow()._rszObstacles()}_rszOptsFor(e,t){return this._editorRuntimeOrThrow()._rszOptsFor(e,t)}_rszResolution(e,t,i){return this._editorRuntimeOrThrow()._rszResolution(e,t,i)}_rszSnapshot(){return this._editorRuntimeOrThrow()._rszSnapshot()}_rszResetController(){return this._editorRuntimeOrThrow()._rszResetController()}_rszProjectPreview(e,t,i,n,r){return this._editorRuntimeOrThrow()._rszProjectPreview(e,t,i,n,r)}_rszAcceptPreview(e,t){return this._editorRuntimeOrThrow()._rszAcceptPreview(e,t)}_rszSpaceCandidateGeometry(e,t){return this._editorRuntimeOrThrow()._rszSpaceCandidateGeometry(e,t)}_rszSpaceCandidateRenderable(e,t){return this._editorRuntimeOrThrow()._rszSpaceCandidateRenderable(e,t)}_rszCandidateRenderable(e){return this._editorRuntimeOrThrow()._rszCandidateRenderable(e)}_rszEdgeDown(e,t,i){return this._editorRuntimeOrThrow()._rszEdgeDown(e,t,i)}_rszReasonText(e){return this._editorRuntimeOrThrow()._rszReasonText(e)}_rszDisabledActivate(e,t){return this._editorRuntimeOrThrow()._rszDisabledActivate(e,t)}_rszDisabledKey(e,t){return this._editorRuntimeOrThrow()._rszDisabledKey(e,t)}_rszMove(e){return this._editorRuntimeOrThrow()._rszMove(e)}_rszUp(e){return this._editorRuntimeOrThrow()._rszUp(e)}_rszCancelDrag(e){return this._editorRuntimeOrThrow()._rszCancelDrag(e)}_rszPointerCancel(e){return this._editorRuntimeOrThrow()._rszPointerCancel(e)}_rszEdgeLabels(e,t,i=this._resize?.rooms){return this._editorRuntimeOrThrow()._rszEdgeLabels(e,t,i)}_rszInnerSpanCms(e,t,i){return this._editorRuntimeOrThrow()._rszInnerSpanCms(e,t,i)}_renderResizeMeasurements(){return this._editorRuntimeOrThrow()._renderResizeMeasurements()}_renderResizeLayer(e){return this._editorRuntimeOrThrow()._renderResizeLayer(e)}get _openingsR(){const e=this._curSpaceCfg,t=this._spaceH,i=this._spaceModel();return i?(e?.openings||[]).flatMap(e=>{const n={...e,rx:e.x*cf,ry:e.y*t,rlen:e.length*cf};if(!e.host||"wall"===e.host.kind)return[n];const r=Dd(e,i.partitions,cf,this._cellCm,this._gridPitch);if(!r.resolved)return"plan"===this._mode?[{...n,orphanReason:r.reason||"invalid-host"}]:[];let[o,s]=r.resolved.center;const a=this._physicalDrag;return a?.moved&&"partition"===a.kind&&a.id===e.host.id&&(o+=a.delta[0],s+=a.delta[1]),[{...e,rx:o,ry:s,rlen:r.resolved.length,angle:r.resolved.angle,partitionHost:r.resolved}]}):[]}_partitionOpeningCuts(e=this._spaceModel(),t=()=>!0){if(!e)return[];return vp(gp(this._curSpaceCfg?.id===e.id?this._curSpaceCfg:null,e,this._cellCm,this._gridPitch,cf),t)}_roomWallOpeningInputs(e=this._openingsR,t=this._spaceModel()){if(!t)return[];const i=this._openCuts();return yp(e,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,cf)}_openingFace(e,t,i){return e.partitionHost?Od(e.partitionHost,i):Es(t,{x:e.rx,y:e.ry,angle:e.angle,length:e.rlen,flip_v:i})}_cmToUnits(e){return e/this._cellCm*this._gridPitch}get _decorList(){const e=this._curSpaceCfg;return Array.isArray(e?.decor)?e.decor:[]}get _decorH(){return cf}_decorResolvedStyle(e){return function(e,t,i,n=tr){const r="rect"===e?.kind||"ellipse"===e?.kind,o=e,s=Tt(o?.color,n.color);return{color:s,opacity:ir(o?.opacity,n.opacity),widthCm:lr(e,t,i,n.widthCm),fill:!!r&&!0===o?.fill,fillColor:Tt(o?.fill_color,o?.fill?s:n.fillColor),fillOpacity:r&&o?.fill?ir(o?.fill_opacity,.25):n.fillOpacity}}(e,this._cellCm,this._gridPitch,tr)}_decorWidthUnits(e){return((e,t,i,n=tr.widthCm)=>{const r=Number(e?.width_cm);if(Number.isFinite(r)&&r>0)return sr(r,t,i);const o=Number(e?.width);return Number.isFinite(o)&&o>0?o:sr(n,t,i)})(e,this._cellCm,this._gridPitch,tr.widthCm)}_decorTextSizeCm(e){if("text"===e?.kind){const t=Number(e.size_cm);return Number.isFinite(t)&&t>0?t:ar(20*Fi(e),this._cellCm,this._gridPitch)}return ar(20,this._cellCm,this._gridPitch)}_decorTextUnits(e){if("text"!==e.kind)return 20;const t=Number(e.size_cm);return Number.isFinite(t)&&t>0?sr(t,this._cellCm,this._gridPitch):20*Fi(e)}_decorSmallField(e){return Math.round(100*(this._imperial?e/2.54:e))/100}_decorSmallCm(e){const t=this._imperial?2.54*e:e;return Number.isFinite(t)?Math.max(.1,Math.min(100,t)):.1}_decorTextCm(e){const t=this._imperial?2.54*e:e;return Number.isFinite(t)?Math.max(.1,Math.min(2e3,t)):.1}_decorLargeField(e){return Math.round(100*(this._imperial?e/30.48:e/100))/100}_decorLargeCm(e){const t=this._imperial?30.48*e:100*e;return Number.isFinite(t)?Math.max(.1,Math.min(Sa*this._cellCm,t)):.1}_angleField(e){const t=Number(e);return Number.isFinite(t)?String(Number(t.toFixed(3))):"0"}_decorBoxOf(e){return"rect"!==e.kind&&"ellipse"!==e.kind&&"furniture"!==e.kind?null:{x:e.x*cf,y:e.y*this._decorH,w:e.w*cf,h:e.h*this._decorH,angle:nr(e.angle)||void 0}}_decorSnapGeometry(e){return this._editorRuntimeOrThrow()._decorSnapGeometry(e)}_decorSnap(e,t="mouse",i){return this._editorRuntimeOrThrow()._decorSnap(e,t,i)}_replaceDecor(e,t){return this._editorRuntimeOrThrow()._replaceDecor(e,t)}_cancelDecorGesture(){return this._editorRuntimeOrThrow()._cancelDecorGesture()}_decorPointerDown(e){return this._editorRuntimeOrThrow()._decorPointerDown(e)}_decorCommitDraft(){return this._editorRuntimeOrThrow()._decorCommitDraft()}_decorShapeDown(e,t){if(this._editorRuntime)return this._editorRuntime._decorShapeDown(e,t)}_decorMoveUpdate(e){return this._editorRuntimeOrThrow()._decorMoveUpdate(e)}_decorShapeDbl(e,t){if(this._editorRuntime)return this._editorRuntime._decorShapeDbl(e,t)}_openDecorProperties(e){return this._editorRuntimeOrThrow()._openDecorProperties(e)}_decorOpenText(e){return this._editorRuntimeOrThrow()._decorOpenText(e)}_decorRememberTextSelection(e){return this._editorRuntimeOrThrow()._decorRememberTextSelection(e)}_decorInsertLiveVariable(e){return this._editorRuntimeOrThrow()._decorInsertLiveVariable(e)}_decorSaveText(){return this._editorRuntimeOrThrow()._decorSaveText()}_decorSaveShape(){return this._editorRuntimeOrThrow()._decorSaveShape()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(e=>e.id===this._decorSel)||null}_dtPivot(e){return this._editorRuntimeOrThrow()._dtPivot(e)}_dtApply(e,t){return this._editorRuntimeOrThrow()._dtApply(e,t)}_dtStart(e,t,i,n){return this._editorRuntimeOrThrow()._dtStart(e,t,i,n)}_dtMove(e){return this._editorRuntimeOrThrow()._dtMove(e)}_dtUp(){return this._editorRuntimeOrThrow()._dtUp()}_dtMeasure(){return this._editorRuntimeOrThrow()._dtMeasure()}_deleteDecor(e){return this._editorRuntimeOrThrow()._deleteDecor(e)}_decorDeleteSel(){return this._editorRuntimeOrThrow()._decorDeleteSel()}_confirmDecorErase(){return this._editorRuntimeOrThrow()._confirmDecorErase()}get _furnWalls(){const e=this._rawPhysicalBodiesR().flatMap(e=>e.map((t,i)=>{const n=e[(i+1)%e.length];return[t[0],t[1],n[0],n[1]]}));return[...this._segments,...e]}get _furnWallReach(){return 6*this._gridPitch}_furnFieldValue(e){return this._editorRuntimeOrThrow()._furnFieldValue(e)}_furnFieldToCm(e){return this._editorRuntimeOrThrow()._furnFieldToCm(e)}_furnPick(e){return this._editorRuntimeOrThrow()._furnPick(e)}_furnPlace(e,t=!1){return this._editorRuntimeOrThrow()._furnPlace(e,t)}_furnMoveUpdate(e){return this._editorRuntimeOrThrow()._furnMoveUpdate(e)}_decorApplyBox(e,t){return this._editorRuntimeOrThrow()._decorApplyBox(e,t)}get _furnLive(){const e=this._dtDrag;if(!e||"scale"!==e.kind||!e.orig)return null;const t=this._decorList.find(t=>t.id===e.id);if(!t||"furniture"!==t.kind)return null;const i=cf,n=this._decorH,r=t.w*i,o=t.h*n,s=function(e,t,i,n,r){const o=e+i/2,s=t+n/2,a=(Number(r)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=(e,t)=>{const i=e-o,n=t-s;return[o+i*l-n*c,s+i*c+n*l]};return[h(e,t),h(e+i,t),h(e+i,t+n),h(e,t+n)]}(t.x*i,t.y*n,r,o,Number(t.angle)||0),a=(e,t)=>[(e[0]+t[0])/2,(e[1]+t[1])/2],l=a(s[0],s[1]),c=a(s[0],s[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[r,0])},{x:c[0],y:c[1],text:this._fmtLen([0,0],[0,o])}]}_renderFurnPalette(){return this._editorRuntimeOrThrow()._renderFurnPalette()}get _bdBase(){const e=this._curSpaceCfg;return e?.plan_url?{...ya(e.plan_aspect,cf)}:null}get _bdRect(){const e=this._curSpaceCfg;return e?.plan_url?ka(e,cf):null}get _bdParams(){const e=this._curSpaceCfg,t=Number(e?.plan_x),i=Number(e?.plan_y),n=Number(e?.plan_scale),r=Number(e?.plan_scale_x),o=Number(e?.plan_scale_y),s=Number.isFinite(n)&&n>0?n:1;return{dx:Number.isFinite(t)?t:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(r)&&r>0?r:s,sy:Number.isFinite(o)&&o>0?o:s,angle:nr(e?.plan_angle)}}_openBackdropDialog(e){if(this._editorRuntime)return this._editorRuntime._openBackdropDialog(e)}_saveBackdropDialog(){return this._editorRuntimeOrThrow()._saveBackdropDialog()}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&"backdrop"===this._decorTool}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(e,t,i,n,r){return this._editorRuntimeOrThrow()._bdApply(e,t,i,n,r)}_bdStart(e,t,i=!1){return this._editorRuntimeOrThrow()._bdStart(e,t,i)}_bdMove(e){return this._editorRuntimeOrThrow()._bdMove(e)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const e=this._bdParams;return 0!==e.dx||0!==e.dy||1!==e.sx||1!==e.sy||0!==e.angle}_bdReset(){return this._editorRuntimeOrThrow()._bdReset()}_bdUp(){return this._editorRuntimeOrThrow()._bdUp()}get _bdLive(){if(!this._bdDrag)return null;const e=this._bdRect;return e?{x:e.x+e.w/2,y:e.y+e.h/2,text:`${this._fmtLen([0,0],[e.w,0])} × ${this._fmtLen([0,0],[0,e.h])}`}:null}_renderBackdropFrame(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderBackdropFrame(e):V}_renderTextFrame(e){const t=this._dtSel,i=this._dtBox;if(!t||!i||i.id!==t.id)return V;const n=.018*Math.max(e.w,e.h),r=n/4;if("line"===t.kind){const e=[t.x1*cf,t.y1*this._decorH],i=[t.x2*cf,t.y2*this._decorH];return j` + `}_stageAspect(){const e=this._stageEl,t=this._baseVb();return e&&e.clientHeight?e.clientWidth/e.clientHeight:t[2]/t[3]}_viewOr(e){return this._view&&this._view.w?this._view:fi(e,this._stageAspect())}_roomLabelReferenceViewWidth(e){if(!this._markup)return e.w;const t=this._stageEl,i=this.renderRoot.querySelector(".editorchrome");if(!t||t.clientWidth<=0||t.clientHeight<=0)return e.w;const n=t.clientHeight+(i?.getBoundingClientRect().height||0);return n<=0?e.w:this._viewForModeTarget(this._zoom,void 0,void 0,t.clientWidth,n).w}_screenToVb(e,t){const i=this._stageEl,n=this._viewOr(this._baseVb()),r=i?.clientWidth||1,o=i?.clientHeight||1;return[n.x+e/r*n.w,n.y+t/o*n.h]}_clampView(e,t){const i=(e,t,i,n)=>{const r=1*Math.max(t,n),o=i-r,s=i+n-t+r;return Math.max(Math.min(o,s),Math.min(Math.max(o,s),e))};return{w:e.w,h:e.h,x:i(e.x,e.w,t.x,t.w),y:i(e.y,e.h,t.y,t.h)}}_applyView(e,t,i){const n=this._baseVb(),r=fi(n,this._stageAspect()),o=Math.min(gf.ZOOM_MAX,Math.max(gf.ZOOM_MIN,e)),s=r.w/o,a=r.h/o,l=this._viewOr(n),c=t??l.x+l.w/2,h=i??l.y+l.h/2,d=this._clampView({x:c-s/2,y:h-a/2,w:s,h:a},r);return!(this._view&&Math.abs(this._zoom-o)<1e-9&&Math.abs(this._view.x-d.x)<1e-6&&Math.abs(this._view.y-d.y)<1e-6&&Math.abs(this._view.w-d.w)<1e-6&&Math.abs(this._view.h-d.h)<1e-6)&&("opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1)),this._zoom=o,this._view=d,!0)}_bootWatch(){clearTimeout(this._bootTimer),this._bootStart=Date.now(),this._bootLastH=-1,this._bootLastChange=this._bootStart;const e=()=>{if(!this._booting)return;const t=Date.now(),i=this._stageEl?this._stageEl.clientHeight:0;i!==this._bootLastH&&(this._bootLastH=i,this._bootLastChange=t);const n=t-this._bootStart;n>=1200||n>=700&&i>0&&t-this._bootLastChange>=250?this._bootSettled():this._bootTimer=window.setTimeout(e,100)};this._bootTimer=window.setTimeout(e,100)}_bootSettled(){this._booting&&!this._bootSettling&&(this._bootSettling=!0,this._refitView(),this._bootSettleRaf=requestAnimationFrame(()=>{this._bootSettleRaf=0,this._finishBootSettled()}))}_finishBootSettled(){if(!this._booting)return;this._refitView(),this._booting=!1,this._bootSettling=!1;const e=this._stageEl?.clientHeight??0;!this._config?.kiosk&&e>0&&this._warmPatch({hdrH:this._hdrH,stageH:e,vp:this._warmViewportState()},!0),this._bootFading=!0,this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220),this._bootSoft=!0,clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Y_)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_beginResumeSettle(){this._booting||this._resumeSettling||(this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:this._cancelModeTransition(!0),this._viewportInvalidAt=0,this._beginContinuityCandidate("warm-resume",!1),this._loading?this.requestUpdate():this._loadFromServer())}_refitView(){if(this._modeTransitionBusy||this._warmModeRequest)return;const e=this._stageEl;if(!e||"visible"!==document.visibilityState||e.clientWidth<=0||e.clientHeight<=0)return void(this._viewportInvalidAt||(this._viewportInvalidAt=Date.now()));const t=[e.clientWidth,e.clientHeight],i=this._lastValidStageSize,n=!!i&&Math.abs(i[0]-t[0])<=.5&&Math.abs(i[1]-t[1])<=.5,r=this._viewportInvalidAt?Date.now()-this._viewportInvalidAt:0;if(this._viewportInvalidAt=0,!i)return this._lastValidStageSize=t,void(this._view||this._applyView(this._zoom));n?this._pendingRefitSize=null:(this._pendingRefitSize=t,this._refitRaf||(this._refitRaf=requestAnimationFrame(()=>{this._refitRaf=0;const e=this._pendingRefitSize;this._pendingRefitSize=null;const t=this._stageEl;if(!e||!t||t.clientWidth<=0||t.clientHeight<=0)return;if(Math.abs(t.clientWidth-e[0])>.5||Math.abs(t.clientHeight-e[1])>.5)return void this._refitView();const i=this._lastValidStageSize;if(i&&Math.abs(i[0]-e[0])<=.5&&Math.abs(i[1]-e[1])<=.5)return;this._lastValidStageSize=e;const n=this._view;r>=wl?this._beginContinuityCandidate("stage-size-restored",!0,"stage-size"):this._continuity.hasCompleteFrame&&this._beginContinuityCandidate("stage-resize",!0,"stage-size"),this._applyView(this._zoom,n?n.x+n.w/2:void 0,n?n.y+n.h/2:void 0)})))}_zoomAt(e,t,i){const n=this._stageEl;if(!n)return;const r=fi(this._baseVb(),this._stageAspect()),o=Math.min(gf.ZOOM_MAX,Math.max(gf.ZOOM_MIN,i));"opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1));const s=n.clientWidth,a=n.clientHeight,l=this._screenToVb(e,t),c=r.w/o,h=r.h/o;this._zoom=o,this._view=this._clampView({x:l[0]-e/s*c,y:l[1]-t/a*h,w:c,h:h},r)}_onWheel(e){const t=this._stageEl;if(!t)return;e.preventDefault();const i=t.getBoundingClientRect(),n=e.deltaY<0?1.15:1/1.15;this._zoomAt(e.clientX-i.left,e.clientY-i.top,this._zoom*n),this._saveZoom()}_stepZoom(e){const t=this._stageEl;t&&(this._zoomAt(t.clientWidth/2,t.clientHeight/2,this._zoom*(e>0?1.4:1/1.4)),this._saveZoom())}_resetZoom(){const e=this._baseVb();"opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1)),this._zoom=1,this._view=fi(e,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(of,JSON.stringify(this._zoomBySpace))}catch{}}}_restoreZoom(){const e=this._zoomBySpace[this._space]||1;this._zoom=e;const t=this._stageEl;if(t&&t.clientHeight){const t=this._baseVb();return this._applyView(e,t[0]+t[2]/2,t[1]+t[3]/2),void this.requestUpdate()}this._view=null,requestAnimationFrame(()=>{if(!this._stageEl)return;const t=this._baseVb();this._applyView(e,t[0]+t[2]/2,t[1]+t[3]/2),this.requestUpdate()})}_stagePointerDown(e){if(this._vacFit)return;if(this._kiosk&&(this._cyclePausedUntil=Date.now()+6e4,0===this._pointers.size?(this._swipeStart={x:e.clientX,y:e.clientY,id:e.pointerId},e.target.closest?.(".dev, .roomlabel, .oplock")||(clearTimeout(this._kioskHoldTimer),this._kioskHoldTimer=window.setTimeout(()=>{this._kioskDialog=!0,this._swipeStart=null},3e3))):(this._swipeStart=null,clearTimeout(this._kioskHoldTimer))),this._drag)return;if(this._markup&&e.target.closest?.(".roomlabel, .rlhandle, .rszhandle, .dev, .oplock, .op-hit, button"))return;if("devices"===this._mode&&e.target.closest(".dev"))return;if("decor"===this._mode&&this._decorPointerDown(e))return;this._pointers.set(e.pointerId,{x:e.clientX,y:e.clientY});const t=this._viewOr(this._baseVb());if(1===this._pointers.size)this._panStart={sx:e.clientX,sy:e.clientY,vx:t.x,vy:t.y},this._panLock=null,this._suppressClick=!1;else if(2===this._pointers.size){"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();const e=[...this._pointers.values()],t=Math.hypot(e[0].x-e[1].x,e[0].y-e[1].y);this._pinchStart={dist:t,zoom:this._zoom},this._panStart=null,this._panLock=null}}get _swipeZone(){return!this._hasFixedFloor&&this._kiosk&&this._zoom<=1.001&&this._model.length>1}_stagePointerMove(e){if(this._physicalRotate?.pid!==e.pointerId)if(this._physicalDrag?.pid!==e.pointerId)if(this._dtDrag?.pid!==e.pointerId)if(this._bdDrag?.pid!==e.pointerId){if(this._decorDraft?.pid===e.pointerId){const t=this._decorDraft;let i=this._decorSnap(this._svgPoint(e),e.pointerType);if(e.shiftKey&&("rect"===t.kind||"ellipse"===t.kind)){const e=i[0]-t.a[0],n=i[1]-t.a[1],r=Math.max(Math.abs(e),Math.abs(n));i=this._snap([t.a[0]+(e<0?-r:r),t.a[1]+(n<0?-r:r)])}return void(this._decorDraft={...t,b:i})}if(this._decorMove?.pid!==e.pointerId){if("decor"!==this._mode||"furniture"!==this._decorTool||!this._editorRuntime||(this._notePointer(e),!this._editorRuntime._furnPointerMove(e,this._pointerModality.hoverEnabled)))if(this._pointers.has(e.pointerId)){if(this._pointers.set(e.pointerId,{x:e.clientX,y:e.clientY}),this._markup&&1===this._pointers.size&&this._markupMove(e),this._pinchStart&&this._pointers.size>=2){"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();const e=[...this._pointers.values()],t=Math.hypot(e[0].x-e[1].x,e[0].y-e[1].y)/(this._pinchStart.dist||1),i=this._stageEl.getBoundingClientRect(),n=(e[0].x+e[1].x)/2-i.left,r=(e[0].y+e[1].y)/2-i.top;this._zoomAt(n,r,this._pinchStart.zoom*t),this._suppressClick=!0,this._saveZoom()}else if(this._panStart){const t=e.clientX-this._panStart.sx,i=e.clientY-this._panStart.sy;Math.abs(t)+Math.abs(i)>4&&(this._suppressClick=!0,clearTimeout(this._holdTimer),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover()),null===this._panLock&&Math.abs(t)+Math.abs(i)>8&&(this._panLock=this._swipeZone&&Math.abs(t)>1.5*Math.abs(i)?"swipe":"pan");const n=this._stageEl;if("pan"===this._panLock&&n){const e=this._baseVb(),r=this._viewOr(e),o=fi(e,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-t/(n.clientWidth||1)*r.w,y:this._panStart.vy-i/(n.clientHeight||1)*r.h,w:r.w,h:r.h},o)}}}else this._markupMove(e)}else this._decorMoveUpdate(e)}else this._bdMove(e);else this._dtMove(e);else this._physicalMove(e);else this._physicalRotateMove(e)}_stagePointerLeave(e){"decor"===this._mode&&this._editorRuntime?._furnPointerLeave(e),this._markup&&("opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover())}_stagePointerUp(e){if(this._kiosk){clearTimeout(this._kioskHoldTimer);const t=this._swipeStart;if(this._swipeStart=null,t&&t.id===e.pointerId){const i=e.clientX-t.x,n=e.clientY-t.y;if(Math.abs(i)+Math.abs(n)<8){const e=Date.now();e-this._lastTap<350&&this._resetZoom(),this._lastTap=e}const r="pan"===this._panLock?null:function(e,t,i,n,r,o=60){if(i>1.001||n.length<2)return null;if(Math.abs(e)e.id),this._space);r&&this._slideTo(r,i<0?"left":"right")&&(this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}if(this._physicalDrag?.pid===e.pointerId)return void this._physicalUp(e);if(this._physicalRotate?.pid===e.pointerId)return void this._physicalRotateUp(e);if(this._dtDrag?.pid===e.pointerId)return void this._dtUp();if(this._bdDrag?.pid===e.pointerId)return void this._bdUp();if(this._decorDraft?.pid===e.pointerId)return void this._decorCommitDraft();if(this._decorMove?.pid===e.pointerId)return this._decorMove.moved&&(this._recordGeometry(this._t("history.decor_move"),this._decorMove.before),this._saveConfig()),void(this._decorMove=null);if(this._editorRuntime?._furnPointerUp(e))return;const t=!!this._pinchStart||!!this._panStart;this._pointers.delete(e.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,setTimeout(()=>this._suppressClick=!1,0)),t&&0===this._pointers.size&&this.requestUpdate()}_clickRoom(e){!this._suppressClick&&e.area&&mf("/config/areas/area/"+e.area)}_pointerDown(e,t){if("view"!==this._mode&&"devices"!==this._mode)return;if("view"===this._mode){this._holdFired=!1,clearTimeout(this._holdTimer);const i=e.pointerId,n=e.currentTarget;return void(this._holdTimer=window.setTimeout(()=>{this._holdFired=!0,this._interruptViewGesture(i,n),this._infoCard=t},600))}if("ha_disabled"===t.bindingStatus?.kind)return;e.preventDefault();const i=this._pos(t);this._drag={id:t.id,sx:e.clientX,sy:e.clientY,ox:i.x,oy:i.y,moved:!1},ff(e),this._tip=null}_pointerMove(e,t){if("devices"!==this._mode)return;if(!this._drag||this._drag.id!==t.id)return;const i=this.renderRoot.querySelector(".stage");if(!i)return;const n=this._baseVb(),r=i.getBoundingClientRect(),o=this._viewOr(n),s=(e.clientX-this._drag.sx)/r.width*o.w,a=(e.clientY-this._drag.sy)/r.height*o.h;Math.abs(e.clientX-this._drag.sx)+Math.abs(e.clientY-this._drag.sy)>3&&(this._drag.moved=!0,clearTimeout(this._holdTimer));const l=Aa(this._drag.ox+s),c=Aa(this._drag.oy+a);this._savePos(t,l,c)}_pointerUp(e,t){if(clearTimeout(this._holdTimer),"devices"!==this._mode)return;if(!this._drag||this._drag.id!==t.id)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&(this._selId=t.id,window.setTimeout(()=>this._drag=null,0))}_showToast(e){for(const e of this.renderRoot.querySelectorAll("hp-dialog"))e.closeTransientOverlays("toast");this._toast=e,clearTimeout(this._toastTimer),this._toastTimer=window.setTimeout(()=>{this._toast=""},3500)}_syncPointerHoverTargets(){const e=this._pointerModality.hoverEnabled;for(const t of this.renderRoot.querySelectorAll(cf))t.toggleAttribute("data-pointer-hover",e)}_syncPointerHoverSubtree(e){if(e.nodeType!==Node.ELEMENT_NODE)return;const t=e,i=this._pointerModality.hoverEnabled;t.matches(cf)&&t.toggleAttribute("data-pointer-hover",i);for(const e of t.querySelectorAll(cf))e.toggleAttribute("data-pointer-hover",i)}_clearTransientHover(e=!1){e&&this._pointerModality.suspend(),this._tip&&(this._tip=null),this._hoverRoom&&(this._hoverRoom=null)}_notePointer(e){const t=this._pointerModality.modality,i=this._pointerModality.note(e);"touch"!==i&&"pen"!==i||t===i&&!this._tip&&!this._hoverRoom||this._clearTransientHover()}_guardTouchGesture(e){if(this._editorSecondary?.handleOutsideDismiss(e))return;if("click"===e.type){if(!this._suppressClick&&!this._touchSequenceMultitouch&&Date.now()>this._touchClickBlockUntil)return;return e.preventDefault(),void e.stopImmediatePropagation()}const t=e;if(this._notePointer(t),"touch"===t.pointerType)if("pointerdown"!==e.type){if("pointermove"===e.type){const e=this._touchContacts.get(t.pointerId);if(!e)return;const i={...e,x:t.clientX,y:t.clientY};if(this._touchContacts.set(t.pointerId,i),this._touchSequenceMultitouch&&"view"===this._mode&&!this._vacFit&&this._pinchStart&&[...this._touchContacts.values()].every(e=>e.inStage)){this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY});const e=[...this._touchContacts.values()];if(e.length>=2&&this._stageEl){const[t,i]=e,n=Math.hypot(t.x-i.x,t.y-i.y)/(this._pinchStart.dist||1),r=this._stageEl.getBoundingClientRect();this._zoomAt((t.x+i.x)/2-r.left,(t.y+i.y)/2-r.top,this._pinchStart.zoom*n),this._saveZoom()}}return}if("pointerup"===e.type||"pointercancel"===e.type||"lostpointercapture"===e.type){if(this._clearTransientHover(),this._touchContacts.delete(t.pointerId),this._touchSequenceMultitouch){if(this._touchClickBlockUntil=Date.now()+500,this._pointers.delete(t.pointerId),!this._vacFit&&this._pointers.size>=2){const[e,t]=[...this._pointers.values()];this._pinchStart={dist:Math.hypot(e.x-t.x,e.y-t.y),zoom:this._zoom}}else this._pinchStart=null;0===this._pointers.size&&(this._panStart=null,this._panLock=null)}0===this._touchContacts.size&&(this._touchSequenceMultitouch=!1)}}else if(this._touchContacts.set(t.pointerId,{x:t.clientX,y:t.clientY,inStage:!!t.target?.closest?.(".stage")}),this._touchContacts.size>=2){this._clearTransientHover(),this._touchSequenceMultitouch=!0,this._touchClickBlockUntil=Number.POSITIVE_INFINITY,clearTimeout(this._holdTimer),clearTimeout(this._kioskHoldTimer),this._swipeStart=null;const e=[...this._touchContacts.values()];if("view"===this._mode&&!this._vacFit&&e.every(e=>e.inStage)){this._pointers=new Map([...this._touchContacts].map(([e,t])=>[e,{x:t.x,y:t.y}]));const[t,i]=e;this._pinchStart={dist:Math.hypot(t.x-i.x,t.y-i.y),zoom:this._zoom},this._panStart=null,this._panLock=null}else this._vacFit&&(this._pointers.clear(),this._pinchStart=null,this._panStart=null,this._panLock=null)}}_showTip(e,t,i,n,r,o){this._notePointer(e),this._pointerModality.hoverEnabled&&(this._drag||(this._tip={x:e.clientX,y:e.clientY,title:t,meta:i,lqi:n,temp:r,hum:o}))}get _gridPitch(){return Ta}get _cellCm(){const e=Number(this._curSpaceCfg?.cell_cm);return Number.isFinite(e)&&e>0?e:5}_fmtLen(e,t){const i=function(e,t,i,n){return Math.hypot(t[0]-e[0],t[1]-e[1])/i*n}(e,t,this._gridPitch,this._cellCm);return Ft(i,"mi"===this.hass?.config?.unit_system?.length)}get _curSpaceCfg(){const e=this._resize?.preview;return e&&e.space===this._space?e.sp:this._serverCfg?.spaces.find(e=>e.id===this._space)}get _renderCfg(){const e=this._resize?.preview;return e&&this._serverCfg?{...this._serverCfg,spaces:this._serverCfg.spaces.map(t=>t.id===e.space?e.sp:t)}:this._serverCfg}get _spaceH(){return this._curSpaceCfg,hf}get _segments(){const e=this._curSpaceCfg,t=this._spaceH;return Nt(e?.rooms||[]).map(e=>[e[0]*hf,e[1]*t,e[2]*hf,e[3]*t])}_savedNav(){if(this._hasFixedFloor)return null;try{return JSON.parse(localStorage.getItem(sf)||"null")}catch{return null}}_saveNav(){if(!this._hasFixedFloor)try{localStorage.setItem(sf,JSON.stringify({space:this._space}))}catch{}}_leaveCardRoute(){if(this._routeDepartureHandled)return;this._routeDepartureHandled=!0,"view"!==this._mode&&this._setMode("view",!1),this._pendingNavMode=null,this._geometryHistory.clear(),this._activeDraftId=null,this._resumeDraftBySpace={},this._draftSegmentCms=[],this._closingWallCm=null,this._drawWallField=null,this._showHidden=!1,this._tapConfirm=null,this._vacCalConfirm=null,this._decorEraseConfirm=null,this._openingInfo=null,this._closeInfoCard(),this._rulesDialog=null,this._alignDialog=null,this._preflightClipboardFallback=null,this._backupImportDialog=null,this._backupExportDialog=null,this._settingsDialog=null,this._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._openingDialog=null,this._physicalDialog=null,this._wallDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._mergeDialog=null,this._roomDialog=!1,this._spaceDialog=null,this._importDialog=null;const e=this._warmSlot;e?.owner===this._warmGen&&(e.vp=this._warmViewportState(),e.dlg=null,e.frameFingerprint="",clearTimeout(e.evict),e.evict=0),this._saveNav()}_setMode(e,t=!0){return this._warmModeRequest=0,this._editorRuntime?this._editorRuntime._setMode(e,t):"view"===e?void this._editorModeRequest++:void this._requestMode(e,t)}_primeDrawWallField(){return this._editorRuntimeOrThrow()._primeDrawWallField()}get _drawWallFieldValue(){return null===this._drawWallField?so(15,this._imperial):this._drawWallField}get _drawWallCm(){const e=(e=>{const t=String(e??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(t))return null;const i=Number(t);return Number.isFinite(i)?i:null})(this._drawWallFieldValue);if(null==e||e<0)return null;const t=this._imperial?2.54*e:e,i="column"===this._tool?Ws:100;return t>=("column"===this._tool?1:0)&&t<=i?t:null}get _drawWallMaxCm(){return"column"===this._tool?Ws:100}_showPhysicalRange(e=this._drawWallMaxCm,t=0){return this._editorRuntimeOrThrow()._showPhysicalRange(e,t)}_draftSegmentCount(e=this._curSpaceCfg){return this._editorRuntimeOrThrow()._draftSegmentCount(e)}_mergeSpacePartitions(e,t){return this._editorRuntimeOrThrow()._mergeSpacePartitions(e,t)}_finishWallChain(){return this._editorRuntimeOrThrow()._finishWallChain()}_activateMarkupTool(e){return this._editorRuntimeOrThrow()._activateMarkupTool(e)}_limitReached(e){return this._editorRuntimeOrThrow()._limitReached(e)}_svgPoint(e){return this._editorRuntimeOrThrow()._svgPoint(e)}_snap(e){return this._editorRuntimeOrThrow()._snap(e)}_snapDrawPoint(e,t=!1){return this._editorRuntimeOrThrow()._snapDrawPoint(e,t)}_planSnapOpeningCuts(e,t){return this._editorRuntimeOrThrow()._planSnapOpeningCuts(e,t)}_planSnapGeometrySnapshot(){return this._editorRuntimeOrThrow()._planSnapGeometrySnapshot()}_hiddenWallDiagnosticSnapshot(){return this._editorRuntimeOrThrow()._hiddenWallDiagnosticSnapshot()}_planStructuralGeometrySnapshot(){return this._editorRuntimeOrThrow()._planStructuralGeometrySnapshot()}_planSnapContextKey(e){return this._editorRuntimeOrThrow()._planSnapContextKey(e)}_resolvePlanDrawPoint(e,t){return this._editorRuntimeOrThrow()._resolvePlanDrawPoint(e,t)}get _activePlanSnapCandidate(){if(!this._markup||"draw"!==this._tool)return null;const e=this._planSnapHover;if(!e)return null;const t=this._planSnapGeometrySnapshot();return e.contextKey===this._planSnapContextKey(t.key)?e.candidate:null}get _activePlanSnapConflicts(){if(!this._markup||"draw"!==this._tool)return[];const e=this._planSnapHover;if(!e)return[];const t=this._planSnapGeometrySnapshot();return e.contextKey===this._planSnapContextKey(t.key)?e.conflicts:[]}_clearPlanSnapHover(e=!0){return this._editorRuntime?this._editorRuntimeOrThrow()._clearPlanSnapHover(e):(this._planSnapHover=null,void(e&&(this._cursorPt=null)))}_samePt(e,t){return Kt(e,t)}_dropLegacySegments(){return this._editorRuntimeOrThrow()._dropLegacySegments()}_rollbackRejectedPhysicalWrites(e){return this._editorRuntimeOrThrow()._rollbackRejectedPhysicalWrites(e)}async _reloadRejectedPhysicalWrite(){return this._editorRuntimeOrThrow()._reloadRejectedPhysicalWrite()}get _cfgWriting(){return this._writesPending>0}async _sendConfigCandidate(e){const t=Xr(e),i=await this.hass.callWS({type:"houseplan/config/set",config:t,expected_rev:this._cfgRev});this._cfgRev=i?.rev??this._cfgRev+1}_writeConfig(){if(this._editorRuntime)return this._editorRuntime._writeConfig();this._writesPending++,this._writeChain=Nm(this._writeChain,async()=>{if(!this._serverCfg)return;const e=Xr(this._serverCfg),t=Rl(e);t!==Rl(this._serverCfg)&&(this._serverCfg=e),this._cfgContentFingerprint=t,await this._sendConfigCandidate(e)});return this._writeChain.finally(()=>{this._writesPending--})}_saveConfig(){this._cfgEpoch++,this._saveConfigDebounced()}_geometrySnapshotFromConfig(e,t){return this._editorRuntimeOrThrow()._geometrySnapshotFromConfig(e,t)}_geometrySnapshot(e=this._space){return this._editorRuntimeOrThrow()._geometrySnapshot(e)}_recordGeometry(e,t){return this._editorRuntimeOrThrow()._recordGeometry(e,t)}_restoreGeometryStateInConfig(e,t,i=!1){return this._editorRuntimeOrThrow()._restoreGeometryStateInConfig(e,t,i)}_restoreGeometryStateLocal(e){return this._editorRuntimeOrThrow()._restoreGeometryStateLocal(e)}_wallModelBlockerLabel(e){return this._editorRuntimeOrThrow()._wallModelBlockerLabel(e)}_hasLegacyZeroWallFields(e=this._serverCfg){return this._editorRuntimeOrThrow()._hasLegacyZeroWallFields(e)}_showWallModelMigrationBlocked(e){return this._editorRuntimeOrThrow()._showWallModelMigrationBlocked(e)}_limitSegmentsOf(e){return this._editorRuntimeOrThrow()._limitSegmentsOf(e)}_junctionLimitViolations(e,t,i){return this._editorRuntimeOrThrow()._junctionLimitViolations(e,t,i)}_junctionLimitLabel(e){return this._editorRuntimeOrThrow()._junctionLimitLabel(e)}_junctionLimitsIntroduced(e,t,i){return this._editorRuntimeOrThrow()._junctionLimitsIntroduced(e,t,i)}_commitPhysicalGeometry(e,t,i=[]){return this._editorRuntimeOrThrow()._commitPhysicalGeometry(e,t,i)}_clearGeometryGesture(){return this._editorRuntime?this._editorRuntimeOrThrow()._clearGeometryGesture():(this._path=[],this._planSnapHover=null,this._cursorPt=null,this._openingHoverCandidate=null,this._openingJambBlockCm=null,this._openingPreset=null,this._openingRebindId=null,this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._wallFaceBatch=null,this._wallRepairDiagnostic=null,this._roomDeleteDialog=null,this._wallDialog=null,this._physicalDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._openingDialog=null,this._resize?.reset(),this._decorDraft=null,this._decorMove=null,this._dtDrag=null,void(this._bdDrag=null))}_stagePointerCancel(e){if(this._editorRuntime)return this._editorRuntime._stagePointerCancel(e)}_applyGeometryState(e,t=!1){return this._editorRuntimeOrThrow()._applyGeometryState(e,t)}_roomAt(e){return this._editorRuntimeOrThrow()._roomAt(e)}_overlapRoom(e){return this._editorRuntimeOrThrow()._overlapRoom(e)}_pointInRoom(e,t){return t.poly?Yt(e,t.poly):null!=t.x&&e[0]>=t.x&&e[0]<=t.x+t.w&&e[1]>=t.y&&e[1]<=t.y+t.h}_contourSelfIntersects(e){return this._editorRuntimeOrThrow()._contourSelfIntersects(e)}_canAppendRoomDraftPoint(){return this._editorRuntimeOrThrow()._canAppendRoomDraftPoint()}_markupClick(e){if(this._editorRuntime)return this._editorRuntime._markupClick(e)}_draftEndAt(e,t){return this._editorRuntimeOrThrow()._draftEndAt(e,t)}_mergeDraftEndpoint(e){return this._editorRuntimeOrThrow()._mergeDraftEndpoint(e)}_adoptDraftCms(e,t,i){return this._editorRuntimeOrThrow()._adoptDraftCms(e,t,i)}_draftSegmentsForPath(e,t,i){return this._editorRuntimeOrThrow()._draftSegmentsForPath(e,t,i)}_persistActiveDraftSegment(){return this._editorRuntimeOrThrow()._persistActiveDraftSegment()}_activeWallSourceKey(e){return this._editorRuntimeOrThrow()._activeWallSourceKey(e)}_wallGraphSources(e){return this._editorRuntimeOrThrow()._wallGraphSources(e)}_wallFaceGraph(e,t){return this._editorRuntimeOrThrow()._wallFaceGraph(e,t)}_offerWallFaces(e,t=this._path.length-2,i){return this._editorRuntimeOrThrow()._offerWallFaces(e,t,i)}_beginWallFaceBatch(e){return this._editorRuntimeOrThrow()._beginWallFaceBatch(e)}_offerExistingWallFace(e){return this._editorRuntimeOrThrow()._offerExistingWallFace(e)}_columnClick(e){return this._editorRuntimeOrThrow()._columnClick(e)}_openPhysicalDialog(e,t,i){return this._editorRuntimeOrThrow()._openPhysicalDialog(e,t,i)}_physicalDown(e,t,i){return this._editorRuntimeOrThrow()._physicalDown(e,t,i)}_clampPhysicalDelta(e,t,i){return this._editorRuntimeOrThrow()._clampPhysicalDelta(e,t,i)}_physicalMove(e){return this._editorRuntimeOrThrow()._physicalMove(e)}_physicalUp(e){return this._editorRuntimeOrThrow()._physicalUp(e)}_registerPhysicalTap(e,t,i){return this._editorRuntimeOrThrow()._registerPhysicalTap(e,t,i)}_cancelPhysicalGesture(){return this._editorRuntimeOrThrow()._cancelPhysicalGesture()}_physicalRotateDown(e,t){return this._editorRuntimeOrThrow()._physicalRotateDown(e,t)}_physicalRotateMove(e){return this._editorRuntimeOrThrow()._physicalRotateMove(e)}_physicalRotateUp(e){return this._editorRuntimeOrThrow()._physicalRotateUp(e)}_rszRooms(){return this._editorRuntimeOrThrow()._rszRooms()}_rszOpenings(){return this._editorRuntimeOrThrow()._rszOpenings()}_rszObstacles(){return this._editorRuntimeOrThrow()._rszObstacles()}_rszOptsFor(e,t){return this._editorRuntimeOrThrow()._rszOptsFor(e,t)}_rszResolution(e,t,i){return this._editorRuntimeOrThrow()._rszResolution(e,t,i)}_rszSnapshot(){return this._editorRuntimeOrThrow()._rszSnapshot()}_rszResetController(){return this._editorRuntimeOrThrow()._rszResetController()}_rszProjectPreview(e,t,i,n,r){return this._editorRuntimeOrThrow()._rszProjectPreview(e,t,i,n,r)}_rszAcceptPreview(e,t){return this._editorRuntimeOrThrow()._rszAcceptPreview(e,t)}_rszSpaceCandidateGeometry(e,t){return this._editorRuntimeOrThrow()._rszSpaceCandidateGeometry(e,t)}_rszSpaceCandidateRenderable(e,t){return this._editorRuntimeOrThrow()._rszSpaceCandidateRenderable(e,t)}_rszCandidateRenderable(e){return this._editorRuntimeOrThrow()._rszCandidateRenderable(e)}_rszEdgeDown(e,t,i){return this._editorRuntimeOrThrow()._rszEdgeDown(e,t,i)}_rszReasonText(e){return this._editorRuntimeOrThrow()._rszReasonText(e)}_rszDisabledActivate(e,t){return this._editorRuntimeOrThrow()._rszDisabledActivate(e,t)}_rszDisabledKey(e,t){return this._editorRuntimeOrThrow()._rszDisabledKey(e,t)}_rszMove(e){return this._editorRuntimeOrThrow()._rszMove(e)}_rszUp(e){return this._editorRuntimeOrThrow()._rszUp(e)}_rszCancelDrag(e){return this._editorRuntimeOrThrow()._rszCancelDrag(e)}_rszPointerCancel(e){return this._editorRuntimeOrThrow()._rszPointerCancel(e)}_rszEdgeLabels(e,t,i=this._resize?.rooms){return this._editorRuntimeOrThrow()._rszEdgeLabels(e,t,i)}_rszInnerSpanCms(e,t,i){return this._editorRuntimeOrThrow()._rszInnerSpanCms(e,t,i)}_renderResizeMeasurements(){return this._editorRuntimeOrThrow()._renderResizeMeasurements()}_renderResizeLayer(e){return this._editorRuntimeOrThrow()._renderResizeLayer(e)}get _openingsR(){const e=this._curSpaceCfg,t=this._spaceH,i=this._spaceModel();return i?(e?.openings||[]).flatMap(e=>{const n={...e,rx:e.x*hf,ry:e.y*t,rlen:e.length*hf};if(!e.host||"wall"===e.host.kind)return[n];const r=Dd(e,i.partitions,hf,this._cellCm,this._gridPitch);if(!r.resolved)return"plan"===this._mode?[{...n,orphanReason:r.reason||"invalid-host"}]:[];let[o,s]=r.resolved.center;const a=this._physicalDrag;return a?.moved&&"partition"===a.kind&&a.id===e.host.id&&(o+=a.delta[0],s+=a.delta[1]),[{...e,rx:o,ry:s,rlen:r.resolved.length,angle:r.resolved.angle,partitionHost:r.resolved}]}):[]}_partitionOpeningCuts(e=this._spaceModel(),t=()=>!0){if(!e)return[];return yp(vp(this._curSpaceCfg?.id===e.id?this._curSpaceCfg:null,e,this._cellCm,this._gridPitch,hf),t)}_roomWallOpeningInputs(e=this._openingsR,t=this._spaceModel()){if(!t)return[];const i=this._openCuts();return bp(e,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,hf)}_openingFace(e,t,i){return e.partitionHost?Od(e.partitionHost,i):Es(t,{x:e.rx,y:e.ry,angle:e.angle,length:e.rlen,flip_v:i})}_cmToUnits(e){return e/this._cellCm*this._gridPitch}get _decorList(){const e=this._curSpaceCfg;return Array.isArray(e?.decor)?e.decor:[]}get _decorH(){return hf}_decorResolvedStyle(e){return function(e,t,i,n=tr){const r="rect"===e?.kind||"ellipse"===e?.kind,o=e,s=Tt(o?.color,n.color);return{color:s,opacity:ir(o?.opacity,n.opacity),widthCm:lr(e,t,i,n.widthCm),fill:!!r&&!0===o?.fill,fillColor:Tt(o?.fill_color,o?.fill?s:n.fillColor),fillOpacity:r&&o?.fill?ir(o?.fill_opacity,.25):n.fillOpacity}}(e,this._cellCm,this._gridPitch,tr)}_decorWidthUnits(e){return((e,t,i,n=tr.widthCm)=>{const r=Number(e?.width_cm);if(Number.isFinite(r)&&r>0)return sr(r,t,i);const o=Number(e?.width);return Number.isFinite(o)&&o>0?o:sr(n,t,i)})(e,this._cellCm,this._gridPitch,tr.widthCm)}_decorTextSizeCm(e){if("text"===e?.kind){const t=Number(e.size_cm);return Number.isFinite(t)&&t>0?t:ar(20*Fi(e),this._cellCm,this._gridPitch)}return ar(20,this._cellCm,this._gridPitch)}_decorTextUnits(e){if("text"!==e.kind)return 20;const t=Number(e.size_cm);return Number.isFinite(t)&&t>0?sr(t,this._cellCm,this._gridPitch):20*Fi(e)}_decorSmallField(e){return Math.round(100*(this._imperial?e/2.54:e))/100}_decorSmallCm(e){const t=this._imperial?2.54*e:e;return Number.isFinite(t)?Math.max(.1,Math.min(100,t)):.1}_decorTextCm(e){const t=this._imperial?2.54*e:e;return Number.isFinite(t)?Math.max(.1,Math.min(2e3,t)):.1}_decorLargeField(e){return Math.round(100*(this._imperial?e/30.48:e/100))/100}_decorLargeCm(e){const t=this._imperial?30.48*e:100*e;return Number.isFinite(t)?Math.max(.1,Math.min(Sa*this._cellCm,t)):.1}_angleField(e){const t=Number(e);return Number.isFinite(t)?String(Number(t.toFixed(3))):"0"}_decorBoxOf(e){return"rect"!==e.kind&&"ellipse"!==e.kind&&"furniture"!==e.kind?null:{x:e.x*hf,y:e.y*this._decorH,w:e.w*hf,h:e.h*this._decorH,angle:nr(e.angle)||void 0}}_decorSnapGeometry(e){return this._editorRuntimeOrThrow()._decorSnapGeometry(e)}_decorSnap(e,t="mouse",i){return this._editorRuntimeOrThrow()._decorSnap(e,t,i)}_replaceDecor(e,t){return this._editorRuntimeOrThrow()._replaceDecor(e,t)}_cancelDecorGesture(){return this._editorRuntimeOrThrow()._cancelDecorGesture()}_decorPointerDown(e){return this._editorRuntimeOrThrow()._decorPointerDown(e)}_decorCommitDraft(){return this._editorRuntimeOrThrow()._decorCommitDraft()}_decorShapeDown(e,t){if(this._editorRuntime)return this._editorRuntime._decorShapeDown(e,t)}_decorMoveUpdate(e){return this._editorRuntimeOrThrow()._decorMoveUpdate(e)}_decorShapeDbl(e,t){if(this._editorRuntime)return this._editorRuntime._decorShapeDbl(e,t)}_openDecorProperties(e){return this._editorRuntimeOrThrow()._openDecorProperties(e)}_decorOpenText(e){return this._editorRuntimeOrThrow()._decorOpenText(e)}_decorRememberTextSelection(e){return this._editorRuntimeOrThrow()._decorRememberTextSelection(e)}_decorInsertLiveVariable(e){return this._editorRuntimeOrThrow()._decorInsertLiveVariable(e)}_decorSaveText(){return this._editorRuntimeOrThrow()._decorSaveText()}_decorSaveShape(){return this._editorRuntimeOrThrow()._decorSaveShape()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(e=>e.id===this._decorSel)||null}_dtPivot(e){return this._editorRuntimeOrThrow()._dtPivot(e)}_dtApply(e,t){return this._editorRuntimeOrThrow()._dtApply(e,t)}_dtStart(e,t,i,n){return this._editorRuntimeOrThrow()._dtStart(e,t,i,n)}_dtMove(e){return this._editorRuntimeOrThrow()._dtMove(e)}_dtUp(){return this._editorRuntimeOrThrow()._dtUp()}_dtMeasure(){return this._editorRuntimeOrThrow()._dtMeasure()}_deleteDecor(e){return this._editorRuntimeOrThrow()._deleteDecor(e)}_decorDeleteSel(){return this._editorRuntimeOrThrow()._decorDeleteSel()}_confirmDecorErase(){return this._editorRuntimeOrThrow()._confirmDecorErase()}get _furnWalls(){const e=this._rawPhysicalBodiesR().flatMap(e=>e.map((t,i)=>{const n=e[(i+1)%e.length];return[t[0],t[1],n[0],n[1]]}));return[...this._segments,...e]}get _furnWallReach(){return 6*this._gridPitch}_furnFieldValue(e){return this._editorRuntimeOrThrow()._furnFieldValue(e)}_furnFieldToCm(e){return this._editorRuntimeOrThrow()._furnFieldToCm(e)}_furnPick(e){return this._editorRuntimeOrThrow()._furnPick(e)}_furnPlace(e,t=!1){return this._editorRuntimeOrThrow()._furnPlace(e,t)}_furnMoveUpdate(e){return this._editorRuntimeOrThrow()._furnMoveUpdate(e)}_decorApplyBox(e,t){return this._editorRuntimeOrThrow()._decorApplyBox(e,t)}get _furnLive(){const e=this._dtDrag;if(!e||"scale"!==e.kind||!e.orig)return null;const t=this._decorList.find(t=>t.id===e.id);if(!t||"furniture"!==t.kind)return null;const i=hf,n=this._decorH,r=t.w*i,o=t.h*n,s=function(e,t,i,n,r){const o=e+i/2,s=t+n/2,a=(Number(r)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=(e,t)=>{const i=e-o,n=t-s;return[o+i*l-n*c,s+i*c+n*l]};return[h(e,t),h(e+i,t),h(e+i,t+n),h(e,t+n)]}(t.x*i,t.y*n,r,o,Number(t.angle)||0),a=(e,t)=>[(e[0]+t[0])/2,(e[1]+t[1])/2],l=a(s[0],s[1]),c=a(s[0],s[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[r,0])},{x:c[0],y:c[1],text:this._fmtLen([0,0],[0,o])}]}_renderFurnPalette(){return this._editorRuntimeOrThrow()._renderFurnPalette()}get _bdBase(){const e=this._curSpaceCfg;return e?.plan_url?{...ya(e.plan_aspect,hf)}:null}get _bdRect(){const e=this._curSpaceCfg;return e?.plan_url?ka(e,hf):null}get _bdParams(){const e=this._curSpaceCfg,t=Number(e?.plan_x),i=Number(e?.plan_y),n=Number(e?.plan_scale),r=Number(e?.plan_scale_x),o=Number(e?.plan_scale_y),s=Number.isFinite(n)&&n>0?n:1;return{dx:Number.isFinite(t)?t:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(r)&&r>0?r:s,sy:Number.isFinite(o)&&o>0?o:s,angle:nr(e?.plan_angle)}}_openBackdropDialog(e){if(this._editorRuntime)return this._editorRuntime._openBackdropDialog(e)}_saveBackdropDialog(){return this._editorRuntimeOrThrow()._saveBackdropDialog()}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&"backdrop"===this._decorTool}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(e,t,i,n,r){return this._editorRuntimeOrThrow()._bdApply(e,t,i,n,r)}_bdStart(e,t,i=!1){return this._editorRuntimeOrThrow()._bdStart(e,t,i)}_bdMove(e){return this._editorRuntimeOrThrow()._bdMove(e)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const e=this._bdParams;return 0!==e.dx||0!==e.dy||1!==e.sx||1!==e.sy||0!==e.angle}_bdReset(){return this._editorRuntimeOrThrow()._bdReset()}_bdUp(){return this._editorRuntimeOrThrow()._bdUp()}get _bdLive(){if(!this._bdDrag)return null;const e=this._bdRect;return e?{x:e.x+e.w/2,y:e.y+e.h/2,text:`${this._fmtLen([0,0],[e.w,0])} × ${this._fmtLen([0,0],[0,e.h])}`}:null}_renderBackdropFrame(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderBackdropFrame(e):V}_renderTextFrame(e){const t=this._dtSel,i=this._dtBox;if(!t||!i||i.id!==t.id)return V;const n=.018*Math.max(e.w,e.h),r=n/4;if("line"===t.kind){const e=[t.x1*hf,t.y1*this._decorH],i=[t.x2*hf,t.y2*this._decorH];return j` ${[e,i].map((e,t)=>j`this._dtStart(e,"scale",void 0,t)}> @@ -4597,12 +4597,12 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 cx="${e<0?i.x:i.x+i.w}" cy="${t<0?i.y:i.y+i.h}" r="${n.toFixed(1)}" @pointerdown=${i=>this._dtStart(i,"scale",[e,t])}>`)} - `}get _furniturePreviewPlacement(){const e=this._furnPreviewInput;return e&&this._editorRuntime&&"decor"===this._mode&&"furniture"===this._decorTool&&this._furnPalette&&this._pointerModality.hoverEnabled?this._editorRuntime._resolveFurniturePlacement(e.raw,e.free,"mouse"):null}_renderFurniturePlacementPreview(e){const t=this._furniturePreviewPlacement;if(!t)return V;const i=fl(t.symbol);if(!i)return V;const n=cf,r=this._decorH,o=t.w*n,s=t.h*r,a=t.x*n+o/2,l=t.y*r+s/2,c=(t.angle?`rotate(${t.angle} ${a} ${l}) `:"")+`translate(${t.x*n} ${t.y*r}) scale(${o/i.viewW} ${s/i.viewH})`,h=this._decorStyle,d=gl(sr(h.widthCm,this._cellCm,this._gridPitch),e);return j``}get _furniturePreviewPlacement(){const e=this._furnPreviewInput;return e&&this._editorRuntime&&"decor"===this._mode&&"furniture"===this._decorTool&&this._furnPalette&&this._pointerModality.hoverEnabled?this._editorRuntime._resolveFurniturePlacement(e.raw,e.free,"mouse"):null}_renderFurniturePlacementPreview(e){const t=this._furniturePreviewPlacement;if(!t)return V;const i=fl(t.symbol);if(!i)return V;const n=hf,r=this._decorH,o=t.w*n,s=t.h*r,a=t.x*n+o/2,l=t.y*r+s/2,c=(t.angle?`rotate(${t.angle} ${a} ${l}) `:"")+`translate(${t.x*n} ${t.y*r}) scale(${o/i.viewW} ${s/i.viewH})`,h=this._decorStyle,d=gl(sr(h.widthCm,this._cellCm,this._gridPitch),e);return j``}_renderDecorLayer(){const e=cf,t=this._decorH,i="decor"===this._mode,n=i&&"erase"===this._decorTool,r=this._stageEl,o=this._viewOr(this._baseVb()),s="iso"===this._renderProjection?1:function(e,t,i,n){const r=Number(e),o=Number(t),s=Number(i),a=Number(n);if(![r,o,s,a].every(e=>Number.isFinite(e)&&e>0))return 1;const l=Math.min(r/s,o/a);return Number.isFinite(l)&&l>0?l:1}(r?.clientWidth,r?.clientHeight,o.w,o.h),a=this._decorList.map(r=>{const o="dshape"+(i&&this._decorSel===r.id?" dsel":""),a=this._decorResolvedStyle(r),l=this._decorWidthUnits(r),c=e=>this._decorShapeDown(e,r),h=e=>this._decorShapeDbl(e,r);if("line"===r.kind)return j``}_renderDecorLayer(){const e=hf,t=this._decorH,i="decor"===this._mode,n=i&&"erase"===this._decorTool,r=this._stageEl,o=this._viewOr(this._baseVb()),s="iso"===this._renderProjection?1:function(e,t,i,n){const r=Number(e),o=Number(t),s=Number(i),a=Number(n);if(![r,o,s,a].every(e=>Number.isFinite(e)&&e>0))return 1;const l=Math.min(r/s,o/a);return Number.isFinite(l)&&l>0?l:1}(r?.clientWidth,r?.clientHeight,o.w,o.h),a=this._decorList.map(r=>{const o="dshape"+(i&&this._decorSel===r.id?" dsel":""),a=this._decorResolvedStyle(r),l=this._decorWidthUnits(r),c=e=>this._decorShapeDown(e,r),h=e=>this._decorShapeDbl(e,r);if("line"===r.kind)return j` ${t.lines.map(e=>j``)} - `}_zeroWalls(){const e=this._spaceModel();return e?up(this._curSpaceCfg,e,cf,.02*this._gridPitch):{style:lp(this._curSpaceCfg),lines:[],contour:[],barriers:[],transmissive:[]}}_openCuts(){return this._zeroWalls().contour}_deleteRoomClick(e){return this._editorRuntimeOrThrow()._deleteRoomClick(e)}get _wallKeyPitch(){return Ca}get _spaceWalls(){const e=this._curSpaceCfg?.walls;return Array.isArray(e)?e:[]}_cfgOpenCuts(){const e=this._curSpaceCfg,t=this._spaceModel();if(!e||!t)return[];const i=cp(e,1),n=dp(e,t.rooms,cf,.02*this._gridPitch).map(e=>e.map(e=>e/cf)),r=new Set;return[...i,...n].filter(e=>{const t=e.map(e=>Number(e).toFixed(9)).join(","),i=[e[2],e[3],e[0],e[1]].map(e=>Number(e).toFixed(9)).join(","),n=t[e[0]/cf,e[1]/cf,e[2]/cf,e[3]/cf]))}_paperShapes(e){if(!this._spaceWalls.length)return Ht(e);const t=this._wallUnionGeometry();return t?.paperD?[{path:t.paperD}]:Ht(e)}_wallUnionGeometry(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._physicalBodiesR();if(!t.length&&!i.length)return null;const n=`${this._space}|${this._cfgEpoch}|${e.rooms.length}`;if(!this._wallUnionCache||this._wallUnionCache.key!==n){const r=Y_(this._wallUnionPool,n);if(r.hit)this._wallUnionCache=r.value;else{const r=this._openCuts(),o=this._roomWallOpeningInputs(),s=Cs(e.rooms,t,r,o,this._wallKeyPitch,this._cellCm,this._gridPitch,cf,i);s&&Object.defineProperty(s,"sourceFingerprint",{value:Rl([this._curSpaceCfg,this._cellCm,this._gridPitch]),enumerable:!1});const a={key:n,value:s};X_(this._wallUnionPool,n,a,8),this._wallUnionCache=a}}return this._wallUnionCache.value}_thickWallCuts(){const e=this._spaceModel();if(!e)return[];const t=this._spaceWalls;if(!t.length)return[];const i=this._openCuts();return zs(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,cf).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]])}_innerRoomContour(e,t,i=this._openCuts(),n=this._wallUnionGeometry()?.roomGeom,r=this._wallUnionGeometry()?.multiWallNodes){const o=i.map(e=>e.join(",")).join(";"),s=`${e.id}|${this._cfgEpoch}|${t}|${o}`,a=Y_(this._innerContourCache,s);if(a.hit)return a.value;const l=us(e.rooms,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,cf,n,r);return X_(this._innerContourCache,s,l,600),l}_wallThickHit(e){return this._editorRuntimeOrThrow()._wallThickHit(e)}get _wallThickHover(){if(!this._markup||"wallthick"!==this._tool||!this._cursorPt||this._wallDialog)return null;const e=this._wallThickHit(this._cursorPt);if(!e)return null;const t=function(e,t,i){const n=Number.isFinite(e)&&e>0?e:0,r=Number.isFinite(t)&&t>0?t:5,o=Number.isFinite(i)&&i>0?i:0;return n>0?ao(n,r,o)/2:ma(1.5*o,r)}(e.cm,this._cellCm,this._gridPitch);let i="";for(const n of e.segs)i+=(i?" ":"")+Ho([[n[0],n[1]],[n[2],n[3]]],t,!1);return{segs:e.segs,open:e.open,d:i}}_wallThickClick(e){return this._editorRuntimeOrThrow()._wallThickClick(e)}_wallThickApply(e){return this._editorRuntimeOrThrow()._wallThickApply(e)}_wallHatchDefs(e){const t=no(this._cellCm);return j` + `}_zeroWalls(){const e=this._spaceModel();return e?pp(this._curSpaceCfg,e,hf,.02*this._gridPitch):{style:cp(this._curSpaceCfg),lines:[],contour:[],barriers:[],transmissive:[]}}_openCuts(){return this._zeroWalls().contour}_deleteRoomClick(e){return this._editorRuntimeOrThrow()._deleteRoomClick(e)}get _wallKeyPitch(){return Ca}get _spaceWalls(){const e=this._curSpaceCfg?.walls;return Array.isArray(e)?e:[]}_cfgOpenCuts(){const e=this._curSpaceCfg,t=this._spaceModel();if(!e||!t)return[];const i=hp(e,1),n=up(e,t.rooms,hf,.02*this._gridPitch).map(e=>e.map(e=>e/hf)),r=new Set;return[...i,...n].filter(e=>{const t=e.map(e=>Number(e).toFixed(9)).join(","),i=[e[2],e[3],e[0],e[1]].map(e=>Number(e).toFixed(9)).join(","),n=t[e[0]/hf,e[1]/hf,e[2]/hf,e[3]/hf]))}_paperShapes(e){if(!this._spaceWalls.length)return Ht(e);const t=this._wallUnionGeometry();return t?.paperD?[{path:t.paperD}]:Ht(e)}_wallUnionGeometry(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._physicalBodiesR();if(!t.length&&!i.length)return null;const n=`${this._space}|${this._cfgEpoch}|${e.rooms.length}`;if(!this._wallUnionCache||this._wallUnionCache.key!==n){const r=X_(this._wallUnionPool,n);if(r.hit)this._wallUnionCache=r.value;else{const r=this._openCuts(),o=this._roomWallOpeningInputs(),s=Cs(e.rooms,t,r,o,this._wallKeyPitch,this._cellCm,this._gridPitch,hf,i);s&&Object.defineProperty(s,"sourceFingerprint",{value:Rl([this._curSpaceCfg,this._cellCm,this._gridPitch]),enumerable:!1});const a={key:n,value:s};Z_(this._wallUnionPool,n,a,8),this._wallUnionCache=a}}return this._wallUnionCache.value}_thickWallCuts(){const e=this._spaceModel();if(!e)return[];const t=this._spaceWalls;if(!t.length)return[];const i=this._openCuts();return zs(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,hf).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]])}_innerRoomContour(e,t,i=this._openCuts(),n=this._wallUnionGeometry()?.roomGeom,r=this._wallUnionGeometry()?.multiWallNodes){const o=i.map(e=>e.join(",")).join(";"),s=`${e.id}|${this._cfgEpoch}|${t}|${o}`,a=X_(this._innerContourCache,s);if(a.hit)return a.value;const l=us(e.rooms,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,hf,n,r);return Z_(this._innerContourCache,s,l,600),l}_wallThickHit(e){return this._editorRuntimeOrThrow()._wallThickHit(e)}get _wallThickHover(){if(!this._markup||"wallthick"!==this._tool||!this._cursorPt||this._wallDialog)return null;const e=this._wallThickHit(this._cursorPt);if(!e)return null;const t=function(e,t,i){const n=Number.isFinite(e)&&e>0?e:0,r=Number.isFinite(t)&&t>0?t:5,o=Number.isFinite(i)&&i>0?i:0;return n>0?ao(n,r,o)/2:ma(1.5*o,r)}(e.cm,this._cellCm,this._gridPitch);let i="";for(const n of e.segs)i+=(i?" ":"")+Ho([[n[0],n[1]],[n[2],n[3]]],t,!1);return{segs:e.segs,open:e.open,d:i}}_wallThickClick(e){return this._editorRuntimeOrThrow()._wallThickClick(e)}_wallThickApply(e){return this._editorRuntimeOrThrow()._wallThickApply(e)}_wallHatchDefs(e){const t=no(this._cellCm);return j` - `}_resolvedRoomFills(e,t){const i=new Map,n=new Map;for(const r of e.rooms){const e=this._roomDialog&&r.id===this._roomEditId?this._roomFill||t.fill:fn(t.fill,r),o=this._roomDialog&&r.id===this._roomEditId?this._roomCustomFill||t.customFill:Bi(t.customFill,r),s=Yi(e,"lqi"===e&&r.area?this._roomLqi(r.area):null,"light"===e?Ch(Rh(this._renderPlanHass,this._renderDevices,r,this._virtualLights)):"none","temp"===e?this._roomTemp(r):null,t.tempMin,t.tempMax,this._fillColors,o);i.set(r,s),r.id&&n.set(r.id,s)}return{byRoom:i,byId:n}}_spaceDisplayForRender(){const e=ji(this._curSpaceCfg),t=this._spaceDialog;return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,showBorders:t.showBorders,showNames:t.showNames,hideDecor:t.hideDecor,hideOpenings:t.hideOpenings,color:t.roomColor,opacity:t.roomOpacity,fill:t.fillMode,customFill:t.customFill?qi(t.customFill):Wi,glow:t.glowEnabled,tempMin:t.tempMin,tempMax:t.tempMax,showLqi:t.showLqi,cardFontScale:t.cardFontScale,labelTemp:t.labelTemp,labelHum:t.labelHum,labelLqi:t.labelLqi,labelLight:t.labelLight}:e}_openingWallIndexFor(e,t){const i=e.rooms.map(e=>`${e.id}:${e.poly?.map(e=>e.join(",")).join("/")||`${e.x},${e.y},${e.w},${e.h}`}`).join(";"),n=this._spaceWalls.map(e=>`${e.key}:${e.a?.join(",")||""}:${e.b?.join(",")||""}:${e.cm}`).join(";"),r=t.map(e=>e.join(",")).join(";"),o=[e.id,this._cfgEpoch,this._wallKeyPitch,this._cellCm,this._gridPitch,i,n,r].join("|");let s=this._openingWallIndexCache.get(o);return s||(s=Ps(e.rooms,this._spaceWalls,t,this._wallKeyPitch,this._cellCm,this._gridPitch,cf)),X_(this._openingWallIndexCache,o,s,4),{key:o,value:s}}_renderOpeningTunnelFills(e,t,i="data"){if(this._markup||!this._spaceWalls.length||!this._openingsR.length)return j``;if("glow-base"===i&&![...t.byRoom.values()].some(Boolean))return j``;const n=this._openCuts(),r=this._openingsR.map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=r.map(e=>`${e.x},${e.y},${e.angle},${e.length}`).join(";"),s=this._openingWallIndexFor(e,n),a=`${s.key}|${o}`;return this._openingTunnelCache&&this._openingTunnelCache.key===a||(this._openingTunnelCache={key:a,value:Ls(s.value,r)}),Zu({openings:this._openingsR,geometries:this._openingTunnelCache.value,fillsByRoomId:t.byId,idPrefix:`${e.id}-${i}`,groupClass:"data"===i?"opening-tunnels":"opening-tunnels glow-base-tunnels",dataLayer:i})}_resolvedGlowBase(e,t,i){const n=new Map,r=new Map,o=this._fillColors.glow_base;for(const s of e.rooms){const e=i.byRoom.get(s),a=gn(t.glow,s)&&(!e||e.opacity<=0)?{color:o.c,opacity:o.a,mode:"glow"}:null;n.set(s,a),s.id&&r.set(s.id,a)}return{byRoom:n,byId:r}}_renderGlowBaseRooms(e,t){if(this._markup||![...t.byRoom.values()].some(Boolean))return j``;const i=new Map(e.rooms.map(e=>[e,Et(e)])),n=this._openCuts(),r=this._wallUnionGeometry()?.roomGeom,o=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",s=e.rooms.map(s=>{const a=t.byRoom.get(s)||null,l=i.get(s)||null;if(!a||!l)return V;const c=this._spaceWalls.length&&s.id&&this._innerRoomContour(e,s.id,n,r)||l,h=ai(c,e.rooms.filter(e=>e!==s).map(e=>i.get(e)).filter(e=>!!e)),d=this._cleanFloor(s,c,e).path;return d||h.length?j``}_resolvedRoomFills(e,t){const i=new Map,n=new Map;for(const r of e.rooms){const e=this._roomDialog&&r.id===this._roomEditId?this._roomFill||t.fill:fn(t.fill,r),o=this._roomDialog&&r.id===this._roomEditId?this._roomCustomFill||t.customFill:Bi(t.customFill,r),s=Yi(e,"lqi"===e&&r.area?this._roomLqi(r.area):null,"light"===e?Ch(Rh(this._renderPlanHass,this._renderDevices,r,this._virtualLights)):"none","temp"===e?this._roomTemp(r):null,t.tempMin,t.tempMax,this._fillColors,o);i.set(r,s),r.id&&n.set(r.id,s)}return{byRoom:i,byId:n}}_spaceDisplayForRender(){const e=ji(this._curSpaceCfg),t=this._spaceDialog;return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,showBorders:t.showBorders,showNames:t.showNames,hideDecor:t.hideDecor,hideOpenings:t.hideOpenings,color:t.roomColor,opacity:t.roomOpacity,fill:t.fillMode,customFill:t.customFill?qi(t.customFill):Wi,glow:t.glowEnabled,tempMin:t.tempMin,tempMax:t.tempMax,showLqi:t.showLqi,cardFontScale:t.cardFontScale,labelTemp:t.labelTemp,labelHum:t.labelHum,labelLqi:t.labelLqi,labelLight:t.labelLight}:e}_openingWallIndexFor(e,t){const i=e.rooms.map(e=>`${e.id}:${e.poly?.map(e=>e.join(",")).join("/")||`${e.x},${e.y},${e.w},${e.h}`}`).join(";"),n=this._spaceWalls.map(e=>`${e.key}:${e.a?.join(",")||""}:${e.b?.join(",")||""}:${e.cm}`).join(";"),r=t.map(e=>e.join(",")).join(";"),o=[e.id,this._cfgEpoch,this._wallKeyPitch,this._cellCm,this._gridPitch,i,n,r].join("|");let s=this._openingWallIndexCache.get(o);return s||(s=Ps(e.rooms,this._spaceWalls,t,this._wallKeyPitch,this._cellCm,this._gridPitch,hf)),Z_(this._openingWallIndexCache,o,s,4),{key:o,value:s}}_renderOpeningTunnelFills(e,t,i="data"){if(this._markup||!this._spaceWalls.length||!this._openingsR.length)return j``;if("glow-base"===i&&![...t.byRoom.values()].some(Boolean))return j``;const n=this._openCuts(),r=this._openingsR.map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=r.map(e=>`${e.x},${e.y},${e.angle},${e.length}`).join(";"),s=this._openingWallIndexFor(e,n),a=`${s.key}|${o}`;return this._openingTunnelCache&&this._openingTunnelCache.key===a||(this._openingTunnelCache={key:a,value:Ls(s.value,r)}),Ju({openings:this._openingsR,geometries:this._openingTunnelCache.value,fillsByRoomId:t.byId,idPrefix:`${e.id}-${i}`,groupClass:"data"===i?"opening-tunnels":"opening-tunnels glow-base-tunnels",dataLayer:i})}_resolvedGlowBase(e,t,i){const n=new Map,r=new Map,o=this._fillColors.glow_base;for(const s of e.rooms){const e=i.byRoom.get(s),a=gn(t.glow,s)&&(!e||e.opacity<=0)?{color:o.c,opacity:o.a,mode:"glow"}:null;n.set(s,a),s.id&&r.set(s.id,a)}return{byRoom:n,byId:r}}_renderGlowBaseRooms(e,t){if(this._markup||![...t.byRoom.values()].some(Boolean))return j``;const i=new Map(e.rooms.map(e=>[e,Et(e)])),n=this._openCuts(),r=this._wallUnionGeometry()?.roomGeom,o=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",s=e.rooms.map(s=>{const a=t.byRoom.get(s)||null,l=i.get(s)||null;if(!a||!l)return V;const c=this._spaceWalls.length&&s.id&&this._innerRoomContour(e,s.id,n,r)||l,h=ai(c,e.rooms.filter(e=>e!==s).map(e=>i.get(e)).filter(e=>!!e)),d=this._cleanFloor(s,c,e).path;return d||h.length?j``:s.poly||c!==l?j``)} - `}_isoDecorationLayers(e){const t="undefined"==typeof CSS||"function"!=typeof CSS.supports||CSS.supports("filter","blur(1px)"),i="function"==typeof matchMedia&&matchMedia("(forced-colors: active)").matches;return function(e){const t=!!e.showBorders;return{structural:t,panels:t&&!e.hideOpenings,shadows:t&&e.filtersSupported&&!e.forcedColors,materialNuance:t&&!e.forcedColors,floorSymbols:!e.hideOpenings&&!t}}({showBorders:e.showBorders,hideOpenings:e.hideOpenings,filtersSupported:t,forcedColors:i})}_isoOpeningPanels(e){if(!e.panels)return[];const t=this._isoScene();if(!t)return[];return t.openings.flatMap(e=>{const t=this._openingsR[e.sourceIndex];return t?O_(e,this._openingAmt(t)):[]}).sort((e,t)=>e.depth-t.depth||e.sourceIndex-t.sourceIndex||e.leaf-t.leaf)}_renderIsoDefs(e,t){const i=pa(this._cellCm);return j` + `}_isoDecorationLayers(e){const t="undefined"==typeof CSS||"function"!=typeof CSS.supports||CSS.supports("filter","blur(1px)"),i="function"==typeof matchMedia&&matchMedia("(forced-colors: active)").matches;return function(e){const t=!!e.showBorders;return{structural:t,panels:t&&!e.hideOpenings,shadows:t&&e.filtersSupported&&!e.forcedColors,materialNuance:t&&!e.forcedColors,floorSymbols:!e.hideOpenings&&!t}}({showBorders:e.showBorders,hideOpenings:e.hideOpenings,filtersSupported:t,forcedColors:i})}_isoOpeningPanels(e){if(!e.panels)return[];const t=this._isoScene();if(!t)return[];return t.openings.flatMap(e=>{const t=this._openingsR[e.sourceIndex];return t?F_(e,this._openingAmt(t)):[]}).sort((e,t)=>e.depth-t.depth||e.sourceIndex-t.sourceIndex||e.leaf-t.leaf)}_renderIsoDefs(e,t){const i=pa(this._cellCm);return j` ${"walls"===t&&e.materialNuance?j` @@ -4710,12 +4710,12 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 ${e.panels?j``:V} - `:j``}_roomHoverPaths(e){const t=this._hoverRoom;if("view"!==this._mode||!t||t.space!==e.id)return null;const i=e.rooms.find(e=>e===t.room||!!e.id&&e.id===t.room.id);if(!i)return null;const n=Et(i);if(!n)return null;const r=e.rooms.filter(e=>e!==i).map(e=>({room:e,poly:Et(e)})).filter(e=>!!e.poly),o=ai(n,r.map(e=>e.poly)),s=this._openCuts(),a=.02*this._gridPitch,l=s.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return n.some((e,i)=>xn(t,[e[0],e[1],...n[(i+1)%n.length]])<=4*a)}),c=this._spaceWalls,h=this._wallUnionGeometry()?.roomGeom,d=c.length&&i.id&&this._innerRoomContour(e,i.id,s,h)||n,u=[{axis:n,face:d}];for(const t of o){const i=r.find(e=>e.poly===t)?.room;let n=t;if(c.length&&i?.id){const r=os(e.rooms,i.id,c,s,this._wallKeyPitch,this._cellCm,this._gridPitch,cf);r&&(n=As(r.poly,r.offsets)||t)}u.push({axis:t,face:n})}const p=this._roomWallOpeningInputs(this._openingsR,e).map(e=>{const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,n=Math.sin(t)*e.length/2;return[e.x-i,e.y-n,e.x+i,e.y+n]}),m=l.concat(p),_=u.map(({axis:e,face:t})=>{const i=m.map(i=>((e,t,i)=>{const n=e[2]-e[0],r=e[3]-e[1],o=Math.hypot(n,r);if(o.05)&&xn([c,h],[i[0],i[1],n[0],n[1]])<=4*a){d=!0;break}}if(!d)return null;let u=null;for(let e=0;e.05)continue;const p=xn([c,h],[t[0],t[1],n[0],n[1]]);(!u||p!!e);return i.length?dn(t,i,a).map(e=>`M ${e[0]} ${e[1]} L ${e[2]} ${e[3]}`).join(" "):`M ${t.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`}).filter(Boolean).join(" ");if(!_)return null;const f=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`;return{fillD:[this._cleanFloor(i,d,e).path||f(d),...o.map(f)].join(" "),outlineD:_}}_renderRoomHoverFill(e){return e?j` + `:j``}_roomHoverPaths(e){const t=this._hoverRoom;if("view"!==this._mode||!t||t.space!==e.id)return null;const i=e.rooms.find(e=>e===t.room||!!e.id&&e.id===t.room.id);if(!i)return null;const n=Et(i);if(!n)return null;const r=e.rooms.filter(e=>e!==i).map(e=>({room:e,poly:Et(e)})).filter(e=>!!e.poly),o=ai(n,r.map(e=>e.poly)),s=this._openCuts(),a=.02*this._gridPitch,l=s.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return n.some((e,i)=>xn(t,[e[0],e[1],...n[(i+1)%n.length]])<=4*a)}),c=this._spaceWalls,h=this._wallUnionGeometry()?.roomGeom,d=c.length&&i.id&&this._innerRoomContour(e,i.id,s,h)||n,u=[{axis:n,face:d}];for(const t of o){const i=r.find(e=>e.poly===t)?.room;let n=t;if(c.length&&i?.id){const r=os(e.rooms,i.id,c,s,this._wallKeyPitch,this._cellCm,this._gridPitch,hf);r&&(n=As(r.poly,r.offsets)||t)}u.push({axis:t,face:n})}const p=this._roomWallOpeningInputs(this._openingsR,e).map(e=>{const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,n=Math.sin(t)*e.length/2;return[e.x-i,e.y-n,e.x+i,e.y+n]}),m=l.concat(p),_=u.map(({axis:e,face:t})=>{const i=m.map(i=>((e,t,i)=>{const n=e[2]-e[0],r=e[3]-e[1],o=Math.hypot(n,r);if(o.05)&&xn([c,h],[i[0],i[1],n[0],n[1]])<=4*a){d=!0;break}}if(!d)return null;let u=null;for(let e=0;e.05)continue;const p=xn([c,h],[t[0],t[1],n[0],n[1]]);(!u||p!!e);return i.length?dn(t,i,a).map(e=>`M ${e[0]} ${e[1]} L ${e[2]} ${e[3]}`).join(" "):`M ${t.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`}).filter(Boolean).join(" ");if(!_)return null;const f=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`;return{fillD:[this._cleanFloor(i,d,e).path||f(d),...o.map(f)].join(" "),outlineD:_}}_renderRoomHoverFill(e){return e?j` `:j``}_renderRoomHoverOutline(e){return e?j` - `:j``}_renderWallThickUi(){return this._editorRuntime?this._editorRuntimeOrThrow()._renderWallThickUi():j``}_renderWallThickDialog(){return this._editorRuntimeOrThrow()._renderWallThickDialog()}_openingAt(e){return this._editorRuntimeOrThrow()._openingAt(e)}_resolveOpeningPlacement(e){return this._editorRuntimeOrThrow()._resolveOpeningPlacement(e)}_activateOpeningPlacement(e){return this._editorRuntimeOrThrow()._activateOpeningPlacement(e)}_clearOpeningPlacement(e){return this._editorRuntime?this._editorRuntimeOrThrow()._clearOpeningPlacement(e):(this._openingHoverCandidate=null,this._openingJambBlockCm=null,void(e&&(this._openingPreset=null,this._openingRebindId=null)))}_openingClick(e){return this._editorRuntimeOrThrow()._openingClick(e)}_editOpening(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._editOpening(e)}_opPointerDown(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerDown(e,t)}_opPointerMove(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerMove(e,t)}_opRuler(e,t){return this._editorRuntimeOrThrow()._opRuler(e,t)}_opPointerUp(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerUp(e,t)}_opClick(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opClick(e,t)}_saveOpening(){return this._editorRuntimeOrThrow()._saveOpening()}_deleteOpening(){return this._editorRuntimeOrThrow()._deleteOpening()}_contactCandidates(){return this._editorRuntimeOrThrow()._contactCandidates()}_lockCandidates(){return this._editorRuntimeOrThrow()._lockCandidates()}_toggleOpeningEntityPicker(e){return this._editorRuntimeOrThrow()._toggleOpeningEntityPicker(e)}_filterOpeningEntities(e,t){return this._editorRuntimeOrThrow()._filterOpeningEntities(e,t)}_selectOpeningEntity(e,t){return this._editorRuntimeOrThrow()._selectOpeningEntity(e,t)}_mergeClick(e){return this._editorRuntimeOrThrow()._mergeClick(e)}_commitMerge(){return this._editorRuntimeOrThrow()._commitMerge()}_splitClick(e){return this._editorRuntimeOrThrow()._splitClick(e)}get _contourClosed(){return this._path.length>=4&&this._samePt(this._path[0],this._path[this._path.length-1])&&(null!=this._closingWallCm||!this._activeDraftId)}_markupMove(e){if(this._markup&&this._editorRuntime)return this._editorRuntime._markupMove(e)}get _openingPreview(){const e=this._openingPreset;if("opening"!==this._tool||!e||!this._cursorPt)return null;const t=this._cursorPt,i=this._openingHoverCandidate;if(i&&B_(i,[t[0],t[1]],e.revision,this._cfgEpoch))return i;if(this._openingAt(t))return this._openingHoverCandidate=null,null;const n=this._resolveOpeningPlacement(t);return this._openingHoverCandidate=n,n}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){return this._editorRuntimeOrThrow()._saveRoom()}_decideWallFace(e){return this._editorRuntimeOrThrow()._decideWallFace(e)}_wallSourceCmAt(e,t,i){return this._editorRuntimeOrThrow()._wallSourceCmAt(e,t,i)}_activePathWithRepair(e,t){return this._editorRuntimeOrThrow()._activePathWithRepair(e,t)}_validateWallRepair(e,t){return this._editorRuntimeOrThrow()._validateWallRepair(e,t)}_applyWallRepair(e,t){return this._editorRuntimeOrThrow()._applyWallRepair(e,t)}_applyWallFaceBatch(){return this._editorRuntimeOrThrow()._applyWallFaceBatch()}_commitRoom(){return this._editorRuntimeOrThrow()._commitRoom()}_cancelPath(){return this._editorRuntimeOrThrow()._cancelPath()}_resumeLastDraft(){return this._editorRuntimeOrThrow()._resumeLastDraft()}_roomDialogCancel(){return this._editorRuntimeOrThrow()._roomDialogCancel()}get _freeAreas(){const e=new Set;for(const t of this._serverCfg?.spaces||[])for(const i of t.rooms||[])i.area&&e.add(i.area);for(const t of this._wallFaceBatch?.decisions||[])t.create&&t.area&&e.add(t.area);return Object.values(this.hass?.areas||{}).filter(t=>!e.has(t.area_id)).sort((e,t)=>(e.name||"").localeCompare(t.name||""))}_openDeviceInbox(){return this._editorRuntimeOrThrow()._openDeviceInbox()}_closeMarkerDialog(){return this._editorRuntimeOrThrow()._closeMarkerDialog()}_deviceInboxCandidates(e){return this._editorRuntimeOrThrow()._deviceInboxCandidates(e)}_deviceInboxRows(){return this._editorRuntimeOrThrow()._deviceInboxRows()}_deviceForInboxRow(e){return this._editorRuntimeOrThrow()._deviceForInboxRow(e)}_openInboxMarker(e,t=!1){return this._editorRuntimeOrThrow()._openInboxMarker(e,t)}async _setInboxHidden(e,t){return this._editorRuntimeOrThrow()._setInboxHidden(e,t)}_findInboxDevice(e){return this._editorRuntimeOrThrow()._findInboxDevice(e)}_deviceInboxTabKey(e){return this._editorRuntimeOrThrow()._deviceInboxTabKey(e)}_openMarkerDialog(e){if(this._editorRuntime)return this._editorRuntime._openMarkerDialog(e);this._ensureEditorRuntime().then(t=>{t&&this._openMarkerDialog(e)})}_runCandidates(){return this._editorRuntimeOrThrow()._runCandidates()}_bindingCandidates(){return this._editorRuntimeOrThrow()._bindingCandidates()}_physicalBodiesR(e=this._spaceModel()){if(!e)return[];const t=`${e.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===t)return this._physicalBodiesCache.all;const i=ea(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e));return this._physicalBodiesCache={key:t,...i},i.all}_rawPhysicalBodiesR(e=this._spaceModel()){if(!e)return[];this._physicalBodiesR(e);const t=this._physicalBodiesCache;return t?[...t.drafts,...t.partitions,...t.columns]:[]}_cleanFloor(e,t,i=this._spaceModel()){if(!i)return{floor:t,geom:null,path:"",area:sa([[[...t,t[0]]]])};const n=e.id||`#${i.rooms.indexOf(e)}`,r=`${i.id}|${this._cfgEpoch}|${n}`;if(!this._resize?.preview){const e=Y_(this._cleanFloorCache,r);if(e.hit)return e.value}const o=t.map(e=>e[0]),s=t.map(e=>e[1]),a=[Math.min(...o),Math.min(...s),Math.max(...o),Math.max(...s)],l=this._physicalBodiesR(i).filter(e=>{const t=e.map(e=>e[0]),i=e.map(e=>e[1]);return Math.max(...t)>=a[0]&&Math.min(...t)<=a[2]&&Math.max(...i)>=a[1]&&Math.min(...i)<=a[3]}),c=l.length?oa(t,l):null,h={floor:t,geom:c,path:c?Ks(c):"",area:sa(c||[[[...t,t[0]]]])};return this._resize?.preview||X_(this._cleanFloorCache,r,h,600),h}_autoIconForBinding(e){if("virtual"===e)return"mdi:map-marker";const[t,i]=e.split(":");if(!i)return"";const n=this._fullRegistryHass,r=this._bindingStatus(e),o="active"===r.kind?r.enabledEntityIds:r.allEntityIds;if("device"===t){const e=n.devices?.[i];if(!e)return"mdi:help-circle";const t=o;return t.some(e=>e.startsWith("lock."))?"mdi:lock":Ih(n,e.name_by_user||e.name||"",e.model,t,this._iconRules)}if("entity"===t){const e=n.entities?.[i],t=this.hass.states?.[i],r=e?.name||t?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":Ih(n,r,"",[i],this._iconRules)}return""}_allRoomsFlat(){const e=[];for(const t of this._serverCfg?.spaces||[])for(const i of t.rooms||[])i.area?e.push({value:t.id+"#"+i.area,label:(t.title||t.id)+" · "+i.name}):i.id&&e.push({value:t.id+"#@"+i.id,label:(t.title||t.id)+" · "+i.name+" · "+this._t("marker.subarea")});return e}_errText(e){if(!e)return this._t("err.unknown");if("string"==typeof e)return e;if("invalid_passage_fields"===e.code){const t=String(e.message||e.error||"").match(/space=([^;]*);\s*opening=([^;]*);\s*fields=([^;]*)/);if(t){const e=this._serverCfg?.spaces?.find(e=>String(e.id)===t[1]),i={contact:"opening.contact_label",lock:"opening.lock_label",invert:"opening.invert",flip_h:"opening.flip_h",flip_v:"opening.flip_v"},n=t[3].split(",").filter(Boolean).map(e=>i[e]?this._t(i[e]):e).join(", ");return this._t("opening.invalid_passage_fields",{room:e?.title||t[1],fields:n})}}if("invalid_partition_opening_jamb_margin"===e.code){const t=String(e.message||e.error||"").match(/margin_cm=([^;]*)/),i=t?Number(t[1]):NaN;if(Number.isFinite(i))return this._t("opening.partition_jamb_margin",{distance:Ft(i,this._imperial)})}if(e.message)return e.message;if(e.error)return e.error;if(null!=e.code)return this._t("err.code",{code:e.code});try{return JSON.stringify(e)}catch{return String(e)}}_backupErrorText(e){return this._editorRuntimeOrThrow()._backupErrorText(e)}async _pickMarkerFiles(e){return this._editorRuntimeOrThrow()._pickMarkerFiles(e)}_removeMarkerPdf(e){return this._editorRuntimeOrThrow()._removeMarkerPdf(e)}_markerLightFields(e){return this._editorRuntimeOrThrow()._markerLightFields(e)}_markerTapActionFields(e){return this._editorRuntimeOrThrow()._markerTapActionFields(e)}_markerToggleEntityFields(e){return this._editorRuntimeOrThrow()._markerToggleEntityFields(e)}async _saveMarker(){return this._editorRuntimeOrThrow()._saveMarker()}async _deleteMarker(){return this._editorRuntimeOrThrow()._deleteMarker()}_normPos(e,t,i){return{s:e,x:t/cf,y:i/cf}}_spaceDialogUsesOnboardingRuntime(e){return"create"===e&&!this._editorRuntime&&(!!this._onboardingRuntime||0===(this._serverCfg?.spaces.length||0)||this._importTotal>0||this._importQueue.length>0)}_spaceRuntimeOrThrow(){if(!this._onboardingRuntime)throw new Error("Houseplan onboarding runtime is not loaded");return this._onboardingRuntime}_openSpaceDialog(e,t){return this._spaceDialogUsesOnboardingRuntime(e)?this._onboardingRuntime?void this._onboardingRuntime._openSpaceDialog(e,t):void this._ensureOnboardingRuntime().then(i=>{i&&this._spaceRuntimeOrThrow()._openSpaceDialog(e,t)}):this._editorRuntime?this._editorRuntime._openSpaceDialog(e,t):void this._ensureEditorRuntime().then(i=>{i&&this._openSpaceDialog(e,t)})}async _pickPlanFile(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._pickPlanFile(e):this._editorRuntimeOrThrow()._pickPlanFile(e)}_useServerPlan(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._useServerPlan(e):this._editorRuntimeOrThrow()._useServerPlan(e)}async _readPlanAspect(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._readPlanAspect(e):this._editorRuntimeOrThrow()._readPlanAspect(e)}async _deleteServerPlan(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._deleteServerPlan(e):this._editorRuntimeOrThrow()._deleteServerPlan(e)}_renderServerPlans(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime(e.mode)?this._onboardingRuntime._renderServerPlans(e):this._editorRuntimeOrThrow()._renderServerPlans(e)}async _saveSpaceDialog(){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._saveSpaceDialog():this._editorRuntimeOrThrow()._saveSpaceDialog()}async _deleteSpace(){return this._editorRuntimeOrThrow()._deleteSpace()}async _saveConfigNow(){return this._editorRuntimeOrThrow()._saveConfigNow()}_startImport(){return this._onboardingRuntime?this._onboardingRuntime._startImport():this._editorRuntimeOrThrow()._startImport()}_openNextImport(){return this._onboardingRuntime?this._onboardingRuntime._openNextImport():this._editorRuntimeOrThrow()._openNextImport()}_skipImport(){return this._onboardingRuntime?this._onboardingRuntime._skipImport():this._editorRuntimeOrThrow()._skipImport()}_renderImportDialog(){return this._onboardingRuntime?this._onboardingRuntime._renderImportDialog():this._editorRuntimeOrThrow()._renderImportDialog()}_sunGlobal(){const e=this._settingsDialog;return e?{...this._settings,north_deg:e.northDeg??void 0,bg_mode:e.bgMode,sun_rays:e.sunRays}:this._settings}_sunSpace(){const e=this._spaceDialog,t=this._curSpaceCfg?.settings||{};return e&&"edit"===e.mode&&e.spaceId===this._space?{...t,north_deg:e.northDeg??void 0,bg_mode:e.bgMode??void 0,sun_rays:e.sunRays??void 0}:t}_effNorth(){return Kn(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Yn(this._sunGlobal(),this._sunSpace())}_dayCycleState(e=new Date){return(this._modeTransitionVisual?.viewWeight??("view"===this._mode?1:0))<=0||"daynight"!==this._effBgMode()?null:Hn(this._renderPlanHass,e)}_syncDayCycleClock(){const e=this._dayCycleState();this._dayCycleClockKey=e?Nn(e):"";const t="clock"===e?.source&&"hidden"!==this.ownerDocument.visibilityState&&this.isConnected;t&&!this._dayCycleTimer?this._dayCycleTimer=window.setInterval(this._dayCycleTick,3e4):!t&&this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_dayCycleVisibility(e){"hidden"!==e.kind?(this._dayCycleTick(),this._syncDayCycleClock()):this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_effSunRays(){return Xn(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Zn(this._renderPlanHass):null}_renderSunRays(e){const t=j``,i=this._modeTransitionVisual?.viewWeight??0;if(this._editing&&i<=0||!this._effSunRays())return this._sunFadeReset(),t;const n=this._effNorth(),r=null!==n?Zn(this._renderPlanHass):null;if(!r||r.elevation<=0)return this._sunFadeReset(),t;if(o=r.elevation,Number(o)>=3)this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunOut=!1,this._sunShown=!0;else{if(!this._sunShown)return t;this._sunOut||(this._sunOut=!0,this._sunOutTimer=window.setTimeout(()=>{this._sunOutTimer=0,this._sunShown=!1,this._sunOut=!1,this.requestUpdate()},2e3))}var o;const s=this._zeroWalls(),a=s.barriers.map(e=>e.join(",")).join(";"),l=`${e.id}|${r.azimuth}|${r.elevation}|${n}|${this._cfgEpoch}|${s.style}|${a}`;if(!this._sunRaysCache||this._sunRaysCache.key!==l){const t=e.rooms.map(e=>({id:e.id||"",poly:Et(e)})).filter(e=>!!e.id&&!!e.poly),i=this._openingsR.filter(e=>"window"===e.type&&"partition"!==e.host?.kind).map(e=>({id:e.id,x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=this._spaceWalls,a=this._openCuts(),c=this._openingWallIndexFor(e,a).value,h={},d={},u=this._wallUnionGeometry()?.roomGeom;if(o.length){for(const i of t){const t=this._innerRoomContour(e,i.id,a,u);t&&(h[i.id]=t)}for(const e of i){const t=Es(c,{x:e.x,y:e.y,angle:e.angle,length:e.length});t.cm>0&&(d[e.id]=ao(t.cm,this._cellCm,this._gridPitch))}}let p=Vn(t,i,r.azimuth,r.elevation,n,o.length?h:void 0,o.length?d:void 0);const m=[...this._physicalBodiesR(e),...s.barriers.map(e=>[[e[0],e[1]],[e[2],e[3]]])];m.length&&(p=p.map(e=>{const t=function(e,t,i){return i>0?e.map(e=>function(e){const t=[...e].sort((e,t)=>e[0]-t[0]||e[1]-t[1]);if(t.length<=2)return t;const i=(e,t,i)=>(t[0]-e[0])*(i[1]-e[1])-(t[1]-e[1])*(i[0]-e[0]),n=[];for(const e of t){for(;n.length>=2&&i(n[n.length-2],n[n.length-1],e)<=0;)n.pop();n.push(e)}const r=[];for(let e=t.length-1;e>=0;e--){const n=t[e];for(;r.length>=2&&i(r[r.length-2],r[r.length-1],n)<=0;)r.pop();r.push(n)}return n.slice(0,-1).concat(r.slice(0,-1))}([...e,...e.map(e=>[e[0]+t[0]*i,e[1]+t[1]*i])])).filter(e=>e.length>=3):e}(m,e.dir,e.len),i=e.polys.map(e=>oa(e,t));return{...e,paths:i.map(Ks).filter(Boolean),polys:i.flatMap(la)}}).filter(e=>e.paths?.length||e.polys.length)),this._sunRaysCache={key:l,rays:p,rims:p.map(e=>function(e,t=1e-4){const[i,n]=e.dir,r=-n,o=i,s=[];for(const a of[e.a,e.b]){const l=[];for(const s of e.polys)for(let e=0;et)continue;if(Math.abs((h[0]-a[0])*r+(h[1]-a[1])*o)>t)continue;const d=(c[0]-a[0])*i+(c[1]-a[1])*n,u=(h[0]-a[0])*i+(h[1]-a[1])*n;Math.abs(u-d)<=t||l.push(de[0]-t[0]);const c=[];for(const e of l){const i=c[c.length-1];i&&e[0]<=i[1]+t?i[1]=Math.max(i[1],e[1]):c.push([e[0],e[1]])}for(const[e,t]of c)s.push([[a[0]+i*e,a[1]+n*e],[a[0]+i*t,a[1]+n*t]])}return s}(e))}}const c=this._sunRaysCache.rays,h=this._sunRaysCache.rims;if(!c.length)return t;const d=(u=function(e){const t=Math.min(90,Math.max(-90,Number(e)||0));let i=Ln[Ln.length-1][1];for(let e=1;e + `:j``}_renderWallThickUi(){return this._editorRuntime?this._editorRuntimeOrThrow()._renderWallThickUi():j``}_renderWallThickDialog(){return this._editorRuntimeOrThrow()._renderWallThickDialog()}_openingAt(e){return this._editorRuntimeOrThrow()._openingAt(e)}_resolveOpeningPlacement(e){return this._editorRuntimeOrThrow()._resolveOpeningPlacement(e)}_activateOpeningPlacement(e){return this._editorRuntimeOrThrow()._activateOpeningPlacement(e)}_clearOpeningPlacement(e){return this._editorRuntime?this._editorRuntimeOrThrow()._clearOpeningPlacement(e):(this._openingHoverCandidate=null,this._openingJambBlockCm=null,void(e&&(this._openingPreset=null,this._openingRebindId=null)))}_openingClick(e){return this._editorRuntimeOrThrow()._openingClick(e)}_editOpening(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._editOpening(e)}_opPointerDown(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerDown(e,t)}_opPointerMove(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerMove(e,t)}_opRuler(e,t){return this._editorRuntimeOrThrow()._opRuler(e,t)}_opPointerUp(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerUp(e,t)}_opClick(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opClick(e,t)}_saveOpening(){return this._editorRuntimeOrThrow()._saveOpening()}_deleteOpening(){return this._editorRuntimeOrThrow()._deleteOpening()}_contactCandidates(){return this._editorRuntimeOrThrow()._contactCandidates()}_lockCandidates(){return this._editorRuntimeOrThrow()._lockCandidates()}_toggleOpeningEntityPicker(e){return this._editorRuntimeOrThrow()._toggleOpeningEntityPicker(e)}_filterOpeningEntities(e,t){return this._editorRuntimeOrThrow()._filterOpeningEntities(e,t)}_selectOpeningEntity(e,t){return this._editorRuntimeOrThrow()._selectOpeningEntity(e,t)}_mergeClick(e){return this._editorRuntimeOrThrow()._mergeClick(e)}_commitMerge(){return this._editorRuntimeOrThrow()._commitMerge()}_splitClick(e){return this._editorRuntimeOrThrow()._splitClick(e)}get _contourClosed(){return this._path.length>=4&&this._samePt(this._path[0],this._path[this._path.length-1])&&(null!=this._closingWallCm||!this._activeDraftId)}_markupMove(e){if(this._markup&&this._editorRuntime)return this._editorRuntime._markupMove(e)}get _openingPreview(){const e=this._openingPreset;if("opening"!==this._tool||!e||!this._cursorPt)return null;const t=this._cursorPt,i=this._openingHoverCandidate;if(i&&j_(i,[t[0],t[1]],e.revision,this._cfgEpoch))return i;if(this._openingAt(t))return this._openingHoverCandidate=null,null;const n=this._resolveOpeningPlacement(t);return this._openingHoverCandidate=n,n}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){return this._editorRuntimeOrThrow()._saveRoom()}_decideWallFace(e){return this._editorRuntimeOrThrow()._decideWallFace(e)}_wallSourceCmAt(e,t,i){return this._editorRuntimeOrThrow()._wallSourceCmAt(e,t,i)}_activePathWithRepair(e,t){return this._editorRuntimeOrThrow()._activePathWithRepair(e,t)}_validateWallRepair(e,t){return this._editorRuntimeOrThrow()._validateWallRepair(e,t)}_applyWallRepair(e,t){return this._editorRuntimeOrThrow()._applyWallRepair(e,t)}_applyWallFaceBatch(){return this._editorRuntimeOrThrow()._applyWallFaceBatch()}_commitRoom(){return this._editorRuntimeOrThrow()._commitRoom()}_cancelPath(){return this._editorRuntimeOrThrow()._cancelPath()}_resumeLastDraft(){return this._editorRuntimeOrThrow()._resumeLastDraft()}_roomDialogCancel(){return this._editorRuntimeOrThrow()._roomDialogCancel()}get _freeAreas(){const e=new Set;for(const t of this._serverCfg?.spaces||[])for(const i of t.rooms||[])i.area&&e.add(i.area);for(const t of this._wallFaceBatch?.decisions||[])t.create&&t.area&&e.add(t.area);return Object.values(this.hass?.areas||{}).filter(t=>!e.has(t.area_id)).sort((e,t)=>(e.name||"").localeCompare(t.name||""))}_openDeviceInbox(){return this._editorRuntimeOrThrow()._openDeviceInbox()}_closeMarkerDialog(){return this._editorRuntimeOrThrow()._closeMarkerDialog()}_deviceInboxCandidates(e){return this._editorRuntimeOrThrow()._deviceInboxCandidates(e)}_deviceInboxRows(){return this._editorRuntimeOrThrow()._deviceInboxRows()}_deviceForInboxRow(e){return this._editorRuntimeOrThrow()._deviceForInboxRow(e)}_openInboxMarker(e,t=!1){return this._editorRuntimeOrThrow()._openInboxMarker(e,t)}async _setInboxHidden(e,t){return this._editorRuntimeOrThrow()._setInboxHidden(e,t)}_findInboxDevice(e){return this._editorRuntimeOrThrow()._findInboxDevice(e)}_deviceInboxTabKey(e){return this._editorRuntimeOrThrow()._deviceInboxTabKey(e)}_openMarkerDialog(e){if(this._editorRuntime)return this._editorRuntime._openMarkerDialog(e);this._ensureEditorRuntime().then(t=>{t&&this._openMarkerDialog(e)})}_runCandidates(){return this._editorRuntimeOrThrow()._runCandidates()}_bindingCandidates(){return this._editorRuntimeOrThrow()._bindingCandidates()}_physicalBodiesR(e=this._spaceModel()){if(!e)return[];const t=`${e.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===t)return this._physicalBodiesCache.all;const i=ea(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e));return this._physicalBodiesCache={key:t,...i},i.all}_rawPhysicalBodiesR(e=this._spaceModel()){if(!e)return[];this._physicalBodiesR(e);const t=this._physicalBodiesCache;return t?[...t.drafts,...t.partitions,...t.columns]:[]}_cleanFloor(e,t,i=this._spaceModel()){if(!i)return{floor:t,geom:null,path:"",area:sa([[[...t,t[0]]]])};const n=e.id||`#${i.rooms.indexOf(e)}`,r=`${i.id}|${this._cfgEpoch}|${n}`;if(!this._resize?.preview){const e=X_(this._cleanFloorCache,r);if(e.hit)return e.value}const o=t.map(e=>e[0]),s=t.map(e=>e[1]),a=[Math.min(...o),Math.min(...s),Math.max(...o),Math.max(...s)],l=this._physicalBodiesR(i).filter(e=>{const t=e.map(e=>e[0]),i=e.map(e=>e[1]);return Math.max(...t)>=a[0]&&Math.min(...t)<=a[2]&&Math.max(...i)>=a[1]&&Math.min(...i)<=a[3]}),c=l.length?oa(t,l):null,h={floor:t,geom:c,path:c?Ks(c):"",area:sa(c||[[[...t,t[0]]]])};return this._resize?.preview||Z_(this._cleanFloorCache,r,h,600),h}_autoIconForBinding(e){if("virtual"===e)return"mdi:map-marker";const[t,i]=e.split(":");if(!i)return"";const n=this._fullRegistryHass,r=this._bindingStatus(e),o="active"===r.kind?r.enabledEntityIds:r.allEntityIds;if("device"===t){const e=n.devices?.[i];if(!e)return"mdi:help-circle";const t=o;return t.some(e=>e.startsWith("lock."))?"mdi:lock":Ih(n,e.name_by_user||e.name||"",e.model,t,this._iconRules)}if("entity"===t){const e=n.entities?.[i],t=this.hass.states?.[i],r=e?.name||t?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":Ih(n,r,"",[i],this._iconRules)}return""}_allRoomsFlat(){const e=[];for(const t of this._serverCfg?.spaces||[])for(const i of t.rooms||[])i.area?e.push({value:t.id+"#"+i.area,label:(t.title||t.id)+" · "+i.name}):i.id&&e.push({value:t.id+"#@"+i.id,label:(t.title||t.id)+" · "+i.name+" · "+this._t("marker.subarea")});return e}_errText(e){if(!e)return this._t("err.unknown");if("string"==typeof e)return e;if("invalid_passage_fields"===e.code){const t=String(e.message||e.error||"").match(/space=([^;]*);\s*opening=([^;]*);\s*fields=([^;]*)/);if(t){const e=this._serverCfg?.spaces?.find(e=>String(e.id)===t[1]),i={contact:"opening.contact_label",lock:"opening.lock_label",invert:"opening.invert",flip_h:"opening.flip_h",flip_v:"opening.flip_v"},n=t[3].split(",").filter(Boolean).map(e=>i[e]?this._t(i[e]):e).join(", ");return this._t("opening.invalid_passage_fields",{room:e?.title||t[1],fields:n})}}if("invalid_partition_opening_jamb_margin"===e.code){const t=String(e.message||e.error||"").match(/margin_cm=([^;]*)/),i=t?Number(t[1]):NaN;if(Number.isFinite(i))return this._t("opening.partition_jamb_margin",{distance:Ft(i,this._imperial)})}if(e.message)return e.message;if(e.error)return e.error;if(null!=e.code)return this._t("err.code",{code:e.code});try{return JSON.stringify(e)}catch{return String(e)}}_backupErrorText(e){return this._editorRuntimeOrThrow()._backupErrorText(e)}async _pickMarkerFiles(e){return this._editorRuntimeOrThrow()._pickMarkerFiles(e)}_removeMarkerPdf(e){return this._editorRuntimeOrThrow()._removeMarkerPdf(e)}_markerLightFields(e){return this._editorRuntimeOrThrow()._markerLightFields(e)}_markerTapActionFields(e){return this._editorRuntimeOrThrow()._markerTapActionFields(e)}_markerToggleEntityFields(e){return this._editorRuntimeOrThrow()._markerToggleEntityFields(e)}async _saveMarker(){return this._editorRuntimeOrThrow()._saveMarker()}async _deleteMarker(){return this._editorRuntimeOrThrow()._deleteMarker()}_normPos(e,t,i){return{s:e,x:t/hf,y:i/hf}}_spaceDialogUsesOnboardingRuntime(e){return"create"===e&&!this._editorRuntime&&(!!this._onboardingRuntime||0===(this._serverCfg?.spaces.length||0)||this._importTotal>0||this._importQueue.length>0)}_spaceRuntimeOrThrow(){if(!this._onboardingRuntime)throw new Error("Houseplan onboarding runtime is not loaded");return this._onboardingRuntime}_openSpaceDialog(e,t){return this._spaceDialogUsesOnboardingRuntime(e)?this._onboardingRuntime?void this._onboardingRuntime._openSpaceDialog(e,t):void this._ensureOnboardingRuntime().then(i=>{i&&this._spaceRuntimeOrThrow()._openSpaceDialog(e,t)}):this._editorRuntime?this._editorRuntime._openSpaceDialog(e,t):void this._ensureEditorRuntime().then(i=>{i&&this._openSpaceDialog(e,t)})}async _pickPlanFile(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._pickPlanFile(e):this._editorRuntimeOrThrow()._pickPlanFile(e)}_useServerPlan(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._useServerPlan(e):this._editorRuntimeOrThrow()._useServerPlan(e)}async _readPlanAspect(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._readPlanAspect(e):this._editorRuntimeOrThrow()._readPlanAspect(e)}async _deleteServerPlan(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._deleteServerPlan(e):this._editorRuntimeOrThrow()._deleteServerPlan(e)}_renderServerPlans(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime(e.mode)?this._onboardingRuntime._renderServerPlans(e):this._editorRuntimeOrThrow()._renderServerPlans(e)}async _saveSpaceDialog(){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._saveSpaceDialog():this._editorRuntimeOrThrow()._saveSpaceDialog()}async _deleteSpace(){return this._editorRuntimeOrThrow()._deleteSpace()}async _saveConfigNow(){return this._editorRuntimeOrThrow()._saveConfigNow()}_startImport(){return this._onboardingRuntime?this._onboardingRuntime._startImport():this._editorRuntimeOrThrow()._startImport()}_openNextImport(){return this._onboardingRuntime?this._onboardingRuntime._openNextImport():this._editorRuntimeOrThrow()._openNextImport()}_skipImport(){return this._onboardingRuntime?this._onboardingRuntime._skipImport():this._editorRuntimeOrThrow()._skipImport()}_renderImportDialog(){return this._onboardingRuntime?this._onboardingRuntime._renderImportDialog():this._editorRuntimeOrThrow()._renderImportDialog()}_sunGlobal(){const e=this._settingsDialog;return e?{...this._settings,north_deg:e.northDeg??void 0,bg_mode:e.bgMode,sun_rays:e.sunRays}:this._settings}_sunSpace(){const e=this._spaceDialog,t=this._curSpaceCfg?.settings||{};return e&&"edit"===e.mode&&e.spaceId===this._space?{...t,north_deg:e.northDeg??void 0,bg_mode:e.bgMode??void 0,sun_rays:e.sunRays??void 0}:t}_effNorth(){return Kn(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Yn(this._sunGlobal(),this._sunSpace())}_dayCycleState(e=new Date){return(this._modeTransitionVisual?.viewWeight??("view"===this._mode?1:0))<=0||"daynight"!==this._effBgMode()?null:Hn(this._renderPlanHass,e)}_syncDayCycleClock(){const e=this._dayCycleState();this._dayCycleClockKey=e?Nn(e):"";const t="clock"===e?.source&&"hidden"!==this.ownerDocument.visibilityState&&this.isConnected;t&&!this._dayCycleTimer?this._dayCycleTimer=window.setInterval(this._dayCycleTick,3e4):!t&&this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_dayCycleVisibility(e){"hidden"!==e.kind?(this._dayCycleTick(),this._syncDayCycleClock()):this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_effSunRays(){return Xn(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Zn(this._renderPlanHass):null}_renderSunRays(e){const t=j``,i=this._modeTransitionVisual?.viewWeight??0;if(this._editing&&i<=0||!this._effSunRays())return this._sunFadeReset(),t;const n=this._effNorth(),r=null!==n?Zn(this._renderPlanHass):null;if(!r||r.elevation<=0)return this._sunFadeReset(),t;if(o=r.elevation,Number(o)>=3)this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunOut=!1,this._sunShown=!0;else{if(!this._sunShown)return t;this._sunOut||(this._sunOut=!0,this._sunOutTimer=window.setTimeout(()=>{this._sunOutTimer=0,this._sunShown=!1,this._sunOut=!1,this.requestUpdate()},2e3))}var o;const s=this._zeroWalls(),a=s.barriers.map(e=>e.join(",")).join(";"),l=`${e.id}|${r.azimuth}|${r.elevation}|${n}|${this._cfgEpoch}|${s.style}|${a}`;if(!this._sunRaysCache||this._sunRaysCache.key!==l){const t=e.rooms.map(e=>({id:e.id||"",poly:Et(e)})).filter(e=>!!e.id&&!!e.poly),i=this._openingsR.filter(e=>"window"===e.type&&"partition"!==e.host?.kind).map(e=>({id:e.id,x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=this._spaceWalls,a=this._openCuts(),c=this._openingWallIndexFor(e,a).value,h={},d={},u=this._wallUnionGeometry()?.roomGeom;if(o.length){for(const i of t){const t=this._innerRoomContour(e,i.id,a,u);t&&(h[i.id]=t)}for(const e of i){const t=Es(c,{x:e.x,y:e.y,angle:e.angle,length:e.length});t.cm>0&&(d[e.id]=ao(t.cm,this._cellCm,this._gridPitch))}}let p=Vn(t,i,r.azimuth,r.elevation,n,o.length?h:void 0,o.length?d:void 0);const m=[...this._physicalBodiesR(e),...s.barriers.map(e=>[[e[0],e[1]],[e[2],e[3]]])];m.length&&(p=p.map(e=>{const t=function(e,t,i){return i>0?e.map(e=>function(e){const t=[...e].sort((e,t)=>e[0]-t[0]||e[1]-t[1]);if(t.length<=2)return t;const i=(e,t,i)=>(t[0]-e[0])*(i[1]-e[1])-(t[1]-e[1])*(i[0]-e[0]),n=[];for(const e of t){for(;n.length>=2&&i(n[n.length-2],n[n.length-1],e)<=0;)n.pop();n.push(e)}const r=[];for(let e=t.length-1;e>=0;e--){const n=t[e];for(;r.length>=2&&i(r[r.length-2],r[r.length-1],n)<=0;)r.pop();r.push(n)}return n.slice(0,-1).concat(r.slice(0,-1))}([...e,...e.map(e=>[e[0]+t[0]*i,e[1]+t[1]*i])])).filter(e=>e.length>=3):e}(m,e.dir,e.len),i=e.polys.map(e=>oa(e,t));return{...e,paths:i.map(Ks).filter(Boolean),polys:i.flatMap(la)}}).filter(e=>e.paths?.length||e.polys.length)),this._sunRaysCache={key:l,rays:p,rims:p.map(e=>function(e,t=1e-4){const[i,n]=e.dir,r=-n,o=i,s=[];for(const a of[e.a,e.b]){const l=[];for(const s of e.polys)for(let e=0;et)continue;if(Math.abs((h[0]-a[0])*r+(h[1]-a[1])*o)>t)continue;const d=(c[0]-a[0])*i+(c[1]-a[1])*n,u=(h[0]-a[0])*i+(h[1]-a[1])*n;Math.abs(u-d)<=t||l.push(de[0]-t[0]);const c=[];for(const e of l){const i=c[c.length-1];i&&e[0]<=i[1]+t?i[1]=Math.max(i[1],e[1]):c.push([e[0],e[1]])}for(const[e,t]of c)s.push([[a[0]+i*e,a[1]+n*e],[a[0]+i*t,a[1]+n*t]])}return s}(e))}}const c=this._sunRaysCache.rays,h=this._sunRaysCache.rims;if(!c.length)return t;const d=(u=function(e){const t=Math.min(90,Math.max(-90,Number(e)||0));let i=Ln[Ln.length-1][1];for(let e=1;e ${c.map((e,t)=>{const i=(e.a[0]+e.b[0])/2,n=(e.a[1]+e.b[1])/2,r=i+e.normal[0]*e.depth,o=n+e.normal[1]*e.depth;return j` ${p.map(([e,t])=>j`${this._t("gs.north_letter")} ${null===e?"—":e+"°"} - `}_stageBg(e){const t=this._settingsDialog,i=this._spaceDialog,n=t?t.bgColor||"":Ui(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":e.bgColor||"")||n}_stageBgHex(){const e=this._stageEl;if(e){const t=getComputedStyle(e).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(t)return"#"+t.slice(1,4).map(e=>(+e).toString(16).padStart(2,"0")).join("")}return"#111111"}_preflightDiagnostics(e,t){return this._editorRuntimeOrThrow()._preflightDiagnostics(e,t)}_reportPreflightFailure(e,t){return this._editorRuntimeOrThrow()._reportPreflightFailure(e,t)}_preflightVersionsDiffer(){return this._editorRuntimeOrThrow()._preflightVersionsDiffer()}async _copyPreflightDiagnostics(){return this._editorRuntimeOrThrow()._copyPreflightDiagnostics()}_checkOptimizeGeometry(e){return this._editorRuntimeOrThrow()._checkOptimizeGeometryImpl(e)}_checkSpacePhysicalGeometry(e,t,i){return this._editorRuntimeOrThrow()._checkSpacePhysicalGeometryImpl(e,t,i)}_optimizeReferenceContext(e){return this._editorRuntimeOrThrow()._optimizeReferenceContext(e)}_previewAlignDialog(e){return this._editorRuntimeOrThrow()._previewAlignDialog(e)}async _runAlignToGrid(){return this._editorRuntimeOrThrow()._runAlignToGrid()}async _undoPlanOptimization(){return this._editorRuntimeOrThrow()._undoPlanOptimization()}async _runBackupExport(){return this._editorRuntimeOrThrow()._runBackupExport()}async _pickBackupImport(e){return this._editorRuntimeOrThrow()._pickBackupImport(e)}async _setBackupDuplicatePolicy(e){return this._editorRuntimeOrThrow()._setBackupDuplicatePolicy(e)}async _applyBackupImport(){return this._editorRuntimeOrThrow()._applyBackupImport()}_renderBackupExportDialog(){return this._editorRuntimeOrThrow()._renderBackupExportDialog()}_renderBackupImportDialog(){return this._editorRuntimeOrThrow()._renderBackupImportDialog()}_setFillColor(e,t){return this._editorRuntimeOrThrow()._setFillColor(e,t)}async _saveSettingsDialog(){return this._editorRuntimeOrThrow()._saveSettingsDialog()}_boolInput(e,t,i=!1){return this._editorRuntimeOrThrow()._boolInput(e,t,i)}_rangeInput(e,t,i,n,r,o=!1,s){return this._editorRuntimeOrThrow()._rangeInput(e,t,i,n,r,o,s)}_renderColorRow(e,t){return this._editorRuntimeOrThrow()._renderColorRow(e,t)}get _glowRadiusCm(){const e=Number(this._settings.glow_radius_cm);return Number.isFinite(e)&&e>0?e:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const e=this._glowRadiusCm;return this._imperial?String(Math.round(e/30.48*10)/10):String(e/100)}_glowTransition(e,t){return Zp(this._glowRuntimeState,this._glowRuntimeHost,e,t)}_forgetGlowSource(e){Jp(this._glowRuntimeState,this._glowRuntimeHost,e)}_forgetGlowSpace(e){Qp(this._glowRuntimeState,this._glowRuntimeHost,e)}_warnGlowGeometryFallback(e,t,i,n){im(this._glowRuntimeState,e,t,i,n)}_lightBarriers(e,t){const i=Vp({rawSpaceConfig:this._curSpaceCfg,space:e,openings:this._openingsR,cellCm:this._cellCm,gridPitch:this._gridPitch,openingAmount:e=>this._openingAmt(e)}),n=`${e.id}|${i.fingerprint}`,r=Y_(this._lightBarrierPool,n);if(r.hit)return this._lightBarrierCache=r.value,r.value.value;const o=Gp({space:e,revision:i,walls:this._spaceWalls,zeroWalls:this._zeroWalls(),wallKeyPitch:this._wallKeyPitch,cellCm:this._cellCm,gridPitch:this._gridPitch,coordScale:cf,sharedWallGeometry:this._wallUnionGeometry(),physicalBodies:(t,i)=>(this._lightPhysicalBodiesCache?.key!==i&&(this._lightPhysicalBodiesCache={key:i,all:ea(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,t).all}),this._lightPhysicalBodiesCache.all)}),s={key:n,value:o};return X_(this._lightBarrierPool,n,s,8),this._lightBarrierCache=s,o}_renderGlowLayer(e,t){const i=this._fillColors,n=this._glowRadiusCm/this._cellCm*this._gridPitch,r=e.rooms.map(e=>({r:e,poly:Et(e)})).filter(e=>!!e.poly),o=r.filter(({r:e})=>gn(t.glow,e));if(!o.length)return this._forgetGlowSpace(e.id),j``;const s=this._lightBarriers(e,r),a=Up({hass:this._renderPlanHass,devices:this._renderDevices,virtualLights:this._virtualLights,spaceId:e.id,defaultColor:i.glow_light.c,paletteAlpha:i.glow_light.a,defaultRadiusUnits:n,cellCm:this._cellCm,gridPitch:this._gridPitch,position:e=>this._pos(e)}),l=[],c=new Set;for(const t of a){const{key:i,pos:n}=t;if(c.add(i),Yp(n,s)){this._forgetGlowSource(i);continue}const o=this._glowTransition(i,!!t.appearance);if(!o)continue;t.appearance&&this._glowLastAppearance.set(i,t.appearance);const a=this._glowLastAppearance.get(i);if(!a)continue;let h=null;const d=`${e.id}|${s.fingerprint}|${n.x.toFixed(4)},${n.y.toFixed(4)}|${t.radius.toFixed(4)}`,u=Bp(this._glowRuntimeState,d);u.hit?h=u.value:(h=Kp({spaceId:e.id,source:n,radius:t.radius,scene:s,polygons:r.map(({r:e,poly:t})=>({room:e,poly:t})),onBoundsFailure:(t,i)=>this._warnGlowGeometryFallback(e.id,s.fingerprint,t,i)}),jp(this._glowRuntimeState,d,h)),l.push({key:i,sourceEid:t.sourceEid,domId:o.domId,entering:o.entering,leaving:o.leaving,pos:n,c:a.c,alpha:a.alpha,geometry:h,r:t.radius})}if(em(this._glowRuntimeState,this._glowRuntimeHost,e.id,c),!l.length)return j``;const h=this._spaceWalls,d=o.length===r.length?[]:this._openCuts(),u=this._wallUnionGeometry()?.roomGeom,p=o.length===r.length?null:o.map(({r:t,poly:i})=>{const n=h.length&&t.id&&this._innerRoomContour(e,t.id,d,u)||i,o=this._cleanFloor(t,n,e).path,s=ai(n,r.filter(e=>e.r!==t).map(e=>e.poly)),a=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z";return[o||a(n),...s.map(a)].join(" ")}),m=this._viewOr(this._baseVb()),_=this._stageEl?.clientWidth&&m.w?this._stageEl.clientWidth/m.w:1,f=nm(this._glowRuntimeState,_,!this._pinchStart&&!this._panStart);return rm({spots:l,enabledClip:p,feather:f.feather,featherEnabled:f.enabled,screenBlend:this._glowScreenBlend})}_renderAlignDialog(){return this._editorRuntimeOrThrow()._renderAlignDialog()}_renderSettingsDialog(){return this._editorRuntimeOrThrow()._renderSettingsDialog()}_rulesSet(e){return this._editorRuntimeOrThrow()._rulesSet(e)}async _saveRules(){return this._editorRuntimeOrThrow()._saveRules()}_renderRulesDialog(){return this._editorRuntimeOrThrow()._renderRulesDialog()}_saveKioskScale(e){this._kioskScale={...this._kioskScale,...e};try{localStorage.setItem(sf,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const e=this._kioskScale,t=(t,i)=>{const n=Math.round(100*e[t]);return B` + `}_stageBg(e){const t=this._settingsDialog,i=this._spaceDialog,n=t?t.bgColor||"":Ui(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":e.bgColor||"")||n}_stageBgHex(){const e=this._stageEl;if(e){const t=getComputedStyle(e).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(t)return"#"+t.slice(1,4).map(e=>(+e).toString(16).padStart(2,"0")).join("")}return"#111111"}_preflightDiagnostics(e,t){return this._editorRuntimeOrThrow()._preflightDiagnostics(e,t)}_reportPreflightFailure(e,t){return this._editorRuntimeOrThrow()._reportPreflightFailure(e,t)}_preflightVersionsDiffer(){return this._editorRuntimeOrThrow()._preflightVersionsDiffer()}async _copyPreflightDiagnostics(){return this._editorRuntimeOrThrow()._copyPreflightDiagnostics()}_checkOptimizeGeometry(e){return this._editorRuntimeOrThrow()._checkOptimizeGeometryImpl(e)}_checkSpacePhysicalGeometry(e,t,i){return this._editorRuntimeOrThrow()._checkSpacePhysicalGeometryImpl(e,t,i)}_optimizeReferenceContext(e){return this._editorRuntimeOrThrow()._optimizeReferenceContext(e)}_previewAlignDialog(e){return this._editorRuntimeOrThrow()._previewAlignDialog(e)}async _runAlignToGrid(){return this._editorRuntimeOrThrow()._runAlignToGrid()}async _undoPlanOptimization(){return this._editorRuntimeOrThrow()._undoPlanOptimization()}async _runBackupExport(){return this._editorRuntimeOrThrow()._runBackupExport()}async _pickBackupImport(e){return this._editorRuntimeOrThrow()._pickBackupImport(e)}async _setBackupDuplicatePolicy(e){return this._editorRuntimeOrThrow()._setBackupDuplicatePolicy(e)}async _applyBackupImport(){return this._editorRuntimeOrThrow()._applyBackupImport()}_renderBackupExportDialog(){return this._editorRuntimeOrThrow()._renderBackupExportDialog()}_renderBackupImportDialog(){return this._editorRuntimeOrThrow()._renderBackupImportDialog()}_setFillColor(e,t){return this._editorRuntimeOrThrow()._setFillColor(e,t)}async _saveSettingsDialog(){return this._editorRuntimeOrThrow()._saveSettingsDialog()}_boolInput(e,t,i=!1){return this._editorRuntimeOrThrow()._boolInput(e,t,i)}_rangeInput(e,t,i,n,r,o=!1,s){return this._editorRuntimeOrThrow()._rangeInput(e,t,i,n,r,o,s)}_renderColorRow(e,t){return this._editorRuntimeOrThrow()._renderColorRow(e,t)}get _glowRadiusCm(){const e=Number(this._settings.glow_radius_cm);return Number.isFinite(e)&&e>0?e:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const e=this._glowRadiusCm;return this._imperial?String(Math.round(e/30.48*10)/10):String(e/100)}_glowTransition(e,t){return Jp(this._glowRuntimeState,this._glowRuntimeHost,e,t)}_forgetGlowSource(e){Qp(this._glowRuntimeState,this._glowRuntimeHost,e)}_forgetGlowSpace(e){em(this._glowRuntimeState,this._glowRuntimeHost,e)}_warnGlowGeometryFallback(e,t,i,n){nm(this._glowRuntimeState,e,t,i,n)}_lightBarriers(e,t){const i=Gp({rawSpaceConfig:this._curSpaceCfg,space:e,openings:this._openingsR,cellCm:this._cellCm,gridPitch:this._gridPitch,openingAmount:e=>this._openingAmt(e)}),n=`${e.id}|${i.fingerprint}`,r=X_(this._lightBarrierPool,n);if(r.hit)return this._lightBarrierCache=r.value,r.value.value;const o=Kp({space:e,revision:i,walls:this._spaceWalls,zeroWalls:this._zeroWalls(),wallKeyPitch:this._wallKeyPitch,cellCm:this._cellCm,gridPitch:this._gridPitch,coordScale:hf,sharedWallGeometry:this._wallUnionGeometry(),physicalBodies:(t,i)=>(this._lightPhysicalBodiesCache?.key!==i&&(this._lightPhysicalBodiesCache={key:i,all:ea(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,t).all}),this._lightPhysicalBodiesCache.all)}),s={key:n,value:o};return Z_(this._lightBarrierPool,n,s,8),this._lightBarrierCache=s,o}_renderGlowLayer(e,t){const i=this._fillColors,n=this._glowRadiusCm/this._cellCm*this._gridPitch,r=e.rooms.map(e=>({r:e,poly:Et(e)})).filter(e=>!!e.poly),o=r.filter(({r:e})=>gn(t.glow,e));if(!o.length)return this._forgetGlowSpace(e.id),j``;const s=this._lightBarriers(e,r),a=Vp({hass:this._renderPlanHass,devices:this._renderDevices,virtualLights:this._virtualLights,spaceId:e.id,defaultColor:i.glow_light.c,paletteAlpha:i.glow_light.a,defaultRadiusUnits:n,cellCm:this._cellCm,gridPitch:this._gridPitch,position:e=>this._pos(e)}),l=[],c=new Set;for(const t of a){const{key:i,pos:n}=t;if(c.add(i),Xp(n,s)){this._forgetGlowSource(i);continue}const o=this._glowTransition(i,!!t.appearance);if(!o)continue;t.appearance&&this._glowLastAppearance.set(i,t.appearance);const a=this._glowLastAppearance.get(i);if(!a)continue;let h=null;const d=`${e.id}|${s.fingerprint}|${n.x.toFixed(4)},${n.y.toFixed(4)}|${t.radius.toFixed(4)}`,u=jp(this._glowRuntimeState,d);u.hit?h=u.value:(h=Yp({spaceId:e.id,source:n,radius:t.radius,scene:s,polygons:r.map(({r:e,poly:t})=>({room:e,poly:t})),onBoundsFailure:(t,i)=>this._warnGlowGeometryFallback(e.id,s.fingerprint,t,i)}),Up(this._glowRuntimeState,d,h)),l.push({key:i,sourceEid:t.sourceEid,domId:o.domId,entering:o.entering,leaving:o.leaving,pos:n,c:a.c,alpha:a.alpha,geometry:h,r:t.radius})}if(tm(this._glowRuntimeState,this._glowRuntimeHost,e.id,c),!l.length)return j``;const h=this._spaceWalls,d=o.length===r.length?[]:this._openCuts(),u=this._wallUnionGeometry()?.roomGeom,p=o.length===r.length?null:o.map(({r:t,poly:i})=>{const n=h.length&&t.id&&this._innerRoomContour(e,t.id,d,u)||i,o=this._cleanFloor(t,n,e).path,s=ai(n,r.filter(e=>e.r!==t).map(e=>e.poly)),a=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z";return[o||a(n),...s.map(a)].join(" ")}),m=this._viewOr(this._baseVb()),_=this._stageEl?.clientWidth&&m.w?this._stageEl.clientWidth/m.w:1,f=rm(this._glowRuntimeState,_,!this._pinchStart&&!this._panStart);return om({spots:l,enabledClip:p,feather:f.feather,featherEnabled:f.enabled,screenBlend:this._glowScreenBlend})}_renderAlignDialog(){return this._editorRuntimeOrThrow()._renderAlignDialog()}_renderSettingsDialog(){return this._editorRuntimeOrThrow()._renderSettingsDialog()}_rulesSet(e){return this._editorRuntimeOrThrow()._rulesSet(e)}async _saveRules(){return this._editorRuntimeOrThrow()._saveRules()}_renderRulesDialog(){return this._editorRuntimeOrThrow()._renderRulesDialog()}_saveKioskScale(e){this._kioskScale={...this._kioskScale,...e};try{localStorage.setItem(af,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const e=this._kioskScale,t=(t,i)=>{const n=Math.round(100*e[t]);return B`
{const i=Number(e.target.value);Number.isFinite(i)&&this._saveKioskScale({[t]:i/100})}} aria-label=${i} /> @@ -4807,7 +4807,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 ${this._spaceDialog&&(this._onboardingRuntime||this._editorRuntime)?this._renderSpaceDialog():V} ${this._importDialog&&this._onboardingRuntime?this._renderImportDialog():V} ${this._toast?B``:V} - `;const s=this._spaceModel();if(!s)return V;const a="valid"===o.kind?[s]:n,l=s.vb,c=this._effectiveProjection();this._renderProjection=c,this._labs.active.length&&function(e){h_||f_(e);const t=c_,i=t.active.join(",");if(!i||i===u_)return;u_=i;const n=new Map(e_.map(e=>[e.id,e])),r=t.active.map(e=>{const t=n.get(e);return`${e} (#${t.issue}, expires ${t.expires})`}).join(", ");console.info(`HOUSEPLAN LABS: ${r}`)}(V_);const h="iso"===c,d="devices"===this._mode&&this._showAll,u=this._renderDevices.filter(e=>e.space===s.id&&(!e.hidden||d)),p=this._renderDeviceSnapshot,m=this._spaceDisplayForRender(),_=h?this._isoDecorationLayers(m):null,f=_?this._isoOpeningPanels(_):[],g=this._resolvedRoomFills(s,m),v=this._resolvedGlowBase(s,m,g),y=m.showLqi??this._config.show_signal??!0,b=this._config.icon_size??2.5,w=b>8?2.5:b,k=Xu(w),x=this._viewOr(this._baseVb()),$=this._floorView(x),S=this._editing?"":this._stageBg(m),M=this._opMeasureView,R=this._decorMeasure,T=this._bdLive,C=this._furnLive,D="view"===this._mode?this._editorChromeMode:this._mode,z=this._roomHoverPaths(s),A=s.bg?this._display(s.bg.href):"",P=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null,O=this._modeTransitionVisual,F=this._dayCycleState(),I=O?.viewWeight??("view"===this._mode?1:0),E=this._modeTransition.state?.from.presentedMode,H=!this._markup||!!O&&("view"===O.presentedMode||"devices"===O.presentedMode||"view"===E||"devices"===E),N=O?.stageColor||S,L=O?.sceneBrightness??1;return B` + `;const s=this._spaceModel();if(!s)return V;const a="valid"===o.kind?[s]:n,l=s.vb,c=this._effectiveProjection();this._renderProjection=c,this._labs.active.length&&function(e){d_||g_(e);const t=h_,i=t.active.join(",");if(!i||i===p_)return;p_=i;const n=new Map(t_.map(e=>[e.id,e])),r=t.active.map(e=>{const t=n.get(e);return`${e} (#${t.issue}, expires ${t.expires})`}).join(", ");console.info(`HOUSEPLAN LABS: ${r}`)}(G_);const h="iso"===c,d="devices"===this._mode&&this._showAll,u=this._renderDevices.filter(e=>e.space===s.id&&(!e.hidden||d)),p=this._renderDeviceSnapshot,m=this._spaceDisplayForRender(),_=h?this._isoDecorationLayers(m):null,f=_?this._isoOpeningPanels(_):[],g=this._resolvedRoomFills(s,m),v=this._resolvedGlowBase(s,m,g),y=m.showLqi??this._config.show_signal??!0,b=this._config.icon_size??2.5,w=b>8?2.5:b,k=Zu(w),x=this._viewOr(this._baseVb()),$=this._floorView(x),S=this._editing?"":this._stageBg(m),M=this._opMeasureView,R=this._decorMeasure,T=this._bdLive,C=this._furnLive,D="view"===this._mode?this._editorChromeMode:this._mode,z=this._roomHoverPaths(s),A=s.bg?this._display(s.bg.href):"",P=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null,O=this._modeTransitionVisual,F=this._dayCycleState(),I=O?.viewWeight??("view"===this._mode?1:0),E=this._modeTransition.state?.from.presentedMode,H=!this._markup||!!O&&("view"===O.presentedMode||"devices"===O.presentedMode||"view"===E||"devices"===E),N=O?.stageColor||S,L=O?.sceneBrightness??1;return B` + transform=${_?.structural?k_():V}> ${""} ${this._wallHatchDefs(m.color)}${j`${this._paperShapes(s.rooms).map(e=>"path"in e?j``:"poly"in e?j``:j``)}`} ${this._editing?this._renderMarkupDefs(l):V} @@ -5118,16 +5118,16 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 `:V} ${this._toast?B``:V} - `}_vacCandidateStatus(e,t,i=this._planHass){const n=this._bindingStatus("entity:"+e);if("ha_disabled"===n.kind)return"disabled";if("orphaned"===n.kind)return"missing";if("unverified"===n.kind)return"unverified";const r=i?.states?.[e];return"unavailable"===r?.state?"unavailable":t?.hasPosition?"ok":"unsupported"}_vacOpenAllCameras(e){const t=this._haRegistry.entities||{},i=[];for(const[e,n]of Object.entries(this.hass?.states||{})){if(!e.startsWith("camera."))continue;const r=Gl(e,n,t[e]);r&&i.push(r)}this._vacAllCameraCache={devId:e.id,candidates:i},this._vacAllCamerasFor=e.id}_vacSourceResolution(e,t=!1,i=this._planHass){const n=e.marker?.vacuum,r="string"==typeof n?.source&&!!n.source,o=new Set(e.entities||[]),s=new Set(o);r&&s.add(n.source);const a=i?.entities||{},l=[];for(const e of s){const t=i?.states?.[e],o=Gl(e,t,a[e]);o?l.push(o):r&&e===n.source&&l.push({entityId:e,name:String(t?.attributes?.friendly_name||e),platform:a[e]?.platform?String(a[e].platform):null,category:e.startsWith("camera.")?"camera":"partial",hasPosition:!1,hasRooms:!1,hasPath:!1,hasMapId:!1,score:0})}if(t&&this._vacAllCameraCache?.devId===e.id){const e=new Set(l.map(e=>e.entityId));for(const t of this._vacAllCameraCache.candidates)e.has(t.entityId)||l.push(t)}const c={};for(const e of l)c[e.entityId]=this._vacCandidateStatus(e.entityId,e,i);return function(e,t,i,n){const r="string"==typeof e&&e.length>0,o=new Set(t),s=Array.from(i).sort((t,i)=>r&&t.entityId===e?-1:r&&i.entityId===e?1:i.score-t.score||t.entityId.localeCompare(i.entityId));if(r){const t=s.find(t=>t.entityId===e);return{entityId:e,status:n[e]||(t?.hasPosition?"ok":"unverified"),pinned:!0,candidates:s}}const a=s.find(e=>o.has(e.entityId)&&e.hasPosition&&"ok"===n[e.entityId]);return a?{entityId:a.entityId,status:"ok",pinned:!1,candidates:s}:{entityId:null,status:"none",pinned:!1,candidates:s}}(n?.source,o,l,c)}_vacSource(e,t=this._planHass){if(!1===e.marker?.vacuum?.live)return null;const i=this._vacSourceResolution(e,!1,t);return"ok"===i.status||"unsupported"===i.status?i.entityId:null}_vacEntity(e){return e.primary?.startsWith("vacuum.")?e.primary:(e.entities||[]).find(e=>e.startsWith("vacuum."))||null}_isVacDev(e){return!!this._vacEntity(e)}_activitySourceKey(e){return this._activitySnapshot(e).sourceKey}_activitySnapshot(e,t=Rh(this._planHass,this._devices,null,this._virtualLights)){const i=Iu(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:Nu(this._planHass,e,i)}}_stampActivity(e,t,i){let n=this._activityRt.get(e);n||(n=$m(i||"",[]),this._activityRt.set(e,n)),null!=i&&(n.sources=i),Mm(n,t,Date.now(),window.clearTimeout.bind(window),e=>window.setTimeout(()=>this.requestUpdate(),e))}_syncActivityRuntime(){const e=new Map;if(!this.hass)return e;if(!1===this._config?.live_states){for(const e of this._activityRt.values())clearTimeout(e.timer);return this._activityRt.clear(),e}const t=new Set,i=Rh(this._planHass,this._devices,null,this._virtualLights);for(const n of this._devices){if(n.hidden)continue;if("icon_ripple"!==bi(n.marker?.display))continue;t.add(n.id);const r=this._activitySnapshot(n,i);e.set(n.id,r);const{samples:o,sourceKey:s}=r;let a=this._activityRt.get(n.id);a?a.sources!==s&&Sm(a,s,o,window.clearTimeout.bind(window)):(a=$m(s,o),this._activityRt.set(n.id,a))}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e));return e}_activityTick(){if(!this.hass)return;const e=this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;if("icon_ripple"!==bi(t.marker?.display))continue;const i=e.get(t.id)||this._activitySnapshot(t),{samples:n,sourceKey:r}=i,o=this._activityRt.get(t.id);if(!o||o.sources!==r)continue;const s=Rm(o,n,window.clearTimeout.bind(window));s&&this._stampActivity(t.id,s,r)}}_vacTick(){if(this.hass)for(const e of this._devices){if(e.hidden||!this._isVacDev(e))continue;if("static_icon"===bi(e.marker?.display)){this._vacRt.delete(e.id);continue}const t=this._vacSource(e);if(!t)continue;const i=this._vacEntity(e),n=Jl(this.hass.states[i||""]?.state),r=jl(this.hass.states[t]?.attributes);let o=this._vacRt.get(e.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(e.id,o)),n&&!o.moving&&(o.trail=[],o.lastPos=null);const s="never"!==rc(e.marker?.vacuum)&&!r?.path.length;!n&&o.moving&&(o.endedTs=Date.now(),s&&o.lastPos&&(o.trail=Zl(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=n;const a=r?.pos;if(n&&a){const e=a.x+":"+a.y;if(e!==o.lastKey){const t=Date.now();o.jump=o.lastTs>0&&t-o.lastTs>1e4,o.lastKey=e,o.lastTs=t,s&&o.lastPos&&(o.trail=Zl(o.trail,o.lastPos,40)),o.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(e){const t=this._serverCfg;if(!t)return null;t.markers=t.markers||[];const i=t.markers.find(t=>t.id===e.id);if(i)return i;if("device"!==e.bindingKind&&"entity"!==e.bindingKind||!e.bindingRef)return null;const n={id:e.id,binding:e.bindingKind+":"+e.bindingRef,space:e.space||null,area:e.area||null,hidden:!!e.hidden};return t.markers.push(n),n}_renderVacSection(e){return this._editorRuntimeOrThrow()._renderVacSection(e)}_vacMapId(e,t,i=this._planHass){const n=this._vacEntity(e),r=n?i?.states?.[n]?.attributes?.selected_map:null;return o=t.mapId,s=r,"default"!==o?o:null!=s?String(s):"default";var o,s}_vacSaveMatrix(e,t,i,n){return this._editorRuntimeOrThrow()._vacSaveMatrix(e,t,i,n)}_vacPlanRoomAnchors(e){return this._editorRuntimeOrThrow()._vacPlanRoomAnchors(e)}_vacAutoCalibrate(e){return this._editorRuntimeOrThrow()._vacAutoCalibrate(e)}_vacApplyCalibrationProposal(e){return this._editorRuntimeOrThrow()._vacApplyCalibrationProposal(e)}_vacStartFit(e){return this._editorRuntimeOrThrow()._vacStartFit(e)}_vacFitSave(){return this._editorRuntimeOrThrow()._vacFitSave()}_vacFitTurn(e){return this._editorRuntimeOrThrow()._vacFitTurn(e)}_vacGhostCentre(e){return this._editorRuntimeOrThrow()._vacGhostCentre(e)}_vacDelta(e,t,i){return this._editorRuntimeOrThrow()._vacDelta(e,t,i)}_vacFitPointer(e,t){return this._editorRuntimeOrThrow()._vacFitPointer(e,t)}_renderVacFit(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderVacFit(e):V}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const e=this.renderRoot,t=this._stageEl,i=this._vacLastView,n=e?.querySelectorAll?.(".vacpuck")||[];if(!t||!i||!n.length)return void(this._vacRaf=0);const r=t.getBoundingClientRect();for(const t of n){const n=t.getAttribute("data-mid"),o=t.getBoundingClientRect(),s=i.x+(o.left+o.width/2-r.left)/r.width*i.w,a=i.y+(o.top+o.height/2-r.top)/r.height*i.h;for(const t of e.querySelectorAll(`line.tip[data-mid="${n}"]`))t.setAttribute("x2",s.toFixed(1)),t.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_vacTrailPathD(e,t){const i=e.map(e=>e.map(([e,i])=>Pl(t,e,i))),n=function(e,t,i=console.warn){if(!Number.isFinite(t)||t<=0)return[];const n=[];let r=0;for(const i of e){if(!i.every(ql)){r++;continue}const e=i.filter((e,t)=>0===t||!Bl(e,i[t-1]));if(e.length<2)continue;const o=[{kind:"move",point:e[0]}];if(2!==e.length){for(let i=1;i0&&i(`[houseplan] vacuum trail: ${r} segment(s) dropped — non-finite point (check map calibration)`),n}(i,this._cmToUnits(17.5)),r=e=>{const t=this._scenePoint(e);return`${t[0].toFixed(1)} ${t[1].toFixed(1)}`};return n.map(e=>e.map(e=>"move"===e.kind?`M ${r(e.point)}`:"line"===e.kind?`L ${r(e.point)}`:`Q ${r(e.control)} ${r(e.point)}`).join(" ")).join(" ")}_renderVacuums(e,t){if(this._markup||"decor"===this._mode)return V;const i=this._space+"|"+t.x+"|"+t.y+"|"+t.w+"|"+t.h,n=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const r=[],o=[];for(const i of e){if(i.hidden||!this._isVacDev(i))continue;if("static_icon"===bi(i.marker?.display))continue;const e=this._renderDeviceSnapshot?.facts.get(`vacuum:${i.id}`),s=e?.source??this._vacSource(i,this._renderPlanHass);if(!s)continue;const a=e?.telemetry??jl(this._renderPlanHass?.states[s]?.attributes);if(!a)continue;const l=String(e?.mapId??this._vacMapId(i,a,this._renderPlanHass)),c=i.marker?.vacuum?.calibration?.[l];if(!c||6!==c.length)continue;const h=e?.runtime??this._vacRt.get(i.id),d=h?.moving??!1,u=rc(i.marker?.vacuum),p="always"===u||"cleaning"===u&&d,m=e?.server??this._vacSrvTrails[i.id],_=m?.current?.map_id===l&&Array.isArray(m.current.points)?m.current:null,f=m?.previous?.map_id===l&&Array.isArray(m.previous.points)?m.previous:null;if("always"===u&&f){const e=Nl(f.points),t=this._vacTrailPathD(e,c);t&&o.push(j``)}if(p){const e=Ll(a,_,h?.trail||[]),t=!d||"integration"!==e.source&&"server"!==e.source?e.path:Wl(e.path);if(t.length){const e=this._vacTrailPathD(t,c);e&&o.push(j``);const n=t[t.length-1];if(d&&n?.length>=2){const e=n[n.length-1],[t,r]=Pl(c,e[0],e[1]),s=this._scenePoint([t,r]),a=s[0].toFixed(1),l=s[1].toFixed(1);o.push(j``)}}}if(!d||!a.pos)continue;const[g,v]=Pl(c,a.pos.x,a.pos.y),y=this._scenePoint([g,v]),b=(y[0]-t.x)/t.w*100,w=(y[1]-t.y)/t.h*100,k=h&&h.lastTs>0&&Date.now()-h.lastTs>6e4,x=i.marker?.icon||i.icon||"mdi:robot-vacuum";r.push(B`
e.entityId));for(const t of this._vacAllCameraCache.candidates)e.has(t.entityId)||l.push(t)}const c={};for(const e of l)c[e.entityId]=this._vacCandidateStatus(e.entityId,e,i);return function(e,t,i,n){const r="string"==typeof e&&e.length>0,o=new Set(t),s=Array.from(i).sort((t,i)=>r&&t.entityId===e?-1:r&&i.entityId===e?1:i.score-t.score||t.entityId.localeCompare(i.entityId));if(r){const t=s.find(t=>t.entityId===e);return{entityId:e,status:n[e]||(t?.hasPosition?"ok":"unverified"),pinned:!0,candidates:s}}const a=s.find(e=>o.has(e.entityId)&&e.hasPosition&&"ok"===n[e.entityId]);return a?{entityId:a.entityId,status:"ok",pinned:!1,candidates:s}:{entityId:null,status:"none",pinned:!1,candidates:s}}(n?.source,o,l,c)}_vacSource(e,t=this._planHass){if(!1===e.marker?.vacuum?.live)return null;const i=this._vacSourceResolution(e,!1,t);return"ok"===i.status||"unsupported"===i.status?i.entityId:null}_vacEntity(e){return e.primary?.startsWith("vacuum.")?e.primary:(e.entities||[]).find(e=>e.startsWith("vacuum."))||null}_isVacDev(e){return!!this._vacEntity(e)}_activitySourceKey(e){return this._activitySnapshot(e).sourceKey}_activitySnapshot(e,t=Rh(this._planHass,this._devices,null,this._virtualLights)){const i=Eu(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:Lu(this._planHass,e,i)}}_stampActivity(e,t,i){let n=this._activityRt.get(e);n||(n=Sm(i||"",[]),this._activityRt.set(e,n)),null!=i&&(n.sources=i),Rm(n,t,Date.now(),window.clearTimeout.bind(window),e=>window.setTimeout(()=>this.requestUpdate(),e))}_syncActivityRuntime(){const e=new Map;if(!this.hass)return e;if(!1===this._config?.live_states){for(const e of this._activityRt.values())clearTimeout(e.timer);return this._activityRt.clear(),e}const t=new Set,i=Rh(this._planHass,this._devices,null,this._virtualLights);for(const n of this._devices){if(n.hidden)continue;if("icon_ripple"!==bi(n.marker?.display))continue;t.add(n.id);const r=this._activitySnapshot(n,i);e.set(n.id,r);const{samples:o,sourceKey:s}=r;let a=this._activityRt.get(n.id);a?a.sources!==s&&Mm(a,s,o,window.clearTimeout.bind(window)):(a=Sm(s,o),this._activityRt.set(n.id,a))}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e));return e}_activityTick(){if(!this.hass)return;const e=this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;if("icon_ripple"!==bi(t.marker?.display))continue;const i=e.get(t.id)||this._activitySnapshot(t),{samples:n,sourceKey:r}=i,o=this._activityRt.get(t.id);if(!o||o.sources!==r)continue;const s=Tm(o,n,window.clearTimeout.bind(window));s&&this._stampActivity(t.id,s,r)}}_vacTick(){if(this.hass)for(const e of this._devices){if(e.hidden||!this._isVacDev(e))continue;if("static_icon"===bi(e.marker?.display)){this._vacRt.delete(e.id);continue}const t=this._vacSource(e);if(!t)continue;const i=this._vacEntity(e),n=Jl(this.hass.states[i||""]?.state),r=jl(this.hass.states[t]?.attributes);let o=this._vacRt.get(e.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(e.id,o)),n&&!o.moving&&(o.trail=[],o.lastPos=null);const s="never"!==rc(e.marker?.vacuum)&&!r?.path.length;!n&&o.moving&&(o.endedTs=Date.now(),s&&o.lastPos&&(o.trail=Zl(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=n;const a=r?.pos;if(n&&a){const e=a.x+":"+a.y;if(e!==o.lastKey){const t=Date.now();o.jump=o.lastTs>0&&t-o.lastTs>1e4,o.lastKey=e,o.lastTs=t,s&&o.lastPos&&(o.trail=Zl(o.trail,o.lastPos,40)),o.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(e){const t=this._serverCfg;if(!t)return null;t.markers=t.markers||[];const i=t.markers.find(t=>t.id===e.id);if(i)return i;if("device"!==e.bindingKind&&"entity"!==e.bindingKind||!e.bindingRef)return null;const n={id:e.id,binding:e.bindingKind+":"+e.bindingRef,space:e.space||null,area:e.area||null,hidden:!!e.hidden};return t.markers.push(n),n}_renderVacSection(e){return this._editorRuntimeOrThrow()._renderVacSection(e)}_vacMapId(e,t,i=this._planHass){const n=this._vacEntity(e),r=n?i?.states?.[n]?.attributes?.selected_map:null;return o=t.mapId,s=r,"default"!==o?o:null!=s?String(s):"default";var o,s}_vacSaveMatrix(e,t,i,n){return this._editorRuntimeOrThrow()._vacSaveMatrix(e,t,i,n)}_vacPlanRoomAnchors(e){return this._editorRuntimeOrThrow()._vacPlanRoomAnchors(e)}_vacAutoCalibrate(e){return this._editorRuntimeOrThrow()._vacAutoCalibrate(e)}_vacApplyCalibrationProposal(e){return this._editorRuntimeOrThrow()._vacApplyCalibrationProposal(e)}_vacStartFit(e){return this._editorRuntimeOrThrow()._vacStartFit(e)}_vacFitSave(){return this._editorRuntimeOrThrow()._vacFitSave()}_vacFitTurn(e){return this._editorRuntimeOrThrow()._vacFitTurn(e)}_vacGhostCentre(e){return this._editorRuntimeOrThrow()._vacGhostCentre(e)}_vacDelta(e,t,i){return this._editorRuntimeOrThrow()._vacDelta(e,t,i)}_vacFitPointer(e,t){return this._editorRuntimeOrThrow()._vacFitPointer(e,t)}_renderVacFit(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderVacFit(e):V}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const e=this.renderRoot,t=this._stageEl,i=this._vacLastView,n=e?.querySelectorAll?.(".vacpuck")||[];if(!t||!i||!n.length)return void(this._vacRaf=0);const r=t.getBoundingClientRect();for(const t of n){const n=t.getAttribute("data-mid"),o=t.getBoundingClientRect(),s=i.x+(o.left+o.width/2-r.left)/r.width*i.w,a=i.y+(o.top+o.height/2-r.top)/r.height*i.h;for(const t of e.querySelectorAll(`line.tip[data-mid="${n}"]`))t.setAttribute("x2",s.toFixed(1)),t.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_vacTrailPathD(e,t){const i=e.map(e=>e.map(([e,i])=>Pl(t,e,i))),n=function(e,t,i=console.warn){if(!Number.isFinite(t)||t<=0)return[];const n=[];let r=0;for(const i of e){if(!i.every(ql)){r++;continue}const e=i.filter((e,t)=>0===t||!Bl(e,i[t-1]));if(e.length<2)continue;const o=[{kind:"move",point:e[0]}];if(2!==e.length){for(let i=1;i0&&i(`[houseplan] vacuum trail: ${r} segment(s) dropped — non-finite point (check map calibration)`),n}(i,this._cmToUnits(17.5)),r=e=>{const t=this._scenePoint(e);return`${t[0].toFixed(1)} ${t[1].toFixed(1)}`};return n.map(e=>e.map(e=>"move"===e.kind?`M ${r(e.point)}`:"line"===e.kind?`L ${r(e.point)}`:`Q ${r(e.control)} ${r(e.point)}`).join(" ")).join(" ")}_renderVacuums(e,t){if(this._markup||"decor"===this._mode)return V;const i=this._space+"|"+t.x+"|"+t.y+"|"+t.w+"|"+t.h,n=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const r=[],o=[];for(const i of e){if(i.hidden||!this._isVacDev(i))continue;if("static_icon"===bi(i.marker?.display))continue;const e=this._renderDeviceSnapshot?.facts.get(`vacuum:${i.id}`),s=e?.source??this._vacSource(i,this._renderPlanHass);if(!s)continue;const a=e?.telemetry??jl(this._renderPlanHass?.states[s]?.attributes);if(!a)continue;const l=String(e?.mapId??this._vacMapId(i,a,this._renderPlanHass)),c=i.marker?.vacuum?.calibration?.[l];if(!c||6!==c.length)continue;const h=e?.runtime??this._vacRt.get(i.id),d=h?.moving??!1,u=rc(i.marker?.vacuum),p="always"===u||"cleaning"===u&&d,m=e?.server??this._vacSrvTrails[i.id],_=m?.current?.map_id===l&&Array.isArray(m.current.points)?m.current:null,f=m?.previous?.map_id===l&&Array.isArray(m.previous.points)?m.previous:null;if("always"===u&&f){const e=Nl(f.points),t=this._vacTrailPathD(e,c);t&&o.push(j``)}if(p){const e=Ll(a,_,h?.trail||[]),t=!d||"integration"!==e.source&&"server"!==e.source?e.path:Wl(e.path);if(t.length){const e=this._vacTrailPathD(t,c);e&&o.push(j``);const n=t[t.length-1];if(d&&n?.length>=2){const e=n[n.length-1],[t,r]=Pl(c,e[0],e[1]),s=this._scenePoint([t,r]),a=s[0].toFixed(1),l=s[1].toFixed(1);o.push(j``)}}}if(!d||!a.pos)continue;const[g,v]=Pl(c,a.pos.x,a.pos.y),y=this._scenePoint([g,v]),b=(y[0]-t.x)/t.w*100,w=(y[1]-t.y)/t.h*100,k=h&&h.lastTs>0&&Date.now()-h.lastTs>6e4,x=i.marker?.icon||i.icon||"mdi:robot-vacuum";r.push(B`
{e.stopPropagation();const t=this._vacEntity(i);t&&this._openMoreInfo(t)}}>
`)}return this._vacLastView=t,r.length&&!this._vacRaf&&this._vacRafLoop(),r.length||o.length?B` ${o.length?j`${o}`:V} - ${r}`:V}_renderDevice(e,t,i=!0){const n=this._pos(e),r=this._scenePoint([n.x,n.y]),o=(r[0]-t.x)/t.w*100,s=(r[1]-t.y)/t.h*100,a=this._devicePresentation(e,i),l=[`left:${o}%`,`top:${s}%`,...Gu(a)],c=a.disabledReason,h=a.haDisabled?this._t(`marker.ha_disabled_${c}`):e.userHidden?this._t("marker.hidden_ghost"):e.name,d=Du(a),u="view"===this._mode||"devices"===this._mode,p=[h,a.haDisabled?"":this._t(`marker.state_a11y_${d}`),"none"!==a.pulse.kind?this._t(`marker.pulse_a11y_${a.pulse.reason}`):"",a.valueFullText||a.valueText||"",hu(a.valueBadge),null!=a.lqiText&&a.lqiBand?this._t(`marker.lqi_a11y_${a.lqiBand}`,{value:a.lqiText}):""].filter(Boolean).join(", "),m=[e.model,a.valueBadge?.fullText||"",null!=a.lqiText?"LQI "+a.lqiText:""].filter(Boolean).join(" · ");return B`
this._clickDevice(t,e)} @keydown=${t=>this._keyDevice(t,e)} @@ -5152,8 +5152,8 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 @pointerup=${t=>this._pointerUp(t,e)} @pointercancel=${t=>this._pointerUp(t,e)} > - ${Yu(a,{newDevice:this._newIds.has(e.id),newDeviceTitle:this._t("device.new"),disabledTitle:a.haDisabled?h:""})} -
`}_roomArea(e){const t=Et(e);if(!t)return null;const i=this._spaceModel();if(!i)return null;const n=this._spaceWalls.length&&e.id&&this._innerRoomContour(i,e.id)||t,r=this._cleanFloor(e,n),o=this._cellCm/this._gridPitch;return Tn(r.area*o*o/1e4,"mi"===this._renderPlanHass?.config?.unit_system?.length)}_roomTemp(e){const t=e.settings?.temp_source;if(t)return Gh(this._renderPlanHass,t,"temp",this._markers);const i=Yh(this._spaceModel()?.id,e);return i?this._climate().get(i)?.temp??null:null}_roomHum(e){const t=e.settings?.hum_source;if(t)return Gh(this._renderPlanHass,t,"hum",this._markers);const i=Yh(this._spaceModel()?.id,e);return i?this._climate().get(i)?.hum??null:null}_climate(){const e=this._serverCfg?.markers,t=this._renderPlanHass,i=this._climateCache;if(i&&i.h===t&&i.r===this._iconRules&&i.mk===e)return i.m;const n=Zh(t,this._iconRules,e);return this._climateCache={h:t,r:this._iconRules,mk:e,m:n},n}_resetRoomDialogFields(){return this._editorRuntimeOrThrow()._resetRoomDialogFields()}_openRoomEdit(e){return this._editorRuntimeOrThrow()._openRoomEdit(e)}_roomSettingsFromDialog(){return this._editorRuntimeOrThrow()._roomSettingsFromDialog()}_saveRoomEdit(){return this._editorRuntimeOrThrow()._saveRoomEdit()}_roomSrcCandidates(){return this._editorRuntimeOrThrow()._roomSrcCandidates()}_roomSrcLabel(e){return this._editorRuntimeOrThrow()._roomSrcLabel(e)}_labelPos(e,t){const i=this._layout["rl_"+(e.id||"")];if(i&&i.s===t)return{x:i.x*cf,y:i.y*cf};const n=this._roomCenter(e),r=this._gridPitch;return{x:Aa(Pt(n[0],r)),y:Aa(Pt(n[1],r))}}_labelDown(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelDown(e,t,i)}_labelMove(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelMove(e,t,i)}_labelUp(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelUp(e)}_labelScale(e){const t=this._layout["rl_"+(e.id||"")]?.k;return"number"==typeof t&&Number.isFinite(t)?Math.min(3,Math.max(.5,t)):1}_rlResizeDown(e,t,i){return this._editorRuntimeOrThrow()._rlResizeDown(e,t,i)}_rlResizeMove(e){return this._editorRuntimeOrThrow()._rlResizeMove(e)}_rlResizeUp(){return this._editorRuntimeOrThrow()._rlResizeUp()}_renderRoomGear(e,t,i){return this._editorRuntimeOrThrow()._renderRoomGear(e,t,i)}_renderRoomLabel(e,t,i,n){if(!e.name&&!this._markup)return V;const r=this._labelPos(e,t.id),o=this._scenePoint([r.x,r.y]),s=(o[0]-i.x)/i.w*100,a=(o[1]-i.y)/i.h*100,l=Math.min(1,n.opacity+.25),c=this._labelScale(e),h=[];if(n.labelTemp||n.labelHum||n.labelLqi&&e.area||n.labelLight){if(n.labelTemp){const t=this._roomTemp(e);null!=t&&h.push(B`${t}°`)}if(n.labelHum){const t=this._roomHum(e);null!=t&&h.push(B`${t}%`)}if(n.labelLqi&&e.area){const t=this._roomLqi(e.area);null!=t&&h.push(B`${t}`)}if(n.labelLight){const t=(d=Rh(this._renderPlanHass,this._renderDevices,e,this._virtualLights)).length?{on:d.filter(e=>e.on).length,total:d.length}:null;if(t){const e=0===t.on?this._t("roomcard.light_off"):t.on===t.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:t.on,total:t.total});h.push(B`${e}`)}}}var d;const u=!!e.area,p=!this._markup;return B`
`}_roomArea(e){const t=Et(e);if(!t)return null;const i=this._spaceModel();if(!i)return null;const n=this._spaceWalls.length&&e.id&&this._innerRoomContour(i,e.id)||t,r=this._cleanFloor(e,n),o=this._cellCm/this._gridPitch;return Tn(r.area*o*o/1e4,"mi"===this._renderPlanHass?.config?.unit_system?.length)}_roomTemp(e){const t=e.settings?.temp_source;if(t)return Gh(this._renderPlanHass,t,"temp",this._markers);const i=Yh(this._spaceModel()?.id,e);return i?this._climate().get(i)?.temp??null:null}_roomHum(e){const t=e.settings?.hum_source;if(t)return Gh(this._renderPlanHass,t,"hum",this._markers);const i=Yh(this._spaceModel()?.id,e);return i?this._climate().get(i)?.hum??null:null}_climate(){const e=this._serverCfg?.markers,t=this._renderPlanHass,i=this._climateCache;if(i&&i.h===t&&i.r===this._iconRules&&i.mk===e)return i.m;const n=Zh(t,this._iconRules,e);return this._climateCache={h:t,r:this._iconRules,mk:e,m:n},n}_resetRoomDialogFields(){return this._editorRuntimeOrThrow()._resetRoomDialogFields()}_openRoomEdit(e){return this._editorRuntimeOrThrow()._openRoomEdit(e)}_roomSettingsFromDialog(){return this._editorRuntimeOrThrow()._roomSettingsFromDialog()}_saveRoomEdit(){return this._editorRuntimeOrThrow()._saveRoomEdit()}_roomSrcCandidates(){return this._editorRuntimeOrThrow()._roomSrcCandidates()}_roomSrcLabel(e){return this._editorRuntimeOrThrow()._roomSrcLabel(e)}_labelPos(e,t){const i=this._layout["rl_"+(e.id||"")];if(i&&i.s===t)return{x:i.x*hf,y:i.y*hf};const n=this._roomCenter(e),r=this._gridPitch;return{x:Aa(Pt(n[0],r)),y:Aa(Pt(n[1],r))}}_labelDown(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelDown(e,t,i)}_labelMove(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelMove(e,t,i)}_labelUp(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelUp(e)}_labelScale(e){const t=this._layout["rl_"+(e.id||"")]?.k;return"number"==typeof t&&Number.isFinite(t)?Math.min(3,Math.max(.5,t)):1}_rlResizeDown(e,t,i){return this._editorRuntimeOrThrow()._rlResizeDown(e,t,i)}_rlResizeMove(e){return this._editorRuntimeOrThrow()._rlResizeMove(e)}_rlResizeUp(){return this._editorRuntimeOrThrow()._rlResizeUp()}_renderRoomGear(e,t,i){return this._editorRuntimeOrThrow()._renderRoomGear(e,t,i)}_renderRoomLabel(e,t,i,n){if(!e.name&&!this._markup)return V;const r=this._labelPos(e,t.id),o=this._scenePoint([r.x,r.y]),s=(o[0]-i.x)/i.w*100,a=(o[1]-i.y)/i.h*100,l=Math.min(1,n.opacity+.25),c=this._labelScale(e),h=[];if(n.labelTemp||n.labelHum||n.labelLqi&&e.area||n.labelLight){if(n.labelTemp){const t=this._roomTemp(e);null!=t&&h.push(B`${t}°`)}if(n.labelHum){const t=this._roomHum(e);null!=t&&h.push(B`${t}%`)}if(n.labelLqi&&e.area){const t=this._roomLqi(e.area);null!=t&&h.push(B`${t}`)}if(n.labelLight){const t=(d=Rh(this._renderPlanHass,this._renderDevices,e,this._virtualLights)).length?{on:d.filter(e=>e.on).length,total:d.length}:null;if(t){const e=0===t.on?this._t("roomcard.light_off"):t.on===t.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:t.on,total:t.total});h.push(B`${e}`)}}}var d;const u=!!e.area,p=!this._markup;return B`
this._labelDown(i,e,t.id)} @@ -5171,7 +5171,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 @pointerup=${()=>this._rlResizeUp()} @pointercancel=${()=>this._rlResizeUp()}>`):V}
`}get _measureAnchor(){return this._markup&&this._cursorPt?"draw"===this._tool&&this._path.length&&!this._contourClosed?this._path[this._path.length-1]:"split"===this._tool&&this._splitSel?.pts?.length?this._splitSel.pts[this._splitSel.pts.length-1]:null:null}_renderMeasureLabel(e){const t=this._measureAnchor,i=this._cursorPt,n=(i[0]-e.x)/e.w*100,r=(i[1]-e.y)/e.h*100,o=wn(t,i),s=Math.round(10*o)/10,a=function(e,t,i=.001){if(e.length<2||t.length<2)return!1;const n=Math.abs(t[0]-e[0]),r=Math.abs(t[1]-e[1]);if(!Number.isFinite(n)||!Number.isFinite(r)||Math.hypot(n,r)<=i)return!1;const o=Math.max(n,r,1);return n<=i*o||r<=i*o||Math.abs(n-r)<=i*o}(t,i,2e-4*this._gridPitch);return B`
- ${this._fmtLen(t,i)} · ${s}°
`}get _decorMeasure(){const e=this._decorDraft;if(!e||"decor"!==this._mode)return null;const[t,i]=e.a,[n,r]=e.b;if(Math.abs(t-n)<1e-6&&Math.abs(i-r)<1e-6)return null;const o=(t+n)/2,s=(i+r)/2;if("line"===e.kind){const t=wn(e.a,e.b);return{x:o,y:s,on45:kn(t),text:`${this._fmtLen(e.a,e.b)} · ${Math.round(10*t)/10}°`}}const a=this._fmtLen([t,i],[n,i]),l=this._fmtLen([n,i],[n,r]);if("ellipse"===e.kind){const e=this._fmtLen([0,0],[Math.abs(n-t)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(r-i)/2]);return{x:o,y:s,on45:!1,text:Math.abs(Math.abs(n-t)-Math.abs(r-i))<1e-6?`R ${e}`:`Rx ${e} × Ry ${a}`}}return{x:o,y:s,on45:!1,text:`${a} × ${l} · ${Tn(ar(Math.abs(n-t),this._cellCm,this._gridPitch)*ar(Math.abs(r-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const e=this._drag.id.slice(3),t=this._spaceModel()?.rooms.find(t=>t.id===e);return t?(()=>{const e=this._labelPos(t,this._space);return[e.x,e.y]})():null}return null}if("devices"===this._mode&&this._drag?.moved){const e=this._devices.find(e=>e.id===this._drag.id);return e?(()=>{const t=this._pos(e);return[t.x,t.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const e=this._decorList.find(e=>e.id===this._decorMove.id);if(!e)return null;const t=cf,i=this._decorH;return"line"===e.kind?[e.x1*t,e.y1*i]:[e.x*t,e.y*i]}return null}return null}_alignCandidates(){return this._editorRuntimeOrThrow()._alignCandidates()}_renderAlignGuides(){return this._editorRuntimeOrThrow()._renderAlignGuides()}_renderOpeningCenterTick(e){return this._editorRuntimeOrThrow()._renderOpeningCenterTick(e)}_renderOpeningDimensionGuides(e){return this._editorRuntimeOrThrow()._renderOpeningDimensionGuides(e)}_roomCenter(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}_openingAmt(e){const t=e.contact&&this._renderOpeningEntityAvailable(e.contact)?this._renderPlanHass.states[e.contact]:null;return Bt(e.type,t?.state,!!e.invert,t?.attributes?.current_position)}_planEntityAvailable(e){return!!e&&(!qh(this._fullRegistryHass,e,Wh(this._markers))&&"active"===this._bindingStatus("entity:"+e).kind)}_renderEntityAvailable(e){return!!e&&(!qh(this._renderPlanHass,e,Wh(this._markers))&&(!!this._renderPlanHass.entities?.[e]&&!!this._renderPlanHass.states?.[e]))}_openingEntityAvailable(e){return function(e,t,i=Kc(e)){return!!t&&"active"===ih(e,`entity:${t}`,i).kind}(this.hass,e,this._haRegistry)}_renderOpeningEntityAvailable(e){return t=this._renderPlanHass,!!(i=e)&&!!t?.states?.[i];var t,i}_renderOpeningPlacementPreview(){return this._editorRuntimeOrThrow()._renderOpeningPlacementPreview()}_renderOpenings(e){const t=this._openingsR;if(!t.length)return j``;const i=this._spaceModel();if(!i)return j``;const n=e.color,r=this._spaceWalls,o=this._openCuts(),s=this._openingWallIndexFor(i,o).value;return j`${t.map(e=>{if(e.orphanReason)return j``}get _decorMeasure(){const e=this._decorDraft;if(!e||"decor"!==this._mode)return null;const[t,i]=e.a,[n,r]=e.b;if(Math.abs(t-n)<1e-6&&Math.abs(i-r)<1e-6)return null;const o=(t+n)/2,s=(i+r)/2;if("line"===e.kind){const t=wn(e.a,e.b);return{x:o,y:s,on45:kn(t),text:`${this._fmtLen(e.a,e.b)} · ${Math.round(10*t)/10}°`}}const a=this._fmtLen([t,i],[n,i]),l=this._fmtLen([n,i],[n,r]);if("ellipse"===e.kind){const e=this._fmtLen([0,0],[Math.abs(n-t)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(r-i)/2]);return{x:o,y:s,on45:!1,text:Math.abs(Math.abs(n-t)-Math.abs(r-i))<1e-6?`R ${e}`:`Rx ${e} × Ry ${a}`}}return{x:o,y:s,on45:!1,text:`${a} × ${l} · ${Tn(ar(Math.abs(n-t),this._cellCm,this._gridPitch)*ar(Math.abs(r-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const e=this._drag.id.slice(3),t=this._spaceModel()?.rooms.find(t=>t.id===e);return t?(()=>{const e=this._labelPos(t,this._space);return[e.x,e.y]})():null}return null}if("devices"===this._mode&&this._drag?.moved){const e=this._devices.find(e=>e.id===this._drag.id);return e?(()=>{const t=this._pos(e);return[t.x,t.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const e=this._decorList.find(e=>e.id===this._decorMove.id);if(!e)return null;const t=hf,i=this._decorH;return"line"===e.kind?[e.x1*t,e.y1*i]:[e.x*t,e.y*i]}return null}return null}_alignCandidates(){return this._editorRuntimeOrThrow()._alignCandidates()}_renderAlignGuides(){return this._editorRuntimeOrThrow()._renderAlignGuides()}_renderOpeningCenterTick(e){return this._editorRuntimeOrThrow()._renderOpeningCenterTick(e)}_renderOpeningDimensionGuides(e){return this._editorRuntimeOrThrow()._renderOpeningDimensionGuides(e)}_roomCenter(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}_openingAmt(e){const t=e.contact&&this._renderOpeningEntityAvailable(e.contact)?this._renderPlanHass.states[e.contact]:null;return Bt(e.type,t?.state,!!e.invert,t?.attributes?.current_position)}_planEntityAvailable(e){return!!e&&(!qh(this._fullRegistryHass,e,Wh(this._markers))&&"active"===this._bindingStatus("entity:"+e).kind)}_renderEntityAvailable(e){return!!e&&(!qh(this._renderPlanHass,e,Wh(this._markers))&&(!!this._renderPlanHass.entities?.[e]&&!!this._renderPlanHass.states?.[e]))}_openingEntityAvailable(e){return function(e,t,i=Kc(e)){return!!t&&"active"===ih(e,`entity:${t}`,i).kind}(this.hass,e,this._haRegistry)}_renderOpeningEntityAvailable(e){return t=this._renderPlanHass,!!(i=e)&&!!t?.states?.[i];var t,i}_renderOpeningPlacementPreview(){return this._editorRuntimeOrThrow()._renderOpeningPlacementPreview()}_renderOpenings(e){const t=this._openingsR;if(!t.length)return j``;const i=this._spaceModel();if(!i)return j``;const n=e.color,r=this._spaceWalls,o=this._openCuts(),s=this._openingWallIndexFor(i,o).value;return j`${t.map(e=>{if(e.orphanReason)return j`{const i=this._renderPlanHass.states[t.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),s=(t.angle+90)*Math.PI/180,a="gate"===t.type?this._openingFace(t,r,!t.flip_v):null,l=ma(16,this._cellCm),c=a?-l*a.side:l*(t.flip_v?-1:1),h=t.rx+Math.cos(s)*c,d=t.ry+Math.sin(s)*c,u=this._scenePoint([h,d]),p=(u[0]-e.x)/e.w*100,m=(u[1]-e.y)/e.h*100;return B`
`})}`}_renderOpeningLocks(e){const t=this._openingsR.filter(e=>!e.orphanReason&&("door"===e.type||"gate"===e.type)&&e.lock&&this._renderOpeningEntityAvailable(e.lock));if(!t.length)return B``;const i=this._spaceModel();if(!i)return B``;const n=this._openCuts(),r=this._openingWallIndexFor(i,n).value;return B`${t.map(t=>{const i=this._renderPlanHass.states[t.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),s=(t.angle+90)*Math.PI/180,a="gate"===t.type?this._openingFace(t,r,!t.flip_v):null,l=ma(16,this._cellCm),c=a?-l*a.side:l*(t.flip_v?-1:1),h=t.rx+Math.cos(s)*c,d=t.ry+Math.sin(s)*c,u=this._scenePoint([h,d]),p=(u[0]-e.x)/e.w*100,m=(u[1]-e.y)/e.h*100;return B`
{"view"===this._mode&&(e.stopPropagation(),this._openingInfo=t)}}> -
`}_renderRoomSource(e){return this._editorRuntimeOrThrow()._renderRoomSource(e)}_renderRoomDialog(){return this._editorRuntimeOrThrow()._renderRoomDialog()}}ff.properties={_tabDrag:{state:!0},_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_continuityEpoch:{state:!0},_editorRuntimeLoadingVisible:{state:!0},_backdropGuard:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_partitionDeleteDialog:{state:!0},_roomDeleteDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingPreset:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_furnCategory:{state:!0},_furnPreviewInput:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_vacAllCamerasFor:{state:!0},_vacCalConfirm:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomCustomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_deviceInbox:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_preflightClipboardFallback:{state:!0},_backupExportDialog:{state:!0},_backupImportDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},ff.ZOOM_MAX=8,ff.ZOOM_MIN=1/3,ff.styles=[Nd,Em],customElements.get("houseplan-card")||customElements.define("houseplan-card",ff),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-card"===e.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${V_} `,"background:#3ea6ff;color:#04121f;font-weight:700","");export{wo as $,V as A,Lt as B,Ra as C,le as D,U as E,Et as F,Ca as G,Dd as H,Hd as I,ss as J,Jr as K,Id as L,Td as M,To as N,ls as O,zd as P,Ps as Q,Is as R,ao as S,Kt as T,Zr as U,po as V,tp as W,ip as X,Xo as Y,Or as Z,xo as _,o as a,Uo as a$,Gr as a0,Yr as a1,zr as a2,dp as a3,va as a4,So as a5,Ta as a6,ko as a7,ba as a8,wa as a9,js as aA,zl as aB,up as aC,Rl as aD,Hm as aE,rs as aF,Ts as aG,us as aH,_p as aI,Qt as aJ,Yt as aK,pi as aL,ca as aM,Xs as aN,Ys as aO,Sa as aP,Mo as aQ,Ro as aR,Ds as aS,Ft as aT,Xa as aU,sa as aV,oa as aW,Rn as aX,Tn as aY,ii as aZ,Vo as a_,Oi as aa,Fi as ab,Ti as ac,Ci as ad,$a as ae,os as af,Go as ag,zs as ah,zo as ai,Ws as aj,da as ak,pp as al,Bs as am,as as an,Xn as ao,Yn as ap,Kn as aq,Ui as ar,fe as as,iu as at,y_ as au,so as av,Aa as aw,Pt as ax,Ot as ay,yr as az,B as b,Ei as b$,j as b0,gp as b1,vp as b2,ur as b3,fr as b4,_r as b5,ar as b6,Pa as b7,nr as b8,or as b9,hi as bA,Zt as bB,Wt as bC,Jt as bD,Do as bE,mi as bF,rn as bG,ku as bH,xu as bI,ph as bJ,gd as bK,bi as bL,Si as bM,Mn as bN,vn as bO,Hh as bP,Kr as bQ,Lh as bR,Eh as bS,ji as bT,qi as bU,Wi as bV,fa as bW,lp as bX,Li as bY,Ni as bZ,Hi as b_,hr as ba,wn as bb,ir as bc,sr as bd,rr as be,pr as bf,ml as bg,cl as bh,bl as bi,yl as bj,il as bk,ll as bl,Co as bm,no as bn,ma as bo,nu as bp,Fd as bq,q_ as br,Od as bs,Es as bt,E_ as bu,B_ as bv,np as bw,qt as bx,Ad as by,Ed as bz,he as c,Bd as c$,_a as c0,kp as c1,fp as c2,cr as c3,Vi as c4,Hr as c5,Zn as c6,ge as c7,ke as c8,ve as c9,Th as cA,Rh as cB,xh as cC,_h as cD,ln as cE,uh as cF,Nh as cG,on as cH,Wu as cI,uu as cJ,$h as cK,Sh as cL,fh as cM,ud as cN,wi as cO,yi as cP,pu as cQ,vi as cR,ga as cS,ki as cT,xi as cU,Xr as cV,fl as cW,Qr as cX,Gt as cY,Ma as cZ,Qd as c_,jl as ca,Yl as cb,Ii as cc,rc as cd,El as ce,Xl as cf,Ul as cg,Vl as ch,tc as ci,ic as cj,ec as ck,nc as cl,Pl as cm,yn as cn,bn as co,Bt as cp,H_ as cq,lu as cr,I_ as cs,Ja as ct,cs as cu,Eo as cv,Ho as cw,$u as cx,Vh as cy,Md as cz,Nd as d,Kd as d0,jd as d1,ff as d2,$p as e,Vu as f,Gu as g,ti as h,Sp as i,vr as j,si as k,eu as l,Al as m,St as n,li as o,oi as p,Wh as q,Yu as r,Tt as s,tu as t,qh as u,hu as v,Mu as w,Nt as x,hn as y,xn as z}; +
`}_renderRoomSource(e){return this._editorRuntimeOrThrow()._renderRoomSource(e)}_renderRoomDialog(){return this._editorRuntimeOrThrow()._renderRoomDialog()}}gf.properties={_tabDrag:{state:!0},_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_continuityEpoch:{state:!0},_editorRuntimeLoadingVisible:{state:!0},_backdropGuard:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_partitionDeleteDialog:{state:!0},_roomDeleteDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingPreset:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_furnCategory:{state:!0},_furnPreviewInput:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_vacAllCamerasFor:{state:!0},_vacCalConfirm:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomCustomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_deviceInbox:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_preflightClipboardFallback:{state:!0},_backupExportDialog:{state:!0},_backupImportDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},gf.ZOOM_MAX=8,gf.ZOOM_MIN=1/3,gf.styles=[Nd,Hm],customElements.get("houseplan-card")||customElements.define("houseplan-card",gf),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-card"===e.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${G_} `,"background:#3ea6ff;color:#04121f;font-weight:700","");export{wo as $,V as A,Lt as B,Ra as C,le as D,U as E,Et as F,Ca as G,Dd as H,Hd as I,ss as J,Jr as K,Id as L,Td as M,To as N,ls as O,zd as P,Ps as Q,Is as R,ao as S,Kt as T,Zr as U,po as V,ip as W,np as X,Xo as Y,Or as Z,xo as _,o as a,Uo as a$,Gr as a0,Yr as a1,zr as a2,up as a3,va as a4,So as a5,Ta as a6,ko as a7,ba as a8,wa as a9,js as aA,zl as aB,pp as aC,Rl as aD,Nm as aE,rs as aF,Ts as aG,us as aH,fp as aI,Qt as aJ,Yt as aK,pi as aL,ca as aM,Xs as aN,Ys as aO,Sa as aP,Mo as aQ,Ro as aR,Ds as aS,Ft as aT,Xa as aU,sa as aV,oa as aW,Rn as aX,Tn as aY,ii as aZ,Vo as a_,Oi as aa,Fi as ab,Ti as ac,Ci as ad,$a as ae,os as af,Go as ag,zs as ah,zo as ai,Ws as aj,da as ak,mp as al,Bs as am,as as an,Xn as ao,Yn as ap,Kn as aq,Ui as ar,fe as as,iu as at,b_ as au,so as av,Aa as aw,Pt as ax,Ot as ay,yr as az,B as b,Ei as b$,j as b0,vp as b1,yp as b2,ur as b3,fr as b4,_r as b5,ar as b6,Pa as b7,nr as b8,or as b9,hi as bA,Zt as bB,Wt as bC,Jt as bD,Do as bE,mi as bF,rn as bG,ku as bH,xu as bI,ph as bJ,gd as bK,bi as bL,Si as bM,Mn as bN,vn as bO,Hh as bP,Kr as bQ,Lh as bR,Eh as bS,ji as bT,qi as bU,Wi as bV,fa as bW,cp as bX,Li as bY,Ni as bZ,Hi as b_,hr as ba,wn as bb,ir as bc,sr as bd,rr as be,pr as bf,ml as bg,cl as bh,bl as bi,yl as bj,il as bk,ll as bl,Co as bm,no as bn,ma as bo,nu as bp,Fd as bq,B_ as br,Od as bs,Es as bt,H_ as bu,j_ as bv,rp as bw,qt as bx,Ad as by,Ed as bz,he as c,Qd as c$,_a as c0,xp as c1,gp as c2,cr as c3,Vi as c4,Hr as c5,Zn as c6,ge as c7,ke as c8,ve as c9,Md as cA,Th as cB,Rh as cC,xh as cD,_h as cE,ln as cF,uh as cG,Nh as cH,on as cI,qu as cJ,uu as cK,$h as cL,Sh as cM,fh as cN,ud as cO,wi as cP,yi as cQ,pu as cR,vi as cS,ga as cT,ki as cU,xi as cV,Xr as cW,fl as cX,Qr as cY,Gt as cZ,Ma as c_,jl as ca,Yl as cb,Ii as cc,rc as cd,El as ce,Xl as cf,Ul as cg,Vl as ch,tc as ci,ic as cj,ec as ck,nc as cl,Pl as cm,yn as cn,bn as co,Bt as cp,N_ as cq,lu as cr,E_ as cs,Ja as ct,cs as cu,Eo as cv,Ho as cw,$u as cx,Su as cy,Vh as cz,Nd as d,Bd as d0,Kd as d1,jd as d2,gf as d3,Sp as e,Gu as f,Ku as g,ti as h,Mp as i,vr as j,si as k,eu as l,Al as m,St as n,li as o,oi as p,Wh as q,Xu as r,Tt as s,tu as t,qh as u,hu as v,Ru as w,Nt as x,hn as y,xn as z}; diff --git a/dist/houseplan-assets/houseplan-editor-runtime-CisKVaG7.js b/dist/houseplan-assets/houseplan-editor-runtime-CM78ZHZU.js similarity index 78% rename from dist/houseplan-assets/houseplan-editor-runtime-CisKVaG7.js rename to dist/houseplan-assets/houseplan-editor-runtime-CM78ZHZU.js index b9968a85..b8f8aa48 100644 --- a/dist/houseplan-assets/houseplan-editor-runtime-CisKVaG7.js +++ b/dist/houseplan-assets/houseplan-editor-runtime-CM78ZHZU.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";import{e as t,i as e,E as s,D as i,b as o,A as a,a as n,c as r,s as h,m as l,d as c,l as d,t as p,w as _,f as u,g as m,v as g,r as f,h as b,j as v,p as y,k as $,n as w,o as k,q as x,u as S,x as M,y as D,z as C,B as P,G as I,C as T,F as R,H as z,I as F,J as A,K as N,L as H,M as L,N as O,O as E,P as B,Q as W,R as G,S as j,T as q,U,V as K,W as V,X as J,Y,Z as X,_ as Z,$ as Q,a0 as tt,a1 as et,a2 as st,a3 as it,a4 as ot,a5 as at,a6 as nt,a7 as rt,a8 as ht,a9 as lt,aa as ct,ab as dt,ac as pt,ad as _t,ae as ut,af as mt,ag as gt,ah as ft,ai as bt,aj as vt,ak as yt,al as $t,am as wt,an as kt,ao as xt,ap as St,aq as Mt,ar as Dt,as as Ct,at as Pt,au as It,av as Tt,aw as Rt,ax as zt,ay as Ft,az as At,aA as Nt,aB as Ht,aC as Lt,aD as Ot,aE as Et,aF as Bt,aG as Wt,aH as Gt,aI as jt,aJ as qt,aK as Ut,aL as Kt,aM as Vt,aN as Jt,aO as Yt,aP as Xt,aQ as Zt,aR as Qt,aS as te,aT as ee,aU as se,aV as ie,aW as oe,aX as ae,aY as ne,aZ as re,a_ as he,a$ as le,b0 as ce,b1 as de,b2 as pe,b3 as _e,b4 as ue,b5 as me,b6 as ge,b7 as fe,b8 as be,b9 as ve,ba as ye,bb as $e,bc as we,bd as ke,be as xe,bf as Se,bg as Me,bh as De,bi as Ce,bj as Pe,bk as Ie,bl as Te,bm as Re,bn as ze,bo as Fe,bp as Ae,bq as Ne,br as He,bs as Le,bt as Oe,bu as Ee,bv as Be,bw as We,bx as Ge,by as je,bz as qe,bA as Ue,bB as Ke,bC as Ve,bD as Je,bE as Ye,bF as Xe,bG as Ze,bH as Qe,bI as ts,bJ as es,bK as ss,bL as is,bM as os,bN as as,bO as ns,bP as rs,bQ as hs,bR as ls,bS as cs,bT as ds,bU as ps,bV as _s,bW as us,bX as ms,bY as gs,bZ as fs,b_ as bs,b$ as vs,c0 as ys,c1 as $s,c2 as ws,c3 as ks,c4 as xs,c5 as Ss,c6 as Ms,c7 as Ds,c8 as Cs,c9 as Ps,ca as Is,cb as Ts,cc as Rs,cd as zs,ce as Fs,cf as As,cg as Ns,ch as Hs,ci as Ls,cj as Os,ck as Es,cl as Bs,cm as Ws,cn as Gs,co as js,cp as qs,cq as Us,cr as Ks,cs as Vs,ct as Js,cu as Ys,cv as Xs,cw as Zs,cx as Qs,cy as ti,cz as ei,cA as si,cB as ii,cC as oi,cD as ai,cE as ni,cF as ri,cG as hi,cH as li,cI as ci,cJ as di,cK as pi,cL as _i,cM as ui,cN as mi,cO as gi,cP as fi,cQ as bi,cR as vi,cS as yi,cT as $i,cU as wi,cV as ki,cW as xi,cX as Si,cY as Mi,cZ as Di}from"./houseplan-card-B5mDyjuC.js";import{i as Ci,c as Pi,e as Ii,r as Ti,a as Ri,b as zi,s as Fi,t as Ai}from"./backdrop-pick-DFFLwJ_d.js";const Ni={},Hi=t(class extends e{constructor(){super(...arguments),this.ot=Ni}render(t,e){return e()}update(t,[e,i]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((t,e)=>t===this.ot[e]))return s}else if(this.ot===e)return s;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,i)}}),Li=(t,e,s)=>Math.min(s,Math.max(e,Number.isFinite(t)?t:e));function Oi(t){return Number.isFinite(t)?(t%360+360)%360:0}function Ei(t){if("string"!=typeof t)return null;const e=t.trim().replace(/^#/,""),s=/^[0-9a-fA-F]{3}$/.test(e)?e.split("").map(t=>`${t}${t}`).join(""):e;return/^[0-9a-fA-F]{6}$/.test(s)?`#${s.toLowerCase()}`:null}function Bi(t){const e=Ei(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 Wi(t){const e=Li(t.r,0,255)/255,s=Li(t.g,0,255)/255,i=Li(t.b,0,255)/255,o=Math.max(e,s,i),a=o-Math.min(e,s,i);let n=0;return a>0&&(n=o===e?(s-i)/a%6*60:o===s?60*((i-e)/a+2):60*((e-s)/a+4)),{h:Oi(n),s:0===o?0:a/o*100,v:100*o}}function Gi(t){return function(t){const e=t=>Math.round(Li(t,0,255)).toString(16).padStart(2,"0");return`#${e(t.r)}${e(t.g)}${e(t.b)}`}(function(t){const e=Oi(t.h),s=Li(t.s,0,100)/100,i=Li(t.v,0,100)/100,o=i*s,a=o*(1-Math.abs(e/60%2-1)),n=i-o;let r;return r=e<60?[o,a,0]:e<120?[a,o,0]:e<180?[0,o,a]:e<240?[0,a,o]:e<300?[a,0,o]:[o,0,a],{r:Math.round(255*(r[0]+n)),g:Math.round(255*(r[1]+n)),b:Math.round(255*(r[2]+n))}}(t))}function ji(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 qi(t,e,s,i=7,o=8){const a=s.left+o,n=s.top+o,r=Math.max(a,s.left+s.width-o),h=Math.max(n,s.top+s.height-o),l=Math.max(0,r-a),c=Math.max(0,h-n),d=Math.min(Math.max(0,e.width),l),p=Math.min(Math.max(0,e.height),c);let _=t.left;_+d>r&&(_=t.right-d),_=Math.min(Math.max(a,_),Math.max(a,r-d));const u=t.bottom+i,m=t.top-i-p;let g="bottom",f=u;return u+p>h&&m>=n?(g="top",f=Math.min(Math.max(n,m),Math.max(n,h-p))):f=Math.min(Math.max(n,u),Math.max(n,h-p)),{left:Math.round(_),top:Math.round(f),side:g,maxWidth:Math.round(l),maxHeight:Math.round(c)}}class Ui{constructor(t,e,s){this.owner=t,this.name=e,this.keydown=s,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 s=this.ensureFallback();return s?(i(o`${t}`,s),s):null}surface(t,e){return e?this.owner.shadowRoot?.querySelector(t)||null:this.root?.querySelector(t)||null}destroy(){this.root&&i(a,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 Ki={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class Vi extends r{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=Ki,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 Ui(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=Ei(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 s=this._overlayDispose;this._overlayDispose=null,s?.();const i=this.renderRoot.querySelector(".picker");if(i?.hidePopover)try{i.matches(":popover-open")&&i.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=Vi.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 s=this._surface();if(!t||!e?.isConnected||!s?.isConnected)return!1;const i=ji(t);if(s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden",this._supportsPopover()&&s.showPopover)try{s.matches(":popover-open")||s.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),s=this._surface(),!s?.isConnected)return!1;s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden"}const o=e.getBoundingClientRect(),a=s.getBoundingClientRect();if(!(o.width&&o.height&&a.width&&a.height))return!1;const n=qi(o,a,i);return s.style.left=`${n.left}px`,s.style.top=`${n.top}px`,s.dataset.side=n.side,s.style.visibility="",!0}_labels(){const t=this.pickerLabels||Ki;return{title:t.title||Ki.title,hue:t.hue||Ki.hue,saturation:t.saturation||Ki.saturation,value:t.value||Ki.value,hex:t.hex||Ki.hex,invalidHex:t.invalidHex||Ki.invalidHex}}_syncFromColor(t,e){const s=Ei(h(t,"#607d8b"))||"#607d8b",i=Bi(s);if(!i)return;const o=Wi(i);(!e||o.s>1e-4)&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._hexDraft=s,this._hexInvalid=!1,this._lastValidColor=s}_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(Gi({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,s,i,o){if(!t.shiftKey)return;const a="ArrowRight"===t.key||"ArrowUp"===t.key?10:"ArrowLeft"===t.key||"ArrowDown"===t.key?-10:0;a&&(t.preventDefault(),t.stopPropagation(),o(Math.min(i,Math.max(s,e+a))))}_setSvFromPointer(t,e){const s=t.currentTarget.getBoundingClientRect();s.width&&s.height&&(this._saturation=Math.min(100,Math.max(0,(t.clientX-s.left)/s.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(t.clientY-s.top)/s.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 s=Ei(e),i=s?Bi(s):null;if(!s||!i)return;const o=Wi(i);o.s>1e-4&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._emit(s,this.opacity,!0)}_commitHex(){const t=Ei(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,s=!1){const i=Ei(t)||this._lastValidColor,o=Number.isFinite(e)?e:Number(this.opacity)||0,a=Math.min(1,Math.max(0,o));this.color=i,this.opacity=a,this._lastValidColor=i,s||(this._hexDraft=i),this._hexInvalid=!1,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:i,opacity:a},bubbles:!0,composed:!0}))}_pickerTemplate(t){const e=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),s=Gi({h:this._hue,s:this._saturation,v:this._value}),i=Gi({h:this._hue,s:100,v:100}),n=this._labels(),r=Math.round(this._hue),h=Math.round(this._saturation),l=Math.round(this._value);return o` +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";import{e as t,i as e,E as s,D as i,b as o,A as a,a as n,c as r,s as h,m as l,d as c,l as d,t as p,w as u,f as _,g as m,v as g,r as f,h as b,j as v,p as y,k as $,n as w,o as k,q as x,u as S,x as M,y as D,z as C,B as P,G as I,C as T,F as R,H as z,I as F,J as A,K as N,L as H,M as L,N as O,O as E,P as B,Q as W,R as G,S as j,T as q,U,V,W as K,X as J,Y,Z as X,_ as Z,$ as Q,a0 as tt,a1 as et,a2 as st,a3 as it,a4 as ot,a5 as at,a6 as nt,a7 as rt,a8 as ht,a9 as lt,aa as ct,ab as dt,ac as pt,ad as ut,ae as _t,af as mt,ag as gt,ah as ft,ai as bt,aj as vt,ak as yt,al as $t,am as wt,an as kt,ao as xt,ap as St,aq as Mt,ar as Dt,as as Ct,at as Pt,au as It,av as Tt,aw as Rt,ax as zt,ay as Ft,az as At,aA as Nt,aB as Ht,aC as Lt,aD as Ot,aE as Et,aF as Bt,aG as Wt,aH as Gt,aI as jt,aJ as qt,aK as Ut,aL as Vt,aM as Kt,aN as Jt,aO as Yt,aP as Xt,aQ as Zt,aR as Qt,aS as te,aT as ee,aU as se,aV as ie,aW as oe,aX as ae,aY as ne,aZ as re,a_ as he,a$ as le,b0 as ce,b1 as de,b2 as pe,b3 as ue,b4 as _e,b5 as me,b6 as ge,b7 as fe,b8 as be,b9 as ve,ba as ye,bb as $e,bc as we,bd as ke,be as xe,bf as Se,bg as Me,bh as De,bi as Ce,bj as Pe,bk as Ie,bl as Te,bm as Re,bn as ze,bo as Fe,bp as Ae,bq as Ne,br as He,bs as Le,bt as Oe,bu as Ee,bv as Be,bw as We,bx as Ge,by as je,bz as qe,bA as Ue,bB as Ve,bC as Ke,bD as Je,bE as Ye,bF as Xe,bG as Ze,bH as Qe,bI as ts,bJ as es,bK as ss,bL as is,bM as os,bN as as,bO as ns,bP as rs,bQ as hs,bR as ls,bS as cs,bT as ds,bU as ps,bV as us,bW as _s,bX as ms,bY as gs,bZ as fs,b_ as bs,b$ as vs,c0 as ys,c1 as $s,c2 as ws,c3 as ks,c4 as xs,c5 as Ss,c6 as Ms,c7 as Ds,c8 as Cs,c9 as Ps,ca as Is,cb as Ts,cc as Rs,cd as zs,ce as Fs,cf as As,cg as Ns,ch as Hs,ci as Ls,cj as Os,ck as Es,cl as Bs,cm as Ws,cn as Gs,co as js,cp as qs,cq as Us,cr as Vs,cs as Ks,ct as Js,cu as Ys,cv as Xs,cw as Zs,cx as Qs,cy as ti,cz as ei,cA as si,cB as ii,cC as oi,cD as ai,cE as ni,cF as ri,cG as hi,cH as li,cI as ci,cJ as di,cK as pi,cL as ui,cM as _i,cN as mi,cO as gi,cP as fi,cQ as bi,cR as vi,cS as yi,cT as $i,cU as wi,cV as ki,cW as xi,cX as Si,cY as Mi,cZ as Di,c_ as Ci}from"./houseplan-card-wtxKVHkp.js";import{i as Pi,c as Ii,e as Ti,r as Ri,a as zi,b as Fi,s as Ai,t as Ni}from"./backdrop-pick-TMl_2iBT.js";const Hi={},Li=t(class extends e{constructor(){super(...arguments),this.ot=Hi}render(t,e){return e()}update(t,[e,i]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((t,e)=>t===this.ot[e]))return s}else if(this.ot===e)return s;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,i)}}),Oi=(t,e,s)=>Math.min(s,Math.max(e,Number.isFinite(t)?t:e));function Ei(t){return Number.isFinite(t)?(t%360+360)%360:0}function Bi(t){if("string"!=typeof t)return null;const e=t.trim().replace(/^#/,""),s=/^[0-9a-fA-F]{3}$/.test(e)?e.split("").map(t=>`${t}${t}`).join(""):e;return/^[0-9a-fA-F]{6}$/.test(s)?`#${s.toLowerCase()}`:null}function Wi(t){const e=Bi(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 Gi(t){const e=Oi(t.r,0,255)/255,s=Oi(t.g,0,255)/255,i=Oi(t.b,0,255)/255,o=Math.max(e,s,i),a=o-Math.min(e,s,i);let n=0;return a>0&&(n=o===e?(s-i)/a%6*60:o===s?60*((i-e)/a+2):60*((e-s)/a+4)),{h:Ei(n),s:0===o?0:a/o*100,v:100*o}}function ji(t){return function(t){const e=t=>Math.round(Oi(t,0,255)).toString(16).padStart(2,"0");return`#${e(t.r)}${e(t.g)}${e(t.b)}`}(function(t){const e=Ei(t.h),s=Oi(t.s,0,100)/100,i=Oi(t.v,0,100)/100,o=i*s,a=o*(1-Math.abs(e/60%2-1)),n=i-o;let r;return r=e<60?[o,a,0]:e<120?[a,o,0]:e<180?[0,o,a]:e<240?[0,a,o]:e<300?[a,0,o]:[o,0,a],{r:Math.round(255*(r[0]+n)),g:Math.round(255*(r[1]+n)),b:Math.round(255*(r[2]+n))}}(t))}function qi(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 Ui(t,e,s,i=7,o=8){const a=s.left+o,n=s.top+o,r=Math.max(a,s.left+s.width-o),h=Math.max(n,s.top+s.height-o),l=Math.max(0,r-a),c=Math.max(0,h-n),d=Math.min(Math.max(0,e.width),l),p=Math.min(Math.max(0,e.height),c);let u=t.left;u+d>r&&(u=t.right-d),u=Math.min(Math.max(a,u),Math.max(a,r-d));const _=t.bottom+i,m=t.top-i-p;let g="bottom",f=_;return _+p>h&&m>=n?(g="top",f=Math.min(Math.max(n,m),Math.max(n,h-p))):f=Math.min(Math.max(n,_),Math.max(n,h-p)),{left:Math.round(u),top:Math.round(f),side:g,maxWidth:Math.round(l),maxHeight:Math.round(c)}}class Vi{constructor(t,e,s){this.owner=t,this.name=e,this.keydown=s,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 s=this.ensureFallback();return s?(i(o`${t}`,s),s):null}surface(t,e){return e?this.owner.shadowRoot?.querySelector(t)||null:this.root?.querySelector(t)||null}destroy(){this.root&&i(a,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 Ki={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class Ji extends r{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=Ki,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 Vi(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=Bi(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 s=this._overlayDispose;this._overlayDispose=null,s?.();const i=this.renderRoot.querySelector(".picker");if(i?.hidePopover)try{i.matches(":popover-open")&&i.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=Ji.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 s=this._surface();if(!t||!e?.isConnected||!s?.isConnected)return!1;const i=qi(t);if(s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden",this._supportsPopover()&&s.showPopover)try{s.matches(":popover-open")||s.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),s=this._surface(),!s?.isConnected)return!1;s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden"}const o=e.getBoundingClientRect(),a=s.getBoundingClientRect();if(!(o.width&&o.height&&a.width&&a.height))return!1;const n=Ui(o,a,i);return s.style.left=`${n.left}px`,s.style.top=`${n.top}px`,s.dataset.side=n.side,s.style.visibility="",!0}_labels(){const t=this.pickerLabels||Ki;return{title:t.title||Ki.title,hue:t.hue||Ki.hue,saturation:t.saturation||Ki.saturation,value:t.value||Ki.value,hex:t.hex||Ki.hex,invalidHex:t.invalidHex||Ki.invalidHex}}_syncFromColor(t,e){const s=Bi(h(t,"#607d8b"))||"#607d8b",i=Wi(s);if(!i)return;const o=Gi(i);(!e||o.s>1e-4)&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._hexDraft=s,this._hexInvalid=!1,this._lastValidColor=s}_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(ji({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,s,i,o){if(!t.shiftKey)return;const a="ArrowRight"===t.key||"ArrowUp"===t.key?10:"ArrowLeft"===t.key||"ArrowDown"===t.key?-10:0;a&&(t.preventDefault(),t.stopPropagation(),o(Math.min(i,Math.max(s,e+a))))}_setSvFromPointer(t,e){const s=t.currentTarget.getBoundingClientRect();s.width&&s.height&&(this._saturation=Math.min(100,Math.max(0,(t.clientX-s.left)/s.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(t.clientY-s.top)/s.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 s=Bi(e),i=s?Wi(s):null;if(!s||!i)return;const o=Gi(i);o.s>1e-4&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._emit(s,this.opacity,!0)}_commitHex(){const t=Bi(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,s=!1){const i=Bi(t)||this._lastValidColor,o=Number.isFinite(e)?e:Number(this.opacity)||0,a=Math.min(1,Math.max(0,o));this.color=i,this.opacity=a,this._lastValidColor=i,s||(this._hexDraft=i),this._hexInvalid=!1,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:i,opacity:a},bubbles:!0,composed:!0}))}_pickerTemplate(t){const e=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),s=ji({h:this._hue,s:this._saturation,v:this._value}),i=ji({h:this._hue,s:100,v:100}),n=this._labels(),r=Math.round(this._hue),h=Math.round(this._saturation),l=Math.round(this._value);return o` -
`}afterRender(){const t=this.host.root();this._blocked&&(this._openGroupId?this.closeGroup(!1):"palette"===this._currentModel?.kind&&this._currentModel.dismiss?.()),this._syncGlobalDismissListener();const e=t.querySelector(".editor-secondary"),s=this._renderedContext,i=e?.dataset.contextId||"";if(i&&s&&i!==s&&!window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches&&(this._contentAnimation?.cancel(),this._contentAnimation=e?.querySelector(".editor-secondary-content")?.animate([{opacity:.35,transform:"translateY(-4px)"},{opacity:1,transform:"translateY(0)"}],{duration:100,easing:"ease-out"})),this._renderedContext=i,s&&i!==s&&this._focusOwned){const s=i?e?.querySelector('[tabindex="0"], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])'):t.querySelector(".editbar-tools");s?.focus?.(),this._focusOwned=!!i}if("palette"===this._currentModel?.kind&&this._currentModel.launcherId){const e=[...t.querySelectorAll("[data-editor-palette]")].find(t=>t.dataset.editorPalette===this._currentModel?.launcherId);e&&null!==e.offsetParent||this._currentModel.dismiss?.()}if(!this._openGroupId)return;const o=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===this._openGroupId);o&&null!==o.offsetParent||this.closeGroup(!0)}handleOutsideDismiss(t){if(mn(t,t=>t.hasAttribute("data-editor-navigation")))return"pointerdown"===t.type?this.closeForNavigation():"click"===t.type&&this._dismissPointerTarget&&this._clearDismissClick(),!1;if("click"===t.type&&this._dismissPointerTarget){return!!(t.composedPath?.()||[t.target]).includes(this._dismissPointerTarget)&&(this._clearDismissClick(),t.preventDefault(),t.stopImmediatePropagation(),!0)}const e=this._openGroupId||this._blocked||"palette"!==this._currentModel?.kind?null:this._currentModel;if("pointerdown"===t.type&&(this._openGroupId||e)){const s=mn(t,t=>t.classList.contains("editor-secondary")),i=mn(t,t=>!!t.dataset.editorGroup),o=mn(t,t=>!!t.dataset.editorPalette),a=mn(t,t=>t.classList.contains("stage"));if(!(s||i||o||"stay-open-on-canvas"===e?.dismissPolicy&&a))return t.preventDefault(),t.stopImmediatePropagation(),this._dismissPointerTarget=t.target,clearTimeout(this._dismissTimer),this._dismissTimer=window.setTimeout(()=>{this._clearDismissClick()},800),this._openGroupId?this.closeGroup(!1):e?.dismiss?.(),this._syncGlobalDismissListener(),!0}return!1}_activateGroupItem(t,e,s){if(t.id!==this._openGroupId)return;const i=t.items.find(t=>t.id===e);if(!i||i.disabled)return;const o="toggle"===i.role&&"stay-open"===i.closePolicy,a=0===s.detail;o||(this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener()),i.invoke(),!o&&a&&this.host.updateComplete().then(()=>{const e=[...this.host.root().querySelectorAll("[data-editor-group]")].find(e=>e.dataset.editorGroup===t.id&&null!==e.offsetParent);e?.focus()})}_groupKeydown(t){if(!["ArrowLeft","ArrowRight","Home","End"].includes(t.key))return;const e=[...t.currentTarget.querySelectorAll(".editor-group-item:not([disabled])")];if(!e.length)return;t.preventDefault();const s=e.indexOf(this.host.root().activeElement),i="Home"===t.key?0:"End"===t.key?e.length-1:"ArrowLeft"===t.key?s<=0?e.length-1:s-1:(s+1)%e.length;e[i]?.focus()}_clearDismissClick(){this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}_syncGlobalDismissListener(){const t=!this._blocked&&"palette"===this._currentModel?.kind,e=!!this._openGroupId||t||!!this._dismissPointerTarget;e!==this._globalDismissListening&&(this._globalDismissListening=e,e?(window.addEventListener("pointerdown",this._globalDismissGuard,!0),window.addEventListener("click",this._globalDismissGuard,!0)):(window.removeEventListener("pointerdown",this._globalDismissGuard,!0),window.removeEventListener("click",this._globalDismissGuard,!0)))}}const fn=t=>t.stopPropagation(),bn=1e-6*I,vn=6*I;function yn(t){return Number.isFinite(t)?Math.round(t/I)*I:t}const $n=(t,e,s,i)=>Math.hypot(s-t,i-e),wn=(t,e,s,i,o,a,n,r)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+n-(t+s))),Math.max(Math.abs(a-e),Math.abs(a+r-(e+i)))),kn=(t,e,s,i,o,a,n)=>{const r=Math.max(Number(i)||0,0)/2,h=Math.PI/180,l=Math.cos(s*h)*r,c=Math.sin(s*h)*r,d=Math.cos(n*h)*r,p=Math.sin(n*h)*r,_=Math.max($n(t+l,e+c,o+d,a+p),$n(t-l,e-c,o-d,a-p)),u=Math.max($n(t+l,e+c,o-d,a-p),$n(t-l,e-c,o+d,a+p));return Math.min(_,u)},xn=t=>{const e=Number(t?.cell_cm);return e>0?e:5};function Sn(t,e){const s=JSON.parse(JSON.stringify(t||[])),i=JSON.parse(JSON.stringify(e||{}));let o=0,a=0,n=0,r=0,h="",l=0,c=0,d=0;const p=(t,e)=>{Number.isFinite(t)&&Number.isFinite(e)&&t!==e&&Math.abs(e-t)<=bn&&c++},_=(t,e)=>{p(t[0],e[0]),p(t[1],e[1])},u={};let m=5;for(const t of s){const e=xn(t);null!=t?.id&&(u[String(t.id)]=e),e>m&&(m=e)}const g=(t,e,s,i=!1)=>{if(!(t>bn||i))return;o++,t>n&&(n=t);const a=t*T*e;a>r&&(r=a,h=s)};for(const t of s){const e=xn(t),s=null!=t?.id?String(t.id):"";for(const i of t.rooms||[]){a++;let t=0;if(i.poly?.length)i.poly=i.poly.map(e=>{const s=[yn(e[0]),yn(e[1])];return t=Math.max(t,$n(e[0],e[1],s[0],s[1])),_(e,s),s});else if(null!=i.x&&null!=i.y){const e=i.x,s=i.y,o=i.w||0,a=i.h||0,n=yn(e+o),r=yn(s+a),h=yn(e),l=yn(s),c=Math.max(I,n-h),d=Math.max(I,r-l);t=wn(e,s,o,a,h,l,c,d),i.x=h,i.y=l,i.w=c,i.h=d,p(e,h),p(s,l),p(e+o,h+c),p(s+a,l+d)}g(t,e,s)}for(const i of t.room_drafts||[]){a++;let t=0;const o=i.points||[],n=o.map(e=>{const s=[yn(e[0]),yn(e[1])];return t=Math.max(t,$n(e[0],e[1],s[0],s[1])),s}),r=n.length?[n[0]]:[],h=n.length?[[o[0],n[0]]]:[],l=[];for(let t=0;t+1=2)for(const[t,e]of h)_(t,e);g(t,e,s)}if(Array.isArray(t.room_drafts)){const e=t.room_drafts.length;t.room_drafts=t.room_drafts.filter(t=>t.points?.length>=2),d+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const i of t.partitions||[]){a++;const o=[i.a[0],i.a[1]],n=[i.b[0],i.b[1]],r=[yn(o[0]),yn(o[1])],h=[yn(n[0]),yn(n[1])];let l=Math.max($n(o[0],o[1],r[0],r[1]),$n(n[0],n[1],h[0],h[1]));const c=$n(r[0],r[1],h[0],h[1]),d=(t.openings||[]).filter(t=>"partition"===t.host?.kind&&t.host.id===i.id).every(t=>{const e=Number(t.length),s=Number(t.host.t),i=s*c;return Number.isFinite(e)&&e>0&&Number.isFinite(s)&&s>=0&&s<=1&&i-e/2>=-bn&&i+e/2<=c+bn});c>bn&&d?(i.a=r,i.b=h,_(o,r),_(n,h)):l=0,g(l,e,s)}for(const i of t.wall_columns||[]){a++;const t=[i.center[0],i.center[1]],o=[yn(t[0]),yn(t[1])],n=$n(t[0],t[1],o[0],o[1]);i.center=o,_(t,o),g(n,e,s)}for(const i of t.decor||[]){a++;let t=0;if("line"===i.kind){const e=[i.x1,i.y1],s=[i.x2,i.y2],o=[yn(e[0]),yn(e[1])],a=[yn(s[0]),yn(s[1])];t=Math.max($n(e[0],e[1],o[0],o[1]),$n(s[0],s[1],a[0],a[1])),i.x1=o[0],i.y1=o[1],i.x2=a[0],i.y2=a[1],_(e,o),_(s,a)}else{const e=i.x,s=i.y,o=i.w,a=i.h,n=yn(e),r=yn(s);if(null!=i.w&&null!=i.h){const h=yn(e+o),l=yn(s+a),c=Math.max(I,h-n),d=Math.max(I,l-r);t=wn(e,s,o,a,n,r,c,d),i.w=c,i.h=d,p(e+o,n+c),p(s+a,r+d)}else t=$n(e,s,n,r);i.x=n,i.y=r,p(e,n),p(s,r)}g(t,e,s)}for(const i of t.openings||[]){if(a++,"partition"===i.host?.kind){const o=(t.partitions||[]).find(t=>t.id===i.host.id),a=Number(i.host.t);if(!o||!Number.isFinite(a)||a<0||a>1)continue;const n=o.b[0]-o.a[0],r=o.b[1]-o.a[1];if(Math.hypot(n,r)<=bn)continue;const h=o.a[0]+n*a,c=o.a[1]+r*a;let d=180*Math.atan2(r,n)/Math.PI;d>=90?d-=180:d<-90&&(d+=180);const p=Number(i.angle),_=!(Number.isFinite(p)&&p===d),u=kn(i.x,i.y,Number.isFinite(p)?p:d,Number(i.length)||0,h,c,d);i.x=h,i.y=c,i.angle=d,_&&l++,g(u,e,s,_);continue}const o=P([i.x,i.y],t.rooms||[],vn,{step:I,length:Number(i.length)||0});if(!o)continue;const n=Number(i.angle),r=!(Number.isFinite(n)&&n===o.angle),h=kn(i.x,i.y,Number.isFinite(n)?n:o.angle,Number(i.length)||0,o.x,o.y,o.angle);i.x=o.x,i.y=o.y,i.angle=o.angle,r&&l++,g(h,e,s,r)}}for(const[t,e]of Object.entries(i)){if(!e||"object"!=typeof e)continue;const s=e;if("number"!=typeof s.x||"number"!=typeof s.y)continue;a++;const o=yn(s.x),n=yn(s.y),r=$n(s.x,s.y,o,n);i[t]={...s,x:o,y:n},p(s.x,o),p(s.y,n);const h="string"==typeof s.s?s.s:"";g(r,u[h]??m,h)}return{spaces:s,layout:i,report:{moved:o,coordsCanonicalized:c,total:a,maxShift:n,maxShiftCm:r,maxSpace:h,rotated:l,removedDrafts:d},changed:o>0||c>0}}function Mn(t,e){const s=e.base+t*e.span;return Math.min(1,Math.max(0,s))}const Dn=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),Cn=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1]);function Pn(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(a<=0)return Cn(t,e);let n=((t[0]-e[0])*i+(t[1]-e[1])*o)/a;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))}function In(t,e,s,i,o){for(const i of e)if(i&&!s.has(i.id)&&Dn(i.a)&&Dn(i.b)&&(Cn(t,i.a)<=o||Cn(t,i.b)<=o))return!0;for(const e of i?.roomPolygons||[])for(let s=0;s0&&r>0))return null;if(Math.abs(o[0]/n*(a[1]/r)-o[1]/n*(a[0]/r))>s)return null;for(const[s,o]of[[t.a,e.a],[t.a,e.b],[t.b,e.a],[t.b,e.b]])if(Cn(s,o)<=i)return[(s[0]+o[0])/2,(s[1]+o[1])/2];return null}function Rn(t,e){const s=.05*(Number(e.pitch)>0?Number(e.pitch):1),i=new Map;let o=(t||[]).filter(t=>!!t&&"string"==typeof t.id&&Dn(t.a)&&Dn(t.b));if(o.length<2)return{partitions:[...t||[]],merged:0,openingMoves:[]};const a=e.seedIds&&e.seedIds.length?new Set(e.seedIds):null;let n=0;for(let t=0;tCn(e,t.at)>s);let c=l[0]??r.a,d=l[l.length-1]??h.b;if(l.length<2&&(c=r.a,d=h.b),d[0]0?(d[0]-c[0])/_:0,m=_>0?(d[1]-c[1])/_:0,g=t=>_>0?((t[0]-c[0])*u+(t[1]-c[1])*m)/_:0;for(const t of[r,h]){const e=g(t.a),s=g(t.b)-e;for(const[o,a]of i)a.toId===t.id&&i.set(o,{fromId:a.fromId,toId:p.id,base:e+a.base*s,span:a.span*s});i.set(t.id,{fromId:t.id,toId:p.id,base:e,span:s})}a&&a.add(p.id),o=o.filter((e,s)=>s!==t.i&&s!==t.j),o.splice(t.i,0,p),n++}return{partitions:o,merged:n,openingMoves:[...i.values()]}}function zn(t,e,s,i){if(!t?.length||!s.length)return 0;const o=new Map(s.map(t=>[t.fromId,t]));let a=0;for(const s of t){const t=s?.host;if(!t||"partition"!==t.kind)continue;const n=o.get(t.id);if(!n)continue;s.host={...t,id:n.toId,t:Mn(t.t,n)},a++;const r=z(s,e,i.coordScale,i.cellCm,i.gridPitch).resolved;r&&Object.assign(s,F(s,r,i.coordScale))}return a}function Fn(t,e){const s=e?.excludeDraftId||null;return{roomPolygons:(t?.rooms||[]).map(t=>R(t)).filter(t=>!!t),columns:t?.wall_columns||[],draftEnds:(t?.room_drafts||[]).flatMap(t=>{if(s&&t?.id===s)return[];const e=t?.points||[];return e.length?[e[0],e[e.length-1]]:[]})}}const An=t=>!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),Nn=(t,e)=>t[0]-e[0]||t[1]-e[1],Hn=(t,e,s,i)=>[t[0]+e*i,t[1]+s*i],Ln=(t,e,s,i)=>(t[0]-e[0])*s+(t[1]-e[1])*i,On=(t,e,s,i,o)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o))return null;const h=a/r,l=n/r,c=e=>Math.abs((e[0]-t[0])*l-(e[1]-t[1])*h);if(c(s)>o||c(i)>o)return null;const d=Ln(s,t,h,l),p=Ln(i,t,h,l),_=Math.max(0,Math.min(d,p)),u=Math.min(r,Math.max(d,p));return u-_>o?{lo:_,hi:u}:null},En=(t,e,s)=>Math.min(t.hi,e.hi)-Math.max(t.lo,e.lo)>s,Bn=t=>{const e=new Set(["id","a","b","cm"]);return Object.keys(t||{}).every(t=>e.has(t))},Wn=(t,e,s,i)=>{const o=t.b[0]-t.a[0],a=t.b[1]-t.a[1],n=Math.hypot(o,a);if(!(n>i))return!0;const r=o/n,h=a/n,l=j(t.cm,s.cellCm,s.gridPitch)/2;return e.some(e=>{const o=e?.center;if(!An(o))return!0;const a=Ln(o,t.a,r,h),c=Math.abs((o[0]-t.a[0])*h-(o[1]-t.a[1])*r),d=j(Number(e.cm),s.cellCm,s.gridPitch)*("square"===e.shape?Math.SQRT1_2:.5);return!(Number.isFinite(d)&&d>0)||a>=-d-i&&a<=n+d+i&&c<=l+d+i})},Gn=(t,e,s)=>{if(t.host){const i=z(t,e,s.coordScale,s.cellCm,s.gridPitch).resolved;return i?{center:i.center,angle:i.angle,length:i.length}:null}const i=Number(t.x)*s.coordScale,o=Number(t.y)*s.coordScale,a=Number(t.angle),n=Number(t.length)*s.coordScale;return[i,o,a,n].every(Number.isFinite)&&n>0?{center:[i,o],angle:a,length:n}:null},jn=(t,e,s)=>{const i=t.angle*Math.PI/180,o=Math.cos(i),a=Math.sin(i),n=e.angle*Math.PI/180,r=Math.cos(n),h=Math.sin(n);if(Math.abs(o*h-a*r)>1e-6)return!1;const l=e.center[0]-t.center[0],c=e.center[1]-t.center[1];if(Math.abs(l*a-c*o)>s)return!1;const d=l*o+c*a;return Math.max(-t.length/2,d-e.length/2)({...t,a:[t.a[0]*e,t.a[1]*e],b:[t.b[0]*e,t.b[1]*e]}),Un=(t,e,s,i)=>{const o=`~r-${(t=>{let e=2166136261;for(let s=0;s>>0;return e.toString(36)})(`${t}|${e[0].toFixed(9)},${e[1].toFixed(9)}|${s[0].toFixed(9)},${s[1].toFixed(9)}`)}`,a=`${t.slice(0,Math.max(1,64-o.length))}${o}`;let n=a;for(let t=2;i.has(n);t++){const e=`-${t}`;n=`${a.slice(0,64-e.length)}${e}`}return i.add(n),n},Kn=(t,e)=>{const s=[];for(const i of t){const t=s[s.length-1];!(t&&Math.abs(t.hi-i.lo)<=e&&t.safe===i.safe)||i.safe&&t.signature!==i.signature?s.push({lo:i.lo,hi:i.hi,a:i.a,b:i.b,safe:i.safe,signature:i.signature,roomIds:i.roomIds,finalCm:i.finalCm}):(t.hi=i.hi,t.b=i.b)}return s};function Vn(t,e,s,i,o){const a=s||[],n=Array.isArray(t?.partitions)?t.partitions:[],r=Array.isArray(t?.openings)?t.openings:[],h=Array.isArray(t?.room_drafts)?t.room_drafts:[],l={walls:a,partitions:n,openings:r,roomDrafts:h,partitionsReconciled:0,openingsRehosted:0,removedDrafts:0};if(!n.length&&!h.length)return l;const c=Math.max(2e-4*o.gridPitch,1e-9);let d=a,p=n,_=r;const u=A(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),m=((t,e,s,i)=>{const o=new Map(t.map(t=>[t.id,t])),a=new Set;for(const t of e){if(!o.get(t.id)||!Array.isArray(t.points)||t.points.length<2||t.segments.length!==t.points.length-1)continue;let e=!0;for(let o=0;o+10)){e=!1;break}const h=Math.hypot(n[0]-a[0],n[1]-a[1]);if(!(h>i)){e=!1;break}const l=s.flatMap(t=>{const e=t.cm>0?t.cm:N;if(t.open||"outer"!==t.kind&&"shared"!==t.kind||e+it.lo-e.lo||t.hi-e.hi);let c=0;for(const t of l){if(t.lo>c+i)break;if(c=Math.max(c,t.hi),c>=h-i)break}c!m.has(t.id));let f=0,b=0;const v=new Map(e.partitions.map(t=>[t.id,t]));for(const t of[...n].sort((t,e)=>t.id.localeCompare(e.id))){if(!p.some(e=>e.id===t.id))continue;if(!Bn(t))continue;const s=v.get(t.id);if(!(s&&An(s.a)&&An(s.b)&&Number.isFinite(s.cm)&&s.cm>0))continue;const[a,n]=Nn(s.a,s.b)<=0?[[s.a[0],s.a[1]],[s.b[0],s.b[1]]]:[[s.b[0],s.b[1]],[s.a[0],s.a[1]]],r=n[0]-a[0],h=n[1]-a[1],l=Math.hypot(r,h);if(!(l>c))continue;const u=r/l,g=h/l,y=A(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),$=y.flatMap(t=>{if(t.open||"outer"!==t.kind&&"shared"!==t.kind)return[];const e=On(a,n,t.a,t.b,c);return e?[{interval:t,range:e}]:[]});if(!$.length)continue;const w=_.filter(t=>"partition"===t.host?.kind&&t.host.id===s.id).map(t=>({opening:t,resolved:z(t,e.partitions,o.coordScale,o.cellCm,o.gridPitch).resolved}));if(w.some(t=>!t.resolved))continue;const k=[0,l];for(const{range:t}of $)k.push(t.lo,t.hi);const x=k.slice(),S=new Map;for(const{opening:t,resolved:e}of w){const s=Ln(e.center,a,u,g),i={lo:s-e.length/2,hi:s+e.length/2};S.set(t.id,i),x.push(Math.max(0,i.lo),Math.min(l,i.hi))}const M=[...new Set(x.filter(t=>Number.isFinite(t)&&t>=-c&&t<=l+c).map(t=>Math.max(0,Math.min(l,t)).toFixed(9)))].map(Number).sort((t,e)=>t-e),D=p.map(t=>qn(t,o.coordScale)).filter(t=>t.id!==s.id),C=e.room_drafts.filter(t=>!m.has(t.id)),P=[];for(let t=0;t+1c))continue;const r=Hn(a,u,g,i),h=Hn(a,u,g,n),l=$.filter(({range:t})=>t.lo<=i+c&&t.hi>=n-c).map(({interval:t})=>t),d=new Map;for(const t of l)d.set(t.roomId,t);const p=[...d.values()],_=new Set(p.map(t=>t.kind)),m=new Set(p.map(t=>t.cm>0?t.cm:N)),f=p[0]?.kind,b=[...d.keys()].sort(),v=1===_.size&&1===m.size&&("outer"===f&&1===b.length||"shared"===f&&2===b.length),y={id:s.id,a:r,b:h,cm:s.cm},w=D.some(t=>!!On(r,h,t.a,t.b,c)),k=C.some(t=>t.points.some((e,s)=>s+10?p[0].cm:N:0,I=v?Math.max(S,s.cm):s.cm;P.push({lo:i,hi:n,a:r,b:h,safe:x,signature:x?`${f}|${b.join(",")}|${I}`:"",roomIds:b,roomCm:S,finalCm:I})}if(!P.some(t=>t.safe))continue;const I=new Map;for(const{opening:t,resolved:e}of w){const i=S.get(t.id),a=k.some(t=>t>i.lo+c&&tEn(t,i,c)),r=new Set(n.filter(t=>t.safe).map(t=>t.signature));if(!a&&n.length>0&&n.every(t=>t.safe)&&1===r.size&&H(e,y,c)){I.set(t.id,{resolved:e,signature:[...r][0]});continue}const h=L(s,o.cellCm,o.gridPitch),l={lo:i.lo-h,hi:i.hi+h};for(const t of P)En(t,l,c)&&(t.safe=!1,t.signature="")}let T=!0;for(;T;){T=!1;for(const[t,e]of[...I.entries()]){const i=S.get(t),a=P.filter(t=>En(t,i,c));if(a.length>0&&a.every(t=>t.safe)&&a.every(t=>t.signature===e.signature))continue;I.delete(t);const n=L(s,o.cellCm,o.gridPitch),r={lo:i.lo-n,hi:i.hi+n};for(const t of P)En(t,r,c)&&(t.safe=!1,t.signature="");T=!0}}const R=Kn(P,c),j=R.filter(t=>t.safe),q=R.filter(t=>!t.safe);if(!j.length)continue;if(p.length-1+q.length>2e3)continue;let U=d;for(const t of j){const[e,i]=Nn(s.a,s.b)<=0?[t.a,t.b]:[t.b,t.a];U=O(U,e,i,t.finalCm,o.pitch,o.coordScale)}if(U=E(e.rooms,U,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),U.length>500)continue;const K=new Set(p.map(t=>t.id));K.delete(s.id);const V=q.map((t,e)=>{const i=[t.a[0]/o.coordScale,t.a[1]/o.coordScale],a=[t.b[0]/o.coordScale,t.b[1]/o.coordScale],n=0===e?s.id:Un(s.id,i,a,K);return K.add(n),{id:n,a:i,b:a,cm:s.cm}}),J=p.findIndex(t=>t.id===s.id),Y=p.slice();Y.splice(J,1,...V);const X=Y.map(t=>qn(t,o.coordScale)),Z=new Map;let Q=!0;for(const{opening:t,resolved:e}of w){const s=I.get(t.id);if(s){const e=F(t,s.resolved,o.coordScale),{host:i,...a}=e;Z.set(t.id,a);continue}const i=S.get(t.id),a=V[q.findIndex(t=>t.lo<=i.lo+c&&t.hi>=i.hi-c)],n=a&&X.find(t=>t.id===a.id);if(!a||!n){Q=!1;break}const r=n.b[0]-n.a[0],h=n.b[1]-n.a[1],l=Math.hypot(r,h),d=Ln(e.center,n.a,r/l,h/l),p={...F(t,e,o.coordScale),host:{kind:"partition",id:a.id,t:d/l}};if(!B(p,X,o.coordScale,o.cellCm,o.gridPitch).resolved){Q=!1;break}Z.set(t.id,p)}if(!Q)continue;const tt=_.map(t=>Z.get(t.id)||t),et=[...I.keys()].map(t=>Z.get(t)),st=et.map(t=>Gn(t,X,o));if(st.some(t=>!t))continue;let it=!1;for(let t=0;tt.id));for(const t of tt){if(ot.has(t.id))continue;const e=Gn(t,X,o);e&&st.some(t=>jn(t,e,c))&&(it=!0)}if(it)continue;const at=W(e.rooms,U,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale);[...I.entries()].every(([t,e])=>{const s=Z.get(t),i=G(at,{x:s.x*o.coordScale,y:s.y*o.coordScale,angle:s.angle,length:s.length*o.coordScale},!0),a=[i.negative,i.positive].filter(t=>!!t?.full),n=e.signature.split("|")[1].split(","),r=new Set(a.map(t=>t.roomId));return a.length===n.length&&r.size===n.length&&n.every(t=>r.has(t))})&&(d=U,p=Y,_=tt,f+=j.length,b+=I.size)}return{walls:d,partitions:p,openings:_,roomDrafts:g,partitionsReconciled:f,openingsRehosted:b,removedDrafts:m.size}}const Jn=t=>JSON.parse(JSON.stringify(t)),Yn=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Xn=(t,e)=>e.length>0&&e.length<=35&&("space"===t?/^[a-z0-9_-]+$/.test(e):/^[A-Za-z0-9_-]+$/.test(e));function Zn(t,e){let s=String(e??"");const i=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),o=new RegExp(`^${i}_(.+)_([0-9a-f]{8})$`);let a=0;for(;a<16;a++){const t=o.exec(s);if(!t)return{root:s,layers:a,bounded:!1};s=t[1]}return{root:s,layers:a,bounded:o.test(s)}}const Qn=(t,e,s)=>{const i=t.get(e)||[];i.push(s),t.set(e,i)};const tr=1e-9;class er extends Error{constructor(t,e=""){super(e?`${t}: ${e}`:t),this.reason=t,this.name="WallSegmentModelError"}}const sr=t=>JSON.parse(JSON.stringify(t)),ir=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1])),or=t=>[Number(t[0]),Number(t[1])],ar=t=>`${Number(t[0]).toFixed(12)},${Number(t[1]).toFixed(12)}`,nr=(t,e)=>{const s=ar(t),i=ar(e);return sar(t)<=ar(e)?[or(t),or(e)]:[or(e),or(t)],hr=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),lr=t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],s=[];for(let i=1;i{const s=new Set,i=t=>{const e=Number(t);if(!Number.isFinite(e))return;const i=e/I;Math.abs(i-Math.round(i))>=X&&s.add(e.toFixed(12))};for(const e of Array.isArray(t?.rooms)?t.rooms:[])for(const t of R(e)||[])i(t[0]),i(t[1]);for(const e of[t?.wall_segments,t?.walls])for(const t of Array.isArray(e)?e:[])i(t?.a?.[0]),i(t?.a?.[1]),i(t?.b?.[0]),i(t?.b?.[1]);for(const t of e)i(t?.[0]),i(t?.[1]);return s.size},dr=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;return a>1e-18?((t[0]-e[0])*i+(t[1]-e[1])*o)/a:0},pr=(t,e,s)=>{const i=Math.max(0,Math.min(1,dr(t,e,s)));return Math.hypot(t[0]-(e[0]+(s[0]-e[0])*i),t[1]-(e[1]+(s[1]-e[1])*i))},_r=(t,e,s,i,o=tr)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(r<=o)return 0;const h=e=>Math.abs((e[0]-t[0])*n-(e[1]-t[1])*a)/r;if(h(s)>o||h(i)>o)return 0;const l=dr(s,t,e),c=dr(i,t,e);return Math.max(0,Math.min(1,Math.max(l,c))-Math.max(0,Math.min(l,c)))*r},ur=t=>{const e=(new TextEncoder).encode(t),s=8*e.length,i=64*Math.ceil((e.length+9)/64),o=new Uint8Array(i);o.set(e),o[e.length]=128;const a=new DataView(o.buffer);a.setUint32(i-8,Math.floor(s/4294967296),!1),a.setUint32(i-4,s>>>0,!1);const n=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),r=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),h=new Uint32Array(64),l=(t,e)=>t>>>e|t<<32-e;for(let t=0;t>>3,o=l(s,17)^l(s,19)^s>>>10;h[t]=h[t-16]+i+h[t-7]+o>>>0}let[e,s,i,o,c,d,p,_]=r;for(let t=0;t<64;t++){const a=_+(l(c,6)^l(c,11)^l(c,25))+(c&d^~c&p)+n[t]+h[t]>>>0,r=e&s^e&i^s&i;_=p,p=d,d=c,c=o+a>>>0,o=i,i=s,s=e,e=a+((l(e,2)^l(e,13)^l(e,22))+r>>>0)>>>0}r[0]=r[0]+e>>>0,r[1]=r[1]+s>>>0,r[2]=r[2]+i>>>0,r[3]=r[3]+o>>>0,r[4]=r[4]+c>>>0,r[5]=r[5]+d>>>0,r[6]=r[6]+p>>>0,r[7]=r[7]+_>>>0}const c=new Uint8Array(32),d=new DataView(c.buffer);for(let t=0;t{const e="abcdefghijklmnopqrstuvwxyz234567";let s=0,i=0,o="";for(const a of t)for(i=i<<8|a,s+=8;s>=5;)o+=e[i>>>s-5&31],s-=5;return s&&(o+=e[i<<5-s&31]),o},gr=(t,e,s,i,o="")=>{const[a,n]=rr(e,s),r=`${t}|${ar(a)}|${ar(n)}|${[...i].sort().join(",")}${o}`;return`wall-${mr(ur(r)).slice(0,20)}`};let fr=0;const br=t=>{for(let e=0;e<1e3;e++){const e=globalThis.crypto?.randomUUID?.(),s=`wall-${e||mr(ur(`${Date.now()}|${fr++}|${Math.random()}`)).slice(0,26)}`;if(!t.has(s))return s}throw new er("duplicate-id","id factory exhausted")},vr=t=>{const e=new Map;for(const s of Array.isArray(t?.wall_segments)?t.wall_segments:[])if(s&&"string"==typeof s.id&&ir(s.a)&&ir(s.b)){if(e.has(s.id))throw new er("duplicate-id",s.id);e.set(s.id,s)}return e},yr=(t,e,s)=>{const i=(s,i)=>{const o=t[0]-s[0],a=t[1]-s[1];return Math.abs(e[0]-i[0]-o)<=tr&&Math.abs(e[1]-i[1]-a)<=tr?[o,a]:null};return i(s.a,s.b)||i(s.b,s.a)},$r=(t,e)=>{const s=Array.isArray(t.rooms)?t.rooms:[],i=V(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]),o=i.length?i:J(s,null,1,.04*I,!0),a=[];for(const t of s){const s=R(t),i=Array.isArray(t?.wall_ids)?t.wall_ids:[];if(s&&i.length===s.length)for(let t=0;t"partition"!==t.host?.kind&&[Number(t.x),Number(t.y)].every(Number.isFinite)&&Number.isFinite(Number(t.angle))&&Number(t.length)>0&&o.some(e=>pr([Number(t.x),Number(t.y)],[e[0],e[1]],[e[2],e[3]])<=.04*I)):[],h=r.map(t=>{const e=Number(t.angle)*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Number(t.length)/2,o=[Number(t.x),Number(t.y)];return{a:[o[0]-s[0]*i,o[1]-s[1]*i],b:[o[0]+s[0]*i,o[1]+s[1]*i]}}),l=(t,e)=>r.some(s=>{const i=[Number(s.x),Number(s.y)];if(!Z(t,e,Number(s.angle)))return!1;if(pr(i,t,e)>.02*I)return!1;const o=hr(t,e),a=dr(i,t,e)*o,n=Number(s.length)/2;return a+n>=-tr&&a-n<=o+tr}),c=new Map,d=[];for(const i of s){const r=String(i?.id||""),p=R(i);if(!r||!p||p.length<3)throw new er("invalid-room",r);const _=Y(s,r,n,I,1,[...t.walls||[],...h]);if(!_||_.poly.length<3)throw new er("invalid-room",r);const u=Array.isArray(i.wall_ids)?i.wall_ids:[],m=u.length===p.length;let g=null,f=m;if(f)for(let t=0;ttr||Math.abs(i[1]-g[1])>tr)){f=!1;break}g=i}const b=[];for(let t=0;t<_.poly.length;t++){const s=_.poly[t],i=_.poly[(t+1)%_.poly.length];if(hr(s,i)<=tr)throw new er("zero-length",r);const n=nr(s,i);let h=c.get(n);if(!h){const[t,e]=rr(s,i);h={key:n,a:t,b:e,owners:new Set,preferredIds:new Set,positionalIds:new Set,preferredCarriers:new Map,parentKeys:new Set,zeroWall:!1},c.set(n,h)}if(h.owners.add(r),h.owners.size>2)throw new er("third-owner",n);const d=m?u[_.parent[t]]:void 0;if("string"==typeof d&&d){const o=e.get(d);f||!o||_r(s,i,o.a,o.b)>tr?h.preferredIds.add(d):h.positionalIds.add(d),h.preferredCarriers.set(d,{a:or(p[_.parent[t]]),b:or(p[(_.parent[t]+1)%p.length])})}const g=_.parent[t];h.parentKeys.add(K(p[g],p[(g+1)%p.length],I));const v=[(s[0]+i[0])/2,(s[1]+i[1])/2],y=t=>t.some(t=>pr(v,[t[0],t[1]],[t[2],t[3]])<=.04*I);h.zeroWall||=y(a)||y(o)&&!l(s,i),b.push(n)}const v={...i};delete v.wall_ids,d.push({...v,poly:_.poly.map(t=>[t[0],t[1]]),wall_ids:b})}return{atoms:[...c.values()].sort((t,e)=>t.key.localeCompare(e.key)),rooms:d}},wr=(t,e,s,i,o)=>{const a=new Map([...s.values()].map(t=>[nr(t.a,t.b),t])),n=(t=>{const e=new Map;for(const s of t)"wall"===s.host?.kind&&e.set(s.host.id,(e.get(s.host.id)||0)+1);return e})(t.openings||[]),r=new Map;for(const t of e){const e=o?.get(t.key);if(e){const i=s.get(e);if(!i)throw new er("duplicate-id",e);r.set(t,i);continue}if(t.preferredIds.size>1)throw new er("duplicate-id",[...t.preferredIds].sort().join(","));const i=[...t.preferredIds][0],h=i?s.get(i):void 0;if(h){r.set(t,h);continue}const l=i?t.preferredCarriers.get(i):void 0;if(i&&l){r.set(t,{id:i,a:l.a,b:l.b,cm:0});continue}const c=a.get(t.key);if(c){r.set(t,c);continue}const d=[...s.values()].filter(e=>_r(t.a,t.b,e.a,e.b)>tr),p=[...new Map(d.map(t=>[t.id,t])).values()];if(p.sort((t,e)=>(n.get(e.id)||0)-(n.get(t.id)||0)||hr(e.a,e.b)-hr(t.a,t.b)||t.id.localeCompare(e.id)),p[0])r.set(t,p[0]);else if(1===t.positionalIds.size){const e=s.get([...t.positionalIds][0]);e&&r.set(t,e)}}const h=new Map;for(const[t,e]of r){const s=h.get(e.id)||[];s.push(t),h.set(e.id,s)}for(const[t,e]of h){const i=s.get(t)||r.get(e[0]);if(!i)throw new er("duplicate-id",t);const o=[(i.a[0]+i.b[0])/2,(i.a[1]+i.b[1])/2];e.sort((t,e)=>{const s=pr(o,t.a,t.b)<=tr?0:1,a=pr(o,e.a,e.b)<=tr?0:1;if(s!==a)return s-a;return(pr(i.a,t.a,t.b)<=tr?0:1)-(pr(i.a,e.a,e.b)<=tr?0:1)||t.key.localeCompare(e.key)}),e[0].id=t}const l=(t=>{const e=new Set;for(const s of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns])for(const t of Array.isArray(s)?s:[])"string"==typeof t?.id&&t.id&&e.add(t.id);for(const s of Array.isArray(t.room_drafts)?t.room_drafts:[])for(const t of Array.isArray(s?.segments)?s.segments:[])"string"==typeof t?.id&&t.id&&e.add(t.id);return e})(t);for(const t of e)if(t.id){if(l.has(t.id))throw new er("duplicate-id",t.id);l.add(t.id)}const c=e.filter(t=>!t.id);if(i){const e=c.map(e=>{const[s,i]=rr(e.a,e.b),o=`${String(t.id||"")}|${ar(s)}|${ar(i)}|${[...e.owners].sort().join(",")}`,a=mr(ur(o));return{atom:e,seed:o,digest:a,base:`wall-${a.slice(0,20)}`}}).sort((t,e)=>t.digest.localeCompare(e.digest)||t.atom.key.localeCompare(e.atom.key)),s=new Map;for(const t of e){const e=s.get(t.digest);if(e&&e!==t.seed)throw new er("duplicate-id",t.digest);s.set(t.digest,t.seed);let i=1,o=t.base;for(;l.has(o);)o=`${t.base}-${++i}`;t.atom.id=o,l.add(o)}}else for(const t of c)t.id=br(l),l.add(t.id)},kr=(t,e)=>{if("partition"===t.host?.kind)return null;const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=e=>{if(!(Number(e.cm)>0))return!1;const i=dr(s,e.a,e.b),o=hr(e.a,e.b),a=Number(t.length)/2;return i>=-tr&&i<=1+tr&&pr(s,e.a,e.b)<=.02*I&&Z(e.a,e.b,Number(t.angle))&&Number.isFinite(a)&&a>=0&&i*o-a>=-tr&&i*o+a<=o+tr},o="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null,a=o&&i(o)?[o]:e.filter(i);if(1!==a.length)return null;const n=a[0];return{kind:"wall",id:n.id,t:Math.max(0,Math.min(1,dr(s,n.a,n.b)))}},xr=(t,e)=>{const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=Number(t.length)/2,o=e.filter(e=>{if(!(Number(e.cm)>0))return!1;const o=dr(s,e.a,e.b),a=hr(e.a,e.b);return o>=-tr&&o<=1+tr&&pr(s,e.a,e.b)<=.02*I&&Z(e.a,e.b,Number(t.angle))&&Number.isFinite(i)&&i>=0&&o*a-i>=-tr&&o*a+i<=a+tr}),a=(e=>{if(!e.length)return null;const i="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null;return i||[...e].sort((t,e)=>pr(s,t.a,t.b)-pr(s,e.a,e.b)||Number(e.cm)-Number(t.cm)||(t.ide.id?1:0))[0]})(o);return a?{kind:"wall",id:a.id,t:Math.max(0,Math.min(1,dr(s,a.a,a.b)))}:null},Sr=(t,e,s)=>{const i=vr(t),{atoms:o,rooms:a}=$r(t,i);wr(t,o,i,e,s);const n=o.map(e=>{const s=e.id?i.get(e.id):void 0,o=((t,e,s)=>{if(e.zeroWall)return 0;const i=Array.isArray(t.walls)?t.walls:[],o=K(e.a,e.b,I),a=i.filter(t=>Number(t.cm)>0&&(!(t.key!==o&&!e.parentKeys.has(t.key))||!(!ir(t.a)||!ir(t.b))&&_r(e.a,e.b,t.a,t.b)>=hr(e.a,e.b)-tr)).map(t=>Math.max(1,Math.min(100,Number(t.cm))));if(new Set(a.map(t=>t.toFixed(9))).size>1)throw new er("thickness-conflict",e.key);if(a.length)return a[0];const n=Q(i,e.a,e.b,I,1);return n>0?n:Number(s?.cm)>0?Number(s.cm):0})(t,e,s);return{...s||{},id:e.id,a:[...e.a],b:[...e.b],cm:o}}),r=new Map(o.map(t=>[t.key,t.id]));for(const t of a)t.wall_ids=t.wall_ids.map(t=>r.get(t));t.rooms=a,t.wall_segments=n,t.walls=n.filter(t=>t.cm>0).map(t=>({key:K(t.a,t.b,I),cm:t.cm,a:[...t.a],b:[...t.b]})),t.walls.length||delete t.walls,delete t.open_spans;for(const e of t.rooms)delete e.open_to;return((t,e)=>{const s=new Set;for(const e of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns,t.wall_segments])for(const t of Array.isArray(e)?e:[])"string"==typeof t?.id&&t.id&&s.add(t.id);for(const i of Array.isArray(t.room_drafts)?t.room_drafts:[]){const o=Array.isArray(i.segments)?i.segments:[];for(let a=0;a{const i=Array.isArray(t.openings)?t.openings:[];for(const t of i){if("partition"===t.host?.kind)continue;if(!t.host&&!s){const s=kr(t,e);s&&(t.host=s);continue}const i=kr(t,e)??(s?xr(t,e):null);if(i)t.host=i;else{if(!s)throw new er("opening-host",t.id);delete t.host}}})(t,n,e),n.reduce((t,e)=>t+(i.has(e.id)?0:1),0)};function Mr(t,e={}){const s=JSON.stringify(t),i=sr(t);U(i);let o=0;const a=Number(i?.model_version||0)<9;for(const t of Array.isArray(i?.spaces)?i.spaces:[]){const s=e.lineageSpaceId===String(t?.id||"")?e.lineageHints:void 0;o+=Sr(t,a,s)}return i.model_version=9,U(i),{config:i,changed:s!==JSON.stringify(i),migratedSegments:o}}const Dr=t=>!!t&&"object"==typeof t&&!Array.isArray(t);function Cr(t,e){const s=(t,e)=>{if(Array.isArray(t)&&Array.isArray(e)){const i=new Map;for(const e of t)Dr(e)&&"string"==typeof e.id&&e.id&&i.set(e.id,e);const o=e.map((e,o)=>{if(Dr(e)&&"string"==typeof e.id&&e.id){const t=i.get(e.id);return t?s(t,e):sr(e)}return o[e,e in t?s(t[e],i):sr(i)]);for(const e of Object.keys(t))delete t[e];for(const[e,s]of i)t[e]=s;return t}return sr(e)};return s(t,e)}const Pr=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Ir=(t,e,s)=>Math.min(s,Math.max(e,t)),Tr=t=>ut({spaces:[t]})[0],Rr=(t,e,s)=>{const i=s>0?s:1,o=t=>`${(t[0]/i).toFixed(12)},${(t[1]/i).toFixed(12)}`,a=o(t),n=o(e);return a0?n:1,h=Math.max(i*r*.02,1e-9),l=[];for(const e of t||[])for(const t of R(e)||[])l.push([t[0],t[1]]);const c=[];for(const t of s||[])Array.isArray(t)&&t.length>=4&&t.slice(0,4).every(Number.isFinite)&&c.push([t[0],t[1]],[t[2],t[3]]);const d=(t,e)=>e.some(e=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*h),p=new Map;for(const n of t||[]){if(!n?.id)continue;const _=mt(t,n.id,e,s,i,o,a,r);if(_)for(let t=0;t<_.orig.length;t++){const e=[];for(let s=0;s<_.parent.length;s++)_.parent[s]===t&&e.push(s);for(let t=1;t+10&&u>0&&n===m&&u!==n))continue;const g=_.poly[i],f=_.poly[(i+1)%_.poly.length],b=Math.hypot(f[0]-g[0],f[1]-g[1]);if(!(b>h&&b<.5*a-1e-9*a))continue;if(d(g,c)||d(f,c))continue;if(d(g,l)&&d(f,l))continue;const v=Rr(g,f,r),y=p.get(v);y?y.targets.add(n):p.set(v,{a:[g[0],g[1]],b:[f[0],f[1]],targets:new Set([n])})}}}let _=e.slice();const u=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*h,m=(t,e)=>{if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return!1;const s=[Number(t.a[0])*r,Number(t.a[1])*r],i=[Number(t.b[0])*r,Number(t.b[1])*r];return!![...s,...i].every(Number.isFinite)&&(u(s,e.a)&&u(i,e.b)||u(s,e.b)&&u(i,e.a))};for(const t of[...p.values()].filter(t=>1===t.targets.size).sort((t,e)=>Rr(t.a,t.b,r).localeCompare(Rr(e.a,e.b,r)))){const e=[...t.targets][0],s=_.filter(e=>m(e,t));if(new Set(s.map(t=>t.cm)).size>1)continue;let o=!1;_=_.map(s=>m(s,t)?(o=!0,s.cm===e?s:{...s,cm:e}):s),o||(_=O(_,t.a,t.b,e,i,r))}return _}const Fr=t=>{const e=[];for(const s of t||[]){const t=R(s);if(t?.length)for(let s=0;s"string"==typeof t?.id?t.id:"").filter(Boolean),c=new Set(l),d=new Map;for(const t of l){const e=Zn("space",t);e.layers>0&&Xn("space",e.root)&&Qn(d,e.root,t)}const p=new Map,_=new Map,u=new Map,m=new Map;for(const t of r){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s=new Map;for(const i of Array.isArray(t.rooms)?t.rooms:[]){const t="string"==typeof i?.id?i.id:"";if(!t)continue;const o=Zn("room",t);o.layers>0&&Xn("room",o.root)&&Qn(s,o.root,t),_.has(t)||_.set(t,e),u.has(t)||u.set(t,String(i.name||""));const a="string"==typeof i.area?i.area:"";if(a){const s=m.get(a)||[];s.push({spaceId:e,roomId:t}),m.set(a,s)}}p.set(e,s)}const g=new Set(_.keys()),f=new Map,b=new Map;for(const t of h)"string"==typeof t?.id&&t.id&&(!0===t.removed?b.set(t.id,t):f.set(t.id,t));const v={spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,positionsUnresolved:0,nestedRefsUnresolved:0,deadSpaceIds:[],orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0,removedPositions:[],liveMissingPositions:[],unverifiedPositions:[]},y=new Set,$=t=>c.has(t)?null:(()=>{const e=Zn("space",t).root;if(!Xn("space",e))return null;const s=d.get(e)||[];return 1===s.length?s[0]:null})(),w=(t,e)=>{const s=Zn("room",t).root;if(!Xn("room",s))return null;const i=p.get(e)?.get(s)||[];return 1===i.length?i[0]:null},k=t=>{const e=s.effectiveAreaByMarker?.[t]||"",i=e&&m.get(e)||[];return 1===i.length?i[0]:null},x=(t,e,s)=>{if("string"==typeof t.room_id&&t.room_id&&!g.has(t.room_id)){const i=e?w(t.room_id,e):null;i?(t.room_id=i,v.roomRefsRemapped++):s?(t.room_id=s.roomId,v.roomRefsRemapped++):!0!==t.removed&&delete t.room_id}const i=t.vacuum?.segment_map;if(i&&"object"==typeof i&&!Array.isArray(i))for(const t of Object.keys(i)){const s=i[t];if("string"!=typeof s||!s||g.has(s))continue;const o=e?w(s,e):null;o?(i[t]=o,v.roomRefsRemapped++):v.nestedRefsUnresolved++}};for(const t of h){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s="string"==typeof t.space?t.space:"",i=s&&!c.has(s),a=o[e],n="string"==typeof a?.s?a.s:"",r=n&&!c.has(n),h=!0===t.removed;let l=s&&c.has(s)?s:null,d=null,p=!1;if(i){const i=$(s);i?(t.space=i,l=i,p=!0,v.spaceRefsRemapped++):h||"virtual"===t.binding?h||(delete t.space,l=null,v.markersDetached++):(d=k(e),d?(t.space=d.spaceId,l=d.spaceId,v.spaceRefsRemapped++):(delete t.space,l=null,v.markersDetached++))}h&&!p||x(t,l,d),r&&p&&n===s&&l&&(o[e]={...a,s:l},v.positionsRemapped++,y.add(e))}for(const t of Object.keys(o)){if(y.has(t))continue;const e=o[t],s="string"==typeof e?.s?e.s:"";if(!s||c.has(s))continue;const i=$(s);if(i){if(t.startsWith("rl_")){const s=t.slice(3),a=w(s,i);if(a){const s=`rl_${a}`;Yn(o,s)||(o[s]={...e,s:i}),delete o[t],v.roomRefsRemapped++,v.positionsRemapped++;continue}}o[t]={...e,s:i},v.positionsRemapped++}}const S=s.ownerRoster,M=!0===S?.authoritative,D=new Set(S?.deviceIds||[]),C=new Set(S?.entityIds||[]),P=new Set(Array.isArray(i.settings?.known_devices)?i.settings.known_devices.filter(t=>"string"==typeof t):[]),I=(t,e="")=>{const s=String(S?.names?.[t]||e||"").trim();return s&&s!==t?s:""},T=(t,e,s,i="",o)=>({id:t,spaceId:e,kind:s,name:I(t,i),...o?{reason:o}:{}}),R=t=>{"room_label"===t?v.orphanRoomLabelsRemoved++:"group"===t?v.orphanGroupPositionsRemoved++:v.orphanDevicePositionsRemoved++},z=new Set;for(const[t,e]of Object.entries(o)){const i="string"==typeof e?.s?e.s:"";if(!i||c.has(i))continue;let a,n;const r=f.get(t),h=b.get(t);if(r)a=T(t,i,"device",String(r.name||"")),n="live";else if(h)a=T(t,i,"device",String(h.name||"")),n="absent";else if(t.startsWith("rl_")){const e=t.slice(3);a=T(t,i,"room_label",u.get(e)||""),n=g.has(e)?"live":"absent"}else if(t.startsWith("lg_")){const e=t.slice(3);a=T(t,i,"group"),n=C.has(e)?"live":M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")}else D.has(t)?(a=T(t,i,"device"),n="live"):P.has(t)?(a=T(t,i,"device"),n=M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")):(a=T(t,i,"unknown","","unknown_owner"),n="unverified");if("absent"!==n){if("live"===n){if(v.liveMissingPositions.push(a),!0===s.removeLiveMissingPositions){delete o[t],v.liveMissingPositionsRemoved++,R(a.kind),v.removedPositions.push(a);continue}}else v.unverifiedPositions.push(a);v.positionsUnresolved++,z.add(i)}else delete o[t],R(a.kind),v.removedPositions.push(a)}return v.deadSpaceIds=[...z].sort((t,e)=>t.localeCompare(e)),{config:i,layout:o,report:v,changed:JSON.stringify(i)!==a||JSON.stringify(o)!==n}}(t,e,s),n=a.config,r=tt(n,a.layout);U(n),et(a.layout);const h=JSON.stringify(t||{}),l=JSON.stringify(e||{}),c=Number.isInteger(Number(n.model_version))?Number(n.model_version):0,d=(t=>{let e=0,s=0,i=0;for(const s of t.markers||[]){if("ripple"===s.display&&(s.display="icon_ripple",e++),Array.isArray(s.controls)){const t="string"==typeof s.binding&&s.binding.startsWith("entity:")?s.binding.slice(7):"",i=t?s.controls.filter(e=>e!==t):s.controls;JSON.stringify(i)!==JSON.stringify(s.controls)&&(s.controls=i.length?i:null,e++)}const t=s.vacuum;t&&Pr(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const o of t.spaces||[]){const t=o.settings;"glow"===t?.fill_mode&&("boolean"!=typeof t.glow_enabled&&(t.glow_enabled=!0),t.fill_mode="none",e++,s++);for(const t of o.rooms||[]){const s=t.settings;"glow"===s?.fill_mode&&("boolean"!=typeof s.glow&&(s.glow=!0),delete s.fill_mode,e++,i++)}if(Pr(o,"segments")&&(delete o.segments,e++),Pr(o,"plan_scale")){const t=Number(o.plan_scale);Number.isFinite(t)&&t>=ht&&t<=lt&&(Pr(o,"plan_scale_x")||(o.plan_scale_x=t),Pr(o,"plan_scale_y")||(o.plan_scale_y=t),delete o.plan_scale,e++)}const a=Number(o.cell_cm);Pr(o,"cell_cm")&&(!Number.isFinite(a)||a<.1||a>1e3)&&(o.cell_cm=Number.isFinite(a)&&a>0?Ir(a,.1,1e3):5,e++);const n=Number(o.cell_cm),r=Number.isFinite(n)&&n>0?n:5;for(const t of o.wall_columns||[])if("circle"===t.shape)Pr(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&Pr(t,"angle")){const s=((Number(t.angle)||0)%90+90)%90;t.angle!==s&&(t.angle=s,e++)}for(const t of o.decor||[]){if(Pr(t,"width")){const s=Number(t.width);(Pr(t,"width_cm")||Number.isFinite(s))&&(Pr(t,"width_cm")||(t.width_cm=Number(Ir(s/nt*r,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(Pr(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),Pr(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number(Ir(ct*dt(t)/nt*r,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(Pr(t,"scale")||Pr(t,"size"))&&(delete t.scale,delete t.size,e++);let s=String(t.text??"");const i=[...s.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!pt(t[1]));if(!(Pr(t,"entity")||Pr(t,"attr")||Pr(t,"unit")))continue;const o=String(t.unit??"").trim(),a="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||o?"":_t(t.entity,a);if(i||n){if(n){const e=s.indexOf("{}");s=e>=0?s.slice(0,e)+n+s.slice(e+2):`${s}${s?" ":""}${n}`,t.text=s}delete t.entity,delete t.attr,delete t.unit,e++}}}return{total:e,glowSpaces:s,glowRooms:i}})(n);let p=d.total;const _=(u=n.spaces||[],JSON.parse(JSON.stringify(u)));var u;const m=Sn(n.spaces||[],a.layout);let g=0,f=0,b=0,v="";const y=m.spaces.map(t=>{const e=st(t);g+=e.report.wallsStraightened,f+=e.report.wallsStraightenSkipped;const s=Number(t?.cell_cm)>0?Number(t.cell_cm):5,i=e.report.maxStraightenShift/I*s;return i>b&&(b=i,v=String(t?.id||"")),e.space}),$=g?Sn(y,m.layout):{...m,spaces:y};n.spaces=$.spaces;const w=g?{...m.report,moved:m.report.moved+$.report.moved,coordsCanonicalized:m.report.coordsCanonicalized+$.report.coordsCanonicalized,maxShift:Math.max(m.report.maxShift,$.report.maxShift),maxShiftCm:Math.max(m.report.maxShiftCm,$.report.maxShiftCm),maxSpace:$.report.maxShiftCm>m.report.maxShiftCm?$.report.maxSpace:m.report.maxSpace,rotated:m.report.rotated+$.report.rotated,removedDrafts:m.report.removedDrafts+$.report.removedDrafts}:{...m.report};let k=0,x=0,S=0,M=0,D=0,C=0,P=0,T=0;for(let t=0;t0===Number(t.cm)),walls:e.walls||[]}),a=Tr(e),r=Tr(s);if(!a||!r)continue;const h=Fr(a.rooms),l=Fr(r.rooms),c=.02*nt,d=(e.wall_segments||[]).filter(t=>0===Number(t.cm)).map(t=>({...t,key:K(t.a,t.b,I),cm:0})),p=new Set(d.map(t=>t.key)),u=it(e,a.rooms,ot,c);x+=u.length;for(const t of u){const e=[t[0]/ot,t[1]/ot],s=[t[2]/ot,t[3]/ot],i=K(e,s,I);p.has(i)||(p.add(i),d.push({key:i,a:e,b:s,cm:0}))}const m=d.length,g=at(d,h,l,I,ot),f=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(!(a>0))return Math.hypot(t[0]-e[0],t[1]-e[1]);const n=Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))},b=g.filter(t=>{if(!Array.isArray(t.a)||!Array.isArray(t.b))return!1;const e=t.a.map(t=>t*ot),s=t.b.map(t=>t*ot);return l.some(([t,i])=>f(e,t,i)<=4*c&&f(s,t,i)<=4*c)});S+=Math.max(0,m-b.length);const v=(s.wall_segments||[]).filter(t=>Number(t.cm)>0);s.wall_segments=[...v,...b.map(({key:t,...e})=>e)];const y=b.map(t=>[t.a[0]*ot,t.a[1]*ot,t.b[0]*ot,t.b[1]*ot]),$=Array.isArray(e.walls)?e.walls.length:0;let w=at(e.walls,h,l,I,ot);w=zr(r.rooms,w,y,I,Number(s.cell_cm)>0?Number(s.cell_cm):5,nt,ot),w=E(r.rooms,w,y,I,Number(s.cell_cm)>0?Number(s.cell_cm):5,nt,ot),w=rt(w,s.rooms||[],I,1,y.map(t=>[t[0]/ot,t[1]/ot,t[2]/ot,t[3]/ot])),k+=Math.max(0,$-w.length),w.length?s.walls=w:delete s.walls;const R=Rn(s.partitions||[],{pitch:I,geometry:Fn(s)});R.merged&&(M+=R.merged,s.partitions=R.partitions,zn(s.openings,s.partitions,R.openingMoves,{coordScale:ot,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:nt}));const z=Tr(s);if(z){const t=o(s,z,s.walls||[],y,{pitch:I,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:nt,coordScale:ot});t.partitionsReconciled&&(D+=t.partitionsReconciled,C+=t.openingsRehosted,t.partitions.length?s.partitions=t.partitions:delete s.partitions,t.openings.length?s.openings=t.openings:delete s.openings,t.walls.length?s.walls=t.walls:delete s.walls),t.removedDrafts&&(P+=t.removedDrafts,t.roomDrafts.length?s.room_drafts=t.roomDrafts:delete s.room_drafts)}JSON.stringify({zero:(s.wall_segments||[]).filter(t=>0===Number(t.cm)),walls:s.walls||[]})!==i&&T++}let R=0;c<=9&&(R=function(t){const e=Mr(t);return Cr(t,e.config),{...e,config:t}}(n).migratedSegments);const z=U(n),F=et($.layout),A=JSON.stringify(z)!==h||JSON.stringify(F)!==l;c<9&&A&&(z.model_version=9);const N=JSON.stringify(z)!==h||JSON.stringify(F)!==l,H=Number.isInteger(Number(z.model_version))?Number(z.model_version):c,L=N?w:{...w,moved:0,coordsCanonicalized:0,maxShift:0,maxShiftCm:0,maxSpace:"",rotated:0,removedDrafts:0},O=N?a.report:{...a.report,spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0};return{config:z,layout:F,report:{...L,modelFrom:c,modelTo:H,migrated:N?p:0,glowSpacesMigrated:N?d.glowSpaces:0,glowRoomsMigrated:N?d.glowRooms:0,canonicalized:N?T:0,wallSegmentsMigrated:N?R:0,legacyZeroWallsMigrated:N?x:0,wallsMerged:N?k:0,spansMerged:N?S:0,partitionsMerged:N?M:0,partitionsReconciled:N?D:0,openingsRehosted:N?C:0,redundantDraftsRemoved:N?P:0,wallsStraightened:N?g:0,wallsStraightenSkipped:f,maxStraightenShiftCm:N?b:0,maxStraightenSpace:N?v:"",latticeCoordinatesCanonicalized:N?r.canonicalized:0,latticeCoordinatesFar:N?r.far:0,latticeMaxShift:N?r.maxShift:0,latticeMaxShiftCm:N?r.maxShiftCm:0,latticeSpaces:N?r.spaces:[],...O},changed:N}}const Nr=(t,e)=>t[0]*e[1]-t[1]*e[0],Hr=(t,e)=>t[0]*e[0]+t[1]*e[1],Lr=(t,e)=>[t[0]-e[0],t[1]-e[1]],Or=(t,e,s)=>[t[0]+e[0]*s,t[1]+e[1]*s];function Er(t,e,s){return Math.abs(Nr(t,e))<=s}function Br(t,e,s){return Math.abs(Nr(Lr(t,e),s))}function Wr(t,e,s,i,o){const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o&&s>0))return[];const h=[a/r,n/r];return function(t,e,s){const i=e.map(([e,s])=>[Math.max(0,Math.min(e,s)),Math.min(t,Math.max(e,s))]).filter(([t,e])=>e>t+s).sort((t,e)=>t[0]-e[0]||t[1]-e[1]),o=[];for(const t of i){const e=o[o.length-1];!e||t[0]>e[1]+s?o.push([...t]):e[1]=Math.max(e[1],t[1])}const a=[];let n=0;for(const[t,e]of o)t>n+s&&a.push([n,t]),n=Math.max(n,e);return t>n+s&&a.push([n,t]),a}(r,i.flatMap(e=>{const i=e.b[0]-e.a[0],a=e.b[1]-e.a[1],n=Math.hypot(i,a);if(!(n>o&&Er(h,[i/n,a/n],1e-6)))return[];if(Br(e.a,t,h)>1.1*Math.max(s,o)||Br(e.b,t,h)>1.1*Math.max(s,o))return[];return[[Hr(Lr(e.a,t),h),Hr(Lr(e.b,t),h)]]}),o).flatMap(([e,i])=>{const o=bt({a:Or(t,h,e),b:Or(t,h,i),halfDepth:s});return o?[o]:[]})}function Gr(t){const e=t.coordScale??1,s=Math.max(t.epsilon??2e-4*t.gridPitch,1e-9),i=[];for(const s of t.rooms||[]){if(!s?.id)continue;const o=mt(t.rooms,s.id,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e);if(!o||o.poly.length<3)continue;const a=gt(o.poly,o.offsets);!a||a.length<3||i.push({roomId:s.id,profile:o,inner:a})}i.sort((t,e)=>t.roomId.localeCompare(e.roomId));const o=(t.roomOpenings||[]).map(t=>{const e=t.angle*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Math.max(0,t.length)/2;return{a:Or([t.x,t.y],s,-i),b:Or([t.x,t.y],s,i)}}),a=[];for(const i of ft(t.rooms,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e))for(const t of Wr(i.a,i.b,i.depthUnits/2,o,s))a.push({body:t});const n=new Map;for(const e of t.partitionCuts||[]){const t=n.get(e.hostId)||[];t.push(e),n.set(e.hostId,t)}for(const e of t.partitions||[]){const i=j(e.cm,t.cellCm,t.gridPitch)/2;for(const t of Wr(e.a,e.b,i,n.get(e.id)||[],s))a.push({body:t,partitionId:e.id})}return{rooms:i,boundaries:a,epsilon:s}}function jr(t,e,s,i){const o=t.profile.offsets[e];if(!(o>0))return null;const a=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return function(t){let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(t.profile.poly,e),n=Or(s.center,a,o),r=[];for(let e=0;ei&&Er(s.axis,[h[0]/l,h[1]/l],1e-6)))continue;if(Br(o,n,s.axis)>i||Br(a,n,s.axis)>i)continue;const c=Hr(Lr(o,n),s.axis),d=Hr(Lr(a,n),s.axis);r.push([Math.min(c,d),Math.max(c,d)])}const h=function(t,e){t.sort((t,e)=>t[0]-e[0]||t[1]-e[1]);const s=[];for(const i of t){const t=s[s.length-1];!t||i[0]>t[1]+e?s.push([...i]):t[1]=Math.max(t[1],i[1])}return s}(r,i).find(([t,e])=>t<=i&&e>=-i);if(!h)return null;const[l,c]=h,d=Or(n,s.axis,-s.half),p=Or(n,s.axis,s.half),_=Math.max(0,-s.half-l),u=Math.max(0,c-s.half),m=_>0?Or(n,s.axis,l):d,g=u>0?Or(n,s.axis,c):p,f=Hr(a,s.normal)>=0?1:-1,b=(e,i,o)=>({from:e,to:i,label:[(e[0]+i[0])/2,(e[1]+i[1])/2],axis:s.axis,labelNormal:a,distance:o,roomId:t.roomId,roomSide:f,source:"room-face"});return{left:b(d,m,_),right:b(p,g,u)}}function qr(t){const e=Or(t.center,t.axis,-t.half),s=Or(t.center,t.axis,t.half),i=Or(t.center,t.axis,Math.min(-t.half,t.targetLo)),o=Or(t.center,t.axis,Math.max(t.half,t.targetHi)),a=(e,s)=>({from:e,to:s,label:[(e[0]+s[0])/2,(e[1]+s[1])/2],axis:t.axis,labelNormal:t.normal,distance:Math.hypot(s[0]-e[0],s[1]-e[1]),source:"host-end"});return[a(e,i),a(s,o)]}function Ur(t,e,s,i){const o=Lr(s,e),a=Hr(o,o);if(!(a>i*i))return Math.hypot(...Lr(t,e))<=i;const n=Hr(Lr(t,e),o)/a;if(n<-i||n>1+i)return!1;const r=Or(e,o,Math.max(0,Math.min(1,n)));return Math.hypot(...Lr(t,r))<=i}function Kr(t,e,s){let i=!1;for(let o=0,a=e.length-1;ot[1]!=l>t[1]&&t[0]<(h-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}function Vr(t,e,s,i,o,a){const n=Lr(i,s),r=Nr(e,n);if(Math.abs(r)<=a)return null;const h=Lr(s,t),l=Nr(h,n)/r,c=Nr(h,e)/r;return l<-a||l>o+a||c<-a||c>1+a?null:Math.max(0,Math.min(o,l))}function Jr(t,e,s){const i=qr(s),o="partition"===t.host?.kind?t.host.id:void 0;return[{base:i[0],direction:[-s.axis[0],-s.axis[1]]},{base:i[1],direction:s.axis}].map(({base:t,direction:s})=>{const i=t.distance,a=function(t,e,s,i,o,a){let n=null;for(const r of i)if(!o||r.partitionId!==o){if(Kr(t,r.body,a))return 0;for(let i=0;iHr(Lr(t,o),s));return{center:o,axis:s,normal:i,half:Math.max(0,t.renderedLength)/2,targetLo:Math.min(...a),targetHi:Math.max(...a)}}(t),i=e.rooms.flatMap(t=>{const i=function(t,e,s){const{profile:i}=t;let o=null;for(let t=0;t0))continue;const a=i.poly[t],n=i.poly[(t+1)%i.poly.length],r=Lr(n,a),h=Math.hypot(r[0],r[1]);if(!(h>s&&Er(e.axis,[r[0]/h,r[1]/h],1e-6)))continue;if(Br(a,e.center,e.axis)>s||Br(n,e.center,e.axis)>s)continue;const l=Hr(Lr(a,e.center),e.axis),c=Hr(Lr(n,e.center),e.axis),d=Math.min(l,c),p=Math.max(l,c);if(d>-e.half+s||p0?d:p<0?-p:0);(!o||_2)return qr(s);if(i.length){const t=i.map(({room:t,edge:i})=>jr(t,i,s,e.epsilon));if(t.some(t=>!t))return qr(s);const o=t;return[...o.map(t=>t.left),...o.map(t=>t.right)]}return Jr(t,e,s)}const Xr="1.69.0-beta.4",Zr={badge:"display.badge",icon_ripple:"display.icon_ripple",value:"display.value",static_icon:"display.static_icon"},Qr={badge:"marker.display_hint_badge",icon_ripple:"marker.display_hint_icon_ripple",value:"marker.display_hint_value",static_icon:"marker.display_hint_static_icon"},th=2e3,eh=1e3,sh=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const s=Number(e);return Number.isFinite(s)?s:null},ih=(t,e,s,i)=>{for(t.delete(e),t.set(e,s);t.size>i;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}},oh=1e3,ah=2e3,nh=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}},rh="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";class hh{constructor(t){this.host=t,this._junctionBaselineCache=new WeakMap,this._undoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.undo();t&&(this._applyGeometryState(t.before,!0)?this.host._showToast(this.host._t("history.undone",{name:t.name})):this.host._geometryHistory.clear())},this._redoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.redo();t&&(this._applyGeometryState(t.after,!0)?this.host._showToast(this.host._t("history.redone",{name:t.name})):this.host._geometryHistory.clear())},this._savePhysicalDialog=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=sh(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?vt:100,"column"===t.kind?1:0);const o=this.host._imperial?2.54*i:i,a="column"===t.kind?vt:100,n="column"===t.kind?1:0;if(!Number.isFinite(o)||oa)return void this._showPhysicalRange(a,n);if("column"===t.kind){const e=s.wall_columns.find(e=>e.id===t.id);if(!e)return;const i=sh(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this.host._showToast(this.host._t("toast.physical_angle"));const a="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:o}:{id:t.id,shape:"square",center:e.center,cm:o,angle:i};if(s.wall_columns.some(e=>e.id!==t.id&&yt(e,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"))}const r=this._geometrySnapshot();if("partition"===t.kind){const s=(e.partitions||[]).find(e=>e.id===t.id);if(s&&0===o&&$t(e.openings,{kind:"partition",id:t.id}))return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"));s&&(s.cm=o)}else if("column"===t.kind){const s=(e.wall_columns||[]).find(e=>e.id===t.id);s&&(s.cm=wt(o),s.shape="circle"===t.shape?"circle":"square","square"===s.shape?s.angle=sh(t.angle||"0"):delete s.angle)}else{const s=(e.room_drafts||[]).find(e=>e.id===t.id);s?.segments?.[t.segment||0]&&(s.segments[t.segment||0].cm=o)}const h=this._commitPhysicalGeometry(this.host._t("history.physical_edit"),r);this.host._physicalDialog=null,h||this.host.requestUpdate()},this._deletePhysicalSelection=()=>{const t=this.host._physicalSel,e=this.host._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void this._deleteDraftWhole();const s=this._geometrySnapshot();if("partition"===t.kind){const s=(e.openings||[]).filter(e=>"partition"===e.host?.kind&&e.host.id===t.id).sort((t,e)=>(t.host?.t||0)-(e.host?.t||0));if(s.length)return void(this.host._partitionDeleteDialog={id:t.id,openings:s.map(t=>JSON.parse(JSON.stringify(t)))})}const i="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[i]=(e[i]||[]).filter(e=>e.id!==t.id),e[i].length||delete e[i],this.host._activeDraftId===t.id&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._confirmPartitionDelete=()=>{const t=this.host._partitionDeleteDialog,e=this.host._curSpaceCfg;if(!t||!e)return;const s=this._geometrySnapshot();e.partitions=(e.partitions||[]).filter(e=>e.id!==t.id),e.partitions.length||delete e.partitions,e.openings=(e.openings||[]).filter(e=>"partition"!==e.host?.kind||e.host.id!==t.id),e.openings.length||delete e.openings,this.host._partitionDeleteDialog=null,this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftWhole=()=>{const t="draft"===this.host._physicalDialog?.kind?this.host._physicalDialog.id:"draft"===this.host._physicalSel?.kind?this.host._physicalSel.id:null,e=this.host._curSpaceCfg;if(!t||!e||!confirm(this.host._t("confirm.delete_draft")))return;const s=this._geometrySnapshot();e.room_drafts=(e.room_drafts||[]).filter(e=>e.id!==t),e.room_drafts.length||delete e.room_drafts,this.host._activeDraftId===t&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftSegment=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg;if(!t||"draft"!==t.kind||!e)return;if(!confirm(this.host._t("confirm.delete_draft_segment")))return;const s=(e.room_drafts||[]).findIndex(e=>e.id===t.id);if(s<0)return;const i=e.room_drafts[s],o=Math.max(0,Math.min(i.segments.length-1,t.segment||0)),a=[],n=i.points.slice(0,o+1),r=i.points.slice(o+1);if(n.length>=2&&a.push({id:i.id,points:n,segments:i.segments.slice(0,o)}),r.length>=2&&a.push({id:a.length?`${i.id}-${Date.now().toString(36)}`:i.id,points:r,segments:i.segments.slice(o+1)}),2===a.length&&e.room_drafts.length>=200)return void this.host._showToast(this.host._t("toast.physical_limit"));const h=this._geometrySnapshot();e.room_drafts.splice(s,1,...a),e.room_drafts.length||delete e.room_drafts,this.host._activeDraftId===i.id&&this._cancelPath(),this.host._physicalDialog=null,this.host._physicalSel=a.length?{kind:"draft",id:a[0].id}:null,this._commitPhysicalGeometry(this.host._t("history.draft_segment_delete"),h)},this._furnShiftListener=t=>{if("Shift"!==t.key)return;const e=this.host._furnPreviewInput;if(!e)return;const s="keydown"===t.type;e.free!==s&&(this.host._furnPreviewInput={...e,free:s},this.host.requestUpdate())},this._furnShiftAttached=!1,this._confirmRoomDelete=t=>{const e=this.host._roomDeleteDialog,s=this.host._curSpaceCfg,i=this.host._spaceModel();if(!e||!s||!i)return;const o=i.rooms.find(t=>t.id===e.roomId);if(!o)return void(this.host._roomDeleteDialog=null);const a=this.host._openCuts(),n=nn(A(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,oh).filter(t=>t.roomId===o.id),i.partitions,this.host._openingsR.map(t=>({...t,x:t.rx,y:t.ry})),.02*this.host._gridPitch),r=new Set(n.materialize.filter(t=>!t.reusePartitionId).map(t=>t.interval.key)).size;if(t&&(s.partitions||[]).length+r>ah)return void this.host._showToast(this.host._t("toast.physical_limit"));const h=this._geometrySnapshot(),l=kt(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,oh);if(t){s.partitions||=[];const t=new Map,e=new Map,i=t=>{const e=(s.wall_segments||[]).find(e=>{const s=[Number(e.a?.[0])*oh,Number(e.a?.[1])*oh],i=[Number(e.b?.[0])*oh,Number(e.b?.[1])*oh];return this._samePt(t.a,s)&&this._samePt(t.b,i)||this._samePt(t.a,i)&&this._samePt(t.b,s)});return"string"==typeof e?.id&&e.id?e.id:null},o=Date.now().toString(36);for(const a of n.materialize){let n=a.reusePartitionId?s.partitions.find(t=>t.id===a.reusePartitionId):e.get(a.interval.key);n||(n={id:i(a.interval)||`partition-room-${o}-${e.size}`,a:[a.interval.a[0]/oh,a.interval.a[1]/oh],b:[a.interval.b[0]/oh,a.interval.b[1]/oh],cm:a.interval.cm},s.partitions.push(n),e.set(a.interval.key,n)),t.set(a.interval.key,n)}for(const e of s.openings||[]){const s=n.openingIntervals.get(e.id),i=s?t.get(s):null,o=this.host._openingsR.find(t=>t.id===e.id);if(!i||!o)continue;const a={a:[i.a[0]*oh,i.a[1]*oh],b:[i.b[0]*oh,i.b[1]*oh]};e.host={kind:"partition",id:i.id,t:rn([o.rx,o.ry],a)}}}else if(n.removeOpeningIds.length){const t=new Set(n.removeOpeningIds);s.openings=(s.openings||[]).filter(e=>!t.has(e.id)),s.openings.length||delete s.openings}s.rooms=s.rooms.filter(t=>t.id!==o.id),this.host._cfgEpoch++;const c=this.host._normalizeWalls(l,this.host._openCuts());c.length?s.walls=c:delete s.walls,this.host._roomDeleteDialog=null,this._commitPhysicalGeometry(this.host._t(t?"history.delete_room_keep_walls":"history.delete_room_with_walls"),h),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate()},this._rebindPartitionOpening=()=>{const t=this.host._openingDialog;if(!t?.id)return;const e=t.id;this._activateMarkupTool("opening"),"opening"===this.host._tool&&(this.host._openingRebindId=e,this.host._openingPreset={type:t.type,lengthCm:t.lengthCm,flipH:t.flipH,flipV:t.flipV,revision:++this.host._openingPresetRevision},this.host._openingDialog=null,this.host._openingHoverCandidate=null)},this._deviceInboxTabKey=t=>{if("ArrowLeft"!==t.key&&"ArrowRight"!==t.key)return;const e=this.host._deviceInbox;if(!e)return;const s=["on_plan","available","hidden","readd"],i="ArrowRight"===t.key?1:-1,o=(s.indexOf(e.tab)+i+s.length)%s.length;this.host._deviceInbox={...e,tab:s[o],limit:100,onlyNew:!1},t.preventDefault()},this._keepClosedAsPartitions=()=>{if(this.host._wallFaceBatch)return void this._decideWallFace(!1);if(!this.host._contourClosed||this.host._pendingSplit||!this.host._curSpaceCfg)return;const t=this.host._curSpaceCfg,e=this.host._path.slice(0,-1);if((t.partitions||[]).length+e.length>ah)return void this.host._showToast(this.host._t("toast.physical_limit"));const s=this._geometrySnapshot(),i=Ra(e.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,N);t.partitions||=[];const o=Date.now().toString(36),a=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,n=this._draftSegmentsForPath(this.host._path,a,i);for(let s=0;st.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.contour_to_partitions"),s),this.host._roomDialog=!1,this.host._path=[],delete this.host._resumeDraftBySpace[this.host._space],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}},this._openSettingsDialog=()=>{if(!this.host._norm)return;const t=this.host._glowRadiusCm,e=this.host._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this.host._settingsDialog={colors:JSON.parse(JSON.stringify(this.host._fillColors)),glowRadius:e,bgColor:Dt(this.host._settings,{bgColor:null})||null,northDeg:Mt(this.host._settings,{}),bgMode:St(this.host._settings,{}),sunRays:xt(this.host._settings,{}),busy:!1}},this._openAlignDialog=()=>this._previewAlignDialog(!1),this._toggleOptimizeLivePositions=()=>{const t=this.host._alignDialog;t&&!t.busy&&t.report.liveMissingPositions.length&&this._previewAlignDialog(!t.removeLiveMissingPositions)},this._openBackupExport=()=>{this.host._settingsDialog=null,this.host._backupExportDialog={kind:"full",planOnly:!1,busy:!1,error:""}},this._decorStylePersistTimer=null,this._openRulesDialog=()=>{if(!this.host._norm)return;const t=this.host._settings.icon_rules,e=(t&&t.length?t:Ct).map(t=>({...t}));this.host._rulesDialog={rules:e,test:"",busy:!1}},t._editorSecondary=new gn({root:()=>t.renderRoot,requestUpdate:()=>t.requestUpdate(),updateComplete:()=>t.updateComplete,clearTip:()=>{t._tip=null}}),t._resize=new Wo}_help(t){const e=`${t}.aria`,s=d(this.host.hass,this.host._config?.language);return Pt(s,t)&&Pt(s,e)?o``:a}_setMode(t,e=!0){this.host._endTabDrag(),this._clearFurniturePreview(),this.host._pendingNavMode=null;const s=this.host._spaceModel();if(!s)return this.host._cancelModeTransition(!1),void(this.host._mode="view");if(this.host._kiosk&&"view"!==t)return;if(this.host._mode===t){if(!e&&"view"===t&&this.host._modeTransitionBusy){const e=this.host._currentModeVisual(t),s=this.host._modeTransitionTargetZoom,i=this.host._modeTransitionTargetCenterX,o=this.host._modeTransitionTargetCenterY;this.host._cancelModeTransition(!1),this.host._commitViewModeAtomic(e,s,i,o)}return}if(this.host._bootSoftCancel(),("plan"===t||"decor"===t)&&!this.host._norm)return void this.host._showToast(this.host._t("toast.markup_needs_server"));if(this.host._wallFaceBatch&&this._roomDialogCancel(),"plan"===this.host._mode&&"draw"===this.host._tool&&!this._finishWallChain())return;this.host._clearTransientHover(!0),this.host._cancelDevicePressFeedback();const i=this.host._mode,o=this.host._effectiveProjection(),a=this.host._currentModeVisual(i),n=this.host._modeTransitionBusy,r=a&&"view"!==i&&"view"!==t?{...a,toolbarContentOpacity:this.host._reducedMotion?1:.35}:a;this.host._cancelModeTransition(!1),this.host._editorSecondary.closeForNavigation(),(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)&&this._cancelDecorGesture();const h=!s.bg&&"view"===t!=("view"===i),l=n&&"view"===i&&"view"!==t?this.host._modeTransitionEditorCamera:null;let c=l?.zoom??(n?this.host._modeTransitionTargetZoom:this.host._zoom),d=l?l.centerX:n?this.host._modeTransitionTargetCenterX:r?.viewport.centerX,p=l?l.centerY:n?this.host._modeTransitionTargetCenterY:r?.viewport.centerY;if("view"===i&&"view"!==t&&!n){const e=this.host._view;if(this.host._viewModeSnap={space:this.host._space,zoom:this.host._zoom,cx:e?this.host._logicalViewCenter(o)?.x:void 0,cy:e?this.host._logicalViewCenter(o)?.y:void 0},"iso"===o){const e=this.host._logicalViewCenter("iso");d=e?.x,p=e?.y,this.host._view=null,this.host._mode=t,this.host._applyView(this.host._zoom,e?.x,e?.y)}h&&(c=1,d=void 0,p=void 0)}if("plan"===i&&this.host._activeDraftId&&(this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId),this.host._mode=t,"devices"===i&&"devices"!==t&&(this.host._showHidden=!1,this.host._deviceInbox=null,this.host._deviceInboxReturn=null,this.host._deviceInboxMemo=null),this.host._editorChromeMode="view"===t?i:t,"view"===t){"view"===i||n||(this.host._modeTransitionEditorCamera={zoom:this.host._zoom,centerX:r?.viewport.centerX,centerY:r?.viewport.centerY});const t=this.host._viewModeSnap;if(t&&t.space===this.host._space){c=t.zoom;const e=this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space]?"iso":"flat",s=null!=t.cx&&null!=t.cy&&"iso"===e?It([t.cx,t.cy],0):null;d=s?.[0]??t.cx,p=s?.[1]??t.cy}else t&&(c=this.host._zoomBySpace[this.host._space]||1,d=void 0,p=void 0)}else this.host._modeTransitionEditorCamera={zoom:c,centerX:d,centerY:p};this.host._modeTransitionTargetZoom=c,this.host._modeTransitionTargetCenterX=d,this.host._modeTransitionTargetCenterY=p,("iso"===o||"view"===t&&this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space])&&(this.host._modeTransitionForceAtomic=!0);const _=++this.host._modeTransitionRequest;e?r?(this.host._modeTransitionPreparing=!0,this.host._modeTransitionVisual=r,this.host._prepareModeTransition(_,r,t,c,d,p)):(this.host._modeTransitionPreparing=!1,this.host._modeTransitionVisual=null,this.host._zoom=c,this.host._view=null,requestAnimationFrame(()=>{this.host.isConnected&&_===this.host._modeTransitionRequest&&this.host._mode===t&&(this.host._applyView(c,d,p),"view"===t&&(this.host._viewModeSnap=null,this.host._modeTransitionEditorCamera=null),this.host.requestUpdate())})):"view"===t&&this.host._commitViewModeAtomic(r,c,d,p),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this.host._tool="draw",this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._selId=null,this.host._physicalSel=null,this.host._physicalDialog=null,this.host._physicalDrag=null,this._rszResetController(),this.host._tip=null,this.host._hoverRoom=null,this.host._decorDraft=null,this.host._decorSel=null,this.host._decorMove=null,this.host._backdropDialog=null,this.host._decorTool="select",this.host._bdDrag=null,this.host._dtDrag=null,this.host._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this.host._saveNav()}_primeDrawWallField(){null===this.host._drawWallField&&(this.host._drawWallField=Tt(N,this.host._imperial))}_showPhysicalRange(t=this.host._drawWallMaxCm,e=0){this.host._showToast(this.host._t("toast.physical_range",{min:Tt(e,this.host._imperial),max:Tt(t,this.host._imperial),unit:this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this.host._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_mergeSpacePartitions(t,e){const s=t?.partitions||[];if(s.length<2)return 0;const i=Rn(s,{pitch:I,seedIds:e,geometry:Fn(t,{excludeDraftId:this.host._activeDraftId})});return i.merged?(t.partitions=i.partitions,zn(t.openings,t.partitions,i.openingMoves,{coordScale:oh,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch}),i.merged):0}_finishWallChain(){if("draw"!==this.host._tool||this.host._wallFaceBatch||this.host._roomDialog)return!0;const t=this.host._curSpaceCfg;if(!t||this.host._path.length<2)return this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0;const e=za(this.host._path,Ra(this.host._path.length-1,this.host._draftSegmentCms,this.host._drawWallCm,N)),s=e.length;if((t.partitions||[]).length+s>ah)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const i=this._geometrySnapshot();t.partitions||=[];const o=Date.now().toString(36),a=[],n=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,r=this._draftSegmentsForPath(this.host._path,n,e.map(t=>t.cm));for(let i=0;it.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.wall_chain_finish"),i),delete this.host._resumeDraftBySpace[this.host._space],this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0}_activateMarkupTool(t){t!==this.host._tool&&(this.host._wallFaceBatch&&this._roomDialogCancel(),("draw"!==this.host._tool||this._finishWallChain())&&(this._cancelPath(),"resize"===this.host._tool&&(this.host._resize.dragging?this._rszCancelDrag():this.host._resize.reset()),this.host._tool=t,"draw"===t&&this._resumeLastDraft(),"resize"===t&&this.host._resize.selectRoom(null),"wallthick"===t&&(this.host._wallDialog=null)))}_limitReached(t){const e=this.host._curSpaceCfg;if(!e)return!0;const s="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=ah:(e.wall_columns||[]).length>=500;return s&&this.host._showToast(this.host._t("toast.physical_limit")),s}_svgPoint(t){const e=this.host.renderRoot.querySelector(".stage").getBoundingClientRect();return this.host._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this.host._gridPitch;return[Rt(zt(t[0],e)),Rt(zt(t[1],e))]}_snapDrawPoint(t,e=!1){const s=this.host._path[this.host._path.length-1],i=e&&s?Ft(s,t,this.host._gridPitch,Di):t;return this._snap(i)}_planSnapOpeningCuts(t,e){if(!this.host._openingsR.length)return[];const s=this.host._openingWallIndexFor(t,e).value,i=[];for(const e of this.host._roomWallOpeningInputs(this.host._openingsR,t)){const t=G(s,e);if(!t.negative&&!t.positive)continue;const o=e.angle*Math.PI/180,a=Math.cos(o)*e.length/2,n=Math.sin(o)*e.length/2;i.push([e.x-a,e.y-n,e.x+a,e.y+n])}return i}_planSnapGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|empty`,value:{segments:[],endpoints:[]}};const e=[this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planSnapGeometryCache?.key===e)return this.host._planSnapGeometryCache;const s=this.host._openCuts(),i=xa({space:t,activeDraftId:this.host._activeDraftId,roomCuts:this._planSnapOpeningCuts(t,s),partitionCuts:this._partitionOpeningCuts(t),epsilon:2e-4*this.host._gridPitch});return this.host._planSnapGeometryCache={key:e,value:i},this.host._planSnapGeometryCache}_hiddenWallDiagnosticSnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|hidden-empty`,value:{segments:[],endpoints:[]}};const e=["hidden",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._hiddenWallDiagnosticCache?.key===e)return this.host._hiddenWallDiagnosticCache;const s=ka({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._hiddenWallDiagnosticCache={key:e,value:s},this.host._hiddenWallDiagnosticCache}_planStructuralGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|structural-empty`,value:{segments:[],endpoints:[]}};const e=["structural",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planStructuralGeometryCache?.key===e)return this.host._planStructuralGeometryCache;const s=xa({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._planStructuralGeometryCache={key:e,value:s},this.host._planStructuralGeometryCache}_planSnapContextKey(t){const e=this.host._path[0],s=this.host._path[this.host._path.length-1];return[t,this.host._tool,this.host._path.length,e?`${e[0]},${e[1]}`:"",s?`${s[0]},${s[1]}`:""].join("|")}_resolvePlanDrawPoint(t,e){const s=this._planSnapGeometrySnapshot(),i=this.host._path[this.host._path.length-1],o="draw"===this.host._tool&&this.host._path.length>=3?[{point:this.host._path[0],key:"closure:first-point"}]:[],a={tolerance:this._cssPxToRender(12),distinguishTolerance:this._cssPxToRender(8),gridStep:this.host._gridPitch,excludePoints:i?[i]:[],extraEndpoints:o,epsilon:2e-4*this.host._gridPitch},n=e&&i?Ia(s.value,t,{...a,anchor:i}):function(t,e,s){if(!(ma(e)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??ua,o=s.excludePoints||[],a=Ca(Da(t,e,s),s.distinguishTolerance||0);if(a)return a;let n=null;for(const a of t.segments){const t=[a.a[0],a.a[1],a.b[0],a.b[1]],r=C([e[0],e[1]],t);if(r>s.tolerance)continue;const h=Pa(a,e,s.gridStep);!Sa(h,o,i)&&Ma(r,a.key,n)&&(n={kind:"line",point:h,key:a.key,distance:r,segment:a})}return n?{kind:"resolved",candidate:n,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}(s.value,t,a),r="resolved"===n.kind?n.candidate:null,h=r?[...r.point]:this._snapDrawPoint(t,e),l=i?At(i,h):h;return{point:l,candidate:r&&q(l,r.point)?r:null,conflicts:"ambiguous"===n.kind?n.conflicts:[],ambiguous:"ambiguous"===n.kind,contextKey:this._planSnapContextKey(s.key)}}_clearPlanSnapHover(t=!0){this.host._planSnapHover=null,this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers([]),t&&(this.host._cursorPt=null)}_samePt(t,e){return q(t,e)}_dropLegacySegments(){for(const t of this.host._serverCfg?.markers||[])"ripple"===t.display&&(t.display="icon_ripple");for(const t of this.host._serverCfg?.spaces||[]){delete t.segments;const e=new Set,s=t=>"string"==typeof t&&t.length>=1&&t.length<=64&&!e.has(t),i=t=>(e.add(t),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=Xt);if(Array.isArray(t.partitions)&&(t.partitions=t.partitions.filter(t=>t&&s(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&i(t.id)).map(t=>({...t,cm:Number.isFinite(Number(t.cm))?Math.max(0,Math.min(100,Number(t.cm))):15})),t.partitions.length||delete t.partitions),Array.isArray(t.wall_columns)&&(t.wall_columns=t.wall_columns.filter(t=>t&&s(t.id)&&o(t.center)&&i(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:wt(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:Nt(t.angle)}})),t.wall_columns.length||delete t.wall_columns),Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t&&s(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&i(t.id)).map(t=>{const e=lr(t);return{id:t.id,...e}}).filter(t=>t.points.length>=2),t.room_drafts.length||delete t.room_drafts),Array.isArray(t.walls)){const e=Ht(ut({spaces:[t]}),t.id),s=e?Lt(t,e,oh,.02*nt).contour.map(t=>t.map(t=>t/oh)):V(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=rt(t.walls,t.rooms||[],I,1,s),t.walls.length||delete t.walls}}}_rollbackRejectedPhysicalWrites(t){if(!this.host._serverCfg||!t.length)return!1;let e=!1;for(const[s]of t){const t=this.host._pendingPhysicalWrites.get(s);t&&(e=this._restoreGeometryStateInConfig(this.host._serverCfg,t.before)||e,this.host._pendingPhysicalWrites.delete(s))}return!!e&&(this._clearGeometryGesture(),this.host._geometryHistory.clear(),this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host._regSignature="",this.host._cfgContentFingerprint=Ot(this.host._serverCfg),this.host._maybeRebuildDevices(),this.host.requestUpdate(),!0)}async _reloadRejectedPhysicalWrite(){const t=this.host._writeChain;await t.catch(()=>{}),await this.host._reloadConfigOnly(!0)}_writeConfig(){this.host._writesPending++,this.host._writeChain=Et(this.host._writeChain,async()=>{if(!this.host._serverCfg)return;this._dropLegacySegments();const t=ki(this.host._serverCfg),e=Ot(t),s=[...this.host._pendingPhysicalWrites.entries()];for(const[e,i]of s){const s=t.spaces.find(t=>t.id===e),o=jt(s);if(o===i.fingerprint)continue;let a=!1;try{a=this.host._checkSpacePhysicalGeometry(t,e).ok}catch{a=!1}if(!a)throw this._restoreGeometryStateLocal(i.before),this.host._pendingPhysicalWrites.delete(e),this.host._geometryHistory.clear(),this.host._showToast(this.host._t("toast.geometry_unsafe")),Object.assign(new Error("unsafe wall geometry"),{code:"geometry-unsafe"});i.fingerprint=o}e!==Ot(this.host._serverCfg)&&(this.host._serverCfg=t),this.host._cfgContentFingerprint=e;try{await this.host._sendConfigCandidate(t)}catch(t){if(!this._rollbackRejectedPhysicalWrites(s))throw t;const e=t&&"object"==typeof t?t:Object.assign(new Error(String(t)),{cause:t});throw e.physicalGeometryRolledBack=!0,e}for(const[t,e]of s)this.host._pendingPhysicalWrites.get(t)?.fingerprint===e.fingerprint&&this.host._pendingPhysicalWrites.delete(t)});return this.host._writeChain.finally(()=>{this.host._writesPending--})}_saveConfig(){this.host._cfgEpoch++,this.host._saveConfigDebounced()}_geometrySnapshotFromConfig(t,e){const s=t?.spaces?.find(t=>t.id===e);if(!s)return null;const i=t=>JSON.parse(JSON.stringify(t)),o={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==s[t]&&(o[t]=s[t]);return{spaceId:e,rooms:i(s.rooms||[]),...Array.isArray(s.openings)?{openings:i(s.openings)}:{},...Array.isArray(s.walls)?{walls:i(s.walls)}:{},...Array.isArray(s.wall_segments)?{wall_segments:i(s.wall_segments)}:{},...Array.isArray(s.open_spans)?{open_spans:i(s.open_spans)}:{},...Array.isArray(s.room_drafts)?{room_drafts:i(s.room_drafts)}:{},...Array.isArray(s.partitions)?{partitions:i(s.partitions)}:{},...Array.isArray(s.wall_columns)?{wall_columns:i(s.wall_columns)}:{},...Array.isArray(s.decor)?{decor:i(s.decor)}:{},plan_transform:o}}_geometrySnapshot(t=this.host._space){return this._geometrySnapshotFromConfig(this.host._serverCfg,t)}_recordGeometry(t,e){if(!e)return;const s=this._geometrySnapshot(e.spaceId);s&&JSON.stringify(e)!==JSON.stringify(s)&&(this.host._geometryHistory.push({name:t,before:e,after:s}),this.host.requestUpdate())}_restoreGeometryStateInConfig(t,e,s=!1){const i=t?.spaces?.find(t=>t.id===e.spaceId);if(!i)return!1;const o=t=>JSON.parse(JSON.stringify(t)),a=new Map((i.rooms||[]).map(t=>[t.id,t]));if(i.rooms=o(e.rooms),s)for(const t of i.rooms){const e=a.get(t.id);!Array.isArray(t.wall_ids)&&Array.isArray(e?.wall_ids)&&e.wall_ids.length===t.poly?.length&&(t.wall_ids=o(e.wall_ids))}const n=(t,e)=>{void 0!==e?i[t]=o(e):s&&"wall_segments"===t||delete i[t]},r=new Map((i.openings||[]).map(t=>[t.id,t])),h=new Map((i.room_drafts||[]).map(t=>[t.id,t]));if(n("openings",e.openings),n("walls",e.walls),n("wall_segments",e.wall_segments),n("open_spans",e.open_spans),n("room_drafts",e.room_drafts),n("partitions",e.partitions),n("wall_columns",e.wall_columns),n("decor",e.decor),s){for(const t of i.openings||[]){const e=r.get(t.id);t.host||"wall"!==e?.host?.kind||(t.host=o(e.host))}for(const t of i.room_drafts||[]){const e=h.get(t.id);for(let s=0;s<(t.segments||[]).length;s++)!t.segments[s]?.id&&e?.segments?.[s]?.id&&(t.segments[s].id=e.segments[s].id)}}for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete i[t];return Object.assign(i,o(e.plan_transform||{})),!0}_restoreGeometryStateLocal(t){return!!this._restoreGeometryStateInConfig(this.host._serverCfg,t)&&(this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host.requestUpdate(),!0)}_wallModelBlockerLabel(t){const e=t instanceof er?t.reason:"invalid-room";return this.host._t(`wall_model.reason.${e}`)}_hasLegacyZeroWallFields(t=this.host._serverCfg){return(t?.spaces||[]).some(t=>Array.isArray(t?.open_spans)&&t.open_spans.length>0||(t?.rooms||[]).some(t=>Array.isArray(t?.open_to)&&t.open_to.length>0))}_showWallModelMigrationBlocked(t){const e=this._hasLegacyZeroWallFields()?"toast.zero_wall_migration_blocked":"toast.wall_model_migration_blocked";this.host._showToast(this.host._t(e,{reason:this._wallModelBlockerLabel(t)}))}_limitSegmentsOf(t){const e=[];for(const s of t?.wall_segments||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.partitions||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.room_drafts||[]){const t=Array.isArray(s?.points)?s.points:[],i=Array.isArray(s?.segments)?s.segments:[];for(let o=0;o+1t?.id===e);if(!i)return[];const o=Number(i.cell_cm)>0?Number(i.cell_cm):5,a=this._limitSegmentsOf(i),n=[...ta(a),...aa(a,o,I),...ra(a,o,I)];let r=null;try{r=Bt(i.rooms||[],i.walls||[],[],I,o,I,1)}catch{r=null}let h="ok"===s?.status||"degraded-extra"===s?.status?s.roomGeom:null;if(!h&&r&&r.nodes.length)try{const t=Wt(i.rooms||[],i.walls||[],[],[],I,o,I,1);h="ok"===t?.status||"degraded-extra"===t?.status?t.roomGeom:null}catch{h=null}for(const t of i.rooms||[]){const e=String(t?.id||"");if(!e)continue;let s=null;try{s=Gt(i.rooms||[],e,i.walls||[],[],I,o,I,1,h??void 0,r)}catch{s=null}n.push(...ha(e,s,o,I))}return n}_junctionLimitLabel(t){const e=t=>String(Math.round(10*t)/10);return this.host._t(`junction.limit_${t.rule}`,{actual:e(t.actual),limit:e(t.limit)})}_junctionLimitsIntroduced(t,e,s,i){let o;const a=(e?.spaces||[]).find(t=>t?.id===s);let n="";try{n=jt(a)}catch{n=""}const r=e&&"object"==typeof e?this._junctionBaselineCache.get(e):void 0;if(r&&n&&r.fingerprint===n&&r.spaceId===s&&(o=r.violations),!o){try{const t=Number(e?.model_version||0)>=9?e:Mr(e).config;o=this.host._junctionLimitViolations(t,s)}catch{return[]}e&&"object"==typeof e&&n&&this._junctionBaselineCache.set(e,{spaceId:s,fingerprint:n,violations:o})}let h=[];try{h=this.host._junctionLimitViolations(t,s,i)}catch{return[{rule:"check_failed",subject:s,actual:0,limit:0}]}return function(t,e){const s=new Map;for(const t of e||[])s.set(t.rule,(s.get(t.rule)||0)+1);const i=new Map;for(const e of t||[])i.set(e.rule,[...i.get(e.rule)||[],e]);const o=[];for(const[t,e]of i){const i=e.length-(s.get(t)||0);i>0&&o.push(...e.slice(0,i))}return o}(h,o)}_commitPhysicalGeometry(t,e,s=[]){if(!e||!this.host._serverCfg)return!1;const i=this.host._serverCfg,o=this._geometrySnapshotFromConfig(i,e.spaceId),a=i.spaces.find(t=>t.id===e.spaceId);if(!a||jt(e)===jt(a))return!1;let n,r=!1;try{r=this.host._checkSpacePhysicalGeometry(i,e.spaceId).ok}catch{r=!1}if(!r)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;let h=e;try{if(Number(i.model_version||0)<9){const t=JSON.parse(JSON.stringify(i));if(!this._restoreGeometryStateInConfig(t,e))throw new er("invalid-room",e.spaceId);const s=Mr(t).config;h=this._geometrySnapshotFromConfig(s,e.spaceId)||e;const a=JSON.parse(JSON.stringify(s));if(!o||!this._restoreGeometryStateInConfig(a,o,!0))throw new er("invalid-room",e.spaceId);const r=s.spaces.find(t=>t.id===e.spaceId),l=a.spaces.find(t=>t.id===e.spaceId);if(!Array.isArray(o.wall_segments)&&JSON.stringify(e.rooms)===JSON.stringify(o.rooms))for(const t of l?.wall_segments||[])t.cm=Q(o.walls,t.a,t.b,I,1);const c=((t,e,s)=>{const i=new Map,o=new Map((t?.rooms||[]).map(t=>[t.id,t])),a=new Map((e||[]).map(t=>[t.id,t])),{rooms:n}=$r(s,vr(t));for(const t of n){const e=a.get(t.id),n=o.get(t.id),r=R(e),h=(s.rooms||[]).find(e=>e.id===t.id),l=R(h),c=R(n);if(r&&l&&c&&r.length===l.length&&c.length===n?.wall_ids?.length&&t.poly.length===t.wall_ids.length)for(let e=0;e{const o=[];for(let a=0;atr||pr(r,s,i)>tr)continue;const h=dr(n,s,i),l=dr(r,s,i);h<-tr||l>1+tr||l<=h+tr||o.push({start:h,id:e[a]})}return o.sort((t,e)=>t.start-e.start)},o=s(c,n.wall_ids,r[e],r[(e+1)%r.length]),a=s(t.poly,t.wall_ids,l[e],l[(e+1)%l.length]);if(o.length&&o.length===a.length)for(let t=0;tt.id===e.spaceId),i=this.host._path.length>=2?this.host._path.map(t=>[t[0]/oh,t[1]/oh]):[];i.push(...s.map(t=>[t[0],t[1]])),l=cr(t)<=cr(h,i)&&this.host._checkSpacePhysicalGeometry(n,e.spaceId).ok}catch{l=!1}if(!l)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;const c=JSON.parse(JSON.stringify(i));this._restoreGeometryStateInConfig(c,e);const d=this._junctionLimitsIntroduced(n,c,e.spaceId);if(d.length)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this._junctionLimitLabel(d[0])),!1;Cr(i,n),this._recordGeometry(t,h);const p=i.spaces.find(t=>t.id===e.spaceId),_=this.host._pendingPhysicalWrites.get(e.spaceId);return this.host._pendingPhysicalWrites.set(e.spaceId,{before:_?.before||h,fingerprint:jt(p)}),this._saveConfig(),!0}_clearGeometryGesture(){this._clearFurniturePreview(),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!1),this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._wallFaceBatch=null,this.host._wallRepairDiagnostic=null,this.host._roomDeleteDialog=null,this.host._wallDialog=null,this.host._physicalDialog=null,this.host._physicalSel=null,this.host._physicalDrag=null,this.host._physicalRotate=null,this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._openingDialog=null,this._rszResetController(),this.host._decorDraft=null,this.host._decorMove=null,this.host._dtDrag=null,this.host._bdDrag=null}_stagePointerCancel(t){if(clearTimeout(this.host._kioskHoldTimer),this.host._swipeStart?.id===t.pointerId&&(this.host._swipeStart=null),this.host._furnTouchPending?.pid===t.pointerId)return this._clearFurniturePreview(),this.host._pointers.delete(t.pointerId),void this.host.requestUpdate();if(this.host._physicalDrag?.pid===t.pointerId||this.host._physicalRotate?.pid===t.pointerId)return void this._cancelPhysicalGesture();if(this.host._decorDraft?.pid===t.pointerId)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove?.pid===t.pointerId||this.host._dtDrag?.pid===t.pointerId||this.host._bdDrag?.pid===t.pointerId)return void this._cancelDecorGesture();"opening"===this.host._tool?(this.host._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this.host._tool&&this._clearPlanSnapHover();const e=!!this.host._pinchStart||!!this.host._panStart;this.host._pointers.delete(t.pointerId),this.host._pointers.size<2&&(this.host._pinchStart=null),0===this.host._pointers.size&&(this.host._panStart=null,this.host._panLock=null),e&&0===this.host._pointers.size&&this.host.requestUpdate()}_applyGeometryState(t,e=!1){if(!this.host._canCommitSpace(t.spaceId))return!1;const s=this._geometrySnapshot(t.spaceId);if(!s||!this._restoreGeometryStateLocal(t))return!1;const i=this.host._serverCfg,o=jt(s)!==jt(t);if(o){let o=!1;try{const s=i?this.host._checkSpacePhysicalGeometry(i,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1}let a;try{a=Mr(i).config}catch(t){return this._restoreGeometryStateLocal(s),this._showWallModelMigrationBlocked(t),!1}if(o){let o=!1;try{const s=a?this.host._checkSpacePhysicalGeometry(a,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;Cr(i,a);const n=i?.spaces.find(e=>e.id===t.spaceId),r=this.host._pendingPhysicalWrites.get(t.spaceId);this.host._pendingPhysicalWrites.set(t.spaceId,{before:r?.before||s,fingerprint:jt(n)})}else Cr(i,a);return this._clearGeometryGesture(),this.host._space!==t.spaceId&&(this.host._commitSpace(t.spaceId),this.host._saveNav(),this.host._restoreZoom()),this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate(),!0}_roomAt(t){return this.host._spaceModel()?.rooms.find(e=>{const s=R(e);return!!s&&qt(t,s)})}_overlapRoom(t){return this.host._spaceModel()?.rooms.find(e=>{const s=R(e);return!!s&&$(t,s)})}_pointInRoom(t,e){return e.poly?Ut(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!mo(t))return!0;const e=t.length,s=.001;for(let i=0;i=500)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const t=this.host._curSpaceCfg;return!(!this.host._activeDraftId&&(t?.room_drafts||[]).length>=200||this._draftSegmentCount(t)>=2e3||(t?.partitions||[]).length+this.host._path.length>ah)||(this.host._showToast(this.host._t("toast.physical_limit")),!1)}_markupClick(t){if(this.host._vacFit)return;if(!this.host._markup)return;const e=this.host._spaceModel();if(!e)return void this._clearGeometryGesture();if(this.host._suppressClick)return;if(this.host._drag||this.host._rlResize)return;const s=t.composedPath?.()||[];if(s.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(s.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this.host._tool)return void(this.host._physicalSel=null);if("resize"===this.host._tool){if(this.host._resize.dragging||s.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...e.rooms].reverse().find(t=>this._pointInRoom(i,t));return this.host._resize.selectRoom(t?.id||null),void this.host.requestUpdate()}if("delroom"===this.host._tool)return void this._deleteRoomClick(i);if("opening"===this.host._tool)return void this._openingClick(i);if("merge"===this.host._tool)return void this._mergeClick(i);if("wallthick"===this.host._tool)return void this._wallThickClick(i);if("split"===this.host._tool)return void this._splitClick(i);if("column"===this.host._tool)return void this._columnClick(i);this.host._wallRepairDiagnostic=null;const o=this._resolvePlanDrawPoint(i,t.shiftKey);if(o.ambiguous)return this.host._planSnapHover={contextKey:o.contextKey,candidate:null,conflicts:o.conflicts},this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers(o.conflicts),void this.host._showToast(this.host._t("toast.plan_snap_ambiguous"));let a=o.point;if(t.ctrlKey||t.metaKey){if(t.preventDefault(),this.host._path.length<3)return;a=At(this.host._path[this.host._path.length-1],this.host._path[0])}if(!this.host._path.length){if(!t.shiftKey&&!o.candidate&&this._offerExistingWallFace(i))return;const e=this._draftEndAt(a);return e?(this.host._activeDraftId=e.draft.id,this.host._resumeDraftBySpace[this.host._space]=e.draft.id,this.host._path=e.reverse?[...e.draft.points].reverse().map(t=>[...t]):e.draft.points.map(t=>[...t]),void(this.host._draftSegmentCms=this._adoptDraftCms(this.host._path,e.reverse?[...e.draft.segments].reverse().map(t=>t.cm):e.draft.segments.map(t=>t.cm),e.draft.id))):(this.host._activeDraftId=null,this.host._draftSegmentCms=[],void(this.host._path=[a]))}const n=this.host._path[this.host._path.length-1];if(this._samePt(a,n))return;const r=this._draftEndAt(a,this.host._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(!this._canAppendRoomDraftPoint())return;const h=this.host._drawWallCm;if(null==h)return void this._showPhysicalRange(100);const l=this.host._path.map(t=>[...t]);this.host._path=[...this.host._path,a],this.host._draftSegmentCms=[...this.host._draftSegmentCms,h],this._persistActiveDraftSegment(),this._offerWallFaces(l)}_draftEndAt(t,e){const s=this.host._spaceModel();if(!s)return null;const i=this.host._viewOr(this.host._baseVb()),o=Math.max(.15*this.host._gridPitch,this.host._stageEl?.clientWidth?i.w/this.host._stageEl.clientWidth*12:0);for(const i of s.room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const s=i.points[0],a=i.points[i.points.length-1];if(Math.hypot(t[0]-a[0],t[1]-a[1])<=o)return{draft:i,reverse:!1};if(Math.hypot(t[0]-s[0],t[1]-s[1])<=o)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this.host._curSpaceCfg;if(!e||!this.host._path.length)return;const s=this.host._path.map(t=>[...t]),i=this._wallGraphSources(s),o=Array.isArray(e.room_drafts)?e.room_drafts:[],a=this.host._activeDraftId?o.find(t=>t.id===this.host._activeDraftId):null,n=o.find(e=>e.id===t.draft.id);if(!n)return;const r=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),h=t.reverse?(n.segments||[]).map(t=>({...t})):[...n.segments||[]].reverse().map(t=>({...t})),l=this.host._path[this.host._path.length-1],c=this._samePt(l,r[0]),d=c?null:this.host._drawWallCm;if(!c&&null==d)return void this._showPhysicalRange(100);const p=this._draftSegmentsForPath(this.host._path,a,this.host._draftSegmentCms),_=[...this.host._path.map(t=>[...t]),...c?r.slice(1):r],u=[...p,...null==d?[]:[{cm:d}],...h],m=_.length>=4&&this._samePt(_[0],_[_.length-1]),g=_,f=u;if(g.length>500)return void this.host._showToast(this.host._t("toast.physical_limit"));const b=(a?.segments?.length||0)+(n.segments?.length||0);if(this._draftSegmentCount(e)-b+f.length>2e3)return void this.host._showToast(this.host._t("toast.physical_limit"));if(m){const t=g.slice(0,-1);if(this._contourSelfIntersects(t)||k(t)<=1e-6)return void this.host._showToast(this.host._t("toast.contour_cannot_close"));const e=this._overlapRoom(t);if(e)return void this.host._showToast(this.host._t("toast.room_overlap",{name:e.name||""}))}const v=this._geometrySnapshot(),y=this.host._activeDraftId||t.draft.id,$={...a||n,id:y,points:g.map(t=>[t[0]/oh,t[1]/oh]),segments:f};e.room_drafts=o.filter(e=>e.id!==t.draft.id&&(!this.host._activeDraftId||e.id!==this.host._activeDraftId)),e.room_drafts.push($),this.host._activeDraftId=y,this.host._resumeDraftBySpace[this.host._space]=y,this.host._path=g,this.host._draftSegmentCms=this._adoptDraftCms(g,f.map(t=>Number(t.cm)),y),this.host._physicalSel=null,this._commitPhysicalGeometry(this.host._t("history.draft_merge"),v),null!=d&&this._offerWallFaces(s,s.length-1,i)}_adoptDraftCms(t,e,s){const i=Math.max(0,t.length-1),o=Ra(i,e,this.host._drawWallCm,N);return e.length!==i&&console.debug(`[houseplan] draft ${s??"?"}: восстановлено толщин ${i-e.length} (#234)`),o}_draftSegmentsForPath(t,e,s){const i=Array.isArray(e?.points)?e.points.map(t=>[Number(t?.[0])*oh,Number(t?.[1])*oh]):[],o=Array.isArray(e?.segments)?e.segments:[];return Ra(Math.max(0,t.length-1),s,this.host._drawWallCm,N).map((e,s)=>{const a=t[s],n=t[s+1],r=i.findIndex((t,e)=>{if(e+1>=i.length)return!1;const s=i[e+1];return this._samePt(a,t)&&this._samePt(n,s)||this._samePt(a,s)&&this._samePt(n,t)});return{...r>=0?o[r]:{},cm:e}})}_persistActiveDraftSegment(){if(this.host._path.length<2||!this.host._curSpaceCfg)return;const t=this._geometrySnapshot(),e=this.host._curSpaceCfg;e.room_drafts||=[],this.host._activeDraftId||(this.host._activeDraftId="draft-"+Date.now().toString(36)),this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId;const s=e.room_drafts.findIndex(t=>t.id===this.host._activeDraftId),i={...s>=0?e.room_drafts[s]:{},id:this.host._activeDraftId,points:this.host._path.map(t=>[t[0]/oh,t[1]/oh]),segments:this._draftSegmentsForPath(this.host._path,s>=0?e.room_drafts[s]:null,this.host._draftSegmentCms)};s>=0?e.room_drafts[s]=i:e.room_drafts.push(i),this._commitPhysicalGeometry(this.host._t("history.draft_segment"),t)}_activeWallSourceKey(t){return`active:${this.host._activeDraftId||"session"}:${t}`}_wallGraphSources(t){const e=this._planStructuralGeometrySnapshot().value.segments.map(t=>({a:t.a,b:t.b,key:`static:${t.key}`}));for(let s=0;s+1t.key===s);if(i>=0){const[t]=this.host._wallFaceGraphCache.splice(i,1);return this.host._wallFaceGraphCache.push(t),t.value}const o=en(t,e);return this.host._wallFaceGraphCache.push({key:s,value:o}),this.host._wallFaceGraphCache.length>4&&this.host._wallFaceGraphCache.shift(),o}_offerWallFaces(t,e=this.host._path.length-2,s){if(this.host._path.length<2||this.host._wallFaceBatch||this.host._roomDialog)return;const i=this.host._spaceModel();if(!i)return;const o=this._activeWallSourceKey(e),a=2e-4*this.host._gridPitch;let n,r;try{const e=s||this._wallGraphSources(t);r=this._wallGraphSources(this.host._path),n=function(t,e,s){const i=new Set(t.faces.map(t=>t.key));return e.faces.filter(t=>!i.has(t.key)&&t.sourceKeys.includes(s))}(this._wallFaceGraph(e,a),this._wallFaceGraph(r,a),o)}catch{return}if(!n.length){const t=un(r,{requiredSourceKey:o,maxDistance:j(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:a});return"ambiguous"===t.kind?(this.host._wallRepairDiagnostic=t.proposals[0]||null,void this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous"))):void("repair"!==t.kind||this._overlapRoom(t.face.ring)||this._beginWallFaceBatch([{...t.face,repair:t.proposal}]))}for(const t of i.rooms){const e=R(t);if(!t.id||!e)continue;const s=Kt(e,this.host._path,.02*this.host._gridPitch);if(!s)continue;const[i,o]=s,a=k(i)>=k(o)?i:o,r=a===i?o:i,h=[...n].sort((t,e)=>Math.abs(t.area-k(r))-Math.abs(e.area-k(r))||t.key.localeCompare(e.key))[0];if(!h)return;return void this._beginWallFaceBatch([{...h,ring:r.map(t=>[t[0],t[1]]),split:{roomId:t.id,mainPoly:a,newPoly:r},consumeAllActive:!0}])}const h=n.filter(t=>!(t.ring.length<3||this._contourSelfIntersects(t.ring))&&!this._overlapRoom(t.ring));h.length&&this._beginWallFaceBatch(h)}_beginWallFaceBatch(t){this.host._wallRepairDiagnostic=null,this.host._wallFaceBatch={candidates:[...t].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key)),index:0,decisions:[],activePath:this.host._path.map(t=>[...t]),activeCms:[...this.host._draftSegmentCms],activeDraftId:this.host._activeDraftId},this._clearPlanSnapHover(),this.host._nameSel="",this.host._areaSel="",this._resetRoomDialogFields(),this.host._roomDialog=!0}_offerExistingWallFace(t){if(this.host._path.length||this.host._wallFaceBatch||this.host._roomDialog)return!1;const e=2e-4*this.host._gridPitch;try{let s,i=sn(this._wallFaceGraph(this._wallGraphSources([]),e),t,e);if(!i){const o=un(this._wallGraphSources([]),{point:t,maxDistance:j(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:e});if("ambiguous"===o.kind)return this.host._wallRepairDiagnostic=o.proposals[0]||null,this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous")),!0;if("repair"===o.kind)i=o.face,s=o.proposal;else if("none"===o.kind){const s=un(this._wallGraphSources([]),{point:t,maxDistance:this._cssPxToRender(12),gridStep:this.host._gridPitch,epsilon:e});if("repair"===s.kind)return this.host._wallRepairDiagnostic=s.proposal,this.host._showToast(this.host._t("toast.wall_repair_too_large")),!0}}return!(!i||i.ring.length<3||this._contourSelfIntersects(i.ring)||this._overlapRoom(i.ring))&&(this._beginWallFaceBatch([{...i,existing:!s,repair:s}]),!0)}catch{return!1}}_columnClick(t){const e=this._snap(t),s=this.host._drawWallCm;if(null==s)return void this._showPhysicalRange(vt,1);if(!this.host._curSpaceCfg||this._limitReached("column"))return;const i=this.host._spaceModel();if(!i)return;const o={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:wt(s),angle:0},a=(i.wall_columns||[]).find(t=>yt(t,o,.02*this.host._gridPitch));if(a)return clearTimeout(this.host._duplicateColumnTimer),this.host._duplicateColumnId=a.id,this.host._duplicateColumnTimer=window.setTimeout(()=>{this.host._duplicateColumnId=null},900),void this.host._showToast(this.host._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this.host._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...o,center:[e[0]/oh,e[1]/oh]}),this._commitPhysicalGeometry(this.host._t("history.column_add"),n)}_openPhysicalDialog(t,e,s){const i=this.host._spaceModel();if(i)if("partition"===t){const s=i.partitions.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:Tt(s.cm,this.host._imperial),length:this.host._fmtLen(s.a,s.b)})}else if("column"===t){const s=i.wall_columns.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:Tt(s.cm,this.host._imperial),shape:s.shape,angle:this.host._angleField("square"===s.shape?Nt(s.angle):0)})}else{const o=i.room_drafts.find(t=>t.id===e),a=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,s||0));o?.segments[a]&&(this.host._physicalDialog={kind:t,id:e,segment:a,cm:Tt(o.segments[a].cm,this.host._imperial),length:this.host._fmtLen(o.points[a],o.points[a+1])})}}_physicalDown(t,e,s){const i=this.host._spaceModel();if(!i)return;t.stopPropagation(),nh(t);const o=this._svgPoint(t),a=[];for(const t of[...i.wall_columns].reverse())Vt(o,Jt(t,this.host._cellCm,this.host._gridPitch))&&a.push({kind:"column",id:t.id});for(const t of[...i.partitions].reverse()){const e=Yt(t.a,t.b,t.cm,this.host._cellCm,this.host._gridPitch);e&&Vt(o,e)&&a.push({kind:"partition",id:t.id})}a.some(t=>t.kind===e&&t.id===s)||a.unshift({kind:e,id:s});const n=a.map(t=>`${t.kind}:${t.id}`).sort().join("|"),r=performance.now(),h=this.host._physicalPickCycle,l=a.length>1&&h?.signature===n&&r-h.at>380&&r-h.at<=1200&&Math.hypot(t.clientX-h.x,t.clientY-h.y)<=10?(h.index+1)%a.length:Math.max(0,a.findIndex(t=>t.kind===e&&t.id===s));this.host._physicalPickCycle={signature:n,index:l,x:t.clientX,y:t.clientY,at:r},e=a[l].kind,s=a[l].id;const c="partition"===e?i.partitions.find(t=>t.id===s):i.wall_columns.find(t=>t.id===s);c&&(this.host._physicalSel={kind:e,id:s},this.host._physicalDrag={pid:t.pointerId,kind:e,id:s,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,s){const i="partition"===t?[e.a,e.b]:[e.center],o=i.map(t=>t[0]),a=i.map(t=>t[1]);return[Math.max(-Xt*oh-Math.min(...o),Math.min(Xt*oh-Math.max(...o),s[0])),Math.max(-Xt*oh-Math.min(...a),Math.min(Xt*oh-Math.max(...a),s[1]))]}_physicalMove(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const s=this._svgPoint(t),i="partition"===e.kind?e.base.a:e.base.center,o=this._snap([i[0]+s[0]-e.start[0],i[1]+s[1]-e.start[1]]),a=this._clampPhysicalDelta(e.kind,e.base,[o[0]-i[0],o[1]-i[1]]);this.host._physicalDrag={...e,delta:a,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this.host._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);const s=this.host._spaceModel();if(!this.host._curSpaceCfg||!s)return;const i=this.host._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;if(t){const o={...s,a:[s.a[0]+e.delta[0],s.a[1]+e.delta[1]],b:[s.b[0]+e.delta[0],s.b[1]+e.delta[1]]};t.a=[o.a[0]/oh,o.a[1]/oh],t.b=[o.b[0]/oh,o.b[1]/oh];for(const t of i.openings||[]){if("partition"!==t.host?.kind||t.host.id!==e.id)continue;const s=z(t,[o],oh,this.host._cellCm,this.host._gridPitch).resolved;s&&Object.assign(t,F(t,s,oh))}}}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),o=e.base,a={...o,center:[o.center[0]+e.delta[0],o.center[1]+e.delta[1]]};if(s.wall_columns.some(t=>t.id!==e.id&&yt(t,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));t&&(t.center=[(o.center[0]+e.delta[0])/oh,(o.center[1]+e.delta[1])/oh])}this._commitPhysicalGeometry(this.host._t("history.physical_move"),e.before)}_registerPhysicalTap(t,e,s){const i=performance.now(),o=this.host._physicalLastTap?.kind===t&&this.host._physicalLastTap.id===e&&this.host._physicalLastTap.segment===s&&i-this.host._physicalLastTap.at<=360;this.host._physicalLastTap={kind:t,id:e,segment:s,at:i},o&&(this.host._physicalLastTap=null,this._openPhysicalDialog(t,e,s))}_cancelPhysicalGesture(){this.host._physicalDrag=null,this.host._physicalRotate=null,this.host.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape||!this.host._spaceModel())return;t.preventDefault(),t.stopPropagation(),nh(t);const s=this._svgPoint(t);this.host._physicalSel={kind:"column",id:e.id},this.host._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,baseAngle:Nt(e.angle),angle:Nt(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const s=this._svgPoint(t),i=180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,o=e.baseAngle+i-e.startAngle,a=Nt(t.shiftKey?o:5*Math.round(o/5));this.host._physicalRotate={...e,angle:a,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation(),this.host._physicalRotate=null;const s=this.host._spaceModel();if(!e.moved||!this.host._curSpaceCfg||!s)return;const i=s.wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const o={...i,angle:e.angle};if(s.wall_columns.some(t=>t.id!==e.id&&yt(t,o,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));const a=this.host._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);a&&(a.angle=e.angle,this._commitPhysicalGeometry(this.host._t("history.physical_edit"),e.before))}_rszRooms(){const t=[],e=this.host._spaceModel();if(!e)return t;for(const s of e.rooms){const e=s.id?R(s):null;e&&t.push({id:s.id,poly:e,wall_ids:Array.isArray(s.wall_ids)?[...s.wall_ids]:void 0})}return s=t,i=Math.max(1e-12,1e-12*this.host._gridPitch),s.map(t=>{if(!Array.isArray(t.wall_ids)||t.wall_ids.length!==t.poly.length)return{...t,poly:t.poly.map(t=>[...t])};const e=t.poly.map(t=>[...t]);let s=!0;for(;s&&e.length>3;){s=!1;for(let t=0;t0){e.splice(t,1),s=!0;break}}}return{...t,poly:e}});var s,i}_rszOpenings(){return this.host._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen,hosted:!!t.host,angle:t.angle,type:t.type}))}_rszObstacles(){const t=this.host._spaceModel();if(!t)return[];const e=[];for(const s of t.partitions||[])e.push({kind:"segment",a:[...s.a],b:[...s.b],half:j(s.cm,this.host._cellCm,this.host._gridPitch)/2});for(const s of t.room_drafts||[])for(let t=0;t+1Array.isArray(t.a)&&Array.isArray(t.b)&&t.a.length>=2&&t.b.length>=2).map(t=>({wall:t,a:[t.a[0]*oh,t.a[1]*this.host._spaceH],b:[t.b[0]*oh,t.b[1]*this.host._spaceH]})),o=Math.abs(t[0]-e[0])<=.05*this.host._gridPitch?"v":Math.abs(t[1]-e[1])<=.05*this.host._gridPitch?"h":null,a=i.filter(s=>!!o&&("h"===o?!(Math.abs(s.a[1]-t[1])>.05*this.host._gridPitch||Math.abs(s.b[1]-t[1])>.05*this.host._gridPitch)&&Math.min(Math.max(t[0],e[0]),Math.max(s.a[0],s.b[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s.a[0],s.b[0]))>.05*this.host._gridPitch:!(Math.abs(s.a[0]-t[0])>.05*this.host._gridPitch||Math.abs(s.b[0]-t[0])>.05*this.host._gridPitch)&&Math.min(Math.max(t[1],e[1]),Math.max(s.a[1],s.b[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s.a[1],s.b[1]))>.05*this.host._gridPitch)),n=new Set(a.map(t=>Number(t.wall.cm)).filter(t=>t>0));return{minDim:this.host._cmToUnits(30),eps:.05*this.host._gridPitch,step:this.host._gridPitch,movingHalf:s>0?j(s,this.host._cellCm,this.host._gridPitch)/2:0,obstacles:this._rszObstacles(),thicknessConflict:n.size>1}}_rszResolution(t,e,s){const i=this.host._resize.snapshotIdentity||s||this._rszSnapshot(),o=`${this.host._space}|${this.host._cellCm}|${this.host._gridPitch}|${i}`,a=`${t}:${e}`;return this.host._resize.resolve(o,a,()=>{const s=[...this.host._resize.rooms||this._rszRooms()],i=s.find(e=>e.id===t),o=i?.poly?.[e]||[0,0],a=i?.poly?.[(e+1)%(i?.poly?.length||1)]||[0,0];return function(t,e,s,i,o){const{eps:a}=o,n=t.find(t=>t.id===s);if(!n||i<0||i>=(n.poly?.length||0)||n.poly.length<4||!mo(n.poly))return{enabled:!1,reason:"invalid-geometry"};const r=n.poly[i],h=n.poly[(i+1)%n.poly.length],l=vo(r,h,a);if(!l)return{enabled:!1,reason:"diagonal"};if(!ko(n.poly,i,l,a))return{enabled:!1,reason:"side-angle"};if(o.thicknessConflict)return{enabled:!1,reason:"thickness-conflict"};for(const t of o.obstacles||[])if(Mo(t,r,h,o))return{enabled:!1,reason:"duplicate-physical-wall"};const c=co(ro(h,r)),d=[],p=new Set;let _=!1,u=!1;for(const e of t)if(e.id!==s)for(let t=0;t1||d.length>1)return{enabled:!1,reason:"multiple-rooms"};const m={[s]:i},g=[s];if(1===d.length){const t=d[0];if(!mo(t.room.poly))return{enabled:!1,reason:"invalid-geometry"};if(!ko(t.room.poly,t.edge,l,a))return{enabled:!1,reason:"side-angle"};g.push(t.room.id),m[t.room.id]=t.edge}const f=[];for(const t of e)if(!(_o([t.x,t.y],r,h)>2*a)){if(t.hosted||t.length>c+2*a)return{enabled:!1,reason:"opening-conflict"};f.push(t.id)}const b=Object.fromEntries(g.map(e=>[e,t.find(t=>t.id===e).poly.length])),v=[];for(const e of g){const s=t.find(t=>t.id===e),i=m[e],o=(i-1+s.poly.length)%s.poly.length,n=(i+1)%s.poly.length,r=Po(t,e,o,1,a),h=Po(t,e,n,0,a);if(!r||!h)return{enabled:!1,reason:"partial-shared"};v.push(r,h)}const y={roomId:s,edge:i,a:[...r],b:[...h],n:uo(n.poly,i),roomIds:g,edgeByRoom:m,topology:b,movingOpeningIds:f,sideOwnership:v};if(!No(t,e,y,0,o))return{enabled:!1,reason:"invalid-geometry"};const $=Math.abs(Number(o.step));if(Number.isFinite($)&&$>a){const s=[-$,$];if(!s.some(s=>No(t,e,y,s,o))){if(!s.some(s=>Ro(zo(t,e,y,s),y,a)))return{enabled:!1,reason:"partial-shared"};const i={...o,obstacles:[]};return s.some(s=>No(t,e,y,s,i))?{enabled:!1,reason:"duplicate-physical-wall"}:e.length&&s.some(e=>No(t,[],y,e,o))?{enabled:!1,reason:"opening-conflict"}:{enabled:!1,reason:"invalid-geometry"}}}return{enabled:!0,plan:y}}(s,[...this.host._resize.openings||this._rszOpenings()],t,e,this._rszOptsFor(o,a))})}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this.host._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszResetController(){const t=null!==this.host._resize.preview;this.host._resize.reset(),t&&this.host._cfgEpoch++}_rszProjectPreview(t,e,s,i,o){this.host._rszLimitViolation=null;const a=this.host._serverCfg?.spaces.find(t=>t.id===this.host._space);if(!a||!this.host._serverCfg)return{ok:!1,reason:"missing-context"};const n=JSON.parse(t),r={...a,rooms:n.rooms,openings:n.openings||[]};for(const t of["walls","wall_segments","open_spans","room_drafts","partitions","wall_columns","decor"])void 0!==n[t]?r[t]=n[t]:delete r[t];for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==n.plan_transform?.[t]?r[t]=n.plan_transform[t]:delete r[t];const h=this.host._spaceH;for(const[t,s]of Object.entries(e)){const e=r.rooms.find(e=>e.id===t);e&&(e.poly=s.map(t=>[t[0]/oh,t[1]/h]),delete e.x,delete e.y,delete e.w,delete e.h)}for(const[t,e]of Object.entries(s)){const s=(r.openings||[]).find(e=>e.id===t);s&&(s.x=e[0]/oh,s.y=e[1]/h)}const l=[],c=[];for(const t of i){const e=o.find(e=>e.id===t),s=r.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const i=s.poly.map(t=>[t[0]*oh,t[1]*h]);if(e.poly.length!==i.length)continue;const a=Array.isArray(e.wall_ids)?e.wall_ids:[];for(let t=0;tt.id===s):null;o&&(o.a=[i[t][0]/oh,i[t][1]/oh],o.b=[i[(t+1)%i.length][0]/oh,i[(t+1)%i.length][1]/oh])}}if(l.length&&Array.isArray(r.walls)&&r.walls.length){const t=Zt(r.walls,l,c,this.host._wallKeyPitch,oh,"fixed-topology");if(t.rejected)return{ok:!1,reason:"wall-metadata"};r.walls=t.walls}const d=[];for(const t of r.rooms||[]){const e=R(t);if(e&&!(e.length<2))for(let t=0;tJSON.stringify([t?.key,t?.cm,t?.a,t?.b]),_=new Map;for(const t of n.walls||[]){const e=p(t);_.set(e,(_.get(e)||0)+1)}const u=[];for(const t of r.walls||[]){const e=p(t),s=_.get(e)||0;s?_.set(e,s-1):u.push(t)}if(Qt(u,d,this.host._wallKeyPitch,oh,n.walls||[]).length)return{ok:!1,reason:"wall-metadata"};const m=this._rszSpaceCandidateGeometry(this.host._space,r);if(!m.ok)return{ok:!1,reason:"physical-geometry"};const g={...this.host._serverCfg,spaces:(this.host._serverCfg?.spaces||[]).map(t=>t?.id===this.host._space?r:t)},f=this._junctionLimitsIntroduced(g,this.host._serverCfg,this.host._space,m.wallGeometry);return f.length?(this.host._rszLimitViolation=f[0],{ok:!1,reason:"junction-limit"}):{ok:!0,value:{preview:{space:this.host._space,sp:r},beforeWalls:n.walls||[],afterWalls:r.walls||[],artifact:m.wallGeometry}}}_rszAcceptPreview(t,e){if(this.host._cfgEpoch++,!t||!e)return;const s=te(e);if(!s)return;const i=`${this.host._space}|${this.host._cfgEpoch}|${t.sp.rooms.length}`;Object.defineProperty(s,"sourceFingerprint",{value:Ot([t.sp,this.host._cellCm,this.host._gridPitch]),enumerable:!1});const o={key:i,value:s};ih(this.host._wallUnionPool,i,o,8),this.host._wallUnionCache=o}_rszSpaceCandidateGeometry(t,e){if(!this.host._serverCfg)return{ok:!1,wallGeometry:null};const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};let i=null;try{return{ok:this.host._checkSpacePhysicalGeometry(s,t,t=>{i=t}).ok,wallGeometry:i}}catch{return{ok:!1,wallGeometry:null}}}_rszSpaceCandidateRenderable(t,e){if(!this.host._serverCfg)return!1;try{const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};return this.host._checkSpacePhysicalGeometry(s,t).ok}catch{return!1}}_rszCandidateRenderable(t){return!!t&&t.space===this.host._space&&this._rszSpaceCandidateRenderable(t.space,t.sp)}_rszEdgeDown(t,e,s){if("resize"!==this.host._tool||this.host._resize.dragging)return;t.stopPropagation(),t.preventDefault();const i=this._rszRooms(),o=this._rszResolution(e,s);if(!o.enabled)return void this.host._showToast(this._rszReasonText(o.reason));nh(t);const a=o.plan,n=this._svgPoint(t),r=`${this.host._space}|${this.host._cfgEpoch}|${i.length}`,h=this.host._wallUnionCache?.key===r?this.host._wallUnionCache.value:null,l=this._rszSnapshot();this.host._resize.begin({pointerId:t.pointerId,start:[n[0],n[1]],roomId:e,plan:a,options:this._rszOptsFor(a.a,a.b),rooms:i,openings:this._rszOpenings(),snapshotIdentity:l,before:JSON.parse(l),wallUnionBefore:h})}_rszReasonText(t){return this.host._t(`resize.disabled.${t}`)}_rszDisabledActivate(t,e){t.stopPropagation(),t.preventDefault(),this.host._showToast(this._rszReasonText(e))}_rszDisabledKey(t,e){"Enter"!==t.key&&" "!==t.key||this._rszDisabledActivate(t,e)}_rszMove(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this._svgPoint(t),s=this.host._resize.move({pointerId:t.pointerId,point:[e[0],e[1]],step:this.host._gridPitch,snap:t=>{const e=this._snap(t);return[e[0],e[1]]},project:(t,e,s,i,o)=>this._rszProjectPreview(t,e,s,i,o),publish:(t,e)=>this._rszAcceptPreview(t,e),measure:(t,e)=>this._rszEdgeLabels(t,e)});if("rejected"===s.kind)return s.notify&&this.host._showToast(this.host._rszLimitViolation?`${this.host._t("resize.limit_stopped")} — `+this._junctionLimitLabel(this.host._rszLimitViolation):this.host._t("resize.preview_failed")),void this.host.requestUpdate();"accepted"===s.kind&&this.host.requestUpdate()}_rszUp(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this.host._resize.finish({pointerId:t.pointerId,currentSnapshotIdentity:this._rszSnapshot(),validatePreview:t=>this._rszCandidateRenderable(t)});if("no-op"===e.kind)return this.host._cfgEpoch++,void this.host.requestUpdate();if("rejected"===e.kind)return this.host._cfgEpoch++,this.host._showToast(this.host._t("resize.commit_failed")),void this.host.requestUpdate();const s=e.preview,i=this.host._serverCfg?.spaces.find(t=>t.id===s.space);i&&(i.rooms=s.sp.rooms,i.openings=s.sp.openings,Array.isArray(s.sp.walls)&&(s.sp.walls.length?i.walls=s.sp.walls:delete i.walls),Array.isArray(s.sp.wall_segments)&&(i.wall_segments=s.sp.wall_segments)),this.host._suppressClick=!0,setTimeout(()=>this.host._suppressClick=!1,0),this._commitPhysicalGeometry(this.host._t("history.resize_room"),e.before),this.host.requestUpdate()}_rszCancelDrag(t){const e=this.host._resize.cancel(this._rszSnapshot(),t);if("no-op"!==e.kind){if(this.host._cfgEpoch++,e.restoreWallUnion){const t=this.host._spaceModel();if(t){const s=`${this.host._space}|${this.host._cfgEpoch}|${t.rooms.length}`,i={key:s,value:e.restoreWallUnion};ih(this.host._wallUnionPool,s,i,8),this.host._wallUnionCache=i}}this.host.requestUpdate()}}_rszPointerCancel(t){this.host._resize.ownsPointer(t.pointerId)&&(t.stopPropagation(),this._rszCancelDrag(t.pointerId))}_rszEdgeLabels(t,e,s=this.host._resize.rooms){const i=s,o=[],a=t.polys[e.roomId]||i?.find(t=>t.id===e.roomId).poly;if(!a||!i)return o;const n=a.length,r=this._rszInnerSpanCms(e.roomId,a,t.polys);for(const t of function(t,e){const s=t.length;return[(e-1+s)%s,(e+1)%s]}(a,e.edge)){const e=a[t],s=a[(t+1)%n],i=r?.[t];o.push({kind:"length",x:(e[0]+s[0])/2,y:(e[1]+s[1])/2,text:null==i?this.host._fmtLen(e,s):ee(i,"mi"===this.host.hass?.config?.unit_system?.length),edge:{a:[e[0],e[1]],b:[s[0],s[1]]}})}const h="mi"===this.host.hass?.config?.unit_system?.length,l=e.roomIds,c=this.host._spaceWalls,d=this.host._physicalBodiesR(),p=this.host._baseVb(),_=this.host._view&&this.host._view.w>0&&this.host._view.h>0?this.host._view:{x:p[0],y:p[1],w:p[2],h:p[3]},[u,m]=this.host._lastValidStageSize||[_.w,_.h],g={..._,stageWidth:Math.max(1,u),stageHeight:Math.max(1,m)},f=this.host._spaceModel(),b=this.host._config?.icon_size??2.5,v=f?se(b>8?2.5:b,f,_.w,this.host._kiosk?this.host._kioskScale.icon:1)*g.stageWidth/100:24,y=Math.max(10,.77*v),$=this.host._t("room.settings_short"),w=y*(1.457+.66*Math.max(1,$.length)*.42);for(const s of l){const a=t.polys[s]||i.find(t=>t.id===s).poly,n=c.length&&f&&this.host._innerRoomContour(f,s)||a,r=d.length?ie(oe(n,d))*Math.pow(this.host._cellCm/this.host._gridPitch,2)/1e4:ae(n,this.host._gridPitch,this.host._cellCm),l=ne(r,h),p=jo({poly:a,edge:e.edgeByRoom[s],text:l,view:g,gearCenter:re(a),gearWidthPx:w,gearHeightPx:y});o.push({kind:"area",roomId:s,x:p.anchor[0],y:p.anchor[1],text:l,placement:p})}return o}_rszInnerSpanCms(t,e,s){const i=Object.keys(s).length?Object.entries(s).map(([t,e])=>({id:t,poly:e})):this.host._spaceModel()?.rooms;if(!i?.length)return null;const o=this.host._spaceWalls;if(!o.length)return null;const a=this.host._openCuts(),n=he(i,t,o,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,oh);if(!n||n.length!==e.length)return null;const r=this.host._cellCm/this.host._gridPitch;return e.map((t,s)=>le(e,s,n)*r)}_renderResizeMeasurements(){const t=this.host._resize.liveLabels;if(!t?.length)return a;const e=t.filter(t=>"length"===t.kind),s=t.filter(t=>"area"===t.kind);return ce`
`}afterRender(){const t=this.host.root();this._blocked&&(this._openGroupId?this.closeGroup(!1):"palette"===this._currentModel?.kind&&this._currentModel.dismiss?.()),this._syncGlobalDismissListener();const e=t.querySelector(".editor-secondary"),s=this._renderedContext,i=e?.dataset.contextId||"";if(i&&s&&i!==s&&!window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches&&(this._contentAnimation?.cancel(),this._contentAnimation=e?.querySelector(".editor-secondary-content")?.animate([{opacity:.35,transform:"translateY(-4px)"},{opacity:1,transform:"translateY(0)"}],{duration:100,easing:"ease-out"})),this._renderedContext=i,s&&i!==s&&this._focusOwned){const s=i?e?.querySelector('[tabindex="0"], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])'):t.querySelector(".editbar-tools");s?.focus?.(),this._focusOwned=!!i}if("palette"===this._currentModel?.kind&&this._currentModel.launcherId){const e=[...t.querySelectorAll("[data-editor-palette]")].find(t=>t.dataset.editorPalette===this._currentModel?.launcherId);e&&null!==e.offsetParent||this._currentModel.dismiss?.()}if(!this._openGroupId)return;const o=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===this._openGroupId);o&&null!==o.offsetParent||this.closeGroup(!0)}handleOutsideDismiss(t){if(gn(t,t=>t.hasAttribute("data-editor-navigation")))return"pointerdown"===t.type?this.closeForNavigation():"click"===t.type&&this._dismissPointerTarget&&this._clearDismissClick(),!1;if("click"===t.type&&this._dismissPointerTarget){return!!(t.composedPath?.()||[t.target]).includes(this._dismissPointerTarget)&&(this._clearDismissClick(),t.preventDefault(),t.stopImmediatePropagation(),!0)}const e=this._openGroupId||this._blocked||"palette"!==this._currentModel?.kind?null:this._currentModel;if("pointerdown"===t.type&&(this._openGroupId||e)){const s=gn(t,t=>t.classList.contains("editor-secondary")),i=gn(t,t=>!!t.dataset.editorGroup),o=gn(t,t=>!!t.dataset.editorPalette),a=gn(t,t=>t.classList.contains("stage"));if(!(s||i||o||"stay-open-on-canvas"===e?.dismissPolicy&&a))return t.preventDefault(),t.stopImmediatePropagation(),this._dismissPointerTarget=t.target,clearTimeout(this._dismissTimer),this._dismissTimer=window.setTimeout(()=>{this._clearDismissClick()},800),this._openGroupId?this.closeGroup(!1):e?.dismiss?.(),this._syncGlobalDismissListener(),!0}return!1}_activateGroupItem(t,e,s){if(t.id!==this._openGroupId)return;const i=t.items.find(t=>t.id===e);if(!i||i.disabled)return;const o="toggle"===i.role&&"stay-open"===i.closePolicy,a=0===s.detail;o||(this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener()),i.invoke(),!o&&a&&this.host.updateComplete().then(()=>{const e=[...this.host.root().querySelectorAll("[data-editor-group]")].find(e=>e.dataset.editorGroup===t.id&&null!==e.offsetParent);e?.focus()})}_groupKeydown(t){if(!["ArrowLeft","ArrowRight","Home","End"].includes(t.key))return;const e=[...t.currentTarget.querySelectorAll(".editor-group-item:not([disabled])")];if(!e.length)return;t.preventDefault();const s=e.indexOf(this.host.root().activeElement),i="Home"===t.key?0:"End"===t.key?e.length-1:"ArrowLeft"===t.key?s<=0?e.length-1:s-1:(s+1)%e.length;e[i]?.focus()}_clearDismissClick(){this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}_syncGlobalDismissListener(){const t=!this._blocked&&"palette"===this._currentModel?.kind,e=!!this._openGroupId||t||!!this._dismissPointerTarget;e!==this._globalDismissListening&&(this._globalDismissListening=e,e?(window.addEventListener("pointerdown",this._globalDismissGuard,!0),window.addEventListener("click",this._globalDismissGuard,!0)):(window.removeEventListener("pointerdown",this._globalDismissGuard,!0),window.removeEventListener("click",this._globalDismissGuard,!0)))}}const bn=t=>t.stopPropagation(),vn=1e-6*I,yn=6*I;function $n(t){return Number.isFinite(t)?Math.round(t/I)*I:t}const wn=(t,e,s,i)=>Math.hypot(s-t,i-e),kn=(t,e,s,i,o,a,n,r)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+n-(t+s))),Math.max(Math.abs(a-e),Math.abs(a+r-(e+i)))),xn=(t,e,s,i,o,a,n)=>{const r=Math.max(Number(i)||0,0)/2,h=Math.PI/180,l=Math.cos(s*h)*r,c=Math.sin(s*h)*r,d=Math.cos(n*h)*r,p=Math.sin(n*h)*r,u=Math.max(wn(t+l,e+c,o+d,a+p),wn(t-l,e-c,o-d,a-p)),_=Math.max(wn(t+l,e+c,o-d,a-p),wn(t-l,e-c,o+d,a+p));return Math.min(u,_)},Sn=t=>{const e=Number(t?.cell_cm);return e>0?e:5};function Mn(t,e){const s=JSON.parse(JSON.stringify(t||[])),i=JSON.parse(JSON.stringify(e||{}));let o=0,a=0,n=0,r=0,h="",l=0,c=0,d=0;const p=(t,e)=>{Number.isFinite(t)&&Number.isFinite(e)&&t!==e&&Math.abs(e-t)<=vn&&c++},u=(t,e)=>{p(t[0],e[0]),p(t[1],e[1])},_={};let m=5;for(const t of s){const e=Sn(t);null!=t?.id&&(_[String(t.id)]=e),e>m&&(m=e)}const g=(t,e,s,i=!1)=>{if(!(t>vn||i))return;o++,t>n&&(n=t);const a=t*T*e;a>r&&(r=a,h=s)};for(const t of s){const e=Sn(t),s=null!=t?.id?String(t.id):"";for(const i of t.rooms||[]){a++;let t=0;if(i.poly?.length)i.poly=i.poly.map(e=>{const s=[$n(e[0]),$n(e[1])];return t=Math.max(t,wn(e[0],e[1],s[0],s[1])),u(e,s),s});else if(null!=i.x&&null!=i.y){const e=i.x,s=i.y,o=i.w||0,a=i.h||0,n=$n(e+o),r=$n(s+a),h=$n(e),l=$n(s),c=Math.max(I,n-h),d=Math.max(I,r-l);t=kn(e,s,o,a,h,l,c,d),i.x=h,i.y=l,i.w=c,i.h=d,p(e,h),p(s,l),p(e+o,h+c),p(s+a,l+d)}g(t,e,s)}for(const i of t.room_drafts||[]){a++;let t=0;const o=i.points||[],n=o.map(e=>{const s=[$n(e[0]),$n(e[1])];return t=Math.max(t,wn(e[0],e[1],s[0],s[1])),s}),r=n.length?[n[0]]:[],h=n.length?[[o[0],n[0]]]:[],l=[];for(let t=0;t+1=2)for(const[t,e]of h)u(t,e);g(t,e,s)}if(Array.isArray(t.room_drafts)){const e=t.room_drafts.length;t.room_drafts=t.room_drafts.filter(t=>t.points?.length>=2),d+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const i of t.partitions||[]){a++;const o=[i.a[0],i.a[1]],n=[i.b[0],i.b[1]],r=[$n(o[0]),$n(o[1])],h=[$n(n[0]),$n(n[1])];let l=Math.max(wn(o[0],o[1],r[0],r[1]),wn(n[0],n[1],h[0],h[1]));const c=wn(r[0],r[1],h[0],h[1]),d=(t.openings||[]).filter(t=>"partition"===t.host?.kind&&t.host.id===i.id).every(t=>{const e=Number(t.length),s=Number(t.host.t),i=s*c;return Number.isFinite(e)&&e>0&&Number.isFinite(s)&&s>=0&&s<=1&&i-e/2>=-vn&&i+e/2<=c+vn});c>vn&&d?(i.a=r,i.b=h,u(o,r),u(n,h)):l=0,g(l,e,s)}for(const i of t.wall_columns||[]){a++;const t=[i.center[0],i.center[1]],o=[$n(t[0]),$n(t[1])],n=wn(t[0],t[1],o[0],o[1]);i.center=o,u(t,o),g(n,e,s)}for(const i of t.decor||[]){a++;let t=0;if("line"===i.kind){const e=[i.x1,i.y1],s=[i.x2,i.y2],o=[$n(e[0]),$n(e[1])],a=[$n(s[0]),$n(s[1])];t=Math.max(wn(e[0],e[1],o[0],o[1]),wn(s[0],s[1],a[0],a[1])),i.x1=o[0],i.y1=o[1],i.x2=a[0],i.y2=a[1],u(e,o),u(s,a)}else{const e=i.x,s=i.y,o=i.w,a=i.h,n=$n(e),r=$n(s);if(null!=i.w&&null!=i.h){const h=$n(e+o),l=$n(s+a),c=Math.max(I,h-n),d=Math.max(I,l-r);t=kn(e,s,o,a,n,r,c,d),i.w=c,i.h=d,p(e+o,n+c),p(s+a,r+d)}else t=wn(e,s,n,r);i.x=n,i.y=r,p(e,n),p(s,r)}g(t,e,s)}for(const i of t.openings||[]){if(a++,"partition"===i.host?.kind){const o=(t.partitions||[]).find(t=>t.id===i.host.id),a=Number(i.host.t);if(!o||!Number.isFinite(a)||a<0||a>1)continue;const n=o.b[0]-o.a[0],r=o.b[1]-o.a[1];if(Math.hypot(n,r)<=vn)continue;const h=o.a[0]+n*a,c=o.a[1]+r*a;let d=180*Math.atan2(r,n)/Math.PI;d>=90?d-=180:d<-90&&(d+=180);const p=Number(i.angle),u=!(Number.isFinite(p)&&p===d),_=xn(i.x,i.y,Number.isFinite(p)?p:d,Number(i.length)||0,h,c,d);i.x=h,i.y=c,i.angle=d,u&&l++,g(_,e,s,u);continue}const o=P([i.x,i.y],t.rooms||[],yn,{step:I,length:Number(i.length)||0});if(!o)continue;const n=Number(i.angle),r=!(Number.isFinite(n)&&n===o.angle),h=xn(i.x,i.y,Number.isFinite(n)?n:o.angle,Number(i.length)||0,o.x,o.y,o.angle);i.x=o.x,i.y=o.y,i.angle=o.angle,r&&l++,g(h,e,s,r)}}for(const[t,e]of Object.entries(i)){if(!e||"object"!=typeof e)continue;const s=e;if("number"!=typeof s.x||"number"!=typeof s.y)continue;a++;const o=$n(s.x),n=$n(s.y),r=wn(s.x,s.y,o,n);i[t]={...s,x:o,y:n},p(s.x,o),p(s.y,n);const h="string"==typeof s.s?s.s:"";g(r,_[h]??m,h)}return{spaces:s,layout:i,report:{moved:o,coordsCanonicalized:c,total:a,maxShift:n,maxShiftCm:r,maxSpace:h,rotated:l,removedDrafts:d},changed:o>0||c>0}}function Dn(t,e){const s=e.base+t*e.span;return Math.min(1,Math.max(0,s))}const Cn=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),Pn=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1]);function In(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(a<=0)return Pn(t,e);let n=((t[0]-e[0])*i+(t[1]-e[1])*o)/a;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))}function Tn(t,e,s,i,o){for(const i of e)if(i&&!s.has(i.id)&&Cn(i.a)&&Cn(i.b)&&(Pn(t,i.a)<=o||Pn(t,i.b)<=o))return!0;for(const e of i?.roomPolygons||[])for(let s=0;s0&&r>0))return null;if(Math.abs(o[0]/n*(a[1]/r)-o[1]/n*(a[0]/r))>s)return null;for(const[s,o]of[[t.a,e.a],[t.a,e.b],[t.b,e.a],[t.b,e.b]])if(Pn(s,o)<=i)return[(s[0]+o[0])/2,(s[1]+o[1])/2];return null}function zn(t,e){const s=.05*(Number(e.pitch)>0?Number(e.pitch):1),i=new Map;let o=(t||[]).filter(t=>!!t&&"string"==typeof t.id&&Cn(t.a)&&Cn(t.b));if(o.length<2)return{partitions:[...t||[]],merged:0,openingMoves:[]};const a=e.seedIds&&e.seedIds.length?new Set(e.seedIds):null;let n=0;for(let t=0;tPn(e,t.at)>s);let c=l[0]??r.a,d=l[l.length-1]??h.b;if(l.length<2&&(c=r.a,d=h.b),d[0]0?(d[0]-c[0])/u:0,m=u>0?(d[1]-c[1])/u:0,g=t=>u>0?((t[0]-c[0])*_+(t[1]-c[1])*m)/u:0;for(const t of[r,h]){const e=g(t.a),s=g(t.b)-e;for(const[o,a]of i)a.toId===t.id&&i.set(o,{fromId:a.fromId,toId:p.id,base:e+a.base*s,span:a.span*s});i.set(t.id,{fromId:t.id,toId:p.id,base:e,span:s})}a&&a.add(p.id),o=o.filter((e,s)=>s!==t.i&&s!==t.j),o.splice(t.i,0,p),n++}return{partitions:o,merged:n,openingMoves:[...i.values()]}}function Fn(t,e,s,i){if(!t?.length||!s.length)return 0;const o=new Map(s.map(t=>[t.fromId,t]));let a=0;for(const s of t){const t=s?.host;if(!t||"partition"!==t.kind)continue;const n=o.get(t.id);if(!n)continue;s.host={...t,id:n.toId,t:Dn(t.t,n)},a++;const r=z(s,e,i.coordScale,i.cellCm,i.gridPitch).resolved;r&&Object.assign(s,F(s,r,i.coordScale))}return a}function An(t,e){const s=e?.excludeDraftId||null;return{roomPolygons:(t?.rooms||[]).map(t=>R(t)).filter(t=>!!t),columns:t?.wall_columns||[],draftEnds:(t?.room_drafts||[]).flatMap(t=>{if(s&&t?.id===s)return[];const e=t?.points||[];return e.length?[e[0],e[e.length-1]]:[]})}}const Nn=t=>!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),Hn=(t,e)=>t[0]-e[0]||t[1]-e[1],Ln=(t,e,s,i)=>[t[0]+e*i,t[1]+s*i],On=(t,e,s,i)=>(t[0]-e[0])*s+(t[1]-e[1])*i,En=(t,e,s,i,o)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o))return null;const h=a/r,l=n/r,c=e=>Math.abs((e[0]-t[0])*l-(e[1]-t[1])*h);if(c(s)>o||c(i)>o)return null;const d=On(s,t,h,l),p=On(i,t,h,l),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));return _-u>o?{lo:u,hi:_}:null},Bn=(t,e,s)=>Math.min(t.hi,e.hi)-Math.max(t.lo,e.lo)>s,Wn=t=>{const e=new Set(["id","a","b","cm"]);return Object.keys(t||{}).every(t=>e.has(t))},Gn=(t,e,s,i)=>{const o=t.b[0]-t.a[0],a=t.b[1]-t.a[1],n=Math.hypot(o,a);if(!(n>i))return!0;const r=o/n,h=a/n,l=j(t.cm,s.cellCm,s.gridPitch)/2;return e.some(e=>{const o=e?.center;if(!Nn(o))return!0;const a=On(o,t.a,r,h),c=Math.abs((o[0]-t.a[0])*h-(o[1]-t.a[1])*r),d=j(Number(e.cm),s.cellCm,s.gridPitch)*("square"===e.shape?Math.SQRT1_2:.5);return!(Number.isFinite(d)&&d>0)||a>=-d-i&&a<=n+d+i&&c<=l+d+i})},jn=(t,e,s)=>{if(t.host){const i=z(t,e,s.coordScale,s.cellCm,s.gridPitch).resolved;return i?{center:i.center,angle:i.angle,length:i.length}:null}const i=Number(t.x)*s.coordScale,o=Number(t.y)*s.coordScale,a=Number(t.angle),n=Number(t.length)*s.coordScale;return[i,o,a,n].every(Number.isFinite)&&n>0?{center:[i,o],angle:a,length:n}:null},qn=(t,e,s)=>{const i=t.angle*Math.PI/180,o=Math.cos(i),a=Math.sin(i),n=e.angle*Math.PI/180,r=Math.cos(n),h=Math.sin(n);if(Math.abs(o*h-a*r)>1e-6)return!1;const l=e.center[0]-t.center[0],c=e.center[1]-t.center[1];if(Math.abs(l*a-c*o)>s)return!1;const d=l*o+c*a;return Math.max(-t.length/2,d-e.length/2)({...t,a:[t.a[0]*e,t.a[1]*e],b:[t.b[0]*e,t.b[1]*e]}),Vn=(t,e,s,i)=>{const o=`~r-${(t=>{let e=2166136261;for(let s=0;s>>0;return e.toString(36)})(`${t}|${e[0].toFixed(9)},${e[1].toFixed(9)}|${s[0].toFixed(9)},${s[1].toFixed(9)}`)}`,a=`${t.slice(0,Math.max(1,64-o.length))}${o}`;let n=a;for(let t=2;i.has(n);t++){const e=`-${t}`;n=`${a.slice(0,64-e.length)}${e}`}return i.add(n),n},Kn=(t,e)=>{const s=[];for(const i of t){const t=s[s.length-1];!(t&&Math.abs(t.hi-i.lo)<=e&&t.safe===i.safe)||i.safe&&t.signature!==i.signature?s.push({lo:i.lo,hi:i.hi,a:i.a,b:i.b,safe:i.safe,signature:i.signature,roomIds:i.roomIds,finalCm:i.finalCm}):(t.hi=i.hi,t.b=i.b)}return s};function Jn(t,e,s,i,o){const a=s||[],n=Array.isArray(t?.partitions)?t.partitions:[],r=Array.isArray(t?.openings)?t.openings:[],h=Array.isArray(t?.room_drafts)?t.room_drafts:[],l={walls:a,partitions:n,openings:r,roomDrafts:h,partitionsReconciled:0,openingsRehosted:0,removedDrafts:0};if(!n.length&&!h.length)return l;const c=Math.max(2e-4*o.gridPitch,1e-9);let d=a,p=n,u=r;const _=A(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),m=((t,e,s,i)=>{const o=new Map(t.map(t=>[t.id,t])),a=new Set;for(const t of e){if(!o.get(t.id)||!Array.isArray(t.points)||t.points.length<2||t.segments.length!==t.points.length-1)continue;let e=!0;for(let o=0;o+10)){e=!1;break}const h=Math.hypot(n[0]-a[0],n[1]-a[1]);if(!(h>i)){e=!1;break}const l=s.flatMap(t=>{const e=t.cm>0?t.cm:N;if(t.open||"outer"!==t.kind&&"shared"!==t.kind||e+it.lo-e.lo||t.hi-e.hi);let c=0;for(const t of l){if(t.lo>c+i)break;if(c=Math.max(c,t.hi),c>=h-i)break}c!m.has(t.id));let f=0,b=0;const v=new Map(e.partitions.map(t=>[t.id,t]));for(const t of[...n].sort((t,e)=>t.id.localeCompare(e.id))){if(!p.some(e=>e.id===t.id))continue;if(!Wn(t))continue;const s=v.get(t.id);if(!(s&&Nn(s.a)&&Nn(s.b)&&Number.isFinite(s.cm)&&s.cm>0))continue;const[a,n]=Hn(s.a,s.b)<=0?[[s.a[0],s.a[1]],[s.b[0],s.b[1]]]:[[s.b[0],s.b[1]],[s.a[0],s.a[1]]],r=n[0]-a[0],h=n[1]-a[1],l=Math.hypot(r,h);if(!(l>c))continue;const _=r/l,g=h/l,y=A(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),$=y.flatMap(t=>{if(t.open||"outer"!==t.kind&&"shared"!==t.kind)return[];const e=En(a,n,t.a,t.b,c);return e?[{interval:t,range:e}]:[]});if(!$.length)continue;const w=u.filter(t=>"partition"===t.host?.kind&&t.host.id===s.id).map(t=>({opening:t,resolved:z(t,e.partitions,o.coordScale,o.cellCm,o.gridPitch).resolved}));if(w.some(t=>!t.resolved))continue;const k=[0,l];for(const{range:t}of $)k.push(t.lo,t.hi);const x=k.slice(),S=new Map;for(const{opening:t,resolved:e}of w){const s=On(e.center,a,_,g),i={lo:s-e.length/2,hi:s+e.length/2};S.set(t.id,i),x.push(Math.max(0,i.lo),Math.min(l,i.hi))}const M=[...new Set(x.filter(t=>Number.isFinite(t)&&t>=-c&&t<=l+c).map(t=>Math.max(0,Math.min(l,t)).toFixed(9)))].map(Number).sort((t,e)=>t-e),D=p.map(t=>Un(t,o.coordScale)).filter(t=>t.id!==s.id),C=e.room_drafts.filter(t=>!m.has(t.id)),P=[];for(let t=0;t+1c))continue;const r=Ln(a,_,g,i),h=Ln(a,_,g,n),l=$.filter(({range:t})=>t.lo<=i+c&&t.hi>=n-c).map(({interval:t})=>t),d=new Map;for(const t of l)d.set(t.roomId,t);const p=[...d.values()],u=new Set(p.map(t=>t.kind)),m=new Set(p.map(t=>t.cm>0?t.cm:N)),f=p[0]?.kind,b=[...d.keys()].sort(),v=1===u.size&&1===m.size&&("outer"===f&&1===b.length||"shared"===f&&2===b.length),y={id:s.id,a:r,b:h,cm:s.cm},w=D.some(t=>!!En(r,h,t.a,t.b,c)),k=C.some(t=>t.points.some((e,s)=>s+10?p[0].cm:N:0,I=v?Math.max(S,s.cm):s.cm;P.push({lo:i,hi:n,a:r,b:h,safe:x,signature:x?`${f}|${b.join(",")}|${I}`:"",roomIds:b,roomCm:S,finalCm:I})}if(!P.some(t=>t.safe))continue;const I=new Map;for(const{opening:t,resolved:e}of w){const i=S.get(t.id),a=k.some(t=>t>i.lo+c&&tBn(t,i,c)),r=new Set(n.filter(t=>t.safe).map(t=>t.signature));if(!a&&n.length>0&&n.every(t=>t.safe)&&1===r.size&&H(e,y,c)){I.set(t.id,{resolved:e,signature:[...r][0]});continue}const h=L(s,o.cellCm,o.gridPitch),l={lo:i.lo-h,hi:i.hi+h};for(const t of P)Bn(t,l,c)&&(t.safe=!1,t.signature="")}let T=!0;for(;T;){T=!1;for(const[t,e]of[...I.entries()]){const i=S.get(t),a=P.filter(t=>Bn(t,i,c));if(a.length>0&&a.every(t=>t.safe)&&a.every(t=>t.signature===e.signature))continue;I.delete(t);const n=L(s,o.cellCm,o.gridPitch),r={lo:i.lo-n,hi:i.hi+n};for(const t of P)Bn(t,r,c)&&(t.safe=!1,t.signature="");T=!0}}const R=Kn(P,c),j=R.filter(t=>t.safe),q=R.filter(t=>!t.safe);if(!j.length)continue;if(p.length-1+q.length>2e3)continue;let U=d;for(const t of j){const[e,i]=Hn(s.a,s.b)<=0?[t.a,t.b]:[t.b,t.a];U=O(U,e,i,t.finalCm,o.pitch,o.coordScale)}if(U=E(e.rooms,U,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),U.length>500)continue;const V=new Set(p.map(t=>t.id));V.delete(s.id);const K=q.map((t,e)=>{const i=[t.a[0]/o.coordScale,t.a[1]/o.coordScale],a=[t.b[0]/o.coordScale,t.b[1]/o.coordScale],n=0===e?s.id:Vn(s.id,i,a,V);return V.add(n),{id:n,a:i,b:a,cm:s.cm}}),J=p.findIndex(t=>t.id===s.id),Y=p.slice();Y.splice(J,1,...K);const X=Y.map(t=>Un(t,o.coordScale)),Z=new Map;let Q=!0;for(const{opening:t,resolved:e}of w){const s=I.get(t.id);if(s){const e=F(t,s.resolved,o.coordScale),{host:i,...a}=e;Z.set(t.id,a);continue}const i=S.get(t.id),a=K[q.findIndex(t=>t.lo<=i.lo+c&&t.hi>=i.hi-c)],n=a&&X.find(t=>t.id===a.id);if(!a||!n){Q=!1;break}const r=n.b[0]-n.a[0],h=n.b[1]-n.a[1],l=Math.hypot(r,h),d=On(e.center,n.a,r/l,h/l),p={...F(t,e,o.coordScale),host:{kind:"partition",id:a.id,t:d/l}};if(!B(p,X,o.coordScale,o.cellCm,o.gridPitch).resolved){Q=!1;break}Z.set(t.id,p)}if(!Q)continue;const tt=u.map(t=>Z.get(t.id)||t),et=[...I.keys()].map(t=>Z.get(t)),st=et.map(t=>jn(t,X,o));if(st.some(t=>!t))continue;let it=!1;for(let t=0;tt.id));for(const t of tt){if(ot.has(t.id))continue;const e=jn(t,X,o);e&&st.some(t=>qn(t,e,c))&&(it=!0)}if(it)continue;const at=W(e.rooms,U,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale);[...I.entries()].every(([t,e])=>{const s=Z.get(t),i=G(at,{x:s.x*o.coordScale,y:s.y*o.coordScale,angle:s.angle,length:s.length*o.coordScale},!0),a=[i.negative,i.positive].filter(t=>!!t?.full),n=e.signature.split("|")[1].split(","),r=new Set(a.map(t=>t.roomId));return a.length===n.length&&r.size===n.length&&n.every(t=>r.has(t))})&&(d=U,p=Y,u=tt,f+=j.length,b+=I.size)}return{walls:d,partitions:p,openings:u,roomDrafts:g,partitionsReconciled:f,openingsRehosted:b,removedDrafts:m.size}}const Yn=t=>JSON.parse(JSON.stringify(t)),Xn=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Zn=(t,e)=>e.length>0&&e.length<=35&&("space"===t?/^[a-z0-9_-]+$/.test(e):/^[A-Za-z0-9_-]+$/.test(e));function Qn(t,e){let s=String(e??"");const i=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),o=new RegExp(`^${i}_(.+)_([0-9a-f]{8})$`);let a=0;for(;a<16;a++){const t=o.exec(s);if(!t)return{root:s,layers:a,bounded:!1};s=t[1]}return{root:s,layers:a,bounded:o.test(s)}}const tr=(t,e,s)=>{const i=t.get(e)||[];i.push(s),t.set(e,i)};const er=1e-9;class sr extends Error{constructor(t,e=""){super(e?`${t}: ${e}`:t),this.reason=t,this.name="WallSegmentModelError"}}const ir=t=>JSON.parse(JSON.stringify(t)),or=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1])),ar=t=>[Number(t[0]),Number(t[1])],nr=t=>`${Number(t[0]).toFixed(12)},${Number(t[1]).toFixed(12)}`,rr=(t,e)=>{const s=nr(t),i=nr(e);return snr(t)<=nr(e)?[ar(t),ar(e)]:[ar(e),ar(t)],lr=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),cr=t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],s=[];for(let i=1;i{const s=new Set,i=t=>{const e=Number(t);if(!Number.isFinite(e))return;const i=e/I;Math.abs(i-Math.round(i))>=X&&s.add(e.toFixed(12))};for(const e of Array.isArray(t?.rooms)?t.rooms:[])for(const t of R(e)||[])i(t[0]),i(t[1]);for(const e of[t?.wall_segments,t?.walls])for(const t of Array.isArray(e)?e:[])i(t?.a?.[0]),i(t?.a?.[1]),i(t?.b?.[0]),i(t?.b?.[1]);for(const t of e)i(t?.[0]),i(t?.[1]);return s.size},pr=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;return a>1e-18?((t[0]-e[0])*i+(t[1]-e[1])*o)/a:0},ur=(t,e,s)=>{const i=Math.max(0,Math.min(1,pr(t,e,s)));return Math.hypot(t[0]-(e[0]+(s[0]-e[0])*i),t[1]-(e[1]+(s[1]-e[1])*i))},_r=(t,e,s,i,o=er)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(r<=o)return 0;const h=e=>Math.abs((e[0]-t[0])*n-(e[1]-t[1])*a)/r;if(h(s)>o||h(i)>o)return 0;const l=pr(s,t,e),c=pr(i,t,e);return Math.max(0,Math.min(1,Math.max(l,c))-Math.max(0,Math.min(l,c)))*r},mr=t=>{const e=(new TextEncoder).encode(t),s=8*e.length,i=64*Math.ceil((e.length+9)/64),o=new Uint8Array(i);o.set(e),o[e.length]=128;const a=new DataView(o.buffer);a.setUint32(i-8,Math.floor(s/4294967296),!1),a.setUint32(i-4,s>>>0,!1);const n=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),r=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),h=new Uint32Array(64),l=(t,e)=>t>>>e|t<<32-e;for(let t=0;t>>3,o=l(s,17)^l(s,19)^s>>>10;h[t]=h[t-16]+i+h[t-7]+o>>>0}let[e,s,i,o,c,d,p,u]=r;for(let t=0;t<64;t++){const a=u+(l(c,6)^l(c,11)^l(c,25))+(c&d^~c&p)+n[t]+h[t]>>>0,r=e&s^e&i^s&i;u=p,p=d,d=c,c=o+a>>>0,o=i,i=s,s=e,e=a+((l(e,2)^l(e,13)^l(e,22))+r>>>0)>>>0}r[0]=r[0]+e>>>0,r[1]=r[1]+s>>>0,r[2]=r[2]+i>>>0,r[3]=r[3]+o>>>0,r[4]=r[4]+c>>>0,r[5]=r[5]+d>>>0,r[6]=r[6]+p>>>0,r[7]=r[7]+u>>>0}const c=new Uint8Array(32),d=new DataView(c.buffer);for(let t=0;t{const e="abcdefghijklmnopqrstuvwxyz234567";let s=0,i=0,o="";for(const a of t)for(i=i<<8|a,s+=8;s>=5;)o+=e[i>>>s-5&31],s-=5;return s&&(o+=e[i<<5-s&31]),o},fr=(t,e,s,i,o="")=>{const[a,n]=hr(e,s),r=`${t}|${nr(a)}|${nr(n)}|${[...i].sort().join(",")}${o}`;return`wall-${gr(mr(r)).slice(0,20)}`};let br=0;const vr=t=>{for(let e=0;e<1e3;e++){const e=globalThis.crypto?.randomUUID?.(),s=`wall-${e||gr(mr(`${Date.now()}|${br++}|${Math.random()}`)).slice(0,26)}`;if(!t.has(s))return s}throw new sr("duplicate-id","id factory exhausted")},yr=t=>{const e=new Map;for(const s of Array.isArray(t?.wall_segments)?t.wall_segments:[])if(s&&"string"==typeof s.id&&or(s.a)&&or(s.b)){if(e.has(s.id))throw new sr("duplicate-id",s.id);e.set(s.id,s)}return e},$r=(t,e,s)=>{const i=(s,i)=>{const o=t[0]-s[0],a=t[1]-s[1];return Math.abs(e[0]-i[0]-o)<=er&&Math.abs(e[1]-i[1]-a)<=er?[o,a]:null};return i(s.a,s.b)||i(s.b,s.a)},wr=(t,e)=>{const s=Array.isArray(t.rooms)?t.rooms:[],i=K(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]),o=i.length?i:J(s,null,1,.04*I,!0),a=[];for(const t of s){const s=R(t),i=Array.isArray(t?.wall_ids)?t.wall_ids:[];if(s&&i.length===s.length)for(let t=0;t"partition"!==t.host?.kind&&[Number(t.x),Number(t.y)].every(Number.isFinite)&&Number.isFinite(Number(t.angle))&&Number(t.length)>0&&o.some(e=>ur([Number(t.x),Number(t.y)],[e[0],e[1]],[e[2],e[3]])<=.04*I)):[],h=r.map(t=>{const e=Number(t.angle)*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Number(t.length)/2,o=[Number(t.x),Number(t.y)];return{a:[o[0]-s[0]*i,o[1]-s[1]*i],b:[o[0]+s[0]*i,o[1]+s[1]*i]}}),l=(t,e)=>r.some(s=>{const i=[Number(s.x),Number(s.y)];if(!Z(t,e,Number(s.angle)))return!1;if(ur(i,t,e)>.02*I)return!1;const o=lr(t,e),a=pr(i,t,e)*o,n=Number(s.length)/2;return a+n>=-er&&a-n<=o+er}),c=new Map,d=[];for(const i of s){const r=String(i?.id||""),p=R(i);if(!r||!p||p.length<3)throw new sr("invalid-room",r);const u=Y(s,r,n,I,1,[...t.walls||[],...h]);if(!u||u.poly.length<3)throw new sr("invalid-room",r);const _=Array.isArray(i.wall_ids)?i.wall_ids:[],m=_.length===p.length;let g=null,f=m;if(f)for(let t=0;ter||Math.abs(i[1]-g[1])>er)){f=!1;break}g=i}const b=[];for(let t=0;t2)throw new sr("third-owner",n);const d=m?_[u.parent[t]]:void 0;if("string"==typeof d&&d){const o=e.get(d);f||!o||_r(s,i,o.a,o.b)>er?h.preferredIds.add(d):h.positionalIds.add(d),h.preferredCarriers.set(d,{a:ar(p[u.parent[t]]),b:ar(p[(u.parent[t]+1)%p.length])})}const g=u.parent[t];h.parentKeys.add(V(p[g],p[(g+1)%p.length],I));const v=[(s[0]+i[0])/2,(s[1]+i[1])/2],y=t=>t.some(t=>ur(v,[t[0],t[1]],[t[2],t[3]])<=.04*I);h.zeroWall||=y(a)||y(o)&&!l(s,i),b.push(n)}const v={...i};delete v.wall_ids,d.push({...v,poly:u.poly.map(t=>[t[0],t[1]]),wall_ids:b})}return{atoms:[...c.values()].sort((t,e)=>t.key.localeCompare(e.key)),rooms:d}},kr=(t,e,s,i,o)=>{const a=new Map([...s.values()].map(t=>[rr(t.a,t.b),t])),n=(t=>{const e=new Map;for(const s of t)"wall"===s.host?.kind&&e.set(s.host.id,(e.get(s.host.id)||0)+1);return e})(t.openings||[]),r=new Map;for(const t of e){const e=o?.get(t.key);if(e){const i=s.get(e);if(!i)throw new sr("duplicate-id",e);r.set(t,i);continue}if(t.preferredIds.size>1)throw new sr("duplicate-id",[...t.preferredIds].sort().join(","));const i=[...t.preferredIds][0],h=i?s.get(i):void 0;if(h){r.set(t,h);continue}const l=i?t.preferredCarriers.get(i):void 0;if(i&&l){r.set(t,{id:i,a:l.a,b:l.b,cm:0});continue}const c=a.get(t.key);if(c){r.set(t,c);continue}const d=[...s.values()].filter(e=>_r(t.a,t.b,e.a,e.b)>er),p=[...new Map(d.map(t=>[t.id,t])).values()];if(p.sort((t,e)=>(n.get(e.id)||0)-(n.get(t.id)||0)||lr(e.a,e.b)-lr(t.a,t.b)||t.id.localeCompare(e.id)),p[0])r.set(t,p[0]);else if(1===t.positionalIds.size){const e=s.get([...t.positionalIds][0]);e&&r.set(t,e)}}const h=new Map;for(const[t,e]of r){const s=h.get(e.id)||[];s.push(t),h.set(e.id,s)}for(const[t,e]of h){const i=s.get(t)||r.get(e[0]);if(!i)throw new sr("duplicate-id",t);const o=[(i.a[0]+i.b[0])/2,(i.a[1]+i.b[1])/2];e.sort((t,e)=>{const s=ur(o,t.a,t.b)<=er?0:1,a=ur(o,e.a,e.b)<=er?0:1;if(s!==a)return s-a;return(ur(i.a,t.a,t.b)<=er?0:1)-(ur(i.a,e.a,e.b)<=er?0:1)||t.key.localeCompare(e.key)}),e[0].id=t}const l=(t=>{const e=new Set;for(const s of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns])for(const t of Array.isArray(s)?s:[])"string"==typeof t?.id&&t.id&&e.add(t.id);for(const s of Array.isArray(t.room_drafts)?t.room_drafts:[])for(const t of Array.isArray(s?.segments)?s.segments:[])"string"==typeof t?.id&&t.id&&e.add(t.id);return e})(t);for(const t of e)if(t.id){if(l.has(t.id))throw new sr("duplicate-id",t.id);l.add(t.id)}const c=e.filter(t=>!t.id);if(i){const e=c.map(e=>{const[s,i]=hr(e.a,e.b),o=`${String(t.id||"")}|${nr(s)}|${nr(i)}|${[...e.owners].sort().join(",")}`,a=gr(mr(o));return{atom:e,seed:o,digest:a,base:`wall-${a.slice(0,20)}`}}).sort((t,e)=>t.digest.localeCompare(e.digest)||t.atom.key.localeCompare(e.atom.key)),s=new Map;for(const t of e){const e=s.get(t.digest);if(e&&e!==t.seed)throw new sr("duplicate-id",t.digest);s.set(t.digest,t.seed);let i=1,o=t.base;for(;l.has(o);)o=`${t.base}-${++i}`;t.atom.id=o,l.add(o)}}else for(const t of c)t.id=vr(l),l.add(t.id)},xr=(t,e)=>{if("partition"===t.host?.kind)return null;const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=e=>{if(!(Number(e.cm)>0))return!1;const i=pr(s,e.a,e.b),o=lr(e.a,e.b),a=Number(t.length)/2;return i>=-er&&i<=1+er&&ur(s,e.a,e.b)<=.02*I&&Z(e.a,e.b,Number(t.angle))&&Number.isFinite(a)&&a>=0&&i*o-a>=-er&&i*o+a<=o+er},o="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null,a=o&&i(o)?[o]:e.filter(i);if(1!==a.length)return null;const n=a[0];return{kind:"wall",id:n.id,t:Math.max(0,Math.min(1,pr(s,n.a,n.b)))}},Sr=(t,e)=>{const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=Number(t.length)/2,o=e.filter(e=>{if(!(Number(e.cm)>0))return!1;const o=pr(s,e.a,e.b),a=lr(e.a,e.b);return o>=-er&&o<=1+er&&ur(s,e.a,e.b)<=.02*I&&Z(e.a,e.b,Number(t.angle))&&Number.isFinite(i)&&i>=0&&o*a-i>=-er&&o*a+i<=a+er}),a=(e=>{if(!e.length)return null;const i="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null;return i||[...e].sort((t,e)=>ur(s,t.a,t.b)-ur(s,e.a,e.b)||Number(e.cm)-Number(t.cm)||(t.ide.id?1:0))[0]})(o);return a?{kind:"wall",id:a.id,t:Math.max(0,Math.min(1,pr(s,a.a,a.b)))}:null},Mr=(t,e,s)=>{const i=yr(t),{atoms:o,rooms:a}=wr(t,i);kr(t,o,i,e,s);const n=o.map(e=>{const s=e.id?i.get(e.id):void 0,o=((t,e,s)=>{if(e.zeroWall)return 0;const i=Array.isArray(t.walls)?t.walls:[],o=V(e.a,e.b,I),a=i.filter(t=>Number(t.cm)>0&&(!(t.key!==o&&!e.parentKeys.has(t.key))||!(!or(t.a)||!or(t.b))&&_r(e.a,e.b,t.a,t.b)>=lr(e.a,e.b)-er)).map(t=>Math.max(1,Math.min(100,Number(t.cm))));if(new Set(a.map(t=>t.toFixed(9))).size>1)throw new sr("thickness-conflict",e.key);if(a.length)return a[0];const n=Q(i,e.a,e.b,I,1);return n>0?n:Number(s?.cm)>0?Number(s.cm):0})(t,e,s);return{...s||{},id:e.id,a:[...e.a],b:[...e.b],cm:o}}),r=new Map(o.map(t=>[t.key,t.id]));for(const t of a)t.wall_ids=t.wall_ids.map(t=>r.get(t));t.rooms=a,t.wall_segments=n,t.walls=n.filter(t=>t.cm>0).map(t=>({key:V(t.a,t.b,I),cm:t.cm,a:[...t.a],b:[...t.b]})),t.walls.length||delete t.walls,delete t.open_spans;for(const e of t.rooms)delete e.open_to;return((t,e)=>{const s=new Set;for(const e of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns,t.wall_segments])for(const t of Array.isArray(e)?e:[])"string"==typeof t?.id&&t.id&&s.add(t.id);for(const i of Array.isArray(t.room_drafts)?t.room_drafts:[]){const o=Array.isArray(i.segments)?i.segments:[];for(let a=0;a{const i=Array.isArray(t.openings)?t.openings:[];for(const t of i){if("partition"===t.host?.kind)continue;if(!t.host&&!s){const s=xr(t,e);s&&(t.host=s);continue}const i=xr(t,e)??(s?Sr(t,e):null);if(i)t.host=i;else{if(!s)throw new sr("opening-host",t.id);delete t.host}}})(t,n,e),n.reduce((t,e)=>t+(i.has(e.id)?0:1),0)};function Dr(t,e={}){const s=JSON.stringify(t),i=ir(t);U(i);let o=0;const a=Number(i?.model_version||0)<9;for(const t of Array.isArray(i?.spaces)?i.spaces:[]){const s=e.lineageSpaceId===String(t?.id||"")?e.lineageHints:void 0;o+=Mr(t,a,s)}return i.model_version=9,U(i),{config:i,changed:s!==JSON.stringify(i),migratedSegments:o}}const Cr=t=>!!t&&"object"==typeof t&&!Array.isArray(t);function Pr(t,e){const s=(t,e)=>{if(Array.isArray(t)&&Array.isArray(e)){const i=new Map;for(const e of t)Cr(e)&&"string"==typeof e.id&&e.id&&i.set(e.id,e);const o=e.map((e,o)=>{if(Cr(e)&&"string"==typeof e.id&&e.id){const t=i.get(e.id);return t?s(t,e):ir(e)}return o[e,e in t?s(t[e],i):ir(i)]);for(const e of Object.keys(t))delete t[e];for(const[e,s]of i)t[e]=s;return t}return ir(e)};return s(t,e)}const Ir=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Tr=(t,e,s)=>Math.min(s,Math.max(e,t)),Rr=t=>_t({spaces:[t]})[0],zr=(t,e,s)=>{const i=s>0?s:1,o=t=>`${(t[0]/i).toFixed(12)},${(t[1]/i).toFixed(12)}`,a=o(t),n=o(e);return a0?n:1,h=Math.max(i*r*.02,1e-9),l=[];for(const e of t||[])for(const t of R(e)||[])l.push([t[0],t[1]]);const c=[];for(const t of s||[])Array.isArray(t)&&t.length>=4&&t.slice(0,4).every(Number.isFinite)&&c.push([t[0],t[1]],[t[2],t[3]]);const d=(t,e)=>e.some(e=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*h),p=new Map;for(const n of t||[]){if(!n?.id)continue;const u=mt(t,n.id,e,s,i,o,a,r);if(u)for(let t=0;t0&&_>0&&n===m&&_!==n))continue;const g=u.poly[i],f=u.poly[(i+1)%u.poly.length],b=Math.hypot(f[0]-g[0],f[1]-g[1]);if(!(b>h&&b<.5*a-1e-9*a))continue;if(d(g,c)||d(f,c))continue;if(d(g,l)&&d(f,l))continue;const v=zr(g,f,r),y=p.get(v);y?y.targets.add(n):p.set(v,{a:[g[0],g[1]],b:[f[0],f[1]],targets:new Set([n])})}}}let u=e.slice();const _=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*h,m=(t,e)=>{if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return!1;const s=[Number(t.a[0])*r,Number(t.a[1])*r],i=[Number(t.b[0])*r,Number(t.b[1])*r];return!![...s,...i].every(Number.isFinite)&&(_(s,e.a)&&_(i,e.b)||_(s,e.b)&&_(i,e.a))};for(const t of[...p.values()].filter(t=>1===t.targets.size).sort((t,e)=>zr(t.a,t.b,r).localeCompare(zr(e.a,e.b,r)))){const e=[...t.targets][0],s=u.filter(e=>m(e,t));if(new Set(s.map(t=>t.cm)).size>1)continue;let o=!1;u=u.map(s=>m(s,t)?(o=!0,s.cm===e?s:{...s,cm:e}):s),o||(u=O(u,t.a,t.b,e,i,r))}return u}const Ar=t=>{const e=[];for(const s of t||[]){const t=R(s);if(t?.length)for(let s=0;s"string"==typeof t?.id?t.id:"").filter(Boolean),c=new Set(l),d=new Map;for(const t of l){const e=Qn("space",t);e.layers>0&&Zn("space",e.root)&&tr(d,e.root,t)}const p=new Map,u=new Map,_=new Map,m=new Map;for(const t of r){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s=new Map;for(const i of Array.isArray(t.rooms)?t.rooms:[]){const t="string"==typeof i?.id?i.id:"";if(!t)continue;const o=Qn("room",t);o.layers>0&&Zn("room",o.root)&&tr(s,o.root,t),u.has(t)||u.set(t,e),_.has(t)||_.set(t,String(i.name||""));const a="string"==typeof i.area?i.area:"";if(a){const s=m.get(a)||[];s.push({spaceId:e,roomId:t}),m.set(a,s)}}p.set(e,s)}const g=new Set(u.keys()),f=new Map,b=new Map;for(const t of h)"string"==typeof t?.id&&t.id&&(!0===t.removed?b.set(t.id,t):f.set(t.id,t));const v={spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,positionsUnresolved:0,nestedRefsUnresolved:0,deadSpaceIds:[],orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0,removedPositions:[],liveMissingPositions:[],unverifiedPositions:[]},y=new Set,$=t=>c.has(t)?null:(()=>{const e=Qn("space",t).root;if(!Zn("space",e))return null;const s=d.get(e)||[];return 1===s.length?s[0]:null})(),w=(t,e)=>{const s=Qn("room",t).root;if(!Zn("room",s))return null;const i=p.get(e)?.get(s)||[];return 1===i.length?i[0]:null},k=t=>{const e=s.effectiveAreaByMarker?.[t]||"",i=e&&m.get(e)||[];return 1===i.length?i[0]:null},x=(t,e,s)=>{if("string"==typeof t.room_id&&t.room_id&&!g.has(t.room_id)){const i=e?w(t.room_id,e):null;i?(t.room_id=i,v.roomRefsRemapped++):s?(t.room_id=s.roomId,v.roomRefsRemapped++):!0!==t.removed&&delete t.room_id}const i=t.vacuum?.segment_map;if(i&&"object"==typeof i&&!Array.isArray(i))for(const t of Object.keys(i)){const s=i[t];if("string"!=typeof s||!s||g.has(s))continue;const o=e?w(s,e):null;o?(i[t]=o,v.roomRefsRemapped++):v.nestedRefsUnresolved++}};for(const t of h){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s="string"==typeof t.space?t.space:"",i=s&&!c.has(s),a=o[e],n="string"==typeof a?.s?a.s:"",r=n&&!c.has(n),h=!0===t.removed;let l=s&&c.has(s)?s:null,d=null,p=!1;if(i){const i=$(s);i?(t.space=i,l=i,p=!0,v.spaceRefsRemapped++):h||"virtual"===t.binding?h||(delete t.space,l=null,v.markersDetached++):(d=k(e),d?(t.space=d.spaceId,l=d.spaceId,v.spaceRefsRemapped++):(delete t.space,l=null,v.markersDetached++))}h&&!p||x(t,l,d),r&&p&&n===s&&l&&(o[e]={...a,s:l},v.positionsRemapped++,y.add(e))}for(const t of Object.keys(o)){if(y.has(t))continue;const e=o[t],s="string"==typeof e?.s?e.s:"";if(!s||c.has(s))continue;const i=$(s);if(i){if(t.startsWith("rl_")){const s=t.slice(3),a=w(s,i);if(a){const s=`rl_${a}`;Xn(o,s)||(o[s]={...e,s:i}),delete o[t],v.roomRefsRemapped++,v.positionsRemapped++;continue}}o[t]={...e,s:i},v.positionsRemapped++}}const S=s.ownerRoster,M=!0===S?.authoritative,D=new Set(S?.deviceIds||[]),C=new Set(S?.entityIds||[]),P=new Set(Array.isArray(i.settings?.known_devices)?i.settings.known_devices.filter(t=>"string"==typeof t):[]),I=(t,e="")=>{const s=String(S?.names?.[t]||e||"").trim();return s&&s!==t?s:""},T=(t,e,s,i="",o)=>({id:t,spaceId:e,kind:s,name:I(t,i),...o?{reason:o}:{}}),R=t=>{"room_label"===t?v.orphanRoomLabelsRemoved++:"group"===t?v.orphanGroupPositionsRemoved++:v.orphanDevicePositionsRemoved++},z=new Set;for(const[t,e]of Object.entries(o)){const i="string"==typeof e?.s?e.s:"";if(!i||c.has(i))continue;let a,n;const r=f.get(t),h=b.get(t);if(r)a=T(t,i,"device",String(r.name||"")),n="live";else if(h)a=T(t,i,"device",String(h.name||"")),n="absent";else if(t.startsWith("rl_")){const e=t.slice(3);a=T(t,i,"room_label",_.get(e)||""),n=g.has(e)?"live":"absent"}else if(t.startsWith("lg_")){const e=t.slice(3);a=T(t,i,"group"),n=C.has(e)?"live":M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")}else D.has(t)?(a=T(t,i,"device"),n="live"):P.has(t)?(a=T(t,i,"device"),n=M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")):(a=T(t,i,"unknown","","unknown_owner"),n="unverified");if("absent"!==n){if("live"===n){if(v.liveMissingPositions.push(a),!0===s.removeLiveMissingPositions){delete o[t],v.liveMissingPositionsRemoved++,R(a.kind),v.removedPositions.push(a);continue}}else v.unverifiedPositions.push(a);v.positionsUnresolved++,z.add(i)}else delete o[t],R(a.kind),v.removedPositions.push(a)}return v.deadSpaceIds=[...z].sort((t,e)=>t.localeCompare(e)),{config:i,layout:o,report:v,changed:JSON.stringify(i)!==a||JSON.stringify(o)!==n}}(t,e,s),n=a.config,r=tt(n,a.layout);U(n),et(a.layout);const h=JSON.stringify(t||{}),l=JSON.stringify(e||{}),c=Number.isInteger(Number(n.model_version))?Number(n.model_version):0,d=(t=>{let e=0,s=0,i=0;for(const s of t.markers||[]){if("ripple"===s.display&&(s.display="icon_ripple",e++),Array.isArray(s.controls)){const t="string"==typeof s.binding&&s.binding.startsWith("entity:")?s.binding.slice(7):"",i=t?s.controls.filter(e=>e!==t):s.controls;JSON.stringify(i)!==JSON.stringify(s.controls)&&(s.controls=i.length?i:null,e++)}const t=s.vacuum;t&&Ir(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const o of t.spaces||[]){const t=o.settings;"glow"===t?.fill_mode&&("boolean"!=typeof t.glow_enabled&&(t.glow_enabled=!0),t.fill_mode="none",e++,s++);for(const t of o.rooms||[]){const s=t.settings;"glow"===s?.fill_mode&&("boolean"!=typeof s.glow&&(s.glow=!0),delete s.fill_mode,e++,i++)}if(Ir(o,"segments")&&(delete o.segments,e++),Ir(o,"plan_scale")){const t=Number(o.plan_scale);Number.isFinite(t)&&t>=ht&&t<=lt&&(Ir(o,"plan_scale_x")||(o.plan_scale_x=t),Ir(o,"plan_scale_y")||(o.plan_scale_y=t),delete o.plan_scale,e++)}const a=Number(o.cell_cm);Ir(o,"cell_cm")&&(!Number.isFinite(a)||a<.1||a>1e3)&&(o.cell_cm=Number.isFinite(a)&&a>0?Tr(a,.1,1e3):5,e++);const n=Number(o.cell_cm),r=Number.isFinite(n)&&n>0?n:5;for(const t of o.wall_columns||[])if("circle"===t.shape)Ir(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&Ir(t,"angle")){const s=((Number(t.angle)||0)%90+90)%90;t.angle!==s&&(t.angle=s,e++)}for(const t of o.decor||[]){if(Ir(t,"width")){const s=Number(t.width);(Ir(t,"width_cm")||Number.isFinite(s))&&(Ir(t,"width_cm")||(t.width_cm=Number(Tr(s/nt*r,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(Ir(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),Ir(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number(Tr(ct*dt(t)/nt*r,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(Ir(t,"scale")||Ir(t,"size"))&&(delete t.scale,delete t.size,e++);let s=String(t.text??"");const i=[...s.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!pt(t[1]));if(!(Ir(t,"entity")||Ir(t,"attr")||Ir(t,"unit")))continue;const o=String(t.unit??"").trim(),a="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||o?"":ut(t.entity,a);if(i||n){if(n){const e=s.indexOf("{}");s=e>=0?s.slice(0,e)+n+s.slice(e+2):`${s}${s?" ":""}${n}`,t.text=s}delete t.entity,delete t.attr,delete t.unit,e++}}}return{total:e,glowSpaces:s,glowRooms:i}})(n);let p=d.total;const u=(_=n.spaces||[],JSON.parse(JSON.stringify(_)));var _;const m=Mn(n.spaces||[],a.layout);let g=0,f=0,b=0,v="";const y=m.spaces.map(t=>{const e=st(t);g+=e.report.wallsStraightened,f+=e.report.wallsStraightenSkipped;const s=Number(t?.cell_cm)>0?Number(t.cell_cm):5,i=e.report.maxStraightenShift/I*s;return i>b&&(b=i,v=String(t?.id||"")),e.space}),$=g?Mn(y,m.layout):{...m,spaces:y};n.spaces=$.spaces;const w=g?{...m.report,moved:m.report.moved+$.report.moved,coordsCanonicalized:m.report.coordsCanonicalized+$.report.coordsCanonicalized,maxShift:Math.max(m.report.maxShift,$.report.maxShift),maxShiftCm:Math.max(m.report.maxShiftCm,$.report.maxShiftCm),maxSpace:$.report.maxShiftCm>m.report.maxShiftCm?$.report.maxSpace:m.report.maxSpace,rotated:m.report.rotated+$.report.rotated,removedDrafts:m.report.removedDrafts+$.report.removedDrafts}:{...m.report};let k=0,x=0,S=0,M=0,D=0,C=0,P=0,T=0;for(let t=0;t0===Number(t.cm)),walls:e.walls||[]}),a=Rr(e),r=Rr(s);if(!a||!r)continue;const h=Ar(a.rooms),l=Ar(r.rooms),c=.02*nt,d=(e.wall_segments||[]).filter(t=>0===Number(t.cm)).map(t=>({...t,key:V(t.a,t.b,I),cm:0})),p=new Set(d.map(t=>t.key)),_=it(e,a.rooms,ot,c);x+=_.length;for(const t of _){const e=[t[0]/ot,t[1]/ot],s=[t[2]/ot,t[3]/ot],i=V(e,s,I);p.has(i)||(p.add(i),d.push({key:i,a:e,b:s,cm:0}))}const m=d.length,g=at(d,h,l,I,ot),f=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(!(a>0))return Math.hypot(t[0]-e[0],t[1]-e[1]);const n=Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))},b=g.filter(t=>{if(!Array.isArray(t.a)||!Array.isArray(t.b))return!1;const e=t.a.map(t=>t*ot),s=t.b.map(t=>t*ot);return l.some(([t,i])=>f(e,t,i)<=4*c&&f(s,t,i)<=4*c)});S+=Math.max(0,m-b.length);const v=(s.wall_segments||[]).filter(t=>Number(t.cm)>0);s.wall_segments=[...v,...b.map(({key:t,...e})=>e)];const y=b.map(t=>[t.a[0]*ot,t.a[1]*ot,t.b[0]*ot,t.b[1]*ot]),$=Array.isArray(e.walls)?e.walls.length:0;let w=at(e.walls,h,l,I,ot);w=Fr(r.rooms,w,y,I,Number(s.cell_cm)>0?Number(s.cell_cm):5,nt,ot),w=E(r.rooms,w,y,I,Number(s.cell_cm)>0?Number(s.cell_cm):5,nt,ot),w=rt(w,s.rooms||[],I,1,y.map(t=>[t[0]/ot,t[1]/ot,t[2]/ot,t[3]/ot])),k+=Math.max(0,$-w.length),w.length?s.walls=w:delete s.walls;const R=zn(s.partitions||[],{pitch:I,geometry:An(s)});R.merged&&(M+=R.merged,s.partitions=R.partitions,Fn(s.openings,s.partitions,R.openingMoves,{coordScale:ot,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:nt}));const z=Rr(s);if(z){const t=o(s,z,s.walls||[],y,{pitch:I,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:nt,coordScale:ot});t.partitionsReconciled&&(D+=t.partitionsReconciled,C+=t.openingsRehosted,t.partitions.length?s.partitions=t.partitions:delete s.partitions,t.openings.length?s.openings=t.openings:delete s.openings,t.walls.length?s.walls=t.walls:delete s.walls),t.removedDrafts&&(P+=t.removedDrafts,t.roomDrafts.length?s.room_drafts=t.roomDrafts:delete s.room_drafts)}JSON.stringify({zero:(s.wall_segments||[]).filter(t=>0===Number(t.cm)),walls:s.walls||[]})!==i&&T++}let R=0;c<=9&&(R=function(t){const e=Dr(t);return Pr(t,e.config),{...e,config:t}}(n).migratedSegments);const z=U(n),F=et($.layout),A=JSON.stringify(z)!==h||JSON.stringify(F)!==l;c<9&&A&&(z.model_version=9);const N=JSON.stringify(z)!==h||JSON.stringify(F)!==l,H=Number.isInteger(Number(z.model_version))?Number(z.model_version):c,L=N?w:{...w,moved:0,coordsCanonicalized:0,maxShift:0,maxShiftCm:0,maxSpace:"",rotated:0,removedDrafts:0},O=N?a.report:{...a.report,spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0};return{config:z,layout:F,report:{...L,modelFrom:c,modelTo:H,migrated:N?p:0,glowSpacesMigrated:N?d.glowSpaces:0,glowRoomsMigrated:N?d.glowRooms:0,canonicalized:N?T:0,wallSegmentsMigrated:N?R:0,legacyZeroWallsMigrated:N?x:0,wallsMerged:N?k:0,spansMerged:N?S:0,partitionsMerged:N?M:0,partitionsReconciled:N?D:0,openingsRehosted:N?C:0,redundantDraftsRemoved:N?P:0,wallsStraightened:N?g:0,wallsStraightenSkipped:f,maxStraightenShiftCm:N?b:0,maxStraightenSpace:N?v:"",latticeCoordinatesCanonicalized:N?r.canonicalized:0,latticeCoordinatesFar:N?r.far:0,latticeMaxShift:N?r.maxShift:0,latticeMaxShiftCm:N?r.maxShiftCm:0,latticeSpaces:N?r.spaces:[],...O},changed:N}}const Hr=(t,e)=>t[0]*e[1]-t[1]*e[0],Lr=(t,e)=>t[0]*e[0]+t[1]*e[1],Or=(t,e)=>[t[0]-e[0],t[1]-e[1]],Er=(t,e,s)=>[t[0]+e[0]*s,t[1]+e[1]*s];function Br(t,e,s){return Math.abs(Hr(t,e))<=s}function Wr(t,e,s){return Math.abs(Hr(Or(t,e),s))}function Gr(t,e,s,i,o){const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o&&s>0))return[];const h=[a/r,n/r];return function(t,e,s){const i=e.map(([e,s])=>[Math.max(0,Math.min(e,s)),Math.min(t,Math.max(e,s))]).filter(([t,e])=>e>t+s).sort((t,e)=>t[0]-e[0]||t[1]-e[1]),o=[];for(const t of i){const e=o[o.length-1];!e||t[0]>e[1]+s?o.push([...t]):e[1]=Math.max(e[1],t[1])}const a=[];let n=0;for(const[t,e]of o)t>n+s&&a.push([n,t]),n=Math.max(n,e);return t>n+s&&a.push([n,t]),a}(r,i.flatMap(e=>{const i=e.b[0]-e.a[0],a=e.b[1]-e.a[1],n=Math.hypot(i,a);if(!(n>o&&Br(h,[i/n,a/n],1e-6)))return[];if(Wr(e.a,t,h)>1.1*Math.max(s,o)||Wr(e.b,t,h)>1.1*Math.max(s,o))return[];return[[Lr(Or(e.a,t),h),Lr(Or(e.b,t),h)]]}),o).flatMap(([e,i])=>{const o=bt({a:Er(t,h,e),b:Er(t,h,i),halfDepth:s});return o?[o]:[]})}function jr(t){const e=t.coordScale??1,s=Math.max(t.epsilon??2e-4*t.gridPitch,1e-9),i=[];for(const s of t.rooms||[]){if(!s?.id)continue;const o=mt(t.rooms,s.id,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e);if(!o||o.poly.length<3)continue;const a=gt(o.poly,o.offsets);!a||a.length<3||i.push({roomId:s.id,profile:o,inner:a})}i.sort((t,e)=>t.roomId.localeCompare(e.roomId));const o=(t.roomOpenings||[]).map(t=>{const e=t.angle*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Math.max(0,t.length)/2;return{a:Er([t.x,t.y],s,-i),b:Er([t.x,t.y],s,i)}}),a=[];for(const i of ft(t.rooms,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e))for(const t of Gr(i.a,i.b,i.depthUnits/2,o,s))a.push({body:t});const n=new Map;for(const e of t.partitionCuts||[]){const t=n.get(e.hostId)||[];t.push(e),n.set(e.hostId,t)}for(const e of t.partitions||[]){const i=j(e.cm,t.cellCm,t.gridPitch)/2;for(const t of Gr(e.a,e.b,i,n.get(e.id)||[],s))a.push({body:t,partitionId:e.id})}return{rooms:i,boundaries:a,epsilon:s}}function qr(t,e,s,i){const o=t.profile.offsets[e];if(!(o>0))return null;const a=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return function(t){let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(t.profile.poly,e),n=Er(s.center,a,o),r=[];for(let e=0;ei&&Br(s.axis,[h[0]/l,h[1]/l],1e-6)))continue;if(Wr(o,n,s.axis)>i||Wr(a,n,s.axis)>i)continue;const c=Lr(Or(o,n),s.axis),d=Lr(Or(a,n),s.axis);r.push([Math.min(c,d),Math.max(c,d)])}const h=function(t,e){t.sort((t,e)=>t[0]-e[0]||t[1]-e[1]);const s=[];for(const i of t){const t=s[s.length-1];!t||i[0]>t[1]+e?s.push([...i]):t[1]=Math.max(t[1],i[1])}return s}(r,i).find(([t,e])=>t<=i&&e>=-i);if(!h)return null;const[l,c]=h,d=Er(n,s.axis,-s.half),p=Er(n,s.axis,s.half),u=Math.max(0,-s.half-l),_=Math.max(0,c-s.half),m=u>0?Er(n,s.axis,l):d,g=_>0?Er(n,s.axis,c):p,f=Lr(a,s.normal)>=0?1:-1,b=(e,i,o)=>({from:e,to:i,label:[(e[0]+i[0])/2,(e[1]+i[1])/2],axis:s.axis,labelNormal:a,distance:o,roomId:t.roomId,roomSide:f,source:"room-face"});return{left:b(d,m,u),right:b(p,g,_)}}function Ur(t){const e=Er(t.center,t.axis,-t.half),s=Er(t.center,t.axis,t.half),i=Er(t.center,t.axis,Math.min(-t.half,t.targetLo)),o=Er(t.center,t.axis,Math.max(t.half,t.targetHi)),a=(e,s)=>({from:e,to:s,label:[(e[0]+s[0])/2,(e[1]+s[1])/2],axis:t.axis,labelNormal:t.normal,distance:Math.hypot(s[0]-e[0],s[1]-e[1]),source:"host-end"});return[a(e,i),a(s,o)]}function Vr(t,e,s,i){const o=Or(s,e),a=Lr(o,o);if(!(a>i*i))return Math.hypot(...Or(t,e))<=i;const n=Lr(Or(t,e),o)/a;if(n<-i||n>1+i)return!1;const r=Er(e,o,Math.max(0,Math.min(1,n)));return Math.hypot(...Or(t,r))<=i}function Kr(t,e,s){let i=!1;for(let o=0,a=e.length-1;ot[1]!=l>t[1]&&t[0]<(h-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}function Jr(t,e,s,i,o,a){const n=Or(i,s),r=Hr(e,n);if(Math.abs(r)<=a)return null;const h=Or(s,t),l=Hr(h,n)/r,c=Hr(h,e)/r;return l<-a||l>o+a||c<-a||c>1+a?null:Math.max(0,Math.min(o,l))}function Yr(t,e,s){const i=Ur(s),o="partition"===t.host?.kind?t.host.id:void 0;return[{base:i[0],direction:[-s.axis[0],-s.axis[1]]},{base:i[1],direction:s.axis}].map(({base:t,direction:s})=>{const i=t.distance,a=function(t,e,s,i,o,a){let n=null;for(const r of i)if(!o||r.partitionId!==o){if(Kr(t,r.body,a))return 0;for(let i=0;iLr(Or(t,o),s));return{center:o,axis:s,normal:i,half:Math.max(0,t.renderedLength)/2,targetLo:Math.min(...a),targetHi:Math.max(...a)}}(t),i=e.rooms.flatMap(t=>{const i=function(t,e,s){const{profile:i}=t;let o=null;for(let t=0;t0))continue;const a=i.poly[t],n=i.poly[(t+1)%i.poly.length],r=Or(n,a),h=Math.hypot(r[0],r[1]);if(!(h>s&&Br(e.axis,[r[0]/h,r[1]/h],1e-6)))continue;if(Wr(a,e.center,e.axis)>s||Wr(n,e.center,e.axis)>s)continue;const l=Lr(Or(a,e.center),e.axis),c=Lr(Or(n,e.center),e.axis),d=Math.min(l,c),p=Math.max(l,c);if(d>-e.half+s||p0?d:p<0?-p:0);(!o||u2)return Ur(s);if(i.length){const t=i.map(({room:t,edge:i})=>qr(t,i,s,e.epsilon));if(t.some(t=>!t))return Ur(s);const o=t;return[...o.map(t=>t.left),...o.map(t=>t.right)]}return Yr(t,e,s)}const Zr="1.69.0-beta.4",Qr={badge:"display.badge",icon_ripple:"display.icon_ripple",value:"display.value",static_icon:"display.static_icon"},th={badge:"marker.display_hint_badge",icon_ripple:"marker.display_hint_icon_ripple",value:"marker.display_hint_value",static_icon:"marker.display_hint_static_icon"},eh=2e3,sh=1e3,ih=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const s=Number(e);return Number.isFinite(s)?s:null},oh=(t,e,s,i)=>{for(t.delete(e),t.set(e,s);t.size>i;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}},ah=1e3,nh=2e3,rh=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}},hh="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";class lh{constructor(t){this.host=t,this._junctionBaselineCache=new WeakMap,this._undoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.undo();t&&(this._applyGeometryState(t.before,!0)?this.host._showToast(this.host._t("history.undone",{name:t.name})):this.host._geometryHistory.clear())},this._redoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.redo();t&&(this._applyGeometryState(t.after,!0)?this.host._showToast(this.host._t("history.redone",{name:t.name})):this.host._geometryHistory.clear())},this._savePhysicalDialog=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=ih(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?vt:100,"column"===t.kind?1:0);const o=this.host._imperial?2.54*i:i,a="column"===t.kind?vt:100,n="column"===t.kind?1:0;if(!Number.isFinite(o)||oa)return void this._showPhysicalRange(a,n);if("column"===t.kind){const e=s.wall_columns.find(e=>e.id===t.id);if(!e)return;const i=ih(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this.host._showToast(this.host._t("toast.physical_angle"));const a="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:o}:{id:t.id,shape:"square",center:e.center,cm:o,angle:i};if(s.wall_columns.some(e=>e.id!==t.id&&yt(e,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"))}const r=this._geometrySnapshot();if("partition"===t.kind){const s=(e.partitions||[]).find(e=>e.id===t.id);if(s&&0===o&&$t(e.openings,{kind:"partition",id:t.id}))return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"));s&&(s.cm=o)}else if("column"===t.kind){const s=(e.wall_columns||[]).find(e=>e.id===t.id);s&&(s.cm=wt(o),s.shape="circle"===t.shape?"circle":"square","square"===s.shape?s.angle=ih(t.angle||"0"):delete s.angle)}else{const s=(e.room_drafts||[]).find(e=>e.id===t.id);s?.segments?.[t.segment||0]&&(s.segments[t.segment||0].cm=o)}const h=this._commitPhysicalGeometry(this.host._t("history.physical_edit"),r);this.host._physicalDialog=null,h||this.host.requestUpdate()},this._deletePhysicalSelection=()=>{const t=this.host._physicalSel,e=this.host._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void this._deleteDraftWhole();const s=this._geometrySnapshot();if("partition"===t.kind){const s=(e.openings||[]).filter(e=>"partition"===e.host?.kind&&e.host.id===t.id).sort((t,e)=>(t.host?.t||0)-(e.host?.t||0));if(s.length)return void(this.host._partitionDeleteDialog={id:t.id,openings:s.map(t=>JSON.parse(JSON.stringify(t)))})}const i="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[i]=(e[i]||[]).filter(e=>e.id!==t.id),e[i].length||delete e[i],this.host._activeDraftId===t.id&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._confirmPartitionDelete=()=>{const t=this.host._partitionDeleteDialog,e=this.host._curSpaceCfg;if(!t||!e)return;const s=this._geometrySnapshot();e.partitions=(e.partitions||[]).filter(e=>e.id!==t.id),e.partitions.length||delete e.partitions,e.openings=(e.openings||[]).filter(e=>"partition"!==e.host?.kind||e.host.id!==t.id),e.openings.length||delete e.openings,this.host._partitionDeleteDialog=null,this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftWhole=()=>{const t="draft"===this.host._physicalDialog?.kind?this.host._physicalDialog.id:"draft"===this.host._physicalSel?.kind?this.host._physicalSel.id:null,e=this.host._curSpaceCfg;if(!t||!e||!confirm(this.host._t("confirm.delete_draft")))return;const s=this._geometrySnapshot();e.room_drafts=(e.room_drafts||[]).filter(e=>e.id!==t),e.room_drafts.length||delete e.room_drafts,this.host._activeDraftId===t&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftSegment=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg;if(!t||"draft"!==t.kind||!e)return;if(!confirm(this.host._t("confirm.delete_draft_segment")))return;const s=(e.room_drafts||[]).findIndex(e=>e.id===t.id);if(s<0)return;const i=e.room_drafts[s],o=Math.max(0,Math.min(i.segments.length-1,t.segment||0)),a=[],n=i.points.slice(0,o+1),r=i.points.slice(o+1);if(n.length>=2&&a.push({id:i.id,points:n,segments:i.segments.slice(0,o)}),r.length>=2&&a.push({id:a.length?`${i.id}-${Date.now().toString(36)}`:i.id,points:r,segments:i.segments.slice(o+1)}),2===a.length&&e.room_drafts.length>=200)return void this.host._showToast(this.host._t("toast.physical_limit"));const h=this._geometrySnapshot();e.room_drafts.splice(s,1,...a),e.room_drafts.length||delete e.room_drafts,this.host._activeDraftId===i.id&&this._cancelPath(),this.host._physicalDialog=null,this.host._physicalSel=a.length?{kind:"draft",id:a[0].id}:null,this._commitPhysicalGeometry(this.host._t("history.draft_segment_delete"),h)},this._furnShiftListener=t=>{if("Shift"!==t.key)return;const e=this.host._furnPreviewInput;if(!e)return;const s="keydown"===t.type;e.free!==s&&(this.host._furnPreviewInput={...e,free:s},this.host.requestUpdate())},this._furnShiftAttached=!1,this._confirmRoomDelete=t=>{const e=this.host._roomDeleteDialog,s=this.host._curSpaceCfg,i=this.host._spaceModel();if(!e||!s||!i)return;const o=i.rooms.find(t=>t.id===e.roomId);if(!o)return void(this.host._roomDeleteDialog=null);const a=this.host._openCuts(),n=rn(A(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,ah).filter(t=>t.roomId===o.id),i.partitions,this.host._openingsR.map(t=>({...t,x:t.rx,y:t.ry})),.02*this.host._gridPitch),r=new Set(n.materialize.filter(t=>!t.reusePartitionId).map(t=>t.interval.key)).size;if(t&&(s.partitions||[]).length+r>nh)return void this.host._showToast(this.host._t("toast.physical_limit"));const h=this._geometrySnapshot(),l=kt(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,ah);if(t){s.partitions||=[];const t=new Map,e=new Map,i=t=>{const e=(s.wall_segments||[]).find(e=>{const s=[Number(e.a?.[0])*ah,Number(e.a?.[1])*ah],i=[Number(e.b?.[0])*ah,Number(e.b?.[1])*ah];return this._samePt(t.a,s)&&this._samePt(t.b,i)||this._samePt(t.a,i)&&this._samePt(t.b,s)});return"string"==typeof e?.id&&e.id?e.id:null},o=Date.now().toString(36);for(const a of n.materialize){let n=a.reusePartitionId?s.partitions.find(t=>t.id===a.reusePartitionId):e.get(a.interval.key);n||(n={id:i(a.interval)||`partition-room-${o}-${e.size}`,a:[a.interval.a[0]/ah,a.interval.a[1]/ah],b:[a.interval.b[0]/ah,a.interval.b[1]/ah],cm:a.interval.cm},s.partitions.push(n),e.set(a.interval.key,n)),t.set(a.interval.key,n)}for(const e of s.openings||[]){const s=n.openingIntervals.get(e.id),i=s?t.get(s):null,o=this.host._openingsR.find(t=>t.id===e.id);if(!i||!o)continue;const a={a:[i.a[0]*ah,i.a[1]*ah],b:[i.b[0]*ah,i.b[1]*ah]};e.host={kind:"partition",id:i.id,t:hn([o.rx,o.ry],a)}}}else if(n.removeOpeningIds.length){const t=new Set(n.removeOpeningIds);s.openings=(s.openings||[]).filter(e=>!t.has(e.id)),s.openings.length||delete s.openings}s.rooms=s.rooms.filter(t=>t.id!==o.id),this.host._cfgEpoch++;const c=this.host._normalizeWalls(l,this.host._openCuts());c.length?s.walls=c:delete s.walls,this.host._roomDeleteDialog=null,this._commitPhysicalGeometry(this.host._t(t?"history.delete_room_keep_walls":"history.delete_room_with_walls"),h),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate()},this._rebindPartitionOpening=()=>{const t=this.host._openingDialog;if(!t?.id)return;const e=t.id;this._activateMarkupTool("opening"),"opening"===this.host._tool&&(this.host._openingRebindId=e,this.host._openingPreset={type:t.type,lengthCm:t.lengthCm,flipH:t.flipH,flipV:t.flipV,revision:++this.host._openingPresetRevision},this.host._openingDialog=null,this.host._openingHoverCandidate=null)},this._deviceInboxTabKey=t=>{if("ArrowLeft"!==t.key&&"ArrowRight"!==t.key)return;const e=this.host._deviceInbox;if(!e)return;const s=["on_plan","available","hidden","readd"],i="ArrowRight"===t.key?1:-1,o=(s.indexOf(e.tab)+i+s.length)%s.length;this.host._deviceInbox={...e,tab:s[o],limit:100,onlyNew:!1},t.preventDefault()},this._keepClosedAsPartitions=()=>{if(this.host._wallFaceBatch)return void this._decideWallFace(!1);if(!this.host._contourClosed||this.host._pendingSplit||!this.host._curSpaceCfg)return;const t=this.host._curSpaceCfg,e=this.host._path.slice(0,-1);if((t.partitions||[]).length+e.length>nh)return void this.host._showToast(this.host._t("toast.physical_limit"));const s=this._geometrySnapshot(),i=za(e.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,N);t.partitions||=[];const o=Date.now().toString(36),a=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,n=this._draftSegmentsForPath(this.host._path,a,i);for(let s=0;st.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.contour_to_partitions"),s),this.host._roomDialog=!1,this.host._path=[],delete this.host._resumeDraftBySpace[this.host._space],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}},this._openSettingsDialog=()=>{if(!this.host._norm)return;const t=this.host._glowRadiusCm,e=this.host._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this.host._settingsDialog={colors:JSON.parse(JSON.stringify(this.host._fillColors)),glowRadius:e,bgColor:Dt(this.host._settings,{bgColor:null})||null,northDeg:Mt(this.host._settings,{}),bgMode:St(this.host._settings,{}),sunRays:xt(this.host._settings,{}),busy:!1}},this._openAlignDialog=()=>this._previewAlignDialog(!1),this._toggleOptimizeLivePositions=()=>{const t=this.host._alignDialog;t&&!t.busy&&t.report.liveMissingPositions.length&&this._previewAlignDialog(!t.removeLiveMissingPositions)},this._openBackupExport=()=>{this.host._settingsDialog=null,this.host._backupExportDialog={kind:"full",planOnly:!1,busy:!1,error:""}},this._decorStylePersistTimer=null,this._openRulesDialog=()=>{if(!this.host._norm)return;const t=this.host._settings.icon_rules,e=(t&&t.length?t:Ct).map(t=>({...t}));this.host._rulesDialog={rules:e,test:"",busy:!1}},t._editorSecondary=new fn({root:()=>t.renderRoot,requestUpdate:()=>t.requestUpdate(),updateComplete:()=>t.updateComplete,clearTip:()=>{t._tip=null}}),t._resize=new Go}_help(t){const e=`${t}.aria`,s=d(this.host.hass,this.host._config?.language);return Pt(s,t)&&Pt(s,e)?o``:a}_setMode(t,e=!0){this.host._endTabDrag(),this._clearFurniturePreview(),this.host._pendingNavMode=null;const s=this.host._spaceModel();if(!s)return this.host._cancelModeTransition(!1),void(this.host._mode="view");if(this.host._kiosk&&"view"!==t)return;if(this.host._mode===t){if(!e&&"view"===t&&this.host._modeTransitionBusy){const e=this.host._currentModeVisual(t),s=this.host._modeTransitionTargetZoom,i=this.host._modeTransitionTargetCenterX,o=this.host._modeTransitionTargetCenterY;this.host._cancelModeTransition(!1),this.host._commitViewModeAtomic(e,s,i,o)}return}if(this.host._bootSoftCancel(),("plan"===t||"decor"===t)&&!this.host._norm)return void this.host._showToast(this.host._t("toast.markup_needs_server"));if(this.host._wallFaceBatch&&this._roomDialogCancel(),"plan"===this.host._mode&&"draw"===this.host._tool&&!this._finishWallChain())return;this.host._clearTransientHover(!0),this.host._cancelDevicePressFeedback();const i=this.host._mode,o=this.host._effectiveProjection(),a=this.host._currentModeVisual(i),n=this.host._modeTransitionBusy,r=a&&"view"!==i&&"view"!==t?{...a,toolbarContentOpacity:this.host._reducedMotion?1:.35}:a;this.host._cancelModeTransition(!1),this.host._editorSecondary.closeForNavigation(),(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)&&this._cancelDecorGesture();const h=!s.bg&&"view"===t!=("view"===i),l=n&&"view"===i&&"view"!==t?this.host._modeTransitionEditorCamera:null;let c=l?.zoom??(n?this.host._modeTransitionTargetZoom:this.host._zoom),d=l?l.centerX:n?this.host._modeTransitionTargetCenterX:r?.viewport.centerX,p=l?l.centerY:n?this.host._modeTransitionTargetCenterY:r?.viewport.centerY;if("view"===i&&"view"!==t&&!n){const e=this.host._view;if(this.host._viewModeSnap={space:this.host._space,zoom:this.host._zoom,cx:e?this.host._logicalViewCenter(o)?.x:void 0,cy:e?this.host._logicalViewCenter(o)?.y:void 0},"iso"===o){const e=this.host._logicalViewCenter("iso");d=e?.x,p=e?.y,this.host._view=null,this.host._mode=t,this.host._applyView(this.host._zoom,e?.x,e?.y)}h&&(c=1,d=void 0,p=void 0)}if("plan"===i&&this.host._activeDraftId&&(this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId),this.host._mode=t,"devices"===i&&"devices"!==t&&(this.host._showHidden=!1,this.host._deviceInbox=null,this.host._deviceInboxReturn=null,this.host._deviceInboxMemo=null),this.host._editorChromeMode="view"===t?i:t,"view"===t){"view"===i||n||(this.host._modeTransitionEditorCamera={zoom:this.host._zoom,centerX:r?.viewport.centerX,centerY:r?.viewport.centerY});const t=this.host._viewModeSnap;if(t&&t.space===this.host._space){c=t.zoom;const e=this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space]?"iso":"flat",s=null!=t.cx&&null!=t.cy&&"iso"===e?It([t.cx,t.cy],0):null;d=s?.[0]??t.cx,p=s?.[1]??t.cy}else t&&(c=this.host._zoomBySpace[this.host._space]||1,d=void 0,p=void 0)}else this.host._modeTransitionEditorCamera={zoom:c,centerX:d,centerY:p};this.host._modeTransitionTargetZoom=c,this.host._modeTransitionTargetCenterX=d,this.host._modeTransitionTargetCenterY=p,("iso"===o||"view"===t&&this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space])&&(this.host._modeTransitionForceAtomic=!0);const u=++this.host._modeTransitionRequest;e?r?(this.host._modeTransitionPreparing=!0,this.host._modeTransitionVisual=r,this.host._prepareModeTransition(u,r,t,c,d,p)):(this.host._modeTransitionPreparing=!1,this.host._modeTransitionVisual=null,this.host._zoom=c,this.host._view=null,requestAnimationFrame(()=>{this.host.isConnected&&u===this.host._modeTransitionRequest&&this.host._mode===t&&(this.host._applyView(c,d,p),"view"===t&&(this.host._viewModeSnap=null,this.host._modeTransitionEditorCamera=null),this.host.requestUpdate())})):"view"===t&&this.host._commitViewModeAtomic(r,c,d,p),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this.host._tool="draw",this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._selId=null,this.host._physicalSel=null,this.host._physicalDialog=null,this.host._physicalDrag=null,this._rszResetController(),this.host._tip=null,this.host._hoverRoom=null,this.host._decorDraft=null,this.host._decorSel=null,this.host._decorMove=null,this.host._backdropDialog=null,this.host._decorTool="select",this.host._bdDrag=null,this.host._dtDrag=null,this.host._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this.host._saveNav()}_primeDrawWallField(){null===this.host._drawWallField&&(this.host._drawWallField=Tt(N,this.host._imperial))}_showPhysicalRange(t=this.host._drawWallMaxCm,e=0){this.host._showToast(this.host._t("toast.physical_range",{min:Tt(e,this.host._imperial),max:Tt(t,this.host._imperial),unit:this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this.host._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_mergeSpacePartitions(t,e){const s=t?.partitions||[];if(s.length<2)return 0;const i=zn(s,{pitch:I,seedIds:e,geometry:An(t,{excludeDraftId:this.host._activeDraftId})});return i.merged?(t.partitions=i.partitions,Fn(t.openings,t.partitions,i.openingMoves,{coordScale:ah,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch}),i.merged):0}_finishWallChain(){if("draw"!==this.host._tool||this.host._wallFaceBatch||this.host._roomDialog)return!0;const t=this.host._curSpaceCfg;if(!t||this.host._path.length<2)return this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0;const e=Fa(this.host._path,za(this.host._path.length-1,this.host._draftSegmentCms,this.host._drawWallCm,N)),s=e.length;if((t.partitions||[]).length+s>nh)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const i=this._geometrySnapshot();t.partitions||=[];const o=Date.now().toString(36),a=[],n=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,r=this._draftSegmentsForPath(this.host._path,n,e.map(t=>t.cm));for(let i=0;it.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.wall_chain_finish"),i),delete this.host._resumeDraftBySpace[this.host._space],this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0}_activateMarkupTool(t){t!==this.host._tool&&(this.host._wallFaceBatch&&this._roomDialogCancel(),("draw"!==this.host._tool||this._finishWallChain())&&(this._cancelPath(),"resize"===this.host._tool&&(this.host._resize.dragging?this._rszCancelDrag():this.host._resize.reset()),this.host._tool=t,"draw"===t&&this._resumeLastDraft(),"resize"===t&&this.host._resize.selectRoom(null),"wallthick"===t&&(this.host._wallDialog=null)))}_limitReached(t){const e=this.host._curSpaceCfg;if(!e)return!0;const s="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=nh:(e.wall_columns||[]).length>=500;return s&&this.host._showToast(this.host._t("toast.physical_limit")),s}_svgPoint(t){const e=this.host.renderRoot.querySelector(".stage").getBoundingClientRect();return this.host._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this.host._gridPitch;return[Rt(zt(t[0],e)),Rt(zt(t[1],e))]}_snapDrawPoint(t,e=!1){const s=this.host._path[this.host._path.length-1],i=e&&s?Ft(s,t,this.host._gridPitch,Ci):t;return this._snap(i)}_planSnapOpeningCuts(t,e){if(!this.host._openingsR.length)return[];const s=this.host._openingWallIndexFor(t,e).value,i=[];for(const e of this.host._roomWallOpeningInputs(this.host._openingsR,t)){const t=G(s,e);if(!t.negative&&!t.positive)continue;const o=e.angle*Math.PI/180,a=Math.cos(o)*e.length/2,n=Math.sin(o)*e.length/2;i.push([e.x-a,e.y-n,e.x+a,e.y+n])}return i}_planSnapGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|empty`,value:{segments:[],endpoints:[]}};const e=[this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planSnapGeometryCache?.key===e)return this.host._planSnapGeometryCache;const s=this.host._openCuts(),i=Sa({space:t,activeDraftId:this.host._activeDraftId,roomCuts:this._planSnapOpeningCuts(t,s),partitionCuts:this._partitionOpeningCuts(t),epsilon:2e-4*this.host._gridPitch});return this.host._planSnapGeometryCache={key:e,value:i},this.host._planSnapGeometryCache}_hiddenWallDiagnosticSnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|hidden-empty`,value:{segments:[],endpoints:[]}};const e=["hidden",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._hiddenWallDiagnosticCache?.key===e)return this.host._hiddenWallDiagnosticCache;const s=xa({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._hiddenWallDiagnosticCache={key:e,value:s},this.host._hiddenWallDiagnosticCache}_planStructuralGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|structural-empty`,value:{segments:[],endpoints:[]}};const e=["structural",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planStructuralGeometryCache?.key===e)return this.host._planStructuralGeometryCache;const s=Sa({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._planStructuralGeometryCache={key:e,value:s},this.host._planStructuralGeometryCache}_planSnapContextKey(t){const e=this.host._path[0],s=this.host._path[this.host._path.length-1];return[t,this.host._tool,this.host._path.length,e?`${e[0]},${e[1]}`:"",s?`${s[0]},${s[1]}`:""].join("|")}_resolvePlanDrawPoint(t,e){const s=this._planSnapGeometrySnapshot(),i=this.host._path[this.host._path.length-1],o="draw"===this.host._tool&&this.host._path.length>=3?[{point:this.host._path[0],key:"closure:first-point"}]:[],a={tolerance:this._cssPxToRender(12),distinguishTolerance:this._cssPxToRender(8),gridStep:this.host._gridPitch,excludePoints:i?[i]:[],extraEndpoints:o,epsilon:2e-4*this.host._gridPitch},n=e&&i?Ta(s.value,t,{...a,anchor:i}):function(t,e,s){if(!(ga(e)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??ma,o=s.excludePoints||[],a=Pa(Ca(t,e,s),s.distinguishTolerance||0);if(a)return a;let n=null;for(const a of t.segments){const t=[a.a[0],a.a[1],a.b[0],a.b[1]],r=C([e[0],e[1]],t);if(r>s.tolerance)continue;const h=Ia(a,e,s.gridStep);!Ma(h,o,i)&&Da(r,a.key,n)&&(n={kind:"line",point:h,key:a.key,distance:r,segment:a})}return n?{kind:"resolved",candidate:n,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}(s.value,t,a),r="resolved"===n.kind?n.candidate:null,h=r?[...r.point]:this._snapDrawPoint(t,e),l=i?At(i,h):h;return{point:l,candidate:r&&q(l,r.point)?r:null,conflicts:"ambiguous"===n.kind?n.conflicts:[],ambiguous:"ambiguous"===n.kind,contextKey:this._planSnapContextKey(s.key)}}_clearPlanSnapHover(t=!0){this.host._planSnapHover=null,this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers([]),t&&(this.host._cursorPt=null)}_samePt(t,e){return q(t,e)}_dropLegacySegments(){for(const t of this.host._serverCfg?.markers||[])"ripple"===t.display&&(t.display="icon_ripple");for(const t of this.host._serverCfg?.spaces||[]){delete t.segments;const e=new Set,s=t=>"string"==typeof t&&t.length>=1&&t.length<=64&&!e.has(t),i=t=>(e.add(t),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=Xt);if(Array.isArray(t.partitions)&&(t.partitions=t.partitions.filter(t=>t&&s(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&i(t.id)).map(t=>({...t,cm:Number.isFinite(Number(t.cm))?Math.max(0,Math.min(100,Number(t.cm))):15})),t.partitions.length||delete t.partitions),Array.isArray(t.wall_columns)&&(t.wall_columns=t.wall_columns.filter(t=>t&&s(t.id)&&o(t.center)&&i(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:wt(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:Nt(t.angle)}})),t.wall_columns.length||delete t.wall_columns),Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t&&s(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&i(t.id)).map(t=>{const e=cr(t);return{id:t.id,...e}}).filter(t=>t.points.length>=2),t.room_drafts.length||delete t.room_drafts),Array.isArray(t.walls)){const e=Ht(_t({spaces:[t]}),t.id),s=e?Lt(t,e,ah,.02*nt).contour.map(t=>t.map(t=>t/ah)):K(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=rt(t.walls,t.rooms||[],I,1,s),t.walls.length||delete t.walls}}}_rollbackRejectedPhysicalWrites(t){if(!this.host._serverCfg||!t.length)return!1;let e=!1;for(const[s]of t){const t=this.host._pendingPhysicalWrites.get(s);t&&(e=this._restoreGeometryStateInConfig(this.host._serverCfg,t.before)||e,this.host._pendingPhysicalWrites.delete(s))}return!!e&&(this._clearGeometryGesture(),this.host._geometryHistory.clear(),this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host._regSignature="",this.host._cfgContentFingerprint=Ot(this.host._serverCfg),this.host._maybeRebuildDevices(),this.host.requestUpdate(),!0)}async _reloadRejectedPhysicalWrite(){const t=this.host._writeChain;await t.catch(()=>{}),await this.host._reloadConfigOnly(!0)}_writeConfig(){this.host._writesPending++,this.host._writeChain=Et(this.host._writeChain,async()=>{if(!this.host._serverCfg)return;this._dropLegacySegments();const t=xi(this.host._serverCfg),e=Ot(t),s=[...this.host._pendingPhysicalWrites.entries()];for(const[e,i]of s){const s=t.spaces.find(t=>t.id===e),o=jt(s);if(o===i.fingerprint)continue;let a=!1;try{a=this.host._checkSpacePhysicalGeometry(t,e).ok}catch{a=!1}if(!a)throw this._restoreGeometryStateLocal(i.before),this.host._pendingPhysicalWrites.delete(e),this.host._geometryHistory.clear(),this.host._showToast(this.host._t("toast.geometry_unsafe")),Object.assign(new Error("unsafe wall geometry"),{code:"geometry-unsafe"});i.fingerprint=o}e!==Ot(this.host._serverCfg)&&(this.host._serverCfg=t),this.host._cfgContentFingerprint=e;try{await this.host._sendConfigCandidate(t)}catch(t){if(!this._rollbackRejectedPhysicalWrites(s))throw t;const e=t&&"object"==typeof t?t:Object.assign(new Error(String(t)),{cause:t});throw e.physicalGeometryRolledBack=!0,e}for(const[t,e]of s)this.host._pendingPhysicalWrites.get(t)?.fingerprint===e.fingerprint&&this.host._pendingPhysicalWrites.delete(t)});return this.host._writeChain.finally(()=>{this.host._writesPending--})}_saveConfig(){this.host._cfgEpoch++,this.host._saveConfigDebounced()}_geometrySnapshotFromConfig(t,e){const s=t?.spaces?.find(t=>t.id===e);if(!s)return null;const i=t=>JSON.parse(JSON.stringify(t)),o={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==s[t]&&(o[t]=s[t]);return{spaceId:e,rooms:i(s.rooms||[]),...Array.isArray(s.openings)?{openings:i(s.openings)}:{},...Array.isArray(s.walls)?{walls:i(s.walls)}:{},...Array.isArray(s.wall_segments)?{wall_segments:i(s.wall_segments)}:{},...Array.isArray(s.open_spans)?{open_spans:i(s.open_spans)}:{},...Array.isArray(s.room_drafts)?{room_drafts:i(s.room_drafts)}:{},...Array.isArray(s.partitions)?{partitions:i(s.partitions)}:{},...Array.isArray(s.wall_columns)?{wall_columns:i(s.wall_columns)}:{},...Array.isArray(s.decor)?{decor:i(s.decor)}:{},plan_transform:o}}_geometrySnapshot(t=this.host._space){return this._geometrySnapshotFromConfig(this.host._serverCfg,t)}_recordGeometry(t,e){if(!e)return;const s=this._geometrySnapshot(e.spaceId);s&&JSON.stringify(e)!==JSON.stringify(s)&&(this.host._geometryHistory.push({name:t,before:e,after:s}),this.host.requestUpdate())}_restoreGeometryStateInConfig(t,e,s=!1){const i=t?.spaces?.find(t=>t.id===e.spaceId);if(!i)return!1;const o=t=>JSON.parse(JSON.stringify(t)),a=new Map((i.rooms||[]).map(t=>[t.id,t]));if(i.rooms=o(e.rooms),s)for(const t of i.rooms){const e=a.get(t.id);!Array.isArray(t.wall_ids)&&Array.isArray(e?.wall_ids)&&e.wall_ids.length===t.poly?.length&&(t.wall_ids=o(e.wall_ids))}const n=(t,e)=>{void 0!==e?i[t]=o(e):s&&"wall_segments"===t||delete i[t]},r=new Map((i.openings||[]).map(t=>[t.id,t])),h=new Map((i.room_drafts||[]).map(t=>[t.id,t]));if(n("openings",e.openings),n("walls",e.walls),n("wall_segments",e.wall_segments),n("open_spans",e.open_spans),n("room_drafts",e.room_drafts),n("partitions",e.partitions),n("wall_columns",e.wall_columns),n("decor",e.decor),s){for(const t of i.openings||[]){const e=r.get(t.id);t.host||"wall"!==e?.host?.kind||(t.host=o(e.host))}for(const t of i.room_drafts||[]){const e=h.get(t.id);for(let s=0;s<(t.segments||[]).length;s++)!t.segments[s]?.id&&e?.segments?.[s]?.id&&(t.segments[s].id=e.segments[s].id)}}for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete i[t];return Object.assign(i,o(e.plan_transform||{})),!0}_restoreGeometryStateLocal(t){return!!this._restoreGeometryStateInConfig(this.host._serverCfg,t)&&(this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host.requestUpdate(),!0)}_wallModelBlockerLabel(t){const e=t instanceof sr?t.reason:"invalid-room";return this.host._t(`wall_model.reason.${e}`)}_hasLegacyZeroWallFields(t=this.host._serverCfg){return(t?.spaces||[]).some(t=>Array.isArray(t?.open_spans)&&t.open_spans.length>0||(t?.rooms||[]).some(t=>Array.isArray(t?.open_to)&&t.open_to.length>0))}_showWallModelMigrationBlocked(t){const e=this._hasLegacyZeroWallFields()?"toast.zero_wall_migration_blocked":"toast.wall_model_migration_blocked";this.host._showToast(this.host._t(e,{reason:this._wallModelBlockerLabel(t)}))}_limitSegmentsOf(t){const e=[];for(const s of t?.wall_segments||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.partitions||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.room_drafts||[]){const t=Array.isArray(s?.points)?s.points:[],i=Array.isArray(s?.segments)?s.segments:[];for(let o=0;o+1t?.id===e);if(!i)return[];const o=Number(i.cell_cm)>0?Number(i.cell_cm):5,a=this._limitSegmentsOf(i),n=[...ea(a),...na(a,o,I),...ha(a,o,I)];let r=null;try{r=Bt(i.rooms||[],i.walls||[],[],I,o,I,1)}catch{r=null}let h="ok"===s?.status||"degraded-extra"===s?.status?s.roomGeom:null;if(!h&&r&&r.nodes.length)try{const t=Wt(i.rooms||[],i.walls||[],[],[],I,o,I,1);h="ok"===t?.status||"degraded-extra"===t?.status?t.roomGeom:null}catch{h=null}for(const t of i.rooms||[]){const e=String(t?.id||"");if(!e)continue;let s=null;try{s=Gt(i.rooms||[],e,i.walls||[],[],I,o,I,1,h??void 0,r)}catch{s=null}n.push(...la(e,s,o,I))}return n}_junctionLimitLabel(t){const e=t=>String(Math.round(10*t)/10);return this.host._t(`junction.limit_${t.rule}`,{actual:e(t.actual),limit:e(t.limit)})}_junctionLimitsIntroduced(t,e,s,i){let o;const a=(e?.spaces||[]).find(t=>t?.id===s);let n="";try{n=jt(a)}catch{n=""}const r=e&&"object"==typeof e?this._junctionBaselineCache.get(e):void 0;if(r&&n&&r.fingerprint===n&&r.spaceId===s&&(o=r.violations),!o){try{const t=Number(e?.model_version||0)>=9?e:Dr(e).config;o=this.host._junctionLimitViolations(t,s)}catch{return[]}e&&"object"==typeof e&&n&&this._junctionBaselineCache.set(e,{spaceId:s,fingerprint:n,violations:o})}let h=[];try{h=this.host._junctionLimitViolations(t,s,i)}catch{return[{rule:"check_failed",subject:s,actual:0,limit:0}]}return function(t,e){const s=new Map;for(const t of e||[])s.set(t.rule,(s.get(t.rule)||0)+1);const i=new Map;for(const e of t||[])i.set(e.rule,[...i.get(e.rule)||[],e]);const o=[];for(const[t,e]of i){const i=e.length-(s.get(t)||0);i>0&&o.push(...e.slice(0,i))}return o}(h,o)}_commitPhysicalGeometry(t,e,s=[]){if(!e||!this.host._serverCfg)return!1;const i=this.host._serverCfg,o=this._geometrySnapshotFromConfig(i,e.spaceId),a=i.spaces.find(t=>t.id===e.spaceId);if(!a||jt(e)===jt(a))return!1;let n,r=!1;try{r=this.host._checkSpacePhysicalGeometry(i,e.spaceId).ok}catch{r=!1}if(!r)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;let h=e;try{if(Number(i.model_version||0)<9){const t=JSON.parse(JSON.stringify(i));if(!this._restoreGeometryStateInConfig(t,e))throw new sr("invalid-room",e.spaceId);const s=Dr(t).config;h=this._geometrySnapshotFromConfig(s,e.spaceId)||e;const a=JSON.parse(JSON.stringify(s));if(!o||!this._restoreGeometryStateInConfig(a,o,!0))throw new sr("invalid-room",e.spaceId);const r=s.spaces.find(t=>t.id===e.spaceId),l=a.spaces.find(t=>t.id===e.spaceId);if(!Array.isArray(o.wall_segments)&&JSON.stringify(e.rooms)===JSON.stringify(o.rooms))for(const t of l?.wall_segments||[])t.cm=Q(o.walls,t.a,t.b,I,1);const c=((t,e,s)=>{const i=new Map,o=new Map((t?.rooms||[]).map(t=>[t.id,t])),a=new Map((e||[]).map(t=>[t.id,t])),{rooms:n}=wr(s,yr(t));for(const t of n){const e=a.get(t.id),n=o.get(t.id),r=R(e),h=(s.rooms||[]).find(e=>e.id===t.id),l=R(h),c=R(n);if(r&&l&&c&&r.length===l.length&&c.length===n?.wall_ids?.length&&t.poly.length===t.wall_ids.length)for(let e=0;e{const o=[];for(let a=0;aer||ur(r,s,i)>er)continue;const h=pr(n,s,i),l=pr(r,s,i);h<-er||l>1+er||l<=h+er||o.push({start:h,id:e[a]})}return o.sort((t,e)=>t.start-e.start)},o=s(c,n.wall_ids,r[e],r[(e+1)%r.length]),a=s(t.poly,t.wall_ids,l[e],l[(e+1)%l.length]);if(o.length&&o.length===a.length)for(let t=0;tt.id===e.spaceId),i=this.host._path.length>=2?this.host._path.map(t=>[t[0]/ah,t[1]/ah]):[];i.push(...s.map(t=>[t[0],t[1]])),l=dr(t)<=dr(h,i)&&this.host._checkSpacePhysicalGeometry(n,e.spaceId).ok}catch{l=!1}if(!l)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;const c=JSON.parse(JSON.stringify(i));this._restoreGeometryStateInConfig(c,e);const d=this._junctionLimitsIntroduced(n,c,e.spaceId);if(d.length)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this._junctionLimitLabel(d[0])),!1;Pr(i,n),this._recordGeometry(t,h);const p=i.spaces.find(t=>t.id===e.spaceId),u=this.host._pendingPhysicalWrites.get(e.spaceId);return this.host._pendingPhysicalWrites.set(e.spaceId,{before:u?.before||h,fingerprint:jt(p)}),this._saveConfig(),!0}_clearGeometryGesture(){this._clearFurniturePreview(),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!1),this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._wallFaceBatch=null,this.host._wallRepairDiagnostic=null,this.host._roomDeleteDialog=null,this.host._wallDialog=null,this.host._physicalDialog=null,this.host._physicalSel=null,this.host._physicalDrag=null,this.host._physicalRotate=null,this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._openingDialog=null,this._rszResetController(),this.host._decorDraft=null,this.host._decorMove=null,this.host._dtDrag=null,this.host._bdDrag=null}_stagePointerCancel(t){if(clearTimeout(this.host._kioskHoldTimer),this.host._swipeStart?.id===t.pointerId&&(this.host._swipeStart=null),this.host._furnTouchPending?.pid===t.pointerId)return this._clearFurniturePreview(),this.host._pointers.delete(t.pointerId),void this.host.requestUpdate();if(this.host._physicalDrag?.pid===t.pointerId||this.host._physicalRotate?.pid===t.pointerId)return void this._cancelPhysicalGesture();if(this.host._decorDraft?.pid===t.pointerId)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove?.pid===t.pointerId||this.host._dtDrag?.pid===t.pointerId||this.host._bdDrag?.pid===t.pointerId)return void this._cancelDecorGesture();"opening"===this.host._tool?(this.host._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this.host._tool&&this._clearPlanSnapHover();const e=!!this.host._pinchStart||!!this.host._panStart;this.host._pointers.delete(t.pointerId),this.host._pointers.size<2&&(this.host._pinchStart=null),0===this.host._pointers.size&&(this.host._panStart=null,this.host._panLock=null),e&&0===this.host._pointers.size&&this.host.requestUpdate()}_applyGeometryState(t,e=!1){if(!this.host._canCommitSpace(t.spaceId))return!1;const s=this._geometrySnapshot(t.spaceId);if(!s||!this._restoreGeometryStateLocal(t))return!1;const i=this.host._serverCfg,o=jt(s)!==jt(t);if(o){let o=!1;try{const s=i?this.host._checkSpacePhysicalGeometry(i,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1}let a;try{a=Dr(i).config}catch(t){return this._restoreGeometryStateLocal(s),this._showWallModelMigrationBlocked(t),!1}if(o){let o=!1;try{const s=a?this.host._checkSpacePhysicalGeometry(a,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;Pr(i,a);const n=i?.spaces.find(e=>e.id===t.spaceId),r=this.host._pendingPhysicalWrites.get(t.spaceId);this.host._pendingPhysicalWrites.set(t.spaceId,{before:r?.before||s,fingerprint:jt(n)})}else Pr(i,a);return this._clearGeometryGesture(),this.host._space!==t.spaceId&&(this.host._commitSpace(t.spaceId),this.host._saveNav(),this.host._restoreZoom()),this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate(),!0}_roomAt(t){return this.host._spaceModel()?.rooms.find(e=>{const s=R(e);return!!s&&qt(t,s)})}_overlapRoom(t){return this.host._spaceModel()?.rooms.find(e=>{const s=R(e);return!!s&&$(t,s)})}_pointInRoom(t,e){return e.poly?Ut(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!go(t))return!0;const e=t.length,s=.001;for(let i=0;i=500)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const t=this.host._curSpaceCfg;return!(!this.host._activeDraftId&&(t?.room_drafts||[]).length>=200||this._draftSegmentCount(t)>=2e3||(t?.partitions||[]).length+this.host._path.length>nh)||(this.host._showToast(this.host._t("toast.physical_limit")),!1)}_markupClick(t){if(this.host._vacFit)return;if(!this.host._markup)return;const e=this.host._spaceModel();if(!e)return void this._clearGeometryGesture();if(this.host._suppressClick)return;if(this.host._drag||this.host._rlResize)return;const s=t.composedPath?.()||[];if(s.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(s.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this.host._tool)return void(this.host._physicalSel=null);if("resize"===this.host._tool){if(this.host._resize.dragging||s.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...e.rooms].reverse().find(t=>this._pointInRoom(i,t));return this.host._resize.selectRoom(t?.id||null),void this.host.requestUpdate()}if("delroom"===this.host._tool)return void this._deleteRoomClick(i);if("opening"===this.host._tool)return void this._openingClick(i);if("merge"===this.host._tool)return void this._mergeClick(i);if("wallthick"===this.host._tool)return void this._wallThickClick(i);if("split"===this.host._tool)return void this._splitClick(i);if("column"===this.host._tool)return void this._columnClick(i);this.host._wallRepairDiagnostic=null;const o=this._resolvePlanDrawPoint(i,t.shiftKey);if(o.ambiguous)return this.host._planSnapHover={contextKey:o.contextKey,candidate:null,conflicts:o.conflicts},this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers(o.conflicts),void this.host._showToast(this.host._t("toast.plan_snap_ambiguous"));let a=o.point;if(t.ctrlKey||t.metaKey){if(t.preventDefault(),this.host._path.length<3)return;a=At(this.host._path[this.host._path.length-1],this.host._path[0])}if(!this.host._path.length){if(!t.shiftKey&&!o.candidate&&this._offerExistingWallFace(i))return;const e=this._draftEndAt(a);return e?(this.host._activeDraftId=e.draft.id,this.host._resumeDraftBySpace[this.host._space]=e.draft.id,this.host._path=e.reverse?[...e.draft.points].reverse().map(t=>[...t]):e.draft.points.map(t=>[...t]),void(this.host._draftSegmentCms=this._adoptDraftCms(this.host._path,e.reverse?[...e.draft.segments].reverse().map(t=>t.cm):e.draft.segments.map(t=>t.cm),e.draft.id))):(this.host._activeDraftId=null,this.host._draftSegmentCms=[],void(this.host._path=[a]))}const n=this.host._path[this.host._path.length-1];if(this._samePt(a,n))return;const r=this._draftEndAt(a,this.host._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(!this._canAppendRoomDraftPoint())return;const h=this.host._drawWallCm;if(null==h)return void this._showPhysicalRange(100);const l=this.host._path.map(t=>[...t]);this.host._path=[...this.host._path,a],this.host._draftSegmentCms=[...this.host._draftSegmentCms,h],this._persistActiveDraftSegment(),this._offerWallFaces(l)}_draftEndAt(t,e){const s=this.host._spaceModel();if(!s)return null;const i=this.host._viewOr(this.host._baseVb()),o=Math.max(.15*this.host._gridPitch,this.host._stageEl?.clientWidth?i.w/this.host._stageEl.clientWidth*12:0);for(const i of s.room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const s=i.points[0],a=i.points[i.points.length-1];if(Math.hypot(t[0]-a[0],t[1]-a[1])<=o)return{draft:i,reverse:!1};if(Math.hypot(t[0]-s[0],t[1]-s[1])<=o)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this.host._curSpaceCfg;if(!e||!this.host._path.length)return;const s=this.host._path.map(t=>[...t]),i=this._wallGraphSources(s),o=Array.isArray(e.room_drafts)?e.room_drafts:[],a=this.host._activeDraftId?o.find(t=>t.id===this.host._activeDraftId):null,n=o.find(e=>e.id===t.draft.id);if(!n)return;const r=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),h=t.reverse?(n.segments||[]).map(t=>({...t})):[...n.segments||[]].reverse().map(t=>({...t})),l=this.host._path[this.host._path.length-1],c=this._samePt(l,r[0]),d=c?null:this.host._drawWallCm;if(!c&&null==d)return void this._showPhysicalRange(100);const p=this._draftSegmentsForPath(this.host._path,a,this.host._draftSegmentCms),u=[...this.host._path.map(t=>[...t]),...c?r.slice(1):r],_=[...p,...null==d?[]:[{cm:d}],...h],m=u.length>=4&&this._samePt(u[0],u[u.length-1]),g=u,f=_;if(g.length>500)return void this.host._showToast(this.host._t("toast.physical_limit"));const b=(a?.segments?.length||0)+(n.segments?.length||0);if(this._draftSegmentCount(e)-b+f.length>2e3)return void this.host._showToast(this.host._t("toast.physical_limit"));if(m){const t=g.slice(0,-1);if(this._contourSelfIntersects(t)||k(t)<=1e-6)return void this.host._showToast(this.host._t("toast.contour_cannot_close"));const e=this._overlapRoom(t);if(e)return void this.host._showToast(this.host._t("toast.room_overlap",{name:e.name||""}))}const v=this._geometrySnapshot(),y=this.host._activeDraftId||t.draft.id,$={...a||n,id:y,points:g.map(t=>[t[0]/ah,t[1]/ah]),segments:f};e.room_drafts=o.filter(e=>e.id!==t.draft.id&&(!this.host._activeDraftId||e.id!==this.host._activeDraftId)),e.room_drafts.push($),this.host._activeDraftId=y,this.host._resumeDraftBySpace[this.host._space]=y,this.host._path=g,this.host._draftSegmentCms=this._adoptDraftCms(g,f.map(t=>Number(t.cm)),y),this.host._physicalSel=null,this._commitPhysicalGeometry(this.host._t("history.draft_merge"),v),null!=d&&this._offerWallFaces(s,s.length-1,i)}_adoptDraftCms(t,e,s){const i=Math.max(0,t.length-1),o=za(i,e,this.host._drawWallCm,N);return e.length!==i&&console.debug(`[houseplan] draft ${s??"?"}: восстановлено толщин ${i-e.length} (#234)`),o}_draftSegmentsForPath(t,e,s){const i=Array.isArray(e?.points)?e.points.map(t=>[Number(t?.[0])*ah,Number(t?.[1])*ah]):[],o=Array.isArray(e?.segments)?e.segments:[];return za(Math.max(0,t.length-1),s,this.host._drawWallCm,N).map((e,s)=>{const a=t[s],n=t[s+1],r=i.findIndex((t,e)=>{if(e+1>=i.length)return!1;const s=i[e+1];return this._samePt(a,t)&&this._samePt(n,s)||this._samePt(a,s)&&this._samePt(n,t)});return{...r>=0?o[r]:{},cm:e}})}_persistActiveDraftSegment(){if(this.host._path.length<2||!this.host._curSpaceCfg)return;const t=this._geometrySnapshot(),e=this.host._curSpaceCfg;e.room_drafts||=[],this.host._activeDraftId||(this.host._activeDraftId="draft-"+Date.now().toString(36)),this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId;const s=e.room_drafts.findIndex(t=>t.id===this.host._activeDraftId),i={...s>=0?e.room_drafts[s]:{},id:this.host._activeDraftId,points:this.host._path.map(t=>[t[0]/ah,t[1]/ah]),segments:this._draftSegmentsForPath(this.host._path,s>=0?e.room_drafts[s]:null,this.host._draftSegmentCms)};s>=0?e.room_drafts[s]=i:e.room_drafts.push(i),this._commitPhysicalGeometry(this.host._t("history.draft_segment"),t)}_activeWallSourceKey(t){return`active:${this.host._activeDraftId||"session"}:${t}`}_wallGraphSources(t){const e=this._planStructuralGeometrySnapshot().value.segments.map(t=>({a:t.a,b:t.b,key:`static:${t.key}`}));for(let s=0;s+1t.key===s);if(i>=0){const[t]=this.host._wallFaceGraphCache.splice(i,1);return this.host._wallFaceGraphCache.push(t),t.value}const o=sn(t,e);return this.host._wallFaceGraphCache.push({key:s,value:o}),this.host._wallFaceGraphCache.length>4&&this.host._wallFaceGraphCache.shift(),o}_offerWallFaces(t,e=this.host._path.length-2,s){if(this.host._path.length<2||this.host._wallFaceBatch||this.host._roomDialog)return;const i=this.host._spaceModel();if(!i)return;const o=this._activeWallSourceKey(e),a=2e-4*this.host._gridPitch;let n,r;try{const e=s||this._wallGraphSources(t);r=this._wallGraphSources(this.host._path),n=function(t,e,s){const i=new Set(t.faces.map(t=>t.key));return e.faces.filter(t=>!i.has(t.key)&&t.sourceKeys.includes(s))}(this._wallFaceGraph(e,a),this._wallFaceGraph(r,a),o)}catch{return}if(!n.length){const t=mn(r,{requiredSourceKey:o,maxDistance:j(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:a});return"ambiguous"===t.kind?(this.host._wallRepairDiagnostic=t.proposals[0]||null,void this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous"))):void("repair"!==t.kind||this._overlapRoom(t.face.ring)||this._beginWallFaceBatch([{...t.face,repair:t.proposal}]))}for(const t of i.rooms){const e=R(t);if(!t.id||!e)continue;const s=Vt(e,this.host._path,.02*this.host._gridPitch);if(!s)continue;const[i,o]=s,a=k(i)>=k(o)?i:o,r=a===i?o:i,h=[...n].sort((t,e)=>Math.abs(t.area-k(r))-Math.abs(e.area-k(r))||t.key.localeCompare(e.key))[0];if(!h)return;return void this._beginWallFaceBatch([{...h,ring:r.map(t=>[t[0],t[1]]),split:{roomId:t.id,mainPoly:a,newPoly:r},consumeAllActive:!0}])}const h=n.filter(t=>!(t.ring.length<3||this._contourSelfIntersects(t.ring))&&!this._overlapRoom(t.ring));h.length&&this._beginWallFaceBatch(h)}_beginWallFaceBatch(t){this.host._wallRepairDiagnostic=null,this.host._wallFaceBatch={candidates:[...t].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key)),index:0,decisions:[],activePath:this.host._path.map(t=>[...t]),activeCms:[...this.host._draftSegmentCms],activeDraftId:this.host._activeDraftId},this._clearPlanSnapHover(),this.host._nameSel="",this.host._areaSel="",this._resetRoomDialogFields(),this.host._roomDialog=!0}_offerExistingWallFace(t){if(this.host._path.length||this.host._wallFaceBatch||this.host._roomDialog)return!1;const e=2e-4*this.host._gridPitch;try{let s,i=on(this._wallFaceGraph(this._wallGraphSources([]),e),t,e);if(!i){const o=mn(this._wallGraphSources([]),{point:t,maxDistance:j(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:e});if("ambiguous"===o.kind)return this.host._wallRepairDiagnostic=o.proposals[0]||null,this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous")),!0;if("repair"===o.kind)i=o.face,s=o.proposal;else if("none"===o.kind){const s=mn(this._wallGraphSources([]),{point:t,maxDistance:this._cssPxToRender(12),gridStep:this.host._gridPitch,epsilon:e});if("repair"===s.kind)return this.host._wallRepairDiagnostic=s.proposal,this.host._showToast(this.host._t("toast.wall_repair_too_large")),!0}}return!(!i||i.ring.length<3||this._contourSelfIntersects(i.ring)||this._overlapRoom(i.ring))&&(this._beginWallFaceBatch([{...i,existing:!s,repair:s}]),!0)}catch{return!1}}_columnClick(t){const e=this._snap(t),s=this.host._drawWallCm;if(null==s)return void this._showPhysicalRange(vt,1);if(!this.host._curSpaceCfg||this._limitReached("column"))return;const i=this.host._spaceModel();if(!i)return;const o={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:wt(s),angle:0},a=(i.wall_columns||[]).find(t=>yt(t,o,.02*this.host._gridPitch));if(a)return clearTimeout(this.host._duplicateColumnTimer),this.host._duplicateColumnId=a.id,this.host._duplicateColumnTimer=window.setTimeout(()=>{this.host._duplicateColumnId=null},900),void this.host._showToast(this.host._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this.host._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...o,center:[e[0]/ah,e[1]/ah]}),this._commitPhysicalGeometry(this.host._t("history.column_add"),n)}_openPhysicalDialog(t,e,s){const i=this.host._spaceModel();if(i)if("partition"===t){const s=i.partitions.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:Tt(s.cm,this.host._imperial),length:this.host._fmtLen(s.a,s.b)})}else if("column"===t){const s=i.wall_columns.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:Tt(s.cm,this.host._imperial),shape:s.shape,angle:this.host._angleField("square"===s.shape?Nt(s.angle):0)})}else{const o=i.room_drafts.find(t=>t.id===e),a=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,s||0));o?.segments[a]&&(this.host._physicalDialog={kind:t,id:e,segment:a,cm:Tt(o.segments[a].cm,this.host._imperial),length:this.host._fmtLen(o.points[a],o.points[a+1])})}}_physicalDown(t,e,s){const i=this.host._spaceModel();if(!i)return;t.stopPropagation(),rh(t);const o=this._svgPoint(t),a=[];for(const t of[...i.wall_columns].reverse())Kt(o,Jt(t,this.host._cellCm,this.host._gridPitch))&&a.push({kind:"column",id:t.id});for(const t of[...i.partitions].reverse()){const e=Yt(t.a,t.b,t.cm,this.host._cellCm,this.host._gridPitch);e&&Kt(o,e)&&a.push({kind:"partition",id:t.id})}a.some(t=>t.kind===e&&t.id===s)||a.unshift({kind:e,id:s});const n=a.map(t=>`${t.kind}:${t.id}`).sort().join("|"),r=performance.now(),h=this.host._physicalPickCycle,l=a.length>1&&h?.signature===n&&r-h.at>380&&r-h.at<=1200&&Math.hypot(t.clientX-h.x,t.clientY-h.y)<=10?(h.index+1)%a.length:Math.max(0,a.findIndex(t=>t.kind===e&&t.id===s));this.host._physicalPickCycle={signature:n,index:l,x:t.clientX,y:t.clientY,at:r},e=a[l].kind,s=a[l].id;const c="partition"===e?i.partitions.find(t=>t.id===s):i.wall_columns.find(t=>t.id===s);c&&(this.host._physicalSel={kind:e,id:s},this.host._physicalDrag={pid:t.pointerId,kind:e,id:s,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,s){const i="partition"===t?[e.a,e.b]:[e.center],o=i.map(t=>t[0]),a=i.map(t=>t[1]);return[Math.max(-Xt*ah-Math.min(...o),Math.min(Xt*ah-Math.max(...o),s[0])),Math.max(-Xt*ah-Math.min(...a),Math.min(Xt*ah-Math.max(...a),s[1]))]}_physicalMove(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const s=this._svgPoint(t),i="partition"===e.kind?e.base.a:e.base.center,o=this._snap([i[0]+s[0]-e.start[0],i[1]+s[1]-e.start[1]]),a=this._clampPhysicalDelta(e.kind,e.base,[o[0]-i[0],o[1]-i[1]]);this.host._physicalDrag={...e,delta:a,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this.host._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);const s=this.host._spaceModel();if(!this.host._curSpaceCfg||!s)return;const i=this.host._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;if(t){const o={...s,a:[s.a[0]+e.delta[0],s.a[1]+e.delta[1]],b:[s.b[0]+e.delta[0],s.b[1]+e.delta[1]]};t.a=[o.a[0]/ah,o.a[1]/ah],t.b=[o.b[0]/ah,o.b[1]/ah];for(const t of i.openings||[]){if("partition"!==t.host?.kind||t.host.id!==e.id)continue;const s=z(t,[o],ah,this.host._cellCm,this.host._gridPitch).resolved;s&&Object.assign(t,F(t,s,ah))}}}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),o=e.base,a={...o,center:[o.center[0]+e.delta[0],o.center[1]+e.delta[1]]};if(s.wall_columns.some(t=>t.id!==e.id&&yt(t,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));t&&(t.center=[(o.center[0]+e.delta[0])/ah,(o.center[1]+e.delta[1])/ah])}this._commitPhysicalGeometry(this.host._t("history.physical_move"),e.before)}_registerPhysicalTap(t,e,s){const i=performance.now(),o=this.host._physicalLastTap?.kind===t&&this.host._physicalLastTap.id===e&&this.host._physicalLastTap.segment===s&&i-this.host._physicalLastTap.at<=360;this.host._physicalLastTap={kind:t,id:e,segment:s,at:i},o&&(this.host._physicalLastTap=null,this._openPhysicalDialog(t,e,s))}_cancelPhysicalGesture(){this.host._physicalDrag=null,this.host._physicalRotate=null,this.host.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape||!this.host._spaceModel())return;t.preventDefault(),t.stopPropagation(),rh(t);const s=this._svgPoint(t);this.host._physicalSel={kind:"column",id:e.id},this.host._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,baseAngle:Nt(e.angle),angle:Nt(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const s=this._svgPoint(t),i=180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,o=e.baseAngle+i-e.startAngle,a=Nt(t.shiftKey?o:5*Math.round(o/5));this.host._physicalRotate={...e,angle:a,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation(),this.host._physicalRotate=null;const s=this.host._spaceModel();if(!e.moved||!this.host._curSpaceCfg||!s)return;const i=s.wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const o={...i,angle:e.angle};if(s.wall_columns.some(t=>t.id!==e.id&&yt(t,o,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));const a=this.host._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);a&&(a.angle=e.angle,this._commitPhysicalGeometry(this.host._t("history.physical_edit"),e.before))}_rszRooms(){const t=[],e=this.host._spaceModel();if(!e)return t;for(const s of e.rooms){const e=s.id?R(s):null;e&&t.push({id:s.id,poly:e,wall_ids:Array.isArray(s.wall_ids)?[...s.wall_ids]:void 0})}return s=t,i=Math.max(1e-12,1e-12*this.host._gridPitch),s.map(t=>{if(!Array.isArray(t.wall_ids)||t.wall_ids.length!==t.poly.length)return{...t,poly:t.poly.map(t=>[...t])};const e=t.poly.map(t=>[...t]);let s=!0;for(;s&&e.length>3;){s=!1;for(let t=0;t0){e.splice(t,1),s=!0;break}}}return{...t,poly:e}});var s,i}_rszOpenings(){return this.host._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen,hosted:!!t.host,angle:t.angle,type:t.type}))}_rszObstacles(){const t=this.host._spaceModel();if(!t)return[];const e=[];for(const s of t.partitions||[])e.push({kind:"segment",a:[...s.a],b:[...s.b],half:j(s.cm,this.host._cellCm,this.host._gridPitch)/2});for(const s of t.room_drafts||[])for(let t=0;t+1Array.isArray(t.a)&&Array.isArray(t.b)&&t.a.length>=2&&t.b.length>=2).map(t=>({wall:t,a:[t.a[0]*ah,t.a[1]*this.host._spaceH],b:[t.b[0]*ah,t.b[1]*this.host._spaceH]})),o=Math.abs(t[0]-e[0])<=.05*this.host._gridPitch?"v":Math.abs(t[1]-e[1])<=.05*this.host._gridPitch?"h":null,a=i.filter(s=>!!o&&("h"===o?!(Math.abs(s.a[1]-t[1])>.05*this.host._gridPitch||Math.abs(s.b[1]-t[1])>.05*this.host._gridPitch)&&Math.min(Math.max(t[0],e[0]),Math.max(s.a[0],s.b[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s.a[0],s.b[0]))>.05*this.host._gridPitch:!(Math.abs(s.a[0]-t[0])>.05*this.host._gridPitch||Math.abs(s.b[0]-t[0])>.05*this.host._gridPitch)&&Math.min(Math.max(t[1],e[1]),Math.max(s.a[1],s.b[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s.a[1],s.b[1]))>.05*this.host._gridPitch)),n=new Set(a.map(t=>Number(t.wall.cm)).filter(t=>t>0));return{minDim:this.host._cmToUnits(30),eps:.05*this.host._gridPitch,step:this.host._gridPitch,movingHalf:s>0?j(s,this.host._cellCm,this.host._gridPitch)/2:0,obstacles:this._rszObstacles(),thicknessConflict:n.size>1}}_rszResolution(t,e,s){const i=this.host._resize.snapshotIdentity||s||this._rszSnapshot(),o=`${this.host._space}|${this.host._cellCm}|${this.host._gridPitch}|${i}`,a=`${t}:${e}`;return this.host._resize.resolve(o,a,()=>{const s=[...this.host._resize.rooms||this._rszRooms()],i=s.find(e=>e.id===t),o=i?.poly?.[e]||[0,0],a=i?.poly?.[(e+1)%(i?.poly?.length||1)]||[0,0];return function(t,e,s,i,o){const{eps:a}=o,n=t.find(t=>t.id===s);if(!n||i<0||i>=(n.poly?.length||0)||n.poly.length<4||!go(n.poly))return{enabled:!1,reason:"invalid-geometry"};const r=n.poly[i],h=n.poly[(i+1)%n.poly.length],l=yo(r,h,a);if(!l)return{enabled:!1,reason:"diagonal"};if(!xo(n.poly,i,l,a))return{enabled:!1,reason:"side-angle"};if(o.thicknessConflict)return{enabled:!1,reason:"thickness-conflict"};for(const t of o.obstacles||[])if(Do(t,r,h,o))return{enabled:!1,reason:"duplicate-physical-wall"};const c=po(ho(h,r)),d=[],p=new Set;let u=!1,_=!1;for(const e of t)if(e.id!==s)for(let t=0;t1||d.length>1)return{enabled:!1,reason:"multiple-rooms"};const m={[s]:i},g=[s];if(1===d.length){const t=d[0];if(!go(t.room.poly))return{enabled:!1,reason:"invalid-geometry"};if(!xo(t.room.poly,t.edge,l,a))return{enabled:!1,reason:"side-angle"};g.push(t.room.id),m[t.room.id]=t.edge}const f=[];for(const t of e)if(!(_o([t.x,t.y],r,h)>2*a)){if(t.hosted||t.length>c+2*a)return{enabled:!1,reason:"opening-conflict"};f.push(t.id)}const b=Object.fromEntries(g.map(e=>[e,t.find(t=>t.id===e).poly.length])),v=[];for(const e of g){const s=t.find(t=>t.id===e),i=m[e],o=(i-1+s.poly.length)%s.poly.length,n=(i+1)%s.poly.length,r=Io(t,e,o,1,a),h=Io(t,e,n,0,a);if(!r||!h)return{enabled:!1,reason:"partial-shared"};v.push(r,h)}const y={roomId:s,edge:i,a:[...r],b:[...h],n:mo(n.poly,i),roomIds:g,edgeByRoom:m,topology:b,movingOpeningIds:f,sideOwnership:v};if(!Ho(t,e,y,0,o))return{enabled:!1,reason:"invalid-geometry"};const $=Math.abs(Number(o.step));if(Number.isFinite($)&&$>a){const s=[-$,$];if(!s.some(s=>Ho(t,e,y,s,o))){if(!s.some(s=>zo(Fo(t,e,y,s),y,a)))return{enabled:!1,reason:"partial-shared"};const i={...o,obstacles:[]};return s.some(s=>Ho(t,e,y,s,i))?{enabled:!1,reason:"duplicate-physical-wall"}:e.length&&s.some(e=>Ho(t,[],y,e,o))?{enabled:!1,reason:"opening-conflict"}:{enabled:!1,reason:"invalid-geometry"}}}return{enabled:!0,plan:y}}(s,[...this.host._resize.openings||this._rszOpenings()],t,e,this._rszOptsFor(o,a))})}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this.host._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszResetController(){const t=null!==this.host._resize.preview;this.host._resize.reset(),t&&this.host._cfgEpoch++}_rszProjectPreview(t,e,s,i,o){this.host._rszLimitViolation=null;const a=this.host._serverCfg?.spaces.find(t=>t.id===this.host._space);if(!a||!this.host._serverCfg)return{ok:!1,reason:"missing-context"};const n=JSON.parse(t),r={...a,rooms:n.rooms,openings:n.openings||[]};for(const t of["walls","wall_segments","open_spans","room_drafts","partitions","wall_columns","decor"])void 0!==n[t]?r[t]=n[t]:delete r[t];for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==n.plan_transform?.[t]?r[t]=n.plan_transform[t]:delete r[t];const h=this.host._spaceH;for(const[t,s]of Object.entries(e)){const e=r.rooms.find(e=>e.id===t);e&&(e.poly=s.map(t=>[t[0]/ah,t[1]/h]),delete e.x,delete e.y,delete e.w,delete e.h)}for(const[t,e]of Object.entries(s)){const s=(r.openings||[]).find(e=>e.id===t);s&&(s.x=e[0]/ah,s.y=e[1]/h)}const l=[],c=[];for(const t of i){const e=o.find(e=>e.id===t),s=r.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const i=s.poly.map(t=>[t[0]*ah,t[1]*h]);if(e.poly.length!==i.length)continue;const a=Array.isArray(e.wall_ids)?e.wall_ids:[];for(let t=0;tt.id===s):null;o&&(o.a=[i[t][0]/ah,i[t][1]/ah],o.b=[i[(t+1)%i.length][0]/ah,i[(t+1)%i.length][1]/ah])}}if(l.length&&Array.isArray(r.walls)&&r.walls.length){const t=Zt(r.walls,l,c,this.host._wallKeyPitch,ah,"fixed-topology");if(t.rejected)return{ok:!1,reason:"wall-metadata"};r.walls=t.walls}const d=[];for(const t of r.rooms||[]){const e=R(t);if(e&&!(e.length<2))for(let t=0;tJSON.stringify([t?.key,t?.cm,t?.a,t?.b]),u=new Map;for(const t of n.walls||[]){const e=p(t);u.set(e,(u.get(e)||0)+1)}const _=[];for(const t of r.walls||[]){const e=p(t),s=u.get(e)||0;s?u.set(e,s-1):_.push(t)}if(Qt(_,d,this.host._wallKeyPitch,ah,n.walls||[]).length)return{ok:!1,reason:"wall-metadata"};const m=this._rszSpaceCandidateGeometry(this.host._space,r);if(!m.ok)return{ok:!1,reason:"physical-geometry"};const g={...this.host._serverCfg,spaces:(this.host._serverCfg?.spaces||[]).map(t=>t?.id===this.host._space?r:t)},f=this._junctionLimitsIntroduced(g,this.host._serverCfg,this.host._space,m.wallGeometry);return f.length?(this.host._rszLimitViolation=f[0],{ok:!1,reason:"junction-limit"}):{ok:!0,value:{preview:{space:this.host._space,sp:r},beforeWalls:n.walls||[],afterWalls:r.walls||[],artifact:m.wallGeometry}}}_rszAcceptPreview(t,e){if(this.host._cfgEpoch++,!t||!e)return;const s=te(e);if(!s)return;const i=`${this.host._space}|${this.host._cfgEpoch}|${t.sp.rooms.length}`;Object.defineProperty(s,"sourceFingerprint",{value:Ot([t.sp,this.host._cellCm,this.host._gridPitch]),enumerable:!1});const o={key:i,value:s};oh(this.host._wallUnionPool,i,o,8),this.host._wallUnionCache=o}_rszSpaceCandidateGeometry(t,e){if(!this.host._serverCfg)return{ok:!1,wallGeometry:null};const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};let i=null;try{return{ok:this.host._checkSpacePhysicalGeometry(s,t,t=>{i=t}).ok,wallGeometry:i}}catch{return{ok:!1,wallGeometry:null}}}_rszSpaceCandidateRenderable(t,e){if(!this.host._serverCfg)return!1;try{const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};return this.host._checkSpacePhysicalGeometry(s,t).ok}catch{return!1}}_rszCandidateRenderable(t){return!!t&&t.space===this.host._space&&this._rszSpaceCandidateRenderable(t.space,t.sp)}_rszEdgeDown(t,e,s){if("resize"!==this.host._tool||this.host._resize.dragging)return;t.stopPropagation(),t.preventDefault();const i=this._rszRooms(),o=this._rszResolution(e,s);if(!o.enabled)return void this.host._showToast(this._rszReasonText(o.reason));rh(t);const a=o.plan,n=this._svgPoint(t),r=`${this.host._space}|${this.host._cfgEpoch}|${i.length}`,h=this.host._wallUnionCache?.key===r?this.host._wallUnionCache.value:null,l=this._rszSnapshot();this.host._resize.begin({pointerId:t.pointerId,start:[n[0],n[1]],roomId:e,plan:a,options:this._rszOptsFor(a.a,a.b),rooms:i,openings:this._rszOpenings(),snapshotIdentity:l,before:JSON.parse(l),wallUnionBefore:h})}_rszReasonText(t){return this.host._t(`resize.disabled.${t}`)}_rszDisabledActivate(t,e){t.stopPropagation(),t.preventDefault(),this.host._showToast(this._rszReasonText(e))}_rszDisabledKey(t,e){"Enter"!==t.key&&" "!==t.key||this._rszDisabledActivate(t,e)}_rszMove(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this._svgPoint(t),s=this.host._resize.move({pointerId:t.pointerId,point:[e[0],e[1]],step:this.host._gridPitch,snap:t=>{const e=this._snap(t);return[e[0],e[1]]},project:(t,e,s,i,o)=>this._rszProjectPreview(t,e,s,i,o),publish:(t,e)=>this._rszAcceptPreview(t,e),measure:(t,e)=>this._rszEdgeLabels(t,e)});if("rejected"===s.kind)return s.notify&&this.host._showToast(this.host._rszLimitViolation?`${this.host._t("resize.limit_stopped")} — `+this._junctionLimitLabel(this.host._rszLimitViolation):this.host._t("resize.preview_failed")),void this.host.requestUpdate();"accepted"===s.kind&&this.host.requestUpdate()}_rszUp(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this.host._resize.finish({pointerId:t.pointerId,currentSnapshotIdentity:this._rszSnapshot(),validatePreview:t=>this._rszCandidateRenderable(t)});if("no-op"===e.kind)return this.host._cfgEpoch++,void this.host.requestUpdate();if("rejected"===e.kind)return this.host._cfgEpoch++,this.host._showToast(this.host._t("resize.commit_failed")),void this.host.requestUpdate();const s=e.preview,i=this.host._serverCfg?.spaces.find(t=>t.id===s.space);i&&(i.rooms=s.sp.rooms,i.openings=s.sp.openings,Array.isArray(s.sp.walls)&&(s.sp.walls.length?i.walls=s.sp.walls:delete i.walls),Array.isArray(s.sp.wall_segments)&&(i.wall_segments=s.sp.wall_segments)),this.host._suppressClick=!0,setTimeout(()=>this.host._suppressClick=!1,0),this._commitPhysicalGeometry(this.host._t("history.resize_room"),e.before),this.host.requestUpdate()}_rszCancelDrag(t){const e=this.host._resize.cancel(this._rszSnapshot(),t);if("no-op"!==e.kind){if(this.host._cfgEpoch++,e.restoreWallUnion){const t=this.host._spaceModel();if(t){const s=`${this.host._space}|${this.host._cfgEpoch}|${t.rooms.length}`,i={key:s,value:e.restoreWallUnion};oh(this.host._wallUnionPool,s,i,8),this.host._wallUnionCache=i}}this.host.requestUpdate()}}_rszPointerCancel(t){this.host._resize.ownsPointer(t.pointerId)&&(t.stopPropagation(),this._rszCancelDrag(t.pointerId))}_rszEdgeLabels(t,e,s=this.host._resize.rooms){const i=s,o=[],a=t.polys[e.roomId]||i?.find(t=>t.id===e.roomId).poly;if(!a||!i)return o;const n=a.length,r=this._rszInnerSpanCms(e.roomId,a,t.polys);for(const t of function(t,e){const s=t.length;return[(e-1+s)%s,(e+1)%s]}(a,e.edge)){const e=a[t],s=a[(t+1)%n],i=r?.[t];o.push({kind:"length",x:(e[0]+s[0])/2,y:(e[1]+s[1])/2,text:null==i?this.host._fmtLen(e,s):ee(i,"mi"===this.host.hass?.config?.unit_system?.length),edge:{a:[e[0],e[1]],b:[s[0],s[1]]}})}const h="mi"===this.host.hass?.config?.unit_system?.length,l=e.roomIds,c=this.host._spaceWalls,d=this.host._physicalBodiesR(),p=this.host._baseVb(),u=this.host._view&&this.host._view.w>0&&this.host._view.h>0?this.host._view:{x:p[0],y:p[1],w:p[2],h:p[3]},[_,m]=this.host._lastValidStageSize||[u.w,u.h],g={...u,stageWidth:Math.max(1,_),stageHeight:Math.max(1,m)},f=this.host._spaceModel(),b=this.host._config?.icon_size??2.5,v=f?se(b>8?2.5:b,f,u.w,this.host._kiosk?this.host._kioskScale.icon:1)*g.stageWidth/100:24,y=Math.max(10,.77*v),$=this.host._t("room.settings_short"),w=y*(1.457+.66*Math.max(1,$.length)*.42);for(const s of l){const a=t.polys[s]||i.find(t=>t.id===s).poly,n=c.length&&f&&this.host._innerRoomContour(f,s)||a,r=d.length?ie(oe(n,d))*Math.pow(this.host._cellCm/this.host._gridPitch,2)/1e4:ae(n,this.host._gridPitch,this.host._cellCm),l=ne(r,h),p=qo({poly:a,edge:e.edgeByRoom[s],text:l,view:g,gearCenter:re(a),gearWidthPx:w,gearHeightPx:y});o.push({kind:"area",roomId:s,x:p.anchor[0],y:p.anchor[1],text:l,placement:p})}return o}_rszInnerSpanCms(t,e,s){const i=Object.keys(s).length?Object.entries(s).map(([t,e])=>({id:t,poly:e})):this.host._spaceModel()?.rooms;if(!i?.length)return null;const o=this.host._spaceWalls;if(!o.length)return null;const a=this.host._openCuts(),n=he(i,t,o,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,ah);if(!n||n.length!==e.length)return null;const r=this.host._cellCm/this.host._gridPitch;return e.map((t,s)=>le(e,s,n)*r)}_renderResizeMeasurements(){const t=this.host._resize.liveLabels;if(!t?.length)return a;const e=t.filter(t=>"length"===t.kind),s=t.filter(t=>"area"===t.kind);return ce`
@@ -852,7 +852,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 {const e=sh(t.target.value);null!=e&&e>0&&(this.host._settingsDialog={...this.host._settingsDialog,glowRadius:e})}} /> + @input=${t=>{const e=ih(t.target.value);null!=e&&e>0&&(this.host._settingsDialog={...this.host._settingsDialog,glowRadius:e})}} /> ${this.host._imperial?this.host._t("gs.unit_ft"):this.host._t("gs.unit_m")}
@@ -1271,7 +1271,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
`:a} -
${this.host._t("gs.about_version",{v:Xr})}
+
${this.host._t("gs.about_version",{v:Zr})}
${this.host._t("gs.about_github")} @@ -1344,7 +1344,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
- `}_renderVacSection(t){const e=this.host._devices.find(e=>e.id===t.devId);if(!e||!this.host._isVacDev(e))return a;const s=e.marker?.vacuum||{},i=this.host._vacAllCamerasFor===e.id,n=this.host._vacSourceResolution(e,i),r=n.entityId,h=n.candidates.find(t=>t.entityId===r)||null,l=r?Is(this.host.hass?.states[r]?.attributes):null,c=!1!==s.live&&("ok"===n.status||"unsupported"===n.status),d=this._vacPlanRoomAnchors(e.space),p=l?Ts(l.rooms,d.map(t=>t.name)):0,_=!!(c&&l&&p>=3),u=Object.keys(s.calibration||{}),m=t=>{const s=this.host._vacEnsureMarker(e);s&&(s.vacuum={...s.vacuum||{},...t},this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate())},g=new Set(e.entities||[]),f=n.candidates.filter(t=>g.has(t.entityId)||t.entityId===s.source),b=i?n.candidates.filter(t=>t.entityId.startsWith("camera.")&&!f.some(e=>e.entityId===t.entityId)):[],v=t=>[t.hasPosition?this.host._t("vac.cap_position"):"",t.hasRooms?this.host._t("vac.cap_rooms_short"):"",t.hasPath?this.host._t("vac.cap_path"):"",t.hasMapId?this.host._t("vac.cap_map"):""].filter(Boolean).join(" · ")||this.host._t("vac.cap_none"),y=t=>o` + `}_renderVacSection(t){const e=this.host._devices.find(e=>e.id===t.devId);if(!e||!this.host._isVacDev(e))return a;const s=e.marker?.vacuum||{},i=this.host._vacAllCamerasFor===e.id,n=this.host._vacSourceResolution(e,i),r=n.entityId,h=n.candidates.find(t=>t.entityId===r)||null,l=r?Is(this.host.hass?.states[r]?.attributes):null,c=!1!==s.live&&("ok"===n.status||"unsupported"===n.status),d=this._vacPlanRoomAnchors(e.space),p=l?Ts(l.rooms,d.map(t=>t.name)):0,u=!!(c&&l&&p>=3),_=Object.keys(s.calibration||{}),m=t=>{const s=this.host._vacEnsureMarker(e);s&&(s.vacuum={...s.vacuum||{},...t},this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate())},g=new Set(e.entities||[]),f=n.candidates.filter(t=>g.has(t.entityId)||t.entityId===s.source),b=i?n.candidates.filter(t=>t.entityId.startsWith("camera.")&&!f.some(e=>e.entityId===t.entityId)):[],v=t=>[t.hasPosition?this.host._t("vac.cap_position"):"",t.hasRooms?this.host._t("vac.cap_rooms_short"):"",t.hasPath?this.host._t("vac.cap_path"):"",t.hasMapId?this.host._t("vac.cap_map"):""].filter(Boolean).join(" · ")||this.host._t("vac.cap_none"),y=t=>o`
- ${_?o``:a} + ${u?o``:a} ${c?o``:a} ${this.host._t("vac.documentation")} @@ -1406,9 +1406,9 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 ${["never","cleaning","always"].map(t=>o` `)} - ${u.length?o`
${Rs(this.host._t("vac.cal_maps"),{maps:u.join(", ")})}
`:a} + ${_.length?o`
${Rs(this.host._t("vac.cal_maps"),{maps:_.join(", ")})}
`:a} `:a} -
`}_vacMapId(t,e,s=this.host._planHass){return this.host._vacMapId(t,e,s)}_vacSaveMatrix(t,e,s,i){const o=this.host._devices.find(e=>e.id===t),a=o?this.host._vacEnsureMarker(o):this.host._serverCfg?.markers?.find(e=>e.id===t);if(!a)return!1;const n={...a.vacuum||{}};return n.source=e,n.calibration={...n.calibration||{},[s]:i.map(t=>Number(t.toFixed(6)))},a.vacuum=n,this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate(),!0}_vacPlanRoomAnchors(t){return(this.host._spaceModelById(t)?.rooms||[]).map(t=>({room:t,poly:R(t)})).filter(({room:t,poly:e})=>t.name&&e).map(({room:t,poly:e})=>{const s=Fs(e);return s?{name:String(t.name),cx:s[0],cy:s[1]}:null}).filter(Boolean)}_vacAutoCalibrate(t){const e=this.host._vacSource(t),s=e?Is(this.host.hass?.states[e]?.attributes):null;if(!e||!s||s.rooms.length<3)return void this.host._showToast(this.host._t("vac.autocal_no_rooms"));const i=this._vacPlanRoomAnchors(t.space),o=As(s.rooms,i);if(!o)return void this.host._showToast(this.host._t("vac.autocal_no_match"));const a=this.host._serverCfg?.spaces?.find(e=>e.id===t.space),n=Number(a?.cell_cm),r=Number.isFinite(n)&&n>0?n:5,h=Ns(o.residual,this.host._gridPitch,r),l=this._vacMapId(t,s);h>Hs?this.host._vacCalConfirm={markerId:t.id,source:e,mapId:l,matrix:o.matrix,rooms:o.matched.length,error:ee(h,"mi"===this.host.hass?.config?.unit_system?.length)}:this._vacSaveMatrix(t.id,e,l,o.matrix)&&this.host._showToast(Rs(this.host._t("vac.autocal_done"),{rooms:String(o.matched.length)}))}_vacApplyCalibrationProposal(t){const e=this.host._vacCalConfirm;if(e){if(this.host._vacCalConfirm=null,t){const t=this.host._devices.find(t=>t.id===e.markerId),s=Ls(e.matrix);if(!t||!s)return;if(this.host._markerDialog=null,t.space!==this.host._space&&!this.host._commitSpace(t.space))return;return void(this.host._vacFit={markerId:e.markerId,source:e.source,mapId:e.mapId,p:s,drag:null})}this._vacSaveMatrix(e.markerId,e.source,e.mapId,e.matrix)&&this.host._showToast(Rs(this.host._t("vac.autocal_done"),{rooms:String(e.rooms)}))}}_vacStartFit(t){const e=this.host._vacSource(t),s=e?Is(this.host.hass?.states[e]?.attributes):null;if(!e||!s)return void this.host._showToast(this.host._t("vac.cal_need_pos"));const i=this._vacMapId(t,s),o=t.marker?.vacuum?.calibration?.[i],a=this.host._spaceModelById(t.space);if(!a)return;const n=a.vb,r=o&&6===o.length&&Ls(o)||Os(s.rooms,n);this.host._markerDialog=null,(t.space===this.host._space||this.host._commitSpace(t.space))&&(this.host._vacFit={markerId:t.id,source:e,mapId:i,p:r,drag:null})}_vacFitSave(){const t=this.host._vacFit;if(!t)return;const e=this._vacSaveMatrix(t.markerId,t.source,t.mapId,Es(t.p));this.host._vacFit=null,e&&this.host._showToast(this.host._t("vac.cal_done"))}_vacFitTurn(t){const e=this.host._vacFit;if(!e)return;const s=Is(this.host.hass?.states[e.source]?.attributes),i=this._vacGhostCentre(s?.rooms||[]),o={...e.p,...t};this.host._vacFit={...e,p:Bs(o,e.p,i[0],i[1])}}_vacGhostCentre(t){const e=[],s=[];for(const i of t)e.push(i.x0??i.cx,i.x1??i.cx),s.push(i.y0??i.cy,i.y1??i.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...s)+Math.max(...s))/2]:[0,0]}_vacDelta(t,e,s){const i=this.host._stageEl,o=i?.clientWidth||1,a=i?.clientHeight||1;return[e/o*t.w,s/a*t.h]}_vacFitPointer(t,e){const s=this.host._vacFit;if(!s)return;if(t.stopPropagation(),"pointerdown"===t.type){const e=t.target,i=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this.host._vacFit={...s,drag:i?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:Number(i.split(",")[0]),fy:Number(i.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:0,fy:0}})}const i=s.drag;if(i){if("pointermove"===t.type){const[o,a]=this._vacDelta(e,t.clientX-i.sx,t.clientY-i.sy);if("move"===i.kind)this.host._vacFit={...s,p:{...i.p0,ox:i.p0.ox+o,oy:i.p0.oy+a}};else{const t=Is(this.host.hass?.states[s.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),n=Es(i.p0),[r,h]=Ws(n,e[0],e[1]),[l,c]=Ws(n,i.fx,i.fy),d=Math.hypot(r-l,h-c)||1,[p,_]=[2*r-l,2*h-c],u=Math.hypot(p+2*o-l,_+2*a-c)/2,m=Math.max(.05,u/d),g={...i.p0,s:i.p0.s*m};this.host._vacFit={...s,p:Bs(g,i.p0,i.fx,i.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this.host._vacFit={...s,drag:null})}}_renderVacFit(t){const e=this.host._vacFit;if(!e)return a;const s=Is(this.host._renderPlanHass?.states?.[e.source]?.attributes);if(!s)return a;const i=Es(e.p),n=[],r=[],h=[];for(const t of s.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>Ws(i,t,e));e.forEach(([t,e])=>{r.push(t),h.push(e)});const[s,o]=Ws(i,t.cx,t.cy);n.push(ce` + `}_vacMapId(t,e,s=this.host._planHass){return this.host._vacMapId(t,e,s)}_vacSaveMatrix(t,e,s,i){const o=this.host._devices.find(e=>e.id===t),a=o?this.host._vacEnsureMarker(o):this.host._serverCfg?.markers?.find(e=>e.id===t);if(!a)return!1;const n={...a.vacuum||{}};return n.source=e,n.calibration={...n.calibration||{},[s]:i.map(t=>Number(t.toFixed(6)))},a.vacuum=n,this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate(),!0}_vacPlanRoomAnchors(t){return(this.host._spaceModelById(t)?.rooms||[]).map(t=>({room:t,poly:R(t)})).filter(({room:t,poly:e})=>t.name&&e).map(({room:t,poly:e})=>{const s=Fs(e);return s?{name:String(t.name),cx:s[0],cy:s[1]}:null}).filter(Boolean)}_vacAutoCalibrate(t){const e=this.host._vacSource(t),s=e?Is(this.host.hass?.states[e]?.attributes):null;if(!e||!s||s.rooms.length<3)return void this.host._showToast(this.host._t("vac.autocal_no_rooms"));const i=this._vacPlanRoomAnchors(t.space),o=As(s.rooms,i);if(!o)return void this.host._showToast(this.host._t("vac.autocal_no_match"));const a=this.host._serverCfg?.spaces?.find(e=>e.id===t.space),n=Number(a?.cell_cm),r=Number.isFinite(n)&&n>0?n:5,h=Ns(o.residual,this.host._gridPitch,r),l=this._vacMapId(t,s);h>Hs?this.host._vacCalConfirm={markerId:t.id,source:e,mapId:l,matrix:o.matrix,rooms:o.matched.length,error:ee(h,"mi"===this.host.hass?.config?.unit_system?.length)}:this._vacSaveMatrix(t.id,e,l,o.matrix)&&this.host._showToast(Rs(this.host._t("vac.autocal_done"),{rooms:String(o.matched.length)}))}_vacApplyCalibrationProposal(t){const e=this.host._vacCalConfirm;if(e){if(this.host._vacCalConfirm=null,t){const t=this.host._devices.find(t=>t.id===e.markerId),s=Ls(e.matrix);if(!t||!s)return;if(this.host._markerDialog=null,t.space!==this.host._space&&!this.host._commitSpace(t.space))return;return void(this.host._vacFit={markerId:e.markerId,source:e.source,mapId:e.mapId,p:s,drag:null})}this._vacSaveMatrix(e.markerId,e.source,e.mapId,e.matrix)&&this.host._showToast(Rs(this.host._t("vac.autocal_done"),{rooms:String(e.rooms)}))}}_vacStartFit(t){const e=this.host._vacSource(t),s=e?Is(this.host.hass?.states[e]?.attributes):null;if(!e||!s)return void this.host._showToast(this.host._t("vac.cal_need_pos"));const i=this._vacMapId(t,s),o=t.marker?.vacuum?.calibration?.[i],a=this.host._spaceModelById(t.space);if(!a)return;const n=a.vb,r=o&&6===o.length&&Ls(o)||Os(s.rooms,n);this.host._markerDialog=null,(t.space===this.host._space||this.host._commitSpace(t.space))&&(this.host._vacFit={markerId:t.id,source:e,mapId:i,p:r,drag:null})}_vacFitSave(){const t=this.host._vacFit;if(!t)return;const e=this._vacSaveMatrix(t.markerId,t.source,t.mapId,Es(t.p));this.host._vacFit=null,e&&this.host._showToast(this.host._t("vac.cal_done"))}_vacFitTurn(t){const e=this.host._vacFit;if(!e)return;const s=Is(this.host.hass?.states[e.source]?.attributes),i=this._vacGhostCentre(s?.rooms||[]),o={...e.p,...t};this.host._vacFit={...e,p:Bs(o,e.p,i[0],i[1])}}_vacGhostCentre(t){const e=[],s=[];for(const i of t)e.push(i.x0??i.cx,i.x1??i.cx),s.push(i.y0??i.cy,i.y1??i.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...s)+Math.max(...s))/2]:[0,0]}_vacDelta(t,e,s){const i=this.host._stageEl,o=i?.clientWidth||1,a=i?.clientHeight||1;return[e/o*t.w,s/a*t.h]}_vacFitPointer(t,e){const s=this.host._vacFit;if(!s)return;if(t.stopPropagation(),"pointerdown"===t.type){const e=t.target,i=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this.host._vacFit={...s,drag:i?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:Number(i.split(",")[0]),fy:Number(i.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:0,fy:0}})}const i=s.drag;if(i){if("pointermove"===t.type){const[o,a]=this._vacDelta(e,t.clientX-i.sx,t.clientY-i.sy);if("move"===i.kind)this.host._vacFit={...s,p:{...i.p0,ox:i.p0.ox+o,oy:i.p0.oy+a}};else{const t=Is(this.host.hass?.states[s.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),n=Es(i.p0),[r,h]=Ws(n,e[0],e[1]),[l,c]=Ws(n,i.fx,i.fy),d=Math.hypot(r-l,h-c)||1,[p,u]=[2*r-l,2*h-c],_=Math.hypot(p+2*o-l,u+2*a-c)/2,m=Math.max(.05,_/d),g={...i.p0,s:i.p0.s*m};this.host._vacFit={...s,p:Bs(g,i.p0,i.fx,i.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this.host._vacFit={...s,drag:null})}}_renderVacFit(t){const e=this.host._vacFit;if(!e)return a;const s=Is(this.host._renderPlanHass?.states?.[e.source]?.attributes);if(!s)return a;const i=Es(e.p),n=[],r=[],h=[];for(const t of s.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>Ws(i,t,e));e.forEach(([t,e])=>{r.push(t),h.push(e)});const[s,o]=Ws(i,t.cx,t.cy);n.push(ce` ${t.name}`)}let l=a;if(s.pos){const[e,o]=Ws(i,s.pos.x,s.pos.y);l=ce``}const c=[];if(r.length){const e=(()=>{const t=i[0]*i[4]-i[1]*i[3];return(e,s)=>[(i[4]*(e-i[2])-i[1]*(s-i[5]))/t,(-i[3]*(e-i[2])+i[0]*(s-i[5]))/t]})(),s=Math.min(...r),o=Math.max(...r),a=Math.min(...h),n=Math.max(...h),l=.022*t.w,d=l/4;for(const[t,i,r,h]of[[s,a,o,n],[o,a,s,n],[o,n,s,a],[s,n,o,a]]){const s=e(r,h);c.push(ce` `)}}return o`t.id===this.host._roomEditId);if(!e)return this.host._roomDialog=!1,void(this.host._roomEditId=null);e.name=this.host._nameSel.trim()||e.name,e.area=this.host._areaSel||null;const s=e.settings||{},i={...s};this.host._roomFill?i.fill_mode=this.host._roomFill:delete i.fill_mode,this.host._roomCustomFill?i.custom_fill=this.host._roomCustomFill:delete i.custom_fill,"glow"===s.fill_mode&&"boolean"!=typeof s.glow&&(i.glow=!0),this.host._roomTempSrc?i.temp_source=this.host._roomTempSrc:delete i.temp_source,this.host._roomHumSrc?i.hum_source=this.host._roomHumSrc:delete i.hum_source,1!==this.host._roomNameScale?i.name_scale=this.host._roomNameScale:delete i.name_scale,1!==this.host._roomLabelScale?i.label_scale=this.host._roomLabelScale:delete i.label_scale,Object.keys(i).length?e.settings=i:delete e.settings,this._saveConfig(),this.host._roomDialog=!1,this.host._roomEditId=null,this.host._nameSel="",this.host._areaSel="",this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate(),this.host._showToast(this.host._t("toast.room_updated"))}_roomSrcCandidates(){const t=this.host._planHass,e=x(this.host._markers),s=this.host._roomSrcFilter.trim().toLowerCase(),i=[];for(const o of Object.values(t.devices)){if("service"===o.entry_type)continue;if(e.devices.has(o.id))continue;const t=(o.name_by_user||o.name||o.id).trim();s&&!t.toLowerCase().includes(s)||i.push({value:"device:"+o.id,label:t,sub:o.model||this.host._t("marker.sub_device")})}for(const[o,a]of Object.entries(t.entities)){if(!o.startsWith("sensor.")||a.hidden)continue;if(S(t,o,e))continue;const n=a.name||t.states[o]?.attributes?.friendly_name||o;s&&!(n+" "+o).toLowerCase().includes(s)||i.push({value:"entity:"+o,label:n,sub:o})}return i.sort((t,e)=>t.label.localeCompare(e.label)),i.slice(0,200)}_roomSrcLabel(t){const e=t.indexOf(":"),s=t.slice(0,e),i=t.slice(e+1);return"device"===s?this.host._fullRegistryHass.devices[i]?.name_by_user||this.host._fullRegistryHass.devices[i]?.name||i:this.host._fullRegistryHass.entities[i]?.name||this.host.hass.states[i]?.attributes?.friendly_name||i}_labelPos(t,e){const s=this.host._layout["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*oh,y:s.y*oh};const i=this._snap(this.host._roomCenter(t));return{x:i[0],y:i[1]}}_labelDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=this._labelPos(e,s);this.host._drag={id:"rl_"+(e.id||""),sx:t.clientX,sy:t.clientY,ox:i.x,oy:i.y,moved:!1},nh(t),this.host._tip=null}_labelMove(t,e,s){const i="rl_"+(e.id||"");if(!this.host._drag||this.host._drag.id!==i)return;const o=this.host._stageEl;if(!o)return;const a=this.host._spaceModelById(s);if(!a)return;const n=a.vb,r=o.getBoundingClientRect(),h=this.host._viewOr(n),l=(t.clientX-this.host._drag.sx)/r.width*h.w,c=(t.clientY-this.host._drag.sy)/r.height*h.h;Math.abs(t.clientX-this.host._drag.sx)+Math.abs(t.clientY-this.host._drag.sy)>3&&(this.host._drag.moved=!0);const d=Rt(this.host._drag.ox+l),p=Rt(this.host._drag.oy+c);this.host._savePos({id:i,space:s},d,p)}_labelUp(t){const e="rl_"+(t.id||"");if(!this.host._drag||this.host._drag.id!==e)return;const s=this.host._drag.moved;this.host._drag=s?this.host._drag:null,s&&window.setTimeout(()=>this.host._drag=null,0)}_labelScale(t){const e=this.host._layout["rl_"+(t.id||"")]?.k;return"number"==typeof e&&Number.isFinite(e)?Math.min(3,Math.max(.5,e)):1}_rlResizeDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=t.target.closest(".roomlabel");if(!i)return;const o=i.getBoundingClientRect(),a=o.left+o.width/2,n=o.top+o.height/2,r=Math.max(8,Math.hypot(t.clientX-a,t.clientY-n));this.host._rlResize={id:"rl_"+(e.id||""),space:s,k0:this._labelScale(e),cx:a,cy:n,d0:r},nh(t)}_rlResizeMove(t){const e=this.host._rlResize;if(!e)return;t.stopPropagation();const s=Math.max(8,Math.hypot(t.clientX-e.cx,t.clientY-e.cy)),i=Math.min(3,Math.max(.5,e.k0*(s/e.d0))),o=this.host._layout[e.id];if(o)this.host._layout={...this.host._layout,[e.id]:{...o,k:i}};else{const t=e.id.slice(3),s=this.host._spaceModelById(e.space);if(!s)return;const o=s.rooms.find(e=>e.id===t);if(!o)return;const a=this._labelPos(o,e.space);this.host._layout={...this.host._layout,[e.id]:{s:e.space,x:a.x/oh,y:a.y/oh,k:i}}}this.host._dirtyPos.add(e.id)}_rlResizeUp(){this.host._rlResize&&(this.host._rlResize=null,this.host._persistLayout())}_renderRoomGear(t,e,s){if(!t.id)return a;let i=null;if(t.poly?(i=this.host._gearPtCache.get(t.poly)||null,i||(i=re(t.poly),this.host._gearPtCache.set(t.poly,i))):null!=t.x&&null!=t.y&&(i=[t.x+(t.w||0)/2,t.y+(t.h||0)/2]),!i)return a;const n=(i[0]-s.x)/s.w*100,r=(i[1]-s.y)/s.h*100;return o``}_resetRoomDialogFields(){this.host._roomEditId=null,this.host._roomFill="",this.host._roomCustomFill=null,this.host._roomTempSrc="",this.host._roomHumSrc="",this.host._roomSrcOpen=null,this.host._roomSrcFilter="",this.host._roomNameScale=1,this.host._roomLabelScale=1}_openRoomEdit(t){if(!t.id)return;this.host._roomEditId=t.id,this.host._nameSel=t.name||"",this.host._areaSel=t.area||"",this.host._roomFill="glow"===t.settings?.fill_mode?"":t.settings?.fill_mode||"";const e=t.settings?.custom_fill;this.host._roomCustomFill=e&&"object"==typeof e?ps(e,ds(this.host._curSpaceCfg).customFill):null,this.host._roomTempSrc=t.settings?.temp_source||"",this.host._roomHumSrc=t.settings?.hum_source||"",this.host._roomNameScale=Gs(t.settings?.name_scale),this.host._roomLabelScale=Gs(t.settings?.label_scale),this.host._roomSrcOpen=null,this.host._roomSrcFilter="",this.host._roomDialog=!0}_roomSettingsFromDialog(){const t={};return this.host._roomFill&&(t.fill_mode=this.host._roomFill),this.host._roomCustomFill&&(t.custom_fill=this.host._roomCustomFill),this.host._roomTempSrc&&(t.temp_source=this.host._roomTempSrc),this.host._roomHumSrc&&(t.hum_source=this.host._roomHumSrc),1!==this.host._roomNameScale&&(t.name_scale=this.host._roomNameScale),1!==this.host._roomLabelScale&&(t.label_scale=this.host._roomLabelScale),Object.keys(t).length?t:null}_saveRoomEdit(){const t=this.host._curSpaceCfg,e=t?.rooms.find(t=>t.id===this.host._roomEditId);if(!e)return this.host._roomDialog=!1,void(this.host._roomEditId=null);e.name=this.host._nameSel.trim()||e.name,e.area=this.host._areaSel||null;const s=e.settings||{},i={...s};this.host._roomFill?i.fill_mode=this.host._roomFill:delete i.fill_mode,this.host._roomCustomFill?i.custom_fill=this.host._roomCustomFill:delete i.custom_fill,"glow"===s.fill_mode&&"boolean"!=typeof s.glow&&(i.glow=!0),this.host._roomTempSrc?i.temp_source=this.host._roomTempSrc:delete i.temp_source,this.host._roomHumSrc?i.hum_source=this.host._roomHumSrc:delete i.hum_source,1!==this.host._roomNameScale?i.name_scale=this.host._roomNameScale:delete i.name_scale,1!==this.host._roomLabelScale?i.label_scale=this.host._roomLabelScale:delete i.label_scale,Object.keys(i).length?e.settings=i:delete e.settings,this._saveConfig(),this.host._roomDialog=!1,this.host._roomEditId=null,this.host._nameSel="",this.host._areaSel="",this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate(),this.host._showToast(this.host._t("toast.room_updated"))}_roomSrcCandidates(){const t=this.host._planHass,e=x(this.host._markers),s=this.host._roomSrcFilter.trim().toLowerCase(),i=[];for(const o of Object.values(t.devices)){if("service"===o.entry_type)continue;if(e.devices.has(o.id))continue;const t=(o.name_by_user||o.name||o.id).trim();s&&!t.toLowerCase().includes(s)||i.push({value:"device:"+o.id,label:t,sub:o.model||this.host._t("marker.sub_device")})}for(const[o,a]of Object.entries(t.entities)){if(!o.startsWith("sensor.")||a.hidden)continue;if(S(t,o,e))continue;const n=a.name||t.states[o]?.attributes?.friendly_name||o;s&&!(n+" "+o).toLowerCase().includes(s)||i.push({value:"entity:"+o,label:n,sub:o})}return i.sort((t,e)=>t.label.localeCompare(e.label)),i.slice(0,200)}_roomSrcLabel(t){const e=t.indexOf(":"),s=t.slice(0,e),i=t.slice(e+1);return"device"===s?this.host._fullRegistryHass.devices[i]?.name_by_user||this.host._fullRegistryHass.devices[i]?.name||i:this.host._fullRegistryHass.entities[i]?.name||this.host.hass.states[i]?.attributes?.friendly_name||i}_labelPos(t,e){const s=this.host._layout["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*ah,y:s.y*ah};const i=this._snap(this.host._roomCenter(t));return{x:i[0],y:i[1]}}_labelDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=this._labelPos(e,s);this.host._drag={id:"rl_"+(e.id||""),sx:t.clientX,sy:t.clientY,ox:i.x,oy:i.y,moved:!1},rh(t),this.host._tip=null}_labelMove(t,e,s){const i="rl_"+(e.id||"");if(!this.host._drag||this.host._drag.id!==i)return;const o=this.host._stageEl;if(!o)return;const a=this.host._spaceModelById(s);if(!a)return;const n=a.vb,r=o.getBoundingClientRect(),h=this.host._viewOr(n),l=(t.clientX-this.host._drag.sx)/r.width*h.w,c=(t.clientY-this.host._drag.sy)/r.height*h.h;Math.abs(t.clientX-this.host._drag.sx)+Math.abs(t.clientY-this.host._drag.sy)>3&&(this.host._drag.moved=!0);const d=Rt(this.host._drag.ox+l),p=Rt(this.host._drag.oy+c);this.host._savePos({id:i,space:s},d,p)}_labelUp(t){const e="rl_"+(t.id||"");if(!this.host._drag||this.host._drag.id!==e)return;const s=this.host._drag.moved;this.host._drag=s?this.host._drag:null,s&&window.setTimeout(()=>this.host._drag=null,0)}_labelScale(t){const e=this.host._layout["rl_"+(t.id||"")]?.k;return"number"==typeof e&&Number.isFinite(e)?Math.min(3,Math.max(.5,e)):1}_rlResizeDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=t.target.closest(".roomlabel");if(!i)return;const o=i.getBoundingClientRect(),a=o.left+o.width/2,n=o.top+o.height/2,r=Math.max(8,Math.hypot(t.clientX-a,t.clientY-n));this.host._rlResize={id:"rl_"+(e.id||""),space:s,k0:this._labelScale(e),cx:a,cy:n,d0:r},rh(t)}_rlResizeMove(t){const e=this.host._rlResize;if(!e)return;t.stopPropagation();const s=Math.max(8,Math.hypot(t.clientX-e.cx,t.clientY-e.cy)),i=Math.min(3,Math.max(.5,e.k0*(s/e.d0))),o=this.host._layout[e.id];if(o)this.host._layout={...this.host._layout,[e.id]:{...o,k:i}};else{const t=e.id.slice(3),s=this.host._spaceModelById(e.space);if(!s)return;const o=s.rooms.find(e=>e.id===t);if(!o)return;const a=this._labelPos(o,e.space);this.host._layout={...this.host._layout,[e.id]:{s:e.space,x:a.x/ah,y:a.y/ah,k:i}}}this.host._dirtyPos.add(e.id)}_rlResizeUp(){this.host._rlResize&&(this.host._rlResize=null,this.host._persistLayout())}_renderRoomGear(t,e,s){if(!t.id)return a;let i=null;if(t.poly?(i=this.host._gearPtCache.get(t.poly)||null,i||(i=re(t.poly),this.host._gearPtCache.set(t.poly,i))):null!=t.x&&null!=t.y&&(i=[t.x+(t.w||0)/2,t.y+(t.h||0)/2]),!i)return a;const n=(i[0]-s.x)/s.w*100,r=(i[1]-s.y)/s.h*100;return o` -
`}_markerValueBadgeFields(t){return Qs({touched:t.valueBadgeTouched,originalHas:t.originalHasValueBadge,original:t.originalValueBadge,enabled:t.valueBadgeEnabled,source:t.valueBadgeSource,position:t.valueBadgePosition})}_markerDraft(t){if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return null;const e=as(t.room),s=Xe(t.binding,t.devId,()=>"__hp_device_preview__"),i=this.host._markers.find(e=>e.id===s||e.id===t.devId),o=es(t.binding,t.controls,this.host._bindingEntities(t.binding)),a=this._effectiveStoredTapAction(t),n={id:s,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&1.5!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle||null,...this._markerTapActionFields(t),...this._markerToggleEntityFields(t),tap_target:"run"===a&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:o.length?o:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=sh(t.glowRadius);return null==e||e<=0?null:Math.round(this.host._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:t.hideFromPlan,vacuum:i?.vacuum||null};return("virtual"===t.binding||t.room)&&(n.space=e?.space||("virtual"===t.binding?this.host._space:null),n.area=e?.area||null,n.room_id=e?.roomId||null),n}_markerPreviewDevice(t){const e=this._markerDraft(t);if(!e)return null;const s=`${this.host._haRegistry.revision}\n${Ot(this.host._markers)}\n${JSON.stringify(e)}`;if(this.host._markerPreviewMemo?.key===s)return this.host._markerPreviewMemo.device;const i=ti({hass:this.host.hass,registry:this.host._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this.host._areaToSpace).map(([t,e])=>[t,e.space])),marker:e,siblingMarkers:this.host._markers,settings:this.host._settings,excluded:this.host._excluded,showAll:this.host._showAll,firstSpaceId:this.host._model[0]?.id||this.host._space,loc:t=>this.host._t(t),iconRules:this.host._iconRules});return this.host._markerPreviewMemo={key:s,device:i},i}_markerPreviewDevices(t){const e=this.host._markerPreviewDevicesMemo;if(e?.base===this.host._devices&&e.preview===t)return e.devices;const s=[...this.host._devices.filter(e=>e.id!==t.id),t];return this.host._markerPreviewDevicesMemo={base:this.host._devices,preview:t,devices:s},s}_toggleIntent(t,e=this.host._devices){return this.host._toggleIntent(t,e)}_toggleIntentForDialog(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s=this._markerPreviewDevices(e);return this._toggleIntent(e,s)}_toggleStateText(t,e){return this.host._toggleStateText(t,e)}_toggleConfirmationStateText(t){return this.host._toggleConfirmationStateText(t)}_toggleConfirmationLines(t){return this.host._toggleConfirmationLines(t)}_toggleHintLines(t){if(!t)return[];const e=t=>this.host._t(`marker.toggle_effect_${t.replace("-","_")}`),s=t=>this.host._t(`marker.toggle_skip_${t.replace("-","_")}`);return ei(t,{single:t=>{if("via"in t&&"virtual-light"===t.via)return this.host._t("marker.virtual_light_target",{name:t.name});const e=t.entityId||("ref"in t?t.ref:""),s=t.name||e;return this.host._t("marker.toggle_hint_single",{name:s,id:e})},group:t=>this.host._t("marker.toggle_hint_group",{count:t.length,names:t.map(t=>`${t.name} (${t.entityId})`).join(", ")}),currentNext:(t,s)=>"virtual-light"===t.via?this.host._t("marker.virtual_light_current",{state:this.host._t("on"===t.state?"marker.virtual_light_state_on":"marker.virtual_light_state_off"),effect:e(s)}):this.host._t("marker.toggle_hint_current",{state:this._toggleStateText(t.entityId,t.state),effect:e(s)}),groupCurrentNext:(t,s)=>this.host._t("marker.toggle_hint_group_current",{on:t.filter(t=>"on"===t.state).length,count:t.length,effect:e(s)}),skipped:t=>this.host._t("marker.toggle_hint_skipped",{count:t.length,targets:t.map(t=>{const e=t.entityId||t.ref;return`${t.name||e} (${e}: ${s(t.reason)})`}).join(", ")}),none:t=>this.host._t(`marker.toggle_none_${t.replaceAll("-","_")}`)})}_effectiveStoredTapAction(t,e){return t.tapActionTouched?t.tapAction:ss(t.originalHasTapAction?t.originalTapAction:null,e)}_effectiveMarkerTapAction(t,e=this._markerPreviewDevice(t)){return this._effectiveStoredTapAction(t,e?.primary?.split(".")[0])}_announceToggleDraft(t){const e=this._markerPreviewDevice(t),s=t.tapActionTouched?t:{...t,tapAction:this._effectiveMarkerTapAction(t,e)},i="toggle"===s.tapAction?this._toggleHintLines(this._toggleIntentForDialog(s)).join(" "):"";return{...s,tapHintAnnouncement:i}}_valueBadgeForBinding(t,e){const s={...t,binding:e,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0},i=this._markerPreviewDevice(s);if(!i)return{valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0};const o=[...this.host._devices.filter(t=>t.id!==i.id),i],a=Qe(this.host._planHass,i,o),n=ts(this.host._planHass,i,a);return{valueBadgeEnabled:t.valueBadgeEnabled&&!!n,valueBadgeSource:n,valueBadgeTouched:!0}}_markerSpatialSource(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s={...e,hidden:!1};return si(ii(this.host._planHass,[...this.host._devices.filter(t=>t.id!==s.id),s],null,this.host._virtualLights).filter(t=>t.device.id===s.id))}_markerAutoHasSpatialSource(t){const e={...t,lightRole:"auto",lightRoleTouched:!0},s=this._markerPreviewDevice(e);return!!s&&oi(this.host._planHass,{...s})}_setMarkerLightRole(t){const e=this.host._markerDialog;e&&(this.host._markerDialog={...e,lightRole:t,lightRoleTouched:!0})}_controlRefInfo(t){if(!t.startsWith("marker:"))return{label:this.host.hass.states[t]?.attributes?.friendly_name||t,sub:t,icon:t.startsWith("light.")?"mdi:lightbulb":"mdi:toggle-switch",warning:!this.host._planEntityAvailable(t)};const e=t.slice(7),s=this.host._markers.find(t=>t.id===e),i=this.host._devices.find(t=>t.id===e);if(!s||s.removed||!0!==s.is_light)return{label:this.host._t("marker.control_missing_label"),sub:`${this.host._t("marker.control_broken")} (${t})`,icon:"mdi:alert-outline",warning:!0};const o=this.host._serverCfg?.spaces.find(t=>t.id===(i?.space||s.space)),a=!!i&&ai(i).length>0;return{label:s.name||i?.name||e,sub:[o?.title||i?.space||s.space,s.room_id||i?.area,a?"":this.host._t("marker.control_passive")].filter(Boolean).join(" · "),icon:s.icon||i?.icon||"mdi:lightbulb-outline",warning:!0===s.hidden||!0===i?.hidden}}_valueBadgeCandidateLabel(t){const e=t.source;if("derived_lqi"===e.kind)return this.host._t("marker.value_badge_lqi");if("derived_marker_state"===e.kind)return this.host._t("marker.value_badge_marker_state",{name:t.label});if("entity_state"===e.kind)return this.host._t("marker.value_badge_state",{name:t.label});const s=this.host.hass.states[e.entity_id]?.attributes?.friendly_name||this.host._fullRegistryHass.entities[e.entity_id]?.name||e.entity_id;return this.host._t(`marker.value_badge_attr_${e.attribute}`,{name:s})}_controlCandidates(t){const e=this._markerDraft(t)?.id||t.devId||"",s=[],i=new Set;for(const t of this.host._markers){if(t.id===e||t.removed||t.hidden||!0!==t.is_light)continue;if("virtual"!==t.binding&&"active"!==this.host._bindingStatus(t.binding).kind)continue;const o=this.host._devices.find(e=>e.id===t.id);if(!o||o.hidden)continue;const a=this._controlRefInfo(`marker:${t.id}`);for(const t of ai(o))i.add(t);s.push({value:`marker:${t.id}`,label:a.label,sub:a.sub,icon:a.icon,search:`${a.label} ${a.sub} ${t.id} ${ai(o).join(" ")}`.toLowerCase()})}const o=Object.keys(this.host.hass.states||{}).filter(e=>ni(e)&&!i.has(e)&&ri(t.binding,[e],this.host._bindingEntities(t.binding)).length>0&&this.host._planEntityAvailable(e)).map(t=>{const e=this._controlRefInfo(t);return{value:t,label:e.label,sub:t,icon:e.icon,search:`${e.label} ${t}`.toLowerCase()}}),a=t.controlsFilter.trim().toLowerCase();return[...s,...o].filter(e=>!t.controls.includes(e.value)&&(!a||e.search.includes(a))).slice(0,12).map(({search:t,...e})=>e)}_addControlRef(t,e){if(e.startsWith("marker:")){const s=this._markerDraft(t)?.id||t.devId||"";if(!s)return void this.host._showToast(this.host._t("toast.marker_binding_required"));const i=e.slice(7),o=this._markerDraft(t),a=o?[...this.host._markers.filter(t=>t.id!==s),o]:this.host._markers;if(hi(a,s,i))return void this.host._showToast(this.host._t("toast.marker_control_cycle"))}this.host._markerDialog=this._announceToggleDraft({...t,controls:[...t.controls,e],controlsFilter:""})}_setMarkerGlowMode(t){const e=this.host._markerDialog;if(!e)return;if("auto"===t)return void(this.host._markerDialog={...e,glowMode:t,glowTouched:!0});const s=this._markerSpatialSource(e),i=li(s?this.host._planHass.states[s.eid]:void 0,null,this.host._fillColors.glow_light.c),o=!e.glowColorDrafted,a="fixed"===t&&!e.glowBrightnessDrafted;this.host._markerDialog={...e,glowMode:t,glowColor:o?i.c:e.glowColor,glowBrightness:a?Math.max(1,Math.round(100*i.bri)):e.glowBrightness,glowColorDrafted:!0,glowBrightnessDrafted:e.glowBrightnessDrafted||"fixed"===t,glowTouched:!0}}_renderMarkerDialog(){const t=this.host._markerDialog,e="virtual"===t.bindingMode,s=this._bindingCandidates();this.host._bindingEntities(t.binding);const i=e?null:this.host._bindingStatus(t.binding),n=!e&&this.host._bindingHasHaPage(t.binding),r=this._markerPreviewDevice(t),h=this._effectiveMarkerTapAction(t,r),l=r?ds(this.host._serverCfg?.spaces.find(t=>t.id===r.space)):null,c=r?this._markerPreviewDevices(r):this.host._devices,d="toggle"===h&&r?this._toggleIntent(r,c):null,p=this._toggleHintLines(d),_=r?ci(this.host._planHass,r,{liveStates:!1!==this.host._config?.live_states,showTemperature:!1!==this.host._config?.show_temperature,showSignal:l?.showLqi??!1!==this.host._config?.show_signal,designPreview:!0,activityRuntime:this.host._activityRt.get(r.id),lightDevices:c,registryHass:this.host._fullRegistryHass,reducedMotion:this.host._reducedMotion}):null,u=r?Qe(this.host._planHass,r,c):[],m=r?ts(this.host._planHass,r,u):null,g=t.valueBadgeTouched?t.valueBadgeEnabled:!!_?.valueBadge,f=t.valueBadgeTouched?t.valueBadgeSource:_?.valueBadge?.source||t.valueBadgeSource,b=t.valueBadgeTouched?t.valueBadgePosition:_?.valueBadge?.position||t.valueBadgePosition,v=di(f),y=!!f&&!u.some(t=>t.key===v),$=u.find(t=>t.key===v),w=_?.valueSource?_.valueSource.attribute?`attr:${_.valueSource.eid}:${_.valueSource.attribute}`:`state:${_.valueSource.eid}`:"",k=this._markerAutoHasSpatialSource(t),x=!!r&&pi(this.host._planHass,{...r}),S=_i(t.lightRole,k,x,t.glowMode),M=!S.sourceExists,D=!S.fromSourceEnabled,C=S.passive,P=S.effectiveMode,I="never"===t.lightRole?this.host._t("marker.glow_disabled_never"):"auto"!==t.lightRole||k?C?this.host._t("marker.glow_passive_hint"):this.host._t("marker.glow_disabled_no_entity"):this.host._t("marker.glow_disabled_auto"),T=r?ai(r):[],R=r&&ui(r)||"",z=!!t.lightEntity&&!T.includes(t.lightEntity),F=r?mi(r):[],A=!!t.toggleEntity&&!F.includes(t.toggleEntity),N="toggle"===h?this._toggleIntentForDialog({...t,toggleEntity:"",toggleEntityTouched:!0}):null,H=N?[...N.targets,...N.skippedTargets].map(t=>t.entityId||("ref"in t?t.ref:"")).filter(Boolean).join(", "):"",L=(()=>{if(e)return null;const i=s.find(e=>e.value===t.binding);if(i)return i.label;const[o,a]=t.binding.split(":");return"device"===o?this.host._fullRegistryHass.devices[a]?.name_by_user||this.host._fullRegistryHass.devices[a]?.name||a:this.host._fullRegistryHass.entities[a]?.name||this.host.hass.states[a]?.attributes?.friendly_name||a})();return o``}_markerValueBadgeFields(t){return Qs({touched:t.valueBadgeTouched,originalHas:t.originalHasValueBadge,original:t.originalValueBadge,enabled:t.valueBadgeEnabled,source:t.valueBadgeSource,position:t.valueBadgePosition})}_markerValueSourceFields(t){return ti({touched:t.valueSourceTouched,originalHas:t.originalHasValueSource,original:t.originalValueSource,source:t.valueSource})}_markerDraft(t){if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return null;const e=as(t.room),s=Xe(t.binding,t.devId,()=>"__hp_device_preview__"),i=this.host._markers.find(e=>e.id===s||e.id===t.devId),o=es(t.binding,t.controls,this.host._bindingEntities(t.binding)),a=this._effectiveStoredTapAction(t),n={id:s,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&1.5!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle||null,...this._markerTapActionFields(t),...this._markerToggleEntityFields(t),tap_target:"run"===a&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:o.length?o:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),...this._markerValueSourceFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=ih(t.glowRadius);return null==e||e<=0?null:Math.round(this.host._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:t.hideFromPlan,vacuum:i?.vacuum||null};return("virtual"===t.binding||t.room)&&(n.space=e?.space||("virtual"===t.binding?this.host._space:null),n.area=e?.area||null,n.room_id=e?.roomId||null),n}_markerPreviewDevice(t){const e=this._markerDraft(t);if(!e)return null;const s=`${this.host._haRegistry.revision}\n${Ot(this.host._markers)}\n${JSON.stringify(e)}`;if(this.host._markerPreviewMemo?.key===s)return this.host._markerPreviewMemo.device;const i=ei({hass:this.host.hass,registry:this.host._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this.host._areaToSpace).map(([t,e])=>[t,e.space])),marker:e,siblingMarkers:this.host._markers,settings:this.host._settings,excluded:this.host._excluded,showAll:this.host._showAll,firstSpaceId:this.host._model[0]?.id||this.host._space,loc:t=>this.host._t(t),iconRules:this.host._iconRules});return this.host._markerPreviewMemo={key:s,device:i},i}_markerPreviewDevices(t){const e=this.host._markerPreviewDevicesMemo;if(e?.base===this.host._devices&&e.preview===t)return e.devices;const s=[...this.host._devices.filter(e=>e.id!==t.id),t];return this.host._markerPreviewDevicesMemo={base:this.host._devices,preview:t,devices:s},s}_toggleIntent(t,e=this.host._devices){return this.host._toggleIntent(t,e)}_toggleIntentForDialog(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s=this._markerPreviewDevices(e);return this._toggleIntent(e,s)}_toggleStateText(t,e){return this.host._toggleStateText(t,e)}_toggleConfirmationStateText(t){return this.host._toggleConfirmationStateText(t)}_toggleConfirmationLines(t){return this.host._toggleConfirmationLines(t)}_toggleHintLines(t){if(!t)return[];const e=t=>this.host._t(`marker.toggle_effect_${t.replace("-","_")}`),s=t=>this.host._t(`marker.toggle_skip_${t.replace("-","_")}`);return si(t,{single:t=>{if("via"in t&&"virtual-light"===t.via)return this.host._t("marker.virtual_light_target",{name:t.name});const e=t.entityId||("ref"in t?t.ref:""),s=t.name||e;return this.host._t("marker.toggle_hint_single",{name:s,id:e})},group:t=>this.host._t("marker.toggle_hint_group",{count:t.length,names:t.map(t=>`${t.name} (${t.entityId})`).join(", ")}),currentNext:(t,s)=>"virtual-light"===t.via?this.host._t("marker.virtual_light_current",{state:this.host._t("on"===t.state?"marker.virtual_light_state_on":"marker.virtual_light_state_off"),effect:e(s)}):this.host._t("marker.toggle_hint_current",{state:this._toggleStateText(t.entityId,t.state),effect:e(s)}),groupCurrentNext:(t,s)=>this.host._t("marker.toggle_hint_group_current",{on:t.filter(t=>"on"===t.state).length,count:t.length,effect:e(s)}),skipped:t=>this.host._t("marker.toggle_hint_skipped",{count:t.length,targets:t.map(t=>{const e=t.entityId||t.ref;return`${t.name||e} (${e}: ${s(t.reason)})`}).join(", ")}),none:t=>this.host._t(`marker.toggle_none_${t.replaceAll("-","_")}`)})}_effectiveStoredTapAction(t,e){return t.tapActionTouched?t.tapAction:ss(t.originalHasTapAction?t.originalTapAction:null,e)}_effectiveMarkerTapAction(t,e=this._markerPreviewDevice(t)){return this._effectiveStoredTapAction(t,e?.primary?.split(".")[0])}_announceToggleDraft(t){const e=this._markerPreviewDevice(t),s=t.tapActionTouched?t:{...t,tapAction:this._effectiveMarkerTapAction(t,e)},i="toggle"===s.tapAction?this._toggleHintLines(this._toggleIntentForDialog(s)).join(" "):"";return{...s,tapHintAnnouncement:i}}_valueBadgeForBinding(t,e){const s={...t,binding:e,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0},i=this._markerPreviewDevice(s);if(!i)return{valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0};const o=[...this.host._devices.filter(t=>t.id!==i.id),i],a=Qe(this.host._planHass,i,o),n=ts(this.host._planHass,i,a);return{valueBadgeEnabled:t.valueBadgeEnabled&&!!n,valueBadgeSource:n,valueBadgeTouched:!0}}_markerSpatialSource(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s={...e,hidden:!1};return ii(oi(this.host._planHass,[...this.host._devices.filter(t=>t.id!==s.id),s],null,this.host._virtualLights).filter(t=>t.device.id===s.id))}_markerAutoHasSpatialSource(t){const e={...t,lightRole:"auto",lightRoleTouched:!0},s=this._markerPreviewDevice(e);return!!s&&ai(this.host._planHass,{...s})}_setMarkerLightRole(t){const e=this.host._markerDialog;e&&(this.host._markerDialog={...e,lightRole:t,lightRoleTouched:!0})}_controlRefInfo(t){if(!t.startsWith("marker:"))return{label:this.host.hass.states[t]?.attributes?.friendly_name||t,sub:t,icon:t.startsWith("light.")?"mdi:lightbulb":"mdi:toggle-switch",warning:!this.host._planEntityAvailable(t)};const e=t.slice(7),s=this.host._markers.find(t=>t.id===e),i=this.host._devices.find(t=>t.id===e);if(!s||s.removed||!0!==s.is_light)return{label:this.host._t("marker.control_missing_label"),sub:`${this.host._t("marker.control_broken")} (${t})`,icon:"mdi:alert-outline",warning:!0};const o=this.host._serverCfg?.spaces.find(t=>t.id===(i?.space||s.space)),a=!!i&&ni(i).length>0;return{label:s.name||i?.name||e,sub:[o?.title||i?.space||s.space,s.room_id||i?.area,a?"":this.host._t("marker.control_passive")].filter(Boolean).join(" · "),icon:s.icon||i?.icon||"mdi:lightbulb-outline",warning:!0===s.hidden||!0===i?.hidden}}_valueBadgeCandidateLabel(t){const e=t.source;if("derived_lqi"===e.kind)return this.host._t("marker.value_badge_lqi");if("derived_marker_state"===e.kind)return this.host._t("marker.value_badge_marker_state",{name:t.label});if("entity_state"===e.kind)return this.host._t("marker.value_badge_state",{name:t.label});const s=this.host.hass.states[e.entity_id]?.attributes?.friendly_name||this.host._fullRegistryHass.entities[e.entity_id]?.name||e.entity_id;return this.host._t(`marker.value_badge_attr_${e.attribute}`,{name:s})}_controlCandidates(t){const e=this._markerDraft(t)?.id||t.devId||"",s=[],i=new Set;for(const t of this.host._markers){if(t.id===e||t.removed||t.hidden||!0!==t.is_light)continue;if("virtual"!==t.binding&&"active"!==this.host._bindingStatus(t.binding).kind)continue;const o=this.host._devices.find(e=>e.id===t.id);if(!o||o.hidden)continue;const a=this._controlRefInfo(`marker:${t.id}`);for(const t of ni(o))i.add(t);s.push({value:`marker:${t.id}`,label:a.label,sub:a.sub,icon:a.icon,search:`${a.label} ${a.sub} ${t.id} ${ni(o).join(" ")}`.toLowerCase()})}const o=Object.keys(this.host.hass.states||{}).filter(e=>ri(e)&&!i.has(e)&&hi(t.binding,[e],this.host._bindingEntities(t.binding)).length>0&&this.host._planEntityAvailable(e)).map(t=>{const e=this._controlRefInfo(t);return{value:t,label:e.label,sub:t,icon:e.icon,search:`${e.label} ${t}`.toLowerCase()}}),a=t.controlsFilter.trim().toLowerCase();return[...s,...o].filter(e=>!t.controls.includes(e.value)&&(!a||e.search.includes(a))).slice(0,12).map(({search:t,...e})=>e)}_addControlRef(t,e){if(e.startsWith("marker:")){const s=this._markerDraft(t)?.id||t.devId||"";if(!s)return void this.host._showToast(this.host._t("toast.marker_binding_required"));const i=e.slice(7),o=this._markerDraft(t),a=o?[...this.host._markers.filter(t=>t.id!==s),o]:this.host._markers;if(li(a,s,i))return void this.host._showToast(this.host._t("toast.marker_control_cycle"))}this.host._markerDialog=this._announceToggleDraft({...t,controls:[...t.controls,e],controlsFilter:""})}_setMarkerGlowMode(t){const e=this.host._markerDialog;if(!e)return;if("auto"===t)return void(this.host._markerDialog={...e,glowMode:t,glowTouched:!0});const s=this._markerSpatialSource(e),i=ci(s?this.host._planHass.states[s.eid]:void 0,null,this.host._fillColors.glow_light.c),o=!e.glowColorDrafted,a="fixed"===t&&!e.glowBrightnessDrafted;this.host._markerDialog={...e,glowMode:t,glowColor:o?i.c:e.glowColor,glowBrightness:a?Math.max(1,Math.round(100*i.bri)):e.glowBrightness,glowColorDrafted:!0,glowBrightnessDrafted:e.glowBrightnessDrafted||"fixed"===t,glowTouched:!0}}_renderMarkerDialog(){const t=this.host._markerDialog,e="virtual"===t.bindingMode,s=this._bindingCandidates();this.host._bindingEntities(t.binding);const i=e?null:this.host._bindingStatus(t.binding),n=!e&&this.host._bindingHasHaPage(t.binding),r=this._markerPreviewDevice(t),h=this._effectiveMarkerTapAction(t,r),l=r?ds(this.host._serverCfg?.spaces.find(t=>t.id===r.space)):null,c=r?this._markerPreviewDevices(r):this.host._devices,d="toggle"===h&&r?this._toggleIntent(r,c):null,p=this._toggleHintLines(d),u=r?di(this.host._planHass,r,{liveStates:!1!==this.host._config?.live_states,showTemperature:!1!==this.host._config?.show_temperature,showSignal:l?.showLqi??!1!==this.host._config?.show_signal,designPreview:!0,activityRuntime:this.host._activityRt.get(r.id),lightDevices:c,registryHass:this.host._fullRegistryHass,reducedMotion:this.host._reducedMotion}):null,_=r?Qe(this.host._planHass,r,c):[],m=r?ts(this.host._planHass,r,_):null,g=t.valueBadgeTouched?t.valueBadgeEnabled:!!u?.valueBadge,f=t.valueBadgeTouched?t.valueBadgeSource:u?.valueBadge?.source||t.valueBadgeSource,b=t.valueBadgeTouched?t.valueBadgePosition:u?.valueBadge?.position||t.valueBadgePosition,v=pi(f),y=!!f&&!_.some(t=>t.key===v),$=_.find(t=>t.key===v),w=pi(t.valueSource),k=!(!t.valueSource||w&&_.some(t=>t.key===w)),x=_.find(t=>t.key===w),S=u?.valueSource?.sourceKey||"",M=this._markerAutoHasSpatialSource(t),D=!!r&&ui(this.host._planHass,{...r}),C=_i(t.lightRole,M,D,t.glowMode),P=!C.sourceExists,I=!C.fromSourceEnabled,T=C.passive,R=C.effectiveMode,z="never"===t.lightRole?this.host._t("marker.glow_disabled_never"):"auto"!==t.lightRole||M?T?this.host._t("marker.glow_passive_hint"):this.host._t("marker.glow_disabled_no_entity"):this.host._t("marker.glow_disabled_auto"),F=r?ni(r):[],A=r&&mi(r)||"",N=!!t.lightEntity&&!F.includes(t.lightEntity),H=r?gi(r):[],L=!!t.toggleEntity&&!H.includes(t.toggleEntity),O="toggle"===h?this._toggleIntentForDialog({...t,toggleEntity:"",toggleEntityTouched:!0}):null,E=O?[...O.targets,...O.skippedTargets].map(t=>t.entityId||("ref"in t?t.ref:"")).filter(Boolean).join(", "):"",B=(()=>{if(e)return null;const i=s.find(e=>e.value===t.binding);if(i)return i.label;const[o,a]=t.binding.split(":");return"device"===o?this.host._fullRegistryHass.devices[a]?.name_by_user||this.host._fullRegistryHass.devices[a]?.name||a:this.host._fullRegistryHass.entities[a]?.name||this.host.hass.states[a]?.attributes?.friendly_name||a})();return o`this._closeMarkerDialog()}>
@@ -1948,7 +1948,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
@@ -1964,7 +1964,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
${"ha"===t.bindingMode?o` ${t.bindingOpen?o`
@@ -1973,7 +1973,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 @input=${e=>this.host._markerDialog={...t,bindingFilter:e.target.value}} />
${s.map(e=>o`
{const s={...t,binding:e.value,bindingOpen:!1,controls:es(e.value,t.controls,this.host._bindingEntities(e.value)),autoIcon:this.host._autoIconForBinding(e.value)};this.host._markerDialog=this._announceToggleDraft({...s,...this._valueBadgeForBinding(s,e.value)})}}> + @click=${()=>{const s={...t,binding:e.value,bindingOpen:!1,controls:es(e.value,t.controls,this.host._bindingEntities(e.value)),autoIcon:this.host._autoIconForBinding(e.value)};this.host._markerDialog=this._announceToggleDraft({...s,...this._valueBadgeForBinding(s,e.value),valueSource:null,valueSourceTouched:!0})}}> ${e.label}${e.sub}
`)} ${s.length?a:o`
${this.host._t("marker.nothing_found")}
`} @@ -1996,28 +1996,28 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 - ${"toggle"===h&&(F.length>1||A)?o`
+ ${"toggle"===h&&(H.length>1||L)?o`
${this._help("marker.toggle_entity.help")}
- ${A?o`

+ ${L?o`

- ${this.host._t("marker.toggle_entity_missing",{entity:t.toggleEntity,fallback:H||this.host._t("marker.toggle_entity_none")})} + ${this.host._t("marker.toggle_entity_missing",{entity:t.toggleEntity,fallback:E||this.host._t("marker.toggle_entity_none")})}

`:a}
`:a} ${"toggle"===h?o`
@@ -2072,7 +2072,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
+ ${this.host._t(M?"marker.light_role_auto_yes":"marker.light_role_auto_no")} @@ -2082,55 +2082,55 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
- ${"always"===t.lightRole&&(T.length>1||z)?o`
+ ${"always"===t.lightRole&&(F.length>1||N)?o`
${this._help("marker.light_entity.help")}
- ${z?o`

+ ${N?o`

- ${this.host._t("marker.light_entity_missing",{entity:t.lightEntity,fallback:R||"—"})} + ${this.host._t("marker.light_entity_missing",{entity:t.lightEntity,fallback:A||"—"})}

`:a}
`:a} -
+
${this.host._t("marker.glow_color_label")}${this._help("marker.glow_mode.help")}
- ${"auto"!==P?o`
+ ${"auto"!==R?o`
{this.host._markerDialog={...t,glowMode:P,glowColor:e.detail.color,glowColorDrafted:!0,glowTouched:!0}}}> - ${"fixed"===P?o` + .disabled=${P} + @hp-color-opacity-change=${e=>{this.host._markerDialog={...t,glowMode:R,glowColor:e.detail.color,glowColorDrafted:!0,glowTouched:!0}}}> + ${"fixed"===R?o` ${this.host._t("marker.glow_brightness")} - ${this._rangeInput(1,100,1,t.glowBrightness,e=>{this.host._markerDialog={...t,glowMode:P,glowBrightness:e,glowBrightnessDrafted:!0,glowTouched:!0}},M,this.host._t("marker.glow_brightness"))} + ${this._rangeInput(1,100,1,t.glowBrightness,e=>{this.host._markerDialog={...t,glowMode:R,glowBrightness:e,glowBrightnessDrafted:!0,glowTouched:!0}},P,this.host._t("marker.glow_brightness"))} ${Math.round(t.glowBrightness)}%`:a}
`:a}
@@ -2141,15 +2141,15 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
this.host._markerDialog={...t,glowRadius:e.target.value}} /> ${this.host._imperial?this.host._t("gs.unit_ft"):this.host._t("gs.unit_m")}
- ${M||C?o`

- ${I} + ${P||T?o`

+ ${z}

`:a} @@ -2170,11 +2170,38 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 -

${this.host._t(Qr[t.display])}

+

${this.host._t(th[t.display])}

+ ${"value"===t.display?o`
+
+ + ${this._help("marker.value_source.help")} +
+ + ${x?o`

${x.technical}

`:a} + ${e?o`

+ + ${this.host._t("marker.preview.reason.value_virtual")} +

`:k?o`

+ ${this.host._t("marker.value_source_missing_hint")} +

`:a} +
`:a} ${"static_icon"===t.display&&this.host._bindingHasAlarm(t.binding)?o`
${this.host._t("marker.static_alarm_warning")} @@ -2182,12 +2209,12 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
${this.host._t("marker.value_badge_title")}${this._help("marker.value_badge.help")} ${"static_icon"===t.display?o`

${this.host._t("marker.value_badge_static")} -

`:u.length||t.valueBadgeSource?a:o`

+

`:_.length||t.valueBadgeSource?a:o`

${this.host._t("marker.value_badge_empty")}

`} ${g?o` @@ -2196,11 +2223,11 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548 ${this._help("marker.value_badge_source.help")}
`:a} - ${_?o` + .deviceName=${t.name.trim()||r?.name||B||""}> `:o`
${this.host._t("marker.preview.select_source")} @@ -2276,7 +2303,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b548
${t.pdfs.map(t=>o` - ${t.name} + ${t.name} this._removeMarkerPdf(t.url)}>`)} `}
- `}}export{rh as EDITOR_RUNTIME_FINGERPRINT,hh as HouseplanEditorRuntime}; + `}}export{hh as EDITOR_RUNTIME_FINGERPRINT,lh as HouseplanEditorRuntime}; diff --git a/dist/houseplan-assets/houseplan-onboarding-runtime-eqCB-Ax0.js b/dist/houseplan-assets/houseplan-onboarding-runtime-BP9ODa6H.js similarity index 98% rename from dist/houseplan-assets/houseplan-onboarding-runtime-eqCB-Ax0.js rename to dist/houseplan-assets/houseplan-onboarding-runtime-BP9ODa6H.js index 7cb8b8a2..1edbd1cc 100644 --- a/dist/houseplan-assets/houseplan-onboarding-runtime-eqCB-Ax0.js +++ b/dist/houseplan-assets/houseplan-onboarding-runtime-BP9ODa6H.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";import{bT as t,bU as s,bV as e,bW as o,aq as a,bX as l,c0 as i,bY as h,bZ as c,b_ as n,b$ as r,A as p,b as _,cS as d,ap as u,ar as g,cT as m}from"./houseplan-card-B5mDyjuC.js";import{i as b,c as $,e as v,r as f,a as y,b as w,s as D,t as S}from"./backdrop-pick-DFFLwJ_d.js";const k=1e3,C=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},M="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";class F{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),c=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,n="none"===h.fill?{...c||e,a:0}:c;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:n,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:n,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:c,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await $(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await v(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return f(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??p}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return _`
${this.host._t("space.loading")}
`;const s=t.saved||[];if(!s.length)return _`
${this.host._t("space.no_saved")}
`;return _`
+globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";import{bT as t,bU as s,bV as e,bW as o,aq as a,bX as l,c0 as i,bY as h,bZ as c,b_ as n,b$ as r,A as p,b as _,cT as d,ap as u,ar as g,cU as m}from"./houseplan-card-wtxKVHkp.js";import{i as b,c as $,e as v,r as f,a as y,b as w,s as D,t as S}from"./backdrop-pick-TMl_2iBT.js";const k=1e3,C=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},M="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";class F{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),c=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,n="none"===h.fill?{...c||e,a:0}:c;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:n,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:n,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:c,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await $(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await v(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return f(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??p}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return _`
${this.host._t("space.loading")}
`;const s=t.saved||[];if(!s.length)return _`
${this.host._t("space.no_saved")}
`;return _`
${s.map(s=>{return _`
diff --git a/dist/houseplan-card.js b/dist/houseplan-card.js index 52c03bb8..70c7b46e 100644 --- a/dist/houseplan-card.js +++ b/dist/houseplan-card.js @@ -1 +1 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b40916c4f480b150ad894b2e5bea3a62b54827233bab2fc96ed6866eedf04f7c";try{await import("./houseplan-assets/houseplan-card-B5mDyjuC.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)} +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="3d6cccf5714d17cd4ec8f516229a0621f28ee00b65b7d84fb542ddc353bc7f5f";try{await import("./houseplan-assets/houseplan-card-wtxKVHkp.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)} diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index a2fe44f8..6124601c 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -214,11 +214,13 @@ Built from the registries (`_buildDevices`), rules carried over 1-to-1 from the ## Live data -- Value satellite: `src/device-value-badge.ts` resolves one explicit - `marker.value_badge` source/position, or projects the legacy automatic - temperature/humidity label when that field is absent. The resolver owns HA - formatting, units, unavailable state, candidate discovery and deterministic - recommendation; renderers consume only `ResolvedDevicePresentation.valueBadge`. +- Value sources: `src/device-value-badge.ts` owns candidate discovery, source + keys, HA formatting, units and unavailable handling for both an explicit + `marker.value_source` inside the **Value + state** face and an explicit + `marker.value_badge` satellite. Absence of `value_source` keeps the legacy + automatic face resolver; absence of `value_badge` projects the legacy + automatic temperature/humidity satellite. Renderers consume only the + corresponding fields of `ResolvedDevicePresentation`. - LQI (zigbee): the average over `*_linkquality` entities → label under the icon; color via `lqiColor()`: ≤40 red → ≥180 green (hsl gradient). The room average is shown in the room tooltip. The same tooltip includes the formatted clean-floor area (inner contour for @@ -303,6 +305,11 @@ Explicit settings override the global legacy temperature gate; explicit off suppresses legacy output. Bottom badges stack above system LQI, and a derived LQI badge de-duplicates that system row. `hp-device-preview` fits and centres the complete face bounding box rather than allowing satellites to clip. +An optional `marker.value_source` selects the same source kinds for the inner +face when `display: value`. Missing explicit data renders a dash without +falling back to another source or the icon; absence/`null` preserves the old +automatic face selection. Derived marker references share the same rewrite and +space-transfer seam as controls and value badges. `normalizeDeviceDisplay()` is the mandatory compatibility gate for every consumer and maps legacy `ripple` to `icon_ripple`. `markerLqiBand()` remains marker-only semantic metadata for accessibility, while `markerLqiColor()` delegates to the shared diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index bed56589..f7af8d13 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +- **Value + state** can now show the exact reading the user needs instead of + relying only on the automatically chosen entity state. The Device editor + offers the same states, useful attributes, LQI and linked-light values as + the adjacent value badge; for example, a cover can show `42 %` position + instead of `Open`. Existing markers keep their previous automatic behaviour, + and tapping the value still runs the same action + ([#378](https://github.com/Matysh/houseplan-card/issues/378)). - Glow in `houseplan-space-card` no longer rebuilds its light graph and wall scene from scratch on every Home Assistant tick: the static path now shares the caching machinery of the full card (light-graph cache, an LRU pool of diff --git a/docs/CHANGELOG.ru.md b/docs/CHANGELOG.ru.md index 9ccfd016..11649a00 100755 --- a/docs/CHANGELOG.ru.md +++ b/docs/CHANGELOG.ru.md @@ -8,6 +8,13 @@ ## Не выпущено +- В режиме **«Значение + состояние»** теперь можно выбрать нужный показатель, + а не полагаться только на автоматически выбранное состояние сущности. + Редактор устройства предлагает те же состояния, полезные атрибуты, LQI и + значения связанных источников света, что внешний бейдж: например, положение + cover отображается как `42 %` вместо `Открыто`. Старые маркеры сохраняют + прежнее автоматическое поведение, а нажатие по значению выполняет то же + действие ([#378](https://github.com/Matysh/houseplan-card/issues/378)). - Glow в `houseplan-space-card` больше не пересобирает граф света и сцену стен с нуля на каждый тик Home Assistant: статический путь получил кэш-механику полной карты (кэш графа света, LRU-пул барьерных сцен — движущаяся дверь diff --git a/docs/CONFIG-COMPATIBILITY.md b/docs/CONFIG-COMPATIBILITY.md index 865c7727..d32d27ee 100644 --- a/docs/CONFIG-COMPATIBILITY.md +++ b/docs/CONFIG-COMPATIBILITY.md @@ -419,6 +419,17 @@ space transfer remaps an internal target and disables/counts a link whose target is outside the transfer. Older clients ignore the field and may erase it if they reconstruct the same marker after a downgrade. +`marker.value_source` is an optional explicit source for the inner face of a +`display: value` marker. Absence or `null` preserves the historical automatic +entity-state choice; an object uses exactly the same discriminated source +contract and formatter as `marker.value_badge.source`. A missing explicit +source stays selected and renders `—` rather than silently falling back. The +top-level schema remains lossless: unchanged future literals round-trip, while +new or changed values receive strict delta validation. Marker-id rewrites and +full/space transfer preserve, remap or report/drop `derived_marker_state.ref` +through the same reference seam as controls and value badges. Older clients +ignore the field and may erase it if they reconstruct the marker. + ## Persistent manual virtual-light state The exact `virtual` + `is_light:true` + `tap_action:toggle` combination has a diff --git a/docs/DEVICE-PRESENTATION.md b/docs/DEVICE-PRESENTATION.md index 1f100062..6dbaa289 100644 --- a/docs/DEVICE-PRESENTATION.md +++ b/docs/DEVICE-PRESENTATION.md @@ -64,7 +64,7 @@ mutation evidence в одном pull request. | F07 | `live_states:false`, не alarm | `face.live_states_disabled` | neutral/base icon; ordinary activity off | normal | `device-presentation-policy-live-gate`; `presentation-row-contract` | | F08 | `static_icon` | `face.static` | neutral/base icon; state/RGB/value/metrics/pulse/vacuum off | normal | `device-presentation-policy-static`; `presentation-row-contract` | | F09 | value + один scalar source | `content.value` | HA-formatted full Text face | normal | `device-presentation-policy-value`; `device-long-value-ellipsis-restored` | -| F10 | value + missing/unavailable/non-scalar | `content.value_fallback_icon` + `content.value_no_state/non_scalar` | icon fallback и точная причина | normal | `device-presentation-policy-value`; `presentation-row-contract` | +| F10 | value auto + missing/unavailable/non-scalar | `content.value_fallback_icon` + `content.value_no_state/non_scalar` | icon fallback и точная причина | normal | `device-presentation-policy-value`; `presentation-row-contract` | | F11 | value + несколько равноправных sources | `content.value_ambiguous_sources` | icon fallback `value_ambiguous_sources` | normal | `device-presentation-policy-value`; `presentation-row-contract` | | F12 | value + virtual marker | `content.value_virtual` | icon fallback `value_virtual` | normal | `device-presentation-policy-value`; `presentation-row-contract` | | F13 | dynamic icon + известный morph | `diagnostics.dynamic_icon` | state icon; действующее cover override сохранено | normal | `device-presentation-policy-diagnostics`; `presentation-row-contract` | @@ -72,6 +72,7 @@ mutation evidence в одном pull request. | F15 | legacy automatic metric | `diagnostics.metrics_enabled` | прежняя temperature/humidity эвристика без записи config | normal | `device-presentation-policy-diagnostics`; `presentation-row-contract` | | F16 | LQI 0/40, 41/179, 180+ | `diagnostics.lqi_low/mid/high` | low/mid/high и continuous canonical colour | normal | `device-marker-lqi-low-boundary-shifted`; `presentation-row-contract` | | F17 | vacuum dynamic/static | `diagnostics.vacuum_live/vacuum_static` | live overlay только у видимого dynamic face | normal | `device-presentation-policy-diagnostics`; `presentation-row-contract` | +| F18 | value explicit + missing/unavailable/non-scalar | `content.value` + `content.value_no_state/non_scalar` | выбранный source сохраняется, Text face показывает `—` без auto/icon fallback | normal | `device-presentation-policy-value`; `presentation-row-contract` | ## Activity и pulse diff --git a/docs/USER-GUIDE.md b/docs/USER-GUIDE.md index 8cba69af..cfe3b97d 100644 --- a/docs/USER-GUIDE.md +++ b/docs/USER-GUIDE.md @@ -652,6 +652,12 @@ alarm remains clear. The four display choices are icon + state; icon + state + activity; value + state; and always-static icon. A separate value badge can show an entity state, useful attribute, average LQI or linked light state on any side of the marker. +For **value + state**, the Device editor also offers **Value source**. Keep +**Automatic (as before)** for the legacy choice, or select one of those same +readings—for example, cover position—to replace the icon with `42 %` rather +than `Open`. A temporarily unavailable saved source stays selected and shows +`—` until it recovers; it is not silently replaced. Changing this source never +changes what a click or tap does. Text and adjacent values are sections of the same shell. They shrink to a readable floor and then expand the shell; they are never ellipsized. The complete visible value capsule is one hover and action target: clicking or diff --git a/docs/USER-GUIDE.ru.md b/docs/USER-GUIDE.ru.md index 3dfc6d6f..b1449490 100644 --- a/docs/USER-GUIDE.ru.md +++ b/docs/USER-GUIDE.ru.md @@ -1050,12 +1050,17 @@ Hover включается только после события от наст |---|---:|---:|---:|---:| | Значок + состояние | Да | Да | Только тревога | Отдельные компактные °/% и LQI возможны | | Значок + состояние и активность | Да | Да | Да | Отдельные компактные °/% и LQI возможны | -| Значение + состояние | Если значение получить нельзя или источник неоднозначен | Да | Только тревога | Температура/влажность либо числовое или текстовое состояние, отформатированное HA | +| Значение + состояние | При автоматическом выборе — если значение получить нельзя или источник неоднозначен; при явном — только для virtual marker | Да | Только тревога | Автоматическое либо явно выбранное состояние, атрибут, LQI или состояние связанного источника света | | Всегда статичный значок | Да | Нейтральная в цветах текущей темы | Нет | Нет | В режиме «Значение вместо иконки» поддерживаются и числа, и локализованные HA -текстовые состояния. Если источник отсутствует, недоступен или неоднозначен, -карточка возвращается к иконке; предпросмотр объясняет причину. Длинный текст +текстовые состояния. Поле **«Источник значения»** позволяет оставить +**«Автоматически (как раньше)»** либо выбрать тот же показатель, который +доступен внешнему бейджу: например, положение cover даст `42 %` вместо +`Открыто`. В автоматическом режиме отсутствующий, недоступный или неоднозначный +источник по-прежнему возвращает иконку. Явно выбранный временно недоступный +источник остаётся выбранным и показывает `—` до восстановления, не подменяясь +другим. Выбор не меняет действие по клику или тапу. Длинный текст уменьшается до читаемого минимума, затем общий shell расширяется: многоточие и скрытие хвоста не используются. LQI остаётся отдельной строкой под shell. diff --git a/scripts/model-invariants.mjs b/scripts/model-invariants.mjs index 02cb4d6f..8467f233 100644 --- a/scripts/model-invariants.mjs +++ b/scripts/model-invariants.mjs @@ -178,6 +178,17 @@ export function checkReferences({ config, layout = {} } = {}, { notes = [] } = { : 'активного маркера-источника не существует'); } } + const valueSource = marker?.value_source; + if (valueSource?.kind === 'derived_marker_state') { + const ref = String(valueSource.ref ?? ''); + const target = ref.startsWith('marker:') ? ref.slice('marker:'.length) : ''; + if (!target || !activeLightMarkerIds.has(target)) { + add('marker_value_source', markerId, ref || '?', + target && activeMarkerIds.has(target) + ? 'маркер-цель не является источником света' + : 'активного маркера-цели не существует'); + } + } } for (const [key, position] of Object.entries(layout || {})) { diff --git a/scripts/smoke-links.mjs b/scripts/smoke-links.mjs index 73d940de..b10ad99c 100644 --- a/scripts/smoke-links.mjs +++ b/scripts/smoke-links.mjs @@ -27,6 +27,17 @@ /** @type {SmokeLink[]} */ export const SMOKE_LINKS = [ + { + symbols: [ + 'resolveValueSource', 'valueSourceWriteFields', 'valueBadgeCandidates', + 'valueBadgeSourceKey', 'resolveDevicePresentation', + ], + smokes: ['smoke_value_face_source.mjs'], + because: 'the #378 production-bundle scenario observes the shared source resolver through ' + + 'the real Device dialog, draft preview, persistence, both plan renderers, unavailable ' + + 'recovery, action dispatch and binding reset; those DOM effects do not call the pure ' + + 'source helpers by name', + }, { symbols: ['roomClimateKey', 'roomClimateMap'], smokes: ['smoke_room_climate_placement.mjs'], diff --git a/src/device-presentation.ts b/src/device-presentation.ts index d10a826c..f8177f14 100644 --- a/src/device-presentation.ts +++ b/src/device-presentation.ts @@ -22,7 +22,10 @@ import { import { resolveToggleIntent, toggleCoverEntity } from './device-toggle'; import type { DevItem } from './types'; import { safeStoredColor } from './color'; -import { resolveDeviceValueBadge, type ResolvedValueBadge } from './device-value-badge'; +import { + resolveDeviceValueBadge, resolveValueSource, valueBadgeSourceKey, + type ResolvedValueBadge, +} from './device-value-badge'; import { DEVICE_PULSE_DEFAULT_SCALE, resolveDevicePulse, type ResolvedDevicePulse, } from './device-pulse'; @@ -80,10 +83,11 @@ export interface ResolvedPresentationSource { } export interface ResolvedValueSource { - kind: 'temperature' | 'humidity' | 'entity'; + kind: 'temperature' | 'humidity' | 'entity' | 'derived_lqi' | 'derived_marker_state'; eid: string; attribute?: string; text: string; + sourceKey: string; } export interface PresentationExplanation { @@ -470,24 +474,72 @@ function resolveValue( d: DevItem, sources: ResolvedPresentationSources, showTemperature: boolean, -): { source: ResolvedValueSource | null; text: string | null; fallback: ValueFallbackReason | null } { - if (d.virtual) return { source: null, text: null, fallback: 'value_virtual' }; + markerStates: readonly { ref: string; on: boolean; name: string }[] = [], +): { + source: ResolvedValueSource | null; + text: string | null; + fullText: string | null; + fallback: ValueFallbackReason | null; + explicit: boolean; +} { + if (d.virtual) { + return { source: null, text: null, fullText: null, fallback: 'value_virtual', explicit: false }; + } + const explicitSource = d.marker?.value_source; + if (explicitSource) { + const resolved = resolveValueSource(hass, d, explicitSource, markerStates); + const kind: ResolvedValueSource['kind'] = explicitSource.kind === 'derived_lqi' + ? 'derived_lqi' + : explicitSource.kind === 'derived_marker_state' + ? 'derived_marker_state' + : explicitSource.kind === 'entity_attribute' && explicitSource.attribute.includes('temperature') + ? 'temperature' + : explicitSource.kind === 'entity_attribute' && explicitSource.attribute.includes('humidity') + ? 'humidity' : 'entity'; + const eid = explicitSource.kind === 'entity_state' || explicitSource.kind === 'entity_attribute' + ? explicitSource.entity_id + : explicitSource.kind === 'derived_marker_state' ? explicitSource.ref : 'derived:lqi'; + const attribute = explicitSource.kind === 'entity_attribute' + ? explicitSource.attribute : undefined; + return { + source: { + kind, eid, attribute, text: resolved.text, + sourceKey: valueBadgeSourceKey(explicitSource), + }, + text: resolved.text, + fullText: resolved.fullText, + fallback: resolved.failure === 'non_scalar' ? 'value_non_scalar' + : resolved.failure === 'missing' ? 'value_no_state' : null, + explicit: true, + }; + } if (showTemperature) { const climate = firstClimateTemperature(hass, d); if (climate) { return { - source: { kind: 'temperature', eid: climate.eid, attribute: 'current_temperature', text: climate.text }, + source: { + kind: 'temperature', eid: climate.eid, attribute: 'current_temperature', text: climate.text, + sourceKey: `attr:${climate.eid}:current_temperature`, + }, text: climate.text, + fullText: climate.text, fallback: null, + explicit: false, }; } const temp = firstTemperature(hass, d); if (temp) { - return { source: { kind: 'temperature', eid: temp.eid, text: temp.text }, text: temp.text, fallback: null }; + return { + source: { kind: 'temperature', eid: temp.eid, text: temp.text, sourceKey: `state:${temp.eid}` }, + text: temp.text, fullText: temp.text, fallback: null, explicit: false, + }; } const hum = firstHumidity(hass, d); if (hum) { - return { source: { kind: 'humidity', eid: hum.eid, text: hum.text }, text: hum.text, fallback: null }; + return { + source: { kind: 'humidity', eid: hum.eid, text: hum.text, sourceKey: `state:${hum.eid}` }, + text: hum.text, fullText: hum.text, fallback: null, explicit: false, + }; } } // Passive marker sources deliberately use a `marker:*` identity. They are @@ -510,13 +562,20 @@ function resolveValue( return { source: null, text: null, + fullText: null, fallback: valueEids.length ? 'value_ambiguous_sources' : 'value_no_state', + explicit: false, }; } const eid = valueEids[0]; const value = validStateValue(hass, eid); - if (value.fallback) return { source: null, text: null, fallback: value.fallback }; - return { source: { kind: 'entity', eid, text: value.text }, text: value.text, fallback: null }; + if (value.fallback) { + return { source: null, text: null, fullText: null, fallback: value.fallback, explicit: false }; + } + return { + source: { kind: 'entity', eid, text: value.text, sourceKey: `state:${eid}` }, + text: value.text, fullText: value.text, fallback: null, explicit: false, + }; } function signatureOf( @@ -530,7 +589,8 @@ function signatureOf( ...sources.visualSources.map((source) => `${source.role}:${source.eid}`), ...sources.criticalSources.map((source) => `critical:${source.eid}`), ].sort(); - const value = valueSource ? `${valueSource.kind}:${valueSource.eid}:${valueSource.attribute || ''}` : 'none'; + const value = valueSource + ? `${valueSource.kind}:${valueSource.sourceKey}:${valueSource.eid}:${valueSource.attribute || ''}` : 'none'; return [binding, sources.sourceKind, ...rows, `value:${value}`].join('\n'); } @@ -595,6 +655,13 @@ export function resolveDevicePresentation( hass, d, options.lightDevices || [d], options.lightSources, options.registryHass || hass, ); + const needsMarkerState = d.marker?.value_source?.kind === 'derived_marker_state' + || d.marker?.value_badge?.source?.kind === 'derived_marker_state'; + const markerStateGraph = options.lightSources + || (needsMarkerState ? resolvedLightSources(hass, options.lightDevices || [d]) : []); + const markerStates = markerStateGraph + .filter((source) => source.key.startsWith('marker:')) + .map((source) => ({ ref: source.key, on: source.on, name: source.device.name })); const status = d.bindingStatus; const haDisabled = status?.kind === 'ha_disabled'; const orphaned = status?.kind === 'orphaned'; @@ -623,8 +690,8 @@ export function resolveDevicePresentation( const controllerFace = sources.sourceKind === 'controls' || (configuredController && sources.sourceKind !== 'light' && sources.sourceKind !== 'cover'); const value = staticIcon && options.sourceDetails === false - ? { source: null, text: null, fallback: null } - : resolveValue(hass, d, sources, options.showTemperature); + ? { source: null, text: null, fullText: null, fallback: null, explicit: false } + : resolveValue(hass, d, sources, options.showTemperature, markerStates); const sourceSignature = signatureOf(d, sources, value.source); const activitySignature = activitySourceSignature(hass, d, sources); const rt = options.activityRuntime; @@ -664,17 +731,12 @@ export function resolveDevicePresentation( && !!d.primary && isHumEntity(hass, d.primary) ? humFor(hass, d.entities) : null; const lqi = policy.metrics && options.showSignal && !d.virtual ? lqiFor(hass, d.entities) : null; - const markerStateGraph = options.lightSources || (d.marker?.value_badge?.source?.kind === 'derived_marker_state' - ? resolvedLightSources(hass, options.lightDevices || [d]) - : []); const valueBadge = resolveDeviceValueBadge(hass, d, { showTemperature: options.showTemperature, showSignal: options.showSignal, display, effectiveHidden, - markerStates: markerStateGraph - .filter((source) => source.key.startsWith('marker:')) - .map((source) => ({ ref: source.key, on: source.on, name: source.device.name })), + markerStates, }); const realVisualSource = sources.visualSources.find((source) => !source.eid.startsWith('marker:')); const actEid = sources.sourceKind === 'cover' @@ -757,8 +819,8 @@ export function resolveDevicePresentation( display, icon, valueText, - valueFullText: valueText, - fallbackReason: display === 'value' && !valueText ? value.fallback : null, + valueFullText: policy.face === 'value' ? value.fullText : null, + fallbackReason: display === 'value' ? value.fallback : null, activity, activityGeneration: rt?.gen || 1, pulse, @@ -766,7 +828,8 @@ export function resolveDevicePresentation( tempText: temp == null ? null : String(temp), humText: hum == null ? null : String(hum), valueBadge, - lqiText: lqi == null || valueBadge?.isLqi ? null : String(lqi), + lqiText: lqi == null || valueBadge?.isLqi || value.source?.kind === 'derived_lqi' + ? null : String(lqi), lqiColor: lqi == null ? null : markerLqiColor(lqi), lqiBand: lqi == null ? null : markerLqiBand(lqi), lightColor, diff --git a/src/device-value-badge.ts b/src/device-value-badge.ts index 73de73cf..27ab6987 100644 --- a/src/device-value-badge.ts +++ b/src/device-value-badge.ts @@ -31,6 +31,8 @@ export interface ResolvedValueBadge { availability: 'available' | 'unavailable' | 'missing'; isLqi: boolean; tone: 'temperature' | 'humidity' | 'lqi' | 'default'; + /** Why the configured source could not produce a scalar value. */ + failure: 'missing' | 'non_scalar' | null; } export interface ValueBadgeCandidate { @@ -90,7 +92,8 @@ export function valueBadgeSourceKey(source: ValueBadgeSource | null | undefined) if (source.kind === 'entity_state') return `state:${source.entity_id}`; if (source.kind === 'entity_attribute') return `attr:${source.entity_id}:${source.attribute}`; if (source.kind === 'derived_marker_state') return `marker:${source.ref}`; - return 'derived:lqi'; + if (source.kind === 'derived_lqi') return 'derived:lqi'; + return ''; } export function valueBadgeSourceFromKey(key: string): ValueBadgeSource | null { @@ -153,42 +156,81 @@ function sourceTone(source: ValueBadgeSource): ResolvedValueBadge['tone'] { return 'default'; } -function resolveSource( +export function resolveValueSource( hass: any, d: DevItem, source: ValueBadgeSource, markerStates: readonly { ref: string; on: boolean; name: string }[] = [], ): Omit { let text: string | null = null; let label = ''; let availability: ResolvedValueBadge['availability'] = 'available'; + let failure: ResolvedValueBadge['failure'] = null; if (source.kind === 'entity_state') { label = entityName(hass, source.entity_id); const st = hass?.states?.[source.entity_id]; - if (!st) availability = 'missing'; - else if (!sourceRegistryEnabled(hass, source.entity_id)) availability = 'unavailable'; - else if (['unknown', 'unavailable'].includes(String(st.state).toLowerCase())) availability = 'unavailable'; + if (!st) { + availability = 'missing'; + failure = 'missing'; + } else if (!sourceRegistryEnabled(hass, source.entity_id)) { + availability = 'unavailable'; + failure = 'missing'; + } else if (!['string', 'number', 'boolean'].includes(typeof st.state)) { + availability = 'unavailable'; + failure = 'non_scalar'; + } else if (['unknown', 'unavailable'].includes(String(st.state).toLowerCase())) { + availability = 'unavailable'; + failure = 'missing'; + } else { const value = hassValue(hass, source.entity_id); text = value ? valueWithUnit(value, String(st.attributes?.unit_of_measurement || '')) : null; - if (!text) availability = 'unavailable'; + if (!text) { + availability = 'unavailable'; + failure = 'missing'; + } } } else if (source.kind === 'entity_attribute') { label = `${attrLabel(source.attribute)} · ${entityName(hass, source.entity_id)}`; const st = hass?.states?.[source.entity_id]; - if (!st) availability = 'missing'; - else if (!sourceRegistryEnabled(hass, source.entity_id)) availability = 'unavailable'; + const hasAttribute = !!st && source.attribute in (st.attributes || {}); + const raw = hasAttribute ? st.attributes?.[source.attribute] : undefined; + if (!st) { + availability = 'missing'; + failure = 'missing'; + } else if (!sourceRegistryEnabled(hass, source.entity_id)) { + availability = 'unavailable'; + failure = 'missing'; + } else if (['unknown', 'unavailable'].includes(String(st.state).toLowerCase())) { + availability = 'unavailable'; + failure = 'missing'; + } else if (!hasAttribute) { + availability = 'unavailable'; + failure = 'missing'; + } else if (!['string', 'number', 'boolean'].includes(typeof raw)) { + availability = 'unavailable'; + failure = 'non_scalar'; + } else { text = formatAttribute(hass, source.entity_id, source.attribute); - if (text == null) availability = 'unavailable'; + if (text == null) { + availability = 'unavailable'; + failure = 'missing'; + } } } else if (source.kind === 'derived_lqi') { label = 'LQI'; const lqi = lqiFor(hass, d.entities); - if (lqi == null) availability = 'unavailable'; + if (lqi == null) { + availability = 'unavailable'; + failure = 'missing'; + } else text = String(lqi); } else { label = 'Light state'; const match = markerStates.find((item) => item.ref === source.ref); - if (!match) availability = 'missing'; + if (!match) { + availability = 'missing'; + failure = 'missing'; + } else { const state = match.on ? 'on' : 'off'; const loc = hass?.localize?.(`state.default.${state}`); @@ -205,6 +247,7 @@ function resolveSource( availability, isLqi: source.kind === 'derived_lqi', tone: sourceTone(source), + failure, }; } @@ -252,10 +295,11 @@ export function resolveDeviceValueBadge( availability: 'missing', isLqi: false, tone: 'default', + failure: 'missing', }; return { configured: true, enabled: true, position: stored.position || 'right', - ...resolveSource(hass, d, stored.source, options.markerStates), + ...resolveValueSource(hass, d, stored.source, options.markerStates), }; } if (!options.showTemperature || options.display === 'value') return null; @@ -283,6 +327,7 @@ export function resolveDeviceValueBadge( ? `${attrLabel(source.attribute)} · ${entityName(hass, source.entity_id)}` : source.kind === 'entity_state' ? entityName(hass, source.entity_id) : '', text: legacyText, fullText: legacyText, availability: 'available', isLqi: false, tone, + failure: null, }; } @@ -317,7 +362,7 @@ export function valueBadgeCandidates( const add = (source: ValueBadgeSource): void => { const key = valueBadgeSourceKey(source); if (candidates.some((item) => item.key === key)) return; - const resolved = resolveSource(hass, d, source, markerStates); + const resolved = resolveValueSource(hass, d, source, markerStates); const technical = source.kind === 'entity_attribute' ? `${source.entity_id} · ${source.attribute}` : source.kind === 'entity_state' ? source.entity_id @@ -395,3 +440,16 @@ export function valueBadgeWriteFields(options: { }, }; } + +/** Pure persistence gate for the value face; auto is represented by absence. */ +export function valueSourceWriteFields(options: { + touched: boolean; + originalHas: boolean; + original: ValueBadgeSource | null | undefined; + source: ValueBadgeSource | null; +}): { value_source?: ValueBadgeSource | null } { + if (!options.touched) { + return options.originalHas ? { value_source: options.original } : {}; + } + return options.source ? { value_source: options.source } : {}; +} diff --git a/src/devices.ts b/src/devices.ts index 0ff8bb6a..87091c50 100644 --- a/src/devices.ts +++ b/src/devices.ts @@ -883,8 +883,13 @@ export function rewriteMarkerControlReferences( source: { ...marker.value_badge.source, ref: to as `marker:${string}` }, } : marker.value_badge; + const valueSource = marker.value_source?.kind === 'derived_marker_state' + && marker.value_source.ref === from + ? { ...marker.value_source, ref: to as `marker:${string}` } + : marker.value_source; return controls === marker.controls && valueBadge === marker.value_badge - ? marker : { ...marker, controls, value_badge: valueBadge }; + && valueSource === marker.value_source + ? marker : { ...marker, controls, value_badge: valueBadge, value_source: valueSource }; }); } diff --git a/src/houseplan-card.ts b/src/houseplan-card.ts index c9b6d520..c19e7b30 100755 --- a/src/houseplan-card.ts +++ b/src/houseplan-card.ts @@ -2107,6 +2107,10 @@ export class HouseplanCard extends LitElement { valueBadgeTouched: boolean; originalHasValueBadge: boolean; originalValueBadge: MarkerValueBadge | null | undefined; + valueSource: ValueBadgeSource | null; + valueSourceTouched: boolean; + originalHasValueSource: boolean; + originalValueSource: ValueBadgeSource | null | undefined; useClimateTemp: boolean; // badge + room-average vote from climate current_temperature model: string; link: string; diff --git a/src/houseplan-editor-runtime.ts b/src/houseplan-editor-runtime.ts index 8b24f67d..779ea450 100644 --- a/src/houseplan-editor-runtime.ts +++ b/src/houseplan-editor-runtime.ts @@ -267,7 +267,8 @@ import { } from './activity-runtime'; import { recommendedValueBadgeSource, valueBadgeCandidates, valueBadgeSourceFromKey, - valueBadgeSourceKey, valueBadgeTitle, valueBadgeWriteFields, type ValueBadgeCandidate, + valueBadgeSourceKey, valueBadgeTitle, valueBadgeWriteFields, valueSourceWriteFields, + type ValueBadgeCandidate, } from './device-value-badge'; import { createRenderDeviceSnapshot, presentationSnapshotKey, renderDeviceSnapshotPositions, @@ -958,7 +959,7 @@ export interface HouseplanEditorHostPort { _layout: Record; _layoutRev: number; _logicalViewCenter: (projection: "flat" | "iso") => { x: number; y: number; } | null; - _markerDialog: { devId?: string; uploadId?: string; name: string; binding: string; bindingMode: "virtual" | "ha"; bindingOpen: boolean; showEntities: boolean; bindingFilter: string; icon: string; autoIcon: string; display: DeviceDisplayMode; rippleColor: string; rippleSize: number; size: number; angle: number; tapAction: string; tapActionTouched: boolean; originalHasTapAction: boolean; originalTapAction: string | null | undefined; tapHintAnnouncement: string; toggleEntity: string; toggleEntityTouched: boolean; originalHasToggleEntity: boolean; originalToggleEntity: string | null | undefined; tapTarget: string; tapConfirm: boolean; runFilter: string; controls: string[]; controlsFilter: string; glowRadius: string; lightRole: "auto" | "always" | "never"; lightRoleTouched: boolean; originalHasIsLight: boolean; originalIsLight: boolean | null | undefined; lightEntity: string; lightEntityTouched: boolean; originalHasLightEntity: boolean; originalLightEntity: string | null | undefined; glowMode: "auto" | "color" | "fixed"; glowColor: string; glowBrightness: number; glowColorDrafted: boolean; glowBrightnessDrafted: boolean; glowTouched: boolean; originalHasGlowColor: boolean; originalGlowColor: { c: string; bri?: number | null; } | null | undefined; valueBadgeEnabled: boolean; valueBadgeSource: ValueBadgeSource | null; valueBadgePosition: ValueBadgePosition; valueBadgeTouched: boolean; originalHasValueBadge: boolean; originalValueBadge: MarkerValueBadge | null | undefined; useClimateTemp: boolean; model: string; link: string; description: string; pdfs: PdfRef[]; room: string; hideFromPlan: boolean; busy: boolean; } | null; + _markerDialog: { devId?: string; uploadId?: string; name: string; binding: string; bindingMode: "virtual" | "ha"; bindingOpen: boolean; showEntities: boolean; bindingFilter: string; icon: string; autoIcon: string; display: DeviceDisplayMode; rippleColor: string; rippleSize: number; size: number; angle: number; tapAction: string; tapActionTouched: boolean; originalHasTapAction: boolean; originalTapAction: string | null | undefined; tapHintAnnouncement: string; toggleEntity: string; toggleEntityTouched: boolean; originalHasToggleEntity: boolean; originalToggleEntity: string | null | undefined; tapTarget: string; tapConfirm: boolean; runFilter: string; controls: string[]; controlsFilter: string; glowRadius: string; lightRole: "auto" | "always" | "never"; lightRoleTouched: boolean; originalHasIsLight: boolean; originalIsLight: boolean | null | undefined; lightEntity: string; lightEntityTouched: boolean; originalHasLightEntity: boolean; originalLightEntity: string | null | undefined; glowMode: "auto" | "color" | "fixed"; glowColor: string; glowBrightness: number; glowColorDrafted: boolean; glowBrightnessDrafted: boolean; glowTouched: boolean; originalHasGlowColor: boolean; originalGlowColor: { c: string; bri?: number | null; } | null | undefined; valueBadgeEnabled: boolean; valueBadgeSource: ValueBadgeSource | null; valueBadgePosition: ValueBadgePosition; valueBadgeTouched: boolean; originalHasValueBadge: boolean; originalValueBadge: MarkerValueBadge | null | undefined; valueSource: ValueBadgeSource | null; valueSourceTouched: boolean; originalHasValueSource: boolean; originalValueSource: ValueBadgeSource | null | undefined; useClimateTemp: boolean; model: string; link: string; description: string; pdfs: PdfRef[]; room: string; hideFromPlan: boolean; busy: boolean; } | null; _markerPreviewDevicesMemo: { base: readonly DevItem[]; preview: DevItem; devices: readonly DevItem[]; } | null; _markerPreviewMemo: { key: string; device: DevItem | null; } | null; _markers: Marker[]; @@ -7642,6 +7643,7 @@ public _openMarkerDialog(d?: DevItem): void { const hasLightEntity = Object.prototype.hasOwnProperty.call(marker || {}, 'light_entity'); const hasGlowColor = Object.prototype.hasOwnProperty.call(marker || {}, 'glow_color'); const hasValueBadge = Object.prototype.hasOwnProperty.call(marker || {}, 'value_badge'); + const hasValueSource = Object.prototype.hasOwnProperty.call(marker || {}, 'value_source'); const hasTapAction = Object.prototype.hasOwnProperty.call(marker || {}, 'tap_action'); const hasToggleEntity = Object.prototype.hasOwnProperty.call(marker || {}, 'toggle_entity'); const glowOverride = normalizeGlowColorOverride(marker?.glow_color); @@ -7703,6 +7705,10 @@ public _openMarkerDialog(d?: DevItem): void { valueBadgeTouched: false, originalHasValueBadge: hasValueBadge, originalValueBadge: marker?.value_badge, + valueSource: marker?.value_source || null, + valueSourceTouched: false, + originalHasValueSource: hasValueSource, + originalValueSource: marker?.value_source, useClimateTemp: d.marker?.use_climate_temp === true, glowRadius: Number(d.marker?.glow_radius_cm) > 0 ? String(this.host._imperial @@ -7739,6 +7745,8 @@ public _openMarkerDialog(d?: DevItem): void { originalHasGlowColor: false, originalGlowColor: undefined, valueBadgeEnabled: false, valueBadgeSource: null, valueBadgePosition: 'right', valueBadgeTouched: false, originalHasValueBadge: false, originalValueBadge: undefined, + valueSource: null, valueSourceTouched: false, + originalHasValueSource: false, originalValueSource: undefined, useClimateTemp: false, glowRadius: '', model: '', link: '', description: '', pdfs: [], room: '', hideFromPlan: false, busy: false, uploadId: 'up_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6), @@ -8029,6 +8037,7 @@ public async _saveMarker(): Promise { // pdfs may be rewritten below when rebinding changes the marker id ...this._markerLightFields(dlg), ...this._markerValueBadgeFields(dlg), + ...this._markerValueSourceFields(dlg), use_climate_temp: dlg.useClimateTemp ? true : null, glow_radius_cm: (() => { const v = strictNumber(dlg.glowRadius); @@ -8083,6 +8092,10 @@ public async _saveMarker(): Promise { && marker.value_badge.source.ref === `marker:${oldId}`) { marker.value_badge.source = { kind: 'derived_marker_state', ref: `marker:${id}` }; } + if (oldId && oldId !== id && marker.value_source?.kind === 'derived_marker_state' + && marker.value_source.ref === `marker:${oldId}`) { + marker.value_source = { kind: 'derived_marker_state', ref: `marker:${id}` }; + } // remove the previous marker (by the old id and by the new id) cfg.markers = cfg.markers.filter( (m) => m.id !== id && m.id !== oldId @@ -11747,6 +11760,17 @@ public _markerValueBadgeFields( }); } +public _markerValueSourceFields( + d: NonNullable, + ): Pick | Record { + return valueSourceWriteFields({ + touched: d.valueSourceTouched, + originalHas: d.originalHasValueSource, + original: d.originalValueSource, + source: d.valueSource, + }); + } + public _markerDraft(d: NonNullable): Marker | null { if (d.bindingMode === 'ha' && (!d.binding || d.binding === 'virtual')) return null; const roomRef = parseRoomRef(d.room); @@ -11773,6 +11797,7 @@ public _markerDraft(d: NonNullable): M controls: controls.length ? controls : null, ...this._markerLightFields(d), ...this._markerValueBadgeFields(d), + ...this._markerValueSourceFields(d), use_climate_temp: d.useClimateTemp ? true : null, glow_radius_cm: (() => { const value = strictNumber(d.glowRadius); @@ -12150,11 +12175,11 @@ public _renderMarkerDialog(): TemplateResult { const badgeSourceMissing = !!effectiveBadgeSource && !badgeCandidates.some((item) => item.key === badgeSourceKey); const selectedBadgeCandidate = badgeCandidates.find((item) => item.key === badgeSourceKey); - const innerValueSourceKey = previewPresentation?.valueSource - ? previewPresentation.valueSource.attribute - ? `attr:${previewPresentation.valueSource.eid}:${previewPresentation.valueSource.attribute}` - : `state:${previewPresentation.valueSource.eid}` - : ''; + const valueSourceKey = valueBadgeSourceKey(d.valueSource); + const valueSourceMissing = !!d.valueSource && (!valueSourceKey + || !badgeCandidates.some((item) => item.key === valueSourceKey)); + const selectedValueSourceCandidate = badgeCandidates.find((item) => item.key === valueSourceKey); + const innerValueSourceKey = previewPresentation?.valueSource?.sourceKey || ''; const autoHasSpatialSource = this._markerAutoHasSpatialSource(d); const statefulSource = !!previewDevice && hasOwnStatefulLightSource(this.host._planHass, { ...previewDevice, hidden: false }); @@ -12233,6 +12258,7 @@ public _renderMarkerDialog(): TemplateResult { }; this.host._markerDialog = this._announceToggleDraft({ ...next, ...this._valueBadgeForBinding(next, 'virtual'), + valueSource: null, valueSourceTouched: true, }); }} /> ${this.host._t('marker.virtual_option')} @@ -12280,6 +12306,7 @@ public _renderMarkerDialog(): TemplateResult { }; this.host._markerDialog = this._announceToggleDraft({ ...next, ...this._valueBadgeForBinding(next, c.value), + valueSource: null, valueSourceTouched: true, }); }}> ${c.label}${c.sub} @@ -12583,6 +12610,39 @@ public _renderMarkerDialog(): TemplateResult { `)}

${this.host._t(DISPLAY_HINT_KEYS[d.display])}

+ ${d.display === 'value' ? html`
+
+ + ${this._help('marker.value_source.help')} +
+ + ${selectedValueSourceCandidate + ? html`

${selectedValueSourceCandidate.technical}

` + : nothing} + ${isVirtual ? html`

+ + ${this.host._t('marker.preview.reason.value_virtual')} +

` : valueSourceMissing ? html`

+ ${this.host._t('marker.value_source_missing_hint')} +

` : nothing} +
` : nothing} ${d.display === 'static_icon' && this.host._bindingHasAlarm(d.binding) ? html`
diff --git a/src/i18n/de.json b/src/i18n/de.json index ba2a38c1..197398f5 100644 --- a/src/i18n/de.json +++ b/src/i18n/de.json @@ -190,7 +190,7 @@ "marker.display_hint_badge": "Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.", "marker.display_hint_icon": "Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.", "marker.display_hint_icon_ripple": "Das Symbol, die dynamische Platte und der Puls zeigen einen kurzen Puls für Ereignisse und einen kontinuierlichen Puls für Arbeit, Bewegung oder Anwesenheit. Rote Alarme haben eine separate Priorität.", - "marker.display_hint_value": "Ein eindeutiger Home Assistant-Wert ersetzt das Symbol, während die Platte weiterhin den Zustand anzeigt. Rote Alarme bleiben sichtbar.", + "marker.display_hint_value": "Ein ausgewählter oder automatisch ermittelter Home Assistant-Wert ersetzt das Symbol, während die Platte weiterhin den Zustand anzeigt. Rote Alarme bleiben sichtbar.", "marker.display_hint_static_icon": "Die themenabhängige Hülle und das Symbol bleiben immer gleich. Zustand, Aktivität, Nichtverfügbarkeit und Alarme verändern nicht das Erscheinungsbild.", "marker.static_alarm_warning": "Dieses Gerät kann Alarme melden. Die statische Anzeige verbirgt die visuelle Alarmanzeige des Markers.", "marker.preview.title": "Anzeigevorschau", @@ -665,6 +665,11 @@ "marker.value_badge_attr_volume_level": "Lautstärke · {name}", "marker.value_badge_attr_battery_level": "Batterie · {name}", "marker.value_badge_attr_fan_speed": "Lüftergeschwindigkeit · {name}", + "marker.value_source": "Wertquelle", + "marker.value_source.help": "Wähle den Wert, der das Symbol ersetzt. Die Tipp-Aktion bleibt unverändert.", + "marker.value_source.help.aria": "Hilfe: Wertquelle in der Gerätefläche", + "marker.value_source_auto": "Automatisch (wie bisher)", + "marker.value_source_missing_hint": "Die gespeicherte Quelle ist derzeit nicht verfügbar. Bis sie wieder verfügbar ist oder ein anderer Wert gewählt wird, zeigt die Fläche —.", "marker.light_role_label": "Ist dieses Gerät eine Lichtquelle?", "marker.light_role.help": "Steuert, ob dieser Marker selbst eine räumliche Lichtquelle ist. Verknüpfte Lichter oben bleiben unabhängige Raumlichtsteuerungen.", "marker.light_role.help.aria": "Hilfe: ob dieses Gerät eine Lichtquelle ist", diff --git a/src/i18n/en.json b/src/i18n/en.json index 8b5d82ae..1f6de2e5 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -190,7 +190,7 @@ "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_value": "A selected or automatically resolved 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", @@ -665,6 +665,11 @@ "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.value_source": "Value source", + "marker.value_source.help": "Choose the value that replaces the icon. The tap action does not change.", + "marker.value_source.help.aria": "Help: value face source", + "marker.value_source_auto": "Automatic (as before)", + "marker.value_source_missing_hint": "The saved source is currently unavailable. The face shows — until it recovers or you choose another value.", "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", diff --git a/src/i18n/fr.json b/src/i18n/fr.json index 3bdcfac2..03435086 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -190,7 +190,7 @@ "marker.display_hint_badge": "L’icône et le fond dynamique affichent l’état de l’appareil sans la pulsation d’activité habituelle. Les alarmes rouges restent visibles.", "marker.display_hint_icon": "L’icône et le fond dynamique affichent l’état de l’appareil sans la pulsation d’activité habituelle. Les alarmes rouges restent visibles.", "marker.display_hint_icon_ripple": "L’icône, le fond dynamique et la pulsation affichent une brève animation pour les événements et une animation continue pour le fonctionnement, le mouvement ou la présence. Les alarmes rouges restent prioritaires.", - "marker.display_hint_value": "Une valeur Home Assistant non ambiguë remplace l’icône tandis que le fond continue d’indiquer l’état. Les alarmes rouges restent visibles.", + "marker.display_hint_value": "Une valeur Home Assistant sélectionnée ou résolue automatiquement remplace l’icône tandis que le fond continue d’indiquer l’état. Les alarmes rouges restent visibles.", "marker.display_hint_static_icon": "Le contour adapté au thème et l’icône restent toujours identiques. L’état, l’activité, l’indisponibilité et les alarmes ne modifient pas l’apparence.", "marker.static_alarm_warning": "Cet appareil peut signaler des alarmes. L’affichage statique masque l’indication visuelle d’alarme du marqueur.", "marker.preview.title": "Aperçu de l’affichage", @@ -665,6 +665,11 @@ "marker.value_badge_attr_volume_level": "Volume · {name}", "marker.value_badge_attr_battery_level": "Batterie · {name}", "marker.value_badge_attr_fan_speed": "Vitesse du ventilateur · {name}", + "marker.value_source": "Source de la valeur", + "marker.value_source.help": "Choisissez la valeur qui remplace l’icône. L’action au toucher ne change pas.", + "marker.value_source.help.aria": "Aide : source de la valeur dans le marqueur", + "marker.value_source_auto": "Automatique (comme avant)", + "marker.value_source_missing_hint": "La source enregistrée est indisponible. Le marqueur affiche — jusqu’à son retour ou jusqu’au choix d’une autre valeur.", "marker.light_role_label": "Cet appareil est-il une source lumineuse ?", "marker.light_role.help": "Détermine si ce marqueur est lui-même une source lumineuse spatiale. Les lumières liées ci-dessus restent des contrôles de lumière de pièce indépendants.", "marker.light_role.help.aria": "Aide : cet appareil est-il une source lumineuse", diff --git a/src/i18n/ru.json b/src/i18n/ru.json index ab9aafe6..c4f50229 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -190,7 +190,7 @@ "marker.display_hint_badge": "Значок и динамическая подложка показывают состояние устройства без обычной пульсации активности. Красная тревога сохраняется.", "marker.display_hint_icon": "Значок и динамическая подложка показывают состояние устройства без обычной пульсации активности. Красная тревога сохраняется.", "marker.display_hint_icon_ripple": "Значок, динамическая подложка и пульсация: короткая — для событий, постоянная — для работы, движения или присутствия. Красная тревога имеет отдельный приоритет.", - "marker.display_hint_value": "Значок заменяется одним однозначным значением Home Assistant; подложка продолжает показывать состояние. Красная тревога сохраняется.", + "marker.display_hint_value": "Значок заменяется выбранным или автоматически найденным значением Home Assistant; подложка продолжает показывать состояние. Красная тревога сохраняется.", "marker.display_hint_static_icon": "Подложка в цветах темы и значок всегда остаются одинаковыми. Состояния, активность, недоступность и тревоги не меняют отображение.", "marker.static_alarm_warning": "Это устройство может сообщать о тревогах. Статичный режим скрывает визуальную тревогу маркера.", "marker.preview.title": "Предпросмотр отображения", @@ -665,6 +665,11 @@ "marker.value_badge_attr_volume_level": "Громкость · {name}", "marker.value_badge_attr_battery_level": "Заряд батареи · {name}", "marker.value_badge_attr_fan_speed": "Скорость вентилятора · {name}", + "marker.value_source": "Источник значения", + "marker.value_source.help": "Выберите, что заменит значок. Действие по нажатию не изменится.", + "marker.value_source.help.aria": "Подсказка: источник значения внутри значка", + "marker.value_source_auto": "Автоматически (как раньше)", + "marker.value_source_missing_hint": "Сохранённый источник сейчас недоступен. До его восстановления или выбора другого значения внутри будет показано —.", "marker.light_role_label": "Является источником света", "marker.light_role.help": "Определяет, является ли сам маркер пространственным источником света. Привязанные выше лампы остаются независимыми источниками для комнаты.", "marker.light_role.help.aria": "Подсказка: является ли устройство источником света", diff --git a/src/types.ts b/src/types.ts index 1bdf86f9..fd94e8a1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -176,6 +176,9 @@ export interface Marker { /** Optional, user-controlled value satellite around the device face. * Missing keeps the legacy temperature/humidity compatibility heuristic. */ value_badge?: MarkerValueBadge | null; + /** Explicit source for the `value` display face. Missing/null keeps the + * historical automatic entity-state resolver. */ + value_source?: ValueBadgeSource | null; /** * Climate devices (AC, thermostat) know the room temperature * (attributes.current_temperature). Opt-in per marker: show it as a badge diff --git a/test/device-presentation-policy.test.mjs b/test/device-presentation-policy.test.mjs index 619d529d..3a826edf 100644 --- a/test/device-presentation-policy.test.mjs +++ b/test/device-presentation-policy.test.mjs @@ -305,6 +305,16 @@ const rowRunners = { id: 'vacuum', icon: 'mdi:robot-vacuum', entities: ['vacuum.robot'], primary: 'vacuum.robot', marker: { id: 'vacuum', binding: 'device:vacuum', vacuum: { live: true } }, }, {}, (r) => assert.equal(r.vacuumLive, true)), + F18: presentationRow({ + entities: ['switch.offline'], primary: 'switch.offline', + marker: { + id: 'd1', binding: 'device:d1', display: 'value', + value_source: { kind: 'entity_state', entity_id: 'switch.offline' }, + }, + }, {}, (r) => { + assert.equal(r.valueText, '—'); + assert.equal(r.fallbackReason, 'value_no_state'); + }), A01: presentationRow({ icon: 'mdi:smoke-detector', entities: ['binary_sensor.smoke'], primary: 'binary_sensor.smoke', }, { liveStates: false }, (r) => assert.deepEqual([r.pulse.kind, r.pulse.reason], ['alarm', 'alarm'])), diff --git a/test/device-presentation.test.mjs b/test/device-presentation.test.mjs index 709e827d..646d94ce 100644 --- a/test/device-presentation.test.mjs +++ b/test/device-presentation.test.mjs @@ -15,7 +15,9 @@ import { resolveBindingProviders, resolveEntityProvider, } from '../test-build/integration-provider.js'; -import { valueBadgeTitle, valueBadgeWriteFields } from '../test-build/device-value-badge.js'; +import { + valueBadgeCandidates, valueBadgeTitle, valueBadgeWriteFields, valueSourceWriteFields, +} from '../test-build/device-value-badge.js'; import { buildDevices, deviceFromMarkerDraft, resolvedLightSources, } from '../test-build/devices.js'; @@ -833,6 +835,137 @@ test('value mode refuses to choose the first of multiple equal control sources', assert.equal(result.visualSources.length, 2); }); +test('issue 378 explicit cover position uses the exact value-badge formatter', () => { + const h = hass({ + 'cover.awning': state('cover.awning', 'open', { current_position: 42, supported_features: 15 }), + }, { + 'cover.awning': { entity_id: 'cover.awning', device_id: 'd1', platform: 'demo' }, + }); + const source = { kind: 'entity_attribute', entity_id: 'cover.awning', attribute: 'current_position' }; + const awning = device({ + name: 'Awning', icon: 'mdi:awning', entities: ['cover.awning'], primary: 'cover.awning', + marker: { + id: 'd1', binding: 'device:d1', display: 'value', value_source: source, + value_badge: { enabled: true, source, position: 'right' }, + }, + }); + const result = resolveDevicePresentation(h, awning, options); + assert.equal(result.valueText, '42 %'); + assert.equal(result.valueFullText, '42 %'); + assert.equal(result.valueBadge?.text, result.valueText); + assert.equal(result.valueSource?.sourceKey, 'attr:cover.awning:current_position'); + assert.equal(result.sourceKind, 'cover'); + assert.deepEqual( + valueBadgeCandidates(h, awning).find((candidate) => candidate.key === result.valueSource?.sourceKey)?.source, + source, + ); +}); + +test('issue 378 explicit unavailable source shows a dash and restores without auto fallback', () => { + const h = hass({}, { + 'cover.awning': { entity_id: 'cover.awning', device_id: 'd1', platform: 'demo' }, + }); + const awning = device({ + name: 'Awning', icon: 'mdi:awning', entities: ['cover.awning'], primary: 'cover.awning', + marker: { + id: 'd1', binding: 'device:d1', display: 'value', + value_source: { kind: 'entity_attribute', entity_id: 'cover.awning', attribute: 'current_position' }, + }, + }); + const missing = resolveDevicePresentation(h, awning, options); + assert.equal(missing.valueText, '—'); + assert.equal(missing.valueFullText, 'Unavailable'); + assert.equal(missing.fallbackReason, 'value_no_state'); + assert.equal(missing.valueSource?.sourceKey, 'attr:cover.awning:current_position'); + assert.ok(missing.decisionIds.includes('content.value')); + + h.states['cover.awning'] = state('cover.awning', 'open', { + current_position: 55, supported_features: 15, + }); + const restored = resolveDevicePresentation(h, awning, options); + assert.equal(restored.valueText, '55 %'); + assert.equal(restored.fallbackReason, null); + + h.states['cover.awning'] = state('cover.awning', 'unavailable', { + current_position: 55, supported_features: 15, + }); + const staleAttribute = resolveDevicePresentation(h, awning, options); + assert.equal(staleAttribute.valueText, '—'); + assert.equal(staleAttribute.fallbackReason, 'value_no_state'); + + const legacy = resolveDevicePresentation(hass({}, h.entities), { + ...awning, marker: { id: 'd1', binding: 'device:d1', display: 'value' }, + }, options); + assert.equal(legacy.valueText, null); + assert.equal(legacy.fallbackReason, 'value_no_state'); +}); + +test('issue 378 explicit non-scalar value stays selected and reports the stable reason', () => { + const h = hass({ + 'cover.awning': state('cover.awning', 'open', { current_position: { invalid: true } }), + }, { + 'cover.awning': { entity_id: 'cover.awning', device_id: 'd1', platform: 'demo' }, + }); + const result = resolveDevicePresentation(h, device({ + entities: ['cover.awning'], primary: 'cover.awning', + marker: { + id: 'd1', binding: 'device:d1', display: 'value', + value_source: { kind: 'entity_attribute', entity_id: 'cover.awning', attribute: 'current_position' }, + }, + }), options); + assert.equal(result.valueText, '—'); + assert.equal(result.fallbackReason, 'value_non_scalar'); +}); + +test('issue 378 derived sources share the plan graph and suppress duplicate LQI', () => { + const h = hass({ + 'switch.driver': state('switch.driver', 'on'), + 'sensor.linkquality': state('sensor.linkquality', '150', { unit_of_measurement: 'lqi' }), + }, { + 'switch.driver': { entity_id: 'switch.driver', device_id: 'lamp', platform: 'demo' }, + 'sensor.linkquality': { entity_id: 'sensor.linkquality', device_id: 'd1', platform: 'demo' }, + }); + const lamp = device({ + id: 'lamp', name: 'Lamp', entities: ['switch.driver'], primary: 'switch.driver', + bindingRef: 'lamp', marker: { id: 'lamp', binding: 'device:lamp', is_light: true }, + }); + const controller = device({ + controls: ['marker:lamp'], + marker: { + id: 'd1', binding: 'device:d1', display: 'value', controls: ['marker:lamp'], + value_source: { kind: 'derived_marker_state', ref: 'marker:lamp' }, + }, + }); + const stateResult = resolveDevicePresentation(h, controller, { ...options, lightDevices: [controller, lamp] }); + assert.equal(stateResult.valueText, 'On'); + assert.equal(stateResult.valueSource?.kind, 'derived_marker_state'); + + const lqiResult = resolveDevicePresentation(h, device({ + entities: ['sensor.linkquality'], primary: 'sensor.linkquality', + marker: { id: 'd1', binding: 'device:d1', display: 'value', value_source: { kind: 'derived_lqi' } }, + }), options); + assert.equal(lqiResult.valueText, '150'); + assert.equal(lqiResult.lqiText, null); +}); + +test('issue 378 persistence keeps untouched data and represents auto by absence', () => { + assert.deepEqual(valueSourceWriteFields({ + touched: false, originalHas: false, original: undefined, + source: { kind: 'entity_state', entity_id: 'sensor.projected' }, + }), {}); + const future = { kind: 'future_source', payload: { preserved: true } }; + assert.equal(valueSourceWriteFields({ + touched: false, originalHas: true, original: future, source: null, + }).value_source, future); + assert.deepEqual(valueSourceWriteFields({ + touched: true, originalHas: true, original: future, source: null, + }), {}); + assert.deepEqual(valueSourceWriteFields({ + touched: true, originalHas: false, original: undefined, + source: { kind: 'entity_state', entity_id: 'sensor.selected' }, + }), { value_source: { kind: 'entity_state', entity_id: 'sensor.selected' } }); +}); + test('hidden design preview is visible without mutating the hidden contract', () => { const h = hass( { 'switch.relay': state('switch.relay', 'on') }, diff --git a/test/devices.test.mjs b/test/devices.test.mjs index 493f29e9..71a070ce 100644 --- a/test/devices.test.mjs +++ b/test/devices.test.mjs @@ -1910,6 +1910,13 @@ test('issue 84 lifecycle: delete/rebind updates marker links and cycles are reje rewriteMarkerControlReferences(withBadge, 'b', 'renamed')[0].value_badge.source.ref, 'marker:renamed', ); + const withValueSource = [{ + id: 'a', value_source: { kind: 'derived_marker_state', ref: 'marker:b' }, + }]; + assert.equal( + rewriteMarkerControlReferences(withValueSource, 'b', 'renamed')[0].value_source.ref, + 'marker:renamed', + ); assert.equal(markerControlWouldCycle(markers, 'c', 'a'), true); assert.equal(markerControlWouldCycle(markers, 'a', 'c'), false); assert.equal(markerControlWouldCycle(markers, 'a', 'a'), true); diff --git a/test/fixtures/device-presentation-decisions.mjs b/test/fixtures/device-presentation-decisions.mjs index fe183d9b..344bd814 100644 --- a/test/fixtures/device-presentation-decisions.mjs +++ b/test/fixtures/device-presentation-decisions.mjs @@ -43,6 +43,7 @@ const ROWS = [ ['F15', 'diagnostics.metrics_enabled', 'device-presentation-policy-diagnostics'], ['F16', 'diagnostics.lqi_low', 'device-marker-lqi-low-boundary-shifted'], ['F17', 'diagnostics.vacuum_live', 'device-presentation-policy-diagnostics'], + ['F18', 'content.value', 'device-presentation-policy-value'], ['A01', 'pulse.alarm_alarm', 'device-presentation-policy-alarm'], ['A02', 'activity.pulse_suppressed', 'device-presentation-policy-pulse-gate'], ['A03', 'pulse.short_event', 'device-presentation-policy-pulse-gate'], diff --git a/test/native-select-contract.test.mjs b/test/native-select-contract.test.mjs index 8fdee3df..1336ab85 100644 --- a/test/native-select-contract.test.mjs +++ b/test/native-select-contract.test.mjs @@ -38,7 +38,7 @@ test('persisted dynamic marker selects project their selected option explicitly' ])); for (const id of [ 'marker-room', 'marker-tap-action', 'marker-toggle-entity', 'marker-light-entity', 'marker-display', - 'marker-value-badge-source', 'marker-value-badge-position', + 'marker-value-source', 'marker-value-badge-source', 'marker-value-badge-position', ]) { const block = byId.get(id); assert.ok(block, id); diff --git a/tests_backend/test_ha_import_export.py b/tests_backend/test_ha_import_export.py index 77c514a2..0b7bfc57 100644 --- a/tests_backend/test_ha_import_export.py +++ b/tests_backend/test_ha_import_export.py @@ -1511,6 +1511,27 @@ def test_issue_90_space_merge_remaps_internal_badge_marker_link(tmp_path: Path) assert details["dropped_marker_links"] == 0 +def test_issue_378_space_merge_remaps_internal_value_face_marker_link(tmp_path: Path) -> None: + document = _document(tmp_path, "space") + markers = document["payload"]["config"]["markers"] + markers[0]["name"] = "Controller" + markers[0]["value_source"] = { + "kind": "derived_marker_state", "ref": "marker:dumb", + } + markers.append({ + "id": "dumb", "binding": "virtual", "space": "ground", + "room_id": "living", "name": "Dumb lamp", "is_light": True, + }) + merged, _layout, details = build_space_merge( + document, {"spaces": [], "markers": [], "settings": {}}, {}, "skip", + ) + imported = [m for m in merged["markers"] if m.get("space") == details["space_id"]] + by_name = {m.get("name"): m for m in imported} + assert by_name["Controller"]["value_source"]["ref"] == \ + "marker:" + by_name["Dumb lamp"]["id"] + assert details["dropped_marker_links"] == 0 + + def test_space_export_drops_marker_links_outside_selection(tmp_path: Path) -> None: config = _config() config["markers"][0]["controls"] = ["marker:outside", "light.keep"] @@ -1549,6 +1570,47 @@ def test_issue_90_space_export_disables_badge_link_outside_selection(tmp_path: P assert document["transfer"]["dropped_marker_links"] == 1 +def test_issue_378_space_export_drops_value_face_link_outside_selection(tmp_path: Path) -> None: + config = _config() + config["markers"][0]["value_source"] = { + "kind": "derived_marker_state", "ref": "marker:outside", + } + config["markers"].append({ + "id": "outside", "binding": "virtual", "space": "other", + "name": "Other lamp", "is_light": True, + }) + document, _ = create_export( + SimpleNamespace(instance_id="instance-a"), {"config": config}, + {"layout": {"lamp": {"s": "ground", "x": 0.5, "y": 0.5}}}, + kind="space", space_id="ground", card_version="1.61.0", config_root=tmp_path, + ) + marker = document["payload"]["config"]["markers"][0] + assert "value_source" not in marker + assert document["transfer"]["dropped_marker_links"] == 1 + + +def test_issue_378_virtualized_duplicate_drops_value_face_link(tmp_path: Path) -> None: + document = _document(tmp_path, "space") + controller = document["payload"]["config"]["markers"][0] + controller["name"] = "Controller" + controller["value_source"] = { + "kind": "derived_marker_state", "ref": "marker:dumb", + } + document["payload"]["config"]["markers"].append({ + "id": "dumb", "binding": "virtual", "space": "ground", "is_light": True, + }) + current = { + "spaces": [], "markers": [{"id": "existing", "binding": controller["binding"]}], + } + merged, _layout, details = build_space_merge(document, current, {}, "virtual") + imported_controller = next( + marker for marker in merged["markers"] + if marker.get("name") == controller.get("name") and marker.get("binding") == "virtual" + ) + assert "value_source" not in imported_controller + assert details["dropped_marker_links"] == 1 + + def test_space_merge_drops_link_when_target_is_virtualized_duplicate(tmp_path: Path) -> None: document = _document(tmp_path, "space") controller = document["payload"]["config"]["markers"][0] diff --git a/tests_backend/test_validation.py b/tests_backend/test_validation.py index ad7c39b5..f16ce87c 100644 --- a/tests_backend/test_validation.py +++ b/tests_backend/test_validation.py @@ -2400,3 +2400,75 @@ def test_issue_90_marker_value_badge_reference_uses_canonical_ref(): with pytest.raises(v.MarkerControlError) as invalid: v.validate_marker_value_badges(config, {"markers": []}) assert invalid.value.code == "invalid_value_badge_source" + + +def test_issue_378_value_face_source_is_lossless_and_strict_only_when_changed(): + source = { + "kind": "entity_attribute", "entity_id": "cover.awning", + "attribute": "current_position", "future_source": {"kept": True}, + } + config = {"markers": [{ + "id": "awning", "binding": "entity:cover.awning", "display": "value", + "value_source": source, + }]} + v.validate_marker_value_badges(config, {"markers": []}) + stored = v.MARKER_SCHEMA(config["markers"][0]) + assert stored["value_source"]["future_source"] == {"kept": True} + + future = {"markers": [{ + "id": "future", "binding": "entity:sensor.future", "display": "value", + "value_source": {"kind": "future_source", "payload": {"kept": True}}, + }]} + v.validate_marker_value_badges(future, future) + with pytest.raises(v.MarkerControlError) as changed: + v.validate_marker_value_badges(future, {"markers": []}) + assert changed.value.code == "invalid_value_source" + with pytest.raises(v.MarkerControlError) as imported: + v.validate_marker_value_badges(future, validate_all=True) + assert imported.value.code == "invalid_value_source" + + invalid_attr = {"markers": [{ + "id": "bad", "binding": "entity:sensor.x", "display": "value", + "value_source": { + "kind": "entity_attribute", "entity_id": "sensor.x", + "attribute": "entity_picture", + }, + }]} + with pytest.raises(v.MarkerControlError) as attribute: + v.validate_marker_value_badges(invalid_attr, {"markers": []}) + assert attribute.value.code == "invalid_value_source_attribute" + + +def test_issue_378_value_face_marker_reference_and_id_rename_are_delta_safe(): + config = {"markers": [ + {"id": "lamp", "binding": "virtual", "is_light": True}, + {"id": "controller", "binding": "device:controller", "display": "value", + "value_source": {"kind": "derived_marker_state", "ref": "marker:lamp"}}, + ]} + v.validate_marker_value_badges(config, {"markers": []}) + + missing = {"markers": [ + config["markers"][0], + {**config["markers"][1], "value_source": { + "kind": "derived_marker_state", "ref": "marker:gone", + }}, + ]} + with pytest.raises(v.MarkerControlError) as absent: + v.validate_marker_value_badges(missing, config) + assert absent.value.code == "value_source_marker_missing" + + not_light = {"markers": [ + {**config["markers"][0], "is_light": False}, config["markers"][1], + ]} + with pytest.raises(v.MarkerControlError) as target: + v.validate_marker_value_badges(not_light, {"markers": []}) + assert target.value.code == "value_source_marker_not_light" + + # Stable binding identifies a renamed marker, so dormant unchanged source data + # remains old data instead of becoming a newly-invalid write. + future_old = {"markers": [{ + "id": "old", "binding": "device:controller", "display": "value", + "value_source": {"kind": "future_source", "payload": 1}, + }]} + future_renamed = {"markers": [{**future_old["markers"][0], "id": "new"}]} + v.validate_marker_value_badges(future_renamed, future_old)