buffr default keymap (page mode)

Reference for the default page-mode bindings shipped by buffr_modal::Keymap::default_bindings.

Leader key: the default is a single space (general.leader = " " in Config), so the one <leader> binding below (<leader>pPinTab) is typed as <Space>p out of the box. Set [general] leader = "\\" for the vim convention; build_keymap feeds that character to Keymap::default_bindings, so every <leader> chord follows the config. (buffr --audit-keymap prints the raw table strings, so a leader chord shows as the literal token <leader>p rather than the key you actually press — Keymap::audit_default_bindings ignores the leader it is handed. Cosmetic only.)

Defaults mirror Vieb (stock app/renderer/input.js). Intentional divergences are flagged inline with [buffr].

The engine speaks vim-flavoured chord notation. <C-...> = Ctrl, <S-...> = Shift, <M-...> / <A-...> = Alt, <D-...> = Super (Cmd on macOS), <leader> = configured leader char.

Modes

ModeTriggerNotes
Normalinitial / <Esc>Default; bindings below.
Visualleft-drag ≥ 4 pxText selection in the page. y yanks, <Esc> cancels.
Command: or eCommand line / omnibar focused. <Esc> returns.
Hintf / FDOM hint overlay active. <Esc> returns.
Pending(transient)Multi-key prefix in flight. Not user-bindable.
Inserttext-field focusForwarded to Engine::feed_edit_mode_key.

Count prefix

  • Leading digits accumulate: 5j scrolls down 5 lines, 12G jumps to line 12 (when implemented). 0 alone is bindable (vim convention: column 0); digits 1-9 always start a count.

Ambiguity timeout

When a binding is a prefix of a longer one (g vs gg), the engine waits up to Engine::timeout() (default 1000ms). If the user does not extend the prefix, the shorter action fires.

Normal-mode bindings

Scroll

KeysActionNotes
jScrollDown(1)
kScrollUp(1)
hScrollLeft(1)
lScrollRight(1)
<Down>ScrollDown(1)
<Up>ScrollUp(1)
<Left>ScrollLeft(1)
<Right>ScrollRight(1)
<C-e>ScrollDown(1)
<C-y>ScrollUp(1)
<C-d>ScrollHalfPageDown
<C-u>ScrollHalfPageUp
<C-f>ScrollFullPageDown
<C-b>ScrollFullPageUp
<PageDown>ScrollFullPageDown
<PageUp>ScrollFullPageUp
ggScrollTop
GScrollBottom
<Home>ScrollTop
<End>ScrollBottom

Tabs

KeysActionNotes
HTabPrev[buffr] Vieb uses H for history-back.
LTabNext[buffr] Vieb uses L for history-forward.
gtTabNext
gTTabPrev
oTabNewRight[buffr] Open tab to the right of active; omnibar opens so you type a URL.
OTabNewLeft[buffr] Open tab to the left of active; omnibar opens so you type a URL.
<C-t>TabNewRightConventional-browser alternate for o.
dTabClose
<C-w>TabCloseDeliberately a leaf — there are no <C-w>X prefix chords.
uReopenClosedTabStack-based: repeated u undoes successive closes.
<C-S-t>ReopenClosedTabConventional-browser alternate for u.
<leader>pPinTabDefault leader is space, i.e. <Space>p.
pPasteUrl { after: true }Open the clipboard URL in a tab to the right. Non-URL clipboard = no-op.
PPasteUrl { after: false }Same, to the left.
<C-S-h>MoveTabLeftShuffle the active tab one slot left.
<C-S-l>MoveTabRightShuffle the active tab one slot right.

TabClose (and :q) close the active tab. The application only exits when the last tab is gone. PinTab toggles the pinned bit (pinned tabs sort to the front — pin does not prevent close). There is no PageAction for duplicating a tab — the capability exists only as the "Duplicate Tab" entry in the tab-strip right-click menu (ContextMenuItem::TabDuplicate). See multi-tab.md and context-menu.md.

History

KeysActionNotes
JHistoryBack[buffr] Vieb uses J for next-tab.
KHistoryForward[buffr] Vieb uses K for previous-tab.
<C-o>HistoryBack
<C-i>HistoryForward

Reload / stop

KeysActionNotes
rReload
RReloadHard
<C-r>ReloadHard

Note: <Esc> is not bound to StopLoading in Normal mode; it is ExitInsertMode — it blurs the focused DOM element and resets the engine to Normal unconditionally.

<C-c> is StopLoading (a buffr extension); y is YankUrl.

Omnibar / command line

KeysActionNotes
eOpenOmnibar
<C-l>OpenOmnibar
:OpenCommandLine
;OpenCommandLine[buffr] alias; Vieb uses ; for hints.

Hints

KeysAction
fEnterHintMode
FEnterHintModeBackground

Find

KeysAction
/Find { forward: true }
?Find { forward: false }
nFindNext
NFindPrev

Yank

KeysActionNotes
yYankUrl

Zoom

KeysActionNotes
+ZoomIn
=ZoomInMatches Chromium's Ctrl+= alias for zoom-in.
-ZoomOut
_ZoomOut
0ZoomReset
)ZoomReset
<C-0>ZoomResetVieb-style alias for the conventional chord.

DevTools

KeysAction
<F12>OpenDevTools
<C-S-i>OpenDevTools

Insert mode

