blodemd 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/dev-server/app/[[...slug]]/page.tsx +139 -0
  2. package/dev-server/app/blodemd-dev/invalidate/route.ts +12 -0
  3. package/dev-server/app/blodemd-dev/static/[...path]/route.ts +32 -0
  4. package/dev-server/app/blodemd-dev/version/route.ts +14 -0
  5. package/dev-server/app/blodemd-internal/proxy/route.ts +86 -0
  6. package/dev-server/app/error.tsx +24 -0
  7. package/dev-server/app/globals.css +4 -0
  8. package/dev-server/app/layout.tsx +38 -0
  9. package/dev-server/app/not-found.tsx +18 -0
  10. package/dev-server/app/search/route.ts +17 -0
  11. package/dev-server/components/dev-reload-script.tsx +86 -0
  12. package/dev-server/components/providers.tsx +15 -0
  13. package/dev-server/lib/dev-state.ts +8 -0
  14. package/dev-server/lib/local-content-source.ts +103 -0
  15. package/dev-server/lib/local-runtime.tsx +558 -0
  16. package/dev-server/next.config.js +46 -0
  17. package/dev-server/package.json +57 -0
  18. package/dev-server/postcss.config.mjs +7 -0
  19. package/dev-server/public/glide-variable.woff2 +0 -0
  20. package/dev-server/tsconfig.json +49 -0
  21. package/dist/cli.mjs +108 -39
  22. package/dist/cli.mjs.map +1 -1
  23. package/docs/app/globals.css +455 -0
  24. package/docs/components/api/api-playground.tsx +295 -0
  25. package/docs/components/api/api-reference.tsx +121 -0
  26. package/docs/components/content/collection-index.tsx +114 -0
  27. package/docs/components/docs/contextual-menu.tsx +406 -0
  28. package/docs/components/docs/copy-page-menu.tsx +255 -0
  29. package/docs/components/docs/doc-header.tsx +192 -0
  30. package/docs/components/docs/doc-shell.tsx +289 -0
  31. package/docs/components/docs/doc-sidebar.tsx +206 -0
  32. package/docs/components/docs/doc-toc.tsx +45 -0
  33. package/docs/components/docs/mobile-nav.tsx +207 -0
  34. package/docs/components/mdx/accordion.tsx +83 -0
  35. package/docs/components/mdx/badge.tsx +79 -0
  36. package/docs/components/mdx/callout.tsx +88 -0
  37. package/docs/components/mdx/card.tsx +104 -0
  38. package/docs/components/mdx/code-block.tsx +75 -0
  39. package/docs/components/mdx/code-group.tsx +94 -0
  40. package/docs/components/mdx/color.tsx +87 -0
  41. package/docs/components/mdx/columns.tsx +25 -0
  42. package/docs/components/mdx/expandable.tsx +45 -0
  43. package/docs/components/mdx/field-layout.tsx +77 -0
  44. package/docs/components/mdx/frame.tsx +23 -0
  45. package/docs/components/mdx/get-text-content.ts +18 -0
  46. package/docs/components/mdx/icon.tsx +56 -0
  47. package/docs/components/mdx/index.tsx +96 -0
  48. package/docs/components/mdx/installer.tsx +20 -0
  49. package/docs/components/mdx/panel.tsx +11 -0
  50. package/docs/components/mdx/param-field.tsx +56 -0
  51. package/docs/components/mdx/preview.tsx +36 -0
  52. package/docs/components/mdx/prompt.tsx +63 -0
  53. package/docs/components/mdx/request-example.tsx +27 -0
  54. package/docs/components/mdx/response-field.tsx +42 -0
  55. package/docs/components/mdx/steps.tsx +92 -0
  56. package/docs/components/mdx/tabs.tsx +88 -0
  57. package/docs/components/mdx/tile.tsx +43 -0
  58. package/docs/components/mdx/tooltip.tsx +71 -0
  59. package/docs/components/mdx/tree.tsx +120 -0
  60. package/docs/components/mdx/type-table.tsx +71 -0
  61. package/docs/components/mdx/update.tsx +44 -0
  62. package/docs/components/mdx/video.tsx +12 -0
  63. package/docs/components/mdx/view.tsx +66 -0
  64. package/docs/components/providers.tsx +15 -0
  65. package/docs/components/ui/breadcrumb.tsx +92 -0
  66. package/docs/components/ui/button.tsx +90 -0
  67. package/docs/components/ui/card.tsx +92 -0
  68. package/docs/components/ui/command.tsx +139 -0
  69. package/docs/components/ui/dialog.tsx +97 -0
  70. package/docs/components/ui/field.tsx +237 -0
  71. package/docs/components/ui/input.tsx +105 -0
  72. package/docs/components/ui/label.tsx +22 -0
  73. package/docs/components/ui/popover.tsx +72 -0
  74. package/docs/components/ui/search.tsx +380 -0
  75. package/docs/components/ui/separator.tsx +26 -0
  76. package/docs/components/ui/sheet.tsx +104 -0
  77. package/docs/components/ui/sidebar.tsx +433 -0
  78. package/docs/components/ui/theme-toggle.tsx +62 -0
  79. package/docs/components/ui/tooltip.tsx +53 -0
  80. package/docs/lib/contextual-options.ts +193 -0
  81. package/docs/lib/docs-collection.ts +22 -0
  82. package/docs/lib/mdx.ts +90 -0
  83. package/docs/lib/navigation.ts +288 -0
  84. package/docs/lib/openapi.ts +158 -0
  85. package/docs/lib/routes.ts +10 -0
  86. package/docs/lib/server-cache.ts +83 -0
  87. package/docs/lib/shiki.ts +35 -0
  88. package/docs/lib/theme.ts +29 -0
  89. package/docs/lib/toc.ts +2 -0
  90. package/docs/lib/utils.ts +5 -0
  91. package/package.json +33 -4
  92. package/packages/@repo/common/dist/index.d.ts +9 -0
  93. package/packages/@repo/common/dist/index.d.ts.map +1 -0
  94. package/packages/@repo/common/dist/index.js +42 -0
  95. package/packages/@repo/common/package.json +34 -0
  96. package/packages/@repo/common/src/common.unit.test.ts +55 -0
  97. package/packages/@repo/common/src/index.ts +51 -0
  98. package/packages/@repo/contracts/dist/api-key.d.ts +30 -0
  99. package/packages/@repo/contracts/dist/api-key.d.ts.map +1 -0
  100. package/packages/@repo/contracts/dist/api-key.js +20 -0
  101. package/packages/@repo/contracts/dist/dates.d.ts +4 -0
  102. package/packages/@repo/contracts/dist/dates.d.ts.map +1 -0
  103. package/packages/@repo/contracts/dist/dates.js +2 -0
  104. package/packages/@repo/contracts/dist/deployment.d.ts +71 -0
  105. package/packages/@repo/contracts/dist/deployment.d.ts.map +1 -0
  106. package/packages/@repo/contracts/dist/deployment.js +46 -0
  107. package/packages/@repo/contracts/dist/domain.d.ts +94 -0
  108. package/packages/@repo/contracts/dist/domain.d.ts.map +1 -0
  109. package/packages/@repo/contracts/dist/domain.js +36 -0
  110. package/packages/@repo/contracts/dist/ids.d.ts +14 -0
  111. package/packages/@repo/contracts/dist/ids.d.ts.map +1 -0
  112. package/packages/@repo/contracts/dist/ids.js +10 -0
  113. package/packages/@repo/contracts/dist/index.d.ts +10 -0
  114. package/packages/@repo/contracts/dist/index.d.ts.map +1 -0
  115. package/packages/@repo/contracts/dist/index.js +11 -0
  116. package/packages/@repo/contracts/dist/pagination.d.ts +23 -0
  117. package/packages/@repo/contracts/dist/pagination.d.ts.map +1 -0
  118. package/packages/@repo/contracts/dist/pagination.js +15 -0
  119. package/packages/@repo/contracts/dist/project.d.ts +25 -0
  120. package/packages/@repo/contracts/dist/project.d.ts.map +1 -0
  121. package/packages/@repo/contracts/dist/project.js +23 -0
  122. package/packages/@repo/contracts/dist/tenant.d.ts +99 -0
  123. package/packages/@repo/contracts/dist/tenant.d.ts.map +1 -0
  124. package/packages/@repo/contracts/dist/tenant.js +36 -0
  125. package/packages/@repo/contracts/dist/user.d.ts +9 -0
  126. package/packages/@repo/contracts/dist/user.d.ts.map +1 -0
  127. package/packages/@repo/contracts/dist/user.js +9 -0
  128. package/packages/@repo/contracts/package.json +37 -0
  129. package/packages/@repo/contracts/src/api-key.ts +27 -0
  130. package/packages/@repo/contracts/src/dates.ts +4 -0
  131. package/packages/@repo/contracts/src/deployment.ts +73 -0
  132. package/packages/@repo/contracts/src/domain.ts +51 -0
  133. package/packages/@repo/contracts/src/ids.ts +22 -0
  134. package/packages/@repo/contracts/src/index.ts +11 -0
  135. package/packages/@repo/contracts/src/pagination.ts +21 -0
  136. package/packages/@repo/contracts/src/project.ts +30 -0
  137. package/packages/@repo/contracts/src/tenant.ts +54 -0
  138. package/packages/@repo/contracts/src/user.ts +12 -0
  139. package/packages/@repo/models/dist/docs-config.d.ts +985 -0
  140. package/packages/@repo/models/dist/docs-config.d.ts.map +1 -0
  141. package/packages/@repo/models/dist/docs-config.js +548 -0
  142. package/packages/@repo/models/dist/index.d.ts +3 -0
  143. package/packages/@repo/models/dist/index.d.ts.map +1 -0
  144. package/packages/@repo/models/dist/index.js +3 -0
  145. package/packages/@repo/models/dist/tenant.d.ts +25 -0
  146. package/packages/@repo/models/dist/tenant.d.ts.map +1 -0
  147. package/packages/@repo/models/dist/tenant.js +1 -0
  148. package/packages/@repo/models/package.json +37 -0
  149. package/packages/@repo/models/src/docs-config.ts +648 -0
  150. package/packages/@repo/models/src/index.ts +3 -0
  151. package/packages/@repo/models/src/tenant.ts +29 -0
  152. package/packages/@repo/prebuild/dist/index.d.ts +2 -0
  153. package/packages/@repo/prebuild/dist/index.d.ts.map +1 -0
  154. package/packages/@repo/prebuild/dist/index.js +2 -0
  155. package/packages/@repo/prebuild/dist/openapi.d.ts +43 -0
  156. package/packages/@repo/prebuild/dist/openapi.d.ts.map +1 -0
  157. package/packages/@repo/prebuild/dist/openapi.js +58 -0
  158. package/packages/@repo/prebuild/package.json +39 -0
  159. package/packages/@repo/prebuild/src/index.ts +2 -0
  160. package/packages/@repo/prebuild/src/openapi.ts +116 -0
  161. package/packages/@repo/previewing/dist/blob-source.d.ts +16 -0
  162. package/packages/@repo/previewing/dist/blob-source.d.ts.map +1 -0
  163. package/packages/@repo/previewing/dist/blob-source.js +110 -0
  164. package/packages/@repo/previewing/dist/content-source.d.ts +12 -0
  165. package/packages/@repo/previewing/dist/content-source.d.ts.map +1 -0
  166. package/packages/@repo/previewing/dist/content-source.js +1 -0
  167. package/packages/@repo/previewing/dist/fs-source.d.ts +11 -0
  168. package/packages/@repo/previewing/dist/fs-source.d.ts.map +1 -0
  169. package/packages/@repo/previewing/dist/fs-source.js +79 -0
  170. package/packages/@repo/previewing/dist/index.d.ts +120 -0
  171. package/packages/@repo/previewing/dist/index.d.ts.map +1 -0
  172. package/packages/@repo/previewing/dist/index.js +984 -0
  173. package/packages/@repo/previewing/package.json +41 -0
  174. package/packages/@repo/previewing/src/blob-source.ts +167 -0
  175. package/packages/@repo/previewing/src/content-source.ts +12 -0
  176. package/packages/@repo/previewing/src/fs-source.ts +111 -0
  177. package/packages/@repo/previewing/src/index.ts +1490 -0
  178. package/packages/@repo/previewing/src/index.unit.test.ts +290 -0
  179. package/packages/@repo/validation/dist/index.d.ts +12 -0
  180. package/packages/@repo/validation/dist/index.d.ts.map +1 -0
  181. package/packages/@repo/validation/dist/index.js +30 -0
  182. package/packages/@repo/validation/package.json +37 -0
  183. package/packages/@repo/validation/src/index.ts +59 -0
  184. package/packages/@repo/validation/src/mintlify-docs-schema.json +5016 -0
