checkpoint before checking out main
This commit is contained in:
@@ -105,6 +105,7 @@ public struct AppGroupConfiguration: Sendable, Equatable {
|
||||
public var uiLanguage: AppUILanguage
|
||||
public var translationTargetLocaleId: String
|
||||
public var handednessPreference: HandednessPreference
|
||||
/// Decode-only compatibility field for cursor drag pads removed in 2.0.0.
|
||||
public var cursorDragNavigationEnabled: Bool
|
||||
/// Typing-grid haptic strength (off / light / strong).
|
||||
public var keyboardHapticIntensity: KeyboardHapticIntensity
|
||||
@@ -300,7 +301,7 @@ public struct AppGroupConfiguration: Sendable, Equatable {
|
||||
),
|
||||
cursorDragNavigationEnabled: {
|
||||
if defaults.object(forKey: Keys.cursorDragNavigationEnabled) == nil {
|
||||
return true
|
||||
return false
|
||||
}
|
||||
return defaults.bool(forKey: Keys.cursorDragNavigationEnabled)
|
||||
}(),
|
||||
@@ -594,17 +595,7 @@ public struct AppGroupConfiguration: Sendable, Equatable {
|
||||
return .empty
|
||||
}
|
||||
do {
|
||||
var dictionary = try JSONDecoder().decode(PersonalDictionary.self, from: data)
|
||||
if dictionary.entries.contains(where: { $0.source == .history }) {
|
||||
for index in dictionary.entries.indices where dictionary.entries[index].source == .history {
|
||||
dictionary.entries[index].source = .manual
|
||||
}
|
||||
dictionary.version += 1
|
||||
if let migrated = try? JSONEncoder().encode(dictionary) {
|
||||
defaults.set(migrated, forKey: Keys.personalDictionary)
|
||||
}
|
||||
}
|
||||
return dictionary
|
||||
return try JSONDecoder().decode(PersonalDictionary.self, from: data)
|
||||
} catch {
|
||||
OSGLog.config.warning("personalDictionary decode failed: \(error.localizedDescription, privacy: .public)")
|
||||
return .empty
|
||||
|
||||
@@ -39,12 +39,11 @@ public enum KeyboardChromeLayout {
|
||||
public static let horizontalInset: CGFloat = 8
|
||||
/// Voice-surface content column cap.
|
||||
///
|
||||
/// The voice surface is a sparse cluster — two cursor-drag pads flanking a
|
||||
/// fixed 121 pt mic — over a transparent background, so filling an iPad's
|
||||
/// width buys no visual width; it only parks delete/return at the screen
|
||||
/// edges and turns each drag pad into a ~450 pt runway. The typing surface
|
||||
/// has the opposite need (a key grid must fill the width to match the
|
||||
/// system keyboard), which is why it no longer shares this constant.
|
||||
/// The voice surface is a sparse control cluster over a transparent
|
||||
/// background, so filling an iPad's width buys no visual width and only
|
||||
/// parks delete/return at the screen edges. The typing surface has the
|
||||
/// opposite need (a key grid must fill the width to match the system
|
||||
/// keyboard), which is why it no longer shares this constant.
|
||||
public static let voiceContentMaxWidth: CGFloat = 700
|
||||
|
||||
/// Width at or above which the typing surface switches to wide-iPad
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//
|
||||
// Sources (mutually exclusive per entry):
|
||||
// - `.manual` user typed it in by hand
|
||||
// - `.history` legacy auto-learned entries (migrated to `.manual`)
|
||||
// - `.history` user-confirmed recommendations from typing history
|
||||
// - `.contacts` imported from the iOS Contacts framework
|
||||
// - `.recentEdit` extracted from edits the user made to a
|
||||
// polished transcript before sending
|
||||
@@ -274,12 +274,28 @@ extension PersonalDictionary {
|
||||
return entries.first { $0.term.lowercased() == key }
|
||||
}
|
||||
|
||||
/// Insert or update a manual entry. Returns the saved entry.
|
||||
/// Insert or update a manual entry. Kept as a convenience for existing callers.
|
||||
@discardableResult
|
||||
public mutating func upsertManual(
|
||||
term: String,
|
||||
existingID: UUID? = nil,
|
||||
regenerateAliases: Bool = false
|
||||
) -> Entry? {
|
||||
upsert(
|
||||
term: term,
|
||||
existingID: existingID,
|
||||
source: .manual,
|
||||
regenerateAliases: regenerateAliases
|
||||
)
|
||||
}
|
||||
|
||||
/// Insert or update an entry while preserving its user-visible origin.
|
||||
@discardableResult
|
||||
public mutating func upsert(
|
||||
term: String,
|
||||
existingID: UUID? = nil,
|
||||
source: Entry.Source,
|
||||
regenerateAliases: Bool = false
|
||||
) -> Entry? {
|
||||
let trimmed = term.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmed.isEmpty else { return nil }
|
||||
@@ -292,7 +308,7 @@ extension PersonalDictionary {
|
||||
let termChanged = entry.term.caseInsensitiveCompare(trimmed) != .orderedSame
|
||||
entry.term = trimmed
|
||||
entry.category = category
|
||||
entry.source = .manual
|
||||
entry.source = source
|
||||
if termChanged || regenerateAliases {
|
||||
entry.aliases = []
|
||||
}
|
||||
@@ -307,7 +323,7 @@ extension PersonalDictionary {
|
||||
var entry = entries[idx]
|
||||
entry.term = trimmed
|
||||
entry.category = category
|
||||
entry.source = .manual
|
||||
entry.source = source
|
||||
entry.updatedAt = Date()
|
||||
entries[idx] = entry
|
||||
return entry
|
||||
@@ -318,7 +334,7 @@ extension PersonalDictionary {
|
||||
term: trimmed,
|
||||
aliases: [],
|
||||
category: category,
|
||||
source: .manual,
|
||||
source: source,
|
||||
createdAt: now,
|
||||
updatedAt: now
|
||||
)
|
||||
|
||||
@@ -113,7 +113,7 @@ public final class ProviderConfig: ObservableObject, @unchecked Sendable {
|
||||
didSet {
|
||||
guard !isApplyingConfiguration, localeId != configuration.localeId else { return }
|
||||
configuration.localeId = localeId
|
||||
persistConfiguration()
|
||||
persistConfiguration(postConfigChanged: true)
|
||||
}
|
||||
}
|
||||
/// "local" → on-device ASR + user's LLM polish (requires user API key).
|
||||
@@ -220,16 +220,6 @@ public final class ProviderConfig: ObservableObject, @unchecked Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
/// Press-and-drag pads beside the mic for four-way caret movement.
|
||||
@Published public var cursorDragNavigationEnabled: Bool {
|
||||
didSet {
|
||||
guard !isApplyingConfiguration,
|
||||
cursorDragNavigationEnabled != configuration.cursorDragNavigationEnabled else { return }
|
||||
configuration.cursorDragNavigationEnabled = cursorDragNavigationEnabled
|
||||
persistConfiguration(postConfigChanged: true)
|
||||
}
|
||||
}
|
||||
|
||||
/// Typing-grid haptic strength (off / light / strong). Default is light.
|
||||
@Published public var keyboardHapticIntensity: KeyboardHapticIntensity {
|
||||
didSet {
|
||||
@@ -445,7 +435,6 @@ public final class ProviderConfig: ObservableObject, @unchecked Sendable {
|
||||
uiLanguage = configuration.uiLanguage
|
||||
translationTargetLocaleId = configuration.translationTargetLocaleId
|
||||
handednessPreference = configuration.handednessPreference
|
||||
cursorDragNavigationEnabled = configuration.cursorDragNavigationEnabled
|
||||
keyboardHapticIntensity = configuration.keyboardHapticIntensity
|
||||
polishIntensity = configuration.polishIntensity
|
||||
aiResponseLength = configuration.aiResponseLength
|
||||
@@ -551,7 +540,6 @@ public final class ProviderConfig: ObservableObject, @unchecked Sendable {
|
||||
uiLanguage = fresh.uiLanguage
|
||||
translationTargetLocaleId = fresh.translationTargetLocaleId
|
||||
handednessPreference = fresh.handednessPreference
|
||||
cursorDragNavigationEnabled = fresh.cursorDragNavigationEnabled
|
||||
keyboardHapticIntensity = fresh.keyboardHapticIntensity
|
||||
polishIntensity = fresh.polishIntensity
|
||||
aiResponseLength = fresh.aiResponseLength
|
||||
|
||||
Reference in New Issue
Block a user