Keybindings#

The ngapp.keybindings module provides a reusable keybinding manager with a floating mode indicator and a help overlay. It supports global shortcuts, per-component bindings, two-level modal keys, and visibility-based activation.

Quick start#

from ngapp.app import App
from ngapp.components import Div
from ngapp.keybindings import KeybindingManager, keybinding_styles

class MyApp(App):
    def __init__(self):
        super().__init__()
        self.kb = KeybindingManager(self)
        self.kb.add("h", self.kb.toggle_help, "Show shortcuts", "General")
        self.add_keybinding("escape", lambda e: self.kb.on_escape())

        # Include the indicator and overlay in your layout
        super().__init__(
            Div("Hello"),
            self.kb.indicator,
            self.kb.help_overlay,
        )
        keybinding_styles.inject(self)

Global bindings#

Use kb.add(key, callback, description, group) to register shortcuts that are always active regardless of which component is visible:

kb.add("ctrl+s", self.save, "Save", "General")
kb.add("ctrl+b", self.toggle_sidebar, "Toggle sidebar", "Panels")

Component bindings (exclusive)#

If your app shows one main component at a time (e.g. tabs), use kb.set_component(comp) to swap keybindings when the active view changes.

The component must implement get_keybindings():

class MyView:
    def get_keybindings(self):
        return {
            "flat": [
                ("w", self.toggle_wireframe, "Toggle wireframe", "Display"),
            ],
            "modes": [
                ("s", "Show", [
                    ("e", self.toggle_edges, "Toggle edges"),
                    ("v", self.toggle_volumes, "Toggle volumes"),
                ]),
            ],
        }

Then in your app:

def on_tab_changed(self, comp):
    self.kb.set_component(comp)

Component bindings (visibility-based)#

For apps where multiple panels can be visible simultaneously, use activate_component / deactivate_component:

# When component becomes visible
self.kb.activate_component(panel)

# When component is hidden
self.kb.deactivate_component(panel)

Multiple components can be active at the same time. Their bindings are merged (last-activated wins on conflicts).

Custom theme#

Pass a Theme to customize the indicator/overlay colors:

from ngapp.style import Theme
from ngapp.keybindings import KeybindingManager

my_theme = Theme(accent="#FF6B35", hint="#94a3b8", muted="#666", border="#ddd")
kb = KeybindingManager(app, theme=my_theme)

API reference#

Reusable keybinding manager with floating indicator and help overlay.

Provides KeybindingManager — a two-layer (global + component) keybinding dispatcher with a floating mode indicator and a help overlay showing all registered shortcuts.

Usage:

from ngapp.keybindings import KeybindingManager

class MyApp(App):
    def __init__(self):
        super().__init__()
        self.kb = KeybindingManager(self)
        self.kb.add("h", self.kb.toggle_help, "Show shortcuts", "General")
        self.add_keybinding("escape", lambda e: self.kb.on_escape())
class ngapp.keybindings.HelpOverlay(manager)#

Floating overlay showing all registered keybindings.

hide()#
show()#
class ngapp.keybindings.KeybindingManager(app, after_action=None, theme=None)#

Two-layer keybinding manager with floating indicator and help overlay.

Global bindings (add) are always active. Component bindings come from comp.get_keybindings() and are swapped on every set_component() call so only relevant shortcuts are shown.

get_keybindings() returns:

{
    "flat": [(key, callback, description, group), ...],
    "modes": [(trigger_key, mode_name, [(key, cb, desc), ...]), ...],
}

Parameters#

appApp or Component

The app (or root component) that owns the keybindings. Must support add_keybinding(key, callback).

after_actioncallable, optional

Called after every keybinding action (e.g. to refresh UI).

themeTheme, optional

Custom theme tokens for the indicator colors.

activate_component(comp)#

Activate a component’s keybindings (additive).

Call this when a component becomes visible. Multiple components can be active simultaneously. Use deactivate_component() when the component is hidden.

The component must implement get_keybindings() returning:

{"flat": [(key, cb, desc, group), ...],
 "modes": [(trigger, name, [(key, cb, desc), ...]), ...]}
add(key, callback, description, group='General')#

Register a global keybinding (always active).

deactivate_component(comp)#

Deactivate a component’s keybindings.

Call this when a component becomes hidden.

property entries#
on_escape()#
set_component(comp)#

Swap component bindings (exclusive). Only these + global are active.

This deactivates all previously active components and activates only the given one. Use activate_component() / deactivate_component() for non-exclusive visibility-based activation.

toggle_help()#
class ngapp.keybindings.ModeIndicator(theme=None)#

Floating bar showing available keys in the active submenu.

hide()#
show(mode_name, entries)#