Rocky 2d44423f4c fix(mac): prevent Option release deadlock and HUD layout storm
The hold-to-talk release path called AsyncStream.Continuation.finish()
while holding MacAudioRecorder's NSLock; onTermination re-entered the
same lock on the main thread and froze the app. Finish snapshot sinks
outside the lock and guard the audio tap once stop() begins.

The dictation HUD no longer reassigns its hosting view and forces a
synchronous relayout on every view-model tick; it uses a fixed panel
size and SwiftUI @ObservedObject refresh instead.

Adds OSGKeyboardMacTests with a regression test that fails under the
old lock re-entry pattern.
2026-07-29 16:50:32 +08:00
2026-06-17 23:08:58 +08:00

OSGKeyboard

Speak it. It's typed.

Voice input for iPhone, iPad, and Mac. Speak in any app — polished text lands at your cursor.

Platform Platform Swift Version License

Website · 中文 README · Privacy Policy


Why OSGKeyboard

  • Works everywhere — Messages, Notes, Notion, Cursor, Mail, WeChat — wherever you type
  • Speak, don't edit — tap (iOS) or hold Option (Mac); AI adds punctuation and structure for you
  • On-device by default — local recognition on iOS; optional local models on Mac. Cloud upload only when you opt in
  • Bring your own LLM — built-in polish out of the box, or plug in DeepSeek, OpenAI, Anthropic, OpenRouter, and more
  • Mac global dictation — menu-bar app, bottom overlay with live feedback, inserts into the frontmost app

Three steps

  1. Install & authorize — add the iOS keyboard with Full Access; grant mic + Accessibility on Mac
  2. Pick an engine — local ASR + built-in polish (zero config), or your own API keys
  3. Start talking — switch to OSGKeyboard, or hold Option on Mac

Platforms

iOS / iPadOS macOS
Keyboard / global hotkey hold Option
Local speech recognition SpeechAnalyzer SenseVoice / Qwen3
AI polish
Post-polish translation
Personal dictionary iCloud sync
Dictation history
Live UI Dynamic Island floating pill

Privacy

Speech is transcribed on-device by default. Polish sends text only — not raw audio. We never log ordinary keystrokes. See the Privacy Policy.


Build from source

Requires macOS with Xcode 26 and XcodeGen.

git clone https://github.com/hkgood/OSGKeyboard.git
cd OSGKeyboard
./Scripts/generate-xcodeproj.sh
open OSGKeyboard.xcodeproj

Run tests:

xcodebuild test -project OSGKeyboard.xcodeproj -scheme OSGKeyboard \
  -destination 'platform=iOS Simulator,name=iPhone 17'

Architecture (brief)

OSGKeyboard/          Main iOS app (Flow session host)
OSGKeyboardExt/       Custom keyboard extension
OSGKeyboardMac/       macOS menu-bar app
OSGKeyboardShared/    Shared framework (ASR, LLM, sync, design system)

Flow session model (iOS): the host app keeps a long-lived audio session; the keyboard sends start/stop signals via App Group; polished text is delivered back for insertion.

Engine modes:

  • local — on-device ASR; built-in polish (or your own LLM key)
  • cloud — uploads audio to your configured ASR provider, then polishes via LLM

See CHANGELOG.md for release history and CONTRIBUTING.md for PR guidelines.


Adding an LLM provider

Append a preset in OSGKeyboardShared/Models/LLMProvider.swift — any OpenAI-compatible /chat/completions endpoint works out of the box.


License

Source Available License — personal, non-commercial use only. Commercial licensing: rocky.hk@gmail.com.

S
Description
No description provided
Readme 57 MiB
Languages
Swift 93.7%
Python 4.6%
Shell 0.8%
HTML 0.5%
Objective-C++ 0.3%