import React, { useState, useEffect, useCallback, useRef } from 'react'; import { Upload, Volume2, RefreshCcw, Image as ImageIcon, Lightbulb, Star, Trophy, Heart, Clock, XCircle, CheckCircle, HelpCircle, Rocket, Type, MousePointerClick, BookOpen, ArrowRight, ArrowLeft, Home, Shuffle, Edit3, ImagePlus, Ear, CheckSquare, VolumeX } from 'lucide-react'; /** * ========================================== * 圖片處理元件 (Triple-Fallback Safe Image) * ========================================== */ const SafeImage = ({ src, word }) => { const [imgStatus, setImgStatus] = useState('loading'); const [imgSrc, setImgSrc] = useState(src); useEffect(() => { setImgStatus('loading'); setImgSrc(src); }, [src]); return (
{imgStatus === 'loading' && (
)} {imgSrc && ( {word} setImgStatus('success')} onError={() => { const fallback = `https://api.dicebear.com/9.x/bottts/svg?seed=${encodeURIComponent(word)}`; if (imgSrc !== fallback) setImgSrc(fallback); }} /> )}
); }; const App = () => { // --- 狀態定義 --- const [appState, setAppState] = useState('upload'); const [wordList, setWordList] = useState([]); const [currentIndex, setCurrentIndex] = useState(0); const [score, setScore] = useState(0); const [lives, setLives] = useState(3); const [isMuted, setIsMuted] = useState(false); // --- 檔案處理 --- const handleFileUpload = async (e) => { const file = e.target.files[0]; if (!file) return; // 動態載入 XLSX 庫確保 GitHub Pages 運行流暢 const script = document.createElement('script'); script.src = "https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"; script.onload = () => { const reader = new FileReader(); reader.onload = (evt) => { const wb = window.XLSX.read(evt.target.result, { type: 'array' }); const data = window.XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { header: 1 }); const parsed = data.slice(1).map(row => ({ word: String(row[0] || '').trim().toLowerCase(), translation: row[1] || '', hasFetched: false })).filter(i => i.word); setWordList(parsed); setAppState('selectMode'); }; reader.readAsArrayBuffer(file); }; document.head.appendChild(script); }; return (
{/* Header */}

Spelling Adventure

{wordList.length > 0 && ( )}
{appState === 'upload' && (

準備好開始單字大冒險嗎?

支援 .xlsx 格式 (A欄單字, B欄翻譯)

)} {appState === 'selectMode' && (
)} {/* 遊戲中與其它狀態... (邏輯同前) */} {appState === 'playing' && wordList.length > 0 && (

{wordList[currentIndex].word}

{/* 拼字邏輯與 UI 按鈕 */}
)}
); }; export default App;