Hamed744 commited on
Commit
1b7416e
·
verified ·
1 Parent(s): 18b1fa7

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +142 -139
index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>آلفا TTS - نسخه نهایی</title>
7
  <style>
8
  @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;700;800&display=swap');
9
 
@@ -11,18 +11,19 @@
11
  --app-font: 'Vazirmatn', sans-serif;
12
  --app-header-grad-start: #1a2980;
13
  --app-header-grad-end: #26d0ce;
14
- --app-panel-bg: rgba(255, 255, 255, 0.65);
15
- --app-input-bg: rgba(255, 255, 255, 0.7);
16
  --app-button-bg: #5f27cd;
17
- --app-main-bg: linear-gradient(170deg, #e0c3fc 0%, #8ec5fc 100%);
18
- --app-text-primary: #1e1e1e;
19
- --app-text-secondary: #4a4a4a;
20
- --app-border-color: rgba(255, 255, 255, 0.4);
 
21
  --radius-card: 24px;
22
- --radius-input: 14px;
23
- --shadow-card: 0 10px 40px -10px rgba(0,0,0,0.15);
24
- --shadow-button: 0 5px 15px -3px rgba(95, 39, 205, 0.5);
25
- --speaker-selected-border: 4px solid var(--app-button-bg);
26
  }
27
 
28
  body {
@@ -31,46 +32,51 @@
31
  background: var(--app-main-bg);
32
  color: var(--app-text-primary);
33
  font-size: 16px;
34
- line-height: 1.7;
35
  margin: 0;
36
- padding: 2rem 1rem;
37
  min-height: 100vh;
38
- box-sizing: border-box;
39
  display: flex;
40
- align-items: center;
41
- justify-content: center;
 
42
  }
43
 
44
  .container {
45
  max-width: 950px;
46
- width: 100%;
47
- }
48
-
49
- .main-content {
50
- padding: 2.5rem;
51
- background: var(--app-panel-bg);
52
- border-radius: var(--radius-card);
53
- box-shadow: var(--shadow-card);
54
- backdrop-filter: blur(20px);
55
- border: 1px solid var(--app-border-color);
56
  }
57
 
58
- .app-title {
 
59
  text-align: center;
60
- margin-bottom: 2.5rem;
 
 
 
 
61
  }
62
- .app-title h1 {
63
  font-size: 2.8em;
64
  font-weight: 800;
65
- margin: 0;
66
- background: linear-gradient(45deg, var(--app-header-grad-start), var(--app-header-grad-end));
67
- -webkit-background-clip: text;
68
- -webkit-text-fill-color: transparent;
69
  }
70
- .app-title p {
71
  font-size: 1.2em;
72
- color: var(--app-text-secondary);
73
- margin-top: 0.5rem;
 
 
 
 
 
 
 
 
 
74
  }
75
 
76
  .form-group {
@@ -78,155 +84,144 @@
78
  }
79
 
80
  label {
81
- display: flex;
82
- align-items: center;
83
- gap: 0.7rem;
84
  font-weight: 700;
85
  color: var(--app-text-primary);
86
  font-size: 1.1em;
87
- margin-bottom: 1rem;
88
  }
89
-
90
  textarea, input[type="text"] {
91
  width: 100%;
92
  padding: 1rem;
93
  border-radius: var(--radius-input);
94
- border: 1px solid var(--app-border-color);
95
  background-color: var(--app-input-bg);
96
- box-shadow: inset 0 2px 4px rgba(0,0,0,0.04);
97
  font-family: var(--app-font);
98
  font-size: 1rem;
99
  box-sizing: border-box;
100
  transition: all 0.2s ease-in-out;
101
  }
 
102
  textarea:focus, input[type="text"]:focus {
103
  outline: none;
104
  border-color: var(--app-button-bg);
105
- box-shadow: 0 0 0 4px rgba(95, 39, 205, 0.2);
 
106
  }
107
 
108
- /* Speaker Selection Grid - NEW DESIGN */
 
 
 
 
 
 
109
  #speaker-grid {
110
  display: grid;
111
- grid-template-columns: repeat(4, 1fr); /* 4 columns */
112
  gap: 1.5rem;
113
  }
 
 
 
 
 
114
  .speaker-card {
115
  cursor: pointer;
116
- position: relative;
 
 
 
 
117
  overflow: hidden;
118
- border-radius: var(--radius-input);
119
- box-shadow: 0 5px 15px rgba(0,0,0,0.1);
120
- transition: transform 0.3s ease, box-shadow 0.3s ease;
 
121
  }
122
- .speaker-card:hover {
123
- transform: scale(1.05);
124
- box-shadow: 0 8px 25px rgba(0,0,0,0.15);
125
  }
126
  .speaker-card input[type="radio"] {
127
  display: none;
128
  }
129
- .speaker-card .speaker-visual {
130
- border-radius: var(--radius-input);
131
- border: 4px solid transparent;
132
- transition: border-color 0.3s ease;
133
- }
134
  .speaker-card img {
135
  width: 100%;
136
- height: 200px;
137
  object-fit: cover;
138
  display: block;
139
  }
140
- .speaker-card .speaker-name-overlay {
141
- position: absolute;
142
- bottom: 0;
143
- left: 0;
144
- right: 0;
145
- background: linear-gradient(to top, rgba(0,0,0,0.8), transparent);
146
- color: white;
147
- padding: 2rem 1rem 1rem 1rem;
148
- font-weight: 700;
149
- font-size: 1.1em;
150
- text-align: right;
151
- opacity: 0;
152
- transform: translateY(20px);
153
- transition: opacity 0.3s ease, transform 0.3s ease;
154
- }
155
- .speaker-card:hover .speaker-name-overlay {
156
- opacity: 1;
157
- transform: translateY(0);
158
- }
159
- .speaker-card .selection-tick {
160
- position: absolute;
161
- top: 10px;
162
- left: 10px;
163
- width: 30px;
164
- height: 30px;
165
- background-color: var(--app-button-bg);
166
- color: white;
167
- border-radius: 50%;
168
- display: flex;
169
- align-items: center;
170
- justify-content: center;
171
- transform: scale(0);
172
- transition: transform 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28);
173
  }
174
  .speaker-card input[type="radio"]:checked + .speaker-visual {
175
  border-color: var(--app-button-bg);
 
 
176
  }
177
- .speaker-card input[type="radio"]:checked + .speaker-visual .selection-tick {
178
- transform: scale(1);
 
 
179
  }
180
-
181
- /* Slider */
182
  .slider-container { display: flex; align-items: center; gap: 1rem; }
183
  input[type="range"] { flex-grow: 1; cursor: pointer; }
184
  #temperature-value { font-weight: bold; background-color: var(--app-input-bg); padding: 0.2rem 0.8rem; border-radius: 8px; border: 1px solid var(--app-border-color); min-width: 40px; text-align: center; }
185
 
 
186
  #generate-btn { width: 100%; padding: 1rem 1.5rem; font-size: 1.2em; font-weight: 700; font-family: var(--app-font); background: var(--app-button-bg); color: white; border: none; border-radius: var(--radius-input); cursor: pointer; transition: all 0.3s ease; box-shadow: var(--shadow-button); }
187
- #generate-btn:hover:not(:disabled) { filter: brightness(1.1); transform: translateY(-2px); box-shadow: 0 8px 20px -5px rgba(95, 39, 205, 0.6); }
188
  #generate-btn:disabled { background-color: #999; cursor: not-allowed; box-shadow: none; }
189
-
190
- #output-section { margin-top: 2.5rem; padding: 1.5rem; background-color: var(--app-input-bg); border-radius: var(--radius-card); min-height: 100px; display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 1rem; }
191
  #status-message { font-weight: 500; color: var(--app-text-secondary); }
192
  #audio-player { width: 100%; margin-top: 1rem; display: none; }
193
-
194
- @media (max-width: 768px) {
195
- #speaker-grid { grid-template-columns: repeat(2, 1fr); }
196
- .main-content { padding: 1.5rem; }
197
- body { padding: 1rem 0.5rem; }
198
- }
199
  </style>
200
  </head>
201
  <body>
202
  <div class="container">
 
 
 
 
 
203
  <main class="main-content">
204
- <div class="app-title">
205
- <h1>آلفا TTS</h1>
206
- <p>جادوی تبدیل متن به صدا</p>
207
- </div>
208
  <form id="tts-form">
209
  <div class="form-group">
210
  <label for="text-input">📝 متن برای تبدیل</label>
211
- <textarea id="text-input" rows="4" placeholder="اینجا متن خود را به فارسی وارد کنید...">این یک آزمایش برای بررسی کیفیت صدای تولید شده توسط هوش مصنوعی آلفا است.</textarea>
212
  </div>
213
  <div class="form-group">
214
  <label for="prompt-input">🗣️ سبک و لحن گفتار (اختیاری)</label>
215
  <input type="text" id="prompt-input" value="با صدایی طبیعی و روان." placeholder="مثال: با لحنی شاد و پرانرژی">
216
  </div>
 
217
  <div class="form-group">
218
  <label>🎤 گوینده را انتخاب کنید</label>
219
- <div id="speaker-grid"></div>
 
 
220
  </div>
 
221
  <div class="form-group">
222
- <label for="temperature-slider">🌡️ میزان خلاقیت صدا</label>
223
  <div class="slider-container">
224
  <input type="range" id="temperature-slider" min="0.1" max="1.5" step="0.05" value="0.9">
225
  <span id="temperature-value">0.9</span>
226
  </div>
227
  </div>
 
228
  <button type="submit" id="generate-btn">🚀 تولید و پخش صدا</button>
229
  </form>
 
230
  <div id="output-section">
231
  <div id="status-message">خروجی صدا در اینجا نمایش داده می‌شود</div>
232
  <audio id="audio-player" controls></audio>
@@ -241,19 +236,28 @@
241
  const GET_DATA_URL_BASE = `${HF_SPACE_URL}/gradio_api/queue/data`;
242
  const FILE_URL_BASE = `${HF_SPACE_URL}/gradio_api/file=`;
243
 
244
- const FN_INDEX = 1;
245
 
246
- const speakers = {
247
- "Charon": "شهاب", "Zephyr": "نسیم", "Iapetus": "کیان", "Enceladus": "آوا",
248
- "Leda": "لیدا", "Kore": "ترنم", "Puck": "پژمان", "Fenrir": "آرش",
249
- "Aoede": "چکاوک", "Callirrhoe": "باران", "Autonoe": "روشنک", "Orus": "داریوش",
250
- "Rasalthgeti": "هما", "Algenib": "البرز", "Erinome": "ارمغان", "Despina": "دریا",
251
- "Algieba": "آناهیتا", "Umbriel": "سهند", "Pulcherrima": "پریچه��", "Gacrux": "گرشا",
252
- "Schedar": "ستاره", "Alnilam": "آرتین", "Achernar": "آناهید", "Laomedeia": "ماندانا",
253
- "Sulafat": "سروش", "Sadaltager": "سپهر", "Sadachbia": "سحر", "Vindemiatrix": "پروین",
254
- "Zubenelgenubi": "آذر", "Achird": "آرشام"
255
- };
 
 
 
 
 
 
 
 
256
 
 
257
  const form = document.getElementById('tts-form');
258
  const textInput = document.getElementById('text-input');
259
  const promptInput = document.getElementById('prompt-input');
@@ -265,24 +269,23 @@
265
  const audioPlayer = document.getElementById('audio-player');
266
 
