Occasional hangs? Now there's an escape hatch and a black box.
Added
- 🆘 Hold-to-restart — if the service ever freezes, press & hold the on-screen indicator ~1.8s: a red ring fills, then it force-restarts (launchd brings it back in ~1s). It also snapshots all thread stacks right before exiting, so the freeze is captured even if you restart immediately.
- 🔬 Freeze watchdog — when something stays stuck in a busy state too long (transcription 45s / recording 180s, configurable), it dumps every thread's stack to
logs/freeze_diagnostics.logfor diagnosis. Diagnostics-only — never auto-restarts. - ⏱️ Real
whisper-clitimeout (WHISPER_SUBPROCESS_TIMEOUT, default 90s) — kills a hung child instead of leaking it for 3 minutes behind the old thread-based wrapper.
Fixed
- The on-screen indicator now receives clicks. The app's
status_bar.pyranAppHelper.runConsoleEventLoop(), which pumps the run loop (animations/callAfter/display all work) but never dispatchesNSAppmouse events to windows — so the hold-to-restart overlay got nothing. Switched toAppHelper.runEventLoop()(activation policy unchanged). Diagnosed empirically: under the console loop, clicks fired neitheracceptsFirstMousenormouseDownunder both Prohibited and Accessory;runEventLoopfixed it.
How it ships
Two kit-owned files (src/utils/freeze_watchdog.py, src/ui/hold_restart.py, MIT) copied in by install.sh; the patcher wires 6 thin hooks into main.py / status_bar.py / local_whisper.py. Verified byte-identical to a known-good install (30 edits + 5 shipped files) and idempotent.
macOS only. Not affiliated with or endorsed by upstream. See CREDITS.md.