A native macOS menu-bar app that ports the Fast Debate Paste AutoHotkey workflow to the Mac. It copies evidence from a source app (usually a browser), runs the same equation-omission text processing, and sends it straight into a CardMirror doc — by default whichever doc CardMirror designates as the speech doc — all from global hotkeys, without ever leaving the app you're copying from.
It lives in the menu bar only (no Dock icon). Copying from the source app is synthesized keystrokes + the clipboard; delivery into CardMirror goes exclusively through its native integration bridge. CardMirror must be running — if the bridge isn't reachable the action fails with an explanation, and nothing is pasted.
One line in Terminal — no Gatekeeper warnings (curl-downloaded apps carry no quarantine flag), and updates re-run the same line without re-prompting for Accessibility — or just use Check for Updates… in the menu-bar menu (1.8.0+): the app updates itself in place and relaunches, keeping the Accessibility grant:
curl -fsSL https://raw.githubusercontent.com/ant981228/fast-debate-paste-mac/main/install.sh | bashApple Silicon only. The one thing macOS still requires is the one-time Accessibility grant on first launch (System Settings → Privacy & Security → Accessibility) — that permission is what allows synthesized Cmd-C/Cmd-V, and no distribution method can skip it.
Requires the Swift toolchain (Xcode or Command Line Tools — xcode-select --install).
./build.sh # → dist/Fast Debate Paste.app
./build.sh install # also copies it to /ApplicationsThen launch the app. On first run macOS will ask for Accessibility permission (System Settings → Privacy & Security → Accessibility) — this is required so the app can send Cmd-C on your behalf to copy from the source app. Grant it and relaunch. The menu-bar icon (clipboard) exposes every action, the target picker, Help, and config controls.
| Action | Hotkey |
|---|---|
| Select target doc | Ctrl+Shift+W |
| Copy-Paste | Ctrl+Shift+C |
| Copy-Paste (no line breaks) | Ctrl+Shift+V |
| Copy-Paste (no line breaks, no return) | Ctrl+Shift+B |
| Help | Ctrl+Shift+H |
Select target doc pops a list at your cursor: Speech doc (the
default — follows CardMirror's own speech-doc designation) or any open
doc by name. A specific doc stays targeted until you change it; if it
closes, the target resets to the speech doc.
- Copy from the frontmost app (
copyKey, defaultCmd+C). - Process the text:
- a bare number / short
token numberbecomes[EQUATION … OMITTED](the debate convention for omitting equations & figures), - the "no line breaks" variants collapse line breaks into spaces.
- a bare number / short
- Deliver into the target doc, addressed by id over CardMirror's bridge — no window switching, no focus dependence; you stay in the source app. If the bridge isn't reachable the action fails with an explanation — no keystrokes are ever synthesized into CardMirror.
Everything is configurable via JSON at:
~/Library/Application Support/FastDebatePaste/config.json
Use Edit Config… in the menu to open it, then Reload Config to apply
(re-registers hotkeys live). Hotkey strings use cmd, shift, opt,
ctrl joined with +, plus a key — e.g. cmd+shift+c, f10, ctrl+0,
shift+home.
Notable keys to set for your setup:
- the four hotkey bindings, if
Ctrl+Shift+C/V/B/Wcollide with anything on your machine.
The "Copy-Paste Current Header", "Copy URL and Paste", Zotero, and Research Tracker actions from the Windows version are not included — this port is deliberately CardMirror-only and minimal.
The app delivers evidence through CardMirror's native HTTP bridge
instead of synthesizing a paste — sidestepping the keystroke/clipboard
timing fragility entirely and letting CardMirror place the text natively
(correct card_body paragraphs, no stray tags). It still copies from
the source app via keystrokes; only the insert into CardMirror step
goes native.
How it works (CardMirrorClient.swift): discovery uses the shared
cardmirror-bridge handshake directory at
~/Library/Application Support/cardmirror-bridge/ — CardMirror maintains
cardmirror.session.json there (per-launch loopback port + token,
deleted on quit), and this app registers its own identity file
(fast-debate-paste.json) so CardMirror's consent prompt can name it.
Each action reads the session file, GET /docs to list the open docs
(doubling as the liveness probe) and resolve the target — the speech doc
or a doc picked by name — then POST /inserts
{text, role, newParagraph, omitted, target}. The insert lands in the
addressed doc regardless of window focus. The full contract is in
docs/cardmirror-integration-spec.md.
There is no keystroke fallback. CardMirror gates external inserts behind per-app consent, and synthesized keystrokes are invisible to that gate — a silent fallback would bypass a decision the user made on purpose. Every failure (CardMirror not running, consent denied, no editable doc, timeout) is surfaced as a message instead, and nothing is pasted behind CardMirror's back.
Because docs are addressed directly, Fast Debate Paste never activates or focuses CardMirror windows — the only focus change it ever makes is bringing CardMirror forward when its permission prompt needs an answer.
Package.swift SwiftPM manifest
build.sh builds + packages the .app bundle
Sources/FastDebatePaste/
main.swift app bootstrap (accessory / menu-bar only)
CardMirrorClient.swift native HTTP bridge client (discovery→docs→insert)
AppDelegate.swift status item, menu, hotkey wiring, help
Config.swift JSON config model + persistence
AppState.swift shared config/target state + alerts
Keys.swift keycode map + hotkey-string parser
Keyboard.swift CGEvent keystroke synthesis
HotKeyManager.swift Carbon global-hotkey registration
TextProcessor.swift equation-omission + line-break rules
PasteActions.swift the copy → process → paste engine
legacy/ the original Windows AutoHotkey version