267
  function createSpeakerCards() {
268
- Object.entries(speakers).forEach(([englishName, persianName]) => {
269
  const card = document.createElement('label');
270
  card.className = 'speaker-card';
271
- card.setAttribute('for', `speaker-${englishName}`);
272
- // استفاده از Unsplash برای عکس‌های واقعی و با کیفیت
273
- const imageUrl = `https://source.unsplash.com/200x200/?portrait,person,${englishName}`;
274
- const isChecked = englishName === 'Charon' ? 'checked' : '';
275
-
 
 
 
 
276
  card.innerHTML = `
277
- <input type="radio" name="speaker" value="${englishName}" id="speaker-${englishName}" ${isChecked}>
278
  <div class="speaker-visual">
279
- <img src="${imageUrl}" alt="عکس گوینده ${persianName}">
280
- <div class="speaker-name-overlay">${persianName}</div>
281
- <div class="selection-tick">
282
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
283
- <path d="M12.736 3.97a.733.733 0 0 1 1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06.733.733 0 0 1 1.047 0l3.052 3.093 5.4-6.425z"/>
284
- </svg>
285
- </div>
286
  </div>
287
  `;
288
  speakerGrid.appendChild(card);
@@ -363,7 +366,7 @@
363
  }
364
  break;
365
  }
366
- } catch (e) { /* نادیده گرفتن خطاهای پارس کردن */ }
367
  }
368
  if (finalFilePath) break;
369
  }
@@ -375,7 +378,7 @@
375
  audioPlayer.style.display = 'block';
376
  audioPlayer.play();
377
  } else {
378
- throw new Error('فایل صوتی از سرور دریافت نشد.');
379
  }
380
 
381
  } catch (error) {
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>آلفا TTS - رابط کاربری مدرن</title>
7
  <style>
8
  @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;700;800&display=swap');
9
 
 
11
  --app-font: 'Vazirmatn', sans-serif;
12
  --app-header-grad-start: #1a2980;
13
  --app-header-grad-end: #26d0ce;
14
+ --app-panel-bg: #FFFFFF;
15
+ --app-input-bg: #F7F7F7;
16
  --app-button-bg: #5f27cd;
17
+ --app-button-hover-bg: #481e9e;
18
+ --app-main-bg: linear-gradient(170deg, #F3E8FF 0%, #E0F2FE 100%);
19
+ --app-text-primary: #2c3e50;
20
+ --app-text-secondary: #555;
21
+ --app-border-color: #E0E0E0;
22
  --radius-card: 24px;
23
+ --radius-input: 12px;
24
+ --shadow-card: 0 10px 30px -5px rgba(0,0,0,0.08);
25
+ --shadow-button: 0 4px 15px -2px rgba(95, 39, 205, 0.4);
26
+ --speaker-selected-glow: 0 0 15px rgba(95, 39, 205, 0.5);
27
  }
28
 
29
  body {
 
32
  background: var(--app-main-bg);
33
  color: var(--app-text-primary);
34
  font-size: 16px;
35
+ line-height: 1.65;
36
  margin: 0;
37
+ padding: 0;
38
  min-height: 100vh;
 
39
  display: flex;
40
+ flex-direction: column;
41
+ -webkit-font-smoothing: antialiased;
42
+ -moz-osx-font-smoothing: grayscale;
43
  }
44
 
45
  .container {
46
  max-width: 950px;
47
+ width: 95%;
48
+ margin: 0 auto;
49
+ padding-bottom: 40px;
 
 
 
 
 
 
 
50
  }
51
 
52
+ .app-header {
53
+ padding: 3rem 1.5rem 5rem 1.5rem;
54
  text-align: center;
55
+ background-image: linear-gradient(135deg, var(--app-header-grad-start) 0%, var(--app-header-grad-end) 100%);
56
+ color: white;
57
+ border-bottom-left-radius: var(--radius-card);
58
+ border-bottom-right-radius: var(--radius-card);
59
+ box-shadow: 0 6px 20px -5px rgba(0,0,0,0.2);
60
  }
61
+ .app-header h1 {
62
  font-size: 2.8em;
63
  font-weight: 800;
64
+ margin:0 0 0.5rem 0;
65
+ text-shadow: 0 2px 4px rgba(0,0,0,0.15);
 
 
66
  }
67
+ .app-header p {
68
  font-size: 1.2em;
69
+ color: rgba(255,255,255,0.9);
70
+ margin-top:0;
71
+ opacity: 0.9;
72
+ }
73
+
74
+ .main-content {
75
+ padding: 2.5rem;
76
+ margin: -3.5rem auto 2rem auto;
77
+ background-color: var(--app-panel-bg);
78
+ border-radius: var(--radius-card);
79
+ box-shadow: var(--shadow-card);
80
  }
81
 
82
  .form-group {
 
84
  }
85
 
86
  label {
87
+ display: block;
 
 
88
  font-weight: 700;
89
  color: var(--app-text-primary);
90
  font-size: 1.1em;
91
+ margin-bottom: 0.8rem;
92
  }
93
+
94
  textarea, input[type="text"] {
95
  width: 100%;
96
  padding: 1rem;
97
  border-radius: var(--radius-input);
98
+ border: 2px solid var(--app-border-color);
99
  background-color: var(--app-input-bg);
100
+ box-shadow: none;
101
  font-family: var(--app-font);
102
  font-size: 1rem;
103
  box-sizing: border-box;
104
  transition: all 0.2s ease-in-out;
105
  }
106
+
107
  textarea:focus, input[type="text"]:focus {
108
  outline: none;
109
  border-color: var(--app-button-bg);
110
+ box-shadow: 0 0 0 3px rgba(95, 39, 205, 0.2);
111
+ background-color: #fff;
112
  }
113
 
114
+ /* --- Speaker Selection Grid (طراحی جدید) --- */
115
+ .speaker-gallery {
116
+ background-color: var(--app-input-bg);
117
+ padding: 1.5rem;
118
+ border-radius: var(--radius-card);
119
+ border: 1px solid var(--app-border-color);
120
+ }
121
  #speaker-grid {
122
  display: grid;
123
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
124
  gap: 1.5rem;
125
  }
