diff --git a/electron/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/Header.tsx b/electron/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/Header.tsx index 7177698b..540701bb 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/Header.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/Header.tsx @@ -8,7 +8,7 @@ import { trackEvent, MixpanelEvent } from "@/utils/mixpanel"; const Header = () => { const pathname = usePathname(); return ( -
+
diff --git a/electron/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/PresentationCard.tsx b/electron/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/PresentationCard.tsx index 24e1d9d1..3da715e3 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/PresentationCard.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/PresentationCard.tsx @@ -1,5 +1,5 @@ 'use client' -import React from "react"; +import React, { useEffect } from "react"; import { Card } from "@/components/ui/card"; import { DashboardApi } from "@/app/(presentation-generator)/services/api/dashboard"; @@ -12,7 +12,7 @@ import { import { useRouter } from "next/navigation"; import { toast } from "sonner"; -import { useFontLoader } from "@/app/(presentation-generator)/hooks/useFontLoader"; +import { useFontLoader } from "@/app/(presentation-generator)/hooks/useFontLoad"; import SlideScale from "@/app/(presentation-generator)/components/PresentationRender"; import MarkdownRenderer from "@/components/MarkDownRender"; @@ -28,11 +28,52 @@ export const PresentationCard = ({ onDeleted?: (presentationId: string) => void; }) => { const router = useRouter(); - useFontLoader(presentation.fonts || []); + const handlePreview = (e: React.MouseEvent) => { e.preventDefault(); router.push(`/presentation?id=${id}&type=standard`); }; + useEffect(() => { + applyTheme(presentation.theme) + }, []) + const applyTheme = async (theme: any) => { + const element = document.getElementById(`dashboard-presentation-card-${id}`) + if (!element) return; + + if (!theme || !theme.data || !theme.data.colors['graph_0']) { return; } + const cssVariables = { + '--primary-color': theme.data.colors['primary'], + '--background-color': theme.data.colors['background'], + '--card-color': theme.data.colors['card'], + '--stroke': theme.data.colors['stroke'], + '--primary-text': theme.data.colors['primary_text'], + '--background-text': theme.data.colors['background_text'], + '--graph-0': theme.data.colors['graph_0'], + '--graph-1': theme.data.colors['graph_1'], + '--graph-2': theme.data.colors['graph_2'], + '--graph-3': theme.data.colors['graph_3'], + '--graph-4': theme.data.colors['graph_4'], + '--graph-5': theme.data.colors['graph_5'], + '--graph-6': theme.data.colors['graph_6'], + '--graph-7': theme.data.colors['graph_7'], + '--graph-8': theme.data.colors['graph_8'], + '--graph-9': theme.data.colors['graph_9'], + } + Object.entries(cssVariables).forEach(([key, value]) => { + element.style.setProperty(key, value) + }) + // + if (theme.data.fonts.textFont.url && theme.data.fonts.textFont.name) { + useFontLoader({ [theme.data.fonts.textFont.name]: theme.data.fonts.textFont.url }) + } + + // Apply fonts to preview container + element.style.setProperty('font-family', `"${theme.data.fonts.textFont.name}"`) + element.style.setProperty('--heading-font-family', `"${theme.data.fonts.textFont.name}"`) + element.style.setProperty('--body-font-family', `"${theme.data.fonts.textFont.name}"`) + + + } const handleDelete = async (e: React.MouseEvent) => { e.preventDefault(); @@ -59,7 +100,9 @@ export const PresentationCard = ({ onClick={handlePreview} className="bg-[#F8FBFB] font-syne shadow-none sm:shadow-none presentation-card rounded-[12px] p-0 group hover:shadow-md transition-all duration-500 slide-theme cursor-pointer overflow-hidden flex flex-col" > -
+
{/*

{presentation.type} diff --git a/electron/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx b/electron/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx index 5065d91f..d4db580f 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx @@ -12,7 +12,7 @@ interface EditableLayoutWrapperProps { slideData: any; isEditMode?: boolean; properties?: any; - + } interface EditableElement { @@ -29,7 +29,7 @@ const EditableLayoutWrapper: React.FC = ({ slideIndex, slideData, properties, - + }) => { const dispatch = useDispatch(); const containerRef = useRef(null); @@ -245,7 +245,7 @@ const EditableLayoutWrapper: React.FC = ({ } } }); - + // Process SVG icons svgElements.forEach((svg, index) => { const svgEl = svg as SVGElement; diff --git a/electron/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx b/electron/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx index ae452f27..65eae752 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx @@ -67,7 +67,7 @@ const ImageEditor = ({ (properties && properties[imageIdx] && properties[imageIdx].initialObjectFit) || - "cover" + "cover" ); // Refs @@ -104,7 +104,7 @@ const ImageEditor = ({ console.error("error in getting previous generated images", error); setError( error.message || - "Failed to get previous generated images. Please try again." + "Failed to get previous generated images. Please try again." ); } }; @@ -233,7 +233,7 @@ const ImageEditor = ({ trackEvent(MixpanelEvent.ImageEditor_UploadImage_API_Call); const result = await ImagesApi.uploadImage(file); setUploadedImageUrl(result.file_url || result.path); - } catch (err:any) { + } catch (err: any) { setUploadError("Failed to upload image. Please try again."); toast.error(err.message || "Failed to upload image. Please try again."); console.log("Upload error:", err.message); @@ -247,7 +247,7 @@ const ImageEditor = ({ setUploadedImagesLoading(true); const result = await ImagesApi.getUploadedImages(); setUploadedImages(result); - } catch (err:any) { + } catch (err: any) { toast.error(err.message || "Failed to get uploaded images. Please try again."); console.log("Get uploaded images error:", err.message); } finally { @@ -265,7 +265,7 @@ const ImageEditor = ({ const result = await ImagesApi.deleteImage(image_id); setUploadedImages(uploadedImages.filter((image) => image.id !== image_id)); toast.success(result.message || "Image deleted successfully"); - } catch (err:any) { + } catch (err: any) { toast.error(err.message || "Failed to delete image. Please try again."); } }; @@ -480,12 +480,12 @@ const ImageEditor = ({ } className="cursor-pointer group aspect-[4/3] rounded-lg overflow-hidden relative border border-gray-200" > - { + { e.stopPropagation(); handleDeleteImage(image.id) - }}/> + }} /> Uploaded preview @@ -496,7 +496,7 @@ const ImageEditor = ({

- +
)) )} @@ -578,7 +578,7 @@ const ImageEditor = ({ variant="outline" className={cn( objectFit === "cover" && - "bg-blue-50 border-blue-500" + "bg-blue-50 border-blue-500" )} onClick={() => handleFitChange("cover")} > @@ -588,7 +588,7 @@ const ImageEditor = ({ variant="outline" className={cn( objectFit === "contain" && - "bg-blue-50 border-blue-500" + "bg-blue-50 border-blue-500" )} onClick={() => handleFitChange("contain")} > @@ -607,7 +607,7 @@ const ImageEditor = ({
} {/* Focus Point */} - {} + { }
diff --git a/electron/servers/nextjs/app/(presentation-generator)/components/PresentationRender.tsx b/electron/servers/nextjs/app/(presentation-generator)/components/PresentationRender.tsx index 4361845a..e2781787 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/components/PresentationRender.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/components/PresentationRender.tsx @@ -6,7 +6,7 @@ import { V1ContentRender } from '../../(presentation-generator)/components/V1Con const BASE_WIDTH = 1280; const BASE_HEIGHT = 720; -const SlideScale = ({ slide }: { slide: any }) => { +const SlideScale = ({ slide, theme }: { slide: any, theme?: any }) => { const containerRef = useRef(null); const [containerWidth, setContainerWidth] = useState(0); @@ -62,12 +62,12 @@ const SlideScale = ({ slide }: { slide: any }) => { } as React.CSSProperties} > - diff --git a/electron/servers/nextjs/app/(presentation-generator)/components/V1ContentRender.tsx b/electron/servers/nextjs/app/(presentation-generator)/components/V1ContentRender.tsx index f90fa3f6..35b05e5d 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/components/V1ContentRender.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/components/V1ContentRender.tsx @@ -86,7 +86,7 @@ export const V1ContentRender = ({ slide, isEditMode, theme }: { slide: any, isEd if (isEditMode) { return ( -
+
{ - const injectFonts = (fontUrls: string[]) => { - fontUrls.forEach((fontUrl) => { - if (!fontUrl) return; - let newFontUrl = fontUrl.includes('fonts.googleapis') ? fontUrl : `https://localhost:5000${fontUrl}`; - const existingStyle = document.querySelector(`style[data-font-url="${newFontUrl}"]`); - if (existingStyle) return; - const style = document.createElement("style"); - style.setAttribute("data-font-url", newFontUrl); - style.textContent = `@import url('${newFontUrl}');`; - document.head.appendChild(style); - }); - }; - injectFonts(fonts); -}; \ No newline at end of file diff --git a/electron/servers/nextjs/app/(presentation-generator)/outline/components/OutlineContent.tsx b/electron/servers/nextjs/app/(presentation-generator)/outline/components/OutlineContent.tsx index 80e0b136..ad1b8116 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/outline/components/OutlineContent.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/outline/components/OutlineContent.tsx @@ -57,18 +57,7 @@ const OutlineContent: React.FC = ({
)} - {/*
-
- Presentation Outline -
- {isStreaming && ( -
-
- Generating outlines... -
- )} -
*/} - {/* Skeleton loading state */} + {isLoading && (
{[...Array(6)].map((_, index) => ( @@ -93,7 +82,7 @@ const OutlineContent: React.FC = ({ {/* Outlines content */} {outlines && outlines.length > 0 && ( -
+
-

slide {index}

+

Slide {index}

{/* Editable Markdown Content */} {isStreaming ? ( isActiveStreaming ? ( diff --git a/electron/servers/nextjs/app/(presentation-generator)/outline/components/OutlinePage.tsx b/electron/servers/nextjs/app/(presentation-generator)/outline/components/OutlinePage.tsx index 14f3bc30..94643fbd 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/outline/components/OutlinePage.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/outline/components/OutlinePage.tsx @@ -49,54 +49,54 @@ const OutlinePage: React.FC = () => { duration={loadingState.duration} /> - -
- - - - Outline & Content - - - - Select Template - - + +
+ + {/* Reserves vertical space so content does not sit under the fixed tab bar */} +
+
+
+ + + Outline & Content + + + + Select Template + + +
+
-
- -
- -
+
+ + - -
- -
+ +
- {/* Fixed Button */} -
+
-

+

{isPresentationSaving &&
diff --git a/electron/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx b/electron/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx index 931ac183..94795e98 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx @@ -87,14 +87,7 @@ const PresentationPage: React.FC = ({ handleSlideChange(newSlide, presentationData); }; - // useEffect(() => { - // if(!loading && !isStreaming && presentationData?.slides && presentationData?.slides.length > 0){ - // const presentation_id = presentationData?.slides[0].layout.split(":")[0].split("custom-")[1]; - // const fonts = getCustomTemplateFonts(presentation_id); - // useFontLoader(fonts || []); - // } - // }, [presentationData,loading,isStreaming]); // Presentation Mode View if (isPresentMode) { return ( @@ -133,6 +126,7 @@ const PresentationPage: React.FC = ({ style={{ background: "#ffffff", }} + id="presentation-slides-wrapper" className="flex gap-6 relative " >
@@ -144,10 +138,10 @@ const PresentationPage: React.FC = ({ />
-
+
@@ -141,14 +141,14 @@ const SidePanel = ({ className="w-full h-[calc(100vh-120px)] hide-scrollbar overflow-hidden slide-theme " > -

Slides

+

Slides ({presentationData?.slides?.length})

-
+
{isStreaming ? ( presentationData && presentationData?.slides.map((slide: any, index: number) => ( diff --git a/electron/servers/nextjs/app/(presentation-generator)/presentation/components/SlideContent.tsx b/electron/servers/nextjs/app/(presentation-generator)/presentation/components/SlideContent.tsx index eb18871c..67193c34 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/presentation/components/SlideContent.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/presentation/components/SlideContent.tsx @@ -21,6 +21,7 @@ import { trackEvent, MixpanelEvent } from "@/utils/mixpanel"; import { addToHistory } from "@/store/slices/undoRedoSlice"; import { V1ContentRender } from "../../components/V1ContentRender"; import NewSlide from "./NewSlide"; +import SlideScale from "../../components/PresentationRender"; interface SlideContentProps { slide: any; @@ -145,7 +146,8 @@ const SlideContent = ({ slide, index, presentationId }: SlideContentProps) => { data-group={slide.layout_group} className={` w-full group font-syne `} > - + {/* */} + {!showNewSlideSelection && (
diff --git a/electron/servers/nextjs/app/(presentation-generator)/presentation/components/SortableSlide.tsx b/electron/servers/nextjs/app/(presentation-generator)/presentation/components/SortableSlide.tsx index 12ec0f4e..92068719 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/presentation/components/SortableSlide.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/presentation/components/SortableSlide.tsx @@ -13,8 +13,6 @@ interface SortableSlideProps { const SCALE = 0.125; export function SortableSlide({ slide, index, selectedSlide, onSlideClick }: SortableSlideProps) { - const searchParams = useSearchParams(); - const type = searchParams.get("type") as 'standard' | 'smart'; const lastClickTime = useRef(0); const { attributes, diff --git a/electron/servers/nextjs/utils/providerConstants.ts b/electron/servers/nextjs/utils/providerConstants.ts index 1b1756ed..0861cc62 100644 --- a/electron/servers/nextjs/utils/providerConstants.ts +++ b/electron/servers/nextjs/utils/providerConstants.ts @@ -93,6 +93,12 @@ export const IMAGE_PROVIDERS: Record = { }; export const LLM_PROVIDERS: Record = { + codex: { + value: "codex", + label: "ChatGPT", + description: "ChatGPT Plus/Pro via OAuth", + icon: "/icons/chatgpt.png", + }, openai: { value: "openai", label: "OpenAI", @@ -126,12 +132,7 @@ export const LLM_PROVIDERS: Record = { description: "Custom LLM", icon: "/icons/custom.png", }, - codex: { - value: "codex", - label: "ChatGPT", - description: "ChatGPT Plus/Pro via OAuth", - icon: "/icons/chatgpt.png", - }, + }; export const DALLE_3_QUALITY_OPTIONS = [ diff --git a/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/PresentationCard.tsx b/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/PresentationCard.tsx index 24e1d9d1..0f6a3cc7 100644 --- a/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/PresentationCard.tsx +++ b/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/PresentationCard.tsx @@ -12,7 +12,7 @@ import { import { useRouter } from "next/navigation"; import { toast } from "sonner"; -import { useFontLoader } from "@/app/(presentation-generator)/hooks/useFontLoader"; +import { useFontLoader } from "@/app/(presentation-generator)/hooks/useFontLoad"; import SlideScale from "@/app/(presentation-generator)/components/PresentationRender"; import MarkdownRenderer from "@/components/MarkDownRender"; @@ -28,7 +28,7 @@ export const PresentationCard = ({ onDeleted?: (presentationId: string) => void; }) => { const router = useRouter(); - useFontLoader(presentation.fonts || []); + const handlePreview = (e: React.MouseEvent) => { e.preventDefault(); router.push(`/presentation?id=${id}&type=standard`); diff --git a/servers/nextjs/app/(presentation-generator)/components/V1ContentRender.tsx b/servers/nextjs/app/(presentation-generator)/components/V1ContentRender.tsx index f90fa3f6..0278d805 100644 --- a/servers/nextjs/app/(presentation-generator)/components/V1ContentRender.tsx +++ b/servers/nextjs/app/(presentation-generator)/components/V1ContentRender.tsx @@ -86,7 +86,7 @@ export const V1ContentRender = ({ slide, isEditMode, theme }: { slide: any, isEd if (isEditMode) { return ( -
+
= ({ {/* Outlines content */} {outlines && outlines.length > 0 && ( -
+
{ duration={loadingState.duration} /> - -
- - - - Outline & Content - - - - Select Template - - + +
+ +
+
+
+ + + Outline & Content + + + + Select Template + + +
+
-
- -
- -
+
+ + - -
- -
+ +
- {/* Fixed Button */} -
+