A minimal email TUI where you read with Markdown and write in Neovim. neomd.ssp.sh/docs
email markdown neovim tui
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

using my fork with improved neomd features

sspaeti b6e69a18 248876c6

+104 -74
+1
README.md
··· 142 142 - **Link opener** — links in emails are numbered `[1]`-`[0]` in the reader header; press `space+digit` to open in `$BROWSER` 143 143 - **CC, BCC, Reply-all** — optional Cc/Bcc fields (toggle with `ctrl+b`); `R` in the reader replies to sender + all CC recipients 144 144 - **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 145 + - **GitHub/Obsidian-style callouts in emails** — compose emails with callout syntax `> [!note]`, `> [!tip]`, `> [!warning]` for styled alert boxes in HTML emails; rendered with colored left borders, subtle backgrounds, and emoji icons 145 146 - **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) 146 147 - **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 147 148 - **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
+1
docs/content/docs/_index.md
··· 149 149 - **Link opener** — links in emails are numbered `[1]`-`[0]` in the reader header; press `space+digit` to open in `$BROWSER` 150 150 - **CC, BCC, Reply-all** — optional Cc/Bcc fields (toggle with `ctrl+b`); `R` in the reader replies to sender + all CC recipients 151 151 - **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 152 + - **GitHub/Obsidian-style callouts in emails** — compose emails with callout syntax `> [!note]`, `> [!tip]`, `> [!warning]` for styled alert boxes in HTML emails; rendered with colored left borders, subtle backgrounds, and emoji icons 152 153 - **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) 153 154 - **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 154 155 - **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
+63 -67
docs/content/docs/sending.md
··· 14 14 - **Images** → `multipart/related` with `Content-ID` — displayed inline in the email body 15 15 - **Other files** (PDF, zip, …) → `multipart/mixed` — shown as downloadable attachments 16 16 17 - ## Callouts (Admonition) 18 - 19 - neomd supports GitHub/Obsidian-style [callouts](https://www.ssp.sh/brain/admonition-call-outs) through the [this extension (with my fork)](https://github.com/sspaeti/goldmark-obsidian-callout-for-neomd) for highlighted information boxes in your emails. Use the `> [!TYPE]` syntax to create styled alert boxes: 20 - 21 - This is how it looks at the recievers end: 22 - ![neomd](images/callouts.png) 23 - 24 - ```markdown 25 - > [!note] 26 - > This is a note callout with default styling 27 - 28 - > [!tip] Pro Tip 29 - > Use custom titles by adding text after the type 30 - 31 - > [!warning] Important 32 - > Callouts can have multiple paragraphs 33 - > 34 - > Just add blank blockquote lines between them 35 - 36 - > [!important] 37 - > Recipients see colored boxes with icons in HTML email clients 38 - > while plain text clients show it as a blockquote 39 - ``` 40 - 41 - **Available callout types:** 42 - - `[!note]` — Blue info box 43 - - `[!tip]` — Green success/tip box 44 - - `[!important]` — Purple important box 45 - - `[!warning]` — Yellow warning box 46 - - `[!caution]` — Red caution/danger box 47 - 48 - **Features:** 49 - - Custom titles — add text after the type: `> [!warning] Security Alert` 50 - - Multiple paragraphs — use `> ` (blockquote with space) for blank lines 51 - - Works in both syntaxes: `> [!note]` (with space) or `>[!note]` (without space) 52 - 53 - **What recipients see:** 54 - 55 - HTML email clients (Gmail, Outlook, Apple Mail) display callouts as colored boxes with: 56 - - Colored left border (4px solid) 57 - - Colored background 58 - - Bold title with icon 59 - - Proper spacing and padding 60 - 61 - >[!NOTE] 62 - > Plain text email clients show callouts as regular blockquotes (graceful degradation). 63 - 64 - **Example in composed email:** 65 - 66 - ```markdown 67 - Hi team, 68 - 69 - Here's the update on the project: 70 - 71 - > [!tip] Good News 72 - > We're ahead of schedule! The new feature shipped yesterday. 73 - 74 - > [!warning] Action Required 75 - > Please review the security audit by Friday. 76 - > 77 - > Contact @security if you have questions. 78 - 79 - Thanks, 80 - Simon 81 - ``` 82 - 83 - 84 17 ## Multiple From Addresses 85 18 86 19 Add `[[senders]]` blocks to config to define extra identities that share an existing account's SMTP credentials: ··· 252 185 For full HTML signature configuration examples, see [Configuration Reference](configuration#html-signatures). 253 186 254 187 For reading emails — images, links, attachments, and navigation — see [Reading Emails](reading). 188 + 189 + ## Callouts (Admonition) 190 + 191 + neomd supports GitHub/Obsidian-style [callouts](https://www.ssp.sh/brain/admonition-call-outs) through the [this extension (with my fork)](https://github.com/sspaeti/goldmark-obsidian-callout-for-neomd) for highlighted information boxes in your emails. Use the `> [!TYPE]` syntax to create styled alert boxes: 192 + 193 + This is how it looks at the recievers end: 194 + ![neomd](/images/callouts.png) 195 + 196 + ```markdown 197 + > [!note] 198 + > This is a note callout with default styling 199 + 200 + > [!tip] Pro Tip 201 + > Use custom titles by adding text after the type 202 + 203 + > [!warning] Important 204 + > Callouts can have multiple paragraphs 205 + > 206 + > Just add blank blockquote lines between them 207 + 208 + ``` 209 + 210 + **Available callout types:** 211 + - `[!note]` — Blue info box 212 + - `[!tip]` — Green success/tip box 213 + - `[!warning]` — Yellow warning box 214 + 215 + **Features:** 216 + - Custom titles — add text after the type: `> [!warning] Security Alert` 217 + - Multiple paragraphs — use `> ` (blockquote with space) for blank lines 218 + - Works in both syntaxes: `> [!note]` (with space) or `>[!note]` (without space) 219 + 220 + **What recipients see:** 221 + 222 + HTML email clients (Gmail, Outlook, Apple Mail) display callouts as colored boxes with: 223 + - Colored left border (4px solid) 224 + - Colored background 225 + - Bold title with icon 226 + - Proper spacing and padding 227 + 228 + >[!NOTE] 229 + > Plain text email clients show callouts as regular blockquotes (graceful degradation). 230 + 231 + **Example in composed email:** 232 + 233 + ```markdown 234 + Hi team, 235 + 236 + Here's the update on the project: 237 + 238 + > [!tip] Good News 239 + > We're ahead of schedule! The new feature shipped yesterday. 240 + 241 + > [!warning] Action Required 242 + > Please review the security audit by Friday. 243 + > 244 + > Contact @security if you have questions. 245 + 246 + Thanks, 247 + Simon 248 + ``` 249 + 250 +
+1 -3
go.mod
··· 5 5 require ( 6 6 github.com/BurntSushi/toml v1.6.0 7 7 github.com/JohannesKaufmann/html-to-markdown v1.6.0 8 - github.com/VojtaStruhar/goldmark-obsidian-callout v0.1.0 9 8 github.com/charmbracelet/bubbles v1.0.0 10 9 github.com/charmbracelet/bubbletea v1.3.10 11 10 github.com/charmbracelet/glamour v0.9.1 ··· 13 12 github.com/emersion/go-imap/v2 v2.0.0-beta.8 14 13 github.com/emersion/go-message v0.18.2 15 14 github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 15 + github.com/sspaeti/goldmark-obsidian-callout-for-neomd v0.1.1 16 16 github.com/yuin/goldmark v1.7.8 17 17 golang.org/x/oauth2 v0.35.0 18 18 ) ··· 52 52 golang.org/x/term v0.30.0 // indirect 53 53 golang.org/x/text v0.23.0 // indirect 54 54 ) 55 - 56 - replace github.com/VojtaStruhar/goldmark-obsidian-callout => /home/sspaeti/git/email/goldmark-obsidian-callout-for-neomd
+2
go.sum
··· 98 98 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 99 99 github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= 100 100 github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= 101 + github.com/sspaeti/goldmark-obsidian-callout-for-neomd v0.1.1 h1:obWsWMJZGW4w9Qj1/WKLDweR+olq23r24UPS86XTbQU= 102 + github.com/sspaeti/goldmark-obsidian-callout-for-neomd v0.1.1/go.mod h1:p/gHio8liSHKd6Zcig2LFSU8Ym1yV9L+Dt1eYP9F6+I= 101 103 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 102 104 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 103 105 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+35 -3
internal/integration_test.go
··· 462 462 defer cli.Close() 463 463 464 464 subject := uniqueSubject("signature") 465 - // Simulate a compose with signature (same format as editor.Prelude adds) 466 - body := "Hi there,\n\nThis is the email body.\n\n" + 465 + // Simulate a compose with signature and callouts (same format as editor.Prelude adds) 466 + body := "Hi team,\n\n" + 467 + "Here's the update on the project:\n\n" + 468 + "> [!tip] Good News\n" + 469 + "> We're ahead of schedule! The new feature shipped yesterday.\n\n" + 470 + "> [!warning] Action Required\n" + 471 + "> Please review the security audit by Friday.\n\n" + 472 + "> [!note] note\n" + 473 + "> Please read\n\n" + 474 + "Thanks,\n" + 475 + "Simon\n\n" + 467 476 "-- \n" + 468 477 "**Simon Späti**\n" + 469 478 "Data Engineer, [SSP Data](https://ssp.sh/)\n" ··· 495 504 } 496 505 497 506 // Body content before signature should also be rendered 498 - if !strings.Contains(rawHTML, "email body") { 507 + if !strings.Contains(rawHTML, "update on the project") { 499 508 t.Errorf("HTML missing email body text, got: %s", truncate(rawHTML, 500)) 509 + } 510 + 511 + // Callout rendering verification 512 + if !strings.Contains(rawHTML, "callout callout-tip") { 513 + t.Errorf("HTML missing tip callout class, got: %s", truncate(rawHTML, 800)) 514 + } 515 + if !strings.Contains(rawHTML, "callout callout-warning") { 516 + t.Errorf("HTML missing warning callout class, got: %s", truncate(rawHTML, 800)) 517 + } 518 + if !strings.Contains(rawHTML, "callout callout-note") { 519 + t.Errorf("HTML missing note callout class, got: %s", truncate(rawHTML, 800)) 520 + } 521 + if !strings.Contains(rawHTML, "💡") { // Light bulb emoji for tip 522 + t.Errorf("HTML missing tip callout icon, got: %s", truncate(rawHTML, 800)) 523 + } 524 + if !strings.Contains(rawHTML, "⚠️") { // Warning sign emoji 525 + t.Errorf("HTML missing warning callout icon, got: %s", truncate(rawHTML, 800)) 526 + } 527 + if !strings.Contains(rawHTML, "Good News") { 528 + t.Errorf("HTML missing custom callout title, got: %s", truncate(rawHTML, 800)) 529 + } 530 + if !strings.Contains(rawHTML, "ahead of schedule") { 531 + t.Errorf("HTML missing callout content, got: %s", truncate(rawHTML, 800)) 500 532 } 501 533 } 502 534
+1 -1
internal/render/html.go
··· 4 4 "bytes" 5 5 "fmt" 6 6 7 - callout "github.com/VojtaStruhar/goldmark-obsidian-callout" 7 + callout "github.com/sspaeti/goldmark-obsidian-callout-for-neomd" 8 8 "github.com/yuin/goldmark" 9 9 "github.com/yuin/goldmark/extension" 10 10 "github.com/yuin/goldmark/renderer/html"