@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.
- package/dist/_virtual/_plugin-vue_export-helper.js +10 -0
- package/dist/_virtual/constants.js +4 -0
- package/dist/_virtual/index.js +5 -0
- package/dist/_virtual/index2.js +4 -0
- package/dist/composables/index.d.ts +11 -0
- package/dist/composables/index.js +24 -0
- package/dist/composables/useAudioPlayer.d.ts +22 -0
- package/dist/composables/useAudioPlayer.js +58 -0
- package/dist/composables/useCallout.d.ts +43 -0
- package/dist/composables/useCallout.js +71 -0
- package/dist/composables/useCodeBlock.d.ts +23 -0
- package/dist/composables/useCodeBlock.js +67 -0
- package/dist/composables/useGallery.d.ts +37 -0
- package/dist/composables/useGallery.js +84 -0
- package/dist/composables/useMedia.d.ts +18 -0
- package/dist/composables/useMedia.js +9 -0
- package/dist/composables/useYouTube.d.ts +31 -0
- package/dist/composables/useYouTube.js +32 -0
- package/dist/edit/AlertEdit.vue.d.ts +12 -0
- package/dist/edit/AlertEdit.vue.js +4 -0
- package/dist/edit/AlertEdit.vue2.js +18 -0
- package/dist/edit/AudioPlayerEdit.vue.d.ts +3 -0
- package/dist/edit/AudioPlayerEdit.vue.js +117 -0
- package/dist/edit/AudioPlayerEdit.vue2.js +4 -0
- package/dist/edit/BadgeEdit.vue.d.ts +2 -0
- package/dist/edit/BadgeEdit.vue.js +4 -0
- package/dist/edit/BadgeEdit.vue2.js +13 -0
- package/dist/edit/ButtonEdit.vue.d.ts +2 -0
- package/dist/edit/ButtonEdit.vue.js +4 -0
- package/dist/edit/ButtonEdit.vue2.js +13 -0
- package/dist/edit/CalloutEdit.vue.d.ts +13 -0
- package/dist/edit/CalloutEdit.vue.js +85 -0
- package/dist/edit/CalloutEdit.vue2.js +4 -0
- package/dist/edit/CardEdit.vue.d.ts +12 -0
- package/dist/edit/CardEdit.vue.js +4 -0
- package/dist/edit/CardEdit.vue2.js +18 -0
- package/dist/edit/CodeBlockEdit.vue.d.ts +12 -0
- package/dist/edit/CodeBlockEdit.vue.js +7 -0
- package/dist/edit/CodeBlockEdit.vue2.js +89 -0
- package/dist/edit/GalleryEdit.vue.d.ts +3 -0
- package/dist/edit/GalleryEdit.vue.js +64 -0
- package/dist/edit/GalleryEdit.vue2.js +4 -0
- package/dist/edit/IconEdit.vue.d.ts +2 -0
- package/dist/edit/IconEdit.vue.js +4 -0
- package/dist/edit/IconEdit.vue2.js +13 -0
- package/dist/edit/TooltipEdit.vue.d.ts +16 -0
- package/dist/edit/TooltipEdit.vue.js +4 -0
- package/dist/edit/TooltipEdit.vue2.js +24 -0
- package/dist/edit/YouTubeEdit.vue.d.ts +3 -0
- package/dist/edit/YouTubeEdit.vue.js +41 -0
- package/dist/edit/YouTubeEdit.vue2.js +4 -0
- package/dist/edit/index.d.ts +29 -0
- package/dist/edit/index.js +35 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +75 -0
- package/dist/mdx-render-vue.css +31 -0
- package/dist/node_modules/@ark-ui/vue/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/factory.js +35 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/presence/use-presence-context.js +6 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/presence/use-presence.js +40 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-arrow-tip.js +5 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-arrow-tip.vue_vue_type_script_setup_true_lang.js +21 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-arrow.js +5 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-arrow.vue_vue_type_script_setup_true_lang.js +21 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-content.js +5 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-content.vue_vue_type_script_setup_true_lang.js +25 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-context.js +5 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-context.vue_vue_type_script_setup_true_lang.js +14 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-positioner.js +5 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-positioner.vue_vue_type_script_setup_true_lang.js +23 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-root-provider.js +5 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-root-provider.vue_vue_type_script_setup_true_lang.js +38 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-root.js +5 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-root.vue_vue_type_script_setup_true_lang.js +68 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-trigger.js +5 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip-trigger.vue_vue_type_script_setup_true_lang.js +25 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/tooltip.js +22 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/use-tooltip-context.js +6 -0
- package/dist/node_modules/@ark-ui/vue/dist/components/tooltip/use-tooltip.js +36 -0
- package/dist/node_modules/@ark-ui/vue/dist/providers/environment/use-environment-context.js +13 -0
- package/dist/node_modules/@ark-ui/vue/dist/providers/locale/use-locale-context.js +12 -0
- package/dist/node_modules/@ark-ui/vue/dist/utils/clean-props.js +8 -0
- package/dist/node_modules/@ark-ui/vue/dist/utils/create-context.js +14 -0
- package/dist/node_modules/@ark-ui/vue/dist/utils/dynamic.js +34 -0
- package/dist/node_modules/@ark-ui/vue/dist/utils/unref-element.js +8 -0
- package/dist/node_modules/@ark-ui/vue/dist/utils/use-forward-expose.js +46 -0
- package/dist/node_modules/@ark-ui/vue/dist/utils/use-render-strategy.js +5 -0
- package/dist/node_modules/@braintree/sanitize-url/dist/constants.js +20 -0
- package/dist/node_modules/@braintree/sanitize-url/dist/index.js +75 -0
- package/dist/node_modules/@floating-ui/core/dist/floating-ui.core.js +728 -0
- package/dist/node_modules/@floating-ui/dom/dist/floating-ui.dom.js +629 -0
- package/dist/node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.js +176 -0
- package/dist/node_modules/@floating-ui/utils/dist/floating-ui.utils.js +151 -0
- package/dist/node_modules/@iconify/vue/dist/iconify.js +1691 -0
- package/dist/node_modules/@zag-js/anatomy/dist/create-anatomy.js +29 -0
- package/dist/node_modules/@zag-js/core/dist/create-machine.js +28 -0
- package/dist/node_modules/@zag-js/core/dist/merge-props.js +55 -0
- package/dist/node_modules/@zag-js/core/dist/scope.js +20 -0
- package/dist/node_modules/@zag-js/core/dist/state.js +176 -0
- package/dist/node_modules/@zag-js/core/dist/types.js +11 -0
- package/dist/node_modules/@zag-js/dom-query/dist/chunk-QZ7TP4HQ.js +6 -0
- package/dist/node_modules/@zag-js/dom-query/dist/computed-style.js +11 -0
- package/dist/node_modules/@zag-js/dom-query/dist/event.js +37 -0
- package/dist/node_modules/@zag-js/dom-query/dist/node.js +64 -0
- package/dist/node_modules/@zag-js/dom-query/dist/overflow.js +28 -0
- package/dist/node_modules/@zag-js/dom-query/dist/platform.js +23 -0
- package/dist/node_modules/@zag-js/dom-query/dist/query.js +6 -0
- package/dist/node_modules/@zag-js/dom-query/dist/raf.js +52 -0
- package/dist/node_modules/@zag-js/dom-query/dist/set.js +22 -0
- package/dist/node_modules/@zag-js/dom-query/dist/shared.js +8 -0
- package/dist/node_modules/@zag-js/focus-visible/dist/index.js +159 -0
- package/dist/node_modules/@zag-js/popper/dist/get-anchor.js +39 -0
- package/dist/node_modules/@zag-js/popper/dist/get-placement.js +296 -0
- package/dist/node_modules/@zag-js/popper/dist/get-styles.js +47 -0
- package/dist/node_modules/@zag-js/popper/dist/middleware.js +79 -0
- package/dist/node_modules/@zag-js/popper/dist/placement.js +7 -0
- package/dist/node_modules/@zag-js/presence/dist/presence.connect.js +18 -0
- package/dist/node_modules/@zag-js/presence/dist/presence.machine.js +175 -0
- package/dist/node_modules/@zag-js/tooltip/dist/tooltip.anatomy.js +7 -0
- package/dist/node_modules/@zag-js/tooltip/dist/tooltip.connect.js +155 -0
- package/dist/node_modules/@zag-js/tooltip/dist/tooltip.dom.js +24 -0
- package/dist/node_modules/@zag-js/tooltip/dist/tooltip.machine.js +416 -0
- package/dist/node_modules/@zag-js/tooltip/dist/tooltip.store.js +9 -0
- package/dist/node_modules/@zag-js/types/dist/prop-types.js +14 -0
- package/dist/node_modules/@zag-js/utils/dist/array.js +7 -0
- package/dist/node_modules/@zag-js/utils/dist/equal.js +36 -0
- package/dist/node_modules/@zag-js/utils/dist/functions.js +11 -0
- package/dist/node_modules/@zag-js/utils/dist/guard.js +26 -0
- package/dist/node_modules/@zag-js/utils/dist/object.js +16 -0
- package/dist/node_modules/@zag-js/utils/dist/store.js +44 -0
- package/dist/node_modules/@zag-js/utils/dist/warning.js +31 -0
- package/dist/node_modules/@zag-js/vue/dist/bindable.js +48 -0
- package/dist/node_modules/@zag-js/vue/dist/machine.js +258 -0
- package/dist/node_modules/@zag-js/vue/dist/normalize-props.js +42 -0
- package/dist/node_modules/@zag-js/vue/dist/refs.js +14 -0
- package/dist/node_modules/@zag-js/vue/dist/track.js +22 -0
- package/dist/node_modules/dompurify/dist/purify.es.js +1190 -0
- package/dist/registry.d.ts +76 -0
- package/dist/registry.js +164 -0
- package/dist/render/alert/Alert.vue.d.ts +18 -0
- package/dist/render/alert/Alert.vue.js +4 -0
- package/dist/render/alert/Alert.vue2.js +43 -0
- package/dist/render/alert/index.d.ts +2 -0
- package/dist/render/audioplayer/AudioPlayerDefault.vue.d.ts +3 -0
- package/dist/render/audioplayer/AudioPlayerDefault.vue.js +116 -0
- package/dist/render/audioplayer/AudioPlayerDefault.vue2.js +4 -0
- package/dist/render/audioplayer/index.d.ts +5 -0
- package/dist/render/audioplayer/index.js +5 -0
- package/dist/render/badge/Badge.vue.d.ts +10 -0
- package/dist/render/badge/Badge.vue.js +4 -0
- package/dist/render/badge/Badge.vue2.js +28 -0
- package/dist/render/badge/index.d.ts +2 -0
- package/dist/render/button/Button.vue.d.ts +13 -0
- package/dist/render/button/Button.vue.js +4 -0
- package/dist/render/button/Button.vue2.js +42 -0
- package/dist/render/button/index.d.ts +2 -0
- package/dist/render/callout/CalloutDefault.vue.d.ts +13 -0
- package/dist/render/callout/CalloutDefault.vue.js +86 -0
- package/dist/render/callout/CalloutDefault.vue2.js +4 -0
- package/dist/render/callout/CalloutMinimal.vue.d.ts +13 -0
- package/dist/render/callout/CalloutMinimal.vue.js +27 -0
- package/dist/render/callout/CalloutMinimal.vue2.js +4 -0
- package/dist/render/callout/index.d.ts +6 -0
- package/dist/render/callout/index.js +7 -0
- package/dist/render/card/Card.vue.d.ts +15 -0
- package/dist/render/card/Card.vue.js +4 -0
- package/dist/render/card/Card.vue2.js +29 -0
- package/dist/render/card/index.d.ts +2 -0
- package/dist/render/codeblock/CodeBlockDefault.vue.d.ts +12 -0
- package/dist/render/codeblock/CodeBlockDefault.vue.js +7 -0
- package/dist/render/codeblock/CodeBlockDefault.vue2.js +92 -0
- package/dist/render/codeblock/index.d.ts +5 -0
- package/dist/render/codeblock/index.js +5 -0
- package/dist/render/gallery/GalleryGrid.vue.d.ts +3 -0
- package/dist/render/gallery/GalleryGrid.vue.js +125 -0
- package/dist/render/gallery/GalleryGrid.vue2.js +4 -0
- package/dist/render/gallery/GalleryLightbox.vue.d.ts +16 -0
- package/dist/render/gallery/GalleryLightbox.vue.js +7 -0
- package/dist/render/gallery/GalleryLightbox.vue2.js +170 -0
- package/dist/render/gallery/index.d.ts +6 -0
- package/dist/render/gallery/index.js +7 -0
- package/dist/render/icon/Icon.vue.d.ts +9 -0
- package/dist/render/icon/Icon.vue.js +4 -0
- package/dist/render/icon/Icon.vue2.js +25 -0
- package/dist/render/icon/index.d.ts +2 -0
- package/dist/render/index.d.ts +40 -0
- package/dist/render/index.js +63 -0
- package/dist/render/tooltip/Tooltip.vue.d.ts +18 -0
- package/dist/render/tooltip/Tooltip.vue.js +4 -0
- package/dist/render/tooltip/Tooltip.vue2.js +58 -0
- package/dist/render/tooltip/index.d.ts +2 -0
- package/dist/render/youtube/YouTubeDefault.vue.d.ts +3 -0
- package/dist/render/youtube/YouTubeDefault.vue.js +79 -0
- package/dist/render/youtube/YouTubeDefault.vue2.js +4 -0
- package/dist/render/youtube/index.d.ts +5 -0
- package/dist/render/youtube/index.js +5 -0
- package/dist/useAudioPlayer.d.ts +22 -0
- package/dist/useCallout.d.ts +46 -0
- package/dist/useCodeBlock.d.ts +25 -0
- package/dist/useGallery.d.ts +44 -0
- package/dist/useMedia.d.ts +16 -0
- package/dist/useYouTube.d.ts +32 -0
- package/package.json +125 -0
- package/src/composables/index.ts +16 -0
- package/src/composables/useAudioPlayer.ts +84 -0
- package/src/composables/useCallout.ts +103 -0
- package/src/composables/useCodeBlock.ts +90 -0
- package/src/composables/useGallery.ts +143 -0
- package/src/composables/useMedia.ts +23 -0
- package/src/composables/useYouTube.ts +58 -0
- package/src/edit/AlertEdit.vue +9 -0
- package/src/edit/AudioPlayerEdit.vue +92 -0
- package/src/edit/BadgeEdit.vue +7 -0
- package/src/edit/ButtonEdit.vue +10 -0
- package/src/edit/CalloutEdit.vue +107 -0
- package/src/edit/CardEdit.vue +9 -0
- package/src/edit/CodeBlockEdit.vue +76 -0
- package/src/edit/GalleryEdit.vue +65 -0
- package/src/edit/IconEdit.vue +7 -0
- package/src/edit/TooltipEdit.vue +15 -0
- package/src/edit/YouTubeEdit.vue +33 -0
- package/src/edit/index.ts +32 -0
- package/src/index.ts +62 -0
- package/src/registry.ts +272 -0
- package/src/render/alert/Alert.vue +32 -0
- package/src/render/alert/index.ts +2 -0
- package/src/render/audioplayer/AudioPlayerDefault.vue +89 -0
- package/src/render/audioplayer/index.ts +8 -0
- package/src/render/badge/Badge.vue +25 -0
- package/src/render/badge/index.ts +2 -0
- package/src/render/button/Button.vue +41 -0
- package/src/render/button/index.ts +2 -0
- package/src/render/callout/CalloutDefault.vue +101 -0
- package/src/render/callout/CalloutMinimal.vue +31 -0
- package/src/render/callout/index.ts +9 -0
- package/src/render/card/Card.vue +15 -0
- package/src/render/card/index.ts +2 -0
- package/src/render/codeblock/CodeBlockDefault.vue +76 -0
- package/src/render/codeblock/index.ts +8 -0
- package/src/render/gallery/GalleryGrid.vue +162 -0
- package/src/render/gallery/GalleryLightbox.vue +162 -0
- package/src/render/gallery/index.ts +9 -0
- package/src/render/icon/Icon.vue +22 -0
- package/src/render/icon/index.ts +2 -0
- package/src/render/index.ts +76 -0
- package/src/render/tooltip/Tooltip.vue +35 -0
- package/src/render/tooltip/index.ts +2 -0
- package/src/render/youtube/YouTubeDefault.vue +72 -0
- package/src/render/youtube/index.ts +8 -0
package/src/registry.ts
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MDX Component Registry (Vue)
|
|
3
|
+
*
|
|
4
|
+
* Attaches Vue-specific render/edit loaders to the framework-agnostic metadata
|
|
5
|
+
* published by @blogmcp/mdx-schema. Schema is the single source of truth for
|
|
6
|
+
* component descriptions, prop shapes, and MCP-facing examples — this file
|
|
7
|
+
* adds only the Vue wiring (dynamic imports of SFCs + editor theming).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { defineAsyncComponent, defineComponent, h, type Component } from 'vue'
|
|
11
|
+
import {
|
|
12
|
+
componentMetadata,
|
|
13
|
+
getComponentMetadata,
|
|
14
|
+
getComponentNames,
|
|
15
|
+
type ComponentMetadata,
|
|
16
|
+
} from '@blogmcp/mdx-schema/metadata'
|
|
17
|
+
import { prepareProps } from '@blogmcp/mdx-schema'
|
|
18
|
+
|
|
19
|
+
// Re-export schema helpers so callers that already have the registry can reach
|
|
20
|
+
// metadata without a second import.
|
|
21
|
+
export { componentMetadata, getComponentMetadata, getComponentNames }
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Sentinel property read by the per-renderer "prepareProps is wired"
|
|
25
|
+
* integration test. Wrapped components carry this with the component's
|
|
26
|
+
* schema name so the test can confirm wrapping end-to-end (vs the
|
|
27
|
+
* historical regex-grep on registry source, which was easily defeated
|
|
28
|
+
* by commented-out code and unused imports).
|
|
29
|
+
*/
|
|
30
|
+
export const MDX_PREPARED_SENTINEL = '__mdxPreparePropsAppliedFor' as const
|
|
31
|
+
|
|
32
|
+
export interface EditorConfig {
|
|
33
|
+
/** SVG icon name used in MdxBlockView header */
|
|
34
|
+
icon: string
|
|
35
|
+
/** Primary text/accent color */
|
|
36
|
+
color: string
|
|
37
|
+
/** Border color */
|
|
38
|
+
borderColor: string
|
|
39
|
+
/** Background color (typically rgba) */
|
|
40
|
+
bgColor: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ComponentRegistration {
|
|
44
|
+
metadata: ComponentMetadata
|
|
45
|
+
editor: EditorConfig
|
|
46
|
+
/** Render variants: variant name → async loader. Must include 'default'. */
|
|
47
|
+
render: Record<string, () => Promise<{ default: Component }>>
|
|
48
|
+
/** Edit component (dark theme) for md-editor preview */
|
|
49
|
+
edit: () => Promise<{ default: Component }>
|
|
50
|
+
/** Optional custom editor view for complex inline editing (Gallery drag-and-drop, CodeBlock textarea) */
|
|
51
|
+
editorView?: () => Promise<{ default: Component }>
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Per-component Vue wiring. Metadata is pulled from the schema package by name
|
|
56
|
+
* so Phase 1b additions (Button, Card, etc.) only require schema changes +
|
|
57
|
+
* a new entry here.
|
|
58
|
+
*/
|
|
59
|
+
interface VueWiring {
|
|
60
|
+
editor: EditorConfig
|
|
61
|
+
render: Record<string, () => Promise<{ default: Component }>>
|
|
62
|
+
edit: () => Promise<{ default: Component }>
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const vueWiring: Record<string, VueWiring> = {
|
|
66
|
+
Callout: {
|
|
67
|
+
editor: { icon: 'info', color: '#569cd6', borderColor: '#569cd6', bgColor: 'rgba(86, 156, 214, 0.08)' },
|
|
68
|
+
render: {
|
|
69
|
+
default: () => import('./render/callout/CalloutDefault.vue'),
|
|
70
|
+
minimal: () => import('./render/callout/CalloutMinimal.vue'),
|
|
71
|
+
},
|
|
72
|
+
edit: () => import('./edit/CalloutEdit.vue'),
|
|
73
|
+
},
|
|
74
|
+
YouTube: {
|
|
75
|
+
editor: { icon: 'play', color: '#ff4444', borderColor: '#ff4444', bgColor: 'rgba(255, 68, 68, 0.08)' },
|
|
76
|
+
render: {
|
|
77
|
+
default: () => import('./render/youtube/YouTubeDefault.vue'),
|
|
78
|
+
},
|
|
79
|
+
edit: () => import('./edit/YouTubeEdit.vue'),
|
|
80
|
+
},
|
|
81
|
+
Gallery: {
|
|
82
|
+
editor: { icon: 'image', color: '#4ec9b0', borderColor: '#4ec9b0', bgColor: 'rgba(78, 201, 176, 0.08)' },
|
|
83
|
+
render: {
|
|
84
|
+
default: () => import('./render/gallery/GalleryGrid.vue'),
|
|
85
|
+
grid: () => import('./render/gallery/GalleryGrid.vue'),
|
|
86
|
+
},
|
|
87
|
+
edit: () => import('./edit/GalleryEdit.vue'),
|
|
88
|
+
},
|
|
89
|
+
CodeBlock: {
|
|
90
|
+
editor: { icon: 'code', color: '#ce9178', borderColor: '#ce9178', bgColor: 'rgba(206, 145, 120, 0.08)' },
|
|
91
|
+
render: {
|
|
92
|
+
default: () => import('./render/codeblock/CodeBlockDefault.vue'),
|
|
93
|
+
},
|
|
94
|
+
edit: () => import('./edit/CodeBlockEdit.vue'),
|
|
95
|
+
},
|
|
96
|
+
AudioPlayer: {
|
|
97
|
+
editor: { icon: 'audio', color: '#b48ead', borderColor: '#b48ead', bgColor: 'rgba(180, 142, 173, 0.08)' },
|
|
98
|
+
render: {
|
|
99
|
+
default: () => import('./render/audioplayer/AudioPlayerDefault.vue'),
|
|
100
|
+
},
|
|
101
|
+
edit: () => import('./edit/AudioPlayerEdit.vue'),
|
|
102
|
+
},
|
|
103
|
+
Button: {
|
|
104
|
+
editor: { icon: 'button', color: '#2563eb', borderColor: '#2563eb', bgColor: 'rgba(37, 99, 235, 0.08)' },
|
|
105
|
+
render: {
|
|
106
|
+
default: () => import('./render/button/Button.vue'),
|
|
107
|
+
},
|
|
108
|
+
edit: () => import('./edit/ButtonEdit.vue'),
|
|
109
|
+
},
|
|
110
|
+
Card: {
|
|
111
|
+
editor: { icon: 'card', color: '#6b7280', borderColor: '#6b7280', bgColor: 'rgba(107, 114, 128, 0.08)' },
|
|
112
|
+
render: {
|
|
113
|
+
default: () => import('./render/card/Card.vue'),
|
|
114
|
+
},
|
|
115
|
+
edit: () => import('./edit/CardEdit.vue'),
|
|
116
|
+
},
|
|
117
|
+
Alert: {
|
|
118
|
+
editor: { icon: 'alert', color: '#d97706', borderColor: '#d97706', bgColor: 'rgba(217, 119, 6, 0.08)' },
|
|
119
|
+
render: {
|
|
120
|
+
default: () => import('./render/alert/Alert.vue'),
|
|
121
|
+
},
|
|
122
|
+
edit: () => import('./edit/AlertEdit.vue'),
|
|
123
|
+
},
|
|
124
|
+
Badge: {
|
|
125
|
+
editor: { icon: 'badge', color: '#059669', borderColor: '#059669', bgColor: 'rgba(5, 150, 105, 0.08)' },
|
|
126
|
+
render: {
|
|
127
|
+
default: () => import('./render/badge/Badge.vue'),
|
|
128
|
+
},
|
|
129
|
+
edit: () => import('./edit/BadgeEdit.vue'),
|
|
130
|
+
},
|
|
131
|
+
Icon: {
|
|
132
|
+
editor: { icon: 'icon', color: '#7c3aed', borderColor: '#7c3aed', bgColor: 'rgba(124, 58, 237, 0.08)' },
|
|
133
|
+
render: {
|
|
134
|
+
default: () => import('./render/icon/Icon.vue'),
|
|
135
|
+
},
|
|
136
|
+
edit: () => import('./edit/IconEdit.vue'),
|
|
137
|
+
},
|
|
138
|
+
Tooltip: {
|
|
139
|
+
editor: { icon: 'tooltip', color: '#0891b2', borderColor: '#0891b2', bgColor: 'rgba(8, 145, 178, 0.08)' },
|
|
140
|
+
render: {
|
|
141
|
+
default: () => import('./render/tooltip/Tooltip.vue'),
|
|
142
|
+
},
|
|
143
|
+
edit: () => import('./edit/TooltipEdit.vue'),
|
|
144
|
+
},
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Merged registry: schema metadata + Vue wiring, keyed by component name. */
|
|
148
|
+
export const componentRegistry: Record<string, ComponentRegistration> = Object.fromEntries(
|
|
149
|
+
componentMetadata.map(metadata => {
|
|
150
|
+
const wiring = vueWiring[metadata.name]
|
|
151
|
+
if (!wiring) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`[mdx-components] No Vue wiring registered for component "${metadata.name}". ` +
|
|
154
|
+
`Add an entry to vueWiring in registry.ts or remove the schema entry.`,
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
return [metadata.name, { metadata, ...wiring }] as const
|
|
158
|
+
}),
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
/** Get available render variant names for a component */
|
|
162
|
+
export function getComponentVariants(name: string): string[] {
|
|
163
|
+
const reg = componentRegistry[name]
|
|
164
|
+
return reg ? Object.keys(reg.render) : []
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Async resolver — loads only the requested render component (raw, no shim). */
|
|
168
|
+
export async function getRenderComponentAsync(
|
|
169
|
+
name: string,
|
|
170
|
+
variant: string = 'default',
|
|
171
|
+
): Promise<Component | undefined> {
|
|
172
|
+
const reg = componentRegistry[name]
|
|
173
|
+
if (!reg) return undefined
|
|
174
|
+
const loader = reg.render[variant] || reg.render.default
|
|
175
|
+
if (!loader) return undefined
|
|
176
|
+
const mod = await loader()
|
|
177
|
+
return mod.default
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ─── Wrapped (prepareProps-aware) component resolution ──────────────────
|
|
181
|
+
//
|
|
182
|
+
// `getWrappedRenderComponent` is what blog consumers SHOULD use to render
|
|
183
|
+
// MDX-authored components. It returns a Vue component whose render runs
|
|
184
|
+
// every incoming attribute through `prepareProps(attrs, metadata)` before
|
|
185
|
+
// forwarding to the real render SFC. That makes the schema's render-time
|
|
186
|
+
// shim impossible to forget at any call site: `h(Comp, { ...item.props })`
|
|
187
|
+
// automatically gets normalized props, including in recursively-rendered
|
|
188
|
+
// nested MDX children.
|
|
189
|
+
//
|
|
190
|
+
// The wrapper is built around `defineAsyncComponent` so the underlying
|
|
191
|
+
// SFC stays lazily loaded — the wrapper itself is sync and cheap to
|
|
192
|
+
// create. Results are cached per (name, variant) so the wrapper has a
|
|
193
|
+
// stable identity across re-renders (important for Vue's keyed-vdom
|
|
194
|
+
// optimizations).
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Wraps an already-resolved Vue component with a `prepareProps`-aware
|
|
198
|
+
* shim. Returned component is sync; works with both sync `Inner` and
|
|
199
|
+
* `defineAsyncComponent(...)` results. Exported (vs inlined into
|
|
200
|
+
* `getWrappedRenderComponent`) so unit tests can pass a spy component
|
|
201
|
+
* and assert prop normalization without touching the real registry.
|
|
202
|
+
*/
|
|
203
|
+
export function wrapVueComponentForMdx(
|
|
204
|
+
Inner: Component,
|
|
205
|
+
metadata: ComponentMetadata,
|
|
206
|
+
variant: string = 'default',
|
|
207
|
+
): Component {
|
|
208
|
+
const wrapped = defineComponent({
|
|
209
|
+
name: `MdxPrepared${metadata.name}${variant === 'default' ? '' : `_${variant}`}`,
|
|
210
|
+
inheritAttrs: false,
|
|
211
|
+
setup(_props, { attrs, slots }) {
|
|
212
|
+
return () => h(
|
|
213
|
+
Inner,
|
|
214
|
+
prepareProps(attrs as Record<string, unknown>, metadata),
|
|
215
|
+
slots,
|
|
216
|
+
)
|
|
217
|
+
},
|
|
218
|
+
})
|
|
219
|
+
;(wrapped as unknown as Record<string, unknown>)[MDX_PREPARED_SENTINEL] = metadata.name
|
|
220
|
+
return wrapped
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const wrappedRenderCache: Map<string, Component> = new Map()
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Returns a Vue component that wraps the lazy-loaded render SFC for
|
|
227
|
+
* `name`/`variant` with a `prepareProps`-aware shim. Returns `undefined`
|
|
228
|
+
* if the component isn't registered.
|
|
229
|
+
*
|
|
230
|
+
* Wrapped components carry `MDX_PREPARED_SENTINEL` on themselves, set
|
|
231
|
+
* to the schema component name — used by the per-renderer integration
|
|
232
|
+
* test to confirm wrapping was applied (instead of the historical
|
|
233
|
+
* brittle regex-grep approach).
|
|
234
|
+
*/
|
|
235
|
+
export function getWrappedRenderComponent(
|
|
236
|
+
name: string,
|
|
237
|
+
variant: string = 'default',
|
|
238
|
+
): Component | undefined {
|
|
239
|
+
const cacheKey = `${name}:${variant}`
|
|
240
|
+
const cached = wrappedRenderCache.get(cacheKey)
|
|
241
|
+
if (cached) return cached
|
|
242
|
+
|
|
243
|
+
const reg = componentRegistry[name]
|
|
244
|
+
if (!reg) return undefined
|
|
245
|
+
const loader = reg.render[variant] || reg.render.default
|
|
246
|
+
if (!loader) return undefined
|
|
247
|
+
|
|
248
|
+
const Inner = defineAsyncComponent(() => loader().then(m => m.default))
|
|
249
|
+
const wrapped = wrapVueComponentForMdx(Inner, reg.metadata, variant)
|
|
250
|
+
wrappedRenderCache.set(cacheKey, wrapped)
|
|
251
|
+
return wrapped
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Test-only — clears the wrapped-component cache between specs. */
|
|
255
|
+
export function _resetWrappedRenderCacheForTests(): void {
|
|
256
|
+
wrappedRenderCache.clear()
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Async resolver for edit components */
|
|
260
|
+
export async function getEditComponentAsync(
|
|
261
|
+
name: string,
|
|
262
|
+
): Promise<Component | undefined> {
|
|
263
|
+
const reg = componentRegistry[name]
|
|
264
|
+
if (!reg) return undefined
|
|
265
|
+
const mod = await reg.edit()
|
|
266
|
+
return mod.default
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Get editor config for a component (icon, colors) */
|
|
270
|
+
export function getEditorConfig(name: string): EditorConfig | undefined {
|
|
271
|
+
return componentRegistry[name]?.editor
|
|
272
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import { Icon } from '@iconify/vue'
|
|
4
|
+
import { alertStyles } from '@blogmcp/mdx-schema/styles'
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
severity?: 'info' | 'success' | 'warning' | 'error'
|
|
8
|
+
title?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(defineProps<Props>(), { severity: 'info' })
|
|
12
|
+
|
|
13
|
+
// severity → wrap classes + iconify icon + icon color shared with the React
|
|
14
|
+
// renderer via @blogmcp/mdx-schema/styles.
|
|
15
|
+
const cfg = computed(() => alertStyles.severity[props.severity])
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<div
|
|
20
|
+
:class="[alertStyles.base, cfg.wrap]"
|
|
21
|
+
role="alert"
|
|
22
|
+
data-mdx-component="Alert"
|
|
23
|
+
>
|
|
24
|
+
<Icon :icon="cfg.icon" :class="['mt-0.5 shrink-0', cfg.iconClass]" :width="18" :height="18" />
|
|
25
|
+
<div class="min-w-0 flex-1">
|
|
26
|
+
<div v-if="title" class="mb-0.5 font-semibold">{{ title }}</div>
|
|
27
|
+
<div class="prose-sm">
|
|
28
|
+
<slot />
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* AudioPlayer Default Render Component
|
|
4
|
+
*
|
|
5
|
+
* Light theme with HTML5 audio, title, and collapsible transcript.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useAudioPlayer } from '../../composables/useAudioPlayer'
|
|
9
|
+
import type { AudioPlayerProps } from '@blogmcp/mdx-schema'
|
|
10
|
+
|
|
11
|
+
const props = defineProps<AudioPlayerProps>()
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
audioUrl,
|
|
15
|
+
displayTitle,
|
|
16
|
+
hasTitle,
|
|
17
|
+
showTranscription,
|
|
18
|
+
transcriptionText,
|
|
19
|
+
transcriptionSummary,
|
|
20
|
+
transcriptionLoading,
|
|
21
|
+
transcriptionExpanded,
|
|
22
|
+
toggleTranscription,
|
|
23
|
+
} = useAudioPlayer(props)
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<template>
|
|
27
|
+
<div class="audio-player my-6 rounded-lg bg-gray-50 p-5 border border-gray-200" data-mdx-component="AudioPlayer">
|
|
28
|
+
<!-- Title -->
|
|
29
|
+
<div class="flex items-center gap-3 mb-3">
|
|
30
|
+
<svg
|
|
31
|
+
class="w-5 h-5 text-gray-500 flex-shrink-0"
|
|
32
|
+
fill="none"
|
|
33
|
+
stroke="currentColor"
|
|
34
|
+
viewBox="0 0 24 24"
|
|
35
|
+
>
|
|
36
|
+
<path
|
|
37
|
+
stroke-linecap="round"
|
|
38
|
+
stroke-linejoin="round"
|
|
39
|
+
stroke-width="2"
|
|
40
|
+
d="M15.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z"
|
|
41
|
+
/>
|
|
42
|
+
</svg>
|
|
43
|
+
<h3 v-if="hasTitle" class="text-base font-semibold text-gray-800 m-0">
|
|
44
|
+
{{ displayTitle }}
|
|
45
|
+
</h3>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<!-- Audio player -->
|
|
49
|
+
<audio
|
|
50
|
+
:src="audioUrl"
|
|
51
|
+
controls
|
|
52
|
+
preload="metadata"
|
|
53
|
+
class="w-full"
|
|
54
|
+
/>
|
|
55
|
+
|
|
56
|
+
<!-- Collapsible transcription -->
|
|
57
|
+
<div v-if="showTranscription" class="mt-4">
|
|
58
|
+
<button
|
|
59
|
+
class="flex items-center gap-2 text-sm text-gray-600 hover:text-gray-900 transition-colors cursor-pointer bg-transparent border-0 p-0"
|
|
60
|
+
@click="toggleTranscription"
|
|
61
|
+
>
|
|
62
|
+
<svg
|
|
63
|
+
class="w-4 h-4 transition-transform"
|
|
64
|
+
:class="{ 'rotate-90': transcriptionExpanded }"
|
|
65
|
+
fill="none"
|
|
66
|
+
stroke="currentColor"
|
|
67
|
+
viewBox="0 0 24 24"
|
|
68
|
+
>
|
|
69
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
70
|
+
</svg>
|
|
71
|
+
Transcription
|
|
72
|
+
</button>
|
|
73
|
+
<div
|
|
74
|
+
v-if="transcriptionExpanded"
|
|
75
|
+
class="mt-2 p-3 bg-white rounded border border-gray-200 text-sm text-gray-700 leading-relaxed max-h-64 overflow-y-auto"
|
|
76
|
+
>
|
|
77
|
+
<p v-if="transcriptionLoading" class="text-gray-400 italic">Loading transcription...</p>
|
|
78
|
+
<p v-else-if="!transcriptionText" class="text-gray-400 italic">No transcription available.</p>
|
|
79
|
+
<template v-else>
|
|
80
|
+
<div v-if="transcriptionSummary" class="mb-3 pb-3 border-b border-gray-200">
|
|
81
|
+
<div class="text-xs uppercase tracking-wide text-gray-400 mb-1">Summary</div>
|
|
82
|
+
<div class="text-gray-800">{{ transcriptionSummary }}</div>
|
|
83
|
+
</div>
|
|
84
|
+
<div class="whitespace-pre-wrap">{{ transcriptionText }}</div>
|
|
85
|
+
</template>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</template>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import { badgeStyles } from '@blogmcp/mdx-schema/styles'
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
label: string
|
|
7
|
+
color?: 'primary' | 'neutral' | 'success' | 'warning' | 'error'
|
|
8
|
+
variant?: 'solid' | 'soft' | 'outline'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(defineProps<Props>(), { color: 'neutral', variant: 'soft' })
|
|
12
|
+
|
|
13
|
+
// Variant × color class table lives in @blogmcp/mdx-schema/styles (shared with
|
|
14
|
+
// the React renderer). Explicit entries so Tailwind JIT sees every class.
|
|
15
|
+
const classes = computed(() =>
|
|
16
|
+
[
|
|
17
|
+
badgeStyles.base,
|
|
18
|
+
badgeStyles.variantColor[props.variant][props.color],
|
|
19
|
+
].join(' '),
|
|
20
|
+
)
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<span :class="classes" data-mdx-component="Badge">{{ label }}</span>
|
|
25
|
+
</template>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import { buttonStyles } from '@blogmcp/mdx-schema/styles'
|
|
4
|
+
// URL sanitizer — returns 'about:blank' for javascript:/data:text/html/etc.
|
|
5
|
+
// Component-level prop sanitization: DOMPurify only sees HTML output, never
|
|
6
|
+
// JSX props consumed by render code. See:
|
|
7
|
+
// competitor-monitor/docs/internal/sanitization-trust-contract.md
|
|
8
|
+
import { sanitizeUrl } from '@blogmcp/sanitize'
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
label: string
|
|
12
|
+
to?: string
|
|
13
|
+
variant?: 'solid' | 'outline' | 'soft' | 'ghost' | 'link'
|
|
14
|
+
color?: 'primary' | 'neutral' | 'success' | 'warning' | 'error'
|
|
15
|
+
size?: 'sm' | 'md' | 'lg'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
19
|
+
variant: 'solid',
|
|
20
|
+
color: 'primary',
|
|
21
|
+
size: 'md',
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const safeHref = computed(() => (props.to ? sanitizeUrl(props.to) : undefined))
|
|
25
|
+
|
|
26
|
+
// Variant × color × size class tables live in @blogmcp/mdx-schema/styles so
|
|
27
|
+
// the Vue and React renderers compose identical strings. Explicit (non-
|
|
28
|
+
// interpolated) entries so Tailwind's JIT sees every class at build time.
|
|
29
|
+
const classes = computed(() =>
|
|
30
|
+
[
|
|
31
|
+
buttonStyles.base,
|
|
32
|
+
buttonStyles.size[props.size],
|
|
33
|
+
buttonStyles.variantColor[props.variant][props.color],
|
|
34
|
+
].join(' '),
|
|
35
|
+
)
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<a v-if="safeHref" :href="safeHref" :class="classes" data-mdx-component="Button">{{ label }}</a>
|
|
40
|
+
<button v-else type="button" :class="classes" data-mdx-component="Button">{{ label }}</button>
|
|
41
|
+
</template>
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Callout Default Render Component
|
|
4
|
+
*
|
|
5
|
+
* Standard callout styling for blog display.
|
|
6
|
+
* Light theme optimized with accessible colors.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { computed } from 'vue'
|
|
10
|
+
import type { CalloutProps, CalloutType } from '@blogmcp/mdx-schema'
|
|
11
|
+
import { calloutStyles } from '@blogmcp/mdx-schema/styles'
|
|
12
|
+
|
|
13
|
+
const props = defineProps<CalloutProps>()
|
|
14
|
+
|
|
15
|
+
const effectiveType = computed<CalloutType>(() => props.type || 'info')
|
|
16
|
+
|
|
17
|
+
// Per-type color table shared with the React renderer via
|
|
18
|
+
// @blogmcp/mdx-schema/styles (calloutStyles.default).
|
|
19
|
+
const colorClasses = computed(() => calloutStyles.default.type[effectiveType.value])
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div
|
|
24
|
+
:class="[calloutStyles.default.base, colorClasses.bg, colorClasses.border, colorClasses.text]"
|
|
25
|
+
data-mdx-component="Callout"
|
|
26
|
+
>
|
|
27
|
+
<div class="flex items-start gap-3">
|
|
28
|
+
<!-- Icon -->
|
|
29
|
+
<div class="flex-shrink-0 mt-0.5">
|
|
30
|
+
<!-- Info icon -->
|
|
31
|
+
<svg
|
|
32
|
+
v-if="effectiveType === 'info'"
|
|
33
|
+
class="w-5 h-5"
|
|
34
|
+
:class="colorClasses.iconColor"
|
|
35
|
+
fill="none"
|
|
36
|
+
stroke="currentColor"
|
|
37
|
+
viewBox="0 0 24 24"
|
|
38
|
+
>
|
|
39
|
+
<path
|
|
40
|
+
stroke-linecap="round"
|
|
41
|
+
stroke-linejoin="round"
|
|
42
|
+
stroke-width="2"
|
|
43
|
+
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
44
|
+
/>
|
|
45
|
+
</svg>
|
|
46
|
+
<!-- Warning icon -->
|
|
47
|
+
<svg
|
|
48
|
+
v-else-if="effectiveType === 'warning'"
|
|
49
|
+
class="w-5 h-5"
|
|
50
|
+
:class="colorClasses.iconColor"
|
|
51
|
+
fill="none"
|
|
52
|
+
stroke="currentColor"
|
|
53
|
+
viewBox="0 0 24 24"
|
|
54
|
+
>
|
|
55
|
+
<path
|
|
56
|
+
stroke-linecap="round"
|
|
57
|
+
stroke-linejoin="round"
|
|
58
|
+
stroke-width="2"
|
|
59
|
+
d="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"
|
|
60
|
+
/>
|
|
61
|
+
</svg>
|
|
62
|
+
<!-- Tip icon -->
|
|
63
|
+
<svg
|
|
64
|
+
v-else-if="effectiveType === 'tip'"
|
|
65
|
+
class="w-5 h-5"
|
|
66
|
+
:class="colorClasses.iconColor"
|
|
67
|
+
fill="none"
|
|
68
|
+
stroke="currentColor"
|
|
69
|
+
viewBox="0 0 24 24"
|
|
70
|
+
>
|
|
71
|
+
<path
|
|
72
|
+
stroke-linecap="round"
|
|
73
|
+
stroke-linejoin="round"
|
|
74
|
+
stroke-width="2"
|
|
75
|
+
d="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"
|
|
76
|
+
/>
|
|
77
|
+
</svg>
|
|
78
|
+
<!-- Danger icon -->
|
|
79
|
+
<svg
|
|
80
|
+
v-else-if="effectiveType === 'danger'"
|
|
81
|
+
class="w-5 h-5"
|
|
82
|
+
:class="colorClasses.iconColor"
|
|
83
|
+
fill="none"
|
|
84
|
+
stroke="currentColor"
|
|
85
|
+
viewBox="0 0 24 24"
|
|
86
|
+
>
|
|
87
|
+
<path
|
|
88
|
+
stroke-linecap="round"
|
|
89
|
+
stroke-linejoin="round"
|
|
90
|
+
stroke-width="2"
|
|
91
|
+
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
92
|
+
/>
|
|
93
|
+
</svg>
|
|
94
|
+
</div>
|
|
95
|
+
<!-- Content -->
|
|
96
|
+
<div class="flex-1 text-sm leading-relaxed">
|
|
97
|
+
<slot />
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</template>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Callout Minimal Render Component
|
|
4
|
+
*
|
|
5
|
+
* Simple callout styling without icons.
|
|
6
|
+
* Clean, minimal design for content-focused blogs.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { computed } from 'vue'
|
|
10
|
+
import type { CalloutProps, CalloutType } from '@blogmcp/mdx-schema'
|
|
11
|
+
import { calloutStyles } from '@blogmcp/mdx-schema/styles'
|
|
12
|
+
|
|
13
|
+
const props = defineProps<CalloutProps>()
|
|
14
|
+
|
|
15
|
+
const effectiveType = computed<CalloutType>(() => props.type || 'info')
|
|
16
|
+
|
|
17
|
+
// Per-type color table shared with the React renderer via
|
|
18
|
+
// @blogmcp/mdx-schema/styles (calloutStyles.minimal).
|
|
19
|
+
const colorClasses = computed(() => calloutStyles.minimal.type[effectiveType.value])
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div
|
|
24
|
+
:class="[calloutStyles.minimal.base, colorClasses.bg, colorClasses.border, colorClasses.text]"
|
|
25
|
+
data-mdx-component="Callout"
|
|
26
|
+
>
|
|
27
|
+
<div class="text-sm leading-relaxed">
|
|
28
|
+
<slot />
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callout Render Component Variants
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { default as CalloutDefault } from './CalloutDefault.vue'
|
|
6
|
+
export { default as CalloutMinimal } from './CalloutMinimal.vue'
|
|
7
|
+
|
|
8
|
+
// Default export for convenience
|
|
9
|
+
export { default } from './CalloutDefault.vue'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
title?: string
|
|
4
|
+
}
|
|
5
|
+
defineProps<Props>()
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<section class="mdx-card rounded-lg border border-gray-200 bg-white p-5 shadow-sm" data-mdx-component="Card">
|
|
10
|
+
<h3 v-if="title" class="mb-3 text-lg font-semibold text-gray-900">{{ title }}</h3>
|
|
11
|
+
<div class="text-gray-700">
|
|
12
|
+
<slot />
|
|
13
|
+
</div>
|
|
14
|
+
</section>
|
|
15
|
+
</template>
|