Fork me on GitHub

Project Notes

Hammerspoon

Test driving Hammerspoon for automation on macOS

Notes

Reading posts from Hillel Wayne on his use of (Windows-only) AutoHotkey finally motivated me to search out an equivalent for macOS.

The option that immediately caught my attention was Hammerspoon.

About Hammerspoon

Coincidentally, Hammerspoon just celebrated its 10th anniversary with a 1.0.0 release.

What does it do?

  • uses Lua code to interacts with macOS APIs for applications, windows, mouse pointers, filesystem objects, audio devices, batteries, screens, low-level keyboard/mouse events, clipboards, location services, wifi, and more.
  • allows you to implement custom automations by writing Lua code, or use pre-existing “Spoons”

Installation

  • Download the latest
  • unzip and drag Hammerspoon.app to the Applications folder
  • run Hammerspoon.app for initial launch and to configure preferences

hs_prefs

Getting Started

Edit ~/.hammerspoon/init.lua (also available via “Open Config” from Hammerspoon menu). A first example from the Getting Started Guide simply displays a notification popup when a hotkey combo is pressed:

hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
  hs.alert.show("Hello World!")
end)

Attempting a more involved example. This binds the cmd-shift-L key combo to a function that copies the selected text in the Terminal application, converts it to lowercase, and puts it on the clipboard.

-- Convert currently selected text in the Terminal to lowercase and put on the clipboard
-- Hammerspoon will need accessibility permissions in macOS to control other apps.
hs.hotkey.bind({"cmd", "shift"}, "L", function()
  -- Get the currently focused window
  local focusedWindow = hs.window.focusedWindow()
  local app = focusedWindow:application()

  -- Check if the focused application is Terminal
  if app:name() == "Terminal" then
      -- Simulate Cmd+C to copy the selected text to the clipboard
      hs.eventtap.keyStroke({"cmd"}, "C")

      -- Wait a short time to ensure the clipboard is updated
      hs.timer.usleep(10000)  -- 10 milliseconds

      -- Get the clipboard content
      local selectedText = hs.pasteboard.getContents()

      if selectedText then
          -- Convert the selected text to lowercase
          local lowerText = string.lower(selectedText)

          -- Set the clipboard to the lowercase text
          hs.pasteboard.setContents(lowerText)

          -- Show a notification
          hs.alert.show(lowerText .. " is now in the clipboard")
      end
  else
      hs.alert.show("Focus on Terminal to use this shortcut. Current focus:" .. app:name())
  end
end)

Using a “Spoon”

Let’s try a Spoon. I’m going to install Cherry. Cherry tomato (a tiny Pomodoro) – a Pomodoro Timer for the menubar

To install it:

It needs to be enabled in ~/.hammerspoon/init.lua, e.g:

-- Load the Cherry Spoon with default settings and hotkey (cmd-alt-ctrl-c)
hs.loadSpoon("Cherry")
spoon.Cherry:bindHotkeys()

Then a default 25-minute pomodoro timer can be started with the hotkey or selecting “Start” from the cherry menubar item.

cherry cherry2

Credits and References

About LCK#298 tools
Project Source on GitHub Return to the Project Catalog

This page is a web-friendly rendering of my project notes shared in the LittleCodingKata GitHub repository.

LittleCodingKata is my collection of programming exercises, research and code toys broadly spanning things that relate to programming and software development (languages, frameworks and tools).

These range from the trivial to the complex and serious. Many are inspired by existing work and I'll note credits and references where applicable. The focus is quite scattered, as I variously work on things new and important in the moment, or go back to revisit things from the past.

This is primarily a personal collection for my own edification and learning, but anyone who stumbles by is welcome to borrow, steal or reference the work here. And if you spot errors or issues I'd really appreciate some feedback - create an issue, send me an email or even send a pull-request.