Files
houseplan-card/docs/reviews/CODE-REVIEW-360-r1.md
T
2026-08-29 11:48:50 +03:00

14 KiB
Raw Blame History

CODE-REVIEW-360-r1

Issue: #360 «Основной default-цвет декора на главной панели» Этап: код-ревью (PROCESS.md §2.7) Заход: r1 · блокирующих циклов израсходовано 0/2 SHA материала ревью: f91b0ff51254fdf10aee1da56f20a307d7ea1e72 Диапазон: git diff origin/dev...HEAD (один некоммит-мёрдж коммит f91b0ff5)

Вердикт

Зелёный. High: 0, Medium: 0 (ни в скоупе, ни вне скоупа), Low: 0.

Скоуп диффа

Один продуктовый коммит f91b0ff5 feat: expose decor default color in main toolbar, трейлеры Issue: #360 / User-Visible: yes на месте.

Файлы: src/houseplan-editor-runtime.ts (+9 строк — новый постоянный hp-color-opacity в _renderDecorBar), test/color-picker.test.mjs, demo/smoke_decor.mjs, docs/DECOR-EDITOR.md, docs/USER-GUIDE.ru.md, docs/CHANGELOG.md, docs/CHANGELOG.ru.md, docs/images/07-background-editor.png

  • screenshots.json (отпечаток документации), сгенерированные dist/** и custom_components/houseplan/frontend/** (класс D, синхронны с исходником).

Диффу соответствует ровно одна цель ТЗ: сделать существующий session-default _decorStyle.color/_decorStyle.opacity видимым и управляемым с основной панели редактора подложки при любом инструменте, ничего больше не меняя. Правка не трогает геометрию, персистентную модель, i18n-ключи, миграции, touch-контракт и любые другие редакторы — заявленный не-скоуп ТЗ выдержан построчно.

Как проверялось

Зелёного Validate на f91b0ff5 нет, гейты прогнаны локально.

Гейт Статус Примечание
npx tsc --noEmit 🟢 зелёный без вывода
npm test 🟢 зелёный 1534 passed / 1 skipped / 0 failed
npm run build 🟢 зелёный git status после сборки чист — dist/** и custom_components/houseplan/frontend/** побайтово совпадают с закоммиченными (сверка проведена diff -q дополнительно)
node scripts/check-docs.mjs 🟢 зелёный «Documentation checks passed (7 files, 10 external links)» — diff трогает src/**, поэтому обязателен
node scripts/smoke-select.mjs --base origin/dev --head HEAD выполнен 3 «прямых совпадения» по символу _decorStyle: demo/smoke_decor.mjs, demo/smoke_color_picker.mjs, demo/smoke_furniture.mjs
node demo/smoke_decor.mjs 🟢 зелёный назван в тест-плане ТЗ (п.6) и это же смок расширен диффом; см. «тест умеет падать» ниже
node demo/smoke_color_picker.mjs 🟢 зелёный прямое совпадение из smoke-select
node demo/smoke_furniture.mjs 🟢 зелёный прямое совпадение из smoke-select (мебель — один из потребителей _decorStyle)
node --test test/color-picker.test.mjs 🟢 зелёный новые ассерты по decor-default-color; см. «тест умеет падать» ниже
npm run golden:verify не прогонялся ТЗ (план тестов, п.6) явно относит golden/screenshot-ревью к предрелизному гейту (AC6: «Доказательство: smoke + golden/screenshot review перед бетой»); это решение уже прошло зелёное ревью ТЗ (SPEC-REVIEW-360-r2). Локальный прогон дополнительно бессмыслен: правило §12/#13 принимает эталоны только golden:accept -- --reviewed по полному Linux-артефакту CI
npm run invariants -- --config … не прогонялся diff не трогает геометрию/layout/marker.space/open_spans/рёбра — только UI-state _decorStyle и его потребление существующими decorStylePatch/_decorSaveShape, которые сам diff не меняет
python -m pytest tests_backend -q не прогонялся custom_components/**/*.py не тронут
performance-профили не прогонялись не названы в AC, чувствительные к перфу пути (рендер геометрии, пересчёт масок) не тронуты — добавлен один статический biding в toolbar

Дисциплина «тест умеет падать»

Проверено исполнением, не только чтением: временно убран добавленный блок <hp-color-opacity class="decor-default-color" …> из _renderDecorBar (src/houseplan-editor-runtime.ts), пересобран бандл (npm run bundle:sync) — node demo/smoke_decor.mjs упал (Cannot read properties of null (reading 'dispatchEvent') на обращении к main picker). Отдельно тем же способом (без пересборки, тест читает .ts-исходник напрямую) node --test test/color-picker.test.mjs упал на новом ассерте defaultPickerStart >= 0. После проверки код и бандлы восстановлены (git checkout -- src/houseplan-editor-runtime.ts + npm run bundle:sync), git status чист.

