diff --git a/home/home-darwin-work.nix b/home/home-darwin-work.nix index 5a4c404..be87239 100644 --- a/home/home-darwin-work.nix +++ b/home/home-darwin-work.nix @@ -1,8 +1,5 @@ { config, lib, pkgs, globalInputs, system, ... }: -let - leader = "cmd"; # Change this to experiment with different leader keys (e.g., "cmd", "ctrl") -in { # Claude Code Package Override for Corporate Work Environment # @@ -37,40 +34,11 @@ in # System packages home.packages = with pkgs; [ - autoraise google-cloud-sdk ]; # Note: ghostty installed via Homebrew (managed outside of nix) - # Auto-start autoraise on login - launchd.agents.autoraise = { - enable = true; - config = { - ProgramArguments = [ - "${pkgs.autoraise}/bin/AutoRaise" - "-pollMillis" "50" - "-delay" "2" - "-focusDelay" "2" - ]; - RunAtLoad = true; - KeepAlive = true; - }; - }; - - # Auto-start aerospace on login - # NOTE: In 25.11+, this can be simplified to `programs.aerospace.launchd.enable = true` - launchd.agents.aerospace = { - enable = true; - config = { - Program = "${pkgs.aerospace}/Applications/AeroSpace.app/Contents/MacOS/AeroSpace"; - RunAtLoad = true; - KeepAlive = true; - StandardOutPath = "/tmp/aerospace.log"; - StandardErrorPath = "/tmp/aerospace.err.log"; - }; - }; - # Override Darwin-incompatible settings from base role programs.rbw.settings.pinentry = lib.mkForce pkgs.pinentry_mac; @@ -152,97 +120,14 @@ in home.shell.enableShellIntegration = true; - # TODO: Move this to its own role and/or module + # Enable aerospace window manager with autoraise programs.aerospace = { enable = true; - userSettings.mode.main.binding = { - "${leader}-slash" = "layout tiles horizontal vertical"; - "${leader}-comma" = "layout accordion horizontal vertical"; - "${leader}-shift-q" = "close"; - "${leader}-shift-f" = "fullscreen"; - "${leader}-h" = "focus left"; - "${leader}-j" = "focus down"; - "${leader}-k" = "focus up"; - "${leader}-l" = "focus right"; - "${leader}-shift-h" = "move left"; - "${leader}-shift-j" = "move down"; - "${leader}-shift-k" = "move up"; - "${leader}-shift-l" = "move right"; - "${leader}-minus" = "resize smart -50"; - "${leader}-equal" = "resize smart +50"; - "${leader}-1" = "workspace 1"; - "${leader}-2" = "workspace 2"; - "${leader}-3" = "workspace 3"; - "${leader}-4" = "workspace 4"; - "${leader}-5" = "workspace 5"; - "${leader}-6" = "workspace 6"; - "${leader}-7" = "workspace 7"; - "${leader}-8" = "workspace 8"; - "${leader}-9" = "workspace 9"; - "${leader}-0" = "workspace 10"; - "${leader}-shift-1" = "move-node-to-workspace 1"; - "${leader}-shift-2" = "move-node-to-workspace 2"; - "${leader}-shift-3" = "move-node-to-workspace 3"; - "${leader}-shift-4" = "move-node-to-workspace 4"; - "${leader}-shift-5" = "move-node-to-workspace 5"; - "${leader}-shift-6" = "move-node-to-workspace 6"; - "${leader}-shift-7" = "move-node-to-workspace 7"; - "${leader}-shift-8" = "move-node-to-workspace 8"; - "${leader}-shift-9" = "move-node-to-workspace 9"; - "${leader}-shift-0" = "move-node-to-workspace 10"; - "${leader}-tab" = "workspace-back-and-forth"; - "${leader}-shift-tab" = "move-workspace-to-monitor --wrap-around next"; - - "${leader}-enter" = '' - exec-and-forget osascript <<'APPLESCRIPT' - tell application "Ghostty" - activate - tell application "System Events" - keystroke "n" using {command down} - end tell - end tell - APPLESCRIPT - ''; - - "${leader}-shift-enter" = '' - exec-and-forget osascript <<'APPLESCRIPT' - tell application "Google Chrome" - set newWindow to make new window - activate - tell newWindow to set index to 1 - end tell - APPLESCRIPT - ''; - - "${leader}-shift-e" = "exec-and-forget zsh --login -c \"emacsclient -c -n\""; - - # Service mode: Deliberate aerospace window management - "${leader}-i" = "mode service"; - - # Passthrough mode: Temporarily disable aerospace to use macOS shortcuts - # Press Cmd-P, then use any macOS shortcut (like Cmd-K in Slack), then press Cmd-P again to exit - "${leader}-p" = "mode passthrough"; - }; - - # Service mode: For deliberate aerospace window management operations - userSettings.mode.service.binding = { - esc = ["reload-config" "mode main"]; - r = ["flatten-workspace-tree" "mode main"]; # reset layout - f = ["layout floating tiling" "mode main"]; # Toggle between floating and tiling layout - backspace = ["close-all-windows-but-current" "mode main"]; - - "${leader}-shift-h" = ["join-with left" "mode main"]; - "${leader}-shift-j" = ["join-with down" "mode main"]; - "${leader}-shift-k" = ["join-with up" "mode main"]; - "${leader}-shift-l" = ["join-with right" "mode main"]; - }; - - # Passthrough mode: All shortcuts pass through to macOS - # This mode has minimal bindings - just ways to exit back to main mode - userSettings.mode.passthrough.binding = { - esc = "mode main"; - "${leader}-p" = "mode main"; # Toggle back with same key (Cmd-P) - }; + leader = "cmd"; + # Optional: Add per-machine userSettings overrides + # userSettings = { + # mode.main.binding."${leader}-custom" = "custom-command"; + # }; }; home.roles = { @@ -258,5 +143,6 @@ in ./modules/emacs ./modules/kubectl ./modules/tmux + ./modules/aerospace ]; } diff --git a/home/modules/aerospace/default.nix b/home/modules/aerospace/default.nix new file mode 100644 index 0000000..c621948 --- /dev/null +++ b/home/modules/aerospace/default.nix @@ -0,0 +1,200 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.aerospace; +in +{ + options.programs.aerospace = { + enable = mkEnableOption "AeroSpace tiling window manager for macOS"; + + leader = mkOption { + type = types.str; + default = "cmd"; + description = "Leader key for aerospace shortcuts (e.g., 'cmd', 'ctrl', 'alt')"; + example = "ctrl"; + }; + + launchd.enable = mkOption { + type = types.bool; + default = true; + description = "Whether to enable launchd agent for auto-starting aerospace"; + }; + + userSettings = mkOption { + type = types.attrs; + default = {}; + description = '' + Additional aerospace configuration settings to merge with defaults. + Use this to override or extend the default configuration on a per-machine basis. + ''; + example = literalExpression '' + { + mode.main.binding."''${leader}-custom" = "custom-command"; + } + ''; + }; + + autoraise = { + enable = mkOption { + type = types.bool; + default = true; + description = "Whether to enable autoraise (auto-focus window on hover)"; + }; + + pollMillis = mkOption { + type = types.int; + default = 50; + description = "Polling interval in milliseconds"; + }; + + delay = mkOption { + type = types.int; + default = 2; + description = "Delay before raising window"; + }; + + focusDelay = mkOption { + type = types.int; + default = 2; + description = "Delay before focusing window"; + }; + }; + }; + + config = mkIf cfg.enable { + # Only apply on Darwin systems + assertions = [ + { + assertion = pkgs.stdenv.isDarwin; + message = "Aerospace module is only supported on macOS (Darwin) systems"; + } + ]; + + # Install aerospace package and autoraise if enabled + home.packages = [ pkgs.aerospace ] + ++ optionals cfg.autoraise.enable [ pkgs.autoraise ]; + + # Configure aerospace with user settings + programs.aerospace.userSettings = mkMerge [ + # Default configuration with leader key substitution + { + mode.main.binding = { + "${cfg.leader}-slash" = "layout tiles horizontal vertical"; + "${cfg.leader}-comma" = "layout accordion horizontal vertical"; + "${cfg.leader}-shift-q" = "close"; + "${cfg.leader}-shift-f" = "fullscreen"; + "${cfg.leader}-h" = "focus left"; + "${cfg.leader}-j" = "focus down"; + "${cfg.leader}-k" = "focus up"; + "${cfg.leader}-l" = "focus right"; + "${cfg.leader}-shift-h" = "move left"; + "${cfg.leader}-shift-j" = "move down"; + "${cfg.leader}-shift-k" = "move up"; + "${cfg.leader}-shift-l" = "move right"; + "${cfg.leader}-minus" = "resize smart -50"; + "${cfg.leader}-equal" = "resize smart +50"; + "${cfg.leader}-1" = "workspace 1"; + "${cfg.leader}-2" = "workspace 2"; + "${cfg.leader}-3" = "workspace 3"; + "${cfg.leader}-4" = "workspace 4"; + "${cfg.leader}-5" = "workspace 5"; + "${cfg.leader}-6" = "workspace 6"; + "${cfg.leader}-7" = "workspace 7"; + "${cfg.leader}-8" = "workspace 8"; + "${cfg.leader}-9" = "workspace 9"; + "${cfg.leader}-0" = "workspace 10"; + "${cfg.leader}-shift-1" = "move-node-to-workspace 1"; + "${cfg.leader}-shift-2" = "move-node-to-workspace 2"; + "${cfg.leader}-shift-3" = "move-node-to-workspace 3"; + "${cfg.leader}-shift-4" = "move-node-to-workspace 4"; + "${cfg.leader}-shift-5" = "move-node-to-workspace 5"; + "${cfg.leader}-shift-6" = "move-node-to-workspace 6"; + "${cfg.leader}-shift-7" = "move-node-to-workspace 7"; + "${cfg.leader}-shift-8" = "move-node-to-workspace 8"; + "${cfg.leader}-shift-9" = "move-node-to-workspace 9"; + "${cfg.leader}-shift-0" = "move-node-to-workspace 10"; + "${cfg.leader}-tab" = "workspace-back-and-forth"; + "${cfg.leader}-shift-tab" = "move-workspace-to-monitor --wrap-around next"; + + "${cfg.leader}-enter" = '' + exec-and-forget osascript <<'APPLESCRIPT' + tell application "Ghostty" + activate + tell application "System Events" + keystroke "n" using {command down} + end tell + end tell + APPLESCRIPT + ''; + + "${cfg.leader}-shift-enter" = '' + exec-and-forget osascript <<'APPLESCRIPT' + tell application "Google Chrome" + set newWindow to make new window + activate + tell newWindow to set index to 1 + end tell + APPLESCRIPT + ''; + + "${cfg.leader}-shift-e" = "exec-and-forget zsh --login -c \"emacsclient -c -n\""; + + # Service mode: Deliberate aerospace window management + "${cfg.leader}-i" = "mode service"; + + # Passthrough mode: Temporarily disable aerospace to use macOS shortcuts + "${cfg.leader}-p" = "mode passthrough"; + }; + + # Service mode: For deliberate aerospace window management operations + mode.service.binding = { + esc = ["reload-config" "mode main"]; + r = ["flatten-workspace-tree" "mode main"]; # reset layout + f = ["layout floating tiling" "mode main"]; # Toggle between floating and tiling layout + backspace = ["close-all-windows-but-current" "mode main"]; + + "${cfg.leader}-shift-h" = ["join-with left" "mode main"]; + "${cfg.leader}-shift-j" = ["join-with down" "mode main"]; + "${cfg.leader}-shift-k" = ["join-with up" "mode main"]; + "${cfg.leader}-shift-l" = ["join-with right" "mode main"]; + }; + + # Passthrough mode: All shortcuts pass through to macOS + mode.passthrough.binding = { + esc = "mode main"; + "${cfg.leader}-p" = "mode main"; + }; + } + cfg.userSettings + ]; + + # Launchd agent for auto-starting aerospace + launchd.agents.aerospace = mkIf cfg.launchd.enable { + enable = true; + config = { + Program = "${pkgs.aerospace}/Applications/AeroSpace.app/Contents/MacOS/AeroSpace"; + RunAtLoad = true; + KeepAlive = true; + StandardOutPath = "/tmp/aerospace.log"; + StandardErrorPath = "/tmp/aerospace.err.log"; + }; + }; + + # Launchd agent for autoraise + launchd.agents.autoraise = mkIf cfg.autoraise.enable { + enable = true; + config = { + ProgramArguments = [ + "${pkgs.autoraise}/bin/AutoRaise" + "-pollMillis" (toString cfg.autoraise.pollMillis) + "-delay" (toString cfg.autoraise.delay) + "-focusDelay" (toString cfg.autoraise.focusDelay) + ]; + RunAtLoad = true; + KeepAlive = true; + }; + }; + }; +} diff --git a/machines/johno-macbookpro/configuration.nix b/machines/johno-macbookpro/configuration.nix index be7d1e4..d396bfb 100644 --- a/machines/johno-macbookpro/configuration.nix +++ b/machines/johno-macbookpro/configuration.nix @@ -1,6 +1,10 @@ { config, lib, pkgs, ... }: { + imports = [ + ../../modules/aerospace.nix + ]; + # Basic system configuration for macOS work laptop system.stateVersion = 6; @@ -12,13 +16,12 @@ dock.autohide = true; finder.AppleShowAllExtensions = true; NSGlobalDomain.AppleShowAllExtensions = true; + }; - # Configure spaces to span displays (required for aerospace multi-monitor support) - CustomUserPreferences = { - "com.apple.spaces" = { - spans-displays = true; - }; - }; + # Enable aerospace system settings + services.aerospace = { + enable = true; + enableSpansDisplays = true; # Default, but shown for clarity }; # TODO: Find a way to not duplicate this diff --git a/modules/aerospace.nix b/modules/aerospace.nix new file mode 100644 index 0000000..b0d5d2c --- /dev/null +++ b/modules/aerospace.nix @@ -0,0 +1,30 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.aerospace; +in +{ + options.services.aerospace = { + enable = mkEnableOption "AeroSpace window manager system configuration"; + + enableSpansDisplays = mkOption { + type = types.bool; + default = true; + description = '' + Configure macOS Spaces to span displays (required for aerospace multi-monitor support). + When enabled, sets com.apple.spaces.spans-displays to true. + ''; + }; + }; + + config = mkIf cfg.enable { + # Configure spaces to span displays (required for aerospace multi-monitor support) + system.defaults.CustomUserPreferences = mkIf cfg.enableSpansDisplays { + "com.apple.spaces" = { + spans-displays = true; + }; + }; + }; +}