fix(desktop): harden session, meeting, caption and LLM lifecycles; route LLM calls through the gateway

This commit is contained in:
Yun Chan 2026-09-28 02:16:15 +09:00
parent 3a46437f28
commit ddc78546f0
62 changed files with 4786 additions and 648 deletions

View file

@ -1,7 +1,7 @@
// src/preload/index.ts
// contextBridge로 렌더러에 노출할 API 정의
import { contextBridge, ipcRenderer } from 'electron'
import { contextBridge, ipcRenderer, webUtils } from 'electron'
import { IPC_CHANNELS } from '@d3ro/core/ipc-channels'
import type {
AudioDevice,
@ -184,6 +184,12 @@ import type {
DiarizeSessionParams,
} from '@d3ro/core/types'
import { Feature } from '@d3ro/core/types'
import {
MEETING_CHAT_GET_HISTORY_CHANNEL,
MEETING_MODE_GET_LIVE_SESSION_CHANNEL,
type MeetingChatHistoryTurn,
type MeetingLiveSession,
} from '@d3ro/core/meeting-live'
import type { IPCResult } from '@d3ro/core/errors'
/** 로컬 AI 런타임(사이드카 엔진/ffmpeg) 상태 — RuntimeProvisioner가 제공한다 */
@ -581,6 +587,8 @@ const electronAPI = {
invoke<FileTranscriptionResult>(IPC_CHANNELS.FILE_TRANSCRIPTION.START, params),
cancel: () =>
invoke<void>(IPC_CHANNELS.FILE_TRANSCRIPTION.CANCEL),
/** 드롭한 File 의 실제 경로 (Electron 32+ 는 File.path 가 없다). 알 수 없으면 빈 문자열. */
getPathForFile: (file: File): string => webUtils.getPathForFile(file),
getState: () =>
invoke<FileTranscriptionStateInfo>(IPC_CHANNELS.FILE_TRANSCRIPTION.GET_STATE),
onProgress: (cb: (data: FileTranscriptionProgress) => void): Unsubscribe =>
@ -725,6 +733,9 @@ const electronAPI = {
invoke<MeetingMemo>(IPC_CHANNELS.MEETING_MODE.ADD_MEMO, params),
getState: () =>
invoke<MeetingModeStateInfo>(IPC_CHANNELS.MEETING_MODE.GET_STATE),
/** 진행 중인 녹음의 실시간 전사·메모 (페이지를 다시 열 때 복원용). 녹음 중이 아니면 null */
getLiveSession: () =>
invoke<MeetingLiveSession | null>(MEETING_MODE_GET_LIVE_SESSION_CHANNEL),
getSessions: (params: MeetingGetSessionsParams) =>
invoke<MeetingSessionPage>(IPC_CHANNELS.MEETING_MODE.GET_SESSIONS, params),
getSession: (params: MeetingGetSessionParams) =>
@ -782,10 +793,15 @@ const electronAPI = {
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),
/** sessionId 를 주면 그 회의의 채팅만 멈춘다 */
cancel: (params?: { sessionId: string }) =>
invoke<void>(IPC_CHANNELS.MEETING_CHAT.CANCEL, params),
/** sessionId 를 주면 그 회의의 대화만 지운다(생략하면 전부) */
clear: (params?: { sessionId: string }) =>
invoke<void>(IPC_CHANNELS.MEETING_CHAT.CLEAR, params),
/** main 이 기억하는 이 회의의 대화 — 패널을 다시 열 때 보여 준다 */
getHistory: (params: { sessionId: string }) =>
invoke<MeetingChatHistoryTurn[]>(MEETING_CHAT_GET_HISTORY_CHANNEL, params),
onDelta: (cb: (e: MeetingChatDelta) => void): Unsubscribe =>
on(IPC_CHANNELS.MEETING_CHAT.DELTA, cb),
onMessage: (cb: (e: MeetingChatMessage) => void): Unsubscribe =>