comark 0.0.1 → 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 (112) hide show
  1. package/README.md +104 -0
  2. package/dist/index.d.ts +4 -0
  3. package/dist/index.js +6 -0
  4. package/dist/internal/frontmatter.d.ts +16 -0
  5. package/dist/internal/frontmatter.js +43 -0
  6. package/dist/internal/parse/auto-close/index.d.ts +12 -0
  7. package/dist/internal/parse/auto-close/index.js +457 -0
  8. package/dist/internal/parse/auto-close/table.d.ts +4 -0
  9. package/dist/internal/parse/auto-close/table.js +161 -0
  10. package/dist/internal/parse/auto-unwrap.d.ts +20 -0
  11. package/dist/internal/parse/auto-unwrap.js +42 -0
  12. package/dist/internal/parse/html/html_block_rule.d.ts +2 -0
  13. package/dist/internal/parse/html/html_block_rule.js +60 -0
  14. package/dist/internal/parse/html/html_blocks.d.ts +2 -0
  15. package/dist/internal/parse/html/html_blocks.js +66 -0
  16. package/dist/internal/parse/html/html_inline_rule.d.ts +2 -0
  17. package/dist/internal/parse/html/html_inline_rule.js +43 -0
  18. package/dist/internal/parse/html/html_re.d.ts +3 -0
  19. package/dist/internal/parse/html/html_re.js +18 -0
  20. package/dist/internal/parse/html/index.d.ts +18 -0
  21. package/dist/internal/parse/html/index.js +122 -0
  22. package/dist/internal/parse/incremental.d.ts +12 -0
  23. package/dist/internal/parse/incremental.js +39 -0
  24. package/dist/internal/parse/token-processor.d.ts +9 -0
  25. package/dist/internal/parse/token-processor.js +803 -0
  26. package/dist/internal/props-validation.d.ts +12 -0
  27. package/dist/internal/props-validation.js +112 -0
  28. package/dist/internal/stringify/attributes.d.ts +21 -0
  29. package/dist/internal/stringify/attributes.js +67 -0
  30. package/dist/internal/stringify/handlers/a.d.ts +3 -0
  31. package/dist/internal/stringify/handlers/a.js +11 -0
  32. package/dist/internal/stringify/handlers/blockquote.d.ts +3 -0
  33. package/dist/internal/stringify/handlers/blockquote.js +18 -0
  34. package/dist/internal/stringify/handlers/br.d.ts +3 -0
  35. package/dist/internal/stringify/handlers/br.js +3 -0
  36. package/dist/internal/stringify/handlers/code.d.ts +3 -0
  37. package/dist/internal/stringify/handlers/code.js +11 -0
  38. package/dist/internal/stringify/handlers/comment.d.ts +3 -0
  39. package/dist/internal/stringify/handlers/comment.js +6 -0
  40. package/dist/internal/stringify/handlers/del.d.ts +3 -0
  41. package/dist/internal/stringify/handlers/del.js +4 -0
  42. package/dist/internal/stringify/handlers/emphesis.d.ts +3 -0
  43. package/dist/internal/stringify/handlers/emphesis.js +13 -0
  44. package/dist/internal/stringify/handlers/heading.d.ts +3 -0
  45. package/dist/internal/stringify/handlers/heading.js +7 -0
  46. package/dist/internal/stringify/handlers/hr.d.ts +3 -0
  47. package/dist/internal/stringify/handlers/hr.js +3 -0
  48. package/dist/internal/stringify/handlers/html.d.ts +3 -0
  49. package/dist/internal/stringify/handlers/html.js +73 -0
  50. package/dist/internal/stringify/handlers/img.d.ts +3 -0
  51. package/dist/internal/stringify/handlers/img.js +9 -0
  52. package/dist/internal/stringify/handlers/index.d.ts +2 -0
  53. package/dist/internal/stringify/handlers/index.js +56 -0
  54. package/dist/internal/stringify/handlers/li.d.ts +3 -0
  55. package/dist/internal/stringify/handlers/li.js +43 -0
  56. package/dist/internal/stringify/handlers/math.d.ts +3 -0
  57. package/dist/internal/stringify/handlers/math.js +8 -0
  58. package/dist/internal/stringify/handlers/mdc.d.ts +3 -0
  59. package/dist/internal/stringify/handlers/mdc.js +47 -0
  60. package/dist/internal/stringify/handlers/mermaid.d.ts +3 -0
  61. package/dist/internal/stringify/handlers/mermaid.js +8 -0
  62. package/dist/internal/stringify/handlers/ol.d.ts +3 -0
  63. package/dist/internal/stringify/handlers/ol.js +18 -0
  64. package/dist/internal/stringify/handlers/p.d.ts +3 -0
  65. package/dist/internal/stringify/handlers/p.js +8 -0
  66. package/dist/internal/stringify/handlers/pre.d.ts +3 -0
  67. package/dist/internal/stringify/handlers/pre.js +60 -0
  68. package/dist/internal/stringify/handlers/strong.d.ts +3 -0
  69. package/dist/internal/stringify/handlers/strong.js +13 -0
  70. package/dist/internal/stringify/handlers/table.d.ts +8 -0
  71. package/dist/internal/stringify/handlers/table.js +180 -0
  72. package/dist/internal/stringify/handlers/template.d.ts +3 -0
  73. package/dist/internal/stringify/handlers/template.js +14 -0
  74. package/dist/internal/stringify/handlers/ul.d.ts +3 -0
  75. package/dist/internal/stringify/handlers/ul.js +18 -0
  76. package/dist/internal/stringify/indent.d.ts +4 -0
  77. package/dist/internal/stringify/indent.js +8 -0
  78. package/dist/internal/stringify/state.d.ts +13 -0
  79. package/dist/internal/stringify/state.js +121 -0
  80. package/dist/internal/yaml.d.ts +12 -0
  81. package/dist/internal/yaml.js +51 -0
  82. package/dist/parse.d.ts +66 -0
  83. package/dist/parse.js +163 -0
  84. package/dist/plugins/alert.d.ts +2 -0
  85. package/dist/plugins/alert.js +66 -0
  86. package/dist/plugins/emoji.d.ts +3 -0
  87. package/dist/plugins/emoji.js +438 -0
  88. package/dist/plugins/headings.d.ts +48 -0
  89. package/dist/plugins/headings.js +85 -0
  90. package/dist/plugins/highlight.d.ts +63 -0
  91. package/dist/plugins/highlight.js +235 -0
  92. package/dist/plugins/math.d.ts +59 -0
  93. package/dist/plugins/math.js +263 -0
  94. package/dist/plugins/mermaid.d.ts +38 -0
  95. package/dist/plugins/mermaid.js +185 -0
  96. package/dist/plugins/security.d.ts +11 -0
  97. package/dist/plugins/security.js +32 -0
  98. package/dist/plugins/summary.d.ts +2 -0
  99. package/dist/plugins/summary.js +22 -0
  100. package/dist/plugins/task-list.d.ts +8 -0
  101. package/dist/plugins/task-list.js +117 -0
  102. package/dist/plugins/toc.d.ts +15 -0
  103. package/dist/plugins/toc.js +118 -0
  104. package/dist/render.d.ts +18 -0
  105. package/dist/render.js +29 -0
  106. package/dist/types.d.ts +258 -0
  107. package/dist/types.js +1 -0
  108. package/dist/utils/caret.d.ts +7 -0
  109. package/dist/utils/caret.js +36 -0
  110. package/dist/utils/index.d.ts +38 -0
  111. package/dist/utils/index.js +149 -0
  112. package/package.json +73 -9
