comark 0.2.1 → 0.3.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 (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +12 -2
  3. package/dist/internal/frontmatter.d.ts +2 -1
  4. package/dist/internal/frontmatter.js +2 -2
  5. package/dist/internal/parse/auto-close/index.js +58 -23
  6. package/dist/internal/parse/token-processor.js +18 -3
  7. package/dist/internal/stringify/attributes.d.ts +37 -1
  8. package/dist/internal/stringify/attributes.js +96 -12
  9. package/dist/internal/stringify/handlers/a.js +3 -0
  10. package/dist/internal/stringify/handlers/code.js +1 -1
  11. package/dist/internal/stringify/handlers/del.js +1 -1
  12. package/dist/internal/stringify/handlers/html.js +12 -1
  13. package/dist/internal/stringify/handlers/img.js +1 -1
  14. package/dist/internal/stringify/handlers/li.js +14 -1
  15. package/dist/internal/stringify/handlers/math.js +1 -1
  16. package/dist/internal/stringify/handlers/mdc.js +1 -1
  17. package/dist/internal/stringify/handlers/ol.js +2 -2
  18. package/dist/internal/stringify/handlers/pre.js +1 -1
  19. package/dist/internal/stringify/handlers/template.js +1 -1
  20. package/dist/internal/stringify/handlers/ul.js +2 -2
  21. package/dist/internal/stringify/indent.d.ts +2 -1
  22. package/dist/internal/stringify/indent.js +3 -2
  23. package/dist/internal/stringify/state.d.ts +3 -3
  24. package/dist/internal/stringify/state.js +71 -15
  25. package/dist/internal/yaml.d.ts +2 -1
  26. package/dist/internal/yaml.js +3 -1
  27. package/dist/parse.js +13 -2
  28. package/dist/plugins/alert.js +1 -1
  29. package/dist/plugins/binding.d.ts +20 -0
  30. package/dist/plugins/binding.js +61 -0
  31. package/dist/plugins/breaks.d.ts +2 -0
  32. package/dist/plugins/breaks.js +34 -0
  33. package/dist/plugins/footnotes.d.ts +61 -0
  34. package/dist/plugins/footnotes.js +187 -0
  35. package/dist/plugins/highlight.js +6 -4
  36. package/dist/plugins/json-render.d.ts +1 -1
  37. package/dist/plugins/json-render.js +3 -3
  38. package/dist/plugins/punctuation.d.ts +67 -0
  39. package/dist/plugins/punctuation.js +236 -0
  40. package/dist/plugins/security.js +1 -1
  41. package/dist/render.d.ts +2 -1
  42. package/dist/render.js +3 -1
  43. package/dist/types.d.ts +71 -16
  44. package/dist/utils/index.d.ts +9 -0
  45. package/dist/utils/index.js +24 -0
  46. package/dist/vite.d.ts +1 -0
  47. package/dist/vite.js +1 -0
  48. package/package.json +22 -18
  49. package/skills/comark/AGENTS.md +261 -0
  50. package/skills/comark/SKILL.md +489 -0
  51. package/skills/comark/references/markdown-syntax.md +599 -0
  52. package/skills/comark/references/parsing-ast.md +378 -0
  53. package/skills/comark/references/rendering-react.md +445 -0
  54. package/skills/comark/references/rendering-svelte.md +453 -0
  55. package/skills/comark/references/rendering-vue.md +462 -0
  56. package/skills/skills/comark/AGENTS.md +261 -0
  57. package/skills/skills/comark/SKILL.md +489 -0
  58. package/skills/skills/comark/references/markdown-syntax.md +599 -0
  59. package/skills/skills/comark/references/parsing-ast.md +378 -0
  60. package/skills/skills/comark/references/rendering-react.md +445 -0
  61. package/skills/skills/comark/references/rendering-svelte.md +453 -0
  62. package/skills/skills/comark/references/rendering-vue.md +462 -0
  63. package/skills/skills/migrate-mdc-to-comark/SKILL.md +191 -0
  64. package/dist/utils/serialized-task.d.ts +0 -1
  65. package/dist/utils/serialized-task.js +0 -1
@@ -0,0 +1,462 @@
1
+ # Vue Rendering Guide
2
+
3
+ Complete guide for rendering Comark AST in Vue applications.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Basic Usage](#basic-usage)
8
+ - [Custom Components](#custom-components)
9
+ - [Dynamic Component Resolution](#dynamic-component-resolution)
10
+ - [Slots Support](#slots-support)
11
+ - [Streaming Mode](#streaming-mode)
12
+ - [Prose Components](#prose-components)
13
+ - [Error Handling](#error-handling)
14
+ - [Props Access](#props-access)
15
+
16
+ ---
17
+
18
+ ## Basic Usage
19
+
20
+ Use the `Comark` component to render markdown:
21
+
22
+ ```vue
23
+ <template>
24
+ <Comark>{{ content }}</Comark>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import { Comark } from '@comark/vue'
29
+
30
+ const content = `
31
+ # Hello World
32
+
33
+ This is **markdown** content.
34
+
35
+ ::alert{type="info"}
36
+ Important message
37
+ ::
38
+ `
39
+ </script>
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Custom Components
45
+
46
+ Map custom Vue components to Comark elements:
47
+
48
+ ```vue
49
+ <template>
50
+ <Comark :components="customComponents">{{ content }}</Comark>
51
+ </template>
52
+
53
+ <script setup lang="ts">
54
+ import { Comark } from '@comark/vue'
55
+ import CustomHeading from './CustomHeading.vue'
56
+ import CustomAlert from './CustomAlert.vue'
57
+ import CustomCard from './CustomCard.vue'
58
+
59
+ const customComponents = {
60
+ h1: CustomHeading,
61
+ h2: CustomHeading,
62
+ alert: CustomAlert,
63
+ card: CustomCard,
64
+ }
65
+ </script>
66
+ ```
67
+
68
+ ### Custom Component Example
69
+
70
+ ```vue
71
+ <!-- CustomHeading.vue -->
72
+ <template>
73
+ <component :is="tag" :id="id" class="custom-heading">
74
+ <slot />
75
+ </component>
76
+ </template>
77
+
78
+ <script setup lang="ts">
79
+ import { computed } from 'vue'
80
+
81
+ const props = defineProps<{
82
+ __node: any // Comark node
83
+ }>()
84
+
85
+ const tag = computed(() => props.__node[0])
86
+ const id = computed(() => props.__node[1]?.id)
87
+ </script>
88
+
89
+ <style scoped>
90
+ .custom-heading {
91
+ font-family: 'Inter', sans-serif;
92
+ font-weight: 700;
93
+ margin-bottom: 1rem;
94
+ }
95
+ </style>
96
+ ```
97
+
98
+ ### Alert Component Example
99
+
100
+ ```vue
101
+ <!-- CustomAlert.vue -->
102
+ <template>
103
+ <div :class="`alert alert-${type}`" role="alert">
104
+ <div class="alert-icon">
105
+ <Icon :name="iconName" />
106
+ </div>
107
+ <div class="alert-content">
108
+ <slot />
109
+ </div>
110
+ </div>
111
+ </template>
112
+
113
+ <script setup lang="ts">
114
+ import { computed } from 'vue'
115
+
116
+ const props = defineProps<{
117
+ type?: 'info' | 'warning' | 'error' | 'success'
118
+ __node?: any
119
+ }>()
120
+
121
+ const iconName = computed(() => {
122
+ switch (props.type) {
123
+ case 'info': return 'info-circle'
124
+ case 'warning': return 'exclamation-triangle'
125
+ case 'error': return 'times-circle'
126
+ case 'success': return 'check-circle'
127
+ default: return 'info-circle'
128
+ }
129
+ })
130
+ </script>
131
+
132
+ <style scoped>
133
+ .alert {
134
+ display: flex;
135
+ padding: 1rem;
136
+ border-radius: 0.5rem;
137
+ margin-bottom: 1rem;
138
+ }
139
+
140
+ .alert-info {
141
+ background-color: #e3f2fd;
142
+ color: #1976d2;
143
+ }
144
+
145
+ .alert-warning {
146
+ background-color: #fff3e0;
147
+ color: #f57c00;
148
+ }
149
+ </style>
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Dynamic Component Resolution
155
+
156
+ Load components dynamically using `componentsManifest`:
157
+
158
+ ```vue
159
+ <template>
160
+ <Comark
161
+ :components-manifest="loadComponent"
162
+ >{{ content }}</Comark>
163
+ </template>
164
+
165
+ <script setup lang="ts">
166
+ import { Comark } from '@comark/vue'
167
+
168
+ const componentMap = {
169
+ 'alert': () => import('./Alert.vue'),
170
+ 'card': () => import('./Card.vue'),
171
+ 'button': () => import('./Button.vue'),
172
+ }
173
+
174
+ async function loadComponent(name: string) {
175
+ if (componentMap[name]) {
176
+ return componentMap[name]()
177
+ }
178
+ throw new Error(`Component ${name} not found`)
179
+ }
180
+ </script>
181
+ ```
182
+
183
+ ---
184
+
185
+ ## Slots Support
186
+
187
+ Comark components with slots work seamlessly in Vue:
188
+
189
+ ### Markdown with Slots
190
+
191
+ ```markdown
192
+ ::card
193
+ #header
194
+ ## Card Title
195
+
196
+ #content
197
+ Main content here with **markdown** support
198
+
199
+ #footer
200
+ Footer text
201
+ ::
202
+ ```
203
+
204
+ ### Custom Component with Slots
205
+
206
+ ```vue
207
+ <!-- Card.vue -->
208
+ <template>
209
+ <div class="card">
210
+ <div v-if="$slots.header" class="card-header">
211
+ <slot name="header" />
212
+ </div>
213
+ <div class="card-content">
214
+ <slot name="content" />
215
+ <!-- Default slot as fallback -->
216
+ <slot />
217
+ </div>
218
+ <div v-if="$slots.footer" class="card-footer">
219
+ <slot name="footer" />
220
+ </div>
221
+ </div>
222
+ </template>
223
+
224
+ <style scoped>
225
+ .card {
226
+ border: 1px solid #e5e7eb;
227
+ border-radius: 0.5rem;
228
+ overflow: hidden;
229
+ }
230
+
231
+ .card-header {
232
+ background-color: #f9fafb;
233
+ padding: 1rem;
234
+ border-bottom: 1px solid #e5e7eb;
235
+ }
236
+
237
+ .card-content {
238
+ padding: 1rem;
239
+ }
240
+
241
+ .card-footer {
242
+ background-color: #f9fafb;
243
+ padding: 1rem;
244
+ border-top: 1px solid #e5e7eb;
245
+ }
246
+ </style>
247
+ ```
248
+
249
+ ### Nested Slots
250
+
251
+ ```markdown
252
+ ::tabs
253
+ #tab1
254
+ ### First Tab
255
+ Content for tab 1
256
+
257
+ #tab2
258
+ ### Second Tab
259
+ Content for tab 2
260
+ ::
261
+ ```
262
+
263
+ ```vue
264
+ <!-- Tabs.vue -->
265
+ <template>
266
+ <div class="tabs">
267
+ <div class="tab-headers">
268
+ <button
269
+ v-for="(slot, name) in $slots"
270
+ :key="name"
271
+ :class="{ active: activeTab === name }"
272
+ @click="activeTab = name"
273
+ >
274
+ {{ name }}
275
+ </button>
276
+ </div>
277
+ <div class="tab-content">
278
+ <component :is="() => $slots[activeTab]?.()" />
279
+ </div>
280
+ </div>
281
+ </template>
282
+
283
+ <script setup lang="ts">
284
+ import { ref } from 'vue'
285
+
286
+ const activeTab = ref('tab1')
287
+ </script>
288
+ ```
289
+
290
+ ---
291
+
292
+ ## Streaming Mode
293
+
294
+ The `Comark` component can be used with reactive content for streaming scenarios:
295
+
296
+ ```vue
297
+ <template>
298
+ <div>
299
+ <Comark>{{ content }}</Comark>
300
+ <div v-if="isLoading">Loading...</div>
301
+ </div>
302
+ </template>
303
+
304
+ <script setup lang="ts">
305
+ import { ref } from 'vue'
306
+ import { Comark } from '@comark/vue'
307
+
308
+ const content = ref('')
309
+ const isLoading = ref(true)
310
+
311
+ async function loadContent() {
312
+ const response = await fetch('/api/content.md')
313
+ const reader = response.body!.getReader()
314
+ const decoder = new TextDecoder()
315
+
316
+ while (true) {
317
+ const { done, value } = await reader.read()
318
+ if (done) break
319
+ content.value += decoder.decode(value)
320
+ }
321
+
322
+ isLoading.value = false
323
+ }
324
+
325
+ loadContent()
326
+ </script>
327
+ ```
328
+
329
+ ---
330
+
331
+ ---
332
+
333
+ ## Prose Components
334
+
335
+ The `Comark` component uses built-in prose styling automatically. You can override with custom components:
336
+
337
+ ```vue
338
+ <template>
339
+ <Comark :components="components">{{ content }}</Comark>
340
+ </template>
341
+
342
+ <script setup lang="ts">
343
+ import { Comark } from '@comark/vue'
344
+ import CustomAlert from './CustomAlert.vue'
345
+
346
+ const components = {
347
+ alert: CustomAlert, // Override or add custom components
348
+ }
349
+ </script>
350
+ ```
351
+
352
+ ---
353
+
354
+ ## Error Handling
355
+
356
+ The `ComarkRenderer` component has built-in error capture via Vue's `onErrorCaptured` hook. Component rendering errors are caught automatically without crashing the application. You can also use Vue's native `onErrorCaptured` in a parent component to handle errors:
357
+
358
+ ```vue
359
+ <template>
360
+ <Comark :markdown="content" />
361
+ </template>
362
+
363
+ <script setup lang="ts">
364
+ import { onErrorCaptured } from 'vue'
365
+ import { Comark } from '@comark/vue'
366
+
367
+ onErrorCaptured((error) => {
368
+ console.error('Component error:', error)
369
+ return false // prevent propagation
370
+ })
371
+ </script>
372
+ ```
373
+
374
+ ---
375
+
376
+ ## Props Access
377
+
378
+ Custom components receive the original Comark node and parsed props:
379
+
380
+ ```vue
381
+ <!-- CustomAlert.vue -->
382
+ <template>
383
+ <div :class="alertClasses" role="alert">
384
+ <slot />
385
+ </div>
386
+ </template>
387
+
388
+ <script setup lang="ts">
389
+ import { computed } from 'vue'
390
+
391
+ const props = defineProps<{
392
+ type?: string // From {type="info"}
393
+ bool?: boolean // From {bool} → :bool="true"
394
+ count?: number // From {:count="5"}
395
+ data?: object // From {:data='{"key":"val"}'}
396
+ __node?: any // Original Comark node
397
+ }>()
398
+
399
+ const alertClasses = computed(() => [
400
+ 'alert',
401
+ `alert-${props.type || 'info'}`,
402
+ { 'alert-important': props.bool }
403
+ ])
404
+ </script>
405
+ ```
406
+
407
+ ### Property Parsing Rules
408
+
409
+ - Props starting with `:` are parsed as booleans/JSON
410
+ - Standard HTML attributes work normally
411
+ - `__node` provides access to the raw AST node
412
+
413
+ ### Accessing Node Structure
414
+
415
+ ```vue
416
+ <script setup lang="ts">
417
+ const props = defineProps<{ __node?: any }>()
418
+
419
+ // Node structure: [tag, props, ...children]
420
+ const tag = computed(() => props.__node?.[0])
421
+ const nodeProps = computed(() => props.__node?.[1] || {})
422
+ const children = computed(() => props.__node?.slice(2) || [])
423
+ </script>
424
+ ```
425
+
426
+ ### Working with Complex Props
427
+
428
+ ```vue
429
+ <!-- DataTable.vue -->
430
+ <template>
431
+ <table>
432
+ <thead>
433
+ <tr>
434
+ <th v-for="col in columns" :key="col">{{ col }}</th>
435
+ </tr>
436
+ </thead>
437
+ <tbody>
438
+ <slot />
439
+ </tbody>
440
+ </table>
441
+ </template>
442
+
443
+ <script setup lang="ts">
444
+ const props = defineProps<{
445
+ columns?: string[] // From {:columns='["Name","Age"]'}
446
+ sortable?: boolean // From {sortable}
447
+ __node?: any
448
+ }>()
449
+ </script>
450
+ ```
451
+
452
+ **Usage in Markdown:**
453
+
454
+ ```markdown
455
+ ::data-table{:columns='["Name", "Age", "Email"]' sortable}
456
+ Table content here
457
+ ::
458
+ ```
459
+
460
+ ---
461
+
462
+ [← Back to Main Skills Guide](../SKILL.md)
@@ -0,0 +1,191 @@
1
+ ---
2
+ name: migrate-mdc-to-comark
3
+ description: >-
4
+ Migrate a Nuxt project from @nuxtjs/mdc to Comark. Covers package changes, parse/render API mapping, AST format, Nuxt module config, components, slots, plugins, and Nuxt UI integration.
5
+ ---
6
+
7
+ # Migrate from @nuxtjs/mdc to Comark
8
+
9
+ `@nuxtjs/mdc` was Nuxt-only. Comark is its successor — the markdown syntax is fully compatible, your `.md` files need no changes. What changes is the JavaScript API.
10
+
11
+ The migration has two parts: **Core Package** (programmatic API) and **Nuxt Module** (components, slots, config).
12
+
13
+ ## Quick Overview
14
+
15
+ - **Package**: `@nuxtjs/mdc` → `comark` (core only) or `@comark/nuxt` (Nuxt module)
16
+ - **Parse**: `parseMarkdown()` → `parse()` · factory: `createParse()` (sync, no await)
17
+ - **Render**: `stringifyMarkdown()` → `renderMarkdown()` from `comark/render`
18
+ - **AST**: object tree → compact tuples `['tag', props, ...children]`
19
+ - **Result**: `result.body` / `result.data` → `tree.nodes` / `tree.frontmatter`
20
+ - **Renderer**: `<MDCRenderer :body :data>` → `<ComarkRenderer :tree>`
21
+ - **All-in-one**: `<MDC :value>` → `<Comark :markdown>`
22
+ - **Slots**: `<MDCSlot />` → native `<slot />`
23
+ - **Plugins**: global `nuxt.config` → per-component `defineComarkComponent({ plugins })`
24
+ - **Markdown files**: no changes needed
25
+
26
+ ## Core Package
27
+
28
+ ### API Mapping
29
+
30
+ | `@nuxtjs/mdc` | `comark` |
31
+ |---|---|
32
+ | `parseMarkdown(md, opts)` | `parse(md, opts)` from `comark` |
33
+ | `createMarkdownParser(opts)` (async) | `createParse(opts)` (sync — no await) |
34
+ | `stringifyMarkdown(body, data)` | `renderMarkdown(tree)` from `comark/render` |
35
+ | `result.body` (`MDCRoot`) | `tree.nodes` (`ComarkNode[]`) |
36
+ | `result.data` | `tree.frontmatter` |
37
+ | `result.data.title` | `tree.frontmatter.title` |
38
+ | `result.toc` | `tree.meta.toc` (requires `toc` plugin) |
39
+ | `result.excerpt` | `tree.meta.summary` (requires `summary` plugin) |
40
+
41
+ ### AST Format
42
+
43
+ | `@nuxtjs/mdc` | `comark` |
44
+ |---|---|
45
+ | `{ type: 'root', children: MDCNode[] }` | `{ nodes: ComarkNode[], frontmatter: {}, meta: {} }` |
46
+ | `{ type: 'element', tag: 'p', props: {}, children: [] }` | `['p', {}, ...children]` |
47
+ | `{ type: 'text', value: 'hello' }` | `'hello'` (plain string) |
48
+
49
+ ### Parse Options
50
+
51
+ ```typescript
52
+ // Before — MDCParseOptions
53
+ {
54
+ remark: { plugins: { /* record */ } },
55
+ rehype: { options: {...}, plugins: { /* record */ } },
56
+ highlight: { theme: '...', langs: [...] } | false,
57
+ toc: { depth: 3, searchDepth: 2 } | false,
58
+ }
59
+
60
+ // After — ParseOptions
61
+ {
62
+ plugins: ComarkPlugin[], // ordered array, not a record
63
+ autoUnwrap: true, // removes <p> from single-paragraph containers
64
+ autoClose: true, // completes incomplete syntax (useful for streaming)
65
+ html: true, // parse embedded HTML tags
66
+ }
67
+ ```
68
+
69
+ ### Plugins
70
+
71
+ The `unified`/`remark`/`rehype` pipeline is replaced by Comark's own lighter plugin interface.
72
+
73
+ | Feature | Before | After |
74
+ |---|---|---|
75
+ | Syntax highlighting | `rehypeHighlight` via `createMarkdownParser` | `highlight()` from `comark/plugins/highlight` |
76
+ | Table of Contents | `parseMarkdown(md, { toc: { depth: 3 } })` | `toc({ depth: 3 })` plugin |
77
+ | Excerpt / Summary | `result.excerpt` (built-in) | `summary()` plugin → `tree.meta.summary` |
78
+ | Emoji | `remark-emoji` (enabled by default) | `emoji()` plugin (opt-in) |
79
+
80
+ Available plugins: `comark/plugins/toc`, `comark/plugins/highlight`, `comark/plugins/emoji`, `comark/plugins/task-list`, `comark/plugins/summary`, `comark/plugins/security`, `comark/plugins/alert`, `comark/plugins/math`, `comark/plugins/mermaid`, `comark/plugins/punctuation`
81
+
82
+ ## Nuxt Module
83
+
84
+ ### Configuration
85
+
86
+ ```typescript
87
+ // Before
88
+ export default defineNuxtConfig({
89
+ modules: ['@nuxtjs/mdc'],
90
+ mdc: { highlight: { ... }, remarkPlugins: { ... } },
91
+ })
92
+
93
+ // After
94
+ export default defineNuxtConfig({
95
+ modules: ['@comark/nuxt'],
96
+ // No plugin config here — plugins are defined per-component
97
+ })
98
+ ```
99
+
100
+ `@comark/nuxt` auto-imports: `Comark`, `ComarkRenderer`, `defineComarkComponent`, `defineComarkRendererComponent`.
101
+
102
+ ### Components
103
+
104
+ | `@nuxtjs/mdc` | `@comark/nuxt` |
105
+ |---|---|
106
+ | `<MDCRenderer :body :data :components>` | `<ComarkRenderer :tree :components>` |
107
+ | `<MDC :value :parser-options>` | `<Comark :markdown :options>` or `<Comark>{{ md }}</Comark>` |
108
+ | `<MDCSlot />` | `<slot />` |
109
+ | `<MDCSlot unwrap="p" />` | `<slot unwrap="p" />` |
110
+ | `<slot mdc-unwrap="p" />` | `<slot unwrap="p" />` |
111
+
112
+ For a pre-parsed tree, use `<ComarkRenderer>` directly instead of `<Comark>`.
113
+
114
+ #### `<ComarkRenderer>` props changes
115
+
116
+ | MDC `<MDCRenderer>` | Comark `<ComarkRenderer>` | Notes |
117
+ |---|---|---|
118
+ | `body` (`MDCRoot`) | `tree` (`ComarkTree`) | Different AST shape |
119
+ | `data` | — | Frontmatter is in `tree.frontmatter` |
120
+ | `tag` | — | Wrapper is always `<div class="comark-content">` |
121
+ | `prose` | — | `Prose*` resolution is automatic |
122
+ | `unwrap` | — | Use `autoUnwrap` in parse options |
123
+ | `components` | `components` | Same purpose |
124
+ | — | `componentsManifest` | New: dynamic async component resolver |
125
+ | — | `streaming` | New: streaming mode |
126
+ | — | `caret` | New: animated caret for streaming |
127
+
128
+ #### Summary rendering
129
+
130
+ ```vue
131
+ <!-- Before -->
132
+ <MDCRenderer :body="result.excerpt ?? result.body" :data="result.data" />
133
+
134
+ <!-- After -->
135
+ <Comark summary>{{ markdown }}</Comark>
136
+ ```
137
+
138
+ ### `defineComarkComponent`
139
+
140
+ Replaces global `mdc: { ... }` config. Define reusable components with their own plugins and component mappings:
141
+
142
+ ```typescript
143
+ import { defineComarkComponent } from '@comark/vue'
144
+ import highlight from 'comark/plugins/highlight'
145
+ import toc from 'comark/plugins/toc'
146
+
147
+ export const ArticleComark = defineComarkComponent({
148
+ name: 'ArticleComark',
149
+ plugins: [highlight({ themes: { light: githubLight, dark: githubDark } }), toc()],
150
+ components: { alert: CustomAlert },
151
+ })
152
+ ```
153
+
154
+ ### Slots
155
+
156
+ `<MDCSlot />` → native `<slot />`. Named slots work the same (`#slotName` in markdown, `<slot name="slotName">` in component). The `unwrap` attribute (`<slot unwrap="p">`) strips wrapper tags from children.
157
+
158
+ ### Prose Components
159
+
160
+ Same `Prose*.vue` naming convention in `components/prose/`. Resolution changed from kebab-case (`prose-p`) to PascalCase (`ProseP`) internally — no file changes needed.
161
+
162
+ ### Nuxt UI Integration
163
+
164
+ When using Nuxt UI, `@comark/nuxt` registers Nuxt UI prose components automatically. Shorthand callout components are available:
165
+
166
+ ```mdc
167
+ ::note — informational
168
+ ::tip — helpful suggestion
169
+ ::warning — something to watch out for
170
+ ::caution — critical warning
171
+ ```
172
+
173
+ These are **only available with Nuxt UI**. Without it, use `::callout{icon="..." color="..."}`.
174
+
175
+ ## Common Pitfalls
176
+
177
+ 1. **`createParse` is sync** — no `await` needed (unlike `createMarkdownParser`)
178
+ 2. **Attribute naming**: Comark uses `attrs.lang`, not `attrs.language`
179
+ 3. **Frontmatter**: stored in `tree.frontmatter`, not passed separately
180
+ 4. **`renderMarkdown` includes frontmatter** — reads `tree.frontmatter` automatically
181
+ 5. **No `unified` pipeline** — `mdc.config.ts` hooks (`pre`, `remark`, `rehype`, `post`) have no equivalent, use `ComarkPlugin` interface instead
182
+ 6. **Emoji is opt-in** — not enabled by default like in MDC's `remark-emoji`
183
+
184
+ ## Component Syntax
185
+
186
+ The MDC block and inline component syntax is identical — no changes needed in `.md` files.
187
+
188
+ ## Unsupported Features
189
+
190
+ - **Binding syntax** (`{{ variable }}`) — not supported, rendered as plain text
191
+ - **Props binding / data passing** — no equivalent for `parseMarkdown(md, { data: { ... } })`
@@ -1 +0,0 @@
1
- export * from '../../src/utils/serialized-task'
@@ -1 +0,0 @@
1
- export * from '../../src/utils/serialized-task.ts'