blodemd 0.0.12 → 0.0.14

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 (138) hide show
  1. package/README.md +1 -1
  2. package/dev-server/lib/local-runtime.tsx +12 -15
  3. package/dev-server/next.config.js +19 -9
  4. package/dev-server/package.json +5 -1
  5. package/dev-server/tsconfig.json +0 -3
  6. package/dist/cli.mjs +1205 -969
  7. package/dist/cli.mjs.map +1 -1
  8. package/docs/app/globals.css +1 -1
  9. package/docs/components/api/api-playground.tsx +2 -5
  10. package/docs/components/api/api-reference.tsx +29 -3
  11. package/docs/components/docs/contextual-menu.tsx +37 -33
  12. package/docs/components/docs/copy-page-menu.tsx +18 -0
  13. package/docs/components/docs/doc-header.tsx +4 -1
  14. package/docs/components/docs/doc-shell.tsx +48 -18
  15. package/docs/components/docs/doc-sidebar.tsx +131 -89
  16. package/docs/components/docs/doc-toc.tsx +1 -0
  17. package/docs/components/docs/mobile-nav.tsx +72 -68
  18. package/docs/components/docs/sidebar-active-highlight.tsx +34 -0
  19. package/docs/components/docs/sidebar-scroll-area.tsx +9 -4
  20. package/docs/components/git/repo-picker.tsx +5 -2
  21. package/docs/components/mdx/index.tsx +31 -21
  22. package/docs/components/posthog-provider.tsx +42 -0
  23. package/docs/components/tenant-analytics.tsx +49 -0
  24. package/docs/components/third-parties.tsx +4 -1
  25. package/docs/components/ui/copy-button.tsx +16 -2
  26. package/docs/components/ui/search-dialog.tsx +431 -0
  27. package/docs/components/ui/search.tsx +44 -427
  28. package/docs/components/web-mcp.tsx +115 -0
  29. package/docs/lib/agent-skills.ts +48 -0
  30. package/docs/lib/config.ts +4 -5
  31. package/docs/lib/docs-collection.ts +7 -22
  32. package/docs/lib/docs-runtime.tsx +160 -10
  33. package/docs/lib/llms-full.ts +82 -0
  34. package/docs/lib/marketing-markdown.ts +79 -0
  35. package/docs/lib/marketing-site.ts +31 -0
  36. package/docs/lib/mdx.ts +17 -6
  37. package/docs/lib/routes.ts +19 -3
  38. package/docs/lib/shiki.ts +4 -0
  39. package/docs/lib/tenancy.ts +15 -2
  40. package/docs/lib/tenant-headers.ts +29 -0
  41. package/docs/lib/tenant-static.ts +73 -99
  42. package/package.json +10 -5
  43. package/packages/@repo/common/package.json +3 -3
  44. package/packages/@repo/contracts/dist/analytics.d.ts +21 -0
  45. package/packages/@repo/contracts/dist/analytics.d.ts.map +1 -0
  46. package/packages/@repo/contracts/dist/analytics.js +18 -0
  47. package/packages/@repo/contracts/dist/deployment.js +1 -1
  48. package/packages/@repo/contracts/dist/git.js +2 -2
  49. package/packages/@repo/contracts/dist/ids.d.ts.map +1 -1
  50. package/packages/@repo/contracts/dist/ids.js +2 -1
  51. package/packages/@repo/contracts/dist/index.d.ts +1 -0
  52. package/packages/@repo/contracts/dist/index.d.ts.map +1 -1
  53. package/packages/@repo/contracts/dist/index.js +1 -0
  54. package/packages/@repo/contracts/dist/project.d.ts +18 -1
  55. package/packages/@repo/contracts/dist/project.d.ts.map +1 -1
  56. package/packages/@repo/contracts/dist/project.js +3 -1
  57. package/packages/@repo/contracts/dist/tenant.d.ts +36 -0
  58. package/packages/@repo/contracts/dist/tenant.d.ts.map +1 -1
  59. package/packages/@repo/contracts/dist/tenant.js +2 -0
  60. package/packages/@repo/contracts/package.json +3 -3
  61. package/packages/@repo/contracts/src/analytics.ts +33 -0
  62. package/packages/@repo/contracts/src/deployment.ts +1 -1
  63. package/packages/@repo/contracts/src/git.ts +2 -2
  64. package/packages/@repo/contracts/src/ids.ts +2 -1
  65. package/packages/@repo/contracts/src/index.ts +1 -0
  66. package/packages/@repo/contracts/src/project.ts +3 -1
  67. package/packages/@repo/contracts/src/tenant.ts +2 -0
  68. package/packages/@repo/models/dist/docs-config.d.ts +0 -198
  69. package/packages/@repo/models/dist/docs-config.d.ts.map +1 -1
  70. package/packages/@repo/models/dist/docs-config.js +3 -2
  71. package/packages/@repo/models/dist/tenant.d.ts +12 -0
  72. package/packages/@repo/models/dist/tenant.d.ts.map +1 -1
  73. package/packages/@repo/models/package.json +3 -3
  74. package/packages/@repo/models/src/docs-config.ts +3 -3
  75. package/packages/@repo/models/src/tenant.ts +15 -0
  76. package/packages/@repo/prebuild/dist/openapi.d.ts.map +1 -1
  77. package/packages/@repo/prebuild/dist/openapi.js +32 -4
  78. package/packages/@repo/prebuild/package.json +3 -3
  79. package/packages/@repo/prebuild/src/openapi.ts +42 -4
  80. package/packages/@repo/previewing/dist/blob-source.d.ts.map +1 -1
  81. package/packages/@repo/previewing/dist/blob-source.js +18 -5
  82. package/packages/@repo/previewing/dist/config-loader.d.ts +134 -0
  83. package/packages/@repo/previewing/dist/config-loader.d.ts.map +1 -0
  84. package/packages/@repo/previewing/dist/config-loader.js +231 -0
  85. package/packages/@repo/previewing/dist/constants.d.ts +15 -0
  86. package/packages/@repo/previewing/dist/constants.d.ts.map +1 -0
  87. package/packages/@repo/previewing/dist/constants.js +14 -0
  88. package/packages/@repo/previewing/dist/content-index.d.ts +7 -0
  89. package/packages/@repo/previewing/dist/content-index.d.ts.map +1 -0
  90. package/packages/@repo/previewing/dist/content-index.js +291 -0
  91. package/packages/@repo/previewing/dist/index.d.ts +13 -121
  92. package/packages/@repo/previewing/dist/index.d.ts.map +1 -1
  93. package/packages/@repo/previewing/dist/index.js +11 -1003
  94. package/packages/@repo/previewing/dist/markdown/agent-markdown.d.ts +3 -0
  95. package/packages/@repo/previewing/dist/markdown/agent-markdown.d.ts.map +1 -0
  96. package/packages/@repo/previewing/dist/markdown/agent-markdown.js +176 -0
  97. package/packages/@repo/previewing/dist/markdown/format.d.ts +8 -0
  98. package/packages/@repo/previewing/dist/markdown/format.d.ts.map +1 -0
  99. package/packages/@repo/previewing/dist/markdown/format.js +43 -0
  100. package/packages/@repo/previewing/dist/markdown/links.d.ts +3 -0
  101. package/packages/@repo/previewing/dist/markdown/links.d.ts.map +1 -0
  102. package/packages/@repo/previewing/dist/markdown/links.js +31 -0
  103. package/packages/@repo/previewing/dist/openapi-pages.d.ts +7 -0
  104. package/packages/@repo/previewing/dist/openapi-pages.d.ts.map +1 -0
  105. package/packages/@repo/previewing/dist/openapi-pages.js +200 -0
  106. package/packages/@repo/previewing/dist/search-index.d.ts +4 -0
  107. package/packages/@repo/previewing/dist/search-index.d.ts.map +1 -0
  108. package/packages/@repo/previewing/dist/search-index.js +23 -0
  109. package/packages/@repo/previewing/dist/serialization.d.ts +13 -0
  110. package/packages/@repo/previewing/dist/serialization.d.ts.map +1 -0
  111. package/packages/@repo/previewing/dist/serialization.js +108 -0
  112. package/packages/@repo/previewing/dist/toc-index.d.ts +5 -0
  113. package/packages/@repo/previewing/dist/toc-index.d.ts.map +1 -0
  114. package/packages/@repo/previewing/dist/toc-index.js +53 -0
  115. package/packages/@repo/previewing/dist/types.d.ts +92 -0
  116. package/packages/@repo/previewing/dist/types.d.ts.map +1 -0
  117. package/packages/@repo/previewing/dist/types.js +1 -0
  118. package/packages/@repo/previewing/dist/utility-index.d.ts +7 -0
  119. package/packages/@repo/previewing/dist/utility-index.d.ts.map +1 -0
  120. package/packages/@repo/previewing/dist/utility-index.js +201 -0
  121. package/packages/@repo/previewing/package.json +3 -3
  122. package/packages/@repo/previewing/src/blob-source.ts +20 -5
  123. package/packages/@repo/previewing/src/config-loader.ts +316 -0
  124. package/packages/@repo/previewing/src/constants.ts +16 -0
  125. package/packages/@repo/previewing/src/content-index.ts +384 -0
  126. package/packages/@repo/previewing/src/index.ts +69 -1515
  127. package/packages/@repo/previewing/src/markdown/agent-markdown.ts +254 -0
  128. package/packages/@repo/previewing/src/markdown/format.ts +63 -0
  129. package/packages/@repo/previewing/src/markdown/links.ts +46 -0
  130. package/packages/@repo/previewing/src/openapi-pages.ts +319 -0
  131. package/packages/@repo/previewing/src/search-index.ts +38 -0
  132. package/packages/@repo/previewing/src/serialization.ts +191 -0
  133. package/packages/@repo/previewing/src/toc-index.ts +72 -0
  134. package/packages/@repo/previewing/src/types.ts +105 -0
  135. package/packages/@repo/previewing/src/utility-index.ts +304 -0
  136. package/packages/@repo/validation/package.json +3 -3
  137. package/scripts/repo-packages.mjs +8 -0
  138. package/docs/components/ui/site-footer.tsx +0 -41
