mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-28 19:01:34 +00:00
committed by
claude[bot]
parent
e9a148315a
commit
f5c36da648
@@ -0,0 +1,485 @@
|
||||
# Issue #137 — узлы и линии привязки в редакторе Плана
|
||||
|
||||
- **Issue:** https://github.com/Matysh/houseplan-card/issues/137
|
||||
- **Редакция:** первая редакция для независимого ревью; статус определяется только метками issue
|
||||
- **Тип / приоритет:** feature + polish / P2
|
||||
- **Оценка:** пользовательская ценность 7/10; ценность для разработки 5/10;
|
||||
сложность и риск 6/10
|
||||
- **Область:** редактор Плана, инструменты «Контур» и «Перегородка», производный
|
||||
SVG-слой архитектурных сегментов и узлов, pointer/click snap
|
||||
- **Модель данных:** без новых полей, миграции и backend-изменений
|
||||
- **Связано:** #75, #91, `docs/SCOPE.md`, `docs/TOUCH-SUPPORT.md`,
|
||||
`docs/CANVAS.md`, `docs/UX-MODES.md`
|
||||
|
||||
## 1. Сценарий и продуктовый контекст
|
||||
|
||||
**Персона:** администратор дома — основной пользователь редакторов House Plan.
|
||||
|
||||
**Поверхность:** desktop browser с мышью или точным pointer; редактор Плана.
|
||||
Touch editor остаётся best effort по `docs/TOUCH-SUPPORT.md`.
|
||||
|
||||
**Момент:** пользователь выбрал «Контур» или «Перегородка» и собирается поставить
|
||||
первую либо следующую точку нового отрезка рядом с уже нарисованной стеной,
|
||||
сохранённым открытым контуром или независимой перегородкой.
|
||||
|
||||
Задача поддерживает:
|
||||
|
||||
- **J4:** план можно точно нарисовать встроенным GUI, без SVG/Inkscape;
|
||||
- **J6:** существующую геометрию можно продолжать без микрозазоров и визуально
|
||||
неопределённых соединений.
|
||||
|
||||
## 2. Что человек увидит до и после
|
||||
|
||||
**До:** существующие стены скрывают свои точные осевые линии и координаты концов,
|
||||
поэтому следующий клик выглядит приблизительным и заранее не сообщает, соединятся
|
||||
ли отрезки.
|
||||
|
||||
**После:** поверх стен видны тонкие осевые линии и точки их концов, а endpoint или
|
||||
динамическая точка на линии увеличивается там, куда точно присоединится следующий
|
||||
клик.
|
||||
|
||||
## 3. Проблема
|
||||
|
||||
Сейчас `_snapDrawPoint()` знает только глобальную сетку и Shift/45°. Текущий
|
||||
`_renderMarkupLayer()` показывает вершины live-пути, но не даёт единого верхнего
|
||||
слоя уже сохранённой архитектуры. Основная markup-геометрия также рисуется до
|
||||
физических wall bodies и может быть ими закрыта.
|
||||
|
||||
В результате пользователь не различает три разные ситуации:
|
||||
|
||||
1. клик попадёт точно в существующий endpoint;
|
||||
2. клик соединит новый отрезок с серединой существующей стены;
|
||||
3. клик останется обычной точкой сетки рядом со стеной и оставит зазор.
|
||||
|
||||
## 4. Scope
|
||||
|
||||
В issue входят:
|
||||
|
||||
1. Производный pointer-transparent overlay только для активных инструментов
|
||||
«Контур» и «Перегородка» в редакторе Плана.
|
||||
2. Тонкие линии по фактически сплошным интервалам:
|
||||
- завершённых контуров комнат;
|
||||
- сохранённых открытых контуров (`room_drafts`);
|
||||
- независимых перегородок (`partitions`).
|
||||
3. Статические точки в реальных координатах начала и конца исходных сегментов.
|
||||
4. Endpoint-snap и line-snap для первого и последующих кликов обоих инструментов.
|
||||
5. Динамический промежуточный узел на выбранной линии.
|
||||
6. Единый resolver кандидата для hover-preview и commit клика.
|
||||
7. Светлая, тёмная и forced-colours читаемость без нового пользовательского
|
||||
параметра.
|
||||
8. Unit, production-bundle smoke, editor golden и performance-покрытие.
|
||||
9. Описание принятого snap-контракта в документации редактора.
|
||||
|
||||
## 5. Non-scope
|
||||
|
||||
В issue не входят:
|
||||
|
||||
- View, kiosk, редактор Устройств и редактор Подложки;
|
||||
- новые инструменты, режимы, кнопки, настройки или i18n-текст;
|
||||
- колонны, мебель, декор, устройства и сами проёмы как snap-кандидаты;
|
||||
- привязка к пересечению продолжений линий, центрам, касательным или
|
||||
произвольным направляющим;
|
||||
- автоматическое дробление существующей комнаты, draft или partition при
|
||||
T-соединении;
|
||||
- изменение толщины, ключей, порядка или семантики существующих сегментов;
|
||||
- исправление старой off-grid геометрии и массовая оптимизация планов;
|
||||
- полноценный hover-паритет редактора на coarse pointer;
|
||||
- новая схема, backend API, storage key, импорт/экспорт или миграция.
|
||||
|
||||
## 6. Контракт отображения
|
||||
|
||||
### 6.1 Когда слой существует
|
||||
|
||||
Overlay создаётся только когда одновременно выполняются условия:
|
||||
|
||||
- режим — редактор Плана;
|
||||
- активный инструмент — «Контур» или «Перегородка»;
|
||||
- существует текущее пространство.
|
||||
|
||||
Он виден уже до первого клика. При смене инструмента, пространства, выходе в View,
|
||||
закрытии редактора или потере pointer из сцены активный кандидат очищается; сам
|
||||
статический слой исчезает, когда инструмент больше не подходит.
|
||||
|
||||
### 6.2 Геометрия линий
|
||||
|
||||
Линии повторяют оси существующих стен и перегородок и рисуются поверх их физических
|
||||
тел. Они строятся из модели, не измеряют SVG DOM и не становятся новым источником
|
||||
геометрии.
|
||||
|
||||
Из линии и line-snap исключаются:
|
||||
|
||||
- дверные, оконные и воротные вырезы;
|
||||
- намеренно открытые `open-span` интервалы;
|
||||
- нулевые и невалидные интервалы;
|
||||
- сегменты текущего активного draft и live-preview.
|
||||
|
||||
Вырезы применяются к каноническому сегменту до отображения и hit resolution.
|
||||
Статический endpoint исходного сегмента остаётся кандидатом только если к нему
|
||||
примыкает положительный сплошной интервал. Граница проёма сама по себе не создаёт
|
||||
новый постоянный endpoint.
|
||||
|
||||
### 6.3 Геометрия точек
|
||||
|
||||
- Обычная статическая точка имеет физический радиус **5 см** в координатах плана.
|
||||
- Активный endpoint и динамический line-node имеют физический радиус **10 см** и
|
||||
дополнительный контрастный контур.
|
||||
- Совпадающие endpoints разных сегментов отображаются одной точкой.
|
||||
- Точка остаётся привязана к модели при pan/zoom; её физический размер масштабируется
|
||||
вместе с планом и не превращается в фиксированный screen-space marker.
|
||||
- Тонкая линия имеет толщину **1 CSS px** независимо от zoom.
|
||||
|
||||
Точные цветовые токены являются технической деталью, но линия, обычная точка и
|
||||
активная точка должны различаться в светлой, тёмной и forced-colours теме. Никакой
|
||||
пульсации или постоянной анимации нет.
|
||||
|
||||
### 6.4 Layering и интерактивность
|
||||
|
||||
Архитектурный overlay рисуется после room/partition wall bodies и до верхнего
|
||||
интерактивного editor chrome. Он:
|
||||
|
||||
- имеет `pointer-events:none` на группе и дочерних элементах;
|
||||
- не создаёт focusable/ARIA-содержимое;
|
||||
- не перехватывает click, pan, pinch, room card, opening и context tray;
|
||||
- не меняет hit-testing существующих инструментов.
|
||||
|
||||
## 7. Контракт выбора кандидата
|
||||
|
||||
### 7.1 Общая зона
|
||||
|
||||
Resolver использует зону захвата **12 CSS px**, переведённую в координаты текущего
|
||||
view. Поэтому зона pointer-попадания стабильна на экране и не зависит от zoom, хотя
|
||||
видимый радиус точек остаётся физическим.
|
||||
|
||||
Один и тот же чистый resolver вызывается:
|
||||
|
||||
- на pointermove — для отображения активной точки и live-preview;
|
||||
- непосредственно на click/tap — для commit, даже если до него не было pointermove.
|
||||
|
||||
Hover-состояние никогда не считается авторитетом для commit: клик повторно решает
|
||||
кандидата из своих координат и актуальной геометрии.
|
||||
|
||||
### 7.2 Приоритет и стабильность
|
||||
|
||||
Результат всегда один:
|
||||
|
||||
1. если в зоне есть существующий endpoint, выбирается ближайший endpoint;
|
||||
2. только при отсутствии endpoint выбирается ближайший сплошной сегмент;
|
||||
3. совпадающие endpoints дедуплицируются;
|
||||
4. равные расстояния разрешаются детерминированным стабильным порядком геометрии,
|
||||
не зависящим от частоты pointermove;
|
||||
5. текущий anchor исключается как кандидат нулевого отрезка.
|
||||
|
||||
Endpoint имеет приоритет, даже если другая линия формально ближе. Это делает
|
||||
визуально увеличенную точку точным обещанием следующего клика и предотвращает
|
||||
мерцание между endpoint и line-node.
|
||||
|
||||
### 7.3 Endpoint-snap
|
||||
|
||||
Если выбран endpoint:
|
||||
|
||||
- live-preview оканчивается в его точной сохранённой координате;
|
||||
- точка увеличивается до 10 см;
|
||||
- click сохраняет ровно ту же координату;
|
||||
- правило действует для первого и последующих кликов «Контура» и «Перегородки».
|
||||
|
||||
Endpoint-snap внутри активной зоны сильнее обычной сетки и Shift/45°. Вне зоны
|
||||
текущий grid-bound + Shift/45° контракт остаётся без изменений.
|
||||
|
||||
### 7.4 Line-snap и динамический узел
|
||||
|
||||
Если endpoint не выбран, но pointer находится в зоне линии:
|
||||
|
||||
1. raw pointer проецируется на ближайший сплошной сегмент;
|
||||
2. расстояние вдоль сегмента квантуется с текущим шагом сетки, отсчитанным от
|
||||
стабильного начала сегмента;
|
||||
3. итоговая точка остаётся точно на линии;
|
||||
4. на ней отображается единственный динамический узел радиусом 10 см;
|
||||
5. live-preview и click используют одну и ту же итоговую координату.
|
||||
|
||||
На диагонали wall-bound точка не обязана одновременно быть глобальным узлом по X и
|
||||
Y. Это тот же класс координат, который уже применяется для wall-bound opening и
|
||||
split points в `docs/CANVAS.md` §9.3.
|
||||
|
||||
Динамический узел — только preview. После клика существующий сегмент не дробится и
|
||||
не переписывается; новый отрезок получает endpoint в wall-bound координате, поэтому
|
||||
возникает реальное геометрическое T-соединение.
|
||||
|
||||
### 7.5 Текущий контур и валидация
|
||||
|
||||
Активный draft и live-preview исключены из общего overlay и resolver. Исключение —
|
||||
первая точка текущего контура, когда существующий сценарий разрешает его замыкание.
|
||||
Другие вершины и сегменты текущего пути не создают ответвления или self-snap.
|
||||
|
||||
Snap не обходит существующие правила:
|
||||
|
||||
- повторный click в текущий anchor остаётся no-op;
|
||||
- самопересечение, overlap, минимальное число сторон и лимиты сохраняются;
|
||||
- невалидное замыкание продолжает показывать существующий toast;
|
||||
- отмена, Undo/Redo и history boundary остаются прежними.
|
||||
|
||||
## 8. Touch, accessibility и безопасная деградация
|
||||
|
||||
**Touch editor: best effort / intentionally degraded.**
|
||||
|
||||
- Статические линии и точки могут отображаться на coarse pointer.
|
||||
- Tap непосредственно выполняет тот же resolver и может точно соединить endpoint
|
||||
или линию без предварительного hover.
|
||||
- Отдельное состояние увеличенной точки до tap на устройстве без hover не
|
||||
гарантируется.
|
||||
- Pinch, cancelled pointer и синтетический click после pan не должны создавать
|
||||
геометрию; действующий `_suppressClick`/gesture safety contract сохраняется.
|
||||
- View и kiosk не создают overlay DOM и не меняют gestures, действия или pixels.
|
||||
|
||||
Вспомогательная геометрия декоративна для accessibility: она скрыта от assistive
|
||||
technology, а новая клавиатурная навигация в editor не вводится.
|
||||
|
||||
## 9. Модель данных, совместимость и миграция
|
||||
|
||||
Новых данных нет.
|
||||
|
||||
- `rooms[].poly`, `room_drafts`, `partitions`, `walls` и `openings` сохраняют
|
||||
текущую схему;
|
||||
- mid-line snap не вставляет точку в существующий сегмент;
|
||||
- overlay/hover не пишет config, layout или local storage;
|
||||
- write происходит только в существующем commit действия «Контур» или
|
||||
«Перегородка»;
|
||||
- импорт, экспорт, downgrade и старые конфиги не требуют миграции;
|
||||
- backend validation и integration API не меняются.
|
||||
|
||||
## 10. UX и i18n
|
||||
|
||||
Новых строк, кнопок и настроек нет, поэтому i18n-ключи не добавляются.
|
||||
|
||||
`docs/USER-GUIDE.ru.md` должен объяснить одной короткой секцией:
|
||||
|
||||
- линии/точки появляются при рисовании контура или перегородки;
|
||||
- увеличенная точка показывает точное место следующего соединения;
|
||||
- endpoint имеет приоритет, а точка на линии создаёт T-соединение;
|
||||
- desktop остаётся рекомендуемой поверхностью редактора.
|
||||
|
||||
Публичное обещание не распространяется на hover-паритет touch editor.
|
||||
|
||||
## 11. Архитектурный и performance-контракт
|
||||
|
||||
1. Производная архитектурная геометрия собирается из модели и может быть вынесена
|
||||
в чистый helper; SVG DOM не является источником данных.
|
||||
2. Статический набор линий/endpoints кэшируется по структурному fingerprint либо
|
||||
эквивалентному стабильному ключу и не пересобирается на каждый pointermove.
|
||||
3. Pointermove не пишет состояние модели, config/storage и не создаёт новый массив
|
||||
SVG-узлов сверх обновления единственного active candidate.
|
||||
4. Количество статических SVG-элементов ограничено O(E): один элемент на сплошной
|
||||
интервал и один на уникальный endpoint. Динамический line-node — не более одного.
|
||||
5. Resolver может быть линейным от числа кандидатов на первом этапе, но большой
|
||||
fixture обязан пройти неизменные performance-бюджеты. Пространственный индекс
|
||||
допустим как техническая оптимизация, если не меняет контракт выбора.
|
||||
6. Cut-геометрия переиспользует канонические opening/open-span решения и не
|
||||
изобретает второй способ трактовать проёмы.
|
||||
7. Overlay не создаёт websocket, HA service, fetch, timer или внешнюю зависимость.
|
||||
|
||||
## 12. Acceptance criteria
|
||||
|
||||
- **AC1 (`unit` + `smoke`; разработчик):** только в Plan editor при активном
|
||||
«Контуре»/«Перегородке» поверх физических стен существует pointer-transparent
|
||||
overlay завершённых room contours, неактивных saved drafts и partitions; View,
|
||||
kiosk и остальные editor tools не создают его DOM.
|
||||
- **AC2 (`unit` + `golden`; разработчик):** каждый уникальный исходный endpoint с
|
||||
примыкающим сплошным интервалом показан одной точкой радиусом 5 см; линии имеют
|
||||
1 CSS px и повторяют solid intervals; active endpoint/dynamic node имеют радиус
|
||||
10 см и читаемый контур в light/dark/forced colours.
|
||||
- **AC3 (`unit` + `smoke`; разработчик):** первый и последующие клики обоих
|
||||
инструментов внутри 12 CSS px endpoint показывают один active endpoint, ведут
|
||||
live-preview в него и сохраняют его точную координату; endpoint приоритетнее
|
||||
любой линии.
|
||||
- **AC4 (`unit` + `smoke`; разработчик):** при попадании на solid line вне
|
||||
endpoint-priority отображается один динамический узел в wall-bound координате,
|
||||
квантованной вдоль линии; click создаёт точное T-соединение и не дробит/не
|
||||
переписывает существующий сегмент.
|
||||
- **AC5 (`unit`; разработчик):** совпадающие endpoints дедуплицируются, ближайший
|
||||
кандидат выбирается детерминированно, равные расстояния не мерцают, а resolver
|
||||
pointermove и resolver click возвращают одинаковый результат для одинакового
|
||||
snapshot.
|
||||
- **AC6 (`unit` + `smoke`; разработчик):** активный draft/live-preview исключён из
|
||||
общего snap; первая точка остаётся целью разрешённого замыкания, current anchor не
|
||||
создаёт нулевой сегмент, а другие точки текущего пути не создают self-branch.
|
||||
- **AC7 (`unit` + `golden`; разработчик):** door/window/gate и open-span интервалы
|
||||
отсутствуют в линии и line-snap; границы выреза не становятся постоянными
|
||||
endpoints; columns/openings/decor/devices не входят в кандидаты.
|
||||
- **AC8 (`unit` + `smoke`; разработчик):** активный endpoint/line candidate сильнее
|
||||
grid и Shift/45°, а вне 12 CSS px существующие grid-bound, Shift/45°, contour
|
||||
validity, limits и toast contracts остаются без изменений.
|
||||
- **AC9 (`unit` + code review; разработчик/ревьюер):** hover и candidate resolution
|
||||
не пишут config/layout/local storage; commit использует существующие history,
|
||||
Undo/Redo и save boundaries и добавляет только новый endpoint нового отрезка.
|
||||
- **AC10 (`smoke`; разработчик):** tap без предшествующего pointermove повторно
|
||||
решает кандидата и соединяет геометрию; pan, pinch, pointercancel и suppressed
|
||||
synthetic click не создают отрезок. Hover-паритет coarse pointer не требуется.
|
||||
- **AC11 (`smoke` + existing View golden; разработчик):** overlay не перехватывает
|
||||
pointer/focus/ARIA, не меняет View/kiosk pixels и gestures, room/opening/editor
|
||||
chrome interactions и lock/action contract.
|
||||
- **AC12 (`performance` + code review; разработчик/ревьюер):** large-house fixture
|
||||
с 60 комнатами и 60 partitions проходит overlay render и серию pointermove без
|
||||
роста cache/DOM между стабильными кадрами; существующие performance budgets не
|
||||
ослаблены, exact-SHA Full Performance перед бетой зелёный.
|
||||
- **AC13 (`unit` + backend/schema review; разработчик/ревьюер):** config schema,
|
||||
import/export, backend, storage keys, i18n и зависимости не меняются; старые планы
|
||||
читаются без миграции.
|
||||
- **AC14 (`typecheck` + `unit` + `build` + documentation review; разработчик):**
|
||||
implementation-loop gates зелёные, три bundle-копии побайтно одинаковы, оба
|
||||
changelog и пользовательская/internal документация обновлены в видимом коммите.
|
||||
|
||||
## 13. План автотестов
|
||||
|
||||
### 13.1 Unit
|
||||
|
||||
Добавить чистое покрытие сборщика и resolver:
|
||||
|
||||
1. room rectangle, polygon, saved open draft и partition дают ожидаемые segments и
|
||||
endpoints;
|
||||
2. совпадающие room/partition endpoints дедуплицируются независимо от направления;
|
||||
3. active draft, zero-length и columns исключаются;
|
||||
4. opening/open-span cuts оставляют только solid intervals и не создают endpoints
|
||||
на границах cut;
|
||||
5. endpoint выигрывает у более близкой линии в общей 12 px зоне;
|
||||
6. nearest line projection и quantisation along segment стабильны для horizontal,
|
||||
vertical и diagonal случаев;
|
||||
7. tie разрешается одинаково при перестановке pointermove и не меняет snapshot;
|
||||
8. current anchor исключён, first contour point разрешён только для closure;
|
||||
9. candidate overrides Shift, а no-candidate сохраняет существующий grid/45 output;
|
||||
10. cache/fingerprint меняется от структурной геометрии и не меняется от hover.
|
||||
|
||||
Каждый тест должен уметь падать отдельно: удаление dedup, смена приоритета,
|
||||
возвращение точки в opening gap, off-wall округление или включение active draft
|
||||
делают соответствующий тест красным.
|
||||
|
||||
### 13.2 Targeted browser smoke
|
||||
|
||||
Добавить production-bundle сценарий, например `demo/smoke_plan_snap_overlay.mjs`:
|
||||
|
||||
1. открыть Plan editor с комнатой, saved draft, partition, door и open span;
|
||||
2. переключать «Контур»/«Перегородка» и другие tools, проверяя наличие/отсутствие
|
||||
overlay и его DOM-порядок после wall bodies;
|
||||
3. pointermove к endpoint — active radius/класс, preview и commit exact coordinate;
|
||||
4. pointermove к середине horizontal и diagonal line — dynamic node, wall-bound
|
||||
quantisation и T-join без split исходного segment;
|
||||
5. доказать endpoint priority, first-click snap и Shift override;
|
||||
6. доказать отсутствие snap в opening/open-span gap и сохранение обычной grid snap;
|
||||
7. closure current first point работает, intermediate self-snap отсутствует;
|
||||
8. tap без pointermove соединяет, а pan/pinch/cancel/suppressed click не пишет;
|
||||
9. выйти в View/другой editor и убедиться, что overlay DOM исчез и pixels/actions не
|
||||
изменились.
|
||||
|
||||
Smoke пишется вместе с кодом, но полный browser-suite запускается перед бетой.
|
||||
|
||||
### 13.3 Golden
|
||||
|
||||
Добавить либо расширить editor matrix минимум двумя кадрами:
|
||||
|
||||
- light: статические линии/endpoints и active endpoint поверх толстых стен;
|
||||
- dark: dynamic mid-line node, opening/open-span gaps и T-preview поверх стены.
|
||||
|
||||
Проверяются физические размеры относительно масштаба плана, слой над wall bodies,
|
||||
контраст и отсутствие линии через вырезы. View baselines должны остаться
|
||||
pixel-identical.
|
||||
|
||||
Golden принимаются только из полного просмотренного Linux artifact через
|
||||
`npm run golden:accept -- --reviewed` с обязательными трейлерами процесса.
|
||||
|
||||
### 13.4 Performance
|
||||
|
||||
Расширить large-house harness либо добавить отдельный overlay profile:
|
||||
|
||||
- 60 rooms, 60 partitions, opening cuts и несколько saved drafts;
|
||||
- первый render статического overlay;
|
||||
- серия не менее 100 pointermove по endpoint, line и miss;
|
||||
- bounded DOM, cache cap/growth и отсутствие config/network writes;
|
||||
- сравнение exact candidate с base при неизменных budgets.
|
||||
|
||||
Локальный performance-run диагностический. Перед бетой обязательны зелёные
|
||||
performance smoke и Full Performance на точном SHA кандидата.
|
||||
|
||||
### 13.5 Backend
|
||||
|
||||
Backend не меняется. Нового backend-теста не требуется; полный Linux Validate
|
||||
остаётся release gate.
|
||||
|
||||
## 14. План реализации
|
||||
|
||||
1. Ввести чистые типы/сборщик architectural snap geometry и resolver кандидата.
|
||||
2. Подключить к нему канонические room/draft/partition segments и существующие cuts.
|
||||
3. Разделить raw pointer, resolved candidate и committed point так, чтобы click
|
||||
всегда re-resolve актуальный snapshot.
|
||||
4. Добавить верхний pointer-inert SVG overlay и theme/forced-colours styles.
|
||||
5. Добавить unit, targeted smoke, golden/performance fixtures.
|
||||
6. Обновить `docs/CANVAS.md`, `docs/USER-GUIDE.ru.md`, оба changelog и bundle-копии.
|
||||
|
||||
Имена helper-файлов и приватных полей не являются продуктовым контрактом.
|
||||
|
||||
## 15. Release-артефакты
|
||||
|
||||
Изменение пользовательское: implementation-коммиты имеют `User-Visible: yes` и в
|
||||
том же коммите обновляют:
|
||||
|
||||
- `docs/CHANGELOG.md`;
|
||||
- `docs/CHANGELOG.ru.md`;
|
||||
- `docs/USER-GUIDE.ru.md` — видимое поведение editor snap;
|
||||
- `docs/CANVAS.md` — внутренний grid-bound/wall-bound контракт;
|
||||
- три поставляемые bundle-копии.
|
||||
|
||||
Перед бетой обязательны:
|
||||
|
||||
- exact-SHA Linux Validate;
|
||||
- полный smoke-suite;
|
||||
- просмотренный и принятый Linux golden artifact;
|
||||
- performance smoke и Full Performance на точном SHA с неизменными budgets;
|
||||
- code review с отрицательным security/network/schema verdict.
|
||||
|
||||
Отдельный security report не требуется: новых внешних данных, HA calls, HTML input,
|
||||
network/storage путей нет. Публикация проходит через бету до stable.
|
||||
|
||||
## 16. Риски и меры
|
||||
|
||||
| Риск | Вероятность / влияние | Мера |
|
||||
|---|---|---|
|
||||
| Overlay закрыт толстой стеной или opening symbol | medium / high | явный DOM-order smoke и editor golden |
|
||||
| Endpoint/line меняются между hover и click | medium / high | один pure resolver, обязательный re-resolve на click |
|
||||
| T-node оказывается off-wall после grid snap | medium / high | wall-bound projection + along-segment quantisation units |
|
||||
| Opening gap остаётся кликабельной линией | medium / high | единая cut-геометрия, unit + golden gap case |
|
||||
| Active draft привязывается сам к себе | medium / high | исключение по active id, отдельный closure case |
|
||||
| Много SVG-узлов ухудшает pointermove | medium / high | structural cache, O(E) DOM, large-house performance profile |
|
||||
| Точки 5 см плохо видны далеко | medium / low | это принятый физический размер; контрастный stroke и 12 px hit tolerance |
|
||||
| Touch tap и hover расходятся | medium / medium | re-resolve непосредственно на tap, hover parity не обещается |
|
||||
| Snap обходит overlap/self-intersection | low / high | существующая validation после resolution, regression smoke |
|
||||
| Overlay попадает в View/kiosk | low / high | mode/tool guard, DOM absence smoke, unchanged View golden |
|
||||
|
||||
## 17. Откат
|
||||
|
||||
Откат — revert implementation-коммита #137 вместе с тестами, документацией,
|
||||
changelog и bundle-копиями. Persisted schema не меняется, поэтому data rollback и
|
||||
миграция не нужны; созданные новой версией endpoints остаются валидной существующей
|
||||
геометрией и читаются старой версией.
|
||||
|
||||
Если перед бетой красный только новый визуальный/performance gate, релиз блокируется:
|
||||
feature flag или ослабление budget не вводятся как аварийный обход.
|
||||
|
||||
## 18. Принятые технические предположения — можно менять без продуктового ревью
|
||||
|
||||
1. Рекомендуется новый чистый `src/plan-snap-overlay.ts` (или эквивалент) с типами
|
||||
segment/endpoint/candidate; точное имя и раскладка файлов свободны.
|
||||
2. Stable tie key может состоять из нормализованных координат, kind и persisted id;
|
||||
конкретный формат не хранится и не публикуется.
|
||||
3. Dedup использует существующую точность `samePoint` либо более строгую
|
||||
grid-compatible нормализацию; он не объединяет визуально близкие, но разные узлы.
|
||||
4. Screen tolerance вычисляется из текущего view/stage scale один раз на event;
|
||||
допустима единая isotropic scale для SVG `preserveAspectRatio`.
|
||||
5. Статический geometry cache может быть keyed существующим `_cfgEpoch` плюс
|
||||
structural fingerprint или только полным fingerprint; HA state/theme/hover в key
|
||||
не входят.
|
||||
6. Цвета берутся из существующих editor accent/contrast tokens. Можно выбрать dashed
|
||||
или solid line, если golden сохраняет однозначную непрерывность и 1 px толщину.
|
||||
7. Forced-colours реализация может использовать media query и системные цвета;
|
||||
отдельная настройка не нужна.
|
||||
8. Canonical cuts можно получать до SVG render или через чистый helper, но не через
|
||||
DOM measurement и не через второй независимый opening resolver.
|
||||
9. Unit-тесты могут держать новые helper-файлы отдельно от монолитного card test;
|
||||
browser smoke остаётся авторитетом integration/layer/click контракта.
|
||||
10. Нет открытых продуктовых вопросов: D1–D5 и Q1–Q7 приняты владельцем 2026-08-14.
|
||||
@@ -81,6 +81,7 @@ GitHub Issues и GitHub Projects (v2) остаются единственным
|
||||
| [#107](https://github.com/Matysh/houseplan-card/issues/107) Переключение виртуального источника света «Всегда» | [107-virtual-light-toggle.md](107-virtual-light-toggle.md) |
|
||||
| [#122](https://github.com/Matysh/houseplan-card/issues/122) Изометрический режим Stage 2: скрытый режим и визуальная полировка | [122-isometric-stage2.md](122-isometric-stage2.md) |
|
||||
| [#123](https://github.com/Matysh/houseplan-card/issues/123) Split из вершины не меняет наружную геометрию стен | [123-corner-split-wall.md](123-corner-split-wall.md) |
|
||||
| [#137](https://github.com/Matysh/houseplan-card/issues/137) Узлы и линии привязки в редакторе Плана | [137-plan-snap-overlay.md](137-plan-snap-overlay.md) |
|
||||
|
||||
## Правило актуализации
|
||||
|
||||
|
||||
Reference in New Issue
Block a user