clean: 移除所有測試和demo頁面

刪除的頁面:
- /demo-v2: 舊版ClickableTextV2測試頁面
- /demo-v3: 包含語法修正的測試版本
- /test: 基礎測試頁面
- /test-api: API測試頁面
- /test-simple: 簡單測試頁面
- /debug: 調試頁面
- /generate-demo: 生成功能的demo版本

清理原因:
- 這些頁面都使用已刪除的ClickableText組件
- 功能已整合到正式的/generate頁面
- 沒有任何其他文件引用這些頁面
- 減少約125KB的冗餘代碼

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
鄭沛軒 2025-09-21 01:10:50 +08:00
parent 5583b763bc
commit aca9ec2f7a
7 changed files with 0 additions and 1980 deletions

View File

@ -1,100 +0,0 @@
'use client'
import { useState } from 'react'
export default function DebugPage() {
const [input, setInput] = useState('She felt ashamed of her mistake and apologized.')
const [response, setResponse] = useState('')
const [loading, setLoading] = useState(false)
const testDirectApi = async () => {
setLoading(true)
setResponse('測試中...')
try {
console.log('=== 開始API測試 ===')
console.log('輸入:', input)
const apiResponse = await fetch('http://localhost:5000/api/ai/analyze-sentence', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
inputText: input,
forceRefresh: true
})
})
console.log('API狀態:', apiResponse.status)
if (!apiResponse.ok) {
throw new Error(`API錯誤: ${apiResponse.status}`)
}
const data = await apiResponse.json()
console.log('API回應:', data)
// 直接顯示結果,不經過複雜的狀態管理
const translation = data.data?.sentenceMeaning?.translation || '無翻譯'
const explanation = data.data?.sentenceMeaning?.explanation || '無解釋'
const highValueWords = data.data?.highValueWords || []
setResponse(`
API調用成功
📖 翻譯: ${translation}
📝 解釋: ${explanation}
高價值詞彙: ${JSON.stringify(highValueWords)}
🔍 完整響應: ${JSON.stringify(data, null, 2)}
`)
} catch (error) {
console.error('API錯誤:', error)
setResponse(`❌ 錯誤: ${error}`)
} finally {
setLoading(false)
}
}
return (
<div className="p-8 max-w-4xl mx-auto">
<h1 className="text-2xl font-bold mb-6">🐛 API 調</h1>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium mb-2"></label>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
className="w-full p-3 border border-gray-300 rounded-lg"
/>
</div>
<button
onClick={testDirectApi}
disabled={loading}
className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 disabled:opacity-50"
>
{loading ? '測試中...' : '🔍 直接測試 API'}
</button>
<div className="bg-gray-100 p-4 rounded-lg">
<h3 className="font-bold mb-2"></h3>
<pre className="text-sm whitespace-pre-wrap">{response || '點擊按鈕開始測試'}</pre>
</div>
<div className="bg-yellow-100 p-4 rounded-lg">
<h3 className="font-bold mb-2">💡 </h3>
<p className="text-sm">
調API
</p>
</div>
</div>
</div>
)
}

View File

