From 2081ee8000c9f4f39a2eef5ab20ec9b6048c3891 Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Wed, 15 Apr 2026 07:37:25 -0300 Subject: [PATCH] fix: Remove raw HTML display during streaming - show loading indicator instead --- ui/suite/partials/chat.html | 110 +++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 51 deletions(-) diff --git a/ui/suite/partials/chat.html b/ui/suite/partials/chat.html index 668be92..32b5581 100644 --- a/ui/suite/partials/chat.html +++ b/ui/suite/partials/chat.html @@ -307,32 +307,32 @@ function addMessage(sender, content, msgId) { '
' + processedContent + "
"; - } else { - var cleanContent = stripMarkdownBlocks(content); - // Check if content has HTML (any tag, including comments) - var hasHtmlTags = /<\/?[a-zA-Z][^>]*>|/i.test(cleanContent); - console.log("Bot message - hasHtmlTags:", hasHtmlTags, "content length:", cleanContent.length, "msgId:", msgId); + } else { + var cleanContent = stripMarkdownBlocks(content); + // Check if content has HTML (any tag, including comments) + var hasHtmlTags = /<\/?[a-zA-Z][^>]*>|/i.test(cleanContent); + console.log("Bot message - hasHtmlTags:", hasHtmlTags, "content length:", cleanContent.length, "msgId:", msgId); - var parsed; - if (hasHtmlTags && msgId) { - // Streaming HTML content - show as text initially to avoid broken tags - // Will be rendered as HTML at finalizeStreaming - parsed = escapeHtml(cleanContent); - } else if (hasHtmlTags) { - // Complete HTML content - render directly - parsed = cleanContent; - } else { - // Markdown content - parsed = typeof marked !== "undefined" && marked.parse - ? marked.parse(cleanContent) - : escapeHtml(cleanContent); - } - parsed = renderMentionInMessage(parsed); - div.innerHTML = - '
' + - parsed + - "
"; - } + var parsed; + if (hasHtmlTags && msgId) { + // Streaming HTML content - don't show raw HTML, show loading indicator + // Content will be rendered at finalizeStreaming + parsed = '
...
'; + } else if (hasHtmlTags) { + // Complete HTML content - render directly + parsed = cleanContent; + } else { + // Markdown content + parsed = typeof marked !== "undefined" && marked.parse + ? marked.parse(cleanContent) + : escapeHtml(cleanContent); + } + parsed = renderMentionInMessage(parsed); + div.innerHTML = + '
' + + parsed + + "
"; + } messages.appendChild(div); @@ -775,33 +775,41 @@ function addMessage(sender, content, msgId) { return true; } - function updateStreaming(content) { - var el = document.getElementById(streamingMessageId); - if (!el) return; - - var msgContent = el.querySelector(".message-content"); - var cleanContent = stripMarkdownBlocks(content); - - // Detect if it is HTML - var isHtml = /<\/?[a-zA-Z][^>]*>|/i.test(cleanContent); - - if (isHtml) { - // If tags are balanced or it's been too long, render - if (isTagBalanced(cleanContent) || (Date.now() - lastRenderTime > 2000)) { - msgContent.innerHTML = renderMentionInMessage(cleanContent); - lastRenderTime = Date.now(); - if (!isUserScrolling) scrollToBottom(true); - } - } else { - // Standard markdown/text rendering - var parsed = typeof marked !== "undefined" && marked.parse - ? marked.parse(cleanContent) - : escapeHtml(cleanContent); - parsed = renderMentionInMessage(parsed); - msgContent.innerHTML = parsed; - if (!isUserScrolling) scrollToBottom(true); - } +function updateStreaming(content) { + var el = document.getElementById(streamingMessageId); + if (!el) return; + + var msgContent = el.querySelector(".message-content"); + var cleanContent = stripMarkdownBlocks(content); + + // Detect if it is HTML + var isHtml = /<\/?[a-zA-Z][^>]*>|/i.test(cleanContent); + + if (isHtml) { + // If tags are balanced or it's been too long, render as HTML + if (isTagBalanced(cleanContent) || (Date.now() - lastRenderTime > 2000)) { + msgContent.innerHTML = renderMentionInMessage(cleanContent); + lastRenderTime = Date.now(); + if (!isUserScrolling) scrollToBottom(true); + } else { + // Tags not balanced yet - show escaped HTML to avoid showing raw tags + // This prevents HTML from appearing as raw text before it's ready + var escapedContent = escapeHtml(cleanContent); + // Only update if content changed to avoid flickering + if (msgContent.textContent !== cleanContent) { + msgContent.textContent = cleanContent; } + } + } else { + // Standard markdown/text rendering + var parsed = typeof marked !== "undefined" && marked.parse + ? marked.parse(cleanContent) + : escapeHtml(cleanContent); + parsed = renderMentionInMessage(parsed); + msgContent.innerHTML = parsed; + if (!isUserScrolling) scrollToBottom(true); + } +} function finalizeStreaming() { var el = document.getElementById(streamingMessageId);