Files
houseplan-card/docs/PRODUCT-IMPROVEMENT-PLAN.ru.md
T

397 lines
48 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# House Plan — продуктовый аудит и план улучшений
Срез: **v1.60.0**, 7 августа 2026. Документ основан на чтении исходников, локальном проходе главных пользовательских сценариев и проверке фактических состояний интерфейса. Для принятых задач в нём также фиксируется актуальный статус реализации.
## 1. Краткая оценка
House Plan уже вышел за рамки «карточки с картинкой»: это полноценный визуальный редактор с собственной моделью геометрии, живыми состояниями, светом, солнцем, файлами и совместным хранением. Сильная сторона продукта — редкая глубина моделирования дома внутри Home Assistant. Основной риск теперь не недостаток функций, а их накопление без достаточно явной информационной архитектуры.
Текущая картина:
| Область | Оценка | Почему |
|---|---|---|
| Пользовательская ценность | Высокая | План объединяет контроль, визуализацию, геометрию и контекст дома |
| Целостность визуальной модели | Выше средней | Единые статусы устройств, толстые стены, проёмы, Glow и солнце уже согласованы |
| Осваиваемость | Средняя/низкая | Скрытые жесты, длинные диалоги, контекстные действия и отсутствие встроенной легенды |
| Предсказуемость редактирования | Выше средней | Delete разделён на однозначные действия, геометрия получила общий Undo/Redo и обязательную сетку; незамкнутый контур пока теряется |
| Доступность | Ниже средней | Hover-зависимые данные, icon-only действия, нет полноценной семантики/фокус-менеджмента диалогов |
| Поддерживаемость кода | Низкая | `houseplan-card.ts` — 10 932 строки, `styles.ts` — 2 457 строк, в TypeScript около 429 употреблений `any` |
| Документационная целостность | Низкая до этого аудита | Старый README содержит поведение, уже не соответствующее коду; инженерные документы фрагментированы |
Главная рекомендация: на ближайшем цикле не расширять поверхность настроек. Сначала сделать существующую систему объяснимой, обратимой и модульной.
## 2. Принципы целевой системы
| Принцип | Практическое правило |
|---|---|
| Один термин — одно действие | «Стены» не должно означать только создание комнаты; «Удалить» не должно одновременно закрывать виртуальную стену, объединять комнаты и удалять комнату |
| Опасное действие видно заранее | До потери геометрии показывать конкретный результат, а не только общий confirm |
| Настройка живёт на правильном уровне | Глобальное, пространство, комната и устройство должны иметь одинаковую, явно показанную иерархию наследования |
| Состояние объяснимо | Любой цвет, кольцо и смена иконки имеют встроенную легенду/предпросмотр |
| Touch не хуже desktop | Всё, что доступно по hover/right click, имеет тап-эквивалент |
| Единый сеточный инвариант | Все позиционные координаты создаются на сетке; legacy/import между узлами явно исправляет «Оптимизировать планы» |
| Изменение обратимо | Редакторы используют общую историю команд, а не разрозненные локальные Undo |
| Модель и UI типизированы одной схемой | Опция не может существовать только в форме, только в типе или только в backend validation |
## 3. Нелогичные и рискованные функции
Приоритеты:
- **P0** — риск потери данных, опасного управления или блокировки основного сценария;
- **P1** — заметная логическая ошибка или регулярная пользовательская ловушка;
- **P2** — перегрузка, непоследовательность и трудная осваиваемость;
- **P3** — косметика, долг совместимости и внутренняя чистота.
### 3.1 Редактирование и геометрия
| ID | Приоритет | Наблюдение | Пользовательский эффект | Рекомендация |
|---|---|---|---|---|
| UX-01 | P0 | Незамкнутый контур существует только в памяти текущего инструмента и исчезает при переключении | Потеря длинной ручной работы без явного предупреждения | Сохранять черновик пространства на сервере или показывать диалог «Сохранить черновик / отбросить / остаться»; в перспективе реализовать уже спроектированные свободные стены |
| UX-02 | P1 | **Выполнено 2026-08-06:** контекстный Delete разделён на «Закрыть проём в границе», «Объединить комнаты» и «Удалить комнату» | Каждое действие имеет один заранее понятный результат | Delete удаляет только явно выбранную комнату; слияние и закрытие границы не запускаются из него |
| UX-03 | P1 | **Выполнено 2026-08-06:** инструмент замкнутой комнаты называется «Контур комнаты» | Название соответствует доступной геометрии | После реализации свободных стен добавить отдельный инструмент «Стена», не возвращая неоднозначное название |
| UX-04 | P1 | **Расширено 2026-08-07:** Plan и Background используют общий именованный Undo/Redo | Комнаты, проёмы, декор и трансформация картинки отменяются одинаково | Сессионный command stack хранит 50 команд; новая операция очищает redo-ветку; Ctrl+Z / Ctrl+Shift+Z / Ctrl+Y и кнопки работают в обеих панелях |
| UX-05 | P1 | **Решение владельца 2026-08-06:** координаты объектов всегда привязаны к сетке; намеренный off-grid не поддерживается | Оптимизация и редакторы больше не противоречат друг другу | Позиционный обход через `Shift` удалён. Свободные точки округляются к узлам, wall-bound объекты проецируются на стену и квантуются вдоль неё; «Оптимизировать планы» исправляет старые и импортированные данные |
| UX-06 | P2 | После UX-05 `Shift` не отключает сетку, но меняет геометрию жеста: квадрат/круг при рисовании, независимые оси при resize, свободный угол и шаг компаса | Позиционный инвариант не нарушается; контекстные исключения требуют явной подсказки | Показывать подсказку рядом с соответствующим инструментом; не использовать `Shift` для off-grid |
| UX-07 | P2 | Ручки комнатной карточки, локальные масштабы шрифтов и общий масштаб пространства образуют несколько перемножающихся уровней | Трудно понять, почему подпись слишком большая/маленькая | Оставить: общий масштаб пространства + размер названия/метрик комнаты; визуальный drag-scale либо заменить теми же числовыми полями, либо показывать итоговый коэффициент и Reset |
| UX-08 | P2 | Проём кликабелен в редакторе, но полностью инертен в просмотре; только badge замка активен | Неочевидно, где увидеть контакт и параметры двери/окна, особенно без замка | Одиночный клик в View открывает карточку статуса проёма; редактирование остаётся только в Plan |
| UX-09 | P2 | **Выполнено вместе с UX-02:** открытие участка и закрытие пунктирного участка — разные инструменты | У кнопки открытия больше нет обратного скрытого действия | Сохранять разные подписи, курсоры и hover-подсказки |
| UX-26 | P1 · выполнено | Background имел разные рамки/жесты для текста, мебели и картинки; линии/фигуры нельзя было трансформировать одинаково | Выбор объекта давал разный набор возможностей | Единый selection/transform controller: все объекты получают рамку; линия — две конечные ручки; пропорции по умолчанию, `Shift` — независимые оси |
| UX-27 | P1 · выполнено | Толщина, размер текста и заливка декора были в SVG-единицах/коэффициентах, прозрачность отсутствовала, заливка использовала цвет контура и фиксированные 25% | Масштаб пространства менял физический смысл стиля | Канонические `width_cm`, `size_cm`, `opacity`, `fill_color`, `fill_opacity`; cm/in для малых стилей, m/ft для геометрии; legacy читается без скачка и мигрируется оптимизатором |
| UX-28 | P1 · выполнено | Картинка была активна в Select и непрозрачна под всеми инструментами | Рамка перехватывала работу с декором, изображение визуально спорило с ним | Отдельный инструмент картинки; под другими инструментами opacity 0.5 и нет interaction; View/прочие редакторы 1.0; resize/rotate/numeric properties и Undo |
| UX-29 | P2 | Типы, физическая геометрия и `hp-color-opacity` вынесены, но orchestration Background пока остаётся в `houseplan-card.ts` | Корневой компонент всё ещё слишком велик и связан с pointer/render жизненным циклом | Следующий рефакторинг без изменения поведения: `decor-editor.ts`, `live-text-form.ts`, `furniture-palette.ts`; root оставляет только wiring |
| UX-30 | P2 · отложено | Пользовательские картинки декора требуют не только нового SVG-kind, но и безопасного файлового жизненного цикла | Наивное добавление создаст сироты, конфликт удаления и квот | Сначала спроектировать upload/reference/copy-on-write/delete/quota; `DecorImageTransform` уже фиксирует совместимый transform-контракт, UI не реализован |
### 3.2 Устройства и состояния
| ID | Приоритет | Наблюдение | Пользовательский эффект | Рекомендация |
|---|---|---|---|---|
| UX-10 | P1 | Карточечная настройка `show_temperature` управляет и температурой, и влажностью | Название не соответствует результату | Переименовать в «Показывать значения датчиков (° / %)»; миграция ключа не обязательна, достаточно label |
| UX-11 | P1 | «Значение вместо иконки» показывает только число и молча возвращает иконку для текстовых состояний | Пользователь выбирает value, но не видит значение `open/playing/home` | В форме показывать live preview и ограничение; в будущем поддержать локализованный текст состояния с ограничением длины |
| UX-12 | P1 | **Выполнено:** `controls`, «Это источник света» и автоматические `light.*` сведены в `resolvedLightSources(room)` | Glow, заливка «Свет», карточка комнаты, маркер и групповой toggle используют один набор | Сохранить единый resolver при добавлении новых световых представлений |
| UX-13 | P1 | Вероятная несогласованность: Glow clip строится по всем комнатам открытой зоны, не фильтруя локальный `fill_mode: none` | Комната, явно исключённая из тёмной заливки, может всё равно получить световое пятно | Добавить сценарный тест и фильтровать Glow-зону по эффективному `roomFillModeOf`; отдельно решить, должен ли свет физически заходить в opt-out комнату |
| UX-14 | P2 | Жёлтая подложка и кольцо `running` одновременно сообщают работу | В «Иконка + активность» один смысл дублируется двумя эффектами | Сохранить подложку как статус, а кольцо работы сделать более спокойным или показывать только переходы/события; дать предпросмотр |
| UX-15 | P2 | Система красный/жёлтый/оранжевый/бледный/кольцо не имеет доступной легенды в самой карточке | Поведение стройное в коде, но невидимо для пользователя | Кнопка «Легенда состояний» в Device editor и компактная справка в диалоге display с примерами текущего устройства |
| UX-16 | P2 | Автофильтрация скрывает устройства по эвристикам, а причина скрытия не хранится/не показывается | Пользователь не понимает, почему устройство пропало | «Входящие устройства»: Новые / Скрытые автоматически / Скрытые вручную, с причиной и массовыми действиями |
| UX-17 | P2 | Правила иконок — длинный список regex в no-code интерфейсе | Мощно, но сложно и легко сломать приоритетом первой строки | Визуальный rule builder: содержит слова / домен / device class / модель; regex оставить как «Расширенный режим» |
| UX-18 | P2 | Диалог виртуального устройства показывает настройки состояния/света, которые могут не иметь источника | Лишние решения и ложные ожидания | Условно скрывать неработающие секции; для виртуального маркера оставить имя, комнату, иконку, ссылку/описание/файлы и запуск сценария |
| UX-19 | P3 | Секция называется «Manuals (PDF etc.)», но принимает изображения и TXT | Нечёткая модель | Назвать «Файлы и инструкции», показывать разрешённые форматы и лимит 50 МБ |
### 3.3 Комнаты, подсказки и настройки
| ID | Приоритет | Наблюдение | Пользовательский эффект | Рекомендация |
|---|---|---|---|---|
| UX-20 | P1 | Площадь комнаты и часть контекста доступны по hover; touch-first код намеренно отключает hover-подсказки | На планшете новая площадь фактически недоступна в View | Тап по комнате открывает компактную карточку комнаты с площадью/климатом/светом; отдельная маленькая ссылка ведёт в HA-зону |
| UX-21 | P2 | Ссылка на HA-зону спрятана в маленькой иконке рядом с названием комнаты | Низкая обнаруживаемость и маленькая touch-цель | В карточке комнаты сделать явное действие «Открыть зону в HA» |
| UX-22 | P2 | «Скрыть двери и окна» прячет символы, но сохраняет свет, солнце и контактную анимацию | Полезная, но неожиданная семантика | Назвать «Скрыть только символы проёмов» и оставить текущую поясняющую строку |
| UX-23 | P2 | Пространство смешивает источник файла, масштаб, видимость, карточки комнат, цвет, фон, солнце и заливку в одном длинном диалоге | Прокрутка, ошибки уровня настроек, тяжёлое повторное редактирование | Разделить на вкладки «Основа», «Комнаты», «Внешний вид», «Солнце»; опасное удаление вынести в отдельную зону |
| UX-24 | P2 | Общие настройки смешивают палитру, фон/солнце, обслуживание и About | Обслуживание теряется внутри визуальных настроек | Три диалога/страницы: «Внешний вид», «Солнце и окружение», «Обслуживание»; About — отдельная строка/диалог |
| UX-25 | P2 | Комната не может явно выбрать Glow, только наследовать его от пространства | При последующей смене режима пространства нельзя зафиксировать Glow для одной комнаты | Добавить явное «Glow» или заменить radio на «Наследовать / … / Glow» с объяснением поведения |
### 3.4 Навигация и доступность
| ID | Приоритет | Наблюдение | Пользовательский эффект | Рекомендация |
|---|---|---|---|---|
| A11Y-01 | P1 | Настройка пространства и закрытие редактора — отдельные click-target внутри основной кнопки вкладки | Непредсказуемая семантика клавиатуры/скринридера, сложные маленькие цели | Делать соседние `<button>` с собственным `aria-label`, не click-handler на вложенном `ha-icon` |
| A11Y-02 | P1 · выполнено | Все модальные окна переведены на общий `hp-dialog` | Клавиатура и скринридер получают настоящую модальную границу и заголовок | HA `ha-dialog` + нативный fallback; реализованы trap, initial focus, `Esc` и restore focus, в том числе для вложенных и сменяющих друг друга окон |
| A11Y-03 | P1 | Цвет — основной канал для working/open/alarm/LQI | Пользователи с нарушением цветоощущения теряют смысл | Добавить формы/мини-бейджи: `!`, play, open; текстовые статусы в карточке и доступные `aria-label` |
| A11Y-04 | P2 | Часть toolbar-текста исчезает на узкой ширине, остаются title-tooltip и иконка | Title недоступен на touch и плохо работает с клавиатурой | `aria-label`, долгое нажатие/Help overlay, адаптивное overflow-меню вместо безымянных иконок |
| A11Y-05 | P2 | Киоск-настройка открывается скрытым удержанием 3 секунды | Функция почти не обнаруживается | Однократный onboarding/индикатор в углу и документируемая кнопка в конфигурации карточки |
| A11Y-06 | P2 | Native `confirm()` смешан с кастомными диалогами | Разная визуальная модель и слабый контекст результата | Один компонент подтверждения с названием операции, объектом, необратимостью и безопасным default focus |
## 4. Ненужные и устаревшие функции
Не всё из таблицы нужно немедленно удалять из схемы: часть полей следует сначала мигрировать, затем оставить один-два релизных цикла только для чтения.
| Кандидат | Факт в v1.59.0-rc.1 | Решение |
|---|---|---|
| `houseplan-space-card.aspect_ratio` | Поле есть в GUI и типе, но render его не читает; холст квадратный | Удалить из GUI сейчас; один цикл принимать в YAML без эффекта, затем убрать из публичного типа |
| Карточечный `tap_action` | Помечен deprecated/ignored с v1.38.1, но остаётся в `CardConfig` | Показывать однократное предупреждение в editor/YAML diagnostics; затем удалить тип и совместимый код |
| Старый display `ripple` («только пульсация») | Уже не предлагается, читается как `icon_ripple` и мигрируется оптимизатором | Правильное решение уже принято; после достаточного окна убрать backend-значение `ripple` |
| `vacuum.room_highlight` | Есть только в типе и backend schema, фактического потребителя не найдено | Подтвердить поиском тестов/реальных конфигов; удалить или реализовать как отдельную принятую фичу, не хранить «на всякий случай» |
| `vacuum.segment_map` | Есть в типе/schema, фактического потребителя не найдено | Аналогично: миграционная заметка и удаление, если телеметрия не использует |
| `settings.show_all` | Legacy shared-toggle; текущая версия удаляет его после materialization | Завершить миграцию и убрать write/fallback после установленного окна совместимости |
| `settings.exclude_integrations` | Внутренний ключ влияет на фильтр, но публичного UI/документации нет | Либо сделать поддерживаемой advanced-настройкой с причинами, либо заменить версионированными filter rules |
| `settings.group_lights` | Влияет на группировку, но не виден пользователю | Добавить явную настройку в Device discovery либо зафиксировать always-on и удалить ключ |
| Старые `decor.text.entity/attr/unit/size/scale` | Схема принимает для миграции, новый UI их не создаёт | Оставить только мигратор; после гарантированного преобразования вынести из основной модели совместимости |
| Старые `decor.width` и `space.plan_scale` | Читаются без визуального изменения; новый UI пишет `width_cm` и `plan_scale_x/y` | Мигрировать только явной операцией «Оптимизировать планы»; не менять молча при загрузке |
| Старый `space.segments` | Backend уже удаляет ключ, стены выводятся из комнат | Удалить остаточные ссылки из публичной архитектурной документации и тестовых фикстур после окна совместимости |
| Старые README-сценарии | Описывают карточечный tap и исторические display-режимы | Новый user guide сделать источником истины; README сократить до презентации, установки и ссылки на руководство |
### Что не следует убирать
| Функция | Почему сохранить |
|---|---|
| Явная «Иконка + активность» | Позволяет пользователю выбирать степень анимации; проблема не в функции, а в отсутствии предпросмотра/легенды |
| Отдельная жёлтая подложка | Даёт единый устойчивый сигнал работы при любой заливке комнаты |
| Скрыть только символы проёмов | Полезно для чистого плана, если название объясняет сохранение физической семантики |
| Виртуальные стены | Важны для зонирования открытых пространств и распространения света |
| Оптимизация с предпросмотром | Нужна для старых и импортированных моделей; обязательная сетка применяется вместе с миграцией и нормализацией |
| Статическая карточка | Полезна для обзорных дашбордов; нужно только очистить её API |
## 5. Информационная архитектура настроек
### 5.1 Предлагаемая структура верхнего уровня
| Раздел | Содержимое |
|---|---|
| План | Пространства, контуры, стены, виртуальные границы, проёмы |
| Устройства | Входящие, видимые, скрытые; привязки, действия, состояния и иконки |
| Подложка | Изображение, декор, мебель и живой текст |
| Внешний вид | Палитры, размеры по умолчанию, заливки, стены |
| Окружение | Фон, день/ночь, север, солнце и погода |
| Обслуживание | Проверка модели, оптимизация по категориям, отчёт, undo, экспорт диагностики |
| О продукте | Версия, ссылки, журнал изменений |
### 5.2 Диалог пространства
| Вкладка | Поля |
|---|---|
| Основа | Название, источник/файл, клетка, замена/удаление файла |
| Комнаты | Границы, названия, карточка комнаты и её общий масштаб |
| Внешний вид | Цвет/прозрачность, заливка, LQI, декор, проёмы |
| Окружение | Фон, север и солнечные лучи |
### 5.3 Диалог устройства
| Вкладка | Поля | Условность |
|---|---|---|
| Привязка | Имя, HA device/entity/virtual, комната, скрытие | Всегда |
| Управление | Tap action, target, confirm, controls | Только доступные действия домена |
| Состояние | Display, live preview, climate value | Только при HA-привязке |
| Свет | Source flag, controls, radius | Только при возможном on/off источнике |
| Внешний вид | Иконка, размер, поворот, activity color/size | Activity поля только в соответствующем display |
| Пылесос | Live source, calibration, trail | Только для vacuum/координатной сущности |
| Информация | Модель, ссылка, описание, вложения | Всегда |
Форма должна показывать итог, а не внутренние поля: рядом с Display — живой пример текущего устройства; рядом с Tap action — «При клике сейчас будет…».
## 6. Целевая архитектура кода
### 6.1 Текущие узкие места
| Узкое место | Доказательство | Риск |
|---|---|---|
| Монолит карточки | `src/houseplan-card.ts`: более 11 тысяч строк | Любая правка задевает жизненный цикл, диалоги, геометрию и render одновременно |
| Монолит стилей | `src/styles.ts`: 2 457 строк | Трудно ограничить CSS конкретным компонентом и состоянием |
| Слабая типизация | Около 429 `any` в TS | Schema/UI/backend расходятся незаметно |
| Состояние как набор mutable fields | Сотни приватных полей диалогов и drag-сессий | Трудные переходы, warm remount, отмена и параллельные изменения |
| Разрозненная логика настроек | Resolver-ы в `logic.ts`, render и диалогах | Один источник считается по-разному в разных режимах |
| Две серверные записи | Config и layout сохраняются отдельно | Сложность атомарных операций, ревизий и undo |
| HTML диалоги копируются вручную | `menuwrap/dialogwrap` во многих render-методах | Нет общего focus/a11y/confirm-контракта |
### 6.2 Предлагаемое разбиение frontend
```text
src/
app/
houseplan-card.ts # только композиция и жизненный цикл
houseplan-store.ts # нормализованное состояние и серверные ревизии
command-stack.ts # undo/redo и транзакции
navigation-controller.ts # space/mode/viewport/kiosk
editors/
plan/
plan-editor.ts
plan-commands.ts
plan-toolbar.ts
devices/
device-editor.ts
device-form.ts
discovery-inbox.ts
decor/
decor-editor.ts
live-text-form.ts
furniture-palette.ts
render/
room-layer.ts
wall-layer.ts
opening-layer.ts
glow-layer.ts
sun-layer.ts
device-layer.ts
vacuum-layer.ts
settings/
schema.ts # единая типизированная модель
resolve.ts # global → space → room → device
migrations.ts
components/
hp-dialog.ts
hp-confirm.ts
hp-color-opacity.ts
hp-entity-picker.ts
hp-status-preview.ts
```
`logic.ts`, `device-visual.ts`, `sun.ts`, `vacuum.ts`, `open-spans.ts` и `wall-thickness.ts` уже показывают правильное направление: чистая доменная логика отдельно от Lit/DOM. Для Background первый этап выполнен: `src/editors/decor/types.ts`, `geometry.ts` и `hp-color-opacity.ts` отделяют модель, вычисления и общий контрол. Следующий этап — вынести orchestration и формы, не создавая второй набор жестов.
### 6.3 Command model
Каждое изменение должно описываться командой:
```ts
interface PlanCommand {
label: string;
apply(model: HousePlanModel): HousePlanModel;
invert(before: HousePlanModel): PlanCommand;
affectedSpaces: string[];
}
```
Примеры: `AddRoom`, `MoveWall`, `SplitRoom`, `MergeRooms`, `SetWallThickness`, `SetOpenSpan`, `AddOpening`, `MoveDecor`, `MoveMarker`, `OptimizeModel`.
Преимущества:
- единый Undo/Redo;
- одна транзакция config+layout;
- точный журнал «что изменится»;
- защита от случайного повторного применения;
- проще синхронизировать несколько клиентов;
- тесты проверяют команды без DOM.
### 6.4 Единая схема настроек
Нужно сократить тройное дублирование `types.ts` → форма → Voluptuous schema.
Целевой контракт:
1. TypeScript-схема/описание поля задаёт тип, default, диапазон, видимость и миграцию.
2. Форма строится из этой схемы либо использует экспортированный field registry.
3. Backend validation генерируется/сверяется машинным тестом с registry.
4. Любое compatibility-поле имеет `introduced`, `deprecated`, `readUntil`, `migrate`.
5. Неизвестные поля сохраняются для forward compatibility, но не становятся публичными настройками автоматически.
## 7. Дорожная карта
### Этап 0. Источник истины и очистка поверхности
Цель: перестать накапливать ложные настройки до следующего расширения.
| Задача | Результат |
|---|---|
| Сделать новый user guide основным | README ведёт на руководство; исторические спецификации помечены как инженерные |
| Убрать `aspect_ratio` из static GUI | UI больше не обещает неработающее поле |
| Инвентаризировать legacy/dead keys | Таблица телеметрии конфигов/миграций для `tap_action`, `ripple`, `room_highlight`, `segment_map`, `show_all` |
| Добавить schema parity check | Любая GUI-опция обязана читаться renderer-ом и приниматься backend-ом |
| Зафиксировать визуальную легенду | Один behavior spec + сценарная матрица доменов |
Критерий завершения: в визуальных редакторах нет поля, которое не влияет на результат; публичная документация не противоречит коду.
### Этап 1. Безопасность редактирования
| Задача | Результат |
|---|---|
| Черновик незамкнутого контура | Переключение режима не теряет работу |
| Разделить Delete | **Выполнено:** результат каждого действия однозначен |
| Общий Undo/Redo MVP | **Выполнено:** 50 именованных операций Plan editor обратимы |
| Единый сеточный инвариант | **Выполнено:** редакторы не создают intentional off-grid; оптимизатор чинит legacy/import |
| Карточка комнаты по тапу | Площадь и контекст доступны на touch |
Критерий завершения: ни один обычный переход между инструментами не теряет данные без явного решения пользователя.
### Этап 2. Информационная архитектура и доступность
| Задача | Результат |
|---|---|
| Новый `hp-dialog` | Focus trap, Esc, aria, возврат фокуса и адаптивная высота едины |
| Разбить Space/Device/General dialogs | Меньше решений на экране, условные поля |
| Встроить status preview/legend | Display и live states понимаются до сохранения |
| Отдельные кнопки для cog/close | Корректная клавиатурная и touch-семантика |
| Нецветовые признаки состояния | Авария/работа/открыто читаются без цвета |
| Помощь по жестам | Однократный overlay для углового Shift, long-press, swipe, right click alternatives |
Критерий завершения: ключевые сценарии доступны клавиатурой и touch без hover/title; формы не требуют чтения внешней документации для понимания эффекта.
### Этап 3. Модульный frontend
| Задача | Результат |
|---|---|
| Вынести render layers | Комнаты, стены, openings, devices, Glow, Sun, Vacuum независимы |
| Вынести editor controllers | Режимы становятся явными state machines |
| Нормализованный store | Меньше прямых мутаций `_curSpaceCfg` и JSON deep-copy |
| Типизированные dialog models | Удаление основной массы `any` |
| Scoped styles/components | Стили принадлежат компонентам, меньше глобальных пересечений |
| Page objects/scenario matrix | UI-тесты описывают пользовательские действия, а не внутренние методы |
Критерий завершения: корневой `houseplan-card.ts` отвечает за композицию и имеет ориентир менее 1500–2000 строк; новая функция не требует добавлять диалог, логику и CSS в один файл.
### Этап 4. Единая семантика источников и состояний
| Задача | Результат |
|---|---|
| `resolvedLightSources` | Room fill, Glow, room card и marker status считают свет одинаково |
| `resolvedDeviceVisual` как публичный pure contract | UI, статусы и тесты используют одну матрицу |
| Текстовые value states | Значение вместо иконки работает с локализованными нечисловыми состояниями |
| Device discovery inbox | Автоскрытие объяснимо и управляемо |
| Rule builder | Regex перестаёт быть обязательным знанием |
### Этап 5. Свободные стены и незамкнутые контуры
Этот этап следует начинать только после command model и общей истории Undo. Уже сохранённое ТЗ на свободные стены остаётся самостоятельной продуктовой инициативой.
Первая версия должна:
- сохранять незамкнутый путь;
- продолжать его в той же сессии автоматически, после перезагрузки — выбором конца;
- соединять конец с концом без ответвления из середины;
- позволять менять толщину и удалять сегменты;
- при замыкании предлагать создать комнату или оставить замкнутыми стенами;
- нормализовать подряд идущие одинаковые реальные/виртуальные участки.
Не стоит в тот же релиз добавлять T-ветвления, преобразование существующих комнат в свободные стены и сложную смену типа контура: это отдельные уровни сложности.
## 8. План проверки качества после рефакторинга
### Матрицы, которые должны стать исполняемыми спецификациями
| Матрица | Измерения |
|---|---|
| Устройство | domain × device_class × state × display × live_states × controls |
| Свет | source type × hidden × is_light × fill mode × virtual/door connection |
| Стена | thickness A/B × real/virtual spans × T/corner × opening × resize |
| Проём | door/window × thin/thick wall × contact/invert × lock × hide symbols |
| Комната | with/without area × fill override × source override × island/shared wall |
| Ввод | mouse/touch/keyboard × view/editor/kiosk × zoom level |
| Миграция | every deprecated field × optimize × save × reload × older client |
### Продуктовые метрики
| Метрика | Цель |
|---|---|
| Потерянные незавершённые контуры | 0 |
| Необратимые действия редактора | 0, кроме явного удаления файла/пространства с подтверждением |
| GUI-поля без runtime consumer | 0 |
| Ключевые действия, доступные только по hover/right click | 0 |
| Ключевые цвета без альтернативного признака | 0 |
| Расхождения frontend/backend enum | 0 в CI |
| `any` в новых/переписанных модулях | 0, кроме изолированного HA adapter boundary |
| Максимальный размер feature-компонента | Ориентир <800 строк; исключения обосновываются |
## 9. Рекомендуемый ближайший пакет изменений
Если выбирать один компактный релиз без большой архитектурной перестройки, оптимальный состав такой:
1. Удалить `aspect_ratio` из редактора static card.
2. Переименовать `show_temperature` в UI в «Температура и влажность»/«Значения датчиков».
3. Переименовать **Стены** в **Контур комнаты** до реализации свободных стен.
4. Разделить контекстный Delete хотя бы на явные подтверждения с названием фактической операции.
5. Добавить карточку комнаты по тапу с площадью и ссылкой на HA-зону.
6. Добавить live preview трёх display-режимов в диалог устройства.
7. Исправить/подтвердить Glow-clip для room override и унифицировать световые источники.
8. Вынести «Обслуживание» из общих цветов в отдельный диалог.
9. Добавить сохранение черновика контура или хотя бы блокирующее предупреждение при выходе.
10. Начать `hp-dialog` и заменить им один наиболее длинный диалог как эталон.
Этот пакет даст пользователю больше предсказуемости, чем ещё один новый визуальный эффект, и одновременно подготовит границы для последующего рефакторинга.
## 10. Решение по документации
Новая структура должна быть такой:
- корневой README — короткое позиционирование, установка, два YAML-примера и ссылки;
- `docs/USER-GUIDE.ru.md` — пользовательский источник истины;
- отдельные `docs/*` — инженерные спецификации механизмов;
- `CHANGELOG` — только изменения релизов;
- ADR/decisions — почему принято архитектурное решение, без повторения пользовательской инструкции;
- автопроверка внутренних ссылок и упоминаний удалённых опций.
При изменении поведения PR/релиз не считается завершённым, пока обновлены: пользовательская таблица поведения, enum/schema parity и сценарная матрица затронутого домена.