diff --git a/src/components/ProjectSetupModal.tsx b/src/components/ProjectSetupModal.tsx index 37bfc1ed..4826463c 100644 --- a/src/components/ProjectSetupModal.tsx +++ b/src/components/ProjectSetupModal.tsx @@ -2,8 +2,64 @@ import { useState, useEffect } from "react"; import { generateWorkflowId, useWorkflowStore } from "@/store/workflowStore"; -import { ProviderType, ProviderSettings } from "@/types"; +import { ProviderType, ProviderSettings, NodeDefaultsConfig, LLMProvider, LLMModelType } from "@/types"; import { EnvStatusResponse } from "@/app/api/env-status/route"; +import { loadNodeDefaults, saveNodeDefaults } from "@/store/utils/localStorage"; +import { ProviderModel } from "@/lib/providers/types"; +import { ModelSearchDialog } from "@/components/modals/ModelSearchDialog"; + +// LLM provider and model options (mirrored from LLMGenerateNode) +const LLM_PROVIDERS: { value: LLMProvider; label: string }[] = [ + { value: "google", label: "Google" }, + { value: "openai", label: "OpenAI" }, +]; + +const LLM_MODELS: Record = { + google: [ + { value: "gemini-3-flash-preview", label: "Gemini 3 Flash" }, + { value: "gemini-2.5-flash", label: "Gemini 2.5 Flash" }, + { value: "gemini-3-pro-preview", label: "Gemini 3.0 Pro" }, + ], + openai: [ + { value: "gpt-4.1-mini", label: "GPT-4.1 Mini" }, + { value: "gpt-4.1-nano", label: "GPT-4.1 Nano" }, + ], +}; + +// Provider icons +const GeminiIcon = () => ( + + + +); + +const ReplicateIcon = () => ( + + + + + +); + +const FalIcon = () => ( + + + +); + +// Get provider icon component +const getProviderIcon = (provider: ProviderType) => { + switch (provider) { + case "gemini": + return ; + case "replicate": + return ; + case "fal": + return ; + default: + return null; + } +}; interface ProjectSetupModalProps { isOpen: boolean; @@ -29,7 +85,7 @@ export function ProjectSetupModal({ } = useWorkflowStore(); // Tab state - const [activeTab, setActiveTab] = useState<"project" | "providers">("project"); + const [activeTab, setActiveTab] = useState<"project" | "providers" | "nodeDefaults">("project"); // Project tab state const [name, setName] = useState(""); @@ -55,6 +111,11 @@ export function ProjectSetupModal({ }); const [envStatus, setEnvStatus] = useState(null); + // Node defaults tab state + const [localNodeDefaults, setLocalNodeDefaults] = useState({}); + const [showImageModelDialog, setShowImageModelDialog] = useState(false); + const [showVideoModelDialog, setShowVideoModelDialog] = useState(false); + // Pre-fill when opening in settings mode useEffect(() => { if (isOpen) { @@ -85,6 +146,11 @@ export function ProjectSetupModal({ }); setError(null); + // Load node defaults + setLocalNodeDefaults(loadNodeDefaults()); + setShowImageModelDialog(false); + setShowVideoModelDialog(false); + // Fetch env status fetch("/api/env-status") .then((res) => res.json()) @@ -190,11 +256,18 @@ export function ProjectSetupModal({ onClose(); }; + const handleSaveNodeDefaults = () => { + saveNodeDefaults(localNodeDefaults); + onClose(); + }; + const handleSave = () => { if (activeTab === "project") { handleSaveProject(); - } else { + } else if (activeTab === "providers") { handleSaveProviders(); + } else { + handleSaveNodeDefaults(); } }; @@ -248,6 +321,12 @@ export function ProjectSetupModal({ > Providers + {/* Project Tab Content */} @@ -515,6 +594,199 @@ export function ProjectSetupModal({ )} + {/* Node Defaults Tab Content */} + {activeTab === "nodeDefaults" && ( +
+ {/* GenerateImage Section */} +
+
+ Default Image Model +
+ {localNodeDefaults.generateImage?.selectedModel ? ( + <> +
+ {getProviderIcon(localNodeDefaults.generateImage.selectedModel.provider)} + + {localNodeDefaults.generateImage.selectedModel.displayName} + +
+ + + + ) : ( + <> + System default (Gemini nano-banana-pro) + + + )} +
+
+
+ + {/* GenerateVideo Section */} +
+
+ Default Video Model +
+ {localNodeDefaults.generateVideo?.selectedModel ? ( + <> +
+ {getProviderIcon(localNodeDefaults.generateVideo.selectedModel.provider)} + + {localNodeDefaults.generateVideo.selectedModel.displayName} + +
+ + + + ) : ( + <> + None set (select on first use) + + + )} +
+
+
+ + {/* LLM Section */} +
+
+
+ Default LLM Settings + {localNodeDefaults.llm && ( + + )} +
+ + {!localNodeDefaults.llm ? ( +

Using system defaults (Google Gemini 3 Flash)

+ ) : null} + + {/* Provider dropdown */} +
+ + +
+ + {/* Model dropdown */} +
+ + +
+ + {/* Temperature slider */} +
+ + { + setLocalNodeDefaults(prev => ({ + ...prev, + llm: { ...prev.llm, temperature: parseFloat(e.target.value) } + })); + }} + className="flex-1 h-1 bg-neutral-700 rounded-lg appearance-none cursor-pointer accent-neutral-400" + /> +
+
+
+ +

+ These defaults are applied when creating nodes via keyboard shortcuts (Shift+G, Shift+L, etc). +

+
+ )} +