docstra 1.3.0 → 1.4.5

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
@@ -25,19 +25,50 @@ npm install docstra
25
25
 
26
26
  ```ts
27
27
  // docstra.config.ts
28
+ import { defineDocstraConfig } from 'docstra/config';
29
+
28
30
  export default defineDocstraConfig({
29
- siteName: 'Docstra Site',
30
- githubRepo: 'https://github.com/sudhucodes/test',
31
+ siteName: 'Docstra',
32
+ githubRepo: 'https://github.com/sudhucodes/docstra',
31
33
  editOnGithub: true,
32
- formSyncFormID: 'FORM_ID',
34
+ feedback: {
35
+ enabled: true,
36
+ formSyncFormID: 'FORM_ID'
37
+ },
33
38
  navbar: {
34
39
  logo: {
35
- link: '/docs'
40
+ link: '/docs',
41
+ src: '/logo.svg',
42
+ alt: 'Logo',
43
+ className: 'h-9.5 w-auto'
36
44
  },
37
- links: [{ name: 'Guides', href: '/docs/guides' }]
45
+ links: [
46
+ { name: 'Guides', href: '/docs/guides' },
47
+ { name: 'Examples', href: '/docs/examples' }
48
+ ]
38
49
  },
39
50
  sidebar: {
40
- // sidebar links
51
+ links: [
52
+ {
53
+ section: 'Introduction',
54
+ items: [
55
+ {
56
+ name: 'Overview',
57
+ href: '/docs',
58
+ icon: 'NotebookPenIcon'
59
+ },
60
+ {
61
+ name: 'Quick Start',
62
+ href: '/docs/quick-start',
63
+ icon: 'RocketIcon'
64
+ }
65
+ ]
66
+ },
67
+ {
68
+ section: 'Tailwind Setup',
69
+ items: [{ name: 'Installation', href: '/docs/setup/installation' }]
70
+ }
71
+ ]
41
72
  }
42
73
  });
43
74
  ```
@@ -7,11 +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;
14
- formSyncFormID?: string;
21
+ feedback?: {
22
+ enabled?: boolean;
23
+ formSyncFormID?: string;
24
+ watermark?: boolean;
25
+ };
15
26
  githubRepo?: string;
