cyberblaster commited on
Commit
fe7c2d5
·
verified ·
1 Parent(s): 2f2a6bd

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +708 -19
index.html CHANGED
@@ -1,19 +1,708 @@
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</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@400;500;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
11
+ <style>
12
+ :root {
13
+ --bg: #0a0a0b;
14
+ --bg-elevated: #131315;
15
+ --fg: #e8e8e8;
16
+ --muted: #6b6b6b;
17
+ --accent: #00ff88;
18
+ --accent-dim: rgba(0, 255, 136, 0.15);
19
+ --danger: #ff4757;
20
+ --border: #2a2a2d;
21
+ --card: #18181b;
22
+ }
23
+
24
+ * {
25
+ box-sizing: border-box;
26
+ }
27
+
28
+ body {
29
+ font-family: 'Space Grotesk', sans-serif;
30
+ background: var(--bg);
31
+ color: var(--fg);
32
+ min-height: 100vh;
33
+ margin: 0;
34
+ overflow-x: hidden;
35
+ }
36
+
37
+ /* Background Pattern */
38
+ .bg-pattern {
39
+ position: fixed;
40
+ inset: 0;
41
+ background-image:
42
+ linear-gradient(rgba(0, 255, 136, 0.03) 1px, transparent 1px),
43
+ linear-gradient(90deg, rgba(0, 255, 136, 0.03) 1px, transparent 1px);
44
+ background-size: 50px 50px;
45
+ pointer-events: none;
46
+ z-index: 0;
47
+ }
48
+
49
+ .bg-glow {
50
+ position: fixed;
51
+ top: -200px;
52
+ right: -200px;
53
+ width: 600px;
54
+ height: 600px;
55
+ background: radial-gradient(circle, rgba(0, 255, 136, 0.08) 0%, transparent 70%);
56
+ pointer-events: none;
57
+ z-index: 0;
58
+ }
59
+
60
+ .bg-glow-2 {
61
+ position: fixed;
62
+ bottom: -300px;
63
+ left: -200px;
64
+ width: 500px;
65
+ height: 500px;
66
+ background: radial-gradient(circle, rgba(0, 255, 136, 0.05) 0%, transparent 70%);
67
+ pointer-events: none;
68
+ z-index: 0;
69
+ }
70
+
71
+ /* Typography */
72
+ .font-mono {
73
+ font-family: 'JetBrains Mono', monospace;
74
+ }
75
+
76
+ /* Custom Scrollbar */
77
+ ::-webkit-scrollbar {
78
+ width: 6px;
79
+ }
80
+
81
+ ::-webkit-scrollbar-track {
82
+ background: var(--bg);
83
+ }
84
+
85
+ ::-webkit-scrollbar-thumb {
86
+ background: var(--border);
87
+ border-radius: 3px;
88
+ }
89
+
90
+ ::-webkit-scrollbar-thumb:hover {
91
+ background: var(--muted);
92
+ }
93
+
94
+ /* Input Styling */
95
+ .todo-input {
96
+ background: var(--bg);
97
+ border: 2px solid var(--border);
98
+ color: var(--fg);
99
+ transition: all 0.2s ease;
100
+ }
101
+
102
+ .todo-input:focus {
103
+ outline: none;
104
+ border-color: var(--accent);
105
+ box-shadow: 0 0 0 4px var(--accent-dim);
106
+ }
107
+
108
+ .todo-input::placeholder {
109
+ color: var(--muted);
110
+ }
111
+
112
+ /* Button Styling */
113
+ .btn-primary {
114
+ background: var(--accent);
115
+ color: var(--bg);
116
+ font-weight: 700;
117
+ transition: all 0.2s ease;
118
+ position: relative;
119
+ overflow: hidden;
120
+ }
121
+
122
+ .btn-primary::before {
123
+ content: '';
124
+ position: absolute;
125
+ inset: 0;
126
+ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
127
+ transform: translateX(-100%);
128
+ transition: transform 0.5s ease;
129
+ }
130
+
131
+ .btn-primary:hover::before {
132
+ transform: translateX(100%);
133
+ }
134
+
135
+ .btn-primary:hover {
136
+ transform: translateY(-2px);
137
+ box-shadow: 0 8px 25px rgba(0, 255, 136, 0.3);
138
+ }
139
+
140
+ .btn-primary:active {
141
+ transform: translateY(0);
142
+ }
143
+
144
+ /* Filter Buttons */
145
+ .filter-btn {
146
+ background: transparent;
147
+ color: var(--muted);
148
+ border: 1px solid transparent;
149
+ transition: all 0.2s ease;
150
+ }
151
+
152
+ .filter-btn:hover {
153
+ color: var(--fg);
154
+ background: var(--bg-elevated);
155
+ }
156
+
157
+ .filter-btn.active {
158
+ color: var(--accent);
159
+ border-color: var(--accent);
160
+ background: var(--accent-dim);
161
+ }
162
+
163
+ /* Todo Item */
164
+ .todo-item {
165
+ background: var(--card);
166
+ border: 1px solid var(--border);
167
+ transition: all 0.3s ease;
168
+ animation: slideIn 0.4s ease forwards;
169
+ opacity: 0;
170
+ transform: translateY(20px);
171
+ }
172
+
173
+ .todo-item:hover {
174
+ border-color: rgba(0, 255, 136, 0.3);
175
+ transform: translateX(4px);
176
+ }
177
+
178
+ .todo-item.completed {
179
+ opacity: 0.5;
180
+ }
181
+
182
+ .todo-item.completed .todo-text {
183
+ text-decoration: line-through;
184
+ color: var(--muted);
185
+ }
186
+
187
+ .todo-item.removing {
188
+ animation: slideOut 0.3s ease forwards;
189
+ }
190
+
191
+ @keyframes slideIn {
192
+ to {
193
+ opacity: 1;
194
+ transform: translateY(0);
195
+ }
196
+ }
197
+
198
+ @keyframes slideOut {
199
+ to {
200
+ opacity: 0;
201
+ transform: translateX(100px);
202
+ }
203
+ }
204
+
205
+ /* Checkbox */
206
+ .checkbox-container {
207
+ position: relative;
208
+ width: 24px;
209
+ height: 24px;
210
+ flex-shrink: 0;
211
+ }
212
+
213
+ .checkbox-input {
214
+ position: absolute;
215
+ opacity: 0;
216
+ width: 100%;
217
+ height: 100%;
218
+ cursor: pointer;
219
+ z-index: 2;
220
+ }
221
+
222
+ .checkbox-custom {
223
+ position: absolute;
224
+ inset: 0;
225
+ border: 2px solid var(--border);
226
+ border-radius: 6px;
227
+ transition: all 0.2s ease;
228
+ display: flex;
229
+ align-items: center;
230
+ justify-content: center;
231
+ }
232
+
233
+ .checkbox-input:hover + .checkbox-custom {
234
+ border-color: var(--accent);
235
+ }
236
+
237
+ .checkbox-input:checked + .checkbox-custom {
238
+ background: var(--accent);
239
+ border-color: var(--accent);
240
+ }
241
+
242
+ .checkbox-custom svg {
243
+ opacity: 0;
244
+ transform: scale(0);
245
+ transition: all 0.2s ease;
246
+ }
247
+
248
+ .checkbox-input:checked + .checkbox-custom svg {
249
+ opacity: 1;
250
+ transform: scale(1);
251
+ }
252
+
253
+ .checkbox-input:focus-visible + .checkbox-custom {
254
+ box-shadow: 0 0 0 3px var(--accent-dim);
255
+ }
256
+
257
+ /* Delete Button */
258
+ .delete-btn {
259
+ opacity: 0;
260
+ transition: all 0.2s ease;
261
+ color: var(--muted);
262
+ }
263
+
264
+ .todo-item:hover .delete-btn {
265
+ opacity: 1;
266
+ }
267
+
268
+ .delete-btn:hover {
269
+ color: var(--danger);
270
+ transform: scale(1.1);
271
+ }
272
+
273
+ /* Progress Bar */
274
+ .progress-bar {
275
+ height: 4px;
276
+ background: var(--border);
277
+ border-radius: 2px;
278
+ overflow: hidden;
279
+ }
280
+
281
+ .progress-fill {
282
+ height: 100%;
283
+ background: linear-gradient(90deg, var(--accent), #00ccff);
284
+ transition: width 0.5s ease;
285
+ border-radius: 2px;
286
+ }
287
+
288
+ /* Stats Card */
289
+ .stat-card {
290
+ background: var(--card);
291
+ border: 1px solid var(--border);
292
+ transition: all 0.2s ease;
293
+ }
294
+
295
+ .stat-card:hover {
296
+ border-color: var(--accent);
297
+ }
298
+
299
+ /* Header Animation */
300
+ .header-title {
301
+ animation: glitch 3s infinite;
302
+ }
303
+
304
+ @keyframes glitch {
305
+ 0%, 90%, 100% {
306
+ text-shadow: none;
307
+ }
308
+ 92% {
309
+ text-shadow: -2px 0 var(--accent), 2px 0 #ff4757;
310
+ }
311
+ 94% {
312
+ text-shadow: 2px 0 var(--accent), -2px 0 #ff4757;
313
+ }
314
+ 96% {
315
+ text-shadow: none;
316
+ }
317
+ }
318
+
319
+ /* Empty State */
320
+ .empty-state {
321
+ animation: float 3s ease-in-out infinite;
322
+ }
323
+
324
+ @keyframes float {
325
+ 0%, 100% {
326
+ transform: translateY(0);
327
+ }
328
+ 50% {
329
+ transform: translateY(-10px);
330
+ }
331
+ }
332
+
333
+ /* Stagger Animation */
334
+ .stagger-1 { animation-delay: 0.1s; }
335
+ .stagger-2 { animation-delay: 0.2s; }
336
+ .stagger-3 { animation-delay: 0.3s; }
337
+ .stagger-4 { animation-delay: 0.4s; }
338
+
339
+ /* Reduced Motion */
340
+ @media (prefers-reduced-motion: reduce) {
341
+ *, *::before, *::after {
342
+ animation-duration: 0.01ms !important;
343
+ animation-iteration-count: 1 !important;
344
+ transition-duration: 0.01ms !important;
345
+ }
346
+ }
347
+
348
+ /* Priority Indicators */
349
+ .priority-high { border-left: 3px solid var(--danger); }
350
+ .priority-medium { border-left: 3px solid #ffa502; }
351
+ .priority-low { border-left: 3px solid var(--accent); }
352
+
353
+ /* Priority Select */
354
+ .priority-select {
355
+ background: var(--bg);
356
+ border: 1px solid var(--border);
357
+ color: var(--fg);
358
+ transition: all 0.2s ease;
359
+ }
360
+
361
+ .priority-select:focus {
362
+ outline: none;
363
+ border-color: var(--accent);
364
+ }
365
+ </style>
366
+ </head>
367
+ <body>
368
+ <div class="bg-pattern"></div>
369
+ <div class="bg-glow"></div>
370
+ <div class="bg-glow-2"></div>
371
+
372
+ <div class="relative z-10 min-h-screen px-4 py-8 md:py-12">
373
+ <div class="max-w-2xl mx-auto">
374
+
375
+ <!-- Header -->
376
+ <header class="mb-10 md:mb-12">
377
+ <div class="flex items-center justify-between mb-2">
378
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" rel="noopener noreferrer"
379
+ class="text-xs font-mono text-[var(--muted)] hover:text-[var(--accent)] transition-colors">
380
+ Built with anycoder
381
+ </a>
382
+ <span class="font-mono text-xs text-[var(--muted)]" id="datetime"></span>
383
+ </div>
384
+ <h1 class="header-title text-4xl md:text-5xl font-bold tracking-tight">
385
+ TASK<span class="text-[var(--accent)]">FLOW</span>
386
+ </h1>
387
+ <p class="text-[var(--muted)] mt-2 font-mono text-sm">// your productivity terminal</p>
388
+ </header>
389
+
390
+ <!-- Stats -->
391
+ <div class="grid grid-cols-3 gap-3 mb-8" id="stats">
392
+ <div class="stat-card rounded-lg p-4">
393
+ <div class="text-2xl md:text-3xl font-bold text-[var(--accent)]" id="totalCount">0</div>
394
+ <div class="text-xs text-[var(--muted)] font-mono mt-1">TOTAL</div>
395
+ </div>
396
+ <div class="stat-card rounded-lg p-4">
397
+ <div class="text-2xl md:text-3xl font-bold" id="activeCount">0</div>
398
+ <div class="text-xs text-[var(--muted)] font-mono mt-1">ACTIVE</div>
399
+ </div>
400
+ <div class="stat-card rounded-lg p-4">
401
+ <div class="text-2xl md:text-3xl font-bold text-[var(--accent)]" id="completedCount">0</div>
402
+ <div class="text-xs text-[var(--muted)] font-mono mt-1">DONE</div>
403
+ </div>
404
+ </div>
405
+
406
+ <!-- Progress Bar -->
407
+ <div class="mb-8">
408
+ <div class="flex justify-between items-center mb-2">
409
+ <span class="text-xs font-mono text-[var(--muted)]">PROGRESS</span>
410
+ <span class="text-xs font-mono text-[var(--accent)]" id="progressPercent">0%</span>
411
+ </div>
412
+ <div class="progress-bar">
413
+ <div class="progress-fill" id="progressFill" style="width: 0%"></div>
414
+ </div>
415
+ </div>
416
+
417
+ <!-- Input Section -->
418
+ <div class="mb-8">
419
+ <form id="todoForm" class="flex flex-col sm:flex-row gap-3">
420
+ <input
421
+ type="text"
422
+ id="todoInput"
423
+ class="todo-input flex-1 px-4 py-3 rounded-lg text-base"
424
+ placeholder="What needs to be done?"
425
+ autocomplete="off"
426
+ aria-label="New todo input"
427
+ >
428
+ <select id="prioritySelect" class="priority-select px-4 py-3 rounded-lg font-mono text-sm" aria-label="Priority level">
429
+ <option value="low">LOW</option>
430
+ <option value="medium" selected>MED</option>
431
+ <option value="high">HIGH</option>
432
+ </select>
433
+ <button
434
+ type="submit"
435
+ class="btn-primary px-6 py-3 rounded-lg font-mono text-sm whitespace-nowrap"
436
+ aria-label="Add todo"
437
+ >
438
+ ADD TASK
439
+ </button>
440
+ </form>
441
+ </div>
442
+
443
+ <!-- Filters -->
444
+ <div class="flex gap-2 mb-6 overflow-x-auto pb-2" role="tablist" aria-label="Filter todos">
445
+ <button class="filter-btn active px-4 py-2 rounded-lg font-mono text-xs" data-filter="all" role="tab" aria-selected="true">
446
+ ALL
447
+ </button>
448
+ <button class="filter-btn px-4 py-2 rounded-lg font-mono text-xs" data-filter="active" role="tab" aria-selected="false">
449
+ ACTIVE
450
+ </button>
451
+ <button class="filter-btn px-4 py-2 rounded-lg font-mono text-xs" data-filter="completed" role="tab" aria-selected="false">
452
+ COMPLETED
453
+ </button>
454
+ </div>
455
+
456
+ <!-- Todo List -->
457
+ <div id="todoList" class="space-y-3" role="list" aria-label="Todo list">
458
+ <!-- Todos will be rendered here -->
459
+ </div>
460
+
461
+ <!-- Empty State -->
462
+ <div id="emptyState" class="hidden text-center py-16">
463
+ <div class="empty-state inline-block mb-4">
464
+ <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" class="text-[var(--muted)]">
465
+ <path d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"/>
466
+ </svg>
467
+ </div>
468
+ <p class="text-[var(--muted)] font-mono text-sm">NO TASKS YET</p>
469
+ <p class="text-[var(--muted)] font-mono text-xs mt-1 opacity-50">// add your first task above</p>
470
+ </div>
471
+
472
+ <!-- Clear Completed -->
473
+ <div class="mt-8 flex justify-between items-center" id="clearSection">
474
+ <span class="text-xs font-mono text-[var(--muted)]" id="itemsLeft">0 items left</span>
475
+ <button
476
+ id="clearCompleted"
477
+ class="text-xs font-mono text-[var(--danger)] hover:underline hidden"
478
+ aria-label="Clear completed tasks"
479
+ >
480
+ CLEAR COMPLETED
481
+ </button>
482
+ </div>
483
+
484
+ </div>
485
+ </div>
486
+
487
+ <script>
488
+ // Initialize todos array first
489
+ let todos = [];
490
+ let currentFilter = 'all';
491
+
492
+ // DOM Elements - get after initialization
493
+ const todoForm = document.getElementById('todoForm');
494
+ const todoInput = document.getElementById('todoInput');
495
+ const prioritySelect = document.getElementById('prioritySelect');
496
+ const todoList = document.getElementById('todoList');
497
+ const emptyState = document.getElementById('emptyState');
498
+ const filterBtns = document.querySelectorAll('.filter-btn');
499
+ const clearCompletedBtn = document.getElementById('clearCompleted');
500
+ const totalCountEl = document.getElementById('totalCount');
501
+ const activeCountEl = document.getElementById('activeCount');
502
+ const completedCountEl = document.getElementById('completedCount');
503
+ const progressFill = document.getElementById('progressFill');
504
+ const progressPercent = document.getElementById('progressPercent');
505
+ const itemsLeft = document.getElementById('itemsLeft');
506
+ const datetimeEl = document.getElementById('datetime');
507
+
508
+ // Load from localStorage
509
+ function loadTodos() {
510
+ const stored = localStorage.getItem('taskflow-todos');
511
+ if (stored) {
512
+ try {
513
+ todos = JSON.parse(stored);
514
+ } catch (e) {
515
+ todos = [];
516
+ }
517
+ }
518
+ }
519
+
520
+ // Save to localStorage
521
+ function saveTodos() {
522
+ localStorage.setItem('taskflow-todos', JSON.stringify(todos));
523
+ }
524
+
525
+ // Generate unique ID
526
+ function generateId() {
527
+ return Date.now().toString(36) + Math.random().toString(36).substr(2);
528
+ }
529
+
530
+ // Update Stats
531
+ function updateStats() {
532
+ const total = todos.length;
533
+ const completed = todos.filter(t => t.completed).length;
534
+ const active = total - completed;
535
+ const percent = total > 0 ? Math.round((completed / total) * 100) : 0;
536
+
537
+ totalCountEl.textContent = total;
538
+ activeCountEl.textContent = active;
539
+ completedCountEl.textContent = completed;
540
+ progressFill.style.width = percent + '%';
541
+ progressPercent.textContent = percent + '%';
542
+ itemsLeft.textContent = active + ' item' + (active !== 1 ? 's' : '') + ' left';
543
+
544
+ // Show/hide clear completed button
545
+ if (completed > 0) {
546
+ clearCompletedBtn.classList.remove('hidden');
547
+ } else {
548
+ clearCompletedBtn.classList.add('hidden');
549
+ }
550
+ }
551
+
552
+ // Render Todos
553
+ function renderTodos() {
554
+ const filtered = todos.filter(todo => {
555
+ if (currentFilter === 'active') return !todo.completed;
556
+ if (currentFilter === 'completed') return todo.completed;
557
+ return true;
558
+ });
559
+
560
+ if (filtered.length === 0) {
561
+ todoList.innerHTML = '';
562
+ emptyState.classList.remove('hidden');
563
+ } else {
564
+ emptyState.classList.add('hidden');
565
+ todoList.innerHTML = filtered.map((todo, index) => `
566
+ <div
567
+ class="todo-item rounded-lg p-4 flex items-center gap-4 priority-${todo.priority} ${todo.completed ? 'completed' : ''} stagger-${(index % 4) + 1}"
568
+ data-id="${todo.id}"
569
+ role="listitem"
570
+ >
571
+ <label class="checkbox-container">
572
+ <input
573
+ type="checkbox"
574
+ class="checkbox-input"
575
+ ${todo.completed ? 'checked' : ''}
576
+ onchange="toggleTodo('${todo.id}')"
577
+ aria-label="${todo.completed ? 'Mark as incomplete' : 'Mark as complete'}"
578
+ >
579
+ <div class="checkbox-custom">
580
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#0a0a0b" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
581
+ <polyline points="20 6 9 17 4 12"></polyline>
582
+ </svg>
583
+ </div>
584
+ </label>
585
+ <span class="todo-text flex-1 text-base">${escapeHtml(todo.text)}</span>
586
+ <span class="font-mono text-xs text-[var(--muted)] hidden sm:block">${todo.priority.toUpperCase()}</span>
587
+ <button
588
+ class="delete-btn p-2 rounded-lg hover:bg-[var(--bg-elevated)] transition-all"
589
+ onclick="deleteTodo('${todo.id}')"
590
+ aria-label="Delete task"
591
+ >
592
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
593
+ <polyline points="3 6 5 6 21 6"></polyline>
594
+ <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>
595
+ </svg>
596
+ </button>
597
+ </div>
598
+ `).join('');
599
+ }
600
+
601
+ updateStats();
602
+ }
603
+
604
+ // Escape HTML to prevent XSS
605
+ function escapeHtml(text) {
606
+ const div = document.createElement('div');
607
+ div.textContent = text;
608
+ return div.innerHTML;
609
+ }
610
+
611
+ // Add Todo
612
+ function addTodo(text, priority) {
613
+ if (!text.trim()) return;
614
+
615
+ todos.unshift({
616
+ id: generateId(),
617
+ text: text.trim(),
618
+ completed: false,
619
+ priority: priority,
620
+ createdAt: new Date().toISOString()
621
+ });
622
+
623
+ saveTodos();
624
+ renderTodos();
625
+ }
626
+
627
+ // Toggle Todo
628
+ function toggleTodo(id) {
629
+ const todo = todos.find(t => t.id === id);
630
+ if (todo) {
631
+ todo.completed = !todo.completed;
632
+ saveTodos();
633
+ renderTodos();
634
+ }
635
+ }
636
+
637
+ // Delete Todo
638
+ function deleteTodo(id) {
639
+ const item = document.querySelector(`[data-id="${id}"]`);
640
+ if (item) {
641
+ item.classList.add('removing');
642
+ setTimeout(() => {
643
+ todos = todos.filter(t => t.id !== id);
644
+ saveTodos();
645
+ renderTodos();
646
+ }, 300);
647
+ }
648
+ }
649
+
650
+ // Clear Completed
651
+ function clearCompleted() {
652
+ todos = todos.filter(t => !t.completed);
653
+ saveTodos();
654
+ renderTodos();
655
+ }
656
+
657
+ // Update DateTime
658
+ function updateDateTime() {
659
+ const now = new Date();
660
+ const options = {
661
+ weekday: 'short',
662
+ month: 'short',
663
+ day: 'numeric',
664
+ hour: '2-digit',
665
+ minute: '2-digit'
666
+ };
667
+ datetimeEl.textContent = now.toLocaleDateString('en-US', options).toUpperCase();
668
+ }
669
+
670
+ // Event Listeners
671
+ todoForm.addEventListener('submit', (e) => {
672
+ e.preventDefault();
673
+ addTodo(todoInput.value, prioritySelect.value);
674
+ todoInput.value = '';
675
+ todoInput.focus();
676
+ });
677
+
678
+ filterBtns.forEach(btn => {
679
+ btn.addEventListener('click', () => {
680
+ filterBtns.forEach(b => {
681
+ b.classList.remove('active');
682
+ b.setAttribute('aria-selected', 'false');
683
+ });
684
+ btn.classList.add('active');
685
+ btn.setAttribute('aria-selected', 'true');
686
+ currentFilter = btn.dataset.filter;
687
+ renderTodos();
688
+ });
689
+ });
690
+
691
+ clearCompletedBtn.addEventListener('click', clearCompleted);
692
+
693
+ // Keyboard Navigation
694
+ document.addEventListener('keydown', (e) => {
695
+ if (e.key === '/' && !['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) {
696
+ e.preventDefault();
697
+ todoInput.focus();
698
+ }
699
+ });
700
+
701
+ // Initialize
702
+ loadTodos();
703
+ renderTodos();
704
+ updateDateTime();
705
+ setInterval(updateDateTime, 60000);
706
+ </script>
707
+ </body>
708
+ </html>