Commit cc29162037
Unsigned
Layout: unified · split
content/blog/2025-11-02-aerc-macos.org added +418
| @@ -0,0 +1,418 @@ | |||
| 1 | #+date: <2025-11-02 Sun 10:07:14> | ||
| 2 | #+title: Using aerc on macOS | ||
| 3 | #+description: Learn how I use aerc, a TUI email client, on macOS. | ||
| 4 | #+slug: aerc-macos | ||
| 5 | |||
| 6 | #+caption: aerc-mail | ||
| 7 | https://img.cleberg.net/blog/20251102-aerc-macos/read.webp | ||
| 8 | |||
| 9 | Shout-out to [[https://baty.net/posts/2025/10/using-aerc-for-email/][Jack Baty]] for inspiring me to write about my =aerc-mail= client | ||
| 10 | after chatting about it over email recently! | ||
| 11 | |||
| 12 | * Overview | ||
| 13 | |||
| 14 | [[https://aerc-mail.org/][aerc]] is a text user interface (TUI) email client with intuitive defaults, easy | ||
| 15 | setup, and hassle-free operations. This post will walk through my personal | ||
| 16 | configuration for aerc on macOS. | ||
| 17 | |||
| 18 | * Installation | ||
| 19 | |||
| 20 | Since I am on macOS, I opted to install aerc from Homebrew. There are build | ||
| 21 | options for additional modules if you build from source, but I am okay with the | ||
| 22 | default options. | ||
| 23 | |||
| 24 | You can view the information about the package prior to installing it, if you | ||
| 25 | wish: | ||
| 26 | |||
| 27 | #+begin_src sh | ||
| 28 | brew info aerc | ||
| 29 | #+end_src | ||
| 30 | |||
| 31 | #+begin_src sh | ||
| 32 | ==> aerc: stable 0.21.0 (bottled), HEAD | ||
| 33 | Email client that runs in your terminal | ||
| 34 | https://aerc-mail.org/ | ||
| 35 | Installed | ||
| 36 | /opt/homebrew/Cellar/aerc/0.21.0 (48 files, 23MB) * | ||
| 37 | Poured from bottle using the formulae.brew.sh API on 2025-10-05 at 19:51:31 | ||
| 38 | From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/a/aerc.rb | ||
| 39 | License: MIT | ||
| 40 | ==> Dependencies | ||
| 41 | Build: go ✘, scdoc ✘ | ||
| 42 | Required: notmuch ✔ | ||
| 43 | ==> Options | ||
| 44 | --HEAD | ||
| 45 | Install HEAD version | ||
| 46 | ==> Downloading https://formulae.brew.sh/api/formula/aerc.json | ||
| 47 | ==> Analytics | ||
| 48 | install: 136 (30 days), 560 (90 days), 1,772 (365 days) | ||
| 49 | install-on-request: 136 (30 days), 560 (90 days), 1,772 (365 days) | ||
| 50 | build-error: 3 (30 days) | ||
| 51 | #+end_src | ||
| 52 | |||
| 53 | Once you confirm you want to install this package, run the following: | ||
| 54 | |||
| 55 | #+begin_src sh | ||
| 56 | brew install aerc | ||
| 57 | #+end_src | ||
| 58 | |||
| 59 | * First-Time Wizard | ||
| 60 | |||
| 61 | When you run aerc for the first time, an IMAP & SMTP wizard will walk you | ||
| 62 | through a basic setup for your email account(s). | ||
| 63 | |||
| 64 | On macOS, aerc will save your configuration files in the following location: | ||
| 65 | =~/Library/Preferences/aerc=. | ||
| 66 | |||
| 67 | I tried to create a =~/.config/aerc= directory, but the Library directory above | ||
| 68 | took precedence. If you truly need to keep your files in =~/.config=, you could | ||
| 69 | probably symlink that to the Library location. However, I accepted my fate and | ||
| 70 | will just edit the files within the Library directory. | ||
| 71 | |||
| 72 | * Configuration | ||
| 73 | |||
| 74 | Once you have installed and setup aerc for the first time, you can configure the | ||
| 75 | client and account settings. | ||
| 76 | |||
| 77 | As noted above, I will be working within the Library directory that aerc | ||
| 78 | defaults to when installed via brew. | ||
| 79 | |||
| 80 | #+begin_src sh | ||
| 81 | cd ~/Library/Preferences/aerc | ||
| 82 | #+end_src | ||
| 83 | |||
| 84 | This directory should contain the following files. You can open the | ||
| 85 | =accounts.conf= file to confirm it contains the information you input during the | ||
| 86 | wizard process. | ||
| 87 | |||
| 88 | | File | Purpose | | ||
| 89 | |-----------------+-------------------------------------------------------------| | ||
| 90 | | =accounts.conf= | Contains account-specific settings for IMAP & SMTP accounts | | ||
| 91 | | =aerc.conf= | Contains program-wide settings | | ||
| 92 | | =binds.conf= | Contains key bindings for aerc | | ||
| 93 | |||
| 94 | I'll share my personal configuration, which is close to the defaults that | ||
| 95 | existed at the time of writing this post. | ||
| 96 | |||
| 97 | =accounts.conf= | ||
| 98 | |||
| 99 | This is the information from the wizard. The only further customization I | ||
| 100 | provided is the default signature file to be appended to each message I compose. | ||
| 101 | |||
| 102 | #+begin_src conf | ||
| 103 | [Personal] | ||
| 104 | source = imaps://hello%40cleberg.net:PASSWORD@imap.migadu.com | ||
| 105 | outgoing = smtps://hello%40cleberg.net:PASSWORD@smtp.migadu.com | ||
| 106 | default = INBOX | ||
| 107 | from = Christian Cleberg <hello@cleberg.net> | ||
| 108 | copy-to = Sent | ||
| 109 | cache-headers = true | ||
| 110 | signature-file = /Users/cmc/Library/Preferences/aerc/sigs/default | ||
| 111 | #+end_src | ||
| 112 | |||
| 113 | =sigs/default= | ||
| 114 | |||
| 115 | Any content within this file will be appended to my messages, as noted above. | ||
| 116 | |||
| 117 | #+begin_src text | ||
| 118 | Christian Cleberg <hello@cleberg.net> | ||
| 119 | GPG: 3917 973F B159 BBB8 6194 5385 69451A51 7AC0 CB37 | ||
| 120 | #+end_src | ||
| 121 | |||
| 122 | =aerc.conf= | ||
| 123 | |||
| 124 | This contains quite a few customizations for my preferences related to | ||
| 125 | downloads, encryption, mouse controls, editor, and message composition. | ||
| 126 | |||
| 127 | #+begin_src conf | ||
| 128 | [general] | ||
| 129 | # Prefer downloads to be saved to the Downloads folder | ||
| 130 | default-save-path=~/Downloads | ||
| 131 | # Use GPG for PGP encryption | ||
| 132 | pgp-provider=gpg | ||
| 133 | |||
| 134 | [ui] | ||
| 135 | # Enable mouse controls for scrolling, paging, etc. | ||
| 136 | mouse-enabled=true | ||
| 137 | |||
| 138 | [statusline] | ||
| 139 | [viewer] | ||
| 140 | # Use emacs instead of vi | ||
| 141 | pager=/usr/local/bin/emacs_viewer | ||
| 142 | |||
| 143 | [compose] | ||
| 144 | # Use emacs instead of vi | ||
| 145 | editor=emacs | ||
| 146 | # Add Cc as a default field to view each time I compose a message | ||
| 147 | header-layout=To|From,Cc,Subject | ||
| 148 | # Allow me to edit headers when composing (so I can customize From address) | ||
| 149 | edit-headers=true | ||
| 150 | # Warn me if I mentioned these words but don't attach a file | ||
| 151 | empty-subject-warning=true | ||
| 152 | no-attachment-warning=^[^>]*attach(ed|ment) | ||
| 153 | |||
| 154 | [multipart-converters] | ||
| 155 | |||
| 156 | [filters] | ||
| 157 | text/plain=colorize | ||
| 158 | text/calendar=calendar | ||
| 159 | message/delivery-status=colorize | ||
| 160 | message/rfc822=colorize | ||
| 161 | text/html=! html | ||
| 162 | .headers=colorize | ||
| 163 | |||
| 164 | [openers] | ||
| 165 | [hooks] | ||
| 166 | [templates] | ||
| 167 | #+end_src | ||
| 168 | |||
| 169 | #+begin_quote | ||
| 170 | *IMPORTANT*: If you use the =pager= option above that I'm using, you will need | ||
| 171 | to create the =emacs_viewer= file as well. | ||
| 172 | #+end_quote | ||
| 173 | |||
| 174 | You can create the file following my process below, which will create an | ||
| 175 | executable in the location specific in my =aerc.conf= file. I put it in | ||
| 176 | =/usr/local/bin= but you can put it anywhere as long as it exists in the file | ||
| 177 | path specific within your configuration file. | ||
| 178 | |||
| 179 | #+begin_src sh | ||
| 180 | sudo nano /usr/local/bin/emacs_viewer | ||
| 181 | #+end_src | ||
| 182 | |||
| 183 | Paste this inside the file: | ||
| 184 | |||
| 185 | #+begin_src sh | ||
| 186 | #!/bin/bash | ||
| 187 | emacs -nw - < "$1" | ||
| 188 | #+end_src | ||
| 189 | |||
| 190 | #+begin_src sh | ||
| 191 | sudo chmod +x /usr/local/bin/emacs_viewer | ||
| 192 | #+end_src | ||
| 193 | |||
| 194 | =binds.conf= | ||
| 195 | |||
| 196 | Finally, here are the key bindings. I did not change these, but I'm showing them | ||
| 197 | here in case you have an issue and want to see the defaults. | ||
| 198 | |||
| 199 | #+begin_src conf | ||
| 200 | # Binds are of the form <key sequence> = <command to run> | ||
| 201 | # To use '=' in a key sequence, substitute it with "Eq": "<Ctrl+Eq>" | ||
| 202 | # If you wish to bind #, you can wrap the key sequence in quotes: "#" = quit | ||
| 203 | <C-p> = :prev-tab<Enter> | ||
| 204 | <C-PgUp> = :prev-tab<Enter> | ||
| 205 | <C-n> = :next-tab<Enter> | ||
| 206 | <C-PgDn> = :next-tab<Enter> | ||
| 207 | \[t = :prev-tab<Enter> | ||
| 208 | \]t = :next-tab<Enter> | ||
| 209 | <C-t> = :term<Enter> | ||
| 210 | ? = :help keys<Enter> | ||
| 211 | <C-c> = :prompt 'Quit?' quit<Enter> | ||
| 212 | <C-q> = :prompt 'Quit?' quit<Enter> | ||
| 213 | <C-z> = :suspend<Enter> | ||
| 214 | |||
| 215 | [messages] | ||
| 216 | q = :prompt 'Quit?' quit<Enter> | ||
| 217 | |||
| 218 | j = :next<Enter> | ||
| 219 | <Down> = :next<Enter> | ||
| 220 | <C-d> = :next 50%<Enter> | ||
| 221 | <C-f> = :next 100%<Enter> | ||
| 222 | <PgDn> = :next 100%<Enter> | ||
| 223 | |||
| 224 | k = :prev<Enter> | ||
| 225 | <Up> = :prev<Enter> | ||
| 226 | <C-u> = :prev 50%<Enter> | ||
| 227 | <C-b> = :prev 100%<Enter> | ||
| 228 | <PgUp> = :prev 100%<Enter> | ||
| 229 | g = :select 0<Enter> | ||
| 230 | G = :select -1<Enter> | ||
| 231 | |||
| 232 | J = :next-folder<Enter> | ||
| 233 | <C-Down> = :next-folder<Enter> | ||
| 234 | K = :prev-folder<Enter> | ||
| 235 | <C-Up> = :prev-folder<Enter> | ||
| 236 | H = :collapse-folder<Enter> | ||
| 237 | <C-Left> = :collapse-folder<Enter> | ||
| 238 | L = :expand-folder<Enter> | ||
| 239 | <C-Right> = :expand-folder<Enter> | ||
| 240 | |||
| 241 | v = :mark -t<Enter> | ||
| 242 | <Space> = :mark -t<Enter>:next<Enter> | ||
| 243 | V = :mark -v<Enter> | ||
| 244 | |||
| 245 | T = :toggle-threads<Enter> | ||
| 246 | zc = :fold<Enter> | ||
| 247 | zo = :unfold<Enter> | ||
| 248 | za = :fold -t<Enter> | ||
| 249 | zM = :fold -a<Enter> | ||
| 250 | zR = :unfold -a<Enter> | ||
| 251 | <tab> = :fold -t<Enter> | ||
| 252 | |||
| 253 | zz = :align center<Enter> | ||
| 254 | zt = :align top<Enter> | ||
| 255 | zb = :align bottom<Enter> | ||
| 256 | |||
| 257 | <Enter> = :view<Enter> | ||
| 258 | d = :choose -o y 'Really delete this message' delete-message<Enter> | ||
| 259 | D = :delete<Enter> | ||
| 260 | a = :archive flat<Enter> | ||
| 261 | A = :unmark -a<Enter>:mark -T<Enter>:archive flat<Enter> | ||
| 262 | |||
| 263 | C = :compose<Enter> | ||
| 264 | m = :compose<Enter> | ||
| 265 | |||
| 266 | b = :bounce<space> | ||
| 267 | |||
| 268 | rr = :reply -a<Enter> | ||
| 269 | rq = :reply -aq<Enter> | ||
| 270 | Rr = :reply<Enter> | ||
| 271 | Rq = :reply -q<Enter> | ||
| 272 | |||
| 273 | c = :cf<space> | ||
| 274 | $ = :term<space> | ||
| 275 | ! = :term<space> | ||
| 276 | | = :pipe<space> | ||
| 277 | |||
| 278 | / = :search<space> | ||
| 279 | \ = :filter<space> | ||
| 280 | n = :next-result<Enter> | ||
| 281 | N = :prev-result<Enter> | ||
| 282 | <Esc> = :clear<Enter> | ||
| 283 | |||
| 284 | s = :split<Enter> | ||
| 285 | S = :vsplit<Enter> | ||
| 286 | |||
| 287 | pl = :patch list<Enter> | ||
| 288 | pa = :patch apply <Tab> | ||
| 289 | pd = :patch drop <Tab> | ||
| 290 | pb = :patch rebase<Enter> | ||
| 291 | pt = :patch term<Enter> | ||
| 292 | ps = :patch switch <Tab> | ||
| 293 | |||
| 294 | [messages:folder=Drafts] | ||
| 295 | <Enter> = :recall<Enter> | ||
| 296 | |||
| 297 | [view] | ||
| 298 | / = :toggle-key-passthrough<Enter>/ | ||
| 299 | q = :close<Enter> | ||
| 300 | O = :open<Enter> | ||
| 301 | o = :open<Enter> | ||
| 302 | S = :save<space> | ||
| 303 | | = :pipe<space> | ||
| 304 | D = :delete<Enter> | ||
| 305 | A = :archive flat<Enter> | ||
| 306 | |||
| 307 | <C-y> = :copy-link <space> | ||
| 308 | <C-l> = :open-link <space> | ||
| 309 | |||
| 310 | f = :forward<Enter> | ||
| 311 | rr = :reply -a<Enter> | ||
| 312 | rq = :reply -aq<Enter> | ||
| 313 | Rr = :reply<Enter> | ||
| 314 | Rq = :reply -q<Enter> | ||
| 315 | |||
| 316 | H = :toggle-headers<Enter> | ||
| 317 | <C-k> = :prev-part<Enter> | ||
| 318 | <C-Up> = :prev-part<Enter> | ||
| 319 | <C-j> = :next-part<Enter> | ||
| 320 | <C-Down> = :next-part<Enter> | ||
| 321 | J = :next<Enter> | ||
| 322 | <C-Right> = :next<Enter> | ||
| 323 | K = :prev<Enter> | ||
| 324 | <C-Left> = :prev<Enter> | ||
| 325 | |||
| 326 | [view::passthrough] | ||
| 327 | $noinherit = true | ||
| 328 | $ex = <C-x> | ||
| 329 | <Esc> = :toggle-key-passthrough<Enter> | ||
| 330 | |||
| 331 | [compose] | ||
| 332 | # Keybindings used when the embedded terminal is not selected in the compose | ||
| 333 | # view | ||
| 334 | $noinherit = true | ||
| 335 | $ex = <C-x> | ||
| 336 | $complete = <C-o> | ||
| 337 | <C-k> = :prev-field<Enter> | ||
| 338 | <C-Up> = :prev-field<Enter> | ||
| 339 | <C-j> = :next-field<Enter> | ||
| 340 | <C-Down> = :next-field<Enter> | ||
| 341 | <A-p> = :switch-account -p<Enter> | ||
| 342 | <C-Left> = :switch-account -p<Enter> | ||
| 343 | <A-n> = :switch-account -n<Enter> | ||
| 344 | <C-Right> = :switch-account -n<Enter> | ||
| 345 | <tab> = :next-field<Enter> | ||
| 346 | <backtab> = :prev-field<Enter> | ||
| 347 | <C-p> = :prev-tab<Enter> | ||
| 348 | <C-PgUp> = :prev-tab<Enter> | ||
| 349 | <C-n> = :next-tab<Enter> | ||
| 350 | <C-PgDn> = :next-tab<Enter> | ||
| 351 | |||
| 352 | [compose::editor] | ||
| 353 | # Keybindings used when the embedded terminal is selected in the compose view | ||
| 354 | $noinherit = true | ||
| 355 | $ex = <C-x> | ||
| 356 | <C-k> = :prev-field<Enter> | ||
| 357 | <C-Up> = :prev-field<Enter> | ||
| 358 | <C-j> = :next-field<Enter> | ||
| 359 | <C-Down> = :next-field<Enter> | ||
| 360 | <C-p> = :prev-tab<Enter> | ||
| 361 | <C-PgUp> = :prev-tab<Enter> | ||
| 362 | <C-n> = :next-tab<Enter> | ||
| 363 | <C-PgDn> = :next-tab<Enter> | ||
| 364 | |||
| 365 | [compose::review] | ||
| 366 | # Keybindings used when reviewing a message to be sent | ||
| 367 | # Inline comments are used as descriptions on the review screen | ||
| 368 | y = :send<Enter> # Send | ||
| 369 | n = :abort<Enter> # Abort (discard message, no confirmation) | ||
| 370 | s = :sign<Enter> # Toggle signing | ||
| 371 | x = :encrypt<Enter> # Toggle encryption to all recipients | ||
| 372 | v = :preview<Enter> # Preview message | ||
| 373 | p = :postpone<Enter> # Postpone | ||
| 374 | q = :choose -o d discard abort -o p postpone postpone<Enter> # Abort or postpone | ||
| 375 | e = :edit<Enter> # Edit (body and headers) | ||
| 376 | a = :attach<space> # Add attachment | ||
| 377 | d = :detach<space> # Remove attachment | ||
| 378 | |||
| 379 | [terminal] | ||
| 380 | $noinherit = true | ||
| 381 | $ex = <C-x> | ||
| 382 | |||
| 383 | <C-p> = :prev-tab<Enter> | ||
| 384 | <C-n> = :next-tab<Enter> | ||
| 385 | <C-PgUp> = :prev-tab<Enter> | ||
| 386 | <C-PgDn> = :next-tab<Enter> | ||
| 387 | #+end_src | ||
| 388 | |||
| 389 | * Operation | ||
| 390 | |||
| 391 | Now that I have configured the program to my liking, I can run =aerc= in my | ||
| 392 | terminal and see my changes reflected! | ||
| 393 | |||
| 394 | Personally, I keep aerc running in a terminal at all times, since it has the | ||
| 395 | operation to ring a bell when a new message arrives. | ||
| 396 | |||
| 397 | #+caption: Notification | ||
| 398 | [[https://img.cleberg.net/blog/20251102-aerc-macos/notification.webp]] | ||
| 399 | |||
| 400 | The mailbox views and composition screens are fairly standard, but it does a | ||
| 401 | great job at displaying information effectively. | ||
| 402 | |||
| 403 | #+caption: Inbox | ||
| 404 | [[https://img.cleberg.net/blog/20251102-aerc-macos/inbox.webp]] | ||
| 405 | |||
| 406 | #+caption: Compose Message | ||
| 407 | [[https://img.cleberg.net/blog/20251102-aerc-macos/compose.webp]] | ||
| 408 | |||
| 409 | #+caption: Send Message | ||
| 410 | [[https://img.cleberg.net/blog/20251102-aerc-macos/send.webp]] | ||
| 411 | |||
| 412 | #+caption: Read Message | ||
| 413 | [[https://img.cleberg.net/blog/20251102-aerc-macos/read.webp]] | ||
| 414 | |||
| 415 | All in all, I love aerc and far prefer it to my previous client (neomutt). In | ||
| 416 | certain cases, Thunderbird is more convenient, but I don't find myself opening | ||
| 417 | it at all lately. I will definitely update this post in the future if I find | ||
| 418 | anything extraordinary to add about aerc. | ||