satyaki-mitra
feat: Add Text Auth AI Detection System
edf1149
raw
history blame
71.8 kB
<!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 */
.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 */
.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 */
.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 Section */
.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 */
.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);
}
.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 */
.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 */
.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 State */
.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 */
.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 */
.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;
}
/* Enhanced Reasoning Styles */
.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: center;
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: 600;
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 */
.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);
}
/* Download Actions */
.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 */
.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 */
.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;
}
/* Highlighted Text */
.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;
}
.highlight-low {
background-color: rgba(234, 179, 8, 0.25);
padding: 2px 4px;
border-radius: 3px;
}
.highlight-medium {
background-color: rgba(249, 115, 22, 0.35);
padding: 2px 4px;
border-radius: 3px;
}
.highlight-high {
background-color: rgba(239, 68, 68, 0.4);
padding: 2px 4px;
border-radius: 3px;
}
/* Footer */
.footer {
max-width: 1200px;
margin: 6rem auto 0;
padding: 3rem 2rem;
border-top: 1px solid var(--border);
text-align: center;
color: var(--text-muted);
}
/* Responsive */
@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;
}
}
/* Scroll Behavior */
html {
scroll-behavior: smooth;
}
</style>
</head>
<body>
<!-- Header -->
<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>
<!-- Landing Page -->
<div class="landing-page" id="landing-page">
<!-- Hero Section -->
<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>
<!-- Stats -->
<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">2s</div>
<div class="stat-label">Average Processing Time</div>
</div>
</div>
<!-- Features Section -->
<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 DetectGPT 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>
<!-- Metrics 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 XL 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>Statistical 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>DetectGPT <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 -->
<footer class="footer">
<p>&copy; 2025 AI Text Detector Platform</p>
<p style="margin-top: 1rem;">AI detection with enterprise accuracy and explainability.</p>
</footer>
</div>
<!-- Analysis Interface -->
<div class="analysis-interface" id="analysis-interface">
<div class="interface-grid">
<!-- Left Panel: Input -->
<div class="panel">
<h2 class="panel-title">Submit Content for Analysis</h2>
<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. Our system analyzes linguistic patterns, statistical features, and semantic structures to determine authenticity."
></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>
<!-- NEW OPTIONS -->
<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>
<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>
<!-- Right Panel: Results -->
<div class="panel">
<h2 class="panel-title">Analysis Report</h2>
<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>
<!-- Summary Report -->
<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>
<!-- Highlighted Text Report -->
<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>
<!-- Metrics Report -->
<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>
<script>
// Configuration
const API_BASE = '';
let currentAnalysisData = null;
// Navigation
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();
}
// Reset analysis interface
function resetAnalysisInterface() {
// Clear text input
document.getElementById('text-input').value = '';
// Clear file input and display
document.getElementById('file-input').value = '';
document.getElementById('file-name-display').style.display = 'none';
document.getElementById('file-name-display').innerHTML = '';
// Reset tabs to paste
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');
// Reset options to defaults
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;
// Reset report tabs to summary
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');
// Show empty state
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>
`;
// Clear current analysis data
currentAnalysisData = null;
}
// Input Tab Switching
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');
});
});
// Report Tab Switching
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');
});
});
// File Upload Handling
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]);
});
// Drag and Drop
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';
}
// Analyze Button
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);
});
// Refresh Button - clears everything and shows empty state
document.getElementById('refresh-btn').addEventListener('click', () => {
resetAnalysisInterface();
});
// Try Next Button - same as refresh but keeps the interface ready
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);
// Handle different response structures
const detection = data.detection_result;
if (!detection) {
showError('Invalid response structure. Please check the API response format.');
console.error('Full response:', data);
return;
}
// Extract data based on your actual API structure
const ensemble = detection.ensemble_result || detection.ensemble;
const prediction = detection.prediction || {};
const metrics = detection.metric_results || detection.metrics;
const analysis = detection.analysis || {};
// Display Summary with enhanced reasoning
displaySummary(ensemble, prediction, analysis, data.attribution, data.reasoning);
// Display Highlighted Text with enhanced features
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>
`;
}
// Display Metrics with full details
if (metrics && Object.keys(metrics).length > 0) {
displayMetrics(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) {
// Use ensemble values from your actual API response
const aiProbability = ensemble.ai_probability !== undefined ?
(ensemble.ai_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 = parseFloat(confidence) >= 70 ? 'HIGH' :
parseFloat(confidence) >= 40 ? 'MEDIUM' : 'LOW';
const confidenceClass = confidenceLevel === 'HIGH' ? 'confidence-high' :
confidenceLevel === 'MEDIUM' ? 'confidence-medium' : 'confidence-low';
let attributionHTML = '';
if (attribution && attribution.predicted_model) {
const modelName = attribution.predicted_model.replace(/_/g, ' ').replace(/-/g, ' ').toUpperCase();
const modelConf = attribution.confidence ?
(attribution.confidence * 100).toFixed(1) : 'N/A';
let topModels = '';
if (attribution.model_probabilities) {
const sorted = Object.entries(attribution.model_probabilities)
.sort((a, b) => b[1] - a[1])
.slice(0, 3);
topModels = sorted.map(([model, prob]) =>
`<div class="model-match" style="margin-top: 0.5rem;">
<span class="model-name">${model.replace(/_/g, ' ').replace(/-/g, ' ').toUpperCase()}</span>
<span class="model-confidence">${(prob * 100).toFixed(1)}%</span>
</div>`
).join('');
}
attributionHTML = `
<div class="attribution-section">
<div class="attribution-title">🤖 AI Model Attribution</div>
<div class="model-match">
<span class="model-name">Most Likely: ${modelName}</span>
<span class="model-confidence">${modelConf}%</span>
</div>
${topModels}
${attribution.reasoning && attribution.reasoning.length > 0 ?
`<p style="color: var(--text-secondary); margin-top: 1rem; font-size: 0.9rem;">${attribution.reasoning[0]}</p>` : ''}
</div>
`;
}
document.getElementById('summary-report').innerHTML = `
<div class="result-summary">
<div class="gauge-container">
<div class="gauge-circle" style="--gauge-color: ${gaugeColor}; --gauge-degree: ${gaugeDegree}deg;">
<div class="gauge-inner">
<div class="gauge-value">${aiProbability}%</div>
<div class="gauge-label">AI Probability</div>
</div>
</div>
</div>
<div class="result-info-grid">
<div class="info-card">
<div class="info-label">Verdict</div>
<div class="info-value" style="font-size: 1.2rem;">${verdict}</div>
</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" style="font-size: 1.1rem;">${formatDomainName(domain)}</div>
</div>
</div>
${createEnhancedReasoningHTML(ensemble, analysis, reasoning)}
${attributionHTML}
<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>
</div>
`;
}
function createEnhancedReasoningHTML(ensemble, analysis, reasoning) {
// Use actual reasoning data if available
if (reasoning && reasoning.summary) {
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 style="color: var(--text-secondary); line-height: 1.6; margin-bottom: 1.5rem;">
${reasoning.summary}
</div>
${reasoning.key_indicators && reasoning.key_indicators.length > 0 ? `
<div class="metrics-breakdown">
<div class="breakdown-header">Key Indicators</div>
${reasoning.key_indicators.map(indicator => `
<div class="metric-indicator">
<div class="metric-name">${indicator.split(':')[0]}</div>
<div class="metric-details">
<span style="color: var(--text-secondary); font-size: 0.9rem;">${indicator.split(':')[1]}</span>
</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>
`;
}
// Fallback to basic reasoning if no reasoning data
return `
<div class="reasoning-box">
<div class="reasoning-title">💡 Detection Reasoning</div>
<p class="reasoning-text">
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 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: rgba(239, 68, 68, 0.4);"></div>
<div class="legend-label">Very Likely AI (90-100%)</div>
</div>
<div class="legend-item">
<div class="legend-color" style="background: rgba(249, 115, 22, 0.35);"></div>
<div class="legend-label">Likely AI (75-90%)</div>
</div>
<div class="legend-item">
<div class="legend-color" style="background: rgba(245, 158, 11, 0.3);"></div>
<div class="legend-label">Possibly AI (60-75%)</div>
</div>
<div class="legend-item">
<div class="legend-color" style="background: rgba(251, 191, 36, 0.25);"></div>
<div class="legend-label">Uncertain (40-60%)</div>
</div>
<div class="legend-item">
<div class="legend-color" style="background: rgba(163, 230, 53, 0.25);"></div>
<div class="legend-label">Possibly Human (25-40%)</div>
</div>
<div class="legend-item">
<div class="legend-color" style="background: rgba(74, 222, 128, 0.25);"></div>
<div class="legend-label">Likely Human (10-25%)</div>
</div>
<div class="legend-item">
<div class="legend-color" style="background: rgba(34, 197, 94, 0.3);"></div>
<div class="legend-label">Very Likely Human (0-10%)</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;
}
#highlighted-report .highlight:hover {
transform: scale(1.02);
filter: brightness(1.2);
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
z-index: 10;
position: relative;
}
#highlighted-report .very-high-ai {
background-color: rgba(239, 68, 68, 0.4) !important;
border-bottom-color: #ef4444 !important;
}
#highlighted-report .high-ai {
background-color: rgba(249, 115, 22, 0.35) !important;
border-bottom-color: #f97316 !important;
}
#highlighted-report .medium-ai {
background-color: rgba(245, 158, 11, 0.3) !important;
border-bottom-color: #f59e0b !important;
}
#highlighted-report .uncertain {
background-color: rgba(251, 191, 36, 0.25) !important;
border-bottom-color: #fbbf24 !important;
}
#highlighted-report .medium-human {
background-color: rgba(163, 230, 53, 0.25) !important;
border-bottom-color: #a3e635 !important;
}
#highlighted-report .high-human {
background-color: rgba(74, 222, 128, 0.25) !important;
border-bottom-color: #4ade80 !important;
}
#highlighted-report .very-high-human {
background-color: rgba(34, 197, 94, 0.3) !important;
border-bottom-color: #22c55e !important;
}
</style>
`;
}
function displayMetrics(metrics, analysis, ensemble) {
const metricOrder = ['structural', 'perplexity', 'entropy', 'semantic_analysis', 'linguistic', 'detect_gpt'];
let metricsHTML = `
<div style="margin-bottom: 2rem; padding: 1.5rem; background: rgba(51, 65, 85, 0.3); border-radius: 10px;">
<h3 style="color: var(--primary); margin-bottom: 1rem;">📊 Ensemble Analysis</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem;">
<div>
<div style="font-size: 0.85rem; color: var(--text-secondary);">Method</div>
<div style="font-size: 1.1rem; font-weight: 700; color: #fff;">Confidence Calibrated</div>
</div>
<div>
<div style="font-size: 0.85rem; color: var(--text-secondary);">Consensus</div>
<div style="font-size: 1.1rem; font-weight: 700; color: #fff;">${(ensemble.consensus_level * 100).toFixed(1)}%</div>
</div>
<div>
<div style="font-size: 0.85rem; color: var(--text-secondary);">Uncertainty</div>
<div style="font-size: 1.1rem; font-weight: 700; color: #fff;">${(ensemble.uncertainty_score * 100).toFixed(1)}%</div>
</div>
</div>
</div>
`;
metricOrder.forEach(metricKey => {
const metric = metrics[metricKey];
if (!metric) return;
const aiProb = (metric.ai_probability * 100).toFixed(1);
const humanProb = (metric.human_probability * 100).toFixed(1);
const confidence = (metric.confidence * 100).toFixed(1);
const weight = ensemble.metric_weights && ensemble.metric_weights[metricKey] ?
(ensemble.metric_weights[metricKey] * 100).toFixed(1) : '0.0';
const color = metric.ai_probability >= 0.6 ? 'var(--danger)' :
metric.ai_probability >= 0.4 ? 'var(--warning)' : 'var(--success)';
const verdictText = metric.ai_probability >= 0.6 ? 'AI' :
metric.ai_probability >= 0.4 ? 'UNCERTAIN' : 'HUMAN';
const verdictClass = verdictText === 'AI' ? 'verdict-ai' :
verdictText === 'UNCERTAIN' ? 'verdict-uncertain' : 'verdict-human';
metricsHTML += `
<div class="metric-result-card" style="margin-bottom: 1.5rem;">
<div class="metric-header">
<div class="metric-name">${formatMetricName(metricKey)}</div>
<div class="metric-score" style="color: ${color};">${aiProb}%</div>
</div>
<div style="display: flex; gap: 1rem; margin: 1rem 0;">
<div style="flex: 1;">
<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="flex: 1;">
<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>
<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>
<div class="metric-description">
${getMetricDescription(metricKey)}
</div>
${metric.details ? renderMetricDetails(metricKey, metric.details) : ''}
</div>
`;
});
document.getElementById('metrics-report').innerHTML = metricsHTML;
}
function renderMetricDetails(metricName, details) {
if (!details || Object.keys(details).length === 0) return '';
// Key metrics to show for each type
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'],
'detect_gpt': ['stability_score', 'curvature_score', 'likelihood_ratio']
};
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) {
const value = typeof details[key] === 'number' ?
(details[key] < 1 && details[key] > 0 ? (details[key] * 100).toFixed(2) + '%' : details[key].toFixed(2)) :
details[key];
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;">${value}</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.',
detect_gpt: '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',
detect_gpt: 'DetectGPT'
};
return names[name] || name.split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
}
function formatDomainName(domain) {
return domain.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, '-');
// For JSON, download directly from current data
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;
}
// Get the original text for report generation
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';
}
// For PDF, request from server
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);
}
// Smooth scrolling for anchor links
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' });
}
}
});
});
// Initialize - show landing page by default
showLanding();
</script>
</body>
</html>