| <!DOCTYPE html> |
| <html lang="zh-TW"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Transformers.js 即時多國語言情緒分析</title> |
| <style> |
| body { font-family: 'Segoe UI', Arial, sans-serif; max-width: 600px; margin: 40px auto; padding: 20px; text-align: center; background-color: #f5f7fa; } |
| .card { background: white; padding: 30px; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.05); } |
| textarea { width: 100%; height: 120px; padding: 12px; font-size: 16px; border: 2px solid #e2e8f0; border-radius: 8px; box-sizing: border-box; resize: vertical; transition: border 0.2s; } |
| textarea:focus { border-color: #3b82f6; outline: none; } |
| #status { font-weight: bold; margin: 15px 0; color: #4b5563; line-height: 1.5; } |
| #result { margin-top: 25px; padding: 20px; border-radius: 8px; display: none; text-align: left; } |
| .positive { background-color: #dcfce7; color: #14532d; border: 1px solid #bbf7d0; } |
| .negative { background-color: #fee2e2; color: #7f1d1d; border: 1px solid #fecaca; } |
| .badge { display: inline-block; padding: 4px 8px; background: #e2e8f0; border-radius: 4px; font-size: 12px; color: #4b5563; margin-bottom: 10px; } |
| </style> |
| </head> |
| <body> |
|
|
| <div class="card"> |
| <h2>情緒分析大師 (多國語言支援)</h2> |
| <div id="status">正在偵測瀏覽器環境...</div> |
|
|
| <textarea id="inputText" placeholder="請在此輸入文字,支援繁體中文、英文、日文等..." disabled></textarea> |
|
|
| <div id="result"> |
| <div class="badge">AI 分析結果</div> |
| <p><strong>情緒傾向:</strong> <span id="label"></span></p> |
| <p><strong>信心分數:</strong> <span id="score"></span></p> |
| </div> |
| </div> |
|
|
| <script type="module"> |
| |
| import { pipeline, env } from 'https://cdn.jsdelivr.net/npm/@xenova/transformers@latest'; |
| |
| |
| if (window.location.protocol === 'file:') { |
| document.getElementById('status').innerHTML = '❌ <span style="color:red">不支援直接雙擊 HTML 檔案開啟!</span><br>由於瀏覽器安全限制(CORS),請使用本機網頁伺服器(如 Live Server)執行此頁面。'; |
| throw new Error("Cannot run Transformers.js via file:// protocol."); |
| } |
| |
| |
| env.useBrowserCache = true; |
| env.allowLocalModels = false; |
| |
| const statusText = document.getElementById('status'); |
| const inputText = document.getElementById('inputText'); |
| const resultDiv = document.getElementById('result'); |
| const labelSpan = document.getElementById('label'); |
| const scoreSpan = document.getElementById('score'); |
| |
| let classifier; |
| let debounceTimer; |
| |
| |
| |
| const MODEL_NAME = 'Xenova/bert-base-multilingual-uncased-sentiment'; |
| |
| async function initAI() { |
| try { |
| statusText.innerText = '正在下載 AI 模型 (僅首次需要約 80MB)...'; |
| |
| |
| classifier = await pipeline('sentiment-analysis', MODEL_NAME); |
| |
| statusText.innerText = '🟢 模型準備就緒!請在下方輸入文字:'; |
| inputText.disabled = false; |
| inputText.focus(); |
| } catch (err) { |
| statusText.innerHTML = '🔴 模型載入失敗。<br>請確認您有連網,且非使用 file:// 協議開啟。'; |
| console.error(err); |
| } |
| } |
| |
| inputText.addEventListener('input', () => { |
| const text = inputText.value.trim(); |
| |
| if (!text) { |
| resultDiv.style.display = 'none'; |
| return; |
| } |
| |
| clearTimeout(debounceTimer); |
| debounceTimer = setTimeout(async () => { |
| statusText.innerText = '⚡ AI 正在即時分析中...'; |
| |
| try { |
| const result = await classifier(text); |
| const prediction = result[0]; |
| |
| |
| |
| const stars = parseInt(prediction.label); |
| let displayLabel = ''; |
| let className = ''; |
| |
| if (stars >= 4) { |
| displayLabel = `正面 😄 (${prediction.label})`; |
| className = 'positive'; |
| } else if (stars <= 2) { |
| displayLabel = `負面 😢 (${prediction.label})`; |
| className = 'negative'; |
| } else { |
| displayLabel = `中性 😐 (${prediction.label})`; |
| className = ''; |
| } |
| |
| |
| labelSpan.innerText = displayLabel; |
| scoreSpan.innerText = (prediction.score * 100).toFixed(2) + '%'; |
| |
| resultDiv.className = className; |
| resultDiv.style.display = 'block'; |
| statusText.innerText = '🟢 監聽輸入中...'; |
| } catch (error) { |
| console.error(error); |
| statusText.innerText = '❌ 分析時發生錯誤。'; |
| } |
| }, 400); |
| }); |
| |
| initAI(); |
| </script> |
| </body> |
| </html> |
|
|