diff --git a/src/main/index.ts b/src/main/index.ts index bc4754d..eb891a2 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,12 +1,86 @@ -import { app, BrowserWindow } from 'electron'; +import { app, BrowserWindow, Notification } from 'electron'; import '@shared/types'; -import { createInboxWindow } from './windows/inboxWindow.js'; import { initLogger, logger } from './logger.js'; +import { resolveProfilePaths } from './paths.js'; +import { openDb } from './db/index.js'; +import { NoteRepository } from './repository/NoteRepository.js'; +import { MediaStore } from './services/MediaStore.js'; +import { ContinuityService } from './services/ContinuityService.js'; +import { CaptureService } from './services/CaptureService.js'; +import { NotificationService } from './services/NotificationService.js'; +import { HotkeyService } from './services/HotkeyService.js'; +import { IntentService } from './services/IntentService.js'; +import { HealthChecker } from './services/HealthChecker.js'; +import { LocalOllamaProvider } from './ai/LocalOllamaProvider.js'; +import { AiWorker } from './ai/AiWorker.js'; +import { registerCaptureApi } from './ipc/captureApi.js'; +import { registerInboxApi, pushNoteUpdated } from './ipc/inboxApi.js'; +import { createInboxWindow, getInboxWindow } from './windows/inboxWindow.js'; +import { + createQuickCaptureWindow, showQuickCapture, getQuickCaptureWindow +} from './windows/quickCaptureWindow.js'; +import { createTray } from './tray.js'; +import { MediaGc } from './services/MediaGc.js'; -app.whenReady().then(() => { +app.whenReady().then(async () => { initLogger(); logger.info('app.start', { platform: process.platform, version: app.getVersion() }); + + const paths = resolveProfilePaths('default'); + const db = openDb(paths.dbFile); + const repo = new NoteRepository(db); + const store = new MediaStore(paths.profileDir); + const continuity = new ContinuityService(db); + const intent = new IntentService(repo); + + const provider = new LocalOllamaProvider({ + endpoint: process.env.INKLING_OLLAMA_ENDPOINT + }); + const health = new HealthChecker(provider); + void health.runOnce().then((h) => logger.info('ai.health', { ...h } as Record)); + + const worker = new AiWorker(repo, provider, { + onUpdate: (note) => pushNoteUpdated(getInboxWindow, note), + logger + }); + + const notify = new NotificationService({ + isSupported: () => Notification.isSupported(), + send: (body) => { + new Notification({ title: 'Inkling', body, silent: false }).show(); + } + }); + + const capture = new CaptureService(repo, store, { + enqueue: (id) => worker.enqueue(id), + celebrate: (id) => notify.celebrate(id) + }); + + registerCaptureApi(capture, getQuickCaptureWindow); + registerInboxApi({ + repo, continuity, capture, health, intent, + getInboxWindow + }); + + const hotkeys = new HotkeyService(); + const reg = hotkeys.register({ + accelerator: process.platform === 'darwin' ? 'Cmd+Shift+J' : 'Ctrl+Shift+J', + onTrigger: () => showQuickCapture() + }); + if (!reg.ok) logger.warn('hotkey.register.failed', { reason: reg.reason }); + createInboxWindow(); + createQuickCaptureWindow(); + createTray( + () => createInboxWindow(), + () => showQuickCapture() + ); + + await worker.loadFromDb(); + + const gc = new MediaGc(db, store); + void gc.run().then((r) => logger.info('media.gc', { ...r } as Record)); + app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createInboxWindow(); }); diff --git a/src/main/tray.ts b/src/main/tray.ts new file mode 100644 index 0000000..5c257a0 --- /dev/null +++ b/src/main/tray.ts @@ -0,0 +1,18 @@ +import { app, Tray, Menu, nativeImage } from 'electron'; + +let tray: Tray | null = null; + +export function createTray(showInbox: () => void, showCapture: () => void): Tray { + const icon = nativeImage.createEmpty(); + tray = new Tray(icon); + tray.setToolTip('Inkling'); + const menu = Menu.buildFromTemplate([ + { label: '구출한 메모 보기', click: showInbox }, + { label: '기억 구출하기', click: showCapture }, + { type: 'separator' }, + { label: '종료', click: () => { app.isQuitting = true; app.quit(); } } + ]); + tray.setContextMenu(menu); + tray.on('click', showInbox); + return tray; +}