Upload dashboard.html with huggingface_hub
Browse files- dashboard.html +560 -0
dashboard.html
ADDED
|
@@ -0,0 +1,560 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html class="light" lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8"/>
|
| 5 |
+
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
| 6 |
+
<title>Trend Hunter AI | Multi-Platform Intelligence</title>
|
| 7 |
+
<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"/>
|
| 8 |
+
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
| 9 |
+
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
| 10 |
+
<script>
|
| 11 |
+
tailwind.config = {
|
| 12 |
+
darkMode:"class",
|
| 13 |
+
theme:{extend:{
|
| 14 |
+
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"},
|
| 15 |
+
fontFamily:{"mono":["Fira Code","monospace"],"sans":["Fira Sans","sans-serif"],"headline":["Fira Code","monospace"],"body":["Fira Sans","sans-serif"]},
|
| 16 |
+
borderRadius:{"DEFAULT":"0.125rem","lg":"0.25rem","xl":"0.5rem","2xl":"1rem","full":"9999px"},
|
| 17 |
+
},},
|
| 18 |
+
}
|
| 19 |
+
</script>
|
| 20 |
+
<style>
|
| 21 |
+
.material-symbols-outlined{font-variation-settings:'FILL' 0,'wght' 400,'GRAD' 0,'opsz' 24;vertical-align:middle;}
|
| 22 |
+
body{font-family:'Fira Sans',sans-serif;background-color:#f6f6ff;color:#272e42;}
|
| 23 |
+
.fira-code{font-family:'Fira Code',monospace;}
|
| 24 |
+
.platform-btn-active{background-color:#0050d4!important;color:white!important;box-shadow:0 10px 15px -3px rgba(0,80,212,0.2);}
|
| 25 |
+
.platform-btn-inactive{background-color:#d9e2ff;color:#535b71;}
|
| 26 |
+
.hidden-section{display:none;}
|
| 27 |
+
.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;}
|
| 28 |
+
.video-card:hover{transform:translateY(-3px);box-shadow:0 10px 20px rgba(0,0,0,0.1);}
|
| 29 |
+
.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;}
|
| 30 |
+
.video-card:hover .thumb-overlay{opacity:1;}
|
| 31 |
+
.platform-tab{cursor:pointer;padding:10px 18px;border-radius:10px;font-weight:600;font-size:0.85rem;transition:all 200ms;color:#535b71;}
|
| 32 |
+
.platform-tab.active{background:#0050d4;color:white;box-shadow:0 4px 12px rgba(0,80,212,0.25);}
|
| 33 |
+
.platform-pane{display:none;}
|
| 34 |
+
.platform-pane.active{display:block;}
|
| 35 |
+
.score-bar-fill{height:100%;border-radius:9999px;background:linear-gradient(90deg,#b90035,#ff7481);transition:width 1s ease;}
|
| 36 |
+
.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;}
|
| 37 |
+
.idea-card:hover{background:#eef0ff;transform:translateX(4px);}
|
| 38 |
+
.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;}
|
| 39 |
+
.history-item:hover{background:#eef0ff;}
|
| 40 |
+
.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;}
|
| 41 |
+
.toast.show{opacity:1;transform:translateY(0);}
|
| 42 |
+
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
|
| 43 |
+
.skeleton{background:linear-gradient(90deg,#e2e7ff 25%,#d9e2ff 50%,#e2e7ff 75%);background-size:200% 100%;animation:shimmer 1.5s infinite;border-radius:8px;}
|
| 44 |
+
</style>
|
| 45 |
+
</head>
|
| 46 |
+
<body class="bg-background selection:bg-primary-container selection:text-on-primary-container">
|
| 47 |
+
|
| 48 |
+
<!-- Toast -->
|
| 49 |
+
<div id="toast" class="toast"></div>
|
| 50 |
+
|
| 51 |
+
<!-- Nav -->
|
| 52 |
+
<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">
|
| 53 |
+
<div class="flex items-center gap-2 text-xl font-bold fira-code text-rose-600">
|
| 54 |
+
<span class="material-symbols-outlined" style="font-variation-settings:'FILL' 1;">insights</span>Trend Hunter
|
| 55 |
+
</div>
|
| 56 |
+
<div class="flex items-center gap-3">
|
| 57 |
+
<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">
|
| 58 |
+
<span class="w-2 h-2 rounded-full bg-green-500 inline-block"></span> Engine Online
|
| 59 |
+
</div>
|
| 60 |
+
<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>
|
| 61 |
+
</div>
|
| 62 |
+
</nav>
|
| 63 |
+
|
| 64 |
+
<!-- History Drawer -->
|
| 65 |
+
<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">
|
| 66 |
+
<div class="flex justify-between items-center mb-3">
|
| 67 |
+
<span class="fira-code font-bold text-sm text-on-background">Search History</span>
|
| 68 |
+
<button onclick="clearHistory()" class="text-xs text-primary hover:underline cursor-pointer">Clear All</button>
|
| 69 |
+
</div>
|
| 70 |
+
<div id="history-list"><p class="text-xs text-outline text-center py-4">No searches yet.</p></div>
|
| 71 |
+
</div>
|
| 72 |
+
|
| 73 |
+
<main class="pt-32 pb-20 px-4 max-w-7xl mx-auto space-y-16">
|
| 74 |
+
|
| 75 |
+
<!-- Hero -->
|
| 76 |
+
<section class="text-center space-y-8">
|
| 77 |
+
<div class="space-y-4">
|
| 78 |
+
<h1 class="fira-code text-4xl md:text-6xl font-bold tracking-tight text-on-background">
|
| 79 |
+
What's <span class="text-primary italic">Trending</span> Right Now?
|
| 80 |
+
</h1>
|
| 81 |
+
<p class="max-w-2xl mx-auto text-on-surface-variant text-lg md:text-xl font-medium">
|
| 82 |
+
Autonomous AI research across 4 major social networks to find your next viral niche in seconds.
|
| 83 |
+
</p>
|
| 84 |
+
</div>
|
| 85 |
+
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 max-w-4xl mx-auto">
|
| 86 |
+
<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">
|
| 87 |
+
<span class="fira-code text-3xl font-bold text-primary">2.4M+</span>
|
| 88 |
+
<span class="font-mono text-[10px] uppercase tracking-widest text-outline">Data Points</span>
|
| 89 |
+
</div>
|
| 90 |
+
<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">
|
| 91 |
+
<span class="fira-code text-3xl font-bold text-secondary">842</span>
|
| 92 |
+
<span class="font-mono text-[10px] uppercase tracking-widest text-outline">Niches Found</span>
|
| 93 |
+
</div>
|
| 94 |
+
<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">
|
| 95 |
+
<span class="fira-code text-3xl font-bold text-tertiary">4</span>
|
| 96 |
+
<span class="font-mono text-[10px] uppercase tracking-widest text-outline">Platforms</span>
|
| 97 |
+
</div>
|
| 98 |
+
<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">
|
| 99 |
+
<span class="fira-code text-3xl font-bold text-on-background">v2.0</span>
|
| 100 |
+
<span class="font-mono text-[10px] uppercase tracking-widest text-outline">Engine</span>
|
| 101 |
+
</div>
|
| 102 |
+
</div>
|
| 103 |
+
</section>
|
| 104 |
+
|
| 105 |
+
<!-- Research Control Panel -->
|
| 106 |
+
<section class="max-w-[860px] mx-auto">
|
| 107 |
+
<div class="bg-surface-container-lowest rounded-2xl shadow-xl border-t-[4px] border-primary p-8 md:p-10 space-y-8">
|
| 108 |
+
|
| 109 |
+
<!-- Mode Toggle -->
|
| 110 |
+
<div class="flex gap-3 p-1 bg-surface-container-high rounded-xl">
|
| 111 |
+
<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">
|
| 112 |
+
<span class="material-symbols-outlined text-sm">search</span> Research a Niche
|
| 113 |
+
</button>
|
| 114 |
+
<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">
|
| 115 |
+
<span class="material-symbols-outlined text-sm">local_fire_department</span> Discover Trends Now
|
| 116 |
+
</button>
|
| 117 |
+
</div>
|
| 118 |
+
|
| 119 |
+
<!-- Niche Input -->
|
| 120 |
+
<div id="niche-panel">
|
| 121 |
+
<label class="block">
|
| 122 |
+
<span class="fira-code text-sm font-bold text-on-surface-variant mb-2 block uppercase tracking-wider">Target Niche or Keyword</span>
|
| 123 |
+
<div class="relative group">
|
| 124 |
+
<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>
|
| 125 |
+
<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"/>
|
| 126 |
+
</div>
|
| 127 |
+
</label>
|
| 128 |
+
</div>
|
| 129 |
+
|
| 130 |
+
<!-- Discovery Mode Message -->
|
| 131 |
+
<div id="discover-panel" class="hidden-section">
|
| 132 |
+
<div class="bg-tertiary-container/20 border border-tertiary/20 rounded-xl p-5 text-center space-y-2">
|
| 133 |
+
<span class="material-symbols-outlined text-3xl text-tertiary" style="font-variation-settings:'FILL' 1;">local_fire_department</span>
|
| 134 |
+
<p class="fira-code font-bold text-on-background">Discover Mode Active</p>
|
| 135 |
+
<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>
|
| 136 |
+
</div>
|
| 137 |
+
</div>
|
| 138 |
+
|
| 139 |
+
<!-- Platform Chips -->
|
| 140 |
+
<div id="platform-section">
|
| 141 |
+
<div class="flex items-center justify-between mb-3">
|
| 142 |
+
<span class="fira-code text-sm font-bold text-on-surface-variant uppercase tracking-wider">Platform Synthesis Focus</span>
|
| 143 |
+
<div class="flex gap-3">
|
| 144 |
+
<button onclick="selectAll()" class="text-secondary text-xs font-mono font-bold hover:underline cursor-pointer">Select All</button>
|
| 145 |
+
<button onclick="clearAll()" class="text-outline text-xs font-mono font-bold hover:underline cursor-pointer">Clear</button>
|
| 146 |
+
</div>
|
| 147 |
+
</div>
|
| 148 |
+
<div class="flex flex-wrap gap-3">
|
| 149 |
+
<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">
|
| 150 |
+
<span class="material-symbols-outlined text-sm">smart_display</span> YouTube
|
| 151 |
+
</button>
|
| 152 |
+
<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">
|
| 153 |
+
<span class="material-symbols-outlined text-sm">bolt</span> TikTok
|
| 154 |
+
</button>
|
| 155 |
+
<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">
|
| 156 |
+
<span class="material-symbols-outlined text-sm">photo_camera</span> Instagram
|
| 157 |
+
</button>
|
| 158 |
+
<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">
|
| 159 |
+
<span class="material-symbols-outlined text-sm">close</span> X /Twitter
|
| 160 |
+
</button>
|
| 161 |
+
</div>
|
| 162 |
+
<div class="flex justify-between items-center text-[11px] font-mono text-outline uppercase tracking-tight px-1 mt-2">
|
| 163 |
+
<span>* High-intensity scanning enabled</span>
|
| 164 |
+
<span id="platform-count" class="text-secondary font-bold">4 platforms selected</span>
|
| 165 |
+
</div>
|
| 166 |
+
</div>
|
| 167 |
+
|
| 168 |
+
<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">
|
| 169 |
+
RUN RESEARCH <span class="material-symbols-outlined">arrow_forward</span>
|
| 170 |
+
</button>
|
| 171 |
+
</div>
|
| 172 |
+
</section>
|
| 173 |
+
|
| 174 |
+
<!-- Loading State -->
|
| 175 |
+
<section id="loading-state" class="hidden-section max-w-[860px] mx-auto space-y-6">
|
| 176 |
+
<div class="relative w-full h-2 bg-surface-container rounded-full overflow-hidden">
|
| 177 |
+
<div id="progress-bar" class="absolute top-0 left-0 h-full bg-primary rounded-full transition-all duration-700" style="width:0%"></div>
|
| 178 |
+
</div>
|
| 179 |
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
| 180 |
+
<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">
|
| 181 |
+
<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>
|
| 182 |
+
<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>
|
| 183 |
+
</div>
|
| 184 |
+
<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">
|
| 185 |
+
<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>
|
| 186 |
+
<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>
|
| 187 |
+
</div>
|
| 188 |
+
<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">
|
| 189 |
+
<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>
|
| 190 |
+
<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>
|
| 191 |
+
</div>
|
| 192 |
+
</div>
|
| 193 |
+
</section>
|
| 194 |
+
|
| 195 |
+
<!-- Results Board -->
|
| 196 |
+
<section id="results-board" class="hidden-section max-w-5xl mx-auto space-y-12">
|
| 197 |
+
|
| 198 |
+
<!-- Header + Export -->
|
| 199 |
+
<div class="flex flex-col md:flex-row md:items-end justify-between gap-4 px-2">
|
| 200 |
+
<div>
|
| 201 |
+
<h2 class="fira-code text-2xl font-bold text-on-background">Research Findings</h2>
|
| 202 |
+
<p id="session-line" class="text-on-surface-variant font-medium text-sm">Session ID: #TH-0000-X0</p>
|
| 203 |
+
</div>
|
| 204 |
+
<div class="flex gap-3 flex-wrap">
|
| 205 |
+
<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">
|
| 206 |
+
<span class="material-symbols-outlined text-sm">content_copy</span> COPY
|
| 207 |
+
</button>
|
| 208 |
+
<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">
|
| 209 |
+
<span class="material-symbols-outlined text-sm">download</span> EXPORT .TXT
|
| 210 |
+
</button>
|
| 211 |
+
</div>
|
| 212 |
+
</div>
|
| 213 |
+
|
| 214 |
+
<!-- Trend Score -->
|
| 215 |
+
<div class="bg-surface-container-lowest rounded-2xl p-6 shadow-sm border border-outline-variant/20">
|
| 216 |
+
<div class="flex items-center justify-between mb-3">
|
| 217 |
+
<span class="fira-code text-sm font-bold text-on-surface-variant uppercase tracking-wider">Trend Opportunity Score</span>
|
| 218 |
+
<span id="score-label" class="fira-code text-2xl font-bold text-primary">--/100</span>
|
| 219 |
+
</div>
|
| 220 |
+
<div class="h-3 bg-surface-container rounded-full overflow-hidden">
|
| 221 |
+
<div id="score-bar" class="score-bar-fill" style="width:0%"></div>
|
| 222 |
+
</div>
|
| 223 |
+
<p id="score-tag" class="text-xs font-mono text-outline mt-2 uppercase tracking-widest"></p>
|
| 224 |
+
</div>
|
| 225 |
+
|
| 226 |
+
<!-- Platform-wise Results Tabs -->
|
| 227 |
+
<div id="platform-results-section">
|
| 228 |
+
<div class="flex gap-2 flex-wrap mb-4" id="platform-tabs"></div>
|
| 229 |
+
<div id="platform-panes"></div>
|
| 230 |
+
</div>
|
| 231 |
+
|
| 232 |
+
<!-- Content Ideas -->
|
| 233 |
+
<div>
|
| 234 |
+
<h3 class="fira-code text-xl font-bold text-on-background mb-4 flex items-center gap-2">
|
| 235 |
+
<span class="material-symbols-outlined text-secondary" style="font-variation-settings:'FILL' 1;">lightbulb</span> Content Angle Ideas
|
| 236 |
+
</h3>
|
| 237 |
+
<div id="ideas-list" class="space-y-3"></div>
|
| 238 |
+
</div>
|
| 239 |
+
|
| 240 |
+
<!-- Full Report Raw -->
|
| 241 |
+
<div>
|
| 242 |
+
<div class="flex items-center justify-between mb-4">
|
| 243 |
+
<h3 class="fira-code text-xl font-bold text-on-background">Full Raw Report</h3>
|
| 244 |
+
<button onclick="downloadReport()" class="text-secondary font-mono text-sm font-bold hover:text-primary transition-colors flex items-center gap-2 cursor-pointer">
|
| 245 |
+
<span class="material-symbols-outlined text-sm">download</span> DOWNLOAD .TXT
|
| 246 |
+
</button>
|
| 247 |
+
</div>
|
| 248 |
+
<div class="bg-surface-container-low rounded-2xl overflow-hidden shadow-inner p-1">
|
| 249 |
+
<div class="bg-white rounded-xl p-8 border-l-[4px] border-secondary min-h-[200px]">
|
| 250 |
+
<pre id="report-text" class="fira-code text-sm leading-relaxed text-on-background whitespace-pre-wrap"></pre>
|
| 251 |
+
</div>
|
| 252 |
+
</div>
|
| 253 |
+
</div>
|
| 254 |
+
</section>
|
| 255 |
+
|
| 256 |
+
<!-- Bento Section -->
|
| 257 |
+
<section class="grid grid-cols-1 md:grid-cols-4 gap-4 h-auto md:h-[400px]">
|
| 258 |
+
<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">
|
| 259 |
+
<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"/>
|
| 260 |
+
<div class="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent flex items-end p-8">
|
| 261 |
+
<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>
|
| 262 |
+
</div>
|
| 263 |
+
</div>
|
| 264 |
+
<div class="md:col-span-2 bg-surface-container-high rounded-2xl overflow-hidden relative group h-48 md:h-full">
|
| 265 |
+
<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"/>
|
| 266 |
+
</div>
|
| 267 |
+
<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">
|
| 268 |
+
<span class="material-symbols-outlined text-4xl" style="font-variation-settings:'FILL' 1;">rocket_launch</span>
|
| 269 |
+
<div class="fira-code font-bold text-sm">Scale Your Insights</div>
|
| 270 |
+
<p class="text-xs text-white/70">Start researching trends in any niche above.</p>
|
| 271 |
+
</div>
|
| 272 |
+
</section>
|
| 273 |
+
</main>
|
| 274 |
+
|
| 275 |
+
<footer class="w-full flex justify-center items-center py-10">
|
| 276 |
+
<div class="text-center space-y-2">
|
| 277 |
+
<div class="text-slate-500 font-mono text-[10px] uppercase tracking-widest">Trend Hunter AI • Multi-Platform Intelligence v2.0</div>
|
| 278 |
+
<div class="flex justify-center gap-6">
|
| 279 |
+
<span onclick="shareReport()" class="text-slate-400 hover:text-rose-400 transition-colors material-symbols-outlined cursor-pointer">share</span>
|
| 280 |
+
<span class="text-slate-400 hover:text-rose-400 transition-colors material-symbols-outlined cursor-pointer">help</span>
|
| 281 |
+
<span class="text-slate-400 hover:text-rose-400 transition-colors material-symbols-outlined cursor-pointer">settings</span>
|
| 282 |
+
</div>
|
| 283 |
+
</div>
|
| 284 |
+
</footer>
|
| 285 |
+
|
| 286 |
+
<script>
|
| 287 |
+
const BACKEND = 'http://localhost:8000';
|
| 288 |
+
let selectedPlatforms = new Set(['youtube','tiktok','instagram','twitter']);
|
| 289 |
+
let currentMode = 'niche';
|
| 290 |
+
let lastReport = '';
|
| 291 |
+
let lastNiche = '';
|
| 292 |
+
let searchHistory = JSON.parse(localStorage.getItem('th_history') || '[]');
|
| 293 |
+
|
| 294 |
+
// Boot
|
| 295 |
+
window.addEventListener('load', async () => {
|
| 296 |
+
renderHistory();
|
| 297 |
+
try { await fetch(`${BACKEND}/docs`); document.getElementById('engine-badge').style.display = 'flex'; } catch {}
|
| 298 |
+
});
|
| 299 |
+
|
| 300 |
+
// Mode toggle
|
| 301 |
+
function setMode(mode) {
|
| 302 |
+
currentMode = mode;
|
| 303 |
+
document.getElementById('niche-panel').className = mode === 'niche' ? '' : 'hidden-section';
|
| 304 |
+
document.getElementById('discover-panel').className = mode === 'discover' ? '' : 'hidden-section';
|
| 305 |
+
document.getElementById('platform-section').className = mode === 'niche' ? '' : 'hidden-section';
|
| 306 |
+
const btnNiche = document.getElementById('mode-niche');
|
| 307 |
+
const btnDiscover = document.getElementById('mode-discover');
|
| 308 |
+
if (mode === 'niche') {
|
| 309 |
+
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';
|
| 310 |
+
btnDiscover.className = 'flex-1 py-2 px-4 fira-code text-sm font-bold rounded-lg text-on-surface-variant transition-all cursor-pointer';
|
| 311 |
+
} else {
|
| 312 |
+
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';
|
| 313 |
+
btnNiche.className = 'flex-1 py-2 px-4 fira-code text-sm font-bold rounded-lg text-on-surface-variant transition-all cursor-pointer';
|
| 314 |
+
}
|
| 315 |
+
}
|
| 316 |
+
|
| 317 |
+
// Platform toggles
|
| 318 |
+
function togglePlatform(id, btn) {
|
| 319 |
+
if (selectedPlatforms.has(id)) { selectedPlatforms.delete(id); btn.classList.remove('platform-btn-active'); btn.classList.add('platform-btn-inactive'); }
|
| 320 |
+
else { selectedPlatforms.add(id); btn.classList.remove('platform-btn-inactive'); btn.classList.add('platform-btn-active'); }
|
| 321 |
+
updateCount();
|
| 322 |
+
}
|
| 323 |
+
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(); }
|
| 324 |
+
function clearAll() { document.querySelectorAll('.platform-btn').forEach(b => { b.classList.remove('platform-btn-active'); b.classList.add('platform-btn-inactive'); }); selectedPlatforms.clear(); updateCount(); }
|
| 325 |
+
function updateCount() { const n = selectedPlatforms.size; document.getElementById('platform-count').textContent = `${n} platform${n!==1?'s':''} selected`; }
|
| 326 |
+
|
| 327 |
+
// Step animation
|
| 328 |
+
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+'%'; }
|
| 329 |
+
function doneStep(id) { document.getElementById(id).querySelector('.step-dots').classList.add('hidden'); }
|
| 330 |
+
|
| 331 |
+
// Main research
|
| 332 |
+
async function startResearch() {
|
| 333 |
+
if (currentMode === 'niche') {
|
| 334 |
+
const niche = document.getElementById('niche-input').value.trim();
|
| 335 |
+
if (!niche) { showToast('Please enter a niche keyword.'); return; }
|
| 336 |
+
if (selectedPlatforms.size === 0) { showToast('Select at least one platform.'); return; }
|
| 337 |
+
}
|
| 338 |
+
|
| 339 |
+
show('loading-state'); hide('results-board');
|
| 340 |
+
['step-1','step-2','step-3'].forEach(id => { document.getElementById(id).classList.add('opacity-40'); document.getElementById(id).querySelector('.step-dots').classList.add('hidden'); });
|
| 341 |
+
document.getElementById('progress-bar').style.width = '0%';
|
| 342 |
+
|
| 343 |
+
const runBtn = document.getElementById('run-btn');
|
| 344 |
+
runBtn.disabled = true;
|
| 345 |
+
runBtn.innerHTML = '<span class="material-symbols-outlined animate-spin">sync</span> ANALYZING...';
|
| 346 |
+
|
| 347 |
+
setTimeout(() => activateStep('step-1', 30), 400);
|
| 348 |
+
setTimeout(() => { doneStep('step-1'); activateStep('step-2', 60); }, 4000);
|
| 349 |
+
setTimeout(() => { doneStep('step-2'); activateStep('step-3', 85); }, 8000);
|
| 350 |
+
|
| 351 |
+
try {
|
| 352 |
+
let data;
|
| 353 |
+
if (currentMode === 'discover') {
|
| 354 |
+
const res = await fetch(`${BACKEND}/api/trending`);
|
| 355 |
+
if (!res.ok) throw new Error(`Server error: ${res.status}`);
|
| 356 |
+
data = await res.json();
|
| 357 |
+
lastNiche = 'Trending Discovery';
|
| 358 |
+
} else {
|
| 359 |
+
const niche = document.getElementById('niche-input').value.trim();
|
| 360 |
+
lastNiche = niche;
|
| 361 |
+
const res = await fetch(`${BACKEND}/api/research`, {
|
| 362 |
+
method:'POST', headers:{'Content-Type':'application/json'},
|
| 363 |
+
body: JSON.stringify({ niche, platforms: [...selectedPlatforms] })
|
| 364 |
+
});
|
| 365 |
+
if (!res.ok) throw new Error(`Server error: ${res.status}`);
|
| 366 |
+
data = await res.json();
|
| 367 |
+
addHistory(niche);
|
| 368 |
+
}
|
| 369 |
+
|
| 370 |
+
doneStep('step-3');
|
| 371 |
+
document.getElementById('progress-bar').style.width = '100%';
|
| 372 |
+
await sleep(600);
|
| 373 |
+
hide('loading-state');
|
| 374 |
+
renderResults(data);
|
| 375 |
+
show('results-board');
|
| 376 |
+
document.getElementById('results-board').scrollIntoView({ behavior:'smooth', block:'start' });
|
| 377 |
+
|
| 378 |
+
} catch (err) {
|
| 379 |
+
hide('loading-state');
|
| 380 |
+
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`;
|
| 381 |
+
show('results-board');
|
| 382 |
+
showToast(`Error: ${err.message}`);
|
| 383 |
+
} finally {
|
| 384 |
+
runBtn.disabled = false;
|
| 385 |
+
runBtn.innerHTML = 'RUN RESEARCH <span class="material-symbols-outlined">arrow_forward</span>';
|
| 386 |
+
}
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
function renderResults(data) {
|
| 390 |
+
// Session ID
|
| 391 |
+
const sid = `TH-${Math.floor(1000+Math.random()*9000)}-X${Math.floor(Math.random()*10)}`;
|
| 392 |
+
document.getElementById('session-line').textContent = `Session: #${sid} • Niche: ${lastNiche}`;
|
| 393 |
+
|
| 394 |
+
// Trend Score
|
| 395 |
+
const score = data.trend_score || 0;
|
| 396 |
+
document.getElementById('score-label').textContent = `${score}/100`;
|
| 397 |
+
setTimeout(() => { document.getElementById('score-bar').style.width = score + '%'; }, 300);
|
| 398 |
+
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';
|
| 399 |
+
document.getElementById('score-tag').textContent = tag;
|
| 400 |
+
|
| 401 |
+
// Platform-wise tabs + panes
|
| 402 |
+
renderPlatformResults(data.platform_results || []);
|
| 403 |
+
|
| 404 |
+
// Videos
|
| 405 |
+
if (data.videos && data.videos.length > 0) {
|
| 406 |
+
// Injected into YouTube platform pane
|
| 407 |
+
}
|
| 408 |
+
|
| 409 |
+
// Content Ideas
|
| 410 |
+
const ideasEl = document.getElementById('ideas-list');
|
| 411 |
+
const ideas = data.content_ideas || [];
|
| 412 |
+
ideasEl.innerHTML = ideas.map(idea => `
|
| 413 |
+
<div class="idea-card" onclick="copyText('${escAttr(idea)}')" title="Click to copy">
|
| 414 |
+
<div class="flex items-start justify-between gap-2">
|
| 415 |
+
<span>${escHtml(idea)}</span>
|
| 416 |
+
<span class="material-symbols-outlined text-sm text-outline flex-shrink-0">content_copy</span>
|
| 417 |
+
</div>
|
| 418 |
+
</div>`).join('');
|
| 419 |
+
|
| 420 |
+
// Raw report
|
| 421 |
+
lastReport = data.report || '';
|
| 422 |
+
document.getElementById('report-text').textContent = lastReport;
|
| 423 |
+
}
|
| 424 |
+
|
| 425 |
+
function renderPlatformResults(platformResults) {
|
| 426 |
+
const tabsEl = document.getElementById('platform-tabs');
|
| 427 |
+
const panesEl = document.getElementById('platform-panes');
|
| 428 |
+
tabsEl.innerHTML = '';
|
| 429 |
+
panesEl.innerHTML = '';
|
| 430 |
+
if (!platformResults || platformResults.length === 0) return;
|
| 431 |
+
|
| 432 |
+
platformResults.forEach((pr, idx) => {
|
| 433 |
+
// Tab
|
| 434 |
+
const tab = document.createElement('button');
|
| 435 |
+
tab.className = `platform-tab ${idx === 0 ? 'active' : ''}`;
|
| 436 |
+
tab.innerHTML = `<span class="material-symbols-outlined text-sm mr-1">${pr.icon||'circle'}</span>${pr.platform}`;
|
| 437 |
+
tab.onclick = () => switchTab(idx, platformResults.length);
|
| 438 |
+
tabsEl.appendChild(tab);
|
| 439 |
+
|
| 440 |
+
// Pane
|
| 441 |
+
const pane = document.createElement('div');
|
| 442 |
+
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`;
|
| 443 |
+
pane.id = `pane-${idx}`;
|
| 444 |
+
|
| 445 |
+
let inner = `
|
| 446 |
+
<div class="flex items-center justify-between">
|
| 447 |
+
<h4 class="fira-code font-bold text-lg text-on-background flex items-center gap-2">
|
| 448 |
+
<span class="material-symbols-outlined" style="color:${pr.color||'#888'}">${pr.icon||'circle'}</span>${pr.platform}
|
| 449 |
+
</h4>
|
| 450 |
+
<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>
|
| 451 |
+
</div>
|
| 452 |
+
<div class="space-y-2">
|
| 453 |
+
${(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('')}
|
| 454 |
+
</div>`;
|
| 455 |
+
|
| 456 |
+
// YouTube: videos grid
|
| 457 |
+
if (pr.platform === 'YouTube' && pr.videos && pr.videos.length > 0) {
|
| 458 |
+
inner += `<div><p class="fira-code text-xs font-bold text-on-surface-variant uppercase tracking-widest mb-3">Trending Videos</p>
|
| 459 |
+
<div class="grid grid-cols-2 md:grid-cols-3 gap-3">${pr.videos.slice(0,6).map(v => videoCardHtml(v)).join('')}</div></div>`;
|
| 460 |
+
}
|
| 461 |
+
|
| 462 |
+
// Social: topic rows
|
| 463 |
+
if (pr.trends && pr.trends.length > 0) {
|
| 464 |
+
inner += `<div><p class="fira-code text-xs font-bold text-on-surface-variant uppercase tracking-widest mb-3">Trending Topics</p>
|
| 465 |
+
<div class="space-y-3">${pr.trends.map(t => trendRowHtml(t, pr.platform)).join('')}</div></div>`;
|
| 466 |
+
}
|
| 467 |
+
|
| 468 |
+
// Raw data
|
| 469 |
+
if (pr.raw_data && pr.raw_data.length > 0) {
|
| 470 |
+
inner += `<details class="text-xs"><summary class="cursor-pointer text-outline font-mono uppercase tracking-widest hover:text-secondary">Show Raw Data</summary>
|
| 471 |
+
<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>`;
|
| 472 |
+
}
|
| 473 |
+
|
| 474 |
+
pane.innerHTML = inner;
|
| 475 |
+
panesEl.appendChild(pane);
|
| 476 |
+
});
|
| 477 |
+
}
|
| 478 |
+
|
| 479 |
+
function switchTab(idx, total) {
|
| 480 |
+
document.querySelectorAll('.platform-tab').forEach((t,i) => t.classList.toggle('active', i===idx));
|
| 481 |
+
document.querySelectorAll('.platform-pane').forEach((p,i) => p.classList.toggle('active', i===idx));
|
| 482 |
+
}
|
| 483 |
+
|
| 484 |
+
function videoCardHtml(v) {
|
| 485 |
+
const date = v.published_at ? new Date(v.published_at).toLocaleDateString('en-US',{month:'short',day:'numeric'}) : '';
|
| 486 |
+
return `<a href="${escAttr(v.url)}" target="_blank" rel="noopener noreferrer" class="video-card">
|
| 487 |
+
<div style="position:relative;aspect-ratio:16/9;overflow:hidden;background:#eef0ff;">
|
| 488 |
+
${v.thumbnail?`<img src="${escAttr(v.thumbnail)}" alt="" style="width:100%;height:100%;object-fit:cover;" loading="lazy"/>`:
|
| 489 |
+
`<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>`}
|
| 490 |
+
<div class="thumb-overlay"><span class="material-symbols-outlined" style="font-size:2.5rem;color:white;">play_circle</span></div>
|
| 491 |
+
</div>
|
| 492 |
+
<div style="padding:10px 12px;">
|
| 493 |
+
<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>
|
| 494 |
+
<p style="font-size:0.72rem;color:#b90035;font-weight:600;margin:0 0 2px;">${escHtml(v.channel||'')}</p>
|
| 495 |
+
${date?`<p style="font-size:0.68rem;color:#6f768e;margin:0;">${date}</p>`:''}
|
| 496 |
+
</div>
|
| 497 |
+
</a>`;
|
| 498 |
+
}
|
| 499 |
+
|
| 500 |
+
function trendRowHtml(t, platform) {
|
| 501 |
+
const metric = t.views || t.engagement || t.tweets || '';
|
| 502 |
+
return `<div class="flex items-center justify-between bg-surface-container-low p-3 rounded-xl">
|
| 503 |
+
<div>
|
| 504 |
+
<p class="font-semibold text-sm text-on-background">${escHtml(t.topic||'')}</p>
|
| 505 |
+
<p class="text-xs text-secondary font-mono mt-0.5">${escHtml(t.hashtag||'')} ${metric?`· ${metric}`:''}</p>
|
| 506 |
+
</div>
|
| 507 |
+
<span class="text-xs font-bold text-tertiary bg-tertiary-container/30 px-2 py-1 rounded-full">${escHtml(t.growth||'')}</span>
|
| 508 |
+
</div>`;
|
| 509 |
+
}
|
| 510 |
+
|
| 511 |
+
// History
|
| 512 |
+
function addHistory(niche) {
|
| 513 |
+
searchHistory = [{ niche, time: new Date().toLocaleTimeString(), date: new Date().toLocaleDateString() }, ...searchHistory.filter(h=>h.niche!==niche)].slice(0,10);
|
| 514 |
+
localStorage.setItem('th_history', JSON.stringify(searchHistory));
|
| 515 |
+
renderHistory();
|
| 516 |
+
}
|
| 517 |
+
function clearHistory() { searchHistory = []; localStorage.removeItem('th_history'); renderHistory(); }
|
| 518 |
+
function renderHistory() {
|
| 519 |
+
const el = document.getElementById('history-list');
|
| 520 |
+
el.innerHTML = searchHistory.length === 0 ? '<p class="text-xs text-outline text-center py-4">No searches yet.</p>' :
|
| 521 |
+
searchHistory.map(h => `<div class="history-item" onclick="loadFromHistory('${escAttr(h.niche)}')">
|
| 522 |
+
<span class="text-on-background font-medium truncate">${escHtml(h.niche)}</span>
|
| 523 |
+
<span class="text-outline text-xs flex-shrink-0">${h.time}</span>
|
| 524 |
+
</div>`).join('');
|
| 525 |
+
}
|
| 526 |
+
function loadFromHistory(niche) {
|
| 527 |
+
setMode('niche');
|
| 528 |
+
document.getElementById('niche-input').value = niche;
|
| 529 |
+
toggleHistory();
|
| 530 |
+
startResearch();
|
| 531 |
+
}
|
| 532 |
+
function toggleHistory() { const d = document.getElementById('history-drawer'); d.classList.toggle('hidden'); }
|
| 533 |
+
|
| 534 |
+
// Utilities
|
| 535 |
+
function downloadReport() {
|
| 536 |
+
if (!lastReport) { showToast('Run research first.'); return; }
|
| 537 |
+
const blob = new Blob([lastReport], {type:'text/plain'});
|
| 538 |
+
const url = URL.createObjectURL(blob);
|
| 539 |
+
const a = document.createElement('a'); a.href=url; a.download=`${lastNiche.replace(/\s+/g,'_')}_Trend_Report.txt`;
|
| 540 |
+
document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url);
|
| 541 |
+
showToast('Report downloaded!');
|
| 542 |
+
}
|
| 543 |
+
function copyReport() { navigator.clipboard.writeText(lastReport).then(() => showToast('Report copied to clipboard!')); }
|
| 544 |
+
function copyText(text) { navigator.clipboard.writeText(text).then(() => showToast('Copied!')); }
|
| 545 |
+
function shareReport() { if (navigator.share) navigator.share({title:`Trend Report: ${lastNiche}`, text: lastReport.slice(0,500)+'...' }); else showToast('Share not supported in this browser.'); }
|
| 546 |
+
function showToast(msg) { const t = document.getElementById('toast'); t.textContent=msg; t.classList.add('show'); setTimeout(()=>t.classList.remove('show'),2800); }
|
| 547 |
+
function show(id) { document.getElementById(id).classList.remove('hidden-section'); }
|
| 548 |
+
function hide(id) { document.getElementById(id).classList.add('hidden-section'); }
|
| 549 |
+
function sleep(ms) { return new Promise(r=>setTimeout(r,ms)); }
|
| 550 |
+
function escHtml(s) { return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
| 551 |
+
function escAttr(s) { return String(s).replace(/"/g,'"').replace(/'/g,'''); }
|
| 552 |
+
|
| 553 |
+
// Enter key
|
| 554 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 555 |
+
document.getElementById('niche-input').addEventListener('keydown', e => { if(e.key==='Enter') startResearch(); });
|
| 556 |
+
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'); });
|
| 557 |
+
});
|
| 558 |
+
</script>
|
| 559 |
+
</body>
|
| 560 |
+
</html>
|