boltdocs 3.2.2 → 3.3.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 (155) hide show
  1. package/bin/boltdocs.js +20 -1
  2. package/bin/preloader.mjs +28 -0
  3. package/dist/{adapters-ByYY44Zy.mjs → adapters-BmB4TKCc.mjs} +1 -1
  4. package/dist/{adapters-DvmtpdSF.cjs → adapters-Cp1Maurn.cjs} +1 -1
  5. package/dist/audit-CLXjMzwQ.mjs +11 -0
  6. package/dist/audit-COgjnQ5n.cjs +11 -0
  7. package/dist/build-BS2hefiL.cjs +6 -0
  8. package/dist/build-C5mNh9D-.mjs +6 -0
  9. package/dist/cache-CJE9nWLE.cjs +6 -0
  10. package/dist/cache-OY4HzIXl.mjs +6 -0
  11. package/dist/callout-oI_LCbxS.d.ts +71 -0
  12. package/dist/callout-plqx9Dw-.d.cts +71 -0
  13. package/dist/client/index.cjs +1265 -569
  14. package/dist/client/index.d.cts +3 -542
  15. package/dist/client/index.d.ts +3 -542
  16. package/dist/client/index.js +1167 -521
  17. package/dist/client/mdx.cjs +1 -1
  18. package/dist/client/mdx.d.cts +24 -2
  19. package/dist/client/mdx.d.ts +24 -2
  20. package/dist/client/mdx.js +1 -1
  21. package/dist/client/primitives.cjs +14 -4
  22. package/dist/client/primitives.d.cts +281 -38
  23. package/dist/client/primitives.d.ts +281 -38
  24. package/dist/client/primitives.js +5 -5
  25. package/dist/client/router.cjs +49 -0
  26. package/dist/client/router.d.cts +3 -0
  27. package/dist/client/router.d.ts +3 -0
  28. package/dist/client/router.js +8 -0
  29. package/dist/client/theme/reset.css +40 -4
  30. package/dist/{config-BpzaPsJj.d.cts → config-BoRlz0Ca.d.mts} +157 -14
  31. package/dist/{config-xtS0ZkMm.d.mts → config-CTsX_tGM.d.cts} +157 -14
  32. package/dist/config-Ceib-AOH.cjs +7 -0
  33. package/dist/config-DGLQsy7y.mjs +7 -0
  34. package/dist/{package-CH2ChQqw.cjs → config-files-BYHDOrKn.mjs} +1 -1
  35. package/dist/config-files-D9cdBMl2.cjs +6 -0
  36. package/dist/config-jJh8WYJI.cjs +6 -0
  37. package/dist/csp-C4LmN1ko.cjs +6 -0
  38. package/dist/csp-D6JEOqKh.mjs +6 -0
  39. package/dist/dev-Ch3hEe5Y.cjs +6 -0
  40. package/dist/dev-QPNxysrd.mjs +6 -0
  41. package/dist/{docs-layout-BdtZ9z6s.js → docs-layout-CbZ7LDRe.js} +504 -249
  42. package/dist/{docs-layout-CFWI15Id.cjs → docs-layout-vzfXed1Q.cjs} +516 -254
  43. package/dist/{doctor-BBtmNJE0.cjs → doctor-B1_YL0Wt.cjs} +1 -1
  44. package/dist/doctor-C7whl4ED.cjs +6 -0
  45. package/dist/doctor-DmQnml2k.mjs +6 -0
  46. package/dist/{generator-COepVWGi.cjs → generator-BlW45ST7.cjs} +5 -3
  47. package/dist/{generator-CcOBN5vI.mjs → generator-BwknNejd.mjs} +5 -3
  48. package/dist/{handler-XX7cLTqe.cjs → handler-CDEoKARD.cjs} +2 -2
  49. package/dist/{handler-DzQae4eA.mjs → handler-D7UyZlPm.mjs} +2 -2
  50. package/dist/{image-Bz87fCla.js → image-B-NDSDuY.js} +129 -11
  51. package/dist/{image-C4TlpMHn.cjs → image-Hnaa2bJd.cjs} +175 -9
  52. package/dist/index-C56bL-A-.d.cts +1344 -0
  53. package/dist/{sidebar-COTaMHR-.d.ts → index-CPUUKzxD.d.cts} +344 -327
  54. package/dist/{sidebar-CrEdJuHX.d.cts → index-Ci4kfOHF.d.ts} +344 -327
  55. package/dist/index-Cu9H6CRa.d.ts +1344 -0
  56. package/dist/link-CgdI6O2o.js +872 -0
  57. package/dist/{link-D1Gjkb-2.cjs → link-vZjmD-es.cjs} +406 -223
  58. package/dist/{mdx-BuO6a1f7.js → mdx-BRT71V5Q.js} +211 -168
  59. package/dist/{mdx-CD-tSjRG.cjs → mdx-D0Absdq5.cjs} +225 -169
  60. package/dist/native-BNCY7PRF.cjs +6 -0
  61. package/dist/native-Di9Fnfrv.mjs +6 -0
  62. package/dist/native-Kre2T5eS.cjs +6 -0
  63. package/dist/node/cache.cjs +1 -1
  64. package/dist/node/cache.d.cts +19 -3
  65. package/dist/node/cache.d.mts +19 -3
  66. package/dist/node/cache.mjs +1 -1
  67. package/dist/node/cli-entry.cjs +1 -9
  68. package/dist/node/cli-entry.mjs +1 -9
  69. package/dist/node/index.cjs +1 -1
  70. package/dist/node/index.d.cts +245 -17
  71. package/dist/node/index.d.mts +246 -18
  72. package/dist/node/index.mjs +1 -1
  73. package/dist/node/mdx/shiki-adapter.cjs +1 -1
  74. package/dist/node/mdx/shiki-adapter.d.cts +24 -3
  75. package/dist/node/mdx/shiki-adapter.d.mts +24 -2
  76. package/dist/node/mdx/shiki-adapter.mjs +1 -1
  77. package/dist/node-Du5UL5wo.mjs +23 -0
  78. package/dist/node-DunuGqyp.cjs +23 -0
  79. package/dist/{package-DSSB8B5L.mjs → package-DA848keM.mjs} +1 -1
  80. package/dist/package-wplzySjz.cjs +6 -0
  81. package/dist/pages-external-BBw8ObVK.mjs +6 -0
  82. package/dist/pages-external-DPiPFxxP.cjs +6 -0
  83. package/dist/parser-BqTOsEUa.cjs +6 -0
  84. package/dist/parser-CGMY2lJ7.mjs +6 -0
  85. package/dist/plugin-BcTzvHAJ.mjs +69 -0
  86. package/dist/plugin-TulVMt1z.cjs +69 -0
  87. package/dist/plugin-context-C-l90hFt.cjs +15 -0
  88. package/dist/plugin-context-wqHNSZum.mjs +15 -0
  89. package/dist/plugin-lifecycle-B1BzSHdn.cjs +6 -0
  90. package/dist/resolve-B7vWOd8R.cjs +6 -0
  91. package/dist/resolve-C2kKnk_5.mjs +6 -0
  92. package/dist/resolve-It1Iug83.cjs +6 -0
  93. package/dist/resolve-YdTrt_FG.mjs +6 -0
  94. package/dist/router-C3kygdRK.cjs +1285 -0
  95. package/dist/router-CZBxMRAH.js +987 -0
  96. package/dist/routes-BL4y79tb.mjs +6 -0
  97. package/dist/routes-C2nPecwu.cjs +6 -0
  98. package/dist/routes-U9L8psZ9.cjs +6 -0
  99. package/dist/{search-dialog-DHqwjJuG.cjs → search-dialog-BCOHQGSt.cjs} +1 -1
  100. package/dist/search-dialog-Bp_P1MPP.js +481 -0
  101. package/dist/{search-dialog-DFnlx8LO.js → search-dialog-BqkzDTq2.js} +107 -57
  102. package/dist/{search-dialog-BRFno7HD.cjs → search-dialog-CIeIUhAn.cjs} +218 -43
  103. package/dist/{search-dialog-D6TvjcuV.js → search-dialog-DtmxmfEu.js} +1 -1
  104. package/dist/{search-dialog-B1NERUNx.cjs → search-dialog-x_qHU8ty.cjs} +107 -58
  105. package/dist/server/index.cjs +1 -1
  106. package/dist/server/index.d.mts +1 -1
  107. package/dist/server/index.mjs +1 -1
  108. package/dist/update-check-BKqmYfwT.cjs +11 -0
  109. package/dist/update-check-QnrVR3q1.mjs +11 -0
  110. package/dist/utils-8lzafE49.mjs +7 -0
  111. package/dist/utils-ej6_SjIZ.cjs +7 -0
  112. package/dist/virtual-modules-Ca5tHk1X.cjs +30 -0
  113. package/dist/virtual-modules-Dlgt1fP0.mjs +30 -0
  114. package/package.json +18 -16
  115. package/src/shared/mdx-path.ts +12 -0
  116. package/src/shared/structured-data.ts +145 -0
  117. package/src/shared/types.ts +220 -16
  118. package/dist/banner-BMop1KLF.d.ts +0 -100
  119. package/dist/banner-CVD0GugR.d.cts +0 -100
  120. package/dist/cache-CO4Dy2uQ.cjs +0 -6
  121. package/dist/cache-Dk53iOo6.mjs +0 -6
  122. package/dist/config-CCXp3aXO.mjs +0 -7
  123. package/dist/config-CMd4XoFq.cjs +0 -6
  124. package/dist/config-DlGoEM9D.cjs +0 -7
  125. package/dist/csp-CRp_3U7a.cjs +0 -6
  126. package/dist/csp-oTC2ygvF.mjs +0 -6
  127. package/dist/doctor-Bv16QSrI.cjs +0 -7
  128. package/dist/doctor-DOAbSaiL.mjs +0 -7
  129. package/dist/headers-Ddwek8K7.mjs +0 -6
  130. package/dist/headers-dg4SDrjY.cjs +0 -6
  131. package/dist/link-2PzgTnaP.js +0 -673
  132. package/dist/meta-loader-C4Hu9RN9.mjs +0 -6
  133. package/dist/meta-loader-Dq1zcFnh.cjs +0 -6
  134. package/dist/node-B5hu_6qj.cjs +0 -104
  135. package/dist/node-BXtqM1uH.mjs +0 -6
  136. package/dist/node-C8725aw7.mjs +0 -104
  137. package/dist/node-aD1eisrR.cjs +0 -6
  138. package/dist/pages-external-BYa4JSda.mjs +0 -6
  139. package/dist/pages-external-XkYIs-d5.cjs +0 -6
  140. package/dist/parser-DCiEWkSk.cjs +0 -8
  141. package/dist/parser-qk9FIHJ7.mjs +0 -8
  142. package/dist/plugin-CX1OZ6jB.cjs +0 -73
  143. package/dist/plugin-CgfiWaFZ.mjs +0 -73
  144. package/dist/routes-BW6zsk7Z.mjs +0 -6
  145. package/dist/routes-BgoIB5wb.cjs +0 -6
  146. package/dist/routes-CORVxc02.cjs +0 -6
  147. package/dist/search-dialog-BmxHiKwt.js +0 -312
  148. package/dist/shiki-adapter-CoQ6jyJ4.cjs +0 -6
  149. package/dist/shiki-adapter-D-aKw7PP.mjs +0 -6
  150. package/dist/utils-Crm9oPcV.mjs +0 -7
  151. package/dist/utils-D6Xtj0FZ.cjs +0 -7
  152. /package/dist/{errors-0zGHRLue.mjs → errors-N-b6Sn2G.mjs} +0 -0
  153. /package/dist/{index-1KsTDq4o.d.mts → index-lFoyzCXy.d.mts} +0 -0
  154. /package/dist/{types-generator-BXhv7Ill.cjs → types-generator-DDSA93P3.cjs} +0 -0
  155. /package/dist/{types-generator-bDPvUztF.mjs → types-generator-DG1GX1EZ.mjs} +0 -0