126
+ @media (min-width: 768px) {
127
+ #speaker-grid {
128
+ grid-template-columns: repeat(4, 1fr);
129
+ }
130
+ }
131
  .speaker-card {
132
  cursor: pointer;
133
+ transition: all 0.3s ease;
134
+ }
135
+ .speaker-card .speaker-visual {
136
+ border: 3px solid transparent;
137
+ border-radius: var(--radius-card);
138
  overflow: hidden;
139
+ text-align: center;
140
+ box-shadow: 0 4px 15px rgba(0,0,0,0.08);
141
+ position: relative;
142
+ background-color: #fff;
143
  }
144
+ .speaker-card:hover .speaker-visual {
145
+ transform: translateY(-5px);
146
+ box-shadow: 0 8px 25px rgba(0,0,0,0.12);
147
  }
148
  .speaker-card input[type="radio"] {
149
  display: none;
150
  }
 
 
 
 
 
151
  .speaker-card img {
152
  width: 100%;
153
+ height: 150px;
154
  object-fit: cover;
155
  display: block;
156
  }
157
+ .speaker-card .speaker-name {
158
+ padding: 0.8rem 0.5rem;
159
+ font-weight: 500;
160
+ background-color: rgba(255, 255, 255, 0.8);
161
+ backdrop-filter: blur(5px);
162
+ transition: background-color 0.3s ease;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  }
164
  .speaker-card input[type="radio"]:checked + .speaker-visual {
165
  border-color: var(--app-button-bg);
166
+ box-shadow: var(--speaker-selected-glow);
167
+ transform: translateY(-2px) scale(1.02);
168
  }
169
+ .speaker-card input[type="radio"]:checked + .speaker-visual .speaker-name {
170
+ background-color: var(--app-button-bg);
171
+ color: white;
172
+ font-weight: 700;
173
  }
