mirror of
https://github.com/usememos/memos.git
synced 2025-12-17 14:19:17 +08:00
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>
This commit is contained in:
parent
fb736c20d3
commit
fa3e0fc7f9
1 changed files with 25 additions and 31 deletions
|
|
@ -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;
|
|
||||||
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
|
// Resolve theme to actual value (handles "system" theme + system theme changes)
|
||||||
|
const currentTheme = useMemo(() => resolveTheme(themePreference), [themePreference, systemThemeChange]);
|
||||||
|
|
||||||
|
// Listen for OS theme changes when using "system" theme preference
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
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 () => {
|
const renderDiagram = async () => {
|
||||||
if (!codeContent || !containerRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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…
Add table
Reference in a new issue