Hamed744 commited on
Commit
6070214
·
verified ·
1 Parent(s): f5cf77c

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +94 -104
index.html CHANGED
@@ -71,7 +71,7 @@
71
  animation: fadeInDown 0.8s 0.1s ease-out backwards;
72
  }
73
  .app-header h1 {
74
- font-size: 2.8em; /* Reduced size */
75
  font-weight: 900;
76
  margin:0 0 0.75rem 0;
77
  background: linear-gradient(45deg, var(--accent-primary), var(--accent-secondary));
@@ -80,15 +80,15 @@
80
  letter-spacing: -1px;
81
  }
82
  .app-header p {
83
- font-size: 1.2em; /* Slightly reduced */
84
  color: var(--text-secondary);
85
  margin-top:0;
86
- opacity: 0.9; /* Slightly less prominent */
87
  font-weight: 400;
88
  }
89
 
90
  .main-content {
91
- padding: 2.5rem; /* Adjusted padding */
92
  background-color: var(--panel-bg);
93
  border-radius: var(--radius-card);
94
  box-shadow: var(--shadow-strong);
@@ -96,7 +96,7 @@
96
  animation: fadeInUp 0.8s 0.3s ease-out backwards;
97
  }
98
 
99
- .form-group { margin-bottom: 2rem; } /* Adjusted margin */
100
 
101
  .label-with-info {
102
  display: flex;
@@ -144,7 +144,7 @@
144
  z-index: 10;
145
  bottom: 140%;
146
  left: 50%;
147
- transform: translateX(-50%) translateY(10px); /* Start slightly lower */
148
  opacity: 0;
149
  transition: opacity 0.3s, visibility 0.3s, transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
150
  font-size: 0.88em;
@@ -165,7 +165,7 @@
165
  .info-tooltip-icon.active .tooltip-text {
166
  visibility: visible;
167
  opacity: 1;
168
- transform: translateX(-50%) translateY(0); /* Animate to final position */
169
  }
170
 
171
 
@@ -173,35 +173,35 @@
173
  display: block;
174
  font-weight: 700;
175
  color: var(--text-primary);
176
- font-size: 1.05em; /* Adjusted size */
177
- margin-bottom: 0.8rem; /* Adjusted margin */
178
  }
179
 
180
  textarea, input[type="text"] {
181
  width: 100%;
182
- padding: 0.9rem 1.1rem; /* Adjusted padding */
183
  border-radius: var(--radius-input);
184
  border: 1px solid var(--panel-border);
185
  background-color: var(--input-bg);
186
  color: var(--text-primary);
187
  box-shadow: var(--shadow-subtle) inset;
188
  font-family: var(--app-font);
189
- font-size: 1rem; /* Adjusted size */
190
  box-sizing: border-box;
191
  transition: var(--transition-smooth);
192
  }
193
  textarea:focus, input[type="text"]:focus {
194
  outline: none;
195
  border-color: var(--input-border-focus);
196
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.25), var(--shadow-subtle) inset; /* Slightly adjusted focus shadow */
197
  background-color: #fff;
198
  }
199
- textarea { min-height: 100px; resize: vertical; } /* Adjusted min-height */
200
 
201
  .char-counter-wrapper {
202
  font-size: 0.85em;
203
  color: var(--text-secondary);
204
- text-align: left; /* For LTR numbers, but RTL page */
205
  margin-top: 0.5rem;
206
  padding: 0 0.2rem;
207
  }
@@ -218,27 +218,27 @@
218
  padding: 1rem 1.2rem;
219
  box-shadow: var(--shadow-medium);
220
  border: 1px solid var(--panel-border);
221
- transition: var(--transition-bounce); /* Bouncy transition */
222
  position: relative;
223
  margin-bottom: 1.2rem;
224
  cursor: pointer;
225
  }
226
  #selected-speaker-card:hover {
227
- transform: translateY(-6px) scale(1.03); /* More pronounced hover */
228
  box-shadow: var(--shadow-strong);
229
  }
230
  #selected-speaker-card img {
231
- width: 75px; height: 75px; /* Slightly smaller */
232
  border-radius: 50%;
233
  object-fit: cover;
234
- margin-left: 18px; /* Adjusted margin */
235
- border: 3px solid var(--accent-secondary); /* Thinner border */
236
- box-shadow: 0 0 12px -2px rgba(16, 185, 129, 0.5); /* Adjusted shadow */
237
  background-color: #e0e0e0;
238
  transition: var(--transition-smooth);
239
  }
240
  #selected-speaker-card:hover img {
241
- transform: scale(1.08) rotate(3deg); /* More dynamic image hover */
242
  }
243
  #selected-speaker-info h3 { margin: 0; font-size: 1.35em; font-weight: 800; color: var(--text-primary); }
244
  #selected-speaker-info p { margin: 4px 0 0; color: var(--text-secondary); font-size: 0.88em; font-weight: 500; }
@@ -248,26 +248,26 @@
248
  align-items: center;
249
  justify-content: center;
250
  margin: 0 auto;
251
- padding: 10px 20px; /* Adjusted padding */
252
  border-radius: var(--radius-input);
253
  background: linear-gradient(45deg, var(--accent-primary-hover), var(--accent-primary));
254
  border: none;
255
  color: #fff;
256
  cursor: pointer;
257
- font-family: var(--app-font); font-weight: 600; /* Adjusted weight */
258
- font-size: 1em; /* Adjusted size */
259
  transition: var(--transition-smooth);
