From 9bf7096234975bfe28bd5720dd693e5f0e842ad3 Mon Sep 17 00:00:00 2001 From: shiva raj badu Date: Wed, 8 Apr 2026 22:28:14 +0545 Subject: [PATCH 1/2] feat: New Custom Template UI --- .../custom-template/CustomTemplatePage.tsx | 273 ++++ .../components/APIKeyWarning.tsx | 21 - .../components/EachSlide/EditControls.tsx | 167 --- .../components/EachSlide/HtmlEditor.tsx | 97 -- .../components/EachSlide/NewEachSlide.tsx | 572 ++++++-- .../components/EachSlide/SlideActions.tsx | 103 -- .../EachSlide/SlideContentDisplay.tsx | 191 ++- .../components/FileUploadSection.tsx | 307 +++-- .../components/FontManager.tsx | 350 ++--- .../components/LoadingSpinner.tsx | 2 +- .../components/SaveLayoutButton.tsx | 14 +- .../components/SaveLayoutModal.tsx | 58 +- .../components/SchemaEditor.tsx | 1178 +++++++++++++++++ .../components/SchemaEditorPanel.tsx | 39 + .../components/SchemaElementHighlighter.tsx | 307 +++++ .../components/SchemaHighlightContext.tsx | 129 ++ .../components/SlideContent.tsx | 70 +- .../components/SlidePreviewSection.tsx | 104 ++ .../components/TemplateCreationProgress.tsx | 160 +++ .../components/TemplateStudioHeader.tsx | 17 + .../custom-template/components/Timer.tsx | 84 +- .../components/steps/SlidesList.tsx | 63 + .../components/steps/Step2FontManagement.tsx | 40 + .../components/steps/Step3SlidePreview.tsx | 31 + .../steps/Step4TemplateCreation.tsx | 81 ++ .../custom-template/constants/index.ts | 94 ++ .../custom-template/hooks/index.ts | 12 + .../custom-template/hooks/useAPIKeyCheck.ts | 31 - .../hooks/useCompiledLayout.ts | 19 + .../custom-template/hooks/useCustomLayout.ts | 32 - .../custom-template/hooks/useDrawingCanvas.ts | 167 --- .../custom-template/hooks/useFileUpload.ts | 5 +- .../hooks/useFontManagement.ts | 153 --- .../custom-template/hooks/useHtmlEdit.ts | 49 - .../custom-template/hooks/useLayoutSaving.ts | 141 +- .../custom-template/hooks/useSlideEdit.ts | 149 +-- .../hooks/useSlideProcessing.ts | 229 ---- .../custom-template/hooks/useSlideUndoRedo.ts | 200 +++ .../hooks/useTemplateCreation.ts | 402 ++++++ .../custom-template/page.tsx | 186 +-- .../custom-template/types/index.ts | 172 ++- electron/servers/nextjs/package-lock.json | 103 +- electron/servers/nextjs/package.json | 1 + 43 files changed, 4473 insertions(+), 2130 deletions(-) create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/CustomTemplatePage.tsx delete mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/components/APIKeyWarning.tsx delete mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/components/EachSlide/EditControls.tsx delete mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/components/EachSlide/HtmlEditor.tsx delete mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/components/EachSlide/SlideActions.tsx create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/components/SchemaEditor.tsx create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/components/SchemaEditorPanel.tsx create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/components/SchemaElementHighlighter.tsx create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/components/SchemaHighlightContext.tsx create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/components/SlidePreviewSection.tsx create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/components/TemplateCreationProgress.tsx create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/components/TemplateStudioHeader.tsx create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/components/steps/SlidesList.tsx create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/components/steps/Step2FontManagement.tsx create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/components/steps/Step3SlidePreview.tsx create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/components/steps/Step4TemplateCreation.tsx create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/constants/index.ts create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/hooks/index.ts delete mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/hooks/useAPIKeyCheck.ts create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/hooks/useCompiledLayout.ts delete mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/hooks/useCustomLayout.ts delete mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/hooks/useDrawingCanvas.ts delete mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/hooks/useFontManagement.ts delete mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/hooks/useHtmlEdit.ts delete mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/hooks/useSlideProcessing.ts create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/hooks/useSlideUndoRedo.ts create mode 100644 electron/servers/nextjs/app/(presentation-generator)/custom-template/hooks/useTemplateCreation.ts diff --git a/electron/servers/nextjs/app/(presentation-generator)/custom-template/CustomTemplatePage.tsx b/electron/servers/nextjs/app/(presentation-generator)/custom-template/CustomTemplatePage.tsx new file mode 100644 index 00000000..5c22630f --- /dev/null +++ b/electron/servers/nextjs/app/(presentation-generator)/custom-template/CustomTemplatePage.tsx @@ -0,0 +1,273 @@ +"use client"; + + + +import React, { useEffect, useCallback, useState } from "react"; +import { useRouter } from "next/navigation"; + + + +import { useFileUpload } from "./hooks/useFileUpload"; +import { useTemplateCreation } from "./hooks/useTemplateCreation"; +import { useLayoutSaving } from "./hooks/useLayoutSaving"; + +import { ProcessedSlide } from "./types"; +import { TAILWIND_CDN_URL } from "./constants"; +import { TemplateStudioHeader } from "./components/TemplateStudioHeader"; +import { TemplateCreationProgress } from "./components/TemplateCreationProgress"; +import { Step2FontManagement } from "./components/steps/Step2FontManagement"; +import { Step3SlidePreview } from "./components/steps/Step3SlidePreview"; +import { Step4TemplateCreation } from "./components/steps/Step4TemplateCreation"; +import { SaveLayoutButton } from "./components/SaveLayoutButton"; +import { SaveLayoutModal } from "./components/SaveLayoutModal"; +import { FileUploadSection } from "./components/FileUploadSection"; + +import { useFontLoader } from "../hooks/useFontLoad"; +import Header from "@/app/(presentation-generator)/(dashboard)/dashboard/components/Header"; + + + + +const CustomTemplatePage = () => { + const router = useRouter(); + + const [schemaEditorSlideIndex, setSchemaEditorSlideIndex] = useState(null); + const [schemaPreviewData, setSchemaPreviewData] = useState>>({}); + + const { selectedFile, handleFileSelect, removeFile } = useFileUpload(); + + + const { + state, + uploadedFonts, + slides, + setSlides, + completedSlides, + checkFonts, + uploadFont, + removeFont, + fontUploadAndPreview, + initTemplateCreation, + retrySlide, + } = useTemplateCreation(); + + // Layout saving hook + const { + isSavingLayout, + isModalOpen, + openSaveModal, + closeSaveModal, + saveLayout, + } = useLayoutSaving(slides); + + + useEffect(() => { + const existingScript = document.querySelector('script[src*="tailwindcss.com"]'); + if (!existingScript) { + const script = document.createElement("script"); + script.src = TAILWIND_CDN_URL; + script.async = true; + document.head.appendChild(script); + } + }, []); + + + /** + * Step 1: Check fonts in uploaded PPTX + */ + const handleCheckFonts = useCallback(async () => { + if (selectedFile) { + await checkFonts(selectedFile); + } + }, [selectedFile, checkFonts]); + + /** + * Step 2: Upload fonts and generate preview + */ + const handleFontUploadAndPreview = useCallback(async () => { + if (selectedFile) { + const data = await fontUploadAndPreview(selectedFile); + if (data) { + useFontLoader(data.fonts); + } + } + }, [selectedFile, fontUploadAndPreview]); + + /** + * Step 5: Save template with metadata + */ + const handleSaveTemplate = useCallback(async ( + layoutName: string, + description: string, + template_info_id: string + ): Promise => { + const id = await saveLayout(layoutName, description, template_info_id); + if (id) { + router.push(`/template-preview?id=custom-${id}`); + } + return id; + }, [saveLayout, router]); + + /** + * Update a specific slide's data + */ + const handleSlideUpdate = useCallback((index: number, updatedSlideData: Partial) => { + setSlides((prevSlides) => + prevSlides.map((s, i) => + i === index + ? { ...s, ...updatedSlideData, modified: true } + : s + ) + ); + }, [setSlides]); + + + /** + * Open schema editor for a specific slide + */ + const handleOpenSchemaEditor = useCallback((index: number | null) => { + setSchemaEditorSlideIndex(index); + }, []); + + /** + * Close schema editor + */ + const handleCloseSchemaEditor = useCallback(() => { + setSchemaEditorSlideIndex(null); + }, []); + + /** + * Save changes from schema editor + */ + const handleSchemaEditorSave = useCallback((updatedReact: string) => { + if (schemaEditorSlideIndex !== null) { + setSlides(prev => prev.map((s, i) => + i === schemaEditorSlideIndex ? { ...s, react: updatedReact } : s + )); + } + setSchemaEditorSlideIndex(null); + }, [schemaEditorSlideIndex, setSlides]); + + /** + * Update schema preview content (for AI fill) + */ + const handleSchemaPreviewContent = useCallback((content: Record) => { + if (schemaEditorSlideIndex !== null) { + setSchemaPreviewData(prev => ({ + ...prev, + [schemaEditorSlideIndex]: content + })); + } + }, [schemaEditorSlideIndex]); + + /** + * Clear schema preview data for a specific slide + */ + const handleClearSchemaPreview = useCallback((slideIndex: number) => { + setSchemaPreviewData(prev => { + const newData = { ...prev }; + delete newData[slideIndex]; + return newData; + }); + }, []); + + + + const showFileUpload = state.step === 'file-upload'; + const showFontManager = state.step === 'font-check' || state.step === 'font-upload'; + const showPreview = state.step === 'slides-preview'; + const showSlides = state.step === 'template-creation' || state.step === 'completed'; + const isProcessingCompleted = state.step === 'completed'; + + + + return ( +
+ +
+ + {showFileUpload ? ( +
+ + +
+ ) : ( +
+ + + + {/* Step 2: Font Management */} + {showFontManager && ( + + )} + + {/* Step 3: Slide Preview */} + {showPreview && ( + + )} + + {/* Step 4: Template Creation & Editing */} + {showSlides && slides.length > 0 && ( + + )} + + {/* Floating Save Template Button */} + {isProcessingCompleted && slides.some((s) => s.processed) && ( + s.processing)} + /> + )} + + {/* Save Template Modal */} + +
+ )} + +
+ ); +}; + +export default CustomTemplatePage; diff --git a/electron/servers/nextjs/app/(presentation-generator)/custom-template/components/APIKeyWarning.tsx b/electron/servers/nextjs/app/(presentation-generator)/custom-template/components/APIKeyWarning.tsx deleted file mode 100644 index 0a6447b2..00000000 --- a/electron/servers/nextjs/app/(presentation-generator)/custom-template/components/APIKeyWarning.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import Header from "@/app/(presentation-generator)/(dashboard)/dashboard/components/Header"; - -export const APIKeyWarning: React.FC = () => { - return ( -
-
-
-
-

- Please add "GOOGLE_API_KEY" to enable template creation via AI. -

-

Please add your OpenAI API Key to process the layout

-

- This feature requires an OpenAI model GPT-5. Configure your key in settings or via environment variables. -

-
-
-
- ); -}; \ No newline at end of file diff --git a/electron/servers/nextjs/app/(presentation-generator)/custom-template/components/EachSlide/EditControls.tsx b/electron/servers/nextjs/app/(presentation-generator)/custom-template/components/EachSlide/EditControls.tsx deleted file mode 100644 index 82f2f111..00000000 --- a/electron/servers/nextjs/app/(presentation-generator)/custom-template/components/EachSlide/EditControls.tsx +++ /dev/null @@ -1,167 +0,0 @@ -'use client' - -import React from "react"; -import { Button } from "@/components/ui/button"; -import { Textarea } from "@/components/ui/textarea"; -import { Pencil, Eraser, RotateCcw, SendHorizontal, X } from "lucide-react"; -import { EditControlsProps } from "../../types"; - -export const EditControls: React.FC = ({ - isEditMode, - prompt, - isUpdating, - strokeWidth, - strokeColor, - eraserMode, - onPromptChange, - onSave, - onCancel, - onStrokeWidthChange, - onStrokeColorChange, - onEraserModeChange, - onClearCanvas, -}) => { - const colors = [ - "#000000", - "#FF0000", - "#00FF00", - "#0000FF", - "#FFFF00", - "#FF00FF", - "#00FFFF", - "#FFA500", - ]; - - const strokeWidths = [1, 3, 5, 8, 12]; - - if (!isEditMode) return null; - - return ( -
- {/* Drawing Tools */} -
-
- {/* Drawing Tools */} -
- - - -
- - {/* Color Picker */} - {!eraserMode && ( -
- {colors.map((color) => ( -
- )} - - {/* Stroke Width */} -
- {strokeWidths.map((width) => ( - - ))} -
- - -
- - -
- - {/* Prompt Section */} -
- -
-