Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ai/ai-react-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<AppMode>("chat");

useEffect(() => {
Expand Down
37 changes: 24 additions & 13 deletions ai/ai-react-app/src/components/Common/PromptInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
CountTokensResponse,
Part,
getGenerativeModel,
ImagenModelParams,
} from "firebase/ai";
import { fileToGenerativePart } from "../../utils/fileUtils";
import { AppMode } from "../../App";
Expand All @@ -23,8 +22,8 @@ interface PromptInputProps {
activeMode: AppMode;
aiInstance: AI;
currentParams?: ModelParams;
currentImagenParams?: ImagenModelParams;
selectedFile: File | null;
selectedAspectRatio?: string;
}

const PromptInput: React.FC<PromptInputProps> = ({
Expand All @@ -39,6 +38,7 @@ const PromptInput: React.FC<PromptInputProps> = ({
aiInstance,
currentParams,
selectedFile,
selectedAspectRatio,
}) => {
const [tokenCount, setTokenCount] = useState<CountTokensResponse | null>(
null,
Expand All @@ -55,11 +55,15 @@ const PromptInput: React.FC<PromptInputProps> = ({
setTokenCountError(null);

const currentPromptText = prompt.trim();
let textToCount = currentPromptText;
if (selectedAspectRatio) {
textToCount += `\nUse aspect ratio ${selectedAspectRatio}`;
}
const parts: Part[] = [];

// Add text part if present
if (currentPromptText) {
parts.push({ text: currentPromptText });
if (textToCount) {
parts.push({ text: textToCount });
}

// Add file part if present
Expand Down Expand Up @@ -137,15 +141,22 @@ const PromptInput: React.FC<PromptInputProps> = ({

{/* Main Input Area */}
<div className={styles.inputArea}>
<textarea
className={styles.promptTextarea}
value={prompt}
onChange={(e) => onPromptChange(e.target.value)}
placeholder={placeholder}
disabled={isLoading || isCountingTokens} // Disable if main loading OR counting
rows={3}
aria-label="Prompt input"
/>
<div style={{ display: 'flex', flexDirection: 'column', flexGrow: 1 }}>
<textarea
className={styles.promptTextarea}
value={prompt}
onChange={(e) => onPromptChange(e.target.value)}
placeholder={placeholder}
disabled={isLoading || isCountingTokens} // Disable if main loading OR counting
rows={3}
aria-label="Prompt input"
/>
{selectedAspectRatio && (
<div style={{ color: '#aaa', fontSize: '0.9em', marginTop: '5px', width: '100%' }}>
Use aspect ratio {selectedAspectRatio}
</div>
)}
</div>
<button
className={styles.runButton}
onClick={onSubmit}
Expand Down
2 changes: 1 addition & 1 deletion ai/ai-react-app/src/components/Layout/LeftSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const LeftSidebar: React.FC<LeftSidebarProps> = ({
// 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" },
];

Expand Down
34 changes: 20 additions & 14 deletions ai/ai-react-app/src/components/Layout/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -46,10 +45,13 @@ const MainLayout: React.FC<MainLayoutProps> = ({
model: AVAILABLE_GENERATIVE_MODELS[0],
...defaultGenerativeParams,
});
const [imagenParams, setImagenParams] = useState<ImagenModelParams>({
model: AVAILABLE_IMAGEN_MODELS[0],
...defaultImagenParams,
const [nanoBananaParams, setNanoBananaParams] = useState<ModelParams>({
model: AVAILABLE_NANO_BANANA_MODELS[0],
generationConfig: {
responseModalities: [ResponseModality.TEXT, ResponseModality.IMAGE],
},
});
const [selectedAspectRatio, setSelectedAspectRatio] = useState<string | undefined>();

const [usageMetadata, setUsageMetadata] = useState<UsageMetadata | null>(
null,
Expand All @@ -60,7 +62,9 @@ const MainLayout: React.FC<MainLayoutProps> = ({
try {
const backendInstance =
activeBackendType === BackendType.VERTEX_AI
? new VertexAIBackend()
? activeMode === "nanobanana"
? new VertexAIBackend("global")
: new VertexAIBackend()
: new GoogleAIBackend();
const aiInstance = getAI(getApp(), { backend: backendInstance });
setActiveAI(aiInstance);
Expand All @@ -75,14 +79,14 @@ const MainLayout: React.FC<MainLayoutProps> = ({
);
setActiveAI(null);
}
}, [activeBackendType]);
}, [activeBackendType, activeMode]);

useEffect(() => {
setUsageMetadata(null);
}, [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");
Expand All @@ -109,9 +113,9 @@ const MainLayout: React.FC<MainLayoutProps> = ({
activeMode={activeMode}
/>
);
case "imagenGen":
case "nanobanana":
return (
<ImagenView aiInstance={activeAI} currentParams={imagenParams} />
<NanoBananaView aiInstance={activeAI} currentParams={nanoBananaParams} selectedAspectRatio={selectedAspectRatio} />
);
case "live":
return (
Expand Down Expand Up @@ -152,8 +156,10 @@ const MainLayout: React.FC<MainLayoutProps> = ({
activeMode={activeMode}
generativeParams={generativeParams}
setGenerativeParams={setGenerativeParams}
imagenParams={imagenParams}
setImagenParams={setImagenParams}
nanoBananaParams={nanoBananaParams}
setNanoBananaParams={setNanoBananaParams}
selectedAspectRatio={selectedAspectRatio}
setSelectedAspectRatio={setSelectedAspectRatio}
/>
</div>
</div>
Expand Down
Loading
Loading