Release v1.61.0-beta.7 candidate

This commit is contained in:
Matysh
2026-08-11 02:49:42 +03:00
parent d2bc908280
commit c237baaffd
50 changed files with 4432 additions and 2328 deletions
+32 -5
View File
@@ -552,6 +552,29 @@ out must not queue the same url again, a failure backs off rather than retrying
on the next frame, and an in-flight entry expires after `SIGN_INFLIGHT_MS` so a
promise that never settles cannot block retries forever.
**Visual continuity is a frame contract, not a loading screen** (#73).
`src/visual-continuity.ts` owns one tokenised state machine shared by the full
and static cards. A complete frame remains mounted during resume, reconnect,
structural revalidation and positive-size changes; `0×0` observations never
change the viewport. Config and layout carry independent revision plus
content-fingerprint identities, so revision-only echoes preserve authoritative
objects and geometry caches while changed content cannot hide behind an equal
revision. A candidate becomes complete only after Lit settles, required signed
assets are loaded, and two animation-frame opportunities pass for the current
token. A bounded trace and the production `data-continuity-state`,
`data-continuity-token`, `data-frame-fingerprint` and conditional
`data-recovery-reason` attributes expose this contract without entity ids or
URLs.
The signed-asset runtime is authority-scoped (`hass.connection`), bounded and
shared across placements. A warm remount can therefore use an already loaded
backdrop synchronously. Refresh is stale-while-decode: the painted signed URL
stays authoritative until its replacement has loaded and decoded off-DOM.
Only when no complete/stale frame can be retained may the controller show the
localized opaque recovery overlay, after a 150 ms delay. The overlay never
steals initial focus; while visible it alone is interactive and the scene is
`inert`.
**Room climate is one pass per hass snapshot** (review R2-3). `areaClimateMap()`
classifies the whole registry once and returns `Map<area, {temp, hum}>`; the
card memoizes it on `hass` identity, which Home Assistant replaces on every
@@ -582,9 +605,12 @@ Shared, framework-light modules keep the two views from diverging:
use the shared `ResolvedDevicePresentation` and `renderDeviceFace`; optional card settings
can disable ordinary live dressing, temperature or signal without creating another
semantic implementation.
- `src/config-store.ts` — module-level `{config, rev, layout}` cache shared by all embedded
- `src/config-store.ts` — module-level `{config, rev, configFingerprint, layout,
layoutRev, layoutFingerprint}` cache shared by all embedded
cards (dedupes `houseplan/config/get`), seeded synchronously from the full card's
localStorage snapshot (`houseplan_card_cfg_v1`) and invalidated on `houseplan_config_updated`.
localStorage snapshot (`houseplan_card_cfg_v1`) and invalidated on
`houseplan_config_updated` or `houseplan_layout_updated` without first
clearing the visible static snapshot.
**Static contract:** the schematic layer (`.hp-static-stage`) is `pointer-events:none`; the
footer button lives outside it and stays clickable.
@@ -652,8 +678,8 @@ hash falls back to the default.
`_lightBarriers` collects everything opaque: the wall bodies exactly as the
plan draws them (`wallBodiesGeometry`, real thickness, mitred junctions),
every independent body (partition, column, draft), and the bare outline of
any edge that carries no thickness. It cuts out the exceptions: doorways,
gates and arches — cut through the masonry, so an opening is a real gap
any edge that carries no thickness. It cuts out the exceptions: doorways
and gates — cut through the masonry, so an opening is a real gap
between two jamb faces and a beam is narrowed by the returns of a thick wall
— plus virtual (open) boundaries, which are not walls at all. Windows stay
solid, so an indoor lamp never washes the street; the light's masonry is cut
@@ -680,7 +706,8 @@ hash falls back to the default.
room ignored). There is no spill layer, no sector, no tunnel rectangle, no
open-zone graph and no shadow mask left in the light path.
Barriers are cached by a fingerprint of their own geometry, never by
Barriers are cached per space by a fingerprint of their complete geometry
(every body point, both wall endpoints and scale inputs), never by
`_cfgEpoch`: the epoch lags behind geometry edited in place, and a stale
barrier set is invisible — the plan simply keeps lighting through a wall that
now exists. The same fingerprint keys the per-source region cache. A cached per-
+28 -2
View File
@@ -2,6 +2,31 @@
## Unreleased
## v1.61.0-beta.7 — 2026-08-11
- Fixed rare but severe Glow geometry failures: a clipping exception can no
longer paint outside the house, a grid-snapped source on a wall stays dark,
and the angular seam no longer cuts a wedge from an otherwise valid pool.
Light barriers now invalidate for every changed body point, wall endpoint,
scale or space.
- Delete/Cancel/Save in opening and space dialogs now use the same responsive
grouped footer as physical-object properties, keeping localized actions
inside narrow dialogs.
- Returning to a dashboard no longer hides or rebuilds an already complete
plan. Full and compact cards keep the last coherent frame through tab sleep,
reconnect, remount and transient resize; identical config/layout echoes are
adopted without geometry churn, and protected backdrops share a bounded
loaded cache. A localized recovery layer is reserved for the rare case where
no complete frame can be retained. ([#73](https://github.com/Matysh/houseplan-card/issues/73))
- Restored the room-hover contract without bringing back the Glow flash: a
neutral clean-floor wash now darkens the resolved room fill while its late
outline remains above walls; Glow, sun, openings and devices stay visually
independent. ([#79](https://github.com/Matysh/houseplan-card/issues/79))
- Working device markers keep their yellow resting plate with the ordinary
neutral elevation shadow, use the same hover as other interactive markers,
and no longer add an unrelated yellow outer glow.
([#80](https://github.com/Matysh/houseplan-card/issues/80))
## v1.61.0-beta.6 — 2026-08-11
- Fixed shared dialog layout for localized content: multi-line titles now grow
@@ -22,8 +47,9 @@
for light — there is no floor behind a front door to light, so it no longer
glows halfway. Pools also fade over their whole radius instead of holding a
flat plateau to 70%, so distant floor is faint because it is distant. As a
side effect the light layer became a single clipped circle per source and got
about four times cheaper to paint on a large plan.
side effect the light layer became a single clipped circle per source. Cold
geometry calculation on a complex plan became dramatically faster; the warm
render path remains broadly comparable with beta.5 and is tracked separately.
([#71](https://github.com/Matysh/houseplan-card/issues/71))
- Fixed a one-frame Glow brightness flash when hovering any room. Room hover
now uses a plain SVG wash and double outline without compositor filters.
+29 -2
View File
@@ -8,6 +8,32 @@
## Unreleased
## v1.61.0-beta.7 — 2026-08-11
- Исправлены редкие, но серьёзные сбои геометрии Glow: ошибка clipping больше
не может залить светом пространство вне дома, источник на привязанной к
сетке стене остаётся тёмным, а угловой шов больше не вырезает клин из
корректного пятна. Кэш барьеров теперь инвалидируется при изменении любой
точки тела, конца стены, масштаба или пространства.
- «Удалить»/«Отмена»/«Сохранить» в диалогах проёма и пространства переведены
на тот же адаптивный групповой футер, что свойства физических объектов:
локализованные действия не выходят за границы узкого диалога.
- Возврат на дашборд больше не скрывает и не пересобирает уже готовый план.
Полная и компактная карточки удерживают последний согласованный кадр при сне
вкладки, reconnect, remount и переходном resize; одинаковые ответы
config/layout принимаются без пересборки геометрии, а защищённые подложки
используют общий ограниченный cache загруженных ресурсов. Локализованный
слой восстановления оставлен только для редкого случая, когда удержать
полноценный кадр невозможно. ([#73](https://github.com/Matysh/houseplan-card/issues/73))
- Восстановлен контракт hover комнаты без возврата вспышки Glow: нейтральный
слой по чистому полу затемняет фактическую заливку комнаты, а поздний контур
остаётся над стенами; Glow, солнце, проёмы и устройства визуально не
затрагиваются. ([#79](https://github.com/Matysh/houseplan-card/issues/79))
- Работающие устройства сохраняют жёлтую подложку в покое с обычной нейтральной
тенью, получают тот же hover, что остальные интерактивные маркеры, и больше
не создают лишнее жёлтое свечение вокруг подложки.
([#80](https://github.com/Matysh/houseplan-card/issues/80))
## v1.61.0-beta.6 — 2026-08-11
- Исправлена общая компоновка диалогов для длинных локализованных строк:
@@ -28,8 +54,9 @@
света глухие — за входной дверью нет пола, освещать нечего, и она больше не
светится наполовину. Пул вдобавок затухает по всему радиусу, а не держит
плато до 70%, поэтому дальний пол слабее просто потому, что он дальше. Побочно
слой света стал одной обрезанной окружностью на источник и рисуется примерно
вчетверо быстрее на большом плане.
слой света стал одной обрезанной окружностью на источник. Холодный расчёт
геометрии сложного плана стал радикально быстрее; тёплый путь в целом остался
сопоставим с beta.5 и контролируется отдельно.
([#71](https://github.com/Matysh/houseplan-card/issues/71))
- Исправлена однокадровая вспышка яркости Glow при наведении на любую комнату.
Hover теперь использует обычную SVG-заливку и двойной контур без фильтров
+13 -7
View File
@@ -32,7 +32,7 @@ plan geometry and shares it between every lamp in the space.
**Transparent**
- doorways, gates and arches — cut through the masonry, so an opening is a real
- doorways and gates — cut through the masonry, so an opening is a real
gap between two jamb faces and a thick wall's returns narrow the beam;
- virtual (open) boundaries, which are not walls to begin with.
@@ -103,9 +103,10 @@ region cache (`_glowClipCache`).
- `test/light-visibility.test.mjs` — the sweep itself: a wall stops light, a
doorway lets a beam through and only through, a column's shadow has the
angular width its size dictates, an occluder out of range changes nothing, an
edge through the lamp is ignored, and a corner made by two crossing barriers
is lit right up to the corner.
angular width its size dictates, an occluder out of range changes nothing, a
source on an opaque edge is rejected, the ±π seam cannot cut a wedge from
the fan, and a corner made by two crossing barriers is lit right up to the
corner.
- `demo/smoke_glow.mjs` — pixels on a rendered plan: the aperture itself is lit,
the floor behind a door is lit, the visible beam is no wider than twice the
opening, there is no light behind a column while the floor beside it is lit,
@@ -121,6 +122,11 @@ region cache (`_glowClipCache`).
## Performance
One clipped circle per source and one blur for the layer: 60 sources in the
`large-light-blend-v1` profile render in ~75 ms against ~259 ms for the layered
model on the same runner (CPU throttled 4×).
The large cold geometry recalculation (20 rooms, 20 partitions, 14 columns,
61 sources) dropped from about 23.5 s in beta.5 to about 0.33 s in beta.6 on
the review runner — roughly 70×. The official warm `large-light-blend-v1`
path is generally level with beta.5; the 30-source sample was temporarily
slower and remains a performance watch item. During pinch/pan and the bounded
500 ms source fade, the whole-layer blur is bypassed and its parameters stay
frozen; the final screen-space feather is restored once after the transition
instead of rebuilding and evaluating the filter for every animation frame.
+7 -5
View File
@@ -1,14 +1,16 @@
<!-- release: v1.61.0-beta.6 -->
<!-- release: v1.61.0-beta.7 -->
## Основное
- Свет на плане переведён на единую геометрическую модель: лампа освещает только видимый ей пол, реальные стены и колонны отбрасывают тени, а свет через дверные проёмы остаётся непрерывным и ограничивается откосами ([#71](https://github.com/Matysh/houseplan-card/issues/71)).
- План сохраняет последний цельный кадр при возврате на вкладку, reconnect и remount, поэтому больше не должен мигать или пересобираться без необходимости ([#73](https://github.com/Matysh/houseplan-card/issues/73)).
- Исправлены редкие сбои геометрии Glow, способные засветить область вне дома или исказить тени у стен.
- Мелкие исправления и улучшения.
## Highlights
- Plan lighting now uses one geometric visibility model: a lamp lights only the floor it can see, real walls and columns cast shadows, and doorway light remains continuous and constrained by the jambs ([#71](https://github.com/Matysh/houseplan-card/issues/71)).
- The plan keeps its last coherent frame through tab resume, reconnect and remount, avoiding unnecessary flashes and rebuilds ([#73](https://github.com/Matysh/houseplan-card/issues/73)).
- Fixed rare Glow geometry failures that could light outside the house or distort wall shadows.
- Small fixes and improvements.
[Полный список изменений на русском](https://github.com/Matysh/houseplan-card/blob/v1.61.0-beta.6/docs/CHANGELOG.ru.md)
· [Full changelog in English](https://github.com/Matysh/houseplan-card/blob/v1.61.0-beta.6/docs/CHANGELOG.md)
[Полный список изменений на русском](https://github.com/Matysh/houseplan-card/blob/v1.61.0-beta.7/docs/CHANGELOG.ru.md)
· [Full changelog in English](https://github.com/Matysh/houseplan-card/blob/v1.61.0-beta.7/docs/CHANGELOG.md)
+2 -3
View File
@@ -22,11 +22,10 @@ metadata). Only an explicit owner-approved emergency hotfix may skip this gate.
| Item | State |
|---|---|
| Version | **v1.61.0-beta.6** everywhere (manifest, const.py, package.json, CARD_VERSION) — prerelease candidate with visibility-based light transport, stable room hover and shared responsive dialog chrome |
| Current local cycle | v1.61.0-beta.6 rebuilds light transport on ONE rule accepted by the owner after manual testing (#71): **a lamp lights the floor it can see**. `_lightBarriers` collects the drawn wall bodies with their real thickness, independent bodies and the outlines of edges without thickness, cuts out passages (a window and a door with no floor behind it stay solid) and virtual boundaries, then splits every barrier where barriers cross so the sweep never skips a junction corner; `visibilityPolygon` (`src/light-visibility.ts`) returns the region and one clipped circle paints it. The separate spill path, the doorway sector, the aperture tunnel, the open-zone graph and the shadow mask are gone, and with them the class of bugs where two layers disagreed. Pools now fade over the whole radius (`GLOW_FALLOFF`) instead of holding a plateau to 70%, and the layer gets one screen-pixel-sized blur as a penumbra. Smoke asserts pixels: aperture lit, floor behind a door lit, nothing behind a column (≤3) while the floor beside it is lit (≥8), a lit→unlit border at most 4 px wide, one painted child per spot, an outside door that changes the lit region not at all. Room hover was also moved off compositor filters to remove a one-frame Glow brightness flash (#72), and shared dialogs now grow for multiline titles and wrap destructive/commit action groups safely (#77/#78). 60 sources render ~3.5× faster than the layered model. The model, its
exceptions and its gotchas are documented in `docs/LIGHT.md`. |
| Current local cycle | After v1.61.0-beta.6, #73 visual continuity is implemented locally alongside the deterministic #79/#80 fixes and the beta.6 review corrections. Full and compact cards share one tokenised controller, keep the last coherent frame through resume/reconnect/remount/resize, adopt config/layout by revision plus content fingerprint, share loaded signed assets and expose a delayed localized recovery fallback only when no complete frame exists. Deterministic unit/frame-sequence coverage is added but not run yet under the owner's local-edit rule. Room hover remains split into a neutral clean-floor wash below Glow/sun/walls and a late outline above walls; the device CSS cascade restores common hover and neutral elevation shadow. Glow now fails dark on clipping errors and sources placed on barriers, normalizes the angular seam, fingerprints complete barrier geometry per space, freezes feather parameters through viewport gestures and has a semantic golden assertion that the receiving room is actually lit. Opening/space footers use the shared responsive action groups. `docs/LIGHT.md` remains canonical. |
| Workflow | Owner's rule since 2026-08-07: ordinary fixes/features are made **locally, without tests and without commits**. A requested pre-release gets a production build plus the smallest targeted unit/smoke set covering the changed surfaces, one tested `dev` commit/tag and a GitHub Release with `prerelease=true`; `main` stays untouched. The complete local frontend/backend/smoke gate runs only before a stable release, after which `main` is fast-forwarded to the exact tested `dev` SHA and the GitHub Release uses `prerelease=false`. Release bodies are short and bilingual (Russian first): only significant user changes get individual bullets, while minor/code-only work is grouped as `Мелкие исправления и улучшения` / `Small fixes and improvements`; every body ends with separate links to the Russian and English changelogs. Detailed RU/EN changelog bullets may link the corresponding closed GitHub Issues; open or partially delivered issues are never presented as shipped. Telegram announcements are sent only for stable releases; beta and RC publication is silent. `docs/RELEASE-NOTES.md` is the current canonical body instance; `npm run release:prerelease -- <tag> --issues=… --yes` is the primary local publication path and the manual `Publish prerelease` workflow is its GitHub-only equivalent once present on `main`. Nothing is copied to the home instance by hand |
| GitHub | https://github.com/Matysh/houseplan-card — [Issues](https://github.com/Matysh/houseplan-card/issues) are the canonical task records and the linked [Project v2](https://github.com/users/Matysh/projects/1) is the canonical priority/status view; both must stay current. `main` carries stable releases; pre-release tags may point directly at `dev`. Work lands on `dev` and is merged into `main` for a stable release, so `dev` is normally equal to or ahead of `main`, never behind. Push via SSH key `ha_jb` (remote git@github.com:…); API releases via the fine-grained PAT in `~/.git-credentials` (Contents R/W, issued 2026-07-23) |
| CI | Prerelease publication requires a green exact-SHA Validate: frontend/backend, smoke, golden, HACS/Hassfest and a short absolute-ceiling performance smoke. Obsolete same-ref Validate runs are cancelled. Full seven-sample base/candidate performance moved to `performance.yml` (`main` push, weekly, manual); stable release assets fail closed unless both Validate and Full Performance are green for the exact tagged SHA. |
| CI | Prerelease publication requires a green exact-SHA Validate: frontend/backend, smoke (including the #73 rAF frame sampler), golden, HACS/Hassfest and a short absolute-ceiling performance smoke. Obsolete same-ref Validate runs are cancelled. Full seven-sample base/candidate performance moved to `performance.yml` (`main` push, weekly, manual); stable release assets fail closed unless Validate and Full Performance are green for the exact tagged SHA and the stable-only CDP compositor screencast finds no empty/black presented frame. |
| HACS | Custom repository works. **Inclusion PR: hacs/default#9004** — open, valid, labeled, mergeable clean, never drafted. Queue: 1212 open, 835 older than ours. Merge rate COLLAPSED: 75 in July but almost all in the first decade, 0 in the last week (checked 2026-07-29) — maintainers process in rare bursts; ETA unknowable, months at best. Nothing actionable on our side |
| Home instance | ha.jbstudio.pro (SSH port **22222**, key `ha_jb`; HA config root is `/mnt/data/supervisor/homeassistant` — `/config` does NOT exist in this SSH environment), last direct copy was **v1.57.0**; from v1.58.0 on it updates itself through HACS by tag (no scp) |
| Localization | UI en/ru (src/i18n/*.json), everything user-visible localized incl. kiosk popover |
+38 -20
View File
@@ -783,12 +783,12 @@ separately promised workflows:
does not immediately arm a new opening [manual]
- [ ] Open boundaries (v1.37.0): virtual stretches exist only between two
rooms on their shared wall and render dashed; glow light floods the whole
connected open zone transitively, door sectors work from the zone's
outer walls; merge/split keep links by room id [auto: smoke_openwall]
connected floor transitively wherever the source has line of sight;
merge/split keep links by room id [auto: smoke_openwall]
- [ ] Light transport (#71, model: `docs/LIGHT.md`): a source paints exactly
ONE region — the floor it can see. Opaque: the drawn wall bodies with their real thickness, plus
independent bodies, plus untouched outlines of edges without thickness.
Transparent: doorways, gates, arches and virtual boundaries — but only
Transparent: doorways, gates and virtual boundaries — but only
where BOTH sides are floor; a window and an outside door are opaque, and
an outside door must not change the lit region at all (no half-lit
tunnel). Light must never appear inside a wall body: at an opening it
@@ -801,8 +801,9 @@ separately promised workflows:
beam stays no wider than twice the opening. Behind a column there is no
light at all (delta ≤ 3) while the floor beside it is lit (≥ 8), and the
lit→unlit border is at most 4 stage pixels wide — a geometric edge, not a
Gaussian. A spot contains exactly one painted child; no `mask`, `filter`
or `feGaussianBlur` may appear anywhere in the light layer, and the pool
Gaussian. A spot contains exactly one painted child; no `mask` or
per-source filter may appear in the light layer, exactly one
`feGaussianBlur` feathers the complete layer, and the pool
clip may never leave the rooms. The pool gradient falls off monotonically
over the whole radius (no plateau; at 70% at most 75% of the centre
alpha). A lamp lights a room across a virtual boundary, and across two of
@@ -812,7 +813,7 @@ separately promised workflows:
golden: lighting-opaque-glow-two-doorways-dark]
- [ ] Per-source glow radius (v1.36.2): the device dialog has a "Glow radius"
field (HA units; empty = general-settings default shown as placeholder);
an override changes that source's pool and door sectors only [auto: smoke_glow]
an override changes that source's visibility-clipped pool only [auto: smoke_glow]
- [ ] Hidden-light primary (v1.36.1): a lamp whose light entity is HIDDEN in
the registry (folded into a light group) still toggles/reflects the lamp,
not its do-not-disturb switch or identify button; visible entities of the
@@ -1105,19 +1106,28 @@ that it automatically runs blocking verification. Review and accept the
`golden-images` CI artifact rather than treating a developer OS raster as the
canonical set. See `demo/golden/README.md`.
### Issue #73 preparation gate (2026-08-10)
### Issue #73 baseline and implementation (2026-08-11)
Visual-continuity implementation starts only after renderer dependencies #67,
#71 and #72 are published, closed and verified by the exact-SHA prerelease
gate. The v1.61.0-beta.6 candidate contains the accepted visibility-based light
model and filter-free room hover, together with matrix-v7 baselines including
`lighting-opaque-glow-two-doorways-dark`. The owner approved the complete
visual result on 2026-08-11; the canonical behaviour is `docs/LIGHT.md`.
The published v1.61.0-beta.6 exact SHA is the renderer baseline for #73: it
contains the accepted visibility-based light model and the matrix-v7 published
baseline; local review fixes prepare matrix-v8 with a semantic warm-pixel gate,
including `lighting-opaque-glow-two-doorways-dark`; canonical light behaviour
is `docs/LIGHT.md`. The owner explicitly started #73 on 2026-08-11.
Do not start #73 from a merely local result. First record the successful
v1.61.0-beta.6 Validate SHA and the blocking Linux golden verification in the
issue; the published exact SHA, rather than an ad-hoc local capture, is the
continuity baseline.
`test/visual-continuity.test.mjs` covers tokens, quick/long return, delayed
overlay timing, paint barriers and fingerprints. `demo/smoke_visual_continuity.mjs`
samples presented frames and rejects hidden/empty plans, viewport rollback,
overlay over a stale frame, missing production attributes and unbounded or
sensitive trace data. It supplements rather than replaces warm-remount,
websocket-resilience and golden verification.
`npm run continuity:screencast` is the separate compositor-level gate required
before a stable release. It captures acknowledged PNG frames through CDP
`Page.startScreencast`, crops the real plan stage, rejects uniform/black frame
regressions and writes the exact frames plus metrics to
`artifacts/continuity-screencast`. Prereleases keep the faster mandatory rAF
smoke; the stable release workflow installs Chromium and runs the screencast
before attaching the public card asset.
## Large-house performance gate
@@ -1727,9 +1737,17 @@ require hands on real hardware — they remain for the human pass.
- [ ] **A quick return does not flash**: leave the browser tab or minimise the
window for a few seconds and return. The existing viewport, day/night
background and room hover remain painted continuously; neither
`skysnap` nor the long-sleep `hpresume` veil is armed
[auto: smoke_sun_live_bg]
background and room hover remain painted continuously; the continuity
token stays unchanged and no recovery overlay is created
[auto: smoke_sun_live_bg, smoke_visual_continuity]
- [ ] **A long return holds the complete frame**: every sampled frame keeps a
visible `.zoomwrap`, non-empty rooms and the same viewport while
config/layout reconnect data is revalidated. A stale frame never gets a
recovery overlay [auto: smoke_visual_continuity, smoke_ws_resilience]
- [ ] **Protected backdrops survive remount and refresh**: a loaded authority-
scoped signed URL is available synchronously to another placement; an
aging URL remains painted until its replacement decodes
[unit: signing.test; auto: smoke_plan_signed, smoke_space_card_bg]
- [ ] **The view does not twitch**: pan the plan into a corner and zoom in
(say 2.5×), leave the tab for long enough that HA reconnects, come back —
the plan is in exactly the same place at exactly the same scale. Not
+2 -2
View File
@@ -687,8 +687,8 @@ Glow и заливка «Свет» продолжают работать по
даже при зафиксированной яркости.
При включённом Glow лампа освещает тот пол, который видит. Стены не пропускают
свет своей настоящей толщиной, как и колонны с отдельными перегородками; двери,
ворота и арки — проходы, поэтому через них свет идёт дальше, а откосы толстой
свет своей настоящей толщиной, как и колонны с отдельными перегородками; двери
и ворота — проходы, поэтому через них свет идёт дальше, а откосы толстой
стены сужают луч. Виртуальные границы не стены вовсе: комнаты за ними
освещаются свободно. Окно и наружная дверь для света глухие — за наружной
дверью нет пола, освещать нечего. Отсюда же берутся тени: тень колонны или угла
+14 -9
View File
@@ -144,15 +144,20 @@ Lovelace **пересоздаёт элемент карточки** — выбр
`ResizeObserver` способен выдать нулевой или промежуточный размер сцены. Такой
размер больше никогда не меняет `_view`.
Если вкладка была скрыта не меньше 15 секунд, обычный режим Просмотра включает
короткий `_resumeSettling`: содержимое сцены остаётся за её уже правильным
фоном, пока размер не станет ненулевым и спокойным. После этого сохранённые
центр и zoom пересчитываются по окончательной геометрии и открываются одним
кадром. Памятка `warmBoot` во время промежуточного кадра не обновляется.
Быстрое переключение вкладок, редакторы и киоск в этот путь не входят. При
быстром возврате сохраняются также уже нарисованные day/night-фон и hover:
безусловный сброс этих полей создавал отдельный `skysnap`-кадр и визуальное
мигание даже после двух секунд в фоне.
Порог 15 секунд теперь только отмечает возможный browser freeze. Быстрый
возврат — строгий no-op: не меняются token, DOM, viewport, day/night-фон и
hover. После долгого сна общий `VisualContinuityController` удерживает на
экране последний полноценный кадр, параллельно перепроверяет config/layout и
принимает только подтверждённый положительный размер сцены. Класса
`hpresume` и скрытия `.zoomwrap` больше нет ни в Просмотре, ни в редакторах,
ни в киоске.
Памятка `warmBoot` не стала вторым lifecycle-механизмом: она по-прежнему
принадлежит placement и теперь дополнительно переносит fingerprint последнего
полного кадра и компактный roster устройств. Подписи и readiness загруженных
подложек принадлежат общему cache соединения, поэтому новый экземпляр может
нарисовать защищённую подложку в первом кадре. Промежуточный `0×0` размер,
неполный candidate и новая подписанная ссылка до decode в памятку не попадают.
Толстые стены дополнительно имеют критические `fill`, `stroke`, opacity и
`fill-rule` как SVG presentation attributes. Поэтому даже промежуточный кадр
+24 -18
View File
@@ -12,7 +12,7 @@
- тёплый и холодный источник дают смешанный цвет в пересечении;
- два одинаковых dim-источника дают более светлое пересечение;
- room/data fill, Glow base, tunnel sectors, backdrop и sun не участвуют в
- room/data fill, Glow base, opening tunnel fills, backdrop и sun не участвуют в
аддитивной группе.
Модель источников, `resolvedLightSources(room)`, радиусы, clipping светом стен и
@@ -36,9 +36,7 @@
<g class="glow-pools-frame" pointer-events="none">
<g class="glow-pools blend-screen" data-blend="screen">
<g class="glow-spot">
<g class="glow-shadow-target" mask="url(#per-source-shadow)">
<circle class="glow-pool" clip-path="url(#per-source-clip)">…</circle>
</g>
<circle class="glow-pool" clip-path="url(#per-source-visibility)">…</circle>
</g>
</g>
</g>
@@ -56,16 +54,16 @@ screen/normal состояний между источниками не быва
.glow-pools.blend-normal > .glow-spot { mix-blend-mode: normal; }
```
После #71 screen-композиция принадлежит внешнему `.glow-spot`, а luminance
mask — обычной внутренней `.glow-shadow-target`. Маску запрещено возвращать на
тот же SVG primitive, который несёт `mix-blend-mode`: Chromium может принять
атрибут в DOM, но проигнорировать его при compositor promotion.
После #71 screen-композиция принадлежит `.glow-spot`, а единственная форма
источника — один circle, обрезанный полигоном видимости, пересечённым с
полом. Per-source luminance mask и отдельный shadow layer удалены: тень — это
пол, не попавший в visibility polygon.
`glow_base`, resolved room/data fill, opening tunnel sectors и sun rays
`glow_base`, resolved room/data fill, opening tunnel fills и sun rays
остаются sibling layers вне `glow-pools-frame`. Их порядок относительно друг
друга сохраняется по #55.
Каждый circle сохраняет собственный `clip-path` после re-parenting. Clip ids
Каждый circle сохраняет собственный visibility `clip-path` после re-parenting. Clip ids
остаются уникальными и стабильными в пределах SVG; объединять per-source clips
в один общий clip запрещено, иначе свет начнёт проходить сквозь чужие стены.
@@ -74,8 +72,11 @@ mask — обычной внутренней `.glow-shadow-target`. Маску
Альфа отдельного pool задаётся только `stop-opacity` его radial gradient:
```text
Aplateau = clamp(fill_colors.glow_light.a * 0.7 * (0.4 + 0.6 * sourceBrightness^(1/2.2)), 0, 1)
A(r <= 70%) = Aplateau
Acenter = clamp(fill_colors.glow_light.a * 0.7 * (0.4 + 0.6 * sourceBrightness^(1/2.2)), 0, 1)
A(r = 0%) = Acenter
A(r = 45%) = 0.88 * Acenter
A(r = 70%) = 0.62 * Acenter
A(r = 86%) = 0.32 * Acenter
A(r = 100%) = 0
screen(S, D) = 1 - (1 - S) * (1 - D) // отдельно для R, G, B в 0..1
```
@@ -83,9 +84,13 @@ screen(S, D) = 1 - (1 - S) * (1 - D) // отдельно для R, G, B в 0..
На `.glow-pool` запрещены `opacity`, `fill-opacity`, filter и дополнительная
alpha: полупрозрачность должна участвовать в blend через gradient stops, а не
создавать отдельную element-композицию. Коэффициент `0.7` уже входит в
`Aplateau`; внешний `glow-pools-frame` не имеет opacity и не применяет его
`Acenter`; внешний `glow-pools-frame` не имеет постоянного opacity и не применяет его
повторно.
`.glow-spot` может временно анимировать opacity 0→1/1→0 в течение 500 мс.
Это только transition появления/исчезновения; в steady-state opacity равна 1 и не
меняет alpha-формулу.
Glow base использует собственный alpha-контракт #55 и не входит в эту формулу.
Blend не меняет base/tunnel opacity и не осветляет paper/backdrop.
@@ -191,7 +196,7 @@ research без скрытого fallback toggle.
- warm+cool overlap соответствует screen-формуле с установленным допуском;
- две одинаковые dim-лампы дают overlap светлее одного pool без channel clip;
- background/base и tunnel sector сохраняют baseline pixels;
- background/base и opening tunnel fill сохраняют baseline pixels;
- перестановка markers не меняет overlap;
- принудительный test-only fallback совпадает с current normal baseline;
- per-room clips переживают re-parenting: pool не появляется за физической
@@ -238,9 +243,10 @@ Release body описывает видимую пользователю смес
SVG render probe; `CSS.supports` сам по себе недостаточен.
2. Все pools документа используют одно screen/normal решение; смешанного режима
нет.
3. Альфа живёт в gradient stops, per-pool и outer-frame opacity отсутствуют;
3. Альфа steady-state живёт в gradient stops; outer-frame opacity
отсутствует, а opacity `.glow-spot` используется только для 500 ms transition;
коэффициент `0.7` применяется ровно один раз внутри формулы stop-opacity.
4. Glow base, data fill, paper/backdrop, tunnel sectors и sun остаются вне
4. Glow base, data fill, paper/backdrop, opening tunnel fills и sun остаются вне
isolated group и сохраняют baseline pixels.
5. Каждый pool сохраняет собственный clip-path после re-parenting.
6. Shared fixture 1/10/30/60 проходит frontend и backend schema tests.
@@ -250,5 +256,5 @@ Release body описывает видимую пользователю смес
9. #55 повторно использует ту же pool group и не создаёт второе смешивание.
10. Документация и ru/en changelog обновлены.
ТЗ готово к реализации только после повторного ревью capability probe,
alpha-семантики и измеримого performance gate.
Фактическая структура после #71 зафиксирована выше; каноническая модель
транспорта света и её ограничения живут в `docs/LIGHT.md`.
@@ -1,7 +1,7 @@
# ТЗ: визуальная непрерывность плана при возврате на вкладку
**Issue:** [#73 — Исключить мигание плана при возврате на вкладку](https://github.com/Matysh/houseplan-card/issues/73)<br>
**Статус:** ТЗ принято после двух раундов ревью; implementation gate пока не пройден, реализация не начата<br>
**Статус:** ТЗ принято после двух раундов ревью; implementation gate подтверждён публикацией `v1.61.0-beta.6` на SHA `d2bc9082807452c88e735c72d4a06d1ff873eaa9`; локальная реализация начата 2026-08-11 по прямому поручению владельца<br>
**Область:** `houseplan-card`, `houseplan-space-card`, lifecycle браузера и Home Assistant<br>
**Модель данных:** без изменений<br>
**Backend API:** без обязательных изменений
@@ -687,7 +687,7 @@ Sampler запускается внутри страницы, используе
**Implementation gate.** Реализация #73 не начинается, пока не закрыты и не прошли code review renderer-задачи [#67](https://github.com/Matysh/houseplan-card/issues/67), [#71](https://github.com/Matysh/houseplan-card/issues/71) и [#72](https://github.com/Matysh/houseplan-card/issues/72). После их закрытия действующие golden images переснимаются только при подтверждённом ожидаемом изменении, переутверждаются и фиксируются как авторитетный baseline для frame-sequence fixture. SHA baseline записывается в issue #73/тестовую документацию до первой реализации continuity controller.
**Текущий статус gate на 2026-08-10.** Зависимость #67 закрыта после code review. Финальные локальные реализации #71 и #72 присутствуют и проходят целевые проверки, но обе issue ещё открыты и изменения не зафиксированы в `dev`, поэтому renderer-gate пока не выполнен. После пересборки `test-build` прошли 152/152 теста в `test/logic.test.mjs`, `test/golden-matrix.test.mjs` и `test/release-contract.test.mjs`; `demo/smoke_glow.mjs` и `demo/smoke_glow_blending.mjs` также зелёные. Детерминированная golden-матрица v7 снята локально полностью: 34/34 сценария без runtime-ошибок, build fingerprint `82ddcb9a6909d1bf624e4a95e511d9c15a6adbab6ae6082cd0e31472c49ad56a`. Windows-кандидат `lighting-opaque-glow-two-doorways-dark` имеет SHA-256 `b0bbb9949e661f0f46b27ea018f9bd0e1485b7e8beb30e7c41af20f83f85ae10`, но служит только диагностическим предпросмотром: владельцем не утверждён и не заменяет авторитетный Linux CI artifact. До закрытия #71/#72, owner approval полного Linux artifact и записи авторитетного baseline SHA реализация continuity controller не начинается. Отдельный frame-sequence baseline будет создан уже на этапе 5 из утверждённого статического renderer baseline, а не подменяет этот gate.
**Текущий статус gate на 2026-08-11.** Gate выполнен релизом `v1.61.0-beta.6` на exact SHA `d2bc9082807452c88e735c72d4a06d1ff873eaa9`: зависимости #71/#72 опубликованы и закрыты, Linux golden matrix v7, full browser smoke и performance smoke прошли в Validate run `31437504173` без candidate/diff artifact. Каноническое описание света после этих изменений — `docs/LIGHT.md`. По прямому поручению владельца #73 переведена в In Progress и реализация controller начата от этого baseline; отдельный frame-sequence baseline создаётся на этапе 5 и не подменяет зафиксированный renderer baseline.
### Этап 1. Наблюдаемость и state machine