Athspi commited on
Commit
3dfb66f
·
verified ·
1 Parent(s): 14c0817

Update static/index.html

Browse files
Files changed (1) hide show
  1. static/index.html +242 -167
static/index.html CHANGED
@@ -22,6 +22,7 @@
22
  --quote-color: #4CAF50;
23
  }
24
 
 
25
  * {
26
  box-sizing: border-box;
27
  margin: 0;
@@ -30,7 +31,7 @@
30
 
31
  html, body {
32
  height: 100%;
33
- overflow: hidden;
34
  }
35
 
36
  body {
@@ -48,22 +49,26 @@
48
  flex-direction: column;
49
  height: 100%;
50
  width: 100%;
51
- max-width: 800px;
52
- margin: 0 auto;
 
 
53
  }
54
 
55
  .chat-area {
56
- flex-grow: 1;
57
- overflow-y: auto;
58
  padding: 20px 16px;
59
  display: flex;
60
  flex-direction: column;
61
  }
62
 
 
63
  .welcome-screen {
64
  text-align: center;
65
- margin: auto;
66
  color: var(--placeholder-color);
 
67
  }
68
 
69
  .welcome-screen h2 {
@@ -72,75 +77,53 @@
72
  margin-bottom: 10px;
73
  }
74
 
 
 
 
 
 
75
  .welcome-screen.hidden {
76
- display: none;
77
  }
78
 
 
79
  .message {
80
- max-width: 85%;
81
  padding: 12px 16px;
82
  border-radius: 18px;
83
  margin-bottom: 12px;
84
- word-wrap: break-word;
85
  line-height: 1.6;
86
  font-size: 1rem;
 
 
87
  }
88
 
89
  .user-message {
90
  background-color: var(--user-bubble-color);
91
- align-self: flex-end;
92
- border-bottom-right-radius: 4px;
93
  }
94
 
95
  .ai-message {
96
  background-color: var(--ai-bubble-color);
97
- align-self: flex-start;
98
- border-bottom-left-radius: 4px;
 
99
  }
100
 
101
- /* Enhanced AI message styling */
102
  .ai-message-content {
103
- overflow: hidden;
104
- }
105
-
106
- .ai-message-content p {
107
- margin-bottom: 12px;
108
- }
109
-
110
- .ai-message-content p:last-child {
111
- margin-bottom: 0;
112
- }
113
-
114
- .ai-message-content strong {
115
- color: #ffffff;
116
- font-weight: 600;
117
- }
118
-
119
- .ai-message-content em {
120
- color: #d1d1d1;
121
- font-style: italic;
122
- }
123
-
124
- .ai-message-content a {
125
- color: var(--link-color);
126
- text-decoration: none;
127
- word-break: break-all;
128
- }
129
-
130
- .ai-message-content a:hover {
131
- text-decoration: underline;
132
- }
133
-
134
- .ai-message-content ul,
135
- .ai-message-content ol {
136
- padding-left: 24px;
137
- margin-bottom: 12px;
138
- }
139
-
140
- .ai-message-content li {
141
- margin-bottom: 6px;
142
- }
143
-
144
  .ai-message-content blockquote {
145
  border-left: 3px solid var(--quote-color);
146
  padding-left: 12px;
@@ -148,15 +131,14 @@
148
  color: #bdbdbd;
149
  margin-bottom: 12px;
150
  }
151
-
152
  .ai-message-content pre {
153
  background-color: var(--code-bg-color);
154
  border-radius: 6px;
155
  padding: 12px;
156
- overflow-x: auto;
157
  margin-bottom: 12px;
158
  }
159
-
160
  .ai-message-content code {
161
  font-family: 'Courier New', Courier, monospace;
162
  background-color: var(--code-bg-color);
@@ -164,29 +146,73 @@
164
  border-radius: 3px;
165
  color: var(--code-text-color);
166
  font-size: 0.9em;
 
 
 
 
 
 
 
167
  }
168
 
169
- .ai-message-content .code-block {
170
- display: block;
171
- white-space: pre-wrap;
172
- padding: 12px;
173
- border-radius: 6px;
174
- background-color: var(--code-bg-color);
175
- margin-bottom: 12px;
 
 
 
 
 
 
 
 
 
176
  }
177
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  .typing-indicator {
179
  display: flex;
180
  align-items: center;
181
  padding: 12px 16px;
182
  }
183
  .typing-indicator span {
184
- height: 8px;
185
- width: 8px;
186
- background-color: var(--icon-color);
187
- border-radius: 50%;
188
- display: inline-block;
189
- margin: 0 2px;
190
  animation: bounce 1.4s infinite both;
191
  }
192
  .typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
@@ -197,46 +223,40 @@
197
  40% { transform: scale(1.0); }
198
  }