260
- box-shadow: 0 4px 10px -2px rgba(59, 130, 246, 0.35), var(--shadow-subtle); /* Adjusted shadow */
261
  }
262
  #change-speaker-btn:hover {
263
  background: linear-gradient(45deg, var(--accent-primary), var(--accent-primary-hover));
264
- transform: translateY(-3px) scale(1.05); /* More lift */
265
- box-shadow: 0 6px 12px -3px rgba(59, 130, 246, 0.45), var(--shadow-medium); /* Stronger shadow */
266
  }
267
  #change-speaker-btn svg {
268
- width: 1.1em; /* Adjusted size */
269
  height: 1.1em;
270
- margin-right: 0.5em; /* Adjusted margin */
271
  fill: currentColor;
272
  }
273
 
@@ -275,20 +275,20 @@
275
  /* --- مودال گالری گویندگان --- */
276
  #speaker-modal {
277
  position: fixed; top: 0; left: 0; width: 100%; height: 100%;
278
- background-color: rgba(18, 24, 38, 0.7); /* Darker backdrop */
279
- backdrop-filter: blur(8px) saturate(150%); /* Adjusted blur */
280
  display: none; align-items: center; justify-content: center;
281
  z-index: 1000; opacity: 0;
282
- transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* Faster fade */
283
  }
284
  #speaker-modal.visible { display: flex; opacity: 1; }
285
  .modal-content {
286
  background: var(--panel-bg);
287
- padding: 2rem; /* Adjusted padding */
288
  border-radius: var(--radius-card);
289
- width: 90%; max-width: 700px; /* Slightly smaller max-width */
290
- max-height: 85vh; overflow-y: auto; /* Adjusted max-height */
291
- transform: scale(0.95) translateY(20px); /* Start slightly smaller and lower */
292
  transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.35s;
293
  border: 1px solid var(--panel-border);
294
  box-shadow: var(--shadow-strong);
@@ -296,11 +296,11 @@
296
  }
297
  #speaker-modal.visible .modal-content { transform: scale(1) translateY(0); opacity: 1;}
298
  .modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.8rem; padding-bottom: 1rem; border-bottom: 1px solid var(--panel-border); }
299
- .modal-header h2 { margin: 0; font-size: 1.6em; font-weight: 800; color: var(--accent-primary);} /* Adjusted size */
300
  .close-modal-btn { background: none; border: none; font-size: 2.5rem; cursor: pointer; color: var(--text-secondary); transition: var(--transition-smooth); line-height: 1; }
301
- .close-modal-btn:hover { color: var(--accent-primary); transform: rotate(90deg) scale(1.1); } /* Changed rotation */
302
 
303
- #speaker-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 1.2rem; } /* Adjusted gap and minmax */
304
  @media (min-width: 640px) { #speaker-grid { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); } }
305
  .speaker-card { cursor: pointer; transition: var(--transition-smooth); text-align: center; position: relative;}
306
  .speaker-card .speaker-visual {
@@ -310,33 +310,33 @@
310
  box-shadow: var(--shadow-subtle);
311
  position: relative;
312
  background-color: var(--input-bg);
313
- transition: var(--transition-bounce); /* Bouncy transition */
314
- padding: 6px; /* Adjusted padding */
315
  }
316
  .speaker-card:hover .speaker-visual {
317
- transform: translateY(-5px) scale(1.06); /* More pop */
318
  box-shadow: var(--shadow-medium);
319
- border-color: rgba(var(--accent-secondary-rgb), 0.3); /* Subtle border on hover */
320
  }
321
  .speaker-card input[type="radio"] { display: none; }
322
  .speaker-card img {
323
- width: 100%; height: 120px; /* Adjusted height */
324
  object-fit: cover; display: block;
325
  background-color: #e0e0e0;
326
  transition: transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);
327
- border-radius: calc(var(--radius-card) - 12px); /* Adjusted inner radius */
328
  }
329
- .speaker-card:hover img { transform: scale(1.12); } /* Slightly more zoom */
330
  .speaker-card .speaker-name { padding: 0.8rem 0.4rem 0.1rem; font-weight: 600; font-size: 0.9em; color: var(--text-secondary); transition: color 0.2s; }
331
  .speaker-card input[type="radio"]:checked + .speaker-visual {
332
  border-color: var(--accent-secondary);
333
- box-shadow: 0 0 25px -5px rgba(16, 185, 129, 0.75); /* Stronger glow */
334
- background: linear-gradient(135deg, var(--accent-secondary-hover), var(--accent-primary)); /* New gradient */
335
- transform: scale(1.05); /* Pop out selected */
336
  }
337
  .speaker-card input[type="radio"]:checked + .speaker-visual img {
338
- border: 2px solid white; /* Thinner border */
339
- transform: scale(1.05); /* Slight zoom for selected image */
340
  }
341
  .speaker-card input[type="radio"]:checked + .speaker-visual .speaker-name {
342
  color: #fff;
@@ -344,10 +344,10 @@
344
  }
345
 
346
  /* --- Slider & Button & Output --- */
347
- .slider-container { display: flex; align-items: center; gap: 1.2rem; } /* Adjusted gap */
348
  input[type="range"] {
349
  flex-grow: 1; -webkit-appearance: none; appearance: none;
350
- width: 100%; height: 8px; /* Thinner track */
351
  background: #EAF0F6;
352
  border-radius: 4px; outline: none;
353
  cursor: pointer;
@@ -360,16 +360,16 @@
360
  }
361
  input[type="range"]::-webkit-slider-thumb {
362
  -webkit-appearance: none; appearance: none;
363
- width: 22px; height: 22px; /* Slightly smaller thumb */
364
  background: #fff;
365
  border-radius: 50%; cursor: pointer;
366
- border: 3px solid var(--accent-primary); /* Thinner border */
367
- box-shadow: 0 2px 6px rgba(0,0,0,0.15); /* Adjusted shadow */
368
  margin-top: -7px;
369
  transition: transform 0.2s ease, box-shadow 0.2s ease;
370
  }
371
  input[type="range"]::-webkit-slider-thumb:hover, input[type="range"]:focus::-webkit-slider-thumb {
372
- transform: scale(1.2); /* More pronounced hover/focus */
373
  box-shadow: 0 3px 8px rgba(59, 130, 246, 0.35);
374
  }
375
  input[type="range"]::-moz-range-thumb {
@@ -387,18 +387,18 @@
387
  }
388
  #temperature-value {
389
  font-weight: 700; background-color: var(--input-bg);
390
- padding: 0.5rem 1rem; /* Adjusted padding */
391
- border-radius: 8px; /* Adjusted radius */
392
  border: 1px solid var(--panel-border);
393
- min-width: 45px; text-align: center; /* Adjusted min-width */
394
  color: var(--accent-primary);
395
- font-size: 1em; /* Adjusted size */
396
  box-shadow: var(--shadow-subtle);
397
  }
398
 
399
  #generate-btn {
400
- width: 100%; padding: 1rem 1.5rem; /* Adjusted padding */
401
- font-size: 1.25em; font-weight: 800; /* Adjusted size */
402
  font-family: var(--app-font);
403
  background: linear-gradient(95deg, var(--accent-secondary) 0%, var(--accent-primary) 100%);
404
  color: #fff;
@@ -406,25 +406,25 @@
406
  border-radius: var(--radius-input);
407
  cursor: pointer;
408
  transition: var(--transition-smooth), transform 0.15s ease-out;
409
- box-shadow: 0 5px 15px -4px rgba(59, 130, 246, 0.45), 0 5px 15px -4px rgba(16, 185, 129, 0.35); /* Adjusted shadow */
410
  position: relative;
411
  overflow: hidden;
412
  letter-spacing: 0.5px;
413
  }
414
  #generate-btn::before {
415
  content: ''; position: absolute;
416
- top: 0; left: -180%; width: 80%; height: 100%; /* Adjusted for effect */
417
  background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.25) 50%, rgba(255,255,255,0) 100%);
418
- transform: skewX(-30deg); /* Steeper angle */
419
- transition: left 0.8s cubic-bezier(0.23, 1, 0.32, 1); /* Slower, smoother shine */
420
  }
421
  #generate-btn:hover::before { left: 180%; }
422
  #generate-btn:hover:not(:disabled) {
423
- transform: translateY(-5px) scale(1.02); /* More lift */
424
- box-shadow: 0 8px 20px -4px rgba(59, 130, 246, 0.55), 0 8px 20px -4px rgba(16, 185, 129, 0.45); /* Stronger shadow */
425
  }
426
  #generate-btn:active:not(:disabled) {
427
- transform: translateY(-2px) scale(0.98); /* More pronounced active state */
428
  }
429
  #generate-btn:disabled {
430
  background: #B8C2CC;
@@ -438,11 +438,11 @@
438
  }
439
 
440
  #output-section {
441
- margin-top: 3rem; /* Adjusted margin */
442
- padding: 2rem; /* Adjusted padding */
443
  background-color: var(--input-bg);
444
  border-radius: var(--radius-card);
445
- min-height: 200px; /* Adjusted min-height */
446
  display: flex; align-items: center; justify-content: center;
447
  flex-direction: column; gap: 1.5rem;
448
  border: 2px dashed var(--panel-border);
@@ -454,7 +454,7 @@
454
  background-color: #fff;
455
  border-style: solid;
456
  border-color: var(--accent-secondary);
457
- box-shadow: 0 0 30px -8px rgba(16, 185, 129, 0.25); /* Soft glow when content is present */
458
  padding: 1.5rem;
459
  }
460
  #output-section.has-content #status-message,
@@ -462,12 +462,11 @@
462
  display: none !important;
463
  }
464
 
465
- #status-message { font-weight: 500; color: var(--text-secondary); text-align: center; font-size: 1.1em; } /* Adjusted size */
466
  #audio-player { width: 100%; margin-top: 0; display: none; border-radius: 10px; box-shadow: var(--shadow-medium); }
467
  #output-section.has-content #audio-player {
468
  margin-top: 0;
469
  }
470
- /* Customize audio player (browser-specific, may not work everywhere) */
471
  #audio-player::-webkit-media-controls-panel { background-color: #fdfdff; border-radius: 10px; padding: 5px; box-shadow: var(--shadow-subtle) inset;}
472
  #audio-player::-webkit-media-controls-play-button { color: var(--accent-primary); background-color: rgba(59, 130, 246, 0.1); border-radius: 50%; margin: 5px;}
473
  #audio-player::-webkit-media-controls-current-time-display,
@@ -481,20 +480,20 @@
481
  flex-direction: column;
482
  align-items: center;
483
  justify-content: center;