@ -1,572 +0,0 @@
'use client'
import { useState } from 'react'
import { ClickableTextV2 } from '@/components/ClickableTextV2'
export default function DemoV2Page() {
const [mode, setMode] = useState<'manual' | 'screenshot'>('manual')
const [textInput, setTextInput] = useState('')
const [isAnalyzing, setIsAnalyzing] = useState(false)
const [showAnalysisView, setShowAnalysisView] = useState(false)
const [sentenceAnalysis, setSentenceAnalysis] = useState<any>(null)
const [sentenceMeaning, setSentenceMeaning] = useState('')
const [usageCount, setUsageCount] = useState(0)
const [isPremium] = useState(false)
// 模擬分析後的句子資料(新版本)
const mockSentenceAnalysis = {
meaning: "他在我們的會議中提出了這件事,但沒有人同意。這句話表達了在會議中有人提出某個議題或想法,但得不到其他與會者的認同。",
highValueWords: ["brought", "up", "meeting", "agreed"], // 高價值詞彙
phrasesDetected: [
{
phrase: "bring up",
words: ["brought", "up"],
colorCode: "#F59E0B"
}
],
words: {
"he": {
word: "he",
translation: "他",
definition: "Used to refer to a male person or animal",
partOfSpeech: "pronoun",
pronunciation: "/hiː/",
synonyms: ["him", "that man"],
antonyms: [],
isPhrase: false,
isHighValue: false, // 低價值詞彙
learningPriority: "low",
difficultyLevel: "A1",
costIncurred: 1
},
"brought": {
word: "brought",
translation: "帶來、提出",
definition: "Past tense of bring; to take or carry something to a place",
partOfSpeech: "verb",
pronunciation: "/brɔːt/",
synonyms: ["carried", "took", "delivered"],
antonyms: ["removed", "took away"],
isPhrase: true,
isHighValue: true, // 高價值片語
learningPriority: "high",
phraseInfo: {
phrase: "bring up",
meaning: "提出(話題)、養育",
warning: "在這個句子中,\"brought up\" 是一個片語,意思是\"提出話題\",而不是單純的\"帶來\"",
colorCode: "#F59E0B"
},
difficultyLevel: "B1",
costIncurred: 0 // 高價值免費
},
"this": {
word: "this",
translation: "這個",
definition: "Used to indicate something near or just mentioned",
partOfSpeech: "pronoun",
pronunciation: "/ðɪs/",
synonyms: ["that", "it"],
antonyms: [],
isPhrase: false,
isHighValue: false, // 低價值詞彙
learningPriority: "low",
difficultyLevel: "A1",
costIncurred: 1
},
"thing": {
word: "thing",
translation: "事情、東西",
definition: "An object, fact, or situation",
partOfSpeech: "noun",
pronunciation: "/θɪŋ/",
synonyms: ["object", "matter", "item"],
antonyms: [],
isPhrase: false,
isHighValue: false, // 低價值詞彙
learningPriority: "low",
difficultyLevel: "A1",
costIncurred: 1
},
"up": {
word: "up",
translation: "向上",
definition: "Toward a higher place or position",
partOfSpeech: "adverb",
pronunciation: "/ʌp/",
synonyms: ["upward", "above"],
antonyms: ["down", "below"],
isPhrase: true,
isHighValue: true, // 高價值片語部分
learningPriority: "high",
phraseInfo: {
phrase: "bring up",
meaning: "提出(話題)、養育",
warning: "\"up\" 在這裡是片語 \"bring up\" 的一部分,不是單獨的\"向上\"的意思",
colorCode: "#F59E0B"
},
difficultyLevel: "B1",
costIncurred: 0 // 高價值免費
},
"during": {
word: "during",
translation: "在...期間",
definition: "Throughout the course or duration of",
partOfSpeech: "preposition",
pronunciation: "/ˈdjʊərɪŋ/",
synonyms: ["throughout", "while"],
antonyms: [],
isPhrase: false,
isHighValue: false, // 低價值詞彙
learningPriority: "low",
difficultyLevel: "A2",
costIncurred: 1
},
"our": {
word: "our",
translation: "我們的",
definition: "Belonging to us",
partOfSpeech: "pronoun",
pronunciation: "/aʊər/",
synonyms: ["ours"],
antonyms: [],
isPhrase: false,
isHighValue: false, // 低價值詞彙
learningPriority: "low",
difficultyLevel: "A1",
costIncurred: 1
},
"meeting": {
word: "meeting",
translation: "會議",
definition: "An organized gathering of people for discussion",
partOfSpeech: "noun",
pronunciation: "/ˈmiːtɪŋ/",
synonyms: ["conference", "assembly", "gathering"],
antonyms: [],
isPhrase: false,
isHighValue: true, // 高價值單字B2級
learningPriority: "high",
difficultyLevel: "B2",
costIncurred: 0 // 高價值免費
},
"and": {
word: "and",
translation: "和、而且",
definition: "Used to connect words or clauses",
partOfSpeech: "conjunction",
pronunciation: "/ænd/",
synonyms: ["plus", "also"],
antonyms: [],
isPhrase: false,
isHighValue: false, // 低價值詞彙
learningPriority: "low",
difficultyLevel: "A1",
costIncurred: 1
},
"no": {
word: "no",
translation: "沒有",
definition: "Not any; not one",
partOfSpeech: "determiner",
pronunciation: "/nəʊ/",
synonyms: ["none", "zero"],
antonyms: ["some", "any"],
isPhrase: false,
isHighValue: false, // 低價值詞彙
learningPriority: "low",
difficultyLevel: "A1",
costIncurred: 1
},
"one": {
word: "one",
translation: "一個人、任何人",
definition: "A single person or thing",
partOfSpeech: "pronoun",
pronunciation: "/wʌn/",
synonyms: ["someone", "anybody"],
antonyms: ["none", "nobody"],
isPhrase: false,
isHighValue: false, // 低價值詞彙
learningPriority: "low",
difficultyLevel: "A1",
costIncurred: 1
},
"agreed": {
word: "agreed",
translation: "同意",
definition: "Past tense of agree; to have the same opinion",
partOfSpeech: "verb",
pronunciation: "/əˈɡriːd/",
synonyms: ["consented", "accepted", "approved"],
antonyms: ["disagreed", "refused"],
isPhrase: false,
isHighValue: true, // 高價值單字B1級
learningPriority: "medium",
difficultyLevel: "B1",
costIncurred: 0 // 高價值免費
}
}
}
// 處理句子分析
const handleAnalyzeSentence = async () => {
if (!textInput.trim()) return
// 檢查使用次數限制
if (!isPremium && usageCount >= 5) {
alert('❌ 免費用戶 3 小時內只能分析 5 次句子,請稍後再試或升級到付費版本')
return
}
setIsAnalyzing(true)
try {
// 模擬 API 調用
await new Promise(resolve => setTimeout(resolve, 2000))
setSentenceAnalysis(mockSentenceAnalysis.words)
setSentenceMeaning(mockSentenceAnalysis.meaning)
setShowAnalysisView(true)
setUsageCount(prev => prev + 1) // 句子分析扣除1次
} catch (error) {
console.error('Error analyzing sentence:', error)
alert('分析句子時發生錯誤,請稍後再試')
} finally {
setIsAnalyzing(false)
}
}
const getHighValueCount = () => {
if (!sentenceAnalysis) return 0
return Object.values(sentenceAnalysis).filter((word: any) => word.isHighValue).length
}
const getLowValueCount = () => {
if (!sentenceAnalysis) return 0
return Object.values(sentenceAnalysis).filter((word: any) => !word.isHighValue).length
}
return (
<div className="min-h-screen bg-gray-50">
{/* Navigation */}
<nav className="bg-white shadow-sm">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16">
<div className="flex items-center">
<h1 className="text-2xl font-bold text-blue-600">DramaLing</h1>
</div>
<div className="flex items-center space-x-4">
<span className="text-gray-600"> v2.0</span>
</div>
</div>
</div>
</nav>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{!showAnalysisView ? (
<div className="max-w-4xl mx-auto">
<h1 className="text-3xl font-bold mb-8">AI - </h1>
{/* 功能說明 */}
<div className="bg-gradient-to-r from-blue-50 to-purple-50 rounded-xl p-6 mb-6 border border-blue-200">
<h2 className="text-lg font-semibold mb-3 text-blue-800">🎯 </h2>
<div className="grid md:grid-cols-2 gap-4 text-sm">
<div className="space-y-2">
<div className="flex items-center gap-2">
<div className="w-4 h-4 bg-yellow-400 border-2 border-yellow-500 rounded"></div>
<span><strong></strong> - </span>
</div>
<div className="flex items-center gap-2">
<div className="w-4 h-4 bg-green-400 border-2 border-green-500 rounded"></div>
<span><strong></strong> - </span>
</div>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<div className="w-4 h-4 border-b-2 border-blue-400"></div>
<span><strong></strong> - 1 </span>
</div>
<div className="flex items-center gap-2">
<span className="text-lg"></span>
<span><strong></strong> - </span>
</div>
</div>
</div>
</div>
{/* Input Mode Selection */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<h2 className="text-lg font-semibold mb-4"></h2>
<div className="grid grid-cols-2 gap-4">
<button
onClick={() => setMode('manual')}
className={`p-4 rounded-lg border-2 transition-all ${
mode === 'manual'
? 'border-blue-600 bg-blue-50'
: 'border-gray-200 hover:border-gray-300'
}`}
>
<div className="text-2xl mb-2"></div>
<div className="font-semibold"></div>
<div className="text-sm text-gray-600 mt-1"></div>
</button>
<button
onClick={() => setMode('screenshot')}
disabled={!isPremium}
className={`p-4 rounded-lg border-2 transition-all relative ${
mode === 'screenshot'
? 'border-blue-600 bg-blue-50'
: isPremium
? 'border-gray-200 hover:border-gray-300'
: 'border-gray-200 bg-gray-100 cursor-not-allowed opacity-60'
}`}
>
<div className="text-2xl mb-2">📷</div>
<div className="font-semibold"></div>
<div className="text-sm text-gray-600 mt-1"> (Phase 2)</div>
{!isPremium && (
<div className="absolute top-2 right-2 px-2 py-1 bg-yellow-100 text-yellow-700 text-xs rounded-full">
</div>
)}
</button>
</div>
</div>
{/* Content Input */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<div>
<h2 className="text-lg font-semibold mb-4"></h2>
<textarea
value={textInput}
onChange={(e) => {
const value = e.target.value
if (mode === 'manual' && value.length > 50) {
return // 阻止輸入超過50字
}
setTextInput(value)
}}
placeholder={mode === 'manual'
? "輸入英文句子最多50字..."
: "貼上您想要學習的英文文本,例如影劇對話、文章段落..."
}
className={`w-full h-40 px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-600 focus:border-transparent outline-none resize-none ${
mode === 'manual' && textInput.length >= 45 ? 'border-yellow-400' :
mode === 'manual' && textInput.length >= 50 ? 'border-red-400' : 'border-gray-300'
}`}
/>
<div className="mt-2 flex justify-between text-sm">
<span className={`${
mode === 'manual' && textInput.length >= 45 ? 'text-yellow-600' :
mode === 'manual' && textInput.length >= 50 ? 'text-red-600' : 'text-gray-600'
}`}>
{mode === 'manual' ? `最多 50 字元 • 目前:${textInput.length} 字元` : `最多 5000 字元 • 目前:${textInput.length} 字元`}
</span>
{mode === 'manual' && textInput.length > 40 && (
<span className={textInput.length >= 50 ? 'text-red-600' : 'text-yellow-600'}>
{textInput.length >= 50 ? '已達上限!' : `還可輸入 ${50 - textInput.length} 字元`}
</span>
)}
</div>
{/* 預設示例 */}
{!textInput && (
<div className="mt-4 p-3 bg-gray-50 rounded-lg">
<div className="text-sm text-gray-700 mb-2">
<strong></strong>
</div>
<button
onClick={() => setTextInput("He brought this thing up during our meeting and no one agreed.")}
className="text-sm text-blue-600 hover:text-blue-800 bg-blue-50 px-3 py-1 rounded border border-blue-200"
>
使He brought this thing up during our meeting and no one agreed.
</button>
</div>
)}
</div>
</div>
{/* 新的按鈕區域 */}
<div className="space-y-4">
{/* 分析句子按鈕 */}
<button
onClick={handleAnalyzeSentence}
disabled={isAnalyzing || (mode === 'manual' && (!textInput || textInput.length > 50)) || (mode === 'screenshot')}
className="w-full bg-blue-600 text-white py-4 rounded-lg font-semibold hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{isAnalyzing ? (
<span className="flex items-center justify-center">
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
...
</span>
) : (
'🔍 分析句子並標記高價值詞彙'
)}
</button>
{/* 使用次數顯示 */}
<div className="text-center text-sm text-gray-600">
{isPremium ? (
<span className="text-green-600">🌟 使</span>
) : (
<span className={usageCount >= 4 ? 'text-red-600' : usageCount >= 3 ? 'text-yellow-600' : 'text-gray-600'}>
使 {usageCount}/5 (3)
{usageCount >= 5 && <span className="block text-red-500 mt-1"></span>}
</span>
)}
</div>
</div>
</div>
) : (
/* 句子分析視圖 */
<div className="max-w-4xl mx-auto">
<div className="flex items-center justify-between mb-6">
<h1 className="text-3xl font-bold"> - </h1>
<button
onClick={() => setShowAnalysisView(false)}
className="text-gray-600 hover:text-gray-900"
>
</button>
</div>
{/* 分析統計 */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<h2 className="text-lg font-semibold mb-4"></h2>
<div className="grid grid-cols-3 gap-4">
<div className="text-center p-3 bg-green-50 rounded-lg">
<div className="text-2xl font-bold text-green-600">{getHighValueCount()}</div>
<div className="text-sm text-green-700"></div>
<div className="text-xs text-green-600"> </div>
</div>
<div className="text-center p-3 bg-blue-50 rounded-lg">
<div className="text-2xl font-bold text-blue-600">{getLowValueCount()}</div>
<div className="text-sm text-blue-700"></div>
<div className="text-xs text-blue-600">💰 </div>
</div>
<div className="text-center p-3 bg-gray-50 rounded-lg">
<div className="text-2xl font-bold text-gray-600">1</div>
<div className="text-sm text-gray-700"></div>
<div className="text-xs text-gray-600">🔍 </div>
</div>
</div>
</div>
{/* 原始句子顯示 */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<h2 className="text-lg font-semibold mb-4"></h2>
<div className="bg-gray-50 p-4 rounded-lg mb-4">
<div className="text-lg leading-relaxed">
{textInput}
</div>
</div>
<h3 className="text-base font-semibold mb-2"></h3>
<div className="text-gray-700 leading-relaxed">
{sentenceMeaning}
</div>
</div>
{/* 互動式文字 */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<h2 className="text-lg font-semibold mb-4"> - </h2>
{/* 圖例說明 */}
<div className="p-4 bg-gradient-to-r from-blue-50 to-green-50 rounded-lg border border-blue-200 mb-4">
<p className="text-sm text-blue-800 mb-3">
<strong>💡 </strong>AI
</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 text-xs">
<div className="flex items-center gap-2">
<div className="px-2 py-1 bg-yellow-100 border-2 border-yellow-400 rounded">brought</div>
<span></span>
</div>
<div className="flex items-center gap-2">
<div className="px-2 py-1 bg-green-100 border-2 border-green-400 rounded">meeting</div>
<span></span>
</div>
<div className="flex items-center gap-2">
<div className="px-2 py-1 border-b border-blue-300">thing</div>
<span>1</span>
</div>
</div>
</div>
<div className="p-6 bg-gray-50 rounded-lg border-2 border-dashed border-gray-300">
<ClickableTextV2
text={textInput}
analysis={sentenceAnalysis}
highValueWords={mockSentenceAnalysis.highValueWords}
phrasesDetected={mockSentenceAnalysis.phrasesDetected}
remainingUsage={5 - usageCount}
onWordClick={(word, analysis) => {
console.log('Clicked word:', word, analysis)
}}
onWordCostConfirm={async (word, cost) => {
if (usageCount >= 5) {
alert('❌ 使用額度不足,無法查詢低價值詞彙')
return false
}
// 這裡可以顯示更詳細的確認對話框
const confirmed = window.confirm(
`查詢 "${word}" 將消耗 ${cost} 次使用額度,您剩餘 ${5 - usageCount} 次。\n\n是否繼續`
)
if (confirmed) {
setUsageCount(prev => prev + cost)
return true
}
return false
}}
/>
</div>
</div>
{/* 使用統計 */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<h2 className="text-lg font-semibold mb-4">使</h2>
<div className="grid grid-cols-2 gap-4">
<div>
<div className="text-sm text-gray-600"></div>
<div className="text-2xl font-bold text-blue-600">{usageCount}</div>
<div className="text-xs text-gray-500"> 1 </div>
</div>
<div>
<div className="text-sm text-gray-600"></div>
<div className={`text-2xl font-bold ${5 - usageCount <= 1 ? 'text-red-600' : 'text-green-600'}`}>
{5 - usageCount}
</div>
<div className="text-xs text-gray-500">
{isPremium ? '無限制' : '3小時內重置'}
</div>
</div>
</div>
</div>
{/* 操作按鈕 */}
<div className="bg-white rounded-xl shadow-sm p-6">
<div className="flex gap-4">
<button
onClick={() => setShowAnalysisView(false)}
className="flex-1 bg-gray-200 text-gray-700 py-3 rounded-lg font-medium hover:bg-gray-300 transition-colors"
>
🔄
</button>
<button
onClick={() => alert('詞卡生成功能開發中...')}
className="flex-1 bg-blue-600 text-white py-3 rounded-lg font-medium hover:bg-blue-700 transition-colors"
>
📖
</button>
</div>
</div>
</div>
)}
</div>
</div>
)
}

View File

@ -1,594 +0,0 @@
'use client'
import { useState, useEffect } from 'react'
import { ClickableTextV2 } from '@/components/ClickableTextV2'
import { GrammarCorrectionPanel } from '@/components/GrammarCorrectionPanel'
export default function DemoV3Page() {
const [mode, setMode] = useState<'manual' | 'screenshot'>('manual')
const [textInput, setTextInput] = useState('')
const [isAnalyzing, setIsAnalyzing] = useState(false)
const [showAnalysisView, setShowAnalysisView] = useState(false)
const [sentenceAnalysis, setSentenceAnalysis] = useState<any>(null)
const [sentenceMeaning, setSentenceMeaning] = useState('')
const [grammarCorrection, setGrammarCorrection] = useState<any>(null)
const [finalText, setFinalText] = useState('') // 最終用於分析的文本
const [usageCount, setUsageCount] = useState(0)
const [isPremium] = useState(false)
const [apiConnected, setApiConnected] = useState(false)
// 模擬正確句子的分析資料
const mockCorrectSentenceAnalysis = {
meaning: "他在我們的會議中提出了這件事,但沒有人同意。這句話表達了在會議中有人提出某個議題或想法,但得不到其他與會者的認同。",
grammarCorrection: {
hasErrors: false,
originalText: "He brought this thing up during our meeting and no one agreed.",
correctedText: null,
corrections: [],
confidenceScore: 0.98
},
highValueWords: ["brought", "up", "meeting", "agreed"],
words: {
"brought": {
word: "brought",
translation: "帶來、提出",
definition: "Past tense of bring; to take or carry something to a place",
partOfSpeech: "verb",
pronunciation: "/brɔːt/",
synonyms: ["carried", "took", "delivered"],
antonyms: ["removed", "took away"],
isPhrase: true,
isHighValue: true,
learningPriority: "high",
phraseInfo: {
phrase: "bring up",
meaning: "提出(話題)、養育",
warning: "在這個句子中,\"brought up\" 是一個片語,意思是\"提出話題\",而不是單純的\"帶來\"",
colorCode: "#F59E0B"
},
difficultyLevel: "B1"
},
"meeting": {
word: "meeting",
translation: "會議",
definition: "An organized gathering of people for discussion",
partOfSpeech: "noun",
pronunciation: "/ˈmiːtɪŋ/",
synonyms: ["conference", "assembly", "gathering"],
antonyms: [],
isPhrase: false,
isHighValue: true,
learningPriority: "high",
difficultyLevel: "B2"
},
"thing": {
word: "thing",
translation: "事情、東西",
definition: "An object, fact, or situation",
partOfSpeech: "noun",
pronunciation: "/θɪŋ/",
synonyms: ["object", "matter", "item"],
antonyms: [],
isPhrase: false,
isHighValue: false,
learningPriority: "low",
difficultyLevel: "A1",
costIncurred: 1
}
}
}
// 模擬有語法錯誤的句子分析資料
const mockErrorSentenceAnalysis = {
meaning: "我昨天去學校遇見了我的朋友們。這句話描述了過去發生的事情,表達了去學校並遇到朋友的經歷。",
grammarCorrection: {
hasErrors: true,
originalText: "I go to school yesterday and meet my friends.",
correctedText: "I went to school yesterday and met my friends.",
corrections: [
{
position: { start: 2, end: 4 },
errorType: "tense_mismatch",
original: "go",
corrected: "went",
reason: "過去式時態修正:句子中有 'yesterday',應使用過去式",
severity: "high"
},
{
position: { start: 29, end: 33 },
errorType: "tense_mismatch",
original: "meet",
corrected: "met",
reason: "過去式時態修正:與 'went' 保持時態一致",
severity: "high"
}
],
confidenceScore: 0.95
},
highValueWords: ["went", "yesterday", "met", "friends"],
words: {
"went": {
word: "went",
translation: "去、前往",
definition: "Past tense of go; to move or travel to a place",
partOfSpeech: "verb",
pronunciation: "/went/",
synonyms: ["traveled", "moved", "proceeded"],
antonyms: ["stayed", "remained"],
isPhrase: false,
isHighValue: true,
learningPriority: "high",
difficultyLevel: "A2"
},
"yesterday": {
word: "yesterday",
translation: "昨天",
definition: "The day before today",
partOfSpeech: "adverb",
pronunciation: "/ˈjestədeɪ/",
synonyms: ["the day before"],
antonyms: ["tomorrow", "today"],
isPhrase: false,
isHighValue: true,
learningPriority: "medium",
difficultyLevel: "A1"
},
"met": {
word: "met",
translation: "遇見、認識",
definition: "Past tense of meet; to encounter or come together with",
partOfSpeech: "verb",
pronunciation: "/met/",
synonyms: ["encountered", "saw", "found"],
antonyms: ["avoided", "missed"],
isPhrase: false,
isHighValue: true,
learningPriority: "high",
difficultyLevel: "A2"
},
"friends": {
word: "friends",
translation: "朋友們",
definition: "People you like and know well",
partOfSpeech: "noun",
pronunciation: "/frends/",
synonyms: ["companions", "buddies", "pals"],
antonyms: ["enemies", "strangers"],
isPhrase: false,
isHighValue: true,
learningPriority: "medium",
difficultyLevel: "A1"
},
"school": {
word: "school",
translation: "學校",
definition: "A place where children go to learn",
partOfSpeech: "noun",
pronunciation: "/skuːl/",
synonyms: ["educational institution"],
antonyms: [],
isPhrase: false,
isHighValue: false,
learningPriority: "low",
difficultyLevel: "A1",
costIncurred: 1
}
}
}
// 處理句子分析 - 使用真實API
const handleAnalyzeSentence = async () => {
if (!textInput.trim()) return
if (!isPremium && usageCount >= 5) {
alert('❌ 免費用戶 3 小時內只能分析 5 次句子,請稍後再試或升級到付費版本')
return
}
setIsAnalyzing(true)
try {
console.log('🚀 開始API調用')
console.log('📝 輸入文本:', textInput)
console.log('🌐 API URL:', 'http://localhost:5000/api/ai/analyze-sentence')
// 調用真實的後端API
const response = await fetch('http://localhost:5000/api/ai/analyze-sentence', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
inputText: textInput,
analysisMode: 'full',
forceRefresh: true // 暫時強制刷新,避免舊快取問題
})
})
console.log('📡 API響應狀態:', response.status, response.statusText)
console.log('📦 響應頭:', [...response.headers.entries()])
if (!response.ok) {
const errorText = await response.text()
console.log('❌ 錯誤響應內容:', errorText)
throw new Error(`API 錯誤: ${response.status} ${response.statusText} - ${errorText}`)
}
const result = await response.json()
console.log('✅ API響應數據:', result)
if (result.success) {
console.log('💫 開始更新前端狀態')
// 確保數據結構完整
const wordAnalysis = result.data.wordAnalysis || {}
const sentenceMeaning = result.data.sentenceMeaning || {}
const grammarCorrection = result.data.grammarCorrection || { hasErrors: false }
const finalText = result.data.finalAnalysisText || textInput
console.log('📊 詞彙分析詞數:', Object.keys(wordAnalysis).length)
console.log('🎯 高價值詞彙:', result.data.highValueWords)
console.log('📝 翻譯內容:', sentenceMeaning.translation)
// 批次更新狀態,避免競態條件
setSentenceAnalysis(wordAnalysis)
setSentenceMeaning((sentenceMeaning.translation || '翻譯處理中...') + ' ' + (sentenceMeaning.explanation || '解釋處理中...'))
setGrammarCorrection(grammarCorrection)
setFinalText(finalText)
// 延遲顯示分析視圖,確保狀態更新完成
setTimeout(() => {
setShowAnalysisView(true)
console.log('✅ 分析視圖已顯示')
}, 100)
setUsageCount(prev => prev + 1)
console.log('🎉 狀態更新完成')
} else {
throw new Error(result.error || '分析失敗')
}
} catch (error) {
console.error('❌ API錯誤詳情:', error)
// 不要自動回退到模擬資料,讓用戶知道真實錯誤
alert(`🔌 無法連接到後端API:\n\n${error instanceof Error ? error.message : '未知錯誤'}\n\n請檢查:\n1. 後端服務是否運行在 localhost:5000\n2. CORS 設定是否正確\n3. 網路連接是否正常`)
// 重置分析狀態
setShowAnalysisView(false)
} finally {
setIsAnalyzing(false)
}
}
const handleAcceptCorrection = () => {
if (grammarCorrection?.correctedText) {
setFinalText(grammarCorrection.correctedText)
// 這裡可以重新分析修正後的句子
alert('✅ 已採用修正版本,後續學習將基於正確的句子進行!')
}
}
const handleRejectCorrection = () => {
setFinalText(grammarCorrection?.originalText || textInput)
alert('📝 已保持原始版本,將基於您的原始輸入進行學習。')
}
// 檢查API連接狀態
useEffect(() => {
const checkApiConnection = async () => {
try {
const response = await fetch('http://localhost:5000/api/ai/test/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ inputText: 'test', extractionType: 'vocabulary', cardCount: 1 })
})
setApiConnected(response.ok)
} catch (error) {
setApiConnected(false)
}
}
checkApiConnection()
}, [])
return (
<div className="min-h-screen bg-gray-50">
{/* Navigation */}
<nav className="bg-white shadow-sm">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16">
<div className="flex items-center">
<h1 className="text-2xl font-bold text-blue-600">DramaLing</h1>
</div>
<div className="flex items-center space-x-4">
<span className="text-green-600 font-medium">🔗 API整合 v3.0</span>
<span className={`text-xs px-2 py-1 rounded-full ${
apiConnected
? 'bg-green-100 text-green-700'
: 'bg-yellow-100 text-yellow-700'
}`}>
{apiConnected ? '✅ 後端已連接' : '⏳ 檢查中...'}
</span>
</div>
</div>
</div>
</nav>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{!showAnalysisView ? (
<div className="max-w-4xl mx-auto">
<h1 className="text-3xl font-bold mb-8">AI + </h1>
{/* API 連接狀態 */}
<div className={`p-4 rounded-lg mb-6 border ${
apiConnected
? 'bg-green-50 border-green-200'
: 'bg-red-50 border-red-200'
}`}>
<div className="flex items-center gap-2">
<span className="text-lg">
{apiConnected ? '✅' : '❌'}
</span>
<span className={`font-medium ${
apiConnected ? 'text-green-800' : 'text-red-800'
}`}>
API : {apiConnected ? '已連接' : '未連接'}
</span>
</div>
{!apiConnected && (
<p className="text-red-700 text-sm mt-2">
http://localhost:5000 運行
</p>
)}
</div>
{/* 功能說明 */}
<div className="bg-gradient-to-r from-red-50 to-green-50 rounded-xl p-6 mb-6 border border-red-200">
<h2 className="text-lg font-semibold mb-3 text-red-800">🔧 + </h2>
<div className="grid md:grid-cols-2 gap-4 text-sm">
<div className="space-y-2">
<div className="flex items-center gap-2">
<span className="text-lg"></span>
<span><strong></strong> - 9</span>
</div>
<div className="flex items-center gap-2">
<span className="text-lg">🔧</span>
<span><strong></strong> - </span>
</div>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<span className="text-lg"></span>
<span><strong></strong> - </span>
</div>
<div className="flex items-center gap-2">
<span className="text-lg">💰</span>
<span><strong></strong> - </span>
</div>
</div>
</div>
</div>
{/* Content Input */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<h2 className="text-lg font-semibold mb-4"> (300)</h2>
<textarea
value={textInput}
onChange={(e) => {
const value = e.target.value
if (mode === 'manual' && value.length > 300) {
return // 阻止輸入超過300字
}
setTextInput(value)
}}
placeholder={mode === 'manual'
? "輸入英文句子最多300字..."
: "貼上您想要學習的英文文本,例如影劇對話、文章段落..."
}
className={`w-full h-40 px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-600 focus:border-transparent outline-none resize-none ${
mode === 'manual' && textInput.length >= 280 ? 'border-yellow-400' :
mode === 'manual' && textInput.length >= 300 ? 'border-red-400' : 'border-gray-300'
}`}
/>
<div className="mt-2 flex justify-between text-sm">
<span className={`${
mode === 'manual' && textInput.length >= 280 ? 'text-yellow-600' :
mode === 'manual' && textInput.length >= 300 ? 'text-red-600' : 'text-gray-600'
}`}>
{mode === 'manual' ? `最多 300 字元 • 目前:${textInput.length} 字元` : `最多 5000 字元 • 目前:${textInput.length} 字元`}
</span>
{mode === 'manual' && textInput.length > 250 && (
<span className={textInput.length >= 300 ? 'text-red-600' : 'text-yellow-600'}>
{textInput.length >= 300 ? '已達上限!' : `還可輸入 ${300 - textInput.length} 字元`}
</span>
)}
</div>
{/* 預設示例 - 包含語法錯誤和正確的句子 */}
{!textInput && (
<div className="mt-4 space-y-3">
<div className="p-3 bg-gray-50 rounded-lg">
<div className="text-sm text-gray-700 mb-2">
<strong> </strong>
</div>
<button
onClick={() => setTextInput("He brought this thing up during our meeting and no one agreed.")}
className="text-sm text-green-600 hover:text-green-800 bg-green-50 px-3 py-1 rounded border border-green-200 w-full text-left"
>
He brought this thing up during our meeting and no one agreed.
</button>
</div>
<div className="p-3 bg-red-50 rounded-lg">
<div className="text-sm text-red-700 mb-2">
<strong> </strong>
</div>
<button
onClick={() => setTextInput("I go to school yesterday and meet my friends.")}
className="text-sm text-red-600 hover:text-red-800 bg-red-50 px-3 py-1 rounded border border-red-200 w-full text-left"
>
I go to school yesterday and meet my friends.
</button>
</div>
</div>
)}
</div>
{/* 分析按鈕 */}
<div className="space-y-4">
<button
onClick={handleAnalyzeSentence}
disabled={isAnalyzing || (mode === 'manual' && (!textInput || textInput.length > 300)) || (mode === 'screenshot')}
className="w-full bg-blue-600 text-white py-4 rounded-lg font-semibold hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{isAnalyzing ? (
<span className="flex items-center justify-center">
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
...
</span>
) : (
'🔍 語法檢查 + 高價值詞彙分析'
)}
</button>
<div className="text-center text-sm text-gray-600">
{isPremium ? (
<span className="text-green-600">🌟 使</span>
) : (
<span className={usageCount >= 4 ? 'text-red-600' : usageCount >= 3 ? 'text-yellow-600' : 'text-gray-600'}>
使 {usageCount}/5 (3)
{usageCount >= 5 && <span className="block text-red-500 mt-1"></span>}
</span>
)}
</div>
</div>
</div>
) : (
/* 句子分析視圖 */
<div className="max-w-4xl mx-auto">
<div className="flex items-center justify-between mb-6">
<h1 className="text-3xl font-bold"> + </h1>
<button
onClick={() => setShowAnalysisView(false)}
className="text-gray-600 hover:text-gray-900"
>
</button>
</div>
{/* 語法修正面板 */}
{grammarCorrection && (
<GrammarCorrectionPanel
correction={grammarCorrection}
onAcceptCorrection={handleAcceptCorrection}
onRejectCorrection={handleRejectCorrection}
/>
)}
{/* 原始句子 vs 分析句子 */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<h2 className="text-lg font-semibold mb-4"></h2>
<div className="grid md:grid-cols-2 gap-4">
<div>
<h3 className="text-sm font-medium text-gray-700 mb-2">📝 </h3>
<div className="p-3 bg-gray-50 rounded-lg border">
<div className="text-base">{textInput}</div>
</div>
</div>
<div>
<h3 className="text-sm font-medium text-gray-700 mb-2">🎯 </h3>
<div className="p-3 bg-blue-50 rounded-lg border border-blue-200">
<div className="text-base font-medium">{finalText}</div>
{finalText !== textInput && (
<div className="text-xs text-blue-600 mt-1"> </div>
)}
</div>
</div>
</div>
<div className="mt-4">
<h3 className="text-sm font-medium text-gray-700 mb-2">📖 </h3>
<div className="text-gray-700 leading-relaxed p-3 bg-gray-50 rounded-lg">
{sentenceMeaning}
</div>
</div>
</div>
{/* 互動式文字 */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<h2 className="text-lg font-semibold mb-4"> - </h2>
<div className="p-4 bg-gradient-to-r from-blue-50 to-green-50 rounded-lg border border-blue-200 mb-4">
<p className="text-sm text-blue-800 mb-3">
<strong>💡 {finalText !== textInput ? '修正後' : '原始'}</strong>
</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 text-xs">
<div className="flex items-center gap-2">
<div className="px-2 py-1 bg-yellow-100 border-2 border-yellow-400 rounded"></div>
<span></span>
</div>
<div className="flex items-center gap-2">
<div className="px-2 py-1 bg-green-100 border-2 border-green-400 rounded"></div>
<span></span>
</div>
<div className="flex items-center gap-2">
<div className="px-2 py-1 border-b border-blue-300"></div>
<span>1</span>
</div>
</div>
</div>
<div className="p-6 bg-gray-50 rounded-lg border-2 border-dashed border-gray-300">
<ClickableTextV2
text={finalText}
analysis={sentenceAnalysis}
remainingUsage={5 - usageCount}
onWordClick={(word, analysis) => {
console.log('Clicked word:', word, analysis)
}}
onWordCostConfirm={async (word, cost) => {
if (usageCount >= 5) {
alert('❌ 使用額度不足,無法查詢低價值詞彙')
return false
}
const confirmed = window.confirm(
`查詢 "${word}" 將消耗 ${cost} 次使用額度,您剩餘 ${5 - usageCount} 次。\n\n是否繼續`
)
if (confirmed) {
setUsageCount(prev => prev + cost)
return true
}
return false
}}
/>
</div>
</div>
{/* 操作按鈕 */}
<div className="bg-white rounded-xl shadow-sm p-6">
<div className="flex gap-4">
<button
onClick={() => setShowAnalysisView(false)}
className="flex-1 bg-gray-200 text-gray-700 py-3 rounded-lg font-medium hover:bg-gray-300 transition-colors"
>
🔄
</button>
<button
onClick={() => alert('詞卡生成功能開發中...')}
className="flex-1 bg-blue-600 text-white py-3 rounded-lg font-medium hover:bg-blue-700 transition-colors"
>
📖
</button>
</div>
</div>
</div>
)}
</div>
</div>
)
}

View File

@ -1,389 +0,0 @@
'use client'
import { useState } from 'react'
import { ClickableText } from '@/components/ClickableText'
export default function GenerateDemoPage() {
const [mode, setMode] = useState<'manual' | 'screenshot'>('manual')
const [textInput, setTextInput] = useState('')
const [extractionType, setExtractionType] = useState<'vocabulary' | 'smart'>('vocabulary')
const [cardCount, setCardCount] = useState(10)
const [isAnalyzing, setIsAnalyzing] = useState(false)
const [showAnalysisView, setShowAnalysisView] = useState(false)
const [sentenceAnalysis, setSentenceAnalysis] = useState<any>(null)
const [sentenceMeaning, setSentenceMeaning] = useState('')
const [usageCount, setUsageCount] = useState(0)
const [isPremium] = useState(false)
// 模擬分析後的句子資料
const mockSentenceAnalysis = {
meaning: "他在我們的會議中提出了這件事,但沒有人同意。這句話表達了在會議中有人提出某個議題或想法,但得不到其他與會者的認同。",
words: {
"he": {
word: "he",
translation: "他",
definition: "Used to refer to a male person or animal",
partOfSpeech: "pronoun",
pronunciation: "/hiː/",
synonyms: ["him", "that man"],
isPhrase: false
},
"brought": {
word: "brought",
translation: "帶來、提出",
definition: "Past tense of bring; to take or carry something to a place",
partOfSpeech: "verb",
pronunciation: "/brɔːt/",
synonyms: ["carried", "took", "delivered"],
isPhrase: true,
phraseInfo: {
phrase: "bring up",
meaning: "提出(話題)、養育",
warning: "在這個句子中,\"brought up\" 是一個片語,意思是\"提出話題\",而不是單純的\"帶來\""
}
},
"this": {
word: "this",
translation: "這個",
definition: "Used to indicate something near or just mentioned",
partOfSpeech: "pronoun",
pronunciation: "/ðɪs/",
synonyms: ["that", "it"],
isPhrase: false
},
"thing": {
word: "thing",
translation: "事情、東西",
definition: "An object, fact, or situation",
partOfSpeech: "noun",
pronunciation: "/θɪŋ/",
synonyms: ["object", "matter", "item"],
isPhrase: false
},
"up": {
word: "up",
translation: "向上",
definition: "Toward a higher place or position",
partOfSpeech: "adverb",
pronunciation: "/ʌp/",
synonyms: ["upward", "above"],
isPhrase: true,
phraseInfo: {
phrase: "bring up",
meaning: "提出(話題)、養育",
warning: "\"up\" 在這裡是片語 \"bring up\" 的一部分,不是單獨的\"向上\"的意思"
}
},
"during": {
word: "during",
translation: "在...期間",
definition: "Throughout the course or duration of",
partOfSpeech: "preposition",
pronunciation: "/ˈdjʊərɪŋ/",
synonyms: ["throughout", "while"],
isPhrase: false
},
"our": {
word: "our",
translation: "我們的",
definition: "Belonging to us",
partOfSpeech: "pronoun",
pronunciation: "/aʊər/",
synonyms: ["ours"],
isPhrase: false
},
"meeting": {
word: "meeting",
translation: "會議",
definition: "An organized gathering of people for discussion",
partOfSpeech: "noun",
pronunciation: "/ˈmiːtɪŋ/",
synonyms: ["conference", "assembly", "gathering"],
isPhrase: false
},
"and": {
word: "and",
translation: "和、而且",
definition: "Used to connect words or clauses",
partOfSpeech: "conjunction",
pronunciation: "/ænd/",
synonyms: ["plus", "also"],
isPhrase: false
},
"no": {
word: "no",
translation: "沒有",
definition: "Not any; not one",
partOfSpeech: "determiner",
pronunciation: "/nəʊ/",
synonyms: ["none", "zero"],
isPhrase: false
},
"one": {
word: "one",
translation: "一個人、任何人",
definition: "A single person or thing",
partOfSpeech: "pronoun",
pronunciation: "/wʌn/",
synonyms: ["someone", "anybody"],
isPhrase: false
},
"agreed": {
word: "agreed",
translation: "同意",
definition: "Past tense of agree; to have the same opinion",
partOfSpeech: "verb",
pronunciation: "/əˈɡriːd/",
synonyms: ["consented", "accepted", "approved"],
isPhrase: false
}
}
}
// 處理句子分析
const handleAnalyzeSentence = async () => {
if (!textInput.trim()) return
// 檢查使用次數限制
if (!isPremium && usageCount >= 5) {
alert('❌ 免費用戶 3 小時內只能分析 5 次句子,請稍後再試或升級到付費版本')
return
}
setIsAnalyzing(true)
try {
// 模擬 API 調用
await new Promise(resolve => setTimeout(resolve, 2000))
setSentenceAnalysis(mockSentenceAnalysis.words)
setSentenceMeaning(mockSentenceAnalysis.meaning)
setShowAnalysisView(true)
setUsageCount(prev => prev + 1)
} catch (error) {
console.error('Error analyzing sentence:', error)
alert('分析句子時發生錯誤,請稍後再試')
} finally {
setIsAnalyzing(false)
}
}
return (
<div className="min-h-screen bg-gray-50">
{/* Navigation */}
<nav className="bg-white shadow-sm">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16">
<div className="flex items-center">
<h1 className="text-2xl font-bold text-blue-600">DramaLing</h1>
</div>
<div className="flex items-center space-x-4">
<span className="text-gray-600">Demo </span>
</div>
</div>
</div>
</nav>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{!showAnalysisView ? (
<div className="max-w-4xl mx-auto">
<h1 className="text-3xl font-bold mb-8">AI - </h1>
{/* Input Mode Selection */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<h2 className="text-lg font-semibold mb-4"></h2>
<div className="grid grid-cols-2 gap-4">
<button
onClick={() => setMode('manual')}
className={`p-4 rounded-lg border-2 transition-all ${
mode === 'manual'
? 'border-blue-600 bg-blue-50'
: 'border-gray-200 hover:border-gray-300'
}`}
>
<div className="text-2xl mb-2"></div>
<div className="font-semibold"></div>
<div className="text-sm text-gray-600 mt-1"></div>
</button>
<button
onClick={() => setMode('screenshot')}
disabled={!isPremium}
className={`p-4 rounded-lg border-2 transition-all relative ${
mode === 'screenshot'
? 'border-blue-600 bg-blue-50'
: isPremium
? 'border-gray-200 hover:border-gray-300'
: 'border-gray-200 bg-gray-100 cursor-not-allowed opacity-60'
}`}
>
<div className="text-2xl mb-2">📷</div>
<div className="font-semibold"></div>
<div className="text-sm text-gray-600 mt-1"> (Phase 2)</div>
{!isPremium && (
<div className="absolute top-2 right-2 px-2 py-1 bg-yellow-100 text-yellow-700 text-xs rounded-full">
</div>
)}
</button>
</div>
</div>
{/* Content Input */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<div>
<h2 className="text-lg font-semibold mb-4"></h2>
<textarea
value={textInput}
onChange={(e) => {
const value = e.target.value
if (mode === 'manual' && value.length > 50) {
return // 阻止輸入超過50字
}
setTextInput(value)
}}
placeholder={mode === 'manual'
? "輸入英文句子最多50字..."
: "貼上您想要學習的英文文本,例如影劇對話、文章段落..."
}
className={`w-full h-40 px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-600 focus:border-transparent outline-none resize-none ${
mode === 'manual' && textInput.length >= 45 ? 'border-yellow-400' :
mode === 'manual' && textInput.length >= 50 ? 'border-red-400' : 'border-gray-300'
}`}
/>
<div className="mt-2 flex justify-between text-sm">
<span className={`${
mode === 'manual' && textInput.length >= 45 ? 'text-yellow-600' :
mode === 'manual' && textInput.length >= 50 ? 'text-red-600' : 'text-gray-600'
}`}>
{mode === 'manual' ? `最多 50 字元 • 目前:${textInput.length} 字元` : `最多 5000 字元 • 目前:${textInput.length} 字元`}
</span>
{mode === 'manual' && textInput.length > 40 && (
<span className={textInput.length >= 50 ? 'text-red-600' : 'text-yellow-600'}>
{textInput.length >= 50 ? '已達上限!' : `還可輸入 ${50 - textInput.length} 字元`}
</span>
)}
</div>
{/* 預設示例 */}
{!textInput && (
<div className="mt-4 p-3 bg-gray-50 rounded-lg">
<div className="text-sm text-gray-700 mb-2">
<strong></strong>
</div>
<button
onClick={() => setTextInput("He brought this thing up during our meeting and no one agreed.")}
className="text-sm text-blue-600 hover:text-blue-800 bg-blue-50 px-3 py-1 rounded border border-blue-200"
>
使He brought this thing up during our meeting and no one agreed.
</button>
</div>
)}
</div>
</div>
{/* 新的按鈕區域 */}
<div className="space-y-4">
{/* 分析句子按鈕 */}
<button
onClick={handleAnalyzeSentence}
disabled={isAnalyzing || (mode === 'manual' && (!textInput || textInput.length > 50)) || (mode === 'screenshot')}
className="w-full bg-blue-600 text-white py-4 rounded-lg font-semibold hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{isAnalyzing ? (
<span className="flex items-center justify-center">
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
...
</span>
) : (
'🔍 分析句子(點擊查詢單字)'
)}
</button>
{/* 使用次數顯示 */}
<div className="text-center text-sm text-gray-600">
{isPremium ? (
<span className="text-green-600">🌟 使</span>
) : (
<span className={usageCount >= 4 ? 'text-red-600' : usageCount >= 3 ? 'text-yellow-600' : 'text-gray-600'}>
使 {usageCount}/5 (3)
{usageCount >= 5 && <span className="block text-red-500 mt-1"></span>}
</span>
)}
</div>
</div>
</div>
) : (
/* 句子分析視圖 */
<div className="max-w-4xl mx-auto">
<div className="flex items-center justify-between mb-6">
<h1 className="text-3xl font-bold"></h1>
<button
onClick={() => setShowAnalysisView(false)}
className="text-gray-600 hover:text-gray-900"
>
</button>
</div>
{/* 原始句子顯示 */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<h2 className="text-lg font-semibold mb-4"></h2>
<div className="bg-gray-50 p-4 rounded-lg mb-4">
<div className="text-lg leading-relaxed">
{textInput}
</div>
</div>
<h3 className="text-base font-semibold mb-2"></h3>
<div className="text-gray-700 leading-relaxed">
{sentenceMeaning}
</div>
</div>
{/* 互動式文字 */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<h2 className="text-lg font-semibold mb-4"></h2>
<div className="p-4 bg-blue-50 rounded-lg border-l-4 border-blue-400 mb-4">
<p className="text-sm text-blue-800">
💡 <strong>使</strong>
</p>
</div>
<div className="p-6 bg-gray-50 rounded-lg border-2 border-dashed border-gray-300">
<ClickableText
text={textInput}
analysis={sentenceAnalysis}
onWordClick={(word, analysis) => {
console.log('Clicked word:', word, analysis)
}}
/>
</div>
</div>
{/* 操作按鈕 */}
<div className="bg-white rounded-xl shadow-sm p-6">
<div className="flex gap-4">
<button
onClick={() => setShowAnalysisView(false)}
className="flex-1 bg-gray-200 text-gray-700 py-3 rounded-lg font-medium hover:bg-gray-300 transition-colors"
>
🔄
</button>
<button
onClick={() => alert('詞卡生成功能開發中...')}
className="flex-1 bg-blue-600 text-white py-3 rounded-lg font-medium hover:bg-blue-700 transition-colors"
>
📖
</button>
</div>
</div>
</div>
)}
</div>
</div>
)
}

View File

@ -1,186 +0,0 @@
'use client'
import { useState } from 'react'
export default function TestApiPage() {
const [textInput, setTextInput] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [result, setResult] = useState<any>(null)
const [error, setError] = useState<string | null>(null)
const handleTest = async () => {
if (!textInput.trim()) return
setIsLoading(true)
setError(null)
setResult(null)
try {
console.log('發送API請求到:', 'http://localhost:5000/api/ai/analyze-sentence')
console.log('請求數據:', { inputText: textInput, analysisMode: 'full' })
const response = await fetch('http://localhost:5000/api/ai/analyze-sentence', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
inputText: textInput,
analysisMode: 'full'
})
})
console.log('API響應狀態:', response.status, response.statusText)
if (!response.ok) {
throw new Error(`API 錯誤: ${response.status} ${response.statusText}`)
}
const result = await response.json()
console.log('API響應數據:', result)
setResult(result)
if (result.success) {
console.log('✅ API調用成功')
} else {
console.log('❌ API返回失敗:', result.error)
setError(result.error)
}
} catch (error) {
console.error('❌ API調用錯誤:', error)
setError(error instanceof Error ? error.message : '未知錯誤')
} finally {
setIsLoading(false)
}
}
return (
<div className="min-h-screen bg-gray-50 p-8">
<div className="max-w-4xl mx-auto">
<h1 className="text-3xl font-bold mb-8">API </h1>
{/* 輸入區域 */}
<div className="bg-white rounded-lg shadow p-6 mb-6">
<h2 className="text-lg font-semibold mb-4">API</h2>
<div className="space-y-4">
<textarea
value={textInput}
onChange={(e) => setTextInput(e.target.value)}
placeholder="輸入英文句子進行測試..."
className="w-full h-32 px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-600 focus:border-transparent outline-none resize-none"
/>
<div className="flex gap-2">
<button
onClick={() => setTextInput("He brought this thing up during our meeting.")}
className="px-4 py-2 bg-green-100 text-green-700 rounded-lg text-sm hover:bg-green-200"
>
使
</button>
<button
onClick={() => setTextInput("I go to school yesterday and meet my friends.")}
className="px-4 py-2 bg-red-100 text-red-700 rounded-lg text-sm hover:bg-red-200"
>
使
</button>
</div>
<button
onClick={handleTest}
disabled={isLoading || !textInput.trim()}
className="w-full bg-blue-600 text-white py-3 rounded-lg font-semibold hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{isLoading ? (
<span className="flex items-center justify-center">
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
API連接...
</span>
) : (
'🔍 測試 API 連接'
)}
</button>
</div>
</div>
{/* 錯誤顯示 */}
{error && (
<div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-6">
<h3 className="text-red-800 font-semibold mb-2"> </h3>
<p className="text-red-700">{error}</p>
</div>
)}
{/* 結果顯示 */}
{result && (
<div className="bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-semibold mb-4">📋 API </h3>
{result.success ? (
<div className="space-y-4">
<div className="flex items-center gap-2">
<span className="text-green-600 text-lg"></span>
<span className="font-medium">API 調</span>
{result.cached && <span className="text-blue-600 text-sm">(使)</span>}
{result.cacheHit && <span className="text-purple-600 text-sm">()</span>}
</div>
{/* 語法檢查結果 */}
{result.data?.grammarCorrection && (
<div className="p-3 bg-gray-50 rounded-lg">
<h4 className="font-medium mb-2"></h4>
{result.data.grammarCorrection.hasErrors ? (
<div className="text-red-600">
{result.data.grammarCorrection.corrections?.length || 0}
</div>
) : (
<div className="text-green-600"> </div>
)}
</div>
)}
{/* 句子意思 */}
{result.data?.sentenceMeaning && (
<div className="p-3 bg-blue-50 rounded-lg">
<h4 className="font-medium mb-2"></h4>
<p className="text-blue-800">{result.data.sentenceMeaning.translation}</p>
<p className="text-blue-600 text-sm mt-1">{result.data.sentenceMeaning.explanation}</p>
</div>
)}
{/* 高價值詞彙 */}
{result.data?.highValueWords && (
<div className="p-3 bg-green-50 rounded-lg">
<h4 className="font-medium mb-2"></h4>
<div className="flex flex-wrap gap-2">
{result.data.highValueWords.map((word: string, idx: number) => (
<span key={idx} className="bg-green-200 text-green-800 px-2 py-1 rounded text-sm">
{word}
</span>
))}
</div>
</div>
)}
{/* 原始響應(調試用) */}
<details className="mt-4">
<summary className="cursor-pointer text-gray-600 text-sm">JSON響應</summary>
<pre className="mt-2 p-3 bg-gray-100 rounded text-xs overflow-auto">
{JSON.stringify(result, null, 2)}
</pre>
</details>
</div>
) : (
<div className="text-red-600">
API : {result.error || '未知錯誤'}
</div>
)}
</div>
)}
</div>
</div>
)
}

