docstra 1.6.3 → 1.7.1

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.
@@ -36,6 +36,7 @@ __export(client_exports, {
36
36
  DocstraHeader: () => DocstraHeader,
37
37
  DocstraPage: () => DocstraPage,
38
38
  DocstraProvider: () => DocstraProvider,
39
+ DocstraSearchBox: () => DocstraSearchBox,
39
40
  DocstraSidebar: () => DocstraSidebar,
40
41
  DocstraTOC: () => DocstraTOC,
41
42
  useDocstra: () => useDocstra
@@ -46,7 +47,7 @@ module.exports = __toCommonJS(client_exports);
46
47
  var import_react2 = require("react");
47
48
  var import_react_toast_msg = require("react-toast-msg");
48
49
 
49
- // src/client/search-box.tsx
50
+ // src/client/components/search-box.tsx
50
51
  var import_lucide_react = require("lucide-react");
51
52
 
52
53
  // src/utils/cn.ts
@@ -56,7 +57,7 @@ function cn(...inputs) {
56
57
  return (0, import_tailwind_merge.twMerge)((0, import_clsx.default)(inputs));
57
58
  }
58
59
 
59
- // src/client/search-box.tsx
60
+ // src/client/components/search-box.tsx
60
61
  var import_react = require("react");
61
62
  var import_fuse = __toESM(require("fuse.js"));
62
63
  var import_link = __toESM(require("next/link"));
@@ -80,23 +81,26 @@ function DocstraSearchBox() {
80
81
  };
81
82
  }, []);
82
83
  const fuse = (0, import_react.useMemo)(() => {
84
+ if (!docs || docs.length === 0) return null;
83
85
  return new import_fuse.default(docs, {
84
- keys: ["frontmatter.title", "frontmatter.description", "headings", "info.slug"],
86
+ keys: ["data.metadata.title", "data.metadata.description"],
85
87
  threshold: 0.3
86
88
  });
87
89
  }, [docs]);
88
90
  (0, import_react.useEffect)(() => {
89
- if (searchQuery) {
91
+ if (searchQuery && fuse) {
90
92
  const results = fuse.search(searchQuery);
91
93
  setSearchResults(results.map((r) => r.item));
94
+ } else if (!searchQuery) {
95
+ setSearchResults([]);
92
96
  }
93
- }, [searchQuery]);
97
+ }, [searchQuery, fuse]);
94
98
  const onSearchHandler = (e) => {
95
99
  e.preventDefault();
96
100
  if (searchQuery) {
97
101
  const component = searchResults[0];
98
102
  if (component) {
99
- router.push(`/docs/${component.info.slug}`);
103
+ router.push(`/docs/${component.slug}`);
100
104
  setSearchQuery("");
101
105
  setOpenSearchBox(false);
102
106
  }
@@ -159,18 +163,18 @@ function DocstraSearchBox() {
159
163
  searchResults.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
160
164
  import_link.default,
161
165
  {
162
- href: `/docs/${item.info.slug.replace("index", "")}`,
163
- onNavigate: () => {
166
+ href: `/docs/${item.slug === "index" ? "" : item.slug}`,
167
+ onClick: () => {
164
168
  setOpenSearchBox(false);
165
169
  setSearchQuery("");
166
170
  },
167
171
  className: "w-full hover:bg-gray-100/70 border-b border-slate-200 transition-all py-4 px-4 last:mb-2",
168
172
  children: [
169
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", { className: "font-medium", children: item.frontmatter.title }),
170
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm mt-1 text-gray-500", children: item.frontmatter.description })
173
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", { className: "font-medium", children: item.data?.metadata?.title || item.slug }),
174
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm mt-1 text-gray-500", children: item.data?.metadata?.description })
171
175
  ]
172
176
  },
173
- item.info.slug
177
+ item.slug
174
178
  )),
175
179
  searchQuery && searchResults.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-gray-500 text-center py-20", children: "No results found" })
176
180
  ]
@@ -190,8 +194,15 @@ function DocstraProvider({
190
194
  children,
191
195
  docstraConfig,
192
196
  docs,
193
- pageData
197
+ pageData: initialPageData,
198
+ slug
194
199
  }) {
200
+ const pageData = initialPageData || (() => {
201
+ const normalizedSlug = Array.isArray(slug) ? slug.length === 0 ? "index" : slug.join("/") : slug || "index";
202
+ return docs.find((d) => d.slug === normalizedSlug);
203
+ })();
204
+ if (!pageData) {
205
+ }
195
206
  const [openSidebar, setOpenSidebar] = (0, import_react2.useState)(false);
196
207
  const [openSearchBox, setOpenSearchBox] = (0, import_react2.useState)(false);
197
208
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
@@ -220,7 +231,7 @@ function useDocstra() {
220
231
  return ctx;
221
232
  }
222
233
 
223
- // src/client/header.tsx
234
+ // src/client/components/header.tsx
224
235
  var import_lucide_react2 = require("lucide-react");
225
236
  var import_react3 = require("react");
226
237
  var import_link2 = __toESM(require("next/link"));
@@ -258,7 +269,7 @@ function DocstraHeader() {
258
269
  ] });
