| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>AI Slop Detector - Analyze AI-Generated Content</title> |
| <style> |
| :root { |
| --primary-color: #007bff; |
| --success-color: #28a745; |
| --danger-color: #dc3545; |
| --warning-color: #ffc107; |
| --info-color: #17a2b8; |
| --dark-color: #343a40; |
| --light-color: #f8f9fa; |
| --border-color: #dee2e6; |
| --shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| --shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.15); |
| } |
| |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| } |
| |
| body { |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| min-height: 100vh; |
| padding: 20px; |
| color: #333; |
| } |
| |
| .container { |
| max-width: 1200px; |
| margin: 0 auto; |
| } |
| |
| header { |
| text-align: center; |
| color: white; |
| margin-bottom: 40px; |
| animation: slideDown 0.6s ease; |
| } |
| |
| header h1 { |
| font-size: 2.5rem; |
| margin-bottom: 10px; |
| font-weight: 700; |
| } |
| |
| header p { |
| font-size: 1.1rem; |
| opacity: 0.9; |
| } |
| |
| .content { |
| display: grid; |
| grid-template-columns: 1fr 1fr; |
| gap: 30px; |
| margin-bottom: 40px; |
| } |
| |
| @media (max-width: 900px) { |
| .content { |
| grid-template-columns: 1fr; |
| } |
| } |
| |
| .card { |
| background: white; |
| border-radius: 12px; |
| padding: 30px; |
| box-shadow: var(--shadow-lg); |
| animation: fadeIn 0.6s ease 0.2s backwards; |
| } |
| |
| .card h2 { |
| font-size: 1.5rem; |
| margin-bottom: 20px; |
| color: var(--dark-color); |
| border-bottom: 3px solid var(--primary-color); |
| padding-bottom: 15px; |
| } |
| |
| |
| .upload-area { |
| border: 3px dashed var(--primary-color); |
| border-radius: 8px; |
| padding: 40px 20px; |
| text-align: center; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| background: linear-gradient(135deg, rgba(0, 123, 255, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%); |
| } |
| |
| .upload-area:hover, |
| .upload-area.dragover { |
| border-color: var(--danger-color); |
| background: linear-gradient(135deg, rgba(220, 53, 69, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%); |
| transform: translateY(-2px); |
| } |
| |
| .upload-area p { |
| color: #666; |
| margin: 10px 0; |
| font-size: 0.95rem; |
| } |
| |
| .upload-icon { |
| font-size: 2.5rem; |
| margin-bottom: 10px; |
| } |
| |
| .file-input { |
| display: none; |
| } |
| |
| .input-group { |
| margin-bottom: 20px; |
| } |
| |
| label { |
| display: block; |
| margin-bottom: 8px; |
| font-weight: 600; |
| color: var(--dark-color); |
| } |
| |
| textarea, |
| input[type="text"], |
| input[type="file"] { |
| width: 100%; |
| padding: 12px; |
| border: 1px solid var(--border-color); |
| border-radius: 6px; |
| font-family: 'Segoe UI', sans-serif; |
| font-size: 0.95rem; |
| transition: border-color 0.3s ease; |
| } |
| |
| textarea { |
| resize: vertical; |
| min-height: 150px; |
| font-family: 'Courier New', monospace; |
| } |
| |
| textarea:focus, |
| input[type="text"]:focus { |
| outline: none; |
| border-color: var(--primary-color); |
| box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1); |
| } |
| |
| .button { |
| background: var(--primary-color); |
| color: white; |
| padding: 12px 24px; |
| border: none; |
| border-radius: 6px; |
| font-size: 1rem; |
| font-weight: 600; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| display: inline-block; |
| text-align: center; |
| } |
| |
| .button:hover { |
| background: #0056b3; |
| transform: translateY(-2px); |
| box-shadow: var(--shadow-lg); |
| } |
| |
| .button:active { |
| transform: translateY(0); |
| } |
| |
| .button.secondary { |
| background: var(--dark-color); |
| } |
| |
| .button.secondary:hover { |
| background: #212529; |
| } |
| |
| .button:disabled { |
| opacity: 0.5; |
| cursor: not-allowed; |
| transform: none; |
| } |
| |
| |
| .results-container { |
| margin-top: 30px; |
| } |
| |
| .result-item { |
| background: var(--light-color); |
| border-left: 5px solid var(--primary-color); |
| padding: 15px; |
| margin-bottom: 15px; |
| border-radius: 6px; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| } |
| |
| .result-item:hover { |
| transform: translateX(5px); |
| box-shadow: var(--shadow); |
| } |
| |
| .result-header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| margin-bottom: 10px; |
| } |
| |
| .result-filename { |
| font-weight: 600; |
| color: var(--dark-color); |
| } |
| |
| .score-badge { |
| display: inline-block; |
| padding: 6px 12px; |
| border-radius: 20px; |
| font-weight: 600; |
| font-size: 0.9rem; |
| color: white; |
| } |
| |
| .score-human { |
| background: var(--success-color); |
| } |
| |
| .score-suspicious { |
| background: var(--warning-color); |
| color: #333; |
| } |
| |
| .score-ai { |
| background: var(--danger-color); |
| } |
| |
| .result-meta { |
| font-size: 0.85rem; |
| color: #666; |
| display: flex; |
| gap: 20px; |
| flex-wrap: wrap; |
| } |
| |
| |
| .analysis-result { |
| background: white; |
| border-radius: 12px; |
| padding: 30px; |
| box-shadow: var(--shadow-lg); |
| margin-top: 20px; |
| animation: slideUp 0.5s ease; |
| } |
| |
| .overall-score { |
| text-align: center; |
| padding: 30px; |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| border-radius: 8px; |
| color: white; |
| margin-bottom: 30px; |
| } |
| |
| .overall-score h3 { |
| font-size: 1.2rem; |
| margin-bottom: 10px; |
| opacity: 0.9; |
| } |
| |
| .score-percentage { |
| font-size: 3rem; |
| font-weight: 700; |
| margin: 10px 0; |
| } |
| |
| .score-confidence { |
| font-size: 1.1rem; |
| opacity: 0.95; |
| text-transform: uppercase; |
| letter-spacing: 1px; |
| } |
| |
| .detector-results { |
| display: grid; |
| grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); |
| gap: 20px; |
| margin: 20px 0; |
| } |
| |
| .detector-card { |
| background: var(--light-color); |
| border: 2px solid var(--border-color); |
| border-radius: 8px; |
| padding: 20px; |
| transition: all 0.3s ease; |
| } |
| |
| .detector-card:hover { |
| border-color: var(--primary-color); |
| box-shadow: var(--shadow-lg); |
| transform: translateY(-3px); |
| } |
| |
| .detector-name { |
| font-weight: 600; |
| font-size: 1.1rem; |
| margin-bottom: 4px; |
| color: var(--dark-color); |
| } |
| |
| .detector-model { |
| font-size: 0.78rem; |
| color: #999; |
| margin-bottom: 14px; |
| font-style: italic; |
| } |
| |
| .detector-score-row { |
| display: flex; |
| align-items: baseline; |
| gap: 10px; |
| margin-bottom: 6px; |
| } |
| |
| .detector-score { |
| font-size: 2rem; |
| font-weight: 700; |
| line-height: 1; |
| } |
| |
| .detector-prediction-badge { |
| display: inline-block; |
| padding: 3px 10px; |
| border-radius: 12px; |
| font-size: 0.78rem; |
| font-weight: 600; |
| text-transform: uppercase; |
| letter-spacing: 0.5px; |
| } |
| |
| .prediction-ai { |
| background: rgba(220, 53, 69, 0.12); |
| color: #c82333; |
| border: 1px solid rgba(220, 53, 69, 0.3); |
| } |
| |
| .prediction-human { |
| background: rgba(40, 167, 69, 0.12); |
| color: #1e7e34; |
| border: 1px solid rgba(40, 167, 69, 0.3); |
| } |
| |
| .prob-bar-wrap { |
| margin: 12px 0 10px; |
| } |
| |
| .prob-bar-labels { |
| display: flex; |
| justify-content: space-between; |
| font-size: 0.78rem; |
| color: #666; |
| margin-bottom: 4px; |
| } |
| |
| .prob-bar-track { |
| height: 10px; |
| border-radius: 5px; |
| background: #e9ecef; |
| overflow: hidden; |
| display: flex; |
| } |
| |
| .prob-bar-ai { |
| height: 100%; |
| background: linear-gradient(90deg, #f8645a, #dc3545); |
| transition: width 0.6s ease; |
| border-radius: 5px 0 0 5px; |
| } |
| |
| .prob-bar-human { |
| height: 100%; |
| background: linear-gradient(90deg, #28a745, #20c963); |
| transition: width 0.6s ease; |
| border-radius: 0 5px 5px 0; |
| } |
| |
| .prob-values { |
| display: flex; |
| justify-content: space-between; |
| margin-top: 4px; |
| font-size: 0.82rem; |
| font-weight: 600; |
| } |
| |
| .prob-ai-val { color: #dc3545; } |
| .prob-human-val { color: #28a745; } |
| |
| .detector-confidence { |
| font-size: 0.82rem; |
| color: #777; |
| margin-top: 10px; |
| } |
| |
| .confidence-dot { |
| display: inline-block; |
| width: 8px; |
| height: 8px; |
| border-radius: 50%; |
| margin-right: 5px; |
| background: #aaa; |
| } |
| |
| .conf-very_low .confidence-dot { background: #adb5bd; } |
| .conf-low .confidence-dot { background: #6c757d; } |
| .conf-medium .confidence-dot { background: #ffc107; } |
| .conf-high .confidence-dot { background: #fd7e14; } |
| .conf-very_high .confidence-dot { background: #dc3545; } |
| |
| .detector-explanation { |
| font-size: 0.85rem; |
| color: #666; |
| line-height: 1.5; |
| margin-top: 10px; |
| padding-top: 10px; |
| border-top: 1px solid var(--border-color); |
| } |
| |
| .stats-grid { |
| display: grid; |
| grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); |
| gap: 15px; |
| margin: 20px 0; |
| } |
| |
| .stat-box { |
| background: var(--light-color); |
| padding: 15px; |
| border-radius: 8px; |
| text-align: center; |
| border: 1px solid var(--border-color); |
| } |
| |
| .stat-label { |
| font-size: 0.85rem; |
| color: #666; |
| font-weight: 500; |
| } |
| |
| .stat-value { |
| font-size: 1.8rem; |
| font-weight: 700; |
| color: var(--primary-color); |
| margin-top: 8px; |
| } |
| |
| |
| .tabs { |
| display: flex; |
| gap: 10px; |
| border-bottom: 2px solid var(--border-color); |
| margin: 20px 0; |
| } |
| |
| .tab-button { |
| background: none; |
| border: none; |
| padding: 10px 20px; |
| cursor: pointer; |
| font-weight: 600; |
| color: #666; |
| border-bottom: 3px solid transparent; |
| transition: all 0.3s ease; |
| } |
| |
| .tab-button.active { |
| color: var(--primary-color); |
| border-bottom-color: var(--primary-color); |
| } |
| |
| .tab-content { |
| display: none; |
| } |
| |
| .tab-content.active { |
| display: block; |
| animation: fadeIn 0.3s ease; |
| } |
| |
| |
| .alert { |
| padding: 15px; |
| border-radius: 6px; |
| margin-bottom: 20px; |
| animation: slideDown 0.3s ease; |
| } |
| |
| .alert-success { |
| background: rgba(40, 167, 69, 0.1); |
| color: #155724; |
| border-left: 4px solid var(--success-color); |
| } |
| |
| .alert-error { |
| background: rgba(220, 53, 69, 0.1); |
| color: #721c24; |
| border-left: 4px solid var(--danger-color); |
| } |
| |
| .alert-info { |
| background: rgba(23, 162, 184, 0.1); |
| color: #0c5460; |
| border-left: 4px solid var(--info-color); |
| } |
| |
| .loading { |
| display: inline-block; |
| width: 20px; |
| height: 20px; |
| border: 3px solid rgba(255, 255, 255, 0.3); |
| border-radius: 50%; |
| border-top-color: white; |
| animation: spin 1s linear infinite; |
| } |
| |
| .spinner { |
| text-align: center; |
| padding: 40px; |
| } |
| |
| @keyframes spin { |
| to { transform: rotate(360deg); } |
| } |
| |
| @keyframes slideDown { |
| from { |
| opacity: 0; |
| transform: translateY(-20px); |
| } |
| to { |
| opacity: 1; |
| transform: translateY(0); |
| } |
| } |
| |
| @keyframes slideUp { |
| from { |
| opacity: 0; |
| transform: translateY(20px); |
| } |
| to { |
| opacity: 1; |
| transform: translateY(0); |
| } |
| } |
| |
| @keyframes fadeIn { |
| from { opacity: 0; } |
| to { opacity: 1; } |
| } |
| |
| |
| .empty-state { |
| text-align: center; |
| padding: 60px 20px; |
| color: #999; |
| } |
| |
| .empty-state-icon { |
| font-size: 3rem; |
| margin-bottom: 20px; |
| } |
| |
| .empty-state p { |
| font-size: 1.1rem; |
| margin-bottom: 20px; |
| } |
| |
| |
| footer { |
| text-align: center; |
| color: white; |
| opacity: 0.8; |
| margin-top: 40px; |
| padding: 20px; |
| } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <header> |
| <h1>🔍 AI Slop Detector</h1> |
| <p>Analyze documents and text for AI-generated content</p> |
| </header> |
|
|
| <div class="content"> |
| |
| <div class="card" id="uploadCard"> |
| <h2>📤 Upload File</h2> |
| <div class="upload-area" id="uploadArea"> |
| <div class="upload-icon">📄</div> |
| <p><strong>Click or drag to upload</strong></p> |
| <p style="font-size: 0.85rem;">Supported: PDF, DOCX, TXT (Max 50MB)</p> |
| </div> |
| <input type="file" id="fileInput" class="file-input" accept=".pdf,.docx,.doc,.txt"> |
| |
| <div style="margin-top: 20px;"> |
| <div class="input-group"> |
| <label for="fileName">Optional: Custom Name</label> |
| <input type="text" id="fileName" placeholder="e.g., document.pdf"> |
| </div> |
| <button class="button" style="width: 100%;" id="uploadButton" disabled> |
| Upload & Analyze |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div class="card" id="textCard"> |
| <h2>✏️ Analyze Text</h2> |
| <div class="input-group"> |
| <label for="rawText">Paste your text here</label> |
| <textarea id="rawText" placeholder="Enter or paste text to analyze..."></textarea> |
| </div> |
| <button class="button" style="width: 100%;" id="analyzeButton"> |
| Analyze Text |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div id="loadingIndicator" style="display: none;" class="spinner"> |
| <div class="loading"></div> |
| <p style="margin-top: 10px; color: white;">Analyzing...</p> |
| </div> |
|
|
| |
| <div id="alertContainer"></div> |
|
|
| |
| <div id="resultsSection" style="display: none;"> |
| <div class="card"> |
| <h2>📊 Analysis Results</h2> |
| |
| <div class="tabs"> |
| <button class="tab-button active" data-tab="current">Current Analysis</button> |
| <button class="tab-button" data-tab="history">History</button> |
| <button class="tab-button" data-tab="statistics">Statistics</button> |
| </div> |
|
|
| |
| <div class="tab-content active" id="currentTab"> |
| <div id="analysisResult"></div> |
| </div> |
|
|
| |
| <div class="tab-content" id="historyTab"> |
| <div id="historyList"></div> |
| </div> |
|
|
| |
| <div class="tab-content" id="statisticsTab"> |
| <div id="statistics"></div> |
| </div> |
| </div> |
| </div> |
|
|
| <footer> |
| <p>🛡️ AI Slop Detector - Powered by Advanced AI Detection Algorithms</p> |
| <p style="font-size: 0.85rem; margin-top: 10px;">Multi-detector ensemble for accurate AI-generated content detection</p> |
| </footer> |
| </div> |
|
|
| <script> |
| |
| const API_BASE = '/api'; |
| |
| |
| let currentResult = null; |
| let selectedFile = null; |
| |
| |
| |
| |
| |
| const uploadArea = document.getElementById('uploadArea'); |
| const fileInput = document.getElementById('fileInput'); |
| const uploadButton = document.getElementById('uploadButton'); |
| const fileName = document.getElementById('fileName'); |
| |
| |
| uploadArea.addEventListener('click', () => fileInput.click()); |
| |
| |
| uploadArea.addEventListener('dragover', (e) => { |
| e.preventDefault(); |
| uploadArea.classList.add('dragover'); |
| }); |
| |
| uploadArea.addEventListener('dragleave', () => { |
| uploadArea.classList.remove('dragover'); |
| }); |
| |
| uploadArea.addEventListener('drop', (e) => { |
| e.preventDefault(); |
| uploadArea.classList.remove('dragover'); |
| handleFileSelect(e.dataTransfer.files); |
| }); |
| |
| |
| fileInput.addEventListener('change', (e) => { |
| handleFileSelect(e.target.files); |
| }); |
| |
| function handleFileSelect(files) { |
| if (files.length === 0) return; |
| |
| selectedFile = files[0]; |
| uploadArea.innerHTML = `<div style="color: #666;">✓ ${selectedFile.name} selected</div>`; |
| uploadButton.disabled = false; |
| } |
| |
| |
| uploadButton.addEventListener('click', async () => { |
| if (!selectedFile) return; |
| |
| showLoading(true); |
| hideAlert(); |
| |
| try { |
| const formData = new FormData(); |
| formData.append('file', selectedFile); |
| |
| const response = await fetch(`${API_BASE}/analyze/file`, { |
| method: 'POST', |
| body: formData |
| }); |
| |
| const data = await response.json(); |
| |
| if (response.ok) { |
| currentResult = data; |
| displayAnalysisResult(data); |
| showAlert('Analysis completed successfully!', 'success'); |
| loadHistory(); |
| loadStatistics(); |
| } else { |
| showAlert(data.message || 'Analysis failed', 'error'); |
| } |
| } catch (error) { |
| showAlert(`Error: ${error.message}`, 'error'); |
| } finally { |
| showLoading(false); |
| } |
| }); |
| |
| |
| |
| |
| |
| const analyzeButton = document.getElementById('analyzeButton'); |
| const rawText = document.getElementById('rawText'); |
| |
| analyzeButton.addEventListener('click', async () => { |
| const text = rawText.value.trim(); |
| |
| if (!text) { |
| showAlert('Please enter some text to analyze', 'error'); |
| return; |
| } |
| |
| if (text.length < 10) { |
| showAlert('Text must be at least 10 characters', 'error'); |
| return; |
| } |
| |
| showLoading(true); |
| hideAlert(); |
| |
| try { |
| const response = await fetch(`${API_BASE}/analyze/text`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: JSON.stringify({ text: text }) |
| }); |
| |
| const data = await response.json(); |
| |
| if (response.ok) { |
| currentResult = data; |
| displayAnalysisResult(data); |
| showAlert('Analysis completed successfully!', 'success'); |
| loadHistory(); |
| loadStatistics(); |
| } else { |
| showAlert(data.message || 'Analysis failed', 'error'); |
| } |
| } catch (error) { |
| showAlert(`Error: ${error.message}`, 'error'); |
| } finally { |
| showLoading(false); |
| } |
| }); |
| |
| |
| |
| |
| |
| function displayAnalysisResult(result) { |
| document.getElementById('resultsSection').style.display = 'block'; |
| |
| const html = ` |
| <div class="analysis-result"> |
| <div class="overall-score"> |
| <h3>Overall AI Likelihood</h3> |
| <div class="score-percentage">${result.overall_ai_score_percentage}</div> |
| <div class="score-confidence">${result.overall_confidence}</div> |
| <div style="margin-top: 15px; font-size: 1rem;">${result.status_label}</div> |
| </div> |
| |
| <h3 style="margin-bottom: 15px;">📈 Detector Results</h3> |
| <div class="detector-results"> |
| ${Object.entries(result.detector_results).map(([name, detector]) => { |
| const meta = detector.metadata || {}; |
| const aiProb = meta.ai_probability != null ? meta.ai_probability : detector.score; |
| const humanProb = meta.human_probability != null ? meta.human_probability : (1 - detector.score); |
| const prediction = meta.prediction || null; |
| const isAI = prediction ? prediction.toLowerCase().includes('ai') : detector.score >= 0.5; |
| const scoreColor = detector.score >= 0.7 ? '#dc3545' : detector.score >= 0.3 ? '#fd7e14' : '#28a745'; |
| const conf = detector.confidence || 'n/a'; |
| return ` |
| <div class="detector-card"> |
| <div class="detector-name">${formatDetectorName(name)}</div> |
| <div class="detector-model">${meta.model || 'N/A'}</div> |
| |
| <div class="detector-score-row"> |
| <div class="detector-score" style="color:${scoreColor}">${(detector.score * 100).toFixed(1)}%</div> |
| ${prediction ? `<span class="detector-prediction-badge ${isAI ? 'prediction-ai' : 'prediction-human'}">${prediction}</span>` : ''} |
| </div> |
| |
| <div class="prob-bar-wrap"> |
| <div class="prob-bar-labels"> |
| <span>AI</span> |
| <span>Human</span> |
| </div> |
| <div class="prob-bar-track"> |
| <div class="prob-bar-ai" style="width:${(aiProb * 100).toFixed(1)}%"></div> |
| <div class="prob-bar-human" style="width:${(humanProb * 100).toFixed(1)}%"></div> |
| </div> |
| <div class="prob-values"> |
| <span class="prob-ai-val">${(aiProb * 100).toFixed(1)}%</span> |
| <span class="prob-human-val">${(humanProb * 100).toFixed(1)}%</span> |
| </div> |
| </div> |
| |
| <div class="detector-confidence conf-${conf}"> |
| <span class="confidence-dot"></span>Confidence: ${conf.replace('_', ' ')} |
| </div> |
| <div class="detector-explanation">${detector.explanation || 'No details'}</div> |
| </div>`; |
| }).join('')} |
| </div> |
| |
| <h3 style="margin-top: 30px; margin-bottom: 15px;">📊 Text Statistics</h3> |
| <div class="stats-grid"> |
| <div class="stat-box"> |
| <div class="stat-label">Characters</div> |
| <div class="stat-value">${result.text_stats.character_count.toLocaleString()}</div> |
| </div> |
| <div class="stat-box"> |
| <div class="stat-label">Words</div> |
| <div class="stat-value">${result.text_stats.word_count.toLocaleString()}</div> |
| </div> |
| <div class="stat-box"> |
| <div class="stat-label">Sentences</div> |
| <div class="stat-value">${result.text_stats.sentence_count}</div> |
| </div> |
| <div class="stat-box"> |
| <div class="stat-label">Avg Word Length</div> |
| <div class="stat-value">${result.text_stats.average_word_length.toFixed(1)}</div> |
| </div> |
| <div class="stat-box"> |
| <div class="stat-label">Emojis</div> |
| <div class="stat-value">${result.text_stats.emoji_count ?? 0}</div> |
| </div> |
| <div class="stat-box"> |
| <div class="stat-label">Em Dashes (—)</div> |
| <div class="stat-value">${result.text_stats.em_dash_count ?? 0}</div> |
| </div> |
| <div class="stat-box"> |
| <div class="stat-label">Arrows (→)</div> |
| <div class="stat-value">${result.text_stats.arrow_count ?? 0}</div> |
| </div> |
| </div> |
| |
| <div style="margin-top: 20px;"> |
| <strong>Detectors Used:</strong> ${result.enabled_detectors.join(', ')} |
| </div> |
| </div> |
| `; |
| |
| document.getElementById('analysisResult').innerHTML = html; |
| document.querySelector('[data-tab="current"]').click(); |
| } |
| |
| function formatDetectorName(name) { |
| const names = { |
| 'roberta': 'RoBERTa Detector', |
| 'perplexity': 'Perplexity Analysis', |
| 'llmdet': 'LLMDet Detector', |
| 'hf_classifier': 'HF Classifier', |
| 'outfox': 'OUTFOX Statistical', |
| 'tmr_detector': 'TMR (Target Mining RoBERTa)', |
| 'pattern': 'Pattern Signals (EM Dash + Emoji)', |
| }; |
| return names[name] || name.toUpperCase(); |
| } |
| |
| async function loadHistory() { |
| try { |
| const response = await fetch(`${API_BASE}/results?limit=10&sort=recent`); |
| const data = await response.json(); |
| |
| if (!data.results || data.results.length === 0) { |
| document.getElementById('historyList').innerHTML = ` |
| <div class="empty-state"> |
| <div class="empty-state-icon">📂</div> |
| <p>No previous analyses yet</p> |
| </div> |
| `; |
| return; |
| } |
| |
| const html = data.results.map(result => ` |
| <div class="result-item" onclick="viewResult(${result.id})"> |
| <div class="result-header"> |
| <span class="result-filename">${result.filename}</span> |
| <span class="score-badge ${getScoreBadgeClass(result.overall_ai_score)}"> |
| ${(result.overall_ai_score * 100).toFixed(1)}% |
| </span> |
| </div> |
| <div class="result-meta"> |
| <span>📅 ${new Date(result.upload_timestamp).toLocaleDateString()}</span> |
| <span>📝 ${result.word_count} words</span> |
| </div> |
| </div> |
| `).join(''); |
| |
| document.getElementById('historyList').innerHTML = html; |
| } catch (error) { |
| console.error('Error loading history:', error); |
| } |
| } |
| |
| async function loadStatistics() { |
| try { |
| const response = await fetch(`${API_BASE}/statistics/summary`); |
| const data = await response.json(); |
| |
| if (!data.summary) { |
| return; |
| } |
| |
| const stats = data.summary; |
| const html = ` |
| <div class="stats-grid" style="margin-bottom: 20px;"> |
| <div class="stat-box"> |
| <div class="stat-label">Total Analyses</div> |
| <div class="stat-value">${stats.total_analyses}</div> |
| </div> |
| <div class="stat-box"> |
| <div class="stat-label">Average AI Score</div> |
| <div class="stat-value">${(stats.average_ai_score * 100).toFixed(1)}%</div> |
| </div> |
| <div class="stat-box"> |
| <div class="stat-label">Text Analyzed</div> |
| <div class="stat-value">${(stats.total_text_analyzed / 1000).toFixed(0)}K chars</div> |
| </div> |
| </div> |
| <div class="stats-grid"> |
| <div class="stat-box"> |
| <div class="stat-label">Likely Human</div> |
| <div class="stat-value" style="color: #28a745;">${stats.likely_human}</div> |
| </div> |
| <div class="stat-box"> |
| <div class="stat-label">Suspicious</div> |
| <div class="stat-value" style="color: #ffc107;">${stats.suspicious}</div> |
| </div> |
| <div class="stat-box"> |
| <div class="stat-label">Likely AI</div> |
| <div class="stat-value" style="color: #dc3545;">${stats.likely_ai}</div> |
| </div> |
| </div> |
| `; |
| |
| document.getElementById('statistics').innerHTML = html; |
| } catch (error) { |
| console.error('Error loading statistics:', error); |
| } |
| } |
| |
| function getScoreBadgeClass(score) { |
| if (score < 0.3) return 'score-human'; |
| if (score < 0.7) return 'score-suspicious'; |
| return 'score-ai'; |
| } |
| |
| async function viewResult(resultId) { |
| try { |
| const response = await fetch(`${API_BASE}/results/${resultId}`); |
| const data = await response.json(); |
| |
| if (response.ok && data.result) { |
| currentResult = data.result; |
| displayAnalysisResult({ |
| ...data.result, |
| detector_results: data.result.detector_results || {}, |
| enabled_detectors: [], |
| overall_ai_score_percentage: `${(data.result.overall_ai_score * 100).toFixed(1)}%`, |
| status_label: 'View from history', |
| text_stats: { |
| character_count: data.result.text_length, |
| word_count: data.result.word_count, |
| sentence_count: 0, |
| average_word_length: 0 |
| } |
| }); |
| } |
| } catch (error) { |
| console.error('Error viewing result:', error); |
| } |
| } |
| |
| |
| |
| |
| |
| function showLoading(show) { |
| document.getElementById('loadingIndicator').style.display = show ? 'block' : 'none'; |
| uploadButton.disabled = show; |
| analyzeButton.disabled = show; |
| } |
| |
| function showAlert(message, type = 'info') { |
| const alertHTML = ` |
| <div class="alert alert-${type}"> |
| ${message} |
| </div> |
| `; |
| const container = document.getElementById('alertContainer'); |
| container.innerHTML = alertHTML; |
| |
| |
| setTimeout(() => hideAlert(), 5000); |
| } |
| |
| function hideAlert() { |
| document.getElementById('alertContainer').innerHTML = ''; |
| } |
| |
| |
| document.querySelectorAll('.tab-button').forEach(button => { |
| button.addEventListener('click', (e) => { |
| const tabName = e.target.dataset.tab; |
| |
| |
| document.querySelectorAll('.tab-content').forEach(tab => { |
| tab.classList.remove('active'); |
| }); |
| |
| |
| document.querySelectorAll('.tab-button').forEach(btn => { |
| btn.classList.remove('active'); |
| }); |
| |
| |
| document.getElementById(tabName + 'Tab').classList.add('active'); |
| e.target.classList.add('active'); |
| }); |
| }); |
| |
| |
| loadHistory(); |
| loadStatistics(); |
| </script> |
| </body> |
| </html> |
|
|