comark 0.3.1 → 0.3.2

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 (80) hide show
  1. package/dist/devtools/index.d.ts +1 -0
  2. package/dist/devtools/index.js +1 -0
  3. package/dist/devtools/register.d.ts +1 -0
  4. package/dist/devtools/register.js +1 -0
  5. package/dist/devtools/registry.d.ts +1 -0
  6. package/dist/devtools/registry.js +1 -0
  7. package/dist/devtools/vite.d.ts +1 -0
  8. package/dist/devtools/vite.js +1 -0
  9. package/dist/internal/frontmatter.d.ts +1 -0
  10. package/dist/internal/frontmatter.js +4 -2
  11. package/dist/internal/parse/auto-close/index.js +25 -13
  12. package/dist/internal/parse/auto-close/table.js +12 -9
  13. package/dist/internal/parse/auto-unwrap.js +2 -10
  14. package/dist/internal/parse/html/html_block_rule.js +1 -1
  15. package/dist/internal/parse/html/html_inline_rule.js +3 -7
  16. package/dist/internal/parse/html/html_re.js +1 -1
  17. package/dist/internal/parse/html/index.js +14 -2
  18. package/dist/internal/parse/syntax/block-params.d.ts +9 -0
  19. package/dist/internal/parse/syntax/block-params.js +48 -0
  20. package/dist/internal/parse/syntax/brackets.d.ts +8 -0
  21. package/dist/internal/parse/syntax/brackets.js +20 -0
  22. package/dist/internal/parse/syntax/props.d.ts +5 -0
  23. package/dist/internal/parse/syntax/props.js +119 -0
  24. package/dist/internal/parse/token-processor.js +25 -24
  25. package/dist/internal/props-validation.js +4 -9
  26. package/dist/internal/stringify/attributes.js +4 -1
  27. package/dist/internal/stringify/handlers/a.js +1 -3
  28. package/dist/internal/stringify/handlers/blockquote.js +2 -4
  29. package/dist/internal/stringify/handlers/code.js +1 -3
  30. package/dist/internal/stringify/handlers/emphesis.js +1 -3
  31. package/dist/internal/stringify/handlers/html.js +26 -16
  32. package/dist/internal/stringify/handlers/img.js +1 -3
  33. package/dist/internal/stringify/handlers/li.js +14 -8
  34. package/dist/internal/stringify/handlers/mdc.js +2 -3
  35. package/dist/internal/stringify/handlers/ol.js +1 -1
  36. package/dist/internal/stringify/handlers/p.d.ts +1 -1
  37. package/dist/internal/stringify/handlers/p.js +4 -1
  38. package/dist/internal/stringify/handlers/pre.js +10 -13
  39. package/dist/internal/stringify/handlers/strong.js +1 -3
  40. package/dist/internal/stringify/handlers/table.js +7 -5
  41. package/dist/internal/stringify/handlers/template.js +1 -1
  42. package/dist/internal/stringify/handlers/ul.js +1 -1
  43. package/dist/internal/stringify/indent.d.ts +1 -5
  44. package/dist/internal/stringify/indent.js +1 -9
  45. package/dist/internal/stringify/state.js +1 -1
  46. package/dist/internal/yaml.js +1 -1
  47. package/dist/parse.js +14 -8
  48. package/dist/plugins/alert.js +1 -1
  49. package/dist/plugins/binding.js +1 -3
  50. package/dist/plugins/breaks.js +1 -1
  51. package/dist/plugins/emoji.js +8 -8
  52. package/dist/plugins/footnotes.js +19 -13
  53. package/dist/plugins/headings.js +2 -4
  54. package/dist/plugins/highlight.d.ts +1 -11
  55. package/dist/plugins/highlight.js +198 -103
  56. package/dist/plugins/json-render.js +5 -9
  57. package/dist/plugins/math.js +4 -6
  58. package/dist/plugins/mermaid.js +6 -20
  59. package/dist/plugins/punctuation.js +5 -6
  60. package/dist/plugins/security.js +2 -2
  61. package/dist/plugins/syntax.d.ts +49 -0
  62. package/dist/plugins/syntax.js +522 -0
  63. package/dist/plugins/task-list.d.ts +1 -1
  64. package/dist/plugins/task-list.js +11 -8
  65. package/dist/plugins/toc.js +1 -1
  66. package/dist/types.d.ts +1 -0
  67. package/dist/utils/comark.tmLanguage.d.ts +335 -0
  68. package/dist/utils/comark.tmLanguage.js +597 -0
  69. package/dist/utils/helpers.js +1 -3
  70. package/dist/utils/index.d.ts +5 -0
  71. package/dist/utils/index.js +25 -3
  72. package/package.json +39 -40
  73. package/skills/skills/comark/AGENTS.md +0 -261
  74. package/skills/skills/comark/SKILL.md +0 -489
  75. package/skills/skills/comark/references/markdown-syntax.md +0 -599
  76. package/skills/skills/comark/references/parsing-ast.md +0 -378
  77. package/skills/skills/comark/references/rendering-react.md +0 -445
  78. package/skills/skills/comark/references/rendering-svelte.md +0 -453
  79. package/skills/skills/comark/references/rendering-vue.md +0 -462
  80. /package/skills/{skills/migrate-mdc-to-comark → migrate-mdc-to-comark}/SKILL.md +0 -0