484
- gap: 1.8rem; /* Adjusted gap */
485
  width: 100%;
486
  min-height: 150px;
487
  }
488
  .orbital-loader {
489
- width: 110px; /* Slightly smaller */
490
  height: 110px;
491
  position: relative;
492
- animation: rotate-loader-orbital 10s linear infinite; /* Faster rotation */
493
  }
494
  .orbit {
495
  position: absolute;
496
  top: 50%; left: 50%;
497
- border: 2px dashed rgba(59, 130, 246, 0.25); /* Lighter dash */
498
  border-radius: 50%;
499
  transform-origin: center center;
500
  }
@@ -504,14 +503,14 @@
504
 
505
  .orbit .satellite {
506
  position: absolute;
507
- width: 10px; height: 10px; /* Smaller satellites */
508
  border-radius: 50%;
509
  background-color: var(--accent-primary);
510
- box-shadow: 0 0 8px var(--accent-primary), 0 0 12px var(--accent-secondary); /* Adjusted shadow */
511
  }
512
  .orbit:nth-child(1) .satellite { top: -5px; left: 50%; animation: satellite-pulse-1 1.4s ease-in-out infinite alternate; }
513
- .orbit:nth-child(2) .satellite { top: 50%; left: -5px; background-color: var(--accent-secondary); animation: satellite-pulse-2 1.4s 0.2s ease-in-out infinite alternate; } /* Added delay */
514
- .orbit:nth-child(3) .satellite { bottom: -5px; right: 50%; animation: satellite-pulse-3 1.4s 0.4s ease-in-out infinite alternate;} /* Added delay */
515
 
516
  @keyframes rotate-loader-orbital { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
517
  @keyframes orbit-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@@ -528,7 +527,7 @@
528
  to { transform: scale(1.1) translateX(50%); opacity: 1; }
529
  }
530
  #loading-text {
531
- font-size: 1.2em; /* Adjusted size */
532
  font-weight: 700;
533
  color: var(--text-primary);
534
  text-align: center;
@@ -691,15 +690,14 @@
691
  // Character Counter
692
  const charCountSpan = document.getElementById('char-count');
693
  const charMaxSpan = document.getElementById('char-max');
694
- const MAX_CHARS = 1000; // You can change this
695
- // textInput.maxLength = MAX_CHARS; // Optional: Enforce limit in HTML
696
  charMaxSpan.textContent = MAX_CHARS;
697
 
