1- import React , { useEffect , useState } from 'react' ;
1+ import React , { useEffect , useRef , useState } from 'react' ;
22import mermaid from 'mermaid' ;
3+ import { useVisualSettings } from '../context/VisualSettingsContext' ;
34
4- const MermaidDiagram = ( { chart, theme = 'dark' , className } ) => {
5+ /*
6+ * Theme-aware mermaid renderer.
7+ *
8+ * Reads fezcodexTheme from VisualSettingsContext (falling back safely if the
9+ * provider isn't in the tree) and picks a palette + container style that fits:
10+ *
11+ * terracotta — bone paper, terra ink, Fraunces-friendly warm ink text
12+ * luxe — off-white card, charcoal line, muted accent
13+ * brutalist — dark card, emerald accent (default legacy look)
14+ *
15+ * Callers can still override theme / className explicitly.
16+ */
17+
18+ const THEME_PRESETS = {
19+ terracotta : {
20+ mermaidTheme : 'base' ,
21+ themeVariables : {
22+ background : '#F3ECE0' ,
23+ primaryColor : '#E8DECE' ,
24+ primaryBorderColor : '#1A1613' ,
25+ primaryTextColor : '#1A1613' ,
26+ secondaryColor : '#F3ECE0' ,
27+ secondaryBorderColor : '#1A161360' ,
28+ secondaryTextColor : '#1A1613' ,
29+ tertiaryColor : '#C96442' ,
30+ tertiaryBorderColor : '#9E4A2F' ,
31+ tertiaryTextColor : '#F3ECE0' ,
32+ lineColor : '#1A1613' ,
33+ textColor : '#1A1613' ,
34+ mainBkg : '#E8DECE' ,
35+ nodeBorder : '#1A1613' ,
36+ clusterBkg : '#F3ECE0' ,
37+ clusterBorder : '#1A161340' ,
38+ edgeLabelBackground : '#F3ECE0' ,
39+ titleColor : '#1A1613' ,
40+ noteBkgColor : '#E8DECE' ,
41+ noteTextColor : '#1A1613' ,
42+ noteBorderColor : '#C96442' ,
43+ fontSize : '15px' ,
44+ } ,
45+ fontFamily : "'IBM Plex Mono', 'JetBrains Mono', monospace" ,
46+ container :
47+ 'mermaid-container my-8 border border-[#1A161330] bg-[#F3ECE0] p-6 overflow-x-auto relative' ,
48+ labelClass : 'Diagram · figure' ,
49+ labelColor : '#C96442' ,
50+ } ,
51+ luxe : {
52+ mermaidTheme : 'base' ,
53+ themeVariables : {
54+ background : '#FAFAF8' ,
55+ primaryColor : '#FFFFFF' ,
56+ primaryBorderColor : '#1A1A1A' ,
57+ primaryTextColor : '#1A1A1A' ,
58+ secondaryColor : '#FAFAF8' ,
59+ secondaryBorderColor : '#1A1A1A40' ,
60+ secondaryTextColor : '#1A1A1A' ,
61+ tertiaryColor : '#8D4004' ,
62+ tertiaryBorderColor : '#8D4004' ,
63+ tertiaryTextColor : '#FFFFFF' ,
64+ lineColor : '#1A1A1A' ,
65+ textColor : '#1A1A1A' ,
66+ mainBkg : '#FFFFFF' ,
67+ nodeBorder : '#1A1A1A' ,
68+ clusterBkg : '#FAFAF8' ,
69+ clusterBorder : '#1A1A1A20' ,
70+ edgeLabelBackground : '#FAFAF8' ,
71+ titleColor : '#1A1A1A' ,
72+ noteBkgColor : '#FAFAF8' ,
73+ noteTextColor : '#1A1A1A' ,
74+ noteBorderColor : '#8D4004' ,
75+ fontSize : '15px' ,
76+ } ,
77+ fontFamily : "'Outfit', 'Inter', system-ui, sans-serif" ,
78+ container :
79+ 'mermaid-container my-8 border border-[#1A1A1A10] bg-[#FAFAF8] p-6 rounded-sm shadow-sm overflow-x-auto' ,
80+ labelClass : null ,
81+ labelColor : null ,
82+ } ,
83+ brutalist : {
84+ mermaidTheme : 'dark' ,
85+ themeVariables : {
86+ fontSize : '16px' ,
87+ } ,
88+ fontFamily : "'JetBrains Mono', monospace" ,
89+ container :
90+ 'mermaid-container my-8 bg-gray-900/30 p-6 rounded-lg overflow-x-auto' ,
91+ labelClass : null ,
92+ labelColor : null ,
93+ } ,
94+ } ;
95+
96+ const MermaidDiagram = ( { chart, theme, className } ) => {
597 const [ svg , setSvg ] = useState ( '' ) ;
698 const [ error , setError ] = useState ( null ) ;
99+ const containerRef = useRef ( null ) ;
100+
101+ const visualSettings = useVisualSettings ( ) ;
102+ const activeThemeKey = ( ( ) => {
103+ if ( theme === 'dark' || theme === 'default' || theme === 'base' || theme === 'neutral' || theme === 'forest' ) {
104+ return null ;
105+ }
106+ return visualSettings ?. fezcodexTheme || 'brutalist' ;
107+ } ) ( ) ;
108+ const preset =
109+ ( activeThemeKey && THEME_PRESETS [ activeThemeKey ] ) || THEME_PRESETS . brutalist ;
110+
111+ const mermaidTheme = theme || preset . mermaidTheme ;
7112
8113 useEffect ( ( ) => {
9114 mermaid . initialize ( {
10115 startOnLoad : false ,
11- theme : theme ,
116+ theme : mermaidTheme ,
12117 securityLevel : 'loose' ,
13- fontFamily : "'JetBrains Mono', monospace" ,
118+ fontFamily : preset . fontFamily ,
14119 useMaxWidth : false ,
15120 htmlLabels : true ,
16121 flowchart : {
17122 padding : 15 ,
18123 useMaxWidth : false ,
19124 htmlLabels : true ,
20125 } ,
21- themeVariables : {
22- fontSize : '16px' ,
23- } ,
126+ themeVariables : preset . themeVariables ,
24127 } ) ;
25- } , [ theme ] ) ;
128+ } , [ mermaidTheme , preset ] ) ;
26129
27130 useEffect ( ( ) => {
28131 const renderChart = async ( ) => {
29132 if ( ! chart ) return ;
30133 try {
31134 const id = `mermaid-${ Math . random ( ) . toString ( 36 ) . substr ( 2 , 9 ) } ` ;
32- // mermaid.render returns an object { svg } in newer versions
33- const { svg } = await mermaid . render ( id , chart ) ;
34- setSvg ( svg ) ;
135+ const { svg : rendered } = await mermaid . render ( id , chart ) ;
136+ setSvg ( rendered ) ;
35137 setError ( null ) ;
36138 } catch ( err ) {
37139 console . error ( 'Mermaid render error:' , err ) ;
38- // Mermaid might leave error text in the DOM, so we can also show a friendly message
39140 setError ( 'Failed to render diagram. Check syntax.' ) ;
40141 }
41142 } ;
42143
43144 renderChart ( ) ;
44- } , [ chart ] ) ;
145+ } , [ chart , mermaidTheme , preset ] ) ;
45146
46147 if ( error ) {
47148 return (
@@ -52,10 +153,18 @@ const MermaidDiagram = ({ chart, theme = 'dark', className }) => {
52153 ) ;
53154 }
54155
55- const containerClass = className || "mermaid- container my-8 bg-gray-900/30 p-6 rounded-lg overflow-x-auto" ;
156+ const containerClass = className || preset . container ;
56157
57158 return (
58- < div className = { containerClass } >
159+ < div ref = { containerRef } className = { containerClass } >
160+ { preset . labelClass && (
161+ < div
162+ className = "absolute top-0 left-4 -translate-y-1/2 bg-[#F3ECE0] px-2 font-ibm-plex-mono text-[9px] tracking-[0.3em] uppercase"
163+ style = { { color : preset . labelColor } }
164+ >
165+ { preset . labelClass }
166+ </ div >
167+ ) }
59168 < style >
60169 { `
61170 .mermaid-container svg {
0 commit comments