thai_fakenews_detector / src /streamlit_app.py
thirdExec's picture
remove clear button
78e02e1
import os
import streamlit as st
from dotenv import load_dotenv
from huggingface_hub import InferenceClient
load_dotenv()
ENDPOINT_URL = os.environ.get("HF_ENDPOINT_URL")
st.set_page_config(
page_title="Thai Fake News Detector",
page_icon="🔍",
layout="centered",
)
# ── Premium Dark Theme CSS with Animations ────────────────────────────────────
st.markdown("""
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+Thai:wght@300;400;500;600;700&display=swap');
:root {
--bg-primary: #000000;
--bg-secondary: #0a0a0a;
--bg-card: #111111;
--accent-purple: #8b5cf6;
--accent-pink: #ec4899;
--accent-cyan: #06b6d4;
--accent-green: #10b981;
--accent-red: #ef4444;
--text-primary: #ffffff;
--text-secondary: #a1a1aa;
--text-muted: #52525b;
--border-color: rgba(255, 255, 255, 0.08);
--glow-purple: rgba(139, 92, 246, 0.4);
}
*:not([data-testid="stIconMaterial"]) {
font-family: 'Noto Sans Thai', -apple-system, BlinkMacSystemFont, sans-serif !important;
}
/* Main Background with Animated Gradient */
.stApp {
background: var(--bg-primary);
background-image:
radial-gradient(ellipse 80% 50% at 50% -20%, rgba(139, 92, 246, 0.15), transparent),
radial-gradient(ellipse 60% 40% at 100% 100%, rgba(236, 72, 153, 0.1), transparent),
radial-gradient(ellipse 40% 30% at 0% 100%, rgba(6, 182, 212, 0.08), transparent);
min-height: 100vh;
}
/* Floating Orbs Animation */
.stApp::before {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background:
radial-gradient(circle at 20% 20%, rgba(139, 92, 246, 0.08) 0%, transparent 40%),
radial-gradient(circle at 80% 80%, rgba(236, 72, 153, 0.06) 0%, transparent 40%),
radial-gradient(circle at 50% 50%, rgba(6, 182, 212, 0.04) 0%, transparent 50%);
animation: floatOrbs 20s ease-in-out infinite;
pointer-events: none;
z-index: 0;
}
@keyframes floatOrbs {
0%, 100% {
background-position: 0% 0%, 100% 100%, 50% 50%;
opacity: 0.6;
}
25% {
background-position: 30% 70%, 70% 30%, 20% 80%;
opacity: 0.8;
}
50% {
background-position: 100% 50%, 0% 50%, 80% 20%;
opacity: 1;
}
75% {
background-position: 70% 30%, 30% 70%, 50% 50%;
opacity: 0.7;
}
}
/* Grid Lines Background */
.stApp::after {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image:
linear-gradient(rgba(255, 255, 255, 0.02) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px);
background-size: 60px 60px;
pointer-events: none;
z-index: 0;
}
/* Title with Shimmer Effect */
.main-title {
font-size: 3rem;
font-weight: 700;
text-align: center;
margin-bottom: 0.5rem;
background: linear-gradient(
90deg,
#ffffff 0%,
#8b5cf6 25%,
#ec4899 50%,
#8b5cf6 75%,
#ffffff 100%
);
background-size: 200% auto;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: shimmer 3s linear infinite;
filter: drop-shadow(0 0 30px var(--glow-purple));
}
@keyframes shimmer {
0% { background-position: 0% center; }
100% { background-position: 200% center; }
}
/* Subtitle with Fade Animation */
.subtitle {
text-align: center;
color: var(--text-secondary);
font-size: 1.1rem;
margin-bottom: 2.5rem;
opacity: 0;
animation: fadeSlideUp 0.8s ease forwards 0.3s;
}
@keyframes fadeSlideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Glass Card Style */
.glass-card {
background: linear-gradient(
135deg,
rgba(17, 17, 17, 0.8) 0%,
rgba(17, 17, 17, 0.6) 100%
);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid var(--border-color);
border-radius: 24px;
padding: 2rem;
margin: 1.5rem 0;
position: relative;
overflow: hidden;
animation: cardAppear 0.6s ease forwards;
}
.glass-card::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.03),
transparent
);
animation: cardShine 4s ease-in-out infinite;
}
@keyframes cardShine {
0%, 100% { left: -100%; }
50% { left: 100%; }
}
@keyframes cardAppear {
from {
opacity: 0;
transform: translateY(30px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
/* Info Box */
.info-box {
background: linear-gradient(135deg, rgba(139, 92, 246, 0.1) 0%, rgba(236, 72, 153, 0.05) 100%);
border: 1px solid rgba(139, 92, 246, 0.2);
border-radius: 20px;
padding: 1.5rem 2rem;
margin: 1.5rem 0;
position: relative;
overflow: hidden;
}
.info-box::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 4px;
height: 100%;
background: linear-gradient(180deg, var(--accent-purple), var(--accent-pink));
border-radius: 4px 0 0 4px;
}
.info-box p {
color: var(--text-secondary);
margin: 0;
line-height: 1.8;
font-size: 0.95rem;
}
/* Sidebar */
section[data-testid="stSidebar"] {
background: linear-gradient(180deg, #050505 0%, #0a0a0a 100%) !important;
border-right: 1px solid var(--border-color);
}
section[data-testid="stSidebar"] > div {
background: transparent !important;
}
.sidebar-header {
text-align: center;
padding: 2rem 1rem;
border-bottom: 1px solid var(--border-color);
margin-bottom: 1.5rem;
}
.sidebar-logo {
width: 80px;
height: 80px;
background: linear-gradient(135deg, var(--accent-purple), var(--accent-pink));
border-radius: 20px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 1rem;
font-size: 2.5rem;
animation: pulse 2s ease-in-out infinite;
box-shadow: 0 10px 40px rgba(139, 92, 246, 0.3);
}
@keyframes pulse {
0%, 100% { transform: scale(1); box-shadow: 0 10px 40px rgba(139, 92, 246, 0.3); }
50% { transform: scale(1.05); box-shadow: 0 15px 50px rgba(139, 92, 246, 0.4); }
}
.sidebar-title {
color: var(--text-primary);
font-size: 1.3rem;
font-weight: 700;
margin: 0;
}
.sidebar-subtitle {
color: var(--text-muted);
font-size: 0.85rem;
margin-top: 0.25rem;
}
/* Sidebar Link Cards */
.link-card {
background: rgba(255, 255, 255, 0.02);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 0.875rem 1rem;
margin: 0.5rem 0;
display: flex;
align-items: center;
gap: 0.75rem;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
text-decoration: none;
}
.link-card:hover {
background: rgba(139, 92, 246, 0.1);
border-color: rgba(139, 92, 246, 0.3);
transform: translateX(4px);
}
.link-card .icon {
width: 36px;
height: 36px;
background: rgba(139, 92, 246, 0.15);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.1rem;
flex-shrink: 0;
}
.link-card .text {
color: var(--text-secondary);
font-size: 0.9rem;
font-weight: 500;
transition: color 0.3s ease;
}
.link-card:hover .text {
color: var(--text-primary);
}
.link-card a {
color: inherit !important;
text-decoration: none !important;
}
/* Stats Card */
.stats-card {
background: linear-gradient(135deg, rgba(6, 182, 212, 0.08) 0%, rgba(139, 92, 246, 0.08) 100%);
border: 1px solid rgba(6, 182, 212, 0.15);
border-radius: 16px;
padding: 1.25rem;
margin-top: 1.5rem;
}
.stats-card .stat-label {
color: var(--accent-cyan);
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 0.5rem;
}
.stats-card .stat-value {
color: var(--text-primary);
font-size: 1.5rem;
font-weight: 700;
}
.stats-card .stat-desc {
color: var(--text-muted);
font-size: 0.8rem;
margin-top: 0.25rem;
}
/* Text Area */
.stTextArea textarea {
background: rgba(17, 17, 17, 0.9) !important;
border: 1px solid var(--border-color) !important;
border-radius: 16px !important;
color: var(--text-primary) !important;
font-size: 1rem !important;
padding: 1rem !important;
transition: all 0.3s ease !important;
}
.stTextArea textarea:focus {
border-color: var(--accent-purple) !important;
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.15), 0 0 30px rgba(139, 92, 246, 0.1) !important;
}
.stTextArea textarea::placeholder {
color: var(--text-muted) !important;
}
.stTextArea label {
color: var(--text-secondary) !important;
font-weight: 500 !important;
margin-bottom: 0.5rem !important;
}
/* Buttons */
.stButton > button {
background: linear-gradient(135deg, var(--accent-purple), var(--accent-pink)) !important;
color: white !important;
border: none !important;
border-radius: 14px !important;
padding: 0.875rem 2rem !important;
font-weight: 600 !important;
font-size: 1rem !important;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
box-shadow: 0 4px 20px rgba(139, 92, 246, 0.35) !important;
position: relative;
overflow: hidden;
}
.stButton > button::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.2),
transparent
);
transition: left 0.5s ease;
}
.stButton > button:hover::before {
left: 100%;
}
.stButton > button:hover {
transform: translateY(-3px) !important;
box-shadow: 0 8px 30px rgba(139, 92, 246, 0.5) !important;
}
.stButton > button:active {
transform: translateY(-1px) !important;
}
/* Secondary Button */
div[data-testid="column"]:last-child .stButton > button {
background: transparent !important;
border: 1px solid var(--border-color) !important;
box-shadow: none !important;
}
div[data-testid="column"]:last-child .stButton > button:hover {
background: rgba(255, 255, 255, 0.05) !important;
border-color: rgba(255, 255, 255, 0.2) !important;
box-shadow: none !important;
}
/* Result Cards */
.result-card {
border-radius: 24px;
padding: 2.5rem;
margin: 2rem 0;
text-align: center;
position: relative;
overflow: hidden;
animation: resultAppear 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes resultAppear {
0% {
opacity: 0;
transform: scale(0.8) translateY(20px);
}
100% {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.result-real {
background: linear-gradient(135deg, rgba(16, 185, 129, 0.15) 0%, rgba(6, 78, 59, 0.2) 100%);
border: 1px solid rgba(16, 185, 129, 0.3);
box-shadow: 0 0 60px rgba(16, 185, 129, 0.15);
}
.result-fake {
background: linear-gradient(135deg, rgba(239, 68, 68, 0.15) 0%, rgba(127, 29, 29, 0.2) 100%);
border: 1px solid rgba(239, 68, 68, 0.3);
box-shadow: 0 0 60px rgba(239, 68, 68, 0.15);
}
.result-unknown {
background: linear-gradient(135deg, rgba(251, 191, 36, 0.15) 0%, rgba(120, 53, 15, 0.2) 100%);
border: 1px solid rgba(251, 191, 36, 0.3);
box-shadow: 0 0 60px rgba(251, 191, 36, 0.15);
}
.result-icon-wrapper {
width: 100px;
height: 100px;
margin: 0 auto 1.5rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 3rem;
animation: iconBounce 0.8s ease forwards 0.3s;
opacity: 0;
}
.result-real .result-icon-wrapper {
background: linear-gradient(135deg, rgba(16, 185, 129, 0.3), rgba(16, 185, 129, 0.1));
box-shadow: 0 0 40px rgba(16, 185, 129, 0.3);
}
.result-fake .result-icon-wrapper {
background: linear-gradient(135deg, rgba(239, 68, 68, 0.3), rgba(239, 68, 68, 0.1));
box-shadow: 0 0 40px rgba(239, 68, 68, 0.3);
}
.result-unknown .result-icon-wrapper {
background: linear-gradient(135deg, rgba(251, 191, 36, 0.3), rgba(251, 191, 36, 0.1));
box-shadow: 0 0 40px rgba(251, 191, 36, 0.3);
}
@keyframes iconBounce {
0% { opacity: 0; transform: scale(0.3); }
50% { transform: scale(1.1); }
100% { opacity: 1; transform: scale(1); }
}
.result-title {
font-size: 2rem;
font-weight: 700;
margin-bottom: 0.75rem;
}
.result-real .result-title { color: var(--accent-green); }
.result-fake .result-title { color: var(--accent-red); }
.result-unknown .result-title { color: #fbbf24; }
.result-desc {
color: var(--text-secondary);
font-size: 1.1rem;
line-height: 1.6;
}
/* Loading Animation */
.loading-container {
text-align: center;
padding: 3rem;
}
.loading-dots {
display: flex;
justify-content: center;
gap: 8px;
margin-bottom: 1.5rem;
}
.loading-dot {
width: 14px;
height: 14px;
background: linear-gradient(135deg, var(--accent-purple), var(--accent-pink));
border-radius: 50%;
animation: dotPulse 1.4s ease-in-out infinite;
}
.loading-dot:nth-child(1) { animation-delay: 0s; }
.loading-dot:nth-child(2) { animation-delay: 0.15s; }
.loading-dot:nth-child(3) { animation-delay: 0.3s; }
@keyframes dotPulse {
0%, 80%, 100% {
transform: scale(0.6);
opacity: 0.4;
}
40% {
transform: scale(1.2);
opacity: 1;
}
}
.loading-text {
color: var(--accent-purple);
font-size: 1.1rem;
font-weight: 500;
}
/* Divider */
.divider {
height: 1px;
background: linear-gradient(90deg, transparent, var(--border-color), rgba(139, 92, 246, 0.3), var(--border-color), transparent);
margin: 2.5rem 0;
position: relative;
}
.divider::after {
content: '';
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 8px;
height: 8px;
background: var(--accent-purple);
border-radius: 50%;
box-shadow: 0 0 10px var(--accent-purple);
}
/* Section Title */
.section-title {
text-align: center;
color: var(--text-primary);
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 1.5rem;
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
}
/* Expander */
.streamlit-expanderHeader,
[data-testid="stExpander"] details summary {
background: rgba(17, 17, 17, 0.8) !important;
border: 1px solid var(--border-color) !important;
border-radius: 12px !important;
color: var(--text-secondary) !important;
font-weight: 500 !important;
list-style: none !important;
}
[data-testid="stExpander"] details[open] summary {
border-radius: 12px 12px 0 0 !important;
}
/* Hide default arrow */
[data-testid="stExpander"] details summary::-webkit-details-marker,
[data-testid="stExpander"] details summary::marker {
display: none !important;
content: '' !important;
}
[data-testid="stExpander"] details summary svg {
fill: var(--text-muted) !important;
}
.streamlit-expanderContent,
[data-testid="stExpander"] details > div {
background: rgba(10, 10, 10, 0.9) !important;
border: 1px solid var(--border-color) !important;
border-top: none !important;
border-radius: 0 0 12px 12px !important;
}
/* Footer */
.footer {
text-align: center;
padding: 2.5rem 1rem;
margin-top: 3rem;
border-top: 1px solid var(--border-color);
}
.footer p {
color: var(--text-muted);
font-size: 0.9rem;
margin: 0.25rem 0;
}
.footer-brand {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
margin-top: 1rem;
color: var(--text-muted);
font-size: 0.85rem;
}
.footer-brand span {
background: linear-gradient(135deg, var(--accent-purple), var(--accent-pink));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-weight: 600;
}
/* Hide Streamlit Elements */
#MainMenu, footer, header { visibility: hidden; }
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-primary);
}
::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, var(--accent-purple), var(--accent-pink));
border-radius: 4px;
}
/* Alert Boxes */
.stAlert {
border-radius: 16px !important;
border: none !important;
}
div[data-testid="stAlert"] {
background: rgba(251, 191, 36, 0.1) !important;
border: 1px solid rgba(251, 191, 36, 0.2) !important;
border-radius: 16px !important;
}
</style>
""", unsafe_allow_html=True)
# ── Sidebar ──────────────────────────────────────────────────────────────────
with st.sidebar:
st.markdown("""
<div class="sidebar-header">
<div class="sidebar-logo">🛡️</div>
<h2 class="sidebar-title">Thai Fake News</h2>
<p class="sidebar-subtitle">AI Detection System</p>
</div>
""", unsafe_allow_html=True)
st.markdown("""
<p style="color: #8b5cf6; font-weight: 600; font-size: 0.85rem; margin-bottom: 0.75rem; text-transform: uppercase; letter-spacing: 1px;">Project Links</p>
<a href="https://huggingface.co/spaces/thirdExec/thai_fakenews_detector" target="_blank" style="text-decoration: none;">
<div class="link-card">
<div class="icon">🤗</div>
<span class="text">HF Space</span>
</div>
</a>
<a href="https://github.com/Thirdbot/FineTuneSloth" target="_blank" style="text-decoration: none;">
<div class="link-card">
<div class="icon">💻</div>
<span class="text">Training Code</span>
</div>
</a>
<a href="https://huggingface.co/thirdExec/Qwen2.5-1.5B-Instruct-ThaiFakeNews-bnb-4bit" target="_blank" style="text-decoration: none;">
<div class="link-card">
<div class="icon">🧠</div>
<span class="text">Model</span>
</div>
</a>
<a href="https://huggingface.co/datasets/EXt1/Thai-True-Fake-News" target="_blank" style="text-decoration: none;">
<div class="link-card">
<div class="icon">📊</div>
<span class="text">Dataset</span>
</div>
</a>
<div class="stats-card">
<div class="stat-label">Training Dataset</div>
<div class="stat-value">6,004</div>
<div class="stat-desc">Thai news articles (2017-2024)<br>from Antifakenewscenter Thailand</div>
</div>
""", unsafe_allow_html=True)
# ── Header ────────────────────────────────────────────────────────────────────
st.markdown('<h1 class="main-title">ตรวจสอบข่าวปลอม</h1>', unsafe_allow_html=True)
st.markdown('<p class="subtitle">Thai Fake News Detector · Powered by Qwen2.5-1.5B</p>', unsafe_allow_html=True)
st.markdown("""
<div class="info-box">
<p>
โมเดลนี้ใช้ <strong style="color: #8b5cf6;">Qwen2.5-1.5B</strong> ที่ผ่านการ fine-tune
บนชุดข้อมูลข่าวภาษาไทย 6,004 บทความ เพื่อจำแนกว่าข่าวนั้นเป็น
<strong style="color: #10b981;">ข่าวจริง</strong> หรือ
<strong style="color: #ef4444;">ข่าวปลอม</strong>
</p>
</div>
""", unsafe_allow_html=True)
# ── Input ─────────────────────────────────────────────────────────────────────
if "news_input" not in st.session_state:
st.session_state.news_input = ""
news_input = st.text_area(
"ใส่หัวข้อข่าวหรือเนื้อหาข่าวที่ต้องการตรวจสอบ",
placeholder="เช่น: รัฐบาลแจกเงินคนละ 10,000 บาท ผ่านแอปเป๋าตัง",
height=160,
key="news_input",
)
run = st.button("ตรวจสอบข่าว", type="primary", use_container_width=True)
# ── Inference ─────────────────────────────────────────────────────────────────
if run:
if not news_input.strip():
st.warning("กรุณาใส่ข้อความข่าวก่อนตรวจสอบ")
else:
loading_placeholder = st.empty()
loading_placeholder.markdown("""
<div class="loading-container">
<div class="loading-dots">
<div class="loading-dot"></div>
<div class="loading-dot"></div>
<div class="loading-dot"></div>
</div>
<p class="loading-text">กำลังวิเคราะห์ข่าว...</p>
</div>
""", unsafe_allow_html=True)
try:
client = InferenceClient(base_url=ENDPOINT_URL, token=os.environ.get("HF_TOKEN"))
response = client.text_generation(
news_input.strip(),
max_new_tokens=20,
temperature=0.05,
do_sample=True,
)
loading_placeholder.empty()
generated = response.split("### Response:")[-1].strip()
st.markdown('<div class="divider"></div>', unsafe_allow_html=True)
st.markdown('<div class="section-title">ผลการตรวจสอบ</div>', unsafe_allow_html=True)
if "จริง" in generated:
st.markdown("""
<div class="result-card result-real">
<div class="result-icon-wrapper">✓</div>
<h2 class="result-title">ข่าวจริง</h2>
<p class="result-desc">โมเดลประเมินว่าข่าวนี้มีแนวโน้มเป็น <strong>ข่าวจริง</strong></p>
</div>
""", unsafe_allow_html=True)
elif "ปลอม" in generated:
st.markdown("""
<div class="result-card result-fake">
<div class="result-icon-wrapper">✗</div>
<h2 class="result-title">ข่าวปลอม</h2>
<p class="result-desc">โมเดลประเมินว่าข่าวนี้มีแนวโน้มเป็น <strong>ข่าวปลอม</strong></p>
</div>
""", unsafe_allow_html=True)
else:
st.markdown("""
<div class="result-card result-unknown">
<div class="result-icon-wrapper">?</div>
<h2 class="result-title">ไม่สามารถวิเคราะห์ได้</h2>
<p class="result-desc">โมเดลไม่สามารถระบุได้ว่าเป็นข่าวจริงหรือข่าวปลอม</p>
</div>
""", unsafe_allow_html=True)
with st.expander("ดูผลลัพธ์ดิบจากโมเดล"):
st.code(response, language=None)
except Exception as exc:
loading_placeholder.empty()
st.error(f"เกิดข้อผิดพลาด: {exc}")
st.markdown("""
<div class="info-box" style="border-color: rgba(239, 68, 68, 0.3); background: linear-gradient(135deg, rgba(239, 68, 68, 0.1) 0%, rgba(127, 29, 29, 0.05) 100%);">
<p style="color: #fbbf24; font-weight: 600; margin-bottom: 0.75rem;">แนวทางแก้ไข:</p>
<p style="color: #a1a1aa;">
• ตรวจสอบการเชื่อมต่ออินเทอร์เน็ต<br>
• โมเดลอาจยังไม่พร้อมใช้งานผ่าน Inference API<br>
• ลองเปิด <a href="https://huggingface.co/spaces/thirdExec/thai_fakenews_detector" style="color: #8b5cf6;">HF Space</a> แทน
</p>
</div>
""", unsafe_allow_html=True)
# ── Footer ────────────────────────────────────────────────────────────────────
st.markdown("""
<div class="footer">
<p>ผลการตรวจสอบจากโมเดล AI อาจไม่ถูกต้องเสมอไป</p>
<p>กรุณายืนยันจากแหล่งข่าวที่น่าเชื่อถือก่อนเผยแพร่ต่อ</p>
<div class="footer-brand">
Made with <span>Thai Fake News Detection Team</span>
</div>
</div>
""", unsafe_allow_html=True)