/** Copyright 2024 Google LLC ... (لایسنس و توضیحات دیگر مثل قبل) ... */ import React, { useEffect, useRef, useState } from "react"; import './App.scss'; import { LiveAPIProvider, useLiveAPIContext } from "./contexts/LiveAPIContext"; import ControlTray from "./components/control-tray/ControlTray"; import { IOSModal } from "./components/ios-modal/IOSModal"; import { isIOS } from "./lib/platform"; import cn from "classnames"; import { LiveConfig } from "./multimodal-live-types"; const myCustomInstruction = ` ... (دستورالعمل سفارشی شما بدون تغییر) ... `.trim(); const initialAppConfig: LiveConfig = { model: "models/gemini-2.0-flash-exp", systemInstruction: { parts: [{ text: myCustomInstruction }], }, }; // آیکون انسان از HTML شما (برای لوگوی بزرگ و کوچک) const SvgHumanIcon = () => ( ); // *** NEW: SVG آیکون میکروفون از HTML مرجع شما *** // این آیکون برای حالت فعال میکروفون (PauseIcon فعلی) استفاده خواهد شد // و آیکون دیگر (MicrophoneIcon فعلی) برای حالت غیرفعال const SvgReferenceMicrophoneIcon = () => ( ); const AppInternalLogic: React.FC<{ isMicActive: boolean; isCamActive: boolean; setIsMicActive: React.Dispatch>; setIsCamActive: React.Dispatch>; createLogoFunction: (isMini: boolean, isActive: boolean, type?: 'human' | 'ai', forFooter?: boolean) => React.ReactNode; // Added forFooter videoRef: React.RefObject; notificationPopoverRef: React.RefObject; notificationButtonRef: React.RefObject; isNotificationOpen: boolean; setIsNotificationOpen: React.Dispatch>; }> = ({ isMicActive, isCamActive, setIsMicActive, setIsCamActive, createLogoFunction, videoRef, notificationPopoverRef, notificationButtonRef, isNotificationOpen, setIsNotificationOpen }) => { const { connected, disconnect } = useLiveAPIContext(); useEffect(() => { if (!isMicActive && !isCamActive && connected) { disconnect(); } }, [isMicActive, isCamActive, connected, disconnect]); return (
{/* Added class main-wrapper */}
مدل‌های هوش مصنوعی می‌توانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید و از وارد کردن اطلاعات حساس بپرهیزید.
{/* Media and Large Logo Area */}
{/* Added class media-area */}
{ /* ... */ }} isAppMicActive={isMicActive} onAppMicToggle={setIsMicActive} isAppCamActive={isCamActive} onAppCamToggle={setIsCamActive} createLogoFunction={createLogoFunction} // Pass SvgReferenceMicrophoneIcon to ControlTray ReferenceMicrophoneIcon={SvgReferenceMicrophoneIcon} />
); } const logoColorConfig = { blue: { // For human ping: "bg-blue-200 dark:bg-blue-700", outer: "bg-blue-200 dark:bg-blue-700", mid: "bg-blue-300 dark:bg-blue-600", inner: "bg-blue-400 dark:bg-blue-500", }, green: { // For AI (if you add it) ping: "bg-green-200 dark:bg-green-700", outer: "bg-green-200 dark:bg-green-700", mid: "bg-green-300 dark:bg-green-600", inner: "bg-green-400 dark:bg-green-500", }, gray: { // Default/Fallback ping: "bg-gray-200 dark:bg-gray-700", outer: "bg-gray-200 dark:bg-gray-700", mid: "bg-gray-300 dark:bg-gray-600", inner: "bg-gray-400 dark:bg-gray-500", } }; function App() { const videoRef = useRef(null); const [showIOSModal, setShowIOSModal] = useState(false); const [isAllowedOrigin, setIsAllowedOrigin] = useState(null); const [isMicActive, setIsMicActive] = useState(false); const [isCamActive, setIsCamActive] = useState(false); const [isNotificationOpen, setIsNotificationOpen] = useState(false); const notificationPopoverRef = useRef(null); const notificationButtonRef = useRef(null); useEffect(() => { if (isIOS()) { setShowIOSModal(true); } const timer = setTimeout(() => { setIsAllowedOrigin(true); }, 100); return () => clearTimeout(timer); }, []); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if ( isNotificationOpen && notificationPopoverRef.current && !notificationPopoverRef.current.contains(event.target as Node) && notificationButtonRef.current && !notificationButtonRef.current.contains(event.target as Node) ) { setIsNotificationOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [isNotificationOpen]); if (isAllowedOrigin === null) { return
در حال بررسی دسترسی...
; } const createLogoFunction = (isMini: boolean, isActive: boolean, type: 'human' | 'ai' = 'human', forFooter: boolean = false) => { if (!isActive) return null; // Use blue for human, green for AI, gray as fallback const colorKey = type === 'human' ? 'blue' : (type === 'ai' ? 'green' : 'gray'); const currentColors = logoColorConfig[colorKey as keyof typeof logoColorConfig] || logoColorConfig.gray; const size = isMini ? 80 : 200; // Size for the overall logo animation const iconSize = isMini ? 35 : 70; // This is for the SvgHumanIcon's container, actual SVG might have own size // Adjust inset for icon based on whether it's for footer or large display // This is crucial for centering the icon within the pulsing circles. let iconInset; if (isMini) { // Small logo (for footer) iconInset = 22; // Original value for small } else { // Large logo (center screen) iconInset = 65; // Original value for large } const insetBase = { ping: isMini ? 10 : 40, outer: 0, mid: isMini ? 5 : 20, inner: isMini ? 12 : 50, icon: iconInset }; return (
{type === 'human' && } {/* Add AI icon here if needed: type === 'ai' && */}
); }; return ( setShowIOSModal(false)} /> ); } export default App;