I replaced 10 years of Keyboard Maestro with one Luau script

genesishash1 pts0 comments

I Replaced 10 Years of Keyboard Maestro with One Rebind Script | Rebind

All posts<br>I've been a Keyboard Maestro user for ten years. Paid for every major upgrade. Built up a Global Macro Group with seventeen macros that my hands depend on: browser history on ⌘J/⌘L, tab switching on ⌘S/⌘D, hard refresh, dev tools, window sizing, a terminal toggle on backtick, forward delete, a text expansion. The kind of setup you stop thinking about because it's part of how you type.

Last week I replaced all of it with one Rebind script. I didn't even write the script — RebindGPT did.

The before state

If you've maintained a serious Keyboard Maestro setup, you know what it looks like. Every remap is a macro. Every macro is a stack of click-configured conditions: If any of the following are true: Google Chrome is at the front, Chromium is at the front... execute: Type the ⌘] Keystroke. Otherwise: No Action. Multiply that by every browser you use, then by every shortcut, then again for the apps that need a different keystroke for the same action.

My "browser history forward" macro alone was two condition blocks deep — one for Chrome-family browsers (⌘]) and one for Firefox-family (⌘→), each enumerating applications one dropdown at a time. Seventeen macros of that. It works, and Keyboard Maestro is genuinely good software — but the setup is opaque, it lives in a proprietary editor, you can't diff it, you can't grep it, and you rebuild it dropdown by dropdown on every new machine.

The old setup: one of seventeen macros — two condition blocks and four app dropdowns just to remap ⌘L per browser family.

The after state

One Luau file. My entire Global Macro Group, expressed as code I can read:

remap("J", "LeftBrace", function() return cmd() and CHROMISH[front()] end) -- Chrome → ⌘[<br>remap("J", "Left", function() return cmd() and FIREFOXISH[front()] end) -- Firefox → ⌘←<br>remap("L", "RightBrace", function() return cmd() and CHROMISH[front()] end) -- Chrome → ⌘]<br>remap("L", "Right", function() return cmd() and FIREFOXISH[front()] end) -- Firefox → ⌘→

That's the entire "browser history" pair — the thing that took four condition blocks across two macros in the old setup. App families are defined once as plain tables and reused everywhere:

BROWSERS = set { "Google Chrome", "Chromium", "Brave Browser", "Firefox", "LibreWolf", "Safari" }<br>ARROW_TABS = set { "Google Chrome", "Chromium", "Brave Browser", "Firefox", "LibreWolf", "Safari", "Code", "Visual Studio Code", "Cursor" }<br>BRACKET_TABS = set { "Ghostty", "Terminal", "Hyper", "iTerm2", "iTerm", "MacVim", term }

Add a browser to the table, every remap picks it up. Try doing that across seventeen macros of dropdowns.

I didn't write this script

Here's the part that actually surprised me. I described my Keyboard Maestro setup to RebindGPT — the shortcuts, which apps needed which variants, the terminal toggle behavior — and it wrote the whole thing. The app-family tables, the modifier helpers, even the subtle stuff I would have gotten wrong on the first pass, like waiting for physical modifiers to clear before injecting a chord that conflicts with them:

-- Emit `chord` after physical modifiers clear — used when the chord conflicts<br>-- with held ⌘/⇧ (macOS applies held modifiers to any injected event).<br>local function injectAfterMods(chord)<br>Run(function()<br>while cmd() or shift() do Sleep(5) end<br>HID.Combo(chord)<br>end)<br>end

That's a real macOS injection pitfall — held modifiers merge into synthetic events — and the generated script handled it without being asked.

It didn't stop at writing the script, either. When an early version polled System.Window() too aggressively in a timer, I pasted the runtime's warning into the same chat and RebindGPT diagnosed the tick-budget problem and rewrote the hot path — slower timer, expensive check moved behind the keypress that actually needs it.

The replacement: taky-km.luau running in the Rebind editor at 0µs/tick, with RebindGPT explaining its own performance fix.

The full script

Everything below is what runs on my machine today. The two configurable values (terminal app and the ⌘P text expansion) surface as UI controls in the Rebind panel, so the script itself stays generic:

--[[<br>rebind: name=taky-km<br>rebind: min_sdk=3.2.1<br>rebind: description=Keyboard Maestro parity — global, app-conditional chord remaps, app control, and configurable terminal/hash.<br>--]]

-- ═══════════════════════════════════════════════════════════════════════════════<br>-- Config<br>-- ═══════════════════════════════════════════════════════════════════════════════

local cfg = UI.Schema({<br>terminal = UI.Text("Terminal", {<br>label = "Terminal App",<br>tooltip = "Process name of your terminal (e.g. Terminal, Ghostty, iTerm2, Kitty).",<br>maxLength = 40,<br>}),<br>hash = UI.Text("your-snippet-here", {<br>label = "Hash String",<br>tooltip = "Text inserted by ⌘P (skipped in Cursor / MacVim).",<br>maxLength = 128,<br>}),<br>})

-- ═══════════════════════════════════════════════════════════════════════════════<br>--...

terminal script rebind browser keyboard maestro

Related Articles