Spaces:
Running
Running
Aditya-Jadhav150
feat: AEGIS-AI Detection Engine Overhaul v2 — Multi-model ensemble, forensic artifact localization, audio detection, decision fusion, rich forensic reporting (Code Only)
ea107b5 Download patch_index.py from Aditya150/Veritas-AI: direct link, hf CLI and curl.
- Browser
- Download file 27.2 kB
-
https://huggingface.co/spaces/Aditya150/Veritas-AI/resolve/main/patch_index.py
- Command line
-
hf download hf://spaces/Aditya150/Veritas-AI/patch_index.py
-
curl -L -o patch_index.py https://huggingface.co/spaces/Aditya150/Veritas-AI/resolve/main/patch_index.py
27.2 kB
| 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 = """ | |
| <!-- Verdict banner --> | |
| <div class="verdict-banner" id="verdict-banner"> | |
| <div class="verdict-info"> | |
| <h4 id="verdict-title">DEEPFAKE DETECTED <span id="risk-badge" class="risk-badge risk-high">HIGH RISK</span></h4> | |
| <p id="verdict-description">High probability of synthetic rendering detected.</p> | |
| </div> | |
| <div class="verdict-percentage" id="verdict-percentage"> | |
| 97<span>%</span> | |
| </div> | |
| </div> | |
| <!-- NEW: Evidence Summary Card --> | |
| <div class="report-section" id="evidence-card" style="display: none;"> | |
| <div class="report-section-title"><i class="fa-solid fa-list-check"></i> Evidence Summary</div> | |
| <ul class="evidence-list" id="evidence-list"></ul> | |
| </div> | |
| <!-- NEW: Confidence Breakdown Card --> | |
| <div class="report-section" id="confidence-breakdown-card" style="display: none;"> | |
| <div class="report-section-title"><i class="fa-solid fa-chart-bar"></i> Confidence Breakdown</div> | |
| <div class="breakdown-table" id="confidence-breakdown-list"></div> | |
| </div> | |
| <!-- Crops comparison --> | |
| <div class="images-comparison"> | |
| <div class="img-box"> | |
| <img id="result-img-orig" src="" alt="Uploaded original" style="display: none;"> | |
| <video id="result-video-orig" src="" autoplay loop muted playsinline style="display: none; width: 100%; border-radius: 8px; border: 1px solid var(--border-glow); box-shadow: 0 0 20px var(--accent-glow);"></video> | |
| <div class="img-box-label">Original Input</div> | |
| </div> | |
| <div class="img-box scan-line-active" id="aligned-box"> | |
| <img id="result-img-aligned" src="" alt="Aligned Face" style="display: none;"> | |
| <div class="img-box-label">Aligned Crop</div> | |
| </div> | |
| </div> | |
| <!-- NEW: Heatmap Carousel --> | |
| <div class="report-section" id="heatmap-card" style="display: none;"> | |
| <div class="report-section-title"><i class="fa-solid fa-layer-group"></i> Forensic Artifact Localization</div> | |
| <div class="tabs" id="heatmap-tabs"></div> | |
| <div id="heatmap-contents"></div> | |
| </div> | |
| <!-- NEW: Ensemble Breakdown Card --> | |
| <div class="report-section" id="ensemble-card" style="display: none;"> | |
| <div class="report-section-title"><i class="fa-solid fa-network-wired"></i> Ensemble Breakdown</div> | |
| <div class="breakdown-table" id="ensemble-breakdown-list"></div> | |
| </div> | |
| <!-- NEW: Metadata Card --> | |
| <div class="report-section" id="metadata-card" style="display: none;"> | |
| <div class="report-section-title"><i class="fa-solid fa-camera"></i> Metadata Analysis</div> | |
| <div class="meta-grid" id="metadata-grid"></div> | |
| <div class="pill-list" id="metadata-flags"></div> | |
| </div> | |
| <!-- NEW: Audio Card --> | |
| <div class="report-section" id="audio-card" style="display: none;"> | |
| <div class="report-section-title"><i class="fa-solid fa-waveform"></i> Audio Analysis</div> | |
| <div class="meta-grid" id="audio-grid"></div> | |
| </div> | |
| <!-- NEW: Temporal Card --> | |
| <div class="report-section" id="temporal-card" style="display: none;"> | |
| <div class="report-section-title"><i class="fa-solid fa-clock-rotate-left"></i> Temporal Analysis</div> | |
| <div class="meta-grid" id="temporal-grid"></div> | |
| <div id="temporal-identities" style="margin-top: 15px;"></div> | |
| </div> | |
| <!-- 9 Metrics breakdown --> | |
| <div class="metrics-grid"> | |
| """ | |
| start_str = """ <!-- Verdict banner -->""" | |
| end_str = """ <!-- 9 Metrics breakdown --> | |
| <div class="metrics-grid">""" | |
| html = html.replace(html[html.find(start_str):html.find(end_str)+len(end_str)], html_structure) | |
| js_addition = """ | |
| <!-- NEW: Processing Stats --> | |
| <div id="stats-card" style="display: none;"> | |
| <div class="collapsible-header" onclick="toggleStats()"> | |
| <div style="font-weight: 600; font-size: 0.9rem;"><i class="fa-solid fa-terminal"></i> Show Debug Info (Processing Stats)</div> | |
| <i class="fa-solid fa-chevron-down" id="stats-chevron"></i> | |
| </div> | |
| <div class="collapsible-content" id="stats-content"> | |
| <div class="meta-grid" id="stats-grid"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Empty state results placeholder --> | |
| """ | |
| html = html.replace(""" </div> | |
| <!-- Empty state results placeholder -->""", 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 = `<span id="risk-badge" class="risk-badge ${riskLevel === 'HIGH' ? 'risk-high' : (riskLevel === 'LOW' ? 'risk-low' : 'risk-medium')}">${riskLevel} RISK</span>`; | |
| verdictTitle.innerHTML = `${verdictTitleText} ${riskBadgeHtml}`; | |
| verdictDescription.textContent = verdictDescText; | |
| verdictPercentage.innerHTML = `${Math.round(confidence)}<span>%</span>`; | |
| 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 ` | |
| <div class="breakdown-row"> | |
| <div class="breakdown-meta"> | |
| <span>${label}</span> | |
| <span style="color: ${color}">${value.toFixed(1)}%</span> | |
| </div> | |
| <div class="breakdown-bar-bg"> | |
| <div class="breakdown-bar-fill" style="width: ${value}%; background: ${color}"></div> | |
| </div> | |
| </div> | |
| `; | |
| } | |
| // --- 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 += `<li><i class="fa-solid fa-check"></i> <span>${ev}</span></li>`; | |
| }); | |
| } 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 += `<button class="tab-btn ${first ? 'active' : ''}" onclick="switchTab('${id}')" id="btn-${id}">${label}</button>`; | |
| contents.innerHTML += ` | |
| <div class="tab-content ${first ? 'active' : ''}" id="content-${id}"> | |
| <img src="${url}" class="heatmap-img"> | |
| </div> | |
| `; | |
| 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 = ` | |
| <div class="meta-item"><span class="meta-label">Camera</span><span class="meta-val">${m.camera_model || 'Unknown'}</span></div> | |
| <div class="meta-item"><span class="meta-label">Lens</span><span class="meta-val">${m.lens_info || 'Unknown'}</span></div> | |
| <div class="meta-item"><span class="meta-label">Software</span><span class="meta-val">${m.software_detected || 'None detected'}</span></div> | |
| <div class="meta-item"><span class="meta-label">EXIF</span><span class="meta-val">${m.has_exif ? 'Present' : 'Absent'}</span></div> | |
| <div class="meta-item"><span class="meta-label">C2PA Status</span><span class="meta-val">${m.c2pa_status || 'Absent'}</span></div> | |
| `; | |
| const flags = document.getElementById('metadata-flags'); | |
| if (m.flags && m.flags.length > 0) { | |
| flags.innerHTML = m.flags.map(f => `<div class="pill">${f}</div>`).join(''); | |
| } else { | |
| flags.innerHTML = `<div class="pill">No anomalies</div>`; | |
| } | |
| } 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 = ` | |
| <div class="meta-item"><span class="meta-label">Audio Verdict</span><span class="meta-val" style="color: ${audioIsFake ? 'var(--danger)' : 'var(--accent)'}">${a.audio_prediction || 'N/A'}</span></div> | |
| <div class="meta-item"><span class="meta-label">Confidence</span><span class="meta-val">${a.audio_confidence ? a.audio_confidence.toFixed(1) + '%' : 'N/A'}</span></div> | |
| <div class="meta-item"><span class="meta-label">Segments</span><span class="meta-val">${a.segments_analyzed || 0}</span></div> | |
| `; | |
| } 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 = ` | |
| <div class="meta-item"><span class="meta-label">Variance</span><span class="meta-val">${t.confidence_variance !== undefined ? t.confidence_variance.toFixed(3) : 'N/A'}</span></div> | |
| <div class="meta-item"><span class="meta-label">Drift</span><span class="meta-val">${t.confidence_drift !== undefined ? t.confidence_drift.toFixed(3) : 'N/A'}</span></div> | |
| <div class="meta-item"><span class="meta-label">Entropy</span><span class="meta-val">${t.prediction_entropy !== undefined ? t.prediction_entropy.toFixed(3) : 'N/A'}</span></div> | |
| <div class="meta-item"><span class="meta-label">Consistency</span><span class="meta-val">${t.temporal_consistency_score !== undefined ? t.temporal_consistency_score.toFixed(3) : 'N/A'}</span></div> | |
| `; | |
| const idTableContainer = document.getElementById('temporal-identities'); | |
| if (t.identities && t.identities.length > 0) { | |
| let idHtml = `<table class="identity-table"><tr><th>ID</th><th>Frames</th><th>Fake Prob</th><th>Verdict</th></tr>`; | |
| t.identities.forEach(id => { | |
| const rowClass = id.flagged ? 'flagged' : ''; | |
| idHtml += `<tr class="${rowClass}"> | |
| <td>#${id.id || id.identity_id}</td> | |
| <td>${id.frames || id.frames_seen}</td> | |
| <td>${(id.mean_fake || id.mean_fake_prob).toFixed(1)}%</td> | |
| <td>${id.prediction || (id.flagged ? 'FAKE' : 'REAL')}</td> | |
| </tr>`; | |
| }); | |
| idHtml += `</table>`; | |
| 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 = ` | |
| <div class="meta-item"><span class="meta-label">Total Time</span><span class="meta-val">${s.total_analysis_ms || 0} ms</span></div> | |
| <div class="meta-item"><span class="meta-label">Inference</span><span class="meta-val">${s.model_inference_ms || 0} ms</span></div> | |
| <div class="meta-item"><span class="meta-label">Heatmaps</span><span class="meta-val">${s.heatmap_generation_ms || 0} ms</span></div> | |
| <div class="meta-item"><span class="meta-label">Peak RAM</span><span class="meta-val">${s.peak_ram_mb || 0} MB</span></div> | |
| `; | |
| } 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) | |