Hamed744 commited on
Commit
5c0555e
·
verified ·
1 Parent(s): fe60ee7

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +661 -311
index.html CHANGED
@@ -3,397 +3,747 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Alpha TTS - نسخه پرایم</title>
7
  <style>
8
  @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;700;800&display=swap');
9
 
10
  :root {
11
  --app-font: 'Vazirmatn', sans-serif;
12
- --bg-dark: #0d0f1a;
13
- --panel-bg: rgba(23, 26, 41, 0.6);
14
- --border-color: rgba(129, 140, 248, 0.2);
15
- --text-primary: #e0e7ff;
16
- --text-secondary: #94a3b8;
17
- --accent-glow: #818cf8;
18
- --accent-gradient: linear-gradient(90deg, #a78bfa, #818cf8, #60a5fa);
19
- --radius-card: 24px;
 
 
 
20
  --radius-input: 12px;
21
- --shadow-glow: 0 0 30px -5px rgba(129, 140, 248, 0.3);
 
 
 
 
 
 
 
22
  }
23
 
24
  body {
25
  font-family: var(--app-font);
26
  direction: rtl;
27
- background-color: var(--bg-dark);
28
- color: var(--text-primary);
29
  font-size: 16px;
30
- line-height: 1.7;
31
  margin: 0;
 
32
  min-height: 100vh;
33
- overflow: hidden; /* برای جلوگیری از اسکرول به خاطر افکت پس‌زمینه */
34
- display: flex;
35
- align-items: center;
36
- justify-content: center;
37
- padding: 2rem 0;
38
  }
39
 
40
- /* --- افکت پس‌زمینه شفق قطبی (Aurora) --- */
41
- body::before {
42
- content: '';
43
  position: fixed;
44
- top: 0; left: 0; right: 0; bottom: 0;
45
- background-image:
46
- radial-gradient(circle at 15% 20%, rgba(167, 139, 250, 0.2), transparent 40%),
47
- radial-gradient(circle at 80% 30%, rgba(96, 165, 250, 0.2), transparent 40%),
48
- radial-gradient(circle at 50% 85%, rgba(139, 92, 246, 0.25), transparent 40%);
49
- animation: aurora 15s infinite linear;
50
  z-index: -1;
 
51
  }
52
 
53
- @keyframes aurora {
54
- 0% { transform: rotate(0deg); }
55
- 100% { transform: rotate(360deg); }
 
 
 
 
 
 
56
  }
57
 
58
- .container {
59
- max-width: 700px;
60
- width: 95%;
61
- max-height: 95vh;
62
- overflow-y: auto;
63
- scrollbar-width: thin;
64
- scrollbar-color: var(--accent-glow) transparent;
65
  }
66
- .container::-webkit-scrollbar { width: 6px; }
67
- .container::-webkit-scrollbar-track { background: transparent; }
68
- .container::-webkit-scrollbar-thumb { background-color: var(--accent-glow); border-radius: 10px; }
69
 
70
- .app-header {
71
- text-align: center;
72
- padding: 2rem 0;
 
 
 
 
73
  }
74
- .app-header h1 {
75
- font-size: 3.5em;
76
- font-weight: 800;
77
- margin: 0;
78
- background: var(--accent-gradient);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  -webkit-background-clip: text;
80
  -webkit-text-fill-color: transparent;
81
- text-shadow: 0 0 20px rgba(167, 139, 250, 0.3);
 
82
  }
83
- .app-header p {
84
- font-size: 1.25em;
85
- color: var(--text-secondary);
86
- margin-top: 0.5rem;
 
 
 
 
87
  }
88
-
89
- /* --- پنل اصلی با افکت شیشه‌ای --- */
90
- .main-content {
91
- padding: 2.5rem;
92
- background: var(--panel-bg);
93
- border-radius: var(--radius-card);
94
- border: 1px solid var(--border-color);
95
- box-shadow: var(--shadow-glow);
96
- backdrop-filter: blur(20px);
97
- -webkit-backdrop-filter: blur(20px);
98
  position: relative;
 
99
  }
100
 
101
- .form-group { margin-bottom: 2rem; }
102
- label { display: block; font-weight: 500; color: var(--text-primary); font-size: 1.1em; margin-bottom: 1rem; }
103
- textarea, input[type="text"] {
 
 
104
  width: 100%;
105
- padding: 1rem;
106
- border-radius: var(--radius-input);
107
- border: 1px solid var(--border-color);
108
- background-color: rgba(13, 15, 26, 0.5);
109
- color: var(--text-primary);
110
- font-family: var(--app-font);
111
- font-size: 1rem;
112
- box-sizing: border-box;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  transition: all 0.3s ease;
114
  }
115
- textarea:focus, input[type="text"]:focus {
116
- outline: none;
117
- border-color: var(--accent-glow);
118
- box-shadow: 0 0 15px rgba(129, 140, 248, 0.4);
119
- background-color: rgba(13, 15, 26, 0.8);
 
 
120
  }
121
 
122
- /* --- دکمه اصلی --- */
123
- #generate-btn {
124
- width: 100%;
125
- padding: 1rem 1.5rem;
126
- font-size: 1.25em;
127
- font-weight: 700;
128
- font-family: var(--app-font);
129
- color: white;
130
- border: none;
131
- border-radius: var(--radius-input);
132
- cursor: pointer;
133
- position: relative;
134
- overflow: hidden;
135
- background: var(--accent-gradient);
136
  transition: all 0.3s ease;
137
- box-shadow: 0 5px 20px -5px rgba(129, 140, 248, 0.5);
138
  }
