Add macOS Ctrl keyboard shortcuts with terminal-aware overrides

Implement Linux-style Ctrl shortcuts (Ctrl+C/V/X/Z for clipboard, Ctrl+N/T/W
for navigation, etc.) while preserving terminal behavior where Ctrl+C sends
SIGINT. Uses per-app NSUserKeyEquivalents to remap Ghostty back to Cmd for
clipboard operations.

Also consolidate aerospace configuration by moving spans-displays preference
from system-level module to home-manager role, allowing full aerospace
configuration to live in home-manager for better modularity.
This commit is contained in:
2025-12-03 10:30:39 -08:00
parent 48fb7cdada
commit ade60ba5ec
4 changed files with 100 additions and 49 deletions

View File

@@ -107,6 +107,7 @@
aerospace = { aerospace = {
enable = true; enable = true;
leader = "cmd"; leader = "cmd";
ctrlShortcuts.enable = true;
sketchybar.enable = true; sketchybar.enable = true;
# Optional: Add per-machine userSettings overrides # Optional: Add per-machine userSettings overrides
# userSettings = { # userSettings = {

View File

@@ -62,6 +62,44 @@ in
}; };
}; };
enableSpansDisplays = mkOption {
type = types.bool;
default = true;
description = ''
Configure macOS Spaces to span displays (required for aerospace multi-monitor support).
Sets com.apple.spaces.spans-displays to true.
NOTE: This was previously set at the system level in modules/aerospace.nix,
but has been moved to home-manager for better modularity.
'';
};
ctrlShortcuts = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Remap common macOS Cmd shortcuts to Ctrl equivalents for all operations.
This makes macOS behave more like Linux.
Shortcuts remapped globally:
- Ctrl+N: New Window
- Ctrl+T: New Tab
- Ctrl+W: Close Tab
- Ctrl+S: Save / Save As
- Ctrl+O: Open
- Ctrl+F: Find
- Ctrl+H: Find and Replace
- Ctrl+P: Print
- Ctrl+C/V/X: Copy/Paste/Cut
- Ctrl+Z: Undo
NOTE: Terminal emulators like Ghostty require per-app overrides (configured separately)
to preserve Ctrl+C as SIGINT instead of Copy.
'';
};
};
sketchybar = { sketchybar = {
enable = mkOption { enable = mkOption {
type = types.bool; type = types.bool;
@@ -80,14 +118,67 @@ in
} }
]; ];
# Configure macOS preferences via targets.darwin.defaults
targets.darwin.defaults = mkMerge [
# Spaces span displays (required for multi-monitor aerospace)
(mkIf cfg.enableSpansDisplays {
"com.apple.spaces" = {
spans-displays = true;
};
})
# Ctrl shortcuts to make macOS behave more like Linux
(mkIf cfg.ctrlShortcuts.enable {
NSGlobalDomain.NSUserKeyEquivalents = {
# Window/Tab operations
"New Window" = "^n";
"New Tab" = "^t";
"Close Tab" = "^w";
# File operations
"Save" = "^s";
"Save As" = "^$s"; # Ctrl+Shift+S
"Open" = "^o";
"Open" = "^o";
# Find operations
"Find" = "^f";
"Find" = "^f";
"Find and Replace" = "^h";
"Find and Replace" = "^h";
# Print
"Print" = "^p";
"Print" = "^p";
# Clipboard operations
"Copy" = "^c";
"Paste" = "^v";
"Cut" = "^x";
# Undo/Redo
"Undo" = "^z";
"Redo" = "^$z"; # Ctrl+Shift+Z
};
})
# Ghostty-specific overrides to preserve terminal behavior
# Remap clipboard operations back to Cmd (macOS default) so Ctrl+C remains SIGINT
(mkIf cfg.ctrlShortcuts.enable {
"com.mitchellh.ghostty".NSUserKeyEquivalents = {
# Remap back to Cmd for clipboard operations
"Copy" = "@c"; # Cmd+C
"Paste" = "@v"; # Cmd+V
"Cut" = "@x"; # Cmd+X
"Undo" = "@z"; # Cmd+Z
"Redo" = "@$z"; # Cmd+Shift+Z
};
})
# Hide macOS menu bar when SketchyBar is enabled # Hide macOS menu bar when SketchyBar is enabled
# Note: Requires logout/login to take effect # Note: Requires logout/login to take effect
targets.darwin.defaults = mkIf cfg.sketchybar.enable { (mkIf cfg.sketchybar.enable {
NSGlobalDomain = { NSGlobalDomain = {
_HIHideMenuBar = true; _HIHideMenuBar = true;
AppleMenuBarVisibleInFullscreen = false; AppleMenuBarVisibleInFullscreen = false;
}; };
}; })
];
# Install aerospace package and optional tools if enabled # Install aerospace package and optional tools if enabled
home.packages = [ pkgs.aerospace ] home.packages = [ pkgs.aerospace ]

View File

@@ -1,10 +1,6 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
{ {
imports = [
../../modules/aerospace.nix
];
# Basic system configuration for macOS work laptop # Basic system configuration for macOS work laptop
system.stateVersion = 6; system.stateVersion = 6;
@@ -18,13 +14,6 @@
NSGlobalDomain.AppleShowAllExtensions = true; NSGlobalDomain.AppleShowAllExtensions = true;
}; };
# Enable our custom aerospace system configuration
# (separate from nix-darwin's services.aerospace which manages the service)
roles.aerospace = {
enable = true;
enableSpansDisplays = true;
};
# TODO: Find a way to not duplicate this # TODO: Find a way to not duplicate this
launchd.user.envVariables = { launchd.user.envVariables = {
# DOOM Emacs environment variables # DOOM Emacs environment variables

View File

@@ -1,30 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.roles.aerospace;
in
{
options.roles.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;
};
};
};
}