199
 
 
200
  .input-area {
201
  display: flex;
202
- align-items: flex-end;
203
  padding: 10px 16px;
204
  border-top: 1px solid var(--border-color);
205
  background-color: var(--input-area-color);
206
- flex-shrink: 0;
207
  }
208
-
209
  .input-wrapper {
210
  display: flex;
211
  align-items: center;
212
  width: 100%;
213
  background-color: var(--user-bubble-color);
214
- border-radius: 24px;
215
  padding: 4px;
216
  }
217
-
218
  .input-area textarea {
219
- flex-grow: 1;
220
  border: none;
221
  background: transparent;
222
  color: var(--text-color);
223
- resize: none;
224
  font-size: 1rem;
225
  line-height: 1.5;
226
- max-height: 120px;
227
- overflow-y: auto;
228
  padding: 8px 12px;
229
- font-family: inherit;
230
- }
231
-
232
- .input-area textarea:focus {
233
- outline: none;
234
- }
235
-
236
- .input-area textarea::placeholder {
237
- color: var(--placeholder-color);
238
  }
 
239
 
 
240
  .input-area .icon-button {
241
  background: none;
242
  border: none;
@@ -245,53 +265,43 @@
245
  display: flex;
246
  align-items: center;
247
  justify-content: center;
 
248
  }
249
-
250
- .input-area .icon-button svg {
251
- width: 24px;
252
- height: 24px;
253
- fill: var(--icon-color);
254
  }
 
255
 
256
  .send-button {
257
  background-color: var(--send-button-color);
258
  border-radius: 50%;
259
  padding: 8px;
260
- transition: background-color 0.2s;
261
  }
262
-
263
  .send-button.disabled {
264
  background-color: #555;
265
  cursor: not-allowed;
 
266
  }
267
-
268
  .send-button.disabled svg {
269
  fill: #888;
270
  }
271
-
272
- .send-button svg {
273
- fill: white;
274
- width: 24px;
275
- height: 24px;
276
- }
277
 
278
  /* Animation for new messages */
279
  @keyframes fadeIn {
280
  from { opacity: 0; transform: translateY(10px); }
281
  to { opacity: 1; transform: translateY(0); }
282
  }
283
-
284
- .message {
285
- animation: fadeIn 0.3s ease-out;
286
- }
287
  </style>
288
  </head>
289
  <body>
290
  <div class="chat-container">
291
  <main class="chat-area">
292
  <div class="welcome-screen">
293
- <h2>What can I help with?</h2>
294
- <p>Ask me anything - I can explain concepts, generate ideas, or help with coding!</p>
 
295
  </div>
296
  </main>
297
 
@@ -317,45 +327,60 @@
317
  const sendButton = document.getElementById('send-button');
318
  const attachButton = document.getElementById('attach-button');
319
 
320
- // Auto-resize textarea
 
 
 
321
  const adjustTextareaHeight = () => {
322
- messageInput.style.height = 'auto';
323
- messageInput.style.height = `${messageInput.scrollHeight}px`;
324
- updateSendButtonState();
325
  };
326
 
327
- // Update send button state
328
  const updateSendButtonState = () => {
329
- if (messageInput.value.trim() === '') {
330
- sendButton.classList.add('disabled');
331
- sendButton.disabled = true;
332
- } else {
333
- sendButton.classList.remove('disabled');
334
- sendButton.disabled = false;
335
- }
336
  };
337
 
338
- // Scroll to bottom of chat
339
  const scrollToBottom = () => {
340
  chatArea.scrollTop = chatArea.scrollHeight;
341
  };
342
 
343
- // Add message to chat
344
- const addMessage = (content, sender, isHTML = false) => {
345
- if (!welcomeScreen.classList.contains('hidden')) {
346
- welcomeScreen.classList.add('hidden');
347
- }
348
 
349
  const messageElement = document.createElement('div');
350
  messageElement.classList.add('message', `${sender}-message`);
351
 
 
352
  if (isHTML) {
353
- const contentDiv = document.createElement('div');
354
  contentDiv.classList.add(`${sender}-message-content`);
355
- contentDiv.innerHTML = content;
356
- messageElement.appendChild(contentDiv);
357
  } else {
358
- messageElement.textContent = content;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  }
360
 
361
  chatArea.appendChild(messageElement);
@@ -363,19 +388,72 @@
363
  return messageElement;
364
  };
365
 
366
- // Show typing indicator
367
  const showTypingIndicator = () => {
368
  const indicatorElement = document.createElement('div');
369
  indicatorElement.classList.add('message', 'ai-message', 'typing-indicator');
370
  indicatorElement.innerHTML = '<span></span><span></span><span></span>';
371
  chatArea.appendChild(indicatorElement);
372
  scrollToBottom();
373
- return indicatorElement;
374
  };
375
 
376
- // Get AI response from backend
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
  const getAIResponse = async (userMessage) => {
378
- const indicator = showTypingIndicator();
379
 
380
  try {
381
  const response = await fetch('/chat', {
@@ -392,68 +470,65 @@
392
  throw new Error(data.error);
393
  }
394
 
 
395
  chatArea.removeChild(indicator);
396
- addMessage(data.response, 'ai', true);
 
397
 
398
  } catch (error) {
399
- chatArea.removeChild(indicator);
400
  addMessage("Sorry, I encountered an error. Please try again.", 'ai');
401
  console.error("Error:", error);
402
  }
403
  };
404
 
405
- // Handle form submission
406
  chatForm.addEventListener('submit', async (e) => {
407
- e.preventDefault();
408
  const message = messageInput.value.trim();
409
 
410
  if (message) {
411
- addMessage(message, 'user');
412
- messageInput.value = '';
413
- adjustTextareaHeight();
414
 
415
- await getAIResponse(message);
416
  }
417
  });
418
 
419
- // Handle Enter key
420
  messageInput.addEventListener('keydown', (e) => {
421
  if (e.key === 'Enter' && !e.shiftKey) {
422
- e.preventDefault();
423
- chatForm.dispatchEvent(new Event('submit'));
424
  }
425
  });
426
 
427
- // Textarea input events
428
  messageInput.addEventListener('input', adjustTextareaHeight);
429
 
430
- // Attach button placeholder
431
  attachButton.addEventListener('click', () => {
432
- // Future implementation for file attachments
433
- messageInput.focus();
 
434
  });
435
 
436
- // Initial setup
437
- updateSendButtonState();
438
- messageInput.focus();
439
 
440
- // Example first message on first load
441
  const isFirstVisit = !localStorage.getItem('chatVisited');
442
  if (isFirstVisit) {
443
  localStorage.setItem('chatVisited', 'true');
 
444
  setTimeout(() => {
445
- const welcomeMessage = `
446
- <p>Hello! I'm your AI assistant powered by Gemini. I can help with:</p>
447
- <ul>
448
- <li>Answering questions</li>
449
- <li>Explaining concepts</li>
450
- <li>Generating ideas</li>
451
- <li>Coding help</li>
452
- </ul>
453
- <p>Try asking me something like: <em>"Explain quantum computing in simple terms"</em> or <em>"Help me debug this Python code"</em></p>
454
- `;
455
- addMessage(welcomeMessage, 'ai', true);
456
- }, 1000);
457
  }
458
  });
459
  </script>
 
22
  --quote-color: #4CAF50;
23
  }
24
 
25
+ /* Reset and Base Styles */
26
  * {
27
  box-sizing: border-box;
28
  margin: 0;
 
31
 
32
  html, body {
33
  height: 100%;
34
+ overflow: hidden; /* Prevent scrolling on main body */
35
  }
36
 
37
  body {
 
49
  flex-direction: column;
50
  height: 100%;
51
  width: 100%;
52
+ max-width: 800px; /* Max width for larger screens */
53
+ margin: 0 auto; /* Center the container */
54
+ border-left: 1px solid var(--border-color); /* Optional: add subtle borders */
55
+ border-right: 1px solid var(--border-color);
56
  }
57
 
58
  .chat-area {
59
+ flex-grow: 1; /* Takes remaining vertical space */
60
+ overflow-y: auto; /* Enable scrolling for chat messages */
61
  padding: 20px 16px;
62
  display: flex;
63
  flex-direction: column;
64
  }
65
 
66
+ /* Welcome Screen */
67
  .welcome-screen {
68
  text-align: center;
69
+ margin: auto; /* Center content vertically and horizontally */
70
  color: var(--placeholder-color);
71
+ padding: 20px;
72
  }
73
 
74
  .welcome-screen h2 {
 
77
  margin-bottom: 10px;
78
  }
79
 
80
+ .welcome-screen p {
81
+ font-size: 1rem;
82
+ line-height: 1.5;
83
+ }
84
+
85
  .welcome-screen.hidden {
86
+ display: none; /* Hide when chat starts */
87
  }
88
 
89
+ /* Message Bubbles */
90
  .message {
91
+ max-width: 85%; /* Limit bubble width */
92
  padding: 12px 16px;
93
  border-radius: 18px;
94
  margin-bottom: 12px;
95
+ word-wrap: break-word; /* Ensure long words wrap */
96
  line-height: 1.6;
97
  font-size: 1rem;
98
+ position: relative; /* For audio button positioning */
99
+ animation: fadeIn 0.3s ease-out; /* Fade in animation for new messages */
100
  }
101
 
102
  .user-message {
103
  background-color: var(--user-bubble-color);
104
+ align-self: flex-end; /* Align user messages to the right */
105
+ border-bottom-right-radius: 4px; /* Slight variation for visual appeal */
106
  }
107
 
108
  .ai-message {
109
  background-color: var(--ai-bubble-color);
110
+ align-self: flex-start; /* Align AI messages to the left */
111
+ border-bottom-left-radius: 4px; /* Slight variation for visual appeal */
112
+ padding-bottom: 30px; /* Space for audio button at the bottom */
113
  }
114
 
115
+ /* AI Message Content Styling (for markdown-converted HTML) */
116
  .ai-message-content {
117
+ overflow: hidden; /* Contains floats/margins */
118
+ }
119
+ .ai-message-content p { margin-bottom: 12px; }
120
+ .ai-message-content p:last-child { margin-bottom: 0; } /* No bottom margin on last paragraph */
121
+ .ai-message-content strong { color: #ffffff; font-weight: 600; }
122
+ .ai-message-content em { color: #d1d1d1; font-style: italic; }
123
+ .ai-message-content a { color: var(--link-color); text-decoration: none; word-break: break-all; }
124
+ .ai-message-content a:hover { text-decoration: underline; }
125
+ .ai-message-content ul, .ai-message-content ol { padding-left: 24px; margin-bottom: 12px; }
126
+ .ai-message-content li { margin-bottom: 6px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  .ai-message-content blockquote {
128
  border-left: 3px solid var(--quote-color);
129
  padding-left: 12px;
 
131
  color: #bdbdbd;
132
  margin-bottom: 12px;
133
  }
134
+ /* Code Blocks */
135
  .ai-message-content pre {
136
  background-color: var(--code-bg-color);
137
  border-radius: 6px;
138
  padding: 12px;
139
+ overflow-x: auto; /* Enable horizontal scrolling for long code lines */
140
  margin-bottom: 12px;
141
  }
 
142
  .ai-message-content code {
143
  font-family: 'Courier New', Courier, monospace;
144
  background-color: var(--code-bg-color);
 
146
  border-radius: 3px;
147
  color: var(--code-text-color);
148
  font-size: 0.9em;
149
+ white-space: pre-wrap; /* Preserve whitespace and wrap long lines */
150
+ }
151
+ /* Inline code styling specific for the `code-block` class which is on `pre` */
152
+ .ai-message-content .code-block code {
153
+ padding: 0; /* Code inside pre shouldn't have extra padding */
154
+ background-color: transparent; /* No background for code inside pre */
155
+ color: inherit; /* Inherit color from pre */
156
  }
157
 
158
+ /* Audio Button Styling */
159
+ .audio-button {
160
+ position: absolute;
161
+ bottom: 6px;
162
+ right: 8px;
163
+ background: rgba(255, 255, 255, 0.1); /* Semi-transparent background */
164
+ border: none;
165
+ border-radius: 50%;
166
+ width: 30px;
167
+ height: 30px;
168
+ display: flex;
169
+ align-items: center;
170
+ justify-content: center;
171
+ cursor: pointer;
172
+ transition: background-color 0.2s;
173
+ z-index: 10; /* Ensure it's above message content */
174
  }
175
+ .audio-button:hover {
176
+ background: rgba(255, 255, 255, 0.2);
177
+ }
178
+ .audio-button svg {
179
+ width: 18px;
180
+ height: 18px;
181
+ fill: var(--icon-color);
182
+ transition: fill 0.2s;
183
+ }
184
+ .audio-button:hover svg {
185
+ fill: white; /* Icon brighter on hover */
186
+ }
187
+ /* Loading spinner for audio button */
188
+ .audio-button.loading .speaker-icon {
189
+ display: none; /* Hide speaker icon when loading */
190
+ }
191
+ .audio-button .loader {
192
+ display: none; /* Hidden by default */
193
+ width: 18px;
194
+ height: 18px;
195
+ border: 2px solid #f3f3f3; /* Light grey border */
196
+ border-top: 2px solid var(--send-button-color); /* Green top border for spinner */
197
+ border-radius: 50%;
198
+ animation: spin 1s linear infinite; /* Spin animation */
199
+ }
200
+ .audio-button.loading .loader {
201
+ display: block; /* Show spinner when loading */
202
+ }
203
+ @keyframes spin {
204
+ 0% { transform: rotate(0deg); }
205
+ 100% { transform: rotate(360deg); }
206
+ }
207
+
208
+ /* Typing Indicator */
209
  .typing-indicator {
210
  display: flex;
211
  align-items: center;
212
  padding: 12px 16px;
213
  }
214
  .typing-indicator span {
215
+ height: 8px; width: 8px; background-color: var(--icon-color); border-radius: 50%; display: inline-block; margin: 0 2px;
 
 
 
 
 
216
  animation: bounce 1.4s infinite both;
217
  }
218
  .typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
 
223
  40% { transform: scale(1.0); }
224
  }
225
 
226
+ /* Input Area */
227
  .input-area {
228
  display: flex;
229
+ align-items: flex-end; /* Align items to the bottom (textarea can grow) */
230
  padding: 10px 16px;
231
  border-top: 1px solid var(--border-color);
232
  background-color: var(--input-area-color);
233
+ flex-shrink: 0; /* Prevent shrinking */
234
  }
 
235
  .input-wrapper {
236
  display: flex;
237
  align-items: center;
238
  width: 100%;
239
  background-color: var(--user-bubble-color);
240
+ border-radius: 24px; /* Pill-shaped input */
241
  padding: 4px;
242
  }
 
243
  .input-area textarea {
244
+ flex-grow: 1; /* Takes up available space */
245
  border: none;
246
  background: transparent;
247
  color: var(--text-color);
248
+ resize: none; /* Disable manual resize */
249
  font-size: 1rem;
250
  line-height: 1.5;
251
+ max-height: 120px; /* Max height for textarea */
252
+ overflow-y: auto; /* Scroll if content exceeds max height */
253
  padding: 8px 12px;
254
+ font-family: inherit; /* Inherit font from body */
255
+ outline: none; /* Remove focus outline */
 
 
 
 
 
 
 
256
  }
257
+ .input-area textarea::placeholder { color: var(--placeholder-color); }
258
 
259
+ /* Icon Buttons (Attach, Send) */
260
  .input-area .icon-button {
261
  background: none;
262
  border: none;
 
265
  display: flex;
266
  align-items: center;
267
  justify-content: center;
268
+ transition: opacity 0.2s;
269
  }
270
+ .input-area .icon-button:hover {
271
+ opacity: 0.8;
 
 
 
272
  }
273
+ .input-area .icon-button svg { width: 24px; height: 24px; fill: var(--icon-color); }
274
 
275
  .send-button {
276
  background-color: var(--send-button-color);
277
  border-radius: 50%;
278
  padding: 8px;
279
+ transition: background-color 0.2s, opacity 0.2s;
280
  }
 
281
  .send-button.disabled {
282
  background-color: #555;
283
  cursor: not-allowed;
284
+ opacity: 0.6;
285
  }
 
286
  .send-button.disabled svg {
287
  fill: #888;
288
  }
289
+ .send-button svg { fill: white; width: 24px; height: 24px; }
 
 
 
 
 
290
 
291
  /* Animation for new messages */
292
  @keyframes fadeIn {
293
  from { opacity: 0; transform: translateY(10px); }
294
  to { opacity: 1; transform: translateY(0); }
295
  }
 
 
 
 
296
  </style>
297
  </head>
298
  <body>
299
  <div class="chat-container">
300
  <main class="chat-area">
301
  <div class="welcome-screen">
302
+ <h2>Welcome to AstroChat!</h2>
303
+ <p>Hello! I'm your AI assistant powered by Gemini. Ask me anything - I can explain concepts, generate ideas, or help with coding!</p>
304
+ <p>Try asking me something like: <em>"Explain quantum computing in simple terms"</em> or <em>"Help me debug this Python code"</em></p>
305
  </div>
306
  </main>
307
 
 
327
  const sendButton = document.getElementById('send-button');
328
  const attachButton = document.getElementById('attach-button');
329
 
330
+ // Global variable to hold the currently playing audio object
331
+ let currentAudio = null;
332
+
333
+ // Adjusts the height of the textarea based on content
334
  const adjustTextareaHeight = () => {
335
+ messageInput.style.height = 'auto'; // Reset height
336
+ messageInput.style.height = `${messageInput.scrollHeight}px`; // Set to scroll height
337
+ updateSendButtonState(); // Update send button state after height adjustment
338
  };
339
 
340
+ // Updates the disabled state of the send button
341
  const updateSendButtonState = () => {
342
+ const isDisabled = messageInput.value.trim() === '';
343
+ sendButton.classList.toggle('disabled', isDisabled);
344
+ sendButton.disabled = isDisabled;
 
 
 
 
345
  };
346
 
347
+ // Scrolls the chat area to the bottom
348
  const scrollToBottom = () => {
349
  chatArea.scrollTop = chatArea.scrollHeight;
350
  };
351
 
352
+ // Adds a message to the chat area
353
+ // `plainText` is used specifically for audio generation, keeping original text without HTML
354
+ const addMessage = (content, sender, isHTML = false, plainText = '') => {
355
+ welcomeScreen.classList.add('hidden'); // Hide welcome screen once chat begins
 
356
 
357
  const messageElement = document.createElement('div');
358
  messageElement.classList.add('message', `${sender}-message`);
359
 
360
+ const contentDiv = document.createElement('div');
361
  if (isHTML) {
 
362
  contentDiv.classList.add(`${sender}-message-content`);
363
+ contentDiv.innerHTML = content; // Set innerHTML for HTML content
 
364
  } else {
365
+ contentDiv.textContent = content; // Set textContent for plain text
366
+ }
367
+ messageElement.appendChild(contentDiv);
368
+
369
+ // Add audio button for AI messages
370
+ if (sender === 'ai' && plainText) {
371
+ const audioButton = document.createElement('button');
372
+ audioButton.classList.add('audio-button');
373
+ // Store the plain text directly on the button for easy retrieval
374
+ audioButton.dataset.plainText = plainText;
375
+ audioButton.innerHTML = `
376
+ <svg class="speaker-icon" viewBox="0 0 24 24">
377
+ <path d="M3 10v4c0 .55.45 1 1 1h3.5l5.5 5V5L7.5 9H4c-.55 0-1 .45-1 1zm14 0h-1.5c-2.31 0-4.22-1.74-4.47-4H10v12h1.5v-2.22c.25-2.26 2.16-4.22 4.47-4.22H17c1.1 0 2 .9 2 2s-.9 2-2 2h-1.5v2H17c2.21 0 4-1.79 4-4s-1.79-4-4-4z"/>
378
+ </svg>
379
+ <div class="loader"></div>
380
+ `;
381
+ audioButton.title = "Listen to response"; // Add tooltip
382
+ audioButton.addEventListener('click', () => playAudio(audioButton, plainText));
383
+ messageElement.appendChild(audioButton);
384
  }
385
 
386
  chatArea.appendChild(messageElement);
 
388
  return messageElement;
389
  };
390
 
391
+ // Displays a typing indicator for the AI
392
  const showTypingIndicator = () => {
393
  const indicatorElement = document.createElement('div');
394
  indicatorElement.classList.add('message', 'ai-message', 'typing-indicator');
395
  indicatorElement.innerHTML = '<span></span><span></span><span></span>';
396
  chatArea.appendChild(indicatorElement);
397
  scrollToBottom();
398
+ return indicatorElement; // Return element to remove it later
399
  };
400
 
401
+ // Plays audio for a given text
402
+ const playAudio = async (buttonElement, text) => {
403
+ // Stop any currently playing audio
404
+ if (currentAudio) {
405
+ currentAudio.pause();
406
+ currentAudio.currentTime = 0;
407
+ currentAudio = null;
408
+ }
409
+
410
+ buttonElement.classList.add('loading'); // Show loading spinner
411
+ buttonElement.disabled = true; // Disable button during loading
412
+
413
+ try {
414
+ const response = await fetch('/generate-audio', {
415
+ method: 'POST',
416
+ headers: { 'Content-Type': 'application/json' },
417
+ body: JSON.stringify({ text: text })
418
+ });
419
+
420
+ const data = await response.json();
421
+
422
+ if (data.error) {
423
+ throw new Error(data.error);
424
+ }
425
+
426
+ currentAudio = new Audio(data.audio_url);
427
+ currentAudio.play();
428
+
429
+ // Remove loading state when audio ends
430
+ currentAudio.onended = () => {
431
+ buttonElement.classList.remove('loading');
432
+ buttonElement.disabled = false;
433
+ currentAudio = null;
434
+ };
435
+
436
+ // Handle audio playback errors
437
+ currentAudio.onerror = () => {
438
+ console.error("Error playing audio.");
439
+ alert("Could not play audio. Please try again.");
440
+ buttonElement.classList.remove('loading');
441
+ buttonElement.disabled = false;
442
+ currentAudio = null;
443
+ };
444
+
445
+ } catch (error) {
446
+ console.error("Error generating audio:", error);
447
+ alert("Failed to generate audio. " + (error.message || "Please try again."));
448
+ buttonElement.classList.remove('loading');
449
+ buttonElement.disabled = false;
450
+ }
451
+ };
452
+
453
+
454
+ // Fetches AI response from the backend
455
  const getAIResponse = async (userMessage) => {
456
+ const indicator = showTypingIndicator(); // Show typing indicator
457
 
458
  try {
459
  const response = await fetch('/chat', {
 
470
  throw new Error(data.error);
471
  }
472
 
473
+ // Remove typing indicator before adding the actual message
474
  chatArea.removeChild(indicator);
475
+ // Add AI message with both HTML and plain text
476
+ addMessage(data.response_html, 'ai', true, data.response_text);
477
 
478
  } catch (error) {
479
+ chatArea.removeChild(indicator); // Remove indicator on error too
480
  addMessage("Sorry, I encountered an error. Please try again.", 'ai');
481
  console.error("Error:", error);
482
  }
483
  };
484
 
485
+ // Event listener for form submission
486
  chatForm.addEventListener('submit', async (e) => {
487
+ e.preventDefault(); // Prevent default form submission behavior (page reload)
488
  const message = messageInput.value.trim();
489
 
490
  if (message) {
491
+ addMessage(message, 'user'); // Add user message to chat
492
+ messageInput.value = ''; // Clear input field
493
+ adjustTextareaHeight(); // Reset textarea height
494
 
495
+ await getAIResponse(message); // Get AI response
496
  }
497
  });
498
 
499
+ // Event listener for Enter key (send message) and Shift+Enter (new line)
500
  messageInput.addEventListener('keydown', (e) => {
501
  if (e.key === 'Enter' && !e.shiftKey) {
502
+ e.preventDefault(); // Prevent new line
503
+ chatForm.dispatchEvent(new Event('submit')); // Trigger form submission
504
  }
505
  });
506
 
507
+ // Event listener for input changes to adjust textarea height and send button state
508
  messageInput.addEventListener('input', adjustTextareaHeight);
509
 
510
+ // Attach button (placeholder functionality)
511
  attachButton.addEventListener('click', () => {
512
+ // In a real app, this would open a file dialog
513
+ alert("Attachment feature not implemented yet!");
514
+ messageInput.focus(); // Keep focus on message input
515
  });
516
 
517
+ // Initial setup on page load
518
+ updateSendButtonState(); // Set initial state of send button
519
+ messageInput.focus(); // Focus on the input field
520
 
521
+ // Optional: Initial welcome message from AI on first visit
522
  const isFirstVisit = !localStorage.getItem('chatVisited');
523
  if (isFirstVisit) {
524
  localStorage.setItem('chatVisited', 'true');
525
+ // Use a timeout to allow page to load fully before first message appears
526
  setTimeout(() => {
527
+ // The welcome screen now has content, so we just let it display initially.
528
+ // If you want an AI message to appear *after* the welcome screen and replace it,
529
+ // you'd use addMessage here instead of directly rendering text in welcome-screen div.
530
+ // For now, the welcome screen itself has the initial greeting.
531
+ }, 500);
 
 
 
 
 
 
 
532
  }
533
  });
534
  </script>