Merge branch 'pdf-pptx-layout' of https://github.com/presenton/presenton into pdf-pptx-layout
merge
This commit is contained in:
commit
39ca51f95d
7 changed files with 80 additions and 49 deletions
Binary file not shown.
|
|
@ -96,6 +96,31 @@ const TiptapTextReplacer: React.FC<TiptapTextReplacerProps> = ({
|
|||
wordBreak: computedStyles.wordBreak,
|
||||
overflow: computedStyles.overflow,
|
||||
textAlignLast: computedStyles.textAlignLast,
|
||||
position: computedStyles.position,
|
||||
top: computedStyles.top,
|
||||
left: computedStyles.left,
|
||||
right: computedStyles.right,
|
||||
bottom: computedStyles.bottom,
|
||||
display: computedStyles.display,
|
||||
flexDirection: computedStyles.flexDirection,
|
||||
flexWrap: computedStyles.flexWrap,
|
||||
flexGrow: computedStyles.flexGrow,
|
||||
flexShrink: computedStyles.flexShrink,
|
||||
flexBasis: computedStyles.flexBasis,
|
||||
alignItems: computedStyles.alignItems,
|
||||
justifyContent: computedStyles.justifyContent,
|
||||
gap: computedStyles.gap,
|
||||
gridTemplateColumns: computedStyles.gridTemplateColumns,
|
||||
gridTemplateRows: computedStyles.gridTemplateRows,
|
||||
gridTemplateAreas: computedStyles.gridTemplateAreas,
|
||||
gridTemplate: computedStyles.gridTemplate,
|
||||
gridAutoFlow: computedStyles.gridAutoFlow,
|
||||
gridAutoColumns: computedStyles.gridAutoColumns,
|
||||
gridAutoRows: computedStyles.gridAutoRows,
|
||||
gridColumn: computedStyles.gridColumn,
|
||||
gridRow: computedStyles.gridRow,
|
||||
gridArea: computedStyles.gridArea,
|
||||
grid: computedStyles.grid,
|
||||
};
|
||||
// Try to find matching data path
|
||||
const dataPath = findDataPath(slideData, trimmedText);
|
||||
|
|
|
|||
|
|
@ -81,8 +81,11 @@ const createCacheKey = (groupName: string, fileName: string): string =>
|
|||
const compileCustomLayout = (layoutCode: string, React: any, z: any) => {
|
||||
const cleanCode = layoutCode
|
||||
.replace(/import\s+React\s+from\s+'react';?/g, "")
|
||||
.replace(/import\s*{\s*z\s*}\s*from\s+'zod';?/g, "");
|
||||
|
||||
.replace(/import\s*{\s*z\s*}\s*from\s+'zod';?/g, "")
|
||||
.replace(/import\s+.*\s+from\s+['"]zod['"];?/g, "")
|
||||
// remove every zod import (any style)
|
||||
.replace(/import\s+.*\s+from\s+['"]zod['"];?/g, "")
|
||||
.replace(/const\s+[^=]*=\s*require\(['"]zod['"]\);?/g, "");
|
||||
const compiled = Babel.transform(cleanCode, {
|
||||
presets: [
|
||||
["react", { runtime: "classic" }],
|
||||
|
|
@ -93,8 +96,9 @@ const compileCustomLayout = (layoutCode: string, React: any, z: any) => {
|
|||
|
||||
const factory = new Function(
|
||||
"React",
|
||||
"z",
|
||||
"_z",
|
||||
`
|
||||
const z = _z;
|
||||
${compiled}
|
||||
|
||||
/* everything declared in the string is in scope here */
|
||||
|
|
@ -108,7 +112,7 @@ const compileCustomLayout = (layoutCode: string, React: any, z: any) => {
|
|||
};
|
||||
`
|
||||
);
|
||||
|
||||
// globalThis.z = z;
|
||||
return factory(React, z);
|
||||
};
|
||||
|
||||
|
|
@ -120,6 +124,7 @@ export const LayoutProvider: React.FC<{
|
|||
const [error, setError] = useState<string | null>(null);
|
||||
const [isPreloading, setIsPreloading] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
console.log("🔍 layoutData", layoutData);
|
||||
|
||||
const buildData = async (groupedLayoutsData: GroupedLayoutsResponse[]) => {
|
||||
const layouts: LayoutInfo[] = [];
|
||||
|
|
@ -343,6 +348,7 @@ export const LayoutProvider: React.FC<{
|
|||
const customGroup = customGroupData.presentations;
|
||||
console.log("🔍 customGroup", customGroup);
|
||||
for (const group of customGroup) {
|
||||
console.log("🔍 group", group);
|
||||
const groupName = `custom-${group.presentation_id}`;
|
||||
fullDataByGroup.set(groupName, []);
|
||||
if (!layoutsByGroup.has(groupName)) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react'
|
||||
import Header from '@/app/dashboard/components/Header'
|
||||
import { Metadata } from 'next'
|
||||
import OutlinePage from './components/OutlinePage'
|
||||
import Header from '@/components/Header'
|
||||
export const metadata: Metadata = {
|
||||
title: "Outline Presentation",
|
||||
description: "Customize and organize your presentation outline. Drag and drop slides, add charts, and generate your presentation with ease.",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
import { useEffect, useRef } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { clearPresentationData, setPresentationData, setStreaming } from "@/store/slices/presentationGeneration";
|
||||
import {
|
||||
clearPresentationData,
|
||||
setPresentationData,
|
||||
setStreaming,
|
||||
} from "@/store/slices/presentationGeneration";
|
||||
import { jsonrepair } from "jsonrepair";
|
||||
import { RootState } from "@/store/store";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export const usePresentationStreaming = (
|
||||
presentationId: string,
|
||||
|
|
@ -11,7 +16,9 @@ export const usePresentationStreaming = (
|
|||
setError: (error: boolean) => void,
|
||||
fetchUserSlides: () => void
|
||||
) => {
|
||||
const { presentationData } = useSelector((state: RootState) => state.presentationGeneration);
|
||||
const { presentationData } = useSelector(
|
||||
(state: RootState) => state.presentationGeneration
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const previousSlidesLength = useRef(0);
|
||||
|
|
@ -64,7 +71,7 @@ export const usePresentationStreaming = (
|
|||
dispatch(setStreaming(false));
|
||||
setLoading(false);
|
||||
eventSource.close();
|
||||
|
||||
|
||||
// Remove stream parameter from URL
|
||||
const newUrl = new URL(window.location.href);
|
||||
newUrl.searchParams.delete("stream");
|
||||
|
|
@ -81,12 +88,20 @@ export const usePresentationStreaming = (
|
|||
setLoading(false);
|
||||
dispatch(setStreaming(false));
|
||||
eventSource.close();
|
||||
|
||||
|
||||
// Remove stream parameter from URL
|
||||
const newUrl = new URL(window.location.href);
|
||||
newUrl.searchParams.delete("stream");
|
||||
window.history.replaceState({}, "", newUrl.toString());
|
||||
break;
|
||||
case "error":
|
||||
eventSource.close();
|
||||
toast.error("Error in outline streaming", {
|
||||
description:
|
||||
data.detail ||
|
||||
"Failed to connect to the server. Please try again.",
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -102,7 +117,7 @@ export const usePresentationStreaming = (
|
|||
if (stream) {
|
||||
initializeStream();
|
||||
} else {
|
||||
if(!presentationData || presentationData.slides.length === 0){
|
||||
if (!presentationData || presentationData.slides.length === 0) {
|
||||
fetchUserSlides();
|
||||
}
|
||||
}
|
||||
|
|
@ -113,4 +128,4 @@ export const usePresentationStreaming = (
|
|||
}
|
||||
};
|
||||
}, [presentationId, stream, dispatch, setLoading, setError, fetchUserSlides]);
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -88,11 +88,11 @@ const FontManager: React.FC<FontManagerProps> = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<Card className="mb-6">
|
||||
<Card className="my-6">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-xl flex items-center gap-2">
|
||||
<Type className="w-6 h-6" />
|
||||
Global Font Management
|
||||
Font Management
|
||||
</CardTitle>
|
||||
<p className="text-sm text-gray-600">
|
||||
Manage fonts across all slides. Upload fonts once and they'll be
|
||||
|
|
@ -158,7 +158,7 @@ const FontManager: React.FC<FontManagerProps> = ({
|
|||
variant="outline"
|
||||
disabled={uploadingFonts.has(fontName)}
|
||||
onClick={() => fileInputRefs.current[fontName]?.click()}
|
||||
className="text-xs bg-blue-600 text-white hover:bg-blue-700 border-blue-600"
|
||||
className="text-xs bg-blue-600 text-white hover:text-white hover:bg-blue-700 border-blue-600"
|
||||
>
|
||||
{uploadingFonts.has(fontName) ? (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { v4 as uuidv4 } from "uuid";
|
|||
import EachSlide from "./components/EachSlide";
|
||||
import FontManager from "./components/FontManager";
|
||||
import Header from "@/components/Header";
|
||||
import { useLayout } from "../(presentation-generator)/context/LayoutContext";
|
||||
|
||||
// Types
|
||||
interface SlideData {
|
||||
|
|
@ -50,6 +51,7 @@ interface FontData {
|
|||
}
|
||||
|
||||
const CustomLayoutPage = () => {
|
||||
const { refetch } = useLayout();
|
||||
// State management
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
const [isProcessingPptx, setIsProcessingPptx] = useState(false);
|
||||
|
|
@ -311,6 +313,7 @@ const CustomLayoutPage = () => {
|
|||
);
|
||||
|
||||
toast.success(`Layout saved successfully`);
|
||||
refetch();
|
||||
setIsLayoutSaved(true);
|
||||
} catch (error) {
|
||||
console.error("Error saving layout:", error);
|
||||
|
|
@ -559,8 +562,6 @@ const CustomLayoutPage = () => {
|
|||
const completedSlides = slides.filter(
|
||||
(slide) => slide.processed || slide.error
|
||||
).length;
|
||||
const progressPercentage =
|
||||
slides.length > 0 ? Math.round((completedSlides / slides.length) * 100) : 0;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 ">
|
||||
|
|
@ -577,17 +578,6 @@ const CustomLayoutPage = () => {
|
|||
</p>
|
||||
</div>
|
||||
|
||||
{/* Global Font Management */}
|
||||
{fontsData && (
|
||||
<FontManager
|
||||
fontsData={fontsData}
|
||||
UploadedFonts={UploadedFonts}
|
||||
uploadFont={uploadFont}
|
||||
removeFont={removeFont}
|
||||
getAllUnsupportedFonts={getAllUnsupportedFonts}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Upload Section */}
|
||||
<Card className="w-full">
|
||||
<CardHeader>
|
||||
|
|
@ -599,6 +589,14 @@ const CustomLayoutPage = () => {
|
|||
Select a PowerPoint file (.pptx) to process. Maximum file size:
|
||||
50MB
|
||||
</CardDescription>
|
||||
{slides.length > 0 && (
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
{slides.some((s) => s.processing) && (
|
||||
<Loader2 className="w-6 h-6 animate-spin text-blue-600" />
|
||||
)}
|
||||
{completedSlides}/{slides.length} slides completed
|
||||
</div>
|
||||
)}
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{!selectedFile ? (
|
||||
|
|
@ -663,28 +661,15 @@ const CustomLayoutPage = () => {
|
|||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Progress Section */}
|
||||
{slides.length > 0 && (
|
||||
<Card className="mt-10">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center justify-between">
|
||||
<span>Processing Progress</span>
|
||||
<span className="text-sm font-normal text-gray-600">
|
||||
{completedSlides}/{slides.length} slides completed
|
||||
</span>
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Converting slides to HTML layouts...
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Progress value={progressPercentage} className="w-full" />
|
||||
<div className="flex justify-between text-sm text-gray-600 mt-2">
|
||||
<span>Progress: {progressPercentage}%</span>
|
||||
<span>{slides.length} total slides</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{/* Global Font Management */}
|
||||
{fontsData && (
|
||||
<FontManager
|
||||
fontsData={fontsData}
|
||||
UploadedFonts={UploadedFonts}
|
||||
uploadFont={uploadFont}
|
||||
removeFont={removeFont}
|
||||
getAllUnsupportedFonts={getAllUnsupportedFonts}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Slides Section */}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue