@baybreezy/docd 0.0.1

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 (268) hide show
  1. package/app/app.config.ts +239 -0
  2. package/app/app.vue +19 -0
  3. package/app/assets/css/tailwind.css +335 -0
  4. package/app/assets/css/theme.css +1171 -0
  5. package/app/components/OgImage/Docs.takumi.vue +69 -0
  6. package/app/components/OgImage/Landing.takumi.vue +64 -0
  7. package/app/components/OgImage/Site.takumi.vue +32 -0
  8. package/app/components/Ui/Avatar/Avatar.vue +75 -0
  9. package/app/components/Ui/Avatar/Badge.vue +34 -0
  10. package/app/components/Ui/Avatar/Fallback.vue +30 -0
  11. package/app/components/Ui/Avatar/Group.vue +29 -0
  12. package/app/components/Ui/Avatar/GroupCount.vue +29 -0
  13. package/app/components/Ui/Avatar/Image.vue +29 -0
  14. package/app/components/Ui/Badge.vue +86 -0
  15. package/app/components/Ui/BadgeGroup.vue +314 -0
  16. package/app/components/Ui/BgPattern/Grid.vue +137 -0
  17. package/app/components/Ui/BorderBeam.vue +97 -0
  18. package/app/components/Ui/Breadcrumbs.vue +103 -0
  19. package/app/components/Ui/Button.vue +169 -0
  20. package/app/components/Ui/ButtonGroup/ButtonGroup.vue +54 -0
  21. package/app/components/Ui/ButtonGroup/Separator.vue +38 -0
  22. package/app/components/Ui/ButtonGroup/Text.vue +33 -0
  23. package/app/components/Ui/Card/Action.vue +31 -0
  24. package/app/components/Ui/Card/Card.vue +58 -0
  25. package/app/components/Ui/Card/Content.vue +34 -0
  26. package/app/components/Ui/Card/Description.vue +36 -0
  27. package/app/components/Ui/Card/Footer.vue +31 -0
  28. package/app/components/Ui/Card/Header.vue +31 -0
  29. package/app/components/Ui/Card/Title.vue +37 -0
  30. package/app/components/Ui/Collapsible/Collapsible.vue +16 -0
  31. package/app/components/Ui/Collapsible/Content.vue +27 -0
  32. package/app/components/Ui/Collapsible/Trigger.vue +13 -0
  33. package/app/components/Ui/Command/Command.vue +115 -0
  34. package/app/components/Ui/Command/Dialog.vue +65 -0
  35. package/app/components/Ui/Command/Empty.vue +32 -0
  36. package/app/components/Ui/Command/Footer.vue +40 -0
  37. package/app/components/Ui/Command/Group.vue +58 -0
  38. package/app/components/Ui/Command/Input.vue +57 -0
  39. package/app/components/Ui/Command/Item.vue +102 -0
  40. package/app/components/Ui/Command/List.vue +33 -0
  41. package/app/components/Ui/Command/Separator.vue +30 -0
  42. package/app/components/Ui/Command/Shortcut.vue +28 -0
  43. package/app/components/Ui/Container.vue +46 -0
  44. package/app/components/Ui/ContextMenu/Arrow.vue +43 -0
  45. package/app/components/Ui/ContextMenu/CheckboxItem.vue +42 -0
  46. package/app/components/Ui/ContextMenu/Content.vue +53 -0
  47. package/app/components/Ui/ContextMenu/ContextMenu.vue +14 -0
  48. package/app/components/Ui/ContextMenu/Group.vue +12 -0
  49. package/app/components/Ui/ContextMenu/Item.vue +65 -0
  50. package/app/components/Ui/ContextMenu/ItemIndicator.vue +30 -0
  51. package/app/components/Ui/ContextMenu/Label.vue +35 -0
  52. package/app/components/Ui/ContextMenu/Portal.vue +12 -0
  53. package/app/components/Ui/ContextMenu/RadioGroup.vue +16 -0
  54. package/app/components/Ui/ContextMenu/RadioItem.vue +40 -0
  55. package/app/components/Ui/ContextMenu/Separator.vue +25 -0
  56. package/app/components/Ui/ContextMenu/Shortcut.vue +30 -0
  57. package/app/components/Ui/ContextMenu/Sub.vue +15 -0
  58. package/app/components/Ui/ContextMenu/SubContent.vue +55 -0
  59. package/app/components/Ui/ContextMenu/SubTrigger.vue +42 -0
  60. package/app/components/Ui/ContextMenu/Trigger.vue +12 -0
  61. package/app/components/Ui/Dialog/Close.vue +12 -0
  62. package/app/components/Ui/Dialog/Content.vue +79 -0
  63. package/app/components/Ui/Dialog/Description.vue +29 -0
  64. package/app/components/Ui/Dialog/Dialog.vue +14 -0
  65. package/app/components/Ui/Dialog/Footer.vue +32 -0
  66. package/app/components/Ui/Dialog/Header.vue +32 -0
  67. package/app/components/Ui/Dialog/Overlay.vue +25 -0
  68. package/app/components/Ui/Dialog/Portal.vue +12 -0
  69. package/app/components/Ui/Dialog/Title.vue +29 -0
  70. package/app/components/Ui/Dialog/Trigger.vue +12 -0
  71. package/app/components/Ui/Divider.vue +102 -0
  72. package/app/components/Ui/Drawer/Close.vue +12 -0
  73. package/app/components/Ui/Drawer/Content.vue +67 -0
  74. package/app/components/Ui/Drawer/Description.vue +33 -0
  75. package/app/components/Ui/Drawer/Drawer.vue +17 -0
  76. package/app/components/Ui/Drawer/Footer.vue +35 -0
  77. package/app/components/Ui/Drawer/Header.vue +34 -0
  78. package/app/components/Ui/Drawer/Overlay.vue +20 -0
  79. package/app/components/Ui/Drawer/Portal.vue +12 -0
  80. package/app/components/Ui/Drawer/Title.vue +35 -0
  81. package/app/components/Ui/Drawer/Trigger.vue +12 -0
  82. package/app/components/Ui/DropdownMenu/Arrow.vue +43 -0
  83. package/app/components/Ui/DropdownMenu/CheckboxItem.vue +41 -0
  84. package/app/components/Ui/DropdownMenu/Content.vue +56 -0
  85. package/app/components/Ui/DropdownMenu/DropdownMenu.vue +15 -0
  86. package/app/components/Ui/DropdownMenu/Group.vue +12 -0
  87. package/app/components/Ui/DropdownMenu/Item.vue +64 -0
  88. package/app/components/Ui/DropdownMenu/ItemIndicator.vue +32 -0
  89. package/app/components/Ui/DropdownMenu/Label.vue +34 -0
  90. package/app/components/Ui/DropdownMenu/Portal.vue +12 -0
  91. package/app/components/Ui/DropdownMenu/RadioGroup.vue +16 -0
  92. package/app/components/Ui/DropdownMenu/RadioItem.vue +40 -0
  93. package/app/components/Ui/DropdownMenu/Separator.vue +25 -0
  94. package/app/components/Ui/DropdownMenu/Shortcut.vue +44 -0
  95. package/app/components/Ui/DropdownMenu/Sub.vue +14 -0
  96. package/app/components/Ui/DropdownMenu/SubContent.vue +56 -0
  97. package/app/components/Ui/DropdownMenu/SubTrigger.vue +47 -0
  98. package/app/components/Ui/DropdownMenu/Trigger.vue +12 -0
  99. package/app/components/Ui/FancyIcon.vue +243 -0
  100. package/app/components/Ui/GradientDivider.vue +26 -0
  101. package/app/components/Ui/IframeLazy.vue +97 -0
  102. package/app/components/Ui/Input.vue +97 -0
  103. package/app/components/Ui/InputGroup/Addon.vue +66 -0
  104. package/app/components/Ui/InputGroup/Button.vue +50 -0
  105. package/app/components/Ui/InputGroup/Input.vue +25 -0
  106. package/app/components/Ui/InputGroup/InputGroup.vue +44 -0
  107. package/app/components/Ui/InputGroup/Text.vue +33 -0
  108. package/app/components/Ui/InputGroup/Textarea.vue +25 -0
  109. package/app/components/Ui/Kbd/Group.vue +32 -0
  110. package/app/components/Ui/Kbd/Kbd.vue +62 -0
  111. package/app/components/Ui/Label.vue +40 -0
  112. package/app/components/Ui/Popover/Anchor.vue +13 -0
  113. package/app/components/Ui/Popover/Arrow.vue +42 -0
  114. package/app/components/Ui/Popover/Close.vue +13 -0
  115. package/app/components/Ui/Popover/Content.vue +55 -0
  116. package/app/components/Ui/Popover/Popover.vue +15 -0
  117. package/app/components/Ui/Popover/Portal.vue +13 -0
  118. package/app/components/Ui/Popover/Trigger.vue +12 -0
  119. package/app/components/Ui/Popover/X.vue +38 -0
  120. package/app/components/Ui/ScrollArea/Corner.vue +12 -0
  121. package/app/components/Ui/ScrollArea/ScrollArea.vue +38 -0
  122. package/app/components/Ui/ScrollArea/Scrollbar.vue +39 -0
  123. package/app/components/Ui/ScrollArea/Thumb.vue +39 -0
  124. package/app/components/Ui/ScrollArea/Viewport.vue +27 -0
  125. package/app/components/Ui/Scrollspy.vue +61 -0
  126. package/app/components/Ui/Separator.vue +40 -0
  127. package/app/components/Ui/Skeleton.vue +39 -0
  128. package/app/components/Ui/Sonner.vue +25 -0
  129. package/app/components/Ui/Tabs/Content.vue +27 -0
  130. package/app/components/Ui/Tabs/Indicator.vue +37 -0
  131. package/app/components/Ui/Tabs/List.vue +37 -0
  132. package/app/components/Ui/Tabs/Tabs.vue +25 -0
  133. package/app/components/Ui/Tabs/Trigger.vue +41 -0
  134. package/app/components/Ui/Textarea.vue +81 -0
  135. package/app/components/Ui/Tooltip/Arrow.vue +30 -0
  136. package/app/components/Ui/Tooltip/Content.vue +50 -0
  137. package/app/components/Ui/Tooltip/Portal.vue +12 -0
  138. package/app/components/Ui/Tooltip/Provider.vue +14 -0
  139. package/app/components/Ui/Tooltip/Tooltip.vue +25 -0
  140. package/app/components/Ui/Tooltip/Trigger.vue +12 -0
  141. package/app/components/Ui/Tree/Item.vue +15 -0
  142. package/app/components/Ui/Tree/Tree.vue +15 -0
  143. package/app/components/Ui/Tree/Virtualizer.vue +13 -0
  144. package/app/components/component-api/ComponentApiSection.vue +135 -0
  145. package/app/components/content/docs/Callout/DocsCallout.vue +99 -0
  146. package/app/components/content/prose/ProseA.global.vue +31 -0
  147. package/app/components/content/prose/ProseBlockquote.global.vue +28 -0
  148. package/app/components/content/prose/ProseCallout.global.vue +306 -0
  149. package/app/components/content/prose/ProseCard.global.vue +100 -0
  150. package/app/components/content/prose/ProseCodeCollapse.global.vue +125 -0
  151. package/app/components/content/prose/ProseCodeCopy.global.vue +67 -0
  152. package/app/components/content/prose/ProseCodeGroup.global.vue +104 -0
  153. package/app/components/content/prose/ProseCodeTree.global.vue +277 -0
  154. package/app/components/content/prose/ProseCollapsible.global.vue +64 -0
  155. package/app/components/content/prose/ProseColorModeImage.global.vue +58 -0
  156. package/app/components/content/prose/ProseComponentApi.global.vue +104 -0
  157. package/app/components/content/prose/ProseComponentEvents.global.vue +27 -0
  158. package/app/components/content/prose/ProseComponentExposed.global.vue +27 -0
  159. package/app/components/content/prose/ProseComponentProps.global.vue +27 -0
  160. package/app/components/content/prose/ProseComponentSlots.global.vue +27 -0
  161. package/app/components/content/prose/ProseEm.global.vue +5 -0
  162. package/app/components/content/prose/ProseField.global.vue +118 -0
  163. package/app/components/content/prose/ProseFieldGroup.global.vue +62 -0
  164. package/app/components/content/prose/ProseH1.global.vue +58 -0
  165. package/app/components/content/prose/ProseH2.global.vue +58 -0
  166. package/app/components/content/prose/ProseH3.global.vue +58 -0
  167. package/app/components/content/prose/ProseH4.global.vue +58 -0
  168. package/app/components/content/prose/ProseH5.global.vue +47 -0
  169. package/app/components/content/prose/ProseH6.global.vue +47 -0
  170. package/app/components/content/prose/ProseHr.global.vue +33 -0
  171. package/app/components/content/prose/ProseIconList.global.vue +113 -0
  172. package/app/components/content/prose/ProseImg.global.vue +119 -0
  173. package/app/components/content/prose/ProseLead.global.vue +28 -0
  174. package/app/components/content/prose/ProseLi.global.vue +25 -0
  175. package/app/components/content/prose/ProseMermaid.global.vue +127 -0
  176. package/app/components/content/prose/ProseOl.global.vue +25 -0
  177. package/app/components/content/prose/ProseP.global.vue +25 -0
  178. package/app/components/content/prose/ProsePmInstall.global.vue +60 -0
  179. package/app/components/content/prose/ProsePmRun.global.vue +51 -0
  180. package/app/components/content/prose/ProsePmX.global.vue +56 -0
  181. package/app/components/content/prose/ProsePre.global.vue +181 -0
  182. package/app/components/content/prose/ProseShowCase.global.vue +56 -0
  183. package/app/components/content/prose/ProseSmartIcon.global.vue +54 -0
  184. package/app/components/content/prose/ProseStep.global.vue +22 -0
  185. package/app/components/content/prose/ProseSteps.global.vue +22 -0
  186. package/app/components/content/prose/ProseStrong.global.vue +34 -0
  187. package/app/components/content/prose/ProseTable.global.vue +34 -0
  188. package/app/components/content/prose/ProseTabs.global.vue +104 -0
  189. package/app/components/content/prose/ProseTabsInner.global.vue +274 -0
  190. package/app/components/content/prose/ProseTbody.global.vue +5 -0
  191. package/app/components/content/prose/ProseTd.global.vue +25 -0
  192. package/app/components/content/prose/ProseTh.global.vue +25 -0
  193. package/app/components/content/prose/ProseThead.global.vue +9 -0
  194. package/app/components/content/prose/ProseTr.global.vue +35 -0
  195. package/app/components/content/prose/ProseUl.global.vue +25 -0
  196. package/app/components/docs/DocsExtraStuff.vue +39 -0
  197. package/app/components/docs/DocsGithubLink.vue +11 -0
  198. package/app/components/docs/DocsHeader.vue +3 -0
  199. package/app/components/docs/DocsLogo.vue +74 -0
  200. package/app/components/docs/DocsMobileBar.vue +26 -0
  201. package/app/components/docs/DocsMobileNav.vue +49 -0
  202. package/app/components/docs/DocsNav.vue +161 -0
  203. package/app/components/docs/DocsPageTransition.vue +32 -0
  204. package/app/components/docs/DocsSearch.vue +220 -0
  205. package/app/components/docs/DocsSearchButton.vue +20 -0
  206. package/app/components/docs/DocsThemeCustomizer.vue +229 -0
  207. package/app/components/docs/DocsThemeToggler.vue +98 -0
  208. package/app/components/docs/DocsTocRail.vue +162 -0
  209. package/app/components/docs/DocsToclink.vue +52 -0
  210. package/app/components/docs/DocsZigZagRail.vue +70 -0
  211. package/app/components/landing/Hero.vue +222 -0
  212. package/app/components/page/PageFooter.vue +49 -0
  213. package/app/components/page/PageHeader.vue +68 -0
  214. package/app/components/page/PageHeaderLinks.vue +138 -0
  215. package/app/components/page/PageMobileToc.vue +162 -0
  216. package/app/components/page/PageToc.vue +33 -0
  217. package/app/composables/useDefaultSlotItems.ts +90 -0
  218. package/app/composables/useDocPage.ts +66 -0
  219. package/app/composables/useDocSearch.ts +23 -0
  220. package/app/composables/useDocd.ts +107 -0
  221. package/app/composables/useLogoAssets.ts +319 -0
  222. package/app/composables/usePm.ts +73 -0
  223. package/app/composables/useProseComponentMeta.ts +30 -0
  224. package/app/composables/useScrollspy.ts +327 -0
  225. package/app/composables/useSeo.ts +240 -0
  226. package/app/composables/useUIConfig.ts +36 -0
  227. package/app/layouts/default.vue +5 -0
  228. package/app/layouts/docs.vue +64 -0
  229. package/app/middleware/colorMode.global.ts +8 -0
  230. package/app/middleware/doc-page-layout.ts +11 -0
  231. package/app/middleware/landing-page-layout.ts +11 -0
  232. package/app/pages/[...slug].vue +63 -0
  233. package/app/plugins/mermaid.client.ts +35 -0
  234. package/app/plugins/navigation-direction.client.ts +15 -0
  235. package/app/plugins/theme-restore.client.ts +7 -0
  236. package/app/templates/landing.vue +40 -0
  237. package/app/transitions/presets.ts +71 -0
  238. package/app/types/prose-component-meta.d.ts +4 -0
  239. package/app/utils/og.ts +26 -0
  240. package/app/utils/prerender.ts +9 -0
  241. package/app/utils/translucent.ts +56 -0
  242. package/app/utils/tw-helper.ts +4 -0
  243. package/content.config.ts +81 -0
  244. package/index.d.ts +25 -0
  245. package/modules/config.ts +89 -0
  246. package/modules/custom-icons.ts +17 -0
  247. package/modules/markdown-rewrite.ts +96 -0
  248. package/modules/prose-component-meta.ts +150 -0
  249. package/modules/routing.ts +22 -0
  250. package/modules/skills/index.ts +167 -0
  251. package/modules/skills/runtime/server/routes/skills-files.ts +49 -0
  252. package/modules/skills/runtime/server/routes/skills-index.ts +8 -0
  253. package/nuxt.config.ts +96 -0
  254. package/nuxt.schema.ts +288 -0
  255. package/package.json +95 -0
  256. package/server/mcp/tools/get-page.ts +66 -0
  257. package/server/mcp/tools/list-pages.ts +61 -0
  258. package/server/routes/sitemap.xml.ts +73 -0
  259. package/server/utils/content.ts +3 -0
  260. package/tailwind.config.cjs +55 -0
  261. package/utils/component-api-content.ts +467 -0
  262. package/utils/component-api-discovery.ts +165 -0
  263. package/utils/component-api-schema.ts +28 -0
  264. package/utils/component-api.ts +243 -0
  265. package/utils/generate-prose-component-meta.ts +303 -0
  266. package/utils/git.ts +123 -0
  267. package/utils/meta.ts +40 -0
  268. package/utils/pages.ts +21 -0
