Skip to content

ShortcutInfoModal

ShortcutInfoModal is the shortcut help modal. It is useful for showing page, modal, or workspace shortcuts grouped into sections, and it hands overflow content to an internal ScrollView when the content is taller than the modal.

shortcut info modal demo

This recording is generated by docs/tapes/shortcut-info-modal.tape. The example first moves the background task list, then opens the shortcut manual. Pressing j several times inside the modal scrolls the manual, but the background selection does not move further. After closing, pressing j again makes the background list respond.

cargo run --example shortcut_info_modal

In the background state, press j/k to move and i to open shortcut help. Inside the modal, press j/k to scroll help content, Esc or i to close, and q to quit.

ShortcutInfoModal(
    open: shortcuts_open.get(),
    title: Line::from("Shortcut reference"),
    sections: vec![
        ShortcutInfoSection::new(
            "Navigation",
            [
                ("Move selection down", "j / Down"),
                ("Move selection up", "k / Up"),
            ],
        ),
        ShortcutInfoSection::new(
            "Modal",
            [
                ("Scroll reference", "j / k"),
                ("Close reference", "Esc / i"),
            ],
        ),
    ],
    on_close: move |_: ()| {
        shortcuts_open.set(false);
    },
)

ShortcutInfoSection::new accepts any entries that can convert into ShortcutInfo. The most common form is (&str, &str): the left side is the action description, and the right side is the shortcut combination.

Like the other built-in modals, this component creates an exclusive input layer internally and passes that same layer to the low-level Modal:

let layer = hooks.use_input_layer(open, true);

hooks.use_event_handler(EventScope::Layer(layer), EventPriority::High, move |event| {
    if close_keys.contains(&key.code) {
        on_close(());
        return EventResult::Consumed;
    }

    EventResult::Ignored
});

Modal(open: open, layer: Some(layer)) {
    ScrollView {
        // shortcut sections
    }
}

The key difference is the final EventResult::Ignored. Close keys are consumed by the modal; other keys continue to the ScrollView on the same layer, so j/k, arrow keys, and page keys can still scroll the help content. Because this layer has blocks_lower=true, even if ScrollView also returns Ignored, the background layer still does not receive the same keys.

The default close keys are Esc, i, and I:

ShortcutInfoModal(
    close_keys: vec![KeyCode::Esc, KeyCode::Char('i'), KeyCode::Char('I')],
    close_hint: Line::from("j/k scroll | Esc / i close").centered(),
)

If your page already uses i for another foreground action, replace close_keys. The bottom hint is controlled by close_hint and is not generated from close_keys.

ShortcutInfoModal exposes the low-level Modal size, mask, and main text styles:

ShortcutInfoModal(
    width: Constraint::Length(78),
    height: Constraint::Length(13),
    style: Style::new().dim(),
    border_style: Style::new().yellow(),
    title_style: Style::new().yellow().bold(),
    section_title_style: Style::new().cyan().bold(),
    description_style: Style::new(),
    key_style: Style::new().yellow(),
)

The content area already wraps a ScrollView. If you need search, filtering, or dynamically generated help items, just regenerate sections. If the modal also needs an input box, list selection, or multi-step interaction, compose the low-level Modal + use_input_layer instead.

For ordinary messages, see AlertModal. When the user needs to choose, use ConfirmModal.