switch from nix-darwin control to more home-manager darwin settings

This commit is contained in:
Noah Masur
2026-07-05 14:34:43 -06:00
parent e0fb590314
commit 35d40ca346
14 changed files with 505 additions and 106 deletions
@@ -202,6 +202,9 @@ in
}; };
}; };
# Used for macOS
xdg.enable = lib.mkDefault pkgs.stdenv.isDarwin;
xdg.desktopEntries.aerc = lib.mkIf (pkgs.stdenv.isLinux) { xdg.desktopEntries.aerc = lib.mkIf (pkgs.stdenv.isLinux) {
name = "aerc"; name = "aerc";
exec = "${lib.getExe config.nmasur.presets.services.i3.terminal} -e aerc %u"; exec = "${lib.getExe config.nmasur.presets.services.i3.terminal} -e aerc %u";
@@ -13,13 +13,35 @@ in
options.nmasur.presets.programs.fish-darwin.enable = lib.mkEnableOption { options.nmasur.presets.programs.fish-darwin.enable = lib.mkEnableOption {
description = "Fish macOS options"; description = "Fish macOS options";
default = config.nmasur.presets.programs.fish && pkgs.stdenv.isDarwin;
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
programs.fish.shellAbbrs = { # Default shell setting doesn't work
home.sessionVariables = {
SHELL = "${pkgs.fish}/bin/fish";
};
programs.fish = {
shellAbbrs = {
# Shortcut to edit hosts file # Shortcut to edit hosts file
hosts = "sudo nvim /etc/hosts"; hosts = "sudo hx /etc/hosts";
};
shellInit = ''
set -g __nixos_path_original $PATH
function __nixos_path_fix -d "fix PATH value"
set -l result (string split ":" $__nixos_path_original)
for elt in $PATH
if not contains -- $elt $result
set -a result $elt
end
end
set -g PATH $result
end
__nixos_path_fix
'';
# # Speeds up fish launch time on macOS
# useBabelfish = true;
}; };
}; };
} }
@@ -16,13 +16,12 @@ in
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
# Set the i3 terminal # Set the i3 terminal
nmasur.presets.services.i3.terminal = nmasur.presets.services.i3.terminal = config.programs.ghostty.package;
if pkgs.stdenv.isDarwin then pkgs.alacritty else config.programs.ghostty.package;
programs.ghostty = { programs.ghostty = {
enable = true; enable = true;
package = if pkgs.stdenv.isDarwin then null else pkgs.ghostty; package = if pkgs.stdenv.isDarwin then pkgs.ghostty-bin else pkgs.ghostty;
enableFishIntegration = true; enableFishIntegration = true;
enableBashIntegration = true; enableBashIntegration = true;
@@ -0,0 +1,82 @@
{
config,
lib,
...
}:
let
cfg = config.nmasur.presets.programs.homebrew;
in
{
options.nmasur.presets.programs.homebrew.enable =
lib.mkEnableOption "Homebrew macOS package manager";
config = lib.mkIf cfg.enable {
home.file.".Brewfile".text = /* homebrew */ ''
# Taps
# tap "homebrew/bundle"
# Brews (CLI Apps)
brew "trash" # Delete files and folders to trash instead of rm
# Casks (GUI Apps)
cask "scroll-reverser" # Different scroll style for mouse vs. trackpad
cask "notunes" # Don't launch Apple Music with the play button
cask "topnotch" # Darkens the menu bar to complete black
cask "ghostty" # Terminal application (not buildable on Nix on macOS)
# Mac App Store apps (requires 'mas' CLI, optional)
# mas "Tailscale", id: 1475387142
'';
# Add homebrew paths to CLI path
home.sessionPath = [
"/opt/homebrew/bin/"
"/opt/homebrew/opt/trash/bin"
];
programs.fish.shellAbbrs.t = "trash";
home.activation.backendBrewBundle = /* bash */ ''
# Requires Homebrew to be installed
if ! /usr/bin/xcode-select --version 2>/dev/null; then
$DRY_RUN_CMD /usr/bin/xcode-select --install
fi
if ! /opt/homebrew/bin/brew --version 2>/dev/null; then
$DRY_RUN_CMD /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
fi
verboseEcho "Syncing Homebrew dependencies via Brewfile..."
# Ensure Homebrew is in the PATH for the script (Apple Silicon path)
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
# Keep activation deterministic and non-interactive. Without this, brew
# auto-updates (git-fetches its taps) which can hang forever when there
# is no TTY (network stall, credential prompt, or a leftover brew lock).
export HOMEBREW_NO_AUTO_UPDATE=1
export HOMEBREW_NO_ENV_HINTS=1
# Point brew at the generated Brewfile in the Nix store directly. This
# avoids depending on the ~/.Brewfile symlink being linked first, and
# sidesteps the --global vs. inherited HOMEBREW_BUNDLE_FILE conflict.
unset HOMEBREW_BUNDLE_FILE
brewfile="${config.home.file.".Brewfile".source}"
if command -v brew &> /dev/null; then
# Install/upgrade everything declared in the Brewfile...
$DRY_RUN_CMD brew bundle install --file="$brewfile"
# ...then uninstall anything NOT listed (replaces deprecated --cleanup).
$DRY_RUN_CMD brew bundle cleanup --file="$brewfile" --force
else
verboseEcho "Warning: Homebrew not found. Actions skipped."
fi
'';
};
}
@@ -0,0 +1,93 @@
{
config,
lib,
...
}:
let
cfg = config.nmasur.presets.services.darwin-settings;
in
{
options.nmasur.presets.services.darwin-settings.enable = lib.mkEnableOption "macOS settings";
config = lib.mkIf cfg.enable {
targets.darwin.defaults = {
NSGlobalDomain = {
# Set to dark mode
AppleInterfaceStyle = "Dark";
# Don't change from dark to light automatically
# AppleInterfaceSwitchesAutomatically = false;
# Enable full keyboard access for all controls (e.g. enable Tab in modal dialogs)
AppleKeyboardUIMode = 3;
# Expand save panel by default
NSNavPanelExpandedStateForSaveMode = true;
# Expand print panel by default
PMPrintingExpandedStateForPrint = true;
# Replace press-and-hold with key repeat
ApplePressAndHoldEnabled = false;
# Set a fast key repeat rate
KeyRepeat = 2;
# Shorten delay before key repeat begins
InitialKeyRepeat = 12;
# Disable autocorrect capitalization
NSAutomaticCapitalizationEnabled = false;
# Disable autocorrect smart dashes
NSAutomaticDashSubstitutionEnabled = false;
# Disable autocorrect adding periods
NSAutomaticPeriodSubstitutionEnabled = false;
# Disable autocorrect smart quotation marks
NSAutomaticQuoteSubstitutionEnabled = false;
# Disable autocorrect spellcheck
NSAutomaticSpellingCorrectionEnabled = false;
};
"com.apple.LaunchServices" = {
# Disable "Are you sure you want to open" dialog
LSQuarantine = false;
};
# Where to save screenshots
"com.apple.screencapture" = {
location = "~/Downloads";
};
# Disable disk image verification
"com.apple.frameworks.diskimages" = {
skip-verify = true;
skip-verify-locked = true;
skip-verify-remote = true;
};
# Require password immediately after screen saver begins
"com.apple.screensaver" = {
askForPassword = 1;
askForPasswordDelay = 0;
};
};
targets.darwin.currentHostDefaults = {
"com.apple.mouse.tapBehavior" = {
# Disable trackpad tap to click
Clicking = false;
};
};
};
}
@@ -0,0 +1,75 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.nmasur.presets.services.dock;
# macOS stores persistent-apps as an array of dicts, not strings.
# nix-darwin's dock module did this transform for us; targets.darwin.defaults
# writes values verbatim, so we build the tile dicts ourselves.
mkDockApp = path: {
tile-data.file-data = {
_CFURLString = path;
_CFURLStringType = 0;
};
};
in
{
options.nmasur.presets.services.dock.enable = lib.mkEnableOption "macOS Dock";
config = lib.mkIf cfg.enable {
targets.darwin.defaults = {
"com.apple.dock" = {
magnification = true;
largesize = 48;
# Automatically show and hide the dock
autohide = true;
# Add translucency in dock for hidden applications
showhidden = true;
# Enable spring loading on all dock items
enable-spring-load-actions-on-all-items = true;
# Highlight hover effect in dock stack grid view
mouse-over-hilite-stack = true;
mineffect = "genie";
orientation = "bottom";
show-recents = false;
tilesize = 44;
persistent-apps = map mkDockApp [
"/Applications/1Password.app"
"${pkgs.slack}/Applications/Slack.app"
"/System/Applications/Calendar.app"
"${pkgs.firefox-unwrapped}/Applications/Firefox.app"
"/System/Applications/Messages.app"
"/System/Applications/Mail.app"
"/Applications/zoom.us.app"
"${config.programs.ghostty.package}/Applications/Ghostty.app"
"${pkgs.discord}/Applications/Discord.app"
"${pkgs.obsidian}/Applications/Obsidian.app"
"${pkgs.thunderbird}/Applications/Thunderbird.app"
];
};
};
# Automatically reload the Dock and Finder when Home Manager switches
home.activation = {
reloadMacSettings = config.lib.dag.entryAfter [ "writeBoundary" ] ''
verboseEcho "Restarting macOS desktop services..."
run /usr/bin/killall Dock Finder || true
'';
};
};
}
@@ -0,0 +1,50 @@
{
config,
lib,
...
}:
let
cfg = config.nmasur.presets.services.finder;
in
{
options.nmasur.presets.services.finder.enable = lib.mkEnableOption "macOS Finder options";
config = lib.mkIf cfg.enable {
targets.darwin.defaults = {
"com.apple.finder" = {
# Default Finder window set to column view
FXPreferredViewStyle = "clmv";
# Finder search in current folder by default
FXDefaultSearchScope = "SCcf";
# Disable warning when changing file extension
FXEnableExtensionChangeWarning = false;
# Allow quitting of Finder application
QuitMenuItem = true;
# Disable the warning before emptying the Trash
WarnOnEmptyTrash = false;
};
NSGlobalDomain = {
# Save to local disk by default, not iCloud
NSDocumentSaveNewDocumentsToCloud = false;
};
# Avoid creating .DS_Store files on network or USB volumes
"com.apple.desktopservices" = {
DSDontWriteNetworkStores = true;
DSDontWriteUSBStores = true;
};
};
};
}
@@ -7,6 +7,7 @@
let let
cfg = config.nmasur.presets.services.hammerspoon; cfg = config.nmasur.presets.services.hammerspoon;
# inherit (config.nmasur.settings) username;
in in
{ {
@@ -15,6 +16,17 @@ in
lib.mkEnableOption "Hammerspoon macOS automation"; lib.mkEnableOption "Hammerspoon macOS automation";
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
home.file.".Brewfile".text = /* homebrew */ ''
cask "hammerspoon" # Different scroll style for mouse vs. trackpad
'';
targets.darwin.defaults = {
"org.hammerspoon.Hammerspoon" = {
MJConfigFile = "${config.xdg.configHome}/hammerspoon/init.lua";
};
};
xdg.configFile."hammerspoon/init.lua".source = ./init.lua; xdg.configFile."hammerspoon/init.lua".source = ./init.lua;
xdg.configFile."hammerspoon/Spoons/ControlEscape.spoon".source = ./Spoons/ControlEscape.spoon; xdg.configFile."hammerspoon/Spoons/ControlEscape.spoon".source = ./Spoons/ControlEscape.spoon;
xdg.configFile."hammerspoon/Spoons/DismissAlerts.spoon".source = ./Spoons/DismissAlerts.spoon; xdg.configFile."hammerspoon/Spoons/DismissAlerts.spoon".source = ./Spoons/DismissAlerts.spoon;
@@ -32,9 +44,18 @@ in
xdg.configFile."hammerspoon/Spoons/HideZoomWindow.spoon".source = ./Spoons/HideZoomWindow.spoon; xdg.configFile."hammerspoon/Spoons/HideZoomWindow.spoon".source = ./Spoons/HideZoomWindow.spoon;
home.activation.reloadHammerspoon = config.lib.dag.entryAfter [ "writeBoundary" ] '' home.activation.reloadHammerspoon = config.lib.dag.entryAfter [ "writeBoundary" ] ''
$DRY_RUN_CMD /Applications/Hammerspoon.app/Contents/Frameworks/hs/hs -c "hs.reload()" || true # The `hs` CLI blocks forever on Hammerspoon's IPC message port when the
# app isn't running (|| true can't help it never returns). Only reload
# when the app is actually up, and wrap each call in `timeout` as a hard
# backstop so activation can never hang even if IPC is unresponsive.
hs=/Applications/Hammerspoon.app/Contents/Frameworks/hs/hs
if /usr/bin/pgrep -x Hammerspoon > /dev/null 2>&1; then
$DRY_RUN_CMD ${pkgs.coreutils}/bin/timeout 10 "$hs" -c "hs.reload()" || true
$DRY_RUN_CMD sleep 1 $DRY_RUN_CMD sleep 1
$DRY_RUN_CMD /Applications/Hammerspoon.app/Contents/Frameworks/hs/hs -c "hs.console.clearConsole()" || true $DRY_RUN_CMD ${pkgs.coreutils}/bin/timeout 10 "$hs" -c "hs.console.clearConsole()" || true
else
verboseEcho "Hammerspoon not running; skipping reload."
fi
''; '';
}; };
@@ -0,0 +1,53 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.nmasur.presets.services.menubar;
in
{
options.nmasur.presets.services.menubar.enable = lib.mkEnableOption "macOS menubar settings";
config = lib.mkIf cfg.enable {
home.packages = [
pkgs.ice-bar # Menu bar hiding
];
targets.darwin.defaults = {
NSGlobalDomain = {
# Only hide menu bar in fullscreen
_HIHideMenuBar = false;
NSStatusItemSelectionPadding = 6;
NSStatusItemSpacing = 6;
};
"com.apple.menuextra.clock" = {
# Show seconds on the clock
ShowSeconds = true;
FlashDateSeparator = false;
};
"leits.MeetingBar" = {
eventTimeFormat = ''"show"'';
eventTitleFormat = ''"none"'';
eventTitleIconFormat = ''"iconCalendar"'';
slackBrowser = ''{"deletable":true,"arguments":"","name":"Slack","path":""}'';
zoomBrowser = ''{"deletable":true,"arguments":"","name":"Zoom","path":""}'';
teamsBrowser = ''{"deletable":true,"arguments":"","name":"Teams","path":""}'';
KeyboardShortcuts_joinEventShortcut = ''{"carbonModifiers":6400,"carbonKeyCode":38}'';
timeFormat = ''"12-hour"'';
};
};
};
}
@@ -17,32 +17,25 @@ in
nmasur.presets = { nmasur.presets = {
fonts.enable = lib.mkDefault true; fonts.enable = lib.mkDefault true;
services.hammerspoon.enable = lib.mkDefault true; services = {
programs.nixpkgs-darwin.enable = lib.mkDefault true; darwin-settings.enable = lib.mkDefault true;
programs.mpv.enable = lib.mkDefault true; dock.enable = lib.mkDefault true;
finder.enable = lib.mkDefault true;
hammerspoon.enable = lib.mkDefault true;
menubar.enable = lib.mkDefault true;
};
programs = {
fish-darwin.enable = lib.mkDefault true;
homebrew.enable = lib.mkDefault true;
nixpkgs-darwin.enable = lib.mkDefault true;
mpv.enable = lib.mkDefault true;
};
}; };
home.homeDirectory = lib.mkForce "/Users/${config.home.username}"; home.homeDirectory = lib.mkForce "/Users/${config.home.username}";
# Default shell setting doesn't work
home.sessionVariables = {
SHELL = "${pkgs.fish}/bin/fish";
};
# Used for aerc
xdg.enable = lib.mkDefault pkgs.stdenv.isDarwin;
programs.fish.shellAbbrs.t = "trash";
# Add homebrew paths to CLI path
home.sessionPath = [
"/opt/homebrew/bin/"
"/opt/homebrew/opt/trash/bin"
];
home.packages = [ home.packages = [
pkgs.noti # Create notifications programmatically pkgs.noti # Create notifications programmatically
pkgs.ice-bar # Menu bar hiding
]; ];
}; };
@@ -43,6 +43,14 @@ in
pkgs.uv # Python packaging pkgs.uv # Python packaging
]; ];
home.file.".Brewfile".text = /* homebrew */ ''
cask "1password" # 1Password will not launch from Nix on macOS
# cask "gitify" # Git notifications in menu bar (downgrade manually from 4.6.1)
# cask "logitech-g-hub" # Mouse and keyboard management
cask "logitune" # Logitech webcam firmware
cask "meetingbar" # Show meetings in menu bar
'';
programs.fish.shellAliases.ec2 = "aws-ec2"; programs.fish.shellAliases.ec2 = "aws-ec2";
nmasur.presets = { nmasur.presets = {
@@ -25,76 +25,76 @@ in
enableKeyMapping = true; # Allows for skhd enableKeyMapping = true; # Allows for skhd
}; };
defaults = { # defaults = {
NSGlobalDomain = { # NSGlobalDomain = {
# Set to dark mode # # Set to dark mode
AppleInterfaceStyle = "Dark"; # AppleInterfaceStyle = "Dark";
# Don't change from dark to light automatically # # Don't change from dark to light automatically
# AppleInterfaceSwitchesAutomatically = false; # # AppleInterfaceSwitchesAutomatically = false;
# Enable full keyboard access for all controls (e.g. enable Tab in modal dialogs) # # Enable full keyboard access for all controls (e.g. enable Tab in modal dialogs)
AppleKeyboardUIMode = 3; # AppleKeyboardUIMode = 3;
# Expand save panel by default # # Expand save panel by default
NSNavPanelExpandedStateForSaveMode = true; # NSNavPanelExpandedStateForSaveMode = true;
# Expand print panel by default # # Expand print panel by default
PMPrintingExpandedStateForPrint = true; # PMPrintingExpandedStateForPrint = true;
# Replace press-and-hold with key repeat # # Replace press-and-hold with key repeat
ApplePressAndHoldEnabled = false; # ApplePressAndHoldEnabled = false;
# Set a fast key repeat rate # # Set a fast key repeat rate
KeyRepeat = 2; # KeyRepeat = 2;
# Shorten delay before key repeat begins # # Shorten delay before key repeat begins
InitialKeyRepeat = 12; # InitialKeyRepeat = 12;
# Disable autocorrect capitalization # # Disable autocorrect capitalization
NSAutomaticCapitalizationEnabled = false; # NSAutomaticCapitalizationEnabled = false;
# Disable autocorrect smart dashes # # Disable autocorrect smart dashes
NSAutomaticDashSubstitutionEnabled = false; # NSAutomaticDashSubstitutionEnabled = false;
# Disable autocorrect adding periods # # Disable autocorrect adding periods
NSAutomaticPeriodSubstitutionEnabled = false; # NSAutomaticPeriodSubstitutionEnabled = false;
# Disable autocorrect smart quotation marks # # Disable autocorrect smart quotation marks
NSAutomaticQuoteSubstitutionEnabled = false; # NSAutomaticQuoteSubstitutionEnabled = false;
# Disable autocorrect spellcheck # # Disable autocorrect spellcheck
NSAutomaticSpellingCorrectionEnabled = false; # NSAutomaticSpellingCorrectionEnabled = false;
}; # };
# Disable "Are you sure you want to open" dialog # # Disable "Are you sure you want to open" dialog
LaunchServices.LSQuarantine = false; # LaunchServices.LSQuarantine = false;
# Disable trackpad tap to click # # Disable trackpad tap to click
trackpad.Clicking = false; # trackpad.Clicking = false;
# Where to save screenshots # # Where to save screenshots
screencapture.location = "~/Downloads"; # screencapture.location = "~/Downloads";
CustomUserPreferences = { # CustomUserPreferences = {
# Disable disk image verification # # Disable disk image verification
"com.apple.frameworks.diskimages" = { # "com.apple.frameworks.diskimages" = {
skip-verify = true; # skip-verify = true;
skip-verify-locked = true; # skip-verify-locked = true;
skip-verify-remote = true; # skip-verify-remote = true;
}; # };
# Require password immediately after screen saver begins # # Require password immediately after screen saver begins
"com.apple.screensaver" = { # "com.apple.screensaver" = {
askForPassword = 1; # askForPassword = 1;
askForPasswordDelay = 0; # askForPasswordDelay = 0;
}; # };
}; # };
CustomSystemPreferences = { # CustomSystemPreferences = {
}; # };
}; # };
# # Settings that don't have an option in nix-darwin # # Settings that don't have an option in nix-darwin
# activationScripts.postActivation.text = '' # activationScripts.postActivation.text = ''
@@ -17,31 +17,31 @@ in
system.primaryUser = config.nmasur.settings.username; system.primaryUser = config.nmasur.settings.username;
nmasur.presets = { nmasur.presets = {
programs = { # programs = {
fish.enable = lib.mkDefault true; # fish.enable = lib.mkDefault true;
homebrew.enable = lib.mkDefault true; # homebrew.enable = lib.mkDefault true;
}; # };
services = { services = {
daily-summary.enable = lib.mkDefault false; # daily-summary.enable = lib.mkDefault false;
dock.enable = lib.mkDefault true; # dock.enable = lib.mkDefault true;
finder.enable = lib.mkDefault true; # finder.enable = lib.mkDefault true;
hammerspoon.enable = lib.mkDefault true; # hammerspoon.enable = lib.mkDefault true;
menubar.enable = lib.mkDefault true; # menubar.enable = lib.mkDefault true;
nix.enable = lib.mkDefault true; nix.enable = lib.mkDefault true;
settings.enable = lib.mkDefault true; settings.enable = lib.mkDefault true;
user.enable = lib.mkDefault true; user.enable = lib.mkDefault true;
}; };
}; };
homebrew.brews = [ # homebrew.brews = [
"trash" # Delete files and folders to trash instead of rm # "trash" # Delete files and folders to trash instead of rm
]; # ];
homebrew.casks = [ # homebrew.casks = [
"scroll-reverser" # Different scroll style for mouse vs. trackpad # "scroll-reverser" # Different scroll style for mouse vs. trackpad
"notunes" # Don't launch Apple Music with the play button # "notunes" # Don't launch Apple Music with the play button
"topnotch" # Darkens the menu bar to complete black # "topnotch" # Darkens the menu bar to complete black
"ghostty" # Terminal application (not buildable on Nix on macOS) # "ghostty" # Terminal application (not buildable on Nix on macOS)
]; # ];
}; };
} }
@@ -16,12 +16,12 @@ in
nmasur.profiles.base.enable = lib.mkDefault true; nmasur.profiles.base.enable = lib.mkDefault true;
homebrew.casks = [ # homebrew.casks = [
"1password" # 1Password will not launch from Nix on macOS # "1password" # 1Password will not launch from Nix on macOS
# "gitify" # Git notifications in menu bar (downgrade manually from 4.6.1) # # "gitify" # Git notifications in menu bar (downgrade manually from 4.6.1)
# "logitech-g-hub" # Mouse and keyboard management # # "logitech-g-hub" # Mouse and keyboard management
"logitune" # Logitech webcam firmware # "logitune" # Logitech webcam firmware
"meetingbar" # Show meetings in menu bar # "meetingbar" # Show meetings in menu bar
]; # ];
}; };
} }