Category Archives: Hammerspoon
how to add a window switcher
command tab
Application switcher, you might enjoy adding a window switcher to your list of keyboard hotkeys. The window switcher allows you to jump between different windows, both those of other apps and the same app with a hotkey like option tab,
which sits nicely next to command tab
in my muscle memory!
This is particularly useful if you have a couple of windows open in several applications, and it is much faster and neater than first using command tab
and then command backtick
to cycle through an app’s windows. Another advantage here is that the window switcher will include full screen and non-full screen windows in multiple spaces, which command backtick
typically does not handle well, something I find particularly frustrating when using Xcode.
Adding a window switcher is easy and doesn’t require any hacking. It does require Hammerspoon, however. But if you’re a regular reader of this blog, you’ll already have installed Hammerspoon after reading my earlier posts on it here and here and here. đ
With Hammerspoon up and running, adding the window switcher is just a case of cutting and pasting some code into your config file, saving it then reloading. You can use the default code in hs.window.switcher docs or use mine below. The default code is a bit ugly for my liking. Instead, I use the code below, which sets up the switcher’s ui as seen in the screenshots here with option tab
and option-shift tab
for shortcuts, but you can modify the appearance to suit your taste. As ever, the Hammerspoon docs are wonderfully clear and easy to follow (take a lesson, Apple!).
-- set up your windowfilter
switcher = hs.window.switcher.new() -- default windowfilter: only visible windows, all Spaces
switcher.ui.highlightColor = {0.4,0.4,0.5,0.8}
switcher.ui.thumbnailSize = 112
switcher.ui.selectedThumbnailSize = 284
switcher.ui.backgroundColor = {0.3, 0.3, 0.3, 0.5}
switcher.ui.fontName = 'System'
switcher.ui.textSize = 14
switcher.ui.showSelectedTitle = false
-- bind to hotkeys; WARNING: at least one modifier key is required!
hs.hotkey.bind("alt","tab",function()switcher:next()end)
hs.hotkey.bind("alt-shift","tab",function()switcher:previous()end)
--EOF
Adjust values such as shortcut bindings as you please, and that’s it. For two minutes work, you just added a very useful window switcher to macOS!
Enjoy! đ
how to display wifi name (ssid) in menu bar

If youâre one for visiting coffee shops and other places with public networks, you might have an interest in todayâs Hammerspoon tip.
This little code added to your config file will display the name (SSID) of the currently connected Wifi network in your menu bar. Whenever the Wifi network changes, the name will automatically update so you can always see at a glance that youâre connected to the Wifi network that you think you are.
Playing around with that unusually long number (between 2147483644 and âŚ647) will move the SSID name along the icon bar. Set it to the default â1000â if you want macOS to decide where it should be.
wifiWatcher = nil
function ssidChanged()
local wifiName = hs.wifi.currentNetwork()
if wifiName then
wifiMenu:setTitle(wifiName)
else
wifiMenu:setTitle("Wifi OFF")
end
end
wifiMenu = hs.menubar.newWithPriority(2147483645)
ssidChanged()
wifiWatcher = hs.wifi.watcher.new(ssidChanged):start()

Enjoy! đ
Further Reading
More fun with Hammerspoon
Get automated with Hammerspoon
more fun with Hammerspoon

For anyone using Hammerspoon and folder âwatchersâ to alert them to changes to a given folder, Iâve updated the watcher function I wrote about here
The problem with the simple alert I demonstrated last time is that it only hangs around for a second or two (much less than a Folder Action alert, which takes a couple of minutes to time out). In this updated function, it now also writes a list of the file changes to a (by default) file on the Desktop. The write is an append: if the file doesnât exist it will create it before writing; if it does exist, it will append the latest changes and date to the file. This way, even if you miss the alert, youâll always have a record of what files have been added, deleted or modified in your watched folder.
In this example, the folder being watched is ~/Library/LaunchAgents since we want to be aware of any adware, malware or other unsavoury processes being surreptitiously added by, for example, apps we download from the internet. Although there are, of course, many legitimate reasons for apps placing items in here, this folder is prime real estate for attackers as it is one of the locations that can launch processes at log in time without the userâs interaction (or knowledge).
Hereâs the code, also available from my pastebin here. A code walkthrough follows.
function writeStringToFileWithMode(aString, aPath, aMode)
local this_file
this_file = io.open(aPath, aMode)
this_file:write(aString)
this_file:close()
end
function myFolderWatch(files)
local str = "Launch Agents folder was modified on " .. os.date() .. " :\n\t"
local this_path = os.getenv("HOME") .. "/Desktop/LaunchFolderModified.txt"
local ignore = "DS_Store"
local count = 0
for _,file in pairs (files) do
count = count + 1
i = string.find(file, ignore)
if not i then
str = str .. file .. "\n\t"
else
if count == 1 then
str = "\n"
end
end
end
str = str .. "\n"
writeStringToFileWithMode(str, this_path, "a")
if string.len(str) > 2 then
hs.alert.show("Launch Agents folder was modified.")
end
end
local aWatcher = hs.pathwatcher.new(os.getenv("HOME") .. "/Library/LaunchAgents/", myFolderWatch):start()
Code walkthrough
The first function, âwriteStringToFileWithModeâ is just a convenience function. Hopefully the clue is in the name. The âaModeâ parameter is either âwâ for write(over) or âaâ for write(append).
The myFolderWatch function starts off by declaring some local variables.
âstrâ includes the initial line that we want to write to our output file and interpolates the time of the change by calling os.date().
âthis_pathâ defines the path that we want to write our list of file names too.
The â .. â in both these assignments is a string concatenator (i.e., like â&â in AppleScript or âstringByAppendingStringâ in Obj-C).
âignoreâ is a string that will help us to exclude .DS_Store files from triggering the alert or appearing in the list of file changes.
The âcountâ variable is an internal var we need in order to eliminate .DS_Store changes when itâs the only change. Lua doesnât have an easy way to count entries in tables, so we bascially iterate a variable each time through the loop to achieve the same effect.
After that, we have the âforâ loop. For loops in Lua are weird (at least for me), as youâll see that they have this structure for a,b in pair (aPair)
. I wonât go into why, other than to say its a result of Luaâs table data structure. The '_'
here is just a dummy variable for the first parameter. The âfilesâ in parentheses are the list of file names (not file specifiers, you AppleScripters!) that were added, deleted, or modified in the watched folder.
The loop begins by incrementing the count, then checks for the ignore file (.DS_Store). If the ignore file is not found, we then append the filename to the str variable.
If it is found, we check the count. If the count is â1â (i.e., the only change was .DS_Store) we discard the entire str var and replace it with new line character. If the count is more than 1 we donât do anything to âstrâ. We just ignore adding anything to it all.
At the end of the for loop we add another new line to the string just so our outputted text file looks nice and neat.
Then we call the write function mentioned above, passing âaâ (append) for the mode.
Finally, we fire the UI alert on the condition that the string has got more than 2 characters in it (if it didnât it was just the â\n\nâ string from ignoring the DS.Store file).
After the function definition, the aWatcher variable sets up the watcher on the path we want to observe and tells the watcher to start monitoring. It tells the watcher to call our myFolderWatch function when anything happens.
Deploying the code
After editing the config file, remember thereâs two steps: i. save the file and ii. choose âReload Configâ from the Hammerspoon menu.
Further Reading:
More details are available about Hammerspoon from the official site here.
My beginners guide with the original simple watcher function is here.