fix(web): make MermaidBlock reactive to system theme changes and improve code quality

- Add system theme listener to detect OS theme changes in real-time
- Refactor to eliminate duplicate theme preference extraction
- Simplify getMermaidTheme function from switch statement to ternary
- Move render guard outside async function for better readability
- Update comments to be more concise and focused

The component now properly re-renders Mermaid diagrams when the OS theme changes while using "system" theme preference.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
pull/5243/head
Johnny 1 week ago
parent fb736c20d3
commit fa3e0fc7f9

@ -3,7 +3,7 @@ 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";
import { resolveTheme, setupSystemThemeListener } from "@/utils/theme";
interface MermaidBlockProps {
children?: React.ReactNode;
@ -12,54 +12,49 @@ interface MermaidBlockProps {
/**
* Maps app theme to Mermaid theme
* @param appTheme - The resolved app theme
* @returns Mermaid theme name
*/
const getMermaidTheme = (appTheme: string): "default" | "dark" => {
switch (appTheme) {
case "default-dark":
return "dark";
case "default":
case "paper":
case "whitewall":
default:
return "default";
}
return appTheme === "default-dark" ? "dark" : "default";
};
export const MermaidBlock = observer(({ children, className }: MermaidBlockProps) => {
const containerRef = useRef<HTMLDivElement>(null);
const [svg, setSvg] = useState<string>("");
const [error, setError] = useState<string>("");
const [systemThemeChange, setSystemThemeChange] = useState(0);
// Extract the code element and its content
// Extract Mermaid code content from children
const codeElement = children as React.ReactElement;
const codeContent = String(codeElement?.props?.children || "").replace(/\n$/, "");
// 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]);
// Get theme preference (reactive via MobX observer)
const themePreference = userStore.state.userGeneralSetting?.theme || instanceStore.state.theme;
// Resolve theme to actual value (handles "system" theme + system theme changes)
const currentTheme = useMemo(() => resolveTheme(themePreference), [themePreference, systemThemeChange]);
// Render diagram when content or theme changes
// Listen for OS theme changes when using "system" theme preference
useEffect(() => {
const renderDiagram = async () => {
if (!codeContent || !containerRef.current) {
return;
}
if (themePreference !== "system") {
return;
}
return setupSystemThemeListener(() => {
setSystemThemeChange((prev) => prev + 1);
});
}, [themePreference]);
// Render Mermaid diagram when content or theme changes
useEffect(() => {
if (!codeContent || !containerRef.current) {
return;
}
const renderDiagram = async () => {
try {
// Generate a unique ID for this diagram
const id = `mermaid-${Math.random().toString(36).substring(7)}`;
// Get the appropriate Mermaid theme for current app theme
const mermaidTheme = getMermaidTheme(currentTheme);
// Initialize mermaid with current theme
mermaid.initialize({
startOnLoad: false,
theme: mermaidTheme,
@ -67,7 +62,6 @@ export const MermaidBlock = observer(({ children, className }: MermaidBlockProps
fontFamily: "inherit",
});
// Render the mermaid diagram
const { svg: renderedSvg } = await mermaid.render(id, codeContent);
setSvg(renderedSvg);
setError("");

Loading…
Cancel
Save