From 97051b5cc27f514c9c1d81ede97a86b70f9694a5 Mon Sep 17 00:00:00 2001 From: Yorick Barbanneau Date: Mon, 11 Aug 2025 00:17:39 +0200 Subject: [PATCH] chore(mail): rework neomutt configuration * add mailcap to handle attachment * add per-account mutt hook * remove Trash folder sync and unassign Trash for each accounts * remove `dd` keybinding * add `Ctrl+b` macro to launch urlscan --- .../home-manager/accounts/email/default.nix | 56 +++++++++++++------ .../accounts/email/files/theme.muttrc | 35 +++++++----- 2 files changed, 58 insertions(+), 33 deletions(-) diff --git a/modules/home-manager/accounts/email/default.nix b/modules/home-manager/accounts/email/default.nix index 069583a..d9c1152 100644 --- a/modules/home-manager/accounts/email/default.nix +++ b/modules/home-manager/accounts/email/default.nix @@ -35,9 +35,14 @@ in defaultSetting = { mbsync = { enable = true; - create = "maildir"; + create = "both"; expunge = "both"; remove = "both"; + patterns = [ + "*" + # Trash is a pain in the ass to manage with notmuch + "!Trash" + ]; }; msmtp = { enable = true; @@ -76,7 +81,7 @@ in extraConfig = '' [FolderNameFilter] folder_explicit_list = archives - folder_transforms = archives:archive + folder_transforms = archives:archived maildir_separator = / [MailMover] folders = ${lib.concatStringsSep " " (lib.unique (lib.mapAttrsToList (n: v: "${n}") mailMoverRules))} @@ -104,13 +109,15 @@ in in { enable = true; unmailboxes = true; + changeFolderWhenSourcingAccount = true; + sourcePrimaryAccount = false; editor = "nvim +/^$/ +':nohl'"; settings = { sleep_time = "1"; mbox_type = "Maildir"; header_color_partial = "yes"; duplicate_threads = "yes"; - index_format = "'%4C| %24.24?GS?%F %GS &%F? %?GR?%GR &%?GU?%GU & ??%?GA?%GA & ?%?GE?%GE & ?%?M?󰘕 %s&%s? %* %?g?  %g? %<[y?%<[m?%<[d?%9[%H:%M ]&%9[%a %d ]>&%9[%b %d ]>&%9[%m/%y ]>'"; + index_format = "'%4C| %24.24?GS?%F %GS &%F? %?GD?%GD &%?GR?%GR &%?GU?%GU & ???%?GA?%GA & ?%?GE?%GE & ?%?M?󰘕 %s&%s? %* %?g?  %g? %<[y?%<[m?%<[d?%9[%H:%M ]&%9[%a %d ]>&%9[%b %d ]>&%9[%m/%y ]>'"; sort_aux = "last-date-sent"; mail_check = "120"; hidden_tags = "inbox,unread,draft,flagged,passed,replied,signed,encrypted,attachment,sent"; @@ -131,24 +138,37 @@ in my_pager = "'  %F |  %s %* Pos: %P '"; compose_format = "' COMPOSE  %a | 󰉉 %l'"; query_command = "'${pkgs.khard}/bin/khard email --parsable --search-in-source-files %s'"; + virtual_spoolfile = "yes"; + mail_check_stats = "yes"; + mh_purge = "yes"; + mailcap_path= "${config.xdg.configHome}/neomutt/mailcap"; }; extraConfig = '' ${(builtins.readFile ./files/theme.muttrc)} charset-hook ^iso-8859-1$ cp1252 ignore * + unignore from date subject to cc bcc tags user-agent x-mailer + + # Attachment auto_view text/x-vcard text/html text/enriched text/calendar alternative_order text/html text/enriched text/plain text/* - tag-transforms "attachment" "󰁦" \ + + tag-transforms "attachment" "󰁦" \ "encrypted" "󱧈" \ "signed" "󱅞" \ "unread" "" \ "replied" "" - tag-formats "attachment" "GA" \ - "encrypted" "GE" \ - "signed" "GS" \ - "unread" "GU" \ - "replied" "GR" + tag-formats "attachment" "GA" \ + "encrypted" "GE" \ + "signed" "GS" \ + "unread" "GU" \ + "replied" "GR" + + ${lib.concatStrings (lib.attrsets.mapAttrsToList ( _: v: if lib.hasAttrByPath [ "neomuttHooks" ] v then v.neomuttHooks else "" ) cfg.accountConfigs)} + # manually source first account instead of use home-manager parameter because + # of $my_pager expansion does not work as this variable is not already set + source ${config.xdg.configHome}/neomutt/${cfg.primary} ''; binds = [ { map = [ "attach" "browser" "index" "pager" ]; key = "g"; action = "noop"; } @@ -172,12 +192,8 @@ in { map = [ "index" ]; key = "\\Ce"; action = "next-line";} { map = [ "index" ]; key = "\\Cy"; action = "previous-line";} - # Delete - { map = [ "pager" "index" ]; key = "d "; action = "noop";} - { map = [ "pager" "index" ]; key = "dd "; action = "delete-message";} - # Reply - { map =[ "pager" "index" ]; key = "R "; action = "group-reply";} + { map =[ "pager" "index" ];gkey = "R"; action = "group-reply";} # sidebar { map = [ "index" "pager" ]; key = ""; action = "sidebar-toggle-visible";} @@ -196,17 +212,15 @@ in { map = [ "index" "pager" ]; key = "\\eX"; action = "vfolder-from-query";} # generate virtual folder from query with time window - { map = [ "index" ]; key = "<"; action = "vfolder-window-backward";} - { map = [ "index" ]; key = ">"; action = "vfolder-window-forward";} { map = [ "index" "pager" ]; key = "\\CD"; action = "modify-tags";} # Editor { map = [ "editor" ]; key = ""; action = "complete-query";} - { map = [ "editor" ]; key = "^T "; action = "complete";} + { map = [ "editor" ]; key = "^T"; action = "complete";} ]; macros = [ - #Define some macros here + { map = [ "pager" ]; key = "\\CB"; action = "${pkgs.urlscan}/bin/urlscan -d -c --color true"; } ] ++ accountMacros; }; programs.mbsync = { @@ -228,5 +242,11 @@ in enable = true; }; home.file."${config.xdg.configHome}/urlscan/config.json".source = ./files/urlscan.config.json; + xdg.configFile."neomutt/mailcap".text = '' + text/html; ${pkgs.xdg-utils}/bin/xdg-open %s; nametemplate=%html + text/html; ${pkgs.w3m}/bin/w3m -I %{charset} -cols 90 -T text/html %s; copiousoutput + application/pdf; ${pkgs.xdg-utils}/bin/xdg-open %s & + image/*; ${pkgs.xdg-utils}/bin/xdg-open %s & + ''; }; } diff --git a/modules/home-manager/accounts/email/files/theme.muttrc b/modules/home-manager/accounts/email/files/theme.muttrc index e7ea68c..e3e0baa 100644 --- a/modules/home-manager/accounts/email/files/theme.muttrc +++ b/modules/home-manager/accounts/email/files/theme.muttrc @@ -1,5 +1,4 @@ # vi: ft=muttrc -# base16-mutt: base16-shell support for mutt ## Base color normal color20 default # softer, bold @@ -30,7 +29,7 @@ color index_author color20 default color index_date color20 default color index_flags color20 default color index_tag color20 default -color index_tags color20 default +color index_tags italic color08 default color index_author italic color06 default "~g !~V" color index_author color04 default "~g ~V" @@ -48,14 +47,16 @@ color index_date italic color00 color06 "~T" color index_tags italic color00 color06 "~T" color index_tag italic color00 color06 "~T" +color index_subject color01 color18 "~D" # deleted messages + color index_subject italic bold color00 default "~T (~U|~N|~O)" ## Weak -color index color01 default "~v~(~F)" # collapsed thread with flagged inside +color index color01 default "~v~(~F)" # collapsed thread with flagged inside # Selection color index italic color00 color06 "~T" # tagged messages -color index color01 color18 "~D" # deleted messages +color index default color18 "~D" # deleted messages ### Message Headers ---------------------------------------------------- @@ -77,22 +78,26 @@ color header color06 color00 "((@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0- # When possible, these regular expressions attempt to match http://spec.commonmark.org/ ## Weak # ~~~ Horizontal rules ~~~ -color body color08 color00 "([[:space:]]*[-+=#*~_]){3,}[[:space:]]*" -## Strong +color body color08 default "([[:space:]]*[-+=#*~_]){3,}[[:space:]]*" + # *Bold* span -color body brightcolor03 color00 "(^|[[:space:][:punct:]])\\*[^*]+\\*([[:space:][:punct:]]|$)" +color body brightcolor03 default "(^|[[:space:][:punct:]])\\*[^*]+\\*([[:space:][:punct:]]|$)" + # _Underline_ span -color body color05 color00 "(^|[[:space:][:punct:]])_[^_]+_([[:space:][:punct:]]|$)" +color body underline color20 default "(^|[[:space:][:punct:]])_[^_]+_([[:space:][:punct:]]|$)" + # /Italic/ span (Sometimes gets directory names) -color body color05 color00 "(^|[[:space:][:punct:]])/[^/]+/([[:space:][:punct:]]|$)" +color body italic color20 default "(^|[[:space:][:punct:]])/[^/]+/([[:space:][:punct:]]|$)" + # ATX headers -color body color04 color00 "^[[:space:]]{0,3}#+[[:space:]].*$" -## Highlight +color body color04 default "^[[:space:]]{0,3}#+[[:space:]].*$" + # `Code` span -color body color02 color00 "(^|[[:space:][:punct:]])\`[^\`]+\`([[:space:][:punct:]]|$)" -# Indented code block -color body color02 color00 "^[[:space:]]{4,}.*$" -# URLs +color body color05 default "(^|[[:space:][:punct:]])\`[^\`]+\`([[:space:][:punct:]]|$)" + +color body bold color01 default "^(\\*[[:space:]])" + +## URI color body italic color04 color00 "([a-z][a-z0-9+-]*://(((([a-z0-9_.!~*'();:&=+$,-]|%[0-9a-f][0-9a-f])*@)?((([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)(:[0-9]+)?)|([a-z0-9_.!~*'()$,;:@&=+-]|%[0-9a-f][0-9a-f])+)(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?(#([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?|(www|ftp)\\.(([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?(:[0-9]+)?(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?(#([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?)[^].,:;!)? \t\r\n<>\"]" # Email addresses color body color06 color00 "((@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]),)*@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]):)?[0-9a-z_.+%$-]+@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\])"