feat(actions): implement ActionMapper config-driven gesture→action lookup

This commit is contained in:
pyr0ball 2026-04-26 21:21:51 -07:00
parent 51a22cbe47
commit 5af689389e
2 changed files with 48 additions and 0 deletions

20
merlin/actions/mapper.py Normal file
View file

@ -0,0 +1,20 @@
"""ActionMapper — translates gesture event names to action names via config."""
from __future__ import annotations
from typing import Optional
from merlin.config import MerlinConfig
class ActionMapper:
def __init__(self, config: MerlinConfig) -> None:
self._mappings = dict(config.mappings)
def map(self, gesture: str) -> Optional[str]:
"""Return the action name for a gesture, or None if unmapped."""
return self._mappings.get(gesture)
def reload(self, config: MerlinConfig) -> None:
"""Replace mappings from a new config (e.g. after user edits in UI)."""
self._mappings = dict(config.mappings)

View file

@ -0,0 +1,28 @@
from merlin.actions.mapper import ActionMapper
from merlin.config import MerlinConfig
def test_maps_known_gesture():
config = MerlinConfig()
mapper = ActionMapper(config)
assert mapper.map("left_blink") == "left_click"
def test_returns_none_for_unknown_gesture():
config = MerlinConfig()
mapper = ActionMapper(config)
assert mapper.map("nonexistent_gesture") is None
def test_custom_mapping_overrides_default():
config = MerlinConfig(mappings={"left_blink": "key_enter"})
mapper = ActionMapper(config)
assert mapper.map("left_blink") == "key_enter"
def test_reload_picks_up_new_config():
config = MerlinConfig()
mapper = ActionMapper(config)
config.mappings["head_nod"] = "double_click"
mapper.reload(config)
assert mapper.map("head_nod") == "double_click"