diff --git a/conversations.php b/conversations.php index 29eb982..5e5f38f 100644 --- a/conversations.php +++ b/conversations.php @@ -2353,6 +2353,16 @@ const container = document.getElementById('chat-conversations'); if (!container) return; + // Safety: remove any empty text-only messages that may have slipped into the model + try { + const before = this.conversations ? this.conversations.length : 0; + if (Array.isArray(this.conversations) && this.conversations.length > 0) { + this.conversations = this.conversations.filter(m => !this.isEmptyMessage(m)); + const removed = before - this.conversations.length; + if (removed > 0 && console && console.info) console.info('Removed', removed, 'empty messages before render'); + } + } catch (e) { console.warn('Failed to trim empty messages before render', e); } + if (initial) { container.innerHTML = ''; } @@ -2372,6 +2382,16 @@ const mid = String(msg.message_id || msg.id || ('local_' + i)); const existingEl = existing.get(mid); + // Skip truly empty messages (safety net) and remove any existing placeholder nodes + if (this.isEmptyMessage(msg)) { + if (existingEl && existingEl.parentNode) { + existingEl.parentNode.removeChild(existingEl); + existing.delete(mid); + if (console && console.info) console.info('Removed empty DOM message', mid); + } + continue; + } + if (existingEl) { // update in place try { @@ -2552,6 +2572,16 @@ return `${m.direction||'?'}|${m.message_type||m.media_type||'text'}|${partMedia}|${ts}`; } + // Helper: determine if a message is effectively empty (no text, no media, and is text type) + isEmptyMessage(m) { + if (!m) return true; + const content = (typeof m.content !== 'undefined' && m.content !== null) ? String(m.content).trim() : ''; + const hasContent = content.length > 0; + const hasMedia = !!(m.local_thumb || m.local_file || m.media_url_external || m.media_url); + const nonText = m.message_type && m.message_type !== 'text'; + return !(hasContent || hasMedia || nonText); + } + // Initialize recording handlers (so mic works before openConversation) and helper to manage recording state initRecordingHandlers() { if (this._recordingHandlersInitialized) return;