Created
February 12, 2020 11:45
-
-
Save mediafinger/86f08583b67b22825c8e8d0a50f9bb42 to your computer and use it in GitHub Desktop.
Metakey setup macOS / use CAPS LOCK to open or switch to apps with Hammerspoon and Karabiner
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- A global variable for the Hyper Mode | |
hyper = hs.hotkey.modal.new({}, 'F17') | |
-- Enter Hyper Mode when F18 (Hyper/Capslock) is pressed | |
function enterHyperMode() | |
hyper.triggered = false | |
hyper:enter() | |
end | |
-- Leave Hyper Mode when F18 (Hyper/Capslock) is pressed, | |
-- send ESCAPE if no other keys are pressed. | |
function exitHyperMode() | |
hyper:exit() | |
if not hyper.triggered then | |
hs.eventtap.keyStroke({}, 'ESCAPE') | |
end | |
end | |
-- Bind the Hyper key | |
f18 = hs.hotkey.bind({}, 'F18', enterHyperMode, exitHyperMode) | |
-- Setup shortcuts, press "CAPS LOCK + X" to activate one | |
-- | |
hyper:bind({}, "R", function() | |
hs.reload() | |
end) | |
hs.alert.show("Config Re-loaded") | |
-- Lock the Screen | |
hyper:bind({}, "L", function() | |
hs.caffeinate.lockScreen() | |
end) | |
function forceLaunchOrFocus(appName) | |
-- first focus with hammerspoon | |
hs.application.launchOrFocus(appName) | |
-- clear timer if exists | |
if ext.cache.launchTimer then ext.cache.launchTimer:stop() end | |
-- wait 500ms for window to appear and try hard to show the window | |
ext.cache.launchTimer = hs.timer.doAfter(0.5, function() | |
local frontmostApp = hs.application.frontmostApplication() | |
local frontmostWindows = hs.fnutils.filter(frontmostApp:allWindows(), function(win) return win:isStandard() end) | |
-- break if this app is not frontmost (when/why?) | |
if frontmostApp:title() ~= appName then | |
print('Expected app in front: ' .. appName .. ' got: ' .. frontmostApp:title()) | |
return | |
end | |
if #frontmostWindows == 0 then | |
-- check if there's app name in window menu (Calendar, Messages, etc...) | |
if frontmostApp:findMenuItem({ 'Window', appName }) then | |
-- select it, usually moves to space with this window | |
frontmostApp:selectMenuItem({ 'Window', appName }) | |
else | |
-- otherwise send cmd-n to create new window | |
hs.eventtap.keyStroke({ 'cmd' }, 'n') | |
end | |
end | |
end) | |
end | |
-- smart app launch or focus or cycle windows | |
function smartLaunchOrFocus(launchApps) | |
local frontmostWindow = hs.window.frontmostWindow() | |
local runningApps = hs.application.runningApplications() | |
local runningWindows = {} | |
-- filter running applications by apps array | |
local runningApps = hs.fnutils.map(launchApps, function(launchApp) | |
return hs.application.get(launchApp) | |
end) | |
-- create table of sorted windows per application | |
hs.fnutils.each(runningApps, function(runningApp) | |
local standardWindows = hs.fnutils.filter(runningApp:allWindows(), function(win) | |
return win:isStandard() | |
end) | |
table.sort(standardWindows, function(a, b) return a:id() < b:id() end) | |
runningWindows = standardWindows | |
end) | |
if #runningApps == 0 then | |
-- if no apps are running then launch first one in list | |
forceLaunchOrFocus(launchApps[1]) | |
elseif #runningWindows == 0 then | |
-- if some apps are running, but no windows - force create one | |
forceLaunchOrFocus(runningApps[1]:title()) | |
else | |
-- check if one of windows is already focused | |
local currentIndex = hs.fnutils.indexOf(runningWindows, frontmostWindow) | |
if not currentIndex then | |
-- if none of them is selected focus the first one | |
runningWindows[1]:focus() | |
else | |
-- otherwise cycle through all the windows | |
local newIndex = currentIndex + 1 | |
if newIndex > #runningWindows then newIndex = 1 end | |
runningWindows[newIndex]:focus() | |
end | |
end | |
end | |
hyper:bind({}, "A", function() | |
smartLaunchOrFocus({"Atom"}) | |
end) | |
hyper:bind({}, "C", function() | |
smartLaunchOrFocus({"Google Chrome"}) | |
end) | |
hyper:bind({}, "E", function() | |
hs.application.launchOrFocus("ExpressVPN") | |
end) | |
hyper:bind({}, "F", function() | |
hs.application.launchOrFocus("Firefox") | |
end) | |
hyper:bind({}, "G", function() | |
smartLaunchOrFocus({"GitX"}) | |
end) | |
hyper:bind({}, "H", function() | |
hs.application.launchOrFocus("HTTP Toolkit") | |
end) | |
hyper:bind({}, "T", function() | |
smartLaunchOrFocus({"iTerm"}) | |
end) | |
hyper:bind({}, "P", function() | |
hs.application.launchOrFocus("Postman") | |
end) | |
hyper:bind({}, "Q", function() | |
hs.application.launchOrFocus("Qbserve") | |
end) | |
hyper:bind({}, "S", function() | |
hs.application.launchOrFocus("Slack") | |
end) | |
hyper:bind({}, "Z", function() | |
hs.application.launchOrFocus("zoom.us") | |
end) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For this hammerspoon config to work, you have to map CAPS LOCK to F18 with Karabiner-Elements.
http://www.hammerspoon.org/
https://github.com/pqrs-org/Karabiner-Elements