diff --git a/app/ipc/index.ts b/app/ipc/index.ts index f4a60fc7..137b3dab 100644 --- a/app/ipc/index.ts +++ b/app/ipc/index.ts @@ -4,7 +4,7 @@ import { setupSlideMetadataHandlers } from "./slide_metadata"; import { setupReadFile } from "./read_file"; import { setupFooterHandlers } from "./footer_handlers"; import { setupThemeHandlers } from "./theme_handlers"; - +import { setupUploadImage } from "./upload_image"; export function setupIpcHandlers() { setupExportHandlers(); setupUserConfigHandlers(); @@ -12,4 +12,5 @@ export function setupIpcHandlers() { setupReadFile(); setupFooterHandlers(); setupThemeHandlers(); + setupUploadImage(); } \ No newline at end of file diff --git a/app/ipc/upload_image.ts b/app/ipc/upload_image.ts new file mode 100644 index 00000000..ad102dd6 --- /dev/null +++ b/app/ipc/upload_image.ts @@ -0,0 +1,28 @@ +import { ipcMain } from "electron"; +import path from "path"; +import fs from "fs"; +import crypto from "crypto"; +import { userDataDir } from "../utils/constants"; + +export function setupUploadImage() { + ipcMain.handle("upload-image", async (_, file: Buffer) => { + try { + // Create uploads directory if it doesn't exist + const uploadsDir = path.join(userDataDir, "uploads"); + fs.mkdirSync(uploadsDir, { recursive: true }); + + // Generate unique filename + const filename = `${crypto.randomBytes(16).toString('hex')}.png`; + const filePath = path.join(uploadsDir, filename); + + // Write file to disk + await fs.writeFileSync(filePath, file); + + // Return the relative path that can be used in the frontend + return filePath; + } catch (error) { + console.error("Error saving image:", error); + throw error; + } + }); +} \ No newline at end of file diff --git a/app/preloads/index.ts b/app/preloads/index.ts index 14084829..91ad8fe4 100644 --- a/app/preloads/index.ts +++ b/app/preloads/index.ts @@ -19,4 +19,5 @@ contextBridge.exposeInMainWorld('electron', { setFooter: (userId: string, properties: any) => ipcRenderer.invoke("set-footer", userId, properties), getTheme: (userId: string) => ipcRenderer.invoke("get-theme", userId), setTheme: (userId: string, themeData: any) => ipcRenderer.invoke("set-theme", userId, themeData), + uploadImage: (file: Buffer) => ipcRenderer.invoke("upload-image", file), }); diff --git a/package.json b/package.json index fd944bc9..cb461685 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "setup:env": "npm install && cd servers/fastapi && poetry install && cd ../../servers/nextjs && npm install", "build:ts": "rm -rf app_dist && tsc", "build:css": "tailwindcss -i ./resources/ui/assets/css/tailwind.import.css -o ./resources/ui/assets/css/tailwind.css --watch", - "build:nextjs": "rm -rf resources/nextjs && cd servers/nextjs && npm run build && mv out ../../resources/nextjs", + "build:nextjs": "rm -rf resources/nextjs && cd servers/nextjs && npm run build && cp -r out ../../resources/nextjs", "build:fastapi": "rm -rf resources/fastapi && cd servers/fastapi && .venv/bin/pyinstaller --distpath ../../resources server.spec", "build:electron": "rm -rf app_dist && tsc && node build.js", "clean:build": "rm -rf resources/nextjs && rm -rf resources/fastapi && rm -rf app_dist" diff --git a/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx b/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx index b058be46..f6d1c8e2 100644 --- a/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx +++ b/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx @@ -7,15 +7,12 @@ import { SheetTitle, } from "@/components/ui/sheet"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { - Search, Wand2, Upload, Edit, - Crop, Move, Maximize, } from "lucide-react"; @@ -38,6 +35,7 @@ import { } from "@/components/ui/popover"; import ToolTip from "@/components/ToolTip"; import { getEnv } from "@/utils/constant"; +import { ipcRenderer } from "electron"; interface ImageEditorProps { initialImage: string | null; @@ -234,8 +232,6 @@ const ImageEditor = ({ } }; - - const handleFileUpload = async ( event: React.ChangeEvent ) => { @@ -263,23 +259,20 @@ const ImageEditor = ({ setIsUploading(true); setUploadError(null); - const formData = new FormData(); - formData.append("file", file); - const response = await fetch("/api/user-upload", { - method: "POST", - body: formData, - }); + // Convert file to buffer + const buffer = await file.arrayBuffer(); - if (!response.ok) { - throw new Error("Upload failed"); - } + // Send to electron main process + // @ts-ignore + const relativePath = await window.electron.uploadImage(Buffer.from(buffer)); - const data = await response.json(); - setUploadedImageUrl(data.url); + // Update state with the returned path + setUploadedImageUrl(relativePath); } catch (err) { const error_message = "Failed to upload image. Please try again."; setUploadError(error_message); + console.error("Upload error:", err); } finally { setIsUploading(false); } @@ -503,7 +496,7 @@ const ImageEditor = ({
- + AI Generate @@ -560,9 +553,7 @@ const ImageEditor = ({ {`Preview Uploaded preview