diff options
Diffstat (limited to 'config/hammerspoon/dotlib.lua')
| -rw-r--r-- | config/hammerspoon/dotlib.lua | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/config/hammerspoon/dotlib.lua b/config/hammerspoon/dotlib.lua new file mode 100644 index 0000000..15fe0b2 --- /dev/null +++ b/config/hammerspoon/dotlib.lua @@ -0,0 +1,139 @@ +-- Helpers shared by init.lua and a machine's own ~/.config/hammerspoon/ +-- local.lua, which reach them with `require("dotlib")`. +-- +-- They used to be locals of init.lua, which meant a local.lua -- a separate +-- chunk -- could not see them and had to copy whatever it needed. Hammerspoon +-- puts the config directory first on package.path (its Contents/Resources/ +-- setup.lua), and init.lua prepends it again from its own location, so this +-- file is found wherever the config is linked. +-- +-- link/hammerspoon.sh links this file next to init.lua. Anything added here is +-- part of what a local.lua may call, so keep the names stable. + +local M = {} + +-- Bind a key. Argument order is the one init.lua has always used: key first, +-- then the modifier table, then the function. +function M.hotkey(key, mods, fn) + hs.hotkey.bind(mods, key, fn) +end + +-- The modifier combinations these configs bind against. +M.mods = { + cs = { "ctrl", "shift" }, + ca = { "ctrl", "alt" }, + csa = { "ctrl", "shift", "alt" }, + cag = { "ctrl", "alt", "cmd" }, + hyp = { "ctrl", "alt", "cmd", "shift" }, + spc = "space", +} + +-- Focus an app. Given several names, the first one installed wins, so one +-- binding can cover machines that have different apps for the same job. +function M.focus(...) + local appNames = { ... } + local selectedAppName = nil + + -- If only one argument is provided, use it directly without searching + if #appNames == 1 then + selectedAppName = appNames[1] + else + -- For multiple arguments, find the first app that exists on disk + local appDirs = { "/Applications/", os.getenv("HOME") .. "/Applications/" } + for _, appName in ipairs(appNames) do + for _, dir in ipairs(appDirs) do + local path = dir .. appName .. ".app" + if hs.fs.attributes(path) then + selectedAppName = appName + break + end + end + if selectedAppName then break end + end + if not selectedAppName then + print("No apps found: " .. table.concat(appNames, ", ")) + end + end + + return function() + if selectedAppName then + print('Opening ' .. selectedAppName) + hs.application.launchOrFocus(selectedAppName) + else + print("No apps found: " .. table.concat(appNames, ", ")) + end + end +end + +-- Run a shell command. +function M.execute(cmd) + return function() + hs.execute(cmd, true) + end +end + +-- Type today's date. +function M.type_current_date() + local clock = os.date('%Y-%m-%d') + hs.eventtap.keyStrokes(clock) +end + +-- Type ids from random-id.ts, through a login shell so it is on PATH. +function M.type_random_ids(count) + local cmd = string.format('/bin/bash --login -c "random-id.ts %s"', count) + return function() + local out = hs.execute(cmd, true) + if count == 1 then + out = out:gsub("%s+$", "") + end + hs.eventtap.keyStrokes(out) + end +end + +-- Type a string. With min_delay/max_delay it is typed character by character, +-- at a randomised pace, for a screencast; without, all at once. +function M.type_text(text, options) + return function() + if options and (options.min_delay or options.max_delay) then + -- Type with delays between characters + local min_delay = options.min_delay or 0 + local max_delay = options.max_delay or min_delay + + local chars = {} + for char in text:gmatch(".") do + table.insert(chars, char) + end + + local function typeNextChar(index) + if index > #chars then return end + + -- Handle newlines specially by sending return key + if chars[index] == "\n" then + hs.eventtap.keyStroke({}, "return") + else + hs.eventtap.keyStrokes(chars[index]) + end + + if index < #chars then + local delay = min_delay + if max_delay > min_delay then + -- Use squared random for skewed distribution + -- Most delays will be near min_delay, higher delays are rarer + local rand_squared = math.random() ^ 2 + delay = min_delay + rand_squared * (max_delay - min_delay) + end + hs.timer.doAfter(delay, function() + typeNextChar(index + 1) + end) + end + end + + typeNextChar(1) + else + -- Fast typing without delays + hs.eventtap.keyStrokes(text) + end + end +end + +return M |
