feat(keyboard): ship AI mode surface with streaming search answers

Add the AI keyboard tab, Agent settings, and user-owned LLM key path for 1.7.0, including streaming answers and web-search transports without the built-in DeepSeek fallback.
This commit is contained in:
Rocky
2026-08-11 01:06:27 +08:00
parent f6212dcd2c
commit 3de665d254
81 changed files with 4467 additions and 398 deletions
@@ -37,6 +37,7 @@ public struct AppGroupConfiguration: Sendable, Equatable {
public static let cursorDragNavigationEnabled = "config.cursorDragNavigationEnabled"
public static let keyboardHapticIntensity = "config.keyboardHapticIntensity"
public static let polishIntensity = "config.polishIntensity"
public static let aiResponseLength = "config.aiResponseLength"
public static let llmThinkingEnabled = "config.llmThinkingEnabled"
public static let detectedAppContext = "config.detectedAppContext"
public static let detectedAppContextAt = "config.detectedAppContextAt"
@@ -89,6 +90,8 @@ public struct AppGroupConfiguration: Sendable, Equatable {
public var keyboardHapticIntensity: KeyboardHapticIntensity
/// Safety envelope for built-in fun polish styles (light by default).
public var polishIntensity: PolishIntensity
/// Soft AI-mode answer length preference (medium by default).
public var aiResponseLength: AIResponseLength
/// Enables provider-specific reasoning / thinking controls for polish LLM requests.
public var llmThinkingEnabled: Bool
public var personalDictionary: PersonalDictionary
@@ -146,10 +149,7 @@ public struct AppGroupConfiguration: Sendable, Equatable {
}
public var isPolishKeyMissing: Bool {
if !apiKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
return false
}
return !PreconfiguredKeys.isDeepseekConfigured
apiKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
}
public var isCloudAPIKeyMissingForVoiceInput: Bool {
@@ -265,6 +265,9 @@ public struct AppGroupConfiguration: Sendable, Equatable {
polishIntensity: PolishIntensity.resolve(
storedRawValue: defaults.string(forKey: Keys.polishIntensity)
),
aiResponseLength: AIResponseLength.resolve(
storedRawValue: defaults.string(forKey: Keys.aiResponseLength)
),
llmThinkingEnabled: defaults.bool(forKey: Keys.llmThinkingEnabled),
personalDictionary: decodePersonalDictionary(from: defaults),
polishStyleCatalog: decodePolishStyleCatalog(from: defaults),
@@ -396,6 +399,7 @@ public struct AppGroupConfiguration: Sendable, Equatable {
defaults.set(cursorDragNavigationEnabled, forKey: Keys.cursorDragNavigationEnabled)
defaults.set(keyboardHapticIntensity.rawValue, forKey: Keys.keyboardHapticIntensity)
defaults.set(polishIntensity.rawValue, forKey: Keys.polishIntensity)
defaults.set(aiResponseLength.rawValue, forKey: Keys.aiResponseLength)
defaults.set(llmThinkingEnabled, forKey: Keys.llmThinkingEnabled)
defaults.set(activePolishStyleId, forKey: Keys.activePolishStyleId)
defaults.set(flowSkipAppSwitch, forKey: Keys.flowSkipAppSwitch)