Files
houseplan-card/dist/houseplan-assets/houseplan-onboarding-runtime-BkJZgs7A.js
T
Codex bb3c1fd12f fix: a cold View survives a telemetry-bearing vacuum (#358)
Systematic audit after #357 ("can there be more bugs with this root
cause?"): _vacMapId was the one remaining hard stub reachable from the
eager View path. It runs inside willUpdate for every vacuum whose
integration reports live telemetry (Tasshack, XCME, Valetudo), so on a
cold tab the #337 stub threw there and the exception took the whole Lit
update cycle with it — the card froze on its very first frame. The demo
mower has no position attributes, telemetry resolved to null, and every
existing smoke (warm and cold) sailed past the branch.

The card now owns the implementation (both dependencies — _vacEntity and
vacMapIdWithFallback — were already eager); the editor runtime delegates
back to the host. The HP-1541-01 invariant (selected_map: 0 is a real map
id, nullish not truthy) moves verbatim and is pinned by the new smoke.

Hardened alongside (audit Lows): _decorShapeDown gets the same
cold-tab guard its twin _decorShapeDbl received in #337 — decor shapes
render in View and CSS pointer-events alone must not be what prevents a
throw; the _vacCalConfirm dialog renders behind the same _editorRuntime
gate as every other editor dialog instead of relying on the implicit
"only the runtime ever sets it".

smoke_cold_view_vacuum: cold tab, vacuum with vacuum_position and
selected_map: 0 — the card commits three successive telemetry frames
(willUpdate alive, not merely the first paint), map id resolves to '0',
no editor chunk requested, a decor pointerdown is a quiet no-op. A
registry mutant restores the delegation and is killed by that smoke.

Issue: #358
User-Visible: yes
2026-08-28 21:49:50 +03:00

218 lines
26 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="c673cca61c08a44252a2303b34ba28b96411e9d5fddb18c938f79fbbc00ccd7f";import{bU as t,bV as s,bW as e,bX as o,aq as a,bY as l,c1 as i,bZ as h,b_ as c,b$ as n,c0 as r,b as p,A as _,cS as d,ap as u,ar as g,cT as m}from"./houseplan-card-CmrzFyoP.js";import{i as b,c as $,a as v,s as f,t as y}from"./space-deletion-BfZFFcpf.js";const w=1e3,D=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},S="c673cca61c08a44252a2303b34ba28b96411e9d5fddb18c938f79fbbc00ccd7f";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),c=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,n="none"===h.fill?{...c||e,a:0}:c;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:n,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:n,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:c,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;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),c=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:c,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" />
<div class="savedmeta">
<b>${s.name}</b>
<span class="muted">${e=s.size,e>=1048576?`${(e/1048576).toFixed(1)} MB`:`${Math.round(e/1024)} KB`}${s.used_by.length?` · ${this.host._t("space.used_by",{list:s.used_by.join(", ")})}`:""}</span>
</div>
<button class="btn ghost" @click=${()=>this._useServerPlan(s.url)}
?disabled=${s.url===t.planUrl}>${this.host._t("btn.use")}</button>
<button class="btn ghost danger"
title=${s.used_by.length||s.url===t.planUrl?this.host._t("space.in_use"):this.host._t("btn.delete")}
?disabled=${s.used_by.length>0||s.url===t.planUrl}
@click=${()=>this._deleteServerPlan(s.name)}>
<ha-icon icon="mdi:trash-can-outline"></ha-icon>
</button>
</div>`;var e})}
</div>`}async _saveConfigNow(){this.host._cfgEpoch++;try{await this.host._writeConfig()}catch(t){const s=t;throw s?.physicalGeometryRolledBack?await this.host._reloadRejectedPhysicalWrite():"conflict"===s?.code&&await this.host._reloadConfigOnly(),t}}async _saveSpaceDialog(){const t=this.host._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this.host._showToast(this.host._t("toast.plan_required"));const s="create"===t.mode&&0===(this.host._serverCfg?.spaces.length||0);this.host._spaceDialog={...t,busy:!0};try{const e="create"===t.mode?`s${Date.now().toString(36)}`:t.spaceId;let o=null;if("file"===t.source&&t.planFile){o={url:(await this.host.hass.callWS({type:"houseplan/plan/set",space_id:e,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let a=t.savedAspect||null;!o&&"file"===t.source&&t.planUrl&&!a&&this.host._aspectJob&&(a=await this.host._aspectJob||null);const l=this.host._serverCfg;let i=l.spaces.find(t=>t.id===e);if("create"===t.mode)i=$(e,t.title.trim()),l.spaces.push(i);else{if(!i)throw new Error(`space ${e} is gone from the config`);i.title=t.title.trim()}if(!i)throw new Error(`space ${e} is unavailable`);if(o?(i.plan_url=o.url,i.plan_aspect=o.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==i.plan_url&&(i.plan_url=t.planUrl,i.plan_aspect=a),"draw"===t.source&&(i.plan_url=null,i.plan_aspect=null,delete i.plan_x,delete i.plan_y,delete i.plan_scale,delete i.plan_scale_x,delete i.plan_scale_y,delete i.plan_angle),i.settings={...i.settings||{},show_borders:t.showBorders,show_names:t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,custom_fill:t.customFill||void 0,glow_enabled:t.glowEnabled,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):c,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):h,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},i.zero_wall_style=t.zeroWallStyle,i.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(w,t.cellCm)):5,await this._saveConfigNow(),this.host._spaceDialog=null,"create"===t.mode&&this.host._commitSpace(i.id),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._importQueue.length)this._openNextImport();else if(s||this.host._importTotal>0){const t=this.host._importTotal>0;this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),await this.host._requestMode("plan"),this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this.host._primeDrawWallField(),this.host._showToast(this.host._t(s&&!t?"toast.space_added_onboard":"import.done"))}else this.host._showToast(this.host._t("create"===t.mode?"toast.space_added":"toast.space_saved")),"create"===t.mode&&("plan"!==this.host._mode?await this.host._requestMode("plan"):(this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this.host._primeDrawWallField(),this.host._saveNav()))}catch(t){const s=t;"conflict"!==s?.code&&await this.host._reloadConfigOnly(!0),this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _deleteSpace(){const t=this.host._spaceDialog;if(!t||"edit"!==t.mode)return;const s=this.host._serverCfg;if(!s)return;const e=s.spaces.find(s=>s.id===t.spaceId);if(!e)return;const o=v(s,this.host._layout||{},t.spaceId||""),a=1===s.spaces.length&&s.spaces[0]?.id===t.spaceId;if(!o.count||a){if(confirm(this.host._t("confirm.delete_space",{title:e.title}))){this.host._spaceDialog={...t,deleteBlockers:0,busy:!0};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),this.host._persistLayout.pending()&&this.host._persistLayout.flush(),await this.host._writeChain;const s=await this.host.hass.callWS({type:"houseplan/space/delete",space_id:t.spaceId,expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev}),[e,o]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._adoptStructuralResponses(e,o),this.host._cfgRev=s?.config_rev??this.host._cfgRev,this.host._layoutRev=s?.layout_rev??this.host._layoutRev,this.host._spaceDialog=null,this.host._space===t.spaceId&&this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||""),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.space_deleted"))}catch(s){const e=s;"conflict"!==e?.code&&"space_in_use"!==e?.code||await Promise.all([this.host._reloadConfigOnly(!0),this.host._reloadLayoutOnly()]);const o=this.host._serverCfg;if(this.host._spaceDialog&&o){const s=v(o,this.host._layout||{},t.spaceId||""),e=1===o.spaces.length&&o.spaces[0]?.id===t.spaceId;this.host._spaceDialog={...this.host._spaceDialog,busy:!1,deleteBlockers:e?0:s.count}}this.host._showToast(this.host._t("toast.delete_failed",{err:this.host._errText(s)}))}}}else this.host._spaceDialog={...t,deleteBlockers:o.count}}_startImport(){const t=this.host._importDialog;if(!t)return;const s=t.floors.filter(t=>t.checked).map(t=>t.name);this.host._importDialog=null,s.length?(this.host._importQueue=s,this.host._importTotal=s.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this.host._importQueue.shift();if(void 0===t)return;const s=i(this.host._imperial);this.host._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:n,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:c,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:s,cellCmInput:o(s,this.host._imperial),cellCmTouched:!1,busy:!1}}_skipImport(){this.host._spaceDialog=null,this.host._importQueue.length?this._openNextImport():this.host._importTotal>0&&this.host._model.length&&(this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),this.host._requestMode("plan").then(()=>{this.host._showToast(this.host._t("import.done"))}))}_renderImportDialog(){const t=this.host._importDialog,s=t.floors.filter(t=>t.checked).length;return p`<hp-dialog .hass=${this.host.hass} .title=${this.host._t("import.title")}
icon="mdi:home-floor-1" @hp-close=${()=>this.host._importDialog=null}>
<div class="body">
<div class="rhint">${this.host._t("import.hint")}</div>
${t.floors.map((s,e)=>p`<label class="floorrow">
<input type="checkbox" .checked=${s.checked}
@change=${o=>{const a=[...t.floors];a[e]={...s,checked:o.target.checked},this.host._importDialog={floors:a}}} />
<span>${s.name}</span>
${null!=s.level?p`<span class="floorlvl">L${s.level}</span>`:_}
</label>`)}
</div>
<div class="row" slot="footer">
<button class="btn ghost" @click=${()=>{this.host._importDialog=null,this._openSpaceDialog("create")}}>${this.host._t("import.manual")}</button>
<span class="spacer"></span>
<button class="btn on" @click=${()=>this._startImport()} ?disabled=${!s}>
<ha-icon icon="mdi:import"></ha-icon>${this.host._t("import.start",{n:s})}
</button>
</div>
</hp-dialog>`}_boolInput(t,s){return p`<input type="checkbox" .checked=${t}
@change=${t=>s(t.target.checked)} />`}_rangeInput(t,s,e,o,a){return p`<input type="range" min=${t} max=${s} step=${e} .value=${String(o)}
@input=${t=>a(Number(t.target.value))} />`}_renderSpaceDialog(){return this._renderSpaceDialogBody(this.host._spaceDialog)}_renderSpaceDialogBody(t){const s=this.host._importTotal>0&&"create"===t.mode?this.host._t("import.progress",{i:this.host._importTotal-this.host._importQueue.length,n:this.host._importTotal}):"",l=()=>{this.host._spaceDialog=null,this.host._importQueue=[],this.host._importTotal=0};return p`<hp-dialog .hass=${this.host.hass}
.title=${`${"create"===t.mode?this.host._t("space.new"):this.host._t("space.header")}${s?` · ${s}`:""}`}
icon="mdi:floor-plan" wide @hp-close=${l}>
<div class="body">
<label>${this.host._t("space.title_label")}</label>
<input class="namein" type="text" placeholder=${this.host._t("space.title_ph")}
.value=${t.title}
@input=${s=>this.host._spaceDialog={...t,title:s.target.value}} />
<label>${this.host._t("space.plan_label")}</label>
<label class="srcrow">
<input type="radio" name="plansrc" .checked=${"file"===t.source}
@change=${()=>this.host._spaceDialog=f(t,"file")} />
<span>${this.host._t("space.source_file")}</span>
</label>
${"file"===t.source?p`<div class="planrow">
${t.planFile?p`<span class="planname">${t.planFile.name}</span>`:t.planUrl?p`<img class="planprev" src=${this.host._display(t.planUrl)}
alt=${this.host._t("space.plan_alt")} />`:p`<span class="planname muted">${this.host._t("space.no_plan")}</span>`}
<span class="fileupload">
<button class="btn filebtn" type="button" @click=${t=>t.currentTarget.nextElementSibling?.click()}>
<ha-icon icon="mdi:upload"></ha-icon>${t.planUrl||t.planFile?this.host._t("btn.replace"):this.host._t("btn.upload")}
</button>
<input type="file" hidden
accept=".svg,.png,.jpg,.jpeg,.webp,image/svg+xml,image/png,image/jpeg,image/webp"
@change=${t=>this._pickPlanFile(t)} />
</span>
<button class="btn ghost" @click=${this._toggleServerPlans}
title=${this.host._t("space.pick_saved_hint")}>
<ha-icon icon="mdi:folder-image"></ha-icon>${this.host._t("space.pick_saved")}
</button>
</div>
${t.pickSaved?this._renderServerPlans(t):_}`:_}
<label class="srcrow">
<input type="radio" name="plansrc" .checked=${"draw"===t.source}
@change=${()=>this.host._spaceDialog=f(t,"draw")} />
<span>${this.host._t("space.source_draw")}</span>
</label>
<label>${this.host._t("space.scale_label")}</label>
<div class="colorrow">
<input class="namein tempin" type="number"
min=${o(.1,this.host._imperial)}
max=${o(w,this.host._imperial)}
step="0.1"
.value=${t.cellCmInput??o(t.cellCm,this.host._imperial)}
@input=${s=>{const e=s.target.value,o=D(e),a=null==o?null:d(o,this.host._imperial);this.host._spaceDialog={...t,cellCmInput:e,cellCmTouched:!0,cellCm:null!=a&&a>0?Math.max(.1,Math.min(w,a)):t.cellCm}}} />
<span class="opl">${this.host._t(this.host._imperial?"space.scale_unit_imperial":"space.scale_unit")}</span>
</div>
<label class="dispsection">${this.host._t("space.display_section")}</label>
<label class="srcrow">
${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>
<select class="areasel" @change=${s=>{const e=s.target.value;this.host._spaceDialog={...t,zeroWallStyle:"solid"===e?"solid":"dashed"}}}>
<option value="dashed" ?selected=${"dashed"===t.zeroWallStyle}>
${this.host._t("space.zero_wall_dashed")}
</option>
<option value="solid" ?selected=${"solid"===t.zeroWallStyle}>
${this.host._t("space.zero_wall_solid")}
</option>
</select>
<div class="rhint">${this.host._t("space.zero_wall_help")}</div>
<label class="srcrow">
${this._boolInput(t.showNames,s=>{this.host._spaceDialog=y(t,"showNames",s)})}
<span>${this.host._t("space.show_names")}</span>
</label>
<label class="srcrow">
${this._boolInput(t.showLqi,s=>{this.host._spaceDialog={...t,showLqi:s}})}
<span>${this.host._t("space.show_lqi")}</span>
</label>
<label class="srcrow">
${this._boolInput(t.hideDecor,s=>{this.host._spaceDialog={...t,hideDecor:s}})}
<span>${this.host._t("space.hide_decor")}</span>
</label>
<div class="rhint">${this.host._t("space.hide_decor_tip")}</div>
<label class="srcrow">
${this._boolInput(t.hideOpenings,s=>{this.host._spaceDialog={...t,hideOpenings:s}})}
<span>${this.host._t("space.hide_openings")}</span>
</label>
<div class="rhint">${this.host._t("space.hide_openings_tip")}</div>
<label class="dispsection">${this.host._t("space.roomcard_section")}</label>
${[["labelTemp","space.label_temp"],["labelHum","space.label_hum"],["labelLqi","space.label_lqi"],["labelLight","space.label_light"]].map(([s,e])=>p`<label class="srcrow">
${this._boolInput(t[s],e=>{this.host._spaceDialog={...t,[s]:e}})}
<span>${this.host._t(e)}</span>
</label>`)}
<label>${this.host._t("space.card_font")}</label>
<div class="colorrow gsrow">
${this._rangeInput(50,300,5,Math.round(100*t.cardFontScale),s=>{this.host._spaceDialog={...t,cardFontScale:s/100}})}
<span class="opv">${Math.round(100*t.cardFontScale)}%</span>
</div>
${this.host._renderCardPreview(t.cardFontScale,1,1)}
<div class="colorrow">
<hp-color-opacity .label=${this.host._t("space.room_color")}
.opacityLabel=${this.host._t("space.opacity")}
.pickerLabels=${this.host._colorPickerLabels}
.color=${t.roomColor} .opacity=${t.roomOpacity} .showOpacity=${!0}
@hp-color-opacity-change=${s=>{this.host._spaceDialog={...t,roomColor:s.detail.color,roomOpacity:s.detail.opacity}}}></hp-color-opacity>
</div>
<label>${this.host._t("space.bg_mode")}</label>
<select class="areasel" @change=${s=>{const e=s.target.value;this.host._spaceDialog={...t,bgMode:"static"===e||"daynight"===e?e:null}}}>
<option value="" ?selected=${null===t.bgMode}>${this.host._t("space.sun_inherit")}</option>
<option value="static" ?selected=${"static"===t.bgMode}>${this.host._t("gs.bg_static")}</option>
<option value="daynight" ?selected=${"daynight"===t.bgMode}>${this.host._t("gs.bg_daynight")}</option>
</select>
${"static"===(t.bgMode??u(this.host._settings,{}))?p`
<div class="colorrow">
<hp-color-opacity .label=${this.host._t("space.bg_color")}
.pickerLabels=${this.host._colorPickerLabels}
.color=${t.bgColor||g(this.host._settings,{bgColor:null})||this.host._stageBgHex()}
.opacity=${1} .showOpacity=${!1}
@hp-color-opacity-change=${s=>{this.host._spaceDialog={...t,bgColor:s.detail.color}}}></hp-color-opacity>
${t.bgColor?p`<button class="btn ghost" @click=${()=>{this.host._spaceDialog={...t,bgColor:null}}}>${this.host._t("space.bg_inherit")}</button>`:p`<span class="opl">${this.host._t("space.bg_inherited")}</span>`}
</div>`:_}
<label>${this.host._t("space.north")}</label>
<div class="colorrow">
<input class="namein tempin" type="number" min="0" max="359" step="1"
placeholder=${this.host._t("space.sun_inherit")}
.value=${null===t.northDeg?"":String(t.northDeg)}
@input=${s=>{const e=s.target.value.trim(),o=""===e?null:Math.round(Number(e));this.host._spaceDialog={...t,northDeg:null!==o&&Number.isFinite(o)?Math.min(359,Math.max(0,o)):null}}} />
<span class="opl">${null===t.northDeg?this.host._t("space.north_inherited",{v:null===a(this.host._settings,{})?"—":`${a(this.host._settings,{})}°`}):"°"}</span>
</div>
<label>${this.host._t("space.sun_rays")}</label>
<select class="areasel" @change=${s=>{const e=s.target.value;this.host._spaceDialog={...t,sunRays:""===e?null:"1"===e}}}>
<option value="" ?selected=${null===t.sunRays}>${this.host._t("space.sun_inherit")}</option>
<option value="1" ?selected=${!0===t.sunRays}>${this.host._t("space.sun_on")}</option>
<option value="0" ?selected=${!1===t.sunRays}>${this.host._t("space.sun_off")}</option>
</select>
<label>${this.host._t("space.fill_label")}</label>
${m.map(t=>[t,`fill.${t}`]).map(([s,o])=>p`<label class="srcrow">
<input type="radio" name="fillmode" .checked=${t.fillMode===s}
@change=${()=>this.host._spaceDialog={...t,fillMode:s}} />
<span>${this.host._t(o)}</span>
${"temp"===s&&"temp"===t.fillMode?p`<span class="temprange">
<input class="namein tempin" type="number" step="0.5" .value=${String(t.tempMin)}
@input=${s=>{const e=D(s.target.value);null!=e&&(this.host._spaceDialog={...t,tempMin:e})}} />
–
<input class="namein tempin" type="number" step="0.5" .value=${String(t.tempMax)}
@input=${s=>{const e=D(s.target.value);null!=e&&(this.host._spaceDialog={...t,tempMax:e})}} /> °C
</span>`:_}
</label>
${"custom"===s&&"custom"===t.fillMode?p`
<div class="colorrow gsrow">
<span class="gsl">${this.host._t("space.custom_fill")}</span>
<hp-color-opacity .label=${this.host._t("space.custom_fill")}
.opacityLabel=${this.host._t("space.opacity")}
.pickerLabels=${this.host._colorPickerLabels}
.color=${(t.customFill||e).c}
.opacity=${(t.customFill||e).a}
@hp-color-opacity-change=${s=>{this.host._spaceDialog={...t,customFill:{c:s.detail.color,a:s.detail.opacity}}}}></hp-color-opacity>
${t.customFill?p`<button class="btn ghost" type="button"
@click=${()=>this.host._spaceDialog={...t,customFill:null}}>
${this.host._t("btn.reset")}</button>`:_}
</div>`:_}`)}
<label class="srcrow">
${this._boolInput(t.glowEnabled,s=>{this.host._spaceDialog={...t,glowEnabled:s}})}
<span>${this.host._t("space.glow_enabled")}</span>
</label>
${t.deleteBlockers?p`<div class="backuperror" role="alert">
${this.host._t("space.delete_blocked",{n:String(t.deleteBlockers)})}
</div>`:_}
</div>
<div class="row dialog-action-footer" slot="footer">
${"edit"===t.mode?p`<div class="dialog-action-group dialog-action-danger">
<button class="btn danger" @click=${()=>this._deleteSpace()} ?disabled=${t.busy}>
<ha-icon icon="mdi:delete-outline"></ha-icon>${this.host._t("btn.delete")}
</button>
</div>`:_}
<div class="dialog-action-group dialog-action-commit">
${this.host._importTotal>0&&"create"===t.mode?p`<button class="btn ghost" @click=${()=>this._skipImport()}>
${this.host._t("btn.skip")}</button>`:_}
<button class="btn ghost" @click=${l}>${this.host._t("btn.cancel")}</button>
<button class="btn on" @click=${()=>this._saveSpaceDialog()}
?disabled=${!t.title.trim()||"file"===t.source&&!(t.planFile||t.planUrl)||t.busy}
title=${"file"!==t.source||t.planFile||t.planUrl?"":this.host._t("title.need_plan")}>
<ha-icon icon="mdi:check"></ha-icon>${t.busy?"…":this.host._t("btn.save")}
</button>
</div>
</div>
</hp-dialog>`}}export{C as HouseplanOnboardingRuntime,S as ONBOARDING_RUNTIME_FINGERPRINT};