259
270
  }
260
271
 
261
- // src/client/sidebar.tsx
272
+ // src/client/components/sidebar.tsx
262
273
  var import_lucide_react3 = require("lucide-react");
263
274
  var import_navigation2 = require("next/navigation");
264
275
 
@@ -269,7 +280,7 @@ function getIcon(name) {
269
280
  return Icons[name];
270
281
  }
271
282
 
272
- // src/client/sidebar.tsx
283
+ // src/client/components/sidebar.tsx
273
284
  var import_link3 = __toESM(require("next/link"));
274
285
  var import_jsx_runtime4 = require("react/jsx-runtime");
275
286
  function DocstraSidebar() {
@@ -339,68 +350,208 @@ function DocstraSidebar() {
339
350
  ] });
340
351
  }
341
352
 
342
- // src/client/docs-page.tsx
353
+ // src/client/components/docstra-toc.tsx
354
+ var import_lucide_react4 = require("lucide-react");
355
+ var import_navigation3 = require("next/navigation");
356
+ var import_react4 = require("react");
343
357
  var import_jsx_runtime5 = require("react/jsx-runtime");
344
- function DocstraPage({ children }) {
345
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "min-h-screen flex bg-white w-full", children });
358
+ function DocstraTOC() {
359
+ const [activeId, setActiveId] = (0, import_react4.useState)(null);
360
+ const [scrollDir, setScrollDir] = (0, import_react4.useState)("down");
361
+ const [isScrolled, setIsScrolled] = (0, import_react4.useState)(false);
362
+ const [isManualClick, setIsManualClick] = (0, import_react4.useState)(false);
363
+ const { docstraConfig, pageData } = useDocstra();
364
+ const mdxFilePath = pageData?.path;
365
+ const headings = pageData?.data?.tableOfContents || [];
366
+ const lastScrollY = (0, import_react4.useRef)(0);
367
+ const observerRef = (0, import_react4.useRef)(null);
368
+ const pathname = (0, import_navigation3.usePathname)();
369
+ const baseUrlOfGithub = docstraConfig?.githubRepo || "https://github.com/sudhucodes/docstra";
370
+ const githubLink = `${baseUrlOfGithub}/edit/main/${mdxFilePath}`;
371
+ (0, import_react4.useEffect)(() => {
372
+ const onScroll = () => {
373
+ const currentY = window.scrollY;
374
+ setScrollDir(currentY > lastScrollY.current ? "down" : "up");
375
+ setIsScrolled(currentY > 100);
376
+ lastScrollY.current = currentY;
377
+ };
378
+ window.addEventListener("scroll", onScroll);
379
+ return () => window.removeEventListener("scroll", onScroll);
380
+ }, []);
381
+ (0, import_react4.useEffect)(() => {
382
+ const allHeadings = document.querySelectorAll(`h2[id], h3[id]`);
383
+ const observer = new IntersectionObserver(
384
+ (entries) => {
385
+ if (isManualClick) return;
386
+ const visible = entries.filter((e) => e.isIntersecting).sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top);
387
+ if (visible.length > 0) {
388
+ const nextActive = scrollDir === "down" ? visible[0].target.id : visible[visible.length - 1].target.id;
389
+ setActiveId(nextActive);
390
+ }
391
+ },
392
+ {
393
+ rootMargin: "-40% 0px -50% 0px",
394
+ threshold: [0, 0.2, 0.6, 1]
395
+ }
396
+ );
397
+ allHeadings.forEach((heading) => observer.observe(heading));
398
+ observerRef.current = observer;
399
+ return () => observer.disconnect();
400
+ }, [pathname, scrollDir, isManualClick]);
401
+ (0, import_react4.useEffect)(() => {
402
+ const scrolled = window.scrollY > 100;
403
+ !scrolled && setActiveId(headings[0]?.id);
404
+ }, [headings]);
405
+ (0, import_react4.useEffect)(() => {
406
+ if (!activeId) return;
407
+ const container = document.querySelector(".toc-scroll-container");
408
+ if (!container) return;
409
+ const activeLink = container.querySelector(`a[href="#${activeId}"]`);
410
+ if (!activeLink) return;
411
+ const containerRect = container.getBoundingClientRect();
412
+ const linkRect = activeLink.getBoundingClientRect();
413
+ const isOutOfView = linkRect.top < containerRect.top || linkRect.bottom > containerRect.bottom;
414
+ if (isOutOfView) {
415
+ activeLink.scrollIntoView({
416
+ behavior: "smooth",
417
+ block: "nearest"
418
+ });
419
+ }
420
+ }, [activeId]);
421
+ (0, import_react4.useEffect)(() => {
422
+ if (!isManualClick) return;
423
+ const timer = setTimeout(() => setIsManualClick(false), 800);
424
+ return () => clearTimeout(timer);
425
+ }, [isManualClick]);
426
+ const handleClick = (id) => {
427
+ setActiveId(id);
428
+ setIsManualClick(true);
429
+ };
430
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("aside", { className: "sticky top-18 text-sm hidden xl:block text-gray-500 shrink-0 h-[calc(100svh-72px)] w-64 border-l border-gray-200 p-6 overflow-y-auto", children: [
431
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "flex items-center gap-2 mb-5", children: [
432
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.TextAlignStartIcon, { className: "size-4" }),
433
+ "On this page"
434
+ ] }),
435
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "toc-scroll-container max-h-3/4 overflow-y-auto scrollbar-none", children: headings.map((h2) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("li", { children: [
436
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TableOfContentsLink, { heading: h2, activeId, handleClick }),
437
+ h2.children.map((h3) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
438
+ TableOfContentsLink,
439
+ {
440
+ heading: h3,
441
+ activeId,
442
+ handleClick,
443
+ className: "pl-8"
444
+ },
445
+ h3.id
446
+ ))
447
+ ] }, h2.id)) }),
448
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("hr", { className: "my-6 border-gray-200" }),
449
+ docstraConfig.editOnGithub && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
450
+ "a",
451
+ {
452
+ href: githubLink,
453
+ rel: "noopener noreferrer",
454
+ className: "flex items-center gap-2 text-gray-400 hover:text-gray-700 transition",
455
+ children: [
456
+ "Edit this page on GitHub",
457
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.SquareArrowOutUpRightIcon, { className: "size-4" })
458
+ ]
459
+ }
460
+ ),
461
+ isScrolled && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
462
+ "button",
463
+ {
464
+ onClick: () => scrollTo({ top: 0, behavior: "smooth" }),
465
+ className: "flex items-center cursor-pointer gap-2 mt-3 text-gray-400 hover:text-gray-700 transition",
466
+ children: [
467
+ "Scroll to top",
468
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.ArrowUpCircleIcon, { className: "size-4" })
469
+ ]
470
+ }
471
+ )
472
+ ] });
473
+ }
474
+ function TableOfContentsLink({ heading, activeId, handleClick, className }) {
475
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
476
+ "a",
477
+ {
478
+ href: `#${heading.id}`,
479
+ className: cn("border-l border-gray-200 block py-1.5 pl-4 transition-colors", className, {
480
+ "border-gray-800 text-gray-900 font-medium": activeId === heading.id,
481
+ "hover:text-gray-800 hover:border-gray-400": activeId !== heading.id
482
+ }),
483
+ onClick: () => handleClick(heading.id),
484
+ children: heading.text
485
+ }
486
+ );
346
487
  }
