Files
OSGKeyboard/project.yml
T

786 lines
33 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# XcodeGen configuration for OSGKeyboard
# Run `./Scripts/generate-xcodeproj.sh` to create OSGKeyboard.xcodeproj.
# App home-screen icon: OSGKeyboard/AppIcon.icon (Icon Composer, iOS 26).
# Repo only tracks project.yml; .xcodeproj is gitignored.
name: OSGKeyboard
options:
bundleIdPrefix: com.osgkeyboard
# XcodeGen 2.37+ emits AppIcon.icon as folder.iconcomposer.icon natively.
# Older XcodeGen releases may need Scripts/patch-icon-composer.sh (fallback).
# Minimum OS: iOS 26. We dropped older iOS support so the
# legacy speech + AVAudioSession branching could be removed in
# favour of iOS 26's `SpeechAnalyzer` (always on-device) and
# `AVAudioApplication.requestRecordPermission` (iOS 17+). iPhone + iPad;
# no Mac Catalyst, no visionOS.
deploymentTarget:
iOS: "26.0"
developmentLanguage: en
createIntermediateGroups: true
generateEmptyDirectories: true
groupSortPosition: top
# iOS local ASR uses iOS 26 `SpeechAnalyzer` + `DictationTranscriber`.
# The shared iOS framework links librime for Chinese typing. Optional
# post-ASR polish and AI mode use the user-selected LLM provider.
# macOS local ASR links mlx-audio-swift (ThirdParty/) for Qwen3 MLX
# streaming; model weights are installed through the bundled catalog.
packages:
MLXAudio:
path: ThirdParty/mlx-audio-swift
Librime:
url: https://github.com/ghostflyby/librime-xcframework.git
exactVersion: 1.17.0-pack.1
settings:
base:
SWIFT_VERSION: "6.0"
IPHONEOS_DEPLOYMENT_TARGET: "26.0"
ENABLE_USER_SCRIPT_SANDBOXING: YES
SWIFT_STRICT_CONCURRENCY: complete
GENERATE_INFOPLIST_FILE: NO
ENABLE_MODULE_VERIFIER: YES
MODULE_VERIFIER_SUPPORTED_LANGUAGES: "objective-c objective-c++"
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS: "gnu17 gnu++17"
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED: YES
ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOLS: YES
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS: YES
STRING_CATALOG_GENERATE_SYMBOLS: YES
CLANG_CXX_LANGUAGE_STANDARD: c++17
MARKETING_VERSION: "2.1.5"
CURRENT_PROJECT_VERSION: "125"
# 签名配置来自 Signing.local.xcconfig(gitignored,不会被覆盖)
# 项目级签名 xcconfig,适用于所有 target
configFiles:
Debug: Signing.local.xcconfig
Release: Signing.local.xcconfig
targets:
# =========================================================
# 主 App
# =========================================================
OSGKeyboard:
type: application
platform: iOS
sources:
- path: OSGKeyboard/AppIcon.icon
buildPhase: resources
# Empty Settings bundle: guarantees the app owns a Settings entry so
# `openSettingsURLString` lands on the app's own page instead of the
# Settings root or the Apps list.
- path: OSGKeyboard/Settings.bundle
type: folder
buildPhase: resources
- path: OSGKeyboard
excludes:
- "AppIcon.icon"
- "OSGKeyboard.icon"
# Declared explicitly under `resources` so it ships as a bundle.
- "Settings.bundle/**"
# Legacy PNG app icons must not coexist with AppIcon.icon — actool
# crashes when both are passed. iOS 26 uses Icon Composer only.
- "Assets.xcassets/AppIcon.appiconset"
- "Resources/CustomLanguageModel/**"
- "Resources/Shortcuts/**"
# Real keyboard chrome for DEBUG what's-new recording (same UI as Ext).
- path: OSGKeyboardExt/Utilities/ExtL10n.swift
- path: OSGKeyboardExt/Views/AIKeyboardView.swift
- path: OSGKeyboardExt/Views/LastInputEditView.swift
- path: OSGKeyboardExt/Views/KeyboardTopControls.swift
- path: OSGKeyboardExt/Views/ClipboardKeyboardViews.swift
- path: OSGKeyboardExt/Views/ToolbarActionButtons.swift
- path: OSGKeyboardExt/Views/GlobeInputModeButton.swift
- path: OSGKeyboardExt/Views/NativeKeyboardKeyStyle.swift
- path: OSGKeyboardExt/Utilities/KeyboardHapticFeedback.swift
- path: OSGKeyboardExt/Utilities/KeyboardSoundFeedback.swift
- path: OSGKeyboardExt/en.lproj/Keyboard.strings
buildPhase: resources
- path: OSGKeyboardExt/zh-Hans.lproj/Keyboard.strings
buildPhase: resources
# `.shortcut` is not a XcodeGen-known resource type; list each file
# as an explicit resource source or it never lands in the app bundle.
- path: OSGKeyboard/Resources/Shortcuts/OSGSaveToNotes.shortcut
type: file
buildPhase: resources
entitlements:
path: OSGKeyboard/OSGKeyboard.entitlements
properties:
com.apple.security.application-groups:
- group.com.osgkeyboard.shared
com.apple.security.device.audio-input: true
# Shared Keychain access group: the host app writes the LLM API
# key here in Settings; the keyboard extension reads it before
# each request. The first entry below becomes each process's
# default access group, so the Keychain helper does not need to
# specify kSecAttrAccessGroup explicitly.
keychain-access-groups:
- $(AppIdentifierPrefix)com.osgkeyboard.shared
# Host-only account sessions use this second group explicitly.
# The keyboard extension is intentionally not entitled to it.
- $(AppIdentifierPrefix)com.osgkeyboard.ios
com.apple.developer.applesignin:
- Default
com.apple.developer.devicecheck.appattest-environment: $(APP_ATTEST_ENVIRONMENT)
com.apple.developer.associated-domains:
- applinks:osglab.com
# iCloud Key-Value Store — personal dictionary sync across
# iPhone / iPad / Mac (Designed for iPad). Main app only.
# NOTE: this entitlement is a single STRING, not an array — an
# array value never matches the profile and breaks automatic
# signing ("doesn't match the entitlements file's value").
com.apple.developer.ubiquity-kvstore-identifier: $(TeamIdentifierPrefix)com.osgkeyboard.ios
resources:
- path: OSGKeyboard/Assets.xcassets
- path: OSGKeyboard/en.lproj
- path: OSGKeyboard/zh-Hans.lproj
- path: OSGKeyboard/Resources/Fonts/MaterialIcons-Regular.ttf
- path: OSGKeyboard/Resources/PrivacyPolicy.html
- path: OSGKeyboard/PrivacyInfo.xcprivacy
# Host-only heavy assets — keep out of the keyboard extension Shared framework.
# CLM uses a unique iOS-only path (`HostCLM/`): XcodeGen dedupes identical
# file paths across iOS/Mac, which previously left the iOS app without CLM.
- path: OSGKeyboard/Resources/Typing/Rime/osg_pinyin.dict.yaml
- path: OSGKeyboard/Resources/Typing/Rime/manifest.json
- path: OSGKeyboard/Resources/HostCLM/v1/OSGKeyboardCLM.bin
- path: OSGKeyboard/Resources/HostCLM/v1/compiled-manifest.json
- path: OSGKeyboard/Resources/LocalASR/local-asr-catalog.json
- path: OSGKeyboard/Resources/Typing/Licenses/NOTICE.txt
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE.txt
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-PINYIN-SIMP-APACHE.txt
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-MATERIAL-ICONS-APACHE.txt
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-JIEBA-MIT.txt
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-PHRASE-PINYIN-DATA-MIT.txt
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-PINYIN-DATA-MIT.txt
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-NORVIG-PD.txt
- path: OSGKeyboard/Resources/Typing/Licenses/THIRD_PARTY_NOTICES.md
- path: OSGKeyboard/Resources/Typing/Licenses/third-party-notices.zip
- path: OSGKeyboard/Resources/Typing/Licenses/LIBRIME-COMBINED-NOTICES.txt
info:
path: OSGKeyboard/Info.plist
properties:
CFBundleDisplayName: OSGKeyboard
CFBundleShortVersionString: "$(MARKETING_VERSION)"
CFBundleVersion: "$(CURRENT_PROJECT_VERSION)"
LSApplicationCategoryType: public.app-category.utilities
# English is the development language; zh-Hans is the only
# translation right now. The list also tells iOS to surface
# the keyboard in the per-app language picker for both.
CFBundleDevelopmentRegion: en
CFBundleLocalizations:
- en
- zh-Hans
UILaunchScreen:
UIColorName: "BackgroundColor"
UISupportedInterfaceOrientations:
- UIInterfaceOrientationPortrait
UISupportedInterfaceOrientations~ipad:
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationPortraitUpsideDown
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
UIApplicationSceneManifest:
UIApplicationSupportsMultipleScenes: false
NSMicrophoneUsageDescription: "OSGKeyboard uses the microphone for voice dictation, including active Flow sessions while you type in other apps."
NSSpeechRecognitionUsageDescription: "OSGKeyboard uses speech recognition to transcribe your voice. Audio is processed on-device by default, or sent to your configured speech provider only when you enable cloud recognition."
NSContactsUsageDescription: "OSGKeyboard uses Contacts only when you choose Create Contact for a copied phone number."
NSRemindersFullAccessUsageDescription: "OSGKeyboard adds reminders only when you choose Extract To-dos for copied text."
NSCalendarsFullAccessUsageDescription: "OSGKeyboard adds calendar events only when you choose Extract Events for copied text."
NSPhotoLibraryAddUsageDescription: "OSGKeyboard saves your invitation poster to Photos only when you choose Save Image."
UIBackgroundModes:
- audio
BGTaskSchedulerPermittedIdentifiers:
- com.osgkeyboard.ios.analytics-sync
NSAppTransportSecurity:
NSAllowsArbitraryLoads: false
ITSAppUsesNonExemptEncryption: false
# StoreKit 2 can return finished consumable credit purchases in
# Transaction.all so the account center can show purchase history.
SKIncludeConsumableInAppPurchaseHistory: true
# Runtime-resolved, team-prefixed group used by the host-private
# account Keychain store. Never copy this key to the extension plist.
OSGPrivateKeychainAccessGroup: $(AppIdentifierPrefix)com.osgkeyboard.ios
CFBundleURLTypes:
- CFBundleURLName: com.osgkeyboard.ios.settings
CFBundleURLSchemes:
- osgkeyboard
LSApplicationQueriesSchemes:
- weixin
- mqq
- wxwork
- dingtalk
- lark
- tg
- whatsapp
- line
- fb-messenger
- slack
- msteams
- discord
- notion
- bear
- obsidian
- drafts5
- googlegmail
- ms-outlook
- googlechrome
- sinaweibo
- xhsdiscover
- sgnl
- kakaotalk
- viber
- zalo
- skype
- zoomus
- shortcuts
- iosamap
- amapuri
- baidumap
- maps
- things
- todoist
- evernote
- onenote
- readdle-spark
- firefox
- microsoft-edge
- fb
- instagram
- twitter
- barcelona
- snapchat
- reddit
- pinterest
- zhihu
- bilibili
- snssdk1128
- tiktok
# All network calls are HTTPS (NSAppTransportSecurity above);
# nothing in the app uses non-exempt encryption. Declaring
# `ITSAppUsesNonExemptEncryption: false` lets us skip the
# annual self-classification questionnaire in App Store
# Connect on every upload.
ITSAppUsesNonExemptEncryption: false
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios
TARGETED_DEVICE_FAMILY: "1,2"
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents: YES
INFOPLIST_KEY_LSApplicationCategoryType: public.app-category.utilities
SUPPORTS_MACCATALYST: NO
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD: NO
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD: NO
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
# TestFlight upload: Automatic signing picks the right App Store profile.
CODE_SIGN_STYLE: Automatic
DEVELOPMENT_TEAM: X329MZU23S
configs:
Debug:
APP_ATTEST_ENVIRONMENT: development
Release:
APP_ATTEST_ENVIRONMENT: production
dependencies:
- target: OSGKeyboardShared
embed: true
- target: OSGKeyboardHostSupport
embed: true
- target: OSGKeyboardExt
# Keyboard Extension is a plugin of the main App; embed it.
embed: true
buildPhase: embedAppExtensions
- sdk: Speech.framework
- sdk: StoreKit.framework
# =========================================================
# Keyboard Extension
# =========================================================
OSGKeyboardExt:
type: app-extension
platform: iOS
sources:
- path: OSGKeyboardExt
excludes:
- "en.lproj"
- "zh-Hans.lproj"
- path: OSGKeyboardExt/en.lproj/Keyboard.strings
buildPhase: resources
- path: OSGKeyboardExt/zh-Hans.lproj/Keyboard.strings
buildPhase: resources
# The extension shows its own microphone / speech prompts, so it needs
# its own InfoPlist.strings — the host app's copy does not cover it.
- path: OSGKeyboardExt/en.lproj/InfoPlist.strings
buildPhase: resources
- path: OSGKeyboardExt/zh-Hans.lproj/InfoPlist.strings
buildPhase: resources
resources:
- path: OSGKeyboardExt/Assets.xcassets
- path: OSGKeyboardExt/PrivacyInfo.xcprivacy
settings:
base:
IPHONEOS_DEPLOYMENT_TARGET: "26.0"
entitlements:
path: OSGKeyboardExt/OSGKeyboardExt.entitlements
properties:
com.apple.security.application-groups:
- group.com.osgkeyboard.shared
# Shared with the host app so the keyboard extension can read the
# user's LLM API key. See OSGKeyboard/OSGKeyboard.entitlements
# for the full rationale.
keychain-access-groups:
- $(AppIdentifierPrefix)com.osgkeyboard.shared
info:
path: OSGKeyboardExt/Info.plist
properties:
CFBundleDisplayName: OSGKeyboard
CFBundleShortVersionString: "$(MARKETING_VERSION)"
CFBundleVersion: "$(CURRENT_PROJECT_VERSION)"
CFBundleDevelopmentRegion: en
CFBundleLocalizations:
- en
- zh-Hans
NSMicrophoneUsageDescription: "OSGKeyboard uses the microphone for voice dictation, including active Flow sessions while you type in other apps."
NSSpeechRecognitionUsageDescription: "OSGKeyboard uses speech recognition to transcribe your voice. Audio is processed on-device by default, or sent to your configured speech provider only when you enable cloud recognition."
NSExtension:
NSExtensionAttributes:
IsASCIICapable: false
PrefersRightToLeft: false
PrimaryLanguage: "mis"
RequestsOpenAccess: true
NSExtensionPointIdentifier: com.apple.keyboard-service
NSExtensionPrincipalClass: $(PRODUCT_MODULE_NAME).KeyboardViewController
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.keyboard
SWIFT_OBJC_BRIDGING_HEADER: OSGKeyboardExt/OSGKeyboardExt-Bridging-Header.h
TARGETED_DEVICE_FAMILY: "1,2"
SUPPORTS_MACCATALYST: NO
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD: NO
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD: NO
# TestFlight upload: Automatic signing picks the right App Store profile.
CODE_SIGN_STYLE: Automatic
DEVELOPMENT_TEAM: X329MZU23S
dependencies:
- target: OSGKeyboardShared
embed: false
# Shared Framework (主 App 与扩展共用)
# =========================================================
OSGKeyboardShared:
type: framework
platform: iOS
sources:
- path: OSGKeyboardShared
excludes:
- "en.lproj"
- "zh-Hans.lproj"
- "**/*.swift.example"
- "Resources/**"
- "PrivacyInfo.xcprivacy"
- path: OSGKeyboardShared/en.lproj/Shared.strings
buildPhase: resources
- path: OSGKeyboardShared/zh-Hans.lproj/Shared.strings
buildPhase: resources
# Privacy manifest required for LevelDB statically linked via librime
# (ITMS-91061). Must live in this framework bundle, not only the app/ext.
- path: OSGKeyboardShared/PrivacyInfo.xcprivacy
buildPhase: resources
# Prefer sources+buildPhase over target-level `resources:` — the latter
# was not emitting Copy Bundle Resources entries for this framework.
- path: OSGKeyboardShared/Resources/Typing/English/english_lexicon.bin
buildPhase: resources
- path: OSGKeyboardShared/Resources/PolishStyles
buildPhase: resources
- path: OSGKeyboardShared/Resources/ClipboardSemantics
buildPhase: resources
- path: OSGRimeBridge
excludes:
- "include/**"
info:
path: OSGKeyboardShared/Info.plist
properties:
CFBundleShortVersionString: "$(MARKETING_VERSION)"
CFBundleVersion: "$(CURRENT_PROJECT_VERSION)"
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.shared
TARGETED_DEVICE_FAMILY: "1,2"
DEFINES_MODULE: YES
SKIP_INSTALL: YES
BUILD_LIBRARY_FOR_DISTRIBUTION: NO
DYLIB_INSTALL_NAME_BASE: "@rpath"
APPLICATION_EXTENSION_API_ONLY: YES
SWIFT_OBJC_BRIDGING_HEADER: OSGRimeBridge/include/OSGRimeBridge.h
CLANG_ENABLE_MODULES: YES
OTHER_CPLUSPLUSFLAGS: "$(inherited) -fmodules -fcxx-modules"
ENABLE_MODULE_VERIFIER: YES
# Embedded frameworks are re-signed by the containing app at package
# time. Signing this framework as a standalone build product is noisy
# and not recommended by Xcode.
CODE_SIGNING_ALLOWED: NO
CODE_SIGNING_REQUIRED: NO
CODE_SIGN_IDENTITY: ""
dependencies:
- package: Librime
product: RimeStatic
- sdk: libsqlite3.tbd
# Host-only support framework (ASR / CLM / Cloud / Charts / StoreKit).
# Keyboard extension does NOT link this — keeps Speech/AVFoundation/Charts
# out of the extension jetsam budget.
OSGKeyboardHostSupport:
type: framework
platform: iOS
sources:
- path: OSGKeyboardHostSupport
info:
path: OSGKeyboardHostSupport/Info.plist
properties:
CFBundleShortVersionString: "$(MARKETING_VERSION)"
CFBundleVersion: "$(CURRENT_PROJECT_VERSION)"
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.hostsupport
TARGETED_DEVICE_FAMILY: "1,2"
DEFINES_MODULE: YES
SKIP_INSTALL: YES
BUILD_LIBRARY_FOR_DISTRIBUTION: NO
DYLIB_INSTALL_NAME_BASE: "@rpath"
APPLICATION_EXTENSION_API_ONLY: NO
ENABLE_MODULE_VERIFIER: YES
CODE_SIGNING_ALLOWED: NO
CODE_SIGNING_REQUIRED: NO
CODE_SIGN_IDENTITY: ""
dependencies:
- target: OSGKeyboardShared
embed: false
- sdk: Speech.framework
- sdk: AVFoundation.framework
- sdk: Charts.framework
- sdk: StoreKit.framework
# =========================================================
# 单元测试
# =========================================================
OSGKeyboardTests:
type: bundle.unit-test
platform: iOS
sources:
- path: OSGKeyboardTests
- path: Tests/ProviderToolRequestCoordinatorTests.swift
info:
path: OSGKeyboardTests/Info.plist
properties:
CFBundleShortVersionString: "$(MARKETING_VERSION)"
CFBundleVersion: "$(CURRENT_PROJECT_VERSION)"
dependencies:
- target: OSGKeyboard
- target: OSGKeyboardHostSupport
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.tests
TARGETED_DEVICE_FAMILY: "1,2"
# =========================================================
# 键盘扩展单元测试 (TEST-4)
# =========================================================
OSGKeyboardExtTests:
type: bundle.unit-test
platform: iOS
sources:
- path: OSGKeyboardExtTests
- path: OSGKeyboardExt/Services/EditHintScheduler.swift
- path: OSGKeyboardExt/Services/ClipboardCaptureCoordinator.swift
# Host-only dict fixture for librime deploy tests (not linked into Ext).
- path: OSGKeyboard/Resources/Typing/Rime/osg_pinyin.dict.yaml
buildPhase: resources
info:
path: OSGKeyboardExtTests/Info.plist
properties:
CFBundleShortVersionString: "$(MARKETING_VERSION)"
CFBundleVersion: "$(CURRENT_PROJECT_VERSION)"
dependencies:
- target: OSGKeyboardShared
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.ext.tests
TARGETED_DEVICE_FAMILY: "1,2"
# =========================================================
# UI 测试:确定性宿主在模拟器运行,PiP/真实键盘链路保留实机验收
# =========================================================
# XCUITest 是 Apple 唯一开放的物理设备触摸注入通道;模拟器可运行
# 内嵌宿主回归,但无法复现 PiP(isPictureInPictureSupported 恒为 false)。
OSGKeyboardUITests:
type: bundle.ui-testing
platform: iOS
sources:
- path: OSGKeyboardUITests
info:
path: OSGKeyboardUITests/Info.plist
properties:
CFBundleShortVersionString: "$(MARKETING_VERSION)"
CFBundleVersion: "$(CURRENT_PROJECT_VERSION)"
dependencies:
- target: OSGKeyboard
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.uitests
TARGETED_DEVICE_FAMILY: "1,2"
CODE_SIGN_STYLE: Automatic
DEVELOPMENT_TEAM: X329MZU23S
# =========================================================
# macOS 单元测试
# =========================================================
# Hosted in the Mac app so `@testable import OSGKeyboard` reaches the
# macOS-only types (MacAudioRecorder, overlay sizing) that cannot compile
# against the iOS targets.
OSGKeyboardMacTests:
type: bundle.unit-test
platform: macOS
deploymentTarget: "15.0"
sources:
- path: OSGKeyboardMacTests
- path: Tests/ProviderToolRequestCoordinatorTests.swift
info:
path: OSGKeyboardMacTests/Info.plist
properties:
CFBundleShortVersionString: "$(MARKETING_VERSION)"
CFBundleVersion: "$(CURRENT_PROJECT_VERSION)"
dependencies:
- target: OSGKeyboardMac
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.mac.tests
MACOSX_DEPLOYMENT_TARGET: "15.0"
CODE_SIGN_STYLE: Automatic
DEVELOPMENT_TEAM: X329MZU23S
# The host target is named OSGKeyboardMac but ships as OSGKeyboard.app,
# so the path XcodeGen infers from the target name does not exist.
TEST_HOST: "$(BUILT_PRODUCTS_DIR)/OSGKeyboard.app/Contents/MacOS/OSGKeyboard"
BUNDLE_LOADER: "$(TEST_HOST)"
# =========================================================
# macOS 菜单栏 App
# =========================================================
# Standalone menu-bar utility: record → local/cloud ASR → polish → insert.
# Reuses the platform-agnostic core files from OSGKeyboardShared at the
# source level (no framework), excluding the iOS-only files that import
# SpeechAnalyzer / AVAudioSession / UIKit / SwiftUI views. Local mode uses
# Qwen3 MLX streaming via mlx-audio-swift plus catalog-managed weights.
OSGKeyboardMac:
type: application
platform: macOS
deploymentTarget: "15.0"
sources:
# Same Icon Composer asset as the iOS app — do not ship a separate PNG
# appiconset; actool crashes if both coexist.
- path: OSGKeyboard/AppIcon.icon
buildPhase: resources
# Reuse the iOS asset catalog for provider logos + brand mark. Exclude
# the PNG appiconset so it doesn't clash with AppIcon.icon above.
- path: OSGKeyboard/Assets.xcassets
excludes:
- "AppIcon.appiconset"
- path: OSGKeyboardMac
- path: OSGKeyboard/Services/OpenSourceLicenseCatalog.swift
# Account stack shared with the iOS app at the source level. These files
# are platform-agnostic (no UIKit); the iOS-only SwiftUI account center
# stays behind in OSGKeyboard/Views/Account and Mac ships MacAccountView.
# AccountCreditStore / AccountCreditPurchaseManager compile here because
# AccountSessionCoordinator owns one, but Mac never surfaces purchase UI:
# Developer ID builds cannot use StoreKit. Credits are bought on iOS/web
# and only consumed here.
- path: OSGKeyboard/Views/Account/AccountCenterModels.swift
- path: OSGKeyboard/Views/Account/AccountSessionCoordinator.swift
- path: OSGKeyboard/Views/Account/AccountCreditStore.swift
- path: OSGKeyboard/Views/Account/AccountCreditPurchaseManager.swift
- path: OSGKeyboard/Views/Account/LiveAccountServices.swift
- path: OSGKeyboard/Views/Account/ReferralProfileViewModel.swift
# Invitation QR poster: pure SwiftUI + Core Image, shared with iOS.
- path: OSGKeyboard/Views/Account/InvitePoster.swift
- path: OSGKeyboard/Views/Account/AccountAppleAuthorizationFactories.swift
- path: OSGKeyboard/Services/AccountAnalyticsBearerBridge.swift
- path: OSGKeyboardHostSupport
excludes:
# iOS SpeechAnalyzer / AVAudioSession capture — Mac has its own paths.
- "Services/ASRService.swift"
- "Services/CloudASR/CloudASRService.swift"
- "Services/FlowAudioSessionCoordinator.swift"
- "Services/FlowCaptureVoiceProcessing.swift"
- "Services/FlowContinuousCapture.swift"
- "Services/LiveDictationController.swift"
- "Models/AudioBufferSnapshot+AVFoundation.swift"
- "Info.plist"
- path: OSGKeyboardShared
excludes:
- "en.lproj"
- "zh-Hans.lproj"
- "Info.plist"
- "**/*.swift.example"
- "Resources/**"
- "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"
- path: OSGKeyboardShared/en.lproj/Shared.strings
buildPhase: resources
- path: OSGKeyboardShared/zh-Hans.lproj/Shared.strings
buildPhase: resources
- path: OSGKeyboardShared/Resources/PolishStyles
buildPhase: resources
- path: OSGKeyboard/Resources/CustomLanguageModel/v1/phrases.tsv
buildPhase: resources
- path: OSGKeyboard/Resources/CustomLanguageModel/v1/OSGKeyboardCLM.bin
buildPhase: resources
- path: OSGKeyboard/Resources/CustomLanguageModel/v1/compiled-manifest.json
buildPhase: resources
- path: OSGKeyboard/Resources/PrivacyPolicy.html
buildPhase: resources
- path: OSGKeyboard/Resources/LocalASR/local-asr-catalog.json
buildPhase: resources
- path: OSGKeyboard/Resources/Typing/Licenses/LIBRIME-COMBINED-NOTICES.txt
buildPhase: resources
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-PINYIN-SIMP-APACHE.txt
buildPhase: resources
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-MATERIAL-ICONS-APACHE.txt
buildPhase: resources
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-JIEBA-MIT.txt
buildPhase: resources
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-PHRASE-PINYIN-DATA-MIT.txt
buildPhase: resources
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-PINYIN-DATA-MIT.txt
buildPhase: resources
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-NORVIG-PD.txt
buildPhase: resources
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE.txt
buildPhase: resources
- path: OSGKeyboard/Resources/Typing/Licenses/NOTICE.txt
buildPhase: resources
entitlements:
path: OSGKeyboardMac/OSGKeyboardMac.entitlements
properties:
# Sandbox disabled on purpose. Precisely: App Sandbox does not itself
# ban the Accessibility API — TCC does, and sandboxed Mac App Store
# apps can be granted it. What makes the sandbox a bad trade here is
# the combination this app needs: a global `.flagsChanged` monitor over
# every keystroke (MacHotkeyService) plus a synthesized ⌘V into other
# apps (MacTextInsertionService). That pairing is a review risk not
# worth taking just to gain StoreKit. Ship Developer ID + notarized.
#
# Consequence for the account stack: no in-app purchases here. Credits
# are bought on iOS/web and only spent on Mac. Sign in with Apple and
# the account API need no sandbox and no Mac App Store.
# NSPasteboard, for the record, is unrestricted under the sandbox —
# clipboard capture was never the reason to opt out.
com.apple.security.app-sandbox: false
com.apple.security.device.audio-input: true
com.apple.security.network.client: true
com.apple.developer.ubiquity-kvstore-identifier: $(TeamIdentifierPrefix)com.osgkeyboard.ios
# Sign in with Apple needs no sandbox and no Mac App Store — a
# Developer ID + notarized build is enough. The Mac App ID must be
# grouped under the iOS primary App ID in the developer portal, or the
# same person gets a different Apple `sub` here than on iOS and the
# server treats them as two accounts.
com.apple.developer.applesignin:
- Default
keychain-access-groups:
- $(AppIdentifierPrefix)com.osgkeyboard.shared
# Host-private account credentials (session tokens, Apple user id).
# Never the .shared group — that one is reachable by the iOS keyboard
# extension; see HostPrivateAccountKeychainDescriptor.
- $(AppIdentifierPrefix)com.osgkeyboard.mac
info:
path: OSGKeyboardMac/Info.plist
properties:
CFBundleDisplayName: OSGKeyboard
CFBundleShortVersionString: "$(MARKETING_VERSION)"
CFBundleVersion: "$(CURRENT_PROJECT_VERSION)"
CFBundleDevelopmentRegion: en
CFBundleLocalizations:
- en
- zh-Hans
LSApplicationCategoryType: public.app-category.utilities
OSGPrivateKeychainAccessGroup: $(AppIdentifierPrefix)com.osgkeyboard.mac
NSMicrophoneUsageDescription: "OSGKeyboard uses the microphone to transcribe your voice via your configured cloud speech provider."
NSSpeechRecognitionUsageDescription: "OSGKeyboard uses on-device speech recognition for local dictation mode."
NSHumanReadableCopyright: "OSGKeyboard"
ITSAppUsesNonExemptEncryption: false
CFBundleURLTypes:
- CFBundleURLName: com.osgkeyboard.mac.seed
CFBundleURLSchemes:
- osgkeyboard
settings:
base:
PRODUCT_NAME: OSGKeyboard
INFOPLIST_KEY_CFBundleDisplayName: OSGKeyboard
INFOPLIST_KEY_CFBundleName: OSGKeyboard
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.mac
MACOSX_DEPLOYMENT_TARGET: "15.0"
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
# macOS app is not an app extension; override the framework-wide flag.
APPLICATION_EXTENSION_API_ONLY: NO
CODE_SIGN_STYLE: Automatic
DEVELOPMENT_TEAM: X329MZU23S
# Required for Developer ID distribution + notarization (outside App Store).
ENABLE_HARDENED_RUNTIME: YES
dependencies:
- package: MLXAudio
product: MLXAudioSTT
- sdk: Speech.framework
- sdk: AVFoundation.framework
- sdk: Charts.framework
- sdk: StoreKit.framework
- sdk: libsqlite3.tbd
schemes:
OSGKeyboard:
build:
targets:
OSGKeyboard: all
OSGKeyboardExt: all
run:
config: Debug
storeKitConfiguration: OSGKeyboard.storekit
test:
config: Debug
targets:
- OSGKeyboardTests
- OSGKeyboardExtTests
archive:
config: Release
# Device-only reproduction harness; kept out of the main scheme so CI and
# Scripts/run-tests.sh keep running unit tests without a physical device.
OSGKeyboardUITests:
build:
targets:
OSGKeyboard: all
OSGKeyboardExt: all
OSGKeyboardUITests: [test]
test:
config: Debug
targets:
- OSGKeyboardUITests
OSGKeyboardMac:
build:
targets:
OSGKeyboardMac: all
run:
config: Debug
test:
config: Debug
targets:
- OSGKeyboardMacTests
archive:
config: Release