@@ -3,15 +3,15 @@
3
3
  * Copyright (c) 2026 Jesus Alcala
4
4
  * Licensed under the MIT License.
5
5
  */
6
- import { B as useConfig, D as Lightbulb, T as Info, c as AlertCircle, d as Check, g as Copy, l as AlertTriangle, r as cn, t as Link } from "./link-2PzgTnaP.js";
7
- import { a as CodeBlockGroup, i as CodeBlockContent, l as useTheme, n as Tooltip, o as CodeBlockHeader, r as CodeBlock$1, s as Heading$1, t as Image$1 } from "./image-Bz87fCla.js";
6
+ import { W as useConfig, b as Copy, f as AlertCircle, h as Check, j as Lightbulb, k as Info, o as cn, p as AlertTriangle, t as Link } from "./link-CgdI6O2o.js";
7
+ import { _ as useTheme, a as CodeBlockContent, c as CodeBlockHeader, h as Heading$1, i as CodeBlockActions, l as CodeBlockPre, n as Tooltip, o as CodeBlockExpand, r as CodeBlock$1, s as CodeBlockGroup, t as Image$1, u as Callout$1 } from "./image-B-NDSDuY.js";
8
8
  import { useCallback, useEffect, useRef, useState } from "react";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { Button } from "react-aria-components";
