Files
OSGKeyboard/docs/mac-parity-audit-2026-08-23.md
2026-08-25 13:42:00 +08:00

76 KiB
Raw Permalink Blame History

OSGKeyboard · iOS 完整检查 + macOS 差距与移植建议报告

报告日期2026-08-23 当前版本2.0.1build 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) 方法:只读代码考古,证据来自 project.yml 共享 / 排除清单 + 文件结构 + 关键源码摘录 + CHANGELOG.md


TL;DR

维度 iOS macOS 结论
产品形态 宿主 App + 自定义键盘扩展 单窗口菜单栏 App + 浮动 HUD 不同形态——Mac 不是键盘,是"按住 Option 说话→⌘V 注入"
核心流水线 Flow 跨进程会话 + SpeechAnalyzer 单一进程 + MLX Audio Qwen3 流式 架构不可同构
LLM 润色 完整(PolishingService 完整(直接复用 iOS PolishingService 完全对齐
本地 ASR iOS 26 SpeechAnalyzer + DictationTranscriber MLX Qwen3-ASR 0.6B/1.7B 4-bit + Apple Speech 兜底 完全不同的技术栈
云 ASR 7 家供应商 + 流式 完全对齐(同 CloudASRClientFactory 完全对齐
个人词典 完全对齐
历史 / 使用统计 iCloud KVS 共用) 完全对齐
润色样式 + 学习 完全对齐
iCloud 同步 (5 路:设置/词典/历史/使用/样式) 5 路全部对齐 完全对齐
提示页 (Tip) 完全对齐
自定义语言模型 CLM UI + 后台 ⚠️ 后台调用了,无 UI 部分缺失
剪贴板历史 + AI 技能 共享代码已编译但无 Mac UI 完全缺失
AI 提示轮播 / 上下文技能 共享代码已编译但无 Mac UI 完全缺失
Flow 跨进程会话 / PiP Mac 形态不需要
自定义键盘 (Rime/拼音/英文) Typing/** 全排除) Mac 形态不需要
OSG 账户 (Sign in with Apple + 积分 + 推荐) 完全缺失
App Attest (无对应 entitlement 完全缺失
一方分析 (Analytics) 108 测试覆盖) (共享代码已编译但无任何调用 完全缺失
助手指令 (Shortcuts) 完全缺失
屏幕唤醒 / 锁 / Live Activity Mac 形态不需要

最关键的发现

  1. macOS 复用策略 = 源码级 include。Mac 不 import OSGKeyboardShared——project.yml:570-602 把 Shared + HostSupport 的 .swift 文件直接编进 Mac target,再用 excludes: 列表过滤 iOS-only 文件。整个框架里没有 #if os(macOS) 条件编译Mac target 内部有几处 import AppKit 守卫,是防御性的)。
  2. LLM 润色、云 ASR、iCloud 同步、本地 ASR 模型管理、模型目录、个人词典、润色样式、提示页、Onboarding——这 9 大块是 iOS / Mac 完全对齐的。差异主要在"宿主 App 才有"的系统集成(Account、App Attest、Analytics、Shortcuts、Flow 跨进程会话)。
  3. "共享代码已编进 Mac 但零调用"的浪费OSGKeyboardShared/Features/Account/Features/ManagedGateway/Features/Analytics/Services/AIClipboard*.swiftServices/AIHint*.swiftServices/AIUserSkill*.swift —— 这些文件编译进了 Mac 二进制但没有任何 Mac 文件引用。编译器开销 + 二进制体积 + 维护面全白付。要么真正接进 Mac,要么从 Mac target 排除。
  4. Mac 真正独占的体验是 MLX 流式 ASR。这是 iOS 受限于 ARM NEON + iOS 26 平台绑定做不到的能力,恰好把 Mac 的 Apple Silicon 算力用足。
  5. 移植优先级不应是"把 iOS 全搬到 Mac"。Mac 是"按住说话→润色→注入"工具,自然形态完全不同。**真正应该补的是:剪贴板 AI、CLM 管理、Account 体系、Analytics。**其他(Flow、自定义键盘)不应该硬塞进 Mac。

1. iOS 端完整状态

1.1 Target 拓扑

Target 平台 类型 .swift 数 备注
OSGKeyboard iOS 26 App 88 宿主 App,沙盒、StoreKit、iCloud、App Attest
OSGKeyboardExt iOS 26 App Extension (键盘服务) 27 RequestsOpenAccess: true
OSGKeyboardShared iOS 26 Framework 230 跨进程共用 + 复用到 Mac
OSGKeyboardHostSupport iOS 26 Framework 29 宿主专用(Speech/AVFoundation/Charts/StoreKit
OSGKeyboardMac macOS 15+ App 39 菜单栏单窗口,沙盒关闭Accessibility 需要)
OSGKeyboardMacTests macOS 15+ Unit Test 3 挂在 Mac App bundle 内
OSGKeyboardTests iOS 26 Unit Test 108 覆盖 host + shared + hostsupport
OSGKeyboardExtTests iOS 26 Unit Test 16 覆盖 ext + shared
OSGKeyboardUITests iOS 26 UI Test TestFlight 截图自动化

1.2 iOS Host App (OSGKeyboard/) — 用户可见的所有功能

导航4 Tab (Keyboard / Skills / Styles / Settings) on iPhoneiPad 自动切换到 NavigationSplitView + 240pt 侧栏 + WideStatusFooter

7 大用户场景

  1. OnboardingViews/OnboardingExperienceView.swift)—— 7 步:欢迎 → 权限 → 键盘安装 → 切换引导 → 4-feature 实践(语音/翻译/回复/AI)→ 登录奖励 → 完成。匿名实践用 OOBEClientInfrastructure 拿服务器发放的体验金。
  2. Home 仪表盘Views/HomeView.swift:122)—— 7 天柱状图 + 指标瓦片 + Flow 连接卡 + 词典/历史卡 + 中英文高频词条 chip。
  3. SettingsViews/SettingsView.swift:30)—— 7 个子面板:账户、AI 代理、剪贴板、Locale、润色强度、翻译目标、语音识别、文本润色、通用、关于。NavigationStack + SettingsRoute 枚举。
  4. Account CenterViews/Account/AccountCenterView.swift:11)—— 已登录摘要、积分 + 等级、StoreKit 商品列表、购买历史链接、登出 / 删号 (reauth)、推荐链接。下拉刷新。
  5. Polish StylesViews/PolishStylesView.swift:11)—— 内置/社区/自定义样式网格 + 编辑器 sheet + "从历史学习"按钮。
  6. AI Agent SkillsViews/AIAgentSkillsView.swift:12)—— 已装/未装技能卡,安装调起 shortcuts:// 或 bundled .shortcut 文件。
  7. Legal / 隐私—— PrivacyPolicyView / OpenSourceLicensesView / 远程 web view。

宿主端 ServicesOSGKeyboard/Services/ 关键件:

文件 职责 LOC 估算
FlowSessionManager.swift 唯一拥有者——AVAudioSession 激活、连续 .playAndRecord 捕获、ASR 选择、后处理、PolishingService 调用、AI 模式、App Group 写、utterance token、launch reconciliation、start/stop/end session ~3500
FlowPictureInPictureController.swift PiP keep-aliveAVPictureInPictureVideoCallViewController 0.1pt contentno audio session
FlowASRPostProcessor.swift 本地引擎转写后处理(LocalASRTranscriptCorrector.apply
FlowTerminationCoordinator.swift 进程退出时同步释放 mic、结束 LiveActivity
FlowAnalyticsOperationRegistry.swift 会话级分析去重,订阅 FlowSessionManager 事件
RimeDeploymentController.swift Rime 资源部署(host-owned
AIAgentShortcutInstaller.swift 调起 shortcuts:// 安装 iCloud 分享的技能 / bundled .shortcut
OOBEClientInfrastructure.swift 匿名 OOBE 体验金客户端
OfficialSkillCatalogRefreshService.swift 15 分钟 ETag-aware 刷新 https://account.osglab.com/v1/content/skills
AIHintRefreshService.swift 12 小时静默刷新远程提示包
AnalyticsHostService.swift BGTaskScheduler 注册 (com.osgkeyboard.ios.analytics-sync) + NWPathMonitor + 后台 drain
HostReturnService.swift 解析 FlowSessionBridge.pendingHostBundleId() 通过 HostAppURLRegistry 重开原 App
AppURLHandler.swift UIApplicationDelegate + UISceneDelegateosgkeyboard://startflow / https://osglab.com/i/... 通用链接 / didBecomeActiveNotification 反弹保留 sourceApplication
AppPermissions.swift AVAudioApplication.requestRecordPermission (iOS 17+) + SFSpeechRecognizer + PasteAccessResult 一次性 pasteAccessVerified

宿主端 Models 关键点

  • AppGroupConfiguration 完整 Codable blob —— 镜像到 App Group + iCloud KVS
  • ProviderConfig 8 家供应商预设(openai/ark/deepseek/qwen/zhipu/moonshot/siliconflow/groq

关键 iOS-only 调用

  • UIOpenURLContext.options.sourceApplication (iOS 26 only) —— 保留源 bundle idhost-return 白名单唯一路径)
  • AVAudioApplication.requestRecordPermission (iOS 17+)
  • SpeechAnalyzer + DictationTranscriber (iOS 26 only)
  • OSAllocatedUnfairLock (iOS 16+)
  • iOS 26 Icon Composer (OSGKeyboard/AppIcon.icon)
  • UIBackgroundModes: audio + BGTaskScheduler
  • applinks:osglab.com Universal Links
  • LSApplicationQueriesSchemes 白名单 50+ 国外 App scheme
  • SKIncludeConsumableInAppPurchaseHistory=true —— StoreKit 2 一次性商品进历史

iOS-only entitlements(在 OSGKeyboard.entitlements):

  • App Group group.com.osgkeyboard.shared(与扩展共享)
  • 两个 keychain-access-groupsshared + iOS-only
  • aps-environment: $(APP_ATTEST_ENVIRONMENT) ← xcconfig 注入(debug=development / release=production
  • Sign in with Apple Default
  • iCloud KVS $(TeamIdentifierPrefix)com.osgkeyboard.ios单字符串,非数组 —— 数组会破坏自动签名)
  • 音频输入
  • 关联域名 applinks:osglab.com

StoreKit 配置OSGKeyboard.storekit):4 个消耗型 —— 自愿打赏 ByRockyACoffee (¥28) + 3 档账户积分 500tks/1500tks/3000tks (¥8/¥18/¥28)。无订阅

1.3 iOS HostSupport 框架 (OSGKeyboardHostSupport/)

设计原则:唯一链接 Speech / AVFoundation / Charts / StoreKit 的 iOS 框架——键盘扩展链接它,把 jetsam 预算留给 Shared。

子目录

路径 内容
Services/ ASR + 云 ASR + Flow 捕获 + Dictation + Tip + CloudASR/ + Tip/
Models/ AudioBufferSnapshot+AVFoundation.swift —— AVAudioPCMBuffer ↔ 共享 AudioBufferSnapshot 适配
DesignSystem/ SevenDayUsageChart + UsageStatsCluster + SupportDeveloperSection
Features/Account/ AccountAPIClient / SignInWithApple / DeviceIntegrity (App Attest) / HostPrivateAccountKeychain / AccountModels / OOBEGrantProvisioningCoordinator

关键 Services

  • ASRService.swift:1 —— 公共协议 transcribe(stream:locale:) 返回 AsyncStream<ASREvent> (.capability/.partial/.final/.error)transcribeChunk(samples:locale:) 用于流水线 Flow 路径。ASRServiceFactory.make(store:) 返回 SpeechAnalyzerASR(本地)或 CloudASRService(云)。SpeechAnalyzerASRiOS 26 SpeechAnalyzer + DictationTranscriber + bundled SFCustomLanguageModelData,锁用 OSAllocatedUnfairLock,支持流水线 chunk 复用。
  • CloudASRService.swift:1 —— 云 ASR 前门。CloudASRClientFactory.make(...) 返回 8 个客户端:ZhipuCloudASRClient / AlibabaFunASRClient / BailianRealtimeASRClient / VolcengineCloudASRClient / OpenAIRealtimeASRClient / PromptCloudASRClient / ManagedVolcengineASRClient / UnsupportedCloudASRClient
  • LiveDictationController.swift:1 —— @MainActor ObservableObject,阶段机 idle/recording/processing/denied/error,自管 AVAudioEngine + AVAudioSession。生产键盘用它,生产用 FlowSessionManager + FlowContinuousCapture
  • FlowAudioSessionCoordinator.swift:1 —— 进程级 AVAudioSession + Flow 的 AVAudioEngine 拥有者。.voiceChat 模式。
  • CustomLanguageModelManager.swift —— 单例,准备 bundled OSGKeyboardCLM.bin 写进 App Group。指数退避 30s/120s/600s。重到 MainAppRoot.scheduleCLMWarmup 延后 45s 启动,Flow 忙时拒绝运行

HostSupport 持久化

  • CLM 编译产物 → App Group container
  • Account 令牌 → host-only keychain走 App Group
  • Managed AI 凭证 → 单独 GatewayGrantKeychainStore镜像"会话可用"布尔进 App Group
  • Tip 计数 → 标准 UserDefaults

1.4 iOS Keyboard Extension (OSGKeyboardExt/)

入口KeyboardViewController.swift:33-38 —— @objc(KeyboardViewController) @MainActor UIInputViewController单一 KeyboardState ObservableObject。

生命周期(KVC.swift

  • init → 启动 KeyboardExtensionMemoryTelemetrywarning 36MB / safe 40MB / critical 48MB
  • viewDidLoad (:148-207) → 设 showsSystemGlobeKey = isPadprimaryLanguage = "mis"(隐藏"English"副标题误导),读 TypingInputConfiguration.preferredSurfaceOnOpen(),调 refreshLayoutMode() + installKeyboardHeight() + configureDictationBehavior() + installServices() + installSwiftUI()
  • viewWillAppear (:256-293) → 标记 KeyboardSetupBridge.markExtensionAppearance(hasFullAccess:)、刷新 Flow/config、同步 onboarding、准备 KeyboardHapticFeedback generators
  • viewIsAppearing/viewDidAppear (:295-356) → 锁高度 lockPresentedKeyboardHeight(),禁系统手势延迟,记录 analytics
  • viewWillDisappear (:209-254) → 通知 AnalyticsExtensionService.keyboardWillDisappear()、取消 assistantFieldActionRefreshTask、使 editHintScheduler 失效、持久化最后 surface
  • textDidChange/selectionDidChangerefreshReturnKeyRole、同步 English 文档上下文
  • didReceiveMemoryWarning (:390-404) → 取消流水线、退出 typing 模式、强制回 voice surface —— typing 引擎是最大内存消费者

installServices (:419-547) 安装的 8 个 coordinator

  • EditHintSchedulermic 上方一句话提示)
  • KeyboardTextInserter(插入 Flow 转写 / undo / redo / 粘贴剪贴板)
  • KeyboardConfigSyncApp Group 配置 + Darwin observers + onboarding 镜像)
  • KeyboardFlowCoordinatorFlow start/stop + session monitor + watchdogs
  • LastInputEditCoordinator(长按 → 编辑上次输入)
  • AIKeyboardCoordinator(长按 → AI 模式)
  • ClipboardCaptureCoordinatorchangeCount 轮询,secure-field 抑制,建议条)
  • AnalyticsExtensionService

installStateActions (:551-655) 装的 30+ actionbeginRecording/endRecording/tapMic/cancelVoiceInput/beginEditLastInput/.../setMode/setLocale/setEngineMode/setTranslationTargetLocaleId/insertNewline/insertSpace/deleteBackward/undoLastInsertion/redoLastInsertion/copySelection/cutSelection/setSurface

手势模型

  • 长按 push-to-talk → RecordButtonGesturePolicy 阈值 longPressDuration = 0.45s
  • 点击 mic → 切换(RecordButton.tapAction
  • 重复删除 → RepeatingPressButton + RepeatingDeleteTiming80→50→30→15 ms 加速)
  • Shift 长按 → TypingSessionController.shiftHeld,双击 capsLock
  • 语言切换 → 3-tab KeyboardInputTabassistant / 中文 / 英文)
  • 触觉 → KeyboardHapticFeedback5 个 Taptic generator + 4 个按键角色 + KeyboardHapticIntensity off/light/strong
  • 声音 → UIDevice.playInputClick() + 系统音 1155 (delete),通过 @retroactive UIInputView: UIInputViewAudioFeedback 启用点击声
  • 主题 → 始终深色(Palette.dark 是 canonical

iOS-only 扩展调用UIInputViewController / UIInputView / UITextDocumentProxy / NSExtensionContext / UIPasteboard.general / UIImpactFeedbackGenerator / UIDevice.playInputClick() / AudioServicesPlaySystemSound(1155) / UIButton / UICollectionView + UICollectionViewCompositionalLayout / UIHostingController<KeyboardSurfaceRoot>

Host/Extension 边界数据AppGroupPersistor.load(into:) 读取):

  • providerId / baseURL / apiKeyKeychain.apiKeyOutcome 解析 shared keychain $(AppIdentifierPrefix)com.osgkeyboard.shared
  • model / modeId(恒为 polish/ localeId / engineModelocal/cloud
  • translationTargetLocaleId / handednessPreference(左/右手 delete↔space 互换)/ clipboardHistoryEnabled / clipboardCandidateBarEnabled
  • keyboardHapticIntensity / clipboardSkillSnapshot(启用技能 ID/ apiKeyAvailability

KeyboardConfigSync (KeyboardConfigSync.swift:24-62) 安装 4 个 FlowSessionDarwinObserversession.changed / command.changed / transcription.changed / host.ready.changed

1.5 iOS Shared Framework (OSGKeyboardShared/)

目录

路径 内容
Constants/AppGroup.swift App Group ID 唯一源
Core/Configuration/ ConfigurationStore 协议 + iOS 实现 AppGroupStore+ConfigurationStore.swift + LiveConfigurationStore
DesignSystem/ (9 文件) Theme / RecordButton / RecordButtonGesturePolicy / WaveformView / EditTextPager / CardPageLayout / SonicParticleField / ThemedRoot / UsageStatCard / UsageSurfaceCard
Features/Analytics/ (10 文件) AnalyticsClient / AnalyticsRepository (SQLite) / AnalyticsUploadCoordinator / KeyboardUsageRepository (跨进程 SQLite WAL) / ...
Features/ManagedGateway/ (8 文件) ManagedLLMClient / GatewayGrantCoordinator (actor) / GatewayGrantCredentialStore / ManagedGatewayAccountAccessPolicy / ManagedGatewayScopePolicy / ManagedGatewayQuestionRouter / ManagedGatewayModels / OOBEGatewayGrantCoordinator
Localization/SharedL10n.swift NSLocalizedString against Shared.strings;解析 AppUILanguage
Models/ (~50 文件) 见下
Resources/ ClipboardSemantics/{*.mlmodel,*.json} (6 CoreML) / PolishStyles/{manifest.json,builtin.*.json} (10 内置样式) / Typing/English/{english_lexicon.{bin,tsv},english_bigrams.tsv}
Services/ (~80 文件) 见下
Typing/ (24 文件) librime + 拼音 + 英文 autocorrect + 触屏 + 布局
Utilities/ (21 文件) 日志、错误模型、keychain、locale、Han 脚本、PCM/WAV、内存预算等

关键 Services 详解

  • PolishingService.swift:34-105 —— public actorpolish(_ raw:mode:systemPrompt:context:) -> PolishOutcome(text:qualityDegraded:polishStyleID:polishStylePrompt:)单一融合 LLM 调用:T1 自纠 → T2 填料清理 → T3 同音纠正 → T4 标点 → T5 结构 → 应用样式。Fun personality 在 heavy 强度切到"仅格式"共享核心。
  • LLMClient 协议 (Services/LLMClient.swift:91-150) —— polish(_:systemPrompt:timeout:) + complete(messages:tools:timeout:options:)。实现:LLMClientFactoryOpenAI 兼容)、AnthropicLLMClientAnthropic Messages API)、ResponsesAPILLMClientOpenAI Responses)、ManagedLLMClient(账户 grant 范围)、AIModeSearchFallbackClient(搜索→纯)。
  • Keychain.swift:15 —— kSecClassGenericPassword 读写 + iCloud 同步变体 + OnFirstUnlockThisDeviceOnly 本地变体 + XCTest 内存回退。
  • AIClipboardSkill.swift:44 —— 技能模型:id / systemImage / titleKey / kind.direct/.transform/.export/ isDefault / shortcutName + iCloud 分享 URL + bundled .shortcut 资源 / thinkingEnabled / customName/Summary/Prompt / requiresShortcut / isUserCreated / isOfficial / managedGatewayTaskKind / supportsReplyStyle
  • ClipboardHistoryStore.swift:10-84 —— App-Group-backed observable store。合并通过 ClipboardHistoryPolicy(去重、清洗、保留)。
  • ClipboardSemanticAnalyzer.swift:1-58 —— 本地:NLTokenizer/NLTagger + 6 CoreML 分类器(ClipboardSemantics/*.mlmodel)产生 language/dates/addresses/phoneNumbers/urls/personNames/organizationNames/sentiment/task/question/invitation/complaint/replyableMessage 意图。
  • ClipboardSkillSemanticRanker.swift:11-66 —— 用语义分析 + 偏好语言对完整目录排序,固定 generic Reply 兜底。Stateless / Sendable
  • SpeechHistoryStore.swift:10-78 —— iCloud-KVS 镜像 observableprompt-fingerprint dedup。
  • LocalASRModelManager.swift:11 —— macOS Qwen3-MLX 权重 Application-Support 安装管理器。状态持久化到 installed-manifest.jsonMac 专用iOS 用系统 SpeechAnalyzer)。
  • LocalASRBiasAdapter.swift:9 —— 分层偏差:PersonalDictionary.effectiveEntries + 内置 BuiltinLexiconIndex.topTerms(...);按前台 App bundle id 选 builtin-computer vs builtin-topXcode/VSCode/Android Studio/IntelliJ/AppCode/Sublime/Terminal/iTerm2/Warp)。
  • LocalASRTranscriptCorrector.swift:9-65 —— 确定式 alias→canonical 替换,最长匹配优先,ASCII whole-word 感知。
  • PolishStyleLearningService.swift:64 —— build(from:)SpeechHistoryEntry 历史生成 PolishStyleLearningCorpus5000 字符 requiredEffectiveCharacterCount)。
  • AIQuestionService.swift:20 —— actor AIConversationStore 保留 retainedConversationRounds 轮 / 对话。
  • AIEventExtraction / AIAddressExtraction / AITodoExtraction —— 解析 LLM 输出为 start|end|title|location / origin|destination / 列表。全部 fail-closed
  • HostAppURLRegistry —— 白名单 deep-link 路径(deployrimesettings/clipboardskill/run 等),host + ext 共用。
  • KeyboardSetupBridge —— markExtensionAppearance(hasFullAccess:) + OOBE 实践会话标志。
  • AppGroupConfigDarwin —— CFNotificationCentercom.osgkeyboard.config.changedhost 写完后)。
  • FlowSessionBridge + FlowSessionKeys —— 跨进程 mailbox 全部 App-Group keyflowHostReady / flowHostReadyAt / flowHeartbeat / flowSessionActive / flowSessionExpires / hostHeavy/At / hostGeneration / transcriptionResult / transcriptionPartial / transcriptionError{Kind,PolishWarning} / pendingKeyboardUtteranceId / lastPiPArmAttemptAt / flow.commandPayload.v1 / flow.commandJournalPayload.v2 / flow.resultPayload.v1 / flow.ackPayload.v1 / flow.startTransaction.v1 / flow.readyPayload.v1
  • SettingsCloudSync / SpeechHistoryCloudSync / UsageStatisticsCloudSync / AppCloudSync —— KVS 镜像;ICloudSyncPreferences + SyncDeviceID 保留每设备身份。
  • AIHintStore + AIHintPool + AIHintKeywordExtractor + AIHintKeywordCompressor + AIHintLocalCatalog —— 闲置热词轮播:App-Group-pack loader、合并远端 + 本地卡片、确定性 10 或 22 字符关键词抽取(zh/en)。
  • TranscriptionPolishFallback.swift —— LLM 润色失败或跳过时,交付原始转写 + 软警告。
  • WhatsNewDemoScenario —— "What's New" 时间轴 driverhostpeek/consume)和 extDEBUG)共用。

Polish 子系统详细

  • PolishContext (Models/PolishContext.swift)appContext + precedingText + followingText + fieldHints + dictionarySupplement + maxPrecedingChars=600 + maxFollowingChars=200
  • AppContext 5 种(code/email/chat/document/unknown
  • AppContextDetector (:30-116)3-fallback 链——textDocumentProxy.documentContextBeforeInput 启发式(iPad 沙盒安全;键盘读不到前台 bundle id)→ 30 分钟缓存 → 环境 blend
  • PolishStylePack (Models/PolishStylePack.swift:9-87)Codable, Equatable, Identifiable, Sendableid/name/prompt/allowsAddedEmoji/kind(.builtin/.user)/createdAt/updatedAt
  • PolishStyleLimitsmaximumUserPacks = 8 / maximumPromptCharacters = 6_000
  • 内置样式分两组:.practicallight/structured/formal/chat+ .fundating/flex/corp/diba/xhs

Typing 子系统Typing/):

  • LibrimeEngine.swift —— @MainActor 中文 IME,部署 3 套 schemaosg_pinyin / osg_double_pinyin_mspy / osg_double_pinyin_sogou
  • EnglishSuggestionEngine.swift —— 3 槽 QuickTypeverbatim / correction / completion),inVocabularyFrequencyGap = 250
  • EnglishLexicon.swift —— mmap 40k 词 english_lexicon.bin
  • EnglishSystemLexicon.swift —— #if canImport(UIKit) 守卫包 UITextChecker + UILexicon
  • EnglishQWERTYProximity.swift —— 空间编辑代价
  • TypingSessionController —— 拥有 typing surfacelanguage/page/shift state、English 引擎、librime 引擎、拼音/英文消歧、autocap、句号快捷、shadow preceding text、supplementaryWords、hot reload
  • TypingSurfaceMetrics (Models/TypingSurfaceMetrics.swift) + KeyboardChromeLayout —— 单一源:键行指标、底部动作行分数、总高 281ptiPad narrow/widenarrow 54pt / wide 76pt),wideIPadWidthThreshold = 1100
  • TypingInputConfiguration —— TypingInputSchema (3 case) + DefaultInputMode (voice/pinyin/english)
  • PeriodShortcut.swift —— iOS 句号快捷:letter→number 后第二个空格 0.45s 内变 ". " + arm sentence Shift
  • TypingAutocapitalization —— 镜像 UITextAutocapitalizationType

Models (~50 文件) 关键

  • AppGroupConfiguration —— 完整 Codable blob
  • SyncedAppSettingsV2 —— per-field SyncedField<T> LWW + broken-clock containment (6h skew horizon)
  • LLMProvider —— 8 家云预设
  • AIUserSkill / AIAgentSkillLayout / OfficialSkillCatalog / OfficialSkillDefinition —— 技能体系
  • PolishStylePack / PolishStyleCatalog / PolishStyleLearningCorpus
  • PersonalDictionary / +Merging / +ASRBias —— 跨设备词典,iCloud 合并;提供 hotwords / asrPromptBias / alibabaHotwordEntries / vocabularySyncFingerprint
  • ClipboardHistoryEntry / SpeechHistoryEntry / SyncedSpeechHistory
  • PolishContext / FieldHints / AppContext / AppUILanguage
  • ProviderConfig / CloudProviderRole / CloudASRModels / LocalASRModelCatalog / LocalASRCapabilities / LocalASRBiasPayload
  • TypingInputConfiguration / TypingSurfaceMetrics / KeyboardChromeLayout
  • HandednessPreference / KeyboardHapticIntensity / PolishIntensity / AIResponseLength
  • FlowUtteranceRequest / FlowUtteranceMode / FlowUtteranceChunkConfig / FlowHandoffPolicy / FlowAck / FlowResult / FlowCommand / FlowReadySnapshot / FlowStartTransaction / FlowTranscriptionError / FlowFieldContext
  • MicVoiceAvailability / +Keyboard —— mic 状态 + 派生
  • EditableInputReference —— 跨进程最后插入引用(10 min TTL、≤1200 graphemes、schema v1
  • EditSessionState —— 关闭状态机 .inactive/.preparing/.listening/.processing/.review/.applying/.appending/.failed
  • AISessionState —— .inactive/.idle/.preparing/.listening/.recognizing/.generating/.ready/.awaitingSend/.inserted/.sent/.failed

Utilities (21 文件)

  • OSGLog.swift —— subsystem + 类别(flow/clm/config/asr/keyboardExt
  • OSGDiag.swift —— NSLog + 内存快照(task_info
  • FlowTrace.swift —— [trace] stage=...
  • FlowPipelineDiagnostics.swift / FlowCaptureTailDrain.swift / FlowUtteranceEndCoordinator.swift / FlowUtterancePCMStore.swift
  • UtteranceStreamChunker.swift / UtteranceBatchFallbackPolicy.swift / UtteranceTranscriptGuard.swift / UtteranceTranscriptStitcher.swift / TranscriptOverlapUtilities.swift / TranscriptLanguageDetector.swift
  • ProgressiveDictationTranscriptAccumulator.swiftMac 排除
  • DictationTextComposer.swift / FinalChunkRecovery.swift / PCMSampleWavEncoder.swiftmono Float32 @16kHz → WAV
  • PromptXMLEscaping.swift / HanScript.swiftBMP 汉字谓词)
  • HostMemoryBudget.swift / KeyboardExtensionMemoryTelemetry.swiftMac 排除
  • ProviderDisplayName.swift / AppVersionDisplay.swift

iOS-only 排除(project.yml:570-586

DesignSystem/WaveformView.swift
DesignSystem/RecordButton.swift
DesignSystem/RecordButtonGesturePolicy.swift
Services/KeyboardState.swift
Services/KeyboardOpenSurfacePolicy.swift
Models/MicVoiceAvailability+Keyboard.swift
Models/TypingInputConfiguration.swift
Models/TypingSurfaceMetrics.swift
Typing/**
Utilities/ProgressiveDictationTranscriptAccumulator.swift

1.6 iOS 测试覆盖(108 + 16 文件)

OSGKeyboardTests/ (108 文件) —— host + shared + hostsupport 端到端:

  • ASR/云 ASRCloudASRServiceTests / CloudASRHTTPClientTests / CloudASRStreamingHelpersTests / CloudASRStreamingEventParsingTests / CloudASRTests / ASRConversionTests / LocalASRModelCatalogTests / LocalASRDownloadSourceSorterTests / LocalASRBiasAdapterTests / AlibabaVocabularySyncTests / FrequentTermStoreTests / PreviewASRControllerStateTests
  • 语音流水线VoicePipelinePerformanceTests / FlowPhysicalAudioStressTests / FlowReliabilityTests / FlowASRPostProcessorTests / ChunkedUtterancePipelineTests / FlowBudgetAndMergeTests / FlowCaptureTailDrainTests / FlowUtteranceEndCoordinatorTests / FlowUtterancePCMStoreTests / UtteranceBatchFallbackPolicyTests / UtteranceStreamChunkerTests / UtteranceTranscriptGuardTests / UtteranceTranscriptStitcherTests / ProgressiveDictationTranscriptAccumulatorTests / FlowSessionBridgeTests / FlowSessionManagerAnalyticsTests / FlowSessionPolicyTests / FlowHandoffPolicyTests / FlowPiPRecoveryPolicyTests / FlowStartTransactionPolicyTests / FlowHomePiPStatusPolicyTests / FlowKeyboardPoliciesTests / KeyboardExtensionMemoryBudgetTests
  • Polish/LLMPolishStylePackTests / PolishStyleLearningServiceTests / PolishPromptComposerQuestionTests / PolishOutputValidatorTests / IntelligentPolishTests / LLMClientTests / AIModeLLMClientTests
  • AI 特性AIQuestionServiceTests / AIEventExtractionTests / AIAddressExtractionTests / AINoteExportTests / AIHintKeywordExtractorTests / AIHintPoolTests / AIUserSkillTests / AIUserSkillStoreTests / AIAgentSkillLayoutTests / AIHistoryAndUsageTests / AssistantFieldActionTests / AISessionStateTests / AIClipboardPromptTests / AnalyticsAIOperationTests / AppleNaturalLanguageCapabilityTests / SpeechHistoryRevisionTests / SpeechHistoryDayDeletionTests / SpeechHistoryCloudSyncTests / PublicContentRefreshServiceTests
  • 剪贴板/工具ClipboardSkillSemanticRankerTests / ClipboardSemanticAnalyzerTests / ClipboardHistoryStoreTests / ClipboardHistoryPolicyTests / AccountCenterViewModelTests / AccountSnapshotLoaderTests / AccountAPIClientTests / AccountSignInCoordinatorTests / AccountCreditPurchaseManagerTests / AccountSecurityPrimitiveTests / ReferralProfileTests / EditTransactionStoreTests / EditableInputReferenceTests / EditLastInputPromptTests
  • iCloud 同步PersonalDictionaryCloudSyncTests / PersonalDictionaryMergeTests / SettingsCloudSyncTests / UsageStatisticsCloudSyncTests / KeyboardUsageRepositoryTests / KeyboardUsageModelTests / KeyboardUsageUploadCoordinatorTests / AnalyticsUploadCoordinatorTests / AnalyticsUploadSchedulingTests / AnalyticsRepositoryTests / AnalyticsModelTests / AnalyticsAttributionTests
  • 本地 ASR + 内存LocalASRModelCatalogTests / KeyboardExtensionMemoryBudgetTests
  • 设置 + 工具AppGroupConfigurationTests / AppGroupOnboardingStoreTests / ConfigurationStoreTests / HostAppURLRegistryTests / KeyboardTranslationConfigProtectionTests / KeychainTests / DeviceIntegrityTests / MicVoiceAvailabilityTests / OpenSourceLicenseCatalogTests / TipProductTests / EnglishTypingOnDeviceTests / TranscriptLanguageDetectorTests
  • 账户/Managed gatewayAccountAPIClientTests / AccountSignInCoordinatorTests / AccountCenterViewModelTests / AccountCreditPurchaseManagerTests / AccountSecurityPrimitiveTests / AccountSnapshotLoaderTests
  • 测试支持AnalyticsTestSupport / KeyboardUsageTestSupport / FakeUbiquitousKeyValueStore

OSGKeyboardExtTests/ (16 文件)

  • EnglishTypingTests / RimePersonalDictionaryExporterTests / KeyHitTestingTests / CandidatePanelExpandTests / KeyboardUsageTypingTests / LibrimeIntegrationTests / ManagedGatewayTests / RimeSchemaGeneratorTests / KeyboardSurfaceStateTests / AnalyticsExtensionPrivacyTests / PinyinNextKeyResolverTests / TypingTouchTrackerTests / FinalChunkRecoveryTests / KeyboardStateTests / EditHintSchedulerTests / ClipboardSuggestionLifecycleTests

2. macOS 端完整状态

2.1 目标基本事实

  • Bundle ID: com.osgkeyboard.macDeveloper ID + 公证,沙盒关闭
  • 最低系统:macOS 15.0。Swift 6 / strict concurrency
  • 装包名:OSGKeyboard.apptarget 名 OSGKeyboardMacPRODUCT_NAME
  • 本地 ASRMLX Audio Qwen3-ASR 0.6B/1.7B 4-bitThirdParty/mlx-audio-swift/ SPM
  • 复用 iOS AppIcon (OSGKeyboard/AppIcon.icon) + iOS 资产目录(除 AppIcon.appiconset

2.2 macOS App 目录结构(OSGKeyboardMac/39 Swift 文件,无子目录)

入口与生命周期

  • OSGKeyboardMacApp.swift:1 —— @main struct OSGKeyboardMacApp: App单一 Window.windowStyle(.hiddenTitleBar).defaultSize(width: 860, height: 600)+ MacAppDelegate: NSApplicationDelegate + NSPopoverDelegate 拥有 NSStatusItem + NSPopover340×420 transient+ 浮动 dictation overlay
  • MenuBarExtraOSGKeyboardMacApp.swift:104-105 注释:与主 Window 同存时图标会消失)
  • 订阅 Notification.Name.settingsDidSyncFromCloud / .personalDictionaryDidSyncFromCloud / .usageStatisticsDidSyncFromCloud / .speechHistoryDidSyncFromCloudiCloud pull 刷新
  • onOpenURLosgkeyboard://seed-demoDEBUG DemoDataSeeder

侧栏/Shell

  • MacRootView.swift —— NavigationSplitView(.balanced),侧栏:OSGLogoWide 品牌头 + 5 个 MacSection 行(.dashboard/.history/.dictionary/.styles/.settings+ "Devices" 页脚;detail 切换 DashboardView/MacHistoryView/MacDictionaryView/MacPolishStylesView/MacSettingsView;底部 MacStatusFooter
  • MacDictationViewModel.swift:12 —— MacSection enum
  • MacTheme.swift:114 —— MacSystemPalette 双模式(light 暖白 + dark stepped systemGray6→4),通过 \.themePalette 注入,颜色方案变更重渲染
  • MacAppearance.swift —— MacAppearancePreferencesystem/light/dark)存 mac.appearancePreferenceapplyToApp 推到 NSApp.appearance 和每个 window

菜单栏 UIStatus Item + Popover):

  • OSGKeyboardMacApp.swift:140 —— variable-length NSStatusItem 模板 NSImagetarget/action = togglePopover(_:)
  • Popover 内容:NSHostingController(rootView: MacMenuBarPopover()) (:214)。未完成 onboarding → "open the main window" 提示;否则 MacContentView(品牌 + record 按钮 + 热键提示 + 状态文本 + 滚动转写 ≤120pt + 模式/翻译/连接状态 + footer)
  • Popover 打开:prepareForPopoverPresentation() snapshot 前台 AppOSGKeyboard 成为 key 时不丢失粘贴目标

录制 overlay

  • MacDictationOverlayController.swift —— 一个 borderless non-activating NSPanellevel floatingWindow + 1.canJoinAllSpaces + .fullScreenAuxiliary + .stationary
  • 状态机驱动 show/hideviewModel.$isRecording/$isPreparingToRecord/$isProcessing CombineLatest3
  • 位置 user-draggable(双击重置底部居中),持久化到 UserDefaultsmac.overlay.hasCustomPosition / centerX / originY
  • MacDictationOverlayView.swift —— SwiftUI pill500pt 内容宽)+ 状态点 + 一行转写 + 实时徽章 + MiniWaveform + stop 按钮
  • MiniWaveform 复用 OSGKeyboardShared/DesignSystem

主窗口 5 大页

  • DashboardView.swift —— GeometryReader 垂直布局:品牌头 + 7 天图(UsageStatsCluster layout: .split+ dictationStage 卡片(viewModel.homePreviewText 累计预览)+ BottomDictationBar(翻译选择器 + record 按钮 + ready chip
  • MacHistoryView.swift —— 按天分组 SpeechHistoryStore,每行 copy/delete context menu"Clear" 带确认
  • MacDictionaryView.swift —— PersonalDictionaryEntry.Category 分组,按使用次数 + 术语排序;搜索框过滤;+MacDictionaryEntryEditor sheet
  • MacPolishStylesView.swift —— PolishStylePack 目录(内置 practical+fun + 用户),只读 prompt 详情 / 完整编辑器(allowsAddedEmoji 切换 + 2400 字符 prompt 限制);学习卡:语料进度 + "Generate learned style" → PolishStyleLearningService.generateStyle
  • MacSettingsView.swift —— NavigationStack + ScrollView 7 段:support developer (Tip) / general (appearance / interface language / recognition language / iCloud sync) / recognition method (cloud vs local) / cloud ASR provider / local ASR model catalog / polish provider / input (hotkey / auto-paste / accessibility 状态) / legal (Privacy Policy + Third-Party Licenses + Restart Onboarding + version)

Onboarding

  • MacOnboardingView.swift:21 —— 6 步:welcome → microphone → accessibility → engine → [.cloudAPI | .localModel],根据 viewModel.config.engineMode 分支
  • Mic 步调 AVCaptureDevice.requestAccess(for: .audio)accessibility 步打开 x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility 并 1s 后重查 AXIsProcessTrusted
  • Cloud 步收 LLM provider + key + modellocal 步显示默认 Qwen3-MLX 0.6B + 下载按钮 + 进度 + "skip" 提示
  • 状态存 mac.hasCompletedMacOnboarding
  • 注释 MacOnboardingView.swift:7-8 —— "Sherpa identifiers and install records are retained only for migration compatibility"近期从 Sherpa 换到 MLX
  • Settings → About 有 "Restart Onboarding" 按钮

Legal / Support

  • MacLegalSettingsViews.swift —— MacPrivacyPolicyViewWKWebView 加载 OSGKeyboard/Resources/PrivacyPolicy.html,按 uiLanguage 切滚动锚点)+ MacOpenSourceLicensesViewOpenSourceLicenseCatalog.entries(for: .macOS) 列表)→ MacOpenSourceLicenseDetailView(许可证名 / upstream 链接 / 用途 / 文本)
  • MacSupportDeveloperTipRows.swift —— StoreKit 2 TipPurchaseManager.shared 消耗型打赏行

DEBUG 工具

  • osgkeyboard://seed-demo URL 触发 DemoDataSeeder.seedRichPlaceholderData
  • MacAudioRecorder.hasLiveSnapshotSinkDEBUG seam,见 MacAudioRecorder.swift:143

2.3 全局热键 + 辅助功能 + 粘贴注入

MacHotkeyService.swift

  • 两个 NSEvent.add*MonitorForEvents(matching: .flagsChanged)global + local
  • 区分左/右 OptionNX_DEVICELALTKEYMASK = 0x20 / NX_DEVICERALTKEYMASK = 0x40);MacHotkeyTriggerrightOption / leftOption / eitherOption
  • 150ms 持有防抖(scheduleBeginTask.sleep(150ms) → fire onPressBegan),松开取消未发起的 begin
  • start() 返回 Accessibility 状态(globalFlagsMonitor == nil ⇒ 大概率无权限),DEBUG 下 NSLog

MacTextInsertionService.swift —— Accessibility 门控合成 ⌘V

  • AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt: true]) 按需
  • insert(_:autoPaste:targetApp:):清 NSPasteboard.general → 写转写 → snapshot 原始 pasteboard所有 representation,不仅是 .string)→ CGEvent + kVK_ANSI_V + cghidEventTap 合成 ⌘V → 等 500ms 后仅当 change count 仍匹配 post-transcript write才恢复 snapshot(用户/clipboard-manager 写入期间不覆盖)
  • FrontmostAppTracker:177)跟踪 NSWorkspace.didActivateApplicationNotificationOSGKeyboard popover 激活时保留原始 paste 目标
  • activate(_:) 重新激活目标 App,等最多 1s 成为 frontmost
  • shouldRestorePasteboard + restoreItems 是单测 seam

2.4 音频捕获

MacAudioRecorder.swift

  • MacAudioRecording 协议
  • AVAudioEngine.inputNode + 4096 帧 tap
  • 输入格式 → 16kHz mono Float32 via AVAudioConverter
  • 10 分钟 @16kHz 硬上限 + 30s trim hysteresis(卡住的热键不会无限增长 buffer)
  • RMS 平滑(attack 0.5 / decay 0.15
  • 权限:AVCaptureDevice.requestAccess(for: .audio);拒/受限抛 microphoneAccessDenied
  • makeSnapshotStream() -> AsyncStream<AudioBufferSnapshot>
  • Sink 安装按 generation 追踪,避免前一 sink 延迟终止处理器卸载新 sink
  • 无输入设备选择器AVAudioEngine.inputNode 用系统默认;iOS AudioBufferSnapshot+AVFoundation.swiftproject.yml:568 排除)

MacHallucinationFilter.swift

  • <asr_text> 脚手架、language XX 前缀、元数据噪声行(language/emotion/event 等)
  • silencePeakThreshold = 0.0005RMS 低于此跳喂 MLX stream
  • shouldDiscardHotwordDump(音频能量低 + 转写主要是词典热词 → 丢假触发)

2.5 MLX / 本地 ASR 流水线

关键文件

  • MacLocalASRService.swift —— Mac 端公共面:selectedModelDefinition() / isModelInstalled(_:) / usesMLXLiveStreaming() / transcribe(samples:locale:bias:)。MLX 后端模型走 MacMLXStreamingASRProvider.appleSpeech 后端或未装模型时落 MacSpeechLocalASR。Legacy Sherpa 标识符(sherpaQwen3/sherpaSenseVoice/sherpaParaformer)只接受以便抛 qwen3ModelMissing —— 明确迁移期处理
  • MacMLXStreamingASRProvider.swift —— actor,拥有单 Qwen3ASRModel 缓存(cachedModelId / cachedModel / didWarmup)。loadModelLocalASRModelInstallState.modelRootURL(definition) 读并 Qwen3ASRModel.fromModelDirectory(root)warmupIfNeeded 用 1s 静音预热 Metal kernel。makeSession 构建 StreamingConfig0.5s decode interval、0.2s boundary、1s boundary boost、encoder window overlap 1s、max cached windows 8、delayPreset: .realtime、language hint 从 MacQwen3LanguageHint.from(locale:)context: bias?.promptBiastemperature: 0、512 max tokens/pass、2/2 min-agreement passes、finalizeCompletedWindows: true
  • MacMLXStreamingSession.swift —— 包装 mlx-audio-swiftStreamingInferenceSession。订阅 AsyncStream<TranscriptionEvent>displayUpdate/ended/provisional/confirmed/stats);MacHallucinationFilter 过滤 display/endedonDisplayUpdate 转发 displayfeed(samples:) 通过 FlowCaptureDrainTracker.rms(of:) 跳过低于 silencePeakThreshold 的数据;stop() 通过 CheckedContinuation 返回最终 StringpeakAudioRMS() 暴露 hotword-dump 守卫用最大 RMS
  • MacMLXLiveCapture.swift —— Option 持有本地 ASR 编排:建 streaming sessionTaskGroup 内 2 个并发任务:(1) 等首 finishSignaldraining.withLock { $0 = true }await FlowUtteranceEndCoordinator.awaitTailCapture(policy: .macMLX))——显式 break 单信号,永不结束的 stream 不会挂住 group(2) 消费 AudioBufferSnapshot stream100ms chunk1600 samples @16kHz)喂 MLXdraining 时 drainTracker.noteAudio。然后喂余量,调 session.stop(),跑 MacHallucinationFilter.shouldDiscardHotwordDump 丢热词-only 转写
  • MacSpeechLocalASR.swift —— Apple Speech 兜底。16kHz PCM 写临时 WAVPCMSampleWavEncoder),SFSpeechURLRecognitionRequest 强制 requiresOnDeviceRecognitionlocale 无 on-device 模型时立即失败 → 清晰错误"去 系统设置 → 键盘 → 听写 下载")。Chinese locale 应用 CustomLanguageModelManager.applyCustomLanguageModel(to:locale:bias:)(与 iOS 共用)。2× 音频长硬超时 + 30s floorRecognitionSession 锁保护单次 resume,识别回调和超时 task 正确竞速
  • MacCloudASRChunkAdapter.swift —— 薄 ASRChunkTranscribing 适配器,包装 CloudASRClientFactory.make(store:)MacDictationPipeline

模型目录OSGKeyboard/Resources/LocalASR/local-asr-catalog.json2.0.1 状态):

  • schemaVersion: 1
  • defaultModelId: "qwen3-mlx-0.6b-4bit"
  • runtimes: []——runtime 随 bundle 走,MLX 内置)
  • 2 个模型:
    1. qwen3-mlx-0.6b-4bit —— Qwen3-ASR 0.6Bbackend: mlxruntimePlatform: macossizeBytes: 730000000~0.7 GB),locale zh-CN/en-US,支持热词(promptOnly),badgeKey: mac.localASR.badge.balanced,路径 models/qwen3-mlx-0.6b-4bit。两个下载源(hfmirror/huggingface)都在 mlx-community/Qwen3-ASR-0.6B-4bit。文件:config.json / generation_config.json / preprocessor_config.json / model.safetensors~708MB / model.safetensors.index.json / tokenizer_config.json / merges.txt / vocab.json
    2. qwen3-mlx-1.7b-4bit —— Qwen3-ASR 1.7B,同上形状,sizeBytes: 1700000000~1.7GB),badgeKey: mac.localASR.badge.quality。源在 mlx-community/Qwen3-ASR-1.7B-4bit

local-asr-catalog.json 中零 iOS 模型。iOS app 运行时用本目录——iOS 用 iOS 26 SpeechAnalyzer + DictationTranscriber,代码路径不同(OSGKeyboardHostSupport/Services/ASRService.swiftMac 排除)。

模型生命周期

  • loadModel(_:) (MacMLXStreamingASRProvider 第 69-81 行) 缓存 loaded Qwen3ASRModel per model id。切模型失效缓存并 re-warm。warmupIfNeeded 每个冷模型跑一次
  • 卸载(MacLocalASRModelSettingsViewModel.deleteModel)→ 共享 LocalASRModelManager.deleteModel(_:catalog:) 删磁盘权重;actor 缓存自动清——下次不同 id 的 loadModel 自然替换

Mac 不支持 iOS 风格的"on-device + cloud hybrid"——只有 engine-mode 开关(local → MLX 或 Apple Speechcloud → 已配云 ASR)。两种模式共用同一 LLM 润色步。

2.6 LLM 润色 + 供应商集成

  • MacDictationPipeline.swift:254 —— 跑的是同一份 PolishingService(store: store).polishWithOutcome(...)iOS OSGKeyboardShared/Services/PolishingService.swift
  • PolishContextappContext(从目标 App bundle id 通过 MacAppContextService 抓)+ dictionarySupplement(来自 local-bias polishFragment),和 iOS 同款
  • MacSettingsView.swift:111 (polishProviderSection) 用 viewModel.polishSelectableProvidersLLMProvider.userSelectablePresets——完整集,与 iOS 相同)。行:provider / API key / baseURL / modelMacProviderModelRow "拉模型列表" 动作) / thinking 切换 / MacProviderToolsRow "Test connection" / 翻译目标
  • MacSettingsView.swift:173 (asrProviderSection) 用 viewModel.asrSelectableProvidersLLMProvider.asrSelectablePresets)。Volcengine 有自定义 auth-modeAPI key vs App ID + Access Token)通过 VolcengineASRFields.parse / updateMacVolcengine(...);其他供应商走通用 baseURL/apiKey/model 行 + "Test connection"
  • Mac 没有 Mac 专用 polish/ASR 供应商集——供应商面和 iOS 一致(DeepSeek/OpenAI/Anthropic 兼容 LLMVolcengine/OpenAI Realtime/Bailian/Alibaba 云 ASR
  • MLX 供应商仅用于本地 ASR—— Mac 端 MLX LLM 润色

2.7 持久化

类别 实现 Key 前缀 / 位置
UserDefaults 标准 mac.*MacHotkeyService:41 / MacAppearance:56 / MacDictationViewModel:103-105 / MacDictationOverlayController:43-45)—— 热键、auto-paste、appearance、onboarding、overlay 位置、MLX 模型选择、下载源、界面语言
App Group 共享 AppGroupStore 与 iOS 同:PersonalDictionary / PolishStyleCatalog+active id / translationTarget / detected appContext / speechHistory / usageStatistics / engineMode / localeId / LLM API key & baseURL / 云 ASR provider id/apiKey/model/baseURL / iCloud-sync-enabled
Keychain 共享 Keychain keychain-access-groups: [$(AppIdentifierPrefix)com.osgkeyboard.shared]entitlement)—— 存 LLM API key
iCloud KVS 5 路全部接好 MacICloudSyncBootstrap.swiftAppCloudSync(makeStore:historyDefaults:),暴露 settingsSync/dictionarySync/polishStyleSync/appCloudSyncentitlement 用 $(TeamIdentifierPrefix)com.osgkeyboard.ios 和 iOS 同桶
语音历史 SpeechHistoryStore.shared + SpeechHistoryStorage UserDefaults 后端 + iCloud SpeechHistoryCloudSync
模型权重 LocalASRModelInstallState.rootDirectory() 由共享 LocalASRModelManager
临时文件 FileManager.default.temporaryDirectory Apple Speech WAVosg-mac-asr-<uuid>.wavdefer 删,MacSpeechLocalASR:71

2.8 系统集成

  • 菜单栏 status item + popover
  • 全局 ⌥ 热键 + 合成 ⌘V
  • URL scheme osgkeyboard(仅 DEBUG osgkeyboard://seed-demo
  • Login item / launchd SMLoginItemSetEnabled
  • 通知(UNUserNotificationCenter)—— HUD 本身就是唯一直达面
  • Touch Bar
  • Services / Share extensions
  • Dock badge
  • LSApplicationCategoryType: public.app-category.utilities
  • ENABLE_HARDENED_RUNTIME: YESDeveloper ID + 公证必需)
  • 沙盒关闭 com.apple.security.app-sandbox: falseproject.yml:628)—— Accessibility + 全局事件 tap + ⌘V 注入都禁在沙盒里
  • 发行渠道:Developer ID + 公证不进 Mac App Store / TestFlight
  • applicationShouldTerminateAfterLastWindowClosed → false(菜单栏项保活)
  • LSUIElement: false设——启动时仍显 Dock 图标)

2.9 macOS 独占(iOS 没有)的能力

  1. NSStatusItem + NSPopover 菜单栏 UI —— SwiftUI MenuBarExtra 故意不用,AppKit 为源
  2. 全局 ⌥ 持有热键NSEvent.add*MonitorForEvents(matching: .flagsChanged))区分左/右/任意 Option
  3. Accessibility 门控 ⌘V 注入CGEvent + cghidEventTap + AXIsProcessTrusted
  4. 前台 App 跟踪FrontmostAppTracker)让 popover 激活 OSGKeyboard 时不丢粘贴目标
  5. MLX Audio Qwen3 流式 ASRQwen3-ASR 0.6B/1.7B 4-bit)通过 mlx-audio-swift SPM
  6. 系统原生 macOS light/dark 调色板(暖白 light + stepped gray6→4 dark),从 AppKit semantic colors 解析,让 window chrome 跟随 SwiftUI 颜色方案
  7. 浮动 non-activating NSPanel HUDfloatingWindow + 1),user-draggable 位置持久化
  8. Bundle-ID → AppContext 映射MacAppContextService.swift)—— iOS 走 AppContextDetector 启发式(键盘读不到前台 bundle id)
  9. 手动 MLX 模型下载/暂停/恢复per-file 字节进度
  10. "Open Storage" reveal-in-Finder MLX 模型根
  11. Volcengine 云 ASR auth-mode 切换API key vs App ID + Access Token
  12. URL scheme: osgkeyboardosgkeyboard://seed-demo DEBUG seed
  13. Apple Speech on-device 兜底(仅当未装 MLX 模型或 backend == .appleSpeech
  14. NSApplicationDelegate-driven AppKit 生命周期applicationShouldTerminateAfterLastWindowClosed → false、status-item 创建、overlay controller 启动、热键接续
  15. MacMLXStreamingASRProvider actor + 模型缓存 + warmup 保单 Qwen3ASRModel 驻留
  16. 两列 MacInlinePicker(供应商行) vs iOS .menu Picker
  17. MacProviderSettingRow 显式 200pt label 列 防长凭证输入挤

2.10 macOS 测试覆盖(OSGKeyboardMacTests/3 文件,挂在 Mac App bundle 内)

project.yml:513-533 覆盖 @testable import OSGKeyboardhost target 名 OSGKeyboardMac 但装包为 OSGKeyboard.app,所以 override TEST_HOST)。

  1. MacDictationViewModelTests.swift —— testCancellingButtonPreparationKeepsGateClosedUntilStartUnwinds:替换 recorder 为 SuspendedMacAudioRecorderstart()CheckedContinuation),验证第二次 toggleRecording()(取消)不重开 isPreparingToRecord
  2. MacAudioRecorderSnapshotStreamTests.swift —— testReplacingSnapshotStreamDoesNotDeadlock + testReplacingSnapshotStreamKeepsTheNewSinkAttached:回归守卫,防 Option 松开时 lock 重入冻结(AsyncStream.Continuation.finish() 在调用线程同步跑 onTerminationhandler 重新拿 installer 持有的同 NSLock 死锁主线程)。第一个测试在 global queue 装 + 2s semaphore 超时,让回归失败而非挂住套件;第二个测读 MacAudioRecorder.hasLiveSnapshotSinkDEBUG seam)确认新 stream 顶替前辈
  3. MacTextInsertionServiceTests.swift —— testRestoreRequiresTranscriptToStillOwnPasteboardpasteboard change-count 守卫)/ testRestoringOriginallyEmptyPasteboardClearsTranscript / testCapturedBundleIdentifierDrivesPolishContextXcode/WeChat/OSGKeyboard 自身经 MacAppContextService.detectContext(bundleIdentifier:)

3. macOS 落后 iOS 的功能(按缺口大小排序)

3.1 完全缺失(共享代码已编进 Mac 但零调用)

缺失功能 iOS 路径 共享代码状态 影响
剪贴板历史 + AI 技能 OSGKeyboard/Views/ClipboardSettingsViewOSGKeyboardExt/Views/ClipboardKeyboardViews ClipboardHistoryStore / ClipboardSemanticAnalyzer / ClipboardSkillSemanticRanker / AIClipboardSkill / AIClipboardSkillLayoutStore / AIClipboardPrompt / ClipboardHistoryPolicy 全部编进 Mac 二进制,无任何 Mac 文件引用 Mac 用户能说话能润色,但没法让"复制即智能"
AI 提示轮播 / 上下文技能 OSGKeyboard/Services/AIHintRefreshServiceOSGKeyboardExt/Views/AIKeyboardView 内的 hint carousel AIHintStore / AIHintPool / AIHintKeywordExtractor / AIHintKeywordCompressor / AIHintLocalCatalog / AIHintModels 全部已编,无 Mac 调用 Mac 没有 idle 屏的"想一句"灵感卡
OSG 账户(Sign in with Apple + 积分 + 推荐) OSGKeyboard/Views/Account/AccountCenterViewAccountPurchaseHistoryView OSGKeyboardHostSupport/Features/Account/ 全部编进 MacAccountAPIClient / SignInWithApple / DeviceIntegrity / HostPrivateAccountKeychain / AccountModels / OOBEGrantProvisioningCoordinator),无 Mac UI 也无调用,且 Mac entitlement 无 com.apple.developer.applesignin Mac 只能用 tip 打赏;OSG 积分体系是 iOS 独占
App Attest OSGKeyboardHostSupport/Features/Account/DeviceIntegrity.swiftSystemAppAttestProvider 已编,但 Mac 无对应 entitlementcom.apple.developer.devicecheck.appattest-environment)也无调用 Mac 不在 attestation 流程里
一方分析(Analytics OSGKeyboard/Services/AnalyticsHostServiceOSGKeyboardExt/Services/AnalyticsExtensionService OSGKeyboardShared/Features/Analytics/ 12+ 文件全部编进 Mac零 Mac 调用 Mac 用户行为完全无遥测;产品迭代失去数据源
助手指令(Shortcuts OSGKeyboard/Services/AIAgentShortcutInstallerAIAgentShortcutRunner、3 个 bundled .shortcutOSGExtractTodos/OSGExtractEvents/OSGSaveToNotes 共享 AIAgentSkill* / AIShortcutShareLink / AIGenericSkillExport / AINoteExport / AIMapNavigation / AIPhoneNumberActions 已编,无 Mac 调用;bundled .shortcut Mac 不装project.yml:97-105 仅 iOS 装) Mac 没法"说一句把当前文本加到 Notes / 提取 Todo"
CLM 用户管理 UI iOS 后台用 CustomLanguageModelManager Mac 了 CLMOSGKeyboardMacApp.swift:117prepareInBackgroundIfNeededMacSpeechLocalASR:67-98applyCustomLanguageModel),MacCLMSettingsView Mac 用户不知道 CLM 在跑;没法改 phrase 库
最后输入编辑(Edit hint / Last-input edit OSGKeyboardExt/Views/LastInputEditViewEditHintSchedulerLastInputEditCoordinator 已编,无 Mac 调用 Mac 没法长按"改刚才那句"
Flow 跨进程会话 OSGKeyboard/Services/FlowSessionManagerFlowPictureInPictureControllerFlowTerminationCoordinatorFlowAnalyticsOperationRegistryFlowASRPostProcessorFlowDiagnosticsOSGKeyboardExt/Services/KeyboardFlowCoordinatorOSGKeyboardShared/Services/FlowSessionBridge* 已编,Mac 仅借用 FlowSessionKeys.cloudASRWaitTimeout 一个常量(MacDictationViewModel.swift:498 架构性差异——Mac 单进程、单用户、单次录制;不需要也不该硬塞
自定义键盘(Rime/拼音/英文) 整个 OSGKeyboardExt/Typing/ + OSGKeyboardShared/Typing/** + LibrimeEngine + RimeResourceInstaller + EnglishSuggestionEngine + Pinyin* + Rime* Mac target 完全排除 Typing/**project.yml:585 架构性差异——Mac 不是键盘扩展;librime 是 iOS C/Objective-C++ 框架,Mac 结构性不兼容
后台任务 iOS UIBackgroundModes: audio + BGTaskSchedulerPermittedIdentifiers: [com.osgkeyboard.ios.analytics-sync] Mac Info.plist BG* keys Mac app 不能后台运行;iOS 的 analytics 同步、Flow PiP 都没 Mac 对应
Settings 子页面 TypingInputSettingsView / AIAgentSkillsView / Account/* / HelpFeedbackView / ReleaseNotesSheet / AppGroupErrorView / NewContactSheet / KeyboardPreviewSheet / 各种 DEBUG demo 无 Mac 对应 见上表
Universal Links 启动 handoff iOS applinks:osglab.com + AppURLHandler 保留 sourceApplication Mac entitlement 共享同 com.apple.developer.associated-domains Mac-side 解析 Mac 不能从网页/邮件 deep-link 进 Onboarding 或 Skill

3.2 部分缺失

缺失 iOS macOS 差异点
Onboarding 深度 7 步 + OOBE 4-feature 实践(用 OOBEClientInfrastructure 拿服务器体验金) 6 步(welcome / mic / accessibility / engine / cloud|local 无 Sign in with Apple、无 OOBE 实践
设置覆盖面 7 个子面板 + Account + AI Agent + Clipboard + Typing Input 5 个段落(support / general / recognition / ASR / local ASR / polish / input / legal iOS 的 AI Agent / Clipboard / Typing 在 Mac 没对应;Mac 的 input 段含热键 + Accessibility 状态(iOS 没有)
AI Hint / Shortcut 链接 提示页 + 3 bundled .shortcut 仅 0Mac 装包不含 .shortcut 整套 AI mode 体验 Mac 无
Provider 切换粒度 iOS 通过 iOS EnginePickerSection + ProviderPickerSection 在 Settings 内 + 主页快捷 Mac 整段 recognitionSection + asrProviderSection + polishProviderSection 拆开 iOS 较紧,Mac 较松

3.3 Mac 自身落后 iOS 之处("应该补"清单)

按"代码复用难度 + 用户价值"排序:

优先级 功能 现状 工作量 建议
P0 剪贴板历史 + AI 技能面板(Mac UI) 共享代码全到位,零 Mac UI ——MacClipboardHistoryView + MacAIClipboardSkillView(仿 MacHistoryView+ 改 MacSettingsView 加一个 Clipboard 段 立刻做。代码复用 100%,价值高
P0 CLM 设置页(Mac UI 后台在跑(OSGKeyboardMacApp:117),无 UI ——MacCLMSettingsView,仿 MacLocalASRModelSettingsView 模板 立刻做。让用户能编辑 phrase 库
P1 OSG 账户 + Sign in with Apple + 积分购买 共享代码编进 Mac,零 UIMac entitlement 缺 com.apple.developer.applesignin + 无 SKIncludeConsumableInAppPurchaseHistory ——加 entitlement + 改 MacICloudSyncBootstrap 已涵盖大部分,加 MacAccountCenterView(仿 AccountCenterView 但精简)+ 改 Settings 一周内可交付
P1 OSG credits 消耗型 IAP500tks/1500tks/3000tks 共享 TipPurchaseManager 已装;iOS 走 AccountCreditPurchaseManager ——MacAccountCenterView 出来后一气呵成 同上
P2 一方分析(Mac 共享 AnalyticsClient 等全编,零调用 中-大——MacAnalyticsHostService + 隐私 / opt-in + 后台任务(Mac 上走 NSProcessInfo.thermalState 节流 + 用户同意时 NSTask 跑) 半年窗口内
P2 AI 提示轮播 / 上下文技能(Mac) 共享代码全到位 ——加 MacAIHintPool 屏 + MacAIQuestionService 入口 + Shortcuts bundle 装载 与 P0 剪贴板可联动
P3 助手指令集成 共享代码 + iOS bundled .shortcut ——Mac 装 .shortcut + AIAgentShortcutInstaller 调起(shortcuts:// URL 仍可用) 与 P2 AI 提示联动
P3 最后输入编辑 共享代码全到位 ——需要 Mac 端保留"刚才插入的引用"(EditableInputReference),但 Mac 走 Accessibility ⌘V 注入,没有 EditableInputReference 的来源——需要新协议 架构问题,先做 PoC
P3 后台同步(Analytics 任务) ——NSTask/SMAppService LaunchAgent 拉 analytics;与 P2 联动

3.4 Mac 形态上"不应该补"的功能

功能 原因
Flow 跨进程会话 Mac 单进程、单用户、单次录制——Flow 设计为键盘扩展 ↔ 宿主 App 的两进程 mailbox。强行移植会引入不必要的 IPC 开销。Mac 用 MacDictationPipeline + MacDictationViewModel + MacDictationOverlayController 三件套已足够
PiP keep-alive macOS 无系统 PiP。MacDictationOverlayController 的 NSPanel 已占位
Rime / 拼音 / 英文 autocorrect Mac 不是键盘扩展,没输入面。LibrimeEngine 是 iOS C/Objective-C++ xcframeworkMac 结构性不兼容。即便 Mac 装键盘扩展(Designed for iPad 那种),librime 也需要重编译
UIBackgroundModes: audio Mac 没用——按住说话时 NSPanel 是 non-activating,不需要 audio session 后台保持
UIOpenURLContext.options.sourceApplication iOS 26 only APIMac 走 NSWorkspace.didActivateApplicationNotification 已解决前台 App 跟踪
AVAudioApplication.requestRecordPermission iOS 17+ onlyMac 走 AVCaptureDevice.requestAccess(for: .audio)
App Group 跨进程 mailbox Mac 单进程不需要。但 AppGroup + iCloud KVS 是用户态多设备同步用的,保留
iOS 风格的 idle 屏 Mac 主窗口即 dashboard,不需要 idle 屏的"AI hint 轮播";但 MacAIHintPool 屏可以放在 Dashboard 顶部,与 P2 联动
LSApplicationQueriesSchemes iOS onlyMac 无 canOpenURL 限制
SKIncludeConsumableInAppPurchaseHistory / Mac storekit 文件 Mac App Store 与 iOS App Store 独立 SKU;目前 Mac 装包是 Developer ID 渠道,不进 Mac App Store,所以 storekit 不需要

4. 适合在 Mac 上做开发和移植的功能

4.1 应该现在做的(P0

① 剪贴板历史 + AI 技能面板

为什么 Mac 适合:共享代码 100% 到位(ClipboardHistoryStore / ClipboardSemanticAnalyzer / ClipboardSkillSemanticRanker / AIClipboardSkill / AIClipboardPrompt),iOS 8 个 UI 调试稳定,只需套 Mac 风格。

具体工作

  • 新增 OSGKeyboardMac/Views/MacClipboardHistoryView.swift(仿 MacHistoryView
  • 新增 OSGKeyboardMac/Views/MacAIClipboardSkillView.swift(仿 AIAgentSkillsView,精简为 Read/Edit/Disable
  • MacSettingsView.swiftclipboardSection(含 toggle 启用历史 / toggle 启用技能候选条 / "Manage Skills" 链接 / "Open History" 链接)
  • MacDictationViewModel 订阅 ClipboardHistoryStore.entries@Published var clipboardEntries
  • 复用 ClipboardCaptureCoordinator 不可(ext-only)——需要 Mac 端 MacClipboardMonitorNSPasteboard.general.changeCount 轮询 + sanitize

测试MacClipboardMonitorTests(用 in-memory pasteboard 模拟)/ MacClipboardHistoryViewModelTests

工作量1-2 周

② CLM 设置页

为什么 Mac 适合Mac 后台已经在跑 CLMOSGKeyboardMacApp:117),只是用户管不到。

具体工作

  • 新增 OSGKeyboardMac/Views/MacCLMSettingsView.swift(仿 MacLocalASRModelSettingsView
  • 复用 CustomLanguageModelManager.shared.state@Published idle/preparing/ready/failed
  • 复用 OSGKeyboard/Resources/HostCLM/v1/OSGKeyboardCLM.bin(已在 Mac 装包)
  • 复用 PersonalDictionary 共享存储(用户输入的 phrase 库 + iCloud 同步)
  • 提供 phrase 列表 / 编辑 / 触发"重编译"按钮

测试MacCLMSettingsViewModelTests(验 state 状态机)

工作量1 周

4.2 应该中期做的(P1

③ OSG 账户 + Sign in with Apple + 积分购买

为什么 Mac 适合:共享代码编进 Mac 都没报错,缺的是 entitlement + UI。

先决条件

  • OSGKeyboardMac.entitlementscom.apple.developer.applesignin: [Default]
  • OSGKeyboardMac/Info.plistSKIncludeConsumableInAppPurchaseHistory: true
  • (选做)把 OSGKeyboard.storekit 内容 mirror 到 OSGKeyboardMac.storekit 或共享

具体工作

  • MacAccountCenterView(仿 iOS AccountCenterView 但精简)
  • 复用 LiveAccountServices / AccountSessionCoordinator@MainActor state machine
  • 复用 AccountCreditPurchaseManagerStoreKit 2 product list → 服务器校验)
  • Settings → 加 "Account" 段,含 sign-in / 积分余额 / 购买历史 / 登出 / 删号

测试MacAccountSessionCoordinatorTestsin-memory AccountAPIClient fake

工作量2-3 周

④ Mac App Attest / DeviceCheck

为什么 Mac 适合iOS attestation 后端已经在用,Mac 端可以通过 DCAppAttestServicemacOS 13+,需要 Mac Catalyst 但当前 SUPPORTS_MACCATALYST: NO)或者改用 DeviceCheck DCDevice.generateTokenmacOS 13+ available without Catalyst)。

先决条件

  • OSGKeyboardMac.entitlementscom.apple.developer.devicecheck.appattest-environment: $(APP_ATTEST_ENVIRONMENT)
  • 若走 App Attest,需要 SUPPORTS_MACCATALYST: YES(改大改)或改 DeviceCheck-only

具体工作

  • MacDeviceIntegrityCoordinator(仿 iOS,但用 DCDevice
  • AccountAPIClient 复用,调 /v1/integrity/attest

工作量1-2 周(仅 Mac);+ Catalyst 决策时间

4.3 应该长期做的(P2

⑤ 一方分析(Mac

为什么 Mac 适合:Mac 用户行为和 iOS 同样有价值(知道用户用不用 MLX 模式、热键触发频率、词条覆盖、润色样式流行度)。

先决条件

  • 共享 AnalyticsClient / AnalyticsRepository 已经完备
  • Mac 上加 MacAnalyticsHostService(仿 iOS):监听 viewModel.$isRecording/$isPreparingToRecord/$isProcessing 推送事件
  • 隐私 / opt-in:在 onboarding 末加一步

挑战

  • Mac 后台无 BGTaskScheduler——改用 NSProcessInfo.thermalState + ProcessInfo.isLowPowerModeEnabled 节流, SMAppService 拉个 LaunchAgent
  • AnalyticsUploadCoordinator 的移动策略(threshold 20 / 60s flush)要 Mac 化

工作量3-4 周

⑥ AI 提示轮播 / 上下文技能

为什么 Mac 适合Mac dashboard 缺内容——除了使用统计,闲置时可推"想一句"灵感卡。

具体工作

  • MacAIHintPool 嵌在 DashboardView 顶部(仿 iOS AIKeyboardView 的 carousel 区域)
  • 复用 AIHintStore / AIHintPool / AIHintKeywordExtractor / AIHintLocalCatalog
  • 上下文技能入口放 MacContentViewpopover 内 record 按钮旁的下拉)

工作量2-3 周

⑦ 助手指令集成

为什么 Mac 适合Mac 上有 Shortcuts.app(更成熟),bundled .shortcut 装入 Mac App bundle 即可。

具体工作

  • OSGKeyboard/Resources/Shortcuts/*.shortcut 进 Mac bundle
  • 复用 AIAgentShortcutInstaller / AIAgentShortcutRun
  • popover / dashboard 加技能快捷入口

工作量1-2 周

4.4 不应该在 Mac 做的(但代码可以清理)

共享代码 为什么不该在 Mac 跑 处置
OSGKeyboardHostSupport/Features/Account/*Mac 已编) App Attest 路径 iOS 专属;Mac 没 entitlement 等 ④ 决策后从 Mac target 排除或接 UI
OSGKeyboardShared/Features/Analytics/*Mac 已编) 无 Mac 端调用 等 ⑤ 决策后从 Mac target 排除或接服务
OSGKeyboardShared/Features/ManagedGateway/*Mac 已编) 是账户 grant 体系,与 ③ 绑定 等 ③ 决策后处理
OSGKeyboardShared/Services/AIClipboard*Mac 已编) 与 ① 绑定 ① 实现后从"无调用"变"有调用"
OSGKeyboardShared/Services/AIHint*Mac 已编) 与 ⑥ 绑定 同上
OSGKeyboardShared/Services/AIUserSkill* / AIAgentSkill*Mac 已编) 与 ③⑦ 绑定 同上
OSGKeyboardShared/Services/Flow*Mac 已编) Mac 架构不兼容 立刻从 Mac target 排除(白付编译器开销)
OSGKeyboardShared/Services/KeyboardState.swiftMac 已排除) 已经是 Mac 排除 ✓
OSGKeyboardShared/Services/EditTransactionStore.swift 与"最后输入编辑"绑定 见 P3

4.5 立即可清的"白编译"清单

OSGKeyboardShared/Services/FlowSessionBridge.swift + 所有 +* 文件
OSGKeyboardShared/Services/FlowSessionKeys.swift
OSGKeyboardShared/Services/FlowSessionDarwin.swift
OSGKeyboardShared/Services/FlowSessionPolicy.swift
OSGKeyboardShared/Services/FlowStartTransactionPolicy.swift
OSGKeyboardShared/Services/FlowKeyboardPolicies.swift
OSGKeyboardShared/Services/FlowHandoffPolicy.swift
OSGKeyboardShared/Services/UtteranceStreamChunker.swift
OSGKeyboardShared/Services/UtteranceBatchFallbackPolicy.swift
OSGKeyboardShared/Services/UtteranceTranscriptGuard.swift
OSGKeyboardShared/Services/UtteranceTranscriptStitcher.swift
OSGKeyboardShared/Services/TranscriptOverlapUtilities.swift
OSGKeyboardShared/Services/ProgressiveDictationTranscriptAccumulator.swift  # 已排除
OSGKeyboardShared/Services/EditTransactionStore.swift
OSGKeyboardShared/Services/EditUsageMetricsStore.swift
OSGKeyboardShared/Services/EditLastInputPromptComposer.swift
OSGKeyboardShared/Services/EditOutputValidator.swift
OSGKeyboardShared/Models/Flow*.swift (全部)
OSGKeyboardShared/Models/EditableInputReference.swift
OSGKeyboardShared/Models/EditSessionState.swift
OSGKeyboardShared/Models/FlowUtterance*.swift
OSGKeyboardShared/Models/FlowInactivityDuration.swift
OSGKeyboardShared/Utilities/FlowCaptureTailDrain.swift
OSGKeyboardShared/Utilities/FlowUtteranceEndCoordinator.swift
OSGKeyboardShared/Utilities/FlowUtterancePCMStore.swift
OSGKeyboardShared/Utilities/FlowTrace.swift
OSGKeyboardShared/Utilities/FlowPipelineDiagnostics.swift
OSGKeyboardShared/Utilities/HostMemoryBudget.swift
OSGKeyboardShared/Utilities/KeyboardExtensionMemoryTelemetry.swift  # 已排除
OSGKeyboardShared/Features/Analytics/  # 全部 12+ 文件
OSGKeyboardShared/Features/ManagedGateway/  # 全部 8+ 文件
OSGKeyboardHostSupport/Features/Account/  # 全部
OSGKeyboardHostSupport/Services/CloudASR/AlibabaVocabularySync.swift  # iOS-only API
OSGKeyboardHostSupport/Services/ASRChunkTranscribing.swift  # 需检查

建议):在 project.yml:558-602 给 Mac target 的 source list 加 excludes:,避免编译这些文件后被链接器裁掉造成的 dead-code 体积。


5. iOS 端需要"修一下"或注意的地方

报告主体是 macOS 差距,但 iOS 端顺手列出 5 个明显可改进点(不修不影响功能,但提升质量):

  1. OSGKeyboardHostSupport/Features/Account/* 整块虽然不通过 Mac UI 暴露,但通过 source include 编进 Mac 二进制——长期应从 Mac target 排除或加 MacAccountServices 实际使用
  2. OSGKeyboardShared/Features/Analytics/* 同上——Mac 编译进二进制但无任何调用
  3. OSGKeyboardShared/Features/ManagedGateway/* 同上
  4. project.yml:570-602 的 Mac target source list 很长且依赖手写排除,建议拆出 OSGKeyboardMacExcludes.yml 维护
  5. OSGKeyboardShared/Typing/EnglishSystemLexicon.swift:11唯一import UIKit 的 Shared 文件——是 iOS ext 独享的 QuickType 数据源。考虑改名/移动到 OSGKeyboardExt/ 减面
  6. iOS FlowSessionManager ~3500 LOC 是单一 god object;建议把 FlowPictureInPictureController / FlowTerminationCoordinator / FlowAnalyticsOperationRegistry 三个生命周期拥有者抽离
  7. iOS appDelegate 反弹 .onOpenURL (AppURLHandler.swift) 注释里写 UIOpenURLContext.options.sourceApplication iOS 26 only——这个 API 在 iOS 26 还在吗?值得 verify(影响 deep-link host-return 白名单)
  8. KeyboardViewController didReceiveMemoryWarning (:390-404) 强制回 voice surface——typing 引擎是最大内存消费者,但每次收到警告就退出 typing 体验略激进;考虑加 cooldown

6. 实施建议

6.1 短期(未来 1-2 周)

  • 任务 A:从 Mac target 排除 Flow* / Edit* / Analytics* / ManagedGateway* / Account*HostSupport)所有共享代码
    • 修改 project.yml:570-602,加 excludes: 列表
    • 验证 Mac target 仍能 build & test pass
    • 节省 Mac 二进制 ~2-4 MB + 减少编译时间 ~10-15s
  • 任务 B:Mac 剪贴板历史 + AI 技能面板P0 ①)
  • 任务 CMac CLM 设置页P0 ②)

6.2 中期(1-2 月)

  • 任务 DOSG 账户 + Sign in with Apple + 积分P1 ③)
  • 任务 EApp Attest 决策 + 实施P1 ④)

6.3 长期(3-6 月)

  • 任务 FMac 一方分析P2 ⑤)
  • 任务 G:AI 提示轮播 / 上下文技能P2 ⑥)
  • 任务 H:助手指令集成P2 ⑦)

6.4 决策项

决策 选项 影响
App Attest 路径 (a) Catalyst + DCAppAttestServicemacOS 13+
(b) 仅 DCDevice.generateToken(无 attestation
(c) 不做
(a) 需开 SUPPORTS_MACCATALYST: YES 改大改;
(b) 安全性较弱但改动小;
(c) Mac 不在 attestation 内
Mac 走 Mac App Store 还是 Developer ID (a) 维持 Developer ID(现状)
(b) 走 Mac App Store
(a) 维持沙盒关闭,Accessibility 自由;
(b) 需重写热键 + 注入方案(沙盒内 Accessibility 拿不到)
是否上 Rime 替代方案 (a) 不做(Mac 不是键盘)
(b) 内部输入法,绕过 librime
(b) 工作量极大,且与产品形态不符
Mac 是否支持 iOS 风格的"on-device + cloud hybrid" (a) 仅 engine-mode toggle(现状)
(b) 支持每句 hybridlocal first,失败 fallback cloud
(b) 需要重新设计 MLX streaming 端

7. 关键引用

主题 文件 行号
iOS 宿主入口 OSGKeyboard/OSGKeyboardApp.swift 1
iOS Flow 单一拥有者 OSGKeyboard/Services/FlowSessionManager.swift 1 (~3500 LOC)
iOS 键盘入口 OSGKeyboardExt/KeyboardViewController.swift 33-38
iOS 键盘生命周期 OSGKeyboardExt/KeyboardViewController.swift 148-655
iOS 共享 ASR 协议 OSGKeyboardShared/Services/PolishingService.swift 34-105
iOS HostSupport ASR 工厂 OSGKeyboardHostSupport/Services/ASRService.swift 1
iOS HostSupport 云 ASR 工厂 OSGKeyboardHostSupport/Services/CloudASR/CloudASRClientFactory 44
Mac 入口 OSGKeyboardMac/OSGKeyboardMacApp.swift 1
Mac MLX 流式 provider OSGKeyboardMac/MacMLXStreamingASRProvider.swift 1
Mac MLX 流式 session OSGKeyboardMac/MacMLXStreamingSession.swift 1
Mac MLX 实时捕获编排 OSGKeyboardMac/MacMLXLiveCapture.swift 1
Mac 热键 OSGKeyboardMac/MacHotkeyService.swift 1
Mac 文本注入 OSGKeyboardMac/MacTextInsertionService.swift 1
Mac 音频 OSGKeyboardMac/MacAudioRecorder.swift 1
Mac 字幕过滤器 OSGKeyboardMac/MacHallucinationFilter.swift 1
Mac 字典流水线 OSGKeyboardMac/MacDictationPipeline.swift 254
Mac 模型目录 OSGKeyboard/Resources/LocalASR/local-asr-catalog.json 1-111
Mac target 定义 project.yml 543-674
Mac target 共享 source 排除 project.yml 558-602
Mac entitlements OSGKeyboardMac/OSGKeyboardMac.entitlements 1

8. 附录:版本与里程碑

版本 日期 关键 Mac 相关变更
2.0.1 (build 90) 2026-08-21 (无 Mac-specific changelog 条目)
2.0.0 (build 85) 2026-08-19 "Optional OSG account"iOS only,但相关 HostSupport 代码编进 Mac
1.8.0 (build 72) 2026-08-14 "Mac Styles / Settings follow the same pairing"(设计系统共用)+ "Mac model downloads"MLX 模型下装载入
Pre-1.8.0 Sherpa → MLX 迁移(MacOnboardingView.swift:7-8 注释)

报告完。
下次更新窗口:2026-09-XX 复审 P0 ① ② 实施情况 + 决策项 6.4 落地。