Phase 5 구현: SQLite DB + History/Dictionary 서비스 + UI
- DB: better-sqlite3 + drizzle-orm (history/dictionary/stats, WAL 모드) - HistoryService: CRUD + 검색 + 통계 + 30일 보존 정책, 세션 완료 시 자동 저장 - DictionaryService: CRUD + 검색 + 사용 횟수 추적 + STT 프롬프트 힌트 - HistoryPage: 목록 + 검색 + 삭제 + 복사 + 페이지네이션 - DictionaryPage: 목록 + 검색 + 추가 다이얼로그 + 삭제 - DashboardPage: 실데이터 통계 (총/오늘 세션, 시간, 단어수, 연속일수) - IPC: history/dictionary/stats 핸들러 + preload API - Bootstrap: DB 초기화 (critical) + 이력 자동 저장 연동
This commit is contained in:
parent
4a5cf6c819
commit
291e2a29d0
17 changed files with 3111 additions and 35 deletions
|
|
@ -6,6 +6,8 @@ import { initConfigService } from './services/ConfigService'
|
|||
import { getHotkeyService } from './services/HotkeyService'
|
||||
import { getVoiceModeService } from './services/VoiceModeService'
|
||||
import { getLocalLLMService } from './services/LocalLLMService'
|
||||
import { getHistoryService } from './services/HistoryService'
|
||||
import { initDatabase } from './db'
|
||||
import {
|
||||
createMainWindow,
|
||||
preloadPopupWindows,
|
||||
|
|
@ -30,6 +32,7 @@ export async function bootstrap(): Promise<void> {
|
|||
const steps: BootstrapStep[] = [
|
||||
{ name: 'logger', critical: false, fn: initLogger },
|
||||
{ name: 'config', critical: false, fn: initConfig },
|
||||
{ name: 'database', critical: true, fn: initDB },
|
||||
{ name: 'create-windows', critical: true, fn: createWindows },
|
||||
{ name: 'tray', critical: false, fn: initTray },
|
||||
{ name: 'ipc-handlers', critical: true, fn: initIpcHandlers },
|
||||
|
|
@ -65,6 +68,10 @@ async function initConfig(): Promise<void> {
|
|||
initConfigService()
|
||||
}
|
||||
|
||||
async function initDB(): Promise<void> {
|
||||
initDatabase()
|
||||
}
|
||||
|
||||
async function createWindows(): Promise<void> {
|
||||
createMainWindow()
|
||||
}
|
||||
|
|
@ -106,11 +113,26 @@ async function initVoiceMode(): Promise<void> {
|
|||
}
|
||||
})
|
||||
|
||||
voiceMode.on('session-completed', ({ finalText }) => {
|
||||
voiceMode.on('session-completed', ({ session, finalText }) => {
|
||||
hideRecordingTip()
|
||||
if (finalText.length > 0) {
|
||||
showResultPopup(finalText)
|
||||
}
|
||||
|
||||
// 이력 저장
|
||||
try {
|
||||
const wordCount = finalText.split(/\s+/).filter((w) => w.length > 0).length
|
||||
getHistoryService().create({
|
||||
originalText: session.transcription || finalText,
|
||||
polishedText: session.processedText,
|
||||
mode: session.mode === 'hands-free' ? 'dictation' : session.mode,
|
||||
status: 'completed',
|
||||
duration: (Date.now() - session.startedAt) / 1000,
|
||||
wordCount
|
||||
})
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to save history: ${error instanceof Error ? error.message : String(error)}`)
|
||||
}
|
||||
})
|
||||
|
||||
voiceMode.on('session-cancelled', () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue