Hammerspoon: инструмент для автоматизации macOS

Если вы давно пользуетесь Mac, у вас наверняка накопился зоопарк утилит: одна управляет окнами, другая переназначает клавиши, третья запускает приложения по хоткею.

Для разных задач я использовал Automator, Shortcuts, Karabiner (очень классная вещь, но у меня с ним на клавиатуре ноутбука была задержка, на внешней клавиатуре все отлично работало) , BetterTouchTool, Raycast, Amphetamine и т.п.

Каждая из них висит в памяти, просит доступ к Accessibility, а еще при каждом обновлении macOS бывает, что что-нибудь перестанет работать. Я через это прошёл — и нашёл выход. Выход этот называется Hammerspoon.

Что такое Hammerspoon

Hammerspoon — это не программа. Это движок автоматизации macOS на Lua, который даёт вам доступ к системным API macOS.

Через Hammerspoon доступны:

  • окна и приложения (hs.window, hs.application)
  • горячие клавиши (hs.hotkey)
  • Wi-Fi и сетевые события (hs.wifi)
  • буфер обмена (hs.pasteboard)
  • уведомления (hs.notify, hs.alert)
  • экраны и их геометрия (hs.screen)
  • аудиоустройства (hs.audiodevice)
  • USB-события (hs.usb)
  • и многое другое — полный список в документации

И все это бесплатно, имеет открытый исходный код, MIT-лицензию. Всё, что вы делаете — это пишете Lua-код в одном файле ~/.hammerspoon/init.lua, и система начинает вести себя так, как вы её запрограммировали.

Спустя некоторое время использования я готов поделиться опытом. В этой статье мы разберём, что такое Hammerspoon и зачем он нужен, как настроить его в реалиях macOS 15 и напишем несколько практических скриптов, которые вы сможете скопировать в свой конфиг уже сегодня.

Установка и первый запуск

Я просто скачиваю zip с GitHub Releases, распаковываю и перетаскиваю приложение в /Applications.

Можно установить и через Homebrew:

brew install --cask hammerspoon

После запуска Hammerspoon попросит дать доступ к Accessibility — это обязательно, без него управление окнами и перехват клавиш работать не будут. Разрешаете в **Системных настройках → Конфиденциальность и безопасность → Универсальный доступ.

Мониторинг ввода (Input Monitoring): необходим для работы глобальных хоткеев и модуля hs.eventtap.

Возможно, по ходу работы, потребуются еще какие-то разрешения.

Ваш главный файл конфигурации живёт по пути ~/.hammerspoon/init.lua.

~/.hammerspoon/init.lua

Создайте этот файл, если его нет.

Если вам как и мне не хочется все делать вручную, просто в верхнем меню нажите на значек приложения и в открывшемся меню можно сделать настройки программы, обновить ее и, для нас это сейчас важно, работать с конфигругацией.

Значок Hammerspoon меню MacOS

Проверим, что всё работает, выберем Open Config (файл init.lua откроется в редакторе по умолчанию), введите туда следующий код:

hs.alert("Hammerspoon работает")

Сохраните файл и в меню Hammerspoon выберите Reload Config. Если вы увидели уведомление — всё готово.

Видим что Hammerspoon работает

Давайте сразу чуть по сложнее сделаем скрипт, по нажатию Ctrl+Alt(Opt)+Cmd +R будет перезагружаться конфигурация:

-- Хоткей для перезагрузки конфига
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "R", function()
    hs.reload()
end)
hs.alert("Hammerspoon config reloaded")

Правда после внесения изменения в init.lua первый раз вам нужно будет перезагрузить конфиг через меню, а дальше попробуйте свой хоткей.

Примеры использования

Давайте перейдём от теории к коду. Все примеры ниже протестированы на macOS 15 Sequoia и используют актуальные API фреймворка.

У вас может не сработать какой-то скрипт полностью, может не работать с какой-то программой или клавишей, такое достаточно часто бывает, приходится гуглить и решать проблему.

1. Управление окнами

Довольно частый сценарий тайлинг окон без мыши.

Добавим хоткеи для раскладки окна используя модуль hs.window. Метод moveToUnit позволяет задать положение окна в процентах от размера экрана.

local hyper = {"alt", "cmd"}

-- Левая половина
hs.hotkey.bind(hyper, "Left", function()
  local win = hs.window.focusedWindow()
  win:moveToUnit(hs.layout.left50)
end)

-- Правая половина
hs.hotkey.bind(hyper, "Right", function()
  local win = hs.window.focusedWindow()
  win:moveToUnit(hs.layout.right50)
end)

-- Центр 60%
hs.hotkey.bind(hyper, "Down", function()
  local win = hs.window.focusedWindow()
  win:moveToUnit({0.2, 0.1, 0.6, 0.8})
end)

-- На весь экран
hs.hotkey.bind(hyper, "Up", function()
  local win = hs.window.focusedWindow()
  win:maximize()
end)

Теперь Ctrl+Alt+Cmd+стрелки управляют окнами лучше, чем любые сторонние программы.

2. Горячие клавиши для запуска приложений

-- Открыть или переключиться на приложение
local function launchOrFocus(appName)
    return function()
        hs.application.launchOrFocus(appName)
    end
end

hs.hotkey.bind(hyper, "T", launchOrFocus("iTerm"))
hs.hotkey.bind(hyper, "E", launchOrFocus("Microsoft Edge"))

launchOrFocus — встроенная функция: если приложение запущено, фокус переходит на него; если нет — запускается.

Чтобы открыть сразу несколько приложений по одному хоткею:

Более сложный сценарий

