chore(release): bump version to 2.1.6 (build 131)

Version: CURRENT_PROJECT_VERSION 130 -> 131 (project.yml, AGENTS.md, READMEs,
docs/llms.txt).

fix(flow): host-app identification now compiles in every build configuration
(Debug, TestFlight, Release). The arbiter probe and HostAppIdentity were
DEBUG-only, so TestFlight/App Store builds never knew the host bundle id and
could not auto-return after a startflow handoff. Debug-only logging and the
Home test card are unchanged.

Also includes the pending work in the tree:
- Home: single account/setup card surface (HomeAccountSurfacePolicy/Views)
- Wide screens: voice key row and centred mic placement, iPhone Duo layout
- Keyboard: clipboard skill ranking and AI skill layout updates
- Debug: account session handoff to the Simulator (+ import script)
- Docs: portfolio assets, privacy policy URL, site/README refresh
This commit is contained in:
Rocky
2026-10-29 11:54:13 +08:00
parent 055a40976f
commit 4dce67df84
65 changed files with 3682 additions and 787 deletions
+39 -12
View File
@@ -7,14 +7,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Fixed
- **Auto-return to the previous app in every build**: host-app identification used to compile only in Debug, so TestFlight and App Store builds never knew which app you were typing in and could not jump back after starting a voice session. It now runs in Debug, TestFlight, and Release; if the system does not expose the host (iOS earlier than 26.4), OSG simply stays open as before. / **所有版本都能自动跳回原 App**:此前识别宿主 App 的逻辑只在 Debug 构建中编译,TestFlight 和 App Store 版本不知道你正在哪个 App 里输入,启动语音会话后无法自动跳回。现在 Debug、TestFlight、正式版都会识别;系统不提供宿主信息时(低于 iOS 26.4)与之前一样停留在 OSG。
## [2.1.6] - 2026-10-27
### Added
- **Translate to your target language**: when the copied text is in your device language and a translation target language is set, the skill bar shows a "Translate <language>" button (for example "翻译英语" / "To Japanese") that translates the copy into that target. It is hidden when translation is off or the target is the same language as the text. / **翻译为目标语言**:复制的文字是设备语言、且已设置翻译目标语言时,技能栏会出现「翻译 XX」按钮(如「翻译英语」/「To Japanese」),点击后把内容译成目标语言;翻译关闭,或目标语言与原文相同时不显示。
- **Polish foreign-language text**: when the copied text is in another language, the skill bar adds a "Polish" button that fixes grammar, improves wording, and polishes the text in one pass while keeping the original language. Automatic translation of foreign text keeps using the existing "auto translate on keyboard open" switch. / **优化外语文本**:复制的文字是其他语言时,技能栏新增「优化」按钮,一次完成语法纠错、措辞优化和润色,结果保持原语言。外语内容的自动翻译仍使用现有的「打开键盘自动翻译」开关。
### Changed
- **iPhone Duo keyboard height**: on the unfolded inner display the keyboard keeps the iPhone key layout but uses rows sized to the system keyboard, so it is 260 pt tall instead of 281 pt and sits close to the height of Apple's keyboard. iPad, other iPhones, and the folded outer display keep their current layout. / **iPhone Duo 键盘高度**:展开后的内屏上,键盘保留 iPhone 键位,但行高按系统键盘设定,高度由 281 pt 降为 260 pt,接近系统键盘。iPad、其他 iPhone 以及合上后的外屏保持原有布局。
- **Voice keys on wide screens**: on iPad and the unfolded iPhone Duo, the voice keyboard replaces the round buttons with one row of system-style keys along the bottom, as tall as the typing keys: space, edit, send, undo, and delete (mirrored by the left/right-hand setting). Space, edit, undo, and delete share one width and send is twice as wide and centered, filling the row edge to edge. Edit and undo are always shown and grey out when unavailable, and while dictation or AI is running the row dims instead of disappearing. The voice surface now spans the same width as the typing keys, so the logo, tabs, and top-right buttons no longer shift when switching tabs. Other iPhones and the folded outer display keep the round buttons. The voice keyboard no longer shows a globe key on any device; switch keyboards from the typing tab. / **宽屏语音按键**:在 iPad 和展开的 iPhone Duo 上,语音键盘把圆形按钮换成底部一排与打字键同高的系统风格按键:空格、编辑、发送、撤销、删除(随左右手设置镜像)。空格、编辑、撤销、删除四键等宽,发送键为其两倍宽且居中,整排铺满;编辑和撤销常驻,不可用时变灰;听写或 AI 运行时整排变暗而不是消失。语音界面与打字键区同宽,切换标签时 Logo、标签栏和右上角按钮不再左右跳动。其他 iPhone 和合上后的外屏仍使用圆形按钮。语音键盘在所有设备上都不再显示地球键,切换键盘请在打字页进行。
- **Shorter keyboard in landscape**: on any iPhone held sideways, including the folded iPhone Duo, the keyboard is 244 pt tall instead of 281 pt, so the app above keeps more of the screen. Typing keys are 40 pt tall with the system-style half-key indent on the second row, and the voice keyboard uses the same bottom row of keys as iPad and the unfolded Duo. Portrait keyboards are unchanged. / **横屏键盘更矮**:任意 iPhone 横放时(包括合上的 iPhone Duo),键盘高度从 281 pt 降到 244 pt,上方的 App 能多露出一些内容。打字键高 40 pt,第二排按系统键盘的方式缩进半个键;语音键盘改用与 iPad、展开的 Duo 相同的底部按键排布。竖屏键盘不变。
- **Centred mic on the voice keyboard**: on every iPhone, the iPhone Duo, and iPad, the mic now sits centred between the top bar and the bottom of the keyboard — the space above it matches the gap plus the bottom row below it — instead of sitting low with extra space piled up above. The three-line live transcript above the mic always keeps its room. / **语音键盘麦克风居中**:所有 iPhone、iPhone Duo 和 iPad 上,麦克风改为在顶栏与键盘底部之间居中:上方空间与下方间距加底排的高度相等,不再把多余高度都堆在麦克风上方、让它偏低。麦克风上方始终保留三行实时听写的空间。
- **Clipboard skills only in the top bar**: the circular skill buttons above the mic are gone on every iPhone and iPad, including onboarding practice. Clipboard skills now appear only as text buttons in the keyboard's top bar, beside the logo; during onboarding the skill being practised shows there and the practice hint stays in the middle of the voice keyboard. / **剪贴板技能只在顶栏**:麦克风上方的圆形技能按钮在所有 iPhone 和 iPad 上都已移除,新手引导练习也不例外。剪贴板技能只以文字按钮形式出现在键盘顶栏 logo 右侧;新手引导时,正在练习的技能显示在顶栏,语音键盘中间保留练习提示。
- **Wide-layout top spacing follows the status bar**: the extra room above the sidebar logo and the Home title is now added only when no status bar sits above the content (the unfolded iPhone Duo held sideways) instead of on every iPhone, so the portrait inner display no longer stacks it on top of its status bar. iPad is unchanged. / **宽屏顶部间距跟随状态栏**:侧边栏 logo 和首页标题上方的额外间距,改为只在内容上方没有状态栏时添加(展开的 iPhone Duo 横放时),不再按“是不是 iPhone”判断;内屏竖放时不再在状态栏之下重复留白。iPad 不变。
- **Clipboard empty state**: an empty clipboard panel now shows an icon and a line explaining that copied text appears there. / **剪贴板空状态**:剪贴板面板为空时显示图标,并说明复制的文字会出现在这里。
- **Privacy policy URL**: the in-app privacy policy link now points to `https://osg.osglab.com/privacy/` on the mainland-hosted OSG website instead of the GitHub Pages address, so the China App Store storefront can reach a policy served from an ICP-filed domain. The policy text now names the correct site and notes that the in-app What's New page is served from `download.osglab.com` and does not run Google Analytics. / **隐私政策链接**:App 内隐私政策改指向境内官网的 `https://osg.osglab.com/privacy/`,不再使用 GitHub Pages 地址,使中国区 App Store 能访问备案域名下的政策页面。政策文本同步更正了站点名称,并说明 App 内「更新说明」页面由 `download.osglab.com` 提供、不运行 Google Analytics。
- **ICP number in the site footer**: the product page now shows the MIIT filing number on its own centered line below the footer, linked to `beian.miit.gov.cn`, as required for sites hosted in mainland China. Its styling lives in a `<style>` block inside `index.html` rather than `assets/site.css`, so the number stays correctly centered and sized even if only the HTML file is deployed or the stylesheet is still cached. / **官网页脚展示备案号**:产品页页脚新增独立居中的一行工信部备案号,并链接到 `beian.miit.gov.cn`,满足境内网站备案展示要求。样式写在 `index.html` 内嵌的 `<style>` 块里而非 `assets/site.css`,这样只部署 HTML 文件、或样式表仍在缓存时,备案号依然居中且字号正确。
- **Monochrome sidebar logo**: the sidebar logo on iPad, unfolded iPhone Duo, and Mac now uses the primary text color (black in light mode, white in dark mode) instead of brand green, matching the iPhone Home logo. / **侧边栏 logo 改为单色**:iPad、展开的 iPhone Duo 和 Mac 的侧边栏 logo 改用主文字色(浅色模式黑色、深色模式白色),不再是品牌绿,与 iPhone 首页 logo 一致。
- **One account card on Home**: Home now shows at most one account or setup card instead of a status card and an account card side by side. Missing permissions, a saved invitation, signing in for credits, choosing between credits and your own API key, running out of credits, and a credits connection problem each get one focused card above the usage stats; once everything works, that spot holds a compact credit summary (or a one-line “sign in for credits” row) instead. “Use OSG Credits” on Home goes through the same cloud-sharing consent as Settings. Voice-session status moves into a small capsule under the logo, which also names the active service when ready. / **首页只保留一张账号卡片**:首页最多只显示一张账号或设置卡片,不再同时出现状态卡和账号卡。缺少权限、已保存邀请码、登录使用积分、在积分与自己的 API Key 之间选择、积分用完、积分连接异常,各自对应一张卡片,放在用量统计上方;一切就绪后,同一位置改为紧凑的积分摘要(或一行「登录使用积分」入口)。首页的「使用 OSG 积分」与设置页一样,需要先同意云端数据共享说明。语音会话状态移到 logo 下方的小胶囊里,就绪时同时显示当前使用的服务。
- **iPhone Duo layout**: Home picks its layout from the width it actually gets, so an unfolded iPhone Duo uses the wide dashboard even when the system reports a compact size class, and folding it switches back to the phone layout; the account card tightens on the short folded screen. / **iPhone Duo 布局**:首页按实际可用宽度选择布局,展开的 iPhone Duo 即使系统报告紧凑尺寸也使用宽屏布局,合上后切回手机布局;外屏较矮时账号卡片会更紧凑。
### Fixed
- **Keyboard width tracking**: the keyboard now records its width whenever it crosses a layout threshold, not only on iPad, so the compact voice layout for narrow keyboards (Display Zoom on small iPhones, iPad Slide Over) actually applies, and the voice keys on the unfolded iPhone Duo no longer fall back to round buttons when the first layout pass reports a stale width. / **键盘宽度记录**:键盘宽度跨过任一布局门槛时都会被记录,不再只在 iPad 上记录。因此窄键盘(小屏 iPhone 开启“放大显示”、iPad 侧拉)的紧凑语音布局能真正生效;展开的 iPhone Duo 在首次布局拿到过时宽度时,语音按键也不会再退回圆形按钮。
- **Missing globe key**: the keyboard now asks the system whether it has to draw its own globe key instead of assuming only iPad does, so the unfolded iPhone Duo — and iPhones with a Home button — get a globe key for switching keyboards. Face ID iPhones keep using the system switch below the keyboard. / **地球键缺失**:键盘改为询问系统是否需要自己提供地球键,不再假定只有 iPad 需要,因此展开的 iPhone Duo(以及带 Home 键的 iPhone)也会显示用于切换键盘的地球键;Face ID 机型仍使用键盘下方的系统切换键。
- **Sidebar toolbar**: the wide layout's bottom status strip becomes a compact toolbar at the bottom of the sidebar — a microphone button starts or ends the voice session, the engine button opens AI service settings, and the globe button picks a translation language in place — with the session state as one word beside them; “Don't translate” no longer shows as a raw key. / **侧边栏工具栏**:宽屏布局的底部状态栏改为侧边栏底部的一排紧凑图标:麦克风按钮开启或结束语音会话,引擎按钮进入 AI 服务设置,地球按钮就地选择翻译语言,旁边用一个词显示会话状态;「不翻译」也不再显示成原始的文案 key。
- **iPhone Duo top spacing**: the unfolded layout adds room above the sidebar logo and the Home title so they no longer crowd the rounded display corner. / **iPhone Duo 顶部间距**:展开布局在侧边栏 logo 和首页标题上方留出更多空间,不再挤在屏幕圆角下。
- **Sidebar stays open**: the wide layout no longer offers a hide-sidebar button, so the sidebar can't get stuck hidden on iPhone Duo. / **侧边栏常驻**:宽屏布局不再提供隐藏侧边栏的按钮,iPhone Duo 上不会再出现侧边栏收起后打不开的情况。
- **Main window crash on iOS 27**: the app no longer crashes when SwiftUI redraws the main window from a background rendering thread while the main thread is busy. / **iOS 27 主界面崩溃**:主线程繁忙、SwiftUI 在后台渲染线程重绘主窗口时,App 不再崩溃。
## [2.1.5] - 2026-10-25
### Added
- **QR invitation poster**: the Invite button now opens a minimal poster that automatically matches light or dark appearance, uses the current App Icon, names the inviter (falling back to “OSG User”), and shows the QR code plus the short invitation code in Chinese or English. Tap the poster to show the code full screen at full brightness for face-to-face scanning; your brightness returns when you close it. You can also save the poster to Photos (add-only access), share the image, or copy the link. On Mac, the account page shows the same poster in a popover and can save it as a PNG. / **二维码邀请海报**:「邀请」按钮会打开自动适配黑白外观的极简海报,使用当前 App Icon,显示邀请人用户名(未设置时为「OSG 用户」),并以中英文展示二维码与短邀请码;点海报可全屏显示二维码并自动调到最亮,方便当面扫码,关闭后恢复原亮度;也可保存到相册(仅申请添加权限)、分享图片或复制链接。Mac 账户页以弹出面板展示同款海报,并可存为 PNG。
- **8-character invitation codes**: every account now has a short code such as `K7M2-Q9XA`, printed on the poster, the Mac poster popover, and the invitation landing page. The code skips look-alike characters (0/O, 1/I/L), so it can be read aloud. Tap “I have a code” to type it, or paste an invitation link or a whole chat message instead; the clipboard is never read on its own. A friend who installed from the App Store therefore still gets credited. For the first 7 days after registering, and until an inviter is bound, the home card shows “Enter a code, or invite more friends for credits” with the “I have a code” button, and Invite moves to the bottom of that sheet; the window follows the server’s campaign deadline. The landing page copies the link and opens the App Store in one tap, and keeps the link-open paths that older app builds rely on. / **8 位邀请码**:每个账号都有一个形如 `K7M2-Q9XA` 的短邀请码,印在海报、Mac 海报面板与邀请落地页上;不含易混字符(0/O、1/I/L),念出来也不会输错。点「我有邀请码」即可输入,也可直接粘贴邀请链接或整段聊天消息;App 不会主动读取剪贴板。好友从 App Store 安装后仍能计入邀请。注册后 7 天内且尚未绑定邀请人时,首页卡片显示「输入邀请码,或邀请更多好友获取积分」与「我有邀请码」按钮,「邀请」入口移到弹窗底部;期限以服务器活动截止时间为准。落地页一键「复制邀请并下载」,并保留旧版本 App 依赖的链接打开方式。
- **Native Reminders and Calendar export**: Extract To-dos and Extract Events now write directly through EventKit in the main app instead of a companion Shortcut — no Shortcut install step, and Calendar is requested with write-only access so existing events are never read. The bundled `OSGExtractTodos` / `OSGExtractEvents` Shortcut resources are removed; Save to Notes still uses its companion Shortcut. / **原生提醒事项与日历写入**:「提取待办」与「提取日程」改由主 App 通过 EventKit 直接写入,不再需要安装配套捷径;日历仅申请「仅写入」权限,不会读取你已有的日程。内置的 `OSGExtractTodos` / `OSGExtractEvents` 捷径资源已移除;「存入备忘录」仍使用配套捷径。
- **Crash and hang reporting**: subscribe to MetricKit so crash, hang, CPU, and disk-write diagnostics — including keyboard-extension memory kills, which never surface as crashes in App Store Connect — are recorded on device and exported from Settings ▸ Diagnostics alongside Flow startup reports. App Store builds keep them on device; beta builds also upload them (see below). / **崩溃与卡顿上报**:接入 MetricKit,将崩溃、卡顿、CPU 与磁盘写入诊断(含在 App Store Connect 中从不显示为崩溃的键盘扩展内存回收)记录在本机,并可在「设置 ▸ 诊断」中与 Flow 启动报告一同导出。App Store 版本仅保留在本机,内测版本会另行上传(见下条)。
- **Keyboard memory relief**: release the Chinese engine, the English lexicon, and in-flight pipeline work at the 40 MiB and 48 MiB budget thresholds, and keep sampling memory for the whole presentation instead of only during startup. Relief sheds at most once per threshold until the footprint falls back below 36 MiB, and the hard shed waits while you are mid-composition, so the keyboard never swaps surfaces out from under an unfinished syllable. / **键盘内存主动降级**:在 40 MiB 与 48 MiB 预算阈值主动释放中文引擎、英文词库与进行中的管线任务,并在整个键盘展示期间持续采样内存,而非仅在启动阶段采样。每个阈值在内存回落到 36 MiB 以下前只释放一次,且正在拼字时会推迟重度释放,不会在音节未完成时切走输入界面。
- **Beta crash upload**: internal builds (local Debug and TestFlight) batch MetricKit crash and hang reports to `POST /v1/analytics/diagnostics` so keyboard memory shutdowns can be investigated without asking a tester to export them by hand, with a Settings toggle, per-report deduplication, retry backoff, and a circuit breaker while the backend route is unavailable. Reports reuse the analytics installation identifier and are skipped entirely when analytics are switched off; App Store builds never upload. / **内测版崩溃上传**:内测版本(本地 Debug 与 TestFlight)将 MetricKit 崩溃与卡顿报告批量发送至 `POST /v1/analytics/diagnostics`,无需测试员手动导出即可排查键盘内存回收问题;配有设置开关、逐份去重、失败退避重试,以及后端路由不可用时的熔断。报告复用统计的安装标识,关闭统计时完全不上传;App Store 版本不会上传。
- **App Group wiring check**: gate CI on a static check that the App Group identifier and the shared Keychain access-group ordering agree across the app, the keyboard extension, the Mac target, and `project.yml`. / **App Group 配置校验**:在 CI 中静态校验 App Group 标识符与共享 Keychain 访问组顺序在主 App、键盘扩展、Mac 端及 `project.yml` 之间保持一致。
### Changed
- **Dictation progress on the microphone**: while a dictation is being recognized and polished, the microphone capsule fills from left to right in the brand colour instead of showing a spinner. The fill steps forward when recognition finishes, slows as it nears the end, and completes only when the text is inserted; the polish estimate adapts to how long polishing usually takes on this device. / **麦克风上的听写进度**:听写识别与润色期间,麦克风胶囊改为从左到右填充品牌色,取代原来的转圈。识别完成时进度向前跳一段,越接近结束走得越慢,只有文字真正插入时才填满;润色耗时的预估会按这台设备的实际情况自动校准。
- **Messaging shift to AI voice keyboard / voice double / agentic skills**: README and the product page now lead with “Say it. It’s typed. Speaks like you. Acts for you.” — the personalization story (learned style, three-tone replies) moves ahead of typing, clipboard skills are framed as agentic actions, and typing becomes the fallback capability. / **宣传定位转向 AI 语音键盘 / 嘴替 / Agentic 技能**:README 与产品页改用「开口即文字。会说你的话,会办你的事。」定位;个人化卖点(专属风格、三语气回复)提前,剪贴板技能表述升级为 Agentic 行动,打字退为兜底能力。
- **Product page redesign**: rebuild the GitHub Pages home as a dark product stage with the new Icon Composer app icon, the three-device hero visual, and real on-device videos and UI cards replacing hand-drawn mockups; add 2.1 selling points (personal style, three-tone replies, monthly usage calendar, EventKit skills export, guided trials and credit rewards), a TestFlight link, and self-hosted inline SVG icons; bump page references to 2.1.0. / **产品页重设计**:GitHub Pages 主页改为深色产品舞台,启用 Icon Composer 新图标与三端同框主视觉,以实机视频与真实界面卡片取代手绘示意;补齐 2.1 卖点(专属风格、三语气个性回复、月度用量日历、EventKit 技能写入、引导体验与积分奖励),新增 TestFlight 入口,图标改为自托管内联 SVG;页面版本号更新至 2.1.0。
- **Product page redesign**: rebuild the GitHub Pages home as a dark product stage with the new Icon Composer app icon, the three-device hero visual, and real on-device videos and UI cards replacing hand-drawn mockups; add 2.1 selling points (personal style, three-tone replies, monthly usage calendar, EventKit skills export, guided trials and credit rewards), a TestFlight link, and self-hosted inline SVG icons; bump page references to 2.1.5. / **产品页重设计**:GitHub Pages 主页改为深色产品舞台,启用 Icon Composer 新图标与三端同框主视觉,以实机视频与真实界面卡片取代手绘示意;补齐 2.1 卖点(专属风格、三语气个性回复、月度用量日历、EventKit 技能写入、引导体验与积分奖励),新增 TestFlight 入口,图标改为自托管内联 SVG;页面版本号更新至 2.1.5。
- **Host-only analytics recording**: the keyboard extension no longer opens the analytics or keyboard-usage SQLite queues. The host app is the only process that records and uploads events, so hiding the keyboard cannot hold WAL locks and get killed as `0xdead10cc`. Keyboard-activated and per-keystroke usage events are no longer collected. / **仅主 App 记埋点**:键盘扩展不再打开统计与键盘用量 SQLite 队列;开库、记账与上传只在主 App 进行,收起键盘时不会因 WAL 锁被系统以 `0xdead10cc` 终止。不再采集「键盘激活」和逐次按键用量。
- **Lighter keyboard redraws while dictating**: the keyboard now redraws only the parts whose data changed. The microphone level (5 times a second) updates just the waveform bars, and live recognized text updates just the status line, instead of rebuilding the whole voice panel each time. / **听写时键盘重绘更轻**:键盘现在只重绘数据有变化的部分。麦克风音量(每秒 5 次)只刷新波形条,实时识别文字只刷新状态行,不再每次都重建整个语音面板。
@@ -35,17 +73,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Auto-reply storms from Universal Clipboard**: copying on a nearby Mac re-announces one copy under several pasteboard changeCounts, and each re-announcement re-armed auto reply/translate and the auto-reply guide — including on every later keyboard open, because re-ingesting the same text kept refreshing its 30-second hint window. A capture whose text is identical to the newest history entry is now treated as the same logical copy (bookkeeping only, no re-analysis), and auto actions additionally dedupe on a content fingerprint within a 30-second window. The same text fires once; copying something else and then re-copying the text still triggers. / **跨端剪贴板引发自动回复风暴**:在附近 Mac 上复制一次会被系统以多个 changeCount 重复宣告,且每次宣告都会重新武装自动回复/翻译与自动回复引导——由于同文本被重复入库并不断刷新 30 秒提示窗口,之后每次打开键盘也会再触发。现在,与最新历史条目文本相同的采集会被视为同一次复制(仅更新记账,不重新分析),自动动作再叠加内容指纹 + 30 秒窗口去重。同一文本只触发一次;复制其他内容后再重新复制该文本仍可触发。
- **Style card captions**: resolve each built-in style’s short description through the in-app language override, so switching App Language updates the caption instead of showing a localization key or the system language. / **风格卡片说明**:内置风格的说明小字改为走应用内语言设置;切换 App 语言后说明会跟着变,而不再显示本地化 key 或跟随系统语言。
- **Personal reply style keeps learning**: regenerating used only the newest ~2,500 characters, so new dictation pushed earlier learning out instead of adding to it. Regeneration is now incremental: it merges what the style already learned (kept on this device only) with dictation and reply choices since the last generation, unlocks after 500 new characters, and the card shows the cumulative character count plus progress since the last generation. Traits that stop getting new support fade out over a few rounds. A style without saved learning (older builds or another device) is relearned in full and replaced. / **个人回复风格持续学习**:此前重新生成只使用最新约 2,500 字,新听写会把早期学习挤掉而非叠加。现在改为增量重新生成:把该风格已学到的特征(仅保存在本机)与上次生成后的新听写和回复选择合并,新增满 500 字即可重新生成;卡片显示累计学习字数与自上次生成后的新增进度。长期没有新证据支持的特征会在几轮后自然淘汰。没有本机学习记录的风格(旧版本或其他设备生成)会全量重新学习并替换。
## [2.2.0] - 2026-09-18
### Added
- **Native Reminders and Calendar export**: Extract To-dos and Extract Events now write directly through EventKit in the main app instead of a companion Shortcut — no Shortcut install step, and Calendar is requested with write-only access so existing events are never read. The bundled `OSGExtractTodos` / `OSGExtractEvents` Shortcut resources are removed; Save to Notes still uses its companion Shortcut. / **原生提醒事项与日历写入**:「提取待办」与「提取日程」改由主 App 通过 EventKit 直接写入,不再需要安装配套捷径;日历仅申请「仅写入」权限,不会读取你已有的日程。内置的 `OSGExtractTodos` / `OSGExtractEvents` 捷径资源已移除;「存入备忘录」仍使用配套捷径。
- **Crash and hang reporting**: subscribe to MetricKit so crash, hang, CPU, and disk-write diagnostics — including keyboard-extension memory kills, which never surface as crashes in App Store Connect — are recorded on device and exported from Settings ▸ Diagnostics alongside Flow startup reports. App Store builds keep them on device; beta builds also upload them (see below). / **崩溃与卡顿上报**:接入 MetricKit,将崩溃、卡顿、CPU 与磁盘写入诊断(含在 App Store Connect 中从不显示为崩溃的键盘扩展内存回收)记录在本机,并可在「设置 ▸ 诊断」中与 Flow 启动报告一同导出。App Store 版本仅保留在本机,内测版本会另行上传(见下条)。
- **Keyboard memory relief**: release the Chinese engine, the English lexicon, and in-flight pipeline work at the 40 MiB and 48 MiB budget thresholds, and keep sampling memory for the whole presentation instead of only during startup. Relief sheds at most once per threshold until the footprint falls back below 36 MiB, and the hard shed waits while you are mid-composition, so the keyboard never swaps surfaces out from under an unfinished syllable. / **键盘内存主动降级**:在 40 MiB 与 48 MiB 预算阈值主动释放中文引擎、英文词库与进行中的管线任务,并在整个键盘展示期间持续采样内存,而非仅在启动阶段采样。每个阈值在内存回落到 36 MiB 以下前只释放一次,且正在拼字时会推迟重度释放,不会在音节未完成时切走输入界面。
- **Beta crash upload**: internal builds (local Debug and TestFlight) batch MetricKit crash and hang reports to `POST /v1/analytics/diagnostics` so keyboard memory shutdowns can be investigated without asking a tester to export them by hand, with a Settings toggle, per-report deduplication, retry backoff, and a circuit breaker while the backend route is unavailable. Reports reuse the analytics installation identifier and are skipped entirely when analytics are switched off; App Store builds never upload. / **内测版崩溃上传**:内测版本(本地 Debug 与 TestFlight)将 MetricKit 崩溃与卡顿报告批量发送至 `POST /v1/analytics/diagnostics`,无需测试员手动导出即可排查键盘内存回收问题;配有设置开关、逐份去重、失败退避重试,以及后端路由不可用时的熔断。报告复用统计的安装标识,关闭统计时完全不上传;App Store 版本不会上传。
- **App Group wiring check**: gate CI on a static check that the App Group identifier and the shared Keychain access-group ordering agree across the app, the keyboard extension, the Mac target, and `project.yml`. / **App Group 配置校验**:在 CI 中静态校验 App Group 标识符与共享 Keychain 访问组顺序在主 App、键盘扩展、Mac 端及 `project.yml` 之间保持一致。
### Fixed
- **App Group error screen**: resolve localized text without the App Group so the screen shown when that container is missing renders its message instead of crashing on launch — the one situation it exists to handle. / **App Group 错误页**:本地化文案不再依赖 App Group,容器缺失时该页面可正常显示提示,而不是在启动时崩溃——这正是它存在的场景。
- **Dictation preview and Mac recording crashes**: install audio taps with the input node's live format and resample adaptively through one shared converter, so changing the audio route mid-recording no longer raises an uncatchable format-mismatch exception. / **听写预览与 Mac 录音崩溃**:音频采集改用输入节点的实时格式,并通过统一的自适应重采样器转换,录音过程中切换音频线路不再触发不可捕获的格式不匹配异常。
- **Degenerate audio routes**: clamp the resampled frame count before converting it, so a hardware rate near zero drops one frame instead of trapping on the realtime audio thread. / **异常音频线路**:重采样帧数在转换前先行钳制,采样率接近零时仅丢弃一帧,而不会在实时音频线程上触发陷阱。