mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-06 22:49:16 +00:00
feat: LED strips — editor notes, docs, demo, smokes, golden scenes, profile (#780)
Stage 5 of #780. - Import summary: «Strips left unbound after import: {n}» from the backend `unbound_led_strips` count; «Optimize plans» reports strips passing through walls per space and edits none (AC16). - Linear field for long strips (ТЗ §13.2): pieces of at most the radius along the polyline, emitters thinned to r/4, each piece clipped to the visibility fans of its own emitters as separate clipPath children (no boolean pass per piece), one floor clip for the whole layer, no fan at all where nothing blocks within the radius; a grid index of body faces and boxed inside tests; unchanged fields skip re-diffing. 50×50 on the large house: first stable frame ~1.4 s, warm space ~1.1 s locally. - led-strips-v1 profile: demo/benchmark_led_strips.mjs with the derived large-house fixture (10×5, 50×50, none), absolute limits of the ТЗ table in demo/performance/budgets-led-strips.json, exact counters (zero recomputes on HA ticks/camera/colour, ≤50 cache entries, no growth over 20 cycles); added to the full performance workflow. - Bundle: LAZY_LED_GZIP_CEILING 10 KiB, LAZY_LED_EDITOR_GZIP_CEILING 11 KiB (measured + 10 %, rounded up); overlaps with the initial and editor graphs refused; the lazy editor graph stays inside its ceiling. - Smokes smoke_led_strip_draw/bind/glow, linked in smoke-links; 13 mutants in the registry (7 browser guards in the inventory); config field registry entry `spaces[].led_strips`. - Golden: five new scenes on the `golden-led` space of the visual fixture (`ledStrips` option, the designer's four strips on #868D94), matrix v71. - Docs: LIGHT, DEVICE-PRESENTATION, USER-GUIDE (en/ru), UX-MODES, ARCHITECTURE, ISOMETRIC, CONFIG-COMPATIBILITY, TOUCH-SUPPORT, demo/stand README, performance README; docs/design/led-strips with the unchanged designer archive, two paired frames and ACCEPTANCE.md; both changelogs. Issue: #780 User-Visible: yes
This commit is contained in:
@@ -689,3 +689,22 @@ compatibility export surface over `mutation-registry.mjs` (declarations),
|
||||
runs); dependencies never point back to the CLI. Guard-input caching is
|
||||
invocation-scoped (one resolver, one tracked-file snapshot); persisted success
|
||||
exists only in the explicit caught-witness ledger. Usage: [TESTING](TESTING.md).
|
||||
|
||||
## LED strips: lazy boundaries (#780)
|
||||
|
||||
`space.led_strips` belongs to the space; the link is one-way strip → marker
|
||||
and the device model stays the only owner of state and services. Four modules:
|
||||
|
||||
| Module | Graph | Holds |
|
||||
|---|---|---|
|
||||
| `led-strip-gate.ts` | initial | which markers a space shows as a strip, the anchor, the page-wide loaders of both chunks |
|
||||
| `led-strip-card.ts` | initial | delegation only: the Devices toolbar button, the device-dialog section, notes, the LED branch of the device history |
|
||||
| `led-strip-runtime.ts` (+ `led-strip-geometry.ts`) | lazy `led` | frame, stripe, hit/focus, 2.5D, static card |
|
||||
| `led-strip-field.ts` | lazy `led-field` | the linear field, loaded by the runtime only for an on strip in a Glow room |
|
||||
| `led-strip-editor.ts` (+ `i18n/led`) | lazy `led-editor` | the Devices tool, tray, picker, representation switch, LED history commands |
|
||||
|
||||
A View without a displayed active strip, and the Devices editor without the
|
||||
tool or an editable strip, load none of them. Each chunk checks the entry
|
||||
build fingerprint; a failed load is fail-dark for the strips only and retried
|
||||
on the next explicit entry. Budgets: `LAZY_LED_GZIP_CEILING` and
|
||||
`LAZY_LED_EDITOR_GZIP_CEILING` in `scripts/bundle-budget.mjs`.
|
||||
|
||||
@@ -2,6 +2,21 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- LED strips: in the Devices editor, the new «LED strip» tool next to «Add»
|
||||
draws a strip on the plan point by point — it snaps to the grid and to wall
|
||||
faces and stops at walls, windows and columns, passing doors and passages.
|
||||
Bind it to a light (or any device) and the strip replaces the device's icon:
|
||||
a two-stroke stripe that, when the light is on with Glow, casts a soft
|
||||
continuous band of its colour (50 cm by default) that does not pass through
|
||||
walls or closed doors; without Glow the stripe itself takes the light's
|
||||
colour. In the View the whole strip is one tap target, also in 2.5D and on
|
||||
the space card (light only with `light_pools`). Any device can switch between
|
||||
«Show as LED strip» and «Show as icon» in its settings; the shape is kept
|
||||
and comes back without redrawing. Drawing, editing vertices and switching are
|
||||
part of Undo/Redo; full and space exports keep the strips, a deleted device
|
||||
leaves an unbound strip, and «Optimize plans» reports strips passing through
|
||||
walls ([#780](https://github.com/Matysh/houseplan-card/issues/780)).
|
||||
|
||||
## v1.79.0-beta.2 — 2026-10-02
|
||||
|
||||
- Recreating the card in Home Assistant now keeps the editor camera steady and the
|
||||
|
||||
@@ -8,6 +8,22 @@
|
||||
|
||||
## Не выпущено
|
||||
|
||||
- LED-ленты: в редакторе устройств рядом с «Добавить» появился инструмент
|
||||
«LED-лента» — лента рисуется на плане по точкам, притягивается к сетке и к
|
||||
граням стен и упирается в стены, окна и колонны, проходя через двери и
|
||||
проёмы. Привяжите её к светильнику (или любому устройству) — и лента заменит
|
||||
значок устройства: полоса из двух штрихов, а при включённом свете с Glow —
|
||||
мягкая непрерывная полоса света его цвета (по умолчанию 50 см), которая не
|
||||
проходит сквозь стены и закрытые двери; без Glow цвет света принимает сама
|
||||
полоса. В просмотре вся лента — одна цель нажатия, в том числе в 2.5D и в
|
||||
карточке пространства (свет — с `light_pools`). Любое устройство
|
||||
переключается между «Показывать LED-лентой» и «Показывать значком» в своих
|
||||
настройках; форма сохраняется и возвращается без перерисовки. Рисование,
|
||||
правка вершин и переключение входят в Отменить/Повторить; полный экспорт и
|
||||
перенос пространства сохраняют ленты, удаление устройства оставляет ленту
|
||||
непривязанной, а «Оптимизировать планы» сообщает число лент, проходящих
|
||||
сквозь стены ([#780](https://github.com/Matysh/houseplan-card/issues/780)).
|
||||
|
||||
## v1.79.0-beta.2 — 2026-10-02
|
||||
|
||||
- При техническом пересоздании карточки Home Assistant сохраняются положение плана в редакторе
|
||||
|
||||
@@ -1117,3 +1117,35 @@ authentication. Plans and marker files are served only by the authenticated
|
||||
Stored configs may still hold older `/houseplan_files/plans|files/…` URLs:
|
||||
`contentUrl()` rewrites them on every read and portable import accepts both
|
||||
prefixes, so there is no storage migration.
|
||||
|
||||
## LED strips: `space.led_strips` (#780)
|
||||
|
||||
```ts
|
||||
led_strips?: Array<{ id: string; points: [number, number][]; marker: string | null; active?: boolean }>;
|
||||
```
|
||||
|
||||
- Optional; absent = no strips; no `model_version` bump. Coordinates are the
|
||||
space's normalised coordinates (scalar JSON-noise cleanup only, no lattice
|
||||
snap — a strip lies on real wall faces).
|
||||
- 2–50 points (a closed strip repeats its first point and needs three distinct
|
||||
vertices), non-zero length, finite coordinates; at most 50 records per space
|
||||
**including** hidden shapes; unique `id` per space; at most one record per
|
||||
marker in the whole config. `active` is a strict boolean; `false` needs a
|
||||
marker.
|
||||
- **Write-path normalisation** (`led_strips.py`, after structure/duplicate
|
||||
checks, before referential validation, one transaction): a link to a marker
|
||||
that is missing (deleted, tombstoned) becomes `marker: null, active: true`
|
||||
with id and points kept; an existing live marker with an empty `space`
|
||||
adopts the strip's space. A non-empty foreign `space` or a second link
|
||||
still rejects the whole write. `config/set` reports the counts
|
||||
`{led_strips: {unbound, space_adopted}}`; older clients may ignore them.
|
||||
- **Older clients** keep the unknown array on an ordinary save; deleting a
|
||||
bound marker succeeds and leaves an unbound strip.
|
||||
- **Transfer.** Full export/import keeps geometry, links and `active`. A
|
||||
space import remaps links through the same marker-id map as the devices;
|
||||
a strip whose marker did not travel (skipped duplicate, absent) arrives
|
||||
unbound — the count is in the import summary. «Plan only» export drops every
|
||||
link (`marker: null, active: true`).
|
||||
- **Diagnostics** count strips, unbound and hidden ones only — no ids,
|
||||
coordinates, room names or HA identifiers.
|
||||
- **Optimize plans** reports strips passing through walls and never edits them.
|
||||
|
||||
@@ -110,6 +110,22 @@ cover → light sources → device role, шторы и медиаплееры)
|
||||
только тогда, когда меняет победившее решение или наблюдаемый результат; полное
|
||||
декартово произведение binding × source × display × activity запрещено.
|
||||
|
||||
## LED strip representation (#780)
|
||||
|
||||
A marker may be shown as an LED strip instead of an icon (`space.led_strips`,
|
||||
`active` ≠ `false`). It is the same device: name, `hidden`, light source and
|
||||
state, `tap_*`, confirmation, `controls`, light colour/brightness/radius and
|
||||
the value badge keep working; the individual `display`, `size`, `angle`,
|
||||
`ripple_*` neither shape the strip nor get erased, and apply again on «Show as
|
||||
icon». While shown as a strip the icon is not drawn, its saved position and
|
||||
auto-grid slot are not used; the anchor — the point at half the polyline
|
||||
length — replaces the position for the room, tooltip, label and badge (an
|
||||
explicit valid `room_id` wins). A hidden shape (`active: false`) changes
|
||||
nothing: the icon works normally. No pulses and no alarm red on the stripe.
|
||||
`hidden` / HA-disabled suppress the stripe, its target and its light like an
|
||||
icon; temporarily unavailable keeps it as a dashed grey stripe. In the device
|
||||
catalogue the marker stays «On the plan».
|
||||
|
||||
## Implementation notes
|
||||
|
||||
- `device-value-badge.ts` owns candidate discovery, source keys, HA formatting,
|
||||
|
||||
@@ -292,6 +292,16 @@ Room names remain screen-facing and lose stroke, text shadow, drop shadow and
|
||||
halo. Iso uses `#303936` on a light presentation and `#f2f0e8` on a dark one;
|
||||
contrast comes from colour, never an outline.
|
||||
|
||||
## LED strips in 2.5D (#780)
|
||||
|
||||
An LED stripe is projected with the plan like a device tile: the body is
|
||||
raised by `ISO_TILE.lift` (0.075 D), its edge is swept `ISO_TILE.depth`
|
||||
(0.1 D) below in `isoEdgeColor`, and an inert blurred floor shadow from
|
||||
`isoTileShadow` follows theme and floor; D takes the shared `ISO_ICON_SCALE`.
|
||||
The linear field stays on the floor plane; the hit path moves with the raised
|
||||
body. Everything is in plan units, so zoom never detaches vertices or the
|
||||
target. Editors and `houseplan-space-card` keep Flat.
|
||||
|
||||
## Stage 6: public mode, tiles, sun and materials (#649)
|
||||
|
||||
The visual language and the numbers come from the designer lab (sketch 07,
|
||||
|
||||
@@ -409,3 +409,42 @@ 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.
|
||||
|
||||
## LED strips: a linear source (#780)
|
||||
|
||||
A marker shown as an LED strip is one source whose emitter is the whole
|
||||
polyline (`space.led_strips`, ТЗ #780). It never also paints a round pool from
|
||||
its anchor. Colour, brightness, role and availability come from the same
|
||||
`resolveGlowCandidates` / `resolveGlowAppearance` path as every other source;
|
||||
only the geometry differs:
|
||||
|
||||
- **Radius.** 50 cm by default, independent of `settings.glow_radius_cm`; the
|
||||
marker's personal `glow_radius_cm` wins.
|
||||
- **Field.** A continuous band along every segment with round free ends: grey
|
||||
luminance bands of the shared `GLOW_FALLOFF`, one piece per stretch, blended
|
||||
with `lighten` inside one isolated group, so corners and the closing of a
|
||||
loop neither seam nor double the brightness. Intensity and the 500 ms fade
|
||||
are the shared `glowAlpha` / `GLOW_FADE_MS`.
|
||||
- **Visibility.** The strip is cut into consecutive pieces no longer than the
|
||||
radius; each piece is clipped to the visibility fans of its own emitters
|
||||
(the shared `visibilityPolygon` over the same barrier scene as pools; the
|
||||
fans are separate paths of one clipPath, no boolean pass per piece), and the
|
||||
whole field layer is clipped once to the floor. A piece with no occluder
|
||||
within the radius needs no fan at all. Windows, columns,
|
||||
thick walls and Solid zero walls block; doors/gates pass by their actual
|
||||
opening; Dashed zero walls are transparent. Emitters on a thick face sit
|
||||
`epsilonGeom` (0.001 cm) outward into free floor; a part buried in a body
|
||||
emits nothing; a strip entirely inside a wall has no field.
|
||||
- **Core.** With effective Glow (space `glow_enabled` + room `glow`) the core
|
||||
stays white and the colour is the field; without Glow the core takes the
|
||||
source colour and there is no field. Off: white core, no field.
|
||||
Unavailable/unknown: dashed grey stripe, no field — the link is kept.
|
||||
- **Surfaces.** The full card renders field and stripe in the View; the
|
||||
Devices editor shows active strips (unbound ones as grey dashes);
|
||||
Plan/Background show a passive translucent stripe. `houseplan-space-card`
|
||||
draws the passive stripe always and the field only with `light_pools: true`
|
||||
— with the option off no barrier, visibility or timer is created.
|
||||
- **Laziness.** The stripe/hit/2.5D code (`led-strip-runtime`) and the field
|
||||
(`led-strip-field`) are separate lazy chunks; the initial graph holds only
|
||||
the presence check and the loader (`led-strip-gate`). Caches are bounded per
|
||||
space (50 shapes, 50 visibilities) and released on space change.
|
||||
|
||||
@@ -178,6 +178,16 @@ cannot operate stale geometry; leaving the editor is always a single safe
|
||||
action. The decorative smoothness of the editor chrome remains best effort on
|
||||
coarse-pointer devices, while the correct final View frame is release-blocking.
|
||||
|
||||
### LED strip tool (#780)
|
||||
|
||||
The LED tool applies the same safety floor as the wall chain: only a clean
|
||||
tap adds a point; pan, pinch, a second finger, `pointercancel`, a lost
|
||||
capture and the synthetic click after navigation add nothing, finish nothing
|
||||
and open no picker. The touch hint does not require keyboard modifiers:
|
||||
switching the tool off finishes the chain. In the View the whole stripe is
|
||||
one target with `max(22 CSS px, t/2)` hit radius; a pan or pinch over it calls
|
||||
no action, long press or more-info, and the next clean tap works at once.
|
||||
|
||||
## Deliberate degradation rule
|
||||
|
||||
When an editor change would be expensive to implement correctly for touch, the
|
||||
|
||||
@@ -1041,6 +1041,35 @@ binding to **Available again**. A disabled or missing binding keeps its saved
|
||||
category and receives a separate Home Assistant status instead of silently
|
||||
moving to another tab.
|
||||
|
||||
### LED strip (#780)
|
||||
|
||||
An LED strip is drawn on the plan as a polyline and can stand in for any
|
||||
device instead of its round icon. Everything happens in the **Devices
|
||||
editor** — there is no need to switch to the Plan editor.
|
||||
|
||||
| Action | Result |
|
||||
|---|---|
|
||||
| **LED strip** (next to «Add») | Starts drawing. A clean click adds a point; the grid and wall faces attract it, a zero-thickness wall its axis; `Shift` gives 45° steps. A new segment stops at the first face of a thick wall, partition, column or window («The strip stopped at a wall»); doors, gates and passages let it through |
|
||||
| `Ctrl/Cmd+Z` while drawing | Removes the chain's last point, not another history step |
|
||||
| `Esc`, a double click on the last point, pressing **LED strip** again, leaving the editor | Finish the strip; a chain of fewer than two distinct points is not saved |
|
||||
| A click on the first point (at least three vertices) | Closes the strip |
|
||||
| After finishing | The device picker opens: lights first, devices bound to another strip are disabled with an explanation, **New device…** creates one and binds it in the same write, **Later** keeps an unbound strip (grey dashes, visible in the Devices editor only) |
|
||||
| A click on a strip | Selects it and shows its vertices; they can be dragged, and walls stop them even on a fast drag. The tray offers **Device settings**, **Bind / Change device**, **Unbind**, **Show as icon**, **Delete strip**. `Esc` or a clean click on free space drops the selection |
|
||||
| In the device dialog | **Show as LED strip** draws a shape for the same marker (or brings a saved one back at once); **Show as icon** returns the ordinary icon, the shape is kept hidden and comes back without redrawing. The usual unsaved-changes question comes first |
|
||||
|
||||
Binding creates no new Home Assistant device: the same marker, light
|
||||
settings, tap actions and badge. The strip glows when the device does: with
|
||||
Glow on, a white core and a soft band of colour 50 cm wide by default (or the
|
||||
device's own radius); without Glow the core takes the light's colour. Light
|
||||
does not pass through walls or closed doors. In View the whole strip is one
|
||||
tap target, like an icon. Every finished shape change and every switch of the
|
||||
representation is one **Undo/Redo** step.
|
||||
|
||||
«Unbind» keeps an unbound shape, «Delete strip» deletes the shape but not the
|
||||
device. Deleting the device leaves the strip unbound. Up to 50 points per
|
||||
strip and 50 strips per space (hidden shapes included). «Optimize plans»
|
||||
reports strips passing through walls and changes nothing in them.
|
||||
|
||||
### Marker basics
|
||||
|
||||
The dialog has five cards: **Basics**, a compact tap-action card without a
|
||||
@@ -2154,6 +2183,7 @@ Marker attachments accept PDF/PNG/JPG/WebP/TXT up to 50 MB, with a
|
||||
| Room details use hover in View | A touch-only user may need an editor or another visible metric |
|
||||
| Sun has no exterior 3D model | It cannot know shadows from trees, awnings or neighbouring structures |
|
||||
| Icon rules use regular expressions | First matching rule wins and invalid expressions are rejected |
|
||||
| LED strips are not cut by walls | A thickened wall does not edit a strip: parts inside the wall do not glow, «Optimize plans» shows how many strips pass through walls |
|
||||
|
||||
Storage guards allow up to 50 spaces, 400 rooms per space, 2,000 markers, 500
|
||||
openings, 1,000 Background items and bounded wall/physical-object catalogues
|
||||
|
||||
@@ -1137,6 +1137,35 @@ binding tombstone не вырезает её из живого устройст
|
||||
|
||||

|
||||
|
||||
### LED-лента (#780)
|
||||
|
||||
Светодиодную ленту можно нарисовать на плане как ломаную и показать ею любое
|
||||
устройство вместо круглого значка. Всё делается в **редакторе устройств** —
|
||||
в редактор плана переходить не нужно.
|
||||
|
||||
| Действие | Результат |
|
||||
|---|---|
|
||||
| **LED-лента** (рядом с «Добавить») | Включает рисование. Чистый клик ставит точку; сетка и грани стен притягивают точку, у стены нулевой толщины — её ось; `Shift` даёт шаг 45°. Новый отрезок упирается в первую грань толстой стены, перегородки, колонны или окна («Лента упёрлась в стену»); двери, ворота и проходы пропускают |
|
||||
| `Ctrl/Cmd+Z` во время рисования | Убирает последнюю точку цепочки, а не чужое действие истории |
|
||||
| `Esc`, двойной клик по последней точке, повторное нажатие **LED-лента**, выход из редактора | Завершают ленту; цепочка меньше двух различных точек не сохраняется |
|
||||
| Клик по первой точке (не меньше трёх вершин) | Замыкает ленту |
|
||||
| После завершения | Открывается выбор устройства: сначала светильники, занятые другой лентой недоступны с пояснением, **Новое устройство…** создаёт его и привязывает в той же записи, **Позже** оставляет непривязанную ленту (серый пунктир, видна только в редакторе устройств) |
|
||||
| Клик по ленте | Выделяет её и показывает вершины; их можно перетаскивать, упор в стены действует и при быстром перетаскивании. Внизу — лоток: **Настройки устройства**, **Привязать / Сменить устройство**, **Отвязать**, **Показывать значком**, **Удалить ленту**. `Esc` или чистый клик по свободному месту снимают выделение |
|
||||
| В диалоге устройства | **Показывать LED-лентой** — нарисовать форму для этого же маркера (или сразу вернуть сохранённую); **Показывать значком** — вернуть обычный значок, форма сохраняется скрыто и восстанавливается без перерисовки. Перед этим действует обычный вопрос о несохранённых изменениях |
|
||||
|
||||
Привязка не создаёт нового устройства в Home Assistant: тот же маркер, те же
|
||||
настройки света, действия по нажатию и бейдж. Лента светится, когда светится
|
||||
устройство: при включённом Glow — белое ядро и мягкая цветная полоса света
|
||||
радиусом 50 см (или персональный радиус устройства), без Glow — ядро цвета
|
||||
источника. Свет не проходит сквозь стены и закрытые двери. В просмотре лента
|
||||
— одна цель нажатия по всей длине, как значок. Каждое законченное изменение
|
||||
формы и смена представления — один шаг **Отменить/Повторить**.
|
||||
|
||||
«Отвязать» оставляет непривязанную форму, «Удалить ленту» удаляет форму, но не
|
||||
устройство. Удаление устройства оставляет ленту непривязанной. Не более 50 точек
|
||||
в ленте и 50 лент в пространстве (включая скрытые формы). «Оптимизировать
|
||||
планы» сообщает число лент, проходящих сквозь стены, и ничего в них не меняет.
|
||||
|
||||
### Основные настройки маркера
|
||||
|
||||
Диалог устройства собран в пять карточек: **Основное**, компактная карточка
|
||||
@@ -2406,6 +2435,7 @@ cycle: 0
|
||||
| Редакторы desktop-first | На touch-устройствах функции редактирования могут быть неудобны, ограничены или отсутствовать; для создания и обслуживания плана используйте компьютер |
|
||||
| Солнце не знает внешнюю 3D-геометрию | Нет теней от соседних крыльев, навесов и деревьев |
|
||||
| Иконки по регулярным выражениям | Правила мощные, но требуют знания regex; первая совпавшая строка побеждает |
|
||||
| LED-лента не режется по стенам | После утолщения стены форма не правится сама: участки внутри стены не светятся, «Оптимизировать планы» показывает число таких лент |
|
||||
|
||||
Технические пределы защиты хранилища: до 50 пространств, 400 комнат на пространство, 2000 маркеров, 500 проёмов, 1000 элементов декора и ограниченные текущей схемой каталоги стен/физических объектов. Конфигурационный пакет ограничен 2 МБ.
|
||||
|
||||
|
||||
@@ -249,6 +249,22 @@ layer you cannot see is a layer you cannot edit.
|
||||
👁 "Show hidden" (local editor tool; replaced the shared show-all toggle),
|
||||
⬡ icon rules.
|
||||
|
||||
- **LED strip** (#780) — the only place a strip is drawn or edited. The tool
|
||||
sits next to «Add»; it is a temporary drawing mode inside Devices, not a
|
||||
separate editor. Clean clicks add points (pan, pinch, a second finger,
|
||||
cancel and the synthetic click after navigation never do), snapping to the
|
||||
grid and to physical wall faces / zero-wall axes; a new segment stops at
|
||||
the first face of masonry, a partition, a column or a window. `Ctrl/Cmd+Z`
|
||||
removes the chain's own point first, `Esc` finishes. A finished strip opens
|
||||
the device picker. A clicked strip is selected (session state only) and
|
||||
shows vertex handles and the LED branch of the secondary tray. An ordinary
|
||||
icon clicked while a strip is selected still opens its own dialog. The
|
||||
device dialog switches the representation («Show as LED strip» / «Show as
|
||||
icon»). Geometry and representation changes are LED commands of the same
|
||||
device history; the stack still never absorbs marker settings or
|
||||
Plan/Background edits. Plan and Background show strips as a passive
|
||||
translucent mark only — no tool, handles or targets.
|
||||
|
||||
## Background — the decor underlay
|
||||
|
||||
- Toolbar tools: Select / optional Plan backdrop / Line / Rectangle / Oval /
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# #780 · LED strips — acceptance against the designer frames
|
||||
|
||||
AC8/AC18 of [#780](https://github.com/Matysh/houseplan-card/issues/780). The
|
||||
product reproduces the designer's four strips — a free straight strip (blue),
|
||||
a strip on the inner face of the bottom wall (violet), a polyline along the
|
||||
wall faces around a corner (yellow) and a closed rectangle (green) — on a
|
||||
synthetic plan with the reference floor `#868D94`. Scene colours
|
||||
`#80D5FF`, `#E680FF`, `#FFEA80`, `#58FF58` are values of the four sources, not
|
||||
a product palette.
|
||||
|
||||
## How the frames were made
|
||||
|
||||
- **Designer** — `source/previews/Led-On.png`, `source/previews/Led-Off.png`
|
||||
(2543 × 1572, unchanged).
|
||||
- **Product** — golden scenes `led-strip-design-reference-on-light` and
|
||||
`led-strip-design-reference-off-light` (`demo/golden/matrix.mjs`, fixture
|
||||
`makeVisualMatrixFixture({ ledStrips: true })` in
|
||||
`demo/fixtures/visual-matrix.mjs`: viewport 1000 × 760, DPR 1, English, light
|
||||
theme, `icon_size` 3.4, 20 cm walls, cell 5 cm). Captured with
|
||||
`node demo/golden/run.mjs --mode=capture --scenario=<id>` on the built bundle;
|
||||
the reviewed baselines are accepted from the Linux CI artifact of the task
|
||||
(label `ci:golden`).
|
||||
|
||||
The plans differ (the designer frame is a fragment of a real floor with
|
||||
furniture and icons), so the comparison is of the visual language and the four
|
||||
behaviours, not of positions.
|
||||
|
||||
## On
|
||||
|
||||

|
||||
|
||||
## Off
|
||||
|
||||

|
||||
|
||||
## ТЗ §3 — visual contract
|
||||
|
||||
| Requirement | Product | Evidence |
|
||||
|---|---|---|
|
||||
| `D = icon_size/100 × iconUnit(space)`, not `marker.size`; 2.5D uses the shared scale | `ledFrame` takes `iconPct` from the full card's or the space card's own `icon_size`; `ISO_ICON_SCALE` in 2.5D | golden `iso-led-strip-dark`, reference pair |
|
||||
| Thickness 0.08 D off / 0.12 D on; outline `#383838` t, core t/2, round joins and caps | `renderLedStripes` | `led-strip-off-light`, reference pair |
|
||||
| Off: white core, no field, both themes | stripe state `off` | smoke `smoke_led_strip_glow.mjs`, `led-strip-off-light` |
|
||||
| On with Glow: white core + coloured field; without Glow: core in the source colour, no field | `ledStripView` + `resolveGlowAppearance` | smoke `smoke_led_strip_glow.mjs` |
|
||||
| Glow is the space/room switch, independent of `fill_mode` | `glowFor(room)` | `lighting-led-strip-glow-dark` uses `fill_mode: none` |
|
||||
| Per-piece offset: t/2 on a thick face into free floor, 0 on free floor and zero walls; continuous transition | `visibleStripPath` | unit `test/led-strip-geometry.test.mjs` (AC8) |
|
||||
| Field 50 cm by default, own `glow_radius_cm` wins; round free ends; no seams or doubled brightness at corners/closure | `ledFrame`, `led-strip-field` (`lighten` in one group) | unit `test/led-strip-runtime.test.mjs`; reference pair |
|
||||
| Shared `glowAlpha` / `GLOW_FALLOFF` / `GLOW_FADE_MS` | field bands from `falloffAt` | unit `test/led-strip-runtime.test.mjs` |
|
||||
| Field under icons, badges and labels; icons not tinted | glow layer below the device layer | reference pair (designer tinting deliberately not reproduced) |
|
||||
|
||||
## Designer §11 — ten visual criteria
|
||||
|
||||
| # | Criterion | Result |
|
||||
|---|---|---|
|
||||
| 1 | Same geometry on/off; only colour/thickness/field change | Stored points never change; only `t` and the derived offset follow the state — pairs on/off |
|
||||
| 2 | White core and dark outline keep contrast on grey floor, near hatched walls, over the field | Visible in both pairs; `#383838` outline, opaque core |
|
||||
| 3 | Straight parts are not a chain of circles | One stroked path per strip; the field is bands of stroked paths, not discs |
|
||||
| 4 | No hard rectangular cut of light at the ends | Round caps of the field bands (blue strip in the pair) |
|
||||
| 5 | No dark gaps or bright spots at corners | Round joins; `lighten` blend of pieces (yellow corner, green loop) |
|
||||
| 6 | A wall strip lights into the room, not behind the wall | Visibility clip from emitters `epsilonGeom` outward (violet, yellow) |
|
||||
| 7 | A closed rectangle gives a continuous field along the perimeter | Green loop in the On pair |
|
||||
| 8 | Colour changes with the entity without changing shape/radius/edges | Colour from `resolveGlowAppearance`; the stored points and the radius do not depend on state (four colours of one fixture in the On pair) |
|
||||
| 9 | When off the field fades smoothly and the thin stripe stays | Shared 500 ms fade; Off pair keeps the stripe |
|
||||
| 10 | Zoom keeps the proportions of thickness, outline, hit area and 2.5D lift | All sizes in plan units of D; hit radius `max(22 px, t/2)` in screen px (unit AC12) |
|
||||
|
||||
## Accepted differences
|
||||
|
||||
- Pixel sizes and the mockup blur (22.2–30 px) are not product filters: the
|
||||
product uses 0.08/0.12 D and the shared falloff, so its stripe is thinner at
|
||||
the default `icon_size` and the band edge is the shared Glow edge.
|
||||
- Icons and labels are not tinted by the field.
|
||||
- The mockup's "half the shared radius" and "always coloured core" are replaced
|
||||
by 50 cm and the white core under Glow (owner's decision in #780).
|
||||
@@ -0,0 +1,22 @@
|
||||
# LED strips — designer materials (#780)
|
||||
|
||||
The designer's archive for the LED strips, kept **unchanged** under
|
||||
[`source/`](source/): `Issue-662-LED-light-spec-2026-10-01.zip` attached to
|
||||
[#662 (comment)](https://github.com/Matysh/houseplan-card/issues/662#issuecomment-5935038397)
|
||||
(archive SHA-256 of every member in [`source/MANIFEST-SHA256.txt`](source/MANIFEST-SHA256.txt);
|
||||
all six checksums verified when the files were copied). The Figma section is
|
||||
[Led Light, 626:34](https://www.figma.com/design/cpGN8MhJGydwVOUzZv8dia/House-plan?node-id=626-34)
|
||||
with the frames Led On (626:35) and Led Off (626:53).
|
||||
|
||||
**The contract is the ТЗ in the body of
|
||||
[#780](https://github.com/Matysh/houseplan-card/issues/780)**, not the archived
|
||||
`source/TZ-issue-662-LED-strips.md`. Where they differ the issue wins — notably
|
||||
the 50 cm default radius, the white core under Glow, editing in the Devices
|
||||
editor and the product thicknesses 0.08/0.12 D instead of the mockup's pixel
|
||||
sizes and blur. These files are documentation only; nothing here reaches the
|
||||
runtime bundle.
|
||||
|
||||
- [`ACCEPTANCE.md`](ACCEPTANCE.md) — the two paired frames and the acceptance
|
||||
table against §3 of the ТЗ and the ten visual criteria of the designer's §11.
|
||||
- [`pairs/`](pairs/) — designer frame (left) next to the product scene (right),
|
||||
256-colour diagnostic copies.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 75 KiB |
@@ -0,0 +1,3 @@
|
||||
[InternetShortcut]
|
||||
URL=https://www.figma.com/design/cpGN8MhJGydwVOUzZv8dia/House-plan?node-id=626-34
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[InternetShortcut]
|
||||
URL=https://github.com/Matysh/houseplan-card/issues/662
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
A0E61CCF1416CE8F92332CD6B855C8131628E32C27ADDD77EDBEE3A6A2B3F37A README.md
|
||||
1A98198905F147E3D1B8720F45481B6D5295FBDB94F4AD407F92FAC044B7A1DA TZ-issue-662-LED-strips.md
|
||||
7AD21EE0042384443B2E41AE337ED31416127E183A72765EBB493AD808FCB651 FIGMA-LINK.url
|
||||
16317687BA78439DA6052B384825E5FEF480A71EB3F46FCDDC4B180EDD09760C ISSUE-LINK.url
|
||||
EBAC64901AAA055DD74A2D04E2EE18093AA59FE65E152CB699BECD8C5C9A82FB previews/Led-On.png
|
||||
8DCDD5F2FE784EE9C9DAB98D092A89457CA171554481586927E23E0DF7FFC18D previews/Led-Off.png
|
||||
@@ -0,0 +1,22 @@
|
||||
# Issue #662 — LED-ленты
|
||||
|
||||
Архив содержит техническое задание и визуальные референсы для задачи [Matysh/houseplan-card#662](https://github.com/Matysh/houseplan-card/issues/662).
|
||||
|
||||
## Состав
|
||||
|
||||
- [`TZ-issue-662-LED-strips.md`](./TZ-issue-662-LED-strips.md) — сводное ТЗ: поведение из issue и визуальные требования из Figma.
|
||||
- [`previews/Led-Off.png`](./previews/Led-Off.png) — четыре примера выключенных LED-лент.
|
||||
- [`previews/Led-On.png`](./previews/Led-On.png) — четыре примера включённых LED-лент и рассеивания света.
|
||||
- [`FIGMA-LINK.url`](./FIGMA-LINK.url) — ссылка на исходный макет.
|
||||
- [`ISSUE-LINK.url`](./ISSUE-LINK.url) — ссылка на исходную задачу.
|
||||
- [`MANIFEST-SHA256.txt`](./MANIFEST-SHA256.txt) — контрольные суммы файлов архива.
|
||||
|
||||
## Основные ссылки
|
||||
|
||||
- Issue: <https://github.com/Matysh/houseplan-card/issues/662>
|
||||
- Figma, секция `Led Light`: <https://www.figma.com/design/cpGN8MhJGydwVOUzZv8dia/House-plan?node-id=626-34>
|
||||
- Фрейм `Led On`: <https://www.figma.com/design/cpGN8MhJGydwVOUzZv8dia/House-plan?node-id=626-35>
|
||||
- Фрейм `Led Off`: <https://www.figma.com/design/cpGN8MhJGydwVOUzZv8dia/House-plan?node-id=626-53>
|
||||
|
||||
Дата подготовки: 1 октября 2026 года.
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
# Техническое задание: LED-ленты на плане
|
||||
|
||||
Связанная задача: [Matysh/houseplan-card#662](https://github.com/Matysh/houseplan-card/issues/662)
|
||||
Визуальный макет: [Figma — `Led Light`](https://www.figma.com/design/cpGN8MhJGydwVOUzZv8dia/House-plan?node-id=626-34)
|
||||
Фреймы: [`Led On`](https://www.figma.com/design/cpGN8MhJGydwVOUzZv8dia/House-plan?node-id=626-35) и [`Led Off`](https://www.figma.com/design/cpGN8MhJGydwVOUzZv8dia/House-plan?node-id=626-53)
|
||||
Дата фиксации: 01.10.2026
|
||||
|
||||
## 1. Назначение
|
||||
|
||||
Добавить в House Plan протяжённое представление устройств `light.*`: LED-лента отображается не точечным значком, а ломаной линией в реальном месте установки. Пользователь должен сразу понимать положение, форму, состояние и цвет ленты, а также включать и выключать её нажатием по любой части линии.
|
||||
|
||||
Поведенческий контракт определяется issue #662. Figma определяет визуальное направление, форму выключенной полосы, характер свечения, работу света у стен, в углах и на свободном участке пола. При возможном расхождении логика и размеры в единицах плана берутся из issue, внешний вид — из Figma.
|
||||
|
||||
## 2. Визуальные референсы
|
||||
|
||||
### 2.1. Выключенное состояние
|
||||
|
||||

|
||||
|
||||
Во фрейме `Led Off` показаны четыре варианта геометрии и размещения:
|
||||
|
||||
1. прямая лента на свободном участке плана;
|
||||
2. прямая лента вплотную к стене;
|
||||
3. Г-образная лента с поворотом в углу;
|
||||
4. замкнутая прямоугольная лента по периметру зоны.
|
||||
|
||||
Требования к выключенной ленте:
|
||||
|
||||
- световое пятно отсутствует;
|
||||
- ядро белое `#FFFFFF`;
|
||||
- внешняя обводка тёмная `#383838`;
|
||||
- концы и стыки визуально скруглены;
|
||||
- линия остаётся различимой на светлом и тёмном фоне плана;
|
||||
- в исходном Figma-фрейме номинальная высота белого ядра — 6 px, внешняя обводка — 3 px, радиус скругления — 5 px;
|
||||
- в продукте итоговая толщина масштабируется с планом и подчиняется C6 issue: `0,08 D` для выключенного состояния, где `D` — диаметр значка маркера.
|
||||
|
||||
### 2.2. Включённое состояние
|
||||
|
||||

|
||||
|
||||
Во фрейме `Led On` показаны четыре сценария:
|
||||
|
||||
1. синяя прямая лента на расстоянии от стены — свет расходится по обе стороны;
|
||||
2. фиолетовая лента вдоль нижней стены — свет направлен в комнату;
|
||||
3. жёлтая Г-образная лента вдоль верхней и правой стен — свет корректно продолжается через угол и ограничивается стенами;
|
||||
4. зелёная лента по периметру помещения — свет соединяется на углах без разрывов и не выходит за стены.
|
||||
|
||||
Требования к включённой ленте:
|
||||
|
||||
- сама полоса остаётся читаемой поверх свечения;
|
||||
- цвет свечения поступает от устройства: live RGB/цветовая температура либо `glow_color`;
|
||||
- показанные в макете синий, фиолетовый, жёлтый и зелёный цвета являются примерами, а не фиксированной палитрой;
|
||||
- свет распространяется непрерывно вдоль каждого сегмента;
|
||||
- на свободных концах должен быть мягкий округлый спад без прямоугольного обрыва;
|
||||
- в точках поворота световые поля сегментов соединяются без тёмного шва и без заметного удвоения яркости;
|
||||
- свет, направленный к толстой стене, обрезается стеной; лента на грани стены освещает только сторону комнаты;
|
||||
- в продукте толщина включённой линии — `0,12 D`; свечение не входит в эту толщину;
|
||||
- появление и исчезновение поля света происходит через существующий `GLOW_FADE_MS`.
|
||||
|
||||
## 3. Измеренные параметры макета Figma
|
||||
|
||||
Параметры ниже документируют исходник 2543×1572 px и нужны для визуального сравнения. Они не заменяют масштабируемые величины `D` из issue.
|
||||
|
||||
| Элемент | Параметр в Figma |
|
||||
|---|---|
|
||||
| Ядро выключенной полосы | `#FFFFFF`, высота 6 px |
|
||||
| Обводка | `#383838`, 3 px, Outside |
|
||||
| Скругление | 5 px |
|
||||
| Синий пример | `#80D5FF` → прозрачный |
|
||||
| Фиолетовый пример | `#E680FF` → прозрачный |
|
||||
| Жёлтый пример | `#FFEA80` → прозрачный |
|
||||
| Зелёный пример | `#58FF58`, мягкие внутренние и внешние тени |
|
||||
| Линейное поле | ориентировочная глубина 86–102 px в исходном фрейме |
|
||||
| Радиальные окончания | диаметр 188–206 px |
|
||||
| Blur радиальных окончаний | 22,2 px |
|
||||
| Blur протяжённого поля | до 30 px в примерах |
|
||||
|
||||
В рабочем рендерере мягкость должна строиться градиентами по `GLOW_FALLOFF`; CSS/SVG `filter` не является обязательной частью реализации и не должен ухудшать производительность.
|
||||
|
||||
## 4. Модель данных
|
||||
|
||||
В каждом пространстве допускается необязательный массив:
|
||||
|
||||
```ts
|
||||
space.led_strips: {
|
||||
id: string;
|
||||
points: number[][];
|
||||
marker: string | null;
|
||||
}[];
|
||||
```
|
||||
|
||||
- `points` содержит от 2 до 50 точек в канонических координатах пространства;
|
||||
- не более 50 лент в одном пространстве;
|
||||
- `id` уникален в пространстве;
|
||||
- один маркер может быть привязан не более чем к одной ленте во всех пространствах;
|
||||
- версия модели не меняется, миграция не требуется;
|
||||
- полный и per-space экспорт/импорт сохраняют геометрию; отсутствующий при импорте маркер превращает ленту в непривязанную.
|
||||
|
||||
Якорь ленты — точка на половине суммарной длины ломаной. Он используется для определения комнаты, подписи, бейджа, подсказки и агрегатов.
|
||||
|
||||
## 5. Рисование и редактирование
|
||||
|
||||
- В редакторе плана после инструмента «Перегородка» появляется кнопка `LED-лента` с иконкой `mdi:led-strip-variant`.
|
||||
- Лента рисуется цепочкой точек по контракту инструмента стен.
|
||||
- Клик или тап добавляет вершину; Shift ограничивает направление шагом 45°; Ctrl+Z удаляет последнюю добавленную точку.
|
||||
- Завершение: Esc, двойной клик по последней точке, смена инструмента или выход из редактора.
|
||||
- Цепочка короче двух точек отбрасывается.
|
||||
- Панорамирование, pinch, второй палец и `pointercancel` не добавляют точки.
|
||||
- Точки прилипают к сетке и к физическим граням стен с порогом магнита мебели.
|
||||
- В v1 можно перетаскивать вершины и удалить ленту целиком. Вставка и удаление отдельной вершины не входят в задачу.
|
||||
|
||||
## 6. Стены и проёмы
|
||||
|
||||
- Лента не может пересекать тело стены с толщиной.
|
||||
- При попытке пересечения новый сегмент останавливается на ближайшей грани тела стены.
|
||||
- Касание стены и движение точно вдоль её грани допустимы.
|
||||
- Через `door`, `gate` и `passage` лента проходит по геометрическому проёму независимо от текущего состояния двери.
|
||||
- Окно остаётся частью тела стены и блокирует ленту.
|
||||
- Стены нулевой толщины, Solid и Dashed, не являются телом и не блокируют рисование.
|
||||
- Перетаскивание вершины проверяет оба соседних сегмента по тем же правилам.
|
||||
- Если стена была утолщена или добавлена позже, геометрия ленты автоматически не исправляется; Optimize сообщает число проблемных лент.
|
||||
|
||||
## 7. Привязка устройства
|
||||
|
||||
- После завершения ломаной открывается существующий диалог добавления устройства; сущности `light.*` показываются первыми.
|
||||
- Сохранение создаёт или переиспользует live-маркер и связывает его с лентой атомарно.
|
||||
- Закрытие или «Позже» сохраняет непривязанную ленту.
|
||||
- Непривязанная лента отображается серым пунктиром только в редакторах и не видна во View, киоске и space-card.
|
||||
- После привязки обычный значок маркера исчезает, но его `layout` сохраняется.
|
||||
- В лотке выбранной ленты доступны: привязать, сменить устройство, отвязать и удалить ленту.
|
||||
- После отвязки или удаления маркер снова отображается значком на сохранённой позиции либо в авторасстановке.
|
||||
|
||||
## 8. Представление и состояния
|
||||
|
||||
| Состояние | Представление |
|
||||
|---|---|
|
||||
| On | линия `0,12 D`, полный цвет `resolveGlowAppearance`, поле света при активной заливке «Свечение источников света» |
|
||||
| Off | линия `0,08 D`, белое/приглушённое ядро с тёмной обводкой по визуальному референсу, без поля света |
|
||||
| Unavailable | серый пунктир, без поля света |
|
||||
| Hidden | не отображается |
|
||||
| Unbound | серый пунктир только в редакторах |
|
||||
|
||||
- Линия строится SVG `path` со скруглёнными концами и соединениями.
|
||||
- Подпись и `value_badge` располагаются относительно якоря.
|
||||
- Hover, long press, контекстное меню и клавиатурный фокус работают как у обычного маркера.
|
||||
- Enter и Space запускают тот же `_clickDevice()`.
|
||||
- У ленты нет пульсации и тревожного красного состояния.
|
||||
|
||||
## 9. Свечение
|
||||
|
||||
- Световой источник линейный: по сегментам строятся капсулы радиуса `glow_radius_cm`, а при отсутствии персонального значения — половины общего радиуса.
|
||||
- Точки выборки: все вершины плюс промежуточные точки с шагом около 100 см, не более восьми точек на ленту.
|
||||
- Капсулы одной ленты смешиваются через `lighten` внутри изолированной группы; с остальными пятнами группа смешивается через `screen`.
|
||||
- Поле пересекается с полом и с объединением полигонов видимости; за стеной свет отсутствует.
|
||||
- Для ленты на грани толстой стены точки выборки сдвигаются на эпсилон в сторону комнаты.
|
||||
- Лента на стене нулевой толщины освещает обе стороны.
|
||||
- Точка выборки внутри тела стены не создаёт веер; если внутри все точки, светового кандидата нет.
|
||||
- В остальных режимах заливки световое поле не отображается.
|
||||
|
||||
## 10. Взаимодействие и 2.5D
|
||||
|
||||
- Активная зона проходит по всей ломаной: расстояние до неё не более `max(22 px экрана, половина толщины)`.
|
||||
- Точка в 20 px от ленты должна попадать в неё, в 30 px — нет.
|
||||
- При наложении с окрашенной капсулой значка приоритет у значка; между лентами выбирается ближайшая, затем стабильный порядок по `id`.
|
||||
- В 2.5D лента поднимается на `0,075 D`, получает торец `0,1 D` и тень на полу; само световое поле остаётся на полу.
|
||||
- `houseplan-space-card` отображает ленту и свет, но остаётся read-only.
|
||||
|
||||
## 11. Критерии визуальной приёмки по Figma
|
||||
|
||||
1. Во включённом и выключенном состоянии геометрия совпадает: изменяются цвет/толщина и наличие свечения, а не траектория.
|
||||
2. Белое ядро и тёмная обводка сохраняют контраст на сером полу, возле штрихованных стен и поверх светового поля.
|
||||
3. Прямые участки не выглядят как цепочка отдельных кругов.
|
||||
4. На концах нет резкого прямоугольного обрыва света.
|
||||
5. На углах отсутствуют тёмные разрывы и чрезмерно яркие круглые пятна.
|
||||
6. Свечение ленты у стены направлено в помещение и не окрашивает пол за стеной.
|
||||
7. Замкнутая прямоугольная лента даёт непрерывное поле по периметру.
|
||||
8. Цвет меняется от сущности без изменения формы, радиуса и поведения границ.
|
||||
9. При выключении поле плавно исчезает, а тонкая полоса остаётся видимой.
|
||||
10. Масштабирование плана сохраняет пропорции толщины, обводки, активной зоны и 2.5D-подъёма.
|
||||
|
||||
## 12. Проверки
|
||||
|
||||
Обязательные группы приёмки из issue #662:
|
||||
|
||||
- backend: лимиты, уникальность, конечность координат, ссылки на маркеры, экспорт/импорт;
|
||||
- unit: якорь, рендер состояний, hit-test, линейные кандидаты света, стены, auto-grid, space-card;
|
||||
- smoke: рисование, привязка/отвязка, свечение и clip-path, 2.5D, упор в стену;
|
||||
- golden: `lighting-led-strip-glow-dark`, `led-strip-off-light`, `iso-led-strip-dark`;
|
||||
- i18n: паритет ru/en/de/fr;
|
||||
- performance: benchmark десяти лент по пять точек.
|
||||
|
||||
## 13. Вне скоупа
|
||||
|
||||
- эффекты и адресные сегменты WLED;
|
||||
- вставка или удаление отдельных вершин;
|
||||
- отдельная высота крепления и дополнительная 3D-модель света от стены;
|
||||
- кривые Безье и произвольные гирлянды;
|
||||
- интерактивность в `houseplan-space-card`;
|
||||
- автоматическая починка лент, оказавшихся внутри стены.
|
||||
|
||||
## 14. Комплект поставки
|
||||
|
||||
- реализация модели, редактора, представления, света, 2.5D, импорта/экспорта и валидации;
|
||||
- сущность `light.demo_led_strip` в demo/golden/smoke fixtures;
|
||||
- четыре словаря i18n;
|
||||
- unit, backend, smoke, golden и mutant-проверки по issue;
|
||||
- документация и changelog RU/EN;
|
||||
- замер производительности в handoff;
|
||||
- визуальное сравнение с двумя PNG из этого архива.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 466 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 920 KiB |
@@ -14,11 +14,11 @@ to prove that the Node witness actually kills it.
|
||||
| --- | ---: | --- |
|
||||
| Performance threshold | 4 | The witness measures real browser wall-time or frame work; a pure assertion cannot prove the budget. |
|
||||
| Browser harness integrity | 4 | The mutation breaks page-error, round-trip or page-registration observation in the browser harness itself. |
|
||||
| Paint, cascade and layer composition | 26 | The invariant depends on computed CSS, SVG paint, clipping, stacking or pixels produced by Chromium. |
|
||||
| Pointer geometry and trusted interaction | 45 | The invariant depends on hit testing, pointer capture, touch/keyboard dispatch or live DOM geometry. |
|
||||
| Paint, cascade and layer composition | 30 | The invariant depends on computed CSS, SVG paint, clipping, stacking or pixels produced by Chromium. |
|
||||
| Pointer geometry and trusted interaction | 47 | The invariant depends on hit testing, pointer capture, touch/keyboard dispatch or live DOM geometry. |
|
||||
| Responsive DOM layout | 38 | The invariant depends on measured element boxes, responsive breakpoints, native/HA dialog shells or focusable target size. |
|
||||
| Custom-element and HA browser lifecycle | 99 | The invariant crosses Lit/custom-element lifecycle, browser storage/events, lazy loading or a complete HA-card state transition. |
|
||||
| **Total** | **216 / 200** | Above the guideline `mutation-gate --check` warns rather than fails (#699); each guard above it is held by its own reason in this inventory and its `because`. |
|
||||
| Custom-element and HA browser lifecycle | 100 | The invariant crosses Lit/custom-element lifecycle, browser storage/events, lazy loading or a complete HA-card state transition. |
|
||||
| **Total** | **223 / 200** | Above the guideline `mutation-gate --check` warns rather than fails (#699); each guard above it is held by its own reason in this inventory and its `because`. |
|
||||
|
||||
## Measured effect
|
||||
|
||||
@@ -91,6 +91,10 @@ The invariant depends on computed CSS, SVG paint, clipping, stacking or pixels p
|
||||
- `iso-sun-card-drops-occluders`
|
||||
- `iso-sun-flat-wedges-remain`
|
||||
- `iso-theme-dark-wall-rule-returns`
|
||||
- `led-core-coloured-under-glow`
|
||||
- `led-icon-not-suppressed`
|
||||
- `led-source-stays-round-at-anchor`
|
||||
- `led-unbound-in-view`
|
||||
- `stage3-w4-device-target-loses-44px-floor`
|
||||
- `stage3-w5-runtime-nudge-writes-storage`
|
||||
- `stage3-w6-no-borders-keeps-raised-plates`
|
||||
@@ -119,6 +123,8 @@ The invariant depends on hit testing, pointer capture, touch/keyboard dispatch o
|
||||
- `furniture-shift-listeners-not-attached`
|
||||
- `furniture-wall-runtime-drops-drag-side`
|
||||
- `furniture-wall-runtime-drops-raw-intent`
|
||||
- `led-pan-adds-point`
|
||||
- `led-pinch-calls-action`
|
||||
- `live-pinch-compositor-demoted-on-active-lit-commit`
|
||||
- `opening-dimension-overlay-hidden`
|
||||
- `opening-search-hides-none`
|
||||
@@ -228,8 +234,6 @@ The invariant crosses Lit/custom-element lifecycle, browser storage/events, lazy
|
||||
- `device-focus-tooltip-handler-removed`
|
||||
- `device-focus-tooltip-room-hover-overwrites`
|
||||
- `device-inbox-batch-rollback`
|
||||
- `household-enter-stops-acting`
|
||||
- `household-marker-drops-keyboard-reach`
|
||||
- `device-markers-rendered-without-keys`
|
||||
- `device-pointer-leave-clears-focus-fallback`
|
||||
- `device-position-cancel-routed-to-commit`
|
||||
@@ -240,10 +244,13 @@ The invariant crosses Lit/custom-element lifecycle, browser storage/events, lazy
|
||||
- `floor-geometry-key-global-epoch`
|
||||
- `floor-geometry-key-ignores-content`
|
||||
- `french-locale-wrong-dictionary`
|
||||
- `household-enter-stops-acting`
|
||||
- `household-marker-drops-keyboard-reach`
|
||||
- `hp-dialog-escape-does-not-close`
|
||||
- `junction-limit-baseline-cache-stale`
|
||||
- `junction-limit-candidate-fail-open`
|
||||
- `junction-limit-write-gate-removed`
|
||||
- `led-auto-slot-reserved`
|
||||
- `locale-failure-toast-dropped`
|
||||
- `marker-reject-keeps-optimistic-candidate`
|
||||
- `marker-rollback-keeps-enqueue-time-revision`
|
||||
@@ -278,16 +285,16 @@ The invariant crosses Lit/custom-element lifecycle, browser storage/events, lazy
|
||||
- `support-stale-preview-response-revives-consent`
|
||||
- `support-timeout-claims-success`
|
||||
- `vacuum-overlay-back-to-the-dock-space-filter`
|
||||
- `volumetric-kiosk-ignores-setting`
|
||||
- `view-current-space-aria-removed`
|
||||
- `volumetric-kiosk-ignores-setting`
|
||||
- `wall-face-apply-skips-overlap-guard`
|
||||
- `wallthick-hit-narrowed`
|
||||
- `warm-pending-mode-leaves-revive-waiting`
|
||||
- `warm-resume-overwrites-view-return-camera`
|
||||
- `warm-resume-camera-depends-on-dialog`
|
||||
- `warm-pan-during-runtime-keeps-refit-blocked`
|
||||
- `warm-late-resume-beats-user-mode`
|
||||
- `warm-late-resume-crosses-space`
|
||||
- `warm-pan-during-runtime-keeps-refit-blocked`
|
||||
- `warm-pending-mode-leaves-revive-waiting`
|
||||
- `warm-resume-camera-depends-on-dialog`
|
||||
- `warm-resume-overwrites-view-return-camera`
|
||||
- `writer-history-skips-finished-chain-normalization`
|
||||
- `zigbee-topology-endpoint-cleanup-skipped`
|
||||
- `zigbee-topology-endpoint-elevation-removed`
|
||||
|
||||
Reference in New Issue
Block a user