()
| 170 | } |
| 171 | |
| 172 | export function send() { |
| 173 | if (S.waiting) { |
| 174 | if (S.ws && S.ws.readyState === WebSocket.OPEN && S.authenticated) { |
| 175 | S.ws.send(JSON.stringify({ type: 'interrupt' })); |
| 176 | } |
| 177 | return; |
| 178 | } |
| 179 | const t = $input.value.trim(); |
| 180 | const hasImage = !!pendingImage; |
| 181 | if ((!t && !hasImage) || !S.ws || S.ws.readyState !== WebSocket.OPEN || !S.authenticated) return; |
| 182 | debugLog('send_invoked', { |
| 183 | hasImage, |
| 184 | textPreview: t.slice(0, 80), |
| 185 | sessionId: S.sessionId || null, |
| 186 | waiting: S.waiting, |
| 187 | }); |
| 188 | |
| 189 | const slashCommand = normalizeSlashCommandInput(t); |
| 190 | if (!hasImage && COMMANDS.some(command => command.name === slashCommand)) { |
| 191 | execCmd(slashCommand); |
| 192 | return; |
| 193 | } |
| 194 | |
| 195 | removeWelcome(); closeGroup(); |
| 196 | |
| 197 | const el = document.createElement('div'); |
| 198 | el.className = 'user-msg'; el.dataset.optimistic = '1'; |
| 199 | let html = ''; |
| 200 | if (hasImage) { |
| 201 | if (pendingImage.status === 'failed') { |
| 202 | showToast('Image upload failed. Re-select the image and try again.'); |
| 203 | return; |
| 204 | } |
| 205 | html += `<img src="${pendingImage.previewUrl}" style="max-width:200px;max-height:120px;border-radius:8px;display:block;margin-bottom:${t ? '6px' : '0'}">`; |
| 206 | } |
| 207 | if (t) html += esc(t).replace(/\n/g, '<br>'); |
| 208 | el.innerHTML = html; |
| 209 | $msgs.appendChild(el); |
| 210 | S.isAtBottom = true; scrollEnd(); |
| 211 | scheduleSessionCacheSave(); |
| 212 | |
| 213 | if (hasImage) { |
| 214 | pendingImage.submitQueued = true; |
| 215 | pendingImage.queuedText = t || ''; |
| 216 | } else { |
| 217 | S.ws.send(JSON.stringify({ type: 'chat', text: t })); |
| 218 | } |
| 219 | |
| 220 | $input.value = ''; $input.style.height = 'auto'; |
| 221 | |
| 222 | if (hasImage && pendingImage.status === 'uploaded') { |
| 223 | submitPendingImageUpload().catch(err => { |
| 224 | if (pendingImage) { |
| 225 | pendingImage.status = 'failed'; |
| 226 | updateImagePreviewUi(); |
| 227 | } |
| 228 | setWaiting(false, 'image_submit_failed'); |
| 229 | showToast(err.message || 'Image submit failed'); |
no test coverage detected