import { useState, useRef } from 'react' import './App.css' // Same-origin '/api' in production (FastAPI serves the built app); the Vite dev // server proxies '/api' to the backend. Override with VITE_API_URL if needed. const API = import.meta.env.VITE_API_URL || '/api' const VERDICT = { 'AUTHENTIC': { icon: 'βœ“', tone: 'good', note: 'No significant tampering signals detected.' }, 'TAMPERED': { icon: '!', tone: 'bad', note: 'This document shows signs of manipulation.' }, 'AI-GENERATED': { icon: 'β—†', tone: 'warn', note: 'This image appears to be AI-generated.' }, } const DET_ICON = { ela: 'πŸ”¬', noise: 'πŸ“‘', copy_move: '🧬', double_jpeg: 'πŸ—œοΈ', font: 'πŸ”€', metadata: '🏷️', ai_generated: 'πŸ€–', model: '🧠', } function Dropzone({ onFile }) { const [drag, setDrag] = useState(false) const ref = useRef() return (
ref.current.click()} onDragOver={e => { e.preventDefault(); setDrag(true) }} onDragLeave={() => setDrag(false)} onDrop={e => { e.preventDefault(); setDrag(false); e.dataTransfer.files[0] && onFile(e.dataTransfer.files[0]) }} > e.target.files[0] && onFile(e.target.files[0])} />
⬆
Drop a document or image
JPG Β· PNG Β· TIFF Β· PDF β€” up to 20 MB
Choose file
) } function Gauge({ value, tone }) { const pct = Math.round(value * 100) const r = 34, c = 2 * Math.PI * r const dash = (value * c) return (
{pct}% suspicion
) } function Bar({ name, score }) { const pct = Math.round(score * 100) const tone = score > 0.6 ? 'bad' : score > 0.35 ? 'warn' : 'good' return (
{DET_ICON[name] || 'β€’'} {name.replace(/_/g, ' ')}
{pct}%
) } export default function App() { const [file, setFile] = useState(null) const [state, setState] = useState('idle') const [res, setRes] = useState(null) const [err, setErr] = useState('') const [view, setView] = useState('heatmap') async function run(f) { setFile(f); setState('loading'); setRes(null); setView('heatmap') const form = new FormData(); form.append('file', f) try { const r = await fetch(`${API}/analyze`, { method: 'POST', body: form }) if (!r.ok) throw new Error((await r.json().catch(() => ({}))).detail || `Server error ${r.status}`) setRes(await r.json()); setState('done') } catch (e) { setErr(e.message); setState('error') } } const reset = () => { setFile(null); setRes(null); setState('idle') } const v = res ? (VERDICT[res.label] || VERDICT.AUTHENTIC) : null return (
πŸ” DocForensics
AI-powered tampering & forgery detection
8 detectors Β· CNN
{state === 'idle' && (
Document forensics

Is this document real?

Upload an image or PDF. Eight forensic detectors and a trained CNN inspect it for forgery, cloning, recompression, and AI generation.

🧬 Copy-moveπŸ”¬ Error-level analysis πŸ€– AI detection🧠 Neural localization
)} {state === 'loading' && (

Analyzing {file?.name}

Running detectors + CNN model…

)} {state === 'error' && (
⚠

{err}

)} {state === 'done' && res && (
{v.icon} {res.label}
{v.note}

Evidence map

{file && } {view === 'heatmap' && res.heatmap_base64 && }

Brighter / red areas indicate higher suspicion.

Detector breakdown

{res.per_detector.map(d => )}
{res.evidence?.length > 0 && (

Findings

    {res.evidence.map((e, i) =>
  • {e}
  • )}
)}
)}
) }