[JJC-20260618-005-A] Review-driven P0 polish
6 review-driven small fixes to the P0 commit series (e0b92ff/e93bab5/2e2d8e3/013c498). No behavior changes, no new features, no push yet. Code: - RED-1: Remove empty init() from OSGKeyboardApp; @StateObject is initialized inline and the only thing init() did was print a DEBUG log. - RED-4: Re-add .preferredColorScheme(.dark) to the 3 keyboard extension #Preview blocks. Custom keyboards always render dark; the preview should match. - RED-6: ThemedRoot #Preview blocks now use the new EnvironmentKey (\.themePalette) via a private ThemedPreviewContent, instead of hard-coding Palette.dark / Palette.light. - BUG-5: ThemedRoot body now also fills the background with the active palette's background color (repeats the colorScheme ternary — accepted because body must be a single expression). Docs: - DOC-1: CHANGELOG [Unreleased] top note: this is a v0.1.0 → v0.1.1 polish, nothing removed, iOS 26 SpeechAnalyzer still 0.2.0. - DOC-2: README + README.zh.md Limitations: explicit note for iOS 26+ users in v0.1.1. Verified: xcodebuild Debug-iphonesimulator BUILD SUCCEEDED, 8/8 unit tests pass on iPhone 17 sim (iOS 26.3.1).
This commit is contained in:
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
> **Note: v0.1.1 polish** — this is a small follow-up to v0.1.0 focused on review-driven cleanup
|
||||||
|
> (theme follow-up, ASR robustness, debug-print hygiene, docs). **No features are removed.**
|
||||||
|
> The iOS 26 `SpeechAnalyzer` path remains deferred to 0.2.0 (see below); v0.1.1 users continue
|
||||||
|
> to ship with the iOS 18 `SFSpeechRecognizer` path that shipped in v0.1.0. User experience is
|
||||||
|
> unchanged from v0.1.0.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- **Theme follows system appearance**: main App now renders a true light palette in light mode via `ThemedRoot` + `EnvironmentKey<ThemePalette>`. The keyboard extension deliberately stays dark (Apple's default) and now uses a transparent `.background(Color.clear)` so the system UI chrome shows through.
|
- **Theme follows system appearance**: main App now renders a true light palette in light mode via `ThemedRoot` + `EnvironmentKey<ThemePalette>`. The keyboard extension deliberately stays dark (Apple's default) and now uses a transparent `.background(Color.clear)` so the system UI chrome shows through.
|
||||||
- **Speech Recognition permission requested on first press**: added `NSSpeechRecognitionUsageDescription` to both targets' `Info.plist` and an explicit `SFSpeechRecognizer.requestAuthorization` call inside `pressBegan()`. Without these the iOS 18 ASR path silently returned `.denied` and the user heard nothing.
|
- **Speech Recognition permission requested on first press**: added `NSSpeechRecognitionUsageDescription` to both targets' `Info.plist` and an explicit `SFSpeechRecognizer.requestAuthorization` call inside `pressBegan()`. Without these the iOS 18 ASR path silently returned `.denied` and the user heard nothing.
|
||||||
|
|||||||
@@ -8,12 +8,6 @@ import OSGKeyboardShared
|
|||||||
struct OSGKeyboardApp: App {
|
struct OSGKeyboardApp: App {
|
||||||
@StateObject private var config = ProviderConfig.shared
|
@StateObject private var config = ProviderConfig.shared
|
||||||
|
|
||||||
init() {
|
|
||||||
#if DEBUG
|
|
||||||
print("🔥 [OSGKeyboardApp] init()")
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
ThemedRoot {
|
ThemedRoot {
|
||||||
|
|||||||
@@ -154,16 +154,19 @@ extension KeyboardRootView {
|
|||||||
#Preview("Keyboard · Idle") {
|
#Preview("Keyboard · Idle") {
|
||||||
KeyboardRootView(state: KeyboardViewController.State.previewIdle)
|
KeyboardRootView(state: KeyboardViewController.State.previewIdle)
|
||||||
.frame(width: 390, height: 280)
|
.frame(width: 390, height: 280)
|
||||||
|
.preferredColorScheme(.dark)
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview("Keyboard · Recording") {
|
#Preview("Keyboard · Recording") {
|
||||||
KeyboardRootView(state: KeyboardViewController.State.previewRecording)
|
KeyboardRootView(state: KeyboardViewController.State.previewRecording)
|
||||||
.frame(width: 390, height: 280)
|
.frame(width: 390, height: 280)
|
||||||
|
.preferredColorScheme(.dark)
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview("Keyboard · Processing") {
|
#Preview("Keyboard · Processing") {
|
||||||
KeyboardRootView(state: KeyboardViewController.State.previewProcessing)
|
KeyboardRootView(state: KeyboardViewController.State.previewProcessing)
|
||||||
.frame(width: 390, height: 280)
|
.frame(width: 390, height: 280)
|
||||||
|
.preferredColorScheme(.dark)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -21,31 +21,36 @@ public struct ThemedRoot<Content: View>: View {
|
|||||||
public var body: some View {
|
public var body: some View {
|
||||||
content()
|
content()
|
||||||
.environment(\.themePalette, colorScheme == .dark ? Palette.dark : Palette.light)
|
.environment(\.themePalette, colorScheme == .dark ? Palette.dark : Palette.light)
|
||||||
|
.background(colorScheme == .dark ? Palette.dark.background : Palette.light.background)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
#Preview("ThemedRoot · Dark") {
|
#Preview("ThemedRoot · Dark") {
|
||||||
ThemedRoot {
|
ThemedRoot {
|
||||||
ZStack {
|
ThemedPreviewContent(title: "Dark")
|
||||||
Palette.dark.background.ignoresSafeArea()
|
|
||||||
Text("Dark")
|
|
||||||
.foregroundStyle(Palette.dark.textPrimary)
|
|
||||||
.font(.title)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.preferredColorScheme(.dark)
|
.preferredColorScheme(.dark)
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview("ThemedRoot · Light") {
|
#Preview("ThemedRoot · Light") {
|
||||||
ThemedRoot {
|
ThemedRoot {
|
||||||
ZStack {
|
ThemedPreviewContent(title: "Light")
|
||||||
Palette.light.background.ignoresSafeArea()
|
|
||||||
Text("Light")
|
|
||||||
.foregroundStyle(Palette.light.textPrimary)
|
|
||||||
.font(.title)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.preferredColorScheme(.light)
|
.preferredColorScheme(.light)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct ThemedPreviewContent: View {
|
||||||
|
@Environment(\.themePalette) private var palette
|
||||||
|
let title: String
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
palette.background.ignoresSafeArea()
|
||||||
|
Text(title)
|
||||||
|
.foregroundStyle(palette.textPrimary)
|
||||||
|
.font(.title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -133,6 +133,7 @@ That's it. No other code changes required.
|
|||||||
- iOS sandboxes keyboard extensions: ~60 MB memory cap, Full Access required.
|
- iOS sandboxes keyboard extensions: ~60 MB memory cap, Full Access required.
|
||||||
- The keyboard does **not** work in password fields or some `WKWebView` textareas (iOS limitation).
|
- The keyboard does **not** work in password fields or some `WKWebView` textareas (iOS limitation).
|
||||||
- iOS 18/19 ships with `SFSpeechRecognizer` for on-device ASR. iOS 26+ `SpeechAnalyzer` is planned for the next release — it is significantly faster and supports more locales.
|
- iOS 18/19 ships with `SFSpeechRecognizer` for on-device ASR. iOS 26+ `SpeechAnalyzer` is planned for the next release — it is significantly faster and supports more locales.
|
||||||
|
- iOS 26+ users in v0.1.1 use the iOS 18 `SFSpeechRecognizer` path; the iOS 26 `SpeechAnalyzer` is planned for 0.2.0.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ LLMProvider(
|
|||||||
- iOS 沙盒:键盘扩展 ~60 MB 内存上限,必须开完全访问
|
- iOS 沙盒:键盘扩展 ~60 MB 内存上限,必须开完全访问
|
||||||
- 密码框与部分 `WKWebView` 输入框不可用(iOS 限制)
|
- 密码框与部分 `WKWebView` 输入框不可用(iOS 限制)
|
||||||
- iOS 18/19 用 `SFSpeechRecognizer` 做端侧 ASR;iOS 26+ 的 `SpeechAnalyzer` 计划下版接入(更快、支持语种更多)
|
- iOS 18/19 用 `SFSpeechRecognizer` 做端侧 ASR;iOS 26+ 的 `SpeechAnalyzer` 计划下版接入(更快、支持语种更多)
|
||||||
|
- v0.1.1 中 iOS 26+ 用户仍走 iOS 18 `SFSpeechRecognizer` 路径;iOS 26 `SpeechAnalyzer` 计划在 0.2.0 接入
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user