Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,962 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import torch
|
| 3 |
+
import numpy as np
|
| 4 |
+
import requests
|
| 5 |
+
import time
|
| 6 |
+
import re
|
| 7 |
+
from PIL import Image
|
| 8 |
+
from transformers import (
|
| 9 |
+
AutoTokenizer,
|
| 10 |
+
AutoModelForSequenceClassification,
|
| 11 |
+
pipeline,
|
| 12 |
+
)
|
| 13 |
+
from huggingface_hub import hf_hub_download
|
| 14 |
+
import tensorflow as tf
|
| 15 |
+
import plotly.graph_objects as go
|
| 16 |
+
import plotly.express as px
|
| 17 |
+
|
| 18 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 19 |
+
# PAGE CONFIG
|
| 20 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 21 |
+
st.set_page_config(
|
| 22 |
+
page_title="DeepTrace AI",
|
| 23 |
+
page_icon="π¬",
|
| 24 |
+
layout="wide",
|
| 25 |
+
initial_sidebar_state="collapsed",
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 29 |
+
# GLOBAL CSS (dark cyber-forensics aesthetic)
|
| 30 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 31 |
+
st.markdown("""
|
| 32 |
+
<style>
|
| 33 |
+
@import url('https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=Syne:wght@400;600;800&family=DM+Sans:wght@300;400;500&display=swap');
|
| 34 |
+
|
| 35 |
+
/* ββ Root Variables ββ */
|
| 36 |
+
:root {
|
| 37 |
+
--bg: #050810;
|
| 38 |
+
--bg2: #0b1120;
|
| 39 |
+
--bg3: #101928;
|
| 40 |
+
--border: #1e2d45;
|
| 41 |
+
--accent: #00d4ff;
|
| 42 |
+
--accent2: #7c3aed;
|
| 43 |
+
--accent3: #10b981;
|
| 44 |
+
--danger: #ef4444;
|
| 45 |
+
--warning: #f59e0b;
|
| 46 |
+
--text: #e2e8f0;
|
| 47 |
+
--muted: #64748b;
|
| 48 |
+
--glow: rgba(0,212,255,0.15);
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
/* ββ Base Reset ββ */
|
| 52 |
+
html, body, [class*="css"] {
|
| 53 |
+
font-family: 'DM Sans', sans-serif;
|
| 54 |
+
background-color: var(--bg) !important;
|
| 55 |
+
color: var(--text) !important;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
.stApp { background: var(--bg) !important; }
|
| 59 |
+
|
| 60 |
+
/* ββ Hide default Streamlit chrome ββ */
|
| 61 |
+
#MainMenu, footer, header { visibility: hidden; }
|
| 62 |
+
.block-container { padding: 0 !important; max-width: 100% !important; }
|
| 63 |
+
|
| 64 |
+
/* ββ Scrollbar ββ */
|
| 65 |
+
::-webkit-scrollbar { width: 4px; }
|
| 66 |
+
::-webkit-scrollbar-track { background: var(--bg2); }
|
| 67 |
+
::-webkit-scrollbar-thumb { background: var(--accent); border-radius: 2px; }
|
| 68 |
+
|
| 69 |
+
/* ββ Animated background grid ββ */
|
| 70 |
+
.grid-bg {
|
| 71 |
+
position: fixed; inset: 0; z-index: 0; pointer-events: none;
|
| 72 |
+
background-image:
|
| 73 |
+
linear-gradient(rgba(0,212,255,0.03) 1px, transparent 1px),
|
| 74 |
+
linear-gradient(90deg, rgba(0,212,255,0.03) 1px, transparent 1px);
|
| 75 |
+
background-size: 60px 60px;
|
| 76 |
+
animation: gridScroll 20s linear infinite;
|
| 77 |
+
}
|
| 78 |
+
@keyframes gridScroll {
|
| 79 |
+
0% { background-position: 0 0; }
|
| 80 |
+
100% { background-position: 60px 60px; }
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
/* ββ Scanline overlay ββ */
|
| 84 |
+
.scanlines {
|
| 85 |
+
position: fixed; inset: 0; z-index: 0; pointer-events: none;
|
| 86 |
+
background: repeating-linear-gradient(
|
| 87 |
+
0deg, transparent, transparent 2px,
|
| 88 |
+
rgba(0,0,0,0.03) 2px, rgba(0,0,0,0.03) 4px
|
| 89 |
+
);
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
/* ββ Hero Header ββ */
|
| 93 |
+
.hero {
|
| 94 |
+
position: relative; z-index: 10;
|
| 95 |
+
background: linear-gradient(135deg, #050810 0%, #0b1120 50%, #080f1c 100%);
|
| 96 |
+
border-bottom: 1px solid var(--border);
|
| 97 |
+
padding: 2.5rem 3rem 2rem;
|
| 98 |
+
display: flex; align-items: center; gap: 1.5rem;
|
| 99 |
+
overflow: hidden;
|
| 100 |
+
}
|
| 101 |
+
.hero::before {
|
| 102 |
+
content: '';
|
| 103 |
+
position: absolute; top: -50%; right: -10%; width: 500px; height: 500px;
|
| 104 |
+
background: radial-gradient(circle, rgba(0,212,255,0.06) 0%, transparent 70%);
|
| 105 |
+
border-radius: 50%;
|
| 106 |
+
}
|
| 107 |
+
.hero::after {
|
| 108 |
+
content: '';
|
| 109 |
+
position: absolute; bottom: -50%; left: 20%; width: 400px; height: 400px;
|
| 110 |
+
background: radial-gradient(circle, rgba(124,58,237,0.06) 0%, transparent 70%);
|
| 111 |
+
border-radius: 50%;
|
| 112 |
+
}
|
| 113 |
+
.hero-icon {
|
| 114 |
+
font-size: 3rem;
|
| 115 |
+
filter: drop-shadow(0 0 20px var(--accent));
|
| 116 |
+
animation: pulse 3s ease-in-out infinite;
|
| 117 |
+
}
|
| 118 |
+
@keyframes pulse {
|
| 119 |
+
0%,100% { filter: drop-shadow(0 0 20px var(--accent)); }
|
| 120 |
+
50% { filter: drop-shadow(0 0 40px var(--accent)); }
|
| 121 |
+
}
|
| 122 |
+
.hero-title {
|
| 123 |
+
font-family: 'Syne', sans-serif;
|
| 124 |
+
font-size: 2.6rem; font-weight: 800; letter-spacing: -0.02em;
|
| 125 |
+
background: linear-gradient(135deg, #00d4ff, #7c3aed);
|
| 126 |
+
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
| 127 |
+
margin: 0; line-height: 1;
|
| 128 |
+
}
|
| 129 |
+
.hero-sub {
|
| 130 |
+
font-family: 'Space Mono', monospace;
|
| 131 |
+
font-size: 0.7rem; color: var(--accent); letter-spacing: 0.2em;
|
| 132 |
+
text-transform: uppercase; margin: 0.4rem 0 0;
|
| 133 |
+
opacity: 0.8;
|
| 134 |
+
}
|
| 135 |
+
.hero-desc {
|
| 136 |
+
font-size: 0.9rem; color: var(--muted); margin: 0; max-width: 500px;
|
| 137 |
+
}
|
| 138 |
+
.status-dot {
|
| 139 |
+
display: inline-block; width: 8px; height: 8px;
|
| 140 |
+
background: var(--accent3); border-radius: 50%;
|
| 141 |
+
margin-right: 0.5rem;
|
| 142 |
+
box-shadow: 0 0 8px var(--accent3);
|
| 143 |
+
animation: blink 2s ease-in-out infinite;
|
| 144 |
+
}
|
| 145 |
+
@keyframes blink {
|
| 146 |
+
0%,100% { opacity: 1; }
|
| 147 |
+
50% { opacity: 0.3; }
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
/* ββ Nav Tabs ββ */
|
| 151 |
+
.nav-wrap {
|
| 152 |
+
position: sticky; top: 0; z-index: 100;
|
| 153 |
+
background: rgba(5,8,16,0.95); backdrop-filter: blur(12px);
|
| 154 |
+
border-bottom: 1px solid var(--border);
|
| 155 |
+
padding: 0 3rem;
|
| 156 |
+
display: flex; gap: 0;
|
| 157 |
+
}
|
| 158 |
+
.stTabs [data-baseweb="tab-list"] {
|
| 159 |
+
background: transparent !important;
|
| 160 |
+
border-bottom: none !important;
|
| 161 |
+
gap: 0 !important;
|
| 162 |
+
padding: 0 !important;
|
| 163 |
+
}
|
| 164 |
+
.stTabs [data-baseweb="tab"] {
|
| 165 |
+
background: transparent !important;
|
| 166 |
+
border: none !important;
|
| 167 |
+
color: var(--muted) !important;
|
| 168 |
+
font-family: 'Space Mono', monospace !important;
|
| 169 |
+
font-size: 0.75rem !important;
|
| 170 |
+
letter-spacing: 0.1em !important;
|
| 171 |
+
text-transform: uppercase !important;
|
| 172 |
+
padding: 1rem 2rem !important;
|
| 173 |
+
border-bottom: 2px solid transparent !important;
|
| 174 |
+
transition: all 0.2s !important;
|
| 175 |
+
}
|
| 176 |
+
.stTabs [data-baseweb="tab"]:hover {
|
| 177 |
+
color: var(--accent) !important;
|
| 178 |
+
background: var(--glow) !important;
|
| 179 |
+
}
|
| 180 |
+
.stTabs [aria-selected="true"] {
|
| 181 |
+
color: var(--accent) !important;
|
| 182 |
+
border-bottom: 2px solid var(--accent) !important;
|
| 183 |
+
background: var(--glow) !important;
|
| 184 |
+
}
|
| 185 |
+
.stTabs [data-baseweb="tab-panel"] { padding: 0 !important; }
|
| 186 |
+
|
| 187 |
+
/* ββ Content area ββ */
|
| 188 |
+
.content-area { padding: 2rem 3rem; position: relative; z-index: 5; }
|
| 189 |
+
|
| 190 |
+
/* ββ Cards ββ */
|
| 191 |
+
.card {
|
| 192 |
+
background: var(--bg2);
|
| 193 |
+
border: 1px solid var(--border);
|
| 194 |
+
border-radius: 12px;
|
| 195 |
+
padding: 1.5rem;
|
| 196 |
+
margin-bottom: 1.25rem;
|
| 197 |
+
position: relative; overflow: hidden;
|
| 198 |
+
transition: border-color 0.2s, box-shadow 0.2s;
|
| 199 |
+
}
|
| 200 |
+
.card:hover {
|
| 201 |
+
border-color: rgba(0,212,255,0.3);
|
| 202 |
+
box-shadow: 0 0 20px rgba(0,212,255,0.05);
|
| 203 |
+
}
|
| 204 |
+
.card::before {
|
| 205 |
+
content: '';
|
| 206 |
+
position: absolute; top: 0; left: 0; right: 0; height: 2px;
|
| 207 |
+
background: linear-gradient(90deg, transparent, var(--accent), transparent);
|
| 208 |
+
opacity: 0;
|
| 209 |
+
transition: opacity 0.3s;
|
| 210 |
+
}
|
| 211 |
+
.card:hover::before { opacity: 1; }
|
| 212 |
+
|
| 213 |
+
.card-label {
|
| 214 |
+
font-family: 'Space Mono', monospace;
|
| 215 |
+
font-size: 0.65rem; color: var(--accent); letter-spacing: 0.2em;
|
| 216 |
+
text-transform: uppercase; margin-bottom: 0.75rem;
|
| 217 |
+
display: flex; align-items: center; gap: 0.5rem;
|
| 218 |
+
}
|
| 219 |
+
.card-label::after {
|
| 220 |
+
content: ''; flex: 1; height: 1px; background: var(--border);
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
/* ββ Result Verdict ββ */
|
| 224 |
+
.verdict-wrap {
|
| 225 |
+
text-align: center; padding: 2rem 1rem;
|
| 226 |
+
background: var(--bg2); border: 1px solid var(--border);
|
| 227 |
+
border-radius: 16px; position: relative; overflow: hidden;
|
| 228 |
+
}
|
| 229 |
+
.verdict-badge {
|
| 230 |
+
display: inline-block;
|
| 231 |
+
font-family: 'Syne', sans-serif;
|
| 232 |
+
font-size: 1.8rem; font-weight: 800;
|
| 233 |
+
padding: 0.5rem 2rem; border-radius: 8px;
|
| 234 |
+
letter-spacing: 0.05em;
|
| 235 |
+
margin-bottom: 0.75rem;
|
| 236 |
+
}
|
| 237 |
+
.verdict-fake {
|
| 238 |
+
background: rgba(239,68,68,0.12);
|
| 239 |
+
border: 1px solid rgba(239,68,68,0.4);
|
| 240 |
+
color: #ef4444;
|
| 241 |
+
box-shadow: 0 0 30px rgba(239,68,68,0.1);
|
| 242 |
+
}
|
| 243 |
+
.verdict-real {
|
| 244 |
+
background: rgba(16,185,129,0.12);
|
| 245 |
+
border: 1px solid rgba(16,185,129,0.4);
|
| 246 |
+
color: #10b981;
|
| 247 |
+
box-shadow: 0 0 30px rgba(16,185,129,0.1);
|
| 248 |
+
}
|
| 249 |
+
.verdict-uncertain {
|
| 250 |
+
background: rgba(245,158,11,0.12);
|
| 251 |
+
border: 1px solid rgba(245,158,11,0.4);
|
| 252 |
+
color: #f59e0b;
|
| 253 |
+
box-shadow: 0 0 30px rgba(245,158,11,0.1);
|
| 254 |
+
}
|
| 255 |
+
.verdict-conf {
|
| 256 |
+
font-family: 'Space Mono', monospace;
|
| 257 |
+
font-size: 0.8rem; color: var(--muted);
|
| 258 |
+
}
|
| 259 |
+
.verdict-conf span {
|
| 260 |
+
color: var(--text); font-size: 1.1rem; font-weight: 700;
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
/* ββ Metric boxes ββ */
|
| 264 |
+
.metric-row { display: flex; gap: 1rem; flex-wrap: wrap; margin: 1rem 0; }
|
| 265 |
+
.metric-box {
|
| 266 |
+
flex: 1; min-width: 120px;
|
| 267 |
+
background: var(--bg3); border: 1px solid var(--border);
|
| 268 |
+
border-radius: 10px; padding: 1rem;
|
| 269 |
+
text-align: center;
|
| 270 |
+
}
|
| 271 |
+
.metric-val {
|
| 272 |
+
font-family: 'Syne', sans-serif;
|
| 273 |
+
font-size: 1.6rem; font-weight: 800;
|
| 274 |
+
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
| 275 |
+
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
| 276 |
+
display: block;
|
| 277 |
+
}
|
| 278 |
+
.metric-key {
|
| 279 |
+
font-family: 'Space Mono', monospace;
|
| 280 |
+
font-size: 0.6rem; color: var(--muted);
|
| 281 |
+
text-transform: uppercase; letter-spacing: 0.1em;
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
/* ββ Tags ββ */
|
| 285 |
+
.tag {
|
| 286 |
+
display: inline-block;
|
| 287 |
+
font-family: 'Space Mono', monospace;
|
| 288 |
+
font-size: 0.65rem; letter-spacing: 0.05em;
|
| 289 |
+
padding: 0.25rem 0.75rem; border-radius: 999px;
|
| 290 |
+
margin: 0.25rem;
|
| 291 |
+
}
|
| 292 |
+
.tag-danger { background: rgba(239,68,68,0.1); border: 1px solid rgba(239,68,68,0.3); color: #ef4444; }
|
| 293 |
+
.tag-warn { background: rgba(245,158,11,0.1); border: 1px solid rgba(245,158,11,0.3); color: #f59e0b; }
|
| 294 |
+
.tag-safe { background: rgba(16,185,129,0.1); border: 1px solid rgba(16,185,129,0.3); color: #10b981; }
|
| 295 |
+
.tag-info { background: rgba(0,212,255,0.1); border: 1px solid rgba(0,212,255,0.3); color: #00d4ff; }
|
| 296 |
+
|
| 297 |
+
/* ββ Streamlit overrides ββ */
|
| 298 |
+
.stTextArea textarea {
|
| 299 |
+
background: var(--bg3) !important;
|
| 300 |
+
border: 1px solid var(--border) !important;
|
| 301 |
+
color: var(--text) !important;
|
| 302 |
+
font-family: 'DM Sans', sans-serif !important;
|
| 303 |
+
border-radius: 10px !important;
|
| 304 |
+
font-size: 0.9rem !important;
|
| 305 |
+
transition: border-color 0.2s !important;
|
| 306 |
+
}
|
| 307 |
+
.stTextArea textarea:focus {
|
| 308 |
+
border-color: var(--accent) !important;
|
| 309 |
+
box-shadow: 0 0 0 1px var(--accent) !important;
|
| 310 |
+
}
|
| 311 |
+
.stTextArea label {
|
| 312 |
+
color: var(--muted) !important;
|
| 313 |
+
font-family: 'Space Mono', monospace !important;
|
| 314 |
+
font-size: 0.7rem !important;
|
| 315 |
+
text-transform: uppercase !important;
|
| 316 |
+
letter-spacing: 0.1em !important;
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
.stButton > button {
|
| 320 |
+
background: linear-gradient(135deg, #00d4ff22, #7c3aed22) !important;
|
| 321 |
+
color: var(--accent) !important;
|
| 322 |
+
border: 1px solid var(--accent) !important;
|
| 323 |
+
border-radius: 8px !important;
|
| 324 |
+
font-family: 'Space Mono', monospace !important;
|
| 325 |
+
font-size: 0.75rem !important;
|
| 326 |
+
letter-spacing: 0.1em !important;
|
| 327 |
+
text-transform: uppercase !important;
|
| 328 |
+
padding: 0.6rem 2rem !important;
|
| 329 |
+
transition: all 0.2s !important;
|
| 330 |
+
width: 100% !important;
|
| 331 |
+
}
|
| 332 |
+
.stButton > button:hover {
|
| 333 |
+
background: linear-gradient(135deg, #00d4ff33, #7c3aed33) !important;
|
| 334 |
+
box-shadow: 0 0 20px rgba(0,212,255,0.2) !important;
|
| 335 |
+
transform: translateY(-1px) !important;
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
.stFileUploader {
|
| 339 |
+
background: var(--bg3) !important;
|
| 340 |
+
border: 1px dashed var(--border) !important;
|
| 341 |
+
border-radius: 12px !important;
|
| 342 |
+
transition: border-color 0.2s !important;
|
| 343 |
+
}
|
| 344 |
+
.stFileUploader:hover { border-color: var(--accent) !important; }
|
| 345 |
+
.stFileUploader label { color: var(--muted) !important; }
|
| 346 |
+
|
| 347 |
+
/* spinner */
|
| 348 |
+
.stSpinner > div { border-top-color: var(--accent) !important; }
|
| 349 |
+
|
| 350 |
+
/* divider */
|
| 351 |
+
hr { border-color: var(--border) !important; margin: 1.5rem 0 !important; }
|
| 352 |
+
|
| 353 |
+
/* image captions */
|
| 354 |
+
.stImage > div > div { color: var(--muted) !important; font-size: 0.75rem !important; }
|
| 355 |
+
|
| 356 |
+
/* ββ Section header ββ */
|
| 357 |
+
.section-header {
|
| 358 |
+
font-family: 'Syne', sans-serif;
|
| 359 |
+
font-size: 1.3rem; font-weight: 700;
|
| 360 |
+
color: var(--text); margin: 0 0 0.25rem;
|
| 361 |
+
}
|
| 362 |
+
.section-sub {
|
| 363 |
+
font-size: 0.85rem; color: var(--muted); margin: 0 0 1.5rem;
|
| 364 |
+
}
|
| 365 |
+
|
| 366 |
+
/* ββ Signal bars decoration ββ */
|
| 367 |
+
.signal { display: flex; align-items: flex-end; gap: 3px; height: 20px; }
|
| 368 |
+
.signal span {
|
| 369 |
+
display: block; width: 4px; border-radius: 2px;
|
| 370 |
+
background: var(--accent);
|
| 371 |
+
animation: signalAnim 1.2s ease-in-out infinite;
|
| 372 |
+
}
|
| 373 |
+
.signal span:nth-child(1) { height: 6px; animation-delay: 0s; }
|
| 374 |
+
.signal span:nth-child(2) { height: 10px; animation-delay: 0.1s; }
|
| 375 |
+
.signal span:nth-child(3) { height: 14px; animation-delay: 0.2s; }
|
| 376 |
+
.signal span:nth-child(4) { height: 10px; animation-delay: 0.3s; }
|
| 377 |
+
.signal span:nth-child(5) { height: 6px; animation-delay: 0.4s; }
|
| 378 |
+
@keyframes signalAnim {
|
| 379 |
+
0%,100% { opacity: 1; }
|
| 380 |
+
50% { opacity: 0.3; }
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
/* ββ About cards ββ */
|
| 384 |
+
.about-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 1rem; }
|
| 385 |
+
.about-card {
|
| 386 |
+
background: var(--bg2); border: 1px solid var(--border);
|
| 387 |
+
border-radius: 12px; padding: 1.5rem; text-align: center;
|
| 388 |
+
transition: all 0.2s;
|
| 389 |
+
}
|
| 390 |
+
.about-card:hover {
|
| 391 |
+
border-color: rgba(0,212,255,0.3);
|
| 392 |
+
transform: translateY(-2px);
|
| 393 |
+
}
|
| 394 |
+
.about-card-icon { font-size: 2rem; margin-bottom: 0.75rem; }
|
| 395 |
+
.about-card-title {
|
| 396 |
+
font-family: 'Syne', sans-serif;
|
| 397 |
+
font-size: 0.95rem; font-weight: 700; color: var(--text);
|
| 398 |
+
margin: 0 0 0.5rem;
|
| 399 |
+
}
|
| 400 |
+
.about-card-desc { font-size: 0.8rem; color: var(--muted); line-height: 1.5; }
|
| 401 |
+
|
| 402 |
+
/* responsive */
|
| 403 |
+
@media (max-width: 768px) {
|
| 404 |
+
.hero { padding: 1.5rem; flex-direction: column; text-align: center; }
|
| 405 |
+
.hero-title { font-size: 1.8rem; }
|
| 406 |
+
.content-area { padding: 1rem; }
|
| 407 |
+
.about-grid { grid-template-columns: 1fr; }
|
| 408 |
+
}
|
| 409 |
+
</style>
|
| 410 |
+
|
| 411 |
+
<div class="grid-bg"></div>
|
| 412 |
+
<div class="scanlines"></div>
|
| 413 |
+
""", unsafe_allow_html=True)
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 417 |
+
# MODEL LOADERS
|
| 418 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 419 |
+
@st.cache_resource(show_spinner=False)
|
| 420 |
+
def load_text_model():
|
| 421 |
+
model_name = "hamzab/roberta-fake-news-classification"
|
| 422 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
| 423 |
+
model = AutoModelForSequenceClassification.from_pretrained(model_name)
|
| 424 |
+
model.eval()
|
| 425 |
+
return tokenizer, model
|
| 426 |
+
|
| 427 |
+
|
| 428 |
+
@st.cache_resource(show_spinner=False)
|
| 429 |
+
def load_image_model():
|
| 430 |
+
path = hf_hub_download(
|
| 431 |
+
repo_id="Muniba930/image-detector",
|
| 432 |
+
filename="image_detector_v2.h5"
|
| 433 |
+
)
|
| 434 |
+
model = tf.keras.models.load_model(path)
|
| 435 |
+
return model
|
| 436 |
+
|
| 437 |
+
|
| 438 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 439 |
+
# PREDICTION FUNCTIONS
|
| 440 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 441 |
+
CLICKBAIT_WORDS = [
|
| 442 |
+
"SHOCKING", "BREAKING", "EXPOSED", "YOU WON'T BELIEVE",
|
| 443 |
+
"UNBELIEVABLE", "MUST SEE", "URGENT", "SECRET", "LEAKED",
|
| 444 |
+
"BANNED", "CENSORED", "THEY DON'T WANT", "EXCLUSIVE"
|
| 445 |
+
]
|
| 446 |
+
|
| 447 |
+
FEAR_WORDS = [
|
| 448 |
+
"danger", "crisis", "collapse", "attack", "war", "threat",
|
| 449 |
+
"catastrophe", "disaster", "chaos", "doom", "apocalypse",
|
| 450 |
+
"deadly", "terror", "panic", "emergency"
|
| 451 |
+
]
|
| 452 |
+
|
| 453 |
+
|
| 454 |
+
def predict_news(text, tokenizer, model):
|
| 455 |
+
inputs = tokenizer(
|
| 456 |
+
text, return_tensors="pt",
|
| 457 |
+
truncation=True, padding=True, max_length=512
|
| 458 |
+
)
|
| 459 |
+
with torch.no_grad():
|
| 460 |
+
outputs = model(**inputs)
|
| 461 |
+
probs = torch.nn.functional.softmax(outputs.logits, dim=1)
|
| 462 |
+
pred = torch.argmax(probs).item()
|
| 463 |
+
confidence = torch.max(probs).item() * 100
|
| 464 |
+
label = model.config.id2label[pred]
|
| 465 |
+
return label, confidence, probs[0].tolist()
|
| 466 |
+
|
| 467 |
+
|
| 468 |
+
def manipulation_score(text):
|
| 469 |
+
text_up = text.upper()
|
| 470 |
+
cb_hits = [w for w in CLICKBAIT_WORDS if w in text_up]
|
| 471 |
+
fear_hits = [w for w in FEAR_WORDS if w.lower() in text.lower()]
|
| 472 |
+
exclamations = text.count("!")
|
| 473 |
+
caps_ratio = sum(1 for c in text if c.isupper()) / max(len(text), 1)
|
| 474 |
+
|
| 475 |
+
score = (
|
| 476 |
+
len(cb_hits) * 20 +
|
| 477 |
+
len(fear_hits) * 10 +
|
| 478 |
+
min(exclamations * 5, 20) +
|
| 479 |
+
min(caps_ratio * 100, 20)
|
| 480 |
+
)
|
| 481 |
+
return min(int(score), 100), cb_hits, fear_hits
|
| 482 |
+
|
| 483 |
+
|
| 484 |
+
def predict_image(img, model):
|
| 485 |
+
img_r = img.resize((224, 224)).convert("RGB")
|
| 486 |
+
arr = np.array(img_r, dtype=np.float32) / 255.0
|
| 487 |
+
arr = np.expand_dims(arr, axis=0)
|
| 488 |
+
pred = model.predict(arr, verbose=0)
|
| 489 |
+
conf = float(pred[0][0]) * 100
|
| 490 |
+
# 0 = AI, 1 = Real (based on training)
|
| 491 |
+
if conf < 50:
|
| 492 |
+
return "AI GENERATED", 100 - conf
|
| 493 |
+
else:
|
| 494 |
+
return "REAL IMAGE", conf
|
| 495 |
+
|
| 496 |
+
|
| 497 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 498 |
+
# CHART HELPERS
|
| 499 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 500 |
+
def gauge_chart(value, title, color):
|
| 501 |
+
fig = go.Figure(go.Indicator(
|
| 502 |
+
mode = "gauge+number",
|
| 503 |
+
value = value,
|
| 504 |
+
title = {"text": title, "font": {"family": "Space Mono", "size": 11, "color": "#64748b"}},
|
| 505 |
+
number= {"suffix": "%", "font": {"family": "Syne", "size": 28, "color": "#e2e8f0"}},
|
| 506 |
+
gauge = {
|
| 507 |
+
"axis" : {"range": [0, 100], "tickcolor": "#1e2d45", "tickfont": {"color": "#64748b", "size": 9}},
|
| 508 |
+
"bar" : {"color": color, "thickness": 0.3},
|
| 509 |
+
"bgcolor" : "#0b1120",
|
| 510 |
+
"bordercolor": "#1e2d45",
|
| 511 |
+
"steps" : [
|
| 512 |
+
{"range": [0, 33], "color": "rgba(16,185,129,0.08)"},
|
| 513 |
+
{"range": [33, 66], "color": "rgba(245,158,11,0.08)"},
|
| 514 |
+
{"range": [66,100], "color": "rgba(239,68,68,0.08)"},
|
| 515 |
+
],
|
| 516 |
+
"threshold" : {"line": {"color": color, "width": 2}, "value": value},
|
| 517 |
+
}
|
| 518 |
+
))
|
| 519 |
+
fig.update_layout(
|
| 520 |
+
paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
|
| 521 |
+
margin=dict(t=30, b=10, l=20, r=20), height=200,
|
| 522 |
+
)
|
| 523 |
+
return fig
|
| 524 |
+
|
| 525 |
+
|
| 526 |
+
def bar_chart(labels, values, colors):
|
| 527 |
+
fig = go.Figure(go.Bar(
|
| 528 |
+
x=values, y=labels, orientation="h",
|
| 529 |
+
marker_color=colors, marker_line_width=0,
|
| 530 |
+
text=[f"{v:.1f}%" for v in values],
|
| 531 |
+
textfont={"family": "Space Mono", "size": 10, "color": "#e2e8f0"},
|
| 532 |
+
textposition="outside",
|
| 533 |
+
))
|
| 534 |
+
fig.update_layout(
|
| 535 |
+
paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
|
| 536 |
+
xaxis=dict(range=[0,120], visible=False),
|
| 537 |
+
yaxis=dict(tickfont={"family": "Space Mono", "size": 10, "color": "#64748b"}),
|
| 538 |
+
margin=dict(t=10, b=10, l=10, r=60), height=120,
|
| 539 |
+
showlegend=False,
|
| 540 |
+
)
|
| 541 |
+
return fig
|
| 542 |
+
|
| 543 |
+
|
| 544 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 545 |
+
# HERO
|
| 546 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 547 |
+
st.markdown("""
|
| 548 |
+
<div class="hero">
|
| 549 |
+
<div class="hero-icon">π¬</div>
|
| 550 |
+
<div>
|
| 551 |
+
<div class="hero-title">DeepTrace AI</div>
|
| 552 |
+
<div class="hero-sub">
|
| 553 |
+
<span class="status-dot"></span>
|
| 554 |
+
Multi-Modal Misinformation Detection System Β· v2.0
|
| 555 |
+
</div>
|
| 556 |
+
<p class="hero-desc">
|
| 557 |
+
Advanced forensic AI for detecting fake news, AI-generated images,
|
| 558 |
+
and emotional manipulation in digital media.
|
| 559 |
+
</p>
|
| 560 |
+
</div>
|
| 561 |
+
<div style="margin-left:auto; display:flex; align-items:center; gap:1rem; flex-wrap:wrap;">
|
| 562 |
+
<div style="text-align:right;">
|
| 563 |
+
<div style="font-family:'Space Mono',monospace; font-size:0.6rem; color:#64748b; letter-spacing:0.1em;">STATUS</div>
|
| 564 |
+
<div style="font-family:'Syne',sans-serif; font-size:0.85rem; color:#10b981; font-weight:700;">β ONLINE</div>
|
| 565 |
+
</div>
|
| 566 |
+
<div class="signal">
|
| 567 |
+
<span></span><span></span><span></span><span></span><span></span>
|
| 568 |
+
</div>
|
| 569 |
+
</div>
|
| 570 |
+
</div>
|
| 571 |
+
""", unsafe_allow_html=True)
|
| 572 |
+
|
| 573 |
+
|
| 574 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 575 |
+
# TABS
|
| 576 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 577 |
+
tab1, tab2, tab3 = st.tabs([
|
| 578 |
+
"π° Fake News Detector",
|
| 579 |
+
"πΌοΈ AI Image Detector",
|
| 580 |
+
"βοΈ About & Models",
|
| 581 |
+
])
|
| 582 |
+
|
| 583 |
+
|
| 584 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 585 |
+
# TAB 1 β FAKE NEWS
|
| 586 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 587 |
+
with tab1:
|
| 588 |
+
st.markdown('<div class="content-area">', unsafe_allow_html=True)
|
| 589 |
+
|
| 590 |
+
st.markdown("""
|
| 591 |
+
<div class="section-header">π° Fake News & Manipulation Detector</div>
|
| 592 |
+
<div class="section-sub">
|
| 593 |
+
Paste a news article or headline. The AI will analyse authenticity,
|
| 594 |
+
emotional manipulation, and clickbait signals.
|
| 595 |
+
</div>
|
| 596 |
+
""", unsafe_allow_html=True)
|
| 597 |
+
|
| 598 |
+
col_in, col_out = st.columns([1, 1], gap="large")
|
| 599 |
+
|
| 600 |
+
with col_in:
|
| 601 |
+
st.markdown('<div class="card"><div class="card-label">π Input Text</div>', unsafe_allow_html=True)
|
| 602 |
+
user_text = st.text_area(
|
| 603 |
+
"Article / Headline",
|
| 604 |
+
height=240,
|
| 605 |
+
placeholder="Paste your news article or headline hereβ¦\n\nExample:\n'BREAKING: Scientists discover miracle cure that Big Pharma is hiding!'",
|
| 606 |
+
label_visibility="collapsed",
|
| 607 |
+
)
|
| 608 |
+
|
| 609 |
+
# sample buttons
|
| 610 |
+
st.markdown('<div class="card-label" style="margin-top:1rem;">π§ͺ Try Samples</div>', unsafe_allow_html=True)
|
| 611 |
+
s1, s2 = st.columns(2)
|
| 612 |
+
with s1:
|
| 613 |
+
if st.button("Real News Sample"):
|
| 614 |
+
user_text = ("NASA's James Webb Space Telescope has captured the deepest infrared "
|
| 615 |
+
"image of the universe, revealing thousands of galaxies that existed "
|
| 616 |
+
"over 13 billion years ago. The milestone image was released in July 2022.")
|
| 617 |
+
st.session_state["sample_text"] = user_text
|
| 618 |
+
with s2:
|
| 619 |
+
if st.button("Fake News Sample"):
|
| 620 |
+
user_text = ("SHOCKING!! Scientists EXPOSED: drinking lemon water CURES cancer in 30 days! "
|
| 621 |
+
"Big Pharma is HIDING this SECRET β SHARE before it gets DELETED!!")
|
| 622 |
+
st.session_state["sample_text"] = user_text
|
| 623 |
+
|
| 624 |
+
if "sample_text" in st.session_state and not user_text:
|
| 625 |
+
user_text = st.session_state["sample_text"]
|
| 626 |
+
|
| 627 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 628 |
+
|
| 629 |
+
analyze_clicked = st.button("π ANALYZE TEXT", key="analyze_btn")
|
| 630 |
+
|
| 631 |
+
with col_out:
|
| 632 |
+
if analyze_clicked and user_text.strip():
|
| 633 |
+
with st.spinner("Loading NLP modelβ¦"):
|
| 634 |
+
tokenizer, text_model = load_text_model()
|
| 635 |
+
|
| 636 |
+
with st.spinner("Analysing contentβ¦"):
|
| 637 |
+
time.sleep(0.3)
|
| 638 |
+
label, conf, probs_list = predict_news(user_text, tokenizer, text_model)
|
| 639 |
+
manip, cb_hits, fear_hits = manipulation_score(user_text)
|
| 640 |
+
word_count = len(user_text.split())
|
| 641 |
+
sent_count = user_text.count(".") + user_text.count("!") + user_text.count("?")
|
| 642 |
+
excl_count = user_text.count("!")
|
| 643 |
+
|
| 644 |
+
# ββ Verdict ββ
|
| 645 |
+
is_fake = label.upper() == "FAKE"
|
| 646 |
+
badge_class = "verdict-fake" if is_fake else "verdict-real"
|
| 647 |
+
verdict_icon = "β οΈ FAKE NEWS" if is_fake else "β
REAL NEWS"
|
| 648 |
+
|
| 649 |
+
st.markdown(f"""
|
| 650 |
+
<div class="verdict-wrap">
|
| 651 |
+
<div class="verdict-badge {badge_class}">{verdict_icon}</div>
|
| 652 |
+
<div class="verdict-conf">Confidence: <span>{conf:.1f}%</span></div>
|
| 653 |
+
</div>
|
| 654 |
+
""", unsafe_allow_html=True)
|
| 655 |
+
|
| 656 |
+
# ββ Gauge row ββ
|
| 657 |
+
g1, g2, g3 = st.columns(3)
|
| 658 |
+
with g1:
|
| 659 |
+
fake_prob = probs_list[0] * 100 if len(probs_list) > 0 else conf
|
| 660 |
+
st.plotly_chart(gauge_chart(fake_prob, "FAKE PROB", "#ef4444"), use_container_width=True)
|
| 661 |
+
with g2:
|
| 662 |
+
real_prob = probs_list[1] * 100 if len(probs_list) > 1 else (100 - conf)
|
| 663 |
+
st.plotly_chart(gauge_chart(real_prob, "REAL PROB", "#10b981"), use_container_width=True)
|
| 664 |
+
with g3:
|
| 665 |
+
st.plotly_chart(gauge_chart(manip, "MANIPULATION", "#f59e0b"), use_container_width=True)
|
| 666 |
+
|
| 667 |
+
# ββ Metrics ββ
|
| 668 |
+
st.markdown(f"""
|
| 669 |
+
<div class="metric-row">
|
| 670 |
+
<div class="metric-box">
|
| 671 |
+
<span class="metric-val">{word_count}</span>
|
| 672 |
+
<span class="metric-key">Words</span>
|
| 673 |
+
</div>
|
| 674 |
+
<div class="metric-box">
|
| 675 |
+
<span class="metric-val">{excl_count}</span>
|
| 676 |
+
<span class="metric-key">Exclamations</span>
|
| 677 |
+
</div>
|
| 678 |
+
<div class="metric-box">
|
| 679 |
+
<span class="metric-val">{len(cb_hits)}</span>
|
| 680 |
+
<span class="metric-key">Clickbait Hits</span>
|
| 681 |
+
</div>
|
| 682 |
+
<div class="metric-box">
|
| 683 |
+
<span class="metric-val">{len(fear_hits)}</span>
|
| 684 |
+
<span class="metric-key">Fear Words</span>
|
| 685 |
+
</div>
|
| 686 |
+
</div>
|
| 687 |
+
""", unsafe_allow_html=True)
|
| 688 |
+
|
| 689 |
+
# ββ Tags ββ
|
| 690 |
+
if cb_hits or fear_hits:
|
| 691 |
+
st.markdown('<div class="card"><div class="card-label">π¨ Detected Signals</div>', unsafe_allow_html=True)
|
| 692 |
+
tags_html = ""
|
| 693 |
+
for w in cb_hits:
|
| 694 |
+
tags_html += f'<span class="tag tag-danger">π΄ {w}</span>'
|
| 695 |
+
for w in fear_hits[:6]:
|
| 696 |
+
tags_html += f'<span class="tag tag-warn">π‘ {w}</span>'
|
| 697 |
+
st.markdown(f'<div>{tags_html}</div></div>', unsafe_allow_html=True)
|
| 698 |
+
|
| 699 |
+
# ββ Analysis summary ββ
|
| 700 |
+
manip_level = "HIGH" if manip > 60 else "MEDIUM" if manip > 30 else "LOW"
|
| 701 |
+
manip_color = "#ef4444" if manip > 60 else "#f59e0b" if manip > 30 else "#10b981"
|
| 702 |
+
st.markdown(f"""
|
| 703 |
+
<div class="card">
|
| 704 |
+
<div class="card-label">π§ Analysis Summary</div>
|
| 705 |
+
<p style="font-size:0.85rem; color:#94a3b8; line-height:1.7; margin:0;">
|
| 706 |
+
The model classified this content as
|
| 707 |
+
<strong style="color:{'#ef4444' if is_fake else '#10b981'}">
|
| 708 |
+
{'FAKE' if is_fake else 'REAL'}
|
| 709 |
+
</strong>
|
| 710 |
+
with <strong style="color:#e2e8f0">{conf:.1f}%</strong> confidence.<br>
|
| 711 |
+
Manipulation score is
|
| 712 |
+
<strong style="color:{manip_color}">{manip_level} ({manip}%)</strong>
|
| 713 |
+
β detected <strong style="color:#e2e8f0">{len(cb_hits)}</strong>
|
| 714 |
+
clickbait keyword(s) and
|
| 715 |
+
<strong style="color:#e2e8f0">{len(fear_hits)}</strong> fear-based word(s).
|
| 716 |
+
{'<br><span style="color:#f59e0b">β οΈ High emotional manipulation detected β verify from multiple sources.</span>' if manip > 50 else ''}
|
| 717 |
+
</p>
|
| 718 |
+
</div>
|
| 719 |
+
""", unsafe_allow_html=True)
|
| 720 |
+
|
| 721 |
+
elif analyze_clicked:
|
| 722 |
+
st.warning("Please enter some text to analyse.")
|
| 723 |
+
else:
|
| 724 |
+
st.markdown("""
|
| 725 |
+
<div class="card" style="text-align:center; padding:3rem 1rem; border-style:dashed;">
|
| 726 |
+
<div style="font-size:3rem; margin-bottom:1rem; opacity:0.3;">π°</div>
|
| 727 |
+
<div style="font-family:'Space Mono',monospace; font-size:0.75rem;
|
| 728 |
+
color:#64748b; letter-spacing:0.1em;">
|
| 729 |
+
AWAITING INPUT
|
| 730 |
+
</div>
|
| 731 |
+
<div style="font-size:0.8rem; color:#475569; margin-top:0.5rem;">
|
| 732 |
+
Paste an article and click Analyze
|
| 733 |
+
</div>
|
| 734 |
+
</div>
|
| 735 |
+
""", unsafe_allow_html=True)
|
| 736 |
+
|
| 737 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 738 |
+
|
| 739 |
+
|
| 740 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 741 |
+
# TAB 2 β IMAGE DETECTOR
|
| 742 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 743 |
+
with tab2:
|
| 744 |
+
st.markdown('<div class="content-area">', unsafe_allow_html=True)
|
| 745 |
+
|
| 746 |
+
st.markdown("""
|
| 747 |
+
<div class="section-header">πΌοΈ AI Image Forensic Analyser</div>
|
| 748 |
+
<div class="section-sub">
|
| 749 |
+
Upload any image. The model will determine whether it was taken by a
|
| 750 |
+
camera or generated by an AI system.
|
| 751 |
+
</div>
|
| 752 |
+
""", unsafe_allow_html=True)
|
| 753 |
+
|
| 754 |
+
col_up, col_res = st.columns([1, 1], gap="large")
|
| 755 |
+
|
| 756 |
+
with col_up:
|
| 757 |
+
st.markdown('<div class="card"><div class="card-label">π Upload Image</div>', unsafe_allow_html=True)
|
| 758 |
+
uploaded = st.file_uploader(
|
| 759 |
+
"Upload image",
|
| 760 |
+
type=["jpg", "jpeg", "png", "webp"],
|
| 761 |
+
label_visibility="collapsed",
|
| 762 |
+
)
|
| 763 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 764 |
+
|
| 765 |
+
if uploaded:
|
| 766 |
+
img = Image.open(uploaded)
|
| 767 |
+
st.image(img, caption="Uploaded Image", use_container_width=True)
|
| 768 |
+
|
| 769 |
+
# Image metadata
|
| 770 |
+
w, h = img.size
|
| 771 |
+
st.markdown(f"""
|
| 772 |
+
<div class="card">
|
| 773 |
+
<div class="card-label">π Image Metadata</div>
|
| 774 |
+
<div class="metric-row" style="margin:0;">
|
| 775 |
+
<div class="metric-box">
|
| 776 |
+
<span class="metric-val" style="font-size:1.1rem;">{w}Γ{h}</span>
|
| 777 |
+
<span class="metric-key">Resolution</span>
|
| 778 |
+
</div>
|
| 779 |
+
<div class="metric-box">
|
| 780 |
+
<span class="metric-val" style="font-size:1.1rem;">{img.mode}</span>
|
| 781 |
+
<span class="metric-key">Color Mode</span>
|
| 782 |
+
</div>
|
| 783 |
+
<div class="metric-box">
|
| 784 |
+
<span class="metric-val" style="font-size:1.1rem;">{uploaded.name.split('.')[-1].upper()}</span>
|
| 785 |
+
<span class="metric-key">Format</span>
|
| 786 |
+
</div>
|
| 787 |
+
</div>
|
| 788 |
+
</div>
|
| 789 |
+
""", unsafe_allow_html=True)
|
| 790 |
+
|
| 791 |
+
scan_clicked = st.button("π¬ SCAN IMAGE", key="scan_btn", disabled=uploaded is None)
|
| 792 |
+
|
| 793 |
+
with col_res:
|
| 794 |
+
if scan_clicked and uploaded:
|
| 795 |
+
with st.spinner("Loading vision modelβ¦"):
|
| 796 |
+
img_model = load_image_model()
|
| 797 |
+
|
| 798 |
+
with st.spinner("Running forensic scanβ¦"):
|
| 799 |
+
time.sleep(0.5)
|
| 800 |
+
verdict, score = predict_image(img, img_model)
|
| 801 |
+
|
| 802 |
+
is_ai = verdict == "AI GENERATED"
|
| 803 |
+
badge_class = "verdict-fake" if is_ai else "verdict-real"
|
| 804 |
+
icon = "π€ AI GENERATED" if is_ai else "π· REAL IMAGE"
|
| 805 |
+
|
| 806 |
+
st.markdown(f"""
|
| 807 |
+
<div class="verdict-wrap">
|
| 808 |
+
<div class="verdict-badge {badge_class}">{icon}</div>
|
| 809 |
+
<div class="verdict-conf">Detection confidence: <span>{score:.1f}%</span></div>
|
| 810 |
+
</div>
|
| 811 |
+
""", unsafe_allow_html=True)
|
| 812 |
+
|
| 813 |
+
# Gauges
|
| 814 |
+
ai_score = score if is_ai else 100 - score
|
| 815 |
+
real_score = 100 - ai_score
|
| 816 |
+
g1, g2 = st.columns(2)
|
| 817 |
+
with g1:
|
| 818 |
+
st.plotly_chart(gauge_chart(ai_score, "AI PROBABILITY", "#ef4444"), use_container_width=True)
|
| 819 |
+
with g2:
|
| 820 |
+
st.plotly_chart(gauge_chart(real_score, "REAL PROBABILITY", "#10b981"), use_container_width=True)
|
| 821 |
+
|
| 822 |
+
# Confidence bar
|
| 823 |
+
fig_bar = bar_chart(
|
| 824 |
+
["AI Generated", "Real / Authentic"],
|
| 825 |
+
[ai_score, real_score],
|
| 826 |
+
["#ef4444" if ai_score > real_score else "#475569",
|
| 827 |
+
"#10b981" if real_score >= ai_score else "#475569"]
|
| 828 |
+
)
|
| 829 |
+
st.markdown('<div class="card"><div class="card-label">π Score Breakdown</div>', unsafe_allow_html=True)
|
| 830 |
+
st.plotly_chart(fig_bar, use_container_width=True)
|
| 831 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 832 |
+
|
| 833 |
+
# Summary
|
| 834 |
+
risk = "HIGH" if ai_score > 75 else "MEDIUM" if ai_score > 45 else "LOW"
|
| 835 |
+
risk_col = "#ef4444" if ai_score > 75 else "#f59e0b" if ai_score > 45 else "#10b981"
|
| 836 |
+
st.markdown(f"""
|
| 837 |
+
<div class="card">
|
| 838 |
+
<div class="card-label">π§ Forensic Summary</div>
|
| 839 |
+
<p style="font-size:0.85rem; color:#94a3b8; line-height:1.7; margin:0;">
|
| 840 |
+
Forensic analysis indicates this image is
|
| 841 |
+
<strong style="color:{'#ef4444' if is_ai else '#10b981'}">
|
| 842 |
+
{'likely AI-generated' if is_ai else 'likely authentic'}
|
| 843 |
+
</strong>
|
| 844 |
+
with <strong style="color:#e2e8f0">{score:.1f}%</strong> confidence.<br>
|
| 845 |
+
AI generation risk level:
|
| 846 |
+
<strong style="color:{risk_col}">{risk}</strong>.
|
| 847 |
+
{'<br><span style="color:#f59e0b">β οΈ Do not use this image as evidence without further verification.</span>' if is_ai else '<br><span style="color:#10b981">β
Image appears to be from a real camera source.</span>'}
|
| 848 |
+
</p>
|
| 849 |
+
</div>
|
| 850 |
+
""", unsafe_allow_html=True)
|
| 851 |
+
|
| 852 |
+
elif not uploaded:
|
| 853 |
+
st.markdown("""
|
| 854 |
+
<div class="card" style="text-align:center; padding:3rem 1rem; border-style:dashed;">
|
| 855 |
+
<div style="font-size:3rem; margin-bottom:1rem; opacity:0.3;">πΌοΈ</div>
|
| 856 |
+
<div style="font-family:'Space Mono',monospace; font-size:0.75rem;
|
| 857 |
+
color:#64748b; letter-spacing:0.1em;">
|
| 858 |
+
NO IMAGE UPLOADED
|
| 859 |
+
</div>
|
| 860 |
+
<div style="font-size:0.8rem; color:#475569; margin-top:0.5rem;">
|
| 861 |
+
Upload an image and click Scan
|
| 862 |
+
</div>
|
| 863 |
+
</div>
|
| 864 |
+
""", unsafe_allow_html=True)
|
| 865 |
+
|
| 866 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 867 |
+
|
| 868 |
+
|
| 869 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 870 |
+
# TAB 3 β ABOUT
|
| 871 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 872 |
+
with tab3:
|
| 873 |
+
st.markdown('<div class="content-area">', unsafe_allow_html=True)
|
| 874 |
+
|
| 875 |
+
st.markdown("""
|
| 876 |
+
<div class="section-header">βοΈ System Architecture & Models</div>
|
| 877 |
+
<div class="section-sub">Technical details about the AI models and pipeline powering DeepTrace AI.</div>
|
| 878 |
+
|
| 879 |
+
<div class="about-grid">
|
| 880 |
+
<div class="about-card">
|
| 881 |
+
<div class="about-card-icon">π§ </div>
|
| 882 |
+
<div class="about-card-title">NLP Engine</div>
|
| 883 |
+
<div class="about-card-desc">
|
| 884 |
+
RoBERTa-based transformer fine-tuned on 72,000+ news articles.
|
| 885 |
+
Classifies content as FAKE or REAL with confidence scoring.
|
| 886 |
+
<br><br>
|
| 887 |
+
<span style="font-family:'Space Mono',monospace; font-size:0.65rem; color:#00d4ff;">
|
| 888 |
+
hamzab/roberta-fake-news-classification
|
| 889 |
+
</span>
|
| 890 |
+
</div>
|
| 891 |
+
</div>
|
| 892 |
+
<div class="about-card">
|
| 893 |
+
<div class="about-card-icon">ποΈ</div>
|
| 894 |
+
<div class="about-card-title">Vision Engine</div>
|
| 895 |
+
<div class="about-card-desc">
|
| 896 |
+
EfficientNetB3 transfer learning model fine-tuned on
|
| 897 |
+
AI-generated vs real image datasets. Detects synthetic textures
|
| 898 |
+
and generation artifacts.
|
| 899 |
+
<br><br>
|
| 900 |
+
<span style="font-family:'Space Mono',monospace; font-size:0.65rem; color:#00d4ff;">
|
| 901 |
+
Muniba930/image-detector
|
| 902 |
+
</span>
|
| 903 |
+
</div>
|
| 904 |
+
</div>
|
| 905 |
+
<div class="about-card">
|
| 906 |
+
<div class="about-card-icon">π</div>
|
| 907 |
+
<div class="about-card-title">Manipulation Analyser</div>
|
| 908 |
+
<div class="about-card-desc">
|
| 909 |
+
Rule-based linguistic analyser detecting clickbait keywords,
|
| 910 |
+
fear-based language, excessive punctuation, and emotional
|
| 911 |
+
manipulation patterns.
|
| 912 |
+
<br><br>
|
| 913 |
+
<span style="font-family:'Space Mono',monospace; font-size:0.65rem; color:#00d4ff;">
|
| 914 |
+
Custom lexical engine
|
| 915 |
+
</span>
|
| 916 |
+
</div>
|
| 917 |
+
</div>
|
| 918 |
+
</div>
|
| 919 |
+
""", unsafe_allow_html=True)
|
| 920 |
+
|
| 921 |
+
st.markdown("<br>", unsafe_allow_html=True)
|
| 922 |
+
|
| 923 |
+
# Tech stack
|
| 924 |
+
st.markdown("""
|
| 925 |
+
<div class="card">
|
| 926 |
+
<div class="card-label">π οΈ Tech Stack</div>
|
| 927 |
+
<div style="display:flex; flex-wrap:wrap; gap:0.5rem; margin-top:0.5rem;">
|
| 928 |
+
<span class="tag tag-info">Streamlit</span>
|
| 929 |
+
<span class="tag tag-info">Hugging Face</span>
|
| 930 |
+
<span class="tag tag-info">PyTorch</span>
|
| 931 |
+
<span class="tag tag-info">TensorFlow</span>
|
| 932 |
+
<span class="tag tag-info">Transformers</span>
|
| 933 |
+
<span class="tag tag-info">EfficientNetB3</span>
|
| 934 |
+
<span class="tag tag-info">RoBERTa</span>
|
| 935 |
+
<span class="tag tag-info">Plotly</span>
|
| 936 |
+
<span class="tag tag-info">Pillow</span>
|
| 937 |
+
</div>
|
| 938 |
+
</div>
|
| 939 |
+
|
| 940 |
+
<div class="card">
|
| 941 |
+
<div class="card-label">β οΈ Limitations & Disclaimer</div>
|
| 942 |
+
<p style="font-size:0.85rem; color:#94a3b8; line-height:1.8; margin:0;">
|
| 943 |
+
β’ This tool is for <strong style="color:#e2e8f0">research and educational purposes</strong>
|
| 944 |
+
β not a definitive fact-checker.<br>
|
| 945 |
+
β’ Image model performance is limited by training data diversity;
|
| 946 |
+
very recent AI generators may not be detected.<br>
|
| 947 |
+
β’ Always cross-reference with trusted news sources before
|
| 948 |
+
drawing conclusions.<br>
|
| 949 |
+
β’ The manipulation score is heuristic-based and may produce
|
| 950 |
+
false positives on legitimate breaking news.
|
| 951 |
+
</p>
|
| 952 |
+
</div>
|
| 953 |
+
""", unsafe_allow_html=True)
|
| 954 |
+
|
| 955 |
+
st.markdown("""
|
| 956 |
+
<div style="text-align:center; padding:2rem 0; color:#334155;
|
| 957 |
+
font-family:'Space Mono',monospace; font-size:0.65rem; letter-spacing:0.1em;">
|
| 958 |
+
DEEPTRACE AI Β· MULTI-MODAL MISINFORMATION DETECTION Β· BUILT WITH β€οΈ USING OPEN-SOURCE AI
|
| 959 |
+
</div>
|
| 960 |
+
""", unsafe_allow_html=True)
|
| 961 |
+
|
| 962 |
+
st.markdown('</div>', unsafe_allow_html=True)
|