noranisa's picture
Update templates/index.html
473078c verified
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SentiScope β€” Social Media Intelligence</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Cabinet+Grotesk:wght@400;500;700;800&family=JetBrains+Mono:wght@300;400;500&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #080a0f;
--s1: #0e1117;
--s2: #141820;
--b1: rgba(255,255,255,0.06);
--b2: rgba(255,255,255,0.10);
--text: #e8eaf0;
--muted: #4a5568;
--muted2: #8892a4;
--accent: #4f9cf9;
--a2: #7b61ff;
--green: #22c55e;
--display: 'Cabinet Grotesk', sans-serif;
--mono: 'JetBrains Mono', monospace;
}
html { scroll-behavior: smooth; }
body { font-family: var(--display); background: var(--bg); color: var(--text); min-height: 100vh; overflow-x: hidden; }
body::before {
content: '';
position: fixed; inset: 0;
background-image: linear-gradient(rgba(79,156,249,0.025) 1px, transparent 1px), linear-gradient(90deg, rgba(79,156,249,0.025) 1px, transparent 1px);
background-size: 64px 64px;
pointer-events: none; z-index: 0;
}
.orb { position: fixed; border-radius: 50%; filter: blur(130px); pointer-events: none; z-index: 0; }
.orb-1 { width: 600px; height: 600px; background: rgba(79,156,249,0.07); top: -250px; right: -150px; }
.orb-2 { width: 500px; height: 500px; background: rgba(123,97,255,0.06); bottom: 100px; left: -200px; }
/* NAV */
nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 1rem 3rem;
background: rgba(8,10,15,0.75);
backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px);
border-bottom: 1px solid var(--b1);
}
.nav-brand { display: flex; align-items: center; gap: 0.65rem; text-decoration: none; }
.brand-icon {
width: 30px; height: 30px; border-radius: 8px;
background: linear-gradient(135deg, var(--accent), var(--a2));
display: flex; align-items: center; justify-content: center;
font-weight: 800; font-size: 0.8rem; color: #fff;
}
.brand-name { font-size: 0.95rem; font-weight: 800; letter-spacing: -0.02em; color: var(--text); }
.brand-name b { color: var(--accent); font-weight: 800; }
nav ul { display: flex; gap: 2rem; list-style: none; }
nav ul a { font-family: var(--mono); font-size: 0.65rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted2); text-decoration: none; transition: color .2s; }
nav ul a:hover { color: var(--text); }
.nav-pill { font-family: var(--mono); font-size: 0.6rem; background: rgba(79,156,249,0.1); color: var(--accent); border: 1px solid rgba(79,156,249,0.22); padding: 0.25rem 0.65rem; border-radius: 20px; letter-spacing: 0.06em; }
/* HERO */
.hero {
position: relative; z-index: 1;
min-height: 100vh;
display: flex; flex-direction: column; align-items: center; justify-content: center;
padding: 8rem 2rem 5rem; text-align: center;
}
.eyebrow {
display: inline-flex; align-items: center; gap: 0.5rem;
font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.14em; text-transform: uppercase;
color: var(--accent); background: rgba(79,156,249,0.08); border: 1px solid rgba(79,156,249,0.18);
padding: 0.3rem 0.85rem; border-radius: 100px; margin-bottom: 2rem;
animation: fu 0.6s 0.1s both;
}
.eyebrow i { display: inline-block; width: 5px; height: 5px; background: var(--accent); border-radius: 50%; animation: blink 2s infinite; }
@keyframes blink { 0%,100%{opacity:1;} 50%{opacity:0.3;} }
h1.ht {
font-size: clamp(3rem, 7.5vw, 6.5rem);
font-weight: 800; line-height: 0.98; letter-spacing: -0.04em;
margin-bottom: 1.5rem; animation: fu 0.7s 0.2s both;
}
h1.ht .l1 { display: block; color: var(--text); }
h1.ht .l2 {
display: block;
background: linear-gradient(135deg, var(--accent) 20%, var(--a2) 80%);
-webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
}
h1.ht .l3 { display: block; color: var(--muted2); }
.hero-sub {
max-width: 520px; font-size: 0.95rem; line-height: 1.75; color: var(--muted2);
margin: 0 auto 2.5rem; animation: fu 0.7s 0.3s both;
}
/* FORM */
.form-box {
width: 100%; max-width: 540px;
background: var(--s1); border: 1px solid var(--b2); border-radius: 14px;
padding: 1.75rem; animation: fu 0.7s 0.4s both;
box-shadow: 0 4px 6px -1px rgba(0,0,0,.3), 0 32px 80px -12px rgba(0,0,0,.5), 0 0 50px rgba(79,156,249,0.06);
position: relative; overflow: hidden;
}
.form-box::after {
content:''; position:absolute; top:0;left:0;right:0; height:1px;
background: linear-gradient(90deg, transparent 5%, rgba(79,156,249,0.5) 50%, transparent 95%);
}
.f-row { display: grid; grid-template-columns: 1fr auto; gap: 0.6rem; margin-bottom: 0.75rem; }
.f-group { display: flex; flex-direction: column; gap: 0.35rem; }
.f-group label { font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }
.f-input {
font-family: var(--mono); font-size: 0.88rem;
background: var(--s2); border: 1px solid var(--b2); border-radius: 7px;
padding: 0.7rem 0.9rem; color: var(--text); outline: none; width: 100%;
transition: border-color .2s, box-shadow .2s;
}
.f-input::placeholder { color: var(--muted); }
.f-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(79,156,249,0.12); }
.src-lbl { font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); margin-bottom: 0.5rem; display: flex; align-items: center; justify-content: space-between; }
.src-lbl b { color: var(--accent); cursor: pointer; font-weight: 400; transition: opacity .2s; }
.src-lbl b:hover { opacity: 0.7; }
.src-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0.4rem; margin-bottom: 0.2rem; }
.src-item { position: relative; }
.src-item input[type="checkbox"] { position: absolute; opacity: 0; width: 0; height: 0; }
.src-item label {
display: flex; align-items: center; gap: 0.5rem;
background: var(--s2); border: 1px solid var(--b2); border-radius: 7px;
padding: 0.5rem 0.6rem; cursor: pointer;
transition: border-color .15s, background .15s;
user-select: none; min-height: 48px;
}
.src-item label:hover { filter: brightness(1.1); }
.src-item input:checked + label { border-color: var(--accent); background: rgba(79,156,249,0.1); }
.src-ico { font-size: 1rem; flex-shrink: 0; }
.src-info { display: flex; flex-direction: column; gap: 0.05rem; flex: 1; min-width: 0; }
.src-name { font-size: 0.7rem; font-weight: 700; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.src-sub { font-family: var(--mono); font-size: 0.48rem; letter-spacing: 0.04em; color: var(--muted); }
.src-box {
width: 13px; height: 13px; border-radius: 3px;
border: 1px solid var(--b2); flex-shrink: 0;
display: flex; align-items: center; justify-content: center;
transition: background .15s, border-color .15s;
}
.src-item input:checked + label .src-box { background: var(--accent); border-color: var(--accent); }
.src-item input:checked + label .src-box::after {
content: ''; display: block; width: 7px; height: 3.5px;
border-left: 1.5px solid #fff; border-bottom: 1.5px solid #fff;
transform: rotate(-45deg) translateY(-1px);
}
.src-warn { font-family: var(--mono); font-size: 0.55rem; color: #f59e0b; margin-top: 0.4rem; display: none; }
.src-warn.show { display: block; }
/* SLIDER */
.slider-wrap { display:flex; flex-direction:column; gap:0.35rem; margin-top:0.1rem; }
.slider-row { display:flex; align-items:center; gap:0.75rem; }
input[type="range"] { flex:1; -webkit-appearance:none; appearance:none; height:3px; background:var(--b2); border-radius:2px; outline:none; cursor:pointer; }
input[type="range"]::-webkit-slider-thumb { -webkit-appearance:none; width:14px; height:14px; border-radius:50%; background:linear-gradient(135deg,var(--accent),var(--a2)); cursor:pointer; transition:transform .15s; }
input[type="range"]::-webkit-slider-thumb:hover { transform:scale(1.2); }
.slider-val { font-family:var(--mono); font-size:0.72rem; font-weight:500; color:var(--accent); min-width:2.5rem; text-align:right; }
/* MULTI-KEYWORD */
.kw-tags { display:flex; flex-wrap:wrap; gap:0.35rem; margin-top:0.4rem; min-height:1.5rem; }
.kw-tag { display:inline-flex; align-items:center; gap:0.3rem; background:rgba(79,156,249,0.1); border:1px solid rgba(79,156,249,0.25); border-radius:5px; padding:0.15rem 0.5rem; font-family:var(--mono); font-size:0.62rem; color:var(--accent); }
.kw-tag button { background:none; border:none; color:var(--accent); cursor:pointer; font-size:0.7rem; line-height:1; padding:0; opacity:0.7; }
.kw-tag button:hover { opacity:1; }
.kw-add-hint { font-family:var(--mono); font-size:0.55rem; color:var(--muted); }
.btn-go {
align-self: flex-end;
background: linear-gradient(135deg, var(--accent), var(--a2));
color: #fff; border: none; border-radius: 7px;
padding: 0.7rem 1.3rem;
font-family: var(--display); font-size: 0.85rem; font-weight: 700;
cursor: pointer; display: flex; align-items: center; gap: 0.35rem;
transition: opacity .2s, transform .15s, box-shadow .2s;
white-space: nowrap;
box-shadow: 0 4px 20px rgba(79,156,249,0.28);
}
.btn-go:hover { opacity: 0.88; transform: translateY(-1px); box-shadow: 0 8px 28px rgba(79,156,249,0.38); }
.btn-go:active { transform: translateY(0); }
.f-hint { font-family: var(--mono); font-size: 0.58rem; color: var(--muted); margin-top: 0.65rem; }
.f-hint b { color: var(--muted2); }
/* STATS */
.stats {
display: flex; gap: 0; margin-top: 2.5rem;
background: var(--s1); border: 1px solid var(--b2); border-radius: 10px; overflow: hidden;
animation: fu 0.7s 0.5s both;
}
.stat {
flex: 1; padding: 1rem 1.5rem;
display: flex; flex-direction: column; gap: 0.2rem; align-items: center;
border-right: 1px solid var(--b1); transition: background .2s;
}
.stat:last-child { border-right: none; }
.stat:hover { background: var(--s2); }
.stat-n {
font-size: 1.4rem; font-weight: 800; letter-spacing: -0.03em;
background: linear-gradient(135deg, var(--text), var(--muted2));
-webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
}
.stat-l { font-family: var(--mono); font-size: 0.55rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); }
/* SCROLL */
.scroll-down {
position: absolute; bottom: 2.5rem; left: 50%; transform: translateX(-50%);
display: flex; flex-direction: column; align-items: center; gap: 0.4rem;
animation: fu 1s 0.8s both;
}
.scroll-down span { font-family: var(--mono); font-size: 0.5rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--muted); }
.scroll-mouse { width: 18px; height: 26px; border: 1.5px solid var(--b2); border-radius: 9px; display: flex; align-items: flex-start; justify-content: center; padding-top: 4px; }
.scroll-dot { width: 2px; height: 5px; background: var(--accent); border-radius: 1px; animation: sd 1.5s infinite; }
@keyframes sd { 0%{opacity:1;transform:translateY(0)} 100%{opacity:0;transform:translateY(7px)} }
/* FEATURES */
.sec { position: relative; z-index: 1; max-width: 1200px; margin: 0 auto; padding: 5rem 3rem; }
.sec-tag { font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--accent); margin-bottom: 0.75rem; }
.sec-h { font-size: 2.4rem; font-weight: 800; letter-spacing: -0.03em; line-height: 1.1; margin-bottom: 3rem; }
.sec-h em { font-style: normal; color: var(--muted2); }
.feat-grid {
display: grid; grid-template-columns: repeat(4, 1fr);
gap: 1px; background: var(--b1); border: 1px solid var(--b1); border-radius: 12px; overflow: hidden;
}
.feat {
background: var(--s1); padding: 1.75rem 1.5rem;
display: flex; flex-direction: column; gap: 0.9rem;
transition: background .2s;
opacity: 0; transform: translateY(16px); transition: opacity .45s, transform .45s, background .2s;
}
.feat.vis { opacity: 1; transform: translateY(0); }
.feat:hover { background: var(--s2); }
.feat-ico {
width: 38px; height: 38px; border-radius: 9px;
display: flex; align-items: center; justify-content: center; font-size: 1rem;
}
.ic-b { background: rgba(79,156,249,0.1); }
.ic-p { background: rgba(123,97,255,0.1); }
.ic-g { background: rgba(34,197,94,0.1); }
.ic-a { background: rgba(245,158,11,0.1); }
.feat-name { font-size: 0.88rem; font-weight: 700; }
.feat-desc { font-family: var(--mono); font-size: 0.65rem; line-height: 1.7; color: var(--muted2); }
/* PIPELINE */
.pipe-wrap {
display: grid; grid-template-columns: repeat(5, 1fr);
position: relative; gap: 0;
}
.pipe-wrap::before {
content: ''; position: absolute; top: 27px; left: 9%; right: 9%; height: 1px;
background: linear-gradient(90deg, transparent, var(--b2), rgba(79,156,249,0.4), var(--b2), transparent);
}
.pipe {
display: flex; flex-direction: column; align-items: center; gap: 0.9rem;
padding: 0 0.75rem; text-align: center;
}
.pipe-circle {
width: 54px; height: 54px; border-radius: 50%;
background: var(--s2); border: 1px solid var(--b2);
display: flex; align-items: center; justify-content: center;
font-family: var(--mono); font-size: 0.7rem; color: var(--accent);
position: relative; z-index: 1; transition: background .2s, border-color .2s;
}
.pipe:hover .pipe-circle { background: rgba(79,156,249,0.1); border-color: var(--accent); }
.pipe-name { font-size: 0.8rem; font-weight: 700; }
.pipe-info { font-family: var(--mono); font-size: 0.6rem; color: var(--muted2); line-height: 1.5; }
/* TECH */
.tech-band {
position: relative; z-index: 1;
border-top: 1px solid var(--b1); border-bottom: 1px solid var(--b1);
padding: 1.5rem 3rem; display: flex; align-items: center; gap: 2rem; flex-wrap: wrap;
}
.tech-lbl { font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--muted); white-space: nowrap; }
.tech-chips { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.chip {
font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.04em;
padding: 0.25rem 0.65rem; border-radius: 5px; border: 1px solid var(--b2);
color: var(--muted2); background: var(--s1); transition: border-color .2s, color .2s;
white-space: nowrap;
}
.chip:hover { border-color: var(--accent); color: var(--accent); }
.chip.hi { border-color: rgba(79,156,249,0.28); color: var(--accent); background: rgba(79,156,249,0.06); }
/* FOOTER */
footer {
position: relative; z-index: 1;
padding: 1.75rem 3rem;
display: flex; align-items: center; justify-content: space-between;
border-top: 1px solid var(--b1);
}
.footer-l { display: flex; align-items: center; gap: 0.6rem; }
.footer-copy { font-family: var(--mono); font-size: 0.6rem; color: var(--muted); letter-spacing: 0.04em; }
.footer-links { display: flex; gap: 1.5rem; }
.footer-links a { font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); text-decoration: none; transition: color .2s; }
.footer-links a:hover { color: var(--text); }
/* LOADING */
#loading {
display: none; position: fixed; inset: 0;
background: rgba(8,10,15,0.94); z-index: 500;
flex-direction: column; align-items: center; justify-content: center; gap: 1.75rem;
backdrop-filter: blur(12px);
}
#loading.on { display: flex; }
.ld-icon {
width: 54px; height: 54px; border-radius: 13px;
background: linear-gradient(135deg, var(--accent), var(--a2));
display: flex; align-items: center; justify-content: center;
font-weight: 800; font-size: 1.4rem; color: #fff;
animation: breathe 2s ease-in-out infinite;
}
@keyframes breathe { 0%,100%{transform:scale(1)} 50%{transform:scale(1.06)} }
.ld-title { font-size: 1rem; font-weight: 700; color: var(--text); letter-spacing: -0.02em; }
.ld-step { font-family: var(--mono); font-size: 0.68rem; color: var(--muted2); transition: opacity .3s; }
.ld-bar { width: 260px; height: 2px; background: var(--s2); border-radius: 1px; overflow: hidden; }
.ld-fill { height: 100%; background: linear-gradient(90deg, var(--accent), var(--a2)); animation: sweep 1.8s ease-in-out infinite; border-radius: 1px; }
@keyframes sweep { 0%{transform:translateX(-100%);width:35%} 50%{width:55%} 100%{transform:translateX(280%);width:35%} }
/* ANIM */
@keyframes fu { from{opacity:0;transform:translateY(18px)} to{opacity:1;transform:translateY(0)} }
/* RESPONSIVE */
@media(max-width:900px){
nav { padding: 1rem 1.5rem; }
nav ul { display: none; }
.hero { padding: 6rem 1.5rem 4rem; }
h1.ht { font-size: 2.8rem; }
.f-row { grid-template-columns: 1fr; }
.btn-go { width: 100%; justify-content: center; }
.sec { padding: 3.5rem 1.5rem; }
.feat-grid { grid-template-columns: 1fr 1fr; }
.pipe-wrap { grid-template-columns: 1fr 1fr; gap: 1.5rem; }
.pipe-wrap::before { display: none; }
.tech-band { padding: 1.2rem 1.5rem; }
footer { padding: 1.5rem; flex-direction: column; gap: 1rem; align-items: flex-start; }
}
</style>
</head>
<body>
<div class="orb orb-1"></div>
<div class="orb orb-2"></div>
<!-- LOADING -->
<div id="loading">
<div class="ld-icon">S</div>
<div class="ld-title">Sedang menganalisis…</div>
<div class="ld-step" id="ld-step">Menghubungkan ke sumber data</div>
<div class="ld-bar"><div class="ld-fill"></div></div>
</div>
<!-- NAV -->
<nav>
<a href="/" class="nav-brand">
<div class="brand-icon">S</div>
<span class="brand-name">Senti<b>Scope</b></span>
</a>
<ul>
<li><a href="#fitur">Fitur</a></li>
<li><a href="#pipeline">Pipeline</a></li>
<li><a href="#stack">Stack</a></li>
</ul>
<span class="nav-pill">v2.0 Β· IndoBERT</span>
</nav>
<!-- HERO -->
<section class="hero">
<div class="eyebrow"><i></i> Platform Analisis Sentimen Media Sosial</div>
<h1 class="ht">
<span class="l1">Petakan Opini</span>
<span class="l2">Publik Digital</span>
<span class="l3">Secara Real-time</span>
</h1>
<p class="hero-sub">
Analisis sentimen berbasis IndoBERT &amp; RoBERTa untuk YouTube, Reddit, Instagram, TikTok, dan Google News β€”
dilengkapi deteksi bot, topic modeling LDA, dan visualisasi jaringan GNN.
</p>
<div class="form-box">
<div class="f-row">
<div class="f-group">
<label for="keyword">Kata Kunci / Topik</label>
<input type="text" id="keyword" class="f-input" placeholder="IKN, pemilu, BBM subsidi…" autocomplete="off">
</div>
<button class="btn-go" onclick="go()">Analisis β†’</button>
</div>
<div class="f-group">
<div class="src-lbl">
<span>Sumber Data</span>
<b onclick="toggleAll()">Pilih Semua</b>
</div>
<div class="src-grid">
<div class="src-item">
<input type="checkbox" id="src-yt" value="youtube" checked>
<label for="src-yt">
<span class="src-ico">β–Ά</span>
<span class="src-info"><span class="src-name">YouTube</span><span class="src-sub">Komentar video</span></span>
<span class="src-box"></span>
</label>
</div>
<div class="src-item">
<input type="checkbox" id="src-rd" value="reddit" checked>
<label for="src-rd">
<span class="src-ico">πŸ‘Ύ</span>
<span class="src-info"><span class="src-name">Reddit</span><span class="src-sub">Komentar thread</span></span>
<span class="src-box"></span>
</label>
</div>
<div class="src-item">
<input type="checkbox" id="src-ig" value="instagram">
<label for="src-ig">
<span class="src-ico">πŸ“Έ</span>
<span class="src-info"><span class="src-name">Instagram</span><span class="src-sub">via Apify</span></span>
<span class="src-box"></span>
</label>
</div>
<div class="src-item">
<input type="checkbox" id="src-tt" value="tiktok">
<label for="src-tt">
<span class="src-ico">🎡</span>
<span class="src-info"><span class="src-name">TikTok</span><span class="src-sub">via Apify</span></span>
<span class="src-box"></span>
</label>
</div>
<div class="src-item">
<input type="checkbox" id="src-nw" value="news">
<label for="src-nw">
<span class="src-ico">πŸ“°</span>
<span class="src-info"><span class="src-name">Google News</span><span class="src-sub">via SerpApi</span></span>
<span class="src-box"></span>
</label>
</div>
</div>
<p class="src-warn" id="src-warn">⚠ Pilih minimal satu sumber data</p>
</div>
<div class="kw-tags" id="kw-tags"></div>
<p class="kw-add-hint">Tekan <b>Enter</b> atau <b>,</b> untuk tambah keyword Β· Maks. 3 keyword</p>
<div class="slider-wrap" style="margin-top:0.75rem">
<label style="font-family:var(--mono);font-size:0.58rem;letter-spacing:0.14em;text-transform:uppercase;color:var(--muted)">Confidence Threshold</label>
<div class="slider-row">
<input type="range" id="conf-slider" min="0.3" max="0.9" step="0.05" value="0.6">
<span class="slider-val" id="conf-val">0.60</span>
</div>
</div>
<p class="f-hint" style="margin-top:0.5rem">Prediksi di bawah threshold akan ditandai <b>Uncertain</b></p>
</div>
<div class="stats">
<div class="stat"><span class="stat-n">9+</span><span class="stat-l">Analisis</span></div>
<div class="stat"><span class="stat-n">5</span><span class="stat-l">Sumber</span></div>
<div class="stat"><span class="stat-n">3</span><span class="stat-l">Model AI</span></div>
<div class="stat"><span class="stat-n">ID</span><span class="stat-l">Bahasa</span></div>
</div>
<div class="scroll-down">
<span>Scroll</span>
<div class="scroll-mouse"><div class="scroll-dot"></div></div>
</div>
</section>
<!-- FEATURES -->
<section class="sec" id="fitur">
<p class="sec-tag">Kapabilitas</p>
<h2 class="sec-h">Analisis multidimensional <em>dalam satu platform</em></h2>
<div class="feat-grid">
<div class="feat">
<div class="feat-ico ic-b">🧠</div>
<div class="feat-name">Sentimen NLP</div>
<p class="feat-desc">Klasifikasi positif / negatif / netral menggunakan transformer IndoBERT fine-tuned pada data bahasa Indonesia.</p>
</div>
<div class="feat">
<div class="feat-ico ic-p">πŸ•Έ</div>
<div class="feat-name">Deteksi Bot</div>
<p class="feat-desc">Identifikasi akun tidak otentik via cosine similarity dan graph centrality dengan threshold adaptif berbasis GNN.</p>
</div>
<div class="feat">
<div class="feat-ico ic-g">πŸ“</div>
<div class="feat-name">Topic Modeling</div>
<p class="feat-desc">Ekstraksi topik laten dari komentar menggunakan LDA dengan 3 komponen, disajikan sebagai word cluster.</p>
</div>
<div class="feat">
<div class="feat-ico ic-a">πŸ“ˆ</div>
<div class="feat-name">Prediksi Tren</div>
<p class="feat-desc">Proyeksi arah sentimen menggunakan regresi linier pada urutan data masuk untuk melihat tren publik.</p>
</div>
</div>
</section>
<!-- PIPELINE -->
<section class="sec" id="pipeline" style="padding-top:0">
<p class="sec-tag">Cara Kerja</p>
<h2 class="sec-h">Pipeline <em>end-to-end</em> otomatis</h2>
<div class="pipe-wrap">
<div class="pipe"><div class="pipe-circle">01</div><div class="pipe-name">Input</div><div class="pipe-info">Kata kunci &amp; pilih sumber data</div></div>
<div class="pipe"><div class="pipe-circle">02</div><div class="pipe-name">Scraping</div><div class="pipe-info">YouTube, Reddit, Instagram, TikTok &amp; Google News</div></div>
<div class="pipe"><div class="pipe-circle">03</div><div class="pipe-name">Preprocessing</div><div class="pipe-info">Cleaning, normalisasi, stopword removal</div></div>
<div class="pipe"><div class="pipe-circle">04</div><div class="pipe-name">Inference</div><div class="pipe-info">IndoBERT + GNN + LDA secara paralel</div></div>
<div class="pipe"><div class="pipe-circle">05</div><div class="pipe-name">Dashboard</div><div class="pipe-info">Visualisasi interaktif + ekspor CSV</div></div>
</div>
</section>
<!-- TECH -->
<div class="tech-band" id="stack">
<span class="tech-lbl">Tech Stack</span>
<div class="tech-chips">
<span class="chip hi">IndoBERT</span>
<span class="chip hi">RoBERTa-ID</span>
<span class="chip hi">GNN</span>
<span class="chip">Flask</span>
<span class="chip">PyTorch</span>
<span class="chip">scikit-learn</span>
<span class="chip">NetworkX</span>
<span class="chip">LDA</span>
<span class="chip">YouTube API</span>
<span class="chip">Reddit PRAW</span>
<span class="chip hi">Apify Instagram</span>
<span class="chip hi">Apify TikTok</span>
<span class="chip hi">SerpApi News</span>
<span class="chip">HuggingFace Spaces</span>
<span class="chip">Docker</span>
</div>
</div>
<!-- FOOTER -->
<footer>
<div class="footer-l">
<div class="brand-icon" style="width:22px;height:22px;font-size:0.65rem;border-radius:5px">S</div>
<span class="footer-copy">SentiScope &copy; 2025 β€” Social Media Intelligence Platform</span>
</div>
<div class="footer-links">
<a href="/">Home</a>
<a href="/download">Download CSV</a>
</div>
</footer>
<script>
const STEPS = [
'Menghubungkan ke sumber data…',
'Mengambil komentar YouTube…',
'Mengambil thread Reddit…',
'Menjalankan model IndoBERT…',
'Menghitung topic modeling LDA…',
'Menganalisis jaringan bot…',
'Menyusun hasil visualisasi…',
];
let si = 0, st;
function tick() {
si = (si + 1) % STEPS.length;
const el = document.getElementById('ld-step');
el.style.opacity = 0;
setTimeout(() => { el.textContent = STEPS[si]; el.style.opacity = 1; }, 250);
st = setTimeout(tick, 2000);
}
function getSelectedSources() {
const boxes = document.querySelectorAll('.src-item input[type="checkbox"]:checked');
const vals = Array.from(boxes).map(b => b.value);
if (vals.length === 0) return null;
if (vals.length === 5) return 'all';
return vals.join(',');
}
function toggleAll() {
const boxes = document.querySelectorAll('.src-item input[type="checkbox"]');
const anyUnchecked = Array.from(boxes).some(b => !b.checked);
boxes.forEach(b => { b.checked = anyUnchecked; });
const btn = document.querySelector('.src-lbl b');
btn.textContent = anyUnchecked ? 'Batal Semua' : 'Pilih Semua';
}
// sync toggle-all label
document.addEventListener('change', () => {
const boxes = document.querySelectorAll('.src-item input[type="checkbox"]');
const btn = document.querySelector('.src-lbl b');
const allOn = Array.from(boxes).every(b => b.checked);
btn.textContent = allOn ? 'Batal Semua' : 'Pilih Semua';
document.getElementById('src-warn').classList.remove('show');
});
// ── Multi-keyword management ──
const kwTags = [];
document.getElementById('keyword').addEventListener('keydown', function(e) {
if ((e.key === 'Enter' || e.key === ',') && this.value.trim()) {
e.preventDefault();
addKeyword(this.value.trim().replace(',',''));
this.value = '';
}
});
function addKeyword(kw) {
if (!kw || kwTags.includes(kw) || kwTags.length >= 3) return;
kwTags.push(kw);
renderTags();
}
function removeKeyword(kw) {
const idx = kwTags.indexOf(kw);
if (idx > -1) kwTags.splice(idx, 1);
renderTags();
}
function renderTags() {
const container = document.getElementById('kw-tags');
container.innerHTML = kwTags.map(k =>
`<span class="kw-tag">${k}<button onclick="removeKeyword('${k}')">Γ—</button></span>`
).join('');
}
// ── Confidence slider ──
document.getElementById('conf-slider').addEventListener('input', function() {
document.getElementById('conf-val').textContent = parseFloat(this.value).toFixed(2);
});
async function go() {
const inputKw = document.getElementById('keyword').value.trim();
if (inputKw) addKeyword(inputKw);
document.getElementById('keyword').value = '';
const allKw = kwTags.length ? kwTags : [inputKw];
const kw = allKw.filter(Boolean).join(',');
const src = getSelectedSources();
const confTh = parseFloat(document.getElementById('conf-slider').value);
if (!kw) {
const inp = document.getElementById('keyword');
inp.style.borderColor = '#ef4444';
inp.focus();
setTimeout(() => inp.style.borderColor = '', 1500);
return;
}
if (!src) {
document.getElementById('src-warn').classList.add('show');
return;
}
document.getElementById('loading').classList.add('on');
document.getElementById('ld-step').textContent = STEPS[0];
st = setTimeout(tick, 2000);
try {
const r = await fetch('/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ keyword: kw, source: src, conf_threshold: confTh })
});
const d = await r.json();
clearTimeout(st);
document.getElementById('loading').classList.remove('on');
sessionStorage.setItem('analysisResult', JSON.stringify({ keyword: kw, source: src, ...d }));
window.location.href = '/result';
} catch(e) {
clearTimeout(st);
document.getElementById('loading').classList.remove('on');
alert('Terjadi kesalahan. Periksa koneksi dan konfigurasi API key.');
}
}
document.getElementById('keyword').addEventListener('keydown', e => { if (e.key === 'Enter') go(); });
// Feature reveal
const feats = document.querySelectorAll('.feat');
const io = new IntersectionObserver(entries => {
entries.forEach((e, i) => { if (e.isIntersecting) setTimeout(() => e.target.classList.add('vis'), i * 80); });
}, { threshold: 0.1 });
feats.forEach(f => io.observe(f));
</script>
</body>
</html>