@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
1 lines • 8.05 kB
Source Map (JSON)
{"version":3,"file":"useMermaid.mjs","names":["mermaidPromise: Promise<typeof import('mermaid').default | null> | null"],"sources":["../../src/hooks/useMermaid.ts"],"sourcesContent":["'use client';\n\nimport { useTheme } from 'antd-style';\nimport type { MermaidConfig } from 'mermaid/dist/config.type';\nimport { useEffect, useMemo, useState } from 'react';\nimport { Md5 } from 'ts-md5';\n\n// 缓存已验证的图表内容\nexport const MD5_LENGTH_THRESHOLD = 10_000;\n\n// Application-level cache for rendered Mermaid SVG\n// Key: cacheKey string, Value: Promise<string>\nconst mermaidCache = new Map<string, Promise<string>>();\n\n// Maximum cache size to prevent memory leaks\nconst MAX_CACHE_SIZE = 500;\n\n// Clean up old cache entries when limit is reached\nconst cleanupCache = () => {\n if (mermaidCache.size > MAX_CACHE_SIZE) {\n // Remove oldest 20% of entries\n const entriesToRemove = Math.floor(MAX_CACHE_SIZE * 0.2);\n const keysToRemove = Array.from(mermaidCache.keys()).slice(0, entriesToRemove);\n for (const key of keysToRemove) {\n mermaidCache.delete(key);\n }\n }\n};\n\n// 懒加载 mermaid 实例\nlet mermaidPromise: Promise<typeof import('mermaid').default | null> | null = null;\n\nexport const loadMermaid = (): Promise<typeof import('mermaid').default | null> => {\n if (typeof window === 'undefined') return Promise.resolve(null);\n\n if (!mermaidPromise) {\n mermaidPromise = import('mermaid').then((mod) => mod.default);\n }\n\n return mermaidPromise;\n};\n\n// Helper to create mermaid config\nexport const createMermaidConfig = (\n theme: ReturnType<typeof useTheme>,\n customTheme?: MermaidConfig['theme'],\n): MermaidConfig => ({\n fontFamily: theme.fontFamilyCode,\n gantt: {\n useWidth: 1920,\n },\n securityLevel: 'loose',\n startOnLoad: false,\n theme: customTheme || (theme.isDarkMode ? 'dark' : 'neutral'),\n themeVariables: customTheme\n ? undefined\n : {\n errorBkgColor: theme.colorTextDescription,\n errorTextColor: theme.colorTextDescription,\n fontFamily: theme.fontFamily,\n lineColor: theme.colorTextSecondary,\n mainBkg: theme.colorBgContainer,\n noteBkgColor: theme.colorInfoBg,\n noteTextColor: theme.colorInfoText,\n pie1: theme.geekblue,\n pie2: theme.colorWarning,\n pie3: theme.colorSuccess,\n pie4: theme.colorError,\n primaryBorderColor: theme.colorBorder,\n primaryColor: theme.colorBgContainer,\n primaryTextColor: theme.colorText,\n secondaryBorderColor: theme.colorInfoBorder,\n secondaryColor: theme.colorInfoBg,\n secondaryTextColor: theme.colorInfoText,\n tertiaryBorderColor: theme.colorSuccessBorder,\n tertiaryColor: theme.colorSuccessBg,\n tertiaryTextColor: theme.colorSuccessText,\n textColor: theme.colorText,\n },\n});\n\n/**\n * 验证并处理 Mermaid 图表内容 - 优化版本(移除 SWR)\n */\nexport const useMermaid = (\n content: string,\n {\n id,\n theme: customTheme,\n }: {\n id: string;\n theme?: MermaidConfig['theme'];\n },\n): string => {\n const theme = useTheme();\n const [data, setData] = useState<string>('');\n\n // 提取主题相关配置到 useMemo 中 - 只依赖实际使用的 theme 属性\n const mermaidConfig = useMemo(\n () => createMermaidConfig(theme, customTheme),\n [\n theme.fontFamilyCode,\n theme.isDarkMode,\n theme.colorTextDescription,\n theme.fontFamily,\n theme.colorTextSecondary,\n theme.colorBgContainer,\n theme.colorInfoBg,\n theme.colorInfoText,\n theme.geekblue,\n theme.colorWarning,\n theme.colorSuccess,\n theme.colorError,\n theme.colorBorder,\n theme.colorInfoBorder,\n theme.colorSuccessBorder,\n theme.colorSuccessBg,\n theme.colorSuccessText,\n theme.colorText,\n customTheme,\n ],\n );\n\n // 为长内容生成哈希键\n const cacheKey = useMemo((): string => {\n const hash = content.length < MD5_LENGTH_THRESHOLD ? content : Md5.hashStr(content);\n return [id, customTheme || (theme.isDarkMode ? 'd' : 'l'), hash].filter(Boolean).join('-');\n }, [content, id, theme.isDarkMode, customTheme]);\n\n useEffect(() => {\n // Check cache first\n const cachedPromise = mermaidCache.get(cacheKey);\n if (cachedPromise) {\n cachedPromise\n .then((svg) => {\n setData(svg);\n })\n .catch(() => {\n // Silently handle errors, fallback will be handled in the promise\n });\n return;\n }\n\n // Create new promise for rendering\n const renderPromise = (async (): Promise<string> => {\n try {\n const mermaidInstance = await loadMermaid();\n if (!mermaidInstance) return '';\n\n // 验证语法\n const isValid = await mermaidInstance.parse(content);\n\n if (isValid) {\n // 初始化并渲染\n mermaidInstance.initialize(mermaidConfig);\n const { svg } = await mermaidInstance.render(id, content);\n return svg;\n } else {\n throw new Error('Mermaid 语法无效');\n }\n } catch (error_) {\n console.error('Mermaid 解析错误:', error_);\n return '';\n }\n })();\n\n // Cache the promise\n mermaidCache.set(cacheKey, renderPromise);\n cleanupCache();\n\n // Handle promise result\n renderPromise\n .then((svg) => {\n // Only update if this is still the current cache key\n if (mermaidCache.get(cacheKey) === renderPromise) {\n setData(svg);\n }\n })\n .catch(() => {\n // Remove failed promise from cache\n if (mermaidCache.get(cacheKey) === renderPromise) {\n mermaidCache.delete(cacheKey);\n }\n });\n }, [cacheKey, content, id, mermaidConfig]);\n\n return data;\n};\n"],"mappings":"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"}