Ezmary commited on
Commit
f76e9d1
·
verified ·
1 Parent(s): 0336c37

Update src/App.tsx

Browse files
Files changed (1) hide show
  1. src/App.tsx +44 -26
src/App.tsx CHANGED
@@ -12,8 +12,8 @@ limitations under the License.
12
  */
13
 
14
  import React, { useEffect, useRef, useState } from "react";
15
- import './App.scss'; // اطمینان از ایمپورت شدن فایل SCSS
16
- import { LiveAPIProvider, useLiveAPIContext } from "./contexts/LiveAPIContext"; // useLiveAPIContext ایمپورت شد
17
  import ControlTray from "./components/control-tray/ControlTray";
18
  import { IOSModal } from "./components/ios-modal/IOSModal";
19
  import { isIOS } from "./lib/platform";
@@ -22,8 +22,6 @@ import { LiveConfig } from "./multimodal-live-types";
22
  import { Altair } from "./components/altair/Altair";
23
  import SidePanel from "./components/side-panel/SidePanel";
24
 
25
-
26
- // --- دستورالعمل شخصی‌سازی شما (با بخش تعامل تصویری) ---
27
  const myCustomInstruction = `
28
  ت1. هویت دستیار:
29
 
@@ -100,7 +98,6 @@ const myCustomInstruction = `
100
  - اگر کاربر سوالی در مورد چیزی که در تصویر می‌بیند پرسید، سعی کن بر اساس تصویر پاسخ دهی.
101
  - هدف اصلی، کمک به یادگیری زبان است، پس تعامل تصویری باید در خدمت این هدف باشد.
102
  `.trim();
103
- // --- ---
104
 
105
  const initialAppConfig: LiveConfig = {
106
  model: "models/gemini-2.0-flash-exp",
@@ -113,7 +110,6 @@ const SvgHumanIcon = () => (
113
  <svg width="70" height="70" viewBox="0 0 88 89" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M75.1481 81.6361H12.9259C9.66667 81.6361 7 78.9721 7 75.7161V58.5112C7 57.5862 7 57.1052 7.44444 56.2172C8.85185 52.9612 13 50.2232 19.4815 47.8922C24.1111 56.6982 33.3704 62.6921 44 62.6921C54.6296 62.6921 63.9259 56.6982 68.5185 47.8922C75 50.1862 79.1852 52.9982 80.5556 56.2172C81 56.6612 81 57.6232 81 58.5112V75.7161C81 78.9721 78.3333 81.6361 75.0741 81.6361H75.1481Z" stroke="#FCFCFC" strokeWidth="6.42146" strokeLinecap="round" strokeLinejoin="round"/><path d="M44.0371 50.1862C33.8519 50.1862 25.5186 41.8612 25.5186 31.6863V26.1363C25.5186 15.9613 33.8519 7.63635 44.0371 7.63635C54.2223 7.63635 62.5556 15.9613 62.5556 26.1363V31.6863C62.5556 41.8612 54.2223 50.1862 44.0371 50.1862Z" stroke="#FCFCFC" strokeWidth="6.42146" strokeLinecap="round" strokeLinejoin="round"/></svg>
114
  );
115
 
116
- // کامپوننت داخلی برای دسترسی به context و اجرای disconnect و سایر منطق‌های وابسته به context
117
  const AppInternalLogic: React.FC<{
118
  isMicActive: boolean;
119
  isCamActive: boolean;
@@ -128,8 +124,8 @@ const AppInternalLogic: React.FC<{
128
  }> = ({
129
  isMicActive,
130
  isCamActive,
131
- setIsMicActive, // برای استفاده در ControlTray
132
- setIsCamActive, // برای استفاده در ControlTray
133
  createLogoFunction,
134
  videoRef,
135
  notificationPopoverRef,
@@ -140,7 +136,6 @@ const AppInternalLogic: React.FC<{
140
  const { connected, disconnect } = useLiveAPIContext();
141
 
142
  useEffect(() => {
143
- // اگر هم میکروفون و هم دوربین غیرفعال شدند و ما متصل هستیم، اتصال را قطع کن
144
  if (!isMicActive && !isCamActive && connected) {
145
  console.log("AppInternalLogic: Both Mic and Cam are off. Disconnecting stream.");
146
  disconnect();
@@ -148,7 +143,8 @@ const AppInternalLogic: React.FC<{
148
  }, [isMicActive, isCamActive, connected, disconnect]);
149
 
150
  return (
151
- <div className="w-full flex flex-col items-center justify-center min-h-screen bg-background text-foreground antialiased">
 
152
  <div className="max-w-3xl w-full flex flex-col items-center justify-center h-full relative">
153
  {/* Header */}
154
  <div className="header-controls">
@@ -184,41 +180,58 @@ const AppInternalLogic: React.FC<{
184
  </div>
185
  </div>
186
 
187
- {/* Main Media Area and Chat Area */}
188
- <div className="flex flex-col md:flex-row w-full h-[calc(100vh-100px)] mt-[60px] mb-[40px]">
189
- <div className="w-full md:w-1/3 p-2 overflow-y-auto">
190
- <SidePanel />
191
- <Altair />
 
 
 
 
 
192
  </div>
193
 
194
- <div className="w-full md:w-2/3 flex flex-col items-center justify-center bg-background relative">
 
 
 
195
  <video
196
  id="video-feed"
197
  ref={videoRef}
198
  autoPlay
199
  playsInline
200
- className={cn("absolute top-0 left-0 w-full h-full object-cover scale-x-[-1]", {
201
- "hidden": !isCamActive,
202
- })}
 
203
  />
204
- <div id="large-logo-container" className={cn("items-center justify-center w-full h-full absolute top-0 left-0", {
205
- "flex": !isCamActive && isMicActive,
206
- "hidden": isCamActive || !isMicActive
207
- })}>
208
- {createLogoFunction(false, !isCamActive && isMicActive)}
 
 
 
 
 
 
 
209
  </div>
 
210
  </div>
211
  </div>
212
 
213
  <ControlTray
214
- videoRef={videoRef}
215
  supportsVideo={true}
216
  onVideoStreamChange={(stream) => { /* App.tsx no longer needs direct videoStream state */ }}
217
  isAppMicActive={isMicActive}
218
  onAppMicToggle={setIsMicActive}
219
  isAppCamActive={isCamActive}
220
  onAppCamToggle={setIsCamActive}
221
- createLogoFunction={createLogoFunction}
222
  />
223
  </div>
224
  </div>
@@ -282,6 +295,11 @@ function App() {
282
  : { ping: 40, outer: 0, mid: 20, inner: 50, icon: 65 };
283
  const bgColorBase = type === 'human' ? 'blue' : 'green';
284
 
 
 
 
 
 
285
  return (
286
  <div className="relative" style={{ width: `${size}px`, height: `${size}px` }}>
287
  <div className={`absolute rounded-full opacity-50 animate-ping bg-${bgColorBase}-200 dark:bg-${bgColorBase}-700`} style={{ inset: `${insetBase.ping}px` }}></div>
 
12
  */
13
 
14
  import React, { useEffect, useRef, useState } from "react";
15
+ import './App.scss';
16
+ import { LiveAPIProvider, useLiveAPIContext } from "./contexts/LiveAPIContext";
17
  import ControlTray from "./components/control-tray/ControlTray";
18
  import { IOSModal } from "./components/ios-modal/IOSModal";
19
  import { isIOS } from "./lib/platform";
 
22
  import { Altair } from "./components/altair/Altair";
23
  import SidePanel from "./components/side-panel/SidePanel";
24
 
 
 
25
  const myCustomInstruction = `
26
  ت1. هویت دستیار:
27
 
 
98
  - اگر کاربر سوالی در مورد چیزی که در تصویر می‌بیند پرسید، سعی کن بر اساس تصویر پاسخ دهی.
99
  - هدف اصلی، کمک به یادگیری زبان است، پس تعامل تصویری باید در خدمت این هدف باشد.
100
  `.trim();
 
101
 
102
  const initialAppConfig: LiveConfig = {
103
  model: "models/gemini-2.0-flash-exp",
 
110
  <svg width="70" height="70" viewBox="0 0 88 89" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M75.1481 81.6361H12.9259C9.66667 81.6361 7 78.9721 7 75.7161V58.5112C7 57.5862 7 57.1052 7.44444 56.2172C8.85185 52.9612 13 50.2232 19.4815 47.8922C24.1111 56.6982 33.3704 62.6921 44 62.6921C54.6296 62.6921 63.9259 56.6982 68.5185 47.8922C75 50.1862 79.1852 52.9982 80.5556 56.2172C81 56.6612 81 57.6232 81 58.5112V75.7161C81 78.9721 78.3333 81.6361 75.0741 81.6361H75.1481Z" stroke="#FCFCFC" strokeWidth="6.42146" strokeLinecap="round" strokeLinejoin="round"/><path d="M44.0371 50.1862C33.8519 50.1862 25.5186 41.8612 25.5186 31.6863V26.1363C25.5186 15.9613 33.8519 7.63635 44.0371 7.63635C54.2223 7.63635 62.5556 15.9613 62.5556 26.1363V31.6863C62.5556 41.8612 54.2223 50.1862 44.0371 50.1862Z" stroke="#FCFCFC" strokeWidth="6.42146" strokeLinecap="round" strokeLinejoin="round"/></svg>
111
  );
112
 
 
113
  const AppInternalLogic: React.FC<{
114
  isMicActive: boolean;
115
  isCamActive: boolean;
 
124
  }> = ({
125
  isMicActive,
126
  isCamActive,
127
+ setIsMicActive,
128
+ setIsCamActive,
129
  createLogoFunction,
130
  videoRef,
131
  notificationPopoverRef,
 
136
  const { connected, disconnect } = useLiveAPIContext();
137
 
138
  useEffect(() => {
 
139
  if (!isMicActive && !isCamActive && connected) {
140
  console.log("AppInternalLogic: Both Mic and Cam are off. Disconnecting stream.");
141
  disconnect();
 
143
  }, [isMicActive, isCamActive, connected, disconnect]);
144
 
145
  return (
146
+ // کلاس‌های اصلی از HTML مرجع
147
+ <div className="w-full flex flex-col items-center justify-center min-h-[90dvh] md:min-h-screen text-foreground antialiased"> {/* bg-background از body میاد */}
148
  <div className="max-w-3xl w-full flex flex-col items-center justify-center h-full relative">
149
  {/* Header */}
150
  <div className="header-controls">
 
180
  </div>
181
  </div>
182
 
183
+ {/* MediaToggle Area - این div اصلی است که ویدیو و لوگوی بزرگ را در بر می‌گیرد */}
184
+ {/* چیدمان Altair و SidePanel باید با دقت بیشتری بررسی شود اگر می‌خواهید دقیقاً مطابق طرح HTML باشند */}
185
+ {/* در اینجا فرض می‌کنیم چت و ویدیو در ستون‌های مجزا هستند */}
186
+ <div className="flex flex-col md:flex-row w-full flex-grow items-stretch"> {/* flex-grow and items-stretch */}
187
+ {/* Chat Area - ستون سمت چپ */}
188
+ <div className="w-full md:w-1/3 p-2 flex flex-col overflow-y-auto"> {/* flex flex-col */}
189
+ <SidePanel /> {/* این کامپوننت‌ها باید برای این چیدمان تنظیم شوند */}
190
+ <div className="flex-grow"> {/* Altair برای پر کردن فضای باقی‌مانده */}
191
+ <Altair />
192
+ </div>
193
  </div>
194
 
195
+ {/* Video and Logo Area - ستون سمت راست */}
196
+ {/* کلاس‌های HTML مرجع برای این بخش: "w-full flex flex-col items-center justify-center h-[90dvh] bg-background top-0 left-0 relative" */}
197
+ {/* ما از bg-background روی body استفاده کرده‌ایم، پس اینجا لازم نیست */}
198
+ <div className="w-full md:w-2/3 flex flex-col items-center justify-center relative flex-grow"> {/* relative and flex-grow */}
199
  <video
200
  id="video-feed"
201
  ref={videoRef}
202
  autoPlay
203
  playsInline
204
+ className={cn(
205
+ "absolute top-0 left-0 w-full h-full object-cover scale-x-[-1]",
206
+ { "hidden": !isCamActive } // نمایش فقط وقتی دوربین فعال است
207
+ )}
208
  />
209
+ {/* large-logo-container فقط وقتی میکروفون فعال و دوربین غیرفعال است نمایش داده می‌شود */}
210
+ <div
211
+ id="large-logo-container"
212
+ className={cn(
213
+ "w-full h-full absolute top-0 left-0 items-center justify-center", // کلاس‌های HTML مرجع
214
+ {
215
+ "flex": isMicActive && !isCamActive, // شرط نمایش
216
+ "hidden": !isMicActive || isCamActive, // شرط مخفی شدن
217
+ }
218
+ )}
219
+ >
220
+ {isMicActive && !isCamActive && createLogoFunction(false, true)}
221
  </div>
222
+ {/* اگر نه میکروفون و نه دوربین فعال باشند، این بخش خالی و سفید خواهد بود (چون ویدیو hidden و لوگو hidden است) */}
223
  </div>
224
  </div>
225
 
226
  <ControlTray
227
+ videoRef={videoRef} // ControlTray به videoRef نیاز دارد
228
  supportsVideo={true}
229
  onVideoStreamChange={(stream) => { /* App.tsx no longer needs direct videoStream state */ }}
230
  isAppMicActive={isMicActive}
231
  onAppMicToggle={setIsMicActive}
232
  isAppCamActive={isCamActive}
233
  onAppCamToggle={setIsCamActive}
234
+ createLogoFunction={createLogoFunction} // برای لوگوی کوچک در ControlTray
235
  />
236
  </div>
237
  </div>
 
295
  : { ping: 40, outer: 0, mid: 20, inner: 50, icon: 65 };
296
  const bgColorBase = type === 'human' ? 'blue' : 'green';
297
 
298
+ // اطمینان از وجود کلاس‌های Tailwind برای purgeCSS
299
+ // <div className="bg-blue-200 dark:bg-blue-700 bg-blue-300 dark:bg-blue-600 bg-blue-400 dark:bg-blue-500"></div>
300
+ // <div className="bg-green-200 dark:bg-green-700 bg-green-300 dark:bg-green-600 bg-green-400 dark:bg-green-500"></div>
301
+
302
+
303
  return (
304
  <div className="relative" style={{ width: `${size}px`, height: `${size}px` }}>
305
  <div className={`absolute rounded-full opacity-50 animate-ping bg-${bgColorBase}-200 dark:bg-${bgColorBase}-700`} style={{ inset: `${insetBase.ping}px` }}></div>