import { contextBridge, ipcRenderer } from 'electron'; // Expose protected methods that allow the renderer process to use // ipcRenderer without exposing the entire object contextBridge.exposeInMainWorld('electronAPI', { // Projects projects: { create: (data: { name: string; description?: string; slug?: string }) => ipcRenderer.invoke('projects:create', data), update: (id: string, data: unknown) => ipcRenderer.invoke('projects:update', id, data), delete: (id: string) => ipcRenderer.invoke('projects:delete', id), deleteWithData: (id: string) => ipcRenderer.invoke('projects:deleteWithData', id), get: (id: string) => ipcRenderer.invoke('projects:get', id), getAll: () => ipcRenderer.invoke('projects:getAll'), getActive: () => ipcRenderer.invoke('projects:getActive'), setActive: (id: string) => ipcRenderer.invoke('projects:setActive', id), }, // Posts posts: { create: (data: unknown) => ipcRenderer.invoke('posts:create', data), update: (id: string, data: unknown) => ipcRenderer.invoke('posts:update', id, data), delete: (id: string) => ipcRenderer.invoke('posts:delete', id), get: (id: string) => ipcRenderer.invoke('posts:get', id), getAll: (options?: { limit?: number; offset?: number }) => ipcRenderer.invoke('posts:getAll', options), getByStatus: (status: string) => ipcRenderer.invoke('posts:getByStatus', status), publish: (id: string) => ipcRenderer.invoke('posts:publish', id), discard: (id: string) => ipcRenderer.invoke('posts:discard', id), hasPublishedVersion: (id: string) => ipcRenderer.invoke('posts:hasPublishedVersion', id), rebuildFromFiles: () => ipcRenderer.invoke('posts:rebuildFromFiles'), reindexText: () => ipcRenderer.invoke('posts:reindexText'), search: (query: string) => ipcRenderer.invoke('posts:search', query), filter: (filter: unknown) => ipcRenderer.invoke('posts:filter', filter), getTags: () => ipcRenderer.invoke('posts:getTags'), getCategories: () => ipcRenderer.invoke('posts:getCategories'), getByYearMonth: () => ipcRenderer.invoke('posts:getByYearMonth'), getTagsWithCounts: () => ipcRenderer.invoke('posts:getTagsWithCounts'), getCategoriesWithCounts: () => ipcRenderer.invoke('posts:getCategoriesWithCounts'), getDashboardStats: () => ipcRenderer.invoke('posts:getDashboardStats'), getLinksTo: (id: string) => ipcRenderer.invoke('posts:getLinksTo', id), getLinkedBy: (id: string) => ipcRenderer.invoke('posts:getLinkedBy', id), rebuildLinks: () => ipcRenderer.invoke('posts:rebuildLinks'), isSlugAvailable: (slug: string, excludePostId?: string) => ipcRenderer.invoke('posts:isSlugAvailable', slug, excludePostId), generateUniqueSlug: (title: string, excludePostId?: string) => ipcRenderer.invoke('posts:generateUniqueSlug', title, excludePostId), }, // Media media: { import: (sourcePath: string, metadata?: unknown) => ipcRenderer.invoke('media:import', sourcePath, metadata), importDialog: () => ipcRenderer.invoke('media:importDialog'), update: (id: string, data: unknown) => ipcRenderer.invoke('media:update', id, data), delete: (id: string) => ipcRenderer.invoke('media:delete', id), get: (id: string) => ipcRenderer.invoke('media:get', id), getUrl: (id: string) => ipcRenderer.invoke('media:getUrl', id), getFilePath: (id: string) => ipcRenderer.invoke('media:getFilePath', id), getAll: () => ipcRenderer.invoke('media:getAll'), rebuildFromFiles: () => ipcRenderer.invoke('media:rebuildFromFiles'), getThumbnail: (id: string, size?: 'small' | 'medium' | 'large') => ipcRenderer.invoke('media:getThumbnail', id, size), regenerateThumbnails: (id: string) => ipcRenderer.invoke('media:regenerateThumbnails', id), }, // Sync sync: { configure: (config: unknown) => ipcRenderer.invoke('sync:configure', config), start: (direction?: string) => ipcRenderer.invoke('sync:start', direction), getStatus: () => ipcRenderer.invoke('sync:getStatus'), isConfigured: () => ipcRenderer.invoke('sync:isConfigured'), getPendingCount: () => ipcRenderer.invoke('sync:getPendingCount'), getLog: (limit?: number) => ipcRenderer.invoke('sync:getLog', limit), stopAutoSync: () => ipcRenderer.invoke('sync:stopAutoSync'), }, // Dropbox File Sync dropbox: { configure: (config: unknown) => ipcRenderer.invoke('dropbox:configure', config), isConfigured: () => ipcRenderer.invoke('dropbox:isConfigured'), getStatus: () => ipcRenderer.invoke('dropbox:getStatus'), syncAll: () => ipcRenderer.invoke('dropbox:syncAll'), startWatching: () => ipcRenderer.invoke('dropbox:startWatching'), stopWatching: () => ipcRenderer.invoke('dropbox:stopWatching'), startPolling: () => ipcRenderer.invoke('dropbox:startPolling'), stopPolling: () => ipcRenderer.invoke('dropbox:stopPolling'), getConflicts: () => ipcRenderer.invoke('dropbox:getConflicts'), resolveConflict: (conflictId: string, resolution: string) => ipcRenderer.invoke('dropbox:resolveConflict', conflictId, resolution), getLastSyncTime: () => ipcRenderer.invoke('dropbox:getLastSyncTime'), }, // Tasks tasks: { getAll: () => ipcRenderer.invoke('tasks:getAll'), getRunning: () => ipcRenderer.invoke('tasks:getRunning'), cancel: (taskId: string) => ipcRenderer.invoke('tasks:cancel', taskId), clearCompleted: () => ipcRenderer.invoke('tasks:clearCompleted'), }, // App app: { getDataPaths: () => ipcRenderer.invoke('app:getDataPaths'), openFolder: (folderPath: string) => ipcRenderer.invoke('app:openFolder', folderPath), showItemInFolder: (itemPath: string) => ipcRenderer.invoke('app:showItemInFolder', itemPath), }, // Meta (tags, categories, and project metadata) meta: { getTags: () => ipcRenderer.invoke('meta:getTags'), getCategories: () => ipcRenderer.invoke('meta:getCategories'), addTag: (tag: string) => ipcRenderer.invoke('meta:addTag', tag), removeTag: (tag: string) => ipcRenderer.invoke('meta:removeTag', tag), addCategory: (category: string) => ipcRenderer.invoke('meta:addCategory', category), removeCategory: (category: string) => ipcRenderer.invoke('meta:removeCategory', category), syncOnStartup: () => ipcRenderer.invoke('meta:syncOnStartup'), getProjectMetadata: () => ipcRenderer.invoke('meta:getProjectMetadata'), setProjectMetadata: (metadata: { name: string; description?: string }) => ipcRenderer.invoke('meta:setProjectMetadata', metadata), updateProjectMetadata: (updates: { name?: string; description?: string }) => ipcRenderer.invoke('meta:updateProjectMetadata', updates), }, // Tag Management (advanced tag operations) tags: { getAll: () => ipcRenderer.invoke('tags:getAll'), getWithCounts: () => ipcRenderer.invoke('tags:getWithCounts'), get: (id: string) => ipcRenderer.invoke('tags:get', id), getByName: (name: string) => ipcRenderer.invoke('tags:getByName', name), create: (data: { name: string; color?: string }) => ipcRenderer.invoke('tags:create', data), update: (id: string, data: { name?: string; color?: string | null }) => ipcRenderer.invoke('tags:update', id, data), delete: (id: string) => ipcRenderer.invoke('tags:delete', id), merge: (sourceTagIds: string[], targetTagId: string) => ipcRenderer.invoke('tags:merge', sourceTagIds, targetTagId), rename: (id: string, newName: string) => ipcRenderer.invoke('tags:rename', id, newName), getPostsWithTag: (tagId: string) => ipcRenderer.invoke('tags:getPostsWithTag', tagId), syncFromPosts: () => ipcRenderer.invoke('tags:syncFromPosts'), }, // Event listeners on: (channel: string, callback: (...args: unknown[]) => void) => { const subscription = (_event: Electron.IpcRendererEvent, ...args: unknown[]) => callback(...args); ipcRenderer.on(channel, subscription); return () => ipcRenderer.removeListener(channel, subscription); }, once: (channel: string, callback: (...args: unknown[]) => void) => { ipcRenderer.once(channel, (_event, ...args) => callback(...args)); }, }); // Type definitions for the exposed API export interface ElectronAPI { projects: { create: (data: { name: string; description?: string; slug?: string }) => Promise; update: (id: string, data: unknown) => Promise; delete: (id: string) => Promise; get: (id: string) => Promise; getAll: () => Promise; getActive: () => Promise; setActive: (id: string) => Promise; }; posts: { create: (data: unknown) => Promise; update: (id: string, data: unknown) => Promise; delete: (id: string) => Promise; get: (id: string) => Promise; getAll: () => Promise; getByStatus: (status: string) => Promise; publish: (id: string) => Promise; unpublish: (id: string) => Promise; rebuildFromFiles: () => Promise; search: (query: string) => Promise; filter: (filter: unknown) => Promise; getTags: () => Promise; getCategories: () => Promise; getByYearMonth: () => Promise<{ year: number; month: number; count: number }[]>; getTagsWithCounts: () => Promise<{ tag: string; count: number }[]>; getCategoriesWithCounts: () => Promise<{ category: string; count: number }[]>; getDashboardStats: () => Promise<{ totalPosts: number; draftCount: number; publishedCount: number; archivedCount: number }>; getLinksTo: (id: string) => Promise<{ id: string; title: string; slug: string }[]>; getLinkedBy: (id: string) => Promise<{ id: string; title: string; slug: string }[]>; rebuildLinks: () => Promise; }; media: { import: (sourcePath: string, metadata?: unknown) => Promise; importDialog: () => Promise; update: (id: string, data: unknown) => Promise; delete: (id: string) => Promise; get: (id: string) => Promise; getAll: () => Promise; rebuildFromFiles: () => Promise; }; sync: { configure: (config: unknown) => Promise; start: (direction?: string) => Promise; getStatus: () => Promise; isConfigured: () => Promise; getPendingCount: () => Promise<{ posts: number; media: number }>; getLog: (limit?: number) => Promise; stopAutoSync: () => Promise; }; dropbox: { configure: (config: unknown) => Promise; isConfigured: () => Promise; getStatus: () => Promise; syncAll: () => Promise; startWatching: () => Promise; stopWatching: () => Promise; startPolling: () => Promise; stopPolling: () => Promise; getConflicts: () => Promise; resolveConflict: (conflictId: string, resolution: string) => Promise; getLastSyncTime: () => Promise; }; tasks: { getAll: () => Promise; getRunning: () => Promise; cancel: (taskId: string) => Promise; clearCompleted: () => Promise; }; app: { getDataPaths: () => Promise<{ database: string; posts: string; media: string }>; openFolder: (folderPath: string) => Promise; showItemInFolder: (itemPath: string) => Promise; }; meta: { getTags: () => Promise; getCategories: () => Promise; addTag: (tag: string) => Promise; removeTag: (tag: string) => Promise; addCategory: (category: string) => Promise; removeCategory: (category: string) => Promise; syncOnStartup: () => Promise<{ tags: string[]; categories: string[] }>; }; on: (channel: string, callback: (...args: unknown[]) => void) => () => void; once: (channel: string, callback: (...args: unknown[]) => void) => void; } declare global { interface Window { electronAPI: ElectronAPI; } }