@blaaiz/docs-core 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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +103 -0
  3. package/dist/chunk-3ZX4WIE3.js +2984 -0
  4. package/dist/chunk-3ZX4WIE3.js.map +1 -0
  5. package/dist/chunk-JCYR6RPE.js +31 -0
  6. package/dist/chunk-JCYR6RPE.js.map +1 -0
  7. package/dist/chunk-ZKOOKLZ3.js +124 -0
  8. package/dist/chunk-ZKOOKLZ3.js.map +1 -0
  9. package/dist/cli.js +534 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/generator.cjs +508 -0
  12. package/dist/generator.cjs.map +1 -0
  13. package/dist/generator.d.cts +122 -0
  14. package/dist/generator.d.ts +122 -0
  15. package/dist/generator.js +177 -0
  16. package/dist/generator.js.map +1 -0
  17. package/dist/index.cjs +3024 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.cts +1182 -0
  20. package/dist/index.d.ts +1182 -0
  21. package/dist/index.js +3 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/navigation-CGqFIPlP.d.cts +498 -0
  24. package/dist/navigation-CGqFIPlP.d.ts +498 -0
  25. package/dist/openapi-types-CJ6p5Cux.d.cts +78 -0
  26. package/dist/openapi-types-CJ6p5Cux.d.ts +78 -0
  27. package/dist/ui/api-try-it.cjs +654 -0
  28. package/dist/ui/api-try-it.cjs.map +1 -0
  29. package/dist/ui/api-try-it.d.cts +78 -0
  30. package/dist/ui/api-try-it.d.ts +78 -0
  31. package/dist/ui/api-try-it.js +509 -0
  32. package/dist/ui/api-try-it.js.map +1 -0
  33. package/dist/ui/ask-ai.cjs +810 -0
  34. package/dist/ui/ask-ai.cjs.map +1 -0
  35. package/dist/ui/ask-ai.d.cts +57 -0
  36. package/dist/ui/ask-ai.d.ts +57 -0
  37. package/dist/ui/ask-ai.js +808 -0
  38. package/dist/ui/ask-ai.js.map +1 -0
  39. package/dist/ui/copy-page.cjs +312 -0
  40. package/dist/ui/copy-page.cjs.map +1 -0
  41. package/dist/ui/copy-page.d.cts +33 -0
  42. package/dist/ui/copy-page.d.ts +33 -0
  43. package/dist/ui/copy-page.js +183 -0
  44. package/dist/ui/copy-page.js.map +1 -0
  45. package/dist/ui/mermaid.cjs +363 -0
  46. package/dist/ui/mermaid.cjs.map +1 -0
  47. package/dist/ui/mermaid.d.cts +13 -0
  48. package/dist/ui/mermaid.d.ts +13 -0
  49. package/dist/ui/mermaid.js +361 -0
  50. package/dist/ui/mermaid.js.map +1 -0
  51. package/dist/ui.cjs +661 -0
  52. package/dist/ui.cjs.map +1 -0
  53. package/dist/ui.d.cts +428 -0
  54. package/dist/ui.d.ts +428 -0
  55. package/dist/ui.js +537 -0
  56. package/dist/ui.js.map +1 -0
  57. package/package.json +145 -0
  58. package/patches/fumadocs-openapi.patch +173 -0
  59. package/skills/AGENTS-section.md +36 -0
  60. package/skills/SKILL.md +363 -0
  61. package/styles/api-reference.css +1417 -0
  62. package/styles/ask-ai.css +563 -0
  63. package/styles/auth.css +462 -0
  64. package/styles/docs.css +247 -0
  65. package/styles/home.css +376 -0
  66. package/templates/init/content/docs/index.mdx.tmpl +52 -0
  67. package/templates/init/content/docs/meta.json.tmpl +3 -0
  68. package/templates/init/content/docs.json.tmpl +12 -0
  69. package/templates/init/content/nav.json.tmpl +7 -0
  70. package/templates/init/docs.config.ts.tmpl +36 -0
  71. package/templates/init/env.example.tmpl +15 -0
