48 KiB
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
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
Каждое изменение должно описываться командой:
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.
Целевой контракт:
- TypeScript-схема/описание поля задаёт тип, default, диапазон, видимость и миграцию.
- Форма строится из этой схемы либо использует экспортированный field registry.
- Backend validation генерируется/сверяется машинным тестом с registry.
- Любое compatibility-поле имеет
introduced,deprecated,readUntil,migrate. - Неизвестные поля сохраняются для 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. Рекомендуемый ближайший пакет изменений
Если выбирать один компактный релиз без большой архитектурной перестройки, оптимальный состав такой:
- Удалить
aspect_ratioиз редактора static card. - Переименовать
show_temperatureв UI в «Температура и влажность»/«Значения датчиков». - Переименовать Стены в Контур комнаты до реализации свободных стен.
- Разделить контекстный Delete хотя бы на явные подтверждения с названием фактической операции.
- Добавить карточку комнаты по тапу с площадью и ссылкой на HA-зону.
- Добавить live preview трёх display-режимов в диалог устройства.
- Исправить/подтвердить Glow-clip для room override и унифицировать световые источники.
- Вынести «Обслуживание» из общих цветов в отдельный диалог.
- Добавить сохранение черновика контура или хотя бы блокирующее предупреждение при выходе.
- Начать
hp-dialogи заменить им один наиболее длинный диалог как эталон.
Этот пакет даст пользователю больше предсказуемости, чем ещё один новый визуальный эффект, и одновременно подготовит границы для последующего рефакторинга.
10. Решение по документации
Новая структура должна быть такой:
- корневой README — короткое позиционирование, установка, два YAML-примера и ссылки;
docs/USER-GUIDE.ru.md— пользовательский источник истины;- отдельные
docs/*— инженерные спецификации механизмов; CHANGELOG— только изменения релизов;- ADR/decisions — почему принято архитектурное решение, без повторения пользовательской инструкции;
- автопроверка внутренних ссылок и упоминаний удалённых опций.
При изменении поведения PR/релиз не считается завершённым, пока обновлены: пользовательская таблица поведения, enum/schema parity и сценарная матрица затронутого домена.