feat: harden Flow cold-start/force-quit and polish macOS dictation UX

Fix cold-start overlay recursion that overflowed the main-thread stack when
recording began while the ready overlay was still up; also remove temporary
on-screen Flow DEBUG panels after the orange-mic investigation, and land the
macOS overlay/catalog/layout polish plus related Flow recovery hardening.
This commit is contained in:
Rocky
2026-07-10 12:39:41 +08:00
parent dcb66a9849
commit cdf833935a
104 changed files with 5794 additions and 853 deletions
+13 -9
View File
@@ -50,10 +50,12 @@
OSGKeyboard is a free, open-source custom keyboard for iOS 26 that turns
your voice into clean, AI-polished text — in any app.
Hold the mic key, speak naturally, release. The keyboard transcribes
your voice entirely on-device (Apple's iOS 26 SpeechAnalyzer +
DictationTranscriber), and only the final text is sent to the AI you
choose to polish it. Your audio never leaves your iPhone.
Hold the mic key, speak naturally, release. By default the keyboard
transcribes your voice entirely on-device (Apple's iOS 26
SpeechAnalyzer + DictationTranscriber), and only the final text is
sent to the AI you choose to polish it — your audio never leaves your
device unless you explicitly opt into the cloud ASR engine, which
uploads recordings to the provider you configure.
WHY OSGKEYBOARD
@@ -62,8 +64,9 @@ WHY OSGKEYBOARD
types for you.
• Push-to-talk, the way voice should work. No more "Hey Siri" mode that
listens to the whole room.
• On-device speech recognition. Powered by Apple's iOS 26 speech
pipeline — no cloud ASR, no audio upload.
• On-device speech recognition by default. Powered by Apple's iOS 26
speech pipeline — no audio upload unless you explicitly enable the
optional cloud ASR engine (confirmation required).
• Bring-your-own AI. Connect any OpenAI-compatible endpoint (OpenAI,
DeepSeek, Qwen DashScope, Moonshot, Zhipu, your own self-hosted
server). Your API key stays in the iOS Keychain.
@@ -76,12 +79,13 @@ WHY OSGKEYBOARD
• Zero dependencies. No trackers, no analytics, no crash reporters.
The whole project is ~8,700 lines of Swift you can audit in an
afternoon.
• Privacy first. PrivacyInfo.xcprivacy declares zero collected data;
we don't run a server.
• Privacy first. PrivacyInfo.xcprivacy declares exactly what the app
touches (voice audio + transcripts, on-device by default, never
linked or tracked); we don't run a server.
BUILT FOR
• iOS 26 and later, iPhone only.
• iOS 26 and later, iPhone and iPad.
• Anyone who types more than 100 words a day on their phone.
• Developers, writers, students, and translators who want voice input
that respects their privacy.
+14 -14
View File
@@ -6,7 +6,7 @@
<!-- Primary SEO -->
<title>OSGKeyboard — On-device voice-to-text keyboard for iOS</title>
<meta name="description" content="OSGKeyboard is a source-available iOS keyboard that turns speech into polished text in any app. On-device recognition (iOS 26+), AI polish, translation, and a personal dictionary. Local or cloud engine — your voice never leaves the device.">
<meta name="description" content="OSGKeyboard is a source-available iOS keyboard that turns speech into polished text in any app. On-device recognition (iOS 26+), AI polish, translation, and a personal dictionary. On-device by default — your voice never leaves the device unless you opt into the cloud engine.">
<meta name="keywords" content="voice keyboard, voice to text, dictation, iOS keyboard, speech to text, on-device ASR, SpeechAnalyzer, AI polish, translation keyboard, open source keyboard, OSGKeyboard">
<meta name="author" content="OSGKeyboard">
<meta name="robots" content="index, follow, max-image-preview:large">
@@ -19,7 +19,7 @@
<meta property="og:type" content="website">
<meta property="og:site_name" content="OSGKeyboard">
<meta property="og:title" content="OSGKeyboard — On-device voice-to-text keyboard for iOS">
<meta property="og:description" content="Turn speech into polished text in any app. On-device recognition (iOS 26+), AI polish, translation, personal dictionary. Your voice never leaves the device.">
<meta property="og:description" content="Turn speech into polished text in any app. On-device recognition (iOS 26+), AI polish, translation, personal dictionary. On-device by default — audio stays on your phone unless you opt into the cloud engine.">
<meta property="og:url" content="https://hkgood.github.io/OSGKeyboard/">
<meta property="og:image" content="https://hkgood.github.io/OSGKeyboard/assets/app-icon.png">
<meta property="og:image:width" content="1024">
@@ -459,7 +459,7 @@
<div class="hero-meta">
<span><span class="material-symbols-outlined" aria-hidden="true">devices</span><span data-i18n="hero.meta1">On-device recognition</span></span>
<span><span class="material-symbols-outlined" aria-hidden="true">bolt</span><span data-i18n="hero.meta2">Local or cloud AI</span></span>
<span><span class="material-symbols-outlined" aria-hidden="true">lock</span><span data-i18n="hero.meta3">Audio never leaves your phone</span></span>
<span><span class="material-symbols-outlined" aria-hidden="true">lock</span><span data-i18n="hero.meta3">On-device by default — audio stays on your phone</span></span>
</div>
</div>
@@ -498,12 +498,12 @@
<div class="feature">
<div class="ficon"><span class="material-symbols-outlined" aria-hidden="true">devices</span></div>
<h3 data-i18n="feat.ondevice.t">On-device recognition</h3>
<p data-i18n="feat.ondevice.b">iOS 26 SpeechAnalyzer transcribes on your iPhone. Audio never leaves the device.</p>
<p data-i18n="feat.ondevice.b">iOS 26 SpeechAnalyzer transcribes on your device by default — audio is uploaded only if you explicitly enable the cloud engine.</p>
</div>
<div class="feature">
<div class="ficon"><span class="material-symbols-outlined" aria-hidden="true">auto_awesome</span></div>
<h3 data-i18n="feat.polish.t">AI polish</h3>
<p data-i18n="feat.polish.b">Punctuation, structure, and clarity in three intensity levels. Only text is sent — never audio.</p>
<p data-i18n="feat.polish.b">Punctuation, structure, and clarity in three intensity levels. Polish sends only text — on the default engine your audio never goes online.</p>
</div>
<div class="feature">
<div class="ficon"><span class="material-symbols-outlined" aria-hidden="true">translate</span></div>
@@ -551,7 +551,7 @@
<div>
<span class="lock"><span class="material-symbols-outlined" aria-hidden="true">lock</span><span data-i18n="close.badge">Privacy by design</span></span>
<h2 data-i18n="close.title">Your voice stays yours</h2>
<p data-i18n="close.body">Speech is processed on-device. We never log ordinary keystrokes. Only transcribed text — never audio — is sent for polish or translation.</p>
<p data-i18n="close.body">Speech is processed on-device by default (the optional cloud engine uploads recordings to the provider you configure). We never log ordinary keystrokes; polish and translation receive only transcribed text.</p>
</div>
<a class="btn btn-quiet" href="privacy/">
<span class="material-symbols-outlined" aria-hidden="true">description</span>
@@ -583,7 +583,7 @@
"hero.cta.secondary": "Privacy",
"hero.meta1": "On-device recognition",
"hero.meta2": "Local or cloud AI",
"hero.meta3": "Audio never leaves your phone",
"hero.meta3": "On-device by default — audio stays on your phone",
"mock.app": "Notes",
"mock.line": "Tomorrow at 3 PM, sync with the design team on the new onboarding flow.",
"mock.chipEngine": "On-device",
@@ -593,9 +593,9 @@
"feat.dictation.t": "Tap-to-talk dictation",
"feat.dictation.b": "Tap to start, tap to stop. Up to 3.5 minutes per take, with a live countdown.",
"feat.ondevice.t": "On-device recognition",
"feat.ondevice.b": "iOS 26 SpeechAnalyzer transcribes on your iPhone. Audio never leaves the device.",
"feat.ondevice.b": "iOS 26 SpeechAnalyzer transcribes on your device by default — audio is uploaded only if you explicitly enable the cloud engine.",
"feat.polish.t": "AI polish",
"feat.polish.b": "Punctuation, structure, and clarity in three intensity levels. Only text is sent — never audio.",
"feat.polish.b": "Punctuation, structure, and clarity in three intensity levels. Polish sends only text — on the default engine your audio never goes online.",
"feat.translate.t": "Built-in translation",
"feat.translate.b": "Translate after polish into English, 中文, 日本語, 한국어 and more — right from the keyboard.",
"feat.dict.t": "Personal dictionary",
@@ -612,7 +612,7 @@
"step3.b": "Switch to OSGKeyboard, tap the mic, speak, tap again. Polished text lands at your cursor.",
"close.badge": "Privacy by design",
"close.title": "Your voice stays yours",
"close.body": "Speech is processed on-device. We never log ordinary keystrokes. Only transcribed text — never audio — is sent for polish or translation.",
"close.body": "Speech is processed on-device by default (the optional cloud engine uploads recordings to the provider you configure). We never log ordinary keystrokes; polish and translation receive only transcribed text.",
"close.cta": "Read the privacy policy",
"footer.copy": "© OSGKeyboard · v0.3.6 · source available, non-commercial",
"footer.source": "Source",
@@ -628,7 +628,7 @@
"hero.cta.secondary": "隐私政策",
"hero.meta1": "设备端识别",
"hero.meta2": "本地或云端 AI",
"hero.meta3": "音频不离开手机",
"hero.meta3": "默认端侧识别,音频留在手机",
"mock.app": "备忘录",
"mock.line": "明天下午三点,和设计团队同步新引导流程。",
"mock.chipEngine": "本地",
@@ -638,9 +638,9 @@
"feat.dictation.t": "点按听写",
"feat.dictation.b": "点一下开始,再点一下结束。单次最长 3.5 分钟,倒计时实时显示。",
"feat.ondevice.t": "设备端识别",
"feat.ondevice.b": "由 iOS 26 SpeechAnalyzer 在本机转写,音频不会离开设备。",
"feat.ondevice.b": "默认由 iOS 26 SpeechAnalyzer 在本机转写——仅当你显式开启云端引擎时,录音才会上传到你配置的服务商。",
"feat.polish.t": "AI 润色",
"feat.polish.b": "自动补全标点、结构与表达,分三档强度。仅发送文字,绝不发送音频。",
"feat.polish.b": "自动补全标点、结构与表达,分三档强度。润色仅发送文字;默认引擎下音频不联网。",
"feat.translate.t": "内置翻译",
"feat.translate.b": "润色后可翻译为 English、中文、日本語、한국어 等——直接在键盘上完成。",
"feat.dict.t": "个性词库",
@@ -657,7 +657,7 @@
"step3.b": "切换到 OSGKeyboard,点麦克风,说话,再点结束,润色好的文字自动插入光标处。",
"close.badge": "隐私优先设计",
"close.title": "你的声音,始终属于你",
"close.body": "语音在设备端处理,我们不记录普通击键。只有转写后的文字(绝非音频)会被发送用于润色或翻译。",
"close.body": "默认在设备端处理语音(可选的云端引擎会把录音上传到你配置的服务商)。我们不记录普通击键;润色与翻译只接收转写文字。",
"close.cta": "查看隐私政策",
"footer.copy": "© OSGKeyboard · v0.3.6 · 源码可见,禁止商用",
"footer.source": "源代码",
+8 -8
View File
@@ -16,11 +16,11 @@
<p class="lang"><a href="#zh">中文</a></p>
<h1>OSGKeyboard Privacy Policy</h1>
<p><strong>Last updated:</strong> July 8, 2026 · <em>v0.5.x</em></p>
<p>OSGKeyboard is a custom iOS keyboard that turns your voice into text. It runs as a Custom Keyboard Extension on iOS 26 and later, and uses Apple's on-device <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code> for transcription. After transcription, text may be polished or translated via a cloud LLM. This policy explains what data the app processes and how it is used.</p>
<p>OSGKeyboard is a custom iOS keyboard that turns your voice into text. It runs as a Custom Keyboard Extension on iOS 26 and later, and uses Apple's on-device <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code> for transcription by default. An optional cloud ASR engine (explicit opt-in) uploads recordings to the provider you configure. After transcription, text may be polished or translated via a cloud LLM. This policy explains what data the app processes and how it is used.</p>
<h2>What we collect</h2>
<ul>
<li><strong>Voice audio</strong> — captured only while you actively record. Audio is transcribed on-device with Apple's <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code>; raw audio is not uploaded by OSGKeyboard.</li>
<li><strong>Voice audio</strong> — captured only while you actively record. On the default <strong>local engine</strong>, audio is transcribed on-device with Apple's <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code> and raw audio is not uploaded. If you explicitly enable the <strong>cloud engine</strong> (a confirmation dialog is shown first), your recordings are uploaded to the ASR provider you configure (e.g. OpenAI, Qwen DashScope, Zhipu) for transcription; that provider's privacy policy applies. OSGKeyboard never stores or proxies your audio on its own servers.</li>
<li><strong>Transcribed text</strong> — after on-device ASR, the transcript (not audio) is sent for polish. On the <strong>local engine</strong>, polish uses a built-in DeepSeek endpoint configured at build time. On the <strong>cloud engine</strong>, polish and optional translation use the OpenAI-compatible API you configure (e.g. OpenAI, Qwen DashScope, Moonshot, Zhipu, Xiaomi MiMo, or your own server).</li>
<li><strong>API credentials</strong> — your cloud-engine LLM API key is stored in the iOS Keychain on your device and is read only when an LLM request is made. It is shared with the main app through a shared Keychain group, never through UserDefaults. When you enable <strong>iCloud settings sync</strong>, API keys replicate through Apple's <strong>iCloud Keychain</strong> to your other signed-in devices — not through iCloud Key-Value Store JSON.</li>
<li><strong>App preferences</strong> — engine mode, recognition language, polish intensity, translation target, handedness, cursor-navigation toggle, and keyboard settings are stored in App Group <code>UserDefaults</code> on your device so the main app and keyboard extension stay in sync. When iCloud settings sync is enabled, these preferences (excluding API keys) may also be mirrored in your private iCloud Key-Value Store account.</li>
@@ -33,12 +33,12 @@
<ul>
<li>We do <strong>not</strong> log or upload ordinary keystrokes you type with the keyboard.</li>
<li>We do <strong>not</strong> operate analytics, crash reporting, or advertising SDKs.</li>
<li>We do <strong>not</strong> upload raw audio to any server, including the LLM provider.</li>
<li>We do <strong>not</strong> upload raw audio anywhere on the default local engine. The only exception is the optional cloud ASR engine: if you explicitly enable it (a confirmation dialog is shown first), recordings go to the ASR provider <em>you</em> configure — never to servers of ours.</li>
<li>We do <strong>not</strong> sell personal data.</li>
</ul>
<h2>How the keyboard extension talks to the host app</h2>
<p>OSGKeyboard uses a long-lived "Flow session" hosted in the main app. The keyboard extension writes tiny "start / stop" signals into an App Group, the main app processes the audio on-device, then sends the transcript for polish (and optional translation) before writing the result back. Audio never leaves your device. Only transcribed text — never audio — is sent to the LLM endpoint (built-in DeepSeek on the local engine, or your configured API on the cloud engine).</p>
<p>OSGKeyboard uses a long-lived "Flow session" hosted in the main app. The keyboard extension writes tiny "start / stop" signals into an App Group, the main app captures the audio, transcribes it (on-device by default; via your configured cloud ASR provider if you opted into the cloud engine), then sends the transcript for polish (and optional translation) before writing the result back. On the default local engine audio never leaves your device; only transcribed text is sent to the LLM endpoint (built-in DeepSeek on the local engine, or your configured API on the cloud engine).</p>
<h2>Permissions</h2>
<ul>
@@ -68,11 +68,11 @@
<hr id="zh">
<h1>OSGKeyboard 隐私政策</h1>
<p><strong>更新日期:</strong>2026 年 7 月 8 日 · <em>v0.5.x</em></p>
<p>OSGKeyboard 是一款 iOS 自定义键盘,可将语音转为文字。它以自定义键盘扩展的形式运行,需要 iOS 26 及以上系统,转写全程使用 Apple 端侧的 <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code>。转写完成后,文字可能经云端 LLM 润色或翻译。本政策说明应用处理哪些数据及用途。</p>
<p>OSGKeyboard 是一款 iOS 自定义键盘,可将语音转为文字。它以自定义键盘扩展的形式运行,需要 iOS 26 及以上系统,默认使用 Apple 端侧的 <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code> 转写;可选的云端识别引擎(需显式二次确认开启)会把录音上传到你配置的服务商。转写完成后,文字可能经云端 LLM 润色或翻译。本政策说明应用处理哪些数据及用途。</p>
<h2>我们处理的数据</h2>
<ul>
<li><strong>语音音频</strong> — 仅在你主动录音时采集。音频在设备端通过 <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code> 转写,OSGKeyboard 不会上传原始录音。</li>
<li><strong>语音音频</strong> — 仅在你主动录音时采集。默认<strong>本地引擎</strong>下,音频在设备端通过 <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code> 转写,原始录音不会上传。若你显式开启<strong>云端引擎</strong>(会先弹出确认对话框),录音会上传到你配置的识别服务商(如 OpenAI、通义 DashScope、智谱)完成转写,适用该服务商的隐私政策。OSGKeyboard 自身绝不存储或中转你的音频</li>
<li><strong>转写文字</strong> — 端侧 ASR 完成后,转写文字(非音频)会发送润色。<strong>本地引擎</strong>使用构建时配置的内置 DeepSeek 端点;<strong>云端引擎</strong>的润色与可选翻译使用你配置的 OpenAI 兼容 APIOpenAI / 通义 DashScope / Moonshot / 智谱 / 小米 MiMo / 自建服务等)。</li>
<li><strong>API 凭证</strong> — 云端引擎的 LLM API Key 保存在设备 Keychain,仅在发起 LLM 请求时读取;通过共享 Keychain 组与主 App 共享,<strong>不会</strong>写入 <code>UserDefaults</code>。开启<strong>iCloud 设置同步</strong>后,API 密钥经 Apple <strong>iCloud 钥匙串</strong>同步到你其他已登录设备,<strong>不会</strong>写入 iCloud 键值存储 JSON。</li>
<li><strong>应用偏好</strong> — 引擎模式、识别语言、润色档位、翻译目标、握持偏好、光标导航开关、键盘设置等保存在 App Group <code>UserDefaults</code>,用于主 App 与键盘扩展之间的状态同步。开启 iCloud 设置同步后,这些偏好(不含 API 密钥)也可能镜像到你私有的 iCloud 键值存储账户。</li>
@@ -85,12 +85,12 @@
<ul>
<li>我们<strong>不会</strong>记录或上传你平时在键盘上的击键内容。</li>
<li>我们<strong>不会</strong>集成分析、崩溃上报或广告 SDK。</li>
<li>我们<strong>不会</strong>将原始录音上传至任何服务器,包括你配置的 LLM 服务商</li>
<li>默认本地引擎下,我们<strong>不会</strong>将原始录音上传至任何服务器。唯一例外是可选的云端识别引擎:你显式开启后(会先弹出确认对话框),录音会发送到<em></em>配置的识别服务商——绝不会发送到我们的服务器</li>
<li>我们<strong>不会</strong>出售个人数据。</li>
</ul>
<h2>键盘扩展与主 App 的通信方式</h2>
<p>OSGKeyboard 采用主 App 维护的「Flow 会话」机制:键盘扩展在 App Group 中写入轻量的「开始 / 停止」信号,主 App 在设备端处理音频,再将转写文字发送润色(及可选翻译)后回写结果。音频不会离开设备;发送给 LLM 的仅为转写文字(本地引擎走内置 DeepSeek,云端引擎走你配置的 API),不包含录音</p>
<p>OSGKeyboard 采用主 App 维护的「Flow 会话」机制:键盘扩展在 App Group 中写入轻量的「开始 / 停止」信号,主 App 采集音频并完成转写(默认在设备端;若你开启云端引擎则经你配置的识别服务商),再将转写文字发送润色(及可选翻译)后回写结果。默认本地引擎下音频不会离开设备;发送给 LLM 的仅为转写文字(本地引擎走内置 DeepSeek,云端引擎走你配置的 API)。</p>
<h2>权限说明</h2>
<ul>
+11 -11
View File
@@ -16,12 +16,12 @@
<body>
<p class="lang"><a href="#zh">中文</a></p>
<h1>OSGKeyboard Privacy Policy</h1>
<p><strong>Last updated:</strong> July 5, 2026 · <em>v0.3.6</em></p>
<p>OSGKeyboard is a custom iOS keyboard that turns your voice into text. It runs as a Custom Keyboard Extension on iOS 26 and later, and uses Apple's on-device <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code> for transcription. After transcription, text may be polished or translated via a cloud LLM. This policy explains what data the app processes and how it is used.</p>
<p><strong>Last updated:</strong> July 9, 2026 · <em>v0.5.x</em></p>
<p>OSGKeyboard is a custom iOS keyboard that turns your voice into text. It runs as a Custom Keyboard Extension on iOS 26 and later, and uses Apple's on-device <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code> for transcription by default. An optional cloud ASR engine (explicit opt-in) uploads recordings to the provider you configure. After transcription, text may be polished or translated via a cloud LLM. This policy explains what data the app processes and how it is used.</p>
<h2>What we collect</h2>
<ul>
<li><strong>Voice audio</strong> — captured only while you actively record. Audio is transcribed on-device with Apple's <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code>; raw audio is not uploaded by OSGKeyboard.</li>
<li><strong>Voice audio</strong> — captured only while you actively record. On the default <strong>local engine</strong>, audio is transcribed on-device with Apple's <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code> and raw audio is not uploaded. If you explicitly enable the <strong>cloud engine</strong> (a confirmation dialog is shown first), your recordings are uploaded to the ASR provider you configure (e.g. OpenAI, Qwen DashScope, Zhipu) for transcription; that provider's privacy policy applies. OSGKeyboard never stores or proxies your audio on its own servers.</li>
<li><strong>Transcribed text</strong> — after on-device ASR, the transcript (not audio) is sent for polish. On the <strong>local engine</strong>, polish uses a built-in DeepSeek endpoint configured at build time. On the <strong>cloud engine</strong>, polish and optional translation use the OpenAI-compatible API you configure (e.g. OpenAI, Qwen DashScope, Moonshot, Zhipu, Xiaomi MiMo, or your own server).</li>
<li><strong>API credentials</strong> — your cloud-engine LLM API key is stored in the iOS Keychain on your device and is read only when an LLM request is made. It is shared with the main app through a shared Keychain group, never through UserDefaults.</li>
<li><strong>App preferences</strong> — engine mode, recognition language, polish intensity, translation target, handedness, cursor-navigation toggle, and keyboard settings are stored in App Group <code>UserDefaults</code> on your device so the main app and keyboard extension stay in sync.</li>
@@ -34,12 +34,12 @@
<ul>
<li>We do <strong>not</strong> log or upload ordinary keystrokes you type with the keyboard.</li>
<li>We do <strong>not</strong> operate analytics, crash reporting, or advertising SDKs.</li>
<li>We do <strong>not</strong> upload raw audio to any server, including the LLM provider.</li>
<li>We do <strong>not</strong> upload raw audio anywhere on the default local engine. The only exception is the optional cloud ASR engine: if you explicitly enable it (a confirmation dialog is shown first), recordings go to the ASR provider <em>you</em> configure — never to servers of ours.</li>
<li>We do <strong>not</strong> sell personal data.</li>
</ul>
<h2>How the keyboard extension talks to the host app</h2>
<p>OSGKeyboard uses a long-lived "Flow session" hosted in the main app. The keyboard extension writes tiny "start / stop" signals into an App Group, the main app processes the audio on-device, then sends the transcript for polish (and optional translation) before writing the result back. Audio never leaves your device. Only transcribed text — never audio — is sent to the LLM endpoint (built-in DeepSeek on the local engine, or your configured API on the cloud engine).</p>
<p>OSGKeyboard uses a long-lived "Flow session" hosted in the main app. The keyboard extension writes tiny "start / stop" signals into an App Group, the main app captures the audio, transcribes it (on-device by default; via your configured cloud ASR provider if you opted into the cloud engine), then sends the transcript for polish (and optional translation) before writing the result back. On the default local engine audio never leaves your device; only transcribed text is sent to the LLM endpoint (built-in DeepSeek on the local engine, or your configured API on the cloud engine).</p>
<h2>Permissions</h2>
<ul>
@@ -68,17 +68,17 @@
<hr id="zh">
<h1>OSGKeyboard 隐私政策</h1>
<p><strong>更新日期:</strong>2026 年 7 月 5 日 · <em>v0.3.6</em></p>
<p>OSGKeyboard 是一款 iOS 自定义键盘,可将语音转为文字。它以自定义键盘扩展的形式运行,需要 iOS 26 及以上系统,转写全程使用 Apple 端侧的 <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code>。转写完成后,文字可能经云端 LLM 润色或翻译。本政策说明应用处理哪些数据及用途。</p>
<p><strong>更新日期:</strong>2026 年 7 月 9 日 · <em>v0.5.x</em></p>
<p>OSGKeyboard 是一款 iOS 自定义键盘,可将语音转为文字。它以自定义键盘扩展的形式运行,需要 iOS 26 及以上系统,默认使用 Apple 端侧的 <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code> 转写;可选的云端识别引擎(需显式二次确认开启)会把录音上传到你配置的服务商。转写完成后,文字可能经云端 LLM 润色或翻译。本政策说明应用处理哪些数据及用途。</p>
<h2>我们处理的数据</h2>
<ul>
<li><strong>语音音频</strong> — 仅在你主动录音时采集。音频在设备端通过 <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code> 转写,OSGKeyboard 不会上传原始录音。</li>
<li><strong>语音音频</strong> — 仅在你主动录音时采集。默认<strong>本地引擎</strong>下,音频在设备端通过 <code>SpeechAnalyzer</code> + <code>DictationTranscriber</code> 转写,原始录音不会上传。若你显式开启<strong>云端引擎</strong>(会先弹出确认对话框),录音会上传到你配置的识别服务商(如 OpenAI、通义 DashScope、智谱)完成转写,适用该服务商的隐私政策。OSGKeyboard 自身绝不存储或中转你的音频</li>
<li><strong>转写文字</strong> — 端侧 ASR 完成后,转写文字(非音频)会发送润色。<strong>本地引擎</strong>使用构建时配置的内置 DeepSeek 端点;<strong>云端引擎</strong>的润色与可选翻译使用你配置的 OpenAI 兼容 APIOpenAI / 通义 DashScope / Moonshot / 智谱 / 小米 MiMo / 自建服务等)。</li>
<li><strong>API 凭证</strong> — 云端引擎的 LLM API Key 保存在设备 Keychain,仅在发起 LLM 请求时读取;通过共享 Keychain 组与主 App 共享,<strong>不会</strong>写入 <code>UserDefaults</code></li>
<li><strong>应用偏好</strong> — 引擎模式、识别语言、润色档位、翻译目标、握持偏好、光标导航开关、键盘设置等保存在 App Group <code>UserDefaults</code>,仅用于主 App 与键盘扩展之间的状态同步。</li>
<li><strong>个性词库</strong> — 你在「词库」Tab 添加的词条与别名保存在本机。润色时会写入 LLM 提示词以保留你的词汇;词库数据不会上传到独立服务器。</li>
<li><strong>语音历史</strong> — 主 App 可在「历史」页保留近期成功转写,最多 500 条,仅本机保存,不会上传</li>
<li><strong>语音历史</strong> — 主 App 可在「历史」页保留近期成功转写,上限 300 条。开启 iCloud 设置同步后,历史也可能经私有 iCloud 键值存储同步</li>
<li><strong>用量统计</strong> — 首页统计卡片的累计听写时长、听写字数、翻译字数、词库词条数均在本地计算与保存。</li>
</ul>
@@ -86,12 +86,12 @@
<ul>
<li>我们<strong>不会</strong>记录或上传你平时在键盘上的击键内容。</li>
<li>我们<strong>不会</strong>集成分析、崩溃上报或广告 SDK。</li>
<li>我们<strong>不会</strong>将原始录音上传至任何服务器,包括你配置的 LLM 服务商</li>
<li>默认本地引擎下,我们<strong>不会</strong>将原始录音上传至任何服务器。唯一例外是可选的云端识别引擎:你显式开启后(会先弹出确认对话框),录音会发送到<em></em>配置的识别服务商——绝不会发送到我们的服务器</li>
<li>我们<strong>不会</strong>出售个人数据。</li>
</ul>
<h2>键盘扩展与主 App 的通信方式</h2>
<p>OSGKeyboard 采用主 App 维护的「Flow 会话」机制:键盘扩展在 App Group 中写入轻量的「开始 / 停止」信号,主 App 在设备端处理音频,再将转写文字发送润色(及可选翻译)后回写结果。音频不会离开设备;发送给 LLM 的仅为转写文字(本地引擎走内置 DeepSeek,云端引擎走你配置的 API),不包含录音</p>
<p>OSGKeyboard 采用主 App 维护的「Flow 会话」机制:键盘扩展在 App Group 中写入轻量的「开始 / 停止」信号,主 App 采集音频并完成转写(默认在设备端;若你开启云端引擎则经你配置的识别服务商),再将转写文字发送润色(及可选翻译)后回写结果。默认本地引擎下音频不会离开设备;发送给 LLM 的仅为转写文字(本地引擎走内置 DeepSeek,云端引擎走你配置的 API)。</p>
<h2>权限说明</h2>
<ul>