feat(stage-tamagotchi): add Apple Speech transcription (#2364)

This commit is contained in:
Neko
2026-08-25 17:41:43 +00:00
committed by GitHub
parent ca40f6ea7a
commit 95cc685b1b
32 changed files with 1142 additions and 191 deletions
@@ -206,14 +206,11 @@ export default {
// - Linux arm64 -> `latest-arm64-linux-arm64.yml`
channel: 'latest-${arch}',
},
extendInfo: [
{
NSMicrophoneUsageDescription: 'AIRI requires microphone access for voice interaction',
},
{
NSCameraUsageDescription: 'AIRI requires camera access for vision understanding',
},
],
extendInfo: {
NSMicrophoneUsageDescription: 'AIRI requires microphone access for voice interaction',
NSSpeechRecognitionUsageDescription: 'AIRI uses Apple Speech to transcribe voice interactions on this device',
NSCameraUsageDescription: 'AIRI requires camera access for vision understanding',
},
// For self-publishing, testing, and distribution after modified the code without access to
// an Apple Developer account, comment and uncomment the following 4 lines.
// Later on when you obtained one, you can set up the necessary certificates and provisioning
@@ -28,6 +28,7 @@ export default defineConfig({
// them into ESM and causing issues in runtime.
'electron-click-drag-plugin',
'uiohook-napi',
'@xsai-apple-speech/transcription-native',
],
},
},
+7
View File
@@ -81,6 +81,9 @@
"@vueuse/core": "catalog:",
"@vueuse/motion": "catalog:",
"@vueuse/shared": "catalog:",
"@xsai-apple-speech/transcription": "catalog:",
"@xsai-apple-speech/transcription-electron-plugin": "catalog:",
"@xsai-apple-speech/transcription-native": "catalog:",
"@xsai-ext/providers": "catalog:",
"@xsai-transformers/embed": "catalog:",
"@xsai-transformers/transcription": "catalog:",
@@ -143,6 +146,10 @@
"xsschema": "catalog:",
"zod": "catalog:"
},
"optionalDependencies": {
"@xsai-apple-speech/transcription-native-darwin-arm64": "catalog:",
"@xsai-apple-speech/transcription-native-darwin-x64": "catalog:"
},
"devDependencies": {
"@electron-toolkit/preload": "catalog:",
"@electron-toolkit/tsconfig": "catalog:",
+7 -1
View File
@@ -28,6 +28,7 @@ import { createGlobalAppConfig } from './configs/global'
import { emitAppBeforeQuit, emitAppReady, emitAppWindowAllClosed } from './libs/bootkit/lifecycle'
import { setElectronMainDirname } from './libs/electron/location'
import { createI18n } from './libs/i18n'
import { setupAppleSpeechTranscriptionService } from './services/airi/apple-speech-transcription'
import { setupServerChannel } from './services/airi/channel-server'
import { setupGodotStageManager } from './services/airi/godot-stage'
import { setupBuiltInServer } from './services/airi/http-server'
@@ -165,6 +166,11 @@ app.whenReady().then(async () => {
build: async () => setupGodotStageManager(),
})
const appleSpeechTranscription = injeca.provide('modules:apple-speech-transcription', {
dependsOn: { lifecycle },
build: ({ dependsOn }) => setupAppleSpeechTranscriptionService(dependsOn),
})
const mcpStdioManager = injeca.provide('modules:mcp-stdio-manager', {
build: async () => setupMcpStdioManager(),
})
@@ -226,7 +232,7 @@ app.whenReady().then(async () => {
})
const mainWindow = injeca.provide('windows:main', {
dependsOn: { editorWindow, settingsWindow, chatWindow, widgetsManager, noticeWindow, beatSync, autoUpdater, serverChannel, godotStageManager, mcpStdioManager, i18n, onboardingWindowManager },
dependsOn: { editorWindow, settingsWindow, chatWindow, widgetsManager, noticeWindow, beatSync, autoUpdater, serverChannel, godotStageManager, mcpStdioManager, i18n, onboardingWindowManager, appleSpeechTranscription },
build: async ({ dependsOn }) => setupMainWindow({
...dependsOn,
onWindowCreated: (window) => {
@@ -0,0 +1,46 @@
import type { Lifecycle } from 'injeca'
import { createContext } from '@moeru/eventa/adapters/electron/main'
import { setupAppleSpeechTranscription } from '@xsai-apple-speech/transcription-electron-plugin/main'
import { ipcMain } from 'electron'
import { isMacOS } from 'std-env'
/**
* Registers the app-wide Apple Speech transport and its native Provider.
*
* The Electron main process owns native work. Renderer Providers communicate
* with it through the Eventa handlers registered by the xsAI plugin.
* Non-macOS hosts return an inactive service without loading the native package.
*
* Call stack:
*
* setupAppleSpeechTranscriptionService
* -> {@link createContext}
* -> {@link setupAppleSpeechTranscription}
* -> {@link createAppleSpeechProvider}
*/
export async function setupAppleSpeechTranscriptionService(options: { lifecycle: Lifecycle }) {
if (!isMacOS)
return { dispose: () => Promise.resolve() }
const { createAppleSpeechProvider } = await import('@xsai-apple-speech/transcription-native')
const eventa = createContext(ipcMain)
const setup = setupAppleSpeechTranscription({
context: eventa.context,
provider: createAppleSpeechProvider(),
})
let disposal: Promise<void> | undefined
const dispose = () => {
disposal ??= (async () => {
// Stop accepting native work before the transport cancels remaining invokes.
await setup.dispose()
eventa.dispose()
})()
return disposal
}
options.lifecycle.appHooks.onStop(dispose)
return { dispose }
}