Skip to content

Instantly share code, notes, and snippets.

@vwal
Last active January 6, 2025 12:43
Show Gist options
  • Save vwal/1222b4297f1b865c7bd570c94af8453f to your computer and use it in GitHub Desktop.
Save vwal/1222b4297f1b865c7bd570c94af8453f to your computer and use it in GitHub Desktop.
Hammerspoon QuitEnhancer Spoon script
--- === QuitEnhancer ===
--- Inspired by/based on https://apple.stackexchange.com/a/349766/55498
---
--- Enhances Command-Q behavior by requiring a hold to quit apps, and adds Shift-Command-Q for instant quit.
--- For Finder, closes the frontmost window instead of quitting the app, with the same delay behavior.
--- Place this script in ~/.hammerspoon/Spoons/QuitEnhancer.spoon/init.lua,
--- and call from ~/.hammerspoon/init.lua with:
--- hs.loadSpoon("QuitEnhancer")
--- spoon.QuitEnhancer:start(2) -- Optional parameter for Cmd-Q quit delay in seconds
local obj = {}
obj.__index = obj
-- Metadata
obj.name = "QuitEnhancer"
obj.version = "1.0"
obj.author = "Ville Walveranta"
obj.homepage = "https://github.com/yourusername/hammerspoon-spoons"
obj.license = "MIT - https://opensource.org/licenses/MIT"
-- Internal variables
obj.cmdQTimer = nil
obj.cmdQAlert = nil
obj.cmdQDelay = 2 -- default delay in seconds
function obj:cmdQCleanup()
hs.alert.closeSpecific(self.cmdQAlert)
self.cmdQTimer = nil
self.cmdQAlert = nil
end
function obj:stopCmdQ()
if self.cmdQTimer then
self.cmdQTimer:stop()
self:cmdQCleanup()
hs.alert("quit canceled", 0.5)
end
end
function obj:closeFinder(immediate)
local finder = hs.application.find("Finder")
if finder then
local win = finder:focusedWindow()
if win then
if immediate then
win:close()
else
self.cmdQTimer = hs.timer.doAfter(self.cmdQDelay, function()
win:close()
self:cmdQCleanup()
end)
self.cmdQAlert = hs.alert("hold to close Finder window", true)
end
end
end
end
function obj:startCmdQ()
local app = hs.application.frontmostApplication()
if app:name() == "Finder" then
self:closeFinder(false) -- false means use delay
return
end
self.cmdQTimer = hs.timer.doAfter(self.cmdQDelay, function()
app:kill()
self:cmdQCleanup()
end)
self.cmdQAlert = hs.alert("hold to quit " .. app:name(), true)
end
function obj:immediateQuit()
local app = hs.application.frontmostApplication()
if app:name() == "Finder" then
self:closeFinder(true) -- true means immediate
return
end
app:kill()
end
--- QuitEnhancer:init()
--- Method
--- Initialize the spoon
function obj:init()
return self
end
--- QuitEnhancer:start()
--- Method
--- Start the quit enhancer
---
--- Parameters:
--- * delay - Optional number of seconds to hold Cmd-Q (default 2)
function obj:start(delay)
self.cmdQDelay = delay or 2
-- Bind the hotkeys
self.cmdQ = hs.hotkey.bind({"cmd"}, "q", function()
self:startCmdQ()
end, function()
self:stopCmdQ()
end)
self.shiftCmdQ = hs.hotkey.bind({"cmd", "shift"}, "q", function()
self:immediateQuit()
end)
return self
end
--- QuitEnhancer:stop()
--- Method
--- Stop the quit enhancer and unbind its hotkeys
function obj:stop()
if self.cmdQ then
self.cmdQ:delete()
self.cmdQ = nil
end
if self.shiftCmdQ then
self.shiftCmdQ:delete()
self.shiftCmdQ = nil
end
return self
end
return obj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment