nock2 commited on
Commit
05b98a9
·
verified ·
1 Parent(s): e3b44d8

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +785 -19
  3. prompts.txt +1 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Solana Nft Gripper
3
- emoji: 🏃
4
- colorFrom: pink
5
- colorTo: yellow
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: solana-nft-gripper
3
+ emoji: 🐳
4
+ colorFrom: green
5
+ colorTo: green
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,785 @@
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="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>🌀 Solana Image Vortex 🌀</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <!-- Psychedelic fonts -->
10
+ <link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Comic+Neue:wght@700&family=VT323&display=swap" rel="stylesheet">
11
+ <style>
12
+ body {
13
+ background-color: #000;
14
+ background-image:
15
+ radial-gradient(circle at 10% 20%, rgba(255, 0, 255, 0.1) 0%, transparent 20%),
16
+ radial-gradient(circle at 90% 80%, rgba(0, 255, 255, 0.1) 0%, transparent 20%),
17
+ repeating-linear-gradient(
18
+ 45deg,
19
+ #000,
20
+ #000 2px,
21
+ rgba(255, 255, 0, 0.05) 2px,
22
+ rgba(255, 255, 0, 0.05) 4px
23
+ );
24
+ font-family: 'VT323', monospace;
25
+ color: #0f0;
26
+ text-shadow: 0 0 5px #0f0;
27
+ }
28
+
29
+ .cyber-title {
30
+ font-family: 'Press Start 2P', cursive;
31
+ text-transform: uppercase;
32
+ letter-spacing: 2px;
33
+ animation: glitch 1s linear infinite;
34
+ }
35
+
36
+ .cyber-subtitle {
37
+ font-family: 'Comic Neue', cursive;
38
+ letter-spacing: 1px;
39
+ }
40
+
41
+ @keyframes glitch {
42
+ 0%, 100% { text-shadow: 0.05em 0 0 #00fffc, -0.05em -0.025em 0 #fc00ff; }
43
+ 14% { text-shadow: 0.05em 0 0 #00fffc, -0.05em -0.025em 0 #fc00ff; }
44
+ 15% { text-shadow: -0.05em 0 0 #00fffc, 0.025em 0.025em 0 #fc00ff; }
45
+ 49% { text-shadow: -0.05em 0 0 #00fffc, 0.025em 0.025em 0 #fc00ff; }
46
+ 50% { text-shadow: 0.025em 0 0 #00fffc, 0.05em 0.025em 0 #fc00ff; }
47
+ 99% { text-shadow: 0.025em 0 0 #00fffc, 0.05em 0.025em 0 #fc00ff; }
48
+ }
49
+
50
+ .cyber-input {
51
+ background: rgba(0, 0, 0, 0.7);
52
+ border: 2px solid #0f0;
53
+ color: #0f0;
54
+ font-family: 'VT323', monospace;
55
+ font-size: 1.5rem;
56
+ box-shadow: 0 0 10px #0f0;
57
+ }
58
+
59
+ .cyber-input::placeholder {
60
+ color: rgba(0, 255, 0, 0.5);
61
+ }
62
+
63
+ .cyber-button {
64
+ background: linear-gradient(45deg, #ff00ff, #00ffff);
65
+ color: black;
66
+ font-family: 'Press Start 2P', cursive;
67
+ font-size: 0.8rem;
68
+ border: none;
69
+ box-shadow: 0 0 15px #ff00ff;
70
+ position: relative;
71
+ overflow: hidden;
72
+ transition: all 0.3s;
73
+ }
74
+
75
+ .cyber-button:hover {
76
+ transform: scale(1.05);
77
+ box-shadow: 0 0 25px #00ffff;
78
+ }
79
+
80
+ .cyber-button::before {
81
+ content: '';
82
+ position: absolute;
83
+ top: -50%;
84
+ left: -50%;
85
+ width: 200%;
86
+ height: 200%;
87
+ background: linear-gradient(
88
+ to bottom right,
89
+ rgba(255, 255, 255, 0.3) 0%,
90
+ rgba(255, 255, 255, 0) 60%
91
+ );
92
+ transform: rotate(30deg);
93
+ animation: shine 3s infinite;
94
+ }
95
+
96
+ @keyframes shine {
97
+ 0% { transform: translateX(-100%) rotate(30deg); }
98
+ 20% { transform: translateX(100%) rotate(30deg); }
99
+ 100% { transform: translateX(100%) rotate(30deg); }
100
+ }
101
+
102
+ .nft-card {
103
+ background: rgba(0, 0, 0, 0.7);
104
+ border: 2px solid #ff00ff;
105
+ box-shadow: 0 0 15px #ff00ff;
106
+ transition: all 0.3s;
107
+ position: relative;
108
+ overflow: hidden;
109
+ }
110
+
111
+ .nft-card:hover {
112
+ transform: translateY(-5px) rotate(1deg);
113
+ box-shadow: 0 0 25px #00ffff;
114
+ border-color: #00ffff;
115
+ }
116
+
117
+ .nft-card::after {
118
+ content: '';
119
+ position: absolute;
120
+ top: 0;
121
+ left: 0;
122
+ right: 0;
123
+ bottom: 0;
124
+ background: linear-gradient(
125
+ 135deg,
126
+ rgba(255, 0, 255, 0.1) 0%,
127
+ rgba(0, 255, 255, 0.1) 100%
128
+ );
129
+ pointer-events: none;
130
+ }
131
+
132
+ .stats-box {
133
+ background: rgba(0, 0, 0, 0.7);
134
+ border: 2px solid #0f0;
135
+ box-shadow: 0 0 10px #0f0;
136
+ }
137
+
138
+ .stats-title {
139
+ color: #ff00ff;
140
+ text-shadow: 0 0 5px #ff00ff;
141
+ }
142
+
143
+ .stats-value {
144
+ color: #00ffff;
145
+ text-shadow: 0 0 5px #00ffff;
146
+ font-family: 'Press Start 2P', cursive;
147
+ }
148
+
149
+ .toast {
150
+ background: linear-gradient(45deg, #ff00ff, #00ffff);
151
+ color: black;
152
+ font-family: 'Press Start 2P', cursive;
153
+ font-size: 0.8rem;
154
+ border: 2px solid black;
155
+ box-shadow: 0 0 20px #0f0;
156
+ }
157
+
158
+ .selected-count {
159
+ background: linear-gradient(45deg, #ff00ff, #00ffff);
160
+ color: black;
161
+ font-family: 'Press Start 2P', cursive;
162
+ font-size: 0.7rem;
163
+ border: 2px solid black;
164
+ box-shadow: 0 0 15px #0f0;
165
+ }
166
+
167
+ .scanning-text {
168
+ animation: colorPulse 2s infinite;
169
+ }
170
+
171
+ @keyframes colorPulse {
172
+ 0% { color: #0f0; }
173
+ 50% { color: #0ff; }
174
+ 100% { color: #0f0; }
175
+ }
176
+
177
+ .loading-spinner {
178
+ border: 4px solid rgba(0, 255, 255, 0.3);
179
+ border-top: 4px solid #0f0;
180
+ border-radius: 50%;
181
+ width: 40px;
182
+ height: 40px;
183
+ animation: spin 1s linear infinite;
184
+ }
185
+
186
+ @keyframes spin {
187
+ 0% { transform: rotate(0deg); }
188
+ 100% { transform: rotate(360deg); }
189
+ }
190
+
191
+ .grid-bg {
192
+ background-image:
193
+ linear-gradient(rgba(0, 255, 255, 0.1) 1px, transparent 1px),
194
+ linear-gradient(90deg, rgba(0, 255, 255, 0.1) 1px, transparent 1px);
195
+ background-size: 20px 20px;
196
+ }
197
+
198
+ .cyber-checkbox {
199
+ appearance: none;
200
+ width: 20px;
201
+ height: 20px;
202
+ border: 2px solid #0f0;
203
+ background: rgba(0, 0, 0, 0.7);
204
+ box-shadow: 0 0 5px #0f0;
205
+ position: relative;
206
+ cursor: pointer;
207
+ }
208
+
209
+ .cyber-checkbox:checked {
210
+ background: #0f0;
211
+ }
212
+
213
+ .cyber-checkbox:checked::after {
214
+ content: '✓';
215
+ position: absolute;
216
+ color: black;
217
+ font-size: 1rem;
218
+ top: -2px;
219
+ left: 2px;
220
+ }
221
+
222
+ .footer {
223
+ border-top: 2px solid #0f0;
224
+ box-shadow: 0 0 20px #0f0;
225
+ }
226
+
227
+ .social-icon {
228
+ transition: all 0.3s;
229
+ }
230
+
231
+ .social-icon:hover {
232
+ transform: scale(1.2);
233
+ color: #ff00ff !important;
234
+ text-shadow: 0 0 10px #ff00ff;
235
+ }
236
+
237
+ .pixel-corners {
238
+ clip-path:
239
+ polygon(
240
+ 0% 5px, 5px 5px, 5px 0%, calc(100% - 5px) 0%,
241
+ calc(100% - 5px) 5px, 100% 5px, 100% calc(100% - 5px),
242
+ calc(100% - 5px) calc(100% - 5px), calc(100% - 5px) 100%,
243
+ 5px 100%, 5px calc(100% - 5px), 0% calc(100% - 5px)
244
+ );
245
+ }
246
+ </style>
247
+ </head>
248
+ <body class="min-h-screen grid-bg">
249
+ <!-- Toast notification -->
250
+ <div id="toast" class="toast fixed bottom-4 right-4 p-3 rounded-sm hidden z-50 pixel-corners">
251
+ <div class="flex items-center gap-2">
252
+ <i class="fas fa-check-circle"></i>
253
+ <span id="toastMessage">Operation completed successfully!</span>
254
+ </div>
255
+ </div>
256
+
257
+ <!-- Selected count badge -->
258
+ <div id="selectedCountBadge" class="selected-count fixed bottom-20 right-4 px-3 py-1 rounded-sm hidden z-50 pixel-corners">
259
+ <span id="selectedCount">0</span> SELECTED
260
+ </div>
261
+
262
+ <div class="container mx-auto px-4 py-12">
263
+ <div class="max-w-4xl mx-auto text-center mb-16">
264
+ <h1 class="cyber-title text-4xl md:text-6xl mb-6">
265
+ <span class="text-[#ff00ff]">🌀</span> SOLANA IMAGE VORTEX <span class="text-[#00ffff]">🌀</span>
266
+ </h1>
267
+ <p class="cyber-subtitle text-xl md:text-2xl mb-8">
268
+ DOWNLOAD PSYCHEDELIC NFT IMAGES FROM ANY SOLANA WALLET
269
+ </p>
270
+
271
+ <div class="max-w-2xl mx-auto p-6 bg-black bg-opacity-70 border-2 border-[#0f0] rounded-sm shadow-lg shadow-[#0f0] mb-12 pixel-corners">
272
+ <div class="flex flex-col md:flex-row gap-4">
273
+ <input
274
+ type="text"
275
+ id="walletAddress"
276
+ placeholder="ENTER SOLANA WALLET ADDRESS..."
277
+ class="cyber-input flex-grow px-4 py-3 rounded-sm"
278
+ >
279
+ <button
280
+ id="fetchBtn"
281
+ class="cyber-button px-6 py-3 font-semibold rounded-sm hover:bg-opacity-90 transition-all duration-300 flex items-center justify-center gap-2 pixel-corners"
282
+ >
283
+ <i class="fas fa-cloud-download-alt"></i> FETCH
284
+ </button>
285
+ </div>
286
+ <div class="mt-4 text-left text-sm text-[#0f0]">
287
+ <p>EXAMPLE WALLET: 6zsuBDfivMcPUP5mmQv26GkET9gZV5Qw5E5ZJ2N5F2Gd</p>
288
+ </div>
289
+ </div>
290
+ </div>
291
+
292
+ <div id="loading" class="hidden text-center py-12">
293
+ <div class="loading-spinner mx-auto mb-6"></div>
294
+ <p class="scanning-text text-2xl">SCANNING WALLET FOR IMAGES...</p>
295
+ </div>
296
+
297
+ <div id="stats" class="hidden mb-12">
298
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
299
+ <div class="stats-box p-6 text-center pixel-corners">
300
+ <p class="stats-title text-lg mb-2">TOTAL NFTS FOUND</p>
301
+ <p id="totalNfts" class="stats-value text-3xl">0</p>
302
+ </div>
303
+ <div class="stats-box p-6 text-center pixel-corners">
304
+ <p class="stats-title text-lg mb-2">IMAGES FOUND</p>
305
+ <p id="imageCount" class="stats-value text-3xl">0</p>
306
+ </div>
307
+ <div class="stats-box p-6 text-center pixel-corners">
308
+ <p class="stats-title text-lg mb-2">SELECTED</p>
309
+ <p id="selectedCountStat" class="stats-value text-3xl">0</p>
310
+ </div>
311
+ </div>
312
+ </div>
313
+
314
+ <div id="results" class="hidden">
315
+ <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-8 gap-4">
316
+ <h2 class="cyber-title text-3xl text-[#ff00ff]">FOUND IMAGES</h2>
317
+ <div class="flex flex-wrap gap-3 w-full sm:w-auto">
318
+ <button
319
+ id="selectAllBtn"
320
+ class="cyber-button px-4 py-2 font-semibold rounded-sm flex items-center gap-2 pixel-corners"
321
+ >
322
+ <i class="fas fa-check-square"></i> SELECT ALL
323
+ </button>
324
+ <button
325
+ id="deselectAllBtn"
326
+ class="cyber-button px-4 py-2 font-semibold rounded-sm flex items-center gap-2 pixel-corners"
327
+ >
328
+ <i class="far fa-square"></i> DESELECT ALL
329
+ </button>
330
+ <button
331
+ id="downloadSelectedBtn"
332
+ class="cyber-button px-4 py-2 font-semibold rounded-sm flex items-center gap-2 pixel-corners"
333
+ >
334
+ <i class="fas fa-download"></i> DOWNLOAD
335
+ </button>
336
+ <button
337
+ id="zipSelectedBtn"
338
+ class="cyber-button px-4 py-2 font-semibold rounded-sm flex items-center gap-2 pixel-corners"
339
+ >
340
+ <i class="fas fa-file-archive"></i> CREATE ZIP
341
+ </button>
342
+ </div>
343
+ </div>
344
+
345
+ <div id="nftGrid" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
346
+ <!-- NFT cards will be inserted here -->
347
+ </div>
348
+ </div>
349
+
350
+ <div id="error" class="hidden bg-black bg-opacity-70 border-l-4 border-[#ff0000] p-4 mb-8 rounded-sm">
351
+ <div class="flex">
352
+ <div class="flex-shrink-0 text-[#ff0000] text-2xl">
353
+ <i class="fas fa-exclamation-circle"></i>
354
+ </div>
355
+ <div class="ml-3">
356
+ <p id="errorMessage" class="text-[#ff0000]">ERROR MESSAGE WILL APPEAR HERE</p>
357
+ </div>
358
+ </div>
359
+ </div>
360
+
361
+ <div id="emptyState" class="text-center py-20">
362
+ <div class="mx-auto w-24 h-24 bg-black bg-opacity-70 rounded-full flex items-center justify-center mb-6 border-2 border-[#0f0]">
363
+ <i class="fas fa-wallet text-[#0f0] text-4xl"></i>
364
+ </div>
365
+ <h3 class="text-2xl font-medium text-[#0f0] mb-4">NO WALLET SCANNED YET</h3>
366
+ <p class="text-[#0f0] max-w-md mx-auto">
367
+ ENTER A SOLANA WALLET ADDRESS ABOVE TO FIND AND DOWNLOAD ALL IMAGES FROM NFTS IN THAT WALLET.
368
+ </p>
369
+ </div>
370
+ </div>
371
+
372
+ <footer class="footer bg-black bg-opacity-70 py-8 px-4 mt-12">
373
+ <div class="max-w-6xl mx-auto">
374
+ <div class="flex flex-col md:flex-row justify-between items-center">
375
+ <div class="mb-4 md:mb-0">
376
+ <p class="text-[#0f0]">🌀 SOLANA IMAGE VORTEX © 2023</p>
377
+ </div>
378
+ <div class="flex space-x-6">
379
+ <a href="#" class="social-icon text-[#0f0] text-xl">
380
+ <i class="fab fa-twitter"></i>
381
+ </a>
382
+ <a href="#" class="social-icon text-[#0f0] text-xl">
383
+ <i class="fab fa-github"></i>
384
+ </a>
385
+ <a href="#" class="social-icon text-[#0f0] text-xl">
386
+ <i class="fab fa-discord"></i>
387
+ </a>
388
+ </div>
389
+ </div>
390
+ <div class="mt-8 text-center text-sm text-[#0f0]">
391
+ <p>THIS TOOL USES THE HELIUS API TO FETCH NFT DATA FROM SOLANA WALLETS. ALL IMAGES REMAIN PROPERTY OF THEIR RESPECTIVE OWNERS.</p>
392
+ </div>
393
+ </div>
394
+ </footer>
395
+
396
+ <!-- JSZip library for ZIP download functionality -->
397
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
398
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
399
+
400
+ <script>
401
+ document.addEventListener('DOMContentLoaded', function() {
402
+ const walletAddress = document.getElementById('walletAddress');
403
+ const fetchBtn = document.getElementById('fetchBtn');
404
+ const loading = document.getElementById('loading');
405
+ const stats = document.getElementById('stats');
406
+ const results = document.getElementById('results');
407
+ const error = document.getElementById('error');
408
+ const errorMessage = document.getElementById('errorMessage');
409
+ const emptyState = document.getElementById('emptyState');
410
+ const nftGrid = document.getElementById('nftGrid');
411
+ const totalNfts = document.getElementById('totalNfts');
412
+ const imageCount = document.getElementById('imageCount');
413
+ const selectedCountStat = document.getElementById('selectedCountStat');
414
+ const selectAllBtn = document.getElementById('selectAllBtn');
415
+ const deselectAllBtn = document.getElementById('deselectAllBtn');
416
+ const downloadSelectedBtn = document.getElementById('downloadSelectedBtn');
417
+ const zipSelectedBtn = document.getElementById('zipSelectedBtn');
418
+ const selectedCountBadge = document.getElementById('selectedCountBadge');
419
+ const selectedCount = document.getElementById('selectedCount');
420
+ const toast = document.getElementById('toast');
421
+ const toastMessage = document.getElementById('toastMessage');
422
+
423
+ // Your Helius API key
424
+ const HELIUS_API_KEY = 'dd4bdaf8-8a81-4f62-b2ec-27ede0bcbd97';
425
+
426
+ let nfts = [];
427
+ let imagesToDownload = [];
428
+ let selectedNFTs = [];
429
+
430
+ // Show toast notification
431
+ function showToast(message, isSuccess = true) {
432
+ toastMessage.textContent = message;
433
+ if (isSuccess) {
434
+ toast.className = 'toast fixed bottom-4 right-4 p-3 rounded-sm flex items-center gap-2 z-50 pixel-corners';
435
+ toast.style.background = 'linear-gradient(45deg, #ff00ff, #00ffff)';
436
+ } else {
437
+ toast.className = 'toast fixed bottom-4 right-4 p-3 rounded-sm flex items-center gap-2 z-50 pixel-corners';
438
+ toast.style.background = 'linear-gradient(45deg, #ff0000, #ff6600)';
439
+ }
440
+
441
+ toast.classList.remove('hidden');
442
+ setTimeout(() => {
443
+ toast.classList.add('hidden');
444
+ }, 3000);
445
+ }
446
+
447
+ // Update selected count display
448
+ function updateSelectedCount() {
449
+ selectedCount.textContent = selectedNFTs.length;
450
+ selectedCountStat.textContent = selectedNFTs.length;
451
+
452
+ if (selectedNFTs.length > 0) {
453
+ selectedCountBadge.classList.remove('hidden');
454
+ } else {
455
+ selectedCountBadge.classList.add('hidden');
456
+ }
457
+ }
458
+
459
+ // Validate Solana wallet address
460
+ function isValidSolanaAddress(address) {
461
+ return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address);
462
+ }
463
+
464
+ // Fetch NFTs from wallet using Helius API
465
+ async function fetchNFTs(address) {
466
+ try {
467
+ // Show loading state
468
+ loading.classList.remove('hidden');
469
+ error.classList.add('hidden');
470
+ emptyState.classList.add('hidden');
471
+ results.classList.add('hidden');
472
+ stats.classList.add('hidden');
473
+
474
+ // Reset previous data
475
+ nfts = [];
476
+ imagesToDownload = [];
477
+ selectedNFTs = [];
478
+ nftGrid.innerHTML = '';
479
+ updateSelectedCount();
480
+
481
+ // Using Helius API to fetch NFTs
482
+ const response = await fetch(`https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}`, {
483
+ method: 'POST',
484
+ headers: {
485
+ 'Content-Type': 'application/json',
486
+ },
487
+ body: JSON.stringify({
488
+ jsonrpc: '2.0',
489
+ id: '1',
490
+ method: 'getAssetsByOwner',
491
+ params: {
492
+ ownerAddress: address,
493
+ page: 1,
494
+ limit: 1000
495
+ }
496
+ })
497
+ });
498
+
499
+ const data = await response.json();
500
+
501
+ if (data.error) {
502
+ throw new Error(data.error.message || 'FAILED TO FETCH NFTS');
503
+ }
504
+
505
+ if (!data.result || !data.result.items || data.result.items.length === 0) {
506
+ throw new Error('NO NFTS FOUND IN THIS WALLET');
507
+ }
508
+
509
+ nfts = data.result.items;
510
+ totalNfts.textContent = nfts.length;
511
+
512
+ // Filter NFTs with images
513
+ imagesToDownload = nfts.filter(nft => {
514
+ return nft.content && nft.content.links && nft.content.links.image;
515
+ });
516
+
517
+ imageCount.textContent = imagesToDownload.length;
518
+
519
+ if (imagesToDownload.length === 0) {
520
+ throw new Error('NO IMAGES FOUND IN NFTS FROM THIS WALLET');
521
+ }
522
+
523
+ // Display NFTs
524
+ displayNFTs(imagesToDownload);
525
+
526
+ // Show results
527
+ loading.classList.add('hidden');
528
+ stats.classList.remove('hidden');
529
+ results.classList.remove('hidden');
530
+
531
+ showToast(`FOUND ${imagesToDownload.length} IMAGES IN WALLET`);
532
+
533
+ } catch (err) {
534
+ console.error('Error fetching NFTs:', err);
535
+ loading.classList.add('hidden');
536
+ error.classList.remove('hidden');
537
+ errorMessage.textContent = err.message.toUpperCase();
538
+ emptyState.classList.remove('hidden');
539
+ showToast(err.message.toUpperCase(), false);
540
+ }
541
+ }
542
+
543
+ // Display NFTs in grid
544
+ function displayNFTs(nfts) {
545
+ nftGrid.innerHTML = '';
546
+
547
+ nfts.forEach((nft, index) => {
548
+ const imageUrl = nft.content.links.image;
549
+ const name = nft.content.metadata.name || 'UNNAMED NFT';
550
+ const collection = nft.grouping && nft.grouping.length > 0 ? nft.grouping[0].group_value : 'NO COLLECTION';
551
+
552
+ const nftCard = document.createElement('div');
553
+ nftCard.className = 'nft-card p-0 overflow-hidden relative pixel-corners';
554
+ nftCard.innerHTML = `
555
+ <div class="absolute top-2 left-2 z-10">
556
+ <input
557
+ type="checkbox"
558
+ class="cyber-checkbox"
559
+ data-index="${index}"
560
+ data-url="${imageUrl}"
561
+ data-name="${name.replace(/[^a-z0-9]/gi, '_').toLowerCase()}"
562
+ >
563
+ </div>
564
+ <div class="relative pb-[100%]">
565
+ <img src="${imageUrl}" alt="${name}" class="absolute h-full w-full object-cover" loading="lazy" onerror="this.src='https://via.placeholder.com/400?text=IMAGE+NOT+AVAILABLE'">
566
+ </div>
567
+ <div class="p-4">
568
+ <h3 class="font-bold text-lg text-[#0f0] truncate">${name.toUpperCase()}</h3>
569
+ <p class="text-sm text-[#0ff] truncate">${collection.toUpperCase()}</p>
570
+ <div class="mt-2 flex justify-between items-center">
571
+ <span class="text-xs text-[#0f0]">#${index + 1}</span>
572
+ <button
573
+ class="download-single-btn px-3 py-1 bg-black bg-opacity-70 text-[#0f0] text-xs rounded-sm hover:bg-[#0f0] hover:text-black transition-colors duration-200 border border-[#0f0] pixel-corners"
574
+ data-url="${imageUrl}"
575
+ data-name="${name.replace(/[^a-z0-9]/gi, '_').toLowerCase()}"
576
+ >
577
+ <i class="fas fa-download"></i> GRAB
578
+ </button>
579
+ </div>
580
+ </div>
581
+ `;
582
+ nftGrid.appendChild(nftCard);
583
+ });
584
+
585
+ // Add event listeners to checkboxes
586
+ document.querySelectorAll('.cyber-checkbox').forEach(checkbox => {
587
+ checkbox.addEventListener('change', function() {
588
+ const index = parseInt(this.getAttribute('data-index'));
589
+ const nft = imagesToDownload[index];
590
+
591
+ if (this.checked) {
592
+ selectedNFTs.push({
593
+ ...nft,
594
+ checkbox: this
595
+ });
596
+ } else {
597
+ selectedNFTs = selectedNFTs.filter(item => item.content.links.image !== nft.content.links.image);
598
+ }
599
+
600
+ updateSelectedCount();
601
+ });
602
+ });
603
+
604
+ // Add event listeners to single download buttons
605
+ document.querySelectorAll('.download-single-btn').forEach(btn => {
606
+ btn.addEventListener('click', function() {
607
+ const url = this.getAttribute('data-url');
608
+ const name = this.getAttribute('data-name');
609
+ downloadImage(url, name);
610
+ });
611
+ });
612
+ }
613
+
614
+ // Download a single image
615
+ function downloadImage(url, name) {
616
+ fetch(url)
617
+ .then(response => response.blob())
618
+ .then(blob => {
619
+ const a = document.createElement('a');
620
+ a.href = URL.createObjectURL(blob);
621
+ a.download = `${name || 'nft'}_${Date.now()}.${blob.type.split('/')[1] || 'jpg'}`;
622
+ document.body.appendChild(a);
623
+ a.click();
624
+ document.body.removeChild(a);
625
+ showToast('IMAGE DOWNLOADED SUCCESSFULLY!');
626
+ })
627
+ .catch(err => {
628
+ console.error('Error downloading image:', err);
629
+ showToast('FAILED TO DOWNLOAD IMAGE', false);
630
+ });
631
+ }
632
+
633
+ // Select all NFTs
634
+ function selectAllNFTs() {
635
+ document.querySelectorAll('.cyber-checkbox').forEach(checkbox => {
636
+ checkbox.checked = true;
637
+ });
638
+
639
+ selectedNFTs = imagesToDownload.map((nft, index) => ({
640
+ ...nft,
641
+ checkbox: document.querySelector(`.cyber-checkbox[data-index="${index}"]`)
642
+ }));
643
+
644
+ updateSelectedCount();
645
+ showToast(`SELECTED ALL ${imagesToDownload.length} IMAGES`);
646
+ }
647
+
648
+ // Deselect all NFTs
649
+ function deselectAllNFTs() {
650
+ document.querySelectorAll('.cyber-checkbox').forEach(checkbox => {
651
+ checkbox.checked = false;
652
+ });
653
+
654
+ selectedNFTs = [];
655
+ updateSelectedCount();
656
+ showToast('DESELECTED ALL IMAGES');
657
+ }
658
+
659
+ // Download selected images
660
+ async function downloadSelectedImages() {
661
+ if (selectedNFTs.length === 0) {
662
+ showToast('PLEASE SELECT AT LEAST ONE IMAGE TO DOWNLOAD', false);
663
+ return;
664
+ }
665
+
666
+ downloadSelectedBtn.disabled = true;
667
+ downloadSelectedBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> DOWNLOADING...';
668
+
669
+ let downloadedCount = 0;
670
+ const totalCount = selectedNFTs.length;
671
+
672
+ // Download images sequentially to avoid browser limitations
673
+ for (let i = 0; i < selectedNFTs.length; i++) {
674
+ const nft = selectedNFTs[i];
675
+ const imageUrl = nft.content.links.image;
676
+ const name = nft.content.metadata.name || 'unnamed_nft';
677
+
678
+ try {
679
+ await new Promise((resolve) => {
680
+ fetch(imageUrl)
681
+ .then(response => response.blob())
682
+ .then(blob => {
683
+ const a = document.createElement('a');
684
+ a.href = URL.createObjectURL(blob);
685
+ a.download = `${name.replace(/[^a-z0-9]/gi, '_').toLowerCase()}_${i}.${blob.type.split('/')[1] || 'jpg'}`;
686
+ document.body.appendChild(a);
687
+ a.click();
688
+ document.body.removeChild(a);
689
+ downloadedCount++;
690
+ resolve();
691
+ })
692
+ .catch(err => {
693
+ console.error('Error downloading image:', err);
694
+ resolve(); // Continue with next image even if one fails
695
+ });
696
+ });
697
+
698
+ // Small delay between downloads
699
+ await new Promise(resolve => setTimeout(resolve, 300));
700
+ } catch (err) {
701
+ console.error('Error in download sequence:', err);
702
+ }
703
+ }
704
+
705
+ downloadSelectedBtn.disabled = false;
706
+ downloadSelectedBtn.innerHTML = '<i class="fas fa-download"></i> DOWNLOAD';
707
+
708
+ showToast(`DOWNLOAD COMPLETED! ${downloadedCount} IMAGES SAVED.`);
709
+ }
710
+
711
+ // Download selected images as a ZIP file
712
+ async function downloadSelectedAsZip() {
713
+ if (selectedNFTs.length === 0) {
714
+ showToast('PLEASE SELECT AT LEAST ONE IMAGE TO DOWNLOAD', false);
715
+ return;
716
+ }
717
+
718
+ zipSelectedBtn.disabled = true;
719
+ zipSelectedBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> CREATING ZIP...';
720
+
721
+ const zip = new JSZip();
722
+ const imgFolder = zip.folder("selected_nft_images");
723
+ let processedCount = 0;
724
+ const totalCount = selectedNFTs.length;
725
+
726
+ // Process images for ZIP
727
+ for (let i = 0; i < selectedNFTs.length; i++) {
728
+ const nft = selectedNFTs[i];
729
+ const imageUrl = nft.content.links.image;
730
+ const name = nft.content.metadata.name || 'unnamed_nft';
731
+ const extension = imageUrl.split('.').pop().split('?')[0] || 'jpg';
732
+
733
+ try {
734
+ const response = await fetch(imageUrl);
735
+ const blob = await response.blob();
736
+ imgFolder.file(`${name.replace(/[^a-z0-9]/gi, '_').toLowerCase()}_${i}.${extension}`, blob);
737
+ processedCount++;
738
+ } catch (err) {
739
+ console.error('Error processing image for ZIP:', err);
740
+ }
741
+ }
742
+
743
+ // Generate and download the ZIP file
744
+ zip.generateAsync({ type: 'blob' }).then(function(content) {
745
+ saveAs(content, "selected_nft_images.zip");
746
+ zipSelectedBtn.disabled = false;
747
+ zipSelectedBtn.innerHTML = '<i class="fas fa-file-archive"></i> CREATE ZIP';
748
+ showToast('ZIP FILE DOWNLOADED SUCCESSFULLY!');
749
+ });
750
+ }
751
+
752
+ // Event listeners
753
+ fetchBtn.addEventListener('click', function() {
754
+ if (!walletAddress.value.trim()) {
755
+ error.classList.remove('hidden');
756
+ errorMessage.textContent = 'PLEASE ENTER A WALLET ADDRESS';
757
+ showToast('PLEASE ENTER A WALLET ADDRESS', false);
758
+ return;
759
+ }
760
+
761
+ if (!isValidSolanaAddress(walletAddress.value.trim())) {
762
+ error.classList.remove('hidden');
763
+ errorMessage.textContent = 'PLEASE ENTER A VALID SOLANA WALLET ADDRESS';
764
+ showToast('INVALID SOLANA WALLET ADDRESS', false);
765
+ return;
766
+ }
767
+
768
+ fetchNFTs(walletAddress.value.trim());
769
+ });
770
+
771
+ selectAllBtn.addEventListener('click', selectAllNFTs);
772
+ deselectAllBtn.addEventListener('click', deselectAllNFTs);
773
+ downloadSelectedBtn.addEventListener('click', downloadSelectedImages);
774
+ zipSelectedBtn.addEventListener('click', downloadSelectedAsZip);
775
+
776
+ // Example wallet address for demo purposes
777
+ walletAddress.addEventListener('focus', function() {
778
+ if (!this.value) {
779
+ this.value = '6zsuBDfivMcPUP5mmQv26GkET9gZV5Qw5E5ZJ2N5F2Gd';
780
+ }
781
+ });
782
+ });
783
+ </script>
784
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=nock2/solana-nft-gripper" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
785
+ </html>
prompts.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ Make the app look stylish. Think psychedelic web1 vibes with crazy fonts