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 = {
enable = true;
leader = "cmd";
ctrlShortcuts.enable = true;
sketchybar.enable = true;
# Optional: Add per-machine userSettings overrides
# 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 = {
enable = mkOption {
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
# Note: Requires logout/login to take effect
targets.darwin.defaults = mkIf cfg.sketchybar.enable {
(mkIf cfg.sketchybar.enable {
NSGlobalDomain = {
_HIHideMenuBar = true;
AppleMenuBarVisibleInFullscreen = false;
};
};
})
];
# Install aerospace package and optional tools if enabled
home.packages = [ pkgs.aerospace ]

View File

@@ -1,10 +1,6 @@
{ config, lib, pkgs, ... }:
{
imports = [
../../modules/aerospace.nix
];
# Basic system configuration for macOS work laptop
system.stateVersion = 6;
@@ -18,13 +14,6 @@
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
launchd.user.envVariables = {
# 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;
};
};
};
}