diff --git a/src/public/app/widgets/llm/chat_widget.js b/src/public/app/widgets/llm/chat_widget.js
index 15b4b695d..48cea4376 100644
--- a/src/public/app/widgets/llm/chat_widget.js
+++ b/src/public/app/widgets/llm/chat_widget.js
@@ -304,21 +304,30 @@ export default class ChatWidget extends TabAwareWidget {
formatMessageContent(content) {
if (!content) return '';
- // Escape HTML
- let formatted = utils.escapeHtml(content);
-
- // Convert markdown-style code blocks to HTML
- formatted = formatted.replace(/```(\w+)?\n([\s\S]+?)\n```/g, (match, language, code) => {
- return `
${utils.escapeHtml(code)}
`;
+ // First extract code blocks to protect them from HTML escaping
+ const codeBlocks = [];
+ let processedContent = content.replace(/```(\w+)?\n([\s\S]+?)\n```/g, (match, language, code) => {
+ const placeholder = `__CODE_BLOCK_${codeBlocks.length}__`;
+ const languageClass = language ? ` language-${language}` : '';
+ codeBlocks.push(`${utils.escapeHtml(code)}
`);
+ return placeholder;
});
- // Convert inline code
- formatted = formatted.replace(/`([^`]+)`/g, '$1');
+ // Escape HTML in the remaining content
+ processedContent = utils.escapeHtml(processedContent);
+
+ // Convert inline code - look for backticks that weren't part of a code block
+ processedContent = processedContent.replace(/`([^`]+)`/g, '$1');
// Convert line breaks
- formatted = formatted.replace(/\n/g, '
');
+ processedContent = processedContent.replace(/\n/g, '
');
- return formatted;
+ // Restore code blocks
+ codeBlocks.forEach((block, index) => {
+ processedContent = processedContent.replace(`__CODE_BLOCK_${index}__`, block);
+ });
+
+ return processedContent;
}
scrollToBottom() {
diff --git a/src/public/app/widgets/llm_chat_panel.ts b/src/public/app/widgets/llm_chat_panel.ts
index b157fb83f..154b8f1e3 100644
--- a/src/public/app/widgets/llm_chat_panel.ts
+++ b/src/public/app/widgets/llm_chat_panel.ts
@@ -5,6 +5,7 @@ import appContext from "../components/app_context.js";
import utils from "../services/utils.js";
import { t } from "../services/i18n.js";
import libraryLoader from "../services/library_loader.js";
+import { applySyntaxHighlight } from "../services/syntax_highlight.js";
// Import the LLM Chat CSS
(async function() {
@@ -217,6 +218,8 @@ export default class LlmChatPanel extends BasicWidget {
const assistantElement = this.noteContextChatMessages.querySelector('.assistant-message:last-child .message-content');
if (assistantElement) {
assistantElement.innerHTML = this.formatMarkdown(assistantResponse);
+ // Apply syntax highlighting to any code blocks in the updated content
+ applySyntaxHighlight($(assistantElement as HTMLElement));
} else {
this.addMessageToChat('assistant', assistantResponse);
}
@@ -292,6 +295,9 @@ export default class LlmChatPanel extends BasicWidget {
this.noteContextChatMessages.appendChild(messageElement);
+ // Apply syntax highlighting to any code blocks in the message
+ applySyntaxHighlight($(contentElement));
+
// Scroll to bottom
this.chatContainer.scrollTop = this.chatContainer.scrollHeight;
}
@@ -382,12 +388,29 @@ export default class LlmChatPanel extends BasicWidget {
* Format markdown content for display
*/
private formatMarkdown(content: string): string {
- // Simple markdown formatting - could be replaced with a proper markdown library
- return content
+ if (!content) return '';
+
+ // First, extract code blocks to protect them from other replacements
+ const codeBlocks: string[] = [];
+ let processedContent = content.replace(/```(\w+)?\n([\s\S]+?)\n```/gs, (match, language, code) => {
+ const placeholder = `__CODE_BLOCK_${codeBlocks.length}__`;
+ const languageClass = language ? ` language-${language}` : '';
+ codeBlocks.push(`${code}
`);
+ return placeholder;
+ });
+
+ // Apply other markdown formatting
+ processedContent = processedContent
.replace(/\*\*(.*?)\*\*/g, '$1')
.replace(/\*(.*?)\*/g, '$1')
- .replace(/`(.*?)`/g, '$1')
- .replace(/\n/g, '
')
- .replace(/```(.*?)```/gs, '$1
');
+ .replace(/`([^`]+)`/g, '$1')
+ .replace(/\n/g, '
');
+
+ // Restore code blocks
+ codeBlocks.forEach((block, index) => {
+ processedContent = processedContent.replace(`__CODE_BLOCK_${index}__`, block);
+ });
+
+ return processedContent;
}
}