174
+
175
+ /* --- Slider --- */
176
  .slider-container { display: flex; align-items: center; gap: 1rem; }
177
  input[type="range"] { flex-grow: 1; cursor: pointer; }
178
  #temperature-value { font-weight: bold; background-color: var(--app-input-bg); padding: 0.2rem 0.8rem; border-radius: 8px; border: 1px solid var(--app-border-color); min-width: 40px; text-align: center; }
179
 
180
+ /* --- Button & Output --- */
181
  #generate-btn { width: 100%; padding: 1rem 1.5rem; font-size: 1.2em; font-weight: 700; font-family: var(--app-font); background: var(--app-button-bg); color: white; border: none; border-radius: var(--radius-input); cursor: pointer; transition: all 0.3s ease; box-shadow: var(--shadow-button); }
182
+ #generate-btn:hover:not(:disabled) { background-color: var(--app-button-hover-bg); transform: translateY(-2px); box-shadow: 0 6px 20px -3px rgba(95, 39, 205, 0.5); }
183
  #generate-btn:disabled { background-color: #999; cursor: not-allowed; box-shadow: none; }
184
+ #output-section { margin-top: 2.5rem; padding: 1.5rem; background-color: #fff; border-radius: var(--radius-card); min-height: 100px; display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 1rem; border: 1px solid var(--app-border-color); }
 
185
  #status-message { font-weight: 500; color: var(--app-text-secondary); }
186
  #audio-player { width: 100%; margin-top: 1rem; display: none; }
 
 
 
 
 
 
187
  </style>
188
  </head>
189
  <body>
190
  <div class="container">
191
+ <header class="app-header">
192
+ <h1>آلفا TTS</h1>
193
+ <p>جادوی تبدیل متن به صدا، به سبک شما</p>
194
+ </header>
195
+
196
  <main class="main-content">
 
 
 
 
197
  <form id="tts-form">
198
  <div class="form-group">
199
  <label for="text-input">📝 متن برای تبدیل</label>
200
+ <textarea id="text-input" rows="5" placeholder="اینجا متن خود را به فارسی وارد کنید...">این یک آزمایش برای بررسی کیفیت صدای تولید شده توسط هوش مصنوعی آلفا است.</textarea>
201
  </div>
202
  <div class="form-group">
203
  <label for="prompt-input">🗣️ سبک و لحن گفتار (اختیاری)</label>
204
  <input type="text" id="prompt-input" value="با صدایی طبیعی و روان." placeholder="مثال: با لحنی شاد و پرانرژی">
205
  </div>
206
+
207
  <div class="form-group">
208
  <label>🎤 گوینده را انتخاب کنید</label>
209
+ <div class="speaker-gallery">
210
+ <div id="speaker-grid"></div>
211
+ </div>
212
  </div>
213
+
214
  <div class="form-group">
215
+ <label for="temperature-slider">🌡️ میزان خلاقیت صدا (0.1 تا 1.5)</label>
216
  <div class="slider-container">
217
  <input type="range" id="temperature-slider" min="0.1" max="1.5" step="0.05" value="0.9">
218
  <span id="temperature-value">0.9</span>
219
  </div>
220
  </div>
221
+
222
  <button type="submit" id="generate-btn">🚀 تولید و پخش صدا</button>
223
  </form>
224
+
225
  <div id="output-section">
226
  <div id="status-message">خروجی صدا در اینجا نمایش داده می‌شود</div>
227
  <audio id="audio-player" controls></audio>
 
236
  const GET_DATA_URL_BASE = `${HF_SPACE_URL}/gradio_api/queue/data`;
237
  const FILE_URL_BASE = `${HF_SPACE_URL}/gradio_api/file=`;
238
 
239
+ const FN_INDEX = 1;
240
 
