mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-07 06:59:46 +00:00
feat(general): «Общие настройки» по референсу — серия 2 из четырёх (#600)
§5 SPEC.md референса. Семь карточек #598 остались, менялось содержимое: - Display — две полные строки с иконкой, подписью и тумблером справа; пояснение радара по §5.2 стало подписью строки (текст прежний, ключ переехал в ленивый словарь как gs.radar_show_live_hint, «?» у строки больше нет); - Zigbee links — заголовок и «?» функции (topology.help) рисует карточка, а блок hp-zigbee-topology-settings получил режим embedded: строка-тумблер, callout «сохраните настройку», подзаголовки ZHA / Zigbee2MQTT, поле тем и строки действий контролами набора. Заголовок больше не удваивается (дефект 1). Лист набора вносится в теневой корень компонента; токены --hpf-* наследуются с хоста. Прежний render без embedded не тронут — его держит контракт #459; - Room fill colors — три подзаголовка (Lights / Temperature / Zigbee signal, Q6) и плитки цвета по три в ряд: название на свотче, hex, число прозрачности; свотч — trigger прежнего hp-color-opacity (Q5); - Light-source glow — две плитки и поле радиуса с единицей внутри рамки; - Plan — плашка цвета стен, сегмент фона, при Static — плашка фона с Reset и подсказкой (тема / свой цвет); - Sun — gs.sun_missing остаётся callout'ом (К5); север: поле градусов с «°», ссылка Clear, «N» и компас 44 px справа — индикатор набора, но тянуть его по-прежнему можно (smoke_sun гоняет настоящий drag; старый 120-px SVG и его CSS в plan.styles.ts удалены); лучи — строка-тумблер с подписью; источник лучей — сегмент (sun-settings-view.ts переписан с <select> на segmented, логика та же); - Data — подзаголовки с «?» и строки действий набора. К10 (Q1): Save только при изменениях и без ошибок (радиус > 0, север 0–359), статус в футере, вопрос при закрытии с изменениями; снимок при открытии — editors/general-form-state.ts поверх общего editors/dialog-baseline.ts (на него же переведён space-form-state.ts). _settingsDialog не получил новых ключей. Удалено мёртвое: _renderColorRow (рантайм и карточка), .sunrow/.suncol, ключи gs.wall_group, gs.bg_group, gs.bg_theme, gs.radar_show_live.help(.aria) в четырёх локалях. Девять новых ключей — в i18n/settings. Смоки переписаны осознанно, с пометкой #600: smoke_general_settings (инвентарь по классам набора, подзаголовки .hpf-sub h4, пояснения под «?» и подписью, ни одного .rhint), smoke_sun (сегмент источника лучей вместо select, поле #gs-north, radius drag 18 px), smoke_backup_transfer (подзаголовок и строка действий), smoke_settings_dialog_cards (callout вместо .rhint). Новый фокусный свидетель demo/smoke_general_settings_form.mjs: семь карточек и отсутствие старой разметки, один заголовок у Zigbee, десять плиток, компас 44, цели 44, запись плиток/плашки/радиуса/севера/сегментов в свои ключи, Save/dirty/вопрос при закрытии. Мутанты: general-save-enabled-without-changes, general-tile-opacity-writes-a-neighbour-key, general-north-clear-writes-zero, topology-embedded-draws-its-own-heading; якоря state-callout-hidden-under-help и dialog-card-loses-its-heading переставлены на новую разметку. Контрактные тесты: houseplan-source.mjs приводит `host.` к `this.` и в телах вынесенных диалогов; color-picker — три явных showOpacity=true (плитки, плашка стены, цвет комнаты); счётчик производных .aria 35 → 34. Бюджет: стартовый граф 290 711 — потолок ОПУЩЕН 292 500 → 292 200 (ушёл 120-px компас из eager-листа и мёртвые ключи); ленивый редакторский 237 929 — потолок 236 300 → 238 600 с разбором в bundle-budget.mjs. Гейты: typecheck, build, bundle:sync, npm test 2792 pass, mutation-gate --check, семь смоков «Общих», smoke_color_picker_consumers без правок. Issue: #600 User-Visible: no
This commit is contained in:
@@ -77,8 +77,11 @@ test('the shared component keeps its API and contains no nested native color pic
|
||||
assert.match(defaultPicker, /this\._updateDecorStyle\(\{ \.\.\.this\._decorStyle, \.\.\.e\.detail \}\)/);
|
||||
assert.equal((card.match(/\.showOpacity=\$\{false\}/g) || []).length, 4,
|
||||
'Glow, ripple and both background pickers stay color-only');
|
||||
assert.equal((card.match(/\.showOpacity=\$\{true\}/g) || []).length, 2,
|
||||
'general fill rows and room color explicitly keep their existing opacity');
|
||||
// #600: плитки палитры (одна общая функция), плашка заливки стен и цвет
|
||||
// границ комнаты — три явных `showOpacity=true`; раньше стена шла общей
|
||||
// строкой с палитрой.
|
||||
assert.equal((card.match(/\.showOpacity=\$\{true\}/g) || []).length, 3,
|
||||
'general fill tiles, the wall-fill plate and room color explicitly keep their existing opacity');
|
||||
assert.match(card, /confirm:\s*this\._t\('color_picker\.confirm'\)/);
|
||||
for (const locale of ['en', 'ru', 'de', 'fr']) {
|
||||
const messages = JSON.parse(readFileSync(new URL(`../src/i18n/${locale}.json`, import.meta.url), 'utf8'));
|
||||
|
||||
+4
-1
@@ -86,6 +86,9 @@
|
||||
.hpf-unit > span { padding: 0 11px 0 3px; white-space: nowrap; color: var(--hpf-muted); font-size: .875rem; }
|
||||
.hpf-unit:focus-within { outline: 2px solid var(--hpf-accent); outline-offset: 2px; }
|
||||
.hpf-unit.hpf-unit-wide, .hpf-unit.hpf-unit-wide > input { width: 100%; min-width: 0; }
|
||||
.hpf-field > .hpf-unit:not(.hpf-unit-wide) { justify-self: start; }
|
||||
.hpf-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; }
|
||||
.hpf-actions > input[type="file"] { display: none; }
|
||||
.hpf-row {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
@@ -294,7 +297,7 @@
|
||||
.hpf-north > .hpf-labelrow { grid-area: 1 / 1; min-height: 28px; }
|
||||
.hpf-north > .hpf-select, .hpf-north > .hpf-unit, .hpf-north > .hpf-input { grid-area: 2 / 1; }
|
||||
.hpf-north > .hpf-north-n { grid-area: 1 / 2; text-align: center; font-size: .875rem; line-height: 28px; color: var(--hpf-muted); }
|
||||
.hpf-compass { grid-area: 2 / 2; display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; border: 1px solid var(--hpf-line); border-radius: 50%; background: var(--hpf-canvas); color: var(--hpf-accent); }
|
||||
.hpf-compass { grid-area: 2 / 2; box-sizing: border-box; display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; border: 1px solid var(--hpf-line); border-radius: 50%; background: var(--hpf-canvas); color: var(--hpf-accent); }
|
||||
.hpf-compass svg { width: 24px; height: 24px; transform-origin: 50% 50%; transform-box: view-box; }
|
||||
.hpf-compass.hpf-unset { color: var(--hpf-muted); }
|
||||
.hpf-tint { padding: 14px; border: 1px solid var(--hpf-tint-line); border-radius: 9px; background: var(--hpf-tint); }
|
||||
|
||||
+4
-1
@@ -72,6 +72,9 @@
|
||||
.hpf-unit > span { padding: 0 11px 0 3px; white-space: nowrap; color: var(--hpf-muted); font-size: .875rem; }
|
||||
.hpf-unit:focus-within { outline: 2px solid var(--hpf-accent); outline-offset: 2px; }
|
||||
.hpf-unit.hpf-unit-wide, .hpf-unit.hpf-unit-wide > input { width: 100%; min-width: 0; }
|
||||
.hpf-field > .hpf-unit:not(.hpf-unit-wide) { justify-self: start; }
|
||||
.hpf-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; }
|
||||
.hpf-actions > input[type="file"] { display: none; }
|
||||
.hpf-row {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
@@ -280,7 +283,7 @@
|
||||
.hpf-north > .hpf-labelrow { grid-area: 1 / 1; min-height: 28px; }
|
||||
.hpf-north > .hpf-select, .hpf-north > .hpf-unit, .hpf-north > .hpf-input { grid-area: 2 / 1; }
|
||||
.hpf-north > .hpf-north-n { grid-area: 1 / 2; text-align: center; font-size: .875rem; line-height: 28px; color: var(--hpf-muted); }
|
||||
.hpf-compass { grid-area: 2 / 2; display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; border: 1px solid var(--hpf-line); border-radius: 50%; background: var(--hpf-canvas); color: var(--hpf-accent); }
|
||||
.hpf-compass { grid-area: 2 / 2; box-sizing: border-box; display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; border: 1px solid var(--hpf-line); border-radius: 50%; background: var(--hpf-canvas); color: var(--hpf-accent); }
|
||||
.hpf-compass svg { width: 24px; height: 24px; transform-origin: 50% 50%; transform-box: view-box; }
|
||||
.hpf-compass.hpf-unset { color: var(--hpf-muted); }
|
||||
.hpf-tint { padding: 14px; border: 1px solid var(--hpf-tint-line); border-radius: 9px; background: var(--hpf-tint); }
|
||||
|
||||
@@ -59,7 +59,10 @@ export function readHouseplanProductionSource() {
|
||||
const fn = file.statements.find((statement) => ts.isFunctionDeclaration(statement)
|
||||
&& statement.name?.text === exported);
|
||||
if (!fn) throw new Error(`Houseplan dialog module is missing ${exported}`);
|
||||
const body = source.slice(source.indexOf('{', fn.getStart(file)), fn.end);
|
||||
// #600: тела диалогов держат `const host = this.host` и пишут `host.…`;
|
||||
// для контрактных регулярок это тот же `this.…`.
|
||||
const body = source.slice(source.indexOf('{', fn.getStart(file)), fn.end)
|
||||
.replaceAll(/(?<!this\.)\bhost\./g, 'this.');
|
||||
moved.set(method, body);
|
||||
}
|
||||
const replacements = [];
|
||||
@@ -92,7 +95,7 @@ export function readHouseplanProductionSource() {
|
||||
const shared = readFileSync(new URL(path, import.meta.url), 'utf8')
|
||||
.replaceAll('port.host.', 'this.')
|
||||
.replaceAll('port.help(', 'this._help(')
|
||||
.replaceAll(/\bhost\./g, 'this.');
|
||||
.replaceAll(/(?<!this\.)\bhost\./g, 'this.');
|
||||
reconstructed += `\n// --- shared dialog module ${path} ---\n${shared}`;
|
||||
}
|
||||
return reconstructed;
|
||||
|
||||
@@ -71,8 +71,10 @@ test('help accessibility copy is derived from every literal help consumer', () =
|
||||
// `space.layers.help` вместо них), плюс шесть новых из ленивого словаря
|
||||
// (подложка, границы, слои, названия, размер шрифта, свечение); коллектор
|
||||
// научился видеть `help(...)` порта — и `radar.help` из секции радара, который
|
||||
// раньше проходил мимо счётчика, встал на учёт — 30 → 35.
|
||||
assert.equal(consumers.derivedHelpAria.size, 35, 'the current settings surface has 35 statically derived help descriptions');
|
||||
// раньше проходил мимо счётчика, встал на учёт — 30 → 35. Серия 2, «Общие
|
||||
// настройки» по §5: пояснение радара стало подписью строки-тумблера (текст
|
||||
// тот же, «?» у него нет) — 35 → 34.
|
||||
assert.equal(consumers.derivedHelpAria.size, 34, 'the current settings surface has 34 statically derived help descriptions');
|
||||
for (const key of consumers.derivedHelpAria) {
|
||||
assert.equal(typeof dictionary[key], 'string', `${key} must accompany its .help consumer`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user