# keyboard_config


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

## Keyboard Parts Builder

Returns `(zone, actions, modes)` tuple for assembly into a
`KeyboardManager`. Review uses simple navigation only (no sub-modes like
segmentation’s split mode).

------------------------------------------------------------------------

### create_review_kb_parts

``` python

def create_review_kb_parts(
    ids:CardStackHtmlIds, # Card stack HTML IDs
    button_ids:CardStackButtonIds, # Card stack button IDs for navigation
    config:CardStackConfig, # Card stack configuration
)->Tuple: # (zone, actions, modes)

```

*Create review-specific keyboard building blocks.*

## Zone Manager Factory

Creates a complete `ZoneManager` for the review step (single-zone, no
zone switching).

------------------------------------------------------------------------

### create_review_keyboard_manager

``` python

def create_review_keyboard_manager(
    ids:CardStackHtmlIds, # Card stack HTML IDs
    button_ids:CardStackButtonIds, # Card stack button IDs for navigation
    config:CardStackConfig, # Card stack configuration
)->ZoneManager: # Configured keyboard zone manager

```

*Create the keyboard zone manager for the review step.*

``` python
# Speed cycle KeyActions wire correctly into the review zone
from cjm_fasthtml_card_stack.core.html_ids import CardStackHtmlIds as _IDs
from cjm_fasthtml_card_stack.core.button_ids import CardStackButtonIds as _BtnIds
from cjm_fasthtml_card_stack.core.config import CardStackConfig as _Cfg

_zone, _actions, _modes = create_review_kb_parts(
    ids=_IDs(prefix="review-cs"),
    button_ids=_BtnIds(prefix="review-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 is not None, "Shift+ArrowUp binding missing"
assert _dn is not None, "Shift+ArrowDown binding missing"

# Scoped to the review card-stack zone, wired to web-audio's cycle helpers,
# grouped with the other audio bindings in the hint panel
assert _up.js_callback == "cycleReviewSpeedUp"
assert _dn.js_callback == "cycleReviewSpeedDown"
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 is not None and _nav_up.htmx_trigger, "Plain ArrowUp should still be the card-stack nav binding"
assert _nav_dn is not None and _nav_dn.htmx_trigger, "Plain ArrowDown should still be the card-stack nav binding"

print("Shift+Arrow speed cycle bindings test passed")
```

    Shift+Arrow speed cycle bindings test passed
