feat(macos): add macOS menu-bar app and harden cross-device iCloud sync

Introduce a standalone macOS menu-bar app (OSGKeyboardMac) that reuses the
platform-agnostic OSGKeyboardShared core: record -> cloud/local ASR -> polish
-> insert. Local mode uses Qwen3-ASR via mlx-swift-asr (macOS 15+, Apple
Silicon); iOS targets stay zero-SPM.

Harden iCloud sync for multi-device correctness:
- Per-field settings merge (appSettings.v2) so concurrent edits no longer
  clobber each other's unrelated fields.
- Per-device usage statistics (G-Counter) that sum instead of max().
- Tombstoned dictionary/history merge so deletes propagate and entries can't
  resurrect.
- API keys replicate via iCloud Keychain, never iCloud KVS JSON; pulling a
  legacy blob without key fields no longer wipes local Keychain entries.
- Add a low-risk "Sync Now" action in Settings.

Fix Flow keyboard mic state: stay orange until the host publishes a real ready
contract, share a single MicVoiceAvailability gate, and self-heal stale
cross-process heartbeat jitter instead of getting stuck.

Extract shared storage (SpeechHistoryStore/UsageStatisticsStore,
ConfigurationStore) into OSGKeyboardShared and add tests for the new
sync/merge logic.
This commit is contained in:
Rocky
2026-07-08 18:13:56 +08:00
parent 128aab1b02
commit c2f07bd8d2
99 changed files with 6735 additions and 740 deletions
+102
View File
@@ -99,3 +99,105 @@
"keyboard.translation.offMenu" = "Don't translate";
"keyboard.translation.a11y" = "Translation";
"keyboard.translation.a11yHint" = "Toggle translation or change the target language.";
/* macOS app */
"mac.section.dashboard" = "Dashboard";
"mac.section.history" = "History";
"mac.section.dictionary" = "Personal Dictionary";
"mac.section.settings" = "Settings";
"mac.brand.subtitle" = "AI DICTATION";
"mac.devices" = "Devices";
"mac.status.ready" = "Ready to dictate…";
"mac.status.listening" = "Listening…";
"mac.status.transcribing" = "Transcribing…";
"mac.status.copied" = "Copied to clipboard";
"mac.status.pasted" = "Inserted into front app";
"mac.status.copiedAndPasted" = "Copied and inserted";
"mac.stat.dictationTime" = "Dictation Time";
"mac.stat.words" = "Dictation Chars";
"mac.stat.translation" = "Translation Chars";
"mac.stat.dictionary" = "Dictionary";
"mac.stat.cumulativeDuration" = "Total time";
"mac.stat.transcribed" = "Transcribed";
"mac.stat.cumulativeTranslation" = "Translated";
"mac.stat.customTerms" = "Custom terms";
"mac.status.chipReady" = "Ready";
"mac.status.chipProcessing" = "Processing";
"mac.record.start" = "Record";
"mac.record.stop" = "Stop";
"mac.record.pressStop" = "Press Stop";
"mac.copy" = "Copy";
"mac.openWindow" = "Open Window";
"mac.quit" = "Quit";
"mac.mode.cloud" = "Cloud Mode";
"mac.mode.local" = "Local Mode";
"mac.connected" = "Connected";
"mac.offline" = "Offline";
"mac.history.recent" = "Recent";
"mac.history.empty" = "No voice transcripts yet.";
"mac.history.select" = "Select a dictation";
"mac.history.clearTitle" = "Clear all history?";
"mac.history.clearMessage" = "This cannot be undone.";
"mac.history.clearConfirm" = "Clear all";
"mac.dict.health" = "Vocabulary Health";
"mac.dict.healthDesc" = "Custom terms that bias recognition and are never rewritten.";
"mac.dict.search" = "Search words";
"mac.dict.empty" = "No words yet";
"mac.dict.emptyBody" = "Add words on your iPhone or iPad to improve recognition accuracy. They sync here via iCloud.";
"mac.dict.noMatch" = "No matches";
"mac.cancel" = "Cancel";
"mac.delete" = "Delete";
"mac.dict.deleteTitle" = "Delete this word?";
"mac.dict.deleteMessage" = "This cannot be undone.";
"mac.hint.holdOption" = "Hold Option to dictate";
"mac.settings.cloudProvider" = "CLOUD PROVIDER";
"mac.settings.service" = "Service";
"mac.settings.apiKey" = "API Key";
"mac.settings.model" = "Model";
"mac.settings.recognition" = "RECOGNITION METHOD";
"mac.settings.cloudEngine" = "Cloud Engine & AI Refinement";
"mac.settings.cloudEngineDesc" = "Premium transcription via your provider's API, plus AI grammar and style polishing.";
"mac.settings.localEngine" = "Local Recognition (Qwen3-ASR)";
"mac.settings.localEngineDesc" = "On-device ASR with Qwen3-ASR 1.7B (MLX). High privacy, zero latency.";
"mac.settings.localSpeechFallback" = "Local Recognition (Apple Speech)";
"mac.settings.localSpeechFallbackDesc" = "On-device Apple Speech when the Qwen3 model is not installed.";
"mac.settings.about" = "About";
"mac.settings.general" = "General";
"mac.settings.input" = "Input & Shortcuts";
"mac.settings.recognitionLanguage" = "Recognition Language";
"mac.settings.interfaceLanguage" = "Interface Language";
"mac.settings.autoPaste" = "Auto-paste after dictation";
"mac.settings.autoPasteDesc" = "Simulate ⌘V in the front app after transcription (requires Accessibility).";
"mac.settings.hotkey" = "Global shortcut";
"mac.settings.hotkeyDesc" = "Hold Option (⌥) to dictate from any app.";
"mac.settings.qwen3Model" = "Qwen3 model folder";
"mac.settings.qwen3ModelDesc" = "Folder with config.json, model.safetensors, vocab.json, and merges.txt.";
"mac.settings.qwen3Browse" = "Choose folder…";
"mac.settings.qwen3Missing" = "Qwen3 model not found — using Apple Speech for now.";
"mac.settings.accessibility" = "Accessibility";
"mac.settings.accessibilityDesc" = "Required for global shortcut and auto-paste.";
"mac.settings.openAccessibility" = "Open System Settings";
"mac.settings.appearance" = "Appearance";
"mac.settings.appearanceDesc" = "Match the system, or force a light or dark look.";
"mac.appearance.system" = "System";
"mac.appearance.light" = "Light";
"mac.appearance.dark" = "Dark";
"mac.error.noAudio" = "No audio captured";
"mac.error.noCloudASR" = "Selected provider has no cloud ASR";
"mac.error.emptyTranscript" = "No speech recognized";
"mac.error.qwen3ModelMissing" = "Qwen3-ASR model not installed";
"mac.error.qwen3LoadFailed" = "Failed to load Qwen3 model: %@";
"mac.error.qwen3InferenceFailed" = "Qwen3 transcription failed: %@";
"mac.error.accessibilityRequired" = "Enable Accessibility for OSGKeyboard in System Settings";
"mac.foregroundApp" = "Front app: %@";
"mac.sync.settingsTitle" = "iCloud Sync";
"mac.sync.settingsSubtitle" = "Sync settings, usage stats, speech history, and API keys across your devices via iCloud.";
"mac.sync.syncNow" = "Sync Now";
"mac.sync.dictTitle" = "Personal dictionary iCloud sync";
"mac.sync.dictSubtitle" = "Keep your dictionary in sync across all devices.";
"mac.sync.error.generic" = "Could not sync with iCloud. Try again later.";
"mac.sync.error.dictTooLarge" = "Dictionary is too large to sync via iCloud.";
"settings.appLanguage.auto" = "Auto";
"settings.appLanguage.english" = "English";
"settings.appLanguage.chinese" = "Chinese";