Menus, Tabs and Fields

How mnml's menus print each row's chord for your keymap profile, how a tab strip pages and reaches every tab, and how the picker, the scrollbars and every text field take the mouse and the keyboard.

Around the editor sits a frame of small controls: the menu bar and the right-click menus, the tab strip over each split, the picker that ctrl+p and the palette open, and the one-line fields — a filter, the find bar, the : line. Each of them is one component used everywhere, so a menu behaves the same whether it dropped from the menu bar or opened on a chip, and every text field takes a click, a paste and a selection the same way.

Most of what follows works the same in both keymap profiles. Where a profile changes something — mostly which chord a menu row prints — the page says so.

Where menus come from

  • The menu bar across the top row: the mnml brand menu, then File, Edit, Selection, View, Go, Run, Terminal, Window and Help. Words that do not fit before the centred workspace chip fold behind a » chip, whose menu lists them.
  • Right-click menus on nearly everything: the editor's text, a file in the tree, a tab, a chip on the statusline, a SESSIONS card, a link, the sidebar's divider.
  • Chip and dock menus: the + menu, the launcher dock's strip, the AI chips.

ui.menu_bar decides whether the bar's words show: always (the default), hidden, or auto, which paints them while a menu is open or the pointer is on the row.

Opening a menu from the keyboard

Key What it opens
F10 The File menu. Not while a debug session is running, where F10 steps over.
Alt + a menu's first letter That menu: Alt+F File, Alt+E Edit, Alt+H Help, Alt+M the brand menu, and so on.
Alt+F10 (standard) The File menu, as view.menu_bar_open.
Shift+F10 The right-click menu of whatever has the keys (view.context_menu_at_focus), in both profiles — a focused SESSIONS card's, for example.

While a menu-bar menu is open, the first letter of every other word is underlined: that is the letter Alt reaches it with. The bar does not take these keys while another overlay is open, while the bar is hidden, or while a terminal pane has the keys — the program in the pane gets them.

Under the vim profile a chord your keymap binds wins over the letter — Alt+H is NvChad's scratch terminal and Alt+R toggles regex search, so Help and Run open from the bar or F10 instead; an Alt letter vim leaves unbound still opens its menu. Under standard the letters stay the menus', as in VS Code.

Inside an open menu:

Key Does
↑ ↓, k j Move between rows.
Home / End First / last row.
Enter Runs the row, or opens its submenu.
→, l Opens the row's submenu. In a menu-bar menu, on a row without one, steps to the next menu.
←, h Steps back out of a submenu. In a menu-bar menu, steps to the previous menu.
Esc, q Closes the menu.

The wheel scrolls a menu taller than the screen; the last cell of its bottom border shows ↑, ↓ or ↕ when there are rows out of sight.

Every row prints its chord

A row that runs a command prints that command's chord at its right edge, in muted grey — the chord for the profile you are in. The menus are how you learn the keys, the same way the command palette is.

A row with no binding prints nothing there. A row whose label and chord do not both fit drops the chord: the label is never cut to make room. The highlighted row prints its chord in the highlight's own colour so it reads on the accent.

The two profiles print different keys for the same row, because they are different keymaps, not one map with patches:

