afaceuser09 commited on
Commit
4af7e95
·
verified ·
1 Parent(s): 8f58604

Update static/index.html

Browse files
Files changed (1) hide show
  1. static/index.html +1967 -596
static/index.html CHANGED
@@ -1,625 +1,1996 @@
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" />
6
- <title>Infinity GRM</title>
7
- <link rel="stylesheet"
8
- href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css">
9
-
10
- <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
11
-
12
- <script src="https://cdn.jsdelivr.net/npm/marked-katex-extension/lib/index.umd.js"></script>
13
-
14
- <script src="https://cdn.jsdelivr.net/npm/dompurify@3.0.8/dist/purify.min.js"></script>
15
- <script src="https://cdn.tailwindcss.com"></script>
16
- <style>
17
- body {
18
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
19
- Arial, sans-serif;
20
- transition: all 0.5s ease-in-out;
21
- }
22
- .chat-content-scroll::-webkit-scrollbar {
23
- width: 8px;
24
- }
25
- .chat-content-scroll::-webkit-scrollbar-track {
26
- background: #ffffff;
27
- }
28
- .chat-content-scroll::-webkit-scrollbar-thumb {
29
- background: #e0e0e0;
30
- border-radius: 4px;
31
- }
32
- .input-shadow {
33
- box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.07), 0 2px 4px 0 rgba(0, 0, 0, 0.07);
34
- }
35
- .send-btn-active {
36
- background-color: #000000;
37
- color: #fff;
38
- }
39
- .drop-zone.drag-over {
40
- background-color: #f0f4f8;
41
- border-color: #1a73e8;
42
- }
43
- #chat-input-div {
44
- max-height: 96px;
45
- overflow-y: auto;
46
- min-height: 96px;
47
- padding: 12px 16px;
48
- }
49
- #chat-input-div img {
50
- max-width: 100%;
51
- height: auto;
52
- border-radius: 8px;
53
- margin-top: 8px;
54
- }
55
- [contentEditable=true]:empty:before {
56
- content: attr(data-placeholder);
57
- color: #9ca3af;
58
- pointer-events: none;
59
- display: block;
60
- }
61
- #messages {
62
- height: calc(100vh - 56px);
63
- overflow-y: auto;
64
- }
65
- /* Voice button listening state */
66
- #voice-btn.listening {
67
- color: #000000 !important;
68
- background-color: #e0e0e0 !important;
69
- }
70
- /* Siri pulse animation */
71
- @keyframes siri-pulse {
72
- 0%, 100% {
73
- transform: scale(1);
74
- filter: drop-shadow(0 0 3px rgb(120, 160, 255));
75
- opacity: 0.9;
76
- }
77
- 50% {
78
- transform: scale(1.1);
79
- filter: drop-shadow(0 0 12px rgb(120, 160, 255));
80
- opacity: 1;
81
- }
82
- }
83
- .siri-animate {
84
- animation: siri-pulse 1.5s ease-in-out infinite;
85
- opacity: 1 !important;
86
- }
87
- /* Image positioned inside input bar */
88
- #siri-anim-image {
89
- transition: opacity 0.3s ease;
90
- opacity: 0;
91
- pointer-events: none;
92
- }
93
- /* Plus and switch chat buttons container */
94
- .chat-control-buttons {
95
- position: fixed;
96
- bottom: 1rem;
97
- left: 1rem;
98
- display: flex;
99
- gap: 0.5rem;
100
- z-index: 60;
101
- }
102
- .ai-response{
103
- max-width:900px;
104
- margin:40px auto;
105
- background:white;
106
- padding:40px;
107
- border-radius:0;
108
- box-shadow:none;
109
- line-height:1.8;
110
- font-size:16px;
111
- }
112
- .ai-response p{
113
- margin:1em 0;
114
- }
115
- .ai-response ul,
116
- .ai-response ol{
117
- margin:1em 0;
118
- padding-left:2rem;
119
- }
120
- .ai-response li{
121
- margin:.5em 0;
122
- }
123
- .ai-response pre{
124
- overflow:auto;
125
- padding:16px;
126
- border-radius:10px;
127
- background:#111827;
128
- color:white;
129
- }
130
- /* Gradio-style Clean Code Blocks */
131
- .ai-response code {
132
- font-family: Consolas, Montserrat, monospace;
133
- font-size: 14px;
134
- line-height: 1.5;
135
- display: block;
136
- /* Gradio Color Palette Variables */
137
- background-color: #F1F3F5; /* Soft gray background (neutral-50) */
138
- color: #000000; /* Dark charcoal text color (neutral-700) */
139
- /* Borders and Edges */
140
- border: 1px solid #e5e7eb; /* Soft border color (neutral-200) */
141
- border-radius: 8px; /* Clean rounded panel corners */
142
- /* Sizing and Real Estate */
143
- padding: 12px 16px; /* Precise internal spatial layout */
144
- margin: 0; /* Ensures the code drops completely flush to its frame */
145
- overflow-x: auto; /* Clean horizontal scrollbar mechanism */
146
- width: 100%;
147
- box-sizing: border-box;
148
- }
149
- /* Force-neutralize the default <pre> block settings wrapper */
150
- pre, .ai-response-container {
151
- background: transparent !important;
152
- padding: 0 !important;
153
- margin: 8px 0 !important;
154
- border: none !important;
155
- box-shadow: none !important;
156
- }
157
- .ai-response table{
158
- border-collapse:collapse;
159
- }
160
- .ai-response th,
161
- .ai-response td{
162
- border:1px solid #ddd;
163
- padding:8px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  }
165
  </style>
166
  </head>
 
 
167
  <body>
168
- <!-- Top Bar -->
169
- <nav
170
- class="fixed top-0 left-0 right-0 bg-white flex items-center justify-between px-1 h-14 z-50"
171
- >
172
- <div>
173
- <img
174
- src="https://copilot.microsoft.com/th/id/BCO.cc808577-1db9-4523-90a6-be7b2abee261.png"
175
- alt="Logo"
176
- class="h-20 w-auto object-contain"
177
- />
178
- </div>
179
- <div class="flex space-x-3">
180
- <a
181
- href="login.html"
182
- class="bg-black text-white px-4 py-2 rounded-full border-2 border-black hover:bg-black transition-colors duration-200 inline-flex items-center justify-center"
183
- >
184
- Log In
185
- </a>
186
- <!-- Stripe Subscription Navbar Trigger Element -->
187
- <button id="checkout-button" class="bg-white text-gray-800 px-4 py-2 rounded-full border-2 border-gray-200 hover:bg-gray-100 transition-colors duration-200 inline-flex items-center justify-center font-medium cursor-pointer">
188
- Sign Up For Free
189
- </button>
190
- </div>
191
- </nav>
192
- <!-- CHAT PAGE -->
193
- <div
194
- id="chat-page"
195
- class="w-full flex flex-col justify-center items-center h-screen pt-14 overflow-hidden transition-all duration-500 hidden"
196
- data-state="initial"
197
- >
198
- <!-- Main Chat Area -->
199
- <div
200
- id="messages"
201
- class="hidden flex-grow overflow-y-auto px-4 sm:px-16 md:px-24 lg:px-48 py-8 chat-content-scroll w-full max-w-7xl mx-auto"
202
  >
203
- <div class="w-full max-w-6xl mx-auto space-y-8">
204
- <!-- Messages populated here -->
205
- </div>
206
- </div>
207
- <!-- Input Area -->
208
- <div id="input-area" class="w-full p-4 transition-all duration-500 max-w-3xl">
209
- <div class="flex flex-col items-center">
210
- <h1
211
- id="infinity-ai-text"
212
- class="text-3xl font-semibold mb-6 transition-opacity duration-300"
213
  >