KeysActionNotes
iFocusFirstInput[buffr] Same as gi — JS focuses first form input; focusin auto-promotes to Insert.
giFocusFirstInput[buffr] Vieb's insertAtFirstInput. JS focuses first input; focusin auto-promotes.
<Esc>ExitInsertModeBlurs the active DOM element; resets edit state and engine to Normal unconditionally.

EnterInsertMode remains in the action enum for advanced user config (e.g. [keymap.normal] "<F2>" = "enter_insert_mode") but is unbound by default.

Visual-mode bindings

Visual mode is entered automatically by dragging with the left mouse button in the page area (more than a 4 px threshold); the embedded CEF view renders the selection itself. There is no key that enters Visual mode by default.

KeysActionNotes
yYankSelectionCopies the page selection via CEF's native frame.copy().
<C-c>YankSelectionSame.
<Esc>EnterMode(Normal)Cancels without yanking.

Hint- and Command-mode bindings

ModeKeysAction
Hint<Esc>EnterMode(Normal)
Command<Esc>EnterMode(Normal)

Every other keystroke in those modes is consumed by the hint filter or the input bar — see the overlay table below.

Mode transitions

The engine reads the resolved [PageAction] and auto-transitions:

  • OpenOmnibar, OpenCommandLineCommand
  • EnterHintMode, EnterHintModeBackgroundHint
  • EnterInsertModeInsert (trie bypassed; feed_edit_mode_key takes over)
  • ExitInsertModeNormal (blurs DOM active element; clears EditFocus)
  • EnterMode(m)m

<Esc> is bound in Normal to ExitInsertMode and in Visual / Command / Hint to EnterMode(Normal) so every mode has a guaranteed escape hatch.

In-overlay shortcuts (command line / omnibar)

When : opens the command line or e/<C-l> opens the omnibar, all keystrokes route to the input bar instead of the page-mode trie. The bindings below mirror readline / vim's command-line conventions.

KeysAction
<Esc> / <C-c>Cancel — close overlay, return to Normal mode.
<CR>Confirm — dispatch the command or navigate to the URL.
<Tab> / <Down>Move suggestion selection one row down (clamps at last).
<S-Tab> / <Up>Move suggestion selection one row up (clears at top).
<Left> / <Right>Move cursor through the buffer.
<BS>Delete the codepoint before the cursor.
<C-u>Clear the entire buffer.
<C-w>Delete the word before the cursor.
<C-v>Paste clipboard text, with CR/LF stripped.
<Space>Literal space (the toolkit reports it as a named key).

In-prompt shortcuts (permissions)

When a page asks for a permission (camera, microphone, geolocation, notifications, clipboard, MIDI sysex, …) buffr surfaces a prompt strip and routes keystrokes to it until the request is resolved. The page content does not see these keys.

KeysAction
a / yAllow once (no row written).
A / YAllow + remember for this origin.
d / nDeny once (no row written).
D / NDeny + remember for this origin.
sSynonym for D — deny + remember.
<Esc>Defer — Dismiss / cancel(), no persistence.

If multiple requests pile up they queue; the statusline shows (N more pending) on the prompt strip. After resolving one the next prompt appears on the following frame.

See crates/buffr-permissions/README.md for the decision-precedence rules.

Mouse / context menu

Gesture / inputAction
Right-click (page area)Open context menu. Items depend on the hit-test target (see context-menu.md).
<Up> / <Down>Move row selection in the open menu.
<Enter>Activate selected menu item.
<Esc>Dismiss menu without action.
Click outside panelDismiss menu without action.
Any non-navigation keyDismiss menu and pass key to normal page-mode dispatcher.
Left-click (tab strip)Switch tab and close the omnibar overlay (parity with gt/gT).
Two-finger swipe rightHistoryBack (≥ 150 px horizontal, 2× more horiz than vertical).
Two-finger swipe leftHistoryForward (same threshold).

Vieb chords intentionally NOT mapped

The following Vieb normal-mode actions have no buffr PageAction equivalent and are skipped until those features land:

Vieb chord(s)Vieb actionReason not mapped
vstartVisualSelectVisual mode is mouse-entered; no keyboard entry chord yet
<C-v>toVisualModeSame
<C-p>previousTab (pointer)Pointer mode not implemented
<C-n>nextTab (pointer)Pointer mode not implemented
m / MsetMark / restoreMarkMarks not implemented
<C-s>downloadLinkNo DownloadLink action
s / StoSearchMode (special)Covered by / / ?
<C-a> / <C-x>incrementUrl / decrementNo URL increment action
<kPlus> / <kMinus>zoomIn / zoomOutkPlus/kMinus not a named key in buffr parser; covered by +/-
<C-Tab> / <C-S-Tab>nextTab / prevTabCovered by H/L and gt/gT

Note that p / P, u, <C-t>, and <C-f> are bound — see the Tabs and Scroll tables above. Only their Vieb semantics differ: buffr's p/P paste a clipboard URL into a new tab, u reopens the last closed tab, <C-t> opens a tab to the right, and <C-f> is a full-page scroll (Vieb's pointer-mode variant is what is unmapped).

Customising

Bindings come from a static table in crates/buffr-modal/src/keymap.rs. User overrides go in ~/.config/buffr/config.toml under [keymap.<mode>] — see config.md for the full schema and action notation. The watcher reloads the keymap on file changes (250ms debounced).