From 43e29d6f9b05b3aa0058d4a1ab3f9a9c85bda606 Mon Sep 17 00:00:00 2001
From: Sean Hatfield <seanhatfield5@gmail.com>
Date: Wed, 19 Feb 2025 12:55:56 +0800
Subject: [PATCH] Markdown support in custom messages (#3267)

* add md support to appearance custom messages

* break out dompurify to util

---------

Co-authored-by: Timothy Carambat <rambat1010@gmail.com>
---
 frontend/src/components/ChatBubble/index.jsx        | 13 ++++++++-----
 frontend/src/components/EditingChatBubble/index.jsx | 11 ++++++++---
 .../ChatHistory/HistoricalMessage/index.jsx         |  7 +------
 frontend/src/utils/chat/purify.js                   |  8 ++++++++
 4 files changed, 25 insertions(+), 14 deletions(-)
 create mode 100644 frontend/src/utils/chat/purify.js

diff --git a/frontend/src/components/ChatBubble/index.jsx b/frontend/src/components/ChatBubble/index.jsx
index 4ffc3d085..7b37cd5d1 100644
--- a/frontend/src/components/ChatBubble/index.jsx
+++ b/frontend/src/components/ChatBubble/index.jsx
@@ -1,6 +1,8 @@
 import React from "react";
 import UserIcon from "../UserIcon";
 import { userFromStorage } from "@/utils/request";
+import renderMarkdown from "@/utils/chat/markdown";
+import DOMPurify from "@/utils/chat/purify";
 
 export default function ChatBubble({ message, type, popMsg }) {
   const isUser = type === "user";
@@ -16,11 +18,12 @@ export default function ChatBubble({ message, type, popMsg }) {
             role={type}
           />
 
-          <span
-            className={`whitespace-pre-line text-white font-normal text-sm md:text-sm flex flex-col gap-y-1 mt-2`}
-          >
-            {message}
-          </span>
+          <div
+            className={`markdown whitespace-pre-line text-white font-normal text-sm md:text-sm flex flex-col gap-y-1 mt-2`}
+            dangerouslySetInnerHTML={{
+              __html: DOMPurify.sanitize(renderMarkdown(message)),
+            }}
+          />
         </div>
       </div>
     </div>
diff --git a/frontend/src/components/EditingChatBubble/index.jsx b/frontend/src/components/EditingChatBubble/index.jsx
index feabd4c6e..652297c79 100644
--- a/frontend/src/components/EditingChatBubble/index.jsx
+++ b/frontend/src/components/EditingChatBubble/index.jsx
@@ -1,6 +1,8 @@
 import React, { useState } from "react";
 import { X } from "@phosphor-icons/react";
 import { useTranslation } from "react-i18next";
+import renderMarkdown from "@/utils/chat/markdown";
+import DOMPurify from "@/utils/chat/purify";
 
 export default function EditingChatBubble({
   message,
@@ -57,9 +59,12 @@ export default function EditingChatBubble({
             />
           ) : (
             tempMessage && (
-              <p className=" font-[500] md:font-semibold text-sm md:text-base break-words light:invert">
-                {tempMessage}
-              </p>
+              <div
+                className="markdown font-[500] md:font-semibold text-sm md:text-base break-words light:invert"
+                dangerouslySetInnerHTML={{
+                  __html: DOMPurify.sanitize(renderMarkdown(tempMessage)),
+                }}
+              />
             )
           )}
         </div>
diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx
index a5d60db4e..5a5454bb2 100644
--- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx
+++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx
@@ -6,7 +6,7 @@ import renderMarkdown from "@/utils/chat/markdown";
 import { userFromStorage } from "@/utils/request";
 import Citations from "../Citation";
 import { v4 } from "uuid";
-import createDOMPurify from "dompurify";
+import DOMPurify from "@/utils/chat/purify";
 import { EditMessageForm, useEditMessage } from "./Actions/EditMessage";
 import { useWatchDeleteMessage } from "./Actions/DeleteMessage";
 import TTSMessage from "./Actions/TTSButton";
@@ -17,11 +17,6 @@ import {
   ThoughtChainComponent,
 } from "../ThoughtContainer";
 
-const DOMPurify = createDOMPurify(window);
-DOMPurify.setConfig({
-  ADD_ATTR: ["target", "rel"],
-});
-
 const HistoricalMessage = ({
   uuid = v4(),
   message,
diff --git a/frontend/src/utils/chat/purify.js b/frontend/src/utils/chat/purify.js
new file mode 100644
index 000000000..a6cf85206
--- /dev/null
+++ b/frontend/src/utils/chat/purify.js
@@ -0,0 +1,8 @@
+import createDOMPurify from "dompurify";
+
+const DOMPurify = createDOMPurify(window);
+DOMPurify.setConfig({
+  ADD_ATTR: ["target", "rel"],
+});
+
+export default DOMPurify;
-- 
GitLab