@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.
- package/LICENSE +21 -0
- package/README.md +65 -0
- package/dist/chunk-ABUQPOOE.js +11 -0
- package/dist/chunk-ABUQPOOE.js.map +1 -0
- package/dist/chunk-DBGGWBFG.cjs +199 -0
- package/dist/chunk-DBGGWBFG.cjs.map +1 -0
- package/dist/chunk-FMX4GLCQ.js +6 -0
- package/dist/chunk-FMX4GLCQ.js.map +1 -0
- package/dist/chunk-UC57Z243.js +143 -0
- package/dist/chunk-UC57Z243.js.map +1 -0
- package/dist/chunk-UPGPO75B.js +164 -0
- package/dist/chunk-UPGPO75B.js.map +1 -0
- package/dist/chunk-VXJAXJ3U.cjs +8 -0
- package/dist/chunk-VXJAXJ3U.cjs.map +1 -0
- package/dist/chunk-W7AEYE7A.cjs +13 -0
- package/dist/chunk-W7AEYE7A.cjs.map +1 -0
- package/dist/chunk-YF5IW525.cjs +155 -0
- package/dist/chunk-YF5IW525.cjs.map +1 -0
- package/dist/index.cjs +17752 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +6091 -0
- package/dist/index.d.ts +6091 -0
- package/dist/index.js +17369 -0
- package/dist/index.js.map +1 -0
- package/dist/layout/theme-storage-key.cjs +12 -0
- package/dist/layout/theme-storage-key.cjs.map +1 -0
- package/dist/layout/theme-storage-key.d.cts +23 -0
- package/dist/layout/theme-storage-key.d.ts +23 -0
- package/dist/layout/theme-storage-key.js +3 -0
- package/dist/layout/theme-storage-key.js.map +1 -0
- package/dist/markdown/index.cjs +550 -0
- package/dist/markdown/index.cjs.map +1 -0
- package/dist/markdown/index.d.cts +221 -0
- package/dist/markdown/index.d.ts +221 -0
- package/dist/markdown/index.js +492 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/rich-text-6V7WZ3WV.cjs +518 -0
- package/dist/rich-text-6V7WZ3WV.cjs.map +1 -0
- package/dist/rich-text-N2FZEJJV.js +491 -0
- package/dist/rich-text-N2FZEJJV.js.map +1 -0
- package/dist/styles.css +794 -0
- package/dist/theme/theme-css.cjs +105 -0
- package/dist/theme/theme-css.cjs.map +1 -0
- package/dist/theme/theme-css.d.cts +49 -0
- package/dist/theme/theme-css.d.ts +49 -0
- package/dist/theme/theme-css.js +102 -0
- package/dist/theme/theme-css.js.map +1 -0
- package/dist/theme/theme-storage.cjs +51 -0
- package/dist/theme/theme-storage.cjs.map +1 -0
- package/dist/theme/theme-storage.d.cts +75 -0
- package/dist/theme/theme-storage.d.ts +75 -0
- package/dist/theme/theme-storage.js +45 -0
- package/dist/theme/theme-storage.js.map +1 -0
- 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 @@
|
|
|
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
|