@@ -38,10 +38,12 @@ visitor) {
38
38
  const res = visitor(node);
39
39
  if (res === false) {
40
40
  // remove the node from the parent
41
+ ;
41
42
  parent.splice(index, 1);
42
43
  return true; // signal that node was removed
43
44
  }
44
45
  if (res !== undefined) {
46
+ ;
45
47
  parent[index] = res;
46
48
  currentNode = res;
47
49
  }
@@ -72,13 +74,29 @@ visitor) {
72
74
  }
73
75
  }
74
76
  // #region String Utils
77
+ export function indent(text, { ignoreFirstLine = false, level = 1, width } = {}) {
78
+ const pad = width ? ' '.repeat(width) : ' '.repeat(level);
79
+ return text
80
+ .split('\n')
81
+ .map((line, index) => {
82
+ if (ignoreFirstLine && index === 0) {
83
+ return line;
84
+ }
85
+ return line ? pad + line : line;
86
+ })
87
+ .join('\n');
88
+ }
75
89
  /**
76
90
  * Convert a string to pascal case
77
91
  * @param str - The string to convert
78
92
  * @returns The pascal case string
79
93
  */
80
94
  export function pascalCase(str) {
81
- return str ? splitByCase(str).map(p => p[0].toUpperCase() + p.slice(1)).join('') : '';
95
+ return str
96
+ ? splitByCase(str)
97
+ .map((p) => (p ? p[0].toUpperCase() + p.slice(1) : ''))
98
+ .join('')
99
+ : '';
82
100
  }
83
101
  /**
84
102
  * Convert a string to kebab case
@@ -86,7 +104,11 @@ export function pascalCase(str) {
86
104
  * @returns The kebab case string
87
105
  */
88
106
  export function kebabCase(str) {
89
- return str ? splitByCase(str).map(p => p.toLowerCase()).join('-') : '';
107
+ return str
108
+ ? splitByCase(str)
109
+ .map((p) => p.toLowerCase())
110
+ .join('-')
111
+ : '';
90
112
  }
