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,509 @@
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 { SyntaxNode } from "@lezer/common";
6
+
7
+ /**
8
+ * Mark decorations for link syntax elements
9
+ */
10
+ const linkMarkDecorations = {
11
+ "link-text": Decoration.mark({ class: "cm-mardora-link-text" }),
12
+ "link-marker": Decoration.mark({ class: "cm-mardora-link-marker" }),
13
+ "link-url": Decoration.mark({ class: "cm-mardora-link-url" }),
14
+ "link-hidden": Decoration.mark({ class: "cm-mardora-link-hidden" }),
15
+ };
16
+
17
+ /**
18
+ * Parse link markdown to extract text and URL
19
+ * Format: [text](url) or [text](url "title")
20
+ */
21
+ function parseLinkMarkdown(content: string): { text: string; url: string; title?: string } | null {
22
+ // Regex to match: [text](url) or [text](url "title")
23
+ const match = content.match(/^\[([^\]]*)\]\(([^"\s)]+)(?:\s+"([^"]*)")?\s*\)$/);
24
+ if (!match) return null;
25
+
26
+ const result: { text: string; url: string; title?: string } = {
27
+ text: match[1] || "",
28
+ url: match[2]!,
29
+ };
30
+
31
+ if (match[3] !== undefined) {
32
+ result.title = match[3];
33
+ }
34
+
35
+ return result;
36
+ }
37
+
38
+ /**
39
+ * Widget for displaying a tooltip with the link URL on hover
40
+ */
41
+ class LinkTooltipWidget extends WidgetType {
42
+ constructor(
43
+ readonly url: string,
44
+ readonly from: number,
45
+ readonly to: number
46
+ ) {
47
+ super();
48
+ }
49
+
50
+ override eq(other: LinkTooltipWidget): boolean {
51
+ return other.url === this.url && other.from === this.from && other.to === this.to;
52
+ }
53
+
54
+ toDOM(view: EditorView) {
55
+ const wrapper = document.createElement("span");
56
+ wrapper.className = "cm-mardora-link-wrapper";
57
+ wrapper.style.cursor = "pointer";
58
+
59
+ // Tooltip element
60
+ const tooltip = document.createElement("span");
61
+ tooltip.className = "cm-mardora-link-tooltip";
62
+ tooltip.textContent = this.url;
63
+ wrapper.appendChild(tooltip);
64
+
65
+ // Show/hide tooltip on hover
66
+ wrapper.addEventListener("mouseenter", () => {
67
+ tooltip.classList.add("cm-mardora-link-tooltip-visible");
68
+ });
69
+
70
+ wrapper.addEventListener("mouseleave", () => {
71
+ tooltip.classList.remove("cm-mardora-link-tooltip-visible");
72
+ });
73
+
74
+ // Click handler - select the raw markdown
75
+ wrapper.addEventListener("click", (e) => {
76
+ if (e.ctrlKey || e.metaKey) {
77
+ // Ctrl+Click: open in new tab
78
+ e.preventDefault();
79
+ e.stopPropagation();
80
+ window.open(this.url, "_blank", "noopener,noreferrer");
81
+ } else {
82
+ // Regular click: select raw markdown
83
+ e.preventDefault();
84
+ e.stopPropagation();
85
+ view.dispatch({
86
+ selection: { anchor: this.from, head: this.to },
87
+ scrollIntoView: true,
88
+ });
89
+ view.focus();
90
+ }
91
+ });
92
+
93
+ return wrapper;
94
+ }
95
+
96
+ override ignoreEvent(event: Event): boolean {
97
+ // Allow click and mouse events to be handled by our handlers
98
+ return event.type !== "click" && event.type !== "mouseenter" && event.type !== "mouseleave";
99
+ }
100
+ }
101
+
102
+ /**
103
+ * LinkPlugin - Decorates and provides interactivity for markdown links
104
+ *
105
+ * Supports the full link syntax: [text](url) and [text](url "title")
106
+ * - Click: reveals raw markdown (selects/focuses the link syntax)
107
+ * - Ctrl+Click: opens the link URL in a new browser tab
108
+ * - Hover: shows tooltip with the link URL
109
+ * - Hides the markdown syntax when cursor is not in range
110
+ * - Shows raw markdown when cursor is within the link range
111
+ */
112
+ export class LinkPlugin extends DecorationPlugin {
113
+ readonly name = "link";
114
+ readonly version = "1.0.0";
115
+ override decorationPriority = 22;
116
+ override readonly requiredNodes = ["Link"] as const;
117
+
118
+ constructor() {
119
+ super();
120
+ }
121
+
122
+ /**
123
+ * Plugin theme
124
+ */
125
+ override get theme() {
126
+ return theme;
127
+ }
128
+
129
+ /**
130
+ * Keyboard shortcuts for link formatting
131
+ */
132
+ override getKeymap(): KeyBinding[] {
133
+ return [
134
+ {
135
+ key: "Mod-k",
136
+ run: (view) => this.toggleLink(view),
137
+ preventDefault: true,
138
+ },
139
+ ];
140
+ }
141
+
142
+ /**
143
+ * URL regex pattern
144
+ */
145
+ private readonly urlPattern = /^(https?:\/\/|www\.)[^\s]+$/i;
146
+
147
+ /**
148
+ * Toggle link on selection
149
+ * - If text is selected and is a URL: [](url) with cursor in brackets
150
+ * - If text is selected (not URL): [text]() with cursor in parentheses
151
+ * - If nothing selected: []() with cursor in brackets
152
+ * - If already a link: remove syntax, leave plain text
153
+ */
154
+ private toggleLink(view: EditorView): boolean {
155
+ const { state } = view;
156
+ const { from, to, empty } = state.selection.main;
157
+ const selectedText = state.sliceDoc(from, to);
158
+
159
+ // Check if selection is already a link [text](url)
160
+ const linkMatch = selectedText.match(/^\[([^\]]*)\]\(([^)]*)\)$/);
161
+ if (linkMatch) {
162
+ // Already a link - extract just the text and replace
163
+ const linkText = linkMatch[1] || "";
164
+ view.dispatch({
165
+ changes: { from, to, insert: linkText },
166
+ selection: { anchor: from, head: from + linkText.length },
167
+ });
168
+ return true;
169
+ }
170
+
171
+ // Check if we're inside a link by looking at surrounding context
172
+ const lineStart = state.doc.lineAt(from).from;
173
+ const lineEnd = state.doc.lineAt(to).to;
174
+ const lineText = state.sliceDoc(lineStart, lineEnd);
175
+
176
+ // Find link pattern in line that contains the selection
177
+ const linkRegex = /\[([^\]]*)\]\(([^)]*)\)/g;
178
+ let match;
179
+ while ((match = linkRegex.exec(lineText)) !== null) {
180
+ const matchFrom = lineStart + match.index;
181
+ const matchTo = matchFrom + match[0].length;
182
+
183
+ // Check if selection is within this link
184
+ if (from >= matchFrom && to <= matchTo) {
185
+ // Remove the link syntax, leave plain text
186
+ const linkText = match[1] || "";
187
+ view.dispatch({
188
+ changes: { from: matchFrom, to: matchTo, insert: linkText },
189
+ selection: { anchor: matchFrom, head: matchFrom + linkText.length },
190
+ });
191
+ return true;
192
+ }
193
+ }
194
+
195
+ if (empty) {
196
+ // No selection - insert []() and place cursor in brackets
197
+ view.dispatch({
198
+ changes: { from, insert: "[]()" },
199
+ selection: { anchor: from + 1 },
200
+ });
201
+ } else if (this.urlPattern.test(selectedText)) {
202
+ // Selected text is a URL - put it in parentheses, cursor in brackets
203
+ const newText = `[](${selectedText})`;
204
+ view.dispatch({
205
+ changes: { from, to, insert: newText },
206
+ selection: { anchor: from + 1 },
207
+ });
208
+ } else {
209
+ // Selected text is not a URL - wrap as link text, cursor in parentheses
210
+ const newText = `[${selectedText}]()`;
211
+ view.dispatch({
212
+ changes: { from, to, insert: newText },
213
+ selection: { anchor: from + selectedText.length + 3 },
214
+ });
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
+
228
+ // Handle Link nodes
229
+ if (name === "Link") {
230
+ const content = view.state.sliceDoc(from, to);
231
+ const parsed = parseLinkMarkdown(content);
232
+
233
+ if (!parsed) return;
234
+
235
+ const cursorInRange = ctx.selectionOverlapsRange(from, to);
236
+
237
+ if (cursorInRange) {
238
+ // Cursor in range: show raw markdown with styling
239
+ this.decorateRawLink(node.node, decorations, view);
240
+ } else {
241
+ // Cursor out of range: hide raw markdown, show styled link text
242
+ // Hide the entire markdown syntax
243
+ decorations.push(linkMarkDecorations["link-hidden"].range(from, to));
244
+
245
+ // Add styled link text with tooltip widget after the hidden markdown
246
+ decorations.push(
247
+ Decoration.widget({
248
+ widget: new LinkTooltipWidget(parsed.url, from, to),
249
+ side: 1,
250
+ }).range(to)
251
+ );
252
+
253
+ // Add replacement decoration to show styled link text
254
+ decorations.push(
255
+ Decoration.replace({
256
+ widget: new LinkTextWidget(parsed.text, parsed.url, from, to, parsed.title),
257
+ }).range(from, to)
258
+ );
259
+ }
260
+ }
261
+ },
262
+ });
263
+ }
264
+
265
+ /**
266
+ * Decorate raw link markdown when cursor is in range
267
+ */
268
+ private decorateRawLink(
269
+ node: SyntaxNode,
270
+ decorations: import("@codemirror/state").Range<Decoration>[],
271
+ view: import("@codemirror/view").EditorView
272
+ ): void {
273
+ const content = view.state.sliceDoc(node.from, node.to);
274
+
275
+ // Style the opening bracket [
276
+ decorations.push(linkMarkDecorations["link-marker"].range(node.from, node.from + 1));
277
+
278
+ // Find and style the link text and closing bracket + opening paren ](
279
+ const bracketParen = content.indexOf("](");
280
+ if (bracketParen !== -1) {
281
+ // Style link text
282
+ if (bracketParen > 1) {
283
+ decorations.push(linkMarkDecorations["link-text"].range(node.from + 1, node.from + bracketParen));
284
+ }
285
+ // Style ]( markers
286
+ decorations.push(
287
+ linkMarkDecorations["link-marker"].range(node.from + bracketParen, node.from + bracketParen + 2)
288
+ );
289
+
290
+ // Find and style the URL
291
+ const urlChild = node.getChild("URL");
292
+ if (urlChild) {
293
+ decorations.push(linkMarkDecorations["link-url"].range(urlChild.from, urlChild.to));
294
+ }
295
+
296
+ // Style closing )
297
+ decorations.push(linkMarkDecorations["link-marker"].range(node.to - 1, node.to));
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Render link to HTML for preview mode
303
+ */
304
+ override renderToHTML(
305
+ node: SyntaxNode,
306
+ _children: string,
307
+ ctx: { sliceDoc(from: number, to: number): string; sanitize(html: string): string }
308
+ ): string | null {
309
+ if (node.name !== "Link") return null;
310
+
311
+ const content = ctx.sliceDoc(node.from, node.to);
312
+ const parsed = parseLinkMarkdown(content);
313
+ if (!parsed) return null;
314
+
315
+ const textContent = ctx.sanitize(parsed.text);
316
+ const urlAttr = ctx.sanitize(parsed.url);
317
+ const titleAttr = parsed.title ? ` title="${ctx.sanitize(parsed.title)}"` : "";
318
+
319
+ return `<a class="cm-mardora-link" href="${urlAttr}"${titleAttr} target="_blank" rel="noopener noreferrer">${textContent}</a>`;
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Widget to display the styled link text with interactivity
325
+ */
326
+ class LinkTextWidget extends WidgetType {
327
+ constructor(
328
+ readonly text: string,
329
+ readonly url: string,
330
+ readonly from: number,
331
+ readonly to: number,
332
+ readonly title?: string
333
+ ) {
334
+ super();
335
+ }
336
+
337
+ override eq(other: LinkTextWidget): boolean {
338
+ return (
339
+ other.text === this.text &&
340
+ other.url === this.url &&
341
+ other.from === this.from &&
342
+ other.to === this.to &&
343
+ other.title === this.title
344
+ );
345
+ }
346
+
347
+ toDOM(view: EditorView) {
348
+ const span = document.createElement("span");
349
+ span.className = "cm-mardora-link-styled";
350
+ span.textContent = this.text;
351
+ span.style.cursor = "pointer";
352
+
353
+ if (this.title) {
354
+ span.title = this.title;
355
+ }
356
+
357
+ // Tooltip element
358
+ const tooltip = document.createElement("span");
359
+ tooltip.className = "cm-mardora-link-tooltip";
360
+ tooltip.textContent = this.url;
361
+ span.appendChild(tooltip);
362
+
363
+ // Show/hide tooltip on hover
364
+ span.addEventListener("mouseenter", () => {
365
+ tooltip.classList.add("cm-mardora-link-tooltip-visible");
366
+ });
367
+
368
+ span.addEventListener("mouseleave", () => {
369
+ tooltip.classList.remove("cm-mardora-link-tooltip-visible");
370
+ });
371
+
372
+ // Click handler
373
+ span.addEventListener("click", (e) => {
374
+ if (e.ctrlKey || e.metaKey) {
375
+ // Ctrl+Click: open in new tab
376
+ e.preventDefault();
377
+ e.stopPropagation();
378
+ window.open(this.url, "_blank", "noopener,noreferrer");
379
+ } else {
380
+ // Regular click: select raw markdown
381
+ e.preventDefault();
382
+ e.stopPropagation();
383
+ view.dispatch({
384
+ selection: { anchor: this.from, head: this.to },
385
+ scrollIntoView: true,
386
+ });
387
+ view.focus();
388
+ }
389
+ });
390
+
391
+ return span;
392
+ }
393
+
394
+ override ignoreEvent(event: Event): boolean {
395
+ // Allow click and mouse events to be handled by our handlers
396
+ return event.type !== "click" && event.type !== "mouseenter" && event.type !== "mouseleave";
397
+ }
398
+ }
399
+
400
+ /**
401
+ * Theme for link styling
402
+ */
403
+ const theme = createTheme({
404
+ default: {
405
+ // Link text
406
+ ".cm-mardora-link-text": {
407
+ color: "#0366d6",
408
+ },
409
+
410
+ // Link markers ([ ] ( ))
411
+ ".cm-mardora-link-marker": {
412
+ color: "#6a737d",
413
+ fontFamily: "var(--font-jetbrains-mono, monospace)",
414
+ },
415
+
416
+ // URL in raw markdown
417
+ ".cm-mardora-link-url": {
418
+ color: "#6a737d",
419
+ fontStyle: "italic",
420
+ },
421
+
422
+ // Hidden markdown syntax
423
+ ".cm-mardora-link-hidden": {
424
+ display: "none",
425
+ },
426
+
427
+ // Styled link when cursor is not in range
428
+ ".cm-mardora-link-styled": {
429
+ color: "#0366d6",
430
+ textDecoration: "underline",
431
+ position: "relative",
432
+ cursor: "pointer",
433
+ },
434
+
435
+ ".cm-mardora-link-styled:hover": {
436
+ color: "#0056b3",
437
+ },
438
+
439
+ // Preview link styling
440
+ ".cm-mardora-link": {
441
+ color: "#0366d6",
442
+ textDecoration: "underline",
443
+ },
444
+
445
+ ".cm-mardora-link:hover": {
446
+ color: "#0056b3",
447
+ },
448
+
449
+ // Tooltip styling
450
+ ".cm-mardora-link-tooltip": {
451
+ display: "none",
452
+ position: "absolute",
453
+ bottom: "100%",
454
+ left: "50%",
455
+ transform: "translateX(-50%)",
456
+ backgroundColor: "#24292e",
457
+ color: "#ffffff",
458
+ padding: "4px 8px",
459
+ borderRadius: "4px",
460
+ fontSize: "12px",
461
+ whiteSpace: "nowrap",
462
+ zIndex: "1000",
463
+ pointerEvents: "none",
464
+ marginBottom: "4px",
465
+ maxWidth: "300px",
466
+ overflow: "hidden",
467
+ textOverflow: "ellipsis",
468
+ },
469
+
470
+ ".cm-mardora-link-tooltip-visible": {
471
+ display: "block",
472
+ },
473
+ },
474
+
475
+ dark: {
476
+ ".cm-mardora-link-text": {
477
+ color: "#58a6ff",
478
+ },
479
+
480
+ ".cm-mardora-link-marker": {
481
+ color: "#8b949e",
482
+ },
483
+
484
+ ".cm-mardora-link-url": {
485
+ color: "#8b949e",
486
+ },
487
+
488
+ ".cm-mardora-link-styled": {
489
+ color: "#58a6ff",
490
+ },
491
+
492
+ ".cm-mardora-link-styled:hover": {
493
+ color: "#79c0ff",
494
+ },
495
+
496
+ ".cm-mardora-link": {
497
+ color: "#58a6ff",
498
+ },
499
+
500
+ ".cm-mardora-link:hover": {
501
+ color: "#79c0ff",
502
+ },
503
+
504
+ ".cm-mardora-link-tooltip": {
505
+ backgroundColor: "#30363d",
506
+ color: "#c9d1d9",
507
+ },
508
+ },
509
+ });