[hammerspoon] rewrite quitter

main
Alpha Chen 6 days ago
parent 41bc4d6be1
commit 7c90a58c36
Signed by: alpha
SSH Key Fingerprint: SHA256:3fOT8fiYQG/aK9ntivV3Bqtg8AYQ7q4nV6ZgihOA20g

@ -1,151 +0,0 @@
--- === Quitter ===
--
-- Quits apps if they have not been used since a specified amount of time.
local obj = {}
obj.__index = obj
-- Metadata
obj.name = "Quitter"
obj.version = "0.1"
obj.author = "Alpha Chen <alpha@kejadlen.dev>"
obj.license = "MIT - https://opensource.org/licenses/MIT"
obj.logger = hs.logger.new("quitter", "warning")
obj.lastFocused = {}
obj.state = "stopped"
--- Quitter.quitAppsAfter
--- Variable
--- Table of applications to quit when unused.
obj.quitAppsAfter = {}
--- Quitter:start()
--- Method
--- Start Quitter
---
--- Parameters:
--- * None
function obj:start()
if self.state == "started" then return end
self:reset()
-- Reset if we're waking from sleep
self.watcher = hs.caffeinate.watcher.new(function(event)
if event ~= hs.caffeinate.watcher.systemDidWake then return end
self:reset()
end)
-- Set last focused time for relevant apps
self.windowFilter = hs.window.filter.new(false)
for app, _ in pairs(self.quitAppsAfter) do
self.windowFilter:allowApp(app)
end
self.windowFilter:subscribe(
{hs.window.filter.windowFocused, hs.window.filter.windowUnfocused},
function(window, appName)
local name = window:application():name()
self.lastFocused[name] = os.time()
end)
self.timer = hs.timer.doEvery(60, function()
self:reap()
end):start()
self.state = "started"
return self
end
--- Quitter:stop()
--- Method
--- Stop Quitter
---
--- Parameters:
--- * None
function obj:stop()
if self.state == "stopped" then return end
self.watcher:stop()
self.windowFilter:unsubscribeAll()
self.timer:stop()
self.state = "stopped"
end
--- Quitter:toggle()
--- Method
--- Toggle Quitter
---
--- Parameters:
--- * None
function obj:toggle()
if self.state == "started" then
self:stop()
else
self:start()
end
end
function obj:reset()
hs.fnutils.ieach(hs.application.runningApplications(), function(app)
local name = app:name()
local duration = self.quitAppsAfter[name]
if duration == nil then return false end
self.lastFocused[name] = os.time()
end)
end
function obj:shouldQuit(app)
if app == nil then return false end
-- Don't quit the currently focused app
if app:isFrontmost() then return false end
local duration = self.quitAppsAfter[app:name()]
if duration == nil then return false end
local lastFocused = self.lastFocused[app:name()]
if lastFocused == nil then return false end
self.logger.d("app: " .. app:name() .. " last focused at " .. lastFocused)
return (os.time() - lastFocused) > duration
end
function obj:reap()
self.logger.d("reaping")
local frontmostApp = hs.application.frontmostApplication()
for appName, _ in pairs(self.quitAppsAfter) do
local app = hs.application.get(appName)
if self:shouldQuit(app) then
hs.notify.new(
function() hs.application.open(app:name()) end,
{
title = "Hammerspoon",
informativeText = "Quitting " .. app:name(),
withdrawAfter = 2,
}
):send()
app:kill()
self.lastFocused[app:name()] = nil
end
end
end
function obj:debug()
local lastFocused = hs.fnutils.map(
spoon.Quitter.lastFocused,
function(t)
return os.date("!%Y-%m-%dT%TZ", t)
end)
print(hs.inspect(lastFocused))
end
return obj

@ -16,11 +16,6 @@
(local {: mash : smash : modal-bind} (require :hotkey)) (local {: mash : smash : modal-bind} (require :hotkey))
(local {: chomp : paste : replace-selection : run} (require :utils)) (local {: chomp : paste : replace-selection : run} (require :utils))
;;; quitter
(let [{: start} (require :quitter-v2)]
(start))
;; debugging ;; debugging
; (hotkey.bind mash "d" #(hs.notify.show (: (hs.window.frontmostWindow) :title) "" "")) ; (hotkey.bind mash "d" #(hs.notify.show (: (hs.window.frontmostWindow) :title) "" ""))
@ -47,23 +42,12 @@
:l :l
nil nil
(fn [] (fn []
(with-selection #(.. "[" $1 "]" "(" $2 ")")))]]) (replace-selection #(.. "[" $1 "]" "(" $2 ")")))]])
;; cmd-shift-c: copy current url ;;; quitter
; (let [{: activated : deactivated} application.watcher
; firefox (hotkey.new [:cmd :shift] :c #(eventtap.keyStrokes :yy)) (let [quitter (require :quitter)]
; safari-applescript "tell application \"Safari\" to get URL of front document" (quitter:start))
; safari (hotkey.new [:cmd :shift] :c
; #(let [(_ obj _) (osascript.applescript safari-applescript)]
; (pasteboard.setContents obj)))
; per-app {"Firefox Developer Edition" firefox :Safari safari}
; update-app (fn [name event _app]
; (match [(. per-app name) event]
; [app-config activated] (app-config:enable)
; [app-config deactivated] (app-config:disable)))
; watcher (application.watcher.new update-app)]
; ;; hold onto watcher as a global so it doesn't get GC'ed
; (set _G.per-app-watcher (watcher:start)))
;;; Spoons ;;; Spoons

@ -0,0 +1,51 @@
(local {: application : caffeinate : logger : timer : window} hs)
(local log (logger.new :quitter :info))
(local to-kill {})
(fn kill [app]
(when (not (app:isFrontmost))
(log.i (.. "killing " (app:name)))
(app:kill)))
(fn mark [app]
(when (= (app:kind) 1)
(log.i (.. "marking " (app:name)))
(set (. to-kill (app:bundleID)) (timer.doAfter 300 #(kill app)))))
(fn unmark [win]
(let [app (win:application)
bundle-id (app:bundleID)
t (?. to-kill bundle-id)]
(when (not= t nil)
(log.i (.. "unmarking " (app:name)))
(t:stop)
(set (. to-kill bundle-id) nil))))
(fn on-wake [event]
(when (= event caffeinate.watcher.systemDidWake)
(each [_ app (ipairs (application.find ""))]
(mark app))))
(local cw (caffeinate.watcher.new on-wake))
(local wf (window.filter.new {:Safari false
:Arc false
"Firefox Developer Edition" false
:Ghostty false
:Miniflux false
:Phanpy false
:Obsidian false
:default true}))
(fn start []
(log.i :starting)
(cw:start)
(wf:subscribe {window.filter.windowFocused unmark
window.filter.windowUnfocused #(mark ($1:application))}))
;; use global so this isn't GC'ed
(set _G.quitter {: to-kill})
{: start}
Loading…
Cancel
Save