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

24 KiB
Raw Permalink Blame History

#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.