mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-06 22:49:16 +00:00
397 lines
48 KiB
Markdown
397 lines
48 KiB
Markdown
# 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 и сценарная матрица затронутого домена.
|