From 4545d7b17d851287bec5642f921e4982a58108f9 Mon Sep 17 00:00:00 2001 From: shiva raj badu Date: Sun, 10 Aug 2025 21:23:38 +0545 Subject: [PATCH] fix(Nextjs): Pdf Export markdown parser and tailwind not loading issue --- .../components/APIKeyWarning.tsx | 2 +- .../custom-template/components/EachSlide.tsx | 728 ------------------ .../components/LoadingSpinner.tsx | 2 +- .../custom-template/page.tsx | 5 +- .../outline/components/GroupLayouts.tsx | 2 +- .../outline/components/OutlineContent.tsx | 1 - .../pdf-maker/PdfMakerPage.tsx | 4 +- .../components/PresentationPage.tsx | 4 +- .../presentation/components/SlideContent.tsx | 3 +- .../template-preview/[slug]/page.tsx | 2 +- 10 files changed, 12 insertions(+), 741 deletions(-) delete mode 100644 servers/nextjs/app/(presentation-generator)/custom-template/components/EachSlide.tsx diff --git a/servers/nextjs/app/(presentation-generator)/custom-template/components/APIKeyWarning.tsx b/servers/nextjs/app/(presentation-generator)/custom-template/components/APIKeyWarning.tsx index ad803232..60a43e59 100644 --- a/servers/nextjs/app/(presentation-generator)/custom-template/components/APIKeyWarning.tsx +++ b/servers/nextjs/app/(presentation-generator)/custom-template/components/APIKeyWarning.tsx @@ -1,5 +1,5 @@ import React from "react"; -import Header from "@/components/Header"; +import Header from "@/app/(presentation-generator)/dashboard/components/Header"; export const APIKeyWarning: React.FC = () => { return ( diff --git a/servers/nextjs/app/(presentation-generator)/custom-template/components/EachSlide.tsx b/servers/nextjs/app/(presentation-generator)/custom-template/components/EachSlide.tsx deleted file mode 100644 index 8f7804e1..00000000 --- a/servers/nextjs/app/(presentation-generator)/custom-template/components/EachSlide.tsx +++ /dev/null @@ -1,728 +0,0 @@ -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Textarea } from "@/components/ui/textarea"; -import { - AlertCircle, - CheckCircle, - Edit, - Loader2, - Pencil, - Eraser, - RotateCcw, - SendHorizontal, - X, - Repeat2, - Trash, -} from "lucide-react"; -import React, { useState, useEffect, useRef, useCallback } from "react"; -import ToolTip from "@/components/ToolTip"; -import html2canvas from "html2canvas"; -import SlideContent from "./SlideContent"; - -const EachSlide = ({ - slide, - index, - retrySlide, - setSlides, - onSlideUpdate, - isProcessing, -}: { - slide: any; - index: number; - retrySlide: (index: number) => void; - setSlides: React.Dispatch>; - onSlideUpdate?: (updatedSlideData: any) => void; - isProcessing: boolean; -}) => { - const [isUpdating, setIsUpdating] = useState(false); - const [prompt, setPrompt] = useState(""); - const [isEditMode, setIsEditMode] = useState(false); - const slideContentRef = useRef(null); - - // Drawing canvas states - const canvasRef = useRef(null); - const slideDisplayRef = useRef(null); - const [strokeWidth, setStrokeWidth] = useState(3); - const [strokeColor, setStrokeColor] = useState("#000000"); - const [eraserMode, setEraserMode] = useState(false); - const [isDrawing, setIsDrawing] = useState(false); - const [slideHtml, setSlideHtml] = useState(""); - const [canvasDimensions, setCanvasDimensions] = useState({ - width: 1280, - height: 720, - }); - const [didYourDraw, setDidYourDraw] = useState(false); - - // Load Tailwind CSS dynamically for slide content - useEffect(() => { - if (slide.processed && slide.html) { - const existingScript = document.querySelector( - 'script[src*="tailwindcss.com"]' - ); - if (!existingScript) { - const script = document.createElement("script"); - script.src = "https://cdn.tailwindcss.com"; - script.async = true; - document.head.appendChild(script); - } - } - }, [slide.processed, slide.html]); - - // Set up canvas when entering edit mode - useEffect(() => { - if (isEditMode && slideContentRef.current && slide.html) { - const rect = slideContentRef.current.getBoundingClientRect(); - - setCanvasDimensions({ - width: Math.max(rect.width, 800), - height: Math.max(rect.height, 600), - }); - - setSlideHtml(slide.html); - } - }, [isEditMode, slide.html]); - - // Apply optimizations once after slide content is rendered in edit mode - useEffect(() => { - if (isEditMode && slideDisplayRef.current && slideHtml) { - const slideContent = slideDisplayRef.current; - - slideContent.style.pointerEvents = "none"; - slideContent.style.userSelect = "none"; - slideContent.style.transform = "translateZ(0)"; - slideContent.style.willChange = "auto"; - slideContent.style.backfaceVisibility = "hidden"; - - const interactiveElements = slideContent.querySelectorAll( - "img, video, iframe, a, button, input, textarea, select" - ); - - interactiveElements.forEach((element) => { - const el = element as HTMLElement; - el.style.pointerEvents = "none"; - el.style.userSelect = "none"; - (el.style as any).webkitUserSelect = "none"; - (el.style as any).webkitTouchCallout = "none"; - (el.style as any).webkitUserDrag = "none"; - el.style.transform = "translateZ(0)"; - el.style.backfaceVisibility = "hidden"; - - if (element.tagName === "IMG") { - (element as HTMLImageElement).draggable = false; - } - - el.onclick = null; - el.onmousedown = null; - el.onmouseup = null; - el.onmousemove = null; - }); - } - }, [isEditMode, slideHtml]); - - const getCanvasContext = () => { - const canvas = canvasRef.current; - if (!canvas) return null; - return canvas.getContext("2d"); - }; - - const getMousePos = (e: React.MouseEvent) => { - const canvas = canvasRef.current; - if (!canvas) return { x: 0, y: 0 }; - - const rect = canvas.getBoundingClientRect(); - return { - x: e.clientX - rect.left, - y: e.clientY - rect.top, - }; - }; - - const getTouchPos = (e: React.TouchEvent) => { - const canvas = canvasRef.current; - if (!canvas) return { x: 0, y: 0 }; - - const rect = canvas.getBoundingClientRect(); - const touch = e.touches[0]; - return { - x: touch.clientX - rect.left, - y: touch.clientY - rect.top, - }; - }; - - const startDrawing = useCallback( - (pos: { x: number; y: number }) => { - const ctx = getCanvasContext(); - if (!ctx) return; - - setIsDrawing(true); - ctx.beginPath(); - ctx.moveTo(pos.x, pos.y); - - if (eraserMode) { - ctx.globalCompositeOperation = "destination-out"; - ctx.lineWidth = strokeWidth * 2; - } else { - ctx.globalCompositeOperation = "source-over"; - ctx.strokeStyle = strokeColor; - ctx.lineWidth = strokeWidth; - } - - ctx.lineCap = "round"; - ctx.lineJoin = "round"; - }, - [eraserMode, strokeColor, strokeWidth] - ); - - const draw = useCallback( - (pos: { x: number; y: number }) => { - if (!isDrawing) return; - setDidYourDraw(true); - - const ctx = getCanvasContext(); - if (!ctx) return; - - ctx.lineTo(pos.x, pos.y); - ctx.stroke(); - }, - [isDrawing] - ); - - const stopDrawing = useCallback(() => { - setIsDrawing(false); - }, []); - - // Mouse events - const handleMouseDown = (e: React.MouseEvent) => { - e.preventDefault(); - const pos = getMousePos(e); - startDrawing(pos); - }; - - const handleMouseMove = (e: React.MouseEvent) => { - e.preventDefault(); - const pos = getMousePos(e); - draw(pos); - }; - - const handleMouseUp = (e: React.MouseEvent) => { - e.preventDefault(); - stopDrawing(); - }; - - // Touch events - const handleTouchStart = (e: React.TouchEvent) => { - e.preventDefault(); - const pos = getTouchPos(e); - startDrawing(pos); - }; - - const handleTouchMove = (e: React.TouchEvent) => { - e.preventDefault(); - const pos = getTouchPos(e); - draw(pos); - }; - - const handleTouchEnd = (e: React.TouchEvent) => { - e.preventDefault(); - stopDrawing(); - }; - - const handleClearCanvas = () => { - const canvas = canvasRef.current; - setDidYourDraw(false); - const ctx = getCanvasContext(); - if (!canvas || !ctx) return; - - ctx.clearRect(0, 0, canvas.width, canvas.height); - }; - - // Convert data URL to blob for form data - const dataURLToBlob = (dataURL: string): Blob => { - const parts = dataURL.split(","); - const contentType = parts[0].match(/:(.*?);/)?.[1] || "image/png"; - const raw = window.atob(parts[1]); - const rawLength = raw.length; - const uInt8Array = new Uint8Array(rawLength); - - for (let i = 0; i < rawLength; ++i) { - uInt8Array[i] = raw.charCodeAt(i); - } - - return new Blob([uInt8Array], { type: contentType }); - }; - - const handleSave = async () => { - if ( - !slideContentRef.current || - !canvasRef.current || - !slideDisplayRef.current - ) - return; - - if (!prompt.trim()) { - alert("Please enter a prompt before saving."); - return; - } - - setIsUpdating(true); - - try { - // Take screenshot of the slide display area (slide only) - const slideOnly = await html2canvas(slideDisplayRef.current, { - backgroundColor: "#ffffff", - scale: 1, - logging: false, - useCORS: true, - ignoreElements: (element) => { - return element.tagName === "CANVAS"; - }, - }); - let slideWithCanvas; - if (didYourDraw) { - // Take screenshot of the entire slide display area including canvas - slideWithCanvas = await html2canvas(slideDisplayRef.current, { - backgroundColor: "#ffffff", - scale: 1, - logging: false, - useCORS: true, - }); - } - - const currentHtml = slide.html; - - const currentUiImageBlob = dataURLToBlob( - slideOnly.toDataURL("image/png") - ); - let sketchImageBlob; - if (didYourDraw && slideWithCanvas) { - sketchImageBlob = dataURLToBlob(slideWithCanvas.toDataURL("image/png")); - } - - const formData = new FormData(); - formData.append( - "current_ui_image", - currentUiImageBlob, - `slide-${slide.slide_number}-current.png` - ); - if (didYourDraw && slideWithCanvas && sketchImageBlob) { - formData.append( - "sketch_image", - sketchImageBlob, - `slide-${slide.slide_number}-sketch.png` - ); - } - formData.append("html", currentHtml); - formData.append("prompt", prompt); - - const response = await fetch("/api/v1/ppt/html-edit/", { - method: "POST", - body: formData, - }); - - if (!response.ok) { - throw new Error(`API call failed: ${response.statusText}`); - } - - const data = await response.json(); - - const updatedSlideData = { - slide_number: slide.slide_number, - html: data.edited_html || currentHtml, - processed: true, - processing: false, - error: undefined, - }; - - if (onSlideUpdate) { - onSlideUpdate(updatedSlideData); - } else { - setSlides((prevSlides) => - prevSlides.map((s, i) => - i === index ? { ...s, ...updatedSlideData } : s - ) - ); - } - - // Exit edit mode - setIsEditMode(false); - setPrompt(""); - handleClearCanvas(); - } catch (error) { - console.error("Error updating slide:", error); - alert( - `Error updating slide: ${ - error instanceof Error ? error.message : "Unknown error" - }` - ); - } finally { - setIsUpdating(false); - } - }; - - const handleEditClick = () => { - setIsEditMode(true); - }; - - const handleCancelEdit = () => { - setIsEditMode(false); - setPrompt(""); - handleClearCanvas(); - }; - - const handleEraserModeChange = (isEraser: boolean) => { - setEraserMode(isEraser); - }; - - const handleStrokeColorChange = (color: string) => { - setStrokeColor(color); - setEraserMode(false); - }; - - const handleStrokeWidthChange = (width: number) => { - setStrokeWidth(width); - }; - - const colors = [ - "#000000", - "#FF0000", - "#00FF00", - "#0000FF", - "#FFFF00", - "#FF00FF", - "#00FFFF", - "#FFA500", - ]; - - const strokeWidths = [1, 3, 5, 8, 12]; - - const handleDeleteSlide = () => { - setSlides((prevSlides) => prevSlides.filter((_, i) => i !== index)); - }; - - return ( - - - -
-
- {slide.processing ? ( - - ) : slide.processed ? ( - - ) : slide.error ? ( - - ) : ( -
- )} -
- - {slide.processed && ( -
- {slide.processed && slide.html && !isEditMode && ( -
- - - -
- )} -
- - - -
-
- - - -
-
- )} -
- - - - - {/* Edit Mode Controls */} - {isEditMode && slide.processed && slide.html && ( -
- {/* Drawing Tools */} -
-
- {/* Drawing Tools */} -
- - - -
- - {/* Color Picker */} - {!eraserMode && ( -
- {colors.map((color) => ( -
- )} - - {/* Stroke Width */} -
- {strokeWidths.map((width) => ( - - ))} -
- - -
- - -
- - {/* Prompt Section */} -
- -
-