akhaliq HF Staff commited on
Commit
d38d101
·
verified ·
1 Parent(s): 0202351

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +686 -19
index.html CHANGED
@@ -1,19 +1,686 @@
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>Taskflow - Modern Todo App</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
11
+ <style>
12
+ :root {
13
+ --bg: #0a0a0f;
14
+ --bg-secondary: #12121a;
15
+ --fg: #f0f0f5;
16
+ --muted: #6b6b80;
17
+ --accent: #00d4aa;
18
+ --accent-glow: rgba(0, 212, 170, 0.3);
19
+ --card: rgba(20, 20, 30, 0.8);
20
+ --border: rgba(255, 255, 255, 0.08);
21
+ --danger: #ff4757;
22
+ --danger-glow: rgba(255, 71, 87, 0.3);
23
+ }
24
+
25
+ * {
26
+ margin: 0;
27
+ padding: 0;
28
+ box-sizing: border-box;
29
+ }
30
+
31
+ html {
32
+ scroll-behavior: smooth;
33
+ }
34
+
35
+ body {
36
+ font-family: 'Inter', sans-serif;
37
+ background: var(--bg);
38
+ color: var(--fg);
39
+ min-height: 100vh;
40
+ overflow-x: hidden;
41
+ }
42
+
43
+ .font-display {
44
+ font-family: 'Space Grotesk', sans-serif;
45
+ }
46
+
47
+ /* Animated background */
48
+ .bg-mesh {
49
+ position: fixed;
50
+ inset: 0;
51
+ z-index: -1;
52
+ overflow: hidden;
53
+ }
54
+
55
+ .bg-mesh::before {
56
+ content: '';
57
+ position: absolute;
58
+ width: 150%;
59
+ height: 150%;
60
+ top: -25%;
61
+ left: -25%;
62
+ background:
63
+ radial-gradient(ellipse 600px 600px at 20% 20%, rgba(0, 212, 170, 0.08) 0%, transparent 50%),
64
+ radial-gradient(ellipse 500px 500px at 80% 80%, rgba(100, 50, 200, 0.06) 0%, transparent 50%),
65
+ radial-gradient(ellipse 400px 400px at 60% 30%, rgba(0, 150, 255, 0.05) 0%, transparent 50%);
66
+ animation: meshMove 20s ease-in-out infinite;
67
+ }
68
+
69
+ @keyframes meshMove {
70
+ 0%, 100% { transform: translate(0, 0) rotate(0deg); }
71
+ 33% { transform: translate(2%, 2%) rotate(1deg); }
72
+ 66% { transform: translate(-1%, 1%) rotate(-1deg); }
73
+ }
74
+
75
+ /* Grid pattern overlay */
76
+ .grid-pattern {
77
+ position: fixed;
78
+ inset: 0;
79
+ z-index: -1;
80
+ background-image:
81
+ linear-gradient(rgba(255, 255, 255, 0.02) 1px, transparent 1px),
82
+ linear-gradient(90deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px);
83
+ background-size: 60px 60px;
84
+ mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 20%, transparent 70%);
85
+ }
86
+
87
+ /* Glass card effect */
88
+ .glass-card {
89
+ background: var(--card);
90
+ backdrop-filter: blur(20px);
91
+ border: 1px solid var(--border);
92
+ border-radius: 20px;
93
+ }
94
+
95
+ /* Custom scrollbar */
96
+ ::-webkit-scrollbar {
97
+ width: 6px;
98
+ }
99
+
100
+ ::-webkit-scrollbar-track {
101
+ background: transparent;
102
+ }
103
+
104
+ ::-webkit-scrollbar-thumb {
105
+ background: var(--border);
106
+ border-radius: 3px;
107
+ }
108
+
109
+ ::-webkit-scrollbar-thumb:hover {
110
+ background: rgba(255, 255, 255, 0.15);
111
+ }
112
+
113
+ /* Input styling */
114
+ .todo-input {
115
+ background: rgba(255, 255, 255, 0.03);
116
+ border: 1px solid var(--border);
117
+ border-radius: 14px;
118
+ padding: 16px 20px;
119
+ font-size: 16px;
120
+ color: var(--fg);
121
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
122
+ width: 100%;
123
+ }
124
+
125
+ .todo-input:focus {
126
+ outline: none;
127
+ border-color: var(--accent);
128
+ box-shadow: 0 0 0 4px var(--accent-glow);
129
+ background: rgba(255, 255, 255, 0.05);
130
+ }
131
+
132
+ .todo-input::placeholder {
133
+ color: var(--muted);
134
+ }
135
+
136
+ /* Button styling */
137
+ .btn-primary {
138
+ background: var(--accent);
139
+ color: var(--bg);
140
+ font-weight: 600;
141
+ padding: 16px 28px;
142
+ border-radius: 14px;
143
+ border: none;
144
+ cursor: pointer;
145
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
146
+ font-size: 15px;
147
+ display: flex;
148
+ align-items: center;
149
+ gap: 8px;
150
+ white-space: nowrap;
151
+ }
152
+
153
+ .btn-primary:hover {
154
+ transform: translateY(-2px);
155
+ box-shadow: 0 8px 30px var(--accent-glow);
156
+ }
157
+
158
+ .btn-primary:active {
159
+ transform: translateY(0);
160
+ }
161
+
162
+ /* Filter buttons */
163
+ .filter-btn {
164
+ background: transparent;
165
+ border: 1px solid var(--border);
166
+ color: var(--muted);
167
+ padding: 10px 18px;
168
+ border-radius: 10px;
169
+ cursor: pointer;
170
+ transition: all 0.25s ease;
171
+ font-size: 14px;
172
+ font-weight: 500;
173
+ }
174
+
175
+ .filter-btn:hover {
176
+ border-color: rgba(255, 255, 255, 0.15);
177
+ color: var(--fg);
178
+ }
179
+
180
+ .filter-btn.active {
181
+ background: var(--accent);
182
+ border-color: var(--accent);
183
+ color: var(--bg);
184
+ }
185
+
186
+ /* Todo item */
187
+ .todo-item {
188
+ background: rgba(255, 255, 255, 0.02);
189
+ border: 1px solid var(--border);
190
+ border-radius: 14px;
191
+ padding: 18px 20px;
192
+ display: flex;
193
+ align-items: center;
194
+ gap: 16px;
195
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
196
+ animation: slideIn 0.4s cubic-bezier(0.4, 0, 0.2, 1);
197
+ }
198
+
199
+ .todo-item:hover {
200
+ background: rgba(255, 255, 255, 0.04);
201
+ border-color: rgba(255, 255, 255, 0.12);
202
+ transform: translateX(4px);
203
+ }
204
+
205
+ .todo-item.completed {
206
+ opacity: 0.5;
207
+ }
208
+
209
+ .todo-item.completed .todo-text {
210
+ text-decoration: line-through;
211
+ color: var(--muted);
212
+ }
213
+
214
+ .todo-item.removing {
215
+ animation: slideOut 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
216
+ }
217
+
218
+ @keyframes slideIn {
219
+ from {
220
+ opacity: 0;
221
+ transform: translateY(-10px);
222
+ }
223
+ to {
224
+ opacity: 1;
225
+ transform: translateY(0);
226
+ }
227
+ }
228
+
229
+ @keyframes slideOut {
230
+ to {
231
+ opacity: 0;
232
+ transform: translateX(30px);
233
+ }
234
+ }
235
+
236
+ /* Custom checkbox */
237
+ .custom-checkbox {
238
+ width: 24px;
239
+ height: 24px;
240
+ border: 2px solid var(--border);
241
+ border-radius: 8px;
242
+ cursor: pointer;
243
+ display: flex;
244
+ align-items: center;
245
+ justify-content: center;
246
+ transition: all 0.25s ease;
247
+ flex-shrink: 0;
248
+ }
249
+
250
+ .custom-checkbox:hover {
251
+ border-color: var(--accent);
252
+ }
253
+
254
+ .custom-checkbox.checked {
255
+ background: var(--accent);
256
+ border-color: var(--accent);
257
+ }
258
+
259
+ .custom-checkbox svg {
260
+ opacity: 0;
261
+ transform: scale(0.5);
262
+ transition: all 0.2s ease;
263
+ }
264
+
265
+ .custom-checkbox.checked svg {
266
+ opacity: 1;
267
+ transform: scale(1);
268
+ }
269
+
270
+ /* Delete button */
271
+ .delete-btn {
272
+ background: transparent;
273
+ border: none;
274
+ color: var(--muted);
275
+ cursor: pointer;
276
+ padding: 8px;
277
+ border-radius: 8px;
278
+ transition: all 0.25s ease;
279
+ display: flex;
280
+ align-items: center;
281
+ justify-content: center;
282
+ opacity: 0;
283
+ }
284
+
285
+ .todo-item:hover .delete-btn {
286
+ opacity: 1;
287
+ }
288
+
289
+ .delete-btn:hover {
290
+ background: var(--danger-glow);
291
+ color: var(--danger);
292
+ }
293
+
294
+ /* Progress bar */
295
+ .progress-bar {
296
+ height: 6px;
297
+ background: rgba(255, 255, 255, 0.1);
298
+ border-radius: 3px;
299
+ overflow: hidden;
300
+ }
301
+
302
+ .progress-fill {
303
+ height: 100%;
304
+ background: linear-gradient(90deg, var(--accent), #00ffcc);
305
+ border-radius: 3px;
306
+ transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1);
307
+ }
308
+
309
+ /* Empty state */
310
+ .empty-state {
311
+ text-align: center;
312
+ padding: 60px 20px;
313
+ color: var(--muted);
314
+ }
315
+
316
+ .empty-state svg {
317
+ margin-bottom: 20px;
318
+ opacity: 0.5;
319
+ }
320
+
321
+ /* Stats card */
322
+ .stat-card {
323
+ background: rgba(255, 255, 255, 0.03);
324
+ border: 1px solid var(--border);
325
+ border-radius: 12px;
326
+ padding: 16px 20px;
327
+ text-align: center;
328
+ }
329
+
330
+ .stat-value {
331
+ font-size: 28px;
332
+ font-weight: 700;
333
+ color: var(--accent);
334
+ line-height: 1;
335
+ }
336
+
337
+ .stat-label {
338
+ font-size: 12px;
339
+ color: var(--muted);
340
+ margin-top: 4px;
341
+ text-transform: uppercase;
342
+ letter-spacing: 0.5px;
343
+ }
344
+
345
+ /* Header link */
346
+ .header-link {
347
+ color: var(--muted);
348
+ text-decoration: none;
349
+ font-size: 12px;
350
+ transition: color 0.2s ease;
351
+ }
352
+
353
+ .header-link:hover {
354
+ color: var(--accent);
355
+ }
356
+
357
+ /* Entrance animations */
358
+ .fade-up {
359
+ opacity: 0;
360
+ transform: translateY(20px);
361
+ animation: fadeUp 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;
362
+ }
363
+
364
+ .fade-up:nth-child(1) { animation-delay: 0.1s; }
365
+ .fade-up:nth-child(2) { animation-delay: 0.2s; }
366
+ .fade-up:nth-child(3) { animation-delay: 0.3s; }
367
+
368
+ @keyframes fadeUp {
369
+ to {
370
+ opacity: 1;
371
+ transform: translateY(0);
372
+ }
373
+ }
374
+
375
+ /* Reduced motion */
376
+ @media (prefers-reduced-motion: reduce) {
377
+ *, *::before, *::after {
378
+ animation-duration: 0.01ms !important;
379
+ animation-iteration-count: 1 !important;
380
+ transition-duration: 0.01ms !important;
381
+ }
382
+ }
383
+
384
+ /* Focus visible */
385
+ :focus-visible {
386
+ outline: 2px solid var(--accent);
387
+ outline-offset: 2px;
388
+ }
389
+
390
+ button:focus:not(:focus-visible) {
391
+ outline: none;
392
+ }
393
+ </style>
394
+ </head>
395
+ <body>
396
+ <div class="bg-mesh"></div>
397
+ <div class="grid-pattern"></div>
398
+
399
+ <div class="min-h-screen py-8 px-4 sm:px-6 lg:px-8">
400
+ <div class="max-w-2xl mx-auto">
401
+
402
+ <!-- Header -->
403
+ <header class="text-center mb-10 fade-up">
404
+ <div class="flex items-center justify-between mb-4">
405
+ <span></span>
406
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" rel="noopener" class="header-link">
407
+ Built with anycoder
408
+ </a>
409
+ </div>
410
+ <h1 class="font-display text-4xl sm:text-5xl font-bold tracking-tight mb-3">
411
+ Task<span style="color: var(--accent)">flow</span>
412
+ </h1>
413
+ <p class="text-base" style="color: var(--muted)">Organize your day, one task at a time</p>
414
+ </header>
415
+
416
+ <!-- Main Card -->
417
+ <main class="glass-card p-6 sm:p-8 fade-up" style="animation-delay: 0.15s;">
418
+
419
+ <!-- Input Section -->
420
+ <form id="todo-form" class="flex flex-col sm:flex-row gap-3 mb-8">
421
+ <input
422
+ type="text"
423
+ id="todo-input"
424
+ class="todo-input flex-1"
425
+ placeholder="What needs to be done?"
426
+ aria-label="New todo input"
427
+ autocomplete="off"
428
+ >
429
+ <button type="submit" class="btn-primary justify-center">
430
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
431
+ <line x1="12" y1="5" x2="12" y2="19"></line>
432
+ <line x1="5" y1="12" x2="19" y2="12"></line>
433
+ </svg>
434
+ Add Task
435
+ </button>
436
+ </form>
437
+
438
+ <!-- Progress Section -->
439
+ <div class="mb-6">
440
+ <div class="flex items-center justify-between mb-2">
441
+ <span class="text-sm font-medium" style="color: var(--muted)">Progress</span>
442
+ <span id="progress-text" class="text-sm font-semibold" style="color: var(--accent)">0%</span>
443
+ </div>
444
+ <div class="progress-bar">
445
+ <div id="progress-fill" class="progress-fill" style="width: 0%"></div>
446
+ </div>
447
+ </div>
448
+
449
+ <!-- Stats -->
450
+ <div class="grid grid-cols-3 gap-3 mb-6">
451
+ <div class="stat-card">
452
+ <div id="stat-total" class="stat-value">0</div>
453
+ <div class="stat-label">Total</div>
454
+ </div>
455
+ <div class="stat-card">
456
+ <div id="stat-active" class="stat-value">0</div>
457
+ <div class="stat-label">Active</div>
458
+ </div>
459
+ <div class="stat-card">
460
+ <div id="stat-completed" class="stat-value">0</div>
461
+ <div class="stat-label">Done</div>
462
+ </div>
463
+ </div>
464
+
465
+ <!-- Filters -->
466
+ <div class="flex flex-wrap gap-2 mb-6">
467
+ <button class="filter-btn active" data-filter="all">All</button>
468
+ <button class="filter-btn" data-filter="active">Active</button>
469
+ <button class="filter-btn" data-filter="completed">Completed</button>
470
+ <button id="clear-completed" class="filter-btn ml-auto" style="color: var(--danger); border-color: rgba(255, 71, 87, 0.3);">
471
+ Clear Done
472
+ </button>
473
+ </div>
474
+
475
+ <!-- Todo List -->
476
+ <div id="todo-list" class="space-y-3" role="list" aria-label="Todo list">
477
+ <!-- Todos will be rendered here -->
478
+ </div>
479
+
480
+ <!-- Empty State -->
481
+ <div id="empty-state" class="empty-state">
482
+ <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
483
+ <path d="M9 11l3 3L22 4"></path>
484
+ <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path>
485
+ </svg>
486
+ <p class="font-display text-lg font-medium mb-1">No tasks yet</p>
487
+ <p class="text-sm">Add your first task to get started</p>
488
+ </div>
489
+ </main>
490
+
491
+ <!-- Footer -->
492
+ <footer class="text-center mt-8 fade-up" style="animation-delay: 0.25s;">
493
+ <p class="text-xs" style="color: var(--muted)">
494
+ Press Enter to add task. Click to complete. Data saved locally.
495
+ </p>
496
+ </footer>
497
+ </div>
498
+ </div>
499
+
500
+ <script>
501
+ // Initialize state
502
+ let todos = JSON.parse(localStorage.getItem('taskflow-todos')) || [];
503
+ let currentFilter = 'all';
504
+
505
+ // DOM Elements
506
+ const todoForm = document.getElementById('todo-form');
507
+ const todoInput = document.getElementById('todo-input');
508
+ const todoList = document.getElementById('todo-list');
509
+ const emptyState = document.getElementById('empty-state');
510
+ const filterBtns = document.querySelectorAll('.filter-btn[data-filter]');
511
+ const clearCompletedBtn = document.getElementById('clear-completed');
512
+ const progressFill = document.getElementById('progress-fill');
513
+ const progressText = document.getElementById('progress-text');
514
+ const statTotal = document.getElementById('stat-total');
515
+ const statActive = document.getElementById('stat-active');
516
+ const statCompleted = document.getElementById('stat-completed');
517
+
518
+ // Generate unique ID
519
+ function generateId() {
520
+ return Date.now().toString(36) + Math.random().toString(36).substr(2);
521
+ }
522
+
523
+ // Save to localStorage
524
+ function saveTodos() {
525
+ localStorage.setItem('taskflow-todos', JSON.stringify(todos));
526
+ }
527
+
528
+ // Update stats
529
+ function updateStats() {
530
+ const total = todos.length;
531
+ const completed = todos.filter(t => t.completed).length;
532
+ const active = total - completed;
533
+ const progress = total > 0 ? Math.round((completed / total) * 100) : 0;
534
+
535
+ statTotal.textContent = total;
536
+ statActive.textContent = active;
537
+ statCompleted.textContent = completed;
538
+ progressFill.style.width = `${progress}%`;
539
+ progressText.textContent = `${progress}%`;
540
+ }
541
+
542
+ // Create todo element
543
+ function createTodoElement(todo) {
544
+ const item = document.createElement('div');
545
+ item.className = `todo-item ${todo.completed ? 'completed' : ''}`;
546
+ item.dataset.id = todo.id;
547
+ item.setAttribute('role', 'listitem');
548
+
549
+ item.innerHTML = `
550
+ <div class="custom-checkbox ${todo.completed ? 'checked' : ''}" role="checkbox" aria-checked="${todo.completed}" tabindex="0" aria-label="Mark as ${todo.completed ? 'incomplete' : 'complete'}">
551
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#0a0a0f" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
552
+ <polyline points="20 6 9 17 4 12"></polyline>
553
+ </svg>
554
+ </div>
555
+ <span class="todo-text flex-1">${escapeHtml(todo.text)}</span>
556
+ <button class="delete-btn" aria-label="Delete task">
557
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
558
+ <polyline points="3 6 5 6 21 6"></polyline>
559
+ <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"></path>
560
+ </svg>
561
+ </button>
562
+ `;
563
+
564
+ // Toggle complete
565
+ const checkbox = item.querySelector('.custom-checkbox');
566
+ checkbox.addEventListener('click', () => toggleTodo(todo.id));
567
+ checkbox.addEventListener('keydown', (e) => {
568
+ if (e.key === 'Enter' || e.key === ' ') {
569
+ e.preventDefault();
570
+ toggleTodo(todo.id);
571
+ }
572
+ });
573
+
574
+ // Delete
575
+ const deleteBtn = item.querySelector('.delete-btn');
576
+ deleteBtn.addEventListener('click', () => deleteTodo(todo.id));
577
+
578
+ return item;
579
+ }
580
+
581
+ // Escape HTML
582
+ function escapeHtml(text) {
583
+ const div = document.createElement('div');
584
+ div.textContent = text;
585
+ return div.innerHTML;
586
+ }
587
+
588
+ // Render todos
589
+ function renderTodos() {
590
+ const filteredTodos = todos.filter(todo => {
591
+ if (currentFilter === 'active') return !todo.completed;
592
+ if (currentFilter === 'completed') return todo.completed;
593
+ return true;
594
+ });
595
+
596
+ todoList.innerHTML = '';
597
+
598
+ if (filteredTodos.length === 0) {
599
+ emptyState.style.display = 'block';
600
+ todoList.style.display = 'none';
601
+ } else {
602
+ emptyState.style.display = 'none';
603
+ todoList.style.display = 'flex';
604
+ todoList.style.flexDirection = 'column';
605
+ filteredTodos.forEach(todo => {
606
+ todoList.appendChild(createTodoElement(todo));
607
+ });
608
+ }
609
+
610
+ updateStats();
611
+ }
612
+
613
+ // Add todo
614
+ function addTodo(text) {
615
+ const trimmedText = text.trim();
616
+ if (!trimmedText) return;
617
+
618
+ const todo = {
619
+ id: generateId(),
620
+ text: trimmedText,
621
+ completed: false,
622
+ createdAt: Date.now()
623
+ };
624
+
625
+ todos.unshift(todo);
626
+ saveTodos();
627
+ renderTodos();
628
+ todoInput.value = '';
629
+ }
630
+
631
+ // Toggle todo
632
+ function toggleTodo(id) {
633
+ todos = todos.map(todo =>
634
+ todo.id === id ? { ...todo, completed: !todo.completed } : todo
635
+ );
636
+ saveTodos();
637
+ renderTodos();
638
+ }
639
+
640
+ // Delete todo
641
+ function deleteTodo(id) {
642
+ const item = todoList.querySelector(`[data-id="${id}"]`);
643
+ if (item) {
644
+ item.classList.add('removing');
645
+ setTimeout(() => {
646
+ todos = todos.filter(todo => todo.id !== id);
647
+ saveTodos();
648
+ renderTodos();
649
+ }, 300);
650
+ }
651
+ }
652
+
653
+ // Clear completed
654
+ function clearCompleted() {
655
+ const completedItems = todoList.querySelectorAll('.todo-item.completed');
656
+ completedItems.forEach(item => item.classList.add('removing'));
657
+
658
+ setTimeout(() => {
659
+ todos = todos.filter(todo => !todo.completed);
660
+ saveTodos();
661
+ renderTodos();
662
+ }, 300);
663
+ }
664
+
665
+ // Event Listeners
666
+ todoForm.addEventListener('submit', (e) => {
667
+ e.preventDefault();
668
+ addTodo(todoInput.value);
669
+ });
670
+
671
+ filterBtns.forEach(btn => {
672
+ btn.addEventListener('click', () => {
673
+ filterBtns.forEach(b => b.classList.remove('active'));
674
+ btn.classList.add('active');
675
+ currentFilter = btn.dataset.filter;
676
+ renderTodos();
677
+ });
678
+ });
679
+
680
+ clearCompletedBtn.addEventListener('click', clearCompleted);
681
+
682
+ // Initial render
683
+ renderTodos();
684
+ </script>
685
+ </body>
686
+ </html>