Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
841bad4e79 | ||
|
|
9ebc430983 | ||
|
|
0a098c1e51 | ||
|
|
cf1c90fb5b | ||
|
|
7fb20c658b | ||
|
|
05c8e164c2 | ||
|
|
9deb1dcfd6 | ||
|
|
a5e06b0527 | ||
|
|
14f5b90c24 | ||
|
|
4de49e0db2 | ||
|
|
80cba30895 | ||
|
|
96f89717bd | ||
|
|
d8949cfffc | ||
|
|
7777b8a0a8 | ||
|
|
45aeadb38e | ||
|
|
b07ef67dcb | ||
|
|
d58a5d0dad | ||
|
|
181b61ae8e | ||
|
|
24e48935c3 | ||
|
|
6b0d7d66dd | ||
|
|
0588fa8f0a | ||
|
|
019cfa5c5a | ||
|
|
5fff48f7b1 | ||
|
|
a74a50d331 | ||
|
|
416d2fae71 | ||
|
|
08bc58cc0e | ||
|
|
c142e4f7b5 | ||
|
|
ad10a5b099 | ||
|
|
76310fc981 | ||
|
|
0f466743c3 | ||
|
|
35ef231191 | ||
|
|
2022e183a5 |
@@ -83,7 +83,9 @@ jobs:
|
||||
# снимок события об этом не знает, а исполнять отозванный запрос нельзя.
|
||||
current=$(gh issue view "$NUM" --repo "$REPO" --json labels --jq '.labels[].name')
|
||||
labels=$(printf '%s\n' "$current" | paste -sd, -)
|
||||
has() { printf '%s\n' "$current" | grep -qx -- "$1"; }
|
||||
# Не grep -q: раннее закрытие pipe даёт printf SIGPIPE, а pipefail
|
||||
# превращает найденную метку в ложный отказ (#793). Читаем до конца.
|
||||
has() { printf '%s\n' "$current" | grep -Fx -- "$1" > /dev/null; }
|
||||
BLOCKED=false; EXHAUSTED=false
|
||||
has blocked && BLOCKED=true
|
||||
has review-4 && EXHAUSTED=true
|
||||
@@ -1029,8 +1031,8 @@ jobs:
|
||||
contents: read
|
||||
issues: write
|
||||
needs: [guard, prepare]
|
||||
# #696: `ship` в рамках сливается без модели — пакетное ревью диапазона
|
||||
# перед бетой (PROCESS.md §11.7, ship-review.yml).
|
||||
# #696/#727: `ship` в рамках сливается без модели — пакетное ревью ночью;
|
||||
# перед бетой проверка покрытия и непрочитанная дельта (PROCESS.md §11.7).
|
||||
if: needs.prepare.outputs.proceed == 'true' && needs.prepare.outputs.reuse != 'true' && needs.prepare.outputs.ship != 'true'
|
||||
runs-on: ubuntu-24.04
|
||||
concurrency:
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
name: "Бета: пакетное ревью ship · тело (#716)"
|
||||
|
||||
# #696, PROCESS.md §11.7: задачи `track:ship` сливаются без ревью модели —
|
||||
# правка в механических рамках и зелёный лёгкий Validate. Их код читает одна
|
||||
# сессия модели перед бетой: все ship-задачи диапазона «прошлый тег..кандидат».
|
||||
# Гейт публикации беты (`ship-review.mjs check`) требует этот документ, если в
|
||||
# диапазоне есть ship-задачи.
|
||||
# правка в механических рамках и зелёный лёгкий Validate. Их код читается
|
||||
# пакетно ночью; перед бетой — только непрочитанные или изменённые задачи.
|
||||
# Гейт публикации беты (`ship-review.mjs check`) требует покрытие всех ship-задач
|
||||
# диапазона «прошлый тег..кандидат»; ночного документа с тем же патч-набором
|
||||
# достаточно, отдельный документ тега беты нужен не всегда (#727).
|
||||
#
|
||||
# Три job, как у release-review.yml (#551, #556): детерминированный вход,
|
||||
# модель без права записи, детерминированная публикация документа в `dev`.
|
||||
@@ -375,7 +376,11 @@ jobs:
|
||||
commit -q -F "$msg"
|
||||
git diff --name-only "origin/dev...HEAD" | node scripts/review-doc-guard.mjs
|
||||
if git push -q "https://x-access-token:$TOKEN@github.com/${{ github.repository }}" HEAD:dev 2> "$push_err"; then
|
||||
echo "### Пакетное ревью ship $TAG" >> "$GITHUB_STEP_SUMMARY"
|
||||
if [ "$MODE" = "nightly" ]; then
|
||||
echo "### Ночное пакетное ревью ship" >> "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
echo "### Предрелизное пакетное ревью ship $TAG" >> "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
echo "Задачи $ISSUES · High $high · Medium $medium · Low $low — \`$DOC\` в dev." >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "::notice::$DOC опубликован: High $high · Medium $medium · Low $low"
|
||||
exit 0
|
||||
|
||||
@@ -608,14 +608,15 @@ patch-id кандидата слияния: вердикт к работе за
|
||||
| ТЗ | строка «что меняется и чем проверить» в теле issue под `## ТЗ` | «Оценка» (§7.2) и до трёх AC в теле issue под `## ТЗ` | полное ТЗ по §7.1 |
|
||||
| Ревью ТЗ | нет | нет | да, лимит 4 цикла |
|
||||
| Локальный гейт | `npm run gate:small` | `npm run gate:small` плюс смоуки `smoke-select` и AC | §8 |
|
||||
| Код-ревью | до слияния нет; пакетное ревью диапазона перед бетой | модель: корректность и AC | документ ревью, как в §2.7 |
|
||||
| Код-ревью | до слияния нет; пакетное ревью ночью, перед бетой — проверка покрытия и непрочитанная дельта (§11.7) | модель: корректность и AC | документ ревью, как в §2.7 |
|
||||
| Лимит циклов код-ревью | — | 2 | 4 |
|
||||
|
||||
**Рамки `track:ship`** механические: дифф `src/**` не больше 30 строк, без новых
|
||||
файлов в `src/**`, без ключей i18n, без полей конфига и без Python. Выход за
|
||||
рамки переводит задачу в `track:show`. Рамки и слияние `ship` без ревью модели
|
||||
проверяет и исполняет конвейер (§10.4, #696); код `ship` читает пакетное ревью
|
||||
диапазона перед бетой (§11.7).
|
||||
ночью заранее; перед бетой проверяется покрытие и читается непрочитанная дельта
|
||||
(§11.7).
|
||||
|
||||
**Проверка риска по изменённым участкам** (#707). Рамки считают строки и файлы,
|
||||
но не видят, что именно тронуто: обработчик `pointerdown` в двенадцать строк
|
||||
@@ -675,7 +676,8 @@ patch-id кандидата слияния: вердикт к работе за
|
||||
`ux-contract`, `perf-touch`, `undocumented`. Шаг решения по вердикту ставит
|
||||
`track:ask`, снимает `track:show` и переводит задачу в `S3-spec` комментарием:
|
||||
критерий словами, документы код-ревью, бюджет. Код остаётся в ветке, полное ТЗ
|
||||
пишется в теле issue, код класса A не пушится до `S5`; бюджет код-ревью не
|
||||
пишется в теле issue; до `S5` запрещён push всей ветки, включая тесты и документы
|
||||
(§11.8), а не только кода класса A; бюджет код-ревью не
|
||||
обнуляется (§4). Подтверждённый владельцем `show` конвейер не повышает: задача
|
||||
получает `blocked` и вопрос владельцу с вариантом по умолчанию «повысить до
|
||||
`ask`». `reclassify` без критерия из списка, на `ask` или на ревью ТЗ —
|
||||
@@ -1411,7 +1413,7 @@ limit`) и своей логики трека не держит; ответ comp
|
||||
|---|---|---|---|
|
||||
| Validate на материале | лёгкий; годится завершённый push-прогон на этом SHA | лёгкий, как у `ship` | лёгкий, как у `ship` (#709) |
|
||||
| Ребейз до ревью | нет, если `git merge-tree` с `dev` чистый | нет, если чистый | да (#257) |
|
||||
| Ревью модели | нет — пакетное ревью перед бетой (§11.7) | корректность и AC; окружение — по нужде | полное |
|
||||
| Ревью модели | нет — пакетное ревью ночью; перед бетой проверка покрытия и непрочитанная дельта (§11.7) | корректность и AC; окружение — по нужде | полное |
|
||||
| Проверка кандидата слияния | ждёт push-прогон кандидата; dispatch — только если его нет | как у `ship` | как у `ship` (#709) |
|
||||
|
||||
Мутантов по диффу нет ни на одном треке (#709, §2.7). Рамки `ship`
|
||||
@@ -1715,7 +1717,8 @@ Golden, браузерные смоки, performance и полный HA-харн
|
||||
|
||||
Это исключение из правила «каждое изменение проходит код-ревью» (§5, §7.1).
|
||||
Второе такое место — трек `ship` (§5): его ревью не отменяется, а переносится
|
||||
на пакетное ревью диапазона перед бетой. Здесь же исключение относится только к
|
||||
на пакетное ревью ночью и проверку покрытия с чтением непрочитанной дельты перед
|
||||
бетой (§11.7). Здесь же исключение относится только к
|
||||
окну между `S8-merged` и выпуском.
|
||||
|
||||
### 11.5 Независимое ревью линии перед стабильным релизом
|
||||
@@ -1784,7 +1787,9 @@ action ревью пускает ровно этого бота (`allowed_bots`,
|
||||
выпуск после такого отказа, исправляют причину и получают новый совместимый
|
||||
полный зелёный прогон на том же финальном SHA либо на новом SHA кандидата.
|
||||
|
||||
### 11.7 Пакетное ревью `ship` перед бетой
|
||||
<a id="117-пакетное-ревью-ship-перед-бетой"></a>
|
||||
|
||||
### 11.7 Пакетное ревью `ship`: ночью и перед бетой
|
||||
|
||||
Решение владельца 2026-09-28, issue #696.
|
||||
|
||||
@@ -1793,7 +1798,9 @@ action ревью пускает ровно этого бота (`allowed_bots`,
|
||||
до того, как она уйдёт пользователям беты. Одна сессия на все ship-задачи
|
||||
диапазона дешевле ревью на каждую, а рамки ship держат объём малым.
|
||||
|
||||
**Шаг.** Перед публикацией беты — `ship-review.yml`
|
||||
**Шаг.** Ночью код читается заранее (режим ниже). Перед публикацией беты
|
||||
проверяется покрытие; непрочитанные или изменённые после ревью задачи читает
|
||||
`ship-review.yml`
|
||||
(`gh workflow run ship-review.yml --ref dev -f tag=vX.Y.Z-beta.N` или кнопка
|
||||
«Run workflow» в Actions). Это тонкий вызывающий файл, он лежит и в `main`:
|
||||
без этого GitHub запуск не даёт. Тело `_ship-review.yml` читается из `dev` при
|
||||
|
||||
@@ -57,7 +57,7 @@ SCHEDULED_GRACE_S = 30 * 24 * 3600
|
||||
FILES_DIR = "houseplan/files"
|
||||
ASSETS_DIR = "houseplan/assets"
|
||||
CONF_ADMIN_ONLY = "admin_only"
|
||||
VERSION = "1.79.0-beta.5"
|
||||
VERSION = "1.79.0"
|
||||
DECOR_ASSETS_API_VERSION = 1
|
||||
SUMMARY_PANEL_API_VERSION = 1
|
||||
|
||||
|
||||
@@ -1,544 +1,544 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"fingerprint": "24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29",
|
||||
"fingerprint": "ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172",
|
||||
"entry": "houseplan-card.js",
|
||||
"panelEntry": "houseplan-panel.js",
|
||||
"initialViewFiles": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-card.js"
|
||||
],
|
||||
"initialViewGzipBytes": 300064,
|
||||
"initialViewGzipBytes": 300133,
|
||||
"initialPanelFiles": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-panel.js"
|
||||
],
|
||||
"initialPanelGzipBytes": 301586,
|
||||
"initialPanelGzipBytes": 301657,
|
||||
"initialPanelOnlyFiles": [
|
||||
"houseplan-panel.js"
|
||||
],
|
||||
"initialPanelOnlyGzipBytes": 2325,
|
||||
"initialPanelOnlyGzipBytes": 2328,
|
||||
"lazyFiles": [
|
||||
"houseplan-assets/backdrop-pick-xG7bdqbY.js",
|
||||
"houseplan-assets/de-DSKqUoCg.js",
|
||||
"houseplan-assets/editor-C9FmtTs8.js",
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/fr-CqDDhzWi.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-2wGCtXGY.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-BO5rUhlm.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-DoUUbpA1.js",
|
||||
"houseplan-assets/iso-scene-render-uTVYGm6V.js",
|
||||
"houseplan-assets/led-strip-editor-B7Pkp8iy.js",
|
||||
"houseplan-assets/led-strip-geometry-BKCkc26J.js",
|
||||
"houseplan-assets/led-strip-runtime-BFsRdnaN.js",
|
||||
"houseplan-assets/live-interaction-runtime-B0JDzeVU.js",
|
||||
"houseplan-assets/moon-runtime-BNmu8sSV.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js",
|
||||
"houseplan-assets/pdf-export-F5-Roup-.js",
|
||||
"houseplan-assets/space-editor-Da26dQiU.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-DRNznxwG.js",
|
||||
"houseplan-assets/zigbee-topology-D8kIFY6J.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-DbX__eJD.js"
|
||||
"houseplan-assets/backdrop-pick-DHF8oaNH.js",
|
||||
"houseplan-assets/de-C4xCvmYr.js",
|
||||
"houseplan-assets/editor-CFo6BxSe.js",
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/fr-BJJefRIG.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-CqreRULk.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DOfjrx0M.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-BAaZNyAl.js",
|
||||
"houseplan-assets/iso-scene-render-CR7lxY-w.js",
|
||||
"houseplan-assets/led-strip-editor-kcNyoue_.js",
|
||||
"houseplan-assets/led-strip-geometry-DaLifxqY.js",
|
||||
"houseplan-assets/led-strip-runtime-Bx6qpdWk.js",
|
||||
"houseplan-assets/live-interaction-runtime-C1983oH8.js",
|
||||
"houseplan-assets/moon-runtime-Ca1mxhJl.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js",
|
||||
"houseplan-assets/pdf-export-C-Y4BSVu.js",
|
||||
"houseplan-assets/space-editor-EDOQT48H.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-CcLP5B5v.js",
|
||||
"houseplan-assets/zigbee-topology-BG_a6cIb.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-CynQ2m3K.js"
|
||||
],
|
||||
"lazyGzipBytes": 477955,
|
||||
"lazyGzipBytes": 478817,
|
||||
"lazyEditorFiles": [
|
||||
"houseplan-assets/backdrop-pick-xG7bdqbY.js",
|
||||
"houseplan-assets/editor-C9FmtTs8.js",
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-2wGCtXGY.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js",
|
||||
"houseplan-assets/zigbee-topology-D8kIFY6J.js"
|
||||
"houseplan-assets/backdrop-pick-DHF8oaNH.js",
|
||||
"houseplan-assets/editor-CFo6BxSe.js",
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-CqreRULk.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js",
|
||||
"houseplan-assets/zigbee-topology-BG_a6cIb.js"
|
||||
],
|
||||
"lazyEditorGzipBytes": 240853,
|
||||
"lazyEditorGzipBytes": 240843,
|
||||
"lazyOnboardingFiles": [
|
||||
"houseplan-assets/backdrop-pick-xG7bdqbY.js",
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-BO5rUhlm.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js"
|
||||
"houseplan-assets/backdrop-pick-DHF8oaNH.js",
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DOfjrx0M.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js"
|
||||
],
|
||||
"lazyOnboardingGzipBytes": 28403,
|
||||
"lazyOnboardingGzipBytes": 28402,
|
||||
"lazyLocaleFiles": [
|
||||
"houseplan-assets/de-DSKqUoCg.js",
|
||||
"houseplan-assets/fr-CqDDhzWi.js"
|
||||
"houseplan-assets/de-C4xCvmYr.js",
|
||||
"houseplan-assets/fr-BJJefRIG.js"
|
||||
],
|
||||
"lazyLocaleGzipBytes": 53862,
|
||||
"lazyLocaleGzipBytes": 53876,
|
||||
"lazyIsometricFiles": [
|
||||
"houseplan-assets/iso-scene-render-uTVYGm6V.js"
|
||||
"houseplan-assets/iso-scene-render-CR7lxY-w.js"
|
||||
],
|
||||
"lazyIsometricGzipBytes": 12346,
|
||||
"lazyIsometricGzipBytes": 12348,
|
||||
"lazyFurnitureArtFiles": [
|
||||
"houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js"
|
||||
"houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js"
|
||||
],
|
||||
"lazyFurnitureArtGzipBytes": 16938,
|
||||
"lazyFurnitureArtGzipBytes": 16936,
|
||||
"lazyPdfFiles": [
|
||||
"houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js",
|
||||
"houseplan-assets/pdf-export-F5-Roup-.js"
|
||||
"houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js",
|
||||
"houseplan-assets/pdf-export-C-Y4BSVu.js"
|
||||
],
|
||||
"lazyPdfGzipBytes": 126934,
|
||||
"lazyMoonFiles": [
|
||||
"houseplan-assets/moon-runtime-BNmu8sSV.js"
|
||||
"houseplan-assets/moon-runtime-Ca1mxhJl.js"
|
||||
],
|
||||
"lazyMoonGzipBytes": 11712,
|
||||
"lazyMoonGzipBytes": 11713,
|
||||
"lazyLedFiles": [
|
||||
"houseplan-assets/led-strip-field-TGhQdAfA.js",
|
||||
"houseplan-assets/led-strip-geometry-BKCkc26J.js",
|
||||
"houseplan-assets/led-strip-runtime-BFsRdnaN.js"
|
||||
"houseplan-assets/led-strip-field-ObbckCkp.js",
|
||||
"houseplan-assets/led-strip-geometry-DaLifxqY.js",
|
||||
"houseplan-assets/led-strip-runtime-Bx6qpdWk.js"
|
||||
],
|
||||
"lazyLedGzipBytes": 9511,
|
||||
"lazyLedGzipBytes": 10613,
|
||||
"lazyLedEditorFiles": [
|
||||
"houseplan-assets/led-strip-editor-B7Pkp8iy.js",
|
||||
"houseplan-assets/led-strip-geometry-BKCkc26J.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js"
|
||||
"houseplan-assets/led-strip-editor-kcNyoue_.js",
|
||||
"houseplan-assets/led-strip-geometry-DaLifxqY.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js"
|
||||
],
|
||||
"lazyLedEditorGzipBytes": 10025,
|
||||
"lazyLedEditorGzipBytes": 10707,
|
||||
"lazyNamespaceLocaleFiles": [
|
||||
"houseplan-assets/led-de-C4BpOh4s.js",
|
||||
"houseplan-assets/led-fr-CsvnerHk.js",
|
||||
"houseplan-assets/led-ru-CfjKBx2L.js",
|
||||
"houseplan-assets/settings-de-BpqGn3Xm.js",
|
||||
"houseplan-assets/settings-fr-CFDoiT4M.js",
|
||||
"houseplan-assets/settings-ru-D0wOiPGO.js",
|
||||
"houseplan-assets/support-de-CDLIF9_X.js",
|
||||
"houseplan-assets/support-fr-DKLccGlQ.js",
|
||||
"houseplan-assets/support-ru-Bvv4yeV8.js",
|
||||
"houseplan-assets/tools-de-C1v4tnST.js",
|
||||
"houseplan-assets/tools-fr-DKItIveg.js",
|
||||
"houseplan-assets/tools-ru-C8TUDL9T.js",
|
||||
"houseplan-assets/topology-de-CVO3cPMO.js",
|
||||
"houseplan-assets/topology-fr-Dk6k34nb.js",
|
||||
"houseplan-assets/topology-ru-DlSB4Cen.js"
|
||||
"houseplan-assets/led-de-yp1GefDC.js",
|
||||
"houseplan-assets/led-fr-CHODsRJm.js",
|
||||
"houseplan-assets/led-ru-D2-9Yy6l.js",
|
||||
"houseplan-assets/settings-de-ZP6F6dIF.js",
|
||||
"houseplan-assets/settings-fr-doqd_TsB.js",
|
||||
"houseplan-assets/settings-ru-CQ1W2gik.js",
|
||||
"houseplan-assets/support-de-haZQ7q8i.js",
|
||||
"houseplan-assets/support-fr-DnRyZ_Ga.js",
|
||||
"houseplan-assets/support-ru-B7zzXsuL.js",
|
||||
"houseplan-assets/tools-de-CYXPPfUH.js",
|
||||
"houseplan-assets/tools-fr-DvygRB10.js",
|
||||
"houseplan-assets/tools-ru-2xmu2w_8.js",
|
||||
"houseplan-assets/topology-de-COda8n9n.js",
|
||||
"houseplan-assets/topology-fr-BT9yhzay.js",
|
||||
"houseplan-assets/topology-ru-C-d06yvD.js"
|
||||
],
|
||||
"lazyNamespaceLocaleGzipBytes": 30050,
|
||||
"lazyNamespaceLocaleGzipBytes": 30082,
|
||||
"files": [
|
||||
{
|
||||
"path": "houseplan-assets/backdrop-pick-xG7bdqbY.js",
|
||||
"sha256": "f04e820a50dc05ded69553a09b2b61914f2fa0a0e0698697d51ca6c1a1a77b6d",
|
||||
"path": "houseplan-assets/backdrop-pick-DHF8oaNH.js",
|
||||
"sha256": "7262c67e3f88f44ee6c6a0973073d1f7c3c9f177ff8254758a21c4e3867c5b06",
|
||||
"rawBytes": 55329,
|
||||
"gzipBytes": 18026,
|
||||
"gzipBytes": 18027,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js"
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/settings-de-BpqGn3Xm.js",
|
||||
"houseplan-assets/settings-fr-CFDoiT4M.js",
|
||||
"houseplan-assets/settings-ru-D0wOiPGO.js"
|
||||
"houseplan-assets/settings-de-ZP6F6dIF.js",
|
||||
"houseplan-assets/settings-fr-doqd_TsB.js",
|
||||
"houseplan-assets/settings-ru-CQ1W2gik.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/de-DSKqUoCg.js",
|
||||
"sha256": "9618183c21f82b810c602576b95608a5f73510b1ac582eb904f8612828b8bea7",
|
||||
"path": "houseplan-assets/de-C4xCvmYr.js",
|
||||
"sha256": "e3778d3139c05e075281f96460e15a6aaafe8dd9200306b0a3799a1bc5710828",
|
||||
"rawBytes": 89873,
|
||||
"gzipBytes": 27164,
|
||||
"gzipBytes": 27169,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/editor-C9FmtTs8.js",
|
||||
"sha256": "da94ef9e376c8250a9edd8e6a37dad2dc18b60296d4c69b1ecab2a3d605a7a00",
|
||||
"path": "houseplan-assets/editor-CFo6BxSe.js",
|
||||
"sha256": "81d90441a06989a01a09c19e8c73567b80cd4d6ba52e4ea4796e096e1e5b7aa4",
|
||||
"rawBytes": 3833,
|
||||
"gzipBytes": 1583,
|
||||
"gzipBytes": 1584,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"sha256": "0096b38769596c6794edca74089b57939cdf4fde2d786fcef3c7a960c561c8ac",
|
||||
"path": "houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"sha256": "d18d3eb8bf1dd9dda6d6fdeed84edc16d3945e8a7e23b1d88e5f2edcef0a0af6",
|
||||
"rawBytes": 25605,
|
||||
"gzipBytes": 5058,
|
||||
"gzipBytes": 5059,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/fr-CqDDhzWi.js",
|
||||
"sha256": "f82a30083b967fa2b3ce2c3fed793dbb4d459c8c5077a56a08afe1c9aefbe23c",
|
||||
"path": "houseplan-assets/fr-BJJefRIG.js",
|
||||
"sha256": "4932ff13f6fdc339e9d67123105f9bd97f051395a3c3310240fc07b119d87024",
|
||||
"rawBytes": 92266,
|
||||
"gzipBytes": 26698,
|
||||
"gzipBytes": 26707,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js",
|
||||
"sha256": "f18e38dd1e1fe810a1d9c67ee43c6af8eb7d95851bae05e08abdfce9712da869",
|
||||
"path": "houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js",
|
||||
"sha256": "10a0b09e4b1513bd02ffba047d6931add1d1106c453af03f6b3c778b5bb55e15",
|
||||
"rawBytes": 52035,
|
||||
"gzipBytes": 16938,
|
||||
"gzipBytes": 16936,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"sha256": "af7c14c7295310be32bb5c9cab08fd5d5c77505369bb1d770db314346e0dbd4d",
|
||||
"rawBytes": 1056226,
|
||||
"gzipBytes": 299261,
|
||||
"path": "houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"sha256": "37c784960d53789a66988a4e4ab8a8afe88aeddb80c5f244f3d453ef86c3ba8c",
|
||||
"rawBytes": 1056405,
|
||||
"gzipBytes": 299329,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/de-DSKqUoCg.js",
|
||||
"houseplan-assets/editor-C9FmtTs8.js",
|
||||
"houseplan-assets/fr-CqDDhzWi.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-2wGCtXGY.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-BO5rUhlm.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-DoUUbpA1.js",
|
||||
"houseplan-assets/iso-scene-render-uTVYGm6V.js",
|
||||
"houseplan-assets/led-strip-editor-B7Pkp8iy.js",
|
||||
"houseplan-assets/led-strip-runtime-BFsRdnaN.js",
|
||||
"houseplan-assets/live-interaction-runtime-B0JDzeVU.js",
|
||||
"houseplan-assets/moon-runtime-BNmu8sSV.js",
|
||||
"houseplan-assets/pdf-export-F5-Roup-.js",
|
||||
"houseplan-assets/space-editor-Da26dQiU.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-DRNznxwG.js"
|
||||
"houseplan-assets/de-C4xCvmYr.js",
|
||||
"houseplan-assets/editor-CFo6BxSe.js",
|
||||
"houseplan-assets/fr-BJJefRIG.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-CqreRULk.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DOfjrx0M.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-BAaZNyAl.js",
|
||||
"houseplan-assets/iso-scene-render-CR7lxY-w.js",
|
||||
"houseplan-assets/led-strip-editor-kcNyoue_.js",
|
||||
"houseplan-assets/led-strip-runtime-Bx6qpdWk.js",
|
||||
"houseplan-assets/live-interaction-runtime-C1983oH8.js",
|
||||
"houseplan-assets/moon-runtime-Ca1mxhJl.js",
|
||||
"houseplan-assets/pdf-export-C-Y4BSVu.js",
|
||||
"houseplan-assets/space-editor-EDOQT48H.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-CcLP5B5v.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-2wGCtXGY.js",
|
||||
"sha256": "1ab3bc4e68c4c60f4e2f8b255617c228f0d0feaa13547275f0ee1dd17c10aaba",
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-CqreRULk.js",
|
||||
"sha256": "772b538d5b87265a0e88117f01034a3ccadd5f4e928ced77cb99254ab74e299a",
|
||||
"rawBytes": 695353,
|
||||
"gzipBytes": 194538,
|
||||
"gzipBytes": 194530,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/backdrop-pick-xG7bdqbY.js",
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js",
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js",
|
||||
"houseplan-assets/zigbee-topology-D8kIFY6J.js"
|
||||
"houseplan-assets/backdrop-pick-DHF8oaNH.js",
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js",
|
||||
"houseplan-assets/zigbee-topology-BG_a6cIb.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/support-de-CDLIF9_X.js",
|
||||
"houseplan-assets/support-fr-DKLccGlQ.js",
|
||||
"houseplan-assets/support-ru-Bvv4yeV8.js",
|
||||
"houseplan-assets/tools-de-C1v4tnST.js",
|
||||
"houseplan-assets/tools-fr-DKItIveg.js",
|
||||
"houseplan-assets/tools-ru-C8TUDL9T.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-DbX__eJD.js"
|
||||
"houseplan-assets/support-de-haZQ7q8i.js",
|
||||
"houseplan-assets/support-fr-DnRyZ_Ga.js",
|
||||
"houseplan-assets/support-ru-B7zzXsuL.js",
|
||||
"houseplan-assets/tools-de-CYXPPfUH.js",
|
||||
"houseplan-assets/tools-fr-DvygRB10.js",
|
||||
"houseplan-assets/tools-ru-2xmu2w_8.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-CynQ2m3K.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-BO5rUhlm.js",
|
||||
"sha256": "95f00d356328a64eafb3812a68f9ac8e5be3820d132d591fccf1f4b1ccacdcdf",
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-DOfjrx0M.js",
|
||||
"sha256": "242ae570916ec2a000a8a0079a948826faeb45c0968cc1006e9670f9d3c4088b",
|
||||
"rawBytes": 15886,
|
||||
"gzipBytes": 4909,
|
||||
"gzipBytes": 4907,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/backdrop-pick-xG7bdqbY.js",
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js"
|
||||
"houseplan-assets/backdrop-pick-DHF8oaNH.js",
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/hp-zigbee-topology-overlay-DoUUbpA1.js",
|
||||
"sha256": "49841cdfc0566b3e88c30069ce5a58bcde76f63f388de53c44b9f95947b55880",
|
||||
"path": "houseplan-assets/hp-zigbee-topology-overlay-BAaZNyAl.js",
|
||||
"sha256": "8cb7c3f010cf7ef792a676bfd994c4498f31222dd12ded79e3e2f37dfae4f52e",
|
||||
"rawBytes": 10958,
|
||||
"gzipBytes": 3766,
|
||||
"gzipBytes": 3768,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js",
|
||||
"houseplan-assets/zigbee-topology-D8kIFY6J.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-DbX__eJD.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js",
|
||||
"houseplan-assets/zigbee-topology-BG_a6cIb.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-CynQ2m3K.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/iso-scene-render-uTVYGm6V.js",
|
||||
"sha256": "dee120646d9ce2e35ec6595a4bda0d625aae09d7ae43c494548613ef9be19d85",
|
||||
"path": "houseplan-assets/iso-scene-render-CR7lxY-w.js",
|
||||
"sha256": "1986dd43c8b76580f893c1629d494f7747f02aa8d544baff20d56fe10b470bb5",
|
||||
"rawBytes": 36700,
|
||||
"gzipBytes": 12346,
|
||||
"gzipBytes": 12348,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/led-de-C4BpOh4s.js",
|
||||
"sha256": "6a140ef9878edec937145205f21317f50141470b9e1098c1fba695ce15af2630",
|
||||
"path": "houseplan-assets/led-de-yp1GefDC.js",
|
||||
"sha256": "eb9ce8bb536ac20e11cf4fe925d68f28b1ba8a5ea02c61abf9c3bb939de0a4c4",
|
||||
"rawBytes": 1907,
|
||||
"gzipBytes": 918,
|
||||
"gzipBytes": 921,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/led-fr-CsvnerHk.js",
|
||||
"sha256": "0f2afb6c3ef7117afe0bf3b892652197fa29d11f685d18d24a4e15fece28d394",
|
||||
"path": "houseplan-assets/led-fr-CHODsRJm.js",
|
||||
"sha256": "adb428bd63b6157ef0a5d749badf94e95204c9c3b83e5f3022accf65d304435e",
|
||||
"rawBytes": 1886,
|
||||
"gzipBytes": 905,
|
||||
"gzipBytes": 906,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/led-ru-CfjKBx2L.js",
|
||||
"sha256": "e0c070ec3b7bb95a26b4cc4506d2293a9ebf3067e718d9abaaf84ad2c10bbc3b",
|
||||
"path": "houseplan-assets/led-ru-D2-9Yy6l.js",
|
||||
"sha256": "bc5575139b30d6e93e07673f47865c332e71011796929a52043645a904d6df31",
|
||||
"rawBytes": 2525,
|
||||
"gzipBytes": 1105,
|
||||
"gzipBytes": 1106,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/led-strip-editor-B7Pkp8iy.js",
|
||||
"sha256": "8755690f52b2a8e74909165b4dd074a01376c9b2e752940a1f010646f864cc2b",
|
||||
"path": "houseplan-assets/led-strip-editor-kcNyoue_.js",
|
||||
"sha256": "dac9fdcaae6444caa1773a07384155cc92b916d2ebf3498e1dfc1b3525a84753",
|
||||
"rawBytes": 21704,
|
||||
"gzipBytes": 7378,
|
||||
"gzipBytes": 7381,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/led-strip-geometry-BKCkc26J.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/led-strip-geometry-DaLifxqY.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/led-de-C4BpOh4s.js",
|
||||
"houseplan-assets/led-fr-CsvnerHk.js",
|
||||
"houseplan-assets/led-ru-CfjKBx2L.js"
|
||||
"houseplan-assets/led-de-yp1GefDC.js",
|
||||
"houseplan-assets/led-fr-CHODsRJm.js",
|
||||
"houseplan-assets/led-ru-D2-9Yy6l.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/led-strip-field-TGhQdAfA.js",
|
||||
"sha256": "1a6721643f02390b945b26bff3803c6af8ef801fb43ca450a14b6bf2f13b1819",
|
||||
"rawBytes": 7231,
|
||||
"gzipBytes": 3057,
|
||||
"path": "houseplan-assets/led-strip-field-ObbckCkp.js",
|
||||
"sha256": "b85d8de01906a7e70f28baff534cf1a211216c99b72005b6d77a0d19cbb5ce8f",
|
||||
"rawBytes": 7920,
|
||||
"gzipBytes": 3321,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/led-strip-geometry-BKCkc26J.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/led-strip-geometry-DaLifxqY.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/led-strip-geometry-BKCkc26J.js",
|
||||
"sha256": "9aa0a021e4f5a7faf186def62ce22f037bd5659e90b31078ebe43f41a56b3e59",
|
||||
"rawBytes": 5218,
|
||||
"gzipBytes": 2237,
|
||||
"path": "houseplan-assets/led-strip-geometry-DaLifxqY.js",
|
||||
"sha256": "2dfbe1509da809008c9a525a7ed02226c026aaa5c0dafcb9f3dc1c44151ed63e",
|
||||
"rawBytes": 7055,
|
||||
"gzipBytes": 2917,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/led-strip-runtime-BFsRdnaN.js",
|
||||
"sha256": "2e62eef6e40dfe65f3d772775d3282d8070d939445a14bf19ad23ffb432fda56",
|
||||
"rawBytes": 10720,
|
||||
"gzipBytes": 4217,
|
||||
"path": "houseplan-assets/led-strip-runtime-Bx6qpdWk.js",
|
||||
"sha256": "6fd2d3578e550dfbe8c38308923e21f9fddeb469f64e8fcf295dc292949ddf32",
|
||||
"rawBytes": 11179,
|
||||
"gzipBytes": 4375,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/led-strip-geometry-BKCkc26J.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/led-strip-geometry-DaLifxqY.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/led-strip-field-TGhQdAfA.js"
|
||||
"houseplan-assets/led-strip-field-ObbckCkp.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/live-interaction-runtime-B0JDzeVU.js",
|
||||
"sha256": "19e27b13aa16d5de342b3403ba55e7466690db80fc35a92dfdb5b3b6ac45c2c6",
|
||||
"path": "houseplan-assets/live-interaction-runtime-C1983oH8.js",
|
||||
"sha256": "04be552ac07f99c03713f162ce0b6129c8491dfd89da9cd8e07cd71866fe309c",
|
||||
"rawBytes": 8370,
|
||||
"gzipBytes": 3279,
|
||||
"gzipBytes": 3281,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/moon-runtime-BNmu8sSV.js",
|
||||
"sha256": "36e73d61c9d8121c4d38d6e2842640b9d10236abb28e2f1b870ccafa3d17b0b6",
|
||||
"path": "houseplan-assets/moon-runtime-Ca1mxhJl.js",
|
||||
"sha256": "97b9f55cb8e2915acb5eaa260f21e2103bc08f0e654c1c0b03c3000604319730",
|
||||
"rawBytes": 30292,
|
||||
"gzipBytes": 11712,
|
||||
"gzipBytes": 11713,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/namespace-language-Bj-cxtbx.js",
|
||||
"sha256": "f335a736ca72fe7d1cded3ec57716c8e1e6d6b4f7609e9c367dc8fe5e8362459",
|
||||
"path": "houseplan-assets/namespace-language-D86tJUeD.js",
|
||||
"sha256": "012aa30cbf8bbbb2eff6b7ef05970ff90fb2b4b576910578e3527b75e68ffda6",
|
||||
"rawBytes": 675,
|
||||
"gzipBytes": 410,
|
||||
"gzipBytes": 409,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/pdf-export-F5-Roup-.js",
|
||||
"sha256": "10a42644f96822823e8588c1b0009615a3881e01981243840a1eddfcb009636c",
|
||||
"path": "houseplan-assets/pdf-export-C-Y4BSVu.js",
|
||||
"sha256": "fba700bc14c66c695f1b3de3e82abdf5fb6b14b70773c3d0f0e185dcf2989491",
|
||||
"rawBytes": 236714,
|
||||
"gzipBytes": 109996,
|
||||
"gzipBytes": 109998,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js",
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/settings-de-BpqGn3Xm.js",
|
||||
"sha256": "1cecf30cead9359ddc80868b6210e0b6f21f105bf89f1c75e87d161f8db21f2f",
|
||||
"path": "houseplan-assets/settings-de-ZP6F6dIF.js",
|
||||
"sha256": "12dd3028d2de2e4fc3bfcf2299e45e253075ca23b95235436ef1cf9156738a3e",
|
||||
"rawBytes": 8886,
|
||||
"gzipBytes": 3407,
|
||||
"gzipBytes": 3411,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/settings-fr-CFDoiT4M.js",
|
||||
"sha256": "99736640c26f4681230c0aa8f697a9419a2c81b96fce19e963c8d0dfaaaaad1a",
|
||||
"path": "houseplan-assets/settings-fr-doqd_TsB.js",
|
||||
"sha256": "0c1f01b69c2b1704ddd29e99dd7609ad679655b5241b3e2b8921057198005ff3",
|
||||
"rawBytes": 9297,
|
||||
"gzipBytes": 3381,
|
||||
"gzipBytes": 3385,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/settings-ru-D0wOiPGO.js",
|
||||
"sha256": "2bd560d5eeb8f430574a59a3c827f52eede08e39218149ed56a9b44bf6ae6e8b",
|
||||
"path": "houseplan-assets/settings-ru-CQ1W2gik.js",
|
||||
"sha256": "bad5c925aac84429d0831a1036ea3162ead8d4431db35ea577b0e806c8fbdbf1",
|
||||
"rawBytes": 12284,
|
||||
"gzipBytes": 3898,
|
||||
"gzipBytes": 3900,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/space-editor-Da26dQiU.js",
|
||||
"sha256": "013609e7975871868fa3f6955095bae588aaf57a4638763c21b14ce3de19abb5",
|
||||
"path": "houseplan-assets/space-editor-EDOQT48H.js",
|
||||
"sha256": "47339ab7329ffd1a0cbd21980c1cf2a2041d7a514ed5992e73a07abdce6fb764",
|
||||
"rawBytes": 2619,
|
||||
"gzipBytes": 1126,
|
||||
"gzipBytes": 1130,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/summary-panel-editor-Dy-Z4Phc.js",
|
||||
"sha256": "f8f1a903c3e44a7dab27d0dda7559d9c6cbd586a338891fb4e11ebc5ab1be378",
|
||||
"path": "houseplan-assets/summary-panel-editor-BZnGwuVh.js",
|
||||
"sha256": "516e3462d3896a9ca782ee0f2f3266a4da6e6b6664e92a619a23b8991e600795",
|
||||
"rawBytes": 14862,
|
||||
"gzipBytes": 3426,
|
||||
"gzipBytes": 3427,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-DRNznxwG.js"
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-CcLP5B5v.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/summary-panel-metrics-DMnGGtKb.js",
|
||||
"sha256": "4ea585b05274db86655e84bccbd9378b7f3f606407d37d0da22999636f3621da",
|
||||
"path": "houseplan-assets/summary-panel-metrics-D_ZXPJxz.js",
|
||||
"sha256": "3be26833b041416cef359e4c7086a0b0e6788680dda8f1583ae76d9256f944fa",
|
||||
"rawBytes": 2701,
|
||||
"gzipBytes": 1344,
|
||||
"gzipBytes": 1348,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/summary-panel-runtime-loaded-DRNznxwG.js",
|
||||
"sha256": "07f42710ef9c27aa47095c0be5ea57657fbfe8ec71afe12059280a82af7115a9",
|
||||
"path": "houseplan-assets/summary-panel-runtime-loaded-CcLP5B5v.js",
|
||||
"sha256": "7db94205b54996355041487465d6bade0530e5ac0d13a0eb9b7645086171ce58",
|
||||
"rawBytes": 73476,
|
||||
"gzipBytes": 20485,
|
||||
"gzipBytes": 20491,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/summary-panel-editor-Dy-Z4Phc.js",
|
||||
"houseplan-assets/summary-panel-metrics-DMnGGtKb.js"
|
||||
"houseplan-assets/summary-panel-editor-BZnGwuVh.js",
|
||||
"houseplan-assets/summary-panel-metrics-D_ZXPJxz.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/support-de-CDLIF9_X.js",
|
||||
"sha256": "eb503ecf5b65bdd715e848e11ba3993a130aa3cd4cc8fd5a23ed4e0b578a15c2",
|
||||
"path": "houseplan-assets/support-de-haZQ7q8i.js",
|
||||
"sha256": "f377e9d552978c850f069eeb05b20c7572f81c7a8c7b2d60838a7907f1c2b3ef",
|
||||
"rawBytes": 6167,
|
||||
"gzipBytes": 2397,
|
||||
"gzipBytes": 2400,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/support-fr-DKLccGlQ.js",
|
||||
"sha256": "dc7049414bfc596a22bcf1f13ab4831e93137406a1ed73bc7ba8ef15f200bc63",
|
||||
"path": "houseplan-assets/support-fr-DnRyZ_Ga.js",
|
||||
"sha256": "bb66f839f3b63a74212dc83d273db535dd4597e5f43c5c1b35f0e1fac401e424",
|
||||
"rawBytes": 6333,
|
||||
"gzipBytes": 2430,
|
||||
"gzipBytes": 2433,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/support-ru-Bvv4yeV8.js",
|
||||
"sha256": "90c3a0895993f4c302c40ceeec324685616aacff980b44540dfe4cd420116310",
|
||||
"path": "houseplan-assets/support-ru-B7zzXsuL.js",
|
||||
"sha256": "63ec59059f3a10c994e4292c4c2b125bb7b2916326dbae27f9975c14be008822",
|
||||
"rawBytes": 8376,
|
||||
"gzipBytes": 2875,
|
||||
"gzipBytes": 2878,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/tools-de-C1v4tnST.js",
|
||||
"sha256": "3fd6e88cba32f2f473b402e3feba44c1ab26792043ef992f0b74fbcefe19cfc7",
|
||||
"path": "houseplan-assets/tools-de-CYXPPfUH.js",
|
||||
"sha256": "411c98cf876e42d7255882d0faa486602453f871d443b0c2b59c22e3b2552efb",
|
||||
"rawBytes": 4130,
|
||||
"gzipBytes": 1375,
|
||||
"gzipBytes": 1378,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/tools-fr-DKItIveg.js",
|
||||
"sha256": "3028cd9e47e0e9495aefbff17b7aaf0de9ca45efdcac20747dcb13ab8a4e4ea7",
|
||||
"path": "houseplan-assets/tools-fr-DvygRB10.js",
|
||||
"sha256": "f7d35402d5f3504baa8c00dc8bd5170487535c1b87f9a8367a28e332cda92bf2",
|
||||
"rawBytes": 4136,
|
||||
"gzipBytes": 1358,
|
||||
"gzipBytes": 1360,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/tools-ru-C8TUDL9T.js",
|
||||
"sha256": "9cbef8d3edffaf6f3eee56da0a078b57bdd9249e45d7c73cca4d8e3a22801626",
|
||||
"path": "houseplan-assets/tools-ru-2xmu2w_8.js",
|
||||
"sha256": "14e6c4c22d615293903fc4f25d831f0af042b4e48234d4f2e114ef7d8817d57a",
|
||||
"rawBytes": 5633,
|
||||
"gzipBytes": 1695,
|
||||
"gzipBytes": 1697,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/topology-de-CVO3cPMO.js",
|
||||
"sha256": "e184d49bce8fa684d4f76f74acc02f1c1f6a60a0f5bc4d5c865b2af7af6af053",
|
||||
"path": "houseplan-assets/topology-de-COda8n9n.js",
|
||||
"sha256": "88ff4a8cccc94e3eba040af628d9a85db664cbf20586f95704e0823a6215c1f9",
|
||||
"rawBytes": 2698,
|
||||
"gzipBytes": 1337,
|
||||
"gzipBytes": 1336,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/topology-fr-Dk6k34nb.js",
|
||||
"sha256": "ad8b6fc06d7bfb65b285c416d5143f8582196e7bb68e4b924d2b7243a8f3a24b",
|
||||
"path": "houseplan-assets/topology-fr-BT9yhzay.js",
|
||||
"sha256": "60cb68c5c9086ec5f4901627a6baa3e3c8cdfda430b0977622b260984122fcfa",
|
||||
"rawBytes": 2718,
|
||||
"gzipBytes": 1354,
|
||||
"gzipBytes": 1356,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/topology-ru-DlSB4Cen.js",
|
||||
"sha256": "5564fcbd3b2d7b82f9212e81e9cf211235934c5dfe03f83f181907e7bb1fc2f7",
|
||||
"path": "houseplan-assets/topology-ru-C-d06yvD.js",
|
||||
"sha256": "ca9a68f252b8275c954ae7ff7ea56d07a8ea5cf14f18eaac285fd0c63a7bad8f",
|
||||
"rawBytes": 3669,
|
||||
"gzipBytes": 1615,
|
||||
"isEntry": false,
|
||||
@@ -546,53 +546,53 @@
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/zigbee-topology-D8kIFY6J.js",
|
||||
"sha256": "9cfaee356879c62621afd7d4759a652db6a4cda08c10a04956d131a9434a4cd1",
|
||||
"path": "houseplan-assets/zigbee-topology-BG_a6cIb.js",
|
||||
"sha256": "98d8e54ec59e2d1810b124a0a0c6c2f528c112ab17d12bc3c0a129e13c456c72",
|
||||
"rawBytes": 10958,
|
||||
"gzipBytes": 4300,
|
||||
"gzipBytes": 4298,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/topology-de-CVO3cPMO.js",
|
||||
"houseplan-assets/topology-fr-Dk6k34nb.js",
|
||||
"houseplan-assets/topology-ru-DlSB4Cen.js"
|
||||
"houseplan-assets/topology-de-COda8n9n.js",
|
||||
"houseplan-assets/topology-fr-BT9yhzay.js",
|
||||
"houseplan-assets/topology-ru-C-d06yvD.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/zigbee-topology-runtime-DbX__eJD.js",
|
||||
"sha256": "24396700d8dca1bfefb32efc2a4391a893763cf756210834e8cddaf108bfc4dc",
|
||||
"path": "houseplan-assets/zigbee-topology-runtime-CynQ2m3K.js",
|
||||
"sha256": "966ef9fa87888f0ed561d1061b36f0192c27700f040305ae038fe9509c7a7e33",
|
||||
"rawBytes": 3983,
|
||||
"gzipBytes": 1789,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js",
|
||||
"houseplan-assets/zigbee-topology-D8kIFY6J.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js",
|
||||
"houseplan-assets/zigbee-topology-BG_a6cIb.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-card.js",
|
||||
"sha256": "e6cbcbf961cb6c0384cf21f5877f34ec1c6507774b4751c839c386396127a9ec",
|
||||
"sha256": "80338f4a64b84431f95438482aeabe1cc6b0f399ccb0c600b318c0f6e6cccc38",
|
||||
"rawBytes": 1205,
|
||||
"gzipBytes": 803,
|
||||
"gzipBytes": 804,
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-panel.js",
|
||||
"sha256": "f2cab62ce7f8131965eb51673f25963e7cff04392e1b62854cb991fc38f60032",
|
||||
"sha256": "ce8c313af85073d1c54ccc74e2111c1f14c5d8e3e8539bc59ec436d6d3e0d574",
|
||||
"rawBytes": 6165,
|
||||
"gzipBytes": 2325,
|
||||
"gzipBytes": 2328,
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";import{aG as e,l as o,aJ as t,eD as s,A as a,eE as i,eF as l,eG as n,E as r,c}from"./houseplan-card-BWxNZe40.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__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";import{aG as e,l as o,aJ as t,eE as s,A as a,eF as i,eG as l,eH as n,E as r,c}from"./houseplan-card-BOdiRDFR.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},_=this._schema,f=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),u=e=>c`<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._formData}
|
||||
.schema=${e}
|
||||
.computeLabel=${e=>h[e.name]||e.name}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>`;return c`
|
||||
${d(f.slice(0,3))}
|
||||
${_?c`<div class="default-floor-error" role="alert"
|
||||
${u(_.slice(0,3))}
|
||||
${f?c`<div class="default-floor-error" role="alert"
|
||||
style="color:var(--error-color,#db4437);margin:-4px 0 12px;overflow-wrap:anywhere">
|
||||
${t(s,"editor.default_floor_missing",{id:_})}
|
||||
${t(s,"editor.default_floor_missing",{id:f})}
|
||||
</div>`:a}
|
||||
${d(f.slice(3))}
|
||||
${u(_.slice(3))}
|
||||
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h);
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";var e={"led.tool":"LED-Streifen","led.hint":"Zeichnen Sie den Streifen Punkt für Punkt entlang der Wandflächen oder über den Boden; er rastet am Raster und an Wandflächen ein. Danach wählen Sie das Gerät, das er darstellt.","led.start":"Klicken, um den Streifen zu beginnen","led.start_touch":"Tippen, um den Streifen zu beginnen","led.points":"Punkte: {n} · Umschalt — 45°-Schritte · Esc — fertig · Strg+Z — Punkt entfernen","led.points_touch":"Punkte: {n} · Werkzeug wechseln zum Beenden","led.finish":"Fertig","led.stopped":"Der Streifen stößt an eine Wand","led.unbound":"Streifen ist mit keinem Gerät verbunden","led.bind":"Gerät verbinden","led.change":"Gerät wechseln","led.later":"Später","led.unbind":"Trennen","led.delete":"Streifen löschen","led.settings":"Geräteeinstellungen","led.type_note":"LED-Streifen: Form im Geräte-Editor auf dem Plan bearbeiten","led.show_strip":"Als LED-Streifen zeigen","led.show_icon":"Als Symbol zeigen","led.cancel":"Abbrechen","led.taken":"Bereits mit einem anderen LED-Streifen verbunden","led.space":"Zuerst den LED-Streifen lösen: Er gehört zu seinem Bereich","led.limit_points":"Höchstens 50 Punkte pro Streifen","led.limit_strips":"Höchstens 50 Streifen pro Bereich","led.pick_title":"Gerät für den Streifen wählen","led.pick_new":"Neues Gerät…","led.pick_empty":"Auf diesem Plan gibt es noch keine freien Geräte.","led.saved":"LED-Streifen gespeichert","led.save_failed":"Streifen nicht gespeichert: {err}","led.history_draw":"LED-Streifen","led.history_shape":"Form des LED-Streifens","led.history_view":"Darstellung des Geräts","led.optimize_walls":"Streifen durch Wände: {n} ({spaces})"};const n="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";export{e as dictionary,n as fingerprint};
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";var e={"led.tool":"LED-Streifen","led.hint":"Zeichnen Sie den Streifen Punkt für Punkt entlang der Wandflächen oder über den Boden; er rastet am Raster und an Wandflächen ein. Danach wählen Sie das Gerät, das er darstellt.","led.start":"Klicken, um den Streifen zu beginnen","led.start_touch":"Tippen, um den Streifen zu beginnen","led.points":"Punkte: {n} · Umschalt — 45°-Schritte · Esc — fertig · Strg+Z — Punkt entfernen","led.points_touch":"Punkte: {n} · Werkzeug wechseln zum Beenden","led.finish":"Fertig","led.stopped":"Der Streifen stößt an eine Wand","led.unbound":"Streifen ist mit keinem Gerät verbunden","led.bind":"Gerät verbinden","led.change":"Gerät wechseln","led.later":"Später","led.unbind":"Trennen","led.delete":"Streifen löschen","led.settings":"Geräteeinstellungen","led.type_note":"LED-Streifen: Form im Geräte-Editor auf dem Plan bearbeiten","led.show_strip":"Als LED-Streifen zeigen","led.show_icon":"Als Symbol zeigen","led.cancel":"Abbrechen","led.taken":"Bereits mit einem anderen LED-Streifen verbunden","led.space":"Zuerst den LED-Streifen lösen: Er gehört zu seinem Bereich","led.limit_points":"Höchstens 50 Punkte pro Streifen","led.limit_strips":"Höchstens 50 Streifen pro Bereich","led.pick_title":"Gerät für den Streifen wählen","led.pick_new":"Neues Gerät…","led.pick_empty":"Auf diesem Plan gibt es noch keine freien Geräte.","led.saved":"LED-Streifen gespeichert","led.save_failed":"Streifen nicht gespeichert: {err}","led.history_draw":"LED-Streifen","led.history_shape":"Form des LED-Streifens","led.history_view":"Darstellung des Geräts","led.optimize_walls":"Streifen durch Wände: {n} ({spaces})"};const n="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";export{e as dictionary,n as fingerprint};
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";var e={"led.tool":"Ruban LED","led.hint":"Tracez le ruban point par point le long des faces des murs ou sur le sol ; il s’aimante à la grille et aux faces des murs. Ensuite, choisissez l’appareil qu’il représente.","led.start":"Cliquez pour commencer le ruban","led.start_touch":"Touchez pour commencer le ruban","led.points":"Points : {n} · Maj — pas de 45° · Échap — terminer · Ctrl+Z — retirer le point","led.points_touch":"Points : {n} · changez d’outil pour terminer","led.finish":"Terminer","led.stopped":"Le ruban bute contre un mur","led.unbound":"Ruban non lié à un appareil","led.bind":"Lier un appareil","led.change":"Changer d’appareil","led.later":"Plus tard","led.unbind":"Délier","led.delete":"Supprimer le ruban","led.settings":"Réglages de l’appareil","led.type_note":"Ruban LED : modifiez sa forme sur le plan dans l’éditeur d’appareils","led.show_strip":"Afficher en ruban LED","led.show_icon":"Afficher en icône","led.cancel":"Annuler","led.taken":"Déjà lié à un autre ruban LED","led.space":"Détachez d’abord le ruban LED : il appartient à son espace","led.limit_points":"50 points au maximum par ruban","led.limit_strips":"50 rubans au maximum par espace","led.pick_title":"Choisissez l’appareil du ruban","led.pick_new":"Nouvel appareil…","led.pick_empty":"Aucun appareil libre sur ce plan pour l’instant.","led.saved":"Ruban LED enregistré","led.save_failed":"Ruban non enregistré : {err}","led.history_draw":"Ruban LED","led.history_shape":"Forme du ruban LED","led.history_view":"Représentation de l’appareil","led.optimize_walls":"Rubans traversant des murs : {n} ({spaces})"};const a="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";export{e as dictionary,a as fingerprint};
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";var e={"led.tool":"Ruban LED","led.hint":"Tracez le ruban point par point le long des faces des murs ou sur le sol ; il s’aimante à la grille et aux faces des murs. Ensuite, choisissez l’appareil qu’il représente.","led.start":"Cliquez pour commencer le ruban","led.start_touch":"Touchez pour commencer le ruban","led.points":"Points : {n} · Maj — pas de 45° · Échap — terminer · Ctrl+Z — retirer le point","led.points_touch":"Points : {n} · changez d’outil pour terminer","led.finish":"Terminer","led.stopped":"Le ruban bute contre un mur","led.unbound":"Ruban non lié à un appareil","led.bind":"Lier un appareil","led.change":"Changer d’appareil","led.later":"Plus tard","led.unbind":"Délier","led.delete":"Supprimer le ruban","led.settings":"Réglages de l’appareil","led.type_note":"Ruban LED : modifiez sa forme sur le plan dans l’éditeur d’appareils","led.show_strip":"Afficher en ruban LED","led.show_icon":"Afficher en icône","led.cancel":"Annuler","led.taken":"Déjà lié à un autre ruban LED","led.space":"Détachez d’abord le ruban LED : il appartient à son espace","led.limit_points":"50 points au maximum par ruban","led.limit_strips":"50 rubans au maximum par espace","led.pick_title":"Choisissez l’appareil du ruban","led.pick_new":"Nouvel appareil…","led.pick_empty":"Aucun appareil libre sur ce plan pour l’instant.","led.saved":"Ruban LED enregistré","led.save_failed":"Ruban non enregistré : {err}","led.history_draw":"Ruban LED","led.history_shape":"Forme du ruban LED","led.history_view":"Représentation de l’appareil","led.optimize_walls":"Rubans traversant des murs : {n} ({spaces})"};const a="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";export{e as dictionary,a as fingerprint};
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";var e={"led.tool":"LED-лента","led.hint":"Нарисуйте ленту по точкам вдоль граней стен или по полу — она прилипает к сетке и граням. После завершения выберите устройство, которое она показывает.","led.start":"Нажмите, чтобы начать ленту","led.start_touch":"Коснитесь, чтобы начать ленту","led.points":"Точек: {n} · Shift — шаг 45° · Esc — завершить · Ctrl+Z — убрать точку","led.points_touch":"Точек: {n} · смените инструмент, чтобы завершить","led.finish":"Завершить","led.stopped":"Лента упёрлась в стену","led.unbound":"Лента не привязана к устройству","led.bind":"Привязать устройство","led.change":"Сменить устройство","led.later":"Позже","led.unbind":"Отвязать","led.delete":"Удалить ленту","led.settings":"Настройки устройства","led.type_note":"LED-лента: форма редактируется на плане в редакторе устройств","led.show_strip":"Показывать LED-лентой","led.show_icon":"Показывать значком","led.cancel":"Отмена","led.taken":"Уже привязано к другой LED-ленте","led.space":"Сначала отвяжите LED-ленту: она принадлежит своему пространству","led.limit_points":"Не более 50 точек в ленте","led.limit_strips":"Не более 50 лент в пространстве","led.pick_title":"Выберите устройство для ленты","led.pick_new":"Новое устройство…","led.pick_empty":"На этом плане пока нет свободных устройств.","led.saved":"LED-лента сохранена","led.save_failed":"Лента не сохранена: {err}","led.history_draw":"LED-лента","led.history_shape":"Форма LED-ленты","led.history_view":"Вид устройства","led.optimize_walls":"Лент, проходящих сквозь стены: {n} ({spaces})"};const d="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";export{e as dictionary,d as fingerprint};
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";var e={"led.tool":"LED-лента","led.hint":"Нарисуйте ленту по точкам вдоль граней стен или по полу — она прилипает к сетке и граням. После завершения выберите устройство, которое она показывает.","led.start":"Нажмите, чтобы начать ленту","led.start_touch":"Коснитесь, чтобы начать ленту","led.points":"Точек: {n} · Shift — шаг 45° · Esc — завершить · Ctrl+Z — убрать точку","led.points_touch":"Точек: {n} · смените инструмент, чтобы завершить","led.finish":"Завершить","led.stopped":"Лента упёрлась в стену","led.unbound":"Лента не привязана к устройству","led.bind":"Привязать устройство","led.change":"Сменить устройство","led.later":"Позже","led.unbind":"Отвязать","led.delete":"Удалить ленту","led.settings":"Настройки устройства","led.type_note":"LED-лента: форма редактируется на плане в редакторе устройств","led.show_strip":"Показывать LED-лентой","led.show_icon":"Показывать значком","led.cancel":"Отмена","led.taken":"Уже привязано к другой LED-ленте","led.space":"Сначала отвяжите LED-ленту: она принадлежит своему пространству","led.limit_points":"Не более 50 точек в ленте","led.limit_strips":"Не более 50 лент в пространстве","led.pick_title":"Выберите устройство для ленты","led.pick_new":"Новое устройство…","led.pick_empty":"На этом плане пока нет свободных устройств.","led.saved":"LED-лента сохранена","led.save_failed":"Лента не сохранена: {err}","led.history_draw":"LED-лента","led.history_shape":"Форма LED-ленты","led.history_view":"Вид устройства","led.optimize_walls":"Лент, проходящих сквозь стены: {n} ({spaces})"};const l="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";export{e as dictionary,l as fingerprint};
|
||||
@@ -1,47 +0,0 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";import{et as e,eB as t,c6 as o,eI as s,eJ as i,ad as n,ed as a,O as r,eK as l,az as d,ee as c,aL as p,eL as u,eM as h,eN as f,A as m,eO as v}from"./houseplan-card-BWxNZe40.js";import{L as g,v as w,s as $,a as _,p as y,b as k,c as b,d as C,e as P}from"./led-strip-geometry-BKCkc26J.js";const M="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";let F,L=null,U=null;function x(e,t){return L||U||F===e||""===F||(U=(void 0===F?import("./led-strip-field-TGhQdAfA.js"):import(new URL(`./led-strip-field-TGhQdAfA.js?${Date.now()}`,import.meta.url).href)).then(e=>{e.LED_FIELD_FINGERPRINT===s?L=e:F=""},()=>{F=e}).finally(()=>{U=null})),L||U?.then(()=>L&&t()),L}const D=e=>e.some(e=>e.glow&&"on"===e.state),R=e=>e.map(e=>[e[0],e[1]]),E="#383838";function A(s,i){if(!s)return null;const n=Array.isArray(s.masonryGeometry)?s.masonryGeometry:[],a=[...e(s.masonryGeometry),...s.opaqueBodies],r=[];for(const e of a)for(let t=0;t<e.length;t++){const o=e[t],s=e[(t+1)%e.length];o&&s&&r.push({a:[o[0],o[1]],b:[s[0],s[1]]})}const l=25,d=new Map;r.forEach((e,t)=>{const o=Math.floor(Math.min(e.a[0],e.b[0])/l),s=Math.floor(Math.max(e.a[0],e.b[0])/l),i=Math.floor(Math.min(e.a[1],e.b[1])/l),n=Math.floor(Math.max(e.a[1],e.b[1])/l);for(let e=o;e<=s;e++)for(let o=i;o<=n;o++){const s=`${e},${o}`,i=d.get(s);i?i.push(t):d.set(s,[t])}});const c=e=>{let t=1/0,o=1/0,s=-1/0,i=-1/0;for(const n of e)t=Math.min(t,n[0]),o=Math.min(o,n[1]),s=Math.max(s,n[0]),i=Math.max(i,n[1]);return[t,o,s,i]},p=(e,t)=>e[0]>=t[0]&&e[0]<=t[2]&&e[1]>=t[1]&&e[1]<=t[3],u=n.filter(e=>e?.[0]?.length).map(e=>({box:c(e[0]),poly:e})),h=s.opaqueBodies.map(e=>({box:c(e),body:e}));return{faces:r,inside:e=>u.some(({box:o,poly:s})=>p(e,o)&&t([e[0],e[1]],[s]))||h.some(({box:t,body:s})=>p(e,t)&&o([e[0],e[1]],s)),epsilon:i,near:e=>{const t=new Set;for(let o=Math.floor(e[0]/l);o<=Math.floor(e[2]/l);o++)for(let s=Math.floor(e[1]/l);s<=Math.floor(e[3]/l);s++)for(const e of d.get(`${o},${s}`)||[])t.add(e);return[...t].map(e=>r[e])}}}function I(e){const t=e.candidate?.sourceEid||e.device.primary||"",o=t?e.hass?.states?.[t]?.state:void 0,s="unavailable"===o||"unknown"===o||!!t&&!!e.hass&&!e.hass.states?.[t]&&!e.device.virtual,i=Number(e.device.marker?.glow_radius_cm),n=Number.isFinite(i)&&i>0?i/e.cellCm*e.gridPitch:e.defaultRadius,a=s?null:e.candidate?.appearance??null;return{strip:e.strip,device:e.device,state:s?"unavailable":a?"on":"off",glow:e.glow,appearance:a,radius:n}}function T(e,t){const o=(s=t.d,P*s);var s;return{t:o,path:b(R(e.strip.points),t.faces,o/2)}}function j(e){if(!e.views.length)return d``;const t=e.handlers,o=[...e.views].sort((e,t)=>e.strip.id<t.strip.id?1:e.strip.id>t.strip.id?-1:0),s=e.iso;return d`<g class="led-strips" data-hp-led-strips="${o.length}"
|
||||
data-hp-iso=${s?"raised":m}>
|
||||
${s?d`<defs><filter id="hp-led-iso-shadow" filterUnits="userSpaceOnUse"
|
||||
x="-100000" y="-100000" width="200000" height="200000">
|
||||
<feGaussianBlur stdDeviation="${s.shadow.sigma}"></feGaussianBlur></filter></defs>`:m}
|
||||
<style>
|
||||
.led-strip .led-focus { fill: none; stroke: transparent; }
|
||||
.led-strip:has(.led-hit:focus-visible) .led-focus { stroke: var(--primary-color, #03a9f4); }
|
||||
.led-strip .led-hit { fill: none; stroke: transparent; cursor: pointer; outline: none; }
|
||||
</style>
|
||||
${v(o,e=>e.strip.id,o=>{const{t:i,path:n}=T(o,e),a=y(n),r="unavailable"===o.state,l=r?"#9e9e9e":"on"===o.state&&!o.glow&&o.appearance?o.appearance.c:"#FFFFFF",c=2*k(i*e.perUnit)/(e.perUnit||1),p=o.device,u=t=>function(e,t,o){const s=e.currentTarget,i=s?.getScreenCTM?.();if(!i||o.views.length<2)return t;const n=i.inverse(),a=[n.a*e.clientX+n.c*e.clientY+n.e,n.b*e.clientX+n.d*e.clientY+n.f],r=o.views.map(e=>{const{t:t,path:s}=T(e,o);return{id:e.strip.id,points:s.points.map(e=>[e[0]*o.perUnit,e[1]*o.perUnit]),closed:s.closed,thicknessPx:t*o.perUnit}}),l=C([a[0]*o.perUnit,a[1]*o.perUnit],r);return o.views.find(e=>e.strip.id===l)||t}(t,o,e).device,h=s?`translate(0 ${-s.lift})`:m;return d`<g class="led-strip state-${o.state}" data-led-strip="${o.strip.id}"
|
||||
data-marker="${p.id}" data-state="${o.state}" data-closed="${n.closed?"true":"false"}">
|
||||
${s?d`<path class="led-iso-shadow" d="${a}" fill="none" stroke="black"
|
||||
stroke-opacity="${s.shadow.opacity}" stroke-width="${i}" stroke-linecap="round"
|
||||
stroke-linejoin="round" filter="url(#hp-led-iso-shadow)" pointer-events="none"
|
||||
transform="translate(${s.shadow.dx} ${s.shadow.dy-s.lift})"></path>
|
||||
${[1,.5].map(e=>d`<path class="led-iso-edge" d="${a}" fill="none" stroke="${s.edge}"
|
||||
stroke-width="${i}" stroke-linecap="round" stroke-linejoin="round" pointer-events="none"
|
||||
transform="translate(0 ${-s.lift+s.depth*e})"></path>`)}`:m}
|
||||
<g transform=${h}>
|
||||
<path class="led-outline" d="${a}" fill="none" stroke="${E}" stroke-width="${i}"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-dasharray=${r?`${2*i} ${1.5*i}`:m}></path>
|
||||
<path class="led-core" d="${a}" fill="none" stroke="${l}" stroke-width="${i/2}"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-dasharray=${r?`${2*i} ${1.5*i}`:m}></path>
|
||||
<path class="led-focus" d="${a}" stroke-width="${1.6*i}" stroke-linecap="round"
|
||||
stroke-linejoin="round" aria-hidden="true"></path>
|
||||
${t?d`<path class="led-hit" d="${a}" stroke-width="${c}"
|
||||
stroke-linecap="round" stroke-linejoin="round" pointer-events="stroke"
|
||||
role="button" tabindex="0" aria-label="${t.label(p)}"
|
||||
data-hp="device" data-id="${p.id}"
|
||||
@click=${e=>t.click(e,u(e))}
|
||||
@keydown=${e=>t.keydown(e,p)}
|
||||
@contextmenu=${e=>t.contextmenu(e,u(e))}
|
||||
@pointerdown=${e=>t.pointerdown(e,u(e))}
|
||||
@pointermove=${e=>t.pointermove(e,u(e))}
|
||||
@pointerup=${e=>t.pointerup(e,u(e))}
|
||||
@pointercancel=${e=>t.pointercancel(e,p)}
|
||||
@lostpointercapture=${e=>t.pointercancel(e,p)}
|
||||
@pointerover=${e=>t.pointerover(e,u(e))}
|
||||
@pointerleave=${()=>t.pointerleave()}
|
||||
@focus=${e=>t.focus(e,p)}
|
||||
@blur=${()=>t.blur(p)}></path>`:m}
|
||||
</g>
|
||||
</g>`})}
|
||||
</g>`}function B(e,t,o,s){return(t?e.find(e=>e.id===t):void 0)??(o?e.find(e=>s(o,e)):void 0)}function G(e){const t=new Map(e.devices.map(e=>[e.id,e])),o=g/e.cellCm*e.gridPitch,s=new Map(i({hass:e.hass,devices:e.devices,virtualLights:e.virtualLights,spaceId:e.space.id,defaultColor:e.defaultColor,paletteAlpha:e.paletteAlpha,defaultRadiusUnits:o,cellCm:e.cellCm,gridPitch:e.gridPitch,position:()=>({x:0,y:0})}).map(e=>[e.key,e])),r=[];for(const i of e.space.led_strips||[]){if(!1===i?.active||!i.marker||!w(i.points))continue;const a={...i,points:i.points.map(e=>[e[0]*n,e[1]*n])},l=t.get(a.marker);if(!l||l.space!==e.space.id)continue;if(l.hidden&&!e.showHidden)continue;const d=B(e.space.rooms,l.marker?.room_id,$(R(a.points)),e.inRoom);r.push(I({strip:a,device:l,candidate:s.get(`${e.space.id}|${l.id}`)??null,hass:e.hass,glow:!!d&&e.glowFor(d),defaultRadius:o,cellCm:e.cellCm,gridPitch:e.gridPitch}))}const l=_/e.cellCm*e.gridPitch;return{views:r,faces:A(e.scene,l),d:e.iconPct/100*a(e.space),scene:e.scene,polygons:e.polygons}}const H=new WeakMap;function N(e){H.delete(e),L?.releaseLedField(e)}function q(e){return{shapes:H.get(e)?.frame.views.length??0,...L?.ledFieldStats(e)??{visibility:0,sources:0,recomputes:0}}}function S(e,t,o){const s=t.rooms.flatMap(e=>{const t=r(e);return t?[{room:e,poly:t}]:[]}),i=s.length?e._lightBarriers(t,s.map(({room:e,poly:t})=>({r:e,poly:t}))):null,n=[t,e._renderDevices,e._renderPlanHass,o,i,e._mode,e._showAll],a=H.get(e);if(a&&a.key.every((e,t)=>e===n[t]))return a.frame;const d=e._config?.icon_size??2.5,c=G({space:t,devices:e._renderDevices,hass:e._renderPlanHass,virtualLights:e._virtualLights,defaultColor:e._fillColors.glow_light.c,paletteAlpha:e._fillColors.glow_light.a,cellCm:e._cellCm,gridPitch:e._gridPitch,iconPct:d>8?2.5:d,scene:i,polygons:s,glowFor:e=>l(o,e),inRoom:(t,o)=>e._pointInRoom(t,o),showHidden:"devices"===e._mode&&e._showAll});return!1!==e.isConnected&&H.set(e,{key:n,frame:c}),c}function O(e,t,o,s){const i=S(e,t,o),n=e._stageEl?.clientWidth,a="iso"===e._renderProjection&&"view"===e._mode,r=a?i.d*c:i.d,l="theme-dark"===p(e._renderPlanHass)?"dark":"light",m=!!e._isoLightFloors?.size,v=u(l,m),g=j({views:i.views,d:r,iso:a?{lift:f.lift*r,depth:f.depth*r,edge:h(E,l,m),shadow:{dx:v.dx*r,dy:v.dy*r,sigma:v.sigma*r,opacity:v.opacityWhite}}:null,faces:i.faces,perUnit:n&&s.w?n/s.w:1,handlers:"view"===e._mode?{click:(t,o)=>e._clickDevice(t,o),keydown:(t,o)=>e._keyDevice(t,o),contextmenu:(t,o)=>e._ctxDevice(t,o),pointerdown:(t,o)=>e._pointerDown(t,o),pointermove:(t,o)=>{const s=e._pointerMove(t,o);s&&e._showDeviceTip(t,s)},pointerup:(t,o)=>e._pointerUp(t,o),pointercancel:(t,o)=>e._pointerCancel(t,o),pointerover:(t,o)=>e._showDeviceTip(t,o),pointerleave:()=>e._clearPointerHover(),focus:(t,o)=>e._showDeviceFocusTip(t,o),blur:t=>e._hideDeviceFocusTip(t.id),label:e=>e.name}:null});return"plan"===e._mode||"decor"===e._mode?d`<g class="led-passive" opacity="0.45" pointer-events="none">${g}</g>`:g}function z(e,t,o){if(!1===e.isConnected)return d``;const s=S(e,t,o),i=s.scene&&(D(s.views)||L?.hasLedField(e))?L||x(t.id,()=>!1!==e.isConnected&&e.requestUpdate()):null;return i?i.renderLedField({views:s.views,scene:s.scene,polygons:s.polygons,faces:s.faces,spaceId:t.id,owner:e,requestUpdate:()=>e.requestUpdate(),isConnected:()=>!1!==e.isConnected}):d``}function W(e){const t=e.space.rooms.flatMap(e=>{const t=r(e);return t?[{room:e,poly:t}]:[]}),o=G({space:e.space,devices:e.devices,hass:e.hass,virtualLights:e.virtualLights,defaultColor:e.defaultColor,paletteAlpha:e.paletteAlpha,cellCm:e.cellCm,gridPitch:e.gridPitch,iconPct:e.iconPct,scene:e.scene,polygons:t,glowFor:e.glowFor,inRoom:e.inRoom,showHidden:!1}),s=e.live?o.views:o.views.map(e=>({...e,state:"off",appearance:null,glow:!1})),i=A({masonryGeometry:e.bodies.masonryGeometry,opaqueBodies:e.bodies.opaqueBodies},_/e.cellCm*e.gridPitch),n=e.scene&&e.live&&(D(s)||L?.hasLedField(e.owner))?x(e.space.id,e.ready):null;return d`${n?n.renderLedField({views:s,scene:e.scene,polygons:t,faces:i,spaceId:e.space.id,owner:e.owner,requestUpdate:e.ready,isConnected:()=>!0}):m}${j({views:s,d:o.d,faces:i,perUnit:e.perUnit,handlers:null})}`}export{M as LED_RUNTIME_FINGERPRINT,A as faceContext,x as ledField,G as ledFrame,S as ledFrameFor,q as ledStats,I as ledStripView,N as releaseLed,z as renderLedFieldFor,O as renderLedLayerFor,j as renderLedStripes,W as renderStaticLed,B as stripRoom};
|
||||
@@ -0,0 +1,47 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";import{eu as e,eC as t,c6 as o,eJ as s,eK as i,ad as n,ee as a,O as r,eL as l,az as d,ef as c,aL as p,eM as u,eN as h,eO as f,A as m,eP as v}from"./houseplan-card-BOdiRDFR.js";import{L as g,v as w,s as $,a as _,b as y,p as k,c as b,d as C,e as P}from"./led-strip-geometry-DaLifxqY.js";const M="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";let F,L=null,U=null;function x(e,t){return L||U||F===e||""===F||(U=(void 0===F?import("./led-strip-field-ObbckCkp.js"):import(new URL(`./led-strip-field-ObbckCkp.js?${Date.now()}`,import.meta.url).href)).then(e=>{e.LED_FIELD_FINGERPRINT===s?L=e:F=""},()=>{F=e}).finally(()=>{U=null})),L||U?.then(()=>L&&t()),L}const D=e=>e.some(e=>e.glow&&"on"===e.state),R=e=>e.map(e=>[e[0],e[1]]),E="#383838";function A(s,i){if(!s)return null;const n=Array.isArray(s.masonryGeometry)?s.masonryGeometry:[],a=[...e(s.masonryGeometry),...s.opaqueBodies],r=[];for(const e of a)for(let t=0;t<e.length;t++){const o=e[t],s=e[(t+1)%e.length];o&&s&&r.push({a:[o[0],o[1]],b:[s[0],s[1]]})}const l=25,d=new Map;r.forEach((e,t)=>{const o=Math.floor(Math.min(e.a[0],e.b[0])/l),s=Math.floor(Math.max(e.a[0],e.b[0])/l),i=Math.floor(Math.min(e.a[1],e.b[1])/l),n=Math.floor(Math.max(e.a[1],e.b[1])/l);for(let e=o;e<=s;e++)for(let o=i;o<=n;o++){const s=`${e},${o}`,i=d.get(s);i?i.push(t):d.set(s,[t])}});const c=e=>{let t=1/0,o=1/0,s=-1/0,i=-1/0;for(const n of e)t=Math.min(t,n[0]),o=Math.min(o,n[1]),s=Math.max(s,n[0]),i=Math.max(i,n[1]);return[t,o,s,i]},p=(e,t)=>e[0]>=t[0]&&e[0]<=t[2]&&e[1]>=t[1]&&e[1]<=t[3],u=n.filter(e=>e?.[0]?.length).map(e=>({box:c(e[0]),poly:e})),h=s.opaqueBodies.map(e=>({box:c(e),body:e}));return{faces:r,inside:e=>u.some(({box:o,poly:s})=>p(e,o)&&t([e[0],e[1]],[s]))||h.some(({box:t,body:s})=>p(e,t)&&o([e[0],e[1]],s)),epsilon:i,near:e=>{const t=new Set;for(let o=Math.floor(e[0]/l);o<=Math.floor(e[2]/l);o++)for(let s=Math.floor(e[1]/l);s<=Math.floor(e[3]/l);s++)for(const e of d.get(`${o},${s}`)||[])t.add(e);return[...t].map(e=>r[e])}}}function I(e){const t=e.candidate?.sourceEid||e.device.primary||"",o=t?e.hass?.states?.[t]?.state:void 0,s="unavailable"===o||"unknown"===o||!!t&&!!e.hass&&!e.hass.states?.[t]&&!e.device.virtual,i=Number(e.device.marker?.glow_radius_cm),n=Number.isFinite(i)&&i>0?i/e.cellCm*e.gridPitch:e.defaultRadius,a=s?null:e.candidate?.appearance??null;return{strip:e.strip,device:e.device,state:s?"unavailable":a?"on":"off",glow:e.glow,appearance:a,radius:n}}const T=new WeakMap;function j(e,t,o){const s=P*o;const i=T.get(e);if(i&&i.faces===t&&i.epsilon===t?.epsilon&&i.value.t===s&&i.points.length===e.points.length&&i.points.every((t,o)=>t[0]===e.points[o][0]&&t[1]===e.points[o][1]))return i.value;const n=R(e.points),a=y(n,t,s/2),r={t:s,path:a,d:k(a)};return T.set(e,{faces:t,epsilon:t?.epsilon,points:n,value:r}),r}function G(e){if(!e.views.length)return d``;const t=e.handlers,o=[...e.views].sort((e,t)=>e.strip.id<t.strip.id?1:e.strip.id>t.strip.id?-1:0),s=e.iso;return d`<g class="led-strips" data-hp-led-strips="${o.length}"
|
||||
data-hp-iso=${s?"raised":m}>
|
||||
${s?d`<defs><filter id="hp-led-iso-shadow" filterUnits="userSpaceOnUse"
|
||||
x="-100000" y="-100000" width="200000" height="200000">
|
||||
<feGaussianBlur stdDeviation="${s.shadow.sigma}"></feGaussianBlur></filter></defs>`:m}
|
||||
<style>
|
||||
.led-strip .led-focus { fill: none; stroke: transparent; }
|
||||
.led-strip:has(.led-hit:focus-visible) .led-focus { stroke: var(--primary-color, #03a9f4); }
|
||||
.led-strip .led-hit { fill: none; stroke: transparent; cursor: pointer; outline: none; }
|
||||
</style>
|
||||
${v(o,e=>e.strip.id,o=>{const{t:i,path:n,d:a}=j(o.strip,e.faces,e.d),r="unavailable"===o.state,l=r?"#9e9e9e":"on"===o.state&&!o.glow&&o.appearance?o.appearance.c:"#FFFFFF",c=2*b(i*e.perUnit)/(e.perUnit||1),p=o.device,u=t=>function(e,t,o){const s=e.currentTarget,i=s?.getScreenCTM?.();if(!i||o.views.length<2)return t;const n=i.inverse(),a=[n.a*e.clientX+n.c*e.clientY+n.e,n.b*e.clientX+n.d*e.clientY+n.f],r=o.views.map(e=>{const{t:t,path:s}=j(e.strip,o.faces,o.d);return{id:e.strip.id,points:s.points.map(e=>[e[0]*o.perUnit,e[1]*o.perUnit]),closed:s.closed,thicknessPx:t*o.perUnit}}),l=C([a[0]*o.perUnit,a[1]*o.perUnit],r);return o.views.find(e=>e.strip.id===l)||t}(t,o,e).device,h=s?`translate(0 ${-s.lift})`:m;return d`<g class="led-strip state-${o.state}" data-led-strip="${o.strip.id}"
|
||||
data-marker="${p.id}" data-state="${o.state}" data-closed="${n.closed?"true":"false"}">
|
||||
${s?d`<path class="led-iso-shadow" d="${a}" fill="none" stroke="black"
|
||||
stroke-opacity="${s.shadow.opacity}" stroke-width="${i}" stroke-linecap="round"
|
||||
stroke-linejoin="round" filter="url(#hp-led-iso-shadow)" pointer-events="none"
|
||||
transform="translate(${s.shadow.dx} ${s.shadow.dy-s.lift})"></path>
|
||||
${[1,.5].map(e=>d`<path class="led-iso-edge" d="${a}" fill="none" stroke="${s.edge}"
|
||||
stroke-width="${i}" stroke-linecap="round" stroke-linejoin="round" pointer-events="none"
|
||||
transform="translate(0 ${-s.lift+s.depth*e})"></path>`)}`:m}
|
||||
<g transform=${h}>
|
||||
<path class="led-outline" d="${a}" fill="none" stroke="${E}" stroke-width="${i}"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-dasharray=${r?`${2*i} ${1.5*i}`:m}></path>
|
||||
<path class="led-core" d="${a}" fill="none" stroke="${l}" stroke-width="${i/2}"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-dasharray=${r?`${2*i} ${1.5*i}`:m}></path>
|
||||
<path class="led-focus" d="${a}" stroke-width="${1.6*i}" stroke-linecap="round"
|
||||
stroke-linejoin="round" aria-hidden="true"></path>
|
||||
${t?d`<path class="led-hit" d="${a}" stroke-width="${c}"
|
||||
stroke-linecap="round" stroke-linejoin="round" pointer-events="stroke"
|
||||
role="button" tabindex="0" aria-label="${t.label(p)}"
|
||||
data-hp="device" data-id="${p.id}"
|
||||
@click=${e=>t.click(e,u(e))}
|
||||
@keydown=${e=>t.keydown(e,p)}
|
||||
@contextmenu=${e=>t.contextmenu(e,u(e))}
|
||||
@pointerdown=${e=>t.pointerdown(e,u(e))}
|
||||
@pointermove=${e=>t.pointermove(e,u(e))}
|
||||
@pointerup=${e=>t.pointerup(e,u(e))}
|
||||
@pointercancel=${e=>t.pointercancel(e,p)}
|
||||
@lostpointercapture=${e=>t.pointercancel(e,p)}
|
||||
@pointerover=${e=>t.pointerover(e,u(e))}
|
||||
@pointerleave=${()=>t.pointerleave()}
|
||||
@focus=${e=>t.focus(e,p)}
|
||||
@blur=${()=>t.blur(p)}></path>`:m}
|
||||
</g>
|
||||
</g>`})}
|
||||
</g>`}function H(e,t,o,s){return(t?e.find(e=>e.id===t):void 0)??(o?e.find(e=>s(o,e)):void 0)}function N(e){const t=new Map(e.devices.map(e=>[e.id,e])),o=g/e.cellCm*e.gridPitch,s=new Map(i({hass:e.hass,devices:e.devices,virtualLights:e.virtualLights,spaceId:e.space.id,defaultColor:e.defaultColor,paletteAlpha:e.paletteAlpha,defaultRadiusUnits:o,cellCm:e.cellCm,gridPitch:e.gridPitch,position:()=>({x:0,y:0})}).map(e=>[e.key,e])),r=[];for(const i of e.space.led_strips||[]){if(!1===i?.active||!i.marker||!w(i.points))continue;const a={...i,points:i.points.map(e=>[e[0]*n,e[1]*n])},l=t.get(a.marker);if(!l||l.space!==e.space.id)continue;if(l.hidden&&!e.showHidden)continue;const d=H(e.space.rooms,l.marker?.room_id,$(R(a.points)),e.inRoom);r.push(I({strip:a,device:l,candidate:s.get(`${e.space.id}|${l.id}`)??null,hass:e.hass,glow:!!d&&e.glowFor(d),defaultRadius:o,cellCm:e.cellCm,gridPitch:e.gridPitch}))}const l=_/e.cellCm*e.gridPitch;return{views:r,faces:A(e.scene,l),d:e.iconPct/100*a(e.space),scene:e.scene,polygons:e.polygons}}const q=new WeakMap;function B(e){for(const t of q.get(e)?.frame.views??[])T.delete(t.strip);q.delete(e),L?.releaseLedField(e)}function S(e){return{shapes:q.get(e)?.frame.views.length??0,...L?.ledFieldStats(e)??{visibility:0,sources:0,visibilityPaths:0,pathChars:0,recomputes:0}}}function O(e,t,o){const s=t.rooms.flatMap(e=>{const t=r(e);return t?[{room:e,poly:t}]:[]}),i=s.length?e._lightBarriers(t,s.map(({room:e,poly:t})=>({r:e,poly:t}))):null,n=[t,e._renderDevices,e._renderPlanHass,o,i,e._mode,e._showAll],a=q.get(e);if(a&&a.key.every((e,t)=>e===n[t]))return a.frame;for(const e of a?.frame.views??[])T.delete(e.strip);const d=e._config?.icon_size??2.5,c=N({space:t,devices:e._renderDevices,hass:e._renderPlanHass,virtualLights:e._virtualLights,defaultColor:e._fillColors.glow_light.c,paletteAlpha:e._fillColors.glow_light.a,cellCm:e._cellCm,gridPitch:e._gridPitch,iconPct:d>8?2.5:d,scene:i,polygons:s,glowFor:e=>l(o,e),inRoom:(t,o)=>e._pointInRoom(t,o),showHidden:"devices"===e._mode&&e._showAll});return!1!==e.isConnected&&q.set(e,{key:n,frame:c}),c}function W(e,t,o,s){const i=O(e,t,o),n=e._stageEl?.clientWidth,a="iso"===e._renderProjection&&"view"===e._mode,r=a?i.d*c:i.d,l="theme-dark"===p(e._renderPlanHass)?"dark":"light",m=!!e._isoLightFloors?.size,v=u(l,m),g=G({views:i.views,d:r,iso:a?{lift:f.lift*r,depth:f.depth*r,edge:h(E,l,m),shadow:{dx:v.dx*r,dy:v.dy*r,sigma:v.sigma*r,opacity:v.opacityWhite}}:null,faces:i.faces,perUnit:n&&s.w?n/s.w:1,handlers:"view"===e._mode?{click:(t,o)=>e._clickDevice(t,o),keydown:(t,o)=>e._keyDevice(t,o),contextmenu:(t,o)=>e._ctxDevice(t,o),pointerdown:(t,o)=>e._pointerDown(t,o),pointermove:(t,o)=>{const s=e._pointerMove(t,o);s&&e._showDeviceTip(t,s)},pointerup:(t,o)=>e._pointerUp(t,o),pointercancel:(t,o)=>e._pointerCancel(t,o),pointerover:(t,o)=>e._showDeviceTip(t,o),pointerleave:()=>e._clearPointerHover(),focus:(t,o)=>e._showDeviceFocusTip(t,o),blur:t=>e._hideDeviceFocusTip(t.id),label:e=>e.name}:null});return"plan"===e._mode||"decor"===e._mode?d`<g class="led-passive" opacity="0.45" pointer-events="none">${g}</g>`:g}function z(e,t,o){if(!1===e.isConnected)return d``;const s=O(e,t,o),i=s.scene&&(D(s.views)||L?.hasLedField(e))?L||x(t.id,()=>!1!==e.isConnected&&e.requestUpdate()):null;return i?i.renderLedField({views:s.views,scene:s.scene,polygons:s.polygons,faces:s.faces,spaceId:t.id,owner:e,requestUpdate:()=>e.requestUpdate(),isConnected:()=>!1!==e.isConnected}):d``}function Y(e){const t=e.space.rooms.flatMap(e=>{const t=r(e);return t?[{room:e,poly:t}]:[]}),o=N({space:e.space,devices:e.devices,hass:e.hass,virtualLights:e.virtualLights,defaultColor:e.defaultColor,paletteAlpha:e.paletteAlpha,cellCm:e.cellCm,gridPitch:e.gridPitch,iconPct:e.iconPct,scene:e.scene,polygons:t,glowFor:e.glowFor,inRoom:e.inRoom,showHidden:!1}),s=e.live?o.views:o.views.map(e=>({...e,state:"off",appearance:null,glow:!1})),i=A({masonryGeometry:e.bodies.masonryGeometry,opaqueBodies:e.bodies.opaqueBodies},_/e.cellCm*e.gridPitch),n=e.scene&&e.live&&(D(s)||L?.hasLedField(e.owner))?x(e.space.id,e.ready):null;return d`${n?n.renderLedField({views:s,scene:e.scene,polygons:t,faces:i,spaceId:e.space.id,owner:e.owner,requestUpdate:e.ready,isConnected:()=>!0}):m}${G({views:s,d:o.d,faces:i,perUnit:e.perUnit,handlers:null})}`}export{M as LED_RUNTIME_FINGERPRINT,A as faceContext,x as ledField,N as ledFrame,O as ledFrameFor,S as ledStats,I as ledStripView,j as ledStripePath,B as releaseLed,z as renderLedFieldFor,W as renderLedLayerFor,G as renderLedStripes,Y as renderStaticLed,H as stripRoom};
|
||||
@@ -1 +0,0 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";import{eQ as e,eR as n,eF as a}from"./houseplan-card-BWxNZe40.js";function o(a,o){return new e([{code:"en",dictionary:a},{code:"ru",loadDictionary:o.ru},{code:"de",loadDictionary:o.de},{code:"fr",loadDictionary:o.fr}],"24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29",console.warn,n)}function r(e){return function(e,n){return{state:a=>{const o=e.state(a);return"pending"!==o&&n.some(e=>"pending"===e.state(a))?"pending":o},dictionary:n=>e.dictionary(n),ensure:a=>Promise.all([e,...n].map(e=>e.ensure(a))).then(()=>{})}}(a,e)}export{o as n,r as s};
|
||||
@@ -0,0 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";import{eR as e,eS as a,eG as n}from"./houseplan-card-BOdiRDFR.js";function o(n,o){return new e([{code:"en",dictionary:n},{code:"ru",loadDictionary:o.ru},{code:"de",loadDictionary:o.de},{code:"fr",loadDictionary:o.fr}],"ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172",console.warn,a)}function r(e){return function(e,a){return{state:n=>{const o=e.state(n);return"pending"!==o&&a.some(e=>"pending"===e.state(n))?"pending":o},dictionary:a=>e.dictionary(a),ensure:n=>Promise.all([e,...a].map(e=>e.ensure(n))).then(()=>{})}}(n,e)}export{o as n,r as s};
|
||||
@@ -1,4 +1,4 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";import{aG as e,l as t,aJ as s,A as a,eE as o,eF as i,eG as n,E as l,eP as c,c as r}from"./houseplan-card-BWxNZe40.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return t(this.hass,this._config?.language)}get _schema(){const e=this._spaces||[];return[e.length?{name:"space",selector:{select:{mode:"dropdown",options:e}}}:{name:"space",selector:{text:{}}},{name:"title",selector:{text:{}}},{name:"fit",selector:{select:{mode:"dropdown",options:[{value:"content",label:s(this._lang,"editor.fit_content")},{value:"house",label:s(this._lang,"editor.fit_house")}]}}},{name:"show_button",selector:{boolean:{}}},{name:"button_label",selector:{text:{}}},{name:"button_target",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"light_pools",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}}]}render(){if(!this.hass||!this._config)return a;const e=o(this,i,t(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return l;this._loadSpaces();const h=this._lang,_={space:s(h,"editor.space"),title:s(h,"editor.title"),fit:s(h,"editor.framing"),show_button:s(h,"editor.show_button"),button_label:s(h,"editor.button_label"),button_target:s(h,"editor.button_target"),icon_size:s(h,"editor.icon_size"),show_temperature:s(h,"editor.show_temperature"),live_states:s(h,"editor.live_states"),light_pools:s(h,"editor.light_pools"),show_signal:s(h,"editor.show_signal")};return r`<ha-form
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";import{aG as e,l as t,aJ as s,A as a,eF as o,eG as i,eH as n,E as l,eQ as c,c as r}from"./houseplan-card-BOdiRDFR.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return t(this.hass,this._config?.language)}get _schema(){const e=this._spaces||[];return[e.length?{name:"space",selector:{select:{mode:"dropdown",options:e}}}:{name:"space",selector:{text:{}}},{name:"title",selector:{text:{}}},{name:"fit",selector:{select:{mode:"dropdown",options:[{value:"content",label:s(this._lang,"editor.fit_content")},{value:"house",label:s(this._lang,"editor.fit_house")}]}}},{name:"show_button",selector:{boolean:{}}},{name:"button_label",selector:{text:{}}},{name:"button_target",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"light_pools",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}}]}render(){if(!this.hass||!this._config)return a;const e=o(this,i,t(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return l;this._loadSpaces();const h=this._lang,_={space:s(h,"editor.space"),title:s(h,"editor.title"),fit:s(h,"editor.framing"),show_button:s(h,"editor.show_button"),button_label:s(h,"editor.button_label"),button_target:s(h,"editor.button_target"),icon_size:s(h,"editor.icon_size"),show_temperature:s(h,"editor.show_temperature"),live_states:s(h,"editor.live_states"),light_pools:s(h,"editor.light_pools"),show_signal:s(h,"editor.show_signal")};return r`<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${{...this._config,fit:c(this._config.fit)}}
|
||||
.schema=${this._schema}
|
||||
@@ -1,4 +1,4 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";import{c as a,A as e}from"./houseplan-card-BWxNZe40.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-DRNznxwG.js";import"./form-kit.styles-cMZ9Wyhz.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__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";import{c as a,A as e}from"./houseplan-card-BOdiRDFR.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-CcLP5B5v.js";import"./form-kit.styles-B6HsmMPA.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__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";import{e_ as e,O as t,b5 as n,M as i,af as o,ad as r,ce as s,e$ as a,cd as c,b4 as l,b3 as d,b6 as u,f0 as f,f1 as m,cg as g,f2 as v}from"./houseplan-card-BWxNZe40.js";function y(e){if(!e.registry.authoritative)return null;const t=new Set,n=u(e.markers);for(const i of Object.values(e.registry.devices||{}))i?.id&&"service"!==i.entry_type&&!n.devices.has(i.id)&&i.area_id&&e.areaToSpace[i.area_id]&&t.add(i.id);for(const i of e.markers||[]){if(i.removed||"virtual"===i.binding)continue;const o=String(i.binding||"").indexOf(":");if(o<1)continue;const r=i.binding.slice(0,o),s=i.binding.slice(o+1),a="device"===r?e.registry.devices?.[s]:null,c="entity"===r?e.registry.entities?.[s]:null,l="device"===r?s:c?.device_id,d="device"===r?a?.area_id:c?.area_id||l&&e.registry.devices?.[l]?.area_id,u="string"==typeof i.room_id&&i.room_id.length>0&&null===i.area?"":i.area||d||"",f=u&&e.areaToSpace[u]||i.space||e.firstSpaceId;l&&e.registry.devices?.[l]&&e.spaceIds.has(f)&&(!n.devices.has(l)||"entity"===r&&n.liveEntities.has(s))&&t.add(l)}return t}function _(e,t){return e?.length?t?.length?v(e,t):e:t}function h(e,t){const n=l(e.rooms,t.walls,t.openCuts,[],i,t.cellCm,o,r);return{roomGeom:"ok"===n.status||"degraded-extra"===n.status?n.roomGeom:void 0,multiWallNodes:d(e.rooms,t.walls,t.openCuts,i,t.cellCm,o,r)}}function*p(l,d,u=h){try{let f=0;for(const m of d){const d=l.spaces.find(e=>String(e?.id)===m.id);if(!d)continue;const g=e(d,m),v=u(m,g);let y=null;for(const e of m.rooms){if(!e.id)continue;const a=t(e);if(!a)continue;const c=n(m.rooms,e.id,g.walls,g.openCuts,i,g.cellCm,o,r,v.roomGeom,v.multiWallNodes)||a;y=_(y,s(c,g.physicalBodies)),yield}const h=a(y||[],m.stairs);let p=h.next();for(;!p.done;)yield,p=h.next();y=p.value;const b=g.cellCm/o;f+=c(y)*b*b/1e4}return f}catch{return null}}function b(e,t,n=h){const i=p(e,t,n);let o=i.next();for(;!o.done;)o=i.next();return o.value}function S(e,t,n,i){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:g(t.areaM2,"mi"===n?.config?.unit_system?.length);try{return new Intl.DateTimeFormat(i||void 0,{dateStyle:"short",timeStyle:"short",timeZone:n?.config?.time_zone||void 0}).format(t.now)}catch{return t.now.toLocaleString()}}function C(e,t){const n=e?.states?.[t];if(!n)return null;const i=f(e,t);return i?m(i,String(n.attributes?.unit_of_measurement||"")):null}export{p as cleanFloorAreaSteps,y as representedHaDeviceIds,h as spaceWallGeometry,C as summaryEntityValue,S as summarySystemValue,b as totalCleanFloorAreaM2};
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";import{e$ as e,O as t,b5 as n,M as i,af as o,ad as r,cf as s,f0 as a,ce as l,b4 as c,b3 as u,b6 as d,f1 as f,f2 as m,ch as g,f3 as v}from"./houseplan-card-BOdiRDFR.js";function y(e){if(!e.registry.authoritative)return null;const t=new Set,n=d(e.markers);for(const i of Object.values(e.registry.devices||{}))i?.id&&"service"!==i.entry_type&&!n.devices.has(i.id)&&i.area_id&&e.areaToSpace[i.area_id]&&t.add(i.id);for(const i of e.markers||[]){if(i.removed||"virtual"===i.binding)continue;const o=String(i.binding||"").indexOf(":");if(o<1)continue;const r=i.binding.slice(0,o),s=i.binding.slice(o+1),a="device"===r?e.registry.devices?.[s]:null,l="entity"===r?e.registry.entities?.[s]:null,c="device"===r?s:l?.device_id,u="device"===r?a?.area_id:l?.area_id||c&&e.registry.devices?.[c]?.area_id,d="string"==typeof i.room_id&&i.room_id.length>0&&null===i.area?"":i.area||u||"",f=d&&e.areaToSpace[d]||i.space||e.firstSpaceId;c&&e.registry.devices?.[c]&&e.spaceIds.has(f)&&(!n.devices.has(c)||"entity"===r&&n.liveEntities.has(s))&&t.add(c)}return t}function _(e,t){return e?.length?t?.length?v(e,t):e:t}function h(e,t){const n=c(e.rooms,t.walls,t.openCuts,[],i,t.cellCm,o,r);return{roomGeom:"ok"===n.status||"degraded-extra"===n.status?n.roomGeom:void 0,multiWallNodes:u(e.rooms,t.walls,t.openCuts,i,t.cellCm,o,r)}}function*p(c,u,d=h){try{let f=0;for(const m of u){const u=c.spaces.find(e=>String(e?.id)===m.id);if(!u)continue;const g=e(u,m),v=d(m,g);let y=null;for(const e of m.rooms){if(!e.id)continue;const a=t(e);if(!a)continue;const l=n(m.rooms,e.id,g.walls,g.openCuts,i,g.cellCm,o,r,v.roomGeom,v.multiWallNodes)||a;y=_(y,s(l,g.physicalBodies)),yield}const h=a(y||[],m.stairs);let p=h.next();for(;!p.done;)yield,p=h.next();y=p.value;const b=g.cellCm/o;f+=l(y)*b*b/1e4}return f}catch{return null}}function b(e,t,n=h){const i=p(e,t,n);let o=i.next();for(;!o.done;)o=i.next();return o.value}function S(e,t,n,i){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:g(t.areaM2,"mi"===n?.config?.unit_system?.length);try{return new Intl.DateTimeFormat(i||void 0,{dateStyle:"short",timeStyle:"short",timeZone:n?.config?.time_zone||void 0}).format(t.now)}catch{return t.now.toLocaleString()}}function C(e,t){const n=e?.states?.[t];if(!n)return null;const i=f(e,t);return i?m(i,String(n.attributes?.unit_of_measurement||"")):null}export{p as cleanFloorAreaSteps,y as representedHaDeviceIds,h as spaceWallGeometry,C as summaryEntityValue,S as summarySystemValue,b as totalCleanFloorAreaM2};
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";var e={"furn.title":"Möbelbibliothek","furn.symbol":"Symbol","furn.group_furniture":"Möbel","furn.group_appliance":"Geräte","furn.group_sanitary":"Sanitär","furn.group_other":"Sonstiges","furn.width":"Breite","furn.depth":"Tiefe","furn.back_to_categories":"Alle Kategorien","furn.pick_hint":"Wählen Sie eine Kategorie und dann ein Symbol.","furn.place_hint":"Klicken Sie auf den Plan — das Element wird an der nächsten Wand platziert. Mit Shift wird es frei platziert.","furn.cat_air_conditioner":"Klimaanlagen","furn.cat_armchair":"Sessel","furn.cat_bathtub":"Badewannen","furn.cat_bed":"Betten","furn.cat_bidet":"Bidets","furn.cat_boiler":"Warmwasserbereiter","furn.cat_chair":"Stühle","furn.cat_coffee_table":"Couchtische","furn.cat_computer":"Computer","furn.cat_cooktop":"Kochfelder","furn.cat_dining_table":"Esstische","furn.cat_dishwasher":"Geschirrspüler","furn.cat_dryer":"Wäschetrockner","furn.cat_exercise":"Fitnessgerät","furn.cat_fireplace":"Kamine","furn.cat_fridge":"Kühlschränke","furn.cat_hood":"Dunstabzugshaube","furn.cat_kitchen_cabinet":"Küchenschränke","furn.cat_kitchen_sink":"Küchenspülen","furn.cat_nightstand":"Schränke","furn.cat_oven":"Backofen","furn.cat_plant":"Pflanzen","furn.cat_rug":"Teppiche","furn.cat_shelving":"Regale","furn.cat_shower":"Duschen","furn.cat_sink":"Waschbecken","furn.cat_sofa":"Sofas","furn.cat_stairs":"Treppen","furn.cat_toilet":"Toiletten","furn.cat_tv":"Fernseher","furn.cat_wardrobe":"Kleiderschränke","furn.cat_washer":"Waschmaschinen","furn.cat_work_table":"Schreibtische","furn.sym_exercise":"Fitnessgerät","furn.sym_computer":"Computer","furn.sym_hood":"Dunstabzugshaube","furn.sym_oven":"Backofen","furn.sym_sofa":"Zweisitzer-Sofa","furn.sym_sofa_three_seat":"Dreisitzer-Sofa","furn.sym_sofa_corner_right":"Ecksofa rechts","furn.sym_armchair":"Polstersessel","furn.sym_armchair_office":"Bürostuhl","furn.sym_coffee_table":"Rechteckiger Couchtisch","furn.sym_coffee_table_round":"Runder Couchtisch","furn.sym_coffee_table_oval":"Ovaler Couchtisch","furn.sym_coffee_table_rounded":"Abgerundeter Couchtisch","furn.sym_table_dining":"Rechteckiger Esstisch","furn.sym_table_round":"Rundtisch","furn.sym_table_dining_oval":"Ovaler Esstisch","furn.sym_table_dining_rounded":"Abgerundeter Esstisch","furn.sym_chair":"Stuhl","furn.sym_chair_bar":"Barhocker","furn.sym_desk":"Rechteckiger Schreibtisch","furn.sym_desk_corner":"Eckschreibtisch","furn.sym_bed_double":"Doppelbett","furn.sym_bed_single":"Einzelbett","furn.sym_nightstand":"Nachttisch","furn.sym_cabinet_tv":"TV-Schrank","furn.sym_cabinet_shoe":"Schuhschrank","furn.sym_cabinet_sink":"Waschtischunterschrank","furn.sym_wardrobe":"Kleiderschrank","furn.sym_bookshelf":"Bücherschrank","furn.sym_wall_unit":"Schrankwand","furn.sym_kitchen_floor":"Küchen-Unterschrank","furn.sym_kitchen_floor_corner":"Küchen-Eckunterschrank","furn.sym_kitchen_wall":"Küchen-Hängeschrank","furn.sym_kitchen_wall_corner":"Küchen-Eckhängeschrank","furn.sym_shelf_floor":"Standregal","furn.sym_shelf_wall":"Wandregal","furn.sym_fridge":"Kühlschrank","furn.sym_stove":"Vierflammiges Kochfeld","furn.sym_cooktop_two":"Zweiflammiges Kochfeld","furn.sym_dishwasher":"Geschirrspüler","furn.sym_washer":"Waschmaschine","furn.sym_dryer":"Wäschetrockner","furn.sym_tv":"Fernseher mit Standfuß","furn.sym_tv_wall":"Wandfernseher","furn.sym_ac":"Klimaanlage","furn.sym_water_heater":"Wassererhitzer","furn.sym_toilet":"Stand-WC","furn.sym_toilet_built_in":"Wand-WC","furn.sym_bathtub":"Rechteckige Badewanne","furn.sym_bathtub_corner":"Eckbadewanne","furn.sym_shower":"Dusche","furn.sym_sink":"Waschbecken","furn.sym_kitchen_sink":"Einzel-Küchenspüle","furn.sym_kitchen_sink_double":"Doppel-Küchenspüle","furn.sym_bidet":"Stand-Bidet","furn.sym_bidet_built_in":"Wand-Bidet","furn.sym_stairs":"Treppe","furn.sym_fireplace":"Kamin","furn.sym_plant":"Pflanze","furn.sym_rug":"Teppich"};const r="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";export{e as dictionary,r as fingerprint};
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";var e={"furn.title":"Möbelbibliothek","furn.symbol":"Symbol","furn.group_furniture":"Möbel","furn.group_appliance":"Geräte","furn.group_sanitary":"Sanitär","furn.group_other":"Sonstiges","furn.width":"Breite","furn.depth":"Tiefe","furn.back_to_categories":"Alle Kategorien","furn.pick_hint":"Wählen Sie eine Kategorie und dann ein Symbol.","furn.place_hint":"Klicken Sie auf den Plan — das Element wird an der nächsten Wand platziert. Mit Shift wird es frei platziert.","furn.cat_air_conditioner":"Klimaanlagen","furn.cat_armchair":"Sessel","furn.cat_bathtub":"Badewannen","furn.cat_bed":"Betten","furn.cat_bidet":"Bidets","furn.cat_boiler":"Warmwasserbereiter","furn.cat_chair":"Stühle","furn.cat_coffee_table":"Couchtische","furn.cat_computer":"Computer","furn.cat_cooktop":"Kochfelder","furn.cat_dining_table":"Esstische","furn.cat_dishwasher":"Geschirrspüler","furn.cat_dryer":"Wäschetrockner","furn.cat_exercise":"Fitnessgerät","furn.cat_fireplace":"Kamine","furn.cat_fridge":"Kühlschränke","furn.cat_hood":"Dunstabzugshaube","furn.cat_kitchen_cabinet":"Küchenschränke","furn.cat_kitchen_sink":"Küchenspülen","furn.cat_nightstand":"Schränke","furn.cat_oven":"Backofen","furn.cat_plant":"Pflanzen","furn.cat_rug":"Teppiche","furn.cat_shelving":"Regale","furn.cat_shower":"Duschen","furn.cat_sink":"Waschbecken","furn.cat_sofa":"Sofas","furn.cat_stairs":"Treppen","furn.cat_toilet":"Toiletten","furn.cat_tv":"Fernseher","furn.cat_wardrobe":"Kleiderschränke","furn.cat_washer":"Waschmaschinen","furn.cat_work_table":"Schreibtische","furn.sym_exercise":"Fitnessgerät","furn.sym_computer":"Computer","furn.sym_hood":"Dunstabzugshaube","furn.sym_oven":"Backofen","furn.sym_sofa":"Zweisitzer-Sofa","furn.sym_sofa_three_seat":"Dreisitzer-Sofa","furn.sym_sofa_corner_right":"Ecksofa rechts","furn.sym_armchair":"Polstersessel","furn.sym_armchair_office":"Bürostuhl","furn.sym_coffee_table":"Rechteckiger Couchtisch","furn.sym_coffee_table_round":"Runder Couchtisch","furn.sym_coffee_table_oval":"Ovaler Couchtisch","furn.sym_coffee_table_rounded":"Abgerundeter Couchtisch","furn.sym_table_dining":"Rechteckiger Esstisch","furn.sym_table_round":"Rundtisch","furn.sym_table_dining_oval":"Ovaler Esstisch","furn.sym_table_dining_rounded":"Abgerundeter Esstisch","furn.sym_chair":"Stuhl","furn.sym_chair_bar":"Barhocker","furn.sym_desk":"Rechteckiger Schreibtisch","furn.sym_desk_corner":"Eckschreibtisch","furn.sym_bed_double":"Doppelbett","furn.sym_bed_single":"Einzelbett","furn.sym_nightstand":"Nachttisch","furn.sym_cabinet_tv":"TV-Schrank","furn.sym_cabinet_shoe":"Schuhschrank","furn.sym_cabinet_sink":"Waschtischunterschrank","furn.sym_wardrobe":"Kleiderschrank","furn.sym_bookshelf":"Bücherschrank","furn.sym_wall_unit":"Schrankwand","furn.sym_kitchen_floor":"Küchen-Unterschrank","furn.sym_kitchen_floor_corner":"Küchen-Eckunterschrank","furn.sym_kitchen_wall":"Küchen-Hängeschrank","furn.sym_kitchen_wall_corner":"Küchen-Eckhängeschrank","furn.sym_shelf_floor":"Standregal","furn.sym_shelf_wall":"Wandregal","furn.sym_fridge":"Kühlschrank","furn.sym_stove":"Vierflammiges Kochfeld","furn.sym_cooktop_two":"Zweiflammiges Kochfeld","furn.sym_dishwasher":"Geschirrspüler","furn.sym_washer":"Waschmaschine","furn.sym_dryer":"Wäschetrockner","furn.sym_tv":"Fernseher mit Standfuß","furn.sym_tv_wall":"Wandfernseher","furn.sym_ac":"Klimaanlage","furn.sym_water_heater":"Wassererhitzer","furn.sym_toilet":"Stand-WC","furn.sym_toilet_built_in":"Wand-WC","furn.sym_bathtub":"Rechteckige Badewanne","furn.sym_bathtub_corner":"Eckbadewanne","furn.sym_shower":"Dusche","furn.sym_sink":"Waschbecken","furn.sym_kitchen_sink":"Einzel-Küchenspüle","furn.sym_kitchen_sink_double":"Doppel-Küchenspüle","furn.sym_bidet":"Stand-Bidet","furn.sym_bidet_built_in":"Wand-Bidet","furn.sym_stairs":"Treppe","furn.sym_fireplace":"Kamin","furn.sym_plant":"Pflanze","furn.sym_rug":"Teppich"};const r="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";export{e as dictionary,r as fingerprint};
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";var e={"furn.title":"Bibliothèque de mobilier","furn.symbol":"Symbole","furn.group_furniture":"Mobilier","furn.group_appliance":"Électroménager","furn.group_sanitary":"Sanitaires","furn.group_other":"Autre","furn.width":"Largeur","furn.depth":"Profondeur","furn.back_to_categories":"Toutes les catégories","furn.pick_hint":"Choisissez un symbole, puis cliquez sur le plan.","furn.place_hint":"Cliquez sur le plan — le meuble se place contre le mur le plus proche. Maj permet un placement libre.","furn.cat_air_conditioner":"Climatiseurs","furn.cat_armchair":"Fauteuils","furn.cat_bathtub":"Baignoires","furn.cat_bed":"Lits","furn.cat_bidet":"Bidets","furn.cat_boiler":"Chauffe-eau","furn.cat_chair":"Chaises","furn.cat_coffee_table":"Tables basses","furn.cat_computer":"Ordinateur","furn.cat_cooktop":"Plaques de cuisson","furn.cat_dining_table":"Tables à manger","furn.cat_dishwasher":"Lave-vaisselle","furn.cat_dryer":"Sèche-linge","furn.cat_exercise":"Équipement de sport","furn.cat_fireplace":"Cheminées","furn.cat_fridge":"Réfrigérateurs","furn.cat_hood":"Hotte","furn.cat_kitchen_cabinet":"Meubles de cuisine","furn.cat_kitchen_sink":"Éviers de cuisine","furn.cat_nightstand":"Meubles","furn.cat_oven":"Four","furn.cat_plant":"Plantes","furn.cat_rug":"Tapis","furn.cat_shelving":"Étagères","furn.cat_shower":"Douches","furn.cat_sink":"Lavabos","furn.cat_sofa":"Canapés","furn.cat_stairs":"Escaliers","furn.cat_toilet":"Toilettes","furn.cat_tv":"Téléviseurs","furn.cat_wardrobe":"Armoires","furn.cat_washer":"Lave-linge","furn.cat_work_table":"Bureaux","furn.sym_exercise":"Équipement de sport","furn.sym_computer":"Ordinateur","furn.sym_hood":"Hotte","furn.sym_oven":"Four","furn.sym_sofa":"Canapé","furn.sym_sofa_three_seat":"Canapé trois places","furn.sym_sofa_corner_right":"Canapé d'angle droit","furn.sym_armchair":"Fauteuil","furn.sym_armchair_office":"Chaise de bureau","furn.sym_coffee_table":"Table basse","furn.sym_coffee_table_round":"Table basse ronde","furn.sym_coffee_table_oval":"Table basse ovale","furn.sym_coffee_table_rounded":"Table basse arrondie","furn.sym_table_dining":"Table à manger","furn.sym_table_round":"Table ronde","furn.sym_table_dining_oval":"Table à manger ovale","furn.sym_table_dining_rounded":"Table à manger arrondie","furn.sym_chair":"Chaise","furn.sym_chair_bar":"Tabouret de bar","furn.sym_desk":"Bureau","furn.sym_desk_corner":"Bureau d'angle","furn.sym_bed_double":"Lit double","furn.sym_bed_single":"Lit simple","furn.sym_nightstand":"Table de chevet","furn.sym_cabinet_tv":"Meuble TV","furn.sym_cabinet_shoe":"Meuble à chaussures","furn.sym_cabinet_sink":"Meuble sous lavabo","furn.sym_wardrobe":"Armoire","furn.sym_bookshelf":"Bibliothèque","furn.sym_wall_unit":"Élément mural","furn.sym_kitchen_floor":"Module de cuisine bas","furn.sym_kitchen_floor_corner":"Module de cuisine bas d'angle","furn.sym_kitchen_wall":"Module de cuisine haut","furn.sym_kitchen_wall_corner":"Module de cuisine haut d'angle","furn.sym_shelf_floor":"Étagère au sol","furn.sym_shelf_wall":"Étagère murale","furn.sym_fridge":"Réfrigérateur","furn.sym_stove":"Cuisinière","furn.sym_cooktop_two":"Plaque à deux feux","furn.sym_dishwasher":"Lave-vaisselle","furn.sym_washer":"Lave-linge","furn.sym_dryer":"Sèche-linge","furn.sym_tv":"Téléviseur","furn.sym_tv_wall":"Téléviseur mural","furn.sym_ac":"Climatiseur","furn.sym_water_heater":"Chauffe-eau","furn.sym_toilet":"Toilettes","furn.sym_toilet_built_in":"Toilettes suspendues","furn.sym_bathtub":"Baignoire","furn.sym_bathtub_corner":"Baignoire d'angle","furn.sym_shower":"Douche","furn.sym_sink":"Lavabo","furn.sym_kitchen_sink":"Évier de cuisine","furn.sym_kitchen_sink_double":"Évier de cuisine double","furn.sym_bidet":"Bidet","furn.sym_bidet_built_in":"Bidet suspendu","furn.sym_stairs":"Escalier","furn.sym_fireplace":"Cheminée","furn.sym_plant":"Plante","furn.sym_rug":"Tapis"};const r="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";export{e as dictionary,r as fingerprint};
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";var e={"furn.title":"Bibliothèque de mobilier","furn.symbol":"Symbole","furn.group_furniture":"Mobilier","furn.group_appliance":"Électroménager","furn.group_sanitary":"Sanitaires","furn.group_other":"Autre","furn.width":"Largeur","furn.depth":"Profondeur","furn.back_to_categories":"Toutes les catégories","furn.pick_hint":"Choisissez un symbole, puis cliquez sur le plan.","furn.place_hint":"Cliquez sur le plan — le meuble se place contre le mur le plus proche. Maj permet un placement libre.","furn.cat_air_conditioner":"Climatiseurs","furn.cat_armchair":"Fauteuils","furn.cat_bathtub":"Baignoires","furn.cat_bed":"Lits","furn.cat_bidet":"Bidets","furn.cat_boiler":"Chauffe-eau","furn.cat_chair":"Chaises","furn.cat_coffee_table":"Tables basses","furn.cat_computer":"Ordinateur","furn.cat_cooktop":"Plaques de cuisson","furn.cat_dining_table":"Tables à manger","furn.cat_dishwasher":"Lave-vaisselle","furn.cat_dryer":"Sèche-linge","furn.cat_exercise":"Équipement de sport","furn.cat_fireplace":"Cheminées","furn.cat_fridge":"Réfrigérateurs","furn.cat_hood":"Hotte","furn.cat_kitchen_cabinet":"Meubles de cuisine","furn.cat_kitchen_sink":"Éviers de cuisine","furn.cat_nightstand":"Meubles","furn.cat_oven":"Four","furn.cat_plant":"Plantes","furn.cat_rug":"Tapis","furn.cat_shelving":"Étagères","furn.cat_shower":"Douches","furn.cat_sink":"Lavabos","furn.cat_sofa":"Canapés","furn.cat_stairs":"Escaliers","furn.cat_toilet":"Toilettes","furn.cat_tv":"Téléviseurs","furn.cat_wardrobe":"Armoires","furn.cat_washer":"Lave-linge","furn.cat_work_table":"Bureaux","furn.sym_exercise":"Équipement de sport","furn.sym_computer":"Ordinateur","furn.sym_hood":"Hotte","furn.sym_oven":"Four","furn.sym_sofa":"Canapé","furn.sym_sofa_three_seat":"Canapé trois places","furn.sym_sofa_corner_right":"Canapé d'angle droit","furn.sym_armchair":"Fauteuil","furn.sym_armchair_office":"Chaise de bureau","furn.sym_coffee_table":"Table basse","furn.sym_coffee_table_round":"Table basse ronde","furn.sym_coffee_table_oval":"Table basse ovale","furn.sym_coffee_table_rounded":"Table basse arrondie","furn.sym_table_dining":"Table à manger","furn.sym_table_round":"Table ronde","furn.sym_table_dining_oval":"Table à manger ovale","furn.sym_table_dining_rounded":"Table à manger arrondie","furn.sym_chair":"Chaise","furn.sym_chair_bar":"Tabouret de bar","furn.sym_desk":"Bureau","furn.sym_desk_corner":"Bureau d'angle","furn.sym_bed_double":"Lit double","furn.sym_bed_single":"Lit simple","furn.sym_nightstand":"Table de chevet","furn.sym_cabinet_tv":"Meuble TV","furn.sym_cabinet_shoe":"Meuble à chaussures","furn.sym_cabinet_sink":"Meuble sous lavabo","furn.sym_wardrobe":"Armoire","furn.sym_bookshelf":"Bibliothèque","furn.sym_wall_unit":"Élément mural","furn.sym_kitchen_floor":"Module de cuisine bas","furn.sym_kitchen_floor_corner":"Module de cuisine bas d'angle","furn.sym_kitchen_wall":"Module de cuisine haut","furn.sym_kitchen_wall_corner":"Module de cuisine haut d'angle","furn.sym_shelf_floor":"Étagère au sol","furn.sym_shelf_wall":"Étagère murale","furn.sym_fridge":"Réfrigérateur","furn.sym_stove":"Cuisinière","furn.sym_cooktop_two":"Plaque à deux feux","furn.sym_dishwasher":"Lave-vaisselle","furn.sym_washer":"Lave-linge","furn.sym_dryer":"Sèche-linge","furn.sym_tv":"Téléviseur","furn.sym_tv_wall":"Téléviseur mural","furn.sym_ac":"Climatiseur","furn.sym_water_heater":"Chauffe-eau","furn.sym_toilet":"Toilettes","furn.sym_toilet_built_in":"Toilettes suspendues","furn.sym_bathtub":"Baignoire","furn.sym_bathtub_corner":"Baignoire d'angle","furn.sym_shower":"Douche","furn.sym_sink":"Lavabo","furn.sym_kitchen_sink":"Évier de cuisine","furn.sym_kitchen_sink_double":"Évier de cuisine double","furn.sym_bidet":"Bidet","furn.sym_bidet_built_in":"Bidet suspendu","furn.sym_stairs":"Escalier","furn.sym_fireplace":"Cheminée","furn.sym_plant":"Plante","furn.sym_rug":"Tapis"};const r="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";export{e as dictionary,r as fingerprint};
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";var e={title:"Zigbee-Verbindungen",toggle:"Zigbee-Verbindungen beim Darüberfahren über ein Gerät anzeigen",hint:"Es werden nur beobachtete direkte Nachbarn angezeigt. Beim Darüberfahren werden keine Daten abgerufen.",help:"Zeigt, mit welchen Zigbee-Geräten jedes Gerät direkt verbunden ist. Die Linienfarbe ist die Verbindungsqualität (LQI) auf derselben Skala wie der LQI-Wert des Geräts: rot bei 40 und darunter, grün bei 180 und darüber. Eine gestrichelte Linie bedeutet, dass die Qualität nicht gemeldet wurde. Ein Pfeil zeigt auf das nächste Gerät auf dem Weg zum Koordinator: ein Pfeil führt hinaus, eingehende Pfeile sind die Geräte, die über dieses Gerät routen. Eine Linie ohne Pfeil ist ein Ersatznachbar. Das ist der Routenbaum, den House Plan bildet, nicht der Weg eines Pakets in diesem Moment: eine Beschriftung am Pfeilende bedeutet, dass das Ziel nicht auf diesem Plan liegt, und ein fehlender Pfeil, dass die Route unbekannt ist. Die Daten stammen aus dem letzten Laden und veralten; Verbindungen sind nur mit der Maus sichtbar.",help_aria:"Hilfe: Zigbee-Verbindungen",admin_only:"Topologiedaten sind nur für Home-Assistant-Administratoren verfügbar.",save_first:"Speichere diese Einstellung, bevor Topologiedaten geladen werden.",zha:"ZHA",zha_read:"ZHA-Daten lesen",zha_hint:"Liest den ZHA-Topologiecache; es wird kein Funkscan gestartet.",z2m:"Zigbee2MQTT",z2m_topics:"Basistopics (eines pro Zeile)",z2m_update:"Karte aktualisieren",z2m_warning:"Ein Netzwerkscan kann 10 Sekunden bis 2 Minuten dauern und Zigbee vorübergehend verlangsamen.",status_idle:"Daten nicht geladen",status_loading:"Wird geladen…",status_ready:"Empfangen: {time}",status_stale:"Empfangen: {time} · veraltet",status_partial:"Empfangen: {time} · einige Knoten fehlen",status_no_links:"Empfangen: {time} · keine beobachteten Verbindungen gefunden",error_permission:"Administratorrechte sind erforderlich.",error_unsupported:"Dieser Anbieter oder die Home-Assistant-API ist nicht verfügbar.",error_timeout:"Der Anbieter hat nicht rechtzeitig geantwortet.",error_invalid_topic:"Prüfe das Zigbee2MQTT-Basistopic.",error_invalid_payload:"Der Anbieter hat nicht unterstützte Topologiedaten geliefert.",error_provider:"Topologiedaten konnten nicht geladen werden.",remote_count:"+{n} in anderen Bereichen",route_device_not_on_plan:"Gerät ist nicht im Plan",route_coordinator_not_on_plan:"Koordinator ist nicht im Plan",route_other_space:"anderer Bereich"};const n="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";export{e as dictionary,n as fingerprint};
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";var e={title:"Zigbee-Verbindungen",toggle:"Zigbee-Verbindungen beim Darüberfahren über ein Gerät anzeigen",hint:"Es werden nur beobachtete direkte Nachbarn angezeigt. Beim Darüberfahren werden keine Daten abgerufen.",help:"Zeigt, mit welchen Zigbee-Geräten jedes Gerät direkt verbunden ist. Die Linienfarbe ist die Verbindungsqualität (LQI) auf derselben Skala wie der LQI-Wert des Geräts: rot bei 40 und darunter, grün bei 180 und darüber. Eine gestrichelte Linie bedeutet, dass die Qualität nicht gemeldet wurde. Ein Pfeil zeigt auf das nächste Gerät auf dem Weg zum Koordinator: ein Pfeil führt hinaus, eingehende Pfeile sind die Geräte, die über dieses Gerät routen. Eine Linie ohne Pfeil ist ein Ersatznachbar. Das ist der Routenbaum, den House Plan bildet, nicht der Weg eines Pakets in diesem Moment: eine Beschriftung am Pfeilende bedeutet, dass das Ziel nicht auf diesem Plan liegt, und ein fehlender Pfeil, dass die Route unbekannt ist. Die Daten stammen aus dem letzten Laden und veralten; Verbindungen sind nur mit der Maus sichtbar.",help_aria:"Hilfe: Zigbee-Verbindungen",admin_only:"Topologiedaten sind nur für Home-Assistant-Administratoren verfügbar.",save_first:"Speichere diese Einstellung, bevor Topologiedaten geladen werden.",zha:"ZHA",zha_read:"ZHA-Daten lesen",zha_hint:"Liest den ZHA-Topologiecache; es wird kein Funkscan gestartet.",z2m:"Zigbee2MQTT",z2m_topics:"Basistopics (eines pro Zeile)",z2m_update:"Karte aktualisieren",z2m_warning:"Ein Netzwerkscan kann 10 Sekunden bis 2 Minuten dauern und Zigbee vorübergehend verlangsamen.",status_idle:"Daten nicht geladen",status_loading:"Wird geladen…",status_ready:"Empfangen: {time}",status_stale:"Empfangen: {time} · veraltet",status_partial:"Empfangen: {time} · einige Knoten fehlen",status_no_links:"Empfangen: {time} · keine beobachteten Verbindungen gefunden",error_permission:"Administratorrechte sind erforderlich.",error_unsupported:"Dieser Anbieter oder die Home-Assistant-API ist nicht verfügbar.",error_timeout:"Der Anbieter hat nicht rechtzeitig geantwortet.",error_invalid_topic:"Prüfe das Zigbee2MQTT-Basistopic.",error_invalid_payload:"Der Anbieter hat nicht unterstützte Topologiedaten geliefert.",error_provider:"Topologiedaten konnten nicht geladen werden.",remote_count:"+{n} in anderen Bereichen",route_device_not_on_plan:"Gerät ist nicht im Plan",route_coordinator_not_on_plan:"Koordinator ist nicht im Plan",route_other_space:"anderer Bereich"};const n="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";export{e as dictionary,n as fingerprint};
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";var e={title:"Liens Zigbee",toggle:"Afficher les liens Zigbee au survol d’un appareil",hint:"Seuls les voisins directs observés sont affichés. Le survol ne lance aucune requête.",help:"Montre avec quels appareils Zigbee chaque appareil communique directement. La couleur de la ligne est la qualité du lien (LQI) sur la même échelle que l'indicateur LQI de l'appareil : rouge à 40 et en dessous, vert à 180 et au-dessus. Une ligne pointillée signifie que la qualité n'a pas été communiquée. Une flèche pointe vers l'appareil suivant sur le chemin du coordinateur : une flèche part, les flèches entrantes sont les appareils qui passent par celui-ci. Une ligne sans flèche est un voisin de secours. C'est l'arbre de routes que construit House Plan, pas le trajet d'un paquet à cet instant : une étiquette au bout d'une flèche signifie que sa cible n'est pas sur ce plan, et l'absence de flèche que la route est inconnue. Les données proviennent du dernier chargement et vieillissent ; les liens ne sont visibles qu'à la souris.",help_aria:"Aide : liens Zigbee",admin_only:"Les données de topologie sont réservées aux administrateurs Home Assistant.",save_first:"Enregistrez ce réglage avant de charger les données de topologie.",zha:"ZHA",zha_read:"Lire les données ZHA",zha_hint:"Lit la topologie en cache de ZHA sans lancer d’analyse radio.",z2m:"Zigbee2MQTT",z2m_topics:"Topics de base (un par ligne)",z2m_update:"Actualiser la carte",z2m_warning:"L’analyse de la carte réseau peut durer de 10 secondes à 2 minutes et réduire temporairement la réactivité de Zigbee.",status_idle:"Données non chargées",status_loading:"Chargement…",status_ready:"Reçu : {time}",status_stale:"Reçu : {time} · obsolète",status_partial:"Reçu : {time} · certains nœuds sont omis",status_no_links:"Reçu : {time} · aucun lien observé trouvé",error_permission:"Les droits administrateur sont requis.",error_unsupported:"Ce fournisseur ou l’API Home Assistant n’est pas disponible.",error_timeout:"Le fournisseur n’a pas répondu à temps.",error_invalid_topic:"Vérifiez le topic de base Zigbee2MQTT.",error_invalid_payload:"Le fournisseur a renvoyé des données de topologie non prises en charge.",error_provider:"Impossible de charger les données de topologie.",remote_count:"+{n} dans d’autres espaces",route_device_not_on_plan:"appareil absent du plan",route_coordinator_not_on_plan:"coordinateur absent du plan",route_other_space:"autre espace"};const s="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";export{e as dictionary,s as fingerprint};
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";var e={title:"Liens Zigbee",toggle:"Afficher les liens Zigbee au survol d’un appareil",hint:"Seuls les voisins directs observés sont affichés. Le survol ne lance aucune requête.",help:"Montre avec quels appareils Zigbee chaque appareil communique directement. La couleur de la ligne est la qualité du lien (LQI) sur la même échelle que l'indicateur LQI de l'appareil : rouge à 40 et en dessous, vert à 180 et au-dessus. Une ligne pointillée signifie que la qualité n'a pas été communiquée. Une flèche pointe vers l'appareil suivant sur le chemin du coordinateur : une flèche part, les flèches entrantes sont les appareils qui passent par celui-ci. Une ligne sans flèche est un voisin de secours. C'est l'arbre de routes que construit House Plan, pas le trajet d'un paquet à cet instant : une étiquette au bout d'une flèche signifie que sa cible n'est pas sur ce plan, et l'absence de flèche que la route est inconnue. Les données proviennent du dernier chargement et vieillissent ; les liens ne sont visibles qu'à la souris.",help_aria:"Aide : liens Zigbee",admin_only:"Les données de topologie sont réservées aux administrateurs Home Assistant.",save_first:"Enregistrez ce réglage avant de charger les données de topologie.",zha:"ZHA",zha_read:"Lire les données ZHA",zha_hint:"Lit la topologie en cache de ZHA sans lancer d’analyse radio.",z2m:"Zigbee2MQTT",z2m_topics:"Topics de base (un par ligne)",z2m_update:"Actualiser la carte",z2m_warning:"L’analyse de la carte réseau peut durer de 10 secondes à 2 minutes et réduire temporairement la réactivité de Zigbee.",status_idle:"Données non chargées",status_loading:"Chargement…",status_ready:"Reçu : {time}",status_stale:"Reçu : {time} · obsolète",status_partial:"Reçu : {time} · certains nœuds sont omis",status_no_links:"Reçu : {time} · aucun lien observé trouvé",error_permission:"Les droits administrateur sont requis.",error_unsupported:"Ce fournisseur ou l’API Home Assistant n’est pas disponible.",error_timeout:"Le fournisseur n’a pas répondu à temps.",error_invalid_topic:"Vérifiez le topic de base Zigbee2MQTT.",error_invalid_payload:"Le fournisseur a renvoyé des données de topologie non prises en charge.",error_provider:"Impossible de charger les données de topologie.",remote_count:"+{n} dans d’autres espaces",route_device_not_on_plan:"appareil absent du plan",route_coordinator_not_on_plan:"coordinateur absent du plan",route_other_space:"autre espace"};const s="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";export{e as dictionary,s as fingerprint};
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";var e={title:"Связи Zigbee",toggle:"Показывать связи Zigbee при наведении на устройство",hint:"Показываются только наблюдаемые прямые соседи. Наведение ничего не запрашивает.",help:"Показывает, с какими устройствами Zigbee каждое связано напрямую. Цвет линии — качество связи (LQI) по той же шкале, что у показателя LQI устройства: красный при 40 и ниже, зелёный при 180 и выше. Пунктир — качество не сообщено. Стрелка ведёт к следующему устройству по пути к координатору: исходящая одна, входящие — те, кто ходит через это устройство. Линия без стрелки — запасной сосед. Это дерево маршрутов, которое строит House Plan, а не путь пакета в эту секунду: подпись на конце стрелки значит, что цель не на этом плане, а отсутствие стрелки — что путь неизвестен. Данные из последней загрузки и устаревают, связи видны только при работе мышью.",help_aria:"Справка: связи Zigbee",admin_only:"Данные топологии доступны только администраторам Home Assistant.",save_first:"Сохраните настройку перед загрузкой данных топологии.",zha:"ZHA",zha_read:"Прочитать данные ZHA",zha_hint:"Читает сохранённую топологию ZHA и не запускает радио-сканирование.",z2m:"Zigbee2MQTT",z2m_topics:"Базовые топики (по одному в строке)",z2m_update:"Обновить карту",z2m_warning:"Сканирование карты сети занимает от 10 секунд до 2 минут и временно может снизить отзывчивость Zigbee.",status_idle:"Данные не загружены",status_loading:"Загрузка…",status_ready:"Получено: {time}",status_stale:"Получено: {time} · данные устарели",status_partial:"Получено: {time} · часть узлов не показана",status_no_links:"Получено: {time} · наблюдаемые связи не найдены",error_permission:"Нужны права администратора.",error_unsupported:"Провайдер или API Home Assistant недоступен.",error_timeout:"Провайдер не ответил вовремя.",error_invalid_topic:"Проверьте базовый топик Zigbee2MQTT.",error_invalid_payload:"Провайдер вернул неподдерживаемые данные топологии.",error_provider:"Не удалось загрузить данные топологии.",remote_count:"+{n} в других пространствах",route_device_not_on_plan:"устройства нет на плане",route_coordinator_not_on_plan:"координатора нет на плане",route_other_space:"другое пространство"};const t="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";export{e as dictionary,t as fingerprint};
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";var e={title:"Связи Zigbee",toggle:"Показывать связи Zigbee при наведении на устройство",hint:"Показываются только наблюдаемые прямые соседи. Наведение ничего не запрашивает.",help:"Показывает, с какими устройствами Zigbee каждое связано напрямую. Цвет линии — качество связи (LQI) по той же шкале, что у показателя LQI устройства: красный при 40 и ниже, зелёный при 180 и выше. Пунктир — качество не сообщено. Стрелка ведёт к следующему устройству по пути к координатору: исходящая одна, входящие — те, кто ходит через это устройство. Линия без стрелки — запасной сосед. Это дерево маршрутов, которое строит House Plan, а не путь пакета в эту секунду: подпись на конце стрелки значит, что цель не на этом плане, а отсутствие стрелки — что путь неизвестен. Данные из последней загрузки и устаревают, связи видны только при работе мышью.",help_aria:"Справка: связи Zigbee",admin_only:"Данные топологии доступны только администраторам Home Assistant.",save_first:"Сохраните настройку перед загрузкой данных топологии.",zha:"ZHA",zha_read:"Прочитать данные ZHA",zha_hint:"Читает сохранённую топологию ZHA и не запускает радио-сканирование.",z2m:"Zigbee2MQTT",z2m_topics:"Базовые топики (по одному в строке)",z2m_update:"Обновить карту",z2m_warning:"Сканирование карты сети занимает от 10 секунд до 2 минут и временно может снизить отзывчивость Zigbee.",status_idle:"Данные не загружены",status_loading:"Загрузка…",status_ready:"Получено: {time}",status_stale:"Получено: {time} · данные устарели",status_partial:"Получено: {time} · часть узлов не показана",status_no_links:"Получено: {time} · наблюдаемые связи не найдены",error_permission:"Нужны права администратора.",error_unsupported:"Провайдер или API Home Assistant недоступен.",error_timeout:"Провайдер не ответил вовремя.",error_invalid_topic:"Проверьте базовый топик Zigbee2MQTT.",error_invalid_payload:"Провайдер вернул неподдерживаемые данные топологии.",error_provider:"Не удалось загрузить данные топологии.",remote_count:"+{n} в других пространствах",route_device_not_on_plan:"устройства нет на плане",route_coordinator_not_on_plan:"координатора нет на плане",route_other_space:"другое пространство"};const a="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";export{e as dictionary,a as fingerprint};
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";import{n as t,c as n}from"./zigbee-topology-D8kIFY6J.js";import{aP as e}from"./houseplan-card-BWxNZe40.js";import"./namespace-language-Bj-cxtbx.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 m(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 h(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,h=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,h=n}),b=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&m(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=m(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&&h?.(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,h as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot};
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";import{n as t,c as n}from"./zigbee-topology-BG_a6cIb.js";import{aP as e}from"./houseplan-card-BOdiRDFR.js";import"./namespace-language-D86tJUeD.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 m(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 h(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,h=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,h=n}),b=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&m(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=m(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&&h?.(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,h as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot};
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";try{await import("./houseplan-assets/houseplan-card-BWxNZe40.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__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";try{await import("./houseplan-assets/houseplan-card-BOdiRDFR.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)}
|
||||
|
||||
@@ -20,5 +20,5 @@
|
||||
"issue_tracker": "https://github.com/Matysh/houseplan-card/issues",
|
||||
"requirements": [],
|
||||
"single_config_entry": true,
|
||||
"version": "1.79.0-beta.5"
|
||||
"version": "1.79.0"
|
||||
}
|
||||
|
||||
@@ -21,11 +21,17 @@
|
||||
* profile's camera scenario, benchmark_large_house.mjs) and the longest Long
|
||||
* Task of the camera — the scenario and the 100-step series, retained heap
|
||||
* after 20 A→B→C→A cycles over a warm cycle (same GC protocol).
|
||||
* #789 additionally observes cameraFullCycleLongTaskMaxMs continuously through
|
||||
* full-quality restoration, immediate restart and a 500 ms tail, with the same
|
||||
* camera ceiling. Historical camera windows remain unchanged; each row retains
|
||||
* raw full-cycle Long Tasks and phase boundaries for audit.
|
||||
*
|
||||
* Counters (exact, every sample): geometry/visibility recomputes over 100
|
||||
* unrelated HA ticks, 100 pan/zoom steps and a colour-only change (0); the
|
||||
* three LED caches of the shown space — shapes ≤ 50, visibility ≤ 50,
|
||||
* retained per-emitter fans ≤ 2500 — identical after every cycle (growth 0);
|
||||
* compact visibility paths ≤ 2500 and retained path text ≤ 4 Mi characters
|
||||
* (≤ 8 MiB UTF-16) — identical after every cycle (growth 0). The true fan
|
||||
* count remains diagnostic: #788 removed lossy endpoint/vertex thinning;
|
||||
* after disconnect 0 retained entries and 0 live LED timers/observers; a
|
||||
* late import after disconnect restores nothing (one extra cold run with the
|
||||
* runtime response delayed); LED chunk requests (none without strips, never
|
||||
@@ -41,6 +47,8 @@ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { launch } from './serve.mjs';
|
||||
import { makeLedStripsFixture } from './performance/led-strips-fixture.mjs';
|
||||
import { ledChunkRequestName } from './performance/led-chunk-request.mjs';
|
||||
import { cameraCycleFailures, cameraCycleSampleFailures, finishLedCameraCycle } from './performance/led-camera-cycle.mjs';
|
||||
|
||||
const valueArg = (name) => process.argv.find((arg) => arg.startsWith(`--${name}=`))?.slice(name.length + 3);
|
||||
const warmupOnly = process.argv.includes('--warmup-only');
|
||||
@@ -115,6 +123,7 @@ function installLedLifecycleTracker() {
|
||||
/** The page side of one mount: the same fixture host as before, shared by the sample and the late-import run. */
|
||||
function pageHost() {
|
||||
return `
|
||||
window.__finishLedCameraCycle = ${finishLedCameraCycle.toString()};
|
||||
window.__ledHost = (fixture) => {
|
||||
const card = document.createElement('houseplan-card');
|
||||
card.setConfig({ type: 'custom:houseplan-card', title: 'LED strips', icon_size: 3.4 });
|
||||
@@ -145,7 +154,7 @@ function pageHost() {
|
||||
window.__ledStats = async (card) => {
|
||||
const url = performance.getEntriesByType('resource').map((entry) => entry.name)
|
||||
.find((name) => /led-strip-runtime-[^/]+\\.js/.test(name));
|
||||
if (!url) return { shapes: 0, visibility: 0, sources: 0, recomputes: 0, loaded: false };
|
||||
if (!url) return { shapes: 0, visibility: 0, sources: 0, visibilityPaths: 0, pathChars: 0, recomputes: 0, loaded: false };
|
||||
const runtime = await import(url);
|
||||
return { ...runtime.ledStats(card), loaded: true };
|
||||
};
|
||||
@@ -163,8 +172,8 @@ async function sample() {
|
||||
await page.emulateMedia({ reducedMotion: 'reduce' });
|
||||
const requests = [];
|
||||
page.on('request', (request) => {
|
||||
const name = request.url().replace(/.*\//, '').replace(/\?.*$/, '');
|
||||
if (/^led-strip-(runtime|field|editor)-/.test(name)) requests.push(name.replace(/-[^-]+\.js$/, ''));
|
||||
const name = ledChunkRequestName(request.url());
|
||||
if (name) requests.push(name);
|
||||
});
|
||||
try {
|
||||
await page.evaluate(installLedLifecycleTracker);
|
||||
@@ -179,7 +188,7 @@ async function sample() {
|
||||
await sleep(5);
|
||||
}
|
||||
};
|
||||
const longTasks = () => {
|
||||
const longTasks = (onEntries) => {
|
||||
const entries = [];
|
||||
const observer = new PerformanceObserver((list) => entries.push(...list.getEntries()));
|
||||
observer.observe({ type: 'longtask', buffered: false });
|
||||
@@ -187,6 +196,7 @@ async function sample() {
|
||||
await sleep(0);
|
||||
entries.push(...observer.takeRecords());
|
||||
observer.disconnect();
|
||||
onEntries?.(entries.map(({ startTime, duration }) => ({ startTime, duration })));
|
||||
return Number(Math.max(0, ...entries.map((entry) => entry.duration)).toFixed(2));
|
||||
};
|
||||
};
|
||||
@@ -247,6 +257,10 @@ async function sample() {
|
||||
const wheel = (deltaY) => stage.dispatchEvent(new WheelEvent('wheel', {
|
||||
deltaY, clientX: rect.left + rect.width / 2, clientY: rect.top + rect.height / 2, bubbles: true, cancelable: true,
|
||||
}));
|
||||
// Keep the old pan/series windows intact. This separate observer also
|
||||
// covers the first quality switch, both restores and the quick restart.
|
||||
const cameraFullCycle = { startTime: performance.now(), entries: [], phases: {} };
|
||||
const stopFullCycle = longTasks(entries => { cameraFullCycle.entries = entries; });
|
||||
const stopPan = longTasks();
|
||||
const panStarted = performance.now();
|
||||
wheel(-120);
|
||||
@@ -263,6 +277,14 @@ async function sample() {
|
||||
}
|
||||
await frame();
|
||||
const cameraSeriesLongTaskMaxMs = await stopSeries();
|
||||
cameraFullCycle.phases.seriesEnd = performance.now();
|
||||
Object.assign(cameraFullCycle.phases, await window.__finishLedCameraCycle({
|
||||
fullQuality: () => !card.hasAttribute('data-led-zoom-quality'),
|
||||
input: async () => { wheel(-60); await card.updateComplete; },
|
||||
frame, now: () => performance.now(), sleep,
|
||||
}));
|
||||
const cameraFullCycleLongTaskMaxMs = await stopFullCycle();
|
||||
cameraFullCycle.endTime = performance.now();
|
||||
const afterCamera = await stats();
|
||||
// A colour-only change rebuilds no geometry or visibility.
|
||||
host.setStates({ ...host.states, ...Object.fromEntries(floorLights.map((id) => [id,
|
||||
@@ -279,8 +301,8 @@ async function sample() {
|
||||
if (strips) await until(() => ledStable());
|
||||
}
|
||||
await frame();
|
||||
const { shapes, visibility, sources } = await stats();
|
||||
return { shapes, visibility, sources };
|
||||
const { shapes, visibility, sources, visibilityPaths, pathChars } = await stats();
|
||||
return { shapes, visibility, sources, visibilityPaths, pathChars };
|
||||
};
|
||||
await cycle();
|
||||
await gc();
|
||||
@@ -295,9 +317,10 @@ async function sample() {
|
||||
await sleep(600);
|
||||
const afterDisconnect = await stats();
|
||||
const liveAfterDisconnect = window.__ledLiveCounts();
|
||||
const key = (entry) => `${entry.shapes}/${entry.visibility}/${entry.sources}`;
|
||||
const key = (entry) => `${entry.shapes}/${entry.visibility}/${entry.sources}/${entry.visibilityPaths}/${entry.pathChars}`;
|
||||
return {
|
||||
firstStableRenderMs, warmSpaceReadyMs, stateUpdateMs, panZoomMs, panZoomLongTaskMaxMs, cameraSeriesLongTaskMaxMs,
|
||||
cameraFullCycleLongTaskMaxMs, cameraFullCycle,
|
||||
retainedHeapBytes: heapBefore == null || heapAfter == null ? null : Math.max(0, heapAfter - heapBefore),
|
||||
counters: strips ? {
|
||||
recomputesOnHaTicks: afterTicks.recomputes - before.recomputes,
|
||||
@@ -306,10 +329,13 @@ async function sample() {
|
||||
shapes: Math.max(...cycleStats.map((entry) => entry.shapes)),
|
||||
visibility: Math.max(...cycleStats.map((entry) => entry.visibility)),
|
||||
sources: Math.max(...cycleStats.map((entry) => entry.sources)),
|
||||
visibilityPaths: Math.max(...cycleStats.map((entry) => entry.visibilityPaths)),
|
||||
pathChars: Math.max(...cycleStats.map((entry) => entry.pathChars)),
|
||||
cacheGrowthOverCycles: new Set(cycleStats.map(key)).size - 1,
|
||||
} : null,
|
||||
disconnect: {
|
||||
retained: afterDisconnect.shapes + afterDisconnect.visibility + afterDisconnect.sources,
|
||||
retained: afterDisconnect.shapes + afterDisconnect.visibility + afterDisconnect.sources
|
||||
+ afterDisconnect.visibilityPaths + afterDisconnect.pathChars,
|
||||
liveBefore: liveBeforeDisconnect,
|
||||
live: liveAfterDisconnect.timers + liveAfterDisconnect.frames + liveAfterDisconnect.observers,
|
||||
},
|
||||
@@ -359,7 +385,7 @@ async function lateImport() {
|
||||
// The chunk the card asked for, by its URL: the same module instance.
|
||||
const late = await page.evaluate(async (url) => {
|
||||
const runtime = url ? await import(url) : null;
|
||||
return { stats: runtime ? { ...runtime.ledStats(window.__lateCard), loaded: true } : { shapes: 0, visibility: 0, sources: 0, loaded: false },
|
||||
return { stats: runtime ? { ...runtime.ledStats(window.__lateCard), loaded: true } : { shapes: 0, visibility: 0, sources: 0, visibilityPaths: 0, pathChars: 0, loaded: false },
|
||||
live: window.__ledLiveCounts() };
|
||||
}, runtimeUrl);
|
||||
return { ...result, ...late, held };
|
||||
@@ -404,7 +430,7 @@ const metric = (name) => {
|
||||
return { median: values.length ? at(0.5) : null, p95: values.length ? at(0.95) : null, samples: values };
|
||||
};
|
||||
const METRICS = ['firstStableRenderMs', 'warmSpaceReadyMs', 'stateUpdateMs', 'panZoomMs', 'panZoomLongTaskMaxMs',
|
||||
'cameraSeriesLongTaskMaxMs', 'retainedHeapBytes'];
|
||||
'cameraSeriesLongTaskMaxMs', 'cameraFullCycleLongTaskMaxMs', 'retainedHeapBytes'];
|
||||
const report = {
|
||||
profile: PROFILE, size, strips: STRIPS, points: POINTS, sourceSha, chromium,
|
||||
samples: rows.length, warmups: discarded, cycles: CYCLES, viewport: { width: 1440, height: 1000 }, dpr: 1,
|
||||
@@ -423,6 +449,8 @@ if (!partial && (report.samples < MIN_SAMPLES || report.warmups < 1)) {
|
||||
failures.push(`${report.samples} samples after ${report.warmups} warmups: the profile needs ≥ ${MIN_SAMPLES} after ≥ 1`);
|
||||
}
|
||||
const limits = budgets.sizes[size] || {};
|
||||
failures.push(...cameraCycleSampleFailures(rows));
|
||||
failures.push(...cameraCycleFailures(report.metrics.cameraFullCycleLongTaskMaxMs, limits.cameraSeriesLongTaskMaxMs));
|
||||
for (const [name, limit] of Object.entries(limits)) {
|
||||
for (const stat of ['median', 'p95']) {
|
||||
const value = report.metrics[name]?.[stat];
|
||||
@@ -440,14 +468,16 @@ if (STRIPS) {
|
||||
for (const key of ['recomputesOnHaTicks', 'recomputesOnCamera', 'recomputesOnColour', 'cacheGrowthOverCycles']) {
|
||||
if (counters[key] !== 0) failures.push(`${key} = ${counters[key]}, expected 0`);
|
||||
}
|
||||
for (const key of ['shapes', 'visibility', 'sources']) {
|
||||
if (counters[key] > caches[key]) failures.push(`${key} cache ${counters[key]} > ${caches[key]}`);
|
||||
for (const key of ['shapes', 'visibility', 'visibilityPaths', 'pathChars']) {
|
||||
if (!Number.isFinite(counters[key])) failures.push(`${key} cache metric missing`);
|
||||
else if (counters[key] > caches[key]) failures.push(`${key} cache ${counters[key]} > ${caches[key]}`);
|
||||
}
|
||||
}
|
||||
if (late) {
|
||||
if (!late.held || !late.stats.loaded) failures.push('late import: the runtime response was never held or never landed');
|
||||
if (late.stripesAfterLoad !== late.stripesBeforeLoad) failures.push('late import rendered the gone card');
|
||||
const retained = late.stats.shapes + late.stats.visibility + late.stats.sources;
|
||||
const retained = late.stats.shapes + late.stats.visibility + late.stats.sources
|
||||
+ late.stats.visibilityPaths + late.stats.pathChars;
|
||||
if (retained) failures.push(`late import restored ${retained} cache entries`);
|
||||
if (late.live.timers + late.live.frames + late.live.observers) failures.push('late import left LED timers/observers');
|
||||
} else if (!skipLateImport) failures.push('late import not measured');
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"schema": 3,
|
||||
"matrixVersion": 71,
|
||||
"acceptedAt": "2026-10-03T13:46:15.484Z",
|
||||
"sourceFingerprint": "862ebd8b95e7cb7603be06fe8af2c8dc52e5cfc3006a3ca79f03c0fb8441f8fb",
|
||||
"acceptedAt": "2026-10-03T21:10:10.375Z",
|
||||
"sourceFingerprint": "660dd7fd37337d645e3b1d038e725b1f2cae1c2ac628726925ff15d1744aa88f",
|
||||
"chromium": "151.0.7922.34",
|
||||
"capturedOn": "linux",
|
||||
"acceptedOn": "linux",
|
||||
@@ -10,20 +10,20 @@
|
||||
"platform": "linux",
|
||||
"arch": "x64",
|
||||
"chromium": "151.0.7922.34",
|
||||
"buildFingerprint": "862ebd8b95e7cb7603be06fe8af2c8dc52e5cfc3006a3ca79f03c0fb8441f8fb",
|
||||
"buildFingerprint": "660dd7fd37337d645e3b1d038e725b1f2cae1c2ac628726925ff15d1744aa88f",
|
||||
"ci": null
|
||||
},
|
||||
"localAttestation": {
|
||||
"schema": "houseplan-golden-wsl/v1",
|
||||
"sha256": "1c7933e47079b24064413013a999ed10376a0aad05f8c8f57f1c683bb64c6ae7",
|
||||
"artifactSha256": "6c7ec7ee5f516ab06e596a88594fd396c83ebe13f018cc36ed7b8d305afe94ff",
|
||||
"createdAt": "2026-10-03T13:46:03.862Z",
|
||||
"sha256": "aaf9ebcb3613071dc8048506f68a889dab6bdea8e62ba5ce677b12d611e9eae7",
|
||||
"artifactSha256": "6f22f397e53e147b8c7cf41ec1b0063eb966fbb57ef3d0a10289728cb7af1f9f",
|
||||
"createdAt": "2026-10-03T21:03:28.942Z",
|
||||
"source": {
|
||||
"repository": "Matysh/houseplan-card",
|
||||
"branch": "issue/786-led-glow-smooth-ends",
|
||||
"commit": "959876816764d5af8eb00a84c983dad1d16ad86e",
|
||||
"tree": "d16c08f4238d3b01d64b036ce49a63d7caae7049",
|
||||
"remoteSha": "959876816764d5af8eb00a84c983dad1d16ad86e",
|
||||
"branch": "issue/790-led-core-source-color",
|
||||
"commit": "181b61ae8e6adc967dde8c2a87799152ec777956",
|
||||
"tree": "ec94fddbfd9acbda68ceef4c158b283f7506062a",
|
||||
"remoteSha": "181b61ae8e6adc967dde8c2a87799152ec777956",
|
||||
"clean": true,
|
||||
"status": ""
|
||||
},
|
||||
@@ -241,11 +241,12 @@
|
||||
"card-editor-invalid-default-floor-light-en": "dbf251ece014cfee252e0dcb2d7c00eda66aa4b5bd125e4c83e248d4319d24b3",
|
||||
"card-editor-invalid-default-floor-dark-ru": "422b74bb391b87e383350247ef1bc1f10ca48c8f6fe418f75bde94e62e3b8bc0",
|
||||
"led-strip-design-reference-off-light": "1a558111dbeb1962b10f47baad20b4689248a347510261152f0d6196906044fd",
|
||||
"led-strip-design-reference-on-light": "16bfb46f2a90c57e95f1307e375e04e5809d41ae677e3b1e1eb56d7e0bc1d8ec",
|
||||
"lighting-led-strip-glow-dark": "254fe1e39c5bb7bbb7e67527f69c767c8bdb6ed959986a8085b18adb152001d6",
|
||||
"led-strip-long-zigzag-glow-light": "cd0847c03b6b397c93fa923dcd06b30d7d4a8f6c6db2f006b9f3e20de0e66e91",
|
||||
"led-strip-endcaps-zoom-light": "a6e1c038a944dfbab4e44dd01cea3ab7b387aa633cc71a74f597c53d33149a26",
|
||||
"led-strip-design-reference-on-light": "1d6b4c2cfac2e1aeb9ce17ae2b3ddb62707e3f46c2ad3967b3669712a8e577fd",
|
||||
"lighting-led-strip-glow-dark": "e6cdec5a16fde53bd1eca255eff554a5923f710d2b6e24904e46e39d8e5ab4ab",
|
||||
"led-strip-long-zigzag-glow-light": "342749c29bac93a9a53f58921a40a10731fbcfdfff4345e71af81ce4de4dfa61",
|
||||
"led-strip-endcaps-zoom-light": "468293d57b96b4b17f61027467c7c43b25d5a0f91972ba0451998cdc61ea3980",
|
||||
"led-strip-rectangle-door-zoom-light": "00e8d937924bff957ef413f5d8b7c6964e44c96e90b9209ea24158217328b2ac",
|
||||
"led-strip-off-light": "cbb4f4794c871d592917653e8b5713d87a6be6dfe470b2161da760c2bb3cebdc",
|
||||
"iso-led-strip-dark": "572728e34f77f911f0ece247c5b7de6653e9c0dfafe04a0ab198b88644081a58"
|
||||
"iso-led-strip-dark": "6605a62b15a47da09920743793b5c7f8cb2768610b6d57b739fa91eeef883676"
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 260 KiB After Width: | Height: | Size: 258 KiB |
|
After Width: | Height: | Size: 185 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 41 KiB |
@@ -1339,6 +1339,14 @@ export const GOLDEN_SCENARIOS = Object.freeze([
|
||||
// must keep the same smooth circular rim; the wall still clips its side.
|
||||
zoom: 4, zoomCenter: [500, 380],
|
||||
ledStripOverride: { points: [[0.43, 0.35], [0.535, 0.35]] }, ...stage },
|
||||
{ id: 'led-strip-rectangle-door-zoom-light', fixture: 'visual', ledStrips: true, space: 'golden-led',
|
||||
mode: 'view', fillMode: 'none', language: 'en', theme: 'light', viewport: { width: 1000, height: 760 },
|
||||
// #787: four stored corners must remain four straight visible sides. The
|
||||
// right side crosses led-door: the opening is optically open, but neither
|
||||
// the stripe nor its emitter line may step back to the wall axis there.
|
||||
zoom: 1.15, zoomCenter: [310, 350],
|
||||
ledStripOverride: { points: [[0.068333, 0.088333], [0.551667, 0.088333],
|
||||
[0.551667, 0.611667], [0.068333, 0.611667], [0.068333, 0.088333]] }, ...stage },
|
||||
{ id: 'led-strip-off-light', fixture: 'visual', ledStrips: true, space: 'golden-led',
|
||||
mode: 'view', fillMode: 'none', stateOverrides: LED_STRIPS_OFF, language: 'en', theme: 'light',
|
||||
viewport: { width: 1000, height: 760 }, ...stage },
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/** Browser-side #789 witnesses. No product imports or private state writes.
|
||||
* The reference freezes the d8949cff full48 mask construction (led-strip-field
|
||||
* lines 433–465). Geometry/visibility are deliberately shared with the actual
|
||||
* DOM; smoke_led_strip_field's independent distance oracle covers those.
|
||||
* Actual raster styles are resolved on the connected node, so a leaked host
|
||||
* coarse selector cannot disappear merely because the clone left its host.
|
||||
*/
|
||||
export function installLedZoomOracle() {
|
||||
const NS = 'http://www.w3.org/2000/svg';
|
||||
const root = card => card.shadowRoot;
|
||||
const bands = field => [...field.querySelectorAll('mask > g > path')];
|
||||
const fields = card => [...root(card).querySelectorAll('[data-led-field]')];
|
||||
const attrs = node => [...node.attributes].map(a => [a.name, a.value]);
|
||||
const visibleThroughHost = (node, card) => {
|
||||
for (let current = node; current;) {
|
||||
const css = getComputedStyle(current);
|
||||
if (css.display === 'none' || css.visibility !== 'visible' || !(Number(css.opacity) > 0)) return false;
|
||||
if (current === card) return true;
|
||||
current = current.parentElement || current.getRootNode()?.host || null;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const stops = [[0, 1], [45, .88], [70, .62], [86, .32], [100, 0]];
|
||||
const grey = fraction => {
|
||||
const at = fraction * 100;
|
||||
for (let i = 1; i < stops.length; i++) {
|
||||
const [a, va] = stops[i - 1], [b, vb] = stops[i];
|
||||
if (at <= b) {
|
||||
const v = Math.round((va + (vb - va) * (at - a) / (b - a)) * 255);
|
||||
return `rgb(${v}, ${v}, ${v})`;
|
||||
}
|
||||
}
|
||||
return 'rgb(0, 0, 0)';
|
||||
};
|
||||
const snapshot = card => {
|
||||
const nodes = [...root(card).querySelectorAll('.led-fields, .led-fields *, [data-led-strip], [data-led-strip] *, [data-glow-source], [data-glow-source] *')];
|
||||
return { nodes, attributes: nodes.map(attrs), cache: [...root(card).querySelectorAll('.led-fields')]
|
||||
.map(n => [n.dataset.ledCache, n.dataset.ledRecomputes]) };
|
||||
};
|
||||
const unchanged = (card, before) => {
|
||||
const after = snapshot(card);
|
||||
const hitWidthChanges = [];
|
||||
const stable = before.nodes.length === after.nodes.length && before.nodes.every((node, i) => {
|
||||
if (node !== after.nodes[i]) return false;
|
||||
const old = before.attributes[i], now = after.attributes[i];
|
||||
if (!node.matches('.led-hit')) return JSON.stringify(old) === JSON.stringify(now);
|
||||
const oldWidth = old.find(([key]) => key === 'stroke-width')?.[1];
|
||||
const newWidth = now.find(([key]) => key === 'stroke-width')?.[1];
|
||||
if (oldWidth !== newWidth) hitWidthChanges.push({ oldWidth, newWidth });
|
||||
return JSON.stringify(old.filter(([key]) => key !== 'stroke-width'))
|
||||
=== JSON.stringify(now.filter(([key]) => key !== 'stroke-width'));
|
||||
});
|
||||
return { stable, hitWidthChanges, cacheStable: JSON.stringify(before.cache) === JSON.stringify(after.cache) };
|
||||
};
|
||||
const quality = card => ({
|
||||
coarse: card.getAttribute('data-led-zoom-quality') === 'coarse',
|
||||
ordinary: [...root(card).querySelectorAll('[data-glow-source]')].map(field => ({
|
||||
visible: visibleThroughHost(field, card),
|
||||
})),
|
||||
fields: fields(card).map(field => {
|
||||
const paths = bands(field);
|
||||
const painted = paths.filter(path => getComputedStyle(path).display !== 'none');
|
||||
const pool = field.querySelector('.led-pool');
|
||||
return { id: field.dataset.ledField, retained: paths.length, painted: painted.length,
|
||||
visible: visibleThroughHost(field, card) && Number(pool.getAttribute('fill-opacity')) > 0,
|
||||
correct24: paths.length === 48 && paths.every((path, k) => k % 2
|
||||
? getComputedStyle(path).display === 'none'
|
||||
: getComputedStyle(path).display !== 'none' && getComputedStyle(path).stroke === grey(1 - (k + 1) / 48)),
|
||||
fill: pool.getAttribute('fill'), alpha: pool.getAttribute('fill-opacity'),
|
||||
};
|
||||
}),
|
||||
});
|
||||
const clone = (card, frozen48) => {
|
||||
const original = root(card).querySelector('.led-fields');
|
||||
if (!original) throw new Error('LED raster oracle: no field');
|
||||
const copy = original.cloneNode(true);
|
||||
fields(card).forEach((field, i) => {
|
||||
const target = copy.querySelectorAll('[data-led-field]')[i];
|
||||
target.style.opacity = getComputedStyle(field).opacity;
|
||||
target.style.visibility = getComputedStyle(field).visibility;
|
||||
target.style.mixBlendMode = getComputedStyle(field).mixBlendMode;
|
||||
const actualPaths = bands(field), targetPaths = bands(target);
|
||||
if (actualPaths.length !== 48) throw new Error(`LED raster oracle: expected retained48, got ${actualPaths.length}`);
|
||||
if (frozen48) {
|
||||
const group = targetPaths[0].parentNode;
|
||||
const d = actualPaths[0].getAttribute('d');
|
||||
const radius = Number(actualPaths[0].getAttribute('stroke-width')) / 2;
|
||||
group.replaceChildren();
|
||||
for (let k = 0; k < 48; k++) {
|
||||
const path = document.createElementNS(NS, 'path');
|
||||
const outer = 1 - k / 48, inner = 1 - (k + 1) / 48;
|
||||
for (const [name, value] of Object.entries({ d, fill: 'none', stroke: grey((outer + inner) / 2),
|
||||
'stroke-width': 2 * outer * radius, 'stroke-linecap': 'round', 'stroke-linejoin': 'round' })) {
|
||||
path.setAttribute(name, String(value));
|
||||
}
|
||||
group.appendChild(path);
|
||||
}
|
||||
} else actualPaths.forEach((path, k) => {
|
||||
const css = getComputedStyle(path);
|
||||
targetPaths[k].style.stroke = css.stroke;
|
||||
targetPaths[k].style.display = css.display;
|
||||
targetPaths[k].style.visibility = css.visibility;
|
||||
});
|
||||
});
|
||||
return copy;
|
||||
};
|
||||
const raster = async (source, box, scale, background) => {
|
||||
const [x, y, w, h] = box;
|
||||
const xml = `<svg xmlns="${NS}" width="${w * scale}" height="${h * scale}" viewBox="${box.join(' ')}"><rect x="${x}" y="${y}" width="${w}" height="${h}" fill="${background}"/>${new XMLSerializer().serializeToString(source)}</svg>`;
|
||||
const img = new Image(); img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(xml)}`;
|
||||
await img.decode();
|
||||
const canvas = document.createElement('canvas'); canvas.width = w * scale; canvas.height = h * scale;
|
||||
const context = canvas.getContext('2d'); context.drawImage(img, 0, 0);
|
||||
return { data: context.getImageData(0, 0, canvas.width, canvas.height).data, canvas };
|
||||
};
|
||||
const full48 = async (card, { scales = [1, 2, 4], background = '#868d94', images = false } = {}) => {
|
||||
const pools = [...root(card).querySelectorAll('.led-pool')];
|
||||
const rects = pools.map(p => ['x', 'y', 'width', 'height'].map(a => Number(p.getAttribute(a))));
|
||||
const x = Math.floor(Math.min(...rects.map(r => r[0]))) - 2;
|
||||
const y = Math.floor(Math.min(...rects.map(r => r[1]))) - 2;
|
||||
const box = [x, y, Math.ceil(Math.max(...rects.map(r => r[0] + r[2]))) - x + 2,
|
||||
Math.ceil(Math.max(...rects.map(r => r[1] + r[3]))) - y + 2];
|
||||
const actual = clone(card, false), expected = clone(card, true), rows = [];
|
||||
for (const scale of scales) {
|
||||
const a = await raster(actual, box, scale, background), b = await raster(expected, box, scale, background);
|
||||
let differentPixels = 0, maxDelta = 0;
|
||||
for (let i = 0; i < a.data.length; i += 4) {
|
||||
let different = false;
|
||||
for (let c = 0; c < 4; c++) {
|
||||
const delta = Math.abs(a.data[i + c] - b.data[i + c]);
|
||||
maxDelta = Math.max(maxDelta, delta); different ||= delta !== 0;
|
||||
}
|
||||
if (different) differentPixels++;
|
||||
}
|
||||
rows.push({ scale, pixels: a.data.length / 4, differentPixels, maxDelta,
|
||||
...(images ? { actualPng: a.canvas.toDataURL(), expectedPng: b.canvas.toDataURL() } : {}) });
|
||||
}
|
||||
return rows;
|
||||
};
|
||||
window.__ledZoomOracle = { snapshot, unchanged, quality, full48 };
|
||||
}
|
||||
@@ -442,7 +442,8 @@ scenario and of a 100-step series, one wheel step per frame — retained heap
|
||||
after 20 A→B→C→A cycles 64 MiB — and on exact counters: zero field geometry
|
||||
recomputes over 100 unrelated HA ticks, 100 camera steps and a colour-only
|
||||
change; the three caches of the shown space (`ledStats` of the runtime chunk)
|
||||
— shapes ≤ 50, visibility ≤ 50, retained per-emitter fans ≤ 2500 — identical
|
||||
— shapes ≤ 50, visibility ≤ 50, compact visibility batches ≤ 2500 and
|
||||
retained SVG path text ≤ 4 Mi characters — identical
|
||||
after every cycle; after disconnect 0 retained entries and 0 live LED
|
||||
timers/frames/observers (tracked by creation stack in the page); one extra
|
||||
cold run holds the runtime response, removes the card meanwhile and requires
|
||||
@@ -455,4 +456,26 @@ samples after one warm-up enforced on the merged set. Base predates the
|
||||
strips, so there is no relative comparison; the zero-LED View remains judged
|
||||
by the relative `large-house-interaction-v1` profile. Exact-SHA Linux output
|
||||
of the full performance workflow (`led-strips` matrix entry) is the gate
|
||||
evidence; a local report is diagnostic.
|
||||
evidence; a local report is diagnostic. Since #788 the actual emitter-fan
|
||||
count remains reported but is not capped by dropping required endpoints or
|
||||
corners: that old limit hid visible holes. Five fans are retained per compact
|
||||
batch; all batches render as one positive-winding clip path per strip. The
|
||||
4 Mi-character bound (at most 8 MiB UTF-16 text) explicitly measures cached
|
||||
paths, excluding the joined Lit/DOM clip string. Timing and the 64 MiB
|
||||
warm-cycle heap-growth limit are unchanged; that is not an absolute browser
|
||||
or GPU memory bound.
|
||||
|
||||
Issue #789 retains every historical window and budget, and adds
|
||||
`cameraFullCycleLongTaskMaxMs`: one continuous observer starting before the
|
||||
first camera wheel, spanning the original scenario and 100-step series, full
|
||||
quality restoration, two animation frames, an immediate wheel restart, a second
|
||||
restoration and two frames, then at least 500 ms of tail. Raw Long Task entries
|
||||
and phase timestamps are saved in each sample's `cameraFullCycle`. The new
|
||||
metric inherits the existing 150 ms camera ceiling for median and p95; missing
|
||||
observations or quality that fails to restore within 1000 ms fail closed. Two
|
||||
frames are a paint opportunity, not proof of compositor presentation. The
|
||||
original `cameraSeriesLongTaskMaxMs` still ends at its original boundary. This
|
||||
addition prevents deferred restoration work from escaping all judged windows;
|
||||
it does not turn local timing into canonical evidence. Issue #789's acceptance
|
||||
additionally requires a 10% task margin (warm p95 ≤1350 ms and both camera-series
|
||||
and full-cycle p95 ≤135 ms), without changing the shared budget JSON.
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"profile": "led-strips-v1",
|
||||
"issue": 780,
|
||||
"note": "ТЗ #780 §13.2: absolute limits applied to median and p95; 10x5 rests on the interaction profile's absolute ceilings, 50x50 are the acceptance bounds set in the ТЗ. The no-strip control is judged by the existing interaction profile against base. Caches of the shown space (ТЗ §13.2): 50 shapes, 50 visibility entries, 2500 retained per-emitter fans; 0 after disconnect.",
|
||||
"note": "#780 timing/heap limits unchanged. #788: preserve every end/vertex; compact positive-winding fans into paths without discarding geometry. Bound actual retained representation: 50 shapes/entries, 2500 compact clip paths, 4 Mi SVG characters (at most 8 MiB UTF-16, one eighth of the unchanged 64 MiB heap budget). Actual fan count is still reported, never relabelled as path count. No growth/recomputes, zero after disconnect.",
|
||||
"caches": {
|
||||
"shapes": 50,
|
||||
"visibility": 50,
|
||||
"sources": 2500
|
||||
"visibilityPaths": 2500,
|
||||
"pathChars": 4194304
|
||||
},
|
||||
"sizes": {
|
||||
"10x5": {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/** Additional #789 window: do not let deferred quality restoration escape
|
||||
* the existing camera observer. Dependencies are passed explicitly so this
|
||||
* exact function runs in the page and has a deterministic Node witness.
|
||||
* Two rAFs are a paint opportunity, never proof of compositor presentation. */
|
||||
export async function finishLedCameraCycle({ fullQuality, input, frame, now, sleep }) {
|
||||
const marks = {};
|
||||
const restored = async (name) => {
|
||||
const started = now();
|
||||
marks[`${name}RestoreStart`] = started;
|
||||
while (!fullQuality()) {
|
||||
if (now() - started >= 1000) throw new Error('LED zoom quality did not restore within 1000 ms');
|
||||
await sleep(5);
|
||||
}
|
||||
marks[`${name}FullQuality`] = now();
|
||||
await frame();
|
||||
if (!fullQuality()) throw new Error('LED zoom quality changed during the final paint opportunity');
|
||||
marks[`${name}PaintOpportunity`] = now();
|
||||
};
|
||||
await restored('first');
|
||||
marks.restartInput = now();
|
||||
await input();
|
||||
await frame();
|
||||
marks.restartSettled = now();
|
||||
await restored('final');
|
||||
await sleep(500);
|
||||
await frame();
|
||||
if (!fullQuality()) throw new Error('LED zoom quality regressed in the restore tail');
|
||||
marks.tailEnd = now();
|
||||
return marks;
|
||||
}
|
||||
|
||||
/** The new full-cycle metric inherits, never relaxes, the old camera limit.
|
||||
* Missing/nonfinite observations are failures even in a partial local run. */
|
||||
export function cameraCycleFailures(metrics, limit) {
|
||||
const failures = [];
|
||||
for (const stat of ['median', 'p95']) {
|
||||
const value = metrics?.[stat];
|
||||
if (!Number.isFinite(value)) failures.push(`cameraFullCycleLongTaskMaxMs ${stat} missing`);
|
||||
else if (Number.isFinite(limit) && value > limit) failures.push(`cameraFullCycleLongTaskMaxMs ${stat} ${value} > ${limit}`);
|
||||
}
|
||||
return failures;
|
||||
}
|
||||
|
||||
/** Check every raw sample before aggregation: filtering missing values out of
|
||||
* a median/p95 must not make a partial or old report pass --merge. */
|
||||
export function cameraCycleSampleFailures(rows) {
|
||||
const failures = [];
|
||||
const phases = ['seriesEnd', 'firstRestoreStart', 'firstFullQuality', 'firstPaintOpportunity',
|
||||
'restartInput', 'restartSettled', 'finalRestoreStart', 'finalFullQuality',
|
||||
'finalPaintOpportunity', 'tailEnd'];
|
||||
rows.forEach((row, index) => {
|
||||
const cycle = row?.cameraFullCycle;
|
||||
const times = [cycle?.startTime, ...phases.map(key => cycle?.phases?.[key]), cycle?.endTime];
|
||||
const invalidTimes = times.some((time, i) => !Number.isFinite(time) || (i > 0 && time < times[i - 1]));
|
||||
const entries = cycle?.entries;
|
||||
if (invalidTimes || !Array.isArray(entries)
|
||||
|| entries.some(entry => !Number.isFinite(entry.startTime) || !Number.isFinite(entry.duration) || entry.duration < 0)
|
||||
|| !Number.isFinite(row.cameraFullCycleLongTaskMaxMs)) {
|
||||
failures.push(`sample ${index}: missing or invalid camera full-cycle evidence`);
|
||||
return;
|
||||
}
|
||||
if (cycle.phases.tailEnd - cycle.phases.finalPaintOpportunity < 500) {
|
||||
failures.push(`sample ${index}: camera full-cycle tail shorter than 500 ms`);
|
||||
}
|
||||
const maximum = Number(Math.max(0, ...entries.map(entry => entry.duration)).toFixed(2));
|
||||
if (row.cameraFullCycleLongTaskMaxMs !== maximum) {
|
||||
failures.push(`sample ${index}: camera full-cycle maximum differs from raw Long Tasks`);
|
||||
}
|
||||
});
|
||||
return failures;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/** Stable LED chunk identity for request accounting, independent of its hash. */
|
||||
export function ledChunkRequestName(url) {
|
||||
const name = url.replace(/[?#].*$/, '').replace(/.*\//, '');
|
||||
// Rollup's base64url hashes may themselves contain '-' (even first).
|
||||
// Capture the known prefix instead of removing a last-hyphen suffix.
|
||||
return /^(led-strip-(?:runtime|field|editor))-[\w-]+\.js$/.exec(name)?.[1] ?? null;
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/** #788: test the painted light, not SVG node counts. The card renders each
|
||||
* synthetic strip through the ordinary config event. Rasterise its actual
|
||||
* field DOM at several pixel densities and compare unobstructed floor pixels
|
||||
* with the independently calculated distance to the stored polyline. This
|
||||
* catches missing end/vertex emitters, winding cancellation and hard rims.
|
||||
* Household exports are deliberately NOT fixtures in this public test.
|
||||
*/
|
||||
import { launch, check, finish } from './serve.mjs';
|
||||
import { installLedZoomOracle } from './helpers/led-zoom-oracle.mjs';
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
|
||||
const { page, browser } = await launch({ width: 1000, height: 820 }, 1);
|
||||
await page.evaluate(installLedZoomOracle);
|
||||
const exactResults = [];
|
||||
const outputDir = process.env.HP_LED_ZOOM_OUTPUT || 'artifacts/led-zoom-quality';
|
||||
async function exact48(label, capture = false) {
|
||||
// This is a CSS/raster contract, not the input/controller witness. The latter
|
||||
// is exercised with trusted wheel/touch in smoke_led_zoom_quality.mjs.
|
||||
const result = await page.evaluate(async capture => {
|
||||
const card = window.__card, oracle = window.__ledZoomOracle;
|
||||
if (card.hasAttribute('data-led-zoom-quality')) throw new Error('exact48 requires a rested owner');
|
||||
const before = oracle.snapshot(card);
|
||||
const idle = await oracle.full48(card);
|
||||
let active, coarseRaster;
|
||||
try {
|
||||
card.setAttribute('data-led-zoom-quality', 'coarse');
|
||||
active = oracle.quality(card);
|
||||
if (capture) coarseRaster = await oracle.full48(card, { scales: [2], images: true });
|
||||
} finally { card.removeAttribute('data-led-zoom-quality'); }
|
||||
const restored = await oracle.full48(card);
|
||||
return { idle, active, restored, invariants: oracle.unchanged(card, before), coarseRaster };
|
||||
}, capture);
|
||||
for (const phase of ['idle', 'restored']) for (const row of result[phase]) {
|
||||
check(`#789 ${label} ${phase} full48 scale=${row.scale}: every pixel equals frozen pre-change48`, row.differentPixels, 0);
|
||||
}
|
||||
check(`#789 ${label}: coarse paints exactly even24 midpoint bands and keeps light visible`,
|
||||
result.active.fields.length > 0 && result.active.fields.every(f => f.retained === 48 && f.painted === 24 && f.correct24 && f.visible));
|
||||
check(`#789 ${label}: quality alone preserves all DOM, attributes and cache counters`,
|
||||
result.invariants.stable && result.invariants.cacheStable);
|
||||
if (capture) {
|
||||
await mkdir(outputDir, { recursive: true });
|
||||
for (const row of result.coarseRaster) {
|
||||
// Negative witness: a leaked coarse mode must not pass the full48 oracle.
|
||||
check(`#789 ${label}: full48 oracle rejects unrecovered24`, row.differentPixels > 0);
|
||||
for (const [kind, key] of [['active24', 'actualPng'], ['full48', 'expectedPng']]) {
|
||||
await writeFile(join(outputDir, `${label.replace(/[^a-z0-9-]/gi, '-')}-${kind}.png`), Buffer.from(row[key].split(',')[1], 'base64'));
|
||||
}
|
||||
delete row.actualPng; delete row.expectedPng;
|
||||
}
|
||||
}
|
||||
exactResults.push({ label, ...result });
|
||||
}
|
||||
const cases = [
|
||||
{ name: 'short residual end', points: [[0.25, 0.35], [0.36225, 0.35]] },
|
||||
{ name: 'diagonal residual end', points: [[0.25, 0.3], [0.341, 0.365625]] },
|
||||
{ name: 'acute outer turn', points: [[0.3, 0.25], [0.32, 0.38], [0.35, 0.25]] },
|
||||
{ name: 'reflected acute turn', points: [[0.3, 0.48], [0.32, 0.35], [0.35, 0.48]] },
|
||||
{ name: 'decimal closed loop', points: [[0.2301, 0.2202], [0.3803, 0.2202], [0.3803, 0.4204], [0.2301, 0.4204], [0.2301, 0.2202]] },
|
||||
{ name: 'intersecting path', points: [[0.3, 0.25], [0.4, 0.45], [0.3, 0.45], [0.4, 0.25]] },
|
||||
{ name: 'mixed free and blocked fans', points: [[0.35, 0.35], [0.585, 0.45]], wall: true },
|
||||
];
|
||||
const results = [];
|
||||
for (const scenario of cases) {
|
||||
for (const radiusCm of [30, 60, 120]) {
|
||||
for (const reversed of [false, true]) {
|
||||
const points = reversed ? [...scenario.points].reverse() : scenario.points;
|
||||
await page.evaluate(async ({ points, wall, radiusCm }) => {
|
||||
await window.__hpTest.setServerConfig(cfg => ({ ...cfg,
|
||||
spaces: [{ id: 'led-oracle', title: 'LED field oracle', cell_cm: 5,
|
||||
rooms: [{ id: 'room', name: 'Room', poly: [[0.08, 0.08], [0.92, 0.08], [0.92, 0.72], [0.08, 0.72]] }],
|
||||
partitions: wall ? [{ id: 'barrier', a: [0.6, 0.1], b: [0.6, 0.7], cm: 12 }] : [],
|
||||
wall_segments: [], openings: [], decor: [], wall_columns: [],
|
||||
settings: { glow_enabled: true, fill_mode: 'none' },
|
||||
led_strips: [{ id: 'oracle', marker: 'oracle-light', points }] }],
|
||||
markers: [{ id: 'oracle-light', binding: 'virtual', is_light: true,
|
||||
space: 'led-oracle', room_id: 'room', glow_radius_cm: radiusCm,
|
||||
glow_color: { c: '#ffffff', bri: 1 } }],
|
||||
}));
|
||||
}, { points, wall: scenario.wall, radiusCm });
|
||||
await page.waitForFunction(() => window.__card.shadowRoot.querySelector('[data-led-field="oracle"]'));
|
||||
await page.waitForTimeout(550);
|
||||
await exact48(`${scenario.name}-r${radiusCm}-${reversed ? 'reverse' : 'forward'}`,
|
||||
radiusCm === 60 && !reversed && ['acute outer turn', 'mixed free and blocked fans'].includes(scenario.name));
|
||||
const raster = await page.evaluate(async ({ points, wall, radiusCm }) => {
|
||||
const source = window.__card.shadowRoot.querySelector('.led-fields').cloneNode(true);
|
||||
// Isolate geometry/falloff from colour/brightness/animation, whose
|
||||
// user-facing lifecycle is independently covered by smoke_led_strip_glow.
|
||||
source.querySelectorAll('.led-pool').forEach(el => {
|
||||
el.setAttribute('fill', '#ffffff'); el.setAttribute('fill-opacity', '1');
|
||||
});
|
||||
const path = points.map(p => p.map(v => v * 1000));
|
||||
const r = radiusCm / 5 * (1000 / 240); // documented physical scale, not read from implementation.
|
||||
const minX = Math.floor(Math.min(...path.map(p => p[0])) - r - 2);
|
||||
const minY = Math.floor(Math.min(...path.map(p => p[1])) - r - 2);
|
||||
const w = Math.ceil(Math.max(...path.map(p => p[0])) + r + 2 - minX);
|
||||
const h = Math.ceil(Math.max(...path.map(p => p[1])) + r + 2 - minY);
|
||||
const distance = (x, y) => Math.min(...path.slice(1).map((b, i) => {
|
||||
const a = path[i], dx = b[0] - a[0], dy = b[1] - a[1];
|
||||
const t = Math.max(0, Math.min(1, ((x - a[0]) * dx + (y - a[1]) * dy) / (dx * dx + dy * dy)));
|
||||
return Math.hypot(x - a[0] - t * dx, y - a[1] - t * dy);
|
||||
}));
|
||||
// The documented shared five-stop brightness contract, not a source import.
|
||||
const stops = [[0, 1], [0.45, 0.88], [0.70, 0.62], [0.86, 0.32], [1, 0]];
|
||||
const expectedAt = d => {
|
||||
const f = d / r;
|
||||
if (f >= 1) return 0;
|
||||
for (let i = 1; i < stops.length; i++) {
|
||||
if (f <= stops[i][0]) {
|
||||
const [x0, y0] = stops[i - 1], [x1, y1] = stops[i];
|
||||
return y0 + (y1 - y0) * (f - x0) / (x1 - x0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
const output = [];
|
||||
for (const scale of [1, 2, 4]) {
|
||||
const xml = `<svg xmlns="http://www.w3.org/2000/svg" width="${w * scale}" height="${h * scale}" viewBox="${minX} ${minY} ${w} ${h}"><rect x="${minX}" y="${minY}" width="${w}" height="${h}" fill="black"/>${new XMLSerializer().serializeToString(source)}</svg>`;
|
||||
const image = new Image();
|
||||
image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(xml)}`;
|
||||
await image.decode();
|
||||
const canvas = document.createElement('canvas'); canvas.width = w * scale; canvas.height = h * scale;
|
||||
const ctx = canvas.getContext('2d'); ctx.drawImage(image, 0, 0);
|
||||
const data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
|
||||
let worst = 0, bad = 0, checked = 0, witness = null;
|
||||
for (let py = 2; py < canvas.height - 2; py += 3) {
|
||||
for (let px = 2; px < canvas.width - 2; px += 3) {
|
||||
const x = minX + (px + 0.5) / scale, y = minY + (py + 0.5) / scale;
|
||||
// Left of the long opaque wall every source has clear sight. On
|
||||
// the far side every source is occluded (no wall ends in this ROI).
|
||||
if (wall && Math.abs(x - 595) < 2) continue;
|
||||
const expected = wall && x > 595 ? 0 : expectedAt(distance(x, y));
|
||||
const actual = data[(py * canvas.width + px) * 4] / 255;
|
||||
const error = Math.abs(expected - actual);
|
||||
checked++;
|
||||
if (error > worst) { worst = error; witness = [x, y, expected, actual]; }
|
||||
if (error > 0.06) bad++;
|
||||
}
|
||||
}
|
||||
output.push({ scale, checked, bad, worst, witness });
|
||||
}
|
||||
return output;
|
||||
}, { points, wall: scenario.wall, radiusCm });
|
||||
for (const row of raster) check(`${scenario.name}, radius=${radiusCm}cm, reverse=${reversed}, raster=${row.scale}: smooth field and opaque wall`, row.bad, 0);
|
||||
results.push({ name: scenario.name, radiusCm, reversed, raster });
|
||||
}
|
||||
}
|
||||
}
|
||||
// Additional complete-pixel cases: colour/alpha are NOT overwritten to white,
|
||||
// and overlap is composited over the same grey in reference and actual images.
|
||||
for (const extra of ['closed-door', 'colour-alpha', 'overlap']) {
|
||||
await page.evaluate(async extra => {
|
||||
const points = extra === 'closed-door'
|
||||
? [[.2, .22], [.55, .22], [.55, .5], [.2, .5], [.2, .22]]
|
||||
: [[.22, .3], [.5, .3], [.5, .45]];
|
||||
await window.__hpTest.setServerConfig(cfg => ({ ...cfg,
|
||||
spaces: [{ id: 'led-oracle', title: 'Colour and door oracle', cell_cm: 5,
|
||||
rooms: [{ id: 'room', name: 'Room', poly: [[.08, .08], [.92, .08], [.92, .72], [.08, .72]] }],
|
||||
partitions: extra === 'closed-door' ? [{ id: 'barrier', a: [.6, .1], b: [.6, .7], cm: 12 }] : [],
|
||||
openings: extra === 'closed-door' ? [{ id: 'door', type: 'door', x: .6, y: .36, angle: 90, length: .14,
|
||||
host: { kind: 'partition', id: 'barrier', t: 13 / 30 } }] : [],
|
||||
wall_segments: [], decor: [], wall_columns: [], settings: { glow_enabled: true, fill_mode: 'none' },
|
||||
led_strips: [{ id: 'oracle', marker: 'oracle-light', points }, ...(extra === 'overlap'
|
||||
? [{ id: 'overlap', marker: 'overlap-light', points: [[.3, .35], [.52, .35]] }] : [])] }],
|
||||
markers: [{ id: 'oracle-light', binding: 'virtual', is_light: true, space: 'led-oracle', room_id: 'room',
|
||||
glow_radius_cm: 120, glow_color: { c: '#75c52a', bri: .43 } },
|
||||
...(extra === 'overlap' ? [{ id: 'overlap-light', binding: 'virtual', is_light: true, space: 'led-oracle',
|
||||
room_id: 'room', glow_radius_cm: 120, glow_color: { c: '#b552dd', bri: .37 } }] : [])],
|
||||
}));
|
||||
}, extra);
|
||||
await page.waitForTimeout(550);
|
||||
if (extra === 'closed-door') {
|
||||
const doorWitness = await page.evaluate(async () => {
|
||||
const read = () => {
|
||||
const field = window.__card.shadowRoot.querySelector('[data-led-field="oracle"]');
|
||||
return { closed: field.dataset.closed,
|
||||
clip: field.querySelector('clipPath path').getAttribute('d') };
|
||||
};
|
||||
const withDoor = read();
|
||||
await window.__hpTest.setServerConfig(cfg => { cfg.spaces[0].openings = []; return cfg; });
|
||||
const withoutDoor = read();
|
||||
await window.__hpTest.setServerConfig(cfg => {
|
||||
cfg.spaces[0].openings = [{ id: 'door', type: 'door', x: .6, y: .36, angle: 90, length: .14,
|
||||
host: { kind: 'partition', id: 'barrier', t: 13 / 30 } }];
|
||||
return cfg;
|
||||
});
|
||||
const restoredDoor = read();
|
||||
return { closed: withDoor.closed === 'true',
|
||||
openingActuallyChangesVisibility: withDoor.clip !== withoutDoor.clip,
|
||||
restoredVisibility: withDoor.clip === restoredDoor.clip };
|
||||
});
|
||||
for (const [key, value] of Object.entries(doorWitness)) check(`#789 closed rectangle/door fixture: ${key}`, value);
|
||||
}
|
||||
// Explicit alpha .43 is a raster-composition fixture, independent of the
|
||||
// product's brightness→alpha mapping (covered by the live HA smoke).
|
||||
await page.evaluate(() => {
|
||||
const pools = window.__card.shadowRoot.querySelectorAll('.led-pool');
|
||||
pools[0].setAttribute('fill', '#75c52a'); pools[0].setAttribute('fill-opacity', '0.43');
|
||||
if (pools[1]) pools[1].setAttribute('fill-opacity', '0.37');
|
||||
});
|
||||
await exact48(extra, true);
|
||||
}
|
||||
check('#789: original seven × three radii × two directions × three scales × two idle phases are covered',
|
||||
exactResults.slice(0, 42).reduce((n, r) => n + r.idle.length + r.restored.length, 0), 252);
|
||||
await finish(browser, { distanceOracle: results, full48Oracle: exactResults });
|
||||
@@ -3,8 +3,8 @@
|
||||
* the View and on the static card. No LED chunk without a displayed active
|
||||
* strip (a hidden shape or an unbound strip loads nothing); an active bound
|
||||
* strip replaces the icon, paints the two-stroke stripe and — with Glow — the
|
||||
* linear field; off/unavailable have no field, unavailable is dashed; without
|
||||
* Glow the core takes the source colour. The whole length is one target: a
|
||||
* linear field; off/unavailable have no field, unavailable is dashed. The on
|
||||
* core takes the source colour with or without Glow. The whole length is one target: a
|
||||
* click toggles once, a touch pan calls nothing. A hidden marker or an
|
||||
* HA-disabled device loads no chunk (r1 M4); the value badge stays at the
|
||||
* anchor, passive (r1 M1). The static card is passive in all four
|
||||
@@ -12,6 +12,8 @@
|
||||
*/
|
||||
import { launch, check, finish } from './serve.mjs';
|
||||
import { installHpTestOnPage } from './helpers/hp-test.mjs';
|
||||
import { mkdir } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
|
||||
const { page, browser } = await launch({ width: 1000, height: 820 }, 1);
|
||||
const ledRequests = [];
|
||||
@@ -48,6 +50,18 @@ const stripe = (marker) => evaluate((marker) => {
|
||||
coreWidth: Number(g.querySelector('.led-core')?.getAttribute('stroke-width')) } : null;
|
||||
}, marker);
|
||||
const field = () => evaluate(() => window.__card.shadowRoot.querySelectorAll('[data-led-field]').length);
|
||||
const painted = () => evaluate(() => {
|
||||
const root = window.__card.shadowRoot;
|
||||
const core = root.querySelector('[data-marker="d_light1"] .led-core');
|
||||
const pool = root.querySelector('[data-led-field="ceiling"] .led-pool');
|
||||
return { core: core?.getAttribute('stroke'), field: pool?.getAttribute('fill'),
|
||||
coreOpacity: core ? getComputedStyle(core).strokeOpacity : null,
|
||||
fieldOpacity: Number(pool?.getAttribute('fill-opacity')),
|
||||
geometry: ['.led-outline', '.led-core', '.led-hit'].map(selector => {
|
||||
const path = root.querySelector(`[data-marker="d_light1"] ${selector}`);
|
||||
return [path?.getAttribute('d'), path?.getAttribute('stroke-width')];
|
||||
}) };
|
||||
});
|
||||
|
||||
await settle();
|
||||
check('no strips: no LED chunk requested', ledRequests.length, 0);
|
||||
@@ -82,9 +96,68 @@ check('unbound strip is absent from the View', await evaluate(() => window.__car
|
||||
check('no round pool at the anchor: the strip is the source', await evaluate(() =>
|
||||
!window.__card.shadowRoot.querySelector('[data-glow-source="light.ceiling"]')));
|
||||
check('no auto-slot reserved for the strip’s marker', await evaluate(() => !('d_light1' in (window.__card._defPos || {}))));
|
||||
await setState('light.ceiling', 'on', { rgb_color: [128, 213, 255], brightness: 64 });
|
||||
await settle();
|
||||
const onStripe = await stripe('d_light1');
|
||||
check('on with Glow: white core and a field', JSON.stringify([onStripe?.state, onStripe?.core, onStripe?.dash, await field() > 0]),
|
||||
JSON.stringify(['on', '#FFFFFF', null, true]));
|
||||
check('on with Glow: source-coloured core and a field', [onStripe?.state, onStripe?.core, onStripe?.dash, await field() > 0],
|
||||
['on', '#80d5ff', null, true]);
|
||||
const firstPaint = await painted();
|
||||
check('#790: rendered core and field share the resolved RGB colour', [firstPaint.core, firstPaint.field], ['#80d5ff', '#80d5ff']);
|
||||
await setState('light.ceiling', 'on', { rgb_color: [225, 70, 35] });
|
||||
await settle();
|
||||
const rgbPaint = await painted();
|
||||
check('#790: HA RGB update changes the existing core and field', [rgbPaint.core, rgbPaint.field], ['#e14623', '#e14623']);
|
||||
check('#790: a colour update changes no outline, core or hit geometry', rgbPaint.geometry, firstPaint.geometry);
|
||||
await setState('light.ceiling', 'on', { rgb_color: null, color_temp_kelvin: 3000 });
|
||||
await settle();
|
||||
const warmPaint = await painted();
|
||||
check('#790: colour temperature uses the same resolved field colour', warmPaint.core === warmPaint.field
|
||||
&& warmPaint.core !== '#FFFFFF' && warmPaint.core !== rgbPaint.core);
|
||||
await setStrips(ceilingStrip, { glow_enabled: true }, { d_light1: { glow_color: { c: '#d837a6', bri: 0.2 } } });
|
||||
await settle();
|
||||
const manualPaint = await painted();
|
||||
check('#790: saved manual colour updates core and field', [manualPaint.core, manualPaint.field], ['#d837a6', '#d837a6']);
|
||||
check('#790: field brightness never becomes core opacity', manualPaint.coreOpacity === '1' && manualPaint.fieldOpacity < 1);
|
||||
check('#790: manual colour changes no physical geometry', manualPaint.geometry, firstPaint.geometry);
|
||||
|
||||
// AC3: the same public HA/config path in both themes, Flat/2.5D and on/off.
|
||||
// Optional local review captures are not golden baselines or release files.
|
||||
const captureDir = process.env.HP_LED_COLOR_SCREENSHOTS;
|
||||
if (captureDir) await mkdir(captureDir, { recursive: true });
|
||||
await setStrips(ceilingStrip, { glow_enabled: true });
|
||||
for (const dark of [false, true]) for (const iso of [false, true]) {
|
||||
await evaluate(async dark => {
|
||||
const card = window.__card;
|
||||
card.hass = { ...card.hass, themes: { ...(card.hass.themes || {}), darkMode: dark } };
|
||||
document.documentElement.style.colorScheme = dark ? 'dark' : 'light';
|
||||
await window.__hpTest.settled();
|
||||
}, dark);
|
||||
await evaluate(iso => window.__hpTest.setVolumetricView(iso), iso);
|
||||
let onGeometry;
|
||||
for (const state of ['on', 'off']) {
|
||||
await setState('light.ceiling', state, { rgb_color: [128, 213, 255] });
|
||||
await page.waitForTimeout(600);
|
||||
const actual = await painted();
|
||||
const label = `#790: ${dark ? 'dark' : 'light'}, ${iso ? '2.5D' : 'Flat'}, ${state}`;
|
||||
check(`${label}: core colour`, actual.core, state === 'on' ? '#80d5ff' : '#FFFFFF');
|
||||
if (state === 'on') onGeometry = actual.geometry;
|
||||
else check(`${label}: geometry stays unchanged`, actual.geometry, onGeometry);
|
||||
if (captureDir) {
|
||||
const box = await page.locator('[data-led-strip="ceiling"] .led-outline').boundingBox();
|
||||
const x = Math.max(0, box.x - 30), y = Math.max(0, box.y - 30);
|
||||
await page.screenshot({ path: join(captureDir, `${dark ? 'dark' : 'light'}-${iso ? 'iso' : 'flat'}-${state}.png`),
|
||||
clip: { x, y, width: Math.min(1000 - x, box.width + 60), height: Math.min(820 - y, box.height + 60) } });
|
||||
}
|
||||
}
|
||||
}
|
||||
await evaluate(async () => {
|
||||
const card = window.__card;
|
||||
card.hass = { ...card.hass, themes: { ...(card.hass.themes || {}), darkMode: false } };
|
||||
document.documentElement.style.colorScheme = 'light';
|
||||
await window.__hpTest.setVolumetricView(false);
|
||||
});
|
||||
await setState('light.ceiling', 'on', { rgb_color: [128, 213, 255] });
|
||||
await settle();
|
||||
|
||||
// r1 M1 (ТЗ §5): the strip keeps the device's value badge, passive, at the
|
||||
// half-length anchor — the icon core, pulse and slot stay suppressed.
|
||||
@@ -186,8 +259,13 @@ await settle();
|
||||
await setState('light.ceiling', 'unavailable');
|
||||
await settle();
|
||||
const unavailable = await stripe('d_light1');
|
||||
check('unavailable: dashed, no field', JSON.stringify([unavailable?.state, !!unavailable?.dash, await field()]),
|
||||
JSON.stringify(['unavailable', true, 0]));
|
||||
check('unavailable: dashed grey, no field', [unavailable?.state, unavailable?.core, !!unavailable?.dash, await field()],
|
||||
['unavailable', '#9e9e9e', true, 0]);
|
||||
await setState('light.ceiling', 'unknown');
|
||||
await settle();
|
||||
const unknown = await stripe('d_light1');
|
||||
check('#790: unknown keeps the grey dashed unavailable presentation',
|
||||
[unknown?.state, unknown?.core, !!unknown?.dash, await field()], ['unavailable', '#9e9e9e', true, 0]);
|
||||
await setState('light.ceiling', 'on', { rgb_color: [128, 213, 255] });
|
||||
await setStrips(await evaluate(() => window.__card._serverCfg.spaces[0].led_strips), { glow_enabled: false });
|
||||
await settle();
|
||||
@@ -197,8 +275,23 @@ await settle();
|
||||
// finish before asserting the stable no-Glow contract.
|
||||
await page.waitForFunction(() => !window.__card.shadowRoot.querySelector('[data-led-field]'));
|
||||
const colored = await stripe('d_light1');
|
||||
check('on without Glow: coloured core, no field', JSON.stringify([colored?.state, colored?.core !== '#FFFFFF', await field()]),
|
||||
JSON.stringify(['on', true, 0]));
|
||||
check('on without Glow: source-coloured core, no field', [colored?.state, colored?.core, await field()],
|
||||
['on', '#80d5ff', 0]);
|
||||
await evaluate(async () => {
|
||||
await window.__hpTest.setServerConfig(cfg => {
|
||||
cfg.spaces[0].settings.glow_enabled = true;
|
||||
const room = cfg.spaces[0].rooms.find(room => room.id === 'r1');
|
||||
room.settings = { ...(room.settings || {}), glow: false };
|
||||
});
|
||||
});
|
||||
await settle();
|
||||
check('#790: room Glow override changes no live core colour',
|
||||
[(await stripe('d_light1'))?.core, await field()], ['#80d5ff', 0]);
|
||||
await evaluate(async () => {
|
||||
await window.__hpTest.setServerConfig(cfg => {
|
||||
cfg.spaces[0].rooms.find(room => room.id === 'r1').settings.glow = null;
|
||||
});
|
||||
});
|
||||
|
||||
// The static card (ТЗ §8, AC14, r1 M6): the full light_pools × live_states
|
||||
// matrix. Passive in all four: no hit path, no focus, a click calls nothing.
|
||||
@@ -235,7 +328,7 @@ const matrix = await evaluate(async () => {
|
||||
target?.dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }));
|
||||
}
|
||||
out[key] = {
|
||||
state: g?.dataset.state, white: core?.getAttribute('stroke') === '#FFFFFF',
|
||||
state: g?.dataset.state, core: core?.getAttribute('stroke'),
|
||||
field: !!sr.querySelector('[data-led-field]'),
|
||||
passive: !sr.querySelector('.led-hit, [data-led-strip] [role], [data-led-strip] [tabindex]'),
|
||||
badge: !!sr.querySelector('[data-led-badge="d_light1"] .value-badge'),
|
||||
@@ -245,16 +338,347 @@ const matrix = await evaluate(async () => {
|
||||
return out;
|
||||
});
|
||||
check('static live_states:false + light_pools:false — neutral, no field, passive', JSON.stringify(matrix['pools=false,live=false']),
|
||||
JSON.stringify({ state: 'off', white: true, field: false, passive: true, badge: true }));
|
||||
JSON.stringify({ state: 'off', core: '#FFFFFF', field: false, passive: true, badge: true }));
|
||||
check('static live_states:true + light_pools:false — source colour in the core, no field, passive', JSON.stringify(matrix['pools=false,live=true']),
|
||||
JSON.stringify({ state: 'on', white: false, field: false, passive: true, badge: true }));
|
||||
JSON.stringify({ state: 'on', core: '#80d5ff', field: false, passive: true, badge: true }));
|
||||
check('static live_states:false + light_pools:true — neutral, no field, passive', JSON.stringify(matrix['pools=true,live=false']),
|
||||
JSON.stringify({ state: 'off', white: true, field: false, passive: true, badge: true }));
|
||||
check('static live_states:true + light_pools:true — white core and the field, passive', JSON.stringify(matrix['pools=true,live=true']),
|
||||
JSON.stringify({ state: 'on', white: true, field: true, passive: true, badge: true }));
|
||||
JSON.stringify({ state: 'off', core: '#FFFFFF', field: false, passive: true, badge: true }));
|
||||
check('static live_states:true + light_pools:true — source colour and the field, passive', JSON.stringify(matrix['pools=true,live=true']),
|
||||
JSON.stringify({ state: 'on', core: '#80d5ff', field: true, passive: true, badge: true }));
|
||||
check('a click on any static stripe toggles nothing', await evaluate(() => window.__card.hass.states['light.ceiling'].state), 'on');
|
||||
|
||||
// #789 AC2: real transition events, not merely the declared CSS duration.
|
||||
// Do not read computed style/layout between insertion and its entering rAF:
|
||||
// that flush can manufacture the initial opacity and hide a lost fade.
|
||||
await page.emulateMedia({ reducedMotion: 'no-preference' });
|
||||
await evaluate(async () => {
|
||||
const main = window.__card;
|
||||
const host = document.createElement('div');
|
||||
host.style.width = '700px'; document.body.appendChild(host);
|
||||
const stat = document.createElement('houseplan-space-card');
|
||||
stat.setConfig({ type: 'custom:houseplan-space-card', space: 'f1', light_pools: true, live_states: true });
|
||||
stat.hass = main.hass; host.appendChild(stat);
|
||||
await stat.updateComplete;
|
||||
const proof = window.__ledFadeProof = {
|
||||
cards: [main, stat], host, savedHass: main.hass, log: [], listeners: [],
|
||||
selectors: ['[data-led-field="ceiling"]', '[data-glow-source="light.floor_lamp"]'],
|
||||
parent: main.parentNode, next: main.nextSibling, updates: [0, 0], late: [], descriptors: [],
|
||||
};
|
||||
proof.nodes = () => proof.cards.map(card => proof.selectors.map(selector => card.shadowRoot.querySelector(selector)));
|
||||
proof.set = async state => {
|
||||
const states = { ...main.hass.states };
|
||||
for (const eid of ['light.ceiling', 'light.floor_lamp']) states[eid] = { ...states[eid], state };
|
||||
const hass = { ...main.hass, states };
|
||||
for (const card of proof.cards) card.hass = hass;
|
||||
await Promise.all(proof.cards.map(card => card.updateComplete));
|
||||
};
|
||||
for (const [cardIndex, card] of proof.cards.entries()) {
|
||||
const listener = event => {
|
||||
if (event.propertyName !== 'opacity') return;
|
||||
const kind = proof.selectors.findIndex(selector => event.target.matches(selector));
|
||||
if (kind >= 0) proof.log.push({ card: cardIndex, kind, event: event.type, elapsed: event.elapsedTime });
|
||||
};
|
||||
for (const name of ['transitionrun', 'transitionend']) card.shadowRoot.addEventListener(name, listener);
|
||||
proof.listeners.push(listener);
|
||||
}
|
||||
await proof.set('off');
|
||||
});
|
||||
try {
|
||||
await page.waitForFunction(() => window.__ledFadeProof.cards.every(card =>
|
||||
card.shadowRoot.querySelector('[data-led-strip]')));
|
||||
await page.waitForTimeout(650);
|
||||
check('#789: both kinds start absent on both surfaces', await evaluate(() =>
|
||||
window.__ledFadeProof.nodes().every(nodes => nodes.every(node => !node))), true);
|
||||
const fadeSet = state => evaluate(async state => {
|
||||
const proof = window.__ledFadeProof; proof.log = []; await proof.set(state);
|
||||
}, state);
|
||||
const fadeSnapshot = () => evaluate(() => {
|
||||
const proof = window.__ledFadeProof;
|
||||
return { log: proof.log, nodes: proof.nodes().map(nodes => nodes.map(node => node ? {
|
||||
opacity: Number(getComputedStyle(node).opacity), leaving: node.classList.contains('is-leaving'),
|
||||
entering: node.classList.contains('is-entering'),
|
||||
} : null)) };
|
||||
});
|
||||
const checkFadeEvents = (phase, result, cards = [0, 1], kinds = [0, 1]) => {
|
||||
for (const card of cards) for (const kind of kinds) {
|
||||
const events = result.log.filter(event => event.card === card && event.kind === kind);
|
||||
const label = `#789: ${card ? 'static' : 'main'} ${kind ? 'ordinary' : 'LED'} ${phase}`;
|
||||
check(`${label} actually starts an opacity transition`, events.some(event => event.event === 'transitionrun'), true);
|
||||
check(`${label} completes the unchanged 500 ms transition`, events.some(event =>
|
||||
event.event === 'transitionend' && Math.abs(event.elapsed - 0.5) < 0.01), true);
|
||||
}
|
||||
};
|
||||
await fadeSet('on');
|
||||
await page.waitForTimeout(750);
|
||||
const fadeOn = await fadeSnapshot();
|
||||
checkFadeEvents('on', fadeOn);
|
||||
check('#789: all four fields finish on at full opacity', fadeOn.nodes.every(nodes =>
|
||||
nodes.every(node => node?.opacity === 1 && !node.leaving)), true);
|
||||
// Observe the real bundled call graph, without wrapping private methods or
|
||||
// inferring render-pass wiring from source text. Coverage is not timing data.
|
||||
const coverageSession = await page.context().newCDPSession(page);
|
||||
try {
|
||||
await coverageSession.send('Profiler.enable');
|
||||
await coverageSession.send('Profiler.startPreciseCoverage', { callCount: true, detailed: true });
|
||||
for (const pass of [1, 2]) {
|
||||
await coverageSession.send('Profiler.takePreciseCoverage');
|
||||
await evaluate(async () => {
|
||||
const card = window.__card;
|
||||
card.requestUpdate();
|
||||
for (let attempt = 0; attempt < 10; attempt++) {
|
||||
const complete = await card.updateComplete;
|
||||
if (complete !== false && !card.isUpdatePending) return;
|
||||
}
|
||||
throw new Error('#789: observed main render did not settle');
|
||||
});
|
||||
const result = await coverageSession.send('Profiler.takePreciseCoverage');
|
||||
const functions = result.result.flatMap(script => script.functions);
|
||||
const calls = name => functions.filter(fn => fn.functionName === name)
|
||||
.reduce((sum, fn) => sum + (fn.ranges[0]?.count || 0), 0);
|
||||
const counts = { render: calls('_renderLightPass'), reads: calls('_lightBarriers'), builds: calls('_resolveLightBarriers') };
|
||||
check(`#789: observed main render pass ${pass} executes`, counts.render > 0, true);
|
||||
check(`#789: Glow and LED share barrier reads in pass ${pass}`, counts.reads >= 2 * counts.render, true);
|
||||
check(`#789: pass ${pass} resolves barriers exactly once per render, never across renders`, counts.builds, counts.render);
|
||||
console.log('#789 render-pass coverage', JSON.stringify({ pass, ...counts }));
|
||||
}
|
||||
} finally {
|
||||
await coverageSession.send('Profiler.stopPreciseCoverage');
|
||||
await coverageSession.send('Profiler.disable');
|
||||
await coverageSession.detach();
|
||||
}
|
||||
await fadeSet('off');
|
||||
check('#789: normal off retains all four nodes for their fade', await evaluate(() =>
|
||||
window.__ledFadeProof.nodes().every(nodes => nodes.every(node => node?.classList.contains('is-leaving')))), true);
|
||||
await page.waitForTimeout(750);
|
||||
const fadeOff = await fadeSnapshot();
|
||||
checkFadeEvents('off', fadeOff);
|
||||
check('#789: normal off removes all four fields after fading', fadeOff.nodes.every(nodes => nodes.every(node => !node)), true);
|
||||
|
||||
await page.emulateMedia({ reducedMotion: 'reduce' });
|
||||
await fadeSet('on');
|
||||
await page.waitForTimeout(200);
|
||||
const reducedOn = await fadeSnapshot();
|
||||
check('#789: reduced motion shows LED and ordinary immediately on both surfaces', reducedOn.nodes.every(nodes =>
|
||||
nodes.every(node => node?.opacity === 1)), true);
|
||||
check('#789: reduced on starts no CSS opacity animation', reducedOn.log.length, 0);
|
||||
await fadeSet('off');
|
||||
await page.waitForTimeout(200);
|
||||
const reducedOff = await fadeSnapshot();
|
||||
check('#789: reduced off is immediately dark on both surfaces', reducedOff.nodes.every(nodes =>
|
||||
nodes.every(node => node?.opacity === 0)), true);
|
||||
check('#789: reduced off starts no CSS opacity animation', reducedOff.log.length, 0);
|
||||
await page.waitForTimeout(450);
|
||||
check('#789: reduced off still removes every field', await evaluate(() =>
|
||||
window.__ledFadeProof.nodes().every(nodes => nodes.every(node => !node))), true);
|
||||
|
||||
await page.emulateMedia({ reducedMotion: 'no-preference' });
|
||||
await fadeSet('on');
|
||||
await page.waitForTimeout(750);
|
||||
await evaluate(() => { window.__ledFadeProof.savedNodes = window.__ledFadeProof.nodes(); });
|
||||
await fadeSet('off');
|
||||
await page.waitForTimeout(100);
|
||||
await fadeSet('on');
|
||||
await page.waitForTimeout(750); // beyond the cancelled off timer as well as the returning fade
|
||||
check('#789: rapid off/on retains every DOM node beyond the stale fade deadline', await evaluate(() => {
|
||||
const proof = window.__ledFadeProof;
|
||||
return proof.nodes().every((nodes, card) => nodes.every((node, kind) => node
|
||||
&& node === proof.savedNodes[card][kind] && !node.classList.contains('is-leaving')
|
||||
&& Number(getComputedStyle(node).opacity) === 1));
|
||||
}), true);
|
||||
|
||||
await fadeSet('off');
|
||||
await page.waitForTimeout(650);
|
||||
await evaluate(async () => {
|
||||
// Both Lit commits finish in microtasks, before a browser entering-frame.
|
||||
await window.__ledFadeProof.set('on'); await window.__ledFadeProof.set('off');
|
||||
});
|
||||
await page.waitForTimeout(650);
|
||||
check('#789: off before the entry frame cannot resurrect a field', await evaluate(() =>
|
||||
window.__ledFadeProof.nodes().every(nodes => nodes.every(node => !node))), true);
|
||||
|
||||
await evaluate(async () => {
|
||||
const proof = window.__ledFadeProof;
|
||||
await proof.set('on');
|
||||
// Lit's updated() can enqueue a header/summary follow-up. Drain only its
|
||||
// microtasks before observing disconnect: entry rAFs must still be pending.
|
||||
const drainUpdates = async phase => {
|
||||
for (let attempt = 0; attempt < 10; attempt++) {
|
||||
const complete = await Promise.all(proof.cards.map(card => card.updateComplete));
|
||||
if (complete.every(result => result !== false) && proof.cards.every(card => !card.isUpdatePending)) return;
|
||||
}
|
||||
throw new Error(`#789: pending Lit microtasks did not settle ${phase} disconnect`);
|
||||
};
|
||||
await drainUpdates('before');
|
||||
if (!proof.nodes().every(nodes => nodes.every(node => node?.classList.contains('is-entering')))) {
|
||||
throw new Error('#789: disconnect witness must precede every entering frame');
|
||||
}
|
||||
for (const [index, card] of proof.cards.entries()) {
|
||||
proof.descriptors[index] = Object.getOwnPropertyDescriptor(card, 'requestUpdate');
|
||||
const original = card.requestUpdate;
|
||||
card.requestUpdate = function (...args) {
|
||||
proof.updates[index]++;
|
||||
if (!this.isConnected) proof.late.push({ card: index, stack: new Error().stack });
|
||||
return original.apply(this, args);
|
||||
};
|
||||
}
|
||||
// Disconnect in this microtask, with entry rAFs/feather timers still pending.
|
||||
for (const card of proof.cards) card.remove();
|
||||
// Teardown itself mutates reactive header/summary state. Finish that same
|
||||
// microtask chain before measuring late callbacks; no rAF/timer can run yet.
|
||||
await drainUpdates('after');
|
||||
proof.updates = [0, 0];
|
||||
proof.late = [];
|
||||
});
|
||||
await page.waitForTimeout(750);
|
||||
check('#789: disconnected main/static owners receive no late transition updates', await evaluate(() =>
|
||||
window.__ledFadeProof.updates), [0, 0]);
|
||||
const late = await evaluate(() => window.__ledFadeProof.late);
|
||||
if (late.length) console.log('#789 late update diagnostic', JSON.stringify(late, null, 2));
|
||||
await evaluate(async () => {
|
||||
const proof = window.__ledFadeProof; proof.log = [];
|
||||
proof.parent.insertBefore(proof.cards[0], proof.next?.parentNode === proof.parent ? proof.next : null);
|
||||
proof.host.appendChild(proof.cards[1]);
|
||||
await Promise.all(proof.cards.map(card => card.updateComplete));
|
||||
});
|
||||
await page.waitForTimeout(750);
|
||||
const reconnected = await fadeSnapshot();
|
||||
checkFadeEvents('reconnected', reconnected);
|
||||
check('#789: reconnect starts clean lifecycles and returns all four fields', reconnected.nodes.every(nodes =>
|
||||
nodes.every(node => node?.opacity === 1 && !node.leaving)), true);
|
||||
for (const exit of [
|
||||
{ label: 'disabled pools', config: { space: 'f1', light_pools: false } },
|
||||
{ label: 'another space', config: { space: 'garden', light_pools: true } },
|
||||
]) {
|
||||
await fadeSet('off');
|
||||
await page.waitForTimeout(650);
|
||||
await evaluate(async config => {
|
||||
const proof = window.__ledFadeProof;
|
||||
await proof.set('on');
|
||||
if (!proof.nodes()[1].every(node => node?.classList.contains('is-entering'))) {
|
||||
throw new Error('#789: config exit must precede the static entering frame');
|
||||
}
|
||||
proof.cards[1].setConfig({ type: 'custom:houseplan-space-card', live_states: true, ...config });
|
||||
await proof.cards[1].updateComplete;
|
||||
}, exit.config);
|
||||
await page.waitForTimeout(750);
|
||||
check(`#789: ${exit.label} removes previous static fields beyond their old callbacks`, await evaluate(() =>
|
||||
window.__ledFadeProof.nodes()[1].every(node => !node)), true);
|
||||
await evaluate(async () => {
|
||||
const proof = window.__ledFadeProof; proof.log = [];
|
||||
proof.cards[1].setConfig({ type: 'custom:houseplan-space-card', space: 'f1', light_pools: true, live_states: true });
|
||||
await proof.cards[1].updateComplete;
|
||||
});
|
||||
await page.waitForTimeout(750);
|
||||
const returned = await fadeSnapshot();
|
||||
checkFadeEvents(`return from ${exit.label}`, returned, [1]);
|
||||
check(`#789: return from ${exit.label} starts fresh static fields`, returned.nodes[1].every(node =>
|
||||
node?.opacity === 1 && !node.leaving), true);
|
||||
}
|
||||
await fadeSet('off');
|
||||
await page.waitForTimeout(650);
|
||||
await evaluate(async () => {
|
||||
const proof = window.__ledFadeProof;
|
||||
await proof.set('on');
|
||||
if (!proof.nodes()[0].every(node => node?.classList.contains('is-entering'))) {
|
||||
throw new Error('#789: main navigation must start before the entering frame');
|
||||
}
|
||||
await window.__hpTest.switchSpace('garden');
|
||||
});
|
||||
await page.waitForTimeout(750);
|
||||
check('#789: main LED-to-empty navigation removes both former sources beyond stale callbacks', await evaluate(() =>
|
||||
window.__ledFadeProof.nodes()[0].every(node => !node)), true);
|
||||
await evaluate(async () => {
|
||||
window.__ledFadeProof.log = [];
|
||||
await window.__hpTest.switchSpace('f1');
|
||||
});
|
||||
await page.waitForTimeout(750);
|
||||
const mainReturned = await fadeSnapshot();
|
||||
checkFadeEvents('return from LED-free space', mainReturned, [0], [0]);
|
||||
// Main ordinary Glow has retained its per-space appearance without replay on
|
||||
// navigation since before #789. Preserve that UX; only LED starts a fresh
|
||||
// lifecycle here. The real on/off fade assertions above still cover both.
|
||||
check('#789: main navigation back restores the LED field at full opacity',
|
||||
mainReturned.nodes[0][0]?.opacity === 1 && !mainReturned.nodes[0][0].leaving, true);
|
||||
const retainedOrdinary = mainReturned.nodes[0][1];
|
||||
check('#789: main navigation preserves steady ordinary Glow without replay',
|
||||
retainedOrdinary?.opacity === 1 && !retainedOrdinary.entering && !retainedOrdinary.leaving, true);
|
||||
|
||||
// Keep the same mounted static card while its server-side space vanishes.
|
||||
// This exercises renderSpaceStatic's !space exit, not setConfig/disconnect.
|
||||
await fadeSet('off');
|
||||
await page.waitForTimeout(650);
|
||||
await evaluate(async () => {
|
||||
const proof = window.__ledFadeProof;
|
||||
await proof.set('on');
|
||||
if (!proof.nodes()[1].every(node => node?.classList.contains('is-entering'))) {
|
||||
throw new Error('#789: server removal must start before the static entering frame');
|
||||
}
|
||||
await window.__hpTest.setServerConfig(cfg => {
|
||||
proof.savedConfig = structuredClone(cfg);
|
||||
cfg.spaces = cfg.spaces.filter(space => space.id !== 'f1');
|
||||
cfg.markers = (cfg.markers || []).filter(marker => marker.space !== 'f1');
|
||||
});
|
||||
});
|
||||
await page.waitForFunction(() => !window.__ledFadeProof.cards[1].shadowRoot.querySelector('[data-led-strip]'));
|
||||
await page.waitForTimeout(750);
|
||||
check('#789: a missing server space clears both fields without disconnecting the static card', await evaluate(() => {
|
||||
const proof = window.__ledFadeProof;
|
||||
return proof.cards[1].isConnected && proof.nodes()[1].every(node => !node);
|
||||
}), true);
|
||||
await evaluate(async () => {
|
||||
const proof = window.__ledFadeProof; proof.log = [];
|
||||
await window.__hpTest.setServerConfig(proof.savedConfig);
|
||||
delete proof.savedConfig;
|
||||
});
|
||||
await page.waitForTimeout(750);
|
||||
const restoredSpace = await fadeSnapshot();
|
||||
checkFadeEvents('restored server space', restoredSpace, [1]);
|
||||
check('#789: restoring a server space starts fresh static LED and ordinary fields', restoredSpace.nodes[1].every(node =>
|
||||
node?.opacity === 1 && !node.leaving), true);
|
||||
await evaluate(() => window.__hpTest.switchSpace('f1'));
|
||||
await page.waitForTimeout(750);
|
||||
} finally {
|
||||
await page.emulateMedia({ reducedMotion: 'no-preference' });
|
||||
await evaluate(async () => {
|
||||
const proof = window.__ledFadeProof;
|
||||
if (proof.savedConfig) {
|
||||
await window.__hpTest.setServerConfig(proof.savedConfig);
|
||||
delete proof.savedConfig;
|
||||
}
|
||||
for (const [index, card] of proof.cards.entries()) {
|
||||
for (const name of ['transitionrun', 'transitionend']) card.shadowRoot.removeEventListener(name, proof.listeners[index]);
|
||||
if (index < proof.descriptors.length) {
|
||||
if (proof.descriptors[index]) Object.defineProperty(card, 'requestUpdate', proof.descriptors[index]);
|
||||
else delete card.requestUpdate;
|
||||
}
|
||||
}
|
||||
const main = proof.cards[0];
|
||||
if (!main.isConnected) proof.parent.insertBefore(main, proof.next?.parentNode === proof.parent ? proof.next : null);
|
||||
main.hass = proof.savedHass;
|
||||
proof.cards[1].remove(); proof.host.remove();
|
||||
delete window.__ledFadeProof;
|
||||
await main.updateComplete;
|
||||
});
|
||||
await page.waitForFunction(() => window.__card.shadowRoot.querySelector('[data-led-strip]'));
|
||||
await settle();
|
||||
}
|
||||
|
||||
for (const mode of ['plan', 'decor']) {
|
||||
await evaluate(mode => window.__hpTest.setMode(mode), mode);
|
||||
check(`#790: ${mode} keeps the coloured core passive and translucent`, await evaluate(() => {
|
||||
const root = window.__card.shadowRoot;
|
||||
return [root.querySelector('[data-marker="d_light1"] .led-core')?.getAttribute('stroke'),
|
||||
root.querySelector('.led-passive')?.getAttribute('opacity'), !!root.querySelector('.led-hit')];
|
||||
}), ['#80d5ff', '0.45', false]);
|
||||
}
|
||||
await evaluate(() => window.__hpTest.setMode('devices'));
|
||||
check('#790: Devices retains the live core colour', (await stripe('d_light1'))?.core, '#80d5ff');
|
||||
await setStrips([...ceilingStrip, { id: 'loose', points: [[0.12, 0.45], [0.30, 0.45]], marker: null }],
|
||||
{ glow_enabled: true });
|
||||
check('#790: unbound Devices strip keeps its grey dashed presentation', await evaluate(() => {
|
||||
const path = window.__card.shadowRoot.querySelector('[data-led-unbound="loose"]');
|
||||
return [path?.getAttribute('stroke'), !!path?.getAttribute('stroke-dasharray')];
|
||||
}), ['#8a8a8a', true]);
|
||||
await page.locator('.led-select-hit[data-led-select="ceiling"]').dispatchEvent('click');
|
||||
await page.waitForTimeout(100);
|
||||
await page.click('[data-led-action="settings"]');
|
||||
@@ -293,4 +717,65 @@ check('the press loads the tool and draws for the same marker', await evaluate((
|
||||
return !!led?.tool && led.chain?.convert === 'd_tv' && !window.__card._markerDialog;
|
||||
}));
|
||||
|
||||
// A fresh document makes both lazy imports genuinely cold. Hold each network
|
||||
// response, disconnect its requesting static card, then let the actual module
|
||||
// finish loading: no private runtime hooks or synthetic callback.
|
||||
await evaluate(() => localStorage.clear()); // isolated demo browser storage only
|
||||
ledRequests.length = 0;
|
||||
await page.reload();
|
||||
await page.waitForFunction(() => window.__card?._loadOk);
|
||||
await installHpTestOnPage(page);
|
||||
await evaluate(async () => {
|
||||
window.__card.setConfig({ type: 'custom:houseplan-card', floor: 'garden' });
|
||||
await window.__card.updateComplete;
|
||||
});
|
||||
await setStrips(ceilingStrip, { glow_enabled: true });
|
||||
check('#789: fixed non-LED main card leaves both LED imports cold', ledRequests.filter(url =>
|
||||
/^led-strip-(runtime|field)-/.test(url)).length, 0);
|
||||
for (const kind of ['runtime', 'field']) {
|
||||
let releaseImport;
|
||||
const barrier = new Promise(resolve => { releaseImport = resolve; });
|
||||
const pattern = new RegExp(`/led-strip-${kind}-[^/]+\\.js(?:\\?.*)?$`);
|
||||
const holdImport = async route => { await barrier; await route.fallback(); };
|
||||
await page.route(pattern, holdImport);
|
||||
try {
|
||||
const requested = page.waitForRequest(pattern);
|
||||
await evaluate(async () => {
|
||||
const card = document.createElement('houseplan-space-card');
|
||||
card.setConfig({ type: 'custom:houseplan-space-card', space: 'f1', light_pools: true, live_states: true });
|
||||
card.hass = window.__card.hass;
|
||||
document.body.appendChild(card);
|
||||
window.__ledColdProof = { card, updates: 0, original: card.requestUpdate };
|
||||
await card.updateComplete;
|
||||
});
|
||||
await requested;
|
||||
check(`#789: cold static ${kind} import has not painted its content yet`, await evaluate(kind =>
|
||||
!window.__ledColdProof.card.shadowRoot.querySelector(kind === 'runtime' ? '[data-led-strip]' : '[data-led-field]'), kind), true);
|
||||
await evaluate(async () => {
|
||||
const proof = window.__ledColdProof, card = proof.card;
|
||||
card.remove();
|
||||
let settled = false;
|
||||
for (let attempt = 0; attempt < 10; attempt++) {
|
||||
const complete = await card.updateComplete;
|
||||
if (complete !== false && !card.isUpdatePending) { settled = true; break; }
|
||||
}
|
||||
if (!settled) throw new Error('#789: cold static teardown did not settle');
|
||||
card.requestUpdate = function (...args) { proof.updates++; return proof.original.apply(this, args); };
|
||||
});
|
||||
const arrived = page.waitForResponse(pattern);
|
||||
releaseImport();
|
||||
await (await arrived).finished();
|
||||
await page.waitForTimeout(750);
|
||||
check(`#789: a cold ${kind} arriving after static disconnect requests no update`, await evaluate(() =>
|
||||
window.__ledColdProof.updates), 0);
|
||||
} finally {
|
||||
releaseImport();
|
||||
await page.unroute(pattern, holdImport);
|
||||
await evaluate(() => {
|
||||
const proof = window.__ledColdProof;
|
||||
if (proof) { delete proof.card.requestUpdate; proof.card.remove(); delete window.__ledColdProof; }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await finish(browser);
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/** #788: browser witness for the visible tube, independent of the Glow field.
|
||||
* Synthetic room-wall fixtures enter through the config-event facade. Expected
|
||||
* corners come from an analytic inset/line equation below, not LED geometry.
|
||||
* Both real SVG lengths and raster pixels protect against extra miter hooks,
|
||||
* backtracking at short subdivisions and a notch through the open doorway.
|
||||
*/
|
||||
import { launch, check, finish } from './serve.mjs';
|
||||
import { fixtureWallKey } from './fixtures/wall-key.mjs';
|
||||
|
||||
const { page, browser } = await launch({ width: 1000, height: 820 }, 1);
|
||||
// All coordinates here are render units. A 12 cm wall at 5 cm/grid cell has
|
||||
// total thickness 10 units (1000/240 units/cell), hence a 5-unit inner inset.
|
||||
// This plan is smaller than the documented 1000-unit icon reference floor:
|
||||
// D=2.5%*1000=25, outline=.12D=3, centreline offset=1.5, core=1.5.
|
||||
const left = 200.1, top = 180.2, right = 500.3, bottom = 480.4;
|
||||
const offset = 1.5, outlineWidth = 3, coreWidth = 1.5;
|
||||
const scenarios = [
|
||||
{ name: 'decimal rectangle and door', angle: 0, tilt: 0, intermediate: false },
|
||||
{ name: 'sloped side and short saved subdivision', angle: 0, tilt: 0.545461, intermediate: true },
|
||||
{ name: 'rotated decimal rectangle and door', angle: 0.137, tilt: 0, intermediate: false },
|
||||
];
|
||||
const results = [];
|
||||
|
||||
await page.evaluate(async () => {
|
||||
const card = window.__card;
|
||||
card.setConfig({ ...card._config, icon_size: 2.5, language: 'en' });
|
||||
await window.__hpTest.setMode('view');
|
||||
});
|
||||
|
||||
for (const scenario of scenarios) {
|
||||
const cos = Math.cos(scenario.angle), sin = Math.sin(scenario.angle);
|
||||
const rotate = ([x, y]) => [400 + (x - 400) * cos - (y - 330) * sin,
|
||||
330 + (x - 400) * sin + (y - 330) * cos];
|
||||
const normalise = (point) => rotate(point).map(value => value / 1000);
|
||||
const rooms = [
|
||||
{ id: 'tube-room', name: 'Tube room', poly: [[left - 5, top - 5], [right + 5, top - 5],
|
||||
[right + 5, bottom + 5], [left - 5, bottom + 5]].map(normalise) },
|
||||
{ id: 'receiving-room', name: 'Receiving room', poly: [[right + 5, top - 5], [755.5, top - 5],
|
||||
[755.5, bottom + 5], [right + 5, bottom + 5]].map(normalise) },
|
||||
];
|
||||
const walls = [];
|
||||
for (const room of rooms) room.poly.forEach((a, i) => {
|
||||
const b = room.poly[(i + 1) % room.poly.length], key = fixtureWallKey(a, b);
|
||||
if (!walls.some(wall => wall.key === key)) walls.push({ key, a, b, cm: 12 });
|
||||
});
|
||||
// The doorway begins just .1 unit from the inner corner: its first face
|
||||
// piece is shorter than the tube's centreline offset. Both rooms exist, so
|
||||
// this is a real optically open door, not an opaque exterior opening.
|
||||
const door = normalise([right + 5, top + 40.1]);
|
||||
const corners = [[left + scenario.tilt, top], [left, bottom], [right, bottom], [right, top]];
|
||||
const source = [...corners, ...(scenario.intermediate ? [[right - 0.1, top]] : [])].map(normalise);
|
||||
// The free left side keeps its genuine slope; intersect it analytically
|
||||
// with y=top+offset and y=bottom-offset instead of silently straightening it.
|
||||
const expected = [[left + scenario.tilt * (1 - offset / (bottom - top)), top + offset],
|
||||
[left + scenario.tilt * offset / (bottom - top), bottom - offset],
|
||||
[right - offset, bottom - offset], [right - offset, top + offset]];
|
||||
if (!scenario.tilt) { expected[0][0] += offset; expected[1][0] += offset; }
|
||||
const expectedCorners = expected.map(rotate);
|
||||
const expectedLength = expectedCorners.reduce((length, a, i) => {
|
||||
const b = expectedCorners[(i + 1) % expectedCorners.length];
|
||||
return length + Math.hypot(b[0] - a[0], b[1] - a[1]);
|
||||
}, 0);
|
||||
|
||||
// Move the stored closure seam to every node, including the short saved
|
||||
// subdivision. The visible result must remain one four-corner tube.
|
||||
for (let start = 0; start < source.length; start++) for (const reversed of [false, true]) {
|
||||
const ordered = [...source.slice(start), ...source.slice(0, start)];
|
||||
if (reversed) ordered.reverse();
|
||||
const points = [...ordered, ordered[0]];
|
||||
await page.evaluate(async ({ rooms, walls, door, angle, points }) => {
|
||||
await window.__hpTest.setServerConfig(cfg => ({ ...cfg,
|
||||
settings: { ...(cfg.settings || {}), volumetric_view: false },
|
||||
spaces: [{ id: 'tube-oracle', title: 'Tube oracle', cell_cm: 5, view_box: [0, 0, 1, 0.7],
|
||||
rooms, walls, wall_segments: [], partitions: [], wall_columns: [], decor: [],
|
||||
openings: [{ id: 'tube-door', type: 'door', x: door[0], y: door[1],
|
||||
angle: 90 + angle * 180 / Math.PI, length: 0.08 }],
|
||||
settings: { glow_enabled: true, fill_mode: 'none', show_names: false, sun_rays: false },
|
||||
led_strips: [{ id: 'tube', marker: 'd_light1', points }] }],
|
||||
markers: [{ id: 'd_light1', binding: 'device:d_light1', space: 'tube-oracle',
|
||||
room_id: 'tube-room', is_light: true, glow_radius_cm: 30 }],
|
||||
}));
|
||||
}, { rooms, walls, door, angle: scenario.angle, points });
|
||||
|
||||
let viewShape = null;
|
||||
for (const mode of ['view', 'devices']) {
|
||||
await page.evaluate(mode => window.__hpTest.setMode(mode), mode);
|
||||
let onShape = null;
|
||||
for (const state of ['on', 'off']) {
|
||||
// Public HA state input, not a write to the card's private resolver.
|
||||
await page.evaluate(async state => {
|
||||
const card = window.__card, previous = card.hass.states['light.ceiling'];
|
||||
// This geometry-only oracle deliberately uses a white source in
|
||||
// both states; source-colour behaviour is held by the Glow smoke.
|
||||
card.hass = { ...card.hass, states: { ...card.hass.states,
|
||||
'light.ceiling': { ...previous, state,
|
||||
attributes: { ...previous.attributes, rgb_color: [255, 255, 255] } } } };
|
||||
await window.__hpTest.settled();
|
||||
}, state);
|
||||
await page.waitForFunction(state => window.__card.shadowRoot
|
||||
.querySelector('[data-led-strip="tube"]')?.dataset.state === state, state);
|
||||
|
||||
const actual = await page.evaluate(async ({ expectedCorners, outlineWidth, coreWidth }) => {
|
||||
const group = window.__card.shadowRoot.querySelector('[data-led-strip="tube"]');
|
||||
const outline = group.querySelector('.led-outline'), core = group.querySelector('.led-core');
|
||||
const d = outline.getAttribute('d');
|
||||
const corners = [...d.matchAll(/[ML]\s*([-+\d.e]+)[,\s]+([-+\d.e]+)/gi)]
|
||||
.map(match => [Number(match[1]), Number(match[2])]);
|
||||
const expectedD = expectedCorners.map((p, i) => `${i ? 'L' : 'M'}${p[0]} ${p[1]}`).join(' ') + ' Z';
|
||||
const minX = Math.floor(Math.min(...expectedCorners.map(p => p[0])) - 8);
|
||||
const minY = Math.floor(Math.min(...expectedCorners.map(p => p[1])) - 8);
|
||||
const width = Math.ceil(Math.max(...expectedCorners.map(p => p[0])) - minX + 8);
|
||||
const height = Math.ceil(Math.max(...expectedCorners.map(p => p[1])) - minY + 8);
|
||||
const raster = async body => {
|
||||
const image = new Image();
|
||||
const xml = `<svg xmlns="http://www.w3.org/2000/svg" width="${width * 2}" height="${height * 2}" viewBox="${minX} ${minY} ${width} ${height}">${body}</svg>`;
|
||||
image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(xml)}`;
|
||||
await image.decode();
|
||||
const canvas = document.createElement('canvas'); canvas.width = width * 2; canvas.height = height * 2;
|
||||
const context = canvas.getContext('2d'); context.drawImage(image, 0, 0);
|
||||
return context.getImageData(0, 0, canvas.width, canvas.height).data;
|
||||
};
|
||||
const expectedMarkup = `<path d="${expectedD}" fill="none" stroke="#383838" stroke-width="${outlineWidth}" stroke-linecap="round" stroke-linejoin="round"/><path d="${expectedD}" fill="none" stroke="#FFFFFF" stroke-width="${coreWidth}" stroke-linecap="round" stroke-linejoin="round"/>`;
|
||||
const [painted, reference] = await Promise.all([
|
||||
raster(new XMLSerializer().serializeToString(outline) + new XMLSerializer().serializeToString(core)),
|
||||
raster(expectedMarkup),
|
||||
]);
|
||||
let badPixels = 0;
|
||||
for (let i = 0; i < painted.length; i += 4) {
|
||||
if ([0, 1, 2, 3].some(channel => Math.abs(painted[i + channel] - reference[i + channel]) > 16)) badPixels++;
|
||||
}
|
||||
return { d, corners, closed: /Z\s*$/i.test(d), length: outline.getTotalLength(),
|
||||
widths: [Number(outline.getAttribute('stroke-width')), Number(core.getAttribute('stroke-width'))],
|
||||
coreD: core.getAttribute('d'), badPixels,
|
||||
openingPresent: !!window.__card.shadowRoot.querySelector('[data-hp="opening"][data-id="tube-door"][data-kind="door"]'),
|
||||
saved: window.__card._serverCfg.spaces[0].led_strips[0].points };
|
||||
}, { expectedCorners, outlineWidth, coreWidth });
|
||||
|
||||
const label = `${scenario.name}, start=${start}, reverse=${reversed}, ${mode}, ${state}`;
|
||||
check(`${label}: four closed visible corners`, actual.closed && actual.corners.length === 4);
|
||||
check(`${label}: fixture includes the real mounted door`, actual.openingPresent);
|
||||
check(`${label}: analytical corner positions`, expectedCorners.every(p =>
|
||||
actual.corners.some(q => Math.hypot(p[0] - q[0], p[1] - q[1]) < 1e-4)));
|
||||
check(`${label}: no extra travel from a hook, backtrack or door notch`, Math.abs(actual.length - expectedLength) < 0.003);
|
||||
check(`${label}: expected tube pixels`, actual.badPixels, 0);
|
||||
check(`${label}: outline/core share geometry`, actual.coreD, actual.d);
|
||||
check(`${label}: physical outline/core widths`, actual.widths, [outlineWidth, coreWidth]);
|
||||
check(`${label}: saved geometry is untouched`, actual.saved, points);
|
||||
const shape = { d: actual.d, widths: actual.widths };
|
||||
if (state === 'on') onShape = shape;
|
||||
else check(`${label}: off keeps the on shape and thickness`, shape, onShape);
|
||||
if (mode === 'view' && state === 'on') viewShape = shape;
|
||||
if (mode === 'devices') check(`${label}: Devices keeps View tube geometry`, shape, viewShape);
|
||||
results.push({ scenario: scenario.name, start, reversed, mode, state,
|
||||
corners: actual.corners.length, badPixels: actual.badPixels, lengthError: actual.length - expectedLength });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
await finish(browser, results);
|
||||
@@ -0,0 +1,322 @@
|
||||
/** #789: real input → temporary LED-only24 → exact full48. Not a benchmark. */
|
||||
import { launch, check, finish } from './serve.mjs';
|
||||
import { installLedZoomOracle } from './helpers/led-zoom-oracle.mjs';
|
||||
|
||||
const { page, browser } = await launch({ width: 1000, height: 820 }, 1, [], { hasTouch: true });
|
||||
await page.evaluate(installLedZoomOracle);
|
||||
const cdp = await page.context().newCDPSession(page);
|
||||
const results = [];
|
||||
const quality = () => page.evaluate(() => window.__ledZoomOracle.quality(window.__card));
|
||||
const idle = async () => {
|
||||
await page.waitForFunction(() => !window.__card.hasAttribute('data-led-zoom-quality')
|
||||
&& !window.__card._cameraTransition.active, undefined, { timeout: 1500 });
|
||||
await page.evaluate(() => window.__hpTest.settled());
|
||||
};
|
||||
const point = () => page.evaluate(() => {
|
||||
const r = window.__card.shadowRoot.querySelector('.stage').getBoundingClientRect();
|
||||
return { x: r.left + r.width * .55, y: r.top + r.height * .65 };
|
||||
});
|
||||
const wheel = async (dy = -80) => {
|
||||
const p = await point(); await page.mouse.move(p.x, p.y); await page.mouse.wheel(0, dy);
|
||||
await page.evaluate(() => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))));
|
||||
};
|
||||
const active = async label => {
|
||||
const q = await quality(); results.push({ label, ...q });
|
||||
check(`${label}: real scale change activates coarse`, q.coarse);
|
||||
check(`${label}: retained48 paints even24 with exact midpoint falloff, never hidden`,
|
||||
q.fields.length > 0 && q.fields.every(f => f.retained === 48 && f.painted === 24 && f.correct24 && f.visible));
|
||||
check(`${label}: ordinary light is not hidden by LED quality`, q.ordinary.length > 0 && q.ordinary.every(f => f.visible));
|
||||
return q.coarse;
|
||||
};
|
||||
const restored = async label => {
|
||||
await idle();
|
||||
const q = await quality();
|
||||
check(`${label}: owner removes coarse and paints original48`, !q.coarse
|
||||
&& q.fields.length > 0 && q.fields.every(f => f.retained === 48 && f.painted === 48 && f.visible));
|
||||
const rows = await page.evaluate(() => window.__ledZoomOracle.full48(window.__card, { scales: [1, 2] }));
|
||||
for (const row of rows) check(`${label}: all restored pixels equal frozen48 at scale ${row.scale}`, row.differentPixels, 0);
|
||||
};
|
||||
const setState = (state, attributes = {}) => page.evaluate(async ({ state, attributes }) => {
|
||||
const card = window.__card, previous = card.hass.states['light.ceiling'];
|
||||
card.hass = { ...card.hass, states: { ...card.hass.states,
|
||||
'light.ceiling': { ...previous, state, attributes: { ...previous.attributes, ...attributes } } } };
|
||||
await card.updateComplete;
|
||||
}, { state, attributes });
|
||||
const touch = (type, points) => cdp.send('Input.dispatchTouchEvent', { type,
|
||||
touchPoints: points.map(([x, y], i) => ({ x, y, id: i + 1, radiusX: 2, radiusY: 2, force: 1 })) });
|
||||
const twoFrames = () => page.evaluate(() => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))));
|
||||
|
||||
try {
|
||||
await setState('on', { rgb_color: [117, 197, 42], brightness: 190 });
|
||||
await page.evaluate(async () => {
|
||||
const card = window.__card, lamp = card.hass.states['light.floor_lamp'];
|
||||
card.hass = { ...card.hass, states: { ...card.hass.states,
|
||||
'light.floor_lamp': { ...lamp, state: 'on', attributes: { ...lamp.attributes, brightness: 200 } } } };
|
||||
await card.updateComplete;
|
||||
await window.__hpTest.setServerConfig(cfg => {
|
||||
const space = cfg.spaces.find(s => s.id === 'f1');
|
||||
space.settings = { ...space.settings, glow_enabled: true };
|
||||
space.led_strips = [
|
||||
{ id: 'quality', marker: 'd_light1', points: [[.15, .3], [.4, .3], [.4, .46]] },
|
||||
{ id: 'overlap', marker: 'quality-virtual', points: [[.2, .35], [.45, .35]] },
|
||||
];
|
||||
cfg.markers = [...(cfg.markers || []).filter(m => !['quality-virtual', 'd_light1', 'd_lamp'].includes(m.id)),
|
||||
{ id: 'd_light1', binding: 'device:d_light1', space: 'f1' },
|
||||
{ id: 'd_lamp', binding: 'device:d_lamp', space: 'f1' },
|
||||
{ id: 'quality-virtual', binding: 'virtual', is_light: true, space: 'f1',
|
||||
glow_radius_cm: 120, glow_color: { c: '#b552dd', bri: .43 } }];
|
||||
return cfg;
|
||||
});
|
||||
});
|
||||
await page.waitForFunction(() => window.__card.shadowRoot.querySelectorAll('[data-led-field]').length === 2);
|
||||
await page.waitForTimeout(600);
|
||||
check('fixture includes ordinary Glow outside LED quality scope', await page.evaluate(() =>
|
||||
window.__card.shadowRoot.querySelectorAll('[data-glow-source]').length > 0));
|
||||
const visibilityGuard = await page.evaluate(() => {
|
||||
const card = window.__card, parent = card.shadowRoot.querySelector('.led-fields');
|
||||
const original = parent.getAttribute('style'), result = {};
|
||||
const restore = () => original === null ? parent.removeAttribute('style') : parent.setAttribute('style', original);
|
||||
try {
|
||||
for (const [property, value] of [['opacity', '0'], ['display', 'none']]) {
|
||||
restore(); parent.style.setProperty(property, value);
|
||||
const q = window.__ledZoomOracle.quality(card);
|
||||
result[property] = q.fields.length === 2 && q.fields.every(f => !f.visible)
|
||||
&& q.ordinary.length > 0 && q.ordinary.every(f => f.visible);
|
||||
}
|
||||
} finally { restore(); }
|
||||
result.restored = parent.getAttribute('style') === original
|
||||
&& window.__ledZoomOracle.quality(card).fields.every(f => f.visible);
|
||||
return result;
|
||||
});
|
||||
for (const [kind, caught] of Object.entries(visibilityGuard))
|
||||
check(`visibility oracle negative control ${kind}: ancestor hiding cannot pass as visible light`, caught);
|
||||
for (const motion of ['no-preference', 'reduce']) {
|
||||
await page.emulateMedia({ reducedMotion: motion });
|
||||
await idle();
|
||||
await page.evaluate(() => { window.__qualityBefore = window.__ledZoomOracle.snapshot(window.__card); });
|
||||
await wheel();
|
||||
const works = await active(`${motion} trusted wheel`);
|
||||
if (process.argv.includes('--red-witness')) break;
|
||||
// Baseline fails the named active24 assertion, not setup/import/timeouts.
|
||||
if (!works) continue;
|
||||
const activeInvariant = await page.evaluate(() => window.__ledZoomOracle.unchanged(window.__card, window.__qualityBefore));
|
||||
check(`${motion}: active coarse preserves geometry, DOM and cache`, activeInvariant.stable && activeInvariant.cacheStable);
|
||||
await restored(`${motion} wheel idle`);
|
||||
const invariant = await page.evaluate(() => window.__ledZoomOracle.unchanged(window.__card, window.__qualityBefore));
|
||||
check(`${motion}: camera retains fields/masks/clips/tubes/ordinary DOM and geometry`, invariant.stable);
|
||||
check(`${motion}: camera never recomputes geometry or grows cache`, invariant.cacheStable);
|
||||
results.push({ motion, permittedTransparentHitWidthChanges: invariant.hitWidthChanges });
|
||||
await wheel();
|
||||
const still = await point(); await page.mouse.move(still.x, still.y); await page.mouse.down();
|
||||
check(`${motion}: stationary pointerdown cannot shorten an active quality lease`, (await quality()).coarse);
|
||||
await page.mouse.up();
|
||||
check(`${motion}: stationary pointerup cannot shorten an active quality lease`, (await quality()).coarse);
|
||||
await restored(`${motion} stationary pointer deadline`);
|
||||
|
||||
// Two native contacts, not _zoomAt or direct calls of pointer handlers.
|
||||
const p = await point();
|
||||
await page.evaluate(() => {
|
||||
window.__qualityTouchEvents = [];
|
||||
const stage = window.__card.shadowRoot.querySelector('.stage');
|
||||
for (const type of ['pointerdown', 'pointermove', 'pointerup', 'pointercancel', 'lostpointercapture']) {
|
||||
stage.addEventListener(type, e => window.__qualityTouchEvents.push({ type, trusted: e.isTrusted,
|
||||
id: e.pointerId, x: e.clientX, scale: window.__card._zoom }));
|
||||
}
|
||||
});
|
||||
await touch('touchStart', [[p.x - 50, p.y], [p.x + 50, p.y]]);
|
||||
await touch('touchMove', [[p.x - 75, p.y], [p.x + 75, p.y]]);
|
||||
await twoFrames();
|
||||
await active(`${motion} trusted pinch`);
|
||||
await page.waitForTimeout(230); // deadline is scale silence, not pointerup.
|
||||
check(`${motion}: held pinch restores48 after scale silence`, !(await quality()).coarse);
|
||||
await touch('touchMove', [[p.x - 90, p.y], [p.x + 90, p.y]]);
|
||||
await twoFrames();
|
||||
await active(`${motion} held pinch restart`);
|
||||
results.push({ motion, touchEvents: await page.evaluate(() => window.__qualityTouchEvents) });
|
||||
await touch('touchCancel', []);
|
||||
await restored(`${motion} cancelled pinch`);
|
||||
|
||||
await touch('touchStart', [[p.x - 50, p.y], [p.x + 50, p.y]]);
|
||||
await touch('touchMove', [[p.x - 65, p.y], [p.x + 65, p.y]]); await twoFrames();
|
||||
await active(`${motion} pinch before lost capture`);
|
||||
check(`${motion}: trusted contact capture is released`, await page.evaluate(() => {
|
||||
const stage = window.__card.shadowRoot.querySelector('.stage');
|
||||
const down = window.__qualityTouchEvents.filter(e => e.type === 'pointerdown').slice(-2);
|
||||
let released = 0;
|
||||
for (const e of down) {
|
||||
const target = [...window.__card.shadowRoot.querySelectorAll('*')].find(n => n.hasPointerCapture?.(e.id));
|
||||
if (target) { target.releasePointerCapture(e.id); released++; }
|
||||
}
|
||||
return released > 0;
|
||||
}));
|
||||
await twoFrames(); await page.waitForTimeout(230);
|
||||
check(`${motion}: lost capture cannot leave coarse stuck`, !(await quality()).coarse);
|
||||
await touch('touchCancel', []);
|
||||
|
||||
// Pan moves the centre without changing scale. A stationary two-contact
|
||||
// gesture also cannot request coarse or keep a stale deadline alive.
|
||||
await touch('touchStart', [[p.x - 50, p.y], [p.x + 50, p.y]]);
|
||||
check(`${motion}: stationary pinch stays full48`, !(await quality()).coarse);
|
||||
await touch('touchEnd', []);
|
||||
const beforePan = await page.evaluate(() => ({ zoom: window.__card._zoom, ...window.__card._view }));
|
||||
await page.mouse.move(p.x, p.y); await page.mouse.down();
|
||||
await page.mouse.move(p.x + 30, p.y + 18, { steps: 3 });
|
||||
check(`${motion}: pan stays full48`, !(await quality()).coarse);
|
||||
await page.mouse.up();
|
||||
const afterPan = await page.evaluate(() => ({ zoom: window.__card._zoom, ...window.__card._view }));
|
||||
check(`${motion}: pan witness actually moves only the centre`, beforePan.zoom === afterPan.zoom
|
||||
&& (beforePan.x !== afterPan.x || beforePan.y !== afterPan.y));
|
||||
|
||||
await page.locator('[data-hp="zoom-in"]').click();
|
||||
await page.evaluate(() => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))));
|
||||
await active(`${motion} toolbar zoom`);
|
||||
await idle();
|
||||
await page.locator('[data-hp="zoom-fit"]').click();
|
||||
await page.evaluate(() => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))));
|
||||
await active(`${motion} toolbar fit`);
|
||||
await restored(`${motion} fit idle`);
|
||||
const centre = await point();
|
||||
await page.mouse.move(centre.x, centre.y); await page.mouse.down();
|
||||
await page.mouse.move(centre.x + 45, centre.y + 20, { steps: 3 }); await page.mouse.up();
|
||||
await page.evaluate(() => {
|
||||
window.__centreBefore = { zoom: window.__card._zoom, ...window.__card._view };
|
||||
window.__centreCoarse = [];
|
||||
window.__centreObserver = new MutationObserver(rows => {
|
||||
for (const row of rows) if (row.attributeName === 'data-led-zoom-quality')
|
||||
window.__centreCoarse.push(row.oldValue, window.__card.getAttribute('data-led-zoom-quality'));
|
||||
});
|
||||
window.__centreObserver.observe(window.__card, { attributes: true, attributeOldValue: true });
|
||||
});
|
||||
await page.locator('[data-hp="zoom-fit"]').click(); await idle();
|
||||
const centred = await page.evaluate(() => {
|
||||
window.__centreObserver.disconnect();
|
||||
return { before: window.__centreBefore, after: { zoom: window.__card._zoom, ...window.__card._view },
|
||||
coarse: window.__centreCoarse };
|
||||
});
|
||||
check(`${motion}: centre-only fit actually changes centre`, centred.before.x !== centred.after.x || centred.before.y !== centred.after.y);
|
||||
check(`${motion}: centre-only fit preserves scale`, centred.before.zoom, centred.after.zoom);
|
||||
check(`${motion}: centre-only interpolated fit never requests coarse`, centred.coarse.includes('coarse'), false);
|
||||
}
|
||||
|
||||
if (!process.argv.includes('--red-witness') && results.some(r => r.coarse)) {
|
||||
// At the scale clamp a wheel event is not an actual scale change.
|
||||
await page.emulateMedia({ reducedMotion: 'reduce' });
|
||||
let reachedMax = false;
|
||||
for (let i = 0; i < 25; i++) {
|
||||
await wheel();
|
||||
if (await page.evaluate(() => window.__card._zoom >= 8)) { reachedMax = true; break; }
|
||||
}
|
||||
check('clamp witness reaches maximum by real wheel while coarse is active', reachedMax && (await quality()).coarse);
|
||||
await page.evaluate(() => { window.__clampStarted = performance.now(); });
|
||||
await page.waitForTimeout(45);
|
||||
await page.evaluate(() => { window.__colourCache = [...window.__card.shadowRoot.querySelectorAll('.led-fields')]
|
||||
.map(n => [n.dataset.ledCache, n.dataset.ledRecomputes]); });
|
||||
await wheel();
|
||||
check('clamped wheel during coarse does not cancel the current lease', (await quality()).coarse);
|
||||
await page.evaluate(async () => {
|
||||
await new Promise(resolve => setTimeout(resolve, Math.max(0, 175 - (performance.now() - window.__clampStarted))));
|
||||
});
|
||||
check('clamped wheel does not extend the original lease', !(await quality()).coarse);
|
||||
await idle();
|
||||
const maxScale = await page.evaluate(() => window.__card._zoom);
|
||||
await wheel();
|
||||
check('clamped wheel leaves scale unchanged', await page.evaluate(() => window.__card._zoom), maxScale);
|
||||
check('clamped wheel neither enters coarse nor extends its deadline', !(await quality()).coarse);
|
||||
await page.locator('[data-hp="zoom-fit"]').click(); await idle();
|
||||
|
||||
await wheel();
|
||||
await setState('on', { rgb_color: [225, 70, 35], brightness: 120 });
|
||||
const changed = await quality();
|
||||
check('new RGB arrives while coarse, not after restoration', changed.coarse
|
||||
&& changed.fields.find(f => f.id === 'quality')?.fill.toLowerCase() === '#e14623');
|
||||
const rgbAlpha = Number(changed.fields.find(f => f.id === 'quality')?.alpha);
|
||||
await setState('on', { brightness: 40 });
|
||||
const dimmed = await quality(), alpha = dimmed.fields.find(f => f.id === 'quality')?.alpha;
|
||||
check('brightness-only update dims current coarse field immediately', dimmed.coarse
|
||||
&& Number(alpha) > 0 && Number(alpha) < rgbAlpha);
|
||||
await restored('latest RGB/brightness');
|
||||
check('restore retains latest RGB and brightness', await page.evaluate(alpha => {
|
||||
const pool = window.__card.shadowRoot.querySelector('[data-led-field="quality"] .led-pool');
|
||||
return pool.getAttribute('fill').toLowerCase() === '#e14623' && pool.getAttribute('fill-opacity') === alpha;
|
||||
}, alpha));
|
||||
check('RGB/brightness updates never recompute geometry or grow caches', await page.evaluate(() =>
|
||||
JSON.stringify(window.__colourCache) === JSON.stringify([...window.__card.shadowRoot.querySelectorAll('.led-fields')]
|
||||
.map(n => [n.dataset.ledCache, n.dataset.ledRecomputes]))));
|
||||
for (const state of ['off', 'unavailable']) {
|
||||
await wheel(); await setState(state); await page.waitForTimeout(650);
|
||||
check(`${state} during coarse is never resurrected by restore`, await page.evaluate(() =>
|
||||
!window.__card.shadowRoot.querySelector('[data-led-field="quality"]')));
|
||||
await wheel(); await setState('on', { rgb_color: [117, 197, 42], brightness: 190 });
|
||||
const appeared = await quality(), newFields = appeared.fields.filter(f => f.id === 'quality');
|
||||
check(`new on field during coarse uses current quality (${state})`, appeared.coarse && newFields.length === 1
|
||||
&& newFields.every(f => f.retained === 48 && f.painted === 24 && f.correct24));
|
||||
await page.waitForTimeout(650); await restored(`on after ${state}`);
|
||||
}
|
||||
|
||||
await page.evaluate(() => {
|
||||
window.__resizeCoarse = false;
|
||||
window.__resizeObserver = new MutationObserver(rows => {
|
||||
window.__resizeCoarse ||= rows.some(row => row.oldValue === 'coarse')
|
||||
|| window.__card.hasAttribute('data-led-zoom-quality');
|
||||
});
|
||||
window.__resizeObserver.observe(window.__card, { attributes: true, attributeOldValue: true,
|
||||
attributeFilter: ['data-led-zoom-quality'] });
|
||||
});
|
||||
await page.setViewportSize({ width: 1020, height: 820 }); await page.waitForTimeout(220);
|
||||
check('structural resize alone never activates coarse, including intermediate frames', await page.evaluate(() => {
|
||||
window.__resizeObserver.disconnect(); return !window.__resizeCoarse && !window.__card.hasAttribute('data-led-zoom-quality');
|
||||
}));
|
||||
await page.setViewportSize({ width: 1000, height: 820 }); await page.waitForTimeout(220);
|
||||
await page.evaluate(async () => {
|
||||
const stat = document.createElement('houseplan-space-card');
|
||||
stat.setConfig({ type: 'custom:houseplan-space-card', space: 'f1', light_pools: true, live_states: true });
|
||||
stat.hass = window.__card.hass; stat.style.cssText = 'position:fixed;left:-1200px;width:700px';
|
||||
document.body.appendChild(stat); window.__qualityStatic = stat; await stat.updateComplete;
|
||||
});
|
||||
await page.waitForTimeout(600); await wheel();
|
||||
check('static owner never inherits main owner coarse', await page.evaluate(() => {
|
||||
const q = window.__ledZoomOracle.quality(window.__qualityStatic);
|
||||
return !q.coarse && q.fields.length === 2 && q.fields.every(f => f.retained === 48 && f.painted === 48);
|
||||
}));
|
||||
await idle();
|
||||
for (const mode of ['plan', 'view']) {
|
||||
await wheel(); await page.evaluate(mode => window.__hpTest.setMode(mode), mode);
|
||||
check(`mode ${mode} resets coarse`, !(await quality()).coarse);
|
||||
}
|
||||
await wheel(); await page.evaluate(async () => { await window.__hpTest.setVolumetricView(true); });
|
||||
check('projection adoption resets coarse', !(await quality()).coarse);
|
||||
await page.evaluate(() => window.__hpTest.setVolumetricView(false));
|
||||
await wheel(); await page.evaluate(() => window.__hpTest.switchSpace('garden'));
|
||||
check('space switch resets coarse', !(await quality()).coarse);
|
||||
await page.evaluate(() => window.__hpTest.switchSpace('f1')); await page.waitForTimeout(600);
|
||||
await wheel();
|
||||
check('document hidden listener resets coarse synchronously', await page.evaluate(() => {
|
||||
// Browser visibility seam: dispatch the real listener, never call the
|
||||
// card's private visibility handler. Restore the original descriptor.
|
||||
const descriptor = Object.getOwnPropertyDescriptor(document, 'visibilityState');
|
||||
try {
|
||||
Object.defineProperty(document, 'visibilityState', { configurable: true, get: () => 'hidden' });
|
||||
document.dispatchEvent(new Event('visibilitychange'));
|
||||
return !window.__card.hasAttribute('data-led-zoom-quality');
|
||||
} finally {
|
||||
if (descriptor) Object.defineProperty(document, 'visibilityState', descriptor);
|
||||
else delete document.visibilityState;
|
||||
document.dispatchEvent(new Event('visibilitychange'));
|
||||
}
|
||||
}));
|
||||
await wheel();
|
||||
check('disconnect immediately removes coarse', await page.evaluate(() => {
|
||||
const c = window.__card; window.__qualityParent = c.parentNode; c.remove();
|
||||
return !c.hasAttribute('data-led-zoom-quality');
|
||||
}));
|
||||
await page.waitForTimeout(220);
|
||||
check('cancelled callback cannot reapply coarse to disconnected owner', !(await quality()).coarse);
|
||||
await page.evaluate(async () => { window.__qualityParent.appendChild(window.__card); await window.__card.updateComplete; });
|
||||
await page.waitForTimeout(650);
|
||||
await wheel(); await active('reconnected owner new wheel'); await restored('reconnected owner');
|
||||
await page.evaluate(() => window.__qualityStatic.remove());
|
||||
}
|
||||
} finally {
|
||||
await cdp.detach();
|
||||
await finish(browser, results);
|
||||
}
|
||||
@@ -1,544 +1,544 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"fingerprint": "24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29",
|
||||
"fingerprint": "ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172",
|
||||
"entry": "houseplan-card.js",
|
||||
"panelEntry": "houseplan-panel.js",
|
||||
"initialViewFiles": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-card.js"
|
||||
],
|
||||
"initialViewGzipBytes": 300064,
|
||||
"initialViewGzipBytes": 300133,
|
||||
"initialPanelFiles": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-panel.js"
|
||||
],
|
||||
"initialPanelGzipBytes": 301586,
|
||||
"initialPanelGzipBytes": 301657,
|
||||
"initialPanelOnlyFiles": [
|
||||
"houseplan-panel.js"
|
||||
],
|
||||
"initialPanelOnlyGzipBytes": 2325,
|
||||
"initialPanelOnlyGzipBytes": 2328,
|
||||
"lazyFiles": [
|
||||
"houseplan-assets/backdrop-pick-xG7bdqbY.js",
|
||||
"houseplan-assets/de-DSKqUoCg.js",
|
||||
"houseplan-assets/editor-C9FmtTs8.js",
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/fr-CqDDhzWi.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-2wGCtXGY.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-BO5rUhlm.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-DoUUbpA1.js",
|
||||
"houseplan-assets/iso-scene-render-uTVYGm6V.js",
|
||||
"houseplan-assets/led-strip-editor-B7Pkp8iy.js",
|
||||
"houseplan-assets/led-strip-geometry-BKCkc26J.js",
|
||||
"houseplan-assets/led-strip-runtime-BFsRdnaN.js",
|
||||
"houseplan-assets/live-interaction-runtime-B0JDzeVU.js",
|
||||
"houseplan-assets/moon-runtime-BNmu8sSV.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js",
|
||||
"houseplan-assets/pdf-export-F5-Roup-.js",
|
||||
"houseplan-assets/space-editor-Da26dQiU.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-DRNznxwG.js",
|
||||
"houseplan-assets/zigbee-topology-D8kIFY6J.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-DbX__eJD.js"
|
||||
"houseplan-assets/backdrop-pick-DHF8oaNH.js",
|
||||
"houseplan-assets/de-C4xCvmYr.js",
|
||||
"houseplan-assets/editor-CFo6BxSe.js",
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/fr-BJJefRIG.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-CqreRULk.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DOfjrx0M.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-BAaZNyAl.js",
|
||||
"houseplan-assets/iso-scene-render-CR7lxY-w.js",
|
||||
"houseplan-assets/led-strip-editor-kcNyoue_.js",
|
||||
"houseplan-assets/led-strip-geometry-DaLifxqY.js",
|
||||
"houseplan-assets/led-strip-runtime-Bx6qpdWk.js",
|
||||
"houseplan-assets/live-interaction-runtime-C1983oH8.js",
|
||||
"houseplan-assets/moon-runtime-Ca1mxhJl.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js",
|
||||
"houseplan-assets/pdf-export-C-Y4BSVu.js",
|
||||
"houseplan-assets/space-editor-EDOQT48H.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-CcLP5B5v.js",
|
||||
"houseplan-assets/zigbee-topology-BG_a6cIb.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-CynQ2m3K.js"
|
||||
],
|
||||
"lazyGzipBytes": 477955,
|
||||
"lazyGzipBytes": 478817,
|
||||
"lazyEditorFiles": [
|
||||
"houseplan-assets/backdrop-pick-xG7bdqbY.js",
|
||||
"houseplan-assets/editor-C9FmtTs8.js",
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-2wGCtXGY.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js",
|
||||
"houseplan-assets/zigbee-topology-D8kIFY6J.js"
|
||||
"houseplan-assets/backdrop-pick-DHF8oaNH.js",
|
||||
"houseplan-assets/editor-CFo6BxSe.js",
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-CqreRULk.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js",
|
||||
"houseplan-assets/zigbee-topology-BG_a6cIb.js"
|
||||
],
|
||||
"lazyEditorGzipBytes": 240853,
|
||||
"lazyEditorGzipBytes": 240843,
|
||||
"lazyOnboardingFiles": [
|
||||
"houseplan-assets/backdrop-pick-xG7bdqbY.js",
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-BO5rUhlm.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js"
|
||||
"houseplan-assets/backdrop-pick-DHF8oaNH.js",
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DOfjrx0M.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js"
|
||||
],
|
||||
"lazyOnboardingGzipBytes": 28403,
|
||||
"lazyOnboardingGzipBytes": 28402,
|
||||
"lazyLocaleFiles": [
|
||||
"houseplan-assets/de-DSKqUoCg.js",
|
||||
"houseplan-assets/fr-CqDDhzWi.js"
|
||||
"houseplan-assets/de-C4xCvmYr.js",
|
||||
"houseplan-assets/fr-BJJefRIG.js"
|
||||
],
|
||||
"lazyLocaleGzipBytes": 53862,
|
||||
"lazyLocaleGzipBytes": 53876,
|
||||
"lazyIsometricFiles": [
|
||||
"houseplan-assets/iso-scene-render-uTVYGm6V.js"
|
||||
"houseplan-assets/iso-scene-render-CR7lxY-w.js"
|
||||
],
|
||||
"lazyIsometricGzipBytes": 12346,
|
||||
"lazyIsometricGzipBytes": 12348,
|
||||
"lazyFurnitureArtFiles": [
|
||||
"houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js"
|
||||
"houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js"
|
||||
],
|
||||
"lazyFurnitureArtGzipBytes": 16938,
|
||||
"lazyFurnitureArtGzipBytes": 16936,
|
||||
"lazyPdfFiles": [
|
||||
"houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js",
|
||||
"houseplan-assets/pdf-export-F5-Roup-.js"
|
||||
"houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js",
|
||||
"houseplan-assets/pdf-export-C-Y4BSVu.js"
|
||||
],
|
||||
"lazyPdfGzipBytes": 126934,
|
||||
"lazyMoonFiles": [
|
||||
"houseplan-assets/moon-runtime-BNmu8sSV.js"
|
||||
"houseplan-assets/moon-runtime-Ca1mxhJl.js"
|
||||
],
|
||||
"lazyMoonGzipBytes": 11712,
|
||||
"lazyMoonGzipBytes": 11713,
|
||||
"lazyLedFiles": [
|
||||
"houseplan-assets/led-strip-field-TGhQdAfA.js",
|
||||
"houseplan-assets/led-strip-geometry-BKCkc26J.js",
|
||||
"houseplan-assets/led-strip-runtime-BFsRdnaN.js"
|
||||
"houseplan-assets/led-strip-field-ObbckCkp.js",
|
||||
"houseplan-assets/led-strip-geometry-DaLifxqY.js",
|
||||
"houseplan-assets/led-strip-runtime-Bx6qpdWk.js"
|
||||
],
|
||||
"lazyLedGzipBytes": 9511,
|
||||
"lazyLedGzipBytes": 10613,
|
||||
"lazyLedEditorFiles": [
|
||||
"houseplan-assets/led-strip-editor-B7Pkp8iy.js",
|
||||
"houseplan-assets/led-strip-geometry-BKCkc26J.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js"
|
||||
"houseplan-assets/led-strip-editor-kcNyoue_.js",
|
||||
"houseplan-assets/led-strip-geometry-DaLifxqY.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js"
|
||||
],
|
||||
"lazyLedEditorGzipBytes": 10025,
|
||||
"lazyLedEditorGzipBytes": 10707,
|
||||
"lazyNamespaceLocaleFiles": [
|
||||
"houseplan-assets/led-de-C4BpOh4s.js",
|
||||
"houseplan-assets/led-fr-CsvnerHk.js",
|
||||
"houseplan-assets/led-ru-CfjKBx2L.js",
|
||||
"houseplan-assets/settings-de-BpqGn3Xm.js",
|
||||
"houseplan-assets/settings-fr-CFDoiT4M.js",
|
||||
"houseplan-assets/settings-ru-D0wOiPGO.js",
|
||||
"houseplan-assets/support-de-CDLIF9_X.js",
|
||||
"houseplan-assets/support-fr-DKLccGlQ.js",
|
||||
"houseplan-assets/support-ru-Bvv4yeV8.js",
|
||||
"houseplan-assets/tools-de-C1v4tnST.js",
|
||||
"houseplan-assets/tools-fr-DKItIveg.js",
|
||||
"houseplan-assets/tools-ru-C8TUDL9T.js",
|
||||
"houseplan-assets/topology-de-CVO3cPMO.js",
|
||||
"houseplan-assets/topology-fr-Dk6k34nb.js",
|
||||
"houseplan-assets/topology-ru-DlSB4Cen.js"
|
||||
"houseplan-assets/led-de-yp1GefDC.js",
|
||||
"houseplan-assets/led-fr-CHODsRJm.js",
|
||||
"houseplan-assets/led-ru-D2-9Yy6l.js",
|
||||
"houseplan-assets/settings-de-ZP6F6dIF.js",
|
||||
"houseplan-assets/settings-fr-doqd_TsB.js",
|
||||
"houseplan-assets/settings-ru-CQ1W2gik.js",
|
||||
"houseplan-assets/support-de-haZQ7q8i.js",
|
||||
"houseplan-assets/support-fr-DnRyZ_Ga.js",
|
||||
"houseplan-assets/support-ru-B7zzXsuL.js",
|
||||
"houseplan-assets/tools-de-CYXPPfUH.js",
|
||||
"houseplan-assets/tools-fr-DvygRB10.js",
|
||||
"houseplan-assets/tools-ru-2xmu2w_8.js",
|
||||
"houseplan-assets/topology-de-COda8n9n.js",
|
||||
"houseplan-assets/topology-fr-BT9yhzay.js",
|
||||
"houseplan-assets/topology-ru-C-d06yvD.js"
|
||||
],
|
||||
"lazyNamespaceLocaleGzipBytes": 30050,
|
||||
"lazyNamespaceLocaleGzipBytes": 30082,
|
||||
"files": [
|
||||
{
|
||||
"path": "houseplan-assets/backdrop-pick-xG7bdqbY.js",
|
||||
"sha256": "f04e820a50dc05ded69553a09b2b61914f2fa0a0e0698697d51ca6c1a1a77b6d",
|
||||
"path": "houseplan-assets/backdrop-pick-DHF8oaNH.js",
|
||||
"sha256": "7262c67e3f88f44ee6c6a0973073d1f7c3c9f177ff8254758a21c4e3867c5b06",
|
||||
"rawBytes": 55329,
|
||||
"gzipBytes": 18026,
|
||||
"gzipBytes": 18027,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js"
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/settings-de-BpqGn3Xm.js",
|
||||
"houseplan-assets/settings-fr-CFDoiT4M.js",
|
||||
"houseplan-assets/settings-ru-D0wOiPGO.js"
|
||||
"houseplan-assets/settings-de-ZP6F6dIF.js",
|
||||
"houseplan-assets/settings-fr-doqd_TsB.js",
|
||||
"houseplan-assets/settings-ru-CQ1W2gik.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/de-DSKqUoCg.js",
|
||||
"sha256": "9618183c21f82b810c602576b95608a5f73510b1ac582eb904f8612828b8bea7",
|
||||
"path": "houseplan-assets/de-C4xCvmYr.js",
|
||||
"sha256": "e3778d3139c05e075281f96460e15a6aaafe8dd9200306b0a3799a1bc5710828",
|
||||
"rawBytes": 89873,
|
||||
"gzipBytes": 27164,
|
||||
"gzipBytes": 27169,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/editor-C9FmtTs8.js",
|
||||
"sha256": "da94ef9e376c8250a9edd8e6a37dad2dc18b60296d4c69b1ecab2a3d605a7a00",
|
||||
"path": "houseplan-assets/editor-CFo6BxSe.js",
|
||||
"sha256": "81d90441a06989a01a09c19e8c73567b80cd4d6ba52e4ea4796e096e1e5b7aa4",
|
||||
"rawBytes": 3833,
|
||||
"gzipBytes": 1583,
|
||||
"gzipBytes": 1584,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"sha256": "0096b38769596c6794edca74089b57939cdf4fde2d786fcef3c7a960c561c8ac",
|
||||
"path": "houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"sha256": "d18d3eb8bf1dd9dda6d6fdeed84edc16d3945e8a7e23b1d88e5f2edcef0a0af6",
|
||||
"rawBytes": 25605,
|
||||
"gzipBytes": 5058,
|
||||
"gzipBytes": 5059,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/fr-CqDDhzWi.js",
|
||||
"sha256": "f82a30083b967fa2b3ce2c3fed793dbb4d459c8c5077a56a08afe1c9aefbe23c",
|
||||
"path": "houseplan-assets/fr-BJJefRIG.js",
|
||||
"sha256": "4932ff13f6fdc339e9d67123105f9bd97f051395a3c3310240fc07b119d87024",
|
||||
"rawBytes": 92266,
|
||||
"gzipBytes": 26698,
|
||||
"gzipBytes": 26707,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js",
|
||||
"sha256": "f18e38dd1e1fe810a1d9c67ee43c6af8eb7d95851bae05e08abdfce9712da869",
|
||||
"path": "houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js",
|
||||
"sha256": "10a0b09e4b1513bd02ffba047d6931add1d1106c453af03f6b3c778b5bb55e15",
|
||||
"rawBytes": 52035,
|
||||
"gzipBytes": 16938,
|
||||
"gzipBytes": 16936,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"sha256": "af7c14c7295310be32bb5c9cab08fd5d5c77505369bb1d770db314346e0dbd4d",
|
||||
"rawBytes": 1056226,
|
||||
"gzipBytes": 299261,
|
||||
"path": "houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"sha256": "37c784960d53789a66988a4e4ab8a8afe88aeddb80c5f244f3d453ef86c3ba8c",
|
||||
"rawBytes": 1056405,
|
||||
"gzipBytes": 299329,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/de-DSKqUoCg.js",
|
||||
"houseplan-assets/editor-C9FmtTs8.js",
|
||||
"houseplan-assets/fr-CqDDhzWi.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-2wGCtXGY.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-BO5rUhlm.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-DoUUbpA1.js",
|
||||
"houseplan-assets/iso-scene-render-uTVYGm6V.js",
|
||||
"houseplan-assets/led-strip-editor-B7Pkp8iy.js",
|
||||
"houseplan-assets/led-strip-runtime-BFsRdnaN.js",
|
||||
"houseplan-assets/live-interaction-runtime-B0JDzeVU.js",
|
||||
"houseplan-assets/moon-runtime-BNmu8sSV.js",
|
||||
"houseplan-assets/pdf-export-F5-Roup-.js",
|
||||
"houseplan-assets/space-editor-Da26dQiU.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-DRNznxwG.js"
|
||||
"houseplan-assets/de-C4xCvmYr.js",
|
||||
"houseplan-assets/editor-CFo6BxSe.js",
|
||||
"houseplan-assets/fr-BJJefRIG.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-CqreRULk.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DOfjrx0M.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-BAaZNyAl.js",
|
||||
"houseplan-assets/iso-scene-render-CR7lxY-w.js",
|
||||
"houseplan-assets/led-strip-editor-kcNyoue_.js",
|
||||
"houseplan-assets/led-strip-runtime-Bx6qpdWk.js",
|
||||
"houseplan-assets/live-interaction-runtime-C1983oH8.js",
|
||||
"houseplan-assets/moon-runtime-Ca1mxhJl.js",
|
||||
"houseplan-assets/pdf-export-C-Y4BSVu.js",
|
||||
"houseplan-assets/space-editor-EDOQT48H.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-CcLP5B5v.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-2wGCtXGY.js",
|
||||
"sha256": "1ab3bc4e68c4c60f4e2f8b255617c228f0d0feaa13547275f0ee1dd17c10aaba",
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-CqreRULk.js",
|
||||
"sha256": "772b538d5b87265a0e88117f01034a3ccadd5f4e928ced77cb99254ab74e299a",
|
||||
"rawBytes": 695353,
|
||||
"gzipBytes": 194538,
|
||||
"gzipBytes": 194530,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/backdrop-pick-xG7bdqbY.js",
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js",
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js",
|
||||
"houseplan-assets/zigbee-topology-D8kIFY6J.js"
|
||||
"houseplan-assets/backdrop-pick-DHF8oaNH.js",
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js",
|
||||
"houseplan-assets/zigbee-topology-BG_a6cIb.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/support-de-CDLIF9_X.js",
|
||||
"houseplan-assets/support-fr-DKLccGlQ.js",
|
||||
"houseplan-assets/support-ru-Bvv4yeV8.js",
|
||||
"houseplan-assets/tools-de-C1v4tnST.js",
|
||||
"houseplan-assets/tools-fr-DKItIveg.js",
|
||||
"houseplan-assets/tools-ru-C8TUDL9T.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-DbX__eJD.js"
|
||||
"houseplan-assets/support-de-haZQ7q8i.js",
|
||||
"houseplan-assets/support-fr-DnRyZ_Ga.js",
|
||||
"houseplan-assets/support-ru-B7zzXsuL.js",
|
||||
"houseplan-assets/tools-de-CYXPPfUH.js",
|
||||
"houseplan-assets/tools-fr-DvygRB10.js",
|
||||
"houseplan-assets/tools-ru-2xmu2w_8.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-CynQ2m3K.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-BO5rUhlm.js",
|
||||
"sha256": "95f00d356328a64eafb3812a68f9ac8e5be3820d132d591fccf1f4b1ccacdcdf",
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-DOfjrx0M.js",
|
||||
"sha256": "242ae570916ec2a000a8a0079a948826faeb45c0968cc1006e9670f9d3c4088b",
|
||||
"rawBytes": 15886,
|
||||
"gzipBytes": 4909,
|
||||
"gzipBytes": 4907,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/backdrop-pick-xG7bdqbY.js",
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js"
|
||||
"houseplan-assets/backdrop-pick-DHF8oaNH.js",
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/hp-zigbee-topology-overlay-DoUUbpA1.js",
|
||||
"sha256": "49841cdfc0566b3e88c30069ce5a58bcde76f63f388de53c44b9f95947b55880",
|
||||
"path": "houseplan-assets/hp-zigbee-topology-overlay-BAaZNyAl.js",
|
||||
"sha256": "8cb7c3f010cf7ef792a676bfd994c4498f31222dd12ded79e3e2f37dfae4f52e",
|
||||
"rawBytes": 10958,
|
||||
"gzipBytes": 3766,
|
||||
"gzipBytes": 3768,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js",
|
||||
"houseplan-assets/zigbee-topology-D8kIFY6J.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-DbX__eJD.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js",
|
||||
"houseplan-assets/zigbee-topology-BG_a6cIb.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-CynQ2m3K.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/iso-scene-render-uTVYGm6V.js",
|
||||
"sha256": "dee120646d9ce2e35ec6595a4bda0d625aae09d7ae43c494548613ef9be19d85",
|
||||
"path": "houseplan-assets/iso-scene-render-CR7lxY-w.js",
|
||||
"sha256": "1986dd43c8b76580f893c1629d494f7747f02aa8d544baff20d56fe10b470bb5",
|
||||
"rawBytes": 36700,
|
||||
"gzipBytes": 12346,
|
||||
"gzipBytes": 12348,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/led-de-C4BpOh4s.js",
|
||||
"sha256": "6a140ef9878edec937145205f21317f50141470b9e1098c1fba695ce15af2630",
|
||||
"path": "houseplan-assets/led-de-yp1GefDC.js",
|
||||
"sha256": "eb9ce8bb536ac20e11cf4fe925d68f28b1ba8a5ea02c61abf9c3bb939de0a4c4",
|
||||
"rawBytes": 1907,
|
||||
"gzipBytes": 918,
|
||||
"gzipBytes": 921,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/led-fr-CsvnerHk.js",
|
||||
"sha256": "0f2afb6c3ef7117afe0bf3b892652197fa29d11f685d18d24a4e15fece28d394",
|
||||
"path": "houseplan-assets/led-fr-CHODsRJm.js",
|
||||
"sha256": "adb428bd63b6157ef0a5d749badf94e95204c9c3b83e5f3022accf65d304435e",
|
||||
"rawBytes": 1886,
|
||||
"gzipBytes": 905,
|
||||
"gzipBytes": 906,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/led-ru-CfjKBx2L.js",
|
||||
"sha256": "e0c070ec3b7bb95a26b4cc4506d2293a9ebf3067e718d9abaaf84ad2c10bbc3b",
|
||||
"path": "houseplan-assets/led-ru-D2-9Yy6l.js",
|
||||
"sha256": "bc5575139b30d6e93e07673f47865c332e71011796929a52043645a904d6df31",
|
||||
"rawBytes": 2525,
|
||||
"gzipBytes": 1105,
|
||||
"gzipBytes": 1106,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/led-strip-editor-B7Pkp8iy.js",
|
||||
"sha256": "8755690f52b2a8e74909165b4dd074a01376c9b2e752940a1f010646f864cc2b",
|
||||
"path": "houseplan-assets/led-strip-editor-kcNyoue_.js",
|
||||
"sha256": "dac9fdcaae6444caa1773a07384155cc92b916d2ebf3498e1dfc1b3525a84753",
|
||||
"rawBytes": 21704,
|
||||
"gzipBytes": 7378,
|
||||
"gzipBytes": 7381,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/led-strip-geometry-BKCkc26J.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/led-strip-geometry-DaLifxqY.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/led-de-C4BpOh4s.js",
|
||||
"houseplan-assets/led-fr-CsvnerHk.js",
|
||||
"houseplan-assets/led-ru-CfjKBx2L.js"
|
||||
"houseplan-assets/led-de-yp1GefDC.js",
|
||||
"houseplan-assets/led-fr-CHODsRJm.js",
|
||||
"houseplan-assets/led-ru-D2-9Yy6l.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/led-strip-field-TGhQdAfA.js",
|
||||
"sha256": "1a6721643f02390b945b26bff3803c6af8ef801fb43ca450a14b6bf2f13b1819",
|
||||
"rawBytes": 7231,
|
||||
"gzipBytes": 3057,
|
||||
"path": "houseplan-assets/led-strip-field-ObbckCkp.js",
|
||||
"sha256": "b85d8de01906a7e70f28baff534cf1a211216c99b72005b6d77a0d19cbb5ce8f",
|
||||
"rawBytes": 7920,
|
||||
"gzipBytes": 3321,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/led-strip-geometry-BKCkc26J.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/led-strip-geometry-DaLifxqY.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/led-strip-geometry-BKCkc26J.js",
|
||||
"sha256": "9aa0a021e4f5a7faf186def62ce22f037bd5659e90b31078ebe43f41a56b3e59",
|
||||
"rawBytes": 5218,
|
||||
"gzipBytes": 2237,
|
||||
"path": "houseplan-assets/led-strip-geometry-DaLifxqY.js",
|
||||
"sha256": "2dfbe1509da809008c9a525a7ed02226c026aaa5c0dafcb9f3dc1c44151ed63e",
|
||||
"rawBytes": 7055,
|
||||
"gzipBytes": 2917,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/led-strip-runtime-BFsRdnaN.js",
|
||||
"sha256": "2e62eef6e40dfe65f3d772775d3282d8070d939445a14bf19ad23ffb432fda56",
|
||||
"rawBytes": 10720,
|
||||
"gzipBytes": 4217,
|
||||
"path": "houseplan-assets/led-strip-runtime-Bx6qpdWk.js",
|
||||
"sha256": "6fd2d3578e550dfbe8c38308923e21f9fddeb469f64e8fcf295dc292949ddf32",
|
||||
"rawBytes": 11179,
|
||||
"gzipBytes": 4375,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/led-strip-geometry-BKCkc26J.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/led-strip-geometry-DaLifxqY.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/led-strip-field-TGhQdAfA.js"
|
||||
"houseplan-assets/led-strip-field-ObbckCkp.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/live-interaction-runtime-B0JDzeVU.js",
|
||||
"sha256": "19e27b13aa16d5de342b3403ba55e7466690db80fc35a92dfdb5b3b6ac45c2c6",
|
||||
"path": "houseplan-assets/live-interaction-runtime-C1983oH8.js",
|
||||
"sha256": "04be552ac07f99c03713f162ce0b6129c8491dfd89da9cd8e07cd71866fe309c",
|
||||
"rawBytes": 8370,
|
||||
"gzipBytes": 3279,
|
||||
"gzipBytes": 3281,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/moon-runtime-BNmu8sSV.js",
|
||||
"sha256": "36e73d61c9d8121c4d38d6e2842640b9d10236abb28e2f1b870ccafa3d17b0b6",
|
||||
"path": "houseplan-assets/moon-runtime-Ca1mxhJl.js",
|
||||
"sha256": "97b9f55cb8e2915acb5eaa260f21e2103bc08f0e654c1c0b03c3000604319730",
|
||||
"rawBytes": 30292,
|
||||
"gzipBytes": 11712,
|
||||
"gzipBytes": 11713,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/namespace-language-Bj-cxtbx.js",
|
||||
"sha256": "f335a736ca72fe7d1cded3ec57716c8e1e6d6b4f7609e9c367dc8fe5e8362459",
|
||||
"path": "houseplan-assets/namespace-language-D86tJUeD.js",
|
||||
"sha256": "012aa30cbf8bbbb2eff6b7ef05970ff90fb2b4b576910578e3527b75e68ffda6",
|
||||
"rawBytes": 675,
|
||||
"gzipBytes": 410,
|
||||
"gzipBytes": 409,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/pdf-export-F5-Roup-.js",
|
||||
"sha256": "10a42644f96822823e8588c1b0009615a3881e01981243840a1eddfcb009636c",
|
||||
"path": "houseplan-assets/pdf-export-C-Y4BSVu.js",
|
||||
"sha256": "fba700bc14c66c695f1b3de3e82abdf5fb6b14b70773c3d0f0e185dcf2989491",
|
||||
"rawBytes": 236714,
|
||||
"gzipBytes": 109996,
|
||||
"gzipBytes": 109998,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/furniture-plan-art.generated-4xe3M3SZ.js",
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/furniture-plan-art.generated-XIg9ZqQe.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/settings-de-BpqGn3Xm.js",
|
||||
"sha256": "1cecf30cead9359ddc80868b6210e0b6f21f105bf89f1c75e87d161f8db21f2f",
|
||||
"path": "houseplan-assets/settings-de-ZP6F6dIF.js",
|
||||
"sha256": "12dd3028d2de2e4fc3bfcf2299e45e253075ca23b95235436ef1cf9156738a3e",
|
||||
"rawBytes": 8886,
|
||||
"gzipBytes": 3407,
|
||||
"gzipBytes": 3411,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/settings-fr-CFDoiT4M.js",
|
||||
"sha256": "99736640c26f4681230c0aa8f697a9419a2c81b96fce19e963c8d0dfaaaaad1a",
|
||||
"path": "houseplan-assets/settings-fr-doqd_TsB.js",
|
||||
"sha256": "0c1f01b69c2b1704ddd29e99dd7609ad679655b5241b3e2b8921057198005ff3",
|
||||
"rawBytes": 9297,
|
||||
"gzipBytes": 3381,
|
||||
"gzipBytes": 3385,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/settings-ru-D0wOiPGO.js",
|
||||
"sha256": "2bd560d5eeb8f430574a59a3c827f52eede08e39218149ed56a9b44bf6ae6e8b",
|
||||
"path": "houseplan-assets/settings-ru-CQ1W2gik.js",
|
||||
"sha256": "bad5c925aac84429d0831a1036ea3162ead8d4431db35ea577b0e806c8fbdbf1",
|
||||
"rawBytes": 12284,
|
||||
"gzipBytes": 3898,
|
||||
"gzipBytes": 3900,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/space-editor-Da26dQiU.js",
|
||||
"sha256": "013609e7975871868fa3f6955095bae588aaf57a4638763c21b14ce3de19abb5",
|
||||
"path": "houseplan-assets/space-editor-EDOQT48H.js",
|
||||
"sha256": "47339ab7329ffd1a0cbd21980c1cf2a2041d7a514ed5992e73a07abdce6fb764",
|
||||
"rawBytes": 2619,
|
||||
"gzipBytes": 1126,
|
||||
"gzipBytes": 1130,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/summary-panel-editor-Dy-Z4Phc.js",
|
||||
"sha256": "f8f1a903c3e44a7dab27d0dda7559d9c6cbd586a338891fb4e11ebc5ab1be378",
|
||||
"path": "houseplan-assets/summary-panel-editor-BZnGwuVh.js",
|
||||
"sha256": "516e3462d3896a9ca782ee0f2f3266a4da6e6b6664e92a619a23b8991e600795",
|
||||
"rawBytes": 14862,
|
||||
"gzipBytes": 3426,
|
||||
"gzipBytes": 3427,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-DRNznxwG.js"
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-CcLP5B5v.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/summary-panel-metrics-DMnGGtKb.js",
|
||||
"sha256": "4ea585b05274db86655e84bccbd9378b7f3f606407d37d0da22999636f3621da",
|
||||
"path": "houseplan-assets/summary-panel-metrics-D_ZXPJxz.js",
|
||||
"sha256": "3be26833b041416cef359e4c7086a0b0e6788680dda8f1583ae76d9256f944fa",
|
||||
"rawBytes": 2701,
|
||||
"gzipBytes": 1344,
|
||||
"gzipBytes": 1348,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/summary-panel-runtime-loaded-DRNznxwG.js",
|
||||
"sha256": "07f42710ef9c27aa47095c0be5ea57657fbfe8ec71afe12059280a82af7115a9",
|
||||
"path": "houseplan-assets/summary-panel-runtime-loaded-CcLP5B5v.js",
|
||||
"sha256": "7db94205b54996355041487465d6bade0530e5ac0d13a0eb9b7645086171ce58",
|
||||
"rawBytes": 73476,
|
||||
"gzipBytes": 20485,
|
||||
"gzipBytes": 20491,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/form-kit.styles-cMZ9Wyhz.js",
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/form-kit.styles-B6HsmMPA.js",
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/summary-panel-editor-Dy-Z4Phc.js",
|
||||
"houseplan-assets/summary-panel-metrics-DMnGGtKb.js"
|
||||
"houseplan-assets/summary-panel-editor-BZnGwuVh.js",
|
||||
"houseplan-assets/summary-panel-metrics-D_ZXPJxz.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/support-de-CDLIF9_X.js",
|
||||
"sha256": "eb503ecf5b65bdd715e848e11ba3993a130aa3cd4cc8fd5a23ed4e0b578a15c2",
|
||||
"path": "houseplan-assets/support-de-haZQ7q8i.js",
|
||||
"sha256": "f377e9d552978c850f069eeb05b20c7572f81c7a8c7b2d60838a7907f1c2b3ef",
|
||||
"rawBytes": 6167,
|
||||
"gzipBytes": 2397,
|
||||
"gzipBytes": 2400,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/support-fr-DKLccGlQ.js",
|
||||
"sha256": "dc7049414bfc596a22bcf1f13ab4831e93137406a1ed73bc7ba8ef15f200bc63",
|
||||
"path": "houseplan-assets/support-fr-DnRyZ_Ga.js",
|
||||
"sha256": "bb66f839f3b63a74212dc83d273db535dd4597e5f43c5c1b35f0e1fac401e424",
|
||||
"rawBytes": 6333,
|
||||
"gzipBytes": 2430,
|
||||
"gzipBytes": 2433,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/support-ru-Bvv4yeV8.js",
|
||||
"sha256": "90c3a0895993f4c302c40ceeec324685616aacff980b44540dfe4cd420116310",
|
||||
"path": "houseplan-assets/support-ru-B7zzXsuL.js",
|
||||
"sha256": "63ec59059f3a10c994e4292c4c2b125bb7b2916326dbae27f9975c14be008822",
|
||||
"rawBytes": 8376,
|
||||
"gzipBytes": 2875,
|
||||
"gzipBytes": 2878,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/tools-de-C1v4tnST.js",
|
||||
"sha256": "3fd6e88cba32f2f473b402e3feba44c1ab26792043ef992f0b74fbcefe19cfc7",
|
||||
"path": "houseplan-assets/tools-de-CYXPPfUH.js",
|
||||
"sha256": "411c98cf876e42d7255882d0faa486602453f871d443b0c2b59c22e3b2552efb",
|
||||
"rawBytes": 4130,
|
||||
"gzipBytes": 1375,
|
||||
"gzipBytes": 1378,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/tools-fr-DKItIveg.js",
|
||||
"sha256": "3028cd9e47e0e9495aefbff17b7aaf0de9ca45efdcac20747dcb13ab8a4e4ea7",
|
||||
"path": "houseplan-assets/tools-fr-DvygRB10.js",
|
||||
"sha256": "f7d35402d5f3504baa8c00dc8bd5170487535c1b87f9a8367a28e332cda92bf2",
|
||||
"rawBytes": 4136,
|
||||
"gzipBytes": 1358,
|
||||
"gzipBytes": 1360,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/tools-ru-C8TUDL9T.js",
|
||||
"sha256": "9cbef8d3edffaf6f3eee56da0a078b57bdd9249e45d7c73cca4d8e3a22801626",
|
||||
"path": "houseplan-assets/tools-ru-2xmu2w_8.js",
|
||||
"sha256": "14e6c4c22d615293903fc4f25d831f0af042b4e48234d4f2e114ef7d8817d57a",
|
||||
"rawBytes": 5633,
|
||||
"gzipBytes": 1695,
|
||||
"gzipBytes": 1697,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/topology-de-CVO3cPMO.js",
|
||||
"sha256": "e184d49bce8fa684d4f76f74acc02f1c1f6a60a0f5bc4d5c865b2af7af6af053",
|
||||
"path": "houseplan-assets/topology-de-COda8n9n.js",
|
||||
"sha256": "88ff4a8cccc94e3eba040af628d9a85db664cbf20586f95704e0823a6215c1f9",
|
||||
"rawBytes": 2698,
|
||||
"gzipBytes": 1337,
|
||||
"gzipBytes": 1336,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/topology-fr-Dk6k34nb.js",
|
||||
"sha256": "ad8b6fc06d7bfb65b285c416d5143f8582196e7bb68e4b924d2b7243a8f3a24b",
|
||||
"path": "houseplan-assets/topology-fr-BT9yhzay.js",
|
||||
"sha256": "60cb68c5c9086ec5f4901627a6baa3e3c8cdfda430b0977622b260984122fcfa",
|
||||
"rawBytes": 2718,
|
||||
"gzipBytes": 1354,
|
||||
"gzipBytes": 1356,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/topology-ru-DlSB4Cen.js",
|
||||
"sha256": "5564fcbd3b2d7b82f9212e81e9cf211235934c5dfe03f83f181907e7bb1fc2f7",
|
||||
"path": "houseplan-assets/topology-ru-C-d06yvD.js",
|
||||
"sha256": "ca9a68f252b8275c954ae7ff7ea56d07a8ea5cf14f18eaac285fd0c63a7bad8f",
|
||||
"rawBytes": 3669,
|
||||
"gzipBytes": 1615,
|
||||
"isEntry": false,
|
||||
@@ -546,53 +546,53 @@
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/zigbee-topology-D8kIFY6J.js",
|
||||
"sha256": "9cfaee356879c62621afd7d4759a652db6a4cda08c10a04956d131a9434a4cd1",
|
||||
"path": "houseplan-assets/zigbee-topology-BG_a6cIb.js",
|
||||
"sha256": "98d8e54ec59e2d1810b124a0a0c6c2f528c112ab17d12bc3c0a129e13c456c72",
|
||||
"rawBytes": 10958,
|
||||
"gzipBytes": 4300,
|
||||
"gzipBytes": 4298,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/topology-de-CVO3cPMO.js",
|
||||
"houseplan-assets/topology-fr-Dk6k34nb.js",
|
||||
"houseplan-assets/topology-ru-DlSB4Cen.js"
|
||||
"houseplan-assets/topology-de-COda8n9n.js",
|
||||
"houseplan-assets/topology-fr-BT9yhzay.js",
|
||||
"houseplan-assets/topology-ru-C-d06yvD.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/zigbee-topology-runtime-DbX__eJD.js",
|
||||
"sha256": "24396700d8dca1bfefb32efc2a4391a893763cf756210834e8cddaf108bfc4dc",
|
||||
"path": "houseplan-assets/zigbee-topology-runtime-CynQ2m3K.js",
|
||||
"sha256": "966ef9fa87888f0ed561d1061b36f0192c27700f040305ae038fe9509c7a7e33",
|
||||
"rawBytes": 3983,
|
||||
"gzipBytes": 1789,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js",
|
||||
"houseplan-assets/namespace-language-Bj-cxtbx.js",
|
||||
"houseplan-assets/zigbee-topology-D8kIFY6J.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js",
|
||||
"houseplan-assets/namespace-language-D86tJUeD.js",
|
||||
"houseplan-assets/zigbee-topology-BG_a6cIb.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-card.js",
|
||||
"sha256": "e6cbcbf961cb6c0384cf21f5877f34ec1c6507774b4751c839c386396127a9ec",
|
||||
"sha256": "80338f4a64b84431f95438482aeabe1cc6b0f399ccb0c600b318c0f6e6cccc38",
|
||||
"rawBytes": 1205,
|
||||
"gzipBytes": 803,
|
||||
"gzipBytes": 804,
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-panel.js",
|
||||
"sha256": "f2cab62ce7f8131965eb51673f25963e7cff04392e1b62854cb991fc38f60032",
|
||||
"sha256": "ce8c313af85073d1c54ccc74e2111c1f14c5d8e3e8539bc59ec436d6d3e0d574",
|
||||
"rawBytes": 6165,
|
||||
"gzipBytes": 2325,
|
||||
"gzipBytes": 2328,
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-BWxNZe40.js"
|
||||
"houseplan-assets/houseplan-card-BOdiRDFR.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";import{aG as e,l as o,aJ as t,eD as s,A as a,eE as i,eF as l,eG as n,E as r,c}from"./houseplan-card-BWxNZe40.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__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";import{aG as e,l as o,aJ as t,eE as s,A as a,eF as i,eG as l,eH as n,E as r,c}from"./houseplan-card-BOdiRDFR.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},_=this._schema,f=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),u=e=>c`<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._formData}
|
||||
.schema=${e}
|
||||
.computeLabel=${e=>h[e.name]||e.name}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>`;return c`
|
||||
${d(f.slice(0,3))}
|
||||
${_?c`<div class="default-floor-error" role="alert"
|
||||
${u(_.slice(0,3))}
|
||||
${f?c`<div class="default-floor-error" role="alert"
|
||||
style="color:var(--error-color,#db4437);margin:-4px 0 12px;overflow-wrap:anywhere">
|
||||
${t(s,"editor.default_floor_missing",{id:_})}
|
||||
${t(s,"editor.default_floor_missing",{id:f})}
|
||||
</div>`:a}
|
||||
${d(f.slice(3))}
|
||||
${u(_.slice(3))}
|
||||
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h);
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";var e={"led.tool":"LED-Streifen","led.hint":"Zeichnen Sie den Streifen Punkt für Punkt entlang der Wandflächen oder über den Boden; er rastet am Raster und an Wandflächen ein. Danach wählen Sie das Gerät, das er darstellt.","led.start":"Klicken, um den Streifen zu beginnen","led.start_touch":"Tippen, um den Streifen zu beginnen","led.points":"Punkte: {n} · Umschalt — 45°-Schritte · Esc — fertig · Strg+Z — Punkt entfernen","led.points_touch":"Punkte: {n} · Werkzeug wechseln zum Beenden","led.finish":"Fertig","led.stopped":"Der Streifen stößt an eine Wand","led.unbound":"Streifen ist mit keinem Gerät verbunden","led.bind":"Gerät verbinden","led.change":"Gerät wechseln","led.later":"Später","led.unbind":"Trennen","led.delete":"Streifen löschen","led.settings":"Geräteeinstellungen","led.type_note":"LED-Streifen: Form im Geräte-Editor auf dem Plan bearbeiten","led.show_strip":"Als LED-Streifen zeigen","led.show_icon":"Als Symbol zeigen","led.cancel":"Abbrechen","led.taken":"Bereits mit einem anderen LED-Streifen verbunden","led.space":"Zuerst den LED-Streifen lösen: Er gehört zu seinem Bereich","led.limit_points":"Höchstens 50 Punkte pro Streifen","led.limit_strips":"Höchstens 50 Streifen pro Bereich","led.pick_title":"Gerät für den Streifen wählen","led.pick_new":"Neues Gerät…","led.pick_empty":"Auf diesem Plan gibt es noch keine freien Geräte.","led.saved":"LED-Streifen gespeichert","led.save_failed":"Streifen nicht gespeichert: {err}","led.history_draw":"LED-Streifen","led.history_shape":"Form des LED-Streifens","led.history_view":"Darstellung des Geräts","led.optimize_walls":"Streifen durch Wände: {n} ({spaces})"};const n="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";export{e as dictionary,n as fingerprint};
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";var e={"led.tool":"LED-Streifen","led.hint":"Zeichnen Sie den Streifen Punkt für Punkt entlang der Wandflächen oder über den Boden; er rastet am Raster und an Wandflächen ein. Danach wählen Sie das Gerät, das er darstellt.","led.start":"Klicken, um den Streifen zu beginnen","led.start_touch":"Tippen, um den Streifen zu beginnen","led.points":"Punkte: {n} · Umschalt — 45°-Schritte · Esc — fertig · Strg+Z — Punkt entfernen","led.points_touch":"Punkte: {n} · Werkzeug wechseln zum Beenden","led.finish":"Fertig","led.stopped":"Der Streifen stößt an eine Wand","led.unbound":"Streifen ist mit keinem Gerät verbunden","led.bind":"Gerät verbinden","led.change":"Gerät wechseln","led.later":"Später","led.unbind":"Trennen","led.delete":"Streifen löschen","led.settings":"Geräteeinstellungen","led.type_note":"LED-Streifen: Form im Geräte-Editor auf dem Plan bearbeiten","led.show_strip":"Als LED-Streifen zeigen","led.show_icon":"Als Symbol zeigen","led.cancel":"Abbrechen","led.taken":"Bereits mit einem anderen LED-Streifen verbunden","led.space":"Zuerst den LED-Streifen lösen: Er gehört zu seinem Bereich","led.limit_points":"Höchstens 50 Punkte pro Streifen","led.limit_strips":"Höchstens 50 Streifen pro Bereich","led.pick_title":"Gerät für den Streifen wählen","led.pick_new":"Neues Gerät…","led.pick_empty":"Auf diesem Plan gibt es noch keine freien Geräte.","led.saved":"LED-Streifen gespeichert","led.save_failed":"Streifen nicht gespeichert: {err}","led.history_draw":"LED-Streifen","led.history_shape":"Form des LED-Streifens","led.history_view":"Darstellung des Geräts","led.optimize_walls":"Streifen durch Wände: {n} ({spaces})"};const n="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";export{e as dictionary,n as fingerprint};
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";var e={"led.tool":"Ruban LED","led.hint":"Tracez le ruban point par point le long des faces des murs ou sur le sol ; il s’aimante à la grille et aux faces des murs. Ensuite, choisissez l’appareil qu’il représente.","led.start":"Cliquez pour commencer le ruban","led.start_touch":"Touchez pour commencer le ruban","led.points":"Points : {n} · Maj — pas de 45° · Échap — terminer · Ctrl+Z — retirer le point","led.points_touch":"Points : {n} · changez d’outil pour terminer","led.finish":"Terminer","led.stopped":"Le ruban bute contre un mur","led.unbound":"Ruban non lié à un appareil","led.bind":"Lier un appareil","led.change":"Changer d’appareil","led.later":"Plus tard","led.unbind":"Délier","led.delete":"Supprimer le ruban","led.settings":"Réglages de l’appareil","led.type_note":"Ruban LED : modifiez sa forme sur le plan dans l’éditeur d’appareils","led.show_strip":"Afficher en ruban LED","led.show_icon":"Afficher en icône","led.cancel":"Annuler","led.taken":"Déjà lié à un autre ruban LED","led.space":"Détachez d’abord le ruban LED : il appartient à son espace","led.limit_points":"50 points au maximum par ruban","led.limit_strips":"50 rubans au maximum par espace","led.pick_title":"Choisissez l’appareil du ruban","led.pick_new":"Nouvel appareil…","led.pick_empty":"Aucun appareil libre sur ce plan pour l’instant.","led.saved":"Ruban LED enregistré","led.save_failed":"Ruban non enregistré : {err}","led.history_draw":"Ruban LED","led.history_shape":"Forme du ruban LED","led.history_view":"Représentation de l’appareil","led.optimize_walls":"Rubans traversant des murs : {n} ({spaces})"};const a="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";export{e as dictionary,a as fingerprint};
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";var e={"led.tool":"Ruban LED","led.hint":"Tracez le ruban point par point le long des faces des murs ou sur le sol ; il s’aimante à la grille et aux faces des murs. Ensuite, choisissez l’appareil qu’il représente.","led.start":"Cliquez pour commencer le ruban","led.start_touch":"Touchez pour commencer le ruban","led.points":"Points : {n} · Maj — pas de 45° · Échap — terminer · Ctrl+Z — retirer le point","led.points_touch":"Points : {n} · changez d’outil pour terminer","led.finish":"Terminer","led.stopped":"Le ruban bute contre un mur","led.unbound":"Ruban non lié à un appareil","led.bind":"Lier un appareil","led.change":"Changer d’appareil","led.later":"Plus tard","led.unbind":"Délier","led.delete":"Supprimer le ruban","led.settings":"Réglages de l’appareil","led.type_note":"Ruban LED : modifiez sa forme sur le plan dans l’éditeur d’appareils","led.show_strip":"Afficher en ruban LED","led.show_icon":"Afficher en icône","led.cancel":"Annuler","led.taken":"Déjà lié à un autre ruban LED","led.space":"Détachez d’abord le ruban LED : il appartient à son espace","led.limit_points":"50 points au maximum par ruban","led.limit_strips":"50 rubans au maximum par espace","led.pick_title":"Choisissez l’appareil du ruban","led.pick_new":"Nouvel appareil…","led.pick_empty":"Aucun appareil libre sur ce plan pour l’instant.","led.saved":"Ruban LED enregistré","led.save_failed":"Ruban non enregistré : {err}","led.history_draw":"Ruban LED","led.history_shape":"Forme du ruban LED","led.history_view":"Représentation de l’appareil","led.optimize_walls":"Rubans traversant des murs : {n} ({spaces})"};const a="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";export{e as dictionary,a as fingerprint};
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";var e={"led.tool":"LED-лента","led.hint":"Нарисуйте ленту по точкам вдоль граней стен или по полу — она прилипает к сетке и граням. После завершения выберите устройство, которое она показывает.","led.start":"Нажмите, чтобы начать ленту","led.start_touch":"Коснитесь, чтобы начать ленту","led.points":"Точек: {n} · Shift — шаг 45° · Esc — завершить · Ctrl+Z — убрать точку","led.points_touch":"Точек: {n} · смените инструмент, чтобы завершить","led.finish":"Завершить","led.stopped":"Лента упёрлась в стену","led.unbound":"Лента не привязана к устройству","led.bind":"Привязать устройство","led.change":"Сменить устройство","led.later":"Позже","led.unbind":"Отвязать","led.delete":"Удалить ленту","led.settings":"Настройки устройства","led.type_note":"LED-лента: форма редактируется на плане в редакторе устройств","led.show_strip":"Показывать LED-лентой","led.show_icon":"Показывать значком","led.cancel":"Отмена","led.taken":"Уже привязано к другой LED-ленте","led.space":"Сначала отвяжите LED-ленту: она принадлежит своему пространству","led.limit_points":"Не более 50 точек в ленте","led.limit_strips":"Не более 50 лент в пространстве","led.pick_title":"Выберите устройство для ленты","led.pick_new":"Новое устройство…","led.pick_empty":"На этом плане пока нет свободных устройств.","led.saved":"LED-лента сохранена","led.save_failed":"Лента не сохранена: {err}","led.history_draw":"LED-лента","led.history_shape":"Форма LED-ленты","led.history_view":"Вид устройства","led.optimize_walls":"Лент, проходящих сквозь стены: {n} ({spaces})"};const d="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";export{e as dictionary,d as fingerprint};
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";var e={"led.tool":"LED-лента","led.hint":"Нарисуйте ленту по точкам вдоль граней стен или по полу — она прилипает к сетке и граням. После завершения выберите устройство, которое она показывает.","led.start":"Нажмите, чтобы начать ленту","led.start_touch":"Коснитесь, чтобы начать ленту","led.points":"Точек: {n} · Shift — шаг 45° · Esc — завершить · Ctrl+Z — убрать точку","led.points_touch":"Точек: {n} · смените инструмент, чтобы завершить","led.finish":"Завершить","led.stopped":"Лента упёрлась в стену","led.unbound":"Лента не привязана к устройству","led.bind":"Привязать устройство","led.change":"Сменить устройство","led.later":"Позже","led.unbind":"Отвязать","led.delete":"Удалить ленту","led.settings":"Настройки устройства","led.type_note":"LED-лента: форма редактируется на плане в редакторе устройств","led.show_strip":"Показывать LED-лентой","led.show_icon":"Показывать значком","led.cancel":"Отмена","led.taken":"Уже привязано к другой LED-ленте","led.space":"Сначала отвяжите LED-ленту: она принадлежит своему пространству","led.limit_points":"Не более 50 точек в ленте","led.limit_strips":"Не более 50 лент в пространстве","led.pick_title":"Выберите устройство для ленты","led.pick_new":"Новое устройство…","led.pick_empty":"На этом плане пока нет свободных устройств.","led.saved":"LED-лента сохранена","led.save_failed":"Лента не сохранена: {err}","led.history_draw":"LED-лента","led.history_shape":"Форма LED-ленты","led.history_view":"Вид устройства","led.optimize_walls":"Лент, проходящих сквозь стены: {n} ({spaces})"};const l="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";export{e as dictionary,l as fingerprint};
|
||||
@@ -1,47 +0,0 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";import{et as e,eB as t,c6 as o,eI as s,eJ as i,ad as n,ed as a,O as r,eK as l,az as d,ee as c,aL as p,eL as u,eM as h,eN as f,A as m,eO as v}from"./houseplan-card-BWxNZe40.js";import{L as g,v as w,s as $,a as _,p as y,b as k,c as b,d as C,e as P}from"./led-strip-geometry-BKCkc26J.js";const M="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";let F,L=null,U=null;function x(e,t){return L||U||F===e||""===F||(U=(void 0===F?import("./led-strip-field-TGhQdAfA.js"):import(new URL(`./led-strip-field-TGhQdAfA.js?${Date.now()}`,import.meta.url).href)).then(e=>{e.LED_FIELD_FINGERPRINT===s?L=e:F=""},()=>{F=e}).finally(()=>{U=null})),L||U?.then(()=>L&&t()),L}const D=e=>e.some(e=>e.glow&&"on"===e.state),R=e=>e.map(e=>[e[0],e[1]]),E="#383838";function A(s,i){if(!s)return null;const n=Array.isArray(s.masonryGeometry)?s.masonryGeometry:[],a=[...e(s.masonryGeometry),...s.opaqueBodies],r=[];for(const e of a)for(let t=0;t<e.length;t++){const o=e[t],s=e[(t+1)%e.length];o&&s&&r.push({a:[o[0],o[1]],b:[s[0],s[1]]})}const l=25,d=new Map;r.forEach((e,t)=>{const o=Math.floor(Math.min(e.a[0],e.b[0])/l),s=Math.floor(Math.max(e.a[0],e.b[0])/l),i=Math.floor(Math.min(e.a[1],e.b[1])/l),n=Math.floor(Math.max(e.a[1],e.b[1])/l);for(let e=o;e<=s;e++)for(let o=i;o<=n;o++){const s=`${e},${o}`,i=d.get(s);i?i.push(t):d.set(s,[t])}});const c=e=>{let t=1/0,o=1/0,s=-1/0,i=-1/0;for(const n of e)t=Math.min(t,n[0]),o=Math.min(o,n[1]),s=Math.max(s,n[0]),i=Math.max(i,n[1]);return[t,o,s,i]},p=(e,t)=>e[0]>=t[0]&&e[0]<=t[2]&&e[1]>=t[1]&&e[1]<=t[3],u=n.filter(e=>e?.[0]?.length).map(e=>({box:c(e[0]),poly:e})),h=s.opaqueBodies.map(e=>({box:c(e),body:e}));return{faces:r,inside:e=>u.some(({box:o,poly:s})=>p(e,o)&&t([e[0],e[1]],[s]))||h.some(({box:t,body:s})=>p(e,t)&&o([e[0],e[1]],s)),epsilon:i,near:e=>{const t=new Set;for(let o=Math.floor(e[0]/l);o<=Math.floor(e[2]/l);o++)for(let s=Math.floor(e[1]/l);s<=Math.floor(e[3]/l);s++)for(const e of d.get(`${o},${s}`)||[])t.add(e);return[...t].map(e=>r[e])}}}function I(e){const t=e.candidate?.sourceEid||e.device.primary||"",o=t?e.hass?.states?.[t]?.state:void 0,s="unavailable"===o||"unknown"===o||!!t&&!!e.hass&&!e.hass.states?.[t]&&!e.device.virtual,i=Number(e.device.marker?.glow_radius_cm),n=Number.isFinite(i)&&i>0?i/e.cellCm*e.gridPitch:e.defaultRadius,a=s?null:e.candidate?.appearance??null;return{strip:e.strip,device:e.device,state:s?"unavailable":a?"on":"off",glow:e.glow,appearance:a,radius:n}}function T(e,t){const o=(s=t.d,P*s);var s;return{t:o,path:b(R(e.strip.points),t.faces,o/2)}}function j(e){if(!e.views.length)return d``;const t=e.handlers,o=[...e.views].sort((e,t)=>e.strip.id<t.strip.id?1:e.strip.id>t.strip.id?-1:0),s=e.iso;return d`<g class="led-strips" data-hp-led-strips="${o.length}"
|
||||
data-hp-iso=${s?"raised":m}>
|
||||
${s?d`<defs><filter id="hp-led-iso-shadow" filterUnits="userSpaceOnUse"
|
||||
x="-100000" y="-100000" width="200000" height="200000">
|
||||
<feGaussianBlur stdDeviation="${s.shadow.sigma}"></feGaussianBlur></filter></defs>`:m}
|
||||
<style>
|
||||
.led-strip .led-focus { fill: none; stroke: transparent; }
|
||||
.led-strip:has(.led-hit:focus-visible) .led-focus { stroke: var(--primary-color, #03a9f4); }
|
||||
.led-strip .led-hit { fill: none; stroke: transparent; cursor: pointer; outline: none; }
|
||||
</style>
|
||||
${v(o,e=>e.strip.id,o=>{const{t:i,path:n}=T(o,e),a=y(n),r="unavailable"===o.state,l=r?"#9e9e9e":"on"===o.state&&!o.glow&&o.appearance?o.appearance.c:"#FFFFFF",c=2*k(i*e.perUnit)/(e.perUnit||1),p=o.device,u=t=>function(e,t,o){const s=e.currentTarget,i=s?.getScreenCTM?.();if(!i||o.views.length<2)return t;const n=i.inverse(),a=[n.a*e.clientX+n.c*e.clientY+n.e,n.b*e.clientX+n.d*e.clientY+n.f],r=o.views.map(e=>{const{t:t,path:s}=T(e,o);return{id:e.strip.id,points:s.points.map(e=>[e[0]*o.perUnit,e[1]*o.perUnit]),closed:s.closed,thicknessPx:t*o.perUnit}}),l=C([a[0]*o.perUnit,a[1]*o.perUnit],r);return o.views.find(e=>e.strip.id===l)||t}(t,o,e).device,h=s?`translate(0 ${-s.lift})`:m;return d`<g class="led-strip state-${o.state}" data-led-strip="${o.strip.id}"
|
||||
data-marker="${p.id}" data-state="${o.state}" data-closed="${n.closed?"true":"false"}">
|
||||
${s?d`<path class="led-iso-shadow" d="${a}" fill="none" stroke="black"
|
||||
stroke-opacity="${s.shadow.opacity}" stroke-width="${i}" stroke-linecap="round"
|
||||
stroke-linejoin="round" filter="url(#hp-led-iso-shadow)" pointer-events="none"
|
||||
transform="translate(${s.shadow.dx} ${s.shadow.dy-s.lift})"></path>
|
||||
${[1,.5].map(e=>d`<path class="led-iso-edge" d="${a}" fill="none" stroke="${s.edge}"
|
||||
stroke-width="${i}" stroke-linecap="round" stroke-linejoin="round" pointer-events="none"
|
||||
transform="translate(0 ${-s.lift+s.depth*e})"></path>`)}`:m}
|
||||
<g transform=${h}>
|
||||
<path class="led-outline" d="${a}" fill="none" stroke="${E}" stroke-width="${i}"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-dasharray=${r?`${2*i} ${1.5*i}`:m}></path>
|
||||
<path class="led-core" d="${a}" fill="none" stroke="${l}" stroke-width="${i/2}"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-dasharray=${r?`${2*i} ${1.5*i}`:m}></path>
|
||||
<path class="led-focus" d="${a}" stroke-width="${1.6*i}" stroke-linecap="round"
|
||||
stroke-linejoin="round" aria-hidden="true"></path>
|
||||
${t?d`<path class="led-hit" d="${a}" stroke-width="${c}"
|
||||
stroke-linecap="round" stroke-linejoin="round" pointer-events="stroke"
|
||||
role="button" tabindex="0" aria-label="${t.label(p)}"
|
||||
data-hp="device" data-id="${p.id}"
|
||||
@click=${e=>t.click(e,u(e))}
|
||||
@keydown=${e=>t.keydown(e,p)}
|
||||
@contextmenu=${e=>t.contextmenu(e,u(e))}
|
||||
@pointerdown=${e=>t.pointerdown(e,u(e))}
|
||||
@pointermove=${e=>t.pointermove(e,u(e))}
|
||||
@pointerup=${e=>t.pointerup(e,u(e))}
|
||||
@pointercancel=${e=>t.pointercancel(e,p)}
|
||||
@lostpointercapture=${e=>t.pointercancel(e,p)}
|
||||
@pointerover=${e=>t.pointerover(e,u(e))}
|
||||
@pointerleave=${()=>t.pointerleave()}
|
||||
@focus=${e=>t.focus(e,p)}
|
||||
@blur=${()=>t.blur(p)}></path>`:m}
|
||||
</g>
|
||||
</g>`})}
|
||||
</g>`}function B(e,t,o,s){return(t?e.find(e=>e.id===t):void 0)??(o?e.find(e=>s(o,e)):void 0)}function G(e){const t=new Map(e.devices.map(e=>[e.id,e])),o=g/e.cellCm*e.gridPitch,s=new Map(i({hass:e.hass,devices:e.devices,virtualLights:e.virtualLights,spaceId:e.space.id,defaultColor:e.defaultColor,paletteAlpha:e.paletteAlpha,defaultRadiusUnits:o,cellCm:e.cellCm,gridPitch:e.gridPitch,position:()=>({x:0,y:0})}).map(e=>[e.key,e])),r=[];for(const i of e.space.led_strips||[]){if(!1===i?.active||!i.marker||!w(i.points))continue;const a={...i,points:i.points.map(e=>[e[0]*n,e[1]*n])},l=t.get(a.marker);if(!l||l.space!==e.space.id)continue;if(l.hidden&&!e.showHidden)continue;const d=B(e.space.rooms,l.marker?.room_id,$(R(a.points)),e.inRoom);r.push(I({strip:a,device:l,candidate:s.get(`${e.space.id}|${l.id}`)??null,hass:e.hass,glow:!!d&&e.glowFor(d),defaultRadius:o,cellCm:e.cellCm,gridPitch:e.gridPitch}))}const l=_/e.cellCm*e.gridPitch;return{views:r,faces:A(e.scene,l),d:e.iconPct/100*a(e.space),scene:e.scene,polygons:e.polygons}}const H=new WeakMap;function N(e){H.delete(e),L?.releaseLedField(e)}function q(e){return{shapes:H.get(e)?.frame.views.length??0,...L?.ledFieldStats(e)??{visibility:0,sources:0,recomputes:0}}}function S(e,t,o){const s=t.rooms.flatMap(e=>{const t=r(e);return t?[{room:e,poly:t}]:[]}),i=s.length?e._lightBarriers(t,s.map(({room:e,poly:t})=>({r:e,poly:t}))):null,n=[t,e._renderDevices,e._renderPlanHass,o,i,e._mode,e._showAll],a=H.get(e);if(a&&a.key.every((e,t)=>e===n[t]))return a.frame;const d=e._config?.icon_size??2.5,c=G({space:t,devices:e._renderDevices,hass:e._renderPlanHass,virtualLights:e._virtualLights,defaultColor:e._fillColors.glow_light.c,paletteAlpha:e._fillColors.glow_light.a,cellCm:e._cellCm,gridPitch:e._gridPitch,iconPct:d>8?2.5:d,scene:i,polygons:s,glowFor:e=>l(o,e),inRoom:(t,o)=>e._pointInRoom(t,o),showHidden:"devices"===e._mode&&e._showAll});return!1!==e.isConnected&&H.set(e,{key:n,frame:c}),c}function O(e,t,o,s){const i=S(e,t,o),n=e._stageEl?.clientWidth,a="iso"===e._renderProjection&&"view"===e._mode,r=a?i.d*c:i.d,l="theme-dark"===p(e._renderPlanHass)?"dark":"light",m=!!e._isoLightFloors?.size,v=u(l,m),g=j({views:i.views,d:r,iso:a?{lift:f.lift*r,depth:f.depth*r,edge:h(E,l,m),shadow:{dx:v.dx*r,dy:v.dy*r,sigma:v.sigma*r,opacity:v.opacityWhite}}:null,faces:i.faces,perUnit:n&&s.w?n/s.w:1,handlers:"view"===e._mode?{click:(t,o)=>e._clickDevice(t,o),keydown:(t,o)=>e._keyDevice(t,o),contextmenu:(t,o)=>e._ctxDevice(t,o),pointerdown:(t,o)=>e._pointerDown(t,o),pointermove:(t,o)=>{const s=e._pointerMove(t,o);s&&e._showDeviceTip(t,s)},pointerup:(t,o)=>e._pointerUp(t,o),pointercancel:(t,o)=>e._pointerCancel(t,o),pointerover:(t,o)=>e._showDeviceTip(t,o),pointerleave:()=>e._clearPointerHover(),focus:(t,o)=>e._showDeviceFocusTip(t,o),blur:t=>e._hideDeviceFocusTip(t.id),label:e=>e.name}:null});return"plan"===e._mode||"decor"===e._mode?d`<g class="led-passive" opacity="0.45" pointer-events="none">${g}</g>`:g}function z(e,t,o){if(!1===e.isConnected)return d``;const s=S(e,t,o),i=s.scene&&(D(s.views)||L?.hasLedField(e))?L||x(t.id,()=>!1!==e.isConnected&&e.requestUpdate()):null;return i?i.renderLedField({views:s.views,scene:s.scene,polygons:s.polygons,faces:s.faces,spaceId:t.id,owner:e,requestUpdate:()=>e.requestUpdate(),isConnected:()=>!1!==e.isConnected}):d``}function W(e){const t=e.space.rooms.flatMap(e=>{const t=r(e);return t?[{room:e,poly:t}]:[]}),o=G({space:e.space,devices:e.devices,hass:e.hass,virtualLights:e.virtualLights,defaultColor:e.defaultColor,paletteAlpha:e.paletteAlpha,cellCm:e.cellCm,gridPitch:e.gridPitch,iconPct:e.iconPct,scene:e.scene,polygons:t,glowFor:e.glowFor,inRoom:e.inRoom,showHidden:!1}),s=e.live?o.views:o.views.map(e=>({...e,state:"off",appearance:null,glow:!1})),i=A({masonryGeometry:e.bodies.masonryGeometry,opaqueBodies:e.bodies.opaqueBodies},_/e.cellCm*e.gridPitch),n=e.scene&&e.live&&(D(s)||L?.hasLedField(e.owner))?x(e.space.id,e.ready):null;return d`${n?n.renderLedField({views:s,scene:e.scene,polygons:t,faces:i,spaceId:e.space.id,owner:e.owner,requestUpdate:e.ready,isConnected:()=>!0}):m}${j({views:s,d:o.d,faces:i,perUnit:e.perUnit,handlers:null})}`}export{M as LED_RUNTIME_FINGERPRINT,A as faceContext,x as ledField,G as ledFrame,S as ledFrameFor,q as ledStats,I as ledStripView,N as releaseLed,z as renderLedFieldFor,O as renderLedLayerFor,j as renderLedStripes,W as renderStaticLed,B as stripRoom};
|
||||
@@ -0,0 +1,47 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";import{eu as e,eC as t,c6 as o,eJ as s,eK as i,ad as n,ee as a,O as r,eL as l,az as d,ef as c,aL as p,eM as u,eN as h,eO as f,A as m,eP as v}from"./houseplan-card-BOdiRDFR.js";import{L as g,v as w,s as $,a as _,b as y,p as k,c as b,d as C,e as P}from"./led-strip-geometry-DaLifxqY.js";const M="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";let F,L=null,U=null;function x(e,t){return L||U||F===e||""===F||(U=(void 0===F?import("./led-strip-field-ObbckCkp.js"):import(new URL(`./led-strip-field-ObbckCkp.js?${Date.now()}`,import.meta.url).href)).then(e=>{e.LED_FIELD_FINGERPRINT===s?L=e:F=""},()=>{F=e}).finally(()=>{U=null})),L||U?.then(()=>L&&t()),L}const D=e=>e.some(e=>e.glow&&"on"===e.state),R=e=>e.map(e=>[e[0],e[1]]),E="#383838";function A(s,i){if(!s)return null;const n=Array.isArray(s.masonryGeometry)?s.masonryGeometry:[],a=[...e(s.masonryGeometry),...s.opaqueBodies],r=[];for(const e of a)for(let t=0;t<e.length;t++){const o=e[t],s=e[(t+1)%e.length];o&&s&&r.push({a:[o[0],o[1]],b:[s[0],s[1]]})}const l=25,d=new Map;r.forEach((e,t)=>{const o=Math.floor(Math.min(e.a[0],e.b[0])/l),s=Math.floor(Math.max(e.a[0],e.b[0])/l),i=Math.floor(Math.min(e.a[1],e.b[1])/l),n=Math.floor(Math.max(e.a[1],e.b[1])/l);for(let e=o;e<=s;e++)for(let o=i;o<=n;o++){const s=`${e},${o}`,i=d.get(s);i?i.push(t):d.set(s,[t])}});const c=e=>{let t=1/0,o=1/0,s=-1/0,i=-1/0;for(const n of e)t=Math.min(t,n[0]),o=Math.min(o,n[1]),s=Math.max(s,n[0]),i=Math.max(i,n[1]);return[t,o,s,i]},p=(e,t)=>e[0]>=t[0]&&e[0]<=t[2]&&e[1]>=t[1]&&e[1]<=t[3],u=n.filter(e=>e?.[0]?.length).map(e=>({box:c(e[0]),poly:e})),h=s.opaqueBodies.map(e=>({box:c(e),body:e}));return{faces:r,inside:e=>u.some(({box:o,poly:s})=>p(e,o)&&t([e[0],e[1]],[s]))||h.some(({box:t,body:s})=>p(e,t)&&o([e[0],e[1]],s)),epsilon:i,near:e=>{const t=new Set;for(let o=Math.floor(e[0]/l);o<=Math.floor(e[2]/l);o++)for(let s=Math.floor(e[1]/l);s<=Math.floor(e[3]/l);s++)for(const e of d.get(`${o},${s}`)||[])t.add(e);return[...t].map(e=>r[e])}}}function I(e){const t=e.candidate?.sourceEid||e.device.primary||"",o=t?e.hass?.states?.[t]?.state:void 0,s="unavailable"===o||"unknown"===o||!!t&&!!e.hass&&!e.hass.states?.[t]&&!e.device.virtual,i=Number(e.device.marker?.glow_radius_cm),n=Number.isFinite(i)&&i>0?i/e.cellCm*e.gridPitch:e.defaultRadius,a=s?null:e.candidate?.appearance??null;return{strip:e.strip,device:e.device,state:s?"unavailable":a?"on":"off",glow:e.glow,appearance:a,radius:n}}const T=new WeakMap;function j(e,t,o){const s=P*o;const i=T.get(e);if(i&&i.faces===t&&i.epsilon===t?.epsilon&&i.value.t===s&&i.points.length===e.points.length&&i.points.every((t,o)=>t[0]===e.points[o][0]&&t[1]===e.points[o][1]))return i.value;const n=R(e.points),a=y(n,t,s/2),r={t:s,path:a,d:k(a)};return T.set(e,{faces:t,epsilon:t?.epsilon,points:n,value:r}),r}function G(e){if(!e.views.length)return d``;const t=e.handlers,o=[...e.views].sort((e,t)=>e.strip.id<t.strip.id?1:e.strip.id>t.strip.id?-1:0),s=e.iso;return d`<g class="led-strips" data-hp-led-strips="${o.length}"
|
||||
data-hp-iso=${s?"raised":m}>
|
||||
${s?d`<defs><filter id="hp-led-iso-shadow" filterUnits="userSpaceOnUse"
|
||||
x="-100000" y="-100000" width="200000" height="200000">
|
||||
<feGaussianBlur stdDeviation="${s.shadow.sigma}"></feGaussianBlur></filter></defs>`:m}
|
||||
<style>
|
||||
.led-strip .led-focus { fill: none; stroke: transparent; }
|
||||
.led-strip:has(.led-hit:focus-visible) .led-focus { stroke: var(--primary-color, #03a9f4); }
|
||||
.led-strip .led-hit { fill: none; stroke: transparent; cursor: pointer; outline: none; }
|
||||
</style>
|
||||
${v(o,e=>e.strip.id,o=>{const{t:i,path:n,d:a}=j(o.strip,e.faces,e.d),r="unavailable"===o.state,l=r?"#9e9e9e":"on"===o.state&&!o.glow&&o.appearance?o.appearance.c:"#FFFFFF",c=2*b(i*e.perUnit)/(e.perUnit||1),p=o.device,u=t=>function(e,t,o){const s=e.currentTarget,i=s?.getScreenCTM?.();if(!i||o.views.length<2)return t;const n=i.inverse(),a=[n.a*e.clientX+n.c*e.clientY+n.e,n.b*e.clientX+n.d*e.clientY+n.f],r=o.views.map(e=>{const{t:t,path:s}=j(e.strip,o.faces,o.d);return{id:e.strip.id,points:s.points.map(e=>[e[0]*o.perUnit,e[1]*o.perUnit]),closed:s.closed,thicknessPx:t*o.perUnit}}),l=C([a[0]*o.perUnit,a[1]*o.perUnit],r);return o.views.find(e=>e.strip.id===l)||t}(t,o,e).device,h=s?`translate(0 ${-s.lift})`:m;return d`<g class="led-strip state-${o.state}" data-led-strip="${o.strip.id}"
|
||||
data-marker="${p.id}" data-state="${o.state}" data-closed="${n.closed?"true":"false"}">
|
||||
${s?d`<path class="led-iso-shadow" d="${a}" fill="none" stroke="black"
|
||||
stroke-opacity="${s.shadow.opacity}" stroke-width="${i}" stroke-linecap="round"
|
||||
stroke-linejoin="round" filter="url(#hp-led-iso-shadow)" pointer-events="none"
|
||||
transform="translate(${s.shadow.dx} ${s.shadow.dy-s.lift})"></path>
|
||||
${[1,.5].map(e=>d`<path class="led-iso-edge" d="${a}" fill="none" stroke="${s.edge}"
|
||||
stroke-width="${i}" stroke-linecap="round" stroke-linejoin="round" pointer-events="none"
|
||||
transform="translate(0 ${-s.lift+s.depth*e})"></path>`)}`:m}
|
||||
<g transform=${h}>
|
||||
<path class="led-outline" d="${a}" fill="none" stroke="${E}" stroke-width="${i}"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-dasharray=${r?`${2*i} ${1.5*i}`:m}></path>
|
||||
<path class="led-core" d="${a}" fill="none" stroke="${l}" stroke-width="${i/2}"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-dasharray=${r?`${2*i} ${1.5*i}`:m}></path>
|
||||
<path class="led-focus" d="${a}" stroke-width="${1.6*i}" stroke-linecap="round"
|
||||
stroke-linejoin="round" aria-hidden="true"></path>
|
||||
${t?d`<path class="led-hit" d="${a}" stroke-width="${c}"
|
||||
stroke-linecap="round" stroke-linejoin="round" pointer-events="stroke"
|
||||
role="button" tabindex="0" aria-label="${t.label(p)}"
|
||||
data-hp="device" data-id="${p.id}"
|
||||
@click=${e=>t.click(e,u(e))}
|
||||
@keydown=${e=>t.keydown(e,p)}
|
||||
@contextmenu=${e=>t.contextmenu(e,u(e))}
|
||||
@pointerdown=${e=>t.pointerdown(e,u(e))}
|
||||
@pointermove=${e=>t.pointermove(e,u(e))}
|
||||
@pointerup=${e=>t.pointerup(e,u(e))}
|
||||
@pointercancel=${e=>t.pointercancel(e,p)}
|
||||
@lostpointercapture=${e=>t.pointercancel(e,p)}
|
||||
@pointerover=${e=>t.pointerover(e,u(e))}
|
||||
@pointerleave=${()=>t.pointerleave()}
|
||||
@focus=${e=>t.focus(e,p)}
|
||||
@blur=${()=>t.blur(p)}></path>`:m}
|
||||
</g>
|
||||
</g>`})}
|
||||
</g>`}function H(e,t,o,s){return(t?e.find(e=>e.id===t):void 0)??(o?e.find(e=>s(o,e)):void 0)}function N(e){const t=new Map(e.devices.map(e=>[e.id,e])),o=g/e.cellCm*e.gridPitch,s=new Map(i({hass:e.hass,devices:e.devices,virtualLights:e.virtualLights,spaceId:e.space.id,defaultColor:e.defaultColor,paletteAlpha:e.paletteAlpha,defaultRadiusUnits:o,cellCm:e.cellCm,gridPitch:e.gridPitch,position:()=>({x:0,y:0})}).map(e=>[e.key,e])),r=[];for(const i of e.space.led_strips||[]){if(!1===i?.active||!i.marker||!w(i.points))continue;const a={...i,points:i.points.map(e=>[e[0]*n,e[1]*n])},l=t.get(a.marker);if(!l||l.space!==e.space.id)continue;if(l.hidden&&!e.showHidden)continue;const d=H(e.space.rooms,l.marker?.room_id,$(R(a.points)),e.inRoom);r.push(I({strip:a,device:l,candidate:s.get(`${e.space.id}|${l.id}`)??null,hass:e.hass,glow:!!d&&e.glowFor(d),defaultRadius:o,cellCm:e.cellCm,gridPitch:e.gridPitch}))}const l=_/e.cellCm*e.gridPitch;return{views:r,faces:A(e.scene,l),d:e.iconPct/100*a(e.space),scene:e.scene,polygons:e.polygons}}const q=new WeakMap;function B(e){for(const t of q.get(e)?.frame.views??[])T.delete(t.strip);q.delete(e),L?.releaseLedField(e)}function S(e){return{shapes:q.get(e)?.frame.views.length??0,...L?.ledFieldStats(e)??{visibility:0,sources:0,visibilityPaths:0,pathChars:0,recomputes:0}}}function O(e,t,o){const s=t.rooms.flatMap(e=>{const t=r(e);return t?[{room:e,poly:t}]:[]}),i=s.length?e._lightBarriers(t,s.map(({room:e,poly:t})=>({r:e,poly:t}))):null,n=[t,e._renderDevices,e._renderPlanHass,o,i,e._mode,e._showAll],a=q.get(e);if(a&&a.key.every((e,t)=>e===n[t]))return a.frame;for(const e of a?.frame.views??[])T.delete(e.strip);const d=e._config?.icon_size??2.5,c=N({space:t,devices:e._renderDevices,hass:e._renderPlanHass,virtualLights:e._virtualLights,defaultColor:e._fillColors.glow_light.c,paletteAlpha:e._fillColors.glow_light.a,cellCm:e._cellCm,gridPitch:e._gridPitch,iconPct:d>8?2.5:d,scene:i,polygons:s,glowFor:e=>l(o,e),inRoom:(t,o)=>e._pointInRoom(t,o),showHidden:"devices"===e._mode&&e._showAll});return!1!==e.isConnected&&q.set(e,{key:n,frame:c}),c}function W(e,t,o,s){const i=O(e,t,o),n=e._stageEl?.clientWidth,a="iso"===e._renderProjection&&"view"===e._mode,r=a?i.d*c:i.d,l="theme-dark"===p(e._renderPlanHass)?"dark":"light",m=!!e._isoLightFloors?.size,v=u(l,m),g=G({views:i.views,d:r,iso:a?{lift:f.lift*r,depth:f.depth*r,edge:h(E,l,m),shadow:{dx:v.dx*r,dy:v.dy*r,sigma:v.sigma*r,opacity:v.opacityWhite}}:null,faces:i.faces,perUnit:n&&s.w?n/s.w:1,handlers:"view"===e._mode?{click:(t,o)=>e._clickDevice(t,o),keydown:(t,o)=>e._keyDevice(t,o),contextmenu:(t,o)=>e._ctxDevice(t,o),pointerdown:(t,o)=>e._pointerDown(t,o),pointermove:(t,o)=>{const s=e._pointerMove(t,o);s&&e._showDeviceTip(t,s)},pointerup:(t,o)=>e._pointerUp(t,o),pointercancel:(t,o)=>e._pointerCancel(t,o),pointerover:(t,o)=>e._showDeviceTip(t,o),pointerleave:()=>e._clearPointerHover(),focus:(t,o)=>e._showDeviceFocusTip(t,o),blur:t=>e._hideDeviceFocusTip(t.id),label:e=>e.name}:null});return"plan"===e._mode||"decor"===e._mode?d`<g class="led-passive" opacity="0.45" pointer-events="none">${g}</g>`:g}function z(e,t,o){if(!1===e.isConnected)return d``;const s=O(e,t,o),i=s.scene&&(D(s.views)||L?.hasLedField(e))?L||x(t.id,()=>!1!==e.isConnected&&e.requestUpdate()):null;return i?i.renderLedField({views:s.views,scene:s.scene,polygons:s.polygons,faces:s.faces,spaceId:t.id,owner:e,requestUpdate:()=>e.requestUpdate(),isConnected:()=>!1!==e.isConnected}):d``}function Y(e){const t=e.space.rooms.flatMap(e=>{const t=r(e);return t?[{room:e,poly:t}]:[]}),o=N({space:e.space,devices:e.devices,hass:e.hass,virtualLights:e.virtualLights,defaultColor:e.defaultColor,paletteAlpha:e.paletteAlpha,cellCm:e.cellCm,gridPitch:e.gridPitch,iconPct:e.iconPct,scene:e.scene,polygons:t,glowFor:e.glowFor,inRoom:e.inRoom,showHidden:!1}),s=e.live?o.views:o.views.map(e=>({...e,state:"off",appearance:null,glow:!1})),i=A({masonryGeometry:e.bodies.masonryGeometry,opaqueBodies:e.bodies.opaqueBodies},_/e.cellCm*e.gridPitch),n=e.scene&&e.live&&(D(s)||L?.hasLedField(e.owner))?x(e.space.id,e.ready):null;return d`${n?n.renderLedField({views:s,scene:e.scene,polygons:t,faces:i,spaceId:e.space.id,owner:e.owner,requestUpdate:e.ready,isConnected:()=>!0}):m}${G({views:s,d:o.d,faces:i,perUnit:e.perUnit,handlers:null})}`}export{M as LED_RUNTIME_FINGERPRINT,A as faceContext,x as ledField,N as ledFrame,O as ledFrameFor,S as ledStats,I as ledStripView,j as ledStripePath,B as releaseLed,z as renderLedFieldFor,W as renderLedLayerFor,G as renderLedStripes,Y as renderStaticLed,H as stripRoom};
|
||||
@@ -1 +0,0 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29";import{eQ as e,eR as n,eF as a}from"./houseplan-card-BWxNZe40.js";function o(a,o){return new e([{code:"en",dictionary:a},{code:"ru",loadDictionary:o.ru},{code:"de",loadDictionary:o.de},{code:"fr",loadDictionary:o.fr}],"24fe93e707e3b9550d14fdbae3465815e509a7cc866526ece24532eebb9dec29",console.warn,n)}function r(e){return function(e,n){return{state:a=>{const o=e.state(a);return"pending"!==o&&n.some(e=>"pending"===e.state(a))?"pending":o},dictionary:n=>e.dictionary(n),ensure:a=>Promise.all([e,...n].map(e=>e.ensure(a))).then(()=>{})}}(a,e)}export{o as n,r as s};
|
||||
@@ -0,0 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172";import{eR as e,eS as a,eG as n}from"./houseplan-card-BOdiRDFR.js";function o(n,o){return new e([{code:"en",dictionary:n},{code:"ru",loadDictionary:o.ru},{code:"de",loadDictionary:o.de},{code:"fr",loadDictionary:o.fr}],"ab07da35681c15ab6136c323500b69766580ea4a788e399ea88f9e7497549172",console.warn,a)}function r(e){return function(e,a){return{state:n=>{const o=e.state(n);return"pending"!==o&&a.some(e=>"pending"===e.state(n))?"pending":o},dictionary:a=>e.dictionary(a),ensure:n=>Promise.all([e,...a].map(e=>e.ensure(n))).then(()=>{})}}(n,e)}export{o as n,r as s};
|
||||