@windrun-huaiin/third-ui 21.0.0 → 22.0.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 (107) hide show
  1. package/dist/ai/ai-markdown.js +1 -1
  2. package/dist/ai/ai-markdown.mjs +1 -1
  3. package/dist/clerk/clerk-page-context-generator.js +3 -2
  4. package/dist/clerk/clerk-page-context-generator.mjs +3 -2
  5. package/dist/clerk/clerk-page-generator.js +4 -3
  6. package/dist/clerk/clerk-page-generator.mjs +4 -3
  7. package/dist/fuma/base/custom-header.d.ts +1 -1
  8. package/dist/fuma/base/custom-header.js +38 -36
  9. package/dist/fuma/base/custom-header.mjs +25 -23
  10. package/dist/fuma/base/custom-home-layout.d.ts +1 -1
  11. package/dist/fuma/base/custom-home-layout.js +1 -1
  12. package/dist/fuma/base/custom-home-layout.mjs +1 -1
  13. package/dist/fuma/base/header-theme-switch.d.ts +5 -0
  14. package/dist/fuma/base/header-theme-switch.js +42 -0
  15. package/dist/fuma/base/header-theme-switch.mjs +40 -0
  16. package/dist/fuma/base/index.d.ts +1 -0
  17. package/dist/fuma/base/index.js +7 -0
  18. package/dist/fuma/base/index.mjs +1 -0
  19. package/dist/fuma/base/site-layout.d.ts +116 -0
  20. package/dist/fuma/base/site-layout.js +72 -0
  21. package/dist/fuma/base/site-layout.mjs +65 -0
  22. package/dist/fuma/fuma-banner-suit.js +9 -6
  23. package/dist/fuma/fuma-banner-suit.mjs +10 -7
  24. package/dist/fuma/fuma-page-genarator.js +1 -1
  25. package/dist/fuma/fuma-page-genarator.mjs +1 -1
  26. package/dist/fuma/heavy/image-grid.d.ts +6 -0
  27. package/dist/fuma/heavy/image-grid.js +17 -0
  28. package/dist/fuma/heavy/image-grid.mjs +15 -0
  29. package/dist/fuma/heavy/image-zoom.d.ts +22 -0
  30. package/dist/fuma/heavy/image-zoom.js +39 -0
  31. package/dist/fuma/heavy/image-zoom.mjs +37 -0
  32. package/dist/fuma/heavy/index.d.ts +4 -0
  33. package/dist/fuma/heavy/index.js +15 -0
  34. package/dist/fuma/heavy/index.mjs +5 -0
  35. package/dist/fuma/heavy/math.d.ts +17 -0
  36. package/dist/fuma/heavy/math.js +60 -0
  37. package/dist/fuma/heavy/math.mjs +57 -0
  38. package/dist/fuma/heavy/mermaid.d.ts +13 -0
  39. package/dist/fuma/heavy/mermaid.js +360 -0
  40. package/dist/fuma/heavy/mermaid.mjs +358 -0
  41. package/dist/fuma/mdx/features.d.ts +8 -0
  42. package/dist/fuma/mdx/features.js +92 -0
  43. package/dist/fuma/mdx/features.mjs +85 -0
  44. package/dist/fuma/mdx/index.d.ts +0 -5
  45. package/dist/fuma/mdx/index.js +0 -11
  46. package/dist/fuma/mdx/index.mjs +0 -5
  47. package/dist/fuma/mdx/markdown-component-map.js +7 -1
  48. package/dist/fuma/mdx/markdown-component-map.mjs +7 -1
  49. package/dist/fuma/mdx/site-mdx-components.d.ts +13 -0
  50. package/dist/fuma/mdx/site-mdx-components.js +19 -0
  51. package/dist/fuma/mdx/site-mdx-components.mjs +17 -0
  52. package/dist/fuma/mdx/site-mdx-presets.d.ts +13 -0
  53. package/dist/fuma/mdx/site-mdx-presets.js +49 -0
  54. package/dist/fuma/mdx/site-mdx-presets.mjs +45 -0
  55. package/dist/fuma/mdx/toc-clerk-portable.js +9 -5
  56. package/dist/fuma/mdx/toc-clerk-portable.mjs +8 -4
  57. package/dist/fuma/mdx/zia-file.js +1 -0
  58. package/dist/fuma/mdx/zia-file.mjs +1 -0
  59. package/dist/fuma/server/optional-features.d.ts +8 -0
  60. package/dist/fuma/server/optional-features.js +111 -0
  61. package/dist/fuma/server/optional-features.mjs +104 -0
  62. package/dist/fuma/server/site-mdx-components.d.ts +13 -0
  63. package/dist/fuma/server/site-mdx-components.js +19 -0
  64. package/dist/fuma/server/site-mdx-components.mjs +17 -0
  65. package/dist/fuma/server/site-mdx-presets.d.ts +194 -0
  66. package/dist/fuma/server/site-mdx-presets.js +46 -0
  67. package/dist/fuma/server/site-mdx-presets.mjs +42 -0
  68. package/dist/fuma/share/index.d.ts +1 -0
  69. package/dist/fuma/share/index.js +7 -0
  70. package/dist/fuma/share/index.mjs +1 -0
  71. package/dist/fuma/share/markdown-component-map.d.ts +3 -0
  72. package/dist/fuma/share/markdown-component-map.js +79 -0
  73. package/dist/fuma/share/markdown-component-map.mjs +77 -0
  74. package/dist/lib/fuma-schema-check-util.js +19 -5
  75. package/dist/lib/fuma-schema-check-util.mjs +19 -5
  76. package/dist/lib/seo-metadata.d.ts +10 -0
  77. package/dist/main/x-button.js +2 -2
  78. package/dist/main/x-button.mjs +2 -2
  79. package/package.json +31 -8
  80. package/src/ai/ai-markdown.tsx +1 -1
  81. package/src/clerk/clerk-page-context-generator.tsx +6 -3
  82. package/src/clerk/clerk-page-generator.tsx +7 -4
  83. package/src/fuma/base/custom-header.tsx +32 -35
  84. package/src/fuma/base/custom-home-layout.tsx +2 -2
  85. package/src/fuma/base/header-theme-switch.tsx +88 -0
  86. package/src/fuma/base/index.ts +1 -0
  87. package/src/fuma/base/site-layout.tsx +289 -0
  88. package/src/fuma/fuma-banner-suit.tsx +30 -28
  89. package/src/fuma/fuma-page-genarator.tsx +1 -1
  90. package/src/fuma/{mdx → heavy}/image-grid.tsx +1 -1
  91. package/src/fuma/heavy/index.ts +7 -0
  92. package/src/fuma/mdx/index.ts +0 -5
  93. package/src/fuma/mdx/toc-clerk-portable.tsx +27 -24
  94. package/src/fuma/mdx/zia-file.tsx +3 -1
  95. package/src/fuma/server/optional-features.tsx +168 -0
  96. package/src/fuma/server/site-mdx-components.tsx +48 -0
  97. package/src/fuma/server/site-mdx-presets.ts +80 -0
  98. package/src/fuma/share/index.ts +1 -0
  99. package/src/fuma/{mdx → share}/markdown-component-map.tsx +1 -1
  100. package/src/lib/fuma-schema-check-util.ts +22 -6
  101. package/src/lib/seo-metadata.ts +47 -0
  102. package/src/main/x-button.tsx +2 -2
  103. package/src/styles/fuma.css +3 -7
  104. package/src/styles/third-ui.css +0 -4
  105. /package/src/fuma/{mdx → heavy}/image-zoom.tsx +0 -0
  106. /package/src/fuma/{mdx → heavy}/math.tsx +0 -0
  107. /package/src/fuma/{mdx → heavy}/mermaid.tsx +0 -0