347
488
 
348
- // src/client/breadcrumbs.tsx
349
- var import_navigation3 = require("next/navigation");
350
- var import_lucide_react4 = require("lucide-react");
351
- var import_link4 = __toESM(require("next/link"));
489
+ // src/client/components/docs-page.tsx
352
490
  var import_jsx_runtime6 = require("react/jsx-runtime");
491
+ function DocstraPage({ children, toc = true, sidebar = true }) {
492
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "min-h-screen flex bg-white w-full", children: [
493
+ sidebar && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DocstraSidebar, {}),
494
+ children,
495
+ toc && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DocstraTOC, {})
496
+ ] });
497
+ }
498
+
499
+ // src/client/components/breadcrumbs.tsx
500
+ var import_navigation4 = require("next/navigation");
501
+ var import_lucide_react5 = require("lucide-react");
502
+ var import_link4 = __toESM(require("next/link"));
503
+ var import_jsx_runtime7 = require("react/jsx-runtime");
353
504
  function DocstraBreadcrumbs() {
354
- const pathname = (0, import_navigation3.usePathname)();
505
+ const pathname = (0, import_navigation4.usePathname)();
355
506
  const paths = pathname.split("/").filter(Boolean);
356
507
  const breadcrumbs = paths.map((segment, index) => {
357
508
  const href = "/" + paths.slice(0, index + 1).join("/");
358
509
  const title = segment.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
359
510
  return { href, title };
360
511
  });
361
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("nav", { "aria-label": "Breadcrumb", className: "flex text-sm items-center pb-4 text-gray-500", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("ol", { className: "flex items-center space-x-2", children: [
362
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_link4.default, { href: "/", className: "hover:text-gray-700 transition-colors font-medium", children: "Home" }) }),
512
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("nav", { "aria-label": "Breadcrumb", className: "flex text-sm items-center pb-4 text-gray-500", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("ol", { className: "flex items-center space-x-2", children: [
513
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_link4.default, { href: "/", className: "hover:text-gray-700 transition-colors font-medium", children: "Home" }) }),
363
514
  breadcrumbs.map((crumb, index) => {
364
515
  const isLast = index === breadcrumbs.length - 1;
365
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { className: "flex items-center space-x-2", children: [
366
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.ChevronRight, { className: "w-4 h-4 text-gray-400" }),
367
- isLast ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-gray-600 font-medium", children: crumb.title }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_link4.default, { href: crumb.href, className: "hover:text-gray-700 font-medium transition-colors", children: crumb.title })
516
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("li", { className: "flex items-center space-x-2", children: [
517
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react5.ChevronRight, { className: "w-4 h-4 text-gray-400" }),
518
+ isLast ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-gray-600 font-medium", children: crumb.title }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_link4.default, { href: crumb.href, className: "hover:text-gray-700 font-medium transition-colors", children: crumb.title })
368
519
  ] }, crumb.href);
369
520
  })
370
521
  ] }) });
