mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
committed by
claude[bot]
parent
083038f25f
commit
cfb8ce401d
@@ -0,0 +1,255 @@
|
||||
# Issue #381 — действие по нажатию «Ничего не делать»
|
||||
|
||||
- **Issue:** https://github.com/Matysh/houseplan-card/issues/381
|
||||
- **Статус документа:** первая редакция, готова к ревью
|
||||
- **Приоритет / тип:** P2 · feature / polish
|
||||
- **Область:** marker config, Редактор устройств, View/киоск, frontend action
|
||||
projection, backend validation, i18n, документация и QA
|
||||
- **Связи:** #94 (универсальный Toggle), #178 (точная Toggle-сущность)
|
||||
- **Ревизия:** 1 (2026-08-30)
|
||||
|
||||
## Сценарий
|
||||
|
||||
Администратор дома размещает на плане датчик или виртуальный маркер, который
|
||||
должен только показывать место и состояние. В Редакторе устройств он выбирает
|
||||
для маркера действие **«Ничего не делать»** и сохраняет настройки. После этого
|
||||
член семьи, гость или киоск может коротко нажать маркер, не открывая карточку и
|
||||
не отправляя команду устройству. Долгое нажатие и правый клик сохраняют свои
|
||||
отдельные информационные действия.
|
||||
|
||||
## Что человек увидит до и после
|
||||
|
||||
**До:** короткое нажатие всегда открывает информационную поверхность либо
|
||||
пытается выполнить команду. **После:** в селекторе появляется явный вариант
|
||||
«Ничего не делать», и короткое нажатие выбранного маркера не даёт никакого
|
||||
результата, пока сам маркер продолжает показывать состояние на плане.
|
||||
|
||||
## Проблема
|
||||
|
||||
`TAP_ACTIONS` предлагает только `info`, `more-info`, `toggle` и `run`.
|
||||
`projectedTapAction()` проецирует неизвестный сохранённый токен в `info`, а
|
||||
`_clickDevice()` после известных команд открывает локальную карточку. Backend
|
||||
`MARKER_SCHEMA` также принимает только эти четыре значения, legacy `cover` и
|
||||
`null`. Поэтому строка в селекторе без сквозного контракта либо будет отклонена
|
||||
сервером, либо после reload превратится в открытие карточки.
|
||||
|
||||
Использовать неработоспособный `toggle` как замену нельзя: это зависимый от
|
||||
текущей привязки safe no-op с диагностикой, который может начать отправлять
|
||||
команду после восстановления цели. Здесь требуется явное и стабильное намерение
|
||||
пользователя.
|
||||
|
||||
## Скоуп
|
||||
|
||||
- Новая строка «Ничего не делать» в селекторе «Действие по нажатию» для
|
||||
существующих и новых реальных/виртуальных маркеров.
|
||||
- Канонический persisted literal `marker.tap_action: "none"`.
|
||||
- Одинаковый no-op для короткого mouse click, touch tap и keyboard activation
|
||||
`Enter`/`Space` в View и киоске.
|
||||
- Сквозное чтение, preview, запись, backend validation, full/space
|
||||
export/import и downgrade-описание нового literal.
|
||||
- Паритет переводов EN/RU/DE/FR и обновление пользовательской документации.
|
||||
- Регресс-проверки остальных действий и default-проекции.
|
||||
|
||||
## Не-скоуп
|
||||
|
||||
- Изменение долгого нажатия, правого клика или жестов редактора.
|
||||
- Отдельная настройка hold/right-click action.
|
||||
- Изменение hit area, hover/focus, внешнего вида, состояния, pulse, Glow, LQI,
|
||||
value badge, room aggregates или доступности маркера.
|
||||
- Изменение defaults: свет без явного значения по-прежнему получает Toggle,
|
||||
остальные устройства — карточку House Plan.
|
||||
- Изменение семантики `info`, `more-info`, `toggle`, `run` и legacy `cover`.
|
||||
- Новая HA service/WebSocket-команда, подтверждение или toast для `none`.
|
||||
- Интерактивность `houseplan-space-card`, которая остаётся отдельной статической
|
||||
поверхностью по своему существующему контракту.
|
||||
|
||||
## Контракт поведения
|
||||
|
||||
### 1. Выбор и сохранение
|
||||
|
||||
1. `TAP_ACTIONS` содержит канонический `none`; селектор показывает его строкой
|
||||
**«Ничего не делать»** после существующих четырёх действий.
|
||||
2. Выбор `none` немедленно меняет draft/effective action диалога. Toggle- и
|
||||
Run-зависимые поля скрываются по тем же правилам, что для `info` и
|
||||
`more-info`; блок `controls` остаётся доступен, поскольку он участвует также
|
||||
в light/presentation graph.
|
||||
3. Save записывает точный literal `tap_action: "none"`. Повторное открытие
|
||||
диалога и reload сохраняют выбранную строку.
|
||||
4. Cancel не меняет конфиг. Простое Open → Save маркера с отсутствующим,
|
||||
legacy либо неизвестным `tap_action` сохраняет прежний lossless-контракт и
|
||||
не материализует `none`.
|
||||
5. `none` не равен `null`, отсутствию или пустой строке. Только явный literal
|
||||
отключает короткое действие; прежняя light/non-light default-проекция не
|
||||
меняется.
|
||||
|
||||
### 2. Короткое нажатие
|
||||
|
||||
1. `projectedTapAction("none", domain)` возвращает `none` для любого domain и
|
||||
binding kind. Неизвестные токены по-прежнему fail closed в `info`.
|
||||
2. В View/киоске `_clickDevice()` после разрешения актуального marker по id и
|
||||
до binding/service веток завершает `none` как quiet no-op.
|
||||
3. No-op означает одновременно: не открывать карточку House Plan или HA
|
||||
more-info, не создавать confirmation, не показывать toast, не вызывать HA
|
||||
service/WebSocket, не запускать press feedback/activity и не менять
|
||||
локальное/серверное состояние.
|
||||
4. Click/tap по маркеру остаётся поглощённым маркером и не превращается в клик
|
||||
по комнате, pan либо другому объекту под ним.
|
||||
5. `Enter` и `Space` идут тем же `_clickDevice()`-путём, предотвращают
|
||||
браузерный default как сейчас и также завершаются без побочного действия.
|
||||
|
||||
### 3. Независимые жесты и presentation
|
||||
|
||||
- Long press 600 ms по-прежнему открывает внутреннюю карточку House Plan.
|
||||
- Правый клик по-прежнему открывает HA more-info основной сущности либо
|
||||
существующий fallback для marker без primary.
|
||||
- В Редакторе устройств короткий клик по маркеру по-прежнему открывает его
|
||||
настройки; `tap_action` относится только к View/киоску.
|
||||
- Marker остаётся видимым, focusable и получает прежние hover/focus стили,
|
||||
tooltip, state, pulse, badge, Glow и LQI. Отдельного disabled-вида нет.
|
||||
- Preview диалога меняет только effective action; рисунок маркера не меняется.
|
||||
|
||||
## UX и i18n
|
||||
|
||||
Добавить одинаковый ключ `tap.none` в четыре словаря:
|
||||
|
||||
- RU: `Ничего не делать`;
|
||||
- EN: `Do nothing`;
|
||||
- DE: `Nichts tun`;
|
||||
- FR: `Ne rien faire`.
|
||||
|
||||
Новая строка находится в существующем native select после `Run`; новая
|
||||
подсказка, иконка либо секция диалога не нужны. Toggle-hint, target chooser и
|
||||
confirmation checkbox при `none` не показываются. Live region Toggle не
|
||||
анонсирует цель.
|
||||
|
||||
## Модель данных, миграция и совместимость
|
||||
|
||||
### Frontend
|
||||
|
||||
- Возвращаемый тип `projectedTapAction()` и все effective-action consumers
|
||||
расширяются literal `none`.
|
||||
- `Marker.tap_action` остаётся forward-compatible `string | null`; отдельная
|
||||
миграция типа или model/store version не требуется.
|
||||
- `devices` fingerprint уже включает исходный `tap_action`, поэтому смена на
|
||||
`none` инвалидирует presentation/action snapshot без нового cache key.
|
||||
|
||||
### Backend
|
||||
|
||||
`MARKER_SCHEMA` принимает `none` рядом с текущими canonical literals. Значение
|
||||
не несёт target/ref и не требует semantic validator. Cross-language parity
|
||||
test обязан доказать, что backend принимает каждый literal из frontend
|
||||
`TAP_ACTIONS`, включая `none`, и продолжает отдельно принимать legacy `cover`.
|
||||
|
||||
### Import/export и downgrade
|
||||
|
||||
- Full и space export/import сохраняют `none` штатным копированием marker;
|
||||
remap ссылок не требуется.
|
||||
- Виртуализация duplicate marker при space import продолжает удалять
|
||||
`tap_action` вместе с HA-dependent marker fields по существующему правилу.
|
||||
- Старый frontend прочитает неизвестный ему `none` как `info`; старый backend
|
||||
отклонит попытку записать изменённый marker с новым literal. Это известная
|
||||
граница downgrade для нового enum value, а не основание маскировать `none`
|
||||
под отсутствующее значение.
|
||||
- Новый frontend/backend не переписывают старые `cover`, absent, `null` или
|
||||
неизвестные untouched значения при редактировании другого поля.
|
||||
|
||||
## Затронутые файлы и модули
|
||||
|
||||
- `src/logic.ts`, `src/device-toggle.ts`, `src/houseplan-card.ts`.
|
||||
- `src/houseplan-editor-runtime.ts`; `src/types.ts` только если потребуется
|
||||
уточнить локальный effective-action type без сужения forward compatibility.
|
||||
- `src/i18n/{en,ru,de,fr}.json`.
|
||||
- `custom_components/houseplan/validation.py`.
|
||||
- `test/device-toggle.test.mjs`, action/click contract tests,
|
||||
`tests_backend/test_validation.py` и целевой browser smoke диалога/маркера.
|
||||
- `docs/USER-GUIDE.md`, `docs/USER-GUIDE.ru.md`,
|
||||
`docs/CONFIG-COMPATIBILITY.md`, `docs/ARCHITECTURE.md`, при необходимости
|
||||
`docs/TESTING.md`, оба changelog.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- **AC1 — selector и persisted value (unit + smoke).** Селектор содержит пять
|
||||
canonical options с `none` последним; выбор сохраняет
|
||||
`tap_action: "none"`, reopen/reload восстанавливает его, Cancel не пишет.
|
||||
- **AC2 — quiet no-op (unit + smoke).** Mouse click, touch tap, `Enter` и
|
||||
`Space` по marker с `none` не открывают поверхности, не вызывают service/WS,
|
||||
toast, confirmation, press animation или activity stamp.
|
||||
- **AC3 — независимые жесты (smoke).** Long press всё ещё открывает карточку
|
||||
House Plan, right click — HA more-info, а клик по тому же marker в Редакторе
|
||||
устройств — настройки marker.
|
||||
- **AC4 — presentation parity (unit + visual assertion).** До и после выбора
|
||||
совпадают DOM/classes/face/state/pulse/badge/LQI/Glow/tooltip/hit area и
|
||||
hover/focus; новый disabled-стиль не появляется.
|
||||
- **AC5 — defaults и legacy (unit).** Absent/`null`/`""` оставляют Toggle для
|
||||
primary light и Info для остальных; `cover` проецируется в Toggle; неизвестный
|
||||
token проецируется в Info; untouched Open → Save сохраняет исходный literal.
|
||||
- **AC6 — соседние действия (unit + smoke).** `info`, `more-info`, `toggle` и
|
||||
`run` выполняют прежние ветки, а Toggle/Run fields и confirmation видны только
|
||||
при соответствующих effective actions.
|
||||
- **AC7 — backend и transfer (backend).** Backend принимает `none` и все
|
||||
`TAP_ACTIONS`, отклоняет произвольный новый изменённый token; full/space
|
||||
transfer сохраняет `none`, duplicate virtualization удаляет его как раньше.
|
||||
- **AC8 — i18n и docs (unit + docs gate).** Четыре словаря имеют паритет,
|
||||
обе версии руководства описывают `none` и неизменные long/right gestures,
|
||||
compatibility/architecture фиксируют literal и downgrade.
|
||||
- **AC9 — гейты и бюджет (commands).** В цикле реализации зелёные
|
||||
`npx tsc --noEmit`, `npm test`, `npm run build` и целевой backend pytest;
|
||||
`no-new-any`, i18n/docs checks и browser smoke проходят на обязательном
|
||||
предрелизном прогоне. Существенного роста initial/editor bundle нет.
|
||||
|
||||
## План автотестов
|
||||
|
||||
- Расширить таблицу `projectedTapAction`: explicit `none` для light/switch и
|
||||
неизменные absent/legacy/unknown варианты.
|
||||
- Добавить целевой тест click dispatcher с шпионами на `_infoCard`,
|
||||
`_openMoreInfo`, `callService`, `callWS`, `_tapConfirm`, toast,
|
||||
`_startDevicePressFeedback` и `_stampActivity`.
|
||||
- Проверить keyboard reuse того же dispatcher и сохранение `stopPropagation` /
|
||||
`preventDefault` на соответствующих путях.
|
||||
- Проверить editor draft/save/reopen/Cancel и отсутствие Toggle/Run dependent
|
||||
controls для `none`; отдельно — lossless untouched legacy/unknown action.
|
||||
- Расширить cross-language backend test `TAP_ACTIONS` и marker validation.
|
||||
- Добавить import/export fixtures для full, space и duplicate virtualization.
|
||||
- Целевой Playwright smoke: выбрать `none`, Save, reopen, reload, выполнить
|
||||
click/tap/keyboard no-op, long press и contextmenu; убедиться, что marker
|
||||
presentation не изменился.
|
||||
|
||||
Мутанты: не добавить `none` в backend → AC7; спроецировать `none` в `info` →
|
||||
AC1/AC2; поставить no-op после fallback-карточки → AC2; сделать ранний return до
|
||||
`stopPropagation` → AC2; связать `none` с unavailable Toggle → AC2/AC5; скрыть
|
||||
marker или изменить face → AC4.
|
||||
|
||||
## Производительность и безопасность
|
||||
|
||||
No-op — одна константная ветка до capability resolution и сетевых операций.
|
||||
Новых подписок, таймеров, cache entries, DOM-узлов и данных HA нет. Вызовов
|
||||
service/WS для `none` быть не может; backend принимает только точный allowlisted
|
||||
literal. Full performance/golden harness не требуется из-за отсутствия
|
||||
геометрической/визуальной дельты, но штатные предрелизные проверки проекта не
|
||||
отменяются.
|
||||
|
||||
## Release-артефакты
|
||||
|
||||
- Пользовательская запись в `docs/CHANGELOG.md` и `docs/CHANGELOG.ru.md` в том
|
||||
же `User-Visible: yes` коммите, что реализация.
|
||||
- Обновлённые EN/RU user guides, compatibility и architecture.
|
||||
- Новый plan golden не требуется: marker pixels и геометрия не меняются.
|
||||
Открытое native select не является стабильной кроссплатформенной golden-
|
||||
поверхностью; наличие/текст option доказывают DOM unit и Playwright smoke.
|
||||
- Docs screenshot не требуется: существующие изображения не утверждают полный
|
||||
список tap actions; текстовые разделы являются каноническими.
|
||||
- Release/performance/security waiver не требуется; перед beta выполняется
|
||||
общий golden/smoke/performance набор по runbook.
|
||||
|
||||
## Принятые предположения
|
||||
|
||||
- `none` расположен последним в текущем порядке selector; порядок можно свободно
|
||||
поменять до реализации без изменения контракта.
|
||||
- Controls остаются редактируемыми, потому что используются presentation/light
|
||||
graph независимо от короткого действия.
|
||||
- Существующая политика соседних полей сохраняется: смена с `run` на любое
|
||||
другое действие очищает `tap_target` при записи, а общие настройки marker не
|
||||
сбрасываются только из-за выбора `none`.
|
||||
- Marker остаётся focusable и визуально интерактивным ради доступных hover,
|
||||
tooltip, long-press и context-menu путей; отдельный disabled appearance не
|
||||
вводится.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Спецификации задач
|
||||
|
||||
Актуально на 2026-08-29.
|
||||
Актуально на 2026-08-30.
|
||||
|
||||
GitHub Issues и GitHub Projects (v2) остаются единственным каноническим backlog проекта. Этот каталог содержит развёрнутые ТЗ: каждое ТЗ ссылается на issue, а issue — на соответствующий файл. Статус, приоритет и факт завершения меняются только в GitHub.
|
||||
|
||||
@@ -114,6 +114,7 @@ GitHub Issues и GitHub Projects (v2) остаются единственным
|
||||
| [#84](https://github.com/Matysh/houseplan-card/issues/84) Источник «Всегда» без сущности HA + [#88](https://github.com/Matysh/houseplan-card/issues/88) ведущая сущность | [084-passive-forced-light-sources.md](084-passive-forced-light-sources.md) |
|
||||
| [#90](https://github.com/Matysh/houseplan-card/issues/90) Управляемый бейдж со значением | [090-device-value-badge.md](090-device-value-badge.md) |
|
||||
| [#378](https://github.com/Matysh/houseplan-card/issues/378) Выбираемый источник для режима «Значение + состояние» | [378-value-face-source.md](378-value-face-source.md) |
|
||||
| [#381](https://github.com/Matysh/houseplan-card/issues/381) Действие по нажатию «Ничего не делать» | [381-no-op-tap-action.md](381-no-op-tap-action.md) |
|
||||
| [#94](https://github.com/Matysh/houseplan-card/issues/94) Универсальное действие «Переключить состояние» | [094-universal-state-toggle.md](094-universal-state-toggle.md) |
|
||||
| [#101](https://github.com/Matysh/houseplan-card/issues/101) Плавный переход View ↔ редакторы | [101-view-editor-transition.md](101-view-editor-transition.md) |
|
||||
| [#107](https://github.com/Matysh/houseplan-card/issues/107) Переключение виртуального источника света «Всегда» | [107-virtual-light-toggle.md](107-virtual-light-toggle.md) |
|
||||
|
||||
Reference in New Issue
Block a user