# Test hint badge with string
from fasthtml.common import to_xml
hint_badge = render_hint_badge("Space", "Select")
html = to_xml(hint_badge)
assert "Space" in html
assert "Select" in html
assert "badge" in html
# Test hint badge with icon
icon_hint = create_nav_icon_hint("arrow-down-up", "Navigate")
html = to_xml(icon_hint)
assert "Navigate" in html
assert "svg" in html # Icon is an SVG
# Test auto_icon conversion
delete_hint = render_hint_badge("Delete", "Remove item", auto_icon=True)
html = to_xml(delete_hint)
assert "svg" in html # Should convert to trash icon
assert "Remove item" in html
# Test get_key_icon
assert get_key_icon("shift") is not None
assert get_key_icon("delete") is not None
assert get_key_icon("unknown_key") is None
# Test modifier key hint (Shift + arrow)
shift_nav = create_modifier_key_hint("shift", lucide_icon("arrow-down-up", size=3), "Reorder")
html = to_xml(shift_nav)
assert "svg" in html
assert "Reorder" in htmlKeyboard Hints
Components for displaying keyboard shortcut hints to users.
Single Hint Badge
create_modifier_key_hint
def create_modifier_key_hint(
modifier:str, # modifier key name (e.g., "shift", "ctrl")
key_icon_or_text:Union[str, FT], # the main key icon or text
description:str, # action description
style:str='ghost', # badge style
)->Div: # hint badge with modifier + key
Create a hint badge with a modifier key and main key.
render_hint_badge
def render_hint_badge(
key_display:Union[str, FT], # formatted key string or icon component
description:str, # action description
style:str='ghost', # badge style (ghost, outline, soft, dash)
auto_icon:bool=False, # auto-convert known keys to icons
)->Div: # hint badge component
Render a single keyboard hint as a badge.
get_key_icon
def get_key_icon(
key_name:str, # key name to look up (case-insensitive)
size:int=3, # icon size
)->FT | None: # icon component or None if no icon mapping
Get a lucide icon for a key name, if one exists.
Hint Group
render_hint_group
def render_hint_group(
group_name:str, # group header text
hints:list[tuple[str, str]], # list of (key_display, description) tuples
badge_style:str='ghost', # badge style for this group
)->Div: # group container with header and hints
Render a group of related keyboard hints.
# Test hint group with string keys
group = render_hint_group(
"Navigation",
[("W/S", "Move"), ("A/D", "Switch")]
)
html = to_xml(group)
assert "Navigation" in html
assert "Move" in html
assert "Switch" in htmlHints from Actions
group_actions_by_hint_group
def group_actions_by_hint_group(
actions:tuple[KeyAction, ...], # actions to group
)->dict[str, list[KeyAction]]: # grouped actions
Group actions by their hint_group attribute.
render_hints_from_actions
def render_hints_from_actions(
actions:tuple[KeyAction, ...], # actions to display hints for
badge_style:str='ghost', # badge style
)->Div: # container with all hint groups
Render keyboard hints from action configurations.
# Test hints from actions
actions = (
KeyAction(key=" ", htmx_trigger="x", description="Select", hint_group="Selection"),
KeyAction(key="Delete", htmx_trigger="y", description="Remove", hint_group="Actions"),
KeyAction(key="Enter", js_callback="z", description="Open", hint_group="Actions"),
KeyAction(key="Backspace", htmx_trigger="w", description="Delete", show_in_hints=False), # Hidden
)
hints_component = render_hints_from_actions(actions)
html = to_xml(hints_component)
assert "Selection" in html
assert "Actions" in html
assert "Select" in html
assert "Remove" in html
assert "Delete" not in html # show_in_hints=FalseFull Keyboard Hints
render_keyboard_hints
def render_keyboard_hints(
manager:ZoneManager, # the zone manager
include_navigation:bool=True, # include navigation hints
include_zone_switch:bool=True, # include zone switching hints
badge_style:str='ghost', # badge style
container_id:str='kb-hints', # container element ID
use_icons:bool=True, # use lucide icons for nav hints
)->Div: # complete hints component
Render complete keyboard hints for a zone manager.
# Test full keyboard hints with icons (default)
from cjm_fasthtml_keyboard_navigation.core.focus_zone import FocusZone
zone1 = FocusZone(id="z1")
zone2 = FocusZone(id="z2")
manager = ZoneManager(
zones=(zone1, zone2),
actions=(
KeyAction(key=" ", htmx_trigger="toggle", description="Select", hint_group="Selection"),
)
)
hints = render_keyboard_hints(manager)
html = to_xml(hints)
assert 'id="kb-hints"' in html
assert "Navigate" in html
assert "Switch Panel" in html # Two zones = show zone switch
assert "Select" in html
assert "svg" in html # Icons are SVGs
# Test without icons
hints_no_icons = render_keyboard_hints(manager, use_icons=False)
html_no_icons = to_xml(hints_no_icons)
assert "↑/↓" in html_no_icons # Text arrows when icons disabled# Single zone = no zone switch hint
single_manager = ZoneManager(zones=(zone1,), actions=())
hints = render_keyboard_hints(single_manager)
html = to_xml(hints)
assert "Switch Panel" not in html