@orz/ui-mermaid 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright 2026 the orzui team
4
+ Copyright 2026 the todou team
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md CHANGED
@@ -1,3 +1,85 @@
1
- # Temporary Holding Version
1
+ # @orz/ui-mermaid
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [Mermaid](https://www.npmjs.com/package/mermaid) diagrams drawn in the active `@orz/ui` theme. The
4
+ package contributes a renderer to `@orz/ui-content`'s registry, so a ```` ```mermaid ```` fence in
5
+ `@orz/ui-markdown` and a `.mmd` file in a viewer both become diagrams without importing this package
6
+ from either.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ pnpm add @orz/ui-mermaid @orz/ui-content @orz/ui react
12
+ ```
13
+
14
+ `mermaid` is a regular dependency: installing this package is the whole setup.
15
+ `@orz/ui-content` is a peer dependency so that the app and every content package share one registry.
16
+
17
+ ## CSS
18
+
19
+ ```css
20
+ @import "tailwindcss";
21
+ @import "@orz/ui/styles.css";
22
+ @import "@orz/ui-mermaid/styles.css";
23
+ ```
24
+
25
+ Import it after `@orz/ui/styles.css`. It holds the diagram's fit rule: a diagram shrinks to its
26
+ column down to 75% of its natural width, then scrolls, and is never enlarged. Like `@orz/ui`'s
27
+ stylesheet, it registers `@source "./dist"` on its own.
28
+
29
+ ## `mermaidContent`
30
+
31
+ ```tsx
32
+ import { ContentRenderers } from "@orz/ui-content";
33
+ import { mermaidContent } from "@orz/ui-mermaid";
34
+
35
+ const RENDERERS = [mermaidContent, markdownContent(), codeContent];
36
+
37
+ <ContentRenderers renderers={RENDERERS}>{app}</ContentRenderers>;
38
+ ```
39
+
40
+ It accepts inline sources whose `lang` is `mermaid` (any case), which is what a markdown fence
41
+ becomes, and files named `*.mmd` or `*.mermaid`, in either placement. Register it before any
42
+ renderer that takes every inline source, such as a code renderer, since the first renderer that
43
+ accepts wins.
44
+
45
+ The source shows until the first render lands, in place of a diagram that does not parse (with a
46
+ one-line summary of the parser's message under it), and behind the Source toggle. That source view
47
+ is `Next`: the renderers registered after this one draw it, so it is highlighted when a code
48
+ renderer is registered and a plain `<pre><code>` when none is. A file is loaded once: the source view gets its text as an inline
49
+ source tagged `mermaid`. A file that fails to load shows the message and a Retry button.
50
+
51
+ ## `MermaidBlock`
52
+
53
+ For a surface that draws a diagram outside the registry:
54
+
55
+ ```tsx
56
+ import { MermaidBlock } from "@orz/ui-mermaid";
57
+
58
+ <MermaidBlock source={text} />
59
+ <MermaidBlock source={text} sourceView={<MySourceView text={text} />} className="my-4" />
60
+ ```
61
+
62
+ `sourceView` defaults to a plain `<pre><code>`.
63
+
64
+ ## Theme
65
+
66
+ A diagram is drawn in the theme tokens in effect on its element (`--background`, `--foreground`,
67
+ `--muted`, `--muted-foreground`, `--border`), so every `@orz/ui` theme gets its own colours with no
68
+ table to maintain. The palette is baked into the SVG, so the diagram is drawn again whenever
69
+ `@orz/ui`'s `useTheme()` reports a new theme, color scheme or variant. Outside a `ThemeProvider` a diagram keeps the colours
70
+ present when it was drawn and does not follow a later switch.
71
+
72
+ ## Loading
73
+
74
+ mermaid is only reached through `import("mermaid")`, so a bundler splits it into chunks of its own,
75
+ fetched the first time a page holds a diagram. A chunk that fails to load leaves the diagram on its
76
+ source, and the next render tries the import again; whether that refetches is the browser's call,
77
+ since some cache a failed module fetch for the life of the page. Labels go through mermaid's
78
+ `securityLevel: "strict"`, which sanitises them: markup survives, scripts and handlers do not.
79
+
80
+ ## Entry points
81
+
82
+ | Import | Contents |
83
+ |---|---|
84
+ | `@orz/ui-mermaid` | `mermaidContent`, `MermaidBlock` |
85
+ | `@orz/ui-mermaid/styles.css` | the `.mermaid-diagram` fit rule |
@@ -0,0 +1,8 @@
1
+ import { type ContentRenderer } from "@orz/ui-content";
2
+ /**
3
+ * The registry entry for mermaid: a source tagged `mermaid` (a fenced block) and a `.mmd` or
4
+ * `.mermaid` file, in either placement. Register it before any renderer that takes every inline
5
+ * source, such as a code renderer, which then draws the diagram's source view through `Next`.
6
+ */
7
+ export declare const mermaidContent: ContentRenderer;
8
+ //# sourceMappingURL=content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../src/content.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,eAAe,EAGrB,MAAM,iBAAiB,CAAC;AAmEzB;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,eAK5B,CAAC"}
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button } from "@orz/ui/components/button";
3
+ import { ContentFallback, useSourceText, } from "@orz/ui-content";
4
+ import { MermaidBlock } from "./mermaid-block.js";
5
+ function isMermaid(source) {
6
+ if (source.kind === "inline")
7
+ return source.lang?.toLowerCase() === "mermaid";
8
+ return /\.(mmd|mermaid)$/i.test(source.name);
9
+ }
10
+ function MermaidFile({ source, placement, className, Next }) {
11
+ const text = useSourceText(source);
12
+ if (text.status === "loading") {
13
+ return (_jsx(ContentFallback, { source: source, placement: placement, className: className }));
14
+ }
15
+ if (text.status === "error") {
16
+ return (_jsxs("div", { className: className, children: [_jsx(ContentFallback, { source: source, placement: placement }), " ", _jsx("span", { children: text.error.message }), " ", _jsx(Button, { size: "xs", variant: "outline", onClick: text.retry, children: "Retry" })] }));
17
+ }
18
+ // Handed on as inline text, not as the file: the code renderer below would
19
+ // load the file a second time, while the diagram is still rendering. Inline
20
+ // text is spelled as a fence's contents, without the file's final newline,
21
+ // which a code view would draw as an empty last line.
22
+ return (_jsx(MermaidBlock, { source: text.text, className: className, sourceView: _jsx(Next, { source: {
23
+ kind: "inline",
24
+ text: text.text.replace(/\r?\n$/, ""),
25
+ lang: "mermaid",
26
+ }, placement: placement }) }));
27
+ }
28
+ function Mermaid(props) {
29
+ const { source, placement, className, Next } = props;
30
+ if (source.kind === "file")
31
+ return _jsx(MermaidFile, { ...props });
32
+ return (_jsx(MermaidBlock, { source: source.text, className: className, sourceView: _jsx(Next, { source: source, placement: placement }) }));
33
+ }
34
+ /**
35
+ * The registry entry for mermaid: a source tagged `mermaid` (a fenced block) and a `.mmd` or
36
+ * `.mermaid` file, in either placement. Register it before any renderer that takes every inline
37
+ * source, such as a code renderer, which then draws the diagram's source view through `Next`.
38
+ */
39
+ export const mermaidContent = {
40
+ name: "mermaid",
41
+ accepts: isMermaid,
42
+ display: "block",
43
+ Component: Mermaid,
44
+ };
45
+ //# sourceMappingURL=content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.js","sourceRoot":"","sources":["../src/content.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AACnD,OAAO,EACL,eAAe,EAIf,aAAa,GACd,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAqB,CAAC;AAInD,SAAS,SAAS,CAAC,MAAqB;IACtC,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,SAAS,CAAC;IAC9E,OAAO,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,WAAW,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAgB;IACvE,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,CACL,KAAC,eAAe,IACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,SAAS,GACpB,CACH,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;QAC5B,OAAO,CACL,eAAK,SAAS,EAAE,SAAS,aACvB,KAAC,eAAe,IAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAI,EAAC,GAAG,EAC7D,yBAAO,IAAI,CAAC,KAAK,CAAC,OAAO,GAAQ,EAAC,GAAG,EACrC,KAAC,MAAM,IAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,OAAO,EAAE,IAAI,CAAC,KAAK,sBAE9C,IACL,CACP,CAAC;IACJ,CAAC;IACD,2EAA2E;IAC3E,4EAA4E;IAC5E,2EAA2E;IAC3E,sDAAsD;IACtD,OAAO,CACL,KAAC,YAAY,IACX,MAAM,EAAE,IAAI,CAAC,IAAI,EACjB,SAAS,EAAE,SAAS,EACpB,UAAU,EACR,KAAC,IAAI,IACH,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;gBACrC,IAAI,EAAE,SAAS;aAChB,EACD,SAAS,EAAE,SAAS,GACpB,GAEJ,CACH,CAAC;AACJ,CAAC;AAED,SAAS,OAAO,CAAC,KAAmB;IAClC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IACrD,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,KAAC,WAAW,OAAK,KAAK,GAAI,CAAC;IAC9D,OAAO,CACL,KAAC,YAAY,IACX,MAAM,EAAE,MAAM,CAAC,IAAI,EACnB,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,KAAC,IAAI,IAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAI,GAC1D,CACH,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAoB;IAC7C,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,OAAO;IAChB,SAAS,EAAE,OAAO;CACnB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { mermaidContent } from "./content.tsx";
2
+ export { MermaidBlock } from "./mermaid-block.tsx";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { mermaidContent } from "./content.js";
2
+ export { MermaidBlock } from "./mermaid-block.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAe,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAqB,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { type ReactNode } from "react";
2
+ /**
3
+ * Mermaid source drawn as its diagram (T-523). `sourceView` shows until the
4
+ * first render lands — the renderer is a lazy chunk — and stays one click
5
+ * away afterwards. A diagram that does not parse keeps its source on screen
6
+ * with the parser's complaint under it.
7
+ *
8
+ * The colors are the theme tokens in effect on this element. The diagram is
9
+ * drawn again whenever `useTheme()` reports a new theme, color scheme or
10
+ * variant; outside a ThemeProvider it keeps the colors present when it was
11
+ * drawn.
12
+ */
13
+ export declare function MermaidBlock({ source, sourceView, className, }: {
14
+ source: string;
15
+ /** Shown in place of the diagram. Default: a plain `<pre><code>`. */
16
+ sourceView?: ReactNode;
17
+ /** On the outer element, which also carries the probe the palette is read from. */
18
+ className?: string;
19
+ }): import("react").JSX.Element;
20
+ //# sourceMappingURL=mermaid-block.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mermaid-block.d.ts","sourceRoot":"","sources":["../src/mermaid-block.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,SAAS,EAIf,MAAM,OAAO,CAAC;AAsDf;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,UAAU,EACV,SAAS,GACV,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BAyEA"}
@@ -0,0 +1,91 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button } from "@orz/ui/components/button";
3
+ import { cn } from "@orz/ui/lib/utils";
4
+ import { useTheme } from "@orz/ui/theme";
5
+ import { useEffect, useRef, useState, } from "react";
6
+ import { errorSummary, renderDiagram, } from "./render.js";
7
+ let canvas;
8
+ /**
9
+ * Any CSS color → `#rrggbb`, composited over `under` so translucent tokens
10
+ * (the dark themes' borders) come out as the color they actually paint.
11
+ */
12
+ function toHex(color, under) {
13
+ canvas ??= document.createElement("canvas").getContext("2d", {
14
+ willReadFrequently: true,
15
+ });
16
+ if (canvas == null)
17
+ return color;
18
+ canvas.clearRect(0, 0, 1, 1);
19
+ canvas.fillStyle = under;
20
+ canvas.fillRect(0, 0, 1, 1);
21
+ canvas.fillStyle = color;
22
+ canvas.fillRect(0, 0, 1, 1);
23
+ const [r = 0, g = 0, b = 0] = canvas.getImageData(0, 0, 1, 1).data;
24
+ return `#${[r, g, b].map((v) => v.toString(16).padStart(2, "0")).join("")}`;
25
+ }
26
+ /** WCAG relative luminance of a `#rrggbb` color. */
27
+ function luminance(hex) {
28
+ const [r = 0, g = 0, b = 0] = [1, 3, 5].map((at) => {
29
+ const c = Number.parseInt(hex.slice(at, at + 2), 16) / 255;
30
+ return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
31
+ });
32
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
33
+ }
34
+ function readPalette(el) {
35
+ const style = getComputedStyle(el);
36
+ const token = (name) => style.getPropertyValue(name).trim();
37
+ const background = toHex(token("--background") || "#fff", "#fff");
38
+ const hex = (name) => toHex(token(name), background);
39
+ return {
40
+ // Read off the painted background rather than `.dark` on <html>: the
41
+ // theme root can be any element, and there may be no ThemeProvider at all.
42
+ dark: luminance(background) < 0.5,
43
+ background,
44
+ foreground: hex("--foreground"),
45
+ muted: hex("--muted"),
46
+ mutedForeground: hex("--muted-foreground"),
47
+ border: hex("--border"),
48
+ };
49
+ }
50
+ /**
51
+ * Mermaid source drawn as its diagram (T-523). `sourceView` shows until the
52
+ * first render lands — the renderer is a lazy chunk — and stays one click
53
+ * away afterwards. A diagram that does not parse keeps its source on screen
54
+ * with the parser's complaint under it.
55
+ *
56
+ * The colors are the theme tokens in effect on this element. The diagram is
57
+ * drawn again whenever `useTheme()` reports a new theme, color scheme or
58
+ * variant; outside a ThemeProvider it keeps the colors present when it was
59
+ * drawn.
60
+ */
61
+ export function MermaidBlock({ source, sourceView, className, }) {
62
+ const { theme, colorScheme, variant } = useTheme();
63
+ const probe = useRef(null);
64
+ const [result, setResult] = useState(null);
65
+ const [showSource, setShowSource] = useState(false);
66
+ useEffect(() => {
67
+ // Read only as a trigger: the colors come off the computed style below,
68
+ // which the store has already switched by the time it notifies.
69
+ void [theme, colorScheme, variant];
70
+ const el = probe.current;
71
+ if (el === null)
72
+ return;
73
+ let live = true;
74
+ renderDiagram(source, readPalette(el), getComputedStyle(el).fontFamily).then((next) => {
75
+ if (live)
76
+ setResult(next);
77
+ });
78
+ return () => {
79
+ live = false;
80
+ };
81
+ }, [source, theme, colorScheme, variant]);
82
+ const diagram = result?.ok === true ? result : null;
83
+ const sourceShown = sourceView ?? (_jsx("pre", { children: _jsx("code", { children: source }) }));
84
+ return (_jsxs("div", { ref: probe, className: cn(diagram === null ? undefined : "relative pt-7", className), children: [diagram !== null && !showSource ? (_jsx("div", { className: "mermaid-diagram overflow-x-auto", style: { "--diagram-width": `${diagram.width}px` },
85
+ // mermaid's own output under securityLevel "strict", which runs
86
+ // labels through DOMPurify: markup survives, scripts and handlers
87
+ // do not.
88
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: sanitized by mermaid, see above
89
+ dangerouslySetInnerHTML: { __html: diagram.svg } })) : (sourceShown), result?.ok === false && (_jsx("p", { className: "mt-1 truncate text-destructive text-xs", title: result.error, children: errorSummary(result.error) })), diagram !== null && (_jsx(Button, { type: "button", size: "xs", variant: "ghost", className: "absolute top-0 right-0", "aria-pressed": showSource, onClick: () => setShowSource((shown) => !shown), children: showSource ? "Diagram" : "Source" }))] }));
90
+ }
91
+ //# sourceMappingURL=mermaid-block.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mermaid-block.js","sourceRoot":"","sources":["../src/mermaid-block.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Colors a diagram is drawn in, already resolved to `#rrggbb`: mermaid's
3
+ * color math (khroma) cannot parse the app's oklch() custom properties.
4
+ */
5
+ export type DiagramPalette = {
6
+ dark: boolean;
7
+ background: string;
8
+ foreground: string;
9
+ muted: string;
10
+ mutedForeground: string;
11
+ border: string;
12
+ };
13
+ export type DiagramResult = {
14
+ ok: true;
15
+ svg: string;
16
+ width: number;
17
+ } | {
18
+ ok: false;
19
+ error: string;
20
+ };
21
+ export declare function renderDiagram(source: string, palette: DiagramPalette, fontFamily: string): Promise<DiagramResult>;
22
+ /** The diagram's natural width, which the fit rule in styles.css scales from. */
23
+ export declare function viewBoxWidth(svg: string): number;
24
+ /**
25
+ * mermaid's parse errors quote the offending line and draw a caret under it,
26
+ * which only lines up in the monospace block the error came from. One line
27
+ * is what fits under the source: where it failed, and what it got there.
28
+ */
29
+ export declare function errorSummary(message: string): string;
30
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAkBjC,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CAqCxB;AA2BD,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGhD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAUpD"}
package/dist/render.js ADDED
@@ -0,0 +1,99 @@
1
+ // mermaid is heavy (T-523); it is only ever reached through this dynamic
2
+ // import, so its chunks are fetched the first time a page actually holds a
3
+ // diagram.
4
+ let loaded;
5
+ function loadMermaid() {
6
+ loaded ??= import("mermaid").then((m) => m.default);
7
+ return loaded;
8
+ }
9
+ // `initialize` is global state, so a render for one palette must not
10
+ // interleave with a render for another: every call waits for the last.
11
+ let queue = Promise.resolve();
12
+ let sequence = 0;
13
+ export function renderDiagram(source, palette, fontFamily) {
14
+ const run = queue.then(async () => {
15
+ try {
16
+ let mermaid;
17
+ try {
18
+ mermaid = await loadMermaid();
19
+ }
20
+ catch (error) {
21
+ // A chunk that failed once (a dropped connection) would stay rejected
22
+ // in the memo and leave every later diagram on its source for the
23
+ // life of the page: forget it so the next render imports again.
24
+ // Whether that refetches is the browser's call; some still cache a
25
+ // failed module fetch.
26
+ loaded = undefined;
27
+ throw error;
28
+ }
29
+ mermaid.initialize({
30
+ startOnLoad: false,
31
+ securityLevel: "strict",
32
+ layout: "elk",
33
+ theme: "base",
34
+ fontFamily,
35
+ themeVariables: themeVariables(palette, fontFamily),
36
+ });
37
+ // parse() first: it throws the readable error without touching the
38
+ // DOM, where a failing render() can leave its scratch node behind.
39
+ await mermaid.parse(source);
40
+ const { svg } = await mermaid.render(`mermaid-${++sequence}`, source);
41
+ return { ok: true, svg, width: viewBoxWidth(svg) };
42
+ }
43
+ catch (error) {
44
+ return {
45
+ ok: false,
46
+ error: error instanceof Error ? error.message : String(error),
47
+ };
48
+ }
49
+ });
50
+ queue = run.catch(() => undefined);
51
+ return run;
52
+ }
53
+ function themeVariables(p, fontFamily) {
54
+ return {
55
+ darkMode: p.dark,
56
+ fontFamily,
57
+ background: p.background,
58
+ primaryColor: p.muted,
59
+ primaryTextColor: p.foreground,
60
+ primaryBorderColor: p.mutedForeground,
61
+ secondaryColor: p.muted,
62
+ tertiaryColor: p.background,
63
+ mainBkg: p.muted,
64
+ nodeBorder: p.mutedForeground,
65
+ textColor: p.foreground,
66
+ nodeTextColor: p.foreground,
67
+ lineColor: p.mutedForeground,
68
+ clusterBkg: p.background,
69
+ clusterBorder: p.border,
70
+ edgeLabelBackground: p.background,
71
+ titleColor: p.foreground,
72
+ noteBkgColor: p.muted,
73
+ noteTextColor: p.foreground,
74
+ noteBorderColor: p.border,
75
+ };
76
+ }
77
+ /** The diagram's natural width, which the fit rule in styles.css scales from. */
78
+ export function viewBoxWidth(svg) {
79
+ const match = /viewBox="[-\d.]+[\s,]+[-\d.]+[\s,]+([\d.]+)/.exec(svg);
80
+ return match ? Number(match[1]) : 0;
81
+ }
82
+ /**
83
+ * mermaid's parse errors quote the offending line and draw a caret under it,
84
+ * which only lines up in the monospace block the error came from. One line
85
+ * is what fits under the source: where it failed, and what it got there.
86
+ */
87
+ export function errorSummary(message) {
88
+ const lines = message
89
+ .split("\n")
90
+ .map((line) => line.trim())
91
+ .filter(Boolean);
92
+ if (lines.length <= 1)
93
+ return lines[0] ?? "";
94
+ const got = lines.at(-1) ?? "";
95
+ return /^Parse error/.test(lines[0] ?? "") && /got /.test(got)
96
+ ? `${lines[0]} ${got}`
97
+ : (lines[0] ?? "");
98
+ }
99
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,56 @@
1
1
  {
2
2
  "name": "@orz/ui-mermaid",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Mermaid diagrams drawn in the active @orz/ui theme, contributed to the @orz/ui-content registry",
5
+ "license": "BSD-3-Clause",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/orzFly/ui.git",
9
+ "directory": "packages/ui-mermaid"
10
+ },
11
+ "type": "module",
12
+ "sideEffects": [
13
+ "**/*.css"
14
+ ],
15
+ "exports": {
16
+ ".": {
17
+ "@orz/source": "./src/index.ts",
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ },
21
+ "./styles.css": "./styles.css"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "styles.css"
26
+ ],
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "peerDependencies": {
31
+ "react": "^19",
32
+ "@orz/ui-content": "^0.1.0",
33
+ "@orz/ui": "^0.1.0"
34
+ },
35
+ "dependencies": {
36
+ "mermaid": "^12.2.0"
37
+ },
38
+ "devDependencies": {
39
+ "@testing-library/react": "^16.3.2",
40
+ "@types/react": "^19.2.18",
41
+ "@types/react-dom": "^19.2.4",
42
+ "happy-dom": "^21.0.3",
43
+ "radix-ui": "^1.6.7",
44
+ "react": "^19.2.8",
45
+ "react-dom": "^19.2.8",
46
+ "vitest": "^5.0.5",
47
+ "@orz/themes": "0.1.0",
48
+ "@orz/ui": "0.1.0",
49
+ "@orz/ui-content": "0.1.0"
50
+ },
51
+ "scripts": {
52
+ "build": "tsc -p tsconfig.build.json",
53
+ "typecheck": "tsc --noEmit",
54
+ "test": "vitest run"
55
+ }
6
56
  }
package/styles.css ADDED
@@ -0,0 +1,22 @@
1
+ /* Tailwind resolves an imported file's @source against that file, so a
2
+ consumer importing this stylesheet scans the classes in the package's
3
+ shipped JS without writing an @source of its own. */
4
+ @source "./dist";
5
+
6
+ @layer components {
7
+ /* A diagram shrinks to the column, but never below 75% of its natural
8
+ width — past that its labels stop being readable, so it scrolls instead
9
+ (T-523). Never enlarged. `!important` beats the width attribute and the
10
+ inline max-width mermaid writes onto its own <svg>. */
11
+ /* biome-ignore-start lint/complexity/noImportantStyles: mermaid's inline style, see above */
12
+ .mermaid-diagram > svg {
13
+ display: block;
14
+ width: min(
15
+ var(--diagram-width),
16
+ max(100%, calc(var(--diagram-width) * 0.75))
17
+ ) !important;
18
+ max-width: none !important;
19
+ height: auto;
20
+ }
21
+ /* biome-ignore-end lint/complexity/noImportantStyles: mermaid's inline style, see above */
22
+ }