@@ -0,0 +1,63 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ toc && toc.links && toc.links.length && !page?.hideToc
5
+ ? 'xl:grid-cols-[1fr_var(--nav-width)]'
6
+ : 'xl:grid-cols-1',
7
+ ]"
8
+ class="xl:grid xl:gap-5"
9
+ >
10
+ <div
11
+ v-if="page"
12
+ class="container prose prose-base max-w-7xl min-w-0 py-5 dark:prose-invert prose-headings:scroll-mt-20 prose-headings:tracking-tight prose-h2:mt-6 prose-h2:border-b prose-h2:pb-3 first:prose-h2:mt-10 prose-a:text-primary prose-a:no-underline prose-a:hover:underline prose-a:hover:underline-offset-2 prose-pre:my-0 prose-pre:rounded-sm prose-pre:p-2 prose-pre:px-0 prose-pre:text-base"
13
+ :class="[isDashed ? 'prose-headings:border-dashed' : '']"
14
+ >
15
+ <PageHeader />
16
+ <ContentRenderer :value="page" />
17
+ <PageFooter />
18
+ </div>
19
+ <PageToc />
20
+ </div>
21
+ </template>
22
+
23
+ <script lang="ts" setup>
24
+ import { findPageHeadline } from "@nuxt/content/utils";
25
+
26
+ definePageMeta({ middleware: "doc-page-layout" });
27
+
28
+ const route = useRoute();
29
+
30
+ const { page, navigation, headline, breadcrumbs } = await useDocPage();
31
+ const { isDashed } = useDocd();
32
+
33
+ if (!page.value) {
34
+ throw createError({ statusCode: 404, statusMessage: "Page not found", fatal: true });
35
+ }
36
+
37
+ const title = page.value?.seo?.title || page.value?.title;
38
+ const description = page.value?.seo?.description || page.value?.description;
39
+
40
+ const publishedAt = computed(() => page.value?.publishedAt || undefined);
41
+ const modifiedAt = computed(() => page.value?.modifiedAt || undefined);
42
+
43
+ useSeo({ title, description, type: "article", publishedAt, modifiedAt, breadcrumbs });
44
+ watch(
45
+ () => navigation?.value,
46
+ () => {
47
+ headline.value = findPageHeadline(navigation?.value, page.value?.path) || headline.value;
48
+ }
49
+ );
50
+ // Add the page path to the prerender list
51
+ addPrerenderPath(`/raw${route.path}.md`);
52
+
53
+ defineOgImage("Docs.takumi", {
54
+ title: title?.slice(0, 60),
55
+ description: formatOgDescription(title, description),
56
+ headline: headline.value,
57
+ });
58
+
59
+ const toc = computed(() => {
60
+ if (!page.value) return;
61
+ return page.value?.body?.toc;
62
+ });
63
+ </script>
@@ -0,0 +1,35 @@
1
+ import mermaid from "mermaid";
2
+ import type { MermaidConfig } from "mermaid";
3
+
4
+ export default defineNuxtPlugin(() => {
5
+ /**
6
+ * Mermaid initialization configuration
7
+ */
8
+ const mermaidInitConfig = {
9
+ startOnLoad: false,
10
+ themeVariables: {
11
+ fontFamily: "var(--font-sans)",
12
+ fontSize: "13px",
13
+ },
14
+ flowchart: {
15
+ curve: "basis",
16
+ useMaxWidth: true,
17
+ },
18
+ sequence: {
19
+ actorMargin: 50,
20
+ showSequenceNumbers: false,
21
+ },
22
+ suppressErrorRendering: true,
23
+ } as MermaidConfig;
24
+ /**
25
+ * Initialize Mermaid with the specified configuration
26
+ */
27
+ mermaid.initialize(mermaidInitConfig);
28
+
29
+ return {
30
+ provide: {
31
+ mermaidInstance: mermaid,
32
+ mermaidInitConfig,
33
+ },
34
+ };
35
+ });
@@ -0,0 +1,15 @@
1
+ export default defineNuxtPlugin(() => {
2
+ const router = useRouter();
3
+ const direction = useState<"forward" | "back">("nav-direction", () => "forward");
4
+
5
+ let currentPosition = window?.history?.state?.position ?? 0;
6
+
7
+ router.beforeEach(() => {
8
+ const nextPosition = window?.history?.state?.position ?? 0;
9
+ direction.value = nextPosition < currentPosition ? "back" : "forward";
10
+ });
11
+
12
+ router.afterEach(() => {
13
+ currentPosition = window?.history?.state?.position ?? 0;
14
+ });
15
+ });
@@ -0,0 +1,7 @@
1
+ export default defineNuxtPlugin(() => {
2
+ const theme = localStorage.getItem("docs-color-theme");
3
+ const radius = localStorage.getItem("docs-radius");
4
+
5
+ if (theme) document.documentElement.classList.add(theme);
6
+ if (radius) document.documentElement.style.setProperty("--radius", radius);
7
+ });
@@ -0,0 +1,40 @@
1
+ <script setup lang="ts">
2
+ interface LandingPage {
3
+ title?: string;
4
+ description?: string;
5
+ layout?: string;
6
+ seo?: {
7
+ title?: string;
8
+ description?: string;
9
+ };
10
+ }
11
+
12
+ definePageMeta({ middleware: "landing-page-layout" });
13
+
14
+ const route = useRoute();
15
+
16
+ const { data: page } = await useAsyncData<LandingPage | null>("landing", () =>
17
+ queryCollection("landing" as never)
18
+ .path(route.path)
19
+ .first()
20
+ .then((r) => r ?? null)
21
+ );
22
+
23
+ if (!page.value) {
24
+ throw createError({ statusCode: 404, statusMessage: "Page not found", fatal: true });
25
+ }
26
+
27
+ const title = page.value?.seo?.title || page.value?.title;
28
+ const description = page.value?.seo?.description || page.value?.description;
29
+
30
+ useSeo({ title, description, type: "website" });
31
+
32
+ defineOgImage("Landing", {
33
+ title: title?.slice(0, 60),
34
+ description: formatOgDescription(title, description),
35
+ });
36
+ </script>
37
+
38
+ <template>
39
+ <ContentRenderer v-if="page" :value="page" />
40
+ </template>
@@ -0,0 +1,71 @@
1
+ export type DocdTransitionName =
2
+ | "fade"
3
+ | "rightToLeft"
4
+ | "leftToRight"
5
+ | "upToDown"
6
+ | "downToUp"
7
+ | "rightToLeftWithFade"
8
+ | "leftToRightWithFade"
9
+ | "zoom"
10
+ | "zoomOut"
11
+ | "cupertino"
12
+ | "cupertinoDialog"
13
+ | "none";
14
+
15
+ export interface TransitionStates {
16
+ initial: Record<string, string | number>;
17
+ exit: Record<string, string | number>;
18
+ }
19
+
20
+ export const transitionPresets: Record<DocdTransitionName, TransitionStates> = {
21
+ fade: {
22
+ initial: { opacity: 0 },
23
+ exit: { opacity: 0 },
24
+ },
25
+ rightToLeft: {
26
+ initial: { x: "100%", opacity: 0 },
27
+ exit: { x: "-30%", opacity: 0 },
28
+ },
29
+ leftToRight: {
30
+ initial: { x: "-100%", opacity: 0 },
31
+ exit: { x: "30%", opacity: 0 },
32
+ },
33
+ upToDown: {
34
+ initial: { y: "-100%", opacity: 0 },
35
+ exit: { y: "30%", opacity: 0 },
36
+ },
37
+ downToUp: {
38
+ initial: { y: "100%", opacity: 0 },
39
+ exit: { y: "-30%", opacity: 0 },
40
+ },
41
+ rightToLeftWithFade: {
42
+ initial: { x: "60%", opacity: 0 },
43
+ exit: { x: "-60%", opacity: 0 },
44
+ },
45
+ leftToRightWithFade: {
46
+ initial: { x: "-60%", opacity: 0 },
47
+ exit: { x: "60%", opacity: 0 },
48
+ },
49
+ zoom: {
50
+ initial: { scale: 0.85, opacity: 0 },
51
+ exit: { scale: 1.1, opacity: 0 },
52
+ },
53
+ zoomOut: {
54
+ initial: { scale: 1.15, opacity: 0 },
55
+ exit: { scale: 0.85, opacity: 0 },
56
+ },
57
+ cupertino: {
58
+ initial: { x: "2%", opacity: 0 },
59
+ exit: { x: "-2%", opacity: 0 },
60
+ },
61
+ cupertinoDialog: {
62
+ initial: { y: "2%", scale: 0.95, opacity: 0 },
63
+ exit: { y: "2%", opacity: 0 },
64
+ },
65
+ none: {
66
+ initial: {},
67
+ exit: {},
68
+ },
69
+ };
70
+
71
+ export const ANIMATE_IDENTITY = { x: 0, y: 0, scale: 1, opacity: 1 } as const;
@@ -0,0 +1,4 @@
1
+ declare module "#build/docd/prose-component-meta" {
2
+ const manifest: import("../../utils/generate-prose-component-meta").ProseComponentMetaManifest;
3
+ export default manifest;
4
+ }
@@ -0,0 +1,26 @@
1
+ // nuxt-og-image caps the encoded URL segment at 200 chars.
2
+ // Fixed overhead (component name, param keys, path encoding) is ~50 chars,
3
+ // leaving a ~150-char budget for title + description combined.
4
+ const OG_BUDGET = 150;
5
+
6
+ /**
7
+ * Trims description to fit within the nuxt-og-image 200-char URL segment limit,
8
+ * accounting for the title length and trying to cut at the last sentence boundary.
9
+ */
10
+ export function formatOgDescription(
11
+ title: string | undefined,
12
+ description: string | undefined
13
+ ): string | undefined {
14
+ if (!description) return undefined;
15
+
16
+ const titleLen = Math.min(title?.length ?? 0, 60);
17
+ const maxLen = OG_BUDGET - titleLen;
18
+ if (maxLen <= 0) return undefined;
19
+
20
+ const cleaned = description.replace(/,/g, "");
21
+ if (cleaned.length <= maxLen) return cleaned;
22
+
23
+ const truncated = cleaned.slice(0, maxLen);
24
+ const lastDot = truncated.lastIndexOf(".");
25
+ return lastDot > 0 ? truncated.slice(0, lastDot + 1) : truncated;
26
+ }
@@ -0,0 +1,9 @@
1
+ export const addPrerenderPath = (path: string) => {
2
+ const event = useRequestEvent();
3
+ if (event) {
4
+ event.node.res.setHeader(
5
+ "x-nitro-prerender",
6
+ [event.node.res.getHeader("x-nitro-prerender"), path].filter(Boolean).join(",")
7
+ );
8
+ }
9
+ };
@@ -0,0 +1,56 @@
1
+ export type FloatingSurfaceTone = "background" | "popover";
2
+ export type FloatingArrowShape = "square" | "svg";
3
+
4
+ export function getTranslucentFloatingPanelClasses(tone: FloatingSurfaceTone) {
5
+ if (tone === "background") {
6
+ return [
7
+ "bg-background/68",
8
+ "supports-backdrop-filter:bg-background/42",
9
+ "supports-backdrop-filter:backdrop-blur-2xl",
10
+ "supports-backdrop-filter:backdrop-saturate-150",
11
+ "supports-backdrop-filter:backdrop-brightness-110",
12
+ "border-border/50",
13
+ "shadow-2xl shadow-black/14 dark:shadow-black/42",
14
+ ].join(" ");
15
+ }
16
+
17
+ return [
18
+ "bg-popover/68",
19
+ "supports-backdrop-filter:bg-popover/40",
20
+ "supports-backdrop-filter:backdrop-blur-2xl",
21
+ "supports-backdrop-filter:backdrop-saturate-150",
22
+ "supports-backdrop-filter:backdrop-brightness-110",
23
+ "border-border/50",
24
+ "shadow-xl shadow-black/12 dark:shadow-black/36",
25
+ ].join(" ");
26
+ }
27
+
28
+ export function getTranslucentFloatingArrowClasses(
29
+ tone: FloatingSurfaceTone,
30
+ shape: FloatingArrowShape = "svg"
31
+ ) {
32
+ const fillClass = tone === "background" ? "fill-background" : "fill-popover";
33
+
34
+ if (shape === "square") {
35
+ return [
36
+ tone === "background" ? "bg-background/68" : "bg-popover/68",
37
+ tone === "background"
38
+ ? "supports-backdrop-filter:bg-background/42"
39
+ : "supports-backdrop-filter:bg-popover/40",
40
+ "supports-backdrop-filter:backdrop-blur-2xl",
41
+ "supports-backdrop-filter:backdrop-saturate-150",
42
+ "supports-backdrop-filter:backdrop-brightness-110",
43
+ "border-border/50",
44
+ "shadow-lg shadow-black/12 dark:shadow-black/36",
45
+ ].join(" ");
46
+ }
47
+
48
+ return [
49
+ `${fillClass}/68`,
50
+ tone === "background"
51
+ ? "supports-backdrop-filter:fill-background/42"
52
+ : "supports-backdrop-filter:fill-popover/40",
53
+ "stroke-border/50 [stroke-width:1px]",
54
+ "drop-shadow-[0_10px_22px_rgb(0_0_0/0.16)] dark:drop-shadow-[0_12px_24px_rgb(0_0_0/0.4)]",
55
+ ].join(" ");
56
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Utility function to return Tailwind CSS classes.
3
+ */
4
+ export const tw = <T extends TemplateStringsArray | string>(tailwindClasses: T) => tailwindClasses;
@@ -0,0 +1,81 @@
1
+ import {
2
+ defineCollection,
3
+ defineContentConfig,
4
+ type DefinedCollection,
5
+ property,
6
+ } from "@nuxt/content";
7
+ import { useNuxt, createResolver } from "@nuxt/kit";
8
+ import { joinURL } from "ufo";
9
+ import { z } from "zod";
10
+
11
+ import { componentApiSchema } from "./utils/component-api-schema";
12
+ import { landingPageExists, docsFolderExists } from "./utils/pages";
13
+
14
+ const resolver = createResolver(import.meta.url);
15
+
16
+ const { options } = useNuxt();
17
+ const cwd = joinURL(options.rootDir, "content");
18
+ const hasLandingPage = landingPageExists(options.rootDir);
19
+ const hasDocsFolder = docsFolderExists(options.rootDir);
20
+
21
+ let collections: Record<string, DefinedCollection> = {};
22
+
23
+ const baseSchema = z.object({
24
+ links: z
25
+ .array(
26
+ z.object({
27
+ title: z.string(),
28
+ href: z.string(),
29
+ icon: z.string().optional(),
30
+ })
31
+ )
32
+ .optional(),
33
+ fancyIcon: property(z.object({})).inherit(resolver.resolve("./app/components/Ui/FancyIcon.vue")),
34
+ hideToc: z.boolean().optional().default(false),
35
+ navigation: z
36
+ .union([
37
+ z.boolean(),
38
+ z.object({
39
+ title: z.string().optional(),
40
+ description: z.string().optional(),
41
+ icon: z.string().optional(),
42
+ }),
43
+ ])
44
+ .default(true),
45
+ publishedAt: z.iso.date().optional(),
46
+ modifiedAt: z.iso.date().optional(),
47
+ layout: z.string().optional(),
48
+ componentApi: componentApiSchema,
49
+ });
50
+
51
+ const docsSchema = baseSchema.extend({
52
+ layout: z.string().default("docs").optional(),
53
+ });
54
+
55
+ collections = {
56
+ docs: defineCollection({
57
+ // Specify the type of content in this collection
58
+ type: "page",
59
+ // Load every file inside the `content` directory
60
+ source: {
61
+ cwd,
62
+ include: hasDocsFolder ? "docs/**" : "**",
63
+ prefix: hasDocsFolder ? "/docs" : "/",
64
+ exclude: ["index.md"],
65
+ },
66
+ schema: docsSchema,
67
+ }),
68
+ };
69
+
70
+ if (!hasLandingPage) {
71
+ collections.landing = defineCollection({
72
+ type: "page",
73
+ source: {
74
+ cwd,
75
+ include: "index.md",
76
+ },
77
+ schema: baseSchema,
78
+ });
79
+ }
80
+
81
+ export default defineContentConfig({ collections });
package/index.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ import type { SkillsModuleOptions } from "./modules/skills";
2
+
3
+ export interface DocdNuxtConfig {
4
+ skills?: SkillsModuleOptions;
5
+ }
6
+
7
+ declare module "@nuxt/schema" {
8
+ interface NuxtConfig {
9
+ docd?: DocdNuxtConfig;
10
+ }
11
+
12
+ interface NuxtOptions {
13
+ docd?: DocdNuxtConfig;
14
+ }
15
+ }
16
+
17
+ declare module "nuxt/schema" {
18
+ interface NuxtConfig {
19
+ docd?: DocdNuxtConfig;
20
+ }
21
+
22
+ interface NuxtOptions {
23
+ docd?: DocdNuxtConfig;
24
+ }
25
+ }
@@ -0,0 +1,89 @@
1
+ import { defineNuxtModule } from "@nuxt/kit";
2
+ import { defaultsDeep } from "lodash-es";
3
+
4
+ import { getGitBranch, getGitEnv, getLocalGitInfo } from "../utils/git";
5
+ import { getPackageJsonMetadata, inferSiteURL } from "../utils/meta";
6
+
7
+ export default defineNuxtModule({
8
+ meta: {
9
+ name: "docd:config",
10
+ },
11
+
12
+ async setup(_options, nuxt) {
13
+ const dir = nuxt.options.rootDir;
14
+ const url = inferSiteURL();
15
+ const meta = await getPackageJsonMetadata(dir);
16
+ const gitInfo = (await getLocalGitInfo(dir)) || getGitEnv();
17
+ const siteName =
18
+ (typeof nuxt.options.site === "object" && nuxt.options.site?.name) ||
19
+ meta.name ||
20
+ gitInfo?.name ||
21
+ "";
22
+
23
+ nuxt.options.vite.optimizeDeps = defaultsDeep({}, nuxt.options.vite.optimizeDeps, {
24
+ include: [
25
+ "mermaid",
26
+ "lodash-es",
27
+ "tailwind-variants",
28
+ "@baybreezy/file-extension-icon",
29
+ "@iconify/utils",
30
+ ],
31
+ });
32
+
33
+ nuxt.options.llms = defaultsDeep({}, nuxt.options.llms, {
34
+ domain: url,
35
+ title: siteName,
36
+ description: meta.description || "",
37
+ full: {
38
+ title: siteName,
39
+ description: meta.description || "",
40
+ },
41
+ });
42
+
43
+ nuxt.options.app.head = defaultsDeep({}, nuxt.options.app.head, {
44
+ title: siteName,
45
+ titleTemplate: `%s - ${siteName}`,
46
+ } as typeof nuxt.options.app.head);
47
+
48
+ nuxt.options.site = defaultsDeep({}, nuxt.options.site, {
49
+ url: url || "",
50
+ name: siteName,
51
+ debug: false,
52
+ }) as typeof nuxt.options.site;
53
+
54
+ if (siteName) {
55
+ nuxt.options.colorMode = defaultsDeep({}, nuxt.options.colorMode, {
56
+ storageKey: `${siteName.toLocaleLowerCase().replace(/\s+/g, "-")}-color-mode`,
57
+ }) as typeof nuxt.options.colorMode;
58
+ }
59
+
60
+ nuxt.options.appConfig.docd = defaultsDeep({}, nuxt.options.appConfig.docd, {
61
+ github: {
62
+ repo: gitInfo?.url ?? url ?? "",
63
+ branch: getGitBranch(),
64
+ contentDir: "content",
65
+ },
66
+ ui: {
67
+ header: {
68
+ title: siteName,
69
+ },
70
+ },
71
+ });
72
+
73
+ nuxt.options.appConfig.seo = defaultsDeep({}, nuxt.options.appConfig.seo, {
74
+ titleTemplate: `%s - ${siteName}`,
75
+ title: siteName,
76
+ description: meta.description || "",
77
+ });
78
+
79
+ const forcedColorMode = (
80
+ nuxt.options.appConfig.docd as { ui?: { colorMode?: string } } | undefined
81
+ )?.ui?.colorMode;
82
+ if (forcedColorMode === "light" || forcedColorMode === "dark") {
83
+ nuxt.options.colorMode = defaultsDeep(
84
+ { preference: forcedColorMode, fallback: forcedColorMode },
85
+ nuxt.options.colorMode || {}
86
+ ) as typeof nuxt.options.colorMode;
87
+ }
88
+ },
89
+ });
@@ -0,0 +1,17 @@
1
+ import { createResolver, defineNuxtModule } from "@nuxt/kit";
2
+
3
+ export default defineNuxtModule({
4
+ meta: {
5
+ name: "docd:custom-icons",
6
+ },
7
+ setup(_options, nuxt) {
8
+ const { resolve } = createResolver(import.meta.url);
9
+
10
+ nuxt.options.icon ||= {};
11
+ nuxt.options.icon.customCollections ||= [];
12
+ nuxt.options.icon.customCollections.push({
13
+ prefix: "custom",
14
+ dir: resolve(nuxt.options.srcDir, "assets/icons"),
15
+ });
16
+ },
17
+ });
@@ -0,0 +1,96 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+
4
+ import { defineNuxtModule, logger } from "@nuxt/kit";
5
+
6
+ const log = logger.withTag("Docd");
7
+
8
+ export default defineNuxtModule({
9
+ meta: {
10
+ name: "docd:markdown-rewrite",
11
+ },
12
+ setup(_options, nuxt) {
13
+ nuxt.hooks.hook("nitro:init", (nitro) => {
14
+ if (nitro.options.dev || !nitro.options.preset.includes("vercel")) {
15
+ return;
16
+ }
17
+
18
+ nitro.hooks.hook("compiled", async () => {
19
+ const vcJSON = resolve(nitro.options.output.dir, "config.json");
20
+ const vcConfig = JSON.parse(await readFile(vcJSON, "utf8"));
21
+
22
+ // Check if llms.txt exists before setting up any routes
23
+ let llmsTxt: string;
24
+ const llmsTxtPath = resolve(nitro.options.output.publicDir, "llms.txt");
25
+ try {
26
+ llmsTxt = await readFile(llmsTxtPath, "utf-8");
27
+ } catch {
28
+ log.warn("llms.txt not found, skipping markdown redirect routes");
29
+ return;
30
+ }
31
+
32
+ // Always redirect / to /llms.txt and ensure plain text content type
33
+ const markdownHeaders = {
34
+ "content-type": "text/markdown; charset=utf-8",
35
+ };
36
+
37
+ const routes = [
38
+ {
39
+ src: "^/$",
40
+ dest: "/llms.txt",
41
+ headers: markdownHeaders,
42
+ has: [{ type: "header", key: "accept", value: "(.*)text/markdown(.*)" }],
43
+ },
44
+ {
45
+ src: "^/$",
46
+ dest: "/llms.txt",
47
+ headers: markdownHeaders,
48
+ has: [{ type: "header", key: "user-agent", value: "curl/.*" }],
49
+ },
50
+ ];
51
+
52
+ // Parse llms.txt to get all documentation page URLs
53
+ const urlRegex = /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g;
54
+ const matches = [...llmsTxt.matchAll(urlRegex)];
55
+
56
+ for (const match of matches) {
57
+ const url = match[2];
58
+ if (!url) continue;
59
+
60
+ try {
61
+ const urlObj = new URL(url);
62
+ const rawPath = urlObj.pathname;
63
+
64
+ if (rawPath === "/") continue;
65
+ if (!rawPath.startsWith("/raw/")) continue;
66
+
67
+ // Convert /raw/getting-started/installation.md → /getting-started/installation
68
+ const pagePath = rawPath.replace("/raw", "").replace(/\.md$/, "");
69
+
70
+ routes.push(
71
+ {
72
+ src: `^${pagePath}$`,
73
+ dest: rawPath,
74
+ headers: markdownHeaders,
75
+ has: [{ type: "header", key: "accept", value: "(.*)text/markdown(.*)" }],
76
+ },
77
+ {
78
+ src: `^${pagePath}$`,
79
+ dest: rawPath,
80
+ headers: markdownHeaders,
81
+ has: [{ type: "header", key: "user-agent", value: "curl/.*" }],
82
+ }
83
+ );
84
+ } catch {
85
+ // Skip invalid URLs
86
+ }
87
+ }
88
+
89
+ vcConfig.routes.unshift(...routes);
90
+
91
+ await writeFile(vcJSON, JSON.stringify(vcConfig, null, 2), "utf8");
92
+ log.info(`Wrote ${routes.length} markdown redirect routes for AI agents`);
93
+ });
94
+ });
95
+ },
96
+ });