Hammerspoon Integration
Hammerspoon is a macOS automation tool that uses Lua scripting. KeyPath ships a Hammerspoon Spoon that lets your scripts react to keyboard layer changes — show an overlay when you enter a window management layer, switch audio devices on a media layer, or anything else Hammerspoon can do.
Install
If you have the KeyPath source checkout, symlink the Spoon:
ln -s /path/to/KeyPath/Integrations/KeyPath.spoon ~/.hammerspoon/Spoons/KeyPath.spoon
Or copy it directly:
cp -r /path/to/KeyPath/Integrations/KeyPath.spoon ~/.hammerspoon/Spoons/
Reload Hammerspoon after installing.
Quick start
Add to your ~/.hammerspoon/init.lua:
hs.loadSpoon("KeyPath")
spoon.KeyPath:onLayerChange(function(layer, previous)
print("Layer: " .. layer .. " (was " .. previous .. ")")
end):start()
Every time you switch layers in KeyPath, your callback fires.
What you can do
Show a notification on layer change
spoon.KeyPath:onLayerChange(function(layer, previous)
if layer ~= "base" then
hs.notify.show("KeyPath", "", "Layer: " .. layer)
end
end)
Window grid on your window layer
spoon.KeyPath:onLayerChange(function(layer)
if layer == "window" then
hs.grid.show()
end
end)
React to service start/stop
spoon.KeyPath:onServiceState(function(state)
hs.notify.show("KeyPath", "", "Service " .. state)
end)
Send actions back to KeyPath
The Spoon can also send actions to KeyPath:
-- Launch an app through KeyPath
spoon.KeyPath:sendAction("launch/Obsidian")
-- Trigger Mission Control
spoon.KeyPath:sendAction("system/mission-control")
Check current state
-- These update automatically as events arrive
print(spoon.KeyPath.currentLayer) -- "nav", "base", etc.
print(spoon.KeyPath.serviceState) -- "running" or "stopped"
API reference
| Method | Description |
|---|---|
:onLayerChange(fn) |
Register callback fn(layer, previous) for layer changes |
:onServiceState(fn) |
Register callback fn(state) for service start/stop |
:start() |
Start listening for KeyPath events |
:stop() |
Stop listening |
:sendAction(uri) |
Send a keypath:// action URI to KeyPath |
.currentLayer |
Most recent layer name (nil until first event) |
.serviceState |
Most recent service state (nil until first event) |
All registration methods return self for chaining.
How it works
KeyPath broadcasts layer changes and service state via macOS Distributed Notifications. The Spoon subscribes using hs.distributednotifications. No network connections, no polling, no configuration — if KeyPath is running, events flow automatically.
You can also listen directly without the Spoon:
hs.distributednotifications.new(function(name, object, userInfo)
print("Layer: " .. userInfo.layer)
end, "com.keypath.layerChanged"):start()
Requirements
- Hammerspoon 0.9.93 or later
- KeyPath with distributed notification support