View File

@ -1,57 +0,0 @@
'use client'
import { useState } from 'react'
export default function TestSimplePage() {
const [result, setResult] = useState('')
const testApi = async () => {
try {
setResult('正在測試...')
const response = await fetch('http://localhost:5000/api/ai/analyze-sentence', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
inputText: 'Test sentence for debugging',
analysisMode: 'full'
})
})
if (response.ok) {
const data = await response.json()
setResult(`✅ API 成功: ${JSON.stringify(data, null, 2)}`)
} else {
setResult(`❌ API 錯誤: ${response.status} ${response.statusText}`)
}
} catch (error) {
setResult(`💥 連接錯誤: ${error}`)
}
}
return (
<div className="p-8">
<h1 className="text-2xl font-bold mb-4">API </h1>
<button
onClick={testApi}
className="bg-blue-600 text-white px-4 py-2 rounded mb-4"
>
API
</button>
<pre className="bg-gray-100 p-4 rounded text-sm overflow-auto max-h-96">
{result || '點擊按鈕測試 API 連接'}
</pre>
<div className="mt-4 p-4 bg-yellow-50 border border-yellow-200 rounded">
<h3 className="font-bold mb-2"></h3>
<code className="text-sm">
curl -s http://localhost:5000/api/ai/analyze-sentence -X POST -H "Content-Type: application/json" -d '{"inputText":"test"}'
</code>
</div>
</div>
)
}

