Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
fb736c20
Unverified
Commit
fb736c20
authored
Nov 09, 2025
by
Johnny
Committed by
GitHub
Nov 09, 2025
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(web): add Mermaid diagram support in markdown (#5242)
Co-authored-by:
Claude
<
noreply@anthropic.com
>
parent
5925e3cf
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
112 additions
and
0 deletions
+112
-0
CodeBlock.tsx
web/src/components/MemoContent/CodeBlock.tsx
+10
-0
MermaidBlock.tsx
web/src/components/MemoContent/MermaidBlock.tsx
+102
-0
No files found.
web/src/components/MemoContent/CodeBlock.tsx
View file @
fb736c20
import
{
CheckIcon
,
CopyIcon
}
from
"lucide-react"
;
import
{
useState
}
from
"react"
;
import
{
cn
}
from
"@/lib/utils"
;
import
{
MermaidBlock
}
from
"./MermaidBlock"
;
interface
PreProps
{
children
?:
React
.
ReactNode
;
...
...
@@ -19,6 +20,15 @@ export const CodeBlock = ({ children, className, ...props }: PreProps) => {
const
match
=
/language-
(\w
+
)
/
.
exec
(
codeClassName
);
const
language
=
match
?
match
[
1
]
:
""
;
// If it's a mermaid block, render with MermaidBlock component
if
(
language
===
"mermaid"
)
{
return
(
<
MermaidBlock
className=
{
className
}
{
...
props
}
>
{
children
}
</
MermaidBlock
>
);
}
const
handleCopy
=
async
()
=>
{
try
{
await
navigator
.
clipboard
.
writeText
(
codeContent
);
...
...
web/src/components/MemoContent/MermaidBlock.tsx
0 → 100644
View file @
fb736c20
import
mermaid
from
"mermaid"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
useEffect
,
useMemo
,
useRef
,
useState
}
from
"react"
;
import
{
cn
}
from
"@/lib/utils"
;
import
{
instanceStore
,
userStore
}
from
"@/store"
;
import
{
resolveTheme
}
from
"@/utils/theme"
;
interface
MermaidBlockProps
{
children
?:
React
.
ReactNode
;
className
?:
string
;
}
/**
* Maps app theme to Mermaid theme
* @param appTheme - The resolved app theme
* @returns Mermaid theme name
*/
const
getMermaidTheme
=
(
appTheme
:
string
):
"default"
|
"dark"
=>
{
switch
(
appTheme
)
{
case
"default-dark"
:
return
"dark"
;
case
"default"
:
case
"paper"
:
case
"whitewall"
:
default
:
return
"default"
;
}
};
export
const
MermaidBlock
=
observer
(({
children
,
className
}:
MermaidBlockProps
)
=>
{
const
containerRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
[
svg
,
setSvg
]
=
useState
<
string
>
(
""
);
const
[
error
,
setError
]
=
useState
<
string
>
(
""
);
// Extract the code element and its content
const
codeElement
=
children
as
React
.
ReactElement
;
const
codeContent
=
String
(
codeElement
?.
props
?.
children
||
""
).
replace
(
/
\n
$/
,
""
);
// Get current theme from store (reactive via MobX observer)
// This will automatically trigger re-render when theme changes
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
useEffect
(()
=>
{
const
renderDiagram
=
async
()
=>
{
if
(
!
codeContent
||
!
containerRef
.
current
)
{
return
;
}
try
{
// Generate a unique ID for this diagram
const
id
=
`mermaid-
${
Math
.
random
().
toString
(
36
).
substring
(
7
)}
`
;
// Get the appropriate Mermaid theme for current app theme
const
mermaidTheme
=
getMermaidTheme
(
currentTheme
);
// Initialize mermaid with current theme
mermaid
.
initialize
({
startOnLoad
:
false
,
theme
:
mermaidTheme
,
securityLevel
:
"strict"
,
fontFamily
:
"inherit"
,
});
// Render the mermaid diagram
const
{
svg
:
renderedSvg
}
=
await
mermaid
.
render
(
id
,
codeContent
);
setSvg
(
renderedSvg
);
setError
(
""
);
}
catch
(
err
)
{
console
.
error
(
"Failed to render mermaid diagram:"
,
err
);
setError
(
err
instanceof
Error
?
err
.
message
:
"Failed to render diagram"
);
}
};
renderDiagram
();
},
[
codeContent
,
currentTheme
]);
// If there's an error, fall back to showing the code
if
(
error
)
{
return
(
<
div
className=
"w-full"
>
<
div
className=
"text-sm text-destructive mb-2"
>
Mermaid Error:
{
error
}
</
div
>
<
pre
className=
{
className
}
>
<
code
className=
"language-mermaid"
>
{
codeContent
}
</
code
>
</
pre
>
</
div
>
);
}
return
(
<
div
ref=
{
containerRef
}
className=
{
cn
(
"mermaid-diagram w-full flex justify-center items-center my-4 overflow-x-auto"
,
className
)
}
dangerouslySetInnerHTML=
{
{
__html
:
svg
}
}
/>
);
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment