# Keyboard Support - Test Instructions
## ⚠️ Breaking Change
**Version 2.0** uses HTMX configuration objects instead of simple URL strings. The old format is **not supported**.
**Old format (no longer supported)**:
```javascript
{"A": "/url"}
```
**New format (required)**:
```javascript
{"A": {"hx-post": "/url"}}
```
## Files
- `keyboard_support.js` - Main keyboard support library with smart timeout logic
- `test_keyboard_support.html` - Test page to verify functionality
## Key Features
### Scope Control with `require_inside`
Each combination can declare whether it should only trigger when the focus is **inside** the registered element, or fire **globally** regardless of focus.
| `require_inside` | Behavior |
|-----------------|----------|
| `true` (default) | Triggers only if focus is inside the element or one of its children |
| `false` | Triggers regardless of where the focus is (global shortcut) |
```javascript
// Only fires when focus is inside #tree-panel
add_keyboard_support('tree-panel', '{"esc": {"hx-post": "/cancel", "require_inside": true}}');
// Fires anywhere on the page
add_keyboard_support('app', '{"ctrl+n": {"hx-post": "/new", "require_inside": false}}');
```
**Python usage (`Keyboard` component):**
```python
# Default: require_inside=True — fires only when inside the element
Keyboard(self, _id="-kb").add("esc", self.commands.cancel())
# Explicit global shortcut
Keyboard(self, _id="-kb").add("ctrl+n", self.commands.new_item(), require_inside=False)
```
### Multiple Simultaneous Triggers
**IMPORTANT**: If multiple elements listen to the same combination, all of them whose `require_inside` condition is satisfied will be triggered simultaneously:
```javascript
add_keyboard_support('modal', '{"esc": {"hx-post": "/close-modal", "require_inside": true}}');
add_keyboard_support('editor', '{"esc": {"hx-post": "/cancel-edit", "require_inside": true}}');
add_keyboard_support('sidebar', '{"esc": {"hx-post": "/hide-sidebar", "require_inside": false}}');
// Pressing ESC while focus is inside 'editor':
// - 'modal' → skipped (require_inside: true, focus not inside)
// - 'editor' → triggered ✓
// - 'sidebar' → triggered ✓ (require_inside: false)
```
### Smart Timeout Logic (Longest Match)
The library uses **a single global timeout** based on the sequence state, not on individual elements:
**Key principle**: If **any element** has a longer sequence possible, **all matching elements wait**.
Examples:
**Example 1**: Three elements, same combination
```javascript
add_keyboard_support('elem1', '{"esc": "/url1"}');
add_keyboard_support('elem2', '{"esc": "/url2"}');
add_keyboard_support('elem3', '{"esc": "/url3"}');
// Press ESC → ALL 3 trigger immediately (no longer sequences exist)
```
**Example 2**: Mixed - one has longer sequence
```javascript
add_keyboard_support('elem1', '{"A": "/url1"}');
add_keyboard_support('elem2', '{"A": "/url2"}');
add_keyboard_support('elem3', '{"A": "/url3", "A B": "/url3b"}');
// Press A:
// - elem3 has "A B" possible → EVERYONE WAITS 500ms
// - If B arrives: only elem3 triggers with "A B"
// - If timeout expires: elem1, elem2, elem3 ALL trigger with "A"
```
**Example 3**: Different combinations
```javascript
add_keyboard_support('elem1', '{"A B": "/url1"}');
add_keyboard_support('elem2', '{"C D": "/url2"}');
// Press A: elem1 waits for B, elem2 not affected
// Press C: elem2 waits for D, elem1 not affected
```
The timeout is tied to the **sequence being typed**, not to individual elements.
### Smart Timeout Logic (Longest Match)
Keyboard shortcuts are **disabled** when typing in input fields:
- `` elements
- `