docs: specify no-op marker tap action

Issue: #381
User-Visible: no
This commit is contained in:
Sergey Matyunin
2026-08-30 07:48:16 +00:00
committed by claude[bot]
parent 083038f25f
commit cfb8ce401d
2 changed files with 257 additions and 1 deletions
+255
View File
@@ -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 не
вводится.
+2 -1
View File
@@ -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) |