https://andrewbaker.ninja/wp-content/themes/twentysixteen/fonts/merriweather-plus-montserrat-plus-inconsolata.css

๐Ÿ‘2views
Shift + Click Your Dock Icon to Cycle App Windows on macOS

If you run multiple Chrome profiles or keep several windows open per app, switching between them on macOS becomes irritating fast. Clicking the Dock icon only brings the app forward. Clicking it again does nothing useful. So you right click, scan the window list, and manually choose the one you want. It breaks flow and adds cognitive drag to something that should be instant.

macOS does not natively cycle through an appโ€™s windows when you click its Dock icon. Keyboard users can press `Command + “ to rotate windows, but mouse first users are left with friction. When you are juggling multiple Chrome accounts, terminals, dashboards, and documents, that friction compounds.

After experimenting with double click detection and Dock zone hacks, the most stable and deterministic solution is simple: hold Shift and click the Dock icon to cycle that appโ€™s windows. No timing tricks. No fragile heuristics. Just an explicit modifier key and a reliable window switch.

What This Does

Normal click activates the app using default macOS behavior. Shift + click activates the app and immediately cycles to the next window. It works with Chrome, Safari, Finder, Terminal, and any application that has multiple windows open. It is compatible with newer macOS versions where Dock behavior and accessibility trees have changed.

One Command Install

Paste this entire block into Terminal. It installs Hammerspoon if needed, writes the configuration, and restarts it.

brew install --cask hammerspoon

mkdir -p ~/.hammerspoon

cat << 'EOF' > ~/.hammerspoon/init.lua
-- Shift + Click a Dock icon to cycle that app's windows
-- Requires: Accessibility + Input Monitoring enabled for Hammerspoon

local function axAttr(el, name)
  local ok, v = pcall(function() return el:attributeValue(name) end)
  if ok then return v end
  return nil
end

local function axParent(el)
  return axAttr(el, "AXParent")
end

local function axRole(el)
  return axAttr(el, "AXRole")
end

local function axSubrole(el)
  return axAttr(el, "AXSubrole")
end

local function axTitle(el)
  return axAttr(el, "AXTitle")
end

local function findDockAppNameAtPoint(x, y)
  local sys = hs.axuielement.systemWideElement()
  local el = sys:elementAtPosition(x, y)
  if not el then return nil end

  local cur = el
  for _ = 1, 30 do
    local sr = axSubrole(cur)
    local r  = axRole(cur)

    if sr == "AXApplicationDockItem" or r == "AXDockItem" then
      local t = axTitle(cur)
      if t and t ~= "" then return t end
    end

    cur = axParent(cur)
    if not cur then break end
  end

  return nil
end

local function cycleAppWindows(app)
  if not app then return end

  local windows = {}
  for _, w in ipairs(app:allWindows()) do
    if w:isStandard() then table.insert(windows, w) end
  end

  if #windows < 2 then
    local w = app:focusedWindow() or app:mainWindow()
    if w then w:focus() end
    return
  end

  local focused = app:focusedWindow()
  local nextIndex = 1

  if focused then
    for i, win in ipairs(windows) do
      if win:id() == focused:id() then
        nextIndex = i + 1
        break
      end
    end
  end

  if nextIndex > #windows then nextIndex = 1 end
  windows[nextIndex]:focus()
end

_G.DockShiftCycleTap = hs.eventtap.new({ hs.eventtap.event.types.leftMouseDown }, function(evt)
  if not evt:getFlags().shift then return false end

  local pos = hs.mouse.absolutePosition()
  local name = findDockAppNameAtPoint(pos.x, pos.y)

  if not name then return false end

  hs.timer.doAfter(0.18, function()
    local app = hs.application.find(name) or hs.application.frontmostApplication()
    cycleAppWindows(app)
  end)

  return false
end)

_G.DockShiftCycleTap:start()
hs.alert.show("Shift+Click Dock window cycling enabled", 0.8)
EOF

killall Hammerspoon 2>/dev/null
open -a Hammerspoon

After running the script, open System Settings, go to Privacy & Security, and enable Hammerspoon under both Accessibility and Input Monitoring. If Input Monitoring is not enabled, mouse click detection will not work.

Why This Is Better

No right clicking. No scanning window lists. No fragile double click timing logic. Just hold Shift and click. The Dock finally behaves like a power tool instead of a static launcher.

Leave a Reply

Your email address will not be published. Required fields are marked *