Luca448 commited on
Commit
d6bb80e
·
1 Parent(s): 7bb1271

Feat: Automatically ask AI to regenerate the card's back (translation, explanation, example, image) when the vocabulary word on the front is changed

Browse files
Files changed (2) hide show
  1. index.html +1 -1
  2. src/views/learn/learn-view.js +87 -2
index.html CHANGED
@@ -51,7 +51,7 @@
51
  </head>
52
  <body class="dark-theme in-chat-view">
53
 
54
- <div id="live-version-badge" style="position: fixed; top: 50px; left: 50%; transform: translateX(-50%); font-size: 10px; color: var(--text-secondary); opacity: 0.6; z-index: 99999; pointer-events: none; font-weight: 500; font-family: monospace; letter-spacing: 1px; background: rgba(0,0,0,0.2); padding: 2px 8px; border-radius: 10px; border: 1px solid rgba(255,255,255,0.05);">BUILD 1.1.96</div>
55
 
56
  <!-- SVG Filters for Liquid Glass Refraction -->
57
  <svg width="0" height="0" style="position: absolute; pointer-events: none;">
 
51
  </head>
52
  <body class="dark-theme in-chat-view">
53
 
54
+ <div id="live-version-badge" style="position: fixed; top: 50px; left: 50%; transform: translateX(-50%); font-size: 10px; color: var(--text-secondary); opacity: 0.6; z-index: 99999; pointer-events: none; font-weight: 500; font-family: monospace; letter-spacing: 1px; background: rgba(0,0,0,0.2); padding: 2px 8px; border-radius: 10px; border: 1px solid rgba(255,255,255,0.05);">BUILD 1.1.97</div>
55
 
56
  <!-- SVG Filters for Liquid Glass Refraction -->
57
  <svg width="0" height="0" style="position: absolute; pointer-events: none;">
src/views/learn/learn-view.js CHANGED
@@ -291,11 +291,96 @@ export class LearnView {
291
  };
292
  }
293
 
294
- updateCardFront(cardId, deckId, newFront) {
295
  if (!newFront || newFront.trim() === '') return;
296
- const res = db.exec("SELECT back FROM flashcards WHERE id = ?", [cardId]);
297
  if (res && res.length > 0) {
 
 
 
 
298
  db.editFlashcard(cardId, newFront.trim(), res[0].back);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  }
300
  }
301
 
 
291
  };
292
  }
293
 
294
+ async updateCardFront(cardId, deckId, newFront) {
295
  if (!newFront || newFront.trim() === '') return;
296
+ const res = db.exec("SELECT front, back FROM flashcards WHERE id = ?", [cardId]);
297
  if (res && res.length > 0) {
298
+ const oldFront = res[0].front;
299
+ if (oldFront.trim() === newFront.trim()) return;
300
+
301
+ // Front has changed, update it
302
  db.editFlashcard(cardId, newFront.trim(), res[0].back);
303
+ this.renderDeckDetails(deckId);
304
+
305
+ // Show loading overlay on the back of the card
306
+ const previewModal = document.getElementById('preview-modal-overlay');
307
+ if (previewModal) {
308
+ const backEl = previewModal.querySelector('.flashcard-back');
309
+ if (backEl) {
310
+ const loader = document.createElement('div');
311
+ loader.id = 'ai-regenerate-loader';
312
+ loader.style = 'position: absolute; top:0; left:0; width:100%; height:100%; background: rgba(10,10,12,0.85); display: flex; flex-direction: column; align-items:center; justify-content:center; z-index: 10; color: var(--primary); font-weight: 500; font-size: 14px; backdrop-filter: blur(4px);';
313
+ loader.innerHTML = `
314
+ <style>@keyframes pulseAI { 0% { opacity:0.5; transform: scale(0.9); } 50% { opacity:1; transform: scale(1.1); } 100% { opacity:0.5; transform: scale(0.9); } }</style>
315
+ <div style="margin-bottom: 12px; font-size: 24px; animation: pulseAI 1.5s infinite;">✨</div>
316
+ <div>KI generiert neue Karte...</div>
317
+ `;
318
+ backEl.appendChild(loader);
319
+
320
+ // Flip the card automatically so the user sees the generation
321
+ const inner = previewModal.querySelector('.flashcard-inner');
322
+ if (inner && !inner.classList.contains('flipped')) {
323
+ inner.classList.add('flipped');
324
+ }
325
+ }
326
+ }
327
+
328
+ try {
329
+ const prompt = `Du bist ein KI-Tutor. Generiere die Rückseite für eine Vokabelkarte.
330
+ Wort: "${newFront.trim()}"
331
+ Gib AUSSCHLIESSLICH ein valides JSON-Objekt zurück, ohne Markdown-Formatierung wie \`\`\`json. Format:
332
+ {
333
+ "translation": "Die kurze Übersetzung",
334
+ "explanation": "Eine kurze Erklärung (1-2 Sätze, zwingend in der Zielsprache der Vokabel)",
335
+ "example": "Ein spezifischer, anspruchsvoller Beispielsatz (zwingend in der Zielsprache)",
336
+ "search_query": "Ein simpler englischer Suchbegriff für ein passendes Bild"
337
+ }`;
338
+
339
+ const aiResponse = await aiRouter.sendMessage(prompt, { history: [] });
340
+ let jsonStr = aiResponse;
341
+ if (jsonStr.includes('\`\`\`json')) {
342
+ jsonStr = jsonStr.split('\`\`\`json')[1].split('\`\`\`')[0];
343
+ } else if (jsonStr.includes('\`\`\`')) {
344
+ jsonStr = jsonStr.split('\`\`\`')[1].split('\`\`\`')[0];
345
+ }
346
+
347
+ const data = JSON.parse(jsonStr.trim());
348
+ const newBack = `${data.translation}\n\n**Erklärung:** ${data.explanation}\n\n**Beispiel:** ${data.example}`;
349
+
350
+ let newImageUrl = null;
351
+ if (data.search_query) {
352
+ const encodedQuery = encodeURIComponent(data.search_query + ' single photo, clear focus, high quality');
353
+ newImageUrl = `https://tse1.mm.bing.net/th?q=${encodedQuery}&w=800&h=1000&c=7&rs=1`;
354
+ }
355
+
356
+ db.editFlashcard(cardId, newFront.trim(), newBack);
357
+ if (newImageUrl) {
358
+ db.exec("UPDATE flashcards SET image_url = ? WHERE id = ?", [newImageUrl, cardId]);
359
+ }
360
+
361
+ this.renderDeckDetails(deckId);
362
+
363
+ const currentModal = document.getElementById('preview-modal-overlay');
364
+ if (currentModal) {
365
+ const updatedRes = db.exec("SELECT * FROM flashcards WHERE id = ?", [cardId]);
366
+ if (updatedRes && updatedRes.length > 0) {
367
+ currentModal.remove();
368
+ this.previewCard(updatedRes[0], deckId);
369
+ // Ensure it stays flipped to show the result
370
+ setTimeout(() => {
371
+ const newInner = document.querySelector('#preview-modal-overlay .flashcard-inner');
372
+ if (newInner) newInner.classList.add('flipped');
373
+ }, 50);
374
+ }
375
+ }
376
+ } catch (e) {
377
+ console.error("Failed to regenerate card:", e);
378
+ const currentModal = document.getElementById('preview-modal-overlay');
379
+ if (currentModal) {
380
+ const loader = currentModal.querySelector('#ai-regenerate-loader');
381
+ if (loader) loader.remove();
382
+ }
383
+ }
384
  }
385
  }
386