139
- #generate-btn:hover:not(:disabled) {
 
140
  transform: translateY(-3px);
141
- box-shadow: 0 8px 25px -5px rgba(129, 140, 248, 0.6);
142
  }
143
- #generate-btn:disabled {
144
- background: #4b5563;
145
- cursor: not-allowed;
146
- box-shadow: none;
147
- transform: none;
 
 
 
 
148
  }
149
-
150
- /* --- بخش خروجی --- */
151
- #output-section {
152
- margin-top: 2.5rem;
153
- padding: 2rem;
154
- border-radius: var(--radius-card);
155
- min-height: 200px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  display: flex;
157
  align-items: center;
158
  justify-content: center;
159
- flex-direction: column;
160
- gap: 1rem;
161
- border: 2px dashed var(--border-color);
162
- transition: all 0.5s ease;
163
- }
164
- #status-message { font-weight: 500; color: var(--text-secondary); text-align: center; }
165
- #audio-player { width: 100%; margin-top: 1rem; display: none; }
166
- #audio-player::-webkit-media-controls-panel { background-color: rgba(30, 41, 59, 0.8); }
167
- #audio-player::-webkit-media-controls-play-button { color: var(--accent-glow); }
168
- /* دیگر استایل‌های پلیر... */
169
-
170
-
171
- /* --- انیمیشن پردازش (AI Core) --- */
172
- #loading-animation { display: none; flex-direction: column; align-items: center; justify-content: center; gap: 2rem; width: 100%; }
173
- .ai-core-container {
174
- width: 150px;
175
- height: 150px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  position: relative;
177
- display: flex;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  align-items: center;
179
  justify-content: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  }
181
- .ai-core-container .ring {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  position: absolute;
183
- width: 100%; height: 100%;
184
- border: 2px solid var(--accent-glow);
185
- border-radius: 50%;
186
- opacity: 0.5;
187
- animation: ring-pulse 3s infinite ease-out;
188
  }
189
- .ai-core-container .ring:nth-child(2) { animation-delay: -1s; }
190
- .ai-core-container .ring:nth-child(3) { animation-delay: -2s; }
191
 
192
- .ai-core-container .core {
193
- width: 50px; height: 50px;
194
- background: var(--accent-gradient);
 
 
195
  border-radius: 50%;
196
- box-shadow: 0 0 20px 5px var(--accent-glow);
197
- animation: core-pulse 1.5s infinite alternate ease-in-out;
198
  }
199
- #loading-text {
200
- font-size: 1.2em;
201
- font-weight: 500;
202
- color: var(--text-primary);
203
- text-shadow: 0 0 10px rgba(129, 140, 248, 0.5);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  }
205
- @keyframes ring-pulse {
206
- 0% { transform: scale(0.1); opacity: 0.8; }
207
- 100% { transform: scale(1); opacity: 0; }
 
208
  }
