\

Hammerspoon

59 points - today at 6:34 PM

Source
  • alexfortin

    today at 8:11 PM

    I use it to enable/disable the wifi when I disconnec/connect the macbook to a specific usb hub with ethernet connection:

      local usbWatcher = hs.usb.watcher.new(function(device)
        if device.productName == "EMEET SmartCam C960" then
          if device.eventType == "added" then
            hs.execute("networksetup -setairportpower en0 off")
            hs.notify.new({title="Wi-Fi", informativeText="Disabled (USB device connected)"}):send()
          elseif device.eventType == "removed" then
            hs.execute("networksetup -setairportpower en0 on")
            hs.notify.new({title="Wi-Fi", informativeText="Re-enabled (USB device removed)"}):send()
          end
        end
      end)
      usbWatcher:start()

    • zdw

      today at 7:55 PM

      I fake a tiling window manager on Mac with Hammerspoon, resizing to fit in specific corners/sizes:

           -- resize based on ratios
          function ratioResize(xr, yr, wr, hr)
            return function ()
              local win = hs.window.focusedWindow()
              win:moveToUnit({x=xr,y=yr,w=wr,h=hr})
            end
          end
      
          -- 4 corners, different sizes
          hs.hotkey.bind({"cmd", "ctrl"}, "w", ratioResize(0,     0, 2/5, 2/3))
          hs.hotkey.bind({"cmd", "ctrl"}, "e", ratioResize(2/5,   0, 3/5, 2/3))
          hs.hotkey.bind({"cmd", "ctrl"}, "s", ratioResize(0,   2/3, 2/5, 1/3))
          hs.hotkey.bind({"cmd", "ctrl"}, "d", ratioResize(2/5, 2/3, 3/5, 1/3))
      
      And to throw windows to other monitors:

          -- send to next screen
          hs.hotkey.bind({"cmd", "ctrl"}, ";", function()
            local win = hs.window.focusedWindow()
            local screen = win:screen()
            local next_screen = screen:next()
      
            win:moveToScreen(next_screen)
          end)

      • overflowy

        today at 8:04 PM

        I use this to remap app keys:

            local appHotkeys = {}
        
            local function remapAppHotkey(appName, fromMods, fromKey, toMods, toKey, delay)
                if not appHotkeys[appName] then
                    appHotkeys[appName] = {}
                end
                local hotkey = hs.hotkey.new(fromMods, fromKey, function()
                    hs.eventtap.keyStroke(toMods, toKey, delay or 0)
                end)
                table.insert(appHotkeys[appName], hotkey)
            end
            
            local appWatcher = hs.application.watcher.new(function(appName, eventType)
                local hotkeys = appHotkeys[appName]
                if not hotkeys then return end
                for _, hotkey in ipairs(hotkeys) do
                    if eventType == hs.application.watcher.activated then
                        hotkey:enable()
                    elseif eventType == hs.application.watcher.deactivated then
                        hotkey:disable()
                    end
                end
            end)
            
            appWatcher:start()
        
            -- Remap app hotkeys
            remapAppHotkey("Finder", { "cmd" }, "q", { "cmd" }, "w", 0.5)
            ... etc ...

        • juancn

          today at 8:02 PM

          I use it to hide Zoom's screen sharing controls so they don't come back when pressing Esc:

              -- Hide Zoom's "share" windows so it doesn't come back on ESC keypress
              local zoomWindow = nil
              local originalFrame = nil
              
              hs.hotkey.bind({"cmd", "ctrl", "alt"}, "H", function()
                print("> trying to hide zoom")
                if not zoomWindow then
                  print(">  looking for window")
                  zoomWindow = hs.window.find("zoom share statusbar window")
                end
              
                if zoomWindow then
                  print(">  found window")
                  if originalFrame then
                    print(">    restoring")
                    zoomWindow:setFrame(originalFrame)
                    originalFrame = nil
                    zoomWindow = nil
                  else
                    print(">    hiding")
                    originalFrame = zoomWindow:frame()
                    local screen = zoomWindow:screen()
                    local frame = zoomWindow:frame()
                    frame.x = screen:frame().w + 99000
                    frame.y = screen:frame().h + 99000
                    zoomWindow:setFrame(frame)
                  end
                else
                  print(">  window not found")
                end
              end)

          • incanus77

            today at 7:34 PM

            Hammerspoon is the glue that holds my Mac together. For a starter list of things to do with this app, a partial list of the things that I'm using it for:

              - Dumping all open Safari tabs to an Obsidian doc
              - Adding 'hyper' (Ctrl-Opt-Cmd) keybinds to pop a new window for:
                - Safari
                - Finder
                - Terminal (Ghostty)
                - VS Code
                - Notes
                - Editing Hammerspoon/AeroSpace/Sketchybar config
                - Reloading Hammerspoon config
                - Reloading Sketchybar
                - Quitting all Dock apps except Finder
                - Screen lock
                - System sleep
                - Opening front Finder folder in VS Code
                - Opening front Safari URL on Archive.today
                - Showing front Safari window tab count
                - Showing front app bundle ID
                - Posting notification about current Music track
                - Controlling my Logi Litra light (various color temps/brightnesses)
                - Starting/stopping a client work timer
              - Tying it to AeroSpace for:
                - Pushing a window to another monitor
                - Performing a two-up window layout
                - Swapping those two windows
                - Closing all other workspace windows
                - Gathering all windows to first workspace
              - Ensuring some background apps stay running if they crash
              - Prompting to unmount disk images if trashed
              - Binding into Skim to jump to specific sections of spec PDFs using terse Markdown URLs

            • swiftcoder

              today at 8:13 PM

              I always confuse "hammerspoon" and "rowhammer"

              • pjm331

                today at 6:50 PM

                here is my entire config

                    hs.hotkey.bind({"ctrl"}, "D", function()
                      hs.grid.show()
                    end)
                
                i've tried all of the other fancy window managers and for me nothing has ever beat the ease of use of just

                (1) ctrl-d to see the grid, (2) type the letter where you want the top left corner of your window to be, (3) type the letter where you want the bottom right corner to be

                window resized

                  • hrmtst93837

                    today at 8:14 PM

                    Neat until you need to sync configs or keep multiple machines in harmony, at which point dotfile headaches stack up with Hammerspoon and Lua. Adding complex logic like window rules, app-specific behavior, or handling monitor changes strips away some of that hotkey simplicity and leads to endless tweaking. Still, for avoiding the mouse, it's one of the few flexible options left on macOS that doesn't feel ancient. Tradeoffs everywhere but nowhere else really compares in control.

                    • elAhmo

                      today at 7:43 PM

                      This is amazing! I have a slightly more elaborate setup that allows me to resize from one or another side, similar to what Apple added recently but with more flexibility, but this is super interesting, thanks for sharing!

                      • stackghost

                        today at 7:25 PM

                        Not that I insert EOFs very often, but does that conflict with CTRL+D in the terminal?

                          • xyzzy_plugh

                            today at 7:28 PM

                            I use EOF all the time to end terminal sessions.

                              • commandertso

                                today at 8:06 PM

                                Great handle, btw.

                            • pjm331

                              today at 7:54 PM

                              yeah the CTRL+D definitely gives me problems from time to time but thus far i have been too lazy to fix it

                      • jjmiv

                        today at 7:16 PM

                        is there a particular reason this was shared?

                        otherwise I'm slowly working on a Spoon that figures out if there is an active meeting in Zoom, Teams, Huddle, Google Meet and will allow for muting, video enable/disable and screen sharing etc

                        • jmcguckin

                          today at 7:56 PM

                          I use it to give me focus-follows-mouse and to have a large circle surrounding the mouse when i move it, to aid finding it.

                          • weitzj

                            today at 7:49 PM

                            I love hammerspoon. That's it :D

                            It's lua, so you can get creative with https://fennel-lang.org/

                            • golem14

                              today at 6:41 PM

                              Has anyone worked on making a config replicating aerospace?

                              Hammerspoon seems like a superset and it’s probably better to just have one, instead of two tools warring about who gets the keypresses?

                                • hirvi74

                                  today at 7:52 PM

                                  What features are you trying to replicate from Aerospace?

                                    • golem14

                                      today at 8:08 PM

                                      Well, a tiling window and workspace manager. But as I am typing this, I’m realizing they hammerspoon can probably do some of the window placement, but maybe not handling workspaces and global state.

                                      I was hoping I could be lazy and ask, and a not-lazy person could give a ready made answer :)

                              • mwagstaff

                                today at 7:44 PM

                                Can't live without Hammerspoon on Mac.

                                Can't live without AutoHotkey on Windows.

                                Thanks to everyone who contributed to both!

                                • trjordan

                                  today at 7:27 PM

                                  I utterly love Hammerspoon.

                                  It's fun to combine with qmk [0], which gives you a bunch more options for hotkeys on your keyboard via layers. I've ended up with a layer where half the keyboard is Hammerspoon shortcuts directly to apps (e.g. go to Slack, to Chrome, etc.) and half of it is in-app shortcuts (like putting cmd-number on the home row, for directly addressing chrome tabs).

                                  Between this and one of the tiling window manager-adjacent tools (I use Sizeup), I can do all my OS-level navigation directly. "Oh I want to go to Slack and go to this DM" is a few keystrokes away, and not dependent on what else I was doing.

                                  [0] https://qmk.fm/

                                  • john-tells-all

                                    today at 6:53 PM

                                    I'd love to have a global "toggle Teams mute" button.

                                      • roxolotl

                                        today at 8:14 PM

                                        ```

                                        hs.loadSpoon("MicMute")

                                        binding = { toggle = { {"ctrl", "alt"}, "m" } }

                                        spoon.MicMute:bindHotkeys(binding)

                                        ```

                                        You'll have to add the MicMute spoon which just mean downloading the zip here, unzipping, and opening the .spoon. https://www.hammerspoon.org/Spoons/MicMute.html

                                        • hirvi74

                                          today at 7:54 PM

                                          What do you mean? Like muting the entire application so no sound comes from Teams or muting yourself while on a call? For the latter, I thought 'Option + Space' worked (or used to)?

                                      • hmokiguess

                                        today at 7:54 PM

                                        what's your favourite spoon?

                                        • hirvi74

                                          today at 7:47 PM

                                          I have fond memories of this app. However, after many years, I have moved on. I am in the process of writing my own replacement for some of the various use cases that Hammerspoon once provided me. Though, Hammerspoon will always be a source of great inspiration.

                                          • rolymath

                                            today at 7:20 PM

                                            Is paperwm jittery for everyone?