import { dialog } from 'electron'; import { getPostEngine } from '../engine/PostEngine'; import { getProjectEngine } from '../engine/ProjectEngine'; import { getMetaEngine } from '../engine/MetaEngine'; import { getMediaEngine } from '../engine/MediaEngine'; import { getPostMediaEngine } from '../engine/PostMediaEngine'; import { taskManager } from '../engine/TaskManager'; import { getBlogGenerationEngine, resolvePublicBaseUrl, type BlogGenerationResult, type BlogGenerationSection, type BlogGenerationOptions, type SiteValidationReport, } from '../engine/BlogGenerationEngine'; import { resolvePageTitle } from '../engine/PageRenderer'; type SafeHandle = (channel: string, handler: (...args: any[]) => Promise) => void; export function registerBlogHandlers(safeHandle: SafeHandle): void { const resolveBlogGenerationBaseOptions = async (): Promise => { const projectEngine = getProjectEngine(); const postEngine = getPostEngine(); const metaEngine = getMetaEngine(); const mediaEngine = getMediaEngine(); const postMediaEngine = getPostMediaEngine(); const project = await projectEngine.getActiveProject(); if (!project) { throw new Error('No active project'); } const dataDir = projectEngine.getDataDir(project.id, project.dataPath); postEngine.setProjectContext(project.id, dataDir); metaEngine.setProjectContext(project.id, dataDir); mediaEngine.setProjectContext(project.id, dataDir, dataDir); postMediaEngine.setProjectContext(project.id); if (!metaEngine.isInitialized()) { await metaEngine.syncOnStartup(); } const metadata = await metaEngine.getProjectMetadata(); const baseUrl = resolvePublicBaseUrl(metadata?.publicUrl); if (!baseUrl) { await dialog.showMessageBox({ type: 'warning', title: 'Public URL Required', message: 'Site rendering requires a public URL.', detail: 'Set Project → Public URL in Settings before rendering the site.', }); throw new Error('Project public URL is not configured'); } const language = metadata?.mainLanguage?.trim() || 'en'; const pageTitle = resolvePageTitle(metadata, project.name, project.description ?? undefined); return { projectId: project.id, projectName: metadata?.name?.trim() || project.name, projectDescription: metadata?.description, dataDir, baseUrl, maxPostsPerPage: metadata?.maxPostsPerPage, language, pageTitle, picoTheme: metadata?.picoTheme, categorySettings: (metadata as any)?.categorySettings, }; }; safeHandle('blog:generateSitemap', async () => { const blogGenerationEngine = getBlogGenerationEngine(); const baseOptions = await resolveBlogGenerationBaseOptions(); const taskTimestamp = Date.now(); const taskGroupId = `site-render-${taskTimestamp}`; const taskGroupName = 'Render Site'; const runSectionTask = async ( section: BlogGenerationSection, taskName: string, taskIdPrefix: string, ): Promise => { return taskManager.runTask({ id: `${taskIdPrefix}-${taskTimestamp}`, name: taskName, groupId: taskGroupId, groupName: taskGroupName, execute: async (onProgress) => { return blogGenerationEngine.generate({ ...baseOptions, sections: [section], }, (progress, message) => onProgress(progress, message || '')); }, }); }; const mergeResults = (results: BlogGenerationResult[]): BlogGenerationResult => { const first = results[0]; return { path: first.path, urlCount: Math.max(...results.map((result) => result.urlCount)), postCount: Math.max(...results.map((result) => result.postCount)), feedPostCount: Math.max(...results.map((result) => result.feedPostCount)), tagCount: Math.max(...results.map((result) => result.tagCount)), categoryCount: Math.max(...results.map((result) => result.categoryCount)), archiveCount: Math.max(...results.map((result) => result.archiveCount)), pagesGenerated: results.reduce((sum, result) => sum + result.pagesGenerated, 0), feeds: { rssPath: first.feeds.rssPath, atomPath: first.feeds.atomPath, }, changed: { sitemap: results.some((result) => result.changed.sitemap), rss: results.some((result) => result.changed.rss), atom: results.some((result) => result.changed.atom), }, }; }; const coreResult = await taskManager.runTask({ id: `site-render-core-${taskTimestamp}`, name: 'Render Site Core', groupId: taskGroupId, groupName: taskGroupName, execute: async (onProgress) => { return blogGenerationEngine.generate({ ...baseOptions, sections: ['core'], }, (progress, message) => onProgress(progress, message || '')); }, }); const [singleResult, categoryResult, tagResult, dateResult] = await Promise.all([ runSectionTask('single', 'Render Single Posts', 'site-render-single'), runSectionTask('category', 'Render Category Archives', 'site-render-category'), runSectionTask('tag', 'Render Tag Archives', 'site-render-tag'), runSectionTask('date', 'Render Date Archives', 'site-render-date'), ]); return mergeResults([coreResult, singleResult, categoryResult, tagResult, dateResult]); }); safeHandle('blog:validateSite', async () => { const blogGenerationEngine = getBlogGenerationEngine(); const baseOptions = await resolveBlogGenerationBaseOptions(); const taskTimestamp = Date.now(); return taskManager.runTask({ id: `site-validate-${taskTimestamp}`, name: 'Validate Site', execute: async (onProgress) => { return blogGenerationEngine.validateSite(baseOptions, (progress, message) => { onProgress(progress, message || 'Validating site...'); }); }, }); }); safeHandle('blog:applyValidation', async (_event, report: SiteValidationReport) => { const blogGenerationEngine = getBlogGenerationEngine(); const baseOptions = await resolveBlogGenerationBaseOptions(); const taskTimestamp = Date.now(); return taskManager.runTask({ id: `site-validate-apply-${taskTimestamp}`, name: 'Apply Site Validation', execute: async (onProgress) => { return blogGenerationEngine.applyValidation(baseOptions, report, (progress, message) => { onProgress(progress, message || 'Applying site validation...'); }); }, }); }); }