senku21230 commited on
Commit
e256ede
·
verified ·
1 Parent(s): 41ad694

Create public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +266 -0
public/index.html ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
+ <title>God-Mode Browser Dashboard</title>
7
+
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Sora:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
11
+
12
+ <script src="https://cdn.tailwindcss.com"></script>
13
+ <style>
14
+ /* Base styles and scrollbar hiding */
15
+ body {
16
+ -webkit-tap-highlight-color: transparent;
17
+ background-color: #09090b; /* Very dark background */
18
+ font-family: 'Sora', sans-serif;
19
+ }
20
+ .hide-scrollbar::-webkit-scrollbar {
21
+ display: none;
22
+ }
23
+ .hide-scrollbar {
24
+ -ms-overflow-style: none;
25
+ scrollbar-width: none;
26
+ }
27
+
28
+ /* Smooth Animations */
29
+ .scale-up {
30
+ animation: scaleUp 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
31
+ }
32
+ .fade-in {
33
+ animation: fadeIn 0.3s ease-out forwards;
34
+ }
35
+
36
+ @keyframes scaleUp {
37
+ from { transform: scale(0.9) translateY(15px); opacity: 0; }
38
+ to { transform: scale(1) translateY(0); opacity: 1; }
39
+ }
40
+ @keyframes fadeIn {
41
+ from { opacity: 0; }
42
+ to { opacity: 1; }
43
+ }
44
+
45
+ /* Ambient glow for the active tabs */
46
+ .tab-glow {
47
+ position: absolute;
48
+ width: 100px;
49
+ height: 100px;
50
+ background: radial-gradient(circle, rgba(255,111,76,0.15) 0%, rgba(0,0,0,0) 70%);
51
+ top: -30px;
52
+ right: -30px;
53
+ pointer-events: none;
54
+ }
55
+
56
+ /* Zero-Latency Video Streaming Container */
57
+ #stream-container {
58
+ width: 100%;
59
+ height: calc(100vh - 5rem);
60
+ position: absolute;
61
+ top: 5rem;
62
+ left: 0;
63
+ z-index: 10;
64
+ display: none;
65
+ background-color: #000;
66
+ }
67
+
68
+ #stream-image {
69
+ width: 100%;
70
+ height: 100%;
71
+ object-fit: contain;
72
+ pointer-events: auto;
73
+ }
74
+ </style>
75
+ </head>
76
+ <body class="text-white h-screen w-screen overflow-hidden flex flex-col fade-in">
77
+
78
+ <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">
79
+
80
+ <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">
81
+ <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>
82
+ <span id="tab-toggle-text">Active Tabs</span>
83
+ </button>
84
+
85
+ <div id="connection-status" class="flex items-center gap-2 text-xs font-medium text-gray-400">
86
+ <div id="status-dot" class="w-2 h-2 rounded-full bg-red-500 animate-pulse"></div>
87
+ <span id="status-text">Connecting...</span>
88
+ </div>
89
+
90
+ <button id="add-tab-btn" class="p-2 flex items-center justify-center text-gray-400 hover:text-white transition-transform active:scale-90">
91
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
92
+ <line x1="12" y1="5" x2="12" y2="19"></line>
93
+ <line x1="5" y1="12" x2="19" y2="12"></line>
94
+ </svg>
95
+ </button>
96
+ </div>
97
+
98
+ <div id="stream-container" class="fade-in">
99
+ <img id="stream-image" src="" alt="Awaiting Stream..."/>
100
+ </div>
101
+
102
+ <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]">
103
+ <div id="tabs-grid" class="w-full">
104
+ </div>
105
+ </div>
106
+
107
+ <script>
108
+ // --- State Management ---
109
+ let tabs = [{ id: Date.now(), title: "Initial Tab" }];
110
+ let activeTabId = tabs[0].id;
111
+ let showingTabs = true;
112
+
113
+ // --- DOM Elements ---
114
+ const tabScrollArea = document.getElementById('tab-scroll-area');
115
+ const tabsGrid = document.getElementById('tabs-grid');
116
+ const addTabBtn = document.getElementById('add-tab-btn');
117
+ const toggleTabsBtn = document.getElementById('toggle-tabs-btn');
118
+ const streamContainer = document.getElementById('stream-container');
119
+ const streamImage = document.getElementById('stream-image');
120
+ const tabCountBadge = document.getElementById('tab-count-badge');
121
+ const statusDot = document.getElementById('status-dot');
122
+ const statusText = document.getElementById('status-text');
123
+
124
+ // --- WebSocket Connection Engine ---
125
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
126
+ const ws = new WebSocket(`${protocol}//${window.location.host}`);
127
+
128
+ ws.onopen = () => {
129
+ statusDot.classList.replace('bg-red-500', 'bg-green-500');
130
+ statusDot.classList.remove('animate-pulse');
131
+ statusText.innerText = "Engine Online";
132
+ statusText.classList.replace('text-gray-400', 'text-green-400');
133
+ };
134
+
135
+ ws.onclose = () => {
136
+ statusDot.classList.replace('bg-green-500', 'bg-red-500');
137
+ statusText.innerText = "Disconnected";
138
+ statusText.classList.replace('text-green-400', 'text-red-400');
139
+ };
140
+
141
+ ws.onmessage = (event) => {
142
+ const message = JSON.parse(event.data);
143
+
144
+ // Ultra-fast CDP Frame Rendering
145
+ if (message.type === 'stream_frame') {
146
+ streamImage.src = 'data:image/jpeg;base64,' + message.data;
147
+ }
148
+ };
149
+
150
+ // --- View Controller (Tabs vs Stream) ---
151
+ function switchView(toTabs) {
152
+ showingTabs = toTabs;
153
+ if (toTabs) {
154
+ tabScrollArea.style.display = 'flex';
155
+ streamContainer.style.display = 'none';
156
+ } else {
157
+ tabScrollArea.style.display = 'none';
158
+ streamContainer.style.display = 'block';
159
+ }
160
+ tabCountBadge.innerText = tabs.length;
161
+ }
162
+
163
+ toggleTabsBtn.addEventListener('click', () => switchView(!showingTabs));
164
+
165
+ // --- UI Rendering Logic ---
166
+ const getTabInnerHtml = (tab) => `
167
+ <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">
168
+ ${tab.id === activeTabId ? '<div class="tab-glow"></div>' : ''}
169
+
170
+ <div class="flex items-center justify-between px-3 py-2.5 bg-[#121214] border-b border-gray-800/50">
171
+ <div class="flex items-center gap-2.5">
172
+ <div class="w-5 h-5 bg-[#ff6f4c] rounded-md flex items-center justify-center shadow-sm">
173
+ <span class="text-[10px] font-bold text-white">${tab.title.charAt(0)}</span>
174
+ </div>
175
+ <span class="text-xs font-semibold text-gray-200 tracking-wide">${tab.title}</span>
176
+ </div>
177
+ <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}">
178
+ <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>
179
+ </button>
180
+ </div>
181
+
182
+ <div class="flex-1 p-4 flex flex-col items-center justify-center bg-[#18181b] select-tab-zone cursor-pointer">
183
+ <div class="w-14 h-14 rounded-[1rem] bg-[#1f1f23] flex items-center justify-center mb-5 border border-gray-800/60 shadow-inner">
184
+ <svg class="w-6 h-6 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
185
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
186
+ <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" />
187
+ </svg>
188
+ </div>
189
+ <span class="text-[10px] text-gray-500 font-bold tracking-widest uppercase">Tap to View</span>
190
+ </div>
191
+ </div>
192
+ `;
193
+
194
+ function renderTabs() {
195
+ tabsGrid.innerHTML = '';
196
+ tabCountBadge.innerText = tabs.length;
197
+
198
+ if(tabs.length === 0) {
199
+ 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>`;
200
+ tabsGrid.className = "w-full max-w-[340px] mx-auto flex flex-col items-center justify-center h-full";
201
+ return;
202
+ }
203
+
204
+ // Grid Layout Adaptation
205
+ 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";
206
+
207
+ tabs.forEach((tab, index) => {
208
+ const tabEl = document.createElement('div');
209
+ let classes = "relative rounded-2xl p-[1px] transform transition-all duration-300 hover:scale-[1.02] active:scale-95 scale-up ";
210
+ classes += "bg-gradient-to-br from-gray-700/50 to-gray-900 hover:from-[#ff6f4c] hover:to-[#ff6f4c]/50 ";
211
+ classes += tabs.length === 1 ? "w-full aspect-[4/5]" : "w-full aspect-[3/4]";
212
+
213
+ tabEl.className = classes;
214
+ tabEl.style.animationDelay = `${index * 0.05}s`;
215
+ tabEl.innerHTML = getTabInnerHtml(tab);
216
+
217
+ // Enter Stream Mode Action
218
+ tabEl.querySelector('.select-tab-zone').addEventListener('click', () => {
219
+ activeTabId = tab.id;
220
+ if (ws.readyState === WebSocket.OPEN) {
221
+ ws.send(JSON.stringify({ type: 'switch_tab', tabId: activeTabId }));
222
+ }
223
+ switchView(false); // Switch to video stream
224
+ renderTabs(); // Update active border color
225
+ });
226
+
227
+ // Close Tab Action
228
+ tabEl.querySelector('.close-tab-btn').addEventListener('click', (e) => {
229
+ e.stopPropagation();
230
+ tabs = tabs.filter(t => t.id !== tab.id);
231
+ renderTabs();
232
+ });
233
+
234
+ tabsGrid.appendChild(tabEl);
235
+ });
236
+ }
237
+
238
+ // Add New Tab Action
239
+ addTabBtn.addEventListener('click', () => {
240
+ const newTabId = Date.now();
241
+ const newTab = { id: newTabId, title: `Target Payload` };
242
+ tabs.push(newTab);
243
+ activeTabId = newTabId;
244
+
245
+ // Dispatch command to Node.js backend to spawn puppeteer page
246
+ if (ws.readyState === WebSocket.OPEN) {
247
+ ws.send(JSON.stringify({ type: 'new_tab', tabId: newTabId }));
248
+ }
249
+
250
+ renderTabs();
251
+
252
+ // Auto scroll to bottom
253
+ setTimeout(() => {
254
+ tabScrollArea.scrollTo({ top: tabScrollArea.scrollHeight, behavior: 'smooth' });
255
+ }, 50);
256
+
257
+ switchView(false); // Automatically drop into the newly spawned tab stream
258
+ });
259
+
260
+ // Initialize First State
261
+ renderTabs();
262
+ switchView(false); // Automatically show the stream of the first tab on load
263
+
264
+ </script>
265
+ </body>
266
+ </html>