chore: snapshot uncommitted local work before rebasing onto cloud-agent

Safety commit so the 94 uncommitted files in the working tree (in-flight
Account/Mac/Tip work plus the feature-preview recording tooling) survive the
move onto cursor/cloud-agent-1787824905651-vxuwc.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-01 11:28:07 -07:00
co-authored by Claude Opus 5
parent 39002336c0
commit 5d51c5bf77
101 changed files with 3629 additions and 1354 deletions
+3 -4
View File
@@ -105,8 +105,8 @@ difference until the server request schema supports them.
## StoreKit credits
The existing `ByRockyACoffee` product remains a voluntary consumable and never
grants credits. Configure `500tks` for 500 credits at USD 0.99, `1500tks` for
The app ships three consumable credit packs and no other in-app purchase.
Configure `500tks` for 500 credits at USD 0.99, `1500tks` for
1,500 credits at USD 1.99 / CNY 18, and `3000tks` for 3,000 credits at
USD 2.99 / CNY 28.
@@ -123,5 +123,4 @@ USD 2.99 / CNY 28.
once.
6. Sign in to another OSG account and verify the first account's transaction is
rejected.
7. Confirm there is no Restore Purchases action for credit packs and that the
voluntary tip still changes only the local support count.
7. Confirm there is no Restore Purchases action for credit packs.
+41 -10
View File
@@ -32,7 +32,7 @@
| Field | Value |
|---|---|
| Price | Free |
| In-App Purchases | Optional consumables: tip `ByRockyACoffee`; managed-credit packs `500tks`, `1500tks`, `3000tks` |
| In-App Purchases | Optional consumables: managed-credit packs `500tks`, `1500tks`, `3000tks` |
| Availability | All configured App Store territories |
| Pre-order | No |
@@ -153,16 +153,14 @@ OSGKeyboard is a custom keyboard for iOS/iPadOS 26.
transactions are finished.
7. AI polish and AI mode can use either managed credits or a user-owned
provider key. Without either, local dictation still inserts recognized text.
8. Optional tip `ByRockyACoffee` remains a consumable support purchase and
does not grant managed credits or unlock features.
9. Clipboard history is off by default. To test it, open Settings →
8. Clipboard history is off by default. To test it, open Settings →
Clipboard, enable History, copy text on this device or through Universal
Clipboard, then return to the keyboard. Secure fields hide the clipboard
entry point. Turning History off preserves saved items; use the separate
confirmed clear action to delete them.
10. First-party Product Analytics is enabled by default under Settings →
About → Privacy. Turning it off deletes queued events. It does not collect
keyboard input, audio, transcripts, prompts, model output, or credentials.
9. First-party Product Analytics is enabled by default under Settings →
About → Privacy. Turning it off deletes queued events. It does not collect
keyboard input, audio, transcripts, prompts, model output, or credentials.
Privacy policy:
https://hkgood.github.io/OSGKeyboard/privacy/
@@ -280,17 +278,50 @@ output, audio, or keyboard content.
`Info.plist` declares `ITSAppUsesNonExemptEncryption = false`. Network calls use
standard HTTPS. Re-evaluate this answer if non-exempt cryptography is added.
## Screenshots
OSGKeyboard ships for **iPhone and iPad** (`TARGETED_DEVICE_FAMILY: "1,2"`), so
both device families need screenshots. Apple scales one uploaded set down to
smaller devices, so only the largest size per family is required.
| Set | Capture device | Pixel size | Required |
|---|---|---|---|
| iPhone 6.9" | iPhone 17 Pro Max | 1320 × 2868 (or 1290 × 2796) | Yes |
| iPad 13" | iPad Pro 13" (M4) | 2064 × 2752 (or 2048 × 2732) | Yes, because the app supports iPad |
Apple adjusts accepted sizes over time — confirm the current list in the
App Store Connect media manager before capturing.
### Capture procedure
1. Run the **submitted build** on the simulators above (Xcode 26).
2. Capture with `xcrun simctl io booted screenshot ~/Desktop/shot.png`.
Simulator captures contain no device bezel, which Apple requires.
3. Verify pixel dimensions; do not upscale a smaller capture.
4. Upload per device family in App Store Connect. Screenshots are **not**
stored in this repository; `docs/assets/screenshots/` holds the smaller,
web-optimized images used by the GitHub Pages site only.
### Scenes, in order
1. **Keyboard at rest** — OSGKeyboard in a text field, not recording
2. **Dictation in progress** — recording state with a partial transcript visible
3. **Chinese / English typing** — candidate bar with pinyin candidates or the
three-slot English QuickType bar
4. **AI assistant** — a streamed answer inside the keyboard before insertion
5. **Clipboard skills** — recommended actions after a copy
6. **Home** — monthly usage calendar, metrics, and personal dictionary
## Submission checklist
- [ ] Confirm `project.yml` still reads version 2.0.3 / build 94
- [ ] Open the existing Xcode project (do not regenerate unless needed)
- [ ] Run the release build and test suites on macOS with Xcode 26
- [ ] Replace screenshots with captures from the submitted build
- [ ] Capture fresh iPhone 6.9" and iPad 13" screenshots from the submitted
build (see Screenshots above)
- [ ] Verify the privacy answers against the submitted provider features
- [ ] In App Store Connect, add Device ID, Product Interaction, and Other
Usage Data for Analytics; linked to the user, not used for tracking
- [ ] Confirm `500tks`, `1500tks`, and `3000tks` are approved, consumable,
and mapped to the server credit catalog
- [ ] Confirm `ByRockyACoffee` remains an optional consumable tip and unlocks
no feature
- [ ] Upload, select build 94, add review notes, and submit
+145
View File
@@ -0,0 +1,145 @@
# 功能预览片录制 / Feature preview recording
四段中文 App Store 预览片的录制与合成流程。成片位于
`docs/assets/app-preview/zh/OSGKeyboard-<slug>-6.7-zh.mp4`。
| slug | 讲什么 | 素材来源 |
|------|--------|----------|
| `voice-polish` | 语音输入与自动润色,收在趣味润色风格 | `--edit-demo` + 润色风格页 |
| `personal-style` | 从历史听写学习你的表达习惯,生成专属风格 | `--polish-styles-screenshot` |
| `clipboard-agent` | 剪贴板 AI Agent,一键回复消息 | `--clipboard-demo` |
| `ask-ai` | 轻点听写、长按问 AI | `--ai-demo` |
## 为什么不用键盘扩展录
`WhatsNewDemoDriver` 那条路(`--whats-new-host`)会让**真实键盘扩展**盖在
Notes 宿主上,画面最真。但把第三方键盘设成**当前**输入法必须点一下地球键,
`simctl` 没有对应命令,`.GlobalPreferences` 里的 `AppleKeyboards` 只决定
"已启用"而不是"当前使用"。
所以这套流程改走 app 内的 DEBUG demo host:它们渲染的是**同一批真实键盘视图**
(`AIKeyboardView`、`LastInputEditView`、`ClipboardHistoryPanelView`……,见
`project.yml` 中 app target 的 `OSGKeyboardExt/Views/*` 源),由脚本化的
`KeyboardState` 驱动,不走 ASR、不走 LLM、不联网,也就不需要任何点击。
`--preview-fullscreen` 会在键盘上方补一块 Notes / Messages 宿主文档
(`FeaturePreviewHostDocument`),把原本留给 What's New 裁剪的空白填满,
让整帧可以直接当全屏预览片用。不传这个 flag 时,各 demo view 的画面与
What's New 工作流完全一致。
## 设备要求
- **iPhone 16 Plus**:原生 1290×2796,正好是 App Store Connect 6.9" 预览位
接受的尺寸之一,零缩放。
- **iOS 26.5 或更新**:app 用到的 `SwiftUI.View.glassEffect(_:in:)` 在 iOS 26.0
运行时里不存在,装上去会在启动时 `Symbol not found` 崩溃。
Xcode 预置的 iPhone 16 Plus 挂在 iOS 26.0 下,需要自建一台:
```bash
xcrun simctl create "OSG-Preview-16Plus" \
com.apple.CoreSimulator.SimDeviceType.iPhone-16-Plus \
com.apple.CoreSimulator.SimRuntime.iOS-26-5
```
## 完整流程
```bash
# 1) 编译(必须带签名,否则没有 entitlements → App Group 不可用 → 启动崩溃)
xcodebuild -project OSGKeyboard.xcodeproj -scheme OSGKeyboard \
-configuration Debug -destination "id=$UDID" \
-onlyUsePackageVersionsFromResolvedFile CODE_SIGNING_ALLOWED=YES build
# 2) 干净安装。覆盖安装不会刷新 App Group 容器,
# 容器缺失时 app 会渲染 AppGroupErrorView,录出来就是一屏报错。
xcrun simctl uninstall "$UDID" com.osgkeyboard.ios
xcrun simctl install "$UDID" \
~/Library/Developer/Xcode/DerivedData/OSGKeyboard-*/Build/Products/Debug-iphonesimulator/OSGKeyboard.app
# 3) 录制(脚本会先校验 App Group 容器,缺失直接报错退出)
Scripts/record_feature_previews.sh "$UDID" # 全部四段
Scripts/record_feature_previews.sh "$UDID" ask-ai # 只录一段
# 4) 合成。需要 Pillow —— 这版 ffmpeg 没有 libfreetype / libass,
# 所有文字都由 PIL 渲染成 RGBA PNG 再 overlay。
python3 -m venv .venv && .venv/bin/pip install Pillow
.venv/bin/python Scripts/compose_feature_previews.py
```
## 已知的坑
**变帧率**。`simctl io recordVideo` 录的是 VFR,时间轴不按墙钟走:直接
`-ss` 会定位到错误位置,`-t` 截出来的长度也不对(实测 24s 的窗口出来 27.4s,
足以让成片超出 App Store 的 30 秒上限)。`compose_feature_previews.py` 会先把
每段原始素材转成 30fps CFR 再裁,所以配置里的 `trim_start` / `trim_duration`
是真实秒数。
**静态页面录不出长片**。VFR 只在画面变化时出帧,纯静态页面录 22 秒也只得到
约 5 秒素材。`Source(freeze_at=…)` 会抽一帧定格,配合 `ken_burns` 用运镜提供
动势 —— `personal-style` 就是这么做的。
**字幕 PNG 必须 `-loop 1`**。单帧输入的 pts 恒为 0,`fade` 的时间轴永远停在
起点,alpha 被钉死,字幕完全不显示。
**色彩标签会丢**。原始素材是 `tv` range / bt709 / sRGB transfer,重编码不显式
带上就变成 `unknown`,播放器猜错会明显发灰。合成脚本每一次编码都显式打标签。
**抽帧检查会骗你**。用 ffmpeg 抽出 PNG 再看,色彩管理与实际播放不同,画面会
偏灰;判断压暗层强度请量像素值,不要看缩略图。
## App 内卡片版(无字幕短循环)
同一批原始素材还会产出第二种成品:给 app 内卡片用的短循环,位于
`docs/assets/feature-cards/<slug>.mp4`,另附 `<slug>-poster.png` 作占位图。
```bash
.venv/bin/python Scripts/compose_feature_cards.py
```
与预览片的区别:**没有字幕、没有片头片尾、没有音轨**,并且裁到键盘区
(`1290×944`),画面里只有真实键盘 chrome,不含任何为讲故事补的宿主界面。
窗口都选在首尾状态相近的位置,再叠一次 0.5 秒的尾接头淡入,所以可以无限循环
播放而看不出接缝(实测首末帧平均像素差 0.19–0.57 / 255)。`personal-style` 是
静态设置页,改用推近再拉回的对称运镜,天然闭合。
裁切区取 `y=1780` 起、高 944:剪贴板面板是键盘区里最高的元素(顶边 y=1779),
屏幕最底部 72px 是键盘下方的空白内边距,一并去掉。
### 技能行必须由真实排序器产生
`AIKeyboardView` 的技能行读 `ClipboardHistoryStore.shared` 与
`ClipboardSemanticRankingStore.shared` 两个**单例**,并交给
`ClipboardSkillSemanticRanker.recommended(...)` 按剪贴板内容排序。demo view 早期
用私有 store + `AIKeyboardView.debugPreviewSkills` 强塞了一排回复风格,结果录出
一个真实 app 里**不可能出现**的键盘——排序器在通用「回复」之外最多只放
`maximumReplyRecommendations = 2` 个专门回复技能。
现在 `ClipboardHistoryDemoView` 直接喂真实单例:把要回复的那条消息 ingest 成
最新剪贴板项,调 `ranking.analyze(entry)` 并**等它出 snapshot**(分析在主 actor 外
跑,抢跑会让行退化成孤零零一个「回复」),然后由排序器决定行内容。当前这条
消息排出的是「译为英语 / 回复 / 澄清追问 / 日程」。
演示脚本只从排出来的行里挑技能点——先点通用「回复」,再点行内第一个
`supportsReplyStyle` 的技能——所以画面里点的永远是屏幕上真实存在的 chip。
app 里**没有**「点一次回复,弹出多条候选」这种交互:点一个技能生成一条。
多风格是靠技能行里并列的多个回复技能实现的,先选风格再生成。
一个容易踩的坑:`AISessionState` 的 `beginGenerating` / `receivePartialAnswer` /
`receiveAnswer` 都用 `activeUtteranceID == utteranceID` 做守卫,而这个 ID 只由
`beginPreparing` 设置。跳过 `beginPreparing` 直接 `beginGenerating`,整串调用会
**静默变成空操作**,键盘区从头到尾不出现任何答案。每一轮都要
`enter()` → `beginPreparing` → `beginGenerating`。
两个 ffmpeg 细节:`zoompan` 的 `d=1` 是按输入帧走的,而 `-loop 1` 读 PNG 默认
**25fps**,不加 `-framerate 30` 出来的片长会短 25/30;另外推拉运镜不要用单条
`zoompan` 表达式同时做进和出——它的实际出帧数对不上 `duration * FPS`,片子会
停在半路,循环处肉眼可见地跳。现在的做法是只渲染推近的一半再 `reverse` 拼接。
## 改动了 demo 时间轴之后
`Scripts/feature_preview_clips.py` 里的裁切点和字幕时间是照着实际录像量出来的。
只要改了 `EditDemoView` / `AIKeyboardDemoView` / `ClipboardHistoryDemoView` 的
时间轴,就要重录并重新对时。合成脚本会校验成片是否落在 App Store 要求的
15–30 秒、1290×2796、H.264 之内,不合规会在输出里标 `!!`。
-109
View File
@@ -1,109 +0,0 @@
# OSGKeyboard 1.8.0 更新说明
> 以下为 OSGKeyboard 从 1.6.6 升级至 1.8.0 的关键变化。
## 新功能
### AI Agent
- 语音听写与 AI 问答合并为全新的助手模式:轻点麦克风开始听写,长按麦克风可直接语音提问。
- 新增热点与情境热词,麦克风区域会展示天气、节日、新闻、热搜等建议;点按即可向 AI 获取相关信息。
- AI 回答会实时显示在键盘中,确认后可插入当前输入框;支持的服务商可联网搜索,并在失败时自动回退。
- 仅当原输入框与光标上下文仍然匹配时自动插入 AI 回答;上下文变化时会保留回答,等待用户明确插入或丢弃。
- 设置 → AI Agent 可调节回复篇幅:简短、中等或详细。
### 剪贴板与 AI 技能
- 新增剪贴板历史。在设置 → 剪贴板开启“历史记录”后,可在本机保存最近 15 条纯文本;该功能默认关闭。
- 新增剪贴板建议条。可从键盘顶栏打开历史并点按插入;开启建议条后,最新复制内容会显示在键盘上方。
- 复制文字后约 30 秒内,助手会显示回复、总结、翻译等快捷技能;技能支持分页浏览,并可在技能页调整顺序。
- 新增技能页,最多可将 8 个技能放入键盘。内置技能可提取待办、添加日历日程、存入备忘录,或识别地址后打开高德、百度与 Apple 地图导航。
- 新增自定义 AI 技能,可设置名称、图标和提示词,也可选择连接自己的 iCloud 快捷指令。
### 编辑上次输入
- 编辑入口移至键盘右下角。点按后可口述修改要求、对比原文与编辑结果,并选择替换原文或追加内容。
- 编辑只使用最近一次由 OSGKeyboard 插入且仍可验证的文字,不会读取输入框中的无关内容。
## 全新 App 界面
- iPhone 底部导航重做为“首页 / 技能 / 风格 / 设置”四栏 Liquid Glass Dock,并以绿色胶囊标示当前页面。
- 首页改为信息总览,将使用统计、听写历史和个性词库集中显示为资料卡。
- 技能与润色风格采用统一的卡片式管理:点按选择、右上角编辑、右下角显示启用状态;技能支持长按拖动排序。
- 设置入口重新整理,AI Agent、剪贴板、语音与文字输入等页面更容易找到,并直接显示当前设置摘要。
- iPad 改为侧栏 + 内容区布局;键盘横竖屏均充分利用可用宽度,并加入系统地球键、撤销、重做、复制和剪切等操作。
## 中英文输入全面升级
- 中英文输入均可在本机学习用户的输入习惯、候选频率和选择偏好;设置中可单独清除打字习惯,不会删除个性词库。
- 拼音优化混合简拼排序,并支持 `zh`、`ch`、`sh` 两键简拼。
- 英文键盘新增类 QuickType 候选栏,同时显示原词、纠错和补全;约 4 万词离线词表会结合系统词库、通讯录名称与文本替换提供建议。
- 英文邻键纠错更贴合真实键位,并减少对专名、短词和全大写词的误改;拒绝纠错后会学习保留原词。
- 支持叠指连打、英文双空格句号,以及根据输入框显示前往、搜索、发送、完成、下一项等操作。
## 稳定性、隐私与重要变化
- 听写、AI 回答、编辑结果和剪贴板粘贴可通过统一的撤销操作回滚。
- 修复切换键盘崩溃、中文输入初始化后无法恢复、通用剪贴板导致键盘卡住,以及 AI 会话结束后麦克风不可用等问题。
- 全部内置与自定义润色风格都会保留问句原意,不再把用户的问题改写成回答。
- 设备端语音识别仍为默认选项;AI 使用用户自己配置的服务商与 API Key,不再提供内置 DeepSeek 回退。
- 剪贴板历史默认关闭,内容保存在本机,并仅在用户主动调用技能后发送给所配置的 AI 服务商。
- 长按麦克风现用于向 AI 提问;编辑上次输入改用右下角编辑按钮。旧的长按剪贴板语音指令已由剪贴板历史和快捷技能取代。
- 已安装旧中文名称“待办”或“日程”快捷指令的用户,需要从技能页重新安装 `OSGExtractTodos` 和 `OSGExtractEvents`。
- 已移除空白区域滑动光标及其设置开关。
---
# OSGKeyboard 1.8.0 Release Notes
> Key changes when upgrading OSGKeyboard from 1.6.6 to 1.8.0.
## New Features
### AI Agent
- Voice dictation and AI questions now share one Assistant mode: tap the microphone to dictate, or hold it to ask a question by voice.
- New trending and contextual hotwords surface suggestions for weather, holidays, news, trending topics, and more; tap one to ask AI about it.
- AI answers stream directly into the keyboard for review and insertion. Supported providers can search the web and fall back automatically when needed.
- AI answers insert automatically only while the original field and cursor context still match. If the context changes, the answer is retained for explicit insertion or discard.
- Settings → AI Agent now offers Short, Medium, and Detailed response lengths.
### Clipboard and AI Skills
- New Clipboard History keeps the latest 15 plain-text copies on this device when enabled in Settings → Clipboard. It is off by default.
- A new Clipboard suggestion strip lets you open history from the keyboard, insert an item, or keep the latest copy above the keyboard.
- For about 30 seconds after copying text, Assistant shows quick Reply, Summarize, Translate, and other skills. Skills support paging and can be reordered from the Skills page.
- The new Skills page lets you place up to eight skills on the keyboard. Built-in skills extract reminders, add calendar events, save to Notes, or open navigation in Amap, Baidu Maps, or Apple Maps.
- Custom AI Skills support your own name, icon, and prompt, with an optional iCloud Shortcut connection.
### Edit Last Input
- Edit moves to the bottom-right keyboard button. Describe the change by voice, compare the original and edited text, then replace or append the result.
- Editing uses only the latest verifiable text inserted by OSGKeyboard and does not read unrelated field content.
## A New App Interface
- The iPhone dock is rebuilt around four Liquid Glass destinations: Home, Skills, Styles, and Settings, with a green capsule marking the current page.
- Home becomes an overview of usage statistics, dictation history, and Personal Dictionary cards.
- Skills and polish styles share a card-based interaction: tap to select, edit from the top-right, see enabled status at the bottom-right, and long-press skills to reorder.
- Settings navigation is reorganized so AI Agent, Clipboard, Speech, and Text Input are easier to find, with current summaries shown beside each row.
- iPad adopts a sidebar + detail layout. The keyboard fills the available portrait and landscape width and adds the system globe key, undo, redo, copy, and cut.
## Chinese and English Typing, Rebuilt
- Chinese and English typing learn candidate frequency and selection preferences locally. Typing habits can be cleared without deleting Personal Dictionary entries.
- Pinyin improves mixed abbreviations and supports two-key `zh`, `ch`, and `sh` abbreviations.
- The English keyboard adds a QuickType-style bar for the typed word, correction, and completion. An offline list of about 40,000 words works with the system lexicon, contact names, and text replacements.
- Neighbor-key correction follows the physical keyboard while avoiding unwanted changes to names, short words, and all-caps text. Rejecting a correction teaches the original.
- Overlapping key presses, the English double-space period, and contextual Go, Search, Send, Done, and Next actions make typing feel closer to the system keyboard.
## Stability, Privacy, and Important Changes
- One Undo action now rolls back dictation, AI answers, edits, and clipboard pastes.
- Fixes include keyboard-switch crashes, Chinese input recovery after setup, Universal Clipboard freezes, and a microphone that could remain unavailable after AI sessions.
- Every built-in and custom polish style preserves questions instead of rewriting them as answers.
- On-device speech recognition remains the default. AI uses your configured provider and API key; the built-in DeepSeek fallback has been removed.
- Clipboard History stays off by default, keeps its contents on device, and sends text to the configured AI provider only after you invoke a skill.
- Holding the microphone now asks AI, while Edit Last Input moves to the bottom-right Edit button. Clipboard History and quick skills replace the old hold-to-command clipboard flow.
- If you installed the older Chinese-named Tasks or Events Shortcuts, reinstall `OSGExtractTodos` and `OSGExtractEvents` from the Skills page.
- Blank-area cursor sliding and its Settings toggle have been removed.
+98 -80
View File
@@ -1,113 +1,132 @@
# StoreKit — Voluntary Support Tip
# StoreKit — managed credit packs
OSGKeyboard offers a single **Consumable** in-app purchase:
OSGKeyboard ships **three Consumable** in-app purchases and no subscriptions.
All three add credits to the optional OSG managed account. The authoritative
local catalog is [`OSGKeyboard.storekit`](../OSGKeyboard.storekit); App Store
Connect must match it exactly.
| Field | Value |
|---|---|
| **Type** | Consumable |
| **Product ID** | `ByRockyACoffee` |
| **Reference name (Connect)** | 给老刘买杯咖啡 |
| **Display name (en)** | Buy me a cup of coffee |
| **Display name (zh-Hans)** | 给老刘买杯冰美式 |
| **Price (China)** | ¥28 (nearest available App Store tier) |
| **Price (US, suggested)** | $3.99 equivalent tier |
| Product ID | Type | Reference name | Display name (en) | Price (CN) | Grants |
|---|---|---|---|---|---|
| `500tks` | Consumable | 500 OSG 积分 | 500 OSG Credits | ¥8 | 500 account credits |
| `1500tks` | Consumable | 1,500 OSG 积分 | 1,500 OSG Credits | ¥18 | 1,500 account credits |
| `3000tks` | Consumable | 3,000 OSG 积分 | 3,000 OSG Credits | ¥28 | 3,000 account credits |
**Important:** this tip does **not** unlock translation, dictionary limits,
long Flow sessions, or any other capability. The app remains fully free.
Rules that apply to all three:
Consumable purchases **cannot be restored** (Apple policy). Settings copy
explains this to users.
- Consumables **cannot be restored** (Apple policy). The UI states this.
- Nothing here gates a feature. Local dictation, BYOK providers, typing,
clipboard skills, and iCloud sync stay free with or without a purchase.
- Credits only add balance to the optional OSG account. They are useless
without Sign in with Apple, and the app never requires an account.
- Credit packs are **iOS / iPadOS only**. The macOS build ships via Developer
ID outside the Mac App Store, so it loads no StoreKit products at all.
## Where the packs appear
iOS Home → credit card → Account Center, and Settings → Account Center. Both
require Sign in with Apple.
## Purchase flow (server-verified)
Credit packs are not client-trusted. `AccountCreditPurchaseManager`:
1. Buys with StoreKit 2, binding `appAccountToken` to the signed-in account ID.
2. Rejects any transaction whose `appAccountToken` or product ID does not match
the configured set.
3. Submits the signed transaction to the account service
(`submitCreditTransaction`) and checks the returned transaction and product
IDs against the local ones.
4. Calls `transaction.finish()` **only after** the server confirms and the
ledger is updated.
A purchase that fails server verification stays unfinished and is retried from
the transaction-updates stream, so a crashed or backgrounded app does not lose
credits.
---
## App Store Connect setup
1. Open **App Store Connect → OSGKeyboard → In-App Purchases**.
2. Create **Consumable** with Product ID `ByRockyACoffee`.
3. Add localizations (en + zh-Hans) using the strings in
`OSGKeyboardShared/*/Shared.strings` (`tip.*` keys) and
`docs/APPSTORE_METADATA.md`.
4. Set pricing: **China ¥28** (App Store only offers fixed tiers — ¥28 is
the nearest to ¥30); pick equivalent tiers for other territories.
5. Submit the IAP for review **with** the app version that includes the
Settings → Support the Developer entry.
2. Create each of the three products as **Consumable** with the exact Product
IDs above. Do not rename an existing ID — Apple treats it as a new product.
3. Add en + zh-Hans localizations. Copy comes from
[`OSGKeyboard.storekit`](../OSGKeyboard.storekit) (`localizations`) and
[`docs/APPSTORE_METADATA.md`](APPSTORE_METADATA.md).
4. Set pricing to the nearest App Store tier for China (¥8 / ¥18 / ¥28) and let
Apple map the equivalent tiers elsewhere.
5. Submit the IAPs for review **together with** the app version that exposes
them. A first IAP submitted without a build is rejected as incomplete.
### Review screenshot (required — fixes「元数据丢失」)
### Review screenshot (required — otherwise「元数据丢失」)
App Store Connect → IAP **ByRockyACoffee** → **审核信息** → **截屏**:
Each product needs a screenshot under **审核信息 → 截屏**: run the app signed
in, open **Home → credit card → Account Center**, and capture the pack list
showing localized prices.
1. Run the app (Simulator or device) with Settings open at the top
**支持开发者** card showing the green **打赏 ¥28.00** button.
2. Capture that screen (⌘S in Simulator, or device screenshot).
3. Upload to **截屏 → 选取文件**.
4. Optional **审核备注**:
Optional **审核备注**:
```
Optional voluntary tip only (Consumable IAP ByRockyACoffee).
All features free before and after purchase. Settings tab → top of page.
Consumable — cannot restore (stated in UI).
Consumable credits for the optional OSG managed speech/AI service.
Requires Sign in with Apple. Local dictation and user-supplied API keys
remain free and never require credits. Consumable — cannot restore.
```
Save — status should become **准备提交**.
---
## Local testing (StoreKit Test — no Connect / sandbox account)
## Local testing (StoreKit Test — no Connect, no sandbox account)
Uses [`OSGKeyboard.storekit`](../OSGKeyboard.storekit). The **OSGKeyboard**
scheme already references it in `project.yml` (`storeKitConfiguration`).
The **OSGKeyboard** scheme already references `OSGKeyboard.storekit` in
`project.yml` (`storeKitConfiguration`).
1. `xcodegen generate && open OSGKeyboard.xcodeproj`
2. **Product → Scheme → Edit Scheme → Run → Options**
- Confirm **StoreKit Configuration** = `OSGKeyboard.storekit`
3. Run **OSGKeyboard** on Simulator (e.g. iPhone 17) or a plugged-in device
4. Open **Settings** tab → top card **支持开发者**
5. Tap **打赏 ¥28.00** → StoreKit Test purchase sheet appears
6. **Buy** → thank-you alert; **Cancel** → no error
7. Repeat buy once (Consumable allows multiple)
1. `./Scripts/generate-xcodeproj.sh && open OSGKeyboard.xcodeproj`
2. **Product → Scheme → Edit Scheme → Run → Options** — confirm
**StoreKit Configuration** = `OSGKeyboard.storekit`
3. Run **OSGKeyboard** on a simulator (e.g. iPhone 17) or a device.
4. Sign in with Apple, then open **Home → credit card → Account Center**.
5. Buy `500tks` → balance increases by 500 after the account service confirms;
the purchase-history row appears.
6. Cancel a purchase → returns to idle with no error spam.
7. Repeat a purchase → allowed (Consumable).
**Debug menu (optional):** Xcode → **Debug → StoreKit → Manage Transactions**
to view / delete test purchases.
**Debug menu:** Xcode → **Debug → StoreKit → Manage Transactions** to inspect
or delete test purchases.
Automated coverage: `AccountCreditPurchaseManagerTests` (in the hermetic
manifest — see [TESTING.md](TESTING.md)) and `AccountCenterUITests`, which runs
separately via `xcodebuild -scheme OSGKeyboardUITests` against the `IAPReview`
stub harness — see
[ACCOUNT_MANAGED_GATEWAY_TESTING.md](ACCOUNT_MANAGED_GATEWAY_TESTING.md).
---
## Sandbox testing (real App Store sandbox — after Connect IAP is 准备提交)
## Sandbox testing (after Connect IAPs reach 准备提交)
1. App Store Connect → **用户和访问** → **沙盒** → create a **Sandbox Tester**
2. On device: **设置 → App Store → 沙盒账户** → sign in (not your real Apple ID)
3. Install via **TestFlight** or **Debug run without** `.storekit`:
- To hit Connect products: Edit Scheme → Run → Options → set StoreKit
Configuration to **None**, then run on device
4. Settings → **支持开发者** → purchase with sandbox account
5. Sandbox charges are free; receipt is real sandbox flow
1. App Store Connect → **用户和访问 → 沙盒** → create a **Sandbox Tester**.
2. On device: **设置 → App Store → 沙盒账户** → sign in (not your real Apple ID).
3. Install via **TestFlight**, or Debug-run with **StoreKit Configuration =
None** so the app hits Connect products.
4. Buy each pack once and confirm the server-side ledger, not just the UI.
5. Sandbox charges are free; the receipt path is the real one.
The full server-side matrix (replayed transactions, interrupted network,
wrong-account rejection) lives in
[ACCOUNT_MANAGED_GATEWAY_TESTING.md](ACCOUNT_MANAGED_GATEWAY_TESTING.md).
---
## Submit to App Review (after local + sandbox pass)
## Release checklist
Yes — **wait until testing looks good**, then:
1. IAP **ByRockyACoffee** status = **准备提交** (pricing + screenshot + localizations)
2. Bump app version in `project.yml` / `CHANGELOG.md` if needed
3. **Archive** → upload build to App Store Connect
4. Open the new **App Store version** page → **App 内购买项目** → **+** → select **ByRockyACoffee**
5. Fill metadata, attach build, submit **version + IAP together** (first IAP rule)
### Sandbox checklist
- [ ] Product loads and shows localized price
- [ ] Successful purchase shows thank-you alert
- [ ] All three products load and show localized prices
- [ ] Each pack adds the correct balance after server verification
- [ ] Purchase history lists the new transaction without duplicates
- [ ] A failed/interrupted verification is retried and not lost
- [ ] User cancel returns to idle (no error spam)
- [ ] Repeat purchase works (Consumable allows multiple)
- [ ] No Restore button for this product (Consumable)
- [ ] Translation, Flow, dictionary, BYOK unchanged after tipping
### macOS note
The menu-bar Mac build (`com.osgkeyboard.mac`) ships via Developer ID outside
the Mac App Store today. StoreKit products load only for App Store builds.
The Mac Settings UI is present for parity; tip IAP requires an App Store
distribution if Mac tipping is enabled later.
- [ ] No Restore button for any of these products
- [ ] Local dictation, BYOK, typing, and clipboard skills unchanged after purchase
---
@@ -115,8 +134,7 @@ distribution if Mac tipping is enabled later.
| File | Role |
|---|---|
| `OSGKeyboardShared/Services/Tip/TipProduct.swift` | Product ID constants |
| `OSGKeyboardShared/Services/Tip/TipPurchaseManager.swift` | StoreKit 2 purchase flow |
| `OSGKeyboardShared/DesignSystem/SupportDeveloperSection.swift` | iOS Settings UI |
| `OSGKeyboardMac/MacSupportDeveloperTipRows.swift` | macOS Settings UI |
| `OSGKeyboard/Views/Account/AccountCreditPurchaseManager.swift` | Purchase + server verification |
| `OSGKeyboard/Views/Account/AccountCreditStore.swift` | StoreKit 2 product/purchase abstraction |
| `OSGKeyboard/Views/Account/AccountCenterView.swift` | Credit pack UI, purchase history |
| `OSGKeyboard.storekit` | Local StoreKit Test catalog |
+5
View File
@@ -1,5 +1,10 @@
# Typing Keyboard Baseline (Phase 0)
> **文档状态**:Phase 0 冻结基线(历史快照,2026-08-03)。它记录当时的选型依据,
> 不随版本更新。**当前权威的依赖版本与许可以 `NOTICE-TYPING.md` 和
> `OSGKeyboard/Resources/Typing/Rime/manifest.json` 为准**;实施进展见
> `docs/keyboard-accuracy-plan.md`。
Frozen reference for NanoMouse × OSGKeyboard fusion.
## NanoMouse reference
+10
View File
@@ -0,0 +1,10 @@
# Archived documents
Point-in-time documents kept for traceability only. **They are not maintained
and do not describe current behavior.** For current facts use `CHANGELOG.md`,
`docs/APPSTORE_METADATA.md`, and the living docs directly under `docs/`.
| Document | What it was | Why it is archived |
|---|---|---|
| [ios-pip-voice-session-plan.md](ios-pip-voice-session-plan.md) | PiP voice-session keep-alive plan (2026-07-26) | Implemented in 1.6.5; its Live Activity assumption was removed in 1.6.6 |
| [mac-parity-audit-2026-08-23.md](mac-parity-audit-2026-08-23.md) | iOS ↔ macOS parity audit at 2.0.1 (build 90) | One-off snapshot; superseded by later releases |
@@ -1,8 +1,10 @@
# iOS PiP 语音会话保活规划
> **文档状态**:产品与架构规划(待验证,未进入实现)
> **适用范围**:iOS 主 App + 键盘扩展 + Live Activity
> **目标版本**:待产品验证后确定
> **文档状态**:**历史规划(已实现,部分结论已被取代)**——保留用于追溯设计取舍,不代表当前行为
> **实际落地**:自动低感知 PiP 于 **1.6.5** 上线(`OSGKeyboard/Services/FlowPictureInPictureController.swift`)
> **主要偏差**:本文假设 Live Activity 继续承担锁屏与灵动岛展示;**1.6.6 已删除 Live Activity 扩展与全部 ActivityKit 代码**,语音会话仅保留静默低感知 PiP,用户可见文案不出现「画中画」
> **当前事实以何为准**:`CHANGELOG.md`、`docs/FLOW_BLUETOOTH_TESTING.md`、`docs/keyboard-memory-budget.md`
> **适用范围**:iOS 主 App + 键盘扩展
> **创建日期**:2026-07-26
---
@@ -1,5 +1,6 @@
# OSGKeyboard · iOS 完整检查 + macOS 差距与移植建议报告
> **文档状态**:**一次性审计快照(历史存档)**——仅代表 2026-08-23 当时的代码状态,其后 Mac 与 iOS 均有改动(如总览页已改为月度用量日历),不要作为现行文档使用
> **报告日期**:2026-08-23
> **当前版本**:2.0.1(build 90)— `project.yml:51-52`
> **审查范围**:iOS Host (`OSGKeyboard/`, 88 .swift) + iOS HostSupport (`OSGKeyboardHostSupport/`, 29 .swift) + iOS Extension (`OSGKeyboardExt/`, 27 .swift) + Shared (`OSGKeyboardShared/`, 230 .swift) + macOS (`OSGKeyboardMac/`, 39 .swift) + macOS Tests (3 .swift)
Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.
+4 -3
View File
@@ -1,11 +1,12 @@
# 打字键盘输入准确率提升计划
> **文档状态**:产品与工程规划(**实现中**:Phase 1~4 已落地,待真机试打)
> **文档状态**:**Phase 1~4 已随 1.6.1(2026-08-05)发布**;本文保留为设计依据与验收脚本,剩余工作见下方「剩余事项」
> **适用范围**:iOS 键盘扩展打字面(`OSGKeyboardExt` 打字键网格)+ 共享布局度量(`OSGKeyboardShared`)
> **关联基线**:`docs/TYPING_KEYBOARD_BASELINE.md`
> **实现分支**:`feat/keyboard-touch-accuracy`
> **已落地代码**:`OSGKeyboardShared/Typing/KeyHitTesting.swift`、`TypingKeyLayout.swift`、`PinyinNextKeyResolver.swift`、`OSGKeyboardExt/Typing/TypingKeyTouchPad.swift`(原分支 `feat/keyboard-touch-accuracy` 已合入 `main` 并删除)
> **创建日期**:2026-08-03
> **目标版本**:待 Phase 0 基线试打后确定发布节奏
>
> **剩余事项**:附录 A 的真机试打脚本尚未留下正式打分记录;Phase 5(气泡预览等加分项)未启动。
---
+1 -1
View File
@@ -76,6 +76,6 @@ operation-specific context. Filter Console by `OSGDiag/memory` and compare:
## Structural split
- Extension links **OSGKeyboardShared** only (no Charts / StoreKit / Speech / HostSupport).
- Host embeds **OSGKeyboardHostSupport** (ASR, CLM, CloudASR, tip/charts UI).
- Host embeds **OSGKeyboardHostSupport** (ASR, CLM, CloudASR, charts UI).
- Heavy assets (`osg_pinyin.dict.yaml`, CLM bin, licenses, local-asr catalog)
ship in the **host app** bundle; extension reads Rime from App Group after deploy.
+1 -1
View File
@@ -1,6 +1,6 @@
# OSGKeyboard 本地 ASR 技术架构
> **文档状态**:1.7.0 代码事实 + 后续评测方向
> **文档状态**:Mac 本地 ASR 现行架构说明(结论已复核至 **2.0.3 / build 94**,2026-08-31)+ 后续评测方向
> **适用范围**:macOS 15+ 本地听写;iOS 26+ 使用 Apple SpeechAnalyzer。
> **当前结论**:Mac 默认安装目录模型为
> `qwen3-mlx-0.6b-4bit`,通过 `mlx-audio-swift` 做 Qwen3 MLX 真流式识别;
+57
View File
@@ -178,6 +178,63 @@
<p class="empty" id="empty-zh">暂无与当前版本匹配的更新说明。</p>
<p class="empty" id="empty-en">No release notes match this app version.</p>
<!-- ========== 2.0.3 ========== -->
<article class="version-block" data-version="2.0.3">
<div class="version-chip">2.0.3</div>
<div class="lang-block" data-lang="zh">
<section class="section">
<h2>英文个性词推荐</h2>
<ul>
<li>除中文输入习惯外,键盘现在也会学习你反复使用的英文生词,并推荐加入个人词库</li>
<li>中英文推荐词都需要你确认后才会加入,原有的中文学习记录不受影响</li>
</ul>
</section>
<section class="section">
<h2>界面一致性优化</h2>
<ul>
<li>统一卡片间距、设置页字体和菜单对齐方式,账号与购买记录的排版更整齐</li>
<li>一级页面与详情页之间的标签栏过渡更自然</li>
</ul>
</section>
<section class="section">
<h2>问题修复</h2>
<ul>
<li>重装 App 或重新回到前台后,Apple 账号登录状态恢复更可靠,登录过程中的加载提示保持一致</li>
<li>在不同 App 之间切换后,中英文候选词可靠恢复,且不会残留上一个输入框的内容</li>
</ul>
</section>
</div>
<div class="lang-block" data-lang="en">
<section class="section">
<h2>English Personal-Term Suggestions</h2>
<ul>
<li>Alongside Chinese typing habits, the keyboard now learns rare English words you use repeatedly and suggests adding them to your Personal Dictionary</li>
<li>Suggestions in either language are added only after you confirm, and existing Chinese learning history is preserved</li>
</ul>
</section>
<section class="section">
<h2>Interface Consistency</h2>
<ul>
<li>Card spacing, settings typography, menu alignment, and the account and purchase-history rows are now consistent</li>
<li>Tab-bar transitions between primary pages and detail pages feel smoother</li>
</ul>
</section>
<section class="section">
<h2>Fixes</h2>
<ul>
<li>Apple sign-in is restored more reliably after a reinstall or when returning to the foreground, with a consistent loading state</li>
<li>Chinese and English candidates recover reliably after switching apps, without leaking state from the previous text field</li>
</ul>
</section>
</div>
</article>
<!-- ========== 2.0.2 ========== -->
<article class="version-block" data-version="2.0.2">
<div class="version-chip">2.0.2</div>
@@ -1,12 +1,12 @@
# Personal Dictionary iCloud KVS — Manual Verification Checklist
Use this on **macOS with Xcode 16+** and at least two devices signed into the **same Apple ID** with iCloud Drive / iCloud enabled.
Use this on **macOS with Xcode 26** (the repository build requirement) and at least two devices signed into the **same Apple ID** with iCloud Drive / iCloud enabled.
## Prerequisites
1. In Apple Developer Portal, enable **iCloud** → **Key-value storage** for `com.osgkeyboard.ios`.
2. Regenerate provisioning profiles after entitlements change.
3. Run `xcodegen generate` and install a fresh build on each device.
3. Run `./Scripts/generate-xcodeproj.sh` and install a fresh build on each device.
## Scenarios
Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