Разбор по AC

  • AC1 (постоянная доступность). _renderDecorBar (src/houseplan-editor-runtime.ts:5324) рендерит .editbar-tools, где новый hp-color-opacity.decor-default-color безусловен — не зависит от this.host._decorTool, в отличие от контекстного picker'а в _renderDecorSecondary (условие draws, строка 5252). В коде ровно один экземпляр class="decor-default-color". Проверено чтением и смоком defaultStyleVisibleForEveryDecorTool (перебор всех 8 инструментов, включая select/erase/backdrop) — доказано.
  • AC2 (все новые типы). Смок everyNewDecorKindUsesMainDefault: после смены основного default создаются line/rect/ellipse (через _decorCommitDraft), text (через _decorPointerDown+_decorSaveText) и furniture (через _furnPlace) — все получают записанный color/opacity. Доказано browser-смоком, как требует AC.
  • AC3 (существующее неизменно). Смок mainPickerLeavesExistingObjectsUntouched сравнивает JSON контрольного объекта до/после смены default побайтово. Доказано.
  • AC4 (заливка независима). Смок mainDefaultDoesNotOverwriteShapeFill: после смены default fill_color/fill_opacity новых rect/ellipse равны ранее выставленным #fedcba/0.23, а не новому основному цвету. В коде обработчик пишет только { ...e.detail } (color, opacity), других полей событие не содержит. Доказано и кодом, и смоком.
  • AC5 (единый state). Смок mainPickerUpdatesOneSessionDefault (main → context) и contextPickerUpdatesMainPicker (context → main) в обе стороны, плюс objectDialogRefreshesMainDefault — сохранение properties-диалога линии обновляет main picker по контракту _decorSaveShape (не менялся диффом). Оба picker'а читают один this.host._decorStyle, что даёт единый источник числа «текущий цвет/opacity» без дублирования (проверка на предмет «одно число — один источник» пройдена чтением). Доказано.
  • AC6 (responsive/a11y). Обычная ширина уже покрыта существующими colorPickerUsesTopLayer/colorPickerInsideViewport. Добавлен отдельный прогон на viewport 560×820: narrowToolbarKeepsToolsAndCloseApart (Undo/Redo и все 8 кнопок инструментов не наезжают на закреплённый .editbar-end), narrowToolbarKeepsDefaultPickerUsable (popover целиком в viewport), defaultPickerReadableInLightAndDark (trigger 40×40 и visibility: visible в обеих темах). Стилизация не потребовала правок chrome.styles.ts: уже существующее правило .decorbar hp-color-opacity { flex: 0 0 auto; } (chrome.styles.ts:124) действует на любой hp-color-opacity внутри .decorbar по тегу, а не по конкретному call site — новый инстанс подпадает автоматически. Golden/screenshot-ревью по AC6 — явно предрелизный гейт (см. таблицу выше), здесь не требуется. Доказано смоком + чтением; golden — по плану ТЗ, не на этом этапе.
  • AC7 (совместимость и гейты). Новых persisted-полей и i18n-ключей нет — переиспользованы decor.color/space.opacity, _colorPickerLabels (было ранее). _decorStyle остаётся приватным полем класса с исходным значением { ...DEFAULT_DECOR_STYLE } (houseplan-card.ts:1006), нигде не пишется в конфиг/localStorage — session-only, как и раньше. typecheck/test/build зелёные (таблица выше). Доказано чтением и гейтами.
  • AC8 (документация). docs/DECOR-EDITOR.md и docs/USER-GUIDE.ru.md описывают постоянный main-bar picker и его связь с контекстным; оба changelog (docs/CHANGELOG.md, docs/CHANGELOG.ru.md) правлены в том же коммите со ссылкой на #360. check-docs.mjs зелёный — отпечаток скриншотов документации пересчитан и совпадает. Доказано.

Что проверено и корректно

  • Единственная точка изменения продуктового кода — 9 строк в _renderDecorBar, использующие уже существующий контракт _decorStyle и компонент hp-color-opacity без второй модели цвета, без нативного <input type="color"> (unit-тест на отсутствие таких input по всем src/** зелёный).
  • Обработчик @hp-color-opacity-change у нового picker'а буквально совпадает с обработчиком контекстного picker'а того же назначения (строки 5267-5268 и 5378-5379 идентичны) — не новая логика, а тот же паттерн, что уже используется в 10+ местах файла; дублирование стилистическое и не вводится этим диффом впервые, вычленять абстракцию ради одной новой строки не по задаче.
  • Бандлы (dist/**, custom_components/houseplan/frontend/**) синхронны с исходником побайтово после чистой пересборки.
  • Откат из ТЗ подтверждён: удаление добавленного блока — единственное необходимое действие, состояние и persisted-данные не завязаны на него (проверено практически при тесте «умеет падать»).

Чего не проверял

  • npm run golden:verify и обновление golden-эталонов — по решению ТЗ (AC6, п.6 плана тестов) это предрелизный гейт (PROCESS §8), а не гейт этого ревью; сам выбор уже прошёл зелёное ревью ТЗ.
  • Инварианты модели геометрии (npm run invariants) — diff не касается рёбер, толщины, layout, marker.space, open_spans.
  • Backend (pytest tests_backend) — custom_components/**/*.py не менялся.
  • Производительность — не названа в AC, чувствительный к перфу код не тронут.
  • Ручное визуальное тестирование в браузере (вне Playwright-смоков) — не проводилось; вместо него отработаны целевой и два смежных browser-смока плюс явная проверка «тест умеет падать» на живом бандле.

Итог

Реализация соответствует контракту ТЗ дословно: один новый постоянный hp-color-opacity на главной панели, никаких побочных изменений в персистентной модели, заливке фигур, существующих объектах, i18n или touch/ View-контракте. Все AC либо доказаны browser-смоком (с подтверждённой способностью упасть), либо разобраны по коду. Дешёвые и целевые гейты зелёные. Оснований для Medium/High нет.