209
- @keyframes core-pulse {
210
- 0% { transform: scale(0.9); box-shadow: 0 0 15px 3px var(--accent-glow); }
211
- 100% { transform: scale(1.1); box-shadow: 0 0 30px 10px var(--accent-glow); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  }
213
  </style>
214
  </head>
215
  <body>
 
 
216
  <div class="container">
217
  <header class="app-header">
218
- <h1>Alpha TTS</h1>
219
- <p>آینده صدا را خلق کنید</p>
220
  </header>
221
 
222
  <main class="main-content">
223
  <form id="tts-form">
224
  <div class="form-group">
225
- <label for="text-input">متن ورودی</label>
226
- <textarea id="text-input" rows="5" placeholder="متن خود را اینجا وارد کنید...">این یک آزمایش برای بررسی کیفیت صدای تولید شده توسط هوش مصنوعی آلفا است.</textarea>
227
  </div>
228
- <!-- سایر فیلدها برای سادگی حذف شده‌اند تا تمرکز روی ظاهر باشد، اما می‌توانند اضافه شوند -->
229
 
230
- <button type="submit" id="generate-btn">
231
- <span>✨ تولید صدا</span>
232
- </button>
233
- </form>
234
 
235
- <div id="output-section">
236
- <div id="status-message">خروجی صدا در اینجا نمایش داده می‌شود</div>
237
- <!-- انیمیشن لودینگ -->
238
- <div id="loading-animation">
239
- <div class="ai-core-container">
240
- <div class="ring"></div>
241
- <div class="ring"></div>
242
- <div class="ring"></div>
243
- <div class="core"></div>
 
 
244
  </div>
245
- <p id="loading-text">در حال ساخت آینده صدا...</p>
246
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  <audio id="audio-player" controls></audio>
248
  </div>
249
  </main>
250
  </div>
251
 
252
- <!-- فیلدهای مخفی و مودال را برای سادگی حذف کردم، اما کد JS زیر هنوز می‌تواند با آنها کار کند -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  <input type="hidden" id="selected_speaker_id_storage" value="Charon">
254
- <input type="hidden" id="prompt-input" value="با صدایی طبیعی و روان.">
255
- <input type="hidden" id="temperature-slider" value="0.9">
256
-
257
  <script>
258
- // جاوا اسکریپت بدون تغییر باقی می‌ماند چون منطق اصلی درست کار می‌کرد.
259
- // فقط بخش مدیریت نمایش/پنهان کردن انیمیشن تغییر می‌کند.
260
  document.addEventListener('DOMContentLoaded', () => {
261
  const HF_SPACE_URL = "https://hamed744-ttspro.hf.space";
262
  const JOIN_QUEUE_URL = `${HF_SPACE_URL}/gradio_api/queue/join`;
263
  const GET_DATA_URL_BASE = `${HF_SPACE_URL}/gradio_api/queue/data`;
264
- const FILE_URL_BASE = `${HF_SPACE_URL}/gradio_api/file=`;
265
- const FN_INDEX = 1;
266
-
267
- const form = document.getElementById('tts-form');
268
- const textInput = document.getElementById('text-input');
269
- const promptInput = document.getElementById('prompt-input');
270
- const tempSlider = document.getElementById('temperature-slider');
271
- const generateBtn = document.getElementById('generate-btn');
272
-
273
- const statusMessage = document.getElementById('status-message');
274
- const audioPlayer = document.getElementById('audio-player');
275
- const loadingAnimation = document.getElementById('loading-animation');
276
- const selectedSpeakerIdStorage = document.getElementById('selected_speaker_id_storage');
277
-
278
- function showLoadingState() {
279
- statusMessage.style.display = 'none';
280
- audioPlayer.style.display = 'none';
281
- audioPlayer.src = '';
282
- loadingAnimation.style.display = 'flex';
283
- generateBtn.disabled = true;
284
- generateBtn.querySelector('span').textContent = 'در حال پردازش...';
285
- }
286
-
287
- function showResultState(isSuccess, message = '') {
288
- loadingAnimation.style.display = 'none';
289
- if (isSuccess) {
290
- statusMessage.style.display = 'none';
291
- audioPlayer.style.display = 'block';
292
- audioPlayer.play();
293
- } else {
294
- statusMessage.textContent = message || 'یک خطای ناشناخته رخ داد.';
295
- statusMessage.style.display = 'block';
296
- audioPlayer.style.display = 'none';
297
- }
298
- generateBtn.disabled = false;
299
- generateBtn.querySelector('span').textContent = '✨ تولید صدا';
300
- }
301
-
302
- // تابع شبیه‌سازی برای تست ظاهر بدون نیاز به API
303
- async function mockGenerateAudio(event) {
304
- event.preventDefault();
305
- showLoadingState();
306
- console.log("شبیه‌سازی شروع شد...");
307
-
308
- // 5 ثانیه صبر می‌کنیم تا انیمیشن دیده شود
309
- await new Promise(resolve => setTimeout(resolve, 5000));
310
-
311
- const isSuccess = Math.random() > 0.2; // 80% شانس موفقیت
312
- if(isSuccess) {
313
- console.log("شبیه‌سازی با موفقیت تمام شد.");
314
- // یک فایل صوتی نمونه برای نمایش پلیر
315
- audioPlayer.src = "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3";
316
- showResultState(true);
317
- } else {
318
- console.log("شبیه‌سازی با خطا تمام شد.");
319
- showResultState(false, "خطا در شبیه‌سازی! لطفاً دوباره تلاش کنید.");
320
- }
321
- }
322
-
323
- // برای استفاده واقعی، این تابع را جایگزین mockGenerateAudio کنید
324
- async function generateAudio(event) {
325
- event.preventDefault();
326
- showLoadingState();
327
-
328
- const text = textInput.value;
329
- if (!text.trim()) {
330
- showResultState(false, 'خطا: متن ورودی نمی‌تواند خالی باشد.');
331
- return;
332
- }
333
-
334
- const prompt = promptInput.value;
335
- const temperature = parseFloat(tempSlider.value);
336
- const selectedSpeaker = selectedSpeakerIdStorage.value;
337
- const sessionHash = Math.random().toString(36).substring(2);
338
-
339
- const payload = {
340
- fn_index: FN_INDEX,
341
- data: [false, null, text, prompt, selectedSpeaker, temperature],
342
- event_data: null,
343
- session_hash: sessionHash
344
- };
345
-
346
- try {
347
- const joinQueueResponse = await fetch(JOIN_QUEUE_URL, {
348
- method: "POST",
349
- headers: { "Content-Type": "application/json" },
350
- body: JSON.stringify(payload)
351
- });
352
- if (!joinQueueResponse.ok) throw new Error(`خطا در اتصال: ${joinQueueResponse.statusText}`);
353
-
354
- const dataResponse = await fetch(`${GET_DATA_URL_BASE}?session_hash=${sessionHash}`);
355
- const reader = dataResponse.body.getReader();
356
- const decoder = new TextDecoder();
357
- let finalFilePath = null;
358
- let buffer = '';
359
-
360
- while (true) {
361
- const { value, done } = await reader.read();
362
- if (done) break;
363
- buffer += decoder.decode(value, { stream: true });
364
- const lines = buffer.split('\n');
365
- buffer = lines.pop();
366
- for (const line of lines) {
367
- if (!line.startsWith('data:')) continue;
368
- try {
369
- const data = JSON.parse(line.substring(5));
370
- if (data.msg === 'process_completed') {
371
- if (data.success && data.output.data[0] && (data.output.data[0].name || data.output.data[0].path)) {
372
- finalFilePath = data.output.data[0].name || data.output.data[0].path;
373
- }
374
- break;
375
- }
376
- } catch (e) {}
377
- }
378
- if (finalFilePath) break;
379
- }
380
-
381
- if (finalFilePath) {
382
- audioPlayer.src = `${FILE_URL_BASE}${finalFilePath}`;
383
- showResultState(true);
384
- } else {
385
- throw new Error('فایل صوتی از سرور دریافت نشد.');
386
- }
387
- } catch (error) {
388
- console.error('خطا:', error);
389
- showResultState(false, `یک خطا رخ داد: ${error.message}`);
390
- }
391
- }
392
-
393
- // **مهم:** برای تست ظاهری از mockGenerateAudio و برای کارکرد واقعی از generateAudio استفاده کنید
394
- form.addEventListener('submit', mockGenerateAudio);
395
- // form.addEventListener('submit', generateAudio);
396
- });
397
- </script>
398
- </body>
399
- </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Alpha TTS - رابط کاربری هوشمند با عکس</title>
7
  <style>
8
  @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;700;800&display=swap');
9
 
10
  :root {
11
  --app-font: 'Vazirmatn', sans-serif;
12
+ --app-header-grad-start: #667eea;
13
+ --app-header-grad-end: #764ba2;
14
+ --app-panel-bg: #FFFFFF;
15
+ --app-input-bg: #F8F9FA;
16
+ --app-button-bg: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ --app-button-hover-bg: linear-gradient(135deg, #5a67d8 0%, #6b46c1 100%);
18
+ --app-main-bg: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
19
+ --app-text-primary: #2d3748;
20
+ --app-text-secondary: #4a5568;
21
+ --app-border-color: #e2e8f0;
22
+ --radius-card: 20px;
23
  --radius-input: 12px;
24
+ --shadow-card: 0 20px 40px -10px rgba(0,0,0,0.1);
25
+ --shadow-button: 0 8px 25px -5px rgba(102, 126, 234, 0.4);
26
+ --speaker-selected-glow: 0 0 20px rgba(102, 126, 234, 0.6);
27
+ --accent-color: #667eea;
28
+ }
29
+
30
+ * {
31
+ box-sizing: border-box;
32
  }
33
 
34
  body {
35
  font-family: var(--app-font);
36
  direction: rtl;
37
+ background: var(--app-main-bg);
38
+ color: var(--app-text-primary);
39
  font-size: 16px;
40
+ line-height: 1.65;
41
  margin: 0;
42
+ padding: 0;
43
  min-height: 100vh;
44
+ -webkit-font-smoothing: antialiased;
45
+ -moz-osx-font-smoothing: grayscale;
46
+ overflow-x: hidden;
 
 
47
  }
48
 
49
+ /* Animated Background */
50
+ .animated-bg {
 
51
  position: fixed;
52
+ top: 0;
53
+ left: 0;
54
+ width: 100%;
55
+ height: 100%;
 
 
56
  z-index: -1;
57
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
58
  }
59
 
60
+ .animated-bg::before {
61
+ content: '';
62
+ position: absolute;
63
+ top: 0;
64
+ left: 0;
65
+ width: 100%;
66
+ height: 100%;
67
+ background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="20" cy="20" r="2" fill="rgba(255,255,255,0.1)"><animate attributeName="opacity" values="0.1;0.3;0.1" dur="3s" repeatCount="indefinite"/></circle><circle cx="80" cy="40" r="1.5" fill="rgba(255,255,255,0.1)"><animate attributeName="opacity" values="0.1;0.4;0.1" dur="2s" repeatCount="indefinite"/></circle><circle cx="40" cy="70" r="2.5" fill="rgba(255,255,255,0.1)"><animate attributeName="opacity" values="0.1;0.2;0.1" dur="4s" repeatCount="indefinite"/></circle><circle cx="90" cy="80" r="1" fill="rgba(255,255,255,0.1)"><animate attributeName="opacity" values="0.1;0.5;0.1" dur="2.5s" repeatCount="indefinite"/></circle></svg>');
68
+ animation: float 20s infinite linear;
69
  }
70
 
71
+ @keyframes float {
72
+ 0% { transform: translateY(0px); }
73
+ 50% { transform: translateY(-20px); }
74
+ 100% { transform: translateY(0px); }
 
 
 
75
  }
 
 
 
76
 
77
+ .container {
78
+ max-width: 900px;
79
+ width: 95%;
80
+ margin: 0 auto;
81
+ padding-bottom: 40px;
82
+ position: relative;
83
+ z-index: 1;
84
  }
85
+
86
+ .app-header {
87
+ padding: 4rem 2rem 6rem 2rem;
88
+ text-align: center;
89
+ background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%);
90
+ backdrop-filter: blur(10px);
91
+ border: 1px solid rgba(255,255,255,0.2);
92
+ color: white;
93
+ border-radius: var(--radius-card);
94
+ box-shadow: 0 20px 40px -10px rgba(0,0,0,0.3);
95
+ margin-bottom: 2rem;
96
+ position: relative;
97
+ overflow: hidden;
98
+ }
99
+
100
+ .app-header::before {
101
+ content: '';
102
+ position: absolute;
103
+ top: -50%;
104
+ left: -50%;
105
+ width: 200%;
106
+ height: 200%;
107
+ background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.1) 50%, transparent 70%);
108
+ animation: shine 3s infinite;
109
+ }
110
+
111
+ @keyframes shine {
112
+ 0% { transform: translateX(-100%) translateY(-100%) rotate(45deg); }
113
+ 100% { transform: translateX(100%) translateY(100%) rotate(45deg); }
114
+ }
115
+
116
+ .app-header h1 {
117
+ font-size: 3.5em;
118
+ font-weight: 800;
119
+ margin: 0 0 1rem 0;
120
+ text-shadow: 0 4px 8px rgba(0,0,0,0.3);
121
+ background: linear-gradient(45deg, #fff, #f0f0f0);
122
+ background-clip: text;
123
  -webkit-background-clip: text;
124
  -webkit-text-fill-color: transparent;
125
+ position: relative;
126
+ z-index: 2;
127
  }
128
+
129
+ .app-header p {
130
+ font-size: 1.3em;
131
+ color: rgba(255,255,255,0.9);
132
+ margin-top: 0;
133
+ opacity: 0.9;
134
+ position: relative;
135
+ z-index: 2;
136
  }
137
+
138
+ .main-content {
139
+ padding: 3rem;
140
+ background: rgba(255,255,255,0.95);
141
+ backdrop-filter: blur(10px);
142
+ border: 1px solid rgba(255,255,255,0.2);
143
+ border-radius: var(--radius-card);
144
+ box-shadow: var(--shadow-card);
 
 
145
  position: relative;
146
+ overflow: hidden;
147
  }
148
 
149
+ .main-content::before {
150
+ content: '';
151
+ position: absolute;
152
+ top: 0;
153
+ left: 0;
154
  width: 100%;
155
+ height: 100%;
156
+ background: linear-gradient(135deg, rgba(102,126,234,0.02) 0%, rgba(118,75,162,0.02) 100%);
157
+ pointer-events: none;
158
+ }
159
+
160
+ .form-group {
161
+ margin-bottom: 2.5rem;
162
+ position: relative;
163
+ z-index: 1;
164
+ }
165
+
166
+ label {
167
+ display: block;
168
+ font-weight: 700;
169
+ color: var(--app-text-primary);
170
+ font-size: 1.1em;
171
+ margin-bottom: 0.8rem;
172
+ display: flex;
173
+ align-items: center;
174
+ gap: 0.5rem;
175
+ }
176
+
177
+ textarea, input[type="text"] {
178
+ width: 100%;
179
+ padding: 1.2rem;
180
+ border-radius: var(--radius-input);
181
+ border: 2px solid var(--app-border-color);
182
+ background: rgba(255,255,255,0.8);
183
+ backdrop-filter: blur(5px);
184
+ box-shadow: 0 4px 15px rgba(0,0,0,0.05);
185
+ font-family: var(--app-font);
186
+ font-size: 1rem;
187
  transition: all 0.3s ease;
188
  }
189
+
190
+ textarea:focus, input[type="text"]:focus {
191
+ outline: none;
192
+ border-color: var(--accent-color);
193
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2), 0 8px 25px rgba(0,0,0,0.1);
194
+ background: rgba(255,255,255,0.95);
195
+ transform: translateY(-2px);
196
  }
