trend-hunter-ai / dashboard.html
agenticworkflowsspace's picture
Upload dashboard.html with huggingface_hub
4de1da0 verified
<!DOCTYPE html>
<html class="light" lang="en">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>Trend Hunter AI | Multi-Platform Intelligence</title>
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600;700&family=Fira+Sans:wght@400;500;600&display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<script>
tailwind.config = {
darkMode:"class",
theme:{extend:{
colors:{"on-tertiary-container":"#006127","secondary-dim":"#0046bb","outline-variant":"#a5adc6","on-background":"#272e42","surface-tint":"#b90035","on-secondary":"#f1f2ff","background":"#f6f6ff","tertiary-container":"#82ff99","surface-container-highest":"#d1dcff","on-primary-fixed":"#000000","secondary-container":"#c4d0ff","on-secondary-container":"#003ea9","on-secondary-fixed-variant":"#0047bd","surface-dim":"#c7d4fa","outline":"#6f768e","on-error":"#ffefec","on-primary-container":"#4e0011","on-tertiary-fixed-variant":"#006c2d","primary-dim":"#a2002d","inverse-surface":"#060e20","error":"#b02500","on-secondary-fixed":"#002d80","inverse-primary":"#ff5168","tertiary":"#006a2c","surface-variant":"#d1dcff","on-primary":"#ffefef","on-surface-variant":"#535b71","surface-bright":"#f6f6ff","on-tertiary":"#cfffcf","secondary-fixed":"#c4d0ff","on-surface":"#272e42","tertiary-dim":"#005d25","error-container":"#f95630","primary":"#b90035","surface-container":"#e2e7ff","surface-container-lowest":"#ffffff","inverse-on-surface":"#959cb5","on-primary-fixed-variant":"#5f0017","on-error-container":"#520c00","tertiary-fixed":"#82ff99","on-tertiary-fixed":"#004c1d","surface-container-low":"#eef0ff","primary-container":"#ff7481","secondary":"#0050d4","surface":"#f6f6ff","surface-container-high":"#d9e2ff"},
fontFamily:{"mono":["Fira Code","monospace"],"sans":["Fira Sans","sans-serif"],"headline":["Fira Code","monospace"],"body":["Fira Sans","sans-serif"]},
borderRadius:{"DEFAULT":"0.125rem","lg":"0.25rem","xl":"0.5rem","2xl":"1rem","full":"9999px"},
},},
}
</script>
<style>
.material-symbols-outlined{font-variation-settings:'FILL' 0,'wght' 400,'GRAD' 0,'opsz' 24;vertical-align:middle;}
body{font-family:'Fira Sans',sans-serif;background-color:#f6f6ff;color:#272e42;}
.fira-code{font-family:'Fira Code',monospace;}
.platform-btn-active{background-color:#0050d4!important;color:white!important;box-shadow:0 10px 15px -3px rgba(0,80,212,0.2);}
.platform-btn-inactive{background-color:#d9e2ff;color:#535b71;}
.hidden-section{display:none;}
.video-card{display:block;text-decoration:none;color:inherit;background:white;border-radius:1rem;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,0.08);transition:all 200ms ease;border:1px solid #eef0ff;}
.video-card:hover{transform:translateY(-3px);box-shadow:0 10px 20px rgba(0,0,0,0.1);}
.thumb-overlay{position:absolute;inset:0;background:rgba(0,0,0,0.35);display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity 200ms;}
.video-card:hover .thumb-overlay{opacity:1;}
.platform-tab{cursor:pointer;padding:10px 18px;border-radius:10px;font-weight:600;font-size:0.85rem;transition:all 200ms;color:#535b71;}
.platform-tab.active{background:#0050d4;color:white;box-shadow:0 4px 12px rgba(0,80,212,0.25);}
.platform-pane{display:none;}
.platform-pane.active{display:block;}
.score-bar-fill{height:100%;border-radius:9999px;background:linear-gradient(90deg,#b90035,#ff7481);transition:width 1s ease;}
.idea-card{background:white;border:1px solid #eef0ff;border-left:4px solid #0050d4;border-radius:0.75rem;padding:14px 18px;font-size:0.9rem;cursor:pointer;transition:all 200ms;}
.idea-card:hover{background:#eef0ff;transform:translateX(4px);}
.history-item{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;border-radius:8px;cursor:pointer;transition:background 150ms;font-size:0.85rem;}
.history-item:hover{background:#eef0ff;}
.toast{position:fixed;bottom:24px;right:24px;background:#272e42;color:white;padding:12px 20px;border-radius:10px;font-size:0.85rem;font-family:'Fira Code',monospace;z-index:1000;opacity:0;transform:translateY(10px);transition:all 300ms;}
.toast.show{opacity:1;transform:translateY(0);}
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
.skeleton{background:linear-gradient(90deg,#e2e7ff 25%,#d9e2ff 50%,#e2e7ff 75%);background-size:200% 100%;animation:shimmer 1.5s infinite;border-radius:8px;}
</style>
</head>
<body class="bg-background selection:bg-primary-container selection:text-on-primary-container">
<!-- Toast -->
<div id="toast" class="toast"></div>
<!-- Nav -->
<nav class="fixed top-4 left-4 right-4 z-50 flex justify-between items-center px-6 py-3 max-w-7xl mx-auto bg-white/90 backdrop-blur-xl rounded-2xl shadow-lg shadow-rose-500/5 border border-slate-200/50">
<div class="flex items-center gap-2 text-xl font-bold fira-code text-rose-600">
<span class="material-symbols-outlined" style="font-variation-settings:'FILL' 1;">insights</span>Trend Hunter
</div>
<div class="flex items-center gap-3">
<div id="engine-badge" class="hidden md:flex items-center gap-2 px-3 py-1 bg-tertiary-container/30 text-on-tertiary-container rounded-full text-xs font-mono font-bold border border-tertiary/20">
<span class="w-2 h-2 rounded-full bg-green-500 inline-block"></span> Engine Online
</div>
<button onclick="toggleHistory()" class="material-symbols-outlined text-slate-600 hover:bg-rose-50 p-2 rounded-xl transition-all duration-200 cursor-pointer" title="Search History">history</button>
</div>
</nav>
<!-- History Drawer -->
<div id="history-drawer" class="hidden fixed top-20 right-4 z-40 w-80 bg-white rounded-2xl shadow-2xl border border-slate-200 p-4 max-h-96 overflow-y-auto">
<div class="flex justify-between items-center mb-3">
<span class="fira-code font-bold text-sm text-on-background">Search History</span>
<button onclick="clearHistory()" class="text-xs text-primary hover:underline cursor-pointer">Clear All</button>
</div>
<div id="history-list"><p class="text-xs text-outline text-center py-4">No searches yet.</p></div>
</div>
<main class="pt-32 pb-20 px-4 max-w-7xl mx-auto space-y-16">
<!-- Hero -->
<section class="text-center space-y-8">
<div class="space-y-4">
<h1 class="fira-code text-4xl md:text-6xl font-bold tracking-tight text-on-background">
What's <span class="text-primary italic">Trending</span> Right Now?
</h1>
<p class="max-w-2xl mx-auto text-on-surface-variant text-lg md:text-xl font-medium">
Autonomous AI research across 4 major social networks to find your next viral niche in seconds.
</p>
</div>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 max-w-4xl mx-auto">
<div class="bg-surface-container-lowest p-6 rounded-2xl border-b-4 border-primary/20 shadow-sm flex flex-col items-center justify-center space-y-1">
<span class="fira-code text-3xl font-bold text-primary">2.4M+</span>
<span class="font-mono text-[10px] uppercase tracking-widest text-outline">Data Points</span>
</div>
<div class="bg-surface-container-lowest p-6 rounded-2xl border-b-4 border-secondary/20 shadow-sm flex flex-col items-center justify-center space-y-1">
<span class="fira-code text-3xl font-bold text-secondary">842</span>
<span class="font-mono text-[10px] uppercase tracking-widest text-outline">Niches Found</span>
</div>
<div class="bg-surface-container-lowest p-6 rounded-2xl border-b-4 border-tertiary/20 shadow-sm flex flex-col items-center justify-center space-y-1">
<span class="fira-code text-3xl font-bold text-tertiary">4</span>
<span class="font-mono text-[10px] uppercase tracking-widest text-outline">Platforms</span>
</div>
<div class="bg-surface-container-lowest p-6 rounded-2xl border-b-4 border-outline/20 shadow-sm flex flex-col items-center justify-center space-y-1">
<span class="fira-code text-3xl font-bold text-on-background">v2.0</span>
<span class="font-mono text-[10px] uppercase tracking-widest text-outline">Engine</span>
</div>
</div>
</section>
<!-- Research Control Panel -->
<section class="max-w-[860px] mx-auto">
<div class="bg-surface-container-lowest rounded-2xl shadow-xl border-t-[4px] border-primary p-8 md:p-10 space-y-8">
<!-- Mode Toggle -->
<div class="flex gap-3 p-1 bg-surface-container-high rounded-xl">
<button id="mode-niche" onclick="setMode('niche')" class="flex-1 py-2 px-4 fira-code text-sm font-bold rounded-lg bg-white shadow-sm text-on-background transition-all cursor-pointer">
<span class="material-symbols-outlined text-sm">search</span> Research a Niche
</button>
<button id="mode-discover" onclick="setMode('discover')" class="flex-1 py-2 px-4 fira-code text-sm font-bold rounded-lg text-on-surface-variant transition-all cursor-pointer">
<span class="material-symbols-outlined text-sm">local_fire_department</span> Discover Trends Now
</button>
</div>
<!-- Niche Input -->
<div id="niche-panel">
<label class="block">
<span class="fira-code text-sm font-bold text-on-surface-variant mb-2 block uppercase tracking-wider">Target Niche or Keyword</span>
<div class="relative group">
<span class="absolute left-4 top-1/2 -translate-y-1/2 material-symbols-outlined text-outline group-focus-within:text-secondary transition-colors">search</span>
<input id="niche-input" class="w-full bg-surface-container-high border-none rounded-xl py-4 pl-12 pr-4 fira-code text-on-surface focus:ring-2 focus:ring-secondary transition-all outline-none" placeholder="e.g. Agentic AI, Sustainable Fashion, Web3..." type="text"/>
</div>
</label>
</div>
<!-- Discovery Mode Message -->
<div id="discover-panel" class="hidden-section">
<div class="bg-tertiary-container/20 border border-tertiary/20 rounded-xl p-5 text-center space-y-2">
<span class="material-symbols-outlined text-3xl text-tertiary" style="font-variation-settings:'FILL' 1;">local_fire_department</span>
<p class="fira-code font-bold text-on-background">Discover Mode Active</p>
<p class="text-sm text-on-surface-variant">We'll scan YouTube's global trending chart and identify hot niches for you — no keyword needed.</p>
</div>
</div>
<!-- Platform Chips -->
<div id="platform-section">
<div class="flex items-center justify-between mb-3">
<span class="fira-code text-sm font-bold text-on-surface-variant uppercase tracking-wider">Platform Synthesis Focus</span>
<div class="flex gap-3">
<button onclick="selectAll()" class="text-secondary text-xs font-mono font-bold hover:underline cursor-pointer">Select All</button>
<button onclick="clearAll()" class="text-outline text-xs font-mono font-bold hover:underline cursor-pointer">Clear</button>
</div>
</div>
<div class="flex flex-wrap gap-3">
<button onclick="togglePlatform('youtube',this)" class="platform-btn platform-btn-active flex items-center gap-2 px-4 py-2 rounded-xl font-medium transition-all hover:translate-y-[-2px] active:scale-95 cursor-pointer">
<span class="material-symbols-outlined text-sm">smart_display</span> YouTube
</button>
<button onclick="togglePlatform('tiktok',this)" class="platform-btn platform-btn-active flex items-center gap-2 px-4 py-2 rounded-xl font-medium transition-all hover:translate-y-[-2px] active:scale-95 cursor-pointer">
<span class="material-symbols-outlined text-sm">bolt</span> TikTok
</button>
<button onclick="togglePlatform('instagram',this)" class="platform-btn platform-btn-active flex items-center gap-2 px-4 py-2 rounded-xl font-medium transition-all hover:translate-y-[-2px] active:scale-95 cursor-pointer">
<span class="material-symbols-outlined text-sm">photo_camera</span> Instagram
</button>
<button onclick="togglePlatform('twitter',this)" class="platform-btn platform-btn-active flex items-center gap-2 px-4 py-2 rounded-xl font-medium transition-all hover:translate-y-[-2px] active:scale-95 cursor-pointer">
<span class="material-symbols-outlined text-sm">close</span> X /Twitter
</button>
</div>
<div class="flex justify-between items-center text-[11px] font-mono text-outline uppercase tracking-tight px-1 mt-2">
<span>* High-intensity scanning enabled</span>
<span id="platform-count" class="text-secondary font-bold">4 platforms selected</span>
</div>
</div>
<button id="run-btn" onclick="startResearch()" class="w-full py-5 bg-gradient-to-br from-primary to-primary-container text-white fira-code font-bold text-lg rounded-xl shadow-xl shadow-primary/20 hover:shadow-2xl hover:translate-y-[-2px] active:translate-y-[1px] transition-all flex items-center justify-center gap-3 cursor-pointer">
RUN RESEARCH <span class="material-symbols-outlined">arrow_forward</span>
</button>
</div>
</section>
<!-- Loading State -->
<section id="loading-state" class="hidden-section max-w-[860px] mx-auto space-y-6">
<div class="relative w-full h-2 bg-surface-container rounded-full overflow-hidden">
<div id="progress-bar" class="absolute top-0 left-0 h-full bg-primary rounded-full transition-all duration-700" style="width:0%"></div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div id="step-1" class="bg-surface-container-low p-5 rounded-2xl flex items-center gap-4 border border-outline-variant/10 opacity-40 transition-all">
<div class="w-10 h-10 rounded-full bg-white flex items-center justify-center text-primary shadow-sm flex-shrink-0"><span class="material-symbols-outlined">cloud_download</span></div>
<div><div class="text-[10px] font-mono uppercase tracking-widest text-outline">Step 01</div><div class="text-sm font-bold flex items-center gap-2">Fetching<span class="step-dots hidden flex gap-1 ml-1"><span class="w-1.5 h-1.5 bg-primary rounded-full animate-bounce"></span><span class="w-1.5 h-1.5 bg-primary rounded-full animate-bounce" style="animation-delay:.2s"></span><span class="w-1.5 h-1.5 bg-primary rounded-full animate-bounce" style="animation-delay:.4s"></span></span></div></div>
</div>
<div id="step-2" class="bg-surface-container-low p-5 rounded-2xl flex items-center gap-4 border border-outline-variant/10 opacity-40 transition-all">
<div class="w-10 h-10 rounded-full bg-white flex items-center justify-center text-primary shadow-sm flex-shrink-0"><span class="material-symbols-outlined">troubleshoot</span></div>
<div><div class="text-[10px] font-mono uppercase tracking-widest text-outline">Step 02</div><div class="text-sm font-bold flex items-center gap-2">Scanning<span class="step-dots hidden flex gap-1 ml-1"><span class="w-1.5 h-1.5 bg-primary rounded-full animate-bounce"></span><span class="w-1.5 h-1.5 bg-primary rounded-full animate-bounce" style="animation-delay:.2s"></span><span class="w-1.5 h-1.5 bg-primary rounded-full animate-bounce" style="animation-delay:.4s"></span></span></div></div>
</div>
<div id="step-3" class="bg-surface-container-low p-5 rounded-2xl flex items-center gap-4 border border-outline-variant/10 opacity-40 transition-all">
<div class="w-10 h-10 rounded-full bg-white flex items-center justify-center text-primary shadow-sm flex-shrink-0"><span class="material-symbols-outlined">psychology</span></div>
<div><div class="text-[10px] font-mono uppercase tracking-widest text-outline">Step 03</div><div class="text-sm font-bold flex items-center gap-2">Synthesizing<span class="step-dots hidden flex gap-1 ml-1"><span class="w-1.5 h-1.5 bg-primary rounded-full animate-bounce"></span><span class="w-1.5 h-1.5 bg-primary rounded-full animate-bounce" style="animation-delay:.2s"></span><span class="w-1.5 h-1.5 bg-primary rounded-full animate-bounce" style="animation-delay:.4s"></span></span></div></div>
</div>
</div>
</section>
<!-- Results Board -->
<section id="results-board" class="hidden-section max-w-5xl mx-auto space-y-12">
<!-- Header + Export -->
<div class="flex flex-col md:flex-row md:items-end justify-between gap-4 px-2">
<div>
<h2 class="fira-code text-2xl font-bold text-on-background">Research Findings</h2>
<p id="session-line" class="text-on-surface-variant font-medium text-sm">Session ID: #TH-0000-X0</p>
</div>
<div class="flex gap-3 flex-wrap">
<button onclick="copyReport()" class="bg-surface-container-high hover:bg-outline-variant text-on-surface px-4 py-2.5 rounded-xl fira-code text-sm font-bold flex items-center gap-2 transition-all cursor-pointer">
<span class="material-symbols-outlined text-sm">content_copy</span> COPY
</button>
<button onclick="downloadReport()" class="bg-secondary hover:bg-secondary-dim text-white px-5 py-2.5 rounded-xl fira-code text-sm font-bold flex items-center gap-2 transition-all shadow-lg shadow-secondary/20 cursor-pointer">
<span class="material-symbols-outlined text-sm">download</span> EXPORT .TXT
</button>
</div>
</div>
<!-- Trend Score -->
<div class="bg-surface-container-lowest rounded-2xl p-6 shadow-sm border border-outline-variant/20">
<div class="flex items-center justify-between mb-3">
<span class="fira-code text-sm font-bold text-on-surface-variant uppercase tracking-wider">Trend Opportunity Score</span>
<span id="score-label" class="fira-code text-2xl font-bold text-primary">--/100</span>
</div>
<div class="h-3 bg-surface-container rounded-full overflow-hidden">
<div id="score-bar" class="score-bar-fill" style="width:0%"></div>
</div>
<p id="score-tag" class="text-xs font-mono text-outline mt-2 uppercase tracking-widest"></p>
</div>
<!-- Platform-wise Results Tabs -->
<div id="platform-results-section">
<div class="flex gap-2 flex-wrap mb-4" id="platform-tabs"></div>
<div id="platform-panes"></div>
</div>
<!-- Content Ideas -->
<div>
<h3 class="fira-code text-xl font-bold text-on-background mb-4 flex items-center gap-2">
<span class="material-symbols-outlined text-secondary" style="font-variation-settings:'FILL' 1;">lightbulb</span> Content Angle Ideas
</h3>
<div id="ideas-list" class="space-y-3"></div>
</div>
<!-- Full Report Raw -->
<div>
<div class="flex items-center justify-between mb-4">
<h3 class="fira-code text-xl font-bold text-on-background">Full Raw Report</h3>
<button onclick="downloadReport()" class="text-secondary font-mono text-sm font-bold hover:text-primary transition-colors flex items-center gap-2 cursor-pointer">
<span class="material-symbols-outlined text-sm">download</span> DOWNLOAD .TXT
</button>
</div>
<div class="bg-surface-container-low rounded-2xl overflow-hidden shadow-inner p-1">
<div class="bg-white rounded-xl p-8 border-l-[4px] border-secondary min-h-[200px]">
<pre id="report-text" class="fira-code text-sm leading-relaxed text-on-background whitespace-pre-wrap"></pre>
</div>
</div>
</div>
</section>
<!-- Bento Section -->
<section class="grid grid-cols-1 md:grid-cols-4 gap-4 h-auto md:h-[400px]">
<div class="md:col-span-2 md:row-span-2 bg-surface-container-high rounded-2xl overflow-hidden relative group h-56 md:h-full">
<img alt="abstract data visualization" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110" src="https://lh3.googleusercontent.com/aida-public/AB6AXuA-P6AjSTVsp9U0Xmp6_06bJnvOdAoA9WDx6w_3QPFLzcFJdRZT6_7xZ5loufV4SBcmMLyKsZnCuNlK3CYeIWr7sI3kIqYaBBKlIsiUoU_JThVnf_Py3C7GZ5mIRTO2oPvbw1flk3yD3faP5BCK35hyl76bChKCxg5I7uXft4Gq5q61Qe3nLxsNsK_fenbAqUNuXXP9TvVUJ1XH4VUsE8OaslUwj0wegPfA1X7ip2xCsK-2U72gb-K3F6KimQ1IqTKklYK176G-PbY"/>
<div class="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent flex items-end p-8">
<div class="text-white space-y-2"><div class="fira-code text-xl font-bold">Global Intelligence</div><p class="text-sm text-slate-200">Processing live streams from 14 server clusters.</p></div>
</div>
</div>
<div class="md:col-span-2 bg-surface-container-high rounded-2xl overflow-hidden relative group h-48 md:h-full">
<img alt="server room" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAv1cWhR26VWtenYrNz4-kfANaOttePUDnHyjp-5xJAn_6yo7MCk6ALvpJ4k-CKfRjyiNkuuvfc43bz19k7vC1y-_fNxGe20CcSRk5_lpOz8DBXSubRCRgg9fPcTH7l3t0WaX46DHdyPU-sBVw-TZbu7fV7wUymhs4y7EUoWTRHa9fnre9i54_hEjh2D6v2dOA6Zg6Da9LvvGXCP_jEySQHdyJjqbYIj8f10-JWjv-7Gkkrn8b6F_5YSirAldetjhfaAxxcstxEYAY"/>
</div>
<div class="md:col-span-2 bg-primary rounded-2xl flex flex-col items-center justify-center text-white p-6 text-center space-y-3 h-48 md:h-full">
<span class="material-symbols-outlined text-4xl" style="font-variation-settings:'FILL' 1;">rocket_launch</span>
<div class="fira-code font-bold text-sm">Scale Your Insights</div>
<p class="text-xs text-white/70">Start researching trends in any niche above.</p>
</div>
</section>
</main>
<footer class="w-full flex justify-center items-center py-10">
<div class="text-center space-y-2">
<div class="text-slate-500 font-mono text-[10px] uppercase tracking-widest">Trend Hunter AI • Multi-Platform Intelligence v2.0</div>
<div class="flex justify-center gap-6">
<span onclick="shareReport()" class="text-slate-400 hover:text-rose-400 transition-colors material-symbols-outlined cursor-pointer">share</span>
<span class="text-slate-400 hover:text-rose-400 transition-colors material-symbols-outlined cursor-pointer">help</span>
<span class="text-slate-400 hover:text-rose-400 transition-colors material-symbols-outlined cursor-pointer">settings</span>
</div>
</div>
</footer>
<script>
const BACKEND = window.location.origin;
let selectedPlatforms = new Set(['youtube','tiktok','instagram','twitter']);
let currentMode = 'niche';
let lastReport = '';
let lastNiche = '';
let searchHistory = JSON.parse(localStorage.getItem('th_history') || '[]');
// Boot
window.addEventListener('load', async () => {
renderHistory();
try { await fetch(`${BACKEND}/docs`); document.getElementById('engine-badge').style.display = 'flex'; } catch {}
});
// Mode toggle
function setMode(mode) {
currentMode = mode;
document.getElementById('niche-panel').className = mode === 'niche' ? '' : 'hidden-section';
document.getElementById('discover-panel').className = mode === 'discover' ? '' : 'hidden-section';
document.getElementById('platform-section').className = mode === 'niche' ? '' : 'hidden-section';
const btnNiche = document.getElementById('mode-niche');
const btnDiscover = document.getElementById('mode-discover');
if (mode === 'niche') {
btnNiche.className = 'flex-1 py-2 px-4 fira-code text-sm font-bold rounded-lg bg-white shadow-sm text-on-background transition-all cursor-pointer';
btnDiscover.className = 'flex-1 py-2 px-4 fira-code text-sm font-bold rounded-lg text-on-surface-variant transition-all cursor-pointer';
} else {
btnDiscover.className = 'flex-1 py-2 px-4 fira-code text-sm font-bold rounded-lg bg-white shadow-sm text-on-background transition-all cursor-pointer';
btnNiche.className = 'flex-1 py-2 px-4 fira-code text-sm font-bold rounded-lg text-on-surface-variant transition-all cursor-pointer';
}
}
// Platform toggles
function togglePlatform(id, btn) {
if (selectedPlatforms.has(id)) { selectedPlatforms.delete(id); btn.classList.remove('platform-btn-active'); btn.classList.add('platform-btn-inactive'); }
else { selectedPlatforms.add(id); btn.classList.remove('platform-btn-inactive'); btn.classList.add('platform-btn-active'); }
updateCount();
}
function selectAll() { document.querySelectorAll('.platform-btn').forEach(b => { b.classList.add('platform-btn-active'); b.classList.remove('platform-btn-inactive'); }); selectedPlatforms = new Set(['youtube','tiktok','instagram','twitter']); updateCount(); }
function clearAll() { document.querySelectorAll('.platform-btn').forEach(b => { b.classList.remove('platform-btn-active'); b.classList.add('platform-btn-inactive'); }); selectedPlatforms.clear(); updateCount(); }
function updateCount() { const n = selectedPlatforms.size; document.getElementById('platform-count').textContent = `${n} platform${n!==1?'s':''} selected`; }
// Step animation
function activateStep(id, pct) { const el = document.getElementById(id); el.classList.remove('opacity-40'); el.querySelector('.step-dots').classList.remove('hidden'); document.getElementById('progress-bar').style.width = pct+'%'; }
function doneStep(id) { document.getElementById(id).querySelector('.step-dots').classList.add('hidden'); }
// Main research
async function startResearch() {
if (currentMode === 'niche') {
const niche = document.getElementById('niche-input').value.trim();
if (!niche) { showToast('Please enter a niche keyword.'); return; }
if (selectedPlatforms.size === 0) { showToast('Select at least one platform.'); return; }
}
show('loading-state'); hide('results-board');
['step-1','step-2','step-3'].forEach(id => { document.getElementById(id).classList.add('opacity-40'); document.getElementById(id).querySelector('.step-dots').classList.add('hidden'); });
document.getElementById('progress-bar').style.width = '0%';
const runBtn = document.getElementById('run-btn');
runBtn.disabled = true;
runBtn.innerHTML = '<span class="material-symbols-outlined animate-spin">sync</span> ANALYZING...';
setTimeout(() => activateStep('step-1', 30), 400);
setTimeout(() => { doneStep('step-1'); activateStep('step-2', 60); }, 4000);
setTimeout(() => { doneStep('step-2'); activateStep('step-3', 85); }, 8000);
try {
let data;
if (currentMode === 'discover') {
const res = await fetch(`${BACKEND}/api/trending`);
if (!res.ok) throw new Error(`Server error: ${res.status}`);
data = await res.json();
lastNiche = 'Trending Discovery';
} else {
const niche = document.getElementById('niche-input').value.trim();
lastNiche = niche;
const res = await fetch(`${BACKEND}/api/research`, {
method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify({ niche, platforms: [...selectedPlatforms] })
});
if (!res.ok) throw new Error(`Server error: ${res.status}`);
data = await res.json();
addHistory(niche);
}
doneStep('step-3');
document.getElementById('progress-bar').style.width = '100%';
await sleep(600);
hide('loading-state');
renderResults(data);
show('results-board');
document.getElementById('results-board').scrollIntoView({ behavior:'smooth', block:'start' });
} catch (err) {
hide('loading-state');
document.getElementById('report-text').textContent = `Error: ${err.message}\n\nMake sure backend is running:\n python -m uvicorn backend.main:app --host 0.0.0.0 --port 8000`;
show('results-board');
showToast(`Error: ${err.message}`);
} finally {
runBtn.disabled = false;
runBtn.innerHTML = 'RUN RESEARCH <span class="material-symbols-outlined">arrow_forward</span>';
}
}
function renderResults(data) {
// Session ID
const sid = `TH-${Math.floor(1000+Math.random()*9000)}-X${Math.floor(Math.random()*10)}`;
document.getElementById('session-line').textContent = `Session: #${sid} • Niche: ${lastNiche}`;
// Trend Score
const score = data.trend_score || 0;
document.getElementById('score-label').textContent = `${score}/100`;
setTimeout(() => { document.getElementById('score-bar').style.width = score + '%'; }, 300);
const tag = score >= 75 ? 'HIGH OPPORTUNITY — Act now, volume is rising fast' : score >= 50 ? 'MEDIUM OPPORTUNITY — Growing niche, worth exploring' : 'EMERGING — Early stage, low competition';
document.getElementById('score-tag').textContent = tag;
// Platform-wise tabs + panes
renderPlatformResults(data.platform_results || []);
// Videos
if (data.videos && data.videos.length > 0) {
// Injected into YouTube platform pane
}
// Content Ideas
const ideasEl = document.getElementById('ideas-list');
const ideas = data.content_ideas || [];
ideasEl.innerHTML = ideas.map(idea => `
<div class="idea-card" onclick="copyText('${escAttr(idea)}')" title="Click to copy">
<div class="flex items-start justify-between gap-2">
<span>${escHtml(idea)}</span>
<span class="material-symbols-outlined text-sm text-outline flex-shrink-0">content_copy</span>
</div>
</div>`).join('');
// Raw report
lastReport = data.report || '';
document.getElementById('report-text').textContent = lastReport;
}
function renderPlatformResults(platformResults) {
const tabsEl = document.getElementById('platform-tabs');
const panesEl = document.getElementById('platform-panes');
tabsEl.innerHTML = '';
panesEl.innerHTML = '';
if (!platformResults || platformResults.length === 0) return;
platformResults.forEach((pr, idx) => {
// Tab
const tab = document.createElement('button');
tab.className = `platform-tab ${idx === 0 ? 'active' : ''}`;
tab.innerHTML = `<span class="material-symbols-outlined text-sm mr-1">${pr.icon||'circle'}</span>${pr.platform}`;
tab.onclick = () => switchTab(idx, platformResults.length);
tabsEl.appendChild(tab);
// Pane
const pane = document.createElement('div');
pane.className = `platform-pane ${idx === 0 ? 'active' : ''} bg-surface-container-lowest rounded-2xl p-6 shadow-sm border border-outline-variant/20 space-y-6`;
pane.id = `pane-${idx}`;
let inner = `
<div class="flex items-center justify-between">
<h4 class="fira-code font-bold text-lg text-on-background flex items-center gap-2">
<span class="material-symbols-outlined" style="color:${pr.color||'#888'}">${pr.icon||'circle'}</span>${pr.platform}
</h4>
<span class="text-xs font-mono font-bold px-3 py-1 rounded-full ${pr.opportunity==='HIGH'?'bg-tertiary-container text-on-tertiary-container':pr.opportunity==='MEDIUM'?'bg-secondary-container text-on-secondary-container':'bg-surface-container text-outline'}">${pr.opportunity||'--'} OPPORTUNITY</span>
</div>
<div class="space-y-2">
${(pr.insights||[]).filter(Boolean).map(i=>`<div class="flex items-start gap-2 text-sm"><span class="material-symbols-outlined text-secondary text-sm flex-shrink-0">check_circle</span><span>${escHtml(i)}</span></div>`).join('')}
</div>`;
// YouTube: videos grid
if (pr.platform === 'YouTube' && pr.videos && pr.videos.length > 0) {
inner += `<div><p class="fira-code text-xs font-bold text-on-surface-variant uppercase tracking-widest mb-3">Trending Videos</p>
<div class="grid grid-cols-2 md:grid-cols-3 gap-3">${pr.videos.slice(0,6).map(v => videoCardHtml(v)).join('')}</div></div>`;
}
// Social: topic rows
if (pr.trends && pr.trends.length > 0) {
inner += `<div><p class="fira-code text-xs font-bold text-on-surface-variant uppercase tracking-widest mb-3">Trending Topics</p>
<div class="space-y-3">${pr.trends.map(t => trendRowHtml(t, pr.platform)).join('')}</div></div>`;
}
// Raw data
if (pr.raw_data && pr.raw_data.length > 0) {
inner += `<details class="text-xs"><summary class="cursor-pointer text-outline font-mono uppercase tracking-widest hover:text-secondary">Show Raw Data</summary>
<pre class="mt-2 bg-surface-container p-3 rounded-xl fira-code text-[11px] leading-relaxed whitespace-pre-wrap text-on-surface-variant">${escHtml(pr.raw_data.join('\n'))}</pre></details>`;
}
pane.innerHTML = inner;
panesEl.appendChild(pane);
});
}
function switchTab(idx, total) {
document.querySelectorAll('.platform-tab').forEach((t,i) => t.classList.toggle('active', i===idx));
document.querySelectorAll('.platform-pane').forEach((p,i) => p.classList.toggle('active', i===idx));
}
function videoCardHtml(v) {
const date = v.published_at ? new Date(v.published_at).toLocaleDateString('en-US',{month:'short',day:'numeric'}) : '';
return `<a href="${escAttr(v.url)}" target="_blank" rel="noopener noreferrer" class="video-card">
<div style="position:relative;aspect-ratio:16/9;overflow:hidden;background:#eef0ff;">
${v.thumbnail?`<img src="${escAttr(v.thumbnail)}" alt="" style="width:100%;height:100%;object-fit:cover;" loading="lazy"/>`:
`<div style="display:flex;align-items:center;justify-content:center;height:100%;"><span class="material-symbols-outlined" style="font-size:2rem;color:#b90035;">smart_display</span></div>`}
<div class="thumb-overlay"><span class="material-symbols-outlined" style="font-size:2.5rem;color:white;">play_circle</span></div>
</div>
<div style="padding:10px 12px;">
<p style="font-size:0.78rem;font-weight:600;line-height:1.4;margin:0 0 4px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;">${escHtml(v.title||'')}</p>
<p style="font-size:0.72rem;color:#b90035;font-weight:600;margin:0 0 2px;">${escHtml(v.channel||'')}</p>
${date?`<p style="font-size:0.68rem;color:#6f768e;margin:0;">${date}</p>`:''}
</div>
</a>`;
}
function trendRowHtml(t, platform) {
const metric = t.views || t.engagement || t.tweets || '';
return `<div class="flex items-center justify-between bg-surface-container-low p-3 rounded-xl">
<div>
<p class="font-semibold text-sm text-on-background">${escHtml(t.topic||'')}</p>
<p class="text-xs text-secondary font-mono mt-0.5">${escHtml(t.hashtag||'')} ${metric?${metric}`:''}</p>
</div>
<span class="text-xs font-bold text-tertiary bg-tertiary-container/30 px-2 py-1 rounded-full">${escHtml(t.growth||'')}</span>
</div>`;
}
// History
function addHistory(niche) {
searchHistory = [{ niche, time: new Date().toLocaleTimeString(), date: new Date().toLocaleDateString() }, ...searchHistory.filter(h=>h.niche!==niche)].slice(0,10);
localStorage.setItem('th_history', JSON.stringify(searchHistory));
renderHistory();
}
function clearHistory() { searchHistory = []; localStorage.removeItem('th_history'); renderHistory(); }
function renderHistory() {
const el = document.getElementById('history-list');
el.innerHTML = searchHistory.length === 0 ? '<p class="text-xs text-outline text-center py-4">No searches yet.</p>' :
searchHistory.map(h => `<div class="history-item" onclick="loadFromHistory('${escAttr(h.niche)}')">
<span class="text-on-background font-medium truncate">${escHtml(h.niche)}</span>
<span class="text-outline text-xs flex-shrink-0">${h.time}</span>
</div>`).join('');
}
function loadFromHistory(niche) {
setMode('niche');
document.getElementById('niche-input').value = niche;
toggleHistory();
startResearch();
}
function toggleHistory() { const d = document.getElementById('history-drawer'); d.classList.toggle('hidden'); }
// Utilities
function downloadReport() {
if (!lastReport) { showToast('Run research first.'); return; }
const blob = new Blob([lastReport], {type:'text/plain'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a'); a.href=url; a.download=`${lastNiche.replace(/\s+/g,'_')}_Trend_Report.txt`;
document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url);
showToast('Report downloaded!');
}
function copyReport() { navigator.clipboard.writeText(lastReport).then(() => showToast('Report copied to clipboard!')); }
function copyText(text) { navigator.clipboard.writeText(text).then(() => showToast('Copied!')); }
function shareReport() { if (navigator.share) navigator.share({title:`Trend Report: ${lastNiche}`, text: lastReport.slice(0,500)+'...' }); else showToast('Share not supported in this browser.'); }
function showToast(msg) { const t = document.getElementById('toast'); t.textContent=msg; t.classList.add('show'); setTimeout(()=>t.classList.remove('show'),2800); }
function show(id) { document.getElementById(id).classList.remove('hidden-section'); }
function hide(id) { document.getElementById(id).classList.add('hidden-section'); }
function sleep(ms) { return new Promise(r=>setTimeout(r,ms)); }
function escHtml(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
function escAttr(s) { return String(s).replace(/"/g,'&quot;').replace(/'/g,'&#39;'); }
// Enter key
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('niche-input').addEventListener('keydown', e => { if(e.key==='Enter') startResearch(); });
document.addEventListener('click', e => { const d = document.getElementById('history-drawer'); if(!d.classList.contains('hidden') && !d.contains(e.target) && !e.target.closest('[onclick="toggleHistory()"]')) d.classList.add('hidden'); });
});
</script>
</body>
</html>