371
522
  }
372
523
 
373
- // src/client/page-buttons.tsx
374
- var import_lucide_react5 = require("lucide-react");
375
- var import_react4 = require("react");
376
- var import_jsx_runtime7 = require("react/jsx-runtime");
524
+ // src/client/components/page-buttons.tsx
525
+ var import_lucide_react6 = require("lucide-react");
526
+ var import_react5 = require("react");
527
+ var import_jsx_runtime8 = require("react/jsx-runtime");
377
528
  function DocstraPageButtons() {
378
529
  const { pageData } = useDocstra();
379
- const [isCopied, setIsCopied] = (0, import_react4.useState)(false);
380
- const [href, setHref] = (0, import_react4.useState)(null);
381
- (0, import_react4.useEffect)(() => {
530
+ const [isCopied, setIsCopied] = (0, import_react5.useState)(false);
531
+ const [href, setHref] = (0, import_react5.useState)(null);
532
+ (0, import_react5.useEffect)(() => {
382
533
  setHref(window.location.href);
383
534
  }, []);
384
535
  const prompt = `Read from this URL: ${href} and explain it to me.`;
385
536
  const handleCopy = () => {
386
- navigator.clipboard.writeText(pageData?.rawContent);
537
+ navigator.clipboard.writeText(pageData?.data.raw || "");
387
538
  setIsCopied(true);
388
539
  setTimeout(() => setIsCopied(false), 3e3);
389
540
  };
390
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-3 mt-6", children: [
391
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
541
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3 mt-6", children: [
542
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
392
543
  "button",
393
544
  {
394
545
  title: "Copy Markdown",
395
546
  onClick: handleCopy,
396
547
  className: "flex items-center cursor-pointer gap-2 bg-gray-50 hover:bg-gray-100 border border-gray-200 px-3 py-2 rounded-md text-xs",
397
548
  children: [
398
- isCopied ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react5.CheckIcon, { className: "size-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react5.CopyIcon, { className: "size-3.5" }),
549
+ isCopied ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react6.CheckIcon, { className: "size-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react6.CopyIcon, { className: "size-3.5" }),
399
550
  "Copy Markdown"
400
551
  ]
401
552
  }
402
553
  ),
403
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
554
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
404
555
  "a",
405
556
  {
406
557
  title: "Ask ChatGPT",
@@ -413,26 +564,26 @@ function DocstraPageButtons() {
413
564
  ] });
414
565
  }
415
566
 
416
- // src/client/docstra-page-head.tsx
417
- var import_jsx_runtime8 = require("react/jsx-runtime");
567
+ // src/client/components/docstra-page-head.tsx
568
+ var import_jsx_runtime9 = require("react/jsx-runtime");
418
569
  function DocstraPageHead() {
419
570
  const { pageData } = useDocstra();
420
- const title = pageData?.frontmatter?.title;
421
- const description = pageData?.frontmatter?.description;
422
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
423
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h1", { className: "text-3xl font-bold", children: title }),
424
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "my-4 text-gray-500", children: description }),
425
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DocstraPageButtons, {}),
426
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("hr", { className: "my-10 border-gray-200" })
571
+ const title = pageData?.data?.metadata?.title;
572
+ const description = pageData?.data?.metadata?.description;
573
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
574
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { className: "text-3xl font-bold", children: title }),
575
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "my-4 text-gray-500", children: description }),
576
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(DocstraPageButtons, {}),
577
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("hr", { className: "my-10 border-gray-200" })
427
578
  ] });
428
579
  }
429
580
 
430
- // src/client/feedback.tsx
431
- var import_react5 = require("react");
432
- var import_lucide_react7 = require("lucide-react");
581
+ // src/client/components/feedback.tsx
582
+ var import_react6 = require("react");
583
+ var import_lucide_react8 = require("lucide-react");
433
584
 
