Spaces:
Paused
Paused
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |
| <title>God-Mode Browser Dashboard</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Sora:wght@300;400;500;600;700;800&display=swap" rel="stylesheet"> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| /* Base styles and scrollbar hiding */ | |
| body { | |
| -webkit-tap-highlight-color: transparent; | |
| background-color: #09090b; /* Very dark background */ | |
| font-family: 'Sora', sans-serif; | |
| } | |
| .hide-scrollbar::-webkit-scrollbar { | |
| display: none; | |
| } | |
| .hide-scrollbar { | |
| -ms-overflow-style: none; | |
| scrollbar-width: none; | |
| } | |
| /* Smooth Animations */ | |
| .scale-up { | |
| animation: scaleUp 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards; | |
| } | |
| .fade-in { | |
| animation: fadeIn 0.3s ease-out forwards; | |
| } | |
| @keyframes scaleUp { | |
| from { transform: scale(0.9) translateY(15px); opacity: 0; } | |
| to { transform: scale(1) translateY(0); opacity: 1; } | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; } | |
| to { opacity: 1; } | |
| } | |
| /* Ambient glow for the active tabs */ | |
| .tab-glow { | |
| position: absolute; | |
| width: 100px; | |
| height: 100px; | |
| background: radial-gradient(circle, rgba(255,111,76,0.15) 0%, rgba(0,0,0,0) 70%); | |
| top: -30px; | |
| right: -30px; | |
| pointer-events: none; | |
| } | |
| /* Zero-Latency Video Streaming Container */ | |
| #stream-container { | |
| width: 100%; | |
| height: calc(100vh - 5rem); | |
| position: absolute; | |
| top: 5rem; | |
| left: 0; | |
| z-index: 10; | |
| display: none; | |
| background-color: #000; | |
| } | |
| #stream-image { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: contain; | |
| pointer-events: auto; | |
| } | |
| </style> | |
| </head> | |
| <body class="text-white h-screen w-screen overflow-hidden flex flex-col fade-in"> | |
| <div class="h-20 flex items-center justify-between px-5 z-20 shrink-0 bg-[#09090b]/90 backdrop-blur-md border-b border-gray-800/50"> | |
| <button id="toggle-tabs-btn" class="px-4 py-2 bg-[#18181b] border border-gray-800 rounded-lg text-sm font-semibold flex items-center gap-2 hover:bg-[#27272a] transition-all shadow-lg active:scale-95"> | |
| <span id="tab-count-badge" class="w-5 h-5 bg-[#ff6f4c] text-white rounded-full flex items-center justify-center text-[10px] shadow-sm">1</span> | |
| <span id="tab-toggle-text">Active Tabs</span> | |
| </button> | |
| <div id="connection-status" class="flex items-center gap-2 text-xs font-medium text-gray-400"> | |
| <div id="status-dot" class="w-2 h-2 rounded-full bg-red-500 animate-pulse"></div> | |
| <span id="status-text">Connecting...</span> | |
| </div> | |
| <button id="add-tab-btn" class="p-2 flex items-center justify-center text-gray-400 hover:text-white transition-transform active:scale-90"> | |
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"> | |
| <line x1="12" y1="5" x2="12" y2="19"></line> | |
| <line x1="5" y1="12" x2="19" y2="12"></line> | |
| </svg> | |
| </button> | |
| </div> | |
| <div id="stream-container" class="fade-in"> | |
| <img id="stream-image" src="" alt="Awaiting Stream..."/> | |
| </div> | |
| <div id="tab-scroll-area" class="flex-1 overflow-y-auto hide-scrollbar p-6 flex flex-col items-center justify-start pt-2 pb-24 cursor-default z-30 bg-[#09090b]"> | |
| <div id="tabs-grid" class="w-full"> | |
| </div> | |
| </div> | |
| <script> | |
| // --- State Management --- | |
| let tabs = [{ id: Date.now(), title: "Initial Tab" }]; | |
| let activeTabId = tabs[0].id; | |
| let showingTabs = true; | |
| // --- DOM Elements --- | |
| const tabScrollArea = document.getElementById('tab-scroll-area'); | |
| const tabsGrid = document.getElementById('tabs-grid'); | |
| const addTabBtn = document.getElementById('add-tab-btn'); | |
| const toggleTabsBtn = document.getElementById('toggle-tabs-btn'); | |
| const streamContainer = document.getElementById('stream-container'); | |
| const streamImage = document.getElementById('stream-image'); | |
| const tabCountBadge = document.getElementById('tab-count-badge'); | |
| const statusDot = document.getElementById('status-dot'); | |
| const statusText = document.getElementById('status-text'); | |
| // --- WebSocket Connection Engine --- | |
| const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; | |
| const ws = new WebSocket(`${protocol}//${window.location.host}`); | |
| ws.onopen = () => { | |
| statusDot.classList.replace('bg-red-500', 'bg-green-500'); | |
| statusDot.classList.remove('animate-pulse'); | |
| statusText.innerText = "Engine Online"; | |
| statusText.classList.replace('text-gray-400', 'text-green-400'); | |
| }; | |
| ws.onclose = () => { | |
| statusDot.classList.replace('bg-green-500', 'bg-red-500'); | |
| statusText.innerText = "Disconnected"; | |
| statusText.classList.replace('text-green-400', 'text-red-400'); | |
| }; | |
| ws.onmessage = (event) => { | |
| const message = JSON.parse(event.data); | |
| // Ultra-fast CDP Frame Rendering | |
| if (message.type === 'stream_frame') { | |
| streamImage.src = 'data:image/jpeg;base64,' + message.data; | |
| } | |
| }; | |
| // --- View Controller (Tabs vs Stream) --- | |
| function switchView(toTabs) { | |
| showingTabs = toTabs; | |
| if (toTabs) { | |
| tabScrollArea.style.display = 'flex'; | |
| streamContainer.style.display = 'none'; | |
| } else { | |
| tabScrollArea.style.display = 'none'; | |
| streamContainer.style.display = 'block'; | |
| } | |
| tabCountBadge.innerText = tabs.length; | |
| } | |
| toggleTabsBtn.addEventListener('click', () => switchView(!showingTabs)); | |
| // --- UI Rendering Logic --- | |
| const getTabInnerHtml = (tab) => ` | |
| <div class="w-full h-full bg-[#18181b] rounded-2xl border ${tab.id === activeTabId ? 'border-[#ff6f4c]' : 'border-gray-800/80'} hover:border-gray-600 transition-colors overflow-hidden relative flex flex-col shadow-xl pointer-events-auto"> | |
| ${tab.id === activeTabId ? '<div class="tab-glow"></div>' : ''} | |
| <div class="flex items-center justify-between px-3 py-2.5 bg-[#121214] border-b border-gray-800/50"> | |
| <div class="flex items-center gap-2.5"> | |
| <div class="w-5 h-5 bg-[#ff6f4c] rounded-md flex items-center justify-center shadow-sm"> | |
| <span class="text-[10px] font-bold text-white">${tab.title.charAt(0)}</span> | |
| </div> | |
| <span class="text-xs font-semibold text-gray-200 tracking-wide">${tab.title}</span> | |
| </div> | |
| <button class="close-tab-btn w-6 h-6 flex items-center justify-center text-gray-500 hover:text-red-400 hover:bg-red-400/10 rounded-full transition-colors" data-id="${tab.id}"> | |
| <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg> | |
| </button> | |
| </div> | |
| <div class="flex-1 p-4 flex flex-col items-center justify-center bg-[#18181b] select-tab-zone cursor-pointer"> | |
| <div class="w-14 h-14 rounded-[1rem] bg-[#1f1f23] flex items-center justify-center mb-5 border border-gray-800/60 shadow-inner"> | |
| <svg class="w-6 h-6 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" /> | |
| </svg> | |
| </div> | |
| <span class="text-[10px] text-gray-500 font-bold tracking-widest uppercase">Tap to View</span> | |
| </div> | |
| </div> | |
| `; | |
| function renderTabs() { | |
| tabsGrid.innerHTML = ''; | |
| tabCountBadge.innerText = tabs.length; | |
| if(tabs.length === 0) { | |
| tabsGrid.innerHTML = `<div class="text-center text-gray-500 mt-20 pointer-events-auto"><p class="text-lg font-medium">System Idle. No Tabs Open.</p></div>`; | |
| tabsGrid.className = "w-full max-w-[340px] mx-auto flex flex-col items-center justify-center h-full"; | |
| return; | |
| } | |
| // Grid Layout Adaptation | |
| tabsGrid.className = tabs.length === 1 ? "w-full max-w-[240px] mx-auto flex flex-col gap-6" : "w-full max-w-[340px] mx-auto grid grid-cols-2 gap-4"; | |
| tabs.forEach((tab, index) => { | |
| const tabEl = document.createElement('div'); | |
| let classes = "relative rounded-2xl p-[1px] transform transition-all duration-300 hover:scale-[1.02] active:scale-95 scale-up "; | |
| classes += "bg-gradient-to-br from-gray-700/50 to-gray-900 hover:from-[#ff6f4c] hover:to-[#ff6f4c]/50 "; | |
| classes += tabs.length === 1 ? "w-full aspect-[4/5]" : "w-full aspect-[3/4]"; | |
| tabEl.className = classes; | |
| tabEl.style.animationDelay = `${index * 0.05}s`; | |
| tabEl.innerHTML = getTabInnerHtml(tab); | |
| // Enter Stream Mode Action | |
| tabEl.querySelector('.select-tab-zone').addEventListener('click', () => { | |
| activeTabId = tab.id; | |
| if (ws.readyState === WebSocket.OPEN) { | |
| ws.send(JSON.stringify({ type: 'switch_tab', tabId: activeTabId })); | |
| } | |
| switchView(false); // Switch to video stream | |
| renderTabs(); // Update active border color | |
| }); | |
| // Close Tab Action | |
| tabEl.querySelector('.close-tab-btn').addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| tabs = tabs.filter(t => t.id !== tab.id); | |
| renderTabs(); | |
| }); | |
| tabsGrid.appendChild(tabEl); | |
| }); | |
| } | |
| // Add New Tab Action | |
| addTabBtn.addEventListener('click', () => { | |
| const newTabId = Date.now(); | |
| const newTab = { id: newTabId, title: `Target Payload` }; | |
| tabs.push(newTab); | |
| activeTabId = newTabId; | |
| // Dispatch command to Node.js backend to spawn puppeteer page | |
| if (ws.readyState === WebSocket.OPEN) { | |
| ws.send(JSON.stringify({ type: 'new_tab', tabId: newTabId })); | |
| } | |
| renderTabs(); | |
| // Auto scroll to bottom | |
| setTimeout(() => { | |
| tabScrollArea.scrollTo({ top: tabScrollArea.scrollHeight, behavior: 'smooth' }); | |
| }, 50); | |
| switchView(false); // Automatically drop into the newly spawned tab stream | |
| }); | |
| // Initialize First State | |
| renderTabs(); | |
| switchView(false); // Automatically show the stream of the first tab on load | |
| </script> | |
| </body> | |
| </html> | |