Configure macOS window tooling
This commit is contained in:
438
dotfiles/hammerspoon/init.lua
Normal file
438
dotfiles/hammerspoon/init.lua
Normal file
@@ -0,0 +1,438 @@
|
|||||||
|
local hyper = { "cmd", "ctrl", "alt" }
|
||||||
|
local hyperShift = { "cmd", "ctrl", "alt", "shift" }
|
||||||
|
local wf = hs.window.filter.defaultCurrentSpace
|
||||||
|
|
||||||
|
hs.window.animationDuration = 0
|
||||||
|
pcall(function()
|
||||||
|
hs.ipc.cliInstall()
|
||||||
|
end)
|
||||||
|
|
||||||
|
local config = {
|
||||||
|
gap = 8,
|
||||||
|
autoColumns = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
local retileTimer = nil
|
||||||
|
local arranging = false
|
||||||
|
local rightCommandDown = false
|
||||||
|
local rightCommandUsed = false
|
||||||
|
local rightCommandKeyCode = 54
|
||||||
|
|
||||||
|
local function notify(message)
|
||||||
|
hs.alert.show(message, 0.7)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function screenFrame(screen)
|
||||||
|
return screen:frame()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sameScreen(a, b)
|
||||||
|
return a and b and a:id() == b:id()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function centerX(win)
|
||||||
|
local f = win:frame()
|
||||||
|
return f.x + (f.w / 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function centerY(win)
|
||||||
|
local f = win:frame()
|
||||||
|
return f.y + (f.h / 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function distance(a, b)
|
||||||
|
local dx = a.x - b.x
|
||||||
|
local dy = a.y - b.y
|
||||||
|
return math.sqrt((dx * dx) + (dy * dy))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function directionScore(focused, candidate, direction)
|
||||||
|
local focusedCenter = {
|
||||||
|
x = centerX(focused),
|
||||||
|
y = centerY(focused),
|
||||||
|
}
|
||||||
|
local candidateCenter = {
|
||||||
|
x = centerX(candidate),
|
||||||
|
y = centerY(candidate),
|
||||||
|
}
|
||||||
|
|
||||||
|
if direction == "left" and candidateCenter.x >= focusedCenter.x then
|
||||||
|
return nil
|
||||||
|
elseif direction == "right" and candidateCenter.x <= focusedCenter.x then
|
||||||
|
return nil
|
||||||
|
elseif direction == "up" and candidateCenter.y >= focusedCenter.y then
|
||||||
|
return nil
|
||||||
|
elseif direction == "down" and candidateCenter.y <= focusedCenter.y then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return distance(focusedCenter, candidateCenter)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function columnWindows(screen)
|
||||||
|
local windows = {}
|
||||||
|
|
||||||
|
for _, win in ipairs(wf:getWindows()) do
|
||||||
|
if win:isStandard()
|
||||||
|
and not win:isMinimized()
|
||||||
|
and sameScreen(win:screen(), screen)
|
||||||
|
then
|
||||||
|
table.insert(windows, win)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(windows, function(a, b)
|
||||||
|
local af = a:frame()
|
||||||
|
local bf = b:frame()
|
||||||
|
|
||||||
|
if math.abs(centerX(a) - centerX(b)) > 24 then
|
||||||
|
return centerX(a) < centerX(b)
|
||||||
|
end
|
||||||
|
|
||||||
|
return af.y < bf.y
|
||||||
|
end)
|
||||||
|
|
||||||
|
return windows
|
||||||
|
end
|
||||||
|
|
||||||
|
local function neighborWindow(direction)
|
||||||
|
local focused = hs.window.focusedWindow()
|
||||||
|
if not focused then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local focusedId = focused:id()
|
||||||
|
local best = nil
|
||||||
|
local bestScore = nil
|
||||||
|
|
||||||
|
for _, win in ipairs(wf:getWindows()) do
|
||||||
|
if win:isStandard()
|
||||||
|
and not win:isMinimized()
|
||||||
|
and sameScreen(win:screen(), focused:screen())
|
||||||
|
and win:id() ~= focusedId
|
||||||
|
then
|
||||||
|
local score = directionScore(focused, win, direction)
|
||||||
|
if score and (not bestScore or score < bestScore) then
|
||||||
|
best = win
|
||||||
|
bestScore = score
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return best
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setFrame(win, frame)
|
||||||
|
win:setFrame(frame, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function tileWindows(windows)
|
||||||
|
if #windows == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
arranging = true
|
||||||
|
|
||||||
|
local screen = windows[1]:screen()
|
||||||
|
local frame = screenFrame(screen)
|
||||||
|
local gap = config.gap
|
||||||
|
local count = #windows
|
||||||
|
local width = (frame.w - (gap * (count - 1))) / count
|
||||||
|
|
||||||
|
for index, win in ipairs(windows) do
|
||||||
|
setFrame(win, {
|
||||||
|
x = frame.x + ((index - 1) * (width + gap)),
|
||||||
|
y = frame.y,
|
||||||
|
w = width,
|
||||||
|
h = frame.h,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
arranging = false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function tileFocusedScreen()
|
||||||
|
local focused = hs.window.focusedWindow()
|
||||||
|
if not focused then
|
||||||
|
notify("No focused window")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local windows = columnWindows(focused:screen())
|
||||||
|
tileWindows(windows)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function focusWindow(direction)
|
||||||
|
local target = neighborWindow(direction)
|
||||||
|
if target then
|
||||||
|
target:focus()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function swapWindow(direction)
|
||||||
|
local focused = hs.window.focusedWindow()
|
||||||
|
local target = neighborWindow(direction)
|
||||||
|
if not focused or not target then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
arranging = true
|
||||||
|
|
||||||
|
local focusedFrame = focused:frame()
|
||||||
|
local targetFrame = target:frame()
|
||||||
|
|
||||||
|
setFrame(focused, targetFrame)
|
||||||
|
setFrame(target, focusedFrame)
|
||||||
|
focused:focus()
|
||||||
|
|
||||||
|
arranging = false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function placeFocused(startColumn, spanColumns, totalColumns)
|
||||||
|
local focused = hs.window.focusedWindow()
|
||||||
|
if not focused then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local frame = screenFrame(focused:screen())
|
||||||
|
local gap = config.gap
|
||||||
|
local unit = (frame.w - (gap * (totalColumns - 1))) / totalColumns
|
||||||
|
local x = frame.x + ((startColumn - 1) * (unit + gap))
|
||||||
|
local width = (unit * spanColumns) + (gap * (spanColumns - 1))
|
||||||
|
|
||||||
|
setFrame(focused, {
|
||||||
|
x = x,
|
||||||
|
y = frame.y,
|
||||||
|
w = width,
|
||||||
|
h = frame.h,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function moveFocusedToScreen(direction)
|
||||||
|
local focused = hs.window.focusedWindow()
|
||||||
|
if not focused then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local target = direction < 0 and focused:screen():previous() or focused:screen():next()
|
||||||
|
focused:moveToScreen(target, false, true)
|
||||||
|
tileWindows(columnWindows(target))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function scheduleRetile()
|
||||||
|
if arranging or not config.autoColumns then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if retileTimer then
|
||||||
|
retileTimer:stop()
|
||||||
|
end
|
||||||
|
|
||||||
|
retileTimer = hs.timer.doAfter(0.25, tileFocusedScreen)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function toggleAutoColumns()
|
||||||
|
config.autoColumns = not config.autoColumns
|
||||||
|
notify(config.autoColumns and "Auto columns on" or "Auto columns off")
|
||||||
|
|
||||||
|
if config.autoColumns then
|
||||||
|
tileFocusedScreen()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
wf:subscribe({
|
||||||
|
hs.window.filter.windowCreated,
|
||||||
|
hs.window.filter.windowDestroyed,
|
||||||
|
hs.window.filter.windowMoved,
|
||||||
|
hs.window.filter.windowInCurrentSpace,
|
||||||
|
hs.window.filter.windowNotInCurrentSpace,
|
||||||
|
hs.window.filter.windowUnminimized,
|
||||||
|
hs.window.filter.windowNotVisible,
|
||||||
|
}, scheduleRetile)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyper, "c", tileFocusedScreen)
|
||||||
|
hs.hotkey.bind(hyper, "v", toggleAutoColumns)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyper, "a", function()
|
||||||
|
focusWindow("left")
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyper, "d", function()
|
||||||
|
focusWindow("right")
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyper, "w", function()
|
||||||
|
focusWindow("up")
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyper, "s", function()
|
||||||
|
focusWindow("down")
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyperShift, "a", function()
|
||||||
|
swapWindow("left")
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyperShift, "d", function()
|
||||||
|
swapWindow("right")
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyperShift, "w", function()
|
||||||
|
swapWindow("up")
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyperShift, "s", function()
|
||||||
|
swapWindow("down")
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyper, "m", function()
|
||||||
|
placeFocused(1, 1, 1)
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyper, "f", function()
|
||||||
|
placeFocused(1, 1, 1)
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyper, "1", function()
|
||||||
|
placeFocused(1, 1, 3)
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyper, "2", function()
|
||||||
|
placeFocused(2, 1, 3)
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyper, "3", function()
|
||||||
|
placeFocused(3, 1, 3)
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyper, "4", function()
|
||||||
|
placeFocused(1, 2, 3)
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyper, "5", function()
|
||||||
|
placeFocused(2, 2, 3)
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyper, "q", function()
|
||||||
|
moveFocusedToScreen(-1)
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyper, "e", function()
|
||||||
|
moveFocusedToScreen(1)
|
||||||
|
end)
|
||||||
|
|
||||||
|
hs.hotkey.bind(hyper, "r", hs.reload)
|
||||||
|
|
||||||
|
local rguiBindings = {}
|
||||||
|
|
||||||
|
local function bindRgui(key, handler, shiftedHandler)
|
||||||
|
rguiBindings[hs.keycodes.map[key]] = {
|
||||||
|
normal = handler,
|
||||||
|
shifted = shiftedHandler,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
bindRgui("a", function()
|
||||||
|
focusWindow("left")
|
||||||
|
end, function()
|
||||||
|
swapWindow("left")
|
||||||
|
end)
|
||||||
|
|
||||||
|
bindRgui("d", function()
|
||||||
|
focusWindow("right")
|
||||||
|
end, function()
|
||||||
|
swapWindow("right")
|
||||||
|
end)
|
||||||
|
|
||||||
|
bindRgui("w", function()
|
||||||
|
focusWindow("up")
|
||||||
|
end, function()
|
||||||
|
swapWindow("up")
|
||||||
|
end)
|
||||||
|
|
||||||
|
bindRgui("s", function()
|
||||||
|
focusWindow("down")
|
||||||
|
end, function()
|
||||||
|
swapWindow("down")
|
||||||
|
end)
|
||||||
|
|
||||||
|
bindRgui("c", tileFocusedScreen)
|
||||||
|
bindRgui("v", toggleAutoColumns)
|
||||||
|
|
||||||
|
bindRgui("m", function()
|
||||||
|
placeFocused(1, 1, 1)
|
||||||
|
end)
|
||||||
|
|
||||||
|
bindRgui("f", function()
|
||||||
|
placeFocused(1, 1, 1)
|
||||||
|
end)
|
||||||
|
|
||||||
|
bindRgui("1", function()
|
||||||
|
placeFocused(1, 1, 3)
|
||||||
|
end)
|
||||||
|
|
||||||
|
bindRgui("2", function()
|
||||||
|
placeFocused(2, 1, 3)
|
||||||
|
end)
|
||||||
|
|
||||||
|
bindRgui("3", function()
|
||||||
|
placeFocused(3, 1, 3)
|
||||||
|
end)
|
||||||
|
|
||||||
|
bindRgui("4", function()
|
||||||
|
placeFocused(1, 2, 3)
|
||||||
|
end)
|
||||||
|
|
||||||
|
bindRgui("5", function()
|
||||||
|
placeFocused(2, 2, 3)
|
||||||
|
end)
|
||||||
|
|
||||||
|
bindRgui("q", function()
|
||||||
|
moveFocusedToScreen(-1)
|
||||||
|
end)
|
||||||
|
|
||||||
|
bindRgui("e", function()
|
||||||
|
moveFocusedToScreen(1)
|
||||||
|
end)
|
||||||
|
|
||||||
|
bindRgui("r", hs.reload)
|
||||||
|
|
||||||
|
local rguiTap = hs.eventtap.new({
|
||||||
|
hs.eventtap.event.types.flagsChanged,
|
||||||
|
hs.eventtap.event.types.keyDown,
|
||||||
|
}, function(event)
|
||||||
|
local keyCode = event:getKeyCode()
|
||||||
|
local eventType = event:getType()
|
||||||
|
|
||||||
|
if eventType == hs.eventtap.event.types.flagsChanged and keyCode == rightCommandKeyCode then
|
||||||
|
rightCommandDown = event:getFlags().cmd
|
||||||
|
if rightCommandDown then
|
||||||
|
rightCommandUsed = false
|
||||||
|
elseif not rightCommandUsed then
|
||||||
|
hs.eventtap.keyStroke({}, "escape", 0)
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if eventType ~= hs.eventtap.event.types.keyDown or not rightCommandDown then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local binding = rguiBindings[keyCode]
|
||||||
|
if not binding then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
rightCommandUsed = true
|
||||||
|
local flags = event:getFlags()
|
||||||
|
local handler = flags.shift and binding.shifted or binding.normal
|
||||||
|
if handler then
|
||||||
|
handler()
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end)
|
||||||
|
|
||||||
|
rguiTap:start()
|
||||||
|
|
||||||
|
notify("Hammerspoon loaded")
|
||||||
@@ -71,6 +71,23 @@
|
|||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
libDir = ../dotfiles/lib;
|
libDir = ../dotfiles/lib;
|
||||||
|
# Keep this on the currently-existing macOS account until the target user
|
||||||
|
# exists locally and its home directory has been migrated.
|
||||||
|
activePrimaryUser = "kat";
|
||||||
|
targetPrimaryUser = "imalison";
|
||||||
|
primaryUser = activePrimaryUser;
|
||||||
|
personalUsers = [
|
||||||
|
activePrimaryUser
|
||||||
|
targetPrimaryUser
|
||||||
|
];
|
||||||
|
# Home Manager activation should only target accounts that exist today.
|
||||||
|
# Add targetPrimaryUser here when the macOS account is ready.
|
||||||
|
enabledHomeUsers = [
|
||||||
|
activePrimaryUser
|
||||||
|
];
|
||||||
|
sharedHomeModules = [./home/common.nix];
|
||||||
|
ivanHomeModules = [./home/ivan.nix];
|
||||||
|
homeForUser = user: "/Users/${user}";
|
||||||
configuration = {
|
configuration = {
|
||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
@@ -78,12 +95,21 @@
|
|||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
essentialPkgs = (import ../nix-shared/system/essential.nix {inherit pkgs lib inputs;}).environment.systemPackages;
|
essentialPkgs = (import ../nix-shared/system/essential.nix {inherit pkgs lib inputs;}).environment.systemPackages;
|
||||||
|
disabledAppleSymbolicHotKey = parameters: {
|
||||||
|
enabled = false;
|
||||||
|
value = {
|
||||||
|
inherit parameters;
|
||||||
|
type = "standard";
|
||||||
|
};
|
||||||
|
};
|
||||||
in {
|
in {
|
||||||
networking.hostName = "mac-demarco-mini";
|
networking.hostName = "mac-demarco-mini";
|
||||||
imports = [(import ./gitea-actions-runner.nix)];
|
imports = [
|
||||||
|
(import ./gitea-actions-runner.nix)
|
||||||
|
];
|
||||||
age = {
|
age = {
|
||||||
identityPaths = [
|
identityPaths = [
|
||||||
"${config.users.users.kat.home}/.ssh/id_ed25519"
|
"${config.users.users.${primaryUser}.home}/.ssh/id_ed25519"
|
||||||
"/etc/ssh/ssh_host_ed25519_key"
|
"/etc/ssh/ssh_host_ed25519_key"
|
||||||
"/etc/ssh/ssh_host_rsa_key"
|
"/etc/ssh/ssh_host_rsa_key"
|
||||||
];
|
];
|
||||||
@@ -135,37 +161,31 @@
|
|||||||
XDG_RUNTIME_DIR = "/var/lib/gitea-runner/tmp";
|
XDG_RUNTIME_DIR = "/var/lib/gitea-runner/tmp";
|
||||||
};
|
};
|
||||||
|
|
||||||
system.primaryUser = "kat";
|
system.primaryUser = primaryUser;
|
||||||
|
|
||||||
|
security.sudo.extraConfig = ''
|
||||||
|
${primaryUser} ALL=(ALL) NOPASSWD: ALL
|
||||||
|
'';
|
||||||
|
|
||||||
system.defaults.NSGlobalDomain."com.apple.swipescrolldirection" = false;
|
system.defaults.NSGlobalDomain."com.apple.swipescrolldirection" = false;
|
||||||
system.defaults.CustomUserPreferences."com.apple.screensaver".idleTime = 300;
|
system.defaults.CustomUserPreferences."com.apple.screensaver".idleTime = 300;
|
||||||
system.defaults.CustomUserPreferences."com.apple.symbolichotkeys".AppleSymbolicHotKeys = {
|
system.defaults.CustomUserPreferences."com.apple.symbolichotkeys".AppleSymbolicHotKeys = {
|
||||||
"60" = {
|
# Disable input source shortcuts that conflict with launcher usage.
|
||||||
enabled = false;
|
"60" = disabledAppleSymbolicHotKey [32 49 262144];
|
||||||
value = {
|
"61" = disabledAppleSymbolicHotKey [32 49 786432];
|
||||||
parameters = [
|
# Disable Spotlight's Command-Space and Finder search window shortcuts.
|
||||||
32
|
"64" = disabledAppleSymbolicHotKey [32 49 1048576];
|
||||||
49
|
"65" = disabledAppleSymbolicHotKey [32 49 1572864];
|
||||||
262144
|
|
||||||
];
|
|
||||||
type = "standard";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
"61" = {
|
|
||||||
enabled = false;
|
|
||||||
value = {
|
|
||||||
parameters = [
|
|
||||||
32
|
|
||||||
49
|
|
||||||
786432
|
|
||||||
];
|
|
||||||
type = "standard";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
system.defaults.screensaver.askForPassword = false;
|
system.defaults.screensaver.askForPassword = false;
|
||||||
system.defaults.screensaver.askForPasswordDelay = 0;
|
system.defaults.screensaver.askForPasswordDelay = 0;
|
||||||
|
|
||||||
|
system.activationScripts.postActivation.text = ''
|
||||||
|
echo >&2 "current-host screensaver defaults..."
|
||||||
|
launchctl asuser "$(id -u -- ${primaryUser})" sudo --user=${primaryUser} -- defaults -currentHost write com.apple.screensaver askForPassword -bool false
|
||||||
|
launchctl asuser "$(id -u -- ${primaryUser})" sudo --user=${primaryUser} -- defaults -currentHost write com.apple.screensaver askForPasswordDelay -int 0
|
||||||
|
'';
|
||||||
|
|
||||||
power.sleep = {
|
power.sleep = {
|
||||||
computer = "never";
|
computer = "never";
|
||||||
display = "never";
|
display = "never";
|
||||||
@@ -237,6 +257,7 @@
|
|||||||
casks = [
|
casks = [
|
||||||
"codex-app"
|
"codex-app"
|
||||||
"ghostty"
|
"ghostty"
|
||||||
|
"hammerspoon"
|
||||||
"raycast"
|
"raycast"
|
||||||
"vlc"
|
"vlc"
|
||||||
];
|
];
|
||||||
@@ -248,6 +269,10 @@
|
|||||||
|
|
||||||
# Auto upgrade nix package and the daemon service.
|
# Auto upgrade nix package and the daemon service.
|
||||||
launchd.user.envVariables.PATH = config.environment.systemPath;
|
launchd.user.envVariables.PATH = config.environment.systemPath;
|
||||||
|
launchd.user.agents.hammerspoon.serviceConfig = {
|
||||||
|
ProgramArguments = ["/usr/bin/open" "-gja" "Hammerspoon"];
|
||||||
|
RunAtLoad = true;
|
||||||
|
};
|
||||||
|
|
||||||
programs.direnv.enable = true;
|
programs.direnv.enable = true;
|
||||||
|
|
||||||
@@ -274,17 +299,19 @@
|
|||||||
# The platform the configuration will be used on.
|
# The platform the configuration will be used on.
|
||||||
|
|
||||||
nixpkgs.hostPlatform = "aarch64-darwin";
|
nixpkgs.hostPlatform = "aarch64-darwin";
|
||||||
users.users.kat.openssh.authorizedKeys.keys = inputs.railbird-secrets.keys.kanivanKeys;
|
users.users =
|
||||||
users.users.gitea-runner = {
|
lib.genAttrs personalUsers (user: {
|
||||||
|
name = user;
|
||||||
|
home = homeForUser user;
|
||||||
|
openssh.authorizedKeys.keys = inputs.railbird-secrets.keys.kanivanKeys;
|
||||||
|
})
|
||||||
|
// {
|
||||||
|
gitea-runner = {
|
||||||
name = "gitea-runner";
|
name = "gitea-runner";
|
||||||
isHidden = false;
|
isHidden = false;
|
||||||
home = "/Users/gitea-runner";
|
home = "/Users/gitea-runner";
|
||||||
createHome = false;
|
createHome = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
users.users.kat = {
|
|
||||||
name = "kat";
|
|
||||||
home = "/Users/kat";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.zsh = {
|
programs.zsh = {
|
||||||
@@ -298,10 +325,10 @@
|
|||||||
extraSpecialArgs = {
|
extraSpecialArgs = {
|
||||||
inherit inputs libDir;
|
inherit inputs libDir;
|
||||||
};
|
};
|
||||||
sharedModules = [./home/common.nix];
|
sharedModules = sharedHomeModules;
|
||||||
users.kat = {
|
users = lib.genAttrs enabledHomeUsers (_: {
|
||||||
imports = [./home/kat.nix];
|
imports = ivanHomeModules;
|
||||||
};
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
@@ -313,9 +340,11 @@
|
|||||||
{
|
{
|
||||||
nix-homebrew = {
|
nix-homebrew = {
|
||||||
enable = true;
|
enable = true;
|
||||||
user = "kat";
|
user = primaryUser;
|
||||||
autoMigrate = true;
|
autoMigrate = true;
|
||||||
package = inputs.brew-src // {
|
package =
|
||||||
|
inputs.brew-src
|
||||||
|
// {
|
||||||
name = "brew-5.1.7";
|
name = "brew-5.1.7";
|
||||||
version = "5.1.7";
|
version = "5.1.7";
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -107,10 +107,26 @@ in {
|
|||||||
|
|
||||||
myModules.codexGeneratedSkills.enable = true;
|
myModules.codexGeneratedSkills.enable = true;
|
||||||
|
|
||||||
home.packages = [
|
home.packages =
|
||||||
pkgs.gnupg
|
[
|
||||||
(pkgs.pass.withExtensions (ext: [ext.pass-otp]))
|
(pkgs.pass.withExtensions (ext: [ext.pass-otp]))
|
||||||
];
|
]
|
||||||
|
++ (with pkgs; [
|
||||||
|
alejandra
|
||||||
|
alt-tab-macos
|
||||||
|
claude-code
|
||||||
|
cocoapods
|
||||||
|
codex
|
||||||
|
gnupg
|
||||||
|
nodejs
|
||||||
|
playwright-cli
|
||||||
|
prettier
|
||||||
|
slack
|
||||||
|
tea
|
||||||
|
typescript
|
||||||
|
vim
|
||||||
|
yarn
|
||||||
|
]);
|
||||||
|
|
||||||
home.activation.repairGpgHomeAndImportKey = lib.hm.dag.entryAfter ["writeBoundary"] ''
|
home.activation.repairGpgHomeAndImportKey = lib.hm.dag.entryAfter ["writeBoundary"] ''
|
||||||
gnupg_dir="$HOME/.gnupg"
|
gnupg_dir="$HOME/.gnupg"
|
||||||
@@ -141,6 +157,27 @@ in {
|
|||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
home.activation.configureRaycastHotkey = lib.hm.dag.entryAfter ["writeBoundary"] ''
|
||||||
|
raycast_domain="com.raycast.macos"
|
||||||
|
desired_hotkey="Command-49"
|
||||||
|
current_hotkey="$(/usr/bin/defaults read "$raycast_domain" raycastGlobalHotkey 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [ -d /Applications/Raycast.app ]; then
|
||||||
|
/usr/bin/xattr -dr com.apple.quarantine /Applications/Raycast.app 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$current_hotkey" != "$desired_hotkey" ]; then
|
||||||
|
/usr/bin/defaults write "$raycast_domain" raycastGlobalHotkey -string "$desired_hotkey"
|
||||||
|
/usr/bin/defaults write "$raycast_domain" mainWindow_isMonitoringGlobalHotkeys -bool true
|
||||||
|
|
||||||
|
if /usr/bin/pgrep -x Raycast >/dev/null 2>&1; then
|
||||||
|
/usr/bin/killall Raycast || true
|
||||||
|
/bin/sleep 1
|
||||||
|
fi
|
||||||
|
/usr/bin/open /Applications/Raycast.app || true
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
home.sessionPath = [
|
home.sessionPath = [
|
||||||
"$HOME/.cargo/bin"
|
"$HOME/.cargo/bin"
|
||||||
"${libDir}/bin"
|
"${libDir}/bin"
|
||||||
@@ -205,8 +242,7 @@ in {
|
|||||||
};
|
};
|
||||||
ProgramArguments = [
|
ProgramArguments = [
|
||||||
"/usr/bin/open"
|
"/usr/bin/open"
|
||||||
"-a"
|
"/Applications/Raycast.app"
|
||||||
"Raycast"
|
|
||||||
];
|
];
|
||||||
KeepAlive = false;
|
KeepAlive = false;
|
||||||
ProcessType = "Interactive";
|
ProcessType = "Interactive";
|
||||||
@@ -216,6 +252,22 @@ in {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
launchd.agents.alt-tab = lib.mkIf pkgs.stdenv.isDarwin {
|
||||||
|
enable = true;
|
||||||
|
config = {
|
||||||
|
ProgramArguments = [
|
||||||
|
"/usr/bin/open"
|
||||||
|
"-gj"
|
||||||
|
"${pkgs.alt-tab-macos}/Applications/AltTab.app"
|
||||||
|
];
|
||||||
|
KeepAlive = false;
|
||||||
|
ProcessType = "Interactive";
|
||||||
|
RunAtLoad = true;
|
||||||
|
StandardOutPath = "${config.home.homeDirectory}/Library/Logs/alt-tab-launchd.log";
|
||||||
|
StandardErrorPath = "${config.home.homeDirectory}/Library/Logs/alt-tab-launchd.err.log";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
programs.starship = {
|
programs.starship = {
|
||||||
enable = true;
|
enable = true;
|
||||||
};
|
};
|
||||||
@@ -247,4 +299,6 @@ in {
|
|||||||
};
|
};
|
||||||
|
|
||||||
xdg.configFile = xdgConfigLinks;
|
xdg.configFile = xdgConfigLinks;
|
||||||
|
|
||||||
|
home.stateVersion = "24.05";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,8 @@
|
|||||||
{pkgs, ...}: {
|
{
|
||||||
home.packages = with pkgs; [
|
config,
|
||||||
alejandra
|
pkgs,
|
||||||
claude-code
|
...
|
||||||
cocoapods
|
}: {
|
||||||
codex
|
|
||||||
nodejs
|
|
||||||
prettier
|
|
||||||
slack
|
|
||||||
tea
|
|
||||||
typescript
|
|
||||||
vim
|
|
||||||
yarn
|
|
||||||
];
|
|
||||||
|
|
||||||
services.git-sync = {
|
services.git-sync = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package =
|
package =
|
||||||
@@ -21,16 +11,14 @@
|
|||||||
else pkgs.git-sync;
|
else pkgs.git-sync;
|
||||||
repositories = {
|
repositories = {
|
||||||
org = {
|
org = {
|
||||||
path = "/Users/kat/org";
|
path = "${config.home.homeDirectory}/org";
|
||||||
uri = "git@github.com:colonelpanic8/org.git";
|
uri = "git@github.com:colonelpanic8/org.git";
|
||||||
interval = 180;
|
interval = 180;
|
||||||
};
|
};
|
||||||
password-store = {
|
password-store = {
|
||||||
path = "/Users/kat/.password-store";
|
path = "${config.home.homeDirectory}/.password-store";
|
||||||
uri = "git@github.com:IvanMalison/.password-store.git";
|
uri = "git@github.com:IvanMalison/.password-store.git";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
home.stateVersion = "24.05";
|
|
||||||
}
|
}
|
||||||
@@ -312,7 +312,7 @@ from transformers import (/' \
|
|||||||
});
|
});
|
||||||
|
|
||||||
happy-coder = final.callPackage ./packages/happy-coder { };
|
happy-coder = final.callPackage ./packages/happy-coder { };
|
||||||
playwright-cli = final.callPackage ./packages/playwright-cli { };
|
playwright-cli = final.callPackage ../../nixos/packages/playwright-cli { };
|
||||||
t3code = final.callPackage ./packages/t3code { };
|
t3code = final.callPackage ./packages/t3code { };
|
||||||
# Custom Waybar fork for workspace taskbar support + external SNI watcher option.
|
# Custom Waybar fork for workspace taskbar support + external SNI watcher option.
|
||||||
waybar = prev.waybar.overrideAttrs (old: {
|
waybar = prev.waybar.overrideAttrs (old: {
|
||||||
|
|||||||
Reference in New Issue
Block a user