214
- Infinity GRM
215
- </h1>
216
- <div
217
- id="drop-zone"
218
- class="relative flex items-end bg-white rounded-xl border border-gray-300 input-shadow w-full max-w-xl transition-colors duration-200"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  >
220
- <img
221
- id="siri-anim-image"
222
- src="https://copilot.microsoft.com/th/id/BCO.e89797dc-19f9-4b01-b660-67d68d16fd2f.png"
223
- alt="Siri Animation"
224
- class="absolute bottom-2 left-4 w-10 h-10 object-contain"
225
- />
226
- <div
227
- id="chat-input-div"
228
- contentEditable="true"
229
- data-placeholder="Ask me anything..."
230
- class="flex-grow p-3 pl-16 pr-20 bg-transparent text-gray-800 focus:outline-none resize-none overflow-y-auto rounded-xl"
231
- ></div>
232
- <button
233
- id="voice-btn"
234
- class="absolute right-12 bottom-2 w-8 h-8 rounded-full flex items-center justify-center text-gray-500 hover:bg-gray-200 p-2 transition-colors duration-200"
235
- title="Voice Input"
236
- >
237
- <svg
238
- xmlns="http://www.w3.org/2000/svg"
239
- fill="none"
240
- viewBox="0 0 24 24"
241
- stroke-width="1.5"
242
- stroke="currentColor"
243
- class="w-5 h-5"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  >
245
- <path
246
- stroke-linecap="round"
247
- stroke-linejoin="round"
248
- d="M12 1.5a3 3 0 00-3 3v7a3 3 0 006 0v-7a3 3 0 00-3-3z"
249
- />
250
- <path
251
- stroke-linecap="round"
252
- stroke-linejoin="round"
253
- d="M19.5 10.5v1.5a6 6 0 01-12 0v-1.5"
254
- />
255
- <path stroke-linecap="round" stroke-linejoin="round" d="M12 19.5v3" />
256
- </svg>
257
- <button
258
- id="send-btn"
259
- class="absolute right-2 bottom-2 w-8 h-8 rounded-full flex items-center justify-center text-gray-500 hover:bg-gray-200 p-2 transition-colors duration-200"
260
- >
261
- <svg
262
- xmlns="http://www.w3.org/2000/svg"
263
- fill="none"
264
- viewBox="0 0 24 24"
265
- stroke-width="1.5"
266
- stroke="currentColor"
267
- class="w-5 h-5"
268
- >
269
- <path
270
- stroke-linecap="round"
271
- stroke-linejoin="round"
272
- d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5"
273
- />
274
- </svg>
275
- </button>
276
- </div>
277
- <p class="text-center text-xs text-gray-400 mt-2">
278
- Infinity GRM can make mistakes. Check important info.
279
- </p>
280
- </div>
281
- </div>
282
- </div>
283
- <!-- LOGIN PAGE -->
284
- <div id="login-page" class="flex items-center justify-center h-screen hidden">
285
- <div class="bg-white p-8 rounded-xl input-shadow w-full max-w-sm">
286
- <h1 class="text-3xl font-semibold text-center mb-6">Log In</h1>
287
- <form id="login-form" action="#" method="POST" onsubmit="return false;">
288
- <div class="mb-4">
289
- <label for="login-email" class="block text-sm font-medium text-gray-700">Email Address</label>
290
- <input
291
- type="email"
292
- id="login-email"
293
- name="email"
294
- class="mt-1 block w-full px-3 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-1 focus:ring-black-500"
295
- required
296
- />
297
- </div>
298
- <div class="mb-6">
299
- <label for="login-password" class="block text-sm font-medium text-gray-700">Password</label>
300
- <input
301
- type="password"
302
- id="login-password"
303
- name="password"
304
- class="mt-1 block w-full px-3 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-1 focus:ring-blue-500"
305
- required
306
- />
307
- </div>
308
- <div>
309
- <button
310
- type="submit"
311
- class="w-full py-2 px-4 bg-black text-white rounded-full hover:bg-black transition-colors duration-200"
312
- >
313
- Log In
314
- </button>
315
  </div>
316
- </form>
317
- <p class="mt-6 text-center text-sm">
318
- Don't have an account?
319
- <button
320
- onclick="showPage('signup-page')"
321
- class="text-black hover:underline"
322
- >
323
- Sign Up For Free
324
- </button>
325
- </p>
326
- <p class="mt-2 text-center text-sm">
327
- <button
328
- onclick="showPage('chat-page')"
329
- class="text-black hover:underline"
330
  >
331
- Back to chat
332
- </button>
333
- </p>
334
- </div>
335
- </div>
336
- <!-- Chat Control Buttons -->
337
- <div class="chat-control-buttons">
338
- <button id="new-chat-btn"
339
- class="w-10 h-10 rounded-full bg-black text-white flex items-center justify-center shadow-lg hover:bg-gray-900 transition-colors"
340
- title="New Chat" aria-label="Create New Chat"
341
- >
342
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"
343
- class="w-6 h-6">
344
- <path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4" />
345
- </svg>
346
- </button>
347
- <button id="switch-chat-btn"
348
- class="w-10 h-10 rounded-full bg-gray-200 text-gray-700 flex items-center justify-center shadow-lg hover:bg-gray-300 transition-colors"
349
- title="Switch Chat" aria-label="Switch Chat"
350
  >
