@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,68 @@
1
+ <template>
2
+ <div class="not-prose mb-10">
3
+ <p
4
+ v-if="headline && !hasFancyIcon"
5
+ class="mb-4 font-bold tracking-tight text-primary capitalize"
6
+ >
7
+ {{ headline }}
8
+ </p>
9
+ <div class="flex flex-col gap-4">
10
+ <UiFancyIcon
11
+ v-if="page?.fancyIcon && page.fancyIcon.icon"
12
+ :theme="page.fancyIcon.theme ?? 'modern-neue'"
13
+ :color="page.fancyIcon.color ?? 'primary'"
14
+ :size="page.fancyIcon.size ?? 'lg'"
15
+ class="shrink-0 text-foreground/70"
16
+ :icon="page.fancyIcon.icon"
17
+ />
18
+ <UiFancyIcon
19
+ v-else-if="page?.navigation && page.navigation.icon && typeof page.navigation === 'object'"
20
+ theme="modern-neue"
21
+ size="lg"
22
+ class="shrink-0 text-foreground/70"
23
+ :icon="page.navigation.icon"
24
+ />
25
+ <div class="w-full grow">
26
+ <div class="flex flex-wrap items-start justify-between gap-4">
27
+ <h1 class="text-4xl leading-none font-bold">{{ page?.title }}</h1>
28
+ <PageHeaderLinks />
29
+ </div>
30
+ <p v-if="page?.description" class="mt-2 mb-4 text-lg text-muted-foreground">
31
+ {{ page?.description }}
32
+ </p>
33
+ <div v-if="page?.links && page?.links?.length" class="not-prose mb-6 flex flex-wrap gap-2">
34
+ <template v-for="(link, i) in page?.links" :key="i">
35
+ <UiBadge
36
+ :href="link?.href"
37
+ target="_blank"
38
+ variant="outline"
39
+ size="md"
40
+ class="transition-colors duration-300"
41
+ >
42
+ <Icon
43
+ v-if="link?.icon"
44
+ :name="link.icon"
45
+ class="size-3.5 shrink-0 text-muted-foreground"
46
+ />{{ link?.title }}
47
+ </UiBadge>
48
+ </template>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ <UiDivider :type="isDashed ? 'dashed' : 'solid'" />
53
+ </div>
54
+ </template>
55
+
56
+ <script lang="ts" setup>
57
+ const { headline, page } = await useDocPage();
58
+ const { isDashed } = useDocd();
59
+
60
+ const hasFancyIcon = computed(() => {
61
+ return (
62
+ (page.value?.fancyIcon && page.value.fancyIcon.icon) ||
63
+ (page.value?.navigation &&
64
+ page.value.navigation.icon &&
65
+ typeof page.value.navigation === "object")
66
+ );
67
+ });
68
+ </script>
@@ -0,0 +1,138 @@
1
+ <template>
2
+ <UiButtonGroup>
3
+ <UiTooltip>
4
+ <UiTooltipTrigger as-child>
5
+ <UiButton variant="secondary" size="sm" :disabled="copied" @click="copyPage()">
6
+ <Icon :name="copied ? 'lucide:check' : 'lucide:copy'" class="size-3.5" />
7
+ <span class="sr-only">{{ copied ? "Copied!" : "Copy Page" }}</span>
8
+ </UiButton>
9
+ </UiTooltipTrigger>
10
+ <UiTooltipContent side="bottom">
11
+ <p class="text-center">
12
+ {{
13
+ copied
14
+ ? "Markdown link copied to clipboard!"
15
+ : "Copy the markdown link for this page to your clipboard."
16
+ }}
17
+ </p>
18
+ </UiTooltipContent>
19
+ </UiTooltip>
20
+ <UiButtonGroupSeparator />
21
+ <UiTooltip>
22
+ <UiDropdownMenu>
23
+ <UiTooltipTrigger as-child>
24
+ <UiDropdownMenuTrigger as-child>
25
+ <UiButton variant="secondary" size="icon-sm">
26
+ <Icon name="lucide:chevron-down" />
27
+ <span class="sr-only">More options for header links </span>
28
+ </UiButton>
29
+ </UiDropdownMenuTrigger>
30
+ </UiTooltipTrigger>
31
+ <UiTooltipContent side="bottom">
32
+ <p class="text-center">More options for this page</p>
33
+ </UiTooltipContent>
34
+ <UiDropdownMenuContent align="end" :collision-padding="10" class="w-60" translucent>
35
+ <UiDropdownMenuGroup class="flex flex-col gap-0.5">
36
+ <template v-for="(item, i) in items" :key="i">
37
+ <UiDropdownMenuItem
38
+ v-if="item.action"
39
+ :icon="item.icon"
40
+ :title="item.label"
41
+ @select="item.action()"
42
+ />
43
+ <UiDropdownMenuItem v-else-if="item.to" :text-value="item.label" as-child>
44
+ <NuxtLink
45
+ :to="item.to"
46
+ target="_blank"
47
+ class="flex w-full cursor-pointer items-center gap-2"
48
+ >
49
+ <Icon :name="item.icon" />
50
+ <p v-html="item.label" />
51
+ <Icon name="lucide:arrow-up-right" class="ml-auto text-muted-foreground" />
52
+ </NuxtLink>
53
+ </UiDropdownMenuItem>
54
+ <UiDropdownMenuSeparator v-else-if="item.separator" />
55
+ </template>
56
+ </UiDropdownMenuGroup>
57
+ </UiDropdownMenuContent>
58
+ </UiDropdownMenu>
59
+ </UiTooltip>
60
+ </UiButtonGroup>
61
+ </template>
62
+
63
+ <script lang="ts" setup>
64
+ const route = useRoute();
65
+ const appBaseURL = useRuntimeConfig().app?.baseURL || "/";
66
+
67
+ const { copy, copied } = useClipboard();
68
+ const markdownLink = computed(() => `${window?.location?.origin}/raw${route.path}.md`);
69
+ const mcpServerUrl = computed(() => `${window?.location?.origin}${appBaseURL}mcp`);
70
+
71
+ const copyPage = async () => {
72
+ try {
73
+ const response = await $fetch<string>(`/raw/${route.path}.md`);
74
+ if (response) {
75
+ copy(response);
76
+ useSonner.success("Markdown Link Copied", {
77
+ description: "The markdown link for this page has been copied to your clipboard.",
78
+ });
79
+ }
80
+ } catch {
81
+ useSonner.error("Link Unavailable", {
82
+ description: "This page does not have a markdown link available.",
83
+ });
84
+ }
85
+ };
86
+
87
+ const items = [
88
+ {
89
+ label: "Copy Markdown Link",
90
+ icon: "lucide:link",
91
+ action: () => {
92
+ copy(markdownLink.value);
93
+ },
94
+ },
95
+ {
96
+ label: "Copy MCP Server URL",
97
+ icon: "lucide:cpu",
98
+ action: () => {
99
+ copy(mcpServerUrl.value);
100
+ useSonner.success("MCP URL Copied", {
101
+ description: "The MCP server URL has been copied to your clipboard.",
102
+ });
103
+ },
104
+ },
105
+ { separator: true },
106
+ {
107
+ label: "View Markdown Page",
108
+ icon: "simple-icons:markdown",
109
+ to: `/raw${route.path}.md`,
110
+ },
111
+ {
112
+ label: "Open in ChatGPT",
113
+ icon: "simple-icons:openai",
114
+ to: `https://chatgpt.com/?hints=search&q=${encodeURIComponent(`Read ${markdownLink.value} so I can ask questions about it.`)}`,
115
+ },
116
+ {
117
+ label: "Open in Claude",
118
+ icon: "simple-icons:claude",
119
+ to: `https://claude.ai/new?q=${encodeURIComponent(`Read ${markdownLink.value} so I can ask questions about it.`)}`,
120
+ },
121
+ {
122
+ label: "Open in T3",
123
+ icon: "lucide:message-circle",
124
+ to: `https://t3.chat/new?q=${encodeURIComponent(`Read ${markdownLink.value} so I can ask questions about it.`)}`,
125
+ },
126
+ { separator: true },
127
+ {
128
+ label: `View llms.txt File`,
129
+ icon: "lucide:bot",
130
+ to: `/llms.txt`,
131
+ },
132
+ {
133
+ label: `View llms-full.txt File`,
134
+ icon: "lucide:bot",
135
+ to: `/llms-full.txt`,
136
+ },
137
+ ];
138
+ </script>
@@ -0,0 +1,162 @@
1
+ <template>
2
+ <div
3
+ v-if="hasToc"
4
+ class="sticky top-[calc(var(--header-height)*2)] z-40 tablet:top-[calc(var(--header-height))] xl:hidden"
5
+ >
6
+ <!-- Always-mounted hidden container — keeps scrollspy alive regardless of popover state -->
7
+ <div
8
+ ref="spyRoot"
9
+ aria-hidden="true"
10
+ class="pointer-events-none absolute size-0 overflow-hidden opacity-0"
11
+ >
12
+ <DocsToclink :links="toc!.links" />
13
+ </div>
14
+
15
+ <UiPopover v-model:open="isOpen">
16
+ <UiPopoverTrigger as-child>
17
+ <button
18
+ class="flex w-full cursor-pointer items-center gap-2.5 border-b bg-background/95 px-4 backdrop-blur-sm transition-colors hover:bg-muted/50"
19
+ :class="isDashed ? 'border-dashed' : ''"
20
+ style="height: 2.5rem"
21
+ >
22
+ <!-- Scroll progress ring -->
23
+ <svg
24
+ width="18"
25
+ height="18"
26
+ viewBox="0 0 18 18"
27
+ class="shrink-0 -rotate-90"
28
+ aria-hidden="true"
29
+ >
30
+ <circle
31
+ cx="9"
32
+ cy="9"
33
+ r="7"
34
+ fill="none"
35
+ stroke="currentColor"
36
+ stroke-width="2"
37
+ class="text-border"
38
+ />
39
+ <circle
40
+ cx="9"
41
+ cy="9"
42
+ r="7"
43
+ fill="none"
44
+ :stroke-dasharray="circumference"
45
+ :stroke-dashoffset="circumference - scrollProgress * circumference"
46
+ stroke-width="2"
47
+ stroke-linecap="round"
48
+ stroke="currentColor"
49
+ class="text-primary transition-[stroke-dashoffset] duration-150 ease-out"
50
+ />
51
+ </svg>
52
+
53
+ <span class="flex-1 truncate text-left text-sm">
54
+ <span v-if="activeHeadingText" class="font-medium text-foreground">
55
+ {{ activeHeadingText }}
56
+ </span>
57
+ <span v-else class="text-muted-foreground">On this page</span>
58
+ </span>
59
+
60
+ <Icon
61
+ name="lucide:chevron-down"
62
+ class="size-4 shrink-0 text-muted-foreground transition-transform duration-200"
63
+ :class="{ 'rotate-180': isOpen }"
64
+ />
65
+ </button>
66
+ </UiPopoverTrigger>
67
+
68
+ <UiPopoverContent
69
+ align="start"
70
+ side="bottom"
71
+ :side-offset="0"
72
+ :collision-padding="0"
73
+ :avoid-collisions="false"
74
+ class="w-(--reka-popover-trigger-width) rounded-none border-x-0 border-t-0 p-0 shadow-sm"
75
+ translucent
76
+ :class="isDashed ? 'border-dashed' : ''"
77
+ >
78
+ <!-- Intercept link clicks to close the popover -->
79
+ <div @click.capture="onContentClick">
80
+ <UiScrollArea>
81
+ <div class="max-h-[50dvh] p-4">
82
+ <UiScrollspy smooth mode="multiple" :offset="120">
83
+ <DocsTocRail>
84
+ <DocsToclink :links="toc!.links" />
85
+ </DocsTocRail>
86
+ </UiScrollspy>
87
+ </div>
88
+ </UiScrollArea>
89
+ </div>
90
+ </UiPopoverContent>
91
+ </UiPopover>
92
+ </div>
93
+ </template>
94
+
95
+ <script lang="ts" setup>
96
+ type TocLink = {
97
+ text: string;
98
+ id: string;
99
+ depth: number;
100
+ children?: TocLink[];
101
+ };
102
+
103
+ const { page } = await useDocPage();
104
+ const { isDashed } = useDocd();
105
+
106
+ const toc = computed(() => page.value?.body?.toc as { links: TocLink[] } | undefined);
107
+ const hasToc = computed(() => !!toc.value?.links?.length && !page.value?.hideToc);
108
+
109
+ // --- Hidden, always-mounted scrollspy root so activeId persists when popover is closed ---
110
+ const spyRoot = ref<HTMLElement | null>(null);
111
+ const { activeId } = useScrollspy({ root: spyRoot, offset: 120, history: false, mode: "single" });
112
+
113
+ // Flatten toc links for lookup
114
+ function flattenLinks(links: TocLink[]): TocLink[] {
115
+ return links.flatMap((l) => [l, ...(l.children ? flattenLinks(l.children) : [])]);
116
+ }
117
+
118
+ const activeHeadingText = computed(() => {
119
+ if (!toc.value?.links) return null;
120
+ const flat = flattenLinks(toc.value.links);
121
+ // Fall back to the first heading when the scrollspy hasn't fired yet
122
+ // (hard reload at top of page, or immediately after navigation).
123
+ const id = activeId.value ?? flat[0]?.id;
124
+ return flat.find((l) => l.id === id)?.text ?? null;
125
+ });
126
+
127
+ // --- Scroll progress ---
128
+ const scrollProgress = ref(0);
129
+ const circumference = 2 * Math.PI * 7;
130
+
131
+ if (import.meta.client) {
132
+ const updateProgress = () => {
133
+ const docH = document.documentElement.scrollHeight;
134
+ const winH = window.innerHeight;
135
+ const max = docH - winH;
136
+ scrollProgress.value = max > 0 ? Math.min(1, window.scrollY / max) : 0;
137
+ };
138
+
139
+ useEventListener(window, "scroll", updateProgress, { passive: true });
140
+ onMounted(updateProgress);
141
+ }
142
+
143
+ // --- Popover state ---
144
+ const isOpen = ref(false);
145
+
146
+ // Reset on navigation
147
+ const route = useRoute();
148
+ watch(
149
+ () => route.path,
150
+ () => {
151
+ isOpen.value = false;
152
+ scrollProgress.value = 0;
153
+ }
154
+ );
155
+
156
+ function onContentClick(e: MouseEvent) {
157
+ const target = e.target as HTMLElement;
158
+ if (target.closest("a")) {
159
+ isOpen.value = false;
160
+ }
161
+ }
162
+ </script>
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <aside
3
+ v-if="toc && toc.links && toc.links.length && !page?.hideToc"
4
+ class="sticky top-14 z-20 hidden h-[calc(100dvh-calc(var(--header-height)+20px))] xl:block"
5
+ >
6
+ <UiScrollArea type="auto" class="h-full">
7
+ <div class="flex flex-col gap-5 p-5">
8
+ <div class="flex items-center gap-2.5 text-muted-foreground">
9
+ <Icon :name="tocIcon" class="size-4" />
10
+ <p class="text-sm font-semibold">{{ tocTitle }}</p>
11
+ </div>
12
+ <UiScrollspy smooth mode="multiple" :offset="80">
13
+ <DocsTocRail>
14
+ <DocsToclink :links="toc.links" />
15
+ </DocsTocRail>
16
+ </UiScrollspy>
17
+
18
+ <p class="text-sm font-semibold">Extra stuff</p>
19
+ <DocsExtraStuff />
20
+ </div>
21
+ </UiScrollArea>
22
+ </aside>
23
+ </template>
24
+
25
+ <script lang="ts" setup>
26
+ const { page } = await useDocPage();
27
+ const { tocTitle, tocIcon } = useDocd();
28
+
29
+ const toc = computed(() => {
30
+ if (!page.value) return;
31
+ return page.value?.body?.toc;
32
+ });
33
+ </script>
@@ -0,0 +1,90 @@
1
+ import { Comment, computed, Fragment, getCurrentInstance, isVNode } from "vue";
2
+ import type { Slots, VNode, VNodeArrayChildren } from "vue";
3
+
4
+ type SlotMetaContext = {
5
+ vnode: VNode;
6
+ index: number;
7
+ props: Record<string, any>;
8
+ };
9
+
10
+ export type SlotItem<TMeta = Record<string, any>> = {
11
+ vnode: VNode;
12
+ index: number;
13
+ key: VNode["key"];
14
+ props: Record<string, any>;
15
+ meta: TMeta;
16
+ };
17
+
18
+ export type UseDefaultSlotItemsOptions<TMeta> = {
19
+ slots?: Slots;
20
+ filter?: (ctx: SlotMetaContext) => boolean;
21
+ mapMeta?: (ctx: SlotMetaContext) => TMeta;
22
+ };
23
+
24
+ /**
25
+ * Composable to extract and manage items from the default slot
26
+ */
27
+ export function useDefaultSlotItems<TMeta = Record<string, any>>(
28
+ options: UseDefaultSlotItemsOptions<TMeta> = {}
29
+ ) {
30
+ const instance = getCurrentInstance();
31
+ const slots = options.slots ?? instance?.slots;
32
+
33
+ const items = computed<SlotItem<TMeta>[]>(() => {
34
+ const rawChildren = slots?.default?.() ?? [];
35
+ const nodes = flattenVNodes(rawChildren);
36
+
37
+ return nodes
38
+ .map((vnode, index) => {
39
+ const props = (vnode.props ?? {}) as Record<string, any>;
40
+ const ctx: SlotMetaContext = { vnode, index, props };
41
+
42
+ if (options.filter && !options.filter(ctx)) {
43
+ return undefined;
44
+ }
45
+
46
+ return {
47
+ vnode,
48
+ index,
49
+ key: vnode.key ?? index,
50
+ props,
51
+ meta: options.mapMeta ? options.mapMeta(ctx) : (props as TMeta),
52
+ };
53
+ })
54
+ .filter(Boolean) as SlotItem<TMeta>[];
55
+ });
56
+
57
+ return {
58
+ items,
59
+ first: computed(() => items.value[0]),
60
+ find: (predicate: (item: SlotItem<TMeta>) => boolean) => items.value.find(predicate),
61
+ };
62
+ }
63
+
64
+ function flattenVNodes(children: VNodeArrayChildren, acc: VNode[] = []): VNode[] {
65
+ children.forEach((child) => {
66
+ if (!child) return;
67
+
68
+ if (Array.isArray(child)) {
69
+ flattenVNodes(child, acc);
70
+ return;
71
+ }
72
+
73
+ if (!isVNode(child)) {
74
+ return;
75
+ }
76
+
77
+ if (child.type === Comment) {
78
+ return;
79
+ }
80
+
81
+ if (child.type === Fragment && Array.isArray(child.children)) {
82
+ flattenVNodes(child.children as VNodeArrayChildren, acc);
83
+ return;
84
+ }
85
+
86
+ acc.push(child);
87
+ });
88
+
89
+ return acc;
90
+ }
@@ -0,0 +1,66 @@
1
+ import type { Collections, ContentNavigationItem } from "@nuxt/content";
2
+ import { findPageHeadline } from "@nuxt/content/utils";
3
+ import { kebabCase } from "lodash-es";
4
+
5
+ export const useSearchModal = () => useState("docSearchModal", () => false);
6
+
7
+ export const useDocPage = async () => {
8
+ const route = useRoute();
9
+ const isLandingRoute = route.path === "/";
10
+
11
+ const [{ data: page }, { data: surround }, { data: navigation }] = await Promise.all([
12
+ useAsyncData(
13
+ kebabCase(route.path),
14
+ () =>
15
+ isLandingRoute
16
+ ? queryCollection("landing" as keyof Collections)
17
+ .path(route.path)
18
+ .first()
19
+ : queryCollection("docs" as keyof Collections)
20
+ .path(route.path)
21
+ .first(),
22
+ { watch: [() => route.path] }
23
+ ),
24
+ useAsyncData(
25
+ `${kebabCase(route.path)}-surround`,
26
+ () => {
27
+ if (isLandingRoute) {
28
+ return Promise.resolve(null);
29
+ }
30
+
31
+ return queryCollectionItemSurroundings("docs" as keyof Collections, route.path, {
32
+ fields: ["title", "description", "path"],
33
+ }).where("path", "NOT LIKE", "%/.navigation");
34
+ },
35
+ { watch: [() => route.path] }
36
+ ),
37
+ useAsyncData(
38
+ () => `navigation_docs`,
39
+ () => queryCollectionNavigation("docs" as keyof Collections),
40
+ {
41
+ watch: [() => route.path],
42
+ transform: (data: ContentNavigationItem[]) =>
43
+ data.find((item) => item.path === "/docs")?.children || data,
44
+ }
45
+ ),
46
+ ]);
47
+
48
+ /**
49
+ * The headline for the current page, derived from the navigation structure.
50
+ */
51
+ const headline = ref(findPageHeadline(navigation?.value, page.value?.path));
52
+ /**
53
+ * The breadcrumbs for the current page, derived from the navigation structure.
54
+ */
55
+ const breadcrumbs = computed(() =>
56
+ findPageBreadcrumbs(navigation?.value, page.value?.path || "")
57
+ );
58
+ /**
59
+ * The previous and next pages in the collection, if available.
60
+ */
61
+ const prev = computed(() => surround.value?.[0]);
62
+ /** The next page in the collection, if available. */
63
+ const next = computed(() => surround.value?.[1]);
64
+
65
+ return { page, surround, navigation, headline, breadcrumbs, prev, next };
66
+ };
@@ -0,0 +1,23 @@
1
+ export interface DocSearchSection {
2
+ id: string;
3
+ title: string;
4
+ titles: string[];
5
+ level: number;
6
+ content: string;
7
+ }
8
+
9
+ const SEARCH_SECTION_IGNORED_TAGS = ["code", "pre"];
10
+
11
+ export function useDocSearchSections() {
12
+ return useLazyAsyncData(
13
+ "docd-search-sections",
14
+ () =>
15
+ queryCollectionSearchSections("docs" as never, {
16
+ ignoredTags: SEARCH_SECTION_IGNORED_TAGS,
17
+ }) as Promise<DocSearchSection[]>,
18
+ {
19
+ server: false,
20
+ default: () => [],
21
+ }
22
+ );
23
+ }
@@ -0,0 +1,107 @@
1
+ import type { DocdConfig, DocdGithubConfig } from "../app.config";
2
+ import {
3
+ ANIMATE_IDENTITY,
4
+ transitionPresets,
5
+ type DocdTransitionName,
6
+ type TransitionStates,
7
+ } from "../transitions/presets";
8
+
9
+ export function useDocd() {
10
+ const appConfig = useAppConfig();
11
+ const route = useRoute();
12
+ const direction = useState<"forward" | "back">("nav-direction", () => "forward");
13
+
14
+ /* =====================================
15
+ = UI =
16
+ ===================================== */
17
+ const borderType = useUIConfig("borderType");
18
+ const isDashed = computed(() => borderType.value === "dashed");
19
+ const extraLinks = useUIConfig("extraLinks");
20
+ const github = computed(
21
+ () => ((appConfig.docd as DocdConfig | undefined)?.github ?? {}) as DocdGithubConfig
22
+ );
23
+
24
+ /* =====================================
25
+ = Footer =
26
+ ===================================== */
27
+ const footer = useUIConfig("footer");
28
+ const hideThemeCustomizer = computed(() => footer.value?.hideThemeCustomizer ?? false);
29
+ const hideLightDarkToggle = computed(() => footer.value?.hideLightDarkToggle ?? false);
30
+
31
+ /* =====================================
32
+ = Header =
33
+ ===================================== */
34
+ const header = useUIConfig("header");
35
+ const headerTitle = computed(() => header.value.title ?? "");
36
+ const hideSearch = computed(() => header.value.hideSearch ?? false);
37
+
38
+ /* =====================================
39
+ = Logo =
40
+ ===================================== */
41
+ const logo = computed(() => header.value.logo);
42
+ const lightLogo = computed(() => logo.value?.light);
43
+ const darkLogo = computed(() => logo.value?.dark);
44
+ const logoAlt = computed(() => logo.value?.alt ?? "Logo");
45
+ const logoClasses = computed(() => logo.value?.classes ?? "");
46
+ const logoUrl = computed(() => logo.value?.url ?? "/");
47
+
48
+ /* =====================================
49
+ = ToC =
50
+ ===================================== */
51
+ const toc = useUIConfig("toc");
52
+ const tocTitle = computed(() => toc.value.title ?? "On this page");
53
+ const tocIcon = computed(() => toc.value.icon ?? "lucide:list");
54
+
55
+ /* =====================================
56
+ = Transition =
57
+ ===================================== */
58
+ const transitionConfig = useUIConfig("transition");
59
+
60
+ const pageMeta = computed(() => route.meta?.pageTransition as DocdTransitionName | undefined);
61
+
62
+ const activeName = computed<DocdTransitionName>(
63
+ () => pageMeta.value ?? (transitionConfig.value.name as DocdTransitionName) ?? "fade"
64
+ );
65
+
66
+ const preset = computed<TransitionStates>(() => {
67
+ const base = transitionPresets[activeName.value] ?? transitionPresets.rightToLeft;
68
+ if (direction.value === "back") {
69
+ return { initial: base.exit, exit: base.initial };
70
+ }
71
+ return base;
72
+ });
73
+
74
+ const duration = computed(() => transitionConfig.value.duration ?? 0.35);
75
+ const easing = computed(() => transitionConfig.value.easing ?? "easeOut");
76
+
77
+ return {
78
+ // ui
79
+ header,
80
+ headerTitle,
81
+ borderType,
82
+ isDashed,
83
+ extraLinks,
84
+ github,
85
+ hideSearch,
86
+ // transition
87
+ activeName,
88
+ preset,
89
+ animate: ANIMATE_IDENTITY,
90
+ duration,
91
+ easing,
92
+ // logo
93
+ logo,
94
+ lightLogo,
95
+ darkLogo,
96
+ logoAlt,
97
+ logoClasses,
98
+ logoUrl,
99
+ // ToC
100
+ toc,
101
+ tocTitle,
102
+ tocIcon,
103
+ // footer
104
+ hideThemeCustomizer,
105
+ hideLightDarkToggle,
106
+ };
107
+ }