import { ipcMain } from "electron"; import { NATIVE_BRIDGE_CHANNEL, NATIVE_BRIDGE_VERSION, type NativeBridgeErrorCode, type NativeBridgeRequest, type NativeBridgeResponse, type NativePlatform, type ProjectFileResult, type ProjectPathResult, } from "../../src/native/contracts"; import type { CursorTelemetryLoadResult } from "../native-bridge/cursor/adapter"; import { TelemetryCursorAdapter } from "../native-bridge/cursor/telemetryCursorAdapter"; import { CursorService } from "../native-bridge/services/cursorService"; import { ProjectService } from "../native-bridge/services/projectService"; import { SystemService } from "../native-bridge/services/systemService"; import { NativeBridgeStateStore } from "../native-bridge/store"; export interface NativeBridgeContext { getPlatform: () => NodeJS.Platform; getCurrentProjectPath: () => string | null; getCurrentVideoPath: () => string | null; saveProjectFile: ( projectData: unknown, suggestedName?: string, existingProjectPath?: string, ) => Promise; loadProjectFile: () => Promise; loadCurrentProjectFile: () => Promise; setCurrentVideoPath: (path: string) => ProjectPathResult | Promise; getCurrentVideoPathResult: () => ProjectPathResult; clearCurrentVideoPath: () => ProjectPathResult; resolveAssetBasePath: () => string | null; resolveVideoPath: (videoPath?: string | null) => string | null; loadCursorRecordingData: ( videoPath: string, ) => Promise; loadCursorTelemetry: (videoPath: string) => Promise; } function normalizePlatform(platform: NodeJS.Platform): NativePlatform { if (platform === "darwin" || platform === "win32") { return platform; } return "linux"; } function createMeta(requestId?: string) { return { version: NATIVE_BRIDGE_VERSION, requestId: requestId || `native-${Date.now()}`, timestampMs: Date.now(), } as const; } function createSuccessResponse(requestId: string | undefined, data: TData) { return { ok: true, data, meta: createMeta(requestId), } satisfies NativeBridgeResponse; } function createErrorResponse( requestId: string | undefined, code: NativeBridgeErrorCode, message: string, retryable = false, ) { return { ok: false, error: { code, message, retryable, }, meta: createMeta(requestId), } satisfies NativeBridgeResponse; } function isBridgeRequest(value: unknown): value is NativeBridgeRequest { if (!value || typeof value !== "object") { return false; } const candidate = value as Partial; return typeof candidate.domain === "string" && typeof candidate.action === "string"; } export function registerNativeBridgeHandlers(context: NativeBridgeContext) { ipcMain.removeHandler(NATIVE_BRIDGE_CHANNEL); const platform = normalizePlatform(context.getPlatform()); const store = new NativeBridgeStateStore(platform); const projectService = new ProjectService({ store, getCurrentProjectPath: context.getCurrentProjectPath, getCurrentVideoPath: context.getCurrentVideoPath, saveProjectFile: context.saveProjectFile, loadProjectFile: context.loadProjectFile, loadCurrentProjectFile: context.loadCurrentProjectFile, setCurrentVideoPath: context.setCurrentVideoPath, getCurrentVideoPathResult: context.getCurrentVideoPathResult, clearCurrentVideoPath: context.clearCurrentVideoPath, }); const cursorService = new CursorService({ store, adapter: new TelemetryCursorAdapter({ loadRecordingData: context.loadCursorRecordingData, resolveVideoPath: context.resolveVideoPath, loadTelemetry: context.loadCursorTelemetry, }), }); const systemService = new SystemService({ store, getPlatform: () => platform, getAssetBasePath: context.resolveAssetBasePath, getCursorCapabilities: () => cursorService.getCapabilities(), }); ipcMain.handle(NATIVE_BRIDGE_CHANNEL, async (_, request: unknown) => { if (!isBridgeRequest(request)) { return createErrorResponse(undefined, "INVALID_REQUEST", "Invalid native bridge request."); } const requestId = request.requestId; const domain = request.domain as string; try { switch (request.domain) { case "system": { const action = request.action as string; switch (request.action) { case "getPlatform": return createSuccessResponse(requestId, systemService.getPlatform()); case "getAssetBasePath": return createSuccessResponse(requestId, systemService.getAssetBasePath()); case "getCapabilities": return createSuccessResponse(requestId, await systemService.getCapabilities()); default: return createErrorResponse( requestId, "UNSUPPORTED_ACTION", `Unsupported system action: ${action}`, ); } } case "project": { const action = request.action as string; switch (request.action) { case "getCurrentContext": return createSuccessResponse(requestId, projectService.getCurrentContext()); case "saveProjectFile": return createSuccessResponse( requestId, await projectService.saveProjectFile( request.payload.projectData, request.payload.suggestedName, request.payload.existingProjectPath, ), ); case "loadProjectFile": return createSuccessResponse(requestId, await projectService.loadProjectFile()); case "loadCurrentProjectFile": return createSuccessResponse( requestId, await projectService.loadCurrentProjectFile(), ); case "setCurrentVideoPath": return createSuccessResponse( requestId, await projectService.setCurrentVideoPath(request.payload.path), ); case "getCurrentVideoPath": return createSuccessResponse(requestId, projectService.getCurrentVideoPath()); case "clearCurrentVideoPath": return createSuccessResponse(requestId, projectService.clearCurrentVideoPath()); default: return createErrorResponse( requestId, "UNSUPPORTED_ACTION", `Unsupported project action: ${action}`, ); } } case "cursor": { const action = request.action as string; switch (request.action) { case "getCapabilities": return createSuccessResponse(requestId, await cursorService.getCapabilities()); case "getTelemetry": return createSuccessResponse( requestId, await cursorService.getTelemetry(request.payload?.videoPath), ); case "getRecordingData": return createSuccessResponse( requestId, await cursorService.getRecordingData(request.payload?.videoPath), ); default: return createErrorResponse( requestId, "UNSUPPORTED_ACTION", `Unsupported cursor action: ${action}`, ); } } default: return createErrorResponse( requestId, "UNSUPPORTED_ACTION", `Unsupported bridge domain: ${domain}`, ); } } catch (error) { return createErrorResponse( requestId, "INTERNAL_ERROR", error instanceof Error ? error.message : "Unknown native bridge error.", true, ); } }); }