{ pkgs, ... }: let # Nix store paths are world-readable, so accounts.conf (IMAP/SMTP passwords) # stays hand-managed in ~/.config/aerc and out of this module. opener = if pkgs.stdenv.isDarwin then "open" else "xdg-open"; # Renders the markdown body of a composed mail into the text/html half of a # multipart/alternative message (see [multipart-converters] below). mdToHtml = pkgs.writeShellScriptBin "aerc-md2html" '' body=$(${pkgs.cmark-gfm}/bin/cmark-gfm \ --extension autolink \ --extension table \ --extension strikethrough \ --extension tasklist) cat < $body HTML ''; notify = if pkgs.stdenv.isDarwin then # -activate focuses Ghostty when the banner is clicked. "${pkgs.terminal-notifier}/bin/terminal-notifier" + " -group \"aerc-$AERC_ACCOUNT\"" + " -title \"$AERC_ACCOUNT/$AERC_FOLDER\"" + " -subtitle \"$AERC_FROM_NAME\"" + " -message \"$AERC_SUBJECT\"" + " -activate com.mitchellh.ghostty" else "${pkgs.libnotify}/bin/notify-send" + " \"[$AERC_ACCOUNT/$AERC_FOLDER] $AERC_FROM_NAME\"" + " \"$AERC_SUBJECT\""; in { home.file = { ".config/aerc/aerc.conf".text = '' [general] default-save-path=~/Downloads pgp-provider=auto default-menu-cmd=fzf --reverse --prompt='> ' [ui] styleset-name=tokyonight mouse-enabled=true threading-enabled=true fuzzy-complete=true keyhint=true keyhint-delay=300ms sidebar-width=24 index-columns=flags:4,name<20%,subject,date>= timestamp-format=2006-01-02 15:04 this-day-time-format=15:04 this-week-time-format=Mon 15:04 this-year-time-format=02 Jan empty-message=(no messages) empty-dirlist=(no folders) [statusline] column-left=[{{.Account}}] {{.StatusInfo}} column-center={{.PendingKeys}} column-right={{.TrayInfo}} [viewer] pager=less -Rc alternatives=text/plain,text/html show-headers=false header-layout=From|To,Cc|Bcc,Date,Subject parse-http-links=true [compose] editor=nvim -c 'set ft=mail' -c 'set textwidth=72' -c 'set nolist' header-layout=To|From,Subject focus-body=true empty-subject-warning=true no-attachment-warning=^[^>]*attach(ed|ment) [multipart-converters] text/html=${mdToHtml}/bin/aerc-md2html [hooks] mail-received=${notify} [filters] text/plain=colorize text/calendar=calendar message/delivery-status=colorize message/rfc822=colorize text/html=! html .headers=colorize [openers] x-scheme-handler/http*=${opener} {} x-scheme-handler/mailto=${opener} {} text/html=${opener} {} application/pdf=${opener} {} image/*=${opener} {} ''; ".config/aerc/binds.conf".text = '' # Part/field navigation is bound twice: C-j/C-k as aerc ships them, plus # ]p/[p and Alt-j/Alt-k (right Option = Alt, see modules/home/ghostty.nix) # which keep working if vim-tmux-navigator's tmux side is ever installed, # since it claims C-h/j/k/l for any pane that is not running vim. = :prev-tab = :next-tab \[t = :prev-tab # Previous tab \]t = :next-tab # Next tab = :term ? = :help keys = :prompt 'Quit?' quit = :suspend [messages] q = :prompt 'Quit?' quit . = :repeat j = :next = :next k = :prev = :prev = :next 50% = :prev 50% = :next 100% = :prev 100% = :next 100% = :prev 100% gg = :select 0 # First message G = :select -1 zz = :align center zt = :align top zb = :align bottom J = :next-folder K = :prev-folder gi = :cf INBOX # Inbox ga = :cf Archive # Archive gd = :cf Drafts # Drafts gs = :cf Sent # Sent gj = :cf Junk # Junk gt = :cf Trash # Trash gf = :menu -dc fzf :cf # Find folder (fzf) c = :cf = :view v = :mark -t = :mark -t:next V = :mark -v tt = :toggle-threads # Threads ts = :toggle-sidebar # Sidebar tf = :toggle-folder # Folder subscription tc = :toggle-thread-context # Thread context zc = :fold zo = :unfold za = :fold -t zM = :fold -a zR = :unfold -a = :fold -t C = :compose r = :reply -aq R = :reply -q f = :forward b = :bounce d = :choose -o y 'Really delete this message' delete-message D = :delete a = :archive flat A = :unmark -a:mark -T:archive flat M = :move mm = :move # Move to folder mc = :copy # Copy to folder mr = :read # Mark read mu = :unread # Mark unread mf = :flag -t -x flagged # Toggle flagged mU = :unsubscribe # Unsubscribe from list s = :split S = :vsplit | = :pipe $ = :term / = :search \ = :filter n = :next-result N = :prev-result = :clear = :clear Fu = :filter -u # Unread Ff = :filter -x flagged # Flagged Fs = :filter -f # From sender Ft = :filter -t # To recipient Fd = :filter -d # Date range Fc = :clear # Clear filter F/ = :filter # Filter prompt [messages:folder=Drafts] = :recall [view] # The viewer forwards every key it does not bind itself to the pager, so # globals like ? and : never arrive: bind them again here. $ex = : ? = :help keys / = :toggle-key-passthrough/ q = :close = :close o = :open O = :open S = :save s = :save -fpa ~/Downloads | = :pipe d = :choose -o y 'Really delete this message' delete-message D = :delete a = :archive flat f = :forward r = :reply -aq R = :reply -q H = :toggle-headers th = :toggle-headers # Headers = :next-part = :next-part = :prev-part = :prev-part \]p = :next-part # Next part \[p = :prev-part # Previous part \]m = :next # Next message \[m = :prev # Previous message J = :next K = :prev gx = :menu -lc fzf :open-link # Open link (fzf) gy = :copy-link [view::passthrough] $noinherit = true $ex = = :toggle-key-passthrough [compose] $noinherit = true $ex = $complete = = :next-field = :prev-field = :next-field = :next-field = :prev-field = :prev-field = :next-field = :prev-field = :switch-account -p = :switch-account -n = :prev-tab = :next-tab [compose::editor] $noinherit = true $ex = = :next-field = :next-field = :prev-field = :prev-field = :next-field = :prev-field = :prev-tab = :next-tab [compose::review] # aerc has no "always send HTML" setting, so the send key adds the # text/html alternative first; Y sends the plain text part on its own. y = :multipart text/html:send # Send (HTML + plain text) Y = :send # Send plain text only h = :multipart text/html # Add HTML alternative H = :multipart -d text/html # Drop HTML alternative n = :abort # Abort (discard message, no confirmation) s = :sign # Toggle signing x = :encrypt # Toggle encryption to all recipients v = :preview # Preview message p = :postpone # Postpone q = :choose -o d discard abort -o p postpone postpone # Abort or postpone e = :edit # Edit (body and headers) a = :attach # Add attachment A = :menu -c 'fzf --multi' :attach # Add attachment (fzf) d = :detach # Remove attachment [terminal] $noinherit = true $ex = = :prev-tab = :next-tab ''; # tokyonight-storm, matching Ghostty (modules/home/ghostty.nix). Background # is left at the terminal default so the panes blend with the window. ".config/aerc/stylesets/tokyonight".text = '' *.default=true *.normal=true default.fg=#c0caf5 error.fg=#f7768e warning.fg=#ff9e64 success.fg=#9ece6a title.fg=#1f2335 title.bg=#7aa2f7 title.bold=true header.fg=#7aa2f7 header.bold=true realname.fg=#c0caf5 email.fg=#565f89 subject.fg=#c0caf5 subject.bold=true date.fg=#565f89 tab.fg=#545c7e tab.bg=#1f2335 tab.selected.fg=#c0caf5 tab.selected.bg=#292e42 tab.selected.bold=true border.fg=#292e42 border.bg=#292e42 spinner.fg=#7dcfff msglist_default.fg=#a9b1d6 msglist_unread.fg=#c0caf5 msglist_unread.bold=true msglist_read.fg=#737aa2 msglist_answered.fg=#bb9af7 msglist_forwarded.fg=#9d7cd8 msglist_flagged.fg=#e0af68 msglist_flagged.bold=true msglist_deleted.fg=#565f89 msglist_deleted.dim=true msglist_result.fg=#9ece6a msglist_result.bold=true msglist_thread_folded.fg=#7dcfff msglist_thread_context.fg=#565f89 msglist_thread_orphan.fg=#ff9e64 msglist_marked.fg=#1f2335 msglist_marked.bg=#7dcfff msglist_gutter.fg=#292e42 msglist_pill.fg=#1f2335 msglist_pill.bg=#7aa2f7 msglist_*.selected.bg=#292e42 msglist_*.selected.bold=true dirlist_default.fg=#a9b1d6 dirlist_unread.fg=#7aa2f7 dirlist_unread.bold=true dirlist_recent.fg=#7dcfff dirlist_*.selected.bg=#292e42 dirlist_*.selected.bold=true statusline_default.fg=#c0caf5 statusline_default.bg=#1f2335 statusline_error.fg=#f7768e statusline_error.bold=true statusline_warning.fg=#ff9e64 statusline_success.fg=#9ece6a statusline_success.bold=true completion_default.fg=#a9b1d6 completion_default.selected.bg=#292e42 completion_description.fg=#565f89 completion_gutter.bg=#1f2335 completion_pill.bg=#7aa2f7 keyhint_default.bg=#1f2335 keyhint_key.fg=#7dcfff keyhint_key.bold=true keyhint_desc.fg=#a9b1d6 part_switcher.bg=#1f2335 part_filename.fg=#7dcfff part_mimetype.fg=#565f89 part_*.selected.bg=#292e42 selector_default.fg=#a9b1d6 selector_focused.fg=#c0caf5 selector_focused.bg=#292e42 selector_chooser.bold=true [viewer] url.fg=#7dcfff url.underline=true header.fg=#7aa2f7 header.bold=true signature.fg=#565f89 signature.dim=true diff_meta.fg=#c0caf5 diff_meta.bold=true diff_chunk.fg=#7dcfff diff_chunk_func.fg=#7dcfff diff_chunk_func.bold=true diff_add.fg=#9ece6a diff_del.fg=#f7768e diff_whitespace.bg=#f7768e quote_1.fg=#7aa2f7 quote_2.fg=#bb9af7 quote_3.fg=#7dcfff quote_4.fg=#565f89 quote_x.fg=#565f89 quote_x.dim=true code.fg=#9ece6a ''; }; }