16 KiB
CODE-REVIEW-140-r1
- Issue: #140 — узкий футер диалогов свойств проёма и физического объекта
- Трек:
trivial(короткий трек, §5.1 PROCESS.md) - Коммит:
40550dc618b5df59a1e123008d7207a899179d4b"Fix editor property dialog footer width", веткаissue/140-dialog-footer-width - Диапазон:
origin/dev..HEAD= один коммит - Ревьюер: Claude (код-ревью), сессия без контекста реализации
Скоуп
Диапазон git diff origin/dev...HEAD затрагивает 8 файлов:
src/houseplan-card.ts— добавлен атрибутwideна<hp-dialog>в_renderOpeningDialog()(:16431) и_renderPhysicalDialog()(:16745). Больше ничего в файле не менялось.test/golden-matrix.test.mjs— существующий unit-тест «all destructive editor dialogs use the shared responsive footer groups» переименован и расширен проверкойassert.match(openTag, /\bwide\b/, …)для_renderOpeningDialog,_renderPhysicalDialog,_renderSpaceDialog.demo/smoke_dialog_footer_width.mjs— новый browser-smoke (215 строк), измеряет реальную геометрию футера в обеих локалях на десктопной и узкой ширине.docs/CHANGELOG.md,docs/CHANGELOG.ru.md— по одному пункту, со ссылкой на #140.dist/houseplan-card.js,custom_components/houseplan/frontend/houseplan-card.js,demo/srv/assets/houseplan-card.js— три копии сгенерированного бандла, идентичные друг другу (см. ниже).
src/styles.ts (общий dialog-action-footer/перенос) не тронут — это
соответствует явному «не в скоупе» из решения владельца. Диалог пространства
(_renderSpaceDialog, :18042) не менялся: он уже использовал wide.
Изменение соответствует зафиксированному владельцем решению в комментариях к
issue (два уточнения: перенос дефекта на hp-dialog.ts/атрибут wide, затем
включение диалога физических объектов в тот же скоуп) и не расширяет его —
CSS-контракт переноса футера не тронут, третья ширина не введена, диалог
черновика (4-кнопочный вариант того же _renderPhysicalDialog) не получил
требования «в одну строку», только требование не переполняться.
Продуктовая линия по docs/SCOPE.md: правка полирует J6 («Keep the plan true
as the home evolves» — два редактора, повседневная работа админа дома в
десктопном браузере) — чисто визуальный дефект layout в admin-only
поверхности, не открывает новых возможностей и не противоречит инвариантам
View mode.
Как проверялось
Соразмерность гейтов: диапазон меняет два вызова компонента и добавляет один
таргетный smoke; полный smoke/golden/performance/pytest не запускался
— обоснование в разделе «Чего не проверял».
| Гейт | Команда | Результат |
|---|---|---|
| Typecheck | npx tsc --noEmit |
green, без вывода |
| Unit | npm test |
green, 789/789 |
| Build + sync копий | npm run build затем cmp трёх копий |
green; после локальной пересборки рабочее дерево осталось чистым (git status --short пусто) — закоммиченный бандл байт-в-байт воспроизводим |
| Bundle hash | sha256sum dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js demo/srv/assets/houseplan-card.js |
все три 9b87823ed5b7b0346657c9cd630f61346adf51e0d228225f9f7baa49bc4d8171 |
| Целевой smoke | node demo/smoke_dialog_footer_width.mjs (после npm run build + копирования в demo/srv/assets) |
OK, все 24 проверки true — числа ниже |
Числа из прогона smoke (десктоп 1000×820, узкий 320×820):
| Диалог | Язык | surfaceWidth |
spareWidth (запас) |
oneRow |
|---|---|---|---|---|
| opening | en | 500.00 | 240.31 | true |
| physical | en | 500.00 | 240.31 | true |
| opening | ru | 500.00 | 187.09 | true |
| physical | ru | 500.00 | 187.09 | true |
| space (контроль) | ru | 500.00 | — | true, не регрессировал |
Узкая ширина (320 px): opening/physical surfaceWidth = 300.8, вписан в
320*0.94+1 = 301.8, wrapped: true (перенос сохранён), noHorizontalOverflow: true. Четырёхкнопочный draft (сегмент черновика) — buttons: 4,
buttonsContained: true, insideViewport: true, без требования одной строки,
как и оговорено в issue.
Дисциплина «тест должен уметь падать» — проверена для обоих прогнанных тестов, не только предъявлена:
- Unit-тест
test/golden-matrix.test.mjs(сабтест «all destructive editor dialogs use the medium shell…»): временно откатилwideв_renderOpeningDialog(), прогналnode --test test/golden-matrix.test.mjs→ сабтест 13 упал (AssertionError: _renderOpeningDialog must reserve the existing 500 px desktop shell). Откатил правку,git status --shortснова пуст. - Smoke
demo/smoke_dialog_footer_width.mjs: тем же способом откатилwideу_renderOpeningDialog(), пересобрал (npm run build, копия вdemo/srv/assets), прогнал smoke →opening_en_medium_shell: false,opening_ru_medium_shell: false(остальные проверки, не зависящие от этого диалога, осталисьtrue, как и должно быть). Откатил правку, пересобрал, сверил хеш бандла — снова совпадает с закоммиченным.
Оба теста реагируют на регресс именно в той точке, которую призваны защищать; это не тавтологичные проверки.
AC — разбор
- AC1 (три кнопки в одну строку в обоих диалогах, en/ru). Доказано smoke:
..._three_actions_one_row: trueдля всех четырёх комбинаций диалог×язык,buttons: 3в каждом случае,oneRow: true. Соответствует требованию владельца «Удалить» слева / «Отмена», «Сохранить» справа — подтверждено по коду (dialog-action-dangerсодержит одну кнопкуbtn dangerс_deletePhysicalSelection/аналог для проёма,dialog-action-commit—Cancel+Save) и геометрически (dangerRect/commitRectна одной строке черезoneRow). - AC2 (запас положительный, перенос не срабатывает, числа названы).
Доказано smoke:
spareWidth— 240.31 px (en) и 187.09 px (ru) на обоих диалогах,positive_localization_headroom: true,oneRow: true— перенос не сработал ни в одной из измеренных комбинаций. Числа приведены выше и воспроизведены мной отдельным прогоном, а не переписаны из хендоффа. - AC3 (узкий экран без горизонтального скролла, перенос сохранён, диалог
пространства и четырёхкнопочный футер черновика не регрессируют). Доказано
smoke на 320 px:
opening_narrow_fits_viewport,physical_narrow_fits_viewport,opening_narrow_keeps_responsive_wrap,physical_narrow_keeps_responsive_wrap,draft_four_actions_remain_contained,space_narrow_not_regressed— всеtrue.wrapped: trueподтверждает, что CSS-перенос (не тронутый диффом) по-прежнему срабатывает на узком экране, а не был случайно отключён атрибутомwide(обе ширины заданы черезmin(px, vw), как и предполагал владелец в комментарии, и это подтверждено измерением, а не одним лишь чтением кода).
Все три AC доказаны исполняемым smoke с реальными числами, а не «verified» без команды.
Проверено чтением, не исполнением
- Диапазон правки в
src/houseplan-card.tsограничен ровно двумя строками (добавление токенаwideк открывающему тегу<hp-dialog>), подтверждено постатейным чтением diff — никаких сопутствующих изменений в теле методов, в_deletePhysicalSelection,_deleteDraftSegment,_deleteDraftWholeили в обработчиках_saveMarker/_savePhysicalDialogнет. src/hp-dialog.tsне менялся; свойствоwide = falseсreflect: true(:36, :220) и правило:host([wide]) .surface { width: min(500px, 94vw); }(:157) — уже существующий, семь раз используемый до этой правки контракт, что подтверждает заявление issue «не вводит нового UX-контракта».src/styles.ts(dialog-action-footer,dialog-action-group,dialog-action-danger/commit, :2989–3012) не менялся — контракт переноса футера, о котором явно сказано «не менять», прочитан и подтверждён нетронутым по diffstat.- Диалог сегмента черновика (
d.kind === 'draft', :16780) рендерится тем же_renderPhysicalDialog(), получилwideнаравне с диалогом физического объекта — это не отдельная ветка кода, значит расширение ширины распространяется на него автоматически; узкий smoke подтверждает containment (4 кнопки), но не требует одной строки, что соответствует явному «не в скоупе» issue. - Трейлеры коммита:
Issue: #140,User-Visible: yes— оба changelog правлены в этом же коммите (docs/CHANGELOG.md,docs/CHANGELOG.ru.md), формулировки используют терминологиюdocs/USER-GUIDE.ru.md(«проём», «Удалить», «Отмена», «Сохранить»). - Ветка
issue/140-dialog-footer-widthсоответствует имени issue; коммит один, без промежуточных «допишу потом».
Что проверено и корректно
- Продуктовая правка минимальна и точна: два атрибута
wide, использующих существующий, уже семикратно применяемый вариант ширины — ровно то решение, которое зафиксировал владелец. - CSS-контракт переноса футера и вторая (узкая) ширина диалога не тронуты — соответствует «не в скоупе».
- Три копии бандла синхронны и воспроизводимы локальной пересборкой байт-в-байт (проверено дважды: до и после экспериментов с откатом).
- Тесты (unit + smoke) реально проверяют регресс, а не тавтологичны — оба прожаты в «падающем» состоянии.
- Трейлеры, changelog, формат хендоффа и именование ветки соответствуют PROCESS.md/AGENTS.md.
- Автоматическое ревью до этого не отработало (см. комментарий issue, прогон); данный документ и вердикт — первый фактический код-ревью по этой задаче, цикл r1/2 (лимит короткого трека).
Чего не проверял — и почему
node demo/smoke_*.mjs(остальные 126 из 127) — не прогонял. Diff не трогает canvas-геометрию, солнце, LQI, освещение, изометрию, тач-инпут и прочие поверхности; единственная затронутая поверхность (футер двух диалогов свойств) покрыта специально написаннымsmoke_dialog_footer_width.mjs, который я прогнал и проверил на способность падать. Прогон всех 127 уместен «когда задача задевает всё» — здесь не тот случай (§8 PROCESS.md, issue #127).npm run golden:verify— не прогонял. Просмотрелdemo/golden/matrix.mjsцеликом: ни один из golden-сценариев не открывает_renderOpeningDialog/_renderPhysicalDialog(там естьdialog: 'device','decor-color','backup-full','backup-space', но не свойства проёма или физического объекта) — набор физически не видит эту поверхность и не дал бы дополнительного сигнала.styles.ts(общая геометрия/цвета плана) не тронут, так что риска регресса в покрытых golden сценах тоже нет.python -m pytest tests_backend -q— не прогонял.custom_components/**/*.pyне входит в diff.- Performance-профили — не прогонял. Ни AC, ни diff не называют чувствительных к перфу путей; правка — статический HTML-атрибут в двух местах рендера диалога.
- Скриншот в реальном браузере (визуальная приёмка человеком) — не делал; вместо этого — измерение геометрии через Playwright в smoke (тот же уровень автоматической проверки, который процесс признаёт доказательством).
Находки
Нет. High: 0, Medium: 0, Low: 0.
Вердикт
Все три AC доказаны исполняемым тестом с реальными числами, дисциплина «тест должен уметь падать» подтверждена для обоих прогнанных тестов, гейты по таблице выше зелёные, трейлеры и changelog в порядке, скоуп не расширен и не сужен относительно решения владельца.
Вердикт: зелёный · цикл r1/2 · High: 0 · Medium: 0 → нет новых issue