diff --git a/app/components/artifacts.tsx b/app/components/artifacts.tsx
index 6c0164b91..4ae7170d5 100644
--- a/app/components/artifacts.tsx
+++ b/app/components/artifacts.tsx
@@ -23,84 +23,89 @@ import { Path, ApiPath, REPO_URL } from "@/app/constant";
 import { Loading } from "./home";
 import styles from "./artifacts.module.scss";
 
-export const HTMLPreview = forwardRef<
-  {
-    reload: () => void;
-  },
-  {
-    code: string;
-    autoHeight?: boolean;
-    height?: number | string;
-    onLoad?: (title?: string) => void;
-  }
->(function HTMLPreview(props, ref) {
-  const iframeRef = useRef<HTMLIFrameElement>(null);
-  const [frameId, setFrameId] = useState<string>(nanoid());
-  const [iframeHeight, setIframeHeight] = useState(600);
-  const [title, setTitle] = useState("");
-  /*
-   * https://stackoverflow.com/questions/19739001/what-is-the-difference-between-srcdoc-and-src-datatext-html-in-an
-   * 1. using srcdoc
-   * 2. using src with dataurl:
-   *    easy to share
-   *    length limit (Data URIs cannot be larger than 32,768 characters.)
-   */
+type HTMLPreviewProps = {
+  code: string;
+  autoHeight?: boolean;
+  height?: number | string;
+  onLoad?: (title?: string) => void;
+};
 
-  useEffect(() => {
-    const handleMessage = (e: any) => {
-      const { id, height, title } = e.data;
-      setTitle(title);
-      if (id == frameId) {
-        setIframeHeight(height);
+export type HTMLPreviewHander = {
+  reload: () => void;
+};
+
+export const HTMLPreview = forwardRef<HTMLPreviewHander, HTMLPreviewProps>(
+  function HTMLPreview(props, ref) {
+    const iframeRef = useRef<HTMLIFrameElement>(null);
+    const [frameId, setFrameId] = useState<string>(nanoid());
+    const [iframeHeight, setIframeHeight] = useState(600);
+    const [title, setTitle] = useState("");
+    /*
+     * https://stackoverflow.com/questions/19739001/what-is-the-difference-between-srcdoc-and-src-datatext-html-in-an
+     * 1. using srcdoc
+     * 2. using src with dataurl:
+     *    easy to share
+     *    length limit (Data URIs cannot be larger than 32,768 characters.)
+     */
+
+    useEffect(() => {
+      const handleMessage = (e: any) => {
+        const { id, height, title } = e.data;
+        setTitle(title);
+        if (id == frameId) {
+          setIframeHeight(height);
+        }
+      };
+      window.addEventListener("message", handleMessage);
+      return () => {
+        window.removeEventListener("message", handleMessage);
+      };
+    }, [frameId]);
+
+    useImperativeHandle(ref, () => ({
+      reload: () => {
+        setFrameId(nanoid());
+      },
+    }));
+
+    const height = useMemo(() => {
+      if (!props.autoHeight) return props.height || 600;
+      if (typeof props.height === "string") {
+        return props.height;
+      }
+      const parentHeight = props.height || 600;
+      return iframeHeight + 40 > parentHeight
+        ? parentHeight
+        : iframeHeight + 40;
+    }, [props.autoHeight, props.height, iframeHeight]);
+
+    const srcDoc = useMemo(() => {
+      const script = `<script>new ResizeObserver((entries) => parent.postMessage({id: '${frameId}', height: entries[0].target.clientHeight}, '*')).observe(document.body)</script>`;
+      if (props.code.includes("</head>")) {
+        props.code.replace("</head>", "</head>" + script);
+      }
+      return props.code + script;
+    }, [props.code, frameId]);
+
+    const handleOnLoad = () => {
+      if (props?.onLoad) {
+        props.onLoad(title);
       }
     };
-    window.addEventListener("message", handleMessage);
-    return () => {
-      window.removeEventListener("message", handleMessage);
-    };
-  }, [frameId]);
 
-  useImperativeHandle(ref, () => ({
-    reload: () => {
-      setFrameId(nanoid());
-    },
-  }));
-
-  const height = useMemo(() => {
-    if (!props.autoHeight) return props.height || 600;
-    if (typeof props.height === "string") {
-      return props.height;
-    }
-    const parentHeight = props.height || 600;
-    return iframeHeight + 40 > parentHeight ? parentHeight : iframeHeight + 40;
-  }, [props.autoHeight, props.height, iframeHeight]);
-
-  const srcDoc = useMemo(() => {
-    const script = `<script>new ResizeObserver((entries) => parent.postMessage({id: '${frameId}', height: entries[0].target.clientHeight}, '*')).observe(document.body)</script>`;
-    if (props.code.includes("</head>")) {
-      props.code.replace("</head>", "</head>" + script);
-    }
-    return props.code + script;
-  }, [props.code, frameId]);
-
-  const handleOnLoad = () => {
-    if (props?.onLoad) {
-      props.onLoad(title);
-    }
-  };
-
-  return (
-    <iframe
-      className={styles["artifacts-iframe"]}
-      key={frameId}
-      ref={iframeRef}
-      sandbox="allow-forms allow-modals allow-scripts"
-      style={{ height }}
-      srcDoc={srcDoc}
-      onLoad={handleOnLoad}
-    />
-  );
-});
+    return (
+      <iframe
+        className={styles["artifacts-iframe"]}
+        key={frameId}
+        ref={iframeRef}
+        sandbox="allow-forms allow-modals allow-scripts"
+        style={{ height }}
+        srcDoc={srcDoc}
+        onLoad={handleOnLoad}
+      />
+    );
+  },
+);
 
 export function ArtifactsShareButton({
   getCode,
@@ -203,7 +208,7 @@ export function Artifacts() {
   const [code, setCode] = useState("");
   const [loading, setLoading] = useState(true);
   const [fileName, setFileName] = useState("");
-  const previewRef = useRef<typeof HTMLPreview>(null);
+  const previewRef = useRef<HTMLPreviewHander>(null);
 
   useEffect(() => {
     if (id) {
diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx
index 717d40793..83fd5b8ab 100644
--- a/app/components/markdown.tsx
+++ b/app/components/markdown.tsx
@@ -14,7 +14,11 @@ import ReloadButtonIcon from "../icons/reload.svg";
 import React from "react";
 import { useDebouncedCallback } from "use-debounce";
 import { showImageModal, FullScreen } from "./ui-lib";
-import { ArtifactsShareButton, HTMLPreview } from "./artifacts";
+import {
+  ArtifactsShareButton,
+  HTMLPreview,
+  HTMLPreviewHander,
+} from "./artifacts";
 import { Plugin } from "../constant";
 import { useChatStore } from "../store";
 import { IconButton } from "./button";
@@ -67,7 +71,7 @@ export function Mermaid(props: { code: string }) {
 
 export function PreCode(props: { children: any }) {
   const ref = useRef<HTMLPreElement>(null);
-  const previewRef = useRef<typeof HTMLPreview>(null);
+  const previewRef = useRef<HTMLPreviewHander>(null);
   const [mermaidCode, setMermaidCode] = useState("");
   const [htmlCode, setHtmlCode] = useState("");
   const { height } = useWindowSize();