diff --git a/web/src/components/MemoContent/CodeBlock.tsx b/web/src/components/MemoContent/CodeBlock.tsx index 94ddfeed6..e9b58e4f3 100644 --- a/web/src/components/MemoContent/CodeBlock.tsx +++ b/web/src/components/MemoContent/CodeBlock.tsx @@ -1,13 +1,11 @@ import copy from "copy-to-clipboard"; import hljs from "highlight.js"; import { CopyIcon } from "lucide-react"; -import { useCallback, useMemo } from "react"; +import { useEffect, useMemo } from "react"; import toast from "react-hot-toast"; import { cn } from "@/lib/utils"; import MermaidBlock from "./MermaidBlock"; import { BaseProps } from "./types"; -import "highlight.js/styles/atom-one-dark.css"; -import "highlight.js/styles/github.css"; // Special languages that are rendered differently. enum SpecialLanguage { @@ -37,6 +35,44 @@ const CodeBlock: React.FC = ({ language, content }: Props) => { return ; } + useEffect(() => { + const dynamicImportStyle = async () => { + const isDark = document.documentElement.classList.contains("dark"); + + // Remove any existing highlight.js style + const existingStyle = document.querySelector("style[data-hljs-theme]"); + if (existingStyle) { + existingStyle.remove(); + } + + try { + // Dynamically import the appropriate CSS. + const cssModule = isDark + ? await import("highlight.js/styles/atom-one-dark.css?inline") + : await import("highlight.js/styles/github.css?inline"); + + // Create and inject the style + const style = document.createElement("style"); + style.textContent = cssModule.default; + style.setAttribute("data-hljs-theme", isDark ? "dark" : "light"); + document.head.appendChild(style); + } catch (error) { + console.warn("Failed to load highlight.js theme:", error); + } + }; + + dynamicImportStyle(); + + // Watch for changes to the dark class + const observer = new MutationObserver(dynamicImportStyle); + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ["class"], + }); + + return () => observer.disconnect(); + }, []); + const highlightedCode = useMemo(() => { try { const lang = hljs.getLanguage(formatedLanguage); @@ -55,16 +91,16 @@ const CodeBlock: React.FC = ({ language, content }: Props) => { }).innerHTML; }, [formatedLanguage, content]); - const handleCopyButtonClick = useCallback(() => { + const copyContent = () => { copy(content); toast.success("Copied to clipboard!"); - }, [content]); + }; return (
{formatedLanguage} - +
diff --git a/web/src/components/MemoContent/MermaidBlock.tsx b/web/src/components/MemoContent/MermaidBlock.tsx index 152aa5a10..b8e9dbef8 100644 --- a/web/src/components/MemoContent/MermaidBlock.tsx +++ b/web/src/components/MemoContent/MermaidBlock.tsx @@ -1,37 +1,17 @@ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef } from "react"; interface Props { content: string; } const MermaidBlock: React.FC = ({ content }: Props) => { - const [colorMode, setColorMode] = useState<"light" | "dark">("light"); const mermaidDockBlock = useRef(null); - // Simple dark mode detection useEffect(() => { - const updateMode = () => { - const isDark = document.documentElement.classList.contains("dark"); - setColorMode(isDark ? "dark" : "light"); - }; - - updateMode(); - - // Watch for changes to the dark class - const observer = new MutationObserver(updateMode); - observer.observe(document.documentElement, { - attributes: true, - attributeFilter: ["class"], - }); - - return () => observer.disconnect(); - }, []); - - useEffect(() => { - // Dynamically import mermaid to ensure compatibility with Vite const initializeMermaid = async () => { + const isDark = document.documentElement.classList.contains("dark"); const mermaid = (await import("mermaid")).default; - mermaid.initialize({ startOnLoad: false, theme: colorMode == "dark" ? "dark" : "default" }); + mermaid.initialize({ startOnLoad: false, theme: isDark ? "dark" : "default" }); if (mermaidDockBlock.current) { mermaid.run({ nodes: [mermaidDockBlock.current],