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

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +405 -19
index.html CHANGED
@@ -1,19 +1,405 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="vi" class="scroll-smooth">
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>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>">
11
+
12
+ <!-- Tailwind CSS -->
13
+ <script src="https://cdn.tailwindcss.com"></script>
14
+ <script>
15
+ tailwind.config = {
16
+ darkMode: 'class',
17
+ theme: {
18
+ extend: {
19
+ fontFamily: {
20
+ sans: ['Inter', 'system-ui', 'sans-serif'],
21
+ mono: ['JetBrains Mono', 'monospace'],
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
+ }
38
+ }
39
+ </script>
40
+
41
+ <!-- React Dependencies -->
42
+ <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
43
+ <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
44
+ <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
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);
58
+ -webkit-backdrop-filter: blur(16px);
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}
77
+ </svg>
78
+ );
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"/>,
86
+ wifiOff: <><line x1="1" y1="1" x2="23" y2="23"/><path d="M16.72 11.06A10.94 10.94 0 0 1 19 12.55"/><path d="M5 12.55a11 11 0 0 1 7.67-9.77"/><path d="M8.53 16.11a6 6 0 0 1 6.95 0"/><path d="M12 20h.01"/></>,
87
+ external: <><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></>,
88
+ plus: <><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></>,
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" },
96
+ { handle: "ZachXBT", name: "ZachXBT", cat: "Security" },
97
+ { handle: "CertiKAlert", name: "CertiK", cat: "Security" },
98
+ { handle: "lookonchain", name: "Lookonchain", cat: "On-chain" },
99
+ { handle: "whale_alert", name: "Whale Alert", cat: "On-chain" },
100
+ { handle: "ArkhamIntel", name: "Arkham", cat: "On-chain" },
101
+ { handle: "WuBlockchain", name: "Wu Blockchain", cat: "News" },
102
+ { handle: "Tier10k", name: "DB (Tier10k)", cat: "News" },
103
+ { handle: "DeFiLlama", name: "DeFiLlama", cat: "Data" }
104
+ ];
105
+
106
+ const CATEGORIES = ["All", "Security", "On-chain", "News", "Data", "Custom"];
107
+ const STORAGE_KEY = 'CRYPTO_FORTRESS_V70';
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">
115
+ <div className="w-10 h-10 rounded-full bg-slate-800/50"></div>
116
+ <div className="flex-1 space-y-2">
117
+ <div className="h-3 bg-slate-800/50 rounded w-1/3"></div>
118
+ <div className="h-2 bg-slate-800/30 rounded w-1/4"></div>
119
+ </div>
120
+ </div>
121
+ <div className="flex-1 bg-slate-800/20 rounded-lg border border-slate-800/30"></div>
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
129
+
130
+ useEffect(() => {
131
+ if (!isOnline) {
132
+ setStatus('offline');
133
+ return;
134
+ }
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",
182
+ "On-chain": "text-cyan-400 border-cyan-500/30 bg-cyan-500/10",
183
+ News: "text-emerald-400 border-emerald-500/30 bg-emerald-500/10",
184
+ Data: "text-purple-400 border-purple-500/30 bg-purple-500/10",
185
+ Custom: "text-amber-400 border-amber-500/30 bg-amber-500/10"
186
+ };
187
+
188
+ return (
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>
203
+ </div>
204
+ <button onClick={() => onRemove(account.handle)} className="text-slate-600 hover:text-red-400 p-2 rounded-lg hover:bg-white/5 transition-all opacity-0 group-hover:opacity-100 focus:opacity-100" title="Remove Feed">
205
+ <Icon size={16}>{icons.trash}</Icon>
206
+ </button>
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
+ )}
235
+ </div>
236
+ </div>
237
+ );
238
+ });
239
+
240
+ // ================== MAIN APP ==================
241
+ const App = () => {
242
+ const [accounts, setAccounts] = useState(() => {
243
+ try {
244
+ const saved = localStorage.getItem(STORAGE_KEY);
245
+ return saved ? JSON.parse(saved) : DEFAULT_ACCOUNTS;
246
+ } catch { return DEFAULT_ACCOUNTS; }
247
+ });
248
+ const [activeCategory, setActiveCategory] = useState("All");
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);
267
+ return () => {
268
+ window.removeEventListener('online', updateOnline);
269
+ window.removeEventListener('offline', updateOnline);
270
+ };
271
+ }, []);
272
+
273
+ // Persistence
274
+ useEffect(() => {
275
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(accounts));
276
+ }, [accounts]);
277
+
278
+ const addAccount = (e) => {
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.");
287
+ return;
288
+ }
289
+
290
+ setAccounts([{ handle, name: handle, cat: "Custom" }, ...accounts]);
291
+ setInput("");
292
+ };
293
+
294
+ const filteredAccounts = useMemo(() => {
295
+ if (activeCategory === "All") return accounts;
296
+ return accounts.filter(acc => acc.cat === activeCategory);
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">
309
+ <Icon size={24} className="text-white">{icons.shield}</Icon>
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
+ >
341
+ {cat.toUpperCase()}
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
352
+ type="text"
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>
362
+ </div>
363
+ </div>
364
+ </header>
365
+
366
+ {/* MAIN GRID */}
367
+ <main className="flex-1 p-4 md:p-6 w-full max-w-[1920px] mx-auto">
368
+ {filteredAccounts.length > 0 ? (
369
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-6">
370
+ {filteredAccounts.map(acc => (
371
+ <TwitterCard
372
+ key={acc.handle}
373
+ account={acc}
374
+ onRemove={(h) => setAccounts(accounts.filter(a => a.handle !== h))}
375
+ isOnline={online}
376
+ />
377
+ ))}
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
+ };
400
+
401
+ const root = ReactDOM.createRoot(document.getElementById('root'));
402
+ root.render(<App />);
403
+ </script>
404
+ </body>
405
+ </html>