@@ -0,0 +1,358 @@
1
+ "use client";
2
+ import { __awaiter } from 'tslib';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+ import { MmdIcon, RefreshCcwIcon, DownloadIcon, XIcon } from '@windrun-huaiin/base-ui/icons';
5
+ import { cn } from '@windrun-huaiin/lib/utils';
6
+ import { useTheme } from 'next-themes';
7
+ import rough from 'roughjs';
8
+ import { useId, useState, useRef, useEffect, useCallback } from 'react';
9
+ import { themeSvgIconColor, themeIconColor } from '@windrun-huaiin/base-ui/lib';
10
+
11
+ function sanitizeFilename(name) {
12
+ return name
13
+ .replace(/[\/:*?"<>|]/g, '_')
14
+ .replace(/\s+/g, '_')
15
+ .slice(0, 120);
16
+ }
17
+ function Mermaid({ chart, title, watermarkEnabled, watermarkText, handDrawn = true, enablePreview = true }) {
18
+ const id = useId();
19
+ const [svg, setSvg] = useState('');
20
+ const { resolvedTheme } = useTheme();
21
+ const [open, setOpen] = useState(false);
22
+ // zoom & pan states for preview dialog
23
+ const [scale, setScale] = useState(1);
24
+ const [translate, setTranslate] = useState({ x: 0, y: 0 });
25
+ const isPanningRef = useRef(false);
26
+ const startPointRef = useRef({ x: 0, y: 0 });
27
+ const startTranslateRef = useRef({ x: 0, y: 0 });
28
+ const activePointersRef = useRef(new Map());
29
+ const pinchStartDistanceRef = useRef(0);
30
+ const pinchStartScaleRef = useRef(1);
31
+ useEffect(() => {
32
+ let isMounted = true;
33
+ void renderChart();
34
+ function renderChart() {
35
+ return __awaiter(this, void 0, void 0, function* () {
36
+ const mermaidConfig = {
37
+ startOnLoad: false,
38
+ securityLevel: 'loose',
39
+ fontFamily: 'inherit',
40
+ themeCSS: 'margin: 1.5rem auto 0;',
41
+ theme: resolvedTheme === 'dark' ? 'dark' : 'default',
42
+ };
43
+ const { default: mermaid } = yield import('mermaid');
44
+ try {
45
+ mermaid.initialize(mermaidConfig);
46
+ const { svg } = yield mermaid.render(id.replaceAll(':', ''), chart.replaceAll('\\n', '\n'));
47
+ let svgWithWatermark = handDrawn ? applyHandDrawnStyle(svg) : svg;
48
+ if (watermarkEnabled && watermarkText) {
49
+ svgWithWatermark = addWatermarkToSvg(svgWithWatermark, watermarkText, themeSvgIconColor);
50
+ }
51
+ if (isMounted)
52
+ setSvg(svgWithWatermark);
53
+ }
54
+ catch (error) {
55
+ console.error('Error while rendering mermaid', error);
56
+ }
57
+ });
58
+ }
59
+ return () => {
60
+ isMounted = false;
61
+ setSvg('');
62
+ };
63
+ }, [chart, id, resolvedTheme, watermarkEnabled, watermarkText, handDrawn]);
64
+ // helpers for preview zoom
65
+ const clamp = (v, min, max) => Math.min(Math.max(v, min), max);
66
+ const resetTransform = useCallback(() => {
67
+ setScale(4); // 400%
68
+ setTranslate({ x: 0, y: 0 });
69
+ }, []);
70
+ const zoomBy = useCallback((delta) => {
71
+ // zoom by center: keep the zoom center at the center of the canvas, without introducing displacement
72
+ setScale((prev) => clamp(prev + delta, 0.25, 10));
73
+ }, []);
74
+ const onWheel = useCallback((e) => {
75
+ // Cmd/Ctrl + wheel zoom (around the center point), otherwise up and down panning
76
+ if (e.metaKey || e.ctrlKey) {
77
+ e.preventDefault();
78
+ e.stopPropagation();
79
+ const delta = e.deltaY > 0 ? -0.1 : 0.1;
80
+ setScale((prev) => clamp(prev + delta, 0.25, 10));
81
+ }
82
+ else {
83
+ // two-finger pan on touchpad: support both horizontal (deltaX) and vertical (deltaY)
84
+ e.preventDefault();
85
+ e.stopPropagation();
86
+ setTranslate((prev) => ({ x: prev.x - e.deltaX, y: prev.y - e.deltaY }));
87
+ }
88
+ }, []);
89
+ const onPointerDown = useCallback((e) => {
90
+ activePointersRef.current.set(e.pointerId, { x: e.clientX, y: e.clientY });
91
+ if (activePointersRef.current.size === 2) {
92
+ const [first, second] = Array.from(activePointersRef.current.values());
93
+ pinchStartDistanceRef.current = Math.hypot(second.x - first.x, second.y - first.y);
94
+ pinchStartScaleRef.current = scale;
95
+ isPanningRef.current = false;
96
+ }
97
+ else {
98
+ isPanningRef.current = true;
99
+ startPointRef.current = { x: e.clientX, y: e.clientY };
100
+ startTranslateRef.current = Object.assign({}, translate);
101
+ }
102
+ e.currentTarget.setPointerCapture(e.pointerId);
103
+ }, [scale, translate]);
104
+ const onPointerMove = useCallback((e) => {
105
+ if (!activePointersRef.current.has(e.pointerId))
106
+ return;
107
+ activePointersRef.current.set(e.pointerId, { x: e.clientX, y: e.clientY });
108
+ if (activePointersRef.current.size === 2) {
109
+ const [first, second] = Array.from(activePointersRef.current.values());
110
+ const distance = Math.hypot(second.x - first.x, second.y - first.y);
111
+ if (pinchStartDistanceRef.current > 0) {
112
+ setScale(clamp((distance / pinchStartDistanceRef.current) * pinchStartScaleRef.current, 0.25, 10));
113
+ }
114
+ return;
115
+ }
116
+ if (!isPanningRef.current)
117
+ return;
118
+ const dx = e.clientX - startPointRef.current.x;
119
+ const dy = e.clientY - startPointRef.current.y;
120
+ setTranslate({ x: startTranslateRef.current.x + dx, y: startTranslateRef.current.y + dy });
121
+ }, []);
122
+ const onPointerUp = useCallback((e) => {
123
+ activePointersRef.current.delete(e.pointerId);
124
+ isPanningRef.current = false;
125
+ if (activePointersRef.current.size === 1) {
126
+ const remaining = Array.from(activePointersRef.current.values())[0];
127
+ startPointRef.current = remaining;
128
+ startTranslateRef.current = Object.assign({}, translate);
129
+ isPanningRef.current = true;
130
+ }
131
+ if (e.currentTarget.hasPointerCapture(e.pointerId)) {
132
+ e.currentTarget.releasePointerCapture(e.pointerId);
133
+ }
134
+ }, [translate]);
135
+ const onPointerCancel = useCallback((e) => {
136
+ activePointersRef.current.delete(e.pointerId);
137
+ isPanningRef.current = false;
138
+ if (e.currentTarget.hasPointerCapture(e.pointerId)) {
139
+ e.currentTarget.releasePointerCapture(e.pointerId);
140
+ }
141
+ }, []);
142
+ const handleDownload = useCallback(() => {
143
+ if (!svg)
144
+ return;
145
+ const fileName = `${sanitizeFilename(title !== null && title !== void 0 ? title : 'mermaid')}.svg`;
146
+ const blob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' });
147
+ const url = URL.createObjectURL(blob);
148
+ const a = document.createElement('a');
149
+ a.href = url;
150
+ a.download = fileName;
151
+ document.body.appendChild(a);
152
+ a.click();
153
+ a.remove();
154
+ URL.revokeObjectURL(url);
155
+ }, [svg, title]);
156
+ // prevent browser-level zoom (touchpad pinch/shortcut) from taking effect when the dialog is open
157
+ useEffect(() => {
158
+ if (!open)
159
+ return;
160
+ // 初次打开时,默认放大到 400%
161
+ resetTransform();
162
+ const onGlobalWheel = (ev) => {
163
+ if (ev.ctrlKey || ev.metaKey) {
164
+ ev.preventDefault();
165
+ }
166
+ };
167
+ const onKeyDown = (ev) => {
168
+ if (!(ev.ctrlKey || ev.metaKey))
169
+ return;
170
+ const k = ev.key;
171
+ if (k === '=' || k === '+') {
172
+ ev.preventDefault();
173
+ setScale((prev) => clamp(prev + 0.2, 0.25, 10));
174
+ }
175
+ else if (k === '-') {
176
+ ev.preventDefault();
177
+ setScale((prev) => clamp(prev - 0.2, 0.25, 10));
178
+ }
179
+ else if (k === '0') {
180
+ ev.preventDefault();
181
+ resetTransform();
182
+ }
183
+ };
184
+ window.addEventListener('wheel', onGlobalWheel, { passive: false, capture: true });
185
+ window.addEventListener('keydown', onKeyDown, { capture: true });
186
+ return () => {
187
+ window.removeEventListener('wheel', onGlobalWheel, true);
188
+ window.removeEventListener('keydown', onKeyDown, true);
189
+ };
190
+ }, [open, resetTransform]);
191
+ // Lock background scroll when dialog is open
192
+ useEffect(() => {
193
+ if (!open)
194
+ return;
195
+ const previousPosition = document.body.style.position;
196
+ const previousTop = document.body.style.top;
197
+ const previousLeft = document.body.style.left;
198
+ const previousRight = document.body.style.right;
199
+ const previousWidth = document.body.style.width;
200
+ const scrollY = window.scrollY;
201
+ document.body.style.position = 'fixed';
202
+ document.body.style.top = `-${scrollY}px`;
203
+ document.body.style.left = '0';
204
+ document.body.style.right = '0';
205
+ document.body.style.width = '100%';
206
+ return () => {
207
+ document.body.style.position = previousPosition;
208
+ document.body.style.top = previousTop;
209
+ document.body.style.left = previousLeft;
210
+ document.body.style.right = previousRight;
211
+ document.body.style.width = previousWidth;
212
+ window.scrollTo(0, scrollY);
213
+ };
214
+ }, [open]);
215
+ return (jsxs("div", { children: [jsxs("div", { className: enablePreview ? 'group relative cursor-zoom-in' : undefined, onClick: () => enablePreview && svg && setOpen(true), children: [jsx("div", { dangerouslySetInnerHTML: { __html: svg } }), enablePreview && svg && (jsx("div", { className: "pointer-events-none absolute right-2 top-2 hidden rounded bg-black/50 px-2 py-0.5 text-[12px] text-white group-hover:block", children: "Preview Chart" }))] }), title && (jsxs("div", { className: cn("mt-2 flex items-center justify-center text-center text-[13px] font-italic", themeIconColor), children: [jsx(MmdIcon, { className: 'mr-1 h-4 w-4' }), jsx("span", { children: title })] })), enablePreview && open && (jsxs("div", { role: "dialog", "aria-modal": "true", "aria-label": typeof title === 'string' ? title : 'Mermaid Preview', className: "fixed inset-0 z-9999 flex items-center justify-center", children: [jsx("div", { className: "absolute inset-0 bg-black/60", onClick: () => { setOpen(false); resetTransform(); }, onWheel: (e) => { e.preventDefault(); e.stopPropagation(); }, onTouchMove: (e) => { e.preventDefault(); e.stopPropagation(); } }), jsxs("div", { className: "relative z-1 max-w-[95vw] w-[95vw] h-[88vh] p-0 bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700 rounded-md shadow-2xl overflow-hidden", children: [jsxs("div", { className: "flex items-center justify-between gap-3 px-3 py-2 border-b border-neutral-200 dark:border-neutral-700", children: [jsxs("div", { className: cn("min-w-0 flex items-center gap-2 text-sm", themeIconColor), children: [jsx(MmdIcon, { className: "h-4 w-4" }), jsx("span", { className: "truncate max-w-[50vw]", children: title !== null && title !== void 0 ? title : 'Mermaid Preview' })] }), jsxs("div", { className: "flex shrink-0 items-center gap-0.5", children: [jsx("button", { "aria-label": "Zoom out", className: "hidden h-6 w-6 items-center justify-center rounded border border-neutral-300 text-[13px] transition-colors hover:bg-neutral-100 active:bg-neutral-200 hover:border-neutral-400 active:border-neutral-500 dark:border-neutral-600 dark:hover:bg-neutral-700 dark:active:bg-neutral-600 dark:hover:border-neutral-500 dark:active:border-neutral-400 sm:flex", onClick: () => zoomBy(-0.5), children: "\uFF0D" }), jsxs("span", { className: "mx-0.5 w-12 text-center text-[12px] select-none", children: [Math.round(scale * 100), "%"] }), jsx("button", { "aria-label": "Zoom in", className: "hidden h-6 w-6 items-center justify-center rounded border border-neutral-300 text-[13px] transition-colors hover:bg-neutral-100 active:bg-neutral-200 hover:border-neutral-400 active:border-neutral-500 dark:border-neutral-600 dark:hover:bg-neutral-700 dark:active:bg-neutral-600 dark:hover:border-neutral-500 dark:active:border-neutral-400 sm:flex", onClick: () => zoomBy(0.5), children: "\uFF0B" }), jsx("div", { className: "mx-1 hidden h-4 w-px bg-neutral-300 dark:bg-neutral-700 sm:block" }), jsx("button", { "aria-label": "Zoom 100%", className: "hidden h-6 min-w-8 items-center justify-center rounded border border-neutral-300 px-1.5 text-[12px] transition-colors hover:bg-neutral-100 active:bg-neutral-200 hover:border-neutral-400 active:border-neutral-500 dark:border-neutral-600 dark:hover:bg-neutral-700 dark:active:bg-neutral-600 dark:hover:border-neutral-500 dark:active:border-neutral-400 sm:inline-flex", onClick: () => setScale(1), children: "X1" }), jsx("button", { "aria-label": "Zoom 200%", className: "ml-1 hidden h-6 min-w-8 items-center justify-center rounded border border-neutral-300 px-1.5 text-[12px] transition-colors hover:bg-neutral-100 active:bg-neutral-200 hover:border-neutral-400 active:border-neutral-500 dark:border-neutral-600 dark:hover:bg-neutral-700 dark:active:bg-neutral-600 dark:hover:border-neutral-500 dark:active:border-neutral-400 sm:inline-flex", onClick: () => setScale(2), children: "X2" }), jsx("button", { "aria-label": "Zoom 300%", className: "ml-1 hidden h-6 min-w-8 items-center justify-center rounded border border-neutral-300 px-1.5 text-[12px] transition-colors hover:bg-neutral-100 active:bg-neutral-200 hover:border-neutral-400 active:border-neutral-500 dark:border-neutral-600 dark:hover:bg-neutral-700 dark:active:bg-neutral-600 dark:hover:border-neutral-500 dark:active:border-neutral-400 sm:inline-flex", onClick: () => setScale(3), children: "X3" }), jsx("button", { "aria-label": "Zoom 1000%", className: "ml-1 hidden h-6 min-w-10 items-center justify-center rounded border border-neutral-300 px-1.5 text-[12px] transition-colors hover:bg-neutral-100 active:bg-neutral-200 hover:border-neutral-400 active:border-neutral-500 dark:border-neutral-600 dark:hover:bg-neutral-700 dark:active:bg-neutral-600 dark:hover:border-neutral-500 dark:active:border-neutral-400 sm:inline-flex", onClick: () => setScale(10), children: "X10" }), jsx("button", { "aria-label": "Reset", className: cn("ml-1 flex h-6 w-6 items-center justify-center rounded transition-colors hover:bg-neutral-100 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600", themeIconColor), onClick: resetTransform, children: jsx(RefreshCcwIcon, { className: "h-3.5 w-3.5" }) }), jsx("button", { "aria-label": "Download SVG", className: cn("ml-1 flex h-6 w-6 items-center justify-center rounded transition-colors hover:bg-neutral-100 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600", themeIconColor), onClick: handleDownload, children: jsx(DownloadIcon, { className: "h-3.5 w-3.5" }) }), jsx("button", { "aria-label": "Close", className: cn("ml-1 flex h-6 w-6 items-center justify-center rounded transition-colors hover:bg-neutral-100 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600", themeIconColor), onClick: () => { setOpen(false); resetTransform(); }, children: jsx(XIcon, { className: "h-3.5 w-3.5" }) })] })] }), jsxs("div", { className: "relative h-[calc(88vh-40px)] w-full overflow-hidden bg-white dark:bg-neutral-900 overscroll-contain touch-none", onWheel: onWheel, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onPointerCancel: onPointerCancel, children: [jsx("div", { className: "absolute left-1/2 top-1/2", style: { transform: `translate(-50%, -50%) translate(${translate.x}px, ${translate.y}px)` }, children: jsx("div", { style: { transform: `scale(${scale})`, transformOrigin: '50% 50%' }, dangerouslySetInnerHTML: { __html: svg } }) }), jsxs("div", { className: "absolute inset-x-3 bottom-3 rounded-md bg-white/92 px-3 py-2 shadow-sm backdrop-blur sm:hidden dark:bg-neutral-900/92", children: [jsxs("label", { className: "mb-1 flex items-center justify-between text-[11px] text-neutral-600 dark:text-neutral-300", children: [jsx("span", { children: "Zoom" }), jsxs("span", { children: [Math.round(scale * 100), "%"] })] }), jsx("input", { "aria-label": "Zoom slider", className: "block w-full", type: "range", min: "25", max: "1000", step: "5", value: Math.round(scale * 100), style: { accentColor: themeSvgIconColor }, onChange: (e) => setScale(clamp(Number(e.target.value) / 100, 0.25, 10)) })] }), jsx("div", { className: "pointer-events-none absolute bottom-2 right-3 hidden rounded bg-black/40 px-2 py-1 text-xs text-white sm:block", children: "Drag to pan, click button to zoom-out or zoom-in" }), jsx("div", { className: "pointer-events-none absolute left-3 top-3 rounded bg-black/40 px-2 py-1 text-[11px] text-white sm:hidden", children: "Drag to pan, pinch to zoom-out or zoom-in" })] })] })] }))] }));
216
+ }
217
+ function addWatermarkToSvg(svg, watermark, watermarkColor) {
218
+ const watermarkText = `
219
+ <text
220
+ x="100%"
221
+ y="98%"
222
+ text-anchor="end"
223
+ font-size="12"
224
+ font-style="italic"
225
+ fill="${watermarkColor}"
226
+ opacity="0.40"
227
+ class="pointer-events-none"
228
+ dx="-8"
229
+ dy="-4"
230
+ >${watermark}</text>
231
+ `;
232
+ return svg.replace('</svg>', `${watermarkText}</svg>`);
233
+ }
234
+ function applyHandDrawnStyle(svg) {
235
+ if (typeof window === 'undefined')
236
+ return svg;
237
+ try {
238
+ const parser = new DOMParser();
239
+ const doc = parser.parseFromString(svg, 'image/svg+xml');
240
+ const svgElement = doc.documentElement;
241
+ if (!svgElement || svgElement.tagName.toLowerCase() !== 'svg')
242
+ return svg;
243
+ const rc = rough.svg(svgElement);
244
+ const serializer = new XMLSerializer();
245
+ const getNumber = (value) => Number.parseFloat(value !== null && value !== void 0 ? value : '') || 0;
246
+ const getStyleValue = (element, name) => {
247
+ const inlineStyle = element.getAttribute('style');
248
+ if (inlineStyle) {
249
+ const match = inlineStyle.match(new RegExp(`(?:^|;)\\s*${name}\\s*:\\s*([^;]+)`));
250
+ if (match === null || match === void 0 ? void 0 : match[1])
251
+ return match[1].trim();
252
+ }
253
+ return element.getAttribute(name);
254
+ };
255
+ const applyAttributes = (source, target) => {
256
+ var _a;
257
+ for (const attr of source.getAttributeNames()) {
258
+ if (attr === 'x' || attr === 'y' || attr === 'x1' || attr === 'y1' || attr === 'x2' || attr === 'y2' || attr === 'width' || attr === 'height' || attr === 'rx' || attr === 'ry' || attr === 'points' || attr === 'd')
259
+ continue;
260
+ target.setAttribute(attr, (_a = source.getAttribute(attr)) !== null && _a !== void 0 ? _a : '');
261
+ }
262
+ };
263
+ const createOptions = (element) => {
264
+ var _a, _b;
265
+ const stroke = (_a = getStyleValue(element, 'stroke')) !== null && _a !== void 0 ? _a : '#000';
266
+ const fill = (_b = getStyleValue(element, 'fill')) !== null && _b !== void 0 ? _b : 'none';
267
+ const strokeWidth = getNumber(getStyleValue(element, 'stroke-width')) || 1.5;
268
+ return {
269
+ stroke,
270
+ fill: fill === 'none' ? undefined : fill,
271
+ strokeWidth,
272
+ roughness: 1.6,
273
+ bowing: 1.25,
274
+ fillStyle: fill === 'none' ? 'hachure' : 'solid',
275
+ fillWeight: 0.8,
276
+ hachureGap: 10,
277
+ preserveVertices: true,
278
+ seed: 7,
279
+ };
280
+ };
281
+ const replaceShape = (element, node) => {
282
+ var _a, _b;
283
+ if (!node || !element.parentNode)
284
+ return;
285
+ applyAttributes(element, node);
286
+ if (element.getAttribute('class')) {
287
+ node.setAttribute('class', (_a = element.getAttribute('class')) !== null && _a !== void 0 ? _a : '');
288
+ }
289
+ if (element.getAttribute('style')) {
290
+ node.setAttribute('style', (_b = element.getAttribute('style')) !== null && _b !== void 0 ? _b : '');
291
+ }
292
+ element.parentNode.replaceChild(node, element);
293
+ };
294
+ svgElement.querySelectorAll('rect').forEach((element) => {
295
+ const x = getNumber(element.getAttribute('x'));
296
+ const y = getNumber(element.getAttribute('y'));
297
+ const width = getNumber(element.getAttribute('width'));
298
+ const height = getNumber(element.getAttribute('height'));
299
+ const rx = getNumber(element.getAttribute('rx'));
300
+ const ry = getNumber(element.getAttribute('ry'));
301
+ const node = rx > 0 || ry > 0
302
+ ? rc.path(`M ${x + rx} ${y}
303
+ H ${x + width - rx}
304
+ Q ${x + width} ${y} ${x + width} ${y + ry}
305
+ V ${y + height - ry}
306
+ Q ${x + width} ${y + height} ${x + width - rx} ${y + height}
307
+ H ${x + rx}
308
+ Q ${x} ${y + height} ${x} ${y + height - ry}
309
+ V ${y + ry}
310
+ Q ${x} ${y} ${x + rx} ${y}
311
+ Z`, createOptions(element))
312
+ : rc.rectangle(x, y, width, height, createOptions(element));
313
+ replaceShape(element, node);
314
+ });
315
+ svgElement.querySelectorAll('line').forEach((element) => {
316
+ const node = rc.line(getNumber(element.getAttribute('x1')), getNumber(element.getAttribute('y1')), getNumber(element.getAttribute('x2')), getNumber(element.getAttribute('y2')), createOptions(element));
317
+ replaceShape(element, node);
318
+ });
319
+ svgElement.querySelectorAll('polyline').forEach((element) => {
320
+ var _a;
321
+ const points = ((_a = element.getAttribute('points')) !== null && _a !== void 0 ? _a : '')
322
+ .trim()
323
+ .split(/\s+/)
324
+ .map((pair) => pair.split(',').map(Number))
325
+ .filter((point) => point.length === 2 && Number.isFinite(point[0]) && Number.isFinite(point[1]));
326
+ if (points.length < 2)
327
+ return;
328
+ const node = rc.linearPath(points, createOptions(element));
329
+ replaceShape(element, node);
330
+ });
331
+ svgElement.querySelectorAll('polygon').forEach((element) => {
332
+ var _a;
333
+ const points = ((_a = element.getAttribute('points')) !== null && _a !== void 0 ? _a : '')
334
+ .trim()
335
+ .split(/\s+/)
336
+ .map((pair) => pair.split(',').map(Number))
337
+ .filter((point) => point.length === 2 && Number.isFinite(point[0]) && Number.isFinite(point[1]));
338
+ if (points.length < 2)
339
+ return;
340
+ const node = rc.polygon(points, createOptions(element));
341
+ replaceShape(element, node);
342
+ });
343
+ svgElement.querySelectorAll('path').forEach((element) => {
344
+ const d = element.getAttribute('d');
345
+ if (!d)
346
+ return;
347
+ const node = rc.path(d, createOptions(element));
348
+ replaceShape(element, node);
349
+ });
350
+ return serializer.serializeToString(svgElement);
351
+ }
352
+ catch (error) {
353
+ console.error('Error while applying hand-drawn mermaid style', error);
354
+ return svg;
355
+ }
356
+ }
357
+
358
+ export { Mermaid };
@@ -0,0 +1,8 @@
1
+ import type { MDXComponents } from 'mdx/types';
2
+ import type { ReactNode } from 'react';
3
+ export declare function createBaseMdxComponents(imageFallbackSrc?: string): MDXComponents;
4
+ export declare function createCodeMdxComponents(iconMap: Record<string, ReactNode>): MDXComponents;
5
+ export declare function createMathMdxComponents(): MDXComponents;
6
+ export declare function createMermaidMdxComponents(watermarkEnabled?: boolean, watermarkText?: string): MDXComponents;
7
+ export declare function createTypeTableMdxComponents(): MDXComponents;
8
+ export declare function createWidgetMdxComponents(cdnBaseUrl?: string, imageFallbackSrc?: string): MDXComponents;
@@ -0,0 +1,92 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var codeblock = require('fumadocs-ui/components/codeblock');
5
+ var typeTable = require('fumadocs-ui/components/type-table');
6
+ var fumadocsTypescript = require('fumadocs-typescript');
7
+ var ui = require('fumadocs-typescript/ui');
8
+ var markdownComponentMap = require('../share/markdown-component-map.js');
9
+ var imageZoom = require('../heavy/image-zoom.js');
10
+ var imageGrid = require('../heavy/image-grid.js');
11
+ var math = require('../heavy/math.js');
12
+ var mermaid = require('../heavy/mermaid.js');
13
+ var trophyCard = require('./trophy-card.js');
14
+ var ziaCard = require('./zia-card.js');
15
+ var gradientButton = require('./gradient-button.js');
16
+ var ziaFile = require('./zia-file.js');
17
+ var sunoEmbed = require('./suno-embed.js');
18
+
19
+ const typeTableGenerator = fumadocsTypescript.createGenerator();
20
+ function tryToMatchIcon(props, iconMap) {
21
+ var _a;
22
+ let lang;
23
+ const dataLanguage = props['data-language'];
24
+ if (dataLanguage && dataLanguage.trim() !== '') {
25
+ lang = dataLanguage.trim().toLowerCase();
26
+ }
27
+ else {
28
+ const title = props.title;
29
+ if (title) {
30
+ const titleParts = title.split('.');
31
+ if (titleParts.length > 1 && titleParts[0] !== '') {
32
+ const extension = (_a = titleParts.pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase();
33
+ if (extension) {
34
+ lang = extension;
35
+ }
36
+ }
37
+ }
38
+ }
39
+ if (lang && iconMap[lang]) {
40
+ return iconMap[lang];
41
+ }
42
+ return undefined;
43
+ }
44
+ function createBaseMdxComponents(imageFallbackSrc) {
45
+ return Object.assign(Object.assign({}, markdownComponentMap.baseMarkdownComponents), { img: (props) => (jsxRuntime.jsx(imageZoom.ImageZoom, Object.assign({}, props, { fallbackSrc: imageFallbackSrc }))) });
46
+ }
47
+ function createCodeMdxComponents(iconMap) {
48
+ return {
49
+ pre: (props) => {
50
+ const customIcon = tryToMatchIcon(props, iconMap);
51
+ return (jsxRuntime.jsx(codeblock.CodeBlock, Object.assign({}, props, (customIcon && { icon: customIcon }), { children: jsxRuntime.jsx(codeblock.Pre, { children: props.children }) })));
52
+ },
53
+ CodeBlock: codeblock.CodeBlock,
54
+ Pre: codeblock.Pre,
55
+ };
56
+ }
57
+ function createMathMdxComponents() {
58
+ return {
59
+ MathBlock: math.MathBlock,
60
+ InlineMath: math.InlineMath,
61
+ };
62
+ }
63
+ function createMermaidMdxComponents(watermarkEnabled, watermarkText) {
64
+ return {
65
+ Mermaid: (props) => (jsxRuntime.jsx(mermaid.Mermaid, Object.assign({}, props, { watermarkEnabled: watermarkEnabled, watermarkText: watermarkText }))),
66
+ };
67
+ }
68
+ function createTypeTableMdxComponents() {
69
+ return {
70
+ TypeTable: typeTable.TypeTable,
71
+ AutoTypeTable: (props) => (jsxRuntime.jsx(ui.AutoTypeTable, Object.assign({}, props, { generator: typeTableGenerator }))),
72
+ };
73
+ }
74
+ function createWidgetMdxComponents(cdnBaseUrl, imageFallbackSrc) {
75
+ return {
76
+ TrophyCard: trophyCard.TrophyCard,
77
+ ZiaCard: ziaCard.ZiaCard,
78
+ GradientButton: gradientButton.GradientButton,
79
+ ZiaFile: ziaFile.ZiaFile,
80
+ ZiaFolder: ziaFile.ZiaFolder,
81
+ SunoEmbed: sunoEmbed.SunoEmbed,
82
+ ImageGrid: (props) => (jsxRuntime.jsx(imageGrid.ImageGrid, Object.assign({}, props, { cdnBaseUrl: cdnBaseUrl }))),
83
+ ImageZoom: (props) => (jsxRuntime.jsx(imageZoom.ImageZoom, Object.assign({}, props, { fallbackSrc: imageFallbackSrc }))),
84
+ };
85
+ }
86
+
87
+ exports.createBaseMdxComponents = createBaseMdxComponents;
88
+ exports.createCodeMdxComponents = createCodeMdxComponents;
89
+ exports.createMathMdxComponents = createMathMdxComponents;
90
+ exports.createMermaidMdxComponents = createMermaidMdxComponents;
91
+ exports.createTypeTableMdxComponents = createTypeTableMdxComponents;
92
+ exports.createWidgetMdxComponents = createWidgetMdxComponents;
@@ -0,0 +1,85 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { Pre, CodeBlock } from 'fumadocs-ui/components/codeblock';
3
+ import { TypeTable } from 'fumadocs-ui/components/type-table';
4
+ import { createGenerator } from 'fumadocs-typescript';
5
+ import { AutoTypeTable } from 'fumadocs-typescript/ui';
6
+ import { baseMarkdownComponents } from '../share/markdown-component-map.mjs';
7
+ import { ImageZoom } from '../heavy/image-zoom.mjs';
8
+ import { ImageGrid } from '../heavy/image-grid.mjs';
9
+ import { InlineMath, MathBlock } from '../heavy/math.mjs';
10
+ import { Mermaid } from '../heavy/mermaid.mjs';
11
+ import { TrophyCard } from './trophy-card.mjs';
12
+ import { ZiaCard } from './zia-card.mjs';
13
+ import { GradientButton } from './gradient-button.mjs';
14
+ import { ZiaFolder, ZiaFile } from './zia-file.mjs';
15
+ import { SunoEmbed } from './suno-embed.mjs';
16
+
17
+ const typeTableGenerator = createGenerator();
18
+ function tryToMatchIcon(props, iconMap) {
19
+ var _a;
20
+ let lang;
21
+ const dataLanguage = props['data-language'];
22
+ if (dataLanguage && dataLanguage.trim() !== '') {
23
+ lang = dataLanguage.trim().toLowerCase();
24
+ }
25
+ else {
26
+ const title = props.title;
27
+ if (title) {
28
+ const titleParts = title.split('.');
29
+ if (titleParts.length > 1 && titleParts[0] !== '') {
30
+ const extension = (_a = titleParts.pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase();
31
+ if (extension) {
32
+ lang = extension;
33
+ }
34
+ }
35
+ }
36
+ }
37
+ if (lang && iconMap[lang]) {
38
+ return iconMap[lang];
39
+ }
40
+ return undefined;
41
+ }
42
+ function createBaseMdxComponents(imageFallbackSrc) {
43
+ return Object.assign(Object.assign({}, baseMarkdownComponents), { img: (props) => (jsx(ImageZoom, Object.assign({}, props, { fallbackSrc: imageFallbackSrc }))) });
44
+ }
45
+ function createCodeMdxComponents(iconMap) {
46
+ return {
47
+ pre: (props) => {
48
+ const customIcon = tryToMatchIcon(props, iconMap);
49
+ return (jsx(CodeBlock, Object.assign({}, props, (customIcon && { icon: customIcon }), { children: jsx(Pre, { children: props.children }) })));
50
+ },
51
+ CodeBlock,
52
+ Pre,
53
+ };
54
+ }
55
+ function createMathMdxComponents() {
56
+ return {
57
+ MathBlock,
58
+ InlineMath,
59
+ };
60
+ }
61
+ function createMermaidMdxComponents(watermarkEnabled, watermarkText) {
62
+ return {
63
+ Mermaid: (props) => (jsx(Mermaid, Object.assign({}, props, { watermarkEnabled: watermarkEnabled, watermarkText: watermarkText }))),
64
+ };
65
+ }
66
+ function createTypeTableMdxComponents() {
67
+ return {
68
+ TypeTable,
69
+ AutoTypeTable: (props) => (jsx(AutoTypeTable, Object.assign({}, props, { generator: typeTableGenerator }))),
70
+ };
71
+ }
72
+ function createWidgetMdxComponents(cdnBaseUrl, imageFallbackSrc) {
73
+ return {
74
+ TrophyCard,
75
+ ZiaCard,
76
+ GradientButton,
77
+ ZiaFile,
78
+ ZiaFolder,
79
+ SunoEmbed,
80
+ ImageGrid: (props) => (jsx(ImageGrid, Object.assign({}, props, { cdnBaseUrl: cdnBaseUrl }))),
81
+ ImageZoom: (props) => (jsx(ImageZoom, Object.assign({}, props, { fallbackSrc: imageFallbackSrc }))),
82
+ };
83
+ }
84
+
85
+ export { createBaseMdxComponents, createCodeMdxComponents, createMathMdxComponents, createMermaidMdxComponents, createTypeTableMdxComponents, createWidgetMdxComponents };
@@ -1,7 +1,4 @@
1
- export * from './mermaid';
2
- export * from './image-zoom';
3
1
  export * from './trophy-card';
4
- export * from './image-grid';
5
2
  export * from './zia-card';
6
3
  export * from './gradient-button';
7
4
  export * from './toc-base';
@@ -11,5 +8,3 @@ export * from './toc-footer-wrapper';
11
8
  export * from './toc-clerk-portable';
12
9
  export * from './banner';
13
10
  export * from './suno-embed';
14
- export * from './markdown-component-map';
15
- export * from './math';
@@ -1,10 +1,7 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
- var mermaid = require('./mermaid.js');
5
- var imageZoom = require('./image-zoom.js');
6
4
  var trophyCard = require('./trophy-card.js');
7
- var imageGrid = require('./image-grid.js');
8
5
  var ziaCard = require('./zia-card.js');
9
6
  var gradientButton = require('./gradient-button.js');
10
7
  var tocBase = require('./toc-base.js');
@@ -14,15 +11,10 @@ var tocFooterWrapper = require('./toc-footer-wrapper.js');
14
11
  var tocClerkPortable = require('./toc-clerk-portable.js');
15
12
  var banner = require('./banner.js');
16
13
  var sunoEmbed = require('./suno-embed.js');
17
- var markdownComponentMap = require('./markdown-component-map.js');
18
- var math = require('./math.js');
19
14
 
20
15
 
21
16
 
22
- exports.Mermaid = mermaid.Mermaid;
23
- exports.ImageZoom = imageZoom.ImageZoom;
24
17
  exports.TrophyCard = trophyCard.TrophyCard;
25
- exports.ImageGrid = imageGrid.ImageGrid;
26
18
  exports.ZiaCard = ziaCard.ZiaCard;
27
19
  exports.GradientButton = gradientButton.GradientButton;
28
20
  exports.EditOnGitHub = tocBase.EditOnGitHub;
@@ -38,6 +30,3 @@ exports.PortableClerkTOCPopover = tocClerkPortable.PortableClerkTOCPopover;
38
30
  exports.PortableClerkTOCScrollArea = tocClerkPortable.PortableClerkTOCScrollArea;
39
31
  exports.Banner = banner.Banner;
40
32
  exports.SunoEmbed = sunoEmbed.SunoEmbed;
41
- exports.baseMarkdownComponents = markdownComponentMap.baseMarkdownComponents;
42
- exports.InlineMath = math.InlineMath;
43
- exports.MathBlock = math.MathBlock;
@@ -1,8 +1,5 @@
1
1
  "use client";
2
- export { Mermaid } from './mermaid.mjs';
3
- export { ImageZoom } from './image-zoom.mjs';
4
2
  export { TrophyCard } from './trophy-card.mjs';
5
- export { ImageGrid } from './image-grid.mjs';
6
3
  export { ZiaCard } from './zia-card.mjs';
7
4
  export { GradientButton } from './gradient-button.mjs';
8
5
  export { EditOnGitHub, LLMCopyButton, LastUpdatedDate } from './toc-base.mjs';
@@ -12,5 +9,3 @@ export { TocFooterWrapper } from './toc-footer-wrapper.mjs';
12
9
  export { PortableClerkTOC, PortableClerkTOCItems, PortableClerkTOCPopover, PortableClerkTOCScrollArea } from './toc-clerk-portable.mjs';
13
10
  export { Banner } from './banner.mjs';
14
11
  export { SunoEmbed } from './suno-embed.mjs';
15
- export { baseMarkdownComponents } from './markdown-component-map.mjs';
16
- export { InlineMath, MathBlock } from './math.mjs';
@@ -4,7 +4,13 @@ var tslib = require('tslib');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var utils = require('@windrun-huaiin/lib/utils');
6
6
  var defaultMdxComponents = require('fumadocs-ui/mdx');
7
- var imageZoom = require('./image-zoom.js');
7
+ var imageZoom = require('../heavy/image-zoom.js');
8
+ require('katex');
9
+ require('@windrun-huaiin/base-ui/icons');
10
+ require('next-themes');
11
+ require('roughjs');
12
+ require('react');
13
+ require('@windrun-huaiin/base-ui/lib');
8
14
 
9
15
  function normalizeMarkdownProps(props) {
10
16
  const { class: legacyClassName, className } = props, restProps = tslib.__rest(props, ["class", "className"]);
@@ -2,7 +2,13 @@ import { __rest } from 'tslib';
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import { cn } from '@windrun-huaiin/lib/utils';
4
4
  import defaultMdxComponents from 'fumadocs-ui/mdx';
5
- import { ImageZoom } from './image-zoom.mjs';
5
+ import { ImageZoom } from '../heavy/image-zoom.mjs';
6
+ import 'katex';
7
+ import '@windrun-huaiin/base-ui/icons';
8
+ import 'next-themes';
9
+ import 'roughjs';
10
+ import 'react';
11
+ import '@windrun-huaiin/base-ui/lib';
6
12
 
7
13
  function normalizeMarkdownProps(props) {
8
14
  const { class: legacyClassName, className } = props, restProps = __rest(props, ["class", "className"]);
@@ -0,0 +1,13 @@
1
+ import type { MDXComponents } from 'mdx/types';
2
+ import type { ReactNode } from 'react';
3
+ export type SiteMdxFeature = 'base' | 'code' | 'math' | 'mermaid' | 'type-table' | 'fuma-ui' | 'widgets';
4
+ export interface SiteMdxComponentsOptions {
5
+ imageFallbackSrc?: string;
6
+ cdnBaseUrl?: string;
7
+ watermarkEnabled?: boolean;
8
+ watermarkText?: string;
9
+ additionalComponents?: MDXComponents;
10
+ iconMap?: Record<string, ReactNode>;
11
+ features?: SiteMdxFeature[];
12
+ }
13
+ export declare function createSiteMdxComponents(options: SiteMdxComponentsOptions): (components?: MDXComponents) => MDXComponents;