Spaces:
Sleeping
Sleeping
Update index.html
Browse files- index.html +31 -13
index.html
CHANGED
|
@@ -15,7 +15,7 @@
|
|
| 15 |
.btn-primary:hover:not(:disabled) { background-color: #4338ca; }
|
| 16 |
.spinner { border: 2px solid rgba(255,255,255,0.3); width: 20px; height: 20px; border-radius: 50%; border-left-color: #fff; animation: spin 1s ease infinite; }
|
| 17 |
@keyframes spin { to { transform: rotate(360deg); } }
|
| 18 |
-
.flashcard-container { perspective: 1000px; }
|
| 19 |
.flashcard-inner { position: relative; width: 100%; transition: transform 0.6s; transform-style: preserve-3d; }
|
| 20 |
.flashcard-container.flipped .flashcard-inner { transform: rotateY(180deg); }
|
| 21 |
.flashcard-front, .flashcard-back { position: absolute; width: 100%; height:100%; backface-visibility: hidden; border: 1px solid #e5e7eb; border-radius: 12px; padding: 20px; display: flex; flex-direction: column; justify-content: space-between; }
|
|
@@ -27,6 +27,8 @@
|
|
| 27 |
.user-message { background-color: #4f46e5; color: white; align-self: flex-end; margin-left: auto; border-bottom-right-radius: 4px; }
|
| 28 |
.ai-message { background-color: #e5e7eb; color: #1f2937; align-self: flex-start; border-bottom-left-radius: 4px; }
|
| 29 |
.mic-button.is-recording { background-color: #ef4444; }
|
|
|
|
|
|
|
| 30 |
</style>
|
| 31 |
</head>
|
| 32 |
<body class="p-4 md:p-8">
|
|
@@ -95,9 +97,8 @@
|
|
| 95 |
</optgroup>
|
| 96 |
<optgroup label="Groq (Ultra Rápido)">
|
| 97 |
<option value="groq:llama-3.1-8b-instant">Llama 3.1 8B</option>
|
| 98 |
-
<option value="groq:
|
| 99 |
-
<option value="groq:
|
| 100 |
-
<option value="groq:mixtral-8x7b-32768">Mixtral 8x7b</option>
|
| 101 |
</optgroup>
|
| 102 |
</select>
|
| 103 |
</div>
|
|
@@ -175,7 +176,6 @@
|
|
| 175 |
audioPlayer.play();
|
| 176 |
audioPlayer.onended = () => URL.revokeObjectURL(audioUrl);
|
| 177 |
} catch (error) {
|
| 178 |
-
// MELHORIA: Exibe o erro para o usuário
|
| 179 |
alert(`Erro ao gerar áudio: ${error.message}. Verifique o console para mais detalhes.`);
|
| 180 |
} finally {
|
| 181 |
playButton.disabled = false;
|
|
@@ -345,10 +345,9 @@
|
|
| 345 |
throw new Error(errData.error || 'Erro desconhecido do servidor.');
|
| 346 |
}
|
| 347 |
const cardData = await response.json();
|
| 348 |
-
if(definition && !cardData.definition) cardData.definition = definition;
|
| 349 |
renderFlashcard(cardData);
|
| 350 |
} catch (error) {
|
| 351 |
-
// MELHORIA: Exibe o erro para o usuário
|
| 352 |
alert(`Falha ao criar flashcard: ${error.message}. Verifique as chaves de API e a seleção do modelo.`);
|
| 353 |
} finally {
|
| 354 |
if (btn) {
|
|
@@ -373,26 +372,29 @@
|
|
| 373 |
const minHeight = 380;
|
| 374 |
container.style.minHeight = `${minHeight}px`;
|
| 375 |
|
|
|
|
|
|
|
| 376 |
container.innerHTML = `
|
|
|
|
| 377 |
<div class="flashcard-inner" style="min-height: ${minHeight}px;">
|
| 378 |
<div class="flashcard-front">
|
| 379 |
<div>
|
| 380 |
<div class="text-sm text-gray-600 mb-2">Frase de Contexto:</div>
|
| 381 |
-
<p class="text-lg text-center text-gray-800">${data.gapped_sentence ||
|
| 382 |
</div>
|
| 383 |
<div class="border-t pt-4 mt-4 text-center">
|
| 384 |
-
<strong class="text-indigo-600">Dica (PT):</strong> ${data.translation ||
|
| 385 |
</div>
|
| 386 |
<div class="text-xs text-center text-gray-400 mt-4">Clique para ver a resposta</div>
|
| 387 |
</div>
|
| 388 |
<div class="flashcard-back">
|
| 389 |
<div>
|
| 390 |
-
<h3 class="text-xl font-bold text-center text-indigo-600 mb-2">${data.term}</h3>
|
| 391 |
<p class="flex items-center justify-center gap-2 text-gray-600 italic mb-3">
|
| 392 |
<button class="audio-btn" onclick="playAudio('${(data.context_sentence || '').replace(/'/g, "\\'")}', event)">🔊</button>
|
| 393 |
-
<span>${data.context_sentence ||
|
| 394 |
</p>
|
| 395 |
-
<p class="text-sm text-gray-800"><strong class="font-semibold">Definição:</strong> ${data.definition ||
|
| 396 |
</div>
|
| 397 |
<div class="border-t mt-3 pt-3">
|
| 398 |
<h4 class="text-sm font-semibold text-center">Pratique sua Pronúncia</h4>
|
|
@@ -406,10 +408,26 @@
|
|
| 406 |
|
| 407 |
flashcardList.prepend(container);
|
| 408 |
container.addEventListener('click', (e) => {
|
| 409 |
-
|
|
|
|
|
|
|
|
|
|
| 410 |
});
|
| 411 |
}
|
| 412 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 413 |
// --- LÓGICA DE PRÁTICA DE PRONÚNCIA ---
|
| 414 |
async function handlePronunciationPractice(button, targetText, event) {
|
| 415 |
event.stopPropagation();
|
|
|
|
| 15 |
.btn-primary:hover:not(:disabled) { background-color: #4338ca; }
|
| 16 |
.spinner { border: 2px solid rgba(255,255,255,0.3); width: 20px; height: 20px; border-radius: 50%; border-left-color: #fff; animation: spin 1s ease infinite; }
|
| 17 |
@keyframes spin { to { transform: rotate(360deg); } }
|
| 18 |
+
.flashcard-container { perspective: 1000px; position: relative; } /* Posição relativa para o botão de excluir */
|
| 19 |
.flashcard-inner { position: relative; width: 100%; transition: transform 0.6s; transform-style: preserve-3d; }
|
| 20 |
.flashcard-container.flipped .flashcard-inner { transform: rotateY(180deg); }
|
| 21 |
.flashcard-front, .flashcard-back { position: absolute; width: 100%; height:100%; backface-visibility: hidden; border: 1px solid #e5e7eb; border-radius: 12px; padding: 20px; display: flex; flex-direction: column; justify-content: space-between; }
|
|
|
|
| 27 |
.user-message { background-color: #4f46e5; color: white; align-self: flex-end; margin-left: auto; border-bottom-right-radius: 4px; }
|
| 28 |
.ai-message { background-color: #e5e7eb; color: #1f2937; align-self: flex-start; border-bottom-left-radius: 4px; }
|
| 29 |
.mic-button.is-recording { background-color: #ef4444; }
|
| 30 |
+
.delete-btn { position: absolute; top: 10px; right: 10px; background: none; border: none; font-size: 1.2rem; cursor: pointer; color: #9ca3af; z-index: 10; }
|
| 31 |
+
.delete-btn:hover { color: #ef4444; }
|
| 32 |
</style>
|
| 33 |
</head>
|
| 34 |
<body class="p-4 md:p-8">
|
|
|
|
| 97 |
</optgroup>
|
| 98 |
<optgroup label="Groq (Ultra Rápido)">
|
| 99 |
<option value="groq:llama-3.1-8b-instant">Llama 3.1 8B</option>
|
| 100 |
+
<option value="groq:mixtral-8x7b-32768">Mixtral 8x7b (Balanceado)</option>
|
| 101 |
+
<option value="groq:gemma-7b-it">Gemma 7B (Confiável)</option>
|
|
|
|
| 102 |
</optgroup>
|
| 103 |
</select>
|
| 104 |
</div>
|
|
|
|
| 176 |
audioPlayer.play();
|
| 177 |
audioPlayer.onended = () => URL.revokeObjectURL(audioUrl);
|
| 178 |
} catch (error) {
|
|
|
|
| 179 |
alert(`Erro ao gerar áudio: ${error.message}. Verifique o console para mais detalhes.`);
|
| 180 |
} finally {
|
| 181 |
playButton.disabled = false;
|
|
|
|
| 345 |
throw new Error(errData.error || 'Erro desconhecido do servidor.');
|
| 346 |
}
|
| 347 |
const cardData = await response.json();
|
| 348 |
+
if(definition && !cardData.definition) cardData.definition = definition;
|
| 349 |
renderFlashcard(cardData);
|
| 350 |
} catch (error) {
|
|
|
|
| 351 |
alert(`Falha ao criar flashcard: ${error.message}. Verifique as chaves de API e a seleção do modelo.`);
|
| 352 |
} finally {
|
| 353 |
if (btn) {
|
|
|
|
| 372 |
const minHeight = 380;
|
| 373 |
container.style.minHeight = `${minHeight}px`;
|
| 374 |
|
| 375 |
+
const fallback = 'Informação não gerada';
|
| 376 |
+
|
| 377 |
container.innerHTML = `
|
| 378 |
+
<button class="delete-btn" onclick="deleteFlashcard('${cardId}', event)">🗑️</button>
|
| 379 |
<div class="flashcard-inner" style="min-height: ${minHeight}px;">
|
| 380 |
<div class="flashcard-front">
|
| 381 |
<div>
|
| 382 |
<div class="text-sm text-gray-600 mb-2">Frase de Contexto:</div>
|
| 383 |
+
<p class="text-lg text-center text-gray-800">${data.gapped_sentence || fallback}</p>
|
| 384 |
</div>
|
| 385 |
<div class="border-t pt-4 mt-4 text-center">
|
| 386 |
+
<strong class="text-indigo-600">Dica (PT):</strong> ${data.translation || fallback}
|
| 387 |
</div>
|
| 388 |
<div class="text-xs text-center text-gray-400 mt-4">Clique para ver a resposta</div>
|
| 389 |
</div>
|
| 390 |
<div class="flashcard-back">
|
| 391 |
<div>
|
| 392 |
+
<h3 class="text-xl font-bold text-center text-indigo-600 mb-2">${data.term || 'Termo'}</h3>
|
| 393 |
<p class="flex items-center justify-center gap-2 text-gray-600 italic mb-3">
|
| 394 |
<button class="audio-btn" onclick="playAudio('${(data.context_sentence || '').replace(/'/g, "\\'")}', event)">🔊</button>
|
| 395 |
+
<span>${data.context_sentence || fallback}</span>
|
| 396 |
</p>
|
| 397 |
+
<p class="text-sm text-gray-800"><strong class="font-semibold">Definição:</strong> ${data.definition || fallback}</p>
|
| 398 |
</div>
|
| 399 |
<div class="border-t mt-3 pt-3">
|
| 400 |
<h4 class="text-sm font-semibold text-center">Pratique sua Pronúncia</h4>
|
|
|
|
| 408 |
|
| 409 |
flashcardList.prepend(container);
|
| 410 |
container.addEventListener('click', (e) => {
|
| 411 |
+
// Evita virar o card se um botão dentro dele for clicado
|
| 412 |
+
if(e.target.tagName !== 'BUTTON' && !e.target.closest('button')) {
|
| 413 |
+
container.classList.toggle('flipped');
|
| 414 |
+
}
|
| 415 |
});
|
| 416 |
}
|
| 417 |
|
| 418 |
+
// --- NOVA FUNÇÃO PARA EXCLUIR FLASHCARD ---
|
| 419 |
+
function deleteFlashcard(cardId, event) {
|
| 420 |
+
event.stopPropagation(); // Impede que o evento de clique se propague para o card (evitando que ele vire)
|
| 421 |
+
const cardToDelete = document.getElementById(cardId);
|
| 422 |
+
if (cardToDelete) {
|
| 423 |
+
cardToDelete.remove();
|
| 424 |
+
}
|
| 425 |
+
// Mostra a mensagem de "sem cards" se a lista ficar vazia
|
| 426 |
+
if (flashcardList.children.length === 1) { // Apenas a mensagem <p> restará
|
| 427 |
+
noCardsMessage.classList.remove('hidden');
|
| 428 |
+
}
|
| 429 |
+
}
|
| 430 |
+
|
| 431 |
// --- LÓGICA DE PRÁTICA DE PRONÚNCIA ---
|
| 432 |
async function handlePronunciationPractice(button, targetText, event) {
|
| 433 |
event.stopPropagation();
|