From d8a4a565d38dbc1b869d6430a5655bbfbc9d7db0 Mon Sep 17 00:00:00 2001 From: shiva raj badu Date: Tue, 24 Feb 2026 17:26:49 +0545 Subject: [PATCH] feat: Enhance dashboard and settings layout with new components and improved UI elements --- .../dashboard/components/DashboardPage.tsx | 46 ++- .../dashboard/components/Header.tsx | 2 +- .../dashboard/components/PresentationGrid.tsx | 2 +- .../(dashboard)/dashboard/loading.tsx | 31 +- .../(dashboard)/layout.tsx | 5 +- .../(dashboard)/settings/SettingPage.tsx | 32 +- .../(dashboard)/settings/SettingSideBar.tsx | 59 ++++ .../templates/components/TemplatePanel.tsx | 36 ++- .../outline/components/CustomTemplateCard.tsx | 63 +--- .../outline/components/OutlinePage.tsx | 6 +- .../outline/components/TemplateSelection.tsx | 288 ++++++++++-------- .../components/PresentationHeader.tsx | 71 ++--- .../presentation/components/ThemeSelector.tsx | 120 ++++++++ .../template-preview/page.tsx | 3 +- .../upload/components/UploadPage.tsx | 2 +- servers/nextjs/components/LLMSelection.tsx | 39 +-- .../public/providers/image-provider.png | Bin 0 -> 30564 bytes servers/nextjs/public/providers/openai.png | Bin 0 -> 7049 bytes 18 files changed, 503 insertions(+), 302 deletions(-) create mode 100644 servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingSideBar.tsx create mode 100644 servers/nextjs/app/(presentation-generator)/presentation/components/ThemeSelector.tsx create mode 100644 servers/nextjs/public/providers/image-provider.png create mode 100644 servers/nextjs/public/providers/openai.png diff --git a/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/DashboardPage.tsx b/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/DashboardPage.tsx index 210fe07f..1e5de6ec 100644 --- a/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/DashboardPage.tsx +++ b/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/DashboardPage.tsx @@ -4,6 +4,8 @@ import React, { useState, useEffect } from "react"; import { DashboardApi } from "@/app/(presentation-generator)/services/api/dashboard"; import { PresentationGrid } from "@/app/(presentation-generator)/(dashboard)/dashboard/components/PresentationGrid"; +import Link from "next/link"; +import { ChevronRight } from "lucide-react"; @@ -44,7 +46,49 @@ const DashboardPage: React.FC = () => { }; return ( -
+
+
+
+

+ + Slide Presentations +

+
+ + + + { + + New presentation + New + + } + {/* { + + New Themes + New + + + } */} +
+
+
{
- {(pathname !== "/upload" && pathname !== "/dashboard") && } + {/* {(pathname !== "/upload" && pathname !== "/dashboard") && } */} trackEvent(MixpanelEvent.Navigation, { from: pathname, to: "/dashboard" })}> +
diff --git a/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/loading.tsx b/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/loading.tsx index 41cd811c..919a55ad 100644 --- a/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/loading.tsx +++ b/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/loading.tsx @@ -1,23 +1,26 @@ -import { Skeleton } from '@/components/ui/skeleton' import React from 'react' const loading = () => { return ( -
- - -
- - -
- { - Array.from({ length: 8 }).map((_, index) => ( - - )) - } +
+
+
+
+
+
+
+
- + {[...Array(15)].map((_, i) => ( +
+
+
+
+
+
+
+ ))}
) } diff --git a/servers/nextjs/app/(presentation-generator)/(dashboard)/layout.tsx b/servers/nextjs/app/(presentation-generator)/(dashboard)/layout.tsx index 3a67a294..a1b9172e 100644 --- a/servers/nextjs/app/(presentation-generator)/(dashboard)/layout.tsx +++ b/servers/nextjs/app/(presentation-generator)/(dashboard)/layout.tsx @@ -1,13 +1,12 @@ import React from 'react' import DashboardSidebar from './Components/DashboardSidebar' -import DashboardNav from './Components/DashboardNav' const layout = ({ children }: { children: React.ReactNode }) => { return ( -
+
- + {children}
diff --git a/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingPage.tsx b/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingPage.tsx index 33e2a808..c9cc3f37 100644 --- a/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingPage.tsx +++ b/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingPage.tsx @@ -14,6 +14,7 @@ import LLMProviderSelection from "@/components/LLMSelection"; import Header from "../dashboard/components/Header"; import { LLMConfig } from "@/types/llm_config"; import { trackEvent, MixpanelEvent } from "@/utils/mixpanel"; +import SettingSideBar from "./SettingSideBar"; // Button state interface interface ButtonState { @@ -28,6 +29,8 @@ interface ButtonState { const SettingsPage = () => { const router = useRouter(); const pathname = usePathname(); + const [mode, setMode] = useState<'nanobanana' | 'presenton'>('presenton') + const [selectedProvider, setSelectedProvider] = useState<'text-provider' | 'image-provider'>('text-provider') const userConfigState = useSelector((state: RootState) => state.userConfig); const [llmConfig, setLlmConfig] = useState( userConfigState.llm_config @@ -155,15 +158,30 @@ const SettingsPage = () => { return (
-
+
+ +
+
+
+

+ Settings +

+
+
+
+
- + {mode === 'nanobanana' &&
+

Nano Banana

+
} + {mode === 'presenton' && } +
{/* Fixed Bottom Button */} diff --git a/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingSideBar.tsx b/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingSideBar.tsx new file mode 100644 index 00000000..b2eb2d75 --- /dev/null +++ b/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingSideBar.tsx @@ -0,0 +1,59 @@ +import React from 'react' +const SettingSideBar = ({ mode, setMode, selectedProvider, setSelectedProvider }: { mode: 'nanobanana' | 'presenton', setMode: (mode: 'nanobanana' | 'presenton') => void, selectedProvider: 'text-provider' | 'image-provider', setSelectedProvider: (provider: 'text-provider' | 'image-provider') => void }) => { + return ( +
+

FILTER BY:

+
+

Select Mode

+
+ + + + + +
+

Select Provider

+ {mode === 'presenton' &&
+ + +
} + { + mode === 'nanobanana' &&
+ +
+ } +
+
+ ) +} + +export default SettingSideBar diff --git a/servers/nextjs/app/(presentation-generator)/(dashboard)/templates/components/TemplatePanel.tsx b/servers/nextjs/app/(presentation-generator)/(dashboard)/templates/components/TemplatePanel.tsx index 7a972392..92e26ac3 100644 --- a/servers/nextjs/app/(presentation-generator)/(dashboard)/templates/components/TemplatePanel.tsx +++ b/servers/nextjs/app/(presentation-generator)/(dashboard)/templates/components/TemplatePanel.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from "react"; import { useRouter } from "next/navigation"; import { Card } from "@/components/ui/card"; -import { ExternalLink, Loader2, Plus } from "lucide-react"; +import { ChevronRight, ExternalLink, Loader2, Plus } from "lucide-react"; import { templates } from "@/app/presentation-templates"; import { TemplateWithData, TemplateLayoutsWithSettings } from "@/app/presentation-templates/utils"; import { @@ -12,11 +12,12 @@ import { } from "@/app/hooks/useCustomTemplates"; import { CompiledLayout } from "@/app/hooks/compileLayout"; import CreateCustomTemplate from "./CreateCustomTemplate"; +import Link from "next/link"; // Component for rendering custom template card with lazy-loaded previews export const CustomTemplateCard = React.memo(function CustomTemplateCard({ template }: { template: CustomTemplates }) { const router = useRouter(); - const { previewLayouts, loading } = useCustomTemplatePreview(`${template.id}`); + const { previewLayouts, loading, totalLayouts } = useCustomTemplatePreview(`${template.id}`); const handleOpen = useCallback(() => { if (template.id.startsWith('custom-')) { router.push(`/template-preview/${template.id}`) @@ -34,7 +35,7 @@ export const CustomTemplateCard = React.memo(function CustomTemplateCard({ templ - Layouts- {template.layoutCount} + Layouts- {totalLayouts}
@@ -157,7 +158,7 @@ const InbuiltTemplateCard = React.memo(function InbuiltTemplateCard({ }); const LayoutPreview = () => { - const [tab, setTab] = useState<'custom' | 'default'>('custom'); + const [tab, setTab] = useState<'custom' | 'default'>('default'); const router = useRouter(); const { templates: customTemplates, loading: customLoading } = useCustomTemplateSummaries(); @@ -191,6 +192,33 @@ const LayoutPreview = () => { return (
+
+
+

+ Templates +

+
+ + + + + + New Template + New + + + +
+
+
diff --git a/servers/nextjs/app/(presentation-generator)/outline/components/CustomTemplateCard.tsx b/servers/nextjs/app/(presentation-generator)/outline/components/CustomTemplateCard.tsx index 4a5ac6fa..3f795157 100644 --- a/servers/nextjs/app/(presentation-generator)/outline/components/CustomTemplateCard.tsx +++ b/servers/nextjs/app/(presentation-generator)/outline/components/CustomTemplateCard.tsx @@ -28,70 +28,11 @@ LayoutPreview.displayName = 'LayoutPreview'; export const CustomTemplateCard = memo(({ template, onSelectTemplate, selectedTemplate }: { template: CustomTemplates, onSelectTemplate: (template: string) => void, selectedTemplate: string | null }) => { - const { previewLayouts, loading: customLoading } = useCustomTemplatePreview(template.id); + const { previewLayouts, loading: customLoading, totalLayouts } = useCustomTemplatePreview(template.id); const isSelected = selectedTemplate === template.id; return ( - // { - // onSelectTemplate(template.id); - // }} - // > - //
- //
- //

- // {template.name} - //

- //
- - - - // {/* Layout previews */} - //
- // {customLoading ? ( - // // Loading placeholders - // [...Array(Math.min(4, template.layoutCount))].map((_, index) => ( - //
- // - //
- // )) - // ) : previewLayouts && previewLayouts?.length > 0 ? ( - // // Actual layout previews - using memoized component - // previewLayouts?.slice(0, 4).map((layout: CompiledLayout, index: number) => ( - // - // )) - // ) : ( - // // Empty state placeholders - // [...Array(Math.min(4, template.layoutCount))].map((_, index) => ( - //
- // No preview - //
- // )) - // )} - //
- - - //
- // {isSelected && ( - //
- // Selected - //
- // )} - //
onSelectTemplate(template.id)} @@ -99,7 +40,7 @@ export const CustomTemplateCard = memo(({ template, onSelectTemplate, selectedTe - Layouts- {template.layoutCount} + Layouts- {totalLayouts}
diff --git a/servers/nextjs/app/(presentation-generator)/outline/components/OutlinePage.tsx b/servers/nextjs/app/(presentation-generator)/outline/components/OutlinePage.tsx index 78d6afdb..aa82ad1f 100644 --- a/servers/nextjs/app/(presentation-generator)/outline/components/OutlinePage.tsx +++ b/servers/nextjs/app/(presentation-generator)/outline/components/OutlinePage.tsx @@ -48,10 +48,10 @@ const OutlinePage: React.FC = () => { duration={loadingState.duration} /> - -
+ +
- + { + const LayoutComponent = layout.component; + return ( +
+
+
+ +
+
+ ); +}); +BuiltInLayoutPreview.displayName = 'BuiltInLayoutPreview'; + +// Memoized built-in template card +const BuiltInTemplateCard = memo(({ template, isSelected, onSelect }: { + template: TemplateLayoutsWithSettings; + isSelected: boolean; + onSelect: (template: TemplateLayoutsWithSettings) => void; +}) => { + const previewLayouts = useMemo(() => template.layouts.slice(0, 4), [template.layouts]); + const handleClick = useCallback(() => onSelect(template), [onSelect, template]); + + return ( + + + Layouts- {template.layouts.length} + + +
+
+ {previewLayouts.map((layout: TemplateWithData, index: number) => ( + + ))} +
+
+
+
+

+ {template.name} +

+

+ {template.description} +

+
+
+
+ ); +}); +BuiltInTemplateCard.displayName = 'BuiltInTemplateCard'; + interface TemplateSelectionProps { selectedTemplate: (TemplateLayoutsWithSettings | string) | null; onSelectTemplate: (template: TemplateLayoutsWithSettings | string) => void; } -const TemplateSelection: React.FC = ({ +const TemplateSelection: React.FC = memo(({ selectedTemplate, onSelectTemplate }) => { - - useEffect(() => { - const existingScript = document.querySelector( 'script[src*="tailwindcss.com"]' ); @@ -30,146 +97,101 @@ const TemplateSelection: React.FC = ({ script.async = true; document.head.appendChild(script); } - }, []); const { templates: customTemplates, loading: customLoading } = useCustomTemplateSummaries(); + // Stable callback for custom template selection + const handleCustomSelect = useCallback( + (template: TemplateLayoutsWithSettings | string) => onSelectTemplate(template), + [onSelectTemplate] + ); + + // Stable callback for built-in template selection + const handleBuiltInSelect = useCallback( + (template: TemplateLayoutsWithSettings) => onSelectTemplate(template), + [onSelectTemplate] + ); + + // Derive the selected custom template id only when selectedTemplate changes + const selectedCustomId = useMemo( + () => (typeof selectedTemplate === 'string' ? selectedTemplate : null), + [selectedTemplate] + ); + + // Derive the selected built-in template id only when selectedTemplate changes + const selectedBuiltInId = useMemo( + () => (typeof selectedTemplate !== 'string' ? selectedTemplate?.id ?? null : null), + [selectedTemplate] + ); + + // Memoize the custom templates section + const customTemplateCards = useMemo(() => { + if (customLoading) { + return ( +
+ + Loading custom templates... +
+ ); + } + if (customTemplates.length === 0) { + return ( + +

No custom templates yet.

+

+ Custom templates you create will appear here. +

+
+ ); + } + return ( +
+ {customTemplates.map((template: CustomTemplates) => ( + + ))} +
+ ); + }, [customLoading, customTemplates, handleCustomSelect, selectedCustomId]); + + // Memoize the built-in templates list + const builtInTemplateCards = useMemo( + () => + templates.map((template: TemplateLayoutsWithSettings) => ( + + )), + [selectedBuiltInId, handleBuiltInSelect] + ); + return (
- {/* In Built Templates */} -
-

In Built Templates

-
- {templates.map((template: TemplateLayoutsWithSettings) => { - const previewLayouts = template.layouts.slice(0, 4); - - return ( - onSelectTemplate(template)} - > - - Layouts- {template.layouts.length} - - -
-
- {previewLayouts.map((layout: TemplateWithData, index: number) => { - const LayoutComponent = layout.component; - return ( -
-
-
- -
-
- ); - })} -
-
-
-
- -

- {template.name} -

-

- {template.description} -

-
- -
- - // onSelectTemplate(template)} - // > - //
- //
- //

- // {template.name} - //

- - //
- - //

- // {template.description} - //

- - //
- // {previewLayouts.map((layout: TemplateWithData, index: number) => { - // const LayoutComponent = layout.component; - // return ( - //
- //
- //
- // - //
- //
- // ); - // })} - //
- //
- // {typeof selectedTemplate !== 'string' && selectedTemplate?.id === template.id && ( - //
- // Selected - //
- // )} - // - ); - })} -
-
- {/* Custom AI Templates */}
-

Custom AI Templates

+

Custom

+
+ {customTemplateCards} +
+ {/* In Built Templates */} +
+

In Built

+
+ {builtInTemplateCards}
- {customLoading ? ( -
- - Loading custom templates... -
- ) : customTemplates.length === 0 ? ( - -

No custom templates yet.

-

- Custom templates you create will appear here. -

-
- ) : ( -
- {customTemplates.map((template: CustomTemplates) => ( - - - ))} -
- )}
); -}; +}); +TemplateSelection.displayName = 'TemplateSelection'; export default TemplateSelection; diff --git a/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationHeader.tsx b/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationHeader.tsx index d7d387e9..691cb222 100644 --- a/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationHeader.tsx +++ b/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationHeader.tsx @@ -1,17 +1,18 @@ "use client"; import { Button } from "@/components/ui/button"; import { - SquareArrowOutUpRight, Play, Loader2, Redo2, Undo2, RotateCcw, ArrowRightFromLine, + ExternalLink, + MoveUpRight, + ArrowUpRight, } from "lucide-react"; import React, { useState } from "react"; -import Wrapper from "@/components/Wrapper"; import { useRouter, usePathname } from "next/navigation"; import { Popover, @@ -19,27 +20,21 @@ import { PopoverTrigger, } from "@/components/ui/popover"; import { PresentationGenerationApi } from "../../services/api/presentation-generation"; -import { OverlayLoader } from "@/components/ui/overlay-loader"; import { useDispatch, useSelector } from "react-redux"; -import Link from "next/link"; import { RootState } from "@/store/store"; import { toast } from "sonner"; -import Announcement from "@/components/Announcement"; import { PptxPresentationModel } from "@/types/pptx_models"; -import HeaderNav from "../../components/HeaderNab"; -import PDFIMAGE from "@/public/pdf.svg"; -import PPTXIMAGE from "@/public/pptx.svg"; -import Image from "next/image"; import { trackEvent, MixpanelEvent } from "@/utils/mixpanel"; import { usePresentationUndoRedo } from "../hooks/PresentationUndoRedo"; import ToolTip from "@/components/ToolTip"; import { clearPresentationData } from "@/store/slices/presentationGeneration"; import { clearHistory } from "@/store/slices/undoRedoSlice"; import { Separator } from "@/components/ui/separator"; +import ThemeSelector from "./ThemeSelector"; const PresentationHeader = ({ presentation_id, @@ -157,28 +152,35 @@ const PresentationHeader = ({ }; const ExportOptions = ({ mobile }: { mobile: boolean }) => ( -
- - +
+

Export as

+
+
+ + + +
@@ -189,12 +191,13 @@ const PresentationHeader = ({ return ( <>
-

{presentationData?.title || "Presentation"}

+

{presentationData?.title || "Presentation"}

{isPresentationSaving &&
} +
@@ -246,10 +249,10 @@ const PresentationHeader = ({ }} disabled={isExporting} > - {isExporting ? : "Export"} + {isExporting ? : "Export"} - + diff --git a/servers/nextjs/app/(presentation-generator)/presentation/components/ThemeSelector.tsx b/servers/nextjs/app/(presentation-generator)/presentation/components/ThemeSelector.tsx new file mode 100644 index 00000000..65ecec72 --- /dev/null +++ b/servers/nextjs/app/(presentation-generator)/presentation/components/ThemeSelector.tsx @@ -0,0 +1,120 @@ +"use client"; +import React, { useState } from 'react' +// import { Theme } from '@/app/(presentation-generator)/services/api/types' +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { Palette } from 'lucide-react'; + +import { useDispatch } from 'react-redux'; +// import { updateTheme } from '@/store/slices/presentationGeneration'; +import { useRouter } from 'next/navigation'; +import { useFontLoader } from '../../hooks/useFontLoader'; +const ThemeSelector = ({ presentation_id, current_theme, themes: allThemes }: { presentation_id: string, current_theme: any, themes: any[] }) => { + const [currentTheme, setCurrentTheme] = useState(current_theme) + const dispatch = useDispatch() + const router = useRouter() + const applyTheme = async (theme: any) => { + const element = document.getElementById('presentation-slides-wrapper') + if (!element) return; + if (allThemes.length === 0) return; + setCurrentTheme(theme) + clearTheme() + if (!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) + }) + // 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}"`) + + // dispatch(updateTheme(theme)) + } + const clearTheme = () => { + const element = document.getElementById('presentation-slides-wrapper') + if (!element) return; + element.style.removeProperty('--primary-color'); + element.style.removeProperty('--background-color'); + element.style.removeProperty('--card-color'); + element.style.removeProperty('--stroke'); + element.style.removeProperty('--primary-text'); + element.style.removeProperty('--background-text'); + element.style.removeProperty('--graph-0'); + element.style.removeProperty('--graph-1'); + element.style.removeProperty('--graph-2'); + element.style.removeProperty('--graph-3'); + element.style.removeProperty('--graph-4'); + element.style.removeProperty('--graph-5'); + element.style.removeProperty('--graph-6'); + element.style.removeProperty('--graph-7'); + element.style.removeProperty('--graph-8'); + element.style.removeProperty('--graph-9'); + } + const resetTheme = async () => { + clearTheme(); + + // dispatch(updateTheme({} as any)) + } + + + return ( + + + + + +
+ + +
+
+ + {allThemes && allThemes.length > 0 && allThemes.map((t) => ( +
applyTheme(t)} + className={`text-left group relative`} + > + +
+
+
+
+
+
+
+
+
+
+

+ {t.name} +

+
+ ))} +
+ + + ) +} + +export default ThemeSelector \ No newline at end of file diff --git a/servers/nextjs/app/(presentation-generator)/template-preview/page.tsx b/servers/nextjs/app/(presentation-generator)/template-preview/page.tsx index a7977a07..e067a288 100644 --- a/servers/nextjs/app/(presentation-generator)/template-preview/page.tsx +++ b/servers/nextjs/app/(presentation-generator)/template-preview/page.tsx @@ -5,8 +5,7 @@ import { Card } from "@/components/ui/card"; import { ExternalLink, Loader2, Plus } from "lucide-react"; import { templates } from "@/app/presentation-templates"; -import type { TemplateLayoutsWithSettings } from "@/app/presentation-templates"; -import { TemplateWithData } from "@/app/presentation-templates/utils"; +import { TemplateLayoutsWithSettings, TemplateWithData } from "@/app/presentation-templates/utils"; import { useCustomTemplateSummaries, useCustomTemplatePreview, diff --git a/servers/nextjs/app/(presentation-generator)/upload/components/UploadPage.tsx b/servers/nextjs/app/(presentation-generator)/upload/components/UploadPage.tsx index 55248085..e97e810d 100644 --- a/servers/nextjs/app/(presentation-generator)/upload/components/UploadPage.tsx +++ b/servers/nextjs/app/(presentation-generator)/upload/components/UploadPage.tsx @@ -46,7 +46,7 @@ const UploadPage = () => { // State management const [files, setFiles] = useState([]); const [config, setConfig] = useState({ - slides: "8", + slides: "5", language: LanguageType.English, prompt: "", tone: ToneType.Default, diff --git a/servers/nextjs/components/LLMSelection.tsx b/servers/nextjs/components/LLMSelection.tsx index 92c583ac..062b547a 100644 --- a/servers/nextjs/components/LLMSelection.tsx +++ b/servers/nextjs/components/LLMSelection.tsx @@ -34,28 +34,7 @@ interface LLMProviderSelectionProps { ) => void; } -const LLM_TABS = [ - { - label: 'OpenAI', - value: 'openai', - }, - { - label: 'Google', - value: 'google', - }, - { - label: 'Anthropic', - value: 'anthropic', - }, - { - label: 'Ollama', - value: 'ollama', - }, - { - label: 'Custom', - value: 'custom', - }, -]; + export default function LLMProviderSelection({ initialLLMConfig, onConfigChange, @@ -236,23 +215,9 @@ export default function LLMProviderSelection({ return ( -
- {/* Provider Selection - Fixed Header */} -
-
- {LLM_TABS.map((tab) => ( - - ))} +
-
-
{/* Scrollable Content */}
diff --git a/servers/nextjs/public/providers/image-provider.png b/servers/nextjs/public/providers/image-provider.png new file mode 100644 index 0000000000000000000000000000000000000000..c27e0591a6f62e524aa94be9f05fd13d690f42ce GIT binary patch literal 30564 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 literal 0 HcmV?d00001