CVNSS commited on
Commit
bdb954a
·
verified ·
1 Parent(s): 7f901d8

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +118 -103
index.html CHANGED
@@ -4,7 +4,9 @@
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>Crypto Intel Fortress — Offline-First & Bulletproof</title>
7
- <meta name="description" content="Real-time Crypto Security & On-chain Monitor. 70 years of experience applied.">
 
 
8
 
9
  <!-- Favicon SVG -->
10
  <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🛡️</text></svg>">
@@ -22,16 +24,13 @@
22
  },
23
  animation: {
24
  'fade-in': 'fadeIn 0.6s cubic-bezier(0.16, 1, 0.3, 1)',
25
- 'slide-up': 'slideUp 0.4s cubic-bezier(0.16, 1, 0.3, 1)',
26
  'pulse-slow': 'pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite',
27
  },
28
  keyframes: {
29
- fadeIn: { '0%': { opacity: '0' }, '100%': { opacity: '1' } },
30
- slideUp: { '0%': { opacity: '0', transform: 'translateY(20px)' }, '100%': { opacity: '1', transform: 'translateY(0)' } }
31
  },
32
  colors: {
33
- 'fortress-bg': '#020617', // Slate 950
34
- 'fortress-card': '#0f172a', // Slate 900
35
  }
36
  }
37
  }
@@ -45,13 +44,10 @@
45
 
46
  <style>
