mardora 1.2.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 (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +113 -0
  3. package/dist/chunk-3OCUX4OO.js +7690 -0
  4. package/dist/chunk-3OCUX4OO.js.map +1 -0
  5. package/dist/chunk-3ZOCCFDL.cjs +74 -0
  6. package/dist/chunk-3ZOCCFDL.cjs.map +1 -0
  7. package/dist/chunk-7JOEPNEV.cjs +7740 -0
  8. package/dist/chunk-7JOEPNEV.cjs.map +1 -0
  9. package/dist/chunk-BIKZQZ6W.js +33 -0
  10. package/dist/chunk-BIKZQZ6W.js.map +1 -0
  11. package/dist/chunk-EQJESPP2.js +234 -0
  12. package/dist/chunk-EQJESPP2.js.map +1 -0
  13. package/dist/chunk-G4SE26YY.js +70 -0
  14. package/dist/chunk-G4SE26YY.js.map +1 -0
  15. package/dist/chunk-KNDWF2DP.cjs +35 -0
  16. package/dist/chunk-KNDWF2DP.cjs.map +1 -0
  17. package/dist/chunk-MLBEBFHB.cjs +2971 -0
  18. package/dist/chunk-MLBEBFHB.cjs.map +1 -0
  19. package/dist/chunk-P7JFCYU3.js +905 -0
  20. package/dist/chunk-P7JFCYU3.js.map +1 -0
  21. package/dist/chunk-SWFUKJDO.cjs +243 -0
  22. package/dist/chunk-SWFUKJDO.cjs.map +1 -0
  23. package/dist/chunk-WFVCG4LD.cjs +926 -0
  24. package/dist/chunk-WFVCG4LD.cjs.map +1 -0
  25. package/dist/chunk-XL6WFGJT.js +2901 -0
  26. package/dist/chunk-XL6WFGJT.js.map +1 -0
  27. package/dist/editor/index.cjs +277 -0
  28. package/dist/editor/index.cjs.map +1 -0
  29. package/dist/editor/index.d.cts +186 -0
  30. package/dist/editor/index.d.ts +186 -0
  31. package/dist/editor/index.js +4 -0
  32. package/dist/editor/index.js.map +1 -0
  33. package/dist/index.cjs +405 -0
  34. package/dist/index.cjs.map +1 -0
  35. package/dist/index.d.cts +13 -0
  36. package/dist/index.d.ts +13 -0
  37. package/dist/index.js +8 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/lib/index.cjs +12 -0
  40. package/dist/lib/index.cjs.map +1 -0
  41. package/dist/lib/index.d.cts +16 -0
  42. package/dist/lib/index.d.ts +16 -0
  43. package/dist/lib/index.js +3 -0
  44. package/dist/lib/index.js.map +1 -0
  45. package/dist/mardora-DCwjomil.d.cts +640 -0
  46. package/dist/mardora-DCwjomil.d.ts +640 -0
  47. package/dist/plugins/index.cjs +104 -0
  48. package/dist/plugins/index.cjs.map +1 -0
  49. package/dist/plugins/index.d.cts +740 -0
  50. package/dist/plugins/index.d.ts +740 -0
  51. package/dist/plugins/index.js +7 -0
  52. package/dist/plugins/index.js.map +1 -0
  53. package/dist/preview/index.cjs +38 -0
  54. package/dist/preview/index.cjs.map +1 -0
  55. package/dist/preview/index.d.cts +101 -0
  56. package/dist/preview/index.d.ts +101 -0
  57. package/dist/preview/index.js +5 -0
  58. package/dist/preview/index.js.map +1 -0
  59. package/dist/types-NBsaxl4d.d.cts +71 -0
  60. package/dist/types-Pw2SWWAR.d.ts +71 -0
  61. package/package.json +92 -0
  62. package/src/editor/attachments/extension.ts +181 -0
  63. package/src/editor/attachments/format.ts +63 -0
  64. package/src/editor/attachments/index.ts +3 -0
  65. package/src/editor/attachments/types.ts +37 -0
  66. package/src/editor/heading-fold/config.ts +25 -0
  67. package/src/editor/heading-fold/extension.ts +268 -0
  68. package/src/editor/heading-fold/extract.ts +88 -0
  69. package/src/editor/heading-fold/index.ts +5 -0
  70. package/src/editor/heading-fold/theme.ts +85 -0
  71. package/src/editor/heading-fold/types.ts +24 -0
  72. package/src/editor/i18n.ts +13 -0
  73. package/src/editor/icons/index.ts +367 -0
  74. package/src/editor/index.ts +16 -0
  75. package/src/editor/mardora.ts +257 -0
  76. package/src/editor/media-lightbox-theme.ts +146 -0
  77. package/src/editor/media-lightbox.ts +125 -0
  78. package/src/editor/plugin.ts +294 -0
  79. package/src/editor/selection-toolbar/activation.ts +123 -0
  80. package/src/editor/selection-toolbar/commands.ts +279 -0
  81. package/src/editor/selection-toolbar/extension.ts +564 -0
  82. package/src/editor/selection-toolbar/i18n.ts +164 -0
  83. package/src/editor/selection-toolbar/index.ts +7 -0
  84. package/src/editor/selection-toolbar/menu.ts +252 -0
  85. package/src/editor/selection-toolbar/position.ts +43 -0
  86. package/src/editor/selection-toolbar/theme.ts +195 -0
  87. package/src/editor/selection-toolbar/types.ts +155 -0
  88. package/src/editor/slash/default-commands.ts +190 -0
  89. package/src/editor/slash/extension.ts +319 -0
  90. package/src/editor/slash/index.ts +7 -0
  91. package/src/editor/slash/insertions.ts +26 -0
  92. package/src/editor/slash/menu.ts +123 -0
  93. package/src/editor/slash/position.ts +61 -0
  94. package/src/editor/slash/query.ts +33 -0
  95. package/src/editor/slash/theme.ts +113 -0
  96. package/src/editor/slash/types.ts +40 -0
  97. package/src/editor/table-of-contents/extension.ts +202 -0
  98. package/src/editor/table-of-contents/extract.ts +53 -0
  99. package/src/editor/table-of-contents/index.ts +7 -0
  100. package/src/editor/table-of-contents/panel.ts +83 -0
  101. package/src/editor/table-of-contents/slug.ts +50 -0
  102. package/src/editor/table-of-contents/storage.ts +35 -0
  103. package/src/editor/table-of-contents/theme.ts +153 -0
  104. package/src/editor/table-of-contents/types.ts +44 -0
  105. package/src/editor/theme.ts +72 -0
  106. package/src/editor/utils.ts +176 -0
  107. package/src/editor/view-plugin.ts +189 -0
  108. package/src/index.ts +5 -0
  109. package/src/lib/index.ts +2 -0
  110. package/src/lib/input-handler.ts +47 -0
  111. package/src/plugins/code-plugin.theme.ts +545 -0
  112. package/src/plugins/code-plugin.ts +1892 -0
  113. package/src/plugins/emoji-plugin.ts +140 -0
  114. package/src/plugins/heading-plugin.ts +194 -0
  115. package/src/plugins/hr-plugin.ts +102 -0
  116. package/src/plugins/html-plugin.ts +353 -0
  117. package/src/plugins/image-plugin.ts +806 -0
  118. package/src/plugins/index.ts +71 -0
  119. package/src/plugins/inline-plugin.ts +311 -0
  120. package/src/plugins/link-plugin.ts +509 -0
  121. package/src/plugins/list-plugin.ts +492 -0
  122. package/src/plugins/math-plugin.ts +526 -0
  123. package/src/plugins/mermaid-plugin.ts +513 -0
  124. package/src/plugins/paragraph-plugin.ts +38 -0
  125. package/src/plugins/quote-plugin.ts +733 -0
  126. package/src/plugins/table-controls-theme.ts +126 -0
  127. package/src/plugins/table-controls.ts +423 -0
  128. package/src/plugins/table-model.ts +661 -0
  129. package/src/plugins/table-plugin.ts +2111 -0
  130. package/src/preview/context.ts +45 -0
  131. package/src/preview/css-generator.ts +64 -0
  132. package/src/preview/default-renderers.ts +29 -0
  133. package/src/preview/index.ts +29 -0
  134. package/src/preview/preview.ts +41 -0
  135. package/src/preview/renderer.ts +184 -0
  136. package/src/preview/syntax-theme.ts +112 -0
  137. package/src/preview/toc.ts +23 -0
  138. package/src/preview/types.ts +89 -0
@@ -0,0 +1,33 @@
1
+ import { EditorSelection } from '@codemirror/state';
2
+ import { EditorView } from '@codemirror/view';
3
+
4
+ // src/lib/input-handler.ts
5
+ function createWrapSelectionInputHandler(markersByInput) {
6
+ return EditorView.inputHandler.of((view, _from, _to, text) => {
7
+ const marker = markersByInput[text];
8
+ if (!marker) {
9
+ return false;
10
+ }
11
+ const ranges = view.state.selection.ranges;
12
+ if (ranges.length === 0 || ranges.some((range) => range.empty)) {
13
+ return false;
14
+ }
15
+ const changes = ranges.map((range) => ({
16
+ from: range.from,
17
+ to: range.to,
18
+ insert: `${marker}${view.state.sliceDoc(range.from, range.to)}${marker}`
19
+ })).reverse();
20
+ const nextRanges = ranges.map(
21
+ (range) => EditorSelection.range(range.from + marker.length, range.to + marker.length)
22
+ );
23
+ view.dispatch({
24
+ changes,
25
+ selection: EditorSelection.create(nextRanges, view.state.selection.mainIndex)
26
+ });
27
+ return true;
28
+ });
29
+ }
30
+
31
+ export { createWrapSelectionInputHandler };
32
+ //# sourceMappingURL=chunk-BIKZQZ6W.js.map
33
+ //# sourceMappingURL=chunk-BIKZQZ6W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/input-handler.ts"],"names":[],"mappings":";;;;AAeO,SAAS,gCAAgC,cAAA,EAAmD;AACjG,EAAA,OAAO,WAAW,YAAA,CAAa,EAAA,CAAG,CAAC,IAAA,EAAM,KAAA,EAAO,KAAK,IAAA,KAAS;AAC5D,IAAA,MAAM,MAAA,GAAS,eAAe,IAAI,CAAA;AAClC,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,SAAA,CAAU,MAAA;AACpC,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,IAAK,MAAA,CAAO,KAAK,CAAC,KAAA,KAAU,KAAA,CAAM,KAAK,CAAA,EAAG;AAC9D,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,MAAM,OAAA,GAAU,MAAA,CACb,GAAA,CAAI,CAAC,KAAA,MAAW;AAAA,MACf,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,IAAI,KAAA,CAAM,EAAA;AAAA,MACV,MAAA,EAAQ,CAAA,EAAG,MAAM,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,QAAA,CAAS,KAAA,CAAM,IAAA,EAAM,KAAA,CAAM,EAAE,CAAC,GAAG,MAAM,CAAA;AAAA,KACxE,CAAE,EACD,OAAA,EAAQ;AAEX,IAAA,MAAM,aAAa,MAAA,CAAO,GAAA;AAAA,MAAI,CAAC,KAAA,KAC7B,eAAA,CAAgB,KAAA,CAAM,KAAA,CAAM,IAAA,GAAO,MAAA,CAAO,MAAA,EAAQ,KAAA,CAAM,EAAA,GAAK,MAAA,CAAO,MAAM;AAAA,KAC5E;AAEA,IAAA,IAAA,CAAK,QAAA,CAAS;AAAA,MACZ,OAAA;AAAA,MACA,WAAW,eAAA,CAAgB,MAAA,CAAO,YAAY,IAAA,CAAK,KAAA,CAAM,UAAU,SAAS;AAAA,KAC7E,CAAA;AAED,IAAA,OAAO,IAAA;AAAA,EACT,CAAC,CAAA;AACH","file":"chunk-BIKZQZ6W.js","sourcesContent":["import { EditorSelection, Extension } from \"@codemirror/state\";\nimport { EditorView } from \"@codemirror/view\";\n\n/**\n * Mapping of typed input characters to surrounding markers.\n *\n * Example:\n * { \"*\": \"*\", \"=\": \"==\" }\n */\nexport type WrapSelectionMarkerMap = Record<string, string>;\n\n/**\n * Creates an input handler that wraps non-empty selections with markdown markers\n * when a mapped character is typed.\n */\nexport function createWrapSelectionInputHandler(markersByInput: WrapSelectionMarkerMap): Extension {\n return EditorView.inputHandler.of((view, _from, _to, text) => {\n const marker = markersByInput[text];\n if (!marker) {\n return false;\n }\n\n const ranges = view.state.selection.ranges;\n if (ranges.length === 0 || ranges.some((range) => range.empty)) {\n return false;\n }\n\n const changes = ranges\n .map((range) => ({\n from: range.from,\n to: range.to,\n insert: `${marker}${view.state.sliceDoc(range.from, range.to)}${marker}`,\n }))\n .reverse();\n\n const nextRanges = ranges.map((range) =>\n EditorSelection.range(range.from + marker.length, range.to + marker.length)\n );\n\n view.dispatch({\n changes,\n selection: EditorSelection.create(nextRanges, view.state.selection.mainIndex),\n });\n\n return true;\n });\n}\n"]}
@@ -0,0 +1,234 @@
1
+ import { extractTocItemsFromState } from './chunk-P7JFCYU3.js';
2
+ import { classHighlighter } from '@lezer/highlight';
3
+ import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
4
+ import { EditorState } from '@codemirror/state';
5
+ import { languages } from '@codemirror/language-data';
6
+ import DOMPurify from 'dompurify';
7
+
8
+ // src/preview/default-renderers.ts
9
+ function escapeHtml(text) {
10
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
11
+ }
12
+ var renderDocument = (_node, children) => {
13
+ return children;
14
+ };
15
+ var defaultRenderers = {
16
+ // Document structure
17
+ Document: renderDocument
18
+ };
19
+ var MAX_WALK_DEPTH = 8;
20
+ function generateSyntaxThemeCSS(syntaxTheme, _wrapperClass) {
21
+ if (!syntaxTheme) return "";
22
+ const styles = extractRuntimeHighlightStyles(syntaxTheme);
23
+ if (!styles.length) return "";
24
+ const cssChunks = [];
25
+ for (const style of styles) {
26
+ const rules = style.module?.getRules();
27
+ if (!rules) continue;
28
+ cssChunks.push(rules);
29
+ }
30
+ if (!cssChunks.length) return "";
31
+ return Array.from(new Set(cssChunks)).join("\n");
32
+ }
33
+ function resolveSyntaxHighlighters(syntaxTheme, includeLegacyClassHighlighter = true) {
34
+ const resolved = [];
35
+ if (includeLegacyClassHighlighter) {
36
+ resolved.push(classHighlighter);
37
+ }
38
+ const styles = extractRuntimeHighlightStyles(syntaxTheme);
39
+ for (const style of styles) {
40
+ if (typeof style.style === "function") {
41
+ resolved.push(style);
42
+ }
43
+ }
44
+ return Array.from(new Set(resolved));
45
+ }
46
+ function extractRuntimeHighlightStyles(input) {
47
+ if (!input) return [];
48
+ const values = Array.isArray(input) ? input : [input];
49
+ const styles = [];
50
+ const visited = /* @__PURE__ */ new WeakSet();
51
+ for (const value of values) {
52
+ walk(value, 0, visited, styles);
53
+ }
54
+ return styles;
55
+ }
56
+ function walk(value, depth, visited, out) {
57
+ if (value === null || value === void 0) return;
58
+ if (depth > MAX_WALK_DEPTH) return;
59
+ if (isRuntimeHighlightStyle(value)) {
60
+ out.push(value);
61
+ }
62
+ if (Array.isArray(value)) {
63
+ for (const item of value) {
64
+ walk(item, depth + 1, visited, out);
65
+ }
66
+ return;
67
+ }
68
+ if (typeof value !== "object") return;
69
+ if (visited.has(value)) return;
70
+ visited.add(value);
71
+ const keys = Object.getOwnPropertyNames(value);
72
+ for (const key of keys) {
73
+ try {
74
+ walk(value[key], depth + 1, visited, out);
75
+ } catch {
76
+ }
77
+ }
78
+ }
79
+ function isRuntimeHighlightStyle(value) {
80
+ if (!value || typeof value !== "object") return false;
81
+ const style = value;
82
+ return Array.isArray(style.specs) && typeof style.style === "function";
83
+ }
84
+ function createPreviewContext(doc, theme, renderChildren, sanitizeHtml = true, syntaxHighlighters = [], headingIdForNode) {
85
+ const context = {
86
+ doc,
87
+ theme,
88
+ syntaxHighlighters,
89
+ sliceDoc(from, to) {
90
+ return doc.slice(from, to);
91
+ },
92
+ sanitize(html) {
93
+ if (!sanitizeHtml) return html;
94
+ if (typeof window !== "undefined") {
95
+ return DOMPurify.sanitize(html);
96
+ }
97
+ return html;
98
+ },
99
+ renderChildren
100
+ };
101
+ if (headingIdForNode) context.headingIdForNode = headingIdForNode;
102
+ return context;
103
+ }
104
+
105
+ // src/preview/renderer.ts
106
+ var PreviewRenderer = class {
107
+ doc;
108
+ theme;
109
+ plugins;
110
+ markdown;
111
+ sanitizeHtml;
112
+ syntaxTheme;
113
+ renderers;
114
+ ctx;
115
+ nodeToPlugins;
116
+ constructor(doc, plugins = [], markdown2, theme = "auto" /* AUTO */, sanitize = true, syntaxTheme) {
117
+ this.doc = doc;
118
+ this.theme = theme;
119
+ this.plugins = plugins;
120
+ this.markdown = markdown2;
121
+ this.sanitizeHtml = sanitize;
122
+ this.syntaxTheme = syntaxTheme;
123
+ this.renderers = { ...defaultRenderers };
124
+ const syntaxHighlighters = resolveSyntaxHighlighters(this.syntaxTheme, true);
125
+ this.ctx = createPreviewContext(doc, theme, this.renderChildren.bind(this), sanitize, syntaxHighlighters);
126
+ this.nodeToPlugins = this.buildNodePluginMap();
127
+ }
128
+ /**
129
+ * Build a map from node names to plugins that handle them
130
+ */
131
+ buildNodePluginMap() {
132
+ const map = /* @__PURE__ */ new Map();
133
+ for (const plugin of this.plugins) {
134
+ if (plugin.renderToHTML && plugin.requiredNodes.length > 0) {
135
+ for (const nodeName of plugin.requiredNodes) {
136
+ const list = map.get(nodeName) || [];
137
+ list.push(plugin);
138
+ map.set(nodeName, list);
139
+ }
140
+ }
141
+ }
142
+ return map;
143
+ }
144
+ /**
145
+ * Render the document to HTML
146
+ */
147
+ async render() {
148
+ const extensions = [
149
+ ...this.markdown,
150
+ ...this.plugins.map((p) => p.getMarkdownConfig()).filter((ext) => ext !== null)
151
+ ];
152
+ const markdownSupport = markdown({
153
+ base: markdownLanguage,
154
+ codeLanguages: languages,
155
+ extensions,
156
+ addKeymap: true,
157
+ completeHTMLTags: true,
158
+ pasteURLAsLink: true
159
+ });
160
+ const parser = markdownSupport.language.parser;
161
+ const tree = parser.parse(this.doc);
162
+ const state = EditorState.create({
163
+ doc: this.doc,
164
+ extensions: [markdownSupport]
165
+ });
166
+ const tocItems = extractTocItemsFromState(state);
167
+ const headingIds = new Map(tocItems.map((item) => [`${item.from}:${item.to}`, item.id]));
168
+ const syntaxHighlighters = resolveSyntaxHighlighters(this.syntaxTheme, true);
169
+ this.ctx = createPreviewContext(
170
+ this.doc,
171
+ this.theme,
172
+ this.renderChildren.bind(this),
173
+ this.sanitizeHtml,
174
+ syntaxHighlighters,
175
+ (node) => headingIds.get(`${node.from}:${node.to}`) ?? null
176
+ );
177
+ return await this.renderNode(tree.topNode);
178
+ }
179
+ /**
180
+ * Render a single node to HTML
181
+ */
182
+ async renderNode(node) {
183
+ return (await this.renderNodeWithExtent(node)).html;
184
+ }
185
+ async renderNodeWithExtent(node) {
186
+ const plugins = this.nodeToPlugins.get(node.name);
187
+ if (plugins) {
188
+ for (const plugin of plugins) {
189
+ const children = await this.renderChildren(node);
190
+ const result = await plugin.renderToHTML(node, children, this.ctx);
191
+ if (result !== null) {
192
+ return {
193
+ html: result,
194
+ to: Math.max(node.to, plugin.getPreviewConsumedTo?.(node, this.ctx) ?? node.to)
195
+ };
196
+ }
197
+ }
198
+ }
199
+ const renderer = this.renderers[node.name];
200
+ if (renderer) {
201
+ const children = await this.renderChildren(node);
202
+ return { html: renderer(node, children, this.ctx), to: node.to };
203
+ }
204
+ if (node.firstChild) {
205
+ return { html: await this.renderChildren(node), to: node.to };
206
+ }
207
+ return { html: this.ctx.sliceDoc(node.from, node.to), to: node.to };
208
+ }
209
+ /**
210
+ * Render all children of a node, including text between nodes
211
+ */
212
+ async renderChildren(node) {
213
+ let result = "";
214
+ let pos = node.from;
215
+ let child = node.firstChild;
216
+ while (child) {
217
+ if (child.from > pos) {
218
+ result += escapeHtml(this.ctx.sliceDoc(pos, child.from));
219
+ }
220
+ const rendered = await this.renderNodeWithExtent(child);
221
+ result += rendered.html;
222
+ pos = rendered.to;
223
+ child = child.nextSibling;
224
+ }
225
+ if (pos < node.to) {
226
+ result += escapeHtml(this.ctx.sliceDoc(pos, node.to));
227
+ }
228
+ return result;
229
+ }
230
+ };
231
+
232
+ export { PreviewRenderer, defaultRenderers, escapeHtml, generateSyntaxThemeCSS };
233
+ //# sourceMappingURL=chunk-EQJESPP2.js.map
234
+ //# sourceMappingURL=chunk-EQJESPP2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/preview/default-renderers.ts","../src/preview/syntax-theme.ts","../src/preview/context.ts","../src/preview/renderer.ts"],"names":["markdown"],"mappings":"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","file":"chunk-EQJESPP2.js","sourcesContent":["import { NodeRenderer, NodeRendererMap } from \"./types\";\n\n/**\n * Escape HTML special characters\n */\nexport function escapeHtml(text: string): string {\n return text\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\n// ============================================\n// DEFAULT RENDERERS\n// ============================================\n\nconst renderDocument: NodeRenderer = (_node, children) => {\n return children;\n};\n\n/**\n * Default node renderers for all markdown node types\n */\nexport const defaultRenderers: NodeRendererMap = {\n // Document structure\n Document: renderDocument,\n};\n","import { classHighlighter, Highlighter } from \"@lezer/highlight\";\nimport type { SyntaxThemeInput } from \"./types\";\n\ntype HighlightSpec = {\n tag?: unknown;\n class?: string;\n [key: string]: unknown;\n};\n\ntype RuntimeHighlightStyle = {\n specs?: HighlightSpec[];\n style?: (tags: readonly import(\"@lezer/highlight\").Tag[]) => string | null;\n module?: { getRules(): string } | null;\n};\n\nconst MAX_WALK_DEPTH = 8;\n\n/**\n * Extract syntax highlight CSS from resolved CodeMirror HighlightStyle modules.\n */\nexport function generateSyntaxThemeCSS(\n syntaxTheme: SyntaxThemeInput | SyntaxThemeInput[] | undefined,\n _wrapperClass: string\n): string {\n void _wrapperClass;\n if (!syntaxTheme) return \"\";\n\n const styles = extractRuntimeHighlightStyles(syntaxTheme);\n if (!styles.length) return \"\";\n\n const cssChunks: string[] = [];\n\n for (const style of styles) {\n const rules = style.module?.getRules();\n if (!rules) continue;\n cssChunks.push(rules);\n }\n\n if (!cssChunks.length) return \"\";\n\n return Array.from(new Set(cssChunks)).join(\"\\n\");\n}\n\nexport function resolveSyntaxHighlighters(\n syntaxTheme: SyntaxThemeInput | SyntaxThemeInput[] | undefined,\n includeLegacyClassHighlighter: boolean = true\n): readonly Highlighter[] {\n const resolved: Highlighter[] = [];\n if (includeLegacyClassHighlighter) {\n resolved.push(classHighlighter);\n }\n\n const styles = extractRuntimeHighlightStyles(syntaxTheme);\n for (const style of styles) {\n if (typeof style.style === \"function\") {\n resolved.push(style as unknown as Highlighter);\n }\n }\n\n return Array.from(new Set(resolved));\n}\n\nfunction extractRuntimeHighlightStyles(\n input: SyntaxThemeInput | SyntaxThemeInput[] | undefined\n): RuntimeHighlightStyle[] {\n if (!input) return [];\n\n const values = Array.isArray(input) ? input : [input];\n const styles: RuntimeHighlightStyle[] = [];\n const visited = new WeakSet<object>();\n\n for (const value of values) {\n walk(value, 0, visited, styles);\n }\n\n return styles;\n}\n\nfunction walk(value: unknown, depth: number, visited: WeakSet<object>, out: RuntimeHighlightStyle[]): void {\n if (value === null || value === undefined) return;\n if (depth > MAX_WALK_DEPTH) return;\n\n if (isRuntimeHighlightStyle(value)) {\n out.push(value);\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n walk(item, depth + 1, visited, out);\n }\n return;\n }\n\n if (typeof value !== \"object\") return;\n if (visited.has(value)) return;\n visited.add(value);\n\n const keys = Object.getOwnPropertyNames(value);\n for (const key of keys) {\n try {\n walk((value as Record<string, unknown>)[key], depth + 1, visited, out);\n } catch {\n // Ignore inaccessible properties\n }\n }\n}\n\nfunction isRuntimeHighlightStyle(value: unknown): value is RuntimeHighlightStyle {\n if (!value || typeof value !== \"object\") return false;\n const style = value as RuntimeHighlightStyle;\n return Array.isArray(style.specs) && typeof style.style === \"function\";\n}\n","import { SyntaxNode } from \"@lezer/common\";\nimport { Highlighter } from \"@lezer/highlight\";\nimport { ThemeEnum } from \"../editor/utils\";\nimport { PreviewContext } from \"./types\";\nimport DOMPurify from \"dompurify\";\n\n/**\n * Creates a PreviewContext for rendering\n */\nexport function createPreviewContext(\n doc: string,\n theme: ThemeEnum,\n renderChildren: (node: SyntaxNode) => Promise<string>,\n sanitizeHtml: boolean = true,\n syntaxHighlighters: readonly Highlighter[] = [],\n headingIdForNode?: (node: SyntaxNode) => string | null\n): PreviewContext {\n const context: PreviewContext = {\n doc,\n theme,\n syntaxHighlighters,\n\n sliceDoc(from: number, to: number): string {\n return doc.slice(from, to);\n },\n\n sanitize(html: string): string {\n if (!sanitizeHtml) return html;\n\n // DOMPurify works in browser; in Node, it needs jsdom\n if (typeof window !== \"undefined\") {\n return DOMPurify.sanitize(html);\n }\n\n // Server-side: return as-is (user should sanitize at application level)\n // or use isomorphic-dompurify in their setup\n return html;\n },\n\n renderChildren,\n };\n\n if (headingIdForNode) context.headingIdForNode = headingIdForNode;\n return context;\n}\n","import { SyntaxNode } from \"@lezer/common\";\nimport { markdown, markdownLanguage } from \"@codemirror/lang-markdown\";\nimport { EditorState } from \"@codemirror/state\";\nimport { MarkdownConfig } from \"@lezer/markdown\";\nimport { languages } from \"@codemirror/language-data\";\n\nimport { MardoraPlugin } from \"../editor/plugin\";\nimport { extractTocItemsFromState } from \"../editor/table-of-contents\";\nimport { ThemeEnum } from \"../editor/utils\";\nimport { createPreviewContext } from \"./context\";\nimport { defaultRenderers, escapeHtml } from \"./default-renderers\";\nimport { resolveSyntaxHighlighters } from \"./syntax-theme\";\nimport { NodeRendererMap, PreviewContext } from \"./types\";\n\n/**\n * Renderer class that walks the syntax tree and produces HTML\n */\nexport class PreviewRenderer {\n private doc: string;\n private theme: ThemeEnum;\n private plugins: MardoraPlugin[];\n private markdown: MarkdownConfig[];\n private sanitizeHtml: boolean;\n private syntaxTheme: import(\"./types\").SyntaxThemeInput | import(\"./types\").SyntaxThemeInput[] | undefined;\n private renderers: NodeRendererMap;\n private ctx: PreviewContext;\n private nodeToPlugins: Map<string, MardoraPlugin[]>;\n\n constructor(\n doc: string,\n plugins: MardoraPlugin[] = [],\n markdown: MarkdownConfig[],\n theme: ThemeEnum = ThemeEnum.AUTO,\n sanitize: boolean = true,\n syntaxTheme?: import(\"./types\").SyntaxThemeInput | import(\"./types\").SyntaxThemeInput[]\n ) {\n this.doc = doc;\n this.theme = theme;\n this.plugins = plugins;\n this.markdown = markdown;\n this.sanitizeHtml = sanitize;\n this.syntaxTheme = syntaxTheme;\n this.renderers = { ...defaultRenderers };\n\n const syntaxHighlighters = resolveSyntaxHighlighters(this.syntaxTheme, true);\n\n // Create context with reference to renderChildren\n this.ctx = createPreviewContext(doc, theme, this.renderChildren.bind(this), sanitize, syntaxHighlighters);\n\n // Build node-to-plugin map for O(1) lookup\n this.nodeToPlugins = this.buildNodePluginMap();\n }\n\n /**\n * Build a map from node names to plugins that handle them\n */\n private buildNodePluginMap(): Map<string, MardoraPlugin[]> {\n const map = new Map<string, MardoraPlugin[]>();\n for (const plugin of this.plugins) {\n if (plugin.renderToHTML && plugin.requiredNodes.length > 0) {\n for (const nodeName of plugin.requiredNodes) {\n const list = map.get(nodeName) || [];\n list.push(plugin);\n map.set(nodeName, list);\n }\n }\n }\n return map;\n }\n\n /**\n * Render the document to HTML\n */\n async render(): Promise<string> {\n // Collect markdown extensions from plugins\n const extensions = [\n ...this.markdown,\n ...this.plugins.map((p) => p.getMarkdownConfig()).filter((ext): ext is NonNullable<typeof ext> => ext !== null),\n ];\n\n // Build parser through @codemirror/lang-markdown to match editor behavior exactly\n const markdownSupport = markdown({\n base: markdownLanguage,\n codeLanguages: languages,\n extensions,\n addKeymap: true,\n completeHTMLTags: true,\n pasteURLAsLink: true,\n });\n const parser = markdownSupport.language.parser;\n\n // Parse the document\n const tree = parser.parse(this.doc);\n const state = EditorState.create({\n doc: this.doc,\n extensions: [markdownSupport],\n });\n const tocItems = extractTocItemsFromState(state);\n const headingIds = new Map(tocItems.map((item) => [`${item.from}:${item.to}`, item.id]));\n const syntaxHighlighters = resolveSyntaxHighlighters(this.syntaxTheme, true);\n\n this.ctx = createPreviewContext(\n this.doc,\n this.theme,\n this.renderChildren.bind(this),\n this.sanitizeHtml,\n syntaxHighlighters,\n (node) => headingIds.get(`${node.from}:${node.to}`) ?? null\n );\n\n // Render from root\n return await this.renderNode(tree.topNode);\n }\n\n /**\n * Render a single node to HTML\n */\n private async renderNode(node: SyntaxNode): Promise<string> {\n return (await this.renderNodeWithExtent(node)).html;\n }\n\n private async renderNodeWithExtent(node: SyntaxNode): Promise<{ html: string; to: number }> {\n // Get plugins that handle this node type (O(1) lookup)\n const plugins = this.nodeToPlugins.get(node.name);\n if (plugins) {\n for (const plugin of plugins) {\n const children = await this.renderChildren(node);\n const result = await plugin.renderToHTML!(node, children, this.ctx);\n if (result !== null) {\n return {\n html: result,\n to: Math.max(node.to, plugin.getPreviewConsumedTo?.(node, this.ctx) ?? node.to),\n };\n }\n }\n }\n\n // Use default renderer\n const renderer = this.renderers[node.name];\n if (renderer) {\n const children = await this.renderChildren(node);\n return { html: renderer(node, children, this.ctx), to: node.to };\n }\n\n // Unknown node - render children or text\n if (node.firstChild) {\n return { html: await this.renderChildren(node), to: node.to };\n }\n\n // Leaf node - return text content\n return { html: this.ctx.sliceDoc(node.from, node.to), to: node.to };\n }\n\n /**\n * Render all children of a node, including text between nodes\n */\n private async renderChildren(node: SyntaxNode): Promise<string> {\n let result = \"\";\n let pos = node.from; // Track position to find text gaps\n let child = node.firstChild;\n\n while (child) {\n // Add any text between the last position and this child\n if (child.from > pos) {\n result += escapeHtml(this.ctx.sliceDoc(pos, child.from));\n }\n\n // Render the child node\n const rendered = await this.renderNodeWithExtent(child);\n result += rendered.html;\n\n // Update position to end of this child\n pos = rendered.to;\n child = child.nextSibling;\n }\n\n // Add any trailing text after the last child\n if (pos < node.to) {\n result += escapeHtml(this.ctx.sliceDoc(pos, node.to));\n }\n\n return result;\n }\n}\n"]}
@@ -0,0 +1,70 @@
1
+ import { PreviewRenderer, generateSyntaxThemeCSS } from './chunk-EQJESPP2.js';
2
+ import { extractTocItemsFromState } from './chunk-P7JFCYU3.js';
3
+ import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
4
+ import { EditorState } from '@codemirror/state';
5
+
6
+ // src/preview/preview.ts
7
+ async function preview(markdown2, config = {}) {
8
+ const {
9
+ plugins = [],
10
+ markdown: markdownConfig = [],
11
+ wrapperClass = "mardora-preview",
12
+ wrapperTag = "article",
13
+ sanitize = true,
14
+ theme = "auto" /* AUTO */,
15
+ syntaxTheme
16
+ } = config;
17
+ const renderer = new PreviewRenderer(markdown2, plugins, markdownConfig, theme, sanitize, syntaxTheme);
18
+ const content = await renderer.render();
19
+ const classAttr = wrapperClass ? ` class="${wrapperClass}"` : "";
20
+ return `<${wrapperTag}${classAttr}>
21
+ ${content}</${wrapperTag}>`;
22
+ }
23
+
24
+ // src/preview/css-generator.ts
25
+ var baseStyles = `.mardora-preview {
26
+ padding: 0 0.5rem;
27
+ }`;
28
+ function generateCSS(config = {}) {
29
+ const {
30
+ plugins = [],
31
+ theme = "auto" /* AUTO */,
32
+ wrapperClass = "mardora-preview",
33
+ includeBase = true,
34
+ syntaxTheme
35
+ } = config;
36
+ const cssChunks = [];
37
+ if (includeBase) {
38
+ if (wrapperClass !== "mardora-preview") {
39
+ cssChunks.push(baseStyles.replace(/\.mardora-preview/g, `.${wrapperClass}`));
40
+ } else {
41
+ cssChunks.push(baseStyles);
42
+ }
43
+ }
44
+ const syntaxCSS = generateSyntaxThemeCSS(syntaxTheme, wrapperClass);
45
+ if (syntaxCSS) {
46
+ cssChunks.push("/* syntax-theme */\n" + syntaxCSS);
47
+ }
48
+ for (const plugin of plugins) {
49
+ const pluginCSS = plugin.getPreviewStyles(theme, wrapperClass);
50
+ if (pluginCSS) cssChunks.push(`/* ${plugin.name} - ${plugin.version} */
51
+ ` + pluginCSS);
52
+ }
53
+ return cssChunks.join("\n\n");
54
+ }
55
+ function extractPreviewTocFromMarkdown(doc, config = {}, markdownConfig = []) {
56
+ const state = EditorState.create({
57
+ doc,
58
+ extensions: [markdown({ base: markdownLanguage, extensions: markdownConfig })]
59
+ });
60
+ return extractTocItemsFromState(state, config).map((item) => ({
61
+ id: item.id,
62
+ level: item.level,
63
+ text: item.text,
64
+ active: item.active
65
+ }));
66
+ }
67
+
68
+ export { extractPreviewTocFromMarkdown, generateCSS, preview };
69
+ //# sourceMappingURL=chunk-G4SE26YY.js.map
70
+ //# sourceMappingURL=chunk-G4SE26YY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/preview/preview.ts","../src/preview/css-generator.ts","../src/preview/toc.ts"],"names":["markdown"],"mappings":";;;;;;AAsBA,eAAsB,OAAA,CAAQA,SAAAA,EAAkB,MAAA,GAAwB,EAAC,EAAoB;AAC3F,EAAA,MAAM;AAAA,IACJ,UAAU,EAAC;AAAA,IACX,QAAA,EAAU,iBAAiB,EAAC;AAAA,IAC5B,YAAA,GAAe,iBAAA;AAAA,IACf,UAAA,GAAa,SAAA;AAAA,IACb,QAAA,GAAW,IAAA;AAAA,IACX,KAAA,GAAA,MAAA;AAAA,IACA;AAAA,GACF,GAAI,MAAA;AAGJ,EAAA,MAAM,QAAA,GAAW,IAAI,eAAA,CAAgBA,SAAAA,EAAU,SAAS,cAAA,EAAgB,KAAA,EAAO,UAAU,WAAW,CAAA;AACpG,EAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAS,MAAA,EAAO;AAGtC,EAAA,MAAM,SAAA,GAAY,YAAA,GAAe,CAAA,QAAA,EAAW,YAAY,CAAA,CAAA,CAAA,GAAM,EAAA;AAC9D,EAAA,OAAO,CAAA,CAAA,EAAI,UAAU,CAAA,EAAG,SAAS,CAAA;AAAA,EAAM,OAAO,KAAK,UAAU,CAAA,CAAA,CAAA;AAC/D;;;ACjCA,IAAM,UAAA,GAAa,CAAA;AAAA;AAAA,CAAA,CAAA;AAsBZ,SAAS,WAAA,CAAY,MAAA,GAA4B,EAAC,EAAW;AAClE,EAAA,MAAM;AAAA,IACJ,UAAU,EAAC;AAAA,IACX,KAAA,GAAA,MAAA;AAAA,IACA,YAAA,GAAe,iBAAA;AAAA,IACf,WAAA,GAAc,IAAA;AAAA,IACd;AAAA,GACF,GAAI,MAAA;AAEJ,EAAA,MAAM,YAAsB,EAAC;AAG7B,EAAA,IAAI,WAAA,EAAa;AAEf,IAAA,IAAI,iBAAiB,iBAAA,EAAmB;AACtC,MAAA,SAAA,CAAU,KAAK,UAAA,CAAW,OAAA,CAAQ,sBAAsB,CAAA,CAAA,EAAI,YAAY,EAAE,CAAC,CAAA;AAAA,IAC7E,CAAA,MAAO;AACL,MAAA,SAAA,CAAU,KAAK,UAAU,CAAA;AAAA,IAC3B;AAAA,EACF;AAGA,EAAA,MAAM,SAAA,GAAY,sBAAA,CAAuB,WAAA,EAAa,YAAY,CAAA;AAClE,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,SAAA,CAAU,IAAA,CAAK,yBAAyB,SAAS,CAAA;AAAA,EACnD;AAGA,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,gBAAA,CAAiB,KAAA,EAAO,YAAY,CAAA;AAC7D,IAAA,IAAI,SAAA,YAAqB,IAAA,CAAK,CAAA,GAAA,EAAM,OAAO,IAAI,CAAA,GAAA,EAAM,OAAO,OAAO,CAAA;AAAA,CAAA,GAAU,SAAS,CAAA;AAAA,EACxF;AAEA,EAAA,OAAO,SAAA,CAAU,KAAK,MAAM,CAAA;AAC9B;ACzDO,SAAS,8BACd,GAAA,EACA,MAAA,GAA2B,EAAC,EAC5B,cAAA,GAAmC,EAAC,EAClB;AAClB,EAAA,MAAM,KAAA,GAAQ,YAAY,MAAA,CAAO;AAAA,IAC/B,GAAA;AAAA,IACA,UAAA,EAAY,CAAC,QAAA,CAAS,EAAE,MAAM,gBAAA,EAAkB,UAAA,EAAY,cAAA,EAAgB,CAAC;AAAA,GAC9E,CAAA;AAED,EAAA,OAAO,yBAAyB,KAAA,EAAO,MAAM,CAAA,CAAE,GAAA,CAAI,CAAC,IAAA,MAAU;AAAA,IAC5D,IAAI,IAAA,CAAK,EAAA;AAAA,IACT,OAAO,IAAA,CAAK,KAAA;AAAA,IACZ,MAAM,IAAA,CAAK,IAAA;AAAA,IACX,QAAQ,IAAA,CAAK;AAAA,GACf,CAAE,CAAA;AACJ","file":"chunk-G4SE26YY.js","sourcesContent":["import { ThemeEnum } from \"../editor/utils\";\nimport { PreviewRenderer } from \"./renderer\";\nimport { PreviewConfig } from \"./types\";\n\n/**\n * Render markdown to semantic HTML\n *\n * @param markdown - Markdown string to render\n * @param config - Preview configuration\n * @returns HTML string\n *\n * @example\n * ```ts\n * import { preview } from 'mardora/preview';\n * import { HeadingPlugin, ListPlugin } from 'mardora/plugins';\n *\n * const html = preview('# Hello World', {\n * plugins: [new HeadingPlugin(), new ListPlugin()],\n * wrapperClass: 'mardora-preview',\n * });\n * ```\n */\nexport async function preview(markdown: string, config: PreviewConfig = {}): Promise<string> {\n const {\n plugins = [],\n markdown: markdownConfig = [],\n wrapperClass = \"mardora-preview\",\n wrapperTag = \"article\",\n sanitize = true,\n theme = ThemeEnum.AUTO,\n syntaxTheme,\n } = config;\n\n // Create renderer and generate HTML\n const renderer = new PreviewRenderer(markdown, plugins, markdownConfig, theme, sanitize, syntaxTheme);\n const content = await renderer.render();\n\n // Wrap in container\n const classAttr = wrapperClass ? ` class=\"${wrapperClass}\"` : \"\";\n return `<${wrapperTag}${classAttr}>\\n${content}</${wrapperTag}>`;\n}\n","import { ThemeEnum } from \"../editor/utils\";\nimport { GenerateCSSConfig } from \"./types\";\nimport { generateSyntaxThemeCSS } from \"./syntax-theme\";\n\n/**\n * Base CSS styles for preview rendering\n */\nconst baseStyles = `.mardora-preview {\n padding: 0 0.5rem;\n}`;\n\n/**\n * Generate CSS for preview rendering\n *\n * @param config - CSS generation configuration\n * @returns CSS string\n *\n * @example\n * ```ts\n * import { generateCSS } from 'mardora/preview';\n * import { HeadingPlugin, ListPlugin } from 'mardora/plugins';\n *\n * const css = generateCSS({\n * plugins: [new HeadingPlugin(), new ListPlugin()],\n * theme: ThemeEnum.AUTO,\n * includeBase: true,\n * });\n * ```\n */\nexport function generateCSS(config: GenerateCSSConfig = {}): string {\n const {\n plugins = [],\n theme = ThemeEnum.AUTO,\n wrapperClass = \"mardora-preview\",\n includeBase = true,\n syntaxTheme,\n } = config;\n\n const cssChunks: string[] = [];\n\n // Include base styles\n if (includeBase) {\n // Replace default wrapper class if custom one is provided\n if (wrapperClass !== \"mardora-preview\") {\n cssChunks.push(baseStyles.replace(/\\.mardora-preview/g, `.${wrapperClass}`));\n } else {\n cssChunks.push(baseStyles);\n }\n }\n\n // Collect syntax highlight styles (`tok-*` classes) from CodeMirror theme/extensions\n const syntaxCSS = generateSyntaxThemeCSS(syntaxTheme, wrapperClass);\n if (syntaxCSS) {\n cssChunks.push(\"/* syntax-theme */\\n\" + syntaxCSS);\n }\n\n // Collect styles from plugins\n for (const plugin of plugins) {\n const pluginCSS = plugin.getPreviewStyles(theme, wrapperClass);\n if (pluginCSS) cssChunks.push(`/* ${plugin.name} - ${plugin.version} */\\n` + pluginCSS);\n }\n\n return cssChunks.join(\"\\n\\n\");\n}\n","import { markdown, markdownLanguage } from \"@codemirror/lang-markdown\";\nimport { EditorState } from \"@codemirror/state\";\nimport type { MarkdownConfig } from \"@lezer/markdown\";\nimport type { MardoraTocConfig, MardoraTocItem } from \"../editor/table-of-contents\";\nimport { extractTocItemsFromState } from \"../editor/table-of-contents\";\n\nexport function extractPreviewTocFromMarkdown(\n doc: string,\n config: MardoraTocConfig = {},\n markdownConfig: MarkdownConfig[] = []\n): MardoraTocItem[] {\n const state = EditorState.create({\n doc,\n extensions: [markdown({ base: markdownLanguage, extensions: markdownConfig })],\n });\n\n return extractTocItemsFromState(state, config).map((item) => ({\n id: item.id,\n level: item.level,\n text: item.text,\n active: item.active,\n }));\n}\n"]}
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ var state = require('@codemirror/state');
4
+ var view = require('@codemirror/view');
5
+
6
+ // src/lib/input-handler.ts
7
+ function createWrapSelectionInputHandler(markersByInput) {
8
+ return view.EditorView.inputHandler.of((view, _from, _to, text) => {
9
+ const marker = markersByInput[text];
10
+ if (!marker) {
11
+ return false;
12
+ }
13
+ const ranges = view.state.selection.ranges;
14
+ if (ranges.length === 0 || ranges.some((range) => range.empty)) {
15
+ return false;
16
+ }
17
+ const changes = ranges.map((range) => ({
18
+ from: range.from,
19
+ to: range.to,
20
+ insert: `${marker}${view.state.sliceDoc(range.from, range.to)}${marker}`
21
+ })).reverse();
22
+ const nextRanges = ranges.map(
23
+ (range) => state.EditorSelection.range(range.from + marker.length, range.to + marker.length)
24
+ );
25
+ view.dispatch({
26
+ changes,
27
+ selection: state.EditorSelection.create(nextRanges, view.state.selection.mainIndex)
28
+ });
29
+ return true;
30
+ });
31
+ }
32
+
33
+ exports.createWrapSelectionInputHandler = createWrapSelectionInputHandler;
34
+ //# sourceMappingURL=chunk-KNDWF2DP.cjs.map
35
+ //# sourceMappingURL=chunk-KNDWF2DP.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/input-handler.ts"],"names":["EditorView","EditorSelection"],"mappings":";;;;;;AAeO,SAAS,gCAAgC,cAAA,EAAmD;AACjG,EAAA,OAAOA,gBAAW,YAAA,CAAa,EAAA,CAAG,CAAC,IAAA,EAAM,KAAA,EAAO,KAAK,IAAA,KAAS;AAC5D,IAAA,MAAM,MAAA,GAAS,eAAe,IAAI,CAAA;AAClC,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,SAAA,CAAU,MAAA;AACpC,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,IAAK,MAAA,CAAO,KAAK,CAAC,KAAA,KAAU,KAAA,CAAM,KAAK,CAAA,EAAG;AAC9D,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,MAAM,OAAA,GAAU,MAAA,CACb,GAAA,CAAI,CAAC,KAAA,MAAW;AAAA,MACf,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,IAAI,KAAA,CAAM,EAAA;AAAA,MACV,MAAA,EAAQ,CAAA,EAAG,MAAM,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,QAAA,CAAS,KAAA,CAAM,IAAA,EAAM,KAAA,CAAM,EAAE,CAAC,GAAG,MAAM,CAAA;AAAA,KACxE,CAAE,EACD,OAAA,EAAQ;AAEX,IAAA,MAAM,aAAa,MAAA,CAAO,GAAA;AAAA,MAAI,CAAC,KAAA,KAC7BC,qBAAA,CAAgB,KAAA,CAAM,KAAA,CAAM,IAAA,GAAO,MAAA,CAAO,MAAA,EAAQ,KAAA,CAAM,EAAA,GAAK,MAAA,CAAO,MAAM;AAAA,KAC5E;AAEA,IAAA,IAAA,CAAK,QAAA,CAAS;AAAA,MACZ,OAAA;AAAA,MACA,WAAWA,qBAAA,CAAgB,MAAA,CAAO,YAAY,IAAA,CAAK,KAAA,CAAM,UAAU,SAAS;AAAA,KAC7E,CAAA;AAED,IAAA,OAAO,IAAA;AAAA,EACT,CAAC,CAAA;AACH","file":"chunk-KNDWF2DP.cjs","sourcesContent":["import { EditorSelection, Extension } from \"@codemirror/state\";\nimport { EditorView } from \"@codemirror/view\";\n\n/**\n * Mapping of typed input characters to surrounding markers.\n *\n * Example:\n * { \"*\": \"*\", \"=\": \"==\" }\n */\nexport type WrapSelectionMarkerMap = Record<string, string>;\n\n/**\n * Creates an input handler that wraps non-empty selections with markdown markers\n * when a mapped character is typed.\n */\nexport function createWrapSelectionInputHandler(markersByInput: WrapSelectionMarkerMap): Extension {\n return EditorView.inputHandler.of((view, _from, _to, text) => {\n const marker = markersByInput[text];\n if (!marker) {\n return false;\n }\n\n const ranges = view.state.selection.ranges;\n if (ranges.length === 0 || ranges.some((range) => range.empty)) {\n return false;\n }\n\n const changes = ranges\n .map((range) => ({\n from: range.from,\n to: range.to,\n insert: `${marker}${view.state.sliceDoc(range.from, range.to)}${marker}`,\n }))\n .reverse();\n\n const nextRanges = ranges.map((range) =>\n EditorSelection.range(range.from + marker.length, range.to + marker.length)\n );\n\n view.dispatch({\n changes,\n selection: EditorSelection.create(nextRanges, view.state.selection.mainIndex),\n });\n\n return true;\n });\n}\n"]}