Some checks failed
BotServer CI / build (push) Failing after 7m9s
- Backend: Add strip_html_tags() function to remove HTML from XLSX cells - Frontend: Strip HTML tags before displaying bot messages - Prompt: Update PROMPT.md to instruct GPT not to show raw HTML Fixes issue where XLSX cell content with HTML formatting was being displayed as raw HTML tags in chat responses.
177 lines
5.8 KiB
JavaScript
177 lines
5.8 KiB
JavaScript
function scrollToBottom(animate) {
|
|
var messages = document.getElementById("messages");
|
|
if (messages) {
|
|
if (animate) {
|
|
messages.scrollTo({ top: messages.scrollHeight, behavior: "smooth" });
|
|
} else {
|
|
messages.scrollTop = messages.scrollHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
function updateScrollButton() {
|
|
var messages = document.getElementById("messages");
|
|
var scrollBtn = document.getElementById("scrollToBottom");
|
|
if (!messages || !scrollBtn) return;
|
|
var isNearBottom = messages.scrollHeight - messages.scrollTop - messages.clientHeight < 100;
|
|
if (isNearBottom) {
|
|
scrollBtn.classList.remove("visible");
|
|
} else {
|
|
scrollBtn.classList.add("visible");
|
|
}
|
|
}
|
|
|
|
function renderMentionInMessage(content) {
|
|
return content.replace(/@(\w+):([^\s]+)/g, function (match, type, name) {
|
|
var entityType = EntityTypes[type.toLowerCase()];
|
|
if (entityType) {
|
|
return '<span class="mention-tag" data-type="' + type + '" data-name="' + escapeHtml(name) + '">' +
|
|
'<span class="mention-icon">' + entityType.icon + "</span>" +
|
|
'<span class="mention-text">@' + type + ":" + escapeHtml(name) + "</span>" +
|
|
"</span>";
|
|
}
|
|
return match;
|
|
});
|
|
}
|
|
|
|
function stripThinkTags(content) {
|
|
// Remove <think>...</think> and anything in between
|
|
return content.replace(/<think>[\s\S]*?(?:<\/think>|$)/gi, "").trim();
|
|
}
|
|
|
|
function stripMarkdownBlocks(content) {
|
|
var cleanContent = stripThinkTags(content);
|
|
var htmlMatch = cleanContent.match(/^```(?:html|xml)?\s*\n([\s\S]+?)\n?```$/i);
|
|
if (htmlMatch) return htmlMatch[1].trim();
|
|
return cleanContent;
|
|
}
|
|
|
|
function addMessage(sender, content, msgId) {
|
|
var messages = document.getElementById("messages");
|
|
if (!messages) return;
|
|
|
|
var div = document.createElement("div");
|
|
div.className = "message " + sender;
|
|
if (msgId) div.id = msgId;
|
|
|
|
if (sender === "user") {
|
|
var processedContent = renderMentionInMessage(escapeHtml(content));
|
|
div.innerHTML = '<div class="message-content user-message">' + processedContent + "</div>";
|
|
} else {
|
|
var cleanContent = stripMarkdownBlocks(content);
|
|
// Strip HTML tags from XLSX content to prevent rendering
|
|
cleanContent = cleanContent.replace(/<[^>]*>/g, "");
|
|
var hasHtmlTags = /<\/?[a-zA-Z][^>]*>|<!--|-->/i.test(cleanContent);
|
|
|
|
var parsed;
|
|
if (msgId) {
|
|
parsed = '<div class="streaming-loading"><span class="loading-dots">...</span></div>';
|
|
} else if (hasHtmlTags) {
|
|
parsed = escapeHtml(cleanContent);
|
|
} else {
|
|
parsed = typeof marked !== "undefined" && marked.parse
|
|
? marked.parse(cleanContent)
|
|
: escapeHtml(cleanContent);
|
|
}
|
|
parsed = renderMentionInMessage(parsed);
|
|
div.innerHTML = '<div class="message-content bot-message">' + parsed + "</div>";
|
|
}
|
|
|
|
messages.appendChild(div);
|
|
|
|
if (!ChatState.isUserScrolling) {
|
|
scrollToBottom(true);
|
|
} else {
|
|
updateScrollButton();
|
|
}
|
|
|
|
setupMentionClickHandlers(div);
|
|
}
|
|
|
|
function isTagBalanced(html) {
|
|
if (!html) return true;
|
|
var lastChevronOpen = html.lastIndexOf('<');
|
|
var lastChevronClose = html.lastIndexOf('>');
|
|
if (lastChevronOpen > lastChevronClose) return false;
|
|
return true;
|
|
}
|
|
|
|
function updateStreaming(content) {
|
|
var el = document.getElementById(ChatState.streamingMessageId);
|
|
if (!el) return;
|
|
|
|
var msgContent = el.querySelector(".message-content");
|
|
var cleanContent = stripMarkdownBlocks(content);
|
|
// Strip HTML tags from XLSX content to prevent rendering
|
|
cleanContent = cleanContent.replace(/<[^>]*>/g, "");
|
|
var isHtml = /<\/?[a-zA-Z][^>]*>|<!--|-->/i.test(cleanContent);
|
|
|
|
if (isHtml) {
|
|
if (isTagBalanced(cleanContent) || (Date.now() - ChatState.lastRenderTime > 2000)) {
|
|
msgContent.innerHTML = renderMentionInMessage(escapeHtml(cleanContent));
|
|
ChatState.lastRenderTime = Date.now();
|
|
if (!ChatState.isUserScrolling) scrollToBottom(true);
|
|
}
|
|
} else {
|
|
var parsed = typeof marked !== "undefined" && marked.parse
|
|
? marked.parse(cleanContent)
|
|
: escapeHtml(cleanContent);
|
|
parsed = renderMentionInMessage(parsed);
|
|
msgContent.innerHTML = parsed;
|
|
if (!ChatState.isUserScrolling) scrollToBottom(true);
|
|
}
|
|
}
|
|
|
|
function finalizeStreaming() {
|
|
var el = document.getElementById(ChatState.streamingMessageId);
|
|
if (el) {
|
|
var cleanContent = stripMarkdownBlocks(ChatState.currentStreamingContent);
|
|
// Strip HTML tags from XLSX content to prevent rendering
|
|
cleanContent = cleanContent.replace(/<[^>]*>/g, "");
|
|
var hasHtmlTags = /<\/?[a-zA-Z][^>]*>|<!--|-->/i.test(cleanContent);
|
|
var parsed = hasHtmlTags
|
|
? escapeHtml(cleanContent)
|
|
: (typeof marked !== "undefined" && marked.parse
|
|
? marked.parse(cleanContent)
|
|
: escapeHtml(cleanContent));
|
|
parsed = renderMentionInMessage(parsed);
|
|
el.querySelector(".message-content").innerHTML = parsed;
|
|
el.removeAttribute("id");
|
|
setupMentionClickHandlers(el);
|
|
if (!ChatState.isUserScrolling) scrollToBottom(true);
|
|
}
|
|
ChatState.streamingMessageId = null;
|
|
ChatState.currentStreamingContent = "";
|
|
ChatState.streamingBuffer = "";
|
|
}
|
|
|
|
function processMessage(data) {
|
|
if (data.is_complete) {
|
|
if (ChatState.isStreaming) {
|
|
finalizeStreaming();
|
|
} else if (data.content && data.content.trim() !== "") {
|
|
addMessage("bot", data.content);
|
|
}
|
|
ChatState.isStreaming = false;
|
|
if (data.suggestions && Array.isArray(data.suggestions) && data.suggestions.length > 0) {
|
|
renderSuggestions(data.suggestions);
|
|
}
|
|
if (data.switchers && Array.isArray(data.switchers) && data.switchers.length > 0) {
|
|
renderBotSwitchers(data.switchers);
|
|
}
|
|
} else {
|
|
if (!ChatState.isStreaming) {
|
|
ChatState.isStreaming = true;
|
|
ChatState.streamingMessageId = "streaming-" + Date.now();
|
|
ChatState.currentStreamingContent = data.content || "";
|
|
addMessage("bot", ChatState.currentStreamingContent, ChatState.streamingMessageId);
|
|
ChatState.lastRenderTime = Date.now();
|
|
} else {
|
|
ChatState.currentStreamingContent += data.content || "";
|
|
var now = Date.now();
|
|
if (now - ChatState.lastRenderTime > ChatState.renderInterval) {
|
|
updateStreaming(ChatState.currentStreamingContent);
|
|
}
|
|
}
|
|
}
|
|
}
|