NO.85 BLOG

⚙️ RectangleやAmphetamineを吸い込んだHammerspoonについて紹介したい

iTermのホットキー起動を残したくて触り始めたら、他のアプリまで自作luaに吸い込んでいた話。

blog TechMake
READ約8分
SHEETA-85
REVA
⚙️ RectangleやAmphetamineを吸い込んだHammerspoonについて紹介したい PLT-85 · FEATURED 2026.06.22 / 工房

⚙️ RectangleやAmphetamineを吸い込んだHammerspoonについて紹介したい

iTermを使っていた頃、ホットキー一発でターミナルをスッと呼び出せる機能が好きでした。とても手に馴染んでいた。 その後ターミナルをGhostty、さらにcmuxと乗り換えて今に至るのですが、乗り換えるたびにホットキー起動が無いことに困っていました。あと、めちゃ寂しい。

これをなんとか自分で再現できないかと探していて、Hammerspoonに出会いました。

Hammerspoonって?

macOSをLuaで好き勝手にいじれる自動化ツールです。

ホットキー起動とかはそれはそれはもうあっさり書けます。 luaで書くので生成AIとも相性がよく、ちょっとお願いするだけでいい感じになります。 こういうショートカット作って!が対話でできるのは非常に便利。

ということは、これが書けるなら簡単な使い方しかしていない他のアプリも、全部置き換えられるのでは…?と思いました。 ちょうどメニューバーのアイコンが増えすぎて困っていたのもあって(以前、Iceで隠す記事も書きました)、「ちょっとしか使っていないアプリを、巻き取ってみようかな」と。

今回はその「Hammerspoonに吸い込まれていった者たち」を、実際のluaと並べてみます。どれも完全な設定ではないので、「こんな雰囲気で」とAIに渡せば、たぶんすぐ動くと思います。

アプリの呼び出し

最初に書いたのがこのホットキー起動です。

書いてみると、ターミナルに限った話じゃなくて。どのアプリでも同じことができる。 そして、そのアプリ自体に設定がなくても、設定できるのがいいところ。 今は「最前面にいれば引っ込めて、いなければ前に出して最大化する」というトグルにして、cmuxだけでなく頻繁に使うConductorとArcに割り当てています。

local function maximizeAppWindow(app)
local win = app and app:mainWindow()
if win then win:maximize() end -- mainWindow が nil でも落ちないように
end
local function launchOrFocusApp(appName, bundleID)
if bundleID then
hs.application.launchOrFocusByBundleID(bundleID)
else
hs.application.launchOrFocus(appName)
end
end
local function toggleAppMaximized(appName, bundleID)
local app = hs.application.find(appName)
if app then
if app:isFrontmost() then
app:hide() -- 最前面にいるなら引っ込める
else
app:activate()
hs.timer.doAfter(0.1, function() maximizeAppWindow(app) end) -- 前に出して最大化
end
else
launchOrFocusApp(appName, bundleID) -- 起動してなければ起こす
end
end
-- bundleID は自分の環境のもの。hs.application.find(app):bundleID() などで確認できます
hs.hotkey.bind({"ctrl", "alt"}, "m", function() toggleAppMaximized("cmux") end)
hs.hotkey.bind({"ctrl", "alt"}, "c", function() toggleAppMaximized("Conductor", "com.conductor.app") end)
hs.hotkey.bind({"ctrl", "alt"}, "a", function() toggleAppMaximized("Arc", "company.thebrowser.Browser") end)

cmuxとConductorを行き来しながらAIに作業させる、みたいな使い方をしているので、Ctrl+Option+MCtrl+Option+Cの2つを叩いているだけでいい感じです。Spotlightでもいいだろ〜っていう意見もあると思いますが、耳を塞ぎます。

ウィンドウ管理:from Rectangle

次に手をつけたのがウィンドウ管理です。 今まではRectangleを使っていました。不満があったわけじゃないし、無料版の範囲でのみ使ってました。非常に便利なアプリです。 ただ、僕が使っていたのは左右半分と四隅の4分割くらい。高機能なアプリなのに、その程度しか触っていなかったです。

なので今回に関しては、これくらいなら置き換えていいな。となり、以下のように変えました

local function moveWindow(position)
local win = hs.window.focusedWindow()
if not win then return end
local positions = {
left = {0, 0, 0.5, 1}, -- 左半分
right = {0.5, 0, 0.5, 1}, -- 右半分
full = {0, 0, 1, 1}, -- 最大化
}
win:moveToUnit(positions[position])
end
hs.hotkey.bind({"ctrl", "alt"}, "left", function() moveWindow("left") end)
hs.hotkey.bind({"ctrl", "alt"}, "right", function() moveWindow("right") end)
hs.hotkey.bind({"ctrl", "alt"}, "return", function() moveWindow("full") end)

moveToUnitは画面を縦横0〜1の比率で見て、そこにウィンドウをはめてくれる関数です。{0, 0, 0.5, 1}なら「左上から、幅50%・高さ100%」。これにCtrl+Option+U/I/J/Kで四隅の4分割も足して、僕が実際に使う操作はだいたい網羅できました。

そして、アプリをひとつ巻き取るたび、メニューバーのアイコンがひとつ消える。これがいいですね。 便利だから〜というよりも、これが楽しいのではないかとさえ思えてきたりw掃除気分。

離席中もMacは働かせる:from Amphetamine

AIにコードを書かせて放置している間、席を立っても、ノートのフタを閉じても、処理を止めたくない。それだけ。たったそれだけのために、Amphetamineを入れていました。

Hammerspoonのhs.caffeinateでスリープは止められます。ただ、これで止まるのは「放っておくと寝る」まで。フタを閉じるとあっさり寝ます。離席中こそ閉じたいのに、ここが地味な穴でした。

