347 lines
7.4 KiB
Lua

-- vim: :foldmethod=marker
hs.application.enableSpotlightForNameSearches(true)
local utils = hs.fnutils
local DEFAULTS = {
["EDITOR"] = {"BBEdit"},
["CHAT"] = {"Telegram"},
["MESSENGER"] = {"Messages"},
["MUSIC"] = {"Doppler"},
}
local APPS = {
browser = {{"Zen"}, {"Chromium"}, {"Safari"}},
editors = {DEFAULTS["EDITOR"], {"Nova"}, {"Xcode"}, {"Code", "VSCodium"}, {"IntelliJ IDEA"}},
chats = {{"Microsoft Teams"}, {"Telegram"}},
messengers = {{"Messages"}, {"Telegram"}, {"Signal"}},
music = {DEFAULTS["MUSIC"], {"Music"}},
calendar = {"Calendar"},
terminal = {"Kitty", "kitty"},
videocalls = {}
}
-- Watchers {{{1
-- Cfg Watcher {{{2
function reload(files)
doReload = false
for _,file in pairs(files) do
if file:sub(-4) == ".lua" then
doReload = true
end
end
if doReload then
hs.reload()
end
end
cfgWatcher = hs.pathwatcher.new(os.getenv("HOME") .. "/.hammerspoon/", reload):start()
hs.alert.show("Config loaded")
-- Apps {{{1
-- Listeners {{{2
local listeners = {}
appWatcher = hs.application.watcher.new(function(name, event, app)
for i, v in ipairs(listeners) do
v(name, event, app)
end
end):start()
function app_listen(func)
listeners[#listeners+1] = func
end
-- {{{3 Terminal(s)
app_listen(function(name, event, app)
if app_matches(app, APPS["terminal"]) and event == hs.application.watcher.deactivated then
-- Don't hide terminal windows that have "claude" in the title
local shouldHide = true
for _, window in ipairs(app:allWindows()) do
if window:title() and string.find(string.lower(window:title()), "claude") then
shouldHide = false
break
end
end
if shouldHide then
app:hide()
end
elseif app_matches(app, {"Terminal"}) and event == hs.application.watcher.deactivated then
app:hide()
end
end)
-- Picker {{{2
function app_matches(app, names) --{{{3
--print("MATCHING: " .. app:name() .. " vs " .. hs.json.encode(names))
for i,v in ipairs(names) do
if app:name() == v then
return true
end
end
return false
end
function app_tofront(names) --{{{3
for i, v in ipairs(names) do
if hs.application.launchOrFocus(v) then
return
end
end
end
function app_exists(names) --{{{3
for i, v in pairs(names) do
local app = hs.application.get(v)
if app then
return true
end
end
return false
end
function app_is_running(apps, names) --it accepts list of apps to choose from for performance sake {{{3
for i, app in ipairs(apps) do
print(app)
for i, name in ipairs(names) do
if app:name() == name and #(app:allWindows()) > 0 then
return true
end
end
end
return false
end
function app_toggle(names) --{{{3
local front = hs.application.frontmostApplication()
-- print(front)
if app_matches(front, names) then
--print("matches " .. front:name())
front:hide()
else
--print("not matches " .. front:name())
app_tofront(names)
end
--print("END TOGGLE")
end
function app_one_of(app, others) --{{{3
for i,v in ipairs(others) do
if app_matches(app, v) then
return v
end
end
return nil
end
function app_pick_next_running(running, front, apps) --{{{3
local pick = false
for i,v in ipairs(apps) do
if app_matches(front, v) then
pick = true
goto continue
end
if pick and app_is_running(running, v) then
return v
end
::continue::
end
return nil
end
function app_pick_first_running(running, front, apps) --{{{3
for i,v in ipairs(apps) do
if app_is_running(running, v) then
return v
end
end
return nil
end
function app_pick(prev, apps, limit_running) --{{{3
local default = apps[1]
local running = hs.application.runningApplications()
local front = hs.application.frontmostApplication()
local picked = app_pick_next_running(running, front, apps)
if picked then
app_toggle(picked)
return picked
end
if prev and app_is_running(running, prev) and not app_matches(front, prev) then
app_toggle(prev)
return prev
end
if app_is_running(running, default) then -- optimisation, don't check for if installed if it's running
app_toggle(default)
used = default
if prev and app_matches(front, prev) and not app_matches(front, default) then
front:hide()
end
return default
end
if app_exists(default) and not limit_running then
app_toggle(default)
if prev and app_matches(front, prev) and not app_matches(front, default) then
front:hide()
end
return default
end
local first = app_pick_first_running(running, front, apps)
if first then
app_toggle(first)
return first
end
return nil
end
-- Key Bindings {{{1
-- Apps {{{2
-- Editor {{{3
hs.hotkey.bind({"cmd", "alt" }, "I", (function()
local last_editor = nil
app_listen(function(name, event, app)
if app and event == hs.application.watcher.activated then
local match = app_one_of(app, APPS["editors"])
if match then
last_editor = match
end
end
end)
return function()
last_editor = app_pick(last_editor and utils.copy(last_editor) or nil, APPS["editors"], true)
end
end)())
-- Finder {{{3
hs.hotkey.bind({"ctrl", "alt"}, "F", function()
app_toggle({"Finder"})
end)
-- Fork {{{3
--hs.hotkey.bind({"cmd", "alt", "ctrl"}, "G", function()
-- app_toggle({"Fork"})
--end)
-- Mail {{{3
hs.hotkey.bind({"cmd", "alt"}, "M", function()
app_toggle({"Mail"})
end)
-- Calendar {{{3
hs.hotkey.bind({"cmd", "alt"}, "C", function()
app_toggle(APPS["calendar"])
end)
-- Notes {{{3
hs.hotkey.bind({"cmd", "alt"}, "N", function()
app_toggle({"Notes"})
end)
-- Reminders {{{3
hs.hotkey.bind({"cmd", "alt"}, "R", function()
app_toggle({"Reminders"})
end)
-- Reminders Alt {{{3
hs.hotkey.bind({"ctrl", "alt"}, "R", function()
app_toggle({"Reminders"})
end)
-- Browser {{{3
hs.hotkey.bind({"cmd", "alt"}, "B", (function()
local last_browser = nil
return function()
last_browser = app_pick(last_browser and utils.copy(last_browser) or nil, APPS["browser"])
end
end)())
-- Git
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "G", function()
app_toggle({"Fork"})
end)
-- Music {{{3
hs.hotkey.bind({"cmd", "alt"}, "U", (function()
local last_music = nil
app_listen(function(name, event, app)
if app and event == hs.application.watcher.activated then
local match = app_one_of(app, APPS["music"])
if match then
last_music = match
end
end
end)
return function()
last_music = app_pick(last_music and utils.copy(last_music) or ni, APPS["music"], true)
end
end)())
-- Chat {{{3
hs.hotkey.bind({"cmd", "alt"}, "S", (function()
local last_chat = nil
return function()
last_chat = app_pick(last_chat and utils.copy(last_chat) or nil, APPS["chats"])
end
end)())
-- Terminal {{{3
hs.hotkey.bind({"cmd"}, "\\", function()
app_toggle(APPS["terminal"])
end)
-- Video Calls {{{3
hs.hotkey.bind({"cmd", "alt"}, "Z", function()
app_toggle(APPS["videocalls"])
end)
-- Messenger {{{3
hs.hotkey.bind({"cmd", "alt"}, "T", (function()
local last_messenger = nil
app_listen(function(name, event, app)
if app and event == hs.application.watcher.activated then
local match = app_one_of(app, APPS["messengers"])
if match then
last_messenger = match
end
end
end)
return function()
last_messenger = app_pick(last_messenger and utils.copy(last_messenger) or nil, APPS["messengers"], true)
end
end)())