跳到主要內容
What's up my neighbor.下載 macOS 版
開發者文件目錄第 2 / 6 篇
  1. 認識專案

    1. 專案定位與平台狀態
    2. 架構與資料流
  2. 動手開發

    1. 開發環境與本機建置
    2. 測試方式與驗證範圍
  3. 參與與發布

    1. 參與方式
    2. 版本、更新、授權與隱私

架構與資料流

從按下快捷鍵到文字回到輸入框:錄音、Groq 辨識、文字處理、剪貼簿與輸入目標驗證,每個模組負責什麼。

適用平台
macOS
對照版本
1.4.5(build 1012)
最後更新

內容來源:macOS repo docs/ARCHITECTURE.md(分支 claude/open-source-docs,待審閱合併)。網站展示經審閱的固定版本,最新內容以 repo 為準。

這份文件說明 What's up my neighbor(macOS)從按下快捷鍵到文字回到輸入框,中間經過哪些模組、資料在哪裡停留。想修改程式前,先從這裡找到要動的檔案。

所有原始碼在 Sources/GroqVoiceInput/,共 53 個 Swift 檔(1.4.5 正式版是 50 個,另外 3 個是尚未發布的毛色外觀),沒有第三方相依套件,只有更新功能使用 Sparkle 2.10.0。

一句話版本

快捷鍵 → 鎖定輸入目標 → 錄音(指定麥克風)→ 暫存檔 → Groq 雲端辨識
      → 語言檢查 → 本機文字處理 → 剪貼簿 → 驗證目標後模擬 ⌘V → 刪除暫存檔

App 是選單列常駐程式(LSUIElement),沒有主視窗、沒有自己的後端,也沒有分析工具。網路只用在三個地方:Groq 辨識、Sparkle 檢查更新、選用的 iPhone 附近連線。

主要流程

1. 快捷鍵與錄音開始

步驟負責的檔案說明
註冊全域快捷鍵HotKeyManager.swiftCarbon hot key;主快捷鍵預設 ⌥↑,iPhone 接力 ⌃⌥↑,剪貼簿歷史 ⌃⌥V
串起所有元件main.swift、VoiceTypeController.swiftmain.swift 只建立 NSApplication 並交給 VoiceTypeController(NSApplicationDelegate);啟動時建立各服務、請求權限、顯示首次引導
錄音狀態機DictationCoordinator.swift一次只管一段錄音;狀態 idle → starting → recording → finalizing → transcribing → delivering,失敗時進 awaitingRetry
鎖定輸入目標FocusTargetService.swift記下當下前景 App、視窗與焦點元素的身分(不讀文字內容、不截圖)
選麥克風AudioInputPolicy.swift、AudioInputDevices.swift沿用系統輸入,但排除 iPhone 連續互通麥克風;可固定電腦或外接麥克風
錄音PinnedMicrophoneRecorder.swift用 AVCaptureSession 指定裝置錄 16 kHz 單聲道 AAC;AVAudioRecorder 無法指定裝置,所以不用它
暫存檔TemporaryRecordingStore.swift錄音寫到 macOS 暫存目錄;啟動時清掉上次異常結束留下的檔案

DictationCoordinator 刻意不 import AppKit,也不碰金鑰;錄音器、辨識器和暫存都透過 protocol 注入,所以能在測試裡用假的元件跑完整個狀態機。

保護機制都在這一層:單段最長 300 秒;靜音自動停止(選用,3/5/10 秒);辨識失敗時錄音保留 120 秒供重試。

2. 雲端辨識

步驟負責的檔案說明
讀金鑰KeychainStore.swiftApp 專用的 Keychain 項目(service 以 Bundle ID 開頭,account api-key);成功讀取後在本次執行期間快取
組請求與解析GroqTranscriptionClient.swiftwhisper-large-v3,POST https://api.groq.com/openai/v1/audio/transcriptions;只 import Foundation,可單獨編譯測試
辨識流程轉接GroqDictationTranscriber.swift讀檔、帶入語言與詞典提示、處理取消
詞典提示MacVocabularyStore.swift啟用的詞條依提示額度組成 prompt;釘選的詞優先

錯誤時優先顯示 Groq 回傳的 error.message,取不到才顯示「Groq 回傳錯誤(HTTP 狀態碼)」。

3. 文字處理

步驟負責的檔案說明
招呼彩蛋CatConfiguration.swift、CatOverlay.swift在任何處理之前比對完整觸發句;命中就交給貓咪回覆,不輸出文字
語言閘門LanguagePolicy.swift檢查文字的 Unicode 腳本是否在允許範圍;不符合時用主要語言自動重試一次
後處理TextPostProcessor.swift純逐字/標準/簡潔三種模式;簡繁、標點、中英文空格、語助詞、數字轉換。只有固定規則,不呼叫任何模型

