amauricunha commited on
Commit
cdeafe8
·
verified ·
1 Parent(s): d52e5bd

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +188 -16
index.html CHANGED
@@ -209,6 +209,24 @@
209
  </div>
210
  </div>
211
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  <!-- Player Controls and Phrase Loop -->
213
  <div class="mt-4 border-t pt-4">
214
  <audio id="audioPlayer" controls class="w-full mb-4"></audio>
@@ -223,8 +241,37 @@
223
  </div>
224
  </div>
225
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
 
227
- <!-- New Flashcards Panel -->
 
228
  <div id="flashcardsPanel" class="card">
229
  <h2 class="text-xl font-semibold mb-4 text-gray-700">Your Flashcards (Study Session)</h2>
230
  <div id="flashcardList" class="space-y-3">
@@ -282,7 +329,14 @@
282
  const flashcardList = document.getElementById('flashcardList');
283
  const noCardsMessage = document.getElementById('noCardsMessage');
284
  const speedSelector = document.getElementById('speedSelector');
285
- const modelSelector = document.getElementById('modelSelector'); // New Selector
 
 
 
 
 
 
 
286
 
287
  const MAX_CHARS = 5000;
288
  let isLooping = false;
@@ -321,6 +375,8 @@
321
  const currentLength = getEditorText().length;
322
  charCount.textContent = currentLength;
323
  playButton.disabled = currentLength === 0;
 
 
324
  });
325
 
326
  // Update character count on load
@@ -345,7 +401,6 @@
345
  loadingSpinner.classList.remove('hidden');
346
 
347
  try {
348
- // Route for the backend using gTTS
349
  const response = await fetch('/tts-proxy', {
350
  method: 'POST',
351
  headers: { 'Content-Type': 'application/json' },
@@ -415,6 +470,126 @@
415
  }
416
  });
417
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
  // --- Quick Translation Functionality (Double Click) ---
419
 
