// Type definitions for the Electron API exposed via preload export interface ImportExecuteResult { taskId: string; totalItems: number; } export interface ImportExecutionProgress { taskId: string; phase: string; current: number; total: number; detail?: string; eta?: number; } export interface ImportCompleteResult { taskId: string; success: boolean; posts: { imported: number; skipped: number; errors: number }; media: { imported: number; skipped: number; errors: number }; pages: { imported: number; skipped: number; errors: number }; tags: { created: number; skipped: number }; } export interface ImportDefinitionData { id: string; projectId: string; name: string; wxrFilePath: string | null; uploadsFolderPath: string | null; lastAnalysisResult: unknown | null; createdAt: string; updatedAt: string; } export interface ProjectMetadata { name: string; description?: string; dataPath?: string; publicUrl?: string; mainLanguage?: string; defaultAuthor?: string; maxPostsPerPage?: number; blogmarkCategory?: string; pythonRuntimeMode?: 'webworker' | 'main-thread'; picoTheme?: import('./picoThemes').PicoThemeName; categoryMetadata?: Record; categorySettings?: Record; } export interface CategoryRenderSettings { renderInLists: boolean; showTitle: boolean; postTemplateSlug?: string; listTemplateSlug?: string; } export interface CategoryMetadata extends CategoryRenderSettings { title: string; } export interface PublishingPreferences { sshHost: string; sshUser: string; sshRemotePath: string; sshMode: 'scp' | 'rsync'; } export interface ProjectData { id: string; name: string; slug: string; description?: string; dataPath?: string; isActive: boolean; createdAt: string; updatedAt: string; } export interface PostData { id: string; projectId: string; title: string; slug: string; excerpt?: string; content: string; status: 'draft' | 'published' | 'archived'; author?: string; createdAt: string; updatedAt: string; publishedAt?: string; tags: string[]; categories: string[]; templateSlug?: string; } export interface PostFilter { status?: 'draft' | 'published' | 'archived'; tags?: string[]; categories?: string[]; year?: number; month?: number; from?: string; to?: string; } export interface SearchResult { id: string; title: string; slug: string; excerpt?: string; } export interface MediaData { id: string; projectId: string; filename: string; originalName: string; mimeType: string; size: number; width?: number; height?: number; title?: string; alt?: string; caption?: string; author?: string; createdAt: string; updatedAt: string; tags: string[]; } export interface MediaFilter { tags?: string[]; year?: number; month?: number; } export interface MediaSearchResult { id: string; originalName: string; title?: string; mimeType: string; createdAt: string; } export type ScriptKind = 'macro' | 'utility' | 'transform'; export interface ScriptData { id: string; projectId: string; slug: string; title: string; kind: ScriptKind; entrypoint: string; enabled: boolean; version: number; filePath: string; content: string; createdAt: string; updatedAt: string; } export type TemplateKind = 'post' | 'list' | 'not-found' | 'partial'; export interface TemplateData { id: string; projectId: string; slug: string; title: string; kind: TemplateKind; enabled: boolean; version: number; filePath: string; content: string; createdAt: string; updatedAt: string; } export interface TemplateDeleteResult { deleted: boolean; references?: { postIds: string[]; tagIds: string[] }; } export interface TaskProgress { taskId: string; name: string; status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'; progress: number; message: string; startTime: string; endTime?: string; error?: string; groupId?: string; groupName?: string; } export interface PaginatedPostsResult { items: PostData[]; hasMore: boolean; total: number; } export interface DashboardStats { totalPosts: number; draftCount: number; publishedCount: number; archivedCount: number; } export interface TagCount { tag: string; count: number; } export interface CategoryCount { category: string; count: number; } export interface TagData { id: string; projectId: string; name: string; color?: string; postTemplateSlug?: string; createdAt: string; updatedAt: string; } export interface TagWithCount { name: string; color: string | null; count: number; } export interface DeleteTagResult { success: boolean; postsUpdated: number; } export interface MergeTagsResult { success: boolean; postsUpdated: number; tagsDeleted: number; targetTag: string; } export interface RenameTagResult { success: boolean; postsUpdated: number; oldName: string; newName: string; } export interface SyncTagsResult { discovered: number; added: string[]; } export interface GitAvailability { gitFound: boolean; version?: string; } export interface GitRepoState { isRepo: boolean; rootPath?: string; currentBranch?: string; hasRemote: boolean; } export type GitFileStatus = 'untracked' | 'modified' | 'deleted' | 'renamed' | 'staged'; export interface GitStatusFile { path: string; status: GitFileStatus; previousPath?: string; } export interface GitStatusCounts { untracked: number; modified: number; deleted: number; renamed: number; staged: number; total: number; } export interface GitStatusDto { files: GitStatusFile[]; counts: GitStatusCounts; } export interface GitDiffDto { filePath: string; patch: string; } export interface GitDiffContentDto { filePath: string; original: string; modified: string; } export interface GitCommitDiffContentDto { commitHash: string; original: string; modified: string; files: GitCommitDiffFileDto[]; } export interface GitCommitDiffFileDto { filePath: string; original: string; modified: string; } export type GitHistorySyncStatus = 'both' | 'local-only' | 'remote-only'; export interface GitHistoryEntry { hash: string; shortHash: string; date: string; subject: string; author: string; syncStatus?: GitHistorySyncStatus; } export interface GitRemoteStateDto { localBranch: string | null; upstreamBranch: string | null; hasUpstream: boolean; ahead: number; behind: number; } export type GitInitPhase = | 'checking-git' | 'initializing-repo' | 'configuring-lfs' | 'tracking-lfs-patterns' | 'staging-files' | 'creating-initial-commit' | 'configuring-remote' | 'completed' | 'failed'; export interface GitInitProgress { phase: GitInitPhase; progress: number; message: string; detail?: string; } export interface GitInitResult { success: boolean; error?: string; code?: 'git-missing' | 'git-lfs-missing' | 'init-failed' | 'remote-failed' | 'commit-failed'; } export interface GitIgnoreEnsureResult { updated: boolean; created: boolean; addedEntries: string[]; } export interface GitLfsPruneResult { success: boolean; dryRun: boolean; verifyRemote: boolean; recentCommitsToKeep: number; output?: string; error?: string; } export interface GitActionResult { success: boolean; code?: 'auth-required' | 'conflict' | 'network' | 'action-failed'; error?: string; guidance?: string[]; } // Post-Media Link types export interface MediaLinkData { id: string; projectId: string; postId: string; mediaId: string; sortOrder: number; createdAt: string; } // Chat/AI types export interface ChatConversation { id: string; title: string; model?: string; createdAt: string; updatedAt: string; } export interface ChatMessage { id: string; conversationId: string; role: 'user' | 'assistant' | 'system' | 'tool'; content: string; toolCallId?: string; toolCalls?: string; createdAt: string; } export interface ChatModel { id: string; name: string; provider?: string; } export interface ChatReadyStatus { ready: boolean; error?: string; backend?: string; } export interface ChatApiKeyStatus { hasKey: boolean; maskedKey: string; } export interface ChatStreamDelta { conversationId: string; delta: string; } export interface ChatToolCall { conversationId: string; toolCall: { name: string; arguments: Record; }; } export interface ChatToolResult { conversationId: string; result: unknown; } export interface ChatTitleUpdate { conversationId: string; title: string; } export interface ChatTokenUsage { conversationId: string; inputTokens: number; outputTokens: number; cacheReadTokens: number; cacheWriteTokens: number; totalTokens: number; cumulativeInputTokens: number; cumulativeOutputTokens: number; cumulativeCacheReadTokens: number; cumulativeCacheWriteTokens: number; cumulativeTotalTokens: number; } export interface ChatSendMetadata { surface?: 'tab' | 'sidebar'; } // A2UI types imported for use in ElectronAPI and re-exported for renderer import type { A2UIServerMessage, A2UIClientAction } from '../a2ui/types'; export type { A2UIServerMessage, A2UIClientAction }; export interface SiteValidationReport { sitemapPath: string; sitemapChanged: boolean; missingUrlPaths: string[]; extraUrlPaths: string[]; updatedPostUrlPaths: string[]; expectedUrlCount: number; existingHtmlUrlCount: number; } export interface SiteValidationApplyResult { renderedUrlCount: number; deletedUrlCount: number; removedEmptyDirCount: number; } export interface CalendarRegenerationResult { calendarPath: string; changed: boolean; } export type MenuItemKind = 'page' | 'submenu' | 'category-archive' | 'home'; export interface MenuItemData { id: string; title: string; kind: MenuItemKind; pageId?: string; pageSlug?: string; categoryName?: string; children: MenuItemData[]; } export interface MenuDocument { items: MenuItemData[]; } export interface ElectronAPI { git: { checkAvailability: () => Promise; getRepoState: (projectPath: string) => Promise; getStatus: (projectPath: string) => Promise; getDiff: (projectPath: string, filePath: string) => Promise; getDiffContent: (projectPath: string, filePath: string) => Promise; getCommitDiffContent: (projectPath: string, commitHash: string) => Promise; getHistory: (projectPath: string, limit?: number) => Promise; getFileHistory: (projectPath: string, filePath: string, limit?: number) => Promise; getRemoteState: (projectPath: string) => Promise; fetch: (projectPath: string) => Promise; pull: (projectPath: string) => Promise; push: (projectPath: string) => Promise; commitAll: (projectPath: string, message: string) => Promise; ensureGitignore: (projectPath: string) => Promise; pruneLfs: (projectPath: string, options?: { dryRun?: boolean; verifyRemote?: boolean; recentCommitsToKeep?: number }) => Promise; init: (projectPath: string, remoteUrl?: string) => Promise; onInitProgress: (callback: (progress: GitInitProgress) => void) => () => void; }; projects: { create: (data: { name: string; description?: string; slug?: string; dataPath?: string }) => Promise; update: (id: string, data: Partial) => Promise; delete: (id: string) => Promise; deleteWithData: (id: string) => Promise; get: (id: string) => Promise; getAll: () => Promise; getActive: () => Promise; setActive: (id: string) => Promise; }; posts: { create: (data: Partial) => Promise; update: (id: string, data: Partial) => Promise; delete: (id: string) => Promise; get: (id: string) => Promise; getPreviewUrl: (id: string, options?: { draft?: boolean }) => Promise; getAll: (options?: { limit?: number; offset?: number }) => Promise; getByStatus: (status: string) => Promise; publish: (id: string) => Promise; discard: (id: string) => Promise; hasPublishedVersion: (id: string) => Promise; rebuildFromFiles: () => Promise; reindexText: () => Promise; search: (query: string) => Promise; filter: (filter: PostFilter) => Promise; getTags: () => Promise; getCategories: () => Promise; getByYearMonth: () => Promise<{ year: number; month: number; count: number }[]>; getDashboardStats: () => Promise; getTagsWithCounts: () => Promise; getCategoriesWithCounts: () => Promise; getLinksTo: (id: string) => Promise; getLinkedBy: (id: string) => Promise; rebuildLinks: () => Promise; isSlugAvailable: (slug: string, excludePostId?: string) => Promise; generateUniqueSlug: (title: string, excludePostId?: string) => Promise; }; media: { import: (sourcePath: string, metadata?: Partial) => Promise; importDialog: () => Promise; update: (id: string, data: Partial) => Promise; replaceFile: (id: string, newSourcePath: string) => Promise; replaceFileDialog: (id: string) => Promise; delete: (id: string) => Promise; get: (id: string) => Promise; getUrl: (id: string) => Promise; getFilePath: (id: string) => Promise; getAll: () => Promise; rebuildFromFiles: () => Promise; reindexText: () => Promise; getThumbnail: (id: string, size?: 'small' | 'medium' | 'large') => Promise; regenerateThumbnails: (id: string) => Promise | null>; regenerateMissingThumbnails: () => Promise<{ processed: number; generated: number; failed: number }>; filter: (filter: MediaFilter) => Promise; search: (query: string) => Promise; getByYearMonth: () => Promise<{ year: number; month: number; count: number }[]>; getTags: () => Promise; getTagsWithCounts: () => Promise; }; scripts: { create: (data: { title: string; kind: ScriptKind; content: string; slug?: string; entrypoint?: string; enabled?: boolean; }) => Promise; update: (id: string, data: { title?: string; kind?: ScriptKind; content?: string; slug?: string; entrypoint?: string; enabled?: boolean; }) => Promise; delete: (id: string) => Promise; get: (id: string) => Promise; getAll: () => Promise; /** Internal: editor macro plugin helper. Not exposed via Python API contract. */ getEnabledMacroSlugs: () => Promise; rebuildFromFiles: () => Promise; }; templates: { create: (data: { title: string; kind: TemplateKind; content: string; slug?: string; enabled?: boolean; }) => Promise; update: (id: string, data: { title?: string; kind?: TemplateKind; content?: string; slug?: string; enabled?: boolean; }) => Promise; delete: (id: string, options?: { force?: boolean }) => Promise; get: (id: string) => Promise; getAll: () => Promise; getEnabledByKind: (kind: TemplateKind) => Promise; validate: (content: string) => Promise<{ valid: boolean; errors: string[] }>; rebuildFromFiles: () => Promise; }; postMedia: { link: (postId: string, mediaId: string) => Promise; unlink: (postId: string, mediaId: string) => Promise; linkMany: (postId: string, mediaIds: string[]) => Promise<{ linked: string[]; skipped: string[] }>; unlinkMany: (postId: string, mediaIds: string[]) => Promise<{ unlinked: string[] }>; getForPost: (postId: string) => Promise; getForMedia: (mediaId: string) => Promise; getMediaDataForPost: (postId: string) => Promise>; reorder: (postId: string, mediaIds: string[]) => Promise; isLinked: (postId: string, mediaId: string) => Promise; import: (postId: string, filePath: string) => Promise; rebuild: () => Promise; }; sync: { checkAvailability: () => Promise<{ gitFound: boolean; version?: string }>; getRepoState: () => Promise<{ isRepo: boolean; rootPath?: string; currentBranch?: string; hasRemote: boolean }>; getStatus: () => Promise<{ files: Array<{ path: string; status: string; previousPath?: string }>; counts: { untracked: number; modified: number; deleted: number; renamed: number; staged: number } }>; getHistory: (limit?: number) => Promise>; getRemoteState: () => Promise<{ localBranch: string | null; upstreamBranch: string | null; hasUpstream: boolean; ahead: number; behind: number }>; fetch: () => Promise<{ success: boolean; code?: string; error?: string; guidance?: string[] }>; pull: () => Promise<{ success: boolean; code?: string; error?: string; guidance?: string[] }>; push: () => Promise<{ success: boolean; code?: string; error?: string; guidance?: string[] }>; commitAll: (message: string) => Promise<{ success: boolean; code?: string; error?: string; guidance?: string[] }>; }; tasks: { getAll: () => Promise; getRunning: () => Promise; cancel: (taskId: string) => Promise; clearCompleted: () => Promise; }; app: { getDataPaths: () => Promise<{ database: string; posts: string; media: string }>; getSystemLanguage: () => Promise; getTitleBarMetrics: () => Promise<{ macosLeftInset: number } | null>; openFolder: (folderPath: string) => Promise; showItemInFolder: (itemPath: string) => Promise; selectFolder: (title?: string) => Promise; getDefaultProjectPath: (projectId: string) => Promise; readProjectMetadata: (folderPath: string) => Promise<{ name?: string; description?: string; publicUrl?: string; mainLanguage?: string } | null>; getBlogmarkBookmarklet: () => Promise; copyToClipboard: (text: string) => Promise; notifyRendererReady: () => Promise; setPreviewPostTarget: (postId: string | null) => Promise; triggerMenuAction: (action: 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[]; projectMetadata: ProjectMetadata | null }>; getProjectMetadata: () => Promise; setProjectMetadata: (metadata: { name: string; description?: string }) => Promise; updateProjectMetadata: (updates: { name?: string; description?: string; dataPath?: string; publicUrl?: string; mainLanguage?: string; defaultAuthor?: string; maxPostsPerPage?: number; blogmarkCategory?: string; pythonRuntimeMode?: 'webworker' | 'main-thread'; picoTheme?: import('./picoThemes').PicoThemeName; categoryMetadata?: Record; categorySettings?: Record }) => Promise; getPublishingPreferences: () => Promise; setPublishingPreferences: (prefs: PublishingPreferences) => Promise; clearPublishingPreferences: () => Promise; }; tags: { getAll: () => Promise; getWithCounts: () => Promise; get: (id: string) => Promise; getByName: (name: string) => Promise; create: (data: { name: string; color?: string }) => Promise; update: (id: string, data: { name?: string; color?: string | null; postTemplateSlug?: string | null }) => Promise; delete: (id: string) => Promise; merge: (sourceTagIds: string[], targetTagId: string) => Promise; rename: (id: string, newName: string) => Promise; getPostsWithTag: (tagId: string) => Promise; syncFromPosts: () => Promise; }; import: { selectAndAnalyze: (uploadsFolder?: string) => Promise; analyzeFile: (filePath: string, uploadsFolder?: string) => Promise; selectUploadsFolder: () => Promise; execute: (reportJson: string, uploadsFolder?: string) => Promise; onProgress: (callback: (data: { step: string; detail?: string }) => void) => () => void; onExecutionProgress: (callback: (data: ImportExecutionProgress) => void) => () => void; onComplete: (callback: (data: ImportCompleteResult) => void) => () => void; }; importDefinitions: { create: (name?: string) => Promise; get: (id: string) => Promise; getAll: () => Promise; update: (id: string, updates: Partial>) => Promise; delete: (id: string) => Promise; onNameUpdated: (callback: (data: { definitionId: string; name: string }) => void) => () => void; }; metadataDiff: { getStats: () => Promise<{ totalPosts: number; publishedPosts: number; draftPosts: number; totalMedia: number; }>; scan: () => Promise<{ totalScanned: number; postsWithDifferences: number; differences: Array<{ postId: string; title: string; slug: string; filePath?: string; hasDifferences: boolean; differences: Record; }>; groups: Array<{ field: string; label: string; posts: Array<{ postId: string; title: string; slug: string; dbValue: unknown; fileValue: unknown; }>; }>; }>; syncDbToFile: (postIds: string[], groupLabel: string) => Promise<{ success: number; failed: number }>; syncFileToDb: (postIds: string[], field: string, groupLabel: string) => Promise<{ success: number; failed: number }>; }; blog: { generateSitemap: () => Promise<{ path: string; urlCount: number; postCount: number; tagCount: number; categoryCount: number; archiveCount: number; pagesGenerated: number; }>; validateSite: () => Promise; applyValidation: (report: SiteValidationReport) => Promise; regenerateCalendar: () => Promise; }; publish: { uploadSite: (credentials: { sshHost: string; sshUser: string; sshRemotePath: string; sshMode: 'scp' | 'rsync'; }) => Promise<{ htmlFilesUploaded: number; thumbnailFilesUploaded: number; mediaFilesUploaded: number; filesSkipped: number; }>; }; menu: { get: () => Promise; save: (menu: MenuDocument) => Promise; }; chat: { // API Key Management checkReady: () => Promise; validateApiKey: (apiKey: string) => Promise<{ isValid: boolean; models: ChatModel[] }>; setApiKey: (apiKey: string) => Promise<{ success: boolean; error?: string }>; getApiKey: () => Promise; // Settings getAvailableModels: () => Promise<{ success: boolean; models?: ChatModel[]; selectedModel?: string; error?: string }>; setDefaultModel: (modelId: string) => Promise<{ success: boolean; error?: string }>; getSystemPrompt: () => Promise<{ success: boolean; prompt?: string; error?: string }>; setSystemPrompt: (prompt: string) => Promise<{ success: boolean; error?: string }>; // Conversations getConversations: () => Promise; createConversation: (title?: string, model?: string) => Promise; getConversation: (id: string) => Promise; updateConversation: (id: string, updates: { title?: string; model?: string }) => Promise; deleteConversation: (id: string) => Promise; // Messaging sendMessage: (conversationId: string, message: string, metadata?: ChatSendMetadata) => Promise<{ success: boolean; message?: string; error?: string }>; addSystemEvent: (conversationId: string, content: string) => Promise<{ success: boolean; error?: string }>; abortMessage: (conversationId: string) => Promise; getHistory: (conversationId: string) => Promise; clearMessages: (conversationId: string) => Promise; setConversationModel: (conversationId: string, modelId: string) => Promise; // Taxonomy Analysis analyzeTaxonomy: (categories: Array<{ name: string; slug: string; existsInProject: boolean }>, tags: Array<{ name: string; slug: string; existsInProject: boolean }>, modelId: string) => Promise<{ success: boolean; categoryMappings?: Record; tagMappings?: Record; error?: string }>; // Media Analysis analyzeMediaImage: (mediaId: string, language?: string) => Promise<{ success: boolean; title?: string; alt?: string; caption?: string; error?: string }>; // Event listeners for streaming/progress onStreamDelta: (callback: (data: ChatStreamDelta) => void) => () => void; onToolCall: (callback: (data: ChatToolCall) => void) => () => void; onToolResult: (callback: (data: ChatToolResult) => void) => () => void; onTitleUpdated: (callback: (data: ChatTitleUpdate) => void) => () => void; onTokenUsage: (callback: (data: ChatTokenUsage) => void) => () => void; // A2UI streaming onA2UIMessage: (callback: (data: { conversationId: string; message: A2UIServerMessage }) => void) => () => void; dispatchA2UIAction: (action: A2UIClientAction) => Promise<{ success: boolean; error?: string }>; }; on: (channel: string, callback: (...args: unknown[]) => void) => () => void; once: (channel: string, callback: (...args: unknown[]) => void) => void; }