diff --git a/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/ImageProvider.tsx b/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/ImageProvider.tsx
new file mode 100644
index 00000000..1a0d006b
--- /dev/null
+++ b/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/ImageProvider.tsx
@@ -0,0 +1,396 @@
+import ToolTip from '@/components/ToolTip'
+import { Button } from '@/components/ui/button'
+import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command'
+import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
+import { Select, SelectItem, SelectContent, SelectTrigger, SelectValue } from '@/components/ui/select'
+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 React, { useState } from 'react'
+
+const ImageProvider = ({ llmConfig, setLlmConfig }: { llmConfig: LLMConfig, setLlmConfig: (config: any) => void }) => {
+ const [openImageProviderSelect, setOpenImageProviderSelect] = useState(false);
+ const isImageGenerationDisabled = llmConfig.DISABLE_IMAGE_GENERATION ?? false;
+ console.log(llmConfig);
+ const handleChangeImageGenerationDisabled = (value: boolean) => {
+ setLlmConfig((prev: any) => ({
+ ...prev,
+ DISABLE_IMAGE_GENERATION: value
+ }));
+ }
+ const input_field_changed = (value: string, field: string) => {
+ setLlmConfig((prev: any) => ({
+ ...prev,
+ [field]: value
+ }));
+ setOpenImageProviderSelect(false);
+ }
+
+
+
+
+ const renderQualitySelector = (llmConfig: LLMConfig, input_field_changed: (value: string, field: string) => void) => {
+ if (llmConfig.IMAGE_PROVIDER === "dall-e-3") {
+ return (
+
+
+
+
+ {/* {DALLE_3_QUALITY_OPTIONS.map((option) => (
+
+ ))} */}
+
+
+ );
+ }
+
+ if (llmConfig.IMAGE_PROVIDER === "gpt-image-1.5") {
+ return (
+
+
+
+
+ {/* {GPT_IMAGE_1_5_QUALITY_OPTIONS.map((option) => (
+
+ ))} */}
+
+
+ );
+ }
+
+ return null;
+ };
+
+
+
+
+ return (
+
+ {/* API Key Input */}
+
+
+
+ handleChangeImageGenerationDisabled(checked)}
+ />
+
+
+
+
+
+
+
+
+

+
+
Image Generation Settings
+
+ Choosing where images come from
+
+
+
+
+
+ {!isImageGenerationDisabled && (
+ <>
+ {/* Image Provider Selection */}
+
+
+
+
+
+
+
+
+
+
+
+ No provider found.
+
+ {Object.values(IMAGE_PROVIDERS).map(
+ (provider, index) => (
+ {
+ input_field_changed(value, "IMAGE_PROVIDER");
+ setOpenImageProviderSelect(false);
+ }}
+ >
+
+
+
+
+
+ {provider.label}
+
+
+
+ {provider.description}
+
+
+
+
+ )
+ )}
+
+
+
+
+
+
+
+
+
+
+ {/* Dynamic API Key Input for Image Provider */}
+ {llmConfig.IMAGE_PROVIDER &&
+ IMAGE_PROVIDERS[llmConfig.IMAGE_PROVIDER] &&
+ (() => {
+ const provider = IMAGE_PROVIDERS[llmConfig.IMAGE_PROVIDER];
+
+ // Show info message when using same API key as main provider
+ if (
+ provider.value === "DALL_E_3" &&
+ llmConfig.LLM === "openai"
+ ) {
+ return <>>;
+ }
+
+ if (
+ provider.value === "GPT_IMAGE_1_5" &&
+ llmConfig.LLM === "openai"
+ ) {
+ return <>>;
+ }
+
+ if (
+ provider.value === "GEMINI_FLASH" &&
+ llmConfig.LLM === "google"
+ ) {
+ return <>>;
+ }
+
+ if (
+ provider.value === "NANO_BANANA_PRO" &&
+ llmConfig.LLM === "google"
+ ) {
+ return <>>;
+ }
+
+ // Show ComfyUI configuration
+ if (provider.value === "comfyui") {
+ return (
+
+
+
+
+ {
+ input_field_changed(
+ e.target.value,
+ "COMFYUI_URL"
+ );
+ }}
+ />
+
+
+
+ Use your machine IP address (not localhost) when
+ running in Docker
+
+
+
+
+
+
+
+ Export your workflow from ComfyUI using "Export
+ (API)" and paste the JSON here.
+
+
+
+ );
+ }
+
+ // Show API key input for other providers
+ return (
+
+
+
+
+ // input_field_changed(
+ // provider.apiKeyField || "",
+ // e.target.value
+ // )
+ // }
+ />
+
+
+
+ );
+ })()}
+
+ {renderQualitySelector(llmConfig, input_field_changed)}
+ >
+ )}
+
+
+
+
+
+ {/* Web Grounding Toggle - show at the end, below models dropdown */}
+
+
+
+
Advanced
+
+ Configure advanced AI features.
+
+
+
+
+
+
+
+
+ )
+}
+
+export default ImageProvider
diff --git a/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingPage.tsx b/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingPage.tsx
index c9cc3f37..77cb4829 100644
--- a/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingPage.tsx
+++ b/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingPage.tsx
@@ -15,6 +15,8 @@ import Header from "../dashboard/components/Header";
import { LLMConfig } from "@/types/llm_config";
import { trackEvent, MixpanelEvent } from "@/utils/mixpanel";
import SettingSideBar from "./SettingSideBar";
+import TextProvider from "./TextProvider";
+import ImageProvider from "./ImageProvider";
// Button state interface
interface ButtonState {
@@ -174,12 +176,18 @@ const SettingsPage = () => {
{mode === 'nanobanana' &&
Nano Banana
}
- {mode === 'presenton' && {
+ setLlmConfig(prev => ({
+ ...prev,
+ [field]: value
+ }));
+ }}
+ llmConfig={llmConfig}
/>}
+ {mode === 'presenton' && selectedProvider === 'image-provider' && }
diff --git a/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/TextProvider.tsx b/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/TextProvider.tsx
new file mode 100644
index 00000000..a4ae09a8
--- /dev/null
+++ b/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/TextProvider.tsx
@@ -0,0 +1,260 @@
+import { Button } from '@/components/ui/button';
+import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command';
+import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
+import { Switch } from '@/components/ui/switch';
+import { cn } from '@/lib/utils';
+import { LLMConfig } from '@/types/llm_config';
+import { Check, ChevronsUpDown, Loader2 } from 'lucide-react';
+import React, { useEffect, useState } from 'react'
+import { toast } from 'sonner';
+
+
+interface OpenAIConfigProps {
+
+ onInputChange: (value: string | boolean, field: string) => void;
+ llmConfig: LLMConfig;
+}
+const TextProvider = ({
+
+ onInputChange,
+ llmConfig
+}: OpenAIConfigProps
+
+) => {
+ const [openModelSelect, setOpenModelSelect] = useState(false);
+ const [availableModels, setAvailableModels] = useState([]);
+ const [modelsLoading, setModelsLoading] = useState(false);
+ const [modelsChecked, setModelsChecked] = useState(false);
+ const [apiKey, setApiKey] = useState('');
+
+ const openaiUrl = "https://api.openai.com/v1";
+
+ useEffect(() => {
+ setAvailableModels([]);
+ setModelsChecked(false);
+ onInputChange("", "openai_model");
+ }, [apiKey]);
+
+ const onApiKeyChange = (value: string) => {
+ setApiKey(value);
+ onInputChange(value, "openai_api_key");
+ };
+
+ const fetchAvailableModels = async () => {
+ // if (!'openaiApiKey') return;
+
+ setModelsLoading(true);
+ try {
+ const response = await fetch('/api/v1/ppt/openai/models/available', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ url: openaiUrl,
+ api_key: 'openaiApiKey'
+ }),
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ setAvailableModels(data);
+ setModelsChecked(true);
+ onInputChange("gpt-4.1", "openai_model");
+ } else {
+ console.error('Failed to fetch models');
+ setAvailableModels([]);
+ setModelsChecked(true);
+ }
+ } catch (error) {
+ console.error('Error fetching models:', error);
+ toast.error('Error fetching models');
+ setAvailableModels([]);
+ setModelsChecked(true);
+ } finally {
+ setModelsLoading(false);
+ }
+ };
+ return (
+
+ {/* API Key Input */}
+
+
+
+
Text Generation Settings
+
+ Choosing where text contets come from
+
+
+
+
+
+
+
+ onApiKeyChange(e.target.value)}
+ className="w-full px-2 py-3 outline-none border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-colors"
+ placeholder="Enter your API key"
+ />
+
+
+ {/* Check for available models button - show when no models checked or no models found */}
+
+ {(!modelsChecked || (modelsChecked && availableModels.length === 0)) && (
+
+
+
+ )}
+
+
+ {/* Show message if no models found */}
+ {modelsChecked && availableModels.length === 0 && (
+
+
+ No models found. Please make sure your API key is valid and has access to OpenAI models.
+
+
+ )}
+
+ {/* Model Selection - only show if models are available */}
+ {modelsChecked && availableModels.length > 0 ? (
+
+
+
+
+
+
+
+
+
+
+
+ No model found.
+
+ {availableModels.map((model, index) => (
+ {
+ onInputChange(value, "openai_model");
+ setOpenModelSelect(false);
+ }}
+ >
+
+
+
+ ))}
+
+
+
+
+
+
+
+ ) : null}
+
+
+
+
+
+ {/* Web Grounding Toggle - show at the end, below models dropdown */}
+
+
+
+
Advanced
+
+ Configure advanced AI features.
+
+
+
+
+
+
+ onInputChange(checked, "")}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default TextProvider
diff --git a/servers/nextjs/public/image-markup.svg b/servers/nextjs/public/image-markup.svg
new file mode 100644
index 00000000..a1270df9
--- /dev/null
+++ b/servers/nextjs/public/image-markup.svg
@@ -0,0 +1,9 @@
+
diff --git a/servers/nextjs/utils/providerConstants.ts b/servers/nextjs/utils/providerConstants.ts
index c8ac5cda..ecccbfa6 100644
--- a/servers/nextjs/utils/providerConstants.ts
+++ b/servers/nextjs/utils/providerConstants.ts
@@ -117,3 +117,34 @@ export const LLM_PROVIDERS: Record = {
description: "Custom LLM",
},
};
+
+export const DALLE_3_QUALITY_OPTIONS = [
+ {
+ label: "Standard",
+ value: "standard",
+ description: "Faster generation with lower cost",
+ },
+ {
+ label: "HD",
+ value: "hd",
+ description: "Higher quality images with increased cost",
+ },
+];
+
+export const GPT_IMAGE_1_5_QUALITY_OPTIONS = [
+ {
+ label: "Low",
+ value: "low",
+ description: "Fastest and most cost-effective",
+ },
+ {
+ label: "Medium",
+ value: "medium",
+ description: "Balanced quality and speed",
+ },
+ {
+ label: "High",
+ value: "high",
+ description: "Best quality with longer generation time",
+ },
+];
\ No newline at end of file