79be7384dd
13 items, 555-line diff, build + 15/15 tests green.
ARCH-A3: Phase.error now carries ErrorKind (micDenied/speechDenied/asr/llm/
appGroupUnavailable/unknown) so the UI can pick icons/copy without parsing
free-form strings. Phase.ErrorKind, Phase, LLMError all Equatable.
ARCH-A4: Every TextField in APISettingsCard gets .keyboardType(.asciiCapable)
to defeat SwiftUI's iOS 18 system-keyboard hand-off that auto-suggests
Chinese/emoji and corrupts API keys / URLs / model names.
ARCH-A5 + DOC-3: PrivacyInfo.xcprivacy audited for honesty. Removed three
declared-but-unused APIs (FileTimestamp / DiskSpace / SystemBootTime) and
added ActiveKeyboards (DDA9.1) to the extension (it actually calls
advanceToNextInputMode in the tap path). Main App now declares only
UserDefaults (CA92.1). CHANGELOG updated.
ARCH-A6: Extracted PermissionManager (mic+speech permission flow, iOS 17
branching) and AppGroupPersistor (App Group load/persist) from the God
Object. KeyboardViewController drops 515 → 459 lines. KeyboardPipelineController
left in-place per risk plan — pressBegan state machine is too race-sensitive
to refactor in this pass.
RED-2: Deleted unused Theme enum (no call sites).
RED-3: Deleted unused cardStyle() alias (no call sites).
RED-7: Single source of truth for LLM timeout — LLMClient.requestTimeout +
LLMClientFactory.defaultRequestTimeout; PolishingService derives timeout from
defaultRequestTimeout+1 instead of hardcoding 15.
RED-8: ASRService.transcribe now emits .capability(onDeviceSupported:) as
first event per session; StatusBadge shows REC ⚠️ when the locale fell
back to cloud. New @Published var onDeviceSupported on State.
TEST-1: testPolishThrowsOnTransportTimeout now actually exercises
cancellation: StubURLProtocol delays response 5s, client.polish is
cancelled via Task.cancel(), test asserts the client throws .cancelled /
.transport / .decoding (was: silently passed).
TEST-2: New testPolisherSkipsNetworkWhenModeOff — PolishingService now
short-circuits when modeId == 'off' and returns trimmed input without
invoking LLMClient (proved via injected CountingLLMClient). Service was
moved to OSGKeyboardShared to be reachable from the test target.
TEST-3: KeyboardState (formerly KeyboardViewController.State) extracted
into OSGKeyboardShared so tests can @testable-import it. 5 phase/mode
tests in new KeyboardStateTests. Typealias preserves the old name.
TEST-4: New OSGKeyboardExtTests target with 6 tests covering State
initial values, phase transitions, structured-error round-trip, mode
switching, and InputMode rawValue round-trip.
185 lines
5.8 KiB
YAML
185 lines
5.8 KiB
YAML
# XcodeGen configuration for OSGKeyboard
|
|
# Run `xcodegen generate` to create OSGKeyboard.xcodeproj
|
|
# Repo only tracks project.yml; .xcodeproj is gitignored.
|
|
|
|
name: OSGKeyboard
|
|
options:
|
|
bundleIdPrefix: com.osgkeyboard
|
|
deploymentTarget:
|
|
iOS: "18.0"
|
|
developmentLanguage: en
|
|
createIntermediateGroups: true
|
|
generateEmptyDirectories: true
|
|
groupSortPosition: top
|
|
|
|
settings:
|
|
base:
|
|
SWIFT_VERSION: "6.0"
|
|
IPHONEOS_DEPLOYMENT_TARGET: "18.0"
|
|
ENABLE_USER_SCRIPT_SANDBOXING: YES
|
|
SWIFT_STRICT_CONCURRENCY: complete
|
|
GENERATE_INFOPLIST_FILE: NO
|
|
ENABLE_MODULE_VERIFIER: YES
|
|
CODE_SIGN_STYLE: Automatic
|
|
CODE_SIGN_IDENTITY: "Apple Development"
|
|
DEVELOPMENT_TEAM: ""
|
|
MARKETING_VERSION: "0.1.0"
|
|
CURRENT_PROJECT_VERSION: "1"
|
|
|
|
targets:
|
|
|
|
# =========================================================
|
|
# 主 App
|
|
# =========================================================
|
|
OSGKeyboard:
|
|
type: application
|
|
platform: iOS
|
|
sources:
|
|
- path: OSGKeyboard
|
|
entitlements:
|
|
path: OSGKeyboard/OSGKeyboard.entitlements
|
|
properties:
|
|
com.apple.security.application-groups:
|
|
- group.com.osgkeyboard.shared
|
|
com.apple.security.device.audio-input: true
|
|
resources:
|
|
- path: OSGKeyboard/Assets.xcassets
|
|
info:
|
|
path: OSGKeyboard/Info.plist
|
|
properties:
|
|
CFBundleDisplayName: OSGKeyboard
|
|
CFBundleShortVersionString: "$(MARKETING_VERSION)"
|
|
CFBundleVersion: "$(CURRENT_PROJECT_VERSION)"
|
|
UILaunchScreen:
|
|
UIColorName: "BackgroundColor"
|
|
UISupportedInterfaceOrientations:
|
|
- UIInterfaceOrientationPortrait
|
|
UIApplicationSceneManifest:
|
|
UIApplicationSupportsMultipleScenes: false
|
|
NSMicrophoneUsageDescription: "OSGKeyboard needs microphone access to transcribe your voice into text."
|
|
NSSpeechRecognitionUsageDescription: "OSGKeyboard uses on-device speech recognition to transcribe your dictation. Audio never leaves your device."
|
|
NSAppTransportSecurity:
|
|
NSAllowsArbitraryLoads: false
|
|
CFBundleURLTypes:
|
|
- CFBundleURLName: com.osgkeyboard.ios.settings
|
|
CFBundleURLSchemes:
|
|
- osgkeyboard
|
|
settings:
|
|
base:
|
|
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios
|
|
TARGETED_DEVICE_FAMILY: "1,2"
|
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents: YES
|
|
dependencies:
|
|
- target: OSGKeyboardShared
|
|
embed: true
|
|
- target: OSGKeyboardExt
|
|
# Keyboard Extension is a plugin of the main App; embed it.
|
|
|
|
# =========================================================
|
|
# Keyboard Extension
|
|
# =========================================================
|
|
OSGKeyboardExt:
|
|
type: app-extension
|
|
platform: iOS
|
|
sources:
|
|
- path: OSGKeyboardExt
|
|
settings:
|
|
base:
|
|
IPHONEOS_DEPLOYMENT_TARGET: "18.0"
|
|
entitlements:
|
|
path: OSGKeyboardExt/OSGKeyboardExt.entitlements
|
|
properties:
|
|
com.apple.security.application-groups:
|
|
- group.com.osgkeyboard.shared
|
|
info:
|
|
path: OSGKeyboardExt/Info.plist
|
|
properties:
|
|
CFBundleDisplayName: OSGKeyboard
|
|
CFBundleShortVersionString: "$(MARKETING_VERSION)"
|
|
CFBundleVersion: "$(CURRENT_PROJECT_VERSION)"
|
|
NSMicrophoneUsageDescription: "OSGKeyboard needs microphone access to transcribe your voice into text."
|
|
NSSpeechRecognitionUsageDescription: "OSGKeyboard uses on-device speech recognition to transcribe your dictation. Audio never leaves your device."
|
|
NSExtension:
|
|
NSExtensionAttributes:
|
|
IsASCIICapable: false
|
|
PrefersRightToLeft: false
|
|
PrimaryLanguage: "en-US"
|
|
RequestsOpenAccess: true
|
|
NSExtensionPointIdentifier: com.apple.keyboard-service
|
|
NSExtensionPrincipalClass: $(PRODUCT_MODULE_NAME).KeyboardViewController
|
|
settings:
|
|
base:
|
|
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.keyboard
|
|
TARGETED_DEVICE_FAMILY: "1,2"
|
|
dependencies:
|
|
- target: OSGKeyboardShared
|
|
embed: false
|
|
|
|
# =========================================================
|
|
# Shared Framework (主 App 与扩展共用)
|
|
# =========================================================
|
|
OSGKeyboardShared:
|
|
type: framework
|
|
platform: iOS
|
|
sources:
|
|
- path: OSGKeyboardShared
|
|
info:
|
|
path: OSGKeyboardShared/Info.plist
|
|
settings:
|
|
base:
|
|
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.shared
|
|
DEFINES_MODULE: YES
|
|
SKIP_INSTALL: YES
|
|
BUILD_LIBRARY_FOR_DISTRIBUTION: NO
|
|
DYLIB_INSTALL_NAME_BASE: "@rpath"
|
|
APPLICATION_EXTENSION_API_ONLY: YES
|
|
ENABLE_MODULE_VERIFIER: YES
|
|
|
|
# =========================================================
|
|
# 单元测试
|
|
# =========================================================
|
|
OSGKeyboardTests:
|
|
type: bundle.unit-test
|
|
platform: iOS
|
|
sources:
|
|
- path: OSGKeyboardTests
|
|
info:
|
|
path: OSGKeyboardTests/Info.plist
|
|
dependencies:
|
|
- target: OSGKeyboard
|
|
settings:
|
|
base:
|
|
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.tests
|
|
|
|
# =========================================================
|
|
# 键盘扩展单元测试 (TEST-4)
|
|
# =========================================================
|
|
OSGKeyboardExtTests:
|
|
type: bundle.unit-test
|
|
platform: iOS
|
|
sources:
|
|
- path: OSGKeyboardExtTests
|
|
info:
|
|
path: OSGKeyboardExtTests/Info.plist
|
|
dependencies:
|
|
- target: OSGKeyboardShared
|
|
settings:
|
|
base:
|
|
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.ext.tests
|
|
|
|
schemes:
|
|
OSGKeyboard:
|
|
build:
|
|
targets:
|
|
OSGKeyboard: all
|
|
OSGKeyboardExt: all
|
|
run:
|
|
config: Debug
|
|
test:
|
|
config: Debug
|
|
targets:
|
|
- OSGKeyboardTests
|
|
- OSGKeyboardExtTests
|
|
archive:
|
|
config: Release
|