diff --git a/bin/mailcap-open b/bin/mailcap-open new file mode 100755 index 0000000..fbf6eaf --- /dev/null +++ b/bin/mailcap-open @@ -0,0 +1,65 @@ +#!/bin/bash +# Open file with mailcap viewer selected via fzf + +export PATH="/etc/profiles/per-user/$USER/bin:/run/current-system/sw/bin:$PATH" + +# Handle piped input or file argument +if [[ -n "$1" ]]; then + file="$1" +else + # Read from stdin to temp file + tmpfile=$(mktemp) + cat > "$tmpfile" + mime=$(file --mime-type -b "$tmpfile") + + # Add extension based on mime type + case "$mime" in + application/pdf) ext=".pdf" ;; + image/png) ext=".png" ;; + image/jpeg) ext=".jpg" ;; + image/gif) ext=".gif" ;; + text/html) ext=".html" ;; + text/plain) ext=".txt" ;; + *) ext="" ;; + esac + + if [[ -n "$ext" ]]; then + mv "$tmpfile" "${tmpfile}${ext}" + file="${tmpfile}${ext}" + else + file="$tmpfile" + fi +fi + +mime=$(file --mime-type -b "$file") + +# Get matching viewers based on mime type +viewers=() +viewers+=("open (default app)") + +case "$mime" in + application/pdf) + viewers+=("zathura") + ;; + image/*) + viewers+=("chafa (terminal)") + ;; + text/html) + viewers+=("w3m (browser)") + viewers+=("less (text)") + ;; + text/*) + viewers+=("less") + ;; +esac + +# Select with fzf +selected=$(printf '%s\n' "${viewers[@]}" | fzf --prompt="Open with: " --height=10) + +case "$selected" in + "open (default app)") open "$file" ;; + "chafa (terminal)") chafa "$file"; read -n 1 -s -r -p "Press any key..." ;; + "zathura") zathura "$file" ;; + "w3m (browser)") w3m -T text/html "$file" ;; + "less"*) less "$file" ;; +esac diff --git a/bin/open-attachment b/bin/open-attachment new file mode 100755 index 0000000..2c0e10e --- /dev/null +++ b/bin/open-attachment @@ -0,0 +1,31 @@ +#!/bin/bash +# Open attachment with correct extension based on mime type + +tmpfile=$(mktemp) +cat > "$tmpfile" + +mime=$(file --mime-type -b "$tmpfile") + +case "$mime" in + application/pdf) ext=".pdf" ;; + image/png) ext=".png" ;; + image/jpeg) ext=".jpg" ;; + image/gif) ext=".gif" ;; + text/html) ext=".html" ;; + text/plain) ext=".txt" ;; + application/zip) ext=".zip" ;; + application/msword) ext=".doc" ;; + application/vnd.openxmlformats-officedocument.wordprocessingml.document) ext=".docx" ;; + application/vnd.ms-excel) ext=".xls" ;; + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet) ext=".xlsx" ;; + application/vnd.ms-powerpoint) ext=".ppt" ;; + application/vnd.openxmlformats-officedocument.presentationml.presentation) ext=".pptx" ;; + *) ext="" ;; +esac + +if [[ -n "$ext" ]]; then + mv "$tmpfile" "${tmpfile}${ext}" + tmpfile="${tmpfile}${ext}" +fi + +open "$tmpfile" diff --git a/bin/open-mail b/bin/open-mail new file mode 100755 index 0000000..0521d48 --- /dev/null +++ b/bin/open-mail @@ -0,0 +1,28 @@ +#!/bin/bash +# Open neomutt in Ghostty, or focus existing window + +if [[ "$(uname)" == "Darwin" ]]; then + if aerospace list-windows --all | grep -q 'Mail.*Ghostty\|Ghostty.*Mail'; then + aerospace workspace 8 + else + aerospace workspace 8 + open -na Ghostty --args --title="Mail" -e /etc/profiles/per-user/rayandrew/bin/neomutt + fi +else + # Linux (i3/sway) + if command -v swaymsg &>/dev/null; then + if swaymsg -t get_tree | grep -q '"name": "Mail"'; then + swaymsg workspace 8 + else + swaymsg workspace 8 + ghostty --title="Mail" -e neomutt & + fi + elif command -v i3-msg &>/dev/null; then + if i3-msg -t get_tree | grep -q '"name": "Mail"'; then + i3-msg workspace 8 + else + i3-msg workspace 8 + ghostty --title="Mail" -e neomutt & + fi + fi +fi diff --git a/bin/open-music b/bin/open-music new file mode 100755 index 0000000..0432a7d --- /dev/null +++ b/bin/open-music @@ -0,0 +1,28 @@ +#!/bin/bash +# Open spotify_player in Ghostty, or focus existing window + +if [[ "$(uname)" == "Darwin" ]]; then + if aerospace list-windows --all | grep -q 'Music.*Ghostty\|Ghostty.*Music'; then + aerospace workspace 7 + else + aerospace workspace 7 + open -na Ghostty --args --title="Music" -e /etc/profiles/per-user/rayandrew/bin/spotify_player + fi +else + # Linux (i3/sway) + if command -v swaymsg &>/dev/null; then + if swaymsg -t get_tree | grep -q '"name": "Music"'; then + swaymsg workspace 7 + else + swaymsg workspace 7 + ghostty --title="Music" -e spotify_player & + fi + elif command -v i3-msg &>/dev/null; then + if i3-msg -t get_tree | grep -q '"name": "Music"'; then + i3-msg workspace 7 + else + i3-msg workspace 7 + ghostty --title="Music" -e spotify_player & + fi + fi +fi diff --git a/bin/path-shim b/bin/path-shim new file mode 100755 index 0000000..62181de --- /dev/null +++ b/bin/path-shim @@ -0,0 +1,13 @@ +#!/bin/bash +# Shim to run commands with nix paths available +# Usage: path-shim command args... +# Or: path-shim "command with args" + +export PATH="/etc/profiles/per-user/$USER/bin:/run/current-system/sw/bin:$PATH" + +if [[ $# -eq 1 ]]; then + # Single argument - run it through bash to handle complex commands + exec bash -c "$1" +else + exec "$@" +fi diff --git a/config/aerospace/aerospace.toml b/config/aerospace/aerospace.toml index 80dfb12..31c911b 100644 --- a/config/aerospace/aerospace.toml +++ b/config/aerospace/aerospace.toml @@ -1,4 +1,5 @@ config-version = 2 +start-at-login = true after-startup-command = [ "workspace 10", @@ -9,13 +10,13 @@ persistent-workspaces = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'] enable-normalization-flatten-containers = false enable-normalization-opposite-orientation-for-nested-containers = false on-focused-monitor-changed = ['move-mouse monitor-lazy-center'] -exec-on-workspace-change = ['/bin/bash', '-c', - '/run/current-system/sw/bin/sketchybar --trigger aerospace_workspace_changed FOCUSED_WORKSPACE=$AEROSPACE_FOCUSED_WORKSPACE PREV_WORKSPACE=$AEROSPACE_PREV_WORKSPACE && /etc/profiles/per-user/rayandrew/bin/aerospace-scratchpad hook pull-window $AEROSPACE_PREV_WORKSPACE $AEROSPACE_FOCUSED_WORKSPACE' +exec-on-workspace-change = ['/Users/rayandrew/dotfiles/bin/path-shim', + 'sketchybar --trigger aerospace_workspace_changed FOCUSED_WORKSPACE=$AEROSPACE_FOCUSED_WORKSPACE PREV_WORKSPACE=$AEROSPACE_PREV_WORKSPACE && aerospace-scratchpad hook pull-window $AEROSPACE_PREV_WORKSPACE $AEROSPACE_FOCUSED_WORKSPACE' ] on-focus-changed = [ # 'move-mouse window-lazy-center', - 'exec-and-forget /bin/bash -c /run/current-system/sw/bin/sketchybar --trigger front_app_switched', - 'exec-and-forget /run/current-system/sw/bin/sketchybar --trigger update_windows' + 'exec-and-forget /Users/rayandrew/dotfiles/bin/path-shim sketchybar --trigger front_app_switched', + 'exec-and-forget /Users/rayandrew/dotfiles/bin/path-shim sketchybar --trigger update_windows' ] [gaps] @@ -28,7 +29,8 @@ on-focus-changed = [ [mode.main.binding] alt-enter = 'exec-and-forget open -na Ghostty' - alt-m = ['workspace 7', 'exec-and-forget open -na Ghostty --args --title="Music" -e /etc/profiles/per-user/rayandrew/bin/spotify_player'] + alt-p = 'exec-and-forget ~/dotfiles/bin/open-music' + alt-m = 'exec-and-forget ~/dotfiles/bin/open-mail' alt-tab = 'workspace-back-and-forth' alt-h = 'focus --boundaries-action wrap-around-the-workspace left' @@ -75,14 +77,14 @@ on-focus-changed = [ alt-shift-semicolon = [ 'mode service', - 'exec-and-forget /run/current-system/sw/bin/sketchybar --trigger send_message MESSAGE="SERVICE MODE" HOLD="true"' + 'exec-and-forget /Users/rayandrew/dotfiles/bin/path-shim sketchybar --trigger send_message MESSAGE=SERVICE_MODE HOLD=true' ] alt-r = [ 'mode resize', - 'exec-and-forget /run/current-system/sw/bin/sketchybar --trigger send_message MESSAGE="RESIZE MODE" HOLD="true"' + 'exec-and-forget /Users/rayandrew/dotfiles/bin/path-shim sketchybar --trigger send_message MESSAGE=RESIZE_MODE HOLD=true' ] - cmd-ctrl-1 = "exec-and-forget aerospace-scratchpad show Finder" + cmd-ctrl-1 = "exec-and-forget /Users/rayandrew/dotfiles/bin/path-shim aerospace-scratchpad show Finder" [mode.resize.binding] h = 'resize width -50' @@ -91,18 +93,18 @@ on-focus-changed = [ l = 'resize width +50' esc = [ 'mode main', - 'exec-and-forget /run/current-system/sw/bin/sketchybar --trigger hide_message' + 'exec-and-forget /Users/rayandrew/dotfiles/bin/path-shim sketchybar --trigger hide_message' ] enter = [ 'mode main', - 'exec-and-forget /run/current-system/sw/bin/sketchybar --trigger hide_message' + 'exec-and-forget /Users/rayandrew/dotfiles/bin/path-shim sketchybar --trigger hide_message' ] [mode.service.binding] esc = [ - 'reload-config', - 'exec-and-forget /run/current-system/sw/bin/sketchybar --trigger hide_message', - 'exec-and-forget /run/current-system/sw/bin/sketchybar --reload', + 'reload-config', + 'exec-and-forget /Users/rayandrew/dotfiles/bin/path-shim sketchybar --trigger hide_message', + 'exec-and-forget /Users/rayandrew/dotfiles/bin/path-shim sketchybar --reload', 'mode main', ] r = ['flatten-workspace-tree', 'mode main'] @@ -117,14 +119,14 @@ on-focus-changed = [ p = [ 'exec-and-forget ~/dotfiles/bin/presentation-mode on', - 'exec-and-forget /run/current-system/sw/bin/sketchybar --bar height=0', - 'exec-and-forget /run/current-system/sw/bin/sketchybar --trigger hide_message', + 'exec-and-forget /Users/rayandrew/dotfiles/bin/path-shim sketchybar --bar height=0', + 'exec-and-forget /Users/rayandrew/dotfiles/bin/path-shim sketchybar --trigger hide_message', 'mode main' ] shift-p = [ 'exec-and-forget ~/dotfiles/bin/presentation-mode off', - 'exec-and-forget /run/current-system/sw/bin/sketchybar --bar height=30', - 'exec-and-forget /run/current-system/sw/bin/sketchybar --trigger hide_message', + 'exec-and-forget /Users/rayandrew/dotfiles/bin/path-shim sketchybar --bar height=30', + 'exec-and-forget /Users/rayandrew/dotfiles/bin/path-shim sketchybar --trigger hide_message', 'mode main' ] diff --git a/config/ghostty/config b/config/ghostty/config index 0471732..29c12b3 100644 --- a/config/ghostty/config +++ b/config/ghostty/config @@ -56,3 +56,5 @@ theme = noctis-azureus keybind = all:ctrl+shift+period=text:\x1b\x1f\x4c\x23\x1f keybind = shift+enter=text:\n +keybind = ctrl+left_bracket=text:\x1b +# keybind = ctrl+a=text:\x01 diff --git a/config/home/.mailcap b/config/home/.mailcap index 811bab7..916b999 100644 --- a/config/home/.mailcap +++ b/config/home/.mailcap @@ -1,28 +1,29 @@ # Mailcap - MIME type handlers for neomutt and other mail clients +# Using path-shim to ensure nix paths are available # HTML - terminal inline view (for auto_view) -text/html; w3m -dump -T text/html %s; copiousoutput +text/html; ~/dotfiles/bin/path-shim w3m -dump -T text/html %s; copiousoutput # HTML - open in browser text/html; open %s; nametemplate=%s.html # Plain text -text/plain; TERM=xterm-256color less %s -text/*; TERM=xterm-256color less %s +text/plain; ~/dotfiles/bin/path-shim TERM=xterm-256color less %s +text/*; ~/dotfiles/bin/path-shim TERM=xterm-256color less %s # PDF -application/pdf; zathura %s +application/pdf; ~/dotfiles/bin/path-shim zathura %s application/pdf; open %s -# Images - terminal (kitty protocol works in ghostty) -image/*; kitten icat --clear %s +# Images - terminal +image/*; ~/dotfiles/bin/path-shim chafa %s; needsterminal image/*; open %s # Video -video/*; iina %s +video/*; ~/dotfiles/bin/path-shim iina %s video/*; open %s # Audio -audio/*; mpv --no-video %s +audio/*; ~/dotfiles/bin/path-shim mpv --no-video %s audio/*; open %s # Microsoft Office - Word @@ -38,10 +39,10 @@ application/vnd.ms-powerpoint; open %s application/vnd.openxmlformats-officedocument.presentationml.presentation; open %s # Archives - show contents -application/zip; unzip -l %s; copiousoutput -application/x-tar; tar -tvf %s; copiousoutput -application/gzip; tar -tzvf %s; copiousoutput -application/x-bzip2; tar -tjvf %s; copiousoutput +application/zip; ~/dotfiles/bin/path-shim unzip -l %s; copiousoutput +application/x-tar; ~/dotfiles/bin/path-shim tar -tvf %s; copiousoutput +application/gzip; ~/dotfiles/bin/path-shim tar -tzvf %s; copiousoutput +application/x-bzip2; ~/dotfiles/bin/path-shim tar -tjvf %s; copiousoutput # Fallback - open with default macOS app application/*; open %s diff --git a/config/mbsync/mbsyncrc b/config/mbsync/mbsyncrc index ddd9908..fac3334 100644 --- a/config/mbsync/mbsyncrc +++ b/config/mbsync/mbsyncrc @@ -5,7 +5,7 @@ IMAPAccount personal Host imap.gmail.com User raydreww@gmail.com -PassCmd "sops -d --extract '[\"personal\"]' ~/dotfiles/home/email/secrets.yaml" +PassCmd "/etc/profiles/per-user/rayandrew/bin/sops -d --extract '[\"personal\"]' ~/dotfiles/home/email/secrets.yaml" TLSType IMAPS CertificateFile /etc/ssl/certs/ca-certificates.crt diff --git a/config/neomutt/keybinds b/config/neomutt/keybinds index 7d947f5..714968f 100644 --- a/config/neomutt/keybinds +++ b/config/neomutt/keybinds @@ -24,7 +24,7 @@ bind pager,browser G bottom-page # Index bind index G last-entry -bind index g noop +bind index,pager g noop bind index gg first-entry bind index D delete-message bind index U undelete-message @@ -58,8 +58,13 @@ bind index,pager @ compose-to-sender bind index,pager D purge-message # Macros -macro index,pager a ":set confirmappend=no delete=yes\n=Archive\n:set confirmappend=yes delete=ask-yes\n" -macro index,pager n "N.\n" -macro attach O "unset wait_keyrm -f /tmp/mutt-attach/tmp/mutt-attach^A" -macro attach,pager A "|git apply" "Apply git patch" +macro index,pager a ":set confirmappend=no delete=yes\n=Archive\n:set confirmappend=yes delete=ask-yes\n" "Archive" +macro index,pager A ":set confirmappend=no delete=yes\n=Inbox\n:set confirmappend=yes delete=ask-yes\n" "Move to Inbox" +macro index r "N." "Mark as read" +# bind index \Ca noop +macro index \Ca "." "Tag all messages" +macro index \Cu "." "Untag all messages" +macro attach o "unset wait_key~/dotfiles/bin/open-attachment" "Open with default app" +macro attach O "unset wait_key~/dotfiles/bin/mailcap-open" "Open with fzf picker" +macro attach,pager p "|git apply" "Apply git patch" macro attach,pager P "|git-apply-patch" "Apply git patch (interactive)" diff --git a/config/neomutt/neomuttrc b/config/neomutt/neomuttrc index 640b0a1..638a28f 100644 --- a/config/neomutt/neomuttrc +++ b/config/neomutt/neomuttrc @@ -51,9 +51,9 @@ set sendmail = 'msmtpq --read-envelope-from --read-recipients' source ~/.config/neomutt/keybinds source ~/.config/neomutt/colors -# Account switching macros -macro index,pager "source ~/.config/neomutt/accounts/uchicago!" -macro index,pager "source ~/.config/neomutt/accounts/personal!" +# Account switching macros (g + u/p for uchicago/personal) +macro index,pager gu "source ~/.config/neomutt/accounts/uchicago!" "Switch to uchicago" +macro index,pager gp "source ~/.config/neomutt/accounts/personal!" "Switch to personal" # Register accounts for folder hooks named-mailboxes "p" "~/mail/personal/Inbox" diff --git a/home/email/default.nix b/home/email/default.nix index 82fe4ac..8820324 100644 --- a/home/email/default.nix +++ b/home/email/default.nix @@ -3,6 +3,7 @@ lib, config, dots, + xdg-config-dir, ... }: @@ -27,6 +28,10 @@ enable = config.custom.email.mbsync; configFile = "${dots}/config/mbsync/mbsyncrc"; frequency = "*:0/1"; + extraPackages = with pkgs; [ sops ]; + environment = { + SOPS_AGE_KEY_FILE = "${xdg-config-dir}/sops/age/keys.txt"; + }; }; # Install mail-related packages @@ -41,7 +46,6 @@ ++ lib.optionals config.custom.email.neomutt [ # Wrapper script for neomutt with truecolor support (writeShellScriptBin "neomutt" '' - export TERMINFO_DIRS="${ncurses}/share/terminfo''${TERMINFO_DIRS:+:$TERMINFO_DIRS}" exec env TERM=xterm-direct ${neomutt}/bin/neomutt "$@" '') ] @@ -49,7 +53,7 @@ mailcap w3m # HTML rendering zathura # PDF viewer - kitty # for kitten icat + chafa # terminal image viewer ]; # Symlink config files diff --git a/hosts/default.nix b/hosts/default.nix index 8702433..0eb8c39 100644 --- a/hosts/default.nix +++ b/hosts/default.nix @@ -54,6 +54,7 @@ let system-font = "Consolas"; dots = "/home/${user}/dotfiles"; home-dir = config.home-manager.users.${user}.home.homeDirectory; + xdg-config-dir = config.home-manager.users.${user}.xdg.configHome; }; users.${user} = { @@ -129,6 +130,7 @@ let inherit host user system; system-font = "SF Mono"; home-dir = config.home-manager.users.${user}.home.homeDirectory; + xdg-config-dir = config.home-manager.users.${user}.xdg.configHome; dots = "/Users/${user}/dotfiles"; }; diff --git a/modules/home-manager/overrides/mbsync.nix b/modules/home-manager/overrides/mbsync.nix index 3c282ee..c10d796 100644 --- a/modules/home-manager/overrides/mbsync.nix +++ b/modules/home-manager/overrides/mbsync.nix @@ -96,6 +96,37 @@ in This is useful for running mailbox indexing tools. ''; }; + + extraPath = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "/etc/profiles/per-user/myuser/bin" ]; + description = '' + Extra paths to add to PATH environment variable for the mbsync service. + Useful for making commands like sops available to PassCmd. + ''; + }; + + extraPackages = mkOption { + type = types.listOf types.package; + default = [ ]; + example = lib.literalExpression "[ pkgs.sops ]"; + description = '' + Extra packages whose bin directories will be added to PATH. + Useful for making commands like sops available to PassCmd. + ''; + }; + + environment = mkOption { + type = types.attrsOf types.str; + default = { }; + example = { + SOPS_AGE_KEY_FILE = "/home/user/.config/sops/age/keys.txt"; + }; + description = '' + Extra environment variables for the mbsync service. + ''; + }; }; config = mkIf cfg.enable (mkMerge [ @@ -134,6 +165,11 @@ in }; }) + # Install extra packages + (mkIf (cfg.extraPackages != [ ]) { + home.packages = cfg.extraPackages; + }) + # Darwin-specific: launchd agent (mkIf isDarwin { launchd.agents.mbsync = { @@ -154,7 +190,20 @@ in RunAtLoad = true; StandardErrorPath = "/tmp/mbsync.err.log"; StandardOutPath = "/tmp/mbsync.out.log"; - }; + } + // (lib.optionalAttrs (cfg.extraPath != [ ] || cfg.extraPackages != [ ] || cfg.environment != { }) { + EnvironmentVariables = { + PATH = concatStringsSep ":" ( + cfg.extraPath + ++ (map (pkg: "${pkg}/bin") cfg.extraPackages) + ++ [ + "/usr/bin" + "/bin" + ] + ); + } + // cfg.environment; + }); }; }) ]);