47
  body { background-color: #000000; color: #e2e8f0; }
48
-
49
- /* Custom Scrollbar for the Elite */
50
  ::-webkit-scrollbar { width: 6px; height: 6px; }
51
  ::-webkit-scrollbar-track { background: #020617; }
52
  ::-webkit-scrollbar-thumb { background: #334155; border-radius: 3px; }
53
  ::-webkit-scrollbar-thumb:hover { background: #475569; }
54
-
55
  .glass-panel {
56
  background: rgba(15, 23, 42, 0.6);
57
  backdrop-filter: blur(16px);
@@ -59,18 +55,24 @@
59
  border: 1px solid rgba(255, 255, 255, 0.05);
60
  box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
61
  }
62
-
63
  .no-scrollbar::-webkit-scrollbar { display: none; }
64
  .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
 
 
 
 
 
 
 
65
  </style>
66
  </head>
67
  <body class="min-h-screen bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-slate-900 via-black to-black selection:bg-cyan-500/30 selection:text-cyan-200">
68
  <div id="root"></div>
69
 
70
  <script type="text/babel">
71
- const { useState, useEffect, useRef, useCallback, useMemo } = React;
72
 
73
- // ================== ICONS (SVG Optimized) ==================
74
  const Icon = ({ children, size = 20, className = "" }) => (
75
  <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
76
  {children}
@@ -79,7 +81,6 @@
79
 
80
  const icons = {
81
  shield: <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10"/>,
82
- zap: <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>,
83
  search: <><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></>,
84
  trash: <><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></>,
85
  wifi: <path d="M5 12.55a11 11 0 0 1 14.08 0M1.42 9a16 16 0 0 1 21.16 0M8.53 16.11a6 6 0 0 1 6.95 0M12 20h.01"/>,
@@ -89,7 +90,7 @@
89
  alert: <><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></>
90
  };
91
 
92
- // ================== DATA & CONFIG ==================
93
  const DEFAULT_ACCOUNTS = [
94
  { handle: "PeckShieldAlert", name: "PeckShield", cat: "Security" },
95
  { handle: "realScamSniffer", name: "Scam Sniffer", cat: "Security" },
@@ -108,7 +109,6 @@
108
 
109
  // ================== COMPONENTS ==================
110
 
111
- // 1. Skeleton Loading (Fortress Style)
112
  const Skeleton = () => (
113
  <div className="w-full h-full p-4 flex flex-col gap-4 animate-pulse-slow">
114
  <div className="flex gap-3">
@@ -122,7 +122,7 @@
122
  </div>
123
  );
124
 
125
- // 2. Twitter Card (The Core Logic)
126
  const TwitterCard = React.memo(({ account, onRemove, isOnline }) => {
127
  const containerRef = useRef(null);
128
  const [status, setStatus] = useState('loading'); // loading | loaded | error | offline
@@ -135,47 +135,68 @@
135
 
136
  setStatus('loading');
137
 
138
- // Observer for lazy loading (Performance Optimization)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  const observer = new IntersectionObserver((entries) => {
140
  if (entries[0].isIntersecting) {
141
- loadWidget();
142
  observer.disconnect();
143
  }
144
- }, { rootMargin: '50px' });
145
 
146
- if (containerRef.current) observer.observe(containerRef.current);
147
 
148
- return () => observer.disconnect();
149
- }, [account.handle, isOnline]);
150
-
151
- const loadWidget = () => {
152
- if (!window.twttr) {
153
- // Retry if script not ready
154
- setTimeout(loadWidget, 1000);
155
- return;
156
- }
157
 
158
- // Clear container
159
- const container = containerRef.current;
160
- if (!container) return;
161
- container.innerHTML = '';
162
-
163
- // Create link for widget
164
- const link = document.createElement('a');
165
- link.className = 'twitter-timeline';
166
- link.href = `https://twitter.com/${account.handle}?ref_src=twsrc%5Etfw`;
167
- link.setAttribute('data-theme', 'dark');
168
- link.setAttribute('data-chrome', 'noheader,nofooter,noborders,transparent,noscrollbar');
169
- link.setAttribute('data-height', '400');
170
- link.textContent = `Tweets by ${account.name}`;
171
- container.appendChild(link);
172
-
173
- // Initialize Widget
174
- window.twttr.widgets.load(container).then((el) => {
175
- if (el) setStatus('loaded');
176
- else setStatus('error');
177
- });
178
- };
179
 
180
  const colorMap = {
181
  Security: "text-red-400 border-red-500/30 bg-red-500/10",
@@ -189,14 +210,14 @@
189
  <div className="glass-panel rounded-2xl overflow-hidden flex flex-col h-[450px] group transition-all duration-300 hover:border-slate-600 hover:shadow-2xl hover:shadow-cyan-900/10 animate-fade-in relative">
190
 
191
  {/* Card Header */}
192
- <div className="p-4 border-b border-white/5 bg-slate-900/50 flex justify-between items-center z-10">
193
  <div className="flex items-center gap-3 overflow-hidden">
194
  <div className={`text-[10px] font-mono font-bold px-2 py-0.5 rounded border uppercase tracking-wider ${colorMap[account.cat] || colorMap.Custom}`}>
195
  {account.cat.substring(0, 3)}
196
  </div>
197
  <div className="flex flex-col min-w-0">
198
  <span className="font-bold text-slate-100 truncate text-sm leading-tight">{account.name}</span>
199
- <a href={`https://x.com/${account.handle}`} target="_blank" className="text-[11px] text-slate-500 hover:text-cyan-400 truncate flex items-center gap-1 transition-colors font-mono">
200
  @{account.handle} <Icon size={10}>{icons.external}</Icon>
201
  </a>
202
  </div>
@@ -207,28 +228,31 @@
207
  </div>
208
 
209
  {/* Card Body */}
210
- <div className="flex-1 relative bg-black/20">
211
- <div ref={containerRef} className={`w-full h-full overflow-y-auto scrollbar-thin ${status !== 'loaded' ? 'hidden' : 'block'}`}></div>
 
212
 
213
  {status === 'loading' && (
214
- <div className="absolute inset-0">
215
  <Skeleton />
216
  </div>
217
  )}
218
 
219
  {(status === 'error' || status === 'offline') && (
220
- <div className="absolute inset-0 flex flex-col items-center justify-center p-6 text-center bg-slate-900/80 backdrop-blur-sm z-20">
221
  <div className="p-3 bg-slate-800 rounded-full mb-3 text-slate-400">
222
  <Icon size={24}>{status === 'offline' ? icons.wifiOff : icons.alert}</Icon>
223
  </div>
224
  <span className="text-slate-300 text-sm font-bold mb-1">
225
- {status === 'offline' ? 'Offline Mode' : 'Widget Blocked'}
226
  </span>
227
  <p className="text-xs text-slate-500 mb-4 max-w-[200px]">
228
- {status === 'offline' ? 'Reconnect to view live intel.' : 'Browser privacy settings or local file restrictions.'}
 
 
229
  </p>
230
- <a href={`https://x.com/${account.handle}`} target="_blank" className="px-5 py-2 bg-slate-800 hover:bg-cyan-600 hover:text-black text-cyan-400 text-xs font-bold font-mono rounded-lg transition-all border border-cyan-500/30">
231
- OPEN X.COM
232
  </a>
233
  </div>
234
  )}
@@ -249,18 +273,23 @@
249
  const [input, setInput] = useState("");
250
  const [online, setOnline] = useState(navigator.onLine);
251
 
252
- // Init
253
  useEffect(() => {
254
- // Twitter Script Inject
255
- if (!document.getElementById('twitter-wjs')) {
256
- const script = document.createElement("script");
257
- script.id = 'twitter-wjs';
258
- script.src = "https://platform.twitter.com/widgets.js";
259
- script.async = true;
260
- document.body.appendChild(script);
261
- }
 
 
 
 
 
 
 
262
 
263
- // Listeners
264
  const updateOnline = () => setOnline(navigator.onLine);
265
  window.addEventListener('online', updateOnline);
266
  window.addEventListener('offline', updateOnline);
@@ -270,7 +299,6 @@
270
  };
271
  }, []);
272
 
273
- // Persistence
274
  useEffect(() => {
275
  localStorage.setItem(STORAGE_KEY, JSON.stringify(accounts));
276
  }, [accounts]);
@@ -279,8 +307,13 @@
279
  e.preventDefault();
280
  if (!input.trim()) return;
281
  let handle = input.trim();
282
- if (handle.includes('/')) handle = handle.split('/').pop().split('?')[0];
283
- handle = handle.replace('@', '');
 
 
 
 
 
284
 
285
  if (accounts.some(a => a.handle.toLowerCase() === handle.toLowerCase())) {
286
  alert("Target already monitored.");
@@ -297,12 +330,10 @@
297
  }, [accounts, activeCategory]);
298
 
299
  return (
300
- <div className="min-h-screen flex flex-col font-sans">
301
-
302
- {/* TOP BAR / HEADER */}
303
  <header className="sticky top-0 z-50 glass-panel border-t-0 border-x-0">
304
  <div className="max-w-[1920px] mx-auto">
305
- {/* Logo & Status Area */}
306
  <div className="px-4 py-4 flex flex-col md:flex-row md:items-center justify-between gap-4">
307
  <div className="flex items-center gap-4">
308
  <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-cyan-600 to-blue-700 flex items-center justify-center shadow-lg shadow-cyan-900/20">
@@ -310,31 +341,26 @@
310
  </div>
311
  <div>
312
  <h1 className="text-xl font-black tracking-tight text-white uppercase flex items-center gap-2">
313
- Crypto Fortress <span className="text-[10px] bg-slate-800 text-slate-400 px-1.5 py-0.5 rounded border border-slate-700">v2.0</span>
314
  </h1>
315
  <p className="text-xs text-slate-500 font-mono">Real-time Intel Command Center</p>
316
  </div>
317
  </div>
318
-
319
- <div className="flex items-center gap-3">
320
- <div className={`flex items-center gap-2 px-3 py-1.5 rounded-full border text-xs font-bold font-mono transition-colors ${online ? 'bg-emerald-950/30 border-emerald-500/30 text-emerald-400' : 'bg-red-950/30 border-red-500/30 text-red-400'}`}>
321
- <Icon size={14}>{online ? icons.wifi : icons.wifiOff}</Icon>
322
- <span>{online ? 'SYSTEM ONLINE' : 'DISCONNECTED'}</span>
323
- </div>
324
  </div>
325
  </div>
326
 
327
- {/* Controls Area */}
328
  <div className="border-t border-white/5 bg-black/20 px-4 py-2 flex flex-col md:flex-row gap-3 justify-between items-center">
329
- {/* Categories */}
330
- <div className="flex gap-2 overflow-x-auto no-scrollbar w-full md:w-auto mask-linear-fade pb-1 md:pb-0">
331
  {CATEGORIES.map(cat => (
332
  <button
333
  key={cat}
334
  onClick={() => setActiveCategory(cat)}
335
  className={`px-3 py-1.5 rounded-lg text-xs font-bold font-mono transition-all whitespace-nowrap border ${
336
  activeCategory === cat
337
- ? 'bg-slate-800 text-cyan-400 border-cyan-500/50 shadow-[0_0_15px_rgba(6,182,212,0.15)]'
338
  : 'border-transparent text-slate-500 hover:text-slate-300 hover:bg-white/5'
339
  }`}
340
  >
@@ -342,10 +368,8 @@
342
  </button>
343
  ))}
344
  </div>
345
-
346
- {/* Add Input */}
347
  <form onSubmit={addAccount} className="flex w-full md:w-auto relative group">
348
- <div className="absolute inset-y-0 left-3 flex items-center pointer-events-none text-slate-600 group-focus-within:text-cyan-500 transition-colors">
349
  <Icon size={14}>{icons.plus}</Icon>
350
  </div>
351
  <input
@@ -353,9 +377,9 @@
353
  value={input}
354
  onChange={(e) => setInput(e.target.value)}
355
  placeholder="Add target handle..."
356
- className="w-full md:w-64 bg-slate-900/50 border border-slate-700 text-slate-200 text-xs rounded-l-lg pl-9 pr-3 py-2 focus:outline-none focus:border-cyan-500 focus:ring-1 focus:ring-cyan-500 placeholder-slate-600 font-mono transition-all"
357
  />
358
- <button className="bg-slate-800 hover:bg-cyan-700 text-white px-4 py-2 rounded-r-lg text-xs font-bold border border-l-0 border-slate-700 hover:border-cyan-600 transition-all">
359
  ADD
360
  </button>
361
  </form>
@@ -378,22 +402,13 @@
378
  </div>
379
  ) : (
380
  <div className="flex flex-col items-center justify-center h-[50vh] text-slate-600">
381
- <div className="p-4 bg-slate-900 rounded-full mb-4 animate-pulse-slow">
382
  <Icon size={48} className="opacity-50">{icons.search}</Icon>
383
  </div>
384
  <h2 className="text-xl font-bold text-slate-500">No Intelligence Found</h2>
385
- <p className="text-sm">Select a different sector or add a new target.</p>
386
  </div>
387
  )}
388
  </main>
389
-
390
- {/* FOOTER */}
391
- <footer className="border-t border-slate-800/50 bg-black py-4 text-center">
392
- <p className="text-[10px] text-slate-600 font-mono uppercase tracking-widest">
393
- Secure Environment • Encrypted Storage • No Tracking
394
- </p>
395
- </footer>
396
-
397
  </div>
398
  );
399
  };
 
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>Crypto Intel Fortress — Offline-First & Bulletproof</title>
7
+ <meta name="description" content="Real-time Crypto Security & On-chain Monitor.">
8
+ <!-- Fix referrer policy for Twitter Widgets -->
9
+ <meta name="referrer" content="no-referrer-when-downgrade">
10
 