@@ -212,7 +212,7 @@
212
212
  }
213
213
 
214
214
  html {
215
- @apply overscroll-y-none scroll-smooth;
215
+ @apply scroll-smooth;
216
216
  }
217
217
 
218
218
  html.dark {
@@ -7,7 +7,6 @@ import { Button } from "@/components/ui/button";
7
7
  import { Field, FieldLabel } from "@/components/ui/field";
8
8
  import { Input } from "@/components/ui/input";
9
9
  import type { OpenApiEntry } from "@/lib/openapi";
10
- import { TENANT_HEADERS } from "@/lib/tenant-headers";
11
10
 
12
11
  interface OperationParameter {
13
12
  description?: string;
@@ -321,15 +320,13 @@ export const ApiPlayground = ({
321
320
  headers: requestHeaders,
322
321
  method,
323
322
  url,
323
+ ...(tenantSlug ? { tenantSlug } : {}),
324
324
  };
325
325
 
326
326
  const requestUrl = useProxy ? proxyPath : url;
327
327
  const requestMethod = useProxy ? "POST" : method;
328
328
  const requestHeadersToSend = useProxy
329
- ? {
330
- "Content-Type": "application/json",
331
- ...(tenantSlug ? { [TENANT_HEADERS.SLUG]: tenantSlug } : {}),
332
- }
329
+ ? { "Content-Type": "application/json" }
333
330
  : requestHeaders;
334
331
 
335
332
  let requestBody: string | undefined;
@@ -92,9 +92,35 @@ export const ApiReference = ({
92
92
  {requestBody?.content ? (
93
93
  <section className="mt-7 grid gap-3">
94
94
  <h2>Request Body</h2>
95
- <pre className="overflow-x-auto rounded-lg bg-code p-3 text-code-foreground">
96
- {JSON.stringify(requestBody.content, null, 2)}
97
- </pre>
95
+ <div className="grid gap-2">
96
+ {Object.entries(requestBody.content).map(([mediaType, value]) => {
97
+ const { schema } = value as { schema?: unknown };
98
+ const ref =
99
+ schema &&
100
+ typeof schema === "object" &&
101
+ "$ref" in schema &&
102
+ typeof (schema as { $ref?: unknown }).$ref === "string"
103
+ ? ((schema as { $ref: string }).$ref.split("/").pop() ??
104
+ "object")
105
+ : null;
106
+ const type =
107
+ schema &&
108
+ typeof schema === "object" &&
109
+ "type" in schema &&
110
+ typeof (schema as { type?: unknown }).type === "string"
111
+ ? (schema as { type: string }).type
112
+ : "object";
113
+ return (
114
+ <div
115
+ className="flex justify-between rounded-lg border border-border bg-background/60 p-2.5"
116
+ key={mediaType}
117
+ >
118
+ <span className="font-mono">{mediaType}</span>
119
+ <span className="text-muted-foreground">{ref ?? type}</span>
120
+ </div>
121
+ );
122
+ })}
123
+ </div>
98
124
  </section>
99
125
  ) : null}
100
126
 
@@ -414,39 +414,43 @@ export const ContextualMenu = ({
414
414
  </div>
415
415
 
416
416
  <PopoverContent align="end" className="w-[320px] rounded-xl p-1">
417
- {resolved.slice(1).map((item) => {
418
- const itemFeedback =
419
- feedback?.id === item.key ? feedback.state : null;
420
- const itemLabel = getFeedbackLabel(itemFeedback, item.title);
421
- const itemDescription = getFeedbackDescription(
422
- itemFeedback,
423
- item.description
424
- );
425
- const ItemIcon = getFeedbackIcon(itemFeedback, item.icon);
426
-
427
- return (
428
- <MenuItem
429
- href={item.type === "link" ? item.href : undefined}
430
- key={item.key}
431
- onSelect={
432
- item.type === "action" ? actionHandlers[item.key] : closeMenu
433
- }
434
- >
435
- <MenuIcon>
436
- <ItemIcon aria-hidden="true" className="size-[18px]" />
437
- </MenuIcon>
438
- <div className="flex-1">
439
- <div className="font-medium">
440
- {itemLabel}
441
- {item.type === "link" ? <ExternalArrow /> : null}
442
- </div>
443
- <div className="text-xs text-muted-foreground">
444
- {itemDescription}
445
- </div>
446
- </div>
447
- </MenuItem>
448
- );
449
- })}
417
+ {menuOpen
418
+ ? resolved.slice(1).map((item) => {
419
+ const itemFeedback =
420
+ feedback?.id === item.key ? feedback.state : null;
421
+ const itemLabel = getFeedbackLabel(itemFeedback, item.title);
422
+ const itemDescription = getFeedbackDescription(
423
+ itemFeedback,
424
+ item.description
425
+ );
426
+ const ItemIcon = getFeedbackIcon(itemFeedback, item.icon);
427
+
428
+ return (
429
+ <MenuItem
430
+ href={item.type === "link" ? item.href : undefined}
431
+ key={item.key}
432
+ onSelect={
433
+ item.type === "action"
434
+ ? actionHandlers[item.key]
435
+ : closeMenu
436
+ }
437
+ >
438
+ <MenuIcon>
439
+ <ItemIcon aria-hidden="true" className="size-[18px]" />
440
+ </MenuIcon>
441
+ <div className="flex-1">
442
+ <div className="font-medium">
443
+ {itemLabel}
444
+ {item.type === "link" ? <ExternalArrow /> : null}
445
+ </div>
446
+ <div className="text-xs text-muted-foreground">
447
+ {itemDescription}
448
+ </div>
449
+ </div>
450
+ </MenuItem>
451
+ );
452
+ })
453
+ : null}
450
454
  </PopoverContent>
451
455
  </Popover>
452
456
  );
@@ -6,6 +6,7 @@ import {
6
6
  ChevronDownSmallIcon,
7
7
  ClaudeaiIcon,
8
8
  CopySimpleIcon,
9
+ FileTextIcon,
9
10
  OpenaiIcon,
10
11
  } from "blode-icons-react";
11
12
  import type React from "react";
@@ -295,6 +296,23 @@ export const CopyPageMenu = ({
295
296
  </div>
296
297
 
297
298
  <PopoverContent align="end" className="w-[280px] rounded-xl p-1">
299
+ {contentUrl ? (
300
+ <MenuItem href={contentUrl} onSelect={closeMenu}>
301
+ <MenuIcon>
302
+ <FileTextIcon aria-hidden="true" className="size-[18px]" />
303
+ </MenuIcon>
304
+ <div className="flex-1">
305
+ <div className="font-medium">
306
+ View as markdown
307
+ <ExternalArrow />
308
+ </div>
309
+ <div className="text-xs text-muted-foreground">
310
+ Open the raw .md source in a new tab
311
+ </div>
312
+ </div>
313
+ </MenuItem>
314
+ ) : null}
315
+
298
316
  <MenuItem onSelect={handleCopy}>
299
317
  <MenuIcon>
300
318
  <CopyIcon aria-hidden="true" className="size-[18px]" />
@@ -125,7 +125,10 @@ export const DocHeader = ({
125
125
  );
126
126
 
127
127
  return (
128
- <header className="sticky top-0 z-50 w-full bg-background">
128
+ <header
129
+ className="sticky top-0 z-50 w-full bg-background"
130
+ data-markdown-ignore=""
131
+ >
129
132
  <div className="container-wrapper px-4 lg:px-8">
130
133
  <div className="flex h-(--header-height) items-center">
131
134
  <MobileNav
@@ -100,6 +100,7 @@ export const DocShell = ({
100
100
  markdownHref,
101
101
  rawContent,
102
102
  tabs,
103
+ tenantSlug,
103
104
  mode,
104
105
  deprecated,
105
106
  hideFooterPagination,
@@ -120,6 +121,7 @@ export const DocShell = ({
120
121
  markdownHref?: string;
121
122
  rawContent?: string;
122
123
  tabs?: NavTab[] | null;
124
+ tenantSlug?: string;
123
125
  mode?: PageMode;
124
126
  deprecated?: boolean;
125
127
  hideFooterPagination?: boolean;
@@ -183,7 +185,26 @@ export const DocShell = ({
183
185
  )}
184
186
  >
185
187
  <div className="flex flex-col gap-2">
186
- <Breadcrumbs basePath={basePath} breadcrumbs={breadcrumbs} />
188
+ <p
189
+ className="sr-only"
190
+ data-agent-directive=""
191
+ data-markdown-ignore=""
192
+ >
193
+ AI agents: fetch the documentation index at{" "}
194
+ <Link href={toDocHref("llms.txt", basePath)}>llms.txt</Link>.
195
+ Markdown versions are available by appending .md to any page
196
+ URL
197
+ {markdownHref ? (
198
+ <>
199
+ , including{" "}
200
+ <Link href={markdownHref}>this page&apos;s markdown</Link>
201
+ </>
202
+ ) : null}
203
+ .
204
+ </p>
205
+ <div data-markdown-ignore="">
206
+ <Breadcrumbs basePath={basePath} breadcrumbs={breadcrumbs} />
207
+ </div>
187
208
  <div className="flex flex-col gap-2">
188
209
  <div className="flex flex-col items-start gap-3 sm:flex-row sm:justify-between">
189
210
  <h1 className="min-w-0 scroll-m-24 text-3xl font-semibold tracking-tight sm:text-3xl">
@@ -194,16 +215,18 @@ export const DocShell = ({
194
215
  </span>
195
216
  ) : null}
196
217
  </h1>
197
- {headerContextualMenu ??
198
- (rawContent === undefined &&
199
- markdownHref === undefined ? null : (
200
- <CopyPageMenu
201
- content={markdownHref ? undefined : rawContent}
202
- contentUrl={markdownHref}
203
- key={`copy-${currentPath}`}
204
- title={pageTitle}
205
- />
206
- ))}
218
+ <div data-markdown-ignore="">
219
+ {headerContextualMenu ??
220
+ (rawContent === undefined &&
221
+ markdownHref === undefined ? null : (
222
+ <CopyPageMenu
223
+ content={markdownHref ? undefined : rawContent}
224
+ contentUrl={markdownHref}
225
+ key={`copy-${currentPath}`}
226
+ title={pageTitle}
227
+ />
228
+ ))}
229
+ </div>
207
230
  </div>
208
231
  {pageDescription ? (
209
232
  <p className="text-[1.05rem] text-muted-foreground sm:text-balance sm:text-base md:max-w-[80%]">
@@ -218,6 +241,7 @@ export const DocShell = ({
218
241
  {!hideFooterPagination && (prevPage || nextPage) ? (
219
242
  <nav
220
243
  className="flex w-full rounded-2xl bg-muted/50 p-1 text-sm"
244
+ data-markdown-ignore=""
221
245
  id="pagination"
222
246
  >
223
247
  {prevPage ? (
@@ -283,6 +307,7 @@ export const DocShell = ({
283
307
  >
284
308
  <a
285
309
  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"
310
+ data-markdown-ignore=""
286
311
  href="#main-content"
287
312
  >
288
313
  Skip to content
@@ -305,13 +330,18 @@ export const DocShell = ({
305
330
  } as React.CSSProperties
306
331
  }
307
332
  >
308
- <DocSidebar
309
- anchors={anchors}
310
- basePath={basePath}
311
- currentPath={currentPath}
312
- entries={nav}
313
- />
314
- {innerContent}
333
+ <div className="min-w-0 lg:col-start-2 lg:row-start-1">
334
+ {innerContent}
335
+ </div>
336
+ <div className="self-stretch lg:col-start-1 lg:row-start-1">
337
+ <DocSidebar
338
+ activeTabIndex={activeTabIndex ?? 0}
339
+ anchors={anchors}
340
+ basePath={basePath}
341
+ nav={nav}
342
+ tenantSlug={tenantSlug}
343
+ />
344
+ </div>
315
345
  </div>
316
346
  ) : (
317
347
  <div className="min-h-min flex-1 items-start px-0 [--top-spacing:0] lg:[--top-spacing:calc(var(--spacing)*4)]">
@@ -1,9 +1,10 @@
1
- import { normalizePath } from "@repo/common";
2
1
  import { ArrowUpRightIcon } from "blode-icons-react";
3
2
  import Image from "next/image";
4
3
  import Link from "next/link";
5
4
 
5
+ import { SidebarActiveHighlight } from "@/components/docs/sidebar-active-highlight";
6
6
  import { DocIcon } from "@/components/icons/doc-icon";
7
+ import { getTenantSidebarData } from "@/lib/docs-runtime";
7
8
  import { getNavPageHref, getNavPageTitle } from "@/lib/navigation";
8
9
  import type { NavEntry, NavPage } from "@/lib/navigation";
9
10
  import { isExternalHref, toDocHref } from "@/lib/routes";
@@ -39,11 +40,9 @@ const NavIcon = ({ icon }: { icon: string }) => {
39
40
  const NavPageLink = ({
40
41
  item,
41
42
  basePath,
42
- isActive,
43
43
  }: {
44
44
  item: NavPage;
45
45
  basePath: string;
46
- isActive: boolean;
47
46
  }) => {
48
47
  const displayTitle = getNavPageTitle(item);
49
48
  const href = getNavPageHref(item, basePath);
@@ -74,16 +73,11 @@ const NavPageLink = ({
74
73
  </>
75
74
  );
76
75
 
77
- const className = cn(
78
- MENU_BUTTON_CLASS,
79
- isActive && "border-accent bg-accent text-foreground"
80
- );
81
-
82
76
  if (isExternal) {
83
77
  return (
84
78
  <a
85
- className={className}
86
- data-active={isActive || undefined}
79
+ className={MENU_BUTTON_CLASS}
80
+ data-sidebar-link=""
87
81
  href={href}
88
82
  rel="noopener noreferrer"
89
83
  target="_blank"
@@ -94,7 +88,12 @@ const NavPageLink = ({
94
88
  }
95
89
 
96
90
  return (
97
- <Link className={className} data-active={isActive || undefined} href={href}>
91
+ <Link
92
+ className={MENU_BUTTON_CLASS}
93
+ data-path={item.path}
94
+ data-sidebar-link=""
95
+ href={href}
96
+ >
98
97
  {linkContent}
99
98
  </Link>
100
99
  );
@@ -124,91 +123,134 @@ const Section = ({
124
123
  </section>
125
124
  );
126
125
 
127
- export const DocSidebar = ({
128
- entries,
129
- currentPath,
126
+ const renderSidebarTree = ({
130
127
  anchors,
131
128
  basePath,
129
+ entries,
132
130
  }: {
131
+ anchors: { label: string; href: string }[];
132
+ basePath: string;
133
133
  entries: NavEntry[];
134
- currentPath: string;
134
+ }) => (
135
+ <>
136
+ {anchors.length ? (
137
+ <Section paddedTop title="Pinned">
138
+ <ul className="space-y-1">
139
+ {anchors.map((anchor) => (
140
+ <li key={anchor.href}>
141
+ <a
142
+ className={cn(MENU_BUTTON_CLASS, "text-foreground")}
143
+ href={
144
+ anchor.href.startsWith("http")
145
+ ? anchor.href
146
+ : toDocHref(anchor.href, basePath)
147
+ }
148
+ >
149
+ {anchor.label}
150
+ </a>
151
+ </li>
152
+ ))}
153
+ </ul>
154
+ </Section>
155
+ ) : null}
156
+ {entries.map((entry, index) => {
157
+ if (entry.type === "page") {
158
+ return (
159
+ <Section key={entry.path} paddedTop={index === 0 && !anchors.length}>
160
+ <ul>
161
+ <li>
162
+ <NavPageLink basePath={basePath} item={entry} />
163
+ </li>
164
+ </ul>
165
+ </Section>
166
+ );
167
+ }
168
+
169
+ return (
170
+ <Section
171
+ key={entry.title}
172
+ paddedTop={index === 0 && !anchors.length}
173
+ title={entry.title}
174
+ >
175
+ <ul className="space-y-0.5">
176
+ {entry.items.map((item) => (
177
+ <li key={item.path}>
178
+ <NavPageLink basePath={basePath} item={item} />
179
+ </li>
180
+ ))}
181
+ </ul>
182
+ </Section>
183
+ );
184
+ })}
185
+ </>
186
+ );
187
+
188
+ const CachedSidebarTree = async ({
189
+ activeTabIndex,
190
+ anchors,
191
+ basePath,
192
+ entries,
193
+ tenantSlug,
194
+ }: {
195
+ activeTabIndex: number;
135
196
  anchors?: { label: string; href: string }[];
136
197
  basePath: string;
198
+ entries?: NavEntry[];
199
+ tenantSlug?: string;
137
200
  }) => {
138
- const activePath = normalizePath(currentPath);
139
- const isActive = (path: string) => normalizePath(path) === activePath;
201
+ if (tenantSlug) {
202
+ const data = await getTenantSidebarData(tenantSlug, activeTabIndex);
203
+ if (data) {
204
+ return renderSidebarTree({
205
+ anchors: data.anchors,
206
+ basePath,
207
+ entries: data.entries,
208
+ });
209
+ }
210
+ }
140
211
 
141
- return (
142
- <aside
143
- 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"
144
- aria-label="Documentation navigation"
145
- >
146
- <div className="h-9" />
147
- <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" />
148
- <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" />
149
- <SidebarScrollArea 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">
150
- {anchors?.length ? (
151
- <Section paddedTop title="Pinned">
152
- <ul className="space-y-1">
153
- {anchors.map((anchor) => (
154
- <li key={anchor.href}>
155
- <a
156
- className={cn(MENU_BUTTON_CLASS, "text-foreground")}
157
- href={
158
- anchor.href.startsWith("http")
159
- ? anchor.href
160
- : toDocHref(anchor.href, basePath)
161
- }
162
- >
163
- {anchor.label}
164
- </a>
165
- </li>
166
- ))}
167
- </ul>
168
- </Section>
169
- ) : null}
170
- {entries.map((entry, index) => {
171
- if (entry.type === "page") {
172
- return (
173
- <Section
174
- key={entry.path}
175
- paddedTop={index === 0 && !anchors?.length}
176
- >
177
- <ul>
178
- <li>
179
- <NavPageLink
180
- basePath={basePath}
181
- isActive={isActive(entry.path)}
182
- item={entry}
183
- />
184
- </li>
185
- </ul>
186
- </Section>
187
- );
188
- }
212
+ if (!entries || entries.length === 0) {
213
+ return null;
214
+ }
189
215
 
190
- return (
191
- <Section
192
- key={entry.title}
193
- paddedTop={index === 0 && !anchors?.length}
194
- title={entry.title}
195
- >
196
- <ul className="space-y-0.5">
197
- {entry.items.map((item) => (
198
- <li key={item.path}>
199
- <NavPageLink
200
- basePath={basePath}
201
- isActive={isActive(item.path)}
202
- item={item}
203
- />
204
- </li>
205
- ))}
206
- </ul>
207
- </Section>
208
- );
209
- })}
210
- <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" />
211
- </SidebarScrollArea>
212
- </aside>
213
- );
216
+ return renderSidebarTree({
217
+ anchors: anchors ?? [],
218
+ basePath,
219
+ entries,
220
+ });
214
221
  };
222
+
223
+ export const DocSidebar = ({
224
+ activeTabIndex,
225
+ anchors,
226
+ basePath,
227
+ nav,
228
+ tenantSlug,
229
+ }: {
230
+ activeTabIndex: number;
231
+ anchors?: { label: string; href: string }[];
232
+ basePath: string;
233
+ nav?: NavEntry[];
234
+ tenantSlug?: string;
235
+ }) => (
236
+ <aside
237
+ aria-label="Documentation navigation"
238
+ 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"
239
+ data-markdown-ignore=""
240
+ >
241
+ <div className="h-9" />
242
+ <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" />
243
+ <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" />
244
+ <SidebarScrollArea 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">
245
+ <CachedSidebarTree
246
+ activeTabIndex={activeTabIndex}
247
+ anchors={anchors}
248
+ basePath={basePath}
249
+ entries={nav}
250
+ tenantSlug={tenantSlug}
251
+ />
252
+ <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" />
253
+ </SidebarScrollArea>
254
+ <SidebarActiveHighlight basePath={basePath} />
255
+ </aside>
256
+ );
@@ -17,6 +17,7 @@ export const DocToc = ({
17
17
  <nav
18
18
  aria-label="Table of contents"
19
19
  className="sticky top-[calc(var(--header-height)+1px)] z-30 ml-auto hidden h-[90svh] w-(--sidebar-width) flex-col gap-4 overflow-hidden overscroll-none pb-8 xl:flex"
20
+ data-markdown-ignore=""
20
21
  >
21
22
  <div className="no-scrollbar flex flex-col gap-8 overflow-y-auto px-8">
22
23
  <div className="flex flex-col gap-2 p-4 pt-0 text-sm">