alfabill commited on
Commit
a15a101
verified
1 Parent(s): 780fd50

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +1277 -19
index.html CHANGED
@@ -1,19 +1,1277 @@
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="es">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>LocalStorage Manager Pro</title>
7
+
8
+ <!-- jQuery -->
9
+ <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
10
+
11
+ <!-- Font Awesome -->
12
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
13
+
14
+ <!-- Google Fonts -->
15
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
16
+
17
+ <style>
18
+ :root {
19
+ --primary: #6366f1;
20
+ --primary-dark: #4f46e5;
21
+ --secondary: #ec4899;
22
+ --success: #10b981;
23
+ --warning: #f59e0b;
24
+ --danger: #ef4444;
25
+ --bg: #0f172a;
26
+ --surface: rgba(30, 41, 59, 0.8);
27
+ --surface-solid: #1e293b;
28
+ --text: #f8fafc;
29
+ --text-muted: #94a3b8;
30
+ --border: rgba(148, 163, 184, 0.1);
31
+ --shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
32
+ --glass: blur(20px) saturate(180%);
33
+ }
34
+
35
+ @media (prefers-color-scheme: light) {
36
+ :root {
37
+ --bg: #f8fafc;
38
+ --surface: rgba(255, 255, 255, 0.8);
39
+ --surface-solid: #ffffff;
40
+ --text: #0f172a;
41
+ --text-muted: #64748b;
42
+ --border: rgba(148, 163, 184, 0.2);
43
+ --shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.15);
44
+ }
45
+ }
46
+
47
+ * {
48
+ margin: 0;
49
+ padding: 0;
50
+ box-sizing: border-box;
51
+ }
52
+
53
+ body {
54
+ font-family: 'Inter', sans-serif;
55
+ background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 100%);
56
+ color: var(--text);
57
+ min-height: 100vh;
58
+ overflow-x: hidden;
59
+ }
60
+
61
+ @media (prefers-color-scheme: light) {
62
+ body {
63
+ background: linear-gradient(135deg, #f0f9ff 0%, #e0e7ff 100%);
64
+ }
65
+ }
66
+
67
+ /* Animated background */
68
+ .bg-animation {
69
+ position: fixed;
70
+ top: 0;
71
+ left: 0;
72
+ width: 100%;
73
+ height: 100%;
74
+ z-index: -1;
75
+ overflow: hidden;
76
+ }
77
+
78
+ .bg-animation::before {
79
+ content: '';
80
+ position: absolute;
81
+ width: 150%;
82
+ height: 150%;
83
+ background: radial-gradient(circle at 20% 80%, var(--primary) 0%, transparent 50%),
84
+ radial-gradient(circle at 80% 20%, var(--secondary) 0%, transparent 50%);
85
+ opacity: 0.15;
86
+ animation: float 20s ease-in-out infinite;
87
+ }
88
+
89
+ @keyframes float {
90
+ 0%, 100% { transform: translate(-10%, -10%) rotate(0deg); }
91
+ 50% { transform: translate(10%, 10%) rotate(180deg); }
92
+ }
93
+
94
+ /* Header */
95
+ header {
96
+ background: var(--surface);
97
+ backdrop-filter: var(--glass);
98
+ border-bottom: 1px solid var(--border);
99
+ padding: 1rem 2rem;
100
+ position: sticky;
101
+ top: 0;
102
+ z-index: 100;
103
+ box-shadow: var(--shadow);
104
+ }
105
+
106
+ .header-content {
107
+ max-width: 1400px;
108
+ margin: 0 auto;
109
+ display: flex;
110
+ justify-content: space-between;
111
+ align-items: center;
112
+ flex-wrap: wrap;
113
+ gap: 1rem;
114
+ }
115
+
116
+ .brand {
117
+ display: flex;
118
+ align-items: center;
119
+ gap: 0.75rem;
120
+ font-size: 1.5rem;
121
+ font-weight: 700;
122
+ background: linear-gradient(135deg, var(--primary), var(--secondary));
123
+ -webkit-background-clip: text;
124
+ -webkit-text-fill-color: transparent;
125
+ background-clip: text;
126
+ }
127
+
128
+ .brand i {
129
+ font-size: 1.75rem;
130
+ background: linear-gradient(135deg, var(--primary), var(--secondary));
131
+ -webkit-background-clip: text;
132
+ -webkit-text-fill-color: transparent;
133
+ }
134
+
135
+ .anycoder-link {
136
+ font-size: 0.875rem;
137
+ color: var(--text-muted);
138
+ text-decoration: none;
139
+ padding: 0.5rem 1rem;
140
+ border-radius: 9999px;
141
+ border: 1px solid var(--border);
142
+ transition: all 0.3s ease;
143
+ display: flex;
144
+ align-items: center;
145
+ gap: 0.5rem;
146
+ }
147
+
148
+ .anycoder-link:hover {
149
+ background: var(--surface-solid);
150
+ color: var(--primary);
151
+ transform: translateY(-2px);
152
+ box-shadow: 0 10px 25px -5px rgba(99, 102, 241, 0.3);
153
+ }
154
+
155
+ /* Container */
156
+ .container {
157
+ max-width: 1400px;
158
+ margin: 2rem auto;
159
+ padding: 0 1.5rem;
160
+ }
161
+
162
+ /* Stats Cards */
163
+ .stats-grid {
164
+ display: grid;
165
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
166
+ gap: 1rem;
167
+ margin-bottom: 2rem;
168
+ }
169
+
170
+ .stat-card {
171
+ background: var(--surface);
172
+ backdrop-filter: var(--glass);
173
+ border: 1px solid var(--border);
174
+ border-radius: 1rem;
175
+ padding: 1.5rem;
176
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
177
+ }
178
+
179
+ .stat-card:hover {
180
+ transform: translateY(-5px);
181
+ box-shadow: var(--shadow);
182
+ }
183
+
184
+ .stat-label {
185
+ font-size: 0.875rem;
186
+ color: var(--text-muted);
187
+ text-transform: uppercase;
188
+ letter-spacing: 0.05em;
189
+ margin-bottom: 0.5rem;
190
+ }
191
+
192
+ .stat-value {
193
+ font-size: 2rem;
194
+ font-weight: 700;
195
+ color: var(--text);
196
+ }
197
+
198
+ .stat-card.primary { border-top: 3px solid var(--primary); }
199
+ .stat-card.success { border-top: 3px solid var(--success); }
200
+ .stat-card.warning { border-top: 3px solid var(--warning); }
201
+ .stat-card.secondary { border-top: 3px solid var(--secondary); }
202
+
203
+ /* Toolbar */
204
+ .toolbar {
205
+ background: var(--surface);
206
+ backdrop-filter: var(--glass);
207
+ border: 1px solid var(--border);
208
+ border-radius: 1rem;
209
+ padding: 1.25rem;
210
+ margin-bottom: 1.5rem;
211
+ display: flex;
212
+ flex-wrap: wrap;
213
+ gap: 1rem;
214
+ align-items: center;
215
+ justify-content: space-between;
216
+ box-shadow: var(--shadow);
217
+ }
218
+
219
+ .toolbar-group {
220
+ display: flex;
221
+ gap: 0.75rem;
222
+ flex-wrap: wrap;
223
+ align-items: center;
224
+ }
225
+
226
+ /* Buttons */
227
+ .btn {
228
+ padding: 0.625rem 1.25rem;
229
+ border-radius: 0.75rem;
230
+ border: none;
231
+ font-weight: 600;
232
+ cursor: pointer;
233
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
234
+ display: inline-flex;
235
+ align-items: center;
236
+ gap: 0.5rem;
237
+ font-size: 0.875rem;
238
+ text-decoration: none;
239
+ color: white;
240
+ }
241
+
242
+ .btn-primary {
243
+ background: linear-gradient(135deg, var(--primary), var(--primary-dark));
244
+ box-shadow: 0 4px 6px -1px rgba(99, 102, 241, 0.3);
245
+ }
246
+
247
+ .btn-primary:hover {
248
+ transform: translateY(-2px);
249
+ box-shadow: 0 10px 15px -3px rgba(99, 102, 241, 0.4);
250
+ }
251
+
252
+ .btn-secondary {
253
+ background: var(--surface-solid);
254
+ border: 1px solid var(--border);
255
+ color: var(--text);
256
+ }
257
+
258
+ .btn-secondary:hover {
259
+ background: var(--primary);
260
+ color: white;
261
+ border-color: var(--primary);
262
+ }
263
+
264
+ .btn-danger {
265
+ background: var(--danger);
266
+ }
267
+
268
+ .btn-sm {
269
+ padding: 0.375rem 0.75rem;
270
+ font-size: 0.75rem;
271
+ }
272
+
273
+ /* Inputs */
274
+ .input-group {
275
+ position: relative;
276
+ display: flex;
277
+ align-items: center;
278
+ }
279
+
280
+ .input-icon {
281
+ position: absolute;
282
+ left: 1rem;
283
+ color: var(--text-muted);
284
+ pointer-events: none;
285
+ }
286
+
287
+ input, select, textarea {
288
+ padding: 0.625rem 1rem 0.625rem 2.5rem;
289
+ border: 1px solid var(--border);
290
+ border-radius: 0.75rem;
291
+ background: var(--surface-solid);
292
+ color: var(--text);
293
+ font-family: inherit;
294
+ font-size: 0.875rem;
295
+ transition: all 0.3s ease;
296
+ min-width: 250px;
297
+ }
298
+
299
+ input:focus, select:focus, textarea:focus {
300
+ outline: none;
301
+ border-color: var(--primary);
302
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
303
+ }
304
+
305
+ /* Table Container */
306
+ .table-container {
307
+ background: var(--surface);
308
+ backdrop-filter: var(--glass);
309
+ border: 1px solid var(--border);
310
+ border-radius: 1rem;
311
+ overflow: hidden;
312
+ box-shadow: var(--shadow);
313
+ }
314
+
315
+ table {
316
+ width: 100%;
317
+ border-collapse: collapse;
318
+ }
319
+
320
+ thead {
321
+ background: rgba(99, 102, 241, 0.1);
322
+ }
323
+
324
+ th {
325
+ padding: 1rem;
326
+ text-align: left;
327
+ font-weight: 600;
328
+ font-size: 0.75rem;
329
+ text-transform: uppercase;
330
+ letter-spacing: 0.05em;
331
+ color: var(--text-muted);
332
+ cursor: pointer;
333
+ user-select: none;
334
+ transition: color 0.3s ease;
335
+ white-space: nowrap;
336
+ }
337
+
338
+ th:hover {
339
+ color: var(--primary);
340
+ }
341
+
342
+ th i {
343
+ margin-left: 0.5rem;
344
+ font-size: 0.75rem;
345
+ }
346
+
347
+ td {
348
+ padding: 1rem;
349
+ border-top: 1px solid var(--border);
350
+ font-size: 0.875rem;
351
+ vertical-align: middle;
352
+ }
353
+
354
+ tr {
355
+ transition: background 0.3s ease;
356
+ }
357
+
358
+ tbody tr:hover {
359
+ background: rgba(99, 102, 241, 0.05);
360
+ }
361
+
362
+ .cell-key {
363
+ font-family: 'JetBrains Mono', monospace;
364
+ font-weight: 600;
365
+ color: var(--primary);
366
+ display: flex;
367
+ align-items: center;
368
+ gap: 0.5rem;
369
+ }
370
+
371
+ .cell-value {
372
+ max-width: 300px;
373
+ overflow: hidden;
374
+ text-overflow: ellipsis;
375
+ white-space: nowrap;
376
+ font-family: 'JetBrains Mono', monospace;
377
+ font-size: 0.8125rem;
378
+ color: var(--text-muted);
379
+ }
380
+
381
+ .cell-date {
382
+ font-size: 0.8125rem;
383
+ color: var(--text-muted);
384
+ display: flex;
385
+ flex-direction: column;
386
+ gap: 0.25rem;
387
+ }
388
+
389
+ .cell-date .time {
390
+ font-size: 0.75rem;
391
+ opacity: 0.7;
392
+ }
393
+
394
+ .cell-url {
395
+ max-width: 200px;
396
+ overflow: hidden;
397
+ text-overflow: ellipsis;
398
+ white-space: nowrap;
399
+ color: var(--secondary);
400
+ font-size: 0.8125rem;
401
+ }
402
+
403
+ .cell-url a {
404
+ color: inherit;
405
+ text-decoration: none;
406
+ }
407
+
408
+ .cell-url a:hover {
409
+ text-decoration: underline;
410
+ }
411
+
412
+ .actions {
413
+ display: flex;
414
+ gap: 0.5rem;
415
+ }
416
+
417
+ .btn-icon {
418
+ width: 2rem;
419
+ height: 2rem;
420
+ border-radius: 0.5rem;
421
+ border: none;
422
+ background: transparent;
423
+ color: var(--text-muted);
424
+ cursor: pointer;
425
+ transition: all 0.3s ease;
426
+ display: inline-flex;
427
+ align-items: center;
428
+ justify-content: center;
429
+ }
430
+
431
+ .btn-icon:hover {
432
+ background: var(--primary);
433
+ color: white;
434
+ transform: scale(1.1);
435
+ }
436
+
437
+ .btn-icon.delete:hover {
438
+ background: var(--danger);
439
+ }
440
+
441
+ /* Empty State */
442
+ .empty-state {
443
+ text-align: center;
444
+ padding: 4rem 2rem;
445
+ color: var(--text-muted);
446
+ }
447
+
448
+ .empty-state i {
449
+ font-size: 4rem;
450
+ margin-bottom: 1rem;
451
+ opacity: 0.3;
452
+ }
453
+
454
+ .empty-state h3 {
455
+ font-size: 1.25rem;
456
+ margin-bottom: 0.5rem;
457
+ color: var(--text);
458
+ }
459
+
460
+ /* Modal */
461
+ .modal-overlay {
462
+ position: fixed;
463
+ top: 0;
464
+ left: 0;
465
+ right: 0;
466
+ bottom: 0;
467
+ background: rgba(0, 0, 0, 0.8);
468
+ backdrop-filter: blur(5px);
469
+ display: flex;
470
+ align-items: center;
471
+ justify-content: center;
472
+ z-index: 1000;
473
+ opacity: 0;
474
+ visibility: hidden;
475
+ transition: all 0.3s ease;
476
+ padding: 1rem;
477
+ }
478
+
479
+ .modal-overlay.active {
480
+ opacity: 1;
481
+ visibility: visible;
482
+ }
483
+
484
+ .modal {
485
+ background: var(--surface-solid);
486
+ border: 1px solid var(--border);
487
+ border-radius: 1.5rem;
488
+ width: 100%;
489
+ max-width: 600px;
490
+ max-height: 90vh;
491
+ overflow-y: auto;
492
+ transform: scale(0.9) translateY(20px);
493
+ transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
494
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
495
+ }
496
+
497
+ .modal-overlay.active .modal {
498
+ transform: scale(1) translateY(0);
499
+ }
500
+
501
+ .modal-header {
502
+ padding: 1.5rem;
503
+ border-bottom: 1px solid var(--border);
504
+ display: flex;
505
+ justify-content: space-between;
506
+ align-items: center;
507
+ }
508
+
509
+ .modal-title {
510
+ font-size: 1.25rem;
511
+ font-weight: 700;
512
+ display: flex;
513
+ align-items: center;
514
+ gap: 0.75rem;
515
+ }
516
+
517
+ .modal-close {
518
+ width: 2.5rem;
519
+ height: 2.5rem;
520
+ border-radius: 0.75rem;
521
+ border: none;
522
+ background: var(--surface);
523
+ color: var(--text-muted);
524
+ cursor: pointer;
525
+ transition: all 0.3s ease;
526
+ display: flex;
527
+ align-items: center;
528
+ justify-content: center;
529
+ font-size: 1.25rem;
530
+ }
531
+
532
+ .modal-close:hover {
533
+ background: var(--danger);
534
+ color: white;
535
+ transform: rotate(90deg);
536
+ }
537
+
538
+ .modal-body {
539
+ padding: 1.5rem;
540
+ }
541
+
542
+ .form-group {
543
+ margin-bottom: 1.25rem;
544
+ }
545
+
546
+ .form-label {
547
+ display: block;
548
+ margin-bottom: 0.5rem;
549
+ font-size: 0.875rem;
550
+ font-weight: 500;
551
+ color: var(--text-muted);
552
+ }
553
+
554
+ .form-input, .form-textarea {
555
+ width: 100%;
556
+ padding: 0.75rem 1rem;
557
+ border: 1px solid var(--border);
558
+ border-radius: 0.75rem;
559
+ background: var(--bg);
560
+ color: var(--text);
561
+ font-family: inherit;
562
+ font-size: 0.875rem;
563
+ transition: all 0.3s ease;
564
+ }
565
+
566
+ .form-textarea {
567
+ min-height: 120px;
568
+ resize: vertical;
569
+ font-family: 'JetBrains Mono', monospace;
570
+ }
571
+
572
+ .form-input:focus, .form-textarea:focus {
573
+ outline: none;
574
+ border-color: var(--primary);
575
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
576
+ }
577
+
578
+ .modal-footer {
579
+ padding: 1.5rem;
580
+ border-top: 1px solid var(--border);
581
+ display: flex;
582
+ justify-content: flex-end;
583
+ gap: 0.75rem;
584
+ }
585
+
586
+ /* Toast */
587
+ .toast-container {
588
+ position: fixed;
589
+ bottom: 2rem;
590
+ right: 2rem;
591
+ z-index: 2000;
592
+ display: flex;
593
+ flex-direction: column;
594
+ gap: 0.75rem;
595
+ }
596
+
597
+ .toast {
598
+ background: var(--surface-solid);
599
+ border: 1px solid var(--border);
600
+ border-left: 4px solid var(--primary);
601
+ border-radius: 0.75rem;
602
+ padding: 1rem 1.5rem;
603
+ box-shadow: var(--shadow);
604
+ display: flex;
605
+ align-items: center;
606
+ gap: 0.75rem;
607
+ animation: slideIn 0.3s ease, fadeOut 0.3s ease 2.7s;
608
+ min-width: 300px;
609
+ }
610
+
611
+ .toast.success { border-left-color: var(--success); }
612
+ .toast.error { border-left-color: var(--danger); }
613
+ .toast.warning { border-left-color: var(--warning); }
614
+
615
+ @keyframes slideIn {
616
+ from {
617
+ transform: translateX(100%);
618
+ opacity: 0;
619
+ }
620
+ to {
621
+ transform: translateX(0);
622
+ opacity: 1;
623
+ }
624
+ }
625
+
626
+ @keyframes fadeOut {
627
+ to {
628
+ opacity: 0;
629
+ transform: translateX(100%);
630
+ }
631
+ }
632
+
633
+ /* Badge */
634
+ .badge {
635
+ display: inline-flex;
636
+ align-items: center;
637
+ padding: 0.25rem 0.75rem;
638
+ border-radius: 9999px;
639
+ font-size: 0.75rem;
640
+ font-weight: 600;
641
+ background: rgba(99, 102, 241, 0.1);
642
+ color: var(--primary);
643
+ }
644
+
645
+ /* Responsive */
646
+ @media (max-width: 768px) {
647
+ .header-content {
648
+ flex-direction: column;
649
+ text-align: center;
650
+ }
651
+
652
+ .toolbar {
653
+ flex-direction: column;
654
+ align-items: stretch;
655
+ }
656
+
657
+ .toolbar-group {
658
+ width: 100%;
659
+ }
660
+
661
+ input, select {
662
+ width: 100%;
663
+ min-width: unset;
664
+ }
665
+
666
+ table {
667
+ display: block;
668
+ overflow-x: auto;
669
+ white-space: nowrap;
670
+ }
671
+
672
+ th, td {
673
+ padding: 0.75rem;
674
+ }
675
+
676
+ .cell-value {
677
+ max-width: 150px;
678
+ }
679
+ }
680
+
681
+ /* Scrollbar */
682
+ ::-webkit-scrollbar {
683
+ width: 8px;
684
+ height: 8px;
685
+ }
686
+
687
+ ::-webkit-scrollbar-track {
688
+ background: var(--bg);
689
+ }
690
+
691
+ ::-webkit-scrollbar-thumb {
692
+ background: var(--primary);
693
+ border-radius: 4px;
694
+ }
695
+
696
+ ::-webkit-scrollbar-thumb:hover {
697
+ background: var(--primary-dark);
698
+ }
699
+
700
+ /* Checkbox toggle */
701
+ .toggle-checkbox {
702
+ display: none;
703
+ }
704
+
705
+ .toggle-label {
706
+ width: 3rem;
707
+ height: 1.5rem;
708
+ background: var(--border);
709
+ border-radius: 9999px;
710
+ position: relative;
711
+ cursor: pointer;
712
+ transition: background 0.3s ease;
713
+ }
714
+
715
+ .toggle-label::after {
716
+ content: '';
717
+ position: absolute;
718
+ width: 1.25rem;
719
+ height: 1.25rem;
720
+ background: white;
721
+ border-radius: 50%;
722
+ top: 0.125rem;
723
+ left: 0.125rem;
724
+ transition: transform 0.3s ease;
725
+ }
726
+
727
+ .toggle-checkbox:checked + .toggle-label {
728
+ background: var(--primary);
729
+ }
730
+
731
+ .toggle-checkbox:checked + .toggle-label::after {
732
+ transform: translateX(1.5rem);
733
+ }
734
+ </style>
735
+ </head>
736
+ <body>
737
+ <div class="bg-animation"></div>
738
+
739
+ <header>
740
+ <div class="header-content">
741
+ <div class="brand">
742
+ <i class="fas fa-database"></i>
743
+ <span>LocalStorage Manager Pro</span>
744
+ </div>
745
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
746
+ <i class="fas fa-code"></i>
747
+ Built with anycoder
748
+ </a>
749
+ </div>
750
+ </header>
751
+
752
+ <div class="container">
753
+ <!-- Stats -->
754
+ <div class="stats-grid">
755
+ <div class="stat-card primary">
756
+ <div class="stat-label">Total Registros</div>
757
+ <div class="stat-value" id="totalRecords">0</div>
758
+ </div>
759
+ <div class="stat-card success">
760
+ <div class="stat-label">脷ltimas 24h</div>
761
+ <div class="stat-value" id="recentRecords">0</div>
762
+ </div>
763
+ <div class="stat-card warning">
764
+ <div class="stat-label">URLs 脷nicas</div>
765
+ <div class="stat-value" id="uniqueUrls">0</div>
766
+ </div>
767
+ <div class="stat-card secondary">
768
+ <div class="stat-label">Tama帽o Total</div>
769
+ <div class="stat-value" id="totalSize">0 KB</div>
770
+ </div>
771
+ </div>
772
+
773
+ <!-- Toolbar -->
774
+ <div class="toolbar">
775
+ <div class="toolbar-group">
776
+ <button class="btn btn-primary" id="btnAdd">
777
+ <i class="fas fa-plus"></i>
778
+ Nuevo Registro
779
+ </button>
780
+ <button class="btn btn-secondary" id="btnClearAll">
781
+ <i class="fas fa-trash-alt"></i>
782
+ Limpiar Todo
783
+ </button>
784
+ </div>
785
+
786
+ <div class="toolbar-group">
787
+ <div class="input-group">
788
+ <i class="fas fa-search input-icon"></i>
789
+ <input type="text" id="searchInput" placeholder="Buscar por llave, valor o URL...">
790
+ </div>
791
+
792
+ <select id="sortSelect">
793
+ <option value="newest">M谩s recientes primero</option>
794
+ <option value="oldest">M谩s antiguos primero</option>
795
+ <option value="key_asc">Llave (A-Z)</option>
796
+ <option value="key_desc">Llave (Z-A)</option>
797
+ <option value="url">URL</option>
798
+ </select>
799
+
800
+ <button class="btn btn-secondary" id="btnExportJson" title="Exportar JSON">
801
+ <i class="fas fa-file-code"></i>
802
+ </button>
803
+ <button class="btn btn-secondary" id="btnExportCsv" title="Exportar CSV">
804
+ <i class="fas fa-file-csv"></i>
805
+ </button>
806
+ </div>
807
+ </div>
808
+
809
+ <!-- Table -->
810
+ <div class="table-container">
811
+ <table id="dataTable">
812
+ <thead>
813
+ <tr>
814
+ <th data-sort="key">
815
+ Llave <i class="fas fa-sort"></i>
816
+ </th>
817
+ <th>Valor</th>
818
+ <th data-sort="date">
819
+ Fecha/Hora <i class="fas fa-sort"></i>
820
+ </th>
821
+ <th>URL de Origen</th>
822
+ <th>Acciones</th>
823
+ </tr>
824
+ </thead>
825
+ <tbody id="tableBody">
826
+ <!-- Dynamic content -->
827
+ </tbody>
828
+ </table>
829
+
830
+ <div class="empty-state" id="emptyState" style="display: none;">
831
+ <i class="fas fa-inbox"></i>
832
+ <h3>No hay registros</h3>
833
+ <p>Comienza agregando tu primera llave a localStorage</p>
834
+ </div>
835
+ </div>
836
+ </div>
837
+
838
+ <!-- Modal -->
839
+ <div class="modal-overlay" id="modal">
840
+ <div class="modal">
841
+ <div class="modal-header">
842
+ <h3 class="modal-title">
843
+ <i class="fas fa-edit"></i>
844
+ <span id="modalTitle">Nuevo Registro</span>
845
+ </h3>
846
+ <button class="modal-close" id="btnCloseModal">
847
+ <i class="fas fa-times"></i>
848
+ </button>
849
+ </div>
850
+ <div class="modal-body">
851
+ <form id="formRecord">
852
+ <input type="hidden" id="recordId">
853
+
854
+ <div class="form-group">
855
+ <label class="form-label">Llave (Key)</label>
856
+ <input type="text" class="form-input" id="inputKey" required placeholder="ejemplo: user_preferences">
857
+ </div>
858
+
859
+ <div class="form-group">
860
+ <label class="form-label">Valor (Value)</label>
861
+ <textarea class="form-textarea" id="inputValue" required placeholder="Valor a almacenar..."></textarea>
862
+ </div>
863
+
864
+ <div class="form-group">
865
+ <label class="form-label">URL de Origen</label>
866
+ <input type="url" class="form-input" id="inputUrl" placeholder="https://ejemplo.com" required>
867
+ </div>
868
+ </form>
869
+ </div>
870
+ <div class="modal-footer">
871
+ <button class="btn btn-secondary" id="btnCancel">Cancelar</button>
872
+ <button class="btn btn-primary" id="btnSave">
873
+ <i class="fas fa-save"></i>
874
+ Guardar
875
+ </button>
876
+ </div>
877
+ </div>
878
+ </div>
879
+
880
+ <!-- Toast Container -->
881
+ <div class="toast-container" id="toastContainer"></div>
882
+
883
+ <script>
884
+ // Application State
885
+ const App = {
886
+ data: [],
887
+ filter: '',
888
+ sortBy: 'newest',
889
+ editingId: null,
890
+ storageKey: 'ls_manager_data_v1',
891
+
892
+ init() {
893
+ this.loadData();
894
+ this.bindEvents();
895
+ this.render();
896
+ this.updateStats();
897
+ },
898
+
899
+ bindEvents() {
900
+ // Add new
901
+ $('#btnAdd').on('click', () => this.openModal());
902
+
903
+ // Close modal
904
+ $('#btnCloseModal, #btnCancel').on('click', () => this.closeModal());
905
+ $('#modal').on('click', (e) => {
906
+ if (e.target === $('#modal')[0]) this.closeModal();
907
+ });
908
+
909
+ // Save
910
+ $('#btnSave').on('click', () => this.saveRecord());
911
+
912
+ // Search
913
+ $('#searchInput').on('input', (e) => {
914
+ this.filter = e.target.value.toLowerCase();
915
+ this.render();
916
+ });
917
+
918
+ // Sort
919
+ $('#sortSelect').on('change', (e) => {
920
+ this.sortBy = e.target.value;
921
+ this.render();
922
+ });
923
+
924
+ // Export
925
+ $('#btnExportJson').on('click', () => this.exportJSON());
926
+ $('#btnExportCsv').on('click', () => this.exportCSV());
927
+
928
+ // Clear all
929
+ $('#btnClearAll').on('click', () => this.clearAll());
930
+
931
+ // Table headers sort
932
+ $('th[data-sort]').on('click', (e) => {
933
+ const sortType = $(e.currentTarget).data('sort');
934
+ this.handleHeaderSort(sortType);
935
+ });
936
+ },
937
+
938
+ loadData() {
939
+ try {
940
+ const stored = localStorage.getItem(this.storageKey);
941
+ if (stored) {
942
+ this.data = JSON.parse(stored);
943
+ }
944
+ } catch (e) {
945
+ console.error('Error loading data:', e);
946
+ this.data = [];
947
+ }
948
+ },
949
+
950
+ saveData() {
951
+ try {
952
+ localStorage.setItem(this.storageKey, JSON.stringify(this.data));
953
+ this.updateStats();
954
+ } catch (e) {
955
+ this.showToast('Error al guardar datos', 'error');
956
+ }
957
+ },
958
+
959
+ generateId() {
960
+ return Date.now().toString(36) + Math.random().toString(36).substr(2);
961
+ },
962
+
963
+ openModal(id = null) {
964
+ this.editingId = id;
965
+
966
+ if (id) {
967
+ const record = this.data.find(r => r.id === id);
968
+ if (record) {
969
+ $('#modalTitle').text('Editar Registro');
970
+ $('#inputKey').val(record.key);
971
+ $('#inputValue').val(record.value);
972
+ $('#inputUrl').val(record.url);
973
+ }
974
+ } else {
975
+ $('#modalTitle').text('Nuevo Registro');
976
+ $('#formRecord')[0].reset();
977
+ $('#inputUrl').val(window.location.href);
978
+ }
979
+
980
+ $('#modal').addClass('active');
981
+ $('#inputKey').focus();
982
+ },
983
+
984
+ closeModal() {
985
+ $('#modal').removeClass('active');
986
+ this.editingId = null;
987
+ setTimeout(() => $('#formRecord')[0].reset(), 300);
988
+ },
989
+
990
+ saveRecord() {
991
+ const key = $('#inputKey').val().trim();
992
+ const value = $('#inputValue').val().trim();
993
+ const url = $('#inputUrl').val().trim();
994
+
995
+ if (!key || !value || !url) {
996
+ this.showToast('Por favor completa todos los campos', 'error');
997
+ return;
998
+ }
999
+
1000
+ // Check duplicate key (only for new records)
1001
+ if (!this.editingId && this.data.some(r => r.key === key)) {
1002
+ if (!confirm('Ya existe una llave con este nombre. 驴Deseas sobrescribirla?')) {
1003
+ return;
1004
+ }
1005
+ // Remove existing
1006
+ this.data = this.data.filter(r => r.key !== key);
1007
+ }
1008
+
1009
+ const now = new Date().toISOString();
1010
+
1011
+ if (this.editingId) {
1012
+ // Update
1013
+ const index = this.data.findIndex(r => r.id === this.editingId);
1014
+ if (index !== -1) {
1015
+ this.data[index] = {
1016
+ ...this.data[index],
1017
+ key,
1018
+ value,
1019
+ url,
1020
+ updatedAt: now
1021
+ };
1022
+ this.showToast('Registro actualizado exitosamente', 'success');
1023
+ }
1024
+ } else {
1025
+ // Create
1026
+ const newRecord = {
1027
+ id: this.generateId(),
1028
+ key,
1029
+ value,
1030
+ url,
1031
+ createdAt: now,
1032
+ updatedAt: now
1033
+ };
1034
+ this.data.push(newRecord);
1035
+ this.showToast('Registro creado exitosamente', 'success');
1036
+ }
1037
+
1038
+ this.saveData();
1039
+ this.render();
1040
+ this.closeModal();
1041
+ },
1042
+
1043
+ deleteRecord(id) {
1044
+ if (confirm('驴Est谩s seguro de eliminar este registro?')) {
1045
+ this.data = this.data.filter(r => r.id !== id);
1046
+ this.saveData();
1047
+ this.render();
1048
+ this.showToast('Registro eliminado', 'warning');
1049
+ }
1050
+ },
1051
+
1052
+ clearAll() {
1053
+ if (this.data.length === 0) {
1054
+ this.showToast('No hay registros para eliminar', 'error');
1055
+ return;
1056
+ }
1057
+
1058
+ if (confirm(`驴Est谩s seguro de eliminar todos los ${this.data.length} registros? Esta acci贸n no se puede deshacer.`)) {
1059
+ this.data = [];
1060
+ this.saveData();
1061
+ this.render();
1062
+ this.showToast('Todos los registros han sido eliminados', 'warning');
1063
+ }
1064
+ },
1065
+
1066
+ getFilteredAndSortedData() {
1067
+ let result = [...this.data];
1068
+
1069
+ // Filter
1070
+ if (this.filter) {
1071
+ result = result.filter(r =>
1072
+ r.key.toLowerCase().includes(this.filter) ||
1073
+ r.value.toLowerCase().includes(this.filter) ||
1074
+ r.url.toLowerCase().includes(this.filter)
1075
+ );
1076
+ }
1077
+
1078
+ // Sort
1079
+ result.sort((a, b) => {
1080
+ switch(this.sortBy) {
1081
+ case 'newest':
1082
+ return new Date(b.createdAt) - new Date(a.createdAt);
1083
+ case 'oldest':
1084
+ return new Date(a.createdAt) - new Date(b.createdAt);
1085
+ case 'key_asc':
1086
+ return a.key.localeCompare(b.key);
1087
+ case 'key_desc':
1088
+ return b.key.localeCompare(a.key);
1089
+ case 'url':
1090
+ return a.url.localeCompare(b.url);
1091
+ default:
1092
+ return 0;
1093
+ }
1094
+ });
1095
+
1096
+ return result;
1097
+ },
1098
+
1099
+ handleHeaderSort(type) {
1100
+ const map = {
1101
+ 'key': this.sortBy === 'key_asc' ? 'key_desc' : 'key_asc',
1102
+ 'date': this.sortBy === 'newest' ? 'oldest' : 'newest'
1103
+ };
1104
+
1105
+ if (map[type]) {
1106
+ $('#sortSelect').val(map[type]).trigger('change');
1107
+ }
1108
+ },
1109
+
1110
+ render() {
1111
+ const displayData = this.getFilteredAndSortedData();
1112
+ const tbody = $('#tableBody');
1113
+ const emptyState = $('#emptyState');
1114
+
1115
+ if (displayData.length === 0) {
1116
+ tbody.empty();
1117
+ emptyState.show();
1118
+ return;
1119
+ }
1120
+
1121
+ emptyState.hide();
1122
+
1123
+ tbody.html(displayData.map(record => {
1124
+ const date = new Date(record.createdAt);
1125
+ const dateStr = date.toLocaleDateString('es-ES', {
1126
+ year: 'numeric',
1127
+ month: 'short',
1128
+ day: 'numeric'
1129
+ });
1130
+ const timeStr = date.toLocaleTimeString('es-ES', {
1131
+ hour: '2-digit',
1132
+ minute: '2-digit'
1133
+ });
1134
+
1135
+ // Truncate value for display
1136
+ const displayValue = record.value.length > 50
1137
+ ? record.value.substring(0, 50) + '...'
1138
+ : record.value;
1139
+
1140
+ return `
1141
+ <tr data-id="${record.id}">
1142
+ <td>
1143
+ <div class="cell-key">
1144
+ <i class="fas fa-key"></i>
1145
+ ${this.escapeHtml(record.key)}
1146
+ </div>
1147
+ </td>
1148
+ <td>
1149
+ <div class="cell-value" title="${this.escapeHtml(record.value)}">
1150
+ ${this.escapeHtml(displayValue)}
1151
+ </div>
1152
+ </td>
1153
+ <td>
1154
+ <div class="cell-date">
1155
+ <span>${dateStr}</span>
1156
+ <span class="time">${timeStr}</span>
1157
+ </div>
1158
+ </td>
1159
+ <td>
1160
+ <div class="cell-url" title="${this.escapeHtml(record.url)}">
1161
+ <a href="${this.escapeHtml(record.url)}" target="_blank" rel="noopener">
1162
+ <i class="fas fa-external-link-alt"></i>
1163
+ ${new URL(record.url).hostname}
1164
+ </a>
1165
+ </div>
1166
+ </td>
1167
+ <td>
1168
+ <div class="actions">
1169
+ <button class="btn-icon" onclick="App.openModal('${record.id}')" title="Editar">
1170
+ <i class="fas fa-edit"></i>
1171
+ </button>
1172
+ <button class="btn-icon delete" onclick="App.deleteRecord('${record.id}')" title="Eliminar">
1173
+ <i class="fas fa-trash"></i>
1174
+ </button>
1175
+ </div>
1176
+ </td>
1177
+ </tr>
1178
+ `;
1179
+ }).join(''));
1180
+ },
1181
+
1182
+ updateStats() {
1183
+ $('#totalRecords').text(this.data.length);
1184
+
1185
+ // Recent (24h)
1186
+ const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
1187
+ const recent = this.data.filter(r => new Date(r.createdAt) > oneDayAgo).length;
1188
+ $('#recentRecords').text(recent);
1189
+
1190
+ // Unique URLs
1191
+ const uniqueUrls = new Set(this.data.map(r => r.url)).size;
1192
+ $('#uniqueUrls').text(uniqueUrls);
1193
+
1194
+ // Size estimation (rough)
1195
+ const size = new Blob([JSON.stringify(this.data)]).size;
1196
+ const sizeKB = (size / 1024).toFixed(2);
1197
+ $('#totalSize').text(sizeKB + ' KB');
1198
+ },
1199
+
1200
+ exportJSON() {
1201
+ if (this.data.length === 0) {
1202
+ this.showToast('No hay datos para exportar', 'error');
1203
+ return;
1204
+ }
1205
+
1206
+ const dataStr = JSON.stringify(this.data, null, 2);
1207
+ const blob = new Blob([dataStr], { type: 'application/json' });
1208
+ const url = URL.createObjectURL(blob);
1209
+ const a = document.createElement('a');
1210
+ a.href = url;
1211
+ a.download = `localstorage_backup_${new Date().toISOString().split('T')[0]}.json`;
1212
+ a.click();
1213
+ URL.revokeObjectURL(url);
1214
+ this.showToast('Archivo JSON descargado', 'success');
1215
+ },
1216
+
1217
+ exportCSV() {
1218
+ if (this.data.length === 0) {
1219
+ this.showToast('No hay datos para exportar', 'error');
1220
+ return;
1221
+ }
1222
+
1223
+ const headers = ['ID', 'Llave', 'Valor', 'URL', 'Fecha Creaci贸n', '脷ltima Actualizaci贸n'];
1224
+ const rows = this.data.map(r => [
1225
+ r.id,
1226
+ `"${r.key.replace(/"/g, '""')}"`,
1227
+ `"${r.value.replace(/"/g, '""')}"`,
1228
+ `"${r.url}"`,
1229
+ r.createdAt,
1230
+ r.updatedAt
1231
+ ]);
1232
+
1233
+ const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n');
1234
+ const blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8;' });
1235
+ const url = URL.createObjectURL(blob);
1236
+ const a = document.createElement('a');
1237
+ a.href = url;
1238
+ a.download = `localstorage_backup_${new Date().toISOString().split('T')[0]}.csv`;
1239
+ a.click();
1240
+ URL.revokeObjectURL(url);
1241
+ this.showToast('Archivo CSV descargado', 'success');
1242
+ },
1243
+
1244
+ showToast(message, type = 'info') {
1245
+ const icons = {
1246
+ success: 'check-circle',
1247
+ error: 'exclamation-circle',
1248
+ warning: 'exclamation-triangle',
1249
+ info: 'info-circle'
1250
+ };
1251
+
1252
+ const toast = $(`
1253
+ <div class="toast ${type}">
1254
+ <i class="fas fa-${icons[type]}"></i>
1255
+ <span>${message}</span>
1256
+ </div>
1257
+ `);
1258
+
1259
+ $('#toastContainer').append(toast);
1260
+
1261
+ setTimeout(() => {
1262
+ toast.remove();
1263
+ }, 3000);
1264
+ },
1265
+
1266
+ escapeHtml(text) {
1267
+ const div = document.createElement('div');
1268
+ div.textContent = text;
1269
+ return div.innerHTML;
1270
+ }
1271
+ };
1272
+
1273
+ // Initialize
1274
+ $(document).ready(() => App.init());
1275
+ </script>
1276
+ </body>
1277
+ </html>