@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,67 @@
1
+ <template>
2
+ <UiTooltip>
3
+ <UiTooltipTrigger as-child>
4
+ <UiButton
5
+ variant="ghost"
6
+ size="icon-xs"
7
+ :aria-label="copied ? 'Copied' : 'Copy to clipboard'"
8
+ :disabled="copied"
9
+ v-bind="$attrs"
10
+ @click="onCopy"
11
+ >
12
+ <AnimatePresence mode="wait">
13
+ <Motion
14
+ v-if="!copied"
15
+ as-child
16
+ as="svg"
17
+ :initial="{ opacity: 0, scale: 0.8 }"
18
+ :animate="{ opacity: 1, scale: 1 }"
19
+ :exit="{ opacity: 0, scale: 0.8 }"
20
+ :transition="{ duration: 0.2 }"
21
+ >
22
+ <Icon name="lucide:clipboard" aria-hidden="true" class="size-4 text-muted-foreground" />
23
+ </Motion>
24
+ <Motion
25
+ v-if="copied"
26
+ as-child
27
+ as="svg"
28
+ :initial="{ opacity: 0, scale: 0.8 }"
29
+ :animate="{ opacity: 1, scale: 1 }"
30
+ :exit="{ opacity: 0, scale: 0.8 }"
31
+ :transition="{ duration: 0.2 }"
32
+ >
33
+ <Icon name="lucide:check" aria-hidden="true" class="size-4 text-emerald-500" />
34
+ </Motion>
35
+ </AnimatePresence>
36
+ </UiButton>
37
+ </UiTooltipTrigger>
38
+ <UiTooltipContent>
39
+ <p>Copy to clipboard</p>
40
+ <UiTooltipArrow />
41
+ </UiTooltipContent>
42
+ </UiTooltip>
43
+ </template>
44
+
45
+ <script lang="ts" setup>
46
+ const props = defineProps<{
47
+ /**
48
+ * The code that should be copied
49
+ */
50
+ code?: string;
51
+ }>();
52
+
53
+ defineOptions({ inheritAttrs: false });
54
+
55
+ const { copied, copy } = useClipboard();
56
+
57
+ const emit = defineEmits<{
58
+ codeCopied: [];
59
+ }>();
60
+
61
+ const onCopy = () => {
62
+ if (!props.code) return;
63
+ copy(props.code);
64
+ useSonner("Copied to clipboard!");
65
+ emit("codeCopied");
66
+ };
67
+ </script>
@@ -0,0 +1,104 @@
1
+ <template>
2
+ <ClientOnly>
3
+ <render />
4
+ </ClientOnly>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { normalizeClass } from "vue";
9
+ import type { SetupContext } from "vue";
10
+
11
+ import { ProseTabs } from "#components";
12
+
13
+ const props = withDefaults(
14
+ defineProps<{
15
+ /**
16
+ * Whether the code group is in a stack layout
17
+ * @default false
18
+ */
19
+ inStack?: boolean;
20
+ /**
21
+ * Sync identifier for syncing with other tab groups
22
+ */
23
+ sync?: string;
24
+ /**
25
+ * Whether to add padding around the tabs
26
+ * @default true
27
+ */
28
+ padded?: boolean;
29
+ /**
30
+ * Disable the search functionality
31
+ * @default false
32
+ */
33
+ disableSearch?: boolean;
34
+ /**
35
+ * Placeholder text for the search input
36
+ * @default 'Search Tab...'
37
+ */
38
+ searchPlaceholder?: string;
39
+ /**
40
+ * Text to display when no tab is found
41
+ * @default 'No tab found.'
42
+ */
43
+ searchEmpty?: string;
44
+ /**
45
+ * Whether the combobox should take the full width of the container
46
+ * @default false
47
+ */
48
+ comboBoxFullWidth?: boolean;
49
+ /**
50
+ * Additional classes to add to the wrapper
51
+ */
52
+ class?: string;
53
+ }>(),
54
+ {
55
+ inStack: false,
56
+ padded: true,
57
+ disableSearch: false,
58
+ searchPlaceholder: "Search Tab...",
59
+ searchEmpty: "No tab found.",
60
+ comboBoxFullWidth: false,
61
+ }
62
+ );
63
+
64
+ const _slots: SetupContext["slots"] = useSlots();
65
+ const { items: slotItems } = useDefaultSlotItems({
66
+ slots: _slots,
67
+ mapMeta: ({ props, index }) => ({
68
+ id: props.filename ?? `tab-${index}`,
69
+ label: props.label ?? props.filename ?? `Tab ${index + 1}`,
70
+ icon: props.icon,
71
+ }),
72
+ });
73
+
74
+ const isMobile = useMediaQuery("(max-width: 640px)", {
75
+ ssrWidth: 639,
76
+ });
77
+
78
+ // Determine the variant based on number of tabs and screen size
79
+ // If there are 5 or more tabs, use the combobox variant
80
+ // If on mobile and more than 4 tabs, use the combobox variant
81
+ const variant = computed(() => {
82
+ if (slotItems.value.length >= 5) return "combobox";
83
+ if (isMobile.value && slotItems.value.length > 4) return "combobox";
84
+ return "card";
85
+ });
86
+
87
+ function render() {
88
+ return h(
89
+ ProseTabs,
90
+ {
91
+ variant: variant.value,
92
+ inStack: props.inStack,
93
+ sync: props.sync,
94
+ padded: props.padded,
95
+ disableSearch: props.disableSearch,
96
+ searchPlaceholder: props.searchPlaceholder,
97
+ searchEmpty: props.searchEmpty,
98
+ comboBoxFullWidth: props.comboBoxFullWidth,
99
+ class: normalizeClass(props.class) || undefined,
100
+ },
101
+ () => slotItems.value.map(({ vnode }) => vnode)
102
+ );
103
+ }
104
+ </script>
@@ -0,0 +1,277 @@
1
+ <template>
2
+ <define-tree-child v-slot="{ items: childItems, level }">
3
+ <li v-for="item in childItems" :key="item.path">
4
+ <TreeItem v-slot="{ isExpanded, isSelected }" as-child :level="level" :value="item">
5
+ <button
6
+ type="button"
7
+ :aria-expanded="item.children?.length ? isExpanded : undefined"
8
+ :aria-label="item.children?.length ? `${item.label} folder` : `${item.label} file`"
9
+ class="inline-flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none"
10
+ :class="[
11
+ isSelected || lastSelectedItem?.path === item.path ? 'bg-muted text-primary' : '',
12
+ ]"
13
+ >
14
+ <!-- Folder/File Icon -->
15
+ <img
16
+ v-if="item.children?.length"
17
+ :src="getMaterialFolderIcon(item.label, isExpanded)"
18
+ :alt="`${item.label} folder ${isExpanded ? 'expanded' : 'collapsed'}`"
19
+ class="size-4 shrink-0"
20
+ />
21
+ <img
22
+ v-else
23
+ :src="getMaterialFileIcon(item.label)"
24
+ :alt="`${item.label} file`"
25
+ class="size-4 shrink-0"
26
+ />
27
+
28
+ <!-- Label -->
29
+ <span class="truncate">{{ item.label }}</span>
30
+
31
+ <!-- Chevron for folders -->
32
+ <Icon
33
+ v-if="item.children?.length"
34
+ name="lucide:chevron-down"
35
+ class="ml-auto size-4 shrink-0 text-muted-foreground transition-transform"
36
+ :class="[isExpanded ? 'rotate-0' : '-rotate-90']"
37
+ aria-hidden="true"
38
+ />
39
+ </button>
40
+
41
+ <!-- Nested children -->
42
+ <ul v-if="item.children?.length && isExpanded" role="group" class="pl-4">
43
+ <TreeChild :items="item.children" :level="level + 1" />
44
+ </ul>
45
+ </TreeItem>
46
+ </li>
47
+ </define-tree-child>
48
+
49
+ <div
50
+ data-slot="prose-code-tree"
51
+ class="grid overflow-hidden rounded-lg border lg:grid-cols-3"
52
+ :class="props.class"
53
+ >
54
+ <nav class="not-prose h-full overflow-hidden px-2" aria-label="File tree navigation">
55
+ <template v-if="props.title">
56
+ <div class="p-2 py-4 text-sm font-medium">
57
+ <span>
58
+ {{ props.title }}
59
+ </span>
60
+ </div>
61
+ <UiGradientDivider />
62
+ </template>
63
+ <!-- Tree Navigation -->
64
+ <div class="max-h-[520px] overflow-y-auto py-2">
65
+ <TreeRoot
66
+ v-model="selectedItem"
67
+ :items="treeItems"
68
+ :get-key="(item) => item.path"
69
+ :default-expanded="expandedPaths"
70
+ >
71
+ <ul class="space-y-1" role="tree">
72
+ <TreeChild :items="treeItems" :level="0" />
73
+ </ul>
74
+ </TreeRoot>
75
+ </div>
76
+ </nav>
77
+
78
+ <!-- Content Display -->
79
+ <div
80
+ class="grid min-h-[240px] grid-cols-1 lg:col-span-2"
81
+ role="region"
82
+ aria-label="File content"
83
+ aria-live="polite"
84
+ >
85
+ <component
86
+ :is="lastSelectedItem?.component"
87
+ v-if="lastSelectedItem"
88
+ meta="noFormat"
89
+ class="mt-0 data-[slot=prose-pre-wrapper]:h-full data-[slot=prose-pre-wrapper]:rounded-none data-[slot=prose-pre-wrapper]:border-0 data-[slot=prose-pre-wrapper]:border-t lg:data-[slot=prose-pre-wrapper]:border-t-0 lg:data-[slot=prose-pre-wrapper]:border-l [&_pre]:h-[calc(100%-45px)] [&_pre]:flex-1 [&>*:first-child]:mt-0"
90
+ />
91
+ <div v-else class="flex h-full items-center justify-center text-sm text-muted-foreground">
92
+ Select a file to view its content
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </template>
97
+
98
+ <script lang="ts" setup>
99
+ import { getMaterialFileIcon, getMaterialFolderIcon } from "@baybreezy/file-extension-icon";
100
+ import type { HTMLAttributes } from "vue";
101
+
102
+ /**
103
+ * TreeItem represents a node in the file tree structure
104
+ */
105
+ type TreeItem = {
106
+ /** Display label for the node */
107
+ label: string;
108
+ /** Full path from root */
109
+ path: string;
110
+ /** Child nodes (only for directories) */
111
+ children?: TreeItem[];
112
+ /** Vue component to render when selected (only for files) */
113
+ component?: any;
114
+ };
115
+
116
+ const props = withDefaults(
117
+ defineProps<{
118
+ /**
119
+ * Default file path to select on mount
120
+ * @example "src/components/Button.vue"
121
+ */
122
+ defaultValue?: string;
123
+ /**
124
+ * Expand all directories by default instead of just the path to defaultValue
125
+ * @default false
126
+ */
127
+ expandAll?: boolean;
128
+ /**
129
+ * Optional title displayed above the file tree
130
+ * @example "Project Structure"
131
+ */
132
+ title?: string;
133
+ /**
134
+ * Additional CSS classes for the root container
135
+ */
136
+ class?: HTMLAttributes["class"];
137
+ }>(),
138
+ {
139
+ defaultValue: undefined,
140
+ expandAll: false,
141
+ }
142
+ );
143
+
144
+ const [DefineTreeChild, TreeChild] = createReusableTemplate<{
145
+ items: TreeItem[];
146
+ level: number;
147
+ }>();
148
+
149
+ const selectedItem = ref<TreeItem>();
150
+ const lastSelectedItem = ref<TreeItem>();
151
+ const rerenderCount = ref(0);
152
+
153
+ // Get slot items from default slot - these are the code blocks passed as children
154
+ const { items: slotItems } = useDefaultSlotItems({
155
+ slots: useSlots(),
156
+ mapMeta: ({ props, vnode }) => ({
157
+ path: props.path || props.filename || props.label,
158
+ icon: props.icon,
159
+ component: vnode,
160
+ }),
161
+ });
162
+
163
+ /**
164
+ * Build hierarchical tree structure from flat slot items
165
+ * Converts flat file paths like "src/components/Button.vue" into nested tree nodes
166
+ */
167
+ const treeItems = computed(() => {
168
+ // Trigger rerender when slots change
169
+ void rerenderCount.value;
170
+
171
+ const flatItems = slotItems.value.map((item) => ({
172
+ label: item.meta.path,
173
+ icon: item.meta.icon,
174
+ component: item.meta.component,
175
+ }));
176
+
177
+ return buildTree(flatItems);
178
+ });
179
+
180
+ /**
181
+ * Build tree structure from flat file paths
182
+ * Automatically creates parent directory nodes and sorts alphabetically
183
+ */
184
+ function buildTree(items: { label: string; icon?: string; component?: any }[]): TreeItem[] {
185
+ const map = new Map<string, TreeItem>();
186
+ const root: TreeItem[] = [];
187
+
188
+ items.forEach((item) => {
189
+ const parts = item.label.split("/");
190
+ let path = "";
191
+
192
+ parts.forEach((part, i) => {
193
+ path = path ? `${path}/${part}` : part;
194
+
195
+ if (!map.has(path)) {
196
+ const isLeaf = i === parts.length - 1;
197
+ const node: TreeItem = {
198
+ label: part,
199
+ path,
200
+ ...(isLeaf ? { component: item.component, icon: item.icon } : { children: [] }),
201
+ };
202
+
203
+ map.set(path, node);
204
+
205
+ if (i === 0) {
206
+ root.push(node);
207
+ } else {
208
+ const parentPath = parts.slice(0, i).join("/");
209
+ map.get(parentPath)?.children?.push(node);
210
+ }
211
+ }
212
+ });
213
+ });
214
+
215
+ // Sort function
216
+ const sort = (nodes: TreeItem[]): TreeItem[] =>
217
+ nodes
218
+ .sort((a, b) =>
219
+ !!a.children === !!b.children ? a.label.localeCompare(b.label) : a.children ? -1 : 1
220
+ )
221
+ .map((n) => ({ ...n, children: n.children && sort(n.children) }));
222
+
223
+ return sort(root);
224
+ }
225
+
226
+ /**
227
+ * Calculate which paths should be expanded based on the selected item or expandAll prop
228
+ */
229
+ const expandedPaths = computed(() => {
230
+ if (props.expandAll) {
231
+ const allPaths = new Set<string>();
232
+ slotItems.value.forEach((item) => {
233
+ const parts = item.meta.path.split("/");
234
+ for (let i = 1; i < parts.length; i++) {
235
+ allPaths.add(parts.slice(0, i).join("/"));
236
+ }
237
+ });
238
+ return Array.from(allPaths);
239
+ }
240
+
241
+ const path = selectedItem.value?.path || props.defaultValue;
242
+ if (!path) return [];
243
+
244
+ const parts = path.split("/");
245
+ return parts.slice(0, -1).map((_, i) => parts.slice(0, i + 1).join("/"));
246
+ });
247
+
248
+ /**
249
+ * Watch for selection changes and update lastSelectedItem if a file (not folder) is selected
250
+ */
251
+ watch(selectedItem, (newVal) => {
252
+ if (newVal && !newVal.children?.length) {
253
+ lastSelectedItem.value = newVal;
254
+ }
255
+ });
256
+
257
+ /**
258
+ * Set initial selection on mount if defaultValue is provided
259
+ */
260
+ onMounted(() => {
261
+ if (props.defaultValue) {
262
+ const findItem = (items: TreeItem[]): TreeItem | undefined => {
263
+ for (const item of items) {
264
+ if (item.path === props.defaultValue) return item;
265
+ if (item.children) {
266
+ const found = findItem(item.children);
267
+ if (found) return found;
268
+ }
269
+ }
270
+ };
271
+ const initial = findItem(treeItems.value);
272
+ if (initial) {
273
+ selectedItem.value = initial;
274
+ }
275
+ }
276
+ });
277
+ </script>
@@ -0,0 +1,64 @@
1
+ <template>
2
+ <UiCollapsible v-slot="{ open }" :default-open="defaultOpen" class="my-4">
3
+ <UiCollapsibleTrigger
4
+ class="group relative inline-flex items-center gap-1.5 rounded-xs text-sm text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-primary"
5
+ :class="triggerClass"
6
+ >
7
+ <ProseSmartIcon v-if="open ? openIcon : closeIcon" :name="open ? openIcon! : closeIcon!" />
8
+ <span>{{ open ? closeTitle : openTitle }}</span>
9
+ </UiCollapsibleTrigger>
10
+ <UiCollapsibleContent :class="contentClass" class="*:my-1.5 *:first:mt-2 *:last:mb-0">
11
+ <slot />
12
+ </UiCollapsibleContent>
13
+ </UiCollapsible>
14
+ </template>
15
+
16
+ <script lang="ts">
17
+ import type { HTMLAttributes } from "vue";
18
+
19
+ export type ProseCollapsibleProps = {
20
+ /**
21
+ * Icon to show when collapsible is open
22
+ * @default "lucide:minus"
23
+ */
24
+ openIcon?: string;
25
+ /**
26
+ * Icon to show when collapsible is closed
27
+ * @default "lucide:plus"
28
+ */
29
+ closeIcon?: string;
30
+ /**
31
+ * Text to show when collapsible is closed (trigger to open)
32
+ * @default "Show more"
33
+ */
34
+ openTitle?: string;
35
+ /**
36
+ * Text to show when collapsible is open (trigger to close)
37
+ * @default "Show less"
38
+ */
39
+ closeTitle?: string;
40
+ /**
41
+ * Whether the collapsible should be open by default
42
+ * @default false
43
+ */
44
+ defaultOpen?: boolean;
45
+ /**
46
+ * Additional CSS classes for the trigger button
47
+ */
48
+ triggerClass?: HTMLAttributes["class"];
49
+ /**
50
+ * Additional CSS classes for the content area
51
+ */
52
+ contentClass?: HTMLAttributes["class"];
53
+ };
54
+ </script>
55
+
56
+ <script lang="ts" setup>
57
+ withDefaults(defineProps<ProseCollapsibleProps>(), {
58
+ openIcon: "lucide:minus",
59
+ closeIcon: "lucide:plus",
60
+ openTitle: "Show more",
61
+ closeTitle: "Show less",
62
+ defaultOpen: false,
63
+ });
64
+ </script>
@@ -0,0 +1,58 @@
1
+ <template>
2
+ <div class="not-first:mt-6">
3
+ <div class="dark:hidden">
4
+ <ProseImg v-bind="forwarded" :src="srcLight" />
5
+ </div>
6
+ <div class="hidden dark:block">
7
+ <ProseImg v-bind="forwarded" :src="srcDark" />
8
+ </div>
9
+ </div>
10
+ </template>
11
+
12
+ <script lang="ts">
13
+ import type { HTMLAttributes } from "vue";
14
+
15
+ export type ProseColorModeImageProps = {
16
+ /**
17
+ * Source URL for light mode image
18
+ */
19
+ srcLight?: string;
20
+ /**
21
+ * Source URL for dark mode image
22
+ */
23
+ srcDark?: string;
24
+ /**
25
+ * Alt text for the image
26
+ */
27
+ alt?: string;
28
+ /**
29
+ * Width of the image
30
+ */
31
+ width?: string | number;
32
+ /**
33
+ * Height of the image
34
+ */
35
+ height?: string | number;
36
+ /**
37
+ * Whether the image should have a lifted style
38
+ */
39
+ lifted?: boolean;
40
+ /**
41
+ * Whether the image should have zoom effect on hover
42
+ * @default true
43
+ */
44
+ zoom?: boolean;
45
+ /**
46
+ * Additional CSS classes for the image element
47
+ */
48
+ class?: HTMLAttributes["class"];
49
+ };
50
+ </script>
51
+
52
+ <script setup lang="ts">
53
+ const props = withDefaults(defineProps<ProseColorModeImageProps>(), {
54
+ zoom: true,
55
+ lifted: false,
56
+ });
57
+ const forwarded = reactiveOmit(props, "srcLight", "srcDark");
58
+ </script>
@@ -0,0 +1,104 @@
1
+ <template>
2
+ <div v-if="resolvedMeta" class="not-prose">
3
+ <div v-if="title" class="mb-4">
4
+ <p class="text-lg font-semibold tracking-tight text-foreground">{{ title }}</p>
5
+ </div>
6
+
7
+ <ProseTabs v-if="sectionCount > 1" :variant="tabsVariant">
8
+ <div
9
+ v-for="section in visibleSections"
10
+ :key="section.kind"
11
+ :label="section.label"
12
+ :icon="section.icon"
13
+ class="[&>*:first-child]:mt-0"
14
+ >
15
+ <component :is="section.component" :items="section.items" :layout="layout" />
16
+ </div>
17
+ </ProseTabs>
18
+
19
+ <div v-else-if="visibleSections[0]" class="space-y-6">
20
+ <component
21
+ :is="visibleSections[0]?.component"
22
+ :items="visibleSections[0].items"
23
+ :layout="layout"
24
+ />
25
+ </div>
26
+
27
+ <p v-else class="text-sm text-muted-foreground">
28
+ No matching component API sections were found.
29
+ </p>
30
+ </div>
31
+
32
+ <p v-else class="not-prose text-sm text-muted-foreground">
33
+ No component metadata was found for <code>{{ path }}</code
34
+ >.
35
+ </p>
36
+ </template>
37
+
38
+ <script lang="ts">
39
+ import type { ComponentApiSectionKind } from "../../../../utils/component-api";
40
+ import type {
41
+ ProseComponentApiLayout,
42
+ ProseComponentMetaEntry,
43
+ } from "../../../../utils/generate-prose-component-meta";
44
+ import type { ProseTabsProps } from "./ProseTabs.global.vue";
45
+
46
+ export type ProseComponentApiProps = {
47
+ path?: string;
48
+ meta?: ProseComponentMetaEntry | null;
49
+ layout?: ProseComponentApiLayout;
50
+ sections?: ComponentApiSectionKind[] | string;
51
+ title?: string;
52
+ tabsVariant?: ProseTabsProps["variant"];
53
+ };
54
+ </script>
55
+
56
+ <script lang="ts" setup>
57
+ import { normalizeComponentApiSections } from "../../../../utils/component-api";
58
+
59
+ const props = withDefaults(defineProps<ProseComponentApiProps>(), {
60
+ layout: "table",
61
+ tabsVariant: "line",
62
+ });
63
+
64
+ const resolvedMeta = computed(() => props.meta ?? resolveProseComponentMeta(props.path));
65
+ const requestedSections = computed(() => normalizeComponentApiSections(props.sections));
66
+ const sectionDefinitions = {
67
+ props: {
68
+ label: "Props",
69
+ icon: "lucide:sliders-horizontal",
70
+ component: "ProseComponentProps",
71
+ },
72
+ slots: {
73
+ label: "Slots",
74
+ icon: "lucide:panel-top",
75
+ component: "ProseComponentSlots",
76
+ },
77
+ events: {
78
+ label: "Events",
79
+ icon: "lucide:zap",
80
+ component: "ProseComponentEvents",
81
+ },
82
+ exposed: {
83
+ label: "Exposed",
84
+ icon: "lucide:square-function",
85
+ component: "ProseComponentExposed",
86
+ },
87
+ } as const;
88
+
89
+ const visibleSections = computed(() => {
90
+ if (!resolvedMeta.value) {
91
+ return [];
92
+ }
93
+
94
+ return requestedSections.value
95
+ .map((kind) => ({
96
+ kind,
97
+ ...sectionDefinitions[kind],
98
+ items: resolvedMeta.value?.[kind] || [],
99
+ }))
100
+ .filter((section) => section.items.length);
101
+ });
102
+
103
+ const sectionCount = computed(() => visibleSections.value.length);
104
+ </script>
@@ -0,0 +1,27 @@
1
+ <template>
2
+ <ComponentApiSection kind="events" :items="resolvedItems" :layout="layout" />
3
+ </template>
4
+
5
+ <script lang="ts">
6
+ import type {
7
+ ProseComponentApiLayout,
8
+ ProseComponentMetaEntry,
9
+ ProseComponentMetaEvent,
10
+ } from "../../../../utils/generate-prose-component-meta";
11
+
12
+ export type ProseComponentEventsProps = {
13
+ path?: string;
14
+ meta?: ProseComponentMetaEntry | null;
15
+ items?: ProseComponentMetaEvent[];
16
+ layout?: ProseComponentApiLayout;
17
+ };
18
+ </script>
19
+
20
+ <script lang="ts" setup>
21
+ const props = withDefaults(defineProps<ProseComponentEventsProps>(), {
22
+ layout: "field",
23
+ });
24
+
25
+ const resolvedMeta = computed(() => props.meta ?? resolveProseComponentMeta(props.path));
26
+ const resolvedItems = computed(() => props.items ?? resolvedMeta.value?.events ?? []);
27
+ </script>