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
@@ -75,6 +75,8 @@ public final class TypingInputConfiguration: ObservableObject {
static let schema = "typing.input.schema"
static let fuzzyPairs = "typing.input.fuzzyPairs"
static let defaultToTyping = "typing.input.defaultToTyping"
static let rememberLastSurface = "typing.input.rememberLastSurface"
static let lastSurface = "typing.input.lastSurface"
static let resourceVersion = "typing.rime.resourceVersion"
}
@@ -90,10 +92,16 @@ public final class TypingInputConfiguration: ObservableObject {
}
/// Selects the text keyboard whenever the extension becomes visible.
/// Ignored when `rememberLastSurface` is on and a prior surface was saved.
@Published public var defaultToTyping: Bool {
didSet { persistIfReady() }
}
/// When on, reopen on the voice/typing surface left at the last dismiss.
@Published public var rememberLastSurface: Bool {
didSet { persistIfReady() }
}
public init(defaults: UserDefaults? = nil) {
self.defaults = defaults ?? AppGroup.defaults
let schemaId = self.defaults.string(forKey: Key.schema) ?? ""
@@ -101,6 +109,7 @@ public final class TypingInputConfiguration: ObservableObject {
let fuzzyIds = self.defaults.stringArray(forKey: Key.fuzzyPairs) ?? []
fuzzyPairs = Set(fuzzyIds.compactMap(PinyinFuzzyPair.init(rawValue:)))
defaultToTyping = self.defaults.bool(forKey: Key.defaultToTyping)
rememberLastSurface = self.defaults.bool(forKey: Key.rememberLastSurface)
isHydrating = false
}
@@ -123,15 +132,48 @@ public final class TypingInputConfiguration: ObservableObject {
let fuzzyIds = defaults.stringArray(forKey: Key.fuzzyPairs) ?? []
fuzzyPairs = Set(fuzzyIds.compactMap(PinyinFuzzyPair.init(rawValue:)))
defaultToTyping = defaults.bool(forKey: Key.defaultToTyping)
rememberLastSurface = defaults.bool(forKey: Key.rememberLastSurface)
isHydrating = false
}
/// Legacy helper for the default-to-typing toggle only (not full open policy).
nonisolated public static func prefersTypingOnOpen(
defaults: UserDefaults? = nil
) -> Bool {
(defaults ?? AppGroup.defaultsIfAvailable)?.bool(forKey: Key.defaultToTyping) ?? false
}
nonisolated public static func remembersLastSurface(
defaults: UserDefaults? = nil
) -> Bool {
(defaults ?? AppGroup.defaultsIfAvailable)?.bool(forKey: Key.rememberLastSurface) ?? false
}
/// Surface to show on the first frame of a keyboard presentation.
/// Prefer last-left surface when remembering; otherwise default-to-typing.
nonisolated public static func preferredSurfaceOnOpen(
defaults: UserDefaults? = nil
) -> KeyboardState.Surface {
let store = defaults ?? AppGroup.defaultsIfAvailable
guard let store else { return .voice }
if store.bool(forKey: Key.rememberLastSurface),
let raw = store.string(forKey: Key.lastSurface),
let surface = KeyboardState.Surface(rawValue: raw) {
return surface
}
return store.bool(forKey: Key.defaultToTyping) ? .typing : .voice
}
/// Persist the surface present when the keyboard leaves the screen.
nonisolated public static func persistLastSurface(
_ surface: KeyboardState.Surface,
defaults: UserDefaults? = nil
) {
(defaults ?? AppGroup.defaultsIfAvailable)?.set(surface.rawValue, forKey: Key.lastSurface)
}
nonisolated public static func installedResourceVersion(
defaults: UserDefaults? = nil
) -> String? {
@@ -150,6 +192,7 @@ public final class TypingInputConfiguration: ObservableObject {
defaults.set(schema.rawValue, forKey: Key.schema)
defaults.set(fuzzyPairs.map(\.rawValue).sorted(), forKey: Key.fuzzyPairs)
defaults.set(defaultToTyping, forKey: Key.defaultToTyping)
defaults.set(rememberLastSurface, forKey: Key.rememberLastSurface)
AppGroupConfigDarwin.postConfigChanged()
}
}