mlmihjaz commited on
Commit
e930e00
·
verified ·
1 Parent(s): 3071b2e

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +540 -1003
index.html CHANGED
@@ -1,1102 +1,639 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
3
-
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>AlphaPop Adventure - Typing & Spelling</title>
8
-
9
- <!-- Import Google Fonts -->
10
  <link rel="preconnect" href="https://fonts.googleapis.com">
11
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
- <link href="https://fonts.googleapis.com/css2?family=Fredoka:wght@400;600;700&family=Nunito:wght@400;600;800&display=swap"
13
- rel="stylesheet">
14
-
15
- <!-- FontAwesome for Icons -->
16
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
17
 
18
  <style>
19
  :root {
20
- /* Palette */
21
- --primary: #6C63FF;
22
- --primary-dark: #5a52d5;
23
- --secondary: #FF6584;
24
- --accent: #43D9AD;
25
- --accent-dark: #32b08b;
26
- --warning: #FFD93D;
27
- --dark: #2D3436;
28
- --light: #f5f7fa;
29
-
30
- /* Gradients & Backgrounds */
31
- --bg-gradient: linear-gradient(135deg, #fdfbfb 0%, #ebedee 100%);
32
- --header-gradient: linear-gradient(90deg, #ffffff 0%, #f0f2f5 100%);
33
- --card-bg: rgba(255, 255, 255, 0.95);
34
-
35
- /* Effects */
36
- --shadow-sm: 0 4px 6px rgba(0, 0, 0, 0.05);
37
- --shadow-lg: 0 15px 35px rgba(108, 99, 255, 0.15);
38
- --glass-border: 1px solid rgba(255, 255, 255, 0.6);
39
- --blur: blur(12px);
40
- --radius-lg: 24px;
41
- --radius-btn: 50px;
42
-
43
- /* Transitions */
44
- --trans-fast: 0.2s ease;
45
- --trans-bounce: 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
46
- }
47
-
48
- * {
49
- box-sizing: border-box;
50
- margin: 0;
51
- padding: 0;
52
- -webkit-tap-highlight-color: transparent;
53
- }
54
-
55
- body {
56
- font-family: 'Nunito', sans-serif;
57
- background: var(--bg-gradient);
58
- height: 100vh;
59
- display: flex;
60
- flex-direction: column;
61
- overflow: hidden;
62
- color: var(--dark);
63
- }
64
-
65
- /* --- Header --- */
66
- header {
67
- padding: 0.8rem 2rem;
68
- display: flex;
69
- justify-content: space-between;
70
- align-items: center;
71
- background: var(--header-gradient);
72
- box-shadow: var(--shadow-sm);
73
- z-index: 100;
74
- backdrop-filter: blur(5px);
75
- }
76
-
77
- .logo {
78
- font-family: 'Fredoka', sans-serif;
79
- font-size: 1.6rem;
80
- font-weight: 700;
81
- color: var(--primary);
82
- display: flex;
83
- align-items: center;
84
- gap: 12px;
85
- text-decoration: none;
86
- }
87
-
88
- .logo i {
89
- background: rgba(108, 99, 255, 0.1);
90
- padding: 10px;
91
- border-radius: 12px;
92
- }
93
-
94
- .header-actions {
95
- display: flex;
96
- gap: 15px;
97
- align-items: center;
98
- }
99
-
100
- .icon-btn {
101
- background: white;
102
- color: var(--primary);
103
- border: none;
104
- width: 44px;
105
- height: 44px;
106
- border-radius: 50%;
107
- cursor: pointer;
108
- display: flex;
109
- justify-content: center;
110
- align-items: center;
111
- font-size: 1.1rem;
112
- transition: var(--trans-fast);
113
- box-shadow: var(--shadow-sm);
114
- }
115
-
116
- .icon-btn:hover {
117
- transform: translateY(-2px);
118
- background: var(--primary);
119
- color: white;
120
- box-shadow: 0 5px 15px rgba(108, 99, 255, 0.3);
121
- }
122
-
123
- .anycoder-badge {
124
- font-size: 0.85rem;
125
- font-weight: 700;
126
- color: var(--dark);
127
- text-decoration: none;
128
- padding: 0.5rem 1.2rem;
129
- background: white;
130
- border-radius: var(--radius-btn);
131
- border: 2px solid #eee;
132
- transition: var(--trans-fast);
133
- display: flex;
134
- align-items: center;
135
- gap: 8px;
136
- }
137
-
138
- .anycoder-badge:hover {
139
- border-color: var(--primary);
140
- color: var(--primary);
141
- transform: translateY(-2px);
142
- }
143
-
144
- /* --- Main Layout --- */
145
- main {
146
- flex: 1;
147
- display: flex;
148
- justify-content: center;
149
- align-items: center;
150
- position: relative;
151
- padding: 1rem;
152
- }
153
-
154
- /* --- Game Board --- */
155
- .game-board {
156
- background: var(--card-bg);
157
- backdrop-filter: var(--blur);
158
- -webkit-backdrop-filter: var(--blur);
159
- border: var(--glass-border);
160
- border-radius: var(--radius-lg);
161
- padding: 2.5rem;
162
- width: 95%;
163
- max-width: 700px;
164
- box-shadow: var(--shadow-lg);
165
- text-align: center;
166
- position: relative;
167
- overflow: hidden;
168
- transition: transform 0.3s ease, filter 0.3s ease;
169
- }
170
-
171
- .game-board.blurred {
172
- filter: blur(8px);
173
- pointer-events: none;
174
- }
175
-
176
- /* Mode Switcher */
177
- .mode-switch {
178
- display: flex;
179
- background: #f0f0f5;
180
- padding: 5px;
181
- border-radius: var(--radius-btn);
182
- margin: 0 auto 2rem auto;
183
- width: fit-content;
184
- position: relative;
185
- }
186
-
187
- .mode-btn {
188
- background: transparent;
189
- border: none;
190
- padding: 10px 25px;
191
- border-radius: var(--radius-btn);
192
- font-family: 'Nunito', sans-serif;
193
- font-weight: 700;
194
- color: #888;
195
- cursor: pointer;
196
- z-index: 2;
197
- transition: color 0.3s ease;
198
- font-size: 1rem;
199
- }
200
-
201
- .mode-btn.active {
202
- color: white;
203
- }
204
-
205
- .switch-bg {
206
- position: absolute;
207
- top: 5px;
208
- left: 5px;
209
- width: calc(50% - 5px);
210
- height: calc(100% - 10px);
211
- background: var(--primary);
212
- border-radius: var(--radius-btn);
213
- transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
214
- z-index: 1;
215
- }
216
-
217
- /* Progress Bar */
218
- .progress-wrapper {
219
- width: 100%;
220
- margin-bottom: 2rem;
221
- }
222
-
223
- .progress-track {
224
- width: 100%;
225
- height: 12px;
226
- background: #eee;
227
- border-radius: 10px;
228
- overflow: hidden;
229
- }
230
-
231
- .progress-fill {
232
- height: 100%;
233
- width: 0%;
234
- background: linear-gradient(90deg, var(--secondary), var(--primary));
235
- border-radius: 10px;
236
- transition: width 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
237
- }
238
-
239
- /* Game Display Area */
240
- .display-area {
241
- min-height: 250px;
242
- display: flex;
243
- flex-direction: column;
244
- justify-content: center;
245
- align-items: center;
246
- margin-bottom: 1rem;
247
- }
248
-
249
- /* Letter Mode Styles */
250
- .big-letter {
251
- font-family: 'Fredoka', sans-serif;
252
- font-size: 9rem;
253
- font-weight: 700;
254
- color: var(--primary);
255
- text-shadow: 4px 4px 0px rgba(108, 99, 255, 0.1);
256
- line-height: 1;
257
- transition: color 0.3s;
258
- }
259
-
260
- /* Word Mode Styles */
261
- .word-container {
262
- display: flex;
263
- gap: 10px;
264
- margin-bottom: 20px;
265
- }
266
-
267
- .letter-box {
268
- font-family: 'Fredoka', sans-serif;
269
- font-size: 3.5rem;
270
- font-weight: 700;
271
- width: 60px;
272
- height: 80px;
273
- border-bottom: 4px solid #ddd;
274
- display: flex;
275
- justify-content: center;
276
- align-items: center;
277
- color: #ccc;
278
- transition: all 0.3s;
279
- text-transform: uppercase;
280
- }
281
-
282
- .letter-box.active {
283
- border-bottom-color: var(--primary);
284
- color: var(--primary);
285
- transform: translateY(-5px);
286
- animation: bounce 1s infinite;
287
- }
288
-
289
- .letter-box.completed {
290
- color: var(--accent-dark);
291
- border-bottom-color: var(--accent);
292
- }
293
-
294
- @keyframes bounce {
295
- 0%, 100% { transform: translateY(-5px); }
296
- 50% { transform: translateY(0); }
297
- }
298
-
299
- /* Image Display */
300
- .image-wrapper {
301
- width: 200px;
302
- height: 200px;
303
- border-radius: 20px;
304
- overflow: hidden;
305
- background: #fff;
306
- box-shadow: inset 0 0 20px rgba(0,0,0,0.05);
307
- border: 4px solid white;
308
- margin: 0 auto;
309
- opacity: 0;
310
- transform: scale(0.9);
311
- transition: all 0.5s var(--trans-bounce);
312
- }
313
-
314
- .image-wrapper.visible {
315
- opacity: 1;
316
- transform: scale(1);
317
- }
318
-
319
- .image-wrapper img {
320
- width: 100%;
321
- height: 100%;
322
- object-fit: cover;
323
- }
324
-
325
- /* Feedback Text */
326
- .status-msg {
327
- font-size: 1.4rem;
328
- font-weight: 700;
329
- color: var(--dark);
330
- margin-top: 1.5rem;
331
- min-height: 2rem;
332
- }
333
-
334
- .sub-status {
335
- font-size: 1rem;
336
- color: #888;
337
- font-weight: 600;
338
- }
339
-
340
- /* Buttons */
341
- .btn-main {
342
- background: var(--primary);
343
- color: white;
344
- border: none;
345
- padding: 1rem 3rem;
346
- font-size: 1.3rem;
347
- border-radius: var(--radius-btn);
348
- cursor: pointer;
349
- font-family: 'Fredoka', sans-serif;
350
- box-shadow: 0 8px 20px rgba(108, 99, 255, 0.3);
351
- transition: var(--trans-fast);
352
- margin-top: 1rem;
353
- display: inline-flex;
354
- align-items: center;
355
- gap: 10px;
356
- }
357
-
358
- .btn-main:hover {
359
- background: var(--primary-dark);
360
- transform: translateY(-3px);
361
- box-shadow: 0 12px 25px rgba(108, 99, 255, 0.4);
362
- }
363
-
364
- .btn-secondary {
365
- background: transparent;
366
- color: var(--primary);
367
- border: 2px solid var(--primary);
368
- padding: 0.8rem 2rem;
369
- font-size: 1rem;
370
- border-radius: var(--radius-btn);
371
- cursor: pointer;
372
- font-family: 'Fredoka', sans-serif;
373
- font-weight: 600;
374
- margin-top: 10px;
375
- transition: var(--trans-fast);
376
- }
377
-
378
- .btn-secondary:hover {
379
- background: rgba(108, 99, 255, 0.05);
380
- }
381
-
382
- /* Screens */
383
- .screen {
384
- display: none;
385
- width: 100%;
386
- height: 100%;
387
- flex-direction: column;
388
- align-items: center;
389
- justify-content: center;
390
- }
391
-
392
- .screen.active {
393
- display: flex;
394
- }
395
-
396
- /* Chat Overlay */
397
- .chat-modal {
398
- position: fixed;
399
- top: 0;
400
- left: 0;
401
- width: 100%;
402
- height: 100%;
403
- background: rgba(0, 0, 0, 0.5);
404
- z-index: 200;
405
- display: flex;
406
- justify-content: center;
407
- align-items: center;
408
- opacity: 0;
409
- visibility: hidden;
410
- transition: all 0.3s ease;
411
- }
412
-
413
- .chat-modal.open {
414
- opacity: 1;
415
- visibility: visible;
416
- }
417
-
418
- .chat-box {
419
- width: 90%;
420
- max-width: 500px;
421
- height: 75vh;
422
- background: white;
423
- border-radius: 24px;
424
- box-shadow: 0 25px 50px rgba(0,0,0,0.25);
425
- display: flex;
426
- flex-direction: column;
427
- overflow: hidden;
428
- transform: translateY(30px);
429
- transition: transform 0.3s var(--trans-bounce);
430
- }
431
-
432
- .chat-modal.open .chat-box {
433
- transform: translateY(0);
434
- }
435
-
436
- .chat-top {
437
- background: var(--primary);
438
- padding: 1rem 1.5rem;
439
- display: flex;
440
- justify-content: space-between;
441
- align-items: center;
442
- color: white;
443
- }
444
-
445
- .chat-top h3 {
446
- font-family: 'Fredoka', sans-serif;
447
- display: flex;
448
- align-items: center;
449
- gap: 10px;
450
- }
451
-
452
- .close-chat {
453
- background: none;
454
- border: none;
455
- color: white;
456
- font-size: 1.4rem;
457
- cursor: pointer;
458
- opacity: 0.8;
459
- }
460
-
461
- .chat-body {
462
- flex: 1;
463
- padding: 1.5rem;
464
- overflow-y: auto;
465
- background: #f8f9fa;
466
- display: flex;
467
- flex-direction: column;
468
- gap: 12px;
469
- }
470
-
471
- .msg {
472
- max-width: 85%;
473
- padding: 12px 18px;
474
- border-radius: 18px;
475
- font-size: 0.95rem;
476
- line-height: 1.5;
477
- position: relative;
478
- animation: slideIn 0.3s ease;
479
- }
480
-
481
- @keyframes slideIn {
482
- from { opacity: 0; transform: translateY(10px); }
483
- to { opacity: 1; transform: translateY(0); }
484
- }
485
-
486
- .msg.bot {
487
- background: white;
488
- color: var(--dark);
489
- border-bottom-left-radius: 4px;
490
- align-self: flex-start;
491
- box-shadow: 0 2px 5px rgba(0,0,0,0.05);
492
- }
493
-
494
- .msg.user {
495
- background: var(--primary);
496
- color: white;
497
- border-bottom-right-radius: 4px;
498
- align-self: flex-end;
499
- box-shadow: 0 2px 5px rgba(108, 99, 255, 0.2);
500
- }
501
-
502
- .chat-footer {
503
- padding: 1rem;
504
- background: white;
505
- border-top: 1px solid #eee;
506
- display: flex;
507
- gap: 10px;
508
- }
509
-
510
- .chat-footer input {
511
- flex: 1;
512
- padding: 12px 20px;
513
- border: 2px solid #eee;
514
- border-radius: 30px;
515
- outline: none;
516
- font-family: 'Nunito', sans-serif;
517
- transition: border-color 0.2s;
518
- }
519
-
520
- .chat-footer input:focus {
521
- border-color: var(--primary);
522
- }
523
-
524
- .send-btn {
525
- width: 45px;
526
- height: 45px;
527
- border-radius: 50%;
528
- border: none;
529
- background: var(--primary);
530
- color: white;
531
- cursor: pointer;
532
- display: flex;
533
- justify-content: center;
534
- align-items: center;
535
- transition: background 0.2s;
536
- }
537
-
538
- .send-btn:hover {
539
- background: var(--secondary);
540
- }
541
-
542
- /* Canvas */
543
- #confetti {
544
- position: absolute;
545
- top: 0;
546
- left: 0;
547
- width: 100%;
548
- height: 100%;
549
- pointer-events: none;
550
- z-index: 50;
551
- }
552
-
553
- /* Animations */
554
- .shake {
555
- animation: shake 0.4s ease;
556
- }
557
-
558
- @keyframes shake {
559
- 0%, 100% { transform: translateX(0); }
560
- 25% { transform: translateX(-10px); color: var(--secondary); }
561
- 75% { transform: translateX(10px); color: var(--secondary); }
562
- }
563
-
564
- /* Responsive */
565
- @media (max-width: 600px) {
566
- .game-board {
567
- width: 100%;
568
- height: 100%;
569
- border-radius: 0;
570
- border: none;
571
- }
572
- .big-letter { font-size: 6rem; }
573
- .letter-box { width: 40px; height: 60px; font-size: 2rem; }
574
- .logo span { display: none; }
575
- header { padding: 0.8rem 1rem; }
576
- .mode-btn { padding: 8px 16px; font-size: 0.9rem; }
577
  }
578
  </style>
579
  </head>
580
-
581
  <body>
582
 
583
- <header>
584
- <a href="#" class="logo">
585
- <i class="fa-solid fa-rocket"></i>
586
- <span>AlphaPop</span>
587
- </a>
588
- <div class="header-actions">
589
- <button class="icon-btn" onclick="app.toggleChat()" title="Talk to AlphaBot">
590
- <i class="fa-solid fa-comment-dots"></i>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
591
  </button>
592
- <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-badge">
593
- <i class="fa-solid fa-code"></i> Built with anycoder
 
594
  </a>
595
  </div>
596
- </header>
597
 
598
- <main>
599
- <canvas id="confetti"></canvas>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
600
 
601
- <div class="game-board" id="gameBoard">
 
602
 
603
- <!-- Start Screen -->
604
- <div id="startScreen" class="screen active">
605
- <div style="font-size: 4rem; color: var(--primary); margin-bottom: 1rem;">
606
- <i class="fa-solid fa-gamepad"></i>
607
- </div>
608
- <h1 style="font-family: 'Fredoka'; font-size: 2.5rem; margin-bottom: 0.5rem; color: var(--dark);">Ready to Play?</h1>
609
- <p style="color: #666; font-size: 1.1rem; margin-bottom: 2rem;">Choose a mode to start learning!</p>
610
-
611
- <div style="display: flex; flex-direction: column; gap: 15px; width: 100%; max-width: 300px;">
612
- <button class="btn-main" onclick="app.startGame('letter')">
613
- <i class="fa-solid fa-a"></i> Letter Mode
614
- </button>
615
- <button class="btn-secondary" onclick="app.startGame('word')">
616
- <i class="fa-solid fa-spell-check"></i> Word Mode
617
  </button>
618
- <button class="btn-secondary" style="border: none; color: #666;" onclick="app.toggleChat()">
619
- <i class="fa-solid fa-robot"></i> Talk to AlphaBot first
 
 
 
 
 
620
  </button>
621
  </div>
622
- </div>
623
 
624
- <!-- Play Screen -->
625
- <div id="playScreen" class="screen">
626
-
627
- <!-- Mode Switcher (Visible during gameplay) -->
628
- <div class="mode-switch">
629
- <div class="switch-bg" id="switchBg"></div>
630
- <button class="mode-btn active" id="btnModeLetter" onclick="app.switchMode('letter')">Letters</button>
631
- <button class="mode-btn" id="btnModeWord" onclick="app.switchMode('word')">Words</button>
632
- </div>
633
 
634
- <div class="progress-wrapper">
635
- <div class="progress-track">
636
- <div class="progress-fill" id="progressBar"></div>
637
- </div>
638
- </div>
 
 
 
 
 
 
639
 
640
- <div class="display-area">
641
- <!-- Letter Mode Elements -->
642
- <div id="letterModeUI" style="display: flex; flex-direction: column; align-items: center;">
643
- <div class="big-letter" id="bigLetter">A</div>
644
- </div>
645
 
646
- <!-- Word Mode Elements -->
647
- <div id="wordModeUI" style="display: none; flex-direction: column; align-items: center;">
648
- <div class="word-container" id="wordContainer">
649
- <!-- Letter boxes injected here -->
650
- </div>
651
- </div>
652
 
653
- <!-- Shared Image -->
654
- <div class="image-wrapper" id="imageWrapper">
655
- <img id="gameImage" src="" alt="Success Image">
656
- </div>
657
- </div>
658
 
659
- <div class="status-msg" id="statusMsg">Press 'A'</div>
660
- <div class="sub-status"><i class="fa-solid fa-volume-high"></i> Sound Enabled</div>
 
 
 
 
661
  </div>
662
-
663
- <!-- End Screen -->
664
- <div id="endScreen" class="screen">
665
- <i class="fa-solid fa-trophy" style="font-size: 5rem; color: var(--warning); margin-bottom: 1rem; animation: bounce 2s infinite;"></i>
666
- <h2 style="font-family: 'Fredoka'; font-size: 2.5rem; margin-bottom: 0.5rem; color: var(--dark);">Awesome!</h2>
667
- <p style="color: #666; margin-bottom: 2rem;">You completed the level.</p>
668
- <button class="btn-main" onclick="app.startGame(app.currentMode)">
669
- Play Again <i class="fa-solid fa-rotate-right"></i>
670
- </button>
671
- <button class="btn-secondary" onclick="app.showStartScreen()">Main Menu</button>
 
 
 
672
  </div>
673
-
674
  </div>
675
- </main>
676
 
677
  <!-- Chat Modal -->
678
- <div class="chat-modal" id="chatModal">
679
- <div class="chat-box">
680
- <div class="chat-top">
681
- <h3><i class="fa-solid fa-robot"></i> AlphaBot</h3>
682
- <button class="close-chat" onclick="app.toggleChat()"><i class="fa-solid fa-xmark"></i></button>
683
  </div>
684
- <div class="chat-body" id="chatBody">
685
- <div class="msg bot">Hello! I'm AlphaBot. I can help you learn! Try typing "Hello" or ask me how to play.</div>
686
  </div>
687
- <div class="chat-footer">
688
- <input type="text" id="chatInput" placeholder="Type a message..." onkeypress="app.handleChatKey(event)">
689
- <button class="send-btn" onclick="app.sendChatMessage()"><i class="fa-solid fa-paper-plane"></i></button>
690
  </div>
691
  </div>
692
  </div>
693
 
694
  <script>
695
  /**
696
- * AlphaPop Application Logic
697
- * Handles Game State, UI Updates, Speech, and Chat
698
  */
699
  const app = {
700
- // State
701
- alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(''),
702
- words: ["CAT", "DOG", "SUN", "CAR", "BEE", "FOX", "OWL", "PIG", "COW", "BAT", "HAT", "MAP", "JAM", "PAN", "VAN"],
703
- currentMode: 'letter', // 'letter' || 'word'
704
- currentIndex: 0,
705
- letterIndex: 0, // For word mode tracking
706
- isPlaying: false,
707
- isChatOpen: false,
708
- isProcessingInput: false, // Debounce visual feedback
709
-
710
- // DOM Elements
711
- ui: {
712
- startScreen: document.getElementById('startScreen'),
713
- playScreen: document.getElementById('playScreen'),
714
- endScreen: document.getElementById('endScreen'),
715
- gameBoard: document.getElementById('gameBoard'),
716
-
717
- // Game UI
718
- letterModeUI: document.getElementById('letterModeUI'),
719
- wordModeUI: document.getElementById('wordModeUI'),
720
- bigLetter: document.getElementById('bigLetter'),
721
- wordContainer: document.getElementById('wordContainer'),
722
- imageWrapper: document.getElementById('imageWrapper'),
723
- gameImage: document.getElementById('gameImage'),
724
- statusMsg: document.getElementById('statusMsg'),
725
- progressBar: document.getElementById('progressBar'),
726
-
727
- // Mode Switcher
728
- switchBg: document.getElementById('switchBg'),
729
- btnModeLetter: document.getElementById('btnModeLetter'),
730
- btnModeWord: document.getElementById('btnModeWord'),
731
-
732
- // Chat
733
- chatModal: document.getElementById('chatModal'),
734
- chatBody: document.getElementById('chatBody'),
735
- chatInput: document.getElementById('chatInput')
736
- },
737
-
738
- init() {
739
- // Global Key Listener
740
- window.addEventListener('keydown', (e) => this.handleGlobalKey(e));
741
-
742
- // Resize Canvas
743
- window.addEventListener('resize', () => this.resizeCanvas());
744
- this.resizeCanvas();
745
-
746
- // Setup Speech Synthesis (preload voices)
747
- window.speechSynthesis.getVoices();
748
- },
749
-
750
- /* --- Navigation --- */
751
- showStartScreen() {
752
- this.isPlaying = false;
753
- this.ui.startScreen.classList.add('active');
754
- this.ui.playScreen.classList.remove('active');
755
- this.ui.endScreen.classList.remove('active');
756
- this.closeChat();
757
  },
758
 
759
- startGame(mode) {
760
- this.currentMode = mode;
761
- this.currentIndex = 0;
762
- this.isPlaying = true;
763
- this.isProcessingInput = false;
764
-
765
- // UI Reset
766
- this.ui.startScreen.classList.remove('active');
767
- this.ui.endScreen.classList.remove('active');
768
- this.ui.playScreen.classList.add('active');
769
-
770
- this.updateModeSwitcherUI();
771
- this.loadLevel();
772
  },
773
 
774
- switchMode(mode) {
775
- if (this.currentMode === mode) return;
776
- this.startGame(mode); // Restart with new mode
777
- },
 
 
 
 
 
778
 
779
- /* --- Game Logic --- */
780
- loadLevel() {
781
- // Reset Visuals
782
- this.ui.imageWrapper.classList.remove('visible');
783
- this.ui.statusMsg.style.color = 'var(--dark)';
784
- this.ui.statusMsg.textContent = "...";
785
-
786
- // Update Progress
787
- const total = this.currentMode === 'letter' ? this.alphabet.length : this.words.length;
788
- const progress = (this.currentIndex / total) * 100;
789
- this.ui.progressBar.style.width = `${progress}%`;
790
 
791
- // Setup specific mode UI
792
- if (this.currentMode === 'letter') {
793
- this.setupLetterLevel();
794
- } else {
795
- this.setupWordLevel();
796
  }
797
- },
798
 
799
- setupLetterLevel() {
800
- this.ui.letterModeUI.style.display = 'flex';
801
- this.ui.wordModeUI.style.display = 'none';
802
-
803
- const letter = this.alphabet[this.currentIndex];
804
- this.ui.bigLetter.textContent = letter;
805
- this.ui.statusMsg.textContent = `Press the '${letter}' key!`;
806
-
807
- this.speak(`Find the letter ${letter}`);
808
  },
809
 
810
- setupWordLevel() {
811
- this.ui.letterModeUI.style.display = 'none';
812
- this.ui.wordModeUI.style.display = 'flex';
813
- this.letterIndex = 0;
814
-
815
- const word = this.words[this.currentIndex];
816
- this.ui.wordContainer.innerHTML = ''; // Clear previous
817
-
818
- // Create letter boxes
819
- for (let i = 0; i < word.length; i++) {
820
- const box = document.createElement('div');
821
- box.className = 'letter-box';
822
- box.textContent = word[i];
823
- box.id = `box-${i}`;
824
- this.ui.wordContainer.appendChild(box);
825
- }
826
-
827
- this.updateWordHighlight();
828
- this.speak(`Let's spell ${word}`);
829
  },
830
 
831
- updateWordHighlight() {
832
- const word = this.words[this.currentIndex];
833
- const boxes = this.ui.wordContainer.children;
834
-
835
- // Reset all
836
- for (let box of boxes) {
837
- box.className = 'letter-box';
838
- }
839
-
840
- // Highlight current
841
- const currentBox = document.getElementById(`box-${this.letterIndex}`);
842
- if (currentBox) {
843
- currentBox.classList.add('active');
844
- }
845
-
846
- // Mark completed
847
- for(let i=0; i<this.letterIndex; i++) {
848
- const completedBox = document.getElementById(`box-${i}`);
849
- if(completedBox) completedBox.classList.add('completed');
850
- }
851
-
852
- this.ui.statusMsg.textContent = `Type '${word[this.letterIndex]}'`;
853
  },
854
 
855
- handleInput(key) {
856
- if (!this.isPlaying || this.isProcessingInput || this.isChatOpen) return;
 
857
 
858
- const upperKey = key.toUpperCase();
859
- let target = '';
860
-
861
- if (this.currentMode === 'letter') {
862
- target = this.alphabet[this.currentIndex];
863
- } else {
864
- target = this.words[this.currentIndex][this.letterIndex];
865
- }
866
-
867
- if (upperKey === target) {
868
- this.handleCorrect(target);
869
- } else {
870
- this.handleWrong();
871
- }
872
- },
873
-
874
- handleCorrect(char) {
875
- this.isProcessingInput = true;
876
- this.speak(char);
877
 
878
- const isLetterMode = this.currentMode === 'letter';
879
- const isWordComplete = !isLetterMode && (this.letterIndex === this.words[this.currentIndex].length - 1);
880
-
881
- // Visual Feedback
882
- this.ui.statusMsg.textContent = "Correct!";
883
- this.ui.statusMsg.style.color = "var(--accent-dark)";
884
 
885
- if (isLetterMode) {
886
- this.ui.bigLetter.style.color = "var(--accent-dark)";
887
- this.showImage(char);
888
- } else {
889
- // Update Word UI
890
- const box = document.getElementById(`box-${this.letterIndex}`);
891
- if(box) {
892
- box.classList.remove('active');
893
- box.classList.add('completed');
894
- }
895
-
896
- if (isWordComplete) {
897
- this.showImage(this.words[this.currentIndex]);
898
- }
899
  }
900
 
901
- // Fire Particles
902
- this.fireConfetti(isWordComplete || isLetterMode ? 1.5 : 0.5);
 
 
 
 
 
903
 
904
- setTimeout(() => {
905
- if (isLetterMode) {
906
- this.ui.bigLetter.style.color = "var(--primary)";
907
- this.nextLevel();
908
- } else {
909
- if (isWordComplete) {
910
- this.nextLevel();
911
- } else {
912
- this.letterIndex++;
913
- this.updateWordHighlight();
914
- this.isProcessingInput = false;
915
- }
916
- }
917
- }, 1200);
918
  },
919
 
920
- handleWrong() {
921
- this.speak("Try again");
922
 
923
- if (this.currentMode === 'letter') {
924
- this.ui.bigLetter.classList.remove('shake');
925
- void this.ui.bigLetter.offsetWidth; // trigger reflow
926
- this.ui.bigLetter.classList.add('shake');
927
- } else {
928
- const box = document.getElementById(`box-${this.letterIndex}`);
929
- if(box) {
930
- box.classList.remove('shake');
931
- void box.offsetWidth;
932
- box.classList.add('shake');
933
- }
934
- }
935
 
936
- this.ui.statusMsg.textContent = "Oops, try again!";
937
- this.ui.statusMsg.style.color = "var(--secondary)";
938
  },
939
 
940
- showImage(seed) {
941
- // Use Picsum with seed to get consistent random images
942
- const url = `https://picsum.photos/seed/${seed}/400/400`;
943
- this.ui.gameImage.src = url;
944
- this.ui.gameImage.onload = () => {
945
- this.ui.imageWrapper.classList.add('visible');
946
- };
947
- },
948
-
949
- nextLevel() {
950
- const total = this.currentMode === 'letter' ? this.alphabet.length : this.words.length;
951
- this.currentIndex++;
952
- this.isProcessingInput = false;
953
-
954
- if (this.currentIndex >= total) {
955
- this.gameOver();
956
- } else {
957
- this.loadLevel();
 
 
 
 
958
  }
959
- },
960
-
961
- gameOver() {
962
- this.isPlaying = false;
963
- this.ui.playScreen.classList.remove('active');
964
- this.ui.endScreen.classList.add('active');
965
- this.ui.progressBar.style.width = '100%';
966
- this.fireConfetti(3.0);
967
- this.speak("Amazing work! You finished the game!");
968
- },
969
 
970
- updateModeSwitcherUI() {
971
- if (this.currentMode === 'letter') {
972
- this.ui.switchBg.style.transform = 'translateX(0)';
973
- this.ui.btnModeLetter.classList.add('active');
974
- this.ui.btnModeWord.classList.remove('active');
975
- } else {
976
- this.ui.switchBg.style.transform = 'translateX(100%)';
977
- this.ui.btnModeLetter.classList.remove('active');
978
- this.ui.btnModeWord.classList.add('active');
979
- }
980
- },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
981
 
982
- /* --- Chat System --- */
983
- toggleChat() {
984
- this.isChatOpen = !this.isChatOpen;
985
- if (this.isChatOpen) {
986
- this.ui.chatModal.classList.add('open');
987
- this.ui.gameBoard.classList.add('blurred');
988
- this.ui.chatInput.focus();
989
- } else {
990
- this.ui.chatModal.classList.remove('open');
991
- this.ui.gameBoard.classList.remove('blurred');
992
- }
993
  },
994
 
995
- handleChatKey(e) {
996
- if (e.key === 'Enter') this.sendChatMessage();
 
997
  },
998
 
999
- sendChatMessage() {
1000
- const text = this.ui.chatInput.value.trim();
 
1001
  if (!text) return;
1002
 
1003
- // User Msg
1004
- this.addMsg(text, 'user');
1005
- this.ui.chatInput.value = '';
1006
 
1007
- // Bot Response
1008
  setTimeout(() => {
1009
- const reply = this.getBotResponse(text);
1010
- this.addMsg(reply, 'bot');
1011
- this.speak(reply);
1012
- }, 600);
 
 
 
 
 
1013
  },
1014
 
1015
- addMsg(text, type) {
 
1016
  const div = document.createElement('div');
1017
- div.className = `msg ${type}`;
1018
- div.textContent = text;
1019
- this.ui.chatBody.appendChild(div);
1020
- this.ui.chatBody.scrollTop = this.ui.chatBody.scrollHeight;
1021
- },
1022
-
1023
- getBotResponse(input) {
1024
- const lower = input.toLowerCase();
1025
-
1026
- if (lower.includes('hello') || lower.includes('hi')) return "Hi there! Ready to learn some letters and words?";
1027
- if (lower.includes('how are you')) return "I'm a robot, so I'm always functioning at 100%!";
1028
- if (lower.includes('play') || lower.includes('game')) return "If you are in Letter Mode, press the key you see. In Word Mode, type the whole word!";
1029
- if (lower.includes('letter')) return "Letter Mode helps you learn individual keys A through Z.";
1030
- if (lower.includes('word')) return "Word Mode is harder. You have to spell words like 'CAT' or 'DOG'.";
1031
- if (lower.includes('help')) return "Just type the letters on your keyboard that match the screen!";
1032
 
1033
- // Check for single letters
1034
- if (/^[a-z]$/.test(lower)) {
1035
- return `You typed ${input.toUpperCase()}! That is correct.`;
1036
- }
1037
-
1038
- const jokes = [
1039
- "Why did the computer go to the doctor? Because it had a virus!",
1040
- "I love teaching you, you are a fast typer!",
1041
- "Keep going, you are doing great!",
1042
- "Did you know? The alphabet has 26 letters!"
1043
- ];
1044
- return jokes[Math.floor(Math.random() * jokes.length)];
1045
  },
1046
 
1047
- /* --- Utilities --- */
1048
- handleGlobalKey(e) {
1049
- // Input handling is routed here
1050
- this.handleInput(e.key);
 
1051
  },
1052
 
1053
- speak(text) {
1054
- if ('speechSynthesis' in window) {
1055
- window.speechSynthesis.cancel(); // Stop previous
1056
- const utterance = new SpeechSynthesisUtterance(text);
1057
- utterance.rate = 1.0;
1058
- utterance.pitch = 1.1; // Slightly higher pitch for "kid friendly" vibe
1059
- window.speechSynthesis.speak(utterance);
1060
- }
1061
  },
1062
 
1063
- /* --- Confetti System --- */
1064
- canvas: null,
1065
- ctx: null,
1066
- particles: [],
1067
-
1068
- resizeCanvas() {
1069
- this.canvas = document.getElementById('confetti');
1070
- this.ctx = this.canvas.getContext('2d');
1071
- this.canvas.width = window.innerWidth;
1072
- this.canvas.height = window.innerHeight;
1073
  },
1074
 
1075
- fireConfetti(intensity = 1.0) {
1076
- const count = 100 * intensity;
1077
- const colors = ['#FF6584', '#6C63FF', '#43D9AD', '#FFD93D', '#FF8C00'];
1078
-
1079
- for (let i = 0; i < count; i++) {
1080
- this.particles.push({
1081
- x: window.innerWidth / 2,
1082
- y: window.innerHeight / 2,
1083
- vx: (Math.random() - 0.5) * 20 * intensity,
1084
- vy: (Math.random() - 1) * 20 * intensity,
1085
- size: Math.random() * 8 + 4,
1086
- color: colors[Math.floor(Math.random() * colors.length)],
1087
- life: 100,
1088
- gravity: 0.5
1089
- });
 
 
1090
  }
 
 
 
 
1091
 
1092
- if (this.particles.length <= count) requestAnimationFrame(() => this.updateConfetti());
 
 
 
1093
  },
1094
 
1095
- updateConfetti() {
1096
- if (!this.ctx) return;
1097
-
1098
- this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
1099
-
1100
- for (let i = 0; i < this.particles.length; i++) {
1101
- let p = this.particles[i];
1102
- p.x += p.vx;
 
 
 
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>EduConnect - Learning Management System</title>
7
+
8
+ <!-- Google Fonts -->
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
12
+
13
+ <!-- FontAwesome -->
 
14
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
15
 
16
  <style>
17
  :root {
18
+ --primary: #008069; /* WhatsApp Green-ish */
19
+ --primary-dark: #005c4b;
20
+ --accent: #34b7f1;
21
+ --bg-body: #e5ddd5; /* Chat background color */
22
+ --bg-white: #ffffff;
23
+ --text-dark: #111b21;
24
+ --text-gray: #667781;
25
+ --border: #e9edef;
26
+ --shadow: 0 2px 5px rgba(0,0,0,0.05);
27
+ --radius: 12px;
28
+ --danger: #ef5350;
29
+ }
30
+
31
+ * { box-sizing: border-box; margin: 0; padding: 0; outline: none; }
32
+ body { font-family: 'Inter', sans-serif; background-color: #f0f2f5; color: var(--text-dark); height: 100vh; overflow: hidden; }
33
+
34
+ /* --- Utilities --- */
35
+ .hidden { display: none !important; }
36
+ .flex { display: flex; }
37
+ .flex-col { flex-direction: column; }
38
+ .center { align-items: center; justify-content: center; }
39
+ .btn { cursor: pointer; border: none; padding: 10px 20px; border-radius: 24px; font-weight: 600; transition: 0.2s; font-size: 0.9rem; }
40
+ .btn-primary { background: var(--primary); color: white; }
41
+ .btn-primary:hover { background: var(--primary-dark); }
42
+ .btn-outline { background: transparent; border: 1px solid var(--primary); color: var(--primary); }
43
+ .btn-icon { background: transparent; color: var(--text-gray); font-size: 1.2rem; padding: 8px; border-radius: 50%; }
44
+ .btn-icon:hover { background: rgba(0,0,0,0.05); color: var(--primary); }
45
+
46
+ /* --- Login Screen --- */
47
+ #loginScreen {
48
+ position: fixed; top: 0; left: 0; width: 100%; height: 100%;
49
+ background: linear-gradient(135deg, var(--primary) 0%, #00a884 100%);
50
+ z-index: 1000; display: flex; align-items: center; justify-content: center;
51
+ }
52
+ .login-card {
53
+ background: white; padding: 3rem; border-radius: 20px; width: 90%; max-width: 400px;
54
+ box-shadow: 0 20px 40px rgba(0,0,0,0.2); text-align: center;
55
+ }
56
+ .login-card h1 { color: var(--primary); margin-bottom: 1rem; }
57
+ .role-btn {
58
+ display: flex; align-items: center; gap: 10px; width: 100%; margin-bottom: 1rem;
59
+ padding: 1rem; border: 2px solid #eee; border-radius: 12px; background: white;
60
+ font-size: 1rem; color: #555; transition: 0.2s;
61
+ }
62
+ .role-btn:hover { border-color: var(--primary); background: #f0fdf9; color: var(--primary); }
63
+
64
+ /* --- App Layout --- */
65
+ #appContainer { display: flex; height: 100vh; width: 100vw; }
66
+
67
+ /* Sidebar (Categories) */
68
+ .sidebar {
69
+ width: 280px; background: white; border-right: 1px solid var(--border);
70
+ display: flex; flex-direction: column; z-index: 10;
71
+ }
72
+ .brand { padding: 20px; font-size: 1.5rem; font-weight: 700; color: var(--primary); display: flex; align-items: center; gap: 10px; }
73
+ .category-list { flex: 1; overflow-y: auto; padding: 10px; }
74
+
75
+ /* Stack Buttons for Categories */
76
+ .cat-btn {
77
+ width: 100%; text-align: left; padding: 12px 16px; margin-bottom: 5px;
78
+ background: #f5f6f6; border: none; border-radius: 10px; color: var(--text-dark);
79
+ font-weight: 500; display: flex; justify-content: space-between; align-items: center;
80
+ transition: 0.2s; cursor: pointer;
81
+ }
82
+ .cat-btn:hover { background: #e9edef; }
83
+ .cat-btn.active { background: var(--primary); color: white; }
84
+ .cat-btn .badge { font-size: 0.75rem; background: rgba(0,0,0,0.1); padding: 2px 6px; border-radius: 4px; }
85
+
86
+ /* Main Content Area */
87
+ .main-content { flex: 1; display: flex; flex-direction: column; background: #f0f2f5; position: relative; }
88
+
89
+ /* Header */
90
+ .top-header {
91
+ height: 60px; background: white; border-bottom: 1px solid var(--border);
92
+ display: flex; align-items: center; justify-content: space-between; padding: 0 20px;
93
+ }
94
+ .user-info { display: flex; align-items: center; gap: 10px; }
95
+ .user-avatar { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; }
96
+
97
+ /* Subcategories (Subjects) Strip */
98
+ .subject-strip {
99
+ padding: 15px 20px; background: white; overflow-x: auto; white-space: nowrap;
100
+ border-bottom: 1px solid var(--border); display: flex; gap: 10px;
101
+ }
102
+ .subject-pill {
103
+ padding: 8px 16px; background: #f0f2f5; border-radius: 20px; color: var(--text-dark);
104
+ font-size: 0.9rem; cursor: pointer; transition: 0.2s; border: 1px solid transparent;
105
+ }
106
+ .subject-pill:hover { background: #e9edef; }
107
+ .subject-pill.active { background: var(--primary); color: white; }
108
+
109
+ /* Feed Area */
110
+ .feed-container {
111
+ flex: 1; overflow-y: auto; padding: 20px; display: flex; flex-direction: column; gap: 20px;
112
+ background-image: url('https://user-images.githubusercontent.com/15075759/28719144-86dc0f70-73b1-11e7-911d-60d70fcded21.png');
113
+ background-blend-mode: overlay; background-color: rgba(255,255,255,0.9);
114
+ }
115
+
116
+ /* Feed Card (WhatsApp Style) */
117
+ .msg-bubble {
118
+ max-width: 600px; align-self: flex-start; background: white;
119
+ padding: 10px; border-radius: 8px; box-shadow: var(--shadow);
120
+ position: relative; animation: slideUp 0.3s ease; width: 100%;
121
+ }
122
+ .msg-bubble.sent { align-self: flex-end; background: #d9fdd3; }
123
+
124
+ .msg-meta { display: flex; justify-content: space-between; margin-bottom: 5px; font-size: 0.8rem; color: var(--text-gray); }
125
+ .msg-content { font-size: 1rem; line-height: 1.4; color: var(--text-dark); }
126
+
127
+ .media-preview {
128
+ margin-top: 10px; border-radius: 8px; overflow: hidden; background: #000;
129
+ max-width: 100%; position: relative; cursor: pointer;
130
+ }
131
+ .media-preview video, .media-preview img { width: 100%; max-height: 300px; object-fit: contain; display: block; }
132
+ .play-btn {
133
+ position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
134
+ color: white; font-size: 3rem; opacity: 0.8; text-shadow: 0 2px 5px rgba(0,0,0,0.5);
135
+ }
136
+ .file-attach {
137
+ display: flex; align-items: center; gap: 15px; padding: 10px;
138
+ background: #f9f9f9; border-radius: 8px; margin-top: 5px; cursor: pointer;
139
+ }
140
+ .file-icon { font-size: 2rem; color: var(--danger); }
141
+ .file-info h4 { font-size: 0.9rem; margin-bottom: 2px; }
142
+ .file-info span { font-size: 0.75rem; color: #888; }
143
+
144
+ /* Floating Action Button for Upload */
145
+ .fab {
146
+ position: absolute; bottom: 30px; right: 30px;
147
+ width: 60px; height: 60px; background: var(--primary); color: white;
148
+ border-radius: 50%; display: flex; align-items: center; justify-content: center;
149
+ font-size: 1.5rem; box-shadow: 0 4px 10px rgba(0,128,105,0.4); cursor: pointer;
150
+ transition: 0.3s; z-index: 50;
151
+ }
152
+ .fab:hover { transform: scale(1.1); }
153
+
154
+ /* Chat Modal */
155
+ .modal-overlay {
156
+ position: fixed; top: 0; left: 0; width: 100%; height: 100%;
157
+ background: rgba(0,0,0,0.5); z-index: 200; display: flex; align-items: center; justify-content: center;
158
+ opacity: 0; pointer-events: none; transition: 0.3s;
159
+ }
160
+ .modal-overlay.open { opacity: 1; pointer-events: all; }
161
+ .modal-box {
162
+ background: white; width: 90%; max-width: 500px; height: 80vh; border-radius: 16px;
163
+ display: flex; flex-direction: column; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.2);
164
+ }
165
+ .modal-header { padding: 15px; background: var(--primary); color: white; display: flex; justify-content: space-between; align-items: center; }
166
+ .chat-area { flex: 1; padding: 15px; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; background: #e5ddd5; }
167
+ .chat-input-area { padding: 10px; background: #f0f0f0; display: flex; gap: 10px; }
168
+ .chat-input-area input { flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 20px; }
169
+
170
+ /* Upload Modal Content */
171
+ .upload-options { padding: 20px; display: grid; grid-template-columns: 1fr 1fr; gap: 15px; }
172
+ .upload-card {
173
+ border: 2px dashed #ddd; border-radius: 12px; padding: 20px; text-align: center;
174
+ cursor: pointer; transition: 0.2s;
175
+ }
176
+ .upload-card:hover { border-color: var(--primary); background: #f0fdf9; }
177
+
178
+ /* Sort Bar */
179
+ .sort-bar {
180
+ padding: 10px 20px; background: white; border-bottom: 1px solid var(--border);
181
+ display: flex; align-items: center; gap: 10px; font-size: 0.9rem; color: var(--text-gray);
182
+ }
183
+ select { padding: 5px 10px; border-radius: 6px; border: 1px solid #ddd; }
184
+
185
+ @keyframes slideUp { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
186
+
187
+ /* Mobile Responsive */
188
+ @media (max-width: 768px) {
189
+ .sidebar { position: absolute; left: -280px; height: 100%; transition: 0.3s; box-shadow: 2px 0 10px rgba(0,0,0,0.1); }
190
+ .sidebar.open { left: 0; }
191
+ .menu-toggle { display: block; }
192
+ }
193
+ @media (min-width: 769px) { .menu-toggle { display: none; } }
194
+
195
+ .anycoder-link {
196
+ font-size: 0.8rem; color: #888; text-decoration: none; margin-top: 20px; display: inline-block;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  }
198
  </style>
199
  </head>
 
200
  <body>
201
 
202
+ <!-- Login Screen -->
203
+ <div id="loginScreen">
204
+ <div class="login-card">
205
+ <div style="font-size: 3rem; color: var(--primary); margin-bottom: 10px;">
206
+ <i class="fa-solid fa-graduation-cap"></i>
207
+ </div>
208
+ <h1>EduConnect</h1>
209
+ <p style="color: #666; margin-bottom: 2rem;">Select your role to continue</p>
210
+
211
+ <button class="role-btn" onclick="app.login('admin')">
212
+ <i class="fa-solid fa-user-shield" style="color: var(--primary);"></i>
213
+ <div style="text-align: left;">
214
+ <strong>Admin</strong><br>
215
+ <span style="font-size: 0.8rem;">Manage content, categories & chat</span>
216
+ </div>
217
+ </button>
218
+
219
+ <button class="role-btn" onclick="app.login('student')">
220
+ <i class="fa-solid fa-user-graduate" style="color: var(--accent);"></i>
221
+ <div style="text-align: left;">
222
+ <strong>Student</strong><br>
223
+ <span style="font-size: 0.8rem;">View assigned materials & chat</span>
224
+ </div>
225
  </button>
226
+
227
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
228
+ Built with anycoder
229
  </a>
230
  </div>
231
+ </div>
232
 
233
+ <!-- Main App Container -->
234
+ <div id="appContainer" class="hidden">
235
+
236
+ <!-- Sidebar: Categories (Grades) -->
237
+ <aside class="sidebar" id="sidebar">
238
+ <div class="brand">
239
+ <i class="fa-solid fa-shapes"></i> EduConnect
240
+ </div>
241
+ <div class="category-list" id="categoryList">
242
+ <!-- Categories injected via JS -->
243
+ </div>
244
+ <div style="padding: 20px; border-top: 1px solid var(--border);">
245
+ <button class="btn btn-outline" style="width: 100%;" onclick="app.logout()">
246
+ <i class="fa-solid fa-sign-out-alt"></i> Logout
247
+ </button>
248
+ </div>
249
+ </aside>
250
 
251
+ <!-- Main Content -->
252
+ <main class="main-content">
253
 
254
+ <!-- Top Header -->
255
+ <header class="top-header">
256
+ <div class="flex center" style="gap: 15px;">
257
+ <button class="btn-icon menu-toggle" onclick="app.toggleSidebar()">
258
+ <i class="fa-solid fa-bars"></i>
 
 
 
 
 
 
 
 
 
259
  </button>
260
+ <h3 id="pageTitle">Select a Grade</h3>
261
+ </div>
262
+ <div class="user-info">
263
+ <span id="userName" style="font-weight: 600; font-size: 0.9rem;">User</span>
264
+ <img src="https://picsum.photos/seed/user/40/40" class="user-avatar" alt="User">
265
+ <button class="btn-icon" onclick="app.openChat()" title="Messages">
266
+ <i class="fa-solid fa-comment-dots"></i>
267
  </button>
268
  </div>
269
+ </header>
270
 
271
+ <!-- Subcategory Strip (Subjects) -->
272
+ <div class="subject-strip" id="subjectStrip">
273
+ <!-- Subjects injected via JS -->
274
+ </div>
 
 
 
 
 
275
 
276
+ <!-- Sorting / Filter Bar -->
277
+ <div class="sort-bar" id="sortBar">
278
+ <i class="fa-solid fa-filter"></i>
279
+ <span>Sort by:</span>
280
+ <select id="sortSelect" onchange="app.renderFeed()">
281
+ <option value="all">All Content</option>
282
+ <option value="video">Videos Only</option>
283
+ <option value="pdf">PDFs & Docs</option>
284
+ <option value="text">Text Only</option>
285
+ </select>
286
+ </div>
287
 
288
+ <!-- Feed Area (WhatsApp Style) -->
289
+ <div class="feed-container" id="feedContainer">
290
+ <!-- Content injected via JS -->
291
+ </div>
 
292
 
293
+ <!-- Floating Action Button -->
294
+ <div id="fab" class="fab hidden" onclick="app.openUploadModal()">
295
+ <i class="fa-solid fa-plus"></i>
296
+ </div>
 
 
297
 
298
+ </main>
299
+ </div>
 
 
 
300
 
301
+ <!-- Upload Modal -->
302
+ <div class="modal-overlay" id="uploadModal">
303
+ <div class="modal-box">
304
+ <div class="modal-header">
305
+ <h3>Upload Content</h3>
306
+ <button class="btn-icon" style="color: white;" onclick="app.closeModals()"><i class="fa-solid fa-times"></i></button>
307
  </div>
308
+ <div class="upload-options">
309
+ <div class="upload-card" onclick="app.simulateUpload('video')">
310
+ <i class="fa-solid fa-video" style="font-size: 2rem; color: var(--primary); margin-bottom: 10px;"></i>
311
+ <p>Upload Video</p>
312
+ </div>
313
+ <div class="upload-card" onclick="app.simulateUpload('pdf')">
314
+ <i class="fa-solid fa-file-pdf" style="font-size: 2rem; color: var(--danger); margin-bottom: 10px;"></i>
315
+ <p>Upload PDF</p>
316
+ </div>
317
+ <div class="upload-card" onclick="app.simulateUpload('text')">
318
+ <i class="fa-solid fa-align-left" style="font-size: 2rem; color: var(--accent); margin-bottom: 10px;"></i>
319
+ <p>Post Text</p>
320
+ </div>
321
  </div>
 
322
  </div>
323
+ </div>
324
 
325
  <!-- Chat Modal -->
326
+ <div class="modal-overlay" id="chatModal">
327
+ <div class="modal-box">
328
+ <div class="modal-header">
329
+ <h3><i class="fa-solid fa-robot"></i> Support Chat</h3>
330
+ <button class="btn-icon" style="color: white;" onclick="app.closeModals()"><i class="fa-solid fa-times"></i></button>
331
  </div>
332
+ <div class="chat-area" id="chatArea">
333
+ <!-- Chat messages -->
334
  </div>
335
+ <div class="chat-input-area">
336
+ <input type="text" id="chatInput" placeholder="Type a message..." onkeypress="if(event.key === 'Enter') app.sendMessage()">
337
+ <button class="btn btn-primary" onclick="app.sendMessage()"><i class="fa-solid fa-paper-plane"></i></button>
338
  </div>
339
  </div>
340
  </div>
341
 
342
  <script>
343
  /**
344
+ * EduConnect Application Logic
345
+ * Simulates PHP/MySQL architecture using localStorage and JavaScript objects.
346
  */
347
  const app = {
348
+ // Data Store (Simulating MySQL Tables)
349
+ data: {
350
+ currentUser: null,
351
+ categories: [
352
+ { id: 1, name: 'Grade 1' },
353
+ { id: 2, name: 'Grade 2' },
354
+ { id: 3, name: 'Grade 3' },
355
+ { id: 4, name: 'Grade 4' },
356
+ { id: 5, name: 'Grade 5' },
357
+ { id: 6, name: 'Grade 6' },
358
+ { id: 7, name: 'Grade 7' },
359
+ { id: 8, name: 'Grade 8' },
360
+ { id: 9, name: 'Grade 9' },
361
+ { id: 10, name: 'Grade 10' },
362
+ { id: 11, name: 'Grade 11' },
363
+ { id: 12, name: 'Grade 12' }
364
+ ],
365
+ subcategories: [
366
+ { id: 1, catId: 1, name: 'Mathematics' },
367
+ { id: 2, catId: 1, name: 'English' },
368
+ { id: 3, catId: 1, name: 'Science' },
369
+ { id: 4, catId: 2, name: 'Mathematics' },
370
+ { id: 5, catId: 2, name: 'English' },
371
+ { id: 6, catId: 3, name: 'Physics' }, // Example for higher grades
372
+ ],
373
+ content: [
374
+ { id: 1, subId: 1, type: 'video', url: 'https://picsum.photos/seed/vid1/600/300', title: 'Introduction to Addition', author: 'Admin', date: '10:00 AM' },
375
+ { id: 2, subId: 1, type: 'pdf', title: 'Chapter 1 Exercises', author: 'Admin', date: '10:05 AM' },
376
+ { id: 3, subId: 2, type: 'text', text: 'Please read pages 10-15 for homework.', author: 'Admin', date: 'Yesterday' },
377
+ { id: 4, subId: 2, type: 'video', url: 'https://picsum.photos/seed/vid2/600/300', title: 'Vocabulary Lesson', author: 'Admin', date: 'Yesterday' }
378
+ ],
379
+ chat: [
380
+ { sender: 'Admin', text: 'Welcome to the new semester!', time: '09:00 AM' }
381
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
382
  },
383
 
384
+ state: {
385
+ selectedCatId: null,
386
+ selectedSubId: null
 
 
 
 
 
 
 
 
 
 
387
  },
388
 
389
+ // --- Authentication ---
390
+ login(role) {
391
+ // Simulate User Session
392
+ this.data.currentUser = {
393
+ name: role === 'admin' ? 'Administrator' : 'Student User',
394
+ role: role,
395
+ // Students only have access to Grade 1 and 2 for demo purposes
396
+ access: role === 'student' ? [1, 2] : null
397
+ };
398
 
399
+ document.getElementById('loginScreen').classList.add('hidden');
400
+ document.getElementById('appContainer').classList.remove('hidden');
401
+ document.getElementById('userName').textContent = this.data.currentUser.name;
 
 
 
 
 
 
 
 
402
 
403
+ // Show FAB only for Admin
404
+ if (role === 'admin') {
405
+ document.getElementById('fab').classList.remove('hidden');
 
 
406
  }
 
407
 
408
+ this.renderCategories();
409
+ this.renderChat();
 
 
 
 
 
 
 
410
  },
411
 
412
+ logout() {
413
+ location.reload(); // Simple reload to clear state for this demo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  },
415
 
416
+ // --- Navigation & Rendering ---
417
+ renderCategories() {
418
+ const list = document.getElementById('categoryList');
419
+ list.innerHTML = '';
420
+
421
+ // Filter based on permissions
422
+ const visibleCats = this.data.categories.filter(c => {
423
+ if (this.data.currentUser.role === 'admin') return true;
424
+ return this.data.currentUser.access.includes(c.id);
425
+ });
426
+
427
+ visibleCats.forEach(cat => {
428
+ const btn = document.createElement('button');
429
+ btn.className = `cat-btn ${this.state.selectedCatId === cat.id ? 'active' : ''}`;
430
+ btn.innerHTML = `<span>${cat.name}</span> <i class="fa-solid fa-chevron-right"></i>`;
431
+ btn.onclick = () => this.selectCategory(cat);
432
+ list.appendChild(btn);
433
+ });
 
 
 
 
434
  },
435
 
436
+ selectCategory(cat) {
437
+ this.state.selectedCatId = cat.id;
438
+ this.state.selectedSubId = null; // Reset sub selection
439
 
440
+ // UI Updates
441
+ document.getElementById('pageTitle').textContent = cat.name;
442
+ this.renderCategories(); // Re-render to update active class
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
 
444
+ // Render Subcategories (Subjects)
445
+ const strip = document.getElementById('subjectStrip');
446
+ strip.innerHTML = '';
 
 
 
447
 
448
+ const subs = this.data.subcategories.filter(s => s.catId === cat.id);
449
+ if (subs.length === 0) {
450
+ strip.innerHTML = '<span style="color:#888; padding:10px;">No subjects available.</span>';
451
+ this.renderFeed(); // Clear feed
452
+ return;
 
 
 
 
 
 
 
 
 
453
  }
454
 
455
+ subs.forEach(sub => {
456
+ const pill = document.createElement('div');
457
+ pill.className = 'subject-pill';
458
+ pill.textContent = sub.name;
459
+ pill.onclick = () => this.selectSubject(sub, pill);
460
+ strip.appendChild(pill);
461
+ });
462
 
463
+ // Auto-select first subject
464
+ this.selectSubject(subs[0], strip.children[0]);
 
 
 
 
 
 
 
 
 
 
 
 
465
  },
466
 
467
+ selectSubject(sub, element) {
468
+ this.state.selectedSubId = sub.id;
469
 
470
+ // UI Highlight
471
+ const pills = document.querySelectorAll('.subject-pill');
472
+ pills.forEach(p => p.classList.remove('active'));
473
+ if(element) element.classList.add('active');
 
 
 
 
 
 
 
 
474
 
475
+ this.renderFeed();
 
476
  },
477
 
478
+ // --- Feed Logic (WhatsApp Style) ---
479
+ renderFeed() {
480
+ const container = document.getElementById('feedContainer');
481
+ container.innerHTML = '';
482
+
483
+ const filterType = document.getElementById('sortSelect').value;
484
+
485
+ // Filter Content
486
+ const items = this.data.content.filter(item => {
487
+ const matchesSub = item.subId === this.state.selectedSubId;
488
+ const matchesType = filterType === 'all' || item.type === filterType;
489
+ return matchesSub && matchesType;
490
+ });
491
+
492
+ if (items.length === 0) {
493
+ container.innerHTML = `
494
+ <div style="text-align:center; margin-top:50px; color:#888;">
495
+ <i class="fa-solid fa-box-open" style="font-size:3rem; margin-bottom:10px;"></i>
496
+ <p>No content found for this selection.</p>
497
+ </div>
498
+ `;
499
+ return;
500
  }
 
 
 
 
 
 
 
 
 
 
501
 
502
+ items.forEach(item => {
503
+ const bubble = document.createElement('div');
504
+ bubble.className = 'msg-bubble';
505
+
506
+ let mediaHtml = '';
507
+ if (item.type === 'video') {
508
+ mediaHtml = `
509
+ <div class="media-preview">
510
+ <img src="${item.url}" alt="Video Thumb">
511
+ <div class="play-btn"><i class="fa-regular fa-circle-play"></i></div>
512
+ </div>
513
+ <div style="font-weight:600; margin-top:5px;">${item.title}</div>
514
+ `;
515
+ } else if (item.type === 'pdf') {
516
+ mediaHtml = `
517
+ <div class="file-attach">
518
+ <div class="file-icon"><i class="fa-solid fa-file-pdf"></i></div>
519
+ <div class="file-info">
520
+ <h4>${item.title}</h4>
521
+ <span>PDF Document</span>
522
+ </div>
523
+ <div style="margin-left:auto;"><i class="fa-solid fa-download"></i></div>
524
+ </div>
525
+ `;
526
+ } else {
527
+ mediaHtml = `<div class="msg-content">${item.text}</div>`;
528
+ }
529
 
530
+ bubble.innerHTML = `
531
+ <div class="msg-meta">
532
+ <span>${item.author}</span>
533
+ <span>${item.date}</span>
534
+ </div>
535
+ ${mediaHtml}
536
+ `;
537
+ container.appendChild(bubble);
538
+ });
 
 
539
  },
540
 
541
+ // --- Chat System ---
542
+ openChat() {
543
+ document.getElementById('chatModal').classList.add('open');
544
  },
545
 
546
+ sendMessage() {
547
+ const input = document.getElementById('chatInput');
548
+ const text = input.value.trim();
549
  if (!text) return;
550
 
551
+ // Add User Message
552
+ this.addChatMessage(this.data.currentUser.name, text);
553
+ input.value = '';
554
 
555
+ // Simulate Bot/Admin Reply
556
  setTimeout(() => {
557
+ const replies = [
558
+ "Thanks for your message! I'll check it.",
559
+ "Please check the 'Grade 1' Mathematics section for the update.",
560
+ "Got it.",
561
+ "Have you finished the homework?"
562
+ ];
563
+ const randomReply = replies[Math.floor(Math.random() * replies.length)];
564
+ this.addChatMessage('Admin', randomReply);
565
+ }, 1000);
566
  },
567
 
568
+ addChatMessage(sender, text) {
569
+ const area = document.getElementById('chatArea');
570
  const div = document.createElement('div');
571
+ const isMe = sender === this.data.currentUser.name;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
572
 
573
+ div.className = `msg-bubble ${isMe ? 'sent' : ''}`;
574
+ // Simplified chat bubble style
575
+ div.style.maxWidth = '80%';
576
+ div.innerHTML = `
577
+ <div style="font-size:0.75rem; color:#666; margin-bottom:2px;">${sender}</div>
578
+ <div>${text}</div>
579
+ `;
580
+ area.appendChild(div);
581
+ area.scrollTop = area.scrollHeight;
 
 
 
582
  },
583
 
584
+ renderChat() {
585
+ // Initial load of chat history
586
+ const area = document.getElementById('chatArea');
587
+ area.innerHTML = '';
588
+ this.data.chat.forEach(c => this.addChatMessage(c.sender, c.text));
589
  },
590
 
591
+ // --- Admin Simulation: Upload ---
592
+ openUploadModal() {
593
+ if (this.data.currentUser.role !== 'admin') return;
594
+ document.getElementById('uploadModal').classList.add('open');
 
 
 
 
595
  },
596
 
597
+ closeModals() {
598
+ document.querySelectorAll('.modal-overlay').forEach(el => el.classList.remove('open'));
 
 
 
 
 
 
 
 
599
  },
600
 
601
+ simulateUpload(type) {
602
+ // Simulate adding new content to the DB
603
+ const newContent = {
604
+ id: Date.now(),
605
+ subId: this.state.selectedSubId,
606
+ type: type,
607
+ date: new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}),
608
+ author: 'Admin'
609
+ };
610
+
611
+ if (type === 'video') {
612
+ newContent.url = `https://picsum.photos/seed/${Date.now()}/600/300`;
613
+ newContent.title = 'New Video Lesson';
614
+ } else if (type === 'pdf') {
615
+ newContent.title = 'Worksheet_' + Math.floor(Math.random() * 100) + '.pdf';
616
+ } else {
617
+ newContent.text = 'Notice: Please bring your notebooks tomorrow.';
618
  }
619
+
620
+ this.data.content.unshift(newContent); // Add to top
621
+ this.closeModals();
622
+ this.renderFeed();
623
 
624
+ // Show simple toast feedback (simulated)
625
+ const fab = document.getElementById('fab');
626
+ fab.style.background = '#4caf50';
627
+ setTimeout(() => fab.style.background = '', 1000);
628
  },
629
 
630
+ toggleSidebar() {
631
+ document.getElementById('sidebar').classList.toggle('open');
632
+ }
633
+ };
634
+
635
+ // Initialize empty state or check login
636
+ // (Login is handled by buttons on screen)
637
+ </script>
638
+ </body>
639
+ </html>