Files
Claude 91a008db9c feat(space): диалог «Пространство» по референсу — серия 1 из четырёх (#600)
§4 SPEC.md референса, четыре карточки: Basics (имя и масштаб в сетке 1fr/175,
подложка двумя choice-карточками, callout о пересчёте масштаба), Appearance
(строка-тумблер границ с иконкой и подписью, сегмент стиля стен с образцами
линии, плашка цвета с hex/прозрачностью/Reset, сегмент заливки с деталями
режима, компактный список «Видимые слои» с логикой «включено = видно» — К2),
Room cards (строка-тумблер названий, четыре плитки значений с отключением и
callout-ссылкой, слайдер размера с числом, концами и «Сбросить до 100%», образец
карточки внутри блока на тинте — Q2), Sun & light (сегмент фона, плашка цвета
фона, радио «свет через окна» в строку, север селектом с компасом 44 px и полем
градусов, строка-тумблер свечения).

Одна форма на два рантайма. `renderSpaceForm` в `editors/space-form.ts`
собирает тело и футер; редакторский вход (`space-settings-dialog.ts`) и
онбординг подают порт с тем, что у них различается, и сами рисуют
`<hp-dialog data-kind=…>` — контракт #489 требует литеральный kind. Копия
разметки онбординга на 300 строк удалена; локальные дубли CELL_CM_* и
strictNumber там же — тоже («одно число — один источник»).

К10 (решение владельца Q1): Save активен только при изменениях и без ошибок,
в футере «Есть несохранённые изменения» либо ссылка «Проверьте поля: N» к
первому ошибочному; «✕», Cancel и Escape с изменениями спрашивают через
`_confirmDanger`, без изменений закрывают сразу. Снимок делается при открытии
(`editors/space-form-state.ts`, WeakMap по хосту) — `SpaceDialogState` не
получил ни одного ключа. Валидация: имя, подложка без изображения, диапазон
температур, север 0–359 — с `aria-invalid` и текстом под полем.

Строки. 51 новый ключ живёт в ленивом словаре `i18n/settings` (приём
#423/#459), не в первом кадре: стартовый граф 291 260 при потолке 292 500.
Отдельный словарь, а не `support`, — иначе онбординг тянул бы весь словарь
редактора ради одной формы. 11 существующих ключей переименованы по Q6
(«Space name», «Appearance», «Room cards», «Draw it myself», «Zigbee signal
next to devices»…), 9 мёртвых удалены из четырёх локалей. `hp-color-opacity`
получил `hide-label`: подпись рисует плашка, `label` остаётся для title/aria и
`smoke_color_picker_consumers` проходит без правок (AC5).

Смоки. Переписаны осознанно (К6 снят), каждый — с пометкой #600 у правки:
smoke_hide_layers (компактные строки, инверсия К2 проверяется кликом),
smoke_ux_fixes (сегмент, строка-тумблер, поля °C, ширина 560 — Q7),
smoke_dialog_footer_width (560 для form-shell), smoke_temp_fill (два поля по
id), smoke_settings_dialog_cards (три сегмента), smoke_space_settings и
smoke_space_create_display_defaults и smoke_lazy_editor_chunk (choice-карточка
по смыслу, не по индексу — порядок §4.2 draw → file), smoke_space_scale_defaults
(поле с единицей). Новый фокусный свидетель demo/smoke_space_settings_form.mjs:
состав карточек и отсутствие старой разметки, оболочка 560 и один скроллер,
цели 44, запись каждого нового контрола в свой ключ, Save/dirty/вопрос при
закрытии, семь контрольных ширин (320…1920).

Мутанты: space-fill-segment-writes-a-neighbour-key,
space-save-enabled-without-changes, space-discard-without-asking,
space-layer-toggle-loses-inversion.

Контрактные тесты: `houseplan-source.mjs` дописывает общие модули диалогов к
логическому исходнику; коллектор i18n видит `help(...)` порта и `shelp(...)`
(заодно встал на учёт `radar.help`, проходивший мимо счётчика — 30 → 35).

Потолок ленивого редакторского графа 225 900 → 236 300 (замер 235 560) с
разбором в bundle-budget.mjs: словарь на четыре локали и подписи — большая
часть роста; промежуточно, итог в конце серий.

Гейты: typecheck, build, bundle:sync, npm test 2792 pass (красным был только
потолок — поднят с доводом), 10 смоков пространства зелёные,
smoke_color_picker_consumers без правок, mutation-gate --check.

Issue: #600
User-Visible: no
2026-09-20 10:28:51 +03:00

42 lines
2.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { launch, checkAll, finish } from './serve.mjs';
const { page, browser } = await launch();
const res = await page.evaluate(async () => {
const out = {};
const c = window.__card;
const sr = () => c.shadowRoot || c.renderRoot;
const setFill = async (settings) => {
c._serverCfg = { ...c._serverCfg, spaces: c._serverCfg.spaces.map((s) => s.id !== 'f1' ? s : ({ ...s, settings })) };
c.requestUpdate(); await c.updateComplete;
return [...sr().querySelectorAll('.room.styled')].map((r) => {
const st = r.getAttribute('style') || '';
const m = st.match(/--room-fill:([^;]+)/);
return m ? m[1] : null;
});
};
// living room avg temp = 22.4 (единственный термометр в demo)
out.comfy = await setFill({ show_borders: true, fill_mode: 'temp', temp_min: 20, temp_max: 25 });
out.cold = await setFill({ show_borders: true, fill_mode: 'temp', temp_min: 23, temp_max: 26 });
out.hot = await setFill({ show_borders: true, fill_mode: 'temp', temp_min: 18, temp_max: 21 });
out.swapped = await setFill({ show_borders: true, fill_mode: 'temp', temp_min: 25, temp_max: 20 });
// диалог: поля границ видны только в режиме temp
c._openSpaceDialog('edit', 'f1'); await c.updateComplete;
// #600 §4.2: границы диапазона — два поля с единицей (`#space-temp-min/max`)
// под сегментом заливки; масштаб и север больше не делят с ними класс.
const tempFields = () => sr().querySelectorAll('#space-temp-min, #space-temp-max').length;
out.dialogTempFields = tempFields();
c._spaceDialog = { ...c._spaceDialog, fillMode: 'custom' }; await c.updateComplete;
out.dialogHiddenWhenCustom = tempFields();
c._spaceDialog = null;
return out;
});
// значения зафиксированы прогоном на v1.43.1 и сверены с кодом (audit T1)
checkAll(res, {
"comfy": ["#66d17a", "transparent", "transparent", "transparent"],
"cold": ["#4fc3f7", "transparent", "transparent", "transparent"],
"hot": ["#ffd45c", "transparent", "transparent", "transparent"],
"swapped": ["#66d17a", "transparent", "transparent", "transparent"],
"dialogTempFields": 2, // мин/макс видны в режиме temp (#600: считаем только их)
"dialogHiddenWhenCustom": 0, // мин/макс скрыты в режиме custom
});
await finish(browser, res);