-73
View File
@@ -1,73 +0,0 @@
# App Store Screenshots
> ⚠️ **PLACEHOLDERS.** The 10 PNGs in this directory are
> automatically generated blanks produced by
> `scripts/generate_screenshot_placeholders.py` and must be
> **replaced with real Simulator screenshots** before App Store
> submission. They use the correct dimensions (1290×2796 for
> 6.7", 1179×2556 for 6.1") so the upload validator will accept
> them, but they contain no real UI.
## Required dimensions (2026)
| Size | Devices | Dimensions | Apple requirement |
|------|---------|-----------|---|
| 6.7" | iPhone 17 Pro Max, 17, 16 Pro Max, 16 Plus, 15 Pro Max, 15 Plus | 1290 × 2796 px | **Required** (3-10 images) |
| 6.1" | iPhone 17 Pro, 17, 16 Pro, 16, 15 Pro, 15, 14 Pro, 14 | 1179 × 2556 px | **Required** (3-10 images) |
| 5.5" | iPhone 8 Plus (legacy) | 1242 × 2208 px | Optional since 2024 |
iPad screenshots are not required because OSGKeyboard is iPhone-only
(`TARGETED_DEVICE_FAMILY = 1`).
## Layout
```
docs/screenshots/
├── 6.7/ ← 1290×2796 (iPhone 17 Pro Max / 17)
│ ├── 01-keyboard-default.png
│ ├── 02-flow-session.png
│ ├── 03-on-device-asr.png
│ ├── 04-llm-polish.png
│ └── 05-providers.png
└── 6.1/ ← 1179×2556 (iPhone 17 Pro / 17)
├── 01-keyboard-default.png
├── 02-flow-session.png
├── 03-on-device-asr.png
├── 04-llm-polish.png
└── 05-providers.png
```
## How to capture real screenshots
1. Open `OSGKeyboard.xcodeproj` in Xcode 26+
2. Run on **iPhone 17 Pro** simulator (6.1" set) and **iPhone 17 Pro Max** simulator (6.7" set)
3. For each scene:
```bash
# Take a screenshot of the simulator window
xcrun simctl io booted screenshot ~/Desktop/shot.png
```
4. Process for App Store (Apple rejects frames containing the device bezel — full-screen content only):
```bash
# The simulator screenshot already has a thin device frame.
# Open in Preview, crop to full screen (⌘+K with ⌥ for precision),
# export as PNG at 1290×2796 or 1179×2556.
sips -z 2796 1290 shot.png --out final-6.7.png
sips -z 2556 1179 shot.png --out final-6.1.png
```
5. Replace the placeholders with the real captures, keeping the
same filenames so the App Store Connect → Version → Uploads UI
auto-pairs by file.
## Scenes to capture
The 5 placeholders are intentional scene placeholders. Capture these
*exact* screens, in this order:
1. **Keyboard at rest** — the iOS keyboard, OSGKeyboard mode, no recording
2. **Flow session active** — keyboard with the green/orange recording ring,
partial transcript visible in the host text field
3. **On-device ASR** — Settings view with the locale list, the on-device
indicator (iPhone icon) visible next to ≥ 3 supported locales
4. **LLM polish** — Settings view with the API provider card, a sample
"polish" transformation shown in the inline preview
5. **Providers** — API Settings card scrolled to show all 6 provider logos