docstra 1.2.0 → 1.4.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/README.md CHANGED
@@ -4,6 +4,8 @@ Docstra is a modern documentation framework for Next.js. It's built with Tailwin
4
4
 
5
5
  > Documentation of Docstra is coming soon. Stay tuned!
6
6
 
7
+ > For any questions, feel free to reach out us on [contact@sudhucodes.com](mailto:contact@sudhucodes.com)
8
+
7
9
  ## Features
8
10
 
9
11
  - MDX support
@@ -7,10 +7,22 @@ interface DocstraContextType {
7
7
  docstraConfig: DocstraConfig;
8
8
  openSearchBox: boolean;
9
9
  setOpenSearchBox: (open: boolean) => void;
10
+ mdxData: mdxDataContent;
11
+ }
12
+ interface mdxDataContent {
13
+ metadata: Record<string, any>;
14
+ mdxContent: string;
15
+ rawMdxContent: string;
16
+ mdxFilePath: string;
10
17
  }
11
18
  interface DocstraConfig {
12
19
  siteName?: string;
13
20
  editOnGithub?: boolean;
21
+ feedback?: {
22
+ enabled?: boolean;
23
+ formSyncFormID?: string;
24
+ watermark?: boolean;
25
+ };
14
26
  formSyncFormID?: string;
15
27
  githubRepo?: string;
16
28
  navbar?: {
@@ -39,9 +51,10 @@ interface DocsLinkGroup {
39
51
  items: DocsLinkItem[];
40
52
  }
41
53
 
42
- declare function DocstraProvider({ children, docstraConfig }: {
54
+ declare function DocstraProvider({ children, docstraConfig, mdxData }: {
43
55
  children: React$1.ReactNode;
44
56
  docstraConfig: DocstraConfig;
57
+ mdxData: mdxDataContent;
45
58
  }): react_jsx_runtime.JSX.Element;
46
59
  declare function useDocstra(): DocstraContextType;
47
60
 
@@ -55,10 +68,8 @@ declare function DocstraPage({ children }: {
55
68
 
56
69
  interface Props {
57
70
  children: React.ReactNode;
58
- metadata: any;
59
- mdxContent: string;
60
71
  }
61
- declare function DocstraBody({ children, metadata, mdxContent }: Props): react_jsx_runtime.JSX.Element;
72
+ declare function DocstraBody({ children }: Props): react_jsx_runtime.JSX.Element;
62
73
 
63
74
  declare function DocstraTOC({ mdxFilePath, rawMdxContent }: {
64
75
  mdxFilePath: string;
@@ -7,10 +7,22 @@ interface DocstraContextType {
7
7
  docstraConfig: DocstraConfig;
8
8
  openSearchBox: boolean;
9
9
  setOpenSearchBox: (open: boolean) => void;
10
+ mdxData: mdxDataContent;
11
+ }
12
+ interface mdxDataContent {
13
+ metadata: Record<string, any>;
14
+ mdxContent: string;
15
+ rawMdxContent: string;
16
+ mdxFilePath: string;
10
17
  }
11
18
  interface DocstraConfig {
12
19
  siteName?: string;
13
20
  editOnGithub?: boolean;
21
+ feedback?: {
22
+ enabled?: boolean;
23
+ formSyncFormID?: string;
24
+ watermark?: boolean;
25
+ };
14
26
  formSyncFormID?: string;
15
27
  githubRepo?: string;
16
28
  navbar?: {
@@ -39,9 +51,10 @@ interface DocsLinkGroup {
39
51
  items: DocsLinkItem[];
40
52
  }
41
53
 
42
- declare function DocstraProvider({ children, docstraConfig }: {
54
+ declare function DocstraProvider({ children, docstraConfig, mdxData }: {
43
55
  children: React$1.ReactNode;
44
56
  docstraConfig: DocstraConfig;
57
+ mdxData: mdxDataContent;
45
58
  }): react_jsx_runtime.JSX.Element;
46
59
  declare function useDocstra(): DocstraContextType;
47
60
 
@@ -55,10 +68,8 @@ declare function DocstraPage({ children }: {
55
68
 
56
69
  interface Props {
57
70
  children: React.ReactNode;
58
- metadata: any;
59
- mdxContent: string;
60
71
  }
61
- declare function DocstraBody({ children, metadata, mdxContent }: Props): react_jsx_runtime.JSX.Element;
72
+ declare function DocstraBody({ children }: Props): react_jsx_runtime.JSX.Element;
62
73
 
63
74
  declare function DocstraTOC({ mdxFilePath, rawMdxContent }: {
64
75
  mdxFilePath: string;
@@ -160,7 +160,8 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
160
160
  var DocstraContext = (0, import_react2.createContext)(null);
161
161
  function DocstraProvider({
162
162
  children,
163
- docstraConfig
163
+ docstraConfig,
164
+ mdxData
164
165
  }) {
165
166
  const [openSidebar, setOpenSidebar] = (0, import_react2.useState)(false);
166
167
  const [openSearchBox, setOpenSearchBox] = (0, import_react2.useState)(false);
@@ -172,7 +173,8 @@ function DocstraProvider({
172
173
  setOpenSidebar,
173
174
  docstraConfig,
174
175
  openSearchBox,
175
- setOpenSearchBox
176
+ setOpenSearchBox,
177
+ mdxData
176
178
  },
177
179
  children: [
178
180
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_toast_msg.ToastContainer, {}),
@@ -214,7 +216,7 @@ function DocstraHeader() {
214
216
  }
215
217
  }, [openSidebar]);
216
218
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("header", { className: "sticky top-0 z-10 text-sm flex items-center justify-between border-b border-gray-100 h-18 px-4 md:px-6 bg-white", children: [
217
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { href: docstraConfig.navbar?.logo?.link || "/", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
219
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: docstraConfig.navbar?.logo?.link || "/", className: "hover:opacity-80 transition", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
218
220
  "img",
219
221
  {
220
222
  src: docstraConfig.navbar?.logo?.src || "/logo.png",
@@ -347,19 +349,18 @@ function DocstraBreadcrumbs() {
347
349
 
348
350
  // src/client/page-buttons.tsx
349
351
  var import_lucide_react5 = require("lucide-react");
350
- var import_navigation3 = require("next/navigation");
351
352
  var import_react4 = require("react");
352
353
  var import_jsx_runtime8 = require("react/jsx-runtime");
353
- function DocstraPageButtons({ rawMdxContent }) {
354
+ function DocstraPageButtons() {
355
+ const { mdxData } = useDocstra();
354
356
  const [isCopied, setIsCopied] = (0, import_react4.useState)(false);
355
357
  const [href, setHref] = (0, import_react4.useState)(null);
356
- const pathname = (0, import_navigation3.usePathname)();
357
358
  (0, import_react4.useEffect)(() => {
358
359
  setHref(window.location.href);
359
360
  }, []);
360
- const prompt = `Read from this URL: ${href + pathname} and explain it to me.`;
361
+ const prompt = `Read from this URL: ${href} and explain it to me.`;
361
362
  const handleCopy = () => {
362
- navigator.clipboard.writeText(rawMdxContent);
363
+ navigator.clipboard.writeText(mdxData?.rawMdxContent);
363
364
  setIsCopied(true);
364
365
  setTimeout(() => setIsCopied(false), 3e3);
365
366
  };
@@ -391,11 +392,12 @@ function DocstraPageButtons({ rawMdxContent }) {
391
392
 
392
393
  // src/client/docstra-page-head.tsx
393
394
  var import_jsx_runtime9 = require("react/jsx-runtime");
394
- function DocstraPageHead({ metadata, mdxContent }) {
395
+ function DocstraPageHead() {
396
+ const { mdxData } = useDocstra();
395
397
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
396
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { className: "text-3xl font-bold", children: metadata.title }),
397
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "my-4 text-gray-500", children: metadata.description }),
398
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(DocstraPageButtons, { rawMdxContent: mdxContent }),
398
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { className: "text-3xl font-bold", children: mdxData?.metadata?.title }),
399
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "my-4 text-gray-500", children: mdxData?.metadata?.description }),
400
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(DocstraPageButtons, {}),
399
401
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("hr", { className: "my-10 border-gray-200" })
400
402
  ] });
401
403
  }
@@ -532,7 +534,7 @@ function DocstraFeedback() {
532
534
  "form",
533
535
  {
534
536
  className: "flex flex-col gap-2",
535
- action: `https://formsync.app/v1/s/${docstraConfig?.formSyncFormID || "your-form-id"}`,
537
+ action: `https://formsync.app/v1/s/${docstraConfig?.feedback?.formSyncFormID || ""}`,
536
538
  method: "POST",
537
539
  children: [
538
540
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
@@ -550,10 +552,17 @@ function DocstraFeedback() {
550
552
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { type: "hidden", name: "page", value: window.location.href }),
551
553
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { type: "hidden", name: "_redirect", value: window.location.href }),
552
554
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Button, { label: "Submit", className: "w-max text-sm" }),
553
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-left text-sm text-gray-500 mt-6", children: [
555
+ docstraConfig?.feedback?.watermark !== false && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-left text-sm text-gray-500 mt-6", children: [
554
556
  "Powered by",
555
557
  " ",
556
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("a", { href: "https://www.formsync.app?utm_source=formsync-docs", className: "text-gray-800", children: "FormSync" })
558
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
559
+ "a",
560
+ {
561
+ href: `https://www.formsync.app?utm_source=${docstraConfig?.githubRepo}`,
562
+ className: "text-gray-800",
563
+ children: "FormSync"
564
+ }
565
+ )
557
566
  ] })
558
567
  ]
559
568
  }
@@ -562,12 +571,12 @@ function DocstraFeedback() {
562
571
  }
563
572
 
564
573
  // src/client/pagination.tsx
565
- var import_navigation4 = require("next/navigation");
574
+ var import_navigation3 = require("next/navigation");
566
575
  var import_link5 = __toESM(require("next/link"));
567
576
  var import_lucide_react8 = require("lucide-react");
568
577
  var import_jsx_runtime13 = require("react/jsx-runtime");
569
578
  function DocstraPagination() {
570
- const pathname = (0, import_navigation4.usePathname)();
579
+ const pathname = (0, import_navigation3.usePathname)();
571
580
  const { docstraConfig } = useDocstra();
572
581
  const flatLinks = docstraConfig?.sidebar?.links?.flatMap((section) => section.items);
573
582
  const currentIndex = flatLinks.findIndex((item) => item.href === pathname);
@@ -607,19 +616,20 @@ function DocstraPagination() {
607
616
 
608
617
  // src/client/docs-body.tsx
609
618
  var import_jsx_runtime14 = require("react/jsx-runtime");
610
- function DocstraBody({ children, metadata, mdxContent }) {
619
+ function DocstraBody({ children }) {
620
+ const { docstraConfig } = useDocstra();
611
621
  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: [
612
622
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraBreadcrumbs, {}),
613
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraPageHead, { metadata, mdxContent }),
623
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraPageHead, {}),
614
624
  children,
615
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraFeedback, {}),
625
+ docstraConfig?.feedback?.enabled && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraFeedback, {}),
616
626
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraPagination, {})
617
627
  ] });
618
628
  }
619
629
 
620
630
  // src/client/docstra-toc.tsx
621
631
  var import_lucide_react9 = require("lucide-react");
622
- var import_navigation5 = require("next/navigation");
632
+ var import_navigation4 = require("next/navigation");
623
633
  var import_react6 = require("react");
624
634
 
625
635
  // src/utils/generate-id-from-text.ts
@@ -662,7 +672,7 @@ function DocstraTOC({ mdxFilePath, rawMdxContent }) {
662
672
  const { docstraConfig } = useDocstra();
663
673
  const lastScrollY = (0, import_react6.useRef)(0);
664
674
  const observerRef = (0, import_react6.useRef)(null);
665
- const pathname = (0, import_navigation5.usePathname)();
675
+ const pathname = (0, import_navigation4.usePathname)();
666
676
  const baseUrlOfGithub = docstraConfig?.githubRepo || "https://github.com/sudhucodes/docstra";
667
677
  const normalized = mdxFilePath.replace(/\\/g, "/");
668
678
  const match = normalized.match(/\/docs\/(.+)$/);
@@ -706,6 +716,22 @@ function DocstraTOC({ mdxFilePath, rawMdxContent }) {
706
716
  const scrolled = window.scrollY > 100;
707
717
  !scrolled && setActiveId(headings[0]?.id);
708
718
  }, [headings]);
719
+ (0, import_react6.useEffect)(() => {
720
+ if (!activeId) return;
721
+ const container = document.querySelector(".toc-scroll-container");
722
+ if (!container) return;
723
+ const activeLink = container.querySelector(`a[href="#${activeId}"]`);
724
+ if (!activeLink) return;
725
+ const containerRect = container.getBoundingClientRect();
726
+ const linkRect = activeLink.getBoundingClientRect();
727
+ const isOutOfView = linkRect.top < containerRect.top || linkRect.bottom > containerRect.bottom;
728
+ if (isOutOfView) {
729
+ activeLink.scrollIntoView({
730
+ behavior: "smooth",
731
+ block: "nearest"
732
+ });
733
+ }
734
+ }, [activeId]);
709
735
  (0, import_react6.useEffect)(() => {
710
736
  if (!isManualClick) return;
711
737
  const timer = setTimeout(() => setIsManualClick(false), 800);
@@ -720,7 +746,7 @@ function DocstraTOC({ mdxFilePath, rawMdxContent }) {
720
746
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react9.TextAlignStartIcon, { className: "size-4" }),
721
747
  "On this page"
722
748
  ] }),
723
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("ul", { children: headings.map((h2) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("li", { children: [
749
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("ul", { className: "toc-scroll-container max-h-3/4 overflow-y-auto scrollbar-none", children: headings.map((h2) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("li", { children: [
724
750
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TableOfContentsLink, { heading: h2, activeId, handleClick }),
725
751
  h2.children.map((h3) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
726
752
  TableOfContentsLink,
@@ -118,7 +118,8 @@ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
118
118
  var DocstraContext = createContext(null);
119
119
  function DocstraProvider({
120
120
  children,
121
- docstraConfig
121
+ docstraConfig,
122
+ mdxData
122
123
  }) {
123
124
  const [openSidebar, setOpenSidebar] = useState2(false);
124
125
  const [openSearchBox, setOpenSearchBox] = useState2(false);
@@ -130,7 +131,8 @@ function DocstraProvider({
130
131
  setOpenSidebar,
131
132
  docstraConfig,
132
133
  openSearchBox,
133
- setOpenSearchBox
134
+ setOpenSearchBox,
135
+ mdxData
134
136
  },
135
137
  children: [
136
138
  /* @__PURE__ */ jsx2(ToastContainer, {}),
@@ -172,7 +174,7 @@ function DocstraHeader() {
172
174
  }
173
175
  }, [openSidebar]);
174
176
  return /* @__PURE__ */ jsxs3("header", { className: "sticky top-0 z-10 text-sm flex items-center justify-between border-b border-gray-100 h-18 px-4 md:px-6 bg-white", children: [
175
- /* @__PURE__ */ jsx4("a", { href: docstraConfig.navbar?.logo?.link || "/", children: /* @__PURE__ */ jsx4(
177
+ /* @__PURE__ */ jsx4(Link, { href: docstraConfig.navbar?.logo?.link || "/", className: "hover:opacity-80 transition", children: /* @__PURE__ */ jsx4(
176
178
  "img",
177
179
  {
178
180
  src: docstraConfig.navbar?.logo?.src || "/logo.png",
@@ -305,19 +307,18 @@ function DocstraBreadcrumbs() {
305
307
 
306
308
  // src/client/page-buttons.tsx
307
309
  import { CheckIcon, CopyIcon } from "lucide-react";
308
- import { usePathname as usePathname3 } from "next/navigation";
309
310
  import { useEffect as useEffect3, useState as useState3 } from "react";
310
311
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
311
- function DocstraPageButtons({ rawMdxContent }) {
312
+ function DocstraPageButtons() {
313
+ const { mdxData } = useDocstra();
312
314
  const [isCopied, setIsCopied] = useState3(false);
313
315
  const [href, setHref] = useState3(null);
314
- const pathname = usePathname3();
315
316
  useEffect3(() => {
316
317
  setHref(window.location.href);
317
318
  }, []);
318
- const prompt = `Read from this URL: ${href + pathname} and explain it to me.`;
319
+ const prompt = `Read from this URL: ${href} and explain it to me.`;
319
320
  const handleCopy = () => {
320
- navigator.clipboard.writeText(rawMdxContent);
321
+ navigator.clipboard.writeText(mdxData?.rawMdxContent);
321
322
  setIsCopied(true);
322
323
  setTimeout(() => setIsCopied(false), 3e3);
323
324
  };
@@ -349,11 +350,12 @@ function DocstraPageButtons({ rawMdxContent }) {
349
350
 
350
351
  // src/client/docstra-page-head.tsx
351
352
  import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
352
- function DocstraPageHead({ metadata, mdxContent }) {
353
+ function DocstraPageHead() {
354
+ const { mdxData } = useDocstra();
353
355
  return /* @__PURE__ */ jsxs7(Fragment3, { children: [
354
- /* @__PURE__ */ jsx9("h1", { className: "text-3xl font-bold", children: metadata.title }),
355
- /* @__PURE__ */ jsx9("p", { className: "my-4 text-gray-500", children: metadata.description }),
356
- /* @__PURE__ */ jsx9(DocstraPageButtons, { rawMdxContent: mdxContent }),
356
+ /* @__PURE__ */ jsx9("h1", { className: "text-3xl font-bold", children: mdxData?.metadata?.title }),
357
+ /* @__PURE__ */ jsx9("p", { className: "my-4 text-gray-500", children: mdxData?.metadata?.description }),
358
+ /* @__PURE__ */ jsx9(DocstraPageButtons, {}),
357
359
  /* @__PURE__ */ jsx9("hr", { className: "my-10 border-gray-200" })
358
360
  ] });
359
361
  }
@@ -490,7 +492,7 @@ function DocstraFeedback() {
490
492
  "form",
491
493
  {
492
494
  className: "flex flex-col gap-2",
493
- action: `https://formsync.app/v1/s/${docstraConfig?.formSyncFormID || "your-form-id"}`,
495
+ action: `https://formsync.app/v1/s/${docstraConfig?.feedback?.formSyncFormID || ""}`,
494
496
  method: "POST",
495
497
  children: [
496
498
  /* @__PURE__ */ jsx12(
@@ -508,10 +510,17 @@ function DocstraFeedback() {
508
510
  /* @__PURE__ */ jsx12("input", { type: "hidden", name: "page", value: window.location.href }),
509
511
  /* @__PURE__ */ jsx12("input", { type: "hidden", name: "_redirect", value: window.location.href }),
510
512
  /* @__PURE__ */ jsx12(Button, { label: "Submit", className: "w-max text-sm" }),
511
- /* @__PURE__ */ jsxs9("p", { className: "text-left text-sm text-gray-500 mt-6", children: [
513
+ docstraConfig?.feedback?.watermark !== false && /* @__PURE__ */ jsxs9("p", { className: "text-left text-sm text-gray-500 mt-6", children: [
512
514
  "Powered by",
513
515
  " ",
514
- /* @__PURE__ */ jsx12("a", { href: "https://www.formsync.app?utm_source=formsync-docs", className: "text-gray-800", children: "FormSync" })
516
+ /* @__PURE__ */ jsx12(
517
+ "a",
518
+ {
519
+ href: `https://www.formsync.app?utm_source=${docstraConfig?.githubRepo}`,
520
+ className: "text-gray-800",
521
+ children: "FormSync"
522
+ }
523
+ )
515
524
  ] })
516
525
  ]
517
526
  }
@@ -520,12 +529,12 @@ function DocstraFeedback() {
520
529
  }
521
530
 
522
531
  // src/client/pagination.tsx
523
- import { usePathname as usePathname4 } from "next/navigation";
532
+ import { usePathname as usePathname3 } from "next/navigation";
524
533
  import Link2 from "next/link";
525
534
  import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react";
526
535
  import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
527
536
  function DocstraPagination() {
528
- const pathname = usePathname4();
537
+ const pathname = usePathname3();
529
538
  const { docstraConfig } = useDocstra();
530
539
  const flatLinks = docstraConfig?.sidebar?.links?.flatMap((section) => section.items);
531
540
  const currentIndex = flatLinks.findIndex((item) => item.href === pathname);
@@ -565,19 +574,20 @@ function DocstraPagination() {
565
574
 
566
575
  // src/client/docs-body.tsx
567
576
  import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
568
- function DocstraBody({ children, metadata, mdxContent }) {
577
+ function DocstraBody({ children }) {
578
+ const { docstraConfig } = useDocstra();
569
579
  return /* @__PURE__ */ jsxs11("main", { className: "flex-1 px-4 md:px-8 py-10 max-w-full text-base/7", children: [
570
580
  /* @__PURE__ */ jsx14(DocstraBreadcrumbs, {}),
571
- /* @__PURE__ */ jsx14(DocstraPageHead, { metadata, mdxContent }),
581
+ /* @__PURE__ */ jsx14(DocstraPageHead, {}),
572
582
  children,
573
- /* @__PURE__ */ jsx14(DocstraFeedback, {}),
583
+ docstraConfig?.feedback?.enabled && /* @__PURE__ */ jsx14(DocstraFeedback, {}),
574
584
  /* @__PURE__ */ jsx14(DocstraPagination, {})
575
585
  ] });
576
586
  }
577
587
 
578
588
  // src/client/docstra-toc.tsx
579
589
  import { ArrowUpCircleIcon, SquareArrowOutUpRightIcon, TextAlignStartIcon } from "lucide-react";
580
- import { usePathname as usePathname5 } from "next/navigation";
590
+ import { usePathname as usePathname4 } from "next/navigation";
581
591
  import { useEffect as useEffect4, useRef, useState as useState5 } from "react";
582
592
 
583
593
  // src/utils/generate-id-from-text.ts
@@ -620,7 +630,7 @@ function DocstraTOC({ mdxFilePath, rawMdxContent }) {
620
630
  const { docstraConfig } = useDocstra();
621
631
  const lastScrollY = useRef(0);
622
632
  const observerRef = useRef(null);
623
- const pathname = usePathname5();
633
+ const pathname = usePathname4();
624
634
  const baseUrlOfGithub = docstraConfig?.githubRepo || "https://github.com/sudhucodes/docstra";
625
635
  const normalized = mdxFilePath.replace(/\\/g, "/");
626
636
  const match = normalized.match(/\/docs\/(.+)$/);
@@ -664,6 +674,22 @@ function DocstraTOC({ mdxFilePath, rawMdxContent }) {
664
674
  const scrolled = window.scrollY > 100;
665
675
  !scrolled && setActiveId(headings[0]?.id);
666
676
  }, [headings]);
677
+ useEffect4(() => {
678
+ if (!activeId) return;
679
+ const container = document.querySelector(".toc-scroll-container");
680
+ if (!container) return;
681
+ const activeLink = container.querySelector(`a[href="#${activeId}"]`);
682
+ if (!activeLink) return;
683
+ const containerRect = container.getBoundingClientRect();
684
+ const linkRect = activeLink.getBoundingClientRect();
685
+ const isOutOfView = linkRect.top < containerRect.top || linkRect.bottom > containerRect.bottom;
686
+ if (isOutOfView) {
687
+ activeLink.scrollIntoView({
688
+ behavior: "smooth",
689
+ block: "nearest"
690
+ });
691
+ }
692
+ }, [activeId]);
667
693
  useEffect4(() => {
668
694
  if (!isManualClick) return;
669
695
  const timer = setTimeout(() => setIsManualClick(false), 800);
@@ -678,7 +704,7 @@ function DocstraTOC({ mdxFilePath, rawMdxContent }) {
678
704
  /* @__PURE__ */ jsx15(TextAlignStartIcon, { className: "size-4" }),
679
705
  "On this page"
680
706
  ] }),
681
- /* @__PURE__ */ jsx15("ul", { children: headings.map((h2) => /* @__PURE__ */ jsxs12("li", { children: [
707
+ /* @__PURE__ */ jsx15("ul", { className: "toc-scroll-container max-h-3/4 overflow-y-auto scrollbar-none", children: headings.map((h2) => /* @__PURE__ */ jsxs12("li", { children: [
682
708
  /* @__PURE__ */ jsx15(TableOfContentsLink, { heading: h2, activeId, handleClick }),
683
709
  h2.children.map((h3) => /* @__PURE__ */ jsx15(
684
710
  TableOfContentsLink,
@@ -3,6 +3,11 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  interface DocstraConfig {
4
4
  siteName?: string;
5
5
  editOnGithub?: boolean;
6
+ feedback?: {
7
+ enabled?: boolean;
8
+ formSyncFormID?: string;
9
+ watermark?: boolean;
10
+ };
6
11
  formSyncFormID?: string;
7
12
  githubRepo?: string;
8
13
  navbar?: {
@@ -3,6 +3,11 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  interface DocstraConfig {
4
4
  siteName?: string;
5
5
  editOnGithub?: boolean;
6
+ feedback?: {
7
+ enabled?: boolean;
8
+ formSyncFormID?: string;
9
+ watermark?: boolean;
10
+ };
6
11
  formSyncFormID?: string;
7
12
  githubRepo?: string;
8
13
  navbar?: {
@@ -44,8 +44,7 @@ var import_gray_matter = __toESM(require("gray-matter"));
44
44
  var DEFAULT_CONTENT_DIR = import_path.default.join(process.cwd(), "app", "docs", "content");
45
45
  function getFilePath({ slug, CONTENT_DIR }) {
46
46
  const slugArray = Array.isArray(slug) && slug.length > 0 ? slug : ["index"];
47
- const USER_CONTENT_DIR = process.cwd() + CONTENT_DIR;
48
- const DIR = USER_CONTENT_DIR || DEFAULT_CONTENT_DIR;
47
+ const DIR = CONTENT_DIR ? import_path.default.join(process.cwd(), CONTENT_DIR) : DEFAULT_CONTENT_DIR;
49
48
  const possiblePaths = [
50
49
  import_path.default.join(DIR, ...slugArray) + ".mdx",
51
50
  import_path.default.join(DIR, ...slugArray) + ".md",
@@ -57,10 +56,13 @@ function getFilePath({ slug, CONTENT_DIR }) {
57
56
  return filePath;
58
57
  }
59
58
  }
60
- throw new Error(`\u274C MDX file not found in: ${DIR} `);
59
+ throw new Error(`\u274C MDX file not found in: ${DIR}`);
61
60
  }
62
61
  function getFileContents({ slug, CONTENT_DIR }) {
63
- const filePath = getFilePath({ slug, CONTENT_DIR: CONTENT_DIR || DEFAULT_CONTENT_DIR });
62
+ const filePath = getFilePath({
63
+ slug,
64
+ CONTENT_DIR
65
+ });
64
66
  const rawFile = import_fs.default.readFileSync(filePath, "utf-8");
65
67
  const { data, content } = (0, import_gray_matter.default)(rawFile);
66
68
  return {
@@ -99,9 +101,6 @@ function CreateHeading({ children, level }) {
99
101
  let fontSize;
100
102
  let iconSize;
101
103
  switch (level) {
102
- case 1:
103
- fontSize = "text-3xl";
104
- break;
105
104
  case 2:
106
105
  fontSize = "text-2xl";
107
106
  iconSize = "size-5";
@@ -122,7 +121,7 @@ function CreateHeading({ children, level }) {
122
121
  fontSize = "text-base";
123
122
  iconSize = "size-3.5";
124
123
  }
125
- return level === 1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { className: "text-3xl font-bold text-gray-900 mb-4", children }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
124
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
126
125
  Heading,
127
126
  {
128
127
  id,
@@ -138,7 +137,7 @@ function CreateHeading({ children, level }) {
138
137
  // src/client/mdx-components.tsx
139
138
  var import_jsx_runtime2 = require("react/jsx-runtime");
140
139
  var defaultMdxComponents = {
141
- h1: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CreateHeading, { ...props, level: 1 }),
140
+ h1: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { className: "text-3xl font-bold text-gray-900 mb-4", children: props.children }),
142
141
  h2: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CreateHeading, { ...props, level: 2 }),
143
142
  h3: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CreateHeading, { ...props, level: 3 }),
144
143
  h4: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CreateHeading, { ...props, level: 4 }),
@@ -5,8 +5,7 @@ import matter from "gray-matter";
5
5
  var DEFAULT_CONTENT_DIR = path.join(process.cwd(), "app", "docs", "content");
6
6
  function getFilePath({ slug, CONTENT_DIR }) {
7
7
  const slugArray = Array.isArray(slug) && slug.length > 0 ? slug : ["index"];
8
- const USER_CONTENT_DIR = process.cwd() + CONTENT_DIR;
9
- const DIR = USER_CONTENT_DIR || DEFAULT_CONTENT_DIR;
8
+ const DIR = CONTENT_DIR ? path.join(process.cwd(), CONTENT_DIR) : DEFAULT_CONTENT_DIR;
10
9
  const possiblePaths = [
11
10
  path.join(DIR, ...slugArray) + ".mdx",
12
11
  path.join(DIR, ...slugArray) + ".md",
@@ -18,10 +17,13 @@ function getFilePath({ slug, CONTENT_DIR }) {
18
17
  return filePath;
19
18
  }
20
19
  }
21
- throw new Error(`\u274C MDX file not found in: ${DIR} `);
20
+ throw new Error(`\u274C MDX file not found in: ${DIR}`);
22
21
  }
23
22
  function getFileContents({ slug, CONTENT_DIR }) {
24
- const filePath = getFilePath({ slug, CONTENT_DIR: CONTENT_DIR || DEFAULT_CONTENT_DIR });
23
+ const filePath = getFilePath({
24
+ slug,
25
+ CONTENT_DIR
26
+ });
25
27
  const rawFile = fs.readFileSync(filePath, "utf-8");
26
28
  const { data, content } = matter(rawFile);
27
29
  return {
@@ -60,9 +62,6 @@ function CreateHeading({ children, level }) {
60
62
  let fontSize;
61
63
  let iconSize;
62
64
  switch (level) {
63
- case 1:
64
- fontSize = "text-3xl";
65
- break;
66
65
  case 2:
67
66
  fontSize = "text-2xl";
68
67
  iconSize = "size-5";
@@ -83,7 +82,7 @@ function CreateHeading({ children, level }) {
83
82
  fontSize = "text-base";
84
83
  iconSize = "size-3.5";
85
84
  }
86
- return level === 1 ? /* @__PURE__ */ jsx("h1", { className: "text-3xl font-bold text-gray-900 mb-4", children }) : /* @__PURE__ */ jsxs(
85
+ return /* @__PURE__ */ jsxs(
87
86
  Heading,
88
87
  {
89
88
  id,
@@ -99,7 +98,7 @@ function CreateHeading({ children, level }) {
99
98
  // src/client/mdx-components.tsx
100
99
  import { jsx as jsx2 } from "react/jsx-runtime";
101
100
  var defaultMdxComponents = {
102
- h1: (props) => /* @__PURE__ */ jsx2(CreateHeading, { ...props, level: 1 }),
101
+ h1: (props) => /* @__PURE__ */ jsx2("h1", { className: "text-3xl font-bold text-gray-900 mb-4", children: props.children }),
103
102
  h2: (props) => /* @__PURE__ */ jsx2(CreateHeading, { ...props, level: 2 }),
104
103
  h3: (props) => /* @__PURE__ */ jsx2(CreateHeading, { ...props, level: 3 }),
105
104
  h4: (props) => /* @__PURE__ */ jsx2(CreateHeading, { ...props, level: 4 }),
package/dist/styles.css CHANGED
@@ -1 +1,43 @@
1
- @source ".";
1
+ @source ".";
2
+
3
+ .scrollbar-none::-webkit-scrollbar {
4
+ display: none;
5
+ }
6
+
7
+ .scrollbar-x::-webkit-scrollbar {
8
+ display: block;
9
+ height: 3.5px;
10
+ }
11
+
12
+ .scrollbar-x::-webkit-scrollbar-track {
13
+ background: var(--color-gray-100);
14
+ }
15
+
16
+ .scrollbar-x::-webkit-scrollbar-thumb {
17
+ background-color: var(--color-gray-300);
18
+ border-radius: 0px;
19
+ background-clip: padding-box;
20
+ }
21
+
22
+ .scrollbar-x::-webkit-scrollbar-thumb:hover {
23
+ background-color: var(--color-gray-400);
24
+ }
25
+
26
+ .scrollbar-y::-webkit-scrollbar {
27
+ display: block;
28
+ width: 3.5px;
29
+ }
30
+
31
+ .scrollbar-y::-webkit-scrollbar-track {
32
+ background: var(--color-gray-100);
33
+ }
34
+
35
+ .scrollbar-y::-webkit-scrollbar-thumb {
36
+ background-color: var(--color-gray-300);
37
+ border-radius: 0px;
38
+ background-clip: padding-box;
39
+ }
40
+
41
+ .scrollbar-y::-webkit-scrollbar-thumb:hover {
42
+ background-color: var(--color-gray-400);
43
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docstra",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "A Modern Documentation Framework for Next.js",
5
5
  "keywords": [
6
6
  "next",