AnkhLP commited on
Commit
3e266bd
Β·
verified Β·
1 Parent(s): e69d763

Upload folder using huggingface_hub

Browse files
Files changed (5) hide show
  1. assets/css/styles.css +348 -0
  2. assets/js/app.js +180 -0
  3. assets/js/fireworks.js +153 -0
  4. index.html +76 -19
  5. manifest.json +21 -0
assets/css/styles.css ADDED
@@ -0,0 +1,348 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ :root {
8
+ --primary: #6366f1;
9
+ --primary-dark: #4f46e5;
10
+ --success: #10b981;
11
+ --danger: #ef4444;
12
+ --text: #1f2937;
13
+ --text-light: #6b7280;
14
+ --border: #e5e7eb;
15
+ --bg: #ffffff;
16
+ --bg-secondary: #f9fafb;
17
+ --shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
18
+ --shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.1);
19
+ }
20
+
21
+ body {
22
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
23
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
24
+ min-height: 100vh;
25
+ color: var(--text);
26
+ line-height: 1.6;
27
+ }
28
+
29
+ #fireworks {
30
+ position: fixed;
31
+ top: 0;
32
+ left: 0;
33
+ width: 100%;
34
+ height: 100%;
35
+ pointer-events: none;
36
+ z-index: 1;
37
+ }
38
+
39
+ header {
40
+ background: rgba(255, 255, 255, 0.95);
41
+ backdrop-filter: blur(10px);
42
+ padding: 1rem;
43
+ box-shadow: var(--shadow-lg);
44
+ position: relative;
45
+ z-index: 10;
46
+ }
47
+
48
+ .header-content {
49
+ max-width: 600px;
50
+ margin: 0 auto;
51
+ display: flex;
52
+ justify-content: space-between;
53
+ align-items: center;
54
+ }
55
+
56
+ h1 {
57
+ font-size: 1.75rem;
58
+ color: var(--primary);
59
+ font-weight: 700;
60
+ }
61
+
62
+ .built-with {
63
+ font-size: 0.75rem;
64
+ color: var(--text-light);
65
+ text-decoration: none;
66
+ transition: color 0.2s;
67
+ }
68
+
69
+ .built-with:hover {
70
+ color: var(--primary);
71
+ }
72
+
73
+ .container {
74
+ max-width: 600px;
75
+ margin: 2rem auto;
76
+ padding: 0 1rem;
77
+ position: relative;
78
+ z-index: 5;
79
+ }
80
+
81
+ .input-section {
82
+ background: var(--bg);
83
+ border-radius: 1rem;
84
+ padding: 1.5rem;
85
+ box-shadow: var(--shadow-lg);
86
+ margin-bottom: 1.5rem;
87
+ }
88
+
89
+ .input-group {
90
+ display: flex;
91
+ gap: 0.75rem;
92
+ margin-bottom: 1rem;
93
+ }
94
+
95
+ #taskInput {
96
+ flex: 1;
97
+ padding: 0.875rem 1rem;
98
+ border: 2px solid var(--border);
99
+ border-radius: 0.75rem;
100
+ font-size: 1rem;
101
+ transition: all 0.2s;
102
+ background: var(--bg-secondary);
103
+ }
104
+
105
+ #taskInput:focus {
106
+ outline: none;
107
+ border-color: var(--primary);
108
+ background: var(--bg);
109
+ }
110
+
111
+ #addBtn {
112
+ padding: 0.875rem 1.25rem;
113
+ background: var(--primary);
114
+ color: white;
115
+ border: none;
116
+ border-radius: 0.75rem;
117
+ font-size: 1.25rem;
118
+ cursor: pointer;
119
+ transition: all 0.2s;
120
+ display: flex;
121
+ align-items: center;
122
+ justify-content: center;
123
+ min-width: 50px;
124
+ }
125
+
126
+ #addBtn:hover {
127
+ background: var(--primary-dark);
128
+ transform: scale(1.05);
129
+ }
130
+
131
+ #addBtn:active {
132
+ transform: scale(0.95);
133
+ }
134
+
135
+ .stats {
136
+ display: flex;
137
+ gap: 1.5rem;
138
+ font-size: 0.875rem;
139
+ color: var(--text-light);
140
+ }
141
+
142
+ .filter-tabs {
143
+ display: flex;
144
+ gap: 0.5rem;
145
+ margin-bottom: 1rem;
146
+ background: rgba(255, 255, 255, 0.9);
147
+ padding: 0.5rem;
148
+ border-radius: 0.75rem;
149
+ }
150
+
151
+ .filter-btn {
152
+ flex: 1;
153
+ padding: 0.625rem;
154
+ border: none;
155
+ background: transparent;
156
+ color: var(--text-light);
157
+ border-radius: 0.5rem;
158
+ cursor: pointer;
159
+ transition: all 0.2s;
160
+ font-weight: 500;
161
+ }
162
+
163
+ .filter-btn.active {
164
+ background: var(--primary);
165
+ color: white;
166
+ }
167
+
168
+ .filter-btn:hover:not(.active) {
169
+ background: var(--bg-secondary);
170
+ }
171
+
172
+ .task-list {
173
+ display: flex;
174
+ flex-direction: column;
175
+ gap: 0.75rem;
176
+ margin-bottom: 1.5rem;
177
+ }
178
+
179
+ .task-item {
180
+ background: rgba(255, 255, 255, 0.95);
181
+ padding: 1rem;
182
+ border-radius: 0.75rem;
183
+ display: flex;
184
+ align-items: flex-start;
185
+ gap: 0.875rem;
186
+ transition: all 0.2s;
187
+ animation: slideIn 0.3s ease-out;
188
+ }
189
+
190
+ @keyframes slideIn {
191
+ from {
192
+ opacity: 0;
193
+ transform: translateX(-20px);
194
+ }
195
+ to {
196
+ opacity: 1;
197
+ transform: translateX(0);
198
+ }
199
+ }
200
+
201
+ .task-item:hover {
202
+ transform: translateY(-2px);
203
+ box-shadow: var(--shadow-lg);
204
+ }
205
+
206
+ .task-item.completed {
207
+ opacity: 0.7;
208
+ }
209
+
210
+ .task-checkbox {
211
+ width: 1.5rem;
212
+ height: 1.5rem;
213
+ border: 2px solid var(--border);
214
+ border-radius: 0.375rem;
215
+ cursor: pointer;
216
+ transition: all 0.2s;
217
+ position: relative;
218
+ flex-shrink: 0;
219
+ margin-top: 0.125rem;
220
+ }
221
+
222
+ .task-checkbox:hover {
223
+ border-color: var(--primary);
224
+ }
225
+
226
+ .task-checkbox.checked {
227
+ background: var(--success);
228
+ border-color: var(--success);
229
+ }
230
+
231
+ .task-checkbox.checked::after {
232
+ content: 'βœ“';
233
+ position: absolute;
234
+ top: 50%;
235
+ left: 50%;
236
+ transform: translate(-50%, -50%);
237
+ color: white;
238
+ font-size: 1rem;
239
+ }
240
+
241
+ .task-content {
242
+ flex: 1;
243
+ }
244
+
245
+ .task-text {
246
+ font-size: 1rem;
247
+ margin-bottom: 0.25rem;
248
+ word-break: break-word;
249
+ }
250
+
251
+ .task-item.completed .task-text {
252
+ text-decoration: line-through;
253
+ color: var(--text-light);
254
+ }
255
+
256
+ .task-timestamps {
257
+ font-size: 0.75rem;
258
+ color: var(--text-light);
259
+ display: flex;
260
+ flex-direction: column;
261
+ gap: 0.125rem;
262
+ }
263
+
264
+ .timestamp {
265
+ display: flex;
266
+ align-items: center;
267
+ gap: 0.25rem;
268
+ }
269
+
270
+ .delete-btn {
271
+ background: var(--danger);
272
+ color: white;
273
+ border: none;
274
+ padding: 0.375rem 0.75rem;
275
+ border-radius: 0.375rem;
276
+ cursor: pointer;
277
+ font-size: 0.875rem;
278
+ transition: all 0.2s;
279
+ opacity: 0.8;
280
+ }
281
+
282
+ .delete-btn:hover {
283
+ opacity: 1;
284
+ transform: scale(1.05);
285
+ }
286
+
287
+ .empty-state {
288
+ text-align: center;
289
+ padding: 3rem 1rem;
290
+ color: white;
291
+ display: none;
292
+ }
293
+
294
+ .empty-state.show {
295
+ display: block;
296
+ }
297
+
298
+ .empty-icon {
299
+ font-size: 3rem;
300
+ margin-bottom: 1rem;
301
+ }
302
+
303
+ .actions {
304
+ display: flex;
305
+ justify-content: center;
306
+ margin-top: 2rem;
307
+ }
308
+
309
+ .clear-btn {
310
+ background: rgba(255, 255, 255, 0.9);
311
+ color: var(--danger);
312
+ border: 2px solid var(--danger);
313
+ padding: 0.75rem 1.5rem;
314
+ border-radius: 0.75rem;
315
+ cursor: pointer;
316
+ font-weight: 500;
317
+ transition: all 0.2s;
318
+ }
319
+
320
+ .clear-btn:hover {
321
+ background: var(--danger);
322
+ color: white;
323
+ }
324
+
325
+ footer {
326
+ text-align: center;
327
+ padding: 2rem 1rem;
328
+ color: white;
329
+ font-size: 0.875rem;
330
+ }
331
+
332
+ @media (max-width: 480px) {
333
+ h1 {
334
+ font-size: 1.5rem;
335
+ }
336
+
337
+ .built-with {
338
+ display: none;
339
+ }
340
+
341
+ .input-section {
342
+ padding: 1rem;
343
+ }
344
+
345
+ .stats {
346
+ font-size: 0.75rem;
347
+ }
348
+ }
assets/js/app.js ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class TodoApp {
2
+ constructor() {
3
+ this.tasks = [];
4
+ this.currentFilter = 'all';
5
+ this.initElements();
6
+ this.initEventListeners();
7
+ this.loadTasks();
8
+ this.initFireworks();
9
+ }
10
+
11
+ initElements() {
12
+ this.taskInput = document.getElementById('taskInput');
13
+ this.addBtn = document.getElementById('addBtn');
14
+ this.taskList = document.getElementById('taskList');
15
+ this.emptyState = document.getElementById('emptyState');
16
+ this.clearCompletedBtn = document.getElementById('clearCompleted');
17
+ this.totalTasksEl = document.getElementById('totalTasks');
18
+ this.completedTasksEl = document.getElementById('completedTasks');
19
+ this.filterBtns = document.querySelectorAll('.filter-btn');
20
+ }
21
+
22
+ initEventListeners() {
23
+ this.addBtn.addEventListener('click', () => this.addTask());
24
+ this.taskInput.addEventListener('keypress', (e) => {
25
+ if (e.key === 'Enter') this.addTask();
26
+ });
27
+
28
+ this.clearCompletedBtn.addEventListener('click', () => this.clearCompleted());
29
+
30
+ this.filterBtns.forEach(btn => {
31
+ btn.addEventListener('click', () => {
32
+ this.filterBtns.forEach(b => b.classList.remove('active'));
33
+ btn.classList.add('active');
34
+ this.currentFilter = btn.dataset.filter;
35
+ this.render();
36
+ });
37
+ });
38
+ }
39
+
40
+ initFireworks() {
41
+ const canvas = document.getElementById('fireworks');
42
+ this.fireworks = new Fireworks(canvas);
43
+ }
44
+
45
+ loadTasks() {
46
+ const stored = localStorage.getItem('tasks');
47
+ if (stored) {
48
+ this.tasks = JSON.parse(stored);
49
+ }
50
+ this.render();
51
+ }
52
+
53
+ saveTasks() {
54
+ localStorage.setItem('tasks', JSON.stringify(this.tasks));
55
+ }
56
+
57
+ addTask() {
58
+ const text = this.taskInput.value.trim();
59
+ if (!text) return;
60
+
61
+ const task = {
62
+ id: Date.now(),
63
+ text: text,
64
+ createdAt: new Date().toISOString(),
65
+ completedAt: null,
66
+ completed: false
67
+ };
68
+
69
+ this.tasks.unshift(task);
70
+ this.taskInput.value = '';
71
+ this.saveTasks();
72
+ this.render();
73
+ }
74
+
75
+ toggleTask(id) {
76
+ const task = this.tasks.find(t => t.id === id);
77
+ if (!task) return;
78
+
79
+ task.completed = !task.completed;
80
+ task.completedAt = task.completed ? new Date().toISOString() : null;
81
+
82
+ if (task.completed) {
83
+ this.fireworks.start();
84
+ }
85
+
86
+ this.saveTasks();
87
+ this.render();
88
+ }
89
+
90
+ deleteTask(id) {
91
+ this.tasks = this.tasks.filter(t => t.id !== id);
92
+ this.saveTasks();
93
+ this.render();
94
+ }
95
+
96
+ clearCompleted() {
97
+ this.tasks = this.tasks.filter(t => !t.completed);
98
+ this.saveTasks();
99
+ this.render();
100
+ }
101
+
102
+ formatDate(dateString) {
103
+ const date = new Date(dateString);
104
+ const now = new Date();
105
+ const diff = now - date;
106
+ const minutes = Math.floor(diff / 60000);
107
+ const hours = Math.floor(diff / 3600000);
108
+ const days = Math.floor(diff / 86400000);
109
+
110
+ if (minutes < 1) return 'just now';
111
+ if (minutes < 60) return `${minutes}m ago`;
112
+ if (hours < 24) return `${hours}h ago`;
113
+ if (days < 7) return `${days}d ago`;
114
+
115
+ return date.toLocaleDateString();
116
+ }
117
+
118
+ getFilteredTasks() {
119
+ switch (this.currentFilter) {
120
+ case 'active':
121
+ return this.tasks.filter(t => !t.completed);
122
+ case 'completed':
123
+ return this.tasks.filter(t => t.completed);
124
+ default:
125
+ return this.tasks;
126
+ }
127
+ }
128
+
129
+ updateStats() {
130
+ const total = this.tasks.length;
131
+ const completed = this.tasks.filter(t => t.completed).length;
132
+
133
+ this.totalTasksEl.textContent = `${total} ${total === 1 ? 'task' : 'tasks'}`;
134
+ this.completedTasksEl.textContent = `${completed} completed`;
135
+ }
136
+
137
+ render() {
138
+ const filteredTasks = this.getFilteredTasks();
139
+
140
+ this.taskList.innerHTML = '';
141
+ this.emptyState.classList.toggle('show', filteredTasks.length === 0);
142
+
143
+ filteredTasks.forEach(task => {
144
+ const taskEl = document.createElement('div');
145
+ taskEl.className = `task-item ${task.completed ? 'completed' : ''}`;
146
+
147
+ taskEl.innerHTML = `
148
+ <div class="task-checkbox ${task.completed ? 'checked' : ''}"
149
+ onclick="app.toggleTask(${task.id})"></div>
150
+ <div class="task-content">
151
+ <div class="task-text">${this.escapeHtml(task.text)}</div>
152
+ <div class="task-timestamps">
153
+ <div class="timestamp">
154
+ πŸ“… Created: ${this.formatDate(task.createdAt)}
155
+ </div>
156
+ ${task.completedAt ? `
157
+ <div class="timestamp">
158
+ βœ… Completed: ${this.formatDate(task.completedAt)}
159
+ </div>
160
+ ` : ''}
161
+ </div>
162
+ </div>
163
+ <button class="delete-btn" onclick="app.deleteTask(${task.id})">Delete</button>
164
+ `;
165
+
166
+ this.taskList.appendChild(taskEl);
167
+ });
168
+
169
+ this.updateStats();
170
+ }
171
+
172
+ escapeHtml(text) {
173
+ const div = document.createElement('div');
174
+ div.textContent = text;
175
+ return div.innerHTML;
176
+ }
177
+ }
178
+
179
+ // Initialize app
180
+ const app = new TodoApp();
assets/js/fireworks.js ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class Fireworks {
2
+ constructor(canvas) {
3
+ this.canvas = canvas;
4
+ this.ctx = canvas.getContext('2d');
5
+ this.particles = [];
6
+ this.rockets = [];
7
+ this.isActive = false;
8
+
9
+ this.resize();
10
+ window.addEventListener('resize', () => this.resize());
11
+ }
12
+
13
+ resize() {
14
+ this.canvas.width = window.innerWidth;
15
+ this.canvas.height = window.innerHeight;
16
+ }
17
+
18
+ createRocket() {
19
+ this.rockets.push({
20
+ x: Math.random() * this.canvas.width,
21
+ y: this.canvas.height,
22
+ vx: (Math.random() - 0.5) * 2,
23
+ vy: -Math.random() * 3 - 12,
24
+ trail: []
25
+ });
26
+ }
27
+
28
+ createExplosion(x, y) {
29
+ const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#f9ca24', '#f0932b', '#eb4d4b', '#6ab04c', '#130f40'];
30
+ const color = colors[Math.floor(Math.random() * colors.length)];
31
+ const particleCount = 50 + Math.random() * 50;
32
+
33
+ for (let i = 0; i < particleCount; i++) {
34
+ const angle = (Math.PI * 2 * i) / particleCount;
35
+ const velocity = 2 + Math.random() * 4;
36
+
37
+ this.particles.push({
38
+ x: x,
39
+ y: y,
40
+ vx: Math.cos(angle) * velocity,
41
+ vy: Math.sin(angle) * velocity,
42
+ alpha: 1,
43
+ color: color,
44
+ size: 2 + Math.random() * 2
45
+ });
46
+ }
47
+ }
48
+
49
+ update() {
50
+ // Update rockets
51
+ for (let i = this.rockets.length - 1; i >= 0; i--) {
52
+ const rocket = this.rockets[i];
53
+
54
+ rocket.trail.push({ x: rocket.x, y: rocket.y });
55
+ if (rocket.trail.length > 10) rocket.trail.shift();
56
+
57
+ rocket.x += rocket.vx;
58
+ rocket.y += rocket.vy;
59
+ rocket.vy += 0.1;
60
+
61
+ if (rocket.vy >= 0) {
62
+ this.createExplosion(rocket.x, rocket.y);
63
+ this.rockets.splice(i, 1);
64
+ }
65
+ }
66
+
67
+ // Update particles
68
+ for (let i = this.particles.length - 1; i >= 0; i--) {
69
+ const p = this.particles[i];
70
+
71
+ p.x += p.vx;
72
+ p.y += p.vy;
73
+ p.vy += 0.05;
74
+ p.vx *= 0.99;
75
+ p.alpha -= 0.01;
76
+
77
+ if (p.alpha <= 0) {
78
+ this.particles.splice(i, 1);
79
+ }
80
+ }
81
+ }
82
+
83
+ draw() {
84
+ this.ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
85
+ this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
86
+
87
+ // Draw rockets
88
+ this.rockets.forEach(rocket => {
89
+ this.ctx.beginPath();
90
+ rocket.trail.forEach((point, index) => {
91
+ if (index === 0) {
92
+ this.ctx.moveTo(point.x, point.y);
93
+ } else {
94
+ this.ctx.lineTo(point.x, point.y);
95
+ }
96
+ });
97
+ this.ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
98
+ this.ctx.lineWidth = 2;
99
+ this.ctx.stroke();
100
+ });
101
+
102
+ // Draw particles
103
+ this.particles.forEach(p => {
104
+ this.ctx.globalAlpha = p.alpha;
105
+ this.ctx.fillStyle = p.color;
106
+ this.ctx.beginPath();
107
+ this.ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
108
+ this.ctx.fill();
109
+
110
+ // Add glow effect
111
+ this.ctx.shadowBlur = 10;
112
+ this.ctx.shadowColor = p.color;
113
+ this.ctx.fill();
114
+ this.ctx.shadowBlur = 0;
115
+ });
116
+
117
+ this.ctx.globalAlpha = 1;
118
+ }
119
+
120
+ animate() {
121
+ if (!this.isActive) return;
122
+
123
+ this.update();
124
+ this.draw();
125
+
126
+ if (Math.random() < 0.05) {
127
+ this.createRocket();
128
+ }
129
+
130
+ requestAnimationFrame(() => this.animate());
131
+ }
132
+
133
+ start() {
134
+ this.isActive = true;
135
+ this.particles = [];
136
+ this.rockets = [];
137
+ this.animate();
138
+
139
+ // Stop after 3 seconds
140
+ setTimeout(() => {
141
+ this.stop();
142
+ }, 3000);
143
+ }
144
+
145
+ stop() {
146
+ this.isActive = false;
147
+ setTimeout(() => {
148
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
149
+ this.particles = [];
150
+ this.rockets = [];
151
+ }, 1000);
152
+ }
153
+ }
index.html CHANGED
@@ -1,19 +1,76 @@
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, maximum-scale=1.0, user-scalable=no">
6
+ <meta name="description" content="A beautiful To-Do List app with fireworks celebration">
7
+ <meta name="theme-color" content="#6366f1">
8
+ <title>TaskFire - To-Do List with Celebration</title>
9
+
10
+ <!-- PWA Manifest -->
11
+ <link rel="manifest" href="manifest.json">
12
+ <meta name="apple-mobile-web-app-capable" content="yes">
13
+ <meta name="apple-mobile-web-app-status-bar-style" content="default">
14
+ <meta name="apple-mobile-web-app-title" content="TaskFire">
15
+ <link rel="apple-touch-icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect fill='%236366f1' width='100' height='100'/><text x='50' y='50' text-anchor='middle' dy='0.3em' fill='white' font-size='50'>πŸ“</text></svg>">
16
+
17
+ <link rel="stylesheet" href="assets/css/styles.css">
18
+ </head>
19
+ <body>
20
+ <canvas id="fireworks"></canvas>
21
+
22
+ <header>
23
+ <div class="header-content">
24
+ <h1>🎯 TaskFire</h1>
25
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with">Built with anycoder</a>
26
+ </div>
27
+ </header>
28
+
29
+ <main class="container">
30
+ <div class="input-section">
31
+ <div class="input-group">
32
+ <input
33
+ type="text"
34
+ id="taskInput"
35
+ placeholder="What needs to be done?"
36
+ maxlength="200"
37
+ autocomplete="off"
38
+ >
39
+ <button id="addBtn" type="button" aria-label="Add task">
40
+ <span>+</span>
41
+ </button>
42
+ </div>
43
+ <div class="stats">
44
+ <span id="totalTasks">0 tasks</span>
45
+ <span id="completedTasks">0 completed</span>
46
+ </div>
47
+ </div>
48
+
49
+ <div class="filter-tabs">
50
+ <button class="filter-btn active" data-filter="all">All</button>
51
+ <button class="filter-btn" data-filter="active">Active</button>
52
+ <button class="filter-btn" data-filter="completed">Completed</button>
53
+ </div>
54
+
55
+ <div id="taskList" class="task-list">
56
+ <!-- Tasks will be dynamically added here -->
57
+ </div>
58
+
59
+ <div class="empty-state" id="emptyState">
60
+ <div class="empty-icon">πŸ“</div>
61
+ <p>No tasks yet. Add one above!</p>
62
+ </div>
63
+
64
+ <div class="actions">
65
+ <button id="clearCompleted" class="clear-btn">Clear Completed</button>
66
+ </div>
67
+ </main>
68
+
69
+ <footer>
70
+ <p>Β© 2024 TaskFire. Stay productive, celebrate more!</p>
71
+ </footer>
72
+
73
+ <script src="assets/js/fireworks.js"></script>
74
+ <script src="assets/js/app.js"></script>
75
+ </body>
76
+ </html>
manifest.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "TaskFire - To-Do List",
3
+ "short_name": "TaskFire",
4
+ "description": "A beautiful To-Do List app with fireworks celebration",
5
+ "start_url": "/",
6
+ "display": "standalone",
7
+ "background_color": "#ffffff",
8
+ "theme_color": "#6366f1",
9
+ "icons": [
10
+ {
11
+ "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 192 192'><rect fill='%236366f1' width='192' height='192'/><text x='96' y='96' text-anchor='middle' dy='0.3em' fill='white' font-size='96'>πŸ“</text></svg>",
12
+ "sizes": "192x192",
13
+ "type": "image/svg+xml"
14
+ },
15
+ {
16
+ "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><rect fill='%236366f1' width='512' height='512'/><text x='256' y='256' text-anchor='middle' dy='0.3em' fill='white' font-size='256'>πŸ“</text></svg>",
17
+ "sizes": "512x512",
18
+ "type": "image/svg+xml"
19
+ }
20
+ ]
21
+ }