241
+ // --- **لیست جدید گویندگان با اسامی فارسی** ---
242
+ const speakers = [
243
+ { id: "Charon", name: "شهاب (مرد)" }, { id: "Zephyr", name: "آوا (زن)" },
244
+ { id: "Achird", name: "نوید (مرد)" }, { id: "Zubenelgenubi", name: "رویا (زن)" },
245
+ { id: "Vindemiatrix", name: "کیان (مرد)" }, { id: "Sadachbia", name: "پریسا (زن)" },
246
+ { id: "Sadaltager", name: "آرش (مرد)" }, { id: "Sulafat", name: "شبنم (زن)" },
247
+ { id: "Laomedeia", name: "سهیل (مرد)" }, { id: "Achernar", name: "مریم (زن)" },
248
+ { id: "Alnilam", name: "بهرام (مرد)" }, { id: "Schedar", name: "نگار (زن)" },
249
+ { id: "Gacrux", name: "فرید (مرد)" }, { id: "Pulcherrima", name: "سارا (زن)" },
250
+ { id: "Umbriel", name: "مانی (مرد)" }, { id: "Algieba", name: "آناهیتا (زن)" },
251
+ { id: "Despina", name: "دلنواز (زن)" }, { id: "Erinome", name: "رسا (مرد)" },
252
+ { id: "Algenib", name: "امید (مرد)" }, { id: "Rasalthgeti", name: "الهه (زن)" },
253
+ { id: "Orus", name: "بردیا (مرد)" }, { id: "Aoede", name: "ترانه (زن)" },
254
+ { id: "Callirrhoe", name: "نیما (مرد)" }, { id: "Autonoe", name: "هستی (زن)" },
255
+ { id: "Enceladus", name: "کامیار (مرد)" }, { id: "Iapetus", name: "ستاره (زن)" },
256
+ { id: "Puck", name: "پویا (مرد)" }, { id: "Kore", name: "مهتاب (زن)" },
257
+ { id: "Fenrir", name: "سام (مرد)" }, { id: "Leda", name: "لیدا (زن)" }
258
+ ];
259
 
260
+ // --- عناصر DOM ---
261
  const form = document.getElementById('tts-form');
262
  const textInput = document.getElementById('text-input');
263
  const promptInput = document.getElementById('prompt-input');
 
269
  const audioPlayer = document.getElementById('audio-player');
270
 
271
  function createSpeakerCards() {
272
+ speakers.forEach((speaker) => {
273
  const card = document.createElement('label');
274
  card.className = 'speaker-card';
275
+ card.setAttribute('for', `speaker-${speaker.id}`);
276
+
277
+ // از یک سرویس عکس پرتره استفاده می‌کنیم
278
+ // اگر جنسیت مشخص است، می‌توانیم عکس‌های مرتبط‌تری انتخاب کنیم
279
+ const gender = speaker.name.includes('(مرد)') ? 'men' : 'women';
280
+ const imageUrl = `https://randomuser.me/api/portraits/${gender}/${speakers.indexOf(speaker) % 100}.jpg`;
281
+
282
+ const isChecked = speaker.id === 'Charon' ? 'checked' : '';
283
+
284
  card.innerHTML = `
285
+ <input type="radio" name="speaker" value="${speaker.id}" id="speaker-${speaker.id}" ${isChecked}>
286
  <div class="speaker-visual">
287
+ <img src="${imageUrl}" alt="عکس گوینده ${speaker.name}" loading="lazy">
288
+ <div class="speaker-name">${speaker.name}</div>
 
 
 
 
 
289
  </div>
290
  `;
291
  speakerGrid.appendChild(card);
 
366
  }
367
  break;
368
  }
369
+ } catch (e) { /* نادیده گرفتن خطاهای پارس */ }
370
  }
371
  if (finalFilePath) break;
372
  }
 
378
  audioPlayer.style.display = 'block';
379
  audioPlayer.play();
380
  } else {
381
+ throw new Error('فایل صوتی از سرور دریافت نشد. کنسول را برای اطلاعات بیشتر بررسی کنید.');
382
  }
383
 
384
  } catch (error) {