feat(V2-1a): Monorepo 구조 전환 — apps/desktop으로 V1 이동

- npm workspaces 루트 (apps/*, packages/*) 세팅
- V1 전체를 apps/desktop/으로 git mv (src, resources, tests, sidecar,
  scripts, electron.vite.config.ts, electron-builder.yml, vitest.config.ts,
  tsconfig.node.json, tsconfig.web.json)
- apps/desktop/package.json 신규 (name=@d3ro/desktop)
- productName: 'd3ro-voice' 명시 — app.getName()을 고정하여 userData 경로
  %APPDATA%\d3ro-voice\ 그대로 유지 (기존 DB/설정 연속성 보장)
- 루트 package.json을 workspace 루트로 재구성, 공통 devDep만 유지
  (typescript, eslint, prettier)
- turbo.json, tsconfig.base.json 추가 (Turborepo 자체 설치는 별도 sub-phase)
- memory/project_status.md 생성 (규칙 13)

검증:
- npm run typecheck 통과
- npm run build 통과 (electron-vite main+preload+renderer)
- npm run dev 실제 실행 → DB/핫키/Ollama 자동 실행 모두 정상
This commit is contained in:
yunchan8804 2026-04-08 14:04:41 +09:00
parent 3a160b9032
commit 45a580878a
178 changed files with 214 additions and 0 deletions

View file

@ -1,683 +0,0 @@
// src/preload/index.ts
// contextBridge로 렌더러에 노출할 API 정의
import { contextBridge, ipcRenderer } from 'electron'
import { IPC_CHANNELS } from '@shared/ipc-channels'
import type {
AudioDevice,
SetDeviceParams,
TestDeviceParams,
TestDeviceResult,
AudioDeviceChangedEvent,
AppConfig,
ConfigGetParams,
ConfigSetParams,
ConfigResetParams,
SetThemeParams,
SetLanguageParams,
ThemeMode,
ConfigChangedEvent,
VoiceState,
VoiceMode,
AudioLevelEvent,
VoiceStateChangedEvent,
TranscriptionDeltaEvent,
TranscriptionCompleteEvent,
VoiceErrorEvent,
StartRecordingParams,
StartRecordingResult,
StopRecordingParams,
StopRecordingResult,
CancelRecordingParams,
SetVoiceModeParams,
STTStatus,
STTModel,
SetSTTModelParams,
SetSTTLanguageParams,
STTStatusChangedEvent,
HotkeyBinding,
SetHotkeyParams,
SetEnabledParams,
HotkeyTriggeredEvent,
LLMStatus,
LLMModel,
LLMProcessParams,
LLMProcessResult,
SetLLMModelParams,
SetServerUrlParams,
LLMStatusChangedEvent,
LLMProcessProgressEvent,
HistoryQueryParams,
HistoryPage,
HistoryGetByIdParams,
HistoryEntry,
HistoryDeleteParams,
HistorySearchParams,
DictionaryQueryParams,
DictionaryPage,
DictionaryEntry,
DictionaryAddParams,
DictionaryUpdateParams,
DictionaryDeleteParams,
DictionarySearchParams,
StatsSummary,
PermissionStatus,
// Phase 10
MemoTag,
AddTagParams,
RemoveTagParams,
GetTagsParams,
SearchByTagParams,
ExportMemoParams,
TagCount,
VoiceCommandRule,
VoiceCommandMatch,
SetVoiceCommandKeywordsParams,
SetVoiceCommandEnabledParams,
CaptureContextResult,
ScreenContext,
LLMChain,
CreateChainParams,
UpdateChainParams,
DeleteChainParams,
ExecuteChainParams,
ChainExecutionResult,
ChainProgress,
CaptionState,
CaptionSegment,
CaptionConfig,
CaptionSessionSummary,
// Phase 11
LicenseInfo,
ActivateLicenseParams,
ActivateLicenseResult,
FeatureAccess,
UsageQuota,
UpgradePromptEvent,
TierComparison,
// Phase 12
FileTranscriptionStartParams,
// Phase 13.1
ConversationSessionInfo,
ConversationMessage,
ConversationSendParams,
ConversationAssistantDelta,
ConversationAssistantMessage,
ConversationError,
// Phase 13.2
RAGDocument,
RAGQueryParams,
RAGQueryResult,
RAGStateInfo,
RAGIndexProgress,
RAGAddDocumentParams,
RAGRemoveDocumentParams,
// Phase 13.3
VoiceActionExecuteParams,
VoiceActionPreset,
VoiceActionHistoryEntry,
VoiceActionPlannedEvent,
VoiceActionExecutedEvent,
VoiceActionErrorEvent,
FileTranscriptionProgress,
FileTranscriptionResult,
FileTranscriptionStateInfo,
MeetingSummarizeParams,
MeetingSummaryResult,
MeetingSummaryGetParams,
MeetingSummaryExportParams,
MeetingSummaryProgress,
DictationTemplate,
CreateTemplateParams,
UpdateTemplateParams,
DeleteTemplateParams,
StartTemplateSessionParams,
SetFieldValueParams,
TemplateSessionInfo,
TemplateFieldCompletedEvent,
TemplateSessionCompletedEvent,
MeetingStartResult,
MeetingMemo,
MeetingAddMemoParams,
MeetingModeStateInfo,
MeetingSessionPage,
MeetingGetSessionsParams,
MeetingSessionDetail,
MeetingGetSessionParams,
MeetingDeleteSessionParams,
MeetingExportParams,
MeetingProcessingProgress,
CaptionSegment,
MeetingDocument,
MeetingDocTemplate,
MeetingGenerateDocParams,
MeetingGenerateDocResult,
MeetingUpdateDocParams,
MeetingDeleteDocParams,
MeetingGetDocsParams,
MeetingUpdateTranscriptParams,
MeetingExportDocParams,
MeetingExportFormat,
MeetingDocGeneratingProgress,
CreateMeetingDocTemplateParams,
UpdateMeetingDocTemplateParams,
DeleteMeetingDocTemplateParams,
MeetingChatMessage,
MeetingChatSendParams,
MeetingChatDelta,
DiarizeSessionParams,
} from '@shared/types'
import { Feature } from '@shared/types'
import type { IPCResult } from '@shared/errors'
type Unsubscribe = () => void
function invoke<TResult>(channel: string, ...args: unknown[]): Promise<IPCResult<TResult>> {
return ipcRenderer.invoke(channel, ...args)
}
function send(channel: string, ...args: unknown[]): void {
ipcRenderer.send(channel, ...args)
}
function on<T>(channel: string, callback: (data: T) => void): Unsubscribe {
const listener = (_event: Electron.IpcRendererEvent, data: T) => callback(data)
ipcRenderer.on(channel, listener)
return () => ipcRenderer.removeListener(channel, listener)
}
const electronAPI = {
// ── Audio ──────────────────────────────────────────────
audio: {
getDevices: () => invoke<AudioDevice[]>(IPC_CHANNELS.AUDIO.GET_DEVICES),
getSelectedDevice: () => invoke<string | null>(IPC_CHANNELS.AUDIO.GET_SELECTED_DEVICE),
setSelectedDevice: (params: SetDeviceParams) =>
invoke<void>(IPC_CHANNELS.AUDIO.SET_SELECTED_DEVICE, params),
testDevice: (params: TestDeviceParams) =>
invoke<TestDeviceResult>(IPC_CHANNELS.AUDIO.TEST_DEVICE, params),
onDeviceChanged: (cb: (e: AudioDeviceChangedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.AUDIO.DEVICE_CHANGED, cb),
onTestLevel: (cb: (e: { level: number }) => void): Unsubscribe =>
on('audio:testLevel', cb),
},
// ── Config ─────────────────────────────────────────────
config: {
get: (params: ConfigGetParams) => invoke<unknown>(IPC_CHANNELS.CONFIG.GET, params),
set: (params: ConfigSetParams) => invoke<void>(IPC_CHANNELS.CONFIG.SET, params),
getAll: () => invoke<AppConfig>(IPC_CHANNELS.CONFIG.GET_ALL),
reset: (params: ConfigResetParams) => invoke<void>(IPC_CHANNELS.CONFIG.RESET, params),
getTheme: () => invoke<ThemeMode>(IPC_CHANNELS.CONFIG.GET_THEME),
setTheme: (params: SetThemeParams) => invoke<void>(IPC_CHANNELS.CONFIG.SET_THEME, params),
getLanguage: () => invoke<string>(IPC_CHANNELS.CONFIG.GET_LANGUAGE),
setLanguage: (params: SetLanguageParams) =>
invoke<void>(IPC_CHANNELS.CONFIG.SET_LANGUAGE, params),
onChanged: (cb: (e: ConfigChangedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.CONFIG.CHANGED, cb)
},
// ── Voice ──────────────────────────────────────────────
voice: {
startRecording: (params: StartRecordingParams) =>
invoke<StartRecordingResult>(IPC_CHANNELS.VOICE.START_RECORDING, params),
stopRecording: (params: StopRecordingParams) =>
invoke<StopRecordingResult>(IPC_CHANNELS.VOICE.STOP_RECORDING, params),
cancelRecording: (params: CancelRecordingParams) =>
invoke<void>(IPC_CHANNELS.VOICE.CANCEL_RECORDING, params),
getState: () => invoke<VoiceState>(IPC_CHANNELS.VOICE.GET_STATE),
setMode: (params: SetVoiceModeParams) =>
invoke<void>(IPC_CHANNELS.VOICE.SET_MODE, params),
getMode: () => invoke<VoiceMode>(IPC_CHANNELS.VOICE.GET_MODE),
onStateChanged: (cb: (e: VoiceStateChangedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE.STATE_CHANGED, cb),
onTranscriptionDelta: (cb: (e: TranscriptionDeltaEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE.TRANSCRIPTION_DELTA, cb),
onTranscriptionComplete: (cb: (e: TranscriptionCompleteEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE.TRANSCRIPTION_COMPLETE, cb),
onError: (cb: (e: VoiceErrorEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE.ERROR, cb),
onAudioLevel: (cb: (e: AudioLevelEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE.AUDIO_LEVEL, cb)
},
// ── STT ────────────────────────────────────────────────
stt: {
getStatus: () => invoke<STTStatus>(IPC_CHANNELS.STT.GET_STATUS),
getModels: () => invoke<STTModel[]>(IPC_CHANNELS.STT.GET_MODELS),
getActiveModel: () => invoke<string | null>(IPC_CHANNELS.STT.GET_ACTIVE_MODEL),
setModel: (params: SetSTTModelParams) =>
invoke<void>(IPC_CHANNELS.STT.SET_MODEL, params),
getLanguage: () => invoke<string>(IPC_CHANNELS.STT.GET_LANGUAGE),
setLanguage: (params: SetSTTLanguageParams) =>
invoke<void>(IPC_CHANNELS.STT.SET_LANGUAGE, params),
onStatusChanged: (cb: (e: STTStatusChangedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.STT.STATUS_CHANGED, cb)
},
// ── Hotkey ─────────────────────────────────────────────
hotkey: {
getDictationShortcut: () =>
invoke<HotkeyBinding>(IPC_CHANNELS.HOTKEY.GET_DICTATION_SHORTCUT),
setDictationShortcut: (params: SetHotkeyParams) =>
invoke<void>(IPC_CHANNELS.HOTKEY.SET_DICTATION_SHORTCUT, params),
getHandsFreeShortcut: () =>
invoke<HotkeyBinding>(IPC_CHANNELS.HOTKEY.GET_HANDS_FREE_SHORTCUT),
setHandsFreeShortcut: (params: SetHotkeyParams) =>
invoke<void>(IPC_CHANNELS.HOTKEY.SET_HANDS_FREE_SHORTCUT, params),
getCommandShortcut: () =>
invoke<HotkeyBinding>(IPC_CHANNELS.HOTKEY.GET_COMMAND_SHORTCUT),
setCommandShortcut: (params: SetHotkeyParams) =>
invoke<void>(IPC_CHANNELS.HOTKEY.SET_COMMAND_SHORTCUT, params),
getCaptionShortcut: () =>
invoke<HotkeyBinding>(IPC_CHANNELS.HOTKEY.GET_CAPTION_SHORTCUT),
setCaptionShortcut: (params: SetHotkeyParams) =>
invoke<void>(IPC_CHANNELS.HOTKEY.SET_CAPTION_SHORTCUT, params),
isEnabled: () => invoke<boolean>(IPC_CHANNELS.HOTKEY.IS_ENABLED),
setEnabled: (params: SetEnabledParams) =>
invoke<void>(IPC_CHANNELS.HOTKEY.SET_ENABLED, params),
onTriggered: (cb: (e: HotkeyTriggeredEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.HOTKEY.TRIGGERED, cb)
},
// ── LLM ────────────────────────────────────────────────
llm: {
getStatus: () => invoke<LLMStatus>(IPC_CHANNELS.LLM.GET_STATUS),
getModels: () => invoke<LLMModel[]>(IPC_CHANNELS.LLM.GET_MODELS),
getActiveModel: () => invoke<string | null>(IPC_CHANNELS.LLM.GET_ACTIVE_MODEL),
setModel: (params: SetLLMModelParams) =>
invoke<void>(IPC_CHANNELS.LLM.SET_MODEL, params),
process: (params: LLMProcessParams) =>
invoke<LLMProcessResult>(IPC_CHANNELS.LLM.PROCESS, params),
cancelProcess: () => invoke<void>(IPC_CHANNELS.LLM.CANCEL_PROCESS),
getServerUrl: () => invoke<string>(IPC_CHANNELS.LLM.GET_SERVER_URL),
setServerUrl: (params: SetServerUrlParams) =>
invoke<void>(IPC_CHANNELS.LLM.SET_SERVER_URL, params),
onStatusChanged: (cb: (e: LLMStatusChangedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.LLM.STATUS_CHANGED, cb),
onProcessProgress: (cb: (e: LLMProcessProgressEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.LLM.PROCESS_PROGRESS, cb)
},
// ── History ────────────────────────────────────────────
history: {
getAll: (params: HistoryQueryParams) =>
invoke<HistoryPage>(IPC_CHANNELS.HISTORY.GET_ALL, params),
getById: (params: HistoryGetByIdParams) =>
invoke<HistoryEntry | null>(IPC_CHANNELS.HISTORY.GET_BY_ID, params),
delete: (params: HistoryDeleteParams) =>
invoke<void>(IPC_CHANNELS.HISTORY.DELETE, params),
deleteAll: () => invoke<void>(IPC_CHANNELS.HISTORY.DELETE_ALL),
search: (params: HistorySearchParams) =>
invoke<HistoryPage>(IPC_CHANNELS.HISTORY.SEARCH, params),
onAdded: (cb: (e: HistoryEntry) => void): Unsubscribe =>
on(IPC_CHANNELS.HISTORY.ADDED, cb)
},
// ── Dictionary ─────────────────────────────────────────
dictionary: {
getAll: (params: DictionaryQueryParams) =>
invoke<DictionaryPage>(IPC_CHANNELS.DICTIONARY.GET_ALL, params),
add: (params: DictionaryAddParams) =>
invoke<DictionaryEntry>(IPC_CHANNELS.DICTIONARY.ADD, params),
update: (params: DictionaryUpdateParams) =>
invoke<DictionaryEntry>(IPC_CHANNELS.DICTIONARY.UPDATE, params),
delete: (params: DictionaryDeleteParams) =>
invoke<void>(IPC_CHANNELS.DICTIONARY.DELETE, params),
search: (params: DictionarySearchParams) =>
invoke<DictionaryPage>(IPC_CHANNELS.DICTIONARY.SEARCH, params)
},
// ── Stats ──────────────────────────────────────────────
stats: {
getSummary: () => invoke<StatsSummary>(IPC_CHANNELS.STATS.GET_SUMMARY)
},
// ── Window ─────────────────────────────────────────────
window: {
minimize: () => send(IPC_CHANNELS.WINDOW.MINIMIZE),
maximize: () => send(IPC_CHANNELS.WINDOW.MAXIMIZE),
close: () => send(IPC_CHANNELS.WINDOW.CLOSE),
isMaximized: () => invoke<boolean>(IPC_CHANNELS.WINDOW.IS_MAXIMIZED)
},
// ── System ─────────────────────────────────────────────
system: {
getPlatform: () => invoke<NodeJS.Platform>(IPC_CHANNELS.SYSTEM.GET_PLATFORM),
getVersion: () => invoke<string>(IPC_CHANNELS.SYSTEM.GET_VERSION),
checkMicPermission: () =>
invoke<PermissionStatus>(IPC_CHANNELS.SYSTEM.CHECK_MIC_PERMISSION),
openExternal: (params: { url: string }) =>
invoke<void>(IPC_CHANNELS.SYSTEM.OPEN_EXTERNAL, params),
},
// ── Instruction (커스텀 명령어) ────────────────────────
instruction: {
getAll: () => invoke<unknown[]>('instruction:getAll'),
getById: (params: { id: string }) => invoke<unknown>('instruction:getById', params),
create: (params: { name: string; description: string; prompt: string }) =>
invoke<unknown>('instruction:create', params),
update: (params: { id: string; name?: string; description?: string; prompt?: string }) =>
invoke<unknown>('instruction:update', params),
delete: (params: { id: string }) => invoke<void>('instruction:delete', params),
reorder: (params: { ids: string[] }) => invoke<void>('instruction:reorder', params),
},
// ── App 이벤트 (실시간 UI 갱신) ────────────────────────
app: {
onDataChanged: (cb: (data: { type: string; activeId?: string }) => void): Unsubscribe =>
on('app:dataChanged', cb),
},
// ── Phase 10: Memo Tags ─────────────────────────────────
memo: {
getTags: (params: GetTagsParams) => invoke<MemoTag[]>(IPC_CHANNELS.MEMO.GET_TAGS, params),
addTag: (params: AddTagParams) => invoke<MemoTag>(IPC_CHANNELS.MEMO.ADD_TAG, params),
removeTag: (params: RemoveTagParams) => invoke<void>(IPC_CHANNELS.MEMO.REMOVE_TAG, params),
getAllTags: () => invoke<TagCount[]>(IPC_CHANNELS.MEMO.GET_ALL_TAGS),
searchByTag: (params: SearchByTagParams) => invoke<HistoryPage>(IPC_CHANNELS.MEMO.SEARCH_BY_TAG, params),
export: (params: ExportMemoParams) => invoke<string>(IPC_CHANNELS.MEMO.EXPORT, params),
},
// ── Phase 10: Voice Commands ────────────────────────────
voiceCommand: {
getAll: () => invoke<VoiceCommandRule[]>(IPC_CHANNELS.VOICE_COMMAND.GET_ALL),
setKeywords: (params: SetVoiceCommandKeywordsParams) => invoke<void>(IPC_CHANNELS.VOICE_COMMAND.SET_KEYWORDS, params),
setEnabled: (params: SetVoiceCommandEnabledParams) => invoke<void>(IPC_CHANNELS.VOICE_COMMAND.SET_ENABLED, params),
isEnabled: () => invoke<boolean>(IPC_CHANNELS.VOICE_COMMAND.IS_ENABLED),
onMatched: (cb: (data: VoiceCommandMatch) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_COMMAND.MATCHED, cb),
},
// ── Phase 10: Screen Context ────────────────────────────
context: {
capture: (captureSelectedText?: boolean) => invoke<CaptureContextResult>(IPC_CHANNELS.CONTEXT.CAPTURE, { captureSelectedText }),
isEnabled: () => invoke<boolean>(IPC_CHANNELS.CONTEXT.IS_ENABLED),
setEnabled: (params: { enabled: boolean }) => invoke<void>(IPC_CHANNELS.CONTEXT.SET_ENABLED, params),
},
// ── Phase 10: LLM Chain ─────────────────────────────────
chain: {
getAll: () => invoke<LLMChain[]>(IPC_CHANNELS.CHAIN.GET_ALL),
create: (params: CreateChainParams) => invoke<LLMChain>(IPC_CHANNELS.CHAIN.CREATE, params),
update: (params: UpdateChainParams) => invoke<LLMChain>(IPC_CHANNELS.CHAIN.UPDATE, params),
delete: (params: DeleteChainParams) => invoke<void>(IPC_CHANNELS.CHAIN.DELETE, params),
execute: (params: ExecuteChainParams) => invoke<ChainExecutionResult>(IPC_CHANNELS.CHAIN.EXECUTE, params),
onProgress: (cb: (data: ChainProgress) => void): Unsubscribe =>
on(IPC_CHANNELS.CHAIN.PROGRESS, cb),
},
// ── Phase 10: Live Caption ──────────────────────────────
caption: {
start: () => invoke<void>(IPC_CHANNELS.CAPTION.START),
stop: () => invoke<void>(IPC_CHANNELS.CAPTION.STOP),
getState: () => invoke<CaptionState>(IPC_CHANNELS.CAPTION.GET_STATE),
setConfig: (params: Partial<CaptionConfig>) => invoke<void>(IPC_CHANNELS.CAPTION.SET_CONFIG, params),
getConfig: () => invoke<CaptionConfig>(IPC_CHANNELS.CAPTION.GET_CONFIG),
onSegment: (cb: (data: CaptionSegment) => void): Unsubscribe =>
on(IPC_CHANNELS.CAPTION.SEGMENT, cb),
onDelta: (cb: (data: { text: string }) => void): Unsubscribe =>
on(IPC_CHANNELS.CAPTION.DELTA, cb),
onStateChanged: (cb: (data: { state: CaptionState }) => void): Unsubscribe =>
on(IPC_CHANNELS.CAPTION.STATE_CHANGED, cb),
/** 시스템 오디오 PCM 데이터를 메인에 전달 (렌더러 → 메인) */
sendSystemAudioData: (data: ArrayBuffer) =>
send(IPC_CHANNELS.CAPTION.SYSTEM_AUDIO_DATA, data),
/** 메인에서 시스템 오디오 캡처 시작 요청 수신 */
onStartSystemAudio: (cb: () => void): Unsubscribe =>
on(IPC_CHANNELS.CAPTION.START_SYSTEM_AUDIO, cb),
/** 메인에서 시스템 오디오 캡처 중지 요청 수신 */
onStopSystemAudio: (cb: () => void): Unsubscribe =>
on(IPC_CHANNELS.CAPTION.STOP_SYSTEM_AUDIO, cb),
/** 시스템 오디오 루프백 활성화 (getDisplayMedia 전에 호출) */
enableLoopback: () => invoke<void>('system-audio:enable-loopback'),
/** 시스템 오디오 루프백 비활성화 */
disableLoopback: () => invoke<void>('system-audio:disable-loopback'),
},
// ── License (Phase 11) ────────────────────────────────
license: {
getInfo: () =>
invoke<LicenseInfo>(IPC_CHANNELS.LICENSE.GET_INFO),
activate: (params: ActivateLicenseParams) =>
invoke<ActivateLicenseResult>(IPC_CHANNELS.LICENSE.ACTIVATE, params),
deactivate: () =>
invoke<void>(IPC_CHANNELS.LICENSE.DEACTIVATE),
checkFeature: (feature: Feature) =>
invoke<FeatureAccess>(IPC_CHANNELS.LICENSE.CHECK_FEATURE, { feature }),
getUsage: (feature: Feature) =>
invoke<UsageQuota>(IPC_CHANNELS.LICENSE.GET_USAGE, { feature }),
getAllUsage: () =>
invoke<UsageQuota[]>(IPC_CHANNELS.LICENSE.GET_ALL_USAGE),
getTierComparison: () =>
invoke<TierComparison[]>(IPC_CHANNELS.LICENSE.GET_TIER_COMPARISON),
onUpgradePrompt: (cb: (e: UpgradePromptEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.LICENSE.UPGRADE_PROMPT, cb),
onTierChanged: (cb: (e: LicenseInfo) => void): Unsubscribe =>
on(IPC_CHANNELS.LICENSE.TIER_CHANGED, cb),
},
// ── File Transcription (Phase 12.1) ────────────────────
fileTranscription: {
start: (params: FileTranscriptionStartParams) =>
invoke<FileTranscriptionResult>(IPC_CHANNELS.FILE_TRANSCRIPTION.START, params),
cancel: () =>
invoke<void>(IPC_CHANNELS.FILE_TRANSCRIPTION.CANCEL),
getState: () =>
invoke<FileTranscriptionStateInfo>(IPC_CHANNELS.FILE_TRANSCRIPTION.GET_STATE),
onProgress: (cb: (data: FileTranscriptionProgress) => void): Unsubscribe =>
on(IPC_CHANNELS.FILE_TRANSCRIPTION.PROGRESS, cb),
onComplete: (cb: (data: FileTranscriptionResult) => void): Unsubscribe =>
on(IPC_CHANNELS.FILE_TRANSCRIPTION.COMPLETE, cb),
onError: (cb: (data: { message: string }) => void): Unsubscribe =>
on(IPC_CHANNELS.FILE_TRANSCRIPTION.ERROR, cb),
},
// ── Meeting Summary (Phase 12.2) ─────────────────────
meetingSummary: {
summarize: (params: MeetingSummarizeParams) =>
invoke<MeetingSummaryResult>(IPC_CHANNELS.MEETING_SUMMARY.SUMMARIZE, params),
getSummary: (params: MeetingSummaryGetParams) =>
invoke<MeetingSummaryResult | null>(IPC_CHANNELS.MEETING_SUMMARY.GET_SUMMARY, params),
exportMarkdown: (params: MeetingSummaryExportParams) =>
invoke<string>(IPC_CHANNELS.MEETING_SUMMARY.EXPORT_MARKDOWN, params),
onSummaryReady: (cb: (data: MeetingSummaryResult) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_SUMMARY.SUMMARY_READY, cb),
onProgress: (cb: (data: MeetingSummaryProgress) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_SUMMARY.SUMMARY_PROGRESS, cb),
},
// ── Dictation Templates (Phase 12.3) ─────────────────
dictationTemplate: {
getAll: () =>
invoke<DictationTemplate[]>(IPC_CHANNELS.DICTATION_TEMPLATE.GET_ALL),
create: (params: CreateTemplateParams) =>
invoke<DictationTemplate>(IPC_CHANNELS.DICTATION_TEMPLATE.CREATE, params),
update: (params: UpdateTemplateParams) =>
invoke<DictationTemplate>(IPC_CHANNELS.DICTATION_TEMPLATE.UPDATE, params),
delete: (params: DeleteTemplateParams) =>
invoke<void>(IPC_CHANNELS.DICTATION_TEMPLATE.DELETE, params),
startSession: (params: StartTemplateSessionParams) =>
invoke<void>(IPC_CHANNELS.DICTATION_TEMPLATE.START_SESSION, params),
cancelSession: () =>
invoke<void>(IPC_CHANNELS.DICTATION_TEMPLATE.CANCEL_SESSION),
getSessionState: () =>
invoke<TemplateSessionInfo | null>(IPC_CHANNELS.DICTATION_TEMPLATE.GET_SESSION_STATE),
setFieldValue: (params: SetFieldValueParams) =>
invoke<void>(IPC_CHANNELS.DICTATION_TEMPLATE.SET_FIELD_VALUE, params),
onSessionStateChanged: (cb: (data: TemplateSessionInfo) => void): Unsubscribe =>
on(IPC_CHANNELS.DICTATION_TEMPLATE.SESSION_STATE_CHANGED, cb),
onFieldCompleted: (cb: (data: TemplateFieldCompletedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.DICTATION_TEMPLATE.FIELD_COMPLETED, cb),
onSessionCompleted: (cb: (data: TemplateSessionCompletedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.DICTATION_TEMPLATE.SESSION_COMPLETED, cb),
},
// ── RAG (Phase 13.2) ──────────────────────────────────
rag: {
addDocument: (params?: RAGAddDocumentParams) =>
invoke<RAGDocument>(IPC_CHANNELS.RAG.ADD_DOCUMENT, params ?? {}),
removeDocument: (params: RAGRemoveDocumentParams) =>
invoke<void>(IPC_CHANNELS.RAG.REMOVE_DOCUMENT, params),
getDocuments: () =>
invoke<RAGDocument[]>(IPC_CHANNELS.RAG.GET_DOCUMENTS),
query: (params: RAGQueryParams) =>
invoke<RAGQueryResult>(IPC_CHANNELS.RAG.QUERY, params),
getState: () =>
invoke<RAGStateInfo>(IPC_CHANNELS.RAG.GET_STATE),
reindex: (documentId: string) =>
invoke<void>(IPC_CHANNELS.RAG.REINDEX, { documentId }),
onIndexProgress: (cb: (data: RAGIndexProgress) => void): Unsubscribe =>
on(IPC_CHANNELS.RAG.INDEX_PROGRESS, cb),
onIndexComplete: (cb: (data: { documentId: string; fileName: string }) => void): Unsubscribe =>
on(IPC_CHANNELS.RAG.INDEX_COMPLETE, cb),
},
// ── Voice Action (Phase 13.3) ────────────────────────
voiceAction: {
execute: (params: VoiceActionExecuteParams) =>
invoke<void>(IPC_CHANNELS.VOICE_ACTION.EXECUTE, params),
getPresets: () =>
invoke<VoiceActionPreset[]>(IPC_CHANNELS.VOICE_ACTION.GET_PRESETS),
getHistory: () =>
invoke<VoiceActionHistoryEntry[]>(IPC_CHANNELS.VOICE_ACTION.GET_HISTORY),
clearHistory: () =>
invoke<void>(IPC_CHANNELS.VOICE_ACTION.CLEAR_HISTORY),
setEnabled: (enabled: boolean) =>
invoke<void>(IPC_CHANNELS.VOICE_ACTION.SET_ENABLED, { enabled }),
isEnabled: () =>
invoke<boolean>(IPC_CHANNELS.VOICE_ACTION.IS_ENABLED),
onActionPlanned: (cb: (data: VoiceActionPlannedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_ACTION.ACTION_PLANNED, cb),
onActionExecuted: (cb: (data: VoiceActionExecutedEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_ACTION.ACTION_EXECUTED, cb),
onActionError: (cb: (data: VoiceActionErrorEvent) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_ACTION.ACTION_ERROR, cb),
},
// ── Voice Conversation (Phase 13.1) ───────────────────
voiceConversation: {
startSession: () =>
invoke<void>(IPC_CHANNELS.VOICE_CONVERSATION.START_SESSION),
stopSession: () =>
invoke<void>(IPC_CHANNELS.VOICE_CONVERSATION.STOP_SESSION),
sendMessage: (params: ConversationSendParams) =>
invoke<void>(IPC_CHANNELS.VOICE_CONVERSATION.SEND_MESSAGE, params),
getState: () =>
invoke<ConversationSessionInfo>(IPC_CHANNELS.VOICE_CONVERSATION.GET_STATE),
getHistory: () =>
invoke<ConversationMessage[]>(IPC_CHANNELS.VOICE_CONVERSATION.GET_HISTORY),
clearHistory: () =>
invoke<void>(IPC_CHANNELS.VOICE_CONVERSATION.CLEAR_HISTORY),
cancelResponse: () =>
invoke<void>(IPC_CHANNELS.VOICE_CONVERSATION.CANCEL_RESPONSE),
finishListening: () =>
invoke<void>('voiceConversation:finishListening'),
onStateChanged: (cb: (data: ConversationSessionInfo) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_CONVERSATION.STATE_CHANGED, cb),
onUserMessage: (cb: (data: ConversationMessage) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_CONVERSATION.USER_MESSAGE, cb),
onAssistantDelta: (cb: (data: ConversationAssistantDelta) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_CONVERSATION.ASSISTANT_DELTA, cb),
onAssistantMessage: (cb: (data: ConversationAssistantMessage) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_CONVERSATION.ASSISTANT_MESSAGE, cb),
onTTSStarted: (cb: () => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_CONVERSATION.TTS_STARTED, cb),
onTTSFinished: (cb: () => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_CONVERSATION.TTS_FINISHED, cb),
onError: (cb: (data: ConversationError) => void): Unsubscribe =>
on(IPC_CHANNELS.VOICE_CONVERSATION.ERROR, cb),
},
// ── Meeting Mode (Phase 14) ───────────────────────────
meetingMode: {
startRecording: () =>
invoke<MeetingStartResult>(IPC_CHANNELS.MEETING_MODE.START_RECORDING),
stopRecording: () =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.STOP_RECORDING),
addMemo: (params: MeetingAddMemoParams) =>
invoke<MeetingMemo>(IPC_CHANNELS.MEETING_MODE.ADD_MEMO, params),
getState: () =>
invoke<MeetingModeStateInfo>(IPC_CHANNELS.MEETING_MODE.GET_STATE),
getSessions: (params: MeetingGetSessionsParams) =>
invoke<MeetingSessionPage>(IPC_CHANNELS.MEETING_MODE.GET_SESSIONS, params),
getSession: (params: MeetingGetSessionParams) =>
invoke<MeetingSessionDetail>(IPC_CHANNELS.MEETING_MODE.GET_SESSION, params),
deleteSession: (params: MeetingDeleteSessionParams) =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.DELETE_SESSION, params),
updateTitle: (params: { sessionId: string; title: string }) =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.UPDATE_TITLE, params),
exportPdf: (params: MeetingExportParams) =>
invoke<string>(IPC_CHANNELS.MEETING_MODE.EXPORT_PDF, params),
exportMarkdown: (params: MeetingExportParams) =>
invoke<string>(IPC_CHANNELS.MEETING_MODE.EXPORT_MARKDOWN, params),
onStateChanged: (cb: (e: MeetingModeStateInfo) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_MODE.STATE_CHANGED, cb),
onSegment: (cb: (e: CaptionSegment) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_MODE.SEGMENT, cb),
onProcessingProgress: (cb: (e: MeetingProcessingProgress) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_MODE.PROCESSING_PROGRESS, cb),
onSessionCompleted: (cb: (e: MeetingSessionDetail) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_MODE.SESSION_COMPLETED, cb),
onError: (cb: (e: { code: number; message: string }) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_MODE.ERROR, cb),
onAudioLevel: (cb: (e: { level: number }) => void): Unsubscribe =>
on('meetingMode:audioLevel', cb),
// Phase 14.5
updateTranscript: (params: MeetingUpdateTranscriptParams) =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.UPDATE_TRANSCRIPT, params),
generateDocument: (params: MeetingGenerateDocParams) =>
invoke<MeetingDocument>(IPC_CHANNELS.MEETING_MODE.GENERATE_DOCUMENT, params),
getDocuments: (params: MeetingGetDocsParams) =>
invoke<MeetingDocument[]>(IPC_CHANNELS.MEETING_MODE.GET_DOCUMENTS, params),
updateDocument: (params: MeetingUpdateDocParams) =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.UPDATE_DOCUMENT, params),
deleteDocument: (params: MeetingDeleteDocParams) =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.DELETE_DOCUMENT, params),
exportDocument: (params: MeetingExportDocParams) =>
invoke<string>(IPC_CHANNELS.MEETING_MODE.EXPORT_DOCUMENT, params),
exportTranscript: (params: { sessionId: string; format: MeetingExportFormat }) =>
invoke<string>(IPC_CHANNELS.MEETING_MODE.EXPORT_TRANSCRIPT, params),
editSegment: (params: { sessionId: string; segmentId: string; text: string }) =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.EDIT_SEGMENT, params),
onDocGeneratingProgress: (cb: (e: MeetingDocGeneratingProgress) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_MODE.DOC_GENERATING_PROGRESS, cb),
// Phase 15
polishTranscript: (params: { sessionId: string }) =>
invoke<string>(IPC_CHANNELS.MEETING_MODE.POLISH_TRANSCRIPT, params),
// Phase 15.5
diarize: (params: DiarizeSessionParams) =>
invoke<void>(IPC_CHANNELS.MEETING_MODE.DIARIZE, params),
onDiarizationProgress: (cb: (e: { sessionId: string; percent: number }) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_MODE.DIARIZATION_PROGRESS, cb),
},
// ── Meeting Chat (Phase 15) ───────────────────────────
meetingChat: {
send: (params: MeetingChatSendParams) =>
invoke<void>(IPC_CHANNELS.MEETING_CHAT.SEND, params),
cancel: () =>
invoke<void>(IPC_CHANNELS.MEETING_CHAT.CANCEL),
clear: () =>
invoke<void>(IPC_CHANNELS.MEETING_CHAT.CLEAR),
onDelta: (cb: (e: MeetingChatDelta) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_CHAT.DELTA, cb),
onMessage: (cb: (e: MeetingChatMessage) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_CHAT.MESSAGE, cb),
onError: (cb: (e: { message: string }) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_CHAT.ERROR, cb),
},
// ── Meeting Doc Templates (Phase 14.5) ───────────────
meetingDocTemplate: {
getAll: () =>
invoke<MeetingDocTemplate[]>(IPC_CHANNELS.MEETING_DOC_TEMPLATE.GET_ALL),
create: (params: CreateMeetingDocTemplateParams) =>
invoke<MeetingDocTemplate>(IPC_CHANNELS.MEETING_DOC_TEMPLATE.CREATE, params),
update: (params: UpdateMeetingDocTemplateParams) =>
invoke<MeetingDocTemplate>(IPC_CHANNELS.MEETING_DOC_TEMPLATE.UPDATE, params),
delete: (params: DeleteMeetingDocTemplateParams) =>
invoke<void>(IPC_CHANNELS.MEETING_DOC_TEMPLATE.DELETE, params),
},
} as const
contextBridge.exposeInMainWorld('electronAPI', electronAPI)
export type ElectronAPI = typeof electronAPI