197
 
198
+ /* Selected Speaker Display */
199
+ #selected-speaker-display {
200
+ text-align: center;
201
+ }
202
+
203
+ #selected-speaker-card {
204
+ display: inline-flex;
205
+ align-items: center;
206
+ background: rgba(255,255,255,0.9);
207
+ backdrop-filter: blur(10px);
208
+ border-radius: 25px;
209
+ padding: 12px;
210
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
211
+ border: 2px solid rgba(102,126,234,0.2);
212
  transition: all 0.3s ease;
 
213
  }
214
+
215
+ #selected-speaker-card:hover {
216
  transform: translateY(-3px);
217
+ box-shadow: 0 15px 35px rgba(0,0,0,0.15);
218
  }
219
+
220
+ #selected-speaker-card img {
221
+ width: 90px;
222
+ height: 90px;
223
+ border-radius: 50%;
224
+ object-fit: cover;
225
+ margin-left: 15px;
226
+ background-color: #eee;
227
+ border: 3px solid rgba(102,126,234,0.3);
228
  }
229
+
230
+ #selected-speaker-info h3 {
231
+ margin: 0;
232
+ font-size: 1.4em;
233
+ color: var(--app-text-primary);
234
+ }
235
+
236
+ #selected-speaker-info p {
237
+ margin: 5px 0 0;
238
+ color: var(--app-text-secondary);
239
+ font-size: 0.9em;
240
+ }
241
+
242
+ #change-speaker-btn {
243
+ display: block;
244
+ margin: 1.5rem auto 0;
245
+ padding: 12px 30px;
246
+ border-radius: 25px;
247
+ background: linear-gradient(135deg, rgba(102,126,234,0.1) 0%, rgba(118,75,162,0.1) 100%);
248
+ border: 2px solid rgba(102,126,234,0.2);
249
+ cursor: pointer;
250
+ font-family: var(--app-font);
251
+ font-weight: 600;
252
+ transition: all 0.3s ease;
253
+ backdrop-filter: blur(5px);
254
+ }
255
+
256
+ #change-speaker-btn:hover {
257
+ background: linear-gradient(135deg, rgba(102,126,234,0.2) 0%, rgba(118,75,162,0.2) 100%);
258
+ transform: translateY(-2px);
259
+ box-shadow: 0 8px 20px rgba(102,126,234,0.2);
260
+ }
261
+
262
+ /* Speaker Modal */
263
+ #speaker-modal {
264
+ position: fixed;
265
+ top: 0;
266
+ left: 0;
267
+ width: 100%;
268
+ height: 100%;
269
+ background-color: rgba(0,0,0,0.6);
270
+ backdrop-filter: blur(10px);
271
+ display: none;
272
+ align-items: center;
273
+ justify-content: center;
274
+ z-index: 1000;
275
+ opacity: 0;
276
+ transition: opacity 0.3s ease;
277
+ }
278
+
279
+ #speaker-modal.visible {
280
+ display: flex;
281
+ opacity: 1;
282
+ }
283
+
284
+ .modal-content {
285
+ background: rgba(255,255,255,0.95);
286
+ backdrop-filter: blur(20px);
287
+ padding: 2.5rem;
288
+ border-radius: var(--radius-card);
289
+ width: 90%;
290
+ max-width: 800px;
291
+ max-height: 80vh;
292
+ overflow-y: auto;
293
+ transform: scale(0.9);
294
+ transition: transform 0.3s ease;
295
+ border: 1px solid rgba(255,255,255,0.3);
296
+ box-shadow: 0 30px 60px rgba(0,0,0,0.3);
297
+ }
298
+
299
+ #speaker-modal.visible .modal-content {
300
+ transform: scale(1);
301
+ }
302
+
303
+ .modal-header {
304
+ display: flex;
305
+ justify-content: space-between;
306
+ align-items: center;
307
+ margin-bottom: 2rem;
308
+ }
309
+
310
+ .modal-header h2 {
311
+ margin: 0;
312
+ color: var(--app-text-primary);
313
+ font-size: 1.8em;
314
+ }
315
+
316
+ .close-modal-btn {
317
+ background: none;
318
+ border: none;
319
+ font-size: 2rem;
320
+ cursor: pointer;
321
+ color: #aaa;
322
+ transition: all 0.2s ease;
323
+ width: 40px;
324
+ height: 40px;
325
+ border-radius: 50%;
326
  display: flex;
327
  align-items: center;
328
  justify-content: center;
329
+ }
330
+
331
+ .close-modal-btn:hover {
332
+ color: #333;
333
+ background: rgba(0,0,0,0.1);
334
+ }
335
+
336
+ #speaker-grid {
337
+ display: grid;
338
+ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
339
+ gap: 1.5rem;
340
+ }
341
+
342
+ @media (min-width: 576px) {
343
+ #speaker-grid {
344
+ grid-template-columns: repeat(4, 1fr);
345
+ }
346
+ }
347
+
348
+ .speaker-card {
349
+ cursor: pointer;
350
+ transition: all 0.3s ease;
351
+ }
352
+
353
+ .speaker-card .speaker-visual {
354
+ border: 3px solid transparent;
355
+ border-radius: var(--radius-card);
356
+ overflow: hidden;
357
+ text-align: center;
358
+ box-shadow: 0 8px 20px rgba(0,0,0,0.1);
359
+ position: relative;
360
+ background: rgba(255,255,255,0.9);
361
+ backdrop-filter: blur(5px);
362
+ }
363
+
364
+ .speaker-card:hover .speaker-visual {
365
+ transform: translateY(-5px);
366
+ box-shadow: 0 15px 30px rgba(0,0,0,0.2);
367
+ }
368
+
369
+ .speaker-card input[type="radio"] {
370
+ display: none;
371
+ }
372
+
373
+ .speaker-card img {
374
+ width: 100%;
375
+ height: 140px;
376
+ object-fit: cover;
377
+ display: block;
378
+ background-color: #eee;
379
+ }
380
+
381
+ .speaker-card .speaker-name {
382
+ padding: 1rem 0.5rem;
383
+ font-weight: 600;
384
+ font-size: 0.9em;
385
+ color: var(--app-text-primary);
386
+ }
387
+
388
+ .speaker-card input[type="radio"]:checked + .speaker-visual {
389
+ border-color: var(--accent-color);
390
+ box-shadow: var(--speaker-selected-glow);
391
+ }
392
+
393
+ /* Slider */
394
+ .slider-container {
395
+ display: flex;
396
+ align-items: center;
397
+ gap: 1rem;
398
+ }
399
+
400
+ input[type="range"] {
401
+ flex-grow: 1;
402
+ cursor: pointer;
403
+ height: 6px;
404
+ border-radius: 3px;
405
+ background: linear-gradient(135deg, var(--accent-color) 0%, #764ba2 100%);
406
+ outline: none;
407
+ -webkit-appearance: none;
408
+ }
409
+
410
+ input[type="range"]::-webkit-slider-thumb {
411
+ -webkit-appearance: none;
412
+ width: 20px;
413
+ height: 20px;
414
+ border-radius: 50%;
415
+ background: linear-gradient(135deg, var(--accent-color) 0%, #764ba2 100%);
416
+ cursor: pointer;
417
+ box-shadow: 0 4px 10px rgba(0,0,0,0.2);
418
+ }
419
+
420
+ #temperature-value {
421
+ font-weight: bold;
422
+ background: linear-gradient(135deg, rgba(102,126,234,0.1) 0%, rgba(118,75,162,0.1) 100%);
423
+ padding: 0.5rem 1rem;
424
+ border-radius: 20px;
425
+ border: 2px solid rgba(102,126,234,0.2);
426
+ min-width: 50px;
427
+ text-align: center;
428
+ backdrop-filter: blur(5px);
429
+ }
430
+
431
+ #generate-btn {
432
+ width: 100%;
433
+ padding: 1.5rem 2rem;
434
+ font-size: 1.3em;
435
+ font-weight: 700;
436
+ font-family: var(--app-font);
437
+ background: var(--app-button-bg);
438
+ color: white;
439
+ border: none;
440
+ border-radius: var(--radius-input);
441
+ cursor: pointer;
442
+ transition: all 0.3s ease;
443
+ box-shadow: var(--shadow-button);
444
  position: relative;
445
+ overflow: hidden;
446
+ }
447
+
448
+ #generate-btn:hover:not(:disabled) {
449
+ background: var(--app-button-hover-bg);
450
+ transform: translateY(-3px);
451
+ box-shadow: 0 12px 35px -5px rgba(102, 126, 234, 0.5);
452
+ }
453
+
454
+ #generate-btn:disabled {
455
+ background: #999;
456
+ cursor: not-allowed;
457
+ box-shadow: none;
458
+ }
459
+
460
+ #output-section {
461
+ margin-top: 2.5rem;
462
+ padding: 2rem;
463
+ background: rgba(255,255,255,0.8);
464
+ backdrop-filter: blur(10px);
465
+ border-radius: var(--radius-card);
466
+ min-height: 120px;
467
+ display: flex;
468
+ align-items: center;
469
+ justify-content: center;
470
+ flex-direction: column;
471
+ gap: 1rem;
472
+ border: 2px solid rgba(102,126,234,0.1);
473
+ box-shadow: 0 10px 25px rgba(0,0,0,0.05);
474
+ }
475
+
476
+ #status-message {
477
+ font-weight: 600;
478
+ color: var(--app-text-secondary);
479
+ font-size: 1.1em;
480
+ }
481
+
482
+ #audio-player {
483
+ width: 100%;
484
+ margin-top: 1rem;
485
+ display: none;
486
+ border-radius: 10px;
487
+ }
488
+
489
+ /* Loading Animation */
490
+ .loading-overlay {
491
+ position: fixed;
492
+ top: 0;
493
+ left: 0;
494
+ width: 100%;
495
+ height: 100%;
496
+ background: rgba(0,0,0,0.8);
497
+ backdrop-filter: blur(10px);
498
+ display: none;
499
  align-items: center;
500
  justify-content: center;
501
+ z-index: 9999;
502
+ opacity: 0;
503
+ transition: opacity 0.3s ease;
504
+ }
505
+
506
+ .loading-overlay.visible {
507
+ display: flex;
508
+ opacity: 1;
509
+ }
510
+
511
+ .loading-content {
512
+ text-align: center;
513
+ color: white;
514
+ max-width: 500px;
515
+ padding: 2rem;
516
+ }
517
+
518
+ .loading-title {
519
+ font-size: 2em;
520
+ font-weight: 700;
521
+ margin-bottom: 1rem;
522
+ background: linear-gradient(45deg, #667eea, #764ba2, #667eea, #764ba2);
523
+ background-size: 300% 300%;
524
+ background-clip: text;
525
+ -webkit-background-clip: text;
526
+ -webkit-text-fill-color: transparent;
527
+ animation: gradientFlow 3s ease-in-out infinite;
528
+ }
529
+
530
+ @keyframes gradientFlow {
531
+ 0%, 100% { background-position: 0% 50%; }
532
+ 50% { background-position: 100% 50%; }
533
+ }
534
+
535
+ .loading-subtitle {
536
+ font-size: 1.2em;
537
+ margin-bottom: 2rem;
538
+ opacity: 0.8;
539
+ }
540
+
541
+ .sound-wave {
542
+ display: flex;
543
+ justify-content: center;
544
+ align-items: flex-end;
545
+ height: 100px;
546
+ gap: 4px;
547
+ margin-bottom: 2rem;
548
+ }
549
+
550
+ .wave-bar {
551
+ width: 6px;
552
+ background: linear-gradient(45deg, #667eea, #764ba2);
553
+ border-radius: 3px;
554
+ animation: wave 1.5s ease-in-out infinite;
555
  }
556
+
557
+ .wave-bar:nth-child(1) { animation-delay: 0s; }
558
+ .wave-bar:nth-child(2) { animation-delay: 0.1s; }
559
+ .wave-bar:nth-child(3) { animation-delay: 0.2s; }
560
+ .wave-bar:nth-child(4) { animation-delay: 0.3s; }
561
+ .wave-bar:nth-child(5) { animation-delay: 0.4s; }
562
+ .wave-bar:nth-child(6) { animation-delay: 0.5s; }
563
+ .wave-bar:nth-child(7) { animation-delay: 0.6s; }
564
+ .wave-bar:nth-child(8) { animation-delay: 0.7s; }
565
+
566
+ @keyframes wave {
567
+ 0%, 100% { height: 20px; }
568
+ 50% { height: 80px; }
569
+ }
570
+
571
+ .floating-particles {
572
  position: absolute;
573
+ width: 100%;
574
+ height: 100%;
575
+ overflow: hidden;
576
+ pointer-events: none;
 
577
  }
 
 
578
 
579
+ .particle {
580
+ position: absolute;
581
+ width: 4px;
582
+ height: 4px;
583
+ background: rgba(255,255,255,0.6);
584
  border-radius: 50%;
585
+ animation: float-particle 4s infinite ease-in-out;
 
586
  }
587
+
588
+ .particle:nth-child(1) { left: 10%; animation-delay: 0s; }
589
+ .particle:nth-child(2) { left: 20%; animation-delay: 0.5s; }
590
+ .particle:nth-child(3) { left: 30%; animation-delay: 1s; }
591
+ .particle:nth-child(4) { left: 40%; animation-delay: 1.5s; }
592
+ .particle:nth-child(5) { left: 50%; animation-delay: 2s; }
593
+ .particle:nth-child(6) { left: 60%; animation-delay: 2.5s; }
594
+ .particle:nth-child(7) { left: 70%; animation-delay: 3s; }
595
+ .particle:nth-child(8) { left: 80%; animation-delay: 3.5s; }
596
+ .particle:nth-child(9) { left: 90%; animation-delay: 4s; }
597
+
598
+ @keyframes float-particle {
599
+ 0% {
600
+ transform: translateY(100px) rotate(0deg);
601
+ opacity: 0;
602
+ }
603
+ 10% { opacity: 1; }
604
+ 90% { opacity: 1; }
605
+ 100% {
606
+ transform: translateY(-100px) rotate(360deg);
607
+ opacity: 0;
608
+ }
609
+ }
610
+
611
+ .ai-icon {
612
+ font-size: 4em;
613
+ margin-bottom: 1rem;
614
+ animation: pulse 2s ease-in-out infinite;
615
  }
616
+
617
+ @keyframes pulse {
618
+ 0%, 100% { transform: scale(1); }
619
+ 50% { transform: scale(1.1); }
620
  }
621
+
622
+ /* Responsive Design */
623
+ @media (max-width: 768px) {
624
+ .app-header {
625
+ padding: 3rem 1.5rem 4rem 1.5rem;
626
+ }
627
+
628
+ .app-header h1 {
629
+ font-size: 2.5em;
630
+ }
631
+
632
+ .main-content {
633
+ padding: 2rem;
634
+ }
635
+
636
+ .container {
637
+ width: 98%;
638
+ }
639
+
640
+ #speaker-grid {
641
+ grid-template-columns: repeat(2, 1fr);
642
+ }
643
  }
644
  </style>
645
  </head>
646
  <body>
647
+ <div class="animated-bg"></div>
648
+
649
  <div class="container">
650
  <header class="app-header">
651
+ <h1>🎤 آلفا TTS</h1>
652
+ <p>✨ جادوی تبدیل متن به صدا، به سبک شما ✨</p>
653
  </header>
654
 
655
  <main class="main-content">
656
  <form id="tts-form">
657
  <div class="form-group">
658
+ <label for="text-input">📝 متن برای تبدیل</label>
659
+ <textarea id="text-input" rows="5" placeholder="اینجا متن خود را به فارسی وارد کنید...">این یک آزمایش برای بررسی کیفیت صدای تولید شده توسط هوش مصنوعی آلفا است.</textarea>
660
  </div>
 
661
 
662
+ <div class="form-group">
663
+ <label for="prompt-input">🎭 سبک و لحن گفتار (اختیاری)</label>
664
+ <input type="text" id="prompt-input" value="با صدایی طبیعی و روان." placeholder="مثال: با لحنی شاد و پرانرژی">
665
+ </div>
666
 
667
+ <div class="form-group">
668
+ <label>🎤 گوینده منتخب</label>
669
+ <div id="selected-speaker-display">
670
+ <div id="selected-speaker-card">
671
+ <img id="selected-speaker-img" src="" alt="عکس گوینده">
672
+ <div id="selected-speaker-info">
673
+ <h3 id="selected-speaker-name"></h3>
674
+ <p>💡 برای تغییر، روی دکمه زیر کلیک کنید</p>
675
+ </div>
676
+ </div>
677
+ <button type="button" id="change-speaker-btn">🔄 تغییر گوینده</button>
678
  </div>
 
679
  </div>
680
+
681
+ <div class="form-group">
682
+ <label for="temperature-slider">🌡️ میزان خلاقیت صدا (0.1 تا 1.5)</label>
683
+ <div class="slider-container">
684
+ <input type="range" id="temperature-slider" min="0.1" max="1.5" step="0.05" value="0.9">
685
+ <span id="temperature-value">0.9</span>
686
+ </div>
687
+ </div>
688
+
689
+ <button type="submit" id="generate-btn">🚀 تولید و پخش صدا</button>
690
+ </form>
691
+
692
+ <div id="output-section">
693
+ <div id="status-message">🎵 خروجی صدا در اینجا نمایش داده می‌شود</div>
694
  <audio id="audio-player" controls></audio>
695
  </div>
696
  </main>
697
  </div>
698
 
699
+ <!-- Speaker Selection Modal -->
700
+ <div id="speaker-modal">
701
+ <div class="modal-content">
702
+ <div class="modal-header">
703
+ <h2>🎭 انتخاب گوینده</h2>
704
+ <button type="button" class="close-modal-btn">×</button>
705
+ </div>
706
+ <div id="speaker-grid"></div>
707
+ </div>
708
+ </div>
709
+
710
+ <!-- Loading Animation Overlay -->
711
+ <div class="loading-overlay" id="loading-overlay">
712
+ <div class="loading-content">
713
+ <div class="ai-icon">🤖</div>
714
+ <div class="loading-title">در حال تبدیل متن به صدا</div>
715
+ <div class="loading-subtitle">با هوش مصنوعی آلفا</div>
716
+
717
+ <div class="sound-wave">
718
+ <div class="wave-bar"></div>
719
+ <div class="wave-bar"></div>
720
+ <div class="wave-bar"></div>
721
+ <div class="wave-bar"></div>
722
+ <div class="wave-bar"></div>
723
+ <div class="wave-bar"></div>
724
+ <div class="wave-bar"></div>
725
+ <div class="wave-bar"></div>
726
+ </div>
727
+
728
+ <div class="floating-particles">
729
+ <div class="particle"></div>
730
+ <div class="particle"></div>
731
+ <div class="particle"></div>
732
+ <div class="particle"></div>
733
+ <div class="particle"></div>
734
+ <div class="particle"></div>
735
+ <div class="particle"></div>
736
+ <div class="particle"></div>
737
+ <div class="particle"></div>
738
+ </div>
739
+ </div>
740
+ </div>
741
+
742
  <input type="hidden" id="selected_speaker_id_storage" value="Charon">
743
+
 
 
744
  <script>
 
 
745
  document.addEventListener('DOMContentLoaded', () => {
746
  const HF_SPACE_URL = "https://hamed744-ttspro.hf.space";
747
  const JOIN_QUEUE_URL = `${HF_SPACE_URL}/gradio_api/queue/join`;
748
  const GET_DATA_URL_BASE = `${HF_SPACE_URL}/gradio_api/queue/data`;
749
+ const FILE_URL_BASE =