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

Loading…
Cancel
Save