Update index.html
Browse files- index.html +284 -205
index.html
CHANGED
|
@@ -2,239 +2,317 @@
|
|
| 2 |
<html lang="en">
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
-
<title>WordConjure:
|
| 7 |
<!-- Tailwind CSS CDN for unified styling -->
|
| 8 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 9 |
-
<!--
|
| 10 |
-
<link href="https://fonts.googleapis.com/css2?family=
|
| 11 |
<style>
|
| 12 |
body {
|
| 13 |
-
font-family: '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
}
|
| 15 |
</style>
|
| 16 |
</head>
|
| 17 |
-
<body class="bg-gradient-to-br from-
|
| 18 |
|
| 19 |
-
<!--
|
| 20 |
-
<div id="welcomeModal" class="fixed inset-0 bg-
|
| 21 |
-
<div class="bg-white p-8 rounded-
|
| 22 |
-
<div class="w-
|
| 23 |
🌍
|
| 24 |
</div>
|
| 25 |
-
<h2 class="text-
|
| 26 |
-
<p class="text-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
Choisissez la langue de l'application pour commencer !
|
| 30 |
</p>
|
| 31 |
-
<div class="w-full grid grid-cols-2 gap-
|
| 32 |
-
<button onclick="selectInitialLang('English')" class="py-2.5 bg-
|
| 33 |
-
<button onclick="selectInitialLang('Spanish')" class="py-2.5 bg-
|
| 34 |
-
<button onclick="selectInitialLang('French')" class="py-2.5 bg-
|
| 35 |
-
<button onclick="selectInitialLang('German')" class="py-2.5 bg-
|
| 36 |
-
<button onclick="selectInitialLang('Chinese')" class="py-2.5 bg-
|
| 37 |
-
<button onclick="selectInitialLang('Japanese')" class="py-2.5 bg-
|
| 38 |
-
<button onclick="selectInitialLang('Korean')" class="py-2.5 bg-
|
| 39 |
-
<button onclick="selectInitialLang('Russian')" class="py-2.5 bg-
|
| 40 |
-
<button onclick="selectInitialLang('Portuguese')" class="py-2.5 bg-
|
| 41 |
-
<button onclick="selectInitialLang('Italian')" class="py-2.5 bg-
|
| 42 |
</div>
|
| 43 |
</div>
|
| 44 |
</div>
|
| 45 |
|
| 46 |
-
<
|
|
|
|
| 47 |
|
| 48 |
-
<!--
|
| 49 |
-
<
|
| 50 |
-
<
|
| 51 |
-
<
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
<
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
<option value="Italian">Italiano</option>
|
| 69 |
-
</select>
|
| 70 |
-
</div>
|
| 71 |
-
</div>
|
| 72 |
-
|
| 73 |
-
<!-- Dashboard Navigation Tabs -->
|
| 74 |
-
<div class="flex gap-4 mb-6">
|
| 75 |
-
<button id="tabVocabBtn" class="bg-indigo-500 text-white shadow-md shadow-indigo-100 rounded-2xl px-5 py-3 font-extrabold text-sm scale-105 transition-all outline-none duration-150">📖 My Word Book</button>
|
| 76 |
-
<button id="tabGamesBtn" class="bg-white text-gray-500 hover:bg-indigo-50 border-2 border-indigo-100/50 rounded-2xl px-5 py-3 font-extrabold text-sm transition-all outline-none duration-150">🎮 Play Arena</button>
|
| 77 |
-
</div>
|
| 78 |
-
|
| 79 |
-
<!-- TAB 1: Vocabulary Workspace -->
|
| 80 |
-
<div id="tabVocabContent" class="block">
|
| 81 |
-
<!-- Advanced Settings Dropdown -->
|
| 82 |
-
<details class="mb-6 bg-gradient-to-r from-violet-50 to-fuchsia-50 border-2 border-violet-100 rounded-2xl p-4 cursor-pointer group shadow-sm">
|
| 83 |
-
<summary id="uiSettingsTitle" class="font-extrabold text-sm text-violet-800 select-none flex justify-between items-center outline-none">
|
| 84 |
-
<span>🛠️ Wizard's Settings Panel</span>
|
| 85 |
-
<span class="text-xs text-violet-400 group-open:rotate-180 transition-transform">▼</span>
|
| 86 |
-
</summary>
|
| 87 |
-
<div class="mt-4 border-t border-violet-100 pt-4 cursor-default flex flex-col gap-4">
|
| 88 |
-
<!-- Word count selector -->
|
| 89 |
-
<div>
|
| 90 |
-
<label id="uiWordCountLabel" class="block text-xs font-black text-violet-700 uppercase tracking-wider mb-2">🔮 How many words to summon?</label>
|
| 91 |
-
<input type="range" id="wordCount" min="1" max="10" value="5" class="w-full accent-indigo-500 h-2 bg-violet-100 rounded-lg cursor-pointer">
|
| 92 |
-
<div class="flex justify-between text-xs text-violet-500 mt-1 font-bold">
|
| 93 |
-
<span>1</span>
|
| 94 |
-
<span id="wordCountVal" class="font-extrabold text-indigo-600">5 words</span>
|
| 95 |
-
<span>10</span>
|
| 96 |
</div>
|
| 97 |
-
</
|
| 98 |
-
<
|
| 99 |
-
|
| 100 |
-
<
|
| 101 |
-
|
| 102 |
-
<
|
| 103 |
-
<input type="radio" name="explLang" value="source" checked class="accent-indigo-500 h-4 w-4 mr-2">
|
| 104 |
-
<span id="uiExplSource">App Language</span>
|
| 105 |
-
</label>
|
| 106 |
-
<label class="inline-flex items-center text-sm text-violet-800 font-bold cursor-pointer">
|
| 107 |
-
<input type="radio" name="explLang" value="target" class="accent-indigo-500 h-4 w-4 mr-2">
|
| 108 |
-
<span id="uiExplTarget">Language to Practice</span>
|
| 109 |
-
</label>
|
| 110 |
</div>
|
| 111 |
-
</
|
| 112 |
-
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
<option value="Japanese">Japanese</option>
|
| 126 |
-
<option value="Korean">Korean</option>
|
| 127 |
-
<option value="Russian">Russian</option>
|
| 128 |
-
<option value="Portuguese">Portuguese</option>
|
| 129 |
-
<option value="Italian">Italian</option>
|
| 130 |
-
</select>
|
| 131 |
</div>
|
| 132 |
-
<div class="
|
| 133 |
-
<label id="
|
| 134 |
-
<select id="
|
| 135 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
</select>
|
| 137 |
</div>
|
| 138 |
-
<button id="translateBtn" class="w-full sm:w-auto px-8 py-3 bg-gradient-to-r from-amber-500 to-orange-500 hover:from-amber-600 hover:to-orange-600 active:translate-y-0.5 text-white font-black text-sm rounded-2xl border-b-4 border-orange-700 shadow-lg transition-all">🪄 Summon Words!</button>
|
| 139 |
</div>
|
| 140 |
|
| 141 |
-
<!--
|
| 142 |
-
<div class="
|
| 143 |
-
<
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
<th id="thExplanation" class="px-5 py-4 text-left text-xs font-black text-indigo-900 uppercase tracking-wider max-w-xs">What does it mean?</th>
|
| 149 |
-
<th id="thVisual" class="px-5 py-4 text-center text-xs font-black text-indigo-900 uppercase tracking-wider w-28">Art Card</th>
|
| 150 |
-
<th id="thAudio" class="px-5 py-4 text-center text-xs font-black text-indigo-900 uppercase tracking-wider w-20">Hear It</th>
|
| 151 |
-
</tr>
|
| 152 |
-
</thead>
|
| 153 |
-
<tbody id="resultsBody" class="bg-white divide-y-2 divide-indigo-50">
|
| 154 |
-
<tr>
|
| 155 |
-
<td colspan="5" id="tdInitialMsg" class="px-6 py-12 text-center text-sm font-bold text-gray-400">Select language and click 'Summon Words'.</td>
|
| 156 |
-
</tr>
|
| 157 |
-
</tbody>
|
| 158 |
-
</table>
|
| 159 |
</div>
|
| 160 |
-
</div>
|
| 161 |
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
<div>
|
| 172 |
-
<
|
| 173 |
-
<
|
| 174 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
</div>
|
| 176 |
-
<
|
| 177 |
-
</div>
|
| 178 |
-
|
| 179 |
-
<!-- Word Monster Selection Card -->
|
| 180 |
-
<div class="border-4 border-emerald-100 bg-emerald-50/20 rounded-3xl p-6 hover:shadow-md hover:-translate-y-1 transition-all duration-200 flex flex-col justify-between">
|
| 181 |
<div>
|
| 182 |
-
<
|
| 183 |
-
<
|
| 184 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
</div>
|
| 186 |
-
<button id="playMonsterBtn" class="w-full py-2.5 bg-emerald-500 hover:bg-emerald-600 active:translate-y-0.5 text-white rounded-xl text-sm font-black border-b-4 border-emerald-700 transition-all cursor-pointer">Play Now</button>
|
| 187 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
<
|
| 194 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
</div>
|
| 196 |
-
<button id="playQuizBtn" class="w-full py-2.5 bg-purple-500 hover:bg-purple-600 active:translate-y-0.5 text-white rounded-xl text-sm font-black border-b-4 border-purple-700 transition-all cursor-pointer">Play Now</button>
|
| 197 |
-
</div>
|
| 198 |
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
|
|
|
|
|
|
| 205 |
</div>
|
| 206 |
-
<button id="playScrambleBtn" class="w-full py-2.5 bg-cyan-500 hover:bg-cyan-600 active:translate-y-0.5 text-white rounded-xl text-sm font-black border-b-4 border-cyan-700 transition-all cursor-pointer">Play Now</button>
|
| 207 |
-
</div>
|
| 208 |
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 215 |
</div>
|
| 216 |
-
<button id="playLinkerBtn" class="w-full py-2.5 bg-orange-500 hover:bg-orange-600 active:translate-y-0.5 text-white rounded-xl text-sm font-black border-b-4 border-orange-700 transition-all cursor-pointer">Play Now</button>
|
| 217 |
</div>
|
| 218 |
</div>
|
| 219 |
-
</div>
|
| 220 |
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
|
|
|
|
|
|
| 226 |
</div>
|
| 227 |
-
<div id="gameArea"></div>
|
| 228 |
</div>
|
| 229 |
</div>
|
| 230 |
</div>
|
| 231 |
|
| 232 |
<!-- Modal for full size image preview -->
|
| 233 |
-
<div id="imageModal" class="hidden fixed inset-0 bg-
|
| 234 |
-
<div class="bg-white p-6 rounded-
|
| 235 |
-
<h3 id="modalTitle" class="text-
|
| 236 |
-
<img id="modalImage" class="w-64 h-64 border
|
| 237 |
-
<button class="mt-5 px-6 py-2 bg-
|
| 238 |
</div>
|
| 239 |
</div>
|
| 240 |
|
|
@@ -338,6 +416,7 @@
|
|
| 338 |
|
| 339 |
document.getElementById("uiTitle").innerText = loc.title;
|
| 340 |
document.getElementById("uiSubtitle").innerText = loc.subtitle;
|
|
|
|
| 341 |
document.getElementById("uiSettingsTitle").firstElementChild.innerText = loc.settings;
|
| 342 |
document.getElementById("uiWordCountLabel").innerText = loc.word_count_label;
|
| 343 |
document.getElementById("uiExplLangLabel").innerText = loc.expl_lang_label;
|
|
@@ -368,19 +447,19 @@
|
|
| 368 |
document.getElementById("uiExplTarget").innerText = loc.target_lang;
|
| 369 |
}
|
| 370 |
|
| 371 |
-
// 3. Tab Switching events with
|
| 372 |
tabVocabBtn.addEventListener("click", () => {
|
| 373 |
tabVocabContent.classList.replace("hidden", "block");
|
| 374 |
tabGamesContent.classList.replace("block", "hidden");
|
| 375 |
-
tabVocabBtn.className = "bg-indigo-
|
| 376 |
-
tabGamesBtn.className = "bg-white text-
|
| 377 |
});
|
| 378 |
|
| 379 |
tabGamesBtn.addEventListener("click", () => {
|
| 380 |
tabVocabContent.classList.replace("block", "hidden");
|
| 381 |
tabGamesContent.classList.replace("hidden", "block");
|
| 382 |
-
tabGamesBtn.className = "bg-indigo-
|
| 383 |
-
tabVocabBtn.className = "bg-white text-
|
| 384 |
});
|
| 385 |
|
| 386 |
wordCount.addEventListener("input", (e) => {
|
|
@@ -400,9 +479,9 @@
|
|
| 400 |
|
| 401 |
resultsBody.innerHTML = `
|
| 402 |
<tr>
|
| 403 |
-
<td colspan="5" class="px-
|
| 404 |
-
<span class="inline-block animate-
|
| 405 |
-
<div class="text-
|
| 406 |
</td>
|
| 407 |
</tr>
|
| 408 |
`;
|
|
@@ -416,25 +495,25 @@
|
|
| 416 |
window.loadedVocabulary.forEach(row => {
|
| 417 |
const [original, translated, explanation, visualB64, audioB64] = row;
|
| 418 |
const tr = document.createElement("tr");
|
| 419 |
-
tr.className = "hover:bg-
|
| 420 |
|
| 421 |
tr.innerHTML = `
|
| 422 |
-
<td class="px-
|
| 423 |
-
<td class="px-
|
| 424 |
-
<td class="px-
|
| 425 |
-
<td class="px-
|
| 426 |
${visualB64 ? `
|
| 427 |
<div class="relative inline-block group">
|
| 428 |
<img src="data:image/png;base64,${visualB64}"
|
| 429 |
-
class="w-
|
| 430 |
alt="${translated}"
|
| 431 |
data-src="data:image/png;base64,${visualB64}"
|
| 432 |
data-word="${translated}">
|
| 433 |
</div>
|
| 434 |
` : '-'}
|
| 435 |
</td>
|
| 436 |
-
<td class="px-
|
| 437 |
-
<button class="play-preload-btn w-
|
| 438 |
data-audio="data:audio/mp3;base64,${audioB64}">
|
| 439 |
🔊
|
| 440 |
</button>
|
|
@@ -466,8 +545,8 @@
|
|
| 466 |
} catch (error) {
|
| 467 |
resultsBody.innerHTML = `
|
| 468 |
<tr>
|
| 469 |
-
<td colspan="5" class="px-
|
| 470 |
-
Error
|
| 471 |
</td>
|
| 472 |
</tr>
|
| 473 |
`;
|
|
|
|
| 2 |
<html lang="en">
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes">
|
| 6 |
+
<title>WordConjure: Multimodal Vocabulary Practice</title>
|
| 7 |
<!-- Tailwind CSS CDN for unified styling -->
|
| 8 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 9 |
+
<!-- Premium sans-serif typography suitable for high school, university, and adult students -->
|
| 10 |
+
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
| 11 |
<style>
|
| 12 |
body {
|
| 13 |
+
font-family: 'Inter', 'Plus Jakarta Sans', sans-serif;
|
| 14 |
+
}
|
| 15 |
+
h1, h2, h3, h4, th, .btn-brand {
|
| 16 |
+
font-family: 'Plus Jakarta Sans', sans-serif;
|
| 17 |
+
}
|
| 18 |
+
/* Touch scrollbars styling to avoid blocking touch inputs on mobile wrappers */
|
| 19 |
+
.scrollbar-thin::-webkit-scrollbar {
|
| 20 |
+
height: 4px;
|
| 21 |
+
width: 4px;
|
| 22 |
+
}
|
| 23 |
+
.scrollbar-thin::-webkit-scrollbar-track {
|
| 24 |
+
background: transparent;
|
| 25 |
+
}
|
| 26 |
+
.scrollbar-thin::-webkit-scrollbar-thumb {
|
| 27 |
+
background: #cbd5e1;
|
| 28 |
+
border-radius: 2px;
|
| 29 |
}
|
| 30 |
</style>
|
| 31 |
</head>
|
| 32 |
+
<body class="bg-gradient-to-br from-[#f8fafc] via-[#f1f5f9] to-[#e2e8f0] min-h-screen p-4 md:p-8 text-slate-800">
|
| 33 |
|
| 34 |
+
<!-- Mobile-Optimized Language Selection Modal with swipe/scroll bounds checks -->
|
| 35 |
+
<div id="welcomeModal" class="fixed inset-0 bg-slate-900/60 backdrop-blur-xs flex items-center justify-center p-4 z-50 overflow-y-auto" style="-webkit-overflow-scrolling: touch; touch-action: auto;">
|
| 36 |
+
<div class="bg-white p-6 md:p-8 rounded-2xl shadow-2xl max-w-lg w-full border border-slate-200 flex flex-col items-center text-center my-auto max-h-[85vh] overflow-y-auto scrollbar-thin" style="-webkit-overflow-scrolling: touch;">
|
| 37 |
+
<div class="w-14 h-14 bg-indigo-50 rounded-full flex items-center justify-center mb-4 text-2xl shadow-inner">
|
| 38 |
🌍
|
| 39 |
</div>
|
| 40 |
+
<h2 class="text-xl font-bold text-slate-900 mb-1">Welcome</h2>
|
| 41 |
+
<p class="text-slate-500 font-medium text-xs mb-5 leading-relaxed">
|
| 42 |
+
Select your preferred language to customize the workspace application interface.<br>
|
| 43 |
+
Seleccione su idioma de preferencia para configurar la aplicação.
|
|
|
|
| 44 |
</p>
|
| 45 |
+
<div class="w-full grid grid-cols-2 gap-2.5">
|
| 46 |
+
<button onclick="selectInitialLang('English')" class="py-2.5 px-3 bg-slate-50 hover:bg-indigo-50 hover:text-indigo-600 border border-slate-200 hover:border-indigo-200 rounded-xl font-semibold text-xs transition-all shadow-xs">English</button>
|
| 47 |
+
<button onclick="selectInitialLang('Spanish')" class="py-2.5 px-3 bg-slate-50 hover:bg-indigo-50 hover:text-indigo-600 border border-slate-200 hover:border-indigo-200 rounded-xl font-semibold text-xs transition-all shadow-xs">Español</button>
|
| 48 |
+
<button onclick="selectInitialLang('French')" class="py-2.5 px-3 bg-slate-50 hover:bg-indigo-50 hover:text-indigo-600 border border-slate-200 hover:border-indigo-200 rounded-xl font-semibold text-xs transition-all shadow-xs">Français</button>
|
| 49 |
+
<button onclick="selectInitialLang('German')" class="py-2.5 px-3 bg-slate-50 hover:bg-indigo-50 hover:text-indigo-600 border border-slate-200 hover:border-indigo-200 rounded-xl font-semibold text-xs transition-all shadow-xs">Deutsch</button>
|
| 50 |
+
<button onclick="selectInitialLang('Chinese')" class="py-2.5 px-3 bg-slate-50 hover:bg-indigo-50 hover:text-indigo-600 border border-slate-200 hover:border-indigo-200 rounded-xl font-semibold text-xs transition-all shadow-xs">中文</button>
|
| 51 |
+
<button onclick="selectInitialLang('Japanese')" class="py-2.5 px-3 bg-slate-50 hover:bg-indigo-50 hover:text-indigo-600 border border-slate-200 hover:border-indigo-200 rounded-xl font-semibold text-xs transition-all shadow-xs">日本語</button>
|
| 52 |
+
<button onclick="selectInitialLang('Korean')" class="py-2.5 px-3 bg-slate-50 hover:bg-indigo-50 hover:text-indigo-600 border border-slate-200 hover:border-indigo-200 rounded-xl font-semibold text-xs transition-all shadow-xs">한국어</button>
|
| 53 |
+
<button onclick="selectInitialLang('Russian')" class="py-2.5 px-3 bg-slate-50 hover:bg-indigo-50 hover:text-indigo-600 border border-slate-200 hover:border-indigo-200 rounded-xl font-semibold text-xs transition-all shadow-xs">Русский</button>
|
| 54 |
+
<button onclick="selectInitialLang('Portuguese')" class="py-2.5 px-3 bg-slate-50 hover:bg-indigo-50 hover:text-indigo-600 border border-slate-200 hover:border-indigo-200 rounded-xl font-semibold text-xs transition-all shadow-xs">Português</button>
|
| 55 |
+
<button onclick="selectInitialLang('Italian')" class="py-2.5 px-3 bg-slate-50 hover:bg-indigo-50 hover:text-indigo-600 border border-slate-200 hover:border-indigo-200 rounded-xl font-semibold text-xs transition-all shadow-xs">Italiano</button>
|
| 56 |
</div>
|
| 57 |
</div>
|
| 58 |
</div>
|
| 59 |
|
| 60 |
+
<!-- Centered Structured Workspace Container -->
|
| 61 |
+
<div class="w-full max-w-3xl mx-auto space-y-4">
|
| 62 |
|
| 63 |
+
<!-- Submission Details Accordion: Located in the upper section, short size, expanding vertically -->
|
| 64 |
+
<details class="bg-white rounded-2xl shadow-xs p-4 border border-slate-200 cursor-pointer group transition-all duration-300">
|
| 65 |
+
<summary class="font-bold text-xs text-slate-500 select-none flex justify-between items-center outline-none">
|
| 66 |
+
<span class="flex items-center gap-2">📋 Submission Details & Verification Checklist (Click to Expand)</span>
|
| 67 |
+
<span class="text-[10px] text-slate-400 group-open:rotate-180 transition-transform">▼</span>
|
| 68 |
+
</summary>
|
| 69 |
+
<div class="mt-3 border-t border-slate-100 pt-3 cursor-default text-xs space-y-3 text-slate-600 font-normal leading-relaxed">
|
| 70 |
+
<p>This application is designed for the <strong>Build Small, Play Big Hackathon</strong>. Below is the compliance tracking checklist for validation requirements:</p>
|
| 71 |
+
|
| 72 |
+
<ul class="space-y-2.5">
|
| 73 |
+
<li class="flex items-start gap-2">
|
| 74 |
+
<span class="text-emerald-600 font-bold mt-0.5">✓</span>
|
| 75 |
+
<div>
|
| 76 |
+
<strong class="text-slate-800">REQ-01: Stay under 32B Parameter Limit</strong>
|
| 77 |
+
<p class="text-slate-400">Operates 100% within constraints with a cumulative parameter count of <strong>17.3 Billion parameters</strong>:</p>
|
| 78 |
+
<ul class="list-disc pl-4 text-slate-400 mt-0.5 space-y-0.5">
|
| 79 |
+
<li>Language Engine: bartowski/Qwen2.5-14B-Instruct-GGUF (14.0B)</li>
|
| 80 |
+
<li>Audio TTS Synthesis: Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice (1.7B)</li>
|
| 81 |
+
<li>Visual Flashcard Diffusion: Efficient-Large-Model/Sana_Sprint_1.6B (1.6B)</li>
|
| 82 |
+
</ul>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
</div>
|
| 84 |
+
</li>
|
| 85 |
+
<li class="flex items-start gap-2">
|
| 86 |
+
<span class="text-emerald-600 font-bold mt-0.5">✓</span>
|
| 87 |
+
<div>
|
| 88 |
+
<strong class="text-slate-800">REQ-02: Ship a Gradio App</strong>
|
| 89 |
+
<p class="text-slate-500">Deploys dynamically via a Gradio mounting container leveraging a FastAPI backend architecture.</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
</div>
|
| 91 |
+
</li>
|
| 92 |
+
<li class="flex items-start gap-2">
|
| 93 |
+
<span class="text-emerald-600 font-bold mt-0.5">✓</span>
|
| 94 |
+
<div>
|
| 95 |
+
<strong class="text-slate-800">REQ-03 & REQ-04: Video Demonstration & Social Media Post</strong>
|
| 96 |
+
<p class="text-slate-500">The illustrative video walkthrough is hosted and embedded directly inside the social media announcement post:</p>
|
| 97 |
+
<div class="mt-1.5 p-2 bg-slate-50 rounded border border-slate-100 text-slate-500 flex items-center gap-2">
|
| 98 |
+
🎥 <span>Announcement & Demo: <a href="https://x.com/_r3gm/status/2066206438712558019" target="_blank" rel="noopener noreferrer" class="text-indigo-600 hover:underline font-semibold">View Announcement & Demo Video on X</a></span>
|
| 99 |
+
</div>
|
| 100 |
+
</div>
|
| 101 |
+
</li>
|
| 102 |
+
<li class="flex items-start gap-2">
|
| 103 |
+
<span class="text-emerald-600 font-bold mt-0.5">✓</span>
|
| 104 |
+
<div>
|
| 105 |
+
<strong class="text-slate-800">REQ-05: Mind the GPU Limit</strong>
|
| 106 |
+
<p class="text-slate-500">Optimized for low footprint environments using sequential processing buffers and native FP16 execution maps.</p>
|
| 107 |
+
</div>
|
| 108 |
+
</li>
|
| 109 |
+
</ul>
|
| 110 |
+
</div>
|
| 111 |
+
</details>
|
| 112 |
|
| 113 |
+
<!-- Main Workspace Card (Premium Minimal Style) -->
|
| 114 |
+
<div class="bg-white rounded-2xl shadow-sm p-6 md:p-8 border border-slate-200/80 transition-all duration-300">
|
| 115 |
+
|
| 116 |
+
<!-- App Language Selection Header -->
|
| 117 |
+
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-4 border-b border-slate-100 pb-4">
|
| 118 |
+
<div>
|
| 119 |
+
<h1 id="uiTitle" class="text-xl md:text-2xl font-bold text-slate-900 tracking-tight flex items-center gap-2">
|
| 120 |
+
📘 WordConjure: Multimodal Vocabulary Practice
|
| 121 |
+
</h1>
|
| 122 |
+
<p id="uiSubtitle" class="text-xs text-slate-500 font-medium">An educational system that automatically generates context-rich translations, clean pronunciation guides, and visual cards to optimize foreign language acquisition.</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
</div>
|
| 124 |
+
<div class="text-right w-full sm:w-auto">
|
| 125 |
+
<label id="uiAppLangLabel" class="block text-[9px] font-bold text-slate-400 uppercase tracking-wider mb-1">App Language</label>
|
| 126 |
+
<select id="guiLangSelect" class="w-full sm:w-auto p-2 border border-slate-200 rounded-xl text-xs font-semibold bg-slate-50 text-slate-700 outline-none cursor-pointer">
|
| 127 |
+
<option value="English">English</option>
|
| 128 |
+
<option value="Spanish">Español</option>
|
| 129 |
+
<option value="French">Français</option>
|
| 130 |
+
<option value="German">Deutsch</option>
|
| 131 |
+
<option value="Chinese">中文</option>
|
| 132 |
+
<option value="Japanese">日本語</option>
|
| 133 |
+
<option value="Korean">한국어</option>
|
| 134 |
+
<option value="Russian">Русский</option>
|
| 135 |
+
<option value="Portuguese">Português</option>
|
| 136 |
+
<option value="Italian">Italiano</option>
|
| 137 |
</select>
|
| 138 |
</div>
|
|
|
|
| 139 |
</div>
|
| 140 |
|
| 141 |
+
<!-- Localized 2-Sentence Tutorial Section -->
|
| 142 |
+
<div class="mb-6 p-3 bg-slate-50 border border-slate-200/60 rounded-xl flex items-start gap-2.5 text-xs text-slate-600 font-normal leading-relaxed">
|
| 143 |
+
<span class="text-sm mt-0.5">💡</span>
|
| 144 |
+
<p id="uiTutorial">
|
| 145 |
+
<strong>First:</strong> Select your target preferences below and click <strong>"Generate Vocabulary"</strong> to load your custom multimodal study deck.
|
| 146 |
+
<strong>Then:</strong> Navigate to the <strong>"Practice Exercises"</strong> tab above to interactively test your memory through structured games.
|
| 147 |
+
</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
</div>
|
|
|
|
| 149 |
|
| 150 |
+
<!-- Dashboard Navigation Tabs -->
|
| 151 |
+
<div class="flex gap-3 mb-6">
|
| 152 |
+
<button id="tabVocabBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white shadow-xs rounded-xl px-4 py-2 font-bold text-xs transition-all outline-none">📖 Vocabulary Deck</button>
|
| 153 |
+
<button id="tabGamesBtn" class="bg-white text-slate-600 hover:bg-slate-50 border border-slate-200 rounded-xl px-4 py-2 font-bold text-xs transition-all outline-none">🎮 Practice Exercises</button>
|
| 154 |
+
</div>
|
| 155 |
+
|
| 156 |
+
<!-- TAB 1: Vocabulary Workspace -->
|
| 157 |
+
<div id="tabVocabContent" class="block">
|
| 158 |
+
<!-- Advanced Settings Dropdown -->
|
| 159 |
+
<details class="mb-5 bg-slate-50 border border-slate-200 rounded-2xl p-4 cursor-pointer group shadow-xs">
|
| 160 |
+
<summary id="uiSettingsTitle" class="font-bold text-xs text-slate-700 select-none flex justify-between items-center outline-none">
|
| 161 |
+
<span>⚙️ Session Configuration</span>
|
| 162 |
+
<span class="text-xs text-slate-400 group-open:rotate-180 transition-transform">▼</span>
|
| 163 |
+
</summary>
|
| 164 |
+
<div class="mt-4 border-t border-slate-200 pt-4 cursor-default flex flex-col gap-4">
|
| 165 |
+
<!-- Word count selector -->
|
| 166 |
<div>
|
| 167 |
+
<label id="uiWordCountLabel" class="block text-xs font-bold text-slate-600 uppercase tracking-wider mb-2">🔢 Vocabulary size to generate:</label>
|
| 168 |
+
<input type="range" id="wordCount" min="1" max="10" value="5" class="w-full accent-indigo-600 h-1 bg-slate-200 rounded-lg cursor-pointer">
|
| 169 |
+
<div class="flex justify-between text-[10px] text-slate-500 mt-1 font-semibold">
|
| 170 |
+
<span>1</span>
|
| 171 |
+
<span id="wordCountVal" class="font-bold text-indigo-600">5 words</span>
|
| 172 |
+
<span>10</span>
|
| 173 |
+
</div>
|
| 174 |
</div>
|
| 175 |
+
<!-- Explanation language selector -->
|
|
|
|
|
|
|
|
|
|
|
|
|
| 176 |
<div>
|
| 177 |
+
<label id="uiExplLangLabel" class="block text-xs font-bold text-slate-600 uppercase tracking-wider mb-2">🗣️ Explanation Language</label>
|
| 178 |
+
<div class="flex flex-col sm:flex-row gap-4 mt-1">
|
| 179 |
+
<label class="inline-flex items-center text-xs text-slate-700 font-semibold cursor-pointer">
|
| 180 |
+
<input type="radio" name="explLang" value="source" checked class="accent-indigo-600 h-4 w-4 mr-2">
|
| 181 |
+
<span id="uiExplSource">App Language</span>
|
| 182 |
+
</label>
|
| 183 |
+
<label class="inline-flex items-center text-xs text-slate-700 font-semibold cursor-pointer">
|
| 184 |
+
<input type="radio" name="explLang" value="target" class="accent-indigo-600 h-4 w-4 mr-2">
|
| 185 |
+
<span id="uiExplTarget">Language to Practice</span>
|
| 186 |
+
</label>
|
| 187 |
+
</div>
|
| 188 |
</div>
|
|
|
|
| 189 |
</div>
|
| 190 |
+
</details>
|
| 191 |
+
|
| 192 |
+
<!-- Language Options (Source is Hidden programmatically) -->
|
| 193 |
+
<div class="flex flex-col sm:flex-row gap-3 mb-5 items-end">
|
| 194 |
+
<div id="srcLangContainer" class="hidden">
|
| 195 |
+
<label id="uiSourceLangLabel" class="block text-[10px] font-bold text-slate-500 uppercase tracking-wider mb-1.5">🌍 Source Language (Native)</label>
|
| 196 |
+
<select id="srcLang" class="w-full p-2 border border-slate-200 rounded-xl text-xs bg-white outline-none">
|
| 197 |
+
<option value="English">English</option>
|
| 198 |
+
<option value="Spanish">Spanish</option>
|
| 199 |
+
<option value="French">French</option>
|
| 200 |
+
<option value="German">German</option>
|
| 201 |
+
<option value="Chinese">Chinese</option>
|
| 202 |
+
<option value="Japanese">Japanese</option>
|
| 203 |
+
<option value="Korean">Korean</option>
|
| 204 |
+
<option value="Russian">Russian</option>
|
| 205 |
+
<option value="Portuguese">Portuguese</option>
|
| 206 |
+
<option value="Italian">Italian</option>
|
| 207 |
+
</select>
|
| 208 |
+
</div>
|
| 209 |
+
<div class="flex-1 w-full">
|
| 210 |
+
<label id="uiTargetLangLabel" class="block text-[10px] font-bold text-slate-600 uppercase tracking-wider mb-1.5">🎯 Target Language to Practice</label>
|
| 211 |
+
<select id="tgtLang" class="w-full p-3 border border-slate-200 rounded-xl text-xs font-semibold bg-slate-50 text-slate-800 outline-none">
|
| 212 |
+
<!-- Filled dynamically based on chosen App Language -->
|
| 213 |
+
</select>
|
| 214 |
+
</div>
|
| 215 |
+
<button id="translateBtn" class="w-full sm:w-auto px-6 py-3 bg-indigo-600 hover:bg-indigo-700 active:translate-y-0.5 text-white font-bold text-xs rounded-xl shadow-xs transition-all">⚡ Generate Vocabulary</button>
|
| 216 |
+
</div>
|
| 217 |
|
| 218 |
+
<!-- Vocabulary Table Scroll Container (Touch/Swipe Optimized for Mobile Devices) -->
|
| 219 |
+
<div class="overflow-x-auto w-full rounded-xl border border-slate-200 shadow-xs" style="-webkit-overflow-scrolling: touch; touch-action: pan-x pan-y;">
|
| 220 |
+
<table class="min-w-full divide-y divide-slate-200 text-xs table-auto">
|
| 221 |
+
<thead class="bg-slate-50">
|
| 222 |
+
<tr>
|
| 223 |
+
<th id="thOriginal" class="px-4 py-3 text-left text-[10px] font-bold text-slate-500 uppercase tracking-wider">Target Word</th>
|
| 224 |
+
<th id="thTranslation" class="px-4 py-3 text-left text-[10px] font-bold text-slate-500 uppercase tracking-wider">Translation</th>
|
| 225 |
+
<th id="thExplanation" class="px-4 py-3 text-left text-[10px] font-bold text-slate-500 uppercase tracking-wider max-w-xs">Contextual Definition</th>
|
| 226 |
+
<th id="thVisual" class="px-4 py-3 text-center text-[10px] font-bold text-slate-500 uppercase tracking-wider w-24">Visual Card</th>
|
| 227 |
+
<th id="thAudio" class="px-4 py-3 text-center text-[10px] font-bold text-slate-500 uppercase tracking-wider w-20">Pronunciation</th>
|
| 228 |
+
</tr>
|
| 229 |
+
</thead>
|
| 230 |
+
<tbody id="resultsBody" class="bg-white divide-y divide-slate-100">
|
| 231 |
+
<tr>
|
| 232 |
+
<td colspan="5" id="tdInitialMsg" class="px-4 py-12 text-center text-xs font-semibold text-slate-400">Configure preferences and generate vocabulary above to populate target dataset.</td>
|
| 233 |
+
</tr>
|
| 234 |
+
</tbody>
|
| 235 |
+
</table>
|
| 236 |
+
</div>
|
| 237 |
+
</div>
|
| 238 |
+
|
| 239 |
+
<!-- TAB 2: Games Hub -->
|
| 240 |
+
<div id="tabGamesContent" class="hidden">
|
| 241 |
+
<!-- Game Selection Panel -->
|
| 242 |
+
<div id="gamesMenu">
|
| 243 |
+
<h3 id="uiGameSelectionTitle" class="text-sm font-bold text-slate-800 mb-4">Select Your Practice Exercise</h3>
|
| 244 |
+
|
| 245 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
| 246 |
+
<!-- Flashcard Memory Match Selection Card -->
|
| 247 |
+
<div class="border border-slate-200 bg-slate-50/40 rounded-xl p-5 hover:shadow-xs transition-all duration-200 flex flex-col justify-between">
|
| 248 |
+
<div>
|
| 249 |
+
<div class="text-2xl mb-2">🧩</div>
|
| 250 |
+
<h4 id="uiGameMemoryTitle" class="text-xs font-bold text-slate-800 mb-1">Interactive Memory Association</h4>
|
| 251 |
+
<p id="uiGameMemoryDesc" class="text-[11px] text-slate-500 font-medium mb-3 leading-relaxed">Match target terms with their translated counterparts and generated visual cards to reinforce semantic retention.</p>
|
| 252 |
+
</div>
|
| 253 |
+
<button id="playMemoryBtn" class="w-full py-2 bg-indigo-600 hover:bg-indigo-700 active:translate-y-0.5 text-white rounded-xl text-[10px] font-semibold transition-all cursor-pointer shadow-xs">Start Practice</button>
|
| 254 |
</div>
|
|
|
|
|
|
|
| 255 |
|
| 256 |
+
<!-- Word Monster Selection Card -->
|
| 257 |
+
<div class="border border-slate-200 bg-slate-50/40 rounded-xl p-5 hover:shadow-xs transition-all duration-200 flex flex-col justify-between">
|
| 258 |
+
<div>
|
| 259 |
+
<div class="text-2xl mb-2">👾</div>
|
| 260 |
+
<h4 id="uiGameMonsterTitle" class="text-xs font-bold text-slate-800 mb-1">Target Word Association (Word Monster)</h4>
|
| 261 |
+
<p id="uiGameMonsterDesc" class="text-[11px] text-slate-500 font-medium mb-3 leading-relaxed">Select and present the appropriate translation card that accurately matches the requested target concept.</p>
|
| 262 |
+
</div>
|
| 263 |
+
<button id="playMonsterBtn" class="w-full py-2 bg-indigo-600 hover:bg-indigo-700 active:translate-y-0.5 text-white rounded-xl text-[10px] font-semibold transition-all cursor-pointer shadow-xs">Start Practice</button>
|
| 264 |
</div>
|
|
|
|
|
|
|
| 265 |
|
| 266 |
+
<!-- Audio-Visual Quiz Selection Card -->
|
| 267 |
+
<div class="border border-slate-200 bg-slate-50/40 rounded-xl p-5 hover:shadow-xs transition-all duration-200 flex flex-col justify-between">
|
| 268 |
+
<div>
|
| 269 |
+
<div class="text-2xl mb-2">🏆</div>
|
| 270 |
+
<h4 id="uiGameQuizTitle" class="text-xs font-bold text-slate-800 mb-1">Acoustic & Visual Assessment</h4>
|
| 271 |
+
<p id="uiGameQuizDesc" class="text-[11px] text-slate-500 font-medium mb-3 leading-relaxed">Examine the audio pronunciation or visual cue and identify the correct matching term from multiple choices.</p>
|
| 272 |
+
</div>
|
| 273 |
+
<button id="playQuizBtn" class="w-full py-2 bg-indigo-600 hover:bg-indigo-700 active:translate-y-0.5 text-white rounded-xl text-[10px] font-semibold transition-all cursor-pointer shadow-xs">Start Practice</button>
|
| 274 |
+
</div>
|
| 275 |
+
|
| 276 |
+
<!-- Scrambled Word Selection Card -->
|
| 277 |
+
<div class="border border-slate-200 bg-slate-50/40 rounded-xl p-5 hover:shadow-xs transition-all duration-200 flex flex-col justify-between">
|
| 278 |
+
<div>
|
| 279 |
+
<div class="text-2xl mb-2">✏️</div>
|
| 280 |
+
<h4 id="uiGameScrambleTitle" class="text-xs font-bold text-slate-800 mb-1">Orthographic Word Reconstruction</h4>
|
| 281 |
+
<p id="uiGameScrambleDesc" class="text-[11px] text-slate-500 font-medium mb-3 leading-relaxed">Reassemble scrambled character sequences into the correct spelling of the translated target term.</p>
|
| 282 |
+
</div>
|
| 283 |
+
<button id="playScrambleBtn" class="w-full py-2 bg-indigo-600 hover:bg-indigo-700 active:translate-y-0.5 text-white rounded-xl text-[10px] font-semibold transition-all cursor-pointer shadow-xs">Start Practice</button>
|
| 284 |
+
</div>
|
| 285 |
+
|
| 286 |
+
<!-- Visual Word Linker Selection Card -->
|
| 287 |
+
<div class="border border-slate-200 bg-slate-50/40 rounded-xl p-5 hover:shadow-xs transition-all duration-200 flex flex-col justify-between">
|
| 288 |
+
<div>
|
| 289 |
+
<div class="text-2xl mb-2">🔗</div>
|
| 290 |
+
<h4 id="uiGameLinkerTitle" class="text-xs font-bold text-slate-800 mb-1">Visual Term Correspondence Mapping</h4>
|
| 291 |
+
<p id="uiGameLinkerDesc" class="text-[11px] text-slate-500 font-medium mb-3 leading-relaxed">Perform bilateral matching between generated visual cues and target practice words to solidify associations.</p>
|
| 292 |
+
</div>
|
| 293 |
+
<button id="playLinkerBtn" class="w-full py-2 bg-indigo-600 hover:bg-indigo-700 active:translate-y-0.5 text-white rounded-xl text-[10px] font-semibold transition-all cursor-pointer shadow-xs">Start Practice</button>
|
| 294 |
</div>
|
|
|
|
| 295 |
</div>
|
| 296 |
</div>
|
|
|
|
| 297 |
|
| 298 |
+
<!-- Active Game Stage -->
|
| 299 |
+
<div id="gameStage" class="hidden border border-slate-200 rounded-xl p-5 bg-white shadow-xs">
|
| 300 |
+
<div class="flex justify-between items-center mb-5 pb-2 border-b border-slate-100">
|
| 301 |
+
<button id="backToMenuBtn" class="text-xs font-bold text-slate-400 hover:text-indigo-600 transition-all outline-none">← Back to Menu</button>
|
| 302 |
+
<div id="gameScoreDisplay" class="text-xs font-bold text-indigo-600 bg-indigo-50 px-4 py-1.5 rounded-full">Score: 0</div>
|
| 303 |
+
</div>
|
| 304 |
+
<div id="gameArea"></div>
|
| 305 |
</div>
|
|
|
|
| 306 |
</div>
|
| 307 |
</div>
|
| 308 |
</div>
|
| 309 |
|
| 310 |
<!-- Modal for full size image preview -->
|
| 311 |
+
<div id="imageModal" class="hidden fixed inset-0 bg-slate-900/60 flex items-center justify-center p-4 z-50 transition-opacity" onclick="closeModal()">
|
| 312 |
+
<div class="bg-white p-6 rounded-2xl shadow-xl max-w-sm w-full border border-slate-200 flex flex-col items-center" onclick="event.stopPropagation()">
|
| 313 |
+
<h3 id="modalTitle" class="text-base font-bold text-slate-900 mb-4 capitalize">Word Card</h3>
|
| 314 |
+
<img id="modalImage" class="w-64 h-64 border border-slate-100 rounded-xl shadow-inner object-contain bg-slate-50" src="" alt="Full preview">
|
| 315 |
+
<button class="mt-5 px-6 py-2 bg-slate-50 hover:bg-slate-100 active:translate-y-0.5 rounded-xl text-xs font-bold text-slate-700 transition-all border border-slate-200" onclick="closeModal()">Close</button>
|
| 316 |
</div>
|
| 317 |
</div>
|
| 318 |
|
|
|
|
| 416 |
|
| 417 |
document.getElementById("uiTitle").innerText = loc.title;
|
| 418 |
document.getElementById("uiSubtitle").innerText = loc.subtitle;
|
| 419 |
+
document.getElementById("uiTutorial").innerHTML = loc.tutorial_text;
|
| 420 |
document.getElementById("uiSettingsTitle").firstElementChild.innerText = loc.settings;
|
| 421 |
document.getElementById("uiWordCountLabel").innerText = loc.word_count_label;
|
| 422 |
document.getElementById("uiExplLangLabel").innerText = loc.expl_lang_label;
|
|
|
|
| 447 |
document.getElementById("uiExplTarget").innerText = loc.target_lang;
|
| 448 |
}
|
| 449 |
|
| 450 |
+
// 3. Tab Switching events with clean layouts
|
| 451 |
tabVocabBtn.addEventListener("click", () => {
|
| 452 |
tabVocabContent.classList.replace("hidden", "block");
|
| 453 |
tabGamesContent.classList.replace("block", "hidden");
|
| 454 |
+
tabVocabBtn.className = "bg-indigo-600 text-white shadow-xs rounded-xl px-4 py-2 font-bold text-xs transition-all outline-none";
|
| 455 |
+
tabGamesBtn.className = "bg-white text-slate-600 hover:bg-slate-50 border border-slate-200 rounded-xl px-4 py-2 font-bold text-xs transition-all outline-none";
|
| 456 |
});
|
| 457 |
|
| 458 |
tabGamesBtn.addEventListener("click", () => {
|
| 459 |
tabVocabContent.classList.replace("block", "hidden");
|
| 460 |
tabGamesContent.classList.replace("hidden", "block");
|
| 461 |
+
tabGamesBtn.className = "bg-indigo-600 text-white shadow-xs rounded-xl px-4 py-2 font-bold text-xs transition-all outline-none";
|
| 462 |
+
tabVocabBtn.className = "bg-white text-slate-600 hover:bg-slate-50 border border-slate-200 rounded-xl px-4 py-2 font-bold text-xs transition-all outline-none";
|
| 463 |
});
|
| 464 |
|
| 465 |
wordCount.addEventListener("input", (e) => {
|
|
|
|
| 479 |
|
| 480 |
resultsBody.innerHTML = `
|
| 481 |
<tr>
|
| 482 |
+
<td colspan="5" class="px-4 py-12 text-center text-xs text-indigo-600 font-semibold">
|
| 483 |
+
<span class="inline-block animate-pulse text-xs mb-1">⚡ Processing Multimodal Vocab Generation...</span>
|
| 484 |
+
<div class="text-[10px] text-slate-400 font-medium">Generating explanations, visual context, and native audio tracks sequentially...</div>
|
| 485 |
</td>
|
| 486 |
</tr>
|
| 487 |
`;
|
|
|
|
| 495 |
window.loadedVocabulary.forEach(row => {
|
| 496 |
const [original, translated, explanation, visualB64, audioB64] = row;
|
| 497 |
const tr = document.createElement("tr");
|
| 498 |
+
tr.className = "hover:bg-slate-50 transition-colors";
|
| 499 |
|
| 500 |
tr.innerHTML = `
|
| 501 |
+
<td class="px-4 py-4 text-slate-800 font-semibold text-xs">${original}</td>
|
| 502 |
+
<td class="px-4 py-4 text-indigo-600 font-bold text-sm">${translated}</td>
|
| 503 |
+
<td class="px-4 py-4 text-slate-500 text-[11px] font-medium leading-relaxed max-w-xs break-words">${explanation}</td>
|
| 504 |
+
<td class="px-4 py-4 text-center">
|
| 505 |
${visualB64 ? `
|
| 506 |
<div class="relative inline-block group">
|
| 507 |
<img src="data:image/png;base64,${visualB64}"
|
| 508 |
+
class="w-10 h-10 mx-auto rounded-lg border border-slate-200 shadow-xs hover:scale-105 active:scale-95 cursor-pointer transition-transform duration-150 object-cover"
|
| 509 |
alt="${translated}"
|
| 510 |
data-src="data:image/png;base64,${visualB64}"
|
| 511 |
data-word="${translated}">
|
| 512 |
</div>
|
| 513 |
` : '-'}
|
| 514 |
</td>
|
| 515 |
+
<td class="px-4 py-4 text-center">
|
| 516 |
+
<button class="play-preload-btn w-8 h-8 bg-slate-50 hover:bg-indigo-50 hover:text-indigo-600 border border-slate-200 active:translate-y-0.5 rounded-full flex items-center justify-center text-xs transition-transform duration-100"
|
| 517 |
data-audio="data:audio/mp3;base64,${audioB64}">
|
| 518 |
🔊
|
| 519 |
</button>
|
|
|
|
| 545 |
} catch (error) {
|
| 546 |
resultsBody.innerHTML = `
|
| 547 |
<tr>
|
| 548 |
+
<td colspan="5" class="px-4 py-8 text-center text-xs text-red-500 font-medium">
|
| 549 |
+
Error generating vocabulary: ${error.message}
|
| 550 |
</td>
|
| 551 |
</tr>
|
| 552 |
`;
|