Update index.html
Browse files- index.html +154 -107
index.html
CHANGED
@@ -38,6 +38,14 @@
|
|
38 |
from { opacity: 0; transform: translateY(20px); }
|
39 |
to { opacity: 1; transform: translateY(0); }
|
40 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
|
42 |
|
43 |
body {
|
@@ -55,7 +63,7 @@
|
|
55 |
display: flex;
|
56 |
justify-content: center;
|
57 |
align-items: flex-start;
|
58 |
-
overflow-x: hidden;
|
59 |
}
|
60 |
|
61 |
.container {
|
@@ -65,27 +73,27 @@
|
|
65 |
}
|
66 |
|
67 |
.app-header {
|
68 |
-
padding: 0.5rem 0 2.5rem 0;
|
69 |
text-align: center;
|
70 |
-
margin-bottom: 1.5rem;
|
71 |
animation: fadeInDown 0.8s 0.1s ease-out backwards;
|
72 |
}
|
73 |
.app-header h1 {
|
74 |
-
font-size: 2.1em;
|
75 |
font-weight: 900;
|
76 |
-
margin:0 0 0.6rem 0;
|
77 |
background: linear-gradient(45deg, var(--accent-primary), var(--accent-secondary));
|
78 |
-webkit-background-clip: text;
|
79 |
-webkit-text-fill-color: transparent;
|
80 |
-
letter-spacing: -0.5px;
|
81 |
}
|
82 |
.app-header p {
|
83 |
-
font-size: 1em;
|
84 |
color: var(--text-secondary);
|
85 |
margin-top:0;
|
86 |
-
opacity: 0.85;
|
87 |
font-weight: 400;
|
88 |
-
line-height: 1.6;
|
89 |
}
|
90 |
|
91 |
.main-content {
|
@@ -109,7 +117,7 @@
|
|
109 |
margin-bottom: 0;
|
110 |
}
|
111 |
|
112 |
-
.info-tooltip-icon
|
113 |
display: inline-flex;
|
114 |
align-items: center;
|
115 |
justify-content: center;
|
@@ -126,49 +134,14 @@
|
|
126 |
transition: var(--transition-smooth);
|
127 |
user-select: none;
|
128 |
}
|
129 |
-
.info-
|
130 |
background-color: var(--accent-primary);
|
131 |
color: white;
|
132 |
border-color: var(--accent-primary);
|
133 |
transform: scale(1.1);
|
134 |
outline: none;
|
135 |
}
|
136 |
-
.tooltip-text
|
137 |
-
visibility: hidden;
|
138 |
-
width: 280px;
|
139 |
-
background-color: var(--text-primary);
|
140 |
-
color: #fff;
|
141 |
-
text-align: right;
|
142 |
-
border-radius: var(--radius-input);
|
143 |
-
padding: 10px 15px;
|
144 |
-
position: absolute;
|
145 |
-
z-index: 10;
|
146 |
-
bottom: 140%;
|
147 |
-
left: 50%;
|
148 |
-
transform: translateX(-50%) translateY(10px);
|
149 |
-
opacity: 0;
|
150 |
-
transition: opacity 0.3s, visibility 0.3s, transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
151 |
-
font-size: 0.88em;
|
152 |
-
font-weight: 400;
|
153 |
-
line-height: 1.6;
|
154 |
-
box-shadow: var(--shadow-medium);
|
155 |
-
}
|
156 |
-
.tooltip-text::after {
|
157 |
-
content: "";
|
158 |
-
position: absolute;
|
159 |
-
top: 100%;
|
160 |
-
left: 50%;
|
161 |
-
margin-left: -5px;
|
162 |
-
border-width: 5px;
|
163 |
-
border-style: solid;
|
164 |
-
border-color: var(--text-primary) transparent transparent transparent;
|
165 |
-
}
|
166 |
-
.info-tooltip-icon.active .tooltip-text {
|
167 |
-
visibility: visible;
|
168 |
-
opacity: 1;
|
169 |
-
transform: translateX(-50%) translateY(0);
|
170 |
-
}
|
171 |
-
|
172 |
|
173 |
label {
|
174 |
display: block;
|
@@ -273,34 +246,48 @@
|
|
273 |
}
|
274 |
|
275 |
|
276 |
-
/* --- مودال
|
277 |
-
|
278 |
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
279 |
-
background-color: rgba(18, 24, 38, 0.
|
280 |
backdrop-filter: blur(8px) saturate(150%);
|
281 |
-
display: none;
|
|
|
282 |
z-index: 1000; opacity: 0;
|
283 |
transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
284 |
}
|
285 |
-
|
286 |
-
|
|
|
287 |
background: var(--panel-bg);
|
288 |
padding: 2rem;
|
289 |
border-radius: var(--radius-card);
|
290 |
-
width: 90%;
|
291 |
-
max-height: 85vh; overflow-y: auto;
|
292 |
-
transform: scale(0.95) translateY(20px);
|
293 |
-
transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.35s;
|
294 |
-
border: 1px solid var(--panel-border);
|
295 |
box-shadow: var(--shadow-strong);
|
296 |
-
|
|
|
|
|
|
|
297 |
}
|
298 |
-
|
299 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
300 |
.modal-header h2 { margin: 0; font-size: 1.6em; font-weight: 800; color: var(--accent-primary);}
|
301 |
.close-modal-btn { background: none; border: none; font-size: 2.5rem; cursor: pointer; color: var(--text-secondary); transition: var(--transition-smooth); line-height: 1; }
|
302 |
.close-modal-btn:hover { color: var(--accent-primary); transform: rotate(90deg) scale(1.1); }
|
303 |
|
|
|
|
|
|
|
|
|
|
|
|
|
304 |
#speaker-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 1.2rem; }
|
305 |
@media (min-width: 640px) { #speaker-grid { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); } }
|
306 |
.speaker-card { cursor: pointer; transition: var(--transition-smooth); text-align: center; position: relative;}
|
@@ -317,7 +304,7 @@
|
|
317 |
.speaker-card:hover .speaker-visual {
|
318 |
transform: translateY(-5px) scale(1.06);
|
319 |
box-shadow: var(--shadow-medium);
|
320 |
-
border-color: rgba(var(--accent-secondary-rgb), 0.3);
|
321 |
}
|
322 |
.speaker-card input[type="radio"] { display: none; }
|
323 |
.speaker-card img {
|
@@ -344,6 +331,30 @@
|
|
344 |
font-weight: 700;
|
345 |
}
|
346 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
347 |
/* --- Slider & Button & Output --- */
|
348 |
.slider-container { display: flex; align-items: center; gap: 1.2rem; }
|
349 |
input[type="range"] {
|
@@ -582,8 +593,8 @@
|
|
582 |
<div class="form-group">
|
583 |
<div class="label-with-info">
|
584 |
<label for="temperature-slider">🌡️ میزان خلاقیت و نوآوری صدا</label>
|
585 |
-
<div class="info-
|
586 |
-
|
587 |
</div>
|
588 |
</div>
|
589 |
<div class="slider-container">
|
@@ -610,11 +621,12 @@
|
|
610 |
</main>
|
611 |
</div>
|
612 |
|
613 |
-
|
614 |
-
|
|
|
615 |
<div class="modal-header">
|
616 |
<h2>گالری گویندگان آلفا نوا</h2>
|
617 |
-
<button type="button" class="close-modal-btn" aria-label="بستن مودال">×</button>
|
618 |
</div>
|
619 |
<div id="speaker-grid">
|
620 |
<!-- Speaker cards will be generated here by JS -->
|
@@ -622,6 +634,23 @@
|
|
622 |
</div>
|
623 |
</div>
|
624 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
625 |
<input type="hidden" id="selected_speaker_id_storage" value="Charon">
|
626 |
|
627 |
<script>
|
@@ -678,27 +707,30 @@
|
|
678 |
const loadingAnimationWrapper = document.getElementById('loading-animation-wrapper');
|
679 |
|
680 |
const selectedSpeakerIdStorage = document.getElementById('selected_speaker_id_storage');
|
681 |
-
const speakerModal = document.getElementById('speaker-modal');
|
682 |
const changeSpeakerBtn = document.getElementById('change-speaker-btn');
|
683 |
const selectedSpeakerCard = document.getElementById('selected-speaker-card');
|
684 |
-
const closeModalBtn = document.querySelector('.close-modal-btn');
|
685 |
const speakerGridInModal = document.getElementById('speaker-grid');
|
686 |
const selectedSpeakerImgDisplay = document.getElementById('selected-speaker-img');
|
687 |
const selectedSpeakerNameDisplay = document.getElementById('selected-speaker-name');
|
688 |
const selectedSpeakerDescDisplay = document.getElementById('selected-speaker-desc');
|
|
|
|
|
|
|
|
|
689 |
const tempInfoIcon = document.getElementById('temp-info-icon');
|
690 |
|
|
|
691 |
// Character Counter
|
692 |
const charCountSpan = document.getElementById('char-count');
|
693 |
const charMaxSpan = document.getElementById('char-max');
|
694 |
-
const MAX_CHARS = 50000;
|
695 |
-
charMaxSpan.textContent = MAX_CHARS.toLocaleString('fa-IR');
|
696 |
|
697 |
textInput.addEventListener('input', () => {
|
698 |
const currentLength = textInput.value.length;
|
699 |
charCountSpan.textContent = currentLength.toLocaleString('fa-IR');
|
700 |
if (currentLength > MAX_CHARS) {
|
701 |
-
charCountSpan.style.color = 'var(--accent-secondary-hover)';
|
702 |
} else {
|
703 |
charCountSpan.style.color = 'var(--accent-primary)';
|
704 |
}
|
@@ -714,7 +746,6 @@
|
|
714 |
const imageIndex = (index * 7 + speaker.id.length * 3 + 5) % 100;
|
715 |
let portraitSizePath = 'thumb/';
|
716 |
if (size === 'large') portraitSizePath = '';
|
717 |
-
|
718 |
return `https://randomuser.me/api/portraits/${portraitSizePath}${gender}/${imageIndex}.jpg`;
|
719 |
}
|
720 |
|
@@ -735,7 +766,6 @@
|
|
735 |
cardLabel.className = 'speaker-card';
|
736 |
cardLabel.setAttribute('for', `modal-speaker-${speaker.id}`);
|
737 |
const isChecked = speaker.id === selectedSpeakerIdStorage.value ? 'checked' : '';
|
738 |
-
|
739 |
cardLabel.innerHTML = `
|
740 |
<input type="radio" name="modal_speaker_selection" value="${speaker.id}" id="modal-speaker-${speaker.id}" ${isChecked}>
|
741 |
<div class="speaker-visual">
|
@@ -743,64 +773,81 @@
|
|
743 |
<div class="speaker-name">${speaker.name}</div>
|
744 |
</div>
|
745 |
`;
|
746 |
-
|
747 |
cardLabel.addEventListener('click', (e) => {
|
748 |
if (e.target.name !== "modal_speaker_selection") {
|
749 |
const radio = cardLabel.querySelector('input[type="radio"]');
|
750 |
if(radio) radio.checked = true;
|
751 |
}
|
752 |
updateSelectedSpeakerDisplay(speaker.id);
|
753 |
-
|
754 |
});
|
755 |
-
|
756 |
speakerGridInModal.appendChild(cardLabel);
|
757 |
});
|
758 |
}
|
759 |
|
760 |
-
|
761 |
-
|
762 |
-
|
|
|
763 |
setTimeout(() => {
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
}, 50);
|
768 |
}
|
769 |
|
770 |
-
|
771 |
-
|
|
|
|
|
|
|
|
|
772 |
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
}
|
778 |
});
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
}
|
783 |
});
|
784 |
|
785 |
-
|
786 |
-
|
787 |
-
tempInfoIcon.addEventListener('
|
788 |
-
e.stopPropagation();
|
789 |
-
tempInfoIcon.classList.toggle('active');
|
790 |
-
});
|
791 |
-
tempInfoIcon.addEventListener('keydown', (e) => {
|
792 |
if (e.key === 'Enter' || e.key === ' ') {
|
793 |
e.preventDefault();
|
794 |
-
|
795 |
}
|
796 |
});
|
797 |
-
|
798 |
-
|
799 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
800 |
}
|
801 |
});
|
802 |
|
803 |
|
|
|
|
|
|
|
804 |
function showLoadingState() {
|
805 |
outputSection.classList.remove('has-content');
|
806 |
statusMessage.style.display = 'none';
|
@@ -874,7 +921,7 @@
|
|
874 |
|
875 |
let finalFilePath = null;
|
876 |
const startTime = Date.now();
|
877 |
-
const timeoutDuration = 90000;
|
878 |
|
879 |
while (Date.now() - startTime < timeoutDuration) {
|
880 |
const dataResponse = await fetch(`${GET_DATA_URL_BASE}?session_hash=${sessionHash}`);
|
|
|
38 |
from { opacity: 0; transform: translateY(20px); }
|
39 |
to { opacity: 1; transform: translateY(0); }
|
40 |
}
|
41 |
+
@keyframes modalZoomIn {
|
42 |
+
from { opacity: 0; transform: scale(0.8) translateY(20px); }
|
43 |
+
to { opacity: 1; transform: scale(1) translateY(0); }
|
44 |
+
}
|
45 |
+
@keyframes modalZoomOut {
|
46 |
+
from { opacity: 1; transform: scale(1) translateY(0); }
|
47 |
+
to { opacity: 0; transform: scale(0.8) translateY(20px); }
|
48 |
+
}
|
49 |
|
50 |
|
51 |
body {
|
|
|
63 |
display: flex;
|
64 |
justify-content: center;
|
65 |
align-items: flex-start;
|
66 |
+
overflow-x: hidden;
|
67 |
}
|
68 |
|
69 |
.container {
|
|
|
73 |
}
|
74 |
|
75 |
.app-header {
|
76 |
+
padding: 0.5rem 0 2.5rem 0;
|
77 |
text-align: center;
|
78 |
+
margin-bottom: 1.5rem;
|
79 |
animation: fadeInDown 0.8s 0.1s ease-out backwards;
|
80 |
}
|
81 |
.app-header h1 {
|
82 |
+
font-size: 2.1em;
|
83 |
font-weight: 900;
|
84 |
+
margin:0 0 0.6rem 0;
|
85 |
background: linear-gradient(45deg, var(--accent-primary), var(--accent-secondary));
|
86 |
-webkit-background-clip: text;
|
87 |
-webkit-text-fill-color: transparent;
|
88 |
+
letter-spacing: -0.5px;
|
89 |
}
|
90 |
.app-header p {
|
91 |
+
font-size: 1em;
|
92 |
color: var(--text-secondary);
|
93 |
margin-top:0;
|
94 |
+
opacity: 0.85;
|
95 |
font-weight: 400;
|
96 |
+
line-height: 1.6;
|
97 |
}
|
98 |
|
99 |
.main-content {
|
|
|
117 |
margin-bottom: 0;
|
118 |
}
|
119 |
|
120 |
+
.info-icon { /* Renamed from info-tooltip-icon for clarity */
|
121 |
display: inline-flex;
|
122 |
align-items: center;
|
123 |
justify-content: center;
|
|
|
134 |
transition: var(--transition-smooth);
|
135 |
user-select: none;
|
136 |
}
|
137 |
+
.info-icon:hover, .info-icon:focus {
|
138 |
background-color: var(--accent-primary);
|
139 |
color: white;
|
140 |
border-color: var(--accent-primary);
|
141 |
transform: scale(1.1);
|
142 |
outline: none;
|
143 |
}
|
144 |
+
/* Removed .tooltip-text and .info-tooltip-icon.active as it's now a modal */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
145 |
|
146 |
label {
|
147 |
display: block;
|
|
|
246 |
}
|
247 |
|
248 |
|
249 |
+
/* --- مودال عمومی --- */
|
250 |
+
.modal-overlay {
|
251 |
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
252 |
+
background-color: rgba(18, 24, 38, 0.6);
|
253 |
backdrop-filter: blur(8px) saturate(150%);
|
254 |
+
display: none; /* Hidden by default */
|
255 |
+
align-items: center; justify-content: center;
|
256 |
z-index: 1000; opacity: 0;
|
257 |
transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
258 |
}
|
259 |
+
.modal-overlay.visible { display: flex; opacity: 1; }
|
260 |
+
|
261 |
+
.modal-dialog {
|
262 |
background: var(--panel-bg);
|
263 |
padding: 2rem;
|
264 |
border-radius: var(--radius-card);
|
265 |
+
width: 90%;
|
|
|
|
|
|
|
|
|
266 |
box-shadow: var(--shadow-strong);
|
267 |
+
border: 1px solid var(--panel-border);
|
268 |
+
opacity: 0; /* For animation */
|
269 |
+
animation-duration: 0.35s;
|
270 |
+
animation-fill-mode: forwards;
|
271 |
}
|
272 |
+
.modal-overlay.visible .modal-dialog {
|
273 |
+
animation-name: modalZoomIn;
|
274 |
+
}
|
275 |
+
.modal-overlay.hiding .modal-dialog {
|
276 |
+
animation-name: modalZoomOut;
|
277 |
+
}
|
278 |
+
|
279 |
+
|
280 |
+
.modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; padding-bottom: 1rem; border-bottom: 1px solid var(--panel-border); }
|
281 |
.modal-header h2 { margin: 0; font-size: 1.6em; font-weight: 800; color: var(--accent-primary);}
|
282 |
.close-modal-btn { background: none; border: none; font-size: 2.5rem; cursor: pointer; color: var(--text-secondary); transition: var(--transition-smooth); line-height: 1; }
|
283 |
.close-modal-btn:hover { color: var(--accent-primary); transform: rotate(90deg) scale(1.1); }
|
284 |
|
285 |
+
|
286 |
+
/* --- مودال گالری گویندگان (اختصاصی) --- */
|
287 |
+
#speaker-modal .modal-dialog { /* Target specific modal dialog */
|
288 |
+
max-width: 700px;
|
289 |
+
max-height: 85vh; overflow-y: auto;
|
290 |
+
}
|
291 |
#speaker-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 1.2rem; }
|
292 |
@media (min-width: 640px) { #speaker-grid { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); } }
|
293 |
.speaker-card { cursor: pointer; transition: var(--transition-smooth); text-align: center; position: relative;}
|
|
|
304 |
.speaker-card:hover .speaker-visual {
|
305 |
transform: translateY(-5px) scale(1.06);
|
306 |
box-shadow: var(--shadow-medium);
|
307 |
+
/* border-color: rgba(var(--accent-secondary-rgb), 0.3); */ /* Requires CSS var for RGB components */
|
308 |
}
|
309 |
.speaker-card input[type="radio"] { display: none; }
|
310 |
.speaker-card img {
|
|
|
331 |
font-weight: 700;
|
332 |
}
|
333 |
|
334 |
+
/* --- مودال اطلاعات (اختصاصی) --- */
|
335 |
+
#info-modal .modal-dialog {
|
336 |
+
max-width: 480px; /* Smaller width for info */
|
337 |
+
}
|
338 |
+
#info-modal-content p {
|
339 |
+
font-size: 1em;
|
340 |
+
line-height: 1.7;
|
341 |
+
color: var(--text-secondary);
|
342 |
+
margin-bottom: 0.5rem;
|
343 |
+
}
|
344 |
+
#info-modal-content p strong {
|
345 |
+
color: var(--text-primary);
|
346 |
+
font-weight: 600;
|
347 |
+
}
|
348 |
+
#info-modal-content .range-info {
|
349 |
+
font-size: 0.9em;
|
350 |
+
color: var(--accent-primary);
|
351 |
+
font-weight: 500;
|
352 |
+
margin-top: 1rem;
|
353 |
+
display: block;
|
354 |
+
text-align: center;
|
355 |
+
}
|
356 |
+
|
357 |
+
|
358 |
/* --- Slider & Button & Output --- */
|
359 |
.slider-container { display: flex; align-items: center; gap: 1.2rem; }
|
360 |
input[type="range"] {
|
|
|
593 |
<div class="form-group">
|
594 |
<div class="label-with-info">
|
595 |
<label for="temperature-slider">🌡️ میزان خلاقیت و نوآوری صدا</label>
|
596 |
+
<div class="info-icon" id="temp-info-icon" role="button" tabindex="0" aria-label="اطلاعات بیشتر">!
|
597 |
+
<!-- Tooltip text removed, will be shown in modal -->
|
598 |
</div>
|
599 |
</div>
|
600 |
<div class="slider-container">
|
|
|
621 |
</main>
|
622 |
</div>
|
623 |
|
624 |
+
<!-- مودال گالری گویندگان -->
|
625 |
+
<div id="speaker-modal" class="modal-overlay">
|
626 |
+
<div class="modal-dialog">
|
627 |
<div class="modal-header">
|
628 |
<h2>گالری گویندگان آلفا نوا</h2>
|
629 |
+
<button type="button" class="close-modal-btn" data-modal-id="speaker-modal" aria-label="بستن مودال">×</button>
|
630 |
</div>
|
631 |
<div id="speaker-grid">
|
632 |
<!-- Speaker cards will be generated here by JS -->
|
|
|
634 |
</div>
|
635 |
</div>
|
636 |
|
637 |
+
<!-- مودال اطلاعات خلاقیت صدا -->
|
638 |
+
<div id="info-modal" class="modal-overlay">
|
639 |
+
<div class="modal-dialog">
|
640 |
+
<div class="modal-header">
|
641 |
+
<h2>🌡️ خلاقیت و نوآوری صدا</h2>
|
642 |
+
<button type="button" class="close-modal-btn" data-modal-id="info-modal" aria-label="بستن توضیحات">×</button>
|
643 |
+
</div>
|
644 |
+
<div id="info-modal-content">
|
645 |
+
<p>این تنظیم مشخص میکند که هوش مصنوعی تا چه حد در تولید صدا <strong>خلاقیت</strong> به خرج دهد.</p>
|
646 |
+
<p><strong>مقادیر بالاتر:</strong> منجر به صدایی متنوعتر، پویاتر و گاهی اوقات غیرمنتظرهتر میشود. مناسب برای زمانی که به دنبال لحنی خاص و منحصربهفرد هستید.</p>
|
647 |
+
<p><strong>مقادیر پایینتر:</strong> صدایی پایدارتر، قابل پیشبینیتر و نزدیکتر به صدای استاندارد گوینده تولید میکند. مناسب برای خوانش متون رسمی یا زمانی که ثبات لحن اهمیت دارد.</p>
|
648 |
+
<span class="range-info">محدوده پیشنهادی: ۰.۱ (پایدار) تا ۱.۵ (بسیار خلاق)</span>
|
649 |
+
</div>
|
650 |
+
</div>
|
651 |
+
</div>
|
652 |
+
|
653 |
+
|
654 |
<input type="hidden" id="selected_speaker_id_storage" value="Charon">
|
655 |
|
656 |
<script>
|
|
|
707 |
const loadingAnimationWrapper = document.getElementById('loading-animation-wrapper');
|
708 |
|
709 |
const selectedSpeakerIdStorage = document.getElementById('selected_speaker_id_storage');
|
|
|
710 |
const changeSpeakerBtn = document.getElementById('change-speaker-btn');
|
711 |
const selectedSpeakerCard = document.getElementById('selected-speaker-card');
|
|
|
712 |
const speakerGridInModal = document.getElementById('speaker-grid');
|
713 |
const selectedSpeakerImgDisplay = document.getElementById('selected-speaker-img');
|
714 |
const selectedSpeakerNameDisplay = document.getElementById('selected-speaker-name');
|
715 |
const selectedSpeakerDescDisplay = document.getElementById('selected-speaker-desc');
|
716 |
+
|
717 |
+
// Modal Elements
|
718 |
+
const speakerModal = document.getElementById('speaker-modal');
|
719 |
+
const infoModal = document.getElementById('info-modal');
|
720 |
const tempInfoIcon = document.getElementById('temp-info-icon');
|
721 |
|
722 |
+
|
723 |
// Character Counter
|
724 |
const charCountSpan = document.getElementById('char-count');
|
725 |
const charMaxSpan = document.getElementById('char-max');
|
726 |
+
const MAX_CHARS = 50000;
|
727 |
+
charMaxSpan.textContent = MAX_CHARS.toLocaleString('fa-IR');
|
728 |
|
729 |
textInput.addEventListener('input', () => {
|
730 |
const currentLength = textInput.value.length;
|
731 |
charCountSpan.textContent = currentLength.toLocaleString('fa-IR');
|
732 |
if (currentLength > MAX_CHARS) {
|
733 |
+
charCountSpan.style.color = 'var(--accent-secondary-hover)';
|
734 |
} else {
|
735 |
charCountSpan.style.color = 'var(--accent-primary)';
|
736 |
}
|
|
|
746 |
const imageIndex = (index * 7 + speaker.id.length * 3 + 5) % 100;
|
747 |
let portraitSizePath = 'thumb/';
|
748 |
if (size === 'large') portraitSizePath = '';
|
|
|
749 |
return `https://randomuser.me/api/portraits/${portraitSizePath}${gender}/${imageIndex}.jpg`;
|
750 |
}
|
751 |
|
|
|
766 |
cardLabel.className = 'speaker-card';
|
767 |
cardLabel.setAttribute('for', `modal-speaker-${speaker.id}`);
|
768 |
const isChecked = speaker.id === selectedSpeakerIdStorage.value ? 'checked' : '';
|
|
|
769 |
cardLabel.innerHTML = `
|
770 |
<input type="radio" name="modal_speaker_selection" value="${speaker.id}" id="modal-speaker-${speaker.id}" ${isChecked}>
|
771 |
<div class="speaker-visual">
|
|
|
773 |
<div class="speaker-name">${speaker.name}</div>
|
774 |
</div>
|
775 |
`;
|
|
|
776 |
cardLabel.addEventListener('click', (e) => {
|
777 |
if (e.target.name !== "modal_speaker_selection") {
|
778 |
const radio = cardLabel.querySelector('input[type="radio"]');
|
779 |
if(radio) radio.checked = true;
|
780 |
}
|
781 |
updateSelectedSpeakerDisplay(speaker.id);
|
782 |
+
hideModal(speakerModal);
|
783 |
});
|
|
|
784 |
speakerGridInModal.appendChild(cardLabel);
|
785 |
});
|
786 |
}
|
787 |
|
788 |
+
// --- Modal Management ---
|
789 |
+
function showModal(modalElement) {
|
790 |
+
modalElement.classList.add('visible');
|
791 |
+
// Focus first focusable element in modal
|
792 |
setTimeout(() => {
|
793 |
+
const firstFocusable = modalElement.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
794 |
+
if (firstFocusable) firstFocusable.focus();
|
795 |
+
}, 50); // Allow transition to start
|
|
|
796 |
}
|
797 |
|
798 |
+
function hideModal(modalElement) {
|
799 |
+
modalElement.classList.add('hiding'); // Add class for exit animation
|
800 |
+
modalElement.addEventListener('animationend', () => {
|
801 |
+
modalElement.classList.remove('visible', 'hiding');
|
802 |
+
}, { once: true });
|
803 |
+
}
|
804 |
|
805 |
+
// Speaker Modal Listeners
|
806 |
+
changeSpeakerBtn.addEventListener('click', () => {
|
807 |
+
createSpeakerCardsInModal();
|
808 |
+
showModal(speakerModal);
|
|
|
809 |
});
|
810 |
+
selectedSpeakerCard.addEventListener('click', () => {
|
811 |
+
createSpeakerCardsInModal();
|
812 |
+
showModal(speakerModal);
|
|
|
813 |
});
|
814 |
|
815 |
+
// Info Modal Listener
|
816 |
+
tempInfoIcon.addEventListener('click', () => showModal(infoModal));
|
817 |
+
tempInfoIcon.addEventListener('keydown', (e) => {
|
|
|
|
|
|
|
|
|
818 |
if (e.key === 'Enter' || e.key === ' ') {
|
819 |
e.preventDefault();
|
820 |
+
showModal(infoModal);
|
821 |
}
|
822 |
});
|
823 |
+
|
824 |
+
|
825 |
+
// General Modal Close Listeners (for all modals)
|
826 |
+
document.querySelectorAll('.modal-overlay').forEach(overlay => {
|
827 |
+
overlay.addEventListener('click', (e) => {
|
828 |
+
if (e.target === overlay) { // Click on backdrop
|
829 |
+
hideModal(overlay);
|
830 |
+
}
|
831 |
+
});
|
832 |
+
});
|
833 |
+
document.querySelectorAll('.close-modal-btn').forEach(button => {
|
834 |
+
button.addEventListener('click', () => {
|
835 |
+
const modalId = button.dataset.modalId;
|
836 |
+
if (modalId) {
|
837 |
+
hideModal(document.getElementById(modalId));
|
838 |
+
}
|
839 |
+
});
|
840 |
+
});
|
841 |
+
document.addEventListener('keydown', (e) => {
|
842 |
+
if (e.key === 'Escape') {
|
843 |
+
document.querySelectorAll('.modal-overlay.visible').forEach(hideModal);
|
844 |
}
|
845 |
});
|
846 |
|
847 |
|
848 |
+
tempSlider.addEventListener('input', () => { tempValueSpan.textContent = tempSlider.value; });
|
849 |
+
|
850 |
+
|
851 |
function showLoadingState() {
|
852 |
outputSection.classList.remove('has-content');
|
853 |
statusMessage.style.display = 'none';
|
|
|
921 |
|
922 |
let finalFilePath = null;
|
923 |
const startTime = Date.now();
|
924 |
+
const timeoutDuration = 90000;
|
925 |
|
926 |
while (Date.now() - startTime < timeoutDuration) {
|
927 |
const dataResponse = await fetch(`${GET_DATA_URL_BASE}?session_hash=${sessionHash}`);
|