// src/shared/types.ts // 모든 IPC 파라미터/반환 타입 정의 // ============================================================ // Common // ============================================================ export type ThemeMode = 'light' | 'dark' | 'auto' | 'nord' | 'solarized' | 'catppuccin' | 'dracula' export type VoiceMode = 'dictation' | 'hands-free' export type PermissionStatus = 'granted' | 'denied' | 'unknown' // ============================================================ // Voice (음성 오케스트레이션) // ============================================================ export enum RecognitionState { IDLE = 'idle', PREPARING = 'preparing', CONNECTING = 'connecting', READY = 'ready', RECOGNIZING = 'recognizing', COMPLETED = 'completed', CANCELLED = 'cancelled', ERROR = 'error', DESTROYED = 'destroyed' } export enum AudioState { IDLE = 'idle', INITIALIZING = 'initializing', STREAMING = 'streaming', STOPPED = 'stopped' } export interface VoiceState { recognitionState: RecognitionState audioState: AudioState mode: VoiceMode sessionId: string | null recordingStartedAt: number | null } export interface StartRecordingParams { sessionId?: string deviceId?: string } export interface StartRecordingResult { sessionId: string } export interface StopRecordingParams { sessionId: string } export interface StopRecordingResult { sessionId: string text: string durationMs: number } export interface CancelRecordingParams { sessionId: string } export interface SetVoiceModeParams { mode: VoiceMode } // Voice events (Main → Renderer) export interface VoiceStateChangedEvent { previousState: RecognitionState currentState: RecognitionState audioState: AudioState sessionId: string | null } export interface TranscriptionDeltaEvent { sessionId: string text: string delta: string isFinal: boolean } export interface TranscriptionCompleteEvent { sessionId: string text: string durationMs: number language: string } export interface VoiceErrorEvent { sessionId: string | null errorCode: number message: string } export interface AudioLevelEvent { level: number } // ============================================================ // Audio (디바이스 & 캡처) // ============================================================ export interface AudioDevice { deviceId: string label: string isDefault: boolean } export interface SetDeviceParams { deviceId: string } export interface TestDeviceParams { deviceId: string durationMs?: number } export interface TestDeviceResult { averageLevel: number peakLevel: number hasAudio: boolean } export interface AudioDeviceChangedEvent { devices: AudioDevice[] type: 'added' | 'removed' | 'default-changed' } // ============================================================ // STT (로컬 Whisper) // ============================================================ export enum STTEngineState { NOT_INSTALLED = 'not-installed', DOWNLOADING = 'downloading', LOADING = 'loading', READY = 'ready', PROCESSING = 'processing', ERROR = 'error' } export interface STTStatus { engineState: STTEngineState activeModel: string | null engineVersion: string | null gpuAccelerated: boolean } export interface STTModel { id: string name: string sizeBytes: number downloaded: boolean languages: string[] accuracy: number speed: number } export interface SetSTTModelParams { modelId: string } export interface DownloadModelParams { modelId: string } export interface SetSTTLanguageParams { language: string } export interface STTStatusChangedEvent { status: STTStatus } export interface DownloadProgressEvent { modelId: string percent: number downloadedBytes: number totalBytes: number bytesPerSecond: number } // ============================================================ // TTS (로컬 TTS) // ============================================================ export enum TTSEngineState { NOT_INSTALLED = 'not-installed', LOADING = 'loading', READY = 'ready', SPEAKING = 'speaking', ERROR = 'error' } export interface TTSStatus { engineState: TTSEngineState activeVoice: string | null engineVersion: string | null } export interface TTSVoice { id: string name: string language: string gender: 'male' | 'female' | 'neutral' downloaded: boolean sizeBytes: number } export interface TTSSpeakParams { text: string voiceId?: string speed?: number } export interface TTSSpeakResult { durationMs: number } export interface SetTTSVoiceParams { voiceId: string } export interface DownloadVoiceParams { voiceId: string } export interface TTSStatusChangedEvent { status: TTSStatus } export interface SpeakingStateChangedEvent { isSpeaking: boolean text: string } // ============================================================ // LLM (Ollama) // ============================================================ export enum LLMConnectionState { DISCONNECTED = 'disconnected', CONNECTING = 'connecting', CONNECTED = 'connected', ERROR = 'error' } export interface LLMStatus { connectionState: LLMConnectionState serverUrl: string activeModel: string | null serverVersion: string | null } export interface LLMModel { id: string name: string sizeBytes: number parameterSize: string quantization: string modifiedAt: string } export type LLMAction = 'refine' | 'translate' | 'summarize' | 'expand' | 'grammar' | 'custom' | 'chain' export interface LLMProcessParams { text: string action: LLMAction targetLanguage?: string customPrompt?: string modelId?: string } export interface LLMProcessResult { originalText: string processedText: string action: LLMAction processingTimeMs: number tokenCount: number } export interface SetLLMModelParams { modelId: string } export interface SetServerUrlParams { url: string } export interface PullModelParams { modelName: string } export interface LLMStatusChangedEvent { status: LLMStatus } export interface LLMProcessProgressEvent { text: string token: string done: boolean } export interface LLMPullProgressEvent { modelName: string status: string percent: number downloadedBytes: number totalBytes: number } // ============================================================ // Hotkey (핫키) // ============================================================ export interface HotkeyBinding { keyCode: number ctrl: boolean alt: boolean shift: boolean meta: boolean displayLabel: string } export interface SetHotkeyParams { binding: HotkeyBinding } export interface SetEnabledParams { enabled: boolean } export type HotkeyAction = 'dictation' | 'hands-free' | 'command' | 'caption' export interface HotkeyTriggeredEvent { action: HotkeyAction type: 'pressed' | 'released' isDoublePress: boolean } export interface HotkeyRecordingResultEvent { binding: HotkeyBinding | null conflictReason: string | null } // ============================================================ // Config (설정) // ============================================================ export interface AppConfig { theme: ThemeMode language: string closeToTray: boolean autoLaunch: boolean soundEnabled: boolean selectedDeviceId: string | null sttModelId: string sttLanguage: string ttsVoiceId: string | null ttsSpeed: number ollamaServerUrl: string llmModelId: string | null defaultLLMAction: LLMAction dictationShortcut: HotkeyBinding handsFreeShortcut: HotkeyBinding commandShortcut: HotkeyBinding /** 실시간 자막 토글 핫키 (Phase 10.1) */ captionShortcut: HotkeyBinding hotkeyEnabled: boolean insertMethod: 'clipboard' | 'keyboard' autoInsert: boolean maxHistoryEntries: number /** 받아쓰기 모드 활성화 (hold-to-talk) */ dictationEnabled: boolean /** Agent 모드 활성화 (더블프레스, dictation 의존) */ agentModeEnabled: boolean /** 핸즈프리 모드 활성화 (토글) */ handsFreeEnabled: boolean /** 스크린 컨텍스트 캡처 활성화 (Phase 10.2) */ screenContextEnabled: boolean } export interface ConfigGetParams { key: keyof AppConfig } export interface ConfigSetParams { key: keyof AppConfig value: AppConfig[keyof AppConfig] } export interface ConfigResetParams { key?: keyof AppConfig } export interface SetThemeParams { theme: ThemeMode } export interface SetLanguageParams { language: string } export interface SetAutoLaunchParams { enabled: boolean } export interface SetCloseToTrayParams { enabled: boolean } export interface ConfigChangedEvent { key: keyof AppConfig value: AppConfig[keyof AppConfig] previousValue: AppConfig[keyof AppConfig] } // ============================================================ // History (히스토리) // ============================================================ export interface HistoryEntry { id: string originalText: string polishedText: string | null focusedApp: string | null focusedAppName: string | null focusedAppWindowTitle: string | null mode: 'dictation' | 'translate' | 'command' | 'caption' | 'file-transcription' status: 'completed' | 'cancelled' | 'error' errorCode: string | null audioLocalPath: string | null duration: number detectedLanguage: string | null micDevice: string | null wordCount: number sttModel: string | null llmModel: string | null sttLatencyMs: number | null llmLatencyMs: number | null createdAt: number updatedAt: number appVersion: string /** Phase 12.2: 회의록 요약 마크다운 */ summaryText: string | null } export interface HistoryQueryParams { page: number pageSize: number sortBy?: 'createdAt' | 'durationMs' | 'wordCount' sortOrder?: 'asc' | 'desc' } export interface HistoryPage { entries: HistoryEntry[] total: number page: number pageSize: number totalPages: number } export interface HistoryGetByIdParams { id: string } export interface HistoryDeleteParams { id: string } export interface HistorySearchParams { query: string page: number pageSize: number } export interface HistoryExportParams { format: 'json' | 'csv' from?: string to?: string } // ============================================================ // Dictionary (사전) // ============================================================ export interface DictionaryEntry { id: string word: string pronunciation: string | null category: 'user' | 'auto' | 'technical' usageCount: number lastUsedAt: number | null createdAt: number updatedAt: number } export interface DictionaryQueryParams { page: number pageSize: number sortBy?: 'word' | 'category' | 'usageCount' | 'createdAt' sortOrder?: 'asc' | 'desc' } export interface DictionaryPage { entries: DictionaryEntry[] total: number page: number pageSize: number totalPages: number } export interface DictionaryAddParams { word: string pronunciation?: string category?: 'user' | 'auto' | 'technical' } export interface DictionaryUpdateParams { id: string word?: string pronunciation?: string category?: 'user' | 'auto' | 'technical' } export interface DictionaryDeleteParams { id: string } export interface DictionaryImportParams { filePath: string format: 'json' | 'csv' } export interface DictionaryImportResult { imported: number skipped: number errors: number } export interface DictionaryExportParams { format: 'json' | 'csv' } export interface DictionarySearchParams { query: string page: number pageSize: number } // ============================================================ // Window (윈도우 제어) // ============================================================ export type RecordingTipState = 'opening' | 'recording' | 'thinking' | 'result' | 'error' export interface ShowRecordingTipParams { state: RecordingTipState text?: string errorMessage?: string } export interface ShowResultPopupParams { text: string autoHideMs?: number } export interface TipMeasuredParams { width: number height: number } export interface TipStateChangedEvent { state: RecordingTipState text?: string errorMessage?: string } export interface TipPrepareEvent { state: RecordingTipState text?: string } export interface TipShowEvent { state: RecordingTipState } // ============================================================ // System (시스템) // ============================================================ export interface ActiveAppInfo { name: string title: string pid: number } export interface ShowNotificationParams { title: string body: string type?: 'info' | 'warning' | 'error' } export interface OpenExternalParams { url: string } export interface InsertTextParams { text: string method?: 'clipboard' | 'keyboard' } export interface InsertTextResult { success: boolean insertedLength: number } export type SoundEffect = | 'recording-start' | 'recording-stop' | 'transcription-complete' | 'error' | 'notification' export interface PlaySoundParams { sound: SoundEffect } export interface SetSoundEnabledParams { enabled: boolean } // ============================================================ // Stats (통계) // ============================================================ export interface StatsSummary { totalRecordingTimeMs: number totalWordCount: number totalSessionCount: number todayRecordingTimeMs: number todayWordCount: number todaySessionCount: number streakDays: number } export interface StatsQueryParams { from: string to: string } export interface DailyStats { date: string recordingTimeMs: number wordCount: number sessionCount: number } export interface WeeklyStats { weekStart: string recordingTimeMs: number wordCount: number sessionCount: number } // ============================================================ // Phase 10: Memo Tags (10.3) // ============================================================ export interface MemoTag { id: string historyId: string tag: string createdAt: number } export interface AddTagParams { historyId: string tag: string } export interface RemoveTagParams { historyId: string tag: string } export interface GetTagsParams { historyId: string } export interface SearchByTagParams { tag: string page: number pageSize: number } export interface ExportMemoParams { format: 'markdown' tag?: string from?: string to?: string } export interface TagCount { tag: string count: number } // ============================================================ // Phase 10: Voice Commands (10.5) // ============================================================ export type KeywordMatchMode = 'prefix' | 'suffix' | 'contains' export interface VoiceCommandKeyword { keyword: string matchMode: KeywordMatchMode } export interface VoiceCommandRule { id: string instructionId: string keywords: VoiceCommandKeyword[] enabled: boolean priority: number } export interface VoiceCommandMatch { matched: boolean ruleId: string | null instructionId: string | null /** 키워드 제거 후 남은 텍스트 */ cleanedText: string /** 매칭된 키워드 */ matchedKeyword: string | null } export interface SetVoiceCommandKeywordsParams { instructionId: string keywords: VoiceCommandKeyword[] } export interface SetVoiceCommandEnabledParams { enabled: boolean } // ============================================================ // Phase 10: Screen Context (10.2) // ============================================================ export interface ScreenContext { appName: string | null windowTitle: string | null selectedText: string | null capturedAt: number } export interface CaptureContextResult { context: ScreenContext /** 선택 텍스트 캡처 시도 여부 */ selectedTextAttempted: boolean } // ============================================================ // Phase 10: LLM Chain (10.4) // ============================================================ export interface ChainStep { instructionId: string /** 이전 단계 결과 사용 or 원본 텍스트 사용 */ inputSource: 'previous' | 'original' } export interface LLMChain { id: string name: string steps: ChainStep[] createdAt: number updatedAt: number } export interface CreateChainParams { name: string steps: ChainStep[] } export interface UpdateChainParams { id: string name?: string steps?: ChainStep[] } export interface DeleteChainParams { id: string } export interface ExecuteChainParams { chainId: string text: string } export interface ChainProgress { chainId: string currentStep: number totalSteps: number stepName: string intermediateText: string } export interface ChainExecutionResult { chainId: string finalText: string steps: Array<{ instructionId: string; output: string; durationMs: number }> totalDurationMs: number } // ============================================================ // Phase 10: Live Caption (10.1) // ============================================================ export type CaptionState = 'inactive' | 'starting' | 'active' | 'stopping' export interface CaptionSegment { id: string text: string timestamp: number isFinal: boolean } export type CaptionAudioSource = 'mic' | 'system' | 'both' export interface CaptionConfig { fontSize: number opacity: number maxLines: number autoClearMs: number /** 오디오 소스: 마이크 / 시스템 오디오 / 둘 다 */ audioSource: CaptionAudioSource } export interface CaptionSessionSummary { sessionId: string segments: CaptionSegment[] startedAt: number endedAt: number totalDurationMs: number } // ============================================================ // Phase 11: License & Monetization // ============================================================ /** 라이센스 티어 */ export type LicenseTier = 'free' | 'pro' | 'pro_plus' /** 기능 게이팅 대상 */ export enum Feature { // 쿼터 제한 기능 (Free에서 횟수 제한) DICTATION = 'dictation', LLM_PROCESS = 'llm_process', // Pro 이상 HISTORY_UNLIMITED = 'history_unlimited', HISTORY_EXPORT = 'history_export', CUSTOM_INSTRUCTION_CREATE = 'custom_instruction_create', LIVE_CAPTION = 'live_caption', SCREEN_CONTEXT = 'screen_context', VOICE_MEMO = 'voice_memo', VOICE_COMMAND = 'voice_command', LLM_CHAIN = 'llm_chain', // Pro+ 이상 FILE_TRANSCRIPTION = 'file_transcription', VOICE_CONVERSATION = 'voice_conversation', DICTATION_TEMPLATE = 'dictation_template', MEETING_SUMMARY = 'meeting_summary', LOCAL_RAG = 'local_rag', OS_AUTOMATION = 'os_automation', } /** 라이센스 정보 (electron-store에 저장) */ export interface LicenseInfo { tier: LicenseTier licenseKey: string | null activatedAt: number | null machineId: string /** 마지막 온라인 검증 시각 */ lastVerifiedAt: number | null /** 오프라인 유예 만료 (lastVerifiedAt + 30일) */ offlineGraceUntil: number | null } /** 일일 사용량 */ export interface DailyUsage { date: string // 'YYYY-MM-DD' feature: string count: number } /** 쿼터 정보 */ export interface UsageQuota { feature: Feature used: number limit: number // -1 = 무제한 remaining: number // -1 = 무제한 resetAt: string // 다음 리셋 시각 (내일 00:00) ISO 8601 } /** 기능 접근 결과 */ export interface FeatureAccess { allowed: boolean reason: 'ok' | 'quota_exceeded' | 'tier_required' | 'license_expired' requiredTier?: LicenseTier quota?: UsageQuota } /** 업그레이드 유도 이벤트 */ export interface UpgradePromptEvent { feature: Feature reason: 'quota_exceeded' | 'tier_required' currentTier: LicenseTier requiredTier: LicenseTier quota?: UsageQuota } /** 라이센스 활성화 파라미터 */ export interface ActivateLicenseParams { licenseKey: string } /** 라이센스 활성화 결과 */ export interface ActivateLicenseResult { success: boolean tier: LicenseTier message: string } /** 티어별 기능 비교 항목 */ export interface TierComparison { feature: Feature featureLabel: string free: boolean | string pro: boolean | string proPlus: boolean | string } // ============================================================ // Phase 12: File Transcription (12.1) // ============================================================ export type FileTranscriptionState = 'idle' | 'converting' | 'transcribing' | 'completed' | 'error' export interface FileTranscriptionStartParams { filePath: string language?: string } export interface FileTranscriptionProgress { jobId: string currentChunk: number totalChunks: number percent: number currentText: string } export interface FileTranscriptionSegment { text: string start: number end: number confidence: number } export interface FileTranscriptionResult { jobId: string filePath: string fileName: string fullText: string segments: FileTranscriptionSegment[] totalDurationSec: number processingTimeMs: number } export interface FileTranscriptionStateInfo { state: FileTranscriptionState jobId: string | null progress: FileTranscriptionProgress | null } // ============================================================ // Phase 12: Meeting Summary (12.2) // ============================================================ export interface MeetingSummaryResult { historyId: string summary: string decisions: string[] actionItems: string[] rawMarkdown: string generatedAt: number } export interface MeetingSummarizeParams { historyId: string } export interface MeetingSummaryGetParams { historyId: string } export interface MeetingSummaryExportParams { historyId: string } export interface MeetingSummaryProgress { historyId: string status: 'generating' | 'done' | 'error' } // ============================================================ // Phase 12: Dictation Templates (12.3) // ============================================================ export interface TemplateField { id: string name: string label: string promptText: string required: boolean maxDurationSec: number } export interface DictationTemplate { id: string name: string description: string fields: TemplateField[] outputFormat: string isBuiltin: boolean createdAt: number updatedAt: number } export interface CreateTemplateParams { name: string description: string fields: TemplateField[] outputFormat: string } export interface UpdateTemplateParams { id: string name?: string description?: string fields?: TemplateField[] outputFormat?: string } export interface DeleteTemplateParams { id: string } export type TemplateSessionState = | 'idle' | 'field-prompting' | 'field-recording' | 'completing' export interface TemplateSessionInfo { templateId: string templateName: string state: TemplateSessionState currentFieldIndex: number totalFields: number currentField: TemplateField | null fieldValues: Record } export interface StartTemplateSessionParams { templateId: string } export interface SetFieldValueParams { fieldId: string value: string } export interface TemplateFieldCompletedEvent { fieldId: string fieldName: string value: string nextField: TemplateField | null } export interface TemplateSessionCompletedEvent { templateId: string outputText: string fieldValues: Record } // ============================================================ // Phase 13: Voice Conversation (13.1) // ============================================================ export type ConversationState = | 'idle' | 'listening' | 'thinking' | 'speaking' export type ConversationRole = 'user' | 'assistant' | 'system' export interface ConversationMessage { id: string role: ConversationRole content: string timestamp: number } export interface ConversationSessionInfo { state: ConversationState messages: ConversationMessage[] isActive: boolean } export interface ConversationSendParams { text: string } export interface ConversationAssistantDelta { messageId: string delta: string accumulated: string } export interface ConversationAssistantMessage { messageId: string content: string } export interface ConversationError { message: string phase: 'stt' | 'llm' | 'tts' } // ============================================================ // Phase 13: Local RAG (13.2) // ============================================================ export interface RAGDocument { id: string fileName: string filePath: string fileType: 'txt' | 'md' | 'pdf' | 'docx' chunkCount: number indexed: boolean indexedAt: number | null addedAt: number } export interface RAGChunk { id: string documentId: string content: string embedding: number[] chunkIndex: number } export interface RAGQueryParams { query: string topK?: number } export interface RAGQueryResult { query: string results: Array<{ documentId: string fileName: string content: string similarity: number }> /** LLM 답변 (컨텍스트 주입 후) */ answer: string } export interface RAGAddDocumentParams { filePath: string } export interface RAGRemoveDocumentParams { documentId: string } export interface RAGIndexProgress { documentId: string fileName: string currentChunk: number totalChunks: number percent: number } export type RAGState = 'idle' | 'indexing' | 'querying' export interface RAGStateInfo { state: RAGState documentCount: number totalChunks: number } // ============================================================ // Phase 13: Voice Action / OS Automation (13.3) // ============================================================ export type VoiceActionType = | 'open_app' | 'open_url' | 'open_file' | 'keyboard_shortcut' | 'type_text' | 'system_command' export interface VoiceActionPlan { action: VoiceActionType target: string description: string safe: boolean } export interface VoiceActionExecuteParams { text: string } export interface VoiceActionPreset { keywords: string[] action: VoiceActionPlan } export interface VoiceActionHistoryEntry { id: string userText: string plan: VoiceActionPlan executed: boolean timestamp: number } export interface VoiceActionPlannedEvent { plan: VoiceActionPlan userText: string } export interface VoiceActionExecutedEvent { plan: VoiceActionPlan success: boolean } export interface VoiceActionErrorEvent { message: string userText: string } // ============================================================ // Phase 14: Meeting Mode // ============================================================ export type MeetingModeState = 'idle' | 'recording' | 'processing' | 'completed' | 'error' export interface MeetingMemo { id: string sessionId: string content: string /** 녹음 시작 기준 상대 타임스탬프 (ms) */ timestampMs: number createdAt: number } export interface MeetingSessionSummary { id: string title: string | null status: 'recording' | 'processing' | 'completed' | 'error' startedAt: number endedAt: number | null durationMs: number | null memoCount: number } export interface MeetingSessionDetail extends MeetingSessionSummary { rawTranscript: string | null minutesMarkdown: string | null minutes: MeetingMinutes | null memos: MeetingMemo[] sttModel: string | null llmModel: string | null errorMessage: string | null } export interface MeetingMinutes { summary: string decisions: string[] actionItems: Array<{ task: string; assignee?: string; deadline?: string }> timeline: Array<{ time: string; content: string; type: 'transcript' | 'memo' }> } export interface MeetingSessionPage { sessions: MeetingSessionSummary[] total: number page: number pageSize: number totalPages: number } export interface MeetingStartResult { sessionId: string } export interface MeetingAddMemoParams { content: string } export interface MeetingGetSessionsParams { page: number pageSize: number } export interface MeetingGetSessionParams { sessionId: string } export interface MeetingDeleteSessionParams { sessionId: string } export interface MeetingExportParams { sessionId: string } export interface MeetingModeStateInfo { state: MeetingModeState sessionId: string | null elapsedMs: number memoCount: number segmentCount: number } export type MeetingProcessingStep = | 'merging' | 'generating' | 'parsing' | 'saving' | 'notifying' export interface MeetingProcessingProgress { sessionId: string step: MeetingProcessingStep percent: number }