refactor(flow): remove Live Activity and keep-alive mode picker
Keep voice sessions on silent low-profile PiP only, and fix custom polish style editor presentation via sheet(item:).
This commit is contained in:
@@ -57,8 +57,6 @@ public struct AppGroupConfiguration: Sendable, Equatable {
|
||||
public static let settingsCloudPayloadV2 = "config.settings.cloudPayload.v2"
|
||||
/// When true, the host app auto-returns to the source app after a cold-start handoff.
|
||||
public static let flowSkipAppSwitch = "config.flowSkipAppSwitch"
|
||||
/// Raw `FlowKeepAliveMode` value; mutually exclusive PiP vs Live Activity path.
|
||||
public static let flowKeepAliveMode = "config.flowKeepAliveMode"
|
||||
/// Raw `FlowInactivityDuration` value; session expires after this idle window.
|
||||
public static let flowInactivityDuration = "config.flowInactivityDuration"
|
||||
/// One-shot: remap previous product defaults (30m / 10m) → 5m.
|
||||
@@ -102,8 +100,6 @@ public struct AppGroupConfiguration: Sendable, Equatable {
|
||||
public var settingsICloudSyncEnabled: Bool
|
||||
/// Auto-return to the host app after `startflow` cold start (default on).
|
||||
public var flowSkipAppSwitch: Bool
|
||||
/// PiP vs Live Activity keep-alive strategy (mutually exclusive).
|
||||
public var flowKeepAliveMode: FlowKeepAliveMode
|
||||
/// Idle timeout before the Flow session ends; resets on each utterance.
|
||||
public var flowInactivityDuration: FlowInactivityDuration
|
||||
/// Whether local `SpeechAnalyzer` should attach the prepared custom language model.
|
||||
@@ -292,9 +288,6 @@ public struct AppGroupConfiguration: Sendable, Equatable {
|
||||
}
|
||||
return defaults.bool(forKey: Keys.flowSkipAppSwitch)
|
||||
}(),
|
||||
flowKeepAliveMode: FlowKeepAliveMode.fromStored(
|
||||
defaults.string(forKey: Keys.flowKeepAliveMode)
|
||||
),
|
||||
flowInactivityDuration: FlowInactivityDuration.fromStored(
|
||||
defaults.string(forKey: Keys.flowInactivityDuration)
|
||||
),
|
||||
@@ -406,7 +399,6 @@ public struct AppGroupConfiguration: Sendable, Equatable {
|
||||
defaults.set(llmThinkingEnabled, forKey: Keys.llmThinkingEnabled)
|
||||
defaults.set(activePolishStyleId, forKey: Keys.activePolishStyleId)
|
||||
defaults.set(flowSkipAppSwitch, forKey: Keys.flowSkipAppSwitch)
|
||||
defaults.set(flowKeepAliveMode.rawValue, forKey: Keys.flowKeepAliveMode)
|
||||
defaults.set(flowInactivityDuration.rawValue, forKey: Keys.flowInactivityDuration)
|
||||
defaults.set(localASRCustomLanguageModelEnabled, forKey: Keys.localASRCustomLanguageModelEnabled)
|
||||
defaults.set(personalDictionaryICloudSyncEnabled, forKey: Keys.personalDictionaryICloudSyncEnabled)
|
||||
|
||||
@@ -32,18 +32,6 @@ public enum FlowInactivityDuration: String, CaseIterable, Identifiable, Sendable
|
||||
}
|
||||
}
|
||||
|
||||
public var labelKey: String {
|
||||
switch self {
|
||||
case .oneMinute: return "settings.flow.inactivity.1m"
|
||||
case .fiveMinutes: return "settings.flow.inactivity.5m"
|
||||
case .tenMinutes: return "settings.flow.inactivity.10m"
|
||||
case .thirtyMinutes: return "settings.flow.inactivity.30m"
|
||||
case .threeHours: return "settings.flow.inactivity.3h"
|
||||
case .twelveHours: return "settings.flow.inactivity.12h"
|
||||
case .twentyFourHours: return "settings.flow.inactivity.24h"
|
||||
}
|
||||
}
|
||||
|
||||
public static func fromStored(_ raw: String?) -> FlowInactivityDuration {
|
||||
guard let raw, let value = FlowInactivityDuration(rawValue: raw) else {
|
||||
return .default
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
// FlowKeepAliveMode.swift
|
||||
// OSGKeyboard · Shared
|
||||
//
|
||||
// User-selectable Flow session keep-alive strategy (mutually exclusive).
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum FlowKeepAliveMode: String, CaseIterable, Identifiable, Sendable, Codable {
|
||||
/// Continuous audio capture + Live Activity.
|
||||
case liveActivity = "liveActivity"
|
||||
/// Picture-in-picture waveform keep-alive; mic released between utterances.
|
||||
case pictureInPicture = "pictureInPicture"
|
||||
|
||||
public var id: String { rawValue }
|
||||
|
||||
/// Used when no valid keep-alive preference has been stored.
|
||||
public static let `default`: FlowKeepAliveMode = .pictureInPicture
|
||||
|
||||
public var labelKey: String {
|
||||
switch self {
|
||||
case .liveActivity: return "settings.flow.keepAlive.liveActivity"
|
||||
case .pictureInPicture: return "settings.flow.keepAlive.pictureInPicture"
|
||||
}
|
||||
}
|
||||
|
||||
public var subtitleKey: String {
|
||||
switch self {
|
||||
case .liveActivity: return "settings.flow.keepAlive.liveActivity.subtitle"
|
||||
case .pictureInPicture: return "settings.flow.keepAlive.pictureInPicture.subtitle"
|
||||
}
|
||||
}
|
||||
|
||||
public static func fromStored(_ raw: String?) -> FlowKeepAliveMode {
|
||||
guard let raw, let value = FlowKeepAliveMode(rawValue: raw) else {
|
||||
return .default
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
@@ -259,22 +259,7 @@ public final class ProviderConfig: ObservableObject, @unchecked Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
/// PiP vs Live Activity keep-alive (mutually exclusive).
|
||||
@Published public var flowKeepAliveMode: FlowKeepAliveMode {
|
||||
didSet {
|
||||
guard !isApplyingConfiguration, flowKeepAliveMode != configuration.flowKeepAliveMode else { return }
|
||||
configuration.flowKeepAliveMode = flowKeepAliveMode
|
||||
if flowKeepAliveMode == .pictureInPicture {
|
||||
configuration.flowSkipAppSwitch = true
|
||||
if flowSkipAppSwitch != true {
|
||||
flowSkipAppSwitch = true
|
||||
}
|
||||
}
|
||||
persistConfiguration()
|
||||
}
|
||||
}
|
||||
|
||||
/// Idle window before an active Flow session expires; Live Activity mode only.
|
||||
/// Retained for storage compatibility; persistent PiP sessions do not expire from inactivity.
|
||||
@Published public var flowInactivityDuration: FlowInactivityDuration {
|
||||
didSet {
|
||||
guard !isApplyingConfiguration,
|
||||
@@ -406,7 +391,6 @@ public final class ProviderConfig: ObservableObject, @unchecked Sendable {
|
||||
polishIntensity = configuration.polishIntensity
|
||||
llmThinkingEnabled = configuration.llmThinkingEnabled
|
||||
flowSkipAppSwitch = configuration.flowSkipAppSwitch
|
||||
flowKeepAliveMode = configuration.flowKeepAliveMode
|
||||
flowInactivityDuration = configuration.flowInactivityDuration
|
||||
localASRCustomLanguageModelEnabled = configuration.localASRCustomLanguageModelEnabled
|
||||
isSyncingProviderAPIKey = true
|
||||
@@ -498,7 +482,6 @@ public final class ProviderConfig: ObservableObject, @unchecked Sendable {
|
||||
polishIntensity = fresh.polishIntensity
|
||||
llmThinkingEnabled = fresh.llmThinkingEnabled
|
||||
flowSkipAppSwitch = fresh.flowSkipAppSwitch
|
||||
flowKeepAliveMode = fresh.flowKeepAliveMode
|
||||
flowInactivityDuration = fresh.flowInactivityDuration
|
||||
localASRCustomLanguageModelEnabled = fresh.localASRCustomLanguageModelEnabled
|
||||
isSyncingProviderAPIKey = true
|
||||
|
||||
@@ -31,7 +31,6 @@ public struct SyncedAppSettingsV2: Codable, Equatable, Sendable {
|
||||
public var activePolishStyleId: SyncedField<String>
|
||||
public var llmThinkingEnabled: SyncedField<Bool>
|
||||
public var flowSkipAppSwitch: SyncedField<Bool>
|
||||
public var flowKeepAliveMode: SyncedField<FlowKeepAliveMode>
|
||||
public var flowInactivityDuration: SyncedField<FlowInactivityDuration>
|
||||
|
||||
public init(
|
||||
@@ -55,7 +54,6 @@ public struct SyncedAppSettingsV2: Codable, Equatable, Sendable {
|
||||
activePolishStyleId: SyncedField<String>,
|
||||
llmThinkingEnabled: SyncedField<Bool>,
|
||||
flowSkipAppSwitch: SyncedField<Bool>,
|
||||
flowKeepAliveMode: SyncedField<FlowKeepAliveMode>,
|
||||
flowInactivityDuration: SyncedField<FlowInactivityDuration>
|
||||
) {
|
||||
self.schemaVersion = schemaVersion
|
||||
@@ -82,7 +80,6 @@ public struct SyncedAppSettingsV2: Codable, Equatable, Sendable {
|
||||
self.activePolishStyleId = activePolishStyleId
|
||||
self.llmThinkingEnabled = llmThinkingEnabled
|
||||
self.flowSkipAppSwitch = flowSkipAppSwitch
|
||||
self.flowKeepAliveMode = flowKeepAliveMode
|
||||
self.flowInactivityDuration = flowInactivityDuration
|
||||
}
|
||||
|
||||
@@ -107,10 +104,13 @@ public struct SyncedAppSettingsV2: Codable, Equatable, Sendable {
|
||||
case activePolishStyleId
|
||||
case llmThinkingEnabled
|
||||
case flowSkipAppSwitch
|
||||
case flowKeepAliveMode
|
||||
case flowInactivityDuration
|
||||
}
|
||||
|
||||
private enum LegacyCodingKeys: String, CodingKey {
|
||||
case flowKeepAliveMode
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
schemaVersion = try container.decode(Int.self, forKey: .schemaVersion)
|
||||
@@ -167,18 +167,15 @@ public struct SyncedAppSettingsV2: Codable, Equatable, Sendable {
|
||||
deviceID: keyboardHapticIntensity.deviceID
|
||||
)
|
||||
flowSkipAppSwitch = try container.decode(SyncedField<Bool>.self, forKey: .flowSkipAppSwitch)
|
||||
flowKeepAliveMode = try container.decodeIfPresent(
|
||||
SyncedField<FlowKeepAliveMode>.self,
|
||||
forKey: .flowKeepAliveMode
|
||||
) ?? SyncedField(
|
||||
value: .liveActivity,
|
||||
updatedAt: flowSkipAppSwitch.updatedAt,
|
||||
deviceID: flowSkipAppSwitch.deviceID
|
||||
)
|
||||
flowInactivityDuration = try container.decode(
|
||||
SyncedField<FlowInactivityDuration>.self,
|
||||
forKey: .flowInactivityDuration
|
||||
)
|
||||
let legacyContainer = try decoder.container(keyedBy: LegacyCodingKeys.self)
|
||||
_ = try legacyContainer.decodeIfPresent(
|
||||
SyncedField<String>.self,
|
||||
forKey: .flowKeepAliveMode
|
||||
)
|
||||
|
||||
if let asrProvider = try container.decodeIfPresent(SyncedField<String>.self, forKey: .asrProviderId) {
|
||||
asrProviderId = asrProvider
|
||||
@@ -224,7 +221,6 @@ public struct SyncedAppSettingsV2: Codable, Equatable, Sendable {
|
||||
activePolishStyleId.updatedAt,
|
||||
llmThinkingEnabled.updatedAt,
|
||||
flowSkipAppSwitch.updatedAt,
|
||||
flowKeepAliveMode.updatedAt,
|
||||
flowInactivityDuration.updatedAt,
|
||||
].max() ?? .distantPast
|
||||
}
|
||||
@@ -264,7 +260,6 @@ public extension SyncedAppSettingsV2 {
|
||||
activePolishStyleId: field(configuration.activePolishStyleId),
|
||||
llmThinkingEnabled: field(configuration.llmThinkingEnabled),
|
||||
flowSkipAppSwitch: field(configuration.flowSkipAppSwitch),
|
||||
flowKeepAliveMode: field(configuration.flowKeepAliveMode),
|
||||
flowInactivityDuration: field(configuration.flowInactivityDuration)
|
||||
)
|
||||
}
|
||||
@@ -296,7 +291,6 @@ public extension SyncedAppSettingsV2 {
|
||||
activePolishStyleId: field(PolishStylePackCatalog.defaultID),
|
||||
llmThinkingEnabled: field(false),
|
||||
flowSkipAppSwitch: field(legacy.flowSkipAppSwitch),
|
||||
flowKeepAliveMode: field(.liveActivity),
|
||||
flowInactivityDuration: field(legacy.flowInactivityDuration)
|
||||
)
|
||||
}
|
||||
@@ -340,7 +334,6 @@ public extension SyncedAppSettingsV2 {
|
||||
),
|
||||
llmThinkingEnabled: .merge(local: local.llmThinkingEnabled, remote: remote.llmThinkingEnabled),
|
||||
flowSkipAppSwitch: .merge(local: local.flowSkipAppSwitch, remote: remote.flowSkipAppSwitch),
|
||||
flowKeepAliveMode: .merge(local: local.flowKeepAliveMode, remote: remote.flowKeepAliveMode),
|
||||
flowInactivityDuration: .merge(
|
||||
local: local.flowInactivityDuration,
|
||||
remote: remote.flowInactivityDuration
|
||||
@@ -368,7 +361,6 @@ public extension SyncedAppSettingsV2 {
|
||||
configuration.activePolishStyleId = activePolishStyleId.value
|
||||
configuration.llmThinkingEnabled = llmThinkingEnabled.value
|
||||
configuration.flowSkipAppSwitch = flowSkipAppSwitch.value
|
||||
configuration.flowKeepAliveMode = flowKeepAliveMode.value
|
||||
configuration.flowInactivityDuration = flowInactivityDuration.value
|
||||
}
|
||||
|
||||
@@ -398,7 +390,6 @@ public extension SyncedAppSettingsV2 {
|
||||
patch(©.activePolishStyleId, value: configuration.activePolishStyleId)
|
||||
patch(©.llmThinkingEnabled, value: configuration.llmThinkingEnabled)
|
||||
patch(©.flowSkipAppSwitch, value: configuration.flowSkipAppSwitch)
|
||||
patch(©.flowKeepAliveMode, value: configuration.flowKeepAliveMode)
|
||||
patch(©.flowInactivityDuration, value: configuration.flowInactivityDuration)
|
||||
return copy
|
||||
}
|
||||
@@ -431,7 +422,6 @@ public extension SyncedAppSettingsV2 {
|
||||
touch(©.activePolishStyleId, value: configuration.activePolishStyleId)
|
||||
touch(©.llmThinkingEnabled, value: configuration.llmThinkingEnabled)
|
||||
touch(©.flowSkipAppSwitch, value: configuration.flowSkipAppSwitch)
|
||||
touch(©.flowKeepAliveMode, value: configuration.flowKeepAliveMode)
|
||||
touch(©.flowInactivityDuration, value: configuration.flowInactivityDuration)
|
||||
return copy
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user