DeepSeek Harness Remote
DeepSeek Harness Remote 可將手機或另一台電腦與你運行 DeepSeek Harness 的電腦安全連接,隨時隨地繼續你的工作,方便隨時查看進度並繼續工作。
AI 翻譯
Free, open-source voice dictation for macOS. Hold a hotkey, speak, release. Local transcription, optional cleanup with your own API key.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Free, private voice dictation for your Mac. Hold a key, talk, let go, and clean, well-formatted text appears in whatever app you are typing in. Your voice never leaves your Mac.
brew install --cask shantirmizi/tap/utter
Or download the DMG (beta, Apple Silicon, macOS 12+) · Website · Report a problem
Why another dictation app:
Everything it does:
Inspired by voice dictation tools. MIT licensed.
⌃ ⌥ Space) to dictate, ⌃ ⌥ R to dictate and rewrite. Change either in
settings.⌘V, and then puts
your previous clipboard back.brew install --cask shantirmizi/tap/utter
Get the app from https://utter.shantirmizi.com, or straight from the latest release. It is a beta for Apple Silicon Macs (M1 or later) on macOS 12 or later.
When a new version is out, About says so (press Check for updates, or
switch on Check automatically). Press Install and restart: Utter
downloads it, checks its signature, swaps itself for the new version and opens
again, keeping your settings and words. This works however you installed it.
Homebrew users can still run brew upgrade --cask shantirmizi/tap/utter.
Versions before 0.6.2 can't do this yet: download the new one once by hand,
and from then on it updates itself.
The first build takes several minutes, nearly all of it compiling.
xcode-select --installcorepack enable)brew install cmakegit clone https://github.com/ShanTirmizi/utter.git
cd utter
pnpm install
pnpm tauri build
The app is written to src-tauri/target/release/bundle/macos/Utter.app. Drag it
into /Applications and open it.
macOS ties the Microphone and Accessibility permissions to the app's signature, and Gatekeeper checks downloads for Apple's notarisation.
APPLE_SIGNING_IDENTITY to a signing certificate of your own to avoid that.scripts/release.sh, which signs, packages
and notarises; see the comments at its top.Utter opens with a five-step welcome that takes about a minute:
fn, say a sentence, and let go. If you skipped
Accessibility, this step offers ⌃ ⌥ Space instead, which works without it.After that, the window has a sidebar: Home, History and Words, then a
Settings group you can fold away (Hotkeys, Speech, Cleanup, AI provider,
Privacy, General), and About at the foot. The status card above About always
says whether Utter is ready; if it is not, it names the one problem, the
section that fixes it carries a ! (and shows even with Settings folded), and
clicking the card opens it. The window follows the system appearance, or pick
Light or Dark under General, which is also where Utter can open at login
and record from a microphone other than the system default.
Closing the window keeps Utter running in the menu bar. The menu bar icon moves while Utter listens or works and shows a dot when it needs you. Its menu starts with the same status, then Open Utter, Copy Last Dictation, Fix Last Word…, Add a Word…, Undo Learning, About Utter, Check for Updates… and Quit Utter.
| Provider | Key | Model |
|---|---|---|
| Anthropic (Claude) | Yes | Defaults to Claude Haiku |
| OpenAI | Yes | Defaults to GPT-5.4 mini |
| Google Gemini | Yes | Defaults to Gemini 3.5 Flash-Lite |
| Groq | Yes | Defaults to GPT-OSS 20B |
| OpenRouter | Yes | You choose |
| Ollama | No, runs on your Mac | You choose from the models you have pulled |
| Other (OpenAI-compatible) | Yes | You enter the base URL and choose |
Keys are stored in the macOS Keychain and nowhere else, one per provider. They are not written to any file, not logged, and never shown again in the app. A key is sent only to the provider it belongs to. With a small model, a typical dictation costs a fraction of a cent.
To dictate without cleanup, switch Cleanup off on the Cleanup page, or simply do not add a key.
Tidying keeps your words. Sometimes you want the opposite: say it roughly, and
have it written well. Hold the rewrite hotkey (⌃ ⌥ R) instead of the
dictation hotkey and Utter rewrites what you said before inserting it.
Pick the style under Cleanup, and press Test on a sample to see what it does to a sentence. The rewrite hotkey itself is under Hotkeys; turn it off there to switch the feature off.
Select text in any app, hold the rewrite hotkey (⌃ ⌥ R) and say what you
want done to it: "make this more formal", "fix the grammar", "translate this to
Spanish". Utter replaces the selection with the result.
Double-tap the hotkey (two quick presses) and Utter keeps listening. Tap either hotkey once more to finish; the text goes in as usual. A single quick tap does nothing but remind you how to start.
Out of the box you dictate with fn (the globe key) on its own:
To use a key combination instead, click Record new hotkey under Hotkeys. Reset to fn brings fn back.
macOS will not register a lone modifier key as a hotkey, so Utter watches for fn with an event tap. That needs the Accessibility permission (already used for pasting) and no other. The tap looks only at fn going down and up and at whether another key is pressed while fn is held; it never looks at which key.
macOS has its own action for the globe key ("Press 🌐 key to" in Keyboard settings), such as opening the emoji picker, and it runs before any app sees the key. So, by default, while Utter is using fn it sets that option to Do Nothing, and puts your own choice back when you quit, pick another hotkey, or click Give it back now under Hotkeys (which turns this off; Turn it off while Utter runs turns it on again). Your original choice is saved first, so it comes back even after a crash, at the next launch. Or change it yourself in Keyboard settings.
The Words tab holds the names, products and terms that are often misheard, spelled the way you want them. Utter gives the list to the speech model as a hint and to the cleanup model as the correct spellings. Up to 200 entries; a handful of the ones you actually say works best.
The History tab lists your recent dictations. Search them, copy one again, fix a word, forget one, or clear them all.
history.json in Utter's Application Support
folder), readable only by your user account. Never sent anywhere.recordings folder next
to history.json, readable only by your user account, with the text as you
corrected it. They are for trying other speech models and prompts on what
you really said (see "Measuring accuracy" under Development). A recording
is deleted with its dictation, when it is older than the last 50, or all at
once when you switch the setting off. They are never uploaded.| Permission | Needed | Why |
|---|---|---|
| Microphone | Yes | To hear you while the hotkey is held. |
| Accessibility | Recommended | To hear the fn key, to press ⌘V in the app you are typing in, to see the text you selected when you edit by voice, to read names in the window in front while you dictate, and to notice a word you correct right after a paste. Without it, Utter copies the text and you paste it yourself, and you dictate with a key combination instead of fn. |
| Input Monitoring | No | With the fn key, Utter notices only that some other key was pressed while fn is held, never which (see "The fn key"). A key combination is registered with macOS as a system hotkey, so Utter never sees your other keystrokes. |
You can change these under System Settings > Privacy & Security.
If Accessibility is switched on but Utter still says it is not allowed: macOS is matching the switch to an older build of the app. In Utter, under General, click Reset it, then switch Utter on again in System Settings. This happens after every rebuild of an ad-hoc signed build, for the reason given under "Signing, and why it matters".
The Privacy page in Utter sums this up for your own settings.
Most of the wait after you let go used to be the language model. Utter now:
How fast the rest is depends on the model. A small model on your Mac writes about 30 words a second, so a long reply still takes a few seconds when it has to be done at the end. A hosted model such as Groq's is many times faster.
Whatever you dictate, a long message never goes in as one giant paragraph. The model is asked for short paragraphs, a new one for each new point. If it returns one long block anyway, or there is no model and the text is the raw transcription, Utter splits it into paragraphs of a few sentences itself, breaking where a sentence moves on ("So", "Also", "Oh, and"). Code editors and terminals are left alone.
⌘V.pnpm tauri dev # run the app with live reload
pnpm dev # the UI only, in a browser, with a pretend backend
cd src-tauri && cargo test # unit tests
cargo run --example transcribe -- model.bin audio.wav # transcribe a file
cargo run --release --example replay # score models on your recordings
With Keep recordings on, every dictation leaves a WAV file and, once you have finished with the pasted text, what you made of it. The replay example runs those recordings through any speech model and any cleanup model, and scores each combination:
cd src-tauri
cargo run --release --example replay -- --model base.en --model large-v3-turbo-q5_0 --cleanup none --cleanup saved
Each recording is compared with, in order: a reference you wrote by hand, the
text as you corrected it, or the text as pasted if you left it alone (which
favours the settings that wrote it). For hand-written references, put a
labels.tsv next to the recordings: one line per file, the file name, a tab,
and exactly what you said. --dir takes any folder of WAV files instead, so a
fixed test set works too. --cleanup is none, saved (the provider and
model set in the app) or provider/model; the key comes from UTTER_API_KEY
or the app's Keychain entry. --each prints every recording.
For each combination it prints the word error rate (case and punctuation ignored), the words you would still have to change per dictation (exactly as typed), the share that would need no change, and the time taken. Only the cleanup model you pick is sent anything, as in the app.
To test the whole pipeline without speaking, a debug build can take recordings
from disk and process them as if dictated. Results are printed and left on the
clipboard; nothing is pasted and nothing is added to the history. Add
@rewrite to a path to use rewrite mode, or @edit to treat the words as an
instruction for the text in UTTER_SIMULATE_SELECTION.
ffmpeg -i clip.wav -ar 16000 -ac 1 -f f32le clip.f32
UTTER_SIMULATE=clip.f32,clip.f32@rewrite pnpm tauri dev
UTTER_SIMULATE=instruction.f32@edit UTTER_SIMULATE_SELECTION="Text to change." pnpm tauri dev
When run with pnpm tauri dev, macOS attributes the permission prompts to your
terminal app rather than to Utter.
The code is laid out as one file per stage of the pipeline, in
src-tauri/src:
| File | Job |
|---|---|
lib.rs | App setup, the commands the window calls, windows and appearance |
hotkey.rs | Register the two push-to-talk hotkeys |
fnkey.rs, globe.rs | The fn key on its own, and switching off macOS's globe-key action while it is used |
audio.rs | Record the microphone, convert to 16 kHz mono, filter out rumble |
transcribe.rs, models.rs | Run whisper.cpp, download models |
parakeet.rs | The Parakeet engine, through a small Swift helper (in the code, not yet bundled in releases) |
tech.rs | Well-known tech names and how speech models mishear them |
speech.rs | Tell speech from silence and noise, so none of it becomes text |
context.rs | Work out which app is focused and what kind it is |
cleanup.rs | The tidy and rewrite prompts, the two API formats, reply checks |
providers.rs | The list of cleanup providers |
selection.rs | Read the text selected in the focused app |
insert.rs | Clipboard save, paste, restore |
pipeline.rs | Runs the stages in order |
history.rs, recordings.rs | Past dictations, in a file on this Mac, and their recordings when kept |
accuracy.rs, replay.rs | What the user changed after a paste, scoring, and what the replay example needs |
words.rs, learn.rs, screen.rs | Your words and their mishearings, learning from corrections, names on screen |
tray.rs, updates.rs | The menu bar icon and menu, checking for and installing updates |
secrets.rs, settings.rs | Keychain and preferences |
The download page lives in site/ (plain HTML, CSS and a little JavaScript)
and is deployed to Vercel.
The interface is React and hand-written CSS, in src:
| File | Job |
|---|---|
main.tsx | Starts the window, or the pill, and sets light or dark |
api.ts | Typed wrappers around the Rust commands and events |
state.tsx, readiness.ts | What the window knows, and whether Utter is ready |
App.tsx | The sidebar, status card, and the first-run switch |
Welcome.tsx | The first-run flow |
sections/ | One file per section |
Pill.tsx | The status pill |
ui.tsx | Icons, keycaps, switches and other small parts |
hotkey.ts, diff.ts, providerMarks.tsx | Hotkey labels, the word diff in Changes, provider logos |
styles/ | quiet.css (tokens for light and dark, and components) and app.css |
dev-mock.ts | The pretend backend for pnpm dev |
With pnpm dev, open http://localhost:1420 and hold ⌃ ⌥ Space (standing
in for fn, which a browser cannot see) to play a dictation. /#pill shows the status pill and ?welcome the first run; the
notes at the top of dev-mock.ts list the other switches.
More on the design choices is in docs/DESIGN.md.
Bug reports and ideas are welcome through the issue forms. For code, see CONTRIBUTING.md; for security problems, SECURITY.md.
The app and the download page use the system font; no typefaces are bundled. The launch film's footage, music, sound effects and voice were generated with ElevenLabs; its app scenes are drawn from the real interfaces of Utter, Slack, Mail and Claude. The provider marks come from @lobehub/icons-static-svg (MIT) and are shown in one colour; they are trademarks of their owners.
permissions.rs| Check and request macOS permissions |
login.rs | Open at login |