@docubook/markdown 2.0.0-alpha.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/dist/index.js ADDED
@@ -0,0 +1,2319 @@
1
+ // src/registry/index.ts
2
+ import { createElement } from "react";
3
+
4
+ // src/utils/Icon.tsx
5
+ import * as LucideIcons from "lucide-react";
6
+ import { jsx } from "react/jsx-runtime";
7
+ var DEFAULT_ICON_SIZE = 20;
8
+ function resolveLucideIcon(icon) {
9
+ const normalized = typeof icon === "string" ? icon.trim() : icon;
10
+ if (!normalized || !Object.hasOwn(LucideIcons, normalized)) {
11
+ return void 0;
12
+ }
13
+ const IconComponent = LucideIcons[normalized];
14
+ return /* @__PURE__ */ jsx(IconComponent, { size: DEFAULT_ICON_SIZE });
15
+ }
16
+
17
+ // src/components/Callout.tsx
18
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
19
+ var palette = {
20
+ danger: {
21
+ border: "hsl(var(--destructive, 0 85% 60%))",
22
+ bg: "hsl(var(--destructive, 0 85% 60%) / 0.16)",
23
+ text: "hsl(var(--foreground, 220 30% 15%))",
24
+ header: "hsl(var(--destructive, 0 85% 60%))",
25
+ content: "hsl(var(--destructive, 0 85% 60%) / 0.75)",
26
+ defaultIcon: "Siren"
27
+ },
28
+ warning: {
29
+ border: "hsl(36 100% 56%)",
30
+ bg: "rgba(249, 115, 22, 0.14)",
31
+ text: "hsl(var(--foreground, 220 30% 15%))",
32
+ header: "hsl(36 100% 56%)",
33
+ content: "rgba(249, 115, 22, 0.9)",
34
+ defaultIcon: "TriangleAlert"
35
+ },
36
+ success: {
37
+ border: "hsl(137 50% 35%)",
38
+ bg: "hsl(137 50% 35% / 0.16)",
39
+ text: "hsl(var(--foreground, 220 30% 15%))",
40
+ header: "hsl(137 50% 35%)",
41
+ content: "hsl(137 50% 35% / 0.75)",
42
+ defaultIcon: "CircleCheck"
43
+ },
44
+ info: {
45
+ border: "hsl(var(--primary, 210 81% 56%))",
46
+ bg: "hsl(var(--primary, 210 81% 56%) / 0.16)",
47
+ text: "hsl(var(--foreground, 220 30% 15%))",
48
+ header: "hsl(var(--primary, 210 81% 56%))",
49
+ content: "hsl(var(--primary, 210 81% 56%) / 0.75)",
50
+ defaultIcon: "Info"
51
+ },
52
+ tip: {
53
+ border: "hsl(137 50% 35%)",
54
+ bg: "hsl(137 50% 35% / 0.16)",
55
+ text: "hsl(var(--foreground, 220 30% 15%))",
56
+ header: "hsl(137 50% 35%)",
57
+ content: "hsl(137 50% 35% / 0.75)",
58
+ defaultIcon: "Lightbulb"
59
+ }
60
+ };
61
+ var LABEL = {
62
+ tip: "Tip",
63
+ info: "Info",
64
+ danger: "Danger",
65
+ warning: "Warning",
66
+ success: "Success"
67
+ };
68
+ function Callout({ type, title, children, style, className, ...props }) {
69
+ const token = palette[type];
70
+ if (!token) return null;
71
+ const resolvedIcon = resolveLucideIcon(token.defaultIcon);
72
+ const normalizedTitle = typeof title === "string" ? title.trim() : title;
73
+ const fallbackTitle = normalizedTitle || LABEL[type];
74
+ return /* @__PURE__ */ jsxs(
75
+ "aside",
76
+ {
77
+ role: "note",
78
+ className,
79
+ ...props,
80
+ style: {
81
+ border: "1px solid transparent",
82
+ borderLeft: `4px solid ${token.border}`,
83
+ borderRadius: 10,
84
+ background: token.bg,
85
+ color: token.text,
86
+ padding: "0.5rem 1rem",
87
+ margin: "1rem 0",
88
+ boxShadow: "0 1px 2px rgba(15, 23, 42, 0.08)",
89
+ ...style
90
+ },
91
+ children: [
92
+ fallbackTitle || resolvedIcon ? /* @__PURE__ */ jsxs(
93
+ "div",
94
+ {
95
+ style: {
96
+ display: "flex",
97
+ alignItems: "center",
98
+ gap: "0.5rem",
99
+ fontWeight: 700,
100
+ marginBottom: "0.35rem",
101
+ color: token.header
102
+ },
103
+ children: [
104
+ resolvedIcon ? /* @__PURE__ */ jsx2(
105
+ "span",
106
+ {
107
+ "aria-hidden": "true",
108
+ style: {
109
+ display: "inline-flex",
110
+ alignItems: "center",
111
+ justifyContent: "center",
112
+ width: 20,
113
+ height: 20,
114
+ color: token.header
115
+ },
116
+ children: resolvedIcon
117
+ }
118
+ ) : null,
119
+ /* @__PURE__ */ jsx2("span", { style: { color: token.header }, children: fallbackTitle })
120
+ ]
121
+ }
122
+ ) : null,
123
+ /* @__PURE__ */ jsx2("div", { style: { color: token.content, lineHeight: 1.5 }, children })
124
+ ]
125
+ }
126
+ );
127
+ }
128
+
129
+ // src/components/AccordionMdx.tsx
130
+ import { useContext, useId, useState } from "react";
131
+
132
+ // src/utils/AccordionGroupContext.tsx
133
+ import { createContext } from "react";
134
+ var AccordionGroupContext = createContext(null);
135
+
136
+ // src/components/AccordionMdx.tsx
137
+ import { ChevronRight } from "lucide-react";
138
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
139
+ function AccordionMdx({ title, icon, className, children }) {
140
+ const groupContext = useContext(AccordionGroupContext);
141
+ const [localOpen, setLocalOpen] = useState(false);
142
+ const panelId = useId();
143
+ const triggerId = `${panelId}-trigger`;
144
+ const isGroup = groupContext?.inGroup === true;
145
+ const isOpen = isGroup ? groupContext?.openTitle === title : localOpen;
146
+ const resolvedIcon = resolveLucideIcon(icon);
147
+ function onToggle() {
148
+ if (isGroup && groupContext) {
149
+ groupContext.setOpenTitle(groupContext.openTitle === title ? null : title);
150
+ return;
151
+ }
152
+ setLocalOpen((prev) => !prev);
153
+ }
154
+ return /* @__PURE__ */ jsxs2(
155
+ "div",
156
+ {
157
+ className: `mdx-accordion ${isGroup ? "mdx-accordion-group-item" : ""} ${className ?? ""}`,
158
+ style: {
159
+ border: "1px solid hsl(var(--border-color, 210 20% 85%))",
160
+ borderRadius: 10,
161
+ overflow: "hidden",
162
+ background: "hsl(var(--card, 0 0% 100%))"
163
+ },
164
+ children: [
165
+ /* @__PURE__ */ jsxs2(
166
+ "button",
167
+ {
168
+ className: "mdx-accordion-header",
169
+ type: "button",
170
+ onClick: onToggle,
171
+ "aria-expanded": isOpen,
172
+ "aria-controls": panelId,
173
+ id: triggerId,
174
+ onKeyDown: (event) => {
175
+ if (event.key === "Enter" || event.key === " ") {
176
+ event.preventDefault();
177
+ onToggle();
178
+ }
179
+ },
180
+ style: {
181
+ width: "100%",
182
+ display: "flex",
183
+ alignItems: "center",
184
+ justifyContent: "flex-start",
185
+ gap: "0.5rem",
186
+ textAlign: "left",
187
+ border: 0,
188
+ cursor: "pointer",
189
+ background: "hsl(var(--muted, 210 20% 92%))",
190
+ color: "hsl(var(--foreground, 220 30% 15%))",
191
+ fontWeight: 600,
192
+ padding: "0.95rem 1rem"
193
+ },
194
+ children: [
195
+ /* @__PURE__ */ jsx3(
196
+ ChevronRight,
197
+ {
198
+ className: "mdx-accordion-chevron",
199
+ "aria-hidden": "true",
200
+ size: 16,
201
+ style: {
202
+ transform: isOpen ? "rotate(90deg)" : "rotate(0deg)",
203
+ transition: "transform 0.2s ease",
204
+ flexShrink: 0,
205
+ color: "hsl(var(--foreground, 220 30% 15%) / 0.74)",
206
+ marginRight: "0.4rem"
207
+ }
208
+ }
209
+ ),
210
+ /* @__PURE__ */ jsxs2("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.5rem", flex: 1 }, children: [
211
+ resolvedIcon ? /* @__PURE__ */ jsx3(
212
+ "span",
213
+ {
214
+ "aria-hidden": "true",
215
+ style: {
216
+ display: "inline-flex",
217
+ alignItems: "center",
218
+ justifyContent: "center",
219
+ minWidth: "1.45rem",
220
+ height: "1.45rem",
221
+ color: "hsl(var(--foreground, 220 30% 15%))"
222
+ },
223
+ children: resolvedIcon
224
+ }
225
+ ) : null,
226
+ /* @__PURE__ */ jsx3("span", { children: title })
227
+ ] })
228
+ ]
229
+ }
230
+ ),
231
+ /* @__PURE__ */ jsx3(
232
+ "div",
233
+ {
234
+ id: panelId,
235
+ role: "region",
236
+ "aria-labelledby": triggerId,
237
+ hidden: !isOpen,
238
+ className: "mdx-accordion-content",
239
+ style: {
240
+ padding: "0.75rem 0.8rem",
241
+ background: "hsl(var(--card, 0 0% 100%))",
242
+ color: "hsl(var(--card-foreground, 220 30% 15%))"
243
+ },
244
+ children
245
+ }
246
+ )
247
+ ]
248
+ }
249
+ );
250
+ }
251
+
252
+ // src/components/AccordionsMdx.tsx
253
+ import { useMemo, useState as useState2 } from "react";
254
+ import { jsx as jsx4 } from "react/jsx-runtime";
255
+ function AccordionsMdx({ children }) {
256
+ const [openTitle, setOpenTitle] = useState2(null);
257
+ const value = useMemo(() => ({ inGroup: true, openTitle, setOpenTitle }), [openTitle]);
258
+ return /* @__PURE__ */ jsx4(AccordionGroupContext.Provider, { value, children: /* @__PURE__ */ jsx4(
259
+ "div",
260
+ {
261
+ className: "mdx-accordion-group",
262
+ style: {
263
+ margin: "1rem 0",
264
+ display: "grid",
265
+ gap: "0.85rem"
266
+ },
267
+ children
268
+ }
269
+ ) });
270
+ }
271
+
272
+ // src/components/CardMdx.tsx
273
+ import {
274
+ cloneElement,
275
+ useId as useId2,
276
+ useState as useState3
277
+ } from "react";
278
+
279
+ // src/components/LinkMdx.tsx
280
+ import { jsx as jsx5 } from "react/jsx-runtime";
281
+ function LinkMdx({ href, rel, target, ...props }) {
282
+ if (!href) return null;
283
+ const external = /^https?:\/\//.test(href);
284
+ const computedTarget = target ?? (external ? "_blank" : void 0);
285
+ const computedRel = rel ?? (external ? "noopener noreferrer" : void 0);
286
+ return /* @__PURE__ */ jsx5("a", { href, target: computedTarget, rel: computedRel, ...props });
287
+ }
288
+
289
+ // src/components/CardMdx.tsx
290
+ import { Fragment, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
291
+ var baseStyle = {
292
+ position: "relative",
293
+ display: "block",
294
+ border: "1px solid hsl(var(--border-color, 210 20% 85%))",
295
+ borderRadius: 16,
296
+ // rounded-2xl
297
+ background: "hsl(var(--card, 0 0% 100%))",
298
+ // bg-base-100
299
+ color: "hsl(var(--foreground, 220 30% 15%))",
300
+ textDecoration: "none",
301
+ padding: "1.5rem",
302
+ // p-6
303
+ overflow: "hidden",
304
+ transition: "border-color 0.2s ease, box-shadow 0.24s ease, transform 0.24s ease"
305
+ };
306
+ var hoverStyle = {
307
+ borderColor: "hsla(var(--primary, 210 81% 56%) / 0.4)",
308
+ // hover:border-primary/40
309
+ boxShadow: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)"
310
+ // shadow-lg
311
+ };
312
+ var iconBoxStyle = {
313
+ display: "inline-flex",
314
+ alignItems: "center",
315
+ justifyContent: "center",
316
+ width: 48,
317
+ // h-12
318
+ height: 48,
319
+ borderRadius: 8,
320
+ // rounded-lg
321
+ background: "hsla(var(--primary, 210 81% 56%) / 0.1)",
322
+ // bg-primary/10
323
+ color: "hsl(var(--primary, 210 81% 56%))",
324
+ flexShrink: 0
325
+ };
326
+ function CardMdx({
327
+ title,
328
+ icon,
329
+ href,
330
+ horizontal,
331
+ children,
332
+ style,
333
+ className,
334
+ __LinkComponent,
335
+ ...props
336
+ }) {
337
+ const patternId = `card-${useId2().replace(/[:.]/g, "-")}`;
338
+ const [hovered, setHovered] = useState3(false);
339
+ const resolvedIcon = resolveLucideIcon(icon);
340
+ const LinkComponent = __LinkComponent ?? LinkMdx;
341
+ const iconNode = resolvedIcon ? cloneElement(resolvedIcon, { size: 24 }) : null;
342
+ const content = /* @__PURE__ */ jsxs3(Fragment, { children: [
343
+ /* @__PURE__ */ jsxs3(
344
+ "svg",
345
+ {
346
+ "aria-hidden": "true",
347
+ style: {
348
+ position: "absolute",
349
+ inset: 0,
350
+ width: "100%",
351
+ height: "100%",
352
+ color: "hsl(var(--primary, 210 81% 56%))",
353
+ pointerEvents: "none"
354
+ },
355
+ children: [
356
+ /* @__PURE__ */ jsx6("defs", { children: /* @__PURE__ */ jsx6("pattern", { id: patternId, width: "40", height: "40", patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ jsx6(
357
+ "path",
358
+ {
359
+ d: "M 40 0 L 0 0 0 40",
360
+ fill: "none",
361
+ stroke: "currentColor",
362
+ strokeWidth: "0.5",
363
+ opacity: "0.15"
364
+ }
365
+ ) }) }),
366
+ /* @__PURE__ */ jsx6("rect", { width: "100%", height: "100%", fill: `url(#${patternId})` })
367
+ ]
368
+ }
369
+ ),
370
+ /* @__PURE__ */ jsx6("div", { style: { position: "relative", zIndex: 10 }, children: /* @__PURE__ */ jsxs3(
371
+ "div",
372
+ {
373
+ style: horizontal ? {
374
+ display: "grid",
375
+ gridTemplateColumns: "auto 1fr",
376
+ gap: "1rem",
377
+ alignItems: "flex-start"
378
+ } : void 0,
379
+ children: [
380
+ iconNode ? /* @__PURE__ */ jsx6(
381
+ "span",
382
+ {
383
+ "aria-hidden": "true",
384
+ "data-card-icon": "",
385
+ style: {
386
+ ...iconBoxStyle,
387
+ marginBottom: horizontal ? 0 : "1rem"
388
+ },
389
+ children: iconNode
390
+ }
391
+ ) : null,
392
+ /* @__PURE__ */ jsxs3("div", { children: [
393
+ /* @__PURE__ */ jsx6(
394
+ "div",
395
+ {
396
+ style: {
397
+ fontWeight: 600,
398
+ // font-semibold
399
+ fontSize: "1.125rem",
400
+ // text-lg
401
+ lineHeight: 1.4,
402
+ marginBottom: children ? "0.5rem" : 0,
403
+ // mb-2
404
+ color: "hsl(var(--foreground, 220 30% 15%))"
405
+ },
406
+ children: title
407
+ }
408
+ ),
409
+ children ? /* @__PURE__ */ jsx6(
410
+ "div",
411
+ {
412
+ style: {
413
+ color: "hsl(var(--muted-foreground, 220 15% 50%))",
414
+ // text-muted-foreground
415
+ fontSize: "0.875rem",
416
+ // text-sm
417
+ lineHeight: 1.5
418
+ },
419
+ children
420
+ }
421
+ ) : null
422
+ ] })
423
+ ]
424
+ }
425
+ ) })
426
+ ] });
427
+ const combinedStyle = {
428
+ ...baseStyle,
429
+ ...hovered ? hoverStyle : null,
430
+ ...style
431
+ };
432
+ const handlers = {
433
+ onMouseEnter: () => setHovered(true),
434
+ onMouseLeave: () => setHovered(false)
435
+ };
436
+ if (href) {
437
+ return /* @__PURE__ */ jsx6(
438
+ LinkComponent,
439
+ {
440
+ href,
441
+ "data-card-hover": "",
442
+ "data-card-link": "",
443
+ style: combinedStyle,
444
+ ...handlers,
445
+ ...className ? { className } : {},
446
+ ...props,
447
+ children: content
448
+ }
449
+ );
450
+ }
451
+ return /* @__PURE__ */ jsx6("div", { style: combinedStyle, ...handlers, ...className ? { className } : {}, ...props, children: content });
452
+ }
453
+
454
+ // src/components/CardsMdx.tsx
455
+ import { jsx as jsx7 } from "react/jsx-runtime";
456
+ function CardsMdx({ cols = 2, children, style }) {
457
+ const columnCount = Math.max(1, Math.min(4, cols));
458
+ return /* @__PURE__ */ jsx7(
459
+ "div",
460
+ {
461
+ className: "docubook-card-group",
462
+ style: {
463
+ "--docubook-card-group-template": `repeat(${columnCount}, minmax(240px, 1fr))`,
464
+ ...style
465
+ },
466
+ children
467
+ }
468
+ );
469
+ }
470
+
471
+ // src/components/CodeBlockMdx.tsx
472
+ import { isValidElement } from "react";
473
+ import {
474
+ SiJavascript,
475
+ SiTypescript,
476
+ SiReact,
477
+ SiPython,
478
+ SiGo,
479
+ SiPhp,
480
+ SiRuby,
481
+ SiSwift,
482
+ SiKotlin,
483
+ SiHtml5,
484
+ SiCss,
485
+ SiSass,
486
+ SiPostgresql,
487
+ SiGraphql,
488
+ SiYaml,
489
+ SiToml,
490
+ SiDocker,
491
+ SiNginx,
492
+ SiGit,
493
+ SiGnubash,
494
+ SiMarkdown
495
+ } from "react-icons/si";
496
+ import { FaJava, FaCode, FaRegFileAlt } from "react-icons/fa";
497
+ import { TbJson } from "react-icons/tb";
498
+
499
+ // src/utils/CopyButton.tsx
500
+ import { useEffect, useId as useId3, useRef, useState as useState4 } from "react";
501
+ import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
502
+ function CopyButton({ content }) {
503
+ const [copied, setCopied] = useState4(false);
504
+ const timeoutRef = useRef(null);
505
+ const statusId = useId3();
506
+ useEffect(() => {
507
+ return () => {
508
+ if (timeoutRef.current) {
509
+ clearTimeout(timeoutRef.current);
510
+ }
511
+ };
512
+ }, []);
513
+ async function onCopy() {
514
+ try {
515
+ await navigator.clipboard.writeText(content);
516
+ setCopied(true);
517
+ if (timeoutRef.current) {
518
+ clearTimeout(timeoutRef.current);
519
+ }
520
+ timeoutRef.current = setTimeout(() => setCopied(false), 1600);
521
+ } catch {
522
+ setCopied(false);
523
+ }
524
+ }
525
+ return /* @__PURE__ */ jsxs4(
526
+ "button",
527
+ {
528
+ type: "button",
529
+ onClick: onCopy,
530
+ "aria-label": copied ? "Copied" : "Copy code",
531
+ "aria-describedby": statusId,
532
+ style: {
533
+ position: "relative",
534
+ border: "1px solid hsl(var(--border-color, 210 20% 85%))",
535
+ borderRadius: 8,
536
+ background: "hsl(var(--card, 0 0% 100%))",
537
+ color: "hsl(var(--foreground, 220 30% 15%))",
538
+ fontSize: "0.78rem",
539
+ padding: "0.35rem 0.55rem",
540
+ cursor: "copy"
541
+ },
542
+ children: [
543
+ copied ? "Copied" : "Copy",
544
+ /* @__PURE__ */ jsx8(
545
+ "span",
546
+ {
547
+ id: statusId,
548
+ "aria-live": "polite",
549
+ style: { position: "absolute", left: -9999, top: "auto" },
550
+ children: copied ? "Code copied to clipboard" : "Ready to copy code"
551
+ }
552
+ )
553
+ ]
554
+ }
555
+ );
556
+ }
557
+
558
+ // src/utils/ExpandableCode.tsx
559
+ import { useEffect as useEffect2, useState as useState5, useRef as useRef2 } from "react";
560
+ import { Ellipsis } from "lucide-react";
561
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
562
+ var DEFAULT_VISIBLE_LINES = 20;
563
+ var FALLBACK_LINE_HEIGHT_PX = 24;
564
+ function ExpandableCode({
565
+ isExpandable,
566
+ totalLines,
567
+ preContent,
568
+ preProps,
569
+ className
570
+ }) {
571
+ const [isOpen, setIsOpen] = useState5(false);
572
+ const [collapsedMaxHeight, setCollapsedMaxHeight] = useState5(
573
+ `${DEFAULT_VISIBLE_LINES * FALLBACK_LINE_HEIGHT_PX}px`
574
+ );
575
+ const preRef = useRef2(null);
576
+ const shouldShow = isExpandable && totalLines > DEFAULT_VISIBLE_LINES;
577
+ const isCollapsed = shouldShow && !isOpen;
578
+ useEffect2(() => {
579
+ const pre = preRef.current;
580
+ if (!pre) return;
581
+ const calculateCollapsedHeight = () => {
582
+ const styles = window.getComputedStyle(pre);
583
+ const paddingTop = Number.parseFloat(styles.paddingTop) || 0;
584
+ const paddingBottom = Number.parseFloat(styles.paddingBottom) || 0;
585
+ const contentHeight = Math.max(pre.scrollHeight - paddingTop - paddingBottom, 0);
586
+ if (totalLines > 0 && contentHeight > 0) {
587
+ const lineHeight2 = contentHeight / totalLines;
588
+ setCollapsedMaxHeight(
589
+ `${DEFAULT_VISIBLE_LINES * lineHeight2 + paddingTop + paddingBottom}px`
590
+ );
591
+ return;
592
+ }
593
+ let lineHeight = Number.parseFloat(styles.lineHeight);
594
+ if (!Number.isFinite(lineHeight) || lineHeight === 0) {
595
+ const fontSize = Number.parseFloat(styles.fontSize);
596
+ lineHeight = Number.isFinite(fontSize) ? fontSize * 1.5 : FALLBACK_LINE_HEIGHT_PX;
597
+ }
598
+ setCollapsedMaxHeight(`${DEFAULT_VISIBLE_LINES * lineHeight + paddingTop + paddingBottom}px`);
599
+ };
600
+ const frame = requestAnimationFrame(calculateCollapsedHeight);
601
+ const timeout = window.setTimeout(calculateCollapsedHeight, 50);
602
+ window.addEventListener("resize", calculateCollapsedHeight);
603
+ return () => {
604
+ cancelAnimationFrame(frame);
605
+ window.clearTimeout(timeout);
606
+ window.removeEventListener("resize", calculateCollapsedHeight);
607
+ };
608
+ }, [preContent, totalLines]);
609
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
610
+ /* @__PURE__ */ jsx9("div", { className: "mdx-expandable-code-container", children: /* @__PURE__ */ jsx9(
611
+ "pre",
612
+ {
613
+ ref: preRef,
614
+ ...preProps,
615
+ className: `${className || ""} mdx-expandable-code`,
616
+ style: {
617
+ margin: 0,
618
+ padding: "0.9rem",
619
+ overflowX: "auto",
620
+ minWidth: "100%",
621
+ maxHeight: isCollapsed ? collapsedMaxHeight : "none",
622
+ overflowY: isCollapsed ? "hidden" : "visible",
623
+ transition: "max-height 0.3s ease",
624
+ scrollbarWidth: "thin"
625
+ },
626
+ children: preContent
627
+ }
628
+ ) }),
629
+ shouldShow && /* @__PURE__ */ jsx9(
630
+ "div",
631
+ {
632
+ className: "code-block-expandable-footer",
633
+ style: {
634
+ display: "flex",
635
+ alignItems: "center",
636
+ justifyContent: "center",
637
+ padding: "0.6rem",
638
+ background: "hsl(var(--muted, 210 20% 92%))",
639
+ borderTop: "1px solid hsl(var(--border-color, 210 20% 85%))"
640
+ },
641
+ children: /* @__PURE__ */ jsxs5(
642
+ "button",
643
+ {
644
+ className: "code-block-expandable-toggle",
645
+ onClick: () => setIsOpen(!isOpen),
646
+ style: {
647
+ display: "flex",
648
+ alignItems: "center",
649
+ gap: "0.5rem",
650
+ padding: "0.4rem 0.8rem",
651
+ border: "1px solid hsl(var(--border-color, 210 20% 85%))",
652
+ borderRadius: 6,
653
+ background: "hsl(var(--background, 210 40% 98%))",
654
+ color: "hsl(var(--foreground, 220 30% 15%))",
655
+ fontSize: "0.85rem",
656
+ fontWeight: 500,
657
+ cursor: "pointer",
658
+ transition: "all 0.2s ease",
659
+ fontFamily: "inherit"
660
+ },
661
+ children: [
662
+ /* @__PURE__ */ jsx9(
663
+ Ellipsis,
664
+ {
665
+ size: 16,
666
+ style: {
667
+ transform: isOpen ? "rotate(180deg)" : "rotate(0deg)",
668
+ transition: "transform 0.2s ease"
669
+ }
670
+ }
671
+ ),
672
+ /* @__PURE__ */ jsx9("span", { children: isOpen ? "Collapse" : `See all ${totalLines} lines` })
673
+ ]
674
+ }
675
+ )
676
+ }
677
+ )
678
+ ] });
679
+ }
680
+
681
+ // src/components/CodeBlockMdx.tsx
682
+ import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
683
+ function getLanguage(className = "") {
684
+ const match = className.match(/language-(\w+)/);
685
+ return match ? match[1] : "text";
686
+ }
687
+ function resolveLanguage(preClassName, dataLanguage, children) {
688
+ if (typeof dataLanguage === "string" && dataLanguage.trim().length > 0) {
689
+ return dataLanguage.trim().toLowerCase();
690
+ }
691
+ const languageFromPre = getLanguage(preClassName ?? "");
692
+ if (languageFromPre !== "text") return languageFromPre;
693
+ if (isValidElement(children)) {
694
+ const childProps = children.props;
695
+ if (typeof childProps.className === "string") {
696
+ const languageFromCode = getLanguage(childProps.className);
697
+ if (languageFromCode !== "text") return languageFromCode;
698
+ }
699
+ }
700
+ return "text";
701
+ }
702
+ function getLanguageIcon(language) {
703
+ const normalized = language.toLowerCase();
704
+ const iconProps = { size: 14 };
705
+ const map = {
706
+ gitignore: /* @__PURE__ */ jsx10(SiGit, { ...iconProps }),
707
+ docker: /* @__PURE__ */ jsx10(SiDocker, { ...iconProps }),
708
+ dockerfile: /* @__PURE__ */ jsx10(SiDocker, { ...iconProps }),
709
+ nginx: /* @__PURE__ */ jsx10(SiNginx, { ...iconProps }),
710
+ sql: /* @__PURE__ */ jsx10(SiPostgresql, { ...iconProps }),
711
+ graphql: /* @__PURE__ */ jsx10(SiGraphql, { ...iconProps }),
712
+ yaml: /* @__PURE__ */ jsx10(SiYaml, { ...iconProps }),
713
+ yml: /* @__PURE__ */ jsx10(SiYaml, { ...iconProps }),
714
+ toml: /* @__PURE__ */ jsx10(SiToml, { ...iconProps }),
715
+ json: /* @__PURE__ */ jsx10(TbJson, { ...iconProps }),
716
+ md: /* @__PURE__ */ jsx10(SiMarkdown, { ...iconProps }),
717
+ markdown: /* @__PURE__ */ jsx10(SiMarkdown, { ...iconProps }),
718
+ bash: /* @__PURE__ */ jsx10(SiGnubash, { ...iconProps }),
719
+ sh: /* @__PURE__ */ jsx10(SiGnubash, { ...iconProps }),
720
+ shell: /* @__PURE__ */ jsx10(SiGnubash, { ...iconProps }),
721
+ swift: /* @__PURE__ */ jsx10(SiSwift, { ...iconProps }),
722
+ kotlin: /* @__PURE__ */ jsx10(SiKotlin, { ...iconProps }),
723
+ kt: /* @__PURE__ */ jsx10(SiKotlin, { ...iconProps }),
724
+ kts: /* @__PURE__ */ jsx10(SiKotlin, { ...iconProps }),
725
+ rb: /* @__PURE__ */ jsx10(SiRuby, { ...iconProps }),
726
+ ruby: /* @__PURE__ */ jsx10(SiRuby, { ...iconProps }),
727
+ php: /* @__PURE__ */ jsx10(SiPhp, { ...iconProps }),
728
+ go: /* @__PURE__ */ jsx10(SiGo, { ...iconProps }),
729
+ py: /* @__PURE__ */ jsx10(SiPython, { ...iconProps }),
730
+ python: /* @__PURE__ */ jsx10(SiPython, { ...iconProps }),
731
+ java: /* @__PURE__ */ jsx10(FaJava, { ...iconProps }),
732
+ tsx: /* @__PURE__ */ jsx10(SiReact, { ...iconProps }),
733
+ typescript: /* @__PURE__ */ jsx10(SiTypescript, { ...iconProps }),
734
+ ts: /* @__PURE__ */ jsx10(SiTypescript, { ...iconProps }),
735
+ jsx: /* @__PURE__ */ jsx10(SiReact, { ...iconProps }),
736
+ js: /* @__PURE__ */ jsx10(SiJavascript, { ...iconProps }),
737
+ javascript: /* @__PURE__ */ jsx10(SiJavascript, { ...iconProps }),
738
+ html: /* @__PURE__ */ jsx10(SiHtml5, { ...iconProps }),
739
+ css: /* @__PURE__ */ jsx10(SiCss, { ...iconProps }),
740
+ scss: /* @__PURE__ */ jsx10(SiSass, { ...iconProps }),
741
+ sass: /* @__PURE__ */ jsx10(SiSass, { ...iconProps }),
742
+ text: /* @__PURE__ */ jsx10(FaRegFileAlt, { ...iconProps }),
743
+ plaintext: /* @__PURE__ */ jsx10(FaRegFileAlt, { ...iconProps })
744
+ };
745
+ return map[normalized] || /* @__PURE__ */ jsx10(FaCode, { ...iconProps });
746
+ }
747
+ function countCodeLines(raw) {
748
+ let normalized = raw.replace(/\r\n/g, "\n");
749
+ if (normalized.startsWith("\n")) normalized = normalized.slice(1);
750
+ if (normalized.endsWith("\n")) normalized = normalized.slice(0, -1);
751
+ if (normalized.length === 0) return 0;
752
+ return normalized.split("\n").length;
753
+ }
754
+ function CodeBlock({ children, raw, ...rest }) {
755
+ const {
756
+ className,
757
+ "data-title": title,
758
+ "data-language": dataLanguage,
759
+ "data-expandable": isExpandable,
760
+ "data-expandable-lines": totalLinesStr,
761
+ ...props
762
+ } = rest;
763
+ const language = resolveLanguage(
764
+ typeof className === "string" ? className : void 0,
765
+ dataLanguage,
766
+ children
767
+ );
768
+ const totalLinesFromMeta = parseInt(totalLinesStr, 10) || 0;
769
+ const totalLinesFromRaw = raw ? countCodeLines(raw) : 0;
770
+ const totalLines = totalLinesFromRaw || totalLinesFromMeta;
771
+ const hasExpandableClass = typeof className === "string" && className.split(" ").includes("mdx-expandable-code");
772
+ const shouldExpand = isExpandable === "true" || hasExpandableClass;
773
+ const preProps = {
774
+ ...props,
775
+ "data-expandable": isExpandable,
776
+ "data-expandable-lines": totalLinesStr
777
+ };
778
+ return /* @__PURE__ */ jsxs6(
779
+ "div",
780
+ {
781
+ className: "code-block-container not-prose",
782
+ style: {
783
+ position: "relative",
784
+ margin: "1.5rem 0",
785
+ border: "1px solid hsl(var(--border-color, 210 20% 85%))",
786
+ overflow: "hidden",
787
+ fontSize: "0.875rem",
788
+ borderRadius: "0.75rem"
789
+ },
790
+ children: [
791
+ /* @__PURE__ */ jsxs6(
792
+ "div",
793
+ {
794
+ className: "code-block-header",
795
+ style: {
796
+ display: "flex",
797
+ alignItems: "center",
798
+ justifyContent: "space-between",
799
+ gap: "0.75rem",
800
+ background: "hsl(var(--muted, 210 20% 92%))",
801
+ padding: "0.5rem 1rem",
802
+ borderBottom: "1px solid hsl(var(--border-color, 210 20% 85%))",
803
+ color: "hsl(var(--muted-foreground, 220 15% 50%))",
804
+ fontFamily: "monospace",
805
+ fontSize: "0.8rem"
806
+ },
807
+ children: [
808
+ /* @__PURE__ */ jsxs6(
809
+ "div",
810
+ {
811
+ style: {
812
+ display: "flex",
813
+ alignItems: "center",
814
+ gap: 6,
815
+ fontSize: "0.78rem",
816
+ color: "hsl(var(--muted-foreground, 220 15% 50%))"
817
+ },
818
+ children: [
819
+ getLanguageIcon(language),
820
+ /* @__PURE__ */ jsx10("span", { children: title || language })
821
+ ]
822
+ }
823
+ ),
824
+ raw ? /* @__PURE__ */ jsx10(
825
+ "div",
826
+ {
827
+ className: "code-block-actions",
828
+ style: {
829
+ display: "inline-flex",
830
+ alignItems: "center",
831
+ marginLeft: "auto"
832
+ },
833
+ children: /* @__PURE__ */ jsx10(
834
+ "div",
835
+ {
836
+ style: {
837
+ color: "hsl(var(--muted-foreground, 220 15% 50%))",
838
+ transition: "color 0.2s ease-in-out"
839
+ },
840
+ children: /* @__PURE__ */ jsx10(CopyButton, { content: raw })
841
+ }
842
+ )
843
+ }
844
+ ) : null
845
+ ]
846
+ }
847
+ ),
848
+ /* @__PURE__ */ jsx10(
849
+ "div",
850
+ {
851
+ className: "code-block-body",
852
+ style: {
853
+ overflowY: "visible",
854
+ backgroundColor: "hsl(var(--background, 210 40% 98%))",
855
+ willChange: "transform",
856
+ borderBottom: "none"
857
+ },
858
+ children: /* @__PURE__ */ jsx10(
859
+ ExpandableCode,
860
+ {
861
+ isExpandable: shouldExpand,
862
+ totalLines,
863
+ preContent: children,
864
+ preProps,
865
+ className
866
+ }
867
+ )
868
+ }
869
+ )
870
+ ]
871
+ }
872
+ );
873
+ }
874
+
875
+ // src/components/TreeMdx.tsx
876
+ import React, {
877
+ useId as useId4,
878
+ useState as useState6
879
+ } from "react";
880
+ import { ChevronRight as ChevronRight2, File as FileIcon, Folder as FolderIcon, FolderOpen } from "lucide-react";
881
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
882
+ function collectText(node, out) {
883
+ if (typeof node === "string" || typeof node === "number") {
884
+ out.push(String(node));
885
+ } else if (Array.isArray(node)) {
886
+ node.forEach((n) => collectText(n, out));
887
+ } else if (React.isValidElement(node)) {
888
+ const kids = node.props?.children;
889
+ if (kids !== void 0 && kids !== null) collectText(kids, out);
890
+ }
891
+ }
892
+ function parseTree(text) {
893
+ const root = [];
894
+ const stack = [];
895
+ let first = true;
896
+ for (const raw of text.split("\n")) {
897
+ const line = raw.replace(/\t/g, " ");
898
+ if (!line.trim()) continue;
899
+ const connectorIdx = line.search(/[├└]/);
900
+ const prefix = connectorIdx >= 0 ? line.slice(0, connectorIdx) : "";
901
+ const name = line.replace(/^[\s│├└─]+/, "").trim();
902
+ if (!name) continue;
903
+ const depth = (prefix.match(/│/g) || []).length;
904
+ const node = { name: name.replace(/\/+$/, ""), children: [] };
905
+ if (first) {
906
+ root.push(node);
907
+ stack.push({ node, depth });
908
+ first = false;
909
+ continue;
910
+ }
911
+ while (stack.length > 1 && stack[stack.length - 1].depth >= depth) stack.pop();
912
+ stack[stack.length - 1].node.children.push(node);
913
+ stack.push({ node, depth });
914
+ }
915
+ return root;
916
+ }
917
+ function TreeMdx({ className, style, children }) {
918
+ const parts = [];
919
+ collectText(children, parts);
920
+ const tree = parseTree(parts.join(""));
921
+ if (tree.length === 0) return null;
922
+ return /* @__PURE__ */ jsx11(
923
+ "div",
924
+ {
925
+ className,
926
+ role: "tree",
927
+ "aria-label": "Tree",
928
+ style: {
929
+ border: "1px solid hsl(var(--border-color, 210 20% 85%))",
930
+ borderRadius: 12,
931
+ padding: "0.5rem",
932
+ margin: "1rem 0",
933
+ background: "hsla(var(--card, 0 0% 100%) / 0.9)",
934
+ backdropFilter: "blur(8px)",
935
+ boxShadow: "0 10px 20px rgba(0,0,0,0.05)",
936
+ ...style
937
+ },
938
+ children: tree.map((node) => /* @__PURE__ */ jsx11(TreeRow, { node }, node.name))
939
+ }
940
+ );
941
+ }
942
+ function TreeRow({ node }) {
943
+ const [isOpen, setIsOpen] = useState6(true);
944
+ const [isHovered, setIsHovered] = useState6(false);
945
+ const [isFocused, setIsFocused] = useState6(false);
946
+ const hasChildren = node.children.length > 0;
947
+ const groupId = useId4();
948
+ const handleKeyDown = (event) => {
949
+ if (!hasChildren) return;
950
+ if (event.key === "ArrowRight") {
951
+ event.preventDefault();
952
+ setIsOpen(true);
953
+ return;
954
+ }
955
+ if (event.key === "ArrowLeft") {
956
+ event.preventDefault();
957
+ setIsOpen(false);
958
+ return;
959
+ }
960
+ if (event.key === "Enter" || event.key === " ") {
961
+ event.preventDefault();
962
+ setIsOpen((prev) => !prev);
963
+ }
964
+ };
965
+ return /* @__PURE__ */ jsxs7(
966
+ "div",
967
+ {
968
+ role: "treeitem",
969
+ "aria-expanded": hasChildren ? isOpen : void 0,
970
+ style: { marginLeft: "0.35rem" },
971
+ children: [
972
+ /* @__PURE__ */ jsxs7(
973
+ "button",
974
+ {
975
+ type: "button",
976
+ className: "flex items-center gap-2 rounded-md px-2 py-1.5 transition-colors duration-150",
977
+ onClick: () => hasChildren && setIsOpen((prev) => !prev),
978
+ onMouseEnter: () => setIsHovered(true),
979
+ onMouseLeave: () => setIsHovered(false),
980
+ onFocus: () => setIsFocused(true),
981
+ onBlur: () => setIsFocused(false),
982
+ onKeyDown: handleKeyDown,
983
+ "aria-label": `${isOpen ? "Collapse" : "Expand"} ${node.name}`,
984
+ "aria-expanded": hasChildren ? isOpen : void 0,
985
+ "aria-controls": hasChildren ? groupId : void 0,
986
+ style: {
987
+ cursor: hasChildren ? "pointer" : "default",
988
+ width: "100%",
989
+ border: "none",
990
+ textAlign: "left",
991
+ background: isHovered || isFocused ? "hsla(var(--muted, 210 20% 92%) / 0.45)" : "transparent"
992
+ },
993
+ children: [
994
+ hasChildren ? /* @__PURE__ */ jsx11(
995
+ ChevronRight2,
996
+ {
997
+ className: "h-3.5 w-3.5 transition-transform duration-200",
998
+ style: {
999
+ transform: isOpen ? "rotate(90deg)" : "rotate(0deg)",
1000
+ color: isHovered || isFocused ? "hsl(var(--primary, 210 81% 56%))" : "hsl(var(--muted-foreground, 220 15% 50%))"
1001
+ }
1002
+ }
1003
+ ) : /* @__PURE__ */ jsx11(
1004
+ FileIcon,
1005
+ {
1006
+ className: "h-3.5 w-3.5",
1007
+ style: {
1008
+ color: isHovered || isFocused ? "hsl(var(--primary, 210 81% 56%))" : "hsl(var(--muted-foreground, 220 15% 50%))"
1009
+ }
1010
+ }
1011
+ ),
1012
+ hasChildren && isOpen ? /* @__PURE__ */ jsx11(
1013
+ FolderOpen,
1014
+ {
1015
+ className: "h-4 w-4",
1016
+ style: {
1017
+ color: isHovered || isFocused ? "hsl(var(--primary, 210 81% 56%))" : "hsl(var(--muted-foreground, 220 15% 50%))"
1018
+ }
1019
+ }
1020
+ ) : hasChildren && /* @__PURE__ */ jsx11(
1021
+ FolderIcon,
1022
+ {
1023
+ className: "h-4 w-4",
1024
+ style: {
1025
+ color: isHovered || isFocused ? "hsl(var(--primary, 210 81% 56%))" : "hsl(var(--muted-foreground, 220 15% 50%))"
1026
+ }
1027
+ }
1028
+ ),
1029
+ /* @__PURE__ */ jsx11(
1030
+ "span",
1031
+ {
1032
+ style: {
1033
+ fontWeight: 600,
1034
+ fontFamily: hasChildren ? "inherit" : "SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
1035
+ fontSize: "0.9rem",
1036
+ color: isHovered || isFocused ? "hsl(var(--primary, 210 81% 56%))" : "hsl(var(--foreground, 220 30% 15%))"
1037
+ },
1038
+ children: node.name
1039
+ }
1040
+ )
1041
+ ]
1042
+ }
1043
+ ),
1044
+ hasChildren && isOpen && /* @__PURE__ */ jsx11(
1045
+ "div",
1046
+ {
1047
+ id: groupId,
1048
+ role: "group",
1049
+ style: {
1050
+ marginLeft: "1rem",
1051
+ borderLeft: "2px solid hsl(var(--border-color, 210 20% 85%))",
1052
+ paddingLeft: "0.6rem"
1053
+ },
1054
+ children: node.children.map((child) => /* @__PURE__ */ jsx11(TreeRow, { node: child }, child.name))
1055
+ }
1056
+ )
1057
+ ]
1058
+ }
1059
+ );
1060
+ }
1061
+
1062
+ // src/components/ImageMdx.tsx
1063
+ import { createPortal } from "react-dom";
1064
+ import { useEffect as useEffect3, useId as useId5, useRef as useRef3, useState as useState7 } from "react";
1065
+ import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
1066
+ function ImageMdx({
1067
+ src,
1068
+ alt = "image",
1069
+ width = 800,
1070
+ height = 350,
1071
+ zoom = true,
1072
+ style,
1073
+ ImageComponent,
1074
+ ...props
1075
+ }) {
1076
+ const Image = ImageComponent ?? "img";
1077
+ const [isOpen, setIsOpen] = useState7(false);
1078
+ const scrollYRef = useRef3(0);
1079
+ const closeButtonRef = useRef3(null);
1080
+ const dialogId = useId5();
1081
+ useEffect3(() => {
1082
+ if (!isOpen) return;
1083
+ scrollYRef.current = window.scrollY;
1084
+ document.body.style.position = "fixed";
1085
+ document.body.style.top = `-${scrollYRef.current}px`;
1086
+ document.body.style.width = "100%";
1087
+ closeButtonRef.current?.focus();
1088
+ const onEsc = (e) => {
1089
+ if (e.key === "Escape") setIsOpen(false);
1090
+ };
1091
+ const onFocusTrap = (e) => {
1092
+ if (e.key !== "Tab") return;
1093
+ const dialog = document.getElementById(dialogId);
1094
+ const focusable = dialog?.querySelectorAll(
1095
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
1096
+ );
1097
+ if (!focusable || focusable.length === 0) return;
1098
+ const first = focusable[0];
1099
+ const last = focusable[focusable.length - 1];
1100
+ if (!e.shiftKey && document.activeElement === last) {
1101
+ e.preventDefault();
1102
+ first.focus();
1103
+ }
1104
+ if (e.shiftKey && document.activeElement === first) {
1105
+ e.preventDefault();
1106
+ last.focus();
1107
+ }
1108
+ };
1109
+ window.addEventListener("keydown", onEsc);
1110
+ window.addEventListener("keydown", onFocusTrap);
1111
+ return () => {
1112
+ document.body.style.position = "";
1113
+ document.body.style.top = "";
1114
+ document.body.style.width = "";
1115
+ window.scrollTo(0, scrollYRef.current);
1116
+ window.removeEventListener("keydown", onEsc);
1117
+ window.removeEventListener("keydown", onFocusTrap);
1118
+ };
1119
+ }, [dialogId, isOpen]);
1120
+ if (!src) return null;
1121
+ const canZoom = zoom;
1122
+ return /* @__PURE__ */ jsxs8(Fragment3, { children: [
1123
+ /* @__PURE__ */ jsx12(
1124
+ "button",
1125
+ {
1126
+ type: "button",
1127
+ onClick: () => canZoom && setIsOpen(true),
1128
+ "aria-label": canZoom ? "Zoom image" : "Image",
1129
+ style: {
1130
+ display: "flex",
1131
+ justifyContent: "center",
1132
+ position: "relative",
1133
+ width: "100%",
1134
+ border: "none",
1135
+ background: "transparent",
1136
+ padding: 0,
1137
+ margin: "1rem 0",
1138
+ cursor: canZoom ? "zoom-in" : "default"
1139
+ },
1140
+ children: /* @__PURE__ */ jsx12(
1141
+ Image,
1142
+ {
1143
+ src,
1144
+ alt,
1145
+ width: typeof width === "number" ? width : parseInt(width, 10),
1146
+ height: typeof height === "number" ? height : parseInt(height, 10),
1147
+ style: {
1148
+ maxWidth: "100%",
1149
+ height: "auto",
1150
+ borderRadius: 10,
1151
+ ...style
1152
+ },
1153
+ ...props
1154
+ }
1155
+ )
1156
+ }
1157
+ ),
1158
+ canZoom && isOpen ? /* @__PURE__ */ jsx12(Portal, { children: /* @__PURE__ */ jsxs8(
1159
+ "div",
1160
+ {
1161
+ id: dialogId,
1162
+ role: "dialog",
1163
+ "aria-modal": "true",
1164
+ "aria-label": "Image preview",
1165
+ onClick: () => setIsOpen(false),
1166
+ style: {
1167
+ position: "fixed",
1168
+ inset: 0,
1169
+ zIndex: 99999,
1170
+ background: "rgba(0,0,0,0.88)",
1171
+ display: "flex",
1172
+ alignItems: "center",
1173
+ justifyContent: "center",
1174
+ padding: "1rem"
1175
+ },
1176
+ children: [
1177
+ /* @__PURE__ */ jsx12(
1178
+ "button",
1179
+ {
1180
+ ref: closeButtonRef,
1181
+ type: "button",
1182
+ "aria-label": "Close image preview",
1183
+ onClick: (e) => {
1184
+ e.stopPropagation();
1185
+ setIsOpen(false);
1186
+ },
1187
+ style: {
1188
+ position: "absolute",
1189
+ top: 16,
1190
+ right: 16,
1191
+ border: "1px solid rgba(255,255,255,0.24)",
1192
+ borderRadius: 8,
1193
+ background: "rgba(0,0,0,0.45)",
1194
+ color: "#fff",
1195
+ padding: "0.4rem 0.55rem",
1196
+ cursor: "pointer"
1197
+ },
1198
+ children: "Close"
1199
+ }
1200
+ ),
1201
+ /* @__PURE__ */ jsx12(
1202
+ "div",
1203
+ {
1204
+ onClick: (e) => e.stopPropagation(),
1205
+ style: {
1206
+ maxWidth: "92vw",
1207
+ maxHeight: "90vh",
1208
+ display: "flex",
1209
+ alignItems: "center",
1210
+ justifyContent: "center"
1211
+ },
1212
+ children: /* @__PURE__ */ jsx12(
1213
+ Image,
1214
+ {
1215
+ src,
1216
+ alt,
1217
+ width: 1920,
1218
+ height: 1080,
1219
+ style: {
1220
+ width: "auto",
1221
+ height: "auto",
1222
+ maxWidth: "92vw",
1223
+ maxHeight: "90vh",
1224
+ objectFit: "contain",
1225
+ borderRadius: 10
1226
+ }
1227
+ }
1228
+ )
1229
+ }
1230
+ )
1231
+ ]
1232
+ }
1233
+ ) }) : null
1234
+ ] });
1235
+ }
1236
+ function Portal({ children }) {
1237
+ if (typeof window === "undefined") return null;
1238
+ return createPortal(children, document.body);
1239
+ }
1240
+
1241
+ // src/components/StepperMdx.tsx
1242
+ import {
1243
+ Children,
1244
+ cloneElement as cloneElement2,
1245
+ isValidElement as isValidElement2
1246
+ } from "react";
1247
+ import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
1248
+ function StepsMdx({ children, style, className, ...props }) {
1249
+ const items = Children.toArray(children);
1250
+ return /* @__PURE__ */ jsxs9(
1251
+ "ol",
1252
+ {
1253
+ className,
1254
+ ...props,
1255
+ style: {
1256
+ listStyle: "none",
1257
+ position: "relative",
1258
+ margin: "1.2rem 0",
1259
+ padding: 0,
1260
+ display: "grid",
1261
+ gap: 0,
1262
+ ...style
1263
+ },
1264
+ children: [
1265
+ items.map((child, index) => {
1266
+ if (!isValidElement2(child)) {
1267
+ return child;
1268
+ }
1269
+ const item = child;
1270
+ return cloneElement2(item, {
1271
+ stepNumber: index + 1
1272
+ });
1273
+ }),
1274
+ items.length > 1 ? /* @__PURE__ */ jsx13(
1275
+ "span",
1276
+ {
1277
+ "aria-hidden": "true",
1278
+ style: {
1279
+ position: "absolute",
1280
+ left: "1.05rem",
1281
+ top: "0.8rem",
1282
+ bottom: "0.8rem",
1283
+ width: 1,
1284
+ background: "hsl(var(--border-color, 210 20% 85%))",
1285
+ pointerEvents: "none"
1286
+ }
1287
+ }
1288
+ ) : null
1289
+ ]
1290
+ }
1291
+ );
1292
+ }
1293
+ function StepMdx({ title, children, style, className, ...props }) {
1294
+ const { stepNumber, ...liProps } = props;
1295
+ return /* @__PURE__ */ jsxs9(
1296
+ "li",
1297
+ {
1298
+ className,
1299
+ ...liProps,
1300
+ style: {
1301
+ position: "relative",
1302
+ zIndex: 1,
1303
+ width: "100%",
1304
+ minWidth: 0,
1305
+ boxSizing: "border-box",
1306
+ marginLeft: "0.25rem",
1307
+ paddingLeft: "2.6rem",
1308
+ paddingBottom: "1.15rem",
1309
+ ...style
1310
+ },
1311
+ children: [
1312
+ /* @__PURE__ */ jsx13(
1313
+ "span",
1314
+ {
1315
+ "aria-hidden": "true",
1316
+ style: {
1317
+ position: "absolute",
1318
+ left: 0,
1319
+ top: 0,
1320
+ width: stepNumber && stepNumber >= 10 ? "1.9rem" : "1.6rem",
1321
+ height: "1.6rem",
1322
+ borderRadius: 10,
1323
+ display: "inline-flex",
1324
+ alignItems: "center",
1325
+ justifyContent: "center",
1326
+ fontSize: "0.82rem",
1327
+ fontWeight: 600,
1328
+ color: "hsl(var(--muted-foreground, 220 15% 50%))",
1329
+ background: "hsl(var(--muted, 210 12% 92%))",
1330
+ border: "1px solid hsl(var(--border-color, 210 20% 85%))"
1331
+ },
1332
+ children: stepNumber ?? "-"
1333
+ }
1334
+ ),
1335
+ /* @__PURE__ */ jsx13(
1336
+ "div",
1337
+ {
1338
+ style: {
1339
+ fontWeight: 700,
1340
+ fontSize: "1.02rem",
1341
+ lineHeight: 1.35,
1342
+ marginBottom: children ? "0.55rem" : 0,
1343
+ color: "hsl(var(--foreground, 220 30% 15%))"
1344
+ },
1345
+ children: title
1346
+ }
1347
+ ),
1348
+ children ? /* @__PURE__ */ jsx13(
1349
+ "div",
1350
+ {
1351
+ style: {
1352
+ color: "hsl(var(--muted-foreground, 220 15% 50%))",
1353
+ display: "flow-root",
1354
+ width: "100%",
1355
+ minWidth: 0,
1356
+ maxWidth: "100%",
1357
+ overflowX: "auto",
1358
+ overflowY: "hidden"
1359
+ },
1360
+ children
1361
+ }
1362
+ ) : null
1363
+ ]
1364
+ }
1365
+ );
1366
+ }
1367
+
1368
+ // src/components/TabsMdx.tsx
1369
+ import {
1370
+ Children as Children2,
1371
+ isValidElement as isValidElement3,
1372
+ useId as useId6,
1373
+ useState as useState8
1374
+ } from "react";
1375
+ import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
1376
+ function TabMdx({ children }) {
1377
+ return /* @__PURE__ */ jsx14(Fragment4, { children });
1378
+ }
1379
+ function titleToValue(title) {
1380
+ return title.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-_]/g, "");
1381
+ }
1382
+ function TabsMdx({ className, children }) {
1383
+ const id = useId6();
1384
+ const childArray = Children2.toArray(children);
1385
+ const tabItems = childArray.filter(
1386
+ (child) => isValidElement3(child) && typeof child.props.title === "string"
1387
+ ).map((child, index) => {
1388
+ const title = child.props.title;
1389
+ const fallback = `tab-${index + 1}`;
1390
+ const slug = titleToValue(title) || fallback;
1391
+ return {
1392
+ value: `${slug}-${index + 1}`,
1393
+ title,
1394
+ children: child.props.children
1395
+ };
1396
+ });
1397
+ const [activeValue, setActiveValue] = useState8(tabItems[0]?.value ?? "");
1398
+ const activeItem = tabItems.find((item) => item.value === activeValue) ?? tabItems[0];
1399
+ return /* @__PURE__ */ jsxs10("div", { className, style: { margin: "1rem 0" }, children: [
1400
+ /* @__PURE__ */ jsx14(
1401
+ "div",
1402
+ {
1403
+ role: "tablist",
1404
+ "aria-orientation": "horizontal",
1405
+ style: {
1406
+ display: "flex",
1407
+ flexWrap: "nowrap",
1408
+ gap: "0.75rem",
1409
+ alignItems: "center",
1410
+ overflowX: "auto",
1411
+ borderBottom: "1px solid hsl(var(--border-color, 210 20% 85%))"
1412
+ },
1413
+ children: tabItems.map((item) => {
1414
+ const active = item.value === activeItem?.value;
1415
+ const triggerId = `${id}-tab-${item.value}`;
1416
+ const panelId = `${id}-panel-${item.value}`;
1417
+ return /* @__PURE__ */ jsx14(
1418
+ "button",
1419
+ {
1420
+ id: triggerId,
1421
+ type: "button",
1422
+ role: "tab",
1423
+ "aria-selected": active,
1424
+ "aria-controls": panelId,
1425
+ tabIndex: active ? 0 : -1,
1426
+ onClick: () => setActiveValue(item.value),
1427
+ style: {
1428
+ border: "none",
1429
+ borderBottom: active ? "2px solid hsl(var(--primary, 210 81% 56%))" : "2px solid transparent",
1430
+ background: "transparent",
1431
+ color: active ? "hsl(var(--primary, 210 81% 56%))" : "hsl(var(--foreground, 220 30% 15%))",
1432
+ fontSize: "0.875rem",
1433
+ fontWeight: 600,
1434
+ lineHeight: 1.2,
1435
+ padding: "0.7rem 0.15rem",
1436
+ cursor: "pointer",
1437
+ whiteSpace: "nowrap"
1438
+ },
1439
+ children: item.title
1440
+ },
1441
+ item.value
1442
+ );
1443
+ })
1444
+ }
1445
+ ),
1446
+ tabItems.map((item) => {
1447
+ const active = item.value === activeItem?.value;
1448
+ const triggerId = `${id}-tab-${item.value}`;
1449
+ const panelId = `${id}-panel-${item.value}`;
1450
+ return /* @__PURE__ */ jsx14(
1451
+ "div",
1452
+ {
1453
+ role: "tabpanel",
1454
+ id: panelId,
1455
+ "aria-labelledby": triggerId,
1456
+ hidden: !active,
1457
+ tabIndex: 0,
1458
+ style: {
1459
+ marginTop: "1rem"
1460
+ },
1461
+ children: item.children
1462
+ },
1463
+ item.value
1464
+ );
1465
+ })
1466
+ ] });
1467
+ }
1468
+
1469
+ // src/components/TableMdx.tsx
1470
+ import { jsx as jsx15 } from "react/jsx-runtime";
1471
+ var tableShellStyle = {
1472
+ position: "relative",
1473
+ width: "100%",
1474
+ overflowX: "auto",
1475
+ overflowY: "hidden",
1476
+ border: "1px solid hsl(var(--border-color, 210 20% 85%))",
1477
+ borderRadius: 10,
1478
+ margin: "1rem 0"
1479
+ };
1480
+ var cellBaseStyle = {
1481
+ padding: "0.6rem 0.75rem",
1482
+ verticalAlign: "middle",
1483
+ borderBottom: "1px solid hsl(var(--border-color, 210 20% 85%))"
1484
+ };
1485
+ function TableMdx({ style, ...props }) {
1486
+ return /* @__PURE__ */ jsx15("div", { style: { ...tableShellStyle }, children: /* @__PURE__ */ jsx15(
1487
+ "table",
1488
+ {
1489
+ ...props,
1490
+ style: {
1491
+ width: "100%",
1492
+ fontSize: "0.925rem",
1493
+ borderCollapse: "collapse",
1494
+ margin: 0,
1495
+ ...style
1496
+ }
1497
+ }
1498
+ ) });
1499
+ }
1500
+ function TableHeaderMdx(props) {
1501
+ return /* @__PURE__ */ jsx15(
1502
+ "thead",
1503
+ {
1504
+ ...props,
1505
+ style: {
1506
+ background: "hsl(var(--muted, 210 20% 92%))",
1507
+ ...props.style
1508
+ }
1509
+ }
1510
+ );
1511
+ }
1512
+ function TableBodyMdx(props) {
1513
+ return /* @__PURE__ */ jsx15("tbody", { ...props });
1514
+ }
1515
+ function TableFooterMdx(props) {
1516
+ return /* @__PURE__ */ jsx15(
1517
+ "tfoot",
1518
+ {
1519
+ ...props,
1520
+ style: {
1521
+ background: "hsl(var(--muted, 210 20% 92%))",
1522
+ borderTop: "1px solid hsl(var(--border-color, 210 20% 85%))",
1523
+ ...props.style
1524
+ }
1525
+ }
1526
+ );
1527
+ }
1528
+ function TableRowMdx(props) {
1529
+ return /* @__PURE__ */ jsx15("tr", { ...props });
1530
+ }
1531
+ function TableHeadMdx(props) {
1532
+ return /* @__PURE__ */ jsx15(
1533
+ "th",
1534
+ {
1535
+ scope: props.scope ?? "col",
1536
+ ...props,
1537
+ style: {
1538
+ ...cellBaseStyle,
1539
+ textAlign: "left",
1540
+ color: "hsl(var(--muted-foreground, 220 15% 50%))",
1541
+ fontWeight: 600,
1542
+ ...props.style
1543
+ }
1544
+ }
1545
+ );
1546
+ }
1547
+ function TableCellMdx(props) {
1548
+ return /* @__PURE__ */ jsx15("td", { ...props, style: { ...cellBaseStyle, ...props.style } });
1549
+ }
1550
+
1551
+ // src/components/TooltipMdx.tsx
1552
+ import {
1553
+ useId as useId7,
1554
+ useLayoutEffect,
1555
+ useRef as useRef4,
1556
+ useState as useState9
1557
+ } from "react";
1558
+ import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
1559
+ var GAP = 10;
1560
+ var EDGE = 8;
1561
+ var TAIL_SIZE = 26;
1562
+ var TAIL_PROTRUSION = 12;
1563
+ var TAIL_PAD = 1;
1564
+ var TAIL_TIP_X = TAIL_SIZE / 2;
1565
+ var TAIL_W = TAIL_SIZE + TAIL_PAD * 2;
1566
+ var TAIL_H = TAIL_PROTRUSION + TAIL_PAD * 2;
1567
+ function TooltipMdx({ text, tip }) {
1568
+ const [open, setOpen] = useState9(false);
1569
+ const id = useId7();
1570
+ const triggerRef = useRef4(null);
1571
+ const bubbleRef = useRef4(null);
1572
+ const [bubbleStyle, setBubbleStyle] = useState9({});
1573
+ const [tailStyle, setTailStyle] = useState9({});
1574
+ const [below, setBelow] = useState9(true);
1575
+ useLayoutEffect(() => {
1576
+ if (!open || !triggerRef.current || !bubbleRef.current) return;
1577
+ const trigger = triggerRef.current.getBoundingClientRect();
1578
+ const bubble = bubbleRef.current.getBoundingClientRect();
1579
+ const viewW = window.innerWidth;
1580
+ const viewH = window.innerHeight;
1581
+ const flip = viewH - trigger.bottom >= trigger.top;
1582
+ const rightOverflow = trigger.left + bubble.width > viewW - EDGE;
1583
+ const left = rightOverflow ? Math.max(EDGE, viewW - bubble.width - EDGE) : Math.min(Math.max(trigger.left, EDGE), viewW - bubble.width - EDGE);
1584
+ const top = flip ? Math.min(trigger.bottom + GAP, viewH - bubble.height - EDGE) : Math.max(trigger.top - GAP - bubble.height, EDGE);
1585
+ setBubbleStyle({ left, top });
1586
+ setBelow(flip);
1587
+ const tailCenter = trigger.left + trigger.width / 2 - left;
1588
+ setTailStyle({
1589
+ left: Math.min(Math.max(tailCenter - TAIL_SIZE / 2, 2), bubble.width - TAIL_SIZE - 2),
1590
+ top: flip ? -TAIL_PROTRUSION + TAIL_PAD : bubble.height - TAIL_PAD
1591
+ });
1592
+ }, [open]);
1593
+ const content = tip ?? "";
1594
+ const show = open && Boolean(content);
1595
+ const bubbleColor = "hsl(var(--card, 0 0% 100%))";
1596
+ const borderColor = "hsl(var(--border-color, 210 20% 85%))";
1597
+ const tipY = TAIL_PROTRUSION + TAIL_PAD;
1598
+ const shoulderY = TAIL_PAD;
1599
+ const tipX = TAIL_TIP_X + TAIL_PAD;
1600
+ const midY = (shoulderY + tipY) / 2;
1601
+ const curveOut = 1.4;
1602
+ const leftMid = (TAIL_PAD + tipX) / 2;
1603
+ const rightMid = (tipX + TAIL_SIZE + TAIL_PAD) / 2;
1604
+ const curveLeft = leftMid - curveOut;
1605
+ const curveRight = rightMid + curveOut;
1606
+ const rightX = TAIL_SIZE + TAIL_PAD;
1607
+ const tailPath = below ? `M ${TAIL_PAD} ${tipY} Q ${curveLeft} ${midY} ${tipX} ${shoulderY} Q ${curveRight} ${midY} ${rightX} ${tipY} Z` : `M ${TAIL_PAD} ${shoulderY} Q ${curveLeft} ${midY} ${tipX} ${tipY} Q ${curveRight} ${midY} ${rightX} ${shoulderY} Z`;
1608
+ return /* @__PURE__ */ jsxs11(
1609
+ "span",
1610
+ {
1611
+ style: { position: "relative", display: "inline-block" },
1612
+ onMouseEnter: () => setOpen(true),
1613
+ onMouseLeave: () => setOpen(false),
1614
+ onFocus: () => setOpen(true),
1615
+ onBlur: () => setOpen(false),
1616
+ children: [
1617
+ /* @__PURE__ */ jsx16(
1618
+ "span",
1619
+ {
1620
+ ref: triggerRef,
1621
+ tabIndex: 0,
1622
+ "aria-describedby": id,
1623
+ style: {
1624
+ cursor: "help",
1625
+ color: "hsl(var(--primary, 210 81% 56%))",
1626
+ textDecorationLine: "underline",
1627
+ textDecorationStyle: "dotted",
1628
+ textDecorationColor: "hsl(var(--primary, 210 81% 56%))",
1629
+ textUnderlineOffset: "0.18em"
1630
+ },
1631
+ onKeyDown: (event) => {
1632
+ if (event.key === "Escape") {
1633
+ setOpen(false);
1634
+ }
1635
+ },
1636
+ children: text ?? "?"
1637
+ }
1638
+ ),
1639
+ show ? /* @__PURE__ */ jsxs11(
1640
+ "span",
1641
+ {
1642
+ ref: bubbleRef,
1643
+ role: "tooltip",
1644
+ style: { position: "fixed", ...bubbleStyle, zIndex: 9999 },
1645
+ children: [
1646
+ /* @__PURE__ */ jsx16(
1647
+ "svg",
1648
+ {
1649
+ "aria-hidden": "true",
1650
+ width: TAIL_SIZE,
1651
+ height: TAIL_PROTRUSION,
1652
+ viewBox: `0 0 ${TAIL_W} ${TAIL_H}`,
1653
+ style: { position: "absolute", ...tailStyle, display: "block" },
1654
+ children: /* @__PURE__ */ jsx16("path", { d: tailPath, fill: bubbleColor, stroke: borderColor, strokeWidth: 1 })
1655
+ }
1656
+ ),
1657
+ /* @__PURE__ */ jsx16(
1658
+ "span",
1659
+ {
1660
+ style: {
1661
+ position: "relative",
1662
+ display: "inline-flex",
1663
+ alignItems: "center",
1664
+ maxWidth: "min(70vw, 22rem)",
1665
+ background: bubbleColor,
1666
+ color: "hsl(var(--foreground, 220 30% 15%))",
1667
+ border: `1px solid ${borderColor}`,
1668
+ borderRadius: 16,
1669
+ boxShadow: "0 8px 28px -6px rgb(0 0 0 / 0.18), 0 2px 8px -2px rgb(0 0 0 / 0.08)",
1670
+ padding: "0.5rem 0.9rem",
1671
+ fontSize: "0.8125rem",
1672
+ lineHeight: 1.5,
1673
+ overflowWrap: "anywhere"
1674
+ },
1675
+ children: content
1676
+ }
1677
+ )
1678
+ ]
1679
+ }
1680
+ ) : null
1681
+ ]
1682
+ }
1683
+ );
1684
+ }
1685
+
1686
+ // src/components/YoutubeMdx.tsx
1687
+ import { jsx as jsx17 } from "react/jsx-runtime";
1688
+ function YoutubeMdx({ videoId, title = "YouTube video" }) {
1689
+ const src = `https://www.youtube-nocookie.com/embed/${videoId}`;
1690
+ return /* @__PURE__ */ jsx17(
1691
+ "div",
1692
+ {
1693
+ style: {
1694
+ position: "relative",
1695
+ width: "100%",
1696
+ paddingBottom: "56.25%",
1697
+ height: 0,
1698
+ overflow: "hidden",
1699
+ background: "#000",
1700
+ borderRadius: 8,
1701
+ boxShadow: "0 4px 20px rgba(0, 0, 0, 0.2)",
1702
+ margin: "1rem 0"
1703
+ },
1704
+ children: /* @__PURE__ */ jsx17(
1705
+ "iframe",
1706
+ {
1707
+ src,
1708
+ title,
1709
+ allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
1710
+ allowFullScreen: true,
1711
+ style: {
1712
+ position: "absolute",
1713
+ top: 0,
1714
+ left: 0,
1715
+ width: "100%",
1716
+ height: "100%",
1717
+ border: "none",
1718
+ borderRadius: 8
1719
+ }
1720
+ }
1721
+ )
1722
+ }
1723
+ );
1724
+ }
1725
+
1726
+ // src/components/MermaidMdx.tsx
1727
+ import {
1728
+ useId as useId8,
1729
+ useRef as useRef5,
1730
+ useEffect as useEffect4,
1731
+ useState as useState10
1732
+ } from "react";
1733
+ import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
1734
+ var mermaidPromise = null;
1735
+ var hintSeen = false;
1736
+ function getTheme() {
1737
+ return document.documentElement.classList.contains("dark") ? "dark" : "default";
1738
+ }
1739
+ var PAN_STEP = 50;
1740
+ var ZOOM_STEP = 1.2;
1741
+ var MIN_SCALE = 0.4;
1742
+ var MAX_SCALE = 4;
1743
+ var clampScale = (scale) => Math.min(MAX_SCALE, Math.max(MIN_SCALE, scale));
1744
+ var controlButtonStyle = {
1745
+ display: "flex",
1746
+ alignItems: "center",
1747
+ justifyContent: "center",
1748
+ width: 28,
1749
+ height: 28,
1750
+ padding: 0,
1751
+ border: "1px solid rgba(127,127,127,0.35)",
1752
+ borderRadius: 6,
1753
+ background: "rgba(127,127,127,0.12)",
1754
+ color: "currentcolor",
1755
+ cursor: "pointer"
1756
+ };
1757
+ var topBarButtonStyle = {
1758
+ display: "flex",
1759
+ alignItems: "center",
1760
+ justifyContent: "center",
1761
+ gap: 6,
1762
+ padding: "6px 10px",
1763
+ border: "1px solid rgba(127,127,127,0.25)",
1764
+ borderRadius: 8,
1765
+ background: "rgba(127,127,127,0.12)",
1766
+ backdropFilter: "blur(8px)",
1767
+ color: "currentcolor",
1768
+ fontSize: 12,
1769
+ lineHeight: 1.4,
1770
+ cursor: "pointer"
1771
+ };
1772
+ function ControlButton({
1773
+ label,
1774
+ onClick,
1775
+ cell,
1776
+ children
1777
+ }) {
1778
+ return /* @__PURE__ */ jsx18(
1779
+ "button",
1780
+ {
1781
+ type: "button",
1782
+ "aria-label": label,
1783
+ title: label,
1784
+ onClick,
1785
+ style: { ...controlButtonStyle, gridColumn: cell.col, gridRow: cell.row },
1786
+ children: /* @__PURE__ */ jsx18(
1787
+ "svg",
1788
+ {
1789
+ width: "14",
1790
+ height: "14",
1791
+ viewBox: "0 0 24 24",
1792
+ fill: "none",
1793
+ stroke: "currentColor",
1794
+ strokeWidth: "2",
1795
+ strokeLinecap: "round",
1796
+ strokeLinejoin: "round",
1797
+ "aria-hidden": "true",
1798
+ children
1799
+ }
1800
+ )
1801
+ }
1802
+ );
1803
+ }
1804
+ function MermaidMdx({ chart, id, className }) {
1805
+ const generatedId = useId8();
1806
+ const domId = id ?? `mermaid-${generatedId.replace(/[:.]/g, "-")}`;
1807
+ const ref = useRef5(null);
1808
+ const chartRef = useRef5(chart);
1809
+ const mermaidRef = useRef5(null);
1810
+ const [error, setError] = useState10(null);
1811
+ const [rendered, setRendered] = useState10(false);
1812
+ const [view, setView] = useState10({ x: 0, y: 0, scale: 1 });
1813
+ const [fullscreen, setFullscreen] = useState10(false);
1814
+ const [isPanning, setIsPanning] = useState10(false);
1815
+ const [showHelp, setShowHelp] = useState10(false);
1816
+ const dragRef = useRef5({ dragging: false, lastX: 0, lastY: 0 });
1817
+ const viewportRef = useRef5(null);
1818
+ chartRef.current = chart;
1819
+ useEffect4(() => {
1820
+ if (typeof window === "undefined") return;
1821
+ if (!chart) return;
1822
+ let cancelled = false;
1823
+ let lazyObserver = null;
1824
+ let syncObserver = null;
1825
+ let themeTimer = null;
1826
+ let lastTheme = getTheme();
1827
+ let runChain = Promise.resolve();
1828
+ function enqueueRun(run) {
1829
+ const next = runChain.then(() => {
1830
+ if (cancelled || !ref.current?.isConnected) return;
1831
+ return run();
1832
+ });
1833
+ runChain = next.catch(() => {
1834
+ });
1835
+ return next;
1836
+ }
1837
+ async function init() {
1838
+ try {
1839
+ if (!mermaidPromise) {
1840
+ mermaidPromise = import("mermaid");
1841
+ }
1842
+ const mod = await mermaidPromise;
1843
+ const mermaid = mermaidRef.current = mod.default;
1844
+ mermaid.initialize({ startOnLoad: false, theme: getTheme() });
1845
+ try {
1846
+ await mermaid.parse(chart);
1847
+ } catch {
1848
+ if (!cancelled) setError("Invalid Mermaid syntax");
1849
+ return;
1850
+ }
1851
+ if (cancelled || !ref.current) return;
1852
+ lazyObserver = new IntersectionObserver(
1853
+ ([entry]) => {
1854
+ if (!entry.isIntersecting || cancelled || !ref.current) return;
1855
+ enqueueRun(() => {
1856
+ const node = ref.current;
1857
+ if (!node) return Promise.resolve();
1858
+ return mermaid.run({ nodes: [node] });
1859
+ }).then(() => {
1860
+ if (!cancelled) setRendered(true);
1861
+ }).catch(() => {
1862
+ if (!cancelled) setError("Failed to render diagram");
1863
+ });
1864
+ lazyObserver?.disconnect();
1865
+ },
1866
+ { rootMargin: "200px" }
1867
+ );
1868
+ lazyObserver.observe(ref.current);
1869
+ } catch {
1870
+ if (!cancelled) setError("Failed to load Mermaid renderer");
1871
+ }
1872
+ }
1873
+ init();
1874
+ syncObserver = new MutationObserver(() => {
1875
+ if (themeTimer) clearTimeout(themeTimer);
1876
+ themeTimer = setTimeout(() => {
1877
+ if (cancelled || !mermaidRef.current || !ref.current) return;
1878
+ if (getTheme() === lastTheme) return;
1879
+ enqueueRun(() => {
1880
+ const mermaid = mermaidRef.current;
1881
+ const node = ref.current;
1882
+ if (!mermaid || !node) return Promise.resolve();
1883
+ lastTheme = getTheme();
1884
+ node.textContent = chartRef.current;
1885
+ node.removeAttribute("data-processed");
1886
+ mermaid.initialize({ startOnLoad: false, theme: lastTheme });
1887
+ return mermaid.run({ nodes: [node] });
1888
+ }).catch((e) => {
1889
+ console.warn("[mermaid] diagram render error:", e);
1890
+ });
1891
+ }, 200);
1892
+ });
1893
+ syncObserver.observe(document.documentElement, {
1894
+ attributes: true,
1895
+ attributeFilter: ["class"]
1896
+ });
1897
+ return () => {
1898
+ cancelled = true;
1899
+ if (themeTimer) clearTimeout(themeTimer);
1900
+ lazyObserver?.disconnect();
1901
+ syncObserver?.disconnect();
1902
+ };
1903
+ }, []);
1904
+ const pan = (dx, dy) => setView((v) => ({ ...v, x: v.x + dx, y: v.y + dy }));
1905
+ const zoom = (factor) => setView((v) => ({ ...v, scale: clampScale(v.scale * factor) }));
1906
+ const resetView = () => setView({ x: 0, y: 0, scale: 1 });
1907
+ useEffect4(() => {
1908
+ if (!fullscreen) return;
1909
+ const previous = document.body.style.overflow;
1910
+ document.body.style.overflow = "hidden";
1911
+ return () => {
1912
+ document.body.style.overflow = previous;
1913
+ };
1914
+ }, [fullscreen]);
1915
+ useEffect4(() => {
1916
+ if (fullscreen) {
1917
+ if (!hintSeen) {
1918
+ hintSeen = true;
1919
+ setShowHelp(true);
1920
+ }
1921
+ } else {
1922
+ setShowHelp(false);
1923
+ }
1924
+ }, [fullscreen]);
1925
+ useEffect4(() => {
1926
+ if (!fullscreen || !rendered) return;
1927
+ const el = viewportRef.current;
1928
+ if (!el) return;
1929
+ const blockWheel = (e) => e.preventDefault();
1930
+ const blockTouchStart = (e) => {
1931
+ const target = e.target;
1932
+ if (target?.closest("button")) return;
1933
+ e.preventDefault();
1934
+ };
1935
+ el.addEventListener("wheel", blockWheel, { passive: false });
1936
+ el.addEventListener("touchstart", blockTouchStart, { passive: false });
1937
+ return () => {
1938
+ el.removeEventListener("wheel", blockWheel);
1939
+ el.removeEventListener("touchstart", blockTouchStart);
1940
+ };
1941
+ }, [fullscreen, rendered]);
1942
+ useEffect4(() => {
1943
+ if (!fullscreen) return;
1944
+ const onKeyDown = (e) => {
1945
+ if (e.ctrlKey || e.metaKey || e.altKey) return;
1946
+ const actions = {
1947
+ ArrowUp: () => pan(0, -PAN_STEP),
1948
+ ArrowDown: () => pan(0, PAN_STEP),
1949
+ ArrowLeft: () => pan(-PAN_STEP, 0),
1950
+ ArrowRight: () => pan(PAN_STEP, 0),
1951
+ "+": () => zoom(ZOOM_STEP),
1952
+ "=": () => zoom(ZOOM_STEP),
1953
+ "-": () => zoom(1 / ZOOM_STEP),
1954
+ "0": resetView,
1955
+ Escape: () => setFullscreen(false)
1956
+ };
1957
+ const action = actions[e.key];
1958
+ if (!action) return;
1959
+ e.preventDefault();
1960
+ action();
1961
+ };
1962
+ window.addEventListener("keydown", onKeyDown);
1963
+ return () => window.removeEventListener("keydown", onKeyDown);
1964
+ }, [fullscreen]);
1965
+ if (!chart) return null;
1966
+ const controlsActive = rendered && fullscreen;
1967
+ const showFullscreenBtn = rendered;
1968
+ const handleKeyDown = (e) => {
1969
+ if (!showFullscreenBtn || controlsActive || e.ctrlKey || e.metaKey || e.altKey) return;
1970
+ if (e.key === "Enter") {
1971
+ e.preventDefault();
1972
+ setFullscreen(true);
1973
+ }
1974
+ };
1975
+ if (error) {
1976
+ return /* @__PURE__ */ jsxs12(
1977
+ "div",
1978
+ {
1979
+ className,
1980
+ style: {
1981
+ border: "1px solid hsl(var(--destructive, 0 85% 60%))",
1982
+ borderRadius: 8,
1983
+ padding: "0.75rem 1rem",
1984
+ margin: "1em 0"
1985
+ },
1986
+ children: [
1987
+ /* @__PURE__ */ jsx18(
1988
+ "p",
1989
+ {
1990
+ style: {
1991
+ color: "hsl(var(--destructive, 0 85% 60%))",
1992
+ fontWeight: 600,
1993
+ marginBottom: 8,
1994
+ fontSize: "0.875rem"
1995
+ },
1996
+ children: "\u26A0\uFE0F Diagram rendering error"
1997
+ }
1998
+ ),
1999
+ /* @__PURE__ */ jsx18(
2000
+ "pre",
2001
+ {
2002
+ style: {
2003
+ fontSize: "0.875rem",
2004
+ whiteSpace: "pre-wrap",
2005
+ margin: 0
2006
+ },
2007
+ children: chart
2008
+ }
2009
+ )
2010
+ ]
2011
+ }
2012
+ );
2013
+ }
2014
+ return /* @__PURE__ */ jsxs12(
2015
+ "div",
2016
+ {
2017
+ ref: viewportRef,
2018
+ className: "max-w-full",
2019
+ tabIndex: showFullscreenBtn ? 0 : void 0,
2020
+ role: showFullscreenBtn ? "group" : void 0,
2021
+ "aria-label": showFullscreenBtn ? fullscreen ? "Mermaid diagram. Drag or use arrow keys to pan, + and - to zoom, click the percentage to reset, Esc to exit." : "Mermaid diagram. Press Enter to open fullscreen view." : void 0,
2022
+ onKeyDown: handleKeyDown,
2023
+ onWheel: controlsActive ? (e) => zoom(e.deltaY > 0 ? 1 / ZOOM_STEP : ZOOM_STEP) : void 0,
2024
+ onTouchStart: controlsActive ? (e) => {
2025
+ const t = e.touches[0];
2026
+ dragRef.current = { dragging: true, lastX: t.clientX, lastY: t.clientY };
2027
+ setIsPanning(true);
2028
+ } : void 0,
2029
+ onTouchMove: controlsActive ? (e) => {
2030
+ const d = dragRef.current;
2031
+ if (!d.dragging) return;
2032
+ const t = e.touches[0];
2033
+ pan(t.clientX - d.lastX, t.clientY - d.lastY);
2034
+ dragRef.current.lastX = t.clientX;
2035
+ dragRef.current.lastY = t.clientY;
2036
+ } : void 0,
2037
+ onTouchEnd: controlsActive ? () => {
2038
+ dragRef.current.dragging = false;
2039
+ setIsPanning(false);
2040
+ } : void 0,
2041
+ onTouchCancel: controlsActive ? () => {
2042
+ dragRef.current.dragging = false;
2043
+ setIsPanning(false);
2044
+ } : void 0,
2045
+ onMouseDown: controlsActive ? (e) => {
2046
+ dragRef.current = { dragging: true, lastX: e.clientX, lastY: e.clientY };
2047
+ setIsPanning(true);
2048
+ } : void 0,
2049
+ onMouseMove: controlsActive ? (e) => {
2050
+ const d = dragRef.current;
2051
+ if (!d.dragging) return;
2052
+ pan(e.clientX - d.lastX, e.clientY - d.lastY);
2053
+ dragRef.current.lastX = e.clientX;
2054
+ dragRef.current.lastY = e.clientY;
2055
+ } : void 0,
2056
+ onMouseUp: controlsActive ? () => {
2057
+ dragRef.current.dragging = false;
2058
+ setIsPanning(false);
2059
+ } : void 0,
2060
+ onMouseLeave: controlsActive ? () => {
2061
+ dragRef.current.dragging = false;
2062
+ setIsPanning(false);
2063
+ } : void 0,
2064
+ style: {
2065
+ ...fullscreen ? {
2066
+ position: "fixed",
2067
+ inset: 0,
2068
+ zIndex: 50,
2069
+ background: "hsl(var(--background, 0 0% 100%))",
2070
+ display: "flex",
2071
+ alignItems: "center",
2072
+ justifyContent: "center"
2073
+ } : { position: "relative" },
2074
+ ...controlsActive ? {
2075
+ overflow: "hidden",
2076
+ touchAction: "none",
2077
+ userSelect: "none",
2078
+ cursor: isPanning ? "grabbing" : "grab"
2079
+ } : { overflow: "auto" }
2080
+ },
2081
+ children: [
2082
+ /* @__PURE__ */ jsx18(
2083
+ "div",
2084
+ {
2085
+ style: controlsActive ? {
2086
+ transform: `translate(${view.x}px, ${view.y}px) scale(${view.scale})`,
2087
+ transformOrigin: "center center",
2088
+ transition: isPanning ? "none" : "transform 150ms ease"
2089
+ } : void 0,
2090
+ children: /* @__PURE__ */ jsx18(
2091
+ "pre",
2092
+ {
2093
+ ref,
2094
+ id: domId,
2095
+ className: `mermaid not-prose${className ? ` ${className}` : ""}`,
2096
+ style: { margin: "1em auto", width: "fit-content", maxWidth: "100%" },
2097
+ children: chart
2098
+ }
2099
+ )
2100
+ }
2101
+ ),
2102
+ showFullscreenBtn ? controlsActive ? /* @__PURE__ */ jsxs12(
2103
+ "div",
2104
+ {
2105
+ role: "group",
2106
+ "aria-label": "Pan and zoom controls",
2107
+ style: { position: "absolute", inset: 0, pointerEvents: "none" },
2108
+ children: [
2109
+ /* @__PURE__ */ jsxs12(
2110
+ "div",
2111
+ {
2112
+ style: {
2113
+ position: "absolute",
2114
+ top: 12,
2115
+ right: 12,
2116
+ display: "flex",
2117
+ flexDirection: "column",
2118
+ alignItems: "flex-end",
2119
+ gap: 8,
2120
+ pointerEvents: "auto"
2121
+ },
2122
+ children: [
2123
+ /* @__PURE__ */ jsxs12("div", { style: { display: "flex", gap: 8 }, children: [
2124
+ /* @__PURE__ */ jsxs12(
2125
+ "button",
2126
+ {
2127
+ type: "button",
2128
+ "aria-label": "Exit full screen",
2129
+ onClick: () => setFullscreen(false),
2130
+ style: topBarButtonStyle,
2131
+ children: [
2132
+ /* @__PURE__ */ jsx18(
2133
+ "kbd",
2134
+ {
2135
+ style: {
2136
+ padding: "1px 5px",
2137
+ borderRadius: 4,
2138
+ border: "1px solid rgba(127,127,127,0.4)",
2139
+ background: "rgba(127,127,127,0.15)",
2140
+ fontSize: 11,
2141
+ fontFamily: "inherit",
2142
+ lineHeight: 1.4
2143
+ },
2144
+ children: "Esc"
2145
+ }
2146
+ ),
2147
+ /* @__PURE__ */ jsx18("span", { children: "Exit fullscreen" })
2148
+ ]
2149
+ }
2150
+ ),
2151
+ /* @__PURE__ */ jsx18(
2152
+ "button",
2153
+ {
2154
+ type: "button",
2155
+ "aria-label": "Toggle help",
2156
+ "aria-expanded": showHelp,
2157
+ title: "Canvas controls & keyboard shortcuts",
2158
+ onClick: () => setShowHelp((v) => !v),
2159
+ style: { ...topBarButtonStyle, width: 28, padding: 0, fontSize: 14 },
2160
+ children: showHelp ? "\xD7" : "?"
2161
+ }
2162
+ )
2163
+ ] }),
2164
+ showHelp ? /* @__PURE__ */ jsxs12(
2165
+ "div",
2166
+ {
2167
+ role: "status",
2168
+ style: {
2169
+ padding: "10px 12px",
2170
+ borderRadius: 10,
2171
+ border: "1px solid rgba(127,127,127,0.25)",
2172
+ background: "rgba(127,127,127,0.12)",
2173
+ backdropFilter: "blur(8px)",
2174
+ color: "currentcolor",
2175
+ fontSize: 12,
2176
+ lineHeight: 1.7,
2177
+ maxWidth: 280
2178
+ },
2179
+ children: [
2180
+ /* @__PURE__ */ jsx18("p", { style: { margin: 0, fontWeight: 600 }, children: "Canvas controls" }),
2181
+ /* @__PURE__ */ jsx18("p", { style: { margin: 0 }, children: "Move \u2014 hold scroll wheel or tap & drag" }),
2182
+ /* @__PURE__ */ jsx18("p", { style: { margin: 0 }, children: "Zoom \u2014 + / \u2212 keys or scroll" }),
2183
+ /* @__PURE__ */ jsx18("p", { style: { margin: 0 }, children: "Reset \u2014 0 or click the zoom %" }),
2184
+ /* @__PURE__ */ jsx18("p", { style: { margin: 0 }, children: "Exit \u2014 Esc" })
2185
+ ]
2186
+ }
2187
+ ) : null
2188
+ ]
2189
+ }
2190
+ ),
2191
+ /* @__PURE__ */ jsxs12(
2192
+ "div",
2193
+ {
2194
+ style: {
2195
+ position: "absolute",
2196
+ bottom: 16,
2197
+ left: "50%",
2198
+ transform: "translateX(-50%)",
2199
+ display: "flex",
2200
+ alignItems: "center",
2201
+ gap: 2,
2202
+ padding: 4,
2203
+ borderRadius: 10,
2204
+ border: "1px solid rgba(127,127,127,0.25)",
2205
+ background: "rgba(127,127,127,0.12)",
2206
+ backdropFilter: "blur(8px)",
2207
+ pointerEvents: "auto"
2208
+ },
2209
+ children: [
2210
+ /* @__PURE__ */ jsxs12(
2211
+ ControlButton,
2212
+ {
2213
+ label: "Zoom out",
2214
+ onClick: () => zoom(1 / ZOOM_STEP),
2215
+ cell: { col: 1, row: 1 },
2216
+ children: [
2217
+ /* @__PURE__ */ jsx18("circle", { cx: "11", cy: "11", r: "7" }),
2218
+ /* @__PURE__ */ jsx18("path", { d: "m21 21-4-4M8 11h6" })
2219
+ ]
2220
+ }
2221
+ ),
2222
+ /* @__PURE__ */ jsxs12(
2223
+ "button",
2224
+ {
2225
+ type: "button",
2226
+ "aria-label": "Reset view",
2227
+ title: "Reset zoom to 100%",
2228
+ onClick: resetView,
2229
+ style: {
2230
+ minWidth: 56,
2231
+ height: 28,
2232
+ padding: "0 8px",
2233
+ border: "none",
2234
+ borderRadius: 6,
2235
+ background: "transparent",
2236
+ color: "currentcolor",
2237
+ fontSize: 12,
2238
+ fontVariantNumeric: "tabular-nums",
2239
+ cursor: "pointer"
2240
+ },
2241
+ children: [
2242
+ Math.round(view.scale * 100),
2243
+ "%"
2244
+ ]
2245
+ }
2246
+ ),
2247
+ /* @__PURE__ */ jsxs12(
2248
+ ControlButton,
2249
+ {
2250
+ label: "Zoom in",
2251
+ onClick: () => zoom(ZOOM_STEP),
2252
+ cell: { col: 3, row: 1 },
2253
+ children: [
2254
+ /* @__PURE__ */ jsx18("circle", { cx: "11", cy: "11", r: "7" }),
2255
+ /* @__PURE__ */ jsx18("path", { d: "m21 21-4-4M8 11h6M11 8v6" })
2256
+ ]
2257
+ }
2258
+ )
2259
+ ]
2260
+ }
2261
+ )
2262
+ ]
2263
+ }
2264
+ ) : /* @__PURE__ */ jsx18("div", { style: { position: "absolute", bottom: 8, right: 8 }, children: /* @__PURE__ */ jsx18(
2265
+ ControlButton,
2266
+ {
2267
+ label: "Enter full screen",
2268
+ onClick: () => setFullscreen(true),
2269
+ cell: { col: 1, row: 1 },
2270
+ children: /* @__PURE__ */ jsx18("path", { d: "M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3M16 21h3a2 2 0 0 0 2-2v-3" })
2271
+ }
2272
+ ) }) : null
2273
+ ]
2274
+ }
2275
+ );
2276
+ }
2277
+
2278
+ // src/registry/index.ts
2279
+ function callout(type) {
2280
+ return (props) => createElement(Callout, { type, ...props });
2281
+ }
2282
+ function createMdxComponents(customComponents = {}) {
2283
+ return {
2284
+ Tabs: TabsMdx,
2285
+ Tab: TabMdx,
2286
+ table: TableMdx,
2287
+ thead: TableHeaderMdx,
2288
+ tbody: TableBodyMdx,
2289
+ tfoot: TableFooterMdx,
2290
+ tr: TableRowMdx,
2291
+ th: TableHeadMdx,
2292
+ td: TableCellMdx,
2293
+ pre: CodeBlock,
2294
+ a: LinkMdx,
2295
+ Link: LinkMdx,
2296
+ Card: CardMdx,
2297
+ Tip: callout("tip"),
2298
+ Info: callout("info"),
2299
+ Danger: callout("danger"),
2300
+ Warning: callout("warning"),
2301
+ Success: callout("success"),
2302
+ Steps: StepsMdx,
2303
+ Step: StepMdx,
2304
+ Accordion: AccordionMdx,
2305
+ Accordions: AccordionsMdx,
2306
+ Cards: CardsMdx,
2307
+ Tree: TreeMdx,
2308
+ Image: ImageMdx,
2309
+ img: ImageMdx,
2310
+ Youtube: YoutubeMdx,
2311
+ Tooltip: TooltipMdx,
2312
+ Mermaid: MermaidMdx,
2313
+ ...customComponents
2314
+ };
2315
+ }
2316
+ export {
2317
+ createMdxComponents
2318
+ };
2319
+ //# sourceMappingURL=index.js.map