View File

@ -1,82 +0,0 @@
'use client'
import { ClickableText } from '@/components/ClickableText'
const mockAnalysis = {
"brought": {
word: "brought",
translation: "帶來、提出",
definition: "Past tense of bring; to take or carry something to a place",
partOfSpeech: "verb",
pronunciation: "/brɔːt/",
synonyms: ["carried", "took", "delivered"],
isPhrase: true,
phraseInfo: {
phrase: "bring up",
meaning: "提出(話題)、養育",
warning: "在這個句子中,\"brought up\" 是一個片語,意思是\"提出話題\",而不是單純的\"帶來\""
}
},
"this": {
word: "this",
translation: "這個",
definition: "Used to indicate something near or just mentioned",
partOfSpeech: "pronoun",
pronunciation: "/ðɪs/",
synonyms: ["that", "it"],
isPhrase: false
},
"thing": {
word: "thing",
translation: "事情、東西",
definition: "An object, fact, or situation",
partOfSpeech: "noun",
pronunciation: "/θɪŋ/",
synonyms: ["object", "matter", "item"],
isPhrase: false
},
"up": {
word: "up",
translation: "向上",
definition: "Toward a higher place or position",
partOfSpeech: "adverb",
pronunciation: "/ʌp/",
synonyms: ["upward", "above"],
isPhrase: true,
phraseInfo: {
phrase: "bring up",
meaning: "提出(話題)、養育",
warning: "\"up\" 在這裡是片語 \"bring up\" 的一部分,不是單獨的\"向上\"的意思"
}
}
}
export default function TestPage() {
return (
<div className="min-h-screen bg-gray-50 p-8">
<div className="max-w-4xl mx-auto">
<h1 className="text-3xl font-bold mb-8"></h1>
<div className="bg-white rounded-xl shadow-sm p-6">
<h2 className="text-lg font-semibold mb-4"></h2>
<div className="p-4 bg-blue-50 rounded-lg border-l-4 border-blue-400 mb-4">
<p className="text-sm text-blue-800">
💡 <strong>使</strong>
</p>
</div>
<div className="p-6 bg-gray-50 rounded-lg border-2 border-dashed border-gray-300">
<ClickableText
text="He brought this thing up during our meeting."
analysis={mockAnalysis}
onWordClick={(word, analysis) => {
console.log('Clicked word:', word, analysis)
}}
/>
</div>
</div>
</div>
</div>
)
}