mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 11:18:48 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a984d479fd | ||
|
|
0d58f77ced | ||
|
|
c27c1e44f3 | ||
|
|
508d38ca71 |
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
|
||||
import { fixtureWallKey } from '../fixtures/visual-matrix.mjs';
|
||||
|
||||
/** Data-only HP-QA-01 capture matrix. Bump when framing or scenarios change. */
|
||||
export const GOLDEN_MATRIX_VERSION = 26;
|
||||
export const GOLDEN_MATRIX_VERSION = 27;
|
||||
|
||||
const stage = { capture: 'stage', threshold: { maxChannelDelta: 10, maxDiffRatio: 0.0005 } };
|
||||
const page = { capture: 'page', threshold: { maxChannelDelta: 10, maxDiffRatio: 0.0008 } };
|
||||
@@ -269,6 +269,9 @@ export const GOLDEN_SCENARIOS = Object.freeze([
|
||||
{ id: 'decor-color-popover-mobile-ru', fixture: 'visual', space: 'golden-geometry',
|
||||
dialog: 'decor-color', language: 'ru', theme: 'dark',
|
||||
viewport: { width: 390, height: 760 }, ...page },
|
||||
{ id: 'decor-color-popover-desktop-en', fixture: 'visual', space: 'golden-geometry',
|
||||
dialog: 'decor-color', language: 'en', theme: 'light',
|
||||
viewport: { width: 760, height: 820 }, ...page },
|
||||
{ id: 'backup-full-preview-desktop-en', fixture: 'visual', space: 'golden-geometry',
|
||||
dialog: 'backup-full', language: 'en', theme: 'dark',
|
||||
viewport: { width: 1000, height: 900 }, ...page },
|
||||
|
||||
@@ -48,6 +48,14 @@ const result = await page.evaluate(async () => {
|
||||
&& surface().scrollWidth <= surface().clientWidth;
|
||||
|
||||
const hue = ranges()[0];
|
||||
const hueStyle = getComputedStyle(hue);
|
||||
out.hueTrackContract = hue?.classList.contains('hue-range')
|
||||
&& hue.min === '0' && hue.max === '359' && hue.step === '1'
|
||||
&& hueStyle.getPropertyValue('--hp-picker-hue-track').includes('linear-gradient')
|
||||
&& hue.getBoundingClientRect().height >= 40;
|
||||
const thumbShadow = hueStyle.getPropertyValue('--hp-picker-hue-thumb-shadow');
|
||||
out.hueThumbContrastContract = thumbShadow.includes('0 0 0 2px')
|
||||
&& thumbShadow.includes('0 0 0 3px');
|
||||
ranges()[1].value = '100';
|
||||
ranges()[1].dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
||||
ranges()[2].value = '100';
|
||||
|
||||
File diff suppressed because one or more lines are too long
Vendored
+95
-45
File diff suppressed because one or more lines are too long
@@ -2,6 +2,11 @@
|
||||
|
||||
## v1.65.0-beta.3 — 2026-08-19
|
||||
|
||||
- The House Plan color picker now draws the full spectrum directly on the Hue
|
||||
track, so the target colour family is visible before moving the slider. Mouse,
|
||||
touch and keyboard values remain unchanged, and a contrasting ring keeps the
|
||||
slider visible over every hue
|
||||
([#192](https://github.com/Matysh/houseplan-card/issues/192)).
|
||||
- Editor color controls now open a single House Plan picker with saturation,
|
||||
brightness, hue, exact HEX and opacity together. The nested browser color
|
||||
dialog is gone, while existing room, space, decor and Glow values keep their
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
|
||||
## v1.65.0-beta.3 — 2026-08-19
|
||||
|
||||
- Палитра House Plan теперь показывает полный спектр прямо на шкале «Оттенок»,
|
||||
поэтому нужную группу цветов видно ещё до движения ползунка. Значения и
|
||||
управление мышью, касанием и клавиатурой не изменились, а контрастный контур
|
||||
сохраняет видимость ползунка на любом оттенке
|
||||
([#192](https://github.com/Matysh/houseplan-card/issues/192)).
|
||||
- Выбор цвета в редакторах теперь сразу открывает единую палитру House Plan с
|
||||
насыщенностью, яркостью, оттенком, точным HEX и прозрачностью. Вложенный
|
||||
системный диалог браузера больше не нужен, а сохранённый формат цветов комнат,
|
||||
|
||||
+9
-3
@@ -85,9 +85,15 @@
|
||||
- [ ] The color-only Glow consumer keeps the same picker without an opacity row,
|
||||
and native/fallback floating surfaces remain mutually exclusive with help
|
||||
[auto: `smoke_help_affordance.mjs`].
|
||||
- [ ] The existing dark mobile decor color golden is reviewed from the complete
|
||||
Linux artifact before a beta; baseline acceptance is not part of the
|
||||
implementation loop [golden: `decor-color-popover-mobile-ru`].
|
||||
- [ ] The Hue range keeps `0…359`, step 1 and its existing input events while its
|
||||
WebKit/Blink and Gecko tracks expose the same cyclic spectrum. A dual
|
||||
theme-aware ring keeps the native thumb distinct from every hue, while
|
||||
forced-colors falls back to system track/thumb rendering [unit:
|
||||
`color-picker.test.mjs`, auto: `smoke_color_picker.mjs`].
|
||||
- [ ] The dark mobile and light desktop open-picker goldens are reviewed from the
|
||||
complete Linux artifact before a beta; baseline acceptance is not part of
|
||||
the implementation loop [golden: `decor-color-popover-mobile-ru`,
|
||||
`decor-color-popover-desktop-en`].
|
||||
|
||||
|
||||
## Open passage (#157)
|
||||
|
||||
+3
-1
@@ -180,7 +180,9 @@ the tray scrolls horizontally instead of shrinking the plan.
|
||||
Where an editor offers a color sample, one click opens the House Plan color
|
||||
picker. Hue, saturation, brightness, an exact HEX value and opacity (when the
|
||||
setting supports it) are available together; there is no second browser color
|
||||
dialog. Changes remain a draft until the owning properties dialog is saved.
|
||||
dialog. The Hue track shows the full colour spectrum at a glance, with a
|
||||
contrasting ring keeping its slider visible. Changes remain a draft until the
|
||||
owning properties dialog is saved.
|
||||
|
||||
<!-- docs-section: input -->
|
||||
|
||||
|
||||
@@ -189,8 +189,9 @@ title: План дома
|
||||
Если редактор показывает образец цвета, один клик открывает палитру House Plan.
|
||||
Оттенок, насыщенность, яркость, точное значение HEX и прозрачность (если она
|
||||
поддерживается этой настройкой) доступны вместе, без второго системного диалога
|
||||
браузера. Изменения остаются черновиком до сохранения родительского диалога
|
||||
свойств.
|
||||
браузера. Шкала «Оттенок» сразу показывает полный цветовой спектр, а контрастный
|
||||
контур сохраняет видимость ползунка. Изменения остаются черновиком до сохранения
|
||||
родительского диалога свойств.
|
||||
|
||||
<!-- docs-section: input -->
|
||||
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
# CODE-REVIEW-192-r1
|
||||
|
||||
- **Issue:** https://github.com/Matysh/houseplan-card/issues/192
|
||||
- **ТЗ:** лёгкий трек (`small`) — живёт в теле issue #192 (ревью —
|
||||
`docs/reviews/SPEC-REVIEW-192-r1.md`, вердикт зелёный, Low-1 снят с записью,
|
||||
Medium: 0)
|
||||
- **Диапазон:** `git log --oneline origin/dev..HEAD` / `git diff
|
||||
origin/dev...HEAD` — один коммит `508d38c` «Show spectrum on hue slider»
|
||||
(`Issue: #192`, `User-Visible: yes`)
|
||||
- **Роль:** ревьюер кода (не автор), этап `S7-code-review`
|
||||
- **Цикл:** r1/2 (лёгкий трек)
|
||||
|
||||
## Скоуп ревью
|
||||
|
||||
Проверялось соответствие реализации:
|
||||
|
||||
- контракту из тела issue #192 (§«Контракт поведения и UX», AC1–AC5, план
|
||||
автотестов, риски);
|
||||
- `docs/SCOPE.md` — задача не расширяет продукт за пределы editor usability
|
||||
polish (J4/J6), View/kiosk не затронуты;
|
||||
- `AGENTS.md`/`PROCESS.md` — классы файлов, трейлеры, оба changelog в одном
|
||||
`User-Visible: yes` коммите, изоляция (только `.hue-range`, не общий
|
||||
`input[type='range']`);
|
||||
- фактическому поведению `src/hp-color-opacity.ts` в браузере (Chromium,
|
||||
Playwright), а не только по чтению CSS;
|
||||
- реально выполненным гейтам (см. ниже), включая полный `npm run
|
||||
golden:verify` — diff меняет видимый рендер открытого picker, что подпадает
|
||||
под критерий «рендер/геометрия/стили» из инструкции к этому ревью.
|
||||
|
||||
## Как проверялось
|
||||
|
||||
1. Прочитан весь diff (`git diff origin/dev...HEAD --stat` и файлы по
|
||||
отдельности): `src/hp-color-opacity.ts` (+23/-0 в стилях), `test/color-
|
||||
picker.test.mjs` (+15 строк, новый тест), `demo/smoke_color_picker.mjs`
|
||||
(+5 строк), `demo/golden/matrix.mjs` (+3, новая сцена `decor-color-
|
||||
popover-desktop-en`), `test/golden-matrix.test.mjs` (+10, contract-тест
|
||||
на список сцен), `docs/{CHANGELOG.md,CHANGELOG.ru.md,USER-GUIDE.md,
|
||||
USER-GUIDE.ru.md,TESTING.md}`, сгенерированные `dist/houseplan-card.js` /
|
||||
`custom_components/houseplan/frontend/houseplan-card.js` /
|
||||
`demo/srv/assets/houseplan-card.js`.
|
||||
2. Прочитано тело issue #192 целиком и все четыре комментария (аналитика
|
||||
владельца, зелёное SPEC-ревью, хендофф реализации) — сверено, что
|
||||
реализация не расширяет и не сужает скоуп относительно принятого ТЗ
|
||||
(только `.hue-range`, S/V/opacity не тронуты).
|
||||
3. Прочитан `src/hp-color-opacity.ts:225-312` построчно: `input[type='range']`
|
||||
(общий, высота 40px, `margin: -5px 0`) не изменён; `.hue-range` добавляет
|
||||
`--hp-picker-hue-track` (linear-gradient, 7 стопов, циклический —
|
||||
0%/100% оба `#f00`) и `accent-color` не тронут; `::-webkit-slider-
|
||||
runnable-track` и `::-moz-range-track` — одинаковый контракт (высота
|
||||
10px, `border`, `border-radius: 999px`, `background: var(--hp-picker-
|
||||
hue-track)`); `::-moz-range-progress` обнулён в `transparent`, чтобы
|
||||
Gecko не закрашивал левую часть трека сплошным accent-цветом поверх
|
||||
градиента; `@media (forced-colors: active)` переопределяет оба трека на
|
||||
`Canvas`/`ButtonText` — системный безопасный fallback без градиента.
|
||||
4. Подтверждено, что новый CSS не покидает `.hue-range`: точечный grep по
|
||||
файлу — `::-webkit-slider-thumb`/`::-moz-range-thumb` для `.hue-range`
|
||||
отсутствуют (совпадает с тем, что ТЗ не заявляло новый thumb-стиль,
|
||||
только «остаётся различимым»); общий `input[type='range']` не содержит
|
||||
`linear-gradient`/`hp-picker-hue-track` (подтверждено также unit-тестом
|
||||
`commonRange`).
|
||||
5. **Дисциплина falsifiability применена к обоим прогнанным тестам:**
|
||||
- unit: воссоздан pre-#192 файл во временном `git worktree` на
|
||||
`origin/dev` и прогнан новый тест `test/color-picker.test.mjs`
|
||||
(«the hue range exposes one cyclic spectrum...») именно против старого
|
||||
`.hue-range` (только `accent-color`, без градиента) — тест корректно
|
||||
падает на первой же проверке
|
||||
(`--hp-picker-hue-track:\s*linear-gradient` не находит совпадения).
|
||||
Worktree удалён после проверки.
|
||||
- browser smoke: `node demo/smoke_color_picker.mjs` прогнан на текущем
|
||||
дереве — все 15 проверок `true`, включая новую `hueTrackContract`
|
||||
(class, `min/max/step`, `--hp-picker-hue-track` содержит
|
||||
`linear-gradient`, `height >= 40px`); падение этой же проверки на
|
||||
pre-#192 бандле подтверждено тем же временным worktree (переменная
|
||||
`--hp-picker-hue-track` не существует на старом `.hue-range`, так что
|
||||
проверка `.includes('linear-gradient')` даёт `false`).
|
||||
6. Собран собственный бандл (`npm run build`) и сверен SHA-256 всех трёх
|
||||
копий (`dist/`, `custom_components/houseplan/frontend/`,
|
||||
`demo/srv/assets/`) — совпадает с заявленным автором в хендоффе
|
||||
(`8ec9aff0...`).
|
||||
7. Прогнан `npm run golden:verify` (полный набор, 67 активных сценариев) —
|
||||
не только названные в TESTING.md сцены: подтверждён список расхождений
|
||||
(см. «Обязательные гейты» и «Гейты по необходимости»).
|
||||
8. **Живая проверка в браузере сверх названных в issue способов
|
||||
доказательства** (см. «Находки»): открыт picker через тот же харнесс,
|
||||
что использует `smoke_color_picker.mjs`
|
||||
(`demo/serve.mjs` + `card._decorTool='line'` + `.trigger` click), hue
|
||||
программно выставлялся в 0°, 55°, 205°, 210°, 220°, 235°, 250°, 260°,
|
||||
каждый раз делался точный `page.screenshot({clip})` по
|
||||
`getBoundingClientRect()` самого `.hue-range`, чтобы увидеть реальный
|
||||
растровый результат, а не полагаться на чтение CSS. Аналогичная проверка
|
||||
повторена на независимо собранном pre-#192 бандле (тот же временный
|
||||
worktree) для сравнения «было/стало».
|
||||
9. Проверены трейлеры и class-принадлежность: `git show 508d38c --stat` —
|
||||
класс A (`src/hp-color-opacity.ts`), B (`test/`, `demo/`), C (`docs/`), D
|
||||
(все три копии бандла) — все в одном коммите; `Issue: #192`,
|
||||
`User-Visible: yes`; оба changelog редактируются в этом же коммите.
|
||||
10. Проверено отсутствие новых runtime dependencies:
|
||||
`git diff origin/dev...HEAD -- package.json package-lock.json` — пусто.
|
||||
|
||||
## Обязательные гейты (всегда)
|
||||
|
||||
| Гейт | Команда | Результат |
|
||||
|---|---|---|
|
||||
| Typecheck | `npx tsc --noEmit` | чисто, без вывода |
|
||||
| Unit-тесты | `npm test` | `889/889`, 0 fail (совпадает с заявленным автором) |
|
||||
| Build + сверка бандлов | `npm run build` + `sha256sum dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js demo/srv/assets/houseplan-card.js` | все три идентичны, `8ec9aff054216bc8df5df9bf6410213254a352c7b742b4b28231e0a0048171b2` — совпадает с хендоффом автора |
|
||||
|
||||
Falsifiability (тест должен уметь падать), применена к обоим прогнанным
|
||||
наборам — см. п.5 выше («Как проверялось»): unit и browser-smoke оба
|
||||
корректно падают на pre-#192 коде.
|
||||
|
||||
## Гейты по необходимости
|
||||
|
||||
| Гейт | Почему запускался | Результат |
|
||||
|---|---|---|
|
||||
| `node demo/smoke_color_picker.mjs` | назван в AC2 issue как способ доказательства, единственная затронутая поверхность | зелёный, все 15 проверок `true`, включая новую `hueTrackContract`; falsifiability подтверждена (см. выше) |
|
||||
| `npm run golden:verify` (полный набор, 67 активных сценариев) | diff меняет видимый рендер открытого picker (новый градиент трека) — подпадает под критерий «рендер/геометрия/стили/слои» из инструкции к ревью | 65 `passed`, 1 `different` (`decor-color-popover-mobile-ru` — ожидаемо, картинка просмотрена, см. «Находки»), 1 `missing-baseline` (`decor-color-popover-desktop-en` — новая сцена, ожидаемо). Изоляция (AC4) подтверждена: ни один из 65 остальных сценариев, включая все прочие поверхности с `input[type=range]` (тряй-панели, диалоги устройств), не изменился |
|
||||
| `python -m pytest tests_backend -q` | не запускался | diff не трогает ни одного файла `custom_components/**/*.py` (подтверждено `git diff --stat`), гейт неприменим |
|
||||
| performance-профили | не запускались | AC явно называет причину не запускать: «статичный CSS gradient, runtime вычислений нет»; diff не трогает canvas/рендер-цикл плана, только статичный CSS двух `input[type=range]` |
|
||||
|
||||
## Что проверено и корректно
|
||||
|
||||
- **AC1 — спектр.** `.hue-range` получает циклический 7-стопный
|
||||
`linear-gradient(to right, #f00 0% … #f00 100%)`, применённый через
|
||||
`::-webkit-slider-runnable-track` и `::-moz-range-track` с одинаковыми
|
||||
стопами (общая CSS-переменная `--hp-picker-hue-track`) — подтверждено
|
||||
чтением и unit source-contract тестом. Живой рендер в Chromium (скриншоты
|
||||
hue=0/55/205/235/250) показывает корректный порядок цветов: красный →
|
||||
жёлтый → зелёный → циан → синий → маджента → красный, совпадает со
|
||||
стандартным hue-кругом.
|
||||
- **AC2 — управление.** `min=0/max=359/step=1` не изменены; mouse (через
|
||||
`.value` + `input` event), touch (pointer-путь `sv-field` не тронут,
|
||||
сам hue остаётся нативным range), Arrow/`Shift+Arrow` (`shiftArrowUsesTenStep`),
|
||||
focus/Escape и `hp-color-opacity-change` — все существующие smoke-проверки
|
||||
остались зелёными без изменений ожиданий, кроме одной новой добавленной
|
||||
(`hueTrackContract`); 120° по-прежнему даёт `#00ff00` (`hueUpdatesSharedDraft`).
|
||||
- **AC4 — изоляция.** Полный `golden:verify` (67 сценариев) подтверждает: из
|
||||
всех поверхностей с `input[type='range']` (decor picker, диалоги устройств,
|
||||
тряй-панели) изменился рендер только у самого открытого color popover;
|
||||
общий `input[type='range']` блок не содержит градиентных деклараций
|
||||
(unit-тест `commonRange` + собственноручный grep).
|
||||
- **AC5 — совместимость.** `package.json`/`package-lock.json` не менялись;
|
||||
API компонента (`color`/`opacity`/`showOpacity`/`disabled`,
|
||||
`hp-color-opacity-change`) не тронуто; i18n-строк не добавлено — сверено
|
||||
чтением diff.
|
||||
- **forced-colors fallback (часть AC3).** `@media (forced-colors: active)`
|
||||
корректно переопределяет оба трека на системные `Canvas`/`ButtonText` без
|
||||
градиента — безопасный вариант деградации, соответствует общей практике
|
||||
WCAG для forced-colors.
|
||||
- **Gecko progress не перекрывает градиент.** `::-moz-range-progress`
|
||||
обнулён в `background: transparent` — без этого Firefox залил бы левую
|
||||
часть трека сплошным accent-цветом поверх спектра, что было прямо названо
|
||||
риском в хендоффе автора; проверено чтением (Gecko недоступен в среде
|
||||
ревью, как и заявлено в «Принятых предположениях» issue).
|
||||
- **Изоляция gradient от текущего выбранного цвета (п.3 контракта).**
|
||||
`--hp-picker-hue-track` — статическая константа, не зависит от `_hue`;
|
||||
подтверждено чтением и тем, что unit-тест матчит буквальные hex-стопы.
|
||||
- **prefers-reduced-motion (п.4 контракта).** Новый CSS не добавляет
|
||||
`transition`/`animation`; существующий `@media (prefers-reduced-motion:
|
||||
reduce)` блок не тронут — подтверждено diff'ом.
|
||||
- **Trailers/классы файлов/changelog.** Коммит `508d38c` несёт `Issue: #192`,
|
||||
`User-Visible: yes`; оба changelog (`docs/CHANGELOG.md`,
|
||||
`docs/CHANGELOG.ru.md`) редактируются в этом же коммите; формулировки
|
||||
(«шкала «Оттенок» показывает полный спектр») соответствуют
|
||||
`docs/USER-GUIDE.ru.md`, термины не изобретены.
|
||||
- **Golden-сценарии (план автотестов).** Новая light-сцена
|
||||
`decor-color-popover-desktop-en` добавлена и покрыта отдельным
|
||||
contract-тестом (`test/golden-matrix.test.mjs`), проверяющим ровно два
|
||||
сценария `dialog === 'decor-color'` с ожидаемыми `theme`/`language`/
|
||||
`width` — соответствует плану автотестов issue.
|
||||
|
||||
## Находки
|
||||
|
||||
### Medium-1 — thumb hue-шкалы теряет контраст с треком в диапазоне циан/синий/индиго (~195°–260°)
|
||||
|
||||
**Файл:** `src/hp-color-opacity.ts:234-259` (новый градиент трека без
|
||||
компенсирующего стиля thumb).
|
||||
|
||||
AC3 issue дословно требует: «Spectrum/thumb читаемы в light/dark... thumb
|
||||
остаётся различимым». Риск-таблица самого issue заранее называла этот
|
||||
сценарий («Thumb теряется на ярком секторе») и предлагала меру
|
||||
(«контрастная border/shadow без уменьшения focus indicator») — эта мера не
|
||||
реализована: в diff нет ни `::-webkit-slider-thumb`, ни `::-moz-range-thumb`
|
||||
для `.hue-range`.
|
||||
|
||||
**Воспроизведение (выполнено, не предположение).** Открыт picker тем же
|
||||
харнессом, что и `demo/smoke_color_picker.mjs` (`demo/serve.mjs`, decor →
|
||||
Line → `.trigger`), hue выставлялся программно через `input.value` +
|
||||
`input`-event, затем снят `page.screenshot({ clip })` строго по
|
||||
`getBoundingClientRect()` элемента `.hue-range`:
|
||||
|
||||
- при hue 205° (то самое значение, что используется в обеих golden-сценах
|
||||
`decor-color-popover-mobile-ru` и `decor-color-popover-desktop-en`,
|
||||
просмотренных автором как «контраст корректен») граница между thumb и
|
||||
треком уже заметно слабее, чем на жёлтом/красном участке;
|
||||
- при 210°, 220°, 235°, 250° thumb почти сливается с треком — узнаваемого
|
||||
контрастного контура нет, различим только по едва заметному перепаду
|
||||
яркости.
|
||||
|
||||
Причина: thumb рисуется фиксированным системным цветом браузера (в
|
||||
тестируемом Chromium — solid blue), который не меняется с `_hue`
|
||||
(независимый от #192 давний баг: `accent-color: var(--hp-picker-hue)` не
|
||||
красит сам thumb ни до, ни после этого коммита — проверено тем же способом
|
||||
на независимо собранном pre-#192 бандле, thumb там тоже фиксированно-синий,
|
||||
но на равномерно-сером треке это никогда не создавало проблемы контраста).
|
||||
До #192 нейтральный серый трек гарантировал контраст с любым
|
||||
фиксированным цветом thumb; #192 заменяет этот трек радугой, и ровно в той
|
||||
части круга, что близка по тону к цвету самого thumb, декларированное в
|
||||
AC3 «thumb остаётся различимым» перестаёт быть верным для реального
|
||||
Chromium-рендера — единственного движка, фактически исполняемого в CI/
|
||||
smoke/golden этого проекта (Gecko/WebKit заявлены как source-contract-only
|
||||
по «Принятым предположениям» issue).
|
||||
|
||||
**Почему Medium, не Low.** Находка не гипотетическая: воспроизведена прямым
|
||||
запуском в том же харнессе, что использует официальный smoke, на реальном
|
||||
исполняемом движке проекта, включая ровно те hue-значения, что приняты в
|
||||
golden baseline. Диапазон ~65° из 359° (≈18% круга) — это не край случай, а
|
||||
частый выбор (синие/циановые акценты в HA-конфигурациях освещения).
|
||||
Функциональность не ломается (клавиатура, числовая подпись `205°`,
|
||||
drag из любой точки трека работают), поэтому не High; но декларированный
|
||||
критерий приёмки (AC3, «thumb остаётся различимым») в этом диапазоне не
|
||||
выполняется, что не позволяет закрыть находку как Low с одной лишь записью.
|
||||
|
||||
**Действие:** заведён отдельный issue
|
||||
[#194](https://github.com/Matysh/houseplan-card/issues/194) (`bug`, `P3`,
|
||||
`S1-new`), со ссылкой на #192 и на этот документ. Обнаруженный смежный
|
||||
давний баг «`accent-color` не красит thumb вообще» описан в issue как явно
|
||||
не-скоуп — самостоятельный вопрос, а не часть этой находки.
|
||||
|
||||
## Чего не проверял
|
||||
|
||||
- **Реальный Firefox/Gecko и Safari/WebKit.** Смотрел только Chromium
|
||||
(единственный движок, доступный в среде ревью и в CI проекта). Gecko/
|
||||
WebKit-декларации (`::-moz-range-track`, `::-moz-range-progress`)
|
||||
проверены исключительно чтением CSS и совпадением стопов с
|
||||
webkit-версией — это прямо принято issue как достаточное доказательство
|
||||
(«Принятые предположения»: «WebKit/Safari и Gecko declarations
|
||||
доказываются source-contract и code review»), не пытаюсь выдать это за
|
||||
исполненный тест.
|
||||
- **Реальный screen-reader / forced-colors режим ОС.** `@media
|
||||
(forced-colors: active)` прочитан построчно (валидный синтаксис, системные
|
||||
ключевые слова `Canvas`/`ButtonText`), но не исполнялся в браузере с
|
||||
реально включённым forced-colors — то же ограничение среды, что уже было
|
||||
зафиксировано как Low в SPEC-ревью и признано доказуемым только чтением
|
||||
кода.
|
||||
- **`prefers-reduced-motion: reduce` живьём** — подтверждено чтением diff
|
||||
(блок не тронут, новый CSS не содержит `transition`/`animation`), не
|
||||
отдельным browser-прогоном с эмуляцией медиа-фичи.
|
||||
- **`npm run golden:accept`** — не запускал и не должен: принятие baseline
|
||||
(в т.ч. новой сцены `decor-color-popover-desktop-en`) принадлежит
|
||||
пре-бета этапу на полном Linux CI-артефакте, не циклу код-ревью.
|
||||
- **Полный `node demo/smoke_*.mjs` набор (127 файлов)** — не запускался
|
||||
целиком: diff касается ровно одной поверхности (`hp-color-opacity`),
|
||||
прогнан только относящийся к ней `smoke_color_picker.mjs` плюс golden
|
||||
целиком (гейт «по необходимости» — рендер/стили).
|
||||
- **Bundle size budget** — issue не называет числовой бюджет (в отличие от
|
||||
#57); не оценивал raw/gzip дельту отдельно, только сверил байт-в-байт
|
||||
идентичность трёх копий и совпадение SHA-256 с хендоффом автора.
|
||||
|
||||
## Вердикт
|
||||
|
||||
Жёлтый. High: 0, Medium: 1 (→ [#194](https://github.com/Matysh/houseplan-card/issues/194)), Low: 0.
|
||||
AC1, AC2, AC4, AC5 выполнены и доказаны исполнением, с личной проверкой
|
||||
falsifiability для обоих прогнанных тестов. AC3 выполнена частично: light/
|
||||
dark читаемость трека и forced-colors fallback подтверждены, но
|
||||
задекларированное в этом же AC «thumb остаётся различимым» не
|
||||
выполняется в диапазоне hue ~195°–260° на Chromium — единственном
|
||||
исполняемом в этом проекте движке — что прямо воспроизведено скриншотами в
|
||||
ходе ревью, а не предположено. Это ровно тот сценарий, который сама задача
|
||||
предвидела в своей риск-таблице и не закрыла кодом. Находка не блокирует
|
||||
(функциональность и три остальных AC не страдают, диапазон — суб-часть
|
||||
одного критерия), поэтому не High; заведена отдельным issue #194 согласно
|
||||
процессу. Жёлтый, а не зелёный — потому что реализация не полностью решает
|
||||
заявленный в AC3 сценарий, даже при том что формальные шаги плана
|
||||
автотестов пройдены.
|
||||
@@ -0,0 +1,132 @@
|
||||
# CODE-REVIEW-192-r2
|
||||
|
||||
Issue: [#192](https://github.com/Matysh/houseplan-card/issues/192) — «Колор-пикер:
|
||||
шкала «Оттенок» показывает реальную радугу вместо одноцветного слайдера».
|
||||
Лёгкий трек (`small`); ТЗ живёт в теле issue. Лимит цикла код-ревью для
|
||||
лёгкого трека — 2; это второй и последний цикл.
|
||||
|
||||
Диапазон: `origin/dev..HEAD` = `508d38c` (Show spectrum on hue slider),
|
||||
`c27c1e4` (docs: review document for #192 — документ r1),
|
||||
`0d58f77` (Keep hue slider thumb visible — исправление по итогам r1).
|
||||
Диапазон соответствует ветке `issue/192-hue-gradient`.
|
||||
|
||||
## Скоуп проверки
|
||||
|
||||
r1 (см. [CODE-REVIEW-192-r1.md](CODE-REVIEW-192-r1.md)) уже подтвердил AC1,
|
||||
AC2, AC4, AC5 исполнением и дал жёлтый вердикт из-за частично проваленной
|
||||
AC3: заявленное «thumb остаётся различимым» не выполнялось на hue ≈195°–260°
|
||||
(включая hue=205° — значение по умолчанию в обеих golden-сценах), Medium
|
||||
заведён как [#194](https://github.com/Matysh/houseplan-card/issues/194).
|
||||
|
||||
r2 — это review исправляющего коммита `0d58f77`. Задача этого цикла: не
|
||||
повторять весь r1 с нуля, а (а) подтвердить, что исправление действительно
|
||||
закрывает найденный дефект, (б) убедиться, что оно не расширило скоуп и не
|
||||
задело остальное, (в) держать общую картину AC1/2/4/5 в поле зрения, раз они
|
||||
не менялись со времён r1.
|
||||
|
||||
## Как проверялось
|
||||
|
||||
| Гейт | Прогнан | Результат |
|
||||
|---|---|---|
|
||||
| `npx tsc --noEmit` | да | зелёный, без вывода |
|
||||
| `npm test` | да | 889 passed, 0 failed — совпадает с хендоффом |
|
||||
| `npm run build` + сверка 3 копий бандла | да | `cmp` тождественны; SHA-256 `d66b779574aebd9222ddf432b746cdf5195cae3030b2cc8ba149dad6f98ce89d` — совпадает с указанным в хендоффе |
|
||||
| `node demo/smoke_color_picker.mjs` | да | 16/16 true, включая новый `hueThumbContrastContract` |
|
||||
| Фальсифицируемость unit-теста | да | во временном worktree на pre-fix коммите `508d38c` с тестовыми файлами из HEAD: `hue-range exposes one cyclic spectrum...` падает ровно на новой ассерции (box-shadow/thumb-shadow), остальные 4 подтеста того же теста проходят |
|
||||
| Фальсифицируемость smoke-проверки | да | тот же pre-fix коммит, пересобранный бандл: `hueThumbContrastContract: false` (единственный провал), после применения `0d58f77` — `true` |
|
||||
| Визуальная проверка контраста thumb | да, вручную через Playwright/`demo/serve.mjs` | скриншоты открытого picker на hue=205° (dark, mobile-профиль) и hue=235° (тот же профиль) — кольцо чётко видно на голубом/синем участке трека, ранее проблемном по r1 |
|
||||
| `npm run golden:capture -- --scenario=decor-color-popover-desktop-en` | да | `missing-baseline` (ожидаемо, эталона ещё нет); candidate открыт — кольцо видно и в light-теме на hue=205° |
|
||||
| `node scripts/process-gate.mjs` (офлайн) | да | «гейт пройден», 1 ожидаемое WARN (ТЗ в теле issue при `small`, а не в `docs/specs/`) |
|
||||
| Трейлеры коммитов | да, чтением `git log` | все три коммита несут `Issue: #192` и корректный `User-Visible:`; `User-Visible: yes` на `508d38c` и `0d58f77` сопровождается правками обоих changelog в том же коммите |
|
||||
|
||||
**Не прогонялось и почему:**
|
||||
|
||||
- `npm run golden:verify` (полный набор, 67 сценариев) — не гонял повторно.
|
||||
r1 уже прогонял полный набор и получил ожидаемую картину (65 passed + 2
|
||||
ожидаемых расхождения ровно в двух `decor-color-popover-*` сценах). Диапазон
|
||||
правки `0d58f77` строго ограничен селекторами `.hue-range::-webkit-slider-thumb`
|
||||
и `.hue-range::-moz-range-thumb` — то есть тем же самым hue-thumb внутри тех
|
||||
же двух сцен, что уже были единственными расхождениями в r1. Полный прогон
|
||||
здесь не добавил бы уверенности к тому, что дают точечные `golden:capture`
|
||||
двух затронутых сцен (сделаны) плюс unit-контракт изоляции (`commonRange`
|
||||
не содержит `linear-gradient|hp-picker-hue-track`, не тронут). Это гейт,
|
||||
соразмерный задаче (PROCESS.md §8): полный набор — предрелизный, не ревью.
|
||||
- `python -m pytest tests_backend -q` — не гонял: `custom_components/**/*.py`
|
||||
не тронут ни в одном из трёх коммитов (diffstat это подтверждает).
|
||||
- performance-профили — не гонял: в AC не названы, правка — один статичный
|
||||
CSS `box-shadow`, без новых вычислений в рантайме.
|
||||
- Firefox/Safari реальным движком — недоступно в этом окружении (только
|
||||
Chromium), это принятое предположение самого ТЗ («Принятые предположения»,
|
||||
автоматизированный smoke остаётся Chromium; Gecko/WebKit declarations
|
||||
доказываются source-contract и код-ревью). Прочитаны декларации
|
||||
`::-moz-range-thumb` — синтаксически корректны и симметричны webkit-версии.
|
||||
|
||||
## Находки
|
||||
|
||||
Нет ни одной High- или Medium-находки в этом цикле.
|
||||
|
||||
Low (не блокирует, снимается с записью, дальнейшая правка не нужна):
|
||||
корректирующий коммит не добавил отдельного smoke/unit-теста, который бы
|
||||
проверял именно *визуальный* контраст (пиксельное сравнение thumb/track) —
|
||||
только наличие CSS custom property. Единственное реальное доказательство
|
||||
контраста на конкретном hue — это golden-эталон (проверяется людьми при
|
||||
приёмке) и ручной скриншот, сделанный в этом ревью. Это соответствует
|
||||
принятому в ТЗ способу доказательства AC3 (golden + source-contract/code
|
||||
review), поэтому не поднимаю до Medium — но фиксирую, что «различимость
|
||||
thumb» как факт живёт исключительно в golden-baseline review, а не в
|
||||
автотесте, который умеет упасть сам по себе на будущей регрессии этого рода.
|
||||
|
||||
Информационно, не находка: описанный в
|
||||
[#194](https://github.com/Matysh/houseplan-card/issues/194) дефект
|
||||
воспроизводится именно на pre-`0d58f77` коде и не воспроизводится после —
|
||||
проверено выше и unit-тестом, и smoke, и скриншотами на hue=205° и hue=235°.
|
||||
Закрытие #194 — не в полномочиях этого ревью (§12: закрывает issue
|
||||
релиз-менеджер/владелец, не ревьюер кода), фиксирую здесь как факт для того,
|
||||
кто будет разбирать #194 дальше.
|
||||
|
||||
## Что проверено и корректно
|
||||
|
||||
- **AC1 (спектр).** Без изменений с r1: `linear-gradient` с ключевыми stops
|
||||
на `.hue-range`, эквивалентные `::-webkit-slider-runnable-track` и
|
||||
`::-moz-range-track`. Подтверждено исполнением (unit + smoke,
|
||||
`hueTrackContract: true`).
|
||||
- **AC2 (управление).** Без изменений с r1 и без затронутого кода в r2:
|
||||
`min=0/max=359/step=1`, mouse/touch/keyboard/`Shift+Arrow`/emit не тронуты.
|
||||
889/889 unit + 16/16 smoke, включая `shiftArrowUsesTenStep` и
|
||||
`hueUpdatesSharedDraft`.
|
||||
- **AC3 (темы и accessibility) — предмет этого цикла.** Ранее проваленная
|
||||
часть («thumb остаётся различимым») теперь выполняется: двухконтурное
|
||||
`box-shadow` (`--card-background-color` внутренний контур,
|
||||
`--primary-text-color` внешний) не зависит от hue и поэтому контрастен на
|
||||
любом участке трека — проверено чтением реализации и визуально на двух
|
||||
ранее проблемных значениях (205°, 235°) в обоих затронутых golden-сценах
|
||||
(dark/mobile, light/desktop). `forced-colors: active` корректно отключает
|
||||
декоративную тень (`box-shadow: none` внутри того же media-блока,
|
||||
подтверждено чтением diff) — системный рендеринг thumb остаётся. Native
|
||||
размер/цвет/hit area бегунка не переопределены, значит риск нарушить
|
||||
hit-area или fallback без color отсутствует. `prefers-reduced-motion`
|
||||
блок не тронут.
|
||||
- **AC4 (изоляция).** Правка `0d58f77` меняет ровно 4 селектора внутри уже
|
||||
существующего scoped-блока `.hue-range`/forced-colors; unit-тест явно
|
||||
проверяет, что `input[type='range']` (общий блок) не содержит новых
|
||||
свойств. Изменённых golden-сцен по-прежнему ровно две — те же, что в r1.
|
||||
- **AC5 (совместимость).** Public API, данные, i18n, стили остальных range
|
||||
не изменены — diffstat и unit source-contract это подтверждают.
|
||||
- **Трейлеры и changelog.** `Issue: #192` на каждом коммите; `User-Visible:
|
||||
yes` на `508d38c` и `0d58f77` — в обоих оба changelog правятся в том же
|
||||
коммите (проверено чтением `git show`). `USER-GUIDE.md`/`USER-GUIDE.ru.md`
|
||||
обновлены той же формулировкой («contrasting ring» / «контрастный контур»)
|
||||
синхронно с кодом.
|
||||
- **Гейты.** Все обязательные (typecheck/test/build+сверка) зелёные и
|
||||
воспроизведены самостоятельно, не только со слов хендоффа; SHA-256
|
||||
бандла совпал.
|
||||
|
||||
## Вердикт
|
||||
|
||||
Дефект AC3, из-за которого r1 получил жёлтый вердикт, исправлен и
|
||||
верифицирован независимым исполнением (unit, smoke, ручной визуальный
|
||||
скриншот на конкретных ранее проблемных hue-значениях), а не только
|
||||
прочитан. Новых High/Medium-находок нет. Скоуп не расширен: правка
|
||||
ограничена thumb-декорацией того же `.hue-range`.
|
||||
|
||||
**Вердикт: зелёный · цикл r2/2 · High: 0 · Medium: 0 → нет**
|
||||
@@ -233,6 +233,40 @@ export class HpColorOpacity extends LitElement {
|
||||
}
|
||||
.hue-range {
|
||||
accent-color: var(--hp-picker-hue, #f00);
|
||||
--hp-picker-hue-track: linear-gradient(to right,
|
||||
#f00 0%, #ff0 16.667%, #0f0 33.333%, #0ff 50%,
|
||||
#00f 66.667%, #f0f 83.333%, #f00 100%);
|
||||
--hp-picker-hue-thumb-shadow:
|
||||
0 0 0 2px var(--card-background-color, #202126),
|
||||
0 0 0 3px var(--primary-text-color, #fff);
|
||||
}
|
||||
.hue-range::-webkit-slider-runnable-track {
|
||||
height: 10px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid var(--divider-color, #666);
|
||||
border-radius: 999px;
|
||||
background: var(--hp-picker-hue-track);
|
||||
}
|
||||
.hue-range::-moz-range-track {
|
||||
height: 10px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid var(--divider-color, #666);
|
||||
border-radius: 999px;
|
||||
background: var(--hp-picker-hue-track);
|
||||
}
|
||||
.hue-range::-moz-range-progress {
|
||||
height: 10px;
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
background: transparent;
|
||||
}
|
||||
.hue-range::-webkit-slider-thumb {
|
||||
border-radius: 50%;
|
||||
box-shadow: var(--hp-picker-hue-thumb-shadow);
|
||||
}
|
||||
.hue-range::-moz-range-thumb {
|
||||
border-radius: 50%;
|
||||
box-shadow: var(--hp-picker-hue-thumb-shadow);
|
||||
}
|
||||
input[type='text'],
|
||||
input[type='number'] {
|
||||
@@ -276,6 +310,22 @@ export class HpColorOpacity extends LitElement {
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.picker { transition: none !important; }
|
||||
}
|
||||
@media (forced-colors: active) {
|
||||
.hue-range::-webkit-slider-runnable-track {
|
||||
border-color: ButtonText;
|
||||
background: Canvas;
|
||||
}
|
||||
.hue-range::-moz-range-track {
|
||||
border-color: ButtonText;
|
||||
background: Canvas;
|
||||
}
|
||||
.hue-range::-webkit-slider-thumb {
|
||||
box-shadow: none;
|
||||
}
|
||||
.hue-range::-moz-range-thumb {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
connectedCallback(): void {
|
||||
|
||||
@@ -48,3 +48,21 @@ test('the shared component keeps its API and contains no nested native color pic
|
||||
assert.equal((card.match(/<hp-color-opacity/g) || []).length, 8);
|
||||
assert.equal((card.match(/\.pickerLabels=\$\{this\._colorPickerLabels\}/g) || []).length, 8);
|
||||
});
|
||||
|
||||
test('the hue range exposes one cyclic spectrum without restyling other ranges', () => {
|
||||
const component = readFileSync(new URL('../src/hp-color-opacity.ts', import.meta.url), 'utf8');
|
||||
const spectrum = component.match(/\.hue-range\s*\{[\s\S]*?\n\s*\}/)?.[0] || '';
|
||||
assert.match(spectrum, /--hp-picker-hue-track:\s*linear-gradient\(to right/);
|
||||
for (const stop of ['#f00 0%', '#ff0 16.667%', '#0f0 33.333%', '#0ff 50%',
|
||||
'#00f 66.667%', '#f0f 83.333%', '#f00 100%']) assert.ok(spectrum.includes(stop), stop);
|
||||
assert.match(component, /\.hue-range::\-webkit-slider-runnable-track\s*\{[\s\S]*?height:\s*10px;[\s\S]*?background:\s*var\(--hp-picker-hue-track\);/);
|
||||
assert.match(component, /\.hue-range::\-moz-range-track\s*\{[\s\S]*?height:\s*10px;[\s\S]*?background:\s*var\(--hp-picker-hue-track\);/);
|
||||
assert.match(component, /\.hue-range::\-moz-range-progress\s*\{[\s\S]*?background:\s*transparent;/);
|
||||
assert.match(component, /\.hue-range::\-webkit-slider-thumb\s*\{[\s\S]*?border-radius:\s*50%;[\s\S]*?box-shadow:\s*var\(--hp-picker-hue-thumb-shadow\);/);
|
||||
assert.match(component, /\.hue-range::\-moz-range-thumb\s*\{[\s\S]*?border-radius:\s*50%;[\s\S]*?box-shadow:\s*var\(--hp-picker-hue-thumb-shadow\);/);
|
||||
assert.match(spectrum, /--hp-picker-hue-thumb-shadow:[\s\S]*?card-background-color[\s\S]*?primary-text-color/);
|
||||
assert.match(component, /@media \(forced-colors:\s*active\)[\s\S]*?background:\s*Canvas;/);
|
||||
assert.match(component, /class="hue-range" type="range" min="0" max="359" step="1"/);
|
||||
const commonRange = component.match(/input\[type='range'\]\s*\{[\s\S]*?\n\s*\}/)?.[0] || '';
|
||||
assert.doesNotMatch(commonRange, /linear-gradient|hp-picker-hue-track/);
|
||||
});
|
||||
|
||||
@@ -209,7 +209,7 @@ test('sun-ray golden requires browser-painted light from a state-only sun entity
|
||||
assert.ok(scenario);
|
||||
const fixture = prepareGoldenFixture(scenario);
|
||||
const space = fixture.config.spaces.find((item) => item.id === scenario.space);
|
||||
assert.equal(GOLDEN_MATRIX_VERSION, 26);
|
||||
assert.equal(GOLDEN_MATRIX_VERSION, 27);
|
||||
assert.equal(space.settings.sun_rays, true);
|
||||
assert.equal(scenario.northDeg, 90,
|
||||
'the sign-sensitive golden must keep a non-zero north direction');
|
||||
@@ -227,6 +227,16 @@ test('sun-ray golden requires browser-painted light from a state-only sun entity
|
||||
assert.equal(scenario.sunRayPixels.minChannelDelta >= 4, true);
|
||||
});
|
||||
|
||||
test('the open color picker golden covers dark mobile and light desktop themes', () => {
|
||||
const scenarios = GOLDEN_SCENARIOS.filter((item) => item.dialog === 'decor-color');
|
||||
assert.deepEqual(scenarios.map(({ id, language, theme, viewport }) => ({
|
||||
id, language, theme, width: viewport.width,
|
||||
})), [
|
||||
{ id: 'decor-color-popover-mobile-ru', language: 'ru', theme: 'dark', width: 390 },
|
||||
{ id: 'decor-color-popover-desktop-en', language: 'en', theme: 'light', width: 760 },
|
||||
]);
|
||||
});
|
||||
|
||||
test('value badge golden covers four positions and bottom badge with separate LQI', () => {
|
||||
const scenario = GOLDEN_SCENARIOS.find((item) => item.id === 'device-value-badge-positions-dark');
|
||||
assert.ok(scenario);
|
||||
|
||||
Reference in New Issue
Block a user