blodemd 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/dist/cli.mjs +50 -174
  2. package/dist/cli.mjs.map +1 -1
  3. package/package.json +3 -36
  4. package/dev-server/app/[[...slug]]/page.tsx +0 -140
  5. package/dev-server/app/blodemd-dev/invalidate/route.ts +0 -12
  6. package/dev-server/app/blodemd-dev/static/[...path]/route.ts +0 -32
  7. package/dev-server/app/blodemd-dev/version/route.ts +0 -14
  8. package/dev-server/app/blodemd-internal/proxy/route.ts +0 -86
  9. package/dev-server/app/error.tsx +0 -24
  10. package/dev-server/app/favicon.ico +0 -0
  11. package/dev-server/app/globals.css +0 -4
  12. package/dev-server/app/layout.tsx +0 -38
  13. package/dev-server/app/not-found.tsx +0 -18
  14. package/dev-server/app/search/route.ts +0 -17
  15. package/dev-server/components/dev-reload-script.tsx +0 -86
  16. package/dev-server/components/providers.tsx +0 -15
  17. package/dev-server/lib/dev-state.ts +0 -8
  18. package/dev-server/lib/local-content-source.ts +0 -103
  19. package/dev-server/lib/local-runtime.tsx +0 -555
  20. package/dev-server/next-env.d.ts +0 -5
  21. package/dev-server/next.config.js +0 -54
  22. package/dev-server/package.json +0 -61
  23. package/dev-server/postcss.config.mjs +0 -7
  24. package/dev-server/public/glide-variable.woff2 +0 -0
  25. package/dev-server/tsconfig.json +0 -50
  26. package/docs/app/globals.css +0 -471
  27. package/docs/components/animate-ui/primitives/buttons/button.tsx +0 -14
  28. package/docs/components/api/api-playground.tsx +0 -467
  29. package/docs/components/api/api-reference.tsx +0 -157
  30. package/docs/components/content/collection-index.tsx +0 -114
  31. package/docs/components/docs/contextual-menu.tsx +0 -495
  32. package/docs/components/docs/copy-page-menu.tsx +0 -364
  33. package/docs/components/docs/doc-header.tsx +0 -223
  34. package/docs/components/docs/doc-shell.tsx +0 -355
  35. package/docs/components/docs/doc-sidebar.tsx +0 -256
  36. package/docs/components/docs/doc-toc.tsx +0 -46
  37. package/docs/components/docs/mobile-nav.tsx +0 -195
  38. package/docs/components/docs/sidebar-active-highlight.tsx +0 -34
  39. package/docs/components/docs/sidebar-scroll-area.tsx +0 -63
  40. package/docs/components/git/repo-picker.tsx +0 -529
  41. package/docs/components/icons/doc-icon.tsx +0 -96
  42. package/docs/components/mdx/accordion.tsx +0 -83
  43. package/docs/components/mdx/agent-instructions.tsx +0 -17
  44. package/docs/components/mdx/badge.tsx +0 -79
  45. package/docs/components/mdx/callout.tsx +0 -88
  46. package/docs/components/mdx/card.tsx +0 -110
  47. package/docs/components/mdx/code-block.tsx +0 -80
  48. package/docs/components/mdx/code-group.tsx +0 -203
  49. package/docs/components/mdx/color.tsx +0 -87
  50. package/docs/components/mdx/columns.tsx +0 -25
  51. package/docs/components/mdx/expandable.tsx +0 -45
  52. package/docs/components/mdx/field-layout.tsx +0 -77
  53. package/docs/components/mdx/frame.tsx +0 -23
  54. package/docs/components/mdx/get-text-content.ts +0 -18
  55. package/docs/components/mdx/icon.tsx +0 -12
  56. package/docs/components/mdx/iframe.tsx +0 -62
  57. package/docs/components/mdx/index.tsx +0 -121
  58. package/docs/components/mdx/installer.tsx +0 -20
  59. package/docs/components/mdx/panel.tsx +0 -11
  60. package/docs/components/mdx/param-field.tsx +0 -56
  61. package/docs/components/mdx/preview.tsx +0 -36
  62. package/docs/components/mdx/prompt.tsx +0 -63
  63. package/docs/components/mdx/request-example.tsx +0 -27
  64. package/docs/components/mdx/response-field.tsx +0 -42
  65. package/docs/components/mdx/steps.tsx +0 -92
  66. package/docs/components/mdx/tabs.tsx +0 -193
  67. package/docs/components/mdx/tile.tsx +0 -43
  68. package/docs/components/mdx/tooltip.tsx +0 -71
  69. package/docs/components/mdx/tree.tsx +0 -120
  70. package/docs/components/mdx/type-table.tsx +0 -71
  71. package/docs/components/mdx/update.tsx +0 -44
  72. package/docs/components/mdx/video.tsx +0 -45
  73. package/docs/components/mdx/view.tsx +0 -66
  74. package/docs/components/posthog-provider.tsx +0 -42
  75. package/docs/components/providers.tsx +0 -15
  76. package/docs/components/tenant-analytics.tsx +0 -49
  77. package/docs/components/third-parties.tsx +0 -32
  78. package/docs/components/ui/badge.tsx +0 -61
  79. package/docs/components/ui/breadcrumb.tsx +0 -112
  80. package/docs/components/ui/button-group.tsx +0 -83
  81. package/docs/components/ui/button.tsx +0 -65
  82. package/docs/components/ui/card.tsx +0 -92
  83. package/docs/components/ui/command.tsx +0 -167
  84. package/docs/components/ui/copy-button.tsx +0 -129
  85. package/docs/components/ui/dialog.tsx +0 -146
  86. package/docs/components/ui/field.tsx +0 -237
  87. package/docs/components/ui/input.tsx +0 -23
  88. package/docs/components/ui/kbd.tsx +0 -98
  89. package/docs/components/ui/label.tsx +0 -22
  90. package/docs/components/ui/morph-icon.tsx +0 -79
  91. package/docs/components/ui/popover.tsx +0 -267
  92. package/docs/components/ui/search-dialog.tsx +0 -431
  93. package/docs/components/ui/search.tsx +0 -101
  94. package/docs/components/ui/separator.tsx +0 -26
  95. package/docs/components/ui/sheet.tsx +0 -133
  96. package/docs/components/ui/sidebar.tsx +0 -758
  97. package/docs/components/ui/skeleton.tsx +0 -11
  98. package/docs/components/ui/switch.tsx +0 -32
  99. package/docs/components/ui/tabs.tsx +0 -138
  100. package/docs/components/ui/theme-toggle.tsx +0 -62
  101. package/docs/components/ui/tooltip.tsx +0 -53
  102. package/docs/components/web-mcp.tsx +0 -115
  103. package/docs/lib/agent-skills.ts +0 -48
  104. package/docs/lib/api-client.ts +0 -72
  105. package/docs/lib/config.ts +0 -6
  106. package/docs/lib/content-root.ts +0 -33
  107. package/docs/lib/content-source.ts +0 -70
  108. package/docs/lib/contextual-options.ts +0 -222
  109. package/docs/lib/dashboard-session.ts +0 -167
  110. package/docs/lib/db.ts +0 -13
  111. package/docs/lib/docs-collection.ts +0 -7
  112. package/docs/lib/docs-runtime.tsx +0 -745
  113. package/docs/lib/edge-config.ts +0 -95
  114. package/docs/lib/env.ts +0 -23
  115. package/docs/lib/etag.ts +0 -22
  116. package/docs/lib/github-install.ts +0 -33
  117. package/docs/lib/llms-full.ts +0 -82
  118. package/docs/lib/marketing-markdown.ts +0 -79
  119. package/docs/lib/marketing-site.ts +0 -31
  120. package/docs/lib/mdx.ts +0 -98
  121. package/docs/lib/navigation.ts +0 -288
  122. package/docs/lib/openapi-proxy.ts +0 -88
  123. package/docs/lib/openapi.ts +0 -158
  124. package/docs/lib/platform-config.ts +0 -6
  125. package/docs/lib/project-authz.ts +0 -46
  126. package/docs/lib/routes.ts +0 -103
  127. package/docs/lib/server-cache.ts +0 -83
  128. package/docs/lib/shiki.ts +0 -44
  129. package/docs/lib/supabase.ts +0 -37
  130. package/docs/lib/tenancy.ts +0 -364
  131. package/docs/lib/tenant-headers.ts +0 -43
  132. package/docs/lib/tenant-static.ts +0 -705
  133. package/docs/lib/tenant-utility-context.ts +0 -62
  134. package/docs/lib/tenants.ts +0 -72
  135. package/docs/lib/theme.ts +0 -29
  136. package/docs/lib/time-ago.ts +0 -24
  137. package/docs/lib/toc.ts +0 -2
  138. package/docs/lib/use-mobile.ts +0 -19
  139. package/docs/lib/use-tab-observer.ts +0 -71
  140. package/docs/lib/utils.ts +0 -5
  141. package/packages/@repo/common/dist/index.d.ts +0 -16
  142. package/packages/@repo/common/dist/index.d.ts.map +0 -1
  143. package/packages/@repo/common/dist/index.js +0 -84
  144. package/packages/@repo/common/package.json +0 -34
  145. package/packages/@repo/common/src/index.ts +0 -101
  146. package/packages/@repo/contracts/dist/analytics.d.ts +0 -21
  147. package/packages/@repo/contracts/dist/analytics.d.ts.map +0 -1
  148. package/packages/@repo/contracts/dist/analytics.js +0 -18
  149. package/packages/@repo/contracts/dist/api-key.d.ts +0 -27
  150. package/packages/@repo/contracts/dist/api-key.d.ts.map +0 -1
  151. package/packages/@repo/contracts/dist/api-key.js +0 -20
  152. package/packages/@repo/contracts/dist/dates.d.ts +0 -4
  153. package/packages/@repo/contracts/dist/dates.d.ts.map +0 -1
  154. package/packages/@repo/contracts/dist/dates.js +0 -2
  155. package/packages/@repo/contracts/dist/deployment.d.ts +0 -71
  156. package/packages/@repo/contracts/dist/deployment.d.ts.map +0 -1
  157. package/packages/@repo/contracts/dist/deployment.js +0 -46
  158. package/packages/@repo/contracts/dist/domain.d.ts +0 -94
  159. package/packages/@repo/contracts/dist/domain.d.ts.map +0 -1
  160. package/packages/@repo/contracts/dist/domain.js +0 -36
  161. package/packages/@repo/contracts/dist/git.d.ts +0 -28
  162. package/packages/@repo/contracts/dist/git.d.ts.map +0 -1
  163. package/packages/@repo/contracts/dist/git.js +0 -24
  164. package/packages/@repo/contracts/dist/ids.d.ts +0 -14
  165. package/packages/@repo/contracts/dist/ids.d.ts.map +0 -1
  166. package/packages/@repo/contracts/dist/ids.js +0 -11
  167. package/packages/@repo/contracts/dist/index.d.ts +0 -12
  168. package/packages/@repo/contracts/dist/index.d.ts.map +0 -1
  169. package/packages/@repo/contracts/dist/index.js +0 -13
  170. package/packages/@repo/contracts/dist/pagination.d.ts +0 -23
  171. package/packages/@repo/contracts/dist/pagination.d.ts.map +0 -1
  172. package/packages/@repo/contracts/dist/pagination.js +0 -15
  173. package/packages/@repo/contracts/dist/project.d.ts +0 -42
  174. package/packages/@repo/contracts/dist/project.d.ts.map +0 -1
  175. package/packages/@repo/contracts/dist/project.js +0 -25
  176. package/packages/@repo/contracts/dist/tenant.d.ts +0 -147
  177. package/packages/@repo/contracts/dist/tenant.d.ts.map +0 -1
  178. package/packages/@repo/contracts/dist/tenant.js +0 -58
  179. package/packages/@repo/contracts/dist/user.d.ts +0 -9
  180. package/packages/@repo/contracts/dist/user.d.ts.map +0 -1
  181. package/packages/@repo/contracts/dist/user.js +0 -9
  182. package/packages/@repo/contracts/package.json +0 -37
  183. package/packages/@repo/contracts/src/analytics.ts +0 -33
  184. package/packages/@repo/contracts/src/api-key.ts +0 -27
  185. package/packages/@repo/contracts/src/dates.ts +0 -4
  186. package/packages/@repo/contracts/src/deployment.ts +0 -73
  187. package/packages/@repo/contracts/src/domain.ts +0 -51
  188. package/packages/@repo/contracts/src/git.ts +0 -31
  189. package/packages/@repo/contracts/src/ids.ts +0 -23
  190. package/packages/@repo/contracts/src/index.ts +0 -13
  191. package/packages/@repo/contracts/src/pagination.ts +0 -21
  192. package/packages/@repo/contracts/src/project.ts +0 -32
  193. package/packages/@repo/contracts/src/tenant.ts +0 -94
  194. package/packages/@repo/contracts/src/user.ts +0 -12
  195. package/packages/@repo/models/dist/docs-config.d.ts +0 -961
  196. package/packages/@repo/models/dist/docs-config.d.ts.map +0 -1
  197. package/packages/@repo/models/dist/docs-config.js +0 -531
  198. package/packages/@repo/models/dist/index.d.ts +0 -3
  199. package/packages/@repo/models/dist/index.d.ts.map +0 -1
  200. package/packages/@repo/models/dist/index.js +0 -3
  201. package/packages/@repo/models/dist/tenant.d.ts +0 -37
  202. package/packages/@repo/models/dist/tenant.d.ts.map +0 -1
  203. package/packages/@repo/models/dist/tenant.js +0 -1
  204. package/packages/@repo/models/package.json +0 -37
  205. package/packages/@repo/models/src/docs-config.ts +0 -629
  206. package/packages/@repo/models/src/index.ts +0 -3
  207. package/packages/@repo/models/src/tenant.ts +0 -44
  208. package/packages/@repo/prebuild/dist/index.d.ts +0 -2
  209. package/packages/@repo/prebuild/dist/index.d.ts.map +0 -1
  210. package/packages/@repo/prebuild/dist/index.js +0 -2
  211. package/packages/@repo/prebuild/dist/openapi.d.ts +0 -43
  212. package/packages/@repo/prebuild/dist/openapi.d.ts.map +0 -1
  213. package/packages/@repo/prebuild/dist/openapi.js +0 -86
  214. package/packages/@repo/prebuild/package.json +0 -39
  215. package/packages/@repo/prebuild/src/index.ts +0 -2
  216. package/packages/@repo/prebuild/src/openapi.ts +0 -154
  217. package/packages/@repo/previewing/dist/blob-source.d.ts +0 -16
  218. package/packages/@repo/previewing/dist/blob-source.d.ts.map +0 -1
  219. package/packages/@repo/previewing/dist/blob-source.js +0 -128
  220. package/packages/@repo/previewing/dist/config-loader.d.ts +0 -134
  221. package/packages/@repo/previewing/dist/config-loader.d.ts.map +0 -1
  222. package/packages/@repo/previewing/dist/config-loader.js +0 -231
  223. package/packages/@repo/previewing/dist/constants.d.ts +0 -15
  224. package/packages/@repo/previewing/dist/constants.d.ts.map +0 -1
  225. package/packages/@repo/previewing/dist/constants.js +0 -14
  226. package/packages/@repo/previewing/dist/content-index.d.ts +0 -7
  227. package/packages/@repo/previewing/dist/content-index.d.ts.map +0 -1
  228. package/packages/@repo/previewing/dist/content-index.js +0 -291
  229. package/packages/@repo/previewing/dist/content-source.d.ts +0 -12
  230. package/packages/@repo/previewing/dist/content-source.d.ts.map +0 -1
  231. package/packages/@repo/previewing/dist/content-source.js +0 -1
  232. package/packages/@repo/previewing/dist/fs-source.d.ts +0 -11
  233. package/packages/@repo/previewing/dist/fs-source.d.ts.map +0 -1
  234. package/packages/@repo/previewing/dist/fs-source.js +0 -71
  235. package/packages/@repo/previewing/dist/index.d.ts +0 -16
  236. package/packages/@repo/previewing/dist/index.d.ts.map +0 -1
  237. package/packages/@repo/previewing/dist/index.js +0 -13
  238. package/packages/@repo/previewing/dist/markdown/agent-markdown.d.ts +0 -3
  239. package/packages/@repo/previewing/dist/markdown/agent-markdown.d.ts.map +0 -1
  240. package/packages/@repo/previewing/dist/markdown/agent-markdown.js +0 -176
  241. package/packages/@repo/previewing/dist/markdown/format.d.ts +0 -8
  242. package/packages/@repo/previewing/dist/markdown/format.d.ts.map +0 -1
  243. package/packages/@repo/previewing/dist/markdown/format.js +0 -43
  244. package/packages/@repo/previewing/dist/markdown/links.d.ts +0 -3
  245. package/packages/@repo/previewing/dist/markdown/links.d.ts.map +0 -1
  246. package/packages/@repo/previewing/dist/markdown/links.js +0 -31
  247. package/packages/@repo/previewing/dist/openapi-pages.d.ts +0 -7
  248. package/packages/@repo/previewing/dist/openapi-pages.d.ts.map +0 -1
  249. package/packages/@repo/previewing/dist/openapi-pages.js +0 -200
  250. package/packages/@repo/previewing/dist/search-index.d.ts +0 -4
  251. package/packages/@repo/previewing/dist/search-index.d.ts.map +0 -1
  252. package/packages/@repo/previewing/dist/search-index.js +0 -23
  253. package/packages/@repo/previewing/dist/serialization.d.ts +0 -13
  254. package/packages/@repo/previewing/dist/serialization.d.ts.map +0 -1
  255. package/packages/@repo/previewing/dist/serialization.js +0 -108
  256. package/packages/@repo/previewing/dist/toc-index.d.ts +0 -5
  257. package/packages/@repo/previewing/dist/toc-index.d.ts.map +0 -1
  258. package/packages/@repo/previewing/dist/toc-index.js +0 -53
  259. package/packages/@repo/previewing/dist/types.d.ts +0 -92
  260. package/packages/@repo/previewing/dist/types.d.ts.map +0 -1
  261. package/packages/@repo/previewing/dist/types.js +0 -1
  262. package/packages/@repo/previewing/dist/utility-index.d.ts +0 -7
  263. package/packages/@repo/previewing/dist/utility-index.d.ts.map +0 -1
  264. package/packages/@repo/previewing/dist/utility-index.js +0 -201
  265. package/packages/@repo/previewing/package.json +0 -41
  266. package/packages/@repo/previewing/src/blob-source.ts +0 -185
  267. package/packages/@repo/previewing/src/config-loader.ts +0 -316
  268. package/packages/@repo/previewing/src/constants.ts +0 -16
  269. package/packages/@repo/previewing/src/content-index.ts +0 -384
  270. package/packages/@repo/previewing/src/content-source.ts +0 -12
  271. package/packages/@repo/previewing/src/fs-source.ts +0 -103
  272. package/packages/@repo/previewing/src/index.ts +0 -71
  273. package/packages/@repo/previewing/src/markdown/agent-markdown.ts +0 -254
  274. package/packages/@repo/previewing/src/markdown/format.ts +0 -63
  275. package/packages/@repo/previewing/src/markdown/links.ts +0 -46
  276. package/packages/@repo/previewing/src/openapi-pages.ts +0 -319
  277. package/packages/@repo/previewing/src/search-index.ts +0 -38
  278. package/packages/@repo/previewing/src/serialization.ts +0 -191
  279. package/packages/@repo/previewing/src/toc-index.ts +0 -72
  280. package/packages/@repo/previewing/src/types.ts +0 -105
  281. package/packages/@repo/previewing/src/utility-index.ts +0 -304
  282. package/packages/@repo/validation/dist/index.d.ts +0 -12
  283. package/packages/@repo/validation/dist/index.d.ts.map +0 -1
  284. package/packages/@repo/validation/dist/index.js +0 -30
  285. package/packages/@repo/validation/package.json +0 -38
  286. package/packages/@repo/validation/src/blodemd-docs-schema.json +0 -3575
  287. package/packages/@repo/validation/src/index.ts +0 -59
  288. package/scripts/prepare-package.mjs +0 -53
  289. package/scripts/repo-packages.mjs +0 -8
