Merge pull request #468 from presenton/fix/ui-issues

fix/ui issues
This commit is contained in:
Shiva Raj Badu 2026-03-27 14:28:20 +05:45 committed by GitHub
commit decbbf8187
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 173 additions and 164 deletions

View file

@ -8,7 +8,7 @@ import { trackEvent, MixpanelEvent } from "@/utils/mixpanel";
const Header = () => {
const pathname = usePathname();
return (
<div className="w-full sticky top-0 z-50 py-7 ">
<div className="w-full bg-white sticky top-0 z-50 py-7 ">
<Wrapper className="px-5 sm:px-10 lg:px-20">
<div className="flex items-center justify-between py-1">
<div className="flex items-center gap-3">

View file

@ -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"
>
<div suppressHydrationWarning={true} className="flex flex-col flex-1 relative z-40">
<div
id={`dashboard-presentation-card-${id}`}
suppressHydrationWarning={true} className="flex flex-col flex-1 relative z-40">
{/* <p className=" text-xs font-syne absolute top-2 flex gap-1 capitalize items-center left-2 rounded-[100px] px-2.5 py-1 bg-[#3A3A3AF5] text-white font-semibold z-40 ">
{presentation.type}

View file

@ -12,7 +12,7 @@ interface EditableLayoutWrapperProps {
slideData: any;
isEditMode?: boolean;
properties?: any;
}
interface EditableElement {
@ -29,7 +29,7 @@ const EditableLayoutWrapper: React.FC<EditableLayoutWrapperProps> = ({
slideIndex,
slideData,
properties,
}) => {
const dispatch = useDispatch();
const containerRef = useRef<HTMLDivElement>(null);
@ -245,7 +245,7 @@ const EditableLayoutWrapper: React.FC<EditableLayoutWrapperProps> = ({
}
}
});
// Process SVG icons
svgElements.forEach((svg, index) => {
const svgEl = svg as SVGElement;

View file

@ -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"
>
<Trash className="absolute group-hover:opacity-100 opacity-0 transition-opacity z-10 w-4 h-4 top-2 right-2 text-red-500" onClick={(e) =>{
<Trash className="absolute group-hover:opacity-100 opacity-0 transition-opacity z-10 w-4 h-4 top-2 right-2 text-red-500" onClick={(e) => {
e.stopPropagation();
handleDeleteImage(image.id)
}}/>
}} />
<img
src={image.file_url || image.path}
src={image.file_url || image.path}
alt="Uploaded preview"
className="w-full h-full object-cover group-hover:scale-105 transition-transform"
/>
@ -496,7 +496,7 @@ const ImageEditor = ({
</span>
</div>
</div>
</div>
))
)}
@ -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 = ({
</div>
}
{/* Focus Point */}
{}
{ }
</div>
</TabsContent>
</Tabs>

View file

@ -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<HTMLDivElement | null>(null);
const [containerWidth, setContainerWidth] = useState<number>(0);
@ -62,12 +62,12 @@ const SlideScale = ({ slide }: { slide: any }) => {
} as React.CSSProperties}
>
<div
{/* <div
className="absolute inset-0 bg-transparent z-30 w-full h-full select-none"
aria-hidden="true"
/>
<V1ContentRender slide={slide} isEditMode={true} />
/> */}
<V1ContentRender slide={slide} isEditMode={true} theme={theme} />
</div>

View file

@ -86,7 +86,7 @@ export const V1ContentRender = ({ slide, isEditMode, theme }: { slide: any, isEd
if (isEditMode) {
return (
<SlideErrorBoundary label={`Slide ${slide.index + 1}`}>
<div ref={containerRef} className={`w-full h-full border border-[#EDEEEF] `}>
<div ref={containerRef} className={`w-full h-full `}>
<EditableLayoutWrapper
slideIndex={slide.index}

View file

@ -1,16 +0,0 @@
export const useFontLoader = ( fonts:string[]) => {
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);
};

View file

@ -57,18 +57,7 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
</span>
</div>
)}
{/* <div className="flex items-center justify-between">
<h5 className="text-lg font-medium">
Presentation Outline
</h5>
{isStreaming && (
<div className="flex items-center text-sm text-blue-600">
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-600 mr-2"></div>
Generating outlines...
</div>
)}
</div> */}
{/* Skeleton loading state */}
{isLoading && (
<div className="space-y-4 bg-white">
{[...Array(6)].map((_, index) => (
@ -93,7 +82,7 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
{/* Outlines content */}
{outlines && outlines.length > 0 && (
<div className="bg-[#F9F8F8] min-h-[calc(100vh-16rem)] p-7 relative z-20 rounded-[20px] overflow-y-auto custom_scrollbar">
<div className="bg-[#F9F8F8] p-7 relative z-20 rounded-[20px] min-h-[calc(100vh-200px)]">
<DndContext
sensors={sensors}
collisionDetection={closestCenter}

View file

@ -146,7 +146,7 @@ export function OutlineItem({
<div id={`outline-item-${index}`} className="flex flex-col basis-full gap-2">
<p className="text-black w-fit text-[10px] font-medium bg-white border border-[#EDEEEF] rounded-[80px] px-2.5">slide {index}</p>
<p className="text-black w-fit text-[10px] font-medium bg-white border border-[#EDEEEF] rounded-[80px] px-2.5">Slide {index}</p>
{/* Editable Markdown Content */}
{isStreaming ? (
isActiveStreaming ? (

View file

@ -49,54 +49,54 @@ const OutlinePage: React.FC = () => {
duration={loadingState.duration}
/>
<Wrapper className="h-full flex flex-col w-full relative px-5 sm:px-10 lg:px-20 ">
<div className="flex-grow w-full hidden-scrollbar mx-auto ">
<Tabs value={activeTab} onValueChange={setActiveTab} className="h-full flex flex-col">
<TabsList className="my-4 h-auto w-fit rounded-full border border-[#EDEEEF] bg-white p-1.5">
<TabsTrigger
value={TABS.OUTLINE}
className="rounded-full px-5 py-2 text-xs font-medium text-[#2D2D2D] shadow-none data-[state=active]:bg-[#F4F3FF] data-[state=active]:text-[#7E3AF2] data-[state=active]:shadow-none"
>
Outline & Content
</TabsTrigger>
<Separator orientation="vertical" className="h-6 mx-1" />
<TabsTrigger
value={TABS.LAYOUTS}
className="relative rounded-full px-5 py-2 text-xs font-medium text-[#2D2D2D] shadow-none data-[state=active]:bg-[#F4F3FF] data-[state=active]:text-[#7E3AF2] data-[state=active]:shadow-none"
>
Select Template
</TabsTrigger>
</TabsList>
<Wrapper className="flex flex-col w-full relative px-5 sm:px-10 lg:px-20 ">
<div className="w-full mx-auto">
<Tabs value={activeTab} onValueChange={setActiveTab} className="flex w-full flex-col">
{/* Reserves vertical space so content does not sit under the fixed tab bar */}
<div className="h-[4.75rem] shrink-0 sm:h-[5rem]" aria-hidden />
<div className="fixed top-26 left-0 right-0 z-40 pb-2">
<div className="mx-auto w-full max-w-[1440px] px-5 sm:px-10 lg:px-20">
<TabsList className="my-4 h-auto w-fit rounded-full border border-[#EDEEEF] bg-white p-1.5">
<TabsTrigger
value={TABS.OUTLINE}
className="rounded-full px-5 py-2 text-xs font-medium text-[#2D2D2D] shadow-none data-[state=active]:bg-[#F4F3FF] data-[state=active]:text-[#7E3AF2] data-[state=active]:shadow-none"
>
Outline & Content
</TabsTrigger>
<Separator orientation="vertical" className="h-6 mx-1" />
<TabsTrigger
value={TABS.LAYOUTS}
className="relative rounded-full px-5 py-2 text-xs font-medium text-[#2D2D2D] shadow-none data-[state=active]:bg-[#F4F3FF] data-[state=active]:text-[#7E3AF2] data-[state=active]:shadow-none"
>
Select Template
</TabsTrigger>
</TabsList>
</div>
</div>
<div className="flex-grow w-full mx-auto">
<TabsContent value={TABS.OUTLINE} className="h-[calc(100vh-15rem)] overflow-y-auto hide-scrollbar"
>
<div>
<OutlineContent
outlines={outlines}
isLoading={streamState.isLoading}
isStreaming={streamState.isStreaming}
activeSlideIndex={streamState.activeSlideIndex}
highestActiveIndex={streamState.highestActiveIndex}
onDragEnd={handleDragEnd}
onAddSlide={handleAddSlide}
/>
</div>
<div className="w-full mx-auto">
<TabsContent value={TABS.OUTLINE} className="mt-0">
<OutlineContent
outlines={outlines}
isLoading={streamState.isLoading}
isStreaming={streamState.isStreaming}
activeSlideIndex={streamState.activeSlideIndex}
highestActiveIndex={streamState.highestActiveIndex}
onDragEnd={handleDragEnd}
onAddSlide={handleAddSlide}
/>
</TabsContent>
<TabsContent value={TABS.LAYOUTS} className="h-[calc(100vh-16rem)] bg-white overflow-y-auto hide-scrollbar">
<div>
<TemplateSelection
selectedTemplate={selectedTemplate}
onSelectTemplate={setSelectedTemplate}
/>
</div>
<TabsContent value={TABS.LAYOUTS} className="mt-0 bg-white">
<TemplateSelection
selectedTemplate={selectedTemplate}
onSelectTemplate={setSelectedTemplate}
/>
</TabsContent>
</div>
</Tabs>
{/* Fixed Button */}
<div className="absolute bottom-[26px] right-[26px] z-50">
<div className="fixed bottom-[26px] right-[26px] z-50">
<GenerateButton
outlineCount={outlines.length}
loadingState={loadingState}

View file

@ -246,7 +246,7 @@ const PresentationHeader = ({
return (
<>
<div className="py-7 sticky top-0 bg-white z-50 mb-[17px] font-syne flex justify-between items-center">
<h2 className="text-lg text-[#101323] font-unbounded "><MarkdownRenderer content={presentationData?.title || "Presentation"} className="mb-0 w-[600px] truncate text-sm text-[#101323] " /></h2>
<h2 className="text-lg text-[#101323] font-unbounded "><MarkdownRenderer content={presentationData?.title || "Presentation"} className="mb-0 max-w-[600px] overflow-ellipsis line-clamp-1 text-sm text-[#101323] " /></h2>
<div className="flex items-center gap-2.5">
{isPresentationSaving && <div className="flex items-center gap-2">

View file

@ -87,14 +87,7 @@ const PresentationPage: React.FC<PresentationPageProps> = ({
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<PresentationPageProps> = ({
style={{
background: "#ffffff",
}}
id="presentation-slides-wrapper"
className="flex gap-6 relative "
>
<div className="w-[200px]">
@ -144,10 +138,10 @@ const PresentationPage: React.FC<PresentationPageProps> = ({
/>
</div>
<div className=" w-full h-[calc(100vh-20px)] hide-scrollbar pr-[25px] overflow-y-auto">
<div className=" w-full h-[calc(100vh-20px)] pr-[25px] overflow-y-auto">
<PresentationHeader presentation_id={presentation_id} isPresentationSaving={isSaving} currentSlide={selectedSlide} />
<div
id="presentation-slides-wrapper"
style={{
background: "rgba(255, 255, 255, 0.10)",
boxShadow: "0 0 20.01px 0 rgba(122, 90, 248, 0.16) inset",

View file

@ -132,7 +132,7 @@ const SidePanel = ({
<Separator orientation="horizontal" className="my-6 " />
<div
className={`
fixed xl:relative h-full z-50 xl:z-auto
relative bg-[#F6F6F9] h-full z-50 xl:z-auto
transition-all duration-300 ease-in-out
`}
>
@ -141,14 +141,14 @@ const SidePanel = ({
className="w-full h-[calc(100vh-120px)] hide-scrollbar overflow-hidden slide-theme "
>
<p className="text-xl font-normal pb-3.5 text-[#000000]">Slides</p>
<p className="text-xl font-normal pb-3.5 text-[#000000]">Slides ({presentationData?.slides?.length})</p>
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
>
<div className=" overflow-y-auto hide-scrollbar h-[calc(100%-140px)] space-y-3.5">
<div className=" overflow-y-auto w-full hide-scrollbar h-[calc(100%-140px)] space-y-3.5">
{isStreaming ? (
presentationData &&
presentationData?.slides.map((slide: any, index: number) => (

View file

@ -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 `}
>
<V1ContentRender slide={slide} isEditMode={true} theme={null} />
{/* <V1ContentRender slide={slide} isEditMode={true} theme={null} /> */}
<SlideScale slide={slide} theme={presentationData?.theme || null} />
{!showNewSlideSelection && (
<div className="group-hover:opacity-100 hidden md:block opacity-0 transition-opacity my-4 duration-300">
<ToolTip content="Add new slide below">

View file

@ -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,

View file

@ -93,6 +93,12 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderOption> = {
};
export const LLM_PROVIDERS: Record<string, LLMProviderOption> = {
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<string, LLMProviderOption> = {
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 = [

View file

@ -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`);

View file

@ -86,7 +86,7 @@ export const V1ContentRender = ({ slide, isEditMode, theme }: { slide: any, isEd
if (isEditMode) {
return (
<SlideErrorBoundary label={`Slide ${slide.index + 1}`}>
<div ref={containerRef} className={`w-full h-full border border-[#EDEEEF] `}>
<div ref={containerRef} className={` `}>
<EditableLayoutWrapper
slideIndex={slide.index}

View file

@ -93,7 +93,7 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
{/* Outlines content */}
{outlines && outlines.length > 0 && (
<div className="bg-[#F9F8F8] min-h-[calc(100vh-16rem)] p-7 relative z-20 rounded-[20px] overflow-y-auto custom_scrollbar">
<div className="bg-[#F9F8F8] p-7 relative z-20 rounded-[20px]">
<DndContext
sensors={sensors}
collisionDetection={closestCenter}

View file

@ -49,54 +49,53 @@ const OutlinePage: React.FC = () => {
duration={loadingState.duration}
/>
<Wrapper className="h-full flex flex-col w-full relative px-5 sm:px-10 lg:px-20 ">
<div className="flex-grow w-full hidden-scrollbar mx-auto ">
<Tabs value={activeTab} onValueChange={setActiveTab} className="h-full flex flex-col">
<TabsList className="my-4 h-auto w-fit rounded-full border border-[#EDEEEF] bg-white p-1.5">
<TabsTrigger
value={TABS.OUTLINE}
className="rounded-full px-5 py-2 text-xs font-medium text-[#2D2D2D] shadow-none data-[state=active]:bg-[#F4F3FF] data-[state=active]:text-[#7E3AF2] data-[state=active]:shadow-none"
>
Outline & Content
</TabsTrigger>
<Separator orientation="vertical" className="h-6 mx-1" />
<TabsTrigger
value={TABS.LAYOUTS}
className="relative rounded-full px-5 py-2 text-xs font-medium text-[#2D2D2D] shadow-none data-[state=active]:bg-[#F4F3FF] data-[state=active]:text-[#7E3AF2] data-[state=active]:shadow-none"
>
Select Template
</TabsTrigger>
</TabsList>
<Wrapper className="flex flex-col w-full relative px-5 sm:px-10 lg:px-20 ">
<div className="w-full mx-auto">
<Tabs value={activeTab} onValueChange={setActiveTab} className="flex w-full flex-col">
<div className="h-[4.75rem] shrink-0 sm:h-[5rem]" aria-hidden />
<div className="fixed top-26 left-0 right-0 z-40 bg-[#FAFAFA] pb-2">
<div className="mx-auto w-full max-w-[1440px] px-5 sm:px-10 lg:px-20">
<TabsList className="my-4 h-auto w-fit rounded-full border border-[#EDEEEF] bg-white p-1.5">
<TabsTrigger
value={TABS.OUTLINE}
className="rounded-full px-5 py-2 text-xs font-medium text-[#2D2D2D] shadow-none data-[state=active]:bg-[#F4F3FF] data-[state=active]:text-[#7E3AF2] data-[state=active]:shadow-none"
>
Outline & Content
</TabsTrigger>
<Separator orientation="vertical" className="h-6 mx-1" />
<TabsTrigger
value={TABS.LAYOUTS}
className="relative rounded-full px-5 py-2 text-xs font-medium text-[#2D2D2D] shadow-none data-[state=active]:bg-[#F4F3FF] data-[state=active]:text-[#7E3AF2] data-[state=active]:shadow-none"
>
Select Template
</TabsTrigger>
</TabsList>
</div>
</div>
<div className="flex-grow w-full mx-auto">
<TabsContent value={TABS.OUTLINE} className="h-[calc(100vh-15rem)] overflow-y-auto hide-scrollbar"
>
<div>
<OutlineContent
outlines={outlines}
isLoading={streamState.isLoading}
isStreaming={streamState.isStreaming}
activeSlideIndex={streamState.activeSlideIndex}
highestActiveIndex={streamState.highestActiveIndex}
onDragEnd={handleDragEnd}
onAddSlide={handleAddSlide}
/>
</div>
<div className="w-full mx-auto">
<TabsContent value={TABS.OUTLINE} className="mt-0">
<OutlineContent
outlines={outlines}
isLoading={streamState.isLoading}
isStreaming={streamState.isStreaming}
activeSlideIndex={streamState.activeSlideIndex}
highestActiveIndex={streamState.highestActiveIndex}
onDragEnd={handleDragEnd}
onAddSlide={handleAddSlide}
/>
</TabsContent>
<TabsContent value={TABS.LAYOUTS} className="h-[calc(100vh-16rem)] bg-white overflow-y-auto hide-scrollbar">
<div>
<TemplateSelection
selectedTemplate={selectedTemplate}
onSelectTemplate={setSelectedTemplate}
/>
</div>
<TabsContent value={TABS.LAYOUTS} className="mt-0 bg-white">
<TemplateSelection
selectedTemplate={selectedTemplate}
onSelectTemplate={setSelectedTemplate}
/>
</TabsContent>
</div>
</Tabs>
{/* Fixed Button */}
<div className="absolute bottom-[26px] right-[26px] z-50">
<div className="fixed bottom-[26px] right-[26px] z-50">
<GenerateButton
outlineCount={outlines.length}
loadingState={loadingState}

View file

@ -5,7 +5,6 @@ import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { ArrowLeft, Home, Loader2, Trash2 } from "lucide-react";
import { useFontLoader } from "../../hooks/useFontLoader";
import { MixpanelEvent, trackEvent } from "@/utils/mixpanel";
import TemplateService from "../../services/api/template";
import Header from "../../(dashboard)/dashboard/components/Header";