TextPostProcessor 有法律用語與詞彙保護表,例如「假扣押」「第159條」不會被拆開或改寫。修改這裡時,scripts/test-text-pipeline.sh 是最主要的回歸測試。

4. 輸出到輸入框

步驟負責的檔案說明
寫入剪貼簿並貼上TextInsertionService.swift先把文字寫進剪貼簿,再驗證目標,通過才送出 ⌘V 鍵盤事件
驗證目標FocusTargetService.swift比對錄音開始時記下的 App、視窗與焦點元素;不一致就停在剪貼簿,不貼到別處
刪除暫存DictationCoordinator.swift文字成功寫進剪貼簿後立刻刪除錄音檔,即使自動貼上被拒絕

本機錄音只會送出 ⌘V,永遠不送 Enter。 「貼上並送出」只存在於 iPhone 接力,而且要使用者在手機上明確按下、Mac 再驗證一次目標。這條規則是產品的安全底線,修改相關程式時要附上測試。

周邊功能

功能檔案資料停在哪裡
剪貼簿歷史ClipboardHistoryMonitor.swift、ClipboardHistoryStore.swift、ClipboardPickerController.swift、ClipboardHistoryMenuController.swift本機 ~/Library/Application Support/AI語音輸入/ClipboardHistory,最多 10 筆、每筆 8 MiB
修正學習PasteCorrectionMonitor.swift、MacVocabularyCorrection.swift、VocabularyLearningStore.swift、VocabularyLearningToastController.swift、VocabularyToastPresentation.swift、VocabularyLearningBubbleView.swift貼上後最多觀察兩分鐘;提示泡泡與選單裡的預覽共用同一套顯示流程,但預覽不會建立或刪除詞條;候選只存修正片段,存在 UserDefaults
詞典管理VocabularyWindowController.swift、MacVocabularyStore.swiftUserDefaults 的 smartVocabulary.entries.v1
錄音浮窗NekoRecordingHUD.swift、NekoSignalMeter.swift不搶焦點的面板;左眼取消、右眼完成;cat.config.json 指定圖片時改用該圖片
選單列StatusMenuController.swift、NekoMenuBarIcon.swift黑貓圖示與所有設定入口
設定SettingsStore.swift、SettingsViews.swiftUserDefaults(App 的 Bundle ID 網域),沿用既有 key 與遷移
桌面小黑貓DesktopPetController.swift、DesktopPetModel.swift、DesktopPetView.swift、DesktopPetGroomingPalm.swift、DesktopPetLift.swift、DesktopPetDesktopReader.swift只在記憶體讀桌面資料夾位置;不連網、不開麥克風。1.4.5 起只有貓咪輪廓接收滑鼠(點擊回應、抓起、放下),透明處穿透、不取得鍵盤焦點
貓咪外觀(毛色,main 上尚未發布)CatAppearance.swift、CatAppearanceArtwork.swift、CatAnatomy.swiftcat.config.json version 2 的 appearance;只重新上色隨 App 打包的原圖,保留原本輪廓與透明度,使用者 PNG 不進這個繪製器
更新AppUpdateController.swift、UpdateReminderPolicy.swift、UpdateDiagnostics.swiftSparkle 每日讀 GitHub 上的 appcast;由使用者決定安裝
iPhone 接力(選用)MacRemoteBridge.swift、RemoteTargetController.swift、RemoteTargetValidationPolicy.swift、RemoteMessages.swift、VocabularySyncModels.swift加密 Multipeer Connectivity;每次啟動預設關閉,協定版本 1 只支援文字接力
診斷PasteDiagnostics.swift只有明確開啟時才在本機寫入有大小上限的 JSON;欄位型別固定,不能記錄文字、剪貼內容、App 與視窗名稱、API Key 或路徑

修改時的界線

這些地方動到就會影響使用者的資料或安全,送 PR 前要特別說明並補測試:

  • App 身分:Bundle ID、Keychain service、UserDefaults key、安裝路徑 ~/Applications/AI語音輸入.app。改了會讓既有使用者的金鑰、設定或更新失效。
  • 送出規則:TextInsertionService 只送 ⌘V;RemoteTargetController 的「送出前再驗證一次」規則不要放寬。
  • 資料去向:任何新的網路請求、檔案寫入或記錄,都要同步更新 PRIVACY.md。
  • 權限:不要新增需要螢幕錄製或完整磁碟取用的功能;不要呼叫 tccutil 重設權限。
  • 版本:版本與 build 只在 Resources/Info.plist 修改,見 UPDATES.md。

建置產物

scripts/build.sh 用 swiftc 分別編譯 arm64 與 x86_64,再用 lipo 合成 Universal 2 執行檔,打包成 build/AI語音輸入.app(執行檔名 FreeVoiceType)。不使用 Xcode 專案,也不需要 Swift Package Manager。詳見 DEVELOPMENT.md。

內容和 App 畫面對不上,或步驟走不下去?可以到 GitHub Issues 告訴我們是哪一篇、哪一步。