@@ -1,364 +0,0 @@
1
- "use client";
2
-
3
- import { slugify } from "@repo/common";
4
- import {
5
- CheckIcon,
6
- ChevronDownSmallIcon,
7
- ClaudeaiIcon,
8
- CopySimpleIcon,
9
- FileTextIcon,
10
- OpenaiIcon,
11
- } from "blode-icons-react";
12
- import type React from "react";
13
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
14
-
15
- import {
16
- Popover,
17
- PopoverContent,
18
- PopoverTrigger,
19
- } from "@/components/ui/popover";
20
-
21
- interface CopyPageMenuProps {
22
- content?: string;
23
- contentUrl?: string;
24
- title: string;
25
- }
26
-
27
- type CopyStatus = "copied" | "error" | "idle";
28
-
29
- const MARKDOWN_ACCEPT = "text/markdown,text/plain;q=0.9,*/*;q=0.8";
30
-
31
- const LEADING_H1_REGEX = /^#\s+([^\r\n]+)(?:\r?\n(?:\r?\n)?)?/;
32
-
33
- const stripMatchingLeadingH1 = (source: string, title: string) => {
34
- const trimmed = source.trimStart();
35
- const match = LEADING_H1_REGEX.exec(trimmed);
36
- if (!match) {
37
- return trimmed.trim();
38
- }
39
-
40
- const [headingLine = "", headingTitle = ""] = match;
41
- if (slugify(headingTitle) !== slugify(title)) {
42
- return trimmed.trim();
43
- }
44
-
45
- return trimmed.slice(headingLine.length).trim();
46
- };
47
-
48
- const formatMarkdownForCopy = (source: string, title: string) => {
49
- const content = stripMatchingLeadingH1(source, title);
50
- if (!content) {
51
- return `# ${title}`;
52
- }
53
-
54
- return `# ${title}\n\n${content}`;
55
- };
56
-
57
- const getCopyLabel = (copyStatus: CopyStatus) => {
58
- switch (copyStatus) {
59
- case "copied": {
60
- return "Copied";
61
- }
62
- case "error": {
63
- return "Copy failed";
64
- }
65
- default: {
66
- return "Copy page";
67
- }
68
- }
69
- };
70
-
71
- const getCopyDescription = (copyStatus: CopyStatus) => {
72
- switch (copyStatus) {
73
- case "copied": {
74
- return "Copied page markdown to clipboard";
75
- }
76
- case "error": {
77
- return "Clipboard access was blocked or the page markdown could not be loaded";
78
- }
79
- default: {
80
- return "Copy page as Markdown for LLMs";
81
- }
82
- }
83
- };
84
-
85
- const getCopyIcon = (copyStatus: CopyStatus) =>
86
- copyStatus === "copied" ? CheckIcon : CopySimpleIcon;
87
-
88
- const MenuItem = ({
89
- children,
90
- href,
91
- onSelect,
92
- }: {
93
- children: React.ReactNode;
94
- href?: string;
95
- onSelect?: () => Promise<void> | void;
96
- }) =>
97
- href ? (
98
- <a
99
- className="flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm transition-colors hover:bg-secondary/25 focus-visible:bg-secondary/25 focus-visible:outline-none"
100
- href={href}
101
- onClick={onSelect}
102
- rel="noopener noreferrer"
103
- target="_blank"
104
- >
105
- {children}
106
- </a>
107
- ) : (
108
- <button
109
- className="flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left text-sm transition-colors hover:bg-secondary/25 focus-visible:bg-secondary/25 focus-visible:outline-none"
110
- onClick={onSelect}
111
- type="button"
112
- >
113
- {children}
114
- </button>
115
- );
116
-
117
- const MenuIcon = ({ children }: { children: React.ReactNode }) => (
118
- <div className="flex size-8 shrink-0 items-center justify-center rounded-md border border-border">
119
- {children}
120
- </div>
121
- );
122
-
123
- const ExternalArrow = () => (
124
- <svg
125
- aria-hidden="true"
126
- className="ml-1 inline-block size-3"
127
- fill="none"
128
- stroke="currentColor"
129
- strokeLinecap="round"
130
- strokeLinejoin="round"
131
- strokeWidth={2}
132
- viewBox="0 0 24 24"
133
- >
134
- <path d="M7 17L17 7M7 7h10v10" />
135
- </svg>
136
- );
137
-
138
- export const CopyPageMenu = ({
139
- content,
140
- contentUrl,
141
- title,
142
- }: CopyPageMenuProps) => {
143
- const resetTimerRef = useRef<number | null>(null);
144
- const [copyStatus, setCopyStatus] = useState<CopyStatus>("idle");
145
- const [fetchedContent, setFetchedContent] = useState<string | null>(null);
146
- const [menuOpen, setMenuOpen] = useState(false);
147
-
148
- const pageUrl = useMemo(
149
- () =>
150
- typeof window === "undefined"
151
- ? ""
152
- : new URL(contentUrl ?? window.location.href, window.location.href)
153
- .href,
154
- [contentUrl]
155
- );
156
-
157
- useEffect(
158
- () => () => {
159
- if (resetTimerRef.current !== null) {
160
- window.clearTimeout(resetTimerRef.current);
161
- }
162
- },
163
- []
164
- );
165
-
166
- useEffect(() => {
167
- if (content || !contentUrl) {
168
- return;
169
- }
170
-
171
- const controller = new AbortController();
172
-
173
- const prefetch = async () => {
174
- try {
175
- const res = await fetch(contentUrl, {
176
- headers: { accept: MARKDOWN_ACCEPT },
177
- signal: controller.signal,
178
- });
179
- const text = res.ok ? await res.text() : null;
180
- if (text) {
181
- setFetchedContent(text);
182
- }
183
- } catch {
184
- // Ignore prefetch failures — the copy handler retries on demand
185
- }
186
- };
187
-
188
- prefetch();
189
-
190
- return () => controller.abort();
191
- }, [content, contentUrl]);
192
-
193
- const closeMenu = useCallback(() => {
194
- setMenuOpen(false);
195
- }, []);
196
-
197
- const setTemporaryCopyStatus = useCallback(
198
- (nextStatus: "copied" | "error") => {
199
- if (resetTimerRef.current !== null) {
200
- window.clearTimeout(resetTimerRef.current);
201
- }
202
-
203
- setCopyStatus(nextStatus);
204
- resetTimerRef.current = window.setTimeout(() => {
205
- setCopyStatus("idle");
206
- resetTimerRef.current = null;
207
- }, 2000);
208
- },
209
- []
210
- );
211
-
212
- const getContent = useCallback(async () => {
213
- const resolvedContent = content ?? fetchedContent;
214
- if (resolvedContent) {
215
- return resolvedContent;
216
- }
217
-
218
- if (!contentUrl) {
219
- return "";
220
- }
221
-
222
- const response = await fetch(contentUrl, {
223
- headers: { accept: MARKDOWN_ACCEPT },
224
- });
225
- if (!response.ok) {
226
- throw new Error(`Failed to load page markdown: ${response.status}`);
227
- }
228
-
229
- const nextContent = await response.text();
230
- setFetchedContent(nextContent);
231
- return nextContent;
232
- }, [content, contentUrl, fetchedContent]);
233
-
234
- const handleCopy = useCallback(async () => {
235
- try {
236
- // Use ClipboardItem with a promise-based blob so the clipboard "slot"
237
- // is claimed synchronously during the tap gesture. iOS Safari revokes
238
- // user-activation if an async boundary (e.g. a network fetch) sits
239
- // between the tap and the clipboard call, so the old writeText path
240
- // fails when tapped before the pre-fetch finishes.
241
- if (typeof ClipboardItem !== "undefined" && navigator.clipboard.write) {
242
- const blobPromise = (async () => {
243
- const text = await getContent();
244
- const markdown = formatMarkdownForCopy(text, title);
245
- return new Blob([markdown], { type: "text/plain" });
246
- })();
247
- const item = new ClipboardItem({ "text/plain": blobPromise });
248
- await navigator.clipboard.write([item]);
249
- } else {
250
- const nextContent = await getContent();
251
- const markdown = formatMarkdownForCopy(nextContent, title);
252
- await navigator.clipboard.writeText(markdown);
253
- }
254
- setTemporaryCopyStatus("copied");
255
- closeMenu();
256
- } catch {
257
- setTemporaryCopyStatus("error");
258
- }
259
- }, [closeMenu, getContent, setTemporaryCopyStatus, title]);
260
-
261
- const chatgptUrl = pageUrl
262
- ? `https://chatgpt.com/?hints=search&q=${encodeURIComponent(`Read from ${pageUrl} so I can ask questions about it.`)}`
263
- : undefined;
264
- const claudeUrl = pageUrl
265
- ? `https://claude.ai/new?q=${encodeURIComponent(`Read from ${pageUrl} so I can ask questions about it.`)}`
266
- : undefined;
267
- const copyLabel = getCopyLabel(copyStatus);
268
- const copyDescription = getCopyDescription(copyStatus);
269
- const CopyIcon = getCopyIcon(copyStatus);
270
-
271
- return (
272
- <Popover onOpenChange={setMenuOpen} open={menuOpen}>
273
- <div className="flex shrink-0 items-center">
274
- <button
275
- className="inline-flex items-center gap-2 rounded-l-xl border border-r-0 border-border px-3 py-1.5 text-sm font-medium transition-colors hover:bg-secondary/25"
276
- onClick={handleCopy}
277
- type="button"
278
- >
279
- <CopyIcon aria-hidden="true" className="size-[18px]" />
280
- <span>{copyLabel}</span>
281
- </button>
282
-
283
- <PopoverTrigger asChild>
284
- <button
285
- aria-expanded={menuOpen}
286
- aria-label="More actions"
287
- className="inline-flex items-center self-stretch rounded-r-xl border border-border px-2 transition-colors hover:bg-secondary/25"
288
- type="button"
289
- >
290
- <ChevronDownSmallIcon
291
- aria-hidden="true"
292
- className="size-[18px] text-muted-foreground"
293
- />
294
- </button>
295
- </PopoverTrigger>
296
- </div>
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
-
316
- <MenuItem onSelect={handleCopy}>
317
- <MenuIcon>
318
- <CopyIcon aria-hidden="true" className="size-[18px]" />
319
- </MenuIcon>
320
- <div>
321
- <div className="font-medium">{copyLabel}</div>
322
- <div className="text-xs text-muted-foreground">
323
- {copyDescription}
324
- </div>
325
- </div>
326
- </MenuItem>
327
-
328
- {chatgptUrl ? (
329
- <MenuItem href={chatgptUrl} onSelect={closeMenu}>
330
- <MenuIcon>
331
- <OpenaiIcon aria-hidden="true" className="size-[18px]" />
332
- </MenuIcon>
333
- <div className="flex-1">
334
- <div className="font-medium">
335
- Open in ChatGPT
336
- <ExternalArrow />
337
- </div>
338
- <div className="text-xs text-muted-foreground">
339
- Ask questions about this page
340
- </div>
341
- </div>
342
- </MenuItem>
343
- ) : null}
344
-
345
- {claudeUrl ? (
346
- <MenuItem href={claudeUrl} onSelect={closeMenu}>
347
- <MenuIcon>
348
- <ClaudeaiIcon aria-hidden="true" className="size-[18px]" />
349
- </MenuIcon>
350
- <div className="flex-1">
351
- <div className="font-medium">
352
- Open in Claude
353
- <ExternalArrow />
354
- </div>
355
- <div className="text-xs text-muted-foreground">
356
- Ask questions about this page
357
- </div>
358
- </div>
359
- </MenuItem>
360
- ) : null}
361
- </PopoverContent>
362
- </Popover>
363
- );
364
- };
@@ -1,223 +0,0 @@
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 { isExternalHref, resolveHref, toDocHref } from "@/lib/routes";
10
- import { cn } from "@/lib/utils";
11
-
12
- const EMPTY_NAV: NavEntry[] = [];
13
-
14
- const Dropdown = ({
15
- label,
16
- items,
17
- basePath,
18
- }: {
19
- label: string;
20
- items: { label: string; url: string }[];
21
- basePath: string;
22
- }) => {
23
- if (!items.length) {
24
- return null;
25
- }
26
- return (
27
- <details className="relative">
28
- <summary className="cursor-pointer rounded-full border border-border bg-background px-3 py-1.5 text-sm">
29
- {label}
30
- </summary>
31
- <div className="absolute right-0 top-11 z-20 grid min-w-36 overflow-hidden rounded-xl border border-border bg-popover shadow-popover">
32
- {items.map((item) => {
33
- const href = resolveHref(item.url, basePath);
34
- const isExternal = isExternalHref(item.url);
35
-
36
- return (
37
- <Link
38
- className="px-3 py-2 hover:bg-accent"
39
- href={href}
40
- key={item.label}
41
- rel={isExternal ? "noopener noreferrer" : undefined}
42
- target={isExternal ? "_blank" : undefined}
43
- >
44
- {item.label}
45
- </Link>
46
- );
47
- })}
48
- </div>
49
- </details>
50
- );
51
- };
52
-
53
- const HeaderTabs = ({
54
- tabs,
55
- activeTabIndex = 0,
56
- basePath,
57
- }: {
58
- tabs: NavTab[];
59
- activeTabIndex?: number;
60
- basePath: string;
61
- }) => (
62
- <nav
63
- aria-label="Navigation tabs"
64
- className="ml-4 hidden items-center gap-0.5 lg:flex"
65
- >
66
- {tabs.map((tab, index) => {
67
- const isActive = index === activeTabIndex;
68
- const href =
69
- (tab.href ? resolveHref(tab.href, basePath) : undefined) ??
70
- (tab.slugPrefix ? toDocHref(tab.slugPrefix, basePath) : undefined);
71
-
72
- if (!href) {
73
- return null;
74
- }
75
-
76
- const isExternal = Boolean(tab.href && isExternalHref(tab.href));
77
-
78
- return (
79
- <Link
80
- className={cn(
81
- "relative px-2.5 py-1.5 text-sm transition-colors",
82
- isActive
83
- ? "font-medium text-primary"
84
- : "text-muted-foreground hover:text-foreground"
85
- )}
86
- href={href}
87
- key={tab.label}
88
- rel={isExternal ? "noopener noreferrer" : undefined}
89
- target={isExternal ? "_blank" : undefined}
90
- >
91
- {tab.label}
92
- {isActive ? (
93
- <span className="absolute inset-x-1 -bottom-3.5 h-0.5 rounded-full bg-primary" />
94
- ) : null}
95
- </Link>
96
- );
97
- })}
98
- </nav>
99
- );
100
-
101
- // oxlint-disable-next-line eslint/complexity
102
- export const DocHeader = ({
103
- config,
104
- basePath,
105
- tabs,
106
- activeTabIndex,
107
- nav = EMPTY_NAV,
108
- }: {
109
- config: SiteConfig;
110
- basePath: string;
111
- tabs?: NavTab[] | null;
112
- activeTabIndex?: number;
113
- nav?: NavEntry[];
114
- }) => {
115
- const globalLinks = config.navigation?.global?.links ?? [];
116
- const versions = config.navigation?.versions ?? [];
117
- const languages = config.navigation?.languages ?? [];
118
- const [primaryVersion] = versions;
119
- const [primaryLanguage] = languages;
120
- const searchDisabled = config.features?.search === false;
121
- const themeToggleDisabled = config.features?.themeToggle === false;
122
- const logoHref = config.logo?.href ?? toDocHref("index", basePath);
123
- const logoIsExternal = Boolean(
124
- config.logo?.href && isExternalHref(config.logo.href)
125
- );
126
-
127
- return (
128
- <header
129
- className="sticky top-0 z-50 w-full bg-background"
130
- data-markdown-ignore=""
131
- >
132
- <div className="container-wrapper px-4 lg:px-8">
133
- <div className="flex h-(--header-height) items-center">
134
- <MobileNav
135
- activeTabIndex={activeTabIndex}
136
- basePath={basePath}
137
- className="flex lg:hidden"
138
- entries={nav}
139
- globalLinks={globalLinks}
140
- tabs={tabs}
141
- />
142
- <Link
143
- className="flex items-center gap-2.5"
144
- href={logoHref}
145
- rel={logoIsExternal ? "noopener noreferrer" : undefined}
146
- target={logoIsExternal ? "_blank" : undefined}
147
- >
148
- {config.logo?.light ? (
149
- <Image
150
- alt={config.logo.alt ?? config.name}
151
- className="dark:hidden"
152
- data-logo="light"
153
- height={32}
154
- loading="eager"
155
- src={config.logo.light}
156
- unoptimized
157
- width={140}
158
- />
159
- ) : null}
160
- {config.logo?.dark ? (
161
- <Image
162
- alt={config.logo.alt ?? config.name}
163
- className="hidden dark:inline-block"
164
- data-logo="dark"
165
- height={32}
166
- loading="eager"
167
- src={config.logo.dark}
168
- unoptimized
169
- width={140}
170
- />
171
- ) : null}
172
- {config.logo?.light || config.logo?.dark ? null : (
173
- <span className="text-xl font-bold">{config.name}</span>
174
- )}
175
- </Link>
176
- {tabs?.length ? (
177
- <HeaderTabs
178
- activeTabIndex={activeTabIndex}
179
- basePath={basePath}
180
- tabs={tabs}
181
- />
182
- ) : null}
183
- <nav
184
- aria-label="External links"
185
- className="ml-1 hidden items-center gap-0 text-sm text-muted-foreground lg:flex"
186
- >
187
- {globalLinks.map((link) => (
188
- <a
189
- className="rounded-lg px-2.5 py-1.5 transition-colors hover:bg-muted hover:text-foreground"
190
- href={link.href}
191
- key={link.label}
192
- rel="noopener noreferrer"
193
- target="_blank"
194
- >
195
- {link.label}
196
- </a>
197
- ))}
198
- </nav>
199
- <div className="ml-auto flex items-center gap-2 md:flex-1 md:justify-end">
200
- {searchDisabled ? null : (
201
- <Search basePath={basePath} key={basePath} />
202
- )}
203
- {primaryVersion ? (
204
- <Dropdown
205
- basePath={basePath}
206
- items={versions}
207
- label={primaryVersion.label}
208
- />
209
- ) : null}
210
- {primaryLanguage ? (
211
- <Dropdown
212
- basePath={basePath}
213
- items={languages}
214
- label={primaryLanguage.label}
215
- />
216
- ) : null}
217
- {themeToggleDisabled ? null : <ThemeToggle />}
218
- </div>
219
- </div>
220
- </div>
221
- </header>
222
- );
223
- };