Spaces:
Running
Running
Update src/App.tsx
Browse files- src/App.tsx +22 -103
src/App.tsx
CHANGED
@@ -10,8 +10,7 @@ import cn from "classnames";
|
|
10 |
import { LiveConfig } from "./multimodal-live-types";
|
11 |
import Logo from "./components/logo/Logo";
|
12 |
|
13 |
-
const myCustomInstruction =
|
14 |
-
تو دستیار صوتی و تصویری پیشرفته از "اپلیکیشن هوش مصنوعی هوشان" هستی
|
15 |
وظیفه اصلی تو کمک به کاربر است.
|
16 |
همیشه و تحت هر شرایطی، فقط به زبان فارسی بسیار روان، دقیق و طبیعی و زیبا صحبت کن. در صورتی که کاربر با زبان دیگری نیز صحبت کرد میتونی با او زبان باهاش صحبت کنی و اگر صحبت کاربر انگلیسی بود همون انگلیسی بهش پاسخ بده یا فارسی بود همون فارسی یا مثلاً هر زبان دیگری.
|
17 |
هرگز، به هیچ عنوان، خودت را به عنوان محصول ،open ai گوگل, مایکروسافت یا هر شرکت دیگری معرفی نکن. هویت تو تنها دستیار صوتی "اپلیکیشن هوش مصنوعی هوشان" است.
|
@@ -30,9 +29,7 @@ const myCustomInstruction = `
|
|
30 |
|
31 |
const initialAppConfig: LiveConfig = {
|
32 |
model: "models/gemini-2.0-flash-exp",
|
33 |
-
systemInstruction: {
|
34 |
-
parts: [{ text: myCustomInstruction }],
|
35 |
-
},
|
36 |
};
|
37 |
|
38 |
interface AppInternalLogicProps {
|
@@ -49,20 +46,9 @@ interface AppInternalLogicProps {
|
|
49 |
onFacingModeChange: (mode: 'user' | 'environment') => void;
|
50 |
}
|
51 |
|
52 |
-
const AppInternalLogic: React.FC<AppInternalLogicProps> = ({
|
53 |
-
isMicActive,
|
54 |
-
isCamActive,
|
55 |
-
setIsMicActive,
|
56 |
-
setIsCamActive,
|
57 |
-
videoRef,
|
58 |
-
notificationPopoverRef,
|
59 |
-
notificationButtonRef,
|
60 |
-
isNotificationOpen,
|
61 |
-
setIsNotificationOpen,
|
62 |
-
currentFacingMode,
|
63 |
-
onFacingModeChange,
|
64 |
-
}) => {
|
65 |
const { connected, disconnect, volume } = useLiveAPIContext();
|
|
|
66 |
|
67 |
useEffect(() => {
|
68 |
if (!isMicActive && !isCamActive && connected) {
|
@@ -74,75 +60,32 @@ const AppInternalLogic: React.FC<AppInternalLogicProps> = ({
|
|
74 |
<div className="w-full flex flex-col items-center justify-center min-h-screen text-foreground antialiased">
|
75 |
<div className="main-wrapper max-w-3xl w-full flex flex-col items-center justify-center h-full relative">
|
76 |
<div className="header-controls">
|
77 |
-
<div />
|
78 |
<div id="notification-trigger-container">
|
79 |
-
<button
|
80 |
-
ref={notificationButtonRef}
|
81 |
-
id="notification-button"
|
82 |
-
aria-label="Notifications"
|
83 |
-
className="p-2 bg-transparent text-gray-600 dark:text-gray-300 hover:text-black dark:hover:text-white transition-colors"
|
84 |
-
onClick={(e) => {
|
85 |
-
e.stopPropagation();
|
86 |
-
setIsNotificationOpen(!isNotificationOpen);
|
87 |
-
}}
|
88 |
-
>
|
89 |
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
|
90 |
</button>
|
91 |
</div>
|
92 |
</div>
|
93 |
-
|
94 |
<div ref={notificationPopoverRef} id="notification-popover-wrapper" className="notification-popover-wrapper">
|
95 |
-
<div
|
96 |
-
|
97 |
-
className={cn("popover-content", {
|
98 |
-
"open animate-popover-open-top-center": isNotificationOpen,
|
99 |
-
"animate-popover-close-top-center": !isNotificationOpen && document.getElementById('notification-popover')?.classList.contains('open'),
|
100 |
-
})}
|
101 |
-
>
|
102 |
-
<div className="notification-popover-text-content">
|
103 |
-
مدلهای هوش مصنوعی میتوانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید و از بیان اطلاعات حساس بپرهیزید.
|
104 |
-
</div>
|
105 |
</div>
|
106 |
</div>
|
107 |
-
|
108 |
<div className="media-area w-full flex flex-col items-center justify-center flex-grow relative">
|
109 |
-
<video
|
110 |
-
id="video-feed"
|
111 |
-
ref={videoRef}
|
112 |
-
autoPlay
|
113 |
-
playsInline
|
114 |
-
className={cn(
|
115 |
-
"absolute top-0 left-0 w-full h-full object-cover",
|
116 |
-
{ "hidden": !isCamActive },
|
117 |
-
{ "scale-x-[-1]": currentFacingMode === 'user' }
|
118 |
-
)}
|
119 |
-
/>
|
120 |
{isMicActive && !isCamActive && (
|
121 |
-
<div
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
isAi={false}
|
129 |
-
speakingVolume={volume}
|
130 |
-
/>
|
131 |
</div>
|
132 |
)}
|
133 |
</div>
|
134 |
-
|
135 |
-
<ControlTray
|
136 |
-
videoRef={videoRef}
|
137 |
-
supportsVideo={true}
|
138 |
-
onVideoStreamChange={(stream) => {}}
|
139 |
-
isAppMicActive={isMicActive}
|
140 |
-
onAppMicToggle={setIsMicActive}
|
141 |
-
isAppCamActive={isCamActive}
|
142 |
-
onAppCamToggle={setIsCamActive}
|
143 |
-
currentFacingMode={currentFacingMode}
|
144 |
-
onFacingModeChange={onFacingModeChange}
|
145 |
-
/>
|
146 |
</div>
|
147 |
</div>
|
148 |
);
|
@@ -160,31 +103,19 @@ function App() {
|
|
160 |
const notificationPopoverRef = useRef<HTMLDivElement>(null);
|
161 |
|
162 |
useEffect(() => {
|
163 |
-
if (isIOS())
|
164 |
-
|
165 |
-
}
|
166 |
-
const timer = setTimeout(() => {
|
167 |
-
setIsAllowedOrigin(true);
|
168 |
-
}, 100);
|
169 |
return () => clearTimeout(timer);
|
170 |
}, []);
|
171 |
|
172 |
useEffect(() => {
|
173 |
const handleClickOutside = (event: MouseEvent) => {
|
174 |
-
if (
|
175 |
-
isNotificationOpen &&
|
176 |
-
notificationPopoverRef.current &&
|
177 |
-
!notificationPopoverRef.current.contains(event.target as Node) &&
|
178 |
-
notificationButtonRef.current &&
|
179 |
-
!notificationButtonRef.current.contains(event.target as Node)
|
180 |
-
) {
|
181 |
setIsNotificationOpen(false);
|
182 |
}
|
183 |
};
|
184 |
document.addEventListener("mousedown", handleClickOutside);
|
185 |
-
return () => {
|
186 |
-
document.removeEventListener("mousedown", handleClickOutside);
|
187 |
-
};
|
188 |
}, [isNotificationOpen]);
|
189 |
|
190 |
if (isAllowedOrigin === null) {
|
@@ -193,19 +124,7 @@ function App() {
|
|
193 |
|
194 |
return (
|
195 |
<LiveAPIProvider initialConfig={initialAppConfig}>
|
196 |
-
<AppInternalLogic
|
197 |
-
isMicActive={isMicActive}
|
198 |
-
setIsMicActive={setIsMicActive}
|
199 |
-
isCamActive={isCamActive}
|
200 |
-
setIsCamActive={setIsCamActive}
|
201 |
-
videoRef={videoRef}
|
202 |
-
notificationPopoverRef={notificationPopoverRef}
|
203 |
-
notificationButtonRef={notificationButtonRef}
|
204 |
-
isNotificationOpen={isNotificationOpen}
|
205 |
-
setIsNotificationOpen={setIsNotificationOpen}
|
206 |
-
currentFacingMode={currentFacingMode}
|
207 |
-
onFacingModeChange={setCurrentFacingMode}
|
208 |
-
/>
|
209 |
<IOSModal isOpen={showIOSModal} onClose={() => setShowIOSModal(false)} />
|
210 |
</LiveAPIProvider>
|
211 |
);
|
|
|
10 |
import { LiveConfig } from "./multimodal-live-types";
|
11 |
import Logo from "./components/logo/Logo";
|
12 |
|
13 |
+
const myCustomInstruction = `تو دستیار صوتی و تصویری پیشرفته از "اپلیکیشن هوش مصنوعی هوشان" هستی
|
|
|
14 |
وظیفه اصلی تو کمک به کاربر است.
|
15 |
همیشه و تحت هر شرایطی، فقط به زبان فارسی بسیار روان، دقیق و طبیعی و زیبا صحبت کن. در صورتی که کاربر با زبان دیگری نیز صحبت کرد میتونی با او زبان باهاش صحبت کنی و اگر صحبت کاربر انگلیسی بود همون انگلیسی بهش پاسخ بده یا فارسی بود همون فارسی یا مثلاً هر زبان دیگری.
|
16 |
هرگز، به هیچ عنوان، خودت را به عنوان محصول ،open ai گوگل, مایکروسافت یا هر شرکت دیگری معرفی نکن. هویت تو تنها دستیار صوتی "اپلیکیشن هوش مصنوعی هوشان" است.
|
|
|
29 |
|
30 |
const initialAppConfig: LiveConfig = {
|
31 |
model: "models/gemini-2.0-flash-exp",
|
32 |
+
systemInstruction: { parts: [{ text: myCustomInstruction }] },
|
|
|
|
|
33 |
};
|
34 |
|
35 |
interface AppInternalLogicProps {
|
|
|
46 |
onFacingModeChange: (mode: 'user' | 'environment') => void;
|
47 |
}
|
48 |
|
49 |
+
const AppInternalLogic: React.FC<AppInternalLogicProps> = ({ isMicActive, isCamActive, setIsMicActive, setIsCamActive, videoRef, notificationPopoverRef, notificationButtonRef, isNotificationOpen, setIsNotificationOpen, currentFacingMode, onFacingModeChange }) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
const { connected, disconnect, volume } = useLiveAPIContext();
|
51 |
+
const [isUserSpeaking, setIsUserSpeaking] = useState(false);
|
52 |
|
53 |
useEffect(() => {
|
54 |
if (!isMicActive && !isCamActive && connected) {
|
|
|
60 |
<div className="w-full flex flex-col items-center justify-center min-h-screen text-foreground antialiased">
|
61 |
<div className="main-wrapper max-w-3xl w-full flex flex-col items-center justify-center h-full relative">
|
62 |
<div className="header-controls">
|
63 |
+
<div />
|
64 |
<div id="notification-trigger-container">
|
65 |
+
<button ref={notificationButtonRef} id="notification-button" aria-label="Notifications" className="p-2 bg-transparent text-gray-600 dark:text-gray-300 hover:text-black dark:hover:text-white transition-colors" onClick={(e) => { e.stopPropagation(); setIsNotificationOpen(!isNotificationOpen); }}>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
|
67 |
</button>
|
68 |
</div>
|
69 |
</div>
|
|
|
70 |
<div ref={notificationPopoverRef} id="notification-popover-wrapper" className="notification-popover-wrapper">
|
71 |
+
<div id="notification-popover" className={cn("popover-content", { "open animate-popover-open-top-center": isNotificationOpen, "animate-popover-close-top-center": !isNotificationOpen && document.getElementById('notification-popover')?.classList.contains('open'), })}>
|
72 |
+
<div className="notification-popover-text-content">مدلهای هوش مصنوعی میتوانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید و از بیان اطلاعات حساس بپرهیزید.</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
</div>
|
74 |
</div>
|
|
|
75 |
<div className="media-area w-full flex flex-col items-center justify-center flex-grow relative">
|
76 |
+
<video id="video-feed" ref={videoRef} autoPlay playsInline className={cn("absolute top-0 left-0 w-full h-full object-cover", { "hidden": !isCamActive }, { "scale-x-[-1]": currentFacingMode === 'user' })} />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
{isMicActive && !isCamActive && (
|
78 |
+
<div id="large-logo-container" className="absolute top-0 left-0 w-full h-full flex items-center justify-center pointer-events-none">
|
79 |
+
<Logo isMini={false} isActive={true} isAi={false} speakingVolume={volume} isUserSpeaking={isUserSpeaking} />
|
80 |
+
</div>
|
81 |
+
)}
|
82 |
+
{isCamActive && (
|
83 |
+
<div className="absolute bottom-24 right-4 z-20 pointer-events-none">
|
84 |
+
<Logo isMini={true} isActive={true} isAi={false} speakingVolume={volume} isUserSpeaking={isUserSpeaking} />
|
|
|
|
|
|
|
85 |
</div>
|
86 |
)}
|
87 |
</div>
|
88 |
+
<ControlTray videoRef={videoRef} supportsVideo={true} onVideoStreamChange={(stream) => {}} isAppMicActive={isMicActive} onAppMicToggle={setIsMicActive} isAppCamActive={isCamActive} onAppCamToggle={setIsCamActive} currentFacingMode={currentFacingMode} onFacingModeChange={onFacingModeChange} onUserSpeakingChange={setIsUserSpeaking} />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
</div>
|
90 |
</div>
|
91 |
);
|
|
|
103 |
const notificationPopoverRef = useRef<HTMLDivElement>(null);
|
104 |
|
105 |
useEffect(() => {
|
106 |
+
if (isIOS()) setShowIOSModal(true);
|
107 |
+
const timer = setTimeout(() => { setIsAllowedOrigin(true); }, 100);
|
|
|
|
|
|
|
|
|
108 |
return () => clearTimeout(timer);
|
109 |
}, []);
|
110 |
|
111 |
useEffect(() => {
|
112 |
const handleClickOutside = (event: MouseEvent) => {
|
113 |
+
if (isNotificationOpen && notificationPopoverRef.current && !notificationPopoverRef.current.contains(event.target as Node) && notificationButtonRef.current && !notificationButtonRef.current.contains(event.target as Node)) {
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
setIsNotificationOpen(false);
|
115 |
}
|
116 |
};
|
117 |
document.addEventListener("mousedown", handleClickOutside);
|
118 |
+
return () => { document.removeEventListener("mousedown", handleClickOutside); };
|
|
|
|
|
119 |
}, [isNotificationOpen]);
|
120 |
|
121 |
if (isAllowedOrigin === null) {
|
|
|
124 |
|
125 |
return (
|
126 |
<LiveAPIProvider initialConfig={initialAppConfig}>
|
127 |
+
<AppInternalLogic isMicActive={isMicActive} setIsMicActive={setIsMicActive} isCamActive={isCamActive} setIsCamActive={setIsCamActive} videoRef={videoRef} notificationPopoverRef={notificationPopoverRef} notificationButtonRef={notificationButtonRef} isNotificationOpen={isNotificationOpen} setIsNotificationOpen={setIsNotificationOpen} currentFacingMode={currentFacingMode} onFacingModeChange={setCurrentFacingMode} />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
<IOSModal isOpen={showIOSModal} onClose={() => setShowIOSModal(false)} />
|
129 |
</LiveAPIProvider>
|
130 |
);
|