Implements: export presentation as pdf using puppeteer

This commit is contained in:
sauravniraula 2025-05-13 23:16:18 +05:45
parent abe4226147
commit f0fdc8d70c
No known key found for this signature in database
GPG key ID: 60FCC1B5A5E83326
4 changed files with 75 additions and 42 deletions

View file

@ -1,34 +1,44 @@
import { ipcMain, shell, dialog } from "electron";
import { downloadsDir } from "../utils/constants";
import { ipcMain, } from "electron";
import { baseDir, downloadsDir, isDev } from "../utils/constants";
import fs from "fs";
import path from "path";
import puppeteer from "puppeteer";
import { showFileDownloadedDialog } from "../utils/dialog";
export function setupExportHandlers() {
ipcMain.handle("file-downloaded", async (_, filePath: string): Promise<IPCStatus> => {
try {
const fileName = path.basename(filePath);
const destinationPath = path.join(downloadsDir, fileName);
const fileName = path.basename(filePath);
const destinationPath = path.join(downloadsDir, fileName);
await fs.promises.rename(filePath, destinationPath);
const { response } = await dialog.showMessageBox({
type: 'question',
buttons: ['Open File', 'Open Folder', 'Cancel'],
defaultId: 0,
title: 'File Downloaded',
message: 'What would you like to do?'
});
if (response === 0) {
await shell.openPath(destinationPath);
} else if (response === 1) {
await shell.openPath(downloadsDir);
}
return { success: true };
} catch (error: any) {
console.error('Error handling downloaded file:', error);
return { success: false };
}
await fs.promises.rename(filePath, destinationPath);
const success = await showFileDownloadedDialog(destinationPath);
return { success };
});
ipcMain.handle("export-as-pdf", async (_, id: string, title: string) => {
const ppt_url = `${process.env.NEXT_PUBLIC_FAST_API}/presentation/${id}`;
const browser = await puppeteer.launch({
headless: true,
executablePath: isDev ? undefined : path.join(baseDir, "dependencies/chrome-headless-shell/linux_build/chrome-headless-shell"),
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 720 });
await page.goto(ppt_url, { waitUntil: "networkidle0", timeout: 30000 });
const pdfBuffer = await page.pdf({
height: 720,
});
const destinationPath = path.join(downloadsDir, `${title}.pdf`);
await fs.promises.writeFile(destinationPath, pdfBuffer);
const success = await showFileDownloadedDialog(destinationPath);
return { success };
})
}

View file

@ -10,10 +10,11 @@ contextBridge.exposeInMainWorld('env', {
contextBridge.exposeInMainWorld('electron', {
fileDownloaded: (filePath: string) => ipcRenderer.invoke("file-downloaded", filePath),
exportAsPDF: (id: string, title: string) => ipcRenderer.invoke("export-as-pdf", id, title),
getUserConfig: () => ipcRenderer.invoke("get-user-config"),
setUserConfig: (userConfig: UserConfig) => ipcRenderer.invoke("set-user-config", userConfig),
readFile: (filePath: string) => ipcRenderer.invoke("read-file", filePath),
getSlideMetadata: (url: string, theme: string, customColors?: any, tempDirectory?: string) =>
getSlideMetadata: (url: string, theme: string, customColors?: any, tempDirectory?: string) =>
ipcRenderer.invoke("get-slide-metadata", url, theme, customColors, tempDirectory),
getFooter: (userId: string) => ipcRenderer.invoke("get-footer", userId),
setFooter: (userId: string, properties: any) => ipcRenderer.invoke("set-footer", userId, properties),

26
app/utils/dialog.ts Normal file
View file

@ -0,0 +1,26 @@
import { shell } from "electron";
import { dialog } from "electron";
import path from "path";
export async function showFileDownloadedDialog(filePath: string): Promise<boolean> {
try {
const { response } = await dialog.showMessageBox({
type: 'question',
buttons: ['Open File', 'Open Folder', 'Cancel'],
defaultId: 0,
title: 'File Downloaded',
message: 'What would you like to do?'
});
if (response === 0) {
await shell.openPath(filePath);
} else if (response === 1) {
await shell.openPath(path.dirname(filePath));
}
return true;
} catch (error: any) {
console.error('Error handling downloaded file:', error);
return false;
}
}

View file

@ -215,26 +215,22 @@ const Header = ({
const handleExportPdf = async () => {
if (isStreaming) return;
setOpen(false);
try {
setOpen(false);
setShowLoader(true);
const apiBody = await metaData();
toast({
title: "Exporting presentation...",
description: "Please wait while we export your presentation.",
variant: "default",
});
const data = await PresentationGenerationApi.exportAsPDF(apiBody);
if (data.path) {
setShowLoader(false);
// @ts-ignore
const ipcResponse = await window.electron.fileDownloaded(data.path);
if (!ipcResponse.success) {
throw new Error("Failed to download file");
}
// @ts-ignore
const ipcResponse = await window.electron.exportAsPDF(presentation_id, presentationData!.presentation!.title);
if (!ipcResponse.success) {
throw new Error("Failed to export as PDF");
}
setShowLoader(false);
} catch (err) {
console.error(err);
setShowLoader(false);
toast({
title: "Having trouble exporting!",
description: