mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfbc85781f | ||
|
|
f57a0c08ce |
@@ -1,111 +1,111 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"fingerprint": "2fe93c69042bf1863b46106ae163065bda75922774e15b28a68631280dcb9457",
|
||||
"fingerprint": "95bafd2bfb8bfeafdfa45a0f5fc090b71ddc2cc9dbf2fb569fb60941c38ef5e8",
|
||||
"entry": "houseplan-card.js",
|
||||
"initialViewFiles": [
|
||||
"houseplan-assets/houseplan-card-RebBKTYk.js",
|
||||
"houseplan-assets/houseplan-card-Cz0AWb2g.js",
|
||||
"houseplan-card.js"
|
||||
],
|
||||
"initialViewGzipBytes": 272475,
|
||||
"initialViewGzipBytes": 272677,
|
||||
"lazyFiles": [
|
||||
"houseplan-assets/de-DzID0Btx.js",
|
||||
"houseplan-assets/editor-D72u49-5.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DakYwgZM.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-_Ccbpb04.js",
|
||||
"houseplan-assets/space-deletion-CVy_7Hj9.js"
|
||||
"houseplan-assets/de-D6nVPpxU.js",
|
||||
"houseplan-assets/editor-DREg7QWs.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DMApKDuE.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DyfrTQG3.js",
|
||||
"houseplan-assets/space-deletion-DeqTZ3B0.js"
|
||||
],
|
||||
"lazyGzipBytes": 166317,
|
||||
"lazyGzipBytes": 166287,
|
||||
"lazyEditorFiles": [
|
||||
"houseplan-assets/editor-D72u49-5.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DakYwgZM.js",
|
||||
"houseplan-assets/space-deletion-CVy_7Hj9.js"
|
||||
"houseplan-assets/editor-DREg7QWs.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DMApKDuE.js",
|
||||
"houseplan-assets/space-deletion-DeqTZ3B0.js"
|
||||
],
|
||||
"lazyEditorGzipBytes": 137195,
|
||||
"lazyEditorGzipBytes": 137185,
|
||||
"lazyOnboardingFiles": [
|
||||
"houseplan-assets/houseplan-onboarding-runtime-_Ccbpb04.js",
|
||||
"houseplan-assets/space-deletion-CVy_7Hj9.js"
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DyfrTQG3.js",
|
||||
"houseplan-assets/space-deletion-DeqTZ3B0.js"
|
||||
],
|
||||
"lazyOnboardingGzipBytes": 7205,
|
||||
"lazyOnboardingGzipBytes": 7198,
|
||||
"lazyLocaleFiles": [
|
||||
"houseplan-assets/de-DzID0Btx.js"
|
||||
"houseplan-assets/de-D6nVPpxU.js"
|
||||
],
|
||||
"lazyLocaleGzipBytes": 22472,
|
||||
"lazyLocaleGzipBytes": 22455,
|
||||
"files": [
|
||||
{
|
||||
"path": "houseplan-assets/de-DzID0Btx.js",
|
||||
"sha256": "2be1bf1c986969cfdbbfaff54e048ea71dc117547532088479c31e4549cc266b",
|
||||
"path": "houseplan-assets/de-D6nVPpxU.js",
|
||||
"sha256": "3057f94023d68a1f8f4d7bf69b19465a45c5d017d17e9c8c00b5be2f0c36cbad",
|
||||
"rawBytes": 73919,
|
||||
"gzipBytes": 22472,
|
||||
"gzipBytes": 22455,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/editor-D72u49-5.js",
|
||||
"sha256": "53e388280fce41bb6b7c1d0441460dacdad613044a7e5529df7beda3f6fa9611",
|
||||
"path": "houseplan-assets/editor-DREg7QWs.js",
|
||||
"sha256": "5c08b305112db058ac0ce42b2b6a8e57a6c10d3b2b0b57378b3c3c78a48d351e",
|
||||
"rawBytes": 3831,
|
||||
"gzipBytes": 1587,
|
||||
"gzipBytes": 1581,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-RebBKTYk.js"
|
||||
"houseplan-assets/houseplan-card-Cz0AWb2g.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-card-RebBKTYk.js",
|
||||
"sha256": "f838f63fc5fdea8f9d850520effd618b90a47db7685f0a7c1ddedce296459630",
|
||||
"rawBytes": 960284,
|
||||
"gzipBytes": 271718,
|
||||
"path": "houseplan-assets/houseplan-card-Cz0AWb2g.js",
|
||||
"sha256": "38d44662e84dae93783754caef2ba13bed030ef173ef54a14f642c89e7f4950c",
|
||||
"rawBytes": 961010,
|
||||
"gzipBytes": 271925,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/de-DzID0Btx.js",
|
||||
"houseplan-assets/editor-D72u49-5.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DakYwgZM.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-_Ccbpb04.js"
|
||||
"houseplan-assets/de-D6nVPpxU.js",
|
||||
"houseplan-assets/editor-DREg7QWs.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DMApKDuE.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DyfrTQG3.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-DakYwgZM.js",
|
||||
"sha256": "574e08c63bfb5c42a408aa41496076ffcd315034e7451af6c7a572b958e5e7b5",
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-DMApKDuE.js",
|
||||
"sha256": "6191dd8b6338053c2aa3544ea2e04db05b31bc65474a9623391823b62dbdd394",
|
||||
"rawBytes": 522188,
|
||||
"gzipBytes": 135053,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-RebBKTYk.js",
|
||||
"houseplan-assets/space-deletion-CVy_7Hj9.js"
|
||||
"houseplan-assets/houseplan-card-Cz0AWb2g.js",
|
||||
"houseplan-assets/space-deletion-DeqTZ3B0.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-_Ccbpb04.js",
|
||||
"sha256": "792aeff9ea709c6c648d37109c52eba6758334ed5ae746e0d9cdf30328fa7a52",
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-DyfrTQG3.js",
|
||||
"sha256": "c94cd05d64d3764a2d26a0aac8a59e3d2bbe11a923b3d8c4c9891f362dfcbaae",
|
||||
"rawBytes": 26356,
|
||||
"gzipBytes": 6650,
|
||||
"gzipBytes": 6647,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-RebBKTYk.js",
|
||||
"houseplan-assets/space-deletion-CVy_7Hj9.js"
|
||||
"houseplan-assets/houseplan-card-Cz0AWb2g.js",
|
||||
"houseplan-assets/space-deletion-DeqTZ3B0.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/space-deletion-CVy_7Hj9.js",
|
||||
"sha256": "b704bdc6222b138ce76b0e6250a34e0821709ca643af777203a26744c33bc983",
|
||||
"path": "houseplan-assets/space-deletion-DeqTZ3B0.js",
|
||||
"sha256": "f4eeaf0367d1b6fe333b35c16ff59f2dc7015f7532fa356aaf7c43db9a0e0827",
|
||||
"rawBytes": 911,
|
||||
"gzipBytes": 555,
|
||||
"gzipBytes": 551,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-card.js",
|
||||
"sha256": "b0dd77205cb44aad9107d5fafffdcf9d12e9e43dc448c8b10579e27d3738aa46",
|
||||
"sha256": "724c889fd050357875c6e38996a179a90ed3172a3822c0b97d17d193fc367ee4",
|
||||
"rawBytes": 1089,
|
||||
"gzipBytes": 757,
|
||||
"gzipBytes": 752,
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-RebBKTYk.js"
|
||||
"houseplan-assets/houseplan-card-Cz0AWb2g.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
}
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+7
-7
@@ -1,14 +1,14 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75922774e15b28a68631280dcb9457";import{c as e,l as o,t,cZ as s,A as a,c_ as i,c$ as l,d0 as n,E as r,b as c}from"./houseplan-card-RebBKTYk.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")},_=this._schema,f=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`<ha-form
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95bafd2bfb8bfeafdfa45a0f5fc090b71ddc2cc9dbf2fb569fb60941c38ef5e8";import{c as e,l as o,t,cZ as s,A as a,c_ as i,c$ as l,d0 as n,E as r,b as c}from"./houseplan-card-Cz0AWb2g.js";class f 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,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`<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._formData}
|
||||
.schema=${e}
|
||||
.computeLabel=${e=>h[e.name]||e.name}
|
||||
.computeLabel=${e=>f[e.name]||e.name}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>`;return c`
|
||||
${d(_.slice(0,3))}
|
||||
${f?c`<div class="default-floor-error" role="alert"
|
||||
${d(h.slice(0,3))}
|
||||
${_?c`<div class="default-floor-error" role="alert"
|
||||
style="color:var(--error-color,#db4437);margin:-4px 0 12px;overflow-wrap:anywhere">
|
||||
${t(s,"editor.default_floor_missing",{id:f})}
|
||||
${t(s,"editor.default_floor_missing",{id:_})}
|
||||
</div>`:a}
|
||||
${d(_.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);
|
||||
+20
-10
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
+5
-5
@@ -1,4 +1,4 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75922774e15b28a68631280dcb9457";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 n,b_ as c,b$ as r,b as p,A as _,cR as d,ap as u,ar as g,cS as m}from"./houseplan-card-RebBKTYk.js";import{i as b,c as $,a as v,s as y,t as f}from"./space-deletion-CVy_7Hj9.js";const w=1e3,D=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},S="2fe93c69042bf1863b46106ae163065bda75922774e15b28a68631280dcb9457";class C{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),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,c="none"===h.fill?{...n||e,a:0}:n;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:c,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:c,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:n,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;const o={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"");if(!o)return void this.host._showToast(this.host._t("toast.plan_formats"));const a=new Uint8Array(await e.arrayBuffer());let l="";for(let t=0;t<a.length;t+=32768)l+=String.fromCharCode(...a.subarray(t,t+32768));const i=btoa(l),h=URL.createObjectURL(e),n=await new Promise(t=>{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:1.414),s.onerror=()=>t(1.414),s.src=h});URL.revokeObjectURL(h),this.host._spaceDialog={...this.host._spaceDialog,planFile:{ext:o,b64:i,aspect:n,name:e.name}}}_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 p`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return p`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return p`<div class="savedplans">
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95bafd2bfb8bfeafdfa45a0f5fc090b71ddc2cc9dbf2fb569fb60941c38ef5e8";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 n,b_ as c,b$ as r,b as p,A as _,cR as d,ap as u,ar as g,cS as m}from"./houseplan-card-Cz0AWb2g.js";import{i as b,c as $,a as v,s as f,t as y}from"./space-deletion-DeqTZ3B0.js";const w=1e3,D=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},S="95bafd2bfb8bfeafdfa45a0f5fc090b71ddc2cc9dbf2fb569fb60941c38ef5e8";class C{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),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,c="none"===h.fill?{...n||e,a:0}:n;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:c,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:c,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:n,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;const o={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"");if(!o)return void this.host._showToast(this.host._t("toast.plan_formats"));const a=new Uint8Array(await e.arrayBuffer());let l="";for(let t=0;t<a.length;t+=32768)l+=String.fromCharCode(...a.subarray(t,t+32768));const i=btoa(l),h=URL.createObjectURL(e),n=await new Promise(t=>{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:1.414),s.onerror=()=>t(1.414),s.src=h});URL.revokeObjectURL(h),this.host._spaceDialog={...this.host._spaceDialog,planFile:{ext:o,b64:i,aspect:n,name:e.name}}}_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 p`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return p`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return p`<div class="savedplans">
|
||||
${s.map(s=>{return p`
|
||||
<div class="savedplan ${s.url===t.planUrl?"cur":""}">
|
||||
<img src=${this.host._display(s.url)} alt="" loading="lazy" decoding="async" />
|
||||
@@ -46,7 +46,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75
|
||||
<label>${this.host._t("space.plan_label")}</label>
|
||||
<label class="srcrow">
|
||||
<input type="radio" name="plansrc" .checked=${"file"===t.source}
|
||||
@change=${()=>this.host._spaceDialog=y(t,"file")} />
|
||||
@change=${()=>this.host._spaceDialog=f(t,"file")} />
|
||||
<span>${this.host._t("space.source_file")}</span>
|
||||
</label>
|
||||
${"file"===t.source?p`<div class="planrow">
|
||||
@@ -68,7 +68,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75
|
||||
${t.pickSaved?this._renderServerPlans(t):_}`:_}
|
||||
<label class="srcrow">
|
||||
<input type="radio" name="plansrc" .checked=${"draw"===t.source}
|
||||
@change=${()=>this.host._spaceDialog=y(t,"draw")} />
|
||||
@change=${()=>this.host._spaceDialog=f(t,"draw")} />
|
||||
<span>${this.host._t("space.source_draw")}</span>
|
||||
</label>
|
||||
|
||||
@@ -85,7 +85,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75
|
||||
|
||||
<label class="dispsection">${this.host._t("space.display_section")}</label>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(t.showBorders,s=>{this.host._spaceDialog=f(t,"showBorders",s)})}
|
||||
${this._boolInput(t.showBorders,s=>{this.host._spaceDialog=y(t,"showBorders",s)})}
|
||||
<span>${this.host._t("space.show_borders")}</span>
|
||||
</label>
|
||||
<label>${this.host._t("space.zero_wall_style")}</label>
|
||||
@@ -99,7 +99,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75
|
||||
</select>
|
||||
<div class="rhint">${this.host._t("space.zero_wall_help")}</div>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(t.showNames,s=>{this.host._spaceDialog=f(t,"showNames",s)})}
|
||||
${this._boolInput(t.showNames,s=>{this.host._spaceDialog=y(t,"showNames",s)})}
|
||||
<span>${this.host._t("space.show_names")}</span>
|
||||
</label>
|
||||
<label class="srcrow">
|
||||
+1
-1
@@ -1 +1 @@
|
||||
function e(e,o){return{id:e,title:o,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function o(e="file"){const o="draw"===e;return{source:e,showBorders:o,showNames:o,displayTouched:!1}}function r(e,o){if(e.displayTouched)return{...e,source:o};const r="draw"===o;return{...e,source:o,showBorders:r,showNames:r}}function s(e,o,r){return{...e,[o]:r,displayTouched:!0}}function t(e,o,r){const s=(e?.spaces||[]).find(e=>e?.id===r),t=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),n=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===r||"string"==typeof e.room_id&&t.has(e.room_id)||o?.[e.id]?.s===r).map(e=>e.id))].sort((e,o)=>e.localeCompare(o));return{markerIds:n,count:n.length}}globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75922774e15b28a68631280dcb9457";export{t as a,e as c,o as i,r as s,s as t};
|
||||
function e(e,o){return{id:e,title:o,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function o(e="file"){const o="draw"===e;return{source:e,showBorders:o,showNames:o,displayTouched:!1}}function r(e,o){if(e.displayTouched)return{...e,source:o};const r="draw"===o;return{...e,source:o,showBorders:r,showNames:r}}function s(e,o,r){return{...e,[o]:r,displayTouched:!0}}function t(e,o,r){const s=(e?.spaces||[]).find(e=>e?.id===r),t=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),n=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===r||"string"==typeof e.room_id&&t.has(e.room_id)||o?.[e.id]?.s===r).map(e=>e.id))].sort((e,o)=>e.localeCompare(o));return{markerIds:n,count:n.length}}globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95bafd2bfb8bfeafdfa45a0f5fc090b71ddc2cc9dbf2fb569fb60941c38ef5e8";export{t as a,e as c,o as i,r as s,s as t};
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75922774e15b28a68631280dcb9457";try{await import("./houseplan-assets/houseplan-card-RebBKTYk.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).":"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__="95bafd2bfb8bfeafdfa45a0f5fc090b71ddc2cc9dbf2fb569fb60941c38ef5e8";try{await import("./houseplan-assets/houseplan-card-Cz0AWb2g.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).":"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)}
|
||||
|
||||
@@ -17,6 +17,115 @@ const res = await page.evaluate(async () => {
|
||||
tabs[2].click(); await settleMode();
|
||||
out.decorMode = c._mode === 'decor';
|
||||
out.decorBar = !!sr().querySelector('.editbar.decorbar');
|
||||
// #362: devices in Background are one translucent landmark layer, never
|
||||
// pointer targets. Exercise both the round core and the part of a value
|
||||
// capsule that extends outside the marker's nominal box.
|
||||
const stage = sr().querySelector('.stage');
|
||||
const devlayer = sr().querySelector('.devlayer');
|
||||
const visibleDevices = [...sr().querySelectorAll('.dev')].filter((node) => {
|
||||
const r = node.getBoundingClientRect();
|
||||
const s = stage?.getBoundingClientRect();
|
||||
return r.width > 0 && r.height > 0 && s
|
||||
&& r.left > s.left && r.right < s.right && r.top > s.top && r.bottom < s.bottom;
|
||||
});
|
||||
const inertDevice = visibleDevices.find((node) =>
|
||||
node.classList.contains('valonly') || node.querySelector('.device-shell.with-values'))
|
||||
|| visibleDevices[0];
|
||||
const deviceFrame = inertDevice?.querySelector('.device-shell-frame');
|
||||
const deviceRect = inertDevice?.getBoundingClientRect();
|
||||
const frameRect = deviceFrame?.getBoundingClientRect();
|
||||
const corePoint = deviceRect
|
||||
? { x: deviceRect.left + deviceRect.width / 2, y: deviceRect.top + deviceRect.height / 2 }
|
||||
: null;
|
||||
const capsulePoint = frameRect
|
||||
? { x: frameRect.right - 1, y: frameRect.top + frameRect.height / 2 }
|
||||
: corePoint;
|
||||
const pointTarget = (point) => point ? sr().elementFromPoint(point.x, point.y) : null;
|
||||
const belowDevice = (point) => {
|
||||
const target = pointTarget(point);
|
||||
return !!target && !target.closest?.('.devlayer');
|
||||
};
|
||||
out.decorDeviceLayerIsTranslucent = !!devlayer
|
||||
&& Math.abs(Number(getComputedStyle(devlayer).opacity) - 0.35) < 0.001;
|
||||
out.decorDeviceCoreFallsThrough = belowDevice(corePoint);
|
||||
out.decorDeviceCapsuleFallsThrough = belowDevice(capsulePoint);
|
||||
out.decorDeviceSubtreeIsPointerInert = !!inertDevice && !!deviceFrame
|
||||
&& getComputedStyle(inertDevice).pointerEvents === 'none'
|
||||
&& getComputedStyle(inertDevice, '::before').pointerEvents === 'none'
|
||||
&& getComputedStyle(deviceFrame).pointerEvents === 'none';
|
||||
|
||||
// The CSS boundary is primary; direct dispatch pins the independent handler
|
||||
// guard. A future cascade regression may make the node targetable again, but
|
||||
// Background still cannot hover, actuate or create a device-layout drag.
|
||||
let serviceCalls = 0, wsCalls = 0;
|
||||
const oldCallService = c.hass.callService;
|
||||
const oldCallWS = c.hass.callWS;
|
||||
c.hass.callService = () => { serviceCalls += 1; };
|
||||
c.hass.callWS = async () => { wsCalls += 1; return { ok: true }; };
|
||||
c._tip = null; c._infoCard = null; c._drag = null;
|
||||
inertDevice?.dispatchEvent(new PointerEvent('pointerover', {
|
||||
bubbles: true, composed: true, cancelable: true, pointerId: 360, pointerType: 'mouse', isPrimary: true,
|
||||
}));
|
||||
inertDevice?.dispatchEvent(new PointerEvent('pointerdown', {
|
||||
bubbles: true, composed: true, cancelable: true, pointerId: 361, pointerType: 'mouse',
|
||||
clientX: corePoint?.x || 0, clientY: corePoint?.y || 0, button: 0, isPrimary: true,
|
||||
}));
|
||||
inertDevice?.dispatchEvent(new MouseEvent('click', {
|
||||
bubbles: true, composed: true, cancelable: true, clientX: corePoint?.x || 0,
|
||||
clientY: corePoint?.y || 0, button: 0,
|
||||
}));
|
||||
out.decorDeviceHandlersFailClosed = serviceCalls === 0 && wsCalls === 0
|
||||
&& c._tip === null && c._infoCard === null && c._drag === null;
|
||||
c.hass.callService = oldCallService;
|
||||
c.hass.callWS = oldCallWS;
|
||||
c._pointers.clear(); c._panStart = null; c._panLock = null;
|
||||
|
||||
const dispatchToolPress = (point, pid, shiftKey = false) => {
|
||||
const target = pointTarget(point);
|
||||
target?.dispatchEvent(new PointerEvent('pointerdown', {
|
||||
bubbles: true, composed: true, cancelable: true, pointerId: pid,
|
||||
pointerType: 'mouse', clientX: point?.x || 0, clientY: point?.y || 0,
|
||||
button: 0, isPrimary: true, shiftKey,
|
||||
}));
|
||||
return target;
|
||||
};
|
||||
c._decorTool = 'line'; c._decorDraft = null; await c.updateComplete;
|
||||
dispatchToolPress(capsulePoint, 362);
|
||||
await c.updateComplete;
|
||||
out.lineStartsThroughDeviceCapsule = c._decorDraft?.kind === 'line';
|
||||
c._decorDraft = null;
|
||||
c._decorTool = 'rect'; await c.updateComplete;
|
||||
dispatchToolPress(corePoint, 363);
|
||||
await c.updateComplete;
|
||||
out.rectStartsThroughDeviceCore = c._decorDraft?.kind === 'rect';
|
||||
c._decorDraft = null;
|
||||
c._decorTool = 'text'; c._decorTextDialog = null; await c.updateComplete;
|
||||
dispatchToolPress(corePoint, 364);
|
||||
await c.updateComplete;
|
||||
out.textStartsThroughDeviceCore = !!c._decorTextDialog && !c._decorTextDialog.id;
|
||||
c._decorTextDialog = null;
|
||||
const decorBeforeDeviceProbe = JSON.parse(JSON.stringify(c._decorList));
|
||||
c._editorSecondary.openPalette();
|
||||
c._decorTool = 'furniture'; c._furnPalette = { symbol: 'chair', w: 45, h: 45 };
|
||||
await c.updateComplete;
|
||||
const furniturePoint = visibleDevices.map((node) => {
|
||||
const r = node.getBoundingClientRect();
|
||||
return { x: r.left + r.width / 2, y: r.top + r.height / 2 };
|
||||
}).find((point) => pointTarget(point)?.closest?.('.stage')) || corePoint;
|
||||
out.furnitureToolArmedThroughDeviceCore = c._decorTool === 'furniture' && c._furnPalette?.symbol === 'chair';
|
||||
out.furnitureTargetFallsThroughDeviceCore = belowDevice(furniturePoint);
|
||||
stage?.dispatchEvent(new PointerEvent('pointerdown', {
|
||||
bubbles: true, composed: true, cancelable: true, pointerId: 365,
|
||||
pointerType: 'mouse', clientX: furniturePoint?.x || 0, clientY: furniturePoint?.y || 0,
|
||||
button: 0, isPrimary: true, shiftKey: true,
|
||||
}));
|
||||
await c.updateComplete;
|
||||
out.furniturePlacesThroughDeviceCore = c._decorList.length === decorBeforeDeviceProbe.length + 1
|
||||
&& c._decorList.some((shape) => shape.kind === 'furniture' && shape.symbol === 'chair');
|
||||
c._curSpaceCfg.decor = decorBeforeDeviceProbe;
|
||||
c._decorSel = null; c._decorTool = 'select'; c._furnPalette = null;
|
||||
c._cfgEpoch = (c._cfgEpoch || 0) + 1;
|
||||
c.requestUpdate(); await c.updateComplete;
|
||||
// The compact colour/opacity control must escape the clipped editor/dialog
|
||||
// surface through the browser top layer and remain inside the viewport.
|
||||
c._decorTool = 'line'; c.requestUpdate(); await c.updateComplete;
|
||||
|
||||
Vendored
+48
-48
@@ -1,111 +1,111 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"fingerprint": "2fe93c69042bf1863b46106ae163065bda75922774e15b28a68631280dcb9457",
|
||||
"fingerprint": "95bafd2bfb8bfeafdfa45a0f5fc090b71ddc2cc9dbf2fb569fb60941c38ef5e8",
|
||||
"entry": "houseplan-card.js",
|
||||
"initialViewFiles": [
|
||||
"houseplan-assets/houseplan-card-RebBKTYk.js",
|
||||
"houseplan-assets/houseplan-card-Cz0AWb2g.js",
|
||||
"houseplan-card.js"
|
||||
],
|
||||
"initialViewGzipBytes": 272475,
|
||||
"initialViewGzipBytes": 272677,
|
||||
"lazyFiles": [
|
||||
"houseplan-assets/de-DzID0Btx.js",
|
||||
"houseplan-assets/editor-D72u49-5.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DakYwgZM.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-_Ccbpb04.js",
|
||||
"houseplan-assets/space-deletion-CVy_7Hj9.js"
|
||||
"houseplan-assets/de-D6nVPpxU.js",
|
||||
"houseplan-assets/editor-DREg7QWs.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DMApKDuE.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DyfrTQG3.js",
|
||||
"houseplan-assets/space-deletion-DeqTZ3B0.js"
|
||||
],
|
||||
"lazyGzipBytes": 166317,
|
||||
"lazyGzipBytes": 166287,
|
||||
"lazyEditorFiles": [
|
||||
"houseplan-assets/editor-D72u49-5.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DakYwgZM.js",
|
||||
"houseplan-assets/space-deletion-CVy_7Hj9.js"
|
||||
"houseplan-assets/editor-DREg7QWs.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DMApKDuE.js",
|
||||
"houseplan-assets/space-deletion-DeqTZ3B0.js"
|
||||
],
|
||||
"lazyEditorGzipBytes": 137195,
|
||||
"lazyEditorGzipBytes": 137185,
|
||||
"lazyOnboardingFiles": [
|
||||
"houseplan-assets/houseplan-onboarding-runtime-_Ccbpb04.js",
|
||||
"houseplan-assets/space-deletion-CVy_7Hj9.js"
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DyfrTQG3.js",
|
||||
"houseplan-assets/space-deletion-DeqTZ3B0.js"
|
||||
],
|
||||
"lazyOnboardingGzipBytes": 7205,
|
||||
"lazyOnboardingGzipBytes": 7198,
|
||||
"lazyLocaleFiles": [
|
||||
"houseplan-assets/de-DzID0Btx.js"
|
||||
"houseplan-assets/de-D6nVPpxU.js"
|
||||
],
|
||||
"lazyLocaleGzipBytes": 22472,
|
||||
"lazyLocaleGzipBytes": 22455,
|
||||
"files": [
|
||||
{
|
||||
"path": "houseplan-assets/de-DzID0Btx.js",
|
||||
"sha256": "2be1bf1c986969cfdbbfaff54e048ea71dc117547532088479c31e4549cc266b",
|
||||
"path": "houseplan-assets/de-D6nVPpxU.js",
|
||||
"sha256": "3057f94023d68a1f8f4d7bf69b19465a45c5d017d17e9c8c00b5be2f0c36cbad",
|
||||
"rawBytes": 73919,
|
||||
"gzipBytes": 22472,
|
||||
"gzipBytes": 22455,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/editor-D72u49-5.js",
|
||||
"sha256": "53e388280fce41bb6b7c1d0441460dacdad613044a7e5529df7beda3f6fa9611",
|
||||
"path": "houseplan-assets/editor-DREg7QWs.js",
|
||||
"sha256": "5c08b305112db058ac0ce42b2b6a8e57a6c10d3b2b0b57378b3c3c78a48d351e",
|
||||
"rawBytes": 3831,
|
||||
"gzipBytes": 1587,
|
||||
"gzipBytes": 1581,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-RebBKTYk.js"
|
||||
"houseplan-assets/houseplan-card-Cz0AWb2g.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-card-RebBKTYk.js",
|
||||
"sha256": "f838f63fc5fdea8f9d850520effd618b90a47db7685f0a7c1ddedce296459630",
|
||||
"rawBytes": 960284,
|
||||
"gzipBytes": 271718,
|
||||
"path": "houseplan-assets/houseplan-card-Cz0AWb2g.js",
|
||||
"sha256": "38d44662e84dae93783754caef2ba13bed030ef173ef54a14f642c89e7f4950c",
|
||||
"rawBytes": 961010,
|
||||
"gzipBytes": 271925,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/de-DzID0Btx.js",
|
||||
"houseplan-assets/editor-D72u49-5.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DakYwgZM.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-_Ccbpb04.js"
|
||||
"houseplan-assets/de-D6nVPpxU.js",
|
||||
"houseplan-assets/editor-DREg7QWs.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DMApKDuE.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DyfrTQG3.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-DakYwgZM.js",
|
||||
"sha256": "574e08c63bfb5c42a408aa41496076ffcd315034e7451af6c7a572b958e5e7b5",
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-DMApKDuE.js",
|
||||
"sha256": "6191dd8b6338053c2aa3544ea2e04db05b31bc65474a9623391823b62dbdd394",
|
||||
"rawBytes": 522188,
|
||||
"gzipBytes": 135053,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-RebBKTYk.js",
|
||||
"houseplan-assets/space-deletion-CVy_7Hj9.js"
|
||||
"houseplan-assets/houseplan-card-Cz0AWb2g.js",
|
||||
"houseplan-assets/space-deletion-DeqTZ3B0.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-_Ccbpb04.js",
|
||||
"sha256": "792aeff9ea709c6c648d37109c52eba6758334ed5ae746e0d9cdf30328fa7a52",
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-DyfrTQG3.js",
|
||||
"sha256": "c94cd05d64d3764a2d26a0aac8a59e3d2bbe11a923b3d8c4c9891f362dfcbaae",
|
||||
"rawBytes": 26356,
|
||||
"gzipBytes": 6650,
|
||||
"gzipBytes": 6647,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-RebBKTYk.js",
|
||||
"houseplan-assets/space-deletion-CVy_7Hj9.js"
|
||||
"houseplan-assets/houseplan-card-Cz0AWb2g.js",
|
||||
"houseplan-assets/space-deletion-DeqTZ3B0.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/space-deletion-CVy_7Hj9.js",
|
||||
"sha256": "b704bdc6222b138ce76b0e6250a34e0821709ca643af777203a26744c33bc983",
|
||||
"path": "houseplan-assets/space-deletion-DeqTZ3B0.js",
|
||||
"sha256": "f4eeaf0367d1b6fe333b35c16ff59f2dc7015f7532fa356aaf7c43db9a0e0827",
|
||||
"rawBytes": 911,
|
||||
"gzipBytes": 555,
|
||||
"gzipBytes": 551,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-card.js",
|
||||
"sha256": "b0dd77205cb44aad9107d5fafffdcf9d12e9e43dc448c8b10579e27d3738aa46",
|
||||
"sha256": "724c889fd050357875c6e38996a179a90ed3172a3822c0b97d17d193fc367ee4",
|
||||
"rawBytes": 1089,
|
||||
"gzipBytes": 757,
|
||||
"gzipBytes": 752,
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-RebBKTYk.js"
|
||||
"houseplan-assets/houseplan-card-Cz0AWb2g.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
}
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+7
-7
@@ -1,14 +1,14 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75922774e15b28a68631280dcb9457";import{c as e,l as o,t,cZ as s,A as a,c_ as i,c$ as l,d0 as n,E as r,b as c}from"./houseplan-card-RebBKTYk.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")},_=this._schema,f=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`<ha-form
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95bafd2bfb8bfeafdfa45a0f5fc090b71ddc2cc9dbf2fb569fb60941c38ef5e8";import{c as e,l as o,t,cZ as s,A as a,c_ as i,c$ as l,d0 as n,E as r,b as c}from"./houseplan-card-Cz0AWb2g.js";class f 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,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`<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._formData}
|
||||
.schema=${e}
|
||||
.computeLabel=${e=>h[e.name]||e.name}
|
||||
.computeLabel=${e=>f[e.name]||e.name}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>`;return c`
|
||||
${d(_.slice(0,3))}
|
||||
${f?c`<div class="default-floor-error" role="alert"
|
||||
${d(h.slice(0,3))}
|
||||
${_?c`<div class="default-floor-error" role="alert"
|
||||
style="color:var(--error-color,#db4437);margin:-4px 0 12px;overflow-wrap:anywhere">
|
||||
${t(s,"editor.default_floor_missing",{id:f})}
|
||||
${t(s,"editor.default_floor_missing",{id:_})}
|
||||
</div>`:a}
|
||||
${d(_.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);
|
||||
+20
-10
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
+5
-5
@@ -1,4 +1,4 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75922774e15b28a68631280dcb9457";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 n,b_ as c,b$ as r,b as p,A as _,cR as d,ap as u,ar as g,cS as m}from"./houseplan-card-RebBKTYk.js";import{i as b,c as $,a as v,s as y,t as f}from"./space-deletion-CVy_7Hj9.js";const w=1e3,D=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},S="2fe93c69042bf1863b46106ae163065bda75922774e15b28a68631280dcb9457";class C{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),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,c="none"===h.fill?{...n||e,a:0}:n;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:c,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:c,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:n,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;const o={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"");if(!o)return void this.host._showToast(this.host._t("toast.plan_formats"));const a=new Uint8Array(await e.arrayBuffer());let l="";for(let t=0;t<a.length;t+=32768)l+=String.fromCharCode(...a.subarray(t,t+32768));const i=btoa(l),h=URL.createObjectURL(e),n=await new Promise(t=>{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:1.414),s.onerror=()=>t(1.414),s.src=h});URL.revokeObjectURL(h),this.host._spaceDialog={...this.host._spaceDialog,planFile:{ext:o,b64:i,aspect:n,name:e.name}}}_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 p`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return p`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return p`<div class="savedplans">
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95bafd2bfb8bfeafdfa45a0f5fc090b71ddc2cc9dbf2fb569fb60941c38ef5e8";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 n,b_ as c,b$ as r,b as p,A as _,cR as d,ap as u,ar as g,cS as m}from"./houseplan-card-Cz0AWb2g.js";import{i as b,c as $,a as v,s as f,t as y}from"./space-deletion-DeqTZ3B0.js";const w=1e3,D=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},S="95bafd2bfb8bfeafdfa45a0f5fc090b71ddc2cc9dbf2fb569fb60941c38ef5e8";class C{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),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,c="none"===h.fill?{...n||e,a:0}:n;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:c,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:c,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:n,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;const o={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"");if(!o)return void this.host._showToast(this.host._t("toast.plan_formats"));const a=new Uint8Array(await e.arrayBuffer());let l="";for(let t=0;t<a.length;t+=32768)l+=String.fromCharCode(...a.subarray(t,t+32768));const i=btoa(l),h=URL.createObjectURL(e),n=await new Promise(t=>{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:1.414),s.onerror=()=>t(1.414),s.src=h});URL.revokeObjectURL(h),this.host._spaceDialog={...this.host._spaceDialog,planFile:{ext:o,b64:i,aspect:n,name:e.name}}}_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 p`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return p`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return p`<div class="savedplans">
|
||||
${s.map(s=>{return p`
|
||||
<div class="savedplan ${s.url===t.planUrl?"cur":""}">
|
||||
<img src=${this.host._display(s.url)} alt="" loading="lazy" decoding="async" />
|
||||
@@ -46,7 +46,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75
|
||||
<label>${this.host._t("space.plan_label")}</label>
|
||||
<label class="srcrow">
|
||||
<input type="radio" name="plansrc" .checked=${"file"===t.source}
|
||||
@change=${()=>this.host._spaceDialog=y(t,"file")} />
|
||||
@change=${()=>this.host._spaceDialog=f(t,"file")} />
|
||||
<span>${this.host._t("space.source_file")}</span>
|
||||
</label>
|
||||
${"file"===t.source?p`<div class="planrow">
|
||||
@@ -68,7 +68,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75
|
||||
${t.pickSaved?this._renderServerPlans(t):_}`:_}
|
||||
<label class="srcrow">
|
||||
<input type="radio" name="plansrc" .checked=${"draw"===t.source}
|
||||
@change=${()=>this.host._spaceDialog=y(t,"draw")} />
|
||||
@change=${()=>this.host._spaceDialog=f(t,"draw")} />
|
||||
<span>${this.host._t("space.source_draw")}</span>
|
||||
</label>
|
||||
|
||||
@@ -85,7 +85,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75
|
||||
|
||||
<label class="dispsection">${this.host._t("space.display_section")}</label>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(t.showBorders,s=>{this.host._spaceDialog=f(t,"showBorders",s)})}
|
||||
${this._boolInput(t.showBorders,s=>{this.host._spaceDialog=y(t,"showBorders",s)})}
|
||||
<span>${this.host._t("space.show_borders")}</span>
|
||||
</label>
|
||||
<label>${this.host._t("space.zero_wall_style")}</label>
|
||||
@@ -99,7 +99,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75
|
||||
</select>
|
||||
<div class="rhint">${this.host._t("space.zero_wall_help")}</div>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(t.showNames,s=>{this.host._spaceDialog=f(t,"showNames",s)})}
|
||||
${this._boolInput(t.showNames,s=>{this.host._spaceDialog=y(t,"showNames",s)})}
|
||||
<span>${this.host._t("space.show_names")}</span>
|
||||
</label>
|
||||
<label class="srcrow">
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
function e(e,o){return{id:e,title:o,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function o(e="file"){const o="draw"===e;return{source:e,showBorders:o,showNames:o,displayTouched:!1}}function r(e,o){if(e.displayTouched)return{...e,source:o};const r="draw"===o;return{...e,source:o,showBorders:r,showNames:r}}function s(e,o,r){return{...e,[o]:r,displayTouched:!0}}function t(e,o,r){const s=(e?.spaces||[]).find(e=>e?.id===r),t=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),n=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===r||"string"==typeof e.room_id&&t.has(e.room_id)||o?.[e.id]?.s===r).map(e=>e.id))].sort((e,o)=>e.localeCompare(o));return{markerIds:n,count:n.length}}globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75922774e15b28a68631280dcb9457";export{t as a,e as c,o as i,r as s,s as t};
|
||||
function e(e,o){return{id:e,title:o,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function o(e="file"){const o="draw"===e;return{source:e,showBorders:o,showNames:o,displayTouched:!1}}function r(e,o){if(e.displayTouched)return{...e,source:o};const r="draw"===o;return{...e,source:o,showBorders:r,showNames:r}}function s(e,o,r){return{...e,[o]:r,displayTouched:!0}}function t(e,o,r){const s=(e?.spaces||[]).find(e=>e?.id===r),t=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),n=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===r||"string"==typeof e.room_id&&t.has(e.room_id)||o?.[e.id]?.s===r).map(e=>e.id))].sort((e,o)=>e.localeCompare(o));return{markerIds:n,count:n.length}}globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95bafd2bfb8bfeafdfa45a0f5fc090b71ddc2cc9dbf2fb569fb60941c38ef5e8";export{t as a,e as c,o as i,r as s,s as t};
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2fe93c69042bf1863b46106ae163065bda75922774e15b28a68631280dcb9457";try{await import("./houseplan-assets/houseplan-card-RebBKTYk.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).":"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__="95bafd2bfb8bfeafdfa45a0f5fc090b71ddc2cc9dbf2fb569fb60941c38ef5e8";try{await import("./houseplan-assets/houseplan-card-Cz0AWb2g.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).":"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)}
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Device markers in the Background editor are now passive, translucent
|
||||
landmarks: their core, ring, value capsule and badges no longer hover, open,
|
||||
actuate or drag, and a drawing or furniture tool receives a press through
|
||||
the whole visible marker instead of losing that point
|
||||
([#362](https://github.com/Matysh/houseplan-card/issues/362)).
|
||||
|
||||
## v1.69.0-beta.3 — 2026-08-29
|
||||
|
||||
- Furniture placement in the Background editor now shows a translucent mouse
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
|
||||
## Не выпущено
|
||||
|
||||
- Маркеры устройств в редакторе подложки теперь служат только полупрозрачными
|
||||
ориентирами: ядро, кольцо, капсула значения и бейджи больше не реагируют на
|
||||
наведение, не открываются, не выполняют действия и не перетаскиваются, а
|
||||
инструмент рисования или мебели получает нажатие сквозь всю видимую область
|
||||
маркера ([#362](https://github.com/Matysh/houseplan-card/issues/362)).
|
||||
|
||||
## v1.69.0-beta.3 — 2026-08-29
|
||||
|
||||
- При размещении мебели в редакторе подложки мышь теперь показывает на плане
|
||||
|
||||
@@ -20,7 +20,7 @@ device state or Home Assistant actions.
|
||||
| Scale | Corner drag preserves aspect ratio. Hold `Shift` for independent axes. |
|
||||
| Rotation | 5° steps by default; `Shift` gives free rotation. Lines use endpoint handles instead of a rotation handle. |
|
||||
| Magnet targets | Only other decor objects and room contours: corners, edge centres, centres and edges. The image, devices and openings are excluded. |
|
||||
| Context emphasis | Decor and its editing chrome stay fully opaque. Rooms, labels, devices, openings, positive-thickness walls and solid/dashed zero-thickness walls are contextual only and render at 35% opacity. |
|
||||
| Context emphasis | Decor and its editing chrome stay fully opaque. Rooms, labels, devices, openings, positive-thickness walls and solid/dashed zero-thickness walls are contextual only and render at 35% opacity. The whole device presentation (core, ring, capsule, values and badges) is pointer-inert: it never hovers, opens, acts or drags, and the active Background tool receives a press through it. |
|
||||
| View composition | All decor kinds form one layer above room/data fills, room hover fill, opening-tunnel fills and Glow base. Live Glow, sun, physical walls, opening symbols, devices and room labels remain above decor. The plan image remains below it. |
|
||||
| Compatibility | Legacy `width`, text `size/scale` and `plan_scale` remain readable. New writes use `width_cm`, `size_cm` and `plan_scale_x/y`. |
|
||||
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
# CODE-REVIEW-362-r1
|
||||
|
||||
Issue: #362 · Заход: r1 · Трек: `small` (лимит циклов код-ревью — 2, §5) ·
|
||||
Вердикт: **зелёный** · блокирующих циклов израсходовано 0/2
|
||||
|
||||
Ветка `issue/362-decor-device-inert`. Хендофф был на `fafd3601`; конвейер
|
||||
привёл ветку к `dev` перед ревью (поверх легло 3 коммита `dev`,
|
||||
`fafd3601 → f57a0c08`, вершина `f57a0c08` "fix: make devices inert in the
|
||||
Background editor"). Ветка полностью содержит `origin/dev`
|
||||
(`merge-base(HEAD, origin/dev) == origin/dev`), т.е. это честный fast-forward
|
||||
rebase без потери коммитов. Разбор — полный (это первый заход, и рёбейз того
|
||||
требует по §7.2 в любом случае).
|
||||
|
||||
## Скоуп
|
||||
|
||||
Контракт (тело issue, small-track ТЗ, зелёное спек-ревью r1): в редакторе
|
||||
Подложка (`mode-decor`) вся presentation-subtree устройства (ядро, 44px
|
||||
псевдо-хит-область, shell/frame, капсула значения, LQI/бейджи, pulse,
|
||||
opening-lock сателлиты) должна быть инертна к hit-testing, а нажатие в той же
|
||||
точке — доставаться активному инструменту Подложки. View и Устройства не
|
||||
меняются. Задача входит в J4/J6 (`docs/SCOPE.md`) и восстанавливает
|
||||
инвариант, уже задокументированный в `docs/DECOR-EDITOR.md` (opacity 0.35,
|
||||
устройства вне magnet targets).
|
||||
|
||||
## Как проверялось
|
||||
|
||||
Дешёвые гейты (обязательны всегда, прогнаны на `f57a0c08`):
|
||||
|
||||
| Гейт | Команда | Результат |
|
||||
|---|---|---|
|
||||
| Typecheck | `npx tsc --noEmit` | PASS, без вывода |
|
||||
| Unit | `npm test` | PASS — 1511 passed, 1 skipped, 0 failed (`# tests 1512`) |
|
||||
| Build + bundle sync | `npm run build && npm run bundle:sync`, затем `cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js` и `cmp dist/houseplan-card.js demo/srv/assets/houseplan-card.js` | PASS, три копии бандла побайтово идентичны |
|
||||
| any-бюджет | `node scripts/no-new-any.mjs --base origin/dev --head HEAD` | PASS — «Новых any нет» (32 добавленные строки в 2 файлах) |
|
||||
| Whitespace | `git diff --check origin/dev...HEAD` | PASS, без вывода |
|
||||
| Docs fingerprint | `node scripts/check-docs.mjs` (обязателен — diff трогает `src/**`) | **RED** ожидаемо: `ERROR screenshot source fingerprint is stale`. Проверено сравнением: тот же гейт на `origin/dev`@`9431a5ce` (временный `git worktree`, чистая сборка) — **PASS** («Documentation checks passed»). Значит, стал stale именно из-за этой задачи. Это не эквивалент #230/#234 (там шаг вообще не прогоняли и находка терялась): отпечаток считается по всему `src/**`, поэтому *любая* правка фронтенда обязана его инвалидировать — «выбирать тут нечего» (см. текст задания ревью). Пересъёмка — отдельная джоба `Docs screenshots` (`workflow_dispatch`) с последующим `npm run docs:accept -- --reviewed`, коммит делает человек (`PROCESS.md` §8); это не часть цикла реализации и не повод для жёлтого вердикта, но обязана быть сделана до релиза — фиксирую как открытый пункт для релиз-менеджера, а не как находку к этому issue. |
|
||||
|
||||
По необходимости (определено diff'ом и AC):
|
||||
|
||||
- `node scripts/smoke-select.mjs --base origin/dev --head HEAD` — выборка:
|
||||
**прямое совпадение (12)**: `smoke_decor.mjs`, `smoke_inert_openings.mjs`,
|
||||
`smoke_isometric_live_touch.mjs`, `smoke_modes.mjs`,
|
||||
`smoke_space_settings.mjs`, `smoke_esc_dialogs.mjs`, `smoke_feedback_v2.mjs`,
|
||||
`smoke_lock_action.mjs`, `smoke_open_passage.mjs`,
|
||||
`smoke_opening_binding.mjs`, `smoke_registryless_opening.mjs`,
|
||||
`smoke_touch_tips.mjs`; плюс 29 «слабых» (одно распространённое имя —
|
||||
`_mode`/`stopPropagation`), решение по ним — не гонять: диф не трогает то
|
||||
поведение, о котором они спорят (wall/room/cover/furniture инструменты вне
|
||||
Подложки), совпадение по имени случайно.
|
||||
Прогнаны все 12 прямых (свежий бандл, `node demo/<name>.mjs` каждый) —
|
||||
**все PASS**, включая `smoke_decor.mjs`, обновлённый этой задачей.
|
||||
Опенинг-смоки (`smoke_inert_openings`, `smoke_lock_action`,
|
||||
`smoke_esc_dialogs`, `smoke_open_passage`, `smoke_opening_binding`,
|
||||
`smoke_registryless_opening`) прогнаны специально: правка тронула JS-гвард
|
||||
`.oplock`-обработчика клика (см. находку Low ниже), эти смоки — прямая
|
||||
regression-проверка того, что `View`-поведение замка (`_openingInfo`,
|
||||
диалог) не сломано. `smoke_modes.mjs` подтверждает регрессии нет в
|
||||
`Devices` (`devDragWorks`, `devClickOpensEditor` — оба true).
|
||||
- `npm run golden:verify` — **не гонялся**. Diff не меняет ни одного
|
||||
painted-свойства: `opacity`/цвет/geometry устройств не тронуты, единственное
|
||||
новое CSS-свойство — `pointer-events`, которое не участвует в растеризации.
|
||||
Единственная удалённая строка (`.stage.mode-decor .oplock { pointer-events:
|
||||
none }`) была вычислительно избыточна и до правки: базовое правило `.oplock`
|
||||
уже задаёт `pointer-events: none`, `auto` включается только
|
||||
`.stage.mode-view .oplock` — вычисленное значение в `mode-decor` не
|
||||
изменилось. AC4 в самом ТЗ также утверждает «golden без переприёмки».
|
||||
- `python -m pytest tests_backend` — не гонялся, diff не трогает
|
||||
`custom_components/**/*.py`.
|
||||
- `node scripts/model-invariants.mjs` — не гонялся, diff не меняет геометрию,
|
||||
`layout`, `marker.space` или `open_spans`.
|
||||
- performance-профили — не названы в AC, не гонялись.
|
||||
|
||||
**«Одно число — один источник»**: диф не вводит и не меняет ни одной новой
|
||||
пользовательской величины. Единственное число рядом с изменением — opacity
|
||||
`0.35`, которое уже было односточником через `architectureOpacity` в
|
||||
`_modeVisualState`/`houseplan-card.ts:1212,1394`, потребляемое CSS-переменной
|
||||
`--hp-mode-architecture-opacity`; эта задача её не трогает.
|
||||
|
||||
### Тест умеет падать — проверено фактическим ревертом, не заявлением
|
||||
|
||||
Дважды откатывал часть фикса на рабочем дереве (сборка, прогон смока/юнита,
|
||||
затем восстановление файла из бэкапа, повторная сборка и `cmp` трёх копий
|
||||
бандла — дерево вернулось чистым, `git status` пуст):
|
||||
|
||||
1. Откат CSS-правила `.stage.mode-decor .devlayer, .stage.mode-decor
|
||||
.devlayer *, .stage.mode-decor .dev::before` к старому
|
||||
`.stage.mode-decor .devlayer { pointer-events: none; }` →
|
||||
`node demo/smoke_decor.mjs` **упал** на трёх ассертах:
|
||||
`decorDeviceCoreFallsThrough`, `decorDeviceCapsuleFallsThrough`,
|
||||
`decorDeviceSubtreeIsPointerInert` (ожидалось true, получено false).
|
||||
2. Откат гварда в `_pointerDown` к старому `if (this._mode === 'plan')
|
||||
return;` → `node demo/smoke_decor.mjs` **упал** на 10 ассертах:
|
||||
`decorDeviceHandlersFailClosed`, `lineStartsThroughDeviceCapsule`,
|
||||
`rectStartsThroughDeviceCore`, `textStartsThroughDeviceCore`,
|
||||
`furniturePlacesThroughDeviceCore` и 5 каскадных (`lineToolStartsDraft` и
|
||||
др. — устройство перехватило `_drag`, что сломало последующие проверки
|
||||
инструментов).
|
||||
|
||||
`test/device-marker-polish-contract.test.mjs` — новый юнит-тест регексами
|
||||
сверяет ровно те же строки кода (CSS-правило, порядок гварда/`stopPropagation`
|
||||
в `_clickDevice`, гварды `_pointerDown/_pointerMove/_pointerUp`, условия в
|
||||
`@pointerover`/`@pointermove`); ревертнутые выше строки буквально не совпали
|
||||
бы с этими регексами — падение подтверждено тем же способом, без отдельного
|
||||
повторного прогона.
|
||||
|
||||
## AC — разбор
|
||||
|
||||
| AC | Статус | Доказательство |
|
||||
|---|---|---|
|
||||
| AC1 (hit-testing инертен для ядра/капсулы) | **Выполнен** | `smoke_decor.mjs`: `decorDeviceCoreFallsThrough`, `decorDeviceCapsuleFallsThrough`, `decorDeviceSubtreeIsPointerInert` — true; подтверждено умением падать (см. выше). CSS-специфичность проверена чтением: `.stage.mode-decor .devlayer *` (0,2,0) бьёт одноклассовые `.dev`/`.device-shell-frame` `pointer-events: auto` (0,1,0) в `devices.styles.ts:144,213`; `::before` — отдельным явным селектором, т.к. `*` не матчит псевдоэлементы. |
|
||||
| AC2 (Line/Rect/Text/Furniture сквозь устройство) | **Выполнен** | `smoke_decor.mjs`: `lineStartsThroughDeviceCapsule`, `rectStartsThroughDeviceCore`, `textStartsThroughDeviceCore`, `furnitureToolArmedThroughDeviceCore`+`furnitureTargetFallsThroughDeviceCore`+`furniturePlacesThroughDeviceCore` — все true. Умение падать подтверждено (пункт 2 выше). |
|
||||
| AC3 (нет device-побочных эффектов) | **Выполнен** | `decorDeviceHandlersFailClosed` (serviceCalls=0, wsCalls=0, `_tip`/`_infoCard`/`_drag` не выставлены) при прямом dispatch pointerover/pointerdown/click — true. Прочитан код: `_clickDevice`/`_pointerDown` возвращают до какого-либо побочного эффекта при `_mode ∉ {view, devices}`; `_pointerMove`/`_pointerUp` (drag/selection/`_savePos`) — гвард `_mode !== 'devices'`; long-press таймер живёт только в view-ветке `_pointerDown`, недостижим в Background. |
|
||||
| AC4 (opacity 0.35 стабилен, смена инструмента не меняет пассивность) | **Выполнен** | `decorDeviceLayerIsTranslucent` (opacity≈0.35) — true; core/capsule fall-through и through-device постановка проверены при 4 разных `_decorTool` (line/rect/text/furniture). Golden не переприёмывался — обоснование в разделе «как проверялось» (paint не меняется). |
|
||||
| AC5 (View/Devices не деградировали) | **Выполнен** | Регрессионные смоки: `smoke_decor.mjs` `visibleInView`/`inertInView` (для decor-фигур, не устройств — существующий несвязанный ассерт, не трогался); `smoke_modes.mjs` `devDragWorks`/`devClickOpensEditor` — true (Devices editor). View-специфичный `_pointerDown`-путь (long-press → `_infoCard`) не тронут кодом (ветка `if (this._mode === 'view')` идёт первой и делает `return` до нового гварда — гвард лишь сузил, какие ещё режимы доходят дальше). Opening-lock смоки (6 штук, список выше) — все PASS, без регрессии `_openingInfo`/View-клика. |
|
||||
| AC6 (нет config/i18n/perf/touch изменений) | **Выполнен** | `git diff --stat` — только `src/houseplan-card.ts`, `src/styles/plan.styles.ts`, `demo/smoke_decor.mjs`, `test/*.mjs`, `docs/*`, бандлы; ни одного i18n/schema файла. Новых listener/observer/render-путей нет — все изменения это ранние `return` внутри уже существующих обработчиков плюс чисто декларативный CSS-каскад. Pointer/pen/touch не различаются нигде в изменённом коде (`PointerEvent`-путь общий для всех `pointerType`), поэтому инертность автоматически распространяется на все типы указателя без отдельного touch-контракта. |
|
||||
|
||||
## Находки
|
||||
|
||||
### Low — верифицировано чтением, фикс не требуется
|
||||
|
||||
`_openingInfo`-обработчик клика по `.oplock` (`src/houseplan-card.ts`,
|
||||
`@click` в `_renderOpeningLocks`) получил тот же паттерн гварда
|
||||
(`if (this._mode !== 'view') return;` до `stopPropagation()`), хотя контракт
|
||||
ТЗ явно называет «opening-lock satellites» частью инертной subtree (пункт 1),
|
||||
а сам AC1 в таблице доказательства называет только `.dev`/`::before`/
|
||||
`.device-shell-frame`/capsule — без oplock. Ни один AC, ни новый блок
|
||||
`smoke_decor.mjs` не проверяют это напрямую для `mode-decor`.
|
||||
|
||||
Прочитано и проверено: `.oplock` уже до этой задачи имел базовое
|
||||
`pointer-events: none` (`dialogs.styles.ts:167`, комментарий «inert while
|
||||
editing; clickable in View (rule below)»), и `auto` включался только
|
||||
`.stage.mode-view .oplock` (`plan.styles.ts:488`). То есть в реальном браузере
|
||||
хэндлер и до, и после правки недостижим кликом мыши/тача в `plan`/`devices`/
|
||||
`decor` — CSS уже резал hit-testing независимо от этой задачи. Изменение —
|
||||
чисто defense-in-depth, симметричное остальному фиксу (контрактный пункт 6:
|
||||
«не полагаться только на `pointer-events: none` родителя»), и не меняет
|
||||
наблюдаемое поведение ни в одном режиме. 6 opening-related смоков (список
|
||||
выше) подтверждают отсутствие регрессии в View. Не блокирует: находка не
|
||||
меняет функциональность и не требует правки в этом issue.
|
||||
|
||||
### Не найдено High/Medium
|
||||
|
||||
Ни одного High. Ни одного Medium — ни в скоупе, ни вне его. Новый issue не
|
||||
заводится.
|
||||
|
||||
## Что проверено и корректно
|
||||
|
||||
- Двойная защита (CSS-каскад + fail-closed JS-гварды) реализована так, как
|
||||
требует контракт: ни один слой не полагается только на другой.
|
||||
- CSS-специфичность нового правила действительно перебивает все три
|
||||
`pointer-events: auto` в `devices.styles.ts`, включая псевдоэлемент
|
||||
`::before`, отдельно вынесенный, поскольку универсальный селектор `*` его
|
||||
не матчит.
|
||||
- Гварды в обработчиках копируют структуру существующего `_ctxDevice`
|
||||
(правый клик — уже был fail-closed вне View до этой задачи, не тронут) —
|
||||
согласованный паттерн по всей поверхности.
|
||||
- `_keyDevice` (клавиатурная активация) уже был защищён гвардом `_mode
|
||||
!== 'view' && _mode !== 'devices'` до этой задачи — не тронут, риска нет
|
||||
(в Background `tabindex` не проставляется, элемент не фокусируем).
|
||||
- И новый юнит-тест, и новый блок смока проверены на умение падать прямым
|
||||
ревертом кода (см. выше) — не приняты на слово автора.
|
||||
- Терминология CHANGELOG/DECOR-EDITOR.md сверена с `docs/USER-GUIDE.ru.md`
|
||||
и `docs/USER-GUIDE.md` («Подложка»/Background editor) — совпадает.
|
||||
- Трейлеры коммита `f57a0c08`: `Issue: #362`, `User-Visible: yes` — оба
|
||||
changelog (`docs/CHANGELOG.md`, `docs/CHANGELOG.ru.md`) правлены в том же
|
||||
коммите.
|
||||
- Три копии бандла (`dist`, `custom_components/.../frontend`,
|
||||
`demo/srv/assets`) побайтово идентичны после `npm run bundle:sync`.
|
||||
|
||||
## Чего не проверял
|
||||
|
||||
- `npm run golden:verify` и `pytest tests_backend` — не гонялись, обоснование
|
||||
выше (diff не меняет paint и не трогает Python).
|
||||
- Полный набор `demo/smoke_*.mjs` (не выборка) — не гонялся, диф не задевает
|
||||
геометрию/стены/проёмы вне уже прогнанных opening-смоков; полный набор —
|
||||
предрелизный гейт.
|
||||
- Performance-профили — не названы в AC, не запускались.
|
||||
- Пересъёмка docs-скриншотов (гейт `docs` в CI) — не выполнялась и не может
|
||||
быть выполнена в этом цикле (нужна выделенная джоба `Docs screenshots` на
|
||||
точном Chromium плюс `--reviewed`-приёмка человеком); зафиксировано выше как
|
||||
открытый пункт для релиз-менеджера, не как находка к этому issue.
|
||||
+20
-5
@@ -5177,9 +5177,12 @@ export class HouseplanCard extends LitElement {
|
||||
}
|
||||
|
||||
private _clickDevice(ev: Event, d: DevItem): void {
|
||||
// Devices are passive landmarks in Plan/Background. Guard before stopping
|
||||
// propagation so a Background tool still owns the same canvas point even
|
||||
// if a future CSS change accidentally makes a marker a hit target again.
|
||||
if (this._mode !== 'view' && this._mode !== 'devices') return;
|
||||
ev.stopPropagation();
|
||||
if (this._drag?.moved || this._suppressClick || this._holdFired) return;
|
||||
if (this._mode === 'plan') return;
|
||||
if (this._mode === 'devices') {
|
||||
this._openMarkerDialog(d);
|
||||
return;
|
||||
@@ -6335,7 +6338,10 @@ export class HouseplanCard extends LitElement {
|
||||
}
|
||||
|
||||
private _pointerDown(ev: PointerEvent, d: DevItem): void {
|
||||
if (this._mode === 'plan') return; // icons are hidden in plan mode anyway
|
||||
// Only View and the Devices editor own device input. In Background the
|
||||
// event must keep bubbling to the active decor tool; do not prevent it,
|
||||
// capture it or create a layout drag.
|
||||
if (this._mode !== 'view' && this._mode !== 'devices') return;
|
||||
if (this._mode === 'view') {
|
||||
// view: no drag, no capture — panning may start on an icon; only the
|
||||
// long-press timer runs (cancelled by stage movement)
|
||||
@@ -6361,6 +6367,7 @@ export class HouseplanCard extends LitElement {
|
||||
}
|
||||
|
||||
private _pointerMove(ev: PointerEvent, d: DevItem): void {
|
||||
if (this._mode !== 'devices') return;
|
||||
if (!this._drag || this._drag.id !== d.id) return;
|
||||
const stage = this.renderRoot.querySelector('.stage') as HTMLElement;
|
||||
if (!stage) return;
|
||||
@@ -6387,6 +6394,7 @@ export class HouseplanCard extends LitElement {
|
||||
|
||||
private _pointerUp(_ev: PointerEvent, d: DevItem): void {
|
||||
clearTimeout(this._holdTimer);
|
||||
if (this._mode !== 'devices') return;
|
||||
if (!this._drag || this._drag.id !== d.id) return;
|
||||
const moved = this._drag.moved;
|
||||
this._drag = moved ? this._drag : null;
|
||||
@@ -12017,11 +12025,14 @@ export class HouseplanCard extends LitElement {
|
||||
@click=${(e: MouseEvent) => this._clickDevice(e, d)}
|
||||
@keydown=${(e: KeyboardEvent) => this._keyDevice(e, d)}
|
||||
@contextmenu=${(e: MouseEvent) => this._ctxDevice(e, d)}
|
||||
@pointerover=${(e: PointerEvent) =>
|
||||
this._showTip(e, d.name, presentation.haDisabled ? ghostLabel : metrics)}
|
||||
@pointerover=${(e: PointerEvent) => {
|
||||
if (this._mode !== 'view' && this._mode !== 'devices') return;
|
||||
this._showTip(e, d.name, presentation.haDisabled ? ghostLabel : metrics);
|
||||
}}
|
||||
@pointerleave=${() => this._clearTransientHover()}
|
||||
@pointerdown=${(e: PointerEvent) => this._pointerDown(e, d)}
|
||||
@pointermove=${(e: PointerEvent) => {
|
||||
if (this._mode !== 'view' && this._mode !== 'devices') return;
|
||||
this._pointerMove(e, d);
|
||||
this._showTip(e, d.name, presentation.haDisabled ? ghostLabel : metrics);
|
||||
}}
|
||||
@@ -12527,7 +12538,11 @@ export class HouseplanCard extends LitElement {
|
||||
const top = ((point[1] - view.y) / view.h) * 100;
|
||||
return html`<div class="oplock ${deviceThemeClass(this._renderPlanHass)} ${locked ? 'locked' : known ? 'unlocked' : 'unknown'}"
|
||||
style="left:${left}%;top:${top}%"
|
||||
@click=${(e: MouseEvent) => { e.stopPropagation(); if (this._mode === 'view') this._openingInfo = o; }}>
|
||||
@click=${(e: MouseEvent) => {
|
||||
if (this._mode !== 'view') return;
|
||||
e.stopPropagation();
|
||||
this._openingInfo = o;
|
||||
}}>
|
||||
<span class="oplock-shell" aria-hidden="true">
|
||||
<span class="oplock-core">
|
||||
<ha-icon icon="${locked ? 'mdi:lock' : known ? 'mdi:lock-open-variant' : 'mdi:lock-question'}"></ha-icon>
|
||||
|
||||
@@ -853,8 +853,18 @@ export const planStyles = css`
|
||||
.stage.mode-decor.dtool-erase .decorlayer .dshape {
|
||||
cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cg transform='rotate(-45 12 12)'%3E%3Crect x='7' y='2' width='10' height='18' rx='2' fill='%23fff' stroke='%23111' stroke-width='1.5'/%3E%3Cpath d='M7 13h10v5a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2z' fill='%23ff9f43' stroke='%23111' stroke-width='1.5'/%3E%3C/g%3E%3C/svg%3E") 5 22, pointer;
|
||||
}
|
||||
.stage.mode-decor .room, .stage.mode-decor .devlayer { pointer-events: none; }
|
||||
.stage.mode-decor .oplock { pointer-events: none; }
|
||||
.stage.mode-decor .room { pointer-events: none; }
|
||||
/* Devices are landmarks in Background, never editing targets. The marker
|
||||
package deliberately re-enables pointer events on the core, its 44 px
|
||||
pseudo hit area and the visible capsule for View/Devices. A none on
|
||||
devlayer alone therefore does not make its HTML descendants inert.
|
||||
Scope the boundary to the whole subtree (including the pseudo element)
|
||||
so a Background tool receives the exact point below every visible part. */
|
||||
.stage.mode-decor .devlayer,
|
||||
.stage.mode-decor .devlayer *,
|
||||
.stage.mode-decor .dev::before {
|
||||
pointer-events: none;
|
||||
}
|
||||
/* Backdrop-editor de-emphasis is a shared mode-transition coordinate.
|
||||
It multiplies whole presentation groups and never changes Glow source
|
||||
alpha, additive blending, or the underlying resolved state. */
|
||||
|
||||
@@ -37,6 +37,33 @@ test('issue 213 gives the visual shell a shared-centre frame that owns capsule i
|
||||
assert.match(styles, /\.device-shell\s*\{[\s\S]*padding:\s*0/);
|
||||
});
|
||||
|
||||
test('issue 362 makes the whole device package inert only in Background', () => {
|
||||
const styles = source('styles.ts');
|
||||
const card = source('houseplan-card.ts');
|
||||
|
||||
assert.match(
|
||||
styles,
|
||||
/\.stage\.mode-decor \.devlayer,\s*\.stage\.mode-decor \.devlayer \*,\s*\.stage\.mode-decor \.dev::before\s*\{\s*pointer-events:\s*none/,
|
||||
'the scoped boundary must beat the core, pseudo hit area and capsule auto rules',
|
||||
);
|
||||
|
||||
const click = card.slice(card.indexOf('private _clickDevice('), card.indexOf('private _showUnavailableToggleTargets('));
|
||||
assert.match(
|
||||
click,
|
||||
/if \(this\._mode !== 'view' && this\._mode !== 'devices'\) return;\s*ev\.stopPropagation\(\)/,
|
||||
'the fail-closed guard must run before propagation is stopped',
|
||||
);
|
||||
const down = card.slice(card.indexOf('private _pointerDown('), card.indexOf('private _showToast('));
|
||||
assert.match(down, /private _pointerDown[\s\S]*this\._mode !== 'view' && this\._mode !== 'devices'/);
|
||||
assert.match(down, /private _pointerMove[\s\S]*this\._mode !== 'devices'/);
|
||||
assert.match(down, /private _pointerUp[\s\S]*clearTimeout\(this\._holdTimer\);\s*if \(this\._mode !== 'devices'\) return/,
|
||||
'View pointerup must still cancel its long-press timer before rejecting drag');
|
||||
|
||||
const render = card.slice(card.indexOf('private _renderDevice('), card.indexOf('private _roomArea('));
|
||||
assert.match(render, /@pointerover=\$\{[\s\S]*this\._mode !== 'view' && this\._mode !== 'devices'/);
|
||||
assert.match(render, /@pointermove=\$\{[\s\S]*this\._mode !== 'view' && this\._mode !== 'devices'/);
|
||||
});
|
||||
|
||||
test('issue 213 projects opening locks through the compact package layers', () => {
|
||||
const styles = source('styles.ts');
|
||||
const card = source('houseplan-card.ts');
|
||||
|
||||
Reference in New Issue
Block a user