|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>AI Text Detector - Verifying Content Authenticity Using Statistics</title> |
|
|
<style> |
|
|
* { |
|
|
margin: 0; |
|
|
padding: 0; |
|
|
box-sizing: border-box; |
|
|
} |
|
|
:root { |
|
|
--primary: #06b6d4; |
|
|
--primary-dark: #0891b2; |
|
|
--secondary: #3b82f6; |
|
|
--success: #10b981; |
|
|
--warning: #f59e0b; |
|
|
--danger: #ef4444; |
|
|
--bg-dark: #0f172a; |
|
|
--bg-darker: #020617; |
|
|
--bg-panel: rgba(30, 41, 59, 0.95); |
|
|
--text-primary: #f1f5f9; |
|
|
--text-secondary: #94a3b8; |
|
|
--text-muted: #64748b; |
|
|
--border: rgba(71, 85, 105, 0.5); |
|
|
} |
|
|
body { |
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; |
|
|
background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%); |
|
|
color: var(--text-primary); |
|
|
line-height: 1.6; |
|
|
min-height: 100vh; |
|
|
} |
|
|
|
|
|
.header { |
|
|
background: rgba(15, 23, 42, 0.98); |
|
|
backdrop-filter: blur(10px); |
|
|
padding: 1rem 2rem; |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
align-items: center; |
|
|
border-bottom: 1px solid var(--border); |
|
|
position: sticky; |
|
|
top: 0; |
|
|
z-index: 1000; |
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
|
|
} |
|
|
.logo { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 0.75rem; |
|
|
font-size: 1.5rem; |
|
|
font-weight: 700; |
|
|
color: #fff; |
|
|
text-decoration: none; |
|
|
} |
|
|
.logo-icon { |
|
|
width: 40px; |
|
|
height: 40px; |
|
|
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); |
|
|
border-radius: 10px; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
font-size: 1.5rem; |
|
|
box-shadow: 0 4px 12px rgba(6, 182, 212, 0.3); |
|
|
} |
|
|
.nav-links { |
|
|
display: flex; |
|
|
gap: 2rem; |
|
|
align-items: center; |
|
|
} |
|
|
.nav-link { |
|
|
color: var(--text-secondary); |
|
|
text-decoration: none; |
|
|
font-weight: 500; |
|
|
transition: color 0.3s; |
|
|
cursor: pointer; |
|
|
} |
|
|
.nav-link:hover { |
|
|
color: var(--primary); |
|
|
} |
|
|
.try-btn { |
|
|
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); |
|
|
color: #fff; |
|
|
padding: 0.75rem 1.5rem; |
|
|
border-radius: 8px; |
|
|
font-weight: 600; |
|
|
border: none; |
|
|
cursor: pointer; |
|
|
transition: transform 0.3s, box-shadow 0.3s; |
|
|
text-decoration: none; |
|
|
display: inline-block; |
|
|
} |
|
|
.try-btn:hover { |
|
|
transform: translateY(-2px); |
|
|
box-shadow: 0 8px 20px rgba(6, 182, 212, 0.4); |
|
|
} |
|
|
|
|
|
.landing-page { |
|
|
display: block; |
|
|
} |
|
|
.hero { |
|
|
max-width: 1200px; |
|
|
margin: 0 auto; |
|
|
padding: 6rem 2rem 4rem; |
|
|
text-align: center; |
|
|
} |
|
|
.hero-title { |
|
|
font-size: 3.5rem; |
|
|
font-weight: 800; |
|
|
margin-bottom: 1.5rem; |
|
|
background: linear-gradient(135deg, #fff 0%, var(--primary) 100%); |
|
|
-webkit-background-clip: text; |
|
|
-webkit-text-fill-color: transparent; |
|
|
background-clip: text; |
|
|
line-height: 1.2; |
|
|
} |
|
|
.hero-subtitle { |
|
|
font-size: 1.5rem; |
|
|
color: var(--text-secondary); |
|
|
margin-bottom: 1rem; |
|
|
} |
|
|
.hero-description { |
|
|
font-size: 1.1rem; |
|
|
color: var(--text-muted); |
|
|
max-width: 700px; |
|
|
margin: 0 auto 3rem; |
|
|
} |
|
|
.accuracy-badge { |
|
|
display: inline-block; |
|
|
background: linear-gradient(135deg, rgba(16, 185, 129, 0.2) 0%, rgba(6, 182, 212, 0.2) 100%); |
|
|
border: 2px solid var(--success); |
|
|
padding: 1rem 2rem; |
|
|
border-radius: 12px; |
|
|
font-size: 1.5rem; |
|
|
font-weight: 700; |
|
|
color: var(--success); |
|
|
margin-bottom: 2rem; |
|
|
} |
|
|
.stats-grid { |
|
|
display: grid; |
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); |
|
|
gap: 2rem; |
|
|
max-width: 1000px; |
|
|
margin: 4rem auto; |
|
|
padding: 0 2rem; |
|
|
} |
|
|
.stat-card { |
|
|
background: var(--bg-panel); |
|
|
padding: 2rem; |
|
|
border-radius: 16px; |
|
|
border: 1px solid var(--border); |
|
|
text-align: center; |
|
|
} |
|
|
.stat-value { |
|
|
font-size: 2.5rem; |
|
|
font-weight: 800; |
|
|
color: var(--primary); |
|
|
margin-bottom: 0.5rem; |
|
|
} |
|
|
.stat-label { |
|
|
color: var(--text-secondary); |
|
|
font-size: 0.95rem; |
|
|
} |
|
|
|
|
|
.features-section { |
|
|
max-width: 1200px; |
|
|
margin: 6rem auto; |
|
|
padding: 0 2rem; |
|
|
} |
|
|
.section-title { |
|
|
font-size: 2.5rem; |
|
|
font-weight: 700; |
|
|
text-align: center; |
|
|
margin-bottom: 1rem; |
|
|
} |
|
|
.section-subtitle { |
|
|
text-align: center; |
|
|
color: var(--text-secondary); |
|
|
font-size: 1.1rem; |
|
|
margin-bottom: 4rem; |
|
|
} |
|
|
.features-grid { |
|
|
display: grid; |
|
|
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); |
|
|
gap: 2rem; |
|
|
} |
|
|
.feature-card { |
|
|
background: var(--bg-panel); |
|
|
padding: 2.5rem; |
|
|
border-radius: 16px; |
|
|
border: 1px solid var(--border); |
|
|
transition: transform 0.3s, box-shadow 0.3s; |
|
|
} |
|
|
.feature-card:hover { |
|
|
transform: translateY(-5px); |
|
|
box-shadow: 0 10px 30px rgba(6, 182, 212, 0.2); |
|
|
} |
|
|
.feature-icon { |
|
|
font-size: 2.5rem; |
|
|
margin-bottom: 1rem; |
|
|
} |
|
|
.feature-title { |
|
|
font-size: 1.4rem; |
|
|
font-weight: 700; |
|
|
margin-bottom: 1rem; |
|
|
color: #fff; |
|
|
} |
|
|
.feature-description { |
|
|
color: var(--text-secondary); |
|
|
line-height: 1.6; |
|
|
} |
|
|
|
|
|
.metrics-info { |
|
|
max-width: 1200px; |
|
|
margin: 6rem auto; |
|
|
padding: 0 2rem; |
|
|
} |
|
|
.metric-card { |
|
|
background: var(--bg-panel); |
|
|
padding: 2rem; |
|
|
border-radius: 12px; |
|
|
border: 1px solid var(--border); |
|
|
margin-bottom: 1.5rem; |
|
|
display: grid; |
|
|
grid-template-columns: 100px 1fr; |
|
|
gap: 2rem; |
|
|
align-items: center; |
|
|
} |
|
|
.metric-icon-box { |
|
|
width: 80px; |
|
|
height: 80px; |
|
|
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); |
|
|
border-radius: 12px; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
font-size: 2rem; |
|
|
} |
|
|
.metric-content h3 { |
|
|
font-size: 1.3rem; |
|
|
margin-bottom: 0.5rem; |
|
|
color: #fff; |
|
|
} |
|
|
.metric-weight { |
|
|
display: inline-block; |
|
|
background: rgba(6, 182, 212, 0.2); |
|
|
padding: 0.25rem 0.75rem; |
|
|
border-radius: 6px; |
|
|
font-size: 0.85rem; |
|
|
color: var(--primary); |
|
|
font-weight: 600; |
|
|
margin-left: 0.5rem; |
|
|
} |
|
|
|
|
|
.analysis-interface { |
|
|
display: none; |
|
|
max-width: 1600px; |
|
|
margin: 2rem auto; |
|
|
padding: 0 2rem 2rem; |
|
|
} |
|
|
.interface-grid { |
|
|
display: grid; |
|
|
grid-template-columns: 1fr 1fr; |
|
|
gap: 2rem; |
|
|
align-items: start; |
|
|
} |
|
|
.panel { |
|
|
background: var(--bg-panel); |
|
|
border-radius: 16px; |
|
|
padding: 2rem; |
|
|
border: 1px solid var(--border); |
|
|
backdrop-filter: blur(10px); |
|
|
height: 850px; |
|
|
overflow: hidden; |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
} |
|
|
.panel-content { |
|
|
flex: 1; |
|
|
overflow-y: auto; |
|
|
padding: 1rem 0; |
|
|
} |
|
|
.panel-title { |
|
|
font-size: 1.5rem; |
|
|
font-weight: 700; |
|
|
margin-bottom: 1.5rem; |
|
|
color: #fff; |
|
|
} |
|
|
.input-tabs { |
|
|
display: flex; |
|
|
gap: 1rem; |
|
|
margin-bottom: 1.5rem; |
|
|
} |
|
|
.input-tab { |
|
|
flex: 1; |
|
|
padding: 0.75rem 1rem; |
|
|
background: rgba(51, 65, 85, 0.6); |
|
|
border: none; |
|
|
border-radius: 8px; |
|
|
color: var(--text-secondary); |
|
|
cursor: pointer; |
|
|
font-size: 0.95rem; |
|
|
font-weight: 600; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
gap: 0.5rem; |
|
|
transition: all 0.3s; |
|
|
} |
|
|
.input-tab.active { |
|
|
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); |
|
|
color: #fff; |
|
|
} |
|
|
.input-tab:hover:not(.active) { |
|
|
background: rgba(71, 85, 105, 0.8); |
|
|
} |
|
|
.tab-content { |
|
|
display: none; |
|
|
} |
|
|
.tab-content.active { |
|
|
display: block; |
|
|
} |
|
|
.text-input { |
|
|
width: 100%; |
|
|
min-height: 450px; |
|
|
padding: 1rem; |
|
|
background: rgba(15, 23, 42, 0.8); |
|
|
border: 1px solid var(--border); |
|
|
border-radius: 8px; |
|
|
color: var(--text-primary); |
|
|
font-size: 0.95rem; |
|
|
line-height: 1.8; |
|
|
resize: vertical; |
|
|
font-family: inherit; |
|
|
} |
|
|
.text-input::placeholder { |
|
|
color: var(--text-muted); |
|
|
} |
|
|
.text-input:focus { |
|
|
outline: none; |
|
|
border-color: var(--primary); |
|
|
} |
|
|
.file-upload-area { |
|
|
border: 2px dashed var(--border); |
|
|
border-radius: 8px; |
|
|
padding: 3rem; |
|
|
text-align: center; |
|
|
cursor: pointer; |
|
|
transition: all 0.3s; |
|
|
background: rgba(15, 23, 42, 0.5); |
|
|
} |
|
|
.file-upload-area:hover { |
|
|
border-color: var(--primary); |
|
|
background: rgba(6, 182, 212, 0.05); |
|
|
} |
|
|
.file-upload-area.drag-over { |
|
|
border-color: var(--primary); |
|
|
background: rgba(6, 182, 212, 0.1); |
|
|
} |
|
|
.file-upload-icon { |
|
|
font-size: 3rem; |
|
|
margin-bottom: 1rem; |
|
|
} |
|
|
.file-input { |
|
|
display: none; |
|
|
} |
|
|
.file-name-display { |
|
|
margin-top: 1rem; |
|
|
padding: 0.75rem; |
|
|
background: rgba(6, 182, 212, 0.1); |
|
|
border-radius: 6px; |
|
|
color: var(--primary); |
|
|
display: none; |
|
|
} |
|
|
.options-section { |
|
|
margin: 1.5rem 0; |
|
|
padding: 1rem; |
|
|
background: rgba(51, 65, 85, 0.3); |
|
|
border-radius: 8px; |
|
|
} |
|
|
.option-row { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 0.75rem; |
|
|
margin-bottom: 0.75rem; |
|
|
} |
|
|
.option-row:last-child { |
|
|
margin-bottom: 0; |
|
|
} |
|
|
.option-label { |
|
|
font-size: 0.9rem; |
|
|
color: var(--text-secondary); |
|
|
flex: 1; |
|
|
} |
|
|
select { |
|
|
background: rgba(15, 23, 42, 0.8); |
|
|
border: 1px solid var(--border); |
|
|
padding: 0.5rem; |
|
|
border-radius: 6px; |
|
|
color: var(--text-primary); |
|
|
font-size: 0.9rem; |
|
|
cursor: pointer; |
|
|
} |
|
|
select:focus { |
|
|
outline: none; |
|
|
border-color: var(--primary); |
|
|
} |
|
|
.checkbox-wrapper { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 0.5rem; |
|
|
} |
|
|
input[type="checkbox"] { |
|
|
width: 18px; |
|
|
height: 18px; |
|
|
cursor: pointer; |
|
|
} |
|
|
.analyze-btn { |
|
|
width: 100%; |
|
|
padding: 1rem; |
|
|
margin-top: 1.5rem; |
|
|
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%); |
|
|
color: #fff; |
|
|
border: none; |
|
|
border-radius: 8px; |
|
|
font-size: 1rem; |
|
|
font-weight: 700; |
|
|
cursor: pointer; |
|
|
transition: all 0.3s; |
|
|
} |
|
|
.analyze-btn:hover:not(:disabled) { |
|
|
transform: translateY(-2px); |
|
|
box-shadow: 0 10px 25px rgba(6, 182, 212, 0.3); |
|
|
} |
|
|
.analyze-btn:disabled { |
|
|
opacity: 0.5; |
|
|
cursor: not-allowed; |
|
|
transform: none; |
|
|
} |
|
|
|
|
|
.report-tabs { |
|
|
display: flex; |
|
|
gap: 1rem; |
|
|
margin-bottom: 1.5rem; |
|
|
border-bottom: 1px solid var(--border); |
|
|
padding-bottom: 0.5rem; |
|
|
} |
|
|
.report-tab { |
|
|
padding: 0.75rem 1rem; |
|
|
background: none; |
|
|
border: none; |
|
|
color: var(--text-secondary); |
|
|
cursor: pointer; |
|
|
font-size: 0.95rem; |
|
|
font-weight: 600; |
|
|
border-bottom: 3px solid transparent; |
|
|
transition: all 0.3s; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 0.5rem; |
|
|
} |
|
|
.report-tab.active { |
|
|
color: var(--primary); |
|
|
border-bottom-color: var(--primary); |
|
|
} |
|
|
.report-content { |
|
|
display: none; |
|
|
} |
|
|
.report-content.active { |
|
|
display: block; |
|
|
} |
|
|
|
|
|
.empty-state { |
|
|
text-align: center; |
|
|
padding: 4rem 2rem; |
|
|
} |
|
|
.empty-icon { |
|
|
width: 80px; |
|
|
height: 80px; |
|
|
margin: 0 auto 1.5rem; |
|
|
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); |
|
|
border-radius: 50%; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
font-size: 2.5rem; |
|
|
} |
|
|
.empty-title { |
|
|
font-size: 1.5rem; |
|
|
font-weight: 700; |
|
|
margin-bottom: 1rem; |
|
|
color: #fff; |
|
|
} |
|
|
.empty-description { |
|
|
color: var(--text-secondary); |
|
|
line-height: 1.6; |
|
|
} |
|
|
|
|
|
.loading { |
|
|
text-align: center; |
|
|
padding: 3rem; |
|
|
} |
|
|
.spinner { |
|
|
width: 50px; |
|
|
height: 50px; |
|
|
border: 4px solid rgba(71, 85, 105, 0.3); |
|
|
border-top-color: var(--primary); |
|
|
border-radius: 50%; |
|
|
animation: spin 1s linear infinite; |
|
|
margin: 0 auto 1rem; |
|
|
} |
|
|
@keyframes spin { |
|
|
to { transform: rotate(360deg); } |
|
|
} |
|
|
|
|
|
.result-summary { |
|
|
text-align: center; |
|
|
padding: 2rem 0; |
|
|
} |
|
|
.gauge-container { |
|
|
width: 220px; |
|
|
height: 220px; |
|
|
margin: 0 auto 2rem; |
|
|
position: relative; |
|
|
} |
|
|
.gauge-circle { |
|
|
width: 100%; |
|
|
height: 100%; |
|
|
border-radius: 50%; |
|
|
background: conic-gradient(var(--gauge-color) 0deg, var(--gauge-color) var(--gauge-degree), rgba(51, 65, 85, 0.3) var(--gauge-degree)); |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); |
|
|
} |
|
|
.gauge-inner { |
|
|
width: 170px; |
|
|
height: 170px; |
|
|
background: var(--bg-panel); |
|
|
border-radius: 50%; |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
} |
|
|
.gauge-value { |
|
|
font-size: 3rem; |
|
|
font-weight: 800; |
|
|
color: var(--gauge-color); |
|
|
} |
|
|
.gauge-label { |
|
|
font-size: 0.9rem; |
|
|
color: var(--text-secondary); |
|
|
margin-top: 0.25rem; |
|
|
} |
|
|
.result-info-grid { |
|
|
display: grid; |
|
|
grid-template-columns: 1fr 1fr 1fr; |
|
|
gap: 1.5rem; |
|
|
margin: 2rem 0; |
|
|
} |
|
|
.info-card { |
|
|
background: rgba(51, 65, 85, 0.3); |
|
|
padding: 1.5rem; |
|
|
border-radius: 10px; |
|
|
border: 1px solid var(--border); |
|
|
} |
|
|
.info-label { |
|
|
font-size: 0.85rem; |
|
|
color: var(--text-secondary); |
|
|
margin-bottom: 0.5rem; |
|
|
text-transform: uppercase; |
|
|
letter-spacing: 0.5px; |
|
|
} |
|
|
.info-value { |
|
|
font-size: 1.4rem; |
|
|
font-weight: 700; |
|
|
color: #fff; |
|
|
} |
|
|
.confidence-badge { |
|
|
display: inline-block; |
|
|
padding: 0.4rem 1rem; |
|
|
border-radius: 6px; |
|
|
font-size: 0.9rem; |
|
|
font-weight: 600; |
|
|
} |
|
|
.confidence-high { |
|
|
background: rgba(16, 185, 129, 0.2); |
|
|
color: var(--success); |
|
|
} |
|
|
.confidence-medium { |
|
|
background: rgba(245, 158, 11, 0.2); |
|
|
color: var(--warning); |
|
|
} |
|
|
.confidence-low { |
|
|
background: rgba(239, 68, 68, 0.2); |
|
|
color: var(--danger); |
|
|
} |
|
|
|
|
|
.reasoning-box { |
|
|
background: rgba(51, 65, 85, 0.4); |
|
|
padding: 1.5rem; |
|
|
border-radius: 10px; |
|
|
border-left: 4px solid var(--primary); |
|
|
margin-top: 2rem; |
|
|
} |
|
|
.reasoning-title { |
|
|
font-weight: 700; |
|
|
margin-bottom: 1rem; |
|
|
color: var(--primary); |
|
|
font-size: 1.1rem; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 0.5rem; |
|
|
} |
|
|
.reasoning-text { |
|
|
color: var(--text-secondary); |
|
|
line-height: 1.7; |
|
|
} |
|
|
|
|
|
.reasoning-box.enhanced { |
|
|
background: linear-gradient(135deg, rgba(30, 41, 59, 0.95) 0%, rgba(15, 23, 42, 0.95) 100%); |
|
|
border: 1px solid rgba(71, 85, 105, 0.5); |
|
|
border-radius: 12px; |
|
|
padding: 1.5rem; |
|
|
margin-top: 2rem; |
|
|
backdrop-filter: blur(10px); |
|
|
} |
|
|
.reasoning-header { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 0.75rem; |
|
|
margin-bottom: 1rem; |
|
|
} |
|
|
.reasoning-icon { |
|
|
font-size: 1.5rem; |
|
|
} |
|
|
.reasoning-title { |
|
|
font-size: 1.1rem; |
|
|
font-weight: 700; |
|
|
color: var(--primary); |
|
|
flex: 1; |
|
|
} |
|
|
.confidence-tag { |
|
|
padding: 0.25rem 0.75rem; |
|
|
border-radius: 20px; |
|
|
font-size: 0.8rem; |
|
|
font-weight: 600; |
|
|
text-transform: uppercase; |
|
|
} |
|
|
.high-confidence { |
|
|
background: rgba(16, 185, 129, 0.2); |
|
|
color: var(--success); |
|
|
border: 1px solid rgba(16, 185, 129, 0.3); |
|
|
} |
|
|
.medium-confidence { |
|
|
background: rgba(245, 158, 11, 0.2); |
|
|
color: var(--warning); |
|
|
border: 1px solid rgba(245, 158, 11, 0.3); |
|
|
} |
|
|
.low-confidence { |
|
|
background: rgba(239, 68, 68, 0.2); |
|
|
color: var(--danger); |
|
|
border: 1px solid rgba(239, 68, 68, 0.3); |
|
|
} |
|
|
.verdict-summary { |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
align-items: center; |
|
|
margin-bottom: 1.5rem; |
|
|
padding: 1rem; |
|
|
background: rgba(51, 65, 85, 0.3); |
|
|
border-radius: 8px; |
|
|
} |
|
|
.verdict-text { |
|
|
font-size: 1.3rem; |
|
|
font-weight: 800; |
|
|
color: var(--warning); |
|
|
} |
|
|
.probability { |
|
|
color: var(--text-secondary); |
|
|
font-size: 0.95rem; |
|
|
} |
|
|
.probability-value { |
|
|
color: var(--text-primary); |
|
|
font-weight: 700; |
|
|
} |
|
|
.metrics-breakdown { |
|
|
margin-bottom: 1.5rem; |
|
|
} |
|
|
.breakdown-header { |
|
|
font-size: 0.9rem; |
|
|
font-weight: 600; |
|
|
color: var(--text-secondary); |
|
|
margin-bottom: 1rem; |
|
|
text-transform: uppercase; |
|
|
letter-spacing: 0.5px; |
|
|
} |
|
|
.metric-indicator { |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
align-items: left; |
|
|
padding: 0.75rem; |
|
|
margin-bottom: 0.5rem; |
|
|
border-radius: 8px; |
|
|
transition: all 0.2s ease; |
|
|
} |
|
|
.metric-indicator:hover { |
|
|
background: rgba(51, 65, 85, 0.4); |
|
|
transform: translateX(4px); |
|
|
} |
|
|
.metric-name { |
|
|
font-weight: 400; |
|
|
color: var(--text-primary); |
|
|
min-width: 140px; |
|
|
} |
|
|
.metric-details { |
|
|
display: flex; |
|
|
gap: 1rem; |
|
|
align-items: center; |
|
|
} |
|
|
.verdict-badge { |
|
|
padding: 0.2rem 0.6rem; |
|
|
border-radius: 6px; |
|
|
font-size: 0.75rem; |
|
|
font-weight: 700; |
|
|
text-transform: uppercase; |
|
|
min-width: 60px; |
|
|
text-align: center; |
|
|
} |
|
|
.ai-badge { |
|
|
background: rgba(239, 68, 68, 0.2); |
|
|
color: var(--danger); |
|
|
border: 1px solid rgba(239, 68, 68, 0.3); |
|
|
} |
|
|
.human-badge { |
|
|
background: rgba(16, 185, 129, 0.2); |
|
|
color: var(--success); |
|
|
border: 1px solid rgba(16, 185, 129, 0.3); |
|
|
} |
|
|
.confidence, .weight { |
|
|
font-size: 0.8rem; |
|
|
color: var(--text-muted); |
|
|
min-width: 100px; |
|
|
} |
|
|
.agreement-indicator { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 0.5rem; |
|
|
padding: 0.75rem; |
|
|
background: rgba(16, 185, 129, 0.1); |
|
|
border: 1px solid rgba(16, 185, 129, 0.2); |
|
|
border-radius: 8px; |
|
|
color: var(--success); |
|
|
} |
|
|
.agreement-icon { |
|
|
font-weight: 700; |
|
|
} |
|
|
.agreement-text { |
|
|
font-size: 0.9rem; |
|
|
font-weight: 600; |
|
|
} |
|
|
|
|
|
.attribution-section { |
|
|
margin-top: 2rem; |
|
|
padding: 1.5rem; |
|
|
background: rgba(51, 65, 85, 0.3); |
|
|
border-radius: 10px; |
|
|
border: 1px solid var(--border); |
|
|
} |
|
|
.attribution-title { |
|
|
font-size: 1.1rem; |
|
|
font-weight: 700; |
|
|
margin-bottom: 1rem; |
|
|
color: #fff; |
|
|
} |
|
|
.model-match { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: space-between; |
|
|
padding: 0.75rem; |
|
|
background: rgba(6, 182, 212, 0.1); |
|
|
border-radius: 6px; |
|
|
margin-bottom: 0.5rem; |
|
|
} |
|
|
.model-name { |
|
|
font-weight: 600; |
|
|
color: var(--text-primary); |
|
|
} |
|
|
.model-confidence { |
|
|
font-weight: 700; |
|
|
color: var(--primary); |
|
|
} |
|
|
.attribution-confidence { |
|
|
margin-top: 0.75rem; |
|
|
font-size: 0.85rem; |
|
|
color: var(--text-secondary); |
|
|
} |
|
|
.attribution-uncertain { |
|
|
color: var(--text-muted); |
|
|
font-style: italic; |
|
|
margin-top: 0.5rem; |
|
|
font-size: 0.9rem; |
|
|
} |
|
|
.attribution-reasoning { |
|
|
color: var(--text-secondary); |
|
|
margin-top: 1rem; |
|
|
font-size: 0.9rem; |
|
|
line-height: 1.4; |
|
|
} |
|
|
|
|
|
.download-actions { |
|
|
display: flex; |
|
|
gap: 1rem; |
|
|
margin-top: 2rem; |
|
|
} |
|
|
.download-btn { |
|
|
flex: 1; |
|
|
padding: 0.75rem; |
|
|
background: rgba(51, 65, 85, 0.6); |
|
|
border: 1px solid var(--border); |
|
|
border-radius: 8px; |
|
|
color: var(--text-primary); |
|
|
font-weight: 600; |
|
|
cursor: pointer; |
|
|
transition: all 0.3s; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
gap: 0.5rem; |
|
|
} |
|
|
.download-btn:hover { |
|
|
background: var(--primary); |
|
|
border-color: var(--primary); |
|
|
transform: translateY(-2px); |
|
|
} |
|
|
|
|
|
.action-buttons { |
|
|
display: flex; |
|
|
gap: 1rem; |
|
|
margin-top: 1.5rem; |
|
|
} |
|
|
.action-btn { |
|
|
flex: 1; |
|
|
padding: 0.75rem; |
|
|
background: rgba(51, 65, 85, 0.6); |
|
|
border: 1px solid var(--border); |
|
|
border-radius: 8px; |
|
|
color: var(--text-primary); |
|
|
font-weight: 600; |
|
|
cursor: pointer; |
|
|
transition: all 0.3s; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
gap: 0.5rem; |
|
|
} |
|
|
.action-btn:hover { |
|
|
background: var(--primary); |
|
|
border-color: var(--primary); |
|
|
transform: translateY(-2px); |
|
|
} |
|
|
.action-btn.refresh { |
|
|
background: rgba(245, 158, 11, 0.2); |
|
|
border-color: var(--warning); |
|
|
color: var(--warning); |
|
|
} |
|
|
.action-btn.refresh:hover { |
|
|
background: var(--warning); |
|
|
color: var(--bg-darker); |
|
|
} |
|
|
|
|
|
.metrics-grid { |
|
|
display: grid; |
|
|
grid-template-columns: repeat(2, 1fr); |
|
|
gap: 1rem; |
|
|
} |
|
|
.metric-result-card { |
|
|
background: rgba(51, 65, 85, 0.4); |
|
|
padding: 1.5rem; |
|
|
border-radius: 10px; |
|
|
border: 1px solid var(--border); |
|
|
} |
|
|
.metric-header { |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
align-items: center; |
|
|
margin-bottom: 0.75rem; |
|
|
} |
|
|
.metric-name { |
|
|
font-weight: 700; |
|
|
color: #fff; |
|
|
font-size: 1.1rem; |
|
|
} |
|
|
.metric-score { |
|
|
font-size: 1.8rem; |
|
|
font-weight: 800; |
|
|
} |
|
|
.metric-verdict { |
|
|
display: inline-block; |
|
|
padding: 0.25rem 0.75rem; |
|
|
border-radius: 6px; |
|
|
font-size: 0.75rem; |
|
|
font-weight: 600; |
|
|
text-transform: uppercase; |
|
|
margin-top: 0.5rem; |
|
|
} |
|
|
.verdict-ai { |
|
|
background: rgba(239, 68, 68, 0.2); |
|
|
color: var(--danger); |
|
|
} |
|
|
.verdict-human { |
|
|
background: rgba(16, 185, 129, 0.2); |
|
|
color: var(--success); |
|
|
} |
|
|
.verdict-uncertain { |
|
|
background: rgba(245, 158, 11, 0.2); |
|
|
color: var(--warning); |
|
|
} |
|
|
.metric-description { |
|
|
font-size: 0.85rem; |
|
|
color: var(--text-secondary); |
|
|
line-height: 1.5; |
|
|
margin-top: 0.75rem; |
|
|
} |
|
|
|
|
|
.highlight-legend { |
|
|
display: flex; |
|
|
gap: 1.5rem; |
|
|
margin-bottom: 1.5rem; |
|
|
padding: 1rem; |
|
|
background: rgba(51, 65, 85, 0.4); |
|
|
border-radius: 8px; |
|
|
flex-wrap: wrap; |
|
|
} |
|
|
.legend-item { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 0.5rem; |
|
|
} |
|
|
.legend-color { |
|
|
width: 20px; |
|
|
height: 20px; |
|
|
border-radius: 4px; |
|
|
} |
|
|
.legend-label { |
|
|
font-size: 0.9rem; |
|
|
color: var(--text-secondary); |
|
|
} |
|
|
.highlighted-text { |
|
|
background: rgba(15, 23, 42, 0.8); |
|
|
padding: 1.5rem; |
|
|
border-radius: 10px; |
|
|
border: 1px solid var(--border); |
|
|
line-height: 1.9; |
|
|
font-size: 0.95rem; |
|
|
} |
|
|
|
|
|
.footer { |
|
|
max-width: 1200px; |
|
|
margin: 6rem auto 0; |
|
|
padding: 3rem 2rem; |
|
|
border-top: 1px solid var(--border); |
|
|
text-align: center; |
|
|
color: var(--text-muted); |
|
|
} |
|
|
|
|
|
.metrics-carousel-container { |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
height: 100%; |
|
|
} |
|
|
.metrics-carousel-content { |
|
|
flex: 1; |
|
|
padding: 0; |
|
|
display: flex; |
|
|
align-items: flex-start; |
|
|
justify-content: flex-start; |
|
|
overflow-y: auto; |
|
|
padding: 1rem; |
|
|
} |
|
|
.metric-slide { |
|
|
display: none; |
|
|
width: 100%; |
|
|
padding: 1rem; |
|
|
} |
|
|
.metric-slide.active { |
|
|
display: block; |
|
|
} |
|
|
.metrics-carousel-nav { |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
align-items: center; |
|
|
padding: 1rem; |
|
|
border-top: 1px solid var(--border); |
|
|
background: rgba(15, 23, 42, 0.8); |
|
|
} |
|
|
.carousel-btn { |
|
|
padding: 0.75rem 1.5rem; |
|
|
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); |
|
|
color: #fff; |
|
|
border: none; |
|
|
border-radius: 8px; |
|
|
font-weight: 600; |
|
|
cursor: pointer; |
|
|
transition: transform 0.3s, box-shadow 0.3s; |
|
|
} |
|
|
.carousel-btn:hover { |
|
|
transform: translateY(-2px); |
|
|
box-shadow: 0 8px 20px rgba(6, 182, 212, 0.4); |
|
|
} |
|
|
.carousel-btn:disabled { |
|
|
opacity: 0.5; |
|
|
cursor: not-allowed; |
|
|
transform: none; |
|
|
} |
|
|
.carousel-position { |
|
|
font-size: 0.9rem; |
|
|
color: var(--text-secondary); |
|
|
font-weight: 600; |
|
|
} |
|
|
|
|
|
.verdict-text { |
|
|
font-size: 1.2rem !important; |
|
|
} |
|
|
.domain-text { |
|
|
font-size: 1.1rem !important; |
|
|
} |
|
|
|
|
|
.verdict-mixed { |
|
|
background: rgba(168, 85, 247, 0.2); |
|
|
color: #a855f7; |
|
|
border: 1px solid rgba(168, 85, 247, 0.3); |
|
|
} |
|
|
|
|
|
|
|
|
.reasoning-bullet-points { |
|
|
margin: 1.5rem 0; |
|
|
line-height: 1.6; |
|
|
text-align: left; |
|
|
} |
|
|
|
|
|
.bullet-point { |
|
|
margin-bottom: 0.75rem; |
|
|
padding-left: 0.5rem; |
|
|
color: var(--text-secondary); |
|
|
font-size: 0.95rem; |
|
|
text-align: left; |
|
|
} |
|
|
|
|
|
.bullet-point:last-child { |
|
|
margin-bottom: 0; |
|
|
} |
|
|
|
|
|
.bullet-point strong { |
|
|
color: var(--text-primary); |
|
|
} |
|
|
|
|
|
|
|
|
@media (max-width: 1200px) { |
|
|
.interface-grid { |
|
|
grid-template-columns: 1fr; |
|
|
} |
|
|
.metrics-grid { |
|
|
grid-template-columns: 1fr; |
|
|
} |
|
|
} |
|
|
@media (max-width: 768px) { |
|
|
.hero-title { |
|
|
font-size: 2.5rem; |
|
|
} |
|
|
.features-grid { |
|
|
grid-template-columns: 1fr; |
|
|
} |
|
|
.metric-card { |
|
|
grid-template-columns: 1fr; |
|
|
text-align: center; |
|
|
} |
|
|
.result-info-grid { |
|
|
grid-template-columns: 1fr; |
|
|
} |
|
|
.nav-links { |
|
|
display: none; |
|
|
} |
|
|
.download-actions, |
|
|
.action-buttons { |
|
|
flex-direction: column; |
|
|
} |
|
|
.highlight-legend { |
|
|
flex-direction: column; |
|
|
gap: 0.75rem; |
|
|
} |
|
|
.panel { |
|
|
height: auto; |
|
|
min-height: 600px; |
|
|
} |
|
|
} |
|
|
|
|
|
html { |
|
|
scroll-behavior: smooth; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
|
|
|
<div class="header"> |
|
|
<a href="#" class="logo" onclick="showLanding(); return false;"> |
|
|
<div class="logo-icon">🔍</div> |
|
|
<span>AI Text Detector</span> |
|
|
</a> |
|
|
<div class="nav-links"> |
|
|
<a href="#features" class="nav-link">Features</a> |
|
|
<a href="#metrics" class="nav-link">Detection Metrics</a> |
|
|
<a href="#" class="nav-link" onclick="showAnalysis(); return false;">Try It Now</a> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="landing-page" id="landing-page"> |
|
|
|
|
|
<section class="hero"> |
|
|
<h1 class="hero-title">AI Text Detection Platform</h1> |
|
|
<p class="hero-subtitle">Verifying Content Authenticity with Precision</p> |
|
|
<p class="hero-description"> |
|
|
Production-ready platform designed to identify AI-generated content across education, |
|
|
publishing, hiring, and research domains using sophisticated ensemble detection. |
|
|
</p> |
|
|
<button class="try-btn" onclick="showAnalysis()"> Try It Now → </button> |
|
|
</section> |
|
|
|
|
|
<div class="stats-grid"> |
|
|
<div class="stat-card"> |
|
|
<div class="stat-value">2.4%</div> |
|
|
<div class="stat-label">False Positive Rate</div> |
|
|
</div> |
|
|
<div class="stat-card"> |
|
|
<div class="stat-value">6</div> |
|
|
<div class="stat-label">Total Detection Metrics</div> |
|
|
</div> |
|
|
<div class="stat-card"> |
|
|
<div class="stat-value">5s</div> |
|
|
<div class="stat-label">Average Processing Time</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<section class="features-section" id="features"> |
|
|
<h2 class="section-title">Why Choose Our Platform?</h2> |
|
|
<p class="section-subtitle"> |
|
|
Advanced technology meets practical application |
|
|
</p> |
|
|
<div class="features-grid"> |
|
|
<div class="feature-card"> |
|
|
<div class="feature-icon">🎯</div> |
|
|
<h3 class="feature-title">Domain-Aware Detection</h3> |
|
|
<p class="feature-description"> |
|
|
Calibrated thresholds for Academic, Technical, Creative, and Casual content types with specialized detection algorithms for each domain. |
|
|
</p> |
|
|
</div> |
|
|
<div class="feature-card"> |
|
|
<div class="feature-icon">🔬</div> |
|
|
<h3 class="feature-title">6-Metric Ensemble</h3> |
|
|
<p class="feature-description"> |
|
|
Combines Perplexity, Entropy, Statistical, Linguistic, Semantic Analysis, and Multi-Perturbation Stability for comprehensive detection with orthogonal signal capture. |
|
|
</p> |
|
|
</div> |
|
|
<div class="feature-card"> |
|
|
<div class="feature-icon">💡</div> |
|
|
<h3 class="feature-title">Explainable Results</h3> |
|
|
<p class="feature-description"> |
|
|
Sentence-level highlighting with confidence scores and detailed reasoning for every detection decision. |
|
|
</p> |
|
|
</div> |
|
|
<div class="feature-card"> |
|
|
<div class="feature-icon">🚀</div> |
|
|
<h3 class="feature-title">Fast Processing</h3> |
|
|
<p class="feature-description"> |
|
|
Analyze short texts in 1.2 seconds, medium documents in 3.5 seconds with parallel metric computation. |
|
|
</p> |
|
|
</div> |
|
|
<div class="feature-card"> |
|
|
<div class="feature-icon">🤖</div> |
|
|
<h3 class="feature-title">Model Attribution</h3> |
|
|
<p class="feature-description"> |
|
|
Identifies which AI model likely generated the text - GPT-4, Claude, Gemini, LLaMA, and more. |
|
|
</p> |
|
|
</div> |
|
|
<div class="feature-card"> |
|
|
<div class="feature-icon">📄</div> |
|
|
<h3 class="feature-title">Multi-Format Support</h3> |
|
|
<p class="feature-description"> |
|
|
Upload and analyze TXT, PDF, DOCX, DOC, and Markdown files with automatic text extraction. |
|
|
</p> |
|
|
</div> |
|
|
</div> |
|
|
</section> |
|
|
|
|
|
<section class="metrics-info" id="metrics"> |
|
|
<h2 class="section-title">Detection Metrics Explained</h2> |
|
|
<p class="section-subtitle"> |
|
|
Understanding the science behind the detection |
|
|
</p> |
|
|
<div class="metric-card"> |
|
|
<div class="metric-icon-box">📊</div> |
|
|
<div class="metric-content"> |
|
|
<h3>Perplexity <span class="metric-weight">Weight: 25%</span></h3> |
|
|
<p>Measures how predictable the text is using GPT-2 language model. AI-generated text typically has lower perplexity (more predictable) than human writing, which tends to be more varied and surprising.</p> |
|
|
</div> |
|
|
</div> |
|
|
<div class="metric-card"> |
|
|
<div class="metric-icon-box">🎲</div> |
|
|
<div class="metric-content"> |
|
|
<h3>Entropy <span class="metric-weight">Weight: 20%</span></h3> |
|
|
<p>Calculates token-level diversity and unpredictability in text sequences. Human writing shows higher entropy with more varied word choices, while AI tends toward more uniform token distributions.</p> |
|
|
</div> |
|
|
</div> |
|
|
<div class="metric-card"> |
|
|
<div class="metric-icon-box">📈</div> |
|
|
<div class="metric-content"> |
|
|
<h3>Structural Analysis <span class="metric-weight">Weight: 15%</span></h3> |
|
|
<p>Analyzes sentence length variance, punctuation patterns, and lexical burstiness. Human writing exhibits more variation in sentence structure and rhythm compared to AI's consistent patterns.</p> |
|
|
</div> |
|
|
</div> |
|
|
<div class="metric-card"> |
|
|
<div class="metric-icon-box">📝</div> |
|
|
<div class="metric-content"> |
|
|
<h3>Linguistic Analysis <span class="metric-weight">Weight: 15%</span></h3> |
|
|
<p>Evaluates POS tag diversity, syntactic complexity, and grammatical patterns. Examines the richness of language structures and whether they match natural human linguistic variation.</p> |
|
|
</div> |
|
|
</div> |
|
|
<div class="metric-card"> |
|
|
<div class="metric-icon-box">🧠</div> |
|
|
<div class="metric-content"> |
|
|
<h3>Semantic Analysis <span class="metric-weight">Weight: 15%</span></h3> |
|
|
<p>Assesses semantic coherence, repetition patterns, and contextual consistency. Detects the subtle semantic fingerprints that distinguish AI-generated content from human writing.</p> |
|
|
</div> |
|
|
</div> |
|
|
<div class="metric-card"> |
|
|
<div class="metric-icon-box">🔍</div> |
|
|
<div class="metric-content"> |
|
|
<h3>Multi-Perturbation Stability <span class="metric-weight">Weight: 10%</span></h3> |
|
|
<p>Tests text stability under random perturbations. AI-generated text tends to maintain higher likelihood scores even when slightly modified, while human text shows more variation.</p> |
|
|
</div> |
|
|
</div> |
|
|
</section> |
|
|
|
|
|
<footer class="footer"> |
|
|
<p>© 2025 AI Text Detector Platform</p> |
|
|
<p style="margin-top: 1rem;">AI detection with enterprise accuracy and explainability.</p> |
|
|
</footer> |
|
|
</div> |
|
|
|
|
|
<div class="analysis-interface" id="analysis-interface"> |
|
|
<div class="interface-grid"> |
|
|
|
|
|
<div class="panel"> |
|
|
<h2 class="panel-title">Submit Content for Analysis</h2> |
|
|
<div class="panel-content"> |
|
|
<div class="input-tabs"> |
|
|
<button class="input-tab active" data-tab="paste"> |
|
|
📋 Paste Text |
|
|
</button> |
|
|
<button class="input-tab" data-tab="upload"> |
|
|
📁 Upload File |
|
|
</button> |
|
|
</div> |
|
|
<div id="paste-tab" class="tab-content active"> |
|
|
<textarea |
|
|
id="text-input" |
|
|
class="text-input" |
|
|
placeholder="Paste your text here for analysis... |
|
|
The more text you provide (minimum 50 characters), the more accurate the detection will be." |
|
|
></textarea> |
|
|
</div> |
|
|
<div id="upload-tab" class="tab-content"> |
|
|
<div class="file-upload-area" id="file-upload-area"> |
|
|
<input type="file" id="file-input" class="file-input" accept=".txt,.pdf,.docx,.doc,.md"> |
|
|
<div class="file-upload-icon">📄</div> |
|
|
<div style="font-size: 1.1rem; font-weight: 600; margin-bottom: 0.5rem;"> |
|
|
Click to upload or drag and drop |
|
|
</div> |
|
|
<div style="color: var(--text-muted); font-size: 0.9rem;"> |
|
|
Supported formats: TXT, PDF, DOCX, DOC, MD |
|
|
</div> |
|
|
<div style="color: var(--text-muted); font-size: 0.85rem; margin-top: 0.5rem;"> |
|
|
Maximum file size: 10MB |
|
|
</div> |
|
|
</div> |
|
|
<div id="file-name-display" class="file-name-display"></div> |
|
|
</div> |
|
|
<div class="options-section"> |
|
|
<div class="option-row"> |
|
|
<label class="option-label">Content Domain:</label> |
|
|
<select id="domain-select"> |
|
|
<option value="">Auto-detect</option> |
|
|
<option value="academic">Academic</option> |
|
|
<option value="technical_doc">Technical/Medical</option> |
|
|
<option value="creative">Creative Writing</option> |
|
|
<option value="social_media">Social Media</option> |
|
|
</select> |
|
|
</div> |
|
|
<div class="option-row"> |
|
|
<label class="option-label">Enable AI Model Attribution:</label> |
|
|
<div class="checkbox-wrapper"> |
|
|
<input type="checkbox" id="enable-attribution" checked> |
|
|
<span style="font-size: 0.85rem; color: var(--text-muted);">Identify which AI model generated the text</span> |
|
|
</div> |
|
|
</div> |
|
|
<div class="option-row"> |
|
|
<label class="option-label">Enable Sentence Highlighting:</label> |
|
|
<div class="checkbox-wrapper"> |
|
|
<input type="checkbox" id="enable-highlighting" checked> |
|
|
<span style="font-size: 0.85rem; color: var(--text-muted);">Show suspicious sentences</span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="option-row"> |
|
|
<label class="option-label">Sentence-Level Analysis:</label> |
|
|
<div class="checkbox-wrapper"> |
|
|
<input type="checkbox" id="use-sentence-level" checked> |
|
|
<span style="font-size: 0.85rem; color: var(--text-muted);">More accurate but slower analysis</span> |
|
|
</div> |
|
|
</div> |
|
|
<div class="option-row"> |
|
|
<label class="option-label">Include Metrics Summary:</label> |
|
|
<div class="checkbox-wrapper"> |
|
|
<input type="checkbox" id="include-metrics-summary" checked> |
|
|
<span style="font-size: 0.85rem; color: var(--text-muted);">Show text analysis statistics</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div style="display: flex; flex-direction: column; gap: 1rem;"> |
|
|
<button id="analyze-btn" class="analyze-btn"> |
|
|
🔍 Analyze Text |
|
|
</button> |
|
|
<div class="action-buttons"> |
|
|
<button id="refresh-btn" class="action-btn refresh"> |
|
|
🔄 Refresh |
|
|
</button> |
|
|
<button id="try-next-btn" class="action-btn"> |
|
|
➕ Try Next |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="panel"> |
|
|
<h2 class="panel-title">Analysis Report</h2> |
|
|
<div class="panel-content"> |
|
|
<div class="report-tabs"> |
|
|
<button class="report-tab active" data-report="summary"> |
|
|
📊 Summary |
|
|
</button> |
|
|
<button class="report-tab" data-report="highlighted"> |
|
|
📝 Highlighted Text |
|
|
</button> |
|
|
<button class="report-tab" data-report="metrics"> |
|
|
ℹ️ Detailed Metrics |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div id="summary-report" class="report-content active"> |
|
|
<div class="empty-state"> |
|
|
<div class="empty-icon">✓</div> |
|
|
<h3 class="empty-title">Ready for Analysis</h3> |
|
|
<p class="empty-description"> |
|
|
Paste text or upload a document to begin comprehensive AI detection analysis. |
|
|
Our 6-metric ensemble will provide detailed insights. |
|
|
</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="highlighted-report" class="report-content"> |
|
|
<div class="empty-state"> |
|
|
<div class="empty-icon">📝</div> |
|
|
<p class="empty-description"> |
|
|
Run an analysis to see sentence-level highlighting |
|
|
</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="metrics-report" class="report-content"> |
|
|
<div class="empty-state"> |
|
|
<div class="empty-icon">📊</div> |
|
|
<p class="empty-description"> |
|
|
Run an analysis to see detailed metric breakdowns |
|
|
</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<script> |
|
|
|
|
|
const API_BASE = ''; |
|
|
let currentAnalysisData = null; |
|
|
let currentMetricIndex = 0; |
|
|
let totalMetrics = 0; |
|
|
|
|
|
|
|
|
function showLanding() { |
|
|
document.getElementById('landing-page').style.display = 'block'; |
|
|
document.getElementById('analysis-interface').style.display = 'none'; |
|
|
window.scrollTo(0, 0); |
|
|
} |
|
|
|
|
|
function showAnalysis() { |
|
|
document.getElementById('landing-page').style.display = 'none'; |
|
|
document.getElementById('analysis-interface').style.display = 'block'; |
|
|
window.scrollTo(0, 0); |
|
|
resetAnalysisInterface(); |
|
|
} |
|
|
|
|
|
|
|
|
function resetAnalysisInterface() { |
|
|
|
|
|
document.getElementById('text-input').value = ''; |
|
|
|
|
|
document.getElementById('file-input').value = ''; |
|
|
document.getElementById('file-name-display').style.display = 'none'; |
|
|
document.getElementById('file-name-display').innerHTML = ''; |
|
|
|
|
|
document.querySelectorAll('.input-tab').forEach(t => t.classList.remove('active')); |
|
|
document.querySelector('.input-tab[data-tab="paste"]').classList.add('active'); |
|
|
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); |
|
|
document.getElementById('paste-tab').classList.add('active'); |
|
|
|
|
|
document.getElementById('domain-select').value = ''; |
|
|
document.getElementById('enable-attribution').checked = true; |
|
|
document.getElementById('enable-highlighting').checked = true; |
|
|
document.getElementById('use-sentence-level').checked = true; |
|
|
document.getElementById('include-metrics-summary').checked = true; |
|
|
|
|
|
document.querySelectorAll('.report-tab').forEach(t => t.classList.remove('active')); |
|
|
document.querySelector('.report-tab[data-report="summary"]').classList.add('active'); |
|
|
document.querySelectorAll('.report-content').forEach(content => content.classList.remove('active')); |
|
|
document.getElementById('summary-report').classList.add('active'); |
|
|
|
|
|
document.getElementById('summary-report').innerHTML = ` |
|
|
<div class="empty-state"> |
|
|
<div class="empty-icon">✓</div> |
|
|
<h3 class="empty-title">Ready for Analysis</h3> |
|
|
<p class="empty-description"> |
|
|
Paste text or upload a document to begin comprehensive AI detection analysis. |
|
|
Our 6-metric ensemble will provide detailed insights. |
|
|
</p> |
|
|
</div> |
|
|
`; |
|
|
document.getElementById('highlighted-report').innerHTML = ` |
|
|
<div class="empty-state"> |
|
|
<div class="empty-icon">📝</div> |
|
|
<p class="empty-description"> |
|
|
Run an analysis to see sentence-level highlighting |
|
|
</p> |
|
|
</div> |
|
|
`; |
|
|
document.getElementById('metrics-report').innerHTML = ` |
|
|
<div class="empty-state"> |
|
|
<div class="empty-icon">📊</div> |
|
|
<p class="empty-description"> |
|
|
Run an analysis to see detailed metric breakdowns |
|
|
</p> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
currentAnalysisData = null; |
|
|
currentMetricIndex = 0; |
|
|
totalMetrics = 0; |
|
|
} |
|
|
|
|
|
|
|
|
document.querySelectorAll('.input-tab').forEach(tab => { |
|
|
tab.addEventListener('click', () => { |
|
|
const tabName = tab.dataset.tab; |
|
|
document.querySelectorAll('.input-tab').forEach(t => t.classList.remove('active')); |
|
|
tab.classList.add('active'); |
|
|
document.querySelectorAll('#paste-tab, #upload-tab').forEach(content => { |
|
|
content.classList.remove('active'); |
|
|
}); |
|
|
document.getElementById(`${tabName}-tab`).classList.add('active'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.report-tab').forEach(tab => { |
|
|
tab.addEventListener('click', () => { |
|
|
const reportName = tab.dataset.report; |
|
|
document.querySelectorAll('.report-tab').forEach(t => t.classList.remove('active')); |
|
|
tab.classList.add('active'); |
|
|
document.querySelectorAll('.report-content').forEach(content => { |
|
|
content.classList.remove('active'); |
|
|
}); |
|
|
document.getElementById(`${reportName}-report`).classList.add('active'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const fileInput = document.getElementById('file-input'); |
|
|
const fileUploadArea = document.getElementById('file-upload-area'); |
|
|
const fileNameDisplay = document.getElementById('file-name-display'); |
|
|
|
|
|
fileUploadArea.addEventListener('click', () => { |
|
|
fileInput.click(); |
|
|
}); |
|
|
|
|
|
fileInput.addEventListener('change', (e) => { |
|
|
handleFileSelect(e.target.files[0]); |
|
|
}); |
|
|
|
|
|
|
|
|
fileUploadArea.addEventListener('dragover', (e) => { |
|
|
e.preventDefault(); |
|
|
fileUploadArea.classList.add('drag-over'); |
|
|
}); |
|
|
|
|
|
fileUploadArea.addEventListener('dragleave', () => { |
|
|
fileUploadArea.classList.remove('drag-over'); |
|
|
}); |
|
|
|
|
|
fileUploadArea.addEventListener('drop', (e) => { |
|
|
e.preventDefault(); |
|
|
fileUploadArea.classList.remove('drag-over'); |
|
|
const file = e.dataTransfer.files[0]; |
|
|
if (file) { |
|
|
fileInput.files = e.dataTransfer.files; |
|
|
handleFileSelect(file); |
|
|
} |
|
|
}); |
|
|
|
|
|
function handleFileSelect(file) { |
|
|
if (!file) return; |
|
|
const allowedTypes = ['.txt', '.pdf', '.docx', '.doc', '.md']; |
|
|
const fileExt = '.' + file.name.split('.').pop().toLowerCase(); |
|
|
if (!allowedTypes.includes(fileExt)) { |
|
|
alert('Unsupported file type. Please upload: TXT, PDF, DOCX, DOC, or MD files.'); |
|
|
return; |
|
|
} |
|
|
if (file.size > 10 * 1024 * 1024) { |
|
|
alert('File size exceeds 10MB limit.'); |
|
|
return; |
|
|
} |
|
|
fileNameDisplay.style.display = 'block'; |
|
|
fileNameDisplay.innerHTML = ` |
|
|
<strong>Selected file:</strong> ${file.name} |
|
|
<span style="color: var(--text-muted);">(${formatFileSize(file.size)})</span> |
|
|
`; |
|
|
} |
|
|
|
|
|
function formatFileSize(bytes) { |
|
|
if (bytes < 1024) return bytes + ' B'; |
|
|
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'; |
|
|
return (bytes / (1024 * 1024)).toFixed(1) + ' MB'; |
|
|
} |
|
|
|
|
|
|
|
|
document.getElementById('analyze-btn').addEventListener('click', async () => { |
|
|
const activeTab = document.querySelector('.input-tab.active').dataset.tab; |
|
|
const textInput = document.getElementById('text-input').value.trim(); |
|
|
const fileInput = document.getElementById('file-input').files[0]; |
|
|
|
|
|
if (activeTab === 'paste' && !textInput) { |
|
|
alert('Please paste some text to analyze (minimum 50 characters).'); |
|
|
return; |
|
|
} |
|
|
if (activeTab === 'paste' && textInput.length < 50) { |
|
|
alert('Text must be at least 50 characters long for accurate analysis.'); |
|
|
return; |
|
|
} |
|
|
if (activeTab === 'upload' && !fileInput) { |
|
|
alert('Please select a file to upload.'); |
|
|
return; |
|
|
} |
|
|
|
|
|
await performAnalysis(activeTab, textInput, fileInput); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('refresh-btn').addEventListener('click', () => { |
|
|
resetAnalysisInterface(); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('try-next-btn').addEventListener('click', () => { |
|
|
resetAnalysisInterface(); |
|
|
}); |
|
|
|
|
|
async function performAnalysis(mode, text, file) { |
|
|
const analyzeBtn = document.getElementById('analyze-btn'); |
|
|
analyzeBtn.disabled = true; |
|
|
analyzeBtn.innerHTML = '⏳ Analyzing...'; |
|
|
showLoading(); |
|
|
|
|
|
try { |
|
|
let response; |
|
|
if (mode === 'paste') { |
|
|
response = await analyzeText(text); |
|
|
} else { |
|
|
response = await analyzeFile(file); |
|
|
} |
|
|
currentAnalysisData = response; |
|
|
displayResults(response); |
|
|
} catch (error) { |
|
|
console.error('Analysis error:', error); |
|
|
showError(error.message || 'Analysis failed. Please try again.'); |
|
|
} finally { |
|
|
analyzeBtn.disabled = false; |
|
|
analyzeBtn.innerHTML = '🔍 Analyze Text'; |
|
|
} |
|
|
} |
|
|
|
|
|
async function analyzeText(text) { |
|
|
const domain = document.getElementById('domain-select').value || null; |
|
|
const enableAttribution = document.getElementById('enable-attribution').checked; |
|
|
const enableHighlighting = document.getElementById('enable-highlighting').checked; |
|
|
const useSentenceLevel = document.getElementById('use-sentence-level').checked; |
|
|
const includeMetricsSummary = document.getElementById('include-metrics-summary').checked; |
|
|
|
|
|
const response = await fetch(`${API_BASE}/api/analyze`, { |
|
|
method: 'POST', |
|
|
headers: { 'Content-Type': 'application/json' }, |
|
|
body: JSON.stringify({ |
|
|
text: text, |
|
|
domain: domain, |
|
|
enable_attribution: enableAttribution, |
|
|
enable_highlighting: enableHighlighting, |
|
|
use_sentence_level: useSentenceLevel, |
|
|
include_metrics_summary: includeMetricsSummary, |
|
|
skip_expensive_metrics: false |
|
|
}) |
|
|
}); |
|
|
|
|
|
if (!response.ok) { |
|
|
const error = await response.json(); |
|
|
throw new Error(error.error || 'Analysis failed'); |
|
|
} |
|
|
return await response.json(); |
|
|
} |
|
|
|
|
|
async function analyzeFile(file) { |
|
|
const domain = document.getElementById('domain-select').value || null; |
|
|
const enableAttribution = document.getElementById('enable-attribution').checked; |
|
|
const useSentenceLevel = document.getElementById('use-sentence-level').checked; |
|
|
const includeMetricsSummary = document.getElementById('include-metrics-summary').checked; |
|
|
|
|
|
const formData = new FormData(); |
|
|
formData.append('file', file); |
|
|
if (domain) formData.append('domain', domain); |
|
|
formData.append('enable_attribution', enableAttribution.toString()); |
|
|
formData.append('use_sentence_level', useSentenceLevel.toString()); |
|
|
formData.append('include_metrics_summary', includeMetricsSummary.toString()); |
|
|
formData.append('skip_expensive_metrics', 'false'); |
|
|
|
|
|
const response = await fetch(`${API_BASE}/api/analyze/file`, { |
|
|
method: 'POST', |
|
|
body: formData |
|
|
}); |
|
|
|
|
|
if (!response.ok) { |
|
|
const error = await response.json(); |
|
|
throw new Error(error.error || 'File analysis failed'); |
|
|
} |
|
|
return await response.json(); |
|
|
} |
|
|
|
|
|
function showLoading() { |
|
|
document.getElementById('summary-report').innerHTML = ` |
|
|
<div class="loading"> |
|
|
<div class="spinner"></div> |
|
|
<p style="color: var(--text-secondary);">Analyzing content with 6-metric ensemble...</p> |
|
|
<p style="color: var(--text-muted); font-size: 0.9rem; margin-top: 0.5rem;"> |
|
|
This may take a few seconds |
|
|
</p> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
function showError(message) { |
|
|
document.getElementById('summary-report').innerHTML = ` |
|
|
<div class="empty-state"> |
|
|
<div class="empty-icon" style="background: linear-gradient(135deg, var(--danger) 0%, #dc2626 100%);">⚠️</div> |
|
|
<h3 class="empty-title">Analysis Failed</h3> |
|
|
<p class="empty-description">${message}</p> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
function displayResults(data) { |
|
|
console.log('Response data:', data); |
|
|
|
|
|
const detection = data.detection_result; |
|
|
if (!detection) { |
|
|
showError('Invalid response structure. Please check the API response format.'); |
|
|
console.error('Full response:', data); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
const ensemble = detection.ensemble_result || detection.ensemble; |
|
|
const prediction = detection.prediction || {}; |
|
|
const metrics = detection.metric_results || detection.metrics; |
|
|
const analysis = detection.analysis || {}; |
|
|
|
|
|
|
|
|
displaySummary(ensemble, prediction, analysis, data.attribution, data.reasoning); |
|
|
|
|
|
|
|
|
if (data.highlighted_html) { |
|
|
displayHighlightedText(data.highlighted_html); |
|
|
} else { |
|
|
document.getElementById('highlighted-report').innerHTML = ` |
|
|
<div class="empty-state"> |
|
|
<p class="empty-description">Highlighting not available for this analysis</p> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
|
|
|
if (metrics && Object.keys(metrics).length > 0) { |
|
|
displayMetricsCarousel(metrics, analysis, ensemble); |
|
|
} else { |
|
|
document.getElementById('metrics-report').innerHTML = ` |
|
|
<div class="empty-state"> |
|
|
<p class="empty-description">Metric details not available</p> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
} |
|
|
|
|
|
function displaySummary(ensemble, prediction, analysis, attribution, reasoning) { |
|
|
|
|
|
const { |
|
|
aiProbability, |
|
|
humanProbability, |
|
|
mixedProbability, |
|
|
verdict, |
|
|
confidence, |
|
|
domain, |
|
|
isAI, |
|
|
gaugeColor, |
|
|
gaugeDegree, |
|
|
confidenceLevel, |
|
|
confidenceClass |
|
|
} = extractSummaryData(ensemble, analysis); |
|
|
|
|
|
|
|
|
const attributionHTML = generateAttributionHTML(attribution); |
|
|
|
|
|
document.getElementById('summary-report').innerHTML = ` |
|
|
<div class="result-summary"> |
|
|
${createGaugeSection(aiProbability, humanProbability, mixedProbability, gaugeColor, gaugeDegree)} |
|
|
${createInfoGrid(verdict, confidence, confidenceClass, domain, mixedProbability)} |
|
|
${createEnhancedReasoningHTML(ensemble, analysis, reasoning)} |
|
|
${attributionHTML} |
|
|
${createDownloadActions()} |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
|
|
|
function extractSummaryData(ensemble, analysis) { |
|
|
const aiProbability = ensemble.ai_probability !== undefined ? |
|
|
(ensemble.ai_probability * 100).toFixed(0) : '0'; |
|
|
|
|
|
const humanProbability = ensemble.human_probability !== undefined ? |
|
|
(ensemble.human_probability * 100).toFixed(0) : '0'; |
|
|
|
|
|
const mixedProbability = ensemble.mixed_probability !== undefined ? |
|
|
(ensemble.mixed_probability * 100).toFixed(0) : '0'; |
|
|
|
|
|
const verdict = ensemble.final_verdict || 'Unknown'; |
|
|
const confidence = ensemble.overall_confidence !== undefined ? |
|
|
(ensemble.overall_confidence * 100).toFixed(1) : '0'; |
|
|
const domain = analysis.domain || 'general'; |
|
|
const isAI = verdict.toLowerCase().includes('ai'); |
|
|
const gaugeColor = isAI ? 'var(--danger)' : 'var(--success)'; |
|
|
const gaugeDegree = aiProbability * 3.6; |
|
|
|
|
|
const confidenceLevel = getConfidenceLevel(parseFloat(confidence)); |
|
|
const confidenceClass = getConfidenceClass(confidenceLevel); |
|
|
|
|
|
return { |
|
|
aiProbability, |
|
|
humanProbability, |
|
|
mixedProbability, |
|
|
verdict, |
|
|
confidence, |
|
|
domain, |
|
|
isAI, |
|
|
gaugeColor, |
|
|
gaugeDegree, |
|
|
confidenceLevel, |
|
|
confidenceClass |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
function getConfidenceLevel(confidence) { |
|
|
if (confidence >= 70) return 'HIGH'; |
|
|
if (confidence >= 40) return 'MEDIUM'; |
|
|
return 'LOW'; |
|
|
} |
|
|
|
|
|
|
|
|
function getConfidenceClass(confidenceLevel) { |
|
|
const classMap = { |
|
|
'HIGH': 'confidence-high', |
|
|
'MEDIUM': 'confidence-medium', |
|
|
'LOW': 'confidence-low' |
|
|
}; |
|
|
return classMap[confidenceLevel] || 'confidence-low'; |
|
|
} |
|
|
|
|
|
|
|
|
function generateAttributionHTML(attribution) { |
|
|
if (!attribution || !attribution.predicted_model) { |
|
|
return ''; |
|
|
} |
|
|
|
|
|
const modelName = formatModelName(attribution.predicted_model); |
|
|
const modelConf = attribution.confidence ? |
|
|
(attribution.confidence * 100).toFixed(1) : 'N/A'; |
|
|
|
|
|
const topModelsHTML = generateTopModelsHTML(attribution.model_probabilities); |
|
|
const reasoningHTML = generateAttributionReasoningHTML(attribution.reasoning); |
|
|
|
|
|
|
|
|
if (attribution.confidence > 0.3) { |
|
|
return ` |
|
|
<div class="attribution-section"> |
|
|
<div class="attribution-title">🤖 AI Model Attribution</div> |
|
|
${topModelsHTML} |
|
|
<div class="attribution-confidence"> |
|
|
Attribution Confidence: <strong>${modelConf}%</strong> |
|
|
</div> |
|
|
${reasoningHTML} |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
return ''; |
|
|
} |
|
|
|
|
|
|
|
|
function generateTopModelsHTML(modelProbabilities) { |
|
|
if (!modelProbabilities) { |
|
|
return '<div class="attribution-uncertain">Model probabilities not available</div>'; |
|
|
} |
|
|
|
|
|
|
|
|
const meaningfulModels = Object.entries(modelProbabilities) |
|
|
.sort((a, b) => b[1] - a[1]) |
|
|
.filter(([model, prob]) => prob > 0.15) |
|
|
.slice(0, 3); |
|
|
|
|
|
if (meaningfulModels.length === 0) { |
|
|
return ` |
|
|
<div class="attribution-uncertain"> |
|
|
Model attribution uncertain - text patterns don't strongly match any specific AI model |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
return meaningfulModels.map(([model, prob]) => |
|
|
`<div class="model-match"> |
|
|
<span class="model-name">${formatModelName(model)}</span> |
|
|
<span class="model-confidence">${(prob * 100).toFixed(1)}%</span> |
|
|
</div>` |
|
|
).join(''); |
|
|
} |
|
|
|
|
|
|
|
|
function formatModelName(modelName) { |
|
|
return modelName.replace(/_/g, ' ').replace(/-/g, ' ').toUpperCase(); |
|
|
} |
|
|
|
|
|
|
|
|
function generateAttributionReasoningHTML(reasoning) { |
|
|
if (!reasoning || !Array.isArray(reasoning) || reasoning.length === 0) { |
|
|
return ''; |
|
|
} |
|
|
|
|
|
return ` |
|
|
<div class="attribution-reasoning"> |
|
|
${reasoning[0]} |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
|
|
|
function createGaugeSection(aiProbability, humanProbability, mixedProbability, gaugeColor, gaugeDegree) { |
|
|
|
|
|
const ai = parseFloat(aiProbability); |
|
|
const human = parseFloat(humanProbability); |
|
|
const mixed = parseFloat(mixedProbability); |
|
|
|
|
|
|
|
|
let maxValue, maxColor, maxLabel; |
|
|
|
|
|
if (ai >= human && ai >= mixed) { |
|
|
maxValue = ai; |
|
|
maxColor = 'var(--danger)'; |
|
|
maxLabel = 'AI Probability'; |
|
|
} else if (human >= ai && human >= mixed) { |
|
|
maxValue = human; |
|
|
maxColor = 'var(--success)'; |
|
|
maxLabel = 'Human Probability'; |
|
|
} else { |
|
|
maxValue = mixed; |
|
|
maxColor = 'var(--primary)'; |
|
|
maxLabel = 'Mixed Probability'; |
|
|
} |
|
|
|
|
|
console.log('Selected:', { maxValue, maxLabel }); |
|
|
|
|
|
|
|
|
const progressDegree = (maxValue / 100) * 360; |
|
|
|
|
|
return ` |
|
|
<div class="gauge-container"> |
|
|
<div class="single-progress-gauge" style=" |
|
|
background: conic-gradient( |
|
|
${maxColor} 0deg, |
|
|
${maxColor} ${progressDegree}deg, |
|
|
rgba(51, 65, 85, 0.3) ${progressDegree}deg, |
|
|
rgba(51, 65, 85, 0.3) 360deg |
|
|
); |
|
|
"> |
|
|
<div class="gauge-inner"> |
|
|
<div class="gauge-value" style="color: ${maxColor}">${maxValue}%</div> |
|
|
<div class="gauge-label">${maxLabel}</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1rem; margin: 1.5rem 0;"> |
|
|
<div style="text-align: center; padding: 1rem; background: rgba(239, 68, 68, 0.1); border-radius: 8px; border: 1px solid rgba(239, 68, 68, 0.3);"> |
|
|
<div style="font-size: 0.85rem; color: var(--danger); margin-bottom: 0.25rem; font-weight: 600;">AI</div> |
|
|
<div style="font-size: 1.4rem; font-weight: 700; color: var(--danger);">${aiProbability}%</div> |
|
|
</div> |
|
|
<div style="text-align: center; padding: 1rem; background: rgba(16, 185, 129, 0.1); border-radius: 8px; border: 1px solid rgba(16, 185, 129, 0.3);"> |
|
|
<div style="font-size: 0.85rem; color: var(--success); margin-bottom: 0.25rem; font-weight: 600;">Human</div> |
|
|
<div style="font-size: 1.4rem; font-weight: 700; color: var(--success);">${humanProbability}%</div> |
|
|
</div> |
|
|
<div style="text-align: center; padding: 1rem; background: rgba(6, 182, 212, 0.1); border-radius: 8px; border: 1px solid rgba(6, 182, 212, 0.3);"> |
|
|
<div style="font-size: 0.85rem; color: var(--primary); margin-bottom: 0.25rem; font-weight: 600;">Mixed</div> |
|
|
<div style="font-size: 1.4rem; font-weight: 700; color: var(--primary);">${mixedProbability}%</div> |
|
|
</div> |
|
|
</div> |
|
|
<style> |
|
|
.single-progress-gauge { |
|
|
width: 220px; |
|
|
height: 220px; |
|
|
margin: 0 auto 2rem; |
|
|
position: relative; |
|
|
border-radius: 50%; |
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); |
|
|
} |
|
|
|
|
|
.gauge-inner { |
|
|
position: absolute; |
|
|
width: 170px; |
|
|
height: 170px; |
|
|
background: var(--bg-panel); |
|
|
border-radius: 50%; |
|
|
top: 50%; |
|
|
left: 50%; |
|
|
transform: translate(-50%, -50%); |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
} |
|
|
|
|
|
.gauge-value { |
|
|
font-size: 3rem; |
|
|
font-weight: 800; |
|
|
} |
|
|
|
|
|
.gauge-label { |
|
|
font-size: 0.9rem; |
|
|
color: var(--text-secondary); |
|
|
margin-top: 0.25rem; |
|
|
} |
|
|
</style> |
|
|
`; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function createInfoGrid(verdict, confidence, confidenceClass, domain, mixedProbability) { |
|
|
const mixedContentInfo = mixedProbability > 10 ? |
|
|
`<div style="margin-top: 0.5rem; font-size: 0.85rem; color: var(--primary);"> |
|
|
🔀 ${mixedProbability}% Mixed Content Detected |
|
|
</div>` : ''; |
|
|
|
|
|
return ` |
|
|
<div class="result-info-grid"> |
|
|
<div class="info-card"> |
|
|
<div class="info-label">Verdict</div> |
|
|
<div class="info-value verdict-text">${verdict}</div> |
|
|
${mixedContentInfo} |
|
|
</div> |
|
|
<div class="info-card"> |
|
|
<div class="info-label">Confidence Level</div> |
|
|
<div class="info-value"> |
|
|
<span class="confidence-badge ${confidenceClass}">${confidence}%</span> |
|
|
</div> |
|
|
</div> |
|
|
<div class="info-card"> |
|
|
<div class="info-label">Content Domain</div> |
|
|
<div class="info-value domain-text">${formatDomainName(domain)}</div> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
|
|
|
function createDownloadActions() { |
|
|
return ` |
|
|
<div class="download-actions"> |
|
|
<button class="download-btn" onclick="downloadReport('json')"> |
|
|
📄 Download JSON |
|
|
</button> |
|
|
<button class="download-btn" onclick="downloadReport('pdf')"> |
|
|
📑 Download PDF Report |
|
|
</button> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
|
|
|
function formatDomainName(domain) { |
|
|
return domain.split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' '); |
|
|
} |
|
|
|
|
|
function createEnhancedReasoningHTML(ensemble, analysis, reasoning) { |
|
|
|
|
|
if (reasoning && reasoning.summary) { |
|
|
|
|
|
const bulletPoints = formatSummaryAsBulletPoints(reasoning.summary, ensemble, analysis); |
|
|
|
|
|
|
|
|
let processedIndicators = []; |
|
|
if (reasoning.key_indicators && reasoning.key_indicators.length > 0) { |
|
|
processedIndicators = reasoning.key_indicators.map(indicator => { |
|
|
let processedIndicator = indicator; |
|
|
|
|
|
|
|
|
processedIndicator = processedIndicator.replace(/*/g, '*') |
|
|
.replace(/*/g, '*'); |
|
|
|
|
|
|
|
|
processedIndicator = processedIndicator.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>') |
|
|
.replace(/\*([^*]+)\*/g, '<strong>$1</strong>'); |
|
|
|
|
|
|
|
|
processedIndicator = processedIndicator.replace(/\*\*/g, '') |
|
|
.replace(/\*(?![^<]*>)/g, ''); |
|
|
|
|
|
|
|
|
processedIndicator = processedIndicator.replace(/_/g, ' '); |
|
|
|
|
|
|
|
|
return processedIndicator; |
|
|
}); |
|
|
} |
|
|
|
|
|
return ` |
|
|
<div class="reasoning-box enhanced"> |
|
|
<div class="reasoning-header"> |
|
|
<div class="reasoning-icon">💡</div> |
|
|
<div class="reasoning-title">Detection Reasoning</div> |
|
|
<div class="confidence-tag ${ensemble.overall_confidence >= 0.7 ? 'high-confidence' : ensemble.overall_confidence >= 0.4 ? 'medium-confidence' : 'low-confidence'}"> |
|
|
${ensemble.overall_confidence >= 0.7 ? 'High Confidence' : ensemble.overall_confidence >= 0.4 ? 'Medium Confidence' : 'Low Confidence'} |
|
|
</div> |
|
|
</div> |
|
|
<div class="verdict-summary"> |
|
|
<div class="verdict-text">${ensemble.final_verdict}</div> |
|
|
<div class="probability">AI Probability: <span class="probability-value">${(ensemble.ai_probability * 100).toFixed(2)}%</span></div> |
|
|
</div> |
|
|
<div class="reasoning-bullet-points"> |
|
|
${bulletPoints} |
|
|
</div> |
|
|
${processedIndicators.length > 0 ? ` |
|
|
<div class="metrics-breakdown"> |
|
|
<div class="breakdown-header" style="text-align: center; font-weight: 700; color: var(--text-secondary); margin-bottom: 1rem;"> |
|
|
KEY INDICATORS |
|
|
</div> |
|
|
${processedIndicators.map(indicator => { |
|
|
// Split indicator into metric name and sub-metric details |
|
|
const colonIndex = indicator.indexOf(':'); |
|
|
if (colonIndex !== -1) { |
|
|
const metricName = indicator.substring(0, colonIndex).trim(); |
|
|
const metricDetails = indicator.substring(colonIndex + 1).trim(); |
|
|
|
|
|
return ` |
|
|
<div style="margin-bottom: 1rem; text-align: left;"> |
|
|
<div style="font-weight: 700; color: #fff; text-align: center; margin-bottom: 0.5rem; font-size: 1rem;"> |
|
|
${metricName} |
|
|
</div> |
|
|
<div style="color: var(--text-secondary); font-size: 0.9rem; line-height: 1.4; text-align: left;"> |
|
|
${metricDetails} |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
} else { |
|
|
// If no colon, treat as general indicator |
|
|
return ` |
|
|
<div style="margin-bottom: 1rem; text-align: left;"> |
|
|
<div style="color: var(--text-secondary); font-size: 0.9rem; line-height: 1.4;"> |
|
|
${indicator} |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
}).join('')} |
|
|
</div> |
|
|
` : ''} |
|
|
${ensemble.consensus_level > 0.7 ? ` |
|
|
<div class="agreement-indicator"> |
|
|
<div class="agreement-icon">✓</div> |
|
|
<div class="agreement-text">Strong metric consensus (${(ensemble.consensus_level * 100).toFixed(1)}%)</div> |
|
|
</div> |
|
|
` : ''} |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
return ` |
|
|
<div class="reasoning-box"> |
|
|
<div class="reasoning-title">💡 Detection Reasoning</div> |
|
|
<p class="reasoning-text" style="text-align: left;"> |
|
|
Analysis based on 6-metric ensemble with domain-aware calibration. |
|
|
The system evaluated linguistic patterns, statistical features, and semantic structures |
|
|
to determine content authenticity with ${(ensemble.overall_confidence * 100).toFixed(1)}% confidence. |
|
|
</p> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
|
|
|
function formatSummaryAsBulletPoints(summary, ensemble, analysis) { |
|
|
let processedSummary = summary; |
|
|
|
|
|
|
|
|
processedSummary = processedSummary.replace(/*/g, '*') |
|
|
.replace(/*/g, '*'); |
|
|
|
|
|
|
|
|
processedSummary = processedSummary.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>') |
|
|
.replace(/\*([^*]+)\*/g, '<strong>$1</strong>'); |
|
|
|
|
|
|
|
|
processedSummary = processedSummary.replace(/\*\*/g, '') |
|
|
.replace(/\*(?![^<]*>)/g, ''); |
|
|
|
|
|
|
|
|
const sentences = processedSummary.split(/\.\s+/); |
|
|
|
|
|
|
|
|
const bulletPoints = []; |
|
|
|
|
|
|
|
|
const confidenceLevel = ensemble.overall_confidence >= 0.7 ? 'High Confidence' : |
|
|
ensemble.overall_confidence >= 0.4 ? 'Medium Confidence' : 'Low Confidence'; |
|
|
bulletPoints.push(`<div class="bullet-point">• ${confidenceLevel}</div>`); |
|
|
|
|
|
|
|
|
bulletPoints.push(`<div class="bullet-point">• ${ensemble.final_verdict}</div>`); |
|
|
|
|
|
|
|
|
bulletPoints.push(`<div class="bullet-point">• AI Probability: ${(ensemble.ai_probability * 100).toFixed(2)}%</div>`); |
|
|
|
|
|
|
|
|
sentences.forEach(sentence => { |
|
|
if (sentence.trim() && |
|
|
!sentence.includes('confidence') && |
|
|
!sentence.includes(ensemble.final_verdict) && |
|
|
!sentence.includes('AI probability')) { |
|
|
|
|
|
let cleanSentence = sentence.trim(); |
|
|
if (!cleanSentence.endsWith('.')) { |
|
|
cleanSentence += '.'; |
|
|
} |
|
|
bulletPoints.push(`<div class="bullet-point">• ${cleanSentence}</div>`); |
|
|
} |
|
|
}); |
|
|
|
|
|
return bulletPoints.join(''); |
|
|
} |
|
|
|
|
|
function displayHighlightedText(html) { |
|
|
document.getElementById('highlighted-report').innerHTML = ` |
|
|
${createDefaultLegend()} |
|
|
<div class="highlighted-text"> |
|
|
${html} |
|
|
</div> |
|
|
${getHighlightStyles()} |
|
|
`; |
|
|
} |
|
|
|
|
|
function createDefaultLegend() { |
|
|
return ` |
|
|
<div class="highlight-legend"> |
|
|
<div class="legend-item"> |
|
|
<div class="legend-color" style="background: #fecaca;"></div> |
|
|
<div class="legend-label">Very Likely AI (90-100%)</div> |
|
|
</div> |
|
|
<div class="legend-item"> |
|
|
<div class="legend-color" style="background: #fed7aa;"></div> |
|
|
<div class="legend-label">Likely AI (75-90%)</div> |
|
|
</div> |
|
|
<div class="legend-item"> |
|
|
<div class="legend-color" style="background: #fde68a;"></div> |
|
|
<div class="legend-label">Possibly AI (60-75%)</div> |
|
|
</div> |
|
|
<div class="legend-item"> |
|
|
<div class="legend-color" style="background: #fef9c3;"></div> |
|
|
<div class="legend-label">Uncertain (40-60%)</div> |
|
|
</div> |
|
|
<div class="legend-item"> |
|
|
<div class="legend-color" style="background: #86efac;"></div> |
|
|
<div class="legend-label">Possibly Human (25-40%)</div> |
|
|
</div> |
|
|
<div class="legend-item"> |
|
|
<div class="legend-color" style="background: #bbf7d0;"></div> |
|
|
<div class="legend-label">Likely Human (10-25%)</div> |
|
|
</div> |
|
|
<div class="legend-item"> |
|
|
<div class="legend-color" style="background: #dcfce7;"></div> |
|
|
<div class="legend-label">Very Likely Human (0-10%)</div> |
|
|
</div> |
|
|
<div class="legend-item"> |
|
|
<div class="legend-color" style="background: #e9d5ff;"></div> |
|
|
<div class="legend-label">Mixed Content</div> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
function getHighlightStyles() { |
|
|
return ` |
|
|
<style> |
|
|
#highlighted-report .highlight { |
|
|
padding: 2px 4px; |
|
|
margin: 0 1px; |
|
|
border-radius: 3px; |
|
|
cursor: help; |
|
|
transition: all 0.2s; |
|
|
border-bottom: 2px solid transparent; |
|
|
color: #000000 !important; |
|
|
font-weight: 500; |
|
|
} |
|
|
#highlighted-report .highlight:hover { |
|
|
transform: translateY(-1px); |
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15); |
|
|
z-index: 10; |
|
|
text-shadow: 0 1px 1px rgba(255,255,255,0.8); |
|
|
} |
|
|
#highlighted-report .very-high-ai { |
|
|
background-color: #fee2e2 !important; |
|
|
border-bottom-color: #ef4444 !important; |
|
|
} |
|
|
#highlighted-report .high-ai { |
|
|
background-color: #fed7aa !important; |
|
|
border-bottom-color: #f97316 !important; |
|
|
} |
|
|
#highlighted-report .medium-ai { |
|
|
background-color: #fef3c7 !important; |
|
|
border-bottom-color: #f59e0b !important; |
|
|
} |
|
|
#highlighted-report .uncertain { |
|
|
background-color: #fef9c3 !important; |
|
|
border-bottom-color: #fbbf24 !important; |
|
|
} |
|
|
#highlighted-report .medium-human { |
|
|
background-color: #ecfccb !important; |
|
|
border-bottom-color: #a3e635 !important; |
|
|
} |
|
|
#highlighted-report .high-human { |
|
|
background-color: #bbf7d0 !important; |
|
|
border-bottom-color: #4ade80 !important; |
|
|
} |
|
|
#highlighted-report .very-high-human { |
|
|
background-color: #dcfce7 !important; |
|
|
border-bottom-color: #22c55e !important; |
|
|
} |
|
|
#highlighted-report .mixed-content { |
|
|
background-color: #e9d5ff !important; |
|
|
border-bottom-color: #a855f7 !important; |
|
|
background-image: repeating-linear-gradient(45deg, transparent, transparent 5px, rgba(168, 85, 247, 0.1) 5px, rgba(168, 85, 247, 0.1) 10px) !important; |
|
|
} |
|
|
</style> |
|
|
`; |
|
|
} |
|
|
|
|
|
function displayMetricsCarousel(metrics, analysis, ensemble) { |
|
|
const metricOrder = ['structural', 'perplexity', 'entropy', 'semantic_analysis', 'linguistic', 'multi_perturbation_stability']; |
|
|
const availableMetrics = metricOrder.filter(key => metrics[key]); |
|
|
totalMetrics = availableMetrics.length; |
|
|
|
|
|
if (totalMetrics === 0) { |
|
|
document.getElementById('metrics-report').innerHTML = ` |
|
|
<div class="empty-state"> |
|
|
<p class="empty-description">No metric details available</p> |
|
|
</div> |
|
|
`; |
|
|
return; |
|
|
} |
|
|
|
|
|
let carouselHTML = ` |
|
|
<div class="metrics-carousel-container"> |
|
|
<div class="metrics-carousel-content"> |
|
|
`; |
|
|
|
|
|
availableMetrics.forEach((metricKey, index) => { |
|
|
const metric = metrics[metricKey]; |
|
|
if (!metric) return; |
|
|
|
|
|
const aiProb = (metric.ai_probability * 100).toFixed(1); |
|
|
const humanProb = (metric.human_probability * 100).toFixed(1); |
|
|
const mixedProb = (metric.mixed_probability * 100).toFixed(1); |
|
|
const confidence = (metric.confidence * 100).toFixed(1); |
|
|
const weight = ensemble.metric_contributions && ensemble.metric_contributions[metricKey] ? |
|
|
(ensemble.metric_contributions[metricKey].weight * 100).toFixed(1) : '0.0'; |
|
|
|
|
|
|
|
|
let verdictText, verdictClass; |
|
|
if (metric.mixed_probability > 0.3) { |
|
|
verdictText = 'MIXED'; |
|
|
verdictClass = 'verdict-mixed'; |
|
|
} else if (metric.ai_probability >= 0.6) { |
|
|
verdictText = 'AI'; |
|
|
verdictClass = 'verdict-ai'; |
|
|
} else if (metric.ai_probability >= 0.4) { |
|
|
verdictText = 'UNCERTAIN'; |
|
|
verdictClass = 'verdict-uncertain'; |
|
|
} else { |
|
|
verdictText = 'HUMAN'; |
|
|
verdictClass = 'verdict-human'; |
|
|
} |
|
|
|
|
|
carouselHTML += ` |
|
|
<div class="metric-slide ${index === 0 ? 'active' : ''}" data-metric-index="${index}"> |
|
|
<div class="metric-result-card"> |
|
|
<div class="metric-header"> |
|
|
<div class="metric-name">${formatMetricName(metricKey)}</div> |
|
|
</div> |
|
|
<div class="metric-description"> |
|
|
${getMetricDescription(metricKey)} |
|
|
</div> |
|
|
|
|
|
<!-- Enhanced Probability Display with Mixed --> |
|
|
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1rem; margin: 1rem 0;"> |
|
|
<div style="text-align: center;"> |
|
|
<div style="font-size: 0.75rem; color: var(--text-muted); margin-bottom: 0.25rem;">AI</div> |
|
|
<div style="background: rgba(51, 65, 85, 0.5); height: 8px; border-radius: 4px; overflow: hidden;"> |
|
|
<div style="background: var(--danger); height: 100%; width: ${aiProb}%; transition: width 0.5s;"></div> |
|
|
</div> |
|
|
<div style="font-size: 0.85rem; font-weight: 600; margin-top: 0.25rem;">${aiProb}%</div> |
|
|
</div> |
|
|
<div style="text-align: center;"> |
|
|
<div style="font-size: 0.75rem; color: var(--text-muted); margin-bottom: 0.25rem;">Human</div> |
|
|
<div style="background: rgba(51, 65, 85, 0.5); height: 8px; border-radius: 4px; overflow: hidden;"> |
|
|
<div style="background: var(--success); height: 100%; width: ${humanProb}%; transition: width 0.5s;"></div> |
|
|
</div> |
|
|
<div style="font-size: 0.85rem; font-weight: 600; margin-top: 0.25rem;">${humanProb}%</div> |
|
|
</div> |
|
|
<div style="text-align: center;"> |
|
|
<div style="font-size: 0.75rem; color: var(--text-muted); margin-bottom: 0.25rem;">Mixed</div> |
|
|
<div style="background: rgba(51, 65, 85, 0.5); height: 8px; border-radius: 4px; overflow: hidden;"> |
|
|
<div style="background: var(--primary); height: 100%; width: ${mixedProb}%; transition: width 0.5s;"></div> |
|
|
</div> |
|
|
<div style="font-size: 0.85rem; font-weight: 600; margin-top: 0.25rem;">${mixedProb}%</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div style="display: flex; justify-content: space-between; align-items: center; margin: 0.75rem 0;"> |
|
|
<span class="metric-verdict ${verdictClass}">${verdictText}</span> |
|
|
<span style="font-size: 0.85rem; color: var(--text-secondary);">Confidence: ${confidence}% | Weight: ${weight}%</span> |
|
|
</div> |
|
|
|
|
|
${metric.details ? renderMetricDetails(metricKey, metric.details) : ''} |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
}); |
|
|
|
|
|
carouselHTML += ` |
|
|
</div> |
|
|
<div class="metrics-carousel-nav"> |
|
|
<button class="carousel-btn prev-btn" onclick="navigateMetrics(-1)" ${currentMetricIndex === 0 ? 'disabled' : ''}>← Previous</button> |
|
|
<div class="carousel-position">${currentMetricIndex + 1} / ${totalMetrics}</div> |
|
|
<button class="carousel-btn next-btn" onclick="navigateMetrics(1)" ${currentMetricIndex === totalMetrics - 1 ? 'disabled' : ''}>Next →</button> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
document.getElementById('metrics-report').innerHTML = carouselHTML; |
|
|
updateCarouselButtons(); |
|
|
} |
|
|
|
|
|
function navigateMetrics(direction) { |
|
|
const newMetricIndex = currentMetricIndex + direction; |
|
|
if (newMetricIndex >= 0 && newMetricIndex < totalMetrics) { |
|
|
currentMetricIndex = newMetricIndex; |
|
|
updateMetricCarousel(); |
|
|
} |
|
|
} |
|
|
|
|
|
function updateMetricCarousel() { |
|
|
const slides = document.querySelectorAll('.metric-slide'); |
|
|
slides.forEach((slide, index) => { |
|
|
if (index === currentMetricIndex) { |
|
|
slide.classList.add('active'); |
|
|
} else { |
|
|
slide.classList.remove('active'); |
|
|
} |
|
|
}); |
|
|
updateCarouselButtons(); |
|
|
|
|
|
const positionElement = document.querySelector('.carousel-position'); |
|
|
if (positionElement) { |
|
|
positionElement.textContent = `${currentMetricIndex + 1} / ${totalMetrics}`; |
|
|
} |
|
|
} |
|
|
|
|
|
function updateCarouselButtons() { |
|
|
const prevBtn = document.querySelector('.prev-btn'); |
|
|
const nextBtn = document.querySelector('.next-btn'); |
|
|
if (prevBtn) { |
|
|
prevBtn.disabled = currentMetricIndex === 0; |
|
|
} |
|
|
if (nextBtn) { |
|
|
nextBtn.disabled = currentMetricIndex === totalMetrics - 1; |
|
|
} |
|
|
} |
|
|
|
|
|
function renderMetricDetails(metricName, details) { |
|
|
if (!details || Object.keys(details).length === 0) return ''; |
|
|
|
|
|
|
|
|
const importantKeys = { |
|
|
'structural': ['burstiness_score', 'length_uniformity', 'avg_sentence_length', 'std_sentence_length'], |
|
|
'perplexity': ['overall_perplexity', 'avg_sentence_perplexity', 'normalized_perplexity'], |
|
|
'entropy': ['token_diversity', 'sequence_unpredictability', 'char_entropy'], |
|
|
'semantic_analysis': ['coherence_score', 'consistency_score', 'repetition_score'], |
|
|
'linguistic': ['pos_diversity', 'syntactic_complexity', 'grammatical_consistency'], |
|
|
'multi_perturbation_stability': ['stability_score', 'curvature_score', 'likelihood_ratio', 'perturbation_variance', 'mixed_probability'] |
|
|
}; |
|
|
|
|
|
const keysToShow = importantKeys[metricName] || Object.keys(details).slice(0, 6); |
|
|
|
|
|
let detailsHTML = '<div style="margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--border);">'; |
|
|
detailsHTML += '<div style="font-size: 0.9rem; font-weight: 600; color: var(--text-secondary); margin-bottom: 0.75rem;">📈 Detailed Metrics:</div>'; |
|
|
detailsHTML += '<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 0.75rem; font-size: 0.85rem;">'; |
|
|
|
|
|
keysToShow.forEach(key => { |
|
|
if (details[key] !== undefined && details[key] !== null) { |
|
|
let value = details[key]; |
|
|
let displayValue; |
|
|
|
|
|
|
|
|
if (typeof value === 'number') { |
|
|
if (key.includes('score') || key.includes('ratio') || key.includes('probability')) { |
|
|
displayValue = (value * 100).toFixed(2) + '%'; |
|
|
} else if (value < 1 && value > 0) { |
|
|
displayValue = value.toFixed(4); |
|
|
} else { |
|
|
displayValue = value.toFixed(2); |
|
|
} |
|
|
} else { |
|
|
displayValue = value; |
|
|
} |
|
|
|
|
|
const label = key.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); |
|
|
detailsHTML += ` |
|
|
<div style="background: rgba(15, 23, 42, 0.6); padding: 0.5rem; border-radius: 6px;"> |
|
|
<div style="color: var(--text-muted); font-size: 0.75rem; margin-bottom: 0.25rem;">${label}</div> |
|
|
<div style="color: var(--primary); font-weight: 700;">${displayValue}</div> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
}); |
|
|
|
|
|
detailsHTML += '</div></div>'; |
|
|
return detailsHTML; |
|
|
} |
|
|
|
|
|
function getMetricDescription(metricName) { |
|
|
const descriptions = { |
|
|
structural: 'Analyzes sentence structure, length patterns, and statistical features.', |
|
|
perplexity: 'Measures text predictability using language model cross-entropy.', |
|
|
entropy: 'Evaluates token diversity and sequence unpredictability.', |
|
|
semantic_analysis: 'Examines semantic coherence, topic consistency, and logical flow.', |
|
|
linguistic: 'Assesses grammatical patterns, syntactic complexity, and style markers.', |
|
|
multi_perturbation_stability: 'Tests text stability under perturbation using curvature analysis.' |
|
|
}; |
|
|
return descriptions[metricName] || 'Metric analysis complete.'; |
|
|
} |
|
|
|
|
|
function formatMetricName(name) { |
|
|
const names = { |
|
|
structural: 'Structural Analysis', |
|
|
perplexity: 'Perplexity', |
|
|
entropy: 'Entropy', |
|
|
semantic_analysis: 'Semantic Analysis', |
|
|
linguistic: 'Linguistic Analysis', |
|
|
multi_perturbation_stability: 'Multi-Perturbation Stability' |
|
|
}; |
|
|
return names[name] || name.split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' '); |
|
|
} |
|
|
|
|
|
async function downloadReport(format) { |
|
|
if (!currentAnalysisData) { |
|
|
alert('No analysis data available'); |
|
|
return; |
|
|
} |
|
|
|
|
|
try { |
|
|
const analysisId = currentAnalysisData.analysis_id; |
|
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); |
|
|
|
|
|
|
|
|
if (format === 'json') { |
|
|
const data = { |
|
|
...currentAnalysisData, |
|
|
download_timestamp: new Date().toISOString(), |
|
|
report_version: '2.0.0' |
|
|
}; |
|
|
const blob = new Blob([JSON.stringify(data, null, 2)], { |
|
|
type: 'application/json' |
|
|
}); |
|
|
const filename = `ai-detection-report-${analysisId}-${timestamp}.json`; |
|
|
await downloadBlob(blob, filename); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
const activeTab = document.querySelector('.input-tab.active').dataset.tab; |
|
|
let textToSend = ''; |
|
|
if (activeTab === 'paste') { |
|
|
textToSend = document.getElementById('text-input').value; |
|
|
} else { |
|
|
textToSend = currentAnalysisData.detection_result?.processed_text?.text || |
|
|
'Uploaded file content - see analysis for details'; |
|
|
} |
|
|
|
|
|
|
|
|
const formData = new FormData(); |
|
|
formData.append('analysis_id', analysisId); |
|
|
formData.append('text', textToSend); |
|
|
formData.append('formats', format); |
|
|
formData.append('include_highlights', document.getElementById('enable-highlighting').checked.toString()); |
|
|
|
|
|
const response = await fetch(`${API_BASE}/api/report/generate`, { |
|
|
method: 'POST', |
|
|
body: formData |
|
|
}); |
|
|
|
|
|
if (!response.ok) { |
|
|
throw new Error('Report generation failed'); |
|
|
} |
|
|
|
|
|
const result = await response.json(); |
|
|
if (result.reports && result.reports[format]) { |
|
|
const filename = result.reports[format]; |
|
|
const downloadResponse = await fetch(`${API_BASE}/api/report/download/${filename}`); |
|
|
if (!downloadResponse.ok) { |
|
|
throw new Error('Failed to download file'); |
|
|
} |
|
|
const blob = await downloadResponse.blob(); |
|
|
const downloadFilename = `ai-detection-${format}-report-${analysisId}-${timestamp}.${format}`; |
|
|
await downloadBlob(blob, downloadFilename); |
|
|
} else { |
|
|
alert('Report file not available'); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('Download error:', error); |
|
|
alert('Failed to download report. Please try again.'); |
|
|
} |
|
|
} |
|
|
|
|
|
async function downloadBlob(blob, filename) { |
|
|
try { |
|
|
const url = URL.createObjectURL(blob); |
|
|
const a = document.createElement('a'); |
|
|
a.href = url; |
|
|
a.download = filename; |
|
|
a.style.display = 'none'; |
|
|
document.body.appendChild(a); |
|
|
a.click(); |
|
|
setTimeout(() => { |
|
|
document.body.removeChild(a); |
|
|
URL.revokeObjectURL(url); |
|
|
showDownloadSuccess(filename); |
|
|
}, 100); |
|
|
} catch (error) { |
|
|
console.error('Download failed:', error); |
|
|
alert('Download failed. Please try again.'); |
|
|
} |
|
|
} |
|
|
|
|
|
function showDownloadSuccess(filename) { |
|
|
const notification = document.createElement('div'); |
|
|
notification.style.cssText = ` |
|
|
position: fixed; |
|
|
top: 20px; |
|
|
right: 20px; |
|
|
background: var(--success); |
|
|
color: white; |
|
|
padding: 1rem 1.5rem; |
|
|
border-radius: 8px; |
|
|
font-weight: 600; |
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.3); |
|
|
z-index: 10000; |
|
|
animation: slideIn 0.3s ease; |
|
|
`; |
|
|
notification.innerHTML = ` |
|
|
<div style="display: flex; align-items: center; gap: 0.5rem;"> |
|
|
<span>✓</span> |
|
|
<span>Downloaded: ${filename}</span> |
|
|
</div> |
|
|
`; |
|
|
document.body.appendChild(notification); |
|
|
|
|
|
if (!document.querySelector('#download-animation')) { |
|
|
const style = document.createElement('style'); |
|
|
style.id = 'download-animation'; |
|
|
style.textContent = ` |
|
|
@keyframes slideIn { |
|
|
from { transform: translateX(100%); opacity: 0; } |
|
|
to { transform: translateX(0); opacity: 1; } |
|
|
} |
|
|
`; |
|
|
document.head.appendChild(style); |
|
|
} |
|
|
|
|
|
setTimeout(() => { |
|
|
if (notification.parentNode) { |
|
|
notification.parentNode.removeChild(notification); |
|
|
} |
|
|
}, 3000); |
|
|
} |
|
|
|
|
|
|
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => { |
|
|
anchor.addEventListener('click', function (e) { |
|
|
const href = this.getAttribute('href'); |
|
|
if (href !== '#') { |
|
|
e.preventDefault(); |
|
|
const target = document.querySelector(href); |
|
|
if (target) { |
|
|
target.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
showLanding(); |
|
|
</script> |
|
|
</body> |
|
|
</html> |