@@ -0,0 +1,192 @@
1
+ import type { SiteConfig } from "@repo/models";
2
+ import Image from "next/image";
3
+ import Link from "next/link";
4
+
5
+ import { MobileNav } from "@/components/docs/mobile-nav";
6
+ import { Search } from "@/components/ui/search";
7
+ import { ThemeToggle } from "@/components/ui/theme-toggle";
8
+ import type { NavEntry, NavTab } from "@/lib/navigation";
9
+ import { toDocHref } from "@/lib/routes";
10
+ import { cn } from "@/lib/utils";
11
+
12
+ const Dropdown = ({
13
+ label,
14
+ items,
15
+ }: {
16
+ label: string;
17
+ items: { label: string; url: string }[];
18
+ }) => {
19
+ if (!items.length) {
20
+ return null;
21
+ }
22
+ return (
23
+ <details className="relative">
24
+ <summary className="cursor-pointer rounded-full border border-border bg-background px-3 py-1.5 text-sm">
25
+ {label}
26
+ </summary>
27
+ <div className="absolute right-0 top-11 z-20 grid min-w-36 overflow-hidden rounded-xl border border-border bg-popover shadow-popover">
28
+ {items.map((item) => (
29
+ <Link
30
+ className="px-3 py-2 hover:bg-accent"
31
+ href={item.url}
32
+ key={item.label}
33
+ >
34
+ {item.label}
35
+ </Link>
36
+ ))}
37
+ </div>
38
+ </details>
39
+ );
40
+ };
41
+
42
+ const HeaderTabs = ({
43
+ tabs,
44
+ activeTabIndex = 0,
45
+ basePath,
46
+ }: {
47
+ tabs: NavTab[];
48
+ activeTabIndex?: number;
49
+ basePath: string;
50
+ }) => (
51
+ <nav
52
+ aria-label="Navigation tabs"
53
+ className="ml-4 hidden items-center gap-0.5 lg:flex"
54
+ >
55
+ {tabs.map((tab, index) => {
56
+ const isActive = index === activeTabIndex;
57
+ const href =
58
+ tab.href ??
59
+ (tab.slugPrefix ? toDocHref(tab.slugPrefix, basePath) : undefined);
60
+
61
+ if (!href) {
62
+ return null;
63
+ }
64
+
65
+ const isExternal = tab.href?.startsWith("http");
66
+
67
+ return (
68
+ <Link
69
+ className={cn(
70
+ "relative px-2.5 py-1.5 text-sm transition-colors",
71
+ isActive
72
+ ? "font-medium text-primary"
73
+ : "text-muted-foreground hover:text-foreground"
74
+ )}
75
+ href={href}
76
+ key={tab.label}
77
+ rel={isExternal ? "noopener noreferrer" : undefined}
78
+ target={isExternal ? "_blank" : undefined}
79
+ >
80
+ {tab.label}
81
+ {isActive ? (
82
+ <span className="absolute inset-x-1 -bottom-3.5 h-0.5 rounded-full bg-primary" />
83
+ ) : null}
84
+ </Link>
85
+ );
86
+ })}
87
+ </nav>
88
+ );
89
+
90
+ // oxlint-disable-next-line eslint/complexity
91
+ export const DocHeader = ({
92
+ config,
93
+ basePath,
94
+ tabs,
95
+ activeTabIndex,
96
+ nav = [],
97
+ }: {
98
+ config: SiteConfig;
99
+ basePath: string;
100
+ tabs?: NavTab[] | null;
101
+ activeTabIndex?: number;
102
+ nav?: NavEntry[];
103
+ }) => {
104
+ const globalLinks = config.navigation?.global?.links ?? [];
105
+ const versions = config.navigation?.versions ?? [];
106
+ const languages = config.navigation?.languages ?? [];
107
+ const [primaryVersion] = versions;
108
+ const [primaryLanguage] = languages;
109
+ const searchDisabled = config.features?.search === false;
110
+ const themeToggleDisabled = config.features?.themeToggle === false;
111
+
112
+ return (
113
+ <header className="sticky top-0 z-50 w-full bg-background">
114
+ <div className="container-wrapper px-6">
115
+ <div className="flex h-(--header-height) items-center">
116
+ <MobileNav
117
+ activeTabIndex={activeTabIndex}
118
+ basePath={basePath}
119
+ entries={nav}
120
+ globalLinks={globalLinks}
121
+ tabs={tabs}
122
+ />
123
+ <Link
124
+ className="flex items-center gap-2.5"
125
+ href={toDocHref("index", basePath)}
126
+ >
127
+ {config.logo?.light ? (
128
+ <Image
129
+ alt={config.logo.alt ?? config.name}
130
+ className="dark:hidden"
131
+ data-logo="light"
132
+ height={32}
133
+ loading="eager"
134
+ src={config.logo.light}
135
+ unoptimized
136
+ width={140}
137
+ />
138
+ ) : null}
139
+ {config.logo?.dark ? (
140
+ <Image
141
+ alt={config.logo.alt ?? config.name}
142
+ className="hidden dark:inline-block"
143
+ data-logo="dark"
144
+ height={32}
145
+ loading="eager"
146
+ src={config.logo.dark}
147
+ unoptimized
148
+ width={140}
149
+ />
150
+ ) : null}
151
+ {config.logo?.light || config.logo?.dark ? null : (
152
+ <span className="text-xl font-bold">{config.name}</span>
153
+ )}
154
+ </Link>
155
+ {tabs?.length ? (
156
+ <HeaderTabs
157
+ activeTabIndex={activeTabIndex}
158
+ basePath={basePath}
159
+ tabs={tabs}
160
+ />
161
+ ) : null}
162
+ <nav
163
+ aria-label="External links"
164
+ className="hidden items-center gap-0 text-sm text-muted-foreground lg:flex"
165
+ >
166
+ {globalLinks.map((link) => (
167
+ <a
168
+ className="rounded-lg px-2.5 py-1.5 transition-colors hover:bg-muted hover:text-foreground"
169
+ href={link.href}
170
+ key={link.label}
171
+ rel="noopener noreferrer"
172
+ target="_blank"
173
+ >
174
+ {link.label}
175
+ </a>
176
+ ))}
177
+ </nav>
178
+ <div className="ml-auto flex items-center gap-2 md:flex-1 md:justify-end">
179
+ {searchDisabled ? null : <Search basePath={basePath} />}
180
+ {primaryVersion ? (
181
+ <Dropdown items={versions} label={primaryVersion.label} />
182
+ ) : null}
183
+ {primaryLanguage ? (
184
+ <Dropdown items={languages} label={primaryLanguage.label} />
185
+ ) : null}
186
+ {themeToggleDisabled ? null : <ThemeToggle />}
187
+ </div>
188
+ </div>
189
+ </div>
190
+ </header>
191
+ );
192
+ };
@@ -0,0 +1,289 @@
1
+ import type { PageMode, SiteConfig } from "@repo/models";
2
+ import { ArrowLeftIcon, ArrowRightIcon } from "blode-icons-react";
3
+ import Link from "next/link";
4
+ import Script from "next/script";
5
+ import { Fragment } from "react";
6
+ import type { ReactNode } from "react";
7
+
8
+ import {
9
+ ContextualMenu,
10
+ ContextualTocItems,
11
+ } from "@/components/docs/contextual-menu";
12
+ import { CopyPageMenu } from "@/components/docs/copy-page-menu";
13
+ import { DocHeader } from "@/components/docs/doc-header";
14
+ import { DocSidebar } from "@/components/docs/doc-sidebar";
15
+ import { DocToc } from "@/components/docs/doc-toc";
16
+ import {
17
+ Breadcrumb,
18
+ BreadcrumbItem,
19
+ BreadcrumbLink,
20
+ BreadcrumbList,
21
+ BreadcrumbPage,
22
+ BreadcrumbSeparator,
23
+ } from "@/components/ui/breadcrumb";
24
+ import { Button } from "@/components/ui/button";
25
+ import type { NavEntry, NavTab } from "@/lib/navigation";
26
+ import { toDocHref } from "@/lib/routes";
27
+ import { themeStylesFromConfig } from "@/lib/theme";
28
+ import type { TocItem } from "@/lib/toc";
29
+ import { cn } from "@/lib/utils";
30
+
31
+ const renderScripts = (
32
+ scripts?: string[],
33
+ strategy: "afterInteractive" | "lazyOnload" = "afterInteractive"
34
+ ) =>
35
+ scripts?.map((script) => (
36
+ <Script key={script} src={script} strategy={strategy} />
37
+ )) ?? null;
38
+
39
+ const Breadcrumbs = ({
40
+ basePath,
41
+ breadcrumbs,
42
+ }: {
43
+ basePath: string;
44
+ breadcrumbs: { label: string; path: string }[];
45
+ }) => {
46
+ if (!breadcrumbs.length) {
47
+ return null;
48
+ }
49
+
50
+ return (
51
+ <Breadcrumb>
52
+ <BreadcrumbList>
53
+ {breadcrumbs.map((crumb, index) => {
54
+ const key = `${crumb.path || "current"}-${crumb.label}`;
55
+ const isLast = index === breadcrumbs.length - 1;
56
+ return (
57
+ <Fragment key={key}>
58
+ <BreadcrumbItem>
59
+ {isLast ? (
60
+ <BreadcrumbPage>{crumb.label}</BreadcrumbPage>
61
+ ) : (
62
+ <BreadcrumbLink asChild>
63
+ <Link href={toDocHref(crumb.path, basePath)}>
64
+ {crumb.label}
65
+ </Link>
66
+ </BreadcrumbLink>
67
+ )}
68
+ </BreadcrumbItem>
69
+ {!isLast && <BreadcrumbSeparator />}
70
+ </Fragment>
71
+ );
72
+ })}
73
+ </BreadcrumbList>
74
+ </Breadcrumb>
75
+ );
76
+ };
77
+
78
+ // oxlint-disable-next-line eslint/complexity
79
+ export const DocShell = ({
80
+ config,
81
+ nav,
82
+ prevPage,
83
+ nextPage,
84
+ toc,
85
+ content,
86
+ currentPath,
87
+ breadcrumbs,
88
+ pageTitle,
89
+ pageDescription,
90
+ anchors,
91
+ activeTabIndex,
92
+ basePath,
93
+ markdownHref,
94
+ rawContent,
95
+ tabs,
96
+ mode,
97
+ deprecated,
98
+ hideFooterPagination,
99
+ }: {
100
+ config: SiteConfig;
101
+ nav: NavEntry[];
102
+ prevPage?: { title: string; path: string };
103
+ nextPage?: { title: string; path: string };
104
+ toc: TocItem[];
105
+ content: ReactNode;
106
+ currentPath: string;
107
+ breadcrumbs: { label: string; path: string }[];
108
+ pageTitle: string;
109
+ pageDescription?: string;
110
+ anchors?: { label: string; href: string }[];
111
+ activeTabIndex?: number;
112
+ basePath: string;
113
+ markdownHref?: string;
114
+ rawContent?: string;
115
+ tabs?: NavTab[] | null;
116
+ mode?: PageMode;
117
+ deprecated?: boolean;
118
+ hideFooterPagination?: boolean;
119
+ }) => {
120
+ const pageMode = mode ?? "default";
121
+ const isCustomMode = pageMode === "custom";
122
+ const showSidebar =
123
+ pageMode !== "custom" &&
124
+ pageMode !== "center" &&
125
+ Boolean((nav?.length ?? 0) || (anchors?.length ?? 0));
126
+ const { contextual } = config;
127
+ const contextualDisplay = contextual?.display ?? "header";
128
+ const hasToc =
129
+ pageMode !== "custom" &&
130
+ pageMode !== "wide" &&
131
+ pageMode !== "frame" &&
132
+ pageMode !== "center" &&
133
+ config.features?.rightToc !== false &&
134
+ config.features?.toc !== false &&
135
+ (toc.length > 0 || (contextual && contextualDisplay === "toc"));
136
+
137
+ const contextualTocItems =
138
+ contextual && contextualDisplay === "toc" && rawContent !== undefined ? (
139
+ <ContextualTocItems
140
+ content={rawContent}
141
+ options={contextual.options}
142
+ pagePath={currentPath}
143
+ title={pageTitle}
144
+ />
145
+ ) : null;
146
+
147
+ const headerContextualMenu =
148
+ contextual && contextualDisplay === "header" && rawContent !== undefined ? (
149
+ <ContextualMenu
150
+ content={rawContent}
151
+ options={contextual.options}
152
+ pagePath={currentPath}
153
+ title={pageTitle}
154
+ />
155
+ ) : null;
156
+
157
+ const innerContent = (
158
+ <div className="flex min-w-0 flex-1 flex-col">
159
+ <div className="h-(--top-spacing) shrink-0" />
160
+ {isCustomMode ? (
161
+ <main id="main-content">{content}</main>
162
+ ) : (
163
+ <main
164
+ className={cn(
165
+ "flex scroll-mt-24 items-stretch gap-1 px-4 lg:px-8",
166
+ !showSidebar && "mx-auto max-w-[960px]"
167
+ )}
168
+ id="main-content"
169
+ >
170
+ <div
171
+ className={cn(
172
+ "mx-auto flex w-full min-w-0 flex-1 flex-col gap-6 px-4 py-6 text-neutral-800 md:px-0 lg:py-8 dark:text-neutral-300",
173
+ pageMode === "wide" ? "max-w-[60rem]" : "max-w-[40rem]"
174
+ )}
175
+ >
176
+ <div className="flex flex-col gap-2">
177
+ <Breadcrumbs basePath={basePath} breadcrumbs={breadcrumbs} />
178
+ <div className="flex flex-col gap-2">
179
+ <div className="flex items-center justify-between md:items-start">
180
+ <h1 className="scroll-m-24 text-3xl font-semibold tracking-tight sm:text-3xl">
181
+ {pageTitle}
182
+ {deprecated ? (
183
+ <span className="ml-3 inline-flex translate-y-[-2px] items-center rounded-md bg-yellow-100 px-2 py-0.5 align-middle text-xs font-medium text-yellow-800 dark:bg-yellow-900/40 dark:text-yellow-300">
184
+ Deprecated
185
+ </span>
186
+ ) : null}
187
+ </h1>
188
+ {headerContextualMenu ??
189
+ (rawContent === undefined &&
190
+ markdownHref === undefined ? null : (
191
+ <CopyPageMenu
192
+ content={rawContent}
193
+ contentUrl={markdownHref}
194
+ title={pageTitle}
195
+ />
196
+ ))}
197
+ </div>
198
+ {pageDescription ? (
199
+ <p className="text-[1.05rem] text-muted-foreground sm:text-balance sm:text-base md:max-w-[80%]">
200
+ {pageDescription}
201
+ </p>
202
+ ) : null}
203
+ </div>
204
+ </div>
205
+ <div className="grid gap-4.5 leading-relaxed [&_blockquote]:border-l-3 [&_blockquote]:border-primary [&_blockquote]:pl-3.5 [&_blockquote]:text-muted-foreground [&_h2]:mt-10 [&_h2]:mb-3 [&_h2]:text-2xl [&_h2]:font-bold [&_h3]:mt-8 [&_h3]:mb-2 [&_h3]:text-[1.375rem] [&_h3]:font-semibold [&_h4]:mt-6 [&_h4]:mb-2 [&_h4]:text-base [&_h4]:font-semibold [&_ol]:list-decimal [&_ol]:pl-5 [&_table]:w-full [&_table]:border-collapse [&_table]:text-sm [&_td]:border-b [&_td]:border-border [&_td]:px-2.5 [&_td]:py-2 [&_td]:text-left [&_th]:border-b [&_th]:border-border [&_th]:px-2.5 [&_th]:py-2 [&_th]:text-left [&_ul]:list-disc [&_ul]:pl-5">
206
+ {content}
207
+ </div>
208
+ {!hideFooterPagination && (prevPage || nextPage) ? (
209
+ <nav className="hidden h-16 w-full items-center gap-2 px-4 sm:flex sm:px-0">
210
+ {prevPage ? (
211
+ <Button asChild size="sm" variant="secondary">
212
+ <Link href={toDocHref(prevPage.path, basePath)}>
213
+ <ArrowLeftIcon aria-hidden="true" />
214
+ {prevPage.title}
215
+ </Link>
216
+ </Button>
217
+ ) : null}
218
+ {nextPage ? (
219
+ <Button
220
+ asChild
221
+ className="ml-auto"
222
+ size="sm"
223
+ variant="secondary"
224
+ >
225
+ <Link href={toDocHref(nextPage.path, basePath)}>
226
+ {nextPage.title}
227
+ <ArrowRightIcon aria-hidden="true" />
228
+ </Link>
229
+ </Button>
230
+ ) : null}
231
+ </nav>
232
+ ) : null}
233
+ </div>
234
+ {hasToc ? (
235
+ <DocToc contextualItems={contextualTocItems} toc={toc} />
236
+ ) : null}
237
+ </main>
238
+ )}
239
+ </div>
240
+ );
241
+
242
+ return (
243
+ <div
244
+ className="min-h-screen font-sans"
245
+ data-has-dark-logo={config.logo?.dark ? "true" : "false"}
246
+ style={themeStylesFromConfig(config)}
247
+ >
248
+ <a
249
+ className="sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-[100] focus:rounded-md focus:bg-background focus:px-4 focus:py-2 focus:text-sm focus:font-medium focus:shadow-sm"
250
+ href="#main-content"
251
+ >
252
+ Skip to content
253
+ </a>
254
+ {renderScripts(config.scripts?.head)}
255
+ <DocHeader
256
+ activeTabIndex={activeTabIndex}
257
+ basePath={basePath}
258
+ config={config}
259
+ nav={nav}
260
+ tabs={tabs}
261
+ />
262
+ <div className="container-wrapper flex flex-1 flex-col px-6">
263
+ {showSidebar ? (
264
+ <div
265
+ className="min-h-min flex-1 items-start px-0 [--top-spacing:0] lg:grid lg:grid-cols-[var(--sidebar-width)_minmax(0,1fr)] lg:[--top-spacing:calc(var(--spacing)*4)]"
266
+ style={
267
+ {
268
+ "--sidebar-width": "calc(var(--spacing) * 72)",
269
+ } as React.CSSProperties
270
+ }
271
+ >
272
+ <DocSidebar
273
+ anchors={anchors}
274
+ basePath={basePath}
275
+ currentPath={currentPath}
276
+ entries={nav}
277
+ />
278
+ {innerContent}
279
+ </div>
280
+ ) : (
281
+ <div className="min-h-min flex-1 items-start px-0 [--top-spacing:0] lg:[--top-spacing:calc(var(--spacing)*4)]">
282
+ {innerContent}
283
+ </div>
284
+ )}
285
+ </div>
286
+ {renderScripts(config.scripts?.body, "lazyOnload")}
287
+ </div>
288
+ );
289
+ };
@@ -0,0 +1,206 @@
1
+ import { normalizePath } from "@repo/common";
2
+ import { ArrowUpRightIcon } from "blode-icons-react";
3
+ import Image from "next/image";
4
+ import Link from "next/link";
5
+
6
+ import { getNavPageHref, getNavPageTitle } from "@/lib/navigation";
7
+ import type { NavEntry, NavPage } from "@/lib/navigation";
8
+ import { toDocHref } from "@/lib/routes";
9
+ import { cn } from "@/lib/utils";
10
+
11
+ const MENU_BUTTON_CLASS =
12
+ "relative flex min-h-[30px] items-center gap-2 overflow-visible rounded-md border border-transparent px-2 text-[0.8rem] font-medium transition-colors hover:bg-accent/70 hover:text-foreground";
13
+
14
+ const NavIcon = ({ icon }: { icon: string }) => {
15
+ if (icon.startsWith("http") || icon.startsWith("/")) {
16
+ return (
17
+ <Image
18
+ alt=""
19
+ className="size-4 shrink-0"
20
+ height={16}
21
+ src={icon}
22
+ unoptimized
23
+ width={16}
24
+ />
25
+ );
26
+ }
27
+ return (
28
+ <span className="size-4 shrink-0 text-center text-[10px] leading-4 text-muted-foreground">
29
+ {icon.slice(0, 2)}
30
+ </span>
31
+ );
32
+ };
33
+
34
+ const NavPageLink = ({
35
+ item,
36
+ basePath,
37
+ isActive,
38
+ }: {
39
+ item: NavPage;
40
+ basePath: string;
41
+ isActive: boolean;
42
+ }) => {
43
+ const displayTitle = getNavPageTitle(item);
44
+
45
+ const linkContent = (
46
+ <>
47
+ {item.icon ? <NavIcon icon={item.icon} /> : null}
48
+ <span className={item.deprecated ? "line-through opacity-60" : undefined}>
49
+ {displayTitle}
50
+ </span>
51
+ {item.tag ? (
52
+ <span className="ml-auto shrink-0 rounded bg-primary/10 px-1.5 py-0.5 text-[10px] font-medium leading-none text-primary">
53
+ {item.tag}
54
+ </span>
55
+ ) : null}
56
+ {item.deprecated && !item.tag ? (
57
+ <span className="ml-auto shrink-0 rounded bg-yellow-100 px-1.5 py-0.5 text-[10px] font-medium leading-none text-yellow-800 dark:bg-yellow-900/40 dark:text-yellow-300">
58
+ Deprecated
59
+ </span>
60
+ ) : null}
61
+ {item.url ? (
62
+ <ArrowUpRightIcon
63
+ aria-hidden="true"
64
+ className="ml-auto size-3 shrink-0 text-muted-foreground"
65
+ />
66
+ ) : null}
67
+ </>
68
+ );
69
+
70
+ const className = cn(
71
+ MENU_BUTTON_CLASS,
72
+ isActive && "border-accent bg-accent text-foreground"
73
+ );
74
+
75
+ if (item.url) {
76
+ return (
77
+ <a
78
+ className={className}
79
+ href={item.url}
80
+ rel="noopener noreferrer"
81
+ target="_blank"
82
+ >
83
+ {linkContent}
84
+ </a>
85
+ );
86
+ }
87
+
88
+ return (
89
+ <Link className={className} href={getNavPageHref(item, basePath)}>
90
+ {linkContent}
91
+ </Link>
92
+ );
93
+ };
94
+
95
+ const Section = ({
96
+ title,
97
+ children,
98
+ paddedTop = false,
99
+ }: {
100
+ title?: string;
101
+ children: React.ReactNode;
102
+ paddedTop?: boolean;
103
+ }) => (
104
+ <section
105
+ className={cn(
106
+ "relative flex w-full min-w-0 flex-col p-2",
107
+ paddedTop && "pt-6"
108
+ )}
109
+ >
110
+ {title ? (
111
+ <div className="mb-2 px-2 font-medium text-muted-foreground text-xs">
112
+ {title}
113
+ </div>
114
+ ) : null}
115
+ <div className="w-full text-sm">{children}</div>
116
+ </section>
117
+ );
118
+
119
+ export const DocSidebar = ({
120
+ entries,
121
+ currentPath,
122
+ anchors,
123
+ basePath,
124
+ }: {
125
+ entries: NavEntry[];
126
+ currentPath: string;
127
+ anchors?: { label: string; href: string }[];
128
+ basePath: string;
129
+ }) => {
130
+ const activePath = normalizePath(currentPath);
131
+ const isActive = (path: string) => normalizePath(path) === activePath;
132
+
133
+ return (
134
+ <aside
135
+ className="sticky top-[calc(var(--header-height)+0.6rem)] z-30 hidden h-[calc(100svh-10rem)] w-[calc(var(--spacing)*56)] shrink-0 flex-col overscroll-none bg-transparent lg:flex"
136
+ aria-label="Documentation navigation"
137
+ >
138
+ <div className="h-9" />
139
+ <div className="absolute top-8 z-10 h-8 w-full shrink-0 bg-gradient-to-b from-background via-background/80 to-background/50 blur-xs" />
140
+ <div className="absolute top-12 right-2 bottom-0 hidden h-full w-px bg-gradient-to-b from-transparent via-border to-transparent lg:flex" />
141
+ <div className="no-scrollbar mx-auto flex min-h-0 w-full flex-1 flex-col gap-2 overflow-y-auto overflow-x-hidden px-2">
142
+ {anchors?.length ? (
143
+ <Section paddedTop title="Pinned">
144
+ <ul className="space-y-1">
145
+ {anchors.map((anchor) => (
146
+ <li key={anchor.href}>
147
+ <a
148
+ className={cn(MENU_BUTTON_CLASS, "text-foreground")}
149
+ href={
150
+ anchor.href.startsWith("http")
151
+ ? anchor.href
152
+ : toDocHref(anchor.href, basePath)
153
+ }
154
+ >
155
+ {anchor.label}
156
+ </a>
157
+ </li>
158
+ ))}
159
+ </ul>
160
+ </Section>
161
+ ) : null}
162
+ {entries.map((entry, index) => {
163
+ if (entry.type === "page") {
164
+ return (
165
+ <Section
166
+ key={entry.path}
167
+ paddedTop={index === 0 && !anchors?.length}
168
+ >
169
+ <ul>
170
+ <li>
171
+ <NavPageLink
172
+ basePath={basePath}
173
+ isActive={isActive(entry.path)}
174
+ item={entry}
175
+ />
176
+ </li>
177
+ </ul>
178
+ </Section>
179
+ );
180
+ }
181
+
182
+ return (
183
+ <Section
184
+ key={entry.title}
185
+ paddedTop={index === 0 && !anchors?.length}
186
+ title={entry.title}
187
+ >
188
+ <ul className="space-y-0.5">
189
+ {entry.items.map((item) => (
190
+ <li key={item.path}>
191
+ <NavPageLink
192
+ basePath={basePath}
193
+ isActive={isActive(item.path)}
194
+ item={item}
195
+ />
196
+ </li>
197
+ ))}
198
+ </ul>
199
+ </Section>
200
+ );
201
+ })}
202
+ <div className="sticky -bottom-1 z-10 h-16 shrink-0 bg-gradient-to-t from-background via-background/80 to-background/50 blur-xs" />
203
+ </div>
204
+ </aside>
205
+ );
206
+ };