434
- // src/client/input.tsx
435
- var import_jsx_runtime9 = require("react/jsx-runtime");
585
+ // src/client/components/input.tsx
586
+ var import_jsx_runtime10 = require("react/jsx-runtime");
436
587
  function Input({
437
588
  label,
438
589
  type,
@@ -445,12 +596,12 @@ function Input({
445
596
  className = "",
446
597
  optional = false
447
598
  }) {
448
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: cn("flex flex-col mt-4 w-full", className), children: [
449
- label && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { htmlFor: id, className: "font-medium w-max cursor-pointer", children: [
599
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: cn("flex flex-col mt-4 w-full", className), children: [
600
+ label && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { htmlFor: id, className: "font-medium w-max cursor-pointer", children: [
450
601
  label,
451
- optional && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-xs font-normal text-gray-400 ml-1 mt-1", children: "(Optional)" })
602
+ optional && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-xs font-normal text-gray-400 ml-1 mt-1", children: "(Optional)" })
452
603
  ] }),
453
- type === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
604
+ type === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
454
605
  "textarea",
455
606
  {
456
607
  id,
@@ -466,7 +617,7 @@ function Input({
466
617
  value,
467
618
  readOnly
468
619
  }
469
- ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
620
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
470
621
  "input",
471
622
  {
472
623
  type,
@@ -483,11 +634,11 @@ function Input({
483
634
  ] });
484
635
  }
485
636
 
486
- // src/client/button.tsx
487
- var import_lucide_react6 = require("lucide-react");
488
- var import_jsx_runtime10 = require("react/jsx-runtime");
637
+ // src/client/components/button.tsx
638
+ var import_lucide_react7 = require("lucide-react");
639
+ var import_jsx_runtime11 = require("react/jsx-runtime");
489
640
  function Button({ label, loading, type = "submit", className = "", disabled = false, onClick }) {
490
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
641
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
491
642
  "button",
492
643
  {
493
644
  type,
@@ -498,21 +649,21 @@ function Button({ label, loading, type = "submit", className = "", disabled = fa
498
649
  ),
499
650
  "aria-label": label,
500
651
  onClick,
501
- children: loading ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.Loader2Icon, { className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 animate-spin mx-auto" }) : label
652
+ children: loading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react7.Loader2Icon, { className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 animate-spin mx-auto" }) : label
502
653
  }
503
654
  );
504
655
  }
505
656
 
506
- // src/client/feedback.tsx
507
- var import_jsx_runtime11 = require("react/jsx-runtime");
657
+ // src/client/components/feedback.tsx
658
+ var import_jsx_runtime12 = require("react/jsx-runtime");
508
659
  function DocstraFeedback() {
509
- const [opinion, setOpinion] = (0, import_react5.useState)(null);
660
+ const [opinion, setOpinion] = (0, import_react6.useState)(null);
510
661
  const { docstraConfig } = useDocstra();
511
- const [message, setMessage] = (0, import_react5.useState)("");
512
- const [submitted, setSubmitted] = (0, import_react5.useState)(false);
513
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "border-y border-gray-200 mt-20 py-10 space-y-4", children: submitted ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
514
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "font-medium text-gray-900", children: "Thank you for your feedback!" }),
515
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
662
+ const [message, setMessage] = (0, import_react6.useState)("");
663
+ const [submitted, setSubmitted] = (0, import_react6.useState)(false);
664
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "border-y border-gray-200 mt-20 py-10 space-y-4", children: submitted ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
665
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "font-medium text-gray-900", children: "Thank you for your feedback!" }),
666
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
516
667
  Button,
517
668
  {
518
669
  onClick: () => {
@@ -524,28 +675,28 @@ function DocstraFeedback() {
524
675
  className: "w-max text-sm"
525
676
  }
526
677
  )
527
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
528
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "font-medium", children: "How is this guide?" }),
529
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex gap-2", children: [
530
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
678
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
679
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "font-medium", children: "How is this guide?" }),
680
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex gap-2", children: [
681
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
531
682
  "button",
532
683
  {
533
684
  onClick: () => setOpinion("good"),
534
685
  className: `flex items-center cursor-pointer gap-1 px-3 py-2 rounded-full border text-sm ${opinion === "good" ? "bg-green-100 text-green-600 border-green-300" : "border-gray-200 text-gray-600 hover:bg-gray-100"}`,
535
686
  children: [
536
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react7.ThumbsUp, { className: `size-4 ${opinion === "good" ? "fill-green-600" : "text-gray-500"}` }),
687
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.ThumbsUp, { className: `size-4 ${opinion === "good" ? "fill-green-600" : "text-gray-500"}` }),
537
688
  "Good"
538
689
  ]
539
690
  }
540
691
  ),
541
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
692
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
542
693
  "button",
543
694
  {
544
695
  onClick: () => setOpinion("bad"),
545
696
  className: `flex items-center cursor-pointer gap-1 px-3 py-2 rounded-full border text-sm ${opinion === "bad" ? "bg-red-100 text-red-600 border-red-300" : "border-gray-200 text-gray-600 hover:bg-gray-100"}`,
546
697
  children: [
547
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
548
- import_lucide_react7.ThumbsDown,
698
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
699
+ import_lucide_react8.ThumbsDown,
549
700
  {
550
701
  className: `size-4 pt-0.5 ${opinion === "bad" ? "fill-red-600" : "text-gray-500"}`
551
702
  }
@@ -555,14 +706,14 @@ function DocstraFeedback() {
555
706
  }
556
707
  )
557
708
  ] }),
