| <!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"> |
|
|
| |
| <div id="toast" class="toast"></div> |
|
|
| |
| <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> |
|
|
| |
| <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"> |
|
|
| |
| <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> |
|
|
| |
| <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"> |
|
|
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <section id="results-board" class="hidden-section max-w-5xl mx-auto space-y-12"> |
|
|
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <div id="platform-results-section"> |
| <div class="flex gap-2 flex-wrap mb-4" id="platform-tabs"></div> |
| <div id="platform-panes"></div> |
| </div> |
|
|
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <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') || '[]'); |
| |
| |
| window.addEventListener('load', async () => { |
| renderHistory(); |
| try { await fetch(`${BACKEND}/docs`); document.getElementById('engine-badge').style.display = 'flex'; } catch {} |
| }); |
| |
| |
| 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'; |
| } |
| } |
| |
| |
| 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`; } |
| |
| |
| 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'); } |
| |
| |
| 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) { |
| |
| const sid = `TH-${Math.floor(1000+Math.random()*9000)}-X${Math.floor(Math.random()*10)}`; |
| document.getElementById('session-line').textContent = `Session: #${sid} • Niche: ${lastNiche}`; |
| |
| |
| 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; |
| |
| |
| renderPlatformResults(data.platform_results || []); |
| |
| |
| if (data.videos && data.videos.length > 0) { |
| |
| } |
| |
| |
| 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(''); |
| |
| |
| 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) => { |
| |
| 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); |
| |
| |
| 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>`; |
| |
| |
| 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>`; |
| } |
| |
| |
| 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>`; |
| } |
| |
| |
| 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>`; |
| } |
| |
| |
| 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'); } |
| |
| |
| 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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); } |
| function escAttr(s) { return String(s).replace(/"/g,'"').replace(/'/g,'''); } |
| |
| |
| 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> |
|
|