Row vim standard
Cut d Ctrl+X
Copy y Ctrl+C
Paste p Ctrl+V
Undo u Ctrl+Z
Redo Ctrl+R Ctrl+Shift+Z
Select all ggVG Ctrl+A
Toggle fold za Ctrl+Shift+[
Save Ctrl+S Ctrl+S

Under vim, a row prints Neovim's own key whenever vim has one — never a VS Code chord in its place. Save stays Ctrl+S, which NvChad binds too. A command reached through the leader prints its leader chord, such as Space f b.

Under standard, a row prints the editor's own keys — the Ctrl chords the standard editor answers itself, as in the table — and never a leader chord. A command whose only standard chord goes through the Ctrl+K leader prints nothing at the right edge. Hover the row instead: the hover help names the leader row that reaches it (Space a e for Explain, typed as Ctrl+K a e in the standard profile, where Ctrl+K opens the same which-key menu).

Inside the sessions mode, the rows print the chords that mode gives its own verbs — Ctrl+N starts a session in the focused column there — and not what those keys mean elsewhere.

Ticks

A row that switches something on or off, or picks one of a few choices, wears a ✓ before its label while it is the current state (* under --ascii). A menu with a row that can be ticked keeps two cells for the tick on every row, so the labels line up whether a row is ticked or not.

Some of the rows that tick:

Menu Row
Window Auto-equalize splits
The sidebar's divider Auto-hide sidebar — ticked, a click puts the sidebar back to always shown
The tree's workspace header Show git-ignored files, Show workspace dots
A revealed auto-hide sidebar Always (docked), Auto-hide (reveal on the edge), Hidden (keyboard only) — the current mode ticked — and the pin
The theme menu Auto: match system (light / dark)

A click on a ticked on/off row turns the thing off again.

A right-click on a link — a URL, a ticket key, or a pull-request reference, in a terminal pane, on a SESSIONS card or in the sessions table — opens a menu with Copy link and Open link. The menu opens on the row under the link, left-aligned to it, so it never covers the link it is for; above the link when the rows below cannot hold it; at the pointer when neither side can. While the menu is open the link stays lit in the accent with a solid underline, so you can see which link the two rows act on.

Copy link, Copy path and every other copy outside the editor reach the system clipboard, so the text pastes into another app.

Tab strips

Every split has a tab strip across its top.

Gesture Does
Click Shows the tab.
Drag Moves the tab along the strip.
Double-click Zooms the tab's pane to fill the page (view.toggle_zoom). Double-click again to put the splits back. A double-click also keeps a preview tab open, as in VS Code.
Middle-click Closes the tab.
Right-click The tab's menu.
Wheel Slides the strip a tab at a time, when its tabs overflow.

The pager

When a strip has more tabs than fit, a pager appears at its right end: ‹ 2/5 › — the page of tabs on show, out of how many. The arrows turn a page, wrapping round at either end. When every tab fits, nothing is painted; on a strip short of room the number goes first, then the arrows.

A page holds whole tabs only. The tab that would be cut at the strip's edge is not painted there; it starts the next page, which is what the page count already said. Opening a tab that sits just past the last whole one brings it into view whole, with its name and its close button.

⋯: every tab by name

Beside the pager, ⋯ (... under --ascii) opens the buffer picker: every tab by name, on the page or not, one click away. It is the pager's companion — it shows only while the strip pages, and it is the first thing to go on a strip short of room, before the pager's number. The buffer picker is also picker.buffers:

vim standard
Space f b Ctrl+K Ctrl+P

Earlier versions printed a +N hidden count here. It is gone: with every page and every tab a click away, nothing is hidden.

Session strips

The strip of a Claude Code or Codex session pane wears the same ‹ n/m › control with a different meaning: the session's place among the sessions it steps through. It has no ⋯; the sessions rail lists the rest. A strip with one session shows no ‹ 1/1 ›.

The picker

The picker is the box behind ctrl+p, the command palette, the buffer list and most lists you choose from. The query is on its first row, with the count flush right (12, or 12 of 340 while a query filters); the ranked rows follow, the selected one marked with ▌, the matched characters in the accent, and a muted detail — a chord, a directory — at the right.

Key Does
↑ ↓, Ctrl+P Ctrl+N, Ctrl+K Ctrl+J Move.
PageUp PageDown, Ctrl+U Ctrl+D Page the list — or scroll the preview, in a picker that has one.
Enter Accepts the selected row.
Esc Closes the picker.
Tab Marks a row, in a picker that takes several.

ui.picker_position puts the box in the centre (the default) or at the top of the screen.

Its scrollbar

A list longer than the box gets a scrollbar in its rightmost column. Press or drag on the bar to move through the list — the selection follows the pointer's place on the track — and the wheel over the bar scrolls as it does over the rows.

On a narrow screen

The box is the screen's width less eight cells, at least 30 and at most 90 (120 with a preview column). It never grows past the screen: below 30 columns it takes what there is. Its height follows the list, up to 22 rows and never more than four fifths of the screen. As the box narrows:

  • the detail gives way before the label, which keeps at least twelve cells, and is cut with …;
  • the count beside the query goes when the row cannot hold it and a usable field;
  • a preview column is dropped when the two halves could not both be read, and the rows get the whole width;
  • the scrollbar is left off a list four cells wide or less.

Scrollbars

Lists, panes and overlays share one scrollbar: a one-cell strip with a brighter thumb, where a click on the track pages and a drag on the thumb carries the view. Besides the lists that always had one, these show it:

  • The picker, as above.
  • A terminal's scrollback. See Terminal.
  • The HTTP pane. The Response box's Body, Headers and Timeline tabs and the request's Body editor, when their rows overflow, on a column of their own beside the text. A press on the Response bar's track jumps there. See HTTP.

Text fields

Every one-line input in mnml is the same field:

  • the query of the picker and the palette, and a prompt's line;
  • the filter at the top of every sidebar list — SESSIONS, TODOS, NOTES, SEARCH and the rest — and of the Files, ZON, browser, grep and sessions-table panes;
  • the find bar's Find and Replace fields;
  • the : line and the settings filter;
  • the HTTP pane's URL and its Params / Headers name and value cells, a WebSocket's message line, and the debug console;
  • a ZON field being edited.

The mouse

Gesture Does
Click Puts the caret there. On a find field, it also gives the field the keys.
Double-click Selects the word under the pointer.
Triple-click Selects the whole line.
Shift+click Grows the selection from the caret to the pointer.

A click inside a ZON field being edited keeps editing, rather than closing the field.

The keyboard

Key Does
← → Move a character.
Ctrl or Alt with ← → Move a word.
Home / End Start / end of the field.
Shift with ← → Home End Grows a selection.
Backspace / Delete Delete a character — or the selection.
Ctrl or Alt with Backspace / Delete Delete a word.

The fields also answer the readline keys — Ctrl+A / Ctrl+E for the start and end, Ctrl+U / Ctrl+K to delete to either end, Ctrl+W for the word before the caret, Alt+B / Alt+F to move a word — where the box they sit in does not use the key itself. The picker, for one, moves its selection with Ctrl+K and Ctrl+U.

Typing or pasting over a selection replaces it; Backspace deletes it.

Paste

A paste goes into the field you are typing in. A pasted line break becomes a space, since a field holds one line. This includes a sidebar list's filter, a pane's filter, the : line and the debug console, which used to pass a paste through to the editor underneath.

A field that opens with a value already in it — a prompt seeded with a name, the find bar's query after a second Ctrl+F — starts with the whole value selected, so typing replaces it.

Next

  • Editor — the frame these controls sit in, and the two keymap profiles.
  • Terminal — selection, copy and paste, and links in a terminal pane.
  • AI sessions — the sessions rail, the sessions mode and the session strip's control.
  • Keymap profiles — every chord that differs between vim and standard.
  • Command reference — every command a menu row can run.
Edit on GitHub