420
  async function fetchExplanationAndTranslation(word, context, isFlashcard=false) {
@@ -423,25 +598,24 @@
423
  const translationText = isFlashcard ? null : document.getElementById('translationText');
424
 
425
  const modelValue = modelSelector.value;
 
426
 
427
  if (!isFlashcard) {
428
- // Show loading state in modal
429
  explanationText.textContent = '';
430
  translationText.textContent = '';
431
  }
432
  spinner.classList.remove('hidden');
433
 
434
  try {
435
- // Route for the backend using Gemini/Groq for explanation/translation
436
  const response = await fetch('/explain-proxy', {
437
  method: 'POST',
438
  headers: { 'Content-Type': 'application/json' },
439
- // Sends the clicked word, context, and the selected model
440
  body: JSON.stringify({
441
  word: word,
442
  context: context,
443
  for_flashcard: isFlashcard,
444
- model: modelValue // NEW: Passes provider:model-name
 
445
  })
446
  });
447
 
@@ -453,7 +627,7 @@
453
  const data = await response.json();
454
 
455
  if (isFlashcard) {
456
- return data; // Return full data for flashcard creation
457
  } else {
458
  explanationText.textContent = data.explanation || 'Could not retrieve explanation.';
459
  translationText.textContent = data.translation || 'Could not retrieve translation.';
@@ -477,17 +651,17 @@
477
 
478
  // Logic to open the Modal on double click
479
  textEditor.addEventListener('dblclick', (event) => {
480
- const selectedWord = window.getSelection().toString().trim().replace(/[^a-zA-Z']/g, '');
 
481
 
482
  if (selectedWord) {
483
  const fullText = getEditorText();
484
- const index = fullText.indexOf(selectedWord);
485
- const context = fullText.substring(Math.max(0, index - 50), index + selectedWord.length + 50);
486
 
487
  document.getElementById('wordSelected').textContent = selectedWord;
488
  openModal();
489
 
490
- // Start fetching explanation and translation
491
  fetchExplanationAndTranslation(selectedWord, context);
492
  }
493
  });
@@ -508,7 +682,6 @@
508
  createCardButton.innerHTML = `<div class="spinner mr-2"></div> Creating Card...`;
509
 
510
  try {
511
- // Fetch the structured data for the flashcard
512
  const cardData = await fetchExplanationAndTranslation(selectedText, context, true);
513
 
514
  if (cardData && cardData.term && cardData.translation && cardData.example) {
@@ -529,17 +702,16 @@
529
  const cardElement = document.createElement('div');
530
  cardElement.className = 'flashcard';
531
 
532
- // Front (Term and Translation)
533
  cardElement.innerHTML = `
534
  <div class="flashcard-front">
535
  <p class="flashcard-term">${data.term.toUpperCase()}</p>
536
  <p class="flashcard-definition">PT: ${data.translation}</p>
537
  <p class="flashcard-example">Example: ${data.example}</p>
538
  </div>
539
- <!-- Back side (Definition) can be toggled via JS if needed -->
540
  `;
541
 
542
- flashcardList.prepend(cardElement); // Add new cards to the top
543
  }
544
 
545
 
 
209
  </div>
210
  </div>
211
 
212
+ <!-- Context Selector (NEW) -->
213
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
214
+ <div>
215
+ <label for="contextSelector" class="block text-sm font-medium text-gray-700">Vocabulary Context / Focus Area</label>
216
+ <select id="contextSelector" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md shadow-sm">
217
+ <option value="General/Social (Casual conversation, daily life)">General/Social (Casual conversation, daily life)</option>
218
+ <option value="Professional/Business (Meetings, emails, reports)">Professional/Business (Meetings, emails, reports)</option>
219
+ <option value="Technical/IT (Software, Engineering, Tech topics)">Technical/IT (Software, Engineering, Tech topics)</option>
220
+ </select>
221
+ </div>
222
+ <!-- Full Translation Button -->
223
+ <div class="flex items-end">
224
+ <button id="translateFullTextButton" onclick="translateFullText()" class="btn-secondary w-full py-2.5 rounded-lg font-semibold disabled:opacity-50">
225
+ Translate Full Text (PT)
226
+ </button>
227
+ </div>
228
+ </div>
229
+
230
  <!-- Player Controls and Phrase Loop -->
231
  <div class="mt-4 border-t pt-4">
232
  <audio id="audioPlayer" controls class="w-full mb-4"></audio>
 
241
  </div>
242
  </div>
243
  </div>
244
+
245
+ <!-- Writing/Translation Activity Panel (NEW) -->
246
+ <div class="card mb-6">
247
+ <h2 class="text-xl font-semibold mb-4 text-gray-700">Writing & Translation Activities</h2>
248
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
249
+ <!-- Activity Selector -->
250
+ <div class="md:col-span-2">
251
+ <label for="activityTypeSelector" class="block text-sm font-medium text-gray-700">Activity Type</label>
252
+ <select id="activityTypeSelector" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md shadow-sm">
253
+ <option value="translation_pt_en">Translation from PT to EN (Practice Writing)</option>
254
+ <option value="email_writing">Formal Email Prompt (Professional Practice)</option>
255
+ <option value="summary_writing">Summarization Prompt (Reading & Writing)</option>
256
+ </select>
257
+ </div>
258
+ <!-- Generate Button -->
259
+ <div class="md:col-span-1 flex items-end">
260
+ <button id="generateActivityButton" onclick="generateActivity()" class="btn-primary w-full py-2.5 rounded-lg font-semibold flex items-center justify-center">
261
+ <span id="activityText">Generate Activity</span>
262
+ <div id="activitySpinner" class="spinner ml-3 hidden"></div>
263
+ </button>
264
+ </div>
265
+ </div>
266
+
267
+ <!-- Activity Output -->
268
+ <div id="activityOutput" class="border p-4 mt-4 rounded-lg bg-gray-50 min-h-[100px]">
269
+ <p class="text-gray-500 italic">Select an activity and context to generate your first prompt.</p>
270
+ </div>
271
+ </div>
272
 
273
+
274
+ <!-- Flashcards Panel -->
275
  <div id="flashcardsPanel" class="card">
276
  <h2 class="text-xl font-semibold mb-4 text-gray-700">Your Flashcards (Study Session)</h2>
277
  <div id="flashcardList" class="space-y-3">
 
329
  const flashcardList = document.getElementById('flashcardList');
330
  const noCardsMessage = document.getElementById('noCardsMessage');
331
  const speedSelector = document.getElementById('speedSelector');
332
+ const modelSelector = document.getElementById('modelSelector');
333
+ const contextSelector = document.getElementById('contextSelector'); // NEW
334
+ const activityTypeSelector = document.getElementById('activityTypeSelector'); // NEW
335
+ const generateActivityButton = document.getElementById('generateActivityButton'); // NEW
336
+ const activityOutput = document.getElementById('activityOutput'); // NEW
337
+ const activitySpinner = document.getElementById('activitySpinner'); // NEW
338
+ const activityText = document.getElementById('activityText'); // NEW
339
+ const translateFullTextButton = document.getElementById('translateFullTextButton'); // NEW
340
 
341
  const MAX_CHARS = 5000;
342
  let isLooping = false;
 
375
  const currentLength = getEditorText().length;
376
  charCount.textContent = currentLength;
377
  playButton.disabled = currentLength === 0;
378
+ // Enable/disable translate full text button based on content
379
+ translateFullTextButton.disabled = currentLength === 0;
380
  });
381
 
382
  // Update character count on load
 
401
  loadingSpinner.classList.remove('hidden');
402
 
403
  try {
 
404
  const response = await fetch('/tts-proxy', {
405
  method: 'POST',
406
  headers: { 'Content-Type': 'application/json' },
 
470
  }
471
  });
472
 
473
+ // --- Full Text Translation Functionality (NEW) ---
474
+
475
+ async function translateFullText() {
476
+ const fullText = getEditorText();
477
+ if (!fullText) {
478
+ alert("Please enter text to translate.");
479
+ return;
480
+ }
481
+
482
+ translateFullTextButton.disabled = true;
483
+ translateFullTextButton.innerHTML = `<div class="spinner mr-2"></div> Translating...`;
484
+ const modelValue = modelSelector.value;
485
+ const context = contextSelector.value;
486
+
487
+ try {
488
+ // Use a generic prompt for full translation
489
+ const prompt = `Translate the following English text into clear, modern Portuguese, ensuring the translation adheres to the context: ${context}.\n\nENGLISH TEXT:\n---${fullText}---`;
490
+
491
+ const response = await fetch('/explain-proxy', { // Reusing explain-proxy for simple text generation
492
+ method: 'POST',
493
+ headers: { 'Content-Type': 'application/json' },
494
+ body: JSON.stringify({
495
+ word: "FULL_TRANSLATION", // Marker word, not used by backend for full text
496
+ context: fullText, // Full text as context
497
+ for_flashcard: false,
498
+ model: modelValue,
499
+ custom_prompt: prompt // Pass the custom prompt
500
+ })
501
+ });
502
+
503
+ if (!response.ok) {
504
+ throw new Error(`HTTP Error: ${response.status}. Check API Keys.`);
505
+ }
506
+
507
+ const data = await response.json();
508
+ const translation = data.translation;
509
+
510
+ // Open a temporary modal or alert the user with the result
511
+ alert("FULL TEXT TRANSLATION:\n\n" + translation);
512
+
513
+ } catch (error) {
514
+ console.error('Error translating full text:', error);
515
+ alert('Failed to translate full text. Check console and API Keys.');
516
+ } finally {
517
+ translateFullTextButton.disabled = false;
518
+ translateFullTextButton.textContent = "Translate Full Text (PT)";
519
+ }
520
+ }
521
+
522
+
523
+ // --- Activity Generation Functionality (NEW) ---
524
+
525
+ async function generateActivity() {
526
+ const activityType = activityTypeSelector.value;
527
+ const context = contextSelector.value;
528
+ const modelValue = modelSelector.value;
529
+ const sourceText = getEditorText();
530
+
531
+ generateActivityButton.disabled = true;
532
+ activityText.textContent = "Generating...";
533
+ activitySpinner.classList.remove('hidden');
534
+ activityOutput.innerHTML = `<p class="text-gray-500 italic">Generating prompt...</p>`;
535
+
536
+ let prompt = "";
537
+ let title = "";
538
+
539
+ if (activityType === 'translation_pt_en') {
540
+ title = "Translation Exercise (PT to EN)";
541
+ // We ask the AI to generate a Portuguese phrase first, based on the context, and then give the prompt.
542
+ prompt = `You are a language teacher. Generate a single, challenging, but short Portuguese phrase that is perfectly relevant to the '${context}' context. Then, provide the Portuguese phrase to the user and prompt them to translate it to English. Format the output as: [PORTUGUESE PHRASE] \n\n Your Task: Translate the phrase above into English.`;
543
+ } else if (activityType === 'email_writing') {
544
+ title = "Formal Email Writing Prompt";
545
+ prompt = `You are a language teacher. Generate a challenging, realistic scenario for a formal email writing exercise, based strictly on the '${context}' context. The prompt must be in English and clearly state the recipient (e.g., 'Your Manager'), the goal (e.g., 'Requesting a budget'), and the required tone (e.g., 'Formal and concise').`;
546
+ } else if (activityType === 'summary_writing' && sourceText) {
547
+ title = "Summarization Prompt";
548
+ prompt = `You are a language teacher. Summarize the following English text in a single, complex English sentence, and then ask the user to expand your summary back into a paragraph, ensuring they use complex vocabulary related to the '${context}' context. The text to summarize is:\n\n---${sourceText}---`;
549
+ } else {
550
+ activityOutput.innerHTML = `<p class="text-red-500 font-semibold">Please enter text in the editor to use the Summarization Prompt.</p>`;
551
+ generateActivityButton.disabled = false;
552
+ activityText.textContent = "Generate Activity";
553
+ activitySpinner.classList.add('hidden');
554
+ return;
555
+ }
556
+
557
+ try {
558
+ // Reusing explain-proxy for simple text generation
559
+ const response = await fetch('/explain-proxy', {
560
+ method: 'POST',
561
+ headers: { 'Content-Type': 'application/json' },
562
+ body: JSON.stringify({
563
+ word: "ACTIVITY_GEN",
564
+ context: context,
565
+ for_flashcard: false,
566
+ model: modelValue,
567
+ custom_prompt: prompt
568
+ })
569
+ });
570
+
571
+ if (!response.ok) {
572
+ throw new Error(`HTTP Error: ${response.status}. Check API Keys.`);
573
+ }
574
+
575
+ const data = await response.json();
576
+ // The explanation field holds the generated activity text
577
+ const activityResult = data.explanation || data.translation || "Failed to generate activity prompt.";
578
+
579
+ activityOutput.innerHTML = `<h3 class="font-bold mb-2">${title}</h3><div class="whitespace-pre-wrap">${activityResult}</div>`;
580
+
581
+
582
+ } catch (error) {
583
+ console.error('Error generating activity:', error);
584
+ activityOutput.innerHTML = `<p class="text-red-500 font-semibold">Activity generation failed. Check if API Keys are configured correctly for ${modelValue}.</p>`;
585
+ } finally {
586
+ generateActivityButton.disabled = false;
587
+ activityText.textContent = "Generate Activity";
588
+ activitySpinner.classList.add('hidden');
589
+ }
590
+ }
591
+
592
+
593
  // --- Quick Translation Functionality (Double Click) ---
594
 
595
  async function fetchExplanationAndTranslation(word, context, isFlashcard=false) {
 
598
  const translationText = isFlashcard ? null : document.getElementById('translationText');
599
 
600
  const modelValue = modelSelector.value;
601
+ const contextFocus = contextSelector.value; // Pass context focus
602
 
603
  if (!isFlashcard) {
 
604
  explanationText.textContent = '';
605
  translationText.textContent = '';
606
  }
607
  spinner.classList.remove('hidden');
608
 
609
  try {
 
610
  const response = await fetch('/explain-proxy', {
611
  method: 'POST',
612
  headers: { 'Content-Type': 'application/json' },
 
613
  body: JSON.stringify({
614
  word: word,
615
  context: context,
616
  for_flashcard: isFlashcard,
617
+ model: modelValue,
618
+ context_focus: contextFocus // NEW: Pass context focus for accurate flashcards
619
  })
620
  });
621
 
 
627
  const data = await response.json();
628
 
629
  if (isFlashcard) {
630
+ return data;
631
  } else {
632
  explanationText.textContent = data.explanation || 'Could not retrieve explanation.';
633
  translationText.textContent = data.translation || 'Could not retrieve translation.';
 
651
 
652
  // Logic to open the Modal on double click
653
  textEditor.addEventListener('dblclick', (event) => {
654
+ const selection = window.getSelection().toString().trim();
655
+ const selectedWord = selection.replace(/[^a-zA-Z']/g, ''); // Basic word selection logic
656
 
657
  if (selectedWord) {
658
  const fullText = getEditorText();
659
+ const index = fullText.indexOf(selection);
660
+ const context = fullText.substring(Math.max(0, index - 50), index + selection.length + 50);
661
 
662
  document.getElementById('wordSelected').textContent = selectedWord;
663
  openModal();
664
 
 
665
  fetchExplanationAndTranslation(selectedWord, context);
666
  }
667
  });
 
682
  createCardButton.innerHTML = `<div class="spinner mr-2"></div> Creating Card...`;
683
 
684
  try {
 
685
  const cardData = await fetchExplanationAndTranslation(selectedText, context, true);
686
 
687
  if (cardData && cardData.term && cardData.translation && cardData.example) {
 
702
  const cardElement = document.createElement('div');
703
  cardElement.className = 'flashcard';
704
 
705
+ // Render card content
706
  cardElement.innerHTML = `
707
  <div class="flashcard-front">
708
  <p class="flashcard-term">${data.term.toUpperCase()}</p>
709
  <p class="flashcard-definition">PT: ${data.translation}</p>
710
  <p class="flashcard-example">Example: ${data.example}</p>
711
  </div>
 
712
  `;
713
 
714
+ flashcardList.prepend(cardElement);
715
  }
716
 
717