У меня есть несколько папок с частями проекта которые я часто открываю в терминале, что если сделать горячую клавишу для открытия терминала с выбором папки, без проблем:

local projects = {
    { text = "Backend API", subText = "~/Code/backend", path = "~/Code/backend" },
    { text = "App UI", subText = "~/Code/frontend", path = "~/Code/frontend" },
}

local chooser = hs.chooser.new(function(choice)
    if not choice then return end
    -- Открываем терминал iTerm в нужной директории
    os.execute("open -a 'iTerm' " .. choice.path)
    hs.notify.show("Hammerspoon", "Запуск проекта", choice.text)
end)

chooser:choices(projects)
hs.hotkey.bind(hyper, "P", function() 
    chooser:show() 
end)

hs.chooser рендерит нативный UI, который поддерживает нечёткий поиск (fuzzy search) из коробки. Вы можете подгружать список choices динамически из JSON-файла или базы данных.

4. Переключение раскладки по приложению

macOS позволяет выставить раскладку по умолчанию, но не переключать её автоматически при смене приложения. Hammerspoon это умеет:

local appLayouts = {
    ["Telegram"] = "RussianWin",
    ["Терминал"] = "ABC",
}

hs.application.watcher.new(function(appName, eventType, appObj)
    if eventType == hs.application.watcher.activated then
        local layout = appLayouts[appName]
        if layout then
            hs.keycodes.currentSourceID("com.apple.keylayout." .. layout)
        end
    end
end):start()

Когда вы переключаетесь на Telegram — автоматически встаёт русская раскладка. Открываете терминал — переключается на английскую. Работает тихо, без участия пользователя.

Замечание: идентификаторы раскладок могут отличаться от системы к системе. Свой можно узнать через hs.keycodes.currentSourceID() в консоли Hammerspoon.

Как посмотреть идентификаторы раскладок MacOS в консоли Hammerspoon

Как структурировать конфиг

Очень быстро init.lua превратится в 1000 строк. Лучше сразу сделать структуру:

~/.hammerspoon/
  init.lua
  windows.lua
  hotkeys.lua
  wifi.lua
  screens.lua

В init.lua:

require("windows")
require("hotkeys")
require("wifi")
require("screens")

И вы получаете аккуратную архитектуру, а не кашу.

Автоперезагрузка конфига при изменении файла

Это первое, что стоит добавить — чтобы не перезагружать конфиг вручную каждый раз:

hs.pathwatcher.new(os.getenv("HOME") .. "/.hammerspoon/", function(files)
    local doReload = false
    for _, file in pairs(files) do
        if file:sub(-4) == ".lua" then
            doReload = true
        end
    end
    if doReload then
        hs.reload()
    end
end):start()

hs.alert("Hammerspoon config reloaded")

Теперь при сохранении любого .lua-файла в директории конфига Hammerspoon сам перезагрузится и покажет уведомление.

Spoons: готовые плагины

Помимо кода с нуля, в Hammerspoon есть система плагинов — Spoons. Это готовые модули, которые подключаются одной строкой. Официальный репозиторий: www.hammerspoon.org/Spoons/.

Примеры полезных Spoons:

  • ReloadConfiguration — автоперезагрузка конфига (альтернатива коду выше)
  • WindowHalfsAndThirds — готовые хоткеи для управления окнами
  • ClipboardTool — менеджер истории буфера обмена
  • Caffeine — не давать Mac засыпать
Использование.

Скачайте zip архив, разархивируйте и положите файл с расширением .spoon в ~/.hammerspoon/Spoons/

hs.loadSpoon("SpoonName")
spoon.SpoonName.someMethod()

Описание методов в документации к конкретному Spoon.

Альтернативы и когда они лучше

Hammerspoon — не единственный инструмент, и не всегда лучший выбор.

Karabiner-Elements — если вам нужно переназначение клавиш на низком уровне. Он работает на уровне драйвера клавиатуры, что даёт возможности, недоступные Hammerspoon: например, сделать так, чтобы одиночное нажатие Caps Lock давало Escape, а удержание — Control. Hammerspoon перехватывает события позже и не может делать такие трюки. Бесплатный, открытый.

BetterTouchTool — если нужен GUI и настройка жестов трекпада. Не требует знания кода, настраивается визуально. Имеет встроенное управление окнами, кастомный Touch Bar, жесты мыши. Лицензия Standard обходится в $12 и включает обновления на два года; Lifetime за $24 — бессрочно. Выбирайте BTT, если не хотите писать код вообще.

Raycast — если нужен быстрый launcher с Extensions. Заменяет Alfred и часть функций Hammerspoon для запуска приложений. Бесплатный в базовой версии, есть платный Pro с AI-фичами. Не скриптуется так же гибко, как Hammerspoon, но требует нулевых усилий для старта.

Automator / Shortcuts — встроенные инструменты Apple. Подходят для простых задач и интеграции с iCloud. Ограничены в возможностях, часто ломаются при обновлении macOS.

Можно комбинировать: например, использовать Karabiner для ремаппинга клавиш и Hammerspoon для всего остального. Многие так и делают.

Вывод

Hammerspoon — это инструмент с порогом входа. Первые полчаса уйдут на то, чтобы разобраться с Lua и структурой API. Зато потом у вас в руках окажется почти неограниченный контроль над поведением вашего Mac.

Я рекомендую начать с малого: скопируйте пример с управлением окнами, положите в init.lua, выдайте разрешения и посмотрите, как система отреагирует.

Документация: hammerspoon.org/docs — полная и хорошо структурированная. Getting Started Guide: hammerspoon.org/go. Исходники и issue tracker: github.com/Hammerspoon/hammerspoon.

Хостинг для ваших проектов