fix: keyboard stability, host-only analytics, and auto-reply dedupe
- record analytics/usage only in the host app; the extension no longer opens the SQLite queues, so hiding the keyboard cannot hold WAL locks (0xdead10cc) - sync clipboard history writes across processes so deletes made on one side are not reverted by the other - guard memory-relief reentrancy on controller and telemetry sides to break the recursive crash loop - resolve credentials off the main thread and migrate legacy keys once per install, so Keychain reads cannot be caught in the suspend window - gate Volcengine ASR ping completion to once (SIGTRAP on double resume) - treat same-text captures as the same logical copy and dedupe auto actions by content fingerprint within 30s, stopping Universal Clipboard storms - realign version line to 2.1.0 (build 110)
This commit is contained in:
@@ -47,7 +47,11 @@ public struct AppGroupPersistor {
|
||||
state.clipboardAutoEmailReplyEnabled = store.clipboardAutoEmailReplyEnabled
|
||||
applySkillSnapshot(store: store, into: state)
|
||||
state.keyboardHapticIntensity = store.keyboardHapticIntensity
|
||||
applyAPIKeyAvailability(store: store, into: state)
|
||||
// Credential availability is intentionally NOT resolved here: it needs
|
||||
// synchronous Keychain reads, which are a RunningBoard 0xdead10cc kill
|
||||
// vector on the extension's main thread inside the appear/suspend
|
||||
// window. KeyboardState's optimistic defaults stand until
|
||||
// `KeyboardConfigSync.refreshCredentialsFromKeychain()` lands.
|
||||
|
||||
#if DEBUG
|
||||
// Log only credential availability and the base URL origin. Never put
|
||||
@@ -106,7 +110,7 @@ public struct AppGroupPersistor {
|
||||
state.clipboardAutoEmailReplyEnabled = store.clipboardAutoEmailReplyEnabled
|
||||
applySkillSnapshot(store: store, into: state)
|
||||
state.keyboardHapticIntensity = store.keyboardHapticIntensity
|
||||
applyAPIKeyAvailability(store: store, into: state)
|
||||
// Credential availability is refreshed asynchronously — see load(into:).
|
||||
}
|
||||
|
||||
/// Resolve once outside SwiftUI body evaluation. Assigning the complete
|
||||
@@ -154,17 +158,22 @@ public struct AppGroupPersistor {
|
||||
)
|
||||
}
|
||||
|
||||
/// Applies a credential snapshot that was resolved OFF the main actor
|
||||
/// (see `KeyboardConfigSync.refreshCredentialsFromKeychain`). This method
|
||||
/// itself performs no Keychain access.
|
||||
///
|
||||
/// Cloud without ASR/LLM keys blocks the mic. Local ASR still works when
|
||||
/// the polish key is missing — show a soft tip above the mic instead.
|
||||
private func applyAPIKeyAvailability(
|
||||
store: AppGroupStore,
|
||||
public func applyCredentialAvailability(
|
||||
isPolishKeyMissing: Bool,
|
||||
isCloudAPIKeyMissingForVoiceInput: Bool,
|
||||
into state: KeyboardViewController.State
|
||||
) {
|
||||
state.aiServiceAvailable = !store.isPolishKeyMissing
|
||||
if store.isCloudAPIKeyMissingForVoiceInput {
|
||||
state.aiServiceAvailable = !isPolishKeyMissing
|
||||
if isCloudAPIKeyMissingForVoiceInput {
|
||||
state.micDisabled = true
|
||||
state.micDisabledHint = ExtL10n.string("keyboard.mic.disabled.missingApiKey")
|
||||
} else if store.isPolishKeyMissing {
|
||||
} else if isPolishKeyMissing {
|
||||
state.micDisabled = false
|
||||
state.micDisabledHint = ExtL10n.string("keyboard.mic.hint.missingPolishApiKey")
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user