Files
hammerspoon/Spoons/Seal.spoon/seal_pasteboard.lua
T
2026-05-14 18:59:23 -04:00

183 lines
4.8 KiB
Lua

--- ==== Seal.plugins.pasteboard ====
---
--- Visual, searchable pasteboard (ie clipboard) history
local obj = {}
obj.__index = obj
obj.__name = "seal_pasteboard"
obj.timer = nil
obj.lastItem = nil
obj.itemBuffer = {}
obj.choices = {}
--- Seal.plugins.pasteboard.historySize
--- Variable
---
--- The number of history items to keep. Defaults to 50
obj.historySize = 50
--- Seal.plugins.pasteboard.saveHistory
--- Variable
---
--- A boolean, true if Seal should automatically load/save clipboard history. Defaults to true
obj.saveHistory = true
--- Seal.plugins.pasteboard.skipUTIs
--- Variable
---
--- An array of UTIs to skip when saving to the history. Defaults to:
--- ```
--- {
--- "de.petermaurer.TransientPasteboardType",
--- "com.typeit4me.clipping",
--- "Pasteboard generator type",
--- "com.agilebits.onepassword",
--- "org.nspasteboard.TransientType",
--- "org.nspasteboard.ConcealedType",
--- "org.nspasteboard.AutoGeneratedType"
--- }
--- ```
obj.skipUTIs = {
"de.petermaurer.TransientPasteboardType",
"com.typeit4me.clipping",
"Pasteboard generator type",
"com.agilebits.onepassword",
"org.nspasteboard.TransientType",
"org.nspasteboard.ConcealedType",
"org.nspasteboard.AutoGeneratedType"
}
function obj:commands()
return {
pb = {
cmd = "pb",
fn = obj.choicesPasteboardCommand,
name = "Pasteboard",
description = "Pasteboard history",
plugin = obj.__name
}
}
end
function obj:bare()
return nil
end
function obj.choicesPasteboardCommand(query)
-- Return the choices that match the query
return hs.fnutils.filter(obj.choices, function(choice)
return string.find(string.lower(choice["text"]), string.lower(query))
end)
end
function obj.pasteboardToChoice(item)
local choice = {}
choice["uuid"] = item["uuid"]
choice["name"] = item["text"]
choice["text"] = item["text"]
choice["kind"] = kind
choice["plugin"] = obj.__name
choice["type"] = "copy"
choice["subText"] = ""
if item["uti"] then
choice["subText"] = item["uti"]
if hs.application.defaultAppForUTI then
local bundleID = hs.application.defaultAppForUTI(item["uti"])
print("Default app for " .. item["uti"] .. " :: " .. (bundleID or "(null)"))
if bundleID then
choice["image"] = hs.image.imageFromAppBundle(bundleID)
end
end
end
if item["dateTime"] then
choice["subText"] = choice["subText"] .. " :: " .. item["dateTime"]
end
return choice
end
function obj.completionCallback(rowInfo)
if rowInfo["type"] == "copy" then
hs.pasteboard.setContents(rowInfo["name"])
end
end
function obj.checkPasteboard()
local pasteboard = hs.pasteboard.getContents()
local shouldSave = false
if pasteboard == nil then
return
end
if (#obj.itemBuffer == 0) or (pasteboard ~= obj.itemBuffer[#obj.itemBuffer]["text"]) then
local currentTypes = hs.pasteboard.allContentTypes()[1]
if currentTypes == nil then
print("ERROR: NO PASTEBOARD CURRENT TYPES. Please file a bug so we can understand this:")
print(hs.inspect(pasteboard))
return
end
for _, aType in pairs(currentTypes) do
for _, uti in pairs(obj.skipUTIs) do
if uti == aType then
return
end
end
end
local item = {}
item["uuid"] = hs.host.uuid()
item["text"] = pasteboard
item["uti"] = currentTypes[1]
item["dateTime"] = os.date()
table.insert(obj.itemBuffer, item)
table.insert(obj.choices, obj.pasteboardToChoice(item))
shouldSave = true
end
if #obj.itemBuffer > obj.historySize then
table.remove(obj.itemBuffer, 1)
table.remove(obj.choices, 1)
shouldSave = true
end
if shouldSave then
obj.save()
end
end
function obj.save()
local json = hs.json.encode(obj.itemBuffer)
local file = io.open(os.getenv("HOME") .. "/.hammerspoon/pasteboard_history.json", "w")
if file then
file:write(json)
file:close()
end
end
function obj.load()
local file = io.open(os.getenv("HOME") .. "/.hammerspoon/pasteboard_history.json", "r")
if file then
local json = hs.json.decode(file:read())
if json then
obj.itemBuffer = json
-- Convert all the items to the choice buffer
for _, v in ipairs(obj.itemBuffer) do
table.insert(obj.choices, obj.pasteboardToChoice(v))
end
end
file:close()
end
end
obj.load()
if obj.timer == nil then
obj.timer = hs.timer.doEvery(1, function() obj.checkPasteboard(obj) end)
obj.timer:start()
end
return obj