-
Notifications
You must be signed in to change notification settings - Fork 100
[docs]: explain how to build your own workflow handler #50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d2998f3
c686396
a15cf9b
f2b9366
1443151
59e91f8
f816b6e
97047ad
13329c8
c11a088
eb269c4
14ab8f7
b56ce14
ff1d91e
427c5e4
8f98d9e
1b9a57b
b653a7b
ee7dd0a
cb14be1
8509c26
8ebcf82
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| 'use client'; | ||
|
|
||
| import { useTheme } from 'next-themes'; | ||
| import { use, useEffect, useId, useState } from 'react'; | ||
|
|
||
| export function Mermaid({ chart }: { chart: string }) { | ||
| const [mounted, setMounted] = useState(false); | ||
|
|
||
| useEffect(() => { | ||
| setMounted(true); | ||
| }, []); | ||
|
|
||
| if (!mounted) return; | ||
| return <MermaidContent chart={chart} />; | ||
| } | ||
|
|
||
| const cache = new Map<string, Promise<unknown>>(); | ||
|
|
||
| function cachePromise<T>( | ||
| key: string, | ||
| setPromise: () => Promise<T> | ||
| ): Promise<T> { | ||
| const cached = cache.get(key); | ||
| if (cached) return cached as Promise<T>; | ||
|
|
||
| const promise = setPromise(); | ||
| cache.set(key, promise); | ||
| return promise; | ||
| } | ||
|
|
||
| function MermaidContent({ chart }: { chart: string }) { | ||
| const id = useId(); | ||
| const { resolvedTheme } = useTheme(); | ||
| const { default: mermaid } = use( | ||
| cachePromise('mermaid', () => import('mermaid')) | ||
| ); | ||
|
|
||
| mermaid.initialize({ | ||
| startOnLoad: false, | ||
| securityLevel: 'loose', | ||
| fontFamily: 'inherit', | ||
| themeCSS: 'margin: 1.5rem auto 0;', | ||
| theme: resolvedTheme === 'dark' ? 'dark' : 'default', | ||
| }); | ||
|
|
||
| const { svg, bindFunctions } = use( | ||
| cachePromise(`${chart}-${resolvedTheme}`, () => { | ||
| return mermaid.render(id, chart.replaceAll('\\n', '\n')); | ||
| }) | ||
| ); | ||
|
Comment on lines
+31
to
+50
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Mermaid diagram may be rendered twice due to theme resolution race condition: once with an undefined theme (cached) and again when the theme actually resolves (with a different cache key). View Details📝 Patch Detailsdiff --git a/docs/components/mermaid.tsx b/docs/components/mermaid.tsx
index 08c3d10..928e7c9 100644
--- a/docs/components/mermaid.tsx
+++ b/docs/components/mermaid.tsx
@@ -31,6 +31,19 @@ function cachePromise<T>(
function MermaidContent({ chart }: { chart: string }) {
const id = useId();
const { resolvedTheme } = useTheme();
+ const [themeResolved, setThemeResolved] = useState(false);
+
+ // Wait for resolvedTheme to be defined before rendering to avoid cache key race conditions
+ useEffect(() => {
+ if (resolvedTheme !== undefined) {
+ setThemeResolved(true);
+ }
+ }, [resolvedTheme]);
+
+ if (!themeResolved) {
+ return null;
+ }
+
const { default: mermaid } = use(
cachePromise('mermaid', () => import('mermaid'))
);
AnalysisMermaid diagram rendered twice due to resolvedTheme race conditionWhat fails: The How to reproduce:
Result: Mermaid's render() function is called twice - once with Expected: According to next-themes documentation on Avoid Hydration Mismatch, "many of the values returned from Root cause: The parent |
||
|
|
||
| return ( | ||
| <div | ||
| ref={(container) => { | ||
| if (container) bindFunctions?.(container); | ||
| }} | ||
| // biome-ignore lint/security/noDangerouslySetInnerHtml: Matching fumadocs + is only running trusted input from mdx files | ||
| dangerouslySetInnerHTML={{ __html: svg }} | ||
| /> | ||
| ); | ||
| } | ||
|
Comment on lines
+31
to
+61
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The View Details📝 Patch Detailsdiff --git a/docs/components/mermaid.tsx b/docs/components/mermaid.tsx
index 08c3d10..355e0a8 100644
--- a/docs/components/mermaid.tsx
+++ b/docs/components/mermaid.tsx
@@ -1,5 +1,6 @@
'use client';
+import { Suspense } from 'react';
import { useTheme } from 'next-themes';
import { use, useEffect, useId, useState } from 'react';
@@ -11,7 +12,11 @@ export function Mermaid({ chart }: { chart: string }) {
}, []);
if (!mounted) return;
- return <MermaidContent chart={chart} />;
+ return (
+ <Suspense fallback={<div>Loading diagram...</div>}>
+ <MermaidContent chart={chart} />
+ </Suspense>
+ );
}
const cache = new Map<string, Promise<unknown>>();
Analysisuse() hook in MermaidContent causes runtime error without Suspense boundaryWhat fails: The How to reproduce:
Result: React throws error because a component suspended while rendering without a Suspense boundary to catch it. Per React's documentation on use(), "When called with a Promise, the Expected: Components using |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mermaid component caches rendered diagrams by chart and theme but not by component instance. When the same chart is rendered multiple times, all instances will have the same ID, causing HTML ID conflicts and accessibility issues.
View Details
Analysis
Duplicate HTML IDs in Mermaid component when rendering identical charts
What fails: The
MermaidContentcomponent indocs/components/mermaid.tsxcaches rendered SVG by chart content and theme, but not by component instance ID. When multiple component instances render identical chart content with the same theme, they all receive the same cached SVG (containing the first instance's ID), resulting in duplicate HTML IDs in the DOM.How to reproduce:
Example:
Result: Both rendered SVGs contain the same ID (e.g.,
id="__react_useId_123"), creating duplicate IDs in the DOM. The cached SVG from the first instance is reused for the second instance, preventing the second instance from getting its own unique ID viauseId().Expected behavior: Each component instance should receive an SVG with its own unique ID, as generated by React's
useId()hook. According to React documentation on useId, each call touseId()should produce a unique, stable identifier. This is particularly important for mermaid diagrams since mermaid itself generates SVG elements with potentially colliding IDs.Fix: Include the component instance ID in the cache key (line 47) so each instance's render is cached separately:
cachePromise(\