import re
with open(r"d:\ANTIGRAVITY\templates\index.html", "r", encoding="utf-8") as f:
html = f.read()
css_addition = """
/* New Forensic Report Styles */
.risk-badge {
font-size: 0.75rem;
font-weight: 700;
padding: 4px 10px;
border-radius: 20px;
text-transform: uppercase;
letter-spacing: 1px;
margin-left: 10px;
display: inline-block;
}
.risk-high { background: rgba(255, 42, 85, 0.2); color: var(--danger); border: 1px solid var(--danger-glow); animation: pulse-risk 2s infinite; }
.risk-medium { background: rgba(255, 204, 0, 0.2); color: var(--warning); border: 1px solid rgba(255, 204, 0, 0.4); }
.risk-low { background: rgba(0, 255, 170, 0.2); color: #00ffaa; border: 1px solid rgba(0, 255, 170, 0.4); }
@keyframes pulse-risk {
0% { box-shadow: 0 0 0 0 var(--danger-glow); }
70% { box-shadow: 0 0 0 10px rgba(255, 42, 85, 0); }
100% { box-shadow: 0 0 0 0 rgba(255, 42, 85, 0); }
}
.report-section {
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 20px;
display: flex;
flex-direction: column;
gap: 15px;
}
.report-section-title {
font-family: 'Space Grotesk', sans-serif;
font-size: 1.1rem;
font-weight: 600;
color: var(--accent);
display: flex;
align-items: center;
gap: 8px;
border-bottom: 1px solid rgba(255,255,255,0.05);
padding-bottom: 10px;
}
.evidence-list { list-style: none; display: flex; flex-direction: column; gap: 8px; }
.evidence-list li {
display: flex; align-items: flex-start; gap: 10px; font-size: 0.85rem; color: var(--text-main);
}
.evidence-list li i { margin-top: 3px; color: var(--accent); }
.breakdown-table { display: flex; flex-direction: column; gap: 12px; }
.breakdown-row { display: flex; flex-direction: column; gap: 5px; }
.breakdown-meta { display: flex; justify-content: space-between; font-size: 0.85rem; }
.breakdown-bar-bg { width: 100%; height: 6px; background: rgba(255,255,255,0.05); border-radius: 3px; overflow: hidden; }
.breakdown-bar-fill { height: 100%; border-radius: 3px; transition: width 1s cubic-bezier(0.1, 0.8, 0.2, 1); width: 0%; }
/* Tab Styles */
.tabs { display: flex; gap: 10px; border-bottom: 1px solid rgba(255,255,255,0.05); padding-bottom: 10px; flex-wrap: wrap; }
.tab-btn { background: none; border: none; color: var(--text-muted); font-family: inherit; font-size: 0.9rem; cursor: pointer; padding: 5px 10px; transition: all 0.3s; border-radius: 4px; }
.tab-btn:hover { color: var(--text-main); background: rgba(255,255,255,0.05); }
.tab-btn.active { color: var(--accent); background: rgba(0, 240, 255, 0.1); }
.tab-content { display: none; margin-top: 15px; }
.tab-content.active { display: block; }
.heatmap-img { width: 100%; border-radius: 8px; border: 1px solid var(--border-glow); }
.meta-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 15px; }
.meta-item { display: flex; flex-direction: column; gap: 5px; }
.meta-label { font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; }
.meta-val { font-size: 0.9rem; font-weight: 500; }
.pill-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.pill { background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); padding: 4px 10px; border-radius: 12px; font-size: 0.75rem; color: var(--text-muted); }
.collapsible-header { display: flex; justify-content: space-between; align-items: center; cursor: pointer; padding: 15px; background: rgba(255,255,255,0.02); border: 1px solid rgba(255,255,255,0.05); border-radius: 8px; margin-top: 15px; }
.collapsible-header:hover { background: rgba(255,255,255,0.04); }
.collapsible-content { display: none; padding: 15px; border: 1px solid rgba(255,255,255,0.05); border-top: none; border-radius: 0 0 8px 8px; margin-top: -8px; background: rgba(0,0,0,0.2); }
.identity-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.identity-table th, .identity-table td { padding: 8px; text-align: left; border-bottom: 1px solid rgba(255,255,255,0.05); }
.identity-table th { color: var(--text-muted); font-weight: 500; }
.identity-table tr.flagged td { color: var(--danger); }
"""
html = html.replace('/* Admin float link */', css_addition + '\n /* Admin float link */')
html_structure = """
DEEPFAKE DETECTED HIGH RISK
High probability of synthetic rendering detected.
97%
Original Input
Aligned Crop
Forensic Artifact Localization
"""
start_str = """ """
end_str = """
"""
html = html.replace(html[html.find(start_str):html.find(end_str)+len(end_str)], html_structure)
js_addition = """
"""
html = html.replace("""
""", js_addition)
js_logic = """
function toggleStats() {
const content = document.getElementById('stats-content');
const chevron = document.getElementById('stats-chevron');
if (content.style.display === 'block') {
content.style.display = 'none';
chevron.className = 'fa-solid fa-chevron-down';
} else {
content.style.display = 'block';
chevron.className = 'fa-solid fa-chevron-up';
}
}
function renderForensicReport(data) {
resultsEmpty.style.display = 'none';
resultsContainer.style.display = 'flex';
// Trigger animation frame
setTimeout(() => {
resultsContainer.classList.add('visible');
}, 50);
// Populate media
if (data.is_video) {
resultImgOrig.style.display = 'none';
resultVideoOrig.style.display = 'block';
resultVideoOrig.src = data.image_url;
resultImgAligned.style.display = 'none';
} else {
resultVideoOrig.style.display = 'none';
resultImgOrig.style.display = 'block';
resultImgOrig.src = data.image_url;
resultImgAligned.style.display = 'block';
resultImgAligned.src = data.aligned_url || data.image_url;
}
// Aligned crop scan animation toggle
if (data.aligned_url) {
alignedBox.classList.add('scan-line-active');
} else {
alignedBox.classList.remove('scan-line-active');
}
// --- 1. Verdict Banner ---
let prediction = data.prediction;
let confidence = data.confidence;
let riskLevel = 'MEDIUM';
if (data.verdict) {
prediction = data.verdict.final_prediction;
confidence = data.verdict.final_confidence;
riskLevel = data.verdict.risk_level || 'MEDIUM';
}
const isFake = prediction === 'FAKE';
verdictBanner.className = 'verdict-banner ' + (isFake ? 'fake' : 'real');
const riskBadge = document.getElementById('risk-badge');
if (riskBadge) {
riskBadge.textContent = riskLevel + " RISK";
riskBadge.className = 'risk-badge ' + (riskLevel === 'HIGH' ? 'risk-high' : (riskLevel === 'LOW' ? 'risk-low' : 'risk-medium'));
}
let verdictTitleText, verdictDescText;
if (data.is_video) {
verdictTitleText = isFake ? 'Verdict: AI Generated Video' : 'Verdict: Real Video';
verdictDescText = isFake
? 'Temporal frame analysis identifies high-confidence markers of video deepfake manipulation.'
: 'Frame-by-frame forensic scan confirms temporal consistency consistent with organic footage.';
} else if (data.method === 'general_vit_detector') {
verdictTitleText = isFake ? 'Verdict: AI Generated Image' : 'Verdict: Real Image';
verdictDescText = isFake
? 'General AI image detector (no face found) identifies AI-synthesis artefacts.'
: 'General AI image analysis finds no synthetic generation markers.';
} else {
verdictTitleText = isFake ? 'Verdict: AI Generated' : 'Verdict: Real Image';
verdictDescText = isFake
? 'Decomposition scan identifies high-confidence markers of synthetic rendering / deepfake.'
: 'Microscopic texture analysis confirms organic, camera-sensor origins.';
}
let riskBadgeHtml = `${riskLevel} RISK`;
verdictTitle.innerHTML = `${verdictTitleText} ${riskBadgeHtml}`;
verdictDescription.textContent = verdictDescText;
verdictPercentage.innerHTML = `${Math.round(confidence)}%`;
if (data.note) {
verdictDescription.textContent += ' [' + data.note + ']';
}
// Helper to render breakdown bars
function createBreakdownBar(label, value, isSourceFake) {
const color = isSourceFake ? 'var(--danger)' : 'var(--accent)';
return `
${label}
${value.toFixed(1)}%
`;
}
// --- 2. Evidence Summary Card ---
if (data.evidence_summary && data.evidence_summary.length > 0) {
document.getElementById('evidence-card').style.display = 'flex';
const ul = document.getElementById('evidence-list');
ul.innerHTML = '';
data.evidence_summary.forEach(ev => {
ul.innerHTML += ` ${ev}`;
});
} else {
document.getElementById('evidence-card').style.display = 'none';
}
// --- 3. Confidence Breakdown Table ---
if (data.confidence_breakdown) {
document.getElementById('confidence-breakdown-card').style.display = 'flex';
const list = document.getElementById('confidence-breakdown-list');
let html = '';
const displayNames = {
'image_ensemble': 'Image Ensemble',
'frequency_analysis': 'Frequency Analysis',
'ela_analysis': 'Error Level Analysis',
'metadata_forensics': 'Metadata Forensics',
'temporal_consistency': 'Temporal Consistency',
'audio_analysis': 'Audio Analysis'
};
for (const [key, val] of Object.entries(data.confidence_breakdown)) {
const label = displayNames[key] || key;
const isFakeSource = val > 50;
html += createBreakdownBar(label, val, isFakeSource);
}
list.innerHTML = html;
setTimeout(() => {
document.querySelectorAll('#confidence-breakdown-list .breakdown-bar-fill').forEach(el => {
el.style.width = el.style.width; // Trigger reflow is implicitly handled if we set it in HTML but let's animate
});
}, 100);
} else {
document.getElementById('confidence-breakdown-card').style.display = 'none';
}
// --- 4. Heatmap Carousel ---
if (data.artifact_localization && (data.artifact_localization.ela_url || data.artifact_localization.freq_url || data.artifact_localization.attention_url)) {
document.getElementById('heatmap-card').style.display = 'flex';
const tabs = document.getElementById('heatmap-tabs');
const contents = document.getElementById('heatmap-contents');
tabs.innerHTML = '';
contents.innerHTML = '';
const loc = data.artifact_localization;
let first = true;
function addTab(id, label, url) {
if (!url) return;
tabs.innerHTML += ``;
contents.innerHTML += `
`;
first = false;
}
addTab('ela', 'Error Level Analysis', loc.ela_url);
addTab('freq', 'Frequency Anomaly', loc.freq_url);
addTab('attn', 'ViT Attention', loc.attention_url);
window.switchTab = function(id) {
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
document.getElementById('btn-' + id).classList.add('active');
document.getElementById('content-' + id).classList.add('active');
};
} else {
document.getElementById('heatmap-card').style.display = 'none';
}
// --- 5. Ensemble Breakdown Card ---
if (data.ensemble) {
document.getElementById('ensemble-card').style.display = 'flex';
const list = document.getElementById('ensemble-breakdown-list');
let html = '';
if(data.ensemble.primary_score !== undefined) html += createBreakdownBar('Primary ViT (Face)', data.ensemble.primary_score, data.ensemble.primary_score > 50);
if(data.ensemble.secondary_score !== undefined) html += createBreakdownBar('Secondary ViT (General)', data.ensemble.secondary_score, data.ensemble.secondary_score > 50);
if(data.ensemble.fused_score !== undefined) html += createBreakdownBar('Fused Ensemble', data.ensemble.fused_score, data.ensemble.fused_score > 50);
list.innerHTML = html;
} else {
document.getElementById('ensemble-card').style.display = 'none';
}
// --- 6. Metadata Analysis Card ---
if (data.metadata_analysis) {
document.getElementById('metadata-card').style.display = 'flex';
const grid = document.getElementById('metadata-grid');
const m = data.metadata_analysis;
grid.innerHTML = `
Camera${m.camera_model || 'Unknown'}
Lens${m.lens_info || 'Unknown'}
Software${m.software_detected || 'None detected'}
EXIF${m.has_exif ? 'Present' : 'Absent'}
C2PA Status${m.c2pa_status || 'Absent'}
`;
const flags = document.getElementById('metadata-flags');
if (m.flags && m.flags.length > 0) {
flags.innerHTML = m.flags.map(f => `${f}
`).join('');
} else {
flags.innerHTML = `No anomalies
`;
}
} else {
document.getElementById('metadata-card').style.display = 'none';
}
// --- 7. Audio Analysis Card ---
if (data.audio_analysis) {
document.getElementById('audio-card').style.display = 'flex';
const grid = document.getElementById('audio-grid');
const a = data.audio_analysis;
const audioIsFake = a.audio_prediction === 'FAKE';
grid.innerHTML = `
Audio Verdict${a.audio_prediction || 'N/A'}
Confidence${a.audio_confidence ? a.audio_confidence.toFixed(1) + '%' : 'N/A'}
Segments${a.segments_analyzed || 0}
`;
} else {
document.getElementById('audio-card').style.display = 'none';
}
// --- 8. Temporal Analysis Card ---
if (data.temporal_analysis) {
document.getElementById('temporal-card').style.display = 'flex';
const grid = document.getElementById('temporal-grid');
const t = data.temporal_analysis;
grid.innerHTML = `
Variance${t.confidence_variance !== undefined ? t.confidence_variance.toFixed(3) : 'N/A'}
Drift${t.confidence_drift !== undefined ? t.confidence_drift.toFixed(3) : 'N/A'}
Entropy${t.prediction_entropy !== undefined ? t.prediction_entropy.toFixed(3) : 'N/A'}
Consistency${t.temporal_consistency_score !== undefined ? t.temporal_consistency_score.toFixed(3) : 'N/A'}
`;
const idTableContainer = document.getElementById('temporal-identities');
if (t.identities && t.identities.length > 0) {
let idHtml = `| ID | Frames | Fake Prob | Verdict |
`;
t.identities.forEach(id => {
const rowClass = id.flagged ? 'flagged' : '';
idHtml += `
| #${id.id || id.identity_id} |
${id.frames || id.frames_seen} |
${(id.mean_fake || id.mean_fake_prob).toFixed(1)}% |
${id.prediction || (id.flagged ? 'FAKE' : 'REAL')} |
`;
});
idHtml += `
`;
idTableContainer.innerHTML = idHtml;
} else {
idTableContainer.innerHTML = '';
}
} else {
document.getElementById('temporal-card').style.display = 'none';
}
// --- 9. Processing Stats ---
if (data.processing_stats) {
document.getElementById('stats-card').style.display = 'block';
const grid = document.getElementById('stats-grid');
const s = data.processing_stats;
grid.innerHTML = `
Total Time${s.total_analysis_ms || 0} ms
Inference${s.model_inference_ms || 0} ms
Heatmaps${s.heatmap_generation_ms || 0} ms
Peak RAM${s.peak_ram_mb || 0} MB
`;
} else {
document.getElementById('stats-card').style.display = 'none';
}
// Populate forensic metrics (safe with defaults if keys are missing)
"""
start_logic = """ function renderForensicReport(data) {"""
end_logic = """ // Populate forensic metrics (safe with defaults if keys are missing)"""
html = html.replace(html[html.find(start_logic):html.find(end_logic)+len(end_logic)], js_logic)
with open(r"d:\ANTIGRAVITY\templates\index.html", "w", encoding="utf-8") as f:
f.write(html)