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
This commit is contained in:
Claude 2025-11-09 06:11:00 +00:00
parent 6fb7520518
commit 1bcc8dfb6f
No known key found for this signature in database
1 changed files with 16 additions and 31 deletions

View File

@ -1,6 +1,9 @@
import mermaid from "mermaid"; 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 { cn } from "@/lib/utils";
import { instanceStore, userStore } from "@/store";
import { resolveTheme } from "@/utils/theme";
interface MermaidBlockProps { interface MermaidBlockProps {
children?: React.ReactNode; children?: React.ReactNode;
@ -9,10 +12,10 @@ interface MermaidBlockProps {
/** /**
* Maps app theme to Mermaid theme * 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 * @returns Mermaid theme name
*/ */
const getMermaidTheme = (appTheme: string | null): "default" | "dark" => { const getMermaidTheme = (appTheme: string): "default" | "dark" => {
switch (appTheme) { switch (appTheme) {
case "default-dark": case "default-dark":
return "dark"; return "dark";
@ -24,41 +27,23 @@ const getMermaidTheme = (appTheme: string | null): "default" | "dark" => {
} }
}; };
/** export const MermaidBlock = observer(({ children, className }: MermaidBlockProps) => {
* Gets the current theme from the document
*/
const getCurrentTheme = (): string => {
return document.documentElement.getAttribute("data-theme") || "default";
};
export const MermaidBlock = ({ children, className }: MermaidBlockProps) => {
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const [svg, setSvg] = useState<string>(""); const [svg, setSvg] = useState<string>("");
const [error, setError] = useState<string>(""); const [error, setError] = useState<string>("");
const [currentTheme, setCurrentTheme] = useState<string>(getCurrentTheme());
// Extract the code element and its content // Extract the code element and its content
const codeElement = children as React.ReactElement; const codeElement = children as React.ReactElement;
const codeContent = String(codeElement?.props?.children || "").replace(/\n$/, ""); const codeContent = String(codeElement?.props?.children || "").replace(/\n$/, "");
// Watch for theme changes // Get current theme from store (reactive via MobX observer)
useEffect(() => { // This will automatically trigger re-render when theme changes
const observer = new MutationObserver((mutations) => { const currentTheme = useMemo(() => {
mutations.forEach((mutation) => { const userTheme = userStore.state.userGeneralSetting?.theme;
if (mutation.type === "attributes" && mutation.attributeName === "data-theme") { const instanceTheme = instanceStore.state.theme;
const newTheme = getCurrentTheme(); const theme = userTheme || instanceTheme;
setCurrentTheme(newTheme); return resolveTheme(theme);
} }, [userStore.state.userGeneralSetting?.theme, instanceStore.state.theme]);
});
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-theme"],
});
return () => observer.disconnect();
}, []);
// Render diagram when content or theme changes // Render diagram when content or theme changes
useEffect(() => { useEffect(() => {
@ -114,4 +99,4 @@ export const MermaidBlock = ({ children, className }: MermaidBlockProps) => {
dangerouslySetInnerHTML={{ __html: svg }} dangerouslySetInnerHTML={{ __html: svg }}
/> />
); );
}; });