feat(macos): local ASR model manager, menu-bar polish, and release 0.5.2
Adds a bundled local ASR model catalog for the macOS app with one-click Sherpa Qwen3 / SenseVoice downloads (pause/resume, inline actions) and a shared model storage directory used by MLX Qwen3. Fixes the light-mode sidebar material and makes the menu-bar icon follow the system appearance with a refreshed status mark. Renames the built product to OSGKeyboard.app. Bumps version to 0.5.2 (build 19).
This commit is contained in:
@@ -141,6 +141,7 @@ final class FlowSessionBridgeTests: XCTestCase {
|
||||
|
||||
func testDarwinNotificationPostsWithoutCrashing() {
|
||||
FlowSessionDarwin.postSessionChanged()
|
||||
FlowSessionDarwin.postCommandChanged()
|
||||
FlowSessionDarwin.postHostReadyChanged()
|
||||
}
|
||||
|
||||
@@ -183,4 +184,112 @@ final class FlowSessionBridgeTests: XCTestCase {
|
||||
XCTAssertFalse(defaults.bool(forKey: FlowSessionKeys.flowHostReady))
|
||||
XCTAssertFalse(FlowSessionBridge.isHostReady(defaults: defaults))
|
||||
}
|
||||
|
||||
func testFlowCommandRoundTrip() {
|
||||
let defaults = makeDefaults()
|
||||
let sessionId = UUID()
|
||||
let utteranceId = UUID()
|
||||
let command = FlowCommand(
|
||||
sessionId: sessionId,
|
||||
utteranceId: utteranceId,
|
||||
commandSeq: 42,
|
||||
action: .startRecording,
|
||||
localeId: "zh-Hans",
|
||||
createdAt: 123
|
||||
)
|
||||
|
||||
FlowSessionBridge.writeCommand(command, defaults: defaults)
|
||||
|
||||
XCTAssertEqual(FlowSessionBridge.latestCommand(defaults: defaults), command)
|
||||
}
|
||||
|
||||
func testFlowResultRoundTripPreservesUtteranceIdentity() {
|
||||
let defaults = makeDefaults()
|
||||
let sessionId = UUID()
|
||||
let utteranceId = UUID()
|
||||
let result = FlowResult(
|
||||
sessionId: sessionId,
|
||||
utteranceId: utteranceId,
|
||||
commandSeq: 43,
|
||||
status: .final,
|
||||
text: "hello",
|
||||
warning: "raw fallback",
|
||||
createdAt: 124
|
||||
)
|
||||
|
||||
FlowSessionBridge.writeResult(result, defaults: defaults)
|
||||
|
||||
XCTAssertEqual(FlowSessionBridge.latestResult(defaults: defaults), result)
|
||||
FlowSessionBridge.clearResult(defaults: defaults)
|
||||
XCTAssertNil(FlowSessionBridge.latestResult(defaults: defaults))
|
||||
}
|
||||
|
||||
func testFlowAckRoundTrip() {
|
||||
let defaults = makeDefaults()
|
||||
let ack = FlowAck(
|
||||
sessionId: UUID(),
|
||||
utteranceId: UUID(),
|
||||
commandSeq: 44,
|
||||
consumedAt: 125
|
||||
)
|
||||
|
||||
FlowSessionBridge.writeAck(ack, defaults: defaults)
|
||||
|
||||
XCTAssertEqual(FlowSessionBridge.latestAck(defaults: defaults), ack)
|
||||
}
|
||||
|
||||
func testReadySnapshotDrivesHostReady() {
|
||||
let defaults = makeDefaults()
|
||||
let sessionId = UUID()
|
||||
let now = Date().timeIntervalSince1970
|
||||
FlowSessionBridge.markSessionActive(duration: 60, sessionId: sessionId, defaults: defaults)
|
||||
let snapshot = FlowReadySnapshot(
|
||||
sessionId: sessionId,
|
||||
ready: true,
|
||||
reason: .ready,
|
||||
heartbeatAt: now,
|
||||
readyAt: now,
|
||||
audioProofAt: now,
|
||||
engineMode: "local",
|
||||
localeId: "zh-Hans",
|
||||
sessionExpiresAt: now + 60
|
||||
)
|
||||
|
||||
FlowSessionBridge.writeReadySnapshot(snapshot, defaults: defaults)
|
||||
|
||||
XCTAssertEqual(FlowSessionBridge.readySnapshot(defaults: defaults), snapshot)
|
||||
XCTAssertTrue(FlowSessionBridge.isHostReady(defaults: defaults))
|
||||
}
|
||||
|
||||
func testClearFlowStateRemovesProtocolPayloads() {
|
||||
let defaults = makeDefaults()
|
||||
let sessionId = UUID()
|
||||
let utteranceId = UUID()
|
||||
FlowSessionBridge.writeCommand(
|
||||
FlowCommand(
|
||||
sessionId: sessionId,
|
||||
utteranceId: utteranceId,
|
||||
commandSeq: 1,
|
||||
action: .startRecording,
|
||||
localeId: "en-US"
|
||||
),
|
||||
defaults: defaults
|
||||
)
|
||||
FlowSessionBridge.writeResult(
|
||||
FlowResult(
|
||||
sessionId: sessionId,
|
||||
utteranceId: utteranceId,
|
||||
commandSeq: 1,
|
||||
status: .partial,
|
||||
text: "hello"
|
||||
),
|
||||
defaults: defaults
|
||||
)
|
||||
|
||||
FlowSessionBridge.clearFlowState(defaults: defaults)
|
||||
|
||||
XCTAssertNil(FlowSessionBridge.latestCommand(defaults: defaults))
|
||||
XCTAssertNil(FlowSessionBridge.latestResult(defaults: defaults))
|
||||
XCTAssertNil(FlowSessionBridge.readySnapshot(defaults: defaults))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user