調べると、フタ閉じスリープを切るにはpmset -a disablesleep 1を叩くしかない。でもこれはsudoが要る。離席のたびにパスワードを打つのも変なので、sudoerspmsetだけパスワードなし(NOPASSWD)で通しておく前提にしました。許可するのはこの1コマンドだけ、に絞ったうえで(そもそもAmphetamineみたいな常駐アプリも、フタ閉じ制御は裏で特権を使ってやってるはずなので、それを自分の手でやるだけ、とも言えます)。

そのうえで「離席モード」をCtrl+Option+Lに。押すと画面をロックして、フタを閉じても15分は本体が動き続ける。sudoが通らないPC(NOPASSWD未設定)では、閉じて寝てしまう事故を防ぐために、そもそも発動しないようにしてあります。

local function setLidSleepDisabled(disabled)
-- pmset でフタ閉じスリープを切る。sudo -n(非対話)なので sudoers の NOPASSWD 前提
local _, ok = hs.execute("/usr/bin/sudo -n /usr/bin/pmset -a disablesleep " .. (disabled and "1" or "0"), true)
return ok == true
end
local function startAwayMode(seconds)
if not setLidSleepDisabled(true) then
hs.alert.show("離席モード使用不可: pmset の sudoers 未設定")
return -- 切れないなら発動しない(フタ閉じ→スリープ事故の防止)
end
hs.timer.doAfter(seconds, function() setLidSleepDisabled(false) end) -- 15分で自動復帰
hs.timer.doAfter(0.6, hs.caffeinate.lockScreen) -- 一瞬アラートを見せてからロック
end
hs.hotkey.bind({"ctrl", "alt"}, "l", function() startAwayMode(15 * 60) end)

caffeinateで粘って、最後の一番固いところ(フタ閉じ)だけpmsetに頼る。きれいではないけど、これで「ロックして立ち去る、でもMacは働き続ける」が手に入りました。

おまけ:チートシート

機能を自分で足していけるのは楽しいんですが、ひとつ問題があって。僕は記憶力が無いです。妻にも3歩歩いたら忘れると毎日のように言われる。設定した時点で満足してしまって、肝心の「何のキーに割り当てたか」をすぐに忘れてしまいます。

なので、Ctrl+Option+/で今あるショートカット一覧を画面に出すチートシートを作りました。hs.canvasで半透明のパネルを描いて、登録済みのキーバインドを並べているだけです。

Ctrl+Option+/ で出る自作チートシート

ちょっとだけ凝ったのは、Ctrl+Optionを押しっぱなしにすると0.35秒後に勝手に一覧が出てくるところ。「あれ、何だっけ」と迷ったときに、キーを押したまま固まっていると答えが出る。忘れる前提で作ってあるわけです。

-- Ctrl+Option を押しっぱなしにすると、少し遅れてチートシートが出る
hs.eventtap.new({ hs.eventtap.event.types.flagsChanged }, function(event)
local flags = event:getFlags()
if flags.ctrl and flags.alt and not flags.cmd and not flags.shift then
showCtrlAltCheatsheetAfterDelay() -- 0.35秒後に表示
else
cancelCtrlAltCheatsheet()
end
return false
end):start()

flagsChangedは修飾キーの状態が変わるたびに飛んでくるイベントです。これを見て「今ちょうどCtrl+Optionだけ押されている」を検出しているようです。

Kindle の窓を毎回同じ場所に

最後はおまけのおまけ。

これはアプリの置き換えというより、僕専用の自動化です。

僕はKindleのスクショを撮るとき、ウィンドウ単位ではなく「画面のこの座標範囲」を固定で撮りたい。ヘッダーやUIを切り落として、本文だけをきれいに残したいからです(Shotomaticというツールに渡している)。そうすると、Kindleの窓が毎回同じ位置・同じ大きさにいてくれないと困る。

そこで、Ctrl+Option+Sで今の窓の位置を保存して、Ctrl+Option+Dでそこに戻す、というのを書きました。

-- Ctrl+Opt+S: 今の Kindle ウィンドウの位置・サイズを記録
local frame = win:frame()
local layout = { x = frame.x, y = frame.y, w = frame.w, h = frame.h }
-- ホスト名ごとに json で保存(Macが変わっても困らないように)
-- Ctrl+Opt+D: 記録した位置に戻す
win:setFrame(hs.geometry.rect(layout.x, layout.y, layout.w, layout.h))

座標をホスト名ごとのjsonに保存しているので、デスクトップとノートで画面サイズが違っても、それぞれの定位置を覚えていてくれます。ここまでくると、もう既製アプリで代用するものでもない。完全に、僕の作業専用です。

次にやってみたいこと:環境の変化で動かす

ここまで紹介したものは、全部「自分でホットキーを押して発動する」自動化です。僕が能動的にキーを叩いて、Macが応える。

でもHammerspoonは「自分が何もしていなくても、環境が変わったら動く」ことができるようなのです。例えば、WiFiが切り替わったとき、とか。

これが動けば、「会社のWiFiに繋いだら自動でスリープ防止をオンにする」みたいなことができる。さっき手で押していたショートカットを、環境のほうから勝手にやってくれるわけです。ここはまだ全然開拓できていなくて、研究の余地がありそうだなと思っています。

ちなみにクリップボード履歴も置き換えられそうな筆頭なんですが、これは今のところAlfredで満足しているので、無理に巻き取らなくていいかな、と。なんでもかんでも自分のコードにしようとすると、それはそれで疲れますしね。

まだまだやれることがありそうで、ちょっとしたお掃除好き(兼カスタマイズ好き)におすすめしたい、Hammerspoonでした。

END OF SHEET A-85