···131131132132On first run, neomd:
1331331. Creates `~/.config/neomd/config.toml` with placeholders — fill in your IMAP/SMTP credentials
134134- - Important: Make sure that the Capitalization and naming of folder in `config.toml` is accroding to webmail IMAP, e.g. [Gmails](docs/gmail.md) uses `sent = "[Gmail]/Sent Mail"` and not `sent` etc.
134134+ - Important: Make sure that the Capitalization and naming of folder in `config.toml` is accroding to webmail IMAP, e.g. [Gmails](docs/content/docs/configurations/gmail.md) uses `sent = "[Gmail]/Sent Mail"` and not `sent` etc.
1351352. Creates `~/.config/neomd/lists/` for screener allowlists (or uses your custom paths from config)
1361363. Creates any missing IMAP folders (ToScreen, Feed, PaperTrail, etc.) automatically
137137138138139139-Neomd also runs on Android (more for fun) — see [docs/android.md](docs/android.md).
139139+Neomd also runs on Android (more for fun) — see [docs/content/docs/configurations/android.md](docs/content/docs/android.md).
140140141141## Configuration
142142···166166167167Use an app-specific password (Gmail, Fastmail, Hostpoint, etc.) rather than your main account password. The `password` and `user` fields support environment variable expansion (`$VAR` or `${VAR}`) so you can avoid storing secrets in the config file.
168168169169-For the full configuration reference including multiple accounts, OAuth2 authentication, `[[senders]]` aliases, folder customization, signatures, and UI options, see [docs/configuration.md](docs/configuration.md).
169169+For the full configuration reference including multiple accounts, OAuth2 authentication, `[[senders]]` aliases, folder customization, signatures, and UI options, see [docs/content/docs/configuration.md](docs/content/docs/configuration.md).
170170171171**Provider-specific guides:**
172172-- Gmail: [docs/gmail.md](docs/gmail.md) — folder name mapping and OAuth2 setup
173173-- Proton Mail Bridge: [docs/proton-bridge.md](docs/proton-bridge.md) — non-standard port configuration
172172+- Gmail: [docs/content/docs/configurations/gmail.md](docs/content/docs/configurations/gmail.md) — folder name mapping and OAuth2 setup
173173+- Proton Mail Bridge: [docs/content/docs/configurations/proton-bridge.md](docs/content/docs/configurations/proton-bridge.md) — non-standard port configuration
174174175175### Onboarding
176176···201201202202### Screener Workflow
203203204204-Find full Screener Workflow at [docs/screener.md](docs/screener.md), classification tables, and bulk re-classification instructions.
204204+Find full Screener Workflow at [docs/content/docs/screener.md](docs/content/docs/screener.md), classification tables, and bulk re-classification instructions.
205205### Keybindings
206206207207Press `?` inside neomd to open the interactive help overlay. Start typing to filter shortcuts.
208208209209-See the [full keybindings reference](docs/keybindings.md) (auto-generated from [`internal/ui/keys.go`](internal/ui/keys.go) via `make docs`).
209209+See the [full keybindings reference](docs/content/docs/keybindings.md) (auto-generated from [`internal/ui/keys.go`](internal/ui/keys.go) via `make docs`).
210210211211### How Sending Works
212212···214214215215Discarding unsent mail now asks for confirmation in compose/pre-send, and `:recover` reopens the latest backup if you want to resume after an abort.
216216217217-- See [docs/sending.md](docs/sending.md) for details on MIME structure, attachments, pre-send review, and drafts.
218218-- See [docs/reading.md](docs/reading.md) for the reader: images, inline links, attachments, and navigation.
217217+- See [docs/content/docs/sending.md](docs/content/docs/sending.md) for details on MIME structure, attachments, pre-send review, and drafts.
218218+- See [docs/content/docs/reading.md](docs/content/docs/reading.md) for the reader: images, inline links, attachments, and navigation.
219219220220### Dev: Makefile Commands
221221···278278279279280280> [!NOTE]
281281-> **Gmail is not recommended.** If you're on Gmail, consider a dedicated email provider (Hostpoint, Fastmail, HEY, Migadu, etc.) for the best neomd experience. Or use Gmail just for fun :). See [docs/gmail.md](docs/gmail.md) for Gmail-specific folder configuration.
281281+> **Gmail is not recommended.** If you're on Gmail, consider a dedicated email provider (Hostpoint, Fastmail, HEY, Migadu, etc.) for the best neomd experience. Or use Gmail just for fun :). See [docs/content/docs/configurations/gmail.md](docs/content/docs/configurations/gmail.md) for Gmail-specific folder configuration.
282282283283**Test your own provider:**
284284```bash
···311311312312I used my experience with Neomutt, TUIs, and the GTD workflow for handling emails with HEY Screener, and added some (hopefully) _taste_ using my favorite tools and aesthetics. Find the full history at [Twitter](https://xcancel.com/sspaeti/status/2036539855182627169#m) - inspired by seeing [Email.md](https://www.emailmd.dev/) on HackerNews.
313313314314-If you [rather read the prompt](https://www.ssp.sh/brain/id-rather-read-the-prompt), check out my [initial prompt](docs/initial-prompt/prompt.md) and its generated [plan](docs/initial-prompt/prompt-plan.md) - which I have iterated and added features by the 100s since then.
314314+If you [rather read the prompt](https://www.ssp.sh/brain/id-rather-read-the-prompt), check out my [initial prompt](https://github.com/ssp-data/neomd/blob/main/docs/initial-prompt/prompt.md) and its generated [plan](https://github.com/ssp-data/neomd/blob/main/docs/initial-prompt/prompt-plan.md) - which I have iterated and added features by the 100s since then.
315315## Roadmap
316316317317See at my second brain at [Roadmap](https://www.ssp.sh/brain/neomd#roadmap).
···11---
22-title: Documentation
22+title: Overview & Philosophy
33+weight: 0
34---
4555-Welcome to the neomd documentation.
66+A minimal terminal email client for people who write in Markdown and live in Neovim.
77+88+[Neomd](https://www.ssp.sh/brain/neomd/) is my way of implementing an email TUI based on my experience with Neomutt, focusing on [Neovim](https://www.ssp.sh/brain/neovim) (input) and reading/writing in [Markdown](https://www.ssp.sh/brain/markdown) and navigating with [Vim Motions](https://www.ssp.sh/brain/vim-language-and-motions) with the GTD workflow and [HEY-Screener](https://www.hey.com/features/the-screener/).
99+1010+1111+{{< callout type="warning" >}}
1212+neomd moves, deletes, and modifies emails directly on your IMAP server. These operations affect your mailbox across all devices. **Back up important emails before first use.** neomd is experimental software and can't take responsibility for lost or misplaced emails. Consider testing with a secondary email account first.
1313+{{< /callout >}}
1414+1515+1616+## The philosophy behind Neomd: What's unique?
1717+1818+The key here is **speed** in which you can **navigate, read, and process** your email. Everything is just a shortcut away, and instantly (ms not seconds). It's similar to the foundations that Superhuman was built on: it runs on Gmail and makes it fast with vim commands.
1919+2020+With the **HEY-Screener**, you get only emails in your inbox that you _screened in_, no spam or sales pitch before you added them. Or don't like them, just screen them out, and they get automatically moved to the "ScreenedOut" folder.
2121+2222+With the [GTD approach](https://www.ssp.sh/brain/getting-things-done-gtd), using folders such as next (inbox), waiting, someday, scheduled, or archive, you can move them with one shortcut. This allows you quickly to move emails you need to wait for, or deal with later, in the right category. **Processing your email only once**.
2323+2424+With the additional **Feed** and **Papertrail**, two additional features from HEY, you can read newsletters (just hit F) on them automatically in their separate tab, or move all your receipts into the Papertrail. Once you mark them as feed or papertrail, they will moved there automatically going forward. So you decide whether to read emails or news by jumping to different tabs.
2525+2626+2727+{{< callout type="info" >}}
2828+neomd's **speed** depends entirely on your IMAP provider. On Hostpoint (the provider I use), a folder switch takes **~33ms** which feels instant. On Gmail, the same operation takes **~570ms** which is noticeably slow. See [Benchmark](#benchmark) for full details and how to test your provider.
2929+{{< /callout >}}
3030+3131+3232+## Screenshots
3333+3434+### Overview: List-View
3535+3636+Feed view with all Newsletters - also workflow with differnt tabs and unread counter only for certain tabs (not all):
3737+
3838+3939+### Reading Panel
4040+4141+Reading an email with Markdown 💙:
4242+4343+
4444+4545+### Sent emails
4646+This is the markdown sent:
4747+4848+```markdown
4949+# [neomd: to: email@domain.com]
5050+# [neomd: subject: this is an email from neomd!]
5151+5252+This email is from Neomd. Great I can add links such as [this](https://ssp.sh) with plain Markdown.
5353+5454+E.g. **bold** or _italic_.
5555+5656+## Does headers work too?
5757+5858+this is a text before a h3.
5959+6060+### H3 header
6161+6262+how does that look in an email?
6363+Best regards
6464+```
6565+6666+*Compose emails in your editor, read them rendered with [glamour](https://github.com/charmbracelet/glamour), and manage your inbox with a [HEY-style screener](https://www.hey.com/features/the-screener/) — all from the terminal.*
6767+6868+6969+Which looks like this:
7070+7171+
7272+7373+Or in Gmail:
7474+
7575+7676+7777+### Video
7878+7979+ YouTube rundown of most features:
8080+ [](https://youtu.be/8aKkldYLWV8)
8181+*(shorter but limited showcase [part 1 video](https://youtu.be/lpmHqIrCC-w))*
8282+8383+8484+## Features
8585+8686+- **Write in Markdown, send beautifully** — compose in `$EDITOR` (defaults to `nvim`), send as `multipart/alternative`: raw Markdown as plain text + goldmark-rendered HTML so recipients get clickable links, bold, headers, inline code, and code blocks
8787+- **Pre-send review** — after closing the editor, review To/Subject/body before sending; attach files, save to Drafts, or re-open the editor — no accidental sends
8888+- **Attachments** — attach files from the pre-send screen via yazi (`a`); images appear inline in the email body, other files as attachments; also attach from within neovim via `<leader>a`; the reader lists all attachments (including inline images) and `1`–`9` downloads and opens them
8989+- **Link opener** — links in emails are numbered `[1]`-`[0]` in the reader header; press `space+digit` to open in `$BROWSER`
9090+- **CC, BCC, Reply-all** — optional Cc/Bcc fields (toggle with `ctrl+b`); `R` in the reader replies to sender + all CC recipients
9191+- **Emoji reactions** — press `ctrl+e` from inbox or reader to react with emoji (👍 ❤️ 😂 🎉 🙏 💯 👀 ✅); instant send with proper threading and quoted message history, no editor needed; reactions appear in conversation threads with neomd branding
9292+- **Drafts** — `d` in pre-send saves to Drafts (IMAP APPEND); `E` in the reader re-opens a draft as an editable compose; compose sessions are auto-backed up to `~/.cache/neomd/drafts/` so you never lose an unsent email (`:recover` to reopen)
9393+- **HTML signatures** — configure separate text and HTML signatures; text signature appears in editor and plain text part, HTML signature in HTML part only; use `[html-signature]` placeholder to control inclusion per-email
9494+- **Multiple From addresses** — define SMTP-only `[[senders]]` aliases (e.g. `s@ssp.sh` through an existing account); cycle with `ctrl+f` in compose and pre-send; sent copies always land in the Sent folder
9595+- **Undo** — `u` reverses the last move or delete (`x`, `A`, `M*`) using the UIDPLUS destination UID
9696+- **Search** — `/` filters loaded emails in-memory; `space /` or `:search` runs IMAP SEARCH across all folders (only fetching header capped at 100 per folder) with results in a temporary "Search" tab; supports `from:`, `subject:`, `to:` prefixes
9797+- **Address autocomplete** — To/Cc/Bcc fields autocomplete from screener lists; navigate with `ctrl+n`/`ctrl+p`, accept with `tab`
9898+- **Everything view** — `ge` or `:everything` shows the 50 most recent emails across all folders; find emails that were screened out, moved to spam, or otherwise hard to locate
9999+- **Threaded inbox** — related emails are grouped together in the inbox list with a vertical connector line (`│`/`╰`), Twitter-style; threads are detected via `In-Reply-To`/`Message-ID` headers with a subject+participant fallback; newest reply on top, root at bottom; `·` reply indicator shows which emails you've answered
100100+- **Conversation view** — `T` or `:thread` shows the full conversation across folders (Inbox, Sent, Archive, etc.) in a temporary tab with `[Folder]` prefix; see your replies alongside received emails
101101+- **Glamour reading** — incoming emails rendered as styled Markdown in the terminal
102102+- **HEY-style screener** — unknown senders land in `ToScreen`; press `I/O/F/P` to approve, block, mark as Feed, or mark as PaperTrail; reuses your existing `screened_in.txt` lists from neomutt
103103+- **Folder tabs** — Inbox, ToScreen, Feed, PaperTrail, Archive, Waiting, Someday, Scheduled, Sent, Trash, ScreenedOut
104104+- **Multi-select** — `m` marks emails, then batch-delete, move, or screen them all at once
105105+- **Auto-screen on load** — screener runs automatically every time the Inbox loads (startup, `R`); keeps your inbox clean without pressing `S` (configurable, on by default)
106106+- **Background sync** — while neomd is open, inbox is fetched and screened every 5 minutes in the background; interval configurable, set to `0` to disable
107107+- **Kanagawa theme** — colors from the [kanagawa.nvim](https://github.com/rebelot/kanagawa.nvim) palette
108108+- **IMAP + SMTP** — direct connection via RFC 6851 MOVE, no local sync daemon required and keeps it in sync if you use it on mobile or different device
109109+110110+## Install
111111+112112+**Prerequisites:** [Go 1.22+](https://go.dev/doc/install) and `make`.
113113+114114+{{< callout type="info" >}}
115115+**Optional attachment helpers:**
116116+- `yazi` enables the built-in file picker used by pre-send `a`
117117+- custom Neovim integration in `custom.lua` enables inline `<leader>a` attachment insertion inside `neomd-*.md` buffers
118118+- without these, neomd still works; the inline Neovim attachment workflow just won't be available
119119+{{< /callout >}}
120120+121121+122122+```sh
123123+git clone https://github.com/ssp-data/neomd
124124+cd neomd
125125+make install # installs to ~/.local/bin/neomd
126126+```
127127+128128+Or just build locally:
129129+130130+```sh
131131+make build
132132+./neomd
133133+```
134134+135135+Or if on Arch Linux (AUR), you can use my [neomd-bin](https://aur.archlinux.org/packages/neomd-bin) via:
136136+```sh
137137+yay -S neomd-bin
138138+```
139139+140140+141141+On first run, neomd:
142142+1. Creates `~/.config/neomd/config.toml` with placeholders — fill in your IMAP/SMTP credentials
143143+ - Important: Make sure that the Capitalization and naming of folder in `config.toml` is accroding to webmail IMAP, e.g. [Gmails](docs/content/docs/configurations/gmail.md) uses `sent = "[Gmail]/Sent Mail"` and not `sent` etc.
144144+2. Creates `~/.config/neomd/lists/` for screener allowlists (or uses your custom paths from config)
145145+3. Creates any missing IMAP folders (ToScreen, Feed, PaperTrail, etc.) automatically
146146+147147+148148+Neomd also runs on Android (more for fun) — see [docs/content/docs/configurations/android.md](docs/content/configurations/android).
149149+150150+## Configuration
151151+152152+On first run, neomd creates `~/.config/neomd/config.toml` with placeholders:
153153+154154+```toml
155155+[[accounts]]
156156+name = "Personal"
157157+imap = "imap.example.com:993" # :993 = TLS, :143 = STARTTLS
158158+smtp = "smtp.example.com:587"
159159+user = "me@example.com"
160160+password = "app-password"
161161+from = "Me <me@example.com>"
162162+starttls = false
163163+tls_cert_file = "" # optional PEM cert/CA for self-signed local bridges
164164+165165+# Root-level settings
166166+store_sent_drafts_in_sending_account = false # default: Sent/Drafts use first account; true = follow sending account
167167+168168+[screener]
169169+screened_in = "~/.config/neomd/lists/screened_in.txt"
170170+screened_out = "~/.config/neomd/lists/screened_out.txt"
171171+feed = "~/.config/neomd/lists/feed.txt"
172172+papertrail = "~/.config/neomd/lists/papertrail.txt"
173173+spam = "~/.config/neomd/lists/spam.txt"
174174+```
175175+176176+Use an app-specific password (Gmail, Fastmail, Hostpoint, etc.) rather than your main account password. The `password` and `user` fields support environment variable expansion (`$VAR` or `${VAR}`) so you can avoid storing secrets in the config file.
177177+178178+For the full configuration reference including multiple accounts, OAuth2 authentication, `[[senders]]` aliases, folder customization, signatures, and UI options, see [docs/content/configuration](docs/content/configuration).
179179+180180+**Provider-specific guides:**
181181+- Gmail: [docs/content/docs/configurations/gmail.md](docs/content/docs/configurations/gmail.md) — folder name mapping and OAuth2 setup
182182+- Proton Mail Bridge: [docs/content/docs/configurations/proton-bridge.md](docs/content/docs/configurations/proton-bridge.md) — non-standard port configuration
183183+184184+### Onboarding
185185+186186+On first launch, **auto-screening is paused** because your screener lists are empty — neomd won't move anything until you've classified your first sender. Your Inbox loads normally so you can explore.
187187+188188+By default, neomd loads and auto-screens only the newest `200` Inbox emails (`[ui].inbox_count`). This keeps startup predictable. If you want to re-screen the entire Inbox on the IMAP server, run `:screen-all` inside neomd; that scans every Inbox email, not just the loaded subset, and can take a while on large mailboxes.
189189+190190+**Getting started with the screener:**
191191+192192+1. From your Inbox, pick an email and press `I` (screen **in**) to approve the sender, or `O` (screen **out**) to block them. This creates your first screener list entry.
193193+2. Once you've classified at least one sender, auto-screening activates on every Inbox load — new emails from known senders are sorted automatically.
194194+3. Unknown senders land in the `ToScreen` tab. Jump there with `gk` (or `Tab`, use `L` or click the tab) and classify them:
195195+ - `I` screen **in** — sender stays in Inbox forever
196196+ - `O` screen **out** — sender never reaches Inbox again
197197+ - `F` **feed** — newsletters go to the Feed tab
198198+ - `P` **papertrail** — receipts go to the PaperTrail tab
199199+4. Use `m` to mark multiple emails, then `I` to batch-approve them all at once. From the `ToScreen` folder, approving/blocking a single unmarked message now applies to all currently queued mail from that sender.
200200+201201+**The best part:** all classifications are saved permanently in your screener lists (`screened_in.txt`, `screened_out.txt`, etc.). An email address screened in will automatically go to your Inbox, and any email screened out will never be in your Inbox again.
202202+203203+You choose who can land in your Inbox. Bye-bye spam. This is the beauty of [HEY-Screener](https://www.hey.com/features/the-screener/), and neomd implements the same concept.
204204+205205+{{< callout type="info" >}}
206206+To disable auto-screening entirely, set `auto_screen_on_load = false` in `[ui]` config. Run `:debug` inside neomd if something isn't working.
207207+{{< /callout >}}
208208+209209+210210+{{< callout type="warning" >}}
211211+`:screen-all` operates on the full Inbox mailbox on the server, not just the emails currently loaded in the UI. Use it when you intentionally want a mailbox-wide reclassification pass.
212212+{{< /callout >}}
213213+214214+215215+### Screener Workflow
216216+217217+Find full Screener Workflow at [docs/content/screener](docs/content/screener), classification tables, and bulk re-classification instructions.
218218+### Keybindings
219219+220220+Press `?` inside neomd to open the interactive help overlay. Start typing to filter shortcuts.
221221+222222+See the [full keybindings reference](docs/content/keybindings) (auto-generated from [`internal/ui/keys.go`](internal/ui/keys.go) via `make docs`).
223223+224224+### How Sending Works
225225+226226+Compose in Markdown, send as `multipart/alternative` (plain text + HTML). Attachments, CC/BCC, multiple From addresses, drafts, and pre-send review are all supported.
227227+228228+Discarding unsent mail now asks for confirmation in compose/pre-send, and `:recover` reopens the latest backup if you want to resume after an abort.
229229+230230+- See [docs/content/sending](docs/content/sending) for details on MIME structure, attachments, pre-send review, and drafts.
231231+- See [docs/content/reading](docs/content/reading) for the reader: images, inline links, attachments, and navigation.
232232+233233+### Dev: Makefile Commands
234234+235235+```
236236+make build compile ./neomd
237237+make run build and run
238238+make install install to ~/.local/bin
239239+make test run tests
240240+make vet go vet
241241+make fmt gofmt -w .
242242+make tidy go mod tidy
243243+make clean remove compiled binary
244244+make help print this list
245245+```
246246+247247+## Stack
248248+249249+- [Bubble Tea](https://github.com/charmbracelet/bubbletea) — TUI framework
250250+- [Bubbles](https://github.com/charmbracelet/bubbles) — list, viewport, textinput components
251251+- [Glamour](https://github.com/charmbracelet/glamour) — Markdown → terminal rendering
252252+- [Lipgloss](https://github.com/charmbracelet/lipgloss) — styling
253253+- [go-imap/v2](https://github.com/emersion/go-imap) — IMAP client (RFC 6851 MOVE)
254254+- [go-message](https://github.com/emersion/go-message) — MIME parsing
255255+- [goldmark](https://github.com/yuin/goldmark) — Markdown → HTML for sending
256256+- [BurntSushi/toml](https://github.com/BurntSushi/toml) — config parsing
257257+258258+## Changelog
259259+260260+See [CHANGELOG.md](CHANGELOG.md) for what's new.
261261+262262+## Benchmark
263263+264264+neomd's responsiveness depends entirely on your IMAP server. Every folder switch, email open, and move requires IMAP round-trips (SELECT + UID SEARCH + FETCH). Here are real measurements from the same machine, same network:
265265+266266+**Hostpoint** (dedicated email provider) — folder switch: **~33ms total**
267267+| Operation | Time |
268268+|-----------|------|
269269+| SELECT | 12ms |
270270+| UID SEARCH | 10ms |
271271+| FETCH (200 emails) | 76ms |
272272+| MOVE (1 email) | 46ms |
273273+274274+**Gmail** — folder switch: **~570ms total** (17x slower than Hostpoint)
275275+| Operation | Time |
276276+|-----------|------|
277277+| SELECT | 200ms |
278278+| UID SEARCH | 180ms |
279279+| FETCH (2 emails) | 190ms |
280280+| MOVE (1 email) | 339ms |
281281+282282+**Outlook/Office365** (with OAuth2 authentication and different network - not really comparable, but gives a indication) — folder switch: **~269ms total** (8x slower than Hostpoint)
283283+| Operation | Time |
284284+|-----------|------|
285285+| SELECT | 45ms |
286286+| UID SEARCH | 22ms |
287287+| FETCH (10 emails) | 180ms |
288288+| MOVE (1 email) | 21ms |
289289+290290+Interestingly, Gmail benchmarks fast on a **fresh single connection** (`scripts/imap-benchmark.sh` shows ~70ms total, same as Hostpoint). But on a **sustained session** with sequential commands — which is how neomd actually uses IMAP — Gmail adds ~180ms latency per command. This is likely Gmail's internal label-to-folder translation and session management overhead. The result: every action in neomd feels much slower on Gmail, while Hostpoint stays instant.
291291+292292+293293+{{< callout type="info" >}}
294294+**Gmail is not recommended.** If you're on Gmail, consider a dedicated email provider (Hostpoint, Fastmail, HEY, Migadu, etc.) for the best neomd experience. Or use Gmail just for fun :). See [docs/content/docs/configurations/gmail.md](docs/content/docs/configurations/gmail.md) for Gmail-specific folder configuration.
295295+{{< /callout >}}
296296+297297+298298+**Test your own provider:**
299299+```bash
300300+# With password
301301+IMAP_HOST=imap.example.com IMAP_USER=me@example.com IMAP_PASS=secret ./scripts/imap-benchmark.sh
302302+303303+# With OAuth2 (reads token from neomd config)
304304+CONFIG=~/.config/neomd/config.toml IMAP_USER=me@gmail.com ./scripts/imap-benchmark.sh
305305+```
306306+307307+## Security
308308+309309+See [SECURITY.md](SECURITY.md) for how credentials, screener lists, temp files, and network connections are handled — with links to the relevant source files.
310310+311311+## Inspirations
312312+313313+- [Neomutt](https://neomutt.org) — the gold standard terminal email client; neomd reuses its screener list format and borrows keybindings (though most are [custom made](https://github.com/sspaeti/dotfiles/blob/master/mutt/.config/mutt/muttrc) and what I use). I implemented the [HEY screener for Neomutt](https://www.ssp.sh/brain/hey-screener-in-neomutt), see note for more information.
314314+- [HEY](https://www.hey.com/features/the-screener/) — the Screener concept: unknown senders wait for a decision before reaching your inbox
315315+- [hey-cli](https://github.com/basecamp/hey-cli) — a Go CLI for HEY; provided the bubbletea patterns used here
316316+- [newsboat](https://newsboat.org) — RSS reader whose `O` open-in-browser binding and vim navigation feel inspired neomd's reader view
317317+- [emailmd.dev](https://www.emailmd.dev) — the idea that email should be written in Markdown when seen on [HN](https://news.ycombinator.com/item?id=47505144)
318318+- [charmbracelet/pop](https://github.com/charmbracelet/pop) — minimal Go email sender from Charm
319319+- [charmbracelet/glamour](https://github.com/charmbracelet/glamour) — Markdown rendering in the terminal
320320+- [kanagawa.nvim](https://github.com/rebelot/kanagawa.nvim) — the color palette used for the inbox
321321+- [msgvault](https://github.com/wesm/msgvault) — Go IMAP archiver; the IMAP client code in neomd is adapted from it
322322+323323+## Disclaimer
324324+325325+This TUI is mostly [vibe-coded](https://www.ssp.sh/brain/vibe-coding) in the sense that all code is written with Claude Code, but guided by very detailed instructions to make the workflow as I use it and like it to be.
326326+327327+I used my experience with Neomutt, TUIs, and the GTD workflow for handling emails with HEY Screener, and added some (hopefully) _taste_ using my favorite tools and aesthetics. Find the full history at [Twitter](https://xcancel.com/sspaeti/status/2036539855182627169#m) - inspired by seeing [Email.md](https://www.emailmd.dev/) on HackerNews.
328328+329329+If you [rather read the prompt](https://www.ssp.sh/brain/id-rather-read-the-prompt), check out my [initial prompt](https://github.com/ssp-data/neomd/blob/main/docs/initial-prompt/prompt.md) and its generated [plan](https://github.com/ssp-data/neomd/blob/main/docs/initial-prompt/prompt-plan.md) - which I have iterated and added features by the 100s since then.
330330+## Roadmap
331331+332332+See at my second brain at [Roadmap](https://www.ssp.sh/brain/neomd#roadmap).
-331
docs/content/docs/overview.md
···11----
22-title: Overview & Philosophy
33-weight: -10
44----
55-66-A minimal terminal email client for people who write in Markdown and live in Neovim.
77-88-[Neomd](https://www.ssp.sh/brain/neomd/) is my way of implementing an email TUI based on my experience with Neomutt, focusing on [Neovim](https://www.ssp.sh/brain/neovim) (input) and reading/writing in [Markdown](https://www.ssp.sh/brain/markdown) and navigating with [Vim Motions](https://www.ssp.sh/brain/vim-language-and-motions) with the GTD workflow and [HEY-Screener](https://www.hey.com/features/the-screener/).
99-1010-1111-{{< callout type="warning" >}}
1212-neomd moves, deletes, and modifies emails directly on your IMAP server. These operations affect your mailbox across all devices. **Back up important emails before first use.** neomd is experimental software and can't take responsibility for lost or misplaced emails. Consider testing with a secondary email account first.
1313-{{< /callout >}}
1414-1515-1616-## The philosophy behind Neomd: What's unique?
1717-1818-The key here is **speed** in which you can **navigate, read, and process** your email. Everything is just a shortcut away, and instantly (ms not seconds). It's similar to the foundations that Superhuman was built on: it runs on Gmail and makes it fast with vim commands.
1919-2020-With the **HEY-Screener**, you get only emails in your inbox that you _screened in_, no spam or sales pitch before you added them. Or don't like them, just screen them out, and they get automatically moved to the "ScreenedOut" folder.
2121-2222-With the [GTD approach](https://www.ssp.sh/brain/getting-things-done-gtd), using folders such as next (inbox), waiting, someday, scheduled, or archive, you can move them with one shortcut. This allows you quickly to move emails you need to wait for, or deal with later, in the right category. **Processing your email only once**.
2323-2424-With the additional **Feed** and **Papertrail**, two additional features from HEY, you can read newsletters (just hit F) on them automatically in their separate tab, or move all your receipts into the Papertrail. Once you mark them as feed or papertrail, they will moved there automatically going forward. So you decide whether to read emails or news by jumping to different tabs.
2525-2626-2727-{{< callout type="info" >}}
2828-neomd's **speed** depends entirely on your IMAP provider. On Hostpoint (the provider I use), a folder switch takes **~33ms** which feels instant. On Gmail, the same operation takes **~570ms** which is noticeably slow. See [Benchmark](#benchmark) for full details and how to test your provider.
2929-{{< /callout >}}
3030-3131-3232-## Screenshots
3333-3434-### Overview: List-View
3535-3636-Feed view with all Newsletters - also workflow with differnt tabs and unread counter only for certain tabs (not all):
3737-
3838-3939-### Reading Panel
4040-4141-Reading an email with Markdown 💙:
4242-4343-
4444-4545-### Sent emails
4646-This is the markdown sent:
4747-4848-```markdown
4949-# [neomd: to: email@domain.com]
5050-# [neomd: subject: this is an email from neomd!]
5151-5252-This email is from Neomd. Great I can add links such as [this](https://ssp.sh) with plain Markdown.
5353-5454-E.g. **bold** or _italic_.
5555-5656-## Does headers work too?
5757-5858-this is a text before a h3.
5959-6060-### H3 header
6161-6262-how does that look in an email?
6363-Best regards
6464-```
6565-6666-*Compose emails in your editor, read them rendered with [glamour](https://github.com/charmbracelet/glamour), and manage your inbox with a [HEY-style screener](https://www.hey.com/features/the-screener/) — all from the terminal.*
6767-6868-6969-Which looks like this:
7070-7171-
7272-7373-Or in Gmail:
7474-
7575-7676-7777-### Video
7878-7979- YouTube rundown of most features:
8080- [](https://youtu.be/8aKkldYLWV8)
8181-*(shorter but limited showcase [part 1 video](https://youtu.be/lpmHqIrCC-w))*
8282-8383-8484-## Features
8585-8686-- **Write in Markdown, send beautifully** — compose in `$EDITOR` (defaults to `nvim`), send as `multipart/alternative`: raw Markdown as plain text + goldmark-rendered HTML so recipients get clickable links, bold, headers, inline code, and code blocks
8787-- **Pre-send review** — after closing the editor, review To/Subject/body before sending; attach files, save to Drafts, or re-open the editor — no accidental sends
8888-- **Attachments** — attach files from the pre-send screen via yazi (`a`); images appear inline in the email body, other files as attachments; also attach from within neovim via `<leader>a`; the reader lists all attachments (including inline images) and `1`–`9` downloads and opens them
8989-- **Link opener** — links in emails are numbered `[1]`-`[0]` in the reader header; press `space+digit` to open in `$BROWSER`
9090-- **CC, BCC, Reply-all** — optional Cc/Bcc fields (toggle with `ctrl+b`); `R` in the reader replies to sender + all CC recipients
9191-- **Drafts** — `d` in pre-send saves to Drafts (IMAP APPEND); `E` in the reader re-opens a draft as an editable compose; compose sessions are auto-backed up to `~/.cache/neomd/drafts/` so you never lose an unsent email (`:recover` to reopen)
9292-- **HTML signatures** — configure separate text and HTML signatures; text signature appears in editor and plain text part, HTML signature in HTML part only; use `[html-signature]` placeholder to control inclusion per-email
9393-- **Multiple From addresses** — define SMTP-only `[[senders]]` aliases (e.g. `s@ssp.sh` through an existing account); cycle with `ctrl+f` in compose and pre-send; sent copies always land in the Sent folder
9494-- **Undo** — `u` reverses the last move or delete (`x`, `A`, `M*`) using the UIDPLUS destination UID
9595-- **Search** — `/` filters loaded emails in-memory; `space /` or `:search` runs IMAP SEARCH across all folders (only fetching header capped at 100 per folder) with results in a temporary "Search" tab; supports `from:`, `subject:`, `to:` prefixes
9696-- **Address autocomplete** — To/Cc/Bcc fields autocomplete from screener lists; navigate with `ctrl+n`/`ctrl+p`, accept with `tab`
9797-- **Everything view** — `ge` or `:everything` shows the 50 most recent emails across all folders; find emails that were screened out, moved to spam, or otherwise hard to locate
9898-- **Threaded inbox** — related emails are grouped together in the inbox list with a vertical connector line (`│`/`╰`), Twitter-style; threads are detected via `In-Reply-To`/`Message-ID` headers with a subject+participant fallback; newest reply on top, root at bottom; `·` reply indicator shows which emails you've answered
9999-- **Conversation view** — `T` or `:thread` shows the full conversation across folders (Inbox, Sent, Archive, etc.) in a temporary tab with `[Folder]` prefix; see your replies alongside received emails
100100-- **Glamour reading** — incoming emails rendered as styled Markdown in the terminal
101101-- **HEY-style screener** — unknown senders land in `ToScreen`; press `I/O/F/P` to approve, block, mark as Feed, or mark as PaperTrail; reuses your existing `screened_in.txt` lists from neomutt
102102-- **Folder tabs** — Inbox, ToScreen, Feed, PaperTrail, Archive, Waiting, Someday, Scheduled, Sent, Trash, ScreenedOut
103103-- **Multi-select** — `m` marks emails, then batch-delete, move, or screen them all at once
104104-- **Auto-screen on load** — screener runs automatically every time the Inbox loads (startup, `R`); keeps your inbox clean without pressing `S` (configurable, on by default)
105105-- **Background sync** — while neomd is open, inbox is fetched and screened every 5 minutes in the background; interval configurable, set to `0` to disable
106106-- **Kanagawa theme** — colors from the [kanagawa.nvim](https://github.com/rebelot/kanagawa.nvim) palette
107107-- **IMAP + SMTP** — direct connection via RFC 6851 MOVE, no local sync daemon required and keeps it in sync if you use it on mobile or different device
108108-109109-## Install
110110-111111-**Prerequisites:** [Go 1.22+](https://go.dev/doc/install) and `make`.
112112-113113-{{< callout type="info" >}}
114114-**Optional attachment helpers:**
115115-- `yazi` enables the built-in file picker used by pre-send `a`
116116-- custom Neovim integration in `custom.lua` enables inline `<leader>a` attachment insertion inside `neomd-*.md` buffers
117117-- without these, neomd still works; the inline Neovim attachment workflow just won't be available
118118-{{< /callout >}}
119119-120120-121121-```sh
122122-git clone https://github.com/ssp-data/neomd
123123-cd neomd
124124-make install # installs to ~/.local/bin/neomd
125125-```
126126-127127-Or just build locally:
128128-129129-```sh
130130-make build
131131-./neomd
132132-```
133133-134134-Or if on Arch Linux (AUR), you can use my [neomd-bin](https://aur.archlinux.org/packages/neomd-bin) via:
135135-```sh
136136-yay -S neomd-bin
137137-```
138138-139139-140140-On first run, neomd:
141141-1. Creates `~/.config/neomd/config.toml` with placeholders — fill in your IMAP/SMTP credentials
142142- - Important: Make sure that the Capitalization and naming of folder in `config.toml` is accroding to webmail IMAP, e.g. [Gmails](configurations/gmail) uses `sent = "[Gmail]/Sent Mail"` and not `sent` etc.
143143-2. Creates `~/.config/neomd/lists/` for screener allowlists (or uses your custom paths from config)
144144-3. Creates any missing IMAP folders (ToScreen, Feed, PaperTrail, etc.) automatically
145145-146146-147147-Neomd also runs on Android (more for fun) — see [configurations/android](configurations/android).
148148-149149-## Configuration
150150-151151-On first run, neomd creates `~/.config/neomd/config.toml` with placeholders:
152152-153153-```toml
154154-[[accounts]]
155155-name = "Personal"
156156-imap = "imap.example.com:993" # :993 = TLS, :143 = STARTTLS
157157-smtp = "smtp.example.com:587"
158158-user = "me@example.com"
159159-password = "app-password"
160160-from = "Me <me@example.com>"
161161-starttls = false
162162-tls_cert_file = "" # optional PEM cert/CA for self-signed local bridges
163163-164164-# Root-level settings
165165-store_sent_drafts_in_sending_account = false # default: Sent/Drafts use first account; true = follow sending account
166166-167167-[screener]
168168-screened_in = "~/.config/neomd/lists/screened_in.txt"
169169-screened_out = "~/.config/neomd/lists/screened_out.txt"
170170-feed = "~/.config/neomd/lists/feed.txt"
171171-papertrail = "~/.config/neomd/lists/papertrail.txt"
172172-spam = "~/.config/neomd/lists/spam.txt"
173173-```
174174-175175-Use an app-specific password (Gmail, Fastmail, Hostpoint, etc.) rather than your main account password. The `password` and `user` fields support environment variable expansion (`$VAR` or `${VAR}`) so you can avoid storing secrets in the config file.
176176-177177-For the full configuration reference including multiple accounts, OAuth2 authentication, `[[senders]]` aliases, folder customization, signatures, and UI options, see [configuration](configuration).
178178-179179-**Provider-specific guides:**
180180-- Gmail: [configurations/gmail](configurations/gmail) — folder name mapping and OAuth2 setup
181181-- Proton Mail Bridge: [configurations/proton-bridge](configurations/proton-bridge) — non-standard port configuration
182182-183183-### Onboarding
184184-185185-On first launch, **auto-screening is paused** because your screener lists are empty — neomd won't move anything until you've classified your first sender. Your Inbox loads normally so you can explore.
186186-187187-By default, neomd loads and auto-screens only the newest `200` Inbox emails (`[ui].inbox_count`). This keeps startup predictable. If you want to re-screen the entire Inbox on the IMAP server, run `:screen-all` inside neomd; that scans every Inbox email, not just the loaded subset, and can take a while on large mailboxes.
188188-189189-**Getting started with the screener:**
190190-191191-1. From your Inbox, pick an email and press `I` (screen **in**) to approve the sender, or `O` (screen **out**) to block them. This creates your first screener list entry.
192192-2. Once you've classified at least one sender, auto-screening activates on every Inbox load — new emails from known senders are sorted automatically.
193193-3. Unknown senders land in the `ToScreen` tab. Jump there with `gk` (or `Tab`, use `L` or click the tab) and classify them:
194194- - `I` screen **in** — sender stays in Inbox forever
195195- - `O` screen **out** — sender never reaches Inbox again
196196- - `F` **feed** — newsletters go to the Feed tab
197197- - `P` **papertrail** — receipts go to the PaperTrail tab
198198-4. Use `m` to mark multiple emails, then `I` to batch-approve them all at once. From the `ToScreen` folder, approving/blocking a single unmarked message now applies to all currently queued mail from that sender.
199199-200200-**The best part:** all classifications are saved permanently in your screener lists (`screened_in.txt`, `screened_out.txt`, etc.). An email address screened in will automatically go to your Inbox, and any email screened out will never be in your Inbox again.
201201-202202-You choose who can land in your Inbox. Bye-bye spam. This is the beauty of [HEY-Screener](https://www.hey.com/features/the-screener/), and neomd implements the same concept.
203203-204204-{{< callout type="info" >}}
205205-To disable auto-screening entirely, set `auto_screen_on_load = false` in `[ui]` config. Run `:debug` inside neomd if something isn't working.
206206-{{< /callout >}}
207207-208208-209209-{{< callout type="warning" >}}
210210-`:screen-all` operates on the full Inbox mailbox on the server, not just the emails currently loaded in the UI. Use it when you intentionally want a mailbox-wide reclassification pass.
211211-{{< /callout >}}
212212-213213-214214-### Screener Workflow
215215-216216-Find full Screener Workflow at [screener](screener), classification tables, and bulk re-classification instructions.
217217-### Keybindings
218218-219219-Press `?` inside neomd to open the interactive help overlay. Start typing to filter shortcuts.
220220-221221-See the [full keybindings reference](keybindings) (auto-generated from [`internal/ui/keys.go`](internal/ui/keys.go) via `make docs`).
222222-223223-### How Sending Works
224224-225225-Compose in Markdown, send as `multipart/alternative` (plain text + HTML). Attachments, CC/BCC, multiple From addresses, drafts, and pre-send review are all supported.
226226-227227-Discarding unsent mail now asks for confirmation in compose/pre-send, and `:recover` reopens the latest backup if you want to resume after an abort.
228228-229229-- See [sending](sending) for details on MIME structure, attachments, pre-send review, and drafts.
230230-- See [reading](reading) for the reader: images, inline links, attachments, and navigation.
231231-232232-### Dev: Makefile Commands
233233-234234-```
235235-make build compile ./neomd
236236-make run build and run
237237-make install install to ~/.local/bin
238238-make test run tests
239239-make vet go vet
240240-make fmt gofmt -w .
241241-make tidy go mod tidy
242242-make clean remove compiled binary
243243-make help print this list
244244-```
245245-246246-## Stack
247247-248248-- [Bubble Tea](https://github.com/charmbracelet/bubbletea) — TUI framework
249249-- [Bubbles](https://github.com/charmbracelet/bubbles) — list, viewport, textinput components
250250-- [Glamour](https://github.com/charmbracelet/glamour) — Markdown → terminal rendering
251251-- [Lipgloss](https://github.com/charmbracelet/lipgloss) — styling
252252-- [go-imap/v2](https://github.com/emersion/go-imap) — IMAP client (RFC 6851 MOVE)
253253-- [go-message](https://github.com/emersion/go-message) — MIME parsing
254254-- [goldmark](https://github.com/yuin/goldmark) — Markdown → HTML for sending
255255-- [BurntSushi/toml](https://github.com/BurntSushi/toml) — config parsing
256256-257257-## Changelog
258258-259259-See [CHANGELOG.md](CHANGELOG.md) for what's new.
260260-261261-## Benchmark
262262-263263-neomd's responsiveness depends entirely on your IMAP server. Every folder switch, email open, and move requires IMAP round-trips (SELECT + UID SEARCH + FETCH). Here are real measurements from the same machine, same network:
264264-265265-**Hostpoint** (dedicated email provider) — folder switch: **~33ms total**
266266-| Operation | Time |
267267-|-----------|------|
268268-| SELECT | 12ms |
269269-| UID SEARCH | 10ms |
270270-| FETCH (200 emails) | 76ms |
271271-| MOVE (1 email) | 46ms |
272272-273273-**Gmail** — folder switch: **~570ms total** (17x slower than Hostpoint)
274274-| Operation | Time |
275275-|-----------|------|
276276-| SELECT | 200ms |
277277-| UID SEARCH | 180ms |
278278-| FETCH (2 emails) | 190ms |
279279-| MOVE (1 email) | 339ms |
280280-281281-**Outlook/Office365** (with OAuth2 authentication and different network - not really comparable, but gives a indication) — folder switch: **~269ms total** (8x slower than Hostpoint)
282282-| Operation | Time |
283283-|-----------|------|
284284-| SELECT | 45ms |
285285-| UID SEARCH | 22ms |
286286-| FETCH (10 emails) | 180ms |
287287-| MOVE (1 email) | 21ms |
288288-289289-Interestingly, Gmail benchmarks fast on a **fresh single connection** (`scripts/imap-benchmark.sh` shows ~70ms total, same as Hostpoint). But on a **sustained session** with sequential commands — which is how neomd actually uses IMAP — Gmail adds ~180ms latency per command. This is likely Gmail's internal label-to-folder translation and session management overhead. The result: every action in neomd feels much slower on Gmail, while Hostpoint stays instant.
290290-291291-292292-{{< callout type="info" >}}
293293-**Gmail is not recommended.** If you're on Gmail, consider a dedicated email provider (Hostpoint, Fastmail, HEY, Migadu, etc.) for the best neomd experience. Or use Gmail just for fun :). See [configurations/gmail](configurations/gmail) for Gmail-specific folder configuration.
294294-{{< /callout >}}
295295-296296-297297-**Test your own provider:**
298298-```bash
299299-# With password
300300-IMAP_HOST=imap.example.com IMAP_USER=me@example.com IMAP_PASS=secret ./scripts/imap-benchmark.sh
301301-302302-# With OAuth2 (reads token from neomd config)
303303-CONFIG=~/.config/neomd/config.toml IMAP_USER=me@gmail.com ./scripts/imap-benchmark.sh
304304-```
305305-306306-## Security
307307-308308-See [SECURITY.md](SECURITY.md) for how credentials, screener lists, temp files, and network connections are handled — with links to the relevant source files.
309309-310310-## Inspirations
311311-312312-- [Neomutt](https://neomutt.org) — the gold standard terminal email client; neomd reuses its screener list format and borrows keybindings (though most are [custom made](https://github.com/sspaeti/dotfiles/blob/master/mutt/.config/mutt/muttrc) and what I use). I implemented the [HEY screener for Neomutt](https://www.ssp.sh/brain/hey-screener-in-neomutt), see note for more information.
313313-- [HEY](https://www.hey.com/features/the-screener/) — the Screener concept: unknown senders wait for a decision before reaching your inbox
314314-- [hey-cli](https://github.com/basecamp/hey-cli) — a Go CLI for HEY; provided the bubbletea patterns used here
315315-- [newsboat](https://newsboat.org) — RSS reader whose `O` open-in-browser binding and vim navigation feel inspired neomd's reader view
316316-- [emailmd.dev](https://www.emailmd.dev) — the idea that email should be written in Markdown when seen on [HN](https://news.ycombinator.com/item?id=47505144)
317317-- [charmbracelet/pop](https://github.com/charmbracelet/pop) — minimal Go email sender from Charm
318318-- [charmbracelet/glamour](https://github.com/charmbracelet/glamour) — Markdown rendering in the terminal
319319-- [kanagawa.nvim](https://github.com/rebelot/kanagawa.nvim) — the color palette used for the inbox
320320-- [msgvault](https://github.com/wesm/msgvault) — Go IMAP archiver; the IMAP client code in neomd is adapted from it
321321-322322-## Disclaimer
323323-324324-This TUI is mostly [vibe-coded](https://www.ssp.sh/brain/vibe-coding) in the sense that all code is written with Claude Code, but guided by very detailed instructions to make the workflow as I use it and like it to be.
325325-326326-I used my experience with Neomutt, TUIs, and the GTD workflow for handling emails with HEY Screener, and added some (hopefully) _taste_ using my favorite tools and aesthetics. Find the full history at [Twitter](https://xcancel.com/sspaeti/status/2036539855182627169#m) - inspired by seeing [Email.md](https://www.emailmd.dev/) on HackerNews.
327327-328328-If you [rather read the prompt](https://www.ssp.sh/brain/id-rather-read-the-prompt), check out my [initial prompt](docs/initial-prompt/prompt.md) and its generated [plan](docs/initial-prompt/prompt-plan.md) - which I have iterated and added features by the 100s since then.
329329-## Roadmap
330330-331331-See at my second brain at [Roadmap](https://www.ssp.sh/brain/neomd#roadmap).
+2-2
scripts/sync-readme-to-docs.sh
···6677REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
88README="$REPO_ROOT/README.md"
99-DOCS_OVERVIEW="$REPO_ROOT/docs/content/docs/overview.md"
99+DOCS_OVERVIEW="$REPO_ROOT/docs/content/docs/_index.md"
10101111if [[ ! -f "$README" ]]; then
1212 echo "Error: README.md not found at $README"
···7171 -e 's|images/|/images/|g' \
7272 >> "$DOCS_OVERVIEW"
73737474-echo "✅ Synced README.md → docs/content/docs/overview.md"
7474+echo "✅ Synced README.md → docs/content/docs/_index.md"
7575echo " Next: Run 'make docs-build' to regenerate the site"