@@ -0,0 +1,361 @@
1
+ 'use client';
2
+ import { useState, useEffect, useId, useRef, useCallback } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ var MIN_SCALE = 0.4;
7
+ var MAX_SCALE = 8;
8
+ var MAX_FIT_SCALE = 2.5;
9
+ var FIT_PADDING = 0.9;
10
+ var IDENTITY = { scale: 1, x: 0, y: 0 };
11
+ function clamp(value, min, max) {
12
+ return Math.min(max, Math.max(min, value));
13
+ }
14
+ function isDarkTheme() {
15
+ return document.documentElement.classList.contains("dark");
16
+ }
17
+ function useMermaidSvg(chart) {
18
+ const rawId = useId().replace(/[^a-zA-Z0-9]/g, "");
19
+ const [svg, setSvg] = useState("");
20
+ const [dark, setDark] = useState(false);
21
+ useEffect(() => {
22
+ setDark(isDarkTheme());
23
+ const observer = new MutationObserver(() => {
24
+ setDark(isDarkTheme());
25
+ });
26
+ observer.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] });
27
+ return () => {
28
+ observer.disconnect();
29
+ };
30
+ }, []);
31
+ useEffect(() => {
32
+ let active = true;
33
+ void (async () => {
34
+ const mermaid = (await import('mermaid')).default;
35
+ mermaid.initialize({
36
+ startOnLoad: false,
37
+ theme: dark ? "dark" : "neutral",
38
+ securityLevel: "loose",
39
+ fontFamily: "inherit",
40
+ themeVariables: { fontSize: "14px" }
41
+ });
42
+ try {
43
+ const result = await mermaid.render(`mmd-${rawId}`, chart);
44
+ if (active) {
45
+ setSvg(result.svg);
46
+ }
47
+ } catch {
48
+ if (active) {
49
+ setSvg("");
50
+ }
51
+ }
52
+ })();
53
+ return () => {
54
+ active = false;
55
+ };
56
+ }, [chart, dark, rawId]);
57
+ return svg;
58
+ }
59
+ var ICONS = {
60
+ zoomIn: /* @__PURE__ */ jsx("path", { d: "M11 8v6M8 11h6M21 21l-4.35-4.35M11 18a7 7 0 1 0 0-14 7 7 0 0 0 0 14Z" }),
61
+ zoomOut: /* @__PURE__ */ jsx("path", { d: "M8 11h6M21 21l-4.35-4.35M11 18a7 7 0 1 0 0-14 7 7 0 0 0 0 14Z" }),
62
+ reset: /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 1 0 3-6.7L3 8m0-5v5h5" }),
63
+ expand: /* @__PURE__ */ jsx("path", { d: "M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3m8 0h3a2 2 0 0 0 2-2v-3" }),
64
+ close: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" })
65
+ };
66
+ function ControlButton({
67
+ label,
68
+ onClick,
69
+ children
70
+ }) {
71
+ return /* @__PURE__ */ jsx(
72
+ "button",
73
+ {
74
+ type: "button",
75
+ "aria-label": label,
76
+ onClick,
77
+ style: {
78
+ display: "inline-flex",
79
+ alignItems: "center",
80
+ justifyContent: "center",
81
+ width: 32,
82
+ height: 32,
83
+ padding: 0,
84
+ border: "1px solid var(--color-fd-border)",
85
+ borderRadius: 8,
86
+ background: "var(--color-fd-card)",
87
+ color: "var(--color-fd-muted-foreground)",
88
+ cursor: "pointer"
89
+ },
90
+ onMouseEnter: (event) => {
91
+ event.currentTarget.style.color = "var(--color-fd-foreground)";
92
+ event.currentTarget.style.background = "color-mix(in oklab, var(--color-fd-primary) 10%, var(--color-fd-card))";
93
+ },
94
+ onMouseLeave: (event) => {
95
+ event.currentTarget.style.color = "var(--color-fd-muted-foreground)";
96
+ event.currentTarget.style.background = "var(--color-fd-card)";
97
+ },
98
+ children: /* @__PURE__ */ jsx(
99
+ "svg",
100
+ {
101
+ width: 16,
102
+ height: 16,
103
+ viewBox: "0 0 24 24",
104
+ fill: "none",
105
+ stroke: "currentColor",
106
+ strokeWidth: 1.8,
107
+ strokeLinecap: "round",
108
+ strokeLinejoin: "round",
109
+ "aria-hidden": "true",
110
+ children
111
+ }
112
+ )
113
+ }
114
+ );
115
+ }
116
+ function Viewer({ svg, fullscreen, onToggleFullscreen }) {
117
+ const stageRef = useRef(null);
118
+ const contentRef = useRef(null);
119
+ const [view, setView] = useState(IDENTITY);
120
+ const drag = useRef(null);
121
+ const zoomAt = useCallback((clientX, clientY, factor) => {
122
+ const stage = stageRef.current;
123
+ if (!stage) {
124
+ return;
125
+ }
126
+ const rect = stage.getBoundingClientRect();
127
+ const px = clientX - rect.left;
128
+ const py = clientY - rect.top;
129
+ setView((current) => {
130
+ const scale = clamp(current.scale * factor, MIN_SCALE, MAX_SCALE);
131
+ const ratio = scale / current.scale;
132
+ return { scale, x: px - (px - current.x) * ratio, y: py - (py - current.y) * ratio };
133
+ });
134
+ }, []);
135
+ const zoomFromCenter = useCallback(
136
+ (factor) => {
137
+ const stage = stageRef.current;
138
+ if (!stage) {
139
+ return;
140
+ }
141
+ const rect = stage.getBoundingClientRect();
142
+ zoomAt(rect.left + rect.width / 2, rect.top + rect.height / 2, factor);
143
+ },
144
+ [zoomAt]
145
+ );
146
+ const fit = useCallback(() => {
147
+ const stage = stageRef.current;
148
+ const content = contentRef.current;
149
+ if (!stage || !content) {
150
+ return;
151
+ }
152
+ const w = content.offsetWidth;
153
+ const h = content.offsetHeight;
154
+ if (w === 0 || h === 0) {
155
+ return;
156
+ }
157
+ const sw = stage.clientWidth;
158
+ const sh = stage.clientHeight;
159
+ const scale = clamp(Math.min(sw / w, sh / h) * FIT_PADDING, MIN_SCALE, MAX_FIT_SCALE);
160
+ setView({ scale, x: (sw - w * scale) / 2, y: (sh - h * scale) / 2 });
161
+ }, []);
162
+ useEffect(() => {
163
+ const stage = stageRef.current;
164
+ if (!stage) {
165
+ return;
166
+ }
167
+ const onWheel = (event) => {
168
+ event.preventDefault();
169
+ zoomAt(event.clientX, event.clientY, event.deltaY < 0 ? 1.12 : 1 / 1.12);
170
+ };
171
+ stage.addEventListener("wheel", onWheel, { passive: false });
172
+ return () => {
173
+ stage.removeEventListener("wheel", onWheel);
174
+ };
175
+ }, [zoomAt]);
176
+ useEffect(() => {
177
+ fit();
178
+ }, [svg, fullscreen, fit]);
179
+ const onPointerDown = (event) => {
180
+ drag.current = { x: event.clientX, y: event.clientY };
181
+ event.currentTarget.setPointerCapture(event.pointerId);
182
+ };
183
+ const onPointerMove = (event) => {
184
+ if (!drag.current) {
185
+ return;
186
+ }
187
+ const dx = event.clientX - drag.current.x;
188
+ const dy = event.clientY - drag.current.y;
189
+ drag.current = { x: event.clientX, y: event.clientY };
190
+ setView((current) => ({ ...current, x: current.x + dx, y: current.y + dy }));
191
+ };
192
+ const onPointerUp = (event) => {
193
+ drag.current = null;
194
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
195
+ event.currentTarget.releasePointerCapture(event.pointerId);
196
+ }
197
+ };
198
+ return /* @__PURE__ */ jsxs("div", { style: { position: "relative", width: "100%", height: fullscreen ? "100%" : 420 }, children: [
199
+ /* @__PURE__ */ jsx(
200
+ "div",
201
+ {
202
+ ref: stageRef,
203
+ onPointerDown,
204
+ onPointerMove,
205
+ onPointerUp,
206
+ onPointerLeave: onPointerUp,
207
+ style: {
208
+ position: "absolute",
209
+ inset: 0,
210
+ overflow: "hidden",
211
+ cursor: "grab",
212
+ touchAction: "none"
213
+ },
214
+ children: /* @__PURE__ */ jsx(
215
+ "div",
216
+ {
217
+ ref: contentRef,
218
+ style: {
219
+ transform: `translate(${String(view.x)}px, ${String(view.y)}px) scale(${String(view.scale)})`,
220
+ transformOrigin: "0 0",
221
+ display: "inline-block",
222
+ willChange: "transform"
223
+ },
224
+ dangerouslySetInnerHTML: { __html: svg }
225
+ }
226
+ )
227
+ }
228
+ ),
229
+ /* @__PURE__ */ jsxs(
230
+ "div",
231
+ {
232
+ style: {
233
+ position: "absolute",
234
+ bottom: 12,
235
+ right: 12,
236
+ display: "flex",
237
+ flexDirection: "column",
238
+ gap: 6
239
+ },
240
+ children: [
241
+ /* @__PURE__ */ jsx(
242
+ ControlButton,
243
+ {
244
+ label: fullscreen ? "Exit full screen" : "Full screen",
245
+ onClick: onToggleFullscreen,
246
+ children: fullscreen ? ICONS.close : ICONS.expand
247
+ }
248
+ ),
249
+ /* @__PURE__ */ jsx(
250
+ ControlButton,
251
+ {
252
+ label: "Zoom in",
253
+ onClick: () => {
254
+ zoomFromCenter(1.3);
255
+ },
256
+ children: ICONS.zoomIn
257
+ }
258
+ ),
259
+ /* @__PURE__ */ jsx(
260
+ ControlButton,
261
+ {
262
+ label: "Zoom out",
263
+ onClick: () => {
264
+ zoomFromCenter(1 / 1.3);
265
+ },
266
+ children: ICONS.zoomOut
267
+ }
268
+ ),
269
+ /* @__PURE__ */ jsx(ControlButton, { label: "Reset view", onClick: fit, children: ICONS.reset })
270
+ ]
271
+ }
272
+ )
273
+ ] });
274
+ }
275
+ function Mermaid({ chart }) {
276
+ const svg = useMermaidSvg(chart);
277
+ const [full, setFull] = useState(false);
278
+ const [mounted, setMounted] = useState(false);
279
+ useEffect(() => {
280
+ setMounted(true);
281
+ }, []);
282
+ useEffect(() => {
283
+ if (!full) {
284
+ return;
285
+ }
286
+ const onKey = (event) => {
287
+ if (event.key === "Escape") {
288
+ setFull(false);
289
+ }
290
+ };
291
+ document.addEventListener("keydown", onKey);
292
+ return () => {
293
+ document.removeEventListener("keydown", onKey);
294
+ };
295
+ }, [full]);
296
+ if (svg === "") {
297
+ return null;
298
+ }
299
+ return /* @__PURE__ */ jsxs("div", { style: { margin: "1.5rem 0", borderRadius: 12, overflow: "hidden" }, children: [
300
+ /* @__PURE__ */ jsx(
301
+ Viewer,
302
+ {
303
+ svg,
304
+ fullscreen: false,
305
+ onToggleFullscreen: () => {
306
+ setFull(true);
307
+ }
308
+ }
309
+ ),
310
+ mounted && full ? createPortal(
311
+ /* @__PURE__ */ jsx(
312
+ "div",
313
+ {
314
+ onClick: (event) => {
315
+ if (event.target === event.currentTarget) {
316
+ setFull(false);
317
+ }
318
+ },
319
+ style: {
320
+ position: "fixed",
321
+ inset: 0,
322
+ zIndex: 9999,
323
+ background: "color-mix(in oklab, black 55%, transparent)",
324
+ backdropFilter: "blur(2px)"
325
+ },
326
+ children: /* @__PURE__ */ jsx(
327
+ "div",
328
+ {
329
+ style: {
330
+ position: "absolute",
331
+ inset: "clamp(20px, 3.5vw, 48px)",
332
+ borderRadius: 16,
333
+ border: "1px solid var(--color-fd-border)",
334
+ overflow: "hidden",
335
+ background: "var(--color-fd-background, #0a0a0b)",
336
+ backgroundImage: "radial-gradient(circle, color-mix(in oklab, var(--color-fd-muted-foreground) 22%, transparent) 1px, transparent 1px)",
337
+ backgroundSize: "22px 22px",
338
+ boxShadow: "0 24px 70px -12px rgba(0, 0, 0, 0.5)"
339
+ },
340
+ children: /* @__PURE__ */ jsx(
341
+ Viewer,
342
+ {
343
+ svg,
344
+ fullscreen: true,
345
+ onToggleFullscreen: () => {
346
+ setFull(false);
347
+ }
348
+ }
349
+ )
350
+ }
351
+ )
352
+ }
353
+ ),
354
+ document.body
355
+ ) : null
356
+ ] });
357
+ }
358
+
359
+ export { Mermaid };
360
+ //# sourceMappingURL=mermaid.js.map
361
+ //# sourceMappingURL=mermaid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ui/mermaid.tsx"],"names":[],"mappings":"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","file":"mermaid.js","sourcesContent":["'use client';\n\nimport {\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type PointerEvent as ReactPointerEvent,\n type ReactNode,\n} from 'react';\nimport { createPortal } from 'react-dom';\n\n/**\n * The Mermaid diagram viewer — renders a chart to SVG and wraps it in an\n * interactive stage: wheel to zoom, drag to pan, a control cluster, and a\n * full-screen lightbox on a dot-grid backdrop. Matches the premium viewer a\n * reader expects from a modern docs site.\n *\n * Client-only and self-contained: it dynamically imports `mermaid` (an optional\n * peer dependency), styles itself with inline styles keyed to the framework's\n * `--color-fd-*` theme variables, and re-renders when the site toggles dark\n * mode. It lives on its own entry (`@blaaiz/docs-core/ui/mermaid`) so the heavy\n * `mermaid` import never reaches the server-compatible `ui` barrel.\n *\n * @packageDocumentation\n */\n\nconst MIN_SCALE = 0.4;\nconst MAX_SCALE = 8;\nconst MAX_FIT_SCALE = 2.5;\nconst FIT_PADDING = 0.9;\n\ninterface View {\n readonly scale: number;\n readonly x: number;\n readonly y: number;\n}\n\nconst IDENTITY: View = { scale: 1, x: 0, y: 0 };\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n\nfunction isDarkTheme(): boolean {\n return document.documentElement.classList.contains('dark');\n}\n\n/**\n * Render a Mermaid chart to an SVG string, re-rendering when the chart changes\n * or the site switches between light and dark themes.\n */\nfunction useMermaidSvg(chart: string): string {\n const rawId = useId().replace(/[^a-zA-Z0-9]/g, '');\n const [svg, setSvg] = useState('');\n const [dark, setDark] = useState(false);\n\n useEffect(() => {\n setDark(isDarkTheme());\n const observer = new MutationObserver(() => {\n setDark(isDarkTheme());\n });\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });\n return () => {\n observer.disconnect();\n };\n }, []);\n\n useEffect(() => {\n let active = true;\n void (async () => {\n const mermaid = (await import('mermaid')).default;\n mermaid.initialize({\n startOnLoad: false,\n theme: dark ? 'dark' : 'neutral',\n securityLevel: 'loose',\n fontFamily: 'inherit',\n themeVariables: { fontSize: '14px' },\n });\n try {\n const result = await mermaid.render(`mmd-${rawId}`, chart);\n if (active) {\n setSvg(result.svg);\n }\n } catch {\n if (active) {\n setSvg('');\n }\n }\n })();\n return () => {\n active = false;\n };\n }, [chart, dark, rawId]);\n\n return svg;\n}\n\nconst ICONS = {\n zoomIn: <path d=\"M11 8v6M8 11h6M21 21l-4.35-4.35M11 18a7 7 0 1 0 0-14 7 7 0 0 0 0 14Z\" />,\n zoomOut: <path d=\"M8 11h6M21 21l-4.35-4.35M11 18a7 7 0 1 0 0-14 7 7 0 0 0 0 14Z\" />,\n reset: <path d=\"M3 12a9 9 0 1 0 3-6.7L3 8m0-5v5h5\" />,\n expand: (\n <path d=\"M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3m8 0h3a2 2 0 0 0 2-2v-3\" />\n ),\n close: <path d=\"M18 6 6 18M6 6l12 12\" />,\n};\n\nfunction ControlButton({\n label,\n onClick,\n children,\n}: {\n label: string;\n onClick: () => void;\n children: ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n aria-label={label}\n onClick={onClick}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n padding: 0,\n border: '1px solid var(--color-fd-border)',\n borderRadius: 8,\n background: 'var(--color-fd-card)',\n color: 'var(--color-fd-muted-foreground)',\n cursor: 'pointer',\n }}\n onMouseEnter={(event) => {\n event.currentTarget.style.color = 'var(--color-fd-foreground)';\n event.currentTarget.style.background =\n 'color-mix(in oklab, var(--color-fd-primary) 10%, var(--color-fd-card))';\n }}\n onMouseLeave={(event) => {\n event.currentTarget.style.color = 'var(--color-fd-muted-foreground)';\n event.currentTarget.style.background = 'var(--color-fd-card)';\n }}\n >\n <svg\n width={16}\n height={16}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.8}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n {children}\n </svg>\n </button>\n );\n}\n\ninterface ViewerProps {\n readonly svg: string;\n readonly fullscreen: boolean;\n readonly onToggleFullscreen: () => void;\n}\n\n/**\n * The interactive diagram surface: the transformed SVG plus its control cluster.\n * The controls sit as a sibling of the draggable stage (not a child), so a click\n * on a control never triggers the stage's pan/pointer-capture. Reused for both\n * the inline card and the full-screen lightbox.\n */\nfunction Viewer({ svg, fullscreen, onToggleFullscreen }: ViewerProps) {\n const stageRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const [view, setView] = useState<View>(IDENTITY);\n const drag = useRef<{ x: number; y: number } | null>(null);\n\n const zoomAt = useCallback((clientX: number, clientY: number, factor: number) => {\n const stage = stageRef.current;\n if (!stage) {\n return;\n }\n const rect = stage.getBoundingClientRect();\n const px = clientX - rect.left;\n const py = clientY - rect.top;\n setView((current) => {\n const scale = clamp(current.scale * factor, MIN_SCALE, MAX_SCALE);\n const ratio = scale / current.scale;\n return { scale, x: px - (px - current.x) * ratio, y: py - (py - current.y) * ratio };\n });\n }, []);\n\n const zoomFromCenter = useCallback(\n (factor: number) => {\n const stage = stageRef.current;\n if (!stage) {\n return;\n }\n const rect = stage.getBoundingClientRect();\n zoomAt(rect.left + rect.width / 2, rect.top + rect.height / 2, factor);\n },\n [zoomAt],\n );\n\n // Scale the diagram to fit the stage and centre it. `offsetWidth/Height` are\n // the untransformed layout size, so this is independent of the current zoom.\n const fit = useCallback(() => {\n const stage = stageRef.current;\n const content = contentRef.current;\n if (!stage || !content) {\n return;\n }\n const w = content.offsetWidth;\n const h = content.offsetHeight;\n if (w === 0 || h === 0) {\n return;\n }\n const sw = stage.clientWidth;\n const sh = stage.clientHeight;\n const scale = clamp(Math.min(sw / w, sh / h) * FIT_PADDING, MIN_SCALE, MAX_FIT_SCALE);\n setView({ scale, x: (sw - w * scale) / 2, y: (sh - h * scale) / 2 });\n }, []);\n\n // Wheel-zoom needs a non-passive listener; React attaches `onWheel` as passive\n // and would warn when we call preventDefault.\n useEffect(() => {\n const stage = stageRef.current;\n if (!stage) {\n return;\n }\n const onWheel = (event: WheelEvent) => {\n event.preventDefault();\n zoomAt(event.clientX, event.clientY, event.deltaY < 0 ? 1.12 : 1 / 1.12);\n };\n stage.addEventListener('wheel', onWheel, { passive: false });\n return () => {\n stage.removeEventListener('wheel', onWheel);\n };\n }, [zoomAt]);\n\n // Fit whenever the diagram or the surface size changes.\n useEffect(() => {\n fit();\n }, [svg, fullscreen, fit]);\n\n const onPointerDown = (event: ReactPointerEvent<HTMLDivElement>) => {\n drag.current = { x: event.clientX, y: event.clientY };\n event.currentTarget.setPointerCapture(event.pointerId);\n };\n\n const onPointerMove = (event: ReactPointerEvent<HTMLDivElement>) => {\n if (!drag.current) {\n return;\n }\n const dx = event.clientX - drag.current.x;\n const dy = event.clientY - drag.current.y;\n drag.current = { x: event.clientX, y: event.clientY };\n setView((current) => ({ ...current, x: current.x + dx, y: current.y + dy }));\n };\n\n const onPointerUp = (event: ReactPointerEvent<HTMLDivElement>) => {\n drag.current = null;\n if (event.currentTarget.hasPointerCapture(event.pointerId)) {\n event.currentTarget.releasePointerCapture(event.pointerId);\n }\n };\n\n return (\n <div style={{ position: 'relative', width: '100%', height: fullscreen ? '100%' : 420 }}>\n <div\n ref={stageRef}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerLeave={onPointerUp}\n style={{\n position: 'absolute',\n inset: 0,\n overflow: 'hidden',\n cursor: 'grab',\n touchAction: 'none',\n }}\n >\n <div\n ref={contentRef}\n style={{\n transform: `translate(${String(view.x)}px, ${String(view.y)}px) scale(${String(view.scale)})`,\n transformOrigin: '0 0',\n display: 'inline-block',\n willChange: 'transform',\n }}\n dangerouslySetInnerHTML={{ __html: svg }}\n />\n </div>\n <div\n style={{\n position: 'absolute',\n bottom: 12,\n right: 12,\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n }}\n >\n <ControlButton\n label={fullscreen ? 'Exit full screen' : 'Full screen'}\n onClick={onToggleFullscreen}\n >\n {fullscreen ? ICONS.close : ICONS.expand}\n </ControlButton>\n <ControlButton\n label=\"Zoom in\"\n onClick={() => {\n zoomFromCenter(1.3);\n }}\n >\n {ICONS.zoomIn}\n </ControlButton>\n <ControlButton\n label=\"Zoom out\"\n onClick={() => {\n zoomFromCenter(1 / 1.3);\n }}\n >\n {ICONS.zoomOut}\n </ControlButton>\n <ControlButton label=\"Reset view\" onClick={fit}>\n {ICONS.reset}\n </ControlButton>\n </div>\n </div>\n );\n}\n\n/**\n * Render a Mermaid chart in an interactive, zoomable viewer.\n *\n * @param props - the Mermaid source in `chart`\n * @public\n */\nexport function Mermaid({ chart }: { chart: string }) {\n const svg = useMermaidSvg(chart);\n const [full, setFull] = useState(false);\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => {\n setMounted(true);\n }, []);\n\n useEffect(() => {\n if (!full) {\n return;\n }\n const onKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setFull(false);\n }\n };\n document.addEventListener('keydown', onKey);\n return () => {\n document.removeEventListener('keydown', onKey);\n };\n }, [full]);\n\n if (svg === '') {\n return null;\n }\n\n return (\n <div style={{ margin: '1.5rem 0', borderRadius: 12, overflow: 'hidden' }}>\n <Viewer\n svg={svg}\n fullscreen={false}\n onToggleFullscreen={() => {\n setFull(true);\n }}\n />\n {mounted && full\n ? createPortal(\n <div\n onClick={(event) => {\n // Click the dimmed gap around the panel to close.\n if (event.target === event.currentTarget) {\n setFull(false);\n }\n }}\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 9999,\n background: 'color-mix(in oklab, black 55%, transparent)',\n backdropFilter: 'blur(2px)',\n }}\n >\n <div\n style={{\n position: 'absolute',\n inset: 'clamp(20px, 3.5vw, 48px)',\n borderRadius: 16,\n border: '1px solid var(--color-fd-border)',\n overflow: 'hidden',\n background: 'var(--color-fd-background, #0a0a0b)',\n backgroundImage:\n 'radial-gradient(circle, color-mix(in oklab, var(--color-fd-muted-foreground) 22%, transparent) 1px, transparent 1px)',\n backgroundSize: '22px 22px',\n boxShadow: '0 24px 70px -12px rgba(0, 0, 0, 0.5)',\n }}\n >\n <Viewer\n svg={svg}\n fullscreen\n onToggleFullscreen={() => {\n setFull(false);\n }}\n />\n </div>\n </div>,\n document.body,\n )\n : null}\n </div>\n );\n}\n"]}