diff --git a/README.md b/README.md index 02d689f..40da2b5 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,8 @@

macOS 26 or later @@ -50,25 +50,75 @@ +Tap a key, speak, and clean text lands in whatever Mac app has focus — think +the built-in macOS dictation, but fast, accurate, and working everywhere you +can type. + +To _blurt_ is to say something suddenly, without stopping to think — which is +more or less what this app lets you do to your Mac. + +Built entirely native (AppKit + SwiftUI — no Electron, no web views). The whole +pipeline lives in **BlurtEngine**, a dependency-free Swift 6 package: mic +capture, one synchronous `POST` to +[AssemblyAI's Sync STT API](https://www.assemblyai.com), and a clipboard paste +into the focused app. No local models, no upload-then-poll job queue, no +background daemons — audio in, polished text out, one HTTP request per +utterance. + +## Features + +- **Works anywhere you can type** — the transcript is pasted into the focused + app via a synthesized ⌘V, with your prior clipboard contents saved and + restored around it. If the target app quit while you were speaking, the text + stays on the clipboard instead of vanishing. +- **One key, no chords** — dictation is triggered by a single lone modifier + (right ⌘ by default; right ⌥ and `fn` also available). Tap to toggle, hold + for push-to-talk. The event tap swallows nothing: a lone modifier types + nothing anyway, and combos like ⌘C pass through untouched. +- **Polished in one step** — each utterance rides to AssemblyAI's Sync STT API + with a contextual prompt built from the focused app, window, and field, the + text around your cursor, and your own key terms — so the transcript comes + back already cleaned up. No separate LLM pass, no model downloads. +- **Fast** — the model responds in under 100 ms. Blurt pre-warms the HTTPS + connection while you're still speaking and flips to "transcribing" at + key-up, so text lands about as soon as you stop talking. +- **Multilingual** — works in 18 languages, detected automatically. +- **Live feedback** — a floating overlay pill shows a real-time mic level + meter and the pipeline phase; a menu bar indicator mirrors it from anywhere. +- **Actual synth cues** — start and stop can be cued by real Yamaha DX7 or + Roland Juno-106 sounds, or turned off. +- **Guided setup** — a first-run wizard walks through Microphone permission, + Accessibility trust, and your API key; the same window later hosts settings + for the trigger key, key terms, and sound pack. +- **No surprises** — updates are manual (check → download the DMG yourself; + no background auto-updater), and there's no telemetry of any kind. + +## Requirements + +- Apple Silicon Mac, macOS 15+ (macOS 26 recommended — enables the Liquid + Glass UI) +- An [AssemblyAI API key](https://www.assemblyai.com/dashboard/api-keys) + (free tier available) + ## Install 1. [Download **Blurt.dmg**](https://github.com/AssemblyAI/blurt/releases/latest/download/Blurt.dmg). 2. Open the disk image and drag **Blurt.app** into `Applications`. -3. Launch Blurt and follow setup: Microphone, Accessibility, and your [AssemblyAI API key](https://www.assemblyai.com/dashboard/api-keys). -4. Dictate with **right command** by default. Tap to toggle, or hold for push-to-talk. -Blurt needs macOS 26 or later on Apple Silicon, plus an AssemblyAI API key -(free tier available). +## Getting started -## Why Blurt +From a fresh install to dictated text in your editor: -- **Accurate.** Transcription runs on AssemblyAI's most accurate speech-to-text model. -- **Fast.** The model responds in under 100 ms, so text lands about as soon as you stop speaking. -- **Multilingual.** Works in 18 languages. -- **Works anywhere you can type.** Blurt pastes the transcript into the focused Mac app. -- **Polished in one step.** Audio goes to AssemblyAI's Sync STT API and comes back as clean text — no model downloads, no separate cleanup pass. -- **Small native app.** The setup window, overlay, hotkey, and paste flow are built for macOS. -- **Actual synth cues.** Start and stop can be cued by real Yamaha DX7 or Roland Juno-106 sounds, or turned off. +1. **Launch Blurt** — the setup wizard requests Microphone and Accessibility + permissions and asks for your + [AssemblyAI API key](https://www.assemblyai.com/dashboard/api-keys). +2. **Click into any text field** — a document, a chat box, a terminal. +3. **Tap right ⌘ and speak** — the overlay pill shows the live mic level. Tap + again to stop, or hold the key and release for push-to-talk. +4. **Read what you said** — the polished transcript is pasted at your cursor. +5. **Tune it** — open Settings to change the trigger key, add key terms + (names, jargon, product words the model should get right), or pick a synth + sound pack. ## Privacy @@ -84,16 +134,51 @@ that audio. ## Build from source -Blurt is MIT-licensed. [`AGENTS.md`](./AGENTS.md) has the architecture notes and -build workflow. +Blurt is MIT-licensed and needs only Xcode and Homebrew to build: ```bash scripts/bootstrap.sh # install the local toolchain scripts/dev-build.sh # build + install Blurt to /Applications -scripts/check.sh # full repo health check +scripts/check.sh # full repo health check — the same script CI runs +swift test # engine unit tests only +``` + +`dev-build.sh` installs to `/Applications` on purpose: macOS won't register +Accessibility/Input-Monitoring permissions for apps living in build +directories, so the app needs a stable install path to be usable at all. +[`AGENTS.md`](./AGENTS.md) has the architecture notes and full workflow; +[`CONTRIBUTING.md`](./CONTRIBUTING.md) covers how changes land. + +## Architecture + +```text +Sources/BlurtEngine/ Swift 6 package owning the pipeline — no external dependencies + Audio/ MicCapture: fresh AVAudioRecorder per session, 16 kHz mono PCM, + live level meter; DX7/Juno-106 sound packs + STT/ AssemblyAITranscriber: one POST to sync.assemblyai.com/transcribe + (u3-sync-pro) + TranscriptionPrompt contextual priming + Pipeline/ DictationSession actor: press/release/cancel commands, phase + stream, auto-release before the API's recording cap + Hotkey/ DictationKeyGate/Router: pure, unit-tested state machine for the + lone-modifier trigger (tap vs hold vs combo) + Injection/ KeyInjector: save clipboard → paste via synthesized ⌘V → restore + FocusCapture/ Accessibility reads of the focused app/window/field that prime + the transcription prompt + Config/, Update/ Keychain API-key store, key terms, manual release check + +App/Blurt/ AppKit/SwiftUI shell (Xcode project generated by XcodeGen) + AppCoordinator.swift the one place the engine is composed for the real app + Hotkey/ DictationKeyTap: the CGEventTap feeding the engine's key gate + Overlay/, MenuBar/ floating status pill, menu bar dictation indicator + Wizard/ setup wizard + settings window ``` -Want to build your own Swift dictation app from scratch? The pipeline — -mic capture, AssemblyAI Sync transcription, and paste-into-the-focused-app — -is a standalone, dependency-free Swift package you can embed: -[`BLURTENGINE.md`](./BLURTENGINE.md) is the developer guide. +The engine is a standalone package you can embed to build your own dictation +app — mic capture, Sync transcription, and paste-into-the-focused-app behind +three protocol seams, fully stubbed in tests. [`BLURTENGINE.md`](./BLURTENGINE.md) +is the developer guide. + +Latency note: perceived speed is mostly bookkeeping. `press()` warms up the +HTTPS connection and kicks off the focused-field context read without awaiting +either; `release()` claims the "transcribing" state before the recording is +even read back from disk — so the stop cue fires at key-up, not after I/O.