11
  <!-- Favicon SVG -->
12
  <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🛡️</text></svg>">
 
24
  },
25
  animation: {
26
  'fade-in': 'fadeIn 0.6s cubic-bezier(0.16, 1, 0.3, 1)',
 
27
  'pulse-slow': 'pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite',
28
  },
29
  keyframes: {
30
+ fadeIn: { '0%': { opacity: '0' }, '100%': { opacity: '1' } }
 
31
  },
32
  colors: {
33
+ 'fortress-bg': '#020617',
 
34
  }
35
  }
36
  }
 
44
 
45
  <style>
46
  body { background-color: #000000; color: #e2e8f0; }
 
 
47
  ::-webkit-scrollbar { width: 6px; height: 6px; }
48
  ::-webkit-scrollbar-track { background: #020617; }
49
  ::-webkit-scrollbar-thumb { background: #334155; border-radius: 3px; }
50
  ::-webkit-scrollbar-thumb:hover { background: #475569; }
 
51
  .glass-panel {
52
  background: rgba(15, 23, 42, 0.6);
53
  backdrop-filter: blur(16px);
 
55
  border: 1px solid rgba(255, 255, 255, 0.05);
56
  box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
57
  }
 
58
  .no-scrollbar::-webkit-scrollbar { display: none; }
59
  .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
60
+
61
+ /* Force twitter widget container height */
62
+ .twitter-container iframe {
63
+ width: 100% !important;
64
+ height: 100% !important;
65
+ border-radius: 8px;
66
+ }
67
  </style>
68
  </head>
69
  <body class="min-h-screen bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-slate-900 via-black to-black selection:bg-cyan-500/30 selection:text-cyan-200">
70
  <div id="root"></div>
71
 
72
  <script type="text/babel">
73
+ const { useState, useEffect, useRef, useMemo } = React;
74
 
75
+ // ================== ICONS ==================
76
  const Icon = ({ children, size = 20, className = "" }) => (
77
  <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
78
  {children}
 
81
 
82
  const icons = {
83
  shield: <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10"/>,
 
84
  search: <><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></>,
85
  trash: <><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></>,
86
  wifi: <path d="M5 12.55a11 11 0 0 1 14.08 0M1.42 9a16 16 0 0 1 21.16 0M8.53 16.11a6 6 0 0 1 6.95 0M12 20h.01"/>,
 
90
  alert: <><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></>
91
  };
92
 
93
+ // ================== DATA ==================
94
  const DEFAULT_ACCOUNTS = [
95
  { handle: "PeckShieldAlert", name: "PeckShield", cat: "Security" },
96
  { handle: "realScamSniffer", name: "Scam Sniffer", cat: "Security" },
 
109
 
110
  // ================== COMPONENTS ==================
111
 
 
112
  const Skeleton = () => (
113
  <div className="w-full h-full p-4 flex flex-col gap-4 animate-pulse-slow">
114
  <div className="flex gap-3">
 
122
  </div>
123
  );
124
 
125
+ // Improved Twitter Card specifically for Hugging Face / Strict Environments
126
  const TwitterCard = React.memo(({ account, onRemove, isOnline }) => {
127
  const containerRef = useRef(null);
128
  const [status, setStatus] = useState('loading'); // loading | loaded | error | offline
 
135
 
136
  setStatus('loading');
137
 
138
+ let isMounted = true;
139
+ const container = containerRef.current;
140
+
141
+ const initWidget = () => {
142
+ if (!window.twttr || !window.twttr.widgets) {
143
+ setTimeout(initWidget, 500);
144
+ return;
145
+ }
146
+
147
+ if (!container) return;
148
+ container.innerHTML = ''; // Clean up
149
+
150
+ // Use createTimeline instead of scanning DOM (More reliable for React/SPA)
151
+ window.twttr.widgets.createTimeline(
152
+ {
153
+ sourceType: 'profile',
154
+ screenName: account.handle
155
+ },
156
+ container,
157
+ {
158
+ theme: 'dark',
159
+ height: 400,
160
+ chrome: 'noheader,nofooter,noborders,transparent,noscrollbar',
161
+ dnt: true // Do Not Track - helps with privacy blockers
162
+ }
163
+ ).then((el) => {
164
+ if (isMounted) {
165
+ if (el) {
166
+ setStatus('loaded');
167
+ } else {
168
+ // Widget returned null (Blocked or handle invalid)
169
+ setStatus('error');
170
+ }
171
+ }
172
+ }).catch(() => {
173
+ if (isMounted) setStatus('error');
174
+ });
175
+ };
176
+
177
+ // Observer for lazy loading
178
  const observer = new IntersectionObserver((entries) => {
179
  if (entries[0].isIntersecting) {
180
+ initWidget();
181
  observer.disconnect();
182
  }
183
+ }, { rootMargin: '100px' });
184
 
185
+ if (container) observer.observe(container);
186
 
187
+ // Timeout Safety: If widget takes too long (e.g. Hugging Face blocks it), show error/link
188
+ const timer = setTimeout(() => {
189
+ if (isMounted && status === 'loading') {
190
+ setStatus('error');
191
+ }
192
+ }, 4000); // 4 seconds timeout
 
 
 
193
 
194
+ return () => {
195
+ isMounted = false;
196
+ observer.disconnect();
197
+ clearTimeout(timer);
198
+ };
199
+ }, [account.handle, isOnline]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
 
201
  const colorMap = {
202
  Security: "text-red-400 border-red-500/30 bg-red-500/10",
 
210
  <div className="glass-panel rounded-2xl overflow-hidden flex flex-col h-[450px] group transition-all duration-300 hover:border-slate-600 hover:shadow-2xl hover:shadow-cyan-900/10 animate-fade-in relative">
211
 
212
  {/* Card Header */}
213
+ <div className="p-4 border-b border-white/5 bg-slate-900/50 flex justify-between items-center z-10 shrink-0">
214
  <div className="flex items-center gap-3 overflow-hidden">
215
  <div className={`text-[10px] font-mono font-bold px-2 py-0.5 rounded border uppercase tracking-wider ${colorMap[account.cat] || colorMap.Custom}`}>
216
  {account.cat.substring(0, 3)}
217
  </div>
218
  <div className="flex flex-col min-w-0">
219
  <span className="font-bold text-slate-100 truncate text-sm leading-tight">{account.name}</span>
220
+ <a href={`https://x.com/${account.handle}`} target="_blank" rel="noopener noreferrer" className="text-[11px] text-slate-500 hover:text-cyan-400 truncate flex items-center gap-1 transition-colors font-mono">
221
  @{account.handle} <Icon size={10}>{icons.external}</Icon>
222
  </a>
223
  </div>
 
228
  </div>
229
 
230
  {/* Card Body */}
231
+ <div className="flex-1 relative bg-black/20 overflow-hidden">
232
+ {/* Twitter Container */}
233
+ <div ref={containerRef} className={`w-full h-full overflow-y-auto twitter-container scrollbar-thin ${status === 'loaded' ? 'opacity-100' : 'opacity-0'} transition-opacity duration-500`}></div>
234
 
235
  {status === 'loading' && (
236
+ <div className="absolute inset-0 pointer-events-none">
237
  <Skeleton />
238
  </div>
239
  )}
240
 
241
  {(status === 'error' || status === 'offline') && (
242
+ <div className="absolute inset-0 flex flex-col items-center justify-center p-6 text-center bg-slate-900/90 backdrop-blur-sm z-20">
243
  <div className="p-3 bg-slate-800 rounded-full mb-3 text-slate-400">
244
  <Icon size={24}>{status === 'offline' ? icons.wifiOff : icons.alert}</Icon>
245
  </div>
246
  <span className="text-slate-300 text-sm font-bold mb-1">
247
+ {status === 'offline' ? 'Offline' : 'Feed Unavailable'}
248
  </span>
249
  <p className="text-xs text-slate-500 mb-4 max-w-[200px]">
250
+ {status === 'offline'
251
+ ? 'Reconnect to internet.'
252
+ : 'Twitter restricts embedding in this environment (HF/AdBlock).'}
253
  </p>
254
+ <a href={`https://x.com/${account.handle}`} target="_blank" rel="noopener noreferrer" className="px-5 py-2 bg-slate-800 hover:bg-cyan-600 hover:text-black text-cyan-400 text-xs font-bold font-mono rounded-lg transition-all border border-cyan-500/30 flex items-center gap-2">
255
+ OPEN ON X.COM <Icon size={12}>{icons.external}</Icon>
256
  </a>
257
  </div>
258
  )}
 
273
  const [input, setInput] = useState("");
274
  const [online, setOnline] = useState(navigator.onLine);
275
 
 
276
  useEffect(() => {
277
+ // Initialize Twitter Widget Script Globally
278
+ window.twttr = (function(d, s, id) {
279
+ var js, fjs = d.getElementsByTagName(s)[0],
280
+ t = window.twttr || {};
281
+ if (d.getElementById(id)) return t;
282
+ js = d.createElement(s);
283
+ js.id = id;
284
+ js.src = "https://platform.twitter.com/widgets.js";
285
+ fjs.parentNode.insertBefore(js, fjs);
286
+ t._e = [];
287
+ t.ready = function(f) {
288
+ t._e.push(f);
289
+ };
290
+ return t;
291
+ }(document, "script", "twitter-wjs"));
292
 
 
293
  const updateOnline = () => setOnline(navigator.onLine);
294
  window.addEventListener('online', updateOnline);
295
  window.addEventListener('offline', updateOnline);
 
299
  };
300
  }, []);
301
 
 
302
  useEffect(() => {
303
  localStorage.setItem(STORAGE_KEY, JSON.stringify(accounts));
304
  }, [accounts]);
 
307
  e.preventDefault();
308
  if (!input.trim()) return;
309
  let handle = input.trim();
310
+ // Clean URL inputs
311
+ try {
312
+ const url = new URL(handle);
313
+ handle = url.pathname.split('/').filter(Boolean).pop();
314
+ } catch (e) {}
315
+
316
+ handle = handle.replace('@', '').replace('?', '').split('/')[0];
317
 
318
  if (accounts.some(a => a.handle.toLowerCase() === handle.toLowerCase())) {
319
  alert("Target already monitored.");
 
330
  }, [accounts, activeCategory]);
331
 
332
  return (
333
+ <div className="min-h-screen flex flex-col font-sans pb-10">
334
+ {/* HEADER */}
 
335
  <header className="sticky top-0 z-50 glass-panel border-t-0 border-x-0">
336
  <div className="max-w-[1920px] mx-auto">
 
337
  <div className="px-4 py-4 flex flex-col md:flex-row md:items-center justify-between gap-4">
338
  <div className="flex items-center gap-4">
339
  <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-cyan-600 to-blue-700 flex items-center justify-center shadow-lg shadow-cyan-900/20">
 
341
  </div>
342
  <div>
343
  <h1 className="text-xl font-black tracking-tight text-white uppercase flex items-center gap-2">
344
+ Crypto Fortress <span className="text-[10px] bg-slate-800 text-slate-400 px-1.5 py-0.5 rounded border border-slate-700">HF-Fixed</span>
345
  </h1>
346
  <p className="text-xs text-slate-500 font-mono">Real-time Intel Command Center</p>
347
  </div>
348
  </div>
349
+ <div className={`flex items-center gap-2 px-3 py-1.5 rounded-full border text-xs font-bold font-mono transition-colors ${online ? 'bg-emerald-950/30 border-emerald-500/30 text-emerald-400' : 'bg-red-950/30 border-red-500/30 text-red-400'}`}>
350
+ <Icon size={14}>{online ? icons.wifi : icons.wifiOff}</Icon>
351
+ <span>{online ? 'ONLINE' : 'OFFLINE'}</span>
 
 
 
352
  </div>
353
  </div>
354
 
 
355
  <div className="border-t border-white/5 bg-black/20 px-4 py-2 flex flex-col md:flex-row gap-3 justify-between items-center">
356
+ <div className="flex gap-2 overflow-x-auto no-scrollbar w-full md:w-auto pb-1 md:pb-0">
 
357
  {CATEGORIES.map(cat => (
358
  <button
359
  key={cat}
360
  onClick={() => setActiveCategory(cat)}
361
  className={`px-3 py-1.5 rounded-lg text-xs font-bold font-mono transition-all whitespace-nowrap border ${
362
  activeCategory === cat
363
+ ? 'bg-slate-800 text-cyan-400 border-cyan-500/50'
364
  : 'border-transparent text-slate-500 hover:text-slate-300 hover:bg-white/5'
365
  }`}
366
  >
 
368
  </button>
369
  ))}
370
  </div>
 
 
371
  <form onSubmit={addAccount} className="flex w-full md:w-auto relative group">
372
+ <div className="absolute inset-y-0 left-3 flex items-center pointer-events-none text-slate-600">
373
  <Icon size={14}>{icons.plus}</Icon>
374
  </div>
375
  <input
 
377
  value={input}
378
  onChange={(e) => setInput(e.target.value)}
379
  placeholder="Add target handle..."
380
+ className="w-full md:w-64 bg-slate-900/50 border border-slate-700 text-slate-200 text-xs rounded-l-lg pl-9 pr-3 py-2 focus:outline-none focus:border-cyan-500 font-mono transition-all"
381
  />
382
+ <button className="bg-slate-800 hover:bg-cyan-700 text-white px-4 py-2 rounded-r-lg text-xs font-bold border border-l-0 border-slate-700">
383
  ADD
384
  </button>
385
  </form>
 
402
  </div>
403
  ) : (
404
  <div className="flex flex-col items-center justify-center h-[50vh] text-slate-600">
405
+ <div className="p-4 bg-slate-900 rounded-full mb-4">
406
  <Icon size={48} className="opacity-50">{icons.search}</Icon>
407
  </div>
408
  <h2 className="text-xl font-bold text-slate-500">No Intelligence Found</h2>
 
409
  </div>
410
  )}
411
  </main>
 
 
 
 
 
 
 
 
412
  </div>
413
  );
414
  };