Configurable hotkey, paste-based injection, overlay above full-screen - #4
Conversation
parrot was unusable on non-Apple keyboards and in terminals. Two independent causes, both places where the docs promised behavior the code never implemented. Hotkey: HotkeyMonitor hardcoded .maskSecondaryFn, so the README's `--hotkey right-option` did nothing. Third-party keyboards handle Fn in firmware and never send it to macOS at all, leaving those users with no working key. Adds a Hotkey enum covering fn plus the left/right variants of option, command, control and shift. Left and right share a single event flag, so each case carries a keycode and the monitor filters on it before reading the flag. Injection: CGEventKeyboardSetUnicodeString is discarded by terminals and Electron apps, which read key events directly. Adds the inject_mode the architecture doc already specified, defaulting to paste: stage the text on the pasteboard, send Cmd+V, restore the previous contents. The snapshot preserves every representation of every item so dictating over a copied image or file does not destroy it, and a pending restore suppresses re-snapshotting so two injections inside the settle window cannot leave the first transcript on the pasteboard permanently. Overlay: the pill sat at .statusBar, below full-screen windows, so it was invisible whenever the frontmost app was full-screen. Raises it to .screenSaver, repositions on every show since the active screen and Space can change between recordings, picks the screen under the pointer rather than NSScreen.main (which follows keyboard focus and is nil for a background agent), and generation-guards the dismiss timer so a recording started inside the 0.18s animation is not ordered back out by the previous hide. Install: the LaunchAgent hardcoded its arguments, so launch-at-login silently reverted both settings.
Default inject mode is now paste (⌘V with full pasteboard snapshot/restore) — terminals and Electron apps drop synthesized unicode key events but all handle paste. --inject-mode type-unicode keeps the old behavior. Hotkey/overlay parts of digimata#4 were skipped; they are superseded by the digimata#7 merge.
|
I would love to see this get merged. The README.md on main already reports it as an available option: But it's not there because this PR is still open :) My use case is a Mac Mini with a mechanical keyboard that has no Fn key. |
|
Implemented in the current Parrot working tree:\n\n- Added configurable hotkey support with side-specific modifier matching.\n- Added paste and type-unicode injection modes. Paste preserves all clipboard items and representations, protects against overlapping restores, user clipboard changes, focus drift, secure fields, and unknown focus.\n- Raised the recording overlay above full-screen apps and repositioned it using the screen under the pointer.\n- Added |
Three things the docs promise that the code never implemented. Each one makes parrot unusable on some setup.
--hotkeyHotkeyMonitorhardcoded.maskSecondaryFn, so the README'sparrot --hotkey right-optiondid nothing.This matters more than a missing flag: non-Apple keyboards handle Fn in firmware. It reshapes the F-row locally and never sends anything to the host, so no application can bind to it. On a Mac mini with a third-party keyboard there is no working key at all — parrot waits forever for an event the hardware cannot produce.
Adds a
Hotkeyenum for fn plus the left/right variants of option, command, control and shift. Left and right share one event flag, so each case carries a keycode and the monitor filters on it before reading the flag.--inject-modeCGEventKeyboardSetUnicodeStringis discarded by terminals and WebKit/Electron-hosted UIs, which read key events directly. Dictation worked in Safari and silently produced nothing in a terminal.Adds the
inject_modefromdocs/architecture.md, defaulting topaste: stage the text, send ⌘V, restore the pasteboard. Two details worth reviewing:Verified in a terminal.
type-unicoderemains available for anyone who wants the pasteboard untouched.Overlay
The pill sat at
.statusBar, below full-screen windows, so it was invisible whenever the frontmost app was full-screen — measured aslayer=25 onscreen=falseduring an active recording. Now.screenSaver.Also repositions on every show, since the active screen and Space change between recordings; picks the screen under the pointer rather than
NSScreen.main, which follows keyboard focus and is nil for a background agent; and generation-guards the dismiss timer so a recording started inside the 0.18s animation isn't ordered back out by the previous hide.Install
The LaunchAgent hardcoded its arguments, so launch-at-login silently reverted both new settings. Easy to miss — it only bites after a reboot.
Default choice worth a second opinion:
pastebriefly touches the clipboard. It's the mode that works everywhere, which is why it's the default, buttype-unicodeas default is a one-line change if you'd rather not touch the pasteboard by default.Tested on macOS 26.5 / M4 with a Logitech keyboard, on
left-option+paste. The other seven modifiers share one code path and a keycode table, but onlyleft-optionwas exercised on hardware.🤖 Generated with Claude Code