11
11
 
12
12
  //#region src/client/components/mdx/field.tsx
13
- const Field = ({ children, name, type, description, required }) => /* @__PURE__ */ jsxs("div", {
14
- className: "my-4 border border-subtle bg-surface/50 p-4 rounded-xl flex flex-col gap-1 text-sm select-none",
13
+ const Field = ({ children, name, type, description, required, className }) => /* @__PURE__ */ jsxs("div", {
14
+ className: cn("my-4 border border-subtle bg-surface/50 p-4 rounded-xl flex flex-col gap-1 text-sm select-none", className),
15
15
  children: [
16
16
  /* @__PURE__ */ jsxs("div", {
17
17
  className: "flex items-center gap-2",
@@ -25,7 +25,7 @@ const Field = ({ children, name, type, description, required }) => /* @__PURE__
25
25
  children: type
26
26
  }),
27
27
  required && /* @__PURE__ */ jsx("span", {
28
- className: "text-xs text-rose-500 font-semibold",
28
+ className: "text-xs text-danger-500 font-semibold",
29
29
  children: "required"
30
30
  })
31
31
  ]
@@ -132,8 +132,8 @@ const Typographics = {
132
132
 
133
133
  //#endregion
134
134
  //#region src/client/components/mdx/table.tsx
135
- const Table = (props) => /* @__PURE__ */ jsx("div", {
136
- className: "my-6 w-full overflow-x-auto rounded-xl border border-subtle bg-surface/30",
135
+ const Table = ({ wrapperClassName, ...props }) => /* @__PURE__ */ jsx("div", {
136
+ className: cn("my-6 w-full overflow-x-auto rounded-xl border border-subtle bg-surface/30", wrapperClassName),
137
137
  children: /* @__PURE__ */ jsx("table", {
138
138
  className: "w-full border-collapse text-left text-sm",
139
139
  ...props
@@ -167,62 +167,60 @@ const TableComponents = {
167
167
 
168
168
  //#endregion
169
169
  //#region src/client/components/mdx/callout.tsx
170
- const variantStyles = {
170
+ /**
171
+ * Default styled callout. Built on the style-neutral Callout primitive and
172
+ * themed through semantic variant tokens (`--color-{danger|success|warning|info|primary}-500`),
173
+ * so a custom theme can restyle it entirely from CSS.
174
+ */
175
+ const variantMeta = {
171
176
  note: {
172
- container: "bg-slate-500/5 dark:bg-slate-500/10 border-slate-500/40 text-slate-800 dark:text-slate-200",
173
- titleText: "text-slate-900 dark:text-slate-100",
174
- iconColor: "text-slate-500",
177
+ container: "bg-primary-500/10 border-primary-500/50",
178
+ accent: "text-primary-500",
175
179
  icon: Info,
176
180
  defaultTitle: "Note"
177
181
  },
178
182
  info: {
179
- container: "bg-indigo-500/5 dark:bg-indigo-500/10 border-indigo-500/40 text-indigo-800 dark:text-indigo-200",
180
- titleText: "text-indigo-900 dark:text-indigo-100",
181
- iconColor: "text-indigo-500",
183
+ container: "bg-info-500/10 border-info-500/50",
184
+ accent: "text-info-500",
182
185
  icon: Info,
183
186
  defaultTitle: "Info"
184
187
  },
185
188
  tip: {
186
- container: "bg-green-500/5 dark:bg-green-500/10 border-green-500/40 text-green-800 dark:text-green-200",
187
- titleText: "text-green-900 dark:text-green-100",
188
- iconColor: "text-green-500",
189
+ container: "bg-success-500/10 border-success-500/50",
190
+ accent: "text-success-500",
189
191
  icon: Lightbulb,
190
192
  defaultTitle: "Tip"
191
193
  },
192
194
  warning: {
193
- container: "bg-amber-500/5 dark:bg-amber-500/10 border-amber-500/40 text-amber-800 dark:text-amber-200",
194
- titleText: "text-amber-900 dark:text-amber-100",
195
- iconColor: "text-amber-500",
195
+ container: "bg-warning-500/10 border-warning-500/50",
196
+ accent: "text-warning-500",
196
197
  icon: AlertTriangle,
197
198
  defaultTitle: "Warning"
198
199
  },
199
200
  danger: {
200
- container: "bg-rose-500/5 dark:bg-rose-500/10 border-rose-500/40 text-rose-800 dark:text-rose-200",
201
- titleText: "text-rose-900 dark:text-rose-100",
202
- iconColor: "text-rose-500",
201
+ container: "bg-danger-500/10 border-danger-500/50",
202
+ accent: "text-danger-500",
203
203
  icon: AlertCircle,
204
204
  defaultTitle: "Danger"
205
205
  }
206
206
  };
207
- function Callout({ children, className = "", variant = "note", title, ...props }) {
208
- const styles = variantStyles[variant] || variantStyles.note;
209
- const Icon = styles.icon;
210
- return /* @__PURE__ */ jsxs("div", {
211
- className: cn("my-6 flex gap-4 p-4 rounded-xl border-2", styles.container, className),
212
- ...props,
213
- children: [/* @__PURE__ */ jsx("div", {
214
- className: cn("shrink-0 pt-0.5", styles.iconColor),
207
+ function Callout({ children, className = "", variant = "note", title, iconClassName, titleClassName, bodyClassName, ...props }) {
208
+ const meta = variantMeta[variant] || variantMeta.note;
209
+ const Icon = meta.icon;
210
+ return /* @__PURE__ */ jsx(Callout$1, {
211
+ variant,
212
+ icon: /* @__PURE__ */ jsx("div", {
213
+ className: cn("pt-0.5", meta.accent, iconClassName),
215
214
  children: /* @__PURE__ */ jsx(Icon, { className: "w-5 h-5 stroke-2" })
216
- }), /* @__PURE__ */ jsxs("div", {
217
- className: "flex-1 text-[0.875rem] leading-[1.6]",
218
- children: [/* @__PURE__ */ jsx("div", {
219
- className: cn("font-bold text-sm mb-1", styles.titleText),
220
- children: title || styles.defaultTitle
221
- }), /* @__PURE__ */ jsx("div", {
222
- className: "prose prose-neutral dark:prose-invert max-w-none [&>p]:m-0 [&>p+p]:mt-2",
223
- children
224
- })]
225
- })]
215
+ }),
216
+ title: /* @__PURE__ */ jsx("div", {
217
+ className: cn("font-bold text-sm text-body", meta.accent, titleClassName),
218
+ children: title || meta.defaultTitle
219
+ }),
220
+ className: cn("my-6 rounded-xl border-2 text-body prose prose-neutral dark:prose-invert max-w-none", meta.container, className),
221
+ bodyClassName: cn("text-[0.875rem] leading-[1.6]", bodyClassName),
222
+ ...props,
223
+ children
226
224
  });
227
225
  }
228
226
 
@@ -251,109 +249,151 @@ const copyToClipboard = async (text) => {
251
249
  };
252
250
 
253
251
  //#endregion
254
- //#region src/client/components/mdx/use-code-block.ts
255
- function useCodeBlock(props) {
252
+ //#region src/client/components/mdx/use-copy-button.ts
253
+ /**
254
+ * Copy-to-clipboard state for a code block (or any copyable surface).
255
+ * `handleCopy(code)` copies the given text and shows a transient "copied"
256
+ * confirmation for 2 seconds.
257
+ */
258
+ function useCopyButton() {
256
259
  const [copied, setCopied] = useState(false);
257
- const [isExpanded, setIsExpanded] = useState(false);
258
- const [isExpandable, setIsExpandable] = useState(false);
259
- const [rated, setRated] = useState(null);
260
- const preRef = useRef(null);
261
260
  const timerRef = useRef(null);
262
- const customConfig = useConfig().integrations?.feedback?.custom;
263
- const showCodeBlockFeedback = !!(customConfig?.enabled && !props.plain);
264
- const lang = props.lang || props["data-lang"] || "";
265
- const isHighlighted = props["data-highlighted"] === "true" || typeof props.className === "string" && props.className.includes("shiki");
266
- const rawHighlightedHtml = props.highlightedHtml || props["data-highlighted-html"];
267
- const effectiveHighlightedHtml = typeof rawHighlightedHtml === "string" ? rawHighlightedHtml.replace(/<span class="line">\s*(?:<span[^>]*>\s*<\/span>)?\s*<\/span>\s*(<\/code>\s*<\/pre>)/g, "$1") : rawHighlightedHtml;
268
- const effectiveTitle = props.title || props["data-title"];
269
- const handleRate = useCallback(async (type) => {
270
- if (rated) return;
271
- setRated(type);
272
- try {
273
- const code = preRef.current?.textContent ?? "";
274
- const snippet = code.trim().slice(0, 100) + (code.length > 100 ? "..." : "");
275
- const blockId = `Code Block (${lang || "plain"}): \`${snippet}\``;
276
- const endpoint = customConfig?.endpoint || "/api/feedback";
277
- await fetch(endpoint, {
278
- method: "POST",
279
- headers: { "Content-Type": "application/json" },
280
- body: JSON.stringify({
281
- rating: type === "up" ? "good" : "bad",
282
- comment: `Rated code block: ${type === "up" ? "Helpful" : "Unhelpful"}`,
283
- path: window.location.pathname,
284
- title: document.title,
285
- blockId
286
- })
287
- });
288
- } catch (err) {
289
- console.error("Failed to submit code block feedback:", err);
290
- }
291
- }, [
292
- rated,
293
- lang,
294
- customConfig?.endpoint
295
- ]);
296
261
  useEffect(() => {
297
262
  return () => {
298
263
  if (timerRef.current) clearTimeout(timerRef.current);
299
264
  };
300
265
  }, []);
301
- const handleCopy = useCallback(async () => {
302
- copyToClipboard(preRef.current?.textContent ?? "");
303
- setCopied(true);
304
- if (timerRef.current) clearTimeout(timerRef.current);
305
- timerRef.current = setTimeout(() => {
306
- setCopied(false);
307
- timerRef.current = null;
308
- }, 2e3);
309
- }, []);
310
- useEffect(() => {
311
- const lines = (preRef.current?.textContent ?? "").trim().split("\n").length;
312
- setIsExpandable(lines > 6);
313
- }, [props.children, props.highlightedHtml]);
314
266
  return {
315
267
  copied,
268
+ handleCopy: useCallback((code) => {
269
+ copyToClipboard(code);
270
+ setCopied(true);
271
+ if (timerRef.current) clearTimeout(timerRef.current);
272
+ timerRef.current = setTimeout(() => {
273
+ setCopied(false);
274
+ timerRef.current = null;
275
+ }, 2e3);
276
+ }, [])
277
+ };
278
+ }
279
+
280
+ //#endregion
281
+ //#region src/client/components/mdx/use-expandable.ts
282
+ /**
283
+ * Expand / collapse behavior for long code blocks. Measures the rendered code
284
+ * through `preRef` and exposes whether the block is expandable, whether it
285
+ * should be visually truncated, and a toggle handler.
286
+ */
287
+ function useExpandable(options = {}) {
288
+ const { children, highlightedHtml, maxLines = 6 } = options;
289
+ const [isExpanded, setIsExpanded] = useState(false);
290
+ const [isExpandable, setIsExpandable] = useState(false);
291
+ const preRef = useRef(null);
292
+ const shouldTruncate = isExpandable && !isExpanded;
293
+ const toggle = () => setIsExpanded((prev) => !prev);
294
+ useEffect(() => {
295
+ const node = preRef.current;
296
+ if (!node) {
297
+ setIsExpandable(false);
298
+ return;
299
+ }
300
+ const lines = (node.textContent ?? "").trim().split("\n").length;
301
+ const hasOverflow = "scrollHeight" in node && "clientHeight" in node && Number(node.scrollHeight) > Number(node.clientHeight);
302
+ setIsExpandable(lines > maxLines || hasOverflow);
303
+ }, [
304
+ children,
305
+ highlightedHtml,
306
+ maxLines
307
+ ]);
308
+ return {
316
309
  isExpanded,
317
310
  setIsExpanded,
318
311
  isExpandable,
319
- preRef,
320
- handleCopy,
321
- shouldTruncate: isExpandable && !isExpanded,
322
- isHighlighted,
323
- effectiveHighlightedHtml,
324
- effectiveTitle,
325
- lang,
326
- showCodeBlockFeedback,
312
+ shouldTruncate,
313
+ toggle,
314
+ preRef
315
+ };
316
+ }
317
+
318
+ //#endregion
319
+ //#region src/client/components/mdx/use-code-block-feedback.ts
320
+ /**
321
+ * Thumbs-up / thumbs-down feedback for code blocks. Only enabled when the
322
+ * `feedback.custom` integration is configured in the site config. The submit
323
+ * behavior can be overridden via `submitFeedback`.
324
+ */
325
+ function useCodeBlockFeedback(options = {}) {
326
+ const { plain = false, lang = "", submitFeedback } = options;
327
+ const [rated, setRated] = useState(null);
328
+ const customConfig = useConfig().integrations?.feedback?.custom;
329
+ const enabled = !!(customConfig?.enabled && !plain);
330
+ return {
327
331
  rated,
328
- handleRate
332
+ handleRate: useCallback(async (type, code) => {
333
+ if (rated) return;
334
+ setRated(type);
335
+ const snippet = code ? code.trim().slice(0, 100) + (code.length > 100 ? "..." : "") : "";
336
+ const blockId = `Code Block (${lang || "plain"}): \`${snippet}\``;
337
+ const payload = {
338
+ rating: type === "up" ? "good" : "bad",
339
+ comment: `Rated code block: ${type === "up" ? "Helpful" : "Unhelpful"}`,
340
+ path: window.location.pathname,
341
+ title: document.title,
342
+ blockId,
343
+ lang,
344
+ snippet
345
+ };
346
+ try {
347
+ if (submitFeedback) {
348
+ await submitFeedback(payload);
349
+ return;
350
+ }
351
+ const endpoint = customConfig?.endpoint || "/api/feedback";
352
+ await fetch(endpoint, {
353
+ method: "POST",
354
+ headers: { "Content-Type": "application/json" },
355
+ body: JSON.stringify(payload)
356
+ });
357
+ } catch (err) {
358
+ console.error("Failed to submit code block feedback:", err);
359
+ }
360
+ }, [
361
+ rated,
362
+ lang,
363
+ customConfig?.endpoint,
364
+ submitFeedback
365
+ ]),
366
+ enabled
329
367
  };
330
368
  }
331
369
 
332
370
  //#endregion
333
371
  //#region src/client/components/mdx/code-block.tsx
334
- const CopyButton = ({ copied, handleCopy }) => {
372
+ const CopyButton = ({ copied, onCopy, className }) => {
335
373
  return /* @__PURE__ */ jsx(Tooltip, {
336
374
  content: copied ? "Copied!" : "Copy code",
337
375
  children: /* @__PURE__ */ jsx(Button, {
338
- onPress: handleCopy,
339
- className: cn("grid place-items-center size-8 bg-transparent outline-none cursor-pointer transition-all duration-200 hover:scale-110 active:scale-95 [&>svg]:size-4 [&>svg]:stroke-2 z-10", copied ? "text-emerald-400" : "text-muted hover:text-body"),
376
+ onPress: onCopy,
377
+ className: cn("grid place-items-center size-8 bg-transparent outline-none cursor-pointer transition-all duration-200 hover:scale-110 active:scale-95 [&>svg]:size-4 [&>svg]:stroke-2 z-10", copied ? "text-emerald-400" : "text-muted hover:text-body", className),
340
378
  "aria-label": "Copy code",
341
379
  children: copied ? /* @__PURE__ */ jsx(Check, { size: 20 }) : /* @__PURE__ */ jsx(Copy, { size: 20 })
342
380
  })
343
381
  });
344
382
  };
345
- const CodeBlockFeedback = ({ rated, onRate }) => {
383
+ const CodeBlockFeedback = ({ rated, onRate, className }) => {
346
384
  return /* @__PURE__ */ jsxs("div", {
347
- className: "flex items-center gap-0.5 border-r border-subtle pr-1.5 mr-1",
385
+ className: cn("flex items-center gap-0.5 border-r border-subtle pr-1.5 mr-1", className),
348
386
  children: [/* @__PURE__ */ jsx(Tooltip, {
349
387
  content: rated === "up" ? "Helpful!" : "This code is helpful",
350
388
  children: /* @__PURE__ */ jsx(Button, {
351
- onPress: (() => onRate("up")),
389
+ onPress: () => onRate("up"),
352
390
  isDisabled: rated !== null,
353
391
  className: cn("grid place-items-center size-8 bg-transparent outline-none cursor-pointer transition-all duration-200 hover:scale-110 active:scale-95 [&>svg]:size-4 [&>svg]:stroke-2 z-10", rated === "up" ? "text-emerald-500 dark:text-emerald-400" : rated === "down" ? "opacity-30 cursor-not-allowed text-muted" : "text-muted hover:text-body"),
354
392
  "aria-label": "Mark as helpful",
355
393
  children: /* @__PURE__ */ jsx("svg", {
356
394
  xmlns: "http://www.w3.org/2000/svg",
395
+ role: "img",
396
+ "aria-label": "Thumb up",
357
397
  viewBox: "0 0 24 24",
358
398
  fill: "none",
359
399
  stroke: "currentColor",
@@ -366,12 +406,14 @@ const CodeBlockFeedback = ({ rated, onRate }) => {
366
406
  }), /* @__PURE__ */ jsx(Tooltip, {
367
407
  content: rated === "down" ? "Unhelpful" : "This code is unhelpful",
368
408
  children: /* @__PURE__ */ jsx(Button, {
369
- onPress: (() => onRate("down")),
409
+ onPress: () => onRate("down"),
370
410
  isDisabled: rated !== null,
371
411
  className: cn("grid place-items-center size-8 bg-transparent outline-none cursor-pointer transition-all duration-200 hover:scale-110 active:scale-95 [&>svg]:size-4 [&>svg]:stroke-2 z-10", rated === "down" ? "text-rose-500 dark:text-rose-400" : rated === "up" ? "opacity-30 cursor-not-allowed text-muted" : "text-muted hover:text-body"),
372
412
  "aria-label": "Mark as unhelpful",
373
413
  children: /* @__PURE__ */ jsx("svg", {
374
414
  xmlns: "http://www.w3.org/2000/svg",
415
+ role: "img",
416
+ "aria-label": "Thumb down",
375
417
  viewBox: "0 0 24 24",
376
418
  fill: "none",
377
419
  stroke: "currentColor",
@@ -385,49 +427,51 @@ const CodeBlockFeedback = ({ rated, onRate }) => {
385
427
  });
386
428
  };
387
429
  function CodeBlock(props) {
388
- const { children, hideCopy = false, highlightedHtml, "data-highlighted-html": dataHighlightedHtml, title, "data-title": dataTitle, "data-lang": dataLang, plain = false, lineNumbers, showLineNumbers, wordWrap, "word-wrap": wordWrapHyphen, metastring, ...rest } = props;
389
- const { className: shikiClassName, ...cleanRest } = rest;
390
- const { copied, isExpanded, setIsExpanded, isExpandable, preRef, handleCopy, shouldTruncate, isHighlighted, effectiveHighlightedHtml, effectiveTitle, showCodeBlockFeedback, rated, handleRate } = useCodeBlock(props);
430
+ const { children, hideCopy = false, highlightedHtml, "data-highlighted-html": dataHighlightedHtml, "data-highlighted": dataHighlighted, title, "data-title": dataTitle, lang: langProp, "data-lang": dataLang, plain = false, className: shikiClassName, lineNumbers, showLineNumbers, wordWrap, "word-wrap": wordWrapHyphen, metastring, feedbackClassName, copyButtonClassName, ...rest } = props;
431
+ const lang = langProp || dataLang || "";
432
+ const isHighlighted = dataHighlighted === "true" || typeof shikiClassName === "string" && shikiClassName.includes("shiki");
433
+ const rawHighlightedHtml = highlightedHtml || dataHighlightedHtml;
434
+ const effectiveHighlightedHtml = typeof rawHighlightedHtml === "string" ? rawHighlightedHtml.replace(/<span class="line">\s*(?:<span[^>]*>\s*<\/span>)?\s*<\/span>\s*(<\/code>\s*<\/pre>)/g, "$1") : rawHighlightedHtml;
435
+ const effectiveTitle = title || dataTitle;
436
+ const { copied, handleCopy } = useCopyButton();
437
+ const { isExpanded, isExpandable, shouldTruncate, toggle, preRef } = useExpandable({
438
+ children,
439
+ highlightedHtml: effectiveHighlightedHtml
440
+ });
441
+ const { rated, handleRate, enabled: showCodeBlockFeedback } = useCodeBlockFeedback({
442
+ plain,
443
+ lang
444
+ });
445
+ const onCopy = () => handleCopy(preRef.current?.textContent ?? "");
446
+ const onRate = (type) => handleRate(type, preRef.current?.textContent ?? "");
391
447
  return /* @__PURE__ */ jsxs(CodeBlock$1, {
392
448
  plain,
393
- className: props.className,
449
+ className: shikiClassName,
394
450
  children: [(effectiveTitle || !hideCopy) && /* @__PURE__ */ jsxs(CodeBlockHeader, {
395
451
  className: cn({ "absolute top-2 left-0 w-full": !effectiveTitle }),
396
- children: [/* @__PURE__ */ jsx(CodeBlockGroup, { children: effectiveTitle && /* @__PURE__ */ jsx("span", { children: effectiveTitle }) }), /* @__PURE__ */ jsxs("div", {
397
- className: "flex items-center gap-1 bg-(--color-code-bg) pl-2 z-10",
398
- children: [showCodeBlockFeedback && /* @__PURE__ */ jsx(CodeBlockFeedback, {
399
- rated,
400
- onRate: handleRate
401
- }), !hideCopy && /* @__PURE__ */ jsx(CopyButton, {
402
- copied,
403
- handleCopy
404
- })]
405
- })]
452
+ children: [/* @__PURE__ */ jsx(CodeBlockGroup, { children: effectiveTitle && /* @__PURE__ */ jsx("span", { children: effectiveTitle }) }), (showCodeBlockFeedback || !hideCopy) && /* @__PURE__ */ jsxs(CodeBlockActions, { children: [showCodeBlockFeedback && /* @__PURE__ */ jsx(CodeBlockFeedback, {
453
+ rated,
454
+ onRate,
455
+ className: feedbackClassName
456
+ }), !hideCopy && /* @__PURE__ */ jsx(CopyButton, {
457
+ copied,
458
+ onCopy,
459
+ className: copyButtonClassName
460
+ })] })]
406
461
  }), /* @__PURE__ */ jsxs(CodeBlockContent, {
407
462
  shouldTruncate,
408
- children: [effectiveHighlightedHtml ? /* @__PURE__ */ jsx("div", {
463
+ children: [/* @__PURE__ */ jsx(CodeBlockPre, {
409
464
  ref: preRef,
410
- className: "shiki-wrapper overflow-x-auto [&>pre]:m-0! [&>pre]:rounded-none! [&>pre]:border-none! [&>pre]:bg-inherit! [&>pre>code]:grid! [&>pre>code]:p-5! [&>pre>code]:text-[0.875rem]! [&>pre>code]:leading-[1.6]! [&>.shiki.shiki-themes]:bg-transparent!",
411
- dangerouslySetInnerHTML: { __html: effectiveHighlightedHtml }
412
- }) : /* @__PURE__ */ jsx("pre", {
413
- ref: preRef,
414
- className: cn("m-0! rounded-none! border-none! bg-transparent!", "text-[0.875rem] leading-[1.6] overflow-x-auto", shikiClassName, {
415
- "p-0! [&>code]:grid! [&>code]:p-5! [&>code]:bg-transparent!": isHighlighted,
416
- "p-5!": !isHighlighted
417
- }),
418
- ...cleanRest,
465
+ highlightedHtml: effectiveHighlightedHtml,
466
+ isHighlighted,
467
+ className: shikiClassName,
468
+ ...rest,
419
469
  children
420
- }), isExpandable && /* @__PURE__ */ jsx("div", {
421
- className: cn({
422
- "absolute bottom-0 inset-x-0 h-32 flex items-end justify-center pb-4 z-10": shouldTruncate,
423
- "relative flex justify-center pb-4 pt-1 -mt-4": !shouldTruncate
424
- }),
425
- style: shouldTruncate ? { backgroundImage: "linear-gradient(to top, var(--color-code-bg) 10%, transparent)" } : void 0,
426
- children: /* @__PURE__ */ jsx(Button, {
427
- onPress: () => setIsExpanded(!isExpanded),
428
- className: "rounded-full bg-surface border border-subtle px-5 py-2 text-[0.8125rem] font-medium text-body outline-none cursor-pointer transition-all hover:bg-soft hover:-translate-y-px backdrop-blur-md",
429
- children: isExpanded ? "Show less" : "Expand code"
430
- })
470
+ }), /* @__PURE__ */ jsx(CodeBlockExpand, {
471
+ isExpandable,
472
+ shouldTruncate,
473
+ isExpanded,
474
+ onToggle: toggle
431
475
  })]
432
476
  })]
433
477
  });
@@ -435,15 +479,15 @@ function CodeBlock(props) {
435
479
 
436
480
  //#endregion
437
481
  //#region src/client/components/mdx/image.tsx
438
- const Image = ({ src, alt, title, theme, className, ...props }) => {
482
+ const Image = ({ src, alt, title, theme, className, figureClassName, figureInnerClassName, figcaptionClassName, ...props }) => {
439
483
  const { theme: themeContext } = useTheme();
440
484
  if (!src) return null;
441
485
  if (theme !== themeContext) return null;
442
486
  const caption = title || alt;
443
487
  return /* @__PURE__ */ jsxs("figure", {
444
- className: "my-6 sm:my-8 flex flex-col items-center justify-center group not-prose",
488
+ className: cn("my-6 sm:my-8 flex flex-col items-center justify-center group not-prose", figureClassName),
445
489
  children: [/* @__PURE__ */ jsx("div", {
446
- className: "relative w-full overflow-hidden rounded-lg sm:rounded-2xl border border-subtle bg-soft/30 transition-all duration-300 sm:max-w-[85%] lg:max-w-full",
490
+ className: cn("relative w-full overflow-hidden rounded-lg sm:rounded-2xl border border-subtle bg-soft/30 transition-all duration-300 sm:max-w-[85%] lg:max-w-full", figureInnerClassName),
447
491
  children: /* @__PURE__ */ jsx(Image$1, {
448
492
  src,
449
493
  alt: alt || "",
@@ -454,7 +498,7 @@ const Image = ({ src, alt, title, theme, className, ...props }) => {
454
498
  ...props
455
499
  })
456
500
  }), caption && /* @__PURE__ */ jsx("figcaption", {
457
- className: "mt-2 sm:mt-3 text-center text-xs sm:text-sm text-muted font-medium select-none tracking-wide opacity-90 sm:opacity-80 group-hover:opacity-100 transition-opacity duration-300 px-2",
501
+ className: cn("mt-2 sm:mt-3 text-center text-xs sm:text-sm text-muted font-medium select-none tracking-wide opacity-90 sm:opacity-80 group-hover:opacity-100 transition-opacity duration-300 px-2", figcaptionClassName),
458
502
  children: caption
459
503
  })]
460
504
  });
@@ -466,7 +510,7 @@ const ImageComponents = {
466
510
 
467
511
  //#endregion
468
512
  //#region src/client/components/mdx/card.tsx
469
- function Card({ className, title, icon, href, children, ...props }) {
513
+ function Card({ className, title, icon, href, children, headerClassName, iconClassName, titleClassName, bodyClassName, ...props }) {
470
514
  const cardRef = useRef(null);
471
515
  const handleMouseMove = (e) => {
472
516
  if (!cardRef.current) return;
@@ -477,7 +521,7 @@ function Card({ className, title, icon, href, children, ...props }) {
477
521
  cardRef.current.style.setProperty("--mouse-y", `${y}px`);
478
522
  };
479
523
  const Wrapper = href ? "a" : "div";
480
- const spotlightColor = "var(--color-primary-500, #eb5828)";
524
+ const spotlightColor = "var(--color-primary-500)";
481
525
  return /* @__PURE__ */ jsxs(Wrapper, {
482
526
  ref: cardRef,
483
527
  href,
@@ -500,17 +544,17 @@ function Card({ className, title, icon, href, children, ...props }) {
500
544
  }
501
545
  }),
502
546
  /* @__PURE__ */ jsxs("div", {
503
- className: "relative z-10 flex items-center gap-3",
547
+ className: cn("relative z-10 flex items-center gap-3", headerClassName),
504
548
  children: [icon && /* @__PURE__ */ jsx("div", {
505
- className: cn("shrink-0 transition-transform duration-500 group-hover:rotate-15 group-hover:scale-110 flex items-center justify-center text-muted group-hover:text-primary-500", "[&>svg]:w-6 [&>svg]:h-6 [&>svg]:stroke-[1.5]"),
549
+ className: cn("shrink-0 transition-transform duration-500 group-hover:rotate-15 group-hover:scale-110 flex items-center justify-center text-muted group-hover:text-primary-500", "[&>svg]:w-6 [&>svg]:h-6 [&>svg]:stroke-[1.5]", iconClassName),
506
550
  children: icon
507
551
  }), title && /* @__PURE__ */ jsx("h3", {
508
- className: "font-semibold text-base m-0 leading-none text-body",
552
+ className: cn("font-semibold text-base m-0 leading-none text-body", titleClassName),
509
553
  children: title
510
554
  })]
511
555
  }),
512
556
  /* @__PURE__ */ jsx("div", {
513
- className: "relative z-10 text-[0.875rem] leading-[1.6] opacity-90 prose prose-neutral dark:prose-invert max-w-none [&>p]:m-0 [&>p+p]:mt-2",
557
+ className: cn("relative z-10 text-[0.875rem] leading-[1.6] opacity-90 prose prose-neutral dark:prose-invert max-w-none [&>p]:m-0 [&>p+p]:mt-2", bodyClassName),
514
558
  children
515
559
  })
516
560
  ]
@@ -538,7 +582,7 @@ function Cards({ children, className, cols = 2, ...props }) {
538
582
  * A subtle display for when the page was last updated.
539
583
  * Small, opaque, and positioned at the bottom of the content with a thin top border divider.
540
584
  */
541
- function LastUpdated({ date }) {
585
+ function LastUpdated({ date, className }) {
542
586
  if (!date) return null;
543
587
  const d = new Date(date);
544
588
  if (Number.isNaN(d.getTime())) return null;
@@ -548,7 +592,7 @@ function LastUpdated({ date }) {
548
592
  day: "numeric"
549
593
  });
550
594
  return /* @__PURE__ */ jsxs("div", {
551
- className: "mt-16 pt-6 border-t border-subtle flex items-center justify-between text-xs text-muted select-none",
595
+ className: cn("mt-16 pt-6 border-t border-subtle flex items-center justify-between text-xs text-muted select-none", className),
552
596
  children: [/* @__PURE__ */ jsx("span", {}), /* @__PURE__ */ jsxs("span", {
553
597
  className: "italic",
554
598
  children: ["Last updated on ", formattedDate]
@@ -672,18 +716,17 @@ const VARIANT_DEFAULT_ICON = {
672
716
  warning: AlertTriangle,
673
717
  danger: AlertCircle
674
718
  };
675
- function TimelineRoot({ children, className, compact = false, ...props }) {
719
+ function TimelineRoot({ children, className, compact = false, connectorClassName, ...props }) {
676
720
  return /* @__PURE__ */ jsxs("ol", {
677
- role: "list",
678
721
  className: cn("relative my-8 ms-3", compact ? "space-y-3" : "space-y-7", className),
679
722
  ...props,
680
723
  children: [/* @__PURE__ */ jsx("span", {
681
724
  "aria-hidden": "true",
682
- className: "pointer-events-none absolute top-3 bottom-3 start-[5.5px] w-px bg-subtle"
725
+ className: cn("pointer-events-none absolute top-3 bottom-3 start-[5.5px] w-px bg-subtle", connectorClassName)
683
726
  }), children]
684
727
  });
685
728
  }
686
- function TimelineItem({ date, title, badge, icon, variant = "primary", locale, className, children, ...props }) {
729
+ function TimelineItem({ date, title, badge, icon, variant = "primary", locale, className, children, dotClassName, headerClassName, timeClassName, badgeClassName, titleClassName, bodyClassName, ...props }) {
687
730
  const palette = VARIANT_PALETTE[variant] ?? VARIANT_PALETTE.primary;
688
731
  const formatted = formatDate(date, locale);
689
732
  const badgeCfg = normalizeBadge(badge);
@@ -707,26 +750,26 @@ function TimelineItem({ date, title, badge, icon, variant = "primary", locale, c
707
750
  children: [
708
751
  /* @__PURE__ */ jsx("span", {
709
752
  ...a11yProps,
710
- className: cn("absolute start-0 top-1 flex items-center justify-center w-3 h-3 rounded-full", "bg-surface border-2 shadow-sm", palette.ring),
753
+ className: cn("absolute start-0 top-1 flex items-center justify-center w-3 h-3 rounded-full", "bg-surface border-2 shadow-sm", palette.ring, dotClassName),
711
754
  children: dot
712
755
  }),
713
756
  /* @__PURE__ */ jsxs("div", {
714
- className: "flex items-center gap-2 flex-wrap mb-1",
757
+ className: cn("flex items-center gap-2 flex-wrap mb-1", headerClassName),
715
758
  children: [formatted && /* @__PURE__ */ jsx("time", {
716
759
  dateTime: date instanceof Date ? date.toISOString() : new Date(date).toISOString(),
717
- className: "text-[11px] uppercase tracking-wider font-mono tabular-nums text-muted select-none",
760
+ className: cn("text-[11px] uppercase tracking-wider font-mono tabular-nums text-muted select-none", timeClassName),
718
761
  children: formatted
719
762
  }), badgeCfg && /* @__PURE__ */ jsx("span", {
720
- className: cn("inline-flex items-center px-2 py-0.5 rounded-full text-[10px] font-bold uppercase tracking-wide border", palette.badgeBg, palette.badgeText, palette.badgeBorder),
763
+ className: cn("inline-flex items-center px-2 py-0.5 rounded-full text-[10px] font-bold uppercase tracking-wide border", palette.badgeBg, palette.badgeText, palette.badgeBorder, badgeClassName),
721
764
  children: badgeCfg.text
722
765
  })]
723
766
  }),
724
767
  /* @__PURE__ */ jsx("h3", {
725
- className: "text-base font-semibold text-body m-0 leading-snug",
768
+ className: cn("text-base font-semibold text-body m-0 leading-snug", titleClassName),
726
769
  children: title
727
770
  }),
728
771
  children && /* @__PURE__ */ jsx("div", {
729
- className: "mt-2 text-[0.9rem] leading-[1.6] text-paragraph prose prose-neutral dark:prose-invert max-w-none [&>p]:m-0 [&>p+p]:mt-2 [&_a]:text-primary-500 [&_a]:no-underline hover:[&_a]:underline",
772
+ className: cn("mt-2 text-[0.9rem] leading-[1.6] text-paragraph prose prose-neutral dark:prose-invert max-w-none [&>p]:m-0 [&>p+p]:mt-2 [&_a]:text-primary-500 [&_a]:no-underline hover:[&_a]:underline", bodyClassName),
730
773
  children
731
774
  })
732
775
  ]
@@ -750,4 +793,4 @@ const mdx_components_default = {
750
793
  };
751
794
 
752
795
  //#endregion
753
- export { copyToClipboard as i, Timeline as n, useCodeBlock as r, mdx_components_default as t };
796
+ export { useCopyButton as a, useExpandable as i, Timeline as n, copyToClipboard as o, useCodeBlockFeedback as r, mdx_components_default as t };