-- 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"}, {"Safari"}}, editors = {DEFAULTS["EDITOR"], {"Xcode"}, {"Code", "Visual Studio Code"}, {"IntelliJ IDEA"}}, chats = {{"Microsoft Teams"}, {"Telegram"}}, messengers = {{"Messages"}, {"Telegram"}, {"Signal"}}, music = {DEFAULTS["MUSIC"], {"Music"}}, calendar = {"Calendar"}, terminal = {"iTerm", "iTerm2"}, 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)())