@burojs/ui-shadcn 0.2.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 (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +65 -0
  3. package/dist/chunk-ABUQPOOE.js +11 -0
  4. package/dist/chunk-ABUQPOOE.js.map +1 -0
  5. package/dist/chunk-DBGGWBFG.cjs +199 -0
  6. package/dist/chunk-DBGGWBFG.cjs.map +1 -0
  7. package/dist/chunk-FMX4GLCQ.js +6 -0
  8. package/dist/chunk-FMX4GLCQ.js.map +1 -0
  9. package/dist/chunk-UC57Z243.js +143 -0
  10. package/dist/chunk-UC57Z243.js.map +1 -0
  11. package/dist/chunk-UPGPO75B.js +164 -0
  12. package/dist/chunk-UPGPO75B.js.map +1 -0
  13. package/dist/chunk-VXJAXJ3U.cjs +8 -0
  14. package/dist/chunk-VXJAXJ3U.cjs.map +1 -0
  15. package/dist/chunk-W7AEYE7A.cjs +13 -0
  16. package/dist/chunk-W7AEYE7A.cjs.map +1 -0
  17. package/dist/chunk-YF5IW525.cjs +155 -0
  18. package/dist/chunk-YF5IW525.cjs.map +1 -0
  19. package/dist/index.cjs +17752 -0
  20. package/dist/index.cjs.map +1 -0
  21. package/dist/index.d.cts +6091 -0
  22. package/dist/index.d.ts +6091 -0
  23. package/dist/index.js +17369 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/layout/theme-storage-key.cjs +12 -0
  26. package/dist/layout/theme-storage-key.cjs.map +1 -0
  27. package/dist/layout/theme-storage-key.d.cts +23 -0
  28. package/dist/layout/theme-storage-key.d.ts +23 -0
  29. package/dist/layout/theme-storage-key.js +3 -0
  30. package/dist/layout/theme-storage-key.js.map +1 -0
  31. package/dist/markdown/index.cjs +550 -0
  32. package/dist/markdown/index.cjs.map +1 -0
  33. package/dist/markdown/index.d.cts +221 -0
  34. package/dist/markdown/index.d.ts +221 -0
  35. package/dist/markdown/index.js +492 -0
  36. package/dist/markdown/index.js.map +1 -0
  37. package/dist/rich-text-6V7WZ3WV.cjs +518 -0
  38. package/dist/rich-text-6V7WZ3WV.cjs.map +1 -0
  39. package/dist/rich-text-N2FZEJJV.js +491 -0
  40. package/dist/rich-text-N2FZEJJV.js.map +1 -0
  41. package/dist/styles.css +794 -0
  42. package/dist/theme/theme-css.cjs +105 -0
  43. package/dist/theme/theme-css.cjs.map +1 -0
  44. package/dist/theme/theme-css.d.cts +49 -0
  45. package/dist/theme/theme-css.d.ts +49 -0
  46. package/dist/theme/theme-css.js +102 -0
  47. package/dist/theme/theme-css.js.map +1 -0
  48. package/dist/theme/theme-storage.cjs +51 -0
  49. package/dist/theme/theme-storage.cjs.map +1 -0
  50. package/dist/theme/theme-storage.d.cts +75 -0
  51. package/dist/theme/theme-storage.d.ts +75 -0
  52. package/dist/theme/theme-storage.js +45 -0
  53. package/dist/theme/theme-storage.js.map +1 -0
  54. package/package.json +165 -0
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ var chunkVXJAXJ3U_cjs = require('../chunk-VXJAXJ3U.cjs');
4
+
5
+
6
+
7
+ Object.defineProperty(exports, "THEME_STORAGE_KEY", {
8
+ enumerable: true,
9
+ get: function () { return chunkVXJAXJ3U_cjs.THEME_STORAGE_KEY; }
10
+ });
11
+ //# sourceMappingURL=theme-storage-key.cjs.map
12
+ //# sourceMappingURL=theme-storage-key.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"theme-storage-key.cjs"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The ONE localStorage key every theme-aware place in the app must agree on.
3
+ * Exported so nothing re-types the string — two files agreeing on a literal
4
+ * is how they drift.
5
+ *
6
+ * Deliberately its own module: no `'use client'`, no React import, nothing
7
+ * that would pull in `use-theme.ts`'s hook (or the rest of the kit's client
8
+ * component tree). That matters because the ONE consumer that needs this
9
+ * constant outside a hook is the bootstrap `<script>` in the playground's
10
+ * root `app/layout.tsx` — a Server Component, which cannot itself carry
11
+ * `'use client'`. Importing from the package's main entry (`@burojs/ui-shadcn`)
12
+ * there fails Next's server/client boundary check, because that entry
13
+ * bundles the whole kit — including many components that call hooks —
14
+ * into one file without a top-level `'use client'` directive (tsup drops it
15
+ * when bundling multiple client modules together). Importing this file
16
+ * directly via the `@burojs/ui-shadcn/theme-storage-key` subpath sidesteps
17
+ * that: it has no hooks, so a Server Component can import the real string
18
+ * value at build/SSR time. `use-theme.ts` re-exports the same constant for
19
+ * everyone else, so there is exactly one literal in the whole codebase.
20
+ */
21
+ declare const THEME_STORAGE_KEY = "buro-theme";
22
+
23
+ export { THEME_STORAGE_KEY };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The ONE localStorage key every theme-aware place in the app must agree on.
3
+ * Exported so nothing re-types the string — two files agreeing on a literal
4
+ * is how they drift.
5
+ *
6
+ * Deliberately its own module: no `'use client'`, no React import, nothing
7
+ * that would pull in `use-theme.ts`'s hook (or the rest of the kit's client
8
+ * component tree). That matters because the ONE consumer that needs this
9
+ * constant outside a hook is the bootstrap `<script>` in the playground's
10
+ * root `app/layout.tsx` — a Server Component, which cannot itself carry
11
+ * `'use client'`. Importing from the package's main entry (`@burojs/ui-shadcn`)
12
+ * there fails Next's server/client boundary check, because that entry
13
+ * bundles the whole kit — including many components that call hooks —
14
+ * into one file without a top-level `'use client'` directive (tsup drops it
15
+ * when bundling multiple client modules together). Importing this file
16
+ * directly via the `@burojs/ui-shadcn/theme-storage-key` subpath sidesteps
17
+ * that: it has no hooks, so a Server Component can import the real string
18
+ * value at build/SSR time. `use-theme.ts` re-exports the same constant for
19
+ * everyone else, so there is exactly one literal in the whole codebase.
20
+ */
21
+ declare const THEME_STORAGE_KEY = "buro-theme";
22
+
23
+ export { THEME_STORAGE_KEY };
@@ -0,0 +1,3 @@
1
+ export { THEME_STORAGE_KEY } from '../chunk-FMX4GLCQ.js';
2
+ //# sourceMappingURL=theme-storage-key.js.map
3
+ //# sourceMappingURL=theme-storage-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"theme-storage-key.js"}
@@ -0,0 +1,550 @@
1
+ 'use strict';
2
+
3
+ var chunkYF5IW525_cjs = require('../chunk-YF5IW525.cjs');
4
+ require('../chunk-VXJAXJ3U.cjs');
5
+ var chunkW7AEYE7A_cjs = require('../chunk-W7AEYE7A.cjs');
6
+ var Markdown = require('react-markdown');
7
+ var rehypeSanitize = require('rehype-sanitize');
8
+ var rehypeSlug = require('rehype-slug');
9
+ var remarkDirective = require('remark-directive');
10
+ var remarkGfm = require('remark-gfm');
11
+ var react = require('@burojs/react');
12
+ var iconsReact = require('@tabler/icons-react');
13
+ var react$1 = require('react');
14
+ var jsxRuntime = require('react/jsx-runtime');
15
+ var unistUtilVisit = require('unist-util-visit');
16
+ var AccordionPrimitive = require('@radix-ui/react-accordion');
17
+
18
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
19
+
20
+ function _interopNamespace(e) {
21
+ if (e && e.__esModule) return e;
22
+ var n = Object.create(null);
23
+ if (e) {
24
+ Object.keys(e).forEach(function (k) {
25
+ if (k !== 'default') {
26
+ var d = Object.getOwnPropertyDescriptor(e, k);
27
+ Object.defineProperty(n, k, d.get ? d : {
28
+ enumerable: true,
29
+ get: function () { return e[k]; }
30
+ });
31
+ }
32
+ });
33
+ }
34
+ n.default = e;
35
+ return Object.freeze(n);
36
+ }
37
+
38
+ var Markdown__default = /*#__PURE__*/_interopDefault(Markdown);
39
+ var rehypeSanitize__default = /*#__PURE__*/_interopDefault(rehypeSanitize);
40
+ var rehypeSlug__default = /*#__PURE__*/_interopDefault(rehypeSlug);
41
+ var remarkDirective__default = /*#__PURE__*/_interopDefault(remarkDirective);
42
+ var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
43
+ var AccordionPrimitive__namespace = /*#__PURE__*/_interopNamespace(AccordionPrimitive);
44
+
45
+ function DocAnchorLink({
46
+ slug,
47
+ stopPropagation,
48
+ className
49
+ }) {
50
+ const t = react.useOptionalT();
51
+ const [copied, setCopied] = react$1.useState(false);
52
+ const timer = react$1.useRef(null);
53
+ react$1.useEffect(
54
+ () => () => {
55
+ if (timer.current) clearTimeout(timer.current);
56
+ },
57
+ []
58
+ );
59
+ const onClick = react$1.useCallback(
60
+ (event) => {
61
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
62
+ if (stopPropagation) event.stopPropagation();
63
+ void navigator.clipboard?.writeText(chunkYF5IW525_cjs.docShareUrl(slug)).then(() => {
64
+ setCopied(true);
65
+ if (timer.current) clearTimeout(timer.current);
66
+ timer.current = setTimeout(() => setCopied(false), 1600);
67
+ }).catch(() => {
68
+ });
69
+ },
70
+ [slug, stopPropagation]
71
+ );
72
+ const label = copied ? t("docPanel.linkCopied", { defaultValue: "Link copied" }) : t("docPanel.copyLink", { defaultValue: "Copy link to this section" });
73
+ return /* @__PURE__ */ jsxRuntime.jsxs(
74
+ "a",
75
+ {
76
+ href: `#${slug}`,
77
+ onClick,
78
+ title: label,
79
+ "aria-label": label,
80
+ "data-testid": `doc-anchor:${slug}`,
81
+ "data-copied": copied ? "true" : void 0,
82
+ className: chunkW7AEYE7A_cjs.cn(
83
+ "ml-2 inline-flex shrink-0 items-center rounded-sm p-0.5 align-middle no-underline",
84
+ "text-muted-foreground transition-opacity hover:text-foreground",
85
+ // Hidden until the reader is at this heading — a permanent « # » beside
86
+ // every heading reads as scaffolding. `focus-visible` keeps it
87
+ // reachable by keyboard, where there is no hover to reveal it.
88
+ "opacity-0 group-hover/anchor:opacity-100 focus-visible:opacity-100",
89
+ "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
90
+ copied && "text-primary opacity-100",
91
+ className
92
+ ),
93
+ children: [
94
+ copied ? /* @__PURE__ */ jsxRuntime.jsx(iconsReact.IconCheck, { className: "size-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsxRuntime.jsx(iconsReact.IconLink, { className: "size-3.5", "aria-hidden": "true" }),
95
+ /* @__PURE__ */ jsxRuntime.jsx("span", { role: "status", className: "sr-only", children: copied ? t("docPanel.linkCopied", { defaultValue: "Link copied" }) : "" })
96
+ ]
97
+ }
98
+ );
99
+ }
100
+
101
+ // src/lib/css-color.ts
102
+ var OKLCH = /^oklch\(\s*([^\s/]+)\s+([^\s/]+)\s+([^\s/]+)\s*(?:\/\s*([^\s/]+)\s*)?\)$/i;
103
+ var KHROMA_SAFE = /^(#|rgba?\(|hsla?\(|[a-z]+$)/i;
104
+ function component(raw, pct) {
105
+ if (raw.toLowerCase() === "none") return 0;
106
+ const percent = raw.endsWith("%");
107
+ const n = Number.parseFloat(percent ? raw.slice(0, -1) : raw);
108
+ if (!Number.isFinite(n)) return null;
109
+ return percent ? n / 100 * pct : n;
110
+ }
111
+ function hueDegrees(raw) {
112
+ if (raw.toLowerCase() === "none") return 0;
113
+ const match = /^(-?[\d.]+)(deg|rad|grad|turn)?$/i.exec(raw);
114
+ if (match === null) return null;
115
+ const n = Number.parseFloat(match[1]);
116
+ if (!Number.isFinite(n)) return null;
117
+ switch ((match[2] ?? "deg").toLowerCase()) {
118
+ case "rad":
119
+ return n * 180 / Math.PI;
120
+ case "grad":
121
+ return n * 0.9;
122
+ case "turn":
123
+ return n * 360;
124
+ default:
125
+ return n;
126
+ }
127
+ }
128
+ function encode(channel) {
129
+ const v = channel <= 31308e-7 ? 12.92 * channel : 1.055 * channel ** (1 / 2.4) - 0.055;
130
+ return Math.round(Math.min(1, Math.max(0, v)) * 255);
131
+ }
132
+ function hex2(value) {
133
+ return value.toString(16).padStart(2, "0");
134
+ }
135
+ function oklchToHex(value) {
136
+ const match = OKLCH.exec(value.trim());
137
+ if (match === null) return null;
138
+ const L = component(match[1], 1);
139
+ const C = component(match[2], 0.4);
140
+ const H = hueDegrees(match[3]);
141
+ const A = match[4] === void 0 ? 1 : component(match[4], 1);
142
+ if (L === null || C === null || H === null || A === null) return null;
143
+ const rad = H * Math.PI / 180;
144
+ const a = C * Math.cos(rad);
145
+ const b = C * Math.sin(rad);
146
+ const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;
147
+ const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;
148
+ const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;
149
+ const r = encode(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s);
150
+ const g = encode(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s);
151
+ const bl = encode(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s);
152
+ const alpha = Math.min(1, Math.max(0, A));
153
+ const suffix = alpha === 1 ? "" : hex2(Math.round(alpha * 255));
154
+ return `#${hex2(r)}${hex2(g)}${hex2(bl)}${suffix}`;
155
+ }
156
+ function toLegacyColor(value) {
157
+ const trimmed = value.trim();
158
+ if (trimmed === "") return null;
159
+ if (KHROMA_SAFE.test(trimmed)) return trimmed;
160
+ return oklchToHex(trimmed);
161
+ }
162
+ function cssVar(styles, name, fallback) {
163
+ return toLegacyColor(styles.getPropertyValue(name)) ?? fallback;
164
+ }
165
+ var seq = 0;
166
+ function MermaidDiagram({ code, className }) {
167
+ const [svg, setSvg] = react$1.useState(null);
168
+ const [failed, setFailed] = react$1.useState(null);
169
+ const { resolved: theme } = chunkYF5IW525_cjs.useTheme();
170
+ const idRef = react$1.useRef(null);
171
+ idRef.current ??= `buro-mermaid-${seq += 1}`;
172
+ react$1.useEffect(() => {
173
+ let cancelled = false;
174
+ void (async () => {
175
+ try {
176
+ const mermaid = (await import('mermaid')).default;
177
+ const styles = getComputedStyle(document.documentElement);
178
+ const dark = theme === "dark";
179
+ mermaid.initialize({
180
+ startOnLoad: false,
181
+ securityLevel: "strict",
182
+ fontFamily: "inherit",
183
+ theme: "base",
184
+ darkMode: dark,
185
+ themeVariables: {
186
+ background: cssVar(styles, "--background", dark ? "#0b0b0c" : "#ffffff"),
187
+ primaryColor: cssVar(styles, "--muted", dark ? "#1c1c1f" : "#f4f4f5"),
188
+ primaryTextColor: cssVar(styles, "--foreground", dark ? "#fafafa" : "#09090b"),
189
+ primaryBorderColor: cssVar(styles, "--border", dark ? "#27272a" : "#e4e4e7"),
190
+ lineColor: cssVar(styles, "--muted-foreground", dark ? "#a1a1aa" : "#71717a"),
191
+ secondaryColor: cssVar(styles, "--secondary", dark ? "#18181b" : "#f4f4f5"),
192
+ tertiaryColor: cssVar(styles, "--accent", dark ? "#18181b" : "#fafafa"),
193
+ fontSize: "13px"
194
+ }
195
+ });
196
+ await mermaid.parse(code);
197
+ const { svg: out } = await mermaid.render(idRef.current, code);
198
+ if (!cancelled) {
199
+ setSvg(out);
200
+ setFailed(null);
201
+ }
202
+ } catch (error) {
203
+ if (!cancelled) {
204
+ setSvg(null);
205
+ setFailed(error instanceof Error ? error.message : String(error));
206
+ }
207
+ }
208
+ })();
209
+ return () => {
210
+ cancelled = true;
211
+ };
212
+ }, [code, theme]);
213
+ if (failed !== null) {
214
+ return /* @__PURE__ */ jsxRuntime.jsx(
215
+ "pre",
216
+ {
217
+ className: chunkW7AEYE7A_cjs.cn("overflow-x-auto rounded-md border border-dashed p-3 text-xs", className),
218
+ title: failed,
219
+ "data-testid": "mermaid:failed",
220
+ children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: code })
221
+ }
222
+ );
223
+ }
224
+ if (svg === null) {
225
+ return /* @__PURE__ */ jsxRuntime.jsx(
226
+ "div",
227
+ {
228
+ className: chunkW7AEYE7A_cjs.cn("h-24 animate-pulse rounded-md border bg-muted/40", className),
229
+ "data-testid": "mermaid:loading",
230
+ "aria-hidden": "true"
231
+ }
232
+ );
233
+ }
234
+ return /* @__PURE__ */ jsxRuntime.jsx(
235
+ "figure",
236
+ {
237
+ className: chunkW7AEYE7A_cjs.cn(
238
+ "not-prose my-4 overflow-x-auto rounded-md border bg-card/40 text-card-foreground p-4",
239
+ // Mermaid emits an intrinsic width; letting it scale down keeps a wide
240
+ // graph inside the column instead of pushing the article sideways.
241
+ "[&_svg]:mx-auto [&_svg]:h-auto [&_svg]:max-w-full",
242
+ className
243
+ ),
244
+ "data-testid": "mermaid:diagram",
245
+ dangerouslySetInnerHTML: { __html: svg }
246
+ }
247
+ );
248
+ }
249
+ var VIDEO_ATTRIBUTES = [
250
+ "src",
251
+ "poster",
252
+ "controls",
253
+ "loop",
254
+ "muted",
255
+ "autoPlay",
256
+ "playsInline",
257
+ "preload",
258
+ "width",
259
+ "height"
260
+ ];
261
+ var docSanitizeSchema = {
262
+ ...rehypeSanitize.defaultSchema,
263
+ // `source` and `picture` are ALREADY in defaultSchema — appending them would
264
+ // be a harmless duplicate, but it would also be a sign the list was never
265
+ // actually read.
266
+ tagNames: [...rehypeSanitize.defaultSchema.tagNames ?? [], "video"],
267
+ attributes: {
268
+ ...rehypeSanitize.defaultSchema.attributes,
269
+ video: [...VIDEO_ATTRIBUTES],
270
+ // defaultSchema.attributes.source is ['srcSet']. Replacing it outright
271
+ // would silently break <picture>, which the default explicitly supports.
272
+ source: [...rehypeSanitize.defaultSchema.attributes?.source ?? [], "src", "type"],
273
+ // `loading` is in neither `img` nor the `*` bucket, so lazy images need it
274
+ // allowed explicitly or the attribute is stripped and every image in a
275
+ // long article loads at once.
276
+ img: [...rehypeSanitize.defaultSchema.attributes?.img ?? [], "loading"]
277
+ }
278
+ };
279
+ var ALLOWED = ["src", "poster", "width", "height"];
280
+ function remarkVideoDirective() {
281
+ return (tree) => {
282
+ unistUtilVisit.visit(tree, (node) => {
283
+ const directive = node;
284
+ if (directive.type !== "leafDirective" && directive.type !== "containerDirective" && directive.type !== "textDirective") {
285
+ return;
286
+ }
287
+ if (directive.name !== "video") return;
288
+ const attrs = directive.attributes ?? {};
289
+ const props = {
290
+ // Always on: a video the reader cannot pause is hostile, and an
291
+ // article that autoplays sound is worse.
292
+ controls: true,
293
+ preload: "metadata"
294
+ };
295
+ for (const key of ALLOWED) {
296
+ const value = attrs[key];
297
+ if (typeof value === "string" && value.length > 0) props[key] = value;
298
+ }
299
+ if (typeof props.src !== "string") return;
300
+ directive.data = { ...directive.data, hName: "video", hProperties: props };
301
+ });
302
+ };
303
+ }
304
+ var Accordion = AccordionPrimitive__namespace.Root;
305
+ function AccordionItem({
306
+ className,
307
+ ...props
308
+ }) {
309
+ return /* @__PURE__ */ jsxRuntime.jsx(
310
+ AccordionPrimitive__namespace.Item,
311
+ {
312
+ className: chunkW7AEYE7A_cjs.cn("border-b border-border/60 last:border-b-0", className),
313
+ ...props
314
+ }
315
+ );
316
+ }
317
+ function AccordionTrigger({
318
+ className,
319
+ children,
320
+ ...props
321
+ }) {
322
+ return /* @__PURE__ */ jsxRuntime.jsx(AccordionPrimitive__namespace.Header, { className: "flex", children: /* @__PURE__ */ jsxRuntime.jsxs(
323
+ AccordionPrimitive__namespace.Trigger,
324
+ {
325
+ className: chunkW7AEYE7A_cjs.cn(
326
+ "flex flex-1 items-start justify-between gap-3 py-3 text-left",
327
+ "text-[0.9375rem] font-medium leading-snug transition-colors",
328
+ "hover:text-primary focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
329
+ "rounded-sm [&[data-state=open]>svg]:rotate-180",
330
+ className
331
+ ),
332
+ ...props,
333
+ children: [
334
+ children,
335
+ /* @__PURE__ */ jsxRuntime.jsx(
336
+ iconsReact.IconChevronDown,
337
+ {
338
+ className: "mt-0.5 size-4 shrink-0 text-muted-foreground transition-transform duration-200",
339
+ "aria-hidden": "true"
340
+ }
341
+ )
342
+ ]
343
+ }
344
+ ) });
345
+ }
346
+ function AccordionContent({
347
+ className,
348
+ children,
349
+ ...props
350
+ }) {
351
+ return /* @__PURE__ */ jsxRuntime.jsx(
352
+ AccordionPrimitive__namespace.Content,
353
+ {
354
+ className: chunkW7AEYE7A_cjs.cn(
355
+ "overflow-hidden",
356
+ "data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
357
+ ),
358
+ ...props,
359
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: chunkW7AEYE7A_cjs.cn("pb-3.5 pt-0", className), children })
360
+ }
361
+ );
362
+ }
363
+ function splitFaq(markdown) {
364
+ const lines = markdown.split("\n");
365
+ const preamble = [];
366
+ const sections = [];
367
+ let current;
368
+ let fence;
369
+ for (const line of lines) {
370
+ const fenceMatch = /^\s{0,3}(`{3,}|~{3,})/.exec(line);
371
+ if (fenceMatch?.[1] !== void 0) {
372
+ const marker = fenceMatch[1][0];
373
+ if (fence === void 0) fence = marker;
374
+ else if (fence === marker) fence = void 0;
375
+ }
376
+ const heading = fence === void 0 ? /^##\s+(.+?)\s*$/.exec(line) : null;
377
+ if (heading?.[1] !== void 0) {
378
+ if (current) sections.push({ question: current.question, answer: current.answer.join("\n") });
379
+ current = { question: heading[1], answer: [] };
380
+ continue;
381
+ }
382
+ if (current) current.answer.push(line);
383
+ else preamble.push(line);
384
+ }
385
+ if (current) sections.push({ question: current.question, answer: current.answer.join("\n") });
386
+ return { preamble: preamble.join("\n").trim(), sections };
387
+ }
388
+ function DocFaq({ children, className }) {
389
+ const { preamble, sections } = splitFaq(children);
390
+ const slugs = react$1.useMemo(() => sections.map((s) => chunkYF5IW525_cjs.docSlug(s.question)), [sections]);
391
+ const [open, setOpen] = react$1.useState([]);
392
+ react$1.useEffect(() => {
393
+ if (typeof window === "undefined") return;
394
+ const sync = () => {
395
+ const id = chunkYF5IW525_cjs.readDocHash();
396
+ if (id === "") return;
397
+ const i = slugs.indexOf(id);
398
+ if (i === -1) return;
399
+ setOpen((prev) => prev.includes(`q${i}`) ? prev : [...prev, `q${i}`]);
400
+ chunkYF5IW525_cjs.scrollToDocAnchor(id, document.body);
401
+ };
402
+ sync();
403
+ window.addEventListener("hashchange", sync);
404
+ return () => window.removeEventListener("hashchange", sync);
405
+ }, [slugs]);
406
+ if (sections.length === 0) return /* @__PURE__ */ jsxRuntime.jsx(DocMarkdown, { className, children });
407
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: chunkW7AEYE7A_cjs.cn("flex flex-col", className), "data-testid": "doc-faq", children: [
408
+ preamble !== "" && /* @__PURE__ */ jsxRuntime.jsx(DocMarkdown, { className: "mb-2", children: preamble }),
409
+ /* @__PURE__ */ jsxRuntime.jsx(Accordion, { type: "multiple", value: open, onValueChange: setOpen, className: "w-full", children: sections.map((s, i) => /* @__PURE__ */ jsxRuntime.jsxs(
410
+ AccordionItem,
411
+ {
412
+ value: `q${i}`,
413
+ id: slugs[i],
414
+ className: "group/anchor scroll-mt-4",
415
+ "data-testid": `doc-faq:item:${i}`,
416
+ children: [
417
+ /* @__PURE__ */ jsxRuntime.jsxs(AccordionTrigger, { children: [
418
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0", children: s.question }),
419
+ /* @__PURE__ */ jsxRuntime.jsx(DocAnchorLink, { slug: slugs[i], stopPropagation: true, className: "ml-auto mr-1" })
420
+ ] }),
421
+ /* @__PURE__ */ jsxRuntime.jsx(AccordionContent, { children: /* @__PURE__ */ jsxRuntime.jsx(DocMarkdown, { className: "prose-p:my-2 first:prose-p:mt-0 last:prose-p:mb-0", children: s.answer }) })
422
+ ]
423
+ },
424
+ `${i}-${s.question}`
425
+ )) })
426
+ ] });
427
+ }
428
+ function DocMarkdown({ children, className }) {
429
+ return /* @__PURE__ */ jsxRuntime.jsx(
430
+ "div",
431
+ {
432
+ className: chunkW7AEYE7A_cjs.cn(
433
+ "prose prose-sm max-w-none dark:prose-invert",
434
+ // The default prose measure is wider than any panel we render into,
435
+ // and its heading rhythm is tuned for a page rather than a column.
436
+ "prose-headings:scroll-mt-4 prose-headings:font-semibold",
437
+ "prose-h1:text-base prose-h2:text-[0.9375rem] prose-h3:text-sm",
438
+ "prose-p:leading-[1.65]",
439
+ "prose-a:font-medium prose-a:underline-offset-2",
440
+ "prose-img:rounded-md prose-img:border",
441
+ "prose-video:rounded-md prose-video:border prose-video:w-full",
442
+ "prose-table:text-[0.8125rem]",
443
+ className
444
+ ),
445
+ children: /* @__PURE__ */ jsxRuntime.jsx(
446
+ Markdown__default.default,
447
+ {
448
+ remarkPlugins: [remarkGfm__default.default, remarkDirective__default.default, remarkVideoDirective],
449
+ rehypePlugins: [rehypeSlug__default.default, [rehypeSanitize__default.default, docSanitizeSchema]],
450
+ components: {
451
+ // Every link in a body leaves the app; routing is not involved, so
452
+ // this stays a plain anchor and the package stays router-agnostic.
453
+ a: ({ href, children: kids, ...rest }) => /* @__PURE__ */ jsxRuntime.jsx("a", { href, target: "_blank", rel: "noopener noreferrer", ...rest, children: kids }),
454
+ // Long articles should not fetch every illustration up front. The
455
+ // attribute has to be allow-listed in the schema too — it is in
456
+ // neither `img` nor the `*` bucket by default.
457
+ img: ({ ...rest }) => /* @__PURE__ */ jsxRuntime.jsx("img", { loading: "lazy", ...rest, alt: rest.alt ?? "" }),
458
+ // A wide GFM table must scroll inside its own box rather than push
459
+ // the panel — or the page — sideways.
460
+ table: ({ children: kids, ...rest }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsx("table", { ...rest, children: kids }) }),
461
+ /*
462
+ ```mermaid blocks become diagrams; every other fence stays a code
463
+ block. `language-*` on `code` is already allow-listed by
464
+ rehype-sanitize's default schema, so no schema change is owed —
465
+ and the diagram source reaches MermaidDiagram as TEXT, never as
466
+ markup, which is what keeps the sanitized pipeline sanitized.
467
+ */
468
+ code: ({ className: cls, children: kids, ...rest }) => {
469
+ if (/\blanguage-mermaid\b/.test(cls ?? "")) {
470
+ return /* @__PURE__ */ jsxRuntime.jsx(MermaidDiagram, { code: String(kids).replace(/\n$/, "") });
471
+ }
472
+ return /* @__PURE__ */ jsxRuntime.jsx("code", { className: cls, ...rest, children: kids });
473
+ },
474
+ /*
475
+ A heading is a linkable place, and an anchor nobody can see is an
476
+ anchor nobody uses — so each one carries a « # » that appears on
477
+ hover or focus and puts the fragment in the address bar.
478
+
479
+ The id on the element is `user-content-<slug>` (the sanitizer's
480
+ clobber prefix); the href is the BARE slug, because that is what a
481
+ person pastes and what `findDocAnchor` resolves. Keeping the visible
482
+ link clean is the whole reason those two are allowed to differ.
483
+ */
484
+ h2: ({ children: kids, id, ...rest }) => /* @__PURE__ */ jsxRuntime.jsx(HeadingWithAnchor, { level: 2, id, ...rest, children: kids }),
485
+ h3: ({ children: kids, id, ...rest }) => /* @__PURE__ */ jsxRuntime.jsx(HeadingWithAnchor, { level: 3, id, ...rest, children: kids }),
486
+ /*
487
+ A mermaid diagram replaces the whole block, so the <pre> that
488
+ react-markdown wraps it in would contribute a stray bordered box
489
+ around the figure.
490
+ */
491
+ pre: ({ children: kids, ...rest }) => {
492
+ const only = Array.isArray(kids) ? kids[0] : kids;
493
+ const cls = typeof only === "object" && only !== null && "props" in only ? only.props?.className ?? "" : "";
494
+ if (/\blanguage-mermaid\b/.test(cls)) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: kids });
495
+ return /* @__PURE__ */ jsxRuntime.jsx("pre", { ...rest, children: kids });
496
+ }
497
+ },
498
+ children
499
+ }
500
+ )
501
+ }
502
+ );
503
+ }
504
+ function HeadingWithAnchor({
505
+ level,
506
+ id,
507
+ children,
508
+ ...rest
509
+ }) {
510
+ const Tag = level === 2 ? "h2" : "h3";
511
+ const slug = id?.startsWith(chunkYF5IW525_cjs.DOC_ID_PREFIX) ? id.slice(chunkYF5IW525_cjs.DOC_ID_PREFIX.length) : id;
512
+ return /* @__PURE__ */ jsxRuntime.jsxs(Tag, { id, className: "group/anchor scroll-mt-4", ...rest, children: [
513
+ children,
514
+ slug !== void 0 && slug !== "" && /* @__PURE__ */ jsxRuntime.jsx(DocAnchorLink, { slug })
515
+ ] });
516
+ }
517
+
518
+ Object.defineProperty(exports, "DOC_ID_PREFIX", {
519
+ enumerable: true,
520
+ get: function () { return chunkYF5IW525_cjs.DOC_ID_PREFIX; }
521
+ });
522
+ Object.defineProperty(exports, "docShareUrl", {
523
+ enumerable: true,
524
+ get: function () { return chunkYF5IW525_cjs.docShareUrl; }
525
+ });
526
+ Object.defineProperty(exports, "docSlug", {
527
+ enumerable: true,
528
+ get: function () { return chunkYF5IW525_cjs.docSlug; }
529
+ });
530
+ Object.defineProperty(exports, "findDocAnchor", {
531
+ enumerable: true,
532
+ get: function () { return chunkYF5IW525_cjs.findDocAnchor; }
533
+ });
534
+ Object.defineProperty(exports, "readDocHash", {
535
+ enumerable: true,
536
+ get: function () { return chunkYF5IW525_cjs.readDocHash; }
537
+ });
538
+ Object.defineProperty(exports, "scrollToDocAnchor", {
539
+ enumerable: true,
540
+ get: function () { return chunkYF5IW525_cjs.scrollToDocAnchor; }
541
+ });
542
+ exports.DocAnchorLink = DocAnchorLink;
543
+ exports.DocFaq = DocFaq;
544
+ exports.DocMarkdown = DocMarkdown;
545
+ exports.MermaidDiagram = MermaidDiagram;
546
+ exports.docSanitizeSchema = docSanitizeSchema;
547
+ exports.remarkVideoDirective = remarkVideoDirective;
548
+ exports.splitFaq = splitFaq;
549
+ //# sourceMappingURL=index.cjs.map
550
+ //# sourceMappingURL=index.cjs.map