558
- opinion && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
709
+ opinion && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
559
710
  "form",
560
711
  {
561
712
  className: "flex flex-col gap-2",
562
713
  action: `https://formsync.app/v1/s/${docstraConfig?.feedback?.formSyncFormID || ""}`,
563
714
  method: "POST",
564
715
  children: [
565
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
716
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
566
717
  Input,
567
718
  {
568
719
  value: message,
@@ -573,14 +724,14 @@ function DocstraFeedback() {
573
724
  type: "textarea"
574
725
  }
575
726
  ),
576
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "hidden", name: "opinion", value: opinion }),
577
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "hidden", name: "page", value: window.location.href }),
578
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "hidden", name: "_redirect", value: window.location.href }),
579
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Button, { label: "Submit", className: "w-max text-sm" }),
580
- docstraConfig?.feedback?.watermark !== false && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "text-left text-sm text-gray-500 mt-6", children: [
727
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { type: "hidden", name: "opinion", value: opinion }),
728
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { type: "hidden", name: "page", value: window.location.href }),
729
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { type: "hidden", name: "_redirect", value: window.location.href }),
730
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Button, { label: "Submit", className: "w-max text-sm" }),
731
+ docstraConfig?.feedback?.watermark !== false && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-left text-sm text-gray-500 mt-6", children: [
581
732
  "Powered by",
582
733
  " ",
583
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
734
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
584
735
  "a",
585
736
  {
586
737
  href: `https://www.formsync.app?utm_source=${docstraConfig?.githubRepo}`,
@@ -595,243 +746,75 @@ function DocstraFeedback() {
595
746
  ] }) });
596
747
  }
597
748
 
598
- // src/client/pagination.tsx
599
- var import_navigation4 = require("next/navigation");
749
+ // src/client/components/pagination.tsx
750
+ var import_navigation5 = require("next/navigation");
600
751
  var import_link5 = __toESM(require("next/link"));
601
- var import_lucide_react8 = require("lucide-react");
602
- var import_jsx_runtime12 = require("react/jsx-runtime");
752
+ var import_lucide_react9 = require("lucide-react");
753
+ var import_jsx_runtime13 = require("react/jsx-runtime");
603
754
  function DocstraPagination() {
604
- const pathname = (0, import_navigation4.usePathname)();
755
+ const pathname = (0, import_navigation5.usePathname)();
605
756
  const { docstraConfig } = useDocstra();
606
757
  const flatLinks = docstraConfig?.sidebar?.links?.flatMap((section) => section.items);
607
758
  const currentIndex = flatLinks.findIndex((item) => item.href === pathname);
608
759
  const prev = currentIndex > 0 ? flatLinks[currentIndex - 1] : null;
609
760
  const next = currentIndex < flatLinks.length - 1 ? flatLinks[currentIndex + 1] : null;
610
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col md:flex-row gap-2 justify-between py-10 mt-10", children: [
611
- prev ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
761
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col md:flex-row gap-2 justify-between py-10 mt-10", children: [
762
+ prev ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
612
763
  import_link5.default,
613
764
  {
614
765
  href: prev.href,
615
766
  className: "flex flex-col items-start gap-2 border border-gray-200 hover:bg-gray-50 py-3 md:min-w-62 pl-4 pr-10 rounded-lg text-sm",
616
767
  children: [
617
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-gray-500", children: "Previous" }),
618
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "flex items-center font-medium", children: [
619
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.ArrowLeftIcon, { className: "size-5 mr-2" }),
768
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-gray-500", children: "Previous" }),
769
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { className: "flex items-center font-medium", children: [
770
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.ArrowLeftIcon, { className: "size-5 mr-2" }),
620
771
  prev.name
621
772
  ] })
622
773
  ]
623
774
  }
624
- ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", {}),
625
- next ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
775
+ ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", {}),
776
+ next ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
626
777
  import_link5.default,
627
778
  {
628
779
  href: next.href,
629
780
  className: "flex flex-col items-end gap-2 border border-gray-200 hover:bg-gray-50 py-3 pr-4 md:min-w-62 pl-10 rounded-lg text-sm",
630
781
  children: [
631
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-gray-500", children: "Next" }),
632
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "flex items-center font-medium", children: [
782
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-gray-500", children: "Next" }),
783
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { className: "flex items-center font-medium", children: [
633
784
  next.name,
634
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.ArrowRightIcon, { className: "size-5 ml-2" })
785
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.ArrowRightIcon, { className: "size-5 ml-2" })
635
786
  ] })
636
787
  ]
637
788
  }
638
- ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", {})
789
+ ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", {})
639
790
  ] });
640
791
  }
641
792
 
