@blogmcp/mdx-render-vue 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/dist/_virtual/_plugin-vue_export-helper.js +10 -0
  2. package/dist/_virtual/constants.js +4 -0
  3. package/dist/_virtual/index.js +5 -0
  4. package/dist/_virtual/index2.js +4 -0
  5. package/dist/composables/index.d.ts +11 -0
  6. package/dist/composables/index.js +24 -0
  7. package/dist/composables/useAudioPlayer.d.ts +22 -0
  8. package/dist/composables/useAudioPlayer.js +58 -0
  9. package/dist/composables/useCallout.d.ts +43 -0
  10. package/dist/composables/useCallout.js +71 -0
  11. package/dist/composables/useCodeBlock.d.ts +23 -0
  12. package/dist/composables/useCodeBlock.js +67 -0
  13. package/dist/composables/useGallery.d.ts +37 -0
  14. package/dist/composables/useGallery.js +84 -0
  15. package/dist/composables/useMedia.d.ts +18 -0
  16. package/dist/composables/useMedia.js +9 -0
  17. package/dist/composables/useYouTube.d.ts +31 -0
  18. package/dist/composables/useYouTube.js +32 -0
  19. package/dist/edit/AlertEdit.vue.d.ts +12 -0
  20. package/dist/edit/AlertEdit.vue.js +4 -0
  21. package/dist/edit/AlertEdit.vue2.js +18 -0
  22. package/dist/edit/AudioPlayerEdit.vue.d.ts +3 -0
  23. package/dist/edit/AudioPlayerEdit.vue.js +117 -0
  24. package/dist/edit/AudioPlayerEdit.vue2.js +4 -0
  25. package/dist/edit/BadgeEdit.vue.d.ts +2 -0
  26. package/dist/edit/BadgeEdit.vue.js +4 -0
  27. package/dist/edit/BadgeEdit.vue2.js +13 -0
  28. package/dist/edit/ButtonEdit.vue.d.ts +2 -0
  29. package/dist/edit/ButtonEdit.vue.js +4 -0
  30. package/dist/edit/ButtonEdit.vue2.js +13 -0
  31. package/dist/edit/CalloutEdit.vue.d.ts +13 -0
  32. package/dist/edit/CalloutEdit.vue.js +85 -0
  33. package/dist/edit/CalloutEdit.vue2.js +4 -0
  34. package/dist/edit/CardEdit.vue.d.ts +12 -0
  35. package/dist/edit/CardEdit.vue.js +4 -0
  36. package/dist/edit/CardEdit.vue2.js +18 -0
  37. package/dist/edit/CodeBlockEdit.vue.d.ts +12 -0
  38. package/dist/edit/CodeBlockEdit.vue.js +7 -0
  39. package/dist/edit/CodeBlockEdit.vue2.js +89 -0
  40. package/dist/edit/GalleryEdit.vue.d.ts +3 -0
  41. package/dist/edit/GalleryEdit.vue.js +64 -0
  42. package/dist/edit/GalleryEdit.vue2.js +4 -0
  43. package/dist/edit/IconEdit.vue.d.ts +2 -0
  44. package/dist/edit/IconEdit.vue.js +4 -0
  45. package/dist/edit/IconEdit.vue2.js +13 -0
  46. package/dist/edit/TooltipEdit.vue.d.ts +16 -0
  47. package/dist/edit/TooltipEdit.vue.js +4 -0
  48. package/dist/edit/TooltipEdit.vue2.js +24 -0
  49. package/dist/edit/YouTubeEdit.vue.d.ts +3 -0
  50. package/dist/edit/YouTubeEdit.vue.js +41 -0
  51. package/dist/edit/YouTubeEdit.vue2.js +4 -0
  52. package/dist/edit/index.d.ts +29 -0
  53. package/dist/edit/index.js +35 -0
  54. package/dist/index.d.ts +12 -0
  55. package/dist/index.js +75 -0
  56. package/dist/mdx-render-vue.css +31 -0
  57. package/dist/node_modules/@ark-ui/vue/dist/_virtual/_rolldown/runtime.js +13 -0
  58. package/dist/node_modules/@ark-ui/vue/dist/components/factory.js +35 -0
  59. package/dist/node_modules/@ark-ui/vue/dist/components/presence/use-presence-context.js +6 -0
  60. package/dist/node_modules/@ark-ui/vue/dist/components/presence/use-presence.js +40 -0
  61. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-arrow-tip.js +5 -0
  62. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-arrow-tip.vue_vue_type_script_setup_true_lang.js +21 -0
  63. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-arrow.js +5 -0
  64. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-arrow.vue_vue_type_script_setup_true_lang.js +21 -0
  65. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-content.js +5 -0
  66. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-content.vue_vue_type_script_setup_true_lang.js +25 -0
  67. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-context.js +5 -0
  68. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-context.vue_vue_type_script_setup_true_lang.js +14 -0
  69. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-positioner.js +5 -0
  70. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-positioner.vue_vue_type_script_setup_true_lang.js +23 -0
  71. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-root-provider.js +5 -0
  72. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-root-provider.vue_vue_type_script_setup_true_lang.js +38 -0
  73. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-root.js +5 -0
  74. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-root.vue_vue_type_script_setup_true_lang.js +68 -0
  75. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-trigger.js +5 -0
  76. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-trigger.vue_vue_type_script_setup_true_lang.js +25 -0
  77. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip.js +22 -0
  78. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/use-tooltip-context.js +6 -0
  79. package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/use-tooltip.js +36 -0
  80. package/dist/node_modules/@ark-ui/vue/dist/providers/environment/use-environment-context.js +13 -0
  81. package/dist/node_modules/@ark-ui/vue/dist/providers/locale/use-locale-context.js +12 -0
  82. package/dist/node_modules/@ark-ui/vue/dist/utils/clean-props.js +8 -0
  83. package/dist/node_modules/@ark-ui/vue/dist/utils/create-context.js +14 -0
  84. package/dist/node_modules/@ark-ui/vue/dist/utils/dynamic.js +34 -0
  85. package/dist/node_modules/@ark-ui/vue/dist/utils/unref-element.js +8 -0
  86. package/dist/node_modules/@ark-ui/vue/dist/utils/use-forward-expose.js +46 -0
  87. package/dist/node_modules/@ark-ui/vue/dist/utils/use-render-strategy.js +5 -0
  88. package/dist/node_modules/@braintree/sanitize-url/dist/constants.js +20 -0
  89. package/dist/node_modules/@braintree/sanitize-url/dist/index.js +75 -0
  90. package/dist/node_modules/@floating-ui/core/dist/floating-ui.core.js +728 -0
  91. package/dist/node_modules/@floating-ui/dom/dist/floating-ui.dom.js +629 -0
  92. package/dist/node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.js +176 -0
  93. package/dist/node_modules/@floating-ui/utils/dist/floating-ui.utils.js +151 -0
  94. package/dist/node_modules/@iconify/vue/dist/iconify.js +1691 -0
  95. package/dist/node_modules/@zag-js/anatomy/dist/create-anatomy.js +29 -0
  96. package/dist/node_modules/@zag-js/core/dist/create-machine.js +28 -0
  97. package/dist/node_modules/@zag-js/core/dist/merge-props.js +55 -0
  98. package/dist/node_modules/@zag-js/core/dist/scope.js +20 -0
  99. package/dist/node_modules/@zag-js/core/dist/state.js +176 -0
  100. package/dist/node_modules/@zag-js/core/dist/types.js +11 -0
  101. package/dist/node_modules/@zag-js/dom-query/dist/chunk-QZ7TP4HQ.js +6 -0
  102. package/dist/node_modules/@zag-js/dom-query/dist/computed-style.js +11 -0
  103. package/dist/node_modules/@zag-js/dom-query/dist/event.js +37 -0
  104. package/dist/node_modules/@zag-js/dom-query/dist/node.js +64 -0
  105. package/dist/node_modules/@zag-js/dom-query/dist/overflow.js +28 -0
  106. package/dist/node_modules/@zag-js/dom-query/dist/platform.js +23 -0
  107. package/dist/node_modules/@zag-js/dom-query/dist/query.js +6 -0
  108. package/dist/node_modules/@zag-js/dom-query/dist/raf.js +52 -0
  109. package/dist/node_modules/@zag-js/dom-query/dist/set.js +22 -0
  110. package/dist/node_modules/@zag-js/dom-query/dist/shared.js +8 -0
  111. package/dist/node_modules/@zag-js/focus-visible/dist/index.js +159 -0
  112. package/dist/node_modules/@zag-js/popper/dist/get-anchor.js +39 -0
  113. package/dist/node_modules/@zag-js/popper/dist/get-placement.js +296 -0
  114. package/dist/node_modules/@zag-js/popper/dist/get-styles.js +47 -0
  115. package/dist/node_modules/@zag-js/popper/dist/middleware.js +79 -0
  116. package/dist/node_modules/@zag-js/popper/dist/placement.js +7 -0
  117. package/dist/node_modules/@zag-js/presence/dist/presence.connect.js +18 -0
  118. package/dist/node_modules/@zag-js/presence/dist/presence.machine.js +175 -0
  119. package/dist/node_modules/@zag-js/tooltip/dist/tooltip.anatomy.js +7 -0
  120. package/dist/node_modules/@zag-js/tooltip/dist/tooltip.connect.js +155 -0
  121. package/dist/node_modules/@zag-js/tooltip/dist/tooltip.dom.js +24 -0
  122. package/dist/node_modules/@zag-js/tooltip/dist/tooltip.machine.js +416 -0
  123. package/dist/node_modules/@zag-js/tooltip/dist/tooltip.store.js +9 -0
  124. package/dist/node_modules/@zag-js/types/dist/prop-types.js +14 -0
  125. package/dist/node_modules/@zag-js/utils/dist/array.js +7 -0
  126. package/dist/node_modules/@zag-js/utils/dist/equal.js +36 -0
  127. package/dist/node_modules/@zag-js/utils/dist/functions.js +11 -0
  128. package/dist/node_modules/@zag-js/utils/dist/guard.js +26 -0
  129. package/dist/node_modules/@zag-js/utils/dist/object.js +16 -0
  130. package/dist/node_modules/@zag-js/utils/dist/store.js +44 -0
  131. package/dist/node_modules/@zag-js/utils/dist/warning.js +31 -0
  132. package/dist/node_modules/@zag-js/vue/dist/bindable.js +48 -0
  133. package/dist/node_modules/@zag-js/vue/dist/machine.js +258 -0
  134. package/dist/node_modules/@zag-js/vue/dist/normalize-props.js +42 -0
  135. package/dist/node_modules/@zag-js/vue/dist/refs.js +14 -0
  136. package/dist/node_modules/@zag-js/vue/dist/track.js +22 -0
  137. package/dist/node_modules/dompurify/dist/purify.es.js +1190 -0
  138. package/dist/registry.d.ts +76 -0
  139. package/dist/registry.js +164 -0
  140. package/dist/render/alert/Alert.vue.d.ts +18 -0
  141. package/dist/render/alert/Alert.vue.js +4 -0
  142. package/dist/render/alert/Alert.vue2.js +43 -0
  143. package/dist/render/alert/index.d.ts +2 -0
  144. package/dist/render/audioplayer/AudioPlayerDefault.vue.d.ts +3 -0
  145. package/dist/render/audioplayer/AudioPlayerDefault.vue.js +116 -0
  146. package/dist/render/audioplayer/AudioPlayerDefault.vue2.js +4 -0
  147. package/dist/render/audioplayer/index.d.ts +5 -0
  148. package/dist/render/audioplayer/index.js +5 -0
  149. package/dist/render/badge/Badge.vue.d.ts +10 -0
  150. package/dist/render/badge/Badge.vue.js +4 -0
  151. package/dist/render/badge/Badge.vue2.js +28 -0
  152. package/dist/render/badge/index.d.ts +2 -0
  153. package/dist/render/button/Button.vue.d.ts +13 -0
  154. package/dist/render/button/Button.vue.js +4 -0
  155. package/dist/render/button/Button.vue2.js +42 -0
  156. package/dist/render/button/index.d.ts +2 -0
  157. package/dist/render/callout/CalloutDefault.vue.d.ts +13 -0
  158. package/dist/render/callout/CalloutDefault.vue.js +86 -0
  159. package/dist/render/callout/CalloutDefault.vue2.js +4 -0
  160. package/dist/render/callout/CalloutMinimal.vue.d.ts +13 -0
  161. package/dist/render/callout/CalloutMinimal.vue.js +27 -0
  162. package/dist/render/callout/CalloutMinimal.vue2.js +4 -0
  163. package/dist/render/callout/index.d.ts +6 -0
  164. package/dist/render/callout/index.js +7 -0
  165. package/dist/render/card/Card.vue.d.ts +15 -0
  166. package/dist/render/card/Card.vue.js +4 -0
  167. package/dist/render/card/Card.vue2.js +29 -0
  168. package/dist/render/card/index.d.ts +2 -0
  169. package/dist/render/codeblock/CodeBlockDefault.vue.d.ts +12 -0
  170. package/dist/render/codeblock/CodeBlockDefault.vue.js +7 -0
  171. package/dist/render/codeblock/CodeBlockDefault.vue2.js +92 -0
  172. package/dist/render/codeblock/index.d.ts +5 -0
  173. package/dist/render/codeblock/index.js +5 -0
  174. package/dist/render/gallery/GalleryGrid.vue.d.ts +3 -0
  175. package/dist/render/gallery/GalleryGrid.vue.js +125 -0
  176. package/dist/render/gallery/GalleryGrid.vue2.js +4 -0
  177. package/dist/render/gallery/GalleryLightbox.vue.d.ts +16 -0
  178. package/dist/render/gallery/GalleryLightbox.vue.js +7 -0
  179. package/dist/render/gallery/GalleryLightbox.vue2.js +170 -0
  180. package/dist/render/gallery/index.d.ts +6 -0
  181. package/dist/render/gallery/index.js +7 -0
  182. package/dist/render/icon/Icon.vue.d.ts +9 -0
  183. package/dist/render/icon/Icon.vue.js +4 -0
  184. package/dist/render/icon/Icon.vue2.js +25 -0
  185. package/dist/render/icon/index.d.ts +2 -0
  186. package/dist/render/index.d.ts +40 -0
  187. package/dist/render/index.js +63 -0
  188. package/dist/render/tooltip/Tooltip.vue.d.ts +18 -0
  189. package/dist/render/tooltip/Tooltip.vue.js +4 -0
  190. package/dist/render/tooltip/Tooltip.vue2.js +58 -0
  191. package/dist/render/tooltip/index.d.ts +2 -0
  192. package/dist/render/youtube/YouTubeDefault.vue.d.ts +3 -0
  193. package/dist/render/youtube/YouTubeDefault.vue.js +79 -0
  194. package/dist/render/youtube/YouTubeDefault.vue2.js +4 -0
  195. package/dist/render/youtube/index.d.ts +5 -0
  196. package/dist/render/youtube/index.js +5 -0
  197. package/dist/useAudioPlayer.d.ts +22 -0
  198. package/dist/useCallout.d.ts +46 -0
  199. package/dist/useCodeBlock.d.ts +25 -0
  200. package/dist/useGallery.d.ts +44 -0
  201. package/dist/useMedia.d.ts +16 -0
  202. package/dist/useYouTube.d.ts +32 -0
  203. package/package.json +125 -0
  204. package/src/composables/index.ts +16 -0
  205. package/src/composables/useAudioPlayer.ts +84 -0
  206. package/src/composables/useCallout.ts +103 -0
  207. package/src/composables/useCodeBlock.ts +90 -0
  208. package/src/composables/useGallery.ts +143 -0
  209. package/src/composables/useMedia.ts +23 -0
  210. package/src/composables/useYouTube.ts +58 -0
  211. package/src/edit/AlertEdit.vue +9 -0
  212. package/src/edit/AudioPlayerEdit.vue +92 -0
  213. package/src/edit/BadgeEdit.vue +7 -0
  214. package/src/edit/ButtonEdit.vue +10 -0
  215. package/src/edit/CalloutEdit.vue +107 -0
  216. package/src/edit/CardEdit.vue +9 -0
  217. package/src/edit/CodeBlockEdit.vue +76 -0
  218. package/src/edit/GalleryEdit.vue +65 -0
  219. package/src/edit/IconEdit.vue +7 -0
  220. package/src/edit/TooltipEdit.vue +15 -0
  221. package/src/edit/YouTubeEdit.vue +33 -0
  222. package/src/edit/index.ts +32 -0
  223. package/src/index.ts +62 -0
  224. package/src/registry.ts +272 -0
  225. package/src/render/alert/Alert.vue +32 -0
  226. package/src/render/alert/index.ts +2 -0
  227. package/src/render/audioplayer/AudioPlayerDefault.vue +89 -0
  228. package/src/render/audioplayer/index.ts +8 -0
  229. package/src/render/badge/Badge.vue +25 -0
  230. package/src/render/badge/index.ts +2 -0
  231. package/src/render/button/Button.vue +41 -0
  232. package/src/render/button/index.ts +2 -0
  233. package/src/render/callout/CalloutDefault.vue +101 -0
  234. package/src/render/callout/CalloutMinimal.vue +31 -0
  235. package/src/render/callout/index.ts +9 -0
  236. package/src/render/card/Card.vue +15 -0
  237. package/src/render/card/index.ts +2 -0
  238. package/src/render/codeblock/CodeBlockDefault.vue +76 -0
  239. package/src/render/codeblock/index.ts +8 -0
  240. package/src/render/gallery/GalleryGrid.vue +162 -0
  241. package/src/render/gallery/GalleryLightbox.vue +162 -0
  242. package/src/render/gallery/index.ts +9 -0
  243. package/src/render/icon/Icon.vue +22 -0
  244. package/src/render/icon/index.ts +2 -0
  245. package/src/render/index.ts +76 -0
  246. package/src/render/tooltip/Tooltip.vue +35 -0
  247. package/src/render/tooltip/index.ts +2 -0
  248. package/src/render/youtube/YouTubeDefault.vue +72 -0
  249. package/src/render/youtube/index.ts +8 -0
