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,353 @@
1
+ import { Decoration, WidgetType } from "@codemirror/view";
2
+ import { syntaxTree } from "@codemirror/language";
3
+ import { DecorationContext, DecorationPlugin } from "../editor/plugin";
4
+ import DOMPurify from "dompurify";
5
+ import { createTheme } from "../editor";
6
+
7
+ /**
8
+ * Mark decorations for HTML content
9
+ */
10
+ const htmlMarkDecorations = {
11
+ "html-tag": Decoration.mark({ class: "cm-mardora-html-tag" }),
12
+ "html-comment": Decoration.mark({ class: "cm-mardora-html-comment" }),
13
+ };
14
+
15
+ /**
16
+ * Line decorations for HTML blocks (when visible)
17
+ */
18
+ const htmlLineDecorations = {
19
+ "html-block": Decoration.line({ class: "cm-mardora-line-html-block" }),
20
+ "hidden-line": Decoration.line({ class: "cm-mardora-hidden-line" }),
21
+ };
22
+
23
+ /**
24
+ * Widget to render sanitized HTML (block)
25
+ */
26
+ class HTMLPreviewWidget extends WidgetType {
27
+ constructor(readonly html: string) {
28
+ super();
29
+ }
30
+
31
+ override eq(other: HTMLPreviewWidget): boolean {
32
+ return other.html === this.html;
33
+ }
34
+
35
+ toDOM() {
36
+ const div = document.createElement("div");
37
+ div.className = "cm-mardora-html-preview";
38
+ div.innerHTML = DOMPurify.sanitize(this.html);
39
+ return div;
40
+ }
41
+
42
+ override ignoreEvent() {
43
+ return false;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Widget to render sanitized inline HTML
49
+ */
50
+ class InlineHTMLPreviewWidget extends WidgetType {
51
+ constructor(readonly html: string) {
52
+ super();
53
+ }
54
+
55
+ override eq(other: InlineHTMLPreviewWidget): boolean {
56
+ return other.html === this.html;
57
+ }
58
+
59
+ toDOM() {
60
+ const span = document.createElement("span");
61
+ span.className = "cm-mardora-inline-html-preview";
62
+ span.innerHTML = DOMPurify.sanitize(this.html);
63
+ return span;
64
+ }
65
+
66
+ override ignoreEvent() {
67
+ return false;
68
+ }
69
+ }
70
+
71
+ interface HTMLGroup {
72
+ from: number;
73
+ to: number;
74
+ }
75
+
76
+ interface HTMLTagInfo {
77
+ from: number;
78
+ to: number;
79
+ tagName: string;
80
+ isClosing: boolean;
81
+ isSelfClosing: boolean;
82
+ }
83
+
84
+ interface InlineHTMLElement {
85
+ from: number;
86
+ to: number;
87
+ content: string;
88
+ }
89
+
90
+ /**
91
+ * Parse an HTML tag to extract its name and type
92
+ */
93
+ function parseHTMLTag(content: string): { tagName: string; isClosing: boolean; isSelfClosing: boolean } | null {
94
+ const match = content.match(/^<\s*(\/?)([a-zA-Z][a-zA-Z0-9-]*)[^>]*(\/?)>$/);
95
+ if (!match) return null;
96
+
97
+ return {
98
+ tagName: match[2]!.toLowerCase(),
99
+ isClosing: match[1] === "/",
100
+ isSelfClosing:
101
+ match[3] === "/" ||
102
+ ["br", "hr", "img", "input", "meta", "link", "area", "base", "col", "embed", "source", "track", "wbr"].includes(
103
+ match[2]!.toLowerCase()
104
+ ),
105
+ };
106
+ }
107
+
108
+ /**
109
+ * HTMLPlugin - Decorates and Renders HTML in markdown
110
+ */
111
+ export class HTMLPlugin extends DecorationPlugin {
112
+ readonly name = "html";
113
+ readonly version = "1.0.0";
114
+ override decorationPriority = 30;
115
+
116
+ constructor() {
117
+ super();
118
+ }
119
+
120
+ /**
121
+ * Plugin theme
122
+ */
123
+ override get theme() {
124
+ return theme;
125
+ }
126
+
127
+ buildDecorations(ctx: DecorationContext): void {
128
+ const { view, decorations } = ctx;
129
+ const tree = syntaxTree(view.state);
130
+
131
+ // Collect blocks and inline tags
132
+ const htmlGroups: HTMLGroup[] = [];
133
+ const htmlTags: HTMLTagInfo[] = [];
134
+
135
+ tree.iterate({
136
+ enter: (node) => {
137
+ const { from, to, name } = node;
138
+
139
+ // Handle HTML Comments
140
+ if (name === "Comment") {
141
+ decorations.push(htmlMarkDecorations["html-comment"].range(from, to));
142
+ return;
143
+ }
144
+
145
+ // Collect inline HTML tags for pairing
146
+ if (name === "HTMLTag") {
147
+ const content = view.state.sliceDoc(from, to);
148
+ const parsed = parseHTMLTag(content);
149
+ if (parsed) {
150
+ htmlTags.push({
151
+ from,
152
+ to,
153
+ tagName: parsed.tagName,
154
+ isClosing: parsed.isClosing,
155
+ isSelfClosing: parsed.isSelfClosing,
156
+ });
157
+ }
158
+ }
159
+
160
+ // Handle HTML Blocks - Collect for grouping
161
+ if (name === "HTMLBlock") {
162
+ const last = htmlGroups[htmlGroups.length - 1];
163
+ if (last) {
164
+ const gap = view.state.sliceDoc(last.to, from);
165
+ if (!gap.trim()) {
166
+ last.to = to;
167
+ return;
168
+ }
169
+ }
170
+ htmlGroups.push({ from, to });
171
+ }
172
+ },
173
+ });
174
+
175
+ // Find complete inline HTML elements (must be on same line)
176
+ const inlineElements: InlineHTMLElement[] = [];
177
+ const usedTags = new Set<number>(); // Track used tag indices
178
+
179
+ for (let i = 0; i < htmlTags.length; i++) {
180
+ if (usedTags.has(i)) continue;
181
+
182
+ const openTag = htmlTags[i]!;
183
+ if (openTag.isClosing) continue;
184
+
185
+ // Handle self-closing tags
186
+ if (openTag.isSelfClosing) {
187
+ inlineElements.push({
188
+ from: openTag.from,
189
+ to: openTag.to,
190
+ content: view.state.sliceDoc(openTag.from, openTag.to),
191
+ });
192
+ usedTags.add(i);
193
+ continue;
194
+ }
195
+
196
+ // Find matching closing tag (must be on same line)
197
+ const openLine = view.state.doc.lineAt(openTag.from);
198
+ let depth = 1;
199
+ let closeTagIndex: number | null = null;
200
+
201
+ for (let j = i + 1; j < htmlTags.length && depth > 0; j++) {
202
+ const tag = htmlTags[j]!;
203
+
204
+ // Stop if we've gone past the open tag's line
205
+ if (tag.from > openLine.to) break;
206
+
207
+ if (tag.tagName === openTag.tagName) {
208
+ if (tag.isClosing) {
209
+ depth--;
210
+ if (depth === 0) {
211
+ closeTagIndex = j;
212
+ }
213
+ } else if (!tag.isSelfClosing) {
214
+ depth++;
215
+ }
216
+ }
217
+ }
218
+
219
+ if (closeTagIndex !== null) {
220
+ const closeTag = htmlTags[closeTagIndex]!;
221
+ inlineElements.push({
222
+ from: openTag.from,
223
+ to: closeTag.to,
224
+ content: view.state.sliceDoc(openTag.from, closeTag.to),
225
+ });
226
+
227
+ // Mark all tags within this range as used (to handle nesting)
228
+ for (let k = i; k <= closeTagIndex; k++) {
229
+ usedTags.add(k);
230
+ }
231
+ }
232
+ }
233
+
234
+ // Sort by position and filter out overlapping elements (keep outermost)
235
+ inlineElements.sort((a, b) => a.from - b.from);
236
+ const filteredElements: InlineHTMLElement[] = [];
237
+ let lastEnd = -1;
238
+
239
+ for (const elem of inlineElements) {
240
+ if (elem.from >= lastEnd) {
241
+ filteredElements.push(elem);
242
+ lastEnd = elem.to;
243
+ }
244
+ }
245
+
246
+ // Apply decorations for inline elements
247
+ for (const elem of filteredElements) {
248
+ const cursorInRange = ctx.cursorInRange(elem.from, elem.to);
249
+
250
+ if (cursorInRange) {
251
+ // Show source - find and style the tags within this element
252
+ for (const tag of htmlTags) {
253
+ if (tag.from >= elem.from && tag.to <= elem.to) {
254
+ decorations.push(htmlMarkDecorations["html-tag"].range(tag.from, tag.to));
255
+ }
256
+ }
257
+ } else {
258
+ // Render preview
259
+ decorations.push(
260
+ Decoration.replace({
261
+ widget: new InlineHTMLPreviewWidget(elem.content),
262
+ }).range(elem.from, elem.to)
263
+ );
264
+ }
265
+ }
266
+
267
+ // Style any remaining unprocessed tags (orphan tags)
268
+ for (let i = 0; i < htmlTags.length; i++) {
269
+ if (!usedTags.has(i)) {
270
+ const tag = htmlTags[i]!;
271
+ decorations.push(htmlMarkDecorations["html-tag"].range(tag.from, tag.to));
272
+ }
273
+ }
274
+
275
+ // Process gathered HTML block groups
276
+ for (const group of htmlGroups) {
277
+ const { from, to } = group;
278
+
279
+ const nodeLineStart = view.state.doc.lineAt(from);
280
+ const nodeLineEnd = view.state.doc.lineAt(to);
281
+ const cursorInRange = ctx.cursorInRange(nodeLineStart.from, nodeLineEnd.to);
282
+
283
+ if (cursorInRange) {
284
+ for (let i = nodeLineStart.number; i <= nodeLineEnd.number; i++) {
285
+ const line = view.state.doc.line(i);
286
+ decorations.push(htmlLineDecorations["html-block"].range(line.from));
287
+ }
288
+ } else {
289
+ const htmlContent = view.state.sliceDoc(from, to);
290
+
291
+ decorations.push(
292
+ Decoration.replace({
293
+ widget: new HTMLPreviewWidget(htmlContent.trim()),
294
+ }).range(from, nodeLineStart.to)
295
+ );
296
+
297
+ for (let i = nodeLineStart.number + 1; i <= nodeLineEnd.number; i++) {
298
+ const line = view.state.doc.line(i);
299
+ decorations.push(htmlLineDecorations["hidden-line"].range(line.from));
300
+ }
301
+ }
302
+ }
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Theme for HTML styling
308
+ */
309
+ const theme = createTheme({
310
+ default: {
311
+ ".cm-mardora-html-tag": {
312
+ color: "#6a737d",
313
+ fontFamily: "var(--font-jetbrains-mono, monospace)",
314
+ fontSize: "0.85em",
315
+ },
316
+
317
+ ".cm-mardora-html-comment": {
318
+ color: "#6a737d",
319
+ fontStyle: "italic",
320
+ fontFamily: "var(--font-jetbrains-mono, monospace)",
321
+ fontSize: "0.85em",
322
+ opacity: 0.5,
323
+ },
324
+
325
+ ".cm-mardora-line-html-block": {
326
+ backgroundColor: "rgba(0, 0, 0, 0.02)",
327
+ },
328
+
329
+ ".cm-mardora-hidden-line": {
330
+ display: "none",
331
+ },
332
+
333
+ ".cm-mardora-html-preview": {
334
+ display: "inline-block",
335
+ width: "100%",
336
+ verticalAlign: "top",
337
+ margin: "0",
338
+ whiteSpace: "normal",
339
+ lineHeight: "1.4",
340
+ },
341
+ ".cm-mardora-html-preview > *:first-child": {
342
+ marginTop: "0",
343
+ },
344
+ ".cm-mardora-html-preview > *:last-child": {
345
+ marginBottom: "0",
346
+ },
347
+
348
+ ".cm-mardora-inline-html-preview": {
349
+ display: "inline",
350
+ whiteSpace: "normal",
351
+ },
352
+ },
353
+ });