Key bindings
Keys register through the mayflyKeymap service as named actions; a binding that collides within its scope is rejected. The /help overlay lists every registered binding live, followed by the shared panel keys below — it is the authoritative source for this page (if anything differs, trust /help).
Global actions
In effect unless a capturing panel (a picker, form, or approval in the editor slot) owns input:
| Key | Action | Description |
|---|---|---|
Ctrl-O | Toggle tool output expansion | Switch the most recent 3 turns of tool cards and thinking blocks between one-line summary and full output |
F6 / Shift+F6 | Move surface focus | Traverse the Editor, the views of the status bar, and the pane lanes: with a view available F6 enters the views first, then the interactive panes in layout order; crossing an end returns to the Editor |
F7 | Previous conversation | Switch the complete UI back to the previously displayed conversation (main, BTW, or subagent); show a notice when no other conversation is open |
F8 | Close side conversation | Close the displayed side conversation (from main, the F7 counterpart): detach a subagent or dispose the temporary BTW Agent |
Shared panel keys
Every panel, picker, and form follows one key grammar. The hint row at the bottom of a focused panel is generated from that same grammar, so it only shows keys that do something in the current state (up to three hints below 80 columns, four from 80, always including Esc when Esc does something):
| Key | Action |
|---|---|
↑/↓ | Move between rows and fields; scroll documents |
←/→ | Cycle a select value, adjust a row setting, open or close a tree branch, or move along a tab strip |
Alt+←/→ or F2/F3 | Switch tabs from anywhere on the surface; wizards validate the step being left |
Alt+↑/↓ or F4/F5 | Move between controls from anywhere outside text editing and open pickers |
← | Used by the focused control when it changes something; otherwise it moves focus to the surface's rail of labels, where it does nothing |
Enter or → on a rail | Enter the page beside the rail; ↑/↓ on the rail change the page at once |
Alt+↓ or F5, F6 | From an empty prompt, enter the views of the status bar (F6 enters them first, then the interactive panes); ←/→ switch views and Esc returns to the prompt |
PgUp/PgDn, Home/End | Page or jump in lists and documents |
Enter | Choose, run, open a picker, apply it, or start editing a field |
Space | Toggle a checkbox or multi-select row, open a multiselect, or fold a tree branch |
Tab/Shift+Tab | Commit a text field or picker and move to the next or previous control (while editing a field) |
Esc | Leave the innermost layer: picker, editing, search, back, return to the first control, then close |
Ctrl+C | Close the surface, asking first when there are unsaved changes |
Type or / | Filter a filterable list; Ctrl+U clears the filter |
1-9 | Pick a numbered row |
Ctrl+E | Expand focused scrollable content to full screen |
Delete | Return a changed field to its inherited or default value |
Alt+Enter or Ctrl+J | Insert a newline in a multi-line field |
Enter, Ctrl+Enter, Alt+Enter, ↑/↓, Backspace | In a prompt: send, send as a steer, insert a newline, walk the history of an empty prompt, or select and then remove the last token |
Ctrl+S, c, x, r, Ctrl+G, Ctrl+F | Save, copy, delete, refresh, open in $EDITOR, or search, wherever a panel offers that meaning |
Details that follow from the grammar:
- Esc leaves one layer per press, the same way everywhere: an open picker is cancelled, then text editing ends (the draft stays), then an active search ends (the filter stays), then a page with a Back target goes back, then focus returns to the panel's first control from anywhere else (the hint reads
Esc back), and only then does the panel close. Tab strips are not a stop on the way out. - Disabled rows and options never take the cursor; movement steps over them. A row or action that is unavailable says why next to its label.
- Select fields change with
←/→and never with↑/↓, which always move to the next field; a focused select shows its value as‹ value ›while it can cycle.Enteropens the option list;Tabapplies the highlighted option and moves on. - Inherited settings carry
(inherited)or(override)after the value. Changing the value overrides it;Deleteon an overriding field returns it to the inherited value. There are no separate override buttons. - Filtering starts on the first typed character or
/. Modifier shortcuts such asAlt+EnterorCtrl+Rkeep working while a filter is active; in multi-select listsSpacestill toggles. - Numbered rows keep their numbers while the list scrolls. In gates such as plan review a digit only moves the cursor;
Enterconfirms. - Confirmations are one shared Yes/No decision with No focused first; the question may carry a sentence about consequences.
Editor context
Text-editing keys (cursor movement, multi-line, undo, kill-ring) belong to the underlying editor; in addition:
| Key | Action | Description |
|---|---|---|
Escape | Interrupt the flow / clear the draft | While a session flow is in progress (the selected Agent or a live descendant runs), interrupt it: safe retraction of the just-submitted message first when the buffer is empty and eligible, restoring it into the editor, with the draft preserved; idle, it clears the draft, which stays one ↑ away in history; an open completion popup only closes |
Ctrl-C | Clear draft → interrupt → exit | With a draft it only empties the box (the draft stays one ↑ away) and the flow continues; with an empty buffer while a session flow is in progress it interrupts the selected Agent and all running continuable descendants; when the whole tree is idle and the buffer is empty, a second press within 1 second exits Mayfly |
Ctrl-Enter | Steer | Inject the non-empty draft as a steering instruction into the current turn, clearing the buffer |
Ctrl-V | Paste image | Store the clipboard image in the attachment library, inserting an [image #N] marker at the cursor |
Ctrl-G | External editor | Hand the draft to an external editor for full-screen editing (mayfly.editorCommand setting → $VISUAL → $EDITOR; Mayfly suspends and yields the terminal); quitting with :cq leaves the draft untouched |
Alt+M | Cycle session model | Step through the current provider's models (same channel as /model — the Host persists the default in the background; the press is consumed, the draft stays intact) |
Backspace | Delete / exit mode | Backspace on an empty ! bash prompt exits back to prompt mode |
? | Key help | On an empty prompt, open key help; anywhere else ? is typed as text |
Alt+↑ | Focus the conversation | From the prompt, focus the newest row of the stream; a queued message there takes e edit, s send now, d cancel, and u undo. In the prompt, ↑/↓ walk the history only |
Shift+Tab | Toggle plan state | normal ↔ plan, preserving permissions and YOLO (see Session modes) |
These keys reach the editor even while a notice is shown under the prompt. Editor extensions may add actions only on modifier keys (such as Alt+R); the editor keeps Esc, Tab, Shift+Tab, and every unmodified key.
Panel contexts
| Surface | Keys |
|---|---|
/help overlay | ↑↓ / PageUp / PageDown / Home / End scroll; Ctrl+E expands; Tab reaches Close; Escape closes |
/sessions picker | Type to filter workspaces or conversations; ↑↓ moves; Enter opens a workspace, then session details; ← / → folds session branches; Esc ends editing/filtering, then returns one panel level |
| Approval panel | The focused default is Reject; ← / → or Tab reach Allow once, Allow for this session, and Reject with feedback; Enter runs; Escape rejects. On the feedback page, Enter sends, Alt+Enter adds a line, Esc ends editing and then returns to the decisions. There are no digit shortcuts |
| Questionnaire | 1–9 or ↑↓ + Enter choose and advance; multi-choice toggles with Space and confirms with Enter; typing in Other starts an answer, Enter submits it, Alt+Enter adds a line; the question tabs switch with ← / → on the strip or Alt+← / Alt+→ (F2 / F3) anywhere, and moving forward validates the current question |
| Form panel | ↑↓ move between fields; typing or Enter starts editing text, Enter confirms and moves on, Alt+Enter inserts a textarea newline; a select changes with ← / → or opens with Enter; a multiselect opens with Enter or Space; Delete returns an overriding field to its inherited value; a number steps with ← / →; Tab takes a matching completion, or commits and moves to the next group; Ctrl+S saves the form from any field; Escape ends editing, then closes (asking first when there are unsaved changes) |
| Plan review | ↑↓ or 1–3 move between decisions and Enter confirms the focused one; c copies the plan, o opens feedback; PageUp / PageDown / Shift+↑↓ scroll the plan |
/model panel | One list grouped by provider: type to filter, ↑↓ selects a model, ← / → adjusts its thinking level; Enter sets the default and switches |
/effort panel | 1–9 or ↑↓ + Enter set the default thinking level |
/permission picker | 1–9 or ↑↓ + Enter switch presets; full access first asks a Yes/No decision with No focused |
/agents browser | Type to filter; ↑↓ selects, Space or ← / → expands/collapses, Enter views; Tab reaches Stop selected, which asks first and names why it cannot run for a one-shot, cold, or parent-of-live row |
/plugin marketplace | Installed / Not installed tabs (← / → on the strip or Alt+← / Alt+→, also F2 / F3); type to filter; Tab reaches Details, Install or Update/repair, and Remove, each available per row; Ctrl+R refreshes |
| Side conversation | Uses the complete main editor and the same panel keys; PageUp / PageDown scroll its transcript; in a resumable one a send wakes the child: Enter queues the message for its next turn and Ctrl+Enter steers it; F7 switches back and F8 closes it |
Named actions and custom bindings
Every key belongs to a named action, and the key is only that action's current binding. The panel keys above are the ui.* actions (ui.up, ui.accept, ui.cancel, ui.tab-next, ui.focus-prev, …); the common meanings are ui.save, ui.copy, ui.delete, ui.refresh, ui.external, and ui.search; Mayfly's own keys keep their mayfly.* ids, and a plugin names its row keys <owner>.<action>. A binding is live in one scope: the editor, a panel, the conversation stream, or everywhere. That is why Ctrl+S steers in the editor and saves in a panel, and Shift+Tab toggles plan mode in the editor and moves back a group in a panel.
Overrides live in the keybindings field of the mayfly settings namespace, keyed by action id, each with the keys that replace the defaults and the label to list it under:
mayfly:
keybindings:
ui.delete: { keys: [d], label: delete }
acme-providers.test: { keys: [ctrl+t], label: Test connection }
preferPlainKeys: trueA change applies at once, and every hint follows it. A key an action gives up stops working: it is not kept as an alias. A binding that collides with another action in the same scope is skipped with a warning naming that action's owner; Esc and Enter cannot be unbound or given to another action; and a plain character cannot be bound in the editor or everywhere, where it would type text. A panel to capture and restore keys (/keys) comes in a later phase.
Every default that uses Alt has a second one without it, because terminals and multiplexers often swallow Alt: F2/F3 switch tabs and Ctrl+J inserts a newline. The first F2-F5 you press tells Mayfly that Alt may not arrive, and from then on the hint rows show the plain key first; preferPlainKeys: true does that for good. Alt keys are read in every common encoding: xterm's ESC [ 1 ; 3 A with or without a kitty event type, a bare ESC prefix, SS3, kitty's CSI u, and modifyOtherKeys.
Panel accelerators declared by plugins must be valid key ids, cannot take shared navigation keys, cannot repeat on one page unless their scopes name different controls, and cannot be plain characters on a panel with a type-to-filter list.