Spaces:
Running
Running
Update index.html
Browse files- index.html +74 -19
index.html
CHANGED
|
@@ -421,6 +421,11 @@
|
|
| 421 |
<label for="edit-sentence-zh-input" class="block text-sm font-semibold text-gray-700 mb-1">中文例句</label>
|
| 422 |
<input id="edit-sentence-zh-input" type="text" class="w-full p-3 border border-gray-300 rounded-lg text-lg">
|
| 423 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 424 |
</div>
|
| 425 |
<div class="flex justify-end gap-4 mt-8">
|
| 426 |
<button type="button" id="cancel-edit-btn" class="px-6 py-2 bg-gray-200 text-gray-800 rounded-lg hover:bg-gray-300 transition-colors">取消</button>
|
|
@@ -656,6 +661,7 @@
|
|
| 656 |
const editChineseInput = document.getElementById('edit-chinese-input');
|
| 657 |
const editSentenceEnInput = document.getElementById('edit-sentence-en-input');
|
| 658 |
const editSentenceZhInput = document.getElementById('edit-sentence-zh-input');
|
|
|
|
| 659 |
const saveEditBtn = document.getElementById('save-edit-btn');
|
| 660 |
const cancelEditBtn = document.getElementById('cancel-edit-btn');
|
| 661 |
const shareModal = document.getElementById('share-modal');
|
|
@@ -786,6 +792,7 @@
|
|
| 786 |
editChineseInput.value = word.chinese;
|
| 787 |
editSentenceEnInput.value = word.sentence?.en || '';
|
| 788 |
editSentenceZhInput.value = word.sentence?.zh || '';
|
|
|
|
| 789 |
editWordModal.classList.remove('hidden');
|
| 790 |
} else if (button.classList.contains('delete-btn')) {
|
| 791 |
indexToDelete = index;
|
|
@@ -800,15 +807,26 @@
|
|
| 800 |
const newChinese = editChineseInput.value.trim();
|
| 801 |
const newSentenceEn = editSentenceEnInput.value.trim();
|
| 802 |
const newSentenceZh = editSentenceZhInput.value.trim();
|
|
|
|
| 803 |
|
| 804 |
if (index >= 0 && newEnglish && newChinese) {
|
| 805 |
words[index].english = newEnglish;
|
| 806 |
words[index].chinese = newChinese;
|
|
|
|
|
|
|
| 807 |
if (newSentenceEn && newSentenceZh) {
|
| 808 |
-
words[index].sentence = {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 809 |
} else {
|
| 810 |
delete words[index].sentence;
|
| 811 |
}
|
|
|
|
| 812 |
saveWordsToStorage();
|
| 813 |
renderWordList();
|
| 814 |
editWordModal.classList.add('hidden');
|
|
@@ -956,7 +974,7 @@
|
|
| 956 |
sentenceZhDisplay.textContent = `(${card.sentence.zh})`;
|
| 957 |
sentenceZhDisplay.classList.add('hidden');
|
| 958 |
toggleTranslationBtn.textContent = '顯示翻譯';
|
| 959 |
-
backText = card.english;
|
| 960 |
answerInput.placeholder = "請填入空格中的單字...";
|
| 961 |
break;
|
| 962 |
}
|
|
@@ -1000,11 +1018,12 @@
|
|
| 1000 |
} else {
|
| 1001 |
wordPool = [...words];
|
| 1002 |
}
|
| 1003 |
-
|
|
|
|
| 1004 |
if (mode === 'sentence-cloze') {
|
| 1005 |
-
wordsForCurrentMode = wordPool.filter(w => w.sentence && w.sentence.en && w.sentence.zh);
|
| 1006 |
if (wordsForCurrentMode.length === 0) {
|
| 1007 |
-
alert("
|
| 1008 |
return;
|
| 1009 |
}
|
| 1010 |
} else if (mode === 'review') {
|
|
@@ -1076,7 +1095,13 @@
|
|
| 1076 |
let effectiveMode = isSpeed ? currentSpeedQuestionType : (currentMode === 'hard' ? 'zh-en' : currentMode);
|
| 1077 |
let rawAnswer;
|
| 1078 |
switch (effectiveMode) {
|
| 1079 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1080 |
rawAnswer = card.english;
|
| 1081 |
answerLang = 'en';
|
| 1082 |
break;
|
|
@@ -1085,7 +1110,13 @@
|
|
| 1085 |
answerLang = 'zh';
|
| 1086 |
break;
|
| 1087 |
}
|
| 1088 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1089 |
}
|
| 1090 |
|
| 1091 |
let isCorrect;
|
|
@@ -1467,7 +1498,8 @@
|
|
| 1467 |
aiProgressContainer.classList.add('hidden');
|
| 1468 |
}
|
| 1469 |
});
|
| 1470 |
-
|
|
|
|
| 1471 |
async function callGeminiToParseText(text) {
|
| 1472 |
const apiKey = apiKeyInput.value.trim();
|
| 1473 |
if (!apiKey) {
|
|
@@ -1482,9 +1514,10 @@
|
|
| 1482 |
請分析以下從英文單字學習講義擷取的文字。你的任務是辨識出每一個單字條目,並為每個條目提取以下資訊:
|
| 1483 |
1. "english": 完整的英文單字,包含詞性標註,例如 "yesterday (adv.)" 或 "study (v.)"。
|
| 1484 |
2. "chinese": 對應的中文翻譯。
|
| 1485 |
-
3. "sentence":
|
| 1486 |
- "en": 英文例句。請將該條目的主要英文單字(不含詞性)替換成 "___"。
|
| 1487 |
- "zh": 完整的中文例句翻譯。
|
|
|
|
| 1488 |
|
| 1489 |
規則:
|
| 1490 |
- 如果一個單字條目有多個例句,請只選擇第一個或最能代表該單字用法的例句。
|
|
@@ -1514,7 +1547,8 @@
|
|
| 1514 |
type: "OBJECT",
|
| 1515 |
properties: {
|
| 1516 |
en: { type: "STRING" },
|
| 1517 |
-
zh: { type: "STRING" }
|
|
|
|
| 1518 |
}
|
| 1519 |
}
|
| 1520 |
}
|
|
@@ -1559,7 +1593,8 @@
|
|
| 1559 |
parsePdfBtn.disabled = false;
|
| 1560 |
}
|
| 1561 |
}
|
| 1562 |
-
|
|
|
|
| 1563 |
function showParseConfirmation(parsedWords) {
|
| 1564 |
parseResultsContainer.innerHTML = '';
|
| 1565 |
selectAllCheckbox.checked = true;
|
|
@@ -1576,6 +1611,7 @@
|
|
| 1576 |
<div class="mt-2 pt-2 border-t border-gray-200">
|
| 1577 |
<p class="text-sm text-gray-600"><strong>例句 (英):</strong> ${word.sentence.en}</p>
|
| 1578 |
<p class="text-sm text-gray-600"><strong>例句 (中):</strong> ${word.sentence.zh}</p>
|
|
|
|
| 1579 |
</div>
|
| 1580 |
` : ''}
|
| 1581 |
</div>
|
|
@@ -1709,8 +1745,10 @@
|
|
| 1709 |
if (!card) return;
|
| 1710 |
const questionType = currentMode === 'speed' ? currentSpeedQuestionType : (currentMode === 'hard' ? 'zh-en' : currentMode);
|
| 1711 |
let correctAnswer;
|
|
|
|
| 1712 |
switch (questionType) {
|
| 1713 |
-
case '
|
|
|
|
| 1714 |
case 'en-zh': correctAnswer = card.chinese.replace(/[\((\[【].*?[\))\]】]/g, "").trim(); break;
|
| 1715 |
default: return;
|
| 1716 |
}
|
|
@@ -2023,14 +2061,32 @@
|
|
| 2023 |
incorrectCount: word.incorrectCount || 0
|
| 2024 |
}));
|
| 2025 |
} else {
|
|
|
|
| 2026 |
const defaultWords = [
|
| 2027 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2028 |
{ english: 'study (v.)', chinese: '研讀' },
|
| 2029 |
-
{ english: '
|
| 2030 |
-
{ english: '
|
| 2031 |
-
{ english: 'a few (adj.)', chinese: '一些' },
|
| 2032 |
-
{ english: '
|
| 2033 |
-
{ english: '
|
| 2034 |
];
|
| 2035 |
words = defaultWords.map(word => ({ ...word, proficiency: 0, incorrectCount: 0 }));
|
| 2036 |
saveWordsToStorage();
|
|
@@ -2075,4 +2131,3 @@
|
|
| 2075 |
</script>
|
| 2076 |
</body>
|
| 2077 |
</html>
|
| 2078 |
-
|
|
|
|
| 421 |
<label for="edit-sentence-zh-input" class="block text-sm font-semibold text-gray-700 mb-1">中文例句</label>
|
| 422 |
<input id="edit-sentence-zh-input" type="text" class="w-full p-3 border border-gray-300 rounded-lg text-lg">
|
| 423 |
</div>
|
| 424 |
+
<!-- 【修改】新增例句答案輸入框 -->
|
| 425 |
+
<div>
|
| 426 |
+
<label for="edit-sentence-answer-input" class="block text-sm font-semibold text-gray-700 mb-1">例句正確答案 (克漏字用)</label>
|
| 427 |
+
<input id="edit-sentence-answer-input" type="text" class="w-full p-3 border border-gray-300 rounded-lg text-lg bg-yellow-50" placeholder="例如 watches, jogging...">
|
| 428 |
+
</div>
|
| 429 |
</div>
|
| 430 |
<div class="flex justify-end gap-4 mt-8">
|
| 431 |
<button type="button" id="cancel-edit-btn" class="px-6 py-2 bg-gray-200 text-gray-800 rounded-lg hover:bg-gray-300 transition-colors">取消</button>
|
|
|
|
| 661 |
const editChineseInput = document.getElementById('edit-chinese-input');
|
| 662 |
const editSentenceEnInput = document.getElementById('edit-sentence-en-input');
|
| 663 |
const editSentenceZhInput = document.getElementById('edit-sentence-zh-input');
|
| 664 |
+
const editSentenceAnswerInput = document.getElementById('edit-sentence-answer-input'); // 【新增】
|
| 665 |
const saveEditBtn = document.getElementById('save-edit-btn');
|
| 666 |
const cancelEditBtn = document.getElementById('cancel-edit-btn');
|
| 667 |
const shareModal = document.getElementById('share-modal');
|
|
|
|
| 792 |
editChineseInput.value = word.chinese;
|
| 793 |
editSentenceEnInput.value = word.sentence?.en || '';
|
| 794 |
editSentenceZhInput.value = word.sentence?.zh || '';
|
| 795 |
+
editSentenceAnswerInput.value = word.sentence?.answer || ''; // 【新增】
|
| 796 |
editWordModal.classList.remove('hidden');
|
| 797 |
} else if (button.classList.contains('delete-btn')) {
|
| 798 |
indexToDelete = index;
|
|
|
|
| 807 |
const newChinese = editChineseInput.value.trim();
|
| 808 |
const newSentenceEn = editSentenceEnInput.value.trim();
|
| 809 |
const newSentenceZh = editSentenceZhInput.value.trim();
|
| 810 |
+
const newSentenceAnswer = editSentenceAnswerInput.value.trim(); // 【新增】
|
| 811 |
|
| 812 |
if (index >= 0 && newEnglish && newChinese) {
|
| 813 |
words[index].english = newEnglish;
|
| 814 |
words[index].chinese = newChinese;
|
| 815 |
+
|
| 816 |
+
// 【修改】更新 sentence 物件
|
| 817 |
if (newSentenceEn && newSentenceZh) {
|
| 818 |
+
words[index].sentence = {
|
| 819 |
+
en: newSentenceEn,
|
| 820 |
+
zh: newSentenceZh
|
| 821 |
+
};
|
| 822 |
+
// 只有在答案存在時才加入
|
| 823 |
+
if (newSentenceAnswer) {
|
| 824 |
+
words[index].sentence.answer = newSentenceAnswer;
|
| 825 |
+
}
|
| 826 |
} else {
|
| 827 |
delete words[index].sentence;
|
| 828 |
}
|
| 829 |
+
|
| 830 |
saveWordsToStorage();
|
| 831 |
renderWordList();
|
| 832 |
editWordModal.classList.add('hidden');
|
|
|
|
| 974 |
sentenceZhDisplay.textContent = `(${card.sentence.zh})`;
|
| 975 |
sentenceZhDisplay.classList.add('hidden');
|
| 976 |
toggleTranslationBtn.textContent = '顯示翻譯';
|
| 977 |
+
backText = card.sentence.answer || card.english; // 【修改】背面顯示正確答案
|
| 978 |
answerInput.placeholder = "請填入空格中的單字...";
|
| 979 |
break;
|
| 980 |
}
|
|
|
|
| 1018 |
} else {
|
| 1019 |
wordPool = [...words];
|
| 1020 |
}
|
| 1021 |
+
|
| 1022 |
+
// 【修改】過濾克漏字模式的單字,必須包含 sentence 和 answer
|
| 1023 |
if (mode === 'sentence-cloze') {
|
| 1024 |
+
wordsForCurrentMode = wordPool.filter(w => w.sentence && w.sentence.en && w.sentence.zh && w.sentence.answer);
|
| 1025 |
if (wordsForCurrentMode.length === 0) {
|
| 1026 |
+
alert("題庫中沒有附帶完整例句(包含克漏字答案)的單字可供此模式使用。");
|
| 1027 |
return;
|
| 1028 |
}
|
| 1029 |
} else if (mode === 'review') {
|
|
|
|
| 1095 |
let effectiveMode = isSpeed ? currentSpeedQuestionType : (currentMode === 'hard' ? 'zh-en' : currentMode);
|
| 1096 |
let rawAnswer;
|
| 1097 |
switch (effectiveMode) {
|
| 1098 |
+
// 【修改】此處是核心邏輯修改
|
| 1099 |
+
case 'sentence-cloze':
|
| 1100 |
+
// 直接使用 sentence.answer 作為正確答案
|
| 1101 |
+
rawAnswer = card.sentence.answer;
|
| 1102 |
+
answerLang = 'en';
|
| 1103 |
+
break;
|
| 1104 |
+
case 'zh-en': case 'listen':
|
| 1105 |
rawAnswer = card.english;
|
| 1106 |
answerLang = 'en';
|
| 1107 |
break;
|
|
|
|
| 1110 |
answerLang = 'zh';
|
| 1111 |
break;
|
| 1112 |
}
|
| 1113 |
+
// 如果 rawAnswer 存在才進行處理
|
| 1114 |
+
if (rawAnswer) {
|
| 1115 |
+
correctAnswer = rawAnswer.replace(/[\((\[【].*?[\))\]】]/g, "").trim();
|
| 1116 |
+
} else {
|
| 1117 |
+
// 預防性程式碼,如果找不到答案就判定為錯
|
| 1118 |
+
correctAnswer = `__NO_ANSWER_DEFINED__${Date.now()}`;
|
| 1119 |
+
}
|
| 1120 |
}
|
| 1121 |
|
| 1122 |
let isCorrect;
|
|
|
|
| 1498 |
aiProgressContainer.classList.add('hidden');
|
| 1499 |
}
|
| 1500 |
});
|
| 1501 |
+
|
| 1502 |
+
// 【修改】升級 AI Prompt 和 Schema
|
| 1503 |
async function callGeminiToParseText(text) {
|
| 1504 |
const apiKey = apiKeyInput.value.trim();
|
| 1505 |
if (!apiKey) {
|
|
|
|
| 1514 |
請分析以下從英文單字學習講義擷取的文字。你的任務是辨識出每一個單字條目,並為每個條目提取以下資訊:
|
| 1515 |
1. "english": 完整的英文單字,包含詞性標註,例如 "yesterday (adv.)" 或 "study (v.)"。
|
| 1516 |
2. "chinese": 對應的中文翻譯。
|
| 1517 |
+
3. "sentence": 一個包含例句的物件。這個物件應該有以下三個屬性:
|
| 1518 |
- "en": 英文例句。請將該條目的主要英文單字(不含詞性)替換成 "___"。
|
| 1519 |
- "zh": 完整的中文例句翻譯。
|
| 1520 |
+
- "answer": 在 "en" 屬性的 "___" 空格中,文法正確的答案。例如,如果單字是 "watch",例句是 "He ___ TV.",那 "answer" 就應該是 "watches"。
|
| 1521 |
|
| 1522 |
規則:
|
| 1523 |
- 如果一個單字條目有多個例句,請只選擇第一個或最能代表該單字用法的例句。
|
|
|
|
| 1547 |
type: "OBJECT",
|
| 1548 |
properties: {
|
| 1549 |
en: { type: "STRING" },
|
| 1550 |
+
zh: { type: "STRING" },
|
| 1551 |
+
answer: { type: "STRING" } // 新增 answer 欄位
|
| 1552 |
}
|
| 1553 |
}
|
| 1554 |
}
|
|
|
|
| 1593 |
parsePdfBtn.disabled = false;
|
| 1594 |
}
|
| 1595 |
}
|
| 1596 |
+
|
| 1597 |
+
// 【修改】更新 AI 結果預覽畫面
|
| 1598 |
function showParseConfirmation(parsedWords) {
|
| 1599 |
parseResultsContainer.innerHTML = '';
|
| 1600 |
selectAllCheckbox.checked = true;
|
|
|
|
| 1611 |
<div class="mt-2 pt-2 border-t border-gray-200">
|
| 1612 |
<p class="text-sm text-gray-600"><strong>例句 (英):</strong> ${word.sentence.en}</p>
|
| 1613 |
<p class="text-sm text-gray-600"><strong>例句 (中):</strong> ${word.sentence.zh}</p>
|
| 1614 |
+
${word.sentence.answer ? `<p class="text-sm text-blue-700 font-semibold"><strong>克漏字答案:</strong> ${word.sentence.answer}</p>` : ''}
|
| 1615 |
</div>
|
| 1616 |
` : ''}
|
| 1617 |
</div>
|
|
|
|
| 1745 |
if (!card) return;
|
| 1746 |
const questionType = currentMode === 'speed' ? currentSpeedQuestionType : (currentMode === 'hard' ? 'zh-en' : currentMode);
|
| 1747 |
let correctAnswer;
|
| 1748 |
+
// 【修改】提示功能現在也使用正確的答案來源
|
| 1749 |
switch (questionType) {
|
| 1750 |
+
case 'sentence-cloze': correctAnswer = card.sentence.answer; break;
|
| 1751 |
+
case 'zh-en': case 'listen': correctAnswer = card.english.replace(/[\((\[【].*?[\))\]】]/g, "").trim(); break;
|
| 1752 |
case 'en-zh': correctAnswer = card.chinese.replace(/[\((\[【].*?[\))\]】]/g, "").trim(); break;
|
| 1753 |
default: return;
|
| 1754 |
}
|
|
|
|
| 2061 |
incorrectCount: word.incorrectCount || 0
|
| 2062 |
}));
|
| 2063 |
} else {
|
| 2064 |
+
// 【修改】使用新的預設單字,展示文法感知功能
|
| 2065 |
const defaultWords = [
|
| 2066 |
+
{
|
| 2067 |
+
english: 'watch (v.)',
|
| 2068 |
+
chinese: '觀看',
|
| 2069 |
+
sentence: {
|
| 2070 |
+
en: 'Mike usually ___ TV on Mondays.',
|
| 2071 |
+
zh: '麥克通常在週一會看電視。',
|
| 2072 |
+
answer: 'watches'
|
| 2073 |
+
}
|
| 2074 |
+
},
|
| 2075 |
+
{
|
| 2076 |
+
english: 'jog (v.)',
|
| 2077 |
+
chinese: '慢跑',
|
| 2078 |
+
sentence: {
|
| 2079 |
+
en: 'She is ___ in the park now.',
|
| 2080 |
+
zh: '她現在正在公園慢跑。',
|
| 2081 |
+
answer: 'jogging'
|
| 2082 |
+
}
|
| 2083 |
+
},
|
| 2084 |
{ english: 'study (v.)', chinese: '研讀' },
|
| 2085 |
+
{ english: 'last (adj.)', chinese: '前一個的' },
|
| 2086 |
+
{ english: 'death (n.)', chinese: '死亡' },
|
| 2087 |
+
{ english: 'a few (adj.)', chinese: '一些' },
|
| 2088 |
+
{ english: 'ago (adv.)', chinese: '以前' },
|
| 2089 |
+
{ english: 'parents (n.)', chinese: '父母親' },
|
| 2090 |
];
|
| 2091 |
words = defaultWords.map(word => ({ ...word, proficiency: 0, incorrectCount: 0 }));
|
| 2092 |
saveWordsToStorage();
|
|
|
|
| 2131 |
</script>
|
| 2132 |
</body>
|
| 2133 |
</html>
|
|
|