Something went wrong. Try again.
Keyboard-first TUI email: write in Neovim, render as Markdown, screen senders first, organize emails once. neomd.ssp.sh
email markdown neovim tui
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159package ui
// HelpSection is one group of keybindings shown in the ? help overlay// and in the Keybindings section of README.md (generated by `make docs`).type HelpSection struct { Title string Rows [][2]string // {key, description}}
// HelpSections is the single source of truth for all keybindings.// Edit here → `make docs` regenerates the README.md keybindings section.var HelpSections = []HelpSection{ {"Navigation", [][2]string{ {"j / k", "move down / up"}, {"d / u", "page down / up in inbox/help"}, {"gg", "jump to top"}, {"G", "jump to bottom"}, {"enter / l", "open email"}, {"h / q / esc", "back to inbox (from reader)"}, {"?", "toggle help overlay (type to filter)"}, }}, {"Folders", [][2]string{ {"L / ] / tab", "next folder tab"}, {"H / [ / shift+tab", "previous folder tab"}, {"gi", "go to Inbox"}, {"ga", "go to Archive"}, {"gf", "go to Feed"}, {"gp", "go to PaperTrail"}, {"gt", "go to Trash"}, {"gs", "go to Sent"}, {"gk", "go to ToScreen"}, {"go", "go to ScreenedOut"}, {"gw", "go to Waiting"}, {"gc", "go to Scheduled (calendar)"}, {"gb", "go to Work (if configured)"}, {"gm", "go to Someday"}, {"gd", "go to Drafts"}, {"ge", "go to Everything — latest 50 emails across all folders"}, {"gS", "go to Spam (not in tab rotation)"}, }}, {"Screener (marked or cursor, any folder)", [][2]string{ {"I", "approve sender → screened_in.txt + move to Inbox (removes from blocked lists)"}, {"O", "block sender → screened_out.txt + move to ScreenedOut (removes from screened_in)"}, {"$", "mark as Spam → spam.txt + move to Spam (removes from screened_in/out)"}, {"F", "mark as Feed → feed.txt + move to Feed"}, {"P", "mark as PaperTrail → papertrail.txt + move to PaperTrail"}, {"Di", "approve whole DOMAIN → @domain.tld appended to screened_in.txt (cursor or open email; y/n)"}, {"Do", "block whole DOMAIN → @domain.tld appended to screened_out.txt (cursor or open email; y/n)"}, {"A", "archive (move to Archive, no screener update)"}, {"B", "move to Work/business (no screener update, if configured)"}, {"S", "dry-run screen inbox (loaded emails), then y/n"}, }}, {"Move (marked or cursor, no screener update)", [][2]string{ {"x", "delete → Trash"}, {"Mi", "move to Inbox"}, {"Ma", "move to Archive"}, {"Mf", "move to Feed"}, {"Mp", "move to PaperTrail"}, {"Mt", "move to Trash"}, {"Ms", "move to Sent"}, {"Mo", "move to ScreenedOut"}, {"Mw", "move to Waiting"}, {"Mc", "move to Scheduled"}, {"Mb", "move to Work (if configured)"}, {"Mm", "move to Someday"}, {"Mk", "move to ToScreen"}, }}, {"Multi-select & Undo", [][2]string{ {"m", "mark / unmark email + advance cursor"}, {"ctrl+u", "clear all marks"}, {"U", "undo last move or delete (reverses x, A, M* — not screener actions)"}, {"X (Trash only)", "permanently delete marked or cursor email(s) — no undo"}, }}, {"Leader Key Mappings (space prefix)", [][2]string{ {"<space>1 … <space>9", "jump to folder tab by number (Inbox=1, ToScreen=2, …)"}, {"<space>/", "IMAP search ALL emails on server (From + Subject)"}, {"<space>S", "scan current folder for spy pixels (skips already scanned)"}, {"<space>d (reader)", "download raw email source (.eml) to ~/Downloads"}, {"<space>n (reader)", "append open email's sender to notify.txt (desktop notifications opt-in)"}, {"<space>N (reader)", "append @domain of open email's sender to notify.txt"}, {"<space>va (reader, calendar invite)", "RSVP accept — send iMIP REPLY to organizer"}, {"<space>vd (reader, calendar invite)", "RSVP decline"}, {"<space>vt (reader, calendar invite)", "RSVP tentative"}, {"<space>vo (reader, calendar invite)", "open .ics in [calendar].open_command (default xdg-open, e.g. morgen)"}, {"<space>w", "show welcome screen"}, }}, {"Sort (, prefix)", [][2]string{ {",m", "date newest first (default)"}, {",M", "date oldest first"}, {",a", "from A→Z"}, {",A", "from Z→A"}, {",s", "size smallest first"}, {",S", "size largest first"}, {",n", "subject A→Z"}, {",N", "subject Z→A"}, }}, {"Email actions", [][2]string{ {"n", "toggle read/unread (marked or cursor)"}, {"N", "jump to next unread email"}, {"ctrl+n", "mark all in current folder as read"}, {"R", "reload / refresh folder"}, {"r", "reply (from inbox or reader)"}, {"ctrl+r", "reply-all — reply to sender + all CC recipients (from inbox or reader)"}, {"ctrl+e", "react with emoji (from inbox or reader)"}, {"f", "forward email (from reader or inbox)"}, {"T", "show full conversation thread across folders (from inbox or reader)"}, {"c", "compose new email"}, {"ctrl+b (compose/pre-send)", "toggle Cc+Bcc fields (both hidden by default)"}, {"ctrl+f (compose/pre-send)", "cycle From address through all accounts + [[senders]] aliases"}, {"a (pre-send)", "attach file via yazi file picker (or $NEOMD_FILE_PICKER)"}, {"D (pre-send)", "remove last attachment"}, {"d (pre-send)", "save to Drafts folder (IMAP APPEND with \\Draft flag)"}, {"s (pre-send)", "spell check — open in nvim with spell on, jump to first error"}, {"p (pre-send)", "preview email in $BROWSER (images rendered, same as recipient sees)"}, {"e (pre-send)", "re-open editor to edit body"}, {"i (pre-send)", "AI handoff — write draft to temp file, spawn [ai].command (default `claude`; quit the tool to return to neomd, edits round-trip back)"}, {"enter (pre-send)", "confirm and send"}, {"1-9 (reader)", "download attachment N to ~/Downloads and open with xdg-open"}, {"space+1-0 (reader)", "open link 1-10 in $BROWSER (0 = 10th link)"}, {"space+l11-99 (reader)", "open link 11-99 in $BROWSER (e.g. space+l26 for [26])"}, {"e (reader)", "open in $EDITOR read-only — search, copy, vim motions"}, {"E (reader)", "continue draft — re-open as editable compose (Drafts folder)"}, {"o (reader)", "open in w3m (terminal browser)"}, {"O (reader)", "open in $BROWSER (GUI browser, images shown)"}, {"ctrl+o (reader)", "open web version / newsletter URL in $BROWSER"}, {"ctrl+a (inbox)", "switch account (if multiple configured)"}, }}, {"Command line (: to open, tab to complete)", [][2]string{ {":screen / :s", "dry-run screen loaded inbox emails"}, {":screen-all / :sa", "dry-run screen ALL inbox emails (no limit)"}, {":reset-toscreen / :rts", "move all ToScreen emails back to Inbox"}, {":mark-read / :mr", "mark all emails in current folder as read"}, {":reload / :r", "reload current folder"}, {":check / :ch", "show screener classification for selected email"}, {":everything / :ev", "show latest 50 emails across all folders"}, {":search / :se", "IMAP search all emails on server (From + Subject + To)"}, {":delete-all / :da", "permanently delete ALL emails in current folder (y/n)"}, {":empty-trash / :et", "permanently delete ALL emails in Trash (y/n)"}, {":create-folders / :cf", "create missing IMAP folders from config (safe, idempotent)"}, {":go-spam / :spam", "open Spam folder (not in tab rotation)"}, {":debug / :dbg", "diagnostic report — IMAP ping, config, folders, state (saved to /tmp/neomd/debug.log)"}, {":notify-test / :nt", "fire a single test desktop notification using the current [notifications] config"}, {":quit / :q", "quit neomd"}, }}, {"Composing", [][2]string{ {"tab (To/Cc/Bcc)", "accept autocomplete suggestion or next field"}, {"ctrl+n / ctrl+p / arrows (To/Cc/Bcc)", "cycle through address suggestions"}, {"enter (on Subject)", "open $EDITOR with a .md temp file"}, {"esc", "cancel"}, }}, {"General", [][2]string{ {"/", "filter loaded emails (From + Subject, in-memory)"}, {"z", "toggle unread-only view (zoomed out/zero inbox)"}, {"<space>/ or :search", "IMAP search ALL emails on server (From + Subject)"}, {"?", "toggle this help"}, {"q", "quit (from inbox)"}, }},}