Tool for mutt to reply outlook html mail with markdown power
Project description
Muttlook
A unified Python tool for reading, replying, and composing HTML emails in mutt/neomutt. Handles Outlook/OWA emails with Markdown composition, terminal rendering, and browser viewing.
Features
- TUI rendering — styled terminal output via
html2text --colourwith Tokyo Night Storm ANSI colors (bold→purple, headers→blue, forwarded headers→dim gray, Teams boilerplate stripped) - Browser viewing — full HTML with inline CID images resolved, opens in default browser
- Markdown reply/compose — write in markdown, send as HTML preserving Outlook thread formatting
- Obsidian-style extensions: callouts (
> [!note]), task checkboxes ([/][-][>]),strikethrough, definition lists, fenced code, tables - Inline image CID handling for both replies and new messages
- Reply detection via
In-Reply-TowithReferencesheader fallback - Graceful fallback to new message mode when notmuch can't find the original
- Built-in email trimming (
mutt-trim) - Notmuch integration for message lookup
Installation
# From GitHub
uv tool install "muttlook @ git+https://github.com/monkeyxite/muttlook.git" --force
# From local clone
uv tool install -e . --force
Dependencies
Python (auto-installed)
click, mail-parser (>=3.9.3), mail-parser-reply, markdown (>=3.1.1), pymdown-extensions (>=10.0), rich (>=13.0), shortuuid
System
html2text— Rust crate (cargo install html2text-cli) for TUI rendering (--colourmode)- Pandoc — HTML template processing for new messages
- Notmuch — finding original messages by Message-ID
- Neomutt — MUA integration
Actions
| Action | Input | Output | Use case |
|---|---|---|---|
--action tui <file> |
Raw HTML file | Styled ANSI text to stdout | Neomutt mailcap pager, nm-html-extract |
--action tui (stdin) |
Full RFC822 email | Styled ANSI text to stdout | Pipe from notmuch show |
--action tui-rich |
Raw HTML file or RFC822 | Rich-styled ANSI text | Experimental alternative renderer |
--action view (stdin) |
Full RFC822 email | Opens HTML in browser | ,w macro, nms Ctrl+O |
--action draft (stdin) |
Email draft from neomutt | HTML file + mutt_cmd | ,m macro, nms Ctrl+R |
--action clean |
— | Removes temp files | Send hook cleanup |
Use Cases
1. Neomutt pager (auto_view HTML)
Mailcap entry — renders HTML emails inline in the neomutt pager:
text/html; muttlook --action tui %s; nametemplate=%s.html; copiousoutput;
2. nm-search (nms) fzf preview
nm-html-extract extracts the HTML part via notmuch and pipes to muttlook:
fzf --preview → nm-html-extract → notmuch show --part=N → muttlook --action tui <file>
3. nm-search inline shortcuts
Ctrl+O → muttlook --action view (browser)
Ctrl+R → muttlook --action draft (reply via neomutt)
Ctrl+F → notmuch tag (GTD: archive/action/waiting/defer/done)
4. Neomutt compose macros
Reply to Outlook email with markdown:
macro compose ,m "<first-entry>\
<pipe-entry>notmuch new 2>/dev/null; muttlook --action draft<enter>\
<enter-command>set compose_confirm_detach_first=no<enter>\
<enter-command>source ~/.cache/muttlook/mutt_cmd<enter>\
<enter-command>set compose_confirm_detach_first=yes<enter>" "reply with md→html"
View HTML in browser with inline images:
macro pager,attach ,w "<pipe-message>muttlook --action view<enter>" "view HTML in browser"
5. Markdown in replies
# Summary
- Action item 1
- Action item 2
| Name | Task |
|------|------|
| Alice | Review |
> [!note] Reminder
> Meeting moved to Friday
- [x] Done
- [ ] Pending
- [/] In progress
- [-] Cancelled
- [>] Deferred
~~cancelled item~~
Term
: Definition
TUI Rendering Pipeline
Input HTML
→ Charset auto-detect (from <meta> tag)
→ Outlook MsoNormal paragraph merge
→ html2text --colour (Rust binary, 120 cols)
→ ANSI color remap:
bold yellow (38;5;11) → purple (35)
# headers → bold blue (1;34) / cyan (1;36)
forwarded headers (From/Sent/To/Cc) → dim gray (90)
Original/Forwarded separators → dim gray (90)
→ Teams/Zoom boilerplate strip
→ Blank line collapse
→ stdout
Colors use standard ANSI 16 codes, mapped through kitty's Tokyo Night Storm theme.
How Draft Works
mail-parser-replyseparates your reply from quoted content- Obsidian callouts (
> [!type]) → styled HTML divs - Obsidian checkboxes (
[/]→ ◐,[-]→ ―,[>]→ ▷) - Markdown → HTML via
pymdown-extensions(tables, tasklist, tilde, fenced_code, def_list) - Reply detection:
In-Reply-To→References(last entry) → new message fallback - Original message fetched via
notmuch search --output=files - Inline images → CID attachments (multipart/related)
- HTML reply embedded into original email's HTML structure
mutt_cmdgenerated for neomutt to attach HTML as MIME alternative
Development
uv tool install -e . --force # Install editable
ruff check src/ # Lint
ruff format src/ # Format
uv run --with pytest pytest tests/ -v # Test (27 tests)
Credits
- mu4e-mimelook — Original inspiration
- Konfekt/mutt-trim — Original Perl trimming logic
- html2text — Rust crate for HTML→ANSI terminal rendering
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file muttlook-0.1.0.tar.gz.
File metadata
- Download URL: muttlook-0.1.0.tar.gz
- Upload date:
- Size: 16.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c479d9fe86e80dedc2a253a5343161a22d92eb0721c7c5874aba3ee0b23b6c1
|
|
| MD5 |
91b6fa54d245f9a091e50df086f0d1b7
|
|
| BLAKE2b-256 |
e111d4343597a7c7b86fa194d34f2ffe567570670eebc62693e9f38bd0e1d63c
|
Provenance
The following attestation bundles were made for muttlook-0.1.0.tar.gz:
Publisher:
ci.yml on monkeyxite/muttlook
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
muttlook-0.1.0.tar.gz -
Subject digest:
4c479d9fe86e80dedc2a253a5343161a22d92eb0721c7c5874aba3ee0b23b6c1 - Sigstore transparency entry: 1390249116
- Sigstore integration time:
-
Permalink:
monkeyxite/muttlook@61925850cef7b98faa89e69a12e8cd945c02aa73 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/monkeyxite
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@61925850cef7b98faa89e69a12e8cd945c02aa73 -
Trigger Event:
push
-
Statement type:
File details
Details for the file muttlook-0.1.0-py3-none-any.whl.
File metadata
- Download URL: muttlook-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bace2af826621997681108e017ce2cdbc8576d810fb517d30ded0a81e93e86ff
|
|
| MD5 |
45fb0dfb65b8386d4f63ada142b7ec9c
|
|
| BLAKE2b-256 |
4e49938960ad5ba0e205d1f7cdf484b45b5ff897043768f921895be7c8b43dbd
|
Provenance
The following attestation bundles were made for muttlook-0.1.0-py3-none-any.whl:
Publisher:
ci.yml on monkeyxite/muttlook
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
muttlook-0.1.0-py3-none-any.whl -
Subject digest:
bace2af826621997681108e017ce2cdbc8576d810fb517d30ded0a81e93e86ff - Sigstore transparency entry: 1390249142
- Sigstore integration time:
-
Permalink:
monkeyxite/muttlook@61925850cef7b98faa89e69a12e8cd945c02aa73 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/monkeyxite
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@61925850cef7b98faa89e69a12e8cd945c02aa73 -
Trigger Event:
push
-
Statement type: