|
142 | 142 | const filePreviewArea = document.getElementById('file-preview-area'); |
143 | 143 | const filePreviewName = document.getElementById('file-preview-name'); |
144 | 144 | const clearFileButton = document.getElementById('clear-file-button'); |
| 145 | + const constellationSelect = document.getElementById('constellationId'); |
145 | 146 |
|
146 | 147 | function scrollChatToBottom() { |
147 | 148 | // A small delay ensures the new content is rendered before scrolling |
|
158 | 159 | }); |
159 | 160 | } |
160 | 161 |
|
| 162 | + function validateChatSubmission() { |
| 163 | + const messageText = chatInput.value.trim(); |
| 164 | + const file = fileInput.files[0]; |
| 165 | + let isValid = true; |
| 166 | + |
| 167 | + if (!constellationSelect.value) { |
| 168 | + constellationSelect.classList.add('is-invalid'); |
| 169 | + isValid = false; |
| 170 | + } else { |
| 171 | + constellationSelect.classList.remove('is-invalid'); |
| 172 | + } |
| 173 | + |
| 174 | + if (!messageText && !file) { |
| 175 | + isValid = false; |
| 176 | + } |
| 177 | + |
| 178 | + return isValid; |
| 179 | + } |
| 180 | + |
| 181 | + // Prevent submission early if invalid (stops HTMX indicator flash) |
| 182 | + chatForm.addEventListener('submit', function(e) { |
| 183 | + if (!validateChatSubmission()) { |
| 184 | + e.preventDefault(); |
| 185 | + e.stopPropagation(); |
| 186 | + e.stopImmediatePropagation(); |
| 187 | + } |
| 188 | + }); |
| 189 | + |
| 190 | + // Remove invalid styling when the user selects a constellation |
| 191 | + constellationSelect.addEventListener('change', function() { |
| 192 | + if (this.value) { |
| 193 | + this.classList.remove('is-invalid'); |
| 194 | + } |
| 195 | + }); |
| 196 | + |
161 | 197 | // Handle file selection to show the preview |
162 | 198 | fileInput.addEventListener('change', function() { |
163 | 199 | if (fileInput.files.length > 0) { |
|
180 | 216 | const messageText = chatInput.value.trim(); |
181 | 217 | const file = fileInput.files[0]; |
182 | 218 |
|
183 | | - if (messageText || file) { |
184 | | - const indicator = document.getElementById('chat-indicator'); |
185 | | - let userMessageContent = ''; |
| 219 | + const indicator = document.getElementById('chat-indicator'); |
| 220 | + let userMessageContent = ''; |
186 | 221 |
|
187 | | - if (file) { |
188 | | - userMessageContent += `<p class="mb-1"><i class="bi bi-file-earmark-arrow-up me-1"></i><em>${escapeHTML(file.name)}</em></p>`; |
189 | | - } |
190 | | - if (messageText) { |
191 | | - userMessageContent += `<p class="mb-0">${escapeHTML(messageText)}</p>`; |
192 | | - } |
| 222 | + if (file) { |
| 223 | + userMessageContent += `<p class="mb-1"><i class="bi bi-file-earmark-arrow-up me-1"></i><em>${escapeHTML(file.name)}</em></p>`; |
| 224 | + } |
| 225 | + if (messageText) { |
| 226 | + userMessageContent += `<p class="mb-0">${escapeHTML(messageText)}</p>`; |
| 227 | + } |
193 | 228 |
|
194 | | - const userMessageHTML = ` |
195 | | - <div class="d-flex mb-3 message-user"> |
196 | | - <div class="p-3 message-content"> |
197 | | - ${userMessageContent} |
198 | | - </div> |
| 229 | + const userMessageHTML = ` |
| 230 | + <div class="d-flex mb-3 message-user"> |
| 231 | + <div class="p-3 message-content"> |
| 232 | + ${userMessageContent} |
199 | 233 | </div> |
200 | | - `; |
| 234 | + </div> |
| 235 | + `; |
201 | 236 |
|
202 | | - // Insert the user's message right before the indicator |
203 | | - indicator?.insertAdjacentHTML('beforebegin', userMessageHTML); |
| 237 | + // Insert the user's message right before the indicator |
| 238 | + indicator?.insertAdjacentHTML('beforebegin', userMessageHTML); |
204 | 239 |
|
205 | | - scrollChatToBottom(); |
206 | | - chatInput.value = ''; // Clear the input field immediately |
207 | | - fileInput.value = ''; // Clear the file input |
208 | | - filePreviewArea.classList.add('d-none'); // Hide the preview |
209 | | - } else { |
210 | | - evt.preventDefault(); // Prevent sending an empty message |
211 | | - } |
| 240 | + scrollChatToBottom(); |
| 241 | + chatInput.value = ''; // Clear the input field immediately |
| 242 | + fileInput.value = ''; // Clear the file input |
| 243 | + filePreviewArea.classList.add('d-none'); // Hide the preview |
212 | 244 | }); |
213 | 245 |
|
214 | 246 | // After the bot response is swapped in, scroll and focus |
|
224 | 256 | chatInput.addEventListener('keydown', function(e) { |
225 | 257 | if (e.key === 'Enter' && !e.shiftKey) { |
226 | 258 | e.preventDefault(); |
227 | | - htmx.trigger('#chat-form', 'submit'); |
| 259 | + if (validateChatSubmission()) { |
| 260 | + htmx.trigger('#chat-form', 'submit'); |
| 261 | + } |
228 | 262 | } |
229 | 263 | }); |
230 | 264 |
|
|
0 commit comments