351
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"
352
- class="w-6 h-6">
353
- <path stroke-linecap="round" stroke-linejoin="round"
354
- d="M4 17v1a2 2 0 002 2h12a2 2 0 002-2v-1M4 7V6a2 2 0 012-2h12a2 2 0 012 2v1M8 10h8M8 14h8" />
355
- </svg>
356
- </button>
357
- </div>
358
- <!-- Switch Chat Modal -->
359
- <div id="chat-modal" class="hidden fixed inset-0 z-50 bg-black bg-opacity-40 flex justify-center items-center">
360
- <div class="bg-white p-6 rounded-xl shadow-lg w-80">
361
- <h3 class="font-semibold text-lg mb-3">Your Chats</h3>
362
- <ul id="chat-list" class="space-y-2"></ul>
363
- <!-- Updated class to 'rounded-full' for completely rounded sides -->
364
- <button id="close-modal" class="mt-4 px-4 py-1 rounded-full bg-black text-white hover:bg-black w-full">Close</button>
365
- </div>
366
- </div>
367
- <script>
368
- if (typeof markedKatex !== "undefined") {
369
- marked.use(markedKatex({
370
- throwOnError: false
371
- }));
372
- }
373
- document.addEventListener("DOMContentLoaded", () => {
374
- const chatPage = document.getElementById("chat-page");
375
- const chatInput = document.getElementById("chat-input-div");
376
- const sendBtn = document.getElementById("send-btn");
377
- const voiceBtn = document.getElementById("voice-btn");
378
- const messagesContainerWrapper = document.getElementById("messages");
379
- const messagesContainer = document.querySelector("#messages .max-w-3xl") || messagesContainerWrapper.querySelector("div");
380
- const infinityAiText = document.getElementById("infinity-ai-text");
381
- const dropZone = document.getElementById("drop-zone");
382
- const siriImage = document.getElementById("siri-anim-image");
383
- const newChatBtn = document.getElementById("new-chat-btn");
384
- const switchChatBtn = document.getElementById("switch-chat-btn");
385
- const chatModal = document.getElementById("chat-modal");
386
- const chatList = document.getElementById("chat-list");
387
- const closeModalBtn = document.getElementById("close-modal");
388
- // Chat management variables
389
- let chats = [];
390
- let currentChat = { id: Date.now(), messages: [] };
391
- const sessionId = "session_" + Math.random().toString(36).substring(2, 9);
392
- function showMessagesArea() {
393
- if (chatPage.getAttribute("data-state") === "initial") {
394
- chatPage.classList.remove("justify-center", "items-center");
395
- chatPage.classList.add("justify-between", "flex-col", "items-center");
396
- messagesContainerWrapper.classList.remove("hidden");
397
- chatPage.setAttribute("data-state", "expanded");
398
- }
399
- }
400
- function addMessage(content, sender) {
401
- const wrapper = document.createElement("div");
402
- if (sender === "user") {
403
- // User message stays as a bubble on the right
404
- wrapper.className = "flex justify-end";
405
- wrapper.innerHTML = `
406
- <div class="bg-gray-100 rounded-xl p-4 max-w-xl">
407
- ${content}
408
- </div>
409
- `;
410
- } else {
411
- // AI response is centered like a document
412
- wrapper.className = "w-full flex justify-center";
413
- let html = content;
414
- // If Markdown libraries are loaded, render Markdown + sanitize
415
- if (typeof marked !== "undefined" && typeof DOMPurify !== "undefined") {
416
- html = DOMPurify.sanitize(marked.parse(content));
417
- } else {
418
- // Fallback: preserve line breaks
419
- html = content.replace(/\n/g, "<br>");
420
- }
421
- wrapper.innerHTML = `
422
- <div class="ai-response">
423
- ${html}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
  </div>
425
- `;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
426
  }
427
- messagesContainer.appendChild(wrapper);
428
- messagesContainerWrapper.scrollTop = messagesContainerWrapper.scrollHeight;
429
- }
430
- function renderMessages() {
431
- messagesContainer.innerHTML = "";
432
- currentChat.messages.forEach(({ content, sender }) => addMessage(content, sender));
433
- messagesContainerWrapper.scrollTop = messagesContainerWrapper.scrollHeight;
434
- }
435
- // ✅ FIXED: always save chats (even empty ones)
436
- function saveCurrentChat() {
437
- const existingIndex = chats.findIndex(c => c.id === currentChat.id);
438
- if (existingIndex > -1) {
439
- chats[existingIndex] = { ...currentChat };
440
- } else {
441
- chats.push({ ...currentChat });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
  }
443
- }
444
- function createNewChat() {
445
- saveCurrentChat();
446
- currentChat = { id: Date.now(), messages: [] };
447
- // ✅ ensure new chat is immediately logged
448
- chats.push({ ...currentChat });
449
- renderMessages();
450
- infinityAiText.style.opacity = '1';
451
- chatPage.classList.remove("justify-between", "flex-col", "items-center");
452
- chatPage.classList.add("justify-center", "items-center");
453
- messagesContainerWrapper.classList.add("hidden");
454
- chatPage.setAttribute("data-state", "initial");
455
- }
456
- async function sendMessage() {
457
- const userMessageContent = chatInput.innerHTML.trim();
458
- if (userMessageContent.length === 0) return;
459
- showMessagesArea();
460
- currentChat.messages.push({ content: userMessageContent, sender: "user" });
461
- renderMessages();
462
- chatInput.innerHTML = "";
463
- updateSendButton();
464
- infinityAiText.style.opacity = "0";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
  try {
466
- // Change line 813 to call your secure server script instead:
467
- const response = await fetch("/ai-bridge.php", {
468
- method: "POST",
469
- headers: {
470
- "Content-Type": "application/json"
471
- },
472
- body: JSON.stringify({
473
- message: userMessageContent,
474
- session_id: sessionId
475
- })
476
- });
477
- const data = await response.json();
478
- if (data.response) {
479
- currentChat.messages.push({
480
- content: data.response,
481
- sender: "ai"
482
- });
483
- } else if (data.error) {
484
- currentChat.messages.push({
485
- content: "Error: " + data.error,
486
- sender: "ai"
487
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
  }
489
- } catch (err) {
490
- currentChat.messages.push({
491
- content: "Failed to connect to the server.",
492
- sender: "ai"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
493
  });
494
  }
495
- renderMessages();
496
- }
497
- function updateSendButton() {
498
- const hasText = chatInput.innerText.trim().length > 0;
499
- const hasImages = chatInput.querySelector("img");
500
- if (hasText || hasImages) {
501
- sendBtn.classList.add("send-btn-active");
502
- sendBtn.classList.remove("text-gray-500", "hover:bg-gray-200");
503
- } else {
504
- sendBtn.classList.remove("send-btn-active");
505
- sendBtn.classList.add("text-gray-500", "hover:bg-gray-200");
 
 
 
 
506
  }
507
- }
508
- dropZone.addEventListener("dragover", e => {
509
- e.preventDefault();
510
- dropZone.classList.add("drag-over");
511
- });
512
- dropZone.addEventListener("dragleave", () => {
513
- dropZone.classList.remove("drag-over");
514
- });
515
- dropZone.addEventListener("drop", e => {
516
- e.preventDefault();
517
- dropZone.classList.remove("drag-over");
518
- const files = e.dataTransfer.files;
519
- if (files.length > 0) {
520
- for (const file of files) {
521
- if (file.type.startsWith("image/")) {
522
- const reader = new FileReader();
523
- reader.onload = ev => {
524
- const img = document.createElement("img");
525
- img.src = ev.target.result;
526
- chatInput.appendChild(img);
527
- updateSendButton();
528
- };
529
- reader.readAsDataURL(file);
530
- }
531
- }
532
  }
533
- });
534
- sendBtn.addEventListener("click", sendMessage);
535
- chatInput.addEventListener("input", updateSendButton);
536
- chatInput.addEventListener("keydown", e => {
537
- if (e.key === "Enter" && !e.shiftKey) {
538
- e.preventDefault();
539
- if (sendBtn.classList.contains("send-btn-active")) sendMessage();
 
 
 
 
 
 
 
 
 
540
  }
541
- });
542
- const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
543
- if (!SpeechRecognition) {
544
- voiceBtn.style.display = "none";
545
- } else {
546
- const recognition = new SpeechRecognition();
547
- recognition.lang = "en-US";
548
- voiceBtn.addEventListener("click", () => {
549
- if (voiceBtn.classList.contains("listening")) {
550
- recognition.stop();
551
- voiceBtn.classList.remove("listening");
552
- siriImage.classList.remove("siri-animate");
553
- } else {
554
- recognition.start();
555
- voiceBtn.classList.add("listening");
556
- siriImage.classList.add("siri-animate");
557
- }
558
- });
559
- recognition.onresult = event => {
560
- const transcript = event.results[0][0].transcript;
561
- chatInput.innerText += (chatInput.innerText ? " " : "") + transcript;
562
- updateSendButton();
563
- };
564
- recognition.onend = () => {
565
- voiceBtn.classList.remove("listening");
566
- siriImage.classList.remove("siri-animate");
567
- };
568
- }
569
- // Chat Modal
570
- switchChatBtn.addEventListener("click", () => {
571
- saveCurrentChat();
572
- if (chats.length === 0) {
573
- chatList.innerHTML = `<li class='text-gray-400 text-sm'>No saved chats.</li>`;
574
- } else {
575
- chatList.innerHTML = chats.map(chat => `
576
- <li>
577
- <button class="w-full text-left px-3 py-2 rounded-lg bg-gray-100 hover:bg-gray-200"
578
- data-id="${chat.id}">
579
- Chat #${chat.id.toString().slice(-4)}
580
- </button>
581
- </li>
582
- `).join("");
583
- chatList.querySelectorAll("button").forEach(btn => {
584
- btn.addEventListener("click", e => {
585
- const id = Number(e.currentTarget.getAttribute("data-id"));
586
- const chat = chats.find(c => c.id === id);
587
- if (chat) {
588
- currentChat = { ...chat };
589
- renderMessages();
590
- chatModal.classList.add("hidden");
591
- infinityAiText.style.opacity = "0";
592
- showMessagesArea();
593
- }
594
- });
595
- });
596
  }
597
- chatModal.classList.remove("hidden");
598
- });
599
- closeModalBtn.addEventListener("click", () => {
600
- chatModal.classList.add("hidden");
601
- });
602
- showPage('chat-page');
603
- updateSendButton();
604
- renderMessages();
605
- // ✅ New Chat button works
606
- newChatBtn.addEventListener("click", createNewChat);
607
- });
608
- });
609
-
610
- function showPage(pageId) {
611
- ['chat-page', 'login-page', 'signup-page'].forEach(id => {
612
- const el = document.getElementById(id);
613
- if (el) {
614
- if (id === pageId) {
615
- el.classList.remove('hidden');
616
- } else {
617
- el.classList.add('hidden');
618
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
619
  }
620
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  }
622
- window.showPage = showPage;
623
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
624
  </body>
625
  </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">
6
+ <title>Infinity GRM</title>
7
+
8
+ <style>
9
+ * {
10
+ box-sizing: border-box;
11
+ margin: 0;
12
+ padding: 0;
13
+ font-family: Arial, sans-serif;
14
+ }
15
+
16
+ html {
17
+ scroll-behavior: smooth;
18
+ }
19
+
20
+ body {
21
+ overflow-x: hidden;
22
+ background: #fff;
23
+ }
24
+
25
+
26
+ /* =========================
27
+ HEADER
28
+ ========================= */
29
+
30
+ header {
31
+ position: fixed;
32
+ top: 0;
33
+ left: 0;
34
+
35
+ width: 100%;
36
+ height: 80px;
37
+
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: space-between;
41
+
42
+ padding: 0 40px;
43
+
44
+ background: #fff;
45
+
46
+ z-index: 1000;
47
+
48
+ transition: background-color 0.3s ease;
49
+ }
50
+
51
+ header.dark {
52
+ background: #000;
53
+ }
54
+
55
+ .logo img {
56
+ height: 50px;
57
+ width: auto;
58
+ display: block;
59
+ }
60
+
61
+ .nav {
62
+ position: absolute;
63
+ left: 50%;
64
+
65
+ transform: translateX(-50%);
66
+
67
+ display: flex;
68
+ gap: 40px;
69
+ }
70
+
71
+ .nav a {
72
+ color: #000;
73
+
74
+ text-decoration: none;
75
+
76
+ font-weight: 600;
77
+ font-size: 15px;
78
+
79
+ transition: color 0.3s ease;
80
+ }
81
+
82
+ header.dark .nav a {
83
+ color: #fff;
84
+ }
85
+
86
+
87
+ /* =========================
88
+ ACCOUNT
89
+ ========================= */
90
+
91
+ .account {
92
+ width: 48px;
93
+ height: 48px;
94
+
95
+ border-radius: 50%;
96
+
97
+ background: #000;
98
+
99
+ display: grid;
100
+ place-items: center;
101
+
102
+ text-decoration: none;
103
+ }
104
+
105
+ .account svg {
106
+ width: 23px;
107
+ height: 23px;
108
+
109
+ display: block;
110
+
111
+ fill: #fff;
112
+ }
113
+
114
+ header.dark .account {
115
+ background: #fff;
116
+ }
117
+
118
+ header.dark .account svg {
119
+ fill: #000;
120
+ }
121
+
122
+
123
+ /* =========================
124
+ MAIN
125
+ ========================= */
126
+
127
+ main {
128
+ min-height: 100vh;
129
+
130
+ padding-top: 80px;
131
+
132
+ display: flex;
133
+ align-items: center;
134
+ justify-content: center;
135
+
136
+ position: relative;
137
+ }
138
+
139
+
140
+ /* =========================
141
+ HOME
142
+ ========================= */
143
+
144
+ .home {
145
+ width: 100%;
146
+
147
+ display: flex;
148
+ flex-direction: column;
149
+
150
+ align-items: center;
151
+ justify-content: center;
152
+ }
153
+
154
+ h1 {
155
+ font-size: 4rem;
156
+
157
+ margin-bottom: 30px;
158
+ }
159
+
160
+
161
+ /* =========================
162
+ INPUT BAR
163
+ ========================= */
164
+
165
+ .input {
166
+ width: min(680px, 90%);
167
+
168
+ min-height: 68px;
169
+
170
+ position: relative;
171
+
172
+ display: flex;
173
+ align-items: center;
174
+
175
+ background: #fff;
176
+
177
+ border: 1px solid #d1d5db;
178
+
179
+ border-radius: 18px;
180
+
181
+ box-shadow:
182
+ 0 5px 25px rgba(0, 0, 0, 0.05);
183
+
184
+ transition:
185
+ border-color 0.2s ease,
186
+ box-shadow 0.2s ease;
187
+ }
188
+
189
+ .input:focus-within {
190
+ border-color: #aaa;
191
+
192
+ box-shadow:
193
+ 0 5px 25px rgba(0, 0, 0, 0.08);
194
+ }
195
+
196
+
197
+ /* =========================
198
+ TEXT INPUT
199
+ ========================= */
200
+
201
+ .edit {
202
+ width: 100%;
203
+
204
+ min-height: 66px;
205
+ max-height: 180px;
206
+
207
+ padding:
208
+ 18px
209
+ 105px
210
+ 18px
211
+ 18px;
212
+
213
+ outline: none;
214
+ border: none;
215
+
216
+ overflow-y: auto;
217
+ overflow-x: hidden;
218
+
219
+ font-size: 16px;
220
+ line-height: 1.4;
221
+
222
+ color: #111;
223
+
224
+ cursor: text;
225
+
226
+ white-space: pre-wrap;
227
+ word-break: break-word;
228
+
229
+ /*
230
+ Makes the contenteditable behave
231
+ consistently after being cleared.
232
+ */
233
+ caret-color: #000;
234
+ }
235
+
236
+ .edit:focus {
237
+ outline: none;
238
+ }
239
+
240
+
241
+ /* =========================
242
+ ASK ME ANYTHING PLACEHOLDER
243
+ ========================= */
244
+
245
+ /*
246
+ Because this is contenteditable rather
247
+ than a normal input, we use ::before.
248
+
249
+ When the element is completely empty,
250
+ "Ask me anything..." appears.
251
+
252
+ Your JavaScript must clear the element
253
+ with innerHTML = "" after sending.
254
+ */
255
+
256
+ .edit:empty::before {
257
+ content: attr(data-placeholder);
258
+
259
+ color: #aaa;
260
+
261
+ pointer-events: none;
262
+
263
+ user-select: none;
264
+ }
265
+
266
+
267
+ /*
268
+ Keep the placeholder visible when the
269
+ contenteditable receives focus.
270
+ */
271
+
272
+ .edit:focus:empty::before {
273
+ color: #aaa;
274
+ }
275
+
276
+
277
+ /* =========================
278
+ BUTTON RESET
279
+ ========================= */
280
+
281
+ button {
282
+ border: 0;
283
+ outline: 0;
284
+
285
+ background: transparent;
286
+
287
+ cursor: pointer;
288
+
289
+ padding: 0;
290
+
291
+ -webkit-appearance: none;
292
+ appearance: none;
293
+ }
294
+
295
+
296
+ /* =========================
297
+ MICROPHONE
298
+ ========================= */
299
+
300
+ .voice {
301
+ position: absolute;
302
+
303
+ right: 52px;
304
+ bottom: 14px;
305
+
306
+ width: 38px;
307
+ height: 38px;
308
+
309
+ min-width: 38px;
310
+ min-height: 38px;
311
+
312
+ border-radius: 50%;
313
+
314
+ display: flex;
315
+ align-items: center;
316
+ justify-content: center;
317
+
318
+ color: #777;
319
+
320
+ background: transparent;
321
+
322
+ transition:
323
+ background-color 0.2s ease,
324
+ color 0.2s ease,
325
+ transform 0.15s ease;
326
+ }
327
+
328
+ .voice:hover {
329
+ background: #eee;
330
+
331
+ color: #333;
332
+ }
333
+
334
+ .voice:active {
335
+ transform: scale(0.94);
336
+ }
337
+
338
+ .voice.active {
339
+ background: #eee;
340
+
341
+ color: #000;
342
+ }
343
+
344
+
345
+ /* =========================
346
+ SEND / UP ARROW
347
+ ========================= */
348
+
349
+ /*
350
+ INACTIVE STATE
351
+
352
+ This matches the gray circle from
353
+ your reference image.
354
+
355
+ It appears even when the input is empty.
356
+ */
357
+
358
+ .send {
359
+ position: absolute;
360
+
361
+ right: 10px;
362
+ bottom: 14px;
363
+
364
+ width: 38px;
365
+ height: 38px;
366
+
367
+ min-width: 38px;
368
+ min-height: 38px;
369
+
370
+ border-radius: 50%;
371
+
372
+ display: flex;
373
+ align-items: center;
374
+ justify-content: center;
375
+
376
+ background: #b3b3b3;
377
+
378
+ color: #fff;
379
+
380
+ transition:
381
+ background-color 0.2s ease,
382
+ color 0.2s ease,
383
+ transform 0.15s ease;
384
+ }
385
+
386
+
387
+ /*
388
+ INACTIVE HOVER
389
+ */
390
+
391
+ .send:hover {
392
+ background: #a5a5a5;
393
+
394
+ color: #fff;
395
+ }
396
+
397
+
398
+ /*
399
+ CLICK
400
+ */
401
+
402
+ .send:active {
403
+ transform: scale(0.94);
404
+ }
405
+
406
+
407
+ /*
408
+ ACTIVE STATE
409
+
410
+ JavaScript adds .active when the
411
+ user enters text.
412
+ */
413
+
414
+ .send.active {
415
+ background: #000;
416
+
417
+ color: #fff;
418
+ }
419
+
420
+
421
+ /*
422
+ ACTIVE HOVER
423
+ */
424
+
425
+ .send.active:hover {
426
+ background: #222;
427
+
428
+ color: #fff;
429
+ }
430
+
431
+
432
+ /* =========================
433
+ SVG ICONS
434
+ ========================= */
435
+
436
+ .voice svg,
437
+ .send svg {
438
+ display: block;
439
+
440
+ width: 20px;
441
+ height: 20px;
442
+
443
+ min-width: 20px;
444
+ min-height: 20px;
445
+
446
+ max-width: 20px;
447
+ max-height: 20px;
448
+
449
+ flex-shrink: 0;
450
+
451
+ overflow: visible;
452
+ }
453
+
454
+ .voice svg {
455
+ width: 21px;
456
+ height: 21px;
457
+
458
+ min-width: 21px;
459
+ min-height: 21px;
460
+
461
+ max-width: 21px;
462
+ max-height: 21px;
463
+ }
464
+
465
+ .voice svg path,
466
+ .send svg path {
467
+ fill: none;
468
+
469
+ stroke: currentColor;
470
+
471
+ vector-effect: non-scaling-stroke;
472
+ }
473
+
474
+
475
+ /* =========================
476
+ MISSION BUTTON
477
+ ========================= */
478
+
479
+ .mission-btn {
480
+ margin-top: 20px;
481
+
482
+ padding:
483
+ 15px
484
+ 35px;
485
+
486
+ border-radius: 50px;
487
+
488
+ background: #000;
489
+
490
+ color: #fff;
491
+
492
+ text-decoration: none;
493
+
494
+ font-weight: bold;
495
+
496
+ transition:
497
+ background-color 0.2s ease,
498
+ transform 0.2s ease;
499
+ }
500
+
501
+ .mission-btn:hover {
502
+ background: #222;
503
+
504
+ transform: translateY(-1px);
505
+ }
506
+
507
+
508
+ /* =========================
509
+ CHAT
510
+ ========================= */
511
+
512
+ .chat {
513
+ display: none;
514
+
515
+ position: fixed;
516
+
517
+ top: 80px;
518
+ right: 0;
519
+ bottom: 0;
520
+ left: 0;
521
+
522
+ flex-direction: column;
523
+
524
+ background: #fff;
525
+
526
+ z-index: 500;
527
+
528
+ /*
529
+ Prevent the page itself from scrolling.
530
+ */
531
+
532
+ overflow: hidden;
533
+ }
534
+
535
+
536
+ /* =========================
537
+ CHAT MESSAGES
538
+ ========================= */
539
+
540
+ .messages {
541
+ flex: 1;
542
+
543
+ min-height: 0;
544
+
545
+ overflow-y: auto;
546
+ overflow-x: hidden;
547
+
548
+ padding:
549
+ 35px
550
+ 20px
551
+ 150px;
552
+
553
+ /*
554
+ Keep scrolling inside the messages
555
+ area instead of moving the webpage.
556
+ */
557
+
558
+ overscroll-behavior: contain;
559
+
560
+ -webkit-overflow-scrolling: touch;
561
+ }
562
+
563
+ .messages-inner {
564
+ max-width: 850px;
565
+
566
+ margin: 0 auto;
567
+ }
568
+
569
+
570
+ /* =========================
571
+ MESSAGE
572
+ ========================= */
573
+
574
+ .message {
575
+ display: flex;
576
+
577
+ margin-bottom: 25px;
578
+ }
579
+
580
+
581
+ /* =========================
582
+ USER MESSAGE
583
+ ========================= */
584
+
585
+ .user {
586
+ justify-content: flex-end;
587
+ }
588
+
589
+ .user div {
590
+ max-width: 70%;
591
+
592
+ background: #f1f1f1;
593
+
594
+ padding:
595
+ 12px
596
+ 17px;
597
+
598
+ border-radius:
599
+ 20px
600
+ 20px
601
+ 6px
602
+ 20px;
603
+
604
+ color: #111;
605
+
606
+ line-height: 1.5;
607
+
608
+ white-space: pre-wrap;
609
+
610
+ word-break: break-word;
611
+ }
612
+
613
+
614
+ /* =========================
615
+ AI MESSAGE
616
+ ========================= */
617
+
618
+ .ai div {
619
+ max-width: 85%;
620
+
621
+ color: #111;
622
+
623
+ line-height: 1.7;
624
+
625
+ white-space: pre-wrap;
626
+
627
+ word-break: break-word;
628
+ }
629
+
630
+
631
+ /* =========================
632
+ CHAT BOTTOM INPUT
633
+ ========================= */
634
+
635
+ .bottom {
636
+ position: absolute;
637
+
638
+ bottom: 0;
639
+ left: 0;
640
+ right: 0;
641
+
642
+ display: flex;
643
+
644
+ justify-content: center;
645
+
646
+ padding:
647
+ 15px
648
+ 20px
649
+ 22px;
650
+
651
+ background:
652
+ linear-gradient(
653
+ rgba(255, 255, 255, 0),
654
+ rgba(255, 255, 255, 0.95) 40%,
655
+ #fff 70%
656
+ );
657
+
658
+ z-index: 10;
659
+ }
660
+
661
+
662
+ /*
663
+ Keep the chat input white.
664
+ */
665
+
666
+ .bottom .input {
667
+ width: min(680px, 100%);
668
+
669
+ background: #fff;
670
+ }
671
+
672
+
673
+ /*
674
+ The same gray inactive send button
675
+ applies to the chat input.
676
+ */
677
+
678
+ .bottom .send {
679
+ background: #b3b3b3;
680
+
681
+ color: #fff;
682
+ }
683
+
684
+ .bottom .send:hover {
685
+ background: #a5a5a5;
686
+
687
+ color: #fff;
688
+ }
689
+
690
+ .bottom .send.active {
691
+ background: #000;
692
+
693
+ color: #fff;
694
+ }
695
+
696
+ .bottom .send.active:hover {
697
+ background: #222;
698
+
699
+ color: #fff;
700
+ }
701
+
702
+
703
+ /* =========================
704
+ MISSION
705
+ ========================= */
706
+
707
+ #mission {
708
+ min-height: 100vh;
709
+
710
+ background: #000;
711
+
712
+ color: #fff;
713
+
714
+ display: flex;
715
+
716
+ flex-direction: column;
717
+
718
+ align-items: center;
719
+ justify-content: center;
720
+
721
+ text-align: center;
722
+
723
+ padding:
724
+ 100px
725
+ 10%;
726
+ }
727
+
728
+ #mission h2 {
729
+ font-size: 4rem;
730
+
731
+ margin-bottom: 30px;
732
+ }
733
+
734
+ #mission p {
735
+ max-width: 900px;
736
+
737
+ font-size: 1.2rem;
738
+
739
+ line-height: 1.9;
740
+ }
741
+
742
+
743
+ /* =========================
744
+ MOBILE
745
+ ========================= */
746
+
747
+ @media (max-width: 700px) {
748
+
749
+ header {
750
+ height: 70px;
751
+
752
+ padding:
753
+ 0
754
+ 15px;
755
+ }
756
+
757
+
758
+ .logo img {
759
+ height: 42px;
760
+ }
761
+
762
+
763
+ .nav {
764
+ gap: 15px;
765
+ }
766
+
767
+
768
+ .nav a {
769
+ font-size: 12px;
770
+ }
771
+
772
+
773
+ .account {
774
+ width: 42px;
775
+ height: 42px;
776
+ }
777
+
778
+
779
+ main {
780
+ padding-top: 70px;
781
+ }
782
+
783
+
784
+ h1 {
785
+ font-size: 2.5rem;
786
+ }
787
+
788
+
789
+ .input {
790
+ width: 92%;
791
+
792
+ min-height: 64px;
793
+ }
794
+
795
+
796
+ .edit {
797
+ min-height: 62px;
798
+
799
+ padding:
800
+ 17px
801
+ 105px
802
+ 17px
803
+ 16px;
804
+ }
805
+
806
+
807
+ .chat {
808
+ top: 70px;
809
+ }
810
+
811
+
812
+ .messages {
813
+ padding:
814
+ 30px
815
+ 15px
816
+ 150px;
817
+ }
818
+
819
+
820
+ .user div {
821
+ max-width: 85%;
822
+ }
823
+
824
+
825
+ .ai div {
826
+ max-width: 92%;
827
+ }
828
+
829
+
830
+ #mission {
831
+ padding:
832
+ 80px
833
+ 8%;
834
+ }
835
+
836
+
837
+ #mission h2 {
838
+ font-size: 2.5rem;
839
+ }
840
+
841
+
842
+ #mission p {
843
+ font-size: 1rem;
844
+
845
+ line-height: 1.7;
846
+ }
847
  }
848
  </style>
849
  </head>
850
+
851
+
852
  <body>
853
+
854
+
855
+ <!-- =========================
856
+ HEADER
857
+ ========================= -->
858
+
859
+ <header id="header">
860
+
861
+ <a
862
+ class="logo"
863
+ href="index.html"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
864
  >
865
+ <img
866
+ src="https://copilot.microsoft.com/th/id/BCO.cc808577-1db9-4523-90a6-be7b2abee261.png"
867
+ alt="InfinityGRM"
 
 
 
 
 
 
 
868
  >
869
+ </a>
870
+
871
+
872
+ <nav class="nav">
873
+
874
+ <a href="index.html">
875
+ Home
876
+ </a>
877
+
878
+ <a href="labs.html">
879
+ Labs
880
+ </a>
881
+
882
+ </nav>
883
+
884
+
885
+ <a
886
+ href="signup.html"
887
+ class="account"
888
+ aria-label="Account"
889
+ >
890
+
891
+ <svg
892
+ viewBox="0 0 24 24"
893
+ aria-hidden="true"
894
  >
895
+
896
+ <path d="M12 12c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm0 2c-3.34 0-10 1.68-10 5v3h20v-3c0-3.32-6.66-5-10-5z"/>
897
+
898
+ </svg>
899
+
900
+ </a>
901
+
902
+ </header>
903
+
904
+
905
+ <!-- =========================
906
+ MAIN
907
+ ========================= -->
908
+
909
+ <main>
910
+
911
+
912
+ <!-- =========================
913
+ HOME
914
+ ========================= -->
915
+
916
+ <section
917
+ class="home"
918
+ id="home"
919
+ >
920
+
921
+ <h1>
922
+ InfinityGRM
923
+ </h1>
924
+
925
+
926
+ <div class="input">
927
+
928
+
929
+ <!-- HOME INPUT -->
930
+
931
+ <div
932
+ id="input"
933
+ class="edit"
934
+ contenteditable="true"
935
+ role="textbox"
936
+ aria-label="Ask me anything"
937
+ data-placeholder="Ask me anything..."
938
+ ></div>
939
+
940
+
941
+ <!-- HOME DICTATION -->
942
+
943
+ <button
944
+ id="voice"
945
+ class="voice"
946
+ type="button"
947
+ aria-label="Dictate"
948
+ title="Dictate"
949
  >
950
+
951
+ <svg
952
+ viewBox="0 0 24 24"
953
+ fill="none"
954
+ xmlns="http://www.w3.org/2000/svg"
955
+ aria-hidden="true"
956
+ >
957
+
958
+ <path
959
+ d="M12 1.5a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0v-7a3 3 0 0 0-3-3z"
960
+ stroke-width="1.5"
961
+ stroke-linecap="round"
962
+ stroke-linejoin="round"
963
+ />
964
+
965
+ <path
966
+ d="M19.5 10.5v1.5a7.5 7.5 0 0 1-15 0v-1.5"
967
+ stroke-width="1.5"
968
+ stroke-linecap="round"
969
+ />
970
+
971
+ <path
972
+ d="M12 19.5v3"
973
+ stroke-width="1.5"
974
+ stroke-linecap="round"
975
+ />
976
+
977
+ </svg>
978
+
979
+ </button>
980
+
981
+
982
+ <!-- HOME SEND -->
983
+
984
+ <button
985
+ id="send"
986
+ class="send"
987
+ type="button"
988
+ aria-label="Send"
989
+ title="Send"
990
+ >
991
+
992
+ <svg
993
+ viewBox="0 0 24 24"
994
+ fill="none"
995
+ xmlns="http://www.w3.org/2000/svg"
996
+ aria-hidden="true"
997
+ >
998
+
999
+ <path
1000
+ d="M12 19V5m0 0-6 6m6-6 6 6"
1001
+ stroke-width="2"
1002
+ stroke-linecap="round"
1003
+ stroke-linejoin="round"
1004
+ />
1005
+
1006
+ </svg>
1007
+
1008
+ </button>
1009
+
 
 
 
 
 
 
 
 
 
 
1010
  </div>
