diff --git a/docs/specs/471-isometric-overlay-white-plates.md b/docs/specs/471-isometric-overlay-white-plates.md new file mode 100644 index 00000000..1ef8a58c --- /dev/null +++ b/docs/specs/471-isometric-overlay-white-plates.md @@ -0,0 +1,394 @@ +# #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. diff --git a/docs/specs/README.md b/docs/specs/README.md index ad54cb1e..84ea7ae0 100644 --- a/docs/specs/README.md +++ b/docs/specs/README.md @@ -28,6 +28,7 @@ GitHub Issues и GitHub Projects (v2) остаются единственным | Issue | ТЗ | |---|---| +| [#471](https://github.com/Matysh/houseplan-card/issues/471) Убрать белые raised plates вокруг маркеров и названий комнат | [471-isometric-overlay-white-plates.md](471-isometric-overlay-white-plates.md) | | [#6](https://github.com/Matysh/houseplan-card/issues/6) Vacuum XCME path segments | [006-vacuum-xcme-path.md](006-vacuum-xcme-path.md) | | [#7](https://github.com/Matysh/houseplan-card/issues/7) Valetudo room outlines | [007-vacuum-valetudo-room-outlines.md](007-vacuum-valetudo-room-outlines.md) | | [#8](https://github.com/Matysh/houseplan-card/issues/8) Vacuum support docs and XCME hint | [008-vacuum-support-docs-xcme-hint.md](008-vacuum-support-docs-xcme-hint.md) |