@@ -0,0 +1,44 @@
1
+ import { ComputedRef } from 'vue';
2
+ import { GalleryProps } from '@blogmcp/mdx-schema';
3
+ import { GalleryTileLayout } from '@blogmcp/mdx-schema/styles';
4
+ import { galleryTileLayout } from '@blogmcp/mdx-schema/styles';
5
+ import { MediaObject } from '@blogmcp/mdx-schema';
6
+
7
+ export { GalleryTileLayout }
8
+
9
+ export { galleryTileLayout }
10
+
11
+ /**
12
+ * Grid column class mappings
13
+ */
14
+ export declare const gridColsClasses: Record<number, string>;
15
+
16
+ /**
17
+ * Type guard for MediaObject
18
+ */
19
+ export declare function isMediaObject(value: unknown): value is MediaObject;
20
+
21
+ /**
22
+ * Check if a URL points to a video file based on extension
23
+ */
24
+ export declare function isVideoUrl(src: string): boolean;
25
+
26
+ /**
27
+ * Composable for Gallery component logic
28
+ */
29
+ export declare function useGallery(props: GalleryProps): {
30
+ effectiveColumns: ComputedRef<1 | 2 | 3 | 4>;
31
+ gridColsClass: ComputedRef<string>;
32
+ gapClass: ComputedRef<string>;
33
+ tileLayout: ComputedRef<GalleryTileLayout>;
34
+ getImageSrc: (index: number) => string;
35
+ getImageAlt: (index: number) => string;
36
+ getImageDescription: (index: number) => string | undefined;
37
+ getImageCaption: (index: number) => string | undefined;
38
+ isVideo: (index: number) => boolean;
39
+ getVideoPoster: (index: number) => string | undefined;
40
+ hasImages: ComputedRef<boolean>;
41
+ imageCount: ComputedRef<number>;
42
+ };
43
+
44
+ export { }
@@ -0,0 +1,16 @@
1
+ import { Component } from 'vue';
2
+ import { InjectionKey } from 'vue';
3
+
4
+ /**
5
+ * Injection key for the optimized image component.
6
+ * The host app should `provide(IMG_COMPONENT_KEY, NuxtImg)`.
7
+ */
8
+ export declare const IMG_COMPONENT_KEY: InjectionKey<Component | string>;
9
+
10
+ /**
11
+ * Returns the provided image component (e.g. NuxtImg),
12
+ * or falls back to plain `'img'` when nothing is provided.
13
+ */
14
+ export declare function useOptimizedImg(): Component | string;
15
+
16
+ export { }
@@ -0,0 +1,32 @@
1
+ import { ComputedRef } from 'vue';
2
+ import { YouTubeProps } from '@blogmcp/mdx-schema';
3
+
4
+ /**
5
+ * Composable for YouTube component logic
6
+ */
7
+ export declare function useYouTube(props: YouTubeProps): {
8
+ embedUrl: ComputedRef<string>;
9
+ iframeTitle: ComputedRef<string>;
10
+ hasTitle: ComputedRef<boolean>;
11
+ thumbnailUrl: ComputedRef<string>;
12
+ iframeAttrs: ComputedRef< {
13
+ src: string;
14
+ title: string;
15
+ frameborder: string;
16
+ allow: string;
17
+ allowfullscreen: boolean;
18
+ }>;
19
+ YOUTUBE_IFRAME_ALLOW: string;
20
+ };
21
+
22
+ /**
23
+ * YouTube embed URL base
24
+ */
25
+ export declare const YOUTUBE_EMBED_BASE = "https://www.youtube.com/embed/";
26
+
27
+ /**
28
+ * Default iframe allow attributes
29
+ */
30
+ export declare const YOUTUBE_IFRAME_ALLOW = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture";
31
+
32
+ export { }
package/package.json ADDED
@@ -0,0 +1,125 @@
1
+ {
2
+ "name": "@blogmcp/mdx-render-vue",
3
+ "version": "0.1.0",
4
+ "description": "Vue renderer for MDX components — edit + render SFCs wired to @blogmcp/mdx-schema",
5
+ "type": "module",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "module": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./src/index.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./edit": {
18
+ "types": "./src/edit/index.ts",
19
+ "import": "./dist/edit/index.js"
20
+ },
21
+ "./render": {
22
+ "types": "./src/render/index.ts",
23
+ "import": "./dist/render/index.js"
24
+ },
25
+ "./composables": {
26
+ "types": "./src/composables/index.ts",
27
+ "import": "./dist/composables/index.js"
28
+ },
29
+ "./registry": {
30
+ "types": "./src/registry.ts",
31
+ "import": "./dist/registry.js"
32
+ },
33
+ "./render/callout": {
34
+ "types": "./src/render/callout/index.ts",
35
+ "import": "./dist/render/callout/index.js"
36
+ },
37
+ "./render/codeblock": {
38
+ "types": "./src/render/codeblock/index.ts",
39
+ "import": "./dist/render/codeblock/index.js"
40
+ },
41
+ "./render/gallery": {
42
+ "types": "./src/render/gallery/index.ts",
43
+ "import": "./dist/render/gallery/index.js"
44
+ },
45
+ "./render/youtube": {
46
+ "types": "./src/render/youtube/index.ts",
47
+ "import": "./dist/render/youtube/index.js"
48
+ },
49
+ "./render/audioplayer": {
50
+ "types": "./src/render/audioplayer/index.ts",
51
+ "import": "./dist/render/audioplayer/index.js"
52
+ },
53
+ "./composables/gallery": {
54
+ "types": "./src/composables/useGallery.ts",
55
+ "import": "./dist/composables/useGallery.js"
56
+ },
57
+ "./composables/media": {
58
+ "types": "./src/composables/useMedia.ts",
59
+ "import": "./dist/composables/useMedia.js"
60
+ },
61
+ "./composables/callout": {
62
+ "types": "./src/composables/useCallout.ts",
63
+ "import": "./dist/composables/useCallout.js"
64
+ },
65
+ "./composables/codeblock": {
66
+ "types": "./src/composables/useCodeBlock.ts",
67
+ "import": "./dist/composables/useCodeBlock.js"
68
+ },
69
+ "./composables/youtube": {
70
+ "types": "./src/composables/useYouTube.ts",
71
+ "import": "./dist/composables/useYouTube.js"
72
+ },
73
+ "./composables/audioplayer": {
74
+ "types": "./src/composables/useAudioPlayer.ts",
75
+ "import": "./dist/composables/useAudioPlayer.js"
76
+ }
77
+ },
78
+ "files": [
79
+ "dist",
80
+ "src"
81
+ ],
82
+ "scripts": {
83
+ "build": "vite build && vue-tsc --emitDeclarationOnly",
84
+ "dev": "vite build --watch",
85
+ "test": "vitest run",
86
+ "test:watch": "vitest",
87
+ "test:e2e": "playwright test --config=test/e2e/playwright.config.ts",
88
+ "test:e2e:ui": "playwright test --config=test/e2e/playwright.config.ts --ui",
89
+ "harness": "vite --config test/e2e/harness/vite.config.ts --port 5099 --open",
90
+ "typecheck": "vue-tsc --noEmit"
91
+ },
92
+ "dependencies": {
93
+ "@blogmcp/mdx-schema": "^0.1.0",
94
+ "@blogmcp/sanitize": "^0.1.0",
95
+ "@ark-ui/vue": "^5.0.0",
96
+ "@iconify/vue": "^5.0.0"
97
+ },
98
+ "peerDependencies": {
99
+ "vue": "^3.4.0"
100
+ },
101
+ "devDependencies": {
102
+ "@types/node": "^20.10.0",
103
+ "@vitejs/plugin-vue": "^6.0.0",
104
+ "@vitest/utils": "3.2.4",
105
+ "@vue/test-utils": "^2.4.0",
106
+ "happy-dom": "^17.4.0",
107
+ "typescript": "^5.3.3",
108
+ "vite": "^7.3.0",
109
+ "vite-plugin-dts": "^4.0.0",
110
+ "vitest": "^3.0.0",
111
+ "autoprefixer": "^10.4.0",
112
+ "postcss": "^8.4.0",
113
+ "tailwindcss": "^3.4.0",
114
+ "vue": "^3.5.0",
115
+ "vue-router": "^4.5.0",
116
+ "vue-tsc": "^2.0.0"
117
+ },
118
+ "keywords": [
119
+ "mdx",
120
+ "vue",
121
+ "components",
122
+ "markdown"
123
+ ],
124
+ "license": "MIT"
125
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * MDX Components Composables
3
+ *
4
+ * Shared logic for MDX components.
5
+ */
6
+
7
+ export { useCallout, calloutConfigs, calloutIconPaths } from './useCallout'
8
+ export { useCodeBlock, getLanguageLabel, languageLabels } from './useCodeBlock'
9
+ export { useGallery, gridColsClasses, isMediaObject, isVideoUrl } from './useGallery'
10
+ export {
11
+ useYouTube,
12
+ YOUTUBE_EMBED_BASE,
13
+ YOUTUBE_IFRAME_ALLOW,
14
+ } from './useYouTube'
15
+ export { useAudioPlayer } from './useAudioPlayer'
16
+ export { useOptimizedImg, IMG_COMPONENT_KEY } from './useMedia'
@@ -0,0 +1,84 @@
1
+ /**
2
+ * AudioPlayer Composable
3
+ *
4
+ * Shared logic for AudioPlayer component variants.
5
+ */
6
+
7
+ import { computed, ref } from 'vue'
8
+ import type { AudioPlayerProps } from '@blogmcp/mdx-schema'
9
+
10
+ /**
11
+ * Composable for AudioPlayer component logic
12
+ */
13
+ export function useAudioPlayer(props: AudioPlayerProps) {
14
+ const hasTitle = computed(() => !!props.title)
15
+
16
+ const displayTitle = computed(() => props.title || props.slug || 'Audio')
17
+
18
+ const showTranscription = computed(() => props.showTranscription ?? false)
19
+
20
+ /**
21
+ * Build audio URL from slug.
22
+ * Uses the KB API media file endpoint.
23
+ */
24
+ const audioUrl = computed(() => {
25
+ if (!props.slug) return ''
26
+ return `/api/media/file/${props.slug}`
27
+ })
28
+
29
+ /**
30
+ * Build transcription API URL from slug.
31
+ */
32
+ const transcriptionUrl = computed(() => {
33
+ if (!props.slug) return ''
34
+ return `/api/media/transcription/${props.slug}`
35
+ })
36
+
37
+ /**
38
+ * Transcription state (for components that load transcription)
39
+ */
40
+ const transcriptionText = ref<string | null>(null)
41
+ const transcriptionSummary = ref<string | null>(null)
42
+ const transcriptionLoading = ref(false)
43
+ const transcriptionExpanded = ref(false)
44
+
45
+ async function loadTranscription() {
46
+ if (!props.slug || transcriptionText.value !== null) return
47
+ transcriptionLoading.value = true
48
+ try {
49
+ const response = await fetch(transcriptionUrl.value)
50
+ if (response.ok) {
51
+ const data = await response.json()
52
+ transcriptionText.value = data.fullText || data.full_text || ''
53
+ transcriptionSummary.value = data.summary || null
54
+ } else {
55
+ transcriptionText.value = ''
56
+ }
57
+ } catch {
58
+ transcriptionText.value = ''
59
+ } finally {
60
+ transcriptionLoading.value = false
61
+ }
62
+ }
63
+
64
+ function toggleTranscription() {
65
+ transcriptionExpanded.value = !transcriptionExpanded.value
66
+ if (transcriptionExpanded.value && transcriptionText.value === null) {
67
+ loadTranscription()
68
+ }
69
+ }
70
+
71
+ return {
72
+ hasTitle,
73
+ displayTitle,
74
+ showTranscription,
75
+ audioUrl,
76
+ transcriptionUrl,
77
+ transcriptionText,
78
+ transcriptionSummary,
79
+ transcriptionLoading,
80
+ transcriptionExpanded,
81
+ loadTranscription,
82
+ toggleTranscription,
83
+ }
84
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Callout Composable
3
+ *
4
+ * Shared logic for Callout component variants.
5
+ */
6
+
7
+ import { computed } from 'vue'
8
+ import type { CalloutType, CalloutProps } from '@blogmcp/mdx-schema'
9
+
10
+ export interface CalloutConfig {
11
+ type: CalloutType
12
+ icon: string
13
+ colors: {
14
+ bg: string
15
+ border: string
16
+ text: string
17
+ iconColor: string
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Default callout configurations for each type
23
+ */
24
+ export const calloutConfigs: Record<CalloutType, Omit<CalloutConfig, 'type'>> = {
25
+ info: {
26
+ icon: 'info-circle',
27
+ colors: {
28
+ bg: 'bg-blue-900/20',
29
+ border: 'border-blue-500',
30
+ text: 'text-blue-200',
31
+ iconColor: 'text-blue-400',
32
+ },
33
+ },
34
+ warning: {
35
+ icon: 'warning-triangle',
36
+ colors: {
37
+ bg: 'bg-yellow-900/20',
38
+ border: 'border-yellow-500',
39
+ text: 'text-yellow-200',
40
+ iconColor: 'text-yellow-400',
41
+ },
42
+ },
43
+ tip: {
44
+ icon: 'lightbulb',
45
+ colors: {
46
+ bg: 'bg-green-900/20',
47
+ border: 'border-green-500',
48
+ text: 'text-green-200',
49
+ iconColor: 'text-green-400',
50
+ },
51
+ },
52
+ danger: {
53
+ icon: 'alert-circle',
54
+ colors: {
55
+ bg: 'bg-red-900/20',
56
+ border: 'border-red-500',
57
+ text: 'text-red-200',
58
+ iconColor: 'text-red-400',
59
+ },
60
+ },
61
+ }
62
+
63
+ /**
64
+ * SVG path data for callout icons
65
+ */
66
+ export const calloutIconPaths: Record<string, string> = {
67
+ 'info-circle': 'M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
68
+ 'warning-triangle':
69
+ 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z',
70
+ lightbulb:
71
+ 'M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z',
72
+ 'alert-circle': 'M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
73
+ }
74
+
75
+ /**
76
+ * Composable for Callout component logic
77
+ */
78
+ export function useCallout(props: CalloutProps) {
79
+ const effectiveType = computed<CalloutType>(() => props.type || 'info')
80
+
81
+ const config = computed(() => ({
82
+ type: effectiveType.value,
83
+ ...calloutConfigs[effectiveType.value],
84
+ }))
85
+
86
+ const containerClasses = computed(() => [
87
+ config.value.colors.bg,
88
+ config.value.colors.border,
89
+ config.value.colors.text,
90
+ ])
91
+
92
+ const iconPath = computed(() => calloutIconPaths[config.value.icon])
93
+
94
+ const iconClasses = computed(() => config.value.colors.iconColor)
95
+
96
+ return {
97
+ effectiveType,
98
+ config,
99
+ containerClasses,
100
+ iconPath,
101
+ iconClasses,
102
+ }
103
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * CodeBlock Composable
3
+ *
4
+ * Shared logic for CodeBlock component variants.
5
+ */
6
+
7
+ import { computed, ref } from 'vue'
8
+ import type { CodeBlockProps } from '@blogmcp/mdx-schema'
9
+
10
+ /**
11
+ * Language label mappings
12
+ */
13
+ export const languageLabels: Record<string, string> = {
14
+ js: 'JavaScript',
15
+ javascript: 'JavaScript',
16
+ ts: 'TypeScript',
17
+ typescript: 'TypeScript',
18
+ vue: 'Vue',
19
+ html: 'HTML',
20
+ css: 'CSS',
21
+ json: 'JSON',
22
+ python: 'Python',
23
+ py: 'Python',
24
+ bash: 'Bash',
25
+ sh: 'Shell',
26
+ sql: 'SQL',
27
+ text: 'Plain Text',
28
+ rust: 'Rust',
29
+ go: 'Go',
30
+ java: 'Java',
31
+ cpp: 'C++',
32
+ c: 'C',
33
+ csharp: 'C#',
34
+ php: 'PHP',
35
+ ruby: 'Ruby',
36
+ swift: 'Swift',
37
+ kotlin: 'Kotlin',
38
+ yaml: 'YAML',
39
+ yml: 'YAML',
40
+ xml: 'XML',
41
+ markdown: 'Markdown',
42
+ md: 'Markdown',
43
+ graphql: 'GraphQL',
44
+ dockerfile: 'Dockerfile',
45
+ }
46
+
47
+ /**
48
+ * Get display label for a language
49
+ */
50
+ export function getLanguageLabel(language?: string): string {
51
+ if (!language) return 'Plain Text'
52
+ const lang = language.toLowerCase()
53
+ if (languageLabels[lang]) return languageLabels[lang]
54
+ // Capitalize first letter as fallback
55
+ return lang.charAt(0).toUpperCase() + lang.slice(1)
56
+ }
57
+
58
+ /**
59
+ * Composable for CodeBlock component logic
60
+ */
61
+ export function useCodeBlock(props: CodeBlockProps) {
62
+ const copied = ref(false)
63
+
64
+ const languageLabel = computed(() => getLanguageLabel(props.language))
65
+
66
+ const languageClass = computed(() => `language-${props.language || 'text'}`)
67
+
68
+ /**
69
+ * Copy code to clipboard
70
+ * @param codeContent - The code text to copy (passed from component)
71
+ */
72
+ async function copyCode(codeContent: string) {
73
+ try {
74
+ await navigator.clipboard.writeText(codeContent)
75
+ copied.value = true
76
+ setTimeout(() => {
77
+ copied.value = false
78
+ }, 2000)
79
+ } catch (error) {
80
+ console.error('Failed to copy code:', error)
81
+ }
82
+ }
83
+
84
+ return {
85
+ copied,
86
+ languageLabel,
87
+ languageClass,
88
+ copyCode,
89
+ }
90
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Gallery Composable
3
+ *
4
+ * Shared logic for Gallery component variants.
5
+ */
6
+
7
+ import { computed } from 'vue'
8
+ import type { GalleryProps, MediaObject } from '@blogmcp/mdx-schema'
9
+ import { galleryTileLayout, type GalleryTileLayout } from '@blogmcp/mdx-schema/styles'
10
+
11
+ // Re-export the shared (framework-agnostic) tile-layout helper so existing
12
+ // importers of this composable keep working. The logic lives once in
13
+ // @blogmcp/mdx-schema/styles and is mirrored by the React renderer.
14
+ export { galleryTileLayout, type GalleryTileLayout }
15
+
16
+ const VIDEO_EXTENSIONS = /\.(mp4|webm|mov|avi|mkv|m4v)(\?.*)?$/i
17
+ const KB_MEDIA_FILE_RE = /\/api\/media\/file\/([^/?#]+)/
18
+
19
+ /**
20
+ * Type guard for MediaObject
21
+ */
22
+ export function isMediaObject(value: unknown): value is MediaObject {
23
+ return typeof value === 'object' && value !== null && 'src' in value && typeof (value as MediaObject).src === 'string'
24
+ }
25
+
26
+ /**
27
+ * Check if a URL points to a video file based on extension
28
+ */
29
+ export function isVideoUrl(src: string): boolean {
30
+ return VIDEO_EXTENSIONS.test(src)
31
+ }
32
+
33
+ /**
34
+ * Grid column class mappings
35
+ */
36
+ export const gridColsClasses: Record<number, string> = {
37
+ 1: 'grid-cols-1',
38
+ 2: 'grid-cols-2',
39
+ 3: 'grid-cols-3',
40
+ 4: 'grid-cols-4',
41
+ }
42
+
43
+ /**
44
+ * Composable for Gallery component logic
45
+ */
46
+ export function useGallery(props: GalleryProps) {
47
+ const effectiveColumns = computed(() => props.columns || 2)
48
+
49
+ const gridColsClass = computed(() => {
50
+ const cols = effectiveColumns.value
51
+ return gridColsClasses[cols] || 'grid-cols-2'
52
+ })
53
+
54
+ const gapClass = computed(() => {
55
+ const gap = props.gap || 4
56
+ return `gap-${gap}`
57
+ })
58
+
59
+ /** Inline tile/media styles + sizing class from aspectRatio/fit/position. */
60
+ const tileLayout = computed(() => galleryTileLayout(props))
61
+
62
+ /**
63
+ * Get the image source URL at a given index
64
+ */
65
+ function getImageSrc(index: number): string {
66
+ const image = props.images[index]
67
+ return isMediaObject(image) ? image.src : image
68
+ }
69
+
70
+ /**
71
+ * Generate alt text for an image, preferring MediaObject.alt
72
+ */
73
+ function getImageAlt(index: number): string {
74
+ const image = props.images[index]
75
+ if (isMediaObject(image)) return image.alt || `Gallery image ${index + 1}`
76
+ return `Gallery image ${index + 1}`
77
+ }
78
+
79
+ /**
80
+ * Get the description for an image (only MediaObject has descriptions)
81
+ */
82
+ function getImageDescription(index: number): string | undefined {
83
+ const image = props.images[index]
84
+ return isMediaObject(image) ? image.description : undefined
85
+ }
86
+
87
+ /**
88
+ * Caption shown below a gallery item: prefer the description, fall back to the
89
+ * alt text. Many authors fill only one of the two, so the caption uses whichever
90
+ * is present (description first — it's the richer field). Returns undefined for
91
+ * plain string images with no metadata (no caption rendered). NOTE: uses the
92
+ * real authored alt, never the `getImageAlt` "Gallery image N" placeholder.
93
+ */
94
+ function getImageCaption(index: number): string | undefined {
95
+ const image = props.images[index]
96
+ if (!isMediaObject(image)) return undefined
97
+ return image.description || image.alt || undefined
98
+ }
99
+
100
+ /**
101
+ * Check if the item at index is a video (by type field or URL extension)
102
+ */
103
+ function isVideo(index: number): boolean {
104
+ const image = props.images[index]
105
+ if (isMediaObject(image)) {
106
+ if (image.type === 'video') return true
107
+ if (image.type === 'image') return false
108
+ return isVideoUrl(image.src)
109
+ }
110
+ return isVideoUrl(image)
111
+ }
112
+
113
+ /**
114
+ * Get poster URL for a video (KB media videos only)
115
+ */
116
+ function getVideoPoster(index: number): string | undefined {
117
+ const src = getImageSrc(index)
118
+ const match = src.match(KB_MEDIA_FILE_RE)
119
+ return match ? `/api/media/poster/${match[1]}` : undefined
120
+ }
121
+
122
+ /**
123
+ * Check if images array is valid
124
+ */
125
+ const hasImages = computed(() => props.images && props.images.length > 0)
126
+
127
+ const imageCount = computed(() => props.images?.length || 0)
128
+
129
+ return {
130
+ effectiveColumns,
131
+ gridColsClass,
132
+ gapClass,
133
+ tileLayout,
134
+ getImageSrc,
135
+ getImageAlt,
136
+ getImageDescription,
137
+ getImageCaption,
138
+ isVideo,
139
+ getVideoPoster,
140
+ hasImages,
141
+ imageCount,
142
+ }
143
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Media Composables
3
+ *
4
+ * Image optimization helpers for MDX components.
5
+ * Uses provide/inject so the host app (blog) can supply NuxtImg
6
+ * while the md-editor (no @nuxt/image) gets a plain 'img' fallback.
7
+ */
8
+
9
+ import { inject, type InjectionKey, type Component } from 'vue'
10
+
11
+ /**
12
+ * Injection key for the optimized image component.
13
+ * The host app should `provide(IMG_COMPONENT_KEY, NuxtImg)`.
14
+ */
15
+ export const IMG_COMPONENT_KEY: InjectionKey<Component | string> = Symbol('ImgComponent')
16
+
17
+ /**
18
+ * Returns the provided image component (e.g. NuxtImg),
19
+ * or falls back to plain `'img'` when nothing is provided.
20
+ */
21
+ export function useOptimizedImg(): Component | string {
22
+ return inject(IMG_COMPONENT_KEY, 'img')
23
+ }