Прямые и винтовые лестницы получили отдельную модель, инструменты редактора, безопасную межэтажную навигацию, вычитание из чистой площади и плоское отображение в 2.5D.
Issue: #663
User-Visible: yes
- settings.volumetric_view (General settings › Display, third switch) replaces
the alpha entry, the header projection toggle and the phone-menu item; one
rule for card, sidebar page and kiosk; editors stay Flat; backend accepts
only a boolean, support package copies it.
- Raised tiles in 2.5D View: no ring, rounded body min(0.275 D, 0.3 h), edge
0.1 D with the lab filters, ×1.12 size also in layout/collision, lift
0.075 D, one floor-shadow layer under all markers (theme × floor table),
frames hugging tile and edge (Alert > Focus > Selected > Hover), forced
colours without edge and shadow (src/iso-tiles.ts, styles/iso-tiles).
- Soft sun wash instead of projected Flat wedges (src/iso-sun.ts): same gates
and windows as sun_rays, length from elevation, parallelogram along the sun,
tone and streaks by floor lightness, sill line.
- Walls take the user's wall colour (top opaque, sides × .77/.68/.60); theme
rules for walls/openings/labels removed; furniture uses the Flat stroke rule.
- Smokes smoke_iso_tiles/iso_sun/iso_theme_walls/volumetric_setting, 16 new
mutants, acceptance scenes STAGE6_ACCEPTANCE_SCENARIOS (golden with their
Linux CI baselines), ACCEPTANCE.md side by side.
Issue: #649
User-Visible: yes
Транзитная геометрия Resize теперь только вычисляет центр для текущего кадра и не удаляет сессионную позицию. Окончательная геометрия по-прежнему очищает точку, если она действительно вышла за комнату.
Issue: #645
User-Visible: yes
Кнопка сохраняет положение в пределах комнаты только на время сессии редактора,
корректно отменяется при pinch/cancel и учитывается подписями Resize.
Issue: #645
User-Visible: yes
General settings colour tiles (room fills and glow) paint their colour at
its opacity over the checkerboard again, so "No light sources" at 0 % no
longer looks like solid grey. The label ink is chosen from the visible
colour (hex mixed with the checkerboard by alpha). Colour plates
(flat-swatch without cover-swatch) stay solid — now witnessed by the
- hp-color-opacity: one `_solidSwatch` condition drives background and
opacity; cover-swatch keeps the checkerboard
- editors/color-tile-ink.ts: pure isLightHex/colorTileInk, unit-tested
- smoke_dialog_polish_605: tileAlpha, defaultsDiffer, noneInkDark,
liveAlpha, platesFlat, plateSolid; oneSurface drops the opaque check
- mutants M-615-tile, M-615-plate, M-615-label
- USER-GUIDE (ru/en): "Colour tile" row; both changelogs
Issue: #615
User-Visible: yes
На вкладках «На плане» и «Скрытые» — флажки строк, «Выбрать все (N)» по
всему отфильтрованному набору (включая строки за «Показать ещё»), панель
«Выбрано: K · Скрыть/Показать выбранные (K) · Снять выбор» в отдельном
контейнере .device-inbox-batch вне .device-inbox-filters.
- src/device-inbox.ts: чистые inboxVisibilityAllowed (один источник для
canHide/canShow и выбора), selectableInboxRows, effectiveInboxSelection,
applyInboxVisibility — свёртка одиночных действий; одиночное = пакет из
одной строки.
- src/device-inbox-batch.ts (новый, lazy editor): выбор, панель, строка-
флажок и writeInboxVisibility — одна запись houseplan/config/set с
expected_rev на пакет, busy='__batch__' (inert), откат маркеров при отказе,
выбор сохраняется и сужается. Одиночный _setInboxHidden рантайма — тонкая
обёртка над той же записью; рантайм стал короче базы на 14 строк.
- сброс выбора при смене вкладки (клик и стрелка), поиска и «Только новые».
- i18n en/ru/de/fr: 9 ключей по ТЗ §8.
- смок demo/smoke_device_inbox_batch.mjs (AC1–AC8, AC10), юниты
test/device-inbox.test.mjs (AC2, AC4), 4 мутанта в реестре.
- docs: USER-GUIDE ru/en, FILTERING.md, оба changelog.
- monolith-baseline: hostRefs 4948→4959, bundleBytes 2500387→2505569 —
новая фича (состояние выбора и запись идут через порт хоста).
- bundle-budget: LAZY_EDITOR_GZIP_CEILING 245 400 → 246 600 (замер 245 610,
+1 190 Б gzip от базы 244 420); initial View 289 449 → 289 765 (+316 Б,
строки en-словаря), в пределах потолка.
Golden device-inbox-* изменятся ожидаемо — пересъёмка предрелизным гейтом
на Linux CI (§11.4).
Issue: #618
User-Visible: yes
План уходил base64 в WebSocket-кадре: файл больше ~3 МиБ давал кадр больше
4 МиБ, HA закрывал сокет до обработчика, и обещанные 8 МБ были недостижимы.
- бэкенд: HouseplanPlanUploadView (POST /api/houseplan/plans/upload), потоковый
предел MAX_PLAN_BYTES (read_bounded), общий writer store_plan_upload для view
и ws_plan_set (контракт WS без изменений);
- карточка: stagePlanFile/uploadPlanFile/renderPlanBackdropGuard в
backdrop-pick.ts для обоих рантаймов; PlanFilePayload хранит Blob вместо b64;
SVG больше предела — тост при выборе, растр — диалог #39 только с уменьшенной
копией, копия больше предела не попадает в staging, 413 называет предел;
- i18n toast.plan_too_large, backdrop.over_limit_body (en/ru/de/fr);
USER-GUIDE ru/en, CHANGELOG ru/en, docs/testing-notes (#617);
- тесты: test/plan-upload-limit.test.mjs, tests_backend/test_plan_upload.py,
test_ha_upload.py (#617), smoke_plan_upload_limit.mjs; три смока переведены
с b64/plan/set на blob/fetchWithAuth; мутанты plan-upload-*;
- база монолита: hostRefs +3 (общий хелпер плана вместо двух копий в рантаймах,
новый тост уменьшенной копии).
Issue: #617
User-Visible: yes
Publish corrected 0.4.1 pack, preserve old cactus objects as a read-only alias, and cover the catalog, renderers, editor, PDF and visual scene.
Issue: #606
User-Visible: yes
Make colour tiles solid, readable and fully clickable; align Data actions and device icon field with the form kit. Cover narrow Windows layouts and both icon-picker branches with a focused browser smoke.
Issue: #605
User-Visible: yes
Продолжение r1 M1: полные fill.* в RU («По силе зигби-сигнала», «По
температуре») не входили одной строкой в четыре колонки сегмента пространства
и переносились на вторую строку кнопками. Референс (§4.2 reference/app.js
fillOptions, §6.1) даёт короткие подписи Custom | Zigbee | Lights |
Temperature — они и стоят в обоих сегментах общими ключами ленивого словаря
fill.seg_* (en/ru/de/fr); room.fill_seg_* удалены. USER-GUIDE ru/en,
ACCEPTANCE.md и smoke_ux_fixes обновлены; changelog называет короткие подписи.
Три эталона меняются к лучшему и требуют повторной приёмки:
device-dialog-desktop-en и toggle-entity-dialog-desktop-en («Colour and
brightness» одной строкой вместо двух), space-room-color-popover-desktop-ru
(сегмент RU без разорванных слов); остальные десять сцен — passed на этом
дереве диагностически.
Issue: #600
User-Visible: yes
M1. Подпись сегмента больше не рвётся посреди слова: колонка `.hpf-seg label`
не сжимается ниже самого длинного слова (`min-width: fit-content`, для
браузеров без него — прежний `0` строкой выше), слова переносятся только по
пробелам (`overflow-wrap: normal`), не поместившийся сегмент переносится на
вторую строку целыми кнопками (`flex-wrap`). Новый свидетель
demo/smoke_dialog_segments_i18n.mjs — четыре диалога × en/ru/de/fr: у каждой
подписи число строк (по Range текстового узла: span — flex-элемент, у блока
один прямоугольник даже при переносе) не больше числа слов, текст внутри
кнопки. На разметке r1 краснеет на en/ru/fr комнаты («Temperature» в 2
строки, «По температуре» в 3). Мутант form-kit-segment-breaks-words
возвращает прежние две строки CSS. ACCEPTANCE.md — строка про подписи
сегмента переписана.
M2. Glow radius — не условие Save: как на dev, нечисло или неположительное
значение при сохранении молча становится общим радиусом. Удалены
glowRadiusValid, MarkerProblem marker.error_glow_radius, ключ в четырёх
локалях словаря settings, aria-invalid у поля; смок device_settings_form
утверждает обратное прежнему (badRadiusDoesNotBlockSave,
zeroRadiusKeepsSaveEnabled) и проверяет «Review N fields» на привязке в
create-режиме. Changelog EN/RU и USER-GUIDE ru/en больше не называют радиус
среди ошибок; таблица устройства говорит про молчаливый общий радиус.
M3. Свидетель дефекта №5 (поповер «?» обрезается границей диалога комнаты):
demo/smoke_dialog_help_clipping.mjs — короткое окно 900×620, диалог комнаты
скроллится, «?» нижней карточки прижат к краю; подсказка выходит за край
скроллера и остаётся hit-test'ируемой во всех пяти точках (elementsFromPoint
сквозь теневые корни — обрезанная часть не hit-test'ится). Четыре ветки:
нативный <dialog> и ha-dialog × Popover API и портальный fallback; ветка
ha-dialog — заглушка с ловушками настоящей mwc-поверхности (transform +
overflow: hidden + собственный скроллер), portal вне поверхности проверен
явно. Настоящий ha-dialog (#505, загрузка колеса HA-frontend) — отдельная
диагностическая приёмка владельца; так и записано в ACCEPTANCE.md, где
появилась таблица AC8 «девять дефектов — свидетель на каждый».
M4. ACCEPTANCE.md: эталонов 13, а не 11 — одиннадцать из ТЗ (список AC10
унаследован от #598 без комнаты) плюс две room-temperature-dialog-*.
L1. USER-GUIDE.ru.md: испорченная строка таблицы настроек пространства
(хвост удалённой строки про декоративный слой) — убран; пояснение «в
редакторе подложки остаются видимыми» вернулось в строку «Видимые слои»;
строка про солнце говорит «слой Двери, окна и ворота выключен» вместо
«Скрыть проёмы включено».
Фикстуры листа диалогов набора пересняты (панель — байт в байт).
Бюджет: initial 289 852 / 290 900; lazy editor 242 989 / 243 900.
Гейты: typecheck, build, bundle:sync (три копии), npm test 2794 pass по
четырём частям, no-new-any, mutation-gate --check, check-inputs --coverage,
смоки: segments_i18n, help_clipping, четыре формы, config_parity,
help_affordance, room_settings, settings_dialog_cards.
Issue: #600
User-Visible: yes
Завершение четырёх серий по референсу docs/design/600-settings-dialogs/:
- из eager-листов dialogs.styles.ts и chrome.styles.ts сняты стили прежней
разметки диалогов (.bindsel/.bindharow/.entcheck, .ctrlchip*, .markerhelp*,
.helpfieldlabel, .markerradios, .markerlightdisabled, .markerbadgetechnical,
.markerglowvalue, .fileupload, .descin, .pdfedit/.pdftag, .gsrow,
.ripple-*row, .radaradditional, .togglehint, футеры marker/room) — 5,8 КБ
исходника; с разметки сняты уже пустые классы;
- «Additional actions» карточки Details — подзаголовок и строка действий
набора, без раскрывашки (radar-section.ts, только additional-placement);
- Q3 уточнён по кадру: hp-device-preview стоит без обёртки — у него свой
заголовок «Display preview · Now», второй над ним удваивался; пустое
состояние — блок на тинте;
- §6.1: «?» у комнаты стоит у поля Home Assistant area, а не у заголовка
Basics; подписи сегмента заливки — None · Zigbee · Lights · Temperature ·
Custom (два коротких ключа room.fill_seg_*), источники — «Average of the
room’s sensors / Specific device or entity», режим свечения — «Colour and
brightness» (тексты прежних ключей укорочены под сегменты);
- подпись поля с «?» и без стоят на одной линии в сетке (min-height 32 по
кнопке hp-help); иконка внутри подсказки — inline;
- парные кадры: demo/capture_design_pairs_600.mjs снимает четыре диалога в
двух темах на 560 в состояниях макетов → docs/design/…/pairs/ (8 PNG,
палитра 256, ~0,5 МБ); ACCEPTANCE.md — статус по диалогам, расхождения с
референсом по каждому со ссылкой на пункт SPEC и принятые адаптации (шапка
hp-dialog по §3, тумблеры набора по К7, секции радара/пылесоса по §7.2);
- changelog EN/RU («Не выпущено»), USER-GUIDE ru/en: раздел «Как устроены
диалоги настроек», таблица настроек пространства по новым карточкам,
диалог комнаты, диалог устройства, ссылка «Как у пространства»;
- бюджет: стартовый граф 289 853 — потолок 291 600 → 290 900 (снятые стили);
ленивый редакторский 243 149 — итоговый потолок 243 900 с разбором по
сериям в bundle-budget.mjs.
Golden: 11 сцен диалогов (device-dialog-*, room-temperature-dialog-*,
toggle-entity-dialog-*, device-help-popover-light-ru,
general-color-popover-desktop-en, settings-help-zoom-200-*,
device-ripple-color-popover-mobile-ru, space-room-color-popover-desktop-ru)
прогнаны диагностически (capture, HP_ALLOW_FOREIGN_CAPTURE, только
artifacts/): харнесс проходит все пути без исключений, кадры «different» —
эталоны переснимаются на Linux CI / машине владельца по #455.
Скриншоты документации (06-device-editor, 06-device-display-preview и др.)
устарели по отпечатку — пересъёмка перед кандидатом беты (#479).
Гейты: typecheck, build, bundle:sync (три копии байт в байт), npm test
2793 pass по четырём частям, mutation-gate --check, check-inputs --coverage,
фокусные смоки четырёх форм и смоки диалога устройства.
Issue: #600
User-Visible: yes
Шаг 1 эпика #591. Набор поднят из редактора боковой панели — он сделан по
макетам дизайнера и уже живёт в продукте, — а не собран заново по прототипу.
Стили набора выдаёт генератор, параметризованный именами: контейнер формы,
префикс классов, карточки, футер и токены цвета. Это не украшение архитектуры,
а условие переезда панели на общий источник: тест с замороженной фикстурой
показывает, что с параметрами панели генератор отдаёт её нынешние правила
дословно — карточку, обводку фокуса, выключенное состояние и ряд-переключатель.
После этого переезд станет подстановкой, а не редизайном, и пиксели панели не
дрогнут. Сама панель в этом коммите не тронута.
Диалог комнаты собран в четыре карточки: «Основное», «Заливка», «Источники»,
«Размеры шрифтов». Порядок полей прежний, записи прежние — те же ключи
черновика, те же условия «Сохранить», тот же `hp-color-opacity` с атомарной
записью цвета и прозрачности. Пояснения уехали под «?» у заголовков групп
(решение владельца 18.09), выбор источника стал сегментированным
переключателем — под капотом это по-прежнему радиогруппа, поэтому стрелки и
скринридер работают как раньше.
Два измерения определили форму решения, и оба стоило сделать до кода.
Первое: лист набора в синхронном графе дал +1459 Б gzip и пробил потолок
initial View на 415 Б. Диалоги — инструмент администратора, во View их не
открывают, поэтому лист уехал в ленивый редакторский граф и вносится в теневой
корень при открытии диалога — тем же способом, которым это делает боковая
панель. Потолок не поднимался.
Второе: `css` с интерполяцией запрещён минификатором сборки (он намеренно
падает, чтобы не зависеть от рантайма), поэтому лист собирается `unsafeCSS`.
Цена — несжатые пробелы генератора, замерено 59 Б gzip; копия литералом стоила
бы расхождения с генератором, то есть единого источника.
Оставшийся рост initial View — 291 356 → 291 872 Б (+516) — это четыре текста
подсказок в двух встроенных локалях. Тексты сокращены до одного предложения:
подсказка под «?» и должна быть короткой.
`_renderRoomSource` переехал в модуль комнаты вместе со стабом карточки: у него
остался ровно один потребитель.
Issue: #594
User-Visible: yes
Пятый вариант «Отображение»: маркер показывает выбранное значение и при этом
никогда не меняет цвет — ни по состоянию, ни по тревоге, ни по недоступности,
ни под цвет RGB-света, ни по активности.
Четыре прежних режима задавали одним выбором две независимые вещи: что
нарисовано внутри маркера и красится ли он состоянием. Поэтому вместо
сравнения с одним токеном появились два производных предиката рядом с
`normalizeDeviceDisplay` — `displayWantsValue` и `displayIsNeutral`, — и их
спрашивают политика, слой пульсации, внешний бейдж, редактор и три ветки
живого слоя пылесоса.
Две ловушки, из-за которых режим не сводится к одной строке в словаре:
- быстрый путь «статичному маркеру источники не нужны» (`sourceDetails: false`)
— это основной путь рендера плана, карточки пространства и PDF. Он оставлен
только режиму без значения: иначе число пропало бы именно на плане и
осталось в предпросмотре редактора;
- три ветки живого пылесоса сравнивают режим строкой и не читают политику,
поэтому зелёная политика их не гарантирует. Свидетель держит две
конфигурации карты: сопоставленная доказывает puck и след, несопоставленная
— бейдж маршрута (при совпадающей калибровке маршрут `ready`, и бейджа не
было бы ни в одном режиме). Бейдж проверяется при наполненном буфере
позиций, иначе его отсутствие объяснялось бы первой веткой.
Потолок initial View перецентрирован 291_700 → 292_400 без изменения общего
бюджета: измеренный факт 291 346 Б оставлял под прежним центром 354 Б —
внутри шумовой полосы метрики.
Эталон: в `device-icon-state-table-{light,dark}` включённая RGB-лампа
переведена в новый режим. Кадры обязаны разойтись; приёмка — отдельным
коммитом класса D с полного линуксового артефакта Validate.
Issue: #588
User-Visible: yes
Режим заливки комнаты и её цвет — два поля конфига под одним переключателем.
«Как у пространства» снимало только режим; цвет оставался и продолжал
применяться, потому что `roomCustomFillOf` отдавал цвет комнаты независимо от
того, чей режим `custom` действует. Так возникало безымянное состояние «режим
наследую, цвет свой» — Cabinet на даче.
Теперь цвет комнаты участвует в раскраске только вместе с её собственным
`fill_mode: 'custom'` (одна функция — все поверхности: карточка, space-card,
PDF, черновик диалога). Диалог загружает цвет в черновик только при своём
режиме, обнуляет его при уходе с «Свой цвет» и показывает строку цвета только
под этим радио; сохранение пишет `custom_fill` только с `fill_mode: 'custom'`,
иначе удаляет — включая сироту от прежнего редактора. Чтение конфиг не
переписывает: застрявшие комнаты выздоравливают обновлением.
- `test/logic.test.mjs`: AC1 — сирота и любой чужой режим → цвет пространства
- `demo/smoke_room_settings.mjs` шаг 7: свой цвет → «Как у пространства» →
ни режима, ни цвета, во View цвет пространства; сирота открывается как
наследование, сохранение её удаляет (проверено красным на базе: 9 фактов)
- `demo/smoke_space_settings.mjs`: override с собственным режимом + сирота
- `demo/golden/harness.mjs`: `roomCustomFill` ставит комнате её режим —
кадры `lighting-custom-glow-*` не меняются
- мутант `room-orphan-colour-wins-again`
- docs: ARCHITECTURE (#56), CONFIG-COMPATIBILITY, USER-GUIDE ru/en, TESTING;
отпечаток скриншотов принят попиксельно (11 кадров)
Issue: #581
User-Visible: yes
Review pipeline (process.yml):
- concurrency moves from the workflow to the guard/review jobs and the guard
runs only for S4-spec-review / S7-code-review. Any other label used to enter
the issue's concurrency group and evict the pending review run (sample of
150 runs since 2026-09-01: 92 empty guard-only runs, 30 cancelled).
- the guard reads the issue's current labels instead of the event snapshot; a
label removed before the run starts is a withdrawn request, no comment.
- a green verdict is re-applied without calling the model when the latest
review document carries the pipeline-recorded verdict `green`/High 0 and the
tree differs from its anchor in nothing outside docs/reviews/** (#437 r4
re-reviewed an unchanged tree for 7 minutes). The verdict from
structured_output is now written into the anchor block for that purpose.
- the reviewer is pinned to the captured material SHA in the prompt; the
broken escaping in the "merge cancelled" comment (empty SHAs) is fixed.
Mutation gate: nine browser guards started with `npm run bundle:sync` although
the runner already builds the mutant bundle — a second rollup plus a
`tsc --noEmit` that fails on a non-strict mutant before the smoke even runs.
Prefix removed; `--check` refuses guards that build the bundle themselves.
Docs: SCOPE (Project v2 dropped, three editors), STATUS (#437 merged, HACS zip
automated), USER-GUIDE ru/en (static card shows live states; kiosk double tap
on free background fits all), #34 → #425 references, #367 named as closed in
bundle-budget messages, PROCESS §10.4 and AGENTS.md describe the controller.
Issue: #499
User-Visible: no
Артефакт один — scripts/sh3d-convert/dist/index.html, самодостаточный
файл, как и лендинг: раскладывается копированием, без сборщиков и CDN.
Собирается склейкой тех же модулей, которые проверяют гейты этапа 1;
относительных импортов на странице нет, поэтому она работает из любого
каталога и без сети.
Три обещания страницы проверяются тестами, а не текстом:
1. Ни одного сетевого вызова и ни одной внешней загрузки. Ищутся формы
ВЫЗОВА, а не имена API: первая редакция теста краснела на собственном
комментарии, где эти API перечислены как запрещённые. Шрифты
системные — <link> к шрифтам это внешний запрос при загрузке, а
обещание «файл не покидает браузер» должно быть буквальным. Заодно
поэтому на странице нет аналитики.
2. Конвертер внутри страницы даёт тот же результат, что модули
репозитория: код извлекается из собранного артефакта, прогоняется на
фикстуре и сравнивается с golden. Склейка, потерявшая модуль,
краснеет.
3. Собранная страница не отстала от исходников: build-page.mjs --check
сверяет закоммиченный артефакт с пересборкой.
Плюс полнота переводов: у каждой подписи есть ru и en, словари
совпадают по ключам, у каждого кода отчёта и каждого кода отказа есть
человеческий текст. Иначе пользователь увидит пустую надпись или
`not_zip`.
Отчёт показывается ДО скачивания и по этажам: сколько комнат, стен и
проёмов, какая клетка сетки, и полный список мест, где конвертер принял
решение, — спрямлённая дуга, обрезанная толщина, непривязанный проём,
пропущенный этаж без комнат. Это требование задачи: искать чужие ошибки
в готовом плане из сотен объектов хуже, чем прочитать их заранее.
Гайды (ru/en) получили раздел про импорт из Sweet Home 3D с прямой
записью, что это необязательный ярлык, а не шаг настройки: SCOPE обещает
«no external editors», и обходить это молчанием нельзя.
Свидетели этапа 2, все проверены отрицательным прогоном: добавленный в
страницу fetch, потерянный русский перевод, правка dist руками, склейка
без модуля sh3d.mjs — каждая краснит свой тест.
Гейты: npm test 1871 tests, 1870 pass, 0 fail; pytest без HA 378 passed,
3 skipped.
Осталось владельцу: раскладка на хост (из песочницы SSH недоступен) и
проверка на настоящем файле Sweet Home 3D.
Issue: #446
User-Visible: no
Light grouping and the excluded-integrations list move from hidden keys
to a Discovery-filters section on the catalog's Available tab: a toggle
(unset = on, the legacy behaviour), searchable integration chips with a
Restore-recommended reset (defaults stored as key absence), and
appear/disappear counters computed by diffing the REAL
seedHiddenBindings/buildDevices outputs — no second copy of the filter.
Saving writes settings once over the ordinary expected_rev path. Every
excluded candidate now names its integration in the catalog. Room
climate follows the same user exclusions through the single
effectiveExcludedIntegrations resolver (spec H2); explicit climate
opt-in stays stronger. The field registry passports both keys as
current supported settings.
User-Visible: yes
Issue: #44