feat: add no-op marker tap action

Issue: #381
User-Visible: yes
This commit is contained in:
Sergey Matyunin
2026-08-30 07:48:16 +00:00
committed by claude[bot]
parent 20cd3241c0
commit ea7803230a
39 changed files with 286 additions and 168 deletions
+4 -3
View File
@@ -15,7 +15,7 @@ import {
type ResolvedLightSource,
type IncomingLightControl,
} from './devices';
import { COVER_GUARDED_CLASSES, isControllable } from './logic';
import { COVER_GUARDED_CLASSES, isControllable, type DeviceTapAction } from './logic';
import type { DevItem } from './types';
import {
isManualVirtualLightMarker,
@@ -463,9 +463,10 @@ function emptyIntent(origin: ToggleOrigin, reason: ToggleNoneReason): ResolvedTo
export function projectedTapAction(
persisted: string | null | undefined,
defaultDomain: string | null | undefined,
): 'info' | 'more-info' | 'toggle' | 'run' {
): DeviceTapAction {
if (persisted === 'cover' || persisted === 'toggle') return 'toggle';
if (persisted === 'more-info' || persisted === 'run' || persisted === 'info') return persisted;
if (persisted === 'more-info' || persisted === 'run'
|| persisted === 'info' || persisted === 'none') return persisted;
// Only actual absence activates the light default. An unknown persisted
// token is invalid data, not an absent choice: fail closed to the local card
// so the UI projection and `toggleOriginOf()` cannot disagree.
+3
View File
@@ -5204,6 +5204,9 @@ export class HouseplanCard extends LitElement {
const action = projectedTapAction(
actionDevice.tapAction, actionDevice.primary?.split('.')[0],
);
// An explicit no-op still owns the click: propagation was stopped and the
// current marker was resolved, but no capability, feedback or UI path runs.
if (action === 'none') return;
// the accidental-tap guard (owner's spec 2026-07-29): any state-changing
// action — toggle or run — may ask first. The dialog is ours, not the
// browser confirm(), so it works and looks right on a wall tablet.
+1
View File
@@ -729,6 +729,7 @@
"marker.ha_registry_limited": "Das vollständige Home Assistant-Register ist für diesen Benutzer nicht verfügbar. Das nicht verifizierte Objekt kann vorerst nicht angezeigt oder verwendet werden.",
"marker.delete_tip": "Löschen Sie das Gerät vollständig aus dem Plan und allen Aggregaten. Sie können es später wieder hinzufügen.",
"tap.run": "Führen Sie Automation/Skript/Szene aus",
"tap.none": "Nichts tun",
"marker.run_target_label": "Was ausführen",
"marker.run_search_ph": "Suchen: Automation, Skript oder Szene…",
"marker.run_target_gone": "Ziel {id} nicht gefunden — erneut auswählen",
+1
View File
@@ -729,6 +729,7 @@
"marker.ha_registry_limited": "The full Home Assistant registry is unavailable to this user. The unverified object cannot be shown or used for now.",
"marker.delete_tip": "Completely delete the device from the plan and every aggregate. You can add it again later.",
"tap.run": "Run automation/script/scene",
"tap.none": "Do nothing",
"marker.run_target_label": "What to run",
"marker.run_search_ph": "Search: automation, script or scene…",
"marker.run_target_gone": "Target {id} not found — pick again",
+1
View File
@@ -729,6 +729,7 @@
"marker.ha_registry_limited": "Le registre Home Assistant complet n’est pas accessible à cet utilisateur. L’objet non vérifié ne peut pas être affiché ni utilisé pour le moment.",
"marker.delete_tip": "Supprimer complètement l’appareil du plan et de tous les agrégats. Vous pourrez l’ajouter de nouveau plus tard.",
"tap.run": "Exécuter une automatisation/un script/une scène",
"tap.none": "Ne rien faire",
"marker.run_target_label": "Élément à exécuter",
"marker.run_search_ph": "Rechercher : automatisation, script ou scène…",
"marker.run_target_gone": "Cible {id} introuvable — choisissez-en une autre",
+1
View File
@@ -729,6 +729,7 @@
"marker.ha_registry_limited": "Полный реестр Home Assistant недоступен этому пользователю. Неподтверждённый объект временно нельзя показывать или использовать.",
"marker.delete_tip": "Полностью удалить устройство с плана и из всех расчётов. Позже его можно добавить заново.",
"tap.run": "Запустить автоматизацию/скрипт/сцену",
"tap.none": "Ничего не делать",
"marker.run_target_label": "Что запускать",
"marker.run_search_ph": "Поиск: автоматизация, скрипт или сцена…",
"marker.run_target_gone": "Цель {id} не найдена — выберите заново",
+2 -1
View File
@@ -907,7 +907,8 @@ export function normalizeDeviceDisplay(value: unknown): DeviceDisplayMode {
? value as DeviceDisplayMode : 'badge';
}
/** Current editor choices. `cover` remains a read/backend compatibility token. */
export const TAP_ACTIONS = ['info', 'more-info', 'toggle', 'run'] as const;
export const TAP_ACTIONS = ['info', 'more-info', 'toggle', 'run', 'none'] as const;
export type DeviceTapAction = typeof TAP_ACTIONS[number];
/** Persisted space-level data fills. `none` remains a read-compatibility token;
* the space editor projects it to `custom` because every room has a visible
* base colour and the user can now choose that colour directly. */