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:
@@ -0,0 +1,24 @@
|
||||
// AppVersionDisplay.swift
|
||||
// OSGKeyboardShared
|
||||
//
|
||||
// Reads marketing / build numbers from the host app Info.plist for Settings.
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Display helpers for the app's marketing version and build number.
|
||||
public enum AppVersionDisplay {
|
||||
/// `CFBundleShortVersionString`, e.g. `"1.5.0"`.
|
||||
public static var marketingVersion: String {
|
||||
Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "—"
|
||||
}
|
||||
|
||||
/// `CFBundleVersion`, e.g. `"37"`.
|
||||
public static var buildNumber: String {
|
||||
Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "—"
|
||||
}
|
||||
|
||||
/// Settings trailing label, e.g. `"1.5.0 (Build 37)"`.
|
||||
public static var detailedLabel: String {
|
||||
"\(marketingVersion) (Build \(buildNumber))"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// HostMemoryBudget.swift
|
||||
// OSGKeyboard · Shared
|
||||
//
|
||||
// Shared RSS gate for host-side heavy work (Rime deploy, CLM, ASR warmup).
|
||||
// Keeps the keyboard extension alive by avoiding host jetsam spikes.
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum HostMemoryBudget {
|
||||
/// Soft ceiling (MiB): defer CLM / Rime / ASR when resident memory is above this.
|
||||
/// Host SwiftUI baseline alone is often ~150–170 MB; 120 was always-trip and
|
||||
/// left Rime/CLM permanently deferred while wrongly signaling hostHeavy.
|
||||
public static let deferHeavyWorkAboveMB: Double = 260
|
||||
public static let projectedPeakCeilingMB: Double = 290
|
||||
|
||||
public static var shouldDeferHeavyWork: Bool {
|
||||
let rss = OSGDiag.memoryMB()
|
||||
guard rss >= 0 else { return false }
|
||||
return rss >= deferHeavyWorkAboveMB
|
||||
}
|
||||
|
||||
public static func gate(
|
||||
_ work: String,
|
||||
category: String = "flow"
|
||||
) -> Bool {
|
||||
let rss = OSGDiag.memoryMB()
|
||||
let estimatedGrowth: Double
|
||||
if work.contains("clm") {
|
||||
estimatedGrowth = 48
|
||||
} else if work.contains("asr") {
|
||||
estimatedGrowth = 32
|
||||
} else if work.contains("rime") {
|
||||
estimatedGrowth = 24
|
||||
} else {
|
||||
estimatedGrowth = 24
|
||||
}
|
||||
if shouldDeferHeavyWork
|
||||
|| (rss >= 0 && rss + estimatedGrowth >= projectedPeakCeilingMB) {
|
||||
OSGDiag.log(
|
||||
"memoryGate defer work=\(work) \(OSGDiag.memoryTag()) "
|
||||
+ "threshold=\(Int(deferHeavyWorkAboveMB))MB "
|
||||
+ "projected=\(Int(max(0, rss + estimatedGrowth)))MB",
|
||||
category: category
|
||||
)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// OSGDiag.swift
|
||||
// OSGKeyboard · Shared
|
||||
//
|
||||
// Always-on diagnostic breadcrumbs. Uses NSLog so lines show up in Xcode /
|
||||
// Console without needing an os.Logger subsystem filter (keyboard extension
|
||||
// OSLog lines are easy to miss when the host process is selected).
|
||||
|
||||
import Foundation
|
||||
import Darwin
|
||||
|
||||
public enum OSGDiag {
|
||||
/// Prefix every line for easy Console search: `OSGDiag`
|
||||
public static func log(_ message: String, category: String = "diag") {
|
||||
let line = "[OSGDiag/\(category)] \(message)"
|
||||
NSLog("%@", line)
|
||||
switch category {
|
||||
case "keyboardExt", "boot":
|
||||
OSGLog.keyboardExt.info("\(line, privacy: .public)")
|
||||
case "flow", "asr":
|
||||
OSGLog.flow.info("\(line, privacy: .public)")
|
||||
default:
|
||||
OSGLog.config.info("\(line, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
/// Resident set size in MiB, or -1 if unavailable.
|
||||
public static func memoryMB() -> Double {
|
||||
var info = mach_task_basic_info()
|
||||
var count = mach_msg_type_number_t(
|
||||
MemoryLayout<mach_task_basic_info>.size / MemoryLayout<natural_t>.size
|
||||
)
|
||||
let kr = withUnsafeMutablePointer(to: &info) { ptr in
|
||||
ptr.withMemoryRebound(to: integer_t.self, capacity: Int(count)) { rebound in
|
||||
task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), rebound, &count)
|
||||
}
|
||||
}
|
||||
guard kr == KERN_SUCCESS else { return -1 }
|
||||
return Double(info.resident_size) / 1_048_576.0
|
||||
}
|
||||
|
||||
public static func memoryTag() -> String {
|
||||
String(format: "rss=%.1fMB", memoryMB())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user