Files
houseplan-card/docs/specs/471-isometric-overlay-white-plates.md
T
2026-09-06 11:23:51 +00:00

395 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# #471 — Убрать видимые raised plates вокруг элементов изометрического View
- **Issue:** https://github.com/Matysh/houseplan-card/issues/471
- **Тип / приоритет:** bug + polish / P1
- **Трек:** полный; задача отменяет явно принятый UX-контракт Stage 3 из #160,
поэтому не проходит критерий лёгкого трека «нет нового или изменённого
UX-контракта»
- **Оценка:** пользовательская ценность 8/10; ценность для архитектуры 7/10;
сложность 4/10; риск 4/10
- **Связано:** #89, #122, #160; `docs/ISOMETRIC.md`,
`docs/adr/160-isometric-stage3-overlays.md`
## 1. Сценарий
Персона — Home admin из `docs/SCOPE.md`, который тестирует скрытый
изометрический режим в полном House Plan View на desktop, wall tablet либо
телефоне. Момент — план со множеством устройств и подписей комнат целиком
вписан в доступную область, поэтому белые подложки поднятых элементов занимают
заметную долю изображения и перекрывают архитектуру.
## 2. Что человек увидит до и после
**До:** вокруг маркеров видны дополнительные белые квадраты, а под длинными
названиями комнат — широкие белые полосы, пересекающие пол и стены.
**После:** остаются сами маркеры с их штатными компактными подложками и обычный
текст комнаты; дополнительного прямоугольного фона вокруг полного габарита
элемента нет.
## 3. Подтверждённая причина и заменяемый контракт
Артефакт является намеренной частью Stage 3, а не браузерной ошибкой:
- `ISO_RAISED_FOOTPRINT` и `isoRaisedOverlayHalfSize()` в
`src/iso-scene-render.ts` вычисляют консервативный габарит для device,
room-label и opening-lock;
- `resolveIsoOverlayPlacement()` в `src/iso-overlays.ts` проецирует этот габарит
и использует его для wall collision, nudge и tether policy;
- `renderIsoRaisedOverlays()` рисует тот же габарит как SVG polygon;
- `.iso-overlay-plate` в `src/styles/plan.styles.ts` имеет почти непрозрачную
светлую заливку `rgba(248, 249, 247, 0.9)`;
- room footprint резервирует worst-case ширину всех включённых метрик, а device
footprint — auxiliary extent, даже если фактическое содержимое короче;
- golden `demo/golden/baselines/isometric-stage3-overlays-light.png` и
contract/smoke тесты сейчас закрепляют наличие видимого polygon.
Первый коммит с поведением — `ee7d4869` из #160. D3 и AC4 исходного ТЗ #160,
ADR и текущий `docs/ISOMETRIC.md` называют эту поверхность raised plate.
#471 заменяет только визуальную часть этого решения: консервативная геометрия
остаётся невидимым collision/fit footprint, но больше не является декоративной
поверхностью. Сам raised plane, координаты и интерактивность не отменяются.
## 4. Решение владельца
В изометрическом View ни у одного поднятого overlay нет отдельной видимой
floor-parallel подложки по его общему прямоугольному габариту.
Единое правило применяется к:
1. device marker целиком;
2. room name и room metrics card;
3. opening lock marker.
Штатные собственные поверхности содержимого не относятся к удаляемой plate:
- shell/core маркера устройства;
- value badge, LQI и прочие satellite badges;
- компактная поверхность lock marker;
- focus/selection/hover affordance, определённые самим интерактивным root.
Grounding shadow и tether остаются. Консервативный footprint остаётся
невидимой расчётной геометрией для collision, nudge, fit/home и room fit.
## 5. Скоуп
1. Прекратить вывод видимых SVG plate и plate texture для всех трёх raised
overlay kinds.
2. Сохранить footprint и все потребляющие его placement/bounds алгоритмы без
числового изменения результата.
3. Развести в именовании и документации невидимый footprint и удалённую
декоративную plate, чтобы safety envelope нельзя было снова принять за
пользовательскую поверхность.
4. Удалить ставшие неиспользуемыми plate styles и overlay texture definition;
grounding shadow definition сохранить.
5. Переписать contract/unit/browser доказательства, которые сейчас требуют
видимый polygon.
6. Переснять затронутые изометрические golden только через независимое Linux
acceptance по общему процессу.
7. Актуализировать текущие внутренние документы Stage 3.
## 6. Не входит
- изменение высоты raised plane, камеры `4°/20°`, wall height или visual offset;
- изменение размеров консервативного footprint;
- новое измерение фактического DOM, `ResizeObserver` либо layout read-loop;
- изменение wall collision, owner selection, nudge direction/cap или tether
visibility rules;
- удаление grounding shadow либо tether;
- новый фон, text shadow, blur, outline или другая замена белой plate;
- редизайн shell/core, badge, room typography либо opening lock;
- изменение z-order, Zigbee topology, Glow, sunlight, vacuum и opening volume;
- новые настройки, schema/config/layout/storage поля, миграция либо i18n;
- публичное включение изометрического режима;
- изменение Flat, редакторов или `houseplan-space-card`.
Если читаемость конкретного текста без общей подложки окажется недостаточной,
альтернативный локальный text treatment требует отдельного продуктового
решения и не добавляется в #471 попутно.
## 7. Контракт невидимого footprint
Для каждого raised overlay по-прежнему вычисляются:
1. неизменная логическая floor-точка;
2. projected floor point;
3. raised point до nudge;
4. консервативный screen-facing footprint на raised plane;
5. конечный visual point и тот же footprint после nudge;
6. grounding и tether geometry.
Footprint не попадает в SVG/HTML как закрашенная или обведённая поверхность и
не создаёт собственный stacking/paint artifact. Он остаётся входом для:
- пересечения с canonical wall silhouettes и safety gap;
- bounded inward nudge;
- `nearWallBefore` / `nearWallAfter` и tether visibility;
- `isoOverlaySceneBounds()`;
- стабильного `resolveIsoOverlayFitEnvelope()` для fit/home и room fit.
Удаление декора не должно менять ни одну координату либо boolean/status
placement result на одинаковом входе. Переименование внутренних полей
`plate*` в `footprint*` предпочтительно и не является изменением модели данных.
## 8. Контракт рендера и композиции
`iso-overlays-svg` сохраняется как inert слой для grounding и tether. В нём не
должно быть `.iso-overlay-plate`, `.iso-overlay-plate-texture` либо эквивалентной
залитой/обведённой геометрии footprint.
Порядок остаётся прежним:
1. floor/live content;
2. structural walls/openings;
3. grounding/tether cues;
4. screen-facing HTML markers/labels/locks;
5. системные tooltip/dialog/chrome.
HTML roots продолжают получать один и тот же `visualScene`. Их role, tabindex,
accessible name, pointer/keyboard handlers, hit target и CSS собственных
элементов не меняются.
`layers.materialNuance` больше не создаёт overlay texture pattern, потому что
допустимой поверхности для него нет. `layers.shadows` продолжает управлять
только grounding shadow. Ошибка decorative capability не должна переводить
сцену в Flat.
## 9. Режимы, состояния и graceful degradation
- Light/dark theme используют одно правило «без plate».
- Forced colors не возвращает `fill: Canvas` вокруг overlay; собственные
нативные поверхности marker/lock продолжают существующий forced-colors
контракт.
- No-filter скрывает grounding nuance как раньше, но не меняет наличие
содержимого.
- Reduced motion ничего не меняет в конечном кадре; новой анимации нет.
- Kiosk и coarse pointer показывают тот же результат, сохраняя минимум 44×44 px
у интерактивного HTML root.
- `show_borders:false` по-прежнему полностью отключает raised placement,
grounding и tether и возвращает overlays к floor anchor.
- Flat, Plan/Devices/Background editors и static card не получают изменений DOM
либо CSS.
## 10. UX и доступность
Новых контролов, жестов, подсказок и текстов нет. Изменение только убирает
визуальный шум. Room fit/Area link, device click/context action и opening-lock
semantics остаются действующими.
Invisible footprint, grounding и tether не получают role, accessible name,
tab stop или pointer events. Focus ring принадлежит исходному HTML root и не
зависит от удалённой plate.
## 11. Модель данных, совместимость и i18n
Config, layout, backend stores, schema version, export/import и localStorage не
меняются. Миграции и downgrade converter отсутствуют.
Внутреннее переименование поля placement не сериализуется и не является
compatibility surface. Старый frontend после downgrade прочитает те же данные
и снова покажет прежнюю plate.
Новых или изменённых строк нет; словари EN/RU/DE/FR не меняются.
## 12. Производительность и lifecycle
- Structural fingerprint, LRU cap 8 и cache invalidation остаются прежними.
- HA state, hover/focus, pan/zoom и opening update не начинают перестраивать
structural scene либо читать layout.
- Удаляются до двух SVG polygon на raised overlay и один неиспользуемый shared
texture pattern; DOM/paint объём может только уменьшиться.
- Footprint/nudge computation сохраняет прежнюю сложность и кэширование.
- Initial Flat graph и alpha lazy boundary не меняются.
- Новый performance profile не нужен; существующие Stage 3 counts,
`bundle:budget` и точечный isometric smoke не должны регрессировать.
## 13. Security и privacy
Новых API, URL, persistence, HA service call или пользовательского HTML нет.
Удаляемая геометрия уже pointer/ARIA-inert; после исправления существующие
interactive roots остаются единственными владельцами действий.
## 14. Критерии приёмки
### AC1 — видимый результат (browser raster + reviewed golden)
В light и dark изометрическом View отсутствует дополнительный прямоугольный
или квадратный фон вокруг device, room label/card и opening lock. Длинное имя
комнаты при fit/home и минимальном масштабе не создаёт белую полосу через стены.
Штатные shell/core/badges/lock surfaces остаются.
### AC2 — отсутствие скрытых визуальных fallback (contract + smoke)
Raised SVG не содержит `.iso-overlay-plate`, plate texture либо эквивалентной
painted footprint geometry при material nuance on/off, forced colors и
unsupported filter. Возврат одного непрозрачного polygon делает защитный тест
красным.
### AC3 — placement parity (unit exact matrix)
На одинаковых fixtures до/после исправления совпадают floor/raised/visual
anchors, footprint points, nudge vector/distance, near-wall flags, status,
reason, tether и grounding для device, room-label и opening-lock.
### AC4 — fit и границы (unit + browser smoke)
`isoOverlaySceneBounds`, fit/home и room fit продолжают учитывать невидимый
footprint и полный screen-facing root. Конечный viewBox и позиция элементов не
меняются только из-за удаления декора; длинные/короткие имена и 0/часть/все
room metrics не клипуются.
### AC5 — interaction и touch (browser smoke)
Device hover/focus/click/context action, room click/Area link, lock action и
44×44 px target работают по прежним roots. Ground/tether/footprint не
перехватывают pointer. Kiosk/coarse pointer сохраняют View safety floor.
### AC6 — режимы и соседние слои (contract + browser smoke)
`show_borders:false` возвращает floor placement без raised subtree; Flat,
редакторы и static card не меняются. Zigbee topology endpoints продолжают
совпадать с фактическими поднятыми marker positions.
### AC7 — data/performance contract (unit + source contract)
Нет новых config/layout/storage/i18n/network/service полей. Structural
fingerprint/build count и LRU не меняются; overlay material definition count и
polygon count уменьшаются, а bundle остаётся в действующем бюджете.
### AC8 — документация и артефакты (review)
`docs/ISOMETRIC.md`, ADR #160 и исходное ТЗ #160 явно отмечают superseding
решение #471: plate как видимая поверхность удалена, footprint как расчётная
геометрия сохранён. Затронутые golden приняты независимым reviewer из полного
Linux artifact.
## 15. План автотестов и red witnesses
### Unit/contract
1. Обновить `test/iso-overlays.test.mjs`: footprint остаётся четырёхугольником,
а все placement outputs для free/near-wall/nudged/degraded/no-borders
fixtures сохраняются.
2. Обновить `test/iso-scene-render.test.mjs`: footprint по-прежнему учитывает
device auxiliaries и room metrics; scene/fit bounds используют его без
видимого renderer.
3. Обновить `test/isometric-contract.test.mjs`: запретить plate selectors,
polygons и overlay texture definition; сохранить raised root, grounding,
tether, forced-colors и 44 px contracts.
4. Проверить material definition count без orphan `hp-iso-overlay-texture`.
### Browser smoke
Расширить `demo/smoke_isometric_contract.mjs` и при необходимости
`demo/smoke_isometric_live_touch.mjs`:
- dense device set и room label с 0/частью/всеми метриками;
- отсутствие painted footprint в обычном, dark, forced-colors и no-filter;
- неизменные centres/nudge/tether/bounds;
- fit/home и room fit без клиппинга;
- pointer/keyboard/coarse-pointer actions и 44 px target;
- topology endpoint следует фактическому marker root.
### Golden
Переснять только действительно изменённые Iso Stage 3 кадры. Flat baselines
должны остаться byte/pixel unchanged. Обязателен плотный общий кадр с длинными
названиями и несколькими типами marker; возвращённая непрозрачная plate должна
создавать заметный diff.
Golden acceptance выполняет только независимый reviewer через
`npm run golden:accept -- --reviewed` на полном Linux CI artifact с provenance.
### Red witnesses
| Witness | Искусственная поломка | Обязательный красный тест |
|---|---|---|
| W1 | Вернуть один painted footprint polygon | DOM/raster smoke и Iso golden |
| W2 | Удалить footprint из wall collision | near-wall/nudge unit |
| W3 | Удалить footprint из fit bounds | long-room-label fit smoke |
| W4 | Удалить 44 px target вместе с plate | coarse-pointer smoke |
| W5 | Вернуть `fill: Canvas` plate в forced colors | forced-colors contract/smoke |
| W6 | Оставить orphan overlay texture pattern | definition-count contract |
Каждый witness запускается на минимальном тесте и полностью откатывается до
финального зелёного состояния.
## 16. Ожидаемые файлы реализации
Продукт и тесты:
- `src/iso-overlays.ts`;
- `src/iso-scene-render.ts`;
- `src/styles/plan.styles.ts`;
- `test/iso-overlays.test.mjs`;
- `test/iso-scene-render.test.mjs`;
- `test/isometric-contract.test.mjs`;
- `demo/smoke_isometric_contract.mjs`;
- при необходимости `demo/smoke_isometric_live_touch.mjs`;
- затронутые Iso golden после независимого принятия.
Документация:
- `docs/ISOMETRIC.md`;
- `docs/ARCHITECTURE.md`;
- `docs/adr/160-isometric-stage3-overlays.md`;
- `docs/specs/160-isometric-stage3.md`;
- `docs/TESTING.md` и `docs/STATUS.md` только в части действующего Stage 3
контракта/доказательств;
- этот документ и индекс `docs/specs/README.md`.
Backend, schema, translations и `src/space-card.ts` не меняются.
## 17. Release-артефакты
Изометрический Stage 3 остаётся скрытым за `hp_alpha`, поэтому по действующему
решению #160 реализация имеет `User-Visible: no` и не получает публичную запись
в `docs/CHANGELOG.md`, `docs/CHANGELOG.ru.md`, README, User Guide, HACS или
release body.
Обязательны:
- синхронные manifest-driven bundles через `npm run bundle:sync`;
- адресные unit/contract/browser tests;
- `golden:verify` с ожидаемым Iso diff и отсутствием Flat diff;
- independently reviewed Linux golden acceptance;
- `check-docs` и штатные exact-SHA beta gates;
- обновление внутренних Stage 3 документов в том же product commit.
## 18. Риски и меры
| Риск | Мера |
|---|---|
| Вместе с декором удаляется collision geometry | Отдельное имя footprint и exact placement unit matrix |
| Fit начинает клиповать длинные подписи | Footprint остаётся в scene/fit bounds; browser fit smoke |
| Исчезает штатная shell маркера | AC1 отдельно различает native surface и raised plate |
| Forced colors возвращает белые квадраты | Negative selector + forced-colors smoke |
| Tether/grounding исчезают из-за удаления всего subtree | Отдельные DOM assertions и golden |
| Golden снова закрепляет случайный фон | W1 и обязательный dense reviewed frame |
| Переименование внутреннего поля задевает cache | Structural fingerprint/build-count assertions |
| Улучшение скрытой alpha попадает в публичные notes | Явный `User-Visible: no` и отрицательный список артефактов |
## 19. Откат
Кодовый откат одного product commit возвращает декоративные polygons и styles.
Config/layout/store не меняются, поэтому миграция, восстановление данных и
действия пользователя не нужны. Немедленный пользовательский workaround —
переключиться в Flat либо отключить `hp_alpha`.
## 20. Принятые технические предположения — можно менять на ревью
1. Внутренние `plate`, `plateHalfSize`, `buildIsoPlatePolygon` и
`plateNearSilhouette` переименовываются в `footprint*`; это снижает риск снова
отрисовать safety envelope. Допустимо сохранить старые имена, если tests/docs
столь же однозначно запрещают видимую поверхность.
2. `renderIsoRaisedOverlays()` может сохранить имя и выводить только tethers;
DOM topology функции не является публичным контрактом.
3. Fit/home намеренно сохраняет прежний envelope, даже если после удаления
plate визуально можно было бы вписать план плотнее. Изменение framing — другая
UX-задача.
4. Opening-lock включён в единое правило, потому использует тот же общий
renderer; оставшийся квадрат был бы тем же артефактом.
5. Grounding shadow остаётся допустимой непрямоугольной подсказкой связи с
floor anchor и не считается заменой plate.