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,492 @@
1
+ import { Decoration, EditorView, KeyBinding, WidgetType } from "@codemirror/view";
2
+ import { syntaxTree } from "@codemirror/language";
3
+ import { DecorationContext, DecorationPlugin } from "../editor/plugin";
4
+ import { createTheme } from "../editor";
5
+ import { Range } from "@codemirror/state";
6
+ import { SyntaxNode } from "@lezer/common";
7
+
8
+ // ============================================================================
9
+ // CSS Classes
10
+ // ============================================================================
11
+
12
+ const classes = {
13
+ // Unordered list classes
14
+ lineUL: "cm-mardora-list-line-ul",
15
+ markUL: "cm-mardora-list-mark-ul",
16
+
17
+ // Ordered list classes
18
+ lineOL: "cm-mardora-list-line-ol",
19
+ markOL: "cm-mardora-list-mark-ol",
20
+
21
+ // Task list classes
22
+ taskLine: "cm-mardora-task-line",
23
+ taskMarker: "cm-mardora-task-marker",
24
+
25
+ // Common classes
26
+ content: "cm-mardora-list-content",
27
+ indent: "cm-mardora-list-indent",
28
+ active: " cm-mardora-active",
29
+ preview: "cm-mardora-preview",
30
+ };
31
+
32
+ // ============================================================================
33
+ // Checkbox Widget
34
+ // ============================================================================
35
+
36
+ /**
37
+ * Interactive checkbox widget for task list items.
38
+ * Replaces `[ ]` or `[x]` markers with a clickable checkbox when not editing.
39
+ */
40
+ export class TaskCheckboxWidget extends WidgetType {
41
+ constructor(readonly checked: boolean) {
42
+ super();
43
+ }
44
+
45
+ override eq(other: TaskCheckboxWidget): boolean {
46
+ return other.checked === this.checked;
47
+ }
48
+
49
+ toDOM(view: EditorView): HTMLElement {
50
+ const wrap = document.createElement("span");
51
+ wrap.className = `cm-mardora-task-checkbox ${this.checked ? "checked" : ""}`;
52
+ wrap.setAttribute("aria-hidden", "true");
53
+
54
+ const checkbox = document.createElement("input");
55
+ checkbox.type = "checkbox";
56
+ checkbox.checked = this.checked;
57
+ checkbox.tabIndex = -1;
58
+
59
+ checkbox.addEventListener("mousedown", (e) => {
60
+ e.preventDefault();
61
+ this.toggleCheckbox(view, wrap);
62
+ });
63
+
64
+ wrap.appendChild(checkbox);
65
+ return wrap;
66
+ }
67
+
68
+ override ignoreEvent(): boolean {
69
+ return false;
70
+ }
71
+
72
+ /** Toggle the checkbox state in the document */
73
+ private toggleCheckbox(view: EditorView, wrap: HTMLElement): void {
74
+ const pos = view.posAtDOM(wrap);
75
+ const line = view.state.doc.lineAt(pos);
76
+ const match = line.text.match(/^(\s*(?:[-*+]|\d+\.)\s*)\[([ xX])\]/);
77
+
78
+ if (match) {
79
+ const markerStart = line.from + match[1]!.length + 1;
80
+ const newChar = this.checked ? " " : "x";
81
+ view.dispatch({
82
+ changes: { from: markerStart, to: markerStart + 1, insert: newChar },
83
+ });
84
+ }
85
+ }
86
+ }
87
+
88
+ // ============================================================================
89
+ // List Plugin
90
+ // ============================================================================
91
+
92
+ /**
93
+ * Decorates markdown lists with custom styling.
94
+ *
95
+ * Supports:
96
+ * - **Unordered lists** — Replaces `*`, `-`, `+` markers with styled bullets
97
+ * - **Ordered lists** — Styles numbered markers (`1.`, `2.`, etc.)
98
+ * - **Task lists** — Renders `[ ]`/`[x]` as interactive checkboxes
99
+ */
100
+ export class ListPlugin extends DecorationPlugin {
101
+ readonly name = "list";
102
+ readonly version = "1.0.0";
103
+ override decorationPriority = 20;
104
+ override readonly requiredNodes = [
105
+ "BulletList",
106
+ "OrderedList",
107
+ "ListItem",
108
+ "ListMark",
109
+ "Task",
110
+ "TaskMarker",
111
+ ] as const;
112
+
113
+ override get theme() {
114
+ return theme;
115
+ }
116
+
117
+ /**
118
+ * Keyboard shortcuts for list formatting
119
+ */
120
+ override getKeymap(): KeyBinding[] {
121
+ return [
122
+ {
123
+ key: "Mod-Shift-8",
124
+ run: (view) => this.toggleListOnLines(view, "- "),
125
+ preventDefault: true,
126
+ },
127
+ {
128
+ key: "Mod-Shift-7",
129
+ run: (view) => this.toggleListOnLines(view, "1. "),
130
+ preventDefault: true,
131
+ },
132
+ {
133
+ key: "Mod-Shift-9",
134
+ run: (view) => this.toggleListOnLines(view, "- [ ] "),
135
+ preventDefault: true,
136
+ },
137
+ ];
138
+ }
139
+
140
+ /**
141
+ * Toggle list marker on current line or selected lines
142
+ */
143
+ private toggleListOnLines(view: EditorView, marker: string): boolean {
144
+ const { state } = view;
145
+ const { from, to } = state.selection.main;
146
+
147
+ // Get all lines in selection
148
+ const startLine = state.doc.lineAt(from);
149
+ const endLine = state.doc.lineAt(to);
150
+
151
+ const changes: { from: number; to: number; insert: string }[] = [];
152
+
153
+ // Regex to match existing list markers
154
+ const listMarkerRegex = /^(\s*)([-*+]|\d+\.)\s(\[[ xX]\]\s)?/;
155
+
156
+ const isOrderedMarker = marker === "1. ";
157
+ let orderNum = 1;
158
+
159
+ for (let lineNum = startLine.number; lineNum <= endLine.number; lineNum++) {
160
+ const line = state.doc.line(lineNum);
161
+ const match = line.text.match(listMarkerRegex);
162
+
163
+ // Get the actual marker to insert (incremental for ordered lists)
164
+ const actualMarker = isOrderedMarker ? `${orderNum}. ` : marker;
165
+
166
+ if (match) {
167
+ // Line already has a list marker - check if same type
168
+ const existingMarker = match[0];
169
+ const indent = match[1] || "";
170
+
171
+ // Check if this is the same marker type (toggle off)
172
+ const isUnordered = /^[-*+]$/.test(match[2]!);
173
+ const isOrdered = /^\d+\.$/.test(match[2]!);
174
+ const hasTask = !!match[3];
175
+
176
+ const wantUnordered = marker === "- ";
177
+ const wantOrdered = isOrderedMarker;
178
+ const wantTask = marker === "- [ ] ";
179
+
180
+ if (
181
+ (wantUnordered && isUnordered && !hasTask) ||
182
+ (wantOrdered && isOrdered && !hasTask) ||
183
+ (wantTask && hasTask)
184
+ ) {
185
+ // Same type - remove the marker
186
+ changes.push({
187
+ from: line.from,
188
+ to: line.from + existingMarker.length,
189
+ insert: indent,
190
+ });
191
+ } else {
192
+ // Different type - replace the marker
193
+ changes.push({
194
+ from: line.from,
195
+ to: line.from + existingMarker.length,
196
+ insert: indent + actualMarker,
197
+ });
198
+ orderNum++;
199
+ }
200
+ } else {
201
+ // No list marker - add one at start of line (after any indent)
202
+ const indentMatch = line.text.match(/^(\s*)/);
203
+ const indent = indentMatch ? indentMatch[1]! : "";
204
+ changes.push({
205
+ from: line.from + indent.length,
206
+ to: line.from + indent.length,
207
+ insert: actualMarker,
208
+ });
209
+ orderNum++;
210
+ }
211
+ }
212
+
213
+ if (changes.length > 0) {
214
+ view.dispatch({ changes });
215
+ }
216
+
217
+ return true;
218
+ }
219
+
220
+ buildDecorations(ctx: DecorationContext): void {
221
+ const { view, decorations } = ctx;
222
+ const tree = syntaxTree(view.state);
223
+
224
+ tree.iterate({
225
+ enter: (node) => {
226
+ const { from, to, name } = node;
227
+ const line = view.state.doc.lineAt(from);
228
+ const cursorInLine = ctx.cursorInRange(line.from, line.to);
229
+
230
+ switch (name) {
231
+ case "ListItem":
232
+ this.decorateListItem(node, line, decorations);
233
+ break;
234
+
235
+ case "ListMark":
236
+ this.decorateListMark(node, line, decorations, cursorInLine);
237
+ break;
238
+
239
+ case "TaskMarker":
240
+ this.decorateTaskMarker(from, to, view, decorations, cursorInLine);
241
+ break;
242
+ }
243
+ },
244
+ });
245
+ }
246
+
247
+ /** Add line decoration for list items with nesting depth */
248
+ private decorateListItem(
249
+ node: Parameters<NonNullable<Parameters<ReturnType<typeof syntaxTree>["iterate"]>[0]["enter"]>>[0],
250
+ line: { from: number },
251
+ decorations: Range<Decoration>[]
252
+ ): void {
253
+ const parent = node.node.parent;
254
+ const listType = parent?.name;
255
+
256
+ // Calculate nesting depth
257
+ let depth = 0;
258
+ let ancestor = node.node.parent;
259
+ while (ancestor) {
260
+ if (ancestor.name === "ListItem") depth++;
261
+ ancestor = ancestor.parent;
262
+ }
263
+
264
+ // Check for task marker child
265
+ const hasTask = this.hasTaskChild(node);
266
+
267
+ // Determine line class based on list type
268
+ let lineClass: string;
269
+ if (hasTask) lineClass = classes.taskLine;
270
+ else if (listType === "OrderedList") lineClass = classes.lineOL;
271
+ else lineClass = classes.lineUL;
272
+
273
+ decorations.push(
274
+ Decoration.line({
275
+ class: lineClass,
276
+ attributes: { style: `--depth: ${depth}` },
277
+ }).range(line.from)
278
+ );
279
+ }
280
+
281
+ /** Check if a ListItem node has a Task child */
282
+ private hasTaskChild(
283
+ node: Parameters<NonNullable<Parameters<ReturnType<typeof syntaxTree>["iterate"]>[0]["enter"]>>[0]
284
+ ): boolean {
285
+ const cursor = node.node.cursor();
286
+ if (cursor.firstChild()) {
287
+ do {
288
+ if (cursor.name === "Task") return true;
289
+ } while (cursor.nextSibling());
290
+ }
291
+ return false;
292
+ }
293
+
294
+ /** Decorate list markers (bullets for UL, numbers for OL) */
295
+ private decorateListMark(
296
+ node: Parameters<NonNullable<Parameters<ReturnType<typeof syntaxTree>["iterate"]>[0]["enter"]>>[0],
297
+ line: { from: number; to: number },
298
+ decorations: Range<Decoration>[],
299
+ cursorInLine: boolean
300
+ ): void {
301
+ const { from, to } = node;
302
+ const parent = node.node.parent;
303
+ const grandparent = parent?.parent;
304
+ const listType = grandparent?.name;
305
+ const activeClass = cursorInLine ? classes.active : "";
306
+
307
+ // Add indent decoration for nested items
308
+ if (from > line.from) {
309
+ decorations.push(Decoration.mark({ class: classes.indent + activeClass }).range(line.from, from));
310
+ }
311
+
312
+ // Add marker decoration based on list type
313
+ const markClass = listType === "OrderedList" ? classes.markOL : classes.markUL;
314
+ decorations.push(Decoration.mark({ class: markClass + activeClass }).range(from, to + 1));
315
+
316
+ // Wrap remaining line content
317
+ const contentStart = to + 1;
318
+ if (contentStart < line.to) {
319
+ decorations.push(Decoration.mark({ class: classes.content }).range(contentStart, line.to));
320
+ }
321
+ }
322
+
323
+ /** Decorate task markers - show checkbox widget or raw text based on cursor */
324
+ private decorateTaskMarker(
325
+ from: number,
326
+ to: number,
327
+ view: EditorView,
328
+ decorations: Range<Decoration>[],
329
+ cursorInLine: boolean
330
+ ): void {
331
+ const text = view.state.sliceDoc(from, to);
332
+ const isChecked = text.includes("x") || text.includes("X");
333
+
334
+ if (cursorInLine) {
335
+ // Show raw marker when editing
336
+ decorations.push(Decoration.mark({ class: classes.taskMarker }).range(from, to));
337
+ } else {
338
+ // Replace with interactive checkbox
339
+ decorations.push(
340
+ Decoration.replace({
341
+ widget: new TaskCheckboxWidget(isChecked),
342
+ }).range(from, to)
343
+ );
344
+ }
345
+ }
346
+
347
+ /** Render list nodes to HTML */
348
+ override renderToHTML(
349
+ node: SyntaxNode,
350
+ children: string,
351
+ ctx: { sliceDoc(from: number, to: number): string; sanitize(html: string): string }
352
+ ): string | null {
353
+ switch (node.name) {
354
+ case "BulletList":
355
+ return `<ul class="${classes.lineUL} ${classes.preview}">${children}</ul>\n`;
356
+
357
+ case "OrderedList":
358
+ return `<ol class="${classes.lineOL} ${classes.preview}">${children}</ol>\n`;
359
+
360
+ case "ListItem":
361
+ return `<li>${children}</li>\n`;
362
+
363
+ case "Task":
364
+ return children;
365
+
366
+ case "TaskMarker": {
367
+ const text = ctx.sliceDoc(node.from, node.to);
368
+ const isChecked = text.includes("x") || text.includes("X");
369
+ return `<input type="checkbox" class="cm-mardora-task-checkbox" disabled ${isChecked ? "checked" : ""} />`;
370
+ }
371
+
372
+ case "ListMark":
373
+ return "";
374
+
375
+ default:
376
+ return null;
377
+ }
378
+ }
379
+ }
380
+
381
+ // ============================================================================
382
+ // Theme
383
+ // ============================================================================
384
+
385
+ const theme = createTheme({
386
+ default: {
387
+ // Indentation marker positioning
388
+ ".cm-mardora-list-indent": {
389
+ overflow: "hidden",
390
+ display: "inline-block",
391
+ position: "absolute",
392
+ left: "calc(1rem * (var(--depth, 0) + 1))",
393
+ transform: "translateX(-100%)",
394
+ },
395
+
396
+ // List line layout (flexbox for marker alignment)
397
+ ".cm-mardora-list-line-ul, .cm-mardora-list-line-ol": {
398
+ position: "relative",
399
+ paddingLeft: "calc(1rem * (var(--depth, 0) + 1)) !important",
400
+ display: "flex",
401
+ alignItems: "start",
402
+ },
403
+ ".cm-mardora-list-line-ul > :first-child, .cm-mardora-list-line-ol > :first-child": {
404
+ flexShrink: 0,
405
+ },
406
+
407
+ // List marker sizing
408
+ ".cm-mardora-list-line-ul .cm-mardora-list-mark-ul, .cm-mardora-list-line-ol .cm-mardora-list-mark-ol": {
409
+ whiteSpace: "pre",
410
+ position: "relative",
411
+ width: "1rem",
412
+ flexShrink: 0,
413
+ },
414
+
415
+ // Hide raw marker text when not active
416
+ ".cm-mardora-list-mark-ul:not(.cm-mardora-active) > span, .cm-mardora-task-line .cm-mardora-list-mark-ol:not(.cm-mardora-active) > span":
417
+ {
418
+ visibility: "hidden",
419
+ display: "none",
420
+ },
421
+
422
+ // Styled bullet for unordered lists
423
+ ".cm-mardora-list-line-ul .cm-mardora-list-mark-ul:not(.cm-mardora-active)::after": {
424
+ content: '"•"',
425
+ color: "var(--color-link)",
426
+ fontWeight: "bold",
427
+ pointerEvents: "none",
428
+ },
429
+
430
+ // Task marker styling (visible when editing)
431
+ ".cm-mardora-task-marker": {
432
+ color: "var(--mardora-highlight, #a4a4a4)",
433
+ fontFamily: "monospace",
434
+ },
435
+
436
+ // Task checkbox container
437
+ ".cm-mardora-task-checkbox": {
438
+ display: "inline-flex",
439
+ verticalAlign: "middle",
440
+ marginRight: "0.3em",
441
+ cursor: "pointer",
442
+ userSelect: "none",
443
+ alignItems: "center",
444
+ height: "1.2em",
445
+ },
446
+
447
+ // Task checkbox input styling
448
+ ".cm-mardora-task-checkbox input": {
449
+ cursor: "pointer",
450
+ margin: 0,
451
+ width: "1.1em",
452
+ height: "1.1em",
453
+ appearance: "none",
454
+ border: "1px solid",
455
+ borderRadius: "0.25em",
456
+ backgroundColor: "transparent",
457
+ position: "relative",
458
+ },
459
+
460
+ // Checkmark for completed tasks
461
+ ".cm-mardora-task-checkbox.checked input::after": {
462
+ content: '"✓"',
463
+ position: "absolute",
464
+ left: "1px",
465
+ top: "-3px",
466
+ },
467
+
468
+ // Preview styles (override editor-specific layout)
469
+ ".cm-mardora-preview": {
470
+ display: "block",
471
+ paddingLeft: "1.5rem",
472
+ margin: "0.5rem 0",
473
+ },
474
+ ".cm-mardora-preview li": {
475
+ display: "list-item",
476
+ marginBottom: "0.25rem",
477
+ },
478
+ "ul.cm-mardora-preview": {
479
+ listStyleType: "disc",
480
+ },
481
+ "ol.cm-mardora-preview": {
482
+ listStyleType: "decimal",
483
+ },
484
+ // Hide list marker for task items
485
+ ".cm-mardora-preview li:has(.cm-mardora-task-checkbox)": {
486
+ listStyleType: "none",
487
+ },
488
+ ".cm-mardora-preview li .cm-mardora-paragraph": {
489
+ padding: "0",
490
+ },
491
+ },
492
+ });