Compare commits

..
Author SHA1 Message Date
Sergey Matyunin d6ec494a1a docs: specify isometric toggle performance
Issue: #124
User-Visible: no
2026-08-15 03:50:34 +03:00
3 changed files with 255 additions and 434 deletions
@@ -0,0 +1,254 @@
# Issue #124 — isometric view toggle выполняет Stage 1 performance budget
- **Issue:** https://github.com/Matysh/houseplan-card/issues/124
- **Статус документа:** готово к будущей реализации; issue остаётся на `S3-spec`
- **Приоритет:** P2
- **Тип:** bug/performance, обычный трек
- **Пользовательское изменение:** нет; скрытый Labs UI и pixels сохраняются
## 1. Симптом и доказательство
В exact-SHA Full Performance для `v1.63.0-beta.1` (`3270e039…`) профиль
`large-house-isometric-v1` измерил:
- `viewToggleMs.median`: 192.8 ms;
- pre-#89 baseline: 31.7 ms;
- limit: 131.7 ms (`base + 20% + 100 ms`);
- все семь candidate samples: 189.2–213.4 ms.
Одно переиспользование wall union в экспериментальном `2576d9d` дало около
10 ms и не закрыло долг. Точная доминирующая причина не доказана. Полная
Lit/DOM смена структурных шаблонов — гипотеза, а не готовое решение.
#156 не является дублем: его regression run имел зелёный `viewToggleMs` на
другой сравнительной линии и прямо исключил #124.
## 2. Цели
1. Профилированием установить стоимость Flat → Iso и Iso → Flat.
2. Уложить canonical profile в неизменённый budget.
3. Сохранить Flat/Iso geometry, live layers, actions, fallback и viewport.
4. Не увеличить cache/heap и не перенести стоимость в соседнюю метрику.
5. Получить воспроизводимое exact-SHA доказательство закрытия исходного долга.
## 3. Не входит в задачу
- ослабление budget/noise/minimum samples;
- публичное включение изометрии;
- новая Stage 3 геометрия/материалы/камера;
- изменение Flat pixels или editors/static card;
- скрытие live layers на время toggle;
- async loading/network request;
- замена renderer architecture без измеренной причины;
- переоткрытие #89 или поглощение #156.
## 4. Канонический benchmark
`demo/benchmark_large_house.mjs --profile=large-house-isometric-v1` остаётся
authority. `viewToggleMs` включает полный user-observable цикл:
1. `_setProjection('flat')`;
2. `updateComplete`;
3. `_setProjection('iso')`;
4. `updateComplete`.
Нельзя завершить metric до painted DOM либо заменить его pure geometry timing.
Minimum — 7 samples на Linux CI с обычной environment/provenance проверкой.
Для закрытия нужны два сравнения:
- **debt closure:** candidate против pre-#89 SHA
`316ee76a2913066f8ccd86d27fdf7b17dabaff48`, который использовался как
историческая граница до merge #89;
- **current regression:** тот же exact candidate против актуального `main`/stable
обычным workflow, чтобы оптимизация не ухудшила v1.64 соседние paths.
Если automation не умеет сохранить pinned comparison artifact, допускается
manual workflow input с exact SHA и ссылка на run в review. Один зелёный push
candidate-vs-parent, где product code одинаков, не закрывает #124.
## 5. Обязательное профилирование до изменения продукта
До выбора реализации author публикует в issue/review:
- отдельные Flat commit и Iso commit timings;
- main-thread flame/trace либо эквивалентные измеренные call stacks;
- Lit `performUpdate`/template commit долю;
- geometry/cache hit/miss и `_isoScene()` build count;
- DOM node add/remove count по классам слоёв;
- layout/style/paint долю;
- минимум три главных cost centres с абсолютным временем.
Профилирование выполняется на production bundle и той же fixture. Dev build,
один sample или ручное ощущение не являются доказательством.
Diagnostic instrumentation:
- живёт в benchmark/harness либо за test-only hook;
- не создаёт production console noise;
- не меняет timing path в финальном closure run;
- удаляется либо остаётся как дешёвый выключенный counter с documented owner.
## 6. Допустимые направления оптимизации
Конкретное направление выбирается только по §5. Допустимы:
- стабилизация shared DOM subtree и переключение минимального structural layer;
- reuse уже вычисленной scene/union/index geometry;
- разделение structural и live templates, чтобы HA layers не пересоздавались;
- memoization чистых template inputs с bounded identity;
- уменьшение синхронного layout/measurement после projection commit;
- использование части `2576d9d`, если trace подтверждает её вклад.
Запрещено:
- держать два полноценных интерактивных дерева, если это дублирует tab order,
actions, ids или нарушает heap budget;
- CSS screenshot/bitmap вместо живого плана;
- пропуск `updateComplete`/paint в benchmark;
- debounce, который делает UI быстрым только до позднего тяжёлого commit;
- global cache без лимита;
- UA-specific fast path.
## 7. Render и DOM invariants
- Labs off/expired: iso DOM, geometry work, storage/network path отсутствуют;
- Flat остаётся reference и не получает Stage 2 DOM/classes/filters;
- editors и `houseplan-space-card` всегда Flat;
- Iso использует одну canonical scene/projection snapshot;
- room fills/hover, Glow/spill, sun, decor/backdrop, vacuum, devices, labels и
opening live panels сохраняются;
- DOM/tab order и actions совпадают по semantic targets;
- no duplicate ids, focus targets или pointer consumers;
- toggle сохраняет logical center и zoom по #89;
- failure latch и explicit retry остаются работоспособными.
Если shared subtree остаётся смонтированным между projections, его inertness,
ARIA и pointer policy должны быть доказаны, а не основаны только на opacity.
## 8. Cache и lifecycle
- `_isoGeometryCache` cap остаётся 8;
- `cacheGrowth.isoGeometry` после profile — 0;
- repeated Flat ↔ Iso не добавляет новые entries при том же fingerprint;
- HA-only state update не rebuild-ит structural geometry;
- theme/locale/live state не меняют geometry key;
- config geometry change инвалидирует правильную scene;
- Labs expiry/removal немедленно делает effective Flat и не удерживает active
iso DOM вне bounded cache;
- warm remount не усыновляет viewport другой projection.
## 9. Соседние performance budgets
Оптимизация не считается успешной, если `viewToggleMs` зелёный, но ухудшены:
- model ready / first stable render;
- space switch;
- HA state update;
- pan/zoom и resize preview;
- switch cycle;
- long-task count/max/total;
- heap growth;
- Flat `large-house-v1`.
Все лимиты читаются из текущих versioned budget files; hardcoded threshold в
production/test не добавляется.
## 10. Touch, accessibility и actions
- projection toggle остаётся hidden Labs control;
- touch/kiosk gestures имеют прежний safety contract;
- активный dialog/tooltip/focus не дублируется между trees;
- tap/long press на room/device/opening вызывает тот же outcome в Flat/Iso;
- locks/security и service target не меняются;
- keyboard tab order не получает скрытых duplicate nodes;
- reduced motion не относится к мгновенному projection toggle и не меняет
benchmark semantics.
## 11. Acceptance criteria
1. Profiling evidence на production fixture публикует измеренную root cause.
2. Pinned pre-#89 exact comparison зелёный по неизменённому `viewToggleMs` budget
с ≥7 samples.
3. Current-main exact comparison также зелёный.
4. Все остальные timing/long-task/heap/cache checks профиля зелёные.
5. Flat profile остаётся в budget.
6. `isoGeometry` cap/growth равны 8/0, repeated toggle — cache hit.
7. Flat/Iso golden и live/touch/action smokes зелёные без несвязанной
переакцептации.
8. Labs off не создаёт iso work/DOM.
9. HA state update не rebuild-ит geometry и не переносит cost из toggle.
10. Viewport/fallback/warm-remount contract #89/#122 не регрессирует.
## 12. План тестирования
### Unit/source contract
- cache key/cap/hit/invalidation;
- effective projection/fallback;
- shared DOM ownership и отсутствие duplicate consumers;
- Labs-off source contract;
- logical center conversion unchanged.
### Browser smoke
- repeated Flat ↔ Iso с layer/action parity;
- room/device/opening/vacuum interactions;
- Glow/sun/room fills remain mounted and state-stable;
- theme/state update during/after toggle;
- editor round-trip always Flat;
- kiosk/touch and warm remount;
- injected geometry exception and explicit retry.
### Golden
- все existing Flat и Iso baselines verify;
- никакой baseline не принимается только ради performance fix;
- если measured solution намеренно меняет DOM без pixels, expected diff = 0.
### Performance
- local 1-sample diagnostic допустим только для итерации;
- closure evidence — два Linux exact-SHA runs по §4, ≥7 samples;
- artifacts содержат raw samples, summaries, provenance и cache counters.
## 13. План реализации
1. Добавить/выполнить profiling instrumentation и опубликовать вывод.
2. Выбрать минимальный measured optimization.
3. Зафиксировать structural/live DOM и cache invariants тестами.
4. Реализовать без visual/action diff.
5. Прогнать typecheck, unit и build в цикле.
6. Перед beta прогнать targeted smoke/golden и оба performance comparison.
## 14. Документация и release-артефакты
- changelog не нужен при чистой performance-починке скрытой функции
(`User-Visible: no`);
- `docs/ISOMETRIC.md` обновляется measured architecture/root cause;
- ADR меняется, только если renderer ownership действительно изменён;
- `docs/TESTING.md`/performance README фиксируют pinned closure evidence;
- user guide и i18n не меняются;
- performance artifacts и review links обязательны;
- golden обновлять запрещено без отдельного visual rationale.
## 15. Риски и откат
| Риск | Мера |
| --- | --- |
| Cost просто отложен после metric | painted completion + trace |
| Два дерева дублируют actions | DOM/ARIA/pointer contract smoke |
| Cache растёт | cap/growth exact assertions |
| Flat подорожал | отдельный flat profile |
| Решение основано на гипотезе | profiling до product diff |
Откат возвращает прежний render path. Cache/storage schema не меняются. Если
оптимизация ломает Flat/actions, Labs kill switch немедленно даёт effective Flat,
но это mitigation, а не критерий принятия красного кода.
## 16. Принятые технические предположения
- историческая debt-граница — exact pre-#89 SHA `316ee76…`;
- benchmark продолжает измерять полный Flat → Iso → Flat/Iso user cycle;
- measured solution может не использовать `2576d9d`;
- public activation изометрии остаётся отдельной будущей задачей.
-433
View File
@@ -1,433 +0,0 @@
# Issue #148 — Tool-first редактирование комнаты в Plan Editor
- **Issue:** https://github.com/Matysh/houseplan-card/issues/148
- **Редакция:** первая редакция для независимого ревью; статус задачи определяется
только метками issue
- **Тип / приоритет:** feature + polish / P2
- **Оценка:** пользовательская ценность 8/10; ценность для разработки 7/10;
сложность и риск 8/10
- **Область:** нижняя панель Plan Editor, шесть инструментов редактирования
комнаты, выбор целей, локальные параметры, Undo/Redo, keyboard и accessibility
- **Модель данных:** сохранённый формат плана не меняется; `ToolSession` — только
runtime-состояние редактора
- **Связано:** `docs/UX-MODES.md`, `docs/CANVAS.md`, `docs/RESIZE.md`,
`docs/WALL-THICKNESS.md`, `docs/TOUCH-SUPPORT.md`
## 1. Сценарий и продуктовый контекст
**Персона:** администратор дома, который строит и исправляет план в
desktop-браузере.
**Поверхность и момент:** пользователь открывает Plan Editor и хочет выполнить
операцию над комнатой, ещё не выбрав комнату или стену.
**До → после, без терминов реализации:** сейчас состав нижней панели зависит от
того, сколько комнат уже выбрано, и заставляет сначала угадывать нужное
выделение; после изменения пользователь одним и тем же способом сначала
выбирает действие, затем требуемые объекты и только после валидного выбора
настраивает и применяет результат.
Это поддерживает J4 и J6 из `docs/SCOPE.md`: встроенный редактор становится
предсказуемым инструментом построения и последующего исправления плана.
## 2. Решение владельца и источник UX-контракта
Владелец 15.08.2026 отклонил предложенное разбиение на этапы: общий framework и
все шесть действий поставляются **одним релизом**. Каноническая запись:
https://github.com/Matysh/houseplan-card/issues/148#issuecomment-5301106841
Приложенный к issue архив `Plan Editor.zip` является UX-референсом. Его
`DECISION.md`, `UX-SPECIFICATION.md`, `WIREFRAMES.md`,
`interactive-prototype.html` и `states.html` уточняют поведение, но не являются
готовым кодом или отдельной системой стилей. При расхождении приоритет таков:
1. решения владельца в issue;
2. это ТЗ;
3. текстовые документы архива;
4. визуальная реализация прототипа.
## 3. Скоуп одного релиза
В задачу входят:
1. единый runtime lifecycle сессии инструмента;
2. стабильная группа «Редактировать комнату» с действиями «Объединить»,
«Разделить», «Размер», «Граница», «Толщина», «Удалить»;
3. перевод на tool-first контракт всех шести действий без сохранения скрытого
preselection-зависимого маршрута;
4. инструкции, счётчики, validation errors и визуальные состояния active,
hover, selected и invalid;
5. локальная панель толщины и контекстные настройки остальных инструментов;
6. единые правила отмены, переключения, фокуса, клавиатуры и Undo/Redo;
7. сохранение верхней навигации и занимаемой нижней панелью геометрии;
8. unit, browser smoke, visual golden, документация и RU/EN changelog.
Поставка только framework, Merge или Thickness при незавершённых остальных
четырёх инструментах не выполняет задачу.
## 4. Не входит в задачу
- новая верхняя навигация, постоянный правый inspector или перенос редактора в
отдельную страницу;
- новые архитектурные операции, которых сейчас нет в Plan Editor;
- изменение результата Merge/Split/Resize/Boundary/Thickness/Delete после
подтверждения, кроме необходимого выравнивания их UX lifecycle;
- изменение моделей комнат, стен, проёмов, перегородок, колонн и HA Areas;
- миграция, schema version или backend API;
- переход Devices/Background editor на новый lifecycle;
- полноценный touch-first Plan Editor: редактор остаётся desktop-first, при
обязательном safety floor из раздела 11;
- изменение View, киоска и верхней segmented navigation;
- новый верхнеуровневый режим «Удаление».
## 5. Информационная архитектура нижней панели
### 5.1. Стабильные группы
В Plan сохраняются существующая верхняя навигация и общий каркас нижней панели.
Панель предоставляет стабильные группы:
- «Выбрать»;
- «Редактировать комнату»;
- «Контур комнаты»;
- «Конструкции»;
- «Проём».
Внутри «Редактировать комнату» состав не меняется от выделения на холсте:
1. Объединить;
2. Разделить;
3. Размер;
4. Граница;
5. Толщина;
6. Удалить.
Группа доступна без предварительного выбора комнаты. Условия конкретной
операции показывает активная сессия, а не disabled-состав подпанели.
### 5.2. Шесть обзорных состояний
Требуемый комплект продуктовых состояний/скриншотов:
1. без выбора;
2. редактирование комнаты;
3. контур комнаты;
4. конструкции;
5. проём;
6. подтверждение удаления.
«Подтверждение удаления» — временное destructive-состояние внутри группы
«Редактировать комнату». Оно не добавляет шестую постоянную группу или вкладку.
### 5.3. Геометрия панели
- раскрытие группы и смена фазы не меняют размеры stage и не сдвигают холст;
- содержимое остаётся в выделенной нижней области и использует существующий
shared secondary controller;
- overflow прокручивается внутри панели; кнопка подтверждения и текущая
инструкция не уходят за viewport;
- локальная плавающая панель может перекрывать stage, но не участвует в layout.
## 6. Каноническая сессия инструмента
Реализация может уточнить имена типов, но обязана иметь один явный источник
runtime-истины со следующим смыслом:
```ts
interface ToolSession {
tool: 'merge' | 'split' | 'resize' | 'boundary' | 'wallthick' | 'delete' | null;
phase: 'idle' | 'selecting_targets' | 'validating_targets' | 'configuring' | 'applying';
targets: ToolTarget[];
anchor?: { x: number; y: number };
draft: Record<string, unknown>;
validationError?: { key: string; targetId?: string };
}
```
Обязательные инварианты:
1. `tool === null` означает отсутствие незавершённой операции;
2. тип, количество, порядок и совместимость целей задаёт активный инструмент;
3. `selectedRooms.length` и другие глобальные selection counts не определяют
состав подпанели;
4. hover не является target и не меняет конфигурацию;
5. invalid target не добавляется в `targets`;
6. `draft` и targets не пишутся в server config, localStorage или history;
7. единственная граница Undo/Redo — успешно применённая команда;
8. после apply сессия завершается и очищается до `idle`; повтор операции
требует нового явного выбора инструмента;
9. переключение space/mode, закрытие редактора и потеря актуальной цели
безопасно отменяют незавершённую сессию;
10. asynchronous validation/apply проверяют revision сессии: запоздалый ответ
отменённого или заменённого инструмента не меняет UI и конфигурацию.
Существующие разрозненные поля допустимы как внутренние детали перехода, но не
могут оставаться конкурирующими источниками lifecycle.
## 7. Контракты шести инструментов
### 7.1. Объединить
1. Клик по «Объединить» включает инструмент и показывает «Выберите две
смежные комнаты», счётчик `0 из 2`.
2. Первая валидная комната получает selected-state, счётчик становится
`1 из 2`.
3. Повторный клик по уже выбранной комнате снимает её; порядок остальных целей
сохраняется.
4. Вторая кандидатура валидируется до добавления: это другая существующая
комната того же space/floor с подходящей общей границей, не занятая
конфликтующей операцией.
5. Невалидная комната не заменяет первую и не становится третьей целью. Рядом
с подпанелью показывается конкретная причина, а кандидат получает временный
invalid-state.
6. После второй валидной комнаты автоматически открываются действующие
настройки Merge. Фокус переходит на их заголовок/первое поле.
7. Пользователь может применить, отменить всю сессию или вернуться к выбору
комнат, сохранив первую/обе валидные цели согласно явной кнопке «Назад».
8. Конфигурация и history меняются только после подтверждения.
### 7.2. Разделить
1. Инструмент просит выбрать одну существующую комнату.
2. После валидного выбора начинается действующий маршрут задания линии Split;
незавершённые точки являются `draft`, а не history-командами.
3. Начальная/конечная точки, пересечения и результирующие полигоны валидируются
по текущим правилам Split; invalid click не повреждает уже валидный draft.
4. Настройки новой комнаты открываются только после законченной валидной линии.
5. «Назад» из настроек возвращает к редактированию линии; подтверждение
применяет Split атомарно, cancel очищает его полностью.
### 7.3. Размер
1. Инструмент просит выбрать одну комнату либо допустимый её сегмент.
2. Первый валидный target включает существующие resize handles и preview.
3. Live preview хранится только в сессии; server config и соседние render paths
не видят частично применённую геометрию.
4. Настройки/контролы показываются после валидного target, а apply фиксирует
одну атомарную history-команду.
5. Cancel восстанавливает точную исходную геометрию без записи и без Undo item.
### 7.4. Граница
1. Инструмент объясняет требуемую текущей операцией цель и принимает только
допустимую общую границу/её endpoints.
2. Первая точка или boundary target переводит сессию в выбор следующей цели;
pan/pinch не подтверждает точку.
3. Невалидная цель не заменяет валидный anchor и сопровождается конкретной
причиной.
4. Конфигурация границы применяется одной подтверждённой командой. Esc во
время двухточечного выбора сначала возвращает к выбору целей по правилам
раздела 9, не создавая history.
### 7.5. Толщина
1. Инструмент показывает «Выберите стену или несколько стен».
2. Доступный atomic wall segment получает hover-state; клик добавляет/снимает
его из набора targets. Открытый span и другой невалидный объект не
добавляются.
3. После первого выбранного сегмента появляется локальная панель рядом с ним:
поле «Толщина», диапазон `1–100 см` (с действующим imperial-equivalent),
«Применить ко всем стенам комнаты» и кнопка подтверждения с галочкой.
4. При нескольких targets anchor панели — общий bbox выбранных сегментов либо
последний выбранный сегмент; выбор должен быть детерминирован.
5. Предпочтительное положение — под anchor с отступом 8–12 px. Панель
переворачивается/сдвигается, чтобы целиком остаться в card viewport, не
закрывать выбранный сегмент и критичные handles.
6. Поле может содержать draft, но стены не меняются до подтверждения. Ошибка
диапазона показывается рядом с полем и блокирует apply.
7. «Ко всем стенам комнаты» вычисляет цели от комнаты выбранного anchor в
момент подтверждения; неоднозначность shared wall решается действующим
владельцем atomic interval и не распространяется на соседнюю комнату
скрыто.
8. Одна галочка создаёт одну атомарную history-команду для всех targets.
### 7.6. Удалить
1. Инструмент просит выбрать одну комнату; hover/selected не удаляют данные.
2. После валидного target открывается встроенное состояние подтверждения с
именем комнаты и явным destructive action.
3. Красный цвет используется для финальной destructive-кнопки и invalid/error,
но не как постоянная окраска всей группы.
4. «Назад» возвращает к выбору комнаты; cancel не создаёт history.
5. Подтверждение удаляет только явно выбранную комнату существующей атомарной
командой и затем очищает сессию.
## 8. Визуальные состояния и обратная связь
- **active tool:** постоянный cyan/accent state кнопки и текстовая инструкция;
- **hover target:** лёгкий контур без изменения target count;
- **selected target:** стабильный заметный контур/заливка и текстовый счётчик;
- **invalid target:** краткий красный/not-allowed state вместе с текстовой
причиной; цвет не является единственным каналом;
- **applying:** повторное подтверждение блокируется, но отменённый устаревший
completion не может завершить новую сессию;
- status/instruction использует `aria-live="polite"`, validation error —
`aria-live="assertive"` или эквивалентный alert.
Стили берутся из текущих токенов редактора. Прототип определяет иерархию и
состояния, но не разрешает вводить параллельную палитру.
## 9. Отмена, keyboard и focus
1. Повторный клик по активному инструменту отменяет всю незавершённую сессию.
2. Переключение инструмента атомарно очищает targets, anchors, draft, preview,
validation error и локальную панель предыдущего.
3. Esc в `configuring` закрывает настройки и возвращает к `selecting_targets`
с последним валидным набором targets.
4. Esc в `selecting_targets` очищает targets и выключает инструмент.
5. Для многошагового Split/Boundary первый Esc может снять последнюю
незавершённую точку только если это явно показано пользователю; следующий
следует общему правилу отмены сессии.
6. Enter применяет только валидную configuring-фазу и никогда не подтверждает
destructive Delete без фокуса на явной кнопке.
7. После автоматического открытия настроек фокус переходит на их heading или
первое поле; после «Назад» — на active tool/instruction; после apply/cancel
— на кнопку запуска инструмента.
8. Кнопки имеют `aria-pressed`, локализованное имя и понятный disabled reason.
## 10. Модель данных, persistence и compatibility
- форматы `spaces`, `rooms`, `walls`, `open_spans`, openings и физических
объектов не меняются;
- `ToolSession`, targets, anchors и drafts не сериализуются;
- миграции, backfill и schema version нет;
- открытие и отмена любого инструмента не вызывает `_saveConfig()`;
- применённые команды используют действующий config/write contract и остаются
читаемыми предыдущей версией;
- import/export не получает новых полей;
- текущие планы открываются без materialisation и побайтовых изменений.
## 11. Touch, zoom, resilience и performance
Plan Editor остаётся desktop-first. На coarse pointer полный ergonomic parity
не требуется, но:
- второй палец, pan, pinch и pointercancel не могут подтвердить цель или apply;
- touch target controls не меньше действующего minimum;
- cancel/switch/space change не оставляет preview или заблокированный stage;
- zoom между выбором targets не меняет их identity и корректно перепривязывает
локальную панель;
- удаление target внешним config update переводит сессию в безопасную отмену с
сообщением, а не применяет операцию к другому объекту.
Hover/pointermove меняют только дешёвый preview. Полная геометрия, render model
и secondary-panel composition не пересчитываются на каждый pixel движения.
Target validation и локальная раскладка входят в существующие bounded caches;
HA state ticks не перестраивают tool session без структурной причины.
## 12. i18n
Все новые пользовательские строки добавляются одновременно в RU и EN:
- название группы и шести действий, если действующие ключи не подходят;
- инструкции каждой фазы;
- счётчики целей (`0 из 2`, `1 из 2`, `2 из 2`) через plural/parameter contract;
- конкретные validation reasons Merge и остальных инструментов;
- «Назад», «Применить», «Применить ко всем стенам комнаты»;
- accessible labels/status для панели и targets.
Нельзя собирать предложения конкатенацией или оставлять prototype-only English.
Термины сверяются с `docs/USER-GUIDE.ru.md`.
## 13. Acceptance criteria
1. «Редактировать комнату» доступно без preselection и всегда показывает все
шесть действий в заданном порядке.
2. В основной панели нет компоновок «выбрана 1 комната»/«выбраны 2 комнаты» и
ветвления состава от `selectedRooms.length`.
3. Каждое из шести действий следует фазам tool → targets → validation →
configuring → apply и не пишет draft в config/history.
4. Merge показывает `0/2` и `1/2`, не принимает третью/невалидную комнату и
автоматически открывает настройки после второй валидной.
5. Thickness допускает один/несколько atomic segments, открывает рядом панель,
валидирует `1–100 см`, поддерживает all-room и применяет только по галочке.
6. Split, Resize и Boundary сохраняют действующую геометрическую семантику, но
получают единые выбор, отмену, настройки и apply boundary.
7. Delete сначала выбирает одну комнату и показывает отдельное подтверждение;
до финальной кнопки данные не меняются.
8. Active, hover, selected и invalid различимы визуально и доступны без цвета.
9. Esc, повторный tool click, смена tool/mode/space и stale target очищают
незавершённое состояние по разделам 6 и 9.
10. Undo/Redo содержит только подтверждённые операции — ровно одну запись на
один apply независимо от числа targets.
11. Верхняя navigation не меняется, secondary content не сдвигает stage, а
локальная панель целиком остаётся в viewport при zoom и у всех краёв.
12. RU/EN, keyboard, focus, ARIA status/error и reduced-motion проходят
проверки; destructive Enter не срабатывает неявно.
13. Существующие планы и wire format не меняются; cancel не вызывает save.
14. Все шесть состояний раздела 5.2 обновлены и приняты visual review.
## 14. Проверки и доказательства
### Unit
- переходы state machine для каждого инструмента, повторного клика, switch,
Esc, apply, stale async completion и external target removal;
- target cardinality/identity и invalid-target exclusion;
- Merge same floor/shared boundary и автоматический configuring transition;
- Thickness multi-select, `1–100`, all-room и одна history-команда;
- отсутствие save/history для всех cancel paths.
### Browser smoke
- полный happy path и cancel/back path всех шести инструментов мышью;
- keyboard-only Merge, Thickness и Delete safety;
- zoom/pan во время выбора и позиционирование локальной панели у четырёх краёв;
- mode/space switch, pointercancel и structural config update;
- верхняя навигация и размеры stage до/после раскрытия групп.
### Golden
Обновляется и reviewится комплект из шести состояний раздела 5.2 минимум в
desktop theme light/dark, плюс узкие golden для:
- Merge `1 из 2`, invalid second target и configuring;
- Thickness hover, multi-selected и local panel у края;
- Delete confirmation.
Baseline меняется только после проверки, что diff соответствует ТЗ. Golden,
smoke и performance запускаются в release gate перед бетой; цикл реализации —
`typecheck`, `unit`, `build` по процессу.
## 15. Release-артефакты
В том же user-visible коммите реализации обязательны:
- `docs/CHANGELOG.md` и `docs/CHANGELOG.ru.md`;
- `docs/USER-GUIDE.ru.md` — новый порядок редактирования всех шести операций;
- `docs/UX-MODES.md` — стабильные группы и tool-first lifecycle;
- при необходимости `docs/CANVAS.md`, `docs/RESIZE.md` и
`docs/WALL-THICKNESS.md`, если реализация уточнит их взаимодействие;
- RU/EN locale files;
- принятые golden/baseline и smoke scenario либо ссылка на канонический
артефакт CI по правилам репозитория;
- `docs/TESTING.md`, если добавлен новый сценарий/gate.
Security и backend release artifacts не требуются, пока реализация не меняет
права или wire format. Если такой change окажется необходим, задача должна
вернуться к владельцу до кода.
## 16. Риски и rollback
Основные риски: два конкурирующих источника selection state, применение stale
draft, потеря Undo boundary, недоступный focus и floating panel вне viewport.
Они закрываются единой сессией, revision guard и проверками раздела 14.
Rollback — возврат UI/runtime-кода и locale/docs без миграции данных. Уже
сохранённые планы остаются совместимыми.
## 17. Принятые технические предположения
1. Shared secondary controller остаётся хозяином нижней contextual surface;
новый lifecycle расширяет его, а не создаёт второй overlay stack.
2. Existing domain helpers Merge/Split/Resize/Boundary/Thickness/Delete
сохраняются; задача унифицирует orchestration и transaction boundaries.
3. Для multi-select Thickness стабильная identity — нормализованный atomic wall
interval вместе с room/space context, а не DOM element или экранные
координаты.
4. «Заблокирована другим процессом» на первом этапе означает локальную
конфликтующую сессию/структурно устаревшую цель; новая distributed locking
модель не вводится.
5. Overview prototype state «Удаление» трактуется как подтверждение внутри
«Редактировать комнату», поскольку это одновременно сохраняет заявленную
стабильную иерархию и шесть продуктовых скриншотов.
+1 -1
View File
@@ -84,9 +84,9 @@ 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) |
| [#124](https://github.com/Matysh/houseplan-card/issues/124) Performance переключения Flat/Iso | [124-isometric-view-toggle-performance.md](124-isometric-view-toggle-performance.md) |
| [#137](https://github.com/Matysh/houseplan-card/issues/137) Узлы и линии привязки в редакторе Плана | [137-plan-snap-overlay.md](137-plan-snap-overlay.md) |
| [#141](https://github.com/Matysh/houseplan-card/issues/141) Бесшовные стыки перегородок и открытых контуров | [141-wall-junctions.md](141-wall-junctions.md) |
| [#148](https://github.com/Matysh/houseplan-card/issues/148) Tool-first редактирование комнаты | [148-tool-first-plan-editor.md](148-tool-first-plan-editor.md) |
## Правило актуализации