this repo has no description
1local appBindings = {
2 ["1"] = "Helium",
3 ["2"] = "Ghostty",
4 ["3"] = "Slack",
5 ["4"] = "Mail",
6 ["5"] = "Messages",
7 ["6"] = "Zoom.us",
8 ["8"] = "Spotify",
9 ["9"] = "Todoist",
10 ["0"] = "Obsidian",
11}
12
13for key, app in pairs(appBindings) do
14 hs.hotkey.bind({ "cmd", "ctrl" }, key, function()
15 hs.application.launchOrFocus(app)
16 end)
17end
18
19hs.hotkey.bind({ "cmd", "ctrl" }, "r", function()
20 hs.reload()
21end)
22
23-- Window management
24hs.window.animationDuration = 0
25
26local function moveWindow(fn)
27 local win = hs.window.focusedWindow()
28 if not win then
29 return
30 end
31 local screen = win:screen():frame()
32 fn(win, screen)
33end
34
35local windowBindings = {
36 h = function(w, s)
37 w:setFrame({ x = s.x, y = s.y, w = s.w / 2, h = s.h })
38 end,
39 l = function(w, s)
40 w:setFrame({ x = s.x + s.w / 2, y = s.y, w = s.w / 2, h = s.h })
41 end,
42 Return = function(w, s)
43 w:setFrame(s)
44 end,
45 u = function(w, s)
46 w:setFrame({ x = s.x, y = s.y, w = s.w / 2, h = s.h / 2 })
47 end,
48 i = function(w, s)
49 w:setFrame({ x = s.x + s.w / 2, y = s.y, w = s.w / 2, h = s.h / 2 })
50 end,
51 j = function(w, s)
52 w:setFrame({ x = s.x, y = s.y + s.h / 2, w = s.w / 2, h = s.h / 2 })
53 end,
54 k = function(w, s)
55 w:setFrame({ x = s.x + s.w / 2, y = s.y + s.h / 2, w = s.w / 2, h = s.h / 2 })
56 end,
57}
58
59for key, fn in pairs(windowBindings) do
60 hs.hotkey.bind({ "ctrl", "alt" }, key, function()
61 moveWindow(fn)
62 end)
63end
64
65-- Cycling window positions (thirds and 2/3rds)
66local cycleState = { g = 0, e = 0 }
67
68-- g: 1/3 width, cycles right → middle → left
69hs.hotkey.bind({ "ctrl", "alt" }, "g", function()
70 local win = hs.window.focusedWindow()
71 if not win then
72 return
73 end
74 local s = win:screen():frame()
75 local positions = {
76 { x = s.x + s.w * 2 / 3, y = s.y, w = s.w / 3, h = s.h },
77 { x = s.x + s.w / 3, y = s.y, w = s.w / 3, h = s.h },
78 { x = s.x, y = s.y, w = s.w / 3, h = s.h },
79 }
80 cycleState.g = (cycleState.g % #positions) + 1
81 win:setFrame(positions[cycleState.g])
82end)
83
84-- e: 2/3 width, cycles left → right
85hs.hotkey.bind({ "ctrl", "alt" }, "e", function()
86 local win = hs.window.focusedWindow()
87 if not win then
88 return
89 end
90 local s = win:screen():frame()
91 local positions = {
92 { x = s.x, y = s.y, w = s.w * 2 / 3, h = s.h },
93 { x = s.x + s.w / 3, y = s.y, w = s.w * 2 / 3, h = s.h },
94 }
95 cycleState.e = (cycleState.e % #positions) + 1
96 win:setFrame(positions[cycleState.e])
97end)
98
99-- Volume control
100local volumeStep = 5
101local volumeMods = { "ctrl", "shift", "cmd" }
102local volumeAlertId = nil
103
104local function showVolumeAlert(vol)
105 if volumeAlertId then
106 hs.alert.closeSpecific(volumeAlertId)
107 end
108 volumeAlertId = hs.alert.show("Volume: " .. math.floor(vol) .. "%")
109end
110
111hs.hotkey.bind(volumeMods, "=", function()
112 local device = hs.audiodevice.defaultOutputDevice()
113 device:setVolume(math.min(100, device:volume() + volumeStep))
114 showVolumeAlert(device:volume())
115end)
116
117hs.hotkey.bind(volumeMods, "-", function()
118 local device = hs.audiodevice.defaultOutputDevice()
119 device:setVolume(math.max(0, device:volume() - volumeStep))
120 showVolumeAlert(device:volume())
121end)
122
123hs.hotkey.bind(volumeMods, "0", function()
124 hs.eventtap.event.newSystemKeyEvent("PLAY", true):post()
125 hs.eventtap.event.newSystemKeyEvent("PLAY", false):post()
126end)
127
128hs.alert.show("Config loaded")