theaimoron commited on
Commit
2248bb5
·
verified ·
1 Parent(s): 004ec4e

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +1608 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Aac App For Web
3
- emoji: 😻
4
- colorFrom: indigo
5
  colorTo: gray
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: aac-app-for-web
3
+ emoji: 🐳
4
+ colorFrom: pink
5
  colorTo: gray
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1608 @@
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>LT | Let's Talk! - Giving a voice to the voiceless</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
11
+
12
+ :root {
13
+ --primary: #4f46e5;
14
+ --primary-light: #6366f1;
15
+ --secondary: #a5b4fc;
16
+ --accent: #10b981;
17
+ --text: #1f2937;
18
+ --light-bg: #f9fafb;
19
+ --card-bg: #ffffff;
20
+ }
21
+
22
+ body {
23
+ font-family: 'Poppins', sans-serif;
24
+ color: var(--text);
25
+ background-color: var(--light-bg);
26
+ }
27
+
28
+ .speech-animation {
29
+ animation: pulse 1.5s infinite;
30
+ }
31
+
32
+ @keyframes pulse {
33
+ 0% { transform: scale(1); }
34
+ 50% { transform: scale(1.05); }
35
+ 100% { transform: scale(1); }
36
+ }
37
+
38
+ .category-btn.active {
39
+ background-color: var(--primary);
40
+ color: white;
41
+ }
42
+
43
+ .word-btn {
44
+ transition: all 0.2s ease;
45
+ }
46
+
47
+ .word-btn:hover {
48
+ transform: translateY(-2px);
49
+ box-shadow: 0 4px 15px rgba(79, 70, 229, 0.2);
50
+ }
51
+
52
+ .sentence-bar {
53
+ min-height: 60px;
54
+ }
55
+
56
+ /* Custom scrollbar */
57
+ ::-webkit-scrollbar {
58
+ width: 6px;
59
+ height: 6px;
60
+ }
61
+
62
+ ::-webkit-scrollbar-track {
63
+ background: #f1f1f1;
64
+ border-radius: 10px;
65
+ }
66
+
67
+ ::-webkit-scrollbar-thumb {
68
+ background: var(--primary-light);
69
+ border-radius: 10px;
70
+ }
71
+
72
+ ::-webkit-scrollbar-thumb:hover {
73
+ background: var(--primary);
74
+ }
75
+
76
+ /* Speech bubble for messages */
77
+ .speech-bubble {
78
+ position: relative;
79
+ background: #e0e7ff;
80
+ border-radius: 1rem;
81
+ padding: 0.75rem 1rem;
82
+ max-width: 80%;
83
+ margin-bottom: 0.5rem;
84
+ box-shadow: 0 2px 5px rgba(0,0,0,0.05);
85
+ }
86
+
87
+ .speech-bubble:after {
88
+ content: '';
89
+ position: absolute;
90
+ bottom: 0;
91
+ left: 20px;
92
+ width: 0;
93
+ height: 0;
94
+ border: 10px solid transparent;
95
+ border-top-color: #e0e7ff;
96
+ border-bottom: 0;
97
+ margin-bottom: -10px;
98
+ }
99
+
100
+ /* Logo styling */
101
+ .logo {
102
+ display: flex;
103
+ align-items: center;
104
+ }
105
+
106
+ .logo-badge {
107
+ width: 36px;
108
+ height: 36px;
109
+ background: linear-gradient(135deg, var(--primary), var(--primary-light));
110
+ color: white;
111
+ border-radius: 8px;
112
+ display: flex;
113
+ align-items: center;
114
+ justify-content: center;
115
+ font-weight: 700;
116
+ margin-right: 10px;
117
+ box-shadow: 0 2px 8px rgba(79, 70, 229, 0.3);
118
+ }
119
+
120
+ .logo-text {
121
+ font-weight: 600;
122
+ font-size: 1.2rem;
123
+ }
124
+
125
+ .logo-tagline {
126
+ font-size: 0.65rem;
127
+ color: #a5b4fc;
128
+ letter-spacing: 0.5px;
129
+ margin-top: 2px;
130
+ }
131
+
132
+ /* Floating action buttons */
133
+ .fab {
134
+ width: 50px;
135
+ height: 50px;
136
+ border-radius: 50%;
137
+ display: flex;
138
+ align-items: center;
139
+ justify-content: center;
140
+ box-shadow: 0 4px 15px rgba(79, 70, 229, 0.3);
141
+ transition: all 0.2s ease;
142
+ }
143
+
144
+ .fab:hover {
145
+ transform: translateY(-3px);
146
+ box-shadow: 0 6px 20px rgba(79, 70, 229, 0.4);
147
+ }
148
+
149
+ /* Gradient buttons */
150
+ .btn-gradient {
151
+ background: linear-gradient(135deg, var(--primary), var(--primary-light));
152
+ color: white;
153
+ border: none;
154
+ transition: all 0.2s ease;
155
+ }
156
+
157
+ .btn-gradient:hover {
158
+ transform: translateY(-2px);
159
+ box-shadow: 0 4px 15px rgba(79, 70, 229, 0.3);
160
+ }
161
+
162
+ /* Modal styling */
163
+ .modal-content {
164
+ border-radius: 16px;
165
+ box-shadow: 0 10px 25px rgba(0,0,0,0.1);
166
+ }
167
+
168
+ /* Word grid styling */
169
+ .word-grid {
170
+ display: grid;
171
+ gap: 12px;
172
+ grid-template-columns: repeat(4, 1fr);
173
+ }
174
+
175
+ @media (max-width: 640px) {
176
+ .word-grid {
177
+ grid-template-columns: repeat(3, 1fr);
178
+ }
179
+ }
180
+
181
+ /* Fade in animation */
182
+ @keyframes fadeIn {
183
+ from { opacity: 0; transform: translateY(10px); }
184
+ to { opacity: 1; transform: translateY(0); }
185
+ }
186
+
187
+ .fade-in {
188
+ animation: fadeIn 0.3s ease-out;
189
+ }
190
+
191
+ /* Glow effect for active elements */
192
+ .glow {
193
+ box-shadow: 0 0 15px rgba(79, 70, 229, 0.3);
194
+ }
195
+
196
+ /* Auth modal tabs */
197
+ .auth-tab {
198
+ padding: 0.75rem 1rem;
199
+ border-bottom: 2px solid transparent;
200
+ transition: all 0.2s ease;
201
+ cursor: pointer;
202
+ }
203
+
204
+ .auth-tab.active {
205
+ border-bottom-color: var(--primary);
206
+ color: var(--primary);
207
+ font-weight: 500;
208
+ }
209
+
210
+ /* Input styling */
211
+ .auth-input {
212
+ padding: 0.75rem 1rem;
213
+ border: 1px solid #e5e7eb;
214
+ border-radius: 0.5rem;
215
+ width: 100%;
216
+ transition: all 0.2s ease;
217
+ }
218
+
219
+ .auth-input:focus {
220
+ border-color: var(--primary-light);
221
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
222
+ outline: none;
223
+ }
224
+
225
+ /* Welcome message */
226
+ .welcome-message {
227
+ background: linear-gradient(135deg, var(--primary), var(--primary-light));
228
+ color: white;
229
+ padding: 0.5rem 1rem;
230
+ border-radius: 2rem;
231
+ font-weight: 500;
232
+ margin-left: 1rem;
233
+ display: inline-flex;
234
+ align-items: center;
235
+ }
236
+
237
+ /* Form validation */
238
+ .error-message {
239
+ color: #ef4444;
240
+ font-size: 0.75rem;
241
+ margin-top: 0.25rem;
242
+ display: none;
243
+ }
244
+
245
+ .input-error {
246
+ border-color: #ef4444;
247
+ }
248
+ </style>
249
+ </head>
250
+ <body class="min-h-screen flex flex-col bg-gray-50">
251
+ <!-- Header -->
252
+ <header class="bg-white shadow-sm">
253
+ <div class="container mx-auto px-4 py-3 flex justify-between items-center">
254
+ <div class="logo">
255
+ <div class="logo-badge">LT</div>
256
+ <div>
257
+ <div class="logo-text">Let's Talk!</div>
258
+ <div class="logo-tagline">Giving a voice to the voiceless</div>
259
+ </div>
260
+ </div>
261
+ <div class="flex items-center space-x-2">
262
+ <div id="welcome-message" class="hidden">
263
+ <span class="welcome-message">
264
+ <i class="fas fa-smile mr-2"></i>
265
+ <span id="user-greeting"></span>
266
+ </span>
267
+ </div>
268
+ <button id="auth-btn" class="px-4 py-2 rounded-lg bg-indigo-600 text-white hover:bg-indigo-700 transition">
269
+ <i class="fas fa-sign-in-alt mr-2"></i> Sign In
270
+ </button>
271
+ <button id="settings-btn" class="p-2 rounded-full text-gray-500 hover:text-indigo-600 hover:bg-indigo-50 transition">
272
+ <i class="fas fa-cog"></i>
273
+ </button>
274
+ <button id="user-btn" class="p-2 rounded-full text-gray-500 hover:text-indigo-600 hover:bg-indigo-50 transition hidden">
275
+ <i class="fas fa-user"></i>
276
+ </button>
277
+ </div>
278
+ </div>
279
+ </header>
280
+
281
+ <!-- Main Content -->
282
+ <main class="flex-1 container mx-auto px-4 py-6 flex flex-col">
283
+ <!-- Message Display Area -->
284
+ <div id="message-display" class="bg-white rounded-xl shadow-sm p-4 mb-4 flex-1 overflow-y-auto max-h-40">
285
+ <div class="empty-message text-center py-8">
286
+ <i class="fas fa-comment-dots text-3xl mb-3 text-gray-300"></i>
287
+ <p class="text-gray-400">Your spoken messages will appear here</p>
288
+ </div>
289
+ </div>
290
+
291
+ <!-- Sentence Construction Bar -->
292
+ <div id="sentence-bar" class="bg-white rounded-xl shadow-sm p-3 mb-4 flex items-center overflow-x-auto sentence-bar">
293
+ <div class="flex space-x-2">
294
+ <!-- Words will be added here dynamically -->
295
+ </div>
296
+ </div>
297
+
298
+ <!-- Action Buttons -->
299
+ <div class="flex justify-between mb-6">
300
+ <button id="clear-btn" class="px-4 py-2 rounded-lg flex items-center space-x-2 transition text-gray-500 hover:text-gray-700 hover:bg-gray-100">
301
+ <i class="fas fa-trash-alt"></i>
302
+ <span>Clear</span>
303
+ </button>
304
+ <div class="flex space-x-3">
305
+ <button id="speak-btn" class="btn-gradient px-5 py-2 rounded-lg flex items-center space-x-2 speech-animation">
306
+ <i class="fas fa-volume-up"></i>
307
+ <span>Speak</span>
308
+ </button>
309
+ <button id="save-phrase-btn" class="bg-emerald-500 hover:bg-emerald-600 text-white px-4 py-2 rounded-lg flex items-center space-x-2 transition shadow-sm">
310
+ <i class="fas fa-save"></i>
311
+ <span>Save</span>
312
+ </button>
313
+ </div>
314
+ </div>
315
+
316
+ <!-- Categories Navigation -->
317
+ <div id="categories" class="flex space-x-2 mb-6 overflow-x-auto pb-2">
318
+ <button data-category="core" class="category-btn active bg-indigo-600 text-white px-4 py-2 rounded-lg whitespace-nowrap">Core Words</button>
319
+ <button data-category="people" class="category-btn bg-gray-100 hover:bg-gray-200 px-4 py-2 rounded-lg whitespace-nowrap">People</button>
320
+ <button data-category="actions" class="category-btn bg-gray-100 hover:bg-gray-200 px-4 py-2 rounded-lg whitespace-nowrap">Actions</button>
321
+ <button data-category="food" class="category-btn bg-gray-100 hover:bg-gray-200 px-4 py-2 rounded-lg whitespace-nowrap">Food</button>
322
+ <button data-category="places" class="category-btn bg-gray-100 hover:bg-gray-200 px-4 py-2 rounded-lg whitespace-nowrap">Places</button>
323
+ <button data-category="feelings" class="category-btn bg-gray-100 hover:bg-gray-200 px-4 py-2 rounded-lg whitespace-nowrap">Feelings</button>
324
+ <button data-category="questions" class="category-btn bg-gray-100 hover:bg-gray-200 px-4 py-2 rounded-lg whitespace-nowrap">Questions</button>
325
+ <button data-category="time" class="category-btn bg-gray-100 hover:bg-gray-200 px-4 py-2 rounded-lg whitespace-nowrap">Time</button>
326
+ </div>
327
+
328
+ <!-- Word Grid -->
329
+ <div id="word-grid" class="word-grid mb-6 flex-1 overflow-y-auto">
330
+ <!-- Words will be loaded dynamically based on category -->
331
+ </div>
332
+
333
+ <!-- Quick Access Bar -->
334
+ <div class="bg-white rounded-xl shadow-sm p-3 flex justify-around">
335
+ <button data-quick="home" class="p-2 rounded-full text-indigo-600 hover:bg-indigo-50 transition">
336
+ <i class="fas fa-home text-xl"></i>
337
+ </button>
338
+ <button data-quick="food" class="p-2 rounded-full text-indigo-600 hover:bg-indigo-50 transition">
339
+ <i class="fas fa-utensils text-xl"></i>
340
+ </button>
341
+ <button data-quick="bathroom" class="p-2 rounded-full text-indigo-600 hover:bg-indigo-50 transition">
342
+ <i class="fas fa-toilet text-xl"></i>
343
+ </button>
344
+ <button data-quick="sleep" class="p-2 rounded-full text-indigo-600 hover:bg-indigo-50 transition">
345
+ <i class="fas fa-bed text-xl"></i>
346
+ </button>
347
+ <button data-quick="learn" class="p-2 rounded-full text-indigo-600 hover:bg-indigo-50 transition">
348
+ <i class="fas fa-book text-xl"></i>
349
+ </button>
350
+ </div>
351
+ </main>
352
+
353
+ <!-- Settings Modal -->
354
+ <div id="settings-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
355
+ <div class="modal-content bg-white rounded-xl w-full max-w-md max-h-[90vh] overflow-y-auto">
356
+ <div class="p-5 border-b border-gray-100">
357
+ <h2 class="text-xl font-bold text-gray-800">Settings</h2>
358
+ </div>
359
+ <div class="p-5 space-y-5">
360
+ <!-- Voice Settings -->
361
+ <div>
362
+ <h3 class="font-medium mb-3 text-gray-700">Voice Settings</h3>
363
+ <div class="space-y-4">
364
+ <div>
365
+ <label class="block text-sm text-gray-600 mb-1">Voice</label>
366
+ <select id="voice-select" class="w-full p-2 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-500">
367
+ <!-- Voices will be populated by JavaScript -->
368
+ </select>
369
+ </div>
370
+ <div>
371
+ <label class="block text-sm text-gray-600 mb-1">Speech Rate</label>
372
+ <div class="flex items-center space-x-2">
373
+ <span class="text-xs text-gray-500">Slow</span>
374
+ <input type="range" id="rate-slider" min="0.5" max="2" step="0.1" value="1" class="flex-1 accent-indigo-500">
375
+ <span class="text-xs text-gray-500">Fast</span>
376
+ </div>
377
+ <div class="text-xs text-gray-500 text-center mt-1" id="rate-value">Normal speed</div>
378
+ </div>
379
+ <div>
380
+ <label class="block text-sm text-gray-600 mb-1">Pitch</label>
381
+ <div class="flex items-center space-x-2">
382
+ <span class="text-xs text-gray-500">Low</span>
383
+ <input type="range" id="pitch-slider" min="0.5" max="2" step="0.1" value="1" class="flex-1 accent-indigo-500">
384
+ <span class="text-xs text-gray-500">High</span>
385
+ </div>
386
+ <div class="text-xs text-gray-500 text-center mt-1" id="pitch-value">Normal pitch</div>
387
+ </div>
388
+ <div class="flex justify-between items-center">
389
+ <span class="text-sm text-gray-700">Preview Voice</span>
390
+ <button id="preview-voice-btn" class="px-4 py-2 bg-indigo-100 text-indigo-700 rounded-lg hover:bg-indigo-200 transition flex items-center">
391
+ <i class="fas fa-play mr-2"></i> Play
392
+ </button>
393
+ </div>
394
+ </div>
395
+ </div>
396
+
397
+ <!-- Display Settings -->
398
+ <div>
399
+ <h3 class="font-medium mb-3 text-gray-700">Display Settings</h3>
400
+ <div class="space-y-4">
401
+ <div class="flex justify-between items-center">
402
+ <span class="text-sm text-gray-700">Dark Mode</span>
403
+ <label class="relative inline-flex items-center cursor-pointer">
404
+ <input type="checkbox" id="dark-mode-toggle" class="sr-only peer">
405
+ <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-indigo-200 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-indigo-600"></div>
406
+ </label>
407
+ </div>
408
+ <div>
409
+ <label class="block text-sm text-gray-600 mb-1">Grid Size</label>
410
+ <select id="grid-size-select" class="w-full p-2 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-500">
411
+ <option value="4">4x4 (Default)</option>
412
+ <option value="3">3x3</option>
413
+ <option value="5">5x5</option>
414
+ <option value="6">6x6</option>
415
+ </select>
416
+ </div>
417
+ <div>
418
+ <label class="block text-sm text-gray-600 mb-1">Text Size</label>
419
+ <select id="text-size-select" class="w-full p-2 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-500">
420
+ <option value="small">Small</option>
421
+ <option value="medium" selected>Medium</option>
422
+ <option value="large">Large</option>
423
+ <option value="xlarge">Extra Large</option>
424
+ </select>
425
+ </div>
426
+ </div>
427
+ </div>
428
+
429
+ <!-- Advanced Settings -->
430
+ <div>
431
+ <h3 class="font-medium mb-3 text-gray-700">Advanced</h3>
432
+ <div class="space-y-4">
433
+ <div class="flex justify-between items-center">
434
+ <span class="text-sm text-gray-700">Word Prediction</span>
435
+ <label class="relative inline-flex items-center cursor-pointer">
436
+ <input type="checkbox" id="prediction-toggle" checked class="sr-only peer">
437
+ <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-indigo-200 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-indigo-600"></div>
438
+ </label>
439
+ </div>
440
+ <div class="flex justify-between items-center">
441
+ <span class="text-sm text-gray-700">Auto Capitalization</span>
442
+ <label class="relative inline-flex items-center cursor-pointer">
443
+ <input type="checkbox" id="capitalization-toggle" checked class="sr-only peer">
444
+ <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-indigo-200 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-indigo-600"></div>
445
+ </label>
446
+ </div>
447
+ <div class="flex justify-between items-center">
448
+ <span class="text-sm text-gray-700">Speak on Select</span>
449
+ <label class="relative inline-flex items-center cursor-pointer">
450
+ <input type="checkbox" id="speak-toggle" class="sr-only peer">
451
+ <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-indigo-200 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-indigo-600"></div>
452
+ </label>
453
+ </div>
454
+ </div>
455
+ </div>
456
+ </div>
457
+ <div class="p-5 border-t border-gray-100 flex justify-end space-x-3">
458
+ <button id="cancel-settings" class="px-5 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg transition">Cancel</button>
459
+ <button id="save-settings" class="px-5 py-2 btn-gradient rounded-lg">Save</button>
460
+ </div>
461
+ </div>
462
+ </div>
463
+
464
+ <!-- User Profile Modal -->
465
+ <div id="user-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
466
+ <div class="modal-content bg-white rounded-xl w-full max-w-md">
467
+ <div class="p-5 border-b border-gray-100">
468
+ <h2 class="text-xl font-bold text-gray-800">User Profile</h2>
469
+ </div>
470
+ <div class="p-5 space-y-5">
471
+ <div class="flex flex-col items-center">
472
+ <div class="w-24 h-24 rounded-full bg-indigo-100 flex items-center justify-center mb-4 shadow-inner">
473
+ <i class="fas fa-user text-4xl text-indigo-600"></i>
474
+ </div>
475
+ <h3 class="font-bold text-lg" id="profile-name">John Doe</h3>
476
+ <p class="text-sm text-gray-500">Primary User</p>
477
+ </div>
478
+
479
+ <div>
480
+ <h3 class="font-medium mb-3 text-gray-700">User Settings</h3>
481
+ <div class="space-y-4">
482
+ <div>
483
+ <label class="block text-sm text-gray-600 mb-1">Name</label>
484
+ <input type="text" id="profile-name-input" value="John Doe" class="w-full p-2 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-500">
485
+ </div>
486
+ <div>
487
+ <label class="block text-sm text-gray-600 mb-1">Age</label>
488
+ <input type="number" id="profile-age" value="12" class="w-full p-2 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-500">
489
+ </div>
490
+ <div>
491
+ <label class="block text-sm text-gray-600 mb-1">Primary Language</label>
492
+ <select id="profile-language" class="w-full p-2 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-500">
493
+ <option selected>English</option>
494
+ <option>Spanish</option>
495
+ <option>French</option>
496
+ <option>German</option>
497
+ </select>
498
+ </div>
499
+ </div>
500
+ </div>
501
+
502
+ <div>
503
+ <h3 class="font-medium mb-3 text-gray-700">Saved Phrases</h3>
504
+ <div class="bg-gray-50 rounded-lg p-3 max-h-40 overflow-y-auto">
505
+ <div class="space-y-2" id="saved-phrases-list">
506
+ <div class="bg-white p-3 rounded-lg flex justify-between items-center shadow-sm">
507
+ <span class="text-gray-700">I need to use the bathroom</span>
508
+ <button class="text-indigo-600 hover:text-indigo-800"><i class="fas fa-play"></i></button>
509
+ </div>
510
+ <div class="bg-white p-3 rounded-lg flex justify-between items-center shadow-sm">
511
+ <span class="text-gray-700">I'm hungry</span>
512
+ <button class="text-indigo-600 hover:text-indigo-800"><i class="fas fa-play"></i></button>
513
+ </div>
514
+ <div class="bg-white p-3 rounded-lg flex justify-between items-center shadow-sm">
515
+ <span class="text-gray-700">I don't feel well</span>
516
+ <button class="text-indigo-600 hover:text-indigo-800"><i class="fas fa-play"></i></button>
517
+ </div>
518
+ <div class="bg-white p-3 rounded-lg flex justify-between items-center shadow-sm">
519
+ <span class="text-gray-700">Can we play a game?</span>
520
+ <button class="text-indigo-600 hover:text-indigo-800"><i class="fas fa-play"></i></button>
521
+ </div>
522
+ </div>
523
+ </div>
524
+ </div>
525
+ </div>
526
+ <div class="p-5 border-t border-gray-100 flex justify-end space-x-3">
527
+ <button id="cancel-user" class="px-5 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg transition">Cancel</button>
528
+ <button id="save-user" class="px-5 py-2 btn-gradient rounded-lg">Save</button>
529
+ </div>
530
+ </div>
531
+ </div>
532
+
533
+ <!-- Save Phrase Modal -->
534
+ <div id="save-phrase-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
535
+ <div class="modal-content bg-white rounded-xl w-full max-w-md">
536
+ <div class="p-5 border-b border-gray-100">
537
+ <h2 class="text-xl font-bold text-gray-800">Save Current Phrase</h2>
538
+ </div>
539
+ <div class="p-5 space-y-4">
540
+ <div>
541
+ <label class="block text-sm text-gray-600 mb-1">Phrase</label>
542
+ <div class="bg-gray-50 p-3 rounded-lg">
543
+ <p id="phrase-to-save" class="text-gray-700">I want to eat pizza</p>
544
+ </div>
545
+ </div>
546
+ <div>
547
+ <label class="block text-sm text-gray-600 mb-1">Name for this phrase</label>
548
+ <input type="text" id="phrase-name" placeholder="e.g. Pizza request" class="w-full p-2 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-500">
549
+ </div>
550
+ <div>
551
+ <label class="block text-sm text-gray-600 mb-1">Category</label>
552
+ <select id="phrase-category" class="w-full p-2 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-500">
553
+ <option>Food</option>
554
+ <option>Requests</option>
555
+ <option>Favorites</option>
556
+ <option>Custom</option>
557
+ </select>
558
+ </div>
559
+ </div>
560
+ <div class="p-5 border-t border-gray-100 flex justify-end space-x-3">
561
+ <button id="cancel-save-phrase" class="px-5 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg transition">Cancel</button>
562
+ <button id="confirm-save-phrase" class="px-5 py-2 bg-emerald-500 hover:bg-emerald-600 text-white rounded-lg shadow-sm">Save</button>
563
+ </div>
564
+ </div>
565
+ </div>
566
+
567
+ <!-- Auth Modal -->
568
+ <div id="auth-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
569
+ <div class="modal-content bg-white rounded-xl w-full max-w-md">
570
+ <div class="p-5 border-b border-gray-100">
571
+ <h2 class="text-xl font-bold text-gray-800">Welcome to Let's Talk!</h2>
572
+ </div>
573
+ <div class="p-5">
574
+ <!-- Auth Tabs -->
575
+ <div class="flex border-b border-gray-200 mb-6">
576
+ <button id="login-tab" class="auth-tab active">Sign In</button>
577
+ <button id="signup-tab" class="auth-tab">Create Account</button>
578
+ </div>
579
+
580
+ <!-- Login Form -->
581
+ <div id="login-form" class="space-y-4">
582
+ <div>
583
+ <label class="block text-sm text-gray-600 mb-1">Email</label>
584
+ <input type="email" id="login-email" class="auth-input" placeholder="your@email.com">
585
+ <div id="login-email-error" class="error-message">Please enter a valid email</div>
586
+ </div>
587
+ <div>
588
+ <label class="block text-sm text-gray-600 mb-1">Password</label>
589
+ <input type="password" id="login-password" class="auth-input" placeholder="••••••••">
590
+ <div id="login-password-error" class="error-message">Password is required</div>
591
+ </div>
592
+ <button id="login-btn" class="w-full btn-gradient py-2 rounded-lg mt-4">
593
+ Sign In
594
+ </button>
595
+ <div class="text-center text-sm text-gray-500 mt-4">
596
+ Don't have an account? <button id="switch-to-signup" class="text-indigo-600 hover:text-indigo-800">Create one</button>
597
+ </div>
598
+ </div>
599
+
600
+ <!-- Signup Form -->
601
+ <div id="signup-form" class="space-y-4 hidden">
602
+ <div>
603
+ <label class="block text-sm text-gray-600 mb-1">First Name</label>
604
+ <input type="text" id="signup-firstname" class="auth-input" placeholder="John">
605
+ <div id="signup-firstname-error" class="error-message">First name is required</div>
606
+ </div>
607
+ <div>
608
+ <label class="block text-sm text-gray-600 mb-1">Last Name</label>
609
+ <input type="text" id="signup-lastname" class="auth-input" placeholder="Doe">
610
+ <div id="signup-lastname-error" class="error-message">Last name is required</div>
611
+ </div>
612
+ <div>
613
+ <label class="block text-sm text-gray-600 mb-1">Email</label>
614
+ <input type="email" id="signup-email" class="auth-input" placeholder="your@email.com">
615
+ <div id="signup-email-error" class="error-message">Please enter a valid email</div>
616
+ </div>
617
+ <div>
618
+ <label class="block text-sm text-gray-600 mb-1">Password</label>
619
+ <input type="password" id="signup-password" class="auth-input" placeholder="••••••••">
620
+ <div id="signup-password-error" class="error-message">Password must be at least 6 characters</div>
621
+ </div>
622
+ <div>
623
+ <label class="block text-sm text-gray-600 mb-1">Confirm Password</label>
624
+ <input type="password" id="signup-confirm-password" class="auth-input" placeholder="••••••••">
625
+ <div id="signup-confirm-error" class="error-message">Passwords must match</div>
626
+ </div>
627
+ <button id="signup-btn" class="w-full btn-gradient py-2 rounded-lg mt-4">
628
+ Create Account
629
+ </button>
630
+ <div class="text-center text-sm text-gray-500 mt-4">
631
+ Already have an account? <button id="switch-to-login" class="text-indigo-600 hover:text-indigo-800">Sign in</button>
632
+ </div>
633
+ </div>
634
+ </div>
635
+ </div>
636
+ </div>
637
+
638
+ <script>
639
+ document.addEventListener('DOMContentLoaded', function() {
640
+ // DOM Elements
641
+ const settingsBtn = document.getElementById('settings-btn');
642
+ const userBtn = document.getElementById('user-btn');
643
+ const savePhraseBtn = document.getElementById('save-phrase-btn');
644
+ const clearBtn = document.getElementById('clear-btn');
645
+ const speakBtn = document.getElementById('speak-btn');
646
+ const categoryBtns = document.querySelectorAll('.category-btn');
647
+ const wordGrid = document.getElementById('word-grid');
648
+ const quickAccessBtns = document.querySelectorAll('[data-quick]');
649
+ const authBtn = document.getElementById('auth-btn');
650
+ const welcomeMessage = document.getElementById('welcome-message');
651
+ const userGreeting = document.getElementById('user-greeting');
652
+
653
+ // Modal Elements
654
+ const settingsModal = document.getElementById('settings-modal');
655
+ const userModal = document.getElementById('user-modal');
656
+ const savePhraseModal = document.getElementById('save-phrase-modal');
657
+ const authModal = document.getElementById('auth-modal');
658
+ const cancelSettings = document.getElementById('cancel-settings');
659
+ const saveSettings = document.getElementById('save-settings');
660
+ const cancelUser = document.getElementById('cancel-user');
661
+ const saveUser = document.getElementById('save-user');
662
+ const cancelSavePhrase = document.getElementById('cancel-save-phrase');
663
+ const confirmSavePhrase = document.getElementById('confirm-save-phrase');
664
+
665
+ // Auth Modal Elements
666
+ const loginTab = document.getElementById('login-tab');
667
+ const signupTab = document.getElementById('signup-tab');
668
+ const loginForm = document.getElementById('login-form');
669
+ const signupForm = document.getElementById('signup-form');
670
+ const switchToSignup = document.getElementById('switch-to-signup');
671
+ const switchToLogin = document.getElementById('switch-to-login');
672
+ const loginBtn = document.getElementById('login-btn');
673
+ const signupBtn = document.getElementById('signup-btn');
674
+ const loginEmail = document.getElementById('login-email');
675
+ const loginPassword = document.getElementById('login-password');
676
+ const signupFirstname = document.getElementById('signup-firstname');
677
+ const signupLastname = document.getElementById('signup-lastname');
678
+ const signupEmail = document.getElementById('signup-email');
679
+ const signupPassword = document.getElementById('signup-password');
680
+ const signupConfirmPassword = document.getElementById('signup-confirm-password');
681
+
682
+ // Error message elements
683
+ const loginEmailError = document.getElementById('login-email-error');
684
+ const loginPasswordError = document.getElementById('login-password-error');
685
+ const signupFirstnameError = document.getElementById('signup-firstname-error');
686
+ const signupLastnameError = document.getElementById('signup-lastname-error');
687
+ const signupEmailError = document.getElementById('signup-email-error');
688
+ const signupPasswordError = document.getElementById('signup-password-error');
689
+ const signupConfirmError = document.getElementById('signup-confirm-error');
690
+
691
+ // Profile Elements
692
+ const profileName = document.getElementById('profile-name');
693
+ const profileNameInput = document.getElementById('profile-name-input');
694
+ const profileAge = document.getElementById('profile-age');
695
+ const profileLanguage = document.getElementById('profile-language');
696
+
697
+ // Voice Settings Elements
698
+ const voiceSelect = document.getElementById('voice-select');
699
+ const rateSlider = document.getElementById('rate-slider');
700
+ const pitchSlider = document.getElementById('pitch-slider');
701
+ const rateValue = document.getElementById('rate-value');
702
+ const pitchValue = document.getElementById('pitch-value');
703
+ const previewVoiceBtn = document.getElementById('preview-voice-btn');
704
+ const phraseToSave = document.getElementById('phrase-to-save');
705
+ const phraseName = document.getElementById('phrase-name');
706
+ const phraseCategory = document.getElementById('phrase-category');
707
+ const savedPhrasesList = document.getElementById('saved-phrases-list');
708
+
709
+ // Display Settings
710
+ const gridSizeSelect = document.getElementById('grid-size-select');
711
+ const textSizeSelect = document.getElementById('text-size-select');
712
+ const darkModeToggle = document.getElementById('dark-mode-toggle');
713
+ const speakToggle = document.getElementById('speak-toggle');
714
+
715
+ // Speech Synthesis
716
+ let voices = [];
717
+ let currentVoice = null;
718
+ let speechRate = 1;
719
+ let speechPitch = 1;
720
+ let speakOnSelect = false;
721
+
722
+ // App State
723
+ let currentCategory = 'core';
724
+ let savedPhrases = [
725
+ { text: "I need to use the bathroom", name: "Bathroom", category: "Requests" },
726
+ { text: "I'm hungry", name: "Hungry", category: "Food" },
727
+ { text: "I don't feel well", name: "Not feeling well", category: "Health" },
728
+ { text: "Can we play a game?", name: "Play game", category: "Requests" }
729
+ ];
730
+
731
+ // User State
732
+ let currentUser = null;
733
+
734
+ // Word Database
735
+ const wordDatabase = {
736
+ core: [
737
+ { symbol: "👋", word: "Hello" },
738
+ { symbol: "👋", word: "Goodbye" },
739
+ { symbol: "👍", word: "Yes" },
740
+ { symbol: "👎", word: "No" },
741
+ { symbol: "🙏", word: "Please" },
742
+ { symbol: "😊", word: "Thank you" },
743
+ { symbol: "❓", word: "What?" },
744
+ { symbol: "👤", word: "I" },
745
+ { symbol: "👥", word: "You" },
746
+ { symbol: "❤️", word: "Love" },
747
+ { symbol: "😊", word: "Happy" },
748
+ { symbol: "😢", word: "Sad" },
749
+ { symbol: "➕", word: "More" },
750
+ { symbol: "➖", word: "Less" },
751
+ { symbol: "🏠", word: "Home" },
752
+ { symbol: "🏫", word: "School" }
753
+ ],
754
+ people: [
755
+ { symbol: "👨", word: "Dad" },
756
+ { symbol: "👩", word: "Mom" },
757
+ { symbol: "👦", word: "Brother" },
758
+ { symbol: "👧", word: "S sister" },
759
+ { symbol: "👴", word: "Grandpa" },
760
+ { symbol: "👵", word: "Grandma" },
761
+ { symbol: "👨‍⚕️", word: "Doctor" },
762
+ { symbol: "👩‍🏫", word: "Teacher" },
763
+ { symbol: "👨‍🎓", word: "Friend" },
764
+ { symbol: "👮", word: "Police" },
765
+ { symbol: "👨‍🍳", word: "Cook" },
766
+ { symbol: "👩‍⚕️", word: "Nurse" },
767
+ { symbol: "👶", word: "Baby" },
768
+ { symbol: "🐶", word: "Dog" },
769
+ { symbol: "🐱", word: "Cat" },
770
+ { symbol: "👪", word: "Family" }
771
+ ],
772
+ actions: [
773
+ { symbol: "🏃", word: "Run" },
774
+ { symbol: "🚶", word: "Walk" },
775
+ { symbol: "🛏️", word: "Sleep" },
776
+ { symbol: "🍽️", word: "Eat" },
777
+ { symbol: "🚰", word: "Drink" },
778
+ { symbol: "🚿", word: "Shower" },
779
+ { symbol: "🛁", word: "Bath" },
780
+ { symbol: "📖", word: "Read" },
781
+ { symbol: "✏️", word: "Write" },
782
+ { symbol: "🎨", word: "Draw" },
783
+ { symbol: "🎮", word: "Play" },
784
+ { symbol: "🎵", word: "Sing" },
785
+ { symbol: "💃", word: "Dance" },
786
+ { symbol: "🏊", word: "Swim" },
787
+ { symbol: "🚗", word: "Drive" },
788
+ { symbol: "🛒", word: "Shop" }
789
+ ],
790
+ food: [
791
+ { symbol: "🍎", word: "Apple" },
792
+ { symbol: "🍌", word: "Banana" },
793
+ { symbol: "🍞", word: "B bread" },
794
+ { symbol: "🧀", word: "Cheese" },
795
+ { symbol: "🍗", word: "Chicken" },
796
+ { symbol: "🍫", word: "Chocolate" },
797
+ { symbol: "☕", word: "Coffee" },
798
+ { symbol: "🍪", word: "Cookie" },
799
+ { symbol: "🥚", word: "Egg" },
800
+ { symbol: "🐟", word: "Fish" },
801
+ { symbol: "🍇", word: "Grapes" },
802
+ { symbol: "🍔", word: "Hamburger" },
803
+ { symbol: "🍦", word: "Ice cream" },
804
+ { symbol: "🍋", word: "Lemon" },
805
+ { symbol: "🍕", word: "Pizza" },
806
+ { symbol: "🥪", word: "Sandwich" }
807
+ ],
808
+ places: [
809
+ { symbol: "🏠", word: "Home" },
810
+ { symbol: "🏫", word: "School" },
811
+ { symbol: "🏥", word: "Hospital" },
812
+ { symbol: "🏪", word: "Store" },
813
+ { symbol: "🏢", word: "Office" },
814
+ { symbol: "🏛️", word: "Library" },
815
+ { symbol: "⛪", word: "Church" },
816
+ { symbol: "🏟️", word: "Stadium" },
817
+ { symbol: "🏖️", word: "Beach" },
818
+ { symbol: "🏞️", word: "Park" },
819
+ { symbol: "🏨", word: "Hotel" },
820
+ { symbol: "✈️", word: "Airport" },
821
+ { symbol: "🚉", word: "Station" },
822
+ { symbol: "🏭", word: "Factory" },
823
+ { symbol: "🏰", word: "Castle" },
824
+ { symbol: "🗽", word: "Monument" }
825
+ ],
826
+ feelings: [
827
+ { symbol: "😊", word: "Happy" },
828
+ { symbol: "😢", word: "Sad" },
829
+ { symbol: "😡", word: "Angry" },
830
+ { symbol: "😨", word: "Scared" },
831
+ { symbol: "😲", word: "Surprised" },
832
+ { symbol: "😴", word: "Sleepy" },
833
+ { symbol: "🤢", word: "Sick" },
834
+ { symbol: "😍", word: "Excited" },
835
+ { symbol: "😞", word: "Disappointed" },
836
+ { symbol: "😕", word: "Confused" },
837
+ { symbol: "😳", word: "Embarrassed" },
838
+ { symbol: "😌", word: "Relaxed" },
839
+ { symbol: "😭", word: "Crying" },
840
+ { symbol: "😤", word: "Frustrated" },
841
+ { symbol: "😃", word: "Joyful" },
842
+ { symbol: "😟", word: "Worried" }
843
+ ],
844
+ questions: [
845
+ { symbol: "❓", word: "What?" },
846
+ { symbol: "❓", word: "Where?" },
847
+ { symbol: "❓", word: "When?" },
848
+ { symbol: "❓", word: "Why?" },
849
+ { symbol: "❓", word: "How?" },
850
+ { symbol: "❓", word: "Who?" },
851
+ { symbol: "❓", word: "Can I?" },
852
+ { symbol: "❓", word: "May I?" },
853
+ { symbol: "❓", word: "Could you?" },
854
+ { symbol: "❓", word: "Would you?" },
855
+ { symbol: "❓", word: "How much?" },
856
+ { symbol: "❓", word: "How many?" },
857
+ { symbol: "❓", word: "Which one?" },
858
+ { symbol: "❓", word: "What time?" },
859
+ { symbol: "❓", word: "What's that?" },
860
+ { symbol: "❓", word: "What's this?" }
861
+ ],
862
+ time: [
863
+ { symbol: "🕛", word: "Morning" },
864
+ { symbol: "🕛", word: "Afternoon" },
865
+ { symbol: "🕛", word: "Evening" },
866
+ { symbol: "🕛", word: "Night" },
867
+ { symbol: "📅", word: "Today" },
868
+ { symbol: "📅", word: "Tomorrow" },
869
+ { symbol: "📅", word: "Yesterday" },
870
+ { symbol: "📅", word: "Now" },
871
+ { symbol: "📅", word: "Later" },
872
+ { symbol: "📅", word: "Soon" },
873
+ { symbol: "📅", word: "Always" },
874
+ { symbol: "📅", word: "Never" },
875
+ { symbol: "📅", word: "Sometimes" },
876
+ { symbol: "📅", word: "Often" },
877
+ { symbol: "📅", word: "Week" },
878
+ { symbol: "📅", word: "Month" }
879
+ ]
880
+ };
881
+
882
+ // Quick Access Phrases
883
+ const quickAccessPhrases = {
884
+ home: [
885
+ { symbol: "🏠", word: "I want to go home" },
886
+ { symbol: "🛋️", word: "I'm tired" },
887
+ { symbol: "📺", word: "Can we watch TV?" },
888
+ { symbol: "🍽️", word: "Dinner time" }
889
+ ],
890
+ food: [
891
+ { symbol: "🍽️", word: "I'm hungry" },
892
+ { symbol: "🍎", word: "I want an apple" },
893
+ { symbol: "🍕", word: "I want pizza" },
894
+ { symbol: "🥤", word: "I'm thirsty" }
895
+ ],
896
+ bathroom: [
897
+ { symbol: "🚽", word: "I need the bathroom" },
898
+ { symbol: "🚿", word: "I need a shower" },
899
+ { symbol: "🛁", word: "I need a bath" },
900
+ { symbol: "🚰", word: "Wash hands" }
901
+ ],
902
+ sleep: [
903
+ { symbol: "🛏️", word: "I'm sleepy" },
904
+ { symbol: "🌙", word: "Good night" },
905
+ { symbol: "🛌", word: "Time for bed" },
906
+ { symbol: "😴", word: "I need a nap" }
907
+ ],
908
+ learn: [
909
+ { symbol: "📖", word: "Read me a story" },
910
+ { symbol: "✏️", word: "I want to draw" },
911
+ { symbol: "🎨", word: "Let's paint" },
912
+ { symbol: "🧩", word: "Let's play a game" }
913
+ ]
914
+ };
915
+
916
+ // Initialize speech synthesis
917
+ function initSpeechSynthesis() {
918
+ // Wait for voices to be loaded
919
+ speechSynthesis.onvoiceschanged = function() {
920
+ voices = speechSynthesis.getVoices();
921
+ populateVoiceList();
922
+ };
923
+
924
+ // Some browsers don't fire the voiceschanged event properly
925
+ // So we'll also check after a short delay
926
+ setTimeout(function() {
927
+ voices = speechSynthesis.getVoices();
928
+ if (voices.length > 0) {
929
+ populateVoiceList();
930
+ }
931
+ }, 1000);
932
+
933
+ // Set up rate and pitch sliders
934
+ rateSlider.addEventListener('input', function() {
935
+ speechRate = parseFloat(this.value);
936
+ updateRateValue();
937
+ });
938
+
939
+ pitchSlider.addEventListener('input', function() {
940
+ speechPitch = parseFloat(this.value);
941
+ updatePitchValue();
942
+ });
943
+
944
+ // Preview voice button
945
+ previewVoiceBtn.addEventListener('click', function() {
946
+ speakText("This is a preview of the selected voice.");
947
+ });
948
+
949
+ // Speak on select toggle
950
+ speakToggle.addEventListener('change', function() {
951
+ speakOnSelect = this.checked;
952
+ });
953
+ }
954
+
955
+ // Populate voice list
956
+ function populateVoiceList() {
957
+ voiceSelect.innerHTML = '';
958
+
959
+ // Filter for English voices (can be expanded for other languages)
960
+ const englishVoices = voices.filter(voice =>
961
+ voice.lang.includes('en') ||
962
+ voice.lang.includes('EN') ||
963
+ voice.lang.includes('Eng')
964
+ );
965
+
966
+ englishVoices.forEach(voice => {
967
+ const option = document.createElement('option');
968
+ option.textContent = `${voice.name} (${voice.lang})`;
969
+ option.setAttribute('data-voice', voice.name);
970
+ voiceSelect.appendChild(option);
971
+
972
+ // Set default voice (usually the first one)
973
+ if (!currentVoice) {
974
+ currentVoice = voice;
975
+ }
976
+ });
977
+
978
+ // Add event listener for voice selection
979
+ voiceSelect.addEventListener('change', function() {
980
+ const selectedVoiceName = this.options[this.selectedIndex].getAttribute('data-voice');
981
+ currentVoice = voices.find(voice => voice.name === selectedVoiceName);
982
+ });
983
+ }
984
+
985
+ // Update rate value display
986
+ function updateRateValue() {
987
+ let rateText = "Normal speed";
988
+ if (speechRate < 0.8) rateText = "Slow";
989
+ else if (speechRate > 1.2) rateText = "Fast";
990
+ rateValue.textContent = rateText;
991
+ }
992
+
993
+ // Update pitch value display
994
+ function updatePitchValue() {
995
+ let pitchText = "Normal pitch";
996
+ if (speechPitch < 0.8) pitchText = "Low";
997
+ else if (speechPitch > 1.2) pitchText = "High";
998
+ pitchValue.textContent = pitchText;
999
+ }
1000
+
1001
+ // Speak text using Web Speech API
1002
+ function speakText(text) {
1003
+ if (!currentVoice) {
1004
+ console.error("No voice selected");
1005
+ return;
1006
+ }
1007
+
1008
+ // Cancel any ongoing speech
1009
+ speechSynthesis.cancel();
1010
+
1011
+ // Create new utterance
1012
+ const utterance = new SpeechSynthesisUtterance(text);
1013
+ utterance.voice = currentVoice;
1014
+ utterance.rate = speechRate;
1015
+ utterance.pitch = speechPitch;
1016
+
1017
+ // Speak it
1018
+ speechSynthesis.speak(utterance);
1019
+ }
1020
+
1021
+ // Load words for a category
1022
+ function loadCategory(category) {
1023
+ currentCategory = category;
1024
+ wordGrid.innerHTML = '';
1025
+
1026
+ const words = wordDatabase[category];
1027
+ if (!words) return;
1028
+
1029
+ words.forEach(word => {
1030
+ const wordBtn = document.createElement('div');
1031
+ wordBtn.className = 'word-btn bg-white hover:bg-indigo-50 p-4 rounded-xl flex flex-col items-center justify-center cursor-pointer shadow-sm fade-in';
1032
+ wordBtn.innerHTML = `
1033
+ <div class="text-3xl mb-2">${word.symbol}</div>
1034
+ <span class="text-sm font-medium text-center text-gray-700">${word.word}</span>
1035
+ `;
1036
+
1037
+ wordBtn.addEventListener('click', () => {
1038
+ addToSentence(word.word);
1039
+ if (speakOnSelect) {
1040
+ speakText(word.word);
1041
+ }
1042
+ });
1043
+
1044
+ wordBtn.addEventListener('mousedown', () => {
1045
+ wordBtn.classList.add('bg-indigo-100');
1046
+ });
1047
+
1048
+ wordBtn.addEventListener('mouseup', () => {
1049
+ wordBtn.classList.remove('bg-indigo-100');
1050
+ });
1051
+
1052
+ wordBtn.addEventListener('mouseleave', () => {
1053
+ wordBtn.classList.remove('bg-indigo-100');
1054
+ });
1055
+
1056
+ wordGrid.appendChild(wordBtn);
1057
+ });
1058
+ }
1059
+
1060
+ // Load quick access phrases
1061
+ function loadQuickAccess(category) {
1062
+ wordGrid.innerHTML = '';
1063
+
1064
+ const phrases = quickAccessPhrases[category];
1065
+ if (!phrases) return;
1066
+
1067
+ phrases.forEach(phrase => {
1068
+ const phraseBtn = document.createElement('div');
1069
+ phraseBtn.className = 'word-btn bg-white hover:bg-indigo-50 p-4 rounded-xl flex flex-col items-center justify-center cursor-pointer shadow-sm fade-in';
1070
+ phraseBtn.innerHTML = `
1071
+ <div class="text-3xl mb-2">${phrase.symbol}</div>
1072
+ <span class="text-sm font-medium text-center text-gray-700">${phrase.word}</span>
1073
+ `;
1074
+
1075
+ phraseBtn.addEventListener('click', () => {
1076
+ addToSentence(phrase.word);
1077
+ if (speakOnSelect) {
1078
+ speakText(phrase.word);
1079
+ }
1080
+ });
1081
+
1082
+ wordGrid.appendChild(phraseBtn);
1083
+ });
1084
+ }
1085
+
1086
+ // Update grid layout based on selected size
1087
+ function updateGridLayout() {
1088
+ const gridSize = gridSizeSelect.value;
1089
+ wordGrid.className = `word-grid mb-6 flex-1 overflow-y-auto grid grid-cols-${gridSize} gap-3`;
1090
+
1091
+ // Reload current category to apply changes
1092
+ if (currentCategory) {
1093
+ loadCategory(currentCategory);
1094
+ }
1095
+ }
1096
+
1097
+ // Update text size
1098
+ function updateTextSize() {
1099
+ const size = textSizeSelect.value;
1100
+ let textSize = 'text-sm';
1101
+
1102
+ switch(size) {
1103
+ case 'small':
1104
+ textSize = 'text-xs';
1105
+ break;
1106
+ case 'medium':
1107
+ textSize = 'text-sm';
1108
+ break;
1109
+ case 'large':
1110
+ textSize = 'text-base';
1111
+ break;
1112
+ case 'xlarge':
1113
+ textSize = 'text-lg';
1114
+ break;
1115
+ }
1116
+
1117
+ // Update all word buttons
1118
+ const wordSpans = document.querySelectorAll('.word-btn span');
1119
+ wordSpans.forEach(span => {
1120
+ span.className = `${textSize} font-medium text-center text-gray-700`;
1121
+ });
1122
+ }
1123
+
1124
+ // Toggle dark mode
1125
+ function toggleDarkMode() {
1126
+ if (darkModeToggle.checked) {
1127
+ document.documentElement.classList.add('dark');
1128
+ document.body.classList.add('bg-gray-900');
1129
+ document.body.classList.remove('bg-gray-50');
1130
+ } else {
1131
+ document.documentElement.classList.remove('dark');
1132
+ document.body.classList.remove('bg-gray-900');
1133
+ document.body.classList.add('bg-gray-50');
1134
+ }
1135
+ }
1136
+
1137
+ // Handle user login
1138
+ function handleLogin() {
1139
+ const email = loginEmail.value.trim();
1140
+ const password = loginPassword.value.trim();
1141
+
1142
+ // Reset error states
1143
+ loginEmail.classList.remove('input-error');
1144
+ loginPassword.classList.remove('input-error');
1145
+ loginEmailError.style.display = 'none';
1146
+ loginPasswordError.style.display = 'none';
1147
+
1148
+ // Simple validation
1149
+ let isValid = true;
1150
+
1151
+ if (!email) {
1152
+ loginEmail.classList.add('input-error');
1153
+ loginEmailError.style.display = 'block';
1154
+ isValid = false;
1155
+ } else if (!/^\S+@\S+\.\S+$/.test(email)) {
1156
+ loginEmail.classList.add('input-error');
1157
+ loginEmailError.textContent = 'Please enter a valid email';
1158
+ loginEmailError.style.display = 'block';
1159
+ isValid = false;
1160
+ }
1161
+
1162
+ if (!password) {
1163
+ loginPassword.classList.add('input-error');
1164
+ loginPasswordError.style.display = 'block';
1165
+ isValid = false;
1166
+ }
1167
+
1168
+ if (!isValid) return;
1169
+
1170
+ // In a real app, you would make an API call here
1171
+ // For demo purposes, we'll just simulate a successful login
1172
+ currentUser = {
1173
+ firstName: "John",
1174
+ lastName: "Doe",
1175
+ email: email,
1176
+ age: 12,
1177
+ language: "English"
1178
+ };
1179
+
1180
+ // Update UI
1181
+ updateUserUI();
1182
+
1183
+ // Close modal
1184
+ authModal.classList.add('hidden');
1185
+
1186
+ // Show welcome message
1187
+ showWelcomeMessage(currentUser.firstName);
1188
+ }
1189
+
1190
+ // Handle user signup
1191
+ function handleSignup() {
1192
+ const firstName = signupFirstname.value.trim();
1193
+ const lastName = signupLastname.value.trim();
1194
+ const email = signupEmail.value.trim();
1195
+ const password = signupPassword.value.trim();
1196
+ const confirmPassword = signupConfirmPassword.value.trim();
1197
+
1198
+ // Reset error states
1199
+ signupFirstname.classList.remove('input-error');
1200
+ signupLastname.classList.remove('input-error');
1201
+ signupEmail.classList.remove('input-error');
1202
+ signupPassword.classList.remove('input-error');
1203
+ signupConfirmPassword.classList.remove('input-error');
1204
+ signupFirstnameError.style.display = 'none';
1205
+ signupLastnameError.style.display = 'none';
1206
+ signupEmailError.style.display = 'none';
1207
+ signupPasswordError.style.display = 'none';
1208
+ signupConfirmError.style.display = 'none';
1209
+
1210
+ // Simple validation
1211
+ let isValid = true;
1212
+
1213
+ if (!firstName) {
1214
+ signupFirstname.classList.add('input-error');
1215
+ signupFirstnameError.style.display = 'block';
1216
+ isValid = false;
1217
+ }
1218
+
1219
+ if (!lastName) {
1220
+ signupLastname.classList.add('input-error');
1221
+ signupLastnameError.style.display = 'block';
1222
+ isValid = false;
1223
+ }
1224
+
1225
+ if (!email) {
1226
+ signupEmail.classList.add('input-error');
1227
+ signupEmailError.style.display = 'block';
1228
+ isValid = false;
1229
+ } else if (!/^\S+@\S+\.\S+$/.test(email)) {
1230
+ signupEmail.classList.add('input-error');
1231
+ signupEmailError.textContent = 'Please enter a valid email';
1232
+ signupEmailError.style.display = 'block';
1233
+ isValid = false;
1234
+ }
1235
+
1236
+ if (!password) {
1237
+ signupPassword.classList.add('input-error');
1238
+ signupPasswordError.style.display = 'block';
1239
+ isValid = false;
1240
+ } else if (password.length < 6) {
1241
+ signupPassword.classList.add('input-error');
1242
+ signupPasswordError.textContent = 'Password must be at least 6 characters';
1243
+ signupPasswordError.style.display = 'block';
1244
+ isValid = false;
1245
+ }
1246
+
1247
+ if (!confirmPassword) {
1248
+ signupConfirmPassword.classList.add('input-error');
1249
+ signupConfirmError.style.display = 'block';
1250
+ isValid = false;
1251
+ } else if (password !== confirmPassword) {
1252
+ signupConfirmPassword.classList.add('input-error');
1253
+ signupConfirmError.textContent = 'Passwords must match';
1254
+ signupConfirmError.style.display = 'block';
1255
+ isValid = false;
1256
+ }
1257
+
1258
+ if (!isValid) return;
1259
+
1260
+ // In a real app, you would make an API call here
1261
+ // For demo purposes, we'll just simulate a successful signup
1262
+ currentUser = {
1263
+ firstName: firstName,
1264
+ lastName: lastName,
1265
+ email: email,
1266
+ age: 12, // Default age
1267
+ language: "English" // Default language
1268
+ };
1269
+
1270
+ // Update UI
1271
+ updateUserUI();
1272
+
1273
+ // Close modal
1274
+ authModal.classList.add('hidden');
1275
+
1276
+ // Show welcome message
1277
+ showWelcomeMessage(currentUser.firstName);
1278
+ }
1279
+
1280
+ // Update user UI after login/signup
1281
+ function updateUserUI() {
1282
+ if (currentUser) {
1283
+ // Change auth button to logout
1284
+ authBtn.innerHTML = '<i class="fas fa-sign-out-alt mr-2"></i> Sign Out';
1285
+ authBtn.removeEventListener('click', showAuthModal);
1286
+ authBtn.addEventListener('click', handleLogout);
1287
+
1288
+ // Show user button
1289
+ userBtn.classList.remove('hidden');
1290
+
1291
+ // Update profile info
1292
+ profileName.textContent = `${currentUser.firstName} ${currentUser.lastName}`;
1293
+ profileNameInput.value = `${currentUser.firstName} ${currentUser.lastName}`;
1294
+ profileAge.value = currentUser.age || 12;
1295
+ profileLanguage.value = currentUser.language || "English";
1296
+ } else {
1297
+ // Change back to login button
1298
+ authBtn.innerHTML = '<i class="fas fa-sign-in-alt mr-2"></i> Sign In';
1299
+ authBtn.removeEventListener('click', handleLogout);
1300
+ authBtn.addEventListener('click', showAuthModal);
1301
+
1302
+ // Hide user button
1303
+ userBtn.classList.add('hidden');
1304
+
1305
+ // Hide welcome message
1306
+ welcomeMessage.classList.add('hidden');
1307
+ }
1308
+ }
1309
+
1310
+ // Handle user logout
1311
+ function handleLogout() {
1312
+ currentUser = null;
1313
+ updateUserUI();
1314
+ }
1315
+
1316
+ // Show welcome message with user's first name
1317
+ function showWelcomeMessage(firstName) {
1318
+ welcomeMessage.classList.remove('hidden');
1319
+ userGreeting.textContent = `Hello ${firstName}!`;
1320
+
1321
+ // Hide after 5 seconds
1322
+ setTimeout(() => {
1323
+ welcomeMessage.classList.add('hidden');
1324
+ }, 5000);
1325
+ }
1326
+
1327
+ // Show auth modal with login form
1328
+ function showAuthModal() {
1329
+ authModal.classList.remove('hidden');
1330
+ loginForm.classList.remove('hidden');
1331
+ signupForm.classList.add('hidden');
1332
+ loginTab.classList.add('active');
1333
+ signupTab.classList.remove('active');
1334
+
1335
+ // Reset form fields
1336
+ loginEmail.value = '';
1337
+ loginPassword.value = '';
1338
+ signupFirstname.value = '';
1339
+ signupLastname.value = '';
1340
+ signupEmail.value = '';
1341
+ signupPassword.value = '';
1342
+ signupConfirmPassword.value = '';
1343
+
1344
+ // Reset error states
1345
+ loginEmail.classList.remove('input-error');
1346
+ loginPassword.classList.remove('input-error');
1347
+ signupFirstname.classList.remove('input-error');
1348
+ signupLastname.classList.remove('input-error');
1349
+ signupEmail.classList.remove('input-error');
1350
+ signupPassword.classList.remove('input-error');
1351
+ signupConfirmPassword.classList.remove('input-error');
1352
+
1353
+ loginEmailError.style.display = 'none';
1354
+ loginPasswordError.style.display = 'none';
1355
+ signupFirstnameError.style.display = 'none';
1356
+ signupLastnameError.style.display = 'none';
1357
+ signupEmailError.style.display = 'none';
1358
+ signupPasswordError.style.display = 'none';
1359
+ signupConfirmError.style.display = 'none';
1360
+ }
1361
+
1362
+ // Event Listeners
1363
+ settingsBtn.addEventListener('click', () => settingsModal.classList.remove('hidden'));
1364
+ userBtn.addEventListener('click', () => {
1365
+ // Load saved phrases
1366
+ renderSavedPhrases();
1367
+ userModal.classList.remove('hidden');
1368
+ });
1369
+ savePhraseBtn.addEventListener('click', () => {
1370
+ // Update the phrase to save with current sentence
1371
+ const sentence = getCurrentSentence();
1372
+ if (sentence.trim()) {
1373
+ phraseToSave.textContent = sentence;
1374
+ phraseName.value = '';
1375
+ savePhraseModal.classList.remove('hidden');
1376
+ }
1377
+ });
1378
+
1379
+ clearBtn.addEventListener('click', clearSentence);
1380
+ speakBtn.addEventListener('click', speakCurrentSentence);
1381
+
1382
+ cancelSettings.addEventListener('click', () => settingsModal.classList.add('hidden'));
1383
+ saveSettings.addEventListener('click', () => {
1384
+ // Save settings
1385
+ updateGridLayout();
1386
+ updateTextSize();
1387
+ toggleDarkMode();
1388
+ speakOnSelect = speakToggle.checked;
1389
+ settingsModal.classList.add('hidden');
1390
+ });
1391
+
1392
+ cancelUser.addEventListener('click', () => userModal.classList.add('hidden'));
1393
+ saveUser.addEventListener('click', () => {
1394
+ // Update user info
1395
+ if (currentUser) {
1396
+ const nameParts = profileNameInput.value.split(' ');
1397
+ currentUser.firstName = nameParts[0] || 'User';
1398
+ currentUser.lastName = nameParts.slice(1).join(' ') || '';
1399
+ currentUser.age = parseInt(profileAge.value) || 12;
1400
+ currentUser.language = profileLanguage.value || "English";
1401
+
1402
+ // Update greeting
1403
+ showWelcomeMessage(currentUser.firstName);
1404
+ }
1405
+ userModal.classList.add('hidden');
1406
+ });
1407
+
1408
+ cancelSavePhrase.addEventListener('click', () => savePhraseModal.classList.add('hidden'));
1409
+ confirmSavePhrase.addEventListener('click', () => {
1410
+ const phraseText = phraseToSave.textContent;
1411
+ const phraseNameValue = phraseName.value || phraseText;
1412
+ const category = phraseCategory.value;
1413
+
1414
+ // Save the phrase
1415
+ savedPhrases.push({
1416
+ text: phraseText,
1417
+ name: phraseNameValue,
1418
+ category: category
1419
+ });
1420
+
1421
+ savePhraseModal.classList.add('hidden');
1422
+ renderSavedPhrases();
1423
+ });
1424
+
1425
+ // Auth modal tabs
1426
+ loginTab.addEventListener('click', () => {
1427
+ loginForm.classList.remove('hidden');
1428
+ signupForm.classList.add('hidden');
1429
+ loginTab.classList.add('active');
1430
+ signupTab.classList.remove('active');
1431
+ });
1432
+
1433
+ signupTab.addEventListener('click', () => {
1434
+ loginForm.classList.add('hidden');
1435
+ signupForm.classList.remove('hidden');
1436
+ loginTab.classList.remove('active');
1437
+ signupTab.classList.add('active');
1438
+ });
1439
+
1440
+ switchToSignup.addEventListener('click', () => {
1441
+ loginForm.classList.add('hidden');
1442
+ signupForm.classList.remove('hidden');
1443
+ loginTab.classList.remove('active');
1444
+ signupTab.classList.add('active');
1445
+ });
1446
+
1447
+ switchToLogin.addEventListener('click', () => {
1448
+ loginForm.classList.remove('hidden');
1449
+ signupForm.classList.add('hidden');
1450
+ loginTab.classList.add('active');
1451
+ signupTab.classList.remove('active');
1452
+ });
1453
+
1454
+ // Auth form submissions
1455
+ loginBtn.addEventListener('click', handleLogin);
1456
+ signupBtn.addEventListener('click', handleSignup);
1457
+
1458
+ // Category buttons
1459
+ categoryBtns.forEach(btn => {
1460
+ btn.addEventListener('click', () => {
1461
+ const category = btn.dataset.category;
1462
+ categoryBtns.forEach(b => b.classList.remove('active', 'bg-indigo-600', 'text-white'));
1463
+ btn.classList.add('active', 'bg-indigo-600', 'text-white');
1464
+
1465
+ loadCategory(category);
1466
+ });
1467
+ });
1468
+
1469
+ // Quick access buttons
1470
+ quickAccessBtns.forEach(btn => {
1471
+ btn.addEventListener('click', () => {
1472
+ const category = btn.dataset.quick;
1473
+ loadQuickAccess(category);
1474
+
1475
+ // Highlight the quick access button
1476
+ quickAccessBtns.forEach(b => b.classList.remove('bg-indigo-600', 'text-white'));
1477
+ btn.classList.add('bg-indigo-600', 'text-white');
1478
+
1479
+ // Reset category buttons
1480
+ categoryBtns.forEach(b => b.classList.remove('active', 'bg-indigo-600', 'text-white'));
1481
+ });
1482
+ });
1483
+
1484
+ // Close modals when clicking outside
1485
+ [settingsModal, userModal, savePhraseModal, authModal].forEach(modal => {
1486
+ modal.addEventListener('click', (e) => {
1487
+ if (e.target === modal) {
1488
+ modal.classList.add('hidden');
1489
+ }
1490
+ });
1491
+ });
1492
+
1493
+ // Auth button
1494
+ authBtn.addEventListener('click', showAuthModal);
1495
+
1496
+ // Render saved phrases in user modal
1497
+ function renderSavedPhrases() {
1498
+ savedPhrasesList.innerHTML = '';
1499
+
1500
+ savedPhrases.forEach((phrase, index) => {
1501
+ const phraseItem = document.createElement('div');
1502
+ phraseItem.className = 'bg-white p-3 rounded-lg flex justify-between items-center shadow-sm mb-2 last:mb-0';
1503
+ phraseItem.innerHTML = `
1504
+ <span class="text-gray-700">${phrase.text}</span>
1505
+ <div>
1506
+ <button class="text-indigo-600 hover:text-indigo-800 mr-2 play-phrase" data-index="${index}">
1507
+ <i class="fas fa-play"></i>
1508
+ </button>
1509
+ <button class="text-red-600 hover:text-red-800 delete-phrase" data-index="${index}">
1510
+ <i class="fas fa-trash"></i>
1511
+ </button>
1512
+ </div>
1513
+ `;
1514
+
1515
+ savedPhrasesList.appendChild(phraseItem);
1516
+ });
1517
+
1518
+ // Add event listeners to play buttons
1519
+ document.querySelectorAll('.play-phrase').forEach(btn => {
1520
+ btn.addEventListener('click', function() {
1521
+ const index = this.dataset.index;
1522
+ speakText(savedPhrases[index].text);
1523
+ });
1524
+ });
1525
+
1526
+ // Add event listeners to delete buttons
1527
+ document.querySelectorAll('.delete-phrase').forEach(btn => {
1528
+ btn.addEventListener('click', function() {
1529
+ const index = this.dataset.index;
1530
+ savedPhrases.splice(index, 1);
1531
+ renderSavedPhrases();
1532
+ });
1533
+ });
1534
+ }
1535
+
1536
+ // Functions
1537
+ function clearSentence() {
1538
+ const sentenceBar = document.getElementById('sentence-bar');
1539
+ sentenceBar.innerHTML = '<div class="flex space-x-2"></div>';
1540
+ }
1541
+
1542
+ function getCurrentSentence() {
1543
+ return Array.from(document.querySelectorAll('#sentence-bar span'))
1544
+ .map(span => span.textContent)
1545
+ .join(' ');
1546
+ }
1547
+
1548
+ function speakCurrentSentence() {
1549
+ const sentence = getCurrentSentence();
1550
+
1551
+ if (sentence.trim()) {
1552
+ // Add to message display
1553
+ const messageDisplay = document.getElementById('message-display');
1554
+
1555
+ // Clear empty message if it exists
1556
+ if (messageDisplay.querySelector('.empty-message')) {
1557
+ messageDisplay.innerHTML = '';
1558
+ }
1559
+
1560
+ const bubble = document.createElement('div');
1561
+ bubble.className = 'speech-bubble self-start fade-in';
1562
+ bubble.innerHTML = `<p>${sentence}</p>`;
1563
+ messageDisplay.appendChild(bubble);
1564
+
1565
+ // Scroll to bottom
1566
+ messageDisplay.scrollTop = messageDisplay.scrollHeight;
1567
+
1568
+ // Speak the sentence
1569
+ speakText(sentence);
1570
+ }
1571
+ }
1572
+
1573
+ function addToSentence(word) {
1574
+ const sentenceBar = document.querySelector('#sentence-bar > div');
1575
+ const wordElement = document.createElement('span');
1576
+ wordElement.className = 'bg-indigo-100 text-indigo-800 px-3 py-1 rounded-full flex items-center shadow-sm fade-in';
1577
+ wordElement.innerHTML = `
1578
+ ${word}
1579
+ <button class="ml-2 text-indigo-500 hover:text-indigo-700">
1580
+ <i class="fas fa-times"></i>
1581
+ </button>
1582
+ `;
1583
+
1584
+ // Add click handler to remove button
1585
+ const removeBtn = wordElement.querySelector('button');
1586
+ removeBtn.addEventListener('click', () => {
1587
+ wordElement.remove();
1588
+ });
1589
+
1590
+ sentenceBar.appendChild(wordElement);
1591
+
1592
+ // Scroll to end
1593
+ sentenceBar.parentElement.scrollLeft = sentenceBar.parentElement.scrollWidth;
1594
+ }
1595
+
1596
+ // Initialize the app
1597
+ initSpeechSynthesis();
1598
+ loadCategory('core');
1599
+ updateUserUI();
1600
+
1601
+ // Set up event listeners for settings
1602
+ gridSizeSelect.addEventListener('change', updateGridLayout);
1603
+ textSizeSelect.addEventListener('change', updateTextSize);
1604
+ darkModeToggle.addEventListener('change', toggleDarkMode);
1605
+ });
1606
+ </script>
1607
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=theaimoron/aac-app-for-web" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
1608
+ </html>