package/dist/parse.js ADDED
@@ -0,0 +1,163 @@
1
+ import MarkdownExit from 'markdown-exit';
2
+ import pluginMdc from '@comark/markdown-it';
3
+ import taskList from "./plugins/task-list.js";
4
+ import alert from "./plugins/alert.js";
5
+ import { applyAutoUnwrap } from "./internal/parse/auto-unwrap.js";
6
+ import { marmdownItTokensToComarkTree } from "./internal/parse/token-processor.js";
7
+ import { autoCloseMarkdown } from "./internal/parse/auto-close/index.js";
8
+ import { parseFrontmatter } from "./internal/frontmatter.js";
9
+ import { extractReusableNodes } from "./internal/parse/incremental.js";
10
+ import html_block from "./internal/parse/html/html_block_rule.js";
11
+ import html_inline from "./internal/parse/html/html_inline_rule.js";
12
+ // Re-export frontmatter utilities
13
+ export { parseFrontmatter } from "./internal/frontmatter.js";
14
+ /**
15
+ * Creates a parser function for Comark content.
16
+ *
17
+ * Returns an async function that takes a markdown string and returns a Promise resolving to a ComarkTree AST.
18
+ * The returned parser applies frontmatter extraction, Comark syntax parsing, token-to-AST conversion,
19
+ * auto-closing of incomplete markdown, optional AST transformations and plugin hooks.
20
+ *
21
+ * @param options - Parser options controlling parsing behavior.
22
+ * @returns An async parser function: (markdown) => Promise<ComarkTree>
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import { createParse } from 'comark'
27
+ *
28
+ * const parse = createParse({ autoUnwrap: false })
29
+ * const tree = await parse('# Hello **World**\n::alert\nhi\n::')
30
+ * console.log(tree.nodes)
31
+ * // → [ ['h1', { id: 'hello-world' }, 'Hello ', ['strong', {}, 'World'] ], ['alert', {}, 'hi'] ]
32
+ *
33
+ * // Enable HTML parsing (on by default) — HTML tags are included in the AST
34
+ * const parseWithHtml = createParse({ html: true })
35
+ * const tree2 = await parseWithHtml('<strong class="bold">Hello</strong> _world_')
36
+ * console.log(tree2.nodes)
37
+ * // → [ ['strong', { class: 'bold' }, 'Hello'], ' ', ['em', {}, 'world'] ]
38
+ *
39
+ * // Disable HTML parsing — HTML tags are treated as plain text
40
+ * const parseNoHtml = createParse({ html: false })
41
+ * ```
42
+ */
43
+ export function createParse(options = {}) {
44
+ const { autoUnwrap = true, autoClose = true, plugins = [] } = options;
45
+ plugins.unshift(taskList());
46
+ plugins.unshift(alert());
47
+ const parser = new MarkdownExit({
48
+ html: false,
49
+ linkify: true,
50
+ })
51
+ .enable(['table', 'strikethrough'])
52
+ .use(pluginMdc);
53
+ if (options.html !== false) {
54
+ parser.inline.ruler.before('text', 'comark_html_inline', html_inline);
55
+ parser.block.ruler.before('html_block', 'comark_html_block', html_block, {
56
+ alt: ['paragraph', 'reference', 'blockquote'],
57
+ });
58
+ }
59
+ for (const plugin of plugins) {
60
+ for (const markdownItPlugin of (plugin.markdownItPlugins || [])) {
61
+ parser.use(markdownItPlugin);
62
+ }
63
+ }
64
+ let lastOutput = null;
65
+ let lastInput = null;
66
+ return async (markdown, opts = {}) => {
67
+ const state = {
68
+ options,
69
+ tokens: [],
70
+ markdown,
71
+ tree: null,
72
+ parsedLines: 0,
73
+ reusableNodes: [],
74
+ };
75
+ const prevOutput = lastOutput;
76
+ if (opts.streaming && prevOutput && markdown.startsWith(lastInput ?? '')) {
77
+ const { remainingMarkdownStartLine, reusedNodes, remainingMarkdown } = extractReusableNodes(markdown, prevOutput);
78
+ // If there is no remaining markdown, return the previous output
79
+ if (!remainingMarkdown)
80
+ return prevOutput;
81
+ state.parsedLines = remainingMarkdownStartLine;
82
+ state.markdown = remainingMarkdown;
83
+ state.reusableNodes = reusedNodes;
84
+ }
85
+ if (autoClose) {
86
+ state.markdown = autoCloseMarkdown(state.markdown);
87
+ }
88
+ for (const plugin of options.plugins || []) {
89
+ await plugin.pre?.(state);
90
+ }
91
+ const { content, data } = await parseFrontmatter(state.markdown);
92
+ state.tokens = parser.parse(content, {});
93
+ // Convert tokens to Comark structure
94
+ let nodes = marmdownItTokensToComarkTree(state.tokens, {
95
+ startLine: state.parsedLines,
96
+ preservePositions: opts.streaming ?? false,
97
+ });
98
+ if (autoUnwrap) {
99
+ nodes = nodes.map((node) => applyAutoUnwrap(node));
100
+ }
101
+ if (opts.streaming) {
102
+ state.tree = {
103
+ frontmatter: state.parsedLines > 0 ? (prevOutput?.frontmatter ?? data) : data,
104
+ meta: {},
105
+ nodes: [...state.reusableNodes, ...nodes],
106
+ };
107
+ // Set last output and input for streaming mode
108
+ lastOutput = state.tree;
109
+ lastInput = markdown;
110
+ }
111
+ else {
112
+ state.tree = {
113
+ frontmatter: data,
114
+ meta: {},
115
+ nodes,
116
+ };
117
+ // Reset last output and input for non-streaming mode
118
+ lastOutput = null;
119
+ lastInput = null;
120
+ }
121
+ for (const plugin of plugins || []) {
122
+ await plugin.post?.(state);
123
+ }
124
+ return state.tree;
125
+ };
126
+ }
127
+ /**
128
+ * Parse Comark content from a string
129
+ *
130
+ * @param markdown - The markdown/Comark content as a string
131
+ * @param options - Parser options
132
+ * @returns ComarkTree - The parsed AST tree
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * import { parse } from 'comark'
137
+ *
138
+ * const content = `---
139
+ * title: Hello World
140
+ * ---
141
+ *
142
+ * # Hello World
143
+ *
144
+ * This is a **markdown** document with *Comark* components.
145
+ *
146
+ * ::alert{type="info"}
147
+ * This is an alert component
148
+ * ::
149
+ * `
150
+ *
151
+ * const tree = await parse(content)
152
+ * console.log(tree.nodes) // Array of AST nodes
153
+ * console.log(tree.frontmatter) // { title: 'Hello World' }
154
+ * console.log(tree.meta) // Additional metadata
155
+ *
156
+ * // Disable auto-unwrap
157
+ * const tree2 = await parse(content, { autoUnwrap: false })
158
+ * ```
159
+ */
160
+ export async function parse(markdown, options = {}) {
161
+ const parse = createParse(options);
162
+ return await parse(markdown);
163
+ }
@@ -0,0 +1,2 @@
1
+ import type { ComarkPlugin } from 'comark';
2
+ export default function alert(): ComarkPlugin;
@@ -0,0 +1,66 @@
1
+ import { visit } from 'comark/utils';
2
+ const markers = {
3
+ '!TIP': {
4
+ type: 'tip',
5
+ title: 'Tip',
6
+ color: '#238636',
7
+ },
8
+ '!NOTE': {
9
+ type: 'note',
10
+ title: 'Note',
11
+ color: '#1f6feb',
12
+ },
13
+ '!IMPORTANT': {
14
+ type: 'important',
15
+ title: 'Important',
16
+ color: '#8957e5',
17
+ },
18
+ '!WARNING': {
19
+ type: 'warning',
20
+ title: 'Warning',
21
+ color: '#9e6a03',
22
+ },
23
+ '!CAUTION': {
24
+ type: 'caution',
25
+ title: 'Caution',
26
+ color: '#da3633',
27
+ },
28
+ };
29
+ export default function alert() {
30
+ return {
31
+ name: 'alert',
32
+ post(state) {
33
+ visit(state.tree, node => Array.isArray(node) && node[0] === 'blockquote', (node) => {
34
+ const element = node;
35
+ if (node[2]?.[0] === 'span') {
36
+ const content = String(node[2][2]).toUpperCase();
37
+ const marker = markers[content];
38
+ if (marker) {
39
+ if (typeof node[3] === 'string') {
40
+ element[3] = String(element[3]).trimStart();
41
+ }
42
+ // remove span node
43
+ element.splice(2, 1);
44
+ element[1].as = marker.type;
45
+ }
46
+ }
47
+ else if (node[2]?.[0] === 'p') {
48
+ const paragraph = node[2];
49
+ if (paragraph[2]?.[0] === 'span') {
50
+ const content = String(paragraph[2][2]).toUpperCase();
51
+ const marker = markers[content];
52
+ if (marker) {
53
+ if (typeof paragraph[3] === 'string') {
54
+ paragraph[3] = String(paragraph[3]).trimStart();
55
+ }
56
+ // remove span node
57
+ paragraph.splice(2, 1);
58
+ // transform node
59
+ element[1].as = marker.type;
60
+ }
61
+ }
62
+ }
63
+ });
64
+ },
65
+ };
66
+ }
@@ -0,0 +1,3 @@
1
+ import type { ComarkPlugin, MarkdownItPlugin } from 'comark';
2
+ export declare const markdownItEmoji: MarkdownItPlugin;
3
+ export default function comarkEmoji(): ComarkPlugin;
@@ -0,0 +1,438 @@
1
+ // Common emoji definitions (200+ emojis)
2
+ // Organized by category for easier maintenance
3
+ const EMOJI_MAP = new Map([
4
+ ['grinning', '😀'],
5
+ ['smiley', '😃'],
6
+ ['smile', '😄'],
7
+ ['grin', '😁'],
8
+ ['laughing', '😆'],
9
+ ['satisfied', '😆'],
10
+ ['sweat_smile', '😅'],
11
+ ['joy', '😂'],
12
+ ['wink', '😉'],
13
+ ['blush', '😊'],
14
+ ['innocent', '😇'],
15
+ ['heart_eyes', '😍'],
16
+ ['kissing_heart', '😘'],
17
+ ['kissing', '😗'],
18
+ ['kissing_closed_eyes', '😚'],
19
+ ['kissing_smiling_eyes', '😙'],
20
+ ['yum', '😋'],
21
+ ['stuck_out_tongue', '😛'],
22
+ ['stuck_out_tongue_winking_eye', '😜'],
23
+ ['stuck_out_tongue_closed_eyes', '😝'],
24
+ ['neutral_face', '😐'],
25
+ ['expressionless', '😑'],
26
+ ['no_mouth', '😶'],
27
+ ['smirk', '😏'],
28
+ ['unamused', '😒'],
29
+ ['relieved', '😌'],
30
+ ['pensive', '😔'],
31
+ ['sleepy', '😪'],
32
+ ['sleeping', '😴'],
33
+ ['mask', '😷'],
34
+ ['dizzy_face', '😵'],
35
+ ['sunglasses', '😎'],
36
+ ['confused', '😕'],
37
+ ['worried', '😟'],
38
+ ['open_mouth', '😮'],
39
+ ['hushed', '😯'],
40
+ ['astonished', '😲'],
41
+ ['flushed', '😳'],
42
+ ['frowning', '😦'],
43
+ ['anguished', '😧'],
44
+ ['fearful', '😨'],
45
+ ['cold_sweat', '😰'],
46
+ ['disappointed_relieved', '😥'],
47
+ ['cry', '😢'],
48
+ ['sob', '😭'],
49
+ ['scream', '😱'],
50
+ ['confounded', '😖'],
51
+ ['persevere', '😣'],
52
+ ['disappointed', '😞'],
53
+ ['sweat', '😓'],
54
+ ['weary', '😩'],
55
+ ['tired_face', '😫'],
56
+ ['triumph', '😤'],
57
+ ['rage', '😡'],
58
+ ['angry', '😠'],
59
+ ['smiling_imp', '😈'],
60
+ ['imp', '👿'],
61
+ ['skull', '💀'],
62
+ ['eyes', '👀'],
63
+ ['eye', '👁️'],
64
+ ['nose', '👃'],
65
+ ['lips', '👄'],
66
+ ['tongue', '👅'],
67
+ ['heart', '❤️'],
68
+ ['yellow_heart', '💛'],
69
+ ['green_heart', '💚'],
70
+ ['blue_heart', '💙'],
71
+ ['purple_heart', '💜'],
72
+ ['broken_heart', '💔'],
73
+ ['thumbsup', '👍'],
74
+ ['+1', '👍'],
75
+ ['thumbsdown', '👎'],
76
+ ['-1', '👎'],
77
+ ['ok_hand', '👌'],
78
+ ['facepunch', '👊'],
79
+ ['punch', '👊'],
80
+ ['fist', '✊'],
81
+ ['v', '✌️'],
82
+ ['wave', '👋'],
83
+ ['hand', '✋'],
84
+ ['raised_hand', '✋'],
85
+ ['clap', '👏'],
86
+ ['pray', '🙏'],
87
+ ['point_up', '☝️'],
88
+ ['point_down', '👇'],
89
+ ['point_left', '👈'],
90
+ ['point_right', '👉'],
91
+ ['raised_hands', '🙌'],
92
+ ['muscle', '💪'],
93
+ ['writing_hand', '✍️'],
94
+ ['nail_care', '💅'],
95
+ ['selfie', '🤳'],
96
+ ['fire', '🔥'],
97
+ ['sparkles', '✨'],
98
+ ['star', '⭐'],
99
+ ['star2', '🌟'],
100
+ ['zap', '⚡'],
101
+ ['boom', '💥'],
102
+ ['collision', '💥'],
103
+ ['100', '💯'],
104
+ ['tada', '🎉'],
105
+ ['confetti_ball', '🎊'],
106
+ ['balloon', '🎈'],
107
+ ['gift', '🎁'],
108
+ ['birthday', '🎂'],
109
+ ['cake', '🍰'],
110
+ ['rocket', '🚀'],
111
+ ['helicopter', '🚁'],
112
+ ['airplane', '✈️'],
113
+ ['boat', '⛵'],
114
+ ['ship', '🚢'],
115
+ ['train', '🚂'],
116
+ ['bus', '🚌'],
117
+ ['taxi', '🚕'],
118
+ ['car', '🚗'],
119
+ ['bike', '🚲'],
120
+ ['checkered_flag', '🏁'],
121
+ ['medal', '🏅'],
122
+ ['trophy', '🏆'],
123
+ ['medal_sports', '🏅'],
124
+ ['medal_military', '🎖️'],
125
+ ['soccer', '⚽'],
126
+ ['basketball', '🏀'],
127
+ ['football', '🏈'],
128
+ ['baseball', '⚾'],
129
+ ['tennis', '🎾'],
130
+ ['bowling', '🎳'],
131
+ ['golf', '⛳'],
132
+ ['dart', '🎯'],
133
+ ['beer', '🍺'],
134
+ ['beers', '🍻'],
135
+ ['wine_glass', '🍷'],
136
+ ['cocktail', '🍸'],
137
+ ['coffee', '☕'],
138
+ ['pizza', '🍕'],
139
+ ['hamburger', '🍔'],
140
+ ['fries', '🍟'],
141
+ ['apple', '🍎'],
142
+ ['banana', '🍌'],
143
+ ['watermelon', '🍉'],
144
+ ['grapes', '🍇'],
145
+ ['strawberry', '🍓'],
146
+ ['cherries', '🍒'],
147
+ ['lemon', '🍋'],
148
+ ['peach', '🍑'],
149
+ ['pear', '🍐'],
150
+ ['pineapple', '🍍'],
151
+ ['tomato', '🍅'],
152
+ ['eggplant', '🍆'],
153
+ ['hot_pepper', '🌶️'],
154
+ ['corn', '🌽'],
155
+ ['bread', '🍞'],
156
+ ['croissant', '🥐'],
157
+ ['baguette_bread', '🥖'],
158
+ ['cheese', '🧀'],
159
+ ['egg', '🥚'],
160
+ ['poultry_leg', '🍗'],
161
+ ['meat_on_bone', '🍖'],
162
+ ['doughnut', '🍩'],
163
+ ['cookie', '🍪'],
164
+ ['chocolate_bar', '🍫'],
165
+ ['candy', '🍬'],
166
+ ['lollipop', '🍭'],
167
+ ['ice_cream', '🍦'],
168
+ ['icecream', '🍨'],
169
+ ['shaved_ice', '🍧'],
170
+ ['tea', '🍵'],
171
+ ['sake', '🍶'],
172
+ ['champagne', '🍾'],
173
+ ['tropical_drink', '🍹'],
174
+ ['sunny', '☀️'],
175
+ ['cloud', '☁️'],
176
+ ['umbrella', '☂️'],
177
+ ['snowflake', '❄️'],
178
+ ['snowman', '⛄'],
179
+ ['rainbow', '🌈'],
180
+ ['ocean', '🌊'],
181
+ ['droplet', '💧'],
182
+ ['zap', '⚡'],
183
+ ['fire', '🔥'],
184
+ ['star', '⭐'],
185
+ ['moon', '🌙'],
186
+ ['partly_sunny', '⛅'],
187
+ ['thunder_cloud_and_rain', '⛈️'],
188
+ ['wind_face', '🌬️'],
189
+ ['fog', '🌫️'],
190
+ ['dog', '🐶'],
191
+ ['cat', '🐱'],
192
+ ['mouse', '🐭'],
193
+ ['rabbit', '🐰'],
194
+ ['fox_face', '🦊'],
195
+ ['bear', '🐻'],
196
+ ['panda_face', '🐼'],
197
+ ['koala', '🐨'],
198
+ ['tiger', '🐯'],
199
+ ['lion', '🦁'],
200
+ ['cow', '🐮'],
201
+ ['pig', '🐷'],
202
+ ['frog', '🐸'],
203
+ ['monkey_face', '🐵'],
204
+ ['see_no_evil', '🙈'],
205
+ ['hear_no_evil', '🙉'],
206
+ ['speak_no_evil', '🙊'],
207
+ ['chicken', '🐔'],
208
+ ['penguin', '🐧'],
209
+ ['bird', '🐦'],
210
+ ['baby_chick', '🐤'],
211
+ ['bee', '🐝'],
212
+ ['bug', '🐛'],
213
+ ['butterfly', '🦋'],
214
+ ['snail', '🐌'],
215
+ ['turtle', '🐢'],
216
+ ['snake', '🐍'],
217
+ ['lizard', '🦎'],
218
+ ['dragon', '🐉'],
219
+ ['whale', '🐳'],
220
+ ['dolphin', '🐬'],
221
+ ['fish', '🐟'],
222
+ ['octopus', '🐙'],
223
+ ['shell', '🐚'],
224
+ ['crab', '🦀'],
225
+ ['tree', '🌲'],
226
+ ['evergreen_tree', '🌲'],
227
+ ['deciduous_tree', '🌳'],
228
+ ['palm_tree', '🌴'],
229
+ ['cactus', '🌵'],
230
+ ['herb', '🌿'],
231
+ ['shamrock', '☘️'],
232
+ ['four_leaf_clover', '🍀'],
233
+ ['maple_leaf', '🍁'],
234
+ ['fallen_leaf', '🍂'],
235
+ ['leaves', '🍃'],
236
+ ['mushroom', '🍄'],
237
+ ['cherry_blossom', '🌸'],
238
+ ['rose', '🌹'],
239
+ ['hibiscus', '🌺'],
240
+ ['sunflower', '🌻'],
241
+ ['blossom', '🌼'],
242
+ ['bouquet', '💐'],
243
+ ['seedling', '🌱'],
244
+ ['christmas_tree', '🎄'],
245
+ ['santa', '🎅'],
246
+ ['gift_heart', '💝'],
247
+ ['ring', '💍'],
248
+ ['gem', '💎'],
249
+ ['bulb', '💡'],
250
+ ['book', '📖'],
251
+ ['pencil', '📝'],
252
+ ['memo', '📝'],
253
+ ['email', '✉️'],
254
+ ['envelope', '✉️'],
255
+ ['phone', '☎️'],
256
+ ['telephone', '☎️'],
257
+ ['iphone', '📱'],
258
+ ['camera', '📷'],
259
+ ['video_camera', '📹'],
260
+ ['tv', '📺'],
261
+ ['computer', '💻'],
262
+ ['keyboard', '⌨️'],
263
+ ['desktop_computer', '🖥️'],
264
+ ['printer', '🖨️'],
265
+ ['mouse', '🖱️'],
266
+ ['trackball', '🖲️'],
267
+ ['joystick', '🕹️'],
268
+ ['watch', '⌚'],
269
+ ['alarm_clock', '⏰'],
270
+ ['stopwatch', '⏱️'],
271
+ ['timer_clock', '⏲️'],
272
+ ['hourglass', '⌛'],
273
+ ['hourglass_flowing_sand', '⏳'],
274
+ ['satellite_antenna', '📡'],
275
+ ['battery', '🔋'],
276
+ ['electric_plug', '🔌'],
277
+ ['lock', '🔒'],
278
+ ['unlock', '🔓'],
279
+ ['key', '🔑'],
280
+ ['mag', '🔍'],
281
+ ['mag_right', '🔎'],
282
+ ['bell', '🔔'],
283
+ ['no_bell', '🔕'],
284
+ ['bookmark', '🔖'],
285
+ ['link', '🔗'],
286
+ ['wrench', '🔧'],
287
+ ['hammer', '🔨'],
288
+ ['nut_and_bolt', '🔩'],
289
+ ['thinking', '🤔'],
290
+ ['thinking_face', '🤔'],
291
+ ['question', '❓'],
292
+ ['grey_question', '❔'],
293
+ ['exclamation', '❗'],
294
+ ['grey_exclamation', '❕'],
295
+ ['warning', '⚠️'],
296
+ ['x', '❌'],
297
+ ['o', '⭕'],
298
+ ['white_check_mark', '✅'],
299
+ ['heavy_check_mark', '✔️'],
300
+ ['accept', '🉑'],
301
+ ['satellite', '📡'],
302
+ ['arrow_up', '⬆️'],
303
+ ['arrow_down', '⬇️'],
304
+ ['arrow_left', '⬅️'],
305
+ ['arrow_right', '➡️'],
306
+ ['arrow_upper_right', '↗️'],
307
+ ['arrow_lower_right', '↘️'],
308
+ ['arrow_lower_left', '↙️'],
309
+ ['arrow_upper_left', '↖️'],
310
+ ['arrow_up_down', '↕️'],
311
+ ['left_right_arrow', '↔️'],
312
+ ['arrows_counterclockwise', '🔄'],
313
+ ['back', '🔙'],
314
+ ['end', '🔚'],
315
+ ['on', '🔛'],
316
+ ['soon', '🔜'],
317
+ ['top', '🔝'],
318
+ ['red_circle', '🔴'],
319
+ ['blue_circle', '🔵'],
320
+ ['white_circle', '⚪'],
321
+ ['black_circle', '⚫'],
322
+ ['red_square', '🟥'],
323
+ ['blue_square', '🟦'],
324
+ ['white_square', '⬜'],
325
+ ['black_square', '⬛'],
326
+ ['orange_square', '🟧'],
327
+ ['yellow_square', '🟨'],
328
+ ['green_square', '🟩'],
329
+ ['purple_square', '🟪'],
330
+ ['brown_square', '🟫'],
331
+ ['diamond_shape_with_a_dot_inside', '💠'],
332
+ ['radio_button', '🔘'],
333
+ ['white_square_button', '🔳'],
334
+ ['black_square_button', '🔲'],
335
+ ['scissors', '✂️'],
336
+ ['paperclip', '📎'],
337
+ ['pushpin', '📌'],
338
+ ['round_pushpin', '📍'],
339
+ ['triangular_flag_on_post', '🚩'],
340
+ ['closed_book', '📕'],
341
+ ['open_book', '📖'],
342
+ ['green_book', '📗'],
343
+ ['blue_book', '📘'],
344
+ ['orange_book', '�orange_book'],
345
+ ['notebook', '📓'],
346
+ ['ledger', '📒'],
347
+ ['page_with_curl', '📃'],
348
+ ['scroll', '📜'],
349
+ ['page_facing_up', '📄'],
350
+ ['newspaper', '📰'],
351
+ ['bookmark_tabs', '📑'],
352
+ ['bar_chart', '📊'],
353
+ ['chart_with_upwards_trend', '📈'],
354
+ ['chart_with_downwards_trend', '📉'],
355
+ ['calendar', '📅'],
356
+ ['date', '📆'],
357
+ ['clipboard', '📋'],
358
+ ['file_folder', '📁'],
359
+ ['open_file_folder', '📂'],
360
+ ['briefcase', '💼'],
361
+ ['package', '📦'],
362
+ ['inbox_tray', '📥'],
363
+ ['outbox_tray', '📤'],
364
+ ['musical_note', '🎵'],
365
+ ['notes', '🎶'],
366
+ ['microphone', '🎤'],
367
+ ['headphones', '🎧'],
368
+ ['guitar', '🎸'],
369
+ ['trumpet', '🎺'],
370
+ ['saxophone', '🎷'],
371
+ ['violin', '🎻'],
372
+ ['drum', '🥁'],
373
+ ['clapper', '🎬'],
374
+ ['art', '🎨'],
375
+ ['performing_arts', '🎭'],
376
+ ['game_die', '🎲'],
377
+ ['slot_machine', '🎰'],
378
+ ]);
379
+ /**
380
+ * Emoji parser for markdown-it
381
+ * Only supports :emoji_name: syntax (no shortcuts/emoticons)
382
+ * Uses Map for O(1) lookups and simple string scanning
383
+ */
384
+ const emojiRule = (state, silent) => {
385
+ const max = state.posMax;
386
+ const start = state.pos;
387
+ // Quick check: must start with ':'
388
+ if (state.src.charCodeAt(start) !== 0x3A /* : */) {
389
+ return false;
390
+ }
391
+ // Find the closing ':'
392
+ let pos = start + 1;
393
+ while (pos < max) {
394
+ const code = state.src.charCodeAt(pos);
395
+ // Found closing ':'
396
+ if (code === 0x3A /* : */) {
397
+ const emojiName = state.src.slice(start + 1, pos);
398
+ // Check if this is a valid emoji
399
+ const emojiChar = EMOJI_MAP.get(emojiName);
400
+ if (emojiChar) {
401
+ if (!silent) {
402
+ const token = state.push('emoji', '', 0);
403
+ token.markup = emojiName;
404
+ token.content = emojiChar;
405
+ }
406
+ state.pos = pos + 1;
407
+ return true;
408
+ }
409
+ // Not a valid emoji, stop searching
410
+ return false;
411
+ }
412
+ // Only allow word characters, digits, underscores, hyphens, and plus
413
+ // This matches the pattern of valid emoji names
414
+ if ((code >= 0x61 && code <= 0x7A) // a-z
415
+ || (code >= 0x41 && code <= 0x5A) // A-Z
416
+ || (code >= 0x30 && code <= 0x39) // 0-9
417
+ || code === 0x5F // _
418
+ || code === 0x2D // -
419
+ || code === 0x2B // +
420
+ ) {
421
+ pos++;
422
+ continue;
423
+ }
424
+ // Invalid character in emoji name
425
+ return false;
426
+ }
427
+ // No closing ':' found
428
+ return false;
429
+ };
430
+ export const markdownItEmoji = (md) => {
431
+ md.inline.ruler.before('emphasis', 'emoji', emojiRule);
432
+ };
433
+ export default function comarkEmoji() {
434
+ return {
435
+ name: 'emoji',
436
+ markdownItPlugins: [markdownItEmoji],
437
+ };
438
+ }