Spaces:
Running
Running
add a remove button in top right of each list button - Follow Up Deployment
Browse files- index.html +153 -247
- prompts.txt +21 -1
index.html
CHANGED
|
@@ -12,7 +12,7 @@
|
|
| 12 |
<style>
|
| 13 |
.flashcard {
|
| 14 |
perspective: 1000px;
|
| 15 |
-
min-height:
|
| 16 |
}
|
| 17 |
.flashcard-inner {
|
| 18 |
transition: transform 0.6s;
|
|
@@ -85,7 +85,7 @@
|
|
| 85 |
</div>
|
| 86 |
</div>
|
| 87 |
|
| 88 |
-
<div id="flashcards-container" class="grid grid-cols-
|
| 89 |
<!-- Flashcards will be inserted here by JavaScript -->
|
| 90 |
</div>
|
| 91 |
</div>
|
|
@@ -95,215 +95,8 @@
|
|
| 95 |
feather.replace();
|
| 96 |
AOS.init();
|
| 97 |
|
| 98 |
-
//
|
| 99 |
-
|
| 100 |
-
{ german: "Bad", english: "bath", mastered: false },
|
| 101 |
-
{ german: "Sofa", english: "sofa", mastered: false },
|
| 102 |
-
{ german: "mal", english: "time / once", mastered: false },
|
| 103 |
-
{ german: "Banane", english: "banana", mastered: false },
|
| 104 |
-
{ german: "nach", english: "after / to / toward", mastered: false },
|
| 105 |
-
{ german: "Zahn", english: "tooth", mastered: false },
|
| 106 |
-
{ german: "Bahn", english: "train / railway", mastered: false },
|
| 107 |
-
{ german: "Aal", english: "eel", mastered: false },
|
| 108 |
-
{ german: "Staat", english: "state / country", mastered: false },
|
| 109 |
-
{ german: "man", english: "one / people (impersonal pronoun)", mastered: false },
|
| 110 |
-
{ german: "Mann", english: "man", mastered: false },
|
| 111 |
-
{ german: "Ball", english: "ball", mastered: false },
|
| 112 |
-
{ german: "Apfel", english: "apple", mastered: false },
|
| 113 |
-
{ german: "lesen", english: "to read", mastered: false },
|
| 114 |
-
{ german: "eben", english: "just / exactly", mastered: false },
|
| 115 |
-
{ german: "leben", english: "to live", mastered: false },
|
| 116 |
-
{ german: "zehn", english: "ten", mastered: false },
|
| 117 |
-
{ german: "sehen", english: "to see", mastered: false },
|
| 118 |
-
{ german: "See", english: "lake / sea (context dependent)", mastered: false },
|
| 119 |
-
{ german: "Tee", english: "tea", mastered: false },
|
| 120 |
-
{ german: "Hemd", english: "shirt", mastered: false },
|
| 121 |
-
{ german: "hell", english: "bright / light", mastered: false },
|
| 122 |
-
{ german: "Küche", english: "kitchen", mastered: false },
|
| 123 |
-
{ german: "Garage", english: "garage", mastered: false },
|
| 124 |
-
{ german: "bitte", english: "please", mastered: false },
|
| 125 |
-
{ german: "Treppe", english: "stairs", mastered: false },
|
| 126 |
-
{ german: "gefallen", english: "to please / to like", mastered: false },
|
| 127 |
-
{ german: "bekommen", english: "to get / to receive", mastered: false },
|
| 128 |
-
{ german: "erzählen", english: "to tell / to narrate", mastered: false },
|
| 129 |
-
{ german: "er", english: "he", mastered: false },
|
| 130 |
-
{ german: "Meer", english: "sea", mastered: false },
|
| 131 |
-
{ german: "sehr", english: "very", mastered: false },
|
| 132 |
-
{ german: "schwer", english: "heavy / difficult", mastered: false },
|
| 133 |
-
{ german: "Löcher", english: "holes", mastered: false },
|
| 134 |
-
{ german: "Bücher", english: "books", mastered: false },
|
| 135 |
-
{ german: "haben", english: "to have", mastered: false },
|
| 136 |
-
{ german: "wir", english: "we", mastered: false },
|
| 137 |
-
{ german: "Igel", english: "hedgehog", mastered: false },
|
| 138 |
-
{ german: "ihr", english: "you (plural) / her", mastered: false },
|
| 139 |
-
{ german: "ihm", english: "him", mastered: false },
|
| 140 |
-
{ german: "Sie", english: "you (formal) / she (capitalized = You)", mastered: false },
|
| 141 |
-
{ german: "sieben", english: "seven", mastered: false },
|
| 142 |
-
{ german: "sieht", english: "sees", mastered: false },
|
| 143 |
-
{ german: "Brille", english: "glasses", mastered: false },
|
| 144 |
-
{ german: "holen", english: "to fetch / get", mastered: false },
|
| 145 |
-
{ german: "bogen", english: "bow / curve / arch", mastered: false },
|
| 146 |
-
{ german: "Obst", english: "fruit", mastered: false },
|
| 147 |
-
{ german: "Ohr", english: "ear", mastered: false },
|
| 148 |
-
{ german: "Bohne", english: "bean", mastered: false },
|
| 149 |
-
{ german: "Boot", english: "boat", mastered: false },
|
| 150 |
-
{ german: "doch", english: "but / however / indeed", mastered: false },
|
| 151 |
-
{ german: "Loch", english: "hole", mastered: false },
|
| 152 |
-
{ german: "kommen", english: "to come", mastered: false },
|
| 153 |
-
{ german: "noch", english: "still / yet", mastered: false },
|
| 154 |
-
{ german: "Mond", english: "moon", mastered: false },
|
| 155 |
-
{ german: "Schule", english: "school", mastered: false },
|
| 156 |
-
{ german: "Ufer", english: "shore / bank", mastered: false },
|
| 157 |
-
{ german: "Buch", english: "book", mastered: false },
|
| 158 |
-
{ german: "Stuhl", english: "chair", mastered: false },
|
| 159 |
-
{ german: "Schuhe", english: "shoes", mastered: false },
|
| 160 |
-
{ german: "Mutter", english: "mother", mastered: false },
|
| 161 |
-
{ german: "Mund", english: "mouth", mastered: false },
|
| 162 |
-
{ german: "Käse", english: "cheese", mastered: false },
|
| 163 |
-
{ german: "Täter", english: "perpetrator / offender", mastered: false },
|
| 164 |
-
{ german: "Zähne", english: "teeth", mastered: false },
|
| 165 |
-
{ german: "Äpfel", english: "apples", mastered: false },
|
| 166 |
-
{ german: "Männer", english: "men", mastered: false },
|
| 167 |
-
{ german: "Löwe", english: "lion", mastered: false },
|
| 168 |
-
{ german: "hören", english: "to hear", mastered: false },
|
| 169 |
-
{ german: "Böse", english: "evil / bad", mastered: false },
|
| 170 |
-
{ german: "Söhne", english: "sons", mastered: false },
|
| 171 |
-
{ german: "Löhne", english: "wages", mastered: false },
|
| 172 |
-
{ german: "völlig", english: "completely", mastered: false },
|
| 173 |
-
{ german: "zwölf", english: "twelve", mastered: false },
|
| 174 |
-
{ german: "Schüler", english: "student / pupil", mastered: false },
|
| 175 |
-
{ german: "Übung", english: "exercise / practice", mastered: false },
|
| 176 |
-
{ german: "Mühe", english: "effort / trouble", mastered: false },
|
| 177 |
-
{ german: "Stühle", english: "chairs", mastered: false },
|
| 178 |
-
{ german: "Glück", english: "luck / happiness", mastered: false },
|
| 179 |
-
{ german: "fünf", english: "five", mastered: false },
|
| 180 |
-
{ german: "Ei", english: "egg", mastered: false },
|
| 181 |
-
{ german: "eins", english: "one", mastered: false },
|
| 182 |
-
{ german: "Mai", english: "May", mastered: false },
|
| 183 |
-
{ german: "Waise", english: "orphan", mastered: false },
|
| 184 |
-
{ german: "Mayer / Meyer / Bayer", english: "surnames", mastered: false },
|
| 185 |
-
{ german: "Auge", english: "eye", mastered: false },
|
| 186 |
-
{ german: "Auto", english: "car", mastered: false },
|
| 187 |
-
{ german: "Bauch", english: "belly / stomach", mastered: false },
|
| 188 |
-
{ german: "Kakao", english: "cocoa", mastered: false },
|
| 189 |
-
{ german: "Leute", english: "people", mastered: false },
|
| 190 |
-
{ german: "neun", english: "nine", mastered: false },
|
| 191 |
-
{ german: "heute", english: "today", mastered: false },
|
| 192 |
-
{ german: "Bäuche", english: "bellies", mastered: false },
|
| 193 |
-
{ german: "Häuser", english: "houses", mastered: false },
|
| 194 |
-
{ german: "Mäuse", english: "mice", mastered: false },
|
| 195 |
-
{ german: "Ball", english: "ball", mastered: false },
|
| 196 |
-
{ german: "Berlin", english: "Berlin", mastered: false },
|
| 197 |
-
{ german: "Hobby", english: "hobby", mastered: false },
|
| 198 |
-
{ german: "abfahren", english: "to depart", mastered: false },
|
| 199 |
-
{ german: "Urlaub", english: "vacation / holiday", mastered: false },
|
| 200 |
-
{ german: "Cent", english: "cent", mastered: false },
|
| 201 |
-
{ german: "circa", english: "approximately", mastered: false },
|
| 202 |
-
{ german: "Café", english: "café", mastered: false },
|
| 203 |
-
{ german: "Computer", english: "computer", mastered: false },
|
| 204 |
-
{ german: "Clown", english: "clown", mastered: false },
|
| 205 |
-
{ german: "du", english: "you (informal singular)", mastered: false },
|
| 206 |
-
{ german: "danke", english: "thanks", mastered: false },
|
| 207 |
-
{ german: "Ende", english: "end", mastered: false },
|
| 208 |
-
{ german: "addieren", english: "to add (math)", mastered: false },
|
| 209 |
-
{ german: "und", english: "and", mastered: false },
|
| 210 |
-
{ german: "Hund", english: "dog", mastered: false },
|
| 211 |
-
{ german: "Frau", english: "woman / Mrs.", mastered: false },
|
| 212 |
-
{ german: "Fisch", english: "fish", mastered: false },
|
| 213 |
-
{ german: "Hof", english: "courtyard / yard / farm", mastered: false },
|
| 214 |
-
{ german: "Schiff", english: "ship", mastered: false },
|
| 215 |
-
{ german: "gut", english: "good", mastered: false },
|
| 216 |
-
{ german: "Giraffe", english: "giraffe", mastered: false },
|
| 217 |
-
{ german: "Bagger", english: "excavator / digger", mastered: false },
|
| 218 |
-
{ german: "montags", english: "on Mondays", mastered: false },
|
| 219 |
-
{ german: "Tag", english: "day", mastered: false },
|
| 220 |
-
{ german: "Weg", english: "way / path", mastered: false },
|
| 221 |
-
{ german: "haben", english: "to have", mastered: false },
|
| 222 |
-
{ german: "Hallo", english: "hello", mastered: false },
|
| 223 |
-
{ german: "abholen", english: "to pick up", mastered: false },
|
| 224 |
-
{ german: "Föhn", english: "hairdryer / foehn wind", mastered: false },
|
| 225 |
-
{ german: "ja", english: "yes", mastered: false },
|
| 226 |
-
{ german: "Jacke", english: "jacket", mastered: false },
|
| 227 |
-
{ german: "Journal", english: "journal", mastered: false },
|
| 228 |
-
{ german: "joggen", english: "to jog", mastered: false },
|
| 229 |
-
{ german: "Katze", english: "cat", mastered: false },
|
| 230 |
-
{ german: "Ecke", english: "corner", mastered: false },
|
| 231 |
-
{ german: "Akkusativ", english: "accusative (grammar)", mastered: false },
|
| 232 |
-
{ german: "wollen", english: "to want", mastered: false },
|
| 233 |
-
{ german: "Lampe", english: "lampe", mastered: false },
|
| 234 |
-
{ german: "kommen", english: "to come", mastered: false },
|
| 235 |
-
{ german: "können", english: "can / to be able to", mastered: false },
|
| 236 |
-
{ german: "nein", english: "no", mastered: false },
|
| 237 |
-
{ german: "Papier", english: "paper", mastered: false },
|
| 238 |
-
{ german: "Gruppe", english: "group", mastered: false },
|
| 239 |
-
{ german: "rot", english: "red", mastered: false },
|
| 240 |
-
{ german: "Rhein", english: "Rhine (river)", mastered: false },
|
| 241 |
-
{ german: "Morgen", english: "morning / tomorrow", mastered: false },
|
| 242 |
-
{ german: "Berlin", english: "Berlin", mastered: false },
|
| 243 |
-
{ german: "mir", english: "me", mastered: false },
|
| 244 |
-
{ german: "so", english: "so / like that", mastered: false },
|
| 245 |
-
{ german: "Haus", english: "house", mastered: false },
|
| 246 |
-
{ german: "Post", english: "post / mail", mastered: false },
|
| 247 |
-
{ german: "müssen", english: "must / have to", mastered: false },
|
| 248 |
-
{ german: "Tante", english: "aunt", mastered: false },
|
| 249 |
-
{ german: "Theater", english: "theater", mastered: false },
|
| 250 |
-
{ german: "Stadt", english: "city / town", mastered: false },
|
| 251 |
-
{ german: "vier", english: "four", mastered: false },
|
| 252 |
-
{ german: "Vater", english: "father", mastered: false },
|
| 253 |
-
{ german: "Vogel", english: "bird", mastered: false },
|
| 254 |
-
{ german: "Vase", english: "vase", mastered: false },
|
| 255 |
-
{ german: "Kurve", english: "curve", mastered: false },
|
| 256 |
-
{ german: "Wagen", english: "car / vehicle", mastered: false },
|
| 257 |
-
{ german: "Xylophon", english: "xylophone", mastered: false },
|
| 258 |
-
{ german: "Typ", english: "type / guy", mastered: false },
|
| 259 |
-
{ german: "Ägypten", english: "Egypt", mastered: false },
|
| 260 |
-
{ german: "Yacht", english: "yacht", mastered: false },
|
| 261 |
-
{ german: "Yak", english: "yak", mastered: false },
|
| 262 |
-
{ german: "Zug", english: "train", mastered: false },
|
| 263 |
-
{ german: "Skizze", english: "sketch", mastered: false },
|
| 264 |
-
{ german: "Satz", english: "sentence", mastered: false },
|
| 265 |
-
{ german: "rechts", english: "right", mastered: false },
|
| 266 |
-
{ german: "Straße", english: "street", mastered: false },
|
| 267 |
-
{ german: "Fuß", english: "foot", mastered: false },
|
| 268 |
-
{ german: "heißen", english: "to be called / to mean", mastered: false },
|
| 269 |
-
{ german: "nach", english: "after / to", mastered: false },
|
| 270 |
-
{ german: "acht", english: "eight", mastered: false },
|
| 271 |
-
{ german: "Nacht", english: "night", mastered: false },
|
| 272 |
-
{ german: "Nächte", english: "nights", mastered: false },
|
| 273 |
-
{ german: "Milch", english: "milk", mastered: false },
|
| 274 |
-
{ german: "China", english: "China", mastered: false },
|
| 275 |
-
{ german: "Sachsen", english: "Saxony", mastered: false },
|
| 276 |
-
{ german: "nächst", english: "next", mastered: false },
|
| 277 |
-
{ german: "sechs", english: "six", mastered: false },
|
| 278 |
-
{ german: "machst", english: "(you) do / make", mastered: false },
|
| 279 |
-
{ german: "machen", english: "to do / to make", mastered: false },
|
| 280 |
-
{ german: "Charakter", english: "character", mastered: false },
|
| 281 |
-
{ german: "Chor", english: "choir", mastered: false },
|
| 282 |
-
{ german: "Chance", english: "chance", mastered: false },
|
| 283 |
-
{ german: "Chef", english: "boss", mastered: false },
|
| 284 |
-
{ german: "Chile", english: "Chile", mastered: false },
|
| 285 |
-
{ german: "Checkliste", english: "checklist", mastered: false },
|
| 286 |
-
{ german: "Stadtbewohner", english: "city dweller", mastered: false },
|
| 287 |
-
{ german: "Königreich", english: "kingdom", mastered: false },
|
| 288 |
-
{ german: "König", english: "king", mastered: false },
|
| 289 |
-
{ german: "Honig", english: "honey", mastered: false },
|
| 290 |
-
{ german: "Photo", english: "photo", mastered: false },
|
| 291 |
-
{ german: "Qualle", english: "jellyfish", mastered: false },
|
| 292 |
-
{ german: "Quelle", english: "source / spring", mastered: false },
|
| 293 |
-
{ german: "Quatsch", english: "nonsense", mastered: false },
|
| 294 |
-
{ german: "schreiben", english: "to write", mastered: false },
|
| 295 |
-
{ german: "Großstadt", english: "big city", mastered: false },
|
| 296 |
-
{ german: "Radsport", english: "cycling", mastered: false },
|
| 297 |
-
{ german: "später", english: "later", mastered: false },
|
| 298 |
-
{ german: "Sport", english: "sport", mastered: false },
|
| 299 |
-
{ german: "Pferd", english: "horse", mastered: false },
|
| 300 |
-
{ german: "Nation", english: "nation", mastered: false },
|
| 301 |
-
{ german: "Situation", english: "situation", mastered: false }
|
| 302 |
-
];
|
| 303 |
-
|
| 304 |
-
// Get flashcards from localStorage or use initial data
|
| 305 |
-
let flashcards = JSON.parse(localStorage.getItem('germanFlashcards')) || initialFlashcards;
|
| 306 |
-
let customFlashcards = JSON.parse(localStorage.getItem('customGermanFlashcards')) || [];
|
| 307 |
|
| 308 |
// Render flashcards
|
| 309 |
function renderFlashcards() {
|
|
@@ -317,18 +110,18 @@
|
|
| 317 |
|
| 318 |
flashcard.innerHTML = `
|
| 319 |
<div class="flashcard-inner h-full">
|
| 320 |
-
<div class="flashcard-front bg-white rounded-lg shadow-md p-
|
| 321 |
-
<h3 class="text-
|
| 322 |
-
<button class="speak-btn mt-
|
| 323 |
-
<i data-feather="volume-2" class="text-indigo-700"></i>
|
| 324 |
</button>
|
| 325 |
-
<p class="text-gray-500 mt-
|
| 326 |
-
${card.mastered ? '<i data-feather="check-circle" class="text-green-500 mt-
|
| 327 |
</div>
|
| 328 |
-
<div class="flashcard-back bg-indigo-100 rounded-lg shadow-md p-
|
| 329 |
-
<h3 class="text-
|
| 330 |
-
<div class="mt-
|
| 331 |
-
<button class="master-btn px-
|
| 332 |
${card.mastered ? 'Mastered' : 'Mark as Mastered'}
|
| 333 |
</button>
|
| 334 |
</div>
|
|
@@ -384,12 +177,19 @@
|
|
| 384 |
renderFlashcards();
|
| 385 |
});
|
| 386 |
|
| 387 |
-
// Reset progress
|
| 388 |
document.getElementById('reset-btn').addEventListener('click', function() {
|
| 389 |
-
if (confirm('Are you sure you want to reset
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 393 |
}
|
| 394 |
});
|
| 395 |
|
|
@@ -405,11 +205,7 @@
|
|
| 405 |
|
| 406 |
|
| 407 |
// Object to store all custom lists
|
| 408 |
-
let customLists = JSON.parse(localStorage.getItem('customGermanLists'));
|
| 409 |
-
if (!customLists) {
|
| 410 |
-
customLists = {};
|
| 411 |
-
localStorage.setItem('customGermanLists', JSON.stringify(customLists));
|
| 412 |
-
}
|
| 413 |
|
| 414 |
// Render custom list buttons
|
| 415 |
function renderCustomListButtons() {
|
|
@@ -417,6 +213,9 @@
|
|
| 417 |
container.innerHTML = '';
|
| 418 |
|
| 419 |
Object.keys(customLists).forEach(listName => {
|
|
|
|
|
|
|
|
|
|
| 420 |
const button = document.createElement('button');
|
| 421 |
button.className = 'px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition flex items-center';
|
| 422 |
button.innerHTML = `<i data-feather="list" class="mr-2"></i> ${listName}`;
|
|
@@ -424,16 +223,45 @@
|
|
| 424 |
flashcards = [...customLists[listName]];
|
| 425 |
renderFlashcards();
|
| 426 |
});
|
| 427 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 428 |
});
|
| 429 |
|
| 430 |
feather.replace();
|
| 431 |
}
|
| 432 |
|
| 433 |
-
// Show original flashcards
|
| 434 |
document.getElementById('show-original-btn').addEventListener('click', function() {
|
| 435 |
-
|
| 436 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 437 |
});
|
| 438 |
|
| 439 |
// Parse and add new flashcards
|
|
@@ -441,12 +269,8 @@
|
|
| 441 |
const input = document.getElementById('new-flashcards-input').value.trim();
|
| 442 |
const listName = document.getElementById('list-name-input').value.trim();
|
| 443 |
|
| 444 |
-
if (!input) {
|
| 445 |
-
alert('Please enter flashcards!');
|
| 446 |
-
return;
|
| 447 |
-
}
|
| 448 |
-
if (!listName) {
|
| 449 |
-
alert('Please enter a list name!');
|
| 450 |
return;
|
| 451 |
}
|
| 452 |
|
|
@@ -465,11 +289,6 @@
|
|
| 465 |
});
|
| 466 |
|
| 467 |
if (newCards.length > 0) {
|
| 468 |
-
// Initialize customLists if it doesn't exist
|
| 469 |
-
if (!customLists) {
|
| 470 |
-
customLists = {};
|
| 471 |
-
}
|
| 472 |
-
|
| 473 |
// Add or update the custom list
|
| 474 |
customLists[listName] = newCards;
|
| 475 |
localStorage.setItem('customGermanLists', JSON.stringify(customLists));
|
|
@@ -498,6 +317,12 @@
|
|
| 498 |
|
| 499 |
// Show only not mastered words
|
| 500 |
document.getElementById('show-not-mastered-btn').addEventListener('click', function() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 501 |
const notMastered = flashcards.filter(card => !card.mastered);
|
| 502 |
if (notMastered.length === 0) {
|
| 503 |
alert('All words in this list are mastered!');
|
|
@@ -516,7 +341,88 @@
|
|
| 516 |
}
|
| 517 |
});
|
| 518 |
|
| 519 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 520 |
renderFlashcards();
|
| 521 |
renderCustomListButtons();
|
| 522 |
});
|
|
|
|
| 12 |
<style>
|
| 13 |
.flashcard {
|
| 14 |
perspective: 1000px;
|
| 15 |
+
min-height: 80px;
|
| 16 |
}
|
| 17 |
.flashcard-inner {
|
| 18 |
transition: transform 0.6s;
|
|
|
|
| 85 |
</div>
|
| 86 |
</div>
|
| 87 |
|
| 88 |
+
<div id="flashcards-container" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4">
|
| 89 |
<!-- Flashcards will be inserted here by JavaScript -->
|
| 90 |
</div>
|
| 91 |
</div>
|
|
|
|
| 95 |
feather.replace();
|
| 96 |
AOS.init();
|
| 97 |
|
| 98 |
+
// Get flashcards from localStorage
|
| 99 |
+
let flashcards = JSON.parse(localStorage.getItem('germanFlashcards')) || [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
|
| 101 |
// Render flashcards
|
| 102 |
function renderFlashcards() {
|
|
|
|
| 110 |
|
| 111 |
flashcard.innerHTML = `
|
| 112 |
<div class="flashcard-inner h-full">
|
| 113 |
+
<div class="flashcard-front bg-white rounded-lg shadow-md p-3 flex flex-col items-center justify-center cursor-pointer h-full">
|
| 114 |
+
<h3 class="text-sm font-bold text-center text-indigo-700">${card.german}</h3>
|
| 115 |
+
<button class="speak-btn mt-1 p-1 bg-indigo-100 rounded-full hover:bg-indigo-200 transition">
|
| 116 |
+
<i data-feather="volume-2" class="text-indigo-700 w-3 h-3"></i>
|
| 117 |
</button>
|
| 118 |
+
<p class="text-xs text-gray-500 mt-1">Click to flip</p>
|
| 119 |
+
${card.mastered ? '<i data-feather="check-circle" class="text-green-500 mt-1 w-3 h-3"></i>' : ''}
|
| 120 |
</div>
|
| 121 |
+
<div class="flashcard-back bg-indigo-100 rounded-lg shadow-md p-2 flex flex-col items-center justify-center cursor-pointer h-full">
|
| 122 |
+
<h3 class="text-xs font-semibold text-center text-gray-800">${card.english}</h3>
|
| 123 |
+
<div class="mt-1 flex space-x-1">
|
| 124 |
+
<button class="master-btn px-2 py-0.5 ${card.mastered ? 'bg-green-500' : 'bg-gray-500'} text-white rounded text-xs">
|
| 125 |
${card.mastered ? 'Mastered' : 'Mark as Mastered'}
|
| 126 |
</button>
|
| 127 |
</div>
|
|
|
|
| 177 |
renderFlashcards();
|
| 178 |
});
|
| 179 |
|
| 180 |
+
// Reset progress of current list
|
| 181 |
document.getElementById('reset-btn').addEventListener('click', function() {
|
| 182 |
+
if (confirm('Are you sure you want to reset progress for the current list?')) {
|
| 183 |
+
const currentListName = getCurrentListName();
|
| 184 |
+
if (currentListName && customLists[currentListName]) {
|
| 185 |
+
// Reset all cards in the current list to not mastered
|
| 186 |
+
customLists[currentListName] = customLists[currentListName].map(card => ({ ...card, mastered: false }));
|
| 187 |
+
localStorage.setItem('customGermanLists', JSON.stringify(customLists));
|
| 188 |
+
// Reload the current list
|
| 189 |
+
flashcards = [...customLists[currentListName]];
|
| 190 |
+
saveFlashcards();
|
| 191 |
+
renderFlashcards();
|
| 192 |
+
}
|
| 193 |
}
|
| 194 |
});
|
| 195 |
|
|
|
|
| 205 |
|
| 206 |
|
| 207 |
// Object to store all custom lists
|
| 208 |
+
let customLists = JSON.parse(localStorage.getItem('customGermanLists')) || {};
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
|
| 210 |
// Render custom list buttons
|
| 211 |
function renderCustomListButtons() {
|
|
|
|
| 213 |
container.innerHTML = '';
|
| 214 |
|
| 215 |
Object.keys(customLists).forEach(listName => {
|
| 216 |
+
const buttonContainer = document.createElement('div');
|
| 217 |
+
buttonContainer.className = 'relative group';
|
| 218 |
+
|
| 219 |
const button = document.createElement('button');
|
| 220 |
button.className = 'px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition flex items-center';
|
| 221 |
button.innerHTML = `<i data-feather="list" class="mr-2"></i> ${listName}`;
|
|
|
|
| 223 |
flashcards = [...customLists[listName]];
|
| 224 |
renderFlashcards();
|
| 225 |
});
|
| 226 |
+
|
| 227 |
+
const removeBtn = document.createElement('button');
|
| 228 |
+
removeBtn.className = 'absolute -top-2 -right-2 bg-red-500 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs opacity-0 group-hover:opacity-100 transition-opacity hover:bg-red-600';
|
| 229 |
+
removeBtn.innerHTML = '×';
|
| 230 |
+
removeBtn.addEventListener('click', (e) => {
|
| 231 |
+
e.stopPropagation();
|
| 232 |
+
if (confirm(`Are you sure you want to delete the "${listName}" list?`)) {
|
| 233 |
+
delete customLists[listName];
|
| 234 |
+
localStorage.setItem('customGermanLists', JSON.stringify(customLists));
|
| 235 |
+
renderCustomListButtons();
|
| 236 |
+
// If we deleted the current list, load the first available list
|
| 237 |
+
const listNames = Object.keys(customLists);
|
| 238 |
+
if (listNames.length > 0) {
|
| 239 |
+
flashcards = [...customLists[listNames[0]]];
|
| 240 |
+
renderFlashcards();
|
| 241 |
+
} else {
|
| 242 |
+
flashcards = [];
|
| 243 |
+
renderFlashcards();
|
| 244 |
+
}
|
| 245 |
+
}
|
| 246 |
+
});
|
| 247 |
+
|
| 248 |
+
buttonContainer.appendChild(button);
|
| 249 |
+
buttonContainer.appendChild(removeBtn);
|
| 250 |
+
container.appendChild(buttonContainer);
|
| 251 |
});
|
| 252 |
|
| 253 |
feather.replace();
|
| 254 |
}
|
| 255 |
|
| 256 |
+
// Show original flashcards (first custom list or empty)
|
| 257 |
document.getElementById('show-original-btn').addEventListener('click', function() {
|
| 258 |
+
const listNames = Object.keys(customLists);
|
| 259 |
+
if (listNames.length > 0) {
|
| 260 |
+
flashcards = [...customLists[listNames[0]]];
|
| 261 |
+
renderFlashcards();
|
| 262 |
+
} else {
|
| 263 |
+
alert('No custom lists available. Please create a list first.');
|
| 264 |
+
}
|
| 265 |
});
|
| 266 |
|
| 267 |
// Parse and add new flashcards
|
|
|
|
| 269 |
const input = document.getElementById('new-flashcards-input').value.trim();
|
| 270 |
const listName = document.getElementById('list-name-input').value.trim();
|
| 271 |
|
| 272 |
+
if (!input || !listName) {
|
| 273 |
+
alert('Please enter both a list name and flashcards!');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
return;
|
| 275 |
}
|
| 276 |
|
|
|
|
| 289 |
});
|
| 290 |
|
| 291 |
if (newCards.length > 0) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 292 |
// Add or update the custom list
|
| 293 |
customLists[listName] = newCards;
|
| 294 |
localStorage.setItem('customGermanLists', JSON.stringify(customLists));
|
|
|
|
| 317 |
|
| 318 |
// Show only not mastered words
|
| 319 |
document.getElementById('show-not-mastered-btn').addEventListener('click', function() {
|
| 320 |
+
// Reload current list from localStorage to get latest mastered status
|
| 321 |
+
const currentListName = getCurrentListName();
|
| 322 |
+
if (currentListName && customLists[currentListName]) {
|
| 323 |
+
flashcards = [...customLists[currentListName]];
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
const notMastered = flashcards.filter(card => !card.mastered);
|
| 327 |
if (notMastered.length === 0) {
|
| 328 |
alert('All words in this list are mastered!');
|
|
|
|
| 341 |
}
|
| 342 |
});
|
| 343 |
|
| 344 |
+
// Helper function to get current list name
|
| 345 |
+
function getCurrentListName() {
|
| 346 |
+
const currentFlashcards = JSON.stringify(flashcards);
|
| 347 |
+
for (const [listName, listCards] of Object.entries(customLists)) {
|
| 348 |
+
if (JSON.stringify(listCards) === currentFlashcards) {
|
| 349 |
+
return listName;
|
| 350 |
+
}
|
| 351 |
+
}
|
| 352 |
+
return null;
|
| 353 |
+
}
|
| 354 |
+
|
| 355 |
+
// Update custom list when flashcards change
|
| 356 |
+
function updateCurrentCustomList() {
|
| 357 |
+
const currentListName = getCurrentListName();
|
| 358 |
+
if (currentListName && customLists[currentListName]) {
|
| 359 |
+
customLists[currentListName] = [...flashcards];
|
| 360 |
+
localStorage.setItem('customGermanLists', JSON.stringify(customLists));
|
| 361 |
+
}
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
// Modified saveFlashcards to also update custom lists
|
| 365 |
+
function saveFlashcards() {
|
| 366 |
+
localStorage.setItem('germanFlashcards', JSON.stringify(flashcards));
|
| 367 |
+
updateCurrentCustomList();
|
| 368 |
+
}
|
| 369 |
+
|
| 370 |
+
// Modified renderCustomListButtons to load from current list when clicked
|
| 371 |
+
function renderCustomListButtons() {
|
| 372 |
+
const container = document.getElementById('custom-lists-container');
|
| 373 |
+
container.innerHTML = '';
|
| 374 |
+
|
| 375 |
+
Object.keys(customLists).forEach(listName => {
|
| 376 |
+
const buttonContainer = document.createElement('div');
|
| 377 |
+
buttonContainer.className = 'relative group';
|
| 378 |
+
|
| 379 |
+
const button = document.createElement('button');
|
| 380 |
+
button.className = 'px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition flex items-center';
|
| 381 |
+
button.innerHTML = `<i data-feather="list" class="mr-2"></i> ${listName}`;
|
| 382 |
+
button.addEventListener('click', () => {
|
| 383 |
+
// Load the full list from localStorage (including mastered status)
|
| 384 |
+
flashcards = [...customLists[listName]];
|
| 385 |
+
saveFlashcards();
|
| 386 |
+
renderFlashcards();
|
| 387 |
+
});
|
| 388 |
+
|
| 389 |
+
const removeBtn = document.createElement('button');
|
| 390 |
+
removeBtn.className = 'absolute -top-2 -right-2 bg-red-500 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs opacity-0 group-hover:opacity-100 transition-opacity hover:bg-red-600';
|
| 391 |
+
removeBtn.innerHTML = '×';
|
| 392 |
+
removeBtn.addEventListener('click', (e) => {
|
| 393 |
+
e.stopPropagation();
|
| 394 |
+
if (confirm(`Are you sure you want to delete the "${listName}" list?`)) {
|
| 395 |
+
delete customLists[listName];
|
| 396 |
+
localStorage.setItem('customGermanLists', JSON.stringify(customLists));
|
| 397 |
+
renderCustomListButtons();
|
| 398 |
+
// If we deleted the current list, load the first available list
|
| 399 |
+
const listNames = Object.keys(customLists);
|
| 400 |
+
if (listNames.length > 0) {
|
| 401 |
+
flashcards = [...customLists[listNames[0]]];
|
| 402 |
+
saveFlashcards();
|
| 403 |
+
renderFlashcards();
|
| 404 |
+
} else {
|
| 405 |
+
flashcards = [];
|
| 406 |
+
saveFlashcards();
|
| 407 |
+
renderFlashcards();
|
| 408 |
+
}
|
| 409 |
+
}
|
| 410 |
+
});
|
| 411 |
+
|
| 412 |
+
buttonContainer.appendChild(button);
|
| 413 |
+
buttonContainer.appendChild(removeBtn);
|
| 414 |
+
container.appendChild(buttonContainer);
|
| 415 |
+
});
|
| 416 |
+
|
| 417 |
+
feather.replace();
|
| 418 |
+
}
|
| 419 |
+
|
| 420 |
+
// Initial render - load first custom list if available
|
| 421 |
+
const listNames = Object.keys(customLists);
|
| 422 |
+
if (listNames.length > 0) {
|
| 423 |
+
flashcards = [...customLists[listNames[0]]];
|
| 424 |
+
}
|
| 425 |
+
|
| 426 |
renderFlashcards();
|
| 427 |
renderCustomListButtons();
|
| 428 |
});
|
prompts.txt
CHANGED
|
@@ -425,4 +425,24 @@ Please enter both a list name and flashcards!
|
|
| 425 |
and no new button added
|
| 426 |
Just add a speaking voice for each german word, only speaks german, not english
|
| 427 |
|
| 428 |
-
also add a button to display not mastered words only of current list, this is applicable for all lists
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 425 |
and no new button added
|
| 426 |
Just add a speaking voice for each german word, only speaks german, not english
|
| 427 |
|
| 428 |
+
also add a button to display not mastered words only of current list, this is applicable for all lists
|
| 429 |
+
remove the built in list in code, let the user insert it and then save it in local storage, read it on reloading, any update on the list update it in local storage as well
|
| 430 |
+
|
| 431 |
+
one thing more, when switching between list name and no mastered read from local storage and display it to keep the list updated
|
| 432 |
+
|
| 433 |
+
any new list link it to localstorage, the localstorage is your database
|
| 434 |
+
remove the built in list in code, let the user insert it and then save it in local storage, read it on reloading, any update on the list update it in local storage as well
|
| 435 |
+
|
| 436 |
+
one thing more, when switching between list name and no mastered read from local storage and display it to keep the list updated
|
| 437 |
+
|
| 438 |
+
any new list link it to localstorage, the localstorage is your database
|
| 439 |
+
remove the built in list in code, let the user insert it and then save it in local storage, read it on reloading, any update on the list update it in local storage as well
|
| 440 |
+
|
| 441 |
+
one thing more, when switching between list name and no mastered read from local storage and display it to keep the list updated
|
| 442 |
+
|
| 443 |
+
any new list link it to localstorage, the localstorage is your database
|
| 444 |
+
reset progress of current list and update local storage
|
| 445 |
+
still the current list rendered after resetting progress
|
| 446 |
+
decrease the card dimension by half
|
| 447 |
+
decrease the width by half and render as much as can in one row
|
| 448 |
+
add a remove button in top right of each list button
|