@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,98 @@
1
+ <template>
2
+ <UiTooltip>
3
+ <UiTooltipTrigger as-child>
4
+ <button
5
+ ref="toggler"
6
+ :class="
7
+ buttonStyles({
8
+ variant: 'ghost',
9
+ size: 'icon-sm',
10
+ })
11
+ "
12
+ @click="toggleTheme"
13
+ >
14
+ <ClientOnly>
15
+ <template #fallback>
16
+ <Icon name="svg-spinners:180-ring-with-bg" />
17
+ </template>
18
+ <Icon :name="iconName" />
19
+ </ClientOnly>
20
+ </button>
21
+ </UiTooltipTrigger>
22
+ <UiTooltipContent> Toggle theme </UiTooltipContent>
23
+ </UiTooltip>
24
+ </template>
25
+ <script lang="ts" setup>
26
+ import { buttonStyles } from "../Ui/Button.vue";
27
+
28
+ const colorMode = useColorMode();
29
+ const buttonRef = useTemplateRef("toggler");
30
+ const iconName = computed(() => {
31
+ switch (colorMode.value) {
32
+ case "light":
33
+ return "lucide:sun-medium";
34
+ case "dark":
35
+ return "lucide:moon";
36
+ default:
37
+ return "lucide:sun-medium";
38
+ }
39
+ });
40
+
41
+ const props = withDefaults(
42
+ defineProps<{
43
+ duration?: number;
44
+ }>(),
45
+ {
46
+ duration: 400,
47
+ }
48
+ );
49
+
50
+ const toggleTheme = () => {
51
+ const button = buttonRef.value;
52
+ if (!button) return;
53
+ const { top, left, width, height } = button.getBoundingClientRect();
54
+ const x = left + width / 2;
55
+ const y = top + height / 2;
56
+ const viewportWidth = window.visualViewport?.width ?? window.innerWidth;
57
+ const viewportHeight = window.visualViewport?.height ?? window.innerHeight;
58
+ const maxRadius = Math.hypot(Math.max(x, viewportWidth - x), Math.max(y, viewportHeight - y));
59
+ const newTheme = colorMode.value === "light" ? "dark" : "light";
60
+ const applyTheme = () => {
61
+ document.documentElement.classList.toggle("dark", newTheme === "dark");
62
+ colorMode.preference = newTheme;
63
+ };
64
+ if (typeof document.startViewTransition !== "function") {
65
+ applyTheme();
66
+ return;
67
+ }
68
+ const transition = document.startViewTransition(applyTheme);
69
+ const ready = transition?.ready;
70
+ if (ready && typeof ready.then === "function") {
71
+ ready.then(() => {
72
+ document.documentElement.animate(
73
+ {
74
+ clipPath: [`circle(0px at ${x}px ${y}px)`, `circle(${maxRadius}px at ${x}px ${y}px)`],
75
+ },
76
+ {
77
+ duration: props.duration,
78
+ easing: "ease-in-out",
79
+ pseudoElement: "::view-transition-new(root)",
80
+ }
81
+ );
82
+ });
83
+ }
84
+ };
85
+ </script>
86
+ <style>
87
+ ::view-transition-old(root),
88
+ ::view-transition-new(root) {
89
+ animation: none;
90
+ mix-blend-mode: normal;
91
+ }
92
+ ::view-transition-new(root) {
93
+ z-index: 9999;
94
+ }
95
+ ::view-transition-old(root) {
96
+ z-index: 1;
97
+ }
98
+ </style>
@@ -0,0 +1,162 @@
1
+ <template>
2
+ <div ref="wrap" class="relative pl-7">
3
+ <DocsZigZagRail
4
+ v-if="railHeight > 0 && pathD"
5
+ :height="railHeight"
6
+ :path-d="pathD"
7
+ :segments="segments"
8
+ :width="WIDTH"
9
+ :dashed="isDashed"
10
+ />
11
+ <slot />
12
+ </div>
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ type RailSegment = {
17
+ top: number;
18
+ height: number;
19
+ };
20
+
21
+ const { isDashed } = useDocd();
22
+ const wrap = ref<HTMLElement | null>(null);
23
+
24
+ const railHeight = ref(0);
25
+ const segments = ref<RailSegment[]>([]);
26
+ const pathD = ref("");
27
+
28
+ const WIDTH = 20;
29
+ const X_OUTER = 1;
30
+ const X_INNER_MAX = WIDTH - 1;
31
+
32
+ const rafMeasure = () => requestAnimationFrame(measure);
33
+
34
+ const measure = () => {
35
+ const el = wrap.value;
36
+ if (!el) return;
37
+
38
+ const links = Array.from(el.querySelectorAll<HTMLElement>('[data-toc-link="true"]'));
39
+ if (!links.length) {
40
+ railHeight.value = 0;
41
+ segments.value = [];
42
+ pathD.value = "";
43
+ return;
44
+ }
45
+
46
+ const last = links[links.length - 1]!;
47
+ railHeight.value = Math.ceil(last.offsetTop + last.offsetHeight);
48
+ pathD.value = buildDepthPath(links, railHeight.value);
49
+ segments.value = buildSegments(links);
50
+ };
51
+
52
+ function buildSegments(links: HTMLElement[]) {
53
+ const nextSegments: RailSegment[] = [];
54
+
55
+ let rangeStart = -1;
56
+ let rangeEnd = -1;
57
+
58
+ const pushRange = () => {
59
+ if (rangeStart === -1 || rangeEnd === -1) return;
60
+
61
+ const startEl = links[rangeStart]!;
62
+ const endEl = links[rangeEnd]!;
63
+ const top = Math.max(0, startEl.offsetTop);
64
+ const bottom = endEl.offsetTop + endEl.offsetHeight;
65
+
66
+ nextSegments.push({
67
+ top,
68
+ height: Math.max(0, bottom - top),
69
+ });
70
+
71
+ rangeStart = -1;
72
+ rangeEnd = -1;
73
+ };
74
+
75
+ links.forEach((link, index) => {
76
+ const isActive = link.getAttribute("data-active") === "true";
77
+ if (!isActive) {
78
+ pushRange();
79
+ return;
80
+ }
81
+
82
+ if (rangeStart === -1) {
83
+ rangeStart = index;
84
+ }
85
+
86
+ rangeEnd = index;
87
+ });
88
+
89
+ pushRange();
90
+
91
+ return nextSegments;
92
+ }
93
+
94
+ function buildDepthPath(els: HTMLElement[], height: number) {
95
+ const items = els.map((node) => {
96
+ const top = Math.round(node.offsetTop);
97
+ const h = Math.round(node.offsetHeight || 0);
98
+ const y = top + Math.round(h / 2);
99
+ return { y, depth: Number(node.dataset.depth ?? "0") };
100
+ });
101
+
102
+ const depths = items.map((i) => i.depth);
103
+ const baseDepth = Math.min(...depths);
104
+ const maxDepth = Math.max(...depths);
105
+ const levels = Math.max(1, maxDepth - baseDepth);
106
+
107
+ const indentStep = (X_INNER_MAX - X_OUTER) / levels;
108
+
109
+ const xForDepth = (depth: number) => {
110
+ const idx = Math.max(0, depth - baseDepth);
111
+ const x = X_OUTER + idx * indentStep;
112
+ return Math.round(Math.min(X_INNER_MAX, Math.max(X_OUTER, x)));
113
+ };
114
+
115
+ const CURVE_SPAN = 8;
116
+
117
+ const clamp = (n: number, min: number, max: number) => Math.min(max, Math.max(min, n));
118
+
119
+ let x = xForDepth(items[0]?.depth ?? baseDepth);
120
+ let yPrev = 0;
121
+
122
+ let d = `M ${x} 0`;
123
+
124
+ for (let i = 0; i < items.length; i++) {
125
+ const y = clamp(items[i]!.y, 0, height);
126
+ const xNext = xForDepth(items[i]!.depth);
127
+
128
+ if (xNext === x) {
129
+ if (y > yPrev) d += ` L ${x} ${y}`;
130
+ yPrev = Math.max(yPrev, y);
131
+ continue;
132
+ }
133
+
134
+ const y0 = clamp(y - CURVE_SPAN, yPrev, height);
135
+ const midY = (y0 + y) / 2;
136
+
137
+ if (y0 > yPrev) d += ` L ${x} ${y0}`;
138
+ d += ` C ${x} ${midY} ${xNext} ${midY} ${xNext} ${y}`;
139
+
140
+ x = xNext;
141
+ yPrev = y;
142
+ }
143
+
144
+ if (height > yPrev) d += ` L ${x} ${height}`;
145
+ return d;
146
+ }
147
+
148
+ onMounted(async () => {
149
+ await nextTick();
150
+ measure();
151
+ });
152
+
153
+ useMutationObserver(wrap, () => rafMeasure(), {
154
+ subtree: true,
155
+ attributes: true,
156
+ childList: true,
157
+ attributeFilter: ["data-active", "class", "style"],
158
+ });
159
+
160
+ useResizeObserver(wrap, () => rafMeasure());
161
+ useEventListener(window, "resize", () => rafMeasure());
162
+ </script>
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <ClientOnly>
3
+ <nav class="flex flex-col gap-2 text-sm">
4
+ <template v-for="(l, i) in links" :key="i">
5
+ <NuxtLink
6
+ v-if="!l.children"
7
+ data-toc-link="true"
8
+ :data-depth="l.depth"
9
+ class="data-[active=true]:decoration-muted-primary line-clamp-1 text-muted-foreground transition-all hover:text-primary data-[active=true]:text-primary"
10
+ :title="l.text"
11
+ :style="{ marginLeft: `${l.depth > 2 ? l.depth * 2 : 0}${isMobile ? 'px' : '%'}` }"
12
+ :to="`#${l.id}`"
13
+ :data-scrollspy-anchor="l.id"
14
+ >
15
+ {{ l.text }}
16
+ </NuxtLink>
17
+
18
+ <div v-else class="flex flex-col gap-2 text-sm">
19
+ <NuxtLink
20
+ data-toc-link="true"
21
+ :data-depth="l.depth"
22
+ class="data-[active=true]:decoration-muted-primary line-clamp-1 text-muted-foreground transition-all hover:text-primary data-[active=true]:text-primary"
23
+ :title="l.text"
24
+ :to="`#${l.id}`"
25
+ :data-scrollspy-anchor="l.id"
26
+ >
27
+ {{ l.text }}
28
+ </NuxtLink>
29
+
30
+ <DocsToclink :links="l.children" />
31
+ </div>
32
+ </template>
33
+ </nav>
34
+ </ClientOnly>
35
+ </template>
36
+
37
+ <script lang="ts" setup>
38
+ import { breakpointsTailwind } from "@vueuse/core";
39
+ type Toclink = {
40
+ text: string;
41
+ id: string;
42
+ depth: number;
43
+ children?: Toclink[];
44
+ };
45
+
46
+ defineProps<{
47
+ links: Toclink[];
48
+ }>();
49
+
50
+ const bp = useBreakpoints(breakpointsTailwind);
51
+ const isMobile = bp.smaller("xl");
52
+ </script>
@@ -0,0 +1,70 @@
1
+ <template>
2
+ <div class="absolute inset-s-0 top-0 rtl:-scale-x-100" :style="containerStyle">
3
+ <div class="absolute inset-0 bg-foreground/20" :style="trackMaskStyle" />
4
+
5
+ <div class="absolute inset-0" :style="segmentMaskStyle">
6
+ <div
7
+ v-for="(segment, index) in props.segments"
8
+ :key="index"
9
+ class="absolute w-full bg-primary transition-[top,height] duration-200 ease-out"
10
+ :style="{ top: `${segment.top}px`, height: `${segment.height}px` }"
11
+ />
12
+ </div>
13
+ </div>
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ import { computed } from "vue";
18
+
19
+ type RailSegment = {
20
+ top: number;
21
+ height: number;
22
+ };
23
+
24
+ const props = withDefaults(
25
+ defineProps<{
26
+ height: number;
27
+ pathD: string;
28
+ segments: RailSegment[];
29
+ width?: number;
30
+ dashed?: boolean;
31
+ }>(),
32
+ { width: 12, dashed: false }
33
+ );
34
+
35
+ function buildMaskUrl(dashed: boolean) {
36
+ const h = Math.max(1, Math.round(props.height));
37
+ const dashAttr = dashed ? ` stroke-dasharray="3 3"` : "";
38
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${props.width} ${h}">
39
+ <path d="${props.pathD}" stroke="black" stroke-width="1" fill="none"${dashAttr}/>
40
+ </svg>`;
41
+
42
+ const encoded = encodeURIComponent(svg)
43
+ .replace(/%20/g, " ")
44
+ .replace(/%3D/g, "=")
45
+ .replace(/%3A/g, ":")
46
+ .replace(/%2F/g, "/")
47
+ .replace(/%2C/g, ",");
48
+
49
+ return `url("data:image/svg+xml,${encoded}")`;
50
+ }
51
+
52
+ function maskStyle(dashed: boolean) {
53
+ return {
54
+ WebkitMaskImage: buildMaskUrl(dashed),
55
+ maskImage: buildMaskUrl(dashed),
56
+ WebkitMaskRepeat: "no-repeat",
57
+ maskRepeat: "no-repeat",
58
+ WebkitMaskSize: "100% 100%",
59
+ maskSize: "100% 100%",
60
+ };
61
+ }
62
+
63
+ const containerStyle = computed(() => ({
64
+ width: `${props.width}px`,
65
+ height: `${props.height}px`,
66
+ }));
67
+
68
+ const trackMaskStyle = computed(() => maskStyle(props.dashed));
69
+ const segmentMaskStyle = computed(() => maskStyle(false));
70
+ </script>
@@ -0,0 +1,222 @@
1
+ <template>
2
+ <div class="relative bg-background">
3
+ <!-- Navbar — outside overflow-hidden so sticky works -->
4
+ <nav
5
+ class="sticky top-4 isolate z-50 container mx-auto rounded-md border border-border/60 bg-background/80 shadow-xs backdrop-blur-md"
6
+ >
7
+ <div class="flex h-14 items-center gap-4">
8
+ <!-- Logo -->
9
+ <slot name="logo">
10
+ <NuxtLink :to="logoHref" class="flex shrink-0 items-center gap-2">
11
+ <slot name="logo-icon">
12
+ <template v-if="logoLight || logoDark">
13
+ <img
14
+ v-if="logoLight"
15
+ :src="logoLight"
16
+ alt="Light Logo"
17
+ class="h-6 w-auto rounded border border-border/50 dark:hidden"
18
+ />
19
+ <img
20
+ v-if="logoDark"
21
+ :src="logoDark"
22
+ alt="Dark Logo"
23
+ class="hidden h-6 w-auto rounded border border-border/50 dark:block"
24
+ />
25
+ </template>
26
+ </slot>
27
+ <span v-if="siteName" class="text-sm font-semibold">{{ siteName }}</span>
28
+ </NuxtLink>
29
+ </slot>
30
+
31
+ <!-- Nav links (desktop) -->
32
+ <div
33
+ v-if="navLinks?.length"
34
+ class="hidden flex-1 items-center justify-center gap-0.5 md:flex"
35
+ >
36
+ <template v-for="link in navLinks" :key="link.label">
37
+ <UiButton
38
+ variant="ghost"
39
+ size="sm"
40
+ :to="link.href"
41
+ class="gap-1 text-sm text-muted-foreground hover:text-foreground"
42
+ >
43
+ {{ link.label }}
44
+ <Icon
45
+ v-if="link.children?.length"
46
+ name="lucide:chevron-down"
47
+ class="size-3.5 opacity-60"
48
+ />
49
+ </UiButton>
50
+ </template>
51
+ </div>
52
+ <div v-else class="flex-1" />
53
+
54
+ <!-- Auth CTAs (desktop) -->
55
+ <div class="hidden shrink-0 items-center gap-2 md:flex">
56
+ <slot name="nav-actions">
57
+ <DocsThemeCustomizer />
58
+ <DocsSearchButton />
59
+ <UiButton v-if="logInHref" variant="ghost" size="sm" :to="logInHref">
60
+ {{ logInLabel }}
61
+ </UiButton>
62
+ <UiButton v-if="signUpHref" size="sm" :to="signUpHref">
63
+ {{ signUpLabel }}
64
+ </UiButton>
65
+ </slot>
66
+ </div>
67
+
68
+ <!-- Mobile actions -->
69
+ <slot name="nav-mobile-trigger">
70
+ <div class="ml-auto flex items-center gap-1 md:hidden">
71
+ <DocsThemeCustomizer />
72
+ <DocsSearchButton />
73
+ <UiButton
74
+ variant="ghost"
75
+ size="icon-sm"
76
+ class="size-7"
77
+ aria-label="Open navigation"
78
+ @click="mobileNavOpen = true"
79
+ >
80
+ <Icon name="lucide:menu" class="size-4" />
81
+ </UiButton>
82
+ </div>
83
+ </slot>
84
+ </div>
85
+ </nav>
86
+
87
+ <DocsMobileNav v-model:open="mobileNavOpen" />
88
+
89
+ <!-- Hero body — overflow-hidden scoped here so it doesn't trap sticky nav -->
90
+ <section class="relative overflow-hidden">
91
+ <UiBgPatternGrid
92
+ size="lg"
93
+ class="pointer-events-none absolute top-0 left-1/2 -translate-x-1/2"
94
+ />
95
+
96
+ <!-- Hero content -->
97
+ <UiContainer class="isolate pt-20 pb-12 text-center sm:pt-24">
98
+ <!-- Badge -->
99
+ <slot name="badge">
100
+ <div v-if="badge" class="mb-8 flex justify-center">
101
+ <NuxtLink :to="badge.href ?? '#'">
102
+ <UiBadgeGroup size="lg" :addon-text="badge.label" theme="modern" pulse>
103
+ {{ badge.text }}
104
+ </UiBadgeGroup>
105
+ </NuxtLink>
106
+ </div>
107
+ </slot>
108
+
109
+ <!-- Headline -->
110
+ <h1
111
+ class="mx-auto max-w-3xl text-4xl font-bold tracking-tight text-balance sm:text-5xl md:text-6xl"
112
+ >
113
+ <slot name="headline">{{ headline }}</slot>
114
+ </h1>
115
+
116
+ <!-- Description -->
117
+ <p
118
+ v-if="description"
119
+ class="mx-auto mt-5 max-w-3xl text-lg text-balance text-muted-foreground"
120
+ >
121
+ <slot name="description">{{ description }}</slot>
122
+ </p>
123
+
124
+ <!-- CTAs -->
125
+ <slot name="ctas">
126
+ <div
127
+ v-if="primaryCta || secondaryCta"
128
+ class="mt-10 flex flex-col items-stretch gap-3 sm:flex-row sm:items-center sm:justify-center"
129
+ >
130
+ <UiButton
131
+ v-if="primaryCta"
132
+ size="lg"
133
+ :to="primaryCta.href"
134
+ class="order-1 w-full sm:order-2 sm:w-auto"
135
+ >
136
+ <Icon v-if="primaryCta.icon" :name="primaryCta.icon" class="size-4" />
137
+ {{ primaryCta.label }}
138
+ </UiButton>
139
+ <UiButton
140
+ v-if="secondaryCta"
141
+ variant="secondary"
142
+ size="lg"
143
+ :to="secondaryCta.href"
144
+ class="order-2 w-full sm:order-1 sm:w-auto"
145
+ >
146
+ <Icon v-if="secondaryCta.icon" :name="secondaryCta.icon" class="size-4" />
147
+ {{ secondaryCta.label }}
148
+ </UiButton>
149
+ </div>
150
+ </slot>
151
+ </UiContainer>
152
+
153
+ <!-- Mockup frame -->
154
+ <div v-if="iframe" class="isolate pb-24">
155
+ <UiContainer class="bg-background">
156
+ <UiIframeLazy v-bind="iframe" />
157
+ </UiContainer>
158
+ </div>
159
+ </section>
160
+ </div>
161
+ </template>
162
+
163
+ <script lang="ts" setup>
164
+ import type { HTMLAttributes } from "vue";
165
+
166
+ export type HeroNavLink = {
167
+ label: string;
168
+ href?: string;
169
+ children?: HeroNavLink[];
170
+ };
171
+
172
+ export type HeroBadge = {
173
+ label: string;
174
+ text: string;
175
+ href?: string;
176
+ };
177
+
178
+ export type HeroCta = {
179
+ label: string;
180
+ href?: string;
181
+ icon?: string;
182
+ };
183
+
184
+ export type HeroIframeProps = {
185
+ src?: string;
186
+ class?: HTMLAttributes["class"];
187
+ iframeClass?: HTMLAttributes["class"];
188
+ loading?: "lazy" | "eager";
189
+ placeholder?: boolean;
190
+ rootMargin?: string;
191
+ threshold?: number | number[];
192
+ disableScroll?: boolean;
193
+ };
194
+
195
+ const mobileNavOpen = ref(false);
196
+
197
+ withDefaults(
198
+ defineProps<{
199
+ siteName?: string;
200
+ logoHref?: string;
201
+ logoLight?: string;
202
+ logoDark?: string;
203
+ navLinks?: HeroNavLink[];
204
+ logInHref?: string;
205
+ logInLabel?: string;
206
+ signUpHref?: string;
207
+ signUpLabel?: string;
208
+ badge?: HeroBadge;
209
+ headline?: string;
210
+ description?: string;
211
+ primaryCta?: HeroCta;
212
+ secondaryCta?: HeroCta;
213
+ iframe?: HeroIframeProps;
214
+ }>(),
215
+ {
216
+ logoHref: "/",
217
+ logInLabel: "Log in",
218
+ signUpLabel: "Sign up",
219
+ navLinks: () => [],
220
+ }
221
+ );
222
+ </script>
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <div class="not-prose @container mt-10 py-10">
3
+ <div class="grid grid-cols-1 gap-5 @min-[600px]:grid-cols-2">
4
+ <NuxtLink
5
+ v-if="prev"
6
+ :to="prev.path"
7
+ class="flex gap-4 rounded-md border p-5 transition hover:border-primary"
8
+ :class="isDashed ? 'border-dashed' : ''"
9
+ >
10
+ <UiFancyIcon
11
+ theme="modern-neue"
12
+ icon="lucide:arrow-left"
13
+ size="md"
14
+ class="text-foreground/80"
15
+ />
16
+ <div class="flex flex-col gap-1">
17
+ <p class="font-semibold lg:text-sm">{{ prev.title }}</p>
18
+ <p class="line-clamp-2 text-[15px] text-ellipsis text-muted-foreground lg:text-sm">
19
+ {{ prev.description }}
20
+ </p>
21
+ </div>
22
+ </NuxtLink>
23
+ <NuxtLink
24
+ v-if="next"
25
+ :to="next.path"
26
+ class="flex justify-between gap-4 rounded-md border p-5 transition hover:border-primary"
27
+ :class="isDashed ? 'border-dashed' : ''"
28
+ >
29
+ <div class="flex flex-col gap-1">
30
+ <p class="font-semibold lg:text-sm">{{ next.title }}</p>
31
+ <p class="line-clamp-2 text-[15px] text-ellipsis text-muted-foreground lg:text-sm">
32
+ {{ next.description }}
33
+ </p>
34
+ </div>
35
+ <UiFancyIcon
36
+ theme="modern-neue"
37
+ icon="lucide:arrow-right"
38
+ size="md"
39
+ class="text-foreground/80"
40
+ />
41
+ </NuxtLink>
42
+ </div>
43
+ </div>
44
+ </template>
45
+
46
+ <script lang="ts" setup>
47
+ const { prev, next } = await useDocPage();
48
+ const { isDashed } = useDocd();
49
+ </script>