Alignment-specific keyboard building blocks for assembly into a shared ZoneManager
Keyboard Parts Builder
Returns (zone, actions, modes) tuple for assembly into a shared ZoneManager by the combined-level keyboard config. No sub-modes needed (unlike decomp’s split mode).
Create alignment-specific keyboard building blocks.
# Speed cycle KeyActions wire correctly into the alignment zonefrom cjm_fasthtml_card_stack.core.html_ids import CardStackHtmlIds as _IDsfrom cjm_fasthtml_card_stack.core.button_ids import CardStackButtonIds as _BtnIdsfrom cjm_fasthtml_card_stack.core.config import CardStackConfig as _Cfg_zone, _actions, _modes = create_align_kb_parts( ids=_IDs(prefix="align-cs"), button_ids=_BtnIds(prefix="align-cs"), config=_Cfg(),)_by_key = {(a.key, frozenset(a.modifiers)): a for a in _actions}_up = _by_key.get(("ArrowUp", frozenset({"shift"})))_dn = _by_key.get(("ArrowDown", frozenset({"shift"})))assert _up isnotNone, "Shift+ArrowUp binding missing"assert _dn isnotNone, "Shift+ArrowDown binding missing"# Scoped to the alignment card-stack zone, wired to web-audio's cycle helpers,# grouped with the other audio bindings in the hint panelassert _up.js_callback =="cycleAlignSpeedUp"assert _dn.js_callback =="cycleAlignSpeedDown"assert _up.zone_ids == (_zone.id,)assert _dn.zone_ids == (_zone.id,)assert _up.hint_group =="Audio"assert _dn.hint_group =="Audio"# Plain ArrowUp/ArrowDown (card-stack nav) must NOT collide — distinct modifiers frozenset_nav_up = _by_key.get(("ArrowUp", frozenset()))_nav_dn = _by_key.get(("ArrowDown", frozenset()))assert _nav_up isnotNoneand _nav_up.htmx_trigger, "Plain ArrowUp should still be the card-stack nav binding"assert _nav_dn isnotNoneand _nav_dn.htmx_trigger, "Plain ArrowDown should still be the card-stack nav binding"print("Shift+Arrow speed cycle bindings test passed")