| name | fix-mermaid |
| description | Use this skill to fix Mermaid diagram syntax errors inside HTML, Markdown, and TSX files. Trigger when the user mentions: "mermaid error", "Syntax error in text", "mermaid not rendering", "diagram is broken", "all diagrams crashed", or references a Mermaid version error (e.g. "mermaid version 10.9.5"). Fixes formatter-induced indentation pollution and statement concatenation that break Mermaid v10 parsing.
|
| allowed-tools | ["Read","Edit","Grep","Bash"] |
Mermaid v10 æ§æä¿®æ£ã¹ãã«
ð ãŸãåå©çšã¹ã¯ãªããã䜿ãïŒããŒã¯ã³ç¯çŽã»æåªå
ïŒ
éç HTML ã® Mermaid æç»åŽ©ããçŽããšãã¯ããã€ã©ãŒãã¬ãŒãïŒrender ã«ãŒãã»SVG åŸåŠçã»äžå€®å¯ã CSSïŒãææžãã§åçæããªãããšã以äžã®åå©çšã¹ã¯ãªããã§æ©æ¢°çåŠçãäžæ¬é©çšã§ããã
-
å³ãœãŒã¹ã JS ãã³ãã¬ãŒããªãã©ã«ã§å®çŸ©ïŒLLM ã®å€æãå¿
èŠãªã®ã¯ããã ãïŒ:
åå³ã 1 ã¹ããŒãã¡ã³ã 1 è¡ã»ã«ã©ã 0ã»æ¹è¡ã¯ <br/> ã§ const DIAGRAMS = { 'diag-1': 'flowchart TD ...' } ãšã㊠HTML ã® <script> å
ã«æžãã
-
æç»ãã€ãã©ã€ã³ãåªçé©çš:
bun run .claude/skills/fix-mermaid/scripts/apply_render_pipeline.ts <file.html>
ããã <div class="mermaid">âŠ</div> â é£çª id ä»ã空 div ãžã®çœ®æãstartOnLoad:false+securityLevel:'loose' ä»äžãapplySvgFixups+render ã«ãŒã泚å
¥ãäžå€®å¯ã CSS 泚å
¥ããŸãšããŠè¡ãïŒåå®è¡ããŠãäºéé©çšããªãïŒã
-
æ£æ¬ Markdown ããå³ã埩å
ããå ŽåïŒHTML åŽãœãŒã¹ãç Žå£ãããçïŒ:
bun run .claude/skills/fix-mermaid/scripts/restore_diagrams.ts <file.html> <source.md>
-
ã€ã³ãã³ãæ±æã»è¡åæã®ã¿ã®ä¿®æ£ïŒ.html/.md/.tsxïŒã¯ fix_mermaid.ts:
bun run .claude/skills/fix-mermaid/scripts/fix_mermaid.ts <file>
SVG å¹
ã®éå: apply_render_pipeline.ts 㯠SVG å¹
ã« viewBox ç±æ¥ã®èªç¶ px å¹
+ maxWidth:100% ã䜿ããwidth:'100%' ã width:'auto'ïŒviewBox ã®ã¿ã§ intrinsic ãµã€ãºãæããªã SVG ã§ã¯ã³ã³ããå
šå¹
ãžäŒžã³ãïŒããå°ãã flowchart LR å³ãç°åžžæ¡å€§ããããã䜿ããªãã
察象
.html ãã¡ã€ã«å
ã® <div class="mermaid"> ãããã¯
.md ãã¡ã€ã«å
ã® ```mermaid ãããã¯
.tsx / .ts / .jsx / .js ãã¡ã€ã«å
ã® chart={...} ãªã©ã®ãã³ãã¬ãŒããªãã©ã«å
ã® Mermaid æ§æ
Mermaid v10 ã®å¿
é ã«ãŒã«
- ã³ã³ãã³ãã¯ã«ã©ã 0é
眮ïŒå
é 空çœãªãïŒ
- åã¹ããŒãã¡ã³ãã¯æ¹è¡ã§åé¢ïŒ1è¡ã«è€æ°é£çµããªãïŒ
- ããŒãã©ãã«
A["text"] ã®å
容ã¯1è¡ã«åãã
mindmap ã®ã¿äŸå€ â å
éšã€ã³ãã³ãã¯éå±€æ§é ã衚ãããä¿æãã
block-beta ã¯äœ¿çšçŠæ¢ â v10.9.5 ã§å
šäœã¯ã©ãã·ã¥ã®åå ã«ãªããgraph TD ã§ä»£æ¿ãã
ããããåå
HTMLãã³ãŒãã®ãã©ãŒããã¿ïŒPrettierçïŒã«ããç Žå£ãã¿ãŒã³:
- 14ã¹ããŒã¹çã®HTML/ã³ãŒãã€ã³ãã³ããMermaidã³ã³ãã³ãã«æ··å
¥ãã
- é·ãããŒãã©ãã«ãè¡åæãããïŒ
A["ããã¹ã ãš ç¶ã"] ã«åãããïŒ
- è€æ°ã¹ããŒãã¡ã³ãã1è¡ã«é£çµãããïŒ
graph TD A["x"] B["y"] A --> BïŒ
ä¿®æ£æé
Grep ã§ä¿®æ£å¯Ÿè±¡ãã¡ã€ã«ãæ€çŽ¢ããMermaid ãããã¯ãææ¡ãã
Read ã§åãããã¯ã確èªããäžèšã«ãŒã«éåãç¹å®ãã
Edit ãŸãã¯èªåä¿®æ£ã¹ã¯ãªããã§åãããã¯ã®å
容ãä¿®æ£ãã
èªåä¿®æ£ãè¡ãå Žå㯠TypeScript çã¹ã¯ãªãã fix_mermaid.ts ã bun ã§å®è¡ããŸã:
bun run .claude/skills/fix-mermaid/scripts/fix_mermaid.ts path/to/file.tsx
倿äŸ
BeforeïŒå£ããç¶æ
ïŒ:
<div class="mermaid">
graph LR A["ããŒãA"] B["ããŒãB"] A --> B
style A fill:#fff
</div>
AfterïŒä¿®æ£åŸïŒ:
<div class="mermaid">
graph LR
A["ããŒãA"]
B["ããŒãB"]
A --> B
style A fill:#fff
</div>
ãã€ã¢ã°ã©ã å¥ã®æ³šæç¹
詳现㯠references/mermaid-v10-guide.md ãåç
§ãèŠç¹ã®ã¿:
| çš®å¥ | 泚æç¹ |
|---|
graph / flowchart | æé »åºãã«ã©ã 0ã«ãŒã«ãå³å® |
sequenceDiagram | Note over A,B: ã¯1è¡ã«åãã |
mindmap | å
éšã€ã³ãã³ãä¿æïŒå¯äžã®äŸå€ïŒ |
block-beta | 䜿çšçŠæ¢ïŒå
šäœã¯ã©ãã·ã¥ïŒ |
htmlLabels: true ç°å¢ | < â <ã> â > ã«å€æ |
å®å°æ€èšŒæžã¿ïŒãã©ãŠã¶ã¬ã³ãã©ãŒåºæã®åé¡ïŒ2026幎3æïŒ
éçããŒãµãŒ @mermaid-js/parser ã§ã¯ãšã©ãŒã«ãªããªããããã©ãŠã¶ã® Mermaid v10.9.5 ã¬ã³ãã©ãŒã§ Syntax error in text ãçºçãããã¿ãŒã³ã
IDEãã©ãŒããã¿ãŒïŒPrettierïŒã«ããç Žå£ãæ ¹æ¬åå
<div class="mermaid"> ã« Mermaid ãœãŒã¹ãçŽæ¥æžããšãVSCode/Prettier ãä¿åã®ãã³ã«ã€ã³ãã³ããä»å ããŠæ§æãå£ããæä¹
察ç㯠JS ãã³ãã¬ãŒããªãã©ã«ãžã®ç§»ç®¡ã
<div class="mermaid">
graph LR
A --> B
</div>
<div id="diag-0"></div>
<script>
const DIAGRAMS = {
'diag-0': `graph LR
A --> B`,
};
mermaid.initialize({ startOnLoad: false });
(async () => {
for (const [id, src] of Object.entries(DIAGRAMS)) {
const { svg } = await mermaid.render('svg-' + id, src);
document.getElementById(id).innerHTML = svg;
}
})();
</script>
ãã®æ¹åŒã§ã¯ --> ã --> ã«ãšã¹ã±ãŒãããå¿
èŠããªããªãã
ãã©ãŠã¶ã¬ã³ãã©ãŒã§ Syntax Error ãèµ·ããæåã»æ§æ
| ç®æ | åé¡ã®ããèšè¿° | å¯ŸåŠ |
|---|
subgraph ã©ãã« | äžžæ¬åŒ§ () ãå«ã | åé€ãŸãã¯å¥è¡šçŸã«çœ®æ |
subgraph ã©ãã« | çµµæåïŒð ð¥ïž çïŒ | åé€ |
participant ... as | çµµæåïŒð€ â¡ çïŒ | åé€ |
ãšããžã©ãã« |...| | å
é ã¹ã©ãã·ã¥ |/command| | ã¹ã©ãã·ã¥ãé€å» |
ããŒãã©ãã« ["..."] | å
šè§æ³¢ããã·ã¥ ã | ãã çã®æ¥æ¬èªã«çœ®æ |
ããŒãã©ãã« ["..."] | ã¹ã©ãã·ã¥ path/to | - ãŸãã¯ã¹ããŒã¹ã«çœ®æ |
è±åœ¢ããŒã {} | ã¯ã©ãŒããªãæ¥æ¬èª {æ°ãããã¡ã€ã«} | {"æ°ãããã¡ã€ã«"} ãšã¯ã©ãŒããã |
quadrantChart ã®åº§æš / ããã¹ã | ããã«ã¯ã©ãŒããŒã·ã§ã³ãªãã®æåå | "" ã§å²ã (äŸ: "CEO/CTO": [0.8, 0.9]) |
| å
šãŠã®å³è§£ (å
šè¬) | å
šè§äžžæ¬åŒ§ ïŒïŒ | åè§äžžæ¬åŒ§ ( ) ã«çœ®æãã |
| å
šãŠã®å³è§£ (å
šè¬) | å
šè§ããã·ã¥ â | åè§ãã€ãã³ - ã«çœ®æãã |
| å
šãŠã®å³è§£ (å
šè¬) | å
šè§ã³ãã³ ïŒ | åè§ã³ãã³ : ã«çœ®æãã |
SVG ãµã€ãºå¶åŸ¡
Mermaid v10 㯠SVG èŠçŽ ã«çµ¶å¯Ÿãã¯ã»ã«å€ã® width/height 屿§ãä»äžãããmermaid.render() åŸã«å¿
ãé€å»ããã
svgEl.removeAttribute('width');
svgEl.removeAttribute('height');
svgEl.style.width = `${w}px`;
svgEl.style.maxWidth = '100%';
svgEl.style.height = 'auto';
CSS ã«ããã©ãŒã«ããã¯ã远å ããïŒ
.mermaid-wrap svg {
max-width: 100% !important;
height: auto !important;
}
ã·ãŒã±ã³ã¹å³ã»ç¶æ
é·ç§»å³çã®äžéšèŠåãïŒã¯ãªããã³ã°ïŒå¯ŸçïŒ2026幎6æè¿œèšïŒ
Mermaid v10 ã®ã·ãŒã±ã³ã¹å³ïŒsequenceDiagramïŒãç¶æ
é·ç§»å³ïŒstateDiagramïŒã®ã¬ã³ãã©ãŒã«ã¯ãæç»ãããæäžéšèŠçŽ ïŒã©ã€ãã©ã€ã³äžç«¯ãäžéšã¢ã¯ã¿ãŒããã¯ã¹ãã«ãŒããããã¯ãèŠåã¡ã¢çïŒã®å¢ç座æšãæ£ããèšç®ã§ãããçæããã SVG ã® viewBox 屿§ã®é«ãïŒheightïŒãäžè¶³ãããã°ããããŸãã
芪èŠçŽ ïŒ.diagram-wrap çïŒã« overflow-x: auto ãªã©ãæå®ãããŠããå ŽåãCSSã®ä»æ§ã«ãã瞊æ¹åãã¯ãªããã³ã°ïŒhidden åçïŒããããããã¯ã¿åºãäžéšèŠçŽ ãåãèœãšãããŠèŠããªããªããŸãã
ã察çã
mermaid.render() åŸã«ãJS ã§åçã« viewBox ã®é«ããæ¡åŒµããååãªã¹ããŒã¹ã確ä¿ããäžã§åé©çšããŸãã
const viewBoxStr = svgEl.getAttribute('viewBox');
if (viewBoxStr) {
const parts = viewBoxStr.split(' ').map(Number);
if (parts.length === 4) {
const isSequenceOrState = src.trim().startsWith('sequenceDiagram') || src.trim().startsWith('stateDiagram');
const extraHeight = isSequenceOrState ? 110 : 15;
svgEl.setAttribute('viewBox', `${parts[0]} ${parts[1]} ${parts[2]} ${parts[3] + extraHeight}`);
}
}
quadrantChart ã®æå被ã察çïŒ2026幎6æè¿œèšïŒ
quadrantChart ã§ãããããããåèŠçŽ ã®ããã¹ãã©ãã«ãéãªã£ãŠè¡šç€ºãããå Žåã¯ãmermaid.initialize ã®èšå®ã«ãŠå
éšæç»è§£å床ã倧ããæå®ããŸãã
mermaid.initialize({
quadrantChart: {
chartWidth: 800,
chartHeight: 600,
pointRadius: 8,
pointLabelFontSize: 14
}
});
ãã®ããã§ãHTML ã®ã©ãããŒïŒ.mermaid-wrap çïŒã®ã€ã³ã©ã€ã³ã¹ã¿ã€ã«ïŒäŸ: style="max-width: 750px; margin: 0 auto;"ïŒã§è¡šç€ºå¹
ãå¶éããããšã§ãæç»æåå士ã®è¢«ããå®å
šã«åé¿ãã€ã€ãç»é¢ã«åãŸã綺éºãã§ã¬ã¹ãã³ã·ã衚瀺ã§ããŸãã
HTML ã§ã® Mermaid å³è§£ã®äžå€®å¯ã Flexbox ã¹ã¿ã€ã«ïŒ2026幎6æè¿œèšïŒ
éç HTML ã§ Mermaid ã衚瀺ããéãå³è§£ãå·Šå¯ãã«ãªãã®ãé²ãäžå€®å¯ãã«ããããã® CSS å®è£
äŸã§ãã
.mermaid-wrap {
display: flex;
justify-content: center;
}
.mermaid {
display: flex;
justify-content: center;
width: 100%;
}
.mermaid svg {
display: block;
margin: 0 auto;
max-width: 100% !important;
height: auto !important;
}
React/Next.js (CSS Modules) ç§»æ€æã®è¡šç€ºãšäžå€®å¯ãïŒ2026幎5æè¿œèšïŒ
React (Next.js App Router) ç§»è¡ã«éããŠå
±éã® MermaidDiagram ã³ã³ããŒãã³ãã䜿çšããå ŽåãCSS Modules ãšã®ç«¶åããã¹ãç°å¢ïŒVitestïŒã§ã®æç»ãšã©ãŒã«æ³šæããå¿
èŠããããŸãã
1. CSS Modules ç°å¢äžã§ã®äžå€®å¯ããšãµã€ãºå¶é
å
±éã® MermaidDiagram ã³ã³ããŒãã³ãã¯åºåæã«ã°ããŒãã«ã¯ã©ã¹ "mermaid" ãä»äžããŸããããããCSS ModulesïŒ*.module.cssïŒã§æå®ãã .mermaid ã¯ã¯ã©ã¹åãããã·ã¥åããããããã¹ã¿ã€ã«ãåœãããªããªãå·Šå¯ãã«ãªã£ãŠããŸããŸãã
ã察çã
- TSX åŽ:
MermaidDiagram ãã©ãã㌠div ã§å²ã¿ãããã·ã¥åãããã¯ã©ã¹å (styles.mermaid) ãšãåå¥å¹
å¶éçšã®ã°ããŒãã« ID (id="diag-X") ãä»äžããŸãã
<div id="diag-0" className={styles.mermaid}>
<MermaidDiagram chart={DIAGRAM_0} />
</div>
- CSS åŽ: ããã·ã¥åã¯ã©ã¹ããäžäœã®ã°ããŒãã«ãª
svg ãã¿ãŒã²ããããããã:global ã»ã¬ã¯ã¿ã䜿çšããŸãã
.mermaid {
display: flex;
justify-content: center;
}
.mermaid :global(svg) {
display: block;
margin: 0 auto;
width: 100%;
max-width: 100%;
height: auto;
}
åå¥ ID ã»ã¬ã¯ã¿ïŒ#diag-0 svg çïŒã¯ CSS Modules ã§ã倿ãããªããããã°ããŒãã« ID ã»ã¬ã¯ã¿çµç±ã§æå€§å¹
ïŒmax-widthïŒãå¶åŸ¡ã§ããŸãã
2. ãã¹ãç°å¢ïŒVitestïŒã§ã® MermaidDiagram ã®ã¢ãã¯å
MermaidDiagram ã¯ã¯ã©ã€ã¢ã³ããµã€ãã§åçã« mermaid ã©ã€ãã©ãªãèªã¿èŸŒãã§åäœããããããã¹ãç°å¢ã§ã® DOM ã¬ã³ããªã³ã°æã«ãšã©ãŒãèµ·ããåå ãšãªããŸãã
ãã¹ããã¡ã€ã«ïŒpage.test.tsxïŒã§ã¯ãå¿
ã vi.mock ã䜿ã£ãŠãããŒèŠçŽ ã«ã¢ãã¯åããŠãã ããã
vi.mock("@/components/MermaidDiagram", () => ({
default: function DummyMermaidDiagram({ chart }: { chart: string }) {
return <pre data-testid="mermaid">{chart}</pre>;
},
}));
ãç§»è¡åŸ/å°æ¥çšãMermaid v11 + React å
±éã³ã³ããŒãã³ãã®å¯èªæ§ã»æååãã»æåè²å¯ŸçïŒ2026幎6æè¿œèšïŒ
å°æ¥ç㪠Next.js (App Router) ç§»è¡åŸã¯ãmermaid@^11.15.0 ã䜿çšããå³ã¯å
±éã³ã³ããŒãã³ã components/Mermaid.tsxïŒ'use client'ïŒã§æç»ããŸããmermaid.render() ãè¿ã SVG æååã dangerouslySetInnerHTML ã§æ³šå
¥ããããŒãžåºæã¹ã¿ã€ã«ã¯éåžžã® CSS (.css) ãŸã㯠CSS Modules ã«çœ®ããŸãã
çç¶ãšæ ¹æ¬åå ã®å¯Ÿå¿è¡š
| çç¶ | æ ¹æ¬åå | 察ç |
|---|
| æåãäœã³ã³ãã©ã¹ãã§èªã¿ã¥ããïŒç¹ã«ãšããžã©ãã«ã»subgraph èŠåºãã»ã·ãŒã±ã³ã¹å³ NoteïŒ | theme:'base'ïŒé darkModeïŒã edgeLabelBackground=lighten(...)ã»noteBkgColor="#fff5ad" çã®æè²èæ¯ãç®åºããããž CSS ã§æè²æåãåœãŠããšæÃæã§èªããªã | theme:'dark' + ãœãªããæ¿è²ã® themeVariables ãæç€ºïŒäžèšïŒ |
| ããŒãå
ã®æåãäžç«¯ã§åãã | æ¡å¯žãšå®æç»ã®æ° px 差㧠SVG viewBox äžç«¯ãèŠåãã | æç»åŸã« viewBox ã®é«ããæ¡åŒµïŒflowchart +15 / sequenceã»state +110ïŒ+ overflow:visible |
| ããŒãæåãå³ç«¯ã§åããïŒemoji ãå«ãå³ã®ã¿ãemoji ç¡ãã®å³ã¯ç¡å·ïŒåãåãã®ç®å°ïŒ | <foreignObject> 㯠SVG 仿§äž overflow:hidden ãããã©ã«ããemoji ã¯ã©ãã«æ¡å¯žæã«ãè±è
(tofu)ãå¹
ã§æž¬ãã宿ç»ã§åºãããã foreignObject å¹
< å®ããã¹ãå¹
ãšãªãã¯ãªãã | CSS ã§ .mermaidTarget foreignObject { overflow: visible }ïŒããŒãç©åœ¢ã¯åååºããã¯ã¿åºããæåãæ å
ã«åãŸãïŒ |
| æåè²ãå€ããŠãå
šãåæ ãããªã | mermaid.initialize() ã¯ã¢ãžã¥ãŒã«æäžäœã§äžåºŠã ãå®è¡ããããã HMR ã§ã¯åå®è¡ãããå€ãããŒãã®ãŸãŸãå ã㊠.next ãã£ãã·ã¥æ±æ | .next åé€ + dev ãµãŒããŒå®å
šåèµ·å + ãã©ãŠã¶ã®ããŒããªããŒãïŒåŸè¿°ïŒ |
| æ¥æ¬èªã©ãã«ã®å¹
äžè¶³ã«ãã軜埮ãªåã | Web ãã©ã³ãïŒNoto Sans JPïŒèªèŸŒåã«æ¡å¯ž | mermaid.render() çŽåã« await document.fonts.readyïŒjsdom çã¯åã¬ãŒãã§ skipïŒ |
æ£æºã® mermaid.initialize èšå®ïŒv11ïŒ
mermaid.initialize({
startOnLoad: false,
theme: 'dark',
securityLevel: 'loose',
themeVariables: {
primaryColor: '#1a73e8', primaryTextColor: '#e8f0fe', primaryBorderColor: '#1a73e8',
lineColor: '#5f7fb8', secondaryColor: '#0f9d58', tertiaryColor: '#0d1a2e',
background: '#060b14', mainBkg: '#0f2040', nodeBorder: '#1a73e8',
clusterBkg: '#0d1a2e', titleColor: '#e8f0fe', edgeLabelBackground: '#0d1a2e',
fontFamily: "'Noto Sans JP', sans-serif", fontSize: '13px',
},
flowchart: { curve: 'basis', padding: 20 },
sequence: { actorMargin: 60, mirrorActors: true },
});
mainBkg ãéæãåéæã«ããªãããšãããŒãèæ¯ããœãªããæ¿è²ã ããããçœæåãèªããCSS ã® !important 匷å¶äžæžããäžèŠã«ãªãã
â ïž SVG åŸåŠçã¯ãæååå å·¥ãã§ã¯ãªããã©ã€ã DOM æäœãã§è¡ã
mermaid.render() ã®æ»ãå€ïŒSVG æååïŒã DOMParser('image/svg+xml') + XMLSerializer ã§åŸåŸ©ãããŠã¯ãªããªããforeignObject å
ã® htmlLabelsïŒXHTML åå空éã® HTMLïŒãå£ããã©ãã«ã width=0ã»ããã¹ã空ã«ãªã£ãŠè¡šç€ºã朰ããã
innerHTML 泚å
¥åŸã®å® DOM èŠçŽ ãçŽæ¥æäœãããReact ã§ã¯ ref + svgStr äŸåã® useEffect ã§ã泚å
¥æžã¿ <svg> ã«å¯ŸããŠåŸåŠçãé©çšããã
const applySvgFixups = (svgEl: SVGSVGElement, chart: string): void => {
svgEl.removeAttribute('width');
svgEl.removeAttribute('height');
svgEl.style.height = 'auto';
svgEl.style.overflow = 'visible';
svgEl.style.marginBottom = '10px';
const viewBox = svgEl.getAttribute('viewBox');
if (!viewBox) return;
const parts = viewBox.split(/\s+/).map(Number);
if (parts.length !== 4 || !parts.every((n) => Number.isFinite(n))) return;
const trimmed = chart.trim();
const isSequenceOrState =
trimmed.startsWith('sequenceDiagram') || trimmed.startsWith('stateDiagram');
const extraHeight = isSequenceOrState ? 110 : 15;
const [x, y, w, h] = parts as [number, number, number, number];
svgEl.style.width = `${w}px`;
svgEl.style.maxWidth = '100%';
svgEl.setAttribute('viewBox', `${x} ${y} ${w} ${h + extraHeight}`);
};
React ã§ã®äœ¿çšäŸïŒcomponents/Mermaid.tsxïŒ:
export default function Mermaid({ chart }: { chart: string }) {
const [svgStr, setSvgStr] = useState('');
const wrapperRef = useRef<HTMLDivElement>(null);
useEffect(() => {
let isMounted = true;
if (!chart) return;
const render = async () => {
try {
const id = `mermaid-svg-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
const { svg } = await mermaid.render(id, chart);
if (isMounted) setSvgStr(svg);
} catch (e) {
console.error('Mermaid rendering failed:', e);
if (isMounted) setSvgStr(`<p class="text-red-500">å³è¡šã®æç»ã«å€±æããŸãã</p>`);
}
};
render();
return () => { isMounted = false; };
}, [chart]);
useEffect(() => {
if (!svgStr || !wrapperRef.current) return;
const svgEl = wrapperRef.current.querySelector('svg');
if (svgEl) applySvgFixups(svgEl as SVGSVGElement, chart);
}, [svgStr, chart]);
return (
<div ref={wrapperRef} className="mermaid-wrapper"
dangerouslySetInnerHTML={{ __html: svgStr }} />
);
}
æåè²ã¯ãããŒãã©ãã«éå®ãã§åœãŠãïŒæèæ¯Ãææåã®åçºé²æ¢ïŒ
éå»ãå
š SVG ããã¹ããž color/fill:#e6e9ee !important ãåœãŠãçµæããšããžã©ãã«ã»subgraph èŠåºãã»ã·ãŒã±ã³ã¹å³ NoteïŒæè²èæ¯ïŒãŸã§æè²æåã«ãªãèªããªããªãããããå©ãããç¹°ãè¿ãããtheme:'dark' ã§èæ¯è²ã¯é©æ£åããããããCSS ã§è²ãåœãŠãã®ã¯ããŒãã©ãã«ïŒ.node .nodeLabelïŒã«éå®ããããšããžã©ãã« / Note ã¯ããŒãä»»ãïŒæèæ¯ïŒææåïŒã«ããã
ããŒãæåè²ã®æ¹éïŒãŠãŒã¶ãŒéžæïŒæããŒãïŒçœ / é»ããŒãïŒé» ãæãèªã¿ãããïŒ:
.mermaidTarget :global(foreignObject) { overflow: visible; }
.mermaidTarget :global(foreignObject > div),
.mermaidTarget :global(.nodeLabel),
.mermaidTarget :global(.edgeLabel) { overflow: visible; }
.mermaidTarget :global(.node .nodeLabel),
.mermaidTarget :global(.node .nodeLabel *) { color: #ffffff !important; }
.mermaidTarget :global(.node[style*="fbbc04" i] .nodeLabel),
.mermaidTarget :global(.node[style*="fbbc04" i] .nodeLabel *),
.mermaidTarget :global(.node:has([style*="fbbc04" i]) .nodeLabel),
.mermaidTarget :global(.node:has([style*="fbbc04" i]) .nodeLabel *),
.mermaidTarget :global(.node:has([fill="#fbbc04" i]) .nodeLabel),
.mermaidTarget :global(.node:has([fill="#fbbc04" i]) .nodeLabel *) { color: #000000 !important; }
.edgeLabel * ã« fill:#fff ãåœãŠãªãããšããžã©ãã«ã®èæ¯ rect ãçœãå¡ã朰ããããè²ãåœãŠãã®ã¯ã©ãã«ããã¹ãã®ã¿ã»color ã®ã¿ã«çããã
å€åŽ DOMPurify ã«ããéå°ãµãã¿ã€ãºã§å³ãå£ããïŒQA_Studies å®å° 2026幎6æïŒ
çç¶: ããŒã¯ã¢ãŒãã§ããŒãå
ããã¹ããæ¶ããïŒèæ¯ãšååïŒïŒç¢å°ã»æ ç·ã®ã¹ã¿ã€ã«ãåœãããªããæ§æã¯æ£ãããã©ãŠã¶ Console ã«ãæ§æãšã©ãŒã¯åºãªãã
æ ¹æ¬åå : mermaid.render() ã®åºå SVG ã«å¯Ÿããã³ã³ããŒãã³ãåŽã§å€åŽãã DOMPurify.sanitize(svg, { USE_PROFILES: { svg: true } }) ããããŠãããSVG ãããã¡ã€ã«ã¯ HTML èŠçŽ ãèš±å¯ããªãããã以äžãåæã«é€å»ãããã
htmlLabels: true ã®ããŒãã©ãã«ã¯ <foreignObject> å
ã® <div class="nodeLabel">ïŒïŒHTMLïŒãšããŠæç»ããã â div/span ãé€å»ãã ããã¹ãæ¶å€±ã
- ããŒã/ãšããž/ããã¹ãã®è²ãäžãã
<style> ãããã¯ã <style> èŠçŽ ããšé€å» â è²ã»ç¢å°ã»æ ç·ãæ¢å®ïŒé»ïŒã«æ»ãã
ADD_TAGS: ['style'] ãè¶³ããŠã USE_PROFILES å¶çŽäžã§ã¯é€å»ãç¶ãã
æä¹
察ç:
- å³è¡šãéçºè
çŽæžãã®éç宿°ïŒå€éšå
¥åãªãïŒãªããå€åŽ DOMPurify ã¯äžèŠã»æå®³ãæ€å»ããŠ
mermaid.render() ã®åºåããã®ãŸãŸæç»ãããXSS æ»æé¢ãç¡ããMermaid v11 ã¯å
éšã« DOMPurify ã忢±ããŠããããèªåã§ç¡å®³åãããã
- ã©ãããŠãå€åŽãµãã¿ã€ãºãæ®ããªã
USE_PROFILES: { svg: true }ïŒHTML ãæé€ãããããã¡ã€ã«ïŒã¯äœ¿ãããstyle ã¿ã°ïŒCSS å
容ã»foreignObjectã»div/spanã»style/class 屿§ãæç€ºèš±å¯ããããã ã CSS ç¡å®³åãèã mermaid åºåå€åã«åŒ±ãããéæšå¥šã
ãã¹ãã®èœãšã穎ïŒåçºé²æ¢ïŒ:
- ã¢ãã¯ã
<svg><g>ok</g></svg> ã®ãããªèªæ SVG ã ãšã<style> ã <foreignObject> ãå«ãŸãªãããå£ãããµãã¿ã€ãºçµè·¯ãäžåºŠãéããããã¹ããç·ã®ãŸãŸæ¬çªãå£ãããã¢ãã¯ã«ã¯å¿
ã <style> ãããã¯ãš <foreignObject> å
HTML ã©ãã«ãå«ããããšã
- ãã ã happy-dom / jsdom 㯠SVG å
<style> å
容ã foreignObject ã® HTML åèŠçŽ ã® DOMParser/XMLSerializer ã©ãŠã³ãããªãããå®å
šåçŸã§ããªãããŠããããã¹ãã§ã¯ã<style> èŠçŽ ãé€å»ãããªãããšããªã©æ©æ¢°çã«ç¢ºå®ãªå·®åã®ã¿ãæ€èšŒããè²ã»ã©ãã«æååã®å¯èŠæ§ã¯å®ãã©ãŠã¶ã®ç®èŠã§æ
ä¿ããã
詳现ãªè§£æ±ºèšé²ã¯ docs/archive/MERMAID_TROUBLESHOOTING.md ãåç
§ã
ç¢ºèªæé ïŒéèŠã»é åºå³å®ïŒ
-
*.css å€æŽæã¯ dev ãµãŒããŒãå®å
šåèµ·åãããmermaid.initialize ãã¢ãžã¥ãŒã«æäžäœïŒ HMR ã§åå®è¡ãããªãããã
kill $(lsof -ti:3000) 2>/dev/null; rm -rf .next; bun run dev
-
ãã©ãŠã¶ã¯ããŒããªããŒãïŒâ+Shift+RïŒãéåžžãªããŒãã§ã¯å€ã SVG/CSS ãæ®ãã
-
ç®èŠç¢ºèªã¯ãŠãŒã¶ãŒåŽã§å®æœããïŒãã®ãªããžããªã§ã¯ Playwright/ãã©ãŠã¶èªåæäœã¯äœ¿ããªãæ¹éïŒã
Mermaid ã諊ã㊠HTML/CSS ã«çœ®ãæããã¹ãã±ãŒã¹
以äžã¯ CSS ã§ã¯å¯ŸåŠäžèœãªãããçŽç²ãª HTML/CSS ãŠã£ãžã§ããã«çœ®ãæããïŒ
flowchart TD ã§ 5ã6 ããŒããçŽåãã§ãŒã³ â çžŠé· 900px è¶
- æ¥ç¶ãããŠããªãè€æ°ã®ãµãã°ã©ãïŒããŒãæ°ãé察称ãªããã¢ã¹ãã¯ãæ¯ã厩ããïŒ
å€æåºæºïŒãããŒã墿žã«é¢ããããä»ã®å³ãšåãé«ãã«åãŸãä¿èšŒããªãå Žåã