91
113
  /**
92
114
  * Convert a string to camel case
@@ -123,7 +145,7 @@ function splitByCase(str) {
123
145
  const charCode = char.charCodeAt(0);
124
146
  const isNumber = charCode >= 48 && charCode <= 57; // '0' to '9'
125
147
  // Fast uppercase check using character codes
126
- const isUpper = isNumber ? void 0 : (charCode >= 65 && charCode <= 90); // 'A' to 'Z'
148
+ const isUpper = isNumber ? void 0 : charCode >= 65 && charCode <= 90; // 'A' to 'Z'
127
149
  if (previousSplitter === false) {
128
150
  if (previousUpper === false && isUpper === true) {
129
151
  parts.push(buff);
package/package.json CHANGED
@@ -1,26 +1,33 @@
1
1
  {
2
2
  "name": "comark",
3
- "type": "module",
4
- "version": "0.3.1",
3
+ "version": "0.3.2",
5
4
  "description": "Components in Markdown (Comark) parser with streaming support for Vue, React, Svelte and HTML",
6
- "author": "",
7
- "license": "MIT",
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/comarkdown/comark.git"
11
- },
12
- "bugs": {
13
- "url": "https://github.com/comarkdown/comark/issues"
14
- },
15
- "homepage": "https://comark.dev",
16
5
  "keywords": [
17
6
  "markdown",
18
7
  "mdc",
19
8
  "parser",
9
+ "react",
20
10
  "streaming",
21
- "vue",
22
- "react"
11
+ "vue"
12
+ ],
13
+ "homepage": "https://comark.dev",
14
+ "bugs": {
15
+ "url": "https://github.com/comarkdown/comark/issues"
16
+ },
17
+ "license": "MIT",
18
+ "author": "",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/comarkdown/comark.git"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "skills"
23
26
  ],
27
+ "type": "module",
28
+ "main": "./dist/index.js",
29
+ "module": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
24
31
  "exports": {
25
32
  ".": "./dist/index.js",
26
33
  "./plugins/*": "./dist/plugins/*.js",
@@ -28,16 +35,27 @@
28
35
  "./parse": "./dist/parse.js",
29
36
  "./render": "./dist/render.js"
30
37
  },
31
- "main": "./dist/index.js",
32
- "module": "./dist/index.js",
33
- "types": "./dist/index.d.ts",
34
- "files": [
35
- "dist",
36
- "skills"
37
- ],
38
38
  "publishConfig": {
39
39
  "access": "public"
40
40
  },
41
+ "dependencies": {
42
+ "entities": "^8.0.0",
43
+ "htmlparser2": "^12.0.0",
44
+ "js-yaml": "^4.1.1",
45
+ "markdown-exit": "1.0.0-beta.9"
46
+ },
47
+ "devDependencies": {
48
+ "@json-render/core": "^0.16.0",
49
+ "@shikijs/twoslash": "^4.0.2",
50
+ "@types/js-yaml": "^4.0.9",
51
+ "@types/markdown-it": "^14.1.2",
52
+ "github-slugger": "^2.0.0",
53
+ "hast-util-to-string": "^3.0.1",
54
+ "minimark": "0.2.0",
55
+ "tsx": "^4.21.0",
56
+ "twoslash": "^0.3.6",
57
+ "vitest": "^4.1.4"
58
+ },
41
59
  "peerDependencies": {
42
60
  "beautiful-mermaid": "^1.1.3",
43
61
  "katex": "^0.16.45",
@@ -54,25 +72,6 @@
54
72
  "optional": true
55
73
  }
56
74
  },
57
- "devDependencies": {
58
- "@json-render/core": "^0.16.0",
59
- "@shikijs/twoslash": "^4.0.2",
60
- "@types/js-yaml": "^4.0.9",
61
- "@types/markdown-it": "^14.1.2",
62
- "github-slugger": "^2.0.0",
63
- "hast-util-to-string": "^3.0.1",
64
- "minimark": "0.2.0",
65
- "tsx": "^4.21.0",
66
- "twoslash": "^0.3.6",
67
- "vitest": "^4.1.4"
68
- },
69
- "dependencies": {
70
- "@comark/markdown-it": "^0.3.4",
71
- "entities": "^8.0.0",
72
- "htmlparser2": "^12.0.0",
73
- "js-yaml": "^4.1.1",
74
- "markdown-exit": "1.0.0-beta.9"
75
- },
76
75
  "scripts": {
77
76
  "stub": "node ../../scripts/stub.mjs",
78
77
  "build": "tsc",
@@ -1,261 +0,0 @@
1
- # Comark — AI Agents & LLM Streaming
2
-
3
- A guide for using Comark in AI agent and LLM-powered applications where markdown is generated incrementally by a language model.
4
-
5
- ## Why Comark for AI?
6
-
7
- LLMs stream markdown token-by-token. Standard markdown parsers expect complete input — they fail or produce broken output on partial streams. Comark was built to handle exactly this:
8
-
9
- - **`autoClose`** (default: `true`) — incomplete syntax like `**bold text` is automatically closed on every parse, so partial tokens always render correctly
10
- - **Streaming mode** — re-renders efficiently as content arrives
11
- - **Caret indicator** — shows a live cursor during generation
12
- - **ANSI rendering** — styled terminal output for CLI agents
13
-
14
- ---
15
-
16
- ## Vue
17
-
18
- ```vue
19
- <script setup lang="ts">
20
- import { ref } from 'vue'
21
- import { Comark } from '@comark/vue'
22
-
23
- const content = ref('')
24
- const streaming = ref(false)
25
-
26
- async function generate(prompt: string) {
27
- content.value = ''
28
- streaming.value = true
29
-
30
- const res = await fetch('/api/chat', {
31
- method: 'POST',
32
- body: JSON.stringify({ prompt }),
33
- })
34
-
35
- const reader = res.body!.getReader()
36
- const decoder = new TextDecoder()
37
-
38
- while (true) {
39
- const { done, value } = await reader.read()
40
- if (done) break
41
- content.value += decoder.decode(value, { stream: true })
42
- }
43
-
44
- streaming.value = false
45
- }
46
- </script>
47
-
48
- <template>
49
- <Comark :streaming="streaming" caret>{{ content }}</Comark>
50
- </template>
51
- ```
52
-
53
- ---
54
-
55
- ## React
56
-
57
- ```tsx
58
- import { useState } from 'react'
59
- import { Comark } from '@comark/react'
60
-
61
- export default function Chat() {
62
- const [content, setContent] = useState('')
63
- const [streaming, setStreaming] = useState(false)
64
-
65
- async function generate(prompt: string) {
66
- setContent('')
67
- setStreaming(true)
68
-
69
- const res = await fetch('/api/chat', {
70
- method: 'POST',
71
- body: JSON.stringify({ prompt }),
72
- })
73
-
74
- const reader = res.body!.getReader()
75
- const decoder = new TextDecoder()
76
-
77
- while (true) {
78
- const { done, value } = await reader.read()
79
- if (done) break
80
- setContent(prev => prev + decoder.decode(value, { stream: true }))
81
- }
82
-
83
- setStreaming(false)
84
- }
85
-
86
- return <Comark streaming={streaming} caret>{content}</Comark>
87
- }
88
- ```
89
-
90
- ---
91
-
92
- ## Svelte
93
-
94
- ```svelte
95
- <script lang="ts">
96
- import { Comark } from '@comark/svelte'
97
-
98
- let content = $state('')
99
- let streaming = $state(false)
100
-
101
- async function generate(prompt: string) {
102
- content = ''
103
- streaming = true
104
-
105
- const res = await fetch('/api/chat', {
106
- method: 'POST',
107
- body: JSON.stringify({ prompt }),
108
- })
109
-
110
- const reader = res.body!.getReader()
111
- const decoder = new TextDecoder()
112
-
113
- while (true) {
114
- const { done, value } = await reader.read()
115
- if (done) break
116
- content += decoder.decode(value, { stream: true })
117
- }
118
-
119
- streaming = false
120
- }
121
- </script>
122
-
123
- <Comark markdown={content} {streaming} caret />
124
- ```
125
-
126
- ---
127
-
128
- ## Terminal / CLI Agents
129
-
130
- Use `@comark/ansi` to render LLM markdown output in terminal-based agents:
131
-
132
- ```typescript
133
- import { log } from '@comark/ansi'
134
-
135
- // Print a complete LLM response to stdout with ANSI styling
136
- await log(llmResponse)
137
- ```
138
-
139
- For streaming terminal output, use `createLog` with a custom `write` function:
140
-
141
- ```typescript
142
- import { createLog } from '@comark/ansi'
143
-
144
- const logStream = createLog({
145
- write: (s) => process.stdout.write(s),
146
- })
147
-
148
- // Call after each chunk to show partial output
149
- await logStream(partialMarkdown)
150
- ```
151
-
152
- ---
153
-
154
- ## Caret Customization
155
-
156
- The `caret` prop appends a blinking cursor to the last text node while `streaming` is `true`. Customize it with a CSS class:
157
-
158
- ```vue
159
- <Comark :streaming="streaming" :caret="{ class: 'animate-blink' }">{{ content }}</Comark>
160
- ```
161
-
162
- ```tsx
163
- <Comark streaming={streaming} caret={{ class: 'animate-blink' }}>{content}</Comark>
164
- ```
165
-
166
- ```svelte
167
- <Comark markdown={content} {streaming} caret={{ class: 'animate-blink' }} />
168
- ```
169
-
170
- ---
171
-
172
- ## With Custom Components
173
-
174
- If your LLM produces Comark component syntax (e.g., `::alert`), register components before streaming begins:
175
-
176
- ```vue
177
- <script setup lang="ts">
178
- import { Comark } from '@comark/vue'
179
- import Alert from './Alert.vue'
180
- import CodeBlock from './CodeBlock.vue'
181
- </script>
182
-
183
- <template>
184
- <Comark :components="{ alert: Alert, pre: CodeBlock }" :streaming="streaming" caret>
185
- {{ content }}
186
- </Comark>
187
- </template>
188
- ```
189
-
190
- ---
191
-
192
- ## With Syntax Highlighting
193
-
194
- Syntax highlighting works during streaming — each re-parse will highlight newly completed code blocks:
195
-
196
- ```vue
197
- <script setup lang="ts">
198
- import { Comark } from '@comark/vue'
199
- import highlight from '@comark/vue/plugins/highlight'
200
- import githubDark from '@shikijs/themes/github-dark'
201
-
202
- const plugins = [highlight({ themes: { light: githubDark, dark: githubDark } })]
203
- </script>
204
-
205
- <template>
206
- <Suspense>
207
- <Comark :plugins="plugins" :streaming="streaming" caret>{{ content }}</Comark>
208
- </Suspense>
209
- </template>
210
- ```
211
-
212
- ```tsx
213
- import { Comark } from '@comark/react'
214
- import highlight from '@comark/react/plugins/highlight'
215
- import githubDark from '@shikijs/themes/github-dark'
216
-
217
- const plugins = [highlight({ themes: { light: githubDark, dark: githubDark } })]
218
-
219
- export default function Chat({ content, streaming }) {
220
- return (
221
- <Comark plugins={plugins} streaming={streaming} caret>
222
- {content}
223
- </Comark>
224
- )
225
- }
226
- ```
227
-
228
- ---
229
-
230
- ## defineComarkComponent for AI Chat
231
-
232
- Pre-configure a Comark component for your AI chat UI once, then reuse it everywhere:
233
-
234
- ```typescript
235
- // comark.ts
236
- import { defineComarkComponent } from '@comark/vue'
237
- import highlight from '@comark/vue/plugins/highlight'
238
- import math, { Math } from '@comark/vue/plugins/math'
239
- import githubDark from '@shikijs/themes/github-dark'
240
- import Alert from './components/Alert.vue'
241
-
242
- export const ChatComark = defineComarkComponent({
243
- name: 'ChatComark',
244
- plugins: [
245
- math(),
246
- highlight({ themes: { light: githubDark, dark: githubDark } }),
247
- ],
248
- components: { Math, alert: Alert },
249
- autoClose: true,
250
- })
251
- ```
252
-
253
- ```vue
254
- <template>
255
- <ChatComark :streaming="streaming" caret>{{ content }}</ChatComark>
256
- </template>
257
- ```
258
-
259
- ---
260
-
261
- [← Back to Skills Guide](./SKILL.md)