698
  textInput.addEventListener('input', () => {
699
  const currentLength = textInput.value.length;
700
  charCountSpan.textContent = currentLength;
701
  if (currentLength > MAX_CHARS) {
702
- charCountSpan.style.color = 'var(--accent-secondary-hover)'; // Or some error color
703
  } else {
704
  charCountSpan.style.color = 'var(--accent-primary)';
705
  }
@@ -712,7 +710,6 @@
712
 
713
  function getImageUrl(speaker, index, size = 'thumb') {
714
  const gender = speaker.name.includes('(مرد)') ? 'men' : 'women';
715
- // A simple way to get somewhat varied images based on index and ID length
716
  const imageIndex = (index * 7 + speaker.id.length * 3 + 5) % 100;
717
  let portraitSizePath = 'thumb/';
718
  if (size === 'large') portraitSizePath = '';
@@ -747,13 +744,11 @@
747
  `;
748
 
749
  cardLabel.addEventListener('click', (e) => {
750
- // Ensure radio gets checked even if label's child is clicked
751
  if (e.target.name !== "modal_speaker_selection") {
752
  const radio = cardLabel.querySelector('input[type="radio"]');
753
  if(radio) radio.checked = true;
754
  }
755
  updateSelectedSpeakerDisplay(speaker.id);
756
- // Add a slight delay for visual feedback before closing
757
  setTimeout(() => speakerModal.classList.remove('visible'), 200);
758
  });
759
 
@@ -764,12 +759,11 @@
764
  function openSpeakerModal() {
765
  createSpeakerCardsInModal();
766
  speakerModal.classList.add('visible');
767
- // Focus management for accessibility
768
  setTimeout(() => {
769
  const firstFocusable = speakerModal.querySelector('input[type="radio"]:checked, input[type="radio"], .close-modal-btn');
770
  if (firstFocusable) firstFocusable.focus();
771
- else closeModalBtn.focus(); // Fallback
772
- }, 50); // Allow modal transition to start
773
  }
774
 
775
  changeSpeakerBtn.addEventListener('click', openSpeakerModal);
@@ -777,7 +771,7 @@
777
 
778
  closeModalBtn.addEventListener('click', () => speakerModal.classList.remove('visible'));
779
  speakerModal.addEventListener('click', (e) => {
780
- if (e.target === speakerModal) { // Click on backdrop
781
  speakerModal.classList.remove('visible');
782
  }
783
  });
@@ -789,7 +783,6 @@
789
 
790
  tempSlider.addEventListener('input', () => { tempValueSpan.textContent = tempSlider.value; });
791
 
792
- // Tooltip functionality
793
  tempInfoIcon.addEventListener('click', (e) => {
794
  e.stopPropagation();
795
  tempInfoIcon.classList.toggle('active');
@@ -836,7 +829,7 @@
836
  outputSection.classList.remove('has-content');
837
  }
838
  generateBtn.disabled = false;
839
- generateBtn.innerHTML = '✨ تولید صدا با آلفا'; // Restored original text
840
  }
841
 
842
  async function generateAudio(event) {
@@ -880,7 +873,7 @@
880
 
881
  let finalFilePath = null;
882
  const startTime = Date.now();
883
- const timeoutDuration = 90000; // 90 seconds timeout
884
 
885
  while (Date.now() - startTime < timeoutDuration) {
886
  const dataResponse = await fetch(`${GET_DATA_URL_BASE}?session_hash=${sessionHash}`);
@@ -906,7 +899,7 @@
906
  console.error("Invalid server response structure or unsuccessful processing:", data);
907
  throw new Error(data.output?.error || 'خطای ناشناخته در پردازش سرور.');
908
  }
909
- break; // Exit inner loop once completed
910
  } else if (data.msg === 'queue_full') {
911
  throw new Error('صف پردازش پر است. لطفا کمی بعد تلاش کنید.');
912
  }
@@ -914,8 +907,8 @@
914
  console.warn("Error parsing JSON from stream:", e, "Line:", line);
915
  }
916
  }
917
- if (finalFilePath) break; // Exit outer loop if file path is found
918
- await new Promise(resolve => setTimeout(resolve, 1000)); // Poll every second
919
  }
920
 
921
  if (finalFilePath) {
@@ -934,17 +927,14 @@
934
  }
935
  }
936
 
937
- // Initial setup
938
  updateSelectedSpeakerDisplay(selectedSpeakerIdStorage.value || speakers[0].id);
939
  form.addEventListener('submit', generateAudio);
940
 
941
- // Initial state for output section
942
  statusMessage.style.display = 'block';
943
  loadingAnimationWrapper.style.display = 'none';
944
  audioPlayer.style.display = 'none';
945
  outputSection.classList.remove('has-content');
946
 
947
- // Add a keyframe for the spinner SVG in JS to keep it local
948
  const styleSheet = document.createElement("style")
949
  styleSheet.type = "text/css"
950
  styleSheet.innerText = `@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }`;
 
71
  animation: fadeInDown 0.8s 0.1s ease-out backwards;
72
  }
73
  .app-header h1 {
74
+ font-size: 2.4em; /* کوچکتر شد */
75
  font-weight: 900;
76
  margin:0 0 0.75rem 0;
77
  background: linear-gradient(45deg, var(--accent-primary), var(--accent-secondary));
 
80
  letter-spacing: -1px;
81
  }
82
  .app-header p {
83
+ font-size: 1.05em; /* کوچکتر شد */
84
  color: var(--text-secondary);
85
  margin-top:0;
86
+ opacity: 0.9;
87
  font-weight: 400;
88
  }
89
 
90
  .main-content {
91
+ padding: 2.5rem;
92
  background-color: var(--panel-bg);
93
  border-radius: var(--radius-card);
94
  box-shadow: var(--shadow-strong);
 
96
  animation: fadeInUp 0.8s 0.3s ease-out backwards;
97
  }
98
 
99
+ .form-group { margin-bottom: 2rem; }
100
 
101
  .label-with-info {
102
  display: flex;
 
144
  z-index: 10;
145
  bottom: 140%;
146
  left: 50%;
147
+ transform: translateX(-50%) translateY(10px);
148
  opacity: 0;
149
  transition: opacity 0.3s, visibility 0.3s, transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
150
  font-size: 0.88em;
 
165
  .info-tooltip-icon.active .tooltip-text {
166
  visibility: visible;
167
  opacity: 1;
168
+ transform: translateX(-50%) translateY(0);
169
  }
170
 
171
 
 
173
  display: block;
174
  font-weight: 700;
175
  color: var(--text-primary);
176
+ font-size: 1.05em;
177
+ margin-bottom: 0.8rem;
178
  }
179
 
180
  textarea, input[type="text"] {
181
  width: 100%;
182
+ padding: 0.9rem 1.1rem;
183
  border-radius: var(--radius-input);
184
  border: 1px solid var(--panel-border);
185
  background-color: var(--input-bg);
186
  color: var(--text-primary);
187
  box-shadow: var(--shadow-subtle) inset;
188
  font-family: var(--app-font);
189
+ font-size: 1rem;
190
  box-sizing: border-box;
191
  transition: var(--transition-smooth);
192
  }
193
  textarea:focus, input[type="text"]:focus {
194
  outline: none;
195
  border-color: var(--input-border-focus);
196
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.25), var(--shadow-subtle) inset;
197
  background-color: #fff;
198
  }
199
+ textarea { min-height: 100px; resize: vertical; }
200
 
201
  .char-counter-wrapper {
202
  font-size: 0.85em;
203
  color: var(--text-secondary);
204
+ text-align: left;
205
  margin-top: 0.5rem;
206
  padding: 0 0.2rem;
207
  }
 
218
  padding: 1rem 1.2rem;
219
  box-shadow: var(--shadow-medium);
220
  border: 1px solid var(--panel-border);
221
+ transition: var(--transition-bounce);
222
  position: relative;
223
  margin-bottom: 1.2rem;
224
  cursor: pointer;
225
  }
226
  #selected-speaker-card:hover {
227
+ transform: translateY(-6px) scale(1.03);
228
  box-shadow: var(--shadow-strong);
229
  }
230
  #selected-speaker-card img {
231
+ width: 75px; height: 75px;
232
  border-radius: 50%;
233
  object-fit: cover;
234
+ margin-left: 18px;
235
+ border: 3px solid var(--accent-secondary);
236
+ box-shadow: 0 0 12px -2px rgba(16, 185, 129, 0.5);
237
  background-color: #e0e0e0;
238
  transition: var(--transition-smooth);
239
  }
240
  #selected-speaker-card:hover img {
241
+ transform: scale(1.08) rotate(3deg);
242
  }
243
  #selected-speaker-info h3 { margin: 0; font-size: 1.35em; font-weight: 800; color: var(--text-primary); }
244
  #selected-speaker-info p { margin: 4px 0 0; color: var(--text-secondary); font-size: 0.88em; font-weight: 500; }
 
248
  align-items: center;
249
  justify-content: center;
250
  margin: 0 auto;
251
+ padding: 10px 20px;
252
  border-radius: var(--radius-input);
253
  background: linear-gradient(45deg, var(--accent-primary-hover), var(--accent-primary));
254
  border: none;
255
  color: #fff;
256
  cursor: pointer;
257
+ font-family: var(--app-font); font-weight: 600;
258
+ font-size: 1em;
259
  transition: var(--transition-smooth);
260
+ box-shadow: 0 4px 10px -2px rgba(59, 130, 246, 0.35), var(--shadow-subtle);
261
  }
262
  #change-speaker-btn:hover {
263
  background: linear-gradient(45deg, var(--accent-primary), var(--accent-primary-hover));
264
+ transform: translateY(-3px) scale(1.05);
265
+ box-shadow: 0 6px 12px -3px rgba(59, 130, 246, 0.45), var(--shadow-medium);
266
  }
267
  #change-speaker-btn svg {
268
+ width: 1.1em;
269
  height: 1.1em;
270
+ margin-right: 0.5em;
271
  fill: currentColor;
272
  }
273
 
 
275
  /* --- مودال گالری گویندگان --- */
276
  #speaker-modal {
277
  position: fixed; top: 0; left: 0; width: 100%; height: 100%;
278
+ background-color: rgba(18, 24, 38, 0.7);
279
+ backdrop-filter: blur(8px) saturate(150%);
280
  display: none; align-items: center; justify-content: center;
281
  z-index: 1000; opacity: 0;
282
+ transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
283
  }
284
  #speaker-modal.visible { display: flex; opacity: 1; }
285
  .modal-content {
286
  background: var(--panel-bg);
287
+ padding: 2rem;
288
  border-radius: var(--radius-card);
289
+ width: 90%; max-width: 700px;
290
+ max-height: 85vh; overflow-y: auto;
291
+ transform: scale(0.95) translateY(20px);
292
  transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.35s;
293
  border: 1px solid var(--panel-border);
294
  box-shadow: var(--shadow-strong);
 
296
  }
297
  #speaker-modal.visible .modal-content { transform: scale(1) translateY(0); opacity: 1;}
298
  .modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.8rem; padding-bottom: 1rem; border-bottom: 1px solid var(--panel-border); }
299
+ .modal-header h2 { margin: 0; font-size: 1.6em; font-weight: 800; color: var(--accent-primary);}
300
  .close-modal-btn { background: none; border: none; font-size: 2.5rem; cursor: pointer; color: var(--text-secondary); transition: var(--transition-smooth); line-height: 1; }
301
+ .close-modal-btn:hover { color: var(--accent-primary); transform: rotate(90deg) scale(1.1); }
302
 
303
+ #speaker-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 1.2rem; }
304
  @media (min-width: 640px) { #speaker-grid { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); } }
305
  .speaker-card { cursor: pointer; transition: var(--transition-smooth); text-align: center; position: relative;}
306
  .speaker-card .speaker-visual {
 
310
  box-shadow: var(--shadow-subtle);
311
  position: relative;
312
  background-color: var(--input-bg);
313
+ transition: var(--transition-bounce);
314
+ padding: 6px;
315
  }
316
  .speaker-card:hover .speaker-visual {
317
+ transform: translateY(-5px) scale(1.06);
318
  box-shadow: var(--shadow-medium);
319
+ border-color: rgba(var(--accent-secondary-rgb), 0.3);
320
  }
321
  .speaker-card input[type="radio"] { display: none; }
322
  .speaker-card img {
323
+ width: 100%; height: 120px;
324
  object-fit: cover; display: block;
325
  background-color: #e0e0e0;
326
  transition: transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);
327
+ border-radius: calc(var(--radius-card) - 12px);
328
  }
329
+ .speaker-card:hover img { transform: scale(1.12); }
330
  .speaker-card .speaker-name { padding: 0.8rem 0.4rem 0.1rem; font-weight: 600; font-size: 0.9em; color: var(--text-secondary); transition: color 0.2s; }
331
  .speaker-card input[type="radio"]:checked + .speaker-visual {
332
  border-color: var(--accent-secondary);
333
+ box-shadow: 0 0 25px -5px rgba(16, 185, 129, 0.75);
334
+ background: linear-gradient(135deg, var(--accent-secondary-hover), var(--accent-primary));
335
+ transform: scale(1.05);
336
  }
337
  .speaker-card input[type="radio"]:checked + .speaker-visual img {
338
+ border: 2px solid white;
339
+ transform: scale(1.05);
340
  }
341
  .speaker-card input[type="radio"]:checked + .speaker-visual .speaker-name {
342
  color: #fff;
 
344
  }
345
 
346
  /* --- Slider & Button & Output --- */
347
+ .slider-container { display: flex; align-items: center; gap: 1.2rem; }
348
  input[type="range"] {
349
  flex-grow: 1; -webkit-appearance: none; appearance: none;
350
+ width: 100%; height: 8px;
351
  background: #EAF0F6;
352
  border-radius: 4px; outline: none;
353
  cursor: pointer;
 
360
  }
361
  input[type="range"]::-webkit-slider-thumb {
362
  -webkit-appearance: none; appearance: none;
363
+ width: 22px; height: 22px;
364
  background: #fff;
365
  border-radius: 50%; cursor: pointer;
366
+ border: 3px solid var(--accent-primary);
367
+ box-shadow: 0 2px 6px rgba(0,0,0,0.15);
368
  margin-top: -7px;
369
  transition: transform 0.2s ease, box-shadow 0.2s ease;
370
  }
371
  input[type="range"]::-webkit-slider-thumb:hover, input[type="range"]:focus::-webkit-slider-thumb {
372
+ transform: scale(1.2);
373
  box-shadow: 0 3px 8px rgba(59, 130, 246, 0.35);
374
  }
375
  input[type="range"]::-moz-range-thumb {
 
387
  }
388
  #temperature-value {
389
  font-weight: 700; background-color: var(--input-bg);
390
+ padding: 0.5rem 1rem;
391
+ border-radius: 8px;
392
  border: 1px solid var(--panel-border);
393
+ min-width: 45px; text-align: center;
394
  color: var(--accent-primary);
395
+ font-size: 1em;
396
  box-shadow: var(--shadow-subtle);
397
  }
398
 
399
  #generate-btn {
400
+ width: 100%; padding: 1rem 1.5rem;
401
+ font-size: 1.25em; font-weight: 800;
402
  font-family: var(--app-font);
403
  background: linear-gradient(95deg, var(--accent-secondary) 0%, var(--accent-primary) 100%);
404
  color: #fff;
 
406
  border-radius: var(--radius-input);
407
  cursor: pointer;
408
  transition: var(--transition-smooth), transform 0.15s ease-out;
409
+ box-shadow: 0 5px 15px -4px rgba(59, 130, 246, 0.45), 0 5px 15px -4px rgba(16, 185, 129, 0.35);
410
  position: relative;
411
  overflow: hidden;
412
  letter-spacing: 0.5px;
413
  }
414
  #generate-btn::before {
415
  content: ''; position: absolute;
416
+ top: 0; left: -180%; width: 80%; height: 100%;
417
  background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.25) 50%, rgba(255,255,255,0) 100%);
418
+ transform: skewX(-30deg);
419
+ transition: left 0.8s cubic-bezier(0.23, 1, 0.32, 1);
420
  }
421
  #generate-btn:hover::before { left: 180%; }
422
  #generate-btn:hover:not(:disabled) {
423
+ transform: translateY(-5px) scale(1.02);
424
+ box-shadow: 0 8px 20px -4px rgba(59, 130, 246, 0.55), 0 8px 20px -4px rgba(16, 185, 129, 0.45);
425
  }
426
  #generate-btn:active:not(:disabled) {
427
+ transform: translateY(-2px) scale(0.98);
428
  }
429
  #generate-btn:disabled {
430
  background: #B8C2CC;
 
438
  }
439
 
440
  #output-section {
441
+ margin-top: 3rem;
442
+ padding: 2rem;
443
  background-color: var(--input-bg);
444
  border-radius: var(--radius-card);
445
+ min-height: 200px;
446
  display: flex; align-items: center; justify-content: center;
447
  flex-direction: column; gap: 1.5rem;
448
  border: 2px dashed var(--panel-border);
 
454
  background-color: #fff;
455
  border-style: solid;
456
  border-color: var(--accent-secondary);
457
+ box-shadow: 0 0 30px -8px rgba(16, 185, 129, 0.25);
458
  padding: 1.5rem;
459
  }
460
  #output-section.has-content #status-message,
 
462
  display: none !important;
463
  }
464
 
465
+ #status-message { font-weight: 500; color: var(--text-secondary); text-align: center; font-size: 1.1em; }
466
  #audio-player { width: 100%; margin-top: 0; display: none; border-radius: 10px; box-shadow: var(--shadow-medium); }
467
  #output-section.has-content #audio-player {
468
  margin-top: 0;
469
  }
 
470
  #audio-player::-webkit-media-controls-panel { background-color: #fdfdff; border-radius: 10px; padding: 5px; box-shadow: var(--shadow-subtle) inset;}
471
  #audio-player::-webkit-media-controls-play-button { color: var(--accent-primary); background-color: rgba(59, 130, 246, 0.1); border-radius: 50%; margin: 5px;}
472
  #audio-player::-webkit-media-controls-current-time-display,
 
480
  flex-direction: column;
481
  align-items: center;
482
  justify-content: center;
483
+ gap: 1.8rem;
484
  width: 100%;
485
  min-height: 150px;
486
  }
487
  .orbital-loader {
488
+ width: 110px;
489
  height: 110px;
490
  position: relative;
491
+ animation: rotate-loader-orbital 10s linear infinite;
492
  }
493
  .orbit {
494
  position: absolute;
495
  top: 50%; left: 50%;
496
+ border: 2px dashed rgba(59, 130, 246, 0.25);
497
  border-radius: 50%;
498
  transform-origin: center center;
499
  }
 
503
 
504
  .orbit .satellite {
505
  position: absolute;
506
+ width: 10px; height: 10px;
507
  border-radius: 50%;
508
  background-color: var(--accent-primary);
509
+ box-shadow: 0 0 8px var(--accent-primary), 0 0 12px var(--accent-secondary);
510
  }
511
  .orbit:nth-child(1) .satellite { top: -5px; left: 50%; animation: satellite-pulse-1 1.4s ease-in-out infinite alternate; }
512
+ .orbit:nth-child(2) .satellite { top: 50%; left: -5px; background-color: var(--accent-secondary); animation: satellite-pulse-2 1.4s 0.2s ease-in-out infinite alternate; }
513
+ .orbit:nth-child(3) .satellite { bottom: -5px; right: 50%; animation: satellite-pulse-3 1.4s 0.4s ease-in-out infinite alternate;}
514
 
515
  @keyframes rotate-loader-orbital { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
516
  @keyframes orbit-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
 
527
  to { transform: scale(1.1) translateX(50%); opacity: 1; }
528
  }
529
  #loading-text {
530
+ font-size: 1.2em;
531
  font-weight: 700;
532
  color: var(--text-primary);
533
  text-align: center;
 
690
  // Character Counter
691
  const charCountSpan = document.getElementById('char-count');
692
  const charMaxSpan = document.getElementById('char-max');
693
+ const MAX_CHARS = 1000;
 
694
  charMaxSpan.textContent = MAX_CHARS;
695
 
696
  textInput.addEventListener('input', () => {
697
  const currentLength = textInput.value.length;
698
  charCountSpan.textContent = currentLength;
699
  if (currentLength > MAX_CHARS) {
700
+ charCountSpan.style.color = 'var(--accent-secondary-hover)';
701
  } else {
702
  charCountSpan.style.color = 'var(--accent-primary)';
703
  }
 
710
 
711
  function getImageUrl(speaker, index, size = 'thumb') {
712
  const gender = speaker.name.includes('(مرد)') ? 'men' : 'women';
 
713
  const imageIndex = (index * 7 + speaker.id.length * 3 + 5) % 100;
714
  let portraitSizePath = 'thumb/';
715
  if (size === 'large') portraitSizePath = '';
 
744
  `;
745
 
746
  cardLabel.addEventListener('click', (e) => {
 
747
  if (e.target.name !== "modal_speaker_selection") {
748
  const radio = cardLabel.querySelector('input[type="radio"]');
749
  if(radio) radio.checked = true;
750
  }
751
  updateSelectedSpeakerDisplay(speaker.id);
 
752
  setTimeout(() => speakerModal.classList.remove('visible'), 200);
753
  });
754
 
 
759
  function openSpeakerModal() {
760
  createSpeakerCardsInModal();
761
  speakerModal.classList.add('visible');
 
762
  setTimeout(() => {
763
  const firstFocusable = speakerModal.querySelector('input[type="radio"]:checked, input[type="radio"], .close-modal-btn');
764
  if (firstFocusable) firstFocusable.focus();
765
+ else closeModalBtn.focus();
766
+ }, 50);
767
  }
768
 
769
  changeSpeakerBtn.addEventListener('click', openSpeakerModal);
 
771
 
772
  closeModalBtn.addEventListener('click', () => speakerModal.classList.remove('visible'));
773
  speakerModal.addEventListener('click', (e) => {
774
+ if (e.target === speakerModal) {
775
  speakerModal.classList.remove('visible');
776
  }
777
  });
 
783
 
784
  tempSlider.addEventListener('input', () => { tempValueSpan.textContent = tempSlider.value; });
785
 
 
786
  tempInfoIcon.addEventListener('click', (e) => {
787
  e.stopPropagation();
788
  tempInfoIcon.classList.toggle('active');
 
829
  outputSection.classList.remove('has-content');
830
  }
831
  generateBtn.disabled = false;
832
+ generateBtn.innerHTML = '✨ تولید صدا با آلفا';
833
  }
834
 
835
  async function generateAudio(event) {
 
873
 
874
  let finalFilePath = null;
875
  const startTime = Date.now();
876
+ const timeoutDuration = 90000;
877
 
878
  while (Date.now() - startTime < timeoutDuration) {
879
  const dataResponse = await fetch(`${GET_DATA_URL_BASE}?session_hash=${sessionHash}`);
 
899
  console.error("Invalid server response structure or unsuccessful processing:", data);
900
  throw new Error(data.output?.error || 'خطای ناشناخته در پردازش سرور.');
901
  }
902
+ break;
903
  } else if (data.msg === 'queue_full') {
904
  throw new Error('صف پردازش پر است. لطفا کمی بعد تلاش کنید.');
905
  }
 
907
  console.warn("Error parsing JSON from stream:", e, "Line:", line);
908
  }
909
  }
910
+ if (finalFilePath) break;
911
+ await new Promise(resolve => setTimeout(resolve, 1000));
912
  }
913
 
914
  if (finalFilePath) {
 
927
  }
928
  }
929
 
 
930
  updateSelectedSpeakerDisplay(selectedSpeakerIdStorage.value || speakers[0].id);
931
  form.addEventListener('submit', generateAudio);
932
 
 
933
  statusMessage.style.display = 'block';
934
  loadingAnimationWrapper.style.display = 'none';
935
  audioPlayer.style.display = 'none';
936
  outputSection.classList.remove('has-content');
937
 
 
938
  const styleSheet = document.createElement("style")
939
  styleSheet.type = "text/css"
940
  styleSheet.innerText = `@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }`;