1011
+
1012
+
1013
+ <!-- MISSION BUTTON -->
1014
+
1015
+ <a
1016
+ href="#mission"
1017
+ class="mission-btn"
 
 
 
 
 
 
 
1018
  >
1019
+ Our Mission
1020
+ </a>
1021
+
1022
+ </section>
1023
+
1024
+
1025
+ <!-- =========================
1026
+ CHAT
1027
+ ========================= -->
1028
+
1029
+ <section
1030
+ class="chat"
1031
+ id="chat"
 
 
 
 
 
 
1032
  >
1033
+
1034
+
1035
+ <!-- MESSAGE AREA -->
1036
+
1037
+ <div
1038
+ class="messages"
1039
+ id="messagesContainer"
1040
+ >
1041
+
1042
+ <div
1043
+ class="messages-inner"
1044
+ id="messages"
1045
+ ></div>
1046
+
1047
+ </div>
1048
+
1049
+
1050
+ <!-- =========================
1051
+ CHAT INPUT
1052
+ ========================= -->
1053
+
1054
+ <div class="bottom">
1055
+
1056
+ <div class="input">
1057
+
1058
+
1059
+ <!-- CHAT INPUT -->
1060
+
1061
+ <div
1062
+ id="chatInput"
1063
+ class="edit"
1064
+ contenteditable="true"
1065
+ role="textbox"
1066
+ aria-label="Ask me anything"
1067
+ data-placeholder="Ask me anything..."
1068
+ ></div>
1069
+
1070
+
1071
+ <!-- CHAT DICTATION -->
1072
+
1073
+ <button
1074
+ id="chatVoice"
1075
+ class="voice"
1076
+ type="button"
1077
+ aria-label="Dictate"
1078
+ title="Dictate"
1079
+ >
1080
+
1081
+ <svg
1082
+ viewBox="0 0 24 24"
1083
+ fill="none"
1084
+ xmlns="http://www.w3.org/2000/svg"
1085
+ aria-hidden="true"
1086
+ >
1087
+
1088
+ <path
1089
+ d="M12 1.5a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0v-7a3 3 0 0 0-3-3z"
1090
+ stroke-width="1.5"
1091
+ stroke-linecap="round"
1092
+ stroke-linejoin="round"
1093
+ />
1094
+
1095
+ <path
1096
+ d="M19.5 10.5v1.5a7.5 7.5 0 0 1-15 0v-1.5"
1097
+ stroke-width="1.5"
1098
+ stroke-linecap="round"
1099
+ />
1100
+
1101
+ <path
1102
+ d="M12 19.5v3"
1103
+ stroke-width="1.5"
1104
+ stroke-linecap="round"
1105
+ />
1106
+
1107
+ </svg>
1108
+
1109
+ </button>
1110
+
1111
+
1112
+ <!-- CHAT SEND -->
1113
+
1114
+ <button
1115
+ id="chatSend"
1116
+ class="send"
1117
+ type="button"
1118
+ aria-label="Send"
1119
+ title="Send"
1120
+ >
1121
+
1122
+ <svg
1123
+ viewBox="0 0 24 24"
1124
+ fill="none"
1125
+ xmlns="http://www.w3.org/2000/svg"
1126
+ aria-hidden="true"
1127
+ >
1128
+
1129
+ <path
1130
+ d="M12 19V5m0 0-6 6m6-6 6 6"
1131
+ stroke-width="2"
1132
+ stroke-linecap="round"
1133
+ stroke-linejoin="round"
1134
+ />
1135
+
1136
+ </svg>
1137
+
1138
+ </button>
1139
+
1140
  </div>
1141
+
1142
+ </div>
1143
+
1144
+ </section>
1145
+
1146
+ </main>
1147
+
1148
+
1149
+ <!-- =========================
1150
+ MISSION
1151
+ ========================= -->
1152
+
1153
+ <section id="mission">
1154
+
1155
+ <h2>
1156
+ Our Mission
1157
+ </h2>
1158
+
1159
+ <p>
1160
+ Our mission is to develop safe superintelligence that elevates and
1161
+ benefits all of humanity. We believe in building advanced AI systems,
1162
+ infrastructure, and breakthroughs that empower individuals and help
1163
+ shape a flourishing future. Every contribution allows us to continue
1164
+ pioneering new frontiers, supporting ambitious research, and ensuring
1165
+ that the rewards of technological progress make a lasting difference
1166
+ for communities around the world.
1167
+ </p>
1168
+
1169
+ </section>
1170
+
1171
+
1172
+ <script>
1173
+
1174
+ /* =========================
1175
+ ELEMENTS
1176
+ ========================= */
1177
+
1178
+ const header =
1179
+ document.getElementById("header");
1180
+
1181
+ const mission =
1182
+ document.getElementById("mission");
1183
+
1184
+ const home =
1185
+ document.getElementById("home");
1186
+
1187
+ const chat =
1188
+ document.getElementById("chat");
1189
+
1190
+ const input =
1191
+ document.getElementById("input");
1192
+
1193
+ const send =
1194
+ document.getElementById("send");
1195
+
1196
+ const chatInput =
1197
+ document.getElementById("chatInput");
1198
+
1199
+ const chatSend =
1200
+ document.getElementById("chatSend");
1201
+
1202
+ const messages =
1203
+ document.getElementById("messages");
1204
+
1205
+ const messagesContainer =
1206
+ document.getElementById("messagesContainer");
1207
+
1208
+ const voice =
1209
+ document.getElementById("voice");
1210
+
1211
+ const chatVoice =
1212
+ document.getElementById("chatVoice");
1213
+
1214
+
1215
+ /* =========================
1216
+ CHAT STATE
1217
+ ========================= */
1218
+
1219
+ /*
1220
+ This tells the header whether the user
1221
+ is currently inside the chat.
1222
+
1223
+ When true, the header will ALWAYS remain white.
1224
+ */
1225
+
1226
+ let chatActive = false;
1227
+
1228
+
1229
+ /* =========================
1230
+ SESSION ID
1231
+ ========================= */
1232
+
1233
+ /*
1234
+ One session ID is created for the entire
1235
+ browser session.
1236
+
1237
+ This allows multiple questions to belong
1238
+ to the same conversation.
1239
+ */
1240
+
1241
+ const sessionId =
1242
+ "session_" +
1243
+ Math.random()
1244
+ .toString(36)
1245
+ .slice(2) +
1246
+ "_" +
1247
+ Date.now();
1248
+
1249
+
1250
+ /* =========================
1251
+ HEADER
1252
+ ========================= */
1253
+
1254
+ function updateHeader() {
1255
+
1256
+ /*
1257
+ CRITICAL FIX:
1258
+
1259
+ If chat is open, never turn the header black.
1260
+
1261
+ The user can scroll through 50+ messages and
1262
+ the header will stay white.
1263
+ */
1264
+
1265
+ if (chatActive) {
1266
+
1267
+ header.classList.remove("dark");
1268
+
1269
+ return;
1270
  }
1271
+
1272
+
1273
+ /*
1274
+ Normal homepage behavior.
1275
+
1276
+ The header becomes black when the Mission
1277
+ section reaches the top of the viewport.
1278
+ */
1279
+
1280
+ const missionTop =
1281
+ mission.getBoundingClientRect().top;
1282
+
1283
+
1284
+ header.classList.toggle(
1285
+ "dark",
1286
+ missionTop <= 100
1287
+ );
1288
+ }
1289
+
1290
+
1291
+ /*
1292
+ Listen for normal page scrolling.
1293
+
1294
+ This does NOT affect the header while chatActive
1295
+ is true.
1296
+ */
1297
+
1298
+ window.addEventListener(
1299
+ "scroll",
1300
+ updateHeader
1301
+ );
1302
+
1303
+
1304
+ /*
1305
+ Initial header state.
1306
+ */
1307
+
1308
+ window.addEventListener(
1309
+ "load",
1310
+ updateHeader
1311
+ );
1312
+
1313
+
1314
+ /* =========================
1315
+ GET CLEAN TEXT
1316
+ ========================= */
1317
+
1318
+ function getInputText(element) {
1319
+
1320
+ return element.innerText
1321
+ .replace(/\u200B/g, "")
1322
+ .replace(/\u00A0/g, " ")
1323
+ .trim();
1324
+ }
1325
+
1326
+
1327
+ /* =========================
1328
+ CLEAR CONTENTEDITABLE
1329
+ ========================= */
1330
+
1331
+ function clearInput(element) {
1332
+
1333
+ /*
1334
+ innerHTML = "" is important.
1335
+
1336
+ contenteditable browsers can insert <br>
1337
+ elements that would otherwise prevent
1338
+ :empty from working correctly.
1339
+ */
1340
+
1341
+ element.innerHTML = "";
1342
+
1343
+ element.textContent = "";
1344
+ }
1345
+
1346
+
1347
+ /* =========================
1348
+ BUTTON STATE
1349
+ ========================= */
1350
+
1351
+ function updateButton(
1352
+ inputElement,
1353
+ buttonElement
1354
+ ) {
1355
+
1356
+ const text =
1357
+ getInputText(inputElement);
1358
+
1359
+
1360
+ buttonElement.classList.toggle(
1361
+ "active",
1362
+ text.length > 0
1363
+ );
1364
+ }
1365
+
1366
+
1367
+ /* =========================
1368
+ ADD MESSAGE
1369
+ ========================= */
1370
+
1371
+ function addMessage(
1372
+ text,
1373
+ type
1374
+ ) {
1375
+
1376
+ const message =
1377
+ document.createElement("div");
1378
+
1379
+ message.className =
1380
+ "message " + type;
1381
+
1382
+
1383
+ const content =
1384
+ document.createElement("div");
1385
+
1386
+ content.textContent =
1387
+ text;
1388
+
1389
+
1390
+ message.appendChild(
1391
+ content
1392
+ );
1393
+
1394
+
1395
+ messages.appendChild(
1396
+ message
1397
+ );
1398
+
1399
+
1400
+ /*
1401
+ Scroll ONLY the chat message area.
1402
+
1403
+ This is important because the page itself
1404
+ should not move when new messages arrive.
1405
+ */
1406
+
1407
+ requestAnimationFrame(() => {
1408
+
1409
+ messagesContainer.scrollTop =
1410
+ messagesContainer.scrollHeight;
1411
+
1412
+ });
1413
+ }
1414
+
1415
+
1416
+ /* =========================
1417
+ SHOW CHAT
1418
+ ========================= */
1419
+
1420
+ function showChat() {
1421
+
1422
+ /*
1423
+ Mark chat as active FIRST.
1424
+
1425
+ This prevents the header scroll handler
1426
+ from turning it black.
1427
+ */
1428
+
1429
+ chatActive = true;
1430
+
1431
+
1432
+ /*
1433
+ Immediately force the header white.
1434
+ */
1435
+
1436
+ header.classList.remove(
1437
+ "dark"
1438
+ );
1439
+
1440
+
1441
+ /*
1442
+ Hide homepage.
1443
+ */
1444
+
1445
+ home.style.display =
1446
+ "none";
1447
+
1448
+
1449
+ /*
1450
+ Show chat.
1451
+ */
1452
+
1453
+ chat.style.display =
1454
+ "flex";
1455
+
1456
+
1457
+ /*
1458
+ Prevent the main document from scrolling
1459
+ while the chat is active.
1460
+ */
1461
+
1462
+ document.body.style.overflow =
1463
+ "hidden";
1464
+
1465
+
1466
+ /*
1467
+ Focus the chat input.
1468
+ */
1469
+
1470
+ requestAnimationFrame(() => {
1471
+
1472
+ chatInput.focus();
1473
+
1474
+ });
1475
+ }
1476
+
1477
+
1478
+ /* =========================
1479
+ SEND MESSAGE
1480
+ ========================= */
1481
+
1482
+ async function sendMessage(
1483
+ inputElement,
1484
+ buttonElement
1485
+ ) {
1486
+
1487
+ const text =
1488
+ getInputText(inputElement);
1489
+
1490
+
1491
+ /*
1492
+ Do nothing if empty.
1493
+ */
1494
+
1495
+ if (!text) {
1496
+ return;
1497
  }
1498
+
1499
+
1500
+ /*
1501
+ Open chat.
1502
+ */
1503
+
1504
+ showChat();
1505
+
1506
+
1507
+ /*
1508
+ Add user's message immediately.
1509
+ */
1510
+
1511
+ addMessage(
1512
+ text,
1513
+ "user"
1514
+ );
1515
+
1516
+
1517
+ /*
1518
+ Clear the input.
1519
+
1520
+ This makes the placeholder
1521
+ "Ask me anything..."
1522
+ appear again.
1523
+ */
1524
+
1525
+ clearInput(
1526
+ inputElement
1527
+ );
1528
+
1529
+
1530
+ /*
1531
+ Reset send button.
1532
+ */
1533
+
1534
+ updateButton(
1535
+ inputElement,
1536
+ buttonElement
1537
+ );
1538
+
1539
+
1540
  try {
1541
+
1542
+ const response = await fetch(
1543
+ "ai-bridge.php",
1544
+ {
1545
+ method: "POST",
1546
+
1547
+ headers: {
1548
+ "Content-Type": "application/json"
1549
+ },
1550
+
1551
+ body: JSON.stringify({
1552
+ message: text,
1553
+ session_id: sessionId
1554
+ })
1555
+ }
1556
+ );
1557
+
1558
+
1559
+ /*
1560
+ Check HTTP status.
1561
+ */
1562
+
1563
+ if (!response.ok) {
1564
+
1565
+ throw new Error(
1566
+ "Server returned " +
1567
+ response.status
1568
+ );
1569
+ }
1570
+
1571
+
1572
+ /*
1573
+ Convert response to JSON.
1574
+ */
1575
+
1576
+ const data =
1577
+ await response.json();
1578
+
1579
+
1580
+ /*
1581
+ Add AI response.
1582
+ */
1583
+
1584
+ addMessage(
1585
+ data.response ||
1586
+ (
1587
+ "Error: " +
1588
+ (
1589
+ data.error ||
1590
+ "No response"
1591
+ )
1592
+ ),
1593
+ "ai"
1594
+ );
1595
+
1596
+
1597
+ } catch (error) {
1598
+
1599
+ console.error(
1600
+ "Chat error:",
1601
+ error
1602
+ );
1603
+
1604
+
1605
+ addMessage(
1606
+ "Failed to connect to the server.",
1607
+ "ai"
1608
+ );
1609
  }
1610
+
1611
+
1612
+ /*
1613
+ ALWAYS reset the chat input.
1614
+
1615
+ This guarantees the placeholder is
1616
+ visible after the AI responds.
1617
+ */
1618
+
1619
+ clearInput(
1620
+ chatInput
1621
+ );
1622
+
1623
+
1624
+ updateButton(
1625
+ chatInput,
1626
+ chatSend
1627
+ );
1628
+
1629
+
1630
+ /*
1631
+ Keep the chat input ready for
1632
+ another question.
1633
+ */
1634
+
1635
+ requestAnimationFrame(() => {
1636
+
1637
+ chatInput.focus();
1638
+
1639
  });
1640
  }
1641
+
1642
+
1643
+ /* =========================
1644
+ HOME INPUT
1645
+ ========================= */
1646
+
1647
+ input.addEventListener(
1648
+ "input",
1649
+ () => {
1650
+
1651
+ updateButton(
1652
+ input,
1653
+ send
1654
+ );
1655
+
1656
  }
1657
+ );
1658
+
1659
+
1660
+ /* =========================
1661
+ CHAT INPUT
1662
+ ========================= */
1663
+
1664
+ chatInput.addEventListener(
1665
+ "input",
1666
+ () => {
1667
+
1668
+ updateButton(
1669
+ chatInput,
1670
+ chatSend
1671
+ );
1672
+
 
 
 
 
 
 
 
 
 
1673
  }
1674
+ );
1675
+
1676
+
1677
+ /* =========================
1678
+ SEND BUTTON
1679
+ ========================= */
1680
+
1681
+ send.addEventListener(
1682
+ "click",
1683
+ () => {
1684
+
1685
+ sendMessage(
1686
+ input,
1687
+ send
1688
+ );
1689
+
1690
  }
1691
+ );
1692
+
1693
+
1694
+ chatSend.addEventListener(
1695
+ "click",
1696
+ () => {
1697
+
1698
+ sendMessage(
1699
+ chatInput,
1700
+ chatSend
1701
+ );
1702
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1703
  }
1704
+ );
1705
+
1706
+
1707
+ /* =========================
1708
+ ENTER TO SEND
1709
+ ========================= */
1710
+
1711
+ input.addEventListener(
1712
+ "keydown",
1713
+ event => {
1714
+
1715
+ /*
1716
+ Enter = send
1717
+ Shift + Enter = new line
1718
+ */
1719
+
1720
+ if (
1721
+ event.key === "Enter" &&
1722
+ !event.shiftKey
1723
+ ) {
1724
+
1725
+ event.preventDefault();
1726
+
1727
+ sendMessage(
1728
+ input,
1729
+ send
1730
+ );
1731
+ }
1732
+
1733
+ }
1734
+ );
1735
+
1736
+
1737
+ chatInput.addEventListener(
1738
+ "keydown",
1739
+ event => {
1740
+
1741
+ /*
1742
+ Enter = send
1743
+ Shift + Enter = new line
1744
+ */
1745
+
1746
+ if (
1747
+ event.key === "Enter" &&
1748
+ !event.shiftKey
1749
+ ) {
1750
+
1751
+ event.preventDefault();
1752
+
1753
+ sendMessage(
1754
+ chatInput,
1755
+ chatSend
1756
+ );
1757
+ }
1758
+
1759
+ }
1760
+ );
1761
+
1762
+
1763
+ /* =========================
1764
+ DICTATION
1765
+ ========================= */
1766
+
1767
+ function setupDictation(
1768
+ button,
1769
+ targetInput,
1770
+ targetSendButton
1771
+ ) {
1772
+
1773
+ const SpeechRecognition =
1774
+ window.SpeechRecognition ||
1775
+ window.webkitSpeechRecognition;
1776
+
1777
+
1778
+ /*
1779
+ Browser doesn't support speech recognition.
1780
+ */
1781
+
1782
+ if (!SpeechRecognition) {
1783
+
1784
+ button.addEventListener(
1785
+ "click",
1786
+ () => {
1787
+
1788
+ alert(
1789
+ "Voice dictation is not supported in this browser."
1790
+ );
1791
+
1792
+ }
1793
+ );
1794
+
1795
+ return;
1796
  }
1797
+
1798
+
1799
+ const recognition =
1800
+ new SpeechRecognition();
1801
+
1802
+
1803
+ recognition.continuous =
1804
+ false;
1805
+
1806
+ recognition.interimResults =
1807
+ false;
1808
+
1809
+ recognition.lang =
1810
+ "en-US";
1811
+
1812
+
1813
+ /* =========================
1814
+ START
1815
+ ========================= */
1816
+
1817
+ button.addEventListener(
1818
+ "click",
1819
+ () => {
1820
+
1821
+ try {
1822
+
1823
+ recognition.start();
1824
+
1825
+ button.classList.add(
1826
+ "active"
1827
+ );
1828
+
1829
+ } catch (error) {
1830
+
1831
+ console.log(
1832
+ "Speech recognition:",
1833
+ error
1834
+ );
1835
+
1836
+ }
1837
+
1838
+ }
1839
+ );
1840
+
1841
+
1842
+ /* =========================
1843
+ RESULT
1844
+ ========================= */
1845
+
1846
+ recognition.addEventListener(
1847
+ "result",
1848
+ event => {
1849
+
1850
+ const transcript =
1851
+ event
1852
+ .results[0][0]
1853
+ .transcript;
1854
+
1855
+
1856
+ /*
1857
+ Add dictated text.
1858
+ */
1859
+
1860
+ const existing =
1861
+ getInputText(
1862
+ targetInput
1863
+ );
1864
+
1865
+
1866
+ if (existing) {
1867
+
1868
+ targetInput.innerText =
1869
+ existing +
1870
+ " " +
1871
+ transcript;
1872
+
1873
+ } else {
1874
+
1875
+ targetInput.innerText =
1876
+ transcript;
1877
+
1878
+ }
1879
+
1880
+
1881
+ /*
1882
+ Update send button.
1883
+ */
1884
+
1885
+ updateButton(
1886
+ targetInput,
1887
+ targetSendButton
1888
+ );
1889
+
1890
+
1891
+ /*
1892
+ Focus input.
1893
+ */
1894
+
1895
+ targetInput.focus();
1896
+
1897
+ }
1898
+ );
1899
+
1900
+
1901
+ /* =========================
1902
+ END
1903
+ ========================= */
1904
+
1905
+ recognition.addEventListener(
1906
+ "end",
1907
+ () => {
1908
+
1909
+ button.classList.remove(
1910
+ "active"
1911
+ );
1912
+
1913
+ }
1914
+ );
1915
+
1916
+
1917
+ /* =========================
1918
+ ERROR
1919
+ ========================= */
1920
+
1921
+ recognition.addEventListener(
1922
+ "error",
1923
+ error => {
1924
+
1925
+ console.log(
1926
+ "Dictation error:",
1927
+ error
1928
+ );
1929
+
1930
+ button.classList.remove(
1931
+ "active"
1932
+ );
1933
+
1934
+ }
1935
+ );
1936
  }
1937
+
1938
+
1939
+ /* =========================
1940
+ ENABLE HOME DICTATION
1941
+ ========================= */
1942
+
1943
+ setupDictation(
1944
+ voice,
1945
+ input,
1946
+ send
1947
+ );
1948
+
1949
+
1950
+ /* =========================
1951
+ ENABLE CHAT DICTATION
1952
+ ========================= */
1953
+
1954
+ setupDictation(
1955
+ chatVoice,
1956
+ chatInput,
1957
+ chatSend
1958
+ );
1959
+
1960
+
1961
+ /* =========================
1962
+ INITIAL STATE
1963
+ ========================= */
1964
+
1965
+ clearInput(
1966
+ input
1967
+ );
1968
+
1969
+ clearInput(
1970
+ chatInput
1971
+ );
1972
+
1973
+
1974
+ updateButton(
1975
+ input,
1976
+ send
1977
+ );
1978
+
1979
+ updateButton(
1980
+ chatInput,
1981
+ chatSend
1982
+ );
1983
+
1984
+
1985
+ /*
1986
+ Make sure the header starts white.
1987
+ */
1988
+
1989
+ header.classList.remove(
1990
+ "dark"
1991
+ );
1992
+
1993
+ </script>
1994
+
1995
  </body>
1996
  </html>