feat: improve New Slide and Presentation Mode components, enhance settings page with image and text provider summaries

This commit is contained in:
shiva raj badu 2026-03-02 18:37:48 +05:45
parent 3cdbf246ab
commit 6d8c34fd5e
No known key found for this signature in database
9 changed files with 89 additions and 20 deletions

View file

@ -7,11 +7,12 @@ import { Switch } from '@/components/ui/switch'
import { cn } from '@/lib/utils'
import { LLMConfig } from '@/types/llm_config'
import { DALLE_3_QUALITY_OPTIONS, GPT_IMAGE_1_5_QUALITY_OPTIONS, IMAGE_PROVIDERS } from '@/utils/providerConstants'
import { Check, ChevronsUpDown } from 'lucide-react'
import { Check, ChevronUp, Eye, EyeOff } from 'lucide-react'
import React, { useState } from 'react'
const ImageProvider = ({ llmConfig, setLlmConfig }: { llmConfig: LLMConfig, setLlmConfig: (config: any) => void }) => {
const [openImageProviderSelect, setOpenImageProviderSelect] = useState(false);
const [showApiKey, setShowApiKey] = useState(false);
const isImageGenerationDisabled = llmConfig.DISABLE_IMAGE_GENERATION ?? false;
const handleChangeImageGenerationDisabled = (value: boolean) => {
setLlmConfig((prev: any) => ({
@ -173,7 +174,7 @@ const ImageProvider = ({ llmConfig, setLlmConfig }: { llmConfig: LLMConfig, setL
: "Select image provider"}
</span>
</div>
<ChevronsUpDown className="w-4 h-4 text-gray-500" />
<ChevronUp className="w-4 h-4 text-gray-500" />
</Button>
</PopoverTrigger>
<PopoverContent
@ -277,7 +278,7 @@ const ImageProvider = ({ llmConfig, setLlmConfig }: { llmConfig: LLMConfig, setL
</label>
<div className="relative">
<input
type="text"
type={showApiKey ? 'text' : 'password'}
placeholder={`Enter your ${provider.apiKeyFieldLabel}`}
className="w-full px-4 py-2.5 h-12 outline-none border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-colors"
value={getFieldValue(provider.apiKeyField)}
@ -288,6 +289,13 @@ const ImageProvider = ({ llmConfig, setLlmConfig }: { llmConfig: LLMConfig, setL
)
}
/>
<button
type="button"
onClick={() => setShowApiKey((prev) => !prev)}
className='absolute right-2 top-1/2 -translate-y-1/2 bg-white px-2 py-1 cursor-pointer'
>
{showApiKey ? <Eye className='w-4 h-4 text-gray-500' /> : <EyeOff className='w-4 h-4 text-gray-500' />}
</button>
</div>
</div>

View file

@ -15,6 +15,7 @@ import { trackEvent, MixpanelEvent } from "@/utils/mixpanel";
import SettingSideBar from "./SettingSideBar";
import TextProvider from "./TextProvider";
import ImageProvider from "./ImageProvider";
import { IMAGE_PROVIDERS, LLM_PROVIDERS } from "@/utils/providerConstants";
// Button state interface
interface ButtonState {
@ -156,6 +157,31 @@ const SettingsPage = () => {
}
const textProviderKey = llmConfig.LLM || "openai";
const textProviderLabel =
LLM_PROVIDERS[textProviderKey]?.label || textProviderKey;
const selectedTextModel =
textProviderKey === "openai"
? llmConfig.OPENAI_MODEL
: textProviderKey === "google"
? llmConfig.GOOGLE_MODEL
: textProviderKey === "anthropic"
? llmConfig.ANTHROPIC_MODEL
: textProviderKey === "ollama"
? llmConfig.OLLAMA_MODEL
: textProviderKey === "custom"
? llmConfig.CUSTOM_MODEL
: "";
const textSummary = selectedTextModel
? `${textProviderLabel} (${selectedTextModel})`
: textProviderLabel;
const imageSummary = llmConfig.DISABLE_IMAGE_GENERATION
? "Image generation disabled"
: llmConfig.IMAGE_PROVIDER
? IMAGE_PROVIDERS[llmConfig.IMAGE_PROVIDER]?.label || llmConfig.IMAGE_PROVIDER
: "No image provider";
return (
<div className="h-screen font-instrument_sans flex flex-col overflow-hidden relative">
<div
@ -171,10 +197,13 @@ const SettingsPage = () => {
<SettingSideBar mode={mode} setMode={setMode} selectedProvider={selectedProvider} setSelectedProvider={setSelectedProvider} />
<div className="w-full">
<div className="sticky top-0 right-0 z-50 py-[28px] backdrop-blur mb-4 ">
<div className="flex xl:flex-row flex-col gap-6 xl:gap-0 items-center justify-between">
<div className="flex gap-3 items-center ">
<h3 className=" text-[28px] tracking-[-0.84px] font-unbounded font-normal text-black flex items-center gap-2">
Settings
</h3>
<p className="text-[10px] px-2.5 py-0.5 rounded-[50px] text-[#7A5AF8] border border-[#EDEEEF] font-medium ">
{textSummary} · {imageSummary}
</p>
</div>
</div>

View file

@ -5,7 +5,7 @@ import { Switch } from '@/components/ui/switch';
import { cn } from '@/lib/utils';
import { LLMConfig } from '@/types/llm_config';
import { LLM_PROVIDERS } from '@/utils/providerConstants';
import { Check, ChevronsUpDown, Loader2, Eye, EyeOff } from 'lucide-react';
import { Check, ChevronsUpDown, Loader2, Eye, EyeOff, ChevronUp } from 'lucide-react';
import React, { useEffect, useMemo, useRef, useState } from 'react'
import { toast } from 'sonner';
@ -245,7 +245,7 @@ const TextProvider = ({
: "Select text provider"}
</span>
</div>
<ChevronsUpDown className="w-4 h-4 text-gray-500" />
<ChevronUp className="w-4 h-4 text-gray-500" />
</Button>
</PopoverTrigger>
<PopoverContent
@ -393,7 +393,7 @@ const TextProvider = ({
: "Select a model"}
</span>
<ChevronsUpDown className="w-4 h-4 text-gray-500" />
<ChevronUp className="w-4 h-4 text-gray-500" />
</Button>
</PopoverTrigger>
<PopoverContent

View file

@ -57,7 +57,7 @@ const OutlinePage: React.FC = () => {
/>
<Wrapper className="h-full flex flex-col w-full relative">
<div className="flex-grow w-full overflow-y-hidden mx-auto mt-6">
<div className="flex-grow w-full overflow-y-hidden 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-[#DFDFE1] bg-[#F8F8F9] p-1.5">
<TabsTrigger

View file

@ -2,9 +2,8 @@
import React, { useEffect, useState, memo, useCallback } from "react";
import { useDispatch } from "react-redux";
import { addNewSlide } from "@/store/slices/presentationGeneration";
import { Loader2, Trash } from "lucide-react";
import { Loader2, X } from "lucide-react";
import { v4 as uuidv4 } from "uuid";
import { Trash2 } from 'lucide-react';
import { toast } from 'sonner';
import { getCustomTemplateDetails } from "@/app/hooks/useCustomTemplates";
import { getTemplatesByTemplateName } from "@/app/presentation-templates";
@ -95,7 +94,7 @@ const NewSlideV1 = ({
<div className="my-6 w-full bg-gray-50 p-8 max-w-[1280px]">
<div className="flex justify-between items-center mb-8">
<h2 className="text-2xl font-semibold">Select a Slide Layout</h2>
<Trash
<X
onClick={() => setShowNewSlideSelection(false)}
className="text-gray-500 text-2xl cursor-pointer"
/>
@ -111,7 +110,7 @@ const NewSlideV1 = ({
<div className="my-6 w-full bg-gray-50 p-8 max-w-[1280px]">
<div className="flex justify-between items-center mb-8">
<h2 className="text-2xl font-semibold">Select a Slide Layout</h2>
<Trash2
<X
onClick={() => setShowNewSlideSelection(false)}
className="text-gray-500 text-2xl cursor-pointer"
/>

View file

@ -10,8 +10,9 @@ import {
EyeOff,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Slide } from "../types/slide";
import { V1ContentRender } from "./V1ContentRender";
import { Slide } from "../../types/slide";
import { V1ContentRender } from "../../components/V1ContentRender";
interface PresentationModeProps {

View file

@ -3,13 +3,13 @@ import React, { useState } from "react";
import { useSelector } from "react-redux";
import { RootState } from "@/store/store";
import { Skeleton } from "@/components/ui/skeleton";
import PresentationMode from "../../components/PresentationMode";
import PresentationMode from "./PresentationMode";
import SidePanel from "./SidePanel";
import SlideContent from "./SlideContent";
import { Button } from "@/components/ui/button";
import { usePathname } from "next/navigation";
import { trackEvent, MixpanelEvent } from "@/utils/mixpanel";
import { AlertCircle, Loader2 } from "lucide-react";
import { AlertCircle } from "lucide-react";
import {
usePresentationStreaming,
usePresentationData,
@ -79,7 +79,6 @@ 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];
@ -132,6 +131,7 @@ const PresentationPage: React.FC<PresentationPageProps> = ({
<SidePanel
selectedSlide={selectedSlide}
onSlideClick={handleSlideClick}
presentationId={presentation_id}
loading={loading}
/>

View file

@ -1,5 +1,5 @@
"use client";
import React, { } from "react";
import React, { useState } from "react";
import { Plus } from "lucide-react";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "@/store/store";
@ -22,10 +22,12 @@ import { SortableSlide } from "./SortableSlide";
import SlideScale from "../../components/PresentationRender";
import { Separator } from "@/components/ui/separator";
import { useRouter } from "next/navigation";
import NewSlide from "./NewSlide";
interface SidePanelProps {
selectedSlide: number;
onSlideClick: (index: number) => void;
presentationId: string;
loading: boolean;
}
@ -33,11 +35,13 @@ interface SidePanelProps {
const SidePanel = ({
selectedSlide,
onSlideClick,
presentationId,
loading,
}: SidePanelProps) => {
const router = useRouter();
const [showNewSlideSelection, setShowNewSlideSelection] = useState(false);
const { presentationData, isStreaming } = useSelector(
(state: RootState) => state.presentationGeneration
@ -45,6 +49,18 @@ const SidePanel = ({
const dispatch = useDispatch();
const lastSlideIndex = presentationData?.slides?.length
? presentationData.slides.length - 1
: 0;
const lastSlideTemplateId = presentationData?.slides?.[lastSlideIndex]?.layout
? presentationData.slides[lastSlideIndex].layout.split(":")[0]
: "";
const handleAddSlideClick = () => {
if (!presentationData?.slides?.length || isStreaming) return;
setShowNewSlideSelection(true);
};
@ -174,12 +190,28 @@ const SidePanel = ({
</DndContext>
<button className=" pt-6 gap-2 flex flex-col py-2 duration-300 items-center justify-center rounded-lg cursor-pointer mx-auto">
<button
type="button"
onClick={handleAddSlideClick}
className="pt-6 gap-2 flex flex-col py-2 duration-300 items-center justify-center rounded-lg cursor-pointer mx-auto"
>
<Plus className="w-3.5 h-3.5" />
<span className="text-[11px] font-normal text-[#000000]">Add Slide</span>
</button>
</div>
</div>
{showNewSlideSelection && lastSlideTemplateId && (
<div className="fixed inset-0 z-[60] bg-black/50 overflow-y-auto p-4">
<div className="min-h-full flex items-start justify-center py-8">
<NewSlide
index={lastSlideIndex}
templateID={lastSlideTemplateId}
setShowNewSlideSelection={setShowNewSlideSelection}
presentationId={presentationId}
/>
</div>
</div>
)}
</div>
);
};

View file

@ -18,9 +18,9 @@ import {
} from "@/store/slices/presentationGeneration";
import { usePathname } from "next/navigation";
import { trackEvent, MixpanelEvent } from "@/utils/mixpanel";
import NewSlide from "../../components/NewSlide";
import { addToHistory } from "@/store/slices/undoRedoSlice";
import { V1ContentRender } from "../../components/V1ContentRender";
import NewSlide from "./NewSlide";
interface SlideContentProps {
slide: any;