642
- // src/client/docs-body.tsx
643
- var import_jsx_runtime13 = require("react/jsx-runtime");
793
+ // src/client/components/docs-body.tsx
794
+ var import_jsx_runtime14 = require("react/jsx-runtime");
644
795
  function DocstraBody({ children }) {
645
796
  const { docstraConfig } = useDocstra();
646
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("main", { className: "flex-1 px-4 md:px-8 py-10 max-w-full text-base/7", children: [
647
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DocstraBreadcrumbs, {}),
648
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DocstraPageHead, {}),
797
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("main", { className: "flex-1 px-4 md:px-8 py-10 max-w-full text-base/7", children: [
798
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraBreadcrumbs, {}),
799
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraPageHead, {}),
649
800
  children,
650
- docstraConfig?.feedback?.enabled && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DocstraFeedback, {}),
651
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DocstraPagination, {})
801
+ docstraConfig?.feedback?.enabled && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraFeedback, {}),
802
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraPagination, {})
652
803
  ] });
653
804
  }
654
805
 
655
- // src/client/docstra-toc.tsx
656
- var import_lucide_react9 = require("lucide-react");
657
- var import_navigation5 = require("next/navigation");
658
- var import_react6 = require("react");
659
-
660
- // src/utils/generate-id-from-text.ts
661
- function generateIdFromText(text) {
662
- return text.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
663
- }
664
-
665
- // src/utils/extract-headings-from-mdx.ts
666
- function extractHeadingsFromMdx(raw) {
667
- const lines = raw.split("\n");
668
- const results = [];
669
- let currentH2 = null;
670
- for (let line of lines) {
671
- line = line.trim();
672
- const h2Match = line.match(/^##\s+(.*)/);
673
- const h3Match = line.match(/^###\s+(.*)/);
674
- if (h2Match) {
675
- const text = h2Match[1].trim();
676
- const id = generateIdFromText(text);
677
- currentH2 = { id, text, children: [] };
678
- results.push(currentH2);
679
- }
680
- if (h3Match && currentH2) {
681
- const text = h3Match[1].trim();
682
- const id = generateIdFromText(text);
683
- currentH2.children.push({ id, text });
684
- }
685
- }
686
- return results;
687
- }
688
-
689
- // src/client/docstra-toc.tsx
690
- var import_jsx_runtime14 = require("react/jsx-runtime");
691
- function DocstraTOC() {
692
- const [headings, setHeadings] = (0, import_react6.useState)([]);
693
- const [activeId, setActiveId] = (0, import_react6.useState)(null);
694
- const [scrollDir, setScrollDir] = (0, import_react6.useState)("down");
695
- const [isScrolled, setIsScrolled] = (0, import_react6.useState)(false);
696
- const [isManualClick, setIsManualClick] = (0, import_react6.useState)(false);
697
- const { docstraConfig, pageData } = useDocstra();
698
- const mdxFilePath = pageData?.info.fullPath;
699
- const rawMdxContent = pageData?.content;
700
- const lastScrollY = (0, import_react6.useRef)(0);
701
- const observerRef = (0, import_react6.useRef)(null);
702
- const pathname = (0, import_navigation5.usePathname)();
703
- const baseUrlOfGithub = docstraConfig?.githubRepo || "https://github.com/sudhucodes/docstra";
704
- const githubLink = `${baseUrlOfGithub}/edit/main/${mdxFilePath}`;
705
- (0, import_react6.useEffect)(() => {
706
- const onScroll = () => {
707
- const currentY = window.scrollY;
708
- setScrollDir(currentY > lastScrollY.current ? "down" : "up");
709
- setIsScrolled(currentY > 100);
710
- lastScrollY.current = currentY;
711
- };
712
- window.addEventListener("scroll", onScroll);
713
- return () => window.removeEventListener("scroll", onScroll);
714
- }, []);
715
- (0, import_react6.useEffect)(() => {
716
- const structured = extractHeadingsFromMdx(rawMdxContent);
717
- setHeadings(structured);
718
- }, [rawMdxContent]);
719
- (0, import_react6.useEffect)(() => {
720
- const allHeadings = document.querySelectorAll(`h2[id], h3[id]`);
721
- const observer = new IntersectionObserver(
722
- (entries) => {
723
- if (isManualClick) return;
724
- const visible = entries.filter((e) => e.isIntersecting).sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top);
725
- if (visible.length > 0) {
726
- const nextActive = scrollDir === "down" ? visible[0].target.id : visible[visible.length - 1].target.id;
727
- setActiveId(nextActive);
728
- }
729
- },
730
- {
731
- rootMargin: "-40% 0px -50% 0px",
732
- threshold: [0, 0.2, 0.6, 1]
733
- }
734
- );
735
- allHeadings.forEach((heading) => observer.observe(heading));
736
- observerRef.current = observer;
737
- return () => observer.disconnect();
738
- }, [pathname, scrollDir, isManualClick]);
739
- (0, import_react6.useEffect)(() => {
740
- const scrolled = window.scrollY > 100;
741
- !scrolled && setActiveId(headings[0]?.id);
742
- }, [headings]);
743
- (0, import_react6.useEffect)(() => {
744
- if (!activeId) return;
745
- const container = document.querySelector(".toc-scroll-container");
746
- if (!container) return;
747
- const activeLink = container.querySelector(`a[href="#${activeId}"]`);
748
- if (!activeLink) return;
749
- const containerRect = container.getBoundingClientRect();
750
- const linkRect = activeLink.getBoundingClientRect();
751
- const isOutOfView = linkRect.top < containerRect.top || linkRect.bottom > containerRect.bottom;
752
- if (isOutOfView) {
753
- activeLink.scrollIntoView({
754
- behavior: "smooth",
755
- block: "nearest"
756
- });
757
- }
758
- }, [activeId]);
759
- (0, import_react6.useEffect)(() => {
760
- if (!isManualClick) return;
761
- const timer = setTimeout(() => setIsManualClick(false), 800);
762
- return () => clearTimeout(timer);
763
- }, [isManualClick]);
764
- const handleClick = (id) => {
765
- setActiveId(id);
766
- setIsManualClick(true);
767
- };
768
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("aside", { className: "sticky top-18 text-sm hidden xl:block text-gray-500 shrink-0 h-[calc(100svh-72px)] w-64 border-l border-gray-200 p-6 overflow-y-auto", children: [
769
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { className: "flex items-center gap-2 mb-5", children: [
770
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react9.TextAlignStartIcon, { className: "size-4" }),
771
- "On this page"
772
- ] }),
773
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ul", { className: "toc-scroll-container max-h-3/4 overflow-y-auto scrollbar-none", children: headings.map((h2) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("li", { children: [
774
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TableOfContentsLink, { heading: h2, activeId, handleClick }),
775
- h2.children.map((h3) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
776
- TableOfContentsLink,
777
- {
778
- heading: h3,
779
- activeId,
780
- handleClick,
781
- className: "pl-8"
782
- },
783
- h3.id
784
- ))
785
- ] }, h2.id)) }),
786
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("hr", { className: "my-6 border-gray-200" }),
787
- docstraConfig.editOnGithub && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
788
- "a",
789
- {
790
- href: githubLink,
791
- rel: "noopener noreferrer",
792
- className: "flex items-center gap-2 text-gray-400 hover:text-gray-700 transition",
793
- children: [
794
- "Edit this page on GitHub",
795
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react9.SquareArrowOutUpRightIcon, { className: "size-4" })
796
- ]
797
- }
798
- ),
799
- isScrolled && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
800
- "button",
801
- {
802
- onClick: () => scrollTo({ top: 0, behavior: "smooth" }),
803
- className: "flex items-center gap-2 mt-3 text-gray-400 hover:text-gray-700 transition",
804
- children: [
805
- "Scroll to top",
806
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react9.ArrowUpCircleIcon, { className: "size-4" })
807
- ]
808
- }
809
- )
810
- ] });
811
- }
812
- function TableOfContentsLink({ heading, activeId, handleClick, className }) {
813
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
814
- "a",
815
- {
816
- href: `#${heading.id}`,
817
- className: cn("border-l border-gray-200 block py-1.5 pl-4 transition-colors", className, {
818
- "border-gray-800 text-gray-900 font-medium": activeId === heading.id,
819
- "hover:text-gray-800 hover:border-gray-400": activeId !== heading.id
820
- }),
821
- onClick: () => handleClick(heading.id),
822
- children: heading.text
823
- }
824
- );
825
- }
826
-
827
- // src/client/code-block.tsx
806
+ // src/client/components/code-block.tsx
828
807
  var import_react7 = require("react");
829
808
  var import_prism_react_renderer = require("prism-react-renderer");
830
809
  var import_lucide_react10 = require("lucide-react");
831
810
  var import_react_toast_msg2 = require("react-toast-msg");
832
811
  var import_jsx_runtime15 = require("react/jsx-runtime");
833
812
  function DocstraCodeBlock(props) {
834
- const { language, title, children } = props;
813
+ const { title, children } = props;
814
+ const childProps = children?.props || {};
815
+ const className = childProps.className || "";
816
+ const matches = className.match(/language-(?<lang>.*)/);
817
+ const language = props.language || matches && matches.groups && matches.groups.lang || "text";
835
818
  const code = typeof children === "string" ? children.trim() : children?.props?.children?.toString()?.trim() || "";
836
819
  const [copied, setCopied] = (0, import_react7.useState)(false);
837
820
  const handleCopy = () => {
@@ -867,6 +850,7 @@ function DocstraCodeBlock(props) {
867
850
  DocstraHeader,
868
851
  DocstraPage,
869
852
  DocstraProvider,
853
+ DocstraSearchBox,
870
854
  DocstraSidebar,
871
855
  DocstraTOC,
872
856
  useDocstra