16
27
  navbar?: {
17
28
  logo?: {
@@ -39,9 +50,10 @@ interface DocsLinkGroup {
39
50
  items: DocsLinkItem[];
40
51
  }
41
52
 
42
- declare function DocstraProvider({ children, docstraConfig }: {
53
+ declare function DocstraProvider({ children, docstraConfig, mdxData }: {
43
54
  children: React$1.ReactNode;
44
55
  docstraConfig: DocstraConfig;
56
+ mdxData: mdxDataContent;
45
57
  }): react_jsx_runtime.JSX.Element;
46
58
  declare function useDocstra(): DocstraContextType;
47
59
 
@@ -55,15 +67,10 @@ declare function DocstraPage({ children }: {
55
67
 
56
68
  interface Props {
57
69
  children: React.ReactNode;
58
- metadata: any;
59
- mdxContent: string;
60
70
  }
61
- declare function DocstraBody({ children, metadata, mdxContent }: Props): react_jsx_runtime.JSX.Element;
71
+ declare function DocstraBody({ children }: Props): react_jsx_runtime.JSX.Element;
62
72
 
63
- declare function DocstraTOC({ mdxFilePath, rawMdxContent }: {
64
- mdxFilePath: string;
65
- rawMdxContent: string;
66
- }): react_jsx_runtime.JSX.Element;
73
+ declare function DocstraTOC(): react_jsx_runtime.JSX.Element;
67
74
 
68
75
  declare function DocstraCodeBlock(props: any): react_jsx_runtime.JSX.Element;
69
76
 
@@ -7,11 +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;
14
- formSyncFormID?: string;
21
+ feedback?: {
22
+ enabled?: boolean;
23
+ formSyncFormID?: string;
24
+ watermark?: boolean;
25
+ };
15
26
  githubRepo?: string;
16
27
  navbar?: {
17
28
  logo?: {
@@ -39,9 +50,10 @@ interface DocsLinkGroup {
39
50
  items: DocsLinkItem[];
40
51
  }
41
52
 
42
- declare function DocstraProvider({ children, docstraConfig }: {
53
+ declare function DocstraProvider({ children, docstraConfig, mdxData }: {
43
54
  children: React$1.ReactNode;
44
55
  docstraConfig: DocstraConfig;
56
+ mdxData: mdxDataContent;
45
57
  }): react_jsx_runtime.JSX.Element;
46
58
  declare function useDocstra(): DocstraContextType;
47
59
 
@@ -55,15 +67,10 @@ declare function DocstraPage({ children }: {
55
67
 
56
68
  interface Props {
57
69
  children: React.ReactNode;
58
- metadata: any;
59
- mdxContent: string;
60
70
  }
61
- declare function DocstraBody({ children, metadata, mdxContent }: Props): react_jsx_runtime.JSX.Element;
71
+ declare function DocstraBody({ children }: Props): react_jsx_runtime.JSX.Element;
62
72
 
63
- declare function DocstraTOC({ mdxFilePath, rawMdxContent }: {
64
- mdxFilePath: string;
65
- rawMdxContent: string;
66
- }): react_jsx_runtime.JSX.Element;
73
+ declare function DocstraTOC(): react_jsx_runtime.JSX.Element;
67
74
 
68
75
  declare function DocstraCodeBlock(props: any): react_jsx_runtime.JSX.Element;
69
76
 
@@ -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, {}),
@@ -349,7 +351,8 @@ function DocstraBreadcrumbs() {
349
351
  var import_lucide_react5 = require("lucide-react");
350
352
  var import_react4 = require("react");
351
353
  var import_jsx_runtime8 = require("react/jsx-runtime");
352
- function DocstraPageButtons({ rawMdxContent }) {
354
+ function DocstraPageButtons() {
355
+ const { mdxData } = useDocstra();
353
356
  const [isCopied, setIsCopied] = (0, import_react4.useState)(false);
354
357
  const [href, setHref] = (0, import_react4.useState)(null);
355
358
  (0, import_react4.useEffect)(() => {
@@ -357,7 +360,7 @@ function DocstraPageButtons({ rawMdxContent }) {
357
360
  }, []);
358
361
  const prompt = `Read from this URL: ${href} and explain it to me.`;
359
362
  const handleCopy = () => {
360
- navigator.clipboard.writeText(rawMdxContent);
363
+ navigator.clipboard.writeText(mdxData?.rawMdxContent);
361
364
  setIsCopied(true);
362
365
  setTimeout(() => setIsCopied(false), 3e3);
363
366
  };
@@ -389,11 +392,12 @@ function DocstraPageButtons({ rawMdxContent }) {
389
392
 
390
393
  // src/client/docstra-page-head.tsx
391
394
  var import_jsx_runtime9 = require("react/jsx-runtime");
392
- function DocstraPageHead({ metadata, mdxContent }) {
395
+ function DocstraPageHead() {
396
+ const { mdxData } = useDocstra();
393
397
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
394
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { className: "text-3xl font-bold", children: metadata.title }),
395
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "my-4 text-gray-500", children: metadata.description }),
396
- /* @__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, {}),
397
401
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("hr", { className: "my-10 border-gray-200" })
398
402
  ] });
399
403
  }
@@ -530,7 +534,7 @@ function DocstraFeedback() {
530
534
  "form",
531
535
  {
532
536
  className: "flex flex-col gap-2",
533
- action: `https://formsync.app/v1/s/${docstraConfig?.formSyncFormID || "your-form-id"}`,
537
+ action: `https://formsync.app/v1/s/${docstraConfig?.feedback?.formSyncFormID || ""}`,
534
538
  method: "POST",
535
539
  children: [
536
540
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
@@ -548,10 +552,17 @@ function DocstraFeedback() {
548
552
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { type: "hidden", name: "page", value: window.location.href }),
549
553
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { type: "hidden", name: "_redirect", value: window.location.href }),
550
554
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Button, { label: "Submit", className: "w-max text-sm" }),
551
- /* @__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: [
552
556
  "Powered by",
553
557
  " ",
554
- /* @__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
+ )
555
566
  ] })
556
567
  ]
557
568
  }
@@ -576,7 +587,7 @@ function DocstraPagination() {
576
587
  import_link5.default,
577
588
  {
578
589
  href: prev.href,
579
- className: "flex flex-col items-start gap-2 hover:bg-gray-100 py-3 md:min-w-52 pl-4 pr-10 rounded-lg text-sm",
590
+ 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",
580
591
  children: [
581
592
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-gray-500", children: "Previous" }),
582
593
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { className: "flex items-center font-medium", children: [
@@ -590,7 +601,7 @@ function DocstraPagination() {
590
601
  import_link5.default,
591
602
  {
592
603
  href: next.href,
593
- className: "flex flex-col items-end gap-2 hover:bg-gray-100 py-3 pr-4 md:min-w-52 pl-10 rounded-lg text-sm",
604
+ 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",
594
605
  children: [
595
606
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-gray-500", children: "Next" }),
596
607
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { className: "flex items-center font-medium", children: [
@@ -605,12 +616,13 @@ function DocstraPagination() {
605
616
 
606
617
  // src/client/docs-body.tsx
607
618
  var import_jsx_runtime14 = require("react/jsx-runtime");
608
- function DocstraBody({ children, metadata, mdxContent }) {
619
+ function DocstraBody({ children }) {
620
+ const { docstraConfig } = useDocstra();
609
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: [
610
622
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraBreadcrumbs, {}),
611
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraPageHead, { metadata, mdxContent }),
623
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraPageHead, {}),
612
624
  children,
613
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraFeedback, {}),
625
+ docstraConfig?.feedback?.enabled && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraFeedback, {}),
614
626
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocstraPagination, {})
615
627
  ] });
616
628
  }
@@ -651,13 +663,14 @@ function extractHeadingsFromMdx(raw) {
651
663
 
652
664
  // src/client/docstra-toc.tsx
653
665
  var import_jsx_runtime15 = require("react/jsx-runtime");
654
- function DocstraTOC({ mdxFilePath, rawMdxContent }) {
666
+ function DocstraTOC() {
655
667
  const [headings, setHeadings] = (0, import_react6.useState)([]);
656
668
  const [activeId, setActiveId] = (0, import_react6.useState)(null);
657
669
  const [scrollDir, setScrollDir] = (0, import_react6.useState)("down");
658
670
  const [isScrolled, setIsScrolled] = (0, import_react6.useState)(false);
659
671
  const [isManualClick, setIsManualClick] = (0, import_react6.useState)(false);
660
- const { docstraConfig } = useDocstra();
672
+ const { docstraConfig, mdxData } = useDocstra();
673
+ const { mdxFilePath, rawMdxContent } = mdxData;
661
674
  const lastScrollY = (0, import_react6.useRef)(0);
662
675
  const observerRef = (0, import_react6.useRef)(null);
663
676
  const pathname = (0, import_navigation4.usePathname)();
@@ -795,12 +808,8 @@ var import_lucide_react10 = require("lucide-react");
795
808
  var import_react_toast_msg3 = require("react-toast-msg");
796
809
  var import_jsx_runtime16 = require("react/jsx-runtime");
797
810
  function DocstraCodeBlock(props) {
798
- const { className, children } = props.children?.props || props;
799
- if (!className || !className.startsWith("language-")) {
800
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("code", { className: "px-1 py-0.5 rounded border border-gray-200 text-gray-800 text-sm", children });
801
- }
802
- const language = className.replace("language-", "") || "text";
803
- const code = typeof children === "string" ? children.trim() : Array.isArray(children) ? children.join("").trim() : "";
811
+ const { language, title, children } = props;
812
+ const code = typeof children === "string" ? children.trim() : children?.props?.children?.toString()?.trim() || "";
804
813
  const [copied, setCopied] = (0, import_react7.useState)(false);
805
814
  const handleCopy = () => {
806
815
  navigator.clipboard.writeText(code);
@@ -815,7 +824,7 @@ function DocstraCodeBlock(props) {
815
824
  }, [copied]);
816
825
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "max-w-2xl overflow-hidden rounded-lg text-sm text-gray-800 border border-gray-200/80 mt-4", children: [
817
826
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center bg-gray-50 justify-between border-b border-gray-200/80", children: [
818
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "text-xs text-gray-400 p-4", children: language }),
827
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-xs text-gray-400 p-4", children: title || language }),
819
828
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
820
829
  "button",
821
830
  {
@@ -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, {}),
@@ -307,7 +309,8 @@ function DocstraBreadcrumbs() {
307
309
  import { CheckIcon, CopyIcon } from "lucide-react";
308
310
  import { useEffect as useEffect3, useState as useState3 } from "react";
309
311
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
310
- function DocstraPageButtons({ rawMdxContent }) {
312
+ function DocstraPageButtons() {
313
+ const { mdxData } = useDocstra();
311
314
  const [isCopied, setIsCopied] = useState3(false);
312
315
  const [href, setHref] = useState3(null);
313
316
  useEffect3(() => {
@@ -315,7 +318,7 @@ function DocstraPageButtons({ rawMdxContent }) {
315
318
  }, []);
316
319
  const prompt = `Read from this URL: ${href} and explain it to me.`;
317
320
  const handleCopy = () => {
318
- navigator.clipboard.writeText(rawMdxContent);
321
+ navigator.clipboard.writeText(mdxData?.rawMdxContent);
319
322
  setIsCopied(true);
320
323
  setTimeout(() => setIsCopied(false), 3e3);
321
324
  };
@@ -347,11 +350,12 @@ function DocstraPageButtons({ rawMdxContent }) {
347
350
 
348
351
  // src/client/docstra-page-head.tsx
349
352
  import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
350
- function DocstraPageHead({ metadata, mdxContent }) {
353
+ function DocstraPageHead() {
354
+ const { mdxData } = useDocstra();
351
355
  return /* @__PURE__ */ jsxs7(Fragment3, { children: [
352
- /* @__PURE__ */ jsx9("h1", { className: "text-3xl font-bold", children: metadata.title }),
353
- /* @__PURE__ */ jsx9("p", { className: "my-4 text-gray-500", children: metadata.description }),
354
- /* @__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, {}),
355
359
  /* @__PURE__ */ jsx9("hr", { className: "my-10 border-gray-200" })
356
360
  ] });
357
361
  }
@@ -488,7 +492,7 @@ function DocstraFeedback() {
488
492
  "form",
489
493
  {
490
494
  className: "flex flex-col gap-2",
491
- action: `https://formsync.app/v1/s/${docstraConfig?.formSyncFormID || "your-form-id"}`,
495
+ action: `https://formsync.app/v1/s/${docstraConfig?.feedback?.formSyncFormID || ""}`,
492
496
  method: "POST",
493
497
  children: [
494
498
  /* @__PURE__ */ jsx12(
@@ -506,10 +510,17 @@ function DocstraFeedback() {
506
510
  /* @__PURE__ */ jsx12("input", { type: "hidden", name: "page", value: window.location.href }),
507
511
  /* @__PURE__ */ jsx12("input", { type: "hidden", name: "_redirect", value: window.location.href }),
508
512
  /* @__PURE__ */ jsx12(Button, { label: "Submit", className: "w-max text-sm" }),
509
- /* @__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: [
510
514
  "Powered by",
511
515
  " ",
512
- /* @__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
+ )
513
524
  ] })
514
525
  ]
515
526
  }
@@ -534,7 +545,7 @@ function DocstraPagination() {
534
545
  Link2,
535
546
  {
536
547
  href: prev.href,
537
- className: "flex flex-col items-start gap-2 hover:bg-gray-100 py-3 md:min-w-52 pl-4 pr-10 rounded-lg text-sm",
548
+ 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",
538
549
  children: [
539
550
  /* @__PURE__ */ jsx13("span", { className: "text-gray-500", children: "Previous" }),
540
551
  /* @__PURE__ */ jsxs10("p", { className: "flex items-center font-medium", children: [
@@ -548,7 +559,7 @@ function DocstraPagination() {
548
559
  Link2,
549
560
  {
550
561
  href: next.href,
551
- className: "flex flex-col items-end gap-2 hover:bg-gray-100 py-3 pr-4 md:min-w-52 pl-10 rounded-lg text-sm",
562
+ 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",
552
563
  children: [
553
564
  /* @__PURE__ */ jsx13("span", { className: "text-gray-500", children: "Next" }),
554
565
  /* @__PURE__ */ jsxs10("p", { className: "flex items-center font-medium", children: [
@@ -563,12 +574,13 @@ function DocstraPagination() {
563
574
 
564
575
  // src/client/docs-body.tsx
565
576
  import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
566
- function DocstraBody({ children, metadata, mdxContent }) {
577
+ function DocstraBody({ children }) {
578
+ const { docstraConfig } = useDocstra();
567
579
  return /* @__PURE__ */ jsxs11("main", { className: "flex-1 px-4 md:px-8 py-10 max-w-full text-base/7", children: [
568
580
  /* @__PURE__ */ jsx14(DocstraBreadcrumbs, {}),
569
- /* @__PURE__ */ jsx14(DocstraPageHead, { metadata, mdxContent }),
581
+ /* @__PURE__ */ jsx14(DocstraPageHead, {}),
570
582
  children,
571
- /* @__PURE__ */ jsx14(DocstraFeedback, {}),
583
+ docstraConfig?.feedback?.enabled && /* @__PURE__ */ jsx14(DocstraFeedback, {}),
572
584
  /* @__PURE__ */ jsx14(DocstraPagination, {})
573
585
  ] });
574
586
  }
@@ -609,13 +621,14 @@ function extractHeadingsFromMdx(raw) {
609
621
 
610
622
  // src/client/docstra-toc.tsx
611
623
  import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
612
- function DocstraTOC({ mdxFilePath, rawMdxContent }) {
624
+ function DocstraTOC() {
613
625
  const [headings, setHeadings] = useState5([]);
614
626
  const [activeId, setActiveId] = useState5(null);
615
627
  const [scrollDir, setScrollDir] = useState5("down");
616
628
  const [isScrolled, setIsScrolled] = useState5(false);
617
629
  const [isManualClick, setIsManualClick] = useState5(false);
618
- const { docstraConfig } = useDocstra();
630
+ const { docstraConfig, mdxData } = useDocstra();
631
+ const { mdxFilePath, rawMdxContent } = mdxData;
619
632
  const lastScrollY = useRef(0);
620
633
  const observerRef = useRef(null);
621
634
  const pathname = usePathname4();
@@ -753,12 +766,8 @@ import { CheckIcon as CheckIcon2, CopyIcon as CopyIcon2 } from "lucide-react";
753
766
  import { toast as toast2 } from "react-toast-msg";
754
767
  import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
755
768
  function DocstraCodeBlock(props) {
756
- const { className, children } = props.children?.props || props;
757
- if (!className || !className.startsWith("language-")) {
758
- return /* @__PURE__ */ jsx16("code", { className: "px-1 py-0.5 rounded border border-gray-200 text-gray-800 text-sm", children });
759
- }
760
- const language = className.replace("language-", "") || "text";
761
- const code = typeof children === "string" ? children.trim() : Array.isArray(children) ? children.join("").trim() : "";
769
+ const { language, title, children } = props;
770
+ const code = typeof children === "string" ? children.trim() : children?.props?.children?.toString()?.trim() || "";
762
771
  const [copied, setCopied] = useState6(false);
763
772
  const handleCopy = () => {
764
773
  navigator.clipboard.writeText(code);
@@ -773,7 +782,7 @@ function DocstraCodeBlock(props) {
773
782
  }, [copied]);
774
783
  return /* @__PURE__ */ jsxs13("div", { className: "max-w-2xl overflow-hidden rounded-lg text-sm text-gray-800 border border-gray-200/80 mt-4", children: [
775
784
  /* @__PURE__ */ jsxs13("div", { className: "flex items-center bg-gray-50 justify-between border-b border-gray-200/80", children: [
776
- /* @__PURE__ */ jsx16("div", { className: "text-xs text-gray-400 p-4", children: language }),
785
+ /* @__PURE__ */ jsx16("span", { className: "text-xs text-gray-400 p-4", children: title || language }),
777
786
  /* @__PURE__ */ jsx16(
778
787
  "button",
779
788
  {
@@ -3,7 +3,11 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  interface DocstraConfig {
4
4
  siteName?: string;
5
5
  editOnGithub?: boolean;
6
- formSyncFormID?: string;
6
+ feedback?: {
7
+ enabled?: boolean;
8
+ formSyncFormID?: string;
9
+ watermark?: boolean;
10
+ };
7
11
  githubRepo?: string;
8
12
  navbar?: {
9
13
  logo?: {
@@ -49,7 +53,7 @@ declare function getFileContents({ slug, CONTENT_DIR }: {
49
53
 
50
54
  declare function defineDocstraConfig<T extends DocstraConfig>(config: T): T;
51
55
 
52
- declare function DocstraMDXCompiler({ mdxContent, components }: {
56
+ declare function DocstraMDXCompiler({ mdxContent, components, }: {
53
57
  mdxContent: string;
54
58
  components?: Record<string, any>;
55
59
  }): react_jsx_runtime.JSX.Element;
@@ -3,7 +3,11 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  interface DocstraConfig {
4
4
  siteName?: string;
5
5
  editOnGithub?: boolean;
6
- formSyncFormID?: string;
6
+ feedback?: {
7
+ enabled?: boolean;
8
+ formSyncFormID?: string;
9
+ watermark?: boolean;
10
+ };
7
11
  githubRepo?: string;
8
12
  navbar?: {
9
13
  logo?: {
@@ -49,7 +53,7 @@ declare function getFileContents({ slug, CONTENT_DIR }: {
49
53
 
50
54
  declare function defineDocstraConfig<T extends DocstraConfig>(config: T): T;
51
55
 
52
- declare function DocstraMDXCompiler({ mdxContent, components }: {
56
+ declare function DocstraMDXCompiler({ mdxContent, components, }: {
53
57
  mdxContent: string;
54
58
  components?: Record<string, any>;
55
59
  }): react_jsx_runtime.JSX.Element;
@@ -81,9 +81,6 @@ function defineDocstraConfig(config) {
81
81
  // src/server/mdx-compiler.tsx
82
82
  var import_rsc = require("next-mdx-remote/rsc");
83
83
 
84
- // src/client/mdx-components.tsx
85
- var import_link = __toESM(require("next/link"));
86
-
87
84
  // src/client/create-heading.tsx
88
85
  var import_lucide_react = require("lucide-react");
89
86
 
@@ -101,9 +98,6 @@ function CreateHeading({ children, level }) {
101
98
  let fontSize;
102
99
  let iconSize;
103
100
  switch (level) {
104
- case 1:
105
- fontSize = "text-3xl";
106
- break;
107
101
  case 2:
108
102
  fontSize = "text-2xl";
109
103
  iconSize = "size-5";
@@ -124,7 +118,7 @@ function CreateHeading({ children, level }) {
124
118
  fontSize = "text-base";
125
119
  iconSize = "size-3.5";
126
120
  }
127
- 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)(
121
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
128
122
  Heading,
129
123
  {
130
124
  id,
@@ -137,39 +131,79 @@ function CreateHeading({ children, level }) {
137
131
  );
138
132
  }
139
133
 
140
- // src/client/mdx-components.tsx
134
+ // src/client/link.tsx
135
+ var import_link = __toESM(require("next/link"));
141
136
  var import_jsx_runtime2 = require("react/jsx-runtime");
137
+ function Link({ href, children, className, onNavigate, ...props }) {
138
+ if (href.startsWith("http")) {
139
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href, className, ...props, target: "_blank", rel: "noopener noreferrer", children });
140
+ }
141
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_link.default, { href, className, onNavigate, ...props, "aria-label": "Ns", children });
142
+ }
143
+
144
+ // src/client/mdx-components.tsx
145
+ var import_jsx_runtime3 = require("react/jsx-runtime");
142
146
  var defaultMdxComponents = {
143
- h1: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CreateHeading, { ...props, level: 1 }),
144
- h2: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CreateHeading, { ...props, level: 2 }),
145
- h3: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CreateHeading, { ...props, level: 3 }),
146
- h4: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CreateHeading, { ...props, level: 4 }),
147
- h5: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CreateHeading, { ...props, level: 5 }),
148
- p: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "my-5", children: props.children }),
149
- hr: () => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("hr", { className: "my-10 border-gray-200" }),
150
- a: (props) => props.href.startsWith("/") ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_link.default, { className: "underline underline-offset-4", href: props.href, children: props.children }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { className: "underline underline-offset-4", href: props.href, children: props.children }),
151
- ul: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { className: "list-['-'] pl-4 space-y-2", children: props.children }),
152
- li: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { className: "mb-2 pl-3 text-gray-700", children: props.children }),
153
- ol: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ol", { className: "list-decimal pl-4 space-y-2", children: props.children }),
154
- blockquote: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("blockquote", { className: "border-l-4 border-gray-200 pl-4 my-8 text-gray-700", children: props.children })
147
+ h1: (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { className: "text-3xl font-bold text-gray-900 mb-4", children: props.children }),
148
+ h2: (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CreateHeading, { ...props, level: 2 }),
149
+ h3: (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CreateHeading, { ...props, level: 3 }),
150
+ h4: (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CreateHeading, { ...props, level: 4 }),
151
+ h5: (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CreateHeading, { ...props, level: 5 }),
152
+ code: (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("code", { className: "px-1 py-0.5 rounded border border-gray-200 text-gray-800 text-sm", children: props.children }),
153
+ p: (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "my-5", children: props.children }),
154
+ hr: () => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("hr", { className: "my-10 border-gray-200" }),
155
+ a: (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Link, { href: props.href, children: props.children }),
156
+ ul: (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "list-['-'] pl-4 space-y-2", children: props.children }),
157
+ li: (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { className: "mb-2 pl-3 text-gray-700", children: props.children }),
158
+ ol: (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ol", { className: "list-decimal pl-4 space-y-2", children: props.children }),
159
+ blockquote: (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("blockquote", { className: "border-l-4 border-gray-200 pl-4 my-8 text-gray-700", children: props.children })
155
160
  };
156
161
 
162
+ // src/plugins/rehype-code-meta.ts
163
+ var import_unist_util_visit = require("unist-util-visit");
164
+ function rehypeCodeMetaProps() {
165
+ return (tree) => {
166
+ (0, import_unist_util_visit.visit)(tree, "element", (node) => {
167
+ if (node.tagName !== "pre") return;
168
+ const code = node.children?.[0];
169
+ if (!code || code.tagName !== "code") return;
170
+ const className = code.properties?.className?.[0];
171
+ const language = className?.replace("language-", "");
172
+ const meta = code.data?.meta || "";
173
+ const metaProps = {};
174
+ meta.replace(
175
+ /(\w+)=["']?([^"'\s]+)["']?/g,
176
+ (_match, key, value) => {
177
+ metaProps[key] = value;
178
+ return "";
179
+ }
180
+ );
181
+ node.properties = { ...node.properties || {}, language, ...metaProps };
182
+ });
183
+ };
184
+ }
185
+
157
186
  // src/server/mdx-compiler.tsx
158
- var import_jsx_runtime3 = require("react/jsx-runtime");
187
+ var import_jsx_runtime4 = require("react/jsx-runtime");
159
188
  function DocstraMDXCompiler({
160
189
  mdxContent,
161
190
  components = {}
162
191
  }) {
163
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
192
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
164
193
  import_rsc.MDXRemote,
165
194
  {
166
195
  source: mdxContent,
167
196
  components: {
168
197
  ...defaultMdxComponents,
169
198
  ...components
199
+ },
200
+ options: {
201
+ mdxOptions: {
202
+ rehypePlugins: [rehypeCodeMetaProps]
203
+ }
170
204
  }
171
205
  }
172
- ) });
206
+ );
173
207
  }
174
208
  // Annotate the CommonJS export names for ESM import in node:
175
209
  0 && (module.exports = {
@@ -42,9 +42,6 @@ function defineDocstraConfig(config) {
42
42
  // src/server/mdx-compiler.tsx
43
43
  import { MDXRemote } from "next-mdx-remote/rsc";
44
44
 
45
- // src/client/mdx-components.tsx
46
- import Link from "next/link";
47
-
48
45
  // src/client/create-heading.tsx
49
46
  import { LinkIcon } from "lucide-react";
50
47
 
@@ -62,9 +59,6 @@ function CreateHeading({ children, level }) {
62
59
  let fontSize;
63
60
  let iconSize;
64
61
  switch (level) {
65
- case 1:
66
- fontSize = "text-3xl";
67
- break;
68
62
  case 2:
69
63
  fontSize = "text-2xl";
70
64
  iconSize = "size-5";
@@ -85,7 +79,7 @@ function CreateHeading({ children, level }) {
85
79
  fontSize = "text-base";
86
80
  iconSize = "size-3.5";
87
81
  }
88
- return level === 1 ? /* @__PURE__ */ jsx("h1", { className: "text-3xl font-bold text-gray-900 mb-4", children }) : /* @__PURE__ */ jsxs(
82
+ return /* @__PURE__ */ jsxs(
89
83
  Heading,
90
84
  {
91
85
  id,
@@ -98,39 +92,79 @@ function CreateHeading({ children, level }) {
98
92
  );
99
93
  }
100
94
 
101
- // src/client/mdx-components.tsx
95
+ // src/client/link.tsx
96
+ import LinkComponent from "next/link";
102
97
  import { jsx as jsx2 } from "react/jsx-runtime";
98
+ function Link({ href, children, className, onNavigate, ...props }) {
99
+ if (href.startsWith("http")) {
100
+ return /* @__PURE__ */ jsx2("a", { href, className, ...props, target: "_blank", rel: "noopener noreferrer", children });
101
+ }
102
+ return /* @__PURE__ */ jsx2(LinkComponent, { href, className, onNavigate, ...props, "aria-label": "Ns", children });
103
+ }
104
+
105
+ // src/client/mdx-components.tsx
106
+ import { jsx as jsx3 } from "react/jsx-runtime";
103
107
  var defaultMdxComponents = {
104
- h1: (props) => /* @__PURE__ */ jsx2(CreateHeading, { ...props, level: 1 }),
105
- h2: (props) => /* @__PURE__ */ jsx2(CreateHeading, { ...props, level: 2 }),
106
- h3: (props) => /* @__PURE__ */ jsx2(CreateHeading, { ...props, level: 3 }),
107
- h4: (props) => /* @__PURE__ */ jsx2(CreateHeading, { ...props, level: 4 }),
108
- h5: (props) => /* @__PURE__ */ jsx2(CreateHeading, { ...props, level: 5 }),
109
- p: (props) => /* @__PURE__ */ jsx2("p", { className: "my-5", children: props.children }),
110
- hr: () => /* @__PURE__ */ jsx2("hr", { className: "my-10 border-gray-200" }),
111
- a: (props) => props.href.startsWith("/") ? /* @__PURE__ */ jsx2(Link, { className: "underline underline-offset-4", href: props.href, children: props.children }) : /* @__PURE__ */ jsx2("a", { className: "underline underline-offset-4", href: props.href, children: props.children }),
112
- ul: (props) => /* @__PURE__ */ jsx2("ul", { className: "list-['-'] pl-4 space-y-2", children: props.children }),
113
- li: (props) => /* @__PURE__ */ jsx2("li", { className: "mb-2 pl-3 text-gray-700", children: props.children }),
114
- ol: (props) => /* @__PURE__ */ jsx2("ol", { className: "list-decimal pl-4 space-y-2", children: props.children }),
115
- blockquote: (props) => /* @__PURE__ */ jsx2("blockquote", { className: "border-l-4 border-gray-200 pl-4 my-8 text-gray-700", children: props.children })
108
+ h1: (props) => /* @__PURE__ */ jsx3("h1", { className: "text-3xl font-bold text-gray-900 mb-4", children: props.children }),
109
+ h2: (props) => /* @__PURE__ */ jsx3(CreateHeading, { ...props, level: 2 }),
110
+ h3: (props) => /* @__PURE__ */ jsx3(CreateHeading, { ...props, level: 3 }),
111
+ h4: (props) => /* @__PURE__ */ jsx3(CreateHeading, { ...props, level: 4 }),
112
+ h5: (props) => /* @__PURE__ */ jsx3(CreateHeading, { ...props, level: 5 }),
113
+ code: (props) => /* @__PURE__ */ jsx3("code", { className: "px-1 py-0.5 rounded border border-gray-200 text-gray-800 text-sm", children: props.children }),
114
+ p: (props) => /* @__PURE__ */ jsx3("p", { className: "my-5", children: props.children }),
115
+ hr: () => /* @__PURE__ */ jsx3("hr", { className: "my-10 border-gray-200" }),
116
+ a: (props) => /* @__PURE__ */ jsx3(Link, { href: props.href, children: props.children }),
117
+ ul: (props) => /* @__PURE__ */ jsx3("ul", { className: "list-['-'] pl-4 space-y-2", children: props.children }),
118
+ li: (props) => /* @__PURE__ */ jsx3("li", { className: "mb-2 pl-3 text-gray-700", children: props.children }),
119
+ ol: (props) => /* @__PURE__ */ jsx3("ol", { className: "list-decimal pl-4 space-y-2", children: props.children }),
120
+ blockquote: (props) => /* @__PURE__ */ jsx3("blockquote", { className: "border-l-4 border-gray-200 pl-4 my-8 text-gray-700", children: props.children })
116
121
  };
117
122
 
123
+ // src/plugins/rehype-code-meta.ts
124
+ import { visit } from "unist-util-visit";
125
+ function rehypeCodeMetaProps() {
126
+ return (tree) => {
127
+ visit(tree, "element", (node) => {
128
+ if (node.tagName !== "pre") return;
129
+ const code = node.children?.[0];
130
+ if (!code || code.tagName !== "code") return;
131
+ const className = code.properties?.className?.[0];
132
+ const language = className?.replace("language-", "");
133
+ const meta = code.data?.meta || "";
134
+ const metaProps = {};
135
+ meta.replace(
136
+ /(\w+)=["']?([^"'\s]+)["']?/g,
137
+ (_match, key, value) => {
138
+ metaProps[key] = value;
139
+ return "";
140
+ }
141
+ );
142
+ node.properties = { ...node.properties || {}, language, ...metaProps };
143
+ });
144
+ };
145
+ }
146
+
118
147
  // src/server/mdx-compiler.tsx
119
- import { Fragment, jsx as jsx3 } from "react/jsx-runtime";
148
+ import { jsx as jsx4 } from "react/jsx-runtime";
120
149
  function DocstraMDXCompiler({
121
150
  mdxContent,
122
151
  components = {}
123
152
  }) {
124
- return /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsx3(
153
+ return /* @__PURE__ */ jsx4(
125
154
  MDXRemote,
126
155
  {
127
156
  source: mdxContent,
128
157
  components: {
129
158
  ...defaultMdxComponents,
130
159
  ...components
160
+ },
161
+ options: {
162
+ mdxOptions: {
163
+ rehypePlugins: [rehypeCodeMetaProps]
164
+ }
131
165
  }
132
166
  }
133
- ) });
167
+ );
134
168
  }
135
169
  export {
136
170
  DocstraMDXCompiler,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docstra",
3
- "version": "1.3.0",
3
+ "version": "1.4.5",
4
4
  "description": "A Modern Documentation Framework for Next.js",
5
5
  "keywords": [
6
6
  "next",
@@ -38,14 +38,14 @@
38
38
  "prettier": "prettier --write src"
39
39
  },
40
40
  "devDependencies": {
41
+ "@tailwindcss/cli": "^4.1.17",
41
42
  "@types/node": "^24.10.1",
42
43
  "@types/react": "^19.2.2",
43
44
  "@types/react-dom": "^19.2.2",
44
45
  "prettier": "^3.6.2",
45
- "tsup": "^8.5.0",
46
- "typescript": "^5.9.3",
47
46
  "tailwindcss": "^4.1.17",
48
- "@tailwindcss/cli": "^4.1.17"
47
+ "tsup": "^8.5.0",
48
+ "typescript": "^5.9.3"
49
49
  },
50
50
  "dependencies": {
51
51
  "clsx": "^2.1.1",
@@ -57,6 +57,7 @@
57
57
  "react": "^18 || ^19",
58
58
  "react-dom": "^18 || ^19",
59
59
  "react-toast-msg": "^2.2.6",
60
- "tailwind-merge": "^3.4.0"
60
+ "tailwind-merge": "^3.4.0",
61
+ "unist-util-visit": "^5.0.0"
61
62
  }
62
63
  }