From 3b2c11a821655d062f3ee2eac164152d6e389325 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Tue, 14 Apr 2026 10:25:23 -0400 Subject: [PATCH 1/3] Imagen -> Nano Banana --- ai/ai-react-app/src/App.tsx | 4 +- .../src/components/Common/PromptInput.tsx | 2 - .../src/components/Layout/LeftSidebar.tsx | 2 +- .../src/components/Layout/MainLayout.tsx | 25 +-- .../src/components/Layout/RightSidebar.tsx | 180 ++++++------------ .../src/services/firebaseAIService.ts | 17 +- ai/ai-react-app/src/views/ImagenView.tsx | 147 -------------- ...w.module.css => NanoBananaView.module.css} | 0 ai/ai-react-app/src/views/NanoBananaView.tsx | 177 +++++++++++++++++ 9 files changed, 259 insertions(+), 295 deletions(-) delete mode 100644 ai/ai-react-app/src/views/ImagenView.tsx rename ai/ai-react-app/src/views/{ImagenView.module.css => NanoBananaView.module.css} (100%) create mode 100644 ai/ai-react-app/src/views/NanoBananaView.tsx diff --git a/ai/ai-react-app/src/App.tsx b/ai/ai-react-app/src/App.tsx index 48052c9a6..c65680c1f 100644 --- a/ai/ai-react-app/src/App.tsx +++ b/ai/ai-react-app/src/App.tsx @@ -2,10 +2,10 @@ import { useEffect, useState } from "react"; import MainLayout from "./components/Layout/MainLayout"; // Defines the primary modes or views available in the application. -export type AppMode = "chat" | "imagenGen" | "live"; +export type AppMode = "chat" | "nanobanana" | "live"; function App() { - // State to manage which main view ('chat' or 'imagenGen') is currently active. + // State to manage which main view ('chat' or 'nanobanana') is currently active. const [activeMode, setActiveMode] = useState("chat"); useEffect(() => { diff --git a/ai/ai-react-app/src/components/Common/PromptInput.tsx b/ai/ai-react-app/src/components/Common/PromptInput.tsx index 93bfc9d6a..b454efbc8 100644 --- a/ai/ai-react-app/src/components/Common/PromptInput.tsx +++ b/ai/ai-react-app/src/components/Common/PromptInput.tsx @@ -6,7 +6,6 @@ import { CountTokensResponse, Part, getGenerativeModel, - ImagenModelParams, } from "firebase/ai"; import { fileToGenerativePart } from "../../utils/fileUtils"; import { AppMode } from "../../App"; @@ -23,7 +22,6 @@ interface PromptInputProps { activeMode: AppMode; aiInstance: AI; currentParams?: ModelParams; - currentImagenParams?: ImagenModelParams; selectedFile: File | null; } diff --git a/ai/ai-react-app/src/components/Layout/LeftSidebar.tsx b/ai/ai-react-app/src/components/Layout/LeftSidebar.tsx index 07b6d8b30..06a1d209d 100644 --- a/ai/ai-react-app/src/components/Layout/LeftSidebar.tsx +++ b/ai/ai-react-app/src/components/Layout/LeftSidebar.tsx @@ -40,7 +40,7 @@ const LeftSidebar: React.FC = ({ // Define the available modes and their display names const modes: { id: AppMode; label: string }[] = [ { id: "chat", label: "Chat" }, - { id: "imagenGen", label: "Imagen Generation" }, + { id: "nanobanana", label: "Nano Banana" }, { id: "live", label: "Live Conversation" }, ]; diff --git a/ai/ai-react-app/src/components/Layout/MainLayout.tsx b/ai/ai-react-app/src/components/Layout/MainLayout.tsx index a72ad13ab..c33e49080 100644 --- a/ai/ai-react-app/src/components/Layout/MainLayout.tsx +++ b/ai/ai-react-app/src/components/Layout/MainLayout.tsx @@ -3,24 +3,23 @@ import TopBar from "./TopBar"; import LeftSidebar from "./LeftSidebar"; import RightSidebar from "./RightSidebar"; import ChatView from "../../views/ChatView"; -import ImagenView from "../../views/ImagenView"; +import NanoBananaView from "../../views/NanoBananaView"; import LiveView from "../../views/LiveView"; import { AppMode } from "../../App"; import { UsageMetadata, ModelParams, - ImagenModelParams, BackendType, AI, VertexAIBackend, GoogleAIBackend, getAI, + ResponseModality, } from "firebase/ai"; import { AVAILABLE_GENERATIVE_MODELS, - AVAILABLE_IMAGEN_MODELS, + AVAILABLE_NANO_BANANA_MODELS, defaultGenerativeParams, - defaultImagenParams, } from "../../services/firebaseAIService"; import styles from "./MainLayout.module.css"; import { getApp } from "firebase/app"; @@ -46,9 +45,11 @@ const MainLayout: React.FC = ({ model: AVAILABLE_GENERATIVE_MODELS[0], ...defaultGenerativeParams, }); - const [imagenParams, setImagenParams] = useState({ - model: AVAILABLE_IMAGEN_MODELS[0], - ...defaultImagenParams, + const [nanoBananaParams, setNanoBananaParams] = useState({ + model: AVAILABLE_NANO_BANANA_MODELS[0], + generationConfig: { + responseModalities: [ResponseModality.TEXT, ResponseModality.IMAGE], + }, }); const [usageMetadata, setUsageMetadata] = useState( @@ -82,7 +83,7 @@ const MainLayout: React.FC = ({ }, [activeMode]); useEffect(() => { - const validModes: AppMode[] = ["chat", "imagenGen", "live"]; + const validModes: AppMode[] = ["chat", "nanobanana", "live"]; if (!validModes.includes(activeMode)) { console.warn(`Invalid activeMode "${activeMode}". Resetting to "chat".`); setActiveMode("chat"); @@ -109,9 +110,9 @@ const MainLayout: React.FC = ({ activeMode={activeMode} /> ); - case "imagenGen": + case "nanobanana": return ( - + ); case "live": return ( @@ -152,8 +153,8 @@ const MainLayout: React.FC = ({ activeMode={activeMode} generativeParams={generativeParams} setGenerativeParams={setGenerativeParams} - imagenParams={imagenParams} - setImagenParams={setImagenParams} + nanoBananaParams={nanoBananaParams} + setNanoBananaParams={setNanoBananaParams} /> diff --git a/ai/ai-react-app/src/components/Layout/RightSidebar.tsx b/ai/ai-react-app/src/components/Layout/RightSidebar.tsx index 9e703ee35..09368311f 100644 --- a/ai/ai-react-app/src/components/Layout/RightSidebar.tsx +++ b/ai/ai-react-app/src/components/Layout/RightSidebar.tsx @@ -3,19 +3,17 @@ import { AppMode } from "../../App"; import styles from "./RightSidebar.module.css"; import { AVAILABLE_GENERATIVE_MODELS, - AVAILABLE_IMAGEN_MODELS, + AVAILABLE_NANO_BANANA_MODELS, defaultFunctionCallingTool, defaultGoogleSearchTool, } from "../../services/firebaseAIService"; import { ModelParams, - ImagenModelParams, HarmCategory, HarmBlockThreshold, FunctionCallingMode, - ImagenSafetyFilterLevel, - ImagenPersonFilterLevel, UsageMetadata, + ResponseModality, } from "firebase/ai"; interface RightSidebarProps { @@ -23,8 +21,8 @@ interface RightSidebarProps { activeMode: AppMode; generativeParams: ModelParams; setGenerativeParams: React.Dispatch>; - imagenParams: ImagenModelParams; - setImagenParams: React.Dispatch>; + nanoBananaParams: ModelParams; + setNanoBananaParams: React.Dispatch>; } const RightSidebar: React.FC = ({ @@ -32,8 +30,8 @@ const RightSidebar: React.FC = ({ activeMode, generativeParams, setGenerativeParams, - imagenParams, - setImagenParams, + nanoBananaParams, + setNanoBananaParams, }) => { const handleModelParamsUpdate = ( updateFn: (prevState: ModelParams) => ModelParams, @@ -41,10 +39,10 @@ const RightSidebar: React.FC = ({ setGenerativeParams((prevState) => updateFn(prevState)); }; - const handleImagenModelParamsUpdate = ( - updateFn: (prevState: ImagenModelParams) => ImagenModelParams, + const handleNanoBananaModelParamsUpdate = ( + updateFn: (prevState: ModelParams) => ModelParams, ) => { - setImagenParams((prevState) => updateFn(prevState)); + setNanoBananaParams((prevState) => updateFn(prevState)); }; const getThresholdForCategory = ( @@ -129,9 +127,9 @@ const RightSidebar: React.FC = ({ if (checked) { // Turn ON JSON nextState.generationConfig.responseMimeType = "application/json"; - nextState.generationConfig.responseSchema = undefined; // Use a default schema // Turn OFF Function Calling by clearing its related fields + nextState.generationConfig.responseSchema = undefined; nextState.tools = undefined; nextState.toolConfig = undefined; } else { @@ -163,6 +161,7 @@ const RightSidebar: React.FC = ({ if (checked) { // Turn ON Google Search Grounding nextState.tools = [defaultGoogleSearchTool]; + nextState.generationConfig.responseMimeType = undefined; // Turn OFF JSON mode and Function Calling nextState.generationConfig.responseMimeType = undefined; @@ -173,57 +172,10 @@ const RightSidebar: React.FC = ({ nextState.tools = undefined; } } - console.log("[RightSidebar] Updated generative params state:", nextState); return nextState; }); }; - const handleImagenModelChange = ( - event: React.ChangeEvent, - ) => { - const newModel = event.target.value; - handleImagenModelParamsUpdate((prev: ImagenModelParams) => ({ - ...prev, - model: newModel, - })); - }; - const handleImagenSamplesChange = ( - event: React.ChangeEvent, - ) => { - const newCount = parseInt(event.target.value, 10); - handleImagenModelParamsUpdate((prev: ImagenModelParams) => ({ - ...prev, - generationConfig: { ...prev.generationConfig, numberOfImages: newCount }, - })); - }; - const handleImagenNegativePromptChange = ( - event: React.ChangeEvent, - ) => { - const newPrompt = event.target.value || undefined; - handleImagenModelParamsUpdate((prev: ImagenModelParams) => ({ - ...prev, - generationConfig: { ...prev.generationConfig, negativePrompt: newPrompt }, - })); - }; - const handleImagenSafetyChange = ( - event: React.ChangeEvent, - ) => { - const newLevel = event.target.value as ImagenSafetyFilterLevel; - handleImagenModelParamsUpdate((prev: ImagenModelParams) => ({ - ...prev, - safetySettings: { ...prev.safetySettings, safetyFilterLevel: newLevel }, - })); - }; - const handleImagenPersonChange = ( - event: React.ChangeEvent, - ) => { - const newLevel = event.target.value as ImagenPersonFilterLevel; - handleImagenModelParamsUpdate((prev: ImagenModelParams) => ({ - ...prev, - safetySettings: { ...prev.safetySettings, personFilterLevel: newLevel }, - })); - }; - // Derive UI state from config const isStructuredOutputActive = generativeParams.generationConfig?.responseMimeType === "application/json"; @@ -439,79 +391,69 @@ const RightSidebar: React.FC = ({ )} - {/* Imagen Settings */} - {activeMode === "imagenGen" && ( + {/* Nano Banana Settings */} + {activeMode === "nanobanana" && (
-
Imagen Settings
+
Nano Banana Settings
+
- - -
-
- -