feat(keyboard): improve typing, voice flow, and polish reliability

Reduce extension memory pressure and delivery races while adding richer candidates, tactile feedback, and safer two-level creative polishing.
This commit is contained in:
Rocky
2026-08-05 21:39:31 +08:00
parent 38e5ad570d
commit 31f5937a7f
177 changed files with 8343 additions and 3904 deletions
+7 -28
View File
@@ -125,6 +125,8 @@ public final class KeyboardState: ObservableObject {
@Published public var returnKeyRole: ReturnKeyRole = .newline
/// Press-and-drag pads beside the mic for four-way caret movement.
@Published public var cursorDragNavigationEnabled: Bool = true
/// Typing-grid haptic strength (off / light / strong).
@Published public var keyboardHapticIntensity: KeyboardHapticIntensity = .default
/// `true` while a cursor-drag pad is being pressed drives the hint
/// shown above the mic.
@Published public var cursorDragActive: Bool = false
@@ -139,31 +141,12 @@ public final class KeyboardState: ObservableObject {
/// Convenience shorthand used by the pipeline and views.
public var isLocalEngine: Bool { engineMode == "local" }
// MARK: - First-launch onboarding (mirrored from ProviderConfig)
// MARK: - Host-app onboarding gate
/// Drives the in-keyboard onboarding overlay. When `false`, the
/// keyboard shows a step-by-step overlay instead of the normal UI;
/// when `true`, normal UI renders. Mirrored from `ProviderConfig`
/// so the keyboard never has to instantiate the main-app config.
/// Mirrored from App Group / Keychain. Setup UI lives only in the host
/// app; the keyboard uses this flag to gate voice (mic / Flow cold-start)
/// and prompt a jump back to the app when incomplete.
@Published public var hasCompletedOnboarding: Bool = false
/// Step the user is currently on (0-based). The overlay reads this
/// to render the right page; main-app `ProviderConfig` is the
/// source of truth and the keyboard mirrors it.
@Published public var onboardingPage: Int = 0
/// `true` when the user tapped something (mic, settings) right
/// before a forced jump to the host app. The keyboard reads this
/// on return and auto-resumes the action so the user does not have
/// to tap the same button twice.
@Published public var pendingResumeAction: ResumeAction = .none
/// Action the keyboard should auto-trigger after a host-app jump
/// completes. Set just before `openHostApp`, consumed (set back to
/// `.none`) after the action fires once.
public enum ResumeAction: Equatable {
case none
case startRecording
case openSettings
}
public enum ReturnKeyRole: Equatable {
case newline
@@ -200,6 +183,7 @@ public final class KeyboardState: ObservableObject {
case .noFullAccess: return "unavailable(noFullAccess)"
case .appGroupUnavailable: return "unavailable(appGroupUnavailable)"
case .missingAPIKey: return "unavailable(missingAPIKey)"
case .onboardingIncomplete: return "unavailable(onboardingIncomplete)"
}
}
}()
@@ -230,11 +214,6 @@ public final class KeyboardState: ObservableObject {
/// is derived from the locale id, so there's no separate toggle to
/// persist. Wired in `KeyboardViewController.installStateActions`.
public var setTranslationTargetLocaleId: (String) -> Void = { _ in }
public var advanceOnboarding: () -> Void = {}
public var completeOnboarding: () -> Void = {}
public var requestMicPermission: () -> Void = {}
public var requestSpeechPermission: () -> Void = {}
public var openSystemSettings: () -> Void = {}
public var insertNewline: () -> Void = {}
public var insertSpace: () -> Void = {}
public var deleteBackward: () -> Void = {}