From 1bcc8dfb6f37e37b1b5f7d5a495144c4600106c0 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Nov 2025 06:11:00 +0000 Subject: [PATCH] refactor(web): use MobX observer for Mermaid theme detection Replace MutationObserver with MobX observer pattern for cleaner, more reactive theme detection in MermaidBlock component. Changes: - Use observer() wrapper from mobx-react-lite - Watch instanceStore.state.theme and userStore.state.userGeneralSetting.theme - Use resolveTheme() utility to handle "system" theme resolution - Remove MutationObserver boilerplate code - Use useMemo for computed theme value Benefits: - Consistent with app's state management architecture (same as App.tsx) - Automatic re-renders via MobX reactivity - Cleaner code with less boilerplate - Better performance - MobX handles optimization - More reliable - reacts to source of truth instead of DOM changes --- .../components/MemoContent/MermaidBlock.tsx | 47 +++++++------------ 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/web/src/components/MemoContent/MermaidBlock.tsx b/web/src/components/MemoContent/MermaidBlock.tsx index 1f0d3afe4..3b09fb986 100644 --- a/web/src/components/MemoContent/MermaidBlock.tsx +++ b/web/src/components/MemoContent/MermaidBlock.tsx @@ -1,6 +1,9 @@ import mermaid from "mermaid"; -import { useEffect, useRef, useState } from "react"; +import { observer } from "mobx-react-lite"; +import { useEffect, useMemo, useRef, useState } from "react"; import { cn } from "@/lib/utils"; +import { instanceStore, userStore } from "@/store"; +import { resolveTheme } from "@/utils/theme"; interface MermaidBlockProps { children?: React.ReactNode; @@ -9,10 +12,10 @@ interface MermaidBlockProps { /** * Maps app theme to Mermaid theme - * @param appTheme - The app's theme value from data-theme attribute + * @param appTheme - The resolved app theme * @returns Mermaid theme name */ -const getMermaidTheme = (appTheme: string | null): "default" | "dark" => { +const getMermaidTheme = (appTheme: string): "default" | "dark" => { switch (appTheme) { case "default-dark": return "dark"; @@ -24,41 +27,23 @@ const getMermaidTheme = (appTheme: string | null): "default" | "dark" => { } }; -/** - * Gets the current theme from the document - */ -const getCurrentTheme = (): string => { - return document.documentElement.getAttribute("data-theme") || "default"; -}; - -export const MermaidBlock = ({ children, className }: MermaidBlockProps) => { +export const MermaidBlock = observer(({ children, className }: MermaidBlockProps) => { const containerRef = useRef(null); const [svg, setSvg] = useState(""); const [error, setError] = useState(""); - const [currentTheme, setCurrentTheme] = useState(getCurrentTheme()); // Extract the code element and its content const codeElement = children as React.ReactElement; const codeContent = String(codeElement?.props?.children || "").replace(/\n$/, ""); - // Watch for theme changes - useEffect(() => { - const observer = new MutationObserver((mutations) => { - mutations.forEach((mutation) => { - if (mutation.type === "attributes" && mutation.attributeName === "data-theme") { - const newTheme = getCurrentTheme(); - setCurrentTheme(newTheme); - } - }); - }); - - observer.observe(document.documentElement, { - attributes: true, - attributeFilter: ["data-theme"], - }); - - return () => observer.disconnect(); - }, []); + // Get current theme from store (reactive via MobX observer) + // This will automatically trigger re-render when theme changes + const currentTheme = useMemo(() => { + const userTheme = userStore.state.userGeneralSetting?.theme; + const instanceTheme = instanceStore.state.theme; + const theme = userTheme || instanceTheme; + return resolveTheme(theme); + }, [userStore.state.userGeneralSetting?.theme, instanceStore.state.theme]); // Render diagram when content or theme changes useEffect(() => { @@ -114,4 +99,4 @@ export const MermaidBlock = ({ children, className }: MermaidBlockProps) => { dangerouslySetInnerHTML={{ __html: svg }} /> ); -}; +});