fix: attach a warm summary runtime before the first render (#506)

Every card instance attached its summary-panel runtime through
import().then(), even when the chunk had loaded long ago. The first render
therefore measured a header without summary controls; the controls arrived
a beat later, the stage shrank, the deferred refit opened a `stage-resize`
continuity candidate and the first HA tick paid three extra render passes
(Full Performance: blend stateUpdate1 50 → 130 ms, overlay 217 → 809 ms;
locally 4 performUpdate per tick instead of 1).

summary-runtime-loader.ts separates the summary code from its state: the
loaded factory is cached per page, every host builds its own runtime from
it (no shared preferences, drafts, subscriptions, timers or DOM). A warm
factory yields the runtime synchronously in connectedCallback, before the
first Lit render; a cold mount still pays one lazy import, concurrent cold
mounts share the pending import, a failed import is forgotten so the next
connection retries, and a disconnect cancels the pending attachment of that
connection. SummaryRuntimeSlot owns the per-host lifecycle so the card core
stays under its line ceiling.

Witnesses: loader unit tests (distinct instances, shared pending import,
cancelled attachment, retry after failure); demo/smoke_summary_warm_attach
(warm replacement and cold-key instance on a warm page own the runtime
before the first render, header/stage stable from the first frame, no
stage-resize, one performUpdate per geometry-neutral tick, a real viewport
resize still opens stage-resize); mutant summary-runtime-attaches-after-
first-render. smoke_summary_panel waited for `_summary` as a readiness
proxy; it now waits for the server config load, which stays asynchronous.

Local paired glow benchmarks (4× CPU throttle): blend 159.7 → 49.9 ms and
overlay 326.7 → 120.4 ms at stateUpdate1 with renders 4 → 1; the isometric
load loses the three summary-owned long tasks.

Docs screenshots: all 11 frames decode pixel-identical to the committed
ones; the manifest carries only the new source fingerprint. Initial View
ceiling recentred 299 100 → 299 600 for the +299 B loader.

Issue: #506
User-Visible: yes
This commit is contained in:
Codex
2026-09-09 13:57:53 +03:00
parent 2e502e09a2
commit 9d6ac98d3a
56 changed files with 906 additions and 447 deletions
@@ -1,309 +1,309 @@
{
"schema": 1,
"fingerprint": "807c2621b5e1c592548244ff5adb94281d1799a1481ada113e6938b375396c5c",
"fingerprint": "95d32c120d1ca37feb5a930e4a20e5ec2c460f82a52d88a41fe0d143f697ae74",
"entry": "houseplan-card.js",
"panelEntry": "houseplan-panel.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-mOz0zLlc.js",
"houseplan-assets/houseplan-card-Bj6mVEeM.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 298592,
"initialViewGzipBytes": 298891,
"initialPanelFiles": [
"houseplan-assets/houseplan-card-mOz0zLlc.js",
"houseplan-assets/houseplan-card-Bj6mVEeM.js",
"houseplan-card.js",
"houseplan-panel.js"
],
"initialPanelGzipBytes": 300898,
"initialPanelGzipBytes": 301193,
"initialPanelOnlyFiles": [
"houseplan-panel.js"
],
"initialPanelOnlyGzipBytes": 2306,
"initialPanelOnlyGzipBytes": 2302,
"lazyFiles": [
"houseplan-assets/backdrop-pick-CE5VSDDA.js",
"houseplan-assets/de-BeD9SIYF.js",
"houseplan-assets/editor-C7LQ7Tds.js",
"houseplan-assets/fr-CNfLzzDS.js",
"houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js",
"houseplan-assets/guard-Cxp8QKxn.js",
"houseplan-assets/houseplan-editor-runtime-BIh9fgX7.js",
"houseplan-assets/houseplan-onboarding-runtime-B6gvpoQT.js",
"houseplan-assets/hp-zigbee-topology-overlay-DgPgbQG8.js",
"houseplan-assets/iso-scene-render-9PxxIEfq.js",
"houseplan-assets/live-interaction-runtime-OETM7GZm.js",
"houseplan-assets/pdf-export-B6G-AgbE.js",
"houseplan-assets/summary-panel-runtime-loaded-0R-S6cIO.js",
"houseplan-assets/zigbee-topology-r_7fRPfa.js",
"houseplan-assets/zigbee-topology-runtime-DzxOfpWb.js"
"houseplan-assets/backdrop-pick-CXOE1u8Z.js",
"houseplan-assets/de-IBWzSVOj.js",
"houseplan-assets/editor-DKd1NGbT.js",
"houseplan-assets/fr-T10CV6-a.js",
"houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js",
"houseplan-assets/guard-7S6iRB0N.js",
"houseplan-assets/houseplan-editor-runtime-DJMGYUqW.js",
"houseplan-assets/houseplan-onboarding-runtime-Cdd3N5hN.js",
"houseplan-assets/hp-zigbee-topology-overlay-HyXCDpzQ.js",
"houseplan-assets/iso-scene-render-miMf0jvt.js",
"houseplan-assets/live-interaction-runtime-Dahsj-4y.js",
"houseplan-assets/pdf-export-DpHlViMV.js",
"houseplan-assets/summary-panel-runtime-loaded-2X-IAkWu.js",
"houseplan-assets/zigbee-topology-DOkBU6gZ.js",
"houseplan-assets/zigbee-topology-runtime-CiDfY5zA.js"
],
"lazyGzipBytes": 417382,
"lazyGzipBytes": 417329,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-CE5VSDDA.js",
"houseplan-assets/editor-C7LQ7Tds.js",
"houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js",
"houseplan-assets/guard-Cxp8QKxn.js",
"houseplan-assets/houseplan-editor-runtime-BIh9fgX7.js",
"houseplan-assets/zigbee-topology-r_7fRPfa.js"
"houseplan-assets/backdrop-pick-CXOE1u8Z.js",
"houseplan-assets/editor-DKd1NGbT.js",
"houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js",
"houseplan-assets/guard-7S6iRB0N.js",
"houseplan-assets/houseplan-editor-runtime-DJMGYUqW.js",
"houseplan-assets/zigbee-topology-DOkBU6gZ.js"
],
"lazyEditorGzipBytes": 205653,
"lazyEditorGzipBytes": 205633,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-CE5VSDDA.js",
"houseplan-assets/houseplan-onboarding-runtime-B6gvpoQT.js"
"houseplan-assets/backdrop-pick-CXOE1u8Z.js",
"houseplan-assets/houseplan-onboarding-runtime-Cdd3N5hN.js"
],
"lazyOnboardingGzipBytes": 14155,
"lazyOnboardingGzipBytes": 14148,
"lazyLocaleFiles": [
"houseplan-assets/de-BeD9SIYF.js",
"houseplan-assets/fr-CNfLzzDS.js"
"houseplan-assets/de-IBWzSVOj.js",
"houseplan-assets/fr-T10CV6-a.js"
],
"lazyLocaleGzipBytes": 54861,
"lazyLocaleGzipBytes": 54843,
"lazyIsometricFiles": [
"houseplan-assets/guard-Cxp8QKxn.js",
"houseplan-assets/iso-scene-render-9PxxIEfq.js"
"houseplan-assets/guard-7S6iRB0N.js",
"houseplan-assets/iso-scene-render-miMf0jvt.js"
],
"lazyIsometricGzipBytes": 13462,
"lazyIsometricGzipBytes": 13452,
"lazyFurnitureArtFiles": [
"houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js"
"houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js"
],
"lazyFurnitureArtGzipBytes": 10250,
"lazyFurnitureArtGzipBytes": 10253,
"lazyPdfFiles": [
"houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js",
"houseplan-assets/pdf-export-B6G-AgbE.js"
"houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js",
"houseplan-assets/pdf-export-DpHlViMV.js"
],
"lazyPdfGzipBytes": 120364,
"lazyPdfGzipBytes": 120367,
"files": [
{
"path": "houseplan-assets/backdrop-pick-CE5VSDDA.js",
"sha256": "650f25935f5a935e0f1c301b3bd03bb094331d1916c1094ea63c6f53866b8c45",
"path": "houseplan-assets/backdrop-pick-CXOE1u8Z.js",
"sha256": "a4abe8807d8c404a7ce9f65b0a72ca3d8f8d4d5bf38fa8220915818f9238c0db",
"rawBytes": 21134,
"gzipBytes": 7149,
"gzipBytes": 7144,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-BeD9SIYF.js",
"sha256": "7d09210471855559b0fea7e7648794d75d120a32628c5d77811e9e7c7e6d9e08",
"path": "houseplan-assets/de-IBWzSVOj.js",
"sha256": "cf031fb380b4b056c116e14a8cb26729ae4d0b275991b07989917f884f8841ff",
"rawBytes": 91441,
"gzipBytes": 27713,
"gzipBytes": 27705,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-C7LQ7Tds.js",
"sha256": "8b59adf27c503ef36a00c2eb0a04395bfd6f5c2eac107e6ba12bc2fdc0c01d56",
"path": "houseplan-assets/editor-DKd1NGbT.js",
"sha256": "e62e388db3b39eaa5f0e8f9b9676b7a6e30c453f84d22d530740f9be093512d1",
"rawBytes": 3837,
"gzipBytes": 1586,
"gzipBytes": 1585,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/fr-CNfLzzDS.js",
"sha256": "b1e8ed99400065e906fcc31a1d1f44940f981fb50c2d09a2ff74cdac10dc689e",
"path": "houseplan-assets/fr-T10CV6-a.js",
"sha256": "b602e1137a8395f963be744ed43d2054e472093502f687e40e6bd3e7a25a8794",
"rawBytes": 93719,
"gzipBytes": 27148,
"gzipBytes": 27138,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js",
"sha256": "0c81d49c5e4c9d0b138090a99ca124361f75309ed582f05d07c69fc1b6b92d7f",
"path": "houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js",
"sha256": "1e26cad8d04b459f4b1140e705a02a1d380b8c49a84f92b8ea2ce3e684544800",
"rawBytes": 33655,
"gzipBytes": 10250,
"gzipBytes": 10253,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/guard-Cxp8QKxn.js",
"sha256": "2a75558ea50a3c91ab270154441b18f7d392989faae049db00c9af57b206e2c4",
"path": "houseplan-assets/guard-7S6iRB0N.js",
"sha256": "67f95421a21516a66157d8fdc0129c2d1280cee31a3621a81039c88835d1c0f8",
"rawBytes": 518,
"gzipBytes": 371,
"gzipBytes": 368,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-mOz0zLlc.js",
"sha256": "9202eb087cecb9012efd1360898e3687b53b6729f65605978a8b52d1ed177721",
"rawBytes": 1075388,
"gzipBytes": 297788,
"path": "houseplan-assets/houseplan-card-Bj6mVEeM.js",
"sha256": "dea85ab6dfeebcb263bd672270041e34473d40b9db24e69259e55d42e5ff44d4",
"rawBytes": 1076387,
"gzipBytes": 298088,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-BeD9SIYF.js",
"houseplan-assets/editor-C7LQ7Tds.js",
"houseplan-assets/fr-CNfLzzDS.js",
"houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js",
"houseplan-assets/houseplan-editor-runtime-BIh9fgX7.js",
"houseplan-assets/houseplan-onboarding-runtime-B6gvpoQT.js",
"houseplan-assets/hp-zigbee-topology-overlay-DgPgbQG8.js",
"houseplan-assets/iso-scene-render-9PxxIEfq.js",
"houseplan-assets/live-interaction-runtime-OETM7GZm.js",
"houseplan-assets/pdf-export-B6G-AgbE.js",
"houseplan-assets/summary-panel-runtime-loaded-0R-S6cIO.js"
"houseplan-assets/de-IBWzSVOj.js",
"houseplan-assets/editor-DKd1NGbT.js",
"houseplan-assets/fr-T10CV6-a.js",
"houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js",
"houseplan-assets/houseplan-editor-runtime-DJMGYUqW.js",
"houseplan-assets/houseplan-onboarding-runtime-Cdd3N5hN.js",
"houseplan-assets/hp-zigbee-topology-overlay-HyXCDpzQ.js",
"houseplan-assets/iso-scene-render-miMf0jvt.js",
"houseplan-assets/live-interaction-runtime-Dahsj-4y.js",
"houseplan-assets/pdf-export-DpHlViMV.js",
"houseplan-assets/summary-panel-runtime-loaded-2X-IAkWu.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-BIh9fgX7.js",
"sha256": "bd9b6667a2f78549c03e5293959a5ce7dd5ab215eee62420f58f3226d06e6109",
"path": "houseplan-assets/houseplan-editor-runtime-DJMGYUqW.js",
"sha256": "bc5163222924f68e6279b412b16a53f28a76b5b4d80d4a03e7fe28c4cce77a39",
"rawBytes": 671972,
"gzipBytes": 178865,
"gzipBytes": 178859,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-CE5VSDDA.js",
"houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js",
"houseplan-assets/guard-Cxp8QKxn.js",
"houseplan-assets/houseplan-card-mOz0zLlc.js",
"houseplan-assets/zigbee-topology-r_7fRPfa.js"
"houseplan-assets/backdrop-pick-CXOE1u8Z.js",
"houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js",
"houseplan-assets/guard-7S6iRB0N.js",
"houseplan-assets/houseplan-card-Bj6mVEeM.js",
"houseplan-assets/zigbee-topology-DOkBU6gZ.js"
],
"dynamicImports": [
"houseplan-assets/zigbee-topology-runtime-DzxOfpWb.js"
"houseplan-assets/zigbee-topology-runtime-CiDfY5zA.js"
]
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-B6gvpoQT.js",
"sha256": "8d2b5ba0b169d463192e7b8531c396f89512117701c7deaa6b2e0c83491504a7",
"path": "houseplan-assets/houseplan-onboarding-runtime-Cdd3N5hN.js",
"sha256": "31ea22267928606938b34c47f1ac8ecc2041048bc8c045a411d7108d353de849",
"rawBytes": 28237,
"gzipBytes": 7006,
"gzipBytes": 7004,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-CE5VSDDA.js",
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/backdrop-pick-CXOE1u8Z.js",
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/hp-zigbee-topology-overlay-DgPgbQG8.js",
"sha256": "ac3c9b34db5772235b1879861640c4be53b912a9e468a274276cf4d0bb8beb02",
"path": "houseplan-assets/hp-zigbee-topology-overlay-HyXCDpzQ.js",
"sha256": "0e18deb183cb473c2a96fb689c91c68a3729b8ad56fb8907b01c30810585dbec",
"rawBytes": 11034,
"gzipBytes": 3730,
"gzipBytes": 3731,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js",
"houseplan-assets/zigbee-topology-r_7fRPfa.js",
"houseplan-assets/zigbee-topology-runtime-DzxOfpWb.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js",
"houseplan-assets/zigbee-topology-DOkBU6gZ.js",
"houseplan-assets/zigbee-topology-runtime-CiDfY5zA.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/iso-scene-render-9PxxIEfq.js",
"sha256": "673ec1ceddb36f8f7db22c1038222726f4227ed84e9f355783c7fe2d037ddfdb",
"path": "houseplan-assets/iso-scene-render-miMf0jvt.js",
"sha256": "00fd101b4e5469f6d289b77eab3056bedf98082520af9139a1afff5823fcb025",
"rawBytes": 38562,
"gzipBytes": 13091,
"gzipBytes": 13084,
"isEntry": false,
"imports": [
"houseplan-assets/guard-Cxp8QKxn.js",
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/guard-7S6iRB0N.js",
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/live-interaction-runtime-OETM7GZm.js",
"sha256": "5934f78ad823a530f1bb2c3cd08e053ebfdd0594f23fddadb9e57067388a3c12",
"path": "houseplan-assets/live-interaction-runtime-Dahsj-4y.js",
"sha256": "631b277aae71d9977fabb3f330c4a9cdf38e47ec51259584540b747d9c9444df",
"rawBytes": 5196,
"gzipBytes": 2129,
"gzipBytes": 2124,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/pdf-export-B6G-AgbE.js",
"sha256": "d02cb2b142ed3d4e40f64993eff91e7a81ac5ed1eac8ce9c3edb9c4298e5a98d",
"path": "houseplan-assets/pdf-export-DpHlViMV.js",
"sha256": "48b07b9ecfbbf2c1a8de53edce8bc32b1217e2c2f20eb5ceb24af75999870d8a",
"rawBytes": 237254,
"gzipBytes": 110114,
"isEntry": false,
"imports": [
"houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js",
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js",
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-editor-Dme32gxk.js",
"sha256": "d434810c15653a8ca694ce41c03cd47f60b9e3ce9f3213f11a333386695658d2",
"path": "houseplan-assets/summary-panel-editor-DOpNDtK_.js",
"sha256": "4ed28cc81b7f0ccac46e0fb6ac37856ddbdf7813040585447089bf4a0a61805f",
"rawBytes": 14811,
"gzipBytes": 3390,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js",
"houseplan-assets/summary-panel-runtime-loaded-0R-S6cIO.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js",
"houseplan-assets/summary-panel-runtime-loaded-2X-IAkWu.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-metrics-JJMWCHbb.js",
"sha256": "39d084074a8865a58dc2875ea6a9baaa77b271cca9c9b27029421ca6f2112fcf",
"path": "houseplan-assets/summary-panel-metrics-DXF83oAy.js",
"sha256": "51953d004b76561a842f67b9455985cceac2663c52499d1e1acc3a3cdfdaded9",
"rawBytes": 2195,
"gzipBytes": 1124,
"gzipBytes": 1121,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-runtime-loaded-0R-S6cIO.js",
"sha256": "c609232a40013bce973b3b61c0b9ce3f248bb097481d437ab16ae971d5f201c4",
"path": "houseplan-assets/summary-panel-runtime-loaded-2X-IAkWu.js",
"sha256": "7598bcc5d26d8d2f9a15cc154c32a0b836ba0ac53b7b5f9b65f3c2c2bc0287d1",
"rawBytes": 69530,
"gzipBytes": 19035,
"gzipBytes": 19034,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": [
"houseplan-assets/summary-panel-editor-Dme32gxk.js",
"houseplan-assets/summary-panel-metrics-JJMWCHbb.js"
"houseplan-assets/summary-panel-editor-DOpNDtK_.js",
"houseplan-assets/summary-panel-metrics-DXF83oAy.js"
]
},
{
"path": "houseplan-assets/zigbee-topology-r_7fRPfa.js",
"sha256": "8267766cadbaf205251335d92caa66bd52ffa045e30e6e5cf1c756416a4e37d7",
"path": "houseplan-assets/zigbee-topology-DOkBU6gZ.js",
"sha256": "0176688590ae76a8766ee735de892e37f29211a042c27f5a2992eda0cc8ef8d9",
"rawBytes": 18961,
"gzipBytes": 7432,
"gzipBytes": 7424,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/zigbee-topology-runtime-DzxOfpWb.js",
"sha256": "db59921337f4f57b8514031b320f97a435a5de52bfb4ec1e83855e10701102a9",
"path": "houseplan-assets/zigbee-topology-runtime-CiDfY5zA.js",
"sha256": "6b43a567d3686249519d43ef6f04bb9596529d99d04f745944c0e561c0e3034e",
"rawBytes": 3941,
"gzipBytes": 1763,
"gzipBytes": 1762,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js",
"houseplan-assets/zigbee-topology-r_7fRPfa.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js",
"houseplan-assets/zigbee-topology-DOkBU6gZ.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "af03812bb65afed2afaeaca3bac52eedbcf2d6683218b28e5f53cf78224721ba",
"sha256": "b8095427f52e7413e84d3202953d15765fbd43d4536226beff01ba7ff692ae2e",
"rawBytes": 1205,
"gzipBytes": 804,
"gzipBytes": 803,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-panel.js",
"sha256": "b7fcfa42f2b03ab3a356197655d34118879732907391b0e40c6b3b85c0e5ca7b",
"sha256": "f0a1709a58213029c08e73da2ed81b217827c8f5cdbf96e082385cfb608eabcc",
"rawBytes": 6124,
"gzipBytes": 2306,
"gzipBytes": 2302,
"isEntry": true,
"imports": [
"houseplan-card.js"
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="807c2621b5e1c592548244ff5adb94281d1799a1481ada113e6938b375396c5c";import{t as e,l as o,x as t,e5 as s,A as a,e6 as i,e7 as l,e8 as n,dV as r,b as c}from"./houseplan-card-mOz0zLlc.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95d32c120d1ca37feb5a930e4a20e5ec2c460f82a52d88a41fe0d143f697ae74";import{t as e,l as o,x as t,e4 as s,A as a,e5 as i,e6 as l,e7 as n,dV as r,b as c}from"./houseplan-card-Bj6mVEeM.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
.hass=${this.hass}
.data=${this._formData}
.schema=${e}
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95d32c120d1ca37feb5a930e4a20e5ec2c460f82a52d88a41fe0d143f697ae74";import{dT as r,dU as e,dV as t}from"./houseplan-card-Bj6mVEeM.js";const s={},a=r(class extends e{constructor(){super(...arguments),this.ot=s}render(r,e){return e()}update(r,[e,s]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((r,e)=>r===this.ot[e]))return t}else if(this.ot===e)return t;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,s)}});export{a as i};
@@ -1 +0,0 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="807c2621b5e1c592548244ff5adb94281d1799a1481ada113e6938b375396c5c";import{dT as r,dU as t,dV as s}from"./houseplan-card-mOz0zLlc.js";const e={},a=r(class extends t{constructor(){super(...arguments),this.ot=e}render(r,t){return t()}update(r,[t,e]){if(Array.isArray(t)){if(Array.isArray(this.ot)&&this.ot.length===t.length&&t.every((r,t)=>r===this.ot[t]))return s}else if(this.ot===t)return s;return this.ot=Array.isArray(t)?Array.from(t):t,this.render(t,e)}});export{a as i};
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="807c2621b5e1c592548244ff5adb94281d1799a1481ada113e6938b375396c5c";import{b as a,A as e}from"./houseplan-card-mOz0zLlc.js";import{s,n as t,a as l,m as r,b as o,c as i,d as u}from"./summary-panel-runtime-loaded-0R-S6cIO.js";const m=m=>{const{host:c,dialog:d,problems:n,t:b}=m,y=n.find(a=>"error"===a.kind),$=d.localShow!==d.baseLocalShow||!d.localOnly&&!s(t(d.draft),d.base),p=$&&!d.busy&&!y,v=a=>n.find(e=>e.path===a),h=a=>a?b(`summary.problem.${a.code}`):"",k=()=>m.close(),g=a`<label class="summary-switch" for="summary-local-show">
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95d32c120d1ca37feb5a930e4a20e5ec2c460f82a52d88a41fe0d143f697ae74";import{b as a,A as e}from"./houseplan-card-Bj6mVEeM.js";import{s,n as t,a as l,m as r,b as o,c as i,d as u}from"./summary-panel-runtime-loaded-2X-IAkWu.js";const m=m=>{const{host:c,dialog:d,problems:n,t:b}=m,y=n.find(a=>"error"===a.kind),$=d.localShow!==d.baseLocalShow||!d.localOnly&&!s(t(d.draft),d.base),p=$&&!d.busy&&!y,v=a=>n.find(e=>e.path===a),h=a=>a?b(`summary.problem.${a.code}`):"",k=()=>m.close(),g=a`<label class="summary-switch" for="summary-local-show">
<span class="summary-switch-caption"><strong>${b("summary.show_local")}</strong>
<small id="summary-local-show-hint">${b("summary.show_local_hint")}</small></span>
<input id="summary-local-show" data-summary-local-show type="checkbox"
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="807c2621b5e1c592548244ff5adb94281d1799a1481ada113e6938b375396c5c";import{az as e,ea as t,eb as n,bs as i,ec as r,P as a,a5 as s,M as o,aq as c,N as l,bq as d,bp as u,ed as f}from"./houseplan-card-mOz0zLlc.js";function g(t){if(!t.registry.authoritative)return null;const n=new Set,i=e(t.markers);for(const e of Object.values(t.registry.devices||{}))e?.id&&"service"!==e.entry_type&&!i.devices.has(e.id)&&e.area_id&&t.areaToSpace[e.area_id]&&n.add(e.id);for(const e of t.markers||[]){if(e.removed||"virtual"===e.binding)continue;const r=String(e.binding||"").indexOf(":");if(r<1)continue;const a=e.binding.slice(0,r),s=e.binding.slice(r+1),o="device"===a?t.registry.devices?.[s]:null,c="entity"===a?t.registry.entities?.[s]:null,l="device"===a?s:c?.device_id,d="device"===a?o?.area_id:c?.area_id||l&&t.registry.devices?.[l]?.area_id,u="string"==typeof e.room_id&&e.room_id.length>0&&null===e.area?"":e.area||d||"",f=u&&t.areaToSpace[u]||e.space||t.firstSpaceId;l&&t.registry.devices?.[l]&&t.spaceIds.has(f)&&(!i.devices.has(l)||"entity"===a&&i.liveEntities.has(s))&&n.add(l)}return n}function m(e,t){return e?.length?t?.length?f(e,t):e:t}function v(e,t){try{let n=0;for(const i of t){const t=e.spaces.find(e=>String(e?.id)===i.id);if(!t)continue;const f=r(t,i);let g=null;for(const e of i.rooms){if(!e.id)continue;const t=a(e);if(!t)continue;const n=s(i.rooms,e.id,f.walls,f.openCuts,o,f.cellCm,c,l)||t;g=m(g,d(n,f.physicalBodies))}const v=f.cellCm/c;n+=u(g)*v*v/1e4}return n}catch{return null}}function y(e,t,n,r){if("system"!==e.type)return null;if("device_count"===e.key)return null===t.deviceCount?null:String(t.deviceCount);if("total_area"===e.key)return null===t.areaM2?null:i(t.areaM2,"mi"===n?.config?.unit_system?.length);try{return new Intl.DateTimeFormat(r||void 0,{dateStyle:"short",timeStyle:"short",timeZone:n?.config?.time_zone||void 0}).format(t.now)}catch{return t.now.toLocaleString()}}function _(e,i){const r=e?.states?.[i];if(!r)return null;const a=t(e,i);return a?n(a,String(r.attributes?.unit_of_measurement||"")):null}export{g as representedHaDeviceIds,_ as summaryEntityValue,y as summarySystemValue,v as totalCleanFloorAreaM2};
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95d32c120d1ca37feb5a930e4a20e5ec2c460f82a52d88a41fe0d143f697ae74";import{az as e,ea as t,eb as n,bs as i,ec as r,P as a,a5 as s,M as o,aq as c,N as l,bq as d,bp as u,ed as f}from"./houseplan-card-Bj6mVEeM.js";function g(t){if(!t.registry.authoritative)return null;const n=new Set,i=e(t.markers);for(const e of Object.values(t.registry.devices||{}))e?.id&&"service"!==e.entry_type&&!i.devices.has(e.id)&&e.area_id&&t.areaToSpace[e.area_id]&&n.add(e.id);for(const e of t.markers||[]){if(e.removed||"virtual"===e.binding)continue;const r=String(e.binding||"").indexOf(":");if(r<1)continue;const a=e.binding.slice(0,r),s=e.binding.slice(r+1),o="device"===a?t.registry.devices?.[s]:null,c="entity"===a?t.registry.entities?.[s]:null,l="device"===a?s:c?.device_id,d="device"===a?o?.area_id:c?.area_id||l&&t.registry.devices?.[l]?.area_id,u="string"==typeof e.room_id&&e.room_id.length>0&&null===e.area?"":e.area||d||"",f=u&&t.areaToSpace[u]||e.space||t.firstSpaceId;l&&t.registry.devices?.[l]&&t.spaceIds.has(f)&&(!i.devices.has(l)||"entity"===a&&i.liveEntities.has(s))&&n.add(l)}return n}function m(e,t){return e?.length?t?.length?f(e,t):e:t}function v(e,t){try{let n=0;for(const i of t){const t=e.spaces.find(e=>String(e?.id)===i.id);if(!t)continue;const f=r(t,i);let g=null;for(const e of i.rooms){if(!e.id)continue;const t=a(e);if(!t)continue;const n=s(i.rooms,e.id,f.walls,f.openCuts,o,f.cellCm,c,l)||t;g=m(g,d(n,f.physicalBodies))}const v=f.cellCm/c;n+=u(g)*v*v/1e4}return n}catch{return null}}function y(e,t,n,r){if("system"!==e.type)return null;if("device_count"===e.key)return null===t.deviceCount?null:String(t.deviceCount);if("total_area"===e.key)return null===t.areaM2?null:i(t.areaM2,"mi"===n?.config?.unit_system?.length);try{return new Intl.DateTimeFormat(r||void 0,{dateStyle:"short",timeStyle:"short",timeZone:n?.config?.time_zone||void 0}).format(t.now)}catch{return t.now.toLocaleString()}}function _(e,i){const r=e?.states?.[i];if(!r)return null;const a=t(e,i);return a?n(a,String(r.attributes?.unit_of_measurement||"")):null}export{g as representedHaDeviceIds,_ as summaryEntityValue,y as summarySystemValue,v as totalCleanFloorAreaM2};
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="807c2621b5e1c592548244ff5adb94281d1799a1481ada113e6938b375396c5c";import{n as t,b as n}from"./zigbee-topology-r_7fRPfa.js";import{F as e}from"./houseplan-card-mOz0zLlc.js";const o=new WeakMap;function i(t){const n=function(t){const n=t?.connection||t;return!n||"object"!=typeof n&&"function"!=typeof n?null:n}(t);if(!n)return null;let e=o.get(n);return e||(e={revision:0,topologies:[],states:{},listeners:new Set,inflight:new Map},o.set(n,e)),e}function r(t,n,e){t.states={...t.states,[n]:e},function(t){t.revision++;for(const n of t.listeners)n()}(t)}function s(t){const n=t?.code;if("permission"===n||"unsupported"===n||"timeout"===n||"invalid_topic"===n||"invalid_payload"===n)return n;const e=String(t?.message||"").toLowerCase();return e.includes("unauthor")||e.includes("permission")?"permission":e.includes("unknown_command")||e.includes("not found")?"unsupported":"provider"}function a(t){return Object.assign(new Error(t),{code:t})}function c(t,n,e){const o=t.inflight.get(n);if(o)return o;r(t,n,{phase:"loading"});const i=e().then(n=>{if(!n.nodes.length&&n.warnings.some(t=>"invalid_payload"===t.code))throw a("invalid_payload");!function(t,n){t.topologies=[...t.topologies.filter(t=>!(t.provider===n.provider&&t.instanceId===n.instanceId)),n],r(t,"zha"===n.provider?"zha":`z2m:${n.instanceId}`,{phase:"ready",obtainedAt:n.obtainedAt,partial:n.warnings.length>0})}(t,n)}).catch(e=>{r(t,n,{phase:"error",error:s(e)})}).finally(()=>t.inflight.delete(n));return t.inflight.set(n,i),i}function l(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function u(t,n){const e=i(t);return e?(e.listeners.add(n),()=>e.listeners.delete(n)):()=>{}}function p(t){if(!0!==t?.user?.is_admin)throw a("permission")}function f(n){const e=i(n);return e?c(e,"zha",async()=>{if(p(n),"function"!=typeof n?.callWS)throw a("unsupported");return t(await n.callWS({type:"zha/devices"}))}):Promise.resolve()}function d(t){return null!==t&&"object"==typeof t?t:null}function h(t){const n=d(t),e=n?.payload??t;if("string"!=typeof e)return e;try{return JSON.parse(e)}catch{return null}}async function g(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function m(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const l=e(o);return c(s,`z2m:${l||String(o)}`,async()=>{if(p(t),!l)throw a("invalid_topic");const e=t.connection,o=e?.subscribeMessage;if("function"!=typeof o||"function"!=typeof t?.callService)throw a("unsupported");const i=function(){const t=globalThis.crypto;return"function"==typeof t?.randomUUID?`houseplan-${t.randomUUID()}`:`houseplan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}(),s=Date.now()+Math.max(1,r);let c=!1,u=null,f=null,m=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,m=n}),b=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&h(t)&&u?.()},{type:"mqtt/subscribe",topic:`${l}/bridge/info`});"function"==typeof r&&b.push(r);const p=await o.call(e,t=>{if(!0===d(t)?.retain)return;const n=h(t);null!==n?n&&function(t){const n=d(t),e=n?.transaction??d(n?.data)?.transaction;return"string"==typeof e||"number"==typeof e?String(e):null}(n)===i&&f?.(n):c&&m?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof p&&b.push(p),await g(y,Math.min(4e3,Math.max(1,s-Date.now()))),c=!0,await t.callService("mqtt","publish",{topic:`${l}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:i}),qos:0,retain:!1});const v=await g(w,s-Date.now()),_=d(v)?.status;if(_&&"ok"!==_)throw a("provider");return n(v,l)}finally{for(const t of b)try{t()}catch{}}})}export{f as readZhaTopology,m as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot};
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95d32c120d1ca37feb5a930e4a20e5ec2c460f82a52d88a41fe0d143f697ae74";import{n as t,b as n}from"./zigbee-topology-DOkBU6gZ.js";import{F as e}from"./houseplan-card-Bj6mVEeM.js";const o=new WeakMap;function i(t){const n=function(t){const n=t?.connection||t;return!n||"object"!=typeof n&&"function"!=typeof n?null:n}(t);if(!n)return null;let e=o.get(n);return e||(e={revision:0,topologies:[],states:{},listeners:new Set,inflight:new Map},o.set(n,e)),e}function r(t,n,e){t.states={...t.states,[n]:e},function(t){t.revision++;for(const n of t.listeners)n()}(t)}function s(t){const n=t?.code;if("permission"===n||"unsupported"===n||"timeout"===n||"invalid_topic"===n||"invalid_payload"===n)return n;const e=String(t?.message||"").toLowerCase();return e.includes("unauthor")||e.includes("permission")?"permission":e.includes("unknown_command")||e.includes("not found")?"unsupported":"provider"}function a(t){return Object.assign(new Error(t),{code:t})}function c(t,n,e){const o=t.inflight.get(n);if(o)return o;r(t,n,{phase:"loading"});const i=e().then(n=>{if(!n.nodes.length&&n.warnings.some(t=>"invalid_payload"===t.code))throw a("invalid_payload");!function(t,n){t.topologies=[...t.topologies.filter(t=>!(t.provider===n.provider&&t.instanceId===n.instanceId)),n],r(t,"zha"===n.provider?"zha":`z2m:${n.instanceId}`,{phase:"ready",obtainedAt:n.obtainedAt,partial:n.warnings.length>0})}(t,n)}).catch(e=>{r(t,n,{phase:"error",error:s(e)})}).finally(()=>t.inflight.delete(n));return t.inflight.set(n,i),i}function l(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function u(t,n){const e=i(t);return e?(e.listeners.add(n),()=>e.listeners.delete(n)):()=>{}}function p(t){if(!0!==t?.user?.is_admin)throw a("permission")}function f(n){const e=i(n);return e?c(e,"zha",async()=>{if(p(n),"function"!=typeof n?.callWS)throw a("unsupported");return t(await n.callWS({type:"zha/devices"}))}):Promise.resolve()}function d(t){return null!==t&&"object"==typeof t?t:null}function h(t){const n=d(t),e=n?.payload??t;if("string"!=typeof e)return e;try{return JSON.parse(e)}catch{return null}}async function g(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function m(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const l=e(o);return c(s,`z2m:${l||String(o)}`,async()=>{if(p(t),!l)throw a("invalid_topic");const e=t.connection,o=e?.subscribeMessage;if("function"!=typeof o||"function"!=typeof t?.callService)throw a("unsupported");const i=function(){const t=globalThis.crypto;return"function"==typeof t?.randomUUID?`houseplan-${t.randomUUID()}`:`houseplan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}(),s=Date.now()+Math.max(1,r);let c=!1,u=null,f=null,m=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,m=n}),b=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&h(t)&&u?.()},{type:"mqtt/subscribe",topic:`${l}/bridge/info`});"function"==typeof r&&b.push(r);const p=await o.call(e,t=>{if(!0===d(t)?.retain)return;const n=h(t);null!==n?n&&function(t){const n=d(t),e=n?.transaction??d(n?.data)?.transaction;return"string"==typeof e||"number"==typeof e?String(e):null}(n)===i&&f?.(n):c&&m?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof p&&b.push(p),await g(y,Math.min(4e3,Math.max(1,s-Date.now()))),c=!0,await t.callService("mqtt","publish",{topic:`${l}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:i}),qos:0,retain:!1});const v=await g(w,s-Date.now()),_=d(v)?.status;if(_&&"ok"!==_)throw a("provider");return n(v,l)}finally{for(const t of b)try{t()}catch{}}})}export{f as readZhaTopology,m as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot};
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="807c2621b5e1c592548244ff5adb94281d1799a1481ada113e6938b375396c5c";try{await import("./houseplan-assets/houseplan-card-mOz0zLlc.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale houseplan-card.js: the implementation chunk is unavailable",e)}
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95d32c120d1ca37feb5a930e4a20e5ec2c460f82a52d88a41fe0d143f697ae74";try{await import("./houseplan-assets/houseplan-card-Bj6mVEeM.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale houseplan-card.js: the implementation chunk is unavailable",e)}
File diff suppressed because one or more lines are too long
+3 -1
View File
@@ -137,7 +137,9 @@ const pickerWitnessSetup = await page.evaluate(async () => {
extra.hass = card.hass;
extra.setConfig({ ...(card._config || {}), type: 'custom:houseplan-card' });
document.body.append(extra);
for (let attempt = 0; attempt < 100 && !extra._summary; attempt++) {
// #506: the warm summary runtime now exists synchronously; the readiness
// this witness needs is the server config load, which stays asynchronous.
for (let attempt = 0; attempt < 400 && !(extra._summary && extra._loadOk); attempt++) {
await new Promise((resolve) => setTimeout(resolve, 5));
}
extra._serverCfg = structuredClone(card._serverCfg);
+153
View File
@@ -0,0 +1,153 @@
// #506: a card instance created on a page whose summary chunk is already
// loaded must own its summary runtime BEFORE the first render. Otherwise the
// first header measurement sees no summary controls, the controls arrive a
// beat later, the stage shrinks and the deferred refit opens a `stage-resize`
// continuity candidate with three render passes on top of the first HA tick
// (Full Performance: blend stateUpdate1 50 → 130 ms, overlay 217 → 809 ms).
//
// Three instances on one 780×669 page with a long title (the soft-layout
// fixture of test/boot-soft-layout.test.mjs):
// c1 — cold page (launchPanelCold): the chunk is imported once, lazily;
// c2 — warm replacement of c1 (same config key, warm memo): AC4;
// c3 — a cold-key instance (new title) on the warm page: AC4 second half;
// then AC5: one geometry-neutral HA tick on c3 costs one performUpdate and no
// stage-resize; then AC6: a real container resize DOES open stage-resize.
// FAILS on the build before #506 (runtime attached via import().then()).
import { launchPanelCold, check, finish } from './serve.mjs';
const LONG_TITLE = 'A deliberately long House Plan title that wraps the header onto a second line at 780 px';
// A page without any card yet: the summary chunk is provably cold for c1.
const { page, browser } = await launchPanelCold({ width: 780, height: 669 });
await page.evaluate(() => import('/assets/houseplan-card.js'));
await page.waitForFunction(() => !!customElements.get('houseplan-card'));
const res = await page.evaluate(async ({ longTitle }) => {
const out = {};
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const Card = customElements.get('houseplan-card');
Card._warmBootReset?.();
localStorage.removeItem('houseplan_card_nav_v1');
const wrap = document.createElement('div');
wrap.style.cssText = 'position:fixed;left:0;top:0;width:780px;height:669px;z-index:99;background:#000';
document.body.appendChild(wrap);
const sr = (c) => c.shadowRoot || c.renderRoot;
const candidates = (c) => {
const seen = [];
const original = c._continuity.beginCandidate.bind(c._continuity);
c._continuity.beginCandidate = (reason, recovery) => { seen.push(reason); return original(reason, recovery); };
return seen;
};
const mk = (title) => {
const c = document.createElement('houseplan-card');
c.setConfig({ type: 'custom:houseplan-card', title });
c.hass = window.__mkHass();
wrap.appendChild(c);
return c;
};
const settle = async (c) => {
const t0 = performance.now();
while ((c._booting || !c._loadOk || !(c._devices?.length > 0)) && performance.now() - t0 < 4000) await sleep(30);
await c.updateComplete;
await sleep(350);
};
const controls = (c) => sr(c).querySelectorAll('.summary-control').length;
/** Per-frame header/stage sizes for ~600 ms after mount. */
const sample = async (c) => {
const frames = [];
const t1 = performance.now();
await new Promise((done) => {
const step = () => {
const stage = sr(c).querySelector('.stage');
if (stage) frames.push({ t: Math.round(performance.now() - t1), hdrH: c._hdrH, stageH: stage.clientHeight, summary: !!c._summary });
if (performance.now() - t1 < 600) requestAnimationFrame(step); else done();
};
requestAnimationFrame(step);
});
return frames;
};
// ---- c1: the cold page pays the import once ----
const c1 = mk(longTitle);
out.coldHasNoRuntimeSynchronously = !c1._summary; // the lazy boundary is intact
await settle(c1);
out.coldRuntimeArrived = !!c1._summary;
const finalHdr = c1._hdrH;
const finalStage = sr(c1).querySelector('.stage').clientHeight;
out.coldHeaderWraps = finalHdr > 60 ? true : `hdrH=${finalHdr}`;
c1.remove();
await sleep(30);
// ---- c2: warm replacement (same key) — AC4 ----
const c2 = mk(longTitle);
out.warmReplacementRuntimeBeforeFirstRender = !!c2._summary && !c2.hasUpdated;
const seen2 = candidates(c2);
const frames2 = await sample(c2);
await settle(c2);
out.warmReplacementFramesSampled = frames2.length > 10;
const bad2 = frames2.filter((f) => Math.abs(f.hdrH - finalHdr) > 1 || Math.abs(f.stageH - finalStage) > 2);
out.warmReplacementHeaderStableFromFirstFrame = bad2.length === 0
? true : `final=${finalHdr}/${finalStage}; frame=${JSON.stringify(bad2[0])}`;
out.warmReplacementNoStageResize = !seen2.includes('stage-resize') ? true : JSON.stringify(seen2);
out.warmReplacementDistinctRuntime = c2._summary !== c1._summary;
c2.remove();
await sleep(30);
// ---- c3: cold key on a warm page (the benchmark's per-sample card) — AC4 ----
const c3 = mk(`${longTitle} #${Date.now()}`);
out.coldKeyRuntimeBeforeFirstRender = !!c3._summary && !c3.hasUpdated;
const seen3 = candidates(c3);
const frames3 = await sample(c3);
await settle(c3);
out.coldKeyFramesSampled = frames3.length > 10;
const hdr3 = c3._hdrH;
const stage3 = sr(c3).querySelector('.stage').clientHeight;
// The first frame that has a stage already has the final header and stage.
const settled3 = frames3.filter((f) => f.stageH > 0);
const bad3 = settled3.filter((f) => Math.abs(f.hdrH - hdr3) > 1 || Math.abs(f.stageH - stage3) > 2);
out.coldKeyHeaderStableFromFirstFrame = bad3.length === 0
? true : `final=${hdr3}/${stage3}; frame=${JSON.stringify(bad3[0])} of ${settled3.length}`;
out.coldKeyNoStageResize = !seen3.includes('stage-resize') ? true : JSON.stringify(seen3);
out.coldKeyControlsRendered = controls(c3) > 0 ? true : 'no summary controls in the header';
// ---- AC5: a geometry-neutral HA tick after startup costs one render ----
seen3.length = 0;
let renders = 0;
const originalUpdate = c3.performUpdate.bind(c3);
c3.performUpdate = () => { renders++; return originalUpdate(); };
const hass = window.__mkHass();
const ceiling = hass.states['light.ceiling'];
c3.hass = { ...hass, states: { ...hass.states, 'light.ceiling': {
...ceiling, attributes: { ...ceiling.attributes, brightness: 200 },
} } };
await c3.updateComplete;
await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));
await sleep(120);
out.firstTickSingleRender = renders === 1 ? true : `performUpdate ×${renders}`;
out.firstTickNoStageResize = !seen3.includes('stage-resize') ? true : JSON.stringify(seen3);
window.__c3 = c3;
window.__seen3 = seen3;
return out;
}, { longTitle: LONG_TITLE });
for (const [k, v] of Object.entries(res)) check(k, v);
// ---- AC6: a real viewport resize still owns the stage (positive witness) ----
await page.setViewportSize({ width: 780, height: 600 });
const rez = await page.evaluate(async () => {
const out = {};
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const c3 = window.__c3;
const seen3 = window.__seen3;
seen3.length = 0;
await sleep(400);
out.realResizeOpensStageResize = seen3.includes('stage-resize') ? true : JSON.stringify(seen3);
const stage = (c3.shadowRoot || c3.renderRoot).querySelector('.stage').clientHeight;
out.realResizeStageFollows = Math.abs(stage - (600 - c3._hdrH)) < 4 ? true : `stage=${stage} hdr=${c3._hdrH}`;
c3.remove();
document.querySelectorAll('div[style*="z-index:99"]').forEach((el) => el.remove());
return out;
});
for (const [k, v] of Object.entries(rez)) check(k, v);
await finish(browser, { ...res, ...rez });
+140 -140
View File
@@ -1,309 +1,309 @@
{
"schema": 1,
"fingerprint": "807c2621b5e1c592548244ff5adb94281d1799a1481ada113e6938b375396c5c",
"fingerprint": "95d32c120d1ca37feb5a930e4a20e5ec2c460f82a52d88a41fe0d143f697ae74",
"entry": "houseplan-card.js",
"panelEntry": "houseplan-panel.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-mOz0zLlc.js",
"houseplan-assets/houseplan-card-Bj6mVEeM.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 298592,
"initialViewGzipBytes": 298891,
"initialPanelFiles": [
"houseplan-assets/houseplan-card-mOz0zLlc.js",
"houseplan-assets/houseplan-card-Bj6mVEeM.js",
"houseplan-card.js",
"houseplan-panel.js"
],
"initialPanelGzipBytes": 300898,
"initialPanelGzipBytes": 301193,
"initialPanelOnlyFiles": [
"houseplan-panel.js"
],
"initialPanelOnlyGzipBytes": 2306,
"initialPanelOnlyGzipBytes": 2302,
"lazyFiles": [
"houseplan-assets/backdrop-pick-CE5VSDDA.js",
"houseplan-assets/de-BeD9SIYF.js",
"houseplan-assets/editor-C7LQ7Tds.js",
"houseplan-assets/fr-CNfLzzDS.js",
"houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js",
"houseplan-assets/guard-Cxp8QKxn.js",
"houseplan-assets/houseplan-editor-runtime-BIh9fgX7.js",
"houseplan-assets/houseplan-onboarding-runtime-B6gvpoQT.js",
"houseplan-assets/hp-zigbee-topology-overlay-DgPgbQG8.js",
"houseplan-assets/iso-scene-render-9PxxIEfq.js",
"houseplan-assets/live-interaction-runtime-OETM7GZm.js",
"houseplan-assets/pdf-export-B6G-AgbE.js",
"houseplan-assets/summary-panel-runtime-loaded-0R-S6cIO.js",
"houseplan-assets/zigbee-topology-r_7fRPfa.js",
"houseplan-assets/zigbee-topology-runtime-DzxOfpWb.js"
"houseplan-assets/backdrop-pick-CXOE1u8Z.js",
"houseplan-assets/de-IBWzSVOj.js",
"houseplan-assets/editor-DKd1NGbT.js",
"houseplan-assets/fr-T10CV6-a.js",
"houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js",
"houseplan-assets/guard-7S6iRB0N.js",
"houseplan-assets/houseplan-editor-runtime-DJMGYUqW.js",
"houseplan-assets/houseplan-onboarding-runtime-Cdd3N5hN.js",
"houseplan-assets/hp-zigbee-topology-overlay-HyXCDpzQ.js",
"houseplan-assets/iso-scene-render-miMf0jvt.js",
"houseplan-assets/live-interaction-runtime-Dahsj-4y.js",
"houseplan-assets/pdf-export-DpHlViMV.js",
"houseplan-assets/summary-panel-runtime-loaded-2X-IAkWu.js",
"houseplan-assets/zigbee-topology-DOkBU6gZ.js",
"houseplan-assets/zigbee-topology-runtime-CiDfY5zA.js"
],
"lazyGzipBytes": 417382,
"lazyGzipBytes": 417329,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-CE5VSDDA.js",
"houseplan-assets/editor-C7LQ7Tds.js",
"houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js",
"houseplan-assets/guard-Cxp8QKxn.js",
"houseplan-assets/houseplan-editor-runtime-BIh9fgX7.js",
"houseplan-assets/zigbee-topology-r_7fRPfa.js"
"houseplan-assets/backdrop-pick-CXOE1u8Z.js",
"houseplan-assets/editor-DKd1NGbT.js",
"houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js",
"houseplan-assets/guard-7S6iRB0N.js",
"houseplan-assets/houseplan-editor-runtime-DJMGYUqW.js",
"houseplan-assets/zigbee-topology-DOkBU6gZ.js"
],
"lazyEditorGzipBytes": 205653,
"lazyEditorGzipBytes": 205633,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-CE5VSDDA.js",
"houseplan-assets/houseplan-onboarding-runtime-B6gvpoQT.js"
"houseplan-assets/backdrop-pick-CXOE1u8Z.js",
"houseplan-assets/houseplan-onboarding-runtime-Cdd3N5hN.js"
],
"lazyOnboardingGzipBytes": 14155,
"lazyOnboardingGzipBytes": 14148,
"lazyLocaleFiles": [
"houseplan-assets/de-BeD9SIYF.js",
"houseplan-assets/fr-CNfLzzDS.js"
"houseplan-assets/de-IBWzSVOj.js",
"houseplan-assets/fr-T10CV6-a.js"
],
"lazyLocaleGzipBytes": 54861,
"lazyLocaleGzipBytes": 54843,
"lazyIsometricFiles": [
"houseplan-assets/guard-Cxp8QKxn.js",
"houseplan-assets/iso-scene-render-9PxxIEfq.js"
"houseplan-assets/guard-7S6iRB0N.js",
"houseplan-assets/iso-scene-render-miMf0jvt.js"
],
"lazyIsometricGzipBytes": 13462,
"lazyIsometricGzipBytes": 13452,
"lazyFurnitureArtFiles": [
"houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js"
"houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js"
],
"lazyFurnitureArtGzipBytes": 10250,
"lazyFurnitureArtGzipBytes": 10253,
"lazyPdfFiles": [
"houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js",
"houseplan-assets/pdf-export-B6G-AgbE.js"
"houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js",
"houseplan-assets/pdf-export-DpHlViMV.js"
],
"lazyPdfGzipBytes": 120364,
"lazyPdfGzipBytes": 120367,
"files": [
{
"path": "houseplan-assets/backdrop-pick-CE5VSDDA.js",
"sha256": "650f25935f5a935e0f1c301b3bd03bb094331d1916c1094ea63c6f53866b8c45",
"path": "houseplan-assets/backdrop-pick-CXOE1u8Z.js",
"sha256": "a4abe8807d8c404a7ce9f65b0a72ca3d8f8d4d5bf38fa8220915818f9238c0db",
"rawBytes": 21134,
"gzipBytes": 7149,
"gzipBytes": 7144,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-BeD9SIYF.js",
"sha256": "7d09210471855559b0fea7e7648794d75d120a32628c5d77811e9e7c7e6d9e08",
"path": "houseplan-assets/de-IBWzSVOj.js",
"sha256": "cf031fb380b4b056c116e14a8cb26729ae4d0b275991b07989917f884f8841ff",
"rawBytes": 91441,
"gzipBytes": 27713,
"gzipBytes": 27705,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-C7LQ7Tds.js",
"sha256": "8b59adf27c503ef36a00c2eb0a04395bfd6f5c2eac107e6ba12bc2fdc0c01d56",
"path": "houseplan-assets/editor-DKd1NGbT.js",
"sha256": "e62e388db3b39eaa5f0e8f9b9676b7a6e30c453f84d22d530740f9be093512d1",
"rawBytes": 3837,
"gzipBytes": 1586,
"gzipBytes": 1585,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/fr-CNfLzzDS.js",
"sha256": "b1e8ed99400065e906fcc31a1d1f44940f981fb50c2d09a2ff74cdac10dc689e",
"path": "houseplan-assets/fr-T10CV6-a.js",
"sha256": "b602e1137a8395f963be744ed43d2054e472093502f687e40e6bd3e7a25a8794",
"rawBytes": 93719,
"gzipBytes": 27148,
"gzipBytes": 27138,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js",
"sha256": "0c81d49c5e4c9d0b138090a99ca124361f75309ed582f05d07c69fc1b6b92d7f",
"path": "houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js",
"sha256": "1e26cad8d04b459f4b1140e705a02a1d380b8c49a84f92b8ea2ce3e684544800",
"rawBytes": 33655,
"gzipBytes": 10250,
"gzipBytes": 10253,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/guard-Cxp8QKxn.js",
"sha256": "2a75558ea50a3c91ab270154441b18f7d392989faae049db00c9af57b206e2c4",
"path": "houseplan-assets/guard-7S6iRB0N.js",
"sha256": "67f95421a21516a66157d8fdc0129c2d1280cee31a3621a81039c88835d1c0f8",
"rawBytes": 518,
"gzipBytes": 371,
"gzipBytes": 368,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-mOz0zLlc.js",
"sha256": "9202eb087cecb9012efd1360898e3687b53b6729f65605978a8b52d1ed177721",
"rawBytes": 1075388,
"gzipBytes": 297788,
"path": "houseplan-assets/houseplan-card-Bj6mVEeM.js",
"sha256": "dea85ab6dfeebcb263bd672270041e34473d40b9db24e69259e55d42e5ff44d4",
"rawBytes": 1076387,
"gzipBytes": 298088,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-BeD9SIYF.js",
"houseplan-assets/editor-C7LQ7Tds.js",
"houseplan-assets/fr-CNfLzzDS.js",
"houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js",
"houseplan-assets/houseplan-editor-runtime-BIh9fgX7.js",
"houseplan-assets/houseplan-onboarding-runtime-B6gvpoQT.js",
"houseplan-assets/hp-zigbee-topology-overlay-DgPgbQG8.js",
"houseplan-assets/iso-scene-render-9PxxIEfq.js",
"houseplan-assets/live-interaction-runtime-OETM7GZm.js",
"houseplan-assets/pdf-export-B6G-AgbE.js",
"houseplan-assets/summary-panel-runtime-loaded-0R-S6cIO.js"
"houseplan-assets/de-IBWzSVOj.js",
"houseplan-assets/editor-DKd1NGbT.js",
"houseplan-assets/fr-T10CV6-a.js",
"houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js",
"houseplan-assets/houseplan-editor-runtime-DJMGYUqW.js",
"houseplan-assets/houseplan-onboarding-runtime-Cdd3N5hN.js",
"houseplan-assets/hp-zigbee-topology-overlay-HyXCDpzQ.js",
"houseplan-assets/iso-scene-render-miMf0jvt.js",
"houseplan-assets/live-interaction-runtime-Dahsj-4y.js",
"houseplan-assets/pdf-export-DpHlViMV.js",
"houseplan-assets/summary-panel-runtime-loaded-2X-IAkWu.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-BIh9fgX7.js",
"sha256": "bd9b6667a2f78549c03e5293959a5ce7dd5ab215eee62420f58f3226d06e6109",
"path": "houseplan-assets/houseplan-editor-runtime-DJMGYUqW.js",
"sha256": "bc5163222924f68e6279b412b16a53f28a76b5b4d80d4a03e7fe28c4cce77a39",
"rawBytes": 671972,
"gzipBytes": 178865,
"gzipBytes": 178859,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-CE5VSDDA.js",
"houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js",
"houseplan-assets/guard-Cxp8QKxn.js",
"houseplan-assets/houseplan-card-mOz0zLlc.js",
"houseplan-assets/zigbee-topology-r_7fRPfa.js"
"houseplan-assets/backdrop-pick-CXOE1u8Z.js",
"houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js",
"houseplan-assets/guard-7S6iRB0N.js",
"houseplan-assets/houseplan-card-Bj6mVEeM.js",
"houseplan-assets/zigbee-topology-DOkBU6gZ.js"
],
"dynamicImports": [
"houseplan-assets/zigbee-topology-runtime-DzxOfpWb.js"
"houseplan-assets/zigbee-topology-runtime-CiDfY5zA.js"
]
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-B6gvpoQT.js",
"sha256": "8d2b5ba0b169d463192e7b8531c396f89512117701c7deaa6b2e0c83491504a7",
"path": "houseplan-assets/houseplan-onboarding-runtime-Cdd3N5hN.js",
"sha256": "31ea22267928606938b34c47f1ac8ecc2041048bc8c045a411d7108d353de849",
"rawBytes": 28237,
"gzipBytes": 7006,
"gzipBytes": 7004,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-CE5VSDDA.js",
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/backdrop-pick-CXOE1u8Z.js",
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/hp-zigbee-topology-overlay-DgPgbQG8.js",
"sha256": "ac3c9b34db5772235b1879861640c4be53b912a9e468a274276cf4d0bb8beb02",
"path": "houseplan-assets/hp-zigbee-topology-overlay-HyXCDpzQ.js",
"sha256": "0e18deb183cb473c2a96fb689c91c68a3729b8ad56fb8907b01c30810585dbec",
"rawBytes": 11034,
"gzipBytes": 3730,
"gzipBytes": 3731,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js",
"houseplan-assets/zigbee-topology-r_7fRPfa.js",
"houseplan-assets/zigbee-topology-runtime-DzxOfpWb.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js",
"houseplan-assets/zigbee-topology-DOkBU6gZ.js",
"houseplan-assets/zigbee-topology-runtime-CiDfY5zA.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/iso-scene-render-9PxxIEfq.js",
"sha256": "673ec1ceddb36f8f7db22c1038222726f4227ed84e9f355783c7fe2d037ddfdb",
"path": "houseplan-assets/iso-scene-render-miMf0jvt.js",
"sha256": "00fd101b4e5469f6d289b77eab3056bedf98082520af9139a1afff5823fcb025",
"rawBytes": 38562,
"gzipBytes": 13091,
"gzipBytes": 13084,
"isEntry": false,
"imports": [
"houseplan-assets/guard-Cxp8QKxn.js",
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/guard-7S6iRB0N.js",
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/live-interaction-runtime-OETM7GZm.js",
"sha256": "5934f78ad823a530f1bb2c3cd08e053ebfdd0594f23fddadb9e57067388a3c12",
"path": "houseplan-assets/live-interaction-runtime-Dahsj-4y.js",
"sha256": "631b277aae71d9977fabb3f330c4a9cdf38e47ec51259584540b747d9c9444df",
"rawBytes": 5196,
"gzipBytes": 2129,
"gzipBytes": 2124,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/pdf-export-B6G-AgbE.js",
"sha256": "d02cb2b142ed3d4e40f64993eff91e7a81ac5ed1eac8ce9c3edb9c4298e5a98d",
"path": "houseplan-assets/pdf-export-DpHlViMV.js",
"sha256": "48b07b9ecfbbf2c1a8de53edce8bc32b1217e2c2f20eb5ceb24af75999870d8a",
"rawBytes": 237254,
"gzipBytes": 110114,
"isEntry": false,
"imports": [
"houseplan-assets/furniture-plan-art.generated-CxZ67Ud8.js",
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/furniture-plan-art.generated-C-yj8ZTi.js",
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-editor-Dme32gxk.js",
"sha256": "d434810c15653a8ca694ce41c03cd47f60b9e3ce9f3213f11a333386695658d2",
"path": "houseplan-assets/summary-panel-editor-DOpNDtK_.js",
"sha256": "4ed28cc81b7f0ccac46e0fb6ac37856ddbdf7813040585447089bf4a0a61805f",
"rawBytes": 14811,
"gzipBytes": 3390,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js",
"houseplan-assets/summary-panel-runtime-loaded-0R-S6cIO.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js",
"houseplan-assets/summary-panel-runtime-loaded-2X-IAkWu.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-metrics-JJMWCHbb.js",
"sha256": "39d084074a8865a58dc2875ea6a9baaa77b271cca9c9b27029421ca6f2112fcf",
"path": "houseplan-assets/summary-panel-metrics-DXF83oAy.js",
"sha256": "51953d004b76561a842f67b9455985cceac2663c52499d1e1acc3a3cdfdaded9",
"rawBytes": 2195,
"gzipBytes": 1124,
"gzipBytes": 1121,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-runtime-loaded-0R-S6cIO.js",
"sha256": "c609232a40013bce973b3b61c0b9ce3f248bb097481d437ab16ae971d5f201c4",
"path": "houseplan-assets/summary-panel-runtime-loaded-2X-IAkWu.js",
"sha256": "7598bcc5d26d8d2f9a15cc154c32a0b836ba0ac53b7b5f9b65f3c2c2bc0287d1",
"rawBytes": 69530,
"gzipBytes": 19035,
"gzipBytes": 19034,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": [
"houseplan-assets/summary-panel-editor-Dme32gxk.js",
"houseplan-assets/summary-panel-metrics-JJMWCHbb.js"
"houseplan-assets/summary-panel-editor-DOpNDtK_.js",
"houseplan-assets/summary-panel-metrics-DXF83oAy.js"
]
},
{
"path": "houseplan-assets/zigbee-topology-r_7fRPfa.js",
"sha256": "8267766cadbaf205251335d92caa66bd52ffa045e30e6e5cf1c756416a4e37d7",
"path": "houseplan-assets/zigbee-topology-DOkBU6gZ.js",
"sha256": "0176688590ae76a8766ee735de892e37f29211a042c27f5a2992eda0cc8ef8d9",
"rawBytes": 18961,
"gzipBytes": 7432,
"gzipBytes": 7424,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/zigbee-topology-runtime-DzxOfpWb.js",
"sha256": "db59921337f4f57b8514031b320f97a435a5de52bfb4ec1e83855e10701102a9",
"path": "houseplan-assets/zigbee-topology-runtime-CiDfY5zA.js",
"sha256": "6b43a567d3686249519d43ef6f04bb9596529d99d04f745944c0e561c0e3034e",
"rawBytes": 3941,
"gzipBytes": 1763,
"gzipBytes": 1762,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js",
"houseplan-assets/zigbee-topology-r_7fRPfa.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js",
"houseplan-assets/zigbee-topology-DOkBU6gZ.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "af03812bb65afed2afaeaca3bac52eedbcf2d6683218b28e5f53cf78224721ba",
"sha256": "b8095427f52e7413e84d3202953d15765fbd43d4536226beff01ba7ff692ae2e",
"rawBytes": 1205,
"gzipBytes": 804,
"gzipBytes": 803,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-mOz0zLlc.js"
"houseplan-assets/houseplan-card-Bj6mVEeM.js"
],
"dynamicImports": []
},
{
"path": "houseplan-panel.js",
"sha256": "b7fcfa42f2b03ab3a356197655d34118879732907391b0e40c6b3b85c0e5ca7b",
"sha256": "f0a1709a58213029c08e73da2ed81b217827c8f5cdbf96e082385cfb608eabcc",
"rawBytes": 6124,
"gzipBytes": 2306,
"gzipBytes": 2302,
"isEntry": true,
"imports": [
"houseplan-card.js"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="807c2621b5e1c592548244ff5adb94281d1799a1481ada113e6938b375396c5c";import{t as e,l as o,x as t,e5 as s,A as a,e6 as i,e7 as l,e8 as n,dV as r,b as c}from"./houseplan-card-mOz0zLlc.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95d32c120d1ca37feb5a930e4a20e5ec2c460f82a52d88a41fe0d143f697ae74";import{t as e,l as o,x as t,e4 as s,A as a,e5 as i,e6 as l,e7 as n,dV as r,b as c}from"./houseplan-card-Bj6mVEeM.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
.hass=${this.hass}
.data=${this._formData}
.schema=${e}
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95d32c120d1ca37feb5a930e4a20e5ec2c460f82a52d88a41fe0d143f697ae74";import{dT as r,dU as e,dV as t}from"./houseplan-card-Bj6mVEeM.js";const s={},a=r(class extends e{constructor(){super(...arguments),this.ot=s}render(r,e){return e()}update(r,[e,s]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((r,e)=>r===this.ot[e]))return t}else if(this.ot===e)return t;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,s)}});export{a as i};
-1
View File
@@ -1 +0,0 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="807c2621b5e1c592548244ff5adb94281d1799a1481ada113e6938b375396c5c";import{dT as r,dU as t,dV as s}from"./houseplan-card-mOz0zLlc.js";const e={},a=r(class extends t{constructor(){super(...arguments),this.ot=e}render(r,t){return t()}update(r,[t,e]){if(Array.isArray(t)){if(Array.isArray(this.ot)&&this.ot.length===t.length&&t.every((r,t)=>r===this.ot[t]))return s}else if(this.ot===t)return s;return this.ot=Array.isArray(t)?Array.from(t):t,this.render(t,e)}});export{a as i};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="807c2621b5e1c592548244ff5adb94281d1799a1481ada113e6938b375396c5c";import{b as a,A as e}from"./houseplan-card-mOz0zLlc.js";import{s,n as t,a as l,m as r,b as o,c as i,d as u}from"./summary-panel-runtime-loaded-0R-S6cIO.js";const m=m=>{const{host:c,dialog:d,problems:n,t:b}=m,y=n.find(a=>"error"===a.kind),$=d.localShow!==d.baseLocalShow||!d.localOnly&&!s(t(d.draft),d.base),p=$&&!d.busy&&!y,v=a=>n.find(e=>e.path===a),h=a=>a?b(`summary.problem.${a.code}`):"",k=()=>m.close(),g=a`<label class="summary-switch" for="summary-local-show">
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95d32c120d1ca37feb5a930e4a20e5ec2c460f82a52d88a41fe0d143f697ae74";import{b as a,A as e}from"./houseplan-card-Bj6mVEeM.js";import{s,n as t,a as l,m as r,b as o,c as i,d as u}from"./summary-panel-runtime-loaded-2X-IAkWu.js";const m=m=>{const{host:c,dialog:d,problems:n,t:b}=m,y=n.find(a=>"error"===a.kind),$=d.localShow!==d.baseLocalShow||!d.localOnly&&!s(t(d.draft),d.base),p=$&&!d.busy&&!y,v=a=>n.find(e=>e.path===a),h=a=>a?b(`summary.problem.${a.code}`):"",k=()=>m.close(),g=a`<label class="summary-switch" for="summary-local-show">
<span class="summary-switch-caption"><strong>${b("summary.show_local")}</strong>
<small id="summary-local-show-hint">${b("summary.show_local_hint")}</small></span>
<input id="summary-local-show" data-summary-local-show type="checkbox"
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="807c2621b5e1c592548244ff5adb94281d1799a1481ada113e6938b375396c5c";import{az as e,ea as t,eb as n,bs as i,ec as r,P as a,a5 as s,M as o,aq as c,N as l,bq as d,bp as u,ed as f}from"./houseplan-card-mOz0zLlc.js";function g(t){if(!t.registry.authoritative)return null;const n=new Set,i=e(t.markers);for(const e of Object.values(t.registry.devices||{}))e?.id&&"service"!==e.entry_type&&!i.devices.has(e.id)&&e.area_id&&t.areaToSpace[e.area_id]&&n.add(e.id);for(const e of t.markers||[]){if(e.removed||"virtual"===e.binding)continue;const r=String(e.binding||"").indexOf(":");if(r<1)continue;const a=e.binding.slice(0,r),s=e.binding.slice(r+1),o="device"===a?t.registry.devices?.[s]:null,c="entity"===a?t.registry.entities?.[s]:null,l="device"===a?s:c?.device_id,d="device"===a?o?.area_id:c?.area_id||l&&t.registry.devices?.[l]?.area_id,u="string"==typeof e.room_id&&e.room_id.length>0&&null===e.area?"":e.area||d||"",f=u&&t.areaToSpace[u]||e.space||t.firstSpaceId;l&&t.registry.devices?.[l]&&t.spaceIds.has(f)&&(!i.devices.has(l)||"entity"===a&&i.liveEntities.has(s))&&n.add(l)}return n}function m(e,t){return e?.length?t?.length?f(e,t):e:t}function v(e,t){try{let n=0;for(const i of t){const t=e.spaces.find(e=>String(e?.id)===i.id);if(!t)continue;const f=r(t,i);let g=null;for(const e of i.rooms){if(!e.id)continue;const t=a(e);if(!t)continue;const n=s(i.rooms,e.id,f.walls,f.openCuts,o,f.cellCm,c,l)||t;g=m(g,d(n,f.physicalBodies))}const v=f.cellCm/c;n+=u(g)*v*v/1e4}return n}catch{return null}}function y(e,t,n,r){if("system"!==e.type)return null;if("device_count"===e.key)return null===t.deviceCount?null:String(t.deviceCount);if("total_area"===e.key)return null===t.areaM2?null:i(t.areaM2,"mi"===n?.config?.unit_system?.length);try{return new Intl.DateTimeFormat(r||void 0,{dateStyle:"short",timeStyle:"short",timeZone:n?.config?.time_zone||void 0}).format(t.now)}catch{return t.now.toLocaleString()}}function _(e,i){const r=e?.states?.[i];if(!r)return null;const a=t(e,i);return a?n(a,String(r.attributes?.unit_of_measurement||"")):null}export{g as representedHaDeviceIds,_ as summaryEntityValue,y as summarySystemValue,v as totalCleanFloorAreaM2};
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95d32c120d1ca37feb5a930e4a20e5ec2c460f82a52d88a41fe0d143f697ae74";import{az as e,ea as t,eb as n,bs as i,ec as r,P as a,a5 as s,M as o,aq as c,N as l,bq as d,bp as u,ed as f}from"./houseplan-card-Bj6mVEeM.js";function g(t){if(!t.registry.authoritative)return null;const n=new Set,i=e(t.markers);for(const e of Object.values(t.registry.devices||{}))e?.id&&"service"!==e.entry_type&&!i.devices.has(e.id)&&e.area_id&&t.areaToSpace[e.area_id]&&n.add(e.id);for(const e of t.markers||[]){if(e.removed||"virtual"===e.binding)continue;const r=String(e.binding||"").indexOf(":");if(r<1)continue;const a=e.binding.slice(0,r),s=e.binding.slice(r+1),o="device"===a?t.registry.devices?.[s]:null,c="entity"===a?t.registry.entities?.[s]:null,l="device"===a?s:c?.device_id,d="device"===a?o?.area_id:c?.area_id||l&&t.registry.devices?.[l]?.area_id,u="string"==typeof e.room_id&&e.room_id.length>0&&null===e.area?"":e.area||d||"",f=u&&t.areaToSpace[u]||e.space||t.firstSpaceId;l&&t.registry.devices?.[l]&&t.spaceIds.has(f)&&(!i.devices.has(l)||"entity"===a&&i.liveEntities.has(s))&&n.add(l)}return n}function m(e,t){return e?.length?t?.length?f(e,t):e:t}function v(e,t){try{let n=0;for(const i of t){const t=e.spaces.find(e=>String(e?.id)===i.id);if(!t)continue;const f=r(t,i);let g=null;for(const e of i.rooms){if(!e.id)continue;const t=a(e);if(!t)continue;const n=s(i.rooms,e.id,f.walls,f.openCuts,o,f.cellCm,c,l)||t;g=m(g,d(n,f.physicalBodies))}const v=f.cellCm/c;n+=u(g)*v*v/1e4}return n}catch{return null}}function y(e,t,n,r){if("system"!==e.type)return null;if("device_count"===e.key)return null===t.deviceCount?null:String(t.deviceCount);if("total_area"===e.key)return null===t.areaM2?null:i(t.areaM2,"mi"===n?.config?.unit_system?.length);try{return new Intl.DateTimeFormat(r||void 0,{dateStyle:"short",timeStyle:"short",timeZone:n?.config?.time_zone||void 0}).format(t.now)}catch{return t.now.toLocaleString()}}function _(e,i){const r=e?.states?.[i];if(!r)return null;const a=t(e,i);return a?n(a,String(r.attributes?.unit_of_measurement||"")):null}export{g as representedHaDeviceIds,_ as summaryEntityValue,y as summarySystemValue,v as totalCleanFloorAreaM2};
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="807c2621b5e1c592548244ff5adb94281d1799a1481ada113e6938b375396c5c";import{n as t,b as n}from"./zigbee-topology-r_7fRPfa.js";import{F as e}from"./houseplan-card-mOz0zLlc.js";const o=new WeakMap;function i(t){const n=function(t){const n=t?.connection||t;return!n||"object"!=typeof n&&"function"!=typeof n?null:n}(t);if(!n)return null;let e=o.get(n);return e||(e={revision:0,topologies:[],states:{},listeners:new Set,inflight:new Map},o.set(n,e)),e}function r(t,n,e){t.states={...t.states,[n]:e},function(t){t.revision++;for(const n of t.listeners)n()}(t)}function s(t){const n=t?.code;if("permission"===n||"unsupported"===n||"timeout"===n||"invalid_topic"===n||"invalid_payload"===n)return n;const e=String(t?.message||"").toLowerCase();return e.includes("unauthor")||e.includes("permission")?"permission":e.includes("unknown_command")||e.includes("not found")?"unsupported":"provider"}function a(t){return Object.assign(new Error(t),{code:t})}function c(t,n,e){const o=t.inflight.get(n);if(o)return o;r(t,n,{phase:"loading"});const i=e().then(n=>{if(!n.nodes.length&&n.warnings.some(t=>"invalid_payload"===t.code))throw a("invalid_payload");!function(t,n){t.topologies=[...t.topologies.filter(t=>!(t.provider===n.provider&&t.instanceId===n.instanceId)),n],r(t,"zha"===n.provider?"zha":`z2m:${n.instanceId}`,{phase:"ready",obtainedAt:n.obtainedAt,partial:n.warnings.length>0})}(t,n)}).catch(e=>{r(t,n,{phase:"error",error:s(e)})}).finally(()=>t.inflight.delete(n));return t.inflight.set(n,i),i}function l(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function u(t,n){const e=i(t);return e?(e.listeners.add(n),()=>e.listeners.delete(n)):()=>{}}function p(t){if(!0!==t?.user?.is_admin)throw a("permission")}function f(n){const e=i(n);return e?c(e,"zha",async()=>{if(p(n),"function"!=typeof n?.callWS)throw a("unsupported");return t(await n.callWS({type:"zha/devices"}))}):Promise.resolve()}function d(t){return null!==t&&"object"==typeof t?t:null}function h(t){const n=d(t),e=n?.payload??t;if("string"!=typeof e)return e;try{return JSON.parse(e)}catch{return null}}async function g(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function m(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const l=e(o);return c(s,`z2m:${l||String(o)}`,async()=>{if(p(t),!l)throw a("invalid_topic");const e=t.connection,o=e?.subscribeMessage;if("function"!=typeof o||"function"!=typeof t?.callService)throw a("unsupported");const i=function(){const t=globalThis.crypto;return"function"==typeof t?.randomUUID?`houseplan-${t.randomUUID()}`:`houseplan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}(),s=Date.now()+Math.max(1,r);let c=!1,u=null,f=null,m=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,m=n}),b=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&h(t)&&u?.()},{type:"mqtt/subscribe",topic:`${l}/bridge/info`});"function"==typeof r&&b.push(r);const p=await o.call(e,t=>{if(!0===d(t)?.retain)return;const n=h(t);null!==n?n&&function(t){const n=d(t),e=n?.transaction??d(n?.data)?.transaction;return"string"==typeof e||"number"==typeof e?String(e):null}(n)===i&&f?.(n):c&&m?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof p&&b.push(p),await g(y,Math.min(4e3,Math.max(1,s-Date.now()))),c=!0,await t.callService("mqtt","publish",{topic:`${l}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:i}),qos:0,retain:!1});const v=await g(w,s-Date.now()),_=d(v)?.status;if(_&&"ok"!==_)throw a("provider");return n(v,l)}finally{for(const t of b)try{t()}catch{}}})}export{f as readZhaTopology,m as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot};
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95d32c120d1ca37feb5a930e4a20e5ec2c460f82a52d88a41fe0d143f697ae74";import{n as t,b as n}from"./zigbee-topology-DOkBU6gZ.js";import{F as e}from"./houseplan-card-Bj6mVEeM.js";const o=new WeakMap;function i(t){const n=function(t){const n=t?.connection||t;return!n||"object"!=typeof n&&"function"!=typeof n?null:n}(t);if(!n)return null;let e=o.get(n);return e||(e={revision:0,topologies:[],states:{},listeners:new Set,inflight:new Map},o.set(n,e)),e}function r(t,n,e){t.states={...t.states,[n]:e},function(t){t.revision++;for(const n of t.listeners)n()}(t)}function s(t){const n=t?.code;if("permission"===n||"unsupported"===n||"timeout"===n||"invalid_topic"===n||"invalid_payload"===n)return n;const e=String(t?.message||"").toLowerCase();return e.includes("unauthor")||e.includes("permission")?"permission":e.includes("unknown_command")||e.includes("not found")?"unsupported":"provider"}function a(t){return Object.assign(new Error(t),{code:t})}function c(t,n,e){const o=t.inflight.get(n);if(o)return o;r(t,n,{phase:"loading"});const i=e().then(n=>{if(!n.nodes.length&&n.warnings.some(t=>"invalid_payload"===t.code))throw a("invalid_payload");!function(t,n){t.topologies=[...t.topologies.filter(t=>!(t.provider===n.provider&&t.instanceId===n.instanceId)),n],r(t,"zha"===n.provider?"zha":`z2m:${n.instanceId}`,{phase:"ready",obtainedAt:n.obtainedAt,partial:n.warnings.length>0})}(t,n)}).catch(e=>{r(t,n,{phase:"error",error:s(e)})}).finally(()=>t.inflight.delete(n));return t.inflight.set(n,i),i}function l(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function u(t,n){const e=i(t);return e?(e.listeners.add(n),()=>e.listeners.delete(n)):()=>{}}function p(t){if(!0!==t?.user?.is_admin)throw a("permission")}function f(n){const e=i(n);return e?c(e,"zha",async()=>{if(p(n),"function"!=typeof n?.callWS)throw a("unsupported");return t(await n.callWS({type:"zha/devices"}))}):Promise.resolve()}function d(t){return null!==t&&"object"==typeof t?t:null}function h(t){const n=d(t),e=n?.payload??t;if("string"!=typeof e)return e;try{return JSON.parse(e)}catch{return null}}async function g(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function m(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const l=e(o);return c(s,`z2m:${l||String(o)}`,async()=>{if(p(t),!l)throw a("invalid_topic");const e=t.connection,o=e?.subscribeMessage;if("function"!=typeof o||"function"!=typeof t?.callService)throw a("unsupported");const i=function(){const t=globalThis.crypto;return"function"==typeof t?.randomUUID?`houseplan-${t.randomUUID()}`:`houseplan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}(),s=Date.now()+Math.max(1,r);let c=!1,u=null,f=null,m=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,m=n}),b=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&h(t)&&u?.()},{type:"mqtt/subscribe",topic:`${l}/bridge/info`});"function"==typeof r&&b.push(r);const p=await o.call(e,t=>{if(!0===d(t)?.retain)return;const n=h(t);null!==n?n&&function(t){const n=d(t),e=n?.transaction??d(n?.data)?.transaction;return"string"==typeof e||"number"==typeof e?String(e):null}(n)===i&&f?.(n):c&&m?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof p&&b.push(p),await g(y,Math.min(4e3,Math.max(1,s-Date.now()))),c=!0,await t.callService("mqtt","publish",{topic:`${l}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:i}),qos:0,retain:!1});const v=await g(w,s-Date.now()),_=d(v)?.status;if(_&&"ok"!==_)throw a("provider");return n(v,l)}finally{for(const t of b)try{t()}catch{}}})}export{f as readZhaTopology,m as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot};
+1 -1
View File
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="807c2621b5e1c592548244ff5adb94281d1799a1481ada113e6938b375396c5c";try{await import("./houseplan-assets/houseplan-card-mOz0zLlc.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale houseplan-card.js: the implementation chunk is unavailable",e)}
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="95d32c120d1ca37feb5a930e4a20e5ec2c460f82a52d88a41fe0d143f697ae74";try{await import("./houseplan-assets/houseplan-card-Bj6mVEeM.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale houseplan-card.js: the implementation chunk is unavailable",e)}
+1 -1
View File
File diff suppressed because one or more lines are too long
+14
View File
@@ -1966,6 +1966,20 @@ imported only after the settings button is pressed. It edits one draft; a
successful revision-checked shared write precedes application of the draft
local show choice.
`summary-runtime-loader.ts` (#506) separates the summary *code* from its
*state*. The loaded factory is remembered per page; every host builds its own
runtime from it — preferences, drafts, subscriptions, timers and DOM are never
shared between card instances. Once the factory is warm, a new instance
(a warm remount or a cold card on a warm page) receives its runtime
synchronously in `connectedCallback`, before the first Lit render, so the
first header measurement already includes the summary controls and no late
summary-driven refit or `stage-resize` continuity candidate follows. The chunk
itself stays lazy: the first cold mount pays one dynamic import, concurrent
cold mounts share that pending import, a failed import is forgotten so the next
connection may retry, and a disconnect cancels the pending attachment of that
connection so a late resolution cannot connect a runtime to a host that has
left the tree. A same-node reconnect keeps its instance.
The #505 designer-aligned surface stays in the lazy summary graph. Its sheet
includes the settings-only composition from `summary-panel-editor-style.ts`.
The runtime installs `summary-panel-dialog-style.ts` in each summary dialog's
+6
View File
@@ -2,6 +2,12 @@
## Unreleased
- Reopening the plan on a page that has already loaded the summary panel no
longer triggers a late header re-measurement and an extra round of heavy
redraws right after the first frame: the panel's controls are part of the
first frame, so the first device/glow update lands on a stable stage
([#506](https://github.com/Matysh/houseplan-card/issues/506)).
## v1.73.0 — 2026-09-09
- House Plan opens from its own page in the Home Assistant sidebar, without
+6
View File
@@ -8,6 +8,12 @@
## Не выпущено
- Повторное открытие плана на странице, где сводная панель уже загружена,
больше не вызывает позднего перемера шапки и лишнего круга тяжёлых
перерисовок сразу после первого кадра: элементы панели входят в первый кадр,
и первое обновление устройств/свечения ложится на стабильную сцену
([#506](https://github.com/Matysh/houseplan-card/issues/506)).
## v1.73.0 — 2026-09-09
- House Plan открывается с собственной страницы в боковом меню Home Assistant;
+12 -12
View File
@@ -3,7 +3,7 @@
"fixture": "synthetic-only",
"chromium": "151.0.7922.34",
"oxipng": "oxipng 10.2.0",
"sourceFingerprint": "81bd8a01a7ec4bbe2a4058b42025c7e153cc51d99a108a575750160578d4ec39",
"sourceFingerprint": "4c472e043fa69a664ba9778200ff67b223e2f23b4eeb29c84688c14d93956de7",
"captureScriptSha256": "9e4b0ae533407fe003a129078aec020f2c83d67cdb6fc6da696ce3d829beaeb5",
"command": "npm run build && node demo/docs/capture.mjs",
"scenarios": {
@@ -15,7 +15,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "81bd8a01a7ec4bbe2a4058b42025c7e153cc51d99a108a575750160578d4ec39",
"sourceSha256": "4c472e043fa69a664ba9778200ff67b223e2f23b4eeb29c84688c14d93956de7",
"imageSha256": "76c35a87810e3dc6890bda7b43da133f57d8860031204a62cda7115c15950090"
},
"view-touch": {
@@ -26,7 +26,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "81bd8a01a7ec4bbe2a4058b42025c7e153cc51d99a108a575750160578d4ec39",
"sourceSha256": "4c472e043fa69a664ba9778200ff67b223e2f23b4eeb29c84688c14d93956de7",
"imageSha256": "795585acb09488236ebb8028a7fcbd918404c5fe01560bb9b4559de13d441625"
},
"space-create": {
@@ -37,7 +37,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "81bd8a01a7ec4bbe2a4058b42025c7e153cc51d99a108a575750160578d4ec39",
"sourceSha256": "4c472e043fa69a664ba9778200ff67b223e2f23b4eeb29c84688c14d93956de7",
"imageSha256": "9f0970de09772674c3156b9fc5b2d49de0ceb22be11ecf8b136047d37d100a84"
},
"room-contour-close": {
@@ -48,7 +48,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "81bd8a01a7ec4bbe2a4058b42025c7e153cc51d99a108a575750160578d4ec39",
"sourceSha256": "4c472e043fa69a664ba9778200ff67b223e2f23b4eeb29c84688c14d93956de7",
"imageSha256": "0c354a79f9c3660788b6c5e889a27b6bd78b1f209dc0eef6bb43e2c1344028c4"
},
"plan-context-tray": {
@@ -59,7 +59,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "81bd8a01a7ec4bbe2a4058b42025c7e153cc51d99a108a575750160578d4ec39",
"sourceSha256": "4c472e043fa69a664ba9778200ff67b223e2f23b4eeb29c84688c14d93956de7",
"imageSha256": "64169c543291bc4df806dcf8ff484b42c8ea55eb343323d945344e0e28f2ce97"
},
"device-editor": {
@@ -70,7 +70,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "81bd8a01a7ec4bbe2a4058b42025c7e153cc51d99a108a575750160578d4ec39",
"sourceSha256": "4c472e043fa69a664ba9778200ff67b223e2f23b4eeb29c84688c14d93956de7",
"imageSha256": "3c8703175605fcf0387d654cd30783ecfd33e21743e70b45190a5ae1a8d355c2"
},
"device-display-preview": {
@@ -81,7 +81,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "81bd8a01a7ec4bbe2a4058b42025c7e153cc51d99a108a575750160578d4ec39",
"sourceSha256": "4c472e043fa69a664ba9778200ff67b223e2f23b4eeb29c84688c14d93956de7",
"imageSha256": "5d6193202102f5e6d85567f1111fa105895b933979fbf369c6ba0b1029deb823"
},
"background-editor": {
@@ -92,7 +92,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "81bd8a01a7ec4bbe2a4058b42025c7e153cc51d99a108a575750160578d4ec39",
"sourceSha256": "4c472e043fa69a664ba9778200ff67b223e2f23b4eeb29c84688c14d93956de7",
"imageSha256": "375900a29aa62632883ede6727b7dbb8b48cabe9d7d0e16425f9779c837f3dd9"
},
"room-card": {
@@ -103,7 +103,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "81bd8a01a7ec4bbe2a4058b42025c7e153cc51d99a108a575750160578d4ec39",
"sourceSha256": "4c472e043fa69a664ba9778200ff67b223e2f23b4eeb29c84688c14d93956de7",
"imageSha256": "3d85424676c823ddca3fa7dab3cce089b6030e48c05d16d2d48d922c66c0cbd2"
},
"device-info": {
@@ -114,7 +114,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "81bd8a01a7ec4bbe2a4058b42025c7e153cc51d99a108a575750160578d4ec39",
"sourceSha256": "4c472e043fa69a664ba9778200ff67b223e2f23b4eeb29c84688c14d93956de7",
"imageSha256": "ac962820f8e1c5cb5b042d39b18dc95905fa840cb2d4749060d991be4f6977c2"
},
"pdf-export": {
@@ -125,7 +125,7 @@
},
"theme": "light",
"language": "en",
"sourceSha256": "81bd8a01a7ec4bbe2a4058b42025c7e153cc51d99a108a575750160578d4ec39",
"sourceSha256": "4c472e043fa69a664ba9778200ff67b223e2f23b4eeb29c84688c14d93956de7",
"imageSha256": "4537692fd57a3ed95528eddd748eee5cb6e456ffab7cc49169853d62459252cd"
}
},
+7 -1
View File
@@ -200,8 +200,14 @@ export const LOW_HEADROOM_WARNING_BYTES = 15_000;
* that ceiling. A 299.1 kB ceiling restores the required >500 B noise margin
* on both sides of the existing 2 kB ratchet band. The overall 301 066 B
* budget is unchanged.
*
* 2026-09-09, #506: потолок перецентрирован 299 100 → 299 600. В initial View
* вошёл `summary-runtime-loader.ts` — кеш фабрики кода сводной панели и слот
* подключения на экземпляр (+299 Б gzip); сам runtime панели остаётся
* ленивым чанком. Измеренный факт 298 891 Б оставляет 709 Б сверху и 1 291 Б
* до нижней границы полосы; общий бюджет 301 066 Б и долг #367 не меняются.
*/
export const INITIAL_VIEW_GZIP_CEILING = 299_100;
export const INITIAL_VIEW_GZIP_CEILING = 299_600;
export const INITIAL_VIEW_CEILING_BAND = 2_000;
/**
+16
View File
@@ -8087,6 +8087,22 @@ const MUTANT_DEFINITIONS = [
+ ' # The store is the durable authority (#335): a drop that\n',
}],
},
{
id: 'summary-runtime-attaches-after-first-render',
guard: 'node demo/smoke_summary_warm_attach.mjs',
because: 'a warm summary chunk must hand the new instance its runtime synchronously in '
+ 'connectedCallback; deferring even a warm factory to a promise lets the first header '
+ 'measurement miss the controls and a late refit follows (#506 AC4/AC5)',
patches: [{
file: 'src/summary-runtime-loader.ts',
find: ' const warm = this.create(host);\n'
+ ' if (warm) {\n'
+ ' ready(warm);\n'
+ ' return attachment;\n'
+ ' }\n',
replace: ' // mutant: the warm factory is deferred like a cold import\n',
}],
},
{
id: 'quota-counts-the-staged-upload-twice',
guard: 'node scripts/backend-test-guard.mjs '
+4 -4
View File
@@ -408,6 +408,7 @@ import {
} from './space-order';
import { applyOpeningMoves, mergeCollinearPartitions, spaceMergeGeometry } from './wall-merge';
import type { MarkerRoomReferenceSnapshot } from './room-reference-transaction';
import { SummaryRuntimeSlot, summaryRuntimeLoader } from './summary-runtime-loader';
const CARD_VERSION = '1.73.0';
const ENTRY_BUILD_FINGERPRINT = '__HOUSEPLAN_SOURCE_FINGERPRINT__';
@@ -2294,7 +2295,7 @@ export class HouseplanCard extends LitElement {
private _renderProjection: 'flat' | 'iso' = 'flat';
// ---- kiosk (wall device) mode ----
private _kioskScale: { icon: number; font: number } = { icon: 1, font: 1 }; private _kioskDialog = false;
private _summary?: import('./summary-panel-runtime-loaded').LoadedSummaryPanelRuntime;
private _summary?: import('./summary-panel-runtime-loaded').LoadedSummaryPanelRuntime; private readonly _summarySlot = new SummaryRuntimeSlot(summaryRuntimeLoader, this, (runtime) => { this._summary = runtime; this._capturedSnapshotSequence = -1; /* summary entities (re)join the live subscription */ if (this.isConnected) { runtime.connect(); if (this.hasUpdated) this.requestUpdate(); } }); // #506: warm code attaches before the first render
/**
* Previous entity states + the short event/terminal-transition window for
* every marker. The map lives outside Lit state: hass ticks update it, one
@@ -2625,7 +2626,7 @@ export class HouseplanCard extends LitElement {
this._showToast(this._t('toast.locale_load_failed'));
}), subscribeFurnitureArtLoadFailures(() => this._showToast(this._t('toast.furniture_art_load_failed')))); // #474
super.connectedCallback();
if (this._summary) this._summary.connect(); else void import('./summary-panel-runtime-loaded').then(({ LoadedSummaryPanelRuntime }) => { this._summary ||= new LoadedSummaryPanelRuntime(this); this._capturedSnapshotSequence = -1; if (this.isConnected) this._summary.connect(); this.requestUpdate(); }).catch(() => undefined);
this._summarySlot.connect();
void this._ensureLiveRuntime().catch(() => this.requestUpdate());
this._pointerModality.connect(this.ownerDocument.defaultView);
const PointerHoverObserver = this.ownerDocument.defaultView?.MutationObserver;
@@ -2719,7 +2720,7 @@ export class HouseplanCard extends LitElement {
if (this._vacRaf) { cancelAnimationFrame(this._vacRaf); this._vacRaf = 0; }
if (this._refitRaf) { cancelAnimationFrame(this._refitRaf); this._refitRaf = 0; }
this._warmModeRequest = 0;
if (this._dayCycleTimer) { clearInterval(this._dayCycleTimer); this._dayCycleTimer = 0; } this._summary?.disconnect();
if (this._dayCycleTimer) { clearInterval(this._dayCycleTimer); this._dayCycleTimer = 0; } this._summarySlot.disconnect();
this._dayCycleClockKey = '';
if (this._bootSettleRaf) { cancelAnimationFrame(this._bootSettleRaf); this._bootSettleRaf = 0; }
this._bootSettling = false;
@@ -12205,7 +12206,6 @@ export class HouseplanCard extends LitElement {
}
/**
* HP-1540-01: an auto-discovered vacuum has NO config marker until the first
* general Save of its dialog, yet the «Живая позиция» section is already
+143
View File
@@ -0,0 +1,143 @@
/**
* Page-level cache of the summary runtime *code*; state stays per host (#506).
*
* The summary panel is a lazy chunk. Before #506 every card instance — even a
* warm remount on a page whose chunk had loaded long ago — attached its
* runtime through `import().then()`, i.e. after Lit's first render. The first
* header measurement therefore saw a header without summary controls; the
* controls arrived a beat later, the stage shrank, and the deferred refit
* opened a `stage-resize` continuity candidate with three more render passes
* on top of the first HA tick.
*
* The loader keeps exactly one thing across instances: the factory that
* builds a runtime for a host. Once it is warm, `create()` hands a fresh
* runtime to a new host synchronously — inside `connectedCallback`, before
* the first render — so the first frame already carries the full header.
* Nothing else is shared: every host gets its own runtime with its own
* preferences, drafts, subscriptions, timers and DOM.
*
* A pending import is shared by concurrent cold mounts (one network round
* trip) but never its result-instance. A failed import is forgotten, so the
* next `attach` may try again; there are no background retries.
*/
export type SummaryRuntimeFactory<Host, Runtime> = (host: Host) => Runtime;
export interface SummaryRuntimeAttachment {
/** Forget this attempt: a later resolution must not touch the host. */
cancel(): void;
}
export class SummaryRuntimeLoader<Host, Runtime> {
private factory: SummaryRuntimeFactory<Host, Runtime> | null = null;
private pending: Promise<SummaryRuntimeFactory<Host, Runtime>> | null = null;
public constructor(private readonly load: () => Promise<SummaryRuntimeFactory<Host, Runtime>>) {}
/** The code is on the page: `create()` will succeed synchronously. */
public get warm(): boolean {
return this.factory !== null;
}
/** A fresh runtime for `host`, or null while the code is still cold. */
public create(host: Host): Runtime | null {
return this.factory ? this.factory(host) : null;
}
/**
* Build the runtime for `host` as soon as the code is available. When it is
* warm, `ready` runs before this returns. Otherwise the shared import is
* awaited and `ready` runs once — unless the attachment was cancelled
* meanwhile (host disconnected, or a newer attach superseded this one).
*/
public attach(host: Host, ready: (runtime: Runtime) => void): SummaryRuntimeAttachment {
let live = true;
const attachment = { cancel: () => { live = false; } };
const warm = this.create(host);
if (warm) {
ready(warm);
return attachment;
}
void this.ensure().then((factory) => {
if (live) ready(factory(host));
}, () => undefined);
return attachment;
}
/** Share one in-flight import; a rejection clears it so a retry is possible. */
public ensure(): Promise<SummaryRuntimeFactory<Host, Runtime>> {
if (this.factory) return Promise.resolve(this.factory);
if (this.pending) return this.pending;
this.pending = this.load().then((factory) => {
this.factory = factory;
this.pending = null;
return factory;
}, (error: unknown) => {
this.pending = null;
throw error;
});
return this.pending;
}
/** Test seam: a page that wants to model a cold chunk again. */
public reset(): void {
this.factory = null;
this.pending = null;
}
}
/** What one host's slot needs from its runtime. */
export interface SummaryRuntimeLike {
connect(): void;
disconnect(): void;
}
/**
* One host's summary runtime and the pending attachment of its current
* connection. `connect()` follows the host's `connectedCallback`, `disconnect()`
* its `disconnectedCallback`; the runtime instance survives a same-node
* reconnect, a pending cold attach does not survive a disconnect.
*/
export class SummaryRuntimeSlot<Host, Runtime extends SummaryRuntimeLike> {
public runtime: Runtime | undefined;
private attachment: SummaryRuntimeAttachment | null = null;
public constructor(
private readonly loader: SummaryRuntimeLoader<Host, Runtime>,
private readonly host: Host,
/** Runs once per instance, right after adoption; connects when the host is in the tree. */
private readonly adopted: (runtime: Runtime) => void,
) {}
public connect(): void {
if (this.runtime) { this.runtime.connect(); return; }
this.attachment?.cancel();
const attachment = this.loader.attach(this.host, (runtime) => this.adopt(runtime));
// Warm code has already run `adopt`; only a cold attach stays pending.
if (!this.runtime) this.attachment = attachment;
}
public disconnect(): void {
this.attachment?.cancel();
this.attachment = null;
this.runtime?.disconnect();
}
private adopt(runtime: Runtime): void {
this.attachment = null;
if (this.runtime) return;
this.runtime = runtime;
this.adopted(runtime);
}
}
type LoadedSummaryPanelRuntime = import('./summary-panel-runtime-loaded').LoadedSummaryPanelRuntime;
/**
* The page's one summary code cache. The chunk stays lazy: the first cold
* mount pays the import; every later instance builds its runtime synchronously.
*/
export const summaryRuntimeLoader = new SummaryRuntimeLoader<unknown, LoadedSummaryPanelRuntime>(
() => import('./summary-panel-runtime-loaded').then(
({ LoadedSummaryPanelRuntime }) => (host) => new LoadedSummaryPanelRuntime(host),
),
);
+113
View File
@@ -0,0 +1,113 @@
// #506: the summary chunk's code is cached per page, its runtime is per host.
import assert from 'node:assert/strict';
import test from 'node:test';
import { SummaryRuntimeLoader } from '../test-build/summary-runtime-loader.js';
const deferred = () => {
let resolve;
let reject;
const promise = new Promise((ok, fail) => { resolve = ok; reject = fail; });
return { promise, resolve, reject };
};
const tick = () => new Promise((done) => setTimeout(done, 0));
class Runtime {
constructor(host) { this.host = host; this.connected = 0; }
}
test('#506 AC1: a warm factory builds a distinct runtime for every host, synchronously', async () => {
let loads = 0;
const loader = new SummaryRuntimeLoader(async () => { loads++; return (host) => new Runtime(host); });
assert.equal(loader.warm, false);
assert.equal(loader.create({ id: 'cold' }), null, 'cold code cannot be created synchronously');
await loader.ensure();
assert.equal(loader.warm, true);
const hostA = { id: 'a' };
const hostB = { id: 'b' };
const readyA = [];
const readyB = [];
loader.attach(hostA, (runtime) => readyA.push(runtime));
loader.attach(hostB, (runtime) => readyB.push(runtime));
assert.equal(readyA.length, 1, 'warm attach runs ready before returning');
assert.equal(readyB.length, 1);
assert.notEqual(readyA[0], readyB[0], 'no runtime instance is shared between hosts');
assert.equal(readyA[0].host, hostA);
assert.equal(readyB[0].host, hostB);
assert.equal(loads, 1, 'the code was loaded once for the page');
});
test('#506 AC2: concurrent cold mounts share one import but not one instance', async () => {
const pending = deferred();
let loads = 0;
const loader = new SummaryRuntimeLoader(() => { loads++; return pending.promise; });
const seen = [];
loader.attach({ id: 'a' }, (runtime) => seen.push(runtime));
loader.attach({ id: 'b' }, (runtime) => seen.push(runtime));
assert.equal(loads, 1);
assert.equal(seen.length, 0, 'nothing is attached before the import lands');
pending.resolve((host) => new Runtime(host));
await tick();
assert.equal(seen.length, 2);
assert.notEqual(seen[0], seen[1]);
assert.deepEqual(seen.map((runtime) => runtime.host.id), ['a', 'b']);
assert.equal(loader.warm, true);
});
test('#506 AC2: a cancelled attachment never reaches its host; a reconnect attaches once', async () => {
const pending = deferred();
const loader = new SummaryRuntimeLoader(() => pending.promise);
const host = { id: 'reconnecting' };
const stale = [];
const fresh = [];
const first = loader.attach(host, (runtime) => stale.push(runtime));
first.cancel(); // disconnected while the chunk was still loading
const second = loader.attach(host, (runtime) => fresh.push(runtime));
pending.resolve((h) => new Runtime(h));
await tick();
assert.deepEqual(stale, [], 'the cancelled attempt must not run its side effects');
assert.equal(fresh.length, 1, 'the live attempt attaches exactly once');
assert.equal(fresh[0].host, host);
second.cancel(); // cancelling a completed attachment is a no-op
assert.equal(fresh.length, 1);
});
test('#506 AC2: a failed import is forgotten so the next attach may retry', async () => {
const attempts = [];
const loader = new SummaryRuntimeLoader(() => {
const attempt = deferred();
attempts.push(attempt);
return attempt.promise;
});
const seen = [];
loader.attach({ id: 'first' }, (runtime) => seen.push(runtime));
attempts[0].reject(new Error('network'));
await tick();
assert.equal(seen.length, 0);
assert.equal(loader.warm, false, 'a failure is not cached as the page state');
loader.attach({ id: 'second' }, (runtime) => seen.push(runtime));
assert.equal(attempts.length, 2, 'the next attach starts a fresh import');
attempts[1].resolve((host) => new Runtime(host));
await tick();
assert.equal(seen.length, 1);
assert.equal(seen[0].host.id, 'second');
assert.equal(loader.warm, true);
});
test('#506: ensure() rejects to its caller and a reset returns the page to cold', async () => {
let fail = true;
const loader = new SummaryRuntimeLoader(async () => {
if (fail) throw new Error('chunk missing');
return (host) => new Runtime(host);
});
await assert.rejects(loader.ensure(), /chunk missing/);
fail = false;
await loader.ensure();
assert.equal(loader.warm, true);
loader.reset();
assert.equal(loader.warm, false);
assert.equal(loader.create({}), null);
});
+1 -1
View File
@@ -11,7 +11,7 @@
},
"include": [
"src/color.ts", "src/styles.ts", "src/styles/*.styles.ts", "src/logic.ts", "src/i18n.ts", "src/i18n/registry.ts", "src/i18n/language-runtime.ts", "src/glow-blend.ts", "src/grid-scale.ts", "src/device-visual.ts", "src/device-pulse.ts", "src/device-presentation.ts", "src/device-marker-geometry.ts",
"src/device-face.ts", "src/device-toggle.ts", "src/marker-toggle-entity.ts", "src/activity-runtime.ts", "src/device-area-relocation.ts",
"src/device-face.ts", "src/device-toggle.ts", "src/marker-toggle-entity.ts", "src/activity-runtime.ts", "src/summary-runtime-loader.ts", "src/device-area-relocation.ts",
"src/ha-binding-status.ts",
"src/zigbee-topology-settings.ts", "src/zigbee-topology.ts", "src/zigbee-topology-geometry.ts", "src/zigbee-topology-runtime.ts",
"src/radar-model.ts", "src/radar-editor.ts", "src/radar-geometry.ts", "src/radar-setup.ts", "src/radar-render.ts",