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
1 change: 1 addition & 0 deletions ai/ai-react-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/config/firebase-config.ts
2 changes: 1 addition & 1 deletion ai/ai-react-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
"firebase": "12.2.1",
"firebase": "12.12.0",
"immer": "^10.1.1",
"react": "^19.2.1",
"react-dom": "^19.2.1"
Expand Down
2 changes: 1 addition & 1 deletion ai/ai-react-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ 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" | "imagenGen" | "live" | "serverTemplate";

function App() {
// State to manage which main view ('chat' or 'imagenGen') is currently active.
Expand Down
6 changes: 4 additions & 2 deletions ai/ai-react-app/src/components/Common/PromptInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface PromptInputProps {
currentParams?: ModelParams;
currentImagenParams?: ImagenModelParams;
selectedFile: File | null;
disabled?: boolean;
}

const PromptInput: React.FC<PromptInputProps> = ({
Expand All @@ -39,6 +40,7 @@ const PromptInput: React.FC<PromptInputProps> = ({
aiInstance,
currentParams,
selectedFile,
disabled = false,
}) => {
const [tokenCount, setTokenCount] = useState<CountTokensResponse | null>(
null,
Expand Down Expand Up @@ -142,14 +144,14 @@ const PromptInput: React.FC<PromptInputProps> = ({
value={prompt}
onChange={(e) => onPromptChange(e.target.value)}
placeholder={placeholder}
disabled={isLoading || isCountingTokens} // Disable if main loading OR counting
disabled={disabled || isLoading || isCountingTokens} // Disable if main loading OR counting
rows={3}
aria-label="Prompt input"
/>
<button
className={styles.runButton}
onClick={onSubmit}
disabled={isLoading || isCountingTokens || !prompt.trim()} // Disable if loading, counting, or empty
disabled={disabled || isLoading || isCountingTokens || !prompt.trim()} // Disable if loading, counting, or empty
aria-label="Submit prompt"
>
{isLoading ? "Running..." : "Run ➤"}
Expand Down
55 changes: 29 additions & 26 deletions ai/ai-react-app/src/components/Layout/LeftSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const LeftSidebar: React.FC<LeftSidebarProps> = ({
{ id: "chat", label: "Chat" },
{ id: "imagenGen", label: "Imagen Generation" },
{ id: "live", label: "Live Conversation" },
{ id: "serverTemplate", label: "Server Prompt Templates" },
];

const handleBackendChange = (event: React.ChangeEvent<HTMLInputElement>) => {
Expand Down Expand Up @@ -111,33 +112,35 @@ const LeftSidebar: React.FC<LeftSidebarProps> = ({
</ul>

{/* Backend Selection */}
<div className={styles.backendSelector}>
<h5 className={styles.selectorTitle}>Backend API</h5>
<div className={styles.radioGroup}>
<label>
<input
type="radio"
name="backend"
value={BackendType.GOOGLE_AI}
checked={activeBackend === BackendType.GOOGLE_AI}
onChange={handleBackendChange}
/>
Gemini Developer API
</label>
</div>
<div className={styles.radioGroup}>
<label>
<input
type="radio"
name="backend"
value={BackendType.VERTEX_AI}
checked={activeBackend === BackendType.VERTEX_AI}
onChange={handleBackendChange}
/>
Vertex AI Gemini API
</label>
{activeMode !== "serverTemplate" && (
<div className={styles.backendSelector}>
<h5 className={styles.selectorTitle}>Backend API</h5>
<div className={styles.radioGroup}>
<label>
<input
type="radio"
name="backend"
value={BackendType.GOOGLE_AI}
checked={activeBackend === BackendType.GOOGLE_AI}
onChange={handleBackendChange}
/>
Gemini Developer API
</label>
</div>
<div className={styles.radioGroup}>
<label>
<input
type="radio"
name="backend"
value={BackendType.VERTEX_AI}
checked={activeBackend === BackendType.VERTEX_AI}
onChange={handleBackendChange}
/>
Vertex AI Gemini API
</label>
</div>
</div>
</div>
)}

{/* Persona Selector */}
{activeMode === "chat" && (
Expand Down
11 changes: 10 additions & 1 deletion ai/ai-react-app/src/components/Layout/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import RightSidebar from "./RightSidebar";
import ChatView from "../../views/ChatView";
import ImagenView from "../../views/ImagenView";
import LiveView from "../../views/LiveView";
import ServerTemplateView from "../../views/ServerTemplateView";
import { AppMode } from "../../App";
import {
UsageMetadata,
Expand Down Expand Up @@ -82,7 +83,7 @@ const MainLayout: React.FC<MainLayoutProps> = ({
}, [activeMode]);

useEffect(() => {
const validModes: AppMode[] = ["chat", "imagenGen", "live"];
const validModes: AppMode[] = ["chat", "imagenGen", "live", "serverTemplate"];
if (!validModes.includes(activeMode)) {
console.warn(`Invalid activeMode "${activeMode}". Resetting to "chat".`);
setActiveMode("chat");
Expand Down Expand Up @@ -117,6 +118,14 @@ const MainLayout: React.FC<MainLayoutProps> = ({
return (
<LiveView aiInstance={activeAI} />
);
case "serverTemplate":
return (
<ServerTemplateView
aiInstance={activeAI}
onUsageMetadataChange={setUsageMetadata}
currentParams={generativeParams}
/>
);
default:
console.error(`Unexpected activeMode: ${activeMode}`);
return (
Expand Down
22 changes: 21 additions & 1 deletion ai/ai-react-app/src/components/Layout/RightSidebar.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,24 @@ input:checked + .slider:before {
.advancedSettings .controlGroup label {
font-weight: 400;
color: var(--color-text-secondary);
}
}
.groupContainer {
border: 1px solid var(--color-border-primary);
border-radius: 8px;
padding: 12px;
margin-bottom: 16px;
background-color: rgba(255, 255, 255, 0.02);
}

.errorBanner {
background-color: rgba(255, 0, 0, 0.1);
border: 1px solid var(--brand-red);
color: var(--brand-red);
padding: 8px 12px;
border-radius: 4px;
font-size: 0.8125rem;
margin-top: 12px;
display: flex;
align-items: center;
gap: 8px;
}
Loading
Loading