From d4ad2f8d15a9e402b3181b11f2eef7864dd59a17 Mon Sep 17 00:00:00 2001 From: altair823 Date: Sat, 25 Apr 2026 12:14:32 +0900 Subject: [PATCH] feat(ipc): inbox handlers with v0.2 setIntent/dismissIntent/continuity Task 21 of the slice plan. registerInboxApi binds every InboxApi method on the main side: inbox:list (paginated), inbox:updateAi (delegates to NoteRepository.updateUserAiFields which flips the *_edited_by_user flags), inbox:delete (routes through CaptureService so the media dir gets cleaned up), inbox:setIntent / inbox:dismissIntent (route through IntentService for input validation), inbox:continuity (Weekly Continuity snapshot), inbox:pendingCount, inbox:ollamaStatus (reads the cached HealthChecker.lastStatus()). pushNoteUpdated helper is exported so AiWorker.onUpdate (wired in Task 30) can fan note:updated events to the inbox renderer. Plan deviation: HealthChecker.ts was pulled forward from Task 29 because Task 21 imports it at compile time. The class is small and final; Task 29 commit only ships OllamaBanner.tsx. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/main/ipc/inboxApi.ts | 54 ++++++++++++++++++++++++++++++ src/main/services/HealthChecker.ts | 13 +++++++ 2 files changed, 67 insertions(+) create mode 100644 src/main/ipc/inboxApi.ts create mode 100644 src/main/services/HealthChecker.ts diff --git a/src/main/ipc/inboxApi.ts b/src/main/ipc/inboxApi.ts new file mode 100644 index 0000000..0fc50eb --- /dev/null +++ b/src/main/ipc/inboxApi.ts @@ -0,0 +1,54 @@ +import { ipcMain, BrowserWindow } from 'electron'; +import type { NoteRepository } from '../repository/NoteRepository.js'; +import type { ContinuityService } from '../services/ContinuityService.js'; +import type { CaptureService } from '../services/CaptureService.js'; +import type { HealthChecker } from '../services/HealthChecker.js'; +import type { IntentService } from '../services/IntentService.js'; +import type { Note } from '@shared/types'; + +export interface InboxIpcDeps { + repo: NoteRepository; + continuity: ContinuityService; + capture: CaptureService; + health: HealthChecker; + intent: IntentService; + getInboxWindow: () => BrowserWindow | null; +} + +export function registerInboxApi(deps: InboxIpcDeps): void { + ipcMain.handle('inbox:list', (_e, opts: { limit: number; cursor?: string }) => + deps.repo.list(opts) + ); + + ipcMain.handle( + 'inbox:updateAi', + (_e, arg: { noteId: string; fields: { title?: string; summary?: string; tags?: string[] } }) => { + deps.repo.updateUserAiFields(arg.noteId, arg.fields); + } + ); + + ipcMain.handle('inbox:delete', async (_e, noteId: string) => { + await deps.capture.deleteNote(noteId); + }); + + ipcMain.handle( + 'inbox:setIntent', + (_e, arg: { noteId: string; text: string }) => { + deps.intent.setIntent(arg.noteId, arg.text); + } + ); + + ipcMain.handle('inbox:dismissIntent', (_e, noteId: string) => { + deps.intent.dismissIntent(noteId); + }); + + ipcMain.handle('inbox:continuity', () => deps.continuity.get()); + ipcMain.handle('inbox:pendingCount', () => deps.repo.getPendingCount()); + ipcMain.handle('inbox:ollamaStatus', () => deps.health.lastStatus()); +} + +export function pushNoteUpdated(getWin: () => BrowserWindow | null, note: Note): void { + const w = getWin(); + if (!w || w.isDestroyed()) return; + w.webContents.send('note:updated', note); +} diff --git a/src/main/services/HealthChecker.ts b/src/main/services/HealthChecker.ts new file mode 100644 index 0000000..a8567a2 --- /dev/null +++ b/src/main/services/HealthChecker.ts @@ -0,0 +1,13 @@ +import type { InferenceProvider, HealthResult } from '../ai/InferenceProvider.js'; + +export class HealthChecker { + private last: HealthResult = { ok: true }; + constructor(private provider: InferenceProvider) {} + + async runOnce(): Promise { + this.last = await this.provider.